宝塔 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 -v
docker compose version
应输出类似:
Docker version 29.8.2
Docker Compose version v5.6.0
3.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.yaml
3.3 创建部署脚本
本例脚本路径为 /data/xiezitai_deploy/deploy.sh。
mkdir -p /data/xiezitai_deploy
cat > /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 "===== 部署完成 ====="
EOF
chmod +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
===== 部署完成 =====
确认容器状态:
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
这一步必须手动跑通,再继续。
四、SSH 密钥配置
4.1 生成专用密钥(服务器上执行)
ssh-keygen -t ed25519 -f ~/.ssh/github_deploy -N ""
生成:
~/.ssh/github_deploy(私钥,给 GitHub)~/.ssh/github_deploy.pub(公钥,留服务器)
4.2 把公钥加入授权列表并加限制
cat ~/.ssh/github_deploy.pub >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.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 本地验证登录
在你本机(把私钥下载到本机后):
chmod 600 ~/.ssh/github_deploy
ssh -i ~/.ssh/github_deploy -p 22 root@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: Deploy
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
script_stop: true
script: |
echo "trigger deploy"
因为服务器
authorized_keys里加了command=,这里的script内容会被服务器忽略,真正执行的是deploy.sh。
情况 B:构建推送 + 部署 一条龙
name: Build and Deploy
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
zhongdaiqi/xiezitai:latest
zhongdaiqi/xiezitai:${{ github.sha }}
deploy:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
port: ${{ secrets.SSH_PORT }}
key: ${{ secrets.SSH_KEY }}
script_stop: true
script: |
echo "trigger deploy"
六、触发验证
- 提交并 push 到
main。 - 打开 GitHub 仓库 → Actions 标签页。
- 点进 workflow,能看到部署日志,与手动 SSH 时一致:
===== ... 开始部署 =====
...
===== 部署完成 =====
- 服务器确认:
docker ps --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 deploy
usermod -aG docker deploy
mkdir -p /home/deploy/.ssh
cat ~/.ssh/github_deploy.pub > /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
GitHub Secrets 里 SSH_USER 改成 deploy。
7.3 禁用 root 密码登录
/etc/ssh/sshd_config:
PasswordAuthentication no
PermitRootLogin prohibit-password
重启 SSH:
systemctl restart sshd
7.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 改成某个 sha
sed -i 's|zhongdaiqi/xiezitai:latest|zhongdaiqi/xiezitai:<sha>|' docker-compose.yaml
docker compose up -d
或更规范的方式,用 .env 控制版本:
# docker-compose.yaml
image: zhongdaiqi/xiezitai:${IMAGE_TAG:-latest}
部署时:
IMAGE_TAG=<sha> docker compose 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. 公钥放到 opc
sudo mkdir -p /home/opc/.ssh
sudo chmod 700 /home/opc/.ssh
sudo vi /home/opc/.ssh/authorized_keys
# 加入:
# restrict,command="/data/xiezitai_deploy/deploy.sh" ssh-ed25519 AAAA...
sudo chown -R opc:opc /home/opc/.ssh
sudo chmod 600 /home/opc/.ssh/authorized_keys
# 2. opc 加入 docker 组
sudo usermod -aG docker opc
# 3. deploy.sh 里 docker 命令加 sudo,并配置免密
echo "opc ALL=(ALL) NOPASSWD: /usr/bin/docker" | sudo tee /etc/sudoers.d/opc-docker
GitHub 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 链路就跑通了。
有任何一步卡住,把报错原文和当前执行的命令贴出来,我帮你定位。记住:私钥永远不要贴出来。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/zhongdaiqi/article/details/167221221



