目次
- 手作業セットアップの限界と冪等性
- 手順書は「実行した人」に依存する
- 冪等性:何度実行しても同じ結果になる
- Ansibleの仕組み|エージェントレスとPush型
- SSHで入ってPythonを実行するだけ
- インベントリ・Playbook・ロールの構造
- 3つの部品の役割
- 最小Playbook例|Nginxとアプリの配置
- Nginxロール:インストール・設定・再読み込み
- アプリロールとsite.yml
- Terraformとの役割分担
- 「箱を作る」のがTerraform、「中身を整える」のがAnsible
- 秘密情報の扱い|ansible-vault
- パスワードを平文でGitに入れない
- トラブル事例:Playbookを流したら本番の設定が消えた
- Ansible導入の進め方
- まとめ
新しいサーバーを1台追加するたびに、以前の手順書を見ながらパッケージを入れ、設定ファイルをコピーし、ユーザーを作り……気づけば半日が過ぎている。しかも「前回入れたはずの設定が今回のサーバーには入っていない」といった差分が積み重なり、本番の2台がいつの間にか微妙に違う状態になっている。こうした悩みに対する定番の答えがAnsibleです。
この記事では、手作業セットアップの限界と「冪等性」という考え方、Ansibleがエージェント不要で動く仕組み、インベントリ・Playbook・ロールという3つの構造、Nginxとアプリを配置する最小のPlaybook例、Terraformとの役割分担、そしてansible-vaultによる秘密情報の扱いまでを解説します。IaC全体の比較はIaCとは?インフラのコード化入門、Terraformの基本はTerraform入門で扱っているので、ここではAnsibleに集中します。
手作業セットアップの限界と冪等性
手順書は「実行した人」に依存する
手順書ベースの運用が破綻するのは、手順書が正しくても、実行するたびに結果が変わるからです。コマンドの打ち間違い、手順の飛ばし、OSのバージョン違いによる挙動の差、「ついでに」入れた設定。これらが積もると、同じ役割のサーバーが同じ状態ではなくなり、「Aでは動くのにBでは動かない」というトラブルの温床になります。さらに、手順書を書いた本人が離れると、誰も再現できなくなります。
冪等性:何度実行しても同じ結果になる
構成管理ツールの核となる考え方が冪等性(べきとうせい)です。「Nginxをインストールする」というコマンドを2回実行すると、2回目はエラーになるか二重にインストールしようとしますが、「Nginxがインストールされた状態にする」というあるべき状態の宣言なら、何度実行しても結果は同じです。すでにその状態ならAnsibleは何もせず「ok」と報告し、違っていれば修正して「changed」と報告します。
この性質があるため、Ansibleは「新規構築」だけでなく「設定の一括変更」「意図しない変更の検出と修復(ドリフト検出)」にも使えます。毎晩Playbookを流しておけば、誰かが手で変えた設定は翌朝には戻っています。
Ansibleの仕組み|エージェントレスとPush型
SSHで入ってPythonを実行するだけ
Chefや旧来のPuppetは、管理対象のサーバーにエージェントを常駐させ、エージェントが中央サーバーから設定を取りに行く(Pull型)方式でした。Ansibleは逆に、管理者の手元(コントロールノード)からSSHで対象サーバーに接続し、小さなPythonスクリプト(モジュール)を転送して実行し、結果を受け取ります(Push型)。対象サーバーに必要なのはSSH接続とPythonだけで、専用のエージェントもポートも不要です。
この設計が中小規模の運用に向いている理由は次のとおりです。
- 既存のサーバーに何も入れずに始められる。SSHで入れる相手ならすぐ管理対象にできる。
- 中央サーバーの構築や維持が不要。ノートPCやCIランナーがコントロールノードになる。
- SSHの鍵管理や踏み台経由の接続を、そのままAnsibleでも使える。
# インストール(macOS/Linux。pipxで隔離して入れるのが管理しやすい)
pipx install ansible
# 疎通確認:インベントリの全ホストにpingモジュールを実行
ansible all -i inventory.ini -m ping
# アドホックに1コマンド実行(-b は become=sudo)
ansible web -i inventory.ini -b -m apt -a "name=nginx state=present update_cache=yes"
インベントリ・Playbook・ロールの構造
3つの部品の役割
| 部品 | 役割 | ファイルの例 |
|---|---|---|
| インベントリ | 「どのサーバーに」を定義する。ホスト一覧とグループ分け、接続情報 | inventory.ini、inventory/production.yml |
| Playbook | 「どのグループに、どの順で、何をするか」を定義するYAML | site.yml、deploy.yml |
| ロール | 「Nginxの設定」のような再利用可能な作業のまとまり。タスク・テンプレート・変数・ハンドラを決まったディレクトリ構成で持つ | roles/nginx/、roles/app/ |
# inventory.ini:グループごとにホストを列挙し、グループ変数を付ける
[web]
web1 ansible_host=10.0.11.21
web2 ansible_host=10.0.11.22
[db]
db1 ansible_host=10.0.21.5
[web:vars]
ansible_user=ops
ansible_ssh_common_args='-o ProxyJump=bastion'
# ディレクトリ構成の例
.
├── inventory.ini
├── site.yml
├── group_vars/
│ └── web.yml # webグループ共通の変数
└── roles/
├── nginx/
│ ├── tasks/main.yml
│ ├── templates/app.conf.j2
│ └── handlers/main.yml
└── app/
├── tasks/main.yml
└── templates/app.service.j2
最初はsite.yml1枚にすべて書いても構いません。タスクが増えて見通しが悪くなったら、役割ごとにロールへ切り出します。ロールの構造は決まっているため、他の人が書いたロールも同じ場所を見れば理解できるのが利点です。
最小Playbook例|Nginxとアプリの配置
Nginxロール:インストール・設定・再読み込み
# roles/nginx/tasks/main.yml
- name: Nginxをインストール
ansible.builtin.apt:
name: nginx
state: present
update_cache: yes
- name: サイト設定を配置(テンプレートから生成)
ansible.builtin.template:
src: app.conf.j2
dest: /etc/nginx/sites-available/app.conf
mode: "0644"
notify: reload nginx
- name: サイトを有効化
ansible.builtin.file:
src: /etc/nginx/sites-available/app.conf
dest: /etc/nginx/sites-enabled/app.conf
state: link
notify: reload nginx
- name: Nginxを起動・自動起動に設定
ansible.builtin.service:
name: nginx
state: started
enabled: yes
# roles/nginx/handlers/main.yml
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
# roles/nginx/templates/app.conf.j2(変数はgroup_varsから)
server {
listen 80;
server_name {{ server_name }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
ここで重要なのがnotifyとhandlersの関係です。設定ファイルが「変更されたときだけ」Nginxをリロードし、変更がなければ何もしません。これが冪等性を保ちながらサービスを不必要に再起動しない仕組みです。Nginx自体の設定項目はLinux×Nginx入門を参照してください。
アプリロールとsite.yml
# roles/app/tasks/main.yml(Gitからコードを取得しsystemdで起動する例)
- name: アプリ用ユーザーを作成
ansible.builtin.user:
name: app
system: yes
shell: /usr/sbin/nologin
- name: コードを取得
ansible.builtin.git:
repo: "{{ app_repo }}"
dest: /opt/app
version: "{{ app_version }}"
notify: restart app
- name: 依存をインストール
ansible.builtin.pip:
requirements: /opt/app/requirements.txt
virtualenv: /opt/app/.venv
- name: systemdユニットを配置
ansible.builtin.template:
src: app.service.j2
dest: /etc/systemd/system/app.service
notify: restart app
- name: アプリを起動
ansible.builtin.systemd:
name: app
state: started
enabled: yes
daemon_reload: yes
# site.yml
- hosts: web
become: yes
roles:
- nginx
- app
# 実行。--check は変更を適用せず差分だけ表示(ドライラン)、--diff で内容を表示
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml
本番に流す前に必ず--check --diffで「何が変わるか」を確認する習慣をつけてください。systemdユニットの書き方はsystemdとは?Linuxのサービス管理で扱っています。
Terraformとの役割分担
「箱を作る」のがTerraform、「中身を整える」のがAnsible
TerraformとAnsibleはどちらもIaCツールですが、得意な層が違います。Terraformはクラウドのリソース(VPC、EC2、RDS、ロードバランサー)を作成・変更・削除する「インフラのプロビジョニング」に強く、Ansibleは作られたサーバーのOSの中(パッケージ、設定ファイル、サービス、ユーザー)を整える「構成管理」に強い。両方使う場合の典型的な流れは次のとおりです。
- TerraformでEC2やネットワークを作る。出力(IPアドレスやタグ)をAnsibleのインベントリに渡す。
- AnsibleでOS設定、ミドルウェア、アプリを配置する。
- 以後、インフラの変更はTerraform、サーバー内の変更はAnsibleで行い、手作業を排除する。
| やりたいこと | 使うツール | 理由 |
|---|---|---|
| サーバーを3台に増やす | Terraform | クラウドAPIの操作 |
| 全サーバーのNginx設定を変える | Ansible | OS内の設定変更 |
| セキュリティグループの穴をふさぐ | Terraform | クラウドリソースの属性 |
| OSのセキュリティ更新を一斉適用する | Ansible | パッケージ操作 |
| コンテナ化されたアプリをKubernetesに配る | どちらも不向き(GitOps系ツール) | コンテナ環境ではイメージが「構成」を担う |
コンテナ中心の環境では、サーバー内の構成はDockerイメージに焼き込まれるため、Ansibleの出番は「ホストOSの初期設定」程度に減ります。その世界での配置の考え方はGitOps入門で扱っています。逆に、EC2やVPS上で直接アプリを動かす構成では、Ansibleが最も費用対効果の高い自動化手段です。
秘密情報の扱い|ansible-vault
パスワードを平文でGitに入れない
Playbookにはデータベースのパスワードや外部APIのキーが必要になりますが、平文でリポジトリに入れてはいけません。Ansibleに同梱されているansible-vaultは、変数ファイルをパスワードで暗号化し、実行時に復号します。暗号化されたファイルはGitにコミットでき、復号用のパスワードだけを別経路(パスワードマネージャー、CIのシークレット)で管理します。
# 秘密変数ファイルを暗号化して作成
ansible-vault create group_vars/web/vault.yml
# 中身(エディタが開く)
# vault_db_password: "S3cr3t!"
# 既存ファイルの編集・閲覧
ansible-vault edit group_vars/web/vault.yml
ansible-vault view group_vars/web/vault.yml
# 実行時にパスワードを渡す(対話入力 or パスワードファイル)
ansible-playbook -i inventory.ini site.yml --ask-vault-pass
ansible-playbook -i inventory.ini site.yml --vault-password-file ~/.vault_pass
# 平文の変数ファイル側では vault_ 接頭辞の変数を参照する(検索性のため)
# group_vars/web/vars.yml
# db_password: "{{ vault_db_password }}"
より大規模になったら、VaultやAWS Parameter Storeから実行時に取得するlookupプラグインへ移行します。秘密情報の管理方針全体はシークレット管理入門を参照してください。
トラブル事例:Playbookを流したら本番の設定が消えた
症状:Nginxの設定変更をAnsibleで適用したところ、本番で動いていた別サイトの設定が消え、そのサイトが停止した。
原因:本番サーバーには、Ansible導入前に手作業で追加した2つ目のサイト設定がsites-enabledに置かれていた。Playbookは「Ansibleが管理する設定以外は削除する」タスク(不要ファイルの掃除)を含んでおり、手作業で入れた設定が「管理外」として消された。ステージングには手作業の設定がなかったため、事前確認で気づけなかった。
対処:バックアップから設定を復元して復旧。恒久対策として、(1)Ansible導入時に既存サーバーの設定をすべて棚卸しし、Playbookに取り込む、(2)本番への初回適用は必ず--check --diffで削除される項目を確認する、(3)ステージングを本番と同じ手順で作り直し、環境差をなくす、の3点を実施した。「あるべき状態を宣言する」ツールは、宣言されていないものを容赦なく扱う、という性質を理解しておく必要があります。
Ansible導入の進め方
- まず1台のステージングサーバーを対象に、Nginx+アプリのPlaybookを書き、何度流しても「changed=0」になることを確認する。
- 既存の本番サーバーの設定を棚卸しし、Playbookに反映する。差分は
--check --diffで確認する。 - 本番に適用し、以後の変更はすべてPlaybook経由にする。手作業での変更を禁止する。
- PlaybookをGitで管理し、変更はプルリクエストでレビューする。秘密情報はansible-vaultで暗号化する。
- CIから定期実行して、設定のズレ(ドリフト)を検出・修復する。
まとめ
Ansibleは、SSHとPythonだけで動くエージェントレスの構成管理ツールで、「あるべき状態を宣言し、何度流しても同じ結果になる」冪等性によって、手作業セットアップの再現性の問題を解消します。インベントリで対象を、Playbookで手順を、ロールで再利用単位を定義し、notifyとhandlersで変更時だけサービスを再読み込みする。Terraformでクラウドの箱を作り、Ansibleで中身を整えるという分担を守り、秘密情報はansible-vaultで暗号化してGitに置く。これが基本形です。
まずは開発用のサーバーを1台用意し、この記事のNginx+アプリのPlaybookを流して「changed=0」になるまで整えてみてください。手順書に依存しない運用への移行や、Terraform・Ansibleを組み合わせたインフラ自動化の設計にお困りの際は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します