はじめに
前回は、GitLabのプロジェクトについて解説しました。GitLabのプロジェクトは、ソースコード管理を中心に、イシュー管理、CI/CD、Wiki、セキュリティまでをまとめて扱うことができます。これにより、開発プロセス全体を効率化し、ツール間の切り替えや管理の手間をなくすことができます。
今回は、その中でもGitLabのCI/CD(継続的インテグレーション・継続的デリバリー)に焦点を当てます。CI/CDは、開発プロセスを自動化し、生産性を大きく向上させる方法です。まずは、CI/CDの基本を解説し、GitLab Runnerのインストールとセットアップを行います。そしてnginxのコンテナイメージを自動でビルド・プッシュ・デプロイする簡単なパイプラインを構築するまでを解説します。
Kubernetesクラスターとの連携をするため、以下を前提としています。本記事では minikube を利用します。
- Dockerやコンテナの基本操作を理解していること
- Kubernetesの基本的な概念(Pod, Deployment, Namespaceなど)を知っていること
- kubectl コマンドを実行できる環境があること
GitLab CI/CDの概要
CI/CD(Continuous Integration / Continuous Delivery & Deployment)は、継続的インテグレーション(CI)と継続的デリバリー(CD)の2つの概念から成り立っています。
- 継続的インテグレーション (CI):開発者がコードをリポジトリにマージするプロセスです。マージされるたびに自動的にビルドとテストが実行され、バグを早期に発見できます。これにより、コードの品質を常に高いレベルに保つことができます。
- 継続的デリバリー (CD):テスト済みのコード変更を、いつでも環境にデプロイできる状態に保つプロセスです。手動でデプロイすることもできますが、継続的デプロイメントでは、すべてのテストがパスしたら自動的にデプロイが行われます。
GitLabは、このCI/CDワークフローを標準機能としてサポートしています。「.gitlab-ci.yml」という設定ファイルを作成すれば、ビルド、テスト、デプロイなどのパイプラインを簡単に定義できます。
.gitlab-ci.ymlによって自動化されたタスク(ジョブ)は、GitLab Runnerというエージェントによって実行されます。GitLab Runnerは、パイプラインの指示を受け取り、実際の作業を行う実行役です。GitLab Runnerは、GitLabとは別にインストール・登録する必要があります。
KubernetesとGitLabの連携
GitLab Runnerは、Dockerや仮想マシンなど様々な実行環境(Executor)でジョブを実行できますが、今回はKubernetesとの連携をとりあげます。Kubernetes Executortは、ジョブをKubernetesのPodとして実行する仕組みのことです。さらに詳しく知りたい人は以下のURLを参考にしてみてください。
https://docs.gitlab.com/runner/executors/kubernetes/
GitLabとKubernetesを連携させるには、Kubernetesクラスター上にGitLab Runnerをデプロイします。GitLab、GitLab Runner、Job Pod、Kubernetesクラスターの関係性は以下のようになっています。
- GitLabのパイプライン実行:ユーザーがGitLab上でコミットやマージを行うと、CI/CDパイプラインが自動的にトリガーされます。
- GitLab Runnerの起動:パイプライン内のジョブが、Kubernetesクラスター上で動作しているGitLab Runnerに割り当てられます。
- Job Podの生成:GitLab RunnerはKubernetes Executorを使用し、ジョブを実行するために一時的なJob PodをKubernetesクラスター内に自動生成します。Job Podはジョブ完了後に削除されます。
- コマンドの実行:このJob Pod内で、パイプラインのジョブに定義されたコマンド(例:kubectl apply)が実行されます。
- コンテナのデプロイ:コマンドの実行により、アプリケーションのコンテナがKubernetesクラスター内にデプロイされます。
この連携により、kubectlコマンドなどを手動で実行することなく、GitLab上でデプロイを自動化できます。GitLabとKubernetesを連携させるために、GitLab RunnerをKubernetesに導入していきます。
環境情報
- GitLab:SaaS版
- minikube:v1.36.0
- Kubernetes Server Version: v1.33.1
- kubectl:v1.33.1
- helm:v3.18.6
GitLab Runnerを導入してみる
それでは、ローカル環境に簡単にKubernetesクラスターを構築できるminikubeを使って、GitLab Runnerを導入してみましょう。minikubeはインストール済みであることを前提とします。
GitLabで今回使用するプロジェクトを作成します。プロジェクトの作成方法は第4回、第7回を参考にしてみてください。今回はブランチの考慮は行わないのでmainブランチのみで検証していきます。
GitLabでプロジェクトのCI/CD設定からRunnerの登録トークンを取得します。
メニューの 設定>CI/CD から[プロジェクトRunnerを作成]をクリックします。
タグを入力して、あとはデフォルトで[Runnerを作成]をクリックします。
タグは.gitlab-ci.ymlでGitLab CI/CDでジョブを実行するRunnerを選択するために使用します。
表示されたRunner認証トークン(glt-xxxxxxx)をメモします。
GItLabはこのページのままで大丈夫です。
それでは、GItLab Runnerを導入してみましょう。
ローカル端末で操作します。
minikubeを起動します。
$ minikube start
Kubernetesにアプリケーションをデプロイする標準的なツールであるHelmをインストールします。
$ sudo snap install helm --classic
インストールが完了したら、Helmが正しくインストールされたか確認します。
バージョン情報が表示されれば、インストールは成功です。
$ helm version
version.BuildInfo{Version:"v3.18.6", GitCommit:"b76a950f6835474e0906b96c9ec68a2eff3a6430", GitTreeState:"clean", GoVersion:"go1.24.6"}
GItLab Runnerをデプロイするネームスペースを作成します。
$ kubectl create namespace gitlab
GItLab Runnerをデプロイします。
$ helm install gitlab-runner gitlab/gitlab-runner -f values.yaml -n gitlab
values.yaml
gitlabUrl: https://gitlab.com
runnerToken: "<取得したRunner認証トークン>"
rbac:
create: true
clusterWideAccess: true
rules:
- resources: ["configmaps", "pods", "pods/attach", "secrets", "services"]
verbs: ["get", "list", "watch", "create", "patch", "delete"]
- resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
- resources: ["serviceAccounts"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create", "patch", "delete"]
serviceAccount:
create: true
name: "gitlab-runner"
runners:
config: |
[[runners]]
name = "Kubernetes GitLab Runner"
executor = "kubernetes"
shell = "bash"
[runners.kubernetes]
terminationGracePeriodSeconds = 5
privileged = true
allow_privilege_escalation = true
image = "alpine"
少ししてから、GItLab Runner Podがデプロイされているか確認します。
$ kubectl get pod -n gitlab
NAME READY STATUS RESTARTS AGE
gitlab-runner-85d8fbcdc4-94ph2 1/1 Running 0 94s
GitLab Runnerがローカルのminikubeに接続するためのkubeconfigの設定をします。
$ kubectl config view --minify --flatten --raw > kubeconfig-inline
$ sed -i 's|https://127.0.0.1:32771|https://kubernetes.default.svc|' kubeconfig-inline
以下で表示されるbase64エンコードしたkubeconfigの文字列は後ほど使用するので、記録しておきます。
$ cat kubeconfig-inline | base64 -w 0
GitLabの画面に戻ります。
[Runnerを表示する]をクリックし、「アサインされたプロジェクト」のRunnerが図のように緑のオンラインになっていたら連携完了です。
Ci/CDの環境変数設定をします。ここで先ほど記録したbase64エンコードしたkubeconfigの文字列を環境変数に設定します。
メニューの 設定>CI/CD から変数を開きます。CI/CD変数の[変数を追加]をクリックします。
キーと値を入力して、[変数を追加]をクリックします。
kubeconfigは機密情報なのでマスク(非可視化)しています。
-
キー:KUBECONFIG_BASE64
-
値:<先ほど記録したbase64エンコードしたkubeconfigの文字列>
追加したCI/CD変数が表示されていたら完了です。
.gitlab-ci.ymlについて
.gitlab-ci.ymlは、GitLab CI/CDパイプラインを定義するためのYAMLファイルです。プロジェクトのルートディレクトリに配置します。
このファイルでは、さまざまなキーワードや要素を使って柔軟なパイプライン定義が可能です。
主な基本構造と記述方法
- stages:パイプラインの実行順序を定義します。例えば、build、test、deployといったステージを設定できます。各ジョブはstageでそれぞれ自身が所属するステージを指定します。
- job:具体的に何を実行するかを定義します。script要素でコマンド記述が必須です。トップレベルのキー名がそのままジョブ名になります。例えば、build_jobの場合はジョブ名がbuild_jobになります。
stagesで順序を定義し、ジョブごとに所属するステージを指定することで一連の処理フローを構築します。stagesで定義した順番に、同一ステージのジョブは並列、次のステージのジョブは順次実行されます。
代表的な機能
- tags:使用するGitLab Runnerを指定します。
- image:ジョブを実行するためのDockerイメージを指定します。Docker / Kubernetes Executorに限ります。
- services:テストやビルド環境に必要な外部サービス(例:DB)用の補助コンテナを追加します。Docker / Kubernetes Executorに限ります。
- script:ジョブで実行するコマンドを記述します。
- before_script / after_script:全ジョブや特定ジョブ実行前後の共通コマンドを定義できます。
- variables:環境変数やパイプライン用の変数を定義できます。
- rules:ブランチ・タグ・パイプラインなどの条件分岐でジョブの起動制御が可能です。mainブランチへpushされた時だけデプロイを実行する場合などに使用します。
参考:https://docs.gitlab.com/ci/yaml/
コンテナイメージのビルド・プッシュ・デプロイをしてみる
CI/CDパイプラインを構築し、コンテナイメージのビルドからデプロイまでを自動化します。このプロセスは通常、複数のステージに分かれます。
- build-and-push ステージ:ここでは、アプリケーションのコンテナイメージをDockerでビルドします。ビルドしたイメージは、GitLabのコンテナレジストリにタグを付けてプッシュします。これにより、作成したイメージがCI/CDパイプラインで利用可能になります。
- deploy ステージ:build-and-push ステージでビルド・プッシュされたコンテナイメージを使用して、ローカル環境のminikubeにアプリケーションをデプロイします。このステージでは、kubectlを使って、デプロイメントの更新やサービスの公開を行います。
はじめに、Gitリポジトリをローカルにクローンします。
$ git clone <GitリポジトリのURL>
以下のディレクトリ構造でファイルを作成していきます。
.
├── .gitlab-ci.yml
├── Dockerfile
├── README.md
├── kubernetes
│ └── deployment.yaml.tpl
└── src
├── html
│ └── index.html
└── nginx.conf
gitlab-ci.ymlにDockerビルドのステップを追加します。
この例では、docker:dind(Docker in Docker)サービスを使ってDockerコマンドを実行し、コンテナイメージのビルドとレジストリへのプッシュを行っています。実運用では権限やネットワーク設定に注意が必要ですが、ここでは動作確認のための簡易構成として使います。
.gitlab-ci.yml
stages:
- build-and-push
- deploy
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
K8S_NAMESPACE: gitlab
build-and-push:
tags:
- git-tutorial-8
stage: build-and-push
image: docker:28.4.0-alpine3.22
services:
- docker:28.4.0-dind-alpine3.22
before_script:
- until docker info; do sleep 1; done;
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
deploy-to-minikube:
tags:
- git-tutorial-8
stage: deploy
image:
name: bitnami/kubectl:1.33.1
entrypoint: [""]
before_script:
- echo "$KUBECONFIG_BASE64" | base64 -d > kubeconfig
- export KUBECONFIG=$(pwd)/kubeconfig
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
script:
- kubectl config set-context --current --namespace=$K8S_NAMESPACE
- kubectl create secret docker-registry regcred --docker-server=https://registry.gitlab.com --docker-username=$CI_REGISTRY_USER --docker-password=$CI_REGISTRY_PASSWORD -n $K8S_NAMESPACE || true
- envsubst < kubernetes/deployment.yaml.tpl > deployment.yaml
- kubectl apply -f deployment.yaml
Dockerfile
FROM nginx:alpine
COPY src/nginx.conf /etc/nginx/nginx.conf
COPY src/html/index.html /usr/share/nginx/html/index.html
CMD ["nginx", "-g", "daemon off;", "-p", "8080"]
src/html/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>GitLab CI/CD Demo</title>
<style>
body {
font-family: sans-serif;
background-color: #f0f0f0;
text-align: center;
padding-top: 50px;
}
</style>
</head>
<body>
<h1>Deployment Successful!</h1>
<p>This is an application deployed via the GitLab CI/CD pipeline.</p>
</body>
</html>
src/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
charset utf-8;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
server {
listen 8080;
location / {
alias /usr/share/nginx/html;
index index.html index.htm;
default_type text/html;
charset utf-8;
}
}
include /etc/nginx/conf.d/*.conf;
}
kubernetes/deployment.yaml.tpl
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG} # GitLab CI/CD の環境変数
ports:
- containerPort: 8080
imagePullSecrets:
- name: regcred
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: LoadBalancer
ports:
- port: 8080
targetPort: 80
selector:
app: nginx
ファイルを作成したら、リモートリポジトリにプッシュします。
$ git add .
$ git commit -m "git-tutorial-8"
$ git push origin main
プッシュしたらメニューの ビルド>パイプライン から、リポジトリへのプッシュをトリガーにパイプラインが実行されていることを確認します。
ステータスが「成功」になっていたら完了です。
各ステージのステータスも確認しておきましょう。
パイプラインの下に表示されているコミットメッセージの下の数列をクリックします。
.gitlab-ci.ymlで定義した2つのステージが完了していることが確認できます。
それでは、ビルドしたコンテナイメージがプロジェクトのコンテナレジストリにプッシュされていることを確認しましょう。
メニューの デプロイ>コンテナレジストリ から、プロジェクトのコンテナレジストリを確認します。
コンテナイメージが表示されたら完了です。
次に、アプリケーションがデプロイされていることを確認しましょう。
ローカル端末で操作します。
デプロイしたネームスペースのリソースを確認します。
nginxのservice、deployment、podが作成されていることが確認できます。
$ kubectl get all -n gitlab
NAME READY STATUS RESTARTS AGE
pod/gitlab-runner-767f96f489-f2qsb 1/1 Running 0 13m
pod/nginx-deployment-7cdb8748c6-bbwlm 1/1 Running 0 12m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-service LoadBalancer 10.98.81.158 <pending> 8080:32740/TCP 12m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/gitlab-runner 1/1 1 1 13m
deployment.apps/nginx-deployment 1/1 1 1 12m
NAME DESIRED CURRENT READY AGE
replicaset.apps/gitlab-runner-767f96f489 1 1 1 13m
replicaset.apps/nginx-deployment-7cdb8748c6 1 1 1 12m
外部からminikube内のサービスにアクセスできるようにします。
$ minikube tunnel
ターミナルはそのままにして、ローカル端末のブラウザでhttp://127.0.0.1:8080/にアクセスします。
アプリケーションの画面が表示されたら完了です。
これで、Gitリポジトリにプッシュするだけで次の一連のワークフローを自動化することができました。
- コンテナイメージをビルド
- ビルドしたコンテナイメージをGitLabのプロジェクトのコンテナレジストリにプッシュ
- プッシュしたコンテナイメージを使用して、Kubernetesクラスターにアプリケーションをデプロイ
まとめ
今回は、CI/CDの基本から、GitLab Runnerの導入、そしてコンテナイメージのビルド・プッシュ・デプロイまで、一連のワークフローを解説しました。
この自動化されたプロセスにより、開発者はより頻繁にコードをリリースでき、サービスの改善サイクルを加速させることができます。
参考文献
http://xn--docs-u83c.gitlab.com/ci/