Drone CI轻量级CI/CD实战:Docker+Compose+外网访问全链路解析
1. 为什么选 Drone CI 而不是 Jenkins一个被低估的轻量级持续集成真相Drone CI 这个词最近在 DevOps 小圈子刷屏但很多人点开官网第一眼就皱眉“文档写得像加密电报”“YAML 配置比写业务逻辑还烧脑”——这恰恰暴露了一个普遍误解把 Drone 当成 Jenkins 的简化版来用。我去年在三个不同规模的团队里落地过 CI/CD 流水线从 5 人初创公司到 80 人的 SaaS 产品线最终全部切换到了 Drone不是因为“新潮”而是因为 Jenkins 在真实场景中暴露出的结构性负担远超它表面提供的功能冗余。Jenkins 的核心问题从来不是功能少而是所有功能都建立在“可插拔”这个假设上。你装完 Git 插件、Docker 插件、Kubernetes 插件、Slack 通知插件……最后发现光是插件之间的依赖冲突和版本兼容性就吃掉了 30% 的运维时间。更致命的是Jenkins 的 Job 配置是 UIXML 混合体一份 Jenkinsfile 改动后必须登录 Web 控制台点“立即构建”才能验证而这个“立即构建”背后可能触发 7 个插件的初始化、8 个环境变量的注入、3 个全局工具的路径校验——整个过程不可见、不可复现、不可版本化。我在某电商客户现场亲眼见过一次 Jenkins Master 因为插件更新失败导致整个流水线瘫痪 4 小时而故障日志里只有一行“Failed to load plugin git-server: NoClassDefFoundError”。Drone 的设计哲学截然不同它不提供“插件”只提供“执行器”。所有构建逻辑、环境准备、产物推送、通知发送全部由你定义的 YAML 文件驱动运行在标准 Docker 容器中。这意味着什么意味着你本地docker run能跑通的命令在 Drone 里 100% 能跑通意味着你 GitHub PR 提交的.drone.yml文件就是唯一可信源Source of Truth不需要任何 Web 界面二次确认意味着当你需要排查构建失败时你不需要翻 Jenkins 的系统日志、插件日志、构建日志三层嵌套只需要看 Drone Agent 容器的标准输出——就像调试你自己的一个 shell 脚本一样直接。关键词Drone CI、持续集成、Docker、Docker Compose、外网访问并非随意堆砌。它们共同指向一个现实需求在资源有限单台云服务器 / 本地 NAS / 开发者笔记本的环境下用最接近生产环境的方式跑起一条真正能用、敢用、省心用的 CI 流水线。Jenkins 是一台功能齐全但需要专人保养的老式柴油机车Drone 则是一辆预装好导航、自动泊车、OTA 升级的电动轿车——你不需要懂发动机原理但必须清楚每条指令的语义和边界。接下来的内容不会教你“如何安装 Drone”而是带你亲手拆解一台 Drone 实例从裸机启动到能响应 GitHub 事件、拉取代码、执行测试、推送镜像、通知 Slack 的完整闭环每一步背后的决策依据、踩坑现场和绕过方案。2. Drone 的最小可行架构为什么必须用 Docker Compose 而不是单容器很多教程一上来就甩出docker run -d --name drone-server ...一行命令然后告诉你“搞定”。这种做法在演示视频里很酷但在真实部署中等于埋下定时炸弹。我见过至少 7 个团队在第二天就遇到问题Drone Server 容器重启后所有构建历史丢失或者 Agent 容器连不上 Server报错connection refused又或者配置文件改了一行整个服务起不来docker logs里只显示invalid configuration却不告诉你哪一行错了。根源在于Drone 不是一个单体应用而是一个Server-Runner 二元协作模型且 Server 本身严重依赖外部状态存储。先说清楚 Drone 的核心组件分工Drone ServerHTTP API 服务负责接收 SCM如 GitHub的 Webhook、解析.drone.yml、调度构建任务、存储构建日志和状态。它本身不执行任何构建命令。Drone Runner真正的“工人”监听 Server 下发的任务拉取代码、启动构建容器、收集输出、上报结果。一个 Server 可以对接多个 RunnerDocker Runner、SSH Runner、Exec Runner 等。外部数据库Drone Server 必须将用户、仓库、构建记录等持久化到外部数据库。官方支持 SQLite仅限开发、PostgreSQL、MySQL。绝对不要用 SQLite 生产环境——它不支持并发写入当两个 PR 同时触发构建时必然有一个失败并报database is locked。那么为什么docker-compose.yml是唯一合理的选择因为它天然解决了三个关键问题2.1 网络隔离与服务发现Drone Server 和 Runner 必须在同一个 Docker 网络内通信。如果用docker run分别启动你需要手动创建网络、指定--network、记住容器 IP 或用--link已废弃。而docker-compose.yml中定义的服务会自动加入一个默认网络并通过服务名如server、runner互相解析。Runner 配置里的DRONE_SERVER环境变量直接写http://server:8000即可无需硬编码 IP。2.2 配置与数据的声明式绑定Drone Server 的配置项极多比如DRONE_GITHUB_CLIENT_ID、DRONE_GITHUB_CLIENT_SECRET、DRONE_DATABASE_DRIVER、DRONE_DATABASE_DATASOURCE……这些绝不能写在命令行里否则docker inspect会暴露敏感信息且无法版本化管理。docker-compose.yml的environment块和volumes块让你能把所有配置集中在一个文件里配合.env文件分离密钥再用 Git 管理整个部署状态。2.3 生命周期一致性docker-compose up -d启动时会按依赖顺序depends_on启动服务docker-compose down关闭时会按相反顺序优雅停止。更重要的是restart: always策略让每个容器在宿主机重启、崩溃后自动恢复而 Jenkins 的systemd服务脚本往往需要额外编写健康检查和恢复逻辑。下面是我经过 12 次迭代、在 Ubuntu 22.04 / CentOS 7 / macOS Monterey 上均验证通过的docker-compose.yml核心骨架已剔除无关注释保留生产必需字段version: 3.8 services: server: image: drone/drone:2.22.0 restart: always ports: - 80:80 - 443:443 environment: - DRONE_SERVER_HOST${DRONE_SERVER_HOST} - DRONE_SERVER_PROTO${DRONE_SERVER_PROTO} - DRONE_GITHUB_SERVERhttps://github.com - DRONE_GITHUB_CLIENT_ID${DRONE_GITHUB_CLIENT_ID} - DRONE_GITHUB_CLIENT_SECRET${DRONE_GITHUB_CLIENT_SECRET} - DRONE_RPC_SECRET${DRONE_RPC_SECRET} - DRONE_DATABASE_DRIVERpostgres - DRONE_DATABASE_DATASOURCEpostgres://drone:dronedb:5432/drone?sslmodedisable - DRONE_TLS_AUTOMATEDtrue volumes: - ./drone-data:/data - /var/run/docker.sock:/var/run/docker.sock runner: image: drone/drone-runner-docker:1.12.0 restart: always depends_on: - server - db environment: - DRONE_RPC_PROTO${DRONE_SERVER_PROTO} - DRONE_RPC_HOST${DRONE_SERVER_HOST} - DRONE_RPC_SECRET${DRONE_RPC_SECRET} - DRONE_RUNNER_CAPACITY2 - DRONE_RUNNER_NAMElocal-docker-runner volumes: - /var/run/docker.sock:/var/run/docker.sock db: image: postgres:14-alpine restart: always environment: - POSTGRES_USERdrone - POSTGRES_PASSWORDdrone - POSTGRES_DBdrone volumes: - ./pg-data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U drone -d drone] interval: 30s timeout: 10s retries: 5提示DRONE_RPC_SECRET是 Server 和 Runner 之间通信的共享密钥必须严格保密且 Server 和 Runner 的值必须完全一致。我建议用openssl rand -hex 16生成存入.env文件切勿硬编码在 YAML 里。这个配置里藏着几个关键细节新手极易忽略./drone-data:/data这是 Drone Server 存储其内部 SQLite 缓存非主库、证书TLS 自动签发、插件配置的目录。必须挂载否则每次重启 Server 都会丢失 TLS 证书导致外网访问失败。/var/run/docker.sock:/var/run/docker.sockRunner 需要调用宿主机 Docker Daemon 来启动构建容器。这是 Docker-in-DockerDinD模式的核心也是 Drone 轻量化的基石——它不自己实现容器运行时而是复用宿主机能力。DRONE_TLS_AUTOMATEDtrue启用 Lets Encrypt 自动 HTTPS。这是实现“外部访问”的关键开关但前提是你的DRONE_SERVER_HOST必须是一个公网可解析的域名如ci.yourcompany.com且 80/443 端口对公网开放。3. 外网访问的生死线Lets Encrypt 自动签发与 Nginx 反向代理的抉择“实现外部访问”是标题里最易被轻视的一环。很多教程教你在docker-compose.yml里开ports: - 80:80 - 443:443然后告诉你“用浏览器访问http://your-server-ip就行了”。这在局域网内确实能打开 Drone Web UI但一旦你尝试用 GitHub 授权登录或让 Drone 监听 GitHub Webhook就会卡死在第一步GitHub 要求 OAuth Callback URL 必须是https协议而你的 IP 地址无法申请 Lets Encrypt 证书。这里必须厘清一个事实Drone 的DRONE_TLS_AUTOMATEDtrue功能只对DRONE_SERVER_HOST配置的域名生效且该域名必须能被公网 DNS 解析并能通过 80/443 端口收到 Lets Encrypt 的 ACME 协议挑战请求。如果你的服务器只有内网 IP如192.168.1.100或域名解析指向的是 NAT 内网地址这个功能会静默失败Server 日志里只有一行failed to obtain certificate没有任何错误详情。所以实现可靠外网访问只有两条路3.1 方案 A直连模式推荐给有独立域名和公网 IP 的用户这是最简洁、最符合 Drone 设计哲学的方案。你需要一个已注册并完成 DNS 解析的域名如ci.example.comA 记录指向你的服务器公网 IP服务器防火墙放行 TCP 80 和 443 端口docker-compose.yml中DRONE_SERVER_HOST设为ci.example.comDRONE_SERVER_PROTO设为https确保./drone-data目录有写入权限chown -R 1001:1001 ./drone-dataDrone 容器默认 UID 1001。启动后Drone Server 会自动向 Lets Encrypt 发起证书申请。首次申请可能需要 1-2 分钟期间访问https://ci.example.com会显示502 Bad Gateway或Connection Refused这是正常现象。成功后你会在./drone-data/certificates/目录下看到cert.pem和key.pem且docker logs drone-server会输出acme: Obtained certificate。注意Lets Encrypt 证书有效期为 90 天Drone 会自动在到期前 30 天续期。但如果你的服务器时间不准确如未启用 NTP续期会失败。务必在宿主机执行timedatectl status确认时间同步正常。3.2 方案 BNginx 反向代理模式推荐给无独立域名或需统一入口的用户当你没有域名或已有 Nginx 作为全站反向代理如托管多个服务gitlab.example.com、jenkins.example.com、ci.example.com则必须用 Nginx 做 TLS 终止。此时Drone Server 应退回到 HTTP 模式所有 HTTPS 处理由 Nginx 完成。Nginx 配置的关键点在于必须透传原始 Host 头和 WebSocket 升级头否则 Drone 的实时日志流会中断。以下是我线上稳定运行 18 个月的 Nginx 配置片段保存为/etc/nginx/conf.d/drone.confupstream drone_backend { server 127.0.0.1:8000; } server { listen 443 ssl http2; server_name ci.example.com; ssl_certificate /etc/letsencrypt/live/ci.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ci.example.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { proxy_pass http://drone_backend; proxy_set_header Host $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_set_header X-Frame-Options DENY; # 关键WebSocket 支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } # Drone 的健康检查端点 location /healthz { proxy_pass http://drone_backend; proxy_set_header Host $host; } } # HTTP 重定向到 HTTPS server { listen 80; server_name ci.example.com; return 301 https://$server_name$request_uri; }对应的docker-compose.yml需要调整DRONE_SERVER_PROTO改为httpDRONE_SERVER_HOST改为ci.example.com仍是域名Nginx 用它做虚拟主机匹配ports可以只开8000:8000不暴露 80/443 给公网由 Nginx 统一接管提示使用 Nginx 方案时DRONE_TLS_AUTOMATED必须设为false否则 Drone Server 会尝试自己监听 443 端口与 Nginx 冲突。无论哪种方案“外部访问”成功的标志只有一个在 GitHub 仓库 Settings Webhooks 页面点击Add webhookPayload URL 填https://ci.example.com/hookContent type 选application/jsonSecret 填你DRONE_RPC_SECRET的值点击Add webhook后右侧的 Recent Deliveries 里能看到200 OK的响应。这才是真正的、可用的外部访问。4. 从零配置第一个流水线.drone.yml的语义解析与实战避坑Drone 的灵魂不在 Server而在每个仓库根目录下的.drone.yml文件。它定义了“当代码发生变化时Drone 应该做什么”。很多初学者照着文档写完却得到Build Failed: no .drone.yml file found或Build Failed: invalid yaml根本原因是没理解 Drone YAML 的执行模型——它不是 Jenkins Pipeline 的 DSL而是一个声明式容器编排描述。4.1 Drone YAML 的三层结构pipeline、steps、services一个合法的.drone.yml必须包含kind: pipeline和type: docker表示使用 Docker Runner其核心是steps数组。每个step本质上就是一个docker run命令的声明--- kind: pipeline type: docker name: default steps: - name: build image: golang:1.21 commands: - go build -o myapp . - go test -v ./... - name: publish image: plugins/docker settings: repo: registry.example.com/myapp tags: ${DRONE_COMMIT_SHA} dockerfile: Dockerfile username: from_secret: docker_username password: from_secret: docker_password这段代码的执行逻辑是Drone Server 解析此文件创建一个名为default的 pipelineRunner 启动第一个容器image: golang:1.21在其中执行commands里的 shell 命令如果build步骤成功exit code 0Runner 启动第二个容器image: plugins/docker将settings转换为docker run参数执行镜像构建和推送。关键认知每个 step 都是隔离的容器彼此不共享文件系统、环境变量、进程空间。build步骤里go build生成的myapp二进制文件默认不会出现在publish步骤的容器里。解决方法有两个方案一推荐使用volumes挂载共享卷。在build步骤添加volumes: - /tmp/build:/tmp/build在publish步骤也挂载同一路径然后commands里指定-o /tmp/build/myapp。方案二使用plugins/docker的context和dockerfile字段让构建过程在同一个容器内完成即build和publish合并在一个 step 里。4.2 最容易踩的五个 YAML 坑附修复代码坑 1缩进错误导致invalid yamlYAML 对缩进极其敏感。常见错误commands:后少了空格写成commands:- go build应为commands:换行后- go buildsettings:下的字段缩进不一致有的 2 空格有的 4 空格修复用 VS Code 安装YAML插件开启editor.detectIndentation粘贴后按ShiftAltF格式化。坑 2环境变量引用语法错误想在commands里用 Git 提交信息写成echo $DRONE_COMMIT_SHA是错的Drone 的环境变量在容器内是小写drone_commit_sha且必须用${}包裹echo ${DRONE_COMMIT_SHA}。坑 3Secret 未正确声明from_secret: docker_password要求该 Secret 必须在 Drone Server 的 UI 或 CLI 中预先创建。在 Web UI 中Settings Secrets Add SecretName 填docker_passwordValue 填密码。Secret 名称区分大小写且不能包含下划线以外的特殊字符。坑 4when条件判断写错想只在 main 分支 push 时构建写成when: branch: main是错的正确语法是when: branch: - main event: - push坑 5trigger未配置导致不触发默认 Drone 会监听所有事件push、pull_request、tag。如果你想只监听特定分支的 push必须显式配置trigger: branch: - main event: - push4.3 一个生产就绪的 Node.js 流水线模板以下是我为一个 Vue 3 Express 全栈项目定制的.drone.yml已在线上稳定运行覆盖 lint、test、build、e2e、deploy 全流程--- kind: pipeline type: docker name: ci-cd # 只在 main 和 develop 分支的 push 事件触发 trigger: branch: - main - develop event: - push steps: - name: restore-cache image: meltwater/drone-cache:1.4.0 pull: if-not-exists settings: backend: filesystem cache_key: node-{{ arch }}-{{ remote }}-{{ checksum package-lock.json }} restore: true mount: - node_modules - name: install-dependencies image: node:18-alpine commands: - npm ci --no-audit --prefer-offline - name: lint image: node:18-alpine commands: - npm run lint - name: test-unit image: node:18-alpine commands: - npm run test:unit - name: build-frontend image: node:18-alpine commands: - cd frontend npm ci --no-audit --prefer-offline npm run build - name: build-backend image: node:18-alpine commands: - cd backend npm ci --no-audit --prefer-offline npm run build - name: e2e-test image: cypress/browsers:node18.17.0-chrome115-ff116 commands: - cd frontend npx cypress run --headless --browser chrome - name: deploy-to-staging image: appleboy/drone-scp when: branch: - develop settings: host: staging-server.example.com username: from_secret: ssh_user key: from_secret: ssh_key port: 22 target: /opt/app/staging source: - frontend/dist/** - backend/dist/** - name: deploy-to-production image: appleboy/drone-scp when: branch: - main settings: host: prod-server.example.com username: from_secret: ssh_user key: from_secret: ssh_key port: 22 target: /opt/app/production source: - frontend/dist/** - backend/dist/** # 缓存 node_modules加速下次构建 - name: rebuild-cache image: meltwater/drone-cache:1.4.0 pull: if-not-exists settings: backend: filesystem cache_key: node-{{ arch }}-{{ remote }}-{{ checksum package-lock.json }} rebuild: true mount: - node_modules depends_on: - install-dependencies这个模板的关键设计点使用meltwater/drone-cache插件缓存node_modules避免每次npm ci下载 200MB 依赖构建时间从 8 分钟降至 2 分钟e2e-test步骤使用官方 Cypress 镜像预装 Chrome 和 Firefox无需自己折腾浏览器环境deploy-to-*步骤用appleboy/drone-scp通过 SSH 密钥安全地将构建产物推送到目标服务器比rsync更可靠rebuild-cache步骤明确depends_on: install-dependencies确保只在依赖安装成功后才执行缓存重建。5. 故障排查全景图从docker-compose logs到构建失败的逐层定位部署 Drone 后最常遇到的问题不是“起不来”而是“看起来起来了但不工作”。比如GitHub Webhook 显示200 OK但 Drone UI 里看不到任何构建记录或者构建日志里卡在Cloning into /drone/src...无限等待。这类问题必须用分层排查法从基础设施到应用逻辑逐层下钻。5.1 第一层Docker Compose 服务状态执行docker-compose ps观察各服务状态server和runner必须是Up且Status列显示healthy如果配置了healthcheckdb必须是Up且Status显示healthyPostgreSQL 的pg_isready检查通过如果runner显示Restarting (1) 2 seconds ago说明它启动失败立刻执行docker-compose logs runner。常见runner启动失败原因DRONE_RPC_SECRET与server不一致日志里会报rpc: failed to authenticateDRONE_RPC_HOST无法解析日志里会报dial tcp: lookup server on 127.0.0.11:53: no such host检查docker-compose.yml里depends_on是否写错服务名/var/run/docker.sock挂载失败日志里会报Cannot connect to the Docker daemon at unix:///var/run/docker.sock检查宿主机 Docker 是否运行systemctl status docker以及挂载路径是否正确ls -l /var/run/docker.sock。5.2 第二层Drone Server 日志深度分析docker-compose logs -f server是黄金命令。重点关注三类日志类型 AWebhook 接收日志当 GitHub 发送 Webhook 时Server 日志会打印time2023-10-05T14:22:33Z levelinfo msgreceived webhook eventpull_request repoowner/repo time2023-10-05T14:22:33Z levelinfo msgenqueued build build123 repoowner/repo如果看到received webhook但没有enqueued build说明.drone.yml不存在或trigger条件不匹配。类型 BRunner 连接日志Runner 成功连接后Server 日志会打印time2023-10-05T14:22:33Z levelinfo msgrunner connected agentlocal-docker-runner如果长时间没有此日志检查runner容器日志或确认DRONE_RPC_SECRET是否一致。类型 C数据库错误日志如果日志里反复出现pq: database drone does not exist说明 PostgreSQL 初始化失败。检查db容器日志docker-compose logs db常见原因是./pg-data目录权限不对PostgreSQL 要求目录属主为postgres用户UID 70执行sudo chown -R 70:70 ./pg-data即可。5.3 第三层构建日志的“黑盒”解剖当构建卡在某个步骤如Cloning into /drone/src...不要只看 Drone UI 的日志框。Drone 的构建日志实际存储在 Server 的./drone-data/logs/目录下按repo_id/build_id/step_id.log组织。例如构建 ID 为123步骤 ID 为456日志文件就是./drone-data/logs/123/456.log。更高效的方法是在docker-compose.yml的server服务里临时添加volumes挂载将日志目录映射到宿主机方便查看server: # ... 其他配置 volumes: - ./drone-data:/data - ./drone-logs:/var/log/drone # 新增映射日志目录 - /var/run/docker.sock:/var/run/docker.sock然后tail -f ./drone-logs/*.log实时跟踪。5.4 第四层网络连通性终极验证当一切看似正常但 Runner 就是拉不到代码执行以下命令验证网络链路进入 Runner 容器docker-compose exec runner sh测试能否解析 GitHubnslookup github.com测试能否连通 GitHubping -c 3 github.com测试能否连通 Drone Servercurl -v http://server:8000/healthz测试能否连通 PostgreSQLpsql -h db -U drone -d drone -c SELECT 1如果nslookup失败说明容器 DNS 配置异常检查宿主机/etc/resolv.conf是否被污染如果curl失败检查server容器的netstat -tuln | grep 8000是否监听在0.0.0.0:8000而非127.0.0.1:8000Drone 默认监听0.0.0.0无需修改。经验之谈我处理过的 83% 的“构建卡住”问题根源都在runner容器的网络配置。一个简单但有效的验证是在runner容器里手动执行git clone https://github.com/owner/repo.git如果失败说明问题出在容器网络或 Git 凭据而非 Drone 本身。6. 运维长跑备份、升级与监控的实战守则Drone 部署完成只是起点真正的挑战在于长期稳定运行。Jenkins 的插件生态让它天然具备丰富的监控和备份方案而 Drone 的极简主义意味着这些能力需要你主动构建。6.1 数据备份只备份这三样其他都是临时的Drone 的核心数据只有三处必须定期备份PostgreSQL 数据库./pg-data目录是整个 Drone 状态的唯一真相源。每天凌晨 2 点执行#!/bin/bash DATE$(date %Y%m%d) docker-compose exec -T db pg_dump -U drone drone /backup/drone-db-$DATE.sql gzip /backup/drone-db-$DATE.sql find /backup -name drone-db-*.sql.gz -mtime 7 -deleteDrone Server 数据目录./drone-data包含 TLS 证书、OAuth Token、插件配置。每周六执行tar -czf /backup/drone-data-$(date %Y%m%d).tar.gz ./drone-data所有仓库的.drone.yml文件它们是流水线的“源代码”必须纳入 Git 版本控制。我要求所有团队在新建仓库时第一件事就是提交一个基础.drone.yml并设置保护分支规则禁止直接 push 到 main。注意./drone-data/certificates/目录下的证书是 Lets Encrypt 自动续期的不要单独备份它。如果服务器崩溃只要./drone-data目录存在Drone Server 重启后会自动续期。单独备份证书反而会导致续期失败。6.2 版本升级滚动升级的黄金法则Drone 的升级不是docker-compose pull docker-compose up -d就完事。必须遵循先升级 Runner再升级 ServerRunner 是无状态的“工人”升级影响小Server 是有状态的“大脑”升级需谨慎。永远使用具体版本号禁用latest标签drone/drone:2.22.0是安全的drone/drone:latest可能拉取到破坏性更新。升级前必做数据库迁移Drone Server 启动时会自动执行数据库迁移migration。但某些大版本如 1.x → 2.x需要手动执行drone-migrate工具。务必查阅 Drone 官方升级指南 确认当前版本到目标版本的迁移步骤。升级命令序列以升级 Server 为例# 1. 拉取新镜像 docker-compose pull server # 2. 查看新旧镜像差异可选 docker images | grep drone/drone # 3. 停止旧服务不删除容器保留网络 docker-compose stop server # 4. 启动新服务 docker-compose up -d server # 5. 观察日志确认迁移完成 docker-compose logs -f server | grep migrating database6.3 基础监控用 Prometheus Grafana 搭建 Drone 仪表盘Drone Server 内置/metrics端点需在docker-compose.yml中添加DRONE_METRICStrue暴露标准 Prometheus 格式指标。只需三步即可接入在server服务中添加环境变量environment: # ... 其他变量 - DRONE_METRICStrue部署 Prometheus配置抓取目标prometheus.ymlscrape_configs: - job_name: drone static_configs: - targets: [server:8000]在 Grafana 中导入社区模板 Drone CI Dashboard (ID: 11223) 即可看到活跃 Runner 数量构建成功率趋势过去