Archlinux部署gitlab

于 2023-10-09 发布 , 于 2023-10-30 更新

Docker

使用docker来部署gitlab-ce,基本上就是两步走:首先创建文件目录,然后编写docker-compose.yml,就完了。

目录结构是这样的:

/opt/gitlab/
|-- config/
|-- data/
|-- logs/
`-- docker-compose.yml

docker-compose.yml的内容修改自官方的文件,主要去除了gitlab-runner的内容1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
version: '3.6'
services:
  gitlab:
    image: 'gitlab/gitlab-ce:latest'
    restart: always
    # 这里也是设置你的域名
    hostname: 'git.mutebot.net'
    environment:
      TZ: 'Asia/Shanghai'
      GITLAB_OMNIBUS_CONFIG: |
        # 这里设置你的域名
        external_url 'https://git.mutebot.net'
        # 更改容器内nginx的http端口
        nginx['listen_port'] = 8380
        # 关闭容器内nginx的监听https,https由host的nginx负责
        nginx['listen_https'] = false
        # 同时也要关闭letsencrypt,不然会自动尝试申请ssl证书然后fatal error
        letsencrypt['enabled'] = false
        # 关掉容器内nginx的client_max_body_size,这个限制gitlab有设置项管理
        nginx['client_max_body_size'] = '0'
    ports:
      # 映射容器内nginx的监听端口到host
      - '8380:8380'
      # 这里其实把gitlab的ssh映射到host,才能实现ssh认证。
      # host的端口可以自己设置
      - '22:22'
    volumes:
      - './config:/etc/gitlab'
      - './logs:/var/log/gitlab'
      - './data:/var/opt/gitlab'
    shm_size: '256m'
    configs:
      # 挂载gitlab.rb配置文件进容器
      - source: gitlab
        target: /omnibus_config.rb
    extra_hosts:
      - "host.docker.internal:host-gateway"
configs:
  gitlab:
    file: ./gitlab.rb

然后配置一下gitlab.rb,主要是为了确保容器内的nginx不要监听https以及不自动尝试申请ssl证书:

1
2
3
nginx['client_max_body_size'] = '0'
nginx['listen_https'] = false
letsencrypt['enabled'] = false

然后就可以拉起容器了:sudo docker compose up -d

Tips

  1. 官方文档:install - docker
  2. 有的教程会说,需要确保yaml的端口映射内外一致,其实是错的。比如官方教程里说可以配置gitlab_rails['gitlab_shell_ssh_port']来更改ssh监听端口,然而实际没有效果。其实根据docker的运作方式可以知道,容器里的端口根本无所谓,只要端口映射写对了就能够正常用。
  3. 修改gitlab.rb之后需要docker compose restart,而修改docker compose.yml的话就需要docker compose down && docker compose up -d了。而官方docker其实是根据你的配置重新build一个出来的,所以耗时非常长。

传统方法

这种庞然大物建议真的,用gitlab-ce的docker,别实机部署,难保哪次system update就炸了,而且很难找log来分析。

Gitlab官方没有支持archlinux,但是archwiki有详尽的安装介绍。只不过wiki中有些没有说清楚的部分,需要记录下来。

请在阅读本文的过程中同时参照官方wiki:GitLab

Pre

Redis

按照Redis页面的指引安装Redis(pacman -S redis)并编辑配置文件(/etc/redis/redis.conf):

  1. 仅限本机用unix domain socket访问,禁用TCP连接:将port 6379改为port 0
  2. 打开unix domain socket监听以及修改权限为0770(允许redis组成员访问):
    1
    2
    
     unixsocket /run/redis/redis.sock
     unixsocketperm 770
    

保存配置之后,启动并设置开机启动Redis服务:

1
2
sudo systemctl enable redis
sudo systemctl start redis

PostgreSQL

从这里开始官方指南就开始有点麻了

根据官方wiki 安装PostgreSQL(pacman -S postgresql)。并不需要急着启动服务,需要先进行配置。

首先切换到postgres用户:sudo -iu postgres;然后初始化initdb -D /var/lib/postgres/data。等命令行出现以下内容就代表初始化完毕:

1
2
3
Success. You can now start the database server using:

    pg_ctl -D /var/lib/postgres/data -l logfile start

随后先exit切回原来的用户,启动并设置开机启动postgresql:

1
2
sudo systemctl enable postgresql.service
sudo systemctl start postgresql.service

GitLab

先安装gitlab包:pacman -S gitlab。也是不急着启动,一大堆要配置的。

基础配置

对于/etc/webapps/gitlab/gitlab.yml,至少要更改gitlab下的host,把它设置成你的完整域名,比如我的是git.mutebot.net

1
2
3
4
5
6
7
8
9
10
production: &base
  #
  # 1. GitLab app settings
  # ==========================

  ## GitLab settings
  gitlab:
    ## Web server settings (note: host is the FQDN, do not include http://)
    host: git.mutebot.net
    port: 80

注意这里的port我没有改成443,因为后面的内网访问其实是需要用http的,所以保留是80就可以了。外网的访问会由外网的Nginx自动重定向至HTTPS。

官方指南的2.2节看到这里就行了,后面一大堆路径配置其实如果是用默认安装方式是不需要看的。接下来直接跳过2.3小节(自定义Puma端口),来到2.4的配置secret部分。

secret

需要往/etc/webapps/gitlab/secret/etc/webapps/gitlab-shell/secret里面随便填点东西,因为gitlab需要根据这两个文件来生成token。官方指南里提供的生成随机字符串的方案是(需要用root身份执行):

1
2
3
4
hexdump -v -n 64 -e '1/1 "%02x"' /dev/urandom > /etc/webapps/gitlab/secret
chmod 640 /etc/webapps/gitlab/secret
hexdump -v -n 64 -e '1/1 "%02x"' /dev/urandom > /etc/webapps/gitlab-shell/secret
chmod 640 /etc/webapps/gitlab-shell/secret

那么用同样的方法我们可以在/etc/webapps/gitlab/secrets.yml里配置一些字符串。该文件需要包含以下内容,其中每个secret都用hexdump -v -n 64 -e '1/1 "%02x"' /dev/urandom生成一条字符串再粘进去就行了:

1
2
3
4
5
production:
  secret_key_base: secret
  db_key_base: secret
  otp_key_base: secret
  openid_connect_signing_key: secret

Redis

为了让GitLab可以连接我们配置好的Redis,需要做两件事:

  1. 把gitlab用户添加到redis用户组:sudo usermod -aG redis gitlab
  2. 配置/etc/webapps/gitlab/resque.yml文件:
    1
    2
    3
    4
    5
    6
    
    development:
     url: unix:/run/redis/redis.sock
    test:
     url: unix:/run/redis/redis.sock
    production:
     url: unix:/run/redis/redis.sock
    

PostgreSQL

为了这一点醋包了一大盘饺子

首先切换到postgres用户,然后创建gitlab用的postgresql用户和数据库。注意自行替换your_username_hereyour_password_here的地方,其他都不需要动。

  1. 无论你输入用户名的时候是否包含大写,最后都会被postgresql转换成小写,所以请务必全小写。(就是因为这个坑我总是auth不成功)
  2. gitlab需要一些postgresql的extension,archwiki上建议直接把gitlab的用户给到superuser权限来让它自己安装extension。这是一种办法,但是为了安全性,建议手动安装extension。

手动安装extension

1
2
3
sudo -iu postgres
# 下面这个命令会打开一个postgresql的命令行
psql -d template1

根据GitLab的文档安装所需extension,比如本文写作时需要安装的包括pg_trgmbtree_gistplpgsql。分两步走,首先创建用户和数据库,然后切换数据库安装插件。

1
2
3
4
CREATE USER your_username_here WITH PASSWORD 'your_password_here';
ALTER USER your_username_here;
CREATE DATABASE gitlabhq_production OWNER your_username_here;
\q
1
2
# 切换数据库到gitlabhq_production
psql -d gitlabhq_production
1
2
3
4
5
6
7
8
/* 先看看装了什么插件 */
\dx
/* 如果输出结果没有包含所需的所有插件,那就安装 */
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS plpgsql;
/* 再看看装了什么插件 */
\dx

正常来说后面这个\dx会输出类似这样的结果:

1
2
3
4
5
6
7
8
gitlabhq_production=# \dx
                                     List of installed extensions
    Name    | Version |   Schema   |                            Description                            
------------+---------+------------+-------------------------------------------------------------------
 btree_gist | 1.7     | public     | support for indexing common datatypes in GiST
 pg_trgm    | 1.6     | public     | text similarity measurement and index searching based on trigrams
 plpgsql    | 1.0     | pg_catalog | PL/pgSQL procedural language
(3 rows)

那就可以了,用\q退出postgresql的命令行就行。

让gitlab自己安装extension

1
2
3
sudo -iu postgres
# 下面这个命令会打开一个postgresql的命令行
psql -d template1
1
2
3
4
CREATE USER your_username_here WITH PASSWORD 'your_password_here';
ALTER USER your_username_here SUPERUSER;
CREATE DATABASE gitlabhq_production OWNER your_username_here;
\q

SUPERUSER是故意的,因为gitlab需要这个权限:

The reason for creating the user as a superuser is that GitLab is trying to be “smart” and install extensions (not just create them in its own userspace). And this is only allowed by superusers in Postgresql.

设置GitLab的数据库配置文件

然后就是改配置/etc/webapps/gitlab/database.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
production:
  main:
    adapter: postgresql
    encoding: unicode
    database: gitlabhq_production
    # 这行本来没有,自己加的
    pool: 10
    # 这里也是要自己改成实际的内容
    username: your_username_here
    password: "your_password_here"
    # 注释掉host
    # host: localhost
    # 添加postgresql的unix domain socket
    socket: /run/postgresql/.s.PGSQL.5432

到这里基本成功一半,可以初始化安装了。

初始化

由于我是远程部署的,没办法通过内网用浏览器访问网页端设置密码,所以只能用命令行参数的方式直接指定root账户的密码和邮箱。

这里就涉及一个小技巧,怎么让zsh不记住这个包含敏感信息的命令呢?其实很简单的:先套娃启动一个zsh,然后unset掉HISTFILE,后面执行的命令就不会记录在历史记录里了。

命令行指定root密码和邮箱的变量分别是GITLAB_ROOT_PASSWORDGITLAB_ROOT_EMAIL,改成自己实际的值。另外,还需要先手动启动gitlab-gitaly.service(不需要设置成开机启动,后面有socket统一处理)。

1
2
3
4
5
6
7
8
9
10
11
# 原地启动一个新的zsh
zsh
# 然后禁用历史记录
unset HISTFILE
# 启动gitlab-gitaly.service
sudo systemctl start gitlab-gitaly.service
cd /usr/share/webapps/gitlab
sudo -u gitlab $(cat environment | xargs) \
    bundle-2.7 exec rake gitlab:setup     \
    GITLAB_ROOT_PASSWORD=yourpassword     \
    GITLAB_ROOT_EMAIL=youremail 

执行之后会提示yes/no的,记得输入别傻等。

可能有的小聪明会问,“sudo的-E选项不是可以保留当前的环境变量嘛?我直接export然后-E就好了呀”。重点就是-E会保留所有的环境变量,包括HOME,这会导致错误的初始化结果——初始化命令的log输出也会提示你:Warning提示$HOME目录无法写入,正在使用临时目录。

然后先忽略官方指南中check your installation的部分,因为按照指南来说你的gitlab-workhorse等等都还没启动,check是肯定不过的。直接先调整modifier bits先:

1
2
3
4
5
6
7
# 直接切root用户做
sudo su
chmod -R ug+rwX,o-rwx /var/lib/gitlab/repositories/
chmod -R ug-s /var/lib/gitlab/repositories
find /var/lib/gitlab/repositories/ -type d -print0 | xargs -0 chmod g+s
# 退root用户
exit

官方指南接下来就是反向代理的部分了,但是我们这里先把整个gitlab启动起来先:

1
2
sudo systemctl enable gitlab.target
sudo systemctl start gitlab.target

Tips

重新初始化

万一由于各种原因需要重新执行这个初始化命令,那就需要先更改sudo的env_keep配置,添加DISABLE_DATABASE_ENVIRONMENT_CHECK。先sudo visudo添加这一行:

1
Defaults env_keep+="DISABLE_DATABASE_ENVIRONMENT_CHECK"

然后再重新初始化:

1
2
3
4
5
export DISABLE_DATABASE_ENVIRONMENT_CHECK=1
sudo -u gitlab $(cat environment | xargs) \
    bundle-2.7 exec rake gitlab:setup     \
    GITLAB_ROOT_PASSWORD=yourpassword     \
    GITLAB_ROOT_EMAIL=youremail

反向代理

由于我是用内网穿透的方式提供服务的,所以这里免不了讲内网穿透的内容。对于直接把gitlab部署在公网服务器的有钱人,那大概看看改改就好。

由于我之前就有其他服务内网穿透到公网了,而且公网的nginx开了proxy_protocol,所以listen的port也有点不同。总而言之内网穿透的影射关系是内网443->公网8002。另外为了ssh授权,还需要映射本地的sshd到公网,这个端口比较敏感就不说了。

公网Nginx

公网的配置很简单,毕竟只是一个转发器。敏感内容就打星号了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
server {
  listen 48443 ssl http2 proxy_protocol;
  server_name git.mutebot.net;
  root /dev/null;

  # ssl证书地址
  ssl_certificate ***;
  ssl_certificate_key ***;

  gzip_vary on;
  gzip_comp_level 4;

  access_log /var/log/nginx/access.log normal;
  error_log /var/log/nginx/error.log warn;

  include /etc/nginx/mime.types;

  location / {
    proxy_ssl_server_name on;
    proxy_ssl_name $host;
    proxy_pass https://127.0.0.1:8002;
    proxy_set_header X-Real-IP $proxy_protocol_addr;
    proxy_set_header X-Forwarded-For $proxy_protocol_addr;
    proxy_set_header Host $http_host;
    proxy_set_header cookie $http_cookie;
    proxy_set_header Proxy-Connection "";
    proxy_ssl_certificate ***;
    proxy_ssl_certificate_key ***;
    proxy_ssl_trusted_certificate ***;
    proxy_http_version 1.1;
  }
}

记得测试并重启nginx:sudo nginx -t && sudo systemctl restart nginx

内网Nginx

内网Nginx的配置因为要兼顾HTTP和HTTPS,所以需要把公用的内容保存起来,放在/etc/nginx/snippets/gitlab.conf里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
location ~ ^/(assets)/ {
    root /usr/share/webapps/gitlab/public;
    gzip_static on; # to serve pre-gzipped version
    expires max;
    add_header Cache-Control public;
}

# 这几行就是为了能够自由切换HTTP和HTTPS的
add_header Cache-Control 'no-store' always;
add_header Cache-Control 'no-cache' always;
expires 0;

location / {
    # unlimited upload size in nginx (so the setting in GitLab applies)
    client_max_body_size 0;

    # proxy timeout should match the timeout value set in /etc/webapps/gitlab/puma.rb
    proxy_read_timeout 60;
    proxy_connect_timeout 60;
    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 $scheme;

    proxy_pass http://gitlab-workhorse;
}

error_page 404 /404.html;
error_page 422 /422.html;
error_page 500 /500.html;
error_page 502 /502.html;
error_page 503 /503.html;
location ~ ^/(404|422|500|502|503)\.html$ {
  root /usr/share/webapps/gitlab/public;
  internal;
}

然后就是server部分的配置,放在/etc/nginx/sites/mutebot/git.conf里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
upstream gitlab-workhorse {
  server unix:/run/gitlab/gitlab-workhorse.socket fail_timeout=0;
}

server {
  listen 80;
  server_name git.mutebot.net;
  include snippets/gitlab.conf;
}

server {
  listen 443 ssl http2;
  server_name git.mutebot.net;
  
  ssl_certificate ***;
  ssl_certificate_key ***;
  ssl_client_certificate ***;
  ssl_trusted_certificate ***;
  ssl_verify_client on;
  include /etc/nginx/ssl_intermediate.conf;
  
  gzip on;
  gzip_vary on;
  gzip_comp_level 4;
  # Remove X-Powered-By, which is an information leak
  fastcgi_hide_header X-Powered-By;
  include snippets/gitlab.conf;
}

记得测试并重启nginx:sudo nginx -t && sudo systemctl restart nginx

到这里,通过访问完整域名(https://git.mutebot.net)应该是可以打开gitlab的登录界面的了。如果不能的话就要看看哪里出问题了。

Post

ssh授权

需要把sshd的配置改一下,将AuthorizedKeysFile加一个%h/上去(/etc/ssh/sshd_config):

1
2
#AuthorizedKeysFile     .ssh/authorized_keys
AuthorizedKeysFile     %h/.ssh/authorized_keys

改完之后重启sshd(sudo systemctl restart sshd)。在GitLab登录之后点击头像,Edit Profile,SSH Keys添加SSH Keys之后,就可以用ssh -T gitlab@git.mutebot.net来测试是否成功了。这里很当然地省略了~/.ssh/config的内容,摸了

自动更新数据库

原文照搬,就是创建两个hooks和一个script。首先是Update前的hook(/etc/pacman.d/hooks/05-gitlab-pre.hook):

1
2
3
4
5
6
7
8
9
[Trigger]
Operation = Upgrade
Type = Package
Target = gitlab

[Action]
Description = Stopping gitlab services
When = PreTransaction
Exec = /usr/bin/systemctl stop gitlab-gitaly.service gitlab-mailroom.service gitlab-puma.service gitlab-sidekiq.service gitlab-workhorse.service

然后是Update后的hook(/etc/pacman.d/hooks/99-gitlab-post.hook):

1
2
3
4
5
6
7
8
9
[Trigger]
Operation = Upgrade
Type = Package
Target = gitlab

[Action]
Description = Migrating GitLab database and starting services
When = PostTransaction
Exec = /etc/pacman.d/scripts/gitlab-migrate-database.sh

最后是post hook所调用的script(/etc/pacman.d/scripts/gitlab-migrate-database.sh):

wiki里写的是bundle-2.7,但是实际上在我更新了gitlab之后,升级脚本并没有运行成功。手动执行这个命令,它会抱怨什么CFPropertyList找不到。但是如果去掉这个版本号,直接用bundle的话,就成功了。

1
2
3
4
5
6
7
#!/bin/sh

cd "/usr/share/webapps/gitlab"
sudo -u gitlab $(cat environment | xargs) bundle exec rake db:migrate

# The hook runs after 30-systemd-daemon-reload.hook so another systemctl daemon-reload is not necessary.
systemctl start gitlab.target

记得给script加上可执行权限:sudo chmod +x /etc/pacman.d/scripts/gitlab-migrate-database.sh

内网直连

我用的是mosdns,所以直接在hosts插件里面新增一条记录,git.mutebot.net的地址就是运行gitlab的机子的IP。这样的话内网机就可以直连gitlab而不用跑公网转发了。但是比如gitlab-runner是一定要用https授权的,那就没办法了,只能不用它了。

参考

  1. 关掉gitlab-ce的docker的letsencrypt:How to disable Let’s Encrypt in the Gitlab CE docker image
  1. 关于这部分内容请看本博客的另一篇文章gitlab-runner。 

目录