手动部署通常意味着登录服务器、拉取代码、编译打包、重启服务,这些重复劳动不仅耗时,还容易因为操作失误导致线上故障。借助GitLab CI/CD和Nginx,可以把这一系列流程固化成自动化流水线:开发人员推送代码后,GitLab Runner自动执行构建与部署任务,Nginx负责对外提供Web服务。接下来我们逐步拆解整套方案,从Runner配置到生产环境落地,覆盖常见场景和踩坑点。

一、理解GitLab CI/CD流水线的核心组件
GitLab CI/CD依赖一个名为.gitlab-ci.yml的YAML文件,它定义了一条流水线中的各个阶段(stage)和任务(job)。每个任务会指定一个Runner来执行,Runner可以是GitLab实例自带的共享Runner,也可以是在服务器上安装的专用Runner。专用Runner的好处是能够直接访问内网资源,也便于控制部署目标。
流水线通常划分为build、test、deploy三个阶段。build阶段负责安装依赖并生成产物,test阶段执行单元测试或代码检查,deploy阶段把产物推送到目标服务器。如果某个阶段失败,后续阶段不会执行,这样能有效阻止有问题的代码上线。下面是一个基础的.gitlab-ci.yml结构,演示了三个阶段和两个任务如何组织:
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- npm install
- npm run build
artifacts:
paths:
- dist/
test_job:
stage: test
script:
- npm test
deploy_job:
stage: deploy
script:
- echo "部署任务占位"
这里使用了artifacts将build阶段生成的dist目录传递给后续阶段,避免重复构建。实际项目中,如果部署任务需要用到构建产物,可以在deploy_job中通过dependencies声明依赖关系,或者直接在前一个任务中完成部署。需要注意,每个job默认在全新的容器中运行,因此必须显式传递文件。
Runner的安装步骤并不复杂,对于Linux服务器,可以使用官方仓库安装gitlab-runner包,然后执行gitlab-runner register命令,按照提示填写GitLab实例地址、注册令牌以及执行器类型。执行器推荐选择shell,这样Runner直接在宿主机上执行脚本,便于调用ssh、rsync等系统命令。如果担心环境污染,也可以选择docker执行器,但部署时需要额外处理SSH认证。
二、通过SSH实现免密部署到Nginx服务器
最常见的部署方式是把构建产物通过SSH同步到远程Nginx服务器。首先需要在GitLab Runner所在的机器上生成SSH密钥对,并把公钥添加到目标服务器的authorized_keys文件中。这样Runner在执行deploy任务时,就能免密登录目标主机。生成密钥的命令如下:
ssh-keygen -t ed25519 -C "gitlab-runner-deploy" -f ~/.ssh/id_ed25519_deploy cat ~/.ssh/id_ed25519_deploy.pub
将公钥内容追加到目标服务器对应用户的~/.ssh/authorized_keys中。为了提高安全性,可以在目标服务器上为该部署用户设置受限shell,或者使用forced command来限制只能执行特定脚本。不过对于大多数中小型项目,直接使用一个独立的部署用户并限制其权限即可满足需求。
在.gitlab-ci.yml的deploy任务中,通过scp或rsync把dist目录同步到Nginx的站点根目录。例如,将dist下的所有文件复制到/var/www/myapp/目录:
deploy_job:
stage: deploy
before_script:
- eval $(ssh-agent -s)
- ssh-add ~/.ssh/id_ed25519_deploy
script:
- rsync -avz --delete dist/ deploy@192.168.1.100:/var/www/myapp/
- ssh deploy@192.168.1.100 "sudo systemctl reload nginx"
only:
- main
这段配置中,before_script启动了ssh-agent并添加私钥,这样后续的ssh和rsync命令才能使用免密认证。rsync的--delete参数会同步删除目标目录中多余的文件,保证远程目录与构建产物完全一致。最后通过ssh远程执行systemctl reload nginx让Nginx重新加载配置。若Nginx配置没有变化,也可以省略reload,但因为静态文件的替换通常不需要重载服务,只有修改了nginx.conf才需要reload或restart。
如果不想在每台Runner上保存明文私钥,可以改用GitLab的CI/CD变量功能,把私钥内容存为受保护的变量,然后在任务中写回临时文件。不过使用文件挂载的方式更简单,适合单Runner场景。无论哪种方式,都要确保私钥权限为600,否则ssh会拒绝使用。
三、Nginx站点配置与零停机发布策略
对于静态站点,Nginx只需要配置root指向部署目录即可。但直接覆盖文件可能导致访问瞬间出现404或加载到不完整的资源。为了解决这个问题,可以采用版本目录切换的方案:每次部署时把构建产物放到一个带时间戳或构建ID的新目录中,然后通过修改软链接的方式指向新目录,最后让Nginx reload。这种方式几乎不会中断服务。
假设站点根目录结构如下:/var/www/myapp/releases/目录存放多个版本,/var/www/myapp/current是指向当前版本的软链接。部署脚本可以这样写:
DEPLOY_DIR="/var/www/myapp/releases/$(date +%Y%m%d%H%M%S)" mkdir -p "$DEPLOY_DIR" rsync -avz --delete dist/ "deploy@192.168.1.100:$DEPLOY_DIR/" ssh deploy@192.168.1.100 "ln -sfn $DEPLOY_DIR /var/www/myapp/current && sudo systemctl reload nginx"
对应的Nginx配置中,root指向/var/www/myapp/current。当软链接原子地切换到新目录后,Nginx reload会重新解析软链接,新请求立即指向新版本。旧版本目录可以保留一段时间,便于快速回滚。如果出现严重问题,只需要把软链接指回上一个版本目录,再reload即可。
对于前后端分离的项目,Nginx往往还需要承担反向代理的角色,把/api/路径的请求转发给后端服务。此时部署任务除了同步前端静态文件,可能还需要重启后端进程。建议把后端服务交给systemd管理,部署时通过ssh执行sudo systemctl restart backend。注意给部署用户授权时,使用sudoers规则限制只能执行特定命令,避免权限过大。
四、完整流水线示例与常见问题排查
把上述内容整合到一个完整的.gitlab-ci.yml中,可以形成一个可用的自动化部署模板。模板包含缓存依赖、构建、测试、SSH部署和远程命令执行。下面是一个基于Vue或React项目的完整示例:
stages:
- build
- deploy
variables:
DEPLOY_HOST: "192.168.1.100"
DEPLOY_USER: "deploy"
DEPLOY_ROOT: "/var/www/myapp"
RELEASE_DIR: "${DEPLOY_ROOT}/releases/$(date +%Y%m%d%H%M%S)"
cache:
paths:
- node_modules/
build_job:
stage: build
image: node:18
before_script:
- npm ci
script:
- npm run build
artifacts:
paths:
- dist/
only:
- main
deploy_job:
stage: deploy
before_script:
- eval $(ssh-agent -s)
- ssh-add ~/.ssh/id_ed25519_deploy
script:
- mkdir -p "$RELEASE_DIR"
- rsync -avz --delete dist/ "${DEPLOY_USER}@${DEPLOY_HOST}:${RELEASE_DIR}/"
- ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "ln -sfn ${RELEASE_DIR} ${DEPLOY_ROOT}/current && sudo systemctl reload nginx"
only:
- main
这个模板使用了date命令生成版本目录名,但如果流水线在多个Runner上并行执行,会出现时间戳相同导致目录冲突。更稳妥的做法是使用GitLab预定义的CI_PIPELINE_ID变量,每个流水线都有唯一ID。把RELEASE_DIR改为${DEPLOY_ROOT}/releases/${CI_PIPELINE_ID}即可避免冲突。
实际部署中最常见的错误是SSH连接失败,通常由于密钥权限不正确、known_hosts校验失败或目标端口被防火墙拦截。可以在ssh命令后添加-v参数输出调试信息。另一个高频问题是Node版本与构建环境不匹配,导致npm install失败,因此建议在build_job中显式指定image为node:18或所需版本。此外,如果部署后页面资源404,检查Nginx的root路径是否正确指向current目录,以及是否有location /的try_files配置。
为了降低首次配置的复杂度,可以先在本地手动执行一遍部署脚本,确认ssh、rsync、sudo权限都正常,再把命令搬进.gitlab-ci.yml。这样能快速定位是CI环境特有还是基础配置问题。整套方案落地后,开发人员只需专注业务代码,每次合并到main分支即可自动完成构建与发布,真正实现提交即上线。
NginxGitLab CI/CD自动化部署修改时间:2026-10-02 10:04:58