🐕

WSL + DockerでGitLab環境構築

2021/12/27に公開

はじめに

今回、ローカル(WSL上)でGitLabを起動し、Git、Container Repository、CI/CDでのビルド、デプロイ(ssh経由)を行う手順を公開しました。今後、社内でGitLabの導入を検討されている方の参考資料として、活用いただければ幸いです。

ただし、一般的に公開されている手順とは異なり、ビルド・デプロイを高速化した手順となっていますので、ご注意ください。また、メモリをOS含めて12Gほど消費するため、専用PCにセットアップしたほうが快適に利用できます。

GitLab

GitLabとは、Gitリポジトリ機能を中心としたDevOpsのオールインワンパッケージソフトです。具体的には、Gitリポジトリ以外に、Package/Container Repository, Redmine, Backlog, Jenkins, WikiなどがGitLab上で利用でき、使いこなせば最強ツールになります。しかも、core機能はOSSとして無料で使用でき、有料サポートでも1ユーザ$19/月~で利用できます。

Dockerでのインストール

サーバとして起動する場合は、IPアドレスをルータで固定化することをお勧めします。Dockerのインストール手順および使い方は、前の記事を参考にしてください。

  1. 公式ページを参考に、Dockerでインストールを行う。
gitlab/.env
GITLAB_HOME=/srv/gitlab
GITLAB_URL=gitlab.example.com
gitlab/docker-compose.yml
version: "3.6"
services:
  web:
    image: 'gitlab/gitlab-ee:latest'
    restart: always
    hostname: '$GITLAB_URL'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://$GITLAB_URL'
        registry_external_url 'https://$GITLAB_URL:4567'
        nginx['ssl_certificate'] = "/etc/gitlab/ssl/$GITLAB_URL.crt"
        nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/$GITLAB_URL.key"
    ports:
      - '443:443'
      - '4567:4567'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
  • デフォルトでは、gitlab.example.comのSSL証明書(30日分)が自動生成される。ドメイン名は、SSL証明書の再作成時に変更可能。
  • Container RepositoryがデフォルトOFFなので、有効にする。
  • OS起動時に毎回サービスを起動させたくない場合は、restart: alwaysを削除する。
  1. docker-composeでサービスを起動してみる。
$ docker-compose up -d
  1. 以下のURLでログイン画面が表示されるか確認する。この時点ではURLが異なるのと、証明書をブラウザにインポートしていないため、警告が出る。
    https://localhost/

ドメインおよびSSL設定

ドメイン設定は、本来はDNSで行うが、今回はローカル環境なので/etc/hostsで行う。また、SSL証明書が30日しか利用できないので、新しいファイルに差し替える。

  1. Port Proxyおよびパーソナルファイアウォールの開放で、他のPCからPORT 22,443,4567でアクセスできるようにする。
gitlab.ps1
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit }
# ip address
$oldaddr = "192.168.1.XXX"
$newipaddr = "192.168.1.XXX"
# portproxy
netsh interface portproxy delete v4tov4 listenport=4567 listenaddress=$oldipaddr
netsh interface portproxy delete v4tov4 listenport=443 listenaddress=$oldipaddr
netsh interface portproxy delete v4tov4 listenport=22 listenaddress=$oldipaddr
netsh interface portproxy add v4tov4 listenport=4567 listenaddress=$newipaddr connectport=4567 connectaddress=127.0.0.1
netsh interface portproxy add v4tov4 listenport=443 listenaddress=$newipaddr connectport=443 connectaddress=127.0.0.1
netsh interface portproxy add v4tov4 listenport=22 listenaddress=$newipaddr connectport=443 connectaddress=127.0.0.1
netsh interface portproxy show all
# firewall
netsh advfirewall firewall delete rule name=gitlab 
netsh advfirewall firewall add rule name=gitlab dir=in action=allow protocol=TCP localport=22,443,4567
netsh advfirewall firewall show rule name=gitlab
pause -1
  1. ドメイン設定を行う。Windowsキー+Rでdriversを実行し、etc/hostsにドメイン名を追加する。VS Codeを利用すると、管理者権限で保存できる。
