台数が増えるほど手作業は限界になり、冪等性のない手順書はいつか破綻します。
この記事では、Linuxサーバーの構成管理・自動化ツールとして実務で広く使われている ansible-playbook コマンド の使い方を解説します。
インベントリの書き方から playbook の基本構文、変数・ハンドラー・ロールの使い方、site.yml × import_playbook による Playbook 一元管理、Playbookの役割別・オペレーション別の分割パターン、commonロールによるベースライン設定の統一、GitHub Actions との連携、トラブルシュートまで、実務で使える形でまとめています。
動作確認環境:RHEL 9.4 / AlmaLinux 9.4 / Ubuntu 24.04 LTS(Ansible 2.16)
この記事のポイント
・ansible-playbookはYAMLのplaybookでサーバーを冪等設定できる
・インベントリでホスト一覧を定義してplaybookで操作対象を指定する
・--checkでドライラン、-v/-vvvでデバッグが本番適用前の基本
・ハンドラーで「変更時だけ再起動」を安全に制御できる
・site.yml×import_playbookで役割別・オペレーション別Playbookを一元管理できる
・import_playbookはPlaybook全体・import_tasksはtask列のみを読む別物
・commonロールをhosts:allで全ホストにベースライン設定を適用できる
・roleは「何を設定するか」、Playbookは「どこに配るか」で責任を分ける
・GitHub Actionsと連携してPlaybook検証・デプロイを自動化できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
Ansibleとansible-playbookコマンドとは
Ansibleは Red Hat が開発するオープンソースの構成管理・自動化ツールです。エージェントレス(管理対象サーバー側に専用ソフトを入れない方式)で動作し、SSH(Linuxの場合)でリモートサーバーに接続して設定を適用します。管理対象のサーバー側には Python が入っていれば追加ソフトウェアは不要という点が、他のツールと比較して導入のハードルを下げています。
ansible-playbook は、Ansibleが提供するコマンドの中核であり、YAML形式で書かれた「playbook」と呼ばれる手順書を読み込んでサーバーへ適用します。コマンド1回で何十台のサーバーを同時に設定でき、何度実行しても同じ結果になる冪等性(べきとうせい)が最大の強みです。
Ansibleには複数のコマンドがありますが、それぞれの役割はこのようになっています。
| コマンド | 役割 |
|---|---|
ansible |
アドホックな単発コマンド実行 |
ansible-playbook |
yamlファイルで定義した手順を実行(本記事のメイン) |
ansible-inventory |
インベントリの確認・デバッグ |
ansible-galaxy |
ロールやコレクションの管理 |
ansible-vault |
機密情報の暗号化 |
Ansibleのインストール方法
1. RHEL 9・AlmaLinux 9・Rocky Linux 9の場合
EPEL リポジトリから Ansible をインストールします。# EPELリポジトリの有効化 sudo dnf install -y epel-release # Ansibleのインストール sudo dnf install -y ansible # バージョン確認 ansible --version
2. Ubuntu 24.04 LTSの場合
Ubuntu では apt もしくは PPA から Ansible をインストールできます。# 公式PPAを追加してからインストール(最新版を使う場合) sudo apt-add-repository --yes --update ppa:ansible/ansible sudo apt install -y ansible # または標準リポジトリから(バージョンは古い場合あり) sudo apt install -y ansible # バージョン確認 ansible --version
3. pipを使ってインストールする場合
DNF/apt が使えない環境や、最新版を使いたい場合は pip でもインストールできます。Python の仮想環境(venv)に閉じ込めると OS 側の Python 環境を汚さずに済みます。# 仮想環境の作成とAnsibleのインストール python3 -m venv ~/ansible-env source ~/ansible-env/bin/activate pip install ansible ansible-lint # バージョン確認 ansible --version ansible-lint --version
[admin@controller ~]$ ansible --version ansible [core 2.16.3] config file = /etc/ansible/ansible.cfg configured module search path = ['/home/admin/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3.11/site-packages/ansible ansible collection location = /home/admin/.ansible/collections:/usr/share/ansible/collections executable location = /usr/bin/ansible python version = 3.11.7 jinja2 version = 3.1.2 libyaml = True
インベントリの書き方
インベントリは、Ansible が操作する対象サーバーの一覧を定義するファイルです。デフォルトパスは/etc/ansible/hosts ですが、-i オプションで任意のファイルを指定するのが実務では一般的です。1. INI形式インベントリの基本
# /etc/ansible/hosts または任意の inventory.ini # 単独のホスト指定 web01.example.com 192.168.10.11 # グループ化 [webservers] web01.example.com web02.example.com [dbservers] db01.example.com ansible_user=dbadmin ansible_port=2222 # グループのグループ(親グループ) [datacenter:children] webservers dbservers
2. YAML形式インベントリ(推奨)
YAML形式はより読みやすく、変数との親和性が高いため実務では推奨されます。# inventory.yml all: children: webservers: hosts: web01.example.com: ansible_user: ec2-user web02.example.com: ansible_user: ec2-user dbservers: hosts: db01.example.com: ansible_user: dbadmin ansible_port: 2222 vars: ansible_ssh_private_key_file: ~/.ssh/id_rsa
3. インベントリの確認
# インベントリ内のホスト一覧を表示 ansible-inventory -i inventory.yml --list # グラフ形式で確認 ansible-inventory -i inventory.yml --graph # 疎通確認(pingモジュール) ansible -i inventory.yml webservers -m ping
playbookの基本構文と作成方法
1. playbookの最小構成
playbook は YAML 形式で記述する「自動化の設計書」です。以下が最小限の構成です。# site.yml --- - name: Webサーバー初期設定 hosts: webservers become: yes # sudo で実行 tasks: - name: Apacheのインストール ansible.builtin.dnf: name: httpd state: present - name: Apacheの起動と自動起動有効化 ansible.builtin.service: name: httpd state: started enabled: yes
・hosts:操作するグループまたはホスト名(インベントリと一致させる)
・become:yes にすると sudo(root 権限)で実行
・tasks:実行するタスクのリスト
・name:タスクの説明文(ログに表示される)
・モジュール名:実際に行う操作(dnf, service, copy 等)
2. when条件でOSごとに処理を切り替える
実務では RHEL 系と Ubuntu 系が混在する環境が少なくありません。when 条件を使うと、OS種別によってモジュールを切り替えられます。tasks: - name: Apacheのインストール(RHEL系) ansible.builtin.dnf: name: httpd state: present when: ansible_os_family == "RedHat" - name: Apacheのインストール(Debian系) ansible.builtin.apt: name: apache2 state: present when: ansible_os_family == "Debian"
ansible_os_family は Gathering Facts で自動取得される変数です。このように同一 playbook でマルチOS環境に対応できます。3. よく使うモジュール一覧
| モジュール | 役割 | 使用例 |
|---|---|---|
ansible.builtin.dnf |
RHEL系パッケージ管理 | Apache, nginxのインストール |
ansible.builtin.apt |
Debian系パッケージ管理 | Ubuntu環境でのインストール |
ansible.builtin.service |
サービス管理 | 起動・停止・自動起動設定 |
ansible.builtin.copy |
ファイルのコピー | 設定ファイルの配布 |
ansible.builtin.template |
テンプレートからファイル生成 | Jinja2変数展開付きの設定配布 |
ansible.builtin.user |
ユーザー管理 | ユーザー作成・削除・グループ追加 |
ansible.builtin.file |
ファイル・ディレクトリ管理 | パーミッション設定・シンボリックリンク |
ansible.builtin.lineinfile |
ファイル内の行編集 | 設定ファイルの特定行変更 |
ansible.builtin.command |
任意コマンドの実行 | 専用モジュールがない処理 |
ansible.builtin.shell |
シェル経由でコマンド実行 | パイプ・リダイレクトを含む処理 |
ansible-playbookコマンドの基本的な使い方
1. 基本実行
# 基本実行 ansible-playbook -i inventory.yml site.yml # SSH鍵を指定して実行 ansible-playbook -i inventory.yml site.yml --private-key=~/.ssh/id_rsa # sudo パスワードを対話的に入力する場合 ansible-playbook -i inventory.yml site.yml --ask-become-pass
2. 実行前のドライラン(--check)
--check オプションは実際には変更を行わず、何が変わるかを事前確認するためのモードです。本番環境への適用前に必ず使う習慣をつけましょう。--diff を組み合わせると、設定ファイルの変更行が diff 形式で表示されるためさらに確認しやすくなります。# ドライラン(変更は行わず確認のみ) ansible-playbook -i inventory.yml site.yml --check # ドライランと差分表示を組み合わせる(より詳細) ansible-playbook -i inventory.yml site.yml --check --diff
3. 特定のホストやグループだけ実行(--limit)
# 特定ホストのみ実行 ansible-playbook -i inventory.yml site.yml --limit web01.example.com # 特定グループのみ実行 ansible-playbook -i inventory.yml site.yml --limit webservers # 複数ホストをカンマ区切りで指定 ansible-playbook -i inventory.yml site.yml --limit "web01.example.com,web02.example.com" # パターンマッチでホストを絞る(ワイルドカード使用可) ansible-playbook -i inventory.yml site.yml --limit "web*"
4. 特定タスクだけ実行(タグ機能)
大規模な playbook では、特定のタスクだけを実行したい場面があります。タグ機能を使うと、対象を絞り込めます。# playbook にタグを付ける例 --- - name: Webサーバー設定 hosts: webservers become: yes tasks: - name: Apacheのインストール ansible.builtin.dnf: name: httpd state: present tags: install - name: 設定ファイルのコピー ansible.builtin.copy: src: httpd.conf dest: /etc/httpd/conf/httpd.conf tags: config # installタグのタスクのみ実行 ansible-playbook -i inventory.yml site.yml --tags install # configタグのタスクをスキップ ansible-playbook -i inventory.yml site.yml --skip-tags config
# 定義されているタグ一覧を事前確認する ansible-playbook -i inventory.yml site.yml --list-tags
playbook: site.yml play #1 (webservers): Webサーバー設定 TAGS: [] TASK TAGS: [config, install, service] play #2 (dbservers): DBサーバー設定 TAGS: [] TASK TAGS: [config, install]
・機能タグ:webserver, database, monitoring のように「何を設定するか」で分類する
・操作タグ:install, configure, restart のように「どのオペレーションか」で分類する
・neverタグ:通常実行ではスキップし、
--tags never を明示したときだけ動かす特殊タグ。初回セットアップのみ実行したい初期化処理などに活用できる5. 冗長ログでデバッグ(-v / -vvv)
# 標準の詳細ログ ansible-playbook -i inventory.yml site.yml -v # さらに詳細なデバッグ情報(SSH接続情報も表示) ansible-playbook -i inventory.yml site.yml -vvv # 最大詳細ログ(機密情報も表示されるため開発環境限定・本番では使用禁止) ansible-playbook -i inventory.yml site.yml -vvvv
実務でよく使う構成パターン
1. 変数を使って設定値を管理する
ハードコーディングを避け、変数で設定値を管理することで playbook の再利用性が大幅に上がります。# site.yml --- - name: Webサーバー設定 hosts: webservers become: yes vars: http_port: 80 max_clients: 200 server_name: "{{ inventory_hostname }}" tasks: - name: httpdのインストール ansible.builtin.dnf: name: httpd state: present - name: 設定ファイルをテンプレートから生成 ansible.builtin.template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf owner: root group: root mode: '0644'
・group_vars/all:全グループ共通の変数ファイル
・group_vars/グループ名:グループ別の変数ファイル
・host_vars/ホスト名:ホスト別の変数ファイル
・playbook内のvars:playbook単位の変数
・--extra-vars(-e):コマンドライン引数(最高優先度)
2. ハンドラーで変更があった時だけ再起動する
設定ファイルが変更されたときだけサービスを再起動したい——この要件を安全に実装するのがハンドラーです。--- - name: Apacheの設定と起動 hosts: webservers become: yes handlers: - name: apache再起動 ansible.builtin.service: name: httpd state: restarted tasks: - name: Apacheのインストール ansible.builtin.dnf: name: httpd state: present - name: 設定ファイルのコピー ansible.builtin.copy: src: httpd.conf dest: /etc/httpd/conf/httpd.conf notify: apache再起動 # 変更があった時だけハンドラーを呼び出す
notify で指定した処理が「変更(changed)」になった時のみ、playbook の末尾で1度だけ実行されます。複数のタスクが同じハンドラーを notify しても、1回しか実行されないため、無駄な再起動を防ぎます。ハンドラーが発火したとき、実行ログには通常タスクとは別に
RUNNING HANDLERS セクションが表示されます。TASK [設定ファイルのコピー] ***************************************************** changed: [web01.example.com] changed: [web02.example.com] RUNNING HANDLERS *********************************************************** TASK [apache再起動] ********************************************************** changed: [web01.example.com] changed: [web02.example.com] PLAY RECAP ***************************************************************** web01.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0 web02.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0
RUNNING HANDLERS セクション自体が出力されず、不要な再起動が走らないことが確認できます。3. 実行結果の確認とデバッグ
実サーバー上での実行結果(出力例)を確認しておきます。[admin@controller ~]$ ansible-playbook -i inventory.yml site.yml PLAY [Webサーバー初期設定] **************************************************** TASK [Gathering Facts] ********************************************************* ok: [web01.example.com] ok: [web02.example.com] TASK [Apacheのインストール] ***************************************************** changed: [web01.example.com] changed: [web02.example.com] TASK [Apacheの起動と自動起動有効化] ******************************************** changed: [web01.example.com] changed: [web02.example.com] PLAY RECAP ********************************************************************* web01.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 web02.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
・ok:変更なし(すでに正しい状態)
・changed:変更を適用した
・failed:エラーが発生した
・unreachable:SSH接続できなかった
・skipped:条件(when)によりスキップ
4. ローリングデプロイ(serialオプション)
本番に10台のWebサーバーがある状態でPlaybookを実行すると、デフォルトでは全台に一斉に変更が適用されます。万が一障害が発生した場合のリスクを下げるために、serial オプションで1回に更新する台数を絞るのが実務でよく使われるパターンです。# site.yml --- - name: Webサーバーの構成適用 hosts: webservers serial: 2 # 一度に2台ずつ更新 max_fail_percentage: 30 # 30%超失敗で中断 become: yes roles: - nginx
max_fail_percentage を設定しておくと、一定割合以上のサーバーで失敗が発生した時点でPlaybookが中断されます。無言で全台に壊れた設定が広がる事態を防ぐための安全装置です。ロールを使ってplaybookを整理する
playbook が大きくなってきたら、ロール(Role)を使って機能ごとに分割します。ロールは Ansible の推奨する構成管理パターンで、再利用性が大幅に向上します。1. ロールのディレクトリ構造
# ansible-galaxy コマンドでロールの雛形を作成 ansible-galaxy init roles/webserver # 生成されるディレクトリ構造 roles/ └── webserver/ ├── tasks/ │ └── main.yml # メインのタスク定義 ├── handlers/ │ └── main.yml # ハンドラー定義 ├── templates/ │ └── httpd.conf.j2 # Jinja2テンプレート ├── files/ ├── vars/ │ └── main.yml # 変数定義(上書き不可の固定変数) ├── defaults/ │ └── main.yml # デフォルト変数(呼び出し側から上書き可能) ├── meta/ │ └── main.yml # ロールのメタ情報 └── README.md
defaults/main.yml と vars/main.yml の使い分けは初学者が迷いやすい箇所です。・defaults/main.yml:呼び出し側から上書き可能なデフォルト変数(ポート番号・ファイルパス・フラグなど)
・vars/main.yml:role内部で固定する変数(上書き不可。内部定数・計算済み値など)
大半の変数は
defaults/ に置き、本当に上書きを禁止したいものだけ vars/ に入れるのが実務上の基本です。呼び出し側が環境別にポート番号やパスを変えられるよう設計することで、roleの再利用性が高まります。2. ロールをplaybookから呼び出す
# site.yml(ロール呼び出し版) --- - name: Webサーバーの構成 hosts: webservers become: yes roles: - webserver # roles/webserver/ を自動的に読み込む - firewall # roles/firewall/ も適用
3. ansible-galaxy collectionsでサードパーティ製ロールを活用する
自作ロール以外にも、ansible-galaxy コミュニティが公開している高品質なコレクションを活用できます。# コレクションのインストール(例:Nginx公式コレクション) ansible-galaxy collection install nginxinc.nginx_core # インストール済みコレクション一覧 ansible-galaxy collection list
requirements.yml に記述しておくと、CI/CD環境でも一括インストールできて便利です。4. roleとPlaybookの責任分担を理解する
Ansibleを使い込むにつれ、「この処理はroleに書くべきか、Playbookに書くべきか」という判断に迷うことがあります。判断の基準はシンプルです。| 書く場所 | 責任の問いかけ | 具体例 |
|---|---|---|
| role | このサービスを設定するために何をするか | Nginxのインストール・設定ファイル配布・起動 |
| Playbook | このグループのホストに何を配るか | webserversグループにnginxとfirewalldを渡す |
hosts: の指定を書かないのはこのためです。roleは接続先のホストを知らなくてよく、Playbookが接続先を決めてroleを渡す構造になっています。この責任境界を守ると、roleは別プロジェクトへの移植が容易になります。Nginxの設定を変えた際の変更が
roles/nginx/ 配下だけに閉じるため、プルリクエストのdiffが整理されてチームレビューのコストが下がります。逆に、role内に hosts: やホスト特有の処理を書き込むほど、その恩恵が失われていきます。もう一つの鉄則が「1ミドルウェア1role」です。「Webサーバー全部入り」のようなroleにnginx・php-fpm・certbotをすべて詰め込むと、nginxだけを別プロジェクトで使い回したいときに丸ごとコピーするしかなくなります。roleの境界はミドルウェア単位で切ることで、再利用性と変更の影響範囲を最小に保てます。
site.ymlとimport_playbookでPlaybookを一元管理する
台数が5台のうちは1本のPlaybookに全設定を書いても問題ありません。しかし担当サーバーが増えるにつれ、1本に詰め込み続けた結果、ファイルが1,000行を超えてから誰も触れなくなった——という状況は現場でよく見られます。具体的には次のような問題が出てきます。・実行範囲を絞れない:WebサーバーだけOSのパッケージを更新したいのに、DBサーバーの設定まで走ってしまう
・変更が怖くなる:1行変えただけで全サーバーに影響するため、誰も触れなくなる
・レビューが回らない:1,000行のPlaybookをPull Requestで差分確認するのは現実的ではない
・再利用ができない:新しいプロジェクトに同じ設定を持ち出したいとき、コピペしか手段がなくなる
webservers・dbservers・lb といった役割ごとにPlaybookを分割し始めると、「どのPlaybookをどの順で実行するか」を管理する仕組みが必要になります。その答えが site.yml × import_playbook の設計です。
1. site.ymlとimport_playbookの基本構成
site.yml はAnsibleプロジェクトの慣習的なエントリーポイントファイルです。このファイル自体は設定をほとんど持たず、役割別Playbookをimport_playbook で束ねる「目次」として機能します。# プロジェクト構成例 . ├── site.yml ← エントリーポイント ├── webservers.yml ← Webサーバー設定Playbook ├── dbservers.yml ← DBサーバー設定Playbook ├── lb.yml ← ロードバランサー設定Playbook ├── inventory/ │ ├── staging/ │ │ ├── hosts │ │ └── group_vars/ │ └── production/ │ ├── hosts │ └── group_vars/ └── roles/ ├── nginx/ ├── mysql/ └── haproxy/
# site.yml --- # 1. まずロードバランサーを設定 - import_playbook: lb.yml # 2. Webサーバー群の設定 - import_playbook: webservers.yml # 3. DBサーバー群の設定 - import_playbook: dbservers.yml
import_playbook の記述順が実行順序になります。site.yml を1本実行するだけで全サーバーへの設定配布が完了する構造です。各Playbookは独立した hosts: 指定を持つため、それぞれの担当サーバーに対してのみ適用されます。インベントリをstaging/productionでディレクトリ分けしておくと、-i で渡すパスを変えるだけで環境を切り替えられます。Playbookのコードは共通のままにしておき、環境差異は
group_vars/ 内のファイルで吸収するのが実務での定番です。# staging環境への適用 ansible-playbook -i inventory/staging/hosts site.yml # 本番環境への適用 ansible-playbook -i inventory/production/hosts site.yml
2. include_playbookとimport_playbookの違い
import_playbook と include_playbook は似た見た目ですが、評価タイミングが根本的に異なります。| 項目 | import_playbook(推奨) | include_playbook |
|---|---|---|
| 評価タイミング | 実行前(静的解析時) | 実行中(動的) |
| --list-tasksとの互換性 | 対応(タスク一覧が確認できる) | 非対応(実行しないと展開されない) |
| --checkドライランとの互換性 | 正常動作 | 不完全(実行時展開のため) |
| ループ内での使用 | 不可 | 可(loop:と組み合わせられる) |
| when:の使用 | 不可(静的展開のため構文エラー) | 可(動的に切り替えられる) |
| タグの伝播 | import_playbook自体にタグを付けると子Playbookのtask全体に伝播 | 同様だが実行時評価のため挙動が異なる場合あり |
ansible-playbook --list-tasks site.yml で実行されるタスク全体を事前に確認でき、--check(ドライラン)も正常に動作するためです。include_playbook は「どのPlaybookを実行するかをループや変数で動的に決めたい」という特殊ケースに限定するのが現場での使い分けです。【注意】Ansible 2.12以降、
include_playbook は廃止されています。Ansible 2.16環境では利用できないため、動的に条件で呼び出しを切り替えたい場合は、後述する「タグを使って特定Playbookだけ実行する」や「環境別エントリーポイントの分離」で対応してください。【import_playbookとimport_tasksの違い】混同しやすいですが、
import_playbook と import_tasks は対象が根本的に異なります。import_playbook は hosts: を含むPlaybook全体を読み込みます。一方 import_tasks は hosts: を持たないtaskの列だけを読み込む別物です。site.ymlから役割別ファイルを束ねる用途には必ず import_playbook を使い、role内部でタスクファイルを分割する場合のみ import_tasks を使う、という使い分けを徹底してください。3. 環境別エントリーポイントの分離
stagingと本番で同じPlaybookを使いたいが、誤って本番に流してしまうリスクを下げたい場合は、エントリーポイントを環境ごとに分けます。# プロジェクト構成(環境分離版) . ├── site.yml ← 全環境一括(CI/CD用など) ├── staging.yml ← staging専用エントリーポイント ├── production.yml ← 本番専用エントリーポイント ├── webservers.yml ├── dbservers.yml └── inventory/ ├── staging/ │ └── hosts └── production/ └── hosts
# staging.yml --- - import_playbook: webservers.yml - import_playbook: dbservers.yml # staging実行 ansible-playbook -i inventory/staging/hosts staging.yml # 本番実行 ansible-playbook -i inventory/production/hosts production.yml
4. タグを使って特定Playbookだけ実行する
import_playbook にタグを付けると、その子Playbookのタスク全体にタグが伝播します。これを利用すると --tags オプションで特定の役割だけを実行できます。# site.yml(タグ付き) --- - import_playbook: lb.yml tags: lb - import_playbook: webservers.yml tags: web - import_playbook: dbservers.yml tags: db # Webサーバーの設定だけ実行 ansible-playbook -i inventory/hosts site.yml --tags web # 実行対象のタスク一覧を事前確認 ansible-playbook -i inventory/hosts site.yml --tags web --list-tasks
--list-tags で利用可能なタグを事前確認する習慣をつけると、意図しないタスクのスキップも防げます。# site.yml で利用可能なタグ一覧を確認する ansible-playbook -i inventory/hosts site.yml --list-tags # webタグのタスク一覧を事前確認してから実行する ansible-playbook -i inventory/hosts site.yml --tags web --list-tasks ansible-playbook -i inventory/hosts site.yml --tags web
5. Playbookのオペレーション別分割パターン
ホストグループ別の分割(webservers.yml / dbservers.yml)が基本パターンですが、同じサーバー群でも「誰がいつ実行するか」によって分割する方法もあります。初回セットアップ・アプリデプロイ・パッチ適用は、実行頻度もリスクも異なります。# オペレーション別分割の構造例 playbooks/ ├── setup.yml # 初回構築(サーバー追加時のみ実行) ├── deploy.yml # アプリデプロイ(頻繁に実行) └── patch.yml # OSパッチ適用(月次)
6. commonロールで全ホストのベースラインを統一する
どの分割パターンを選んでも、site.ymlの先頭でcommon.ymlを実行し、全ホストに共通設定を適用するのが実務の定石です。「どのサーバーでもSSHアクセスできる」「どのサーバーでもログのタイムスタンプが揃っている」という状態をインフラ全体で保証するのがcommonロールの役割です。commonロールに含める典型的な設定:
・ユーザー作成と公開鍵配置:運用担当者のSSHアクセス設定を全ホストで統一する
・タイムゾーンとNTP設定:ログのタイムスタンプを揃えてログ分析を容易にする
・最低限のパッケージインストール:vim, rsync, wget等の運用ツールを全台に配置する
・sshd_configのセキュリティ強化:rootログイン禁止・PasswordAuthentication noを全台統一する
# playbooks/common.yml --- - hosts: all become: yes roles: - common
hosts: all とすることで、inventoryのすべてのホストに共通設定が適用されます。site.ymlを設計する際は、このcommon.ymlを必ず先頭に置くようにします。コメントで意図を明記しておくと、後から読んだチームメンバーにも実行順序の理由が伝わります。# site.yml(commonロールを先頭に置く構成) --- # 全ホストのベースライン設定(必ず最初に実行) - import_playbook: playbooks/common.yml # インフラ基盤レイヤー - import_playbook: playbooks/dbservers.yml # アプリケーションレイヤー(DBより後) - import_playbook: playbooks/webservers.yml # 監視設定(全サービスが起動してから) - import_playbook: playbooks/monitoring.yml
7. 3層構成WebサーバーへのPlaybook適用実践例
ロードバランサー1台(lb01)、Webサーバー2台(web01・web02)、DBサーバー1台(db01)の構成で、site.yml設計の実際を確認します。# inventory/production/hosts [lb] lb01 ansible_host=192.168.10.10 [webservers] web01 ansible_host=192.168.10.21 web02 ansible_host=192.168.10.22 [dbservers] db01 ansible_host=192.168.10.31 [all:vars] ansible_user=deploy ansible_ssh_private_key_file=~/.ssh/deploy_key
# site.yml(3層構成完成形) --- # 1. 全4台にベースライン設定(NTP・ユーザー・SSH設定) - import_playbook: playbooks/common.yml # 2. DBを先に構成(WebサーバーからDBへの接続情報が必要なため) - import_playbook: playbooks/dbservers.yml # 3. Webサーバーは必ずDBの後で構成する - import_playbook: playbooks/webservers.yml # 4. LBはWebが起動してから設定(バックエンドIPをヘルスチェック後に登録) - import_playbook: playbooks/lb.yml
# playbooks/common.yml --- - hosts: all become: yes roles: - common # playbooks/dbservers.yml --- - hosts: dbservers become: yes roles: - mysql # playbooks/webservers.yml --- - hosts: webservers become: yes roles: - nginx - app # playbooks/lb.yml --- - hosts: lb become: yes roles: - haproxy
ansible-playbook -i inventory/production/hosts site.yml を実行すると、4台のサーバーが依存関係の順番に沿って設定されます。Webサーバーだけ再設定したいときは ansible-playbook -i inventory/production/hosts playbooks/webservers.yml を直接実行します。8. site.yml設計でよくある3つの落とし穴
import_playbookで設計する際に陥りやすいミスを確認しておきます。【落とし穴1】site.ymlにPlayを直接書いてしまう
site.ymlをエントリーポイントとして設計するなら、その中に
tasks: や roles: を直書きしないのが鉄則です。# NG: site.ymlにPlayを直接書く --- - name: Webサーバーの設定 hosts: webservers roles: - nginx # OK: import_playbookで委譲する --- - import_playbook: webservers.yml
【落とし穴2】環境ごとにroleを複製する
設定値を環境ごとに変えたいために
roles/nginx-staging/ と roles/nginx-production/ を作ってしまうケースがあります。roleのコードは1つに保ち、環境差異は group_vars/ かroleの defaults/main.yml で吸収するのが正しい設計です。roleを複製すると、片方を更新した際にもう一方への反映が漏れる「二重メンテナンス」が発生します。【落とし穴3】roleの中でhostsを指定する
roleはhostsを知らない設計が正しいため、role内のtasksにhostやdelegateを埋め込むとroleの再利用性が下がります。「どのホストに適用するか」の制御は、必ず呼び出し元のPlaybookに委ねましょう。
CI/CDへの組み込み(GitHub Actions連携)
PlaybookをGitで管理するだけでは不十分です。「誰がいつ実行したかが追跡できない」「lint違反のあるPlaybookが本番に流れてしまう」——こうした問題が手動実行への依存で必ず起きます。GitHub ActionsにPlaybookを組み込むことで、PRのたびに構文チェックが自動で走り、mainブランチへのマージを実行トリガーにできます。1. セルフホストランナーをコントロールノードに使う
GitHubがホストする標準ランナーはNATの外側に位置するため、プライベートなサーバーへのSSH接続ができません。社内ネットワーク内またはVPNアクセス可能なサーバーにセルフホストランナーを配置し、Ansibleのコントロールノードとして使うのが最もシンプルな設計です。セルフホストランナーから対象サーバーへのSSH(22/tcp)疎通は、Linux ポート確認の全コマンドを参考にして事前に確認してください。
2. PRトリガーで構文チェックとlintを自動実行する
# .github/workflows/ansible-syntax-check.yml name: Ansible Syntax Check on: pull_request: paths: - 'playbooks/**' - 'roles/**' - 'inventory/**' jobs: syntax-check: runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: Playbookの構文チェック run: | ansible-playbook playbooks/site.yml --inventory inventory/production/hosts.ini --syntax-check env: ANSIBLE_HOST_KEY_CHECKING: "False" - name: ansible-lintの実行 run: | ansible-lint playbooks/site.yml --profile production
3. mainマージ後にdry-runと本番適用を行う
本番への自動適用は「mainへのpush」をトリガーにして、dry-runと本番適用の2段階に分けます。# .github/workflows/ansible-deploy.yml name: Ansible Deploy on: push: branches: - main jobs: dry-run: runs-on: self-hosted steps: - uses: actions/checkout@v4 - name: SSH鍵の配置 run: | mkdir -p ~/.ssh echo "${{ secrets.ANSIBLE_SSH_PRIVATE_KEY }}" > ~/.ssh/ansible_rsa chmod 600 ~/.ssh/ansible_rsa - name: Vaultパスワードファイル作成 run: printf '%s' "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault_pass.txt - name: dry-run(--check --diff) run: | ansible-playbook playbooks/site.yml --inventory inventory/production/hosts.ini --vault-password-file /tmp/vault_pass.txt --check --diff --private-key ~/.ssh/ansible_rsa env: ANSIBLE_HOST_KEY_CHECKING: "False" - name: Vaultパスワードファイル削除 if: always() run: rm -f /tmp/vault_pass.txt deploy: needs: dry-run runs-on: self-hosted environment: production # 承認者によるレビューゲート steps: - uses: actions/checkout@v4 # ... (dry-runと同様にPlaybook本番適用)
printf '%s' を使うことで、echo で発生する末尾改行もなくせます。environment: production にEnvironment protection rulesで承認者を設定しておくと、dry-run完了後に本番適用ジョブが自動開始せず、担当者がGitHub上で承認するまで待機します。よくあるエラーと対処法
1. SSH接続エラー「UNREACHABLE」
# エラー例 fatal: [web01.example.com]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh", "unreachable": true} # 疎通確認 ansible -i inventory.yml web01.example.com -m ping # SSHで直接接続テスト ssh -i ~/.ssh/id_rsa admin@web01.example.com # インベントリの接続情報を確認 ansible-inventory -i inventory.yml --host web01.example.com
・SSH鍵の指定ミス:
ansible_ssh_private_key_file が正しいか確認・known_hostsへの未登録:初回接続時は
ansible_host_key_checking=false を一時的に設定・ファイアウォールのポートブロック:Linux ポート確認の全コマンド で22番ポートの状態を確認
・ユーザー名の誤り:
ansible_user の設定を見直す2. become(sudo)権限エラー
# エラー例 fatal: [web01.example.com]: FAILED! => {"msg": "Missing sudo password"} # 対処1:インタラクティブにパスワードを入力 ansible-playbook -i inventory.yml site.yml --ask-become-pass # 対処2:sudoersでパスワードなしを設定(NOPASSWD) # /etc/sudoers に以下を追加(visudo で編集) # ansibleuser ALL=(ALL) NOPASSWD: ALL
3. モジュールの実行に失敗する「MODULE FAILURE」
# エラー例 fatal: [web01.example.com]: FAILED! => {"changed": false, "msg": "No package matching 'httpd' found available"} # 対処:パッケージリポジトリの確認 # -vvv でより詳細なエラー内容を確認 ansible-playbook -i inventory.yml site.yml -vvv # 手動でパッケージ確認(アドホックコマンド) ansible -i inventory.yml webservers -m shell -a "dnf list available httpd" --become
4. Vault暗号化ファイルの復号に失敗する
ansible-vault で暗号化した変数ファイルを使っているときに「Decryption failed」が出る場合、パスワードの不一致が原因のほぼ全てです。CI/CD環境でGitHubシークレット経由でパスワードを渡す場合は、末尾改行の混入に要注意です。# 問題:echo は末尾に改行を付与するためパスワードが一致しない echo "$VAULT_PASSWORD" > /tmp/vault_pass.txt # NG # 解決:printf '%s' で末尾改行を除去してから書き出す printf '%s' "$VAULT_PASSWORD" > /tmp/vault_pass.txt # OK # 手動でVaultファイルを検証する ansible-vault decrypt group_vars/production/vault.yml --ask-vault-pass
5. 冪等性が担保されていない処理を書いてしまう
command モジュールや shell モジュールは冪等性がないため、毎回実行されます。同じ処理を繰り返してもサーバーの状態が変わらない書き方を意識しましょう。# NGパターン(毎回ユーザーを作ろうとする) - name: ユーザー作成 ansible.builtin.shell: useradd appuser # OKパターン(すでに存在していれば変更なし) - name: ユーザー作成(冪等) ansible.builtin.user: name: appuser state: present shell: /bin/bash
6. import_playbookにwhen:を付けてエラーになる
# NG: import_playbookにwhen:は使えない(Ansible 2.12以降include_playbookも廃止) - import_playbook: webservers.yml when: env == "production" # ERROR # OK: エントリーポイントを環境別に分離する # staging.yml / production.yml を別ファイルとして用意する(h3 3参照)
import_playbook は静的解析時に展開されるため、実行時変数を参照する when: が使えません。この制約が問題になる場合は、環境別エントリーポイント(staging.yml / production.yml)に分割する設計を検討してください。7. 「Could not match supplied host pattern」
インベントリに存在しないホスト名・グループ名をPlaybookのhosts: に指定した場合に発生します。# 警告例 [WARNING]: Could not match supplied host pattern, ignoring: webservers [WARNING]: No hosts matched, nothing to do # インベントリのグループ構成を確認する ansible-inventory -i inventory.yml --graph # 特定グループのホスト一覧を確認する ansible-inventory -i inventory.yml --list-hosts webservers
hosts: webservers とインベントリのグループ名が大文字小文字も含めて完全一致していることを確認してください。また、ansible-playbook をsite.ymlのあるディレクトリ以外から実行すると import_playbook の相対パスが解決されずエラーになる場合があります。常にプロジェクトルートから実行するか、絶対パスで指定する習慣をつけておくと安全です。8. 「ERROR! 'import_playbook' is not a valid attribute for a Play」が出る
import_playbookはPlaybookのトップレベルにしか書けません。tasksブロックの中やroleの中に書こうとするとこのエラーになります。site.ymlやwebservers.ymlの直下に書いていることを確認してください。# NG:hostsと同列に書こうとしている(Playbookの中のPlay定義内) - hosts: webservers import_playbook: other.yml # これは不可 # OK:トップレベルに書く(hostsと並列でなく独立したリスト項目として) - import_playbook: playbooks/webservers.yml - hosts: monitoring roles: - prometheus
9. 「ERROR! the file ... does not exist」が出る
import_playbookのパスはsite.ymlからの相対パスで解決されます。実行時のカレントディレクトリではなく、site.ymlの置き場所(通常はプロジェクトルート)からの相対パスです。# 実行は常にプロジェクトルートから行う cd /home/deploy/ansible-project ansible-playbook -i inventory/production/hosts site.yml # site.yml が /home/deploy/ansible-project/site.yml にある場合 # playbooks/webservers.yml は # /home/deploy/ansible-project/playbooks/webservers.yml を参照する
ansible-playbook /path/to/site.yml と絶対パスで実行した場合も、相対パスの起点はsite.ymlの場所になります。常にプロジェクトルートからの実行を習慣にしてください。10. site.ymlを実行したのに一部のPlaybookがスキップされる
import_playbookで読み込んだPlaybookのhosts: に指定したグループがインベントリに存在しない場合、そのPlaybookは「0台に対して実行」という扱いでスキップされます。エラーにはならないため気づきにくい挙動です。# 対象ホスト一覧を事前に確認してスキップを事前検出する ansible-playbook --list-hosts site.yml # インベントリのグループ一覧を確認する ansible-inventory -i inventory/production/hosts --graph
ansible-playbook --list-hosts site.yml で対象ホスト一覧を事前確認する習慣をつけておくと、「実行したのに設定が反映されていない」という見えにくいミスを防げます。11. import_playbookとimport_tasksを混同する
site.yml内でimport_tasks を書いてしまうケースが初学者に多く見られます。import_tasks は hosts: を持たないtaskの列だけを読み込むもので、Playbook全体(hosts:・roles:・tasks: を含む)を読み込む import_playbook とは対象が異なります。# NG: site.ymlでimport_tasksを使ってしまう --- - import_tasks: webservers.yml # hostsを含むPlaybookには使えない # OK: Playbook全体を読み込む場合はimport_playbook --- - import_playbook: webservers.yml # import_tasksが適切な場面:role内部でのtaskファイル分割 # roles/nginx/tasks/main.yml --- - import_tasks: install.yml # tasks/install.yml のtask列を読む - import_tasks: configure.yml # tasks/configure.yml のtask列を読む
ansible-playbookの主要オプション早見表
| やりたいこと | コマンド・オプション |
|---|---|
| 基本実行 | ansible-playbook -i inventory.yml site.yml |
| ドライラン(変更なし確認) | ansible-playbook -i inventory.yml site.yml --check |
| 差分付きドライラン | ansible-playbook -i inventory.yml site.yml --check --diff |
| 特定ホストのみ実行 | ansible-playbook -i inventory.yml site.yml --limit web01 |
| タグ指定で部分実行 | ansible-playbook -i inventory.yml site.yml --tags install |
| sudo パスワードを対話入力 | ansible-playbook -i inventory.yml site.yml --ask-become-pass |
| 変数をコマンドラインで渡す | ansible-playbook -i inventory.yml site.yml -e "env=production" |
| デバッグログの表示 | ansible-playbook -i inventory.yml site.yml -vvv |
| 並列実行数を制限(デフォルト5) | ansible-playbook -i inventory.yml site.yml -f 2 |
| ステップ実行(タスクごとに確認) | ansible-playbook -i inventory.yml site.yml --step |
| ローリングデプロイ(N台ずつ) | playbook内で serial: N を指定 |
| タスク一覧を事前確認 | ansible-playbook -i inventory.yml site.yml --list-tasks |
| 対象ホスト一覧を事前確認 | ansible-playbook -i inventory.yml site.yml --list-hosts |
| 複数Playbookをエントリーポイントで束ねる | site.yml に import_playbook を羅列する |
本記事のまとめ
ansible-playbook コマンドは、Linux サーバーの構成管理を「手作業からコード」へ転換する強力なツールです。YAML 形式の playbook でサーバーの「あるべき状態」を定義し、何度実行しても同じ結果になる冪等性が最大の強みです。さらに GitHub Actions と組み合わせることで、構文チェックから本番適用までを自動化し、手動実行の属人化リスクを設計で解消できます。| やること | ポイント |
|---|---|
| インベントリ定義 | ターゲットサーバーをINI/YAML形式でグループ化して管理する |
| 本番適用前の確認 | --check --diffでドライランと変更差分を視覚化する |
| 変更時だけ再起動 | ハンドラー(handlers)でサービス再起動を安全に制御する |
| playbookの整理・再利用 | ロール(roles)で機能ごとに分割して管理する |
| roleとPlaybookの責任分担 | roleは「何を設定するか」、Playbookは「どこに配るか」で責任を分ける |
| 1ミドルウェア1role | roleを役割単位で分割して再利用性と変更の影響範囲を最小に保つ |
| roleの変数設計 | 上書き可能な設定はdefaults/・固定値はvars/に分けて管理する |
| 段階的なデプロイ | serialオプションで一度に更新する台数を制限してリスクを下げる |
| 複数Playbookの一元管理 | site.yml×import_playbookでエントリーポイントを1本に集約する |
| import_playbookとimport_tasksの使い分け | Playbook全体はimport_playbook・task列のみはimport_tasksで読み込む |
| オペレーション別分割 | setup/deploy/patchでPlaybookを分割して実行権限と責任を分ける |
| ベースライン設定の統一 | commonロールをhosts:allで全ホストに適用してSSH・NTP・パッケージを統一する |
| 環境間の誤適用を防ぐ | staging.yml/production.ymlを分けてエントリーポイントを分離する |
| CI/CDへの組み込み | GitHub Actions+セルフホストランナーでPlaybook検証・デプロイを自動化 |
| エラー発生時のデバッグ | -vvvで詳細ログを確認し、原因を特定してから対処する |
手作業で30分かかる初期設定が、ansible-playbook 1回で3分に短縮された——そういう体験が、自動化への確信を生みます。まずは手元の検証環境で試してみてください。
Linux Master Pro Seminar(2日間ハンズオン)では Ansible・systemd・セキュリティ設定まで、
実際のサーバーを触りながら自動化と構成管理を習得できます。
>> セミナー詳細・お申込みはこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 次のページへ:mysqldumpコマンドでMySQLデータベースをバックアップする方法|フルバックアップから特定テーブル・リストアまで
- 前のページへ:pkillコマンドとpgrepコマンドでプロセスを名前で操作する方法|kill・psの代替と実践例
- この記事の属するカテゴリ:Linuxtipsへ戻る

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