资讯动态

Docker实战:用容器化方案部署GitLab代码仓库

发布时间:2026/10/19 11:26:07 来源:尧图企业网站定制
1. 方案选型为什么大家都在用 Docker 跑 GitLab先说说大多数团队的真实经历。两年前我在某公司负责搭建内部代码仓库第一版用的是直接在 Linux 服务器上安装 GitLab。当时想得比较简单yum 装个依赖、下个 rpm 包、改改配置就完事了。结果实际操作下来光是依赖冲突就耗了一整天。GitLab 依赖的组件特别多PostgreSQL、Redis、Nginx、Sidekiq、Gitaly每一个版本都得跟主版本严格匹配。服务器上之前装过其它服务版本早就乱成一锅粥了装到最后我都分不清到底是 GitLab 在跑还是别的服务在被 GitLab 干扰。后来实在没辙换了台干净的机器从零开始装才跑起来。等到三个月后做版本升级噩梦又来了一遍——外部依赖的版本冲突、配置文件格式变化、数据库迁移失败那次升级我周六加了一整天班才搞定。所以后来我给朋友和同事的建议就一句话能用 Docker 跑的就别手动装。GitLab 官方对容器化部署的支持已经非常成熟了官方维护的镜像、官方文档里的 docker-compose 示例都是可以直接照着用的。Docker 把 GitLab 依赖的那一堆组件全部打包到镜像里宿主机只需要有一个 Docker 引擎就行其它什么都不用管。用 Docker 部署 GitLab 解决了最核心的三个问题环境隔离、版本管理和迁移便利。环境隔离意味着宿主机上装什么都不会影响到 GitLab 内部的 PostgreSQL 和 Redis版本管理意味着升级就是拉一个新镜像、重启容器那么简单迁移便利意味着整台服务器要换备份数据文件然后在新机器上起一个同版本的容器数据原样恢复。这篇文章面向两类读者一是公司里需要搭内部代码仓库的运维或开发二是想自己在服务器上搭一个私有 Git 仓库的个人开发者。我会按照我实际部署的流程来写包括参数怎么定、配置怎么改、坑在哪里看完可以直接照着做。2. 部署前的准备先把需求和参数盘清楚2.1 硬件配置怎么定很多人上来就问 Docker 怎么装、命令怎么敲但真正决定部署成败的往往是硬件评估。GitLab 是一个相对“重”的应用尤其是它自带的 Prometheus 监控和后台任务处理非常吃内存。我的经验值是这样的场景建议配置说明个人/小团队 10人2核4G勉强够用需要关掉一些非必要组件中型团队 50人4核8G比较舒服的配置默认组件全开无压力大型团队100人8核16G 起建议单独用物理机不要和别的应用混跑注意这里的配置指的是Docker 容器可用资源不是服务器总配置。如果服务器上还跑了别的容器或服务要额外留出资源。我见过一个真实案例一台 4G 内存的服务器上跑了 GitLab、Nginx 反代和一个定时任务脚本结果 GitLab 频繁 OOM每次都是半夜 Prometheus 采集指标的时候挂掉。后来把 GitLab 挪到单独的 8G 机器上问题立刻消失。2.2 容器端口映射规划GitLab 默认用 80 端口提供 Web 服务用 443 提供 HTTPS用 22 提供 SSH。但服务器上 80/22 端口往往已经被占用了比如 Nginx、别的 SSH 服务所以端口映射一定要提前规划。我常用的映射策略是这样# 宿主机端口:容器端口 8080:80 # Web 访问宿主机用 8080 避免冲突 8443:443 # HTTPS 访问 2222:22 # SSH 端口避免和宿主机 22 冲突如果你有独立的公网 IP 或者用云服务器可以直接用 80 和 443 端口体验更好。但如果端口被占千万别直接杀掉占用进程用映射到高位端口的方式最省事。2.3 数据目录规划必须挂载的三个目录GitLab 容器是“无状态”的——容器删了里面的数据就没了。所以必须把数据目录挂载到宿主机上。GitLab 镜像里需要挂载的目录主要有三个/etc/gitlab配置文件目录存gitlab.rb配置文件/var/log/gitlab日志目录排障时可以直接看宿主机上的日志文件/var/opt/gitlab核心数据目录包括 Git 仓库、数据库、Redis 数据、打包上传的产物等在宿主机上建目录时我习惯把 GitLab 的数据集中放在一个总目录下mkdir -p /data/gitlab/{config,logs,data}这个目录规划很重要。后续做数据备份、迁移、甚至磁盘扩容的时候发现所有东西都集中在一个挂载点下操作会非常方便。3. 实操部署docker-compose 是首选方式3.1 为什么推荐 docker-compose 而不是 docker run部署 Docker 应用有两种常见方式docker run一条命令直接启动或者写一个docker-compose.yml用 Compose 启动。对于 GitLab 这种需要配置大量环境变量、端口映射、数据卷挂载的应用我强烈推荐用 docker-compose。理由很实际docker run的命令往往长到绕地球一圈写错一个参数很难发现而且不方便版本管理。写成 Compose 文件后所有配置都清清楚楚地放在一个 YAML 文件里后续要改端口、加配置项只需修改文件后重新docker compose up -d就行。3.2 完整的 docker-compose.yml 配置先给出我实际使用、跑了一年多没出过大问题的配置version: 3.8 services: gitlab: image: gitlab/gitlab-ce:latest container_name: gitlab restart: always hostname: gitlab.example.com environment: GITLAB_OMNIBUS_CONFIG: | external_url http://gitlab.example.com:8080 nginx[listen_port] 80 nginx[redirect_http_to_https] false gitlab_rails[initial_root_password] YourStrongPassword123 gitlab_rails[gitlab_shell_ssh_port] 2222 gitlab_rails[time_zone] Asia/Shanghai unicorn[worker_processes] 4 prometheus_monitoring[enable] false ports: - 8080:80 - 8443:443 - 2222:22 volumes: - /data/gitlab/config:/etc/gitlab - /data/gitlab/logs:/var/log/gitlab - /data/gitlab/data:/var/opt/gitlab shm_size: 256m逐项解释一下关键配置external_url是 GitLab 对外访问的 URL 地址这个必须设置对。如果通过http://gitlab.example.com:8080访问那这里就填这个地址GitLab 内部的 Nginx 会根据这个 URL 生成所有页面上的链接。填错了会出现一个经典问题网页能打开但 CSS 样式全丢、点击仓库链接跳到错误地址。nginx[listen_port] 80是容器内部 Nginx 的监听端口。注意这里必须是 80因为容器内部 GitLab 的 Nginx 默认监听 80我们通过端口映射把宿主机的 8080 对应到容器的 80。如果在external_url里带了端口号GitLab 会自动调整但有时候还是需要显式声明避免歧义。这个配置是 GitLab 容器化部署的常见“坑”不设置的话访问时会有各种诡异的跳转问题。gitlab_rails[gitlab_shell_ssh_port] 2222是 SSH 克隆地址的端口号。因为我把宿主机的 2222 映射到容器的 22如果这个参数不设置GitLab 的仓库页面里显示的 SSH 克隆地址会是不带端口的gitgitlab.example.com:group/repo.git但实际 SSH 服务跑在 2222 端口上用户直接复制是连不上的。设置成 2222 后页面会正确显示ssh://gitgitlab.example.com:2222/group/repo.git。prometheus_monitoring[enable] false是我经过一段时间的观察后决定关掉的。GitLab 自带的 Prometheus 监控对小型部署来说是个资源大户实测能吃掉 1G 左右的内存。小团队用不到那么详细的监控面板直接关掉能省出不少资源。如果你的服务器内存充足16G 以上可以保留监控数据对排障有参考价值。shm_size: 256m是给容器分配共享内存。GitLab 内部有些组件特别是 Prometheus 和 PostgreSQL对 /dev/shm 的大小比较敏感。默认 64M 在某些场景下会导致奇怪的崩溃问题比如 GitLab 页面偶尔 500、服务重启后起不来。调到 256M 属于比较保险的配置。3.3 启动与验证配置写好之后执行docker compose up -dGitLab 第一次启动会比较慢因为容器内部要初始化数据库、编译 assets、生成密钥等。以 4 核 8G 的机器为例整个过程大约需要 3-5 分钟。可以通过以下命令观察启动日志docker logs -f gitlab看到日志中出现类似GitLab is ready或者All services are up的字样就说明启动完成了。我第一次部署时不知道这个特点看到日志刷了几分钟还以为是卡住了其实是在做初始化。启动完成后浏览器访问http://服务器IP:8080会跳到登录页。管理员账号默认是root密码是我们在配置里通过gitlab_rails[initial_root_password]设置的密码。如果没设置GitLab 会随机生成一个密码并放在/etc/gitlab/initial_root_password文件里24 小时后自动删除。所以还是建议在 Compose 配置里显式设置初始密码省得去容器里翻文件。3.4 关闭注册功能GitLab 默认是允许用户自助注册的。对于内部代码仓库来说这个功能基本用不上而且容易被外部垃圾账号利用如果服务器暴露在公网的话。部署完成后我通常会第一时间关掉它。进入 Admin 区域左下角管理菜单 → 设置 → 通用在“注册限制”里取消“已启用注册”的勾选。如果习惯用命令行也可以通过配置关闭gitlab_rails[gitlab_signup_enabled] false改完配置后执行docker exec gitlab gitlab-ctl reconfigure让配置生效。个人经验如果服务器有公网 IP强烈建议部署时直接把这个关掉否则一天能收到好几封新用户注册的邮件提醒很烦。4. 深入配置从 HTTPS 到备份策略4.1 启用 HTTPS 的两种途径内网部署一般用 HTTP 就够了但如果 GitLab 要暴露到公网或者公司安全规范要求传输加密就需要启用 HTTPS。这块有两条路我分别说下利弊。第一条路用 GitLab 自带的 Nginx 终结 HTTPS。把 SSL 证书放到服务器上在GITLAB_OMNIBUS_CONFIG里加配置external_url https://gitlab.example.com nginx[ssl_certificate] /etc/gitlab/ssl/gitlab.example.com.crt nginx[ssl_certificate_key] /etc/gitlab/ssl/gitlab.example.com.key nginx[ssl_ciphers] ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256注意证书路径是容器内部路径不是宿主机路径。我把证书放在宿主机/data/gitlab/config/ssl/目录下因为/data/gitlab/config已经挂载到容器的/etc/gitlab所以容器内路径就是/etc/gitlab/ssl/xxx.crt。权限有讲究证书私钥文件权限要统一改成640所属用户是 root否则 Nginx 启动的时候会报权限错误。第二条路用宿主机上的 Nginx 做反向代理。如果公司已有统一的 Nginx 网关可以把 GitLab 的external_url设为http://gitlab.example.com:8080然后在宿主机 Nginx 里配置 SSL 终结和反向代理到 8080 端口。这种方式的好处是证书统一管理、多服务共用一个入口坏处是多了一层代理排查问题的时候要分清楚是哪一层出的错。个人建议没有统一的网关环境就用第一条路简单直接有成熟网关或 Kubernetes 环境就选第二条路架构更统一。4.2 修改默认 SSH 端口后的克隆地址问题我刚才在 Compose 里把宿主机 2222 映射到了容器 22这就会带来一个 Git 克隆地址的变化。默认情况下GitLab 页面显示的 SSH 克隆地址是gitgitlab.example.com:group/repo.git。这种 address 用的是 SSH 协议的 scp-like 语法本质上省略了端口号。当 SSH 不在标准 22 端口时这个地址是连不上的必须改成ssh://gitgitlab.example.com:2222/group/repo.git配置了gitlab_rails[gitlab_shell_ssh_port] 2222后GitLab 会自动在页面上显示带端口的地址用户复制后直接能用。这个坑如果不提前处理团队里每个人第一次克隆代码都会失败然后来问你“为什么我连不上仓库”你还要解释一遍端口映射的原理非常浪费时间。4.3 定时备份容器部署一定要做数据备份容器部署最大的风险在于容器本身是随时可以删掉重建的数据安全完全依赖宿主机上的挂载目录。如果宿主机磁盘坏了或者误删了/data/gitlab目录所有代码仓库就全没了。所以备份是必须做的事。GitLab 自带备份工具命令是docker exec -t gitlab gitlab-backup create这条命令会把数据库、Git 仓库、上传文件等打包成一个 tar 文件存放在容器内部的/var/opt/gitlab/backups目录对应宿主机就是/data/gitlab/data/backups。备份文件默认命名格式是1700000000_2023_11_15_16.9.1_gitlab_backup.tar包含时间戳和版本号。注意版本号很重要恢复备份时要求 GitLab 版本和备份时的版本一致或高于备份版本所以升级前一定要先做个备份。我用 cron 做每天凌晨的自动备份0 2 * * * docker exec -t gitlab gitlab-backup create为了避免备份文件无限增长我还写了个简单的清理逻辑保留最近 7 天的备份find /data/gitlab/data/backups -name *.tar -mtime 7 -delete另外要特别提醒备份只包含 GitLab 自身的数据不包含备份文件本身的异地同步。最稳妥的做法是把备份目录再通过 rsync 同步到另一台机器或者放到对象存储上。我见过某团队服务器硬件故障备份文件和数据库在同一块磁盘上结果一起丢了教训非常深刻。4.4 配置文件备份不少朋友做备份只跑gitlab-backup create其实漏了配置文件的备份。gitlab.rb里有你所有的定制配置比如初始密码、HTTPS 证书路径、SSH 端口、外部 URL 等。如果这台机器彻底挂了恢复数据到新机器后配置文件不在了所有定制都没了还得重新想当时都改了什么。我的做法是把/data/gitlab/config整个目录一起同步走因为打包备份不包含它tar -czf /backup/gitlab-config-$(date %F).tar.gz /data/gitlab/config恢复的时候先恢复配置再恢复数据最后起容器基本能回到出事前的状态。5. 升级 GitLab 版本容器化让升级变得简单5.1 升级策略和注意事项GitLab 官方对升级路径是有严格要求的跨大版本直接跳版本升级容易出问题比如 14 升 16官方建议一级一级往上升。不过用 Docker 的话过程相对纯净。我总结的升级步骤是这样的备份数据docker exec -t gitlab gitlab-backup create备份配置目录tar -czf打包/data/gitlab/config拉取新版本镜像docker compose pull gitlab用新镜像重启容器docker compose up -d等待启动完成后访问页面确认服务正常看起来是四步但实际操作中有一个容易忽略的问题GitLab 容器启动时会自动执行数据库迁移这个迁移过程跟数据量有关。数据量大的情况下迁移可能要跑十几分钟甚至更久期间 GitLab 的 Web 服务会显示 502 或者直接不可用。这是正常现象不要以为是卡住了就急着去重启容器——迁移中途强制重启可能导致数据库不一致这是个高危操作。怎么样判断迁移是否完成看日志docker logs -f gitlab看到类似database migrations are complete或Services are running的日志再等一两分钟然后浏览器访问确认。5.2 大版本跳跃的坑虽然 Docker 部署让升级变得容易但不代表可以无视版本路径。我亲身踩过一个坑某次从一个很旧的版本直接升到新版本数据库迁移直接报错界面 500。后来查文档才知道跨多个大版本升级需要先升级到中间版本把数据迁移做完再继续升。所以稳妥的升级策略是参考官方升级路径文档找出从当前版本到目标版本需要经过的中间版本逐个版本执行升级。比如当前是 14.x目标是 16.x那可能需要先升到 15.x 的一个指定版本完成升级后再升到 16.x。用 Docker 做多级升级不算麻烦就是重复执行“改镜像版本号 → 启动 → 等迁移完成”这个循环。麻烦的只是要付出多一些等待时间但稳定第一。5.3 升级后的验证清单升级完成后我习惯按下面的清单逐一验证缺一不可Web 页面能正常打开登录 root 账号项目列表、仓库页面、Merge Request 页面能正常访问拉一个测试仓库验证 Git clone 和 push 正常跑一次 CI 流水线如果配了 Runner看是否正常检查系统管理区的背景任务确认没有异常报错如果哪一步出问题第一时间把备份的配置和数据恢复回去。这也是为什么我反复强调备份的重要性——没有备份就没有后悔药。6. 常见问题排查这些坑我基本都踩过6.1 页面 502 错误Docker 部署 GitLab 时最常见的问题就是刚启动完访问页面出现 502。原因通常有两个容器还在初始化Web 服务还没起来。这时候日志里会不断刷新启动过程耐心等即可。内存不足。GitLab 所有内部服务全部启动需要的内存大约 2-3G如果服务器内存不够某些服务比如 Puma 或 PostgreSQL会不停重启导致页面 502。用docker stats gitlab看一下容器内存占用如果接近上限考虑加内存或者关掉非必要组件。我先观察到容器日志里有puma反复启动失败的信息用docker stats看到内存基本吃满把 Prometheus 关掉后立竿见影。6.2 SSH 克隆报错克隆时如果提示Permission denied (publickey)或Connection refused先分清楚是网络不通还是密钥问题。我建议按下面几步排查确认端口是否通telnet gitlab.example.com 2222确认 SSH 密钥是否添加到 GitLab 账号里用户设置 → SSH 密钥确认克隆地址的端口是不是 2222很多人直接复制默认地址导致连的 22 端口检查容器日志docker logs gitlab | grep sshd还有一个很隐蔽的问题是宿主机如果开了 firewalld 或者云安全组即使 Docker 做了端口映射外网也访问不到。映射是在 Docker 层面做的云服务器的安全组规则要单独放行端口这是新手最容易忽略的。6.3 GitLab 访问慢部署路径对了、功能都正常但页面加载起来慢吞吞。这里有两种常见原因机器规格不够4G 内存下 GitLab 的页面响应确实会有明显延迟。关了 Prometheus 之后会好一些。Git 操作走 HTTPS 但证书校验拖慢速度内网部署往往用的是自签证书每次 Git 操作 Git 客户端都要做证书验证体验很受影响。内网环境建议直接走 HTTP 或者配置 Git 客户端忽略证书校验。6.4 忘记 root 密码这个问题出现频率出乎意料地高。解决办法分两种有配置访问权的情况修改 Compose 里的GITLAB_OMNIBUS_CONFIG加一行gitlab_rails[initial_root_password] NewPassword123然后执行docker compose up -d --force-recreate注意这里要加--force-recreate强制重建容器否则 GitLab 检测到配置变化不一定会触发重新初始化。有容器访问权的情况不需要改配置直接进入 Rails 控制台改密码docker exec -it gitlab gitlab-rails console等到控制台加载完成后输入user User.find_by(username: root) user.password NewPassword123 user.password_confirmation NewPassword123 user.save!输出true就表明密码已更新。退出控制台后用新密码登录即可。6.5 容器时间不对GitLab 页面显示的操作时间或提交时间跟本地时间不一致。这个一般是宿主机时区问题。在 Compose 环境变量里加上TZ: Asia/Shanghai或者启动时映射宿主机的/etc/localtime到容器内。不过我个人更推荐用GITLAB_OMNIBUS_CONFIG里的gitlab_rails[time_zone] Asia/Shanghai来设置这个参数会同步影响 GitLab 内部多个组件的时区设置。如果只是设置容器环境的TZGitLab 内部的 PostgreSQL 可能还是 UTC 时区日志和 Web 界面显示的时区会不一致容易让团队成员困惑。7. GitLab Runner 集成补齐 CI/CD 能力部署完 GitLab 之后下一步大多数团队会做的是接入 Runner 来跑 CI/CD。这块也踩过一些坑这里一并讲讲。GitLab Runner 最简单的部署方式也是 Dockerdocker run -d --name gitlab-runner --restart always \ -v /data/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest然后把 Runner 注册到 GitLab 实例上在项目设置 → CI/CD → Runner 里能看到注册地址和 Tokendocker exec -it gitlab-runner gitlab-runner register \ --url http://gitlab.example.com:8080 \ --token 你的注册Token \ --executor docker \ --docker-image docker:latest \ --docker-volumes /var/run/docker.sock:/var/run/docker.sock有几个容易踩的坑Runner 执行器里跑 docker 命令会失败因为容器里的作业环境没有 Docker 守护进程。解决办法是把宿主机的/var/run/docker.sock挂载进 Runner 容器并在 Runner 配置里让作业容器也能挂载这个 socket上面命令里的--docker-volumes就是干这个的。这样做有安全风险拥有 Docker socket 等于拥有宿主机 root但内网环境里这属于普遍做法风险可控。Runner 无法访问 GitLab如果 Runner 跟 GitLab 不在同一台机器上注册时填的 URL 必须是 Runner 能访问到的地址不能填localhost或127.0.0.1。我见过有人用http://localhost:8080注册 Runner结果 Runner 装在不同机器上永远注册不上卡了很久才反应过来。CI job 执行特别慢默认 Docker executor 用的是docker:latest镜像每次跑 job 都要拉一遍镜像。如果网络不好光拉镜像就用掉几分钟。我的处理方式是预先在宿主机上 pull 常用镜像然后在 Runner 注册时指定--docker-pull-policy if-not-present能省下大量时间。Runner 类型的选择跟团队规模有关团队小、并发低用 Docker executor 就够了如果单项目并发高或者有频繁的构建任务可以考虑 Shell executor 或者单独部署 Kubernetes Runner。但那是另一个话题了这篇文章按下不表。8. 个人经验总结几点值得注意的细节部署 GitLab 这件事本身不难难的是把细节处理好、确保长期稳定运行。最后分享几个我实际使用中的体会。第一把配置的修改全部收敛到 Compose 文件里。不要跑进容器去手动改gitlab.rb因为容器一重建容器内的修改全没了。我之前就是这样在容器里改了配置忘了同步到宿主机结果一次重启后配置丢失排查了很久才想明白。现在所有变更都先改docker-compose.yml再docker compose up -d保持配置可追溯、可审计。第二升级 GitLab 前务必验证备份可恢复。很多人做了备份但从来没验证过恢复流程。某次我们做容灾演练尝试用备份文件恢复时才发现备份是不完整的部分仓库数据缺失要不是演练发现得早真到灾难发生那天就麻烦了。现在的习惯是每月做一次完整的备份恢复演练虽然耗时但求个心安。第三日志别关但也不用整天盯。GitLab 的日志文件会持续增长建议在宿主机上加一层 logrotate 或者定期清理。但至少保留 30 天的日志排障的时候看日志比猜原因靠谱得多。我目前部署的这套 GitLab 已经稳定跑了超过一年中间经历了两次大版本升级、一次整机迁移都没有出过问题。说到底Docker 部署 GitLab 的本质就是把复杂的环境依赖问题简化成“一个容器 三个数据卷”把这套逻辑理解清楚了后面不管是升级、迁移还是故障恢复心里都会有底。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