Ansibleサーバー設定自動化:6ヶ月の本番運用で分かったこと

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

なぜ他のツールを差し置いてAnsibleを選んだのか

6ヶ月前、私はUbuntuサーバーを3台、手作業で管理していました。SSHでログインしてスクリプトを実行し、何も壊れないように祈るだけ。4台目のサーバーを追加したとき、ついに限界に達しました。あるサーバーがnginx 1.18を実行していて、他のサーバーは1.24だったため、try_filesディレクティブの動作がバージョン間で異なり、半日が無駄になりました。その日の午後、ちゃんとした構成管理ツールを導入することを決意しました。

候補はAnsible、Puppet、Chef、SaltStack。ドキュメントを読むだけでなく、実際にテストVMに各ツールを立ち上げてみた週末を経て、Ansibleを選びました。それ以来、毎週本番環境で運用しています。6ヶ月間の実態をお伝えします。

アプローチ比較:Ansible vs 他のツール

ブログ記事を読むだけでは限界があります。実際に何かを設定しようとした瞬間に違いが明らかになります。だからこそ、決断する前に各ツールを実際に触ってみることに時間を費やしました。

手動Bashスクリプト

Bashスクリプトは誰もが最初に使う基本手段です。サーバーが1台なら問題ありません。2台目を追加した途端に欠点が露呈します——冪等性なし、状態追跡なし、クリーンなロールバックなし。12ステップのうち7ステップ目で失敗したスクリプトは、半端に設定されたマシンを残し、元に戻す方法も定かではありません。簡単な一時的なタスクには今でもBashを使いますが、これは構成管理ではありません。

PuppetとChef

どちらも成熟した実績あるツールです。問題は付随するものです。Puppetには独自のDSLを習得する必要があります。ChefはRubyが必要です。どちらも24時間365日稼働する専用マスターサーバーが必要です。15台のサーバーを管理する個人エンジニアや2人チームにとって、Puppetのインフラを立ち上げるだけで丸1日かかり、その後も維持管理が別の継続的な作業になります。それなら午後をプレイブック作成に費やした方がましです。

Terraform

Terraformは優れたツールですが、異なるレイヤーで機能します。インフラのプロビジョニングを担当します:VMの起動、データベースの作成、VPCネットワークの設定。起動後にVM内部で何が起きるかは、まったく別の問題です。私はTerraformとAnsibleを組み合わせて使っています。一方がサーバーを作成し、もう一方が設定します。

Ansible

管理対象ホストにエージェントをインストールする必要なし。マスターサーバーのメンテナンスも不要。YAMLプレイブックはチェックリストのように読めます。重い処理はSSHが担います。LinuxとSSHに慣れている人なら、Ansibleの習得は一から新しいシステムを学ぶというより、既存の知識を拡張する感覚です。

コツをつかんだら、3時間かかっていたサーバーセットアップを約12分に短縮できました。月に複数台のサーバーをプロビジョニングする場合、この時間短縮は積み重なっていきます。

6ヶ月後のメリットとデメリット

実際にうまく機能すること

  • エージェントレスアーキテクチャ — 管理対象ホストにインストールするものは何もありません。SSHアクセスがあれば十分です。既存の10台のサーバーに展開するのに1時間もかかりませんでした。サーバー側の変更はゼロです。
  • 冪等性 — 同じプレイブックを10回実行しても結果は同じです。4回目や5回目の実行での副作用を心配することなく、CI/CDパイプラインにプレイブック実行を組み込めます。
  • 読みやすいプレイブック — Ansibleに触れたことのないジュニアのシステム管理者にプレイブックを渡しました。私の説明なしに上から下まで読んでくれました。YAMLの記述はPuppet DSLやChefのレシピとは異なり、平易な言葉に近い形でマッピングされています。
  • 豊富なモジュールライブラリ — パッケージインストール、ユーザー管理、ファイルテンプレート、サービス管理、Docker、AWS、GCP——ほぼ全てのものにモジュールがあります。6ヶ月間の日常使用で、生のシェルコマンドが必要だったのはおそらく5回程度です。
  • インベントリの柔軟性 — 小規模な固定セットアップには静的ファイルで十分です。AWSやGCPで動的インベントリプラグインに切り替えれば、手動でファイルを編集することなくサーバーが出入りします。

Ansibleの弱点

  • スケール時の速度 — デフォルトでは順次実行です。50台のサーバーにまたがるプレイブックは、ネイティブ並列実行のツールより明らかに時間がかかります。forks設定は助けになります——本番ではforks = 20を使っています——しかし200台以上になると本当のボトルネックになります。
  • エラーメッセージ — タスクが失敗すると、出力が非常に分かりにくいことがあります。私の通常のワークフロー:-vvvを追加し、出力をファイルにパイプして、「FAILED」または「fatal」を検索します。エレガントではありませんが、機能します。
  • ドリフト検出機能なし — Ansibleはサーバーの設定ドリフトを継続的に監視しません。プレイブック実行の間に誰かが/etc/nginx/nginx.confを手動編集しても、再実行するまでAnsibleは気づきません。Puppetはこれをネイティブに処理しており、これは実質的な欠点です。
  • 変数優先順位の複雑さ — Ansibleには22段階の変数優先順位があります。グループ変数、ホスト変数、ロールのデフォルト、追加変数を同時に扱うようになると、実際にどの値が優先されるかを把握するためにドキュメントを毎回確認する必要があります。

