GitHub Actions 可通过 SSH 部署但需手动配置密钥、网络、权限及容错脚本;必须用 PEM 格式无密码私钥存为 secrets,非 root 用户部署,预建目录并设对属主;依赖应在 Actions 中构建后同步,避免服务器端 pip 安装;rsync 需排除 media/logs/db.sqlite3 等敏感路径;部署脚本应加 set -eux 并验证关键步骤,日志和环境差异问题需登录服务器排查。

GitHub Actions 能不能直接 SSH 到服务器执行部署
能,但得自己写脚本、配密钥、处理连接失败和权限问题——不是点个按钮就完事。GitHub Actions 默认运行在 GitHub 托管的 runner 上,它没有你的服务器网络访问权限,也不认识你本地的 ~/.ssh/id_rsa。
常见错误现象:Permission denied (publickey)、ssh: connect to host xxx port 22: Connection timed out、部署后文件权限错乱导致 Web 服务起不来。
- 必须用
SSH_PRIVATE_KEYsecrets 存私钥(不能明文写进 workflow),且私钥需为 PEM 格式、无密码(GitHub Actions 不支持交互式解密) - 目标服务器要开放 22 端口,且
sshd_config中PermitEmptyPasswords no和PasswordAuthentication no是安全前提,但别忘了把 GitHub Actions 的公钥加到~/.ssh/authorized_keys - 推荐用
webuser这类非 root 用户部署,避免sudo权限滥用;部署目录得提前建好、属主设对,否则git pull或pip install -e .会失败
workflow 文件里怎么写 Python 依赖安装和迁移命令
别在服务器上现场跑 pip install -r requirements.txt——网络不稳定、包源不可靠、没缓存还慢。应该在 GitHub Actions 的 Linux runner 上先装好依赖、打包好可运行环境,再推过去。
使用场景:Django/Flask 项目带数据库迁移(python manage.py migrate)、需要编译 C 扩展(如 psycopg2)、或用了 Poetry / Pipenv。
立即学习“Python免费学习笔记(深入)”;
- 用
actions/setup-python@v4指定版本,比如python-version: '3.11',和你服务器上的 Python 版本严格一致,否则.pyc兼容或venv路径会出问题 -
requirements.txt必须冻结所有依赖(含间接依赖),用pip freeze > requirements.txt生成,别留-e git+https://...这种动态引用——Actions 里没 git 凭据,也难调试 - 迁移命令(如
alembic upgrade head)不能放在 Actions 里跑,得放到服务器部署脚本里,并加判断:只在代码更新且alembic_version表存在时才执行,否则重复迁移会崩
怎么避免每次推送都覆盖整个项目目录
直接 rsync -av --delete 很危险:删掉 media/ 或 logs/ 目录下用户上传的文件,或者清空 SQLite 数据库文件。
性能影响:全量同步大项目(比如含 node_modules 或大量静态资源)会拖慢部署,且浪费带宽。
- 用
rsync排除敏感路径:--exclude='media/' --exclude='logs/' --exclude='db.sqlite3' --exclude='.env' - Python 项目不需要传
__pycache__/、*.pyc、.git/,加--filter="P .git/" --filter="P __pycache__/" - 更稳的做法是只同步变更文件:GitHub Actions 提供
github.event.before和github.event.after,可用git diff --name-only ${{ github.event.before }} ${{ github.event.after }}提取改动列表,再针对性 rsync
部署失败后怎么快速定位是哪一步挂了
GitHub Actions 日志默认折叠、滚动快、没颜色,而服务器端的 systemctl status myapp 或 journalctl -u myapp -n 50 才是真相所在。
容易被忽略的地方:部署脚本里每条命令后面不加 || exit 1,导致前一步失败(比如 git pull 报错)但后续仍继续执行,最后报错信息被掩盖。
- 在部署脚本开头加
set -eux:遇到错误立即退出、打印每条执行命令、显示变量展开结果 - 关键步骤后立刻验证,比如
git pull后跑git rev-parse HEAD并 echo 出来;pip install后检查which gunicorn是否存在 - 把服务器日志路径、服务名、健康检查 URL 写死在 workflow 注释里,下次排查时不用翻文档——比如:
# DEBUG: tail -f /var/log/myapp/app.log; systemctl restart myapp; curl http://localhost:8000/health
最麻烦的其实是环境差异:GitHub Actions 用 Ubuntu 22.04,你服务器是 CentOS 7,glibc 版本不兼容,某个 wheel 包直接 import 失败——这种问题不会在 CI 日志里报错,得登录服务器看 import traceback; traceback.print_exc() 输出。










