Ubuntu 24.04下用Docker Compose部署GitLab与MySQL 8.0实战
2026/9/13 4:45:44 网站建设 项目流程

1. 部署环境准备:Ubuntu 24.04 的初始化细节

1.1 版本选择与系统配置建议

聊到 Ubuntu 2024,其实就是 Ubuntu 24.04 LTS(Noble Numbat),2024 年 4 月发布,整整五年支持,作为 GitLab + MySQL 这种自托管服务的底座非常合适。如果你手头机器还是 22.04 LTS,也不用着急,下面这套方案在 22.04 上同样能跑,核心逻辑完全一致,只有 Docker 软件源的代号需要微调。

我建议你在部署之前,先用几个命令把机器底子摸清楚:

lsb_release -a uname -m free -h df -h /

这里面最容易被忽略的是架构。绝大多数服务器是 x86_64,但如果你用的是树莓派、飞腾或者一些 ARM 云主机,后面的 Docker 镜像拉取、Docker Compose 二进制下载都得换成 arm64 版本,这一步提前确认能省掉后面一半的排错时间。

GitLab 是个吃内存大户,官方最低建议是 4GB 内存跑一个最小化实例,配上 MySQL 8.0 之后,我个人建议至少 4GB,8GB 以上更稳。如果机器只有 2GB,后面我会单独讲怎么通过关闭 GitLab 内置组件和加 swap 来续命。

1.2 基础软件包与 apt 源处理

新机器到手,第一件事不是急着装 Docker,而是把系统基础环境理顺。我先装一批后面用得到的工具,顺手把 apt 源调成国内源。这里不是崇洋媚外,纯粹是实测下来,在部分区域默认源拉包的体验确实不稳定,换源后apt update速度快了不止一个量级。

sudo apt update sudo apt install -y curl wget vim net-tools ca-certificates gnupg lsb-release

换源的话,直接编辑/etc/apt/sources.list,把archive.ubuntu.comsecurity.ubuntu.com换成mirrors.ustc.edu.cn或者mirrors.tuna.tsinghua.edu.cn都可以。24.04 的源文件里可能是带Ubuntu-Sources的 deb822 格式,路径在/etc/apt/sources.list.d/ubuntu.sources,操作方法略有区别,就不展开贴全文了,你搜一下"Ubuntu 24.04 换源"就有现成模板。

还有一个容易忽略的点:如果你的服务器是刚开的云主机,建议先把unattended-upgrades的行为看清楚,有时候半夜系统自动更新内核,第二天 Docker 容器老出幺蛾子。我一般直接把自动更新关掉,改成手动维护窗口升级:

sudo dpkg-reconfigure unattended-upgrades

选择不启用即可。

1.3 端口规划:80、443、2222 到底谁用

GitLab 默认占用 80 和 443,因为网页访问、Git HTTP(S) 克隆都要走这俩端口。宿主机上的 22 端口如果已经被 SSH 占了,容器里的 22(GitLab 内置 SSH)就映射不出来,所以需要把宿主机的 2222 映射到容器的 22。

我实际部署时把端口规划固定成下面这样:

宿主机端口容器端口用途说明
8080GitLab HTTP 访问如果被 Nginx 占用,改成 8080
443443GitLab HTTPS 访问暂不用 HTTPS 可保留,不映射
222222GitLab SSH 克隆避免和宿主机 SSH 冲突
33073306MySQL 端口建议不要暴露到公网

这里多说一句,如果你不想让 MySQL 对公网开放,强烈建议不要映射3306:3306,或者只映射到127.0.0.1:3307:3306。GitLab 容器访问 MySQL 走的是 Docker 内部网络,根本不需要从宿主机暴露端口出来,端口映射给外部工具临时连库用的。

2. Docker 与 Docker Compose 的安装,以及离线场景怎么救

2.1 Docker Engine 在线安装的推荐方式

Ubuntu 自带 apt 源里的docker.io版本普遍偏老,Compose 插件也未必齐全,所以我还是推荐走 Docker 官方 apt 源装 Docker Engine。步骤不长,但每一步都有讲究:

# 1. 添加 Docker 官方 GPG 密钥 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # 2. 添加软件源 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # 3. 安装 sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

docker-compose-plugin装完以后,系统里就有 Compose v2 了,命令是docker compose(注意中间有空格),和老的docker-compose不是一回事。现在新项目我统一用 v2 语法。

装完先把服务拉起来,并设置开机自启:

sudo systemctl enable docker --now docker version docker compose version

2.2 配置镜像加速

Docker Hub 在国内访问时有时会拽不动大镜像,GitLab 的镜像动辄两三个 GB,拉取超时非常痛苦。这时候在/etc/docker/daemon.json里配置加速器是常规操作:

{ "registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn" ] }

注意,加速器地址有很多,有的需要你在云服务商控制台申请专属地址,有的是公共镜像站。加完以后:

sudo systemctl restart docker

然后验证:

docker info | grep -A 2 "Registry Mirrors"

能看到你配置的地址就算生效了。拉取镜像慢的问题,这一下就能改善不少。

2.3 离线安装 Docker 和 Compose 的保姆式步骤

生产环境经常遇到内网机,没外网权限,这时候装 Docker 就是另一套玩法。这套离线方案也是我们实际踩过不少坑才理清的。

先在一台能联网的同架构机器上,从 Docker 官方 apt 源把几个 deb 包下载下来:

apt download docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

如果怕漏依赖,用apt-get download会把当前机器的依赖也算进去,但目标机器可能有差异。稳妥的做法是直接去清华或者中科大的 docker-ce 仓库手动下载对应发行版代号和架构的包:

https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/dists/noble/pool/stable/amd64/

docker-cedocker-ce-clicontainerd.iodocker-buildx-plugindocker-compose-plugin这几个包全拷到目标机器上,然后:

sudo dpkg -i *.deb

如果提示缺依赖,直接执行:

sudo apt install -f -y

把剩余的依赖补上就行。装完同样要验证docker versiondocker compose version

离线环境下,如果不想装docker-compose-plugin这个插件包,也可以直接下载 Docker Compose 的独立二进制文件。去 GitHub 对应仓库的 release 页面下载docker-compose-linux-x86_64(或 arm64 版本),然后:

sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose docker-compose --version

这样你就得到了docker-compose老命令,配置文件写法基本一致,唯一的区别是执行命令时不带空格:docker-compose up -d

离线环境下还有一个问题:GitLab 和 MySQL 的镜像怎么搞?答案是docker savedocker load。先在联网机器上拉好镜像:

docker pull gitlab/gitlab-ce:17.3.0-ce.0 docker pull mysql:8.0.40 docker save -o gitlab-ce.tar gitlab/gitlab-ce:17.3.0-ce.0 docker save -o mysql-8.0.tar mysql:8.0.40

把两个 tar 文件拷到内网机器上,然后:

docker load -i gitlab-ce.tar docker load -i mysql-8.0.tar

需要注意,docker savedocker export不一样,save 出来的是完整镜像带历史层,load 回去之后docker images能看到完整信息,export 导出的是容器文件系统,load 不能直接跑起来。这个别搞混了。

3. 编排方案设计:为什么 GitLab 要"外置"一个 MySQL 8.0

3.1 不跑单容器,坚持 Compose 编排的理由

很多教程喜欢直接docker run一条命令把 GitLab 拉起来,图省事。但 GitLab 这种服务关联的数据持久化、配置管理、重启恢复,靠一条docker run根本照顾不过来。我第一次搭的时候就是吃了这个亏,容器删了重建,数据目录还在但又不敢乱动,最后整个重来。

用 Docker Compose 编排的核心价值在于:把两个容器的网络、数据卷、环境变量、依赖关系全部写进一个 YAML 文件,任何新同事拿到文件就能复现整套环境。以后的版本升级、配置修改,都是改 YAML 再执行一条命令的事。

我用的服务划分是:

  • gitlab:GitLab Community Edition 官方镜像
  • mysql:MySQL 8.0 官方镜像

两个容器放在同一个自定义网络里,通过服务名互相访问。这样的话,GitLab 连接数据库的连接串里只需要写mysql这个主机名,Docker 内部的 DNS 会自动解析到 MySQL 容器。

