はじめに
前回の記事では、GitLabのコンテナレジストリについて2回にわたり解説しました。リポジトリに加えてレジストリを利用することで、ソースコードとコンテナイメージを一元管理できる点を確認しました。
今回からは、いよいよGitLabのCI/CD機能に焦点を当てます。CI/CDは、開発からデプロイまでの流れを自動化するための基本となる仕組みです。特にGitLabでは、リポジトリ・レジストリ・Runnerを組み合わせることで、シンプルながら強力なパイプラインを構築できます。
本記事では基本編として、ビルド、push、デプロイといったCI/CDにおける基本的な操作をジョブ単位で検証します。まずはシンプルなジョブを一つずつ動かし、パイプラインの仕組みに慣れることを目的とします。次回はこの内容を発展させ、複数のステージを組み合わせたマルチステージパイプラインを解説します。
ゴール
- GitLab Runnerを利用して簡単なCI/CDパイプラインを実行できるようにする
- ビルド→push→デプロイの一連の流れを体験する
- パイプラインの仕組みを理解する
前提条件
本記事では、あらかじめ以下の環境が構築されていることを前提とします。環境の詳細な構築手順については、環境構築編の記事をご参照ください。
- GitLab(Self-Managed版)
- Omnibusパッケージを利用してLinuxサーバーにインストールしたGitLabを利用します
- 無償版であるCommunity Editionを利用します
- Gitlab Runner
- OpenShiftクラスター上にデプロイ済みのRunnerを利用して、ジョブを実行します
- OpenShiftクラスター
- 閉域環境(インターネット非接続環境)に構築されています
- GitLab Container Registry
- コンテナイメージのpush / pullに利用します
これらの環境を利用して、GitLab CI/CDパイプラインを実行し、ビルドからデプロイまでの流れを検証できます。また、本記事では以下のようにテスト用のプロジェクトとブランチを用意して検証します。
テスト用プロジェクトの作成
- 任意の名称の動作確認用プロジェクトを新規作成します。例:test-project
- このプロジェクト配下で以降のジョブ検証を行います。
ブランチ構成
- 本記事では、各ジョブを個別のブランチで検証します。
- 基本の流れはbuild→push→deployですが、deployについては2つの方法(マニフェスト、Helm)をそれぞれ検証する構成にしています。実際の運用ではHelmを利用するケースが多いですが、まずはマニフェスト適用から試すとより理解が深まります。
- single-job-build
- 目的:Dockerイメージのビルド
- 主要ファイル:.gitlab-ci.yml、Dockerfile
- single-job-push
- 目的:既存イメージのタグ名リネーム&レジストリへのpush
- 主要ファイル:.gitlab-ci.yml
- single-job-deploy-manifest
- 目的:oc applyでマニフェストを適用
- 主要ファイル:.gitlab-ci.yml、deploy.yaml
- ※deployの検証パターン1
- single-job-deploy-helm
- 目的:Helmチャートを利用したデプロイ
- 主要ファイル:.gitlab-ci.yml、templates/ディレクトリ、values.yaml、Chart.yaml
- ※deployの検証パターン2
- single-job-build
各ブランチのファイル配置
single-job-build
test-project/
├─ .gitlab-ci.yml
├─ Dockerfile
└─ README.md
single-job-push
test-project/
├─ .gitlab-ci.yml
└─ README.md
single-job-deploy-manifest
test-project/
├─ .gitlab-ci.yml
├─ deploy.yaml
└─ README.md
single-job-deploy-helm
test-project/
├── Chart.yaml
├── templates
│ └── deployment.yaml
├── values.yaml
└─ README.md
用語説明
本記事で紹介するパイプラインを理解するために、CI/CDに関連する基本用語を簡単におさらいしておきます。
- Pipeline(パイプライン)
- GitLab CI/CDにおける一連の自動化処理の流れを指します。コードのビルド、テスト、デプロイといった複数の処理を順番にまとめたものです。
- Stage(ステージ)
- パイプラインを構成する処理のグループです。たとえば、「build」「test」「deploy」といった大まかな段階をステージとして定義します。ステージは順番に実行されます。
- Job(ジョブ)
- 各ステージの中で実行される具体的な処理の単位です。ビルドのジョブ、デプロイのジョブといった形で定義されます。ジョブは.gitlab-ci.ymlに記述します。
- Runner(ランナー)
- ジョブを実際に実行する役割を担うコンポーネントです。GitLab本体とは別に用意され、指定した環境(Docker、Kubernetes、VMなど)でジョブを動かします。本記事ではOpenShift上にデプロイしたRunnerを利用します。
これらの用語を理解しておくことで、以降のサンプルコードや解説がスムーズに読み進められます。
サンプルコード全体像
今回の検証では、以下の基本的な処理をそれぞれ独立したジョブとして実行し、CI/CDの流れを確認します。
- Build:Dockerイメージのビルド
- Push:GitLab Container Registryへのイメージpush
- Deploy(マニフェスト):Kubernetesマニフェストを適用してデプロイ
- Deploy(Helm):Helmチャートを利用してデプロイ
まずは全体像をイメージしやすいよう、シンプルな.gitlab-ci.ymlの例を示します。
stages:
- build
- push
- deploy
build_job:
stage: build
script:
- echo "Build Docker image"
push_job:
stage: push
script:
- echo "Push image to GitLab Registry"
deploy_job:
stage: deploy
script:
- echo "Deploy application"
上記は最小限の例であり、実際には各ジョブに詳細な処理(docker build / push、kubectl apply、helm installなど)を記述します。以降のセクションでは、各ジョブを個別に取り上げ、実際のコード例とともに動作を確認していきます。
ジョブ別解説
ここからは、実際に.gitlab-ci.yml にジョブを定義し、CI/CDの流れを確認していきます。
基本の流れはBuild → Push → Deployです。まずはビルドとpushを行い、その後のデプロイ方法として「マニフェスト適用」と「Helm」の2種類を検証します。
Buildジョブ
最初に実行するのはBuildジョブです。ソースコードからDockerイメージを作成し、次のステップで利用できる状態にします。
以下の例では、docker buildを実行してnginxのイメージを作成し、GitLabのレジストリに登録できるようにリポジトリ名をtestに変更したタグ(test:v1.0)を付与しています。
なお、本記事の環境ではOpenShiftクラスターはインターネットと接続できない閉域環境に配置しているため、ベースイメージとなるnginxのイメージはあらかじめGitLabのプロジェクト内のレジストリに格納してあります。

最小のnginxベースのサンプルです。
Dockerfile
FROM registry.gitlab.local.example.com/root/test-project/nginx:v1.0
ここでは Docker-in-Docker(dind)でビルドする例を示します。GitLab既定のレジストリ変数(CI_REGISTRY / CI_REGISTRY_IMAGE / CI_REGISTRY_USER / CI_REGISTRY_PASSWORD)を利用します。
このジョブを実行することで、アプリケーションのDockerイメージが生成されます。
.gitlab-ci.yml
stages:
- build
build_job:
stage: build
tags:
- devsecops-runner
image: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3
services:
- name: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3-dind
alias: docker
entrypoint: ["/bin/sh","-lc"]
command:
- >
cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt &&
update-ca-certificates &&
exec dockerd-entrypoint.sh
--tls=false
--host=unix:///var/run/docker.sock
--host=tcp://0.0.0.0:2375
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt
- update-ca-certificates || true
script:
- |
echo "Login to $CI_REGISTRY"
docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
docker build . --tag="${CI_REGISTRY_IMAGE}/test:v1.0"
上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。
プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。


パイプラインの実行に成功したことを確認できました。
ジョブのログを見ると、.gitlab-ci.yml内で指定した通り、イメージタグのリネームが行われています。
Pushジョブ
次に実行するのはPushジョブです。このジョブでは、あらかじめ検証用プロジェクトのレジストリにpushしておいた既存のnginx:v1.0イメージのリポジトリ名をtest:v1.0に付け替えて、GitLab Container Registryへpushします。
stages:
- push
push_only:
stage: push
tags: [devsecops-runner]
image: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3
services:
- name: registry.gitlab.local.example.com/root/registry-project/docker:28.3.3-dind
alias: docker
entrypoint: ["/bin/sh","-lc"]
command:
- >
cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt &&
update-ca-certificates &&
exec dockerd-entrypoint.sh
--tls=false
--host=unix:///var/run/docker.sock
--host=tcp://0.0.0.0:2375
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
SOURCE_IMAGE: registry.gitlab.local.example.com/root/test-project/nginx:v1.0
before_script:
- cp /etc/gitlab-runner/certs/gitlab.local.example.com.crt /usr/local/share/ca-certificates/ca.crt
- update-ca-certificates || true
script: |
echo "Login to $CI_REGISTRY"
docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
echo "Pull source image and push re-tagged"
docker pull "$SOURCE_IMAGE"
docker tag "$SOURCE_IMAGE" "${CI_REGISTRY_IMAGE}/test:v1.0"
docker push "${CI_REGISTRY_IMAGE}/test:v1.0"
このジョブでレジストリにpushしたイメージは、後続のデプロイジョブで利用できるようになります。
上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。
プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。


パイプラインは正常に実行されていることを確認できました。
プロジェクト画面のサイドメニューから[デプロイ] > [コンテナレジストリ]を選択して移動し、レジストリにpushしたイメージが格納されていることを確認します。

ジョブ内で指定した通り、test:v1.0というタグのイメージが保存されていることを確認できました。
Deployジョブ(マニフェスト)
ここからはデプロイの検証です。まずはKubernetesのマニフェストを直接oc applyで適用する方法を試します。
※本記事の環境ではコンテナ基盤としてOpenShiftを利用しているため、クラスター操作のCLIツールとしてocを利用しています。Kubernetes環境を利用している場合は、imageでkubectlを利用可能なイメージを指定し、scriptのデプロイコマンドはkubectl applyを使用してください。
まずはocコマンドを実行できるコンテナイメージをローカルでビルドして、GitLabのレジストリにpushします。
$ podman login registry.redhat.io
$ podman pull registry.redhat.io/openshift4/ose-cli-rhel9:v4.18
$ podman login registry.gitlab.local.example.com
$ podman tag registry.redhat.io/openshift4/ose-cli-rhel9:v4.18 \
registry.gitlab.local.example.com/root/test-project/oc:4.18
$ podman push registry.gitlab.local.example.com/root/test-project/oc:4.18

デプロイテスト用のマニフェストファイルです。このファイルを作成したデプロイテスト用のブランチにpushしておきます。
deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: gitlab-runner-test
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
serviceAccountName: gitlab-runner-sa
containers:
- name: nginx
image: registry.gitlab.local.example.com/root/test-project/test:v1.0
imagePullPolicy: IfNotPresent
.gitlab-ci.yml
stages:
- deploy
deploy_manifest:
stage: deploy
tags: [devsecops-runner]
image: registry.gitlab.local.example.com/root/test-project/oc:4.18
before_script:
- oc whoami
script: |
set -euo pipefail
oc apply -f deploy.yaml
このジョブを実行すると、deploy.yamlに記載されたリソースがOpenShiftクラスター上に作成されます。
上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。
プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。

ジョブの実行が成功していることを確認できました。
続いて、クラスターにPodがデプロイされていることを確認します。
deploy.yamlで指定した通り、myappという名前のPodが起動していることを確認できました。
$ oc get pod -n gitlab-runner-test -l app=myapp
NAME READY STATUS RESTARTS AGE
myapp-7d45468b7-24mwn 1/1 Running 0 69m
Deployジョブ(Helm)
最後にHelmを使ったデプロイ方法です。Helmを利用することで、複数のマニフェストをまとめて管理することができ、環境ごとの設定差分を柔軟に扱うことができます。
今回の検証では、以下のテスト用のHelmチャートを利用します。
先ほどと同様に、プロジェクト上にHelmテスト用のブランチを作成してHelmチャートを作成したブランチにテスト用のチャートをpushしておきます。

Podが参照するイメージは、Pushジョブでpushしたイメージを利用します。
Chart.yaml
apiVersion: v2
name: single-job-deploy-helm-chart
description: A Helm chart for a single deployment job
type: application
version: 0.1.0
appVersion: "1.0"
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: single-job-helm-deploy
labels:
app: helm-test-app
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: helm-test-app
template:
metadata:
labels:
app: helm-test-app
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end}}
containers:
- name: nginx
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
values.yaml
replicaCount: 1
image:
repository: "registry.gitlab.local.example.com/root/test-project/test"
pullPolicy: IfNotPresent
tag: "v1.0"
imagePullSecrets:
- name: gitlab-registry-secret
Helmチャートをデプロイできるようにするため、Helm CLIが利用できるジョブPod用のイメージをプロジェクト内のレジストリに格納します。
$ podman pull docker.io/alpine/helm:3
$ podman tag docker.io/alpine/helm:3 \
registry.gitlab.local.example.com/root/test-project/helm:3
$ podman push registry.gitlab.local.example.com/root/test-project/helm:3

.gitlab-ci.yml
stages: [deploy]
deploy_helm:
stage: deploy
tags: [devsecops-runner]
image: registry.gitlab.local.example.com/root/test-project/helm:3
variables:
RELEASE_NAME: mynginx
NAMESPACE: gitlab-runner-test
before_script:
- |
if [ -n "${KUBECONFIG_DATA:-}" ]; then
mkdir -p ~/.kube
echo "$KUBECONFIG_DATA" | base64 -d > ~/.kube/config
export KUBECONFIG="$HOME/.kube/config"
fi
- helm version
script: |
set -euo pipefail
helm upgrade --install "$RELEASE_NAME" ./chart \
--namespace "$NAMESPACE" \
-f ./values.yaml \
--wait --timeout 180s
helm -n "$NAMESPACE" status "$RELEASE_NAME"
Helmを利用したデプロイでは、再実行時もupgrade –installにより差分更新されるため、継続的デプロイ(CD)の仕組みとして有効です。
上記のサンプルコードをGitLabのプロジェクトにpushすると、pushをトリガーとしてパイプラインが起動します。
プロジェクト画面のサイドメニューから[ビルド] > [パイプライン]を選択して移動し、パイプラインの実行結果を確認します。


ジョブの実行に成功していることを確認できました。
続いて、クラスターにPodがデプロイされていることを確認します。
templates/deployment.yamlで指定した通り、helm-test-appという名前のPodが起動していることを確認できました。
$ oc get pod -n gitlab-runner-test -l app=helm-test-app
NAME READY STATUS RESTARTS AGE
single-job-helm-deploy-6bdbc56bbf-58nv8 1/1 Running 0 68m
まとめ
本記事では、GitLab CI/CDの基本的な使い方を確認するために、ビルド→push→デプロイという一連の流れをジョブ単位で検証しました。
- BuildジョブでDockerイメージを作成
- PushジョブでGitLabコンテナレジストリへ登録
- DeployジョブでOpenShiftクラスターにデプロイ(マニフェスト / Helm)
ジョブを分けて検証することで、CI/CDの各ステップを個別に把握でき、トラブル発生時の切り分けや理解の定着に役立ちます。これは、後に複数のステージを組み合わせたパイプラインを設計する際の基盤となります。
次回は、今回学んだ各ジョブを組み合わせてマルチステージパイプラインを構築したり、パイプラインの実行管理をする方法について解説します。これにより、より実践的なCI/CDパイプラインを設計できるようになります。