diff --git a/README.md b/README.md index aa8300dc..8bb9ce91 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,54 @@ -# Grafana OnCall Incident Response -Grafana OnCall, cloud version of Grafana OnCall: https://grafana.com/products/cloud/ + Developer-friendly, incident response management with brilliant Slack integration. -- Connect monitoring systems -- Collect and analyze data -- On-call rotation -- Automatic escalation -- Never miss alerts with calls and SMS -![Grafana OnCall Screenshot](screenshot.png) + + +- Collect and analyze alerts from multiple monitoring systems +- On-call rotations based on schedules +- Automatic escalations +- Phone calls, SMS, Slack, Telegram notifications + + + + ## Getting Started -OnCall consists of two parts: -1. OnCall backend -2. "Grafana OnCall" plugin you need to install in your Grafana -### How to run OnCall backend -1. An all-in-one image of OnCall is available on docker hub to run it: +### Environments: + +Production: [PRODUCTION.md](PRODUCTION.md). +Developer: [DEVELOPER.md](DEVELOPER.md). + +### Hobby environment + +Download docker-compose.yaml: ```bash -docker run -it --name oncall-backend -p 8000:8000 grafana/oncall-all-in-one +curl https://github.com/... -o docker-compose.yaml ``` -2. When the image starts up you will see a message like this: +Set environment: ```bash -👋 This script will issue an invite token to securely connect the frontend. -Maintainers will be happy to help in the slack channel #grafana-oncall: https://slack.grafana.com/ -Your invite token: , use it in the Grafana OnCall plugin. +export DOMAIN=http://localhost +export SECRET_KEY=my_random_secret_must_be_more_than_32_characters_long +export RABBITMQ_PASSWORD=rabbitmq_secret_pw +export MYSQL_PASSWORD=mysql_secret_pw +export COMPOSE_PROFILES=with_grafana +export GRAFANA_USER=admin +export GRAFANA_PASSWORD=admin ``` -3. If you started your container detached with -d check the log: +Launch services: ```bash -docker logs oncall-backend +docker-compose -f docker-compose.yml up --build -d ``` -### How to install "Grafana OnCall" Plugin and connect with a backend -1. Open Grafana in your browser and login as an Admin -2. Navigate to Configuration → Plugins -3. Type Grafana OnCall into the "Search Grafana plugins" field -4. Select the Grafana OnCall plugin and press the "Install" button -5. On the Grafana OnCall Plugin page Enable the plugin and go to the Configuration tab you should see a status field with the message +Issue invite token and get further instructions: +```bash +docker-compose -f docker-compose.yml run engine python manage.py issue_invite_for_the_frontend --override ``` -OnCall has not been setup, configure & initialize below. -``` -6. Fill in configuration fields using the token you got from the backend earlier, then press "Install Configuration" -``` -OnCall API URL: (The URL & port used to access OnCall) -http://host.docker.internal:8000 - -OnCall Invitation Token (Single use token to connect Grafana instance): -Invitation token from docker startup - -Grafana URL (URL OnCall will use to talk to this Grafana instance): -http://localhost:3000 (or http://host.docker.internal:3000 if your grafana is running in Docker locally) -``` - -## Getting Help -- `#grafana-oncall` channel at https://slack.grafana.com/ -- Grafana Labs community forum for OnCall: https://community.grafana.com -- File an [issue](https://github.com/grafana/oncall/issues) for bugs, issues and feature suggestions. - -## Production Setup - -Looking for the production instructions? We're going to release them soon. Please join our Slack channel to be the first to know about them. ## Further Reading - *Documentation* - [Grafana OnCall](https://grafana.com/docs/grafana-cloud/oncall/) - *Blog Post* - [Announcing Grafana OnCall, the easiest way to do on-call management](https://grafana.com/blog/2021/11/09/announcing-grafana-oncall/) - *Presentation* - [Deep dive into the Grafana, Prometheus, and Alertmanager stack for alerting and on-call management](https://grafana.com/go/observabilitycon/2021/alerting/?pg=blog) - -## FAQ - -- How do I generate a new invitation token to connect plugin with a backend? -```bash -docker exec oncall-backend python manage.py issue_invite_for_the_frontend --override -``` diff --git a/deploy/docker-compose/README.md b/deploy/docker-compose/README.md new file mode 100644 index 00000000..e69de29b diff --git a/deploy/docker-compose/docker-compose.yml b/deploy/docker-compose/docker-compose.yml new file mode 100644 index 00000000..a07bb72e --- /dev/null +++ b/deploy/docker-compose/docker-compose.yml @@ -0,0 +1,169 @@ +services: + engine: + # TODO: change to the public image once it's public + # image: ... + build: ../../engine + ports: + - 8080:8080 + command: > + sh -c "uwsgi --ini uwsgi.ini" + environment: + BASE_URL: https://$DOMAIN + SECRET_KEY: $SECRET_KEY + RABBITMQ_USERNAME: "rabbitmq" + RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD + RABBITMQ_HOST: "rabbitmq" + RABBITMQ_PORT: "5672" + RABBITMQ_DEFAULT_VHOST: "/" + MYSQL_PASSWORD: $MYSQL_PASSWORD + MYSQL_DB_NAME: oncall_hobby + MYSQL_USER: ${MYSQL_USER:-root} + MYSQL_HOST: ${MYSQL_HOST:-mysql} + MYSQL_PORT: 3306 + REDIS_URI: redis://redis:6379/0 + DJANGO_SETTINGS_MODULE: settings.hobby + depends_on: + mysql: + condition: service_healthy + oncall_db_migration: + condition: service_completed_successfully + rabbitmq: + condition: service_started + redis: + condition: service_started + + celery: + # TODO: change to the public image once it's public + build: ../../engine + command: sh -c "./celery_with_exporter.sh" + environment: + BASE_URL: https://$DOMAIN + SECRET_KEY: $SECRET_KEY + RABBITMQ_USERNAME: "rabbitmq" + RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD + RABBITMQ_HOST: "rabbitmq" + RABBITMQ_PORT: "5672" + RABBITMQ_DEFAULT_VHOST: "/" + MYSQL_PASSWORD: $MYSQL_PASSWORD + MYSQL_DB_NAME: oncall_hobby + MYSQL_USER: ${MYSQL_USER:-root} + MYSQL_HOST: ${MYSQL_HOST:-mysql} + MYSQL_PORT: 3306 + REDIS_URI: redis://redis:6379/0 + DJANGO_SETTINGS_MODULE: settings.hobby + CELERY_WORKER_QUEUE: "celery" + CELERY_WORKER_CONCURRENCY: "1" + CELERY_WORKER_MAX_TASKS_PER_CHILD: "100" + CELERY_WORKER_SHUTDOWN_INTERVAL: "65m" + CELERY_WORKER_BEAT_ENABLED: "True" + depends_on: + mysql: + condition: service_healthy + oncall_db_migration: + condition: service_completed_successfully + rabbitmq: + condition: service_started + redis: + condition: service_started + + oncall_db_migration: + build: ../../engine + command: python manage.py migrate --noinput + environment: + BASE_URL: https://$DOMAIN + SECRET_KEY: $SECRET_KEY + RABBITMQ_USERNAME: "rabbitmq" + RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD + RABBITMQ_HOST: "rabbitmq" + RABBITMQ_PORT: "5672" + RABBITMQ_DEFAULT_VHOST: "/" + MYSQL_PASSWORD: $MYSQL_PASSWORD + MYSQL_DB_NAME: oncall_hobby + MYSQL_USER: ${MYSQL_USER:-root} + MYSQL_HOST: ${MYSQL_HOST:-mysql} + MYSQL_PORT: 3306 + REDIS_URI: redis://redis:6379/0 + DJANGO_SETTINGS_MODULE: settings.hobby + depends_on: + mysql: + condition: service_healthy + + mysql: + image: mysql:5.7 + platform: linux/x86_64 + mem_limit: 500m + cpus: 0.5 + command: --default-authentication-plugin=mysql_native_password + restart: always + ports: + - 3306:3306 + volumes: + - dbdata:/var/lib/mysql + environment: + MYSQL_ROOT_PASSWORD: $MYSQL_PASSWORD + MYSQL_DATABASE: oncall_hobby + healthcheck: + test: "mysql -uroot -p$MYSQL_PASSWORD oncall_hobby -e 'select 1'" + timeout: 20s + retries: 10 + + redis: + image: redis + mem_limit: 100m + cpus: 0.1 + restart: always + ports: + - 6379:6379 + + rabbitmq: + image: "rabbitmq:3.7.15-management" + hostname: rabbitmq + mem_limit: 1000m + cpus: 0.5 + volumes: + - rabbitmqdata:/var/lib/rabbitmq + environment: + RABBITMQ_DEFAULT_USER: "rabbitmq" + RABBITMQ_DEFAULT_PASS: $RABBITMQ_PASSWORD + RABBITMQ_DEFAULT_VHOST: "/" + + mysql_to_create_grafana_db: + image: mysql:5.7 + platform: linux/x86_64 + command: bash -c "mysql -h ${MYSQL_HOST:-mysql} -uroot -p${MYSQL_PASSWORD:?err} -e 'CREATE DATABASE IF NOT EXISTS grafana CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'" + depends_on: + mysql: + condition: service_healthy + profiles: + - with_grafana + + grafana: + image: "grafana/grafana:8.3.2" + mem_limit: 500m + ports: + - 3000:3000 + cpus: 0.5 + environment: + GF_DATABASE_TYPE: mysql + GF_DATABASE_HOST: ${MYSQL_HOST:-mysql} + GF_DATABASE_USER: ${MYSQL_USER:-root} + GF_DATABASE_PASSWORD: ${MYSQL_PASSWORD:?err} + GF_SECURITY_ADMIN_USER: ${GRAFANA_USER:-admin} + GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:?err} + GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: grafana-oncall-app + GF_INSTALL_PLUGINS: grafana-oncall-app + volumes: + - ../../grafana-plugin:/var/lib/grafana/plugins/grafana-plugin + depends_on: + mysql_to_create_grafana_db: + condition: service_completed_successfully + mysql: + condition: service_healthy + profiles: + - with_grafana + +volumes: + dbdata: + rabbitmqdata: + caddy_data: + caddy_config: diff --git a/docs/img/GH_discussions.png b/docs/img/GH_discussions.png new file mode 100644 index 00000000..d3a1798a Binary files /dev/null and b/docs/img/GH_discussions.png differ diff --git a/docs/img/community_call.png b/docs/img/community_call.png new file mode 100644 index 00000000..22692fad Binary files /dev/null and b/docs/img/community_call.png differ diff --git a/docs/img/logo.png b/docs/img/logo.png new file mode 100644 index 00000000..ec11fc3a Binary files /dev/null and b/docs/img/logo.png differ diff --git a/docs/img/slack.png b/docs/img/slack.png new file mode 100644 index 00000000..d5ec5e6d Binary files /dev/null and b/docs/img/slack.png differ diff --git a/docs/sources/integrations/webhooks/_index.md b/docs/sources/integrations/webhooks/_index.md index 026458a8..c5a92ffe 100644 --- a/docs/sources/integrations/webhooks/_index.md +++ b/docs/sources/integrations/webhooks/_index.md @@ -9,4 +9,4 @@ You can use webhooks to send alert group notifications, and also to receive aler Follow these links to learn more about using webhooks for OnCall alert notifications: -{{< section >}} \ No newline at end of file +{{< section >}} diff --git a/engine/settings/hobby.py b/engine/settings/hobby.py new file mode 100644 index 00000000..4b2a4e8f --- /dev/null +++ b/engine/settings/hobby.py @@ -0,0 +1,57 @@ +# flake8: noqa: F405 + +from random import randrange + +# Workaround to use pymysql instead of mysqlclient +import pymysql + +from .prod_without_db import * # noqa + +pymysql.install_as_MySQLdb() + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.mysql", + "NAME": os.environ.get("MYSQL_DB_NAME"), + "USER": os.environ.get("MYSQL_USER"), + "PASSWORD": os.environ["MYSQL_PASSWORD"], + "HOST": os.environ.get("MYSQL_HOST"), + "PORT": os.environ.get("MYSQL_PORT"), + "OPTIONS": { + "charset": "utf8mb4", + "connect_timeout": 1, + }, + }, +} + +RABBITMQ_USERNAME = os.environ.get("RABBITMQ_USERNAME") +RABBITMQ_PASSWORD = os.environ.get("RABBITMQ_PASSWORD") +RABBITMQ_HOST = os.environ.get("RABBITMQ_HOST") +RABBITMQ_PORT = os.environ.get("RABBITMQ_PORT") + +CELERY_BROKER_URL = f"amqp://{RABBITMQ_USERNAME}:{RABBITMQ_PASSWORD}@{RABBITMQ_HOST}:{RABBITMQ_PORT}" + +MIRAGE_SECRET_KEY = SECRET_KEY +MIRAGE_CIPHER_IV = "1234567890abcdef" # use default + +APPEND_SLASH = False +SECURE_SSL_REDIRECT = False + +# TODO: OSS: Add these setting to oss settings file. Add Version there too. +OSS_INSTALLATION_FEATURES_ENABLED = True + +INSTALLED_APPS += ["apps.oss_installation"] # noqa + +CELERY_BEAT_SCHEDULE["send_usage_stats"] = { # noqa + "task": "apps.oss_installation.tasks.send_usage_stats_report", + "schedule": crontab(hour=0, minute=randrange(0, 59)), # Send stats report at a random minute past midnight # noqa + "args": (), +} # noqa + +CELERY_BEAT_SCHEDULE["send_cloud_heartbeat"] = { # noqa + "task": "apps.oss_installation.tasks.send_cloud_heartbeat", + "schedule": crontab(minute="*/3"), # noqa + "args": (), +} # noqa + +SEND_ANONYMOUS_USAGE_STATS = True