使用 Docker 部署 GitLab
文档用途
此文档用于说明什么情况下适合将 GitLab 部署在局域网, 以及具体的技术方案.
另外可以参考笔者的配置文件工程 whisperpine/gitlab-compose.
需求分析
网络需求
- 当游戏工程中的美术音频资源体积变大后,
git push或git pull时所花费时间太长,
因为受到网速限制 (尤其是上行流量, 国内哪怕是千兆网络, 其上行带宽一般只有 5 MB/s). - Git 版本管理机制对非文本文件不友好, 任何修改都会创建一份新的数据 (而不是仅记录变动).
启用 Git LFS 也无法避免多份数据的存在, 这会让网络带宽和存储空间负担更大. - CI/CD 每个 Job 执行前, GitLab Runner 所在设备都需要克隆远程仓库,
而构建好的客户端版本和热更新资源等都需要上传并缓存 GitLab Server 上.
因此 CI/CD 的存在, 会消耗更多网络流量, 且网络带宽会影响 CI/CD 的执行速度.
局域网内速度很快, 千兆交换机的理论速度上限是 125MB/s, 实测大约为 110MB/s.
如果在云服务器上部署, 上行流量就被办公室的网络上行带宽限制住了 (大约 5 MB/s).
若直接采用 GitHub 或 GitLab SaaS, 也存在云服务部署方案类似的网速问题 (甚至更差).
如果想将部署在办公室局域网的服务暴露至公网, 可以考虑借助动态 DNS.
然而在这片文档中选用的方案是 Cloudflare Tunnel, 免费且安全.
公网访问体验会下降, 毕竟会受到办公室网络上行带宽的限制.
成本分析
云服务器有订阅费用, 以及下行流量成本 (例如阿里云国内服务器为 0.8 元/GB).
GitLab 这种服务需要始终在线, 即便是空转情况下依然需要 2vCPU-4GB 规格.
如果对弹性和高可用没有需求, 那么云服务在成本方面是不划算的.
在局域网内部署, 就完全没有流量成本, 不过肯定需要购置设备来部署服务.
如果需要在办公室局域网内部署多个服务, 那么这个成本可以均摊.
备份文件需要存储在云端, 但成本极低 (AWS S3 Glacier Deep Archive 每月 $0.99 / TB).
GitHub 或 GitLab SaaS 而言, 如果免费版不满足需求, 那么成本比其他方案都高.
考虑到 GitHub 对于开源项目非常友好 (闭源项目的付费功能, 开源项目可以免费使用),
可以仅将开源项目托管在 GitHub 上, 闭源项目采用别的方案来托管.
方案选择
针对存在大量非文本文件的工程, 建议采用在局域网部署 GitLab 的方案.
缺陷: 需要周期性备份, 在办公室局域网外使用体验下降.
优势: 网速快, 成本低.
配置 GitLab
环境变量
GitLab 的配置文件路径是 /etc/gitlab/gitlab.rb, 下文简称 gitlab.rb.
使用 Docker 部署 GitLab 时, 不建议 直接修改此文件, 建议通过环境变量 GITLAB_OMNIBUS_CONFIG 来设置, gitlab.rb 的字段和注释可以作为参考.
services:
gitlab:
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.example.com'
# 在这里添加更多设置, 用于覆盖 /etc/gitlab/gitlab.rbTLS 证书
GitLab 集成了 TLS 证书的自动申请与更新的功能, 默认使用 Let's Encrypt 颁发的证书.
只要在 gitlab.rb 中的 external_url 字段赋值时, 采用 https 开头的 url 即可.
external_url 'https://gitlab.example.com'为了能够成功申请和更新 TLS 证书, 必须要确保 GitLab Server 的 80 和 443 端口可以被访问.
若采用 Docker 默认的网络驱动类型 (bridge) 会出问题, 因为不大可能把宿主系统的 80 端口映射到容器的 80 端口, 同样 443 端口也不会直接映射. 对此问题的解决方案详见下文 网络驱动.
另外在 tls 证书的签发过程中, 需要确保: 公网环境下, 能够通过域名访问到 GitLab.
因此需要提前配置好 Cloudflare Tunnels, 详见下文 公网访问.
镜像注册
GitLab 提供了镜像注册服务 (Container Registry), 默认情况下通过 5050 端口开放.
不过我们肯定希望避免在 URL 中出现端口号, 因此可以配置镜像注册服务的专用域名.
registry_external_url 'https://registry.example.com'类似于 GitLab 主域名, 只要在 URL 中包含了 https, 就会自动申请 tls 证书.
同样也需要正确配置 Cloudflare Tunnels 才能使得证书成功签发.
邮件设置
邮件在 GitLab 中的主要适用场景是: 发送邮件通知, 以及账户的注册.
为此需要先在云服务上那里配置好邮件服务, 然后将相关信息配置在 gitlab.rb 中.
建议把这些配置写在 compose.yaml 的环境变量 GITLAB_OMNIBUS_CONFIG 下.
### 邮件相关
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = 'smtpdm.aliyun.com' # 记得修改
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = 'noreply@email.exmaple.com' # 记得修改
gitlab_rails['smtp_password'] = 'xxx' # 记得修改
gitlab_rails['smtp_domain'] = 'email.example.com' # 记得修改
gitlab_rails['smtp_authentication'] = 'login'
gitlab_rails['smtp_enable_starttls_auto'] = false
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = 'noreply@email.exmaple.com' # 记得修改
gitlab_rails['gitlab_email_display_name'] = 'GitLab'
gitlab_rails['gitlab_email_reply_to'] = 'support@exmaple.com' # 记得修改降低内存
能明显降低内存占用的设置如下:
##! 关闭 prometheus 以降低内存占用
prometheus_monitoring['enable'] = false
##! **recommend value is 1/4 of total RAM, up to 14GB.**
postgresql['shared_buffers'] = "256MB"网络设置
网络驱动
我们不能使用默认的 Docker 网络驱动类型 (bridge).
除了因为上文提到的 TLS 证书 申请问题, 还涉及到 SSH 端口问题.
由于宿主系统的 22 端口 (SSH 协议默认端口) 肯定不能拿来给容器使用, 因此 bridge 网络驱动下的容器的 22 端口就只能映射到宿主系统的其他端口, 这将会导致我们无法通过 SSH 协议的默认端口来操作 GitLab 远程仓库, 也就是说 git clone [url] 中的 url 将会多一个端口号.
这显然也是不符合预期的, 我们应该希望能使用默认端口号.
为此, 我们需要用到 ipvlan 或 macvlan , 它能给 GitLab 容器提供与宿主设备相同子网内 IP.
以下两种方式都可以创建 macvlan 类型的网络驱动, 分别通过配置文件和命令行:
networks:
gitlab-macvlan:
driver: macvlan
driver_opts:
parent: eth0
ipam:
config:
- subnet: "192.168.3.0/24" # 宿主系统所在的子网
gateway: "192.168.3.1" # 路由器在子网内的 ipdocker network create -d macvlan \
--subnet 192.168.3.0/24 \
--gateway 192.168.3.1 \
-o parent=eth0 \
gitlab-macvlan在使用创建好的 macvlan 类型的网络驱动时, 需要手动指定一个空闲的 IP.
services:
gitlab:
image: gitlab/gitlab-ee:latest
networks:
gitlab-macvlan:
ipv4_address: "192.168.3.66"DNS 设置
在上文中, 我们选择将 192.168.3.66 作为 GitLab 在局域网内的 IP.
我们肯定希望能局域网内能通过 域名 访问 (而非通过 IP 访问), 此时应配置 DNS.
首先需要在局域网内搭建 DNS Server (这里掠过步骤),
然后将路由器的授权服务器设置为 DNS Server 的 IP, 如下图所示.
如果在个人电脑的操作系统中不指定 DNS, 那么就会使用路由器所分派的 DNS.