小〜中規模チームへの推奨セットアップ

6ヶ月の試行錯誤を経て、これが私が推奨する出発点となる構造です。大規模な再編成なしに3台から30台まできれいにスケールします。

ansible/
├── inventory/
│   ├── production
│   └── staging
├── group_vars/
│   ├── all.yml
│   ├── webservers.yml
│   └── dbservers.yml
├── roles/
│   ├── common/
│   │   ├── tasks/main.yml
│   │   └── templates/
│   ├── nginx/
│   └── docker/
├── playbooks/
│   ├── site.yml
│   ├── webservers.yml
│   └── deploy.yml
└── ansible.cfg

ここで最も重要な3つの決断:

  • 本番とステージング用にインベントリファイルを分ける。ステージングではなく本番に対して破壊的なプレイブックを実行した一度きりの経験が、この教訓を永久に刻み込んでくれました。
  • 再利用するもの全てにロールを使う——nginxのセットアップ、Dockerのインストール、共通のセキュリティ強化。ロールは自己完結型でテスト可能で、プロジェクト間で共有しやすいです。
  • 環境固有の設定をプレイブックロジックから分離するためにグループ変数を使う。本番はポート443を使い、ステージングは8443を使います。その違いは変数に属するものであり、プレイブック自体にハードコードすべきではありません。

最小限だが理にかなったansible.cfg

[defaults]
inventory = inventory/production
remote_user = ubuntu
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
forks = 10
callbacks_enabled = profile_tasks

[privilege_escalation]
become = True
become_method = sudo

実装ガイド:ゼロからプレイブック実行まで

ステップ1:Ansibleのインストール

# Ubuntu/Debianコントロールマシンの場合
sudo apt update && sudo apt install -y ansible

# インストールを確認
ansible --version

ステップ2:インベントリの作成

インベントリファイルはAnsibleにどのサーバーを管理し、どのようにグループ化するかを伝えます。最初はシンプルに保ちましょう。

# inventory/production
[webservers]
web01 ansible_host=192.168.1.10
web02 ansible_host=192.168.1.11

[dbservers]
db01 ansible_host=192.168.1.20

[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_ed25519

プレイブックを1行も書く前に、接続性を確認しましょう:

ansible all -i inventory/production -m ping

3つの緑色のpong応答が返ってきたら準備完了です。

ステップ3:最初のプレイブックを書く

具体的なものから始めましょう。全てのサーバーに適用するベースライン設定は、最も価値の高い最初のプレイブックです——新しいサーバーに自動的に実行され、既存のサーバーの一貫性を保ちます。

# playbooks/site.yml
---
- name: 全サーバーに共通設定を適用
  hosts: all
  become: true

  tasks:
    - name: aptパッケージキャッシュを更新
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: 必須パッケージをインストール
      apt:
        name:
          - vim
          - curl
          - htop
          - ufw
          - fail2ban
        state: present

    - name: UFWファイアウォールを有効化
      ufw:
        state: enabled
        policy: deny

    - name: ファイアウォールでSSHを許可
      ufw:
        rule: allow
        port: '22'
        proto: tcp

    - name: fail2banが実行中であることを確認
      service:
        name: fail2ban
        state: started
        enabled: yes

ステップ4:プレイブックを実行する

# まずドライラン——変更を加えずに何が変わるか確認
ansible-playbook playbooks/site.yml --check

# 変更を適用
ansible-playbook playbooks/site.yml

# 特定のホストに限定
ansible-playbook playbooks/site.yml --limit web01

# デバッグ用に詳細出力で実行
ansible-playbook playbooks/site.yml -v

ステップ5:ロールで整理する

プレイブックはすぐに大きくなります。50行を超えたら、繰り返しのロジックをロールに抽出し始めましょう。

# ロールのスキャフォールドを作成
ansible-galaxy init roles/nginx

最小限のnginxロールタスクファイル:

# roles/nginx/tasks/main.yml
---
- name: nginxをインストール
  apt:
    name: nginx
    state: present

- name: nginx設定をコピー
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  notify: nginxを再起動

- name: nginxが起動していることを確認
  service:
    name: nginx
    state: started
    enabled: yes

次に、プレイブックで参照します:

- name: Webサーバーを設定
  hosts: webservers
  become: true
  roles:
    - common
    - nginx

6ヶ月前の自分に伝えたいこと

本番環境を完全に反映したステージング環境を構築し、全てのプレイブックをそこで先に実行しましょう。私はつらい経験からこれを学びました——UFWファイアウォールのプレイブックを本番サーバーで直接テストし、ロックアウトされ、VPSコンソール経由でアクセスを回復するのに40分費やしました。--checkモードはほとんどの問題を検出しますが、全てではありません。起動に5分かかる使い捨てVMは、その価値があります。

初日からAnsibleのコードをGitにコミットしましょう。プレイブックはコードとしてのインフラです——そのように扱いましょう。変更を適用する前にgit diff、何かが壊れたらgit blame。この方法で、そうでなければ完全に忘れていたであろう設定ミスを何度も発見しました。

学習曲線は思ったより短いです。最初の1週間で、1年間手動でやっていたサーバーセットアップを自動化しました。4週目には、日常的なタスクのために個々のサーバーにSSHするのを完全にやめました。2ヶ月が経つ頃には、新しいサーバーのオンボーディングが半日仕事からコーヒーブレイク程度になりました。

Share: