セルフホスト GitLab で non-bundled な Nginx を使うときの覚え書き (Dockerコンテナ間のUNIXドメインソケット通信)
2024年07月18日

概要

通常、Self-Managed版のGitLabは内部的にバンドルされているNginxを使うようになっているが、それを使わず別のWebサーバーを使うことが可能になっている
Docker版のGitLabを利用する際、バンドルされたNginxを無効化し、同じくコンテナとしてたてたNginxを使うようにするために必要になった設定をメモとして残す。

動作環境

  • Linux: Ubuntu 22.04
  • GitLabイメージ: gitlab/gitlab-ce:16.8.5-ce.0
  • Nginxイメージ: nginx:1.25.5

起こった問題

以下のようなdocker-compoaseの定義ファイル(一部抜粋)を用意し、コンテナを起動した。

service:
  gitlab:
    image: gitlab/gitlab-ce:16.8.5-ce.0
    container_name: gitlab
    restart: always
    environment:
      # GITLAB_OMNIBUS_CONFIG環境変数を追加することで内容が設定として反映される
      # nginx['enable'] = false: バンドルされたNginxを無効化
      # web_server['external_users'] = ['www-data']: コンテナで動かすNginxのプロセスはwww-dataユーザーで実行されている
      # gitlab_rails['trusted_proxies'] = ['172.17.0.0/16']: Dockerデフォルトのブリッジネットワークのアドレス範囲からの通信を許可
      GITLAB_OMNIBUS_CONFIG: |
        external_url '<GitLabを公開するURL>'
        nginx['enable'] = false
        web_server['external_users'] = ['www-data']
        gitlab_rails['trusted_proxies'] = ['172.17.0.0/16']
    expose:
      - '80'
    volumes:
      - gitlabsocket:/var/opt/gitlab/gitlab-workhorse/sockets
  nginx:
    container_name: nginx
    build: ./nginx
    restart: always
    tty: true
    ports:
      - '80:80'
    volumes:
      - gitlabsocket:/var/opt/gitlab/gitlab-workhorse/sockets
volumes:
  gitlabsocket:

ここで、Dockerボリュームgitlabsocketを用意し双方のコンテナにマウントすることで、gitlabコンテナの/var/opt/gitlab/gitlab-workhorse/socketsに存在するソケットファイルにnginxコンテナがアクセスできるようにしている。また、Nginx側の.confファイルで以下のようにソケットのパスを指定していた。

upstream gitlab-workhorse {
  server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket
}

上記設定でコンテナを起動してアクセスしたところ、nginxの502 Bad Gatewayのレスポンスとなった。nginx側ではconnect() to unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket failed (13: Permission denied) while connecting to upstreamのようなエラーログが出ており、ソケットファイルにアクセスする権限がないことが原因だった。

うまくいかなかった方法

GitLabのイシューの回答を参考に、以下のようなシェルスクリプトをnginxコンテナの/docker-entrypoint.dに格納するようにした。(Nginx公式のイメージでは/docker-entrypoint.d内のシェルスクリプトがコンテナ起動時に順次実行される

#!/bin/sh

SOCKET_DIR="/var/opt/gitlab/gitlab-workhorse/sockets"
SOCKET_FILE=$SOCKET_DIR"/socket"

while [ ! -e $SOCKET_FILE ]
do
  sleep 1
done

chmod -R 777 $SOCKET_DIR

これにより、ソケットファイルが作られたらその権限を変更することで、nginxのwww-dataユーザーがアクセスしコンテナ間通信ができるようになった。
しかし、ホストのマシンを再起動したり、docker compose restartしたりすると再度通信ができなくなってしまった。これは、ボリュームマウントされているソケットファイルがrestart直後も残っているために、gitlabコンテナが立ち上がり新しいソケットファイルが作られた際にそれを検知して権限を更新し直すことができなかったためと考えられた。

うまくいった方法

そもそも、nginxコンテナ側でソケットファイル/var/opt/gitlab/gitlab-workhorse/sockets/socketの所有者・所有グループを確認すると、UID=998, GID=998となっている。
gitlabコンテナの方を見てみると、gitlabのプロセスを実行しているユーザーgit(グループgit)はUID=998、GID=998であり、ソケットファイルもこのgitユーザーが所有しているということがわかる。
そのため、Nginx側もワーカープロセスをUID998のgitユーザーで動かせばソケットファイルにアクセスできることになる。
gitlabコンテナの設定を以下のように変更してUID、GIDを明示的に指定するとともに、外部Webサーバーのユーザー名はgitに変える。

- web_server['external_users'] = ['www-data']
+ web_server['external_users'] = ['git']
+ user['uid'] = 998
+ user['gid'] = 998

さらに、Nginx側ではDockerfileでgitユーザーとgitグループをUID=998、GID=998で作るよう指定し、

RUN groupadd -g 998 git && useradd -u 998 -g 998 git

Nginxの.confファイルでワーカープロセスはgitユーザーが実行することにする。

user  git;

上記設定により、nginxコンテナとgitlabコンテナ間でUNIXドメインソケットによる通信をさせることができた。