最后在局域网内搭建好的 DNS 服务器中, 添加记录.
WARNING
如果直接通过域名注册商的公网 DNS 解析也能生效, 但是 不建议 这么做.
公网访问
借助 Cloudflare Tunnel, 我们可以将局域网内的服务暴露至公网, 免费且安全.
缺陷是: 如果不启用网络代理, 访问速度慢 (大概相当于不开网络代理访问 GitHub).
因为 Cloudflare 在国内没有边缘节点.
首先在 Cloudflare Tunnel 网页控制台创建 tunnel, 并获取 token.
然后在 compose.yaml 中输入刚刚得到的 token:
services:
cloudflared:
image: cloudflare/cloudflared:latest
restart: always
networks:
cloudflared-gitlab:
# 将 xxx 替换成真实的 token
command: tunnel --no-autoupdate run --token xxx
gitlab: # 此 service 名称在后面的 tunnel 配置中要用到
image: gitlab/gitlab-ee:latest
restart: always
networks:
cloudflared-gitlab:
gitlab-macvlan:
ipv4_address: "xxx"
networks:
cloudflared-gitlab:
gitlab-macvlan:
driver: macvlan由于在 gitlab.rb 配置中启用了 https, 因此 cloudflared 与 gitlab 相互之间也应当使用 https 来通信 (否则会陷入 "转发死循环"). 所以在下图中, Service Type 一定要选择 HTTPS.
另外可以注意到 URL 那一栏填写的是 gitlab, 这与前面示例中的 service 名称一致.