etc/hosts
192.168.1.XXX gitlab.example.com
  1. gitlabのコンテナ内で、既存の秘密鍵を利用し、有効期限10年のSSL証明書を再作成する。
$ cd /etc/gitlab/ssl
$ mv gitlab.example.com.crt gitlab.example.com.crt.bk
→ 既存のCRTファイルをバックアップ
→ 秘密鍵の再作成(openssl genrsa 2048 > gitlab.example.com.key)は省略
$ openssl req -new -key gitlab.example.com.key -subj "/CN=gitlab.example.com" > gitlab.example.com.csr
→ 秘密鍵から公開鍵(csrファイル)を作成。subjectはCNのみ。
$ echo "subjectAltName = DNS:gitlab.example.com" > san.txt
→ ChromeではCNではなく、SAN(Subject Alternative Name)を読み込むので作成。
$ openssl x509 -days 3650 -in gitlab.example.com.csr -out gitlab.example.com.crt -req -signkey gitlab.example.com.key -extfile san.txt
→ オレオレ証明書(crtファイル)を作成する。
  1. コンテナ内で作成したcrtファイルを、WSL上にコピーする。
$ cd ~/gitlab
$ docker cp gitlab_web_1:/etc/gitlab/ssl/gitlab.example.com.crt .
  1. VS Codeのエクスプローラにてcrtファイルをダウンロードし、以下のパス経由でcrtファイルを参照し、「信頼されたルート証明機関」で証明書をインストールする。
  • Edgeの場合:ダブルクリック
  • Chromeの場合:[設定]-[プライバシーとセキュリティ]-[セキュリティ]-[証明書の管理]
  • Firefoxの場合:[設定]-[プライバシーとセキュリティ]-[セキュリティ]-[証明書を表示]
  1. コンテナを再起動し、以下のURLで正常にログイン画面が表示されるか確認する。
    https://gitlab.example.com/

初期設定

  1. rootパスワードを初期化する。docker版だとパスワード変更画面が出てこないので、コンテナ内でパスワード変更を行う。起動まで時間がかかる。
$ gitlab-rails console -e production
irb(main):001:0> 
user = User.where(id: 1).first
user.password = 'password'
user.password_confirmation = 'password'
user.save!
exit
  1. rootユーザでログインする。日本語化はPreferences - Localization、パスワード変更はEdit Profile - Passwordで行う。
  2. ユーザを作成する。左上のMenu - Admin - Users - New userを選択。
    ・Name, Username, Emailを指定する。
    ・その際、パスワードがランダム生成されるため再度編集し、パスワードを変更する。※メール設定を行えば、パスワードがメールで届くが、今回は省略。
  3. 作成したユーザで、Docker経由でContainer Repositoryにログインしてみる。ログインする前にCSRファイルをDockerにコピーする必要がある。
$ docker login gitlab.example.com:4567
Login did not succeed, error: Error response from daemon: Get "https://gitlab.example.com/v2/": x509: certificate signed by unknown authority
→ 不明な機関ということでエラーになるため、オレオレ証明書をdockerにインストールする。
$ sudo mkdir -p /etc/docker/certs.d/gitlab.example.com:4567
$ sudo cp ~/gitlab/gitlab.example.com.crt /etc/docker/certs.d/gitlab.example.com:4567/ca.crt
→ SSL設定で利用したCSRファイルを、Dockerの証明書フォルダにコピーする。
$ docker login gitlab.example.com:4567
Username: <UserID>
Password: <Password>
WARNING! Your password will be stored unencrypted in /home/atoji/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
→ ログインができるようになる。2回目以降はパスワード不要。警告メッセージは、Dockerの標準仕様。

GitLab Runner

GitLab Runnerは、CI/CDをリモートサーバで実行できるようにするツールです。
公式で推奨されているのは、Docker方式ですが、毎回パッケージがダウンロードされてしまい、ビルドが少し遅いため、shellで動かす手順にしています。Dockerでも、.gitlab-ci.ymlにcacheを指定する(記載例あり)と、毎回ダウンロードされることはないので、どちらでも構いません。

また、Runnerには、専用(Specific runners)と共有(Shared runners)の2種類が存在しますが、今回はShared runnersを利用しています。

GitLab Runnerイメージ作成

今回は、shellで実行するため、GitLab Runnerに必要なパッケージをインストールします。今回は、jdkとnpmを入れてみました。

  1. 作成したユーザでログインし、グループを作成(Create a group - Create group)する。
    ・Group name, Group URL : traning
  2. GitLab Runner用のプロジェクトを作成(New project - Create blank project)する。
    ・Project name, Project slug : gitlab-runner
    ・Initialize repository with a README : チェックを外す
※初回git利用時のみ
$ git config --global user.name "<ユーザ名>"
$ git config --global user.email "<メールアドレス>"
$ git config --global http.sslVerify false
→ オレオレ証明書を利用できるようにする。
$ git config --global credential.helper store
→ ログイン・パスワードを保存する。
$ git clone https://gitlab.example.com/training/gitlab-runner.git
→ 外部公開するときは、GitHubと同じように、2段階認証に変更し、SSHでログインしたほうがよい。
やり方はGitHubと同じなので割愛する。
  1. GitLab Runnerでjdk 17およびnpm 8.3.0が利用できるように、Dockerfileを作成する。
gitlab-runner/Dockerfile
FROM gitlab/gitlab-runner:ubuntu
# jdk 17
RUN apt-get -y update
RUN apt-get install -y openjdk-17-jdk
# npm
RUN apt-get install -y curl gnupg
RUN curl -fsSL https://deb.nodesource.com/setup_17.x | bash - 
RUN apt-get install -y nodejs
RUN npm install npm@latest -g

→ packege.jsonやbuild.gradleを用意すれば、事前にパッケージもイメージ化できる。

  1. Dockerイメージをビルドし、Container Repositoryにpushする。
$ docker image build -t gitlab.example.com:4567/training/gitlab-runner .
$ docker push gitlab.example.com:4567/training/gitlab-runner

GitLab Runner実行・登録

  1. 作成したDockerイメージをもとに、docker-composeを作成する。また、gitlabで作成したオレオレ証明書も同フォルダにコピーする。
gitlab-runner/docker-compose.yml
version: '3.6'
services:
  runner:
    image: 'gitlab.example.com:4567/training/gitlab-runner'
    tty: true
    container_name: gitlab-runner
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /srv/gitlab-runner/config:/etc/gitlab-runner
      - ~/gitlab-runner/certs:/etc/gitlab-runner/certs
$ mkdir certs
$ cp ~/gitlab/gitlab.example.com.crt certs/
  1. 作成したファイルはgitでpushしておき、GitLab Runnerを起動する。
$ docker-compose up -d
  1. GitLabにrootでログインし、Menu - Admin - Overview - Runnersを選択し、[Register an instance runner]でtokenを取得する。
  2. Runnerのコンテナ上で、以下のコマンドを実行し、Runnersが追加されたことを確認する。
$ gitlab-runner register
Enter the GitLab instance URL : https://gitlab.example.com/
Enter the registration token: <token>
Enter a description for the runner: ⏎(任意の名前を設定できる)
Enter tags for the runner (comma-separated): ⏎(タグが設定できる)
Enter an executor: shell (dockerでもOK, Docker imageはなんでもよい)
(コマンドで登録する場合)
$ gitlab-runner register -n --url https://gitlab.example.com/ --registration-token <token> --executor shell

別サーバでのGitLab Runner

ここでは、別サーバでGitLab Runnerを動かすための手順を記載しています。PCが1台しかない場合は、本手順はスキップしてください。

  1. WSL上で、Dockerをインストールする。
  2. ドメインおよびSSL設定の2,5を実施し、https://gitlab.example.com/ にログインできるようにする。
  3. 初期設定の5を実施し、Container Repositoryに接続できるようにする。
  4. 先ほど作成したdocker-composeをgitから取得する。
