Introduction
自分用のCI(Continuous Integration)環境やコンテナレジストリが欲しいと思ったことはありませんか?
「ある」と思った人に向けて、今回はGitlab CIを使ったコンテナの自動ビルド環境&配布環境を作るのに何が必要なのか、順番に見ていきます。
コンテナのビルドに使用するKanikoは、Dockerデーモンに依存せずにDockerfile内のコマンドをユーザースペースで実行することで、標準的なKubernetesクラスタや特権無しのDockerデーモンでもコンテナをビルド出来るようにするツールです。
Gitlab runnerでDocker privilegedを使いたくないケースもありますからね。
GitlabとKanikoについての詳しい説明は公式ドキュメントを参照ください。
なお、このドキュメントに出てくるパスワードやトークンは文書を書くためだけに作られている例なので気にしないでください。
Environment
以下の2台の仮想マシンを準備します。どちらも Ubuntu 20.04のクラウドイメージ を使用します。
- qiita-gitlab: Gitlabをインストールするノード
- qiita-runner: Gitlab runnerをインストールするノード
Install Gitlab omunibus
まずは https://about.gitlab.com/install/#ubuntu に沿ってGitlab-EEをインストールします。
Ubuntu 20.04のクラウドイメージは最初から前提となるパッケージが揃っているので、少し省略します。
作業は qiita-gitlab
ノードで実施します。
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
export EXTERNAL_URL="http://qiita-gitlab.example.com"
export GITLAB_ROOT_PASSWORD="ILTTqnDv4iBxKwt5UWzc/rOeEkk1AO/6QzjfK+cS0Pw="
sudo -E apt-get install gitlab-ee
http://qiita-gitlab.example.com
はこの文書のためのURLです。
お使いの環境に合わせて変更してください。
変更しない場合は各ノードのhostsに qiita-gitlab.example.com
を設定してアクセスできるようにしましょう。(手順外)
また EXTERNAL_URL
を https://...
で作成すると自動的にLet’s Encryptで証明書を取得するサポート機能があるようですが、今回は使用しません。
ログイン出来たらOKです。
で、ログイン。
Gitlabのインストールは問題なさそうです。
パスワードの要件 は結構厳しく、適合しないとrootユーザーが作成されません。
rootユーザーが作成されなかった場合は、別途 /etc/gitlab.rb
の gitlab_rails['initial_root_password']
を編集して gitlab-ctl reconfigure
を実行するなどして対応します。
インストールされたバージョンは以下の通りです。
$ sudo gitlab-rake gitlab:env:info
System information
System: Ubuntu 20.04
Proxy: no
Current User: git
Using RVM: no
Ruby Version: 3.0.5p211
Gem Version: 3.2.33
Bundler Version:2.3.15
Rake Version: 13.0.6
Redis Version: 6.2.8
Sidekiq Version:6.5.7
Go Version: unknown
GitLab information
Version: 15.10.0-ee
Revision: defe6e7f882
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 13.8
URL: http://qiita-gitlab.example.com
HTTP Clone URL: http://qiita-gitlab.example.com/some-group/some-project.git
SSH Clone URL: git@qiita-gitlab.example.com:some-group/some-project.git
Elasticsearch: no
Geo: no
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 14.18.0
Repository storages:
- default: unix:/var/opt/gitlab/gitaly/gitaly.socket
GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell
Create Gitlab project
それでは、適当なプロジェクトを作成しましょう。
まずは新規プロジェクトを選択。
Create blank projectを選択。
今回は gitlab-ci-sample
というプロジェクト名にしました。
初期のREADME.md作成はOFFにしましたが、説明の簡略化のためなのでどちらでも構いません。
これでプロジェクトが作成されて、以下の状態になりました。
Project settings
Gitlan runnerに登録するProject専用のRegistration keyを取得します。
Settings -> CI/CD を開いて、
Runnerを選択するとプロジェクト専用のregistration tokenが取得できます。
Gitlab runnerの登録時に、このトークンを使用するのでメモしておきます。
ついでに、デフォルトで有効になっているAuto DevOpsは、今回作成するCI設定範囲の分かりやすさのため無効にしておきます。
Install Gitlab runner
次はDocker用のGitlan runnerを準備しましょう。
作業は qiita-runner
ノードで実施します。
Install docker for gitlab-runner docker executor
まずは https://docs.docker.com/engine/install/ubuntu/ に沿ってDocker Engineをインストールします。
こちらも、Ubuntu 20.04のクラウドイメージは最初から前提となるパッケージが揃っているので、少し省略します。
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Dockerのバージョンは以下の通りです。
DockerはGitlab runnerでKanikoコンテナを動かすために使います。
$ sudo docker version
Client: Docker Engine - Community
Version: 23.0.2
API version: 1.42
Go version: go1.19.7
Git commit: 569dd73
Built: Mon Mar 27 16:16:18 2023
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 23.0.2
API version: 1.42 (minimum version 1.12)
Go version: go1.19.7
Git commit: 219f21b
Built: Mon Mar 27 16:16:18 2023
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.19
GitCommit: 1e1ea6e986c6c86565bc33d52e34b81b3e2bc71f
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d
docker-init:
Version: 0.19.0
GitCommit: de40ad0
Install gitlab-runner
次に https://docs.gitlab.com/runner/install/linux-repository.html#installing-gitlab-runner に沿ってGitlab runnerをインストールします。
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt-get install -y gitlab-runner
Gitlab runnerのVersionは以下の通りです。
$ gitlab-runner -v
Version: 15.10.0
Git revision: 456e3482
Git branch: 15-10-stable
GO version: go1.19.6
Built: 2023-03-17T13:42:46+0000
OS/Arch: linux/amd64
Register gitlab-runner as Project runner
このGitlab runnerは、今回作成したプロジェクト専用のRunnerとして登録します。
先ほど確認したプロジェクト専用のregistration tokenを --registration-token
オプションで指定します。
export GITLAB_PROJECT_TOKEN="GR1348941Kjym6XqzXhGzfczuoSNB"
sudo -E gitlab-runner register --non-interactive \
--url http://qiita-gitlab.example.com \
--registration-token $GITLAB_PROJECT_TOKEN \
--description "Qiita example" \
--tag-list "docker,qiita" \
--executor docker \
--docker-image alpine:latest
-
--executor
にdocker
を選択した場合は、デフォルトで使用されるコンテナを合わせて指定します。特に使う予定は無いので適当にalpine:latest
を設定しました。 -
--tag-list
は指定しなくても構いませんが、CI実行時にタグによって実行Runnerを使い分けることが出来るので、参考に付与しています。
以下のように、登録に成功したログが出ていればOKです。
Registering runner... succeeded runner=GR1348941Kjym6Xqz
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
GitlabのWeb UIでも、Gitlab runnerが登録されていることが確認できます。
Add sample files to gitlab project
それでは、今回作成したプロジェクトに、サンプルコードを登録しましょう。
Gitlab-CIの動作を見るため、以下の2つのファイルを作成します。
- Dockerfile: 自動ビルドしたいサンプルコンテナ
- index.html: 適当な文字列を表示するHTMLファイル
Dockerfile
FROM nginx:latest
COPY index.html /usr/share/nginx/html/index.html
index.html
<!DOCTYPE html><meta charset=utf-8>
<title>Hello world</title>
<body>Gitlab-CI<body>
gitコマンドを使っても良いのですが、今回はWebから全部やりましょう。
リポジトリのページから New file
を選択して、上記のファイルを2つ登録します。
Webエディタが起動するので、新しいファイルを作成して内容を記入します。
できたらCommitします。
Setup Gitlab container registry
更に、今回ビルドしたコンテナを保存する先を作成します。
Gitlabには GitLab Container Registry 機能があるので、コンテナイメージをホスティングできるのです。
今回は自分で作成したCAと証明書を使って Configure Container Registry under its own domain に沿って設定します。
作業は qiita-gitlab
ノードで実施します。
Let’s Encryptなど正規の証明書が取得できる場合は読み飛ばしてください。
また、証明書周りの説明はGitlab CIの本筋から少しずれるため、コマンドを貼り付けるのみでサッと進めます。
Create CA
まずはCAを作成します。国や都市名は適当です。
CNはこの後作成する証明書に合わせて example.com
にします。
$ mkdir registry_certs
$ cd registry_certs/
$ openssl rand -writerand ~/.rnd
$ openssl genrsa -out ca.key 4096
$ openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=JP/ST=tokyo/L=example/O=gitlab/CN=example.com" \
-key ca.key -out ca.crt
Create self-signed certification
作成したCAを元にコンテナレジストリ用の証明書を作成します。
FQDNはGitlabのデフォルトに合わせて registry.example.com
にします。
$ openssl genrsa -out registry.example.com.key 4096
$ openssl req -sha512 -new \
-subj "/C=JP/ST=SDI/L=lab/O=SDI/CN=registry.example.com" \
-key registry.example.com.key -out registry.example.com.csr
$ cat <<_EOL_ > v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=registry.example.com
DNS.2=registry
DNS.3=gitlab
_EOL_
$ openssl x509 -req -sha512 -days 3650 -extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in registry.example.com.csr -out registry.example.com.crt
Enable Gitlab container registry
最後に、作成したCAと証明書をGitlabの設定ディレクトリに格納して、コンテナレジストリ機能を有効にします。
$ sudo cp ca.crt /etc/gitlab/trusted-certs/
$ sudo mkdir /etc/gitlab/ssl
$ sudo cp registry.example.com.crt /etc/gitlab/ssl/
$ sudo cp registry.example.com.key /etc/gitlab/ssl/
$ sudo sed -i -e "s|# registry_external_url 'https://registry.example.com'|registry_external_url 'https://registry.example.com'|" /etc/gitlab/gitlab.rb
$ sudo gitlab-ctl reconfigure
設定が完了すると /opt/gitlab/embedded/ssl/certs
にCAのシンボリックリンクが作成されます。
$ ls -l /opt/gitlab/embedded/ssl/certs
total 224
lrwxrwxrwx 1 root root 32 Mar 28 13:03 5a842aa3.0 -> /etc/gitlab/trusted-certs/ca.crt
-rw-r--r-- 1 root root 147 Mar 28 12:04 README
-rw-r--r-- 1 root root 224369 Mar 21 10:30 cacert.pem
Setting Gitlab runner host to use seif-signed ceritication
Gitlab runnerにも ca.crt
を(SFTPなどで)コピーして、CAを登録しておきます。
CAの登録コマンドは、Ubuntuの場合は以下のようになります。
$ sudo cp ca.crt /usr/local/share/ca-certificates/registry.example.com.crt
$ sudo update-ca-certificates
$ sudo systemctl restart docker
また、Gitlab runnerのコンテナ内部からはgitリポジトリなる qiita-gitlab.example.com
とコンテナレジストリとなる registry.example.com
にアクセスできる必要があります。
一般的にはDNSで解決すべきものですが、ここでは手順通りの設定で動くように /etc/gitlab-runner/config.toml
に extra_hosts
の設定を加えておきましょう。
--- config.toml.old 2023-03-28 13:40:40.111871790 +0000
+++ config.toml 2023-03-28 13:40:09.031921214 +0000
@@ -24,3 +24,4 @@
disable_cache = false
volumes = ["/cache"]
shm_size = 0
+ extra_hosts = ["qiita-gitlab.example.com:192.168.122.246","registry.example.com:192.168.122.246"]
設定を変更したら sudo gitlab-runner restart
で再起動して、設定を適用します。
Create .gitlab-ci.yml
Start gitlab pipelines
まだ何もGitlab CIの設定を入れていないので、作成画面から作業を始めます。
CI/CD -> pipeline -> Editor を開きます。
"Configure pipelines" を選択します。
デフォルトの設定が書かれた .gitlab-ci.yml
の入力画面が表示されるので、内容を決めていきましょう。
Edit .gitlan-ci.yml to use kaniko
それでは、いよいよKanikoを使用してコンテナをビルドするための .gitlab-ci.yml
を作成します。
今回はサンプルで作成したDockerfileを使って、自前のコンテナを自動ビルドするように設定します。
KanikoはDockerfileを読み込むものの、Gitlab runnerがDockerの特権を持たなくてもコンテナのビルドが出来るため、比較的安全かつDocker buildによるゴミが残りにくいという利点があります。
.gitlab-ci.yml
の作成にあたっては、GitlabがKanikoを利用する場合の文書 Use kaniko to build Docker images を参考にします。
まずは Building a Docker image with kaniko に書かれているサンプルを丸ごとそのままコピーします。
ただ、それだけでは少し不足があるので2点ほど追記します。
- 登録したGitlab runnerには
docker
というタグを付けてあるので、それを先頭に付与します- 動作上は必須ではありませんが、今回登録したRunnerをタグで明示しておくと、Gitlab runnerが複数存在する時に区別しやすくなります
- Gitlab runnerがコンテナレジストリの証明書エラーとならないよう
$REGISTRY_CERT
(※登録方法は後述)をkanikoコンテナの中に設置します-
Using a registry with a custom certificate を見ると
/kaniko/ssl/certs/additional-ca-cert-bundle.crt
にCAを追加する手順が記載されていますが、そのままだと.gitlab-ci.yml
が長くなってしまうので変数に置き換えています
-
Using a registry with a custom certificate を見ると
default:
tags:
- docker
build:
stage: build
image:
name: gcr.io/kaniko-project/executor:v1.9.0-debug
entrypoint: [""]
script:
- echo "$REGISTRY_CERT" >> /kaniko/ssl/certs/additional-ca-cert-bundle.crt
- /kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
rules:
- if: $CI_COMMIT_TAG
あとは "Commit changes" で確定して、mainブランチに .gitlab-ci.yml
を追加しましょう。
if: $CI_COMMIT_TAG
と記述しているので、コミットにタグを付けるとCIが起動するようになりました。
kanikoコンテナの指定に -debug
が付与されていることを奇妙に思うかもしれませんが、GitlabではCI/CDの動作にシェルが必要になるため、これを外すと動作しなくなる可能性があります。
The kaniko debug image is recommended (gcr.io/kaniko-project/executor:debug) because it has a shell, and a shell is required for an image to be used with GitLab CI/CD.
Building a Docker image with kaniko
Docs feedback: Building a Docker image with kaniko: debug image required?
Add REGISTRY_CERT
variable
先ほど設定した REGISTRY_CERT
は、今のままでは空っぽなので Settings -> CI/CD -> Variables から、作成した ca.crt
の内容をCIの変数で展開できるようにしておきます。
参考にした Using a registry with a custom certificate の記述では .gitlab-ci.yml
に直接記載していましたが、今回はVariablesを使用しました。
Gitlabで使う共通のコンテナレジストリ用のCAなので、Gitlab全体(Adminコントロールパネル)のVariableに設定しても良いでしょう。
設定すると、下図のようになります。
CAをkanikoが参照できるように設定できていないと、以下のようなエラーが出ます。
error checking push permissions -- make sure you entered the correct tag name, and that you are authenticated correctly, and try again: checking push permission for "registry.example.com/gitlab-instance-6dfd6d5f/gitlab-ci-sample:sample": creating push check transport for registry.example.com failed: Get "https://registry.example.com/v2/": x509: certificate signed by unknown authority
Check results
Gitlab pipeline
まずはコミットにTagを付けてみましょう。
適当なタグを設定します。
Tagを付けると、それに連動してPipelineが起動しました。
Passedになったので、ジョブ結果を見てみます。
以下のようなログが確認できたので、コンテナレジストリへの登録が成功しているようです。
INFO[0009] Pushing image to registry.example.com/gitlab-instance-6dfd6d5f/gitlab-ci-sample:sample
Gitlab container registry
早速 Packages and registries -> Container Registry を見に行くと、コンテナが登録されていました。
中には、設定したタグのコンテナが登録されています。
やりました!コンテナの自動ビルド達成です。
Use Gitlab container registry
では、コンテナレジストリからコンテナを取得して実行してみましょう。
Gitlab runnerにはDockerをインストール済みなので、そこで実行してみます。
ログインに使用するアカウントは root
のものを使用します。
$ sudo -i
# docker login registry.example.com
Username: root
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
# docker pull registry.example.com/gitlab-instance-6dfd6d5f/gitlab-ci-sample:sample
sample: Pulling from gitlab-instance-6dfd6d5f/gitlab-ci-sample
f1f26f570256: Already exists
84181e80d10e: Already exists
1ff0f94a8007: Already exists
d776269cad10: Already exists
e9427fcfa864: Already exists
d4ceccbfc269: Already exists
18059bb02c01: Pull complete
Digest: sha256:6be901ad0d7b7a7b6720bd9a5dcd333bd5d60f977c58481ea7d01d5f42be1359
Status: Downloaded newer image for registry.example.com/gitlab-instance-6dfd6d5f/gitlab-ci-sample:sample
registry.example.com/gitlab-instance-6dfd6d5f/gitlab-ci-sample:sample
なお registry.example.com
は例示用のURLですが、アクセスできないと docker login
に失敗するのでhostsファイルに追記しています。(手順外)
無事に取得できたようです。
作成したコンテナは単なるnginxに静的HTMLファイルを置いただけなので、8000/TCPで動かしてみましょう。
# docker run --rm --name sample -p 8000:80 -d registry.example.com/gitlab-instance-6dfd6d5f/gitlab-ci-sample:sample
ad1f2438b8ae3e90e487d73b0d6c2d5982cb6200971cea596f8a08e129711403
# curl localhost:8000
<!DOCTYPE html><meta charset=utf-8>
<title>Hello world</title>
<body>Gitlab-CI<body>
Gitlab CIとkanikoを使用して、コンテナの自動ビルドとコンテナの配布環境が整いました。
Closing...
Gitlabの構築からコンテナの自動ビルド設定、Gitlabのコンテナレジストリを使ったコンテナの配布環境作成までの道のりを駆け足で辿ってみました。
Gitlabはインストールやアップデートが比較的容易で、無償の範囲でも多くの使い方ができるので、是非活用したいアプリケーションです。
今回は外部環境の依存を避けるために少々寄り道と言いますか、注釈が多くなってしまいました。
やはり、出来るだけDNSや証明書の準備が容易な環境で構成したいですね。
近頃ではGithub actionsが強いので組織内にGitlabを置きたいという話も徐々に減っていると思いますが、上手く棲み分けできると良いのかなと思います。