Appearance
GitLab CI/CD 实战:Docker 构建镜像并通过 SSH 部署到服务器
如果团队代码本来就在 GitLab 上,那很多时候最顺手的选择就是直接用 GitLab CI/CD。
先说结论
对典型 Java 服务来说,GitLab CI 很适合跑下面这条链路:
text
提交到指定分支 -> Maven 打包 -> Docker 构建镜像 -> 推送镜像仓库 -> SSH 到服务器更新服务如果团队已经在用 GitLab 管代码,那这通常比额外搭一层工具更自然。
一、一个最常见的 .gitlab-ci.yml 结构
一般会按阶段拆开:
builddockerdeploy
这样流水线更容易读,也更方便看失败点。
二、一个简化示例
yaml
stages:
- build
- docker
- deploy
variables:
IMAGE_NAME: registry.example.com/demo/user-service
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
build-job:
stage: build
image: maven:3.9.9-eclipse-temurin-21
script:
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
docker-job:
stage: docker
image: docker:27
services:
- docker:27-dind
script:
- docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASSWORD" registry.example.com
- docker build -t $IMAGE_NAME:$IMAGE_TAG .
- docker push $IMAGE_NAME:$IMAGE_TAG
deploy-job:
stage: deploy
image: alpine:3.20
before_script:
- apk add --no-cache openssh-client
- mkdir -p ~/.ssh
- echo "$DEPLOY_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
script:
- |
ssh $DEPLOY_USER@$DEPLOY_HOST "
docker pull $IMAGE_NAME:$IMAGE_TAG &&
export IMAGE_TAG=$IMAGE_TAG &&
cd /mydata/apps/user-service &&
docker compose up -d
"
only:
- main三、为什么 GitLab CI 很适合团队项目
1. 和分支策略结合得更自然
你可以很容易做到:
- feature 分支只构建不部署
- develop 自动发测试环境
- main 走生产发布
2. 和 Merge Request 配合方便
很多团队会把“流水线必须通过”作为合并条件之一。
3. 权限和变量管理更集中
像这些变量可以统一放在 GitLab CI Variables:
- 镜像仓库账号
- SSH 私钥
- 服务器地址
四、Docker in Docker 不是唯一选择
很多文章一提 GitLab CI + Docker 就直接上 dind,但它不是唯一方案。
你还可以考虑:
- Shell Runner 直接使用宿主机 Docker
- Kaniko 构建镜像
- BuildKit 或 buildx
五、部署到服务器时更推荐什么方式
如果你的线上环境本来就是 Docker 或 Compose 管理,那最顺的一条路通常是:
- 流水线只负责推镜像
- 远程服务器只负责拉镜像和重启服务
六、常见坑
1. 把所有逻辑都塞进 YAML
一开始图快,把大量 shell 直接写进 .gitlab-ci.yml 很常见。
2. 构建和部署用的是不同版本标识
如果镜像 tag 不统一,最后会搞不清线上到底跑的是哪个版本。
3. 生产和测试共用同一套变量
这会把环境隔离搞乱。
4. 只做部署,不做发布后校验
部署命令返回成功,不代表业务真的正常。
一句话总结
GitLab CI/CD 最大的优势,不只是“能跑流水线”,而是让代码仓库、分支策略、构建制品和部署动作形成一套更统一的工程闭环。