使用 Github Action + Docker + Git 全自动部署项目到国内服务器,并优化部署速度到 10s
本文中,我会介绍我是怎么自动化部署我的 Nextjs 项目到多个服务器的,并分享一些部署技巧。成功以后,每次 git push 以后,项目会自动把生产构建部署到多个服务器,完全不需要你做任何额外的事情。
我的这个方法有如下几个显著优势:
- 完全适配国内网络环境。在各个方案中,这个方案绝对是部署速度最快的。有图有真相,在之后的自动化部署中,每次只花 10 秒左右

- 轻松的扩展。就算有再多的服务器,也能无痛添加
- 干净舒适的环境变量管理。每一台服务器有自己独立的环境变量,且配置清晰、管理方便,而且由于仓库就在服务器上,甚至还能方便的修改运行时变量,而无需重新构建运行容器。
- 享受 docker 带来的优秀特性,但是又全程免费。无需买阿里云的容器镜像服务等等
- 企业级的合规性,在各个环节的安全评估都经得起推敲。
也有两个局限性:
- 用于部署项目的机子,得有 git 仓库的访问权限。(但是一般生产部署的运维人员权限级别都很高,这应该不是问题)
- 没有分离构建过程和运行过程,所以用于部署项目的机子也要承担构建的算力开支,对机子的性能有要求。
前置知识
列出了读本文需要知道的一些名词。
- Github Action
通过在项目根目录配置 .github/workflow/xxx.yml 来启用 action。
这个在 gitee 里叫做“流水线”
- Github Runner
运行 github action 的环境,可以理解为 github 为你提供的虚拟机。
- Environment secrets
在 github 仓库设置页面有一个 Environment 配置,里面能配置不同的环境;
每一个环境,都有自己的 Secrets。
在 github 仓库设置的 Secrets and variables 页面,同样能看到这些配置。
部署流程设计思路
我的需求是这样的:
我的项目在 github 和 gitee 上都有 private repo,同时我需要把项目部署到国内和海外两台不同的服务器上。两台服务器上用到的环境变量还不一样。
最初,我想到了这几种方案,每个方案的优缺点我都会做出分析
方案1:利用第三方 docker 镜像托管服务
这是大部分人,或者 AI 推荐的做法。
推送代码到 github 以后,github runner (执行 github action 的虚拟机)会在 github 侧构建 docker 镜像,然后把 docker 镜像推送到 docker hub 中央仓库(这就需要在 github 仓库里配置 docker hub 的账号和密码)
然后,在你的服务器上进行 docker pull,从托管 docker 镜像的地方拉取镜像,然后在本地起容器运行。
我们不要在构建镜像的时候,把环境变量作为构建参数注入。而是应该把环境变量的注入时机调整到 docker run 或者 docker compose up 的时候。
如此一来,你的镜像就可以在多台服务器都能复用。
优点
- 构建和运行完全是分开的,你的服务器上只需要跑 docker 容器就行了,对你机子性能要求就不高
- 由于 Github Runner 本身就在海外,所以从
Dockerfilebuild 成镜像的过程不太容易因为网络原因出问题。 - 得益于 docker 的 layer 策略,服务器 docker pull 时需要传输的文件大小较小,不需要每次都重新传输一整个镜像。
缺点
在国内服务器上从 docker hub 拉取镜像的过程很不稳定,失败率极高。如果是使用国内阿里云的容器镜像服务之类的,又要面临不小的费用(最低 ¥45/ 月)
方案2:直接传输镜像到服务器
不太推荐这个方案
在 github runner 侧,利用 docker save 命令把镜像打包成 tar.gz 压缩包,然后传输给服务器,再从镜像获得容器
优点
不需要经过第三方 docker 仓库的中转,模型上看起来好像少一跳。
缺点
彻底丧失了 docker 缓存层的优势,每次都会传完整的镜像,里面包括了 Linux 内核、node 环境等等东西。
就拿 nextjs 项目举例,构建产物才 40MB,但是 docker 镜像有 250 MB。
而且从 github runner 传压缩包到服务器的过程也巨慢无比,不是很稳定。
方案3:弃用 docker,直接传构建产物
这个方案只适合个人开发者部署单个小项目。
思路是在 github runner 里把项目从源码变成构建产物,然后把各个项目的生产构建产物给打包传给服务器,然后直接把服务运行在服务器本机上。
用这个方法,相比方案2 的优化点就在于只需要传较小的构建产物。然而,这样做会丧失 docker 带来的优良特性。
这会在两个方面带来难度:
- 环境管理。你需要在服务器本机上跑起项目所需的环境,比如 node, jdk, maven…
- 服务管理。你需要用 pm2 或者 systemd 来管理多个服务,而不能像 docker 那样统一管理。
这样做,就要求运维人员懂各个项目的情况,对运维人员要求很高,运维和开发的分工会很混淆。
方案 4:本文要讲的方案
思路是我们直接在服务器上维护一个 git 仓库,然后每次让 github runner 去操作那台服务器拉最新源码,然后从源码构建 docker 镜像、跑容器。
这个方案的网络传输过程主要就是服务器拉取源码,以及从源码构建镜像的过程。
对于前者,由于我们可以使用国内的 gitee 仓库,而且每次拉取源码只会拉取源码变化的部分,而不是完整的仓库,所以过程是很快的;
对于后者,Dockerfile 构建镜像过程需要下载 Linux 内核,npm install 需要下载 node 包,但是这些过程都能很轻易的通过镜像源加速。
更有甚者,不同于每次都全新的 github runner 环境,我们的服务器上可以充分利用 Docker 缓存层的功能,每次 docker build 的时候,都会利用之前的缓存进行,所以实际情况是只有第一次慢一点(我的项目约 3 分钟),之后全流程只需要不到 10 秒。
部署步骤
1. 在 github 仓库设置页面,创建需要的环境

我这里有两台服务器,所以我就设置了两个环境。
每一个环境都可以设置自己的 Environment secrets,这个在稍后会被用作项目的 .env 环境变量。
在 Environment secrets 界面,我设置了这几个环境变量:
SERVER_HOST:服务器地址SERVER_USER:登录用的用户名SERVER_SSH_KEY:登录用的 SSH 私钥,这个私钥是用上面那个用户名的身份创建的SERVER_REPO_PATH:要部署的项目,在你机子上的路径。CLIENT_FRONTEND_ENV:你想使用的项目的 .env 文件的全部内容。之所以这么命名,是因为我的机子上可能还要部署 B 端的前后端项目,所以用 client 和 frontend 区分 B 端和 C 端以及前端和后端。
这里我管理 .env 文件的方式值得提一下。即使我们用前面的其他方案,这个细节也是比较重要的。
我们不把配置项拆开,也不用占位符去 hack 的替换他们,而是直接把 .env 文件一整个塞进 CLIENT_FRONTEND_ENV 里面。
之后,再想办法把这个 CLIENT_FRONTEND_ENV 在服务器上变现成真的 .env 文件即可。
2. 创建 github workflow 配置文件
.github\workflows\deploy-frontend.yml
1 | # 部署前端到所有服务器 |
在 matrix 块中,我暂时添加了 2 台服务器,对应着我在 github 仓库设置页面添加的两个 Environment。
部署过程中,两个服务器都会触发部署且互不影响、环境也是隔离的,部署任务是并行进行的。
github action 在这个过程中的作用,就是进入服务器的仓库路径,然后触发我们接下来要写的部署脚本,顺便把 github 仓库设置页面填写的 .env 文件注入进 ssh 会话环境,方便接下来 deploy 脚本读取。
3. 代码中创建 deploy bash 脚本
我们在服务器上,需要拉取最新源码、写 .env 文件、构建、启动容器等等。
为了防止在 github workflw yaml 文件中写太多东西(那里的 bash 没有语法高亮),我们直接新建一个部署脚本在项目代码里:
例如:frontend\scripts\deploy.sh
1 |
|
4. 创建 Docker 相关文件
下面这个 Dockerfile 是 Nextjs 官方提供的模板,我在其中加入了镜像源的配置。
如果你是其他框架的项目,也可以找找官方有没有提供部署示例模板。
frontend\Dockerfile
1 | # syntax=docker.io/docker/dockerfile:1 |
frontend\docker-compose.yml
如果不写 docker-compose ,我们的 docker run 命令就得带一堆参数,不是很优雅。
1 | services: |
frontend\.dockerignore
实测不带这个文件,在 docker desktop 里跑的时候,会因为拷贝文件太多而失败。
1 | node_modules |
5. 在服务器上 clone 项目
SSH 到你的服务器上,然后 cd 进你刚刚在 github Environment Secrets 里配置的 SERVER_REPO_PATH,git clone 你的项目到这个位置。
如果是私有仓库,这边可能就要求你鉴权。你可以通过把服务器上的公钥复制进 git 仓库的设置页面解决这件事。
克隆好以后,别忘了 checkout 到有 script 脚本的那个分支。
之后,你就可以美美发一版上去测试啦。
- 标题: 使用 Github Action + Docker + Git 全自动部署项目到国内服务器,并优化部署速度到 10s
- 作者: 三葉Leaves
- 创建于 : 2025-12-17 00:00:00
- 更新于 : 2025-12-31 22:02:07
- 链接: https://blog.oksanye.com/8d5330237612/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
预览: