宝塔 Docker Compose + GitHub Actions CI/CD 完整教程
- 宝塔面板官网
- 写字台开源项目地址
- 甲骨文部署结果
- 腾讯云部署结果
适用场景:宝塔面板 + Docker Compose 编排部署 + Docker Hub 镜像仓库 + GitHub Actions 自动部署
本文以实际项目为例:镜像zhongdaiqi/xiezitai,服务器 Oracle Cloud,编排文件由宝塔管理。
一、整体架构
开发者 push 代码到 GitHub │ ▼ GitHub Actions ├─ 构建 Docker 镜像 ├─ 推送到 Docker Hub └─ SSH 登录服务器 │ ▼ 服务器 authorized_keys 限制 command="/data/xiezitai_deploy/deploy.sh" │ ▼ deploy.sh 执行 docker compose pull && up -d │ ▼ 容器更新完成二、前置条件
| 项 | 要求 |
|---|---|
| 服务器 | 已装 Docker + Docker Compose(插件版docker compose) |
| 宝塔面板 | 已用「Docker 编排」跑起项目 |
| 镜像仓库 | Docker Hub 账号,镜像已能 push |
| GitHub | 仓库已关联代码,Actions 可用 |
| 本地 | 有 SSH 客户端(Windows PowerShell / macOS / Linux 均可) |
三、服务器端配置
3.1 确认 Docker 环境
docker-vdockercompose version应输出类似:
Docker version 29.8.2 Docker Compose version v5.6.03.2 确认宝塔编排文件位置
宝塔编排文件通常在:
/www/server/panel/data/compose/<项目名>/docker-compose.yaml本例中项目名为xiezitai,所以:
/www/server/panel/data/compose/xiezitai/docker-compose.yaml确认内容里image:是你的 Docker Hub 镜像:
cat/www/server/panel/data/compose/xiezitai/docker-compose.yaml3.3 创建部署脚本
本例脚本路径为/data/xiezitai_deploy/deploy.sh。
mkdir-p/data/xiezitai_deploycat>/data/xiezitai_deploy/deploy.sh<<'EOF' #!/bin/bash set -e COMPOSE_DIR="/www/server/panel/data/compose/xiezitai" cd "$COMPOSE_DIR" echo "===== $(date) 开始部署 =====" docker compose pull docker compose up -d --remove-orphans docker image prune -f echo "===== 部署完成 =====" EOFchmod+x /data/xiezitai_deploy/deploy.sh说明:
set -e:任何一步失败立即退出,CI 能感知。docker compose pull:拉取最新镜像。up -d --remove-orphans:重建容器并清理孤立容器。image prune -f:清理悬空镜像。
3.4 手动验证脚本
bash/data/xiezitai_deploy/deploy.sh预期输出:
===== ... 开始部署 ===== [+] pull 2/2 ✔ Image zhongdaiqi/xiezitai:latest Pulled ✔ Image mariadb:11.4 Pulled [+] up 2/2 ✔ Container xiezitai Running ✔ Container xiezitai-db Healthy ===== 部署完成 =====确认容器状态:
dockerps--format"table {{.Names}}\t{{.Status}}\t{{.Image}}"这一步必须手动跑通,再继续。
四、SSH 密钥配置
4.1 生成专用密钥(服务器上执行)
ssh-keygen-ted25519-f~/.ssh/github_deploy-N""生成:
~/.ssh/github_deploy(私钥,给 GitHub)~/.ssh/github_deploy.pub(公钥,留服务器)
4.2 把公钥加入授权列表并加限制
cat~/.ssh/github_deploy.pub>>~/.ssh/authorized_keyschmod700~/.sshchmod600~/.ssh/authorized_keys编辑~/.ssh/authorized_keys:
vi~/.ssh/authorized_keys找到刚加的那行(ssh-ed25519 AAAA...),在行首加上限制:
restrict,command="/data/xiezitai_deploy/deploy.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... root@instance-xxx说明:
restrict:禁用端口转发、X11、PTY 等所有危险功能。command=...:无论客户端发什么命令,只执行指定脚本。- 这样即使私钥泄露,攻击者也只能触发部署,无法执行任意命令。
4.3 查看私钥内容(复制到 GitHub)
cat~/.ssh/github_deploy完整复制-----BEGIN OPENSSH PRIVATE KEY-----到-----END OPENSSH PRIVATE KEY-----的全部内容。
⚠️ 私钥只能填到 GitHub Secrets,绝不要提交到仓库或贴到聊天里。
4.4 本地验证登录
在你本机(把私钥下载到本机后):
chmod600~/.ssh/github_deployssh-i~/.ssh/github_deploy-p22root@ip地址预期:直接执行 deploy.sh,打印部署日志,然后自动断开,不给你 shell。
看到PTY allocation request failed是正常的(因为加了no-pty)。
如果看到Please login as the user "opc",说明 root 被 Oracle 禁了,需要改用opc用户(见附录)。
五、GitHub 配置
5.1 添加 Secrets
仓库 →Settings → Secrets and variables → Actions → New repository secret:
| Secret 名称 | 值 |
|---|---|
SSH_HOST | ip地址 |
SSH_USER | root |
SSH_PORT | 22 |
SSH_KEY | github_deploy私钥全文 |
如果镜像构建也在 GitHub Actions 里做,再加:
DOCKERHUB_USERNAMEDOCKERHUB_TOKEN(Docker Hub → Account Settings → Security → New Access Token)
5.2 创建 workflow
在项目仓库创建.github/workflows/deploy.yml。
情况 A:镜像已由其他 workflow 构建推送,只需要部署
name:Deployon:push:branches:[main]workflow_dispatch:jobs:deploy:runs-on:ubuntu-lateststeps:-name:Deploy via SSHuses:appleboy/ssh-action@v1with:host:${{secrets.SSH_HOST}}username:${{secrets.SSH_USER}}port:${{secrets.SSH_PORT}}key:${{secrets.SSH_KEY}}script_stop:truescript:|echo "trigger deploy"因为服务器
authorized_keys里加了command=,这里的script内容会被服务器忽略,真正执行的是deploy.sh。
情况 B:构建推送 + 部署 一条龙
name:Build and Deployon:push:branches:[main]workflow_dispatch:jobs:build-and-push:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v4-uses:docker/setup-buildx-action@v3-uses:docker/login-action@v3with:username:${{secrets.DOCKERHUB_USERNAME}}password:${{secrets.DOCKERHUB_TOKEN}}-uses:docker/build-push-action@v6with:context:.push:truetags:|zhongdaiqi/xiezitai:latest zhongdaiqi/xiezitai:${{ github.sha }}deploy:needs:build-and-pushruns-on:ubuntu-lateststeps:-name:Deploy via SSHuses:appleboy/ssh-action@v1with:host:${{secrets.SSH_HOST}}username:${{secrets.SSH_USER}}port:${{secrets.SSH_PORT}}key:${{secrets.SSH_KEY}}script_stop:truescript:|echo "trigger deploy"六、触发验证
- 提交并 push 到
main。 - 打开 GitHub 仓库 →Actions标签页。
- 点进 workflow,能看到部署日志,与手动 SSH 时一致:
===== ... 开始部署 ===== ... ===== 部署完成 =====- 服务器确认:
dockerps--format"table {{.Names}}\t{{.Status}}\t{{.Image}}"七、安全加固(强烈建议)
7.1 锁 appleboy 版本到 commit SHA
@v1是浮动 tag,作者发新版会自动用上。想更稳,锁到具体 SHA:
uses:appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2# v1.2.0到 https://github.com/appleboy/ssh-action/releases 查最新版本对应 SHA。
7.2 用专用用户替代 root(可选,更安全)
useradd-m-s/bin/bash deployusermod-aGdockerdeploymkdir-p/home/deploy/.sshcat~/.ssh/github_deploy.pub>/home/deploy/.ssh/authorized_keyschown-Rdeploy:deploy /home/deploy/.sshchmod700/home/deploy/.sshchmod600/home/deploy/.ssh/authorized_keysGitHub Secrets 里SSH_USER改成deploy。
7.3 禁用 root 密码登录
/etc/ssh/sshd_config:
PasswordAuthentication no PermitRootLogin prohibit-password重启 SSH:
systemctl restart sshd7.4 修改默认 SSH 端口
/etc/ssh/sshd_config改Port 2222,云安全组和宝塔防火墙同步放行,GitHub Secrets 里SSH_PORT改成2222。
八、回滚机制
构建时同时打latest和 commit sha 两个 tag(见情况 B)。回滚时:
cd/www/server/panel/data/compose/xiezitai# 临时把 image 改成某个 shased-i's|zhongdaiqi/xiezitai:latest|zhongdaiqi/xiezitai:<sha>|'docker-compose.yamldockercompose up-d或更规范的方式,用.env控制版本:
# docker-compose.yamlimage:zhongdaiqi/xiezitai:${IMAGE_TAG:-latest}部署时:
IMAGE_TAG=<sha>dockercompose up-d九、常见问题排查
| 现象 | 原因 | 解决 |
|---|---|---|
Permission denied (publickey) | 私钥不对/不全 | 重新cat ~/.ssh/github_deploy,完整粘贴到SSH_KEY |
Load key ... error in libcrypto | 用了.pub公钥登录 | 改用私钥(无.pub后缀) |
PTY allocation request failed | 正常(no-pty) | 忽略 |
Please login as the user "opc" | Oracle 禁用 root | 改用opc用户,见附录 |
dial tcp ... i/o timeout | 网络/防火墙 | 检查 Oracle 安全组、宝塔防火墙放行 SSH 端口 |
Host key verification failed | known_hosts | appleboy默认跳过,一般不会遇到 |
| Action 成功但容器没更新 | 脚本未执行 | 检查authorized_keys的command=路径 |
Conflict. container name already in use | 同名容器残留 | docker rm -f 容器名后重试 |
十、附录:Oracle Cloud 用 opc 用户
如果 root 被禁,改用opc:
# 1. 公钥放到 opcsudomkdir-p/home/opc/.sshsudochmod700/home/opc/.sshsudovi/home/opc/.ssh/authorized_keys# 加入:# restrict,command="/data/xiezitai_deploy/deploy.sh" ssh-ed25519 AAAA...sudochown-Ropc:opc /home/opc/.sshsudochmod600/home/opc/.ssh/authorized_keys# 2. opc 加入 docker 组sudousermod-aGdockeropc# 3. deploy.sh 里 docker 命令加 sudo,并配置免密echo"opc ALL=(ALL) NOPASSWD: /usr/bin/docker"|sudotee/etc/sudoers.d/opc-dockerGitHub Secrets 里SSH_USER改成opc。
十一、完整检查清单
服务器端
- Docker / Docker Compose 已装
- 宝塔编排文件就位,服务正常运行
deploy.sh已创建并chmod +x- 手动
bash deploy.sh成功
SSH
github_deploy密钥已生成- 公钥加入
authorized_keys并加restrict,command=... - 权限 700 / 600 正确
- 本地用私钥 SSH 登录成功(自动触发部署)
GitHub
SSH_HOST/SSH_USER/SSH_PORT/SSH_KEY已加 Secrets.github/workflows/deploy.yml已创建- push 后 Actions 运行成功
- 服务器容器确认已更新
安全
- 私钥只存 GitHub Secrets
authorized_keys已加restrict,command=- (可选)锁 appleboy commit SHA
- (可选)专用用户替代 root
- (可选)禁用密码登录
十二、流程总结
① 服务器:deploy.sh 手动跑通 ② 服务器:生成 SSH 密钥,公钥加 restrict,command= 限制 ③ 本地:用私钥 SSH 验证能自动触发部署 ④ GitHub:配置 4 个 Secrets ⑤ GitHub:创建 deploy.yml ⑥ push 触发,Actions 自动 SSH 执行 deploy.sh ⑦ 服务器:容器自动更新,数据卷复用,不丢数据至此,代码 push → 镜像构建推送 → 服务器自动更新的完整 CI/CD 链路就跑通了。
有任何一步卡住,把报错原文和当前执行的命令贴出来,我帮你定位。记住:私钥永远不要贴出来。