适用场景:宝塔面板 + 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 均可) |
docker -v
docker compose version
应输出类似:
Docker version 29.8.2
Docker Compose version v5.6.0
宝塔编排文件通常在:
/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
本例脚本路径为 /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:清理悬空镜像。
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-keygen -t ed25519 -f ~/.ssh/github_deploy -N ""
生成:
~/.ssh/github_deploy(私钥,给 GitHub)~/.ssh/github_deploy.pub(公钥,留服务器)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=...:无论客户端发什么命令,只执行指定脚本。- 这样即使私钥泄露,攻击者也只能触发部署,无法执行任意命令。
cat ~/.ssh/github_deploy
完整复制 -----BEGIN OPENSSH PRIVATE KEY----- 到 -----END OPENSSH PRIVATE KEY----- 的全部内容。
⚠️ 私钥只能填到 GitHub Secrets,绝不要提交到仓库或贴到聊天里。
在你本机(把私钥下载到本机后):
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 用户(见附录)。
仓库 → 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)
在项目仓库创建 .github/workflows/deploy.yml。
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。
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"
main。===== ... 开始部署 =====
...
===== 部署完成 =====
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
@v1 是浮动 tag,作者发新版会自动用上。想更稳,锁到具体 SHA:
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.0
到 https://github.com/appleboy/ssh-action/releases 查最新版本对应 SHA。
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。
/etc/ssh/sshd_config:
PasswordAuthentication no
PermitRootLogin prohibit-password
重启 SSH:
systemctl restart sshd
/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 容器名 后重试 |
如果 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。
服务器端
deploy.sh 已创建并 chmod +xbash deploy.sh 成功SSH
github_deploy 密钥已生成authorized_keys 并加 restrict,command=...GitHub
SSH_HOST / SSH_USER / SSH_PORT / SSH_KEY 已加 Secrets.github/workflows/deploy.yml 已创建安全
authorized_keys 已加 restrict,command=① 服务器:deploy.sh 手动跑通
② 服务器:生成 SSH 密钥,公钥加 restrict,command= 限制
③ 本地:用私钥 SSH 验证能自动触发部署
④ GitHub:配置 4 个 Secrets
⑤ GitHub:创建 deploy.yml
⑥ push 触发,Actions 自动 SSH 执行 deploy.sh
⑦ 服务器:容器自动更新,数据卷复用,不丢数据
至此,代码 push → 镜像构建推送 → 服务器自动更新 的完整 CI/CD 链路就跑通了。
有任何一步卡住,把报错原文和当前执行的命令贴出来,我帮你定位。记住:私钥永远不要贴出来。
还没有评论,登录后来说两句。