Skip to content

这篇专门讲清楚一个疑问:GitHub Actions 明明跑在 GitHub 平台上,怎么把项目部署到我们自己的服务器?密钥传上去安全吗? 用一篇实战(Spring Boot 部署到阿里云 ECS)把整个链路讲透。

一、核心机制:云端 runner → SSH → 你的服务器

GitHub Actions 的执行环境(runner)在 GitHub 的云主机上,但它可以通过网络连到任何机器——包括你自己的服务器。部署的本质就是:

git push → Actions 云上跑 workflow → 用 SSH 连你的服务器 → 执行部署命令

GitHub Actions 部署到自己服务器

你的服务器不需要装任何 GitHub 组件,只要:

  1. 开 SSH 端口(默认 22)
  2. 有一个能被 SSH 登录的用户(建议专用 deploy 用户,见下文密钥安全)
  3. runner 能连到(公网 IP 可达)

二、密钥安全吗?Secrets 机制

疑问:"SSH 私钥不是要上传到 GitHub 吗?那不是泄密吗?"

答案:传,但用的是 GitHub Secrets(加密变量),不是明文:

  1. 密钥存在仓库 Settings → Secrets and variables → Actions(或组织级 Secrets)
  2. 加密存储:GitHub 加密保存,页面无法回读明文,有审计日志
  3. 运行时注入:workflow 里通过 ${{ secrets.XXX }} 引用,只在执行时注入环境,不会打印到日志
  4. 仓库设 private:别人看不到你的仓库更看不到 Secrets

最佳实践(关键)

做法说明
部署专用密钥不用服务器主账号(root)!单独建 deploy 用户 + 单独 SSH key,权限只限部署目录
仓库 private私有仓库,Secrets 不对外
最小权限deploy 用户只允许 scp 到指定路径、执行指定脚本
定期轮换密钥泄露风险随使用时间增加,定期换
自建 runner(替代方案)把 Actions runner 装到你自己服务器上跑——代码不出 GitHub,不用传密钥(适合保密要求高的场景)

三、实战:Spring Boot 自动部署到服务器

1. 服务器端准备(一次性的)

bash
# 1. 建部署专用用户(不直接用 root)
sudo useradd -m deploy

# 2. 生成部署专用密钥对
ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -N ""

# 3. 公钥给 deploy 用户
sudo mkdir -p /home/deploy/.ssh
sudo cp ~/.ssh/deploy_key.pub /home/deploy/.ssh/authorized_keys

# 4. 建应用目录 + 部署脚本
sudo mkdir -p /opt/myapp
sudo chown -R deploy /opt/myapp

2. GitHub 侧配置 Secrets

仓库 Settings → Secrets and variables → Actions → New repository secret

Secret 名
SERVER_HOST服务器公网 IP(如 47.96.158.10)
SERVER_USERdeploy
SERVER_SSH_KEY私钥内容(cat ~/.ssh/deploy_key 的输出,全选包括头尾注释

3. 写 workflow(.github/workflows/deploy.yml)

yaml
name: Deploy

on:
  push:
    branches: [ main ]      # main 分支推送时触发

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # ① 拉代码
      - uses: actions/checkout@v4

      # ② 装 JDK
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'

      # ③ 打包(跳过测试省时间)
      - run: mvn package -DskipTests

      # ④ 配好 SSH 私钥(从 Secrets 注入)
      - uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.SERVER_SSH_KEY }}

      # ⑤ 用 scp 把 jar 传到服务器
      - name: Upload jar
        run: |
          scp -o StrictHostKeyChecking=no \
            target/myapp.jar \
            ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}:/opt/myapp/

      # ⑥ ssh 远程执行部署脚本(停旧起新)
      - name: Restart service
        run: |
          ssh -o StrictHostKeyChecking=no \
            ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} \
            'cd /opt/myapp && kill -9 $(cat app.pid) 2>/dev/null; nohup java -jar myapp.jar > app.log 2>&1 & echo $! > app.pid'

4. 触发

bash
git push origin main
# → Actions 自动跑 → 检查 Actions 页面日志 → 部署完成

关键点

  • scp:把构建产物(jar)传到服务器
  • ssh '命令':远程执行部署命令(停旧进程、起新进程)
  • StrictHostKeyChecking=no:首次连接不交互确认(测试环境用;生产建议把服务器指纹加进 known_hosts)
  • Secrets 全流程加密,日志里不会出现私钥内容

四、进阶:更规范的部署方式

上面是"裸部署"(scp + 手动起进程),更规范的做法:

方式说明
scp + 脚本(本文)简单直接,适合小项目
Docker 部署jar 打成 Docker 镜像推到仓库(如阿里云 ACR),服务器 docker pull + compose up(见《Docker Compose 多容器编排》)
云原生K8s + 镜像仓库,GitHub Actions 只负责"构建推镜像",部署交给 K8s(滚动更新)

生产环境推荐 Docker 方式:GitHub Actions build 镜像 → push 到镜像仓库 → SSH 到服务器 docker compose pull && up -d——回滚就是 docker compose 切版本,简单安全。

五、常见问题

  1. SSH 连接超时:服务器安全组没开 22 端口 / 防火墙拦截——去云控制台检查
  2. Permission denied:deploy 用户没权限访问 /opt/myapp——chown -R deploy
  3. 私钥报错:Secrets 粘贴时丢了头尾注释(-----BEGIN OPENSSH PRIVATE KEY-----)或换行被吃——重贴完整内容
  4. workflow 语法错:用 GitHub 仓库里的 Action 校验 / 本地 vscode 装 YAML 插件
  5. 不想传密钥自建 runner——在服务器装 GitHub Actions runner(官方提供注册脚本),runner 在你服务器上跑,代码、密钥都不用上传 GitHub

小结

  • GitHub Actions 部署 = 云端 runner 用 SSH 连你自己的服务器执行部署
  • 密钥用 Secrets 加密存储 + deploy 专用密钥 + 仓库 private = 安全
  • 实战链路:checkout → JDK → build → scp jar → ssh 重启
  • 生产推荐 Docker 方式(build 镜像 → push → compose up)
  • 不想传密钥 → 自建 runner 到你自己服务器

基础概念见《CI/CD 快速入门》,容器化部署见《Docker Compose 多容器编排》。