环境

考虑到业务上对日志收集的需求和费用的问题,就直接将sentry部署到已有的演示环境中。演示环境是腾讯云两台服务器组成的docker swarm,并使用 Portainer 对docker swarm 进行管理,所以我们的部署都是通过 portainer 进行。
两台服务器的 /mnt目录时挂载的同一个NFS目录,这样方便容器进行统一挂载宿主机目录。

Sentry 说明

Sentry的服务端分为web、cron、worker这几个部分,应用(客户端)发生错误后将错误信息上报给web,web处理后放入消息队列(Redis存队列),worker从队列中消费数据进行处理,最终数据将存储在PostgreSQL。
其中web、cron、worker这几个部分使用的是同一个docker 镜像,只是启动时的参数不一样;分别对应 sentry run web、sentry run cron、sentry run worker。

部署

dockerhub上最新的sentry版本为9.1.2,还是3年前更新的,已经不建议自己直接使用 docker 镜像进行部署了,官方推荐基于 bash 脚本安装和升级;但是我还是选择了自己使用doker 镜像部署的方式(这样我就可以直接使用Portainer进行操作);

docker 配置pytorch_portainer

虚拟网络

创建一个覆盖网络,后续的所以容器都将加入该网络,我们直接使用portainer来创建:

docker 配置pytorch_运维_02

Redis

Redis 用来实现 Sentry中的消息队列, Redis Stack:

version: '3.7'
networks:
  yanshi-network: 
    external: true
services:
  redis:
    user: root
    image: redis:6.0
    networks:
      - yanshi-network
    deploy:
      replicas: 1
      update_config:
        parallelism: 1
        delay: 5s
        order: stop-first
      resources:
        limits:
          cpus: '0.5'
          memory: 1g
    ports: 
      - "6379:6379"
    environment:
      TZ : 'Asia/Shanghai'
    volumes:
      - /mnt/redis/data:/data
      - /mnt/redis/conf/redis.conf:/etc/redis/redis.conf
    entrypoint: docker-entrypoint.sh redis-server /etc/redis/redis.conf

注意:sentry9.1.2 和 redis 高版本存在兼容性问题,redis:6.0是OK的。

PostgreSQL

Sentry 需要PostgreSQL 来存储数据,PostgreSQL Stack:

version: '3.7'
networks:
  yanshi-network: 
    external: true
services:
  postgres:
    image: postgres:14.0
    networks:
      - yanshi-network
    privileged: true
    restart: unless-stopped
    volumes:
      - /mnt/postgres/data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: ******
    ports:
      - "5432:5432"

Sentry

sentry init

首先我们需要启动一个sentry cli 容器,通过 sentry init 命令生成配置文件 /etc/sentry/config.yml、/etc/sentry/sentry.conf.py, 所以我们需要启动一个sentry 的容器:

version: '3.7'
networks:
  yanshi-network: 
    external: true
services:
  cli:
    image: sentry:9.1.2
    networks:
      - yanshi-network
    volumes:
      - /mnt/sentry/conf:/etc/sentry/
    stdin_open: true # -i interactive
    tty: true # -t tty
    privileged: true
    entrypoint: ["sh"] # 执行 sh

然后进入容器执行 sentry init 命令,然后就会在宿主机 /mnt/sentry/conf 目录下会生成两个配置文件:

docker 配置pytorch_运维_03

sentry upgrade

上面我们生成配置文件后,我们就需要对配置文件进行修改;

/etc/sentry/config.yml

# 邮箱配置
mail.host: 'smtp.qq.com'
mail.port: 587
mail.username: xxxx@foxmail.com'
mail.password: 'xxxxxx'
mail.use-tls: true
mail.from: 'xxxx@foxmail.com'
# redis 配置
redis.clusters:
  default:
    hosts:
      0:
        host: redis
        port: 6379
        db: 0
        password: xxxxxx

/etc/sentry/sentry.conf.py

# 数据库配置
DATABASES = {
    'default': {
        'ENGINE': 'sentry.db.postgres',
        'NAME': 'sentry',
        'USER': 'xx',
        'PASSWORD': 'xxxxxx',
        'HOST': 'postgres',
        'PORT': '',
        'AUTOCOMMIT': True,
        'ATOMIC_REQUESTS': False,
    }
}
# Redis 配置
BROKER_URL = 'redis://:xxxx@redis:6379/0'

在 PostgreSql 中创建对应的数据库,我这里配置的数据库名为 sentry;
然后有到上一步(sentry init)的容器中执行 sentry upgrade, 这一步操作 sentry库中就会自动创建好系统需要的所有数据表, redis 中也会自动创建好相关的数据。
sentry upgrade 执行中会询问是否创建用户:

Would you like to create a user account now? [Y/n]:

我们选择创建,然后根据提示输入用户邮箱和密码就行啦。

sentry web \ worker \ cron

接下来我们就可以完整的部署 sentry了,完整的 stack yaml:

version: '3.7'
networks:
  yanshi-network: 
    external: true
services:
  cli:
    image: sentry:9.1.2
    networks:
      - yanshi-network
    volumes:
      - /mnt/sentry/conf:/etc/sentry/
    stdin_open: true # -i interactive
    tty: true # -t tty
    privileged: true
    entrypoint: ["sh"] # 执行 sh
  server:
    image: sentry:9.1.2
    privileged: true
    restart: unless-stopped
    networks:
      - yanshi-network
    volumes:
      - /mnt/sentry/data:/var/lib/sentry/files
      - /mnt/sentry/conf:/etc/sentry/
    command: /bin/bash -c "sentry run web"
    environment:
      C_FORCE_ROOT: "true"
    ports: 
      - "9000:9000"
  cron:
    image: sentry:9.1.2
    privileged: true
    restart: unless-stopped
    networks:
      - yanshi-network
    volumes:
      - /mnt/sentry/data:/var/lib/sentry/files
      - /mnt/sentry/conf:/etc/sentry/
    command: /bin/bash -c "sentry run cron"
    environment:
      C_FORCE_ROOT: "true"
  worker:
    image: sentry:9.1.2
    privileged: true
    restart: unless-stopped
    networks:
      - yanshi-network
    volumes:
      - /mnt/sentry/data:/var/lib/sentry/files
      - /mnt/sentry/conf:/etc/sentry/
    command: /bin/bash -c "sentry run worker"
    environment:
      C_FORCE_ROOT: "true"

部署完成后就可以通过 http://ip:9000 访问sentry了,输入用户密码登录。

docker 配置pytorch_portainer_04

坎坷

  1. sentry upgrade 时报错:django.db.utils.OperationalError: FATAL: database “sentry” does not exist
    没有在postgreSql中创建好对应的数据库,手动创建后就没问题了。
  2. sentry upgrade 创建用户时选择了N,没有用户登录
    这个可以通过 sentry createuser 来创建用户。
  3. 在管理页面测试邮件发送失败(连接超时)。
    这个是我配置的 mail.port 写错了。
  4. 在管理页面测试邮件发送正常,但邀请成员邮件发送失败。
    查看日志发现如下报错:
16:10:49 [WARNING] sentry.utils.geo: settings.GEOIP_PATH_MMDB not configured.
16:10:52 [INFO] sentry.plugins.github: apps-not-configured
16:10:52 [INFO] sentry.bgtasks: bgtask.spawn (task_name=u'sentry.bgtasks.clean_dsymcache:clean_dsymcache')
 
 -------------- celery@a6d25dd3513d v3.1.18 (Cipater)
---- **** ----- 
--- * ***  * -- Linux-3.10.0-1160.66.1.el7.x86_64-x86_64-with-debian-9.11
-- * - **** --- 
- ** ---------- [config]
- ** ---------- .> app:         sentry:0x7f055f43f150
- ** ---------- .> transport:   redis://:**@redis:6379/2
- ** ---------- .> results:     disabled
- *** --- * --- .> concurrency: 4 (prefork)
-- ******* ---- 
--- ***** ----- [queues]
 -------------- .> activity.notify  exchange=default(direct) key=activity.notify
                .> alerts           exchange=default(direct) key=alerts
                .> app_platform     exchange=default(direct) key=app_platform
                .> assemble         exchange=default(direct) key=assemble
                .> auth             exchange=default(direct) key=auth
                .> buffers.process_pending exchange=default(direct) key=buffers.process_pending
                .> cleanup          exchange=default(direct) key=cleanup
                .> commits          exchange=default(direct) key=commits
                .> counters-0       exchange=counters(direct) key=
                .> default          exchange=default(direct) key=default
                .> digests.delivery exchange=default(direct) key=digests.delivery
                .> digests.scheduling exchange=default(direct) key=digests.scheduling
                .> email            exchange=default(direct) key=email
                .> events.index_event_tags exchange=default(direct) key=events.index_event_tags
                .> events.preprocess_event exchange=default(direct) key=events.preprocess_event
                .> events.process_event exchange=default(direct) key=events.process_event
                .> events.reprocess_events exchange=default(direct) key=events.reprocess_events
                .> events.reprocessing.preprocess_event exchange=default(direct) key=events.reprocessing.preprocess_event
                .> events.reprocessing.process_event exchange=default(direct) key=events.reprocessing.process_event
                .> events.save_event exchange=default(direct) key=events.save_event
                .> files.delete     exchange=default(direct) key=files.delete
                .> integrations     exchange=default(direct) key=integrations
                .> merge            exchange=default(direct) key=merge
                .> options          exchange=default(direct) key=options
                .> reports.deliver  exchange=default(direct) key=reports.deliver
                .> reports.prepare  exchange=default(direct) key=reports.prepare
                .> search           exchange=default(direct) key=search
                .> sleep            exchange=default(direct) key=sleep
                .> stats            exchange=default(direct) key=stats
                .> triggers-0       exchange=triggers(direct) key=
                .> unmerge          exchange=default(direct) key=unmerge
                .> update           exchange=default(direct) key=update

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/sentry/digests/backends/redis.py", line 191, in maintenance
    self.__maintenance_partition(host, deadline, timestamp)
  File "/usr/local/lib/python2.7/site-packages/sentry/digests/backends/redis.py", line 181, in __maintenance_partition
    deadline,
  File "/usr/local/lib/python2.7/site-packages/sentry/utils/redis.py", line 239, in call_script
    return script(keys, args, client)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 2699, in __call__
    return client.evalsha(self.sha, len(keys), *args)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 1944, in evalsha
    return self.execute_command('EVALSHA', sha, numkeys, *keys_and_args)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 573, in execute_command
    return self.parse_response(connection, command_name, **options)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 585, in parse_response
    response = connection.read_response()
  File "/usr/local/lib/python2.7/site-packages/redis/connection.py", line 582, in read_response
    raise response
ResponseError: user_script:11: Attempt to modify a readonly table script: 094f8735408af273da283389ad983f63977831e8, on @user_script:11.
16:10:57 [ERROR] sentry.digests: Failed to perform maintenance on digest partition 0 due to error: ResponseError('user_script:11: Attempt to modify a readonly table script: 094f8735408af273da283389ad983f63977831e8, on @user_script:11.',)
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/sentry/digests/backends/redis.py", line 162, in schedule
    for key, timestamp in self.__schedule_partition(host, deadline, timestamp):
  File "/usr/local/lib/python2.7/site-packages/sentry/digests/backends/redis.py", line 152, in __schedule_partition
    deadline,
  File "/usr/local/lib/python2.7/site-packages/sentry/utils/redis.py", line 239, in call_script
    return script(keys, args, client)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 2694, in __call__
    return client.evalsha(self.sha, len(keys), *args)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 1944, in evalsha
    return self.execute_command('EVALSHA', sha, numkeys, *keys_and_args)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 573, in execute_command
    return self.parse_response(connection, command_name, **options)
  File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 585, in parse_response
    response = connection.read_response()
  File "/usr/local/lib/python2.7/site-packages/redis/connection.py", line 582, in read_response
    raise response
ResponseError: user_script:11: Attempt to modify a readonly table script: 094f8735408af273da283389ad983f63977831e8, on @user_script:11.
16:10:57 [ERROR] sentry.digests: Failed to perform scheduling for partition 0 due to error: ResponseError('user_script:11: Attempt to modify a readonly table script: 094f8735408af273da283389ad983f63977831e8, on @user_script:11.',)

后来google发现是redis版本太新,兼容性问题,换redis6.0后正常。