3.2 为什么不用 GitLab 内置 PostgreSQL

GitLab 默认自带 PostgreSQL,首次启动就会自动初始化好,开箱即用。既然内置数据库这么省事,为什么还要额外装一个 MySQL 8.0?

答案取决于使用场景。如果你只是给三五个人搭个私有 Git 仓库,那内置 PostgreSQL 就挺好,不需要折腾。但如果你所在的团队已经有了 MySQL 8.0 的运维规范、慢查询监控、定时备份通道,GitLab 只是众多业务系统之一,那让 GitLab 复用 MySQL 基础设施更符合实际情况。

GitLab 官方文档确实是支持 MySQL 8.0 的,只是要求使用 InnoDB 引擎、utf8mb4 字符集、MySQL 8.0.16 以上版本。把数据库独立出来,之后做数据迁移、故障切换、扩缩容都更灵活。我这边选择外置 MySQL 还有一层原因:监控和备份通道可以完全复用现成体系,不用为 GitLab 单独再维护一套 PostgreSQL。

3.3 数据卷与重启策略的规划

Compose 文件里最容易搞错的是数据卷的挂载方式。GitLab 容器有四个关键目录需要持久化:

  • /etc/gitlab:配置文件,含密钥
  • /var/opt/gitlab:应用数据、仓库
  • /var/log/gitlab:日志

MySQL 需要持久化的是:

  • /var/lib/mysql:数据库文件
  • /etc/mysql/conf.d:自定义配置目录,这里可放自定义 my.cnf

我建议用命名卷(named volume)或者宿主机目录都可以,我实际更倾向直接在宿主机指定一个明确的目录,比如/srv/gitlab/srv/mysql。好处是出问题的时候可以直接上宿主机看文件、备份目录,不需要再去翻 Docker 卷目录。

重启策略上一律用unless-stopped。这样宿主机重启后 Docker 能自动把两个容器拉起来,不用人工干预。

3.4 网络模型与端口映射:一个容易忽略的细节

两个容器在同一个 Compose 项目里,默认会创建项目名的桥接网络。GitLab 访问 MySQL 走的是容器网络,速度和安全性都好。外部用户访问 GitLab 只走 80/443 和 2222。

这里有一个比较隐蔽的坑:如果宿主机上的 80 端口已经被 Nginx 或者其他 Web 服务占了,GitLab 的external_url要同步修改。比如你只能映射8080:80,那external_url就得写成http://你的IP:8080,否则页面上生成的克隆地址是错的,用户复制下来也推不上去。

同理,SSH 克隆地址由gitlab_rails['gitlab_shell_ssh_port']控制。这个值必须等于宿主机映射出来的端口 2222,否则 GitLab 提示克隆地址时还会是ssh://git@host:22,导致连接失败。

4. docker-compose.yml 完整配置与逐段解读

4.1 完整 YAML 文件

下面这个 YAML 是我整理后的完整版,直接拿去改几个密码就能用:

version: "3.8" services: mysql: image: mysql:8.0.40 container_name: gitlab-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: "ChangeMe_Root_Pass" MYSQL_DATABASE: "gitlabhq_production" MYSQL_USER: "gitlab" MYSQL_PASSWORD: "ChangeMe_GitLab_Pass" TZ: "Asia/Shanghai" command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_general_ci - --default-time-zone=+08:00 volumes: - /srv/mysql/data:/var/lib/mysql - /srv/mysql/conf:/etc/mysql/conf.d ports: - "127.0.0.1:3307:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p$$MYSQL_ROOT_PASSWORD"] interval: 10s timeout: 5s retries: 10 start_period: 30s gitlab: image: gitlab/gitlab-ce:17.3.0-ce.0 container_name: gitlab restart: unless-stopped depends_on: mysql: condition: service_healthy environment: GITLAB_OMNIBUS_CONFIG: | external_url 'http://192.168.1.100' gitlab_rails['gitlab_shell_ssh_port'] = 2222 gitlab_rails['db_adapter'] = 'mysql2' gitlab_rails['db_encoding'] = 'utf8mb4' gitlab_rails['db_host'] = 'mysql' gitlab_rails['db_port'] = 3306 gitlab_rails['db_username'] = 'gitlab' gitlab_rails['db_password'] = 'ChangeMe_GitLab_Pass' gitlab_rails['db_database'] = 'gitlabhq_production' prometheus_monitoring['enable'] = false grafana['enable'] = false shm_size: '256m' ports: - "80:80" - "443:443" - "2222:22" - "5050:5050" volumes: - /srv/gitlab/config:/etc/gitlab - /srv/gitlab/data:/var/opt/gitlab - /srv/gitlab/logs:/var/log/gitlab healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost/-/readiness"] interval: 30s timeout: 10s retries: 10 start_period: 300s

4.2 MySQL 8.0 容器配置逐项说明

MYSQL_DATABASE这个环境变量是关键,MySQL 容器首次初始化数据目录时会自动创建这个库,并且把MYSQL_USERMYSQL_PASSWORD对应的账号授权到该库。GitLab 默认的数据库名就是gitlabhq_production,所以这里直接指定就好,不用再手动建库授权。

command里的--character-set-server=utf8mb4--collation-server=utf8mb4_general_ci是硬性要求。GitLab 连接 MySQL 时的编码必须是 utf8mb4,如果用了默认的 latin1,初始化数据库阶段就会报错或者出现乱码。--default-time-zone=+08:00按你的实际时区来,不设的话 MySQL 默认 UTC,GitLab 显示时间会差八个小时。

healthcheck里的$$MYSQL_ROOT_PASSWORD要注意有两个$,这是 Compose 的转义写法,避免被宿主机环境变量提前解析。mysqladmin ping是判断 MySQL 是否可用的最直观方式,这个健康检查是 GitLab 等待 MySQL 的先决条件。

/srv/mysql/conf目录空着也能挂载,MySQL 镜像会读取里面的.cnf文件。后续如果要调整慢查询日志、binlog 过期时间,直接往里面放配置文件再重启容器就行。

4.3 GitLab 容器配置逐项说明

GITLAB_OMNIBUS_CONFIG是 GitLab 容器最重要的环境变量,它接收一段 Ruby 配置代码,首次启动时会写入/etc/gitlab/gitlab.rb,然后触发gitlab-ctl reconfigure

external_url直接决定 GitLab 页面上所有链接的域名前缀。如果映射的是 80 端口,写http://你的IP或者域名即可,不要带端口。如果映射成了8080:80,这里就要写成http://你的IP:8080

gitlab_rails['gitlab_shell_ssh_port'] = 2222必须和宿主机映射端口一致,这样页面上生成的 SSH 克隆地址会正确带上 2222,用户复制出来的地址才真正可克隆。

prometheus_monitoring['enable'] = falsegrafana['enable'] = false是我强烈建议加的。GitLab 默认会启动一堆监控组件,对一个小团队或者个人部署来说,这些组件白白吃了几百 MB 内存,关掉之后整机负载能明显降下来。

shm_size: '256m'是给容器分配的/dev/shm大小。默认 64MB 对 GitLab 的某些操作来说太紧张,反正内存够的话给 256MB 比较稳妥。

depends_oncondition: service_healthy表示 GitLab 会等 MySQL 健康检查通过后才启动。否则两个容器同时起,GitLab 初始化时数据库还没起来,reconfigure 就会报错,然后反复重试。

5. 启动流程与初始化 GitLab

5.1 第一次启动的时间线

配置改好之后,进入 Compose 文件所在目录执行:

docker compose up -d

第一次启动拉镜像的时间取决于网络,GitLab 镜像大概 2.5GB,慢的时候能拉十几分钟。这里正好能看出镜像加速器有没有配置好。

镜像拉完开始创建容器,MySQL 一般 30 秒内就能起来。GitLab 就慢多了,它的初始化要经历gitlab-ctl reconfigure,这个过程会配置数据库、编译 assets、启动各个子服务,通常要 5 到 10 分钟。这段时间里你访问页面只会看到 502。

怎么判断 GitLab 是否就绪?两种方式:

docker ps docker logs -f gitlab

docker ps看 STATUS 列,如果健康检查通过会显示(healthy)。日志最末尾出现类似:

gitlab Reconfigured! gitlab Initializing...

说明已经 boot 完成。用curl -I http://localhost能收到 302 跳转就说明 Web 服务已经起来了。

5.2 获取 root 初始密码

GitLab 首次启动时会生成一个随机密码,存在容器内的/etc/gitlab/initial_root_password文件里,只有 root 用户能读:

sudo docker exec -it gitlab cat /etc/gitlab/initial_root_password

文件内容类似:

# WARNING: This value is valid only in the following circumstances # 1. If provided manually (either via `GITLAB_ROOT_PASSWORD` environment variable) # 2. Password has been changed automatically... Password: 一串随机字符

在这里我是比较推荐用GITLAB_ROOT_PASSWORD环境变量直接指定初始密码的,省去登录后再去容器里翻文件的麻烦,也更符合自动化部署的习惯。你在environment里加一行:

GITLAB_ROOT_PASSWORD: "YourStrongPass123!"

注意,这个变量只在首次初始化时生效,之后你再改它也不会重置密码。我建议设置一个临时强密码,首次登录后再通过界面改成正式密码,然后把环境变量从 Compose 文件里删掉。

5.3 首次登录与必改配置

浏览器打开http://你的IP,用户名root,密码用上面拿到的临时密码。登录成功后第一步去右上角头像 → Preferences 里改密码,然后顺手把语言切换成简体中文,位置在 Preferences → Localization → Language,改成zh_CN后 GitLab 界面立刻变中文,团队使用起来亲和力高不少。

接着建议立刻创建一个测试项目验证整套链路。新建项目后,按照页面提示添加 SSH Key。生成密钥的标准步骤:

ssh-keygen -t ed25519 -C "youremail@example.com" cat ~/.ssh/id_ed25519.pub

把公钥内容复制到 GitLab 的 SSH Keys 设置页。然后测试克隆:

ssh -T -p 2222 git@192.168.1.100 git clone ssh://git@192.168.1.100:2222/root/test-project.git

如果git clone能拉下来,说明整个 GitLab 服务的 Web 层和 SSH 层都通了。这里最常见的坑就是把克隆地址里的端口看漏,因为地址是ssh://git@host:2222/...格式,端口 2222 不能漏。

5.4 MySQL 8.0 的验证与授权调整

GitLab 起来后,MySQL 这边也应该看一眼,确认账号权限和数据初始化情况都正常:

docker exec -it gitlab-mysql mysql -uroot -p

登录后执行:

SHOW DATABASES; USE gitlabhq_production; SHOW TABLES; SELECT COUNT(*) FROM schema_migrations;

如果schema_migrations有几百条记录,说明 GitLab 的表结构已经完整的迁移到 MySQL 里了。这套部署就算真正落地了。

6. 从实际使用踩过的坑里提炼的排查清单

6.1 502 页面与 CPU 跑满

GitLab 刚启动时 502 是正常现象,重新 configure 完成后会自动恢复。但如果等了 20 分钟还是 502,就要上手查了。

我的排查顺序是:

docker logs --tail 200 gitlab docker stats free -h df -h

日志里如果反复出现puma的 worker 退出或者gitaly连接超时,大概率是内存不够。GitLab 的 Puma、Sidekiq、Gitaly 都吃内存,4GB 机器跑起来基本是满负荷,2GB 机器就算勉强起来也会频繁 OOM。

解决办法有两个方向:

  • 关闭内置监控组件(前面 YAML 里已经关了)
  • 调整 Puma worker 数,在GITLAB_OMNIBUS_CONFIG里加:
puma['worker_processes'] = 2 puma['min_threads'] = 4 puma['max_threads'] = 8

如果内存实在顶不住,加 swap 是最后的兜底手段:

sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

6.2 容器频繁重启的排查思路

