
[!] この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
近年、CI/CDという言葉をよく耳にします。開発・リリースのプロセスを自動化することで、品質や生産性の向上・人的ミスの減少・リリースサイクルの改善や高速化・コスト削減などがメリットとして挙げられています。
世の中には様々なCI/CDのツールがありますが、この記事ではGitLabのCI/CDと連携してパイプラインを実行するGitLab Runnerの構築方法と課題対応について紹介します。
CI/CDとは
継続的インテグレーション(Continuous Integration)と継続的デリバリ(Continuous Delivery)または継続的デプロイ(Continuous Deployment)の略で、この2つを組み合わせたものを「CI/CD」と呼びます。
CIはソースコードをリポジトリで管理し、ビルドからテストまでを自動化することです。ソースコードをリポジトリに登録するたびにビルドからテストまで自動的に行うことで問題を即座に発見できるため、大きな手戻りの発生を抑えることができます。
CDはテストを完了したソフトウェアの開発環境や本番環境へのリリースを自動化することです。これによりテスト完了からリリースまでの時間を短縮することができます。
ビルド・テスト・リリースを自動化することで、担当者による品質のばらつきや人的ミスを排除することも可能となります。
GitLab CI/CD環境
システム構成
私たちが以前より運用してきたGitLabを活用するため、GitLabのCI/CDと連携できる「GitLab Runner」を利用する方針としました。また、Docker Private RegistryがGitLabのプロジェクトごとに利用できる「GitLab Container Registry」の機能も一緒に利用することとしました。
今回構築したGitLab CI/CD環境のシステム構成は以下の通りです。
GitLab CI/CDの動作は以下の通りです。
- GitLab Runnerは実行するパイプラインがあるかを周期的に監視
- GitLabはリポジトリが更新されるとパイプラインを作成
- GitLab Runnerが実行するパイプラインを検知
- GitLab Runnerは実行するパイプラインで使用するDockerイメージをGitLab Container Registryから取得1
- GitLab Runnerは取得したDockerイメージからコンテナを起動
- GitLab Runnerはコンテナ内でリポジトリをgit cloneしパイプラインを実行
GitLab Runnerの動作
GitLab Runnerは、GitLabのリポジトリに変更があった際に、自動的にパイプラインを実行する機能です。
一回の実行単位をパイプラインと呼びます。パイプラインは、1つまたは複数のジョブで構成されます。
パイプラインのひな形は、1つのプロジェクトで1つだけ登録することができます。
下記のパイプラインの例では、#6137と#6138が実行されています。
パイプラインの中では3つのジョブが実行されています。
今回はGitLab RunnerのexecutorをDockerとしているため、1つのジョブは1つのDockerコンテナとして実行されます。ジョブは異なるDockerイメージを指定できます。
ジョブの終了時にはDockerコンテナも削除されるため、ジョブ内で作成したデータ(ビルドして生成されるバイナリファイルなど)も一緒に削除されてしまいます。
そこでアーティファクトとキャッシュを利用します。
アーティファクトとは
アーティファクトは、ジョブ内で作成したデータをGitLabに格納し、ダッシュボードからダウンロードできるようにする機能です。格納するデータは利用者が指定する必要があります。
キャッシュとは
キャッシュは、ジョブ内で作成したデータをジョブ間で共有する機能です。共有するデータは利用者が指定する必要があります。
例)ジョブ間でのキャッシュの利用
Job01:javaのコンパイルが可能なDockerイメージを利用してビルドを実行
Job02:javaの静的解析が可能なDockerイメージを利用してビルド結果を解析
このときキャッシュを利用して、Job01のビルド結果をJob02で利用することが可能になります。
キャッシュ・アーティファクト・レジストリの格納場所
GitLab RunnerとGitLab Container Registryを運用していく上で、キャッシュ・アーティファクト・レジストリを保持するためにディスク容量が必要です。 それらの格納場所について説明します。
GitLab Runnerのキャッシュの格納場所
キャッシュの格納先は、GitLab Runnerサーバの/var/lib/docker/volumes/配下です。
パイプラインごとにDocker Volumeが作成され、その中に格納されます。
1つのプロジェクトでrunner-で始まるDocker Volumeが2つ作成され、1つがキャッシュを格納するもので、もう1つがリポジトリ内のファイルとジョブで作成された成果物が格納されるものになります。
キャッシュのファイル名はcache.zipになります。
├runner-[Runner由来の値]-project-[プロジェクトID]-concurrent-[連番]-cache-[ランダム文字列]/│└_data/│ └[グループ名]/│ └[プロジェクト名]/│ └[キャッシュキー]/│ └cache.zip└runner-[Runner由来の値]-project-[プロジェクトID]-concurrent-[連番]-cache-[ランダム文字列]/ └_data/ └[グループ名]/ └[プロジェクト名]/ ├build/ │└Hello.class ├.git/ │:(略) ├.gitlab-ci.yml ├result.txt └src/ └Hello.java
GitLab Runnerのアーティファクトの格納場所
アーティファクトの格納先は、GitLabサーバの/var/opt/gitlab/gitlab-rails/shared/artifacts/配下です。
1つのプロジェクトでプロジェクトIDのハッシュ値でディレクトリが作成され、日付とジョブIDでディレクトリが作成されます。
アーティファクトのファイル名はartifacts.zipになります。
└[プロジェクトIDのハッシュ値の先頭から1-2桁]/ └[プロジェクトIDのハッシュの先頭から3-4桁]/ └[プロジェクトIDのハッシュ値]/ ├[日付]/ │├[ジョブID]/ ││└[通番]/ ││ └job.log │└[ジョブID]/ │ ├[通番]/ │ │└artifacts.zip │ ├[通番]/ │ │└metadata.gz │ └[通番]/ │ └job.log ├[日付]/ :(略)
GitLab Container Registryの格納場所
GitLab Container Registryの格納先は、GitLabサーバの/var/opt/gitlab/gitlab-rails/shared/registry/配下です。
Dockerイメージはblobs配下に実体(data)が格納されます。
repositories配下には1つのプロジェクトで1つのディレクトリが作成され、実体へのリンクとなるファイル(link)が格納されています。
└docker/ └registry/ └v2/ ├blobs/ │└sha256/ │ ├xx/ │ │└[Dockerイメージのレイヤを表す値1]/ │ │ └data │ ├yy/ │ │└[Dockerイメージのレイヤを表す値2]/ │ │ └data │ └zz/ │ └[Dockerイメージのレイヤを表す値3]/ │ └data └repositories/ └[グループ名]/ └[プロジェクト名]/ └[イメージ名]/ ├_layers/ │└sha256/ │ └[Dockerイメージのレイヤを表す値1]/ │ └link │ └[Dockerイメージのレイヤを表す値2]/ │ └link ├_manifests/ │└revisions/ │ └sha256/ │ └[Dockerイメージのレイヤを表す値3]/ │ └tags/ └_uploads/
構築手順
GitLab Runnerサーバのスペックの決定
GitLab Runnerの公式でも詳細な要件は記載されていないため、以下のような利用を想定しました。
GitLab RunnerにはSpecific RunnerとShared Runnerの2種類があり、前者はプロジェクト単位で、後者は全てのプロジェクト共通で利用します。
今回はShared Runnerを利用することとします。
- Shared Runner数:1
- Shared Runnerを利用するプロジェクト数:10
- プロジェクトで利用するDockerイメージ数:5
- Dockerイメージの容量:500MB
- Shared Runnerの同時実行数:1
- Shared Runnerのキャッシュの利用数:2
- Shared Runnerのキャッシュの容量:1GB
これらの利用想定から試算を行いました。
- Dockerイメージ総容量:10プロジェクト×5×500MB=25GB
- Shared Runnerのキャッシュの総容量:10プロジェクト×2×1GB=20GB
Shared Runnerのコンテナで利用するメモリ量については、検証時にJavaのビルドを実施した際に512MBではOut Of Memoryが発生したため1GBとしました。
これらの試算からGitLab Runnerサーバのスペックを以下としました。
OS | CPU | メモリ | ルートディスク | ボリューム |
---|---|---|---|---|
Ubuntu 20.04 LTS | 4コア | 4GB | 5GB | 60GB |
GitLab Container Registryの設定
GitLab Container Registryの設定は、GitLabの設定ファイルgitlab.rbに行います。GitLab Container Registryのポート番号として5050番を利用します。
listen_httpsをfalseとしているのは、リバースプロキシまではhttps通信ですが、リバースプロキシからGitLabの通信はhttpで行うためです。
registry_external_url 'https://<GitLabのFQDN>:5050'registry_nginx['listen_port'] = 5050registry_nginx['listen_https'] = false
GitLab Runnerの設定
GitLab Runnerのインストール
運用中のGitLabのバージョンは13.11.7のため、今回インストールするGitLab Runnerのバージョンは、マイナーバージョンまでが同じで最新となる13.11.0とします。
インストールの前にパッケージの最新化を行います。また、以降のインストール手順で必要なパッケージをインストールしておきます。
# export http_proxy="http://<プロキシサーバのFQDN>:XXXX"# export https_proxy=$http_proxy# apt-get upgrade# apt-get install \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release
GitLab Runnerをインストールします。
# curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | bash# apt-get install gitlab-runner=13.11.0
Dockerのインストール
Dockerをインストールします(インストール前に/var/lib/docker/に確保したボリュームをマウントする必要があります)。
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg# echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null# apt-get install docker-ce docker-ce-cli containerd.io
Dockerでプロキシの設定が必要な場合は、/etc/systemd/system/docker.service.d/http-proxy.confに設定します。GitLabとの通信にはプロキシの設定が必要ない場合はNO_PROXYの設定もしておきます。
[Service]Environment="HTTP_PROXY=http://<プロキシサーバのFQDN>:XXXX"Environment="HTTPS_PROXY=http://<プロキシサーバのFQDN>:XXXX"Environment="NO_PROXY=<GitLabのFQDN>:5050"
今回はリバースプロキシまではhttps通信を行うため/etc/docker/certs.d/配下と/etc/gitlab-runner/certs/配下にルート証明書を配置しています。
インストールが完了したら、サーバの再起動を行います。
GitLab Runnerの登録
GitLab RunnerをGitLabに登録します。
GitLabに管理者でログインし、管理者エリアを開きます。
概要メニューのRunnerを選択し、登録用のURLとトークンをメモしておきます。
GitLab Runnerサーバでgitlab-runner registerコマンドを実行します。"<"と">"で囲まれた部分は入力項目になります。
# gitlab-runner registerEnter the GitLab instance URL (for example, https://gitlab.com/):<登録用URL>Enter the registration token:<登録用トークン>Enter a description for the runner:[runner-test]: <Shared Runnerの説明>Enter tags for the runner (comma-separated):<Shared Runnerのタグ>Registering runner... succeeded runner=XXXXXXXEnter an executor: docker+machine, kubernetes, custom, docker, shell, ssh, virtualbox, docker-ssh, parallels, docker-ssh+machine:<利用するexecutor>Enter the default Docker image (for example, ruby:2.6):<指定がなかった場合に利用するDockerイメージ>Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
登録が完了すると、GitLabの先ほどの画面に以下のように登録したRunnerの情報が表示されます。
gitlab-runner registerコマンドで設定した内容は/etc/gitlab-runner/config.tomlに反映されます。このファイルに手動で以下の設定を追加します。
- GitLab Runnerでプロキシの設定が必要な場合、environmentでプロキシの設定を行い、GitLabとの通信にはプロキシの設定が必要ない場合はNO_PROXYの設定も行う。
- 検証時の結果からメモリ使用量を1GBに設定する。
:[[runners]]: environment = ["HTTP_PROXY=http://<プロキシサーバのFQDN>:XXXX", "HTTPS_PROXY=http://<プロキシサーバのFQDN>:XXXX", "NO_PROXY=<GitLabのFQDN>"]: [runners.docker]: memory = "1G":
設定ファイルに変更を加えた場合は再起動が必要になります。
# systemctl restart gitlab-runner
課題と対応
今回のGitLab Runnerの構築において課題となった項目と対応について紹介します。
同時実行数
GitLab Runnerはジョブの同時実行数を指定することができます。
上記のようなパイプラインを組んだ場合に、同時実行数が1の場合は[Job01]->[Job02-01]->[Job02-02]の順番で実行され、同時実行数が2の場合は[Job01]の後に[Job02-01]と[Job02-02]が同時に実行されます。
キャッシュを利用する際に、同時実行数を2にした場合[Job02-01]では利用できるが、[Job02-02]では利用できない問題が発生しました。
当初は同時実行数を2で運用しようとしていましたが、この問題のため同時実行数は1で運用することとしました。
GitLab Container RegistryとDocker Private Registryの比較
Dockerイメージの格納先としてDocker Private Registryとの比較検討も行いました。
GitLab Container Registry | Docker Private Registry | |
---|---|---|
アカウント認証 | GitLabのログインで認証が可能 | 別途認証機構の構築が必要 |
アクセス権制御 | プロジェクトごとにアクセス権の制御が可能 | GitLabのプロジェクトと同等のアクセス権の制御が可能か調査が必要 |
上記の結果から、GitLab Container Registryを採用しました。
おわりに
GitLab Runnerの構築が完了しましたので、次回はGitLab Runnerの利用方法について紹介します。
Footnotes
-
DockerイメージはDocker Hubからも取得できます。環境によりプロキシの設定が必要となります。 ↩