跳转至

GitLab 与 CI/CD:部署、Runner 与镜像构建

GitLab 是团队的代码协作与交付平台:它保存 Git 仓库、管理成员与权限、记录 Issue/Merge Request,并在代码变化后触发 CI/CD 流水线。本页只保留一条部署路线:GitLab 与 GitLab Runner 都使用 Docker Compose 运行;镜像构建采用 Runner 绑定宿主机 Docker Socket 的方式。

CI/CD 不是另一个软件,而是一套自动化交付方法:CI(持续集成)负责在代码合并前自动检查、测试和构建;CD 可以指持续交付或持续部署,负责把通过验证的制品送到测试或生产环境。GitLab 提供 Pipeline、Runner、Registry、Environment 和审批能力来落地这套方法,因此本站将 GitLab 和 CI/CD 放在同一页说明。

开发者 git push
  → GitLab:保存代码、触发 Pipeline
  → Runner:领取 Job,执行 .gitlab-ci.yml
  → Docker:构建镜像、推送到 GitLab Container Registry
  → 发布系统:拉取已验证的镜像并部署

先分清几个概念

概念 是什么 谁来使用
GitLab Project 一个代码仓库及其成员、Issue、Wiki、CI 配置 开发与项目成员
Group Project 的上级组织,可统一管理成员和权限 团队、部门
Merge Request(MR) 将分支变更合入目标分支的审核入口 开发、Reviewer
Pipeline 因 push、MR 或手动操作触发的一组自动任务 GitLab 调度
Job Pipeline 中一个具体步骤,如测试、构建镜像 Runner 执行
Runner 实际领取并运行 Job 的执行器,不是 GitLab 本体 运维维护
.gitlab-ci.yml 放在仓库根目录的流水线定义文件 GitLab 读取、Runner 执行
Artifact Job 产生并由 GitLab 保存的文件,如测试报告、安装包 后续 Job、下载者
Container Registry 保存容器镜像的仓库 CI 推送、部署端拉取

GitLab 不等于 Runner。 GitLab 负责保存、审批和调度;Runner 才拥有执行环境。GitLab 页面显示 Job 一直 Pending,通常是没有匹配标签、可用 Runner 离线,或 Runner 没有权限领取该 Job。

部署前的约束

  • 为 GitLab 准备稳定的域名,例如 gitlab.example.comexternal_url 必须与用户浏览器、Git clone URL、Runner 访问地址一致。
  • 本页示例使用 Web 端口 8929 和 SSH 端口 2424,避免与宿主机已有服务冲突。若改端口,external_urlgitlab_shell_ssh_portports 必须一起改。
  • /srv/gitlab 放在可靠的本地磁盘上。Git 仓库数据不适合放在性能不稳定的 NFS 目录。
  • GitLab 较重,先按实际项目数、仓库体积、并发 CI 和 Registry 容量规划 CPU、内存、磁盘与备份;不要把它和高负载 Runner 混放在同一台小机器上。

1. 使用 Docker Compose 部署 GitLab

在 GitLab 主机创建目录:

sudo mkdir -p /srv/gitlab/{config,logs,data}
sudo mkdir -p /opt/gitlab-compose
cd /opt/gitlab-compose

创建 docker-compose.yml。镜像标签必须固定为你已验证的正式版本;下方的 18.0.0 只是格式示例,升级时先在测试环境验证后再替换,不能直接使用 latest

services:
  gitlab:
    image: gitlab/gitlab-ee:18.0.0-ee.0
    container_name: gitlab
    restart: always
    hostname: gitlab.example.com
    shm_size: "256m"
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://gitlab.example.com:8929'
        gitlab_rails['gitlab_shell_ssh_port'] = 2424
    ports:
      - "8929:8929"  # Web/API;正式环境应由受控 HTTPS 入口代理
      - "2424:22"    # Git SSH
    volumes:
      - /srv/gitlab/config:/etc/gitlab
      - /srv/gitlab/logs:/var/log/gitlab
      - /srv/gitlab/data:/var/opt/gitlab

启动并观察初始化:

docker compose up -d
docker compose ps
docker compose logs -f gitlab

首次启动会进行初始化,Web 页面不是立即可用。容器稳定后访问 http://gitlab.example.com:8929;初始管理员为 root,初始密码可在首次启动后的有效期内读取:

docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password

登录后应立即修改 root 密码、建立普通管理员账户、禁用不需要的公开注册,并为项目设置成员角色。再用 Web 与 SSH 两种地址各做一次 clone,确认页面显示的地址与真实网络路径一致。

Compose 部署后的日常动作

目的 操作 注意事项
查看状态 docker compose psdocker compose logs -f gitlab 初始化期间耐心观察日志,不要反复删除容器
修改 GitLab 配置 修改 Compose 中的 GITLAB_OMNIBUS_CONFIG 或持久化的 /srv/gitlab/config/gitlab.rb 后重启 同一项配置不要在两个位置写出相互冲突的值
停止服务 docker compose down 只停止容器,不会删除上述挂载目录
备份 备份 configlogsdata,并按 GitLab 备份策略定期演练恢复 只备份仓库文件而漏掉配置/密钥,恢复通常不完整
升级 固定新镜像版本,先备份、在测试环境验证,再 docker compose pull && docker compose up -d GitLab 不应跨大版本跳跃升级

2. 使用 Docker Compose 部署 Runner

Runner 推荐放在单独的构建主机。这里使用 Docker executor + Docker Socket Binding:Job 在短生命周期容器中执行,而该容器通过 /var/run/docker.sock 调用 Runner 主机的 Docker 引擎构建镜像。

这条路线不需要 Docker-in-Docker,也不需要 privileged: true;但 Docker Socket 几乎等同于宿主机管理员权限,只能给受信任项目使用。不要把能修改 CI 配置的外部贡献者项目分配到这个 Runner。

在 Runner 主机创建 docker-compose.yml

services:
  runner:
    image: gitlab/gitlab-runner:alpine-v18.0.0
    container_name: gitlab-runner-docker
    restart: always
    volumes:
      - /srv/gitlab-runner/config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock
sudo mkdir -p /srv/gitlab-runner/config
mkdir -p /opt/gitlab-runner-compose
cd /opt/gitlab-runner-compose
docker compose up -d

在 GitLab 项目或 Group 的 Settings → CI/CD → Runners 创建 Runner,复制页面给出的 runner authentication token(通常以 glrt- 开头)。然后仍在 Runner 主机、同一 Compose 目录执行注册:

docker compose run --rm runner register \
  --non-interactive \
  --url "http://gitlab.example.com:8929/" \
  --token "glrt-替换为页面中的令牌" \
  --executor "docker" \
  --description "trusted-docker-builder" \
  --tag-list "docker-build" \
  --docker-image "docker:24.0.5-cli" \
  --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"
docker compose restart runner
docker compose logs -f runner

注册成功后,配置会保存到 /srv/gitlab-runner/config/config.toml。其中应确认 executor = "docker"、默认 image 存在,并且 volumes 包含 Docker Socket 与 /cache。如果 Job 长期 Pending,依次检查 Runner 是否 online、项目是否允许使用它、Job 的 tags 是否为 docker-build、以及 Runner 是否允许执行未受保护分支。

3. 让 Pipeline 打包并推送镜像

前提:项目已启用 Container Registry;Runner 有 docker-build 标签;仓库根目录有可正常执行的 Dockerfile。GitLab 会向 Job 注入 CI_REGISTRYCI_REGISTRY_IMAGECI_REGISTRY_USERCI_REGISTRY_PASSWORD,不需要把 Registry 密码写入仓库。

在项目根目录创建 .gitlab-ci.yml

stages:
  - test
  - package

variables:
  IMAGE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
  RELEASE_TAG: "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"

test:
  stage: test
  image: alpine:3.20
  tags: [docker-build]
  script:
    - echo "在这里替换为单元测试、语法检查或构建校验"

package-image:
  stage: package
  image: docker:24.0.5-cli
  tags: [docker-build]
  needs: [test]
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  before_script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
  script:
    - docker build --pull -t "$IMAGE_TAG" -t "$RELEASE_TAG" .
    - docker push "$IMAGE_TAG"
    - docker push "$RELEASE_TAG"

这份流水线只在默认分支构建发布镜像:$CI_COMMIT_SHA 是不可变追溯标签,适合部署与回滚;$CI_COMMIT_REF_SLUG 是分支标签,例如 main,方便查看最新构建。生产部署应优先指定 SHA 或明确的发布版本,不要只依赖 latestmain 这类可变标签

git push main
  → test 成功
  → package-image 使用当前提交构建
  → registry/project/image:<commit-sha>
  → 部署端拉取该确定标签

常见问题定位

现象 优先检查
GitLab 页面打不开或 clone 地址不对 external_url、DNS、端口映射、防火墙,以及 SSH 端口配置是否一致
Job 一直 Pending Runner 是否 online;Job 标签与 Runner 标签是否一致;项目是否允许使用 Runner
docker: not found Job 使用的 image 是否为 Docker CLI 镜像;Runner 的 Socket 是否挂载到 Job
Cannot connect to Docker daemon /var/run/docker.sock 是否存在、Runner config.toml 是否保存了 volume、Runner 是否重启生效
镜像 push 被拒绝 项目 Registry 是否启用;CI 变量是否存在;镜像地址是否使用 $CI_REGISTRY_IMAGE
构建能跑但存在安全风险 该 Runner 是否同时服务不可信项目;Socket Binding 是否被脚本滥用;应拆分受信任 Runner
GitLab 升级后异常 是否固定并逐版本升级;升级前是否备份并验证恢复;是否错误使用了 latest

交付的最小规范

  1. 受保护的默认分支只能通过 MR 合并,并要求至少一人审核。
  2. 每次镜像必须有提交 SHA 或发布版本标签,部署记录应写明使用的镜像完整地址。
  3. Runner 按信任边界拆分:可访问生产密钥、Docker Socket 或内网的 Runner 不服务未知项目。
  4. 密码、令牌、SSH 私钥写入 GitLab CI/CD Variables,并按环境设置保护范围;不要提交到仓库。
  5. GitLab 数据、Registry 和部署配置必须可备份、可恢复、可演练。

官方资料