MySQL 容器起来又挂,挂完又起,这种问题看docker logs gitlab-mysql基本就能定位。我遇到过的几类根因:

  • 宿主机磁盘写满:MySQL 强制崩溃。先df -h,清理/var/lib/docker下的悬空镜像和日志。
  • 数据卷权限不对:容器内 mysql 用户无法写入挂载目录。宿主机执行chown -R 999:999 /srv/mysql/data
  • 初始化数据目录后改了字符集参数:MySQL 8 在数据目录初始化完成后,改--character-set-server会导致启动失败,必须清空数据卷重新初始化。

最后一种是我最深刻的教训。第一次配 Compose 时没加字符集参数,初始化完发现编码不对,直接往 command 里加参数然后重启容器,结果 MySQL 直接拒绝启动,日志里明明白白写着Character set 'utf8mb4' is not a compiled character set之类的报错。最后只能把/srv/mysql/data清空重新来一遍。所以字符集参数必须在第一次启动前就定好,中途改基本等于删库重建。

6.3 GitLab 连接 MySQL 的认证插件踩坑

MySQL 8.0 默认的认证插件是caching_sha2_password,而老一些的 GitLab 版本用的 Ruby MySQL 驱动对这个插件支持得并不好,连接时会报Authentication plugin 'caching_sha2_password' cannot be loaded。后面 GitLab 版本逐步兼容了,但如果你用的镜像偏老,还是会踩到。

处理方式也有两种。一种是 MySQL 启动命令里加:

--default-authentication-plugin=mysql_native_password

但 MySQL 8.4 里这个参数已经移除了。更好的方式是单独给 GitLab 的账号指定认证插件:

ALTER USER 'gitlab'@'%' IDENTIFIED WITH mysql_native_password BY 'ChangeMe_GitLab_Pass'; FLUSH PRIVILEGES;

我个人更建议直接升级到支持caching_sha2_password的 GitLab 版本,欠的技术债早晚要还,MySQL 官方对mysql_native_password的淘汰是既定路线。

6.4 与 GitLab API Token 相关的连接报错

热词里有一条login failed. check api token or gitlab version. log in via git if the versi,这是各种外部工具对接 GitLab API 时的经典报错。常见场景是 Jenkins 或者 GitLab Runner 里填了 Personal Access Token,但版本要求或者权限范围没匹配上。

处理时核对三件事:

  1. Token 是否还有效,GitLab 页面 → Preferences → Access Tokens 里查状态
  2. Token 的 scope 是否勾选了api
  3. 使用的 GitLab API 版本路径是否正确,老代码写死/api/v3的换新版本基本连不上

另外提一嘴,如果后续要配置 GitLab Runner 做 CI/CD,Runner 注册时需要的 registration token 对应着 GitLab 高危安全更新的重要一环。官方多次针对 Runner Token 机制做过调整,你在界面上复制到的 token 要以你当前版本显示为准,不要网上抄一个旧命令就硬套。

6.5 热词里出现的高危漏洞修复

GitLab 社区每隔一段时间就会发布安全版本,如果你用的是命令行直接拉的最新镜像,docker-compose.yml里的镜像 tag 是固定的,不会自动升级。所以要主动保持镜像版本更新。

具体操作后面第 7 节会讲。这里提醒一句:GitLab 官方安全公告是值得订阅的,高危漏洞利用往往发生在公开几天之内,修复窗口越短越好。尤其是你暴露在公网上的实例,别等着被扫描到才去升。

7. 日常运维、备份策略与资源优化

7.1 备份方案:数据库要单独备份

GitLab 自带备份命令:

docker exec -t gitlab gitlab-backup create

这个命令会备份仓库、上传文件、数据库等内容,输出文件在容器内的/var/opt/gitlab/backups目录,也就是宿主机的/srv/gitlab/data/backups

但这里有一个很多人忽略的关键点:如果你用了外置 MySQL,gitlab-backup create会尝试连接你配置的 MySQL 并 Dump 数据库,这部分依赖 GitLab 容器运行时能正常连上 MySQL。一旦 MySQL 容器先挂了,GitLab 备份命令就会直接失败。

所以更稳妥的备份方案是:GitLab 数据备份和 MySQL 数据库备份分开做。

docker exec -it gitlab-mysql mysqldump -uroot -p'ChangeMe_Root_Pass' --single-transaction --routines --triggers gitlabhq_production > /srv/backups/gitlab-db-$(date +%F).sql

同时千万不要忘了备份/srv/gitlab/config里的一堆配置文件,特别是gitlab-secrets.json。这个文件里保存了 GitLab 的加密密钥,丢了它,之前的备份恢复到新环境时会直接失败,数据库数据即使还在也没法解密。

我用 crontab 做了每日备份:

0 2 * * * docker exec -t gitlab gitlab-backup create 2>>/var/log/gitlab-backup.log 10 2 * * * docker exec gitlab-mysql mysqldump -uroot -p'ChangeMe_Root_Pass' --single-transaction gitlabhq_production | gzip > /srv/backups/gitlab-db-$(date +\%F).sql.gz

备份恢复我用过一次,从备份创建恢复到新机器上的完整链路是:

  1. 装好 Compose 环境,起一个全新的 MySQL 容器
  2. 把 sql 备份导进 MySQL
  3. gitlab-secrets.jsongitlab.rb放回/srv/gitlab/config
  4. gitlab-backup产生的 tar 放到备份目录
  5. 启动 GitLab 容器,进入容器执行gitlab-backup restore BACKUP=xxxxxx

顺序不能乱,尤其是先恢复数据库再启动 GitLab,否则 GitLab 启动时发现版本状态和数据库状态不对应,会自动尝试迁移,很可能会失败。

7.2 GitLab 版本升级的正确姿势

升级 GitLab 最忌讳的是一步跨太多版本。官方对跨版本升级有严格的路径限制,比如从 16.x 升到 17.x,有的中间版本可能要求你先升到 16.11。所以升级前一定先查升级路径文档。

我的操作流程是:

# 1. 停掉旧容器 docker compose down # 2. 备份配置文件和数据目录 cp -a /srv/gitlab/config /srv/backups/gitlab-config-$(date +%F) docker run --rm -v /srv/gitlab/data:/backup busybox tar czf /backup-gitlab-data.tar.gz /backup # 3. 修改 docker-compose.yml 镜像版本 # 4. 重新拉取并启动 docker compose pull gitlab docker compose up -d

升级后第一次启动,reconfigure 时间比平时长很多,这是正常的,数据库迁移也需要时间。等容器状态变healthy后,先登录页面看版本号,再打开项目仓库确认历史 commit 和 MR 都正常,最后跑一次备份。升级后的备份很重要,万一有隐藏问题,至少能优雅回滚。

7.3 资源占用优化:关闭不需要的组件

GitLab 默认装了一整套生态组件,很多单机部署根本用不上。除了前面说的 Prometheus 和 Grafana,还有几个可以按需关闭的组件:

nginx['enable'] = true # 如果你前面架了其他 Nginx 做反代,可以关闭内置 Nginx registry['enable'] = false # 不打算做容器镜像仓库就关掉 gitlab_workhorse['enable'] = true # GitLab 核心组件,别关 gitaly['enable'] = true # Git 仓库存储核心,别关

关闭 Container Registry 能省下一部分内存,同时端口 5050 的映射也可以从 Compose 文件里去掉。

如果你要用 GitLab CI/CD,那 Runner 是额外的东西,GitLab 主容器管不到它。热词里有人说"gitlab ci/cd中docker镜像构建与自动化部署实践",这套环境跑起来之后,最顺理成章的扩展就是注册一个 Docker Executor 的 Runner,然后写.gitlab-ci.yml,用docker build构建镜像再推到 Hub 或者私有仓库。Runner 建议也以容器方式部署,和 GitLab 主容器一起放在 Docker 里,维护成本很低。

我在给团队做落地培训的时候,最后留的一句话是:这套方案别看步骤多,真正执行下来不到半天就能跑通,但数据库字符集必须在第一次启动前定好、备份必须坚持做、升级永远先备份,这三条红线守住了,后面基本就是按时升版本、日常看日志的节奏了。如果你只是自己的小项目用,把 Prometheus、Grafana、Registry 全关了,2GB 内存的小机器扛一个 GitLab 加 MySQL 也是能跑起来的,无非慢一点,稳定压倒一切。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询