CI/CDジョブとRunnerの高度な設定
(トップページはこちら) - (GitLab Runnerを始める)
GitLab CI/CDを効果的に運用するには、ジョブの仕組みとRunnerの設定を深く理解することが重要です。本記事では、実務で直面する課題を解決するための知識を、基本概念から実践的な設定パターンまで体系的に解説します。
1. CI/CDジョブの基本概念
1.1 ジョブとは何か
CI/CDジョブは、GitLab CI/CDパイプラインの基本要素です。各ジョブはRunnerで実行され、以下の特徴を持ちます。
- 独立性: 他のジョブと独立して実行される
- 再現性: 同じ環境で何度でも実行できる
- トレーサビリティ: 完全な実行ログが記録される
build-job:
script:
- bundle install
- bundle exec rake build
test-job:
script:
- bundle exec rspec
1.2 ジョブのライフサイクル
ジョブは以下のステータスを遷移します。
重要なステータス
-
pending: Runnerを待機中。この状態が長い場合、Runnerの不足を示唆 -
preparing: 環境準備中。Docker imageのpullなどが行われる -
running: 実際のスクリプトを実行中 -
canceling: キャンセル処理中。after_scriptが実行される
1.3 ジョブの制御
再試行の仕組み
ジョブの再試行は、一時的な障害からの回復に有効です。
test-job:
script:
- bundle exec rspec
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
再試行時の動作:
- 新しいジョブIDで実行される
- 元のジョブと同じ変数・パラメータを使用
- アーティファクトは新規作成される
- 再試行を実行したユーザーに関連付けられる
キャンセルの動作(GitLab Runner 16.10以降)
強制キャンセル(GitLab 17.11以降)
応答しないジョブや、即座に停止したい場合に使用します。
- ジョブトークンが即座に無効化される
-
after_scriptは実行されない - Runnerは即座にジョブを中止する
2. GitLab Runnerの基本設定
2.1 設定ファイルの構造
config.tomlは階層構造を持ちます。
# グローバル設定(すべてのRunnerに適用)
concurrent = 10
log_level = "info"
# 個別のRunner設定
[[runners]]
name = "docker-runner-1"
url = "https://gitlab.com"
token = "TOKEN"
executor = "docker"
# Executor固有の設定
[runners.docker]
image = "ruby:3.3"
# キャッシュ設定
[runners.cache]
Type = "s3"
2.2 設定ファイルの配置と管理
| 実行モード | 配置場所 | 用途 |
|---|---|---|
| root実行 | /etc/gitlab-runner/config.toml |
本番環境、共有Runner |
| 非root実行 | ~/.gitlab-runner/config.toml |
開発環境、個人用Runner |
重要な特性
- 設定変更は3秒以内に自動リロードされる(
listen_addressを除く) - Runnerの再起動は通常不要
-
SIGHUPシグナルで手動リロードも可能
2.3 グローバル設定の最適化
concurrent = 10 # 同時実行ジョブ数の上限
check_interval = 3 # ジョブチェック間隔(秒)
log_level = "warn" # 本番環境では"warn"推奨
log_format = "json" # ログ集約システムとの連携に便利
connection_max_age = "15m" # TLS接続の再利用期間
shutdown_timeout = 30 # シャットダウン時の猶予時間(秒)
concurrentの決め方
concurrent = (CPUコア数 × 2) または (メモリGB数 / ジョブあたりの平均メモリ使用量)
例: 8コア、32GBメモリのサーバーで、各ジョブが平均2GB使用する場合
concurrent = min(8 × 2, 32 / 2) = min(16, 16) = 16
3. 実践的な設定パターン
3.1 小規模チーム向け設定(1-5人)
要件
- シンプルな構成
- 低コスト
- メンテナンス負荷が低い
concurrent = 4
check_interval = 3
log_level = "info"
[[runners]]
name = "team-runner"
url = "https://gitlab.com"
token = "TOKEN"
executor = "docker"
[runners.docker]
image = "ruby:3.3"
privileged = false
volumes = ["/cache"]
pull_policy = ["if-not-present"]
[runners.cache]
Type = "local"
ポイント
-
executor = "docker": 環境の一貫性を確保 -
pull_policy = ["if-not-present"]: イメージダウンロードを最小化 - ローカルキャッシュで十分(分散キャッシュは不要)
3.2 中規模チーム向け設定(10-50人)
要件
- 複数のプロジェクト
- 異なる実行環境
- パフォーマンスの最適化
concurrent = 20
check_interval = 3
log_level = "warn"
log_format = "json"
# Ruby/Rails用Runner
[[runners]]
name = "ruby-runner"
url = "https://gitlab.com"
token = "TOKEN_1"
limit = 10
executor = "docker"
request_concurrency = 3
[runners.docker]
image = "ruby:3.3"
privileged = false
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
allowed_images = ["ruby:*", "postgres:*", "redis:*"]
[runners.cache]
Type = "s3"
Shared = true
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "gitlab-runner-cache"
BucketLocation = "ap-northeast-1"
# Node.js用Runner
[[runners]]
name = "node-runner"
url = "https://gitlab.com"
token = "TOKEN_2"
limit = 10
executor = "docker"
request_concurrency = 3
[runners.docker]
image = "node:20"
privileged = false
allowed_images = ["node:*", "postgres:*", "redis:*"]
[runners.cache]
Type = "s3"
Shared = true
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "gitlab-runner-cache"
BucketLocation = "ap-northeast-1"
ポイント
- 言語・フレームワークごとにRunnerを分離
-
request_concurrency = 3: ロングポーリング問題を回避 - S3キャッシュで複数Runnerでキャッシュを共有
-
limitで各Runnerの負荷を制限
3.3 大規模組織向け設定(50人以上)
要件
- 高可用性
- オートスケーリング
- コスト最適化
concurrent = 100
check_interval = 3
log_level = "warn"
log_format = "json"
[[runners]]
name = "autoscale-runner"
url = "https://gitlab.com"
token = "TOKEN"
executor = "docker-autoscaler"
[runners.autoscaler]
capacity_per_instance = 1
max_use_count = 100
max_instances = 50
plugin = "fleeting-plugin-aws"
[runners.autoscaler.plugin_config]
name = "gitlab-runner-%s"
region = "ap-northeast-1"
instance_type = "t3.medium"
[runners.autoscaler.connector_config]
username = "ec2-user"
use_external_addr = true
# 営業時間中のポリシー
[[runners.autoscaler.policy]]
periods = ["* 9-18 * * mon-fri"]
idle_count = 5
idle_time = "20m0s"
timezone = "Asia/Tokyo"
# 営業時間外のポリシー
[[runners.autoscaler.policy]]
periods = ["* 0-8,19-23 * * mon-fri", "* * * * sat,sun"]
idle_count = 1
idle_time = "30m0s"
timezone = "Asia/Tokyo"
[runners.cache]
Type = "s3"
Shared = true
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "gitlab-runner-cache"
BucketLocation = "ap-northeast-1"
AuthenticationType = "iam"
ポイント
- オートスケーラーで需要に応じてインスタンスを自動調整
- 時間帯別ポリシーでコスト最適化
- IAM認証でセキュリティ強化
-
max_use_count = 100: インスタンスを使い回してコスト削減
4. Executor別の詳細設定
4.1 Docker Executor
最も一般的なExecutorです。コンテナの分離性と再現性を提供します。
基本設定
[runners.docker]
image = "ruby:3.3"
privileged = false
volumes = ["/cache"]
pull_policy = ["if-not-present"]
セキュリティを考慮した設定
[runners.docker]
image = "ruby:3.3"
privileged = false # 特権モードは原則無効
allowed_images = ["ruby:*", "node:*", "python:*"] # 許可イメージを制限
allowed_services = ["postgres:*", "redis:*", "mysql:*"] # 許可サービスを制限
cap_drop = ["ALL"] # すべてのケーパビリティを削除
cap_add = ["NET_BIND_SERVICE"] # 必要なケーパビリティのみ追加
security_opt = ["no-new-privileges:true"] # 権限昇格を防止
パフォーマンスを考慮した設定
[runners.docker]
image = "ruby:3.3"
pull_policy = ["if-not-present", "always"] # 複数ポリシーで柔軟に対応
volumes = [
"/cache",
"/var/run/docker.sock:/var/run/docker.sock" # Docker-in-Docker用
]
cpus = "2" # CPU制限
memory = "4g" # メモリ制限
memory_reservation = "2g" # メモリソフトリミット
shm_size = 268435456 # 共有メモリサイズ(256MB)
サービスの定義
[[runners.docker.services]]
name = "postgres:15"
alias = "db"
environment = [
"POSTGRES_DB=test_db",
"POSTGRES_USER=test_user",
"POSTGRES_PASSWORD=test_password"
]
[[runners.docker.services]]
name = "redis:7"
alias = "cache"
4.2 Kubernetes Executor
Kubernetesクラスタでジョブを実行します。
[runners.kubernetes]
host = "" # 空の場合は自動検出
namespace = "gitlab-runner"
image = "ruby:3.3"
privileged = false
# リソース制限
cpu_limit = "2"
cpu_request = "1"
memory_limit = "4Gi"
memory_request = "2Gi"
# ノード選択
[runners.kubernetes.node_selector]
"node-role.kubernetes.io/runner" = "true"
# イメージプルシークレット
image_pull_secrets = ["gitlab-registry"]
# 許可イメージ
allowed_images = ["ruby:*", "node:*", "python:*"]
allowed_services = ["postgres:*", "redis:*"]
ポイント
-
node_selectorで専用ノードにジョブを配置 - リソース制限でクラスタの安定性を確保
-
image_pull_secretsでプライベートレジストリに対応
4.3 Shell Executor
ホストマシンで直接実行します。シンプルですが、分離性が低いため注意が必要です。
[[runners]]
name = "shell-runner"
url = "https://gitlab.com"
token = "TOKEN"
executor = "shell"
shell = "bash"
builds_dir = "/home/gitlab-runner/builds"
cache_dir = "/home/gitlab-runner/cache"
使用場面
- 特殊なハードウェアへのアクセスが必要な場合
- Dockerが使用できない環境
- 開発・テスト環境
注意点
- ジョブ間でファイルが残る可能性がある
- セキュリティリスクが高い
-
clean_git_config = falseがデフォルト
5. キャッシュ戦略
5.1 キャッシュの種類と選択
5.2 S3キャッシュの実践的な設定
IAM認証を使用した設定(推奨)
[runners.cache]
Type = "s3"
Path = "runner-cache"
Shared = true
MaxUploadedArchiveSize = 5368709120 # 5GB
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "gitlab-runner-cache"
BucketLocation = "ap-northeast-1"
AuthenticationType = "iam"
ServerSideEncryption = "AES256"
必要なIAMポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::gitlab-runner-cache",
"arn:aws:s3:::gitlab-runner-cache/*"
]
}
]
}
マルチパートアップロード対応(5GB以上のキャッシュ用)
[runners.cache]
Type = "s3"
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "gitlab-runner-cache"
BucketLocation = "ap-northeast-1"
AuthenticationType = "iam"
RoleARN = "arn:aws:iam::123456789012:role/GitLabRunnerCacheRole"
5.3 GCSキャッシュの設定
Workload Identity使用(Kubernetes環境)
[runners.cache]
Type = "gcs"
Path = "runner-cache"
Shared = true
[runners.cache.gcs]
BucketName = "gitlab-runner-cache"
サービスアカウント使用
[runners.cache]
Type = "gcs"
[runners.cache.gcs]
CredentialsFile = "/etc/gitlab-runner/gcs-credentials.json"
BucketName = "gitlab-runner-cache"
5.4 キャッシュ圧縮の最適化
variables:
CACHE_COMPRESSION_FORMAT: tarzstd # zip より高圧縮率
CACHE_COMPRESSION_LEVEL: fast # 速度重視
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- vendor/bundle
- node_modules
圧縮レベルの選択
| レベル | 圧縮率 | 速度 | 用途 |
|---|---|---|---|
fastest |
低 | 最速 | 頻繁に更新されるキャッシュ |
fast |
中 | 速い | 一般的な用途(推奨) |
default |
中 | 普通 | バランス重視 |
slow |
高 | 遅い | 大きなキャッシュ |
slowest |
最高 | 最遅 | ネットワーク帯域が狭い環境 |
6. パフォーマンスチューニング
6.1 同時実行数の最適化
ロングポーリング問題の診断
ログに以下のメッセージが出る場合、設定の見直しが必要です。
CONFIGURATION: Long polling issues detected
問題パターンと解決策
問題のある設定
concurrent = 2 # 問題: Runnerの数より少ない
[[runners]]
name = "runner-1"
limit = 2
request_concurrency = 1 # 問題: ロングポーリングでブロック
[[runners]]
name = "runner-2"
limit = 2
request_concurrency = 1
[[runners]]
name = "runner-3"
limit = 2
request_concurrency = 1
改善された設定
concurrent = 10 # Runnerの数より多く設定
[[runners]]
name = "runner-1"
limit = 5
request_concurrency = 3 # 複数リクエストを並列処理
[[runners]]
name = "runner-2"
limit = 5
request_concurrency = 3
6.2 check_intervalの調整
check_interval = 3 # デフォルト: 3秒
調整の指針
| 環境 | 推奨値 | 理由 |
|---|---|---|
| 高頻度ビルド | 1-2秒 | ジョブ開始の遅延を最小化 |
| 通常環境 | 3秒 | バランスが良い |
| 低頻度ビルド | 5-10秒 | GitLabサーバーへの負荷を軽減 |
計算例
Runnerが2つ、check_interval = 10の場合:
スリープ間隔 = 10秒 / 2 = 5秒
6.3 リソース制限の設定
Docker Executorの場合
[runners.docker]
cpus = "2" # CPU数
memory = "4g" # メモリ上限
memory_reservation = "2g" # メモリソフトリミット
memory_swap = "4g" # スワップ上限
shm_size = 268435456 # 共有メモリ(256MB)
Kubernetes Executorの場合
[runners.kubernetes]
cpu_limit = "2"
cpu_request = "1"
memory_limit = "4Gi"
memory_request = "2Gi"
ephemeral_storage_limit = "10Gi"
ephemeral_storage_request = "5Gi"
7. セキュリティのベストプラクティス
7.1 特権モードの回避
問題のある設定
[runners.docker]
privileged = true # 危険: コンテナがホストへフルアクセス可能
安全な設定
[runners.docker]
privileged = false
cap_drop = ["ALL"]
cap_add = ["NET_BIND_SERVICE"] # 必要な権限のみ追加
security_opt = ["no-new-privileges:true"]
7.2 イメージとサービスの制限
[runners.docker]
allowed_images = [
"ruby:3.*",
"node:20.*",
"python:3.11.*",
"registry.example.com/*" # 社内レジストリのみ許可
]
allowed_services = [
"postgres:15.*",
"redis:7.*",
"mysql:8.*"
]
7.3 Git設定のクリーンアップ
[[runners]]
clean_git_config = true # デフォルトでtrue(Shell Executor以外)
クリーンアップされるファイル
- Gitロックファイル:
{index,shallow,HEAD,config}.lock - Post-checkoutフック:
hooks/post-checkout - Git設定ファイル:
.git/config(clean_git_config = trueの場合) - Gitフックディレクトリ:
.git/hooks(clean_git_config = trueの場合)
7.4 認証情報の管理
環境変数での管理(非推奨)
[[runners]]
environment = [
"AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE", # 危険: 平文で保存
"AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
]
IAM/Workload Identityの使用(推奨)
[runners.cache.s3]
AuthenticationType = "iam" # IAMロールを使用
[runners.kubernetes]
service_account = "gitlab-runner" # Workload Identityを使用
8. トラブルシューティング
8.1 よくある問題と解決策
問題1: ジョブがpending状態のまま進まない
原因
- Runnerが登録されていない
- Runnerがオフライン
- タグが一致していない
-
concurrentの上限に達している
診断方法
# Runnerの状態確認
gitlab-runner verify
# ログ確認
tail -f /var/log/gitlab-runner/gitlab-runner.log
解決策
# concurrentを増やす
concurrent = 20 # 現在の値より大きく設定
# または、Runnerのlimitを調整
[[runners]]
limit = 10 # 0(無制限)から適切な値に変更
問題2: Docker imageのpullが遅い
原因
- ネットワーク帯域が狭い
- レジストリが遠い
- pull_policyが
alwaysになっている
解決策
[runners.docker]
pull_policy = ["if-not-present"] # ローカルにあれば再利用
# または、ミラーレジストリを使用
[runners.docker.registry_mirror]
url = "https://mirror.example.com"
問題3: キャッシュが効かない
原因
- キャッシュキーが毎回変わっている
- キャッシュの有効期限切れ
- S3の認証エラー
診断方法
# .gitlab-ci.ymlでキャッシュキーを確認
cache:
key: ${CI_COMMIT_REF_SLUG} # ブランチごとにキャッシュ
paths:
- vendor/bundle
解決策
# S3認証の確認
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
BucketName = "gitlab-runner-cache"
AuthenticationType = "iam" # IAM認証を使用
問題4: メモリ不足でジョブが失敗する
原因
- コンテナのメモリ制限が低すぎる
- 複数ジョブの同時実行でホストのメモリが不足
解決策
[runners.docker]
memory = "4g" # メモリ上限を増やす
memory_reservation = "2g" # ソフトリミットも調整
# または、concurrentを減らす
concurrent = 5 # 同時実行数を制限
8.2 ログレベルの調整
開発・デバッグ時
log_level = "debug"
log_format = "text"
本番環境
log_level = "warn"
log_format = "json" # ログ集約システムとの連携に便利
8.3 ヘルスチェック
Runnerの状態確認
# すべてのRunnerを確認
gitlab-runner verify
# 特定のRunnerを確認
gitlab-runner verify --name "runner-1"
# 削除されたRunnerをクリーンアップ
gitlab-runner verify --delete
メトリクスの有効化
listen_address = ":9252" # Prometheusメトリクスを公開
アクセス: http://runner-host:9252/metrics
9. 高度な設定パターン
9.1 マルチアーキテクチャ対応
[[runners]]
name = "amd64-runner"
executor = "docker"
[runners.docker]
image = "ruby:3.3"
[[runners]]
name = "arm64-runner"
executor = "docker"
[runners.docker]
image = "ruby:3.3"
helper_image = "registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:arm64-latest"
9.2 カスタムビルドディレクトリ
[runners.custom_build_dir]
enabled = true
# .gitlab-ci.yml
variables:
GIT_CLONE_PATH: $CI_BUILDS_DIR/my-project
build:
script:
- pwd # /builds/my-project
注意点
-
GIT_CLONE_PATHは$CI_BUILDS_DIR配下である必要がある -
concurrent > 1の場合、パスの衝突に注意
9.3 セッションサーバーの設定
インタラクティブなデバッグに使用します。
[session_server]
listen_address = "[::]:8093"
advertise_address = "runner.example.com:8093"
session_timeout = 1800 # 30分
使用方法
# .gitlab-ci.yml
debug-job:
script:
- echo "デバッグ用ジョブ"
when: manual
ジョブの詳細画面から「Debug」ボタンでセッションに接続できます。
10. まとめ
10.1 設定のチェックリスト
基本設定
-
concurrentは適切な値に設定されているか -
log_levelは環境に応じて設定されているか -
check_intervalは適切か
セキュリティ
-
privileged = falseになっているか -
allowed_imagesとallowed_servicesが設定されているか - 認証情報はIAM/Workload Identityを使用しているか
-
clean_git_config = trueになっているか
パフォーマンス
- キャッシュが適切に設定されているか
-
request_concurrencyがロングポーリング問題を回避できているか - リソース制限が適切に設定されているか
運用
- ログが適切に出力されているか
- メトリクスが取得できるか
- バックアップ・リカバリ手順が確立されているか
10.2 段階的な最適化アプローチ
フェーズ1: 基本設定(1週間)
- Runnerの登録と基本動作確認
- シンプルなジョブの実行
- ログの確認
フェーズ2: パフォーマンス最適化(2-4週間)
- キャッシュの設定
- 同時実行数の調整
- リソース制限の設定
フェーズ3: セキュリティ強化(1-2週間)
- イメージ・サービスの制限
- 認証方式の見直し
- 権限の最小化
フェーズ4: 運用の安定化(継続的)
- 監視・アラートの設定
- ログ分析
- 定期的な設定見直し
10.3 次のステップ
本記事で解説した内容を実践することで、効率的で安全なCI/CDパイプラインを構築できます。さらに深く学ぶには、以下のトピックを検討してください。
- 高度なキャッシュ戦略: 依存関係の最適化、レイヤーキャッシング
- セキュリティスキャン: SAST、DAST、依存関係スキャンの統合
- パイプライン最適化: 並列実行、DAG、動的子パイプライン
- コスト最適化: スポットインスタンス、リザーブドインスタンスの活用
- マルチクラウド戦略: AWS、GCP、Azureの使い分け
設定は一度行えば終わりではなく、チームの成長やプロジェクトの変化に応じて継続的に見直していくことが重要です。