为什么要写这篇文章?

曾经几年前在 Docker 还没有广泛应用的时候,在公司使用过源码的方式安装和升级过 Gitlab,虽远没有 Docker 方便,因为自己对 Linux 系统的理解,所以整体上感觉还是挺简单的。这几年随着 Docker 的普及,使得安装 Gitlab 更加的容易,不仅方便了我这样的老鸟,也更方便了小白用户们。但是 Gitlab 官方的 Docker 安装文档并没有写得很完善, 除了官方文档之外,检索出来的安装文档也是人云亦云,东拼西凑,结果也就是能运行起来,凑合着能用而已。避免出现下图中“我已经用 Docker 部署好啦”,其实已经翻车的情形。

docker-deployment

我希望每做一件小事的时候也都能抱着“知其然知其所以然”的心态对待,用 Docker 方式安装 Gitlab,说简单来说就是一行命令的事儿,但是这样就够了吗?我看是不够的,所以就有了这篇文档。

本文需要达成的事项

  • 在 CentOS 7 系统中安装 Docker
  • 使用 Docker 方式安装中文版 Gitlab
  • 和宿主机器共用 22(SSH) 端口
  • 支持 SSH(22)/HTTPS(443) 方式推拉仓库
  • 使用 SMTP 方式配置通知邮箱(腾讯企业邮箱)
  • 改写默认的项目标签(Labels)

在 CentOS 7 系统中安装 Docker

这部分参考 Docker 的官方文档, 罗列一下安装步骤, 细节请看 Docker 的官方文档。如果使用 root 用户安装,sudo 可以去掉。

1. 删除老版本 Docker

$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine

2. 安装 Docker CE 的仓库配置

$ sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

3. 安装仓库中最高版本 Docker CE

$ sudo yum install -y docker-ce

4. 启动 Docker

$ sudo systemctl start docker

5. 验证 Docker 是否安装成功

$ sudo docker run hello-world

使用 Docker 方式安装中文版 Gitlab

目前我的团队习惯使用中文版的 Gitlab 的,并且使用的版本是 beginor/gitlab-ce:10.3.1-ce.0,所以还是以这个版本来说明安装配置过程。

在启动 Gitlab 之前,创建几个目录作为 Docker 的卷,这样的配置或者升级 gitlab 的时候可以保留配置和数据。

$ sudo mkdir -p /data/var/lib/gitlab/etc
$ sudo mkdir -p /data/var/lib/gitlab/log
$ sudo mkdir -p /data/var/lib/gitlab/data

启动 Gitlab

$ sudo docker run \
    --detach \
    --sysctl net.core.somaxconn=1024 \
    --publish 8080:80 \
    --publish 8022:22 \
    --name gitlab \
    --restart unless-stopped \
    --volume /data/var/lib/gitlab/etc:/etc/gitlab \
    --volume /data/var/lib/gitlab/log:/var/log/gitlab \
    --volume /data/var/lib/gitlab/data:/var/opt/gitlab \
    beginor/gitlab-ce:10.3.1-ce.0

这个 Gitlab 的 Docker 镜像是基于 Ubuntu 16.04.3 LTS 这个版本来构建的,所以在 docker exec -it gitlab /bin/bash 进入 Docker 容器之后跟使用 Ubuntu 就没有什么差别了。

gitlab 容器中启动了很多服务,主要包括:

  • gitlab
  • redis
  • postgresql
  • nginx
  • sshd

通过查看 Dockerfile 发现,除了可以使用 docker exec -it gitlab /bin/bash 进入容器之外,还可以直接使用 SSH 登录到容器中。其实 Gitlab 这个镜像,并不符合 Dockerfile 最佳实践规范, 因为将会有太多的服务在这个镜像构建的容器之中,不利于服务的扩容和重用。不过通过这种超级包的方式确实大大降低了用户的使用门槛,对于高段位选手,自然也会自己去拆分去解耦,进而去构建自己的镜像。

讲到这里,gitlab 服务已经在运行了,大多数人也认为自己任务完成了,一切到此为止。但是对于有些许强迫症的人来说,是无法接受 HTTP 得用 8080 端口, SSH 得用 8022 端口,这样 Git 的 URL 就不太美观了。

偷懒的人有个很简单的方法来解决这个端口的问题, 可以使用这样的命令启动 Docker

$ sudo docker stop gitlab
$ sudo docker rm gitlab
$ sudo docker run \
    --detach \
    --sysctl net.core.somaxconn=1024 \
    --publish 443:443 \
    --publish 80:80 \
    --publish 22:22 \
    --name gitlab \
    --restart unless-stopped \
    --volume /data/var/lib/gitlab/etc:/etc/gitlab \
    --volume /data/var/lib/gitlab/log:/var/log/gitlab \
    --volume /data/var/lib/gitlab/data:/var/opt/gitlab \
    beginor/gitlab-ce:10.3.1-ce.0

这样有带来了新问题:

  1. 80/443 将会被 gitlab 独占,宿主机器上 Nginx 等 HTTP/HTTPS 服务将无法使用 80/443
  2. 22 将会被 gitlab 独占,那么宿主机器上的 SSHD 服务需要改为其它端口

这两个新问题大概对于有些许强迫症的人来说也是无法接受的。 我们还是回到这样的方式启动 Gitlab

$ sudo docker stop gitlab
$ sudo docker rm gitlab
$ sudo docker run \
    --detach \
    --sysctl net.core.somaxconn=1024 \
    --publish 8080:80 \
    --publish 8022:22 \
    --name gitlab \
    --restart unless-stopped \
    --volume /data/var/lib/gitlab/etc:/etc/gitlab \
    --volume /data/var/lib/gitlab/log:/var/log/gitlab \
    --volume /data/var/lib/gitlab/data:/var/opt/gitlab \
    beginor/gitlab-ce:10.3.1-ce.0

然后寻找其它更好的解决方案:

  1. 使用 Nginx 代理 8080 端口,这样很容易实现 HTTP(80)/HTTPS(443) 端口共用
  2. 共享宿主机器的 SSH(22) 端口,如果使用 git 这个账号登录,则转发 SSH 到 gitlab 的容器中

下面来讲如何解决这两个问题。

使用宿主机器上的 Nginx 配置 HTTP 和 HTTPS

使用宿主机器上的 Nginx 使得我们安装 Gitlab 更加灵活。先停用 Gitlab 容器中的 HTTPS 服务, 需要这样改写配置文件, 需要编辑 /data/var/lib/gitlab/etc/gitlab.rb 文件相应的行,具体配置可以参考这里

external_url "https://gitlab.example.com"
nginx['listen_port'] = 80
nginx['listen_https'] = false

修改完成之后,可以在 Docker 容器中执行 gitlab-ctl reconfigure 来使之生效。这样配置以后,容器中讲只提供 HTTP 服务,不会根据 external_url 解析来自动启动 HTTPS 而导致日志中出现大量的缺少证书的日志。现在只需要配置宿主机器的 Nginx 就可以了。关于如何获取免费的 SSL 证书,这里就不在赘述了,读者可以自行搜索 Let’s Encrypt + Nginx 相关文章,如果没有域名,只有 IP,那可以试试 TrustOcean

Gitlab Docker 和宿主服务器共享 SSH(22) 端口

Gitlab Docker 镜像中默认使用的 SSH 账户是 git,那能不能在宿主机器上也建一个账户 git,只是当 git 帐号进行操作的时候,我们转发命令到 gitlab 容器呢?答案是肯定的,宿主机器上非 git 帐号就不受影响了,还可以正常使用。

我们在宿主机器上创建 git 账户,并且使他的 uid 和 gid 和容器中的值完全一样,Gitlab 容器中 uid 和 gid 都是 998, 修改宿主机器中的值,将便于未来容器的升级。

adduser git
id git
uid=998(git) gid=998(git) groups=998(git)

要完成共享 22 端口,要求 gitlab 容器中的 git 账户的 uid 和 gid 和宿主机器上完全相同,这样读取 SSH Key 时就不会有权限问题。关于如何修改两个系统中 git 账户的 uid 和 gid,可以通过命令 usermodgroupmod 手动修改 git 用户的 uid 和 gid参考文档。或者通过编辑 /data/var/lib/gitlab/etc/gitlab.rb 这个配置文件,修改其中的行,来使用其他的用户具体请参考这里:

user['username'] = 'other-git'
user['group'] = 'other-git'
user['uid'] = 1001
user['gid'] = 1001

修改完成之后,可以在 Docker 容器中执行 gitlab-ctl reconfigure 来使之生效.

在宿主机器上切换到 git 账户:su - git 然后执行 ln -s /data/var/lib/gitlab/data/.ssh .ssh 与 gitlab 容器共享 .ssh 下面的内容。为了在宿主机器上可以使用 git 账户无密码登录到 gitlab 容器中,需要创建在 .ssh 目录中添加一组密钥,并且把公钥加到 .ssh/authorized_keys 文件中去。

在宿主机器上执行:

ssh-keygen -t rsa -P ''
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

添加 no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty~/.ssh/authorized_keys 所在的行首,结果看起来是这样的:

no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAA......CzGuj git@cn-bj-aliyun-3

登录测试一下

ssh -p 8022 127.0.0.1

观察到 ~/.ssh/authorized_keys 其它行都是这样

command="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell key-32",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAA......hOtpAl7J

在宿主机看来,每次 git 账户进行操作是都会执行 /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell, 我们恰好可以使用这个脚本来实现转发。

mkdir -p /opt/gitlab/embedded/service/gitlab-shell/bin/
touch gitlab-shell
chmod +x gitlab-shell

并且输入以下内容到 /opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell

#!/bin/sh

ssh -p 8022 -o StrictHostKeyChecking=no [email protected] "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"

一切就绪了, 可以使用 https://gitlab.example.com/repo.git 或者 [email protected]:repo.git 这样的 URL 了。

使用 SMTP 方式配置通知邮箱(腾讯企业邮箱)

Gitlab SMTP 文档参考这里

gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.exmail.qq.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "[email protected]"
gitlab_rails['smtp_password'] = "password"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = '[email protected]'
gitlab_rails['smtp_domain'] = "exmail.qq.com"

改写默认的项目标签(Labels)

添加丰富的标准,方便进行项目管理。修改 issues_labels.rb 文件

labels = [
{"title": "优先级:P0(紧急)", "color": "#E99695", "description": "立即处理"},
{"title": "优先级:P1(高)", "color": "#E99695", "description": "优先处理"},
{"title": "优先级:P2(中)", "color": "#E99695", "description": "有时间再处理"},
{"title": "优先级:P3(低)", "color": "#E99695", "description": "暂不处理"},
{"title": "分类:BUG", "color": "#D4C5F9", "description": "发现的BUG"},
{"title": "分类:功能增强", "color": "#D4C5F9", "description": "增强已有的功能,属于优化的环节"},
{"title": "分类:功能完善", "color": "#D4C5F9", "description": "完善功能"},
{"title": "分类:文档修改", "color": "#D4C5F9", "description": "只是做文档修改"},
{"title": "分类:新功能", "color": "#D4C5F9", "description": "新的功能和需求"},
{"title": "项目:已上线", "color": "#C5DEF5", "description": "已发布上线"},
{"title": "项目:已排期", "color": "#C5DEF5", "description": "已经安排了开发时间milestone"},
{"title": "项目:已确认", "color": "#C5DEF5", "description": "功能已经确认,后续进行排期"},
{"title": "项目:延后", "color": "#C5DEF5", "description": "功能无法确定是否开发,延期处理"},
{"title": "项目:开发中", "color": "#C5DEF5", "description": "功能正在开发"},
{"title": "项目:待讨论", "color": "#C5DEF5", "description": "需求已经提出,但是需要讨论是否需要开发"},
{"title": "项目:测试中", "color": "#C5DEF5", "description": "功能已经完成开发,正在测试"}
]

总结

使用 Docker 方式安装 Gitlab 部署过程很简单,但是想达到一个理想的配置状况还是挺繁琐的,Docker 并不是治疗百病的良药,打铁还得自身硬。