誰がいつ本番サーバーへ変更を適用したか記録が残らず、障害が起きても「誰が何を当てたのか」を追跡できない。そんな運用上の問題を根本から解消する手段が、GitLab CIとAnsibleの連携設計です。
この記事では、AnsibleとGitLab CIを統合してインフラ変更をパイプラインで安全に管理する設計手順を解説します。RHEL 9系~Ubuntu 24.04 LTSを想定環境として説明します。GitLab CI/CDの基本設定経験があり、Ansible Playbookを手動実行したことがある方を対象にしています。
この記事のポイント
・AnsibleとGitLab CIを連携するとPlaybook実行の履歴・承認フロー・環境別制御が一元管理できる
・.gitlab-ci.ymlにlint→dry-run→staging→productionの4ステージを設計するのが基本形
・シークレットはGitLab CI変数+Ansible Vaultで分離し、リポジトリへの直書きを防ぐ
・MRでのdry-run確認とwhen: manualの組み合わせで本番適用の「ヒューマンゲート」を設ける
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
手動実行の限界とパイプライン化のメリット
Ansibleを手動で実行している運用では、いくつかの構造的な問題が積み重なっていきます。まず変更履歴の不透明さです。ターミナルでansible-playbookを実行しても、「誰が・いつ・どのPlaybookを・どのインベントリに当てたか」はサーバーのログに断片的に残るだけで、一覧性がありません。障害が起きたとき「昨日の誰かの作業が原因かもしれない」という推測から調査を始めることになります。
次にレビューなしの本番適用です。Playbookの変更をGitLabにコミットしたとしても、実行するタイミングが個人の判断に委ねられると、意図せず未レビューの変更が本番に当たることがあります。
GitLab CIとAnsibleを連携させると、この2つの問題を同時に解消できます。
・全実行がパイプラインとして記録される:いつ誰がどのジョブを動かしたか、GitLabのUI上で確認できる
・MRごとにdry-runが自動実行される:変更差分をレビュアーが確認してからマージできる
・環境別のデプロイが制御される:ステージングは自動、本番は手動承認という流れを設計できる
・シークレットが安全に管理される:SSH鍵やVaultパスワードをGitLab CI変数として保管し、リポジトリへの混入を防ぐ
GitLab CI×Ansible統合の全体設計
連携の全体像を整理すると、次のようなフローになります。# 全体フロー(例示) Developer | +-- feature branchにPlaybook変更をpush | +-- Merge Request (MR) を作成 | +-- [自動] validate: ansible-lint 実行 +-- [自動] check: ansible-playbook --check --diff | | | MR画面でdiff確認 --> レビュアーがapprove | +-- mainブランチにmerge | +-- [手動トリガー] deploy-staging +-- [手動トリガー] deploy-production
Ansibleの基礎設計から体系的に学びたい方は、リナックスマスター.JPのAnsibleコンテンツもあわせて参照してください。
GitLab Runner側の要件は以下のとおりです。
・Docker executor:ジョブごとにコンテナが起動・廃棄されるため実行環境が汚染されにくい
・ネットワーク疎通:RunnerからAnsibleの管理対象ホストへSSHが通ること
・権限:Runnerが起動するコンテナからGitLabのプロジェクト変数を参照できること
.gitlab-ci.ymlで実装するパイプラインの構造
1. 4ステージ構成の設計方針
パイプラインは以下の4ステージで構成するのが実用的な基本形です。・validate:ansible-lintによる静的解析。構文エラーやベストプラクティス違反を早期検出する
・check:--check --diffによるdry-run。変更内容をdiff形式で出力してレビュアーが確認できるようにする
・deploy-staging:ステージング環境への適用。developブランチへのpushで手動トリガー
・deploy-production:本番環境への適用。mainブランチへのマージ後に手動トリガー
validateとcheckをMRのたびに自動実行し、deployは必ず手動で起動する設計にすることで、誤適用のリスクを最小化できます。
2. GitLab RunnerとDockerイメージの選択
Ansibleの実行イメージには、バージョンを固定したものを使うのが原則です。latestタグを使うとAnsibleのバージョンが自動更新されてPlaybookの動作が変わることがあります。特定のansible-coreバージョンを含むイメージをtagで指定するか、自前でDockerfileを管理してプロジェクト専用イメージを作成することを検討してください。# Dockerfile(プロジェクト専用Ansibleイメージの例) FROM python:3.11-slim RUN pip install --no-cache-dir \ ansible-core==2.17.* \ ansible-lint==24.* \ boto3 \ botocore RUN ansible-galaxy collection install \ amazon.aws \ community.general
3. .gitlab-ci.ymlの基本テンプレート
実際の.gitlab-ci.ymlの構成例を示します(出力例・環境により構成は異なります)。# .gitlab-ci.yml(設計例) stages: - validate - check - deploy-staging - deploy-production # 全ジョブ共通の前処理 .ansible_base: &ansible_base image: registry.gitlab.example.com/myproject/ansible:2.17 before_script: - echo "$VAULT_PASS" > /tmp/vault-pass && chmod 600 /tmp/vault-pass - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa - echo "StrictHostKeyChecking no" >> ~/.ssh/config # ステージ1: ansible-lintによる静的解析 lint: <<: *ansible_base stage: validate script: - ansible-lint playbooks/ # ステージ2: MR時のdry-run(変更差分の確認) dry-run: <<: *ansible_base stage: check rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' script: - ansible-playbook -i inventory/staging/hosts.ini --vault-password-file /tmp/vault-pass --check --diff playbooks/site.yml 2>&1 | tee ansible-check.log artifacts: paths: - ansible-check.log when: always # ステージ3: ステージング環境デプロイ(手動) deploy-staging: <<: *ansible_base stage: deploy-staging environment: staging rules: - if: '$CI_COMMIT_BRANCH == "develop"' when: manual script: - ansible-playbook -i inventory/staging/hosts.ini --vault-password-file /tmp/vault-pass playbooks/site.yml # ステージ4: 本番環境デプロイ(手動・保護ブランチ) deploy-production: <<: *ansible_base stage: deploy-production environment: production rules: - if: '$CI_COMMIT_BRANCH == "main"' when: manual script: - ansible-playbook -i inventory/production/hosts.ini --vault-password-file /tmp/vault-pass playbooks/site.yml
シークレット管理:GitLab CI変数とAnsible Vaultの連携
パイプラインで安全にAnsibleを実行するために管理が必要なシークレットは、主に次の2種類です。・SSH秘密鍵:Ansibleが管理対象ホストへ接続するために使う。GitLab CI/CDのVariable型として登録する
・Ansible Vaultパスワード:暗号化された変数ファイルを復号するために使う。Masked変数として登録する
GitLabの設定画面(Settings > CI/CD > Variables)で変数を登録するとき、以下の点に注意してください。
| 変数名 | 型 | 推奨設定 | 用途 |
|---|---|---|---|
| SSH_PRIVATE_KEY | Variable | Masked + Protected | 管理ホストへのSSH接続 |
| VAULT_PASS | Variable | Masked + Protected | Ansible Vaultの復号パスワード |
Ansible Vault側では、暗号化が必要な変数(DBパスワード、APIキーなど)を`ansible-vault encrypt_string`でインライン暗号化するか、`group_vars/`配下のファイルをファイルごと暗号化して管理します。パイプライン内では`--vault-password-file`オプションで復号します。
# Ansible Vaultでパスワードをインライン暗号化(ローカルで実行して group_vars に貼る) $ ansible-vault encrypt_string 'db_pass_value' --name 'db_password' db_password: !vault | $ANSIBLE_VAULT;1.1;AES256 38383138306433386... ... # CI側では --vault-password-file で復号 $ ansible-playbook site.yml --vault-password-file /tmp/vault-pass
MRでdry-runを使ったレビューフローの設計
GitLab CIとAnsibleの連携で最も効果的な仕組みの一つが、MR(マージリクエスト)に連動したdry-runの自動実行です。フローは次のようになります。
・ステップ1:開発者がfeatureブランチにPlaybookの変更をpushしてMRを作成する
・ステップ2:CIが自動でdry-runジョブを起動し、--check --diffの結果をアーティファクトとして保存する
・ステップ3:レビュアーがアーティファクトのログを確認し、変更内容が意図どおりかを検証する
・ステップ4:問題がなければMRをapproveしてmainにマージする
・ステップ5:本番デプロイはmanualトリガーで担当者が意識的に起動する
dry-runが有効なのは`--check`モードをAnsibleがサポートしているモジュールに限ります。`shell`モジュールや`command`モジュールはcheck modeで実際には実行されないため、差分が表示されません。Playbook内でshell/commandモジュールの使用を最小限に抑え、冪等性の高いモジュール(`dnf`、`template`、`file`など)で置き換えるとdry-runの精度が上がります。
なお、dry-runの結果をMRのコメントに自動投稿したい場合は、GitLabのAPI(MR Comments APIへのPOST)をCIスクリプトに組み込む方法があります。ただし設定が複雑になるため、まずはアーティファクトをUIから確認する運用から始めるのが現実的です。
ステージング→本番の段階適用設計
本番環境への変更を段階的に適用するために、インベントリを環境別に分離するのが基本設計です。# 推奨ディレクトリ構造(例示) inventory/ +-- staging/ | +-- hosts.ini # ステージングホスト一覧 | +-- group_vars/ | +-- all.yml # ステージング用変数(vault暗号化) +-- production/ +-- hosts.ini # 本番ホスト一覧 +-- group_vars/ +-- all.yml # 本番用変数(vault暗号化)
本番適用ジョブを`when: manual`にすることで、パイプラインは自動で進行せず担当者が明示的にトリガーを押す必要があります。さらにGitLabの「Environments」機能を使ってDeployment approversを設定すると、複数人の承認が揃わないと本番デプロイが実行されない設計にできます。
ステージングで一定時間稼働させて問題がないことを確認してから本番に適用するというフローを、パイプライン上の設計として強制できるのが大きなメリットです。手動実行では「ステージングで確認した」という口頭確認に頼りがちですが、パイプラインとして設計することで「ステージングジョブが成功していないと本番ジョブのトリガーが現れない」という構造的な強制ができます。
よくある設計ミスと対処法
GitLab CI×Ansible連携でよく見られる設計上の問題点を整理します。Dockerイメージのバージョンを固定していない
`latest`タグを使うとAnsibleのマイナーバージョンアップで非推奨オプションが動作しなくなる可能性があります。特定のバージョンタグを固定し、アップグレードは意識的に行ってください。
シークレットをリポジトリに直書きしている
Ansible VaultパスワードやSSH鍵をそのままリポジトリに含めるのは最もリスクの高いミスです。GitLab CI/CD変数として管理し、パイプライン内で一時ファイルに書き出す設計を徹底してください。
ANSIBLE_HOST_KEY_CHECKINGを無効化したままにしている
CI環境では`ANSIBLE_HOST_KEY_CHECKING=False`を設定することが多いですが、これは初回接続時のホスト確認を省略する設定です。実際の本番運用では既知ホストをknown_hostsに登録してから有効に戻すか、接続先ホストのFingerprintを事前に登録する設計を検討してください。
dry-runステージを省略している
「lintが通ればdry-runは不要」と判断してcheckステージを省略すると、変更の影響範囲をレビューできなくなります。dry-runは手間よりメリットの方が大きいため、MR時には必ず実行する設計を維持してください。
Shell executorでシークレットが残存する
Shell executorはジョブ終了後にファイルシステムが残るため、`/tmp/vault-pass`や`~/.ssh/id_rsa`が次のジョブから参照できる状態になります。`after_script`でこれらのファイルを削除する処理を必ず入れてください。
本記事のまとめ
AnsibleとGitLab CIを連携させる設計入門として、パイプラインの全体フロー・.gitlab-ci.ymlの構造・シークレット管理・dry-runレビュー・段階適用の設計パターンを解説しました。| 設計のポイント | 概要 |
|---|---|
| 4ステージ構成 | lint→check(dry-run)→staging→productionの順に構成する |
| シークレット管理 | SSH鍵・VaultパスワードはGitLab CI変数に登録してMasked+Protected設定 |
| MRでのdry-run | merge_request_eventでMR時だけ自動実行し、差分をアーティファクトで提供 |
| 環境分離 | inventory/staging/とinventory/production/を分離し、group_varsも環境別に管理 |
| 本番適用の制御 | when: manual + GitLab EnvironmentsのDeployment approversで多段承認を設ける |
| イメージ固定 | latestタグを避け、ansibleバージョンを明示したイメージタグを使う |
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:シェルスクリプトからAnsibleへ段階的に移行する設計入門|既存の自動化資産をPlaybookに変換する手順と判断基準
- この記事の属するカテゴリ:Ansibleへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます