2026年7月28日

2026年7月28日

Ansibleでサーバー設定を自動化する方法|インストールから初期設定プレイブックまで

Ansibleを使うと、新しいVPSを借りるたびに手でやっていた「ユーザー作成、SSH鍵の登録、パスワードログインの無効化、ファイアウォール設定、パッケージ更新」を1つのファイル(プレイブック)にまとめ、何台に対しても同じ結果で実行できます。この記事では、手元のPCにAnsibleを入れるところから、Ubuntuサーバーの初期設定を自動化するプレイブックを書き、安全に試して本番に流すまでを順に説明します。

Ansibleの仕組みと準備するもの

Ansibleは、操作する側(コントロールノード)からSSHで対象サーバー(マネージドノード)に接続し、手順を実行するツールです。対象サーバーに常駐エージェントを入れる必要はなく、SSHで入れることとPythonが動くことが条件です。UbuntuやDebian、RHEL系の一般的なサーバーイメージにはPythonが入っているため、多くの場合そのまま使えます。

コントロールノードはLinuxかmacOSが前提です。WindowsのPCから使う場合は、WSL(Windows Subsystem for Linux)上のUbuntuなどにインストールします。

もう1つ重要なのが「冪等性(べきとうせい)」という考え方です。Ansibleのモジュールは「この状態になっていてほしい」という宣言で書き、すでにその状態なら何もしません。そのため同じプレイブックを何度流しても、2回目以降は変更が出ないのが正常です。シェルスクリプトとの一番の違いはここで、後から設定を1行足して全サーバーに再実行する、という運用ができます。

Ansibleをインストールする

公式ドキュメントでは、Pythonのツールを隔離して入れられる pipx を使う方法が案内されています(2026年10月現在)。

# Ubuntu / WSL の例
sudo apt update
sudo apt install -y pipx
pipx ensurepath
# シェルを開き直してから
pipx install --include-deps ansible
ansible --version

ansible パッケージには、本体の ansible-core に加えて、よく使うコレクション(community.general や ansible.posix など)が含まれます。本体だけを入れたい場合は ansible-core を指定し、必要なコレクションを ansible-galaxy collection install で追加します。この記事のプレイブックは community.general と ansible.posix を使うので、迷ったら ansible を入れておくと手間がかかりません。

OSのパッケージ(apt install ansible)でも入りますが、配布元によっては版が古いことがあります。チームで使うなら、メンバー間で版をそろえておくと「自分の環境では動いた」という食い違いを減らせます。

インベントリで対象サーバーを定義する

次に、どのサーバーを操作するかを書いた「インベントリ」を作ります。作業用ディレクトリを1つ用意し、その中にファイルを置いていきます。

# inventory.ini
[web]
web1 ansible_host=203.0.113.10
web2 ansible_host=203.0.113.11

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

[web] はグループ名で、プレイブックから「webグループ全体」に対して処理を指定できます。IPアドレスは例なので、自分のサーバーに置き換えてください。ansible_user は最初に接続するユーザーで、クラウドのイメージなら ubuntu や ec2-user、VPSなら root で入る場合もあります。

同じディレクトリに ansible.cfg を置くと、毎回のオプション指定を省けます。

# ansible.cfg
[defaults]
inventory = ./inventory.ini

接続できるかどうかは、ping モジュールで確認します。これはネットワークのpingではなく、SSH接続とPythonの実行ができるかを確かめるものです。

ansible web -m ansible.builtin.ping

各サーバーから "ping": "pong" が返れば準備は完了です。ここで失敗する場合は、プレイブックを書く前に原因をつぶします。多いのは、初回接続時のホスト鍵の確認で止まっている、鍵ファイルのパスや接続ユーザーが違う、サーバー側のファイアウォールでSSHが閉じている、の3つです。一度 ssh ubuntu@203.0.113.10 のように手で接続できるか試すと切り分けが早くなります。

初期設定のプレイブックを書く

ここからが本題です。次のプレイブックは、Ubuntuサーバーに対して「パッケージ更新、作業用ユーザーの作成と鍵登録、SSHの設定強化、UFWの有効化、自動セキュリティ更新」をまとめて行います。

# site.yml
- name: Ubuntu サーバーの初期設定
  hosts: web
  become: true
  vars:
    admin_user: deploy
    admin_pubkey: "{{ lookup('ansible.builtin.file', '~/.ssh/id_ed25519.pub') }}"
    ssh_port: 22

  tasks:
    - name: パッケージ一覧を更新し、インストール済みパッケージを更新する
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600
        upgrade: safe

    - name: 必要なパッケージを入れる
      ansible.builtin.apt:
        name:
          - ufw
          - unattended-upgrades
        state: present

    - name: タイムゾーンを日本時間にする
      community.general.timezone:
        name: Asia/Tokyo

    - name: 作業用ユーザーを作る
      ansible.builtin.user:
        name: "{{ admin_user }}"
        shell: /bin/bash

    - name: 作業用ユーザーに sudo を許可する
      ansible.builtin.copy:
        dest: "/etc/sudoers.d/90-{{ admin_user }}"
        owner: root
        group: root
        mode: "0440"
        content: "{{ admin_user }} ALL=(ALL) NOPASSWD:ALL\n"
        validate: /usr/sbin/visudo -cf %s

    - name: 作業用ユーザーに公開鍵を登録する
      ansible.posix.authorized_key:
        user: "{{ admin_user }}"
        key: "{{ admin_pubkey }}"
        state: present

    - name: SSH の追加設定を置く
      ansible.builtin.copy:
        dest: /etc/ssh/sshd_config.d/00-hardening.conf
        owner: root
        group: root
        mode: "0644"
        content: |
          PermitRootLogin no
          PasswordAuthentication no
          KbdInteractiveAuthentication no
      notify: Reload ssh

    - name: SSH 設定の文法を確認する
      ansible.builtin.command: sshd -t
      changed_when: false

    - name: UFW で SSH を許可する
      community.general.ufw:
        rule: allow
        port: "{{ ssh_port }}"
        proto: tcp

    - name: UFW で HTTP/HTTPS を許可する
      community.general.ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop:
        - "80"
        - "443"

    - name: UFW を有効にする(既定は受信拒否)
      community.general.ufw:
        state: enabled
        default: deny
        direction: incoming

  handlers:
    - name: Reload ssh
      ansible.builtin.service:
        name: ssh
        state: reloaded

いくつかの行には、手作業では気づきにくい理由があります。

**sudoersに validate を付ける理由。** 作業用ユーザーはパスワードを設定せず鍵だけで入る前提なので、パスワードなしで sudo できるようにしています。sudoersの書式を誤ると管理者権限そのものを失うため、visudo -cf で検証に通ったときだけファイルを置くようにしています。パスワードなしの sudo を許したくない場合は、ユーザーにパスワードを設定し、実行時に -K で入力する運用にします。

**SSHの設定を sshd_config.d に置く理由。** 最近のUbuntuの sshd_config は先頭で sshd_config.d/*.conf を読み込み、SSHの設定は「最初に出てきた値が有効」というルールです。クラウドのイメージによっては、このディレクトリにパスワード認証を許可する設定ファイルが置かれていることがあり、本体の sshd_config を書き換えても効かない場合があります。ファイル名を 00- で始めて先に読ませているのはそのためです。

**sshd -t を挟む理由。** 設定に誤りがある状態でSSHを再読み込みすると、次から接続できなくなるおそれがあります。文法チェックが失敗すればその時点でプレイブックが止まり、ハンドラー(再読み込み)は実行されません。なお、サービス名はUbuntu/Debianでは ssh、RHEL系では sshd です。

UFWの順番。 SSHの許可ルールを入れる前にUFWを有効にすると、実行中の接続ごと遮断される可能性があります。許可ルールを先に並べ、有効化は最後にしています。SSHのポートを22から変えている場合は ssh_port を合わせてください。

ハンドラー。 notify は「このタスクで変更が起きたときだけ、最後にハンドラーを実行する」という仕組みです。設定ファイルが前回と同じなら再読み込みは起きません。冪等性を保つための基本的な書き方です。

いきなり本番に流さず、確認してから実行する

プレイブックを書いたら、まず文法チェックとドライランを行います。

ansible-playbook site.yml --syntax-check
ansible-playbook site.yml --check --diff --limit web1

--check は実際には変更せず「何が変わるか」だけを表示し、--diff はファイルの差分を見せます。--limit web1 で1台に絞れるので、最初はテスト用のサーバーか、作り直してもかまわない1台で試すのが安全です。なお、前のタスクの結果に依存する処理(パッケージを入れてから設定する等)は、チェックモードでは正しく判定できず失敗表示になることがあります。

問題がなければ本番を実行します。

ansible-playbook site.yml --limit web1

締め出し対策として、実行中は既存のSSH接続を1つ開いたままにしておいてください。 実行後、別のターミナルから ssh deploy@203.0.113.10 で新しく入れることと、sudo が使えることを確かめてから、元の接続を閉じます。もし入れなくなった場合でも、VPSやクラウドの管理画面にあるWebコンソールから入って設定を戻せます。事前にスナップショットを取っておくと、さらに戻しやすくなります。

1台で動いたら、--limit を外して全台に流します。2回目を実行して、結果の changed=0 になっていれば、冪等に書けている証拠です。

つまずきやすいポイント

パスワードを書かない。 sudo にパスワードが必要な環境では --ask-become-pass(-K)で実行時に入力します。プレイブックやインベントリにパスワードやAPIキーを直接書くのは避け、どうしても保存する必要がある値は ansible-vault で暗号化します。

ansible-vault create group_vars/web/vault.yml
ansible-playbook site.yml --ask-vault-pass

**command や shell に頼りすぎない。** 何でも ansible.builtin.shell で書くと、毎回「変更あり」と表示され、冪等性が崩れます。パッケージなら apt、ファイルなら copy や template、サービスなら service のように、目的に合うモジュールを探すのが先です。モジュール一覧は公式ドキュメントのコレクション索引で調べられます。

モジュール名はコレクション名から書く(FQCN)。 apt ではなく ansible.builtin.apt のように書いておくと、どのコレクションのモジュールかがはっきりし、将来の名前の衝突を避けられます。

ファイルが増えてきたらロールに分ける。 1つのプレイブックが長くなったら、Nginx、PHP、MySQLのように役割ごとに「ロール」として切り出すと、他のサーバーやプロジェクトでも再利用できるようになります。ロールの作り方は別の記事で詳しく扱っています。

まとめ

  • Ansibleはコントロールノードから対象サーバーにSSHで接続して設定を適用するツールで、対象側にはSSHとPythonがあれば足ります。
  • インストールは pipx 経由が公式の案内で、ansible パッケージには主要なコレクションも含まれます。
  • インベントリで対象を定義し、ansible.builtin.ping で接続を確認してからプレイブックを書きます。
  • SSH設定は sshd_config.d の読み込み順に注意し、sshd -t で検証してから再読み込みします。UFWは許可ルールを先に入れてから有効化します。
  • --check --diff と --limit で1台ずつ確かめ、既存のSSH接続を残したまま実行すると締め出しを防げます。2回目の実行で changed=0 になれば冪等に書けています。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