另外在 Additional Application Settings / TLS 中,
还需要勾选 No TLS Verify 选项 (否则会遇到 502 Bad Gateway 错误).

TIP
镜像注册服务 的域名配置方式一样, 且与主域名指向相同的 Service (图中的 https://gitlab).
GitLab 容器内部的 Nginx 会在反向代理的过程中处理好端口问题.
备份与恢复
详见 GitLab 的备份与恢复 文档.
完整示例
展开查看完整的 compose.yaml 示例
services:
cloudflared:
image: cloudflare/cloudflared:latest
restart: always
networks:
cloudflared-gitlab:
command: tunnel --no-autoupdate run --token xxx # 记得设置 token
gitlab:
image: gitlab/gitlab-ee:latest
hostname: gitlab.example.com
restart: always
shm_size: 256m
volumes:
- /etc/localtime:/etc/localtime:ro
- gitlab-config:/etc/gitlab
- gitlab-logs:/var/log/gitlab
- gitlab-data:/var/opt/gitlab
networks:
gitlab-macvlan:
# 找个网段内空闲的 IP 来设置
ipv4_address: "192.168.3.66"
environment:
# 通过此环境变量覆盖 gitlab.rb 中的配置
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.example.com'
##! 关闭 prometheus 以降低内存占用
prometheus_monitoring['enable'] = false
### 邮件相关
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = 'smtpdm.aliyun.com' # 记得修改
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = 'noreply@email.exmaple.com' # 记得修改
gitlab_rails['smtp_password'] = 'xxx' # 记得修改
gitlab_rails['smtp_domain'] = 'email.example.com' # 记得修改
gitlab_rails['smtp_authentication'] = 'login'
gitlab_rails['smtp_enable_starttls_auto'] = false
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = 'noreply@email.exmaple.com' # 记得修改
gitlab_rails['gitlab_email_display_name'] = 'GitLab'
gitlab_rails['gitlab_email_reply_to'] = 'support@exmaple.com' # 记得修改
### 备份相关
## The duration in seconds to keep backups.
## 604800 is meant to be 30 days.
gitlab_rails['backup_keep_time'] = 2592000
## Consider using multipart uploads when file size reaches 100MB.
## Enter a number in bytes.
gitlab_rails['backup_multipart_chunk_size'] = 104857600
## Skip parts of the backup. Comma separated.
gitlab_rails['env'] = {
# 这里列出的类型, 将不会被备份
'SKIP' => 'builds,artifacts,registry,pages'
}
## 设置 bucket 名称
gitlab_rails['backup_upload_remote_directory'] = 'xxx' # 记得修改
## 设置 bucket 的其他元数据
gitlab_rails['backup_upload_connection'] = {
'provider' => 'AWS',
'region' => 'xxx', # 记得修改
'aws_access_key_id' => 'xxx', # 记得修改
'aws_secret_access_key' => 'xxx' # 记得修改
}
networks:
cloudflared-gitlab:
gitlab-macvlan:
driver: macvlan
driver_opts:
parent: eth0
ipam:
config:
- subnet: "192.168.3.0/24"
gateway: "192.168.3.1"
volumes:
gitlab-data:
gitlab-logs:
gitlab-config: