GitLab + Container Registry 安装及反向代理配置

本文介绍了我在局域网内 ubuntu 虚拟机上安装 GitLab ,并配置外网 https+ssh 访问的过程。主要使用公网机器的 nginx 配置 letsencrypt 的 ssl 证书,并且反向代理到 frp 的 http 端口;通过 frp 进行内网穿透,反向代理到内网虚拟机上运行的 GitLab。同样的方式启用了 GitLab 自带的 Container Registry。

思路

因为 GitLab 内存占用非常高,官方推荐内存 4G 缓存 4G 起步,我的个人实验小鸡内存小跑不起来,一直报 502 错误,所以我只好安装到家里的个人机器上,利用 frp 做内网穿透;通过公网主机的 nginx 处理 https 协议,同时将请求反向代理到 frp。安装过程比较麻烦,不过最后的效果不错,速度很理想。

GitLab 安装

CE or EE?

CE(Community Edition)是社区版,开源,MIT授权, EE(Enterprise Edition)是企业版。EE 版核心和 CE 版一样,另外包含一些收费的功能,没提供授权的话,可以和 CE 版一样运行。
后续可以从 CE 版升级到 EE 版。版本说明见:Community Edition or Enterprise Edition
我选择了 CE 版,给钱是不可能给钱的。

准备

  • 一外网主机,运行 frps、nginx
  • 一本地虚拟机, ubuntu 18.04 系统,内存4G(另配置了 4G 交换分区(swap))、虚拟磁盘 20G 固态,运行 frpc

安装

采用软件包的方式安装,照着官方文档就可以了: Omnibus package installation
默认是 EE 版的安装说明,CE 版需要在链接上加上 ?version=ce。CE 版主要安装步骤如下:

sudo apt-get install -y curl openssh-server ca-certificates
sudo apt-get install -y postfix  # 用来发邮件,由于采用 smtp 发信,这步跳过
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo EXTERNAL_URL="http://gitlab.example.com" apt-get install gitlab-ce

其中 EXTERNAL_URL 是外部访问地址,配置成自己的 GitLab 外网地址,安装完成后先测试下能否正常运行,后续会改成 https 的。可以改本地电脑的 hosts ,将 EXTERNAL_URL 里的域名临时解析到虚拟机的 ip 地址,然后在浏览器里访问 EXTERNAL_URL,进去后按照提示设置 root 账号的密码。

https 配置 + 反向代理接力 + 内网穿透

为方便测试,可以将本地电脑的 hosts 先改回来,将 GitLab 所用的域名解析到外网主机 ip。

外网主机准备

  • 安装 nginx、certbot(用于获取 lentsencrypt 证书)

    sudo apt-get update
    sudo apt-get install nginx
    sudo apt-get install software-properties-common
    sudo add-apt-repository ppa:certbot/certbot
    sudo apt-get update
    sudo apt-get install python-certbot-nginx
  • 获取 letsencrypt ssl 证书。运行 sudo certbot certonly,按提示操作即可,生成的证书保存在 /etc/letsencrypt/live/ 对应的域名目录下。

  • frp 安装。外网主机需要运行 frp 服务端(frps),方法不赘述。

  • nginx 配置。直接上配置:/etc/nginx/conf.d/frp.conf

    ## frp
    upstream frp {
    server                    localhost:8080 fail_timeout=0;
    }

    /etc/nginx/sites-enabled/gitlab.conf

    ## http
    server {
    listen                    80;
    server_name               gitlab.example.com;
    return 301 https://$server_name$request_uri;
    }
    ## https
    server {
    listen                    443   ssl;
    server_name               gitlab.example.com;
    server_tokens             off;
    root                      /dev/null;
    ## Increase this if you want to upload larger attachments
    client_max_body_size      20m;
    ## SSL
    ssl                       on;
    ssl_certificate /etc/letsencrypt/live/gitlab.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/gitlab.example.com/privkey.pem;
    ## Individual nginx logs for this vhost
    error_log                 /var/log/nginx/gitlab_ssl_error.log;
    location / {
    ## If you use https make sure you disable gzip compression
    ## to be safe against BREACH attack.
    gzip                    off;
    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;
    proxy_set_header        Host                $http_host;
    proxy_set_header        X-Real-IP           $remote_addr;
    proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto   https;
    proxy_set_header        X-Frame-Options     SAMEORIGIN;
    proxy_pass http://frp;
    }
    }

其中 8080 是 frps 配置里的 vhost_http_port,http 请求通过 301 跳转到 https,https 请求用 nginx 反向代理到 frp,此时防卫 GitLab https 地址会出现 frp 的错误提示:

frp not found

GitLab 虚拟机改动

  • GitLab 配置修改。参考 Supporting proxied SSL ,修改虚拟机里的 /etc/gitlab/gitlab.rb,有 3 处改动:

    ## GitLab URL
    ##! URL on which GitLab will be reachable.
    ##! For more details on configuring external_url see:
    ##! https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab
    external_url 'https://gitlab.example.com'
    ...
    ##! **Override only if you use a reverse proxy**
    ##! Docs: https://docs.gitlab.com/omnibus/settings/nginx.html#setting-the-nginx-listen-port
    nginx['listen_port'] = 80
    
    ##! **Override only if your reverse proxy internally communicates over HTTP**
    ##! Docs: https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl
    nginx['listen_https'] = false

    external_url 改为 httpsnginx['listen_port'] 改为 80nginx['listen_https'] 改为 false,更新配置重新启动 GitLab:

    sudo gitlab-ctl reconfigure
    sudo gitlab-ctl restart

    此时的 GitLab 暂时无法访问到,需要通过反向代理。

  • frpc 配置。虚拟机的 frpc.ini 添加 gitlab 配置段:

    ...
    [gitlab]
    type = http
    local_port = 80
    custom_domains = gitlab.example.com
    ...

    此时 nginx -> frps/frpc -> GitLab 的通道就完全打通了,可以通过 https 地址访问到 GitLab。

ssh 穿透

为了简化操作,并且保持 GitLab 使用 ssh 协议操作代码库时地址的简洁,GitLab 虚拟机的 ssh 端口保持默认的 22 不变,外网主机的 ssh 端口换成别的(通过修改 /etc/ssh/sshd_config 内的 Port 字段),再通过 frp 将外网的 ssh 请求转到内网。虚拟机的 frpc 配置增加 ssh 部分:

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 22

这样 ssh gitlab.example.com 时访问的即是 GitLab 虚拟机,访问外网主机时需要加端口号。

启用 Container Registry

项目的一些发布操作可能会用到 Container Registry,需要启用 GitLab 自带的 Container Registry 。Registry 的设置方法与前面设置 GitLab 时类似,也是 nginx->frp->gitlab 一路反向代理过来。

再次生成 https 证书

再次运行sudo certbot certonly,这次使用 registry 的域名。

配置修改

  • 虚拟机 /etc/gitlab/gitlab.rb

    ...
    ################################################################################
    ## Container Registry settings
    ##! Docs: https://docs.gitlab.com/ce/administration/container_registry.html
    ################################################################################
    
    registry_external_url 'https://registry.example.com'
    ...
    ################################################################################
    ## Registry NGINX
    ################################################################################
    
    # All the settings defined in the "GitLab NGINX" section are also available in this "Registry NGINX" section
    # You just have to change the key "nginx['some_settings']" with "registry_nginx['some_settings']"
    
    # Below you can find settings that are exclusive to "Registry NGINX"
    registry_nginx['enable'] = true
    registry_nginx['listen_port'] = 5001
    registry_nginx['listen_https'] = false
    
    registry_nginx['proxy_set_headers'] = {
      "Host" => "$http_host",
      "X-Real-IP" => "$remote_addr",
      "X-Forwarded-For" => "$proxy_add_x_forwarded_for",
      "X-Forwarded-Proto" => "https",
      "X-Forwarded-Ssl" => "on"
    }
    ...

registry_external_url 一步到位设置成 https 地址,registry_nginx['enable'] 设置为 true,增加 registry_nginx['listen_port'] 设置为 5001(设置为 5000 时访问出错),增加 registry_nginx['listen_https'] 设置为 false,取消注释 registry_nginx['proxy_set_headers']

  • 虚拟机frpc.ini,增加 registry 部分:

    ...
    [gitlab_registry]
    type = http
    local_port = 5001
    custom_domains = registry.example.com
    ...
  • 外网主机 nginx 添加 registry 配置 /etc/nginx/sites-enabled/gitlab_registry.conf

    server {
    listen                    80;
    server_name               registry.example.com;
    return 301 https://$server_name$request_uri;
    }
    server {
    listen                    443   ssl;
    server_name               registry.example.com;
    server_tokens             off;
    root                      /dev/null;
    ## Increase this if you want to upload larger attachments
    client_max_body_size      20m;
    ## SSL
    ssl                       on;
    ssl_certificate /etc/letsencrypt/live/registry.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/registry.example.com/privkey.pem;
    ## Individual nginx logs for this vhost
    error_log                 /var/log/nginx/gitlab_registry_ssl_error.log;
    location / {
    ## If you use https make sure you disable gzip compression
    ## to be safe against BREACH attack.
    gzip                    off;
    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;
    proxy_set_header        Host                $http_host;
    proxy_set_header        X-Real-IP           $remote_addr;
    proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto   https;
    proxy_set_header        X-Frame-Options     SAMEORIGIN;
    proxy_pass http://frp;
    }
    }

注意此处的 proxy_pass 复用了上面 GitLab 使用的 frp 反向代理地址,因为 custom_domains 不同,frp 可以正确处理 2 个不同域名的请求。

访问 registry

浏览器直接访问 https://registry.example.com/v2,会出现 {"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]},表明 registry 启用成功了,此时查看 GitLab 里的项目,会看到有 Registry 标签页:
gitlab project registry

sudo docker login registry.example.com 尝试用户名密码登陆,因为虚拟机的 registry 是通过 http 访问的,不支持 http basic auth,会出现:
Error response from daemon: Get https://registry.codegeass.cc/v2/: unauthorized: HTTP Basic: Access denied
参考GitLab Container Registry#using-with-private-projects,我们用 Token 的方式,docker login registry.example.com -u <your_username> -p <your_access_token>

➜  ~ sudo docker login registry.example.com
Username: qqjt
Password: 
WARNING! Your password will be stored unencrypted in /home/tt/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

密码换成了 Personal Access Token 就可以成功登陆,Personal Access Token可以在 GitLab 个人设置(/profile/personal_access_tokens)的 Access Tokens 处生成。

至此配置就完成了。

GitLab 常用命令

gitlab-ctl start|stop|restart|status    # 控制服务启动/停止
gitlab-ctl tail                         # 查看实时日志
gitlab-ctl reconfigure                  # 更新配置

问题与解决

  • docker push 命令出现 413 Request Entity Too Large 的问题,可以调大前文中 nginx 配置里的 client_max_body_size 参数。

后续思考

frp 同时支持 https 协议的代理,不知道能否利用一下。

参考链接