https://github.com/getsentry/self-hosted
Установить docker, docker-compose, nginx, certbot
Скопировать по ftp: /srv/sentry в /srv/docker/sentry /etc/sentry_cleanup.sh в /srv/docker/sentry /mnt/data/docker/volumes/ в /mnt/data/docker/volumes/
nano /etc/nginx/conf.d/srv_sentry.conf
server {
server_name sentry.symfio.net;
# FOR SSL/HTTPS generation
location ^~ /.well-known {
set $no_cache 1;
allow all;
auth_basic off;
alias /isa/shared/.well-known/;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:9090/;
proxy_read_timeout 90;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/sentry.symfio.net/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/sentry.symfio.net/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = sentry.symfio.net) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name sentry.symfio.net;
return 404; # managed by Certbot
}
> ```
Если нужно сгенерировать ключи certbot'ом, то убираем все строчки и блоки из srv_sentry.conf, которые обозначены как сгенерированные Certbot'ом.
Генерируем сертификаты (домен должен быть уже переведен на IP-адрес и поднят nginx или создаем соответствующие папки /etc/letsencrypt/live/ и копируем туда готовые сертификаты
```bash</span>
certbot certonly --nginx --register-unsafely-without-email sentry.symfio.net
https://develop.sentry.dev/self-hosted/releases/
Последовательное (крупные изменения в БД) (версию 23.7.0 рекомендуется пропустить из-за трудностей с миграцией данных и обновлением Django) <your.sentry.version> -> 9.1.2 -> 21.5.0 -> 21.6.3 -> 23.6.2 -> 23.11.0 -> 24.8.0 -> 25.5.1 -> latest текущее состояние: 25.3.0 (жесткие точки остановки или hard stops) 23.6.2 -> 23.11.0 -> 24.8.0 -> 25.5.1->latest
Для версии с docker просто на соответствующей точке (версии) запускать
./install.sh
Например, текущая версия sentry на старом sy-jenkins: 21.6.3 https://github.com/getsentry/self-hosted/archive/refs/tags/21.6.3.tar.gz
cd /srv/docker/sentry
docker-compose down
# Резервируем текущую версию sentry и базу данных
mkdir sentry21.6.3
cd /srv/docker
tar -czpf srv-docker-sentry_21.6.3.tar.gz sentry/
mv srv-docker-sentry_21.6.3.tar.gz /srv/docker/sentry/sentry21.6.3/
mv sentry/21.6.3.tar.gz /srv/docker/sentry/sentry21.6.3/
cd /srv/docker/sentry
cp * sentry21.6.3
wget https://github.com/getsentry/self-hosted/archive/refs/tags/23.6.2.tar.gz
tar -xvzf 23.6.2.tar.gz
mv self-hosted-23.6.2/ sentry23.6.2
cpf sentry23.6.2/* /srv/docker/sentry
# Сверяем конфиги и меняем под наши
nano sentry21.6.3/.env
nano .env
nano sentry21.6.3/docker-compose.yml
nano docker-compose.yml
# Запускаем
./install.sh
docker-compose up -d
Проверяем на sentry.symfio.net
cd /srv/docker/sentry
docker-compose down
# Резервируем текущую версию sentry и базу данных
cd /srv/docker
tar -czpf srv-docker-sentry_23.6.2.tar.gz sentry/
mv srv-docker-sentry_23.6.2.tar.gz /srv/docker/sentry/sentry23.6.2/
mv sentry/23.6.2.tar.gz /srv/docker/sentry/sentry23.6.2/
cd /srv/docker/sentry
cp * sentry23.6.2
wget https://github.com/getsentry/self-hosted/archive/refs/tags/23.8.0.tar.gz
tar -xvzf 23.8.0.tar.gz
mv self-hosted-23.8.0/ sentry23.8.0
# Сверяем конфиги и меняем под новые конфиги, где надо
diff .env sentry23.8.0/.env
diff docker-compose.yml sentry23.8.0/docker-compose.yml
cpf sentry23.8.0/* /srv/docker/sentry
# Устанавливаем
./install.sh
# Проверяем миграции
docker compose run --rm web sentry django migrate --plan
Planned operations:
No planned migration operations.
# Запускаем
docker-compose down && docker-compose up -d
# Проверка успешно ли выполнена миграция
docker exec -it sentry_onpremise-web-1 sentry django showmigrations --plan
# Проверяем на sentry.symfio.net
# Если нужно создать пользователя
docker-compose run --rm web createuser
# или
sentry createuser
docker exec sentry_onpremise-clickhouse-1 clickhouse-client --query="SELECT project_id, COUNT(*) as span_count, formatReadableSize(SUM(length(op))) as est_size FROM spans_experimental_local WHERE timestamp >= now() - INTERVAL 4 HOUR GROUP BY project_id ORDER BY span_count DESC LIMIT 20 FORMAT PrettyCompact"
❌ Если получаем при миграции ошибку с отсутствующим volume для контейнера vroom (которого и не должно быть в 21.6.3)
Error response from daemon: open /mnt/data/docker/volumes/sentry_onpremise_sentry-vroom/_data: no such file or directory
Error in install/set-up-and-migrate-database.sh:12.
'$dcr web upgrade' exited with status 1
-> ./install.sh:main:34
--> install/set-up-and-migrate-database.sh:source:12
Решение: просто создаем ему фиктивный контейнер
mkdir -p /mnt/data/docker/volumes/sentry_onpremise_sentry-vroom/_data
❌ После миграции на 23.8.0 получаем ошибку при запуске docker-compose up -d
Error response from daemon: open /mnt/data/docker/volumes/sentry_onpremise_sentry-nginx-cache/_data: no such file or directory
Решение: Просто создать, как и в предыдущем случае
mkdir -p /mnt/data/docker/volumes/sentry_onpremise_sentry-nginx-cache/_data
❌ При миграции с 23.11.0 на 24.8.0 можно получить ошибку
Checking memcached backend ...
MemcachedCache found in sentry/sentry.conf.py, you should switch to PyMemcacheCache.
See:
https://develop.sentry.dev/self-hosted/releases/#breaking-changes
Решение: В связи с изменениями кэша, нужно поменять строчку: Меняем django.core.cache.backends.memcached.MemcachedCache на django.core.cache.backends.memcached.PyMemcacheCache
nano sentry/sentry.conf.py
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": ["memcached:11211"],
"TIMEOUT": 3600,
}
}
❌ Ошибка: CSRF Verification Failed  Решение: После настройки балансировщика (если есть) и обратного прокси следует обновить переменную system.url-prefix на текущий протокол и URL
nano /srv/docker/sentry/config.yml
system.url-prefix: 'https://sentry.symfio.net'
Возможно, стоит обновить раздел SSL/TLS. В противном случае можно получать ошибки, связанные с CSRF, при выполнении определенных действий, таких как настройка интеграций (если нет ошибок, то и не нужно)
nano /srv/docker/sentry/sentry.conf.py
❌ Ошибка:
self-hosted minimum docker compose version
Дело не в версии docker-compose, а именно в версии плагина docker'а. Необходимо его обновить. На CentOS 7,8 это сделать немного проблематично из-за того, что repo docker'а ограничивает версию до старой для этих ОС Решение:
hostnamectl
docker compose version
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin
dnf install docker-ce --nobest
docker compose version
systemctl restart docker.service
❌ Ошибка: при переносе получаем ошибку
=> ERROR [sentry-cleanup internal] load metadata for docker.io/library/sentry-self-hosted-local:latest 1.6s
=> CANCELED [subscription-consumer-events internal] load .dockerignore ... 2.2s
------
> [sentry-cleanup internal] load metadata for docker.io/library/sentry-self-hosted-local:latest:
------
failed to solve: sentry-self-hosted-local: failed to resolve source metadata for docker.io/library/sentry-self-hosted-local:latest: pull access denied,repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
Решение: Проблема заключается в том, что при install.sh собираются образы. Но при переносе собранных образов уже нет и он лезет за ними туда, где их быть не может (они все локально собираются). Поэтому нужно просто запустить пересборку локальных образов.
./install.sh build
docker-compose up -d
❌ Ошибка: HTTP 500 после входа в веб-интерфейс или бесконечная загрузка issues (именно их он открывает первыми) Решение: Проблема в том, что превышен лимит соединений скорее всего из-за увеличившегося количества событий: errors, spans, transactions, replays, которые шлются в sentry postgres >100 и скорее всего не включен pgbouncer.
nano sentry/sentry.conf.py
DATABASES = {
"default": {
"ENGINE": "sentry.db.postgres",
"NAME": "postgres",
"USER": "postgres",
"PASSWORD": "",
"HOST": "pgbouncer", # ✅ Через connection pooler
"PORT": "5432",
}
}
nano docker-compose.yml
postgres:
command:
["postgres", "-c", "max_connections=300"]
pgbouncer
environment:
DB_USER: ${POSTGRES_USER:-postgres}
DB_HOST: postgres
DB_NAME: postgres
AUTH_TYPE: trust
POOL_MODE: transaction
ADMIN_USERS: postgres,sentry
MAX_CLIENT_CONN: 10000
DEFAULT_POOL_SIZE: 25
MAX_DB_CONNECTIONS: 100
docker-compose down && docker-compose up -d
Оптимальная комбинация:
| Параметр | Значение | Почему |
|---|---|---|
| PostgreSQL max_connections | 200-300 | Запас, но не больше - каждое соединение ~10MB RAM |
| pgbouncer default_pool_size | 20-30 | Реальный пул на базу данных |
| pgbouncer max_client_conn | 10000 | Уже настроено - сколько клиентов может подключиться к pgbouncer |
Регистрируем бесплатную учетную запись на maxmind.com для GeoIP, если еще не регистрировались - проверить наличие файла /srv/docker/sentry/geoip/GeoIP.conf
nano /srv/docker/sentry/geoip/GeoIP.conf
# GeoIP.conf file for `geoipupdate` program, for versions >= 3.1.1. Used to update GeoIP databases from https://www.maxmind.com. For more information about this config file, visit the docs at
# https://dev.maxmind.com/geoip/updating-databases. `AccountID` is from your MaxMind account.
AccountID 909168
# `LicenseKey` is from your MaxMind account
LicenseKey ВобщемКлючЛицензионныйСвойВставляем
# `EditionIDs` is from your MaxMind account.
EditionIDs GeoLite2-ASN GeoLite2-City GeoLite2-Country
Чтобы оградить от того, чтобы забивался диск и слалась куча ерунды нужно программистам ограничить рейты до 0.01-0.05, кроме ошибок. Но если поток идет, а они пока не умеют/не знают, то можно принудительно это сделать
nano /srv/docker/sentry/sentry/sentry.conf.py
SENTRY_FEATURES["projects:sample-events"] = False
ENABLED_FEATURES = (
"organizations:discover",
"organizations:events",
"organizations:global-views",
"organizations:incidents",
"organizations:integrations-issue-basic",
"organizations:integrations-issue-sync",
"organizations:invite-members",
"organizations:metric-alert-builder-aggregate",
"organizations:sso-basic",
"organizations:sso-rippling",
"organizations:sso-saml2",
"organizations:advanced-search",
"organizations:dashboards-mep",
"organizations:mep-rollout-flag",
"organizations:dashboards-rh-widget",
"projects:custom-inbound-filters",
"projects:data-forwarding",
"projects:discard-groups",
"projects:plugins",
"projects:rate-limits",
"projects:servicehooks",
# feedback/issue UI
"organizations:user-feedback-ingest",
"organizations:user-feedback-replay-clip",
"organizations:user-feedback-ui",
)
DISABLED_FEATURES = (
# производительность/спаны/метрики – чтобы не слались в Kafka
"organizations:performance-view",
"organizations:trace-view-v1",
"organizations:transaction-metrics-extraction",
"organizations:dynamic-sampling",
"organizations:standalone-span-ingestion",
"organizations:profiling",
"organizations:session-replay",
"organizations:session-replay-enable-canvas",
"organizations:session-replay-enable-canvas-replayer",
"organizations:metrics-extraction",
"projects:span-metrics-extraction",
"projects:span-metrics-extraction-addons",
"organizations:starfish-browser-resource-module-image-view",
"organizations:starfish-browser-resource-module-ui",
"organizations:starfish-browser-webvitals",
"organizations:starfish-browser-webvitals-pageoverview-v2",
"organizations:starfish-browser-webvitals-replace-fid-with-inp",
"organizations:starfish-browser-webvitals-use-backend-scores",
"organizations:starfish-mobile-appstart",
)
SENTRY_FEATURES.update({feature: True for feature in ENABLED_FEATURES})
SENTRY_FEATURES.update({feature: False for feature in DISABLED_FEATURES})
cat relay/config.yml
relay:
upstream: "http://web:9000/"
host: 0.0.0.0
port: 3000
logging:
level: DEBUG
processing:
enabled: true
kafka_config:
- {name: "bootstrap.servers", value: "kafka:9092"}
- {name: "message.max.bytes", value: 50000000} #50MB or bust
sampling:
rules:
- id: keep-errors
sample_rate: 1.0
condition:
op: eq
name: event.type
value: error
- id: drop-transactions
sample_rate: 0.0
condition:
op: eq
name: event.type
value: transaction
- id: drop-spans
sample_rate: 0.0
condition:
op: eq
name: event.type
value: span
- id: drop-profiles
sample_rate: 0.0
condition:
op: eq
name: event.type
value: profile
- id: drop-replays
sample_rate: 0.0
condition:
op: eq
name: event.type
value: replay_event
default_sample_rate: 0.0
redis: redis://redis:6379
geoip_path: "/geoip/GeoLite2-City.mmdb"
rules:
- match: '*'
actions:
- type: set
target: user.ip_address
value: "{{http.headers.X-Forwarded-For.split(',')[0].trim()}}" # Первый IP
- type: set
target: extra.x_forwarded_for
value: "{{http.headers.X-Forwarded-For}}" # Полная цепочка для отладки
- type: set
target: tags.client_ip
value: "{{http.headers.X-Forwarded-For.split(',')[0].trim()}}" # Тег для отладки
quotas:
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: organization
scope_id: 1
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: fe2319ce6b2d4373bac433df66881a73
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: eadbea80dde5472885bafda0485830f1
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 423d544122b147ba926899530b85b8fb
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 1bec49216bc24883905bf631bcb34a53
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 2624c54d06104f9db81b18b98458cc24
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 8650e1896bc1479d823a6ccafdbda251
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 29d819da37be4214a846fbc792e09aca
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 00670f45ef76486b916d888cc7fb04fe
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 933fd548e6ce43b786d8d0b4cabce92b
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 371e7eb1590948a5b1813ca9b7cb3012
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 4b3936d2136c484cbe535243ce8c07f5
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 33d1a401e2eb4ce2a81f8ddc79acf840
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 3905b3726b214959854f7587b76cccab
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 12916a5a819d4f7086ef01cc96c68ccf
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: be755aeff2564090bd0936ee8aba8b0f
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 9fa6b71b9235439a833de7cb268a0c23
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 5e762b3ac78948a682c616b0a9fe3e93
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 26e622cec5ad4f7ab7f977a3aecd86eb
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 81370e57dd574e209bb5eb1798d0b3cb
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 008965e795214ea688493e75c3f878d9
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: dfae358bfe3d4a4a8a8d3fc15c03c938
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 731846834be54b7e9e4caa7eeb8dcca2
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 4055fe1b8f0447e794b555f73a0b23ff
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 99bc61c9c60417781ad6b9769fe7b7b2
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: f82823143bd00164fc932d809bf75b1b
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 273f3ac688d82d3a907f7863f4d4ca74
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 62499c0fbc456d604f78542174ecadf1
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 16057f6b31724eedb08e9cc4d561e2c5
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 3186a2c9a56a22ebb7fbbdf0a451cb4b
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 3b15c7e7c21a7f2ff17c86c8fa5cec82
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 48452bae29c639019941c18d439d5b6a
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: babf0b1d52716b7685ee6693a9b05259
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: d980beed2d3a17da8bc469de13fab7f1
reason_code: performance-disabled
- categories:
- transaction
- span
- profile
- replay_event
- replay_recording
limit: 0
window: 60
scope: key
scope_id: 503795a0e0eca5ccc48765620c344aec
reason_code: performance-disabled
тут важны sampling: rules: также rules: и, конечно quotas:
даем возможность делать квоты processing: enabled: true квоты по ключу проекта (по идее можно и scope: project scope_id: 233 - ID проекта, но не пробовал) scope: key scope_id: 503795a0e0eca5ccc48765620c344aec