$ git clone https://gitlab.example.com/training/gitlab-runner.git
  1. あとは、GitLab Runner実行・登録の2~4を実施すると、他のサーバでもGitLab Runnerが利用可能となる。

ビルド・デプロイ

前回作成したJavaのHello WorldをGitLabにコミットし、CI/CDでビルド・デプロイを行います。

Hello Worldプロジェクト作成・ビルド

  1. プロジェクトを作成(New project - Create blank project)する。
    ・Project name, Project slug : hello-world
    ・Initialize repository with a README : チェックを外す(gitのコマンドサンプルが表示される)
$ git clone https://gitlab.example.com/training/hello-world.git
  1. 前回、java17で作成したhello-worldをコピーし、コミットを行う。
  2. 自動ビルドができるように、CIパイプライン(.gitlab-ci.yml)を作成する。gitでpushを行うと、パイプラインが自動実行される。
.gitlab-ci.yml
stages:
  - build

build:
  stage: build
  # runnerをdockerにした場合は、コメント部分を外す
  #image: openjdk:17-alpine
  #cache:
  #  key: ${CI_COMMIT_REF_SLUG}
  #  paths:
  #    - .gradle/
  script:
    - ./gradlew build -x test
  artifacts:
    paths:
      - build/libs
  1. GitLab上で、CI/CD - Pipelinesを確認し、実行結果を確認する。Retryで再実行できる。
    ※私のPCでは、1回目が1分6秒、2回目以降は3秒でした。

Javaアプリ用Dockerイメージ作成

通常、その後はdockerイメージをビルドし、コンテナレジストリに登録するのが一般的ですが、今回は、Javaアプリ用のDockerイメージを作成し、そのイメージのjarファイルを差し替える方法を紹介します。これにより、デプロイ時のイメージビルドが不要になるため、デプロイが早くなります。

  1. javaアプリ用のプロジェクトを作成する。
    ・Project name, Project slug : java-app
    ・Initialize repository with a README : 外す。
$ git clone https://gitlab.example.com/training/java-app.git
  1. javaアプリ用のDockerイメージを作成する。
Dockerfile
FROM openjdk:17-alpine
RUN addgroup -S spring && adduser -S spring -G spring
ENV TZ=Asia/Tokyo
USER spring:spring
COPY app/app.jar /app/app.jar
ENTRYPOINT java -jar /app/app.jar
  1. 今回作成したHello Worldのjarファイルを、app/app.jarとして保存し、pushしておく。
  2. Dockerイメージをビルドし、Container Repositoryにpushする。
$ docker image build -t gitlab.example.com:4567/training/java-app .
$ docker push gitlab.example.com:4567/training/java-app
  1. テストとして、Dockerコンテナを起動し、WEB画面が表示されるか確認する。
    http://localhost:8080/
docker run -d -p 8080:8080  gitlab.example.com:4567/training/java-app

sshでのデプロイ

作成したjavaアプリ用Dockerイメージを利用し、CIパイプラインで指定したサーバにデプロイを行います。

  1. デプロイする端末で、sshを利用できるようにsshdを起動する。
$ sudo service ssh start

自動起動にしたい場合は、dockerと同じように.bashrcに追加する。

echo "" >> ~/.bashrc
echo "if [ \$(service ssh status | awk '{print \$4}') = \"not\" ]; then" >> ~/.bashrc
echo "  sudo service ssh start > /dev/null" >> ~/.bashrc
echo "fi" >> ~/.bashrc
  1. サーバで秘密鍵(id_rsa)と公開鍵(id_rsa.pub)を作成し、公開鍵のファイル名を変更する。
$ ssh-keygen -t rsa
→ 3回リターンを押すと、~/.sshに鍵が作成される。
$ cd ~/.ssh
$ mv id_rsa.pub authorized_keys
  1. 秘密鍵をGitLabのhello-worldプロジェクトに登録(Settings - CI/CD - Variables)する。
    ・Type : Variable(デフォルトのまま)
    ・Key : SSH_PRIVATE_KEY
    ・Value : ~/.ssh/id_rsaの内容
  2. CIパイプライン(.gitlab-ci.yml)に、deploy stagingを追加する。docker版の場合、デプロイ用のimageを作ったほうがよい。
.gitlab-ci.yml
image: ubuntu:20.04

stages:
  - build
  - deploy staging

variables:
  USERID: <ユーザID>
  STAGING_HOSTNAME: gitlab.example.com
  STAGING_PORT: 8080
  STAGING_CONTAINER_NAME: hello-staging
  STAGING_DOMAIN: http://localhost:8080/

build:
  stage: build
  script:
    - ./gradlew build -x test
  artifacts:
    paths:
      - build/libs

# デプロイ用テンプレート
.deploy_template: &deploy
  #image: ubuntu:20.04
  variables:
    # チェックアウトしない。
    GIT_STRATEGY: none
  before_script:
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    # 新規ホストの確認を不要にする。
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
    # 秘密鍵をファイル保存する。
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    #- apt-get update -y & apt-get install openssh-client -y
  script:
    # jarファイルをコピーする。
    - scp -pr build/libs/hello-world-0.0.1-SNAPSHOT.jar $USERID@$HOSTNAME:~/java-app/hello-world.jar
    # 指定したコンテナが存在するかチェックする。
    - CONTAINER_STATUS=`ssh $USERID@$HOSTNAME docker container ls -a -q -f name="$CONTAINER_NAME" | wc -c`
    # コンテナが存在しない場合は新規作成、存在しない場合は再起動を行う。
    - |
      if [ $CONTAINER_STATUS -eq 0 ]; then
        ssh $USERID@$HOSTNAME docker run -d -p $PORT:8080 -v /home/$USERID/java-app/hello-world.jar:/app/app.jar --restart=always --name $CONTAINER_NAME gitlab.example.com:4567/training/java-app
      else
        ssh $USERID@$HOSTNAME docker restart $CONTAINER_NAME
      fi

# コンテナ停止用テンプレート
.stop_template: &stop
  #image: ubuntu:20.04
  variables:
    GIT_STRATEGY: none
  when: manual
  #before_script:
  #- apt-get update -y & apt-get install openssh-client -y
  script:
    # コンテナを削除する。
    - ssh $USERID@$HOSTNAME docker rm -f $CONTAINER_NAME

# ステージングデプロイ
deploy staging:
  <<: *deploy
  stage: deploy staging
  environment:
    name: staging
    url: $STAGING_DOMAIN
    on_stop: stop staging
  variables:
    HOSTNAME: $STAGING_HOSTNAME
    PORT: $STAGING_PORT
    CONTAINER_NAME: $STAGING_CONTAINER_NAME

# ステージング停止
stop staging:
  <<: *stop
  stage: deploy staging
  environment:
    name: staging
    action: stop
  variables:
    HOSTNAME: $STAGING_HOSTNAME
    CONTAINER_NAME: $STAGING_CONTAINER_NAME
  1. gitでpushをすると、Gitlabサーバ上にコンテナが起動し、WEB画面が表示されるか確認する。また、Deployments - Environments - stagingでStopボタンを押すと、コンテナが削除されることも確認する。
  2. .gitlab-ci.ymlのSTAGING_HOSTNAMEやSTAGING_PORTを変更し、別のサーバでもデプロイ可能か確認する。また、ポートを追加することで複数コンテナへのデプロイも可能。

おわりに

もし、GitLab、GitLab Runnerの自動起動をやめたいときは、コンテナを削除しても、データは/srv配下に残るので問題ありません。ただし、パッケージは消えてしまいます。

今回の手順でGitlabの基本機能は理解できたかと思います。他にも、IssusやMerge requests, Wikiという機能もありますが、マニュアルを見ながらが、操作できると思いますので、ぜひ使ってみてください。

今後も、Moniter(Prometheus + Grafana)や、Infrastructure(Terraform)についても記事にしてみたいと思いますので、よろしくお願いいたします。

Discussion