Ansibleに入門してみた
はじめに
Ansibleを勉強する中で学んだことを自分なりに整理してまとめてみました。
これからAnsible学び始める人の参考になれば嬉しいです。
Ansibleとは
Ansibleは、ITインフラの構築・設定・管理をコードで自動化する「構成管理ツール」です。
このような仕組みは「Infrastructure as Code(IaC)」と呼ばれ、手作業の設定を減らし、効率的で再現性のある運用を可能にします。
Ansibleの強み
-
YAML形式
コーディングスキルがなくても内容が理解しやすく、学習コストが低い。 -
エージェントレス
管理対象のサーバーにエージェントを導入する必要がないため導入・運用の手間が少ない。(SSH接続とPythonだけで操作可能) -
冪等性(べきとうせい)
同じ操作を何度実行しても結果が変わらないため、安心して繰り返し使える。
Ansible基本用語
-
コントロールノード
Ansibleを実行するサーバー。 -
ターゲットノード
管理対象のサーバー。 -
インベントリ
ターゲットノードを一覧で定義したファイル。 -
プレイブック
実行する処理を記述したファイル。 -
モジュール
「ファイルの作成」など、具体的な処理を再利用可能な単位としてまとめたもの。
モジュールによって冪等性が担保されている。 -
プラグイン
Ansibleの機能を拡張するためのコンポーネント。 -
ロール
プレイブックを分割・整理して管理しやすくする仕組み。 -
コレクション
モジュール、プラグイン、ロールなどを一つのパッケージとしてまとめて配布するための形式。
実践:WordPressサーバー作成
1.構成図
Windowsをコントロールノードとして設定することができません。
環境の差異をなくすためにVagrantを使って、AnsibleサーバーとWordPressサーバーを構築します。

2.Vagrantによるサーバー起動
この記事では、Vagrantの使い方を説明しません。
下の記事を参考にAnsible用サーバーとWordPress用サーバーを作り、ログインして下さい。
参考までに、私が作成したVagrantfileは以下になっております。
Vagrant.configure("2") do |config|
config.vm.box = "bento/ubuntu-22.04"
config.vm.define "ansible" do |ansible|
ansible.vm.network "private_network", ip: "192.168.50.4"
end
config.vm.define "wordpress" do |wp|
wp.vm.network "private_network", ip: "192.168.50.6"
end
end
3.Ansible用ユーザー作成
Ansibleを利用するためには、コントロールノード(Ansibleサーバー)からターゲットノード(WordPressサーバー)へSSH接続できるようにする必要があります。
そのために、ますばAnsibleがSSH接続に使うユーザーを作成します。
本手順はAnsibleサーバー、WordPressサーバーの両方で実施して下さい。
ansibleユーザー作成
$ sudo useradd -m -s /bin/bash ansible
$ cat /etc/passwd | grep ansible
ansibleユーザロック状態を確認
$ sudo passwd -S ansible
$ sudo passwd ansible
NOPASSWORD設定
$ sudo EDITOR=vim visudo
以下を追加:
ansible ALL=(ALL) NOPASSWD:ALL
4.SSH設定
Ansibleサーバー → WordPressサーバー 間でSSH接続できるように設定します。
あわせて、ローカル端末 → Ansibleサーバー、ローカル端末 → WordPressサーバー のSSH接続設定も行ってください。
SSH鍵の作成
以下作業は、SSH接続元で実施して下さい。
$ sudo su - ansible
$ ssh-keygen -t -rsa
SSH設定ファイルの修正
以下作業は、SSH接続元で実施して下さい。
$ vi ~/.ssh/config
以下を記載:
Host wordpress
Hostname 192.168.50.6
user ansible
Port 22
IdentityFile ~/.ssh/id_rsa
sshd_configの修正
以下作業は、SSH接続先で実施して下さい。
sudo vi /etc/ssh/sshd_config
※以下の設定に変更:
Port 22
PermitRootLogin no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
PermitEmptyPasswords no
公開鍵の登録
以下作業は、SSH接続元で実施して下さい。
sudo su - ansible
mkdir -m 700 ~/.ssh
vi ~/.ssh/authorized_keys
ホスト側で作成したid_rsa.pubの内容を貼り付ける。
$ chmod 600 ~/.ssh/authorized_keys
設定反映のためにsshd再起動
$ sudo systemctl restart sshd
5.Ansibleインストール
Ansibleには2種類の配布形態があります。
- ansible-core:最小限の機能のみを持つパッケージ
- Ansible Community Package:多数のコレクションを同梱したパッケージ
また、インストール方法も2種類あります。
- OSのパッケージマネージャーを使う方法
- Pythonのパッケージマネージャー(pip)を使う方法
今回は Ansible Community Package を、OSのパッケージマネージャー を使ってインストールします。
sudo apt update
sudo apt install software-properties-common
sudo apt-add-repository --yes --update ppa:ansible/ansible
sudo apt install ansible
6.HTTPS化の事前準備
mkcertを用いて、WordPressサーバーをHTTPS化します。
この記事ではmkcertにの使い方は解説しません。
下の記事を参考に、HTTPS化の準備を行って下さい。
7.Ansibleディレクトリ構成
Ansibleサーバーのansibleユーザーのホームディレクトリ配下に、以下の構造でansibleディレクトリを作成して下さい。
ansible/
├── inventory.ini
├── group_vars/
│ └── web.yml
├── wordpress.yml
├── roles/
│ ├── nginx/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ ├── files/
│ │ │ └── nginx.conf
│ │ └── templates/
│ │ └── wordpress.test.j2
│ ├── mysql/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ └── files/
│ │ └── mysqld.cnf
│ ├── php/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ └── files/
│ │ └── www.conf
│ └── wordpress/
│ ├── tasks/
│ │ └── main.yml
│ ├── handlers/
│ │ └── main.yml
│ └── templates/
│ └── wp-config.php.j2
└── ansible.cfg
8.インベントリー(inventory.ini)の作成
インベントリーに以下の内容を記載して下さい。
[web]
wordpress.test ansible_host=192.168.50.6 ansible_user=ansible
- [web]のようにグループ名は[ ]で囲む。
- その下に属するホスト名とホスト変数(ホストにのみ有効)を記載。
- ここでは web グループに wordpress.test を追加し、IPアドレスとユーザーを指定しています
-
[ ]で囲んだグループだけでなく、インベントリーに記載したすべてのホストは自動的に all グループにも所属します。
9.グループ変数(group_vars)の作成
group_vars/web.ymlに以下の内容を記載して下さい。
mysql_root_password: Password!1234
wordpress_db_name: wordpress_db
wordpress_user: ansible
wordpress_user_password: Password!1234
グループ変数は、インベントリーに記載することも可能です。
しかし、可読性の向上やパスワードなどの機密情報を分離する目的で切り分けています。
グループ変数は、group_varsディレクトリに「グループ名.yml」という形式で作成します。
同様に、ホスト変数もhost_varsディレクトリに「ホスト名.yml」として作成できます。
また、変数にはスコープと優先順位が存在します。
大まかな覚え方としては、スコープが狭くなるほど優先順位が高くなります(反比例の関係)。
10.プレイブック(wordpress.yml)の作成
プレイブックに以下を記載して下さい。各行の解説もつけています。
---
- name: Setup users # このプレイの名前。
hosts: all # Targetsセクション。このプレイを実行する対象ホスト。allグループが対象。
become: true # root権限で実行する。root権限で実施する必要がある処理がある場合に記載する。
roles: # Rolesセクション。実行するロールを指定する。
- users # usersロールを実行
- name: Setup Wordpress # このプレイの名前。
hosts: web # Targetsセクション。このプレイを実行する対象ホスト。webグループが対象。
become: true # root権限で実行する。root権限で実施する必要がある処理がある場合に記載する。
roles: # Rolesセクション。実行するロールを指定する。
- nginx # nginxロール
- php # PHPロール
- mysql # MySQLロール
- wordpress # WordPressロール
プレイブックはプレイと呼ばれる実行単位の集まりです。
プレイは以下のセクションで構成されます。
- Targetsセクション:プレイの対象
- Tasksセクション:実際に実行する作業
- Rolesセクション:tasksのまとまりを外部ファイルとして呼び出す
- Handlersセクション:条件付き(notifiy)で呼ばれる処理
- Varsセクション:変数を定義
今回のプレイブックはプレイが2つの構成となっております。
また、Rolesを利用しているため、Tasks、Handlersは個別に記載していません。
Varsはホスト変数、グループ変数、ロール変数で定義しているため、記載していません。
11.各種ロール(roles)の作成
ロールを正しく動作させるには規程されたディレクトリ構造に従う必要があります。
以下に、今回使用しているディレクトリとその機能を記載します。
- files:copyモジュールなどでターゲットノードに送る静的なファイルを配置します。
- tasks:ロールないで実行する作業を記載したファイル(main.yml)を配置します。このディレクトリはロールにおいて必須です。
- tamplates:tamplateモジュールで利用する動的なファイル(~~.j2)を配置します。
- vars:ロール内で使用する変数を記載したファイル(main.yml)を配置します。
- handlers:notifyで呼び出される処理を記載したファイル(main.yml)を配置します。
usersロールの作成
tasksの作成
users/tasks/main.ymlに以下を記載して下さい。
---
- name: Create new Users
ansible.builtin.user:
name: "{{ item.key }}"
password: "{{ item.value.initial_password }}"
update_password: on_create
comment: "{{ item.value.comment }}"
groups: "{{ item.value.groups }}"
shell: "{{ item.value.shell }}"
home: "/home/{{ item.key }}"
state: present
createhome: true
loop: "{{ new_users | dict2items }}"
- name: Add sudoers file
ansible.builtin.template:
src: "{{ item.value.priv }}_sudoers.j2"
dest: "/etc/sudoers.d/{{ item.key }}"
owner: root
group: root
mode: '0400'
validate: 'visudo -c -f %s'
when: item.value.add_sudoers == true
loop: "{{ new_users | dict2items }}"
- name: Distribuite authorized key
ansible.posix.authorized_key:
user: "{{ item.key }}"
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: present
when: item.value.add_authorized_key == true
loop: "{{ new_users | dict2items }}"
ロールはタスクを機能ごとに分割して再利用できるようにしたものです。
各タスクには、名前、モジュール、アーギュメント、条件式などを記載します。
基本的には、手作業で行っていた処理に対応するモジュールを探して置き換えていく形になります。
ここでは変数の利用、when、loopについて解説します。
-
変数の利用:{{ 変数 }}とすることで変数を参照することができます。また、{{ 変数 | フィルター }}とすることで変数を加工することができます。
「9.グループ変数」でも述べたように変数には優先順位とスコープが存在します。そのため、同名の変数を使わないようにし、変数を定どこに定義するかをあらかじめ決めておくことが非常に大事です。 -
when:when: 条件式 とすることで、その条件に合致した場合のみタスクを実行します。
-
loop:loopを使うことで、リストなどのデータを繰り返し呼び出し、処理することができます。
templatesの作成
users/templates/admin_sudoers.j2に以下を記載して下さい。
{{ item.key }} ALL=(ALL) NOPASSWD: ALL
varsの作成
users/vars/main.ymlに以下を記載して下さい。
---
new_users:
ansible:
comment: "Ansible User"
initial_password: "{{ 'password!1234' | password_hash('sha256') }}"
groups: ansible
shell: /bin/bash
add_authorized_key: true
add_sudoers: true
priv: admin
nginxロールの作成
tasksの作成
nginx/tasks/main.ymlに以下を記載して下さい。
---
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Enable and Start nginx service
ansible.builtin.systemd:
name: nginx
state: started
enabled: true
- name: Create the document root directory
ansible.builtin.file:
path: "/var/www/{{ inventory_hostname }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Copy nginx conf
ansible.builtin.copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
backup: no
owner: root
group: root
mode: '0644'
notify: restart nginx service
- name: Create the SSL directory
ansible.builtin.file:
path: "/etc/nginx/ssl"
state: directory
owner: root
group: root
mode: '0700'
- name: Copy SSL certificate
ansible.builtin.copy:
src: "{{ inventory_hostname }}.pem"
dest: "/etc/nginx/ssl/{{ inventory_hostname }}.pem"
owner: root
group: root
mode: '0644'
notify: restart nginx
- name: Copy SSL key
ansible.builtin.copy:
src: "{{ inventory_hostname }}-key.pem"
dest: "/etc/nginx/ssl/{{ inventory_hostname }}-key.pem"
owner: root
group: root
mode: '0600'
notify: restart nginx
- name: Copy Virtual Host conf
ansible.builtin.template:
src: "{{ inventory_hostname }}.conf.j2"
dest: "/etc/nginx/conf.d/{{ inventory_hostname }}.conf"
backup: no
owner: root
group: root
mode: '0644'
notify: restart nginx
ここでは新たに出てきたnotifyについて解説します。
-
notify:notify: ハンドラー名 とすることで、タスクがchangedとなった際に、指定したハンドラーを呼び出します。
ハンドラーはサービスの再起動など変更後にまとめて実行したい処理を定義することが多いです。
filesの作成
nginx/files/nginx.confに以下を記載して下さい。
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
}
http {
include /etc/nginx/mime.types;
log_format custom_log "[nginx] time:$time_iso8601\t"
"server_addr:$server_addr\t"
"host:$host\t"
"method:$request_method\t"
"reqsize:$request_length\t"
"uri:$request_uri\t"
"query:$query_string\t"
"status:$status\t"
"size:$body_bytes_sent\t"
"referer:$http_referer\t"
"ua:$http_user_agent\t"
"forwardedfor:$http_x_forwarded_for\t"
"reqtime:$request_time\t"
"apptime:$upstream_response_time";
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
include /etc/nginx/conf.d/*.conf;
}
nginx/files/wordpress.test-key.pemに「6.HTTPS化の事前準備」で作成した秘密鍵の内容を記載して下さい。
nginx/files/wordpress.test.pemに「6.HTTPS化の事前準備」で作成したサーバー証明書の内容を記載して下さい。
templatesの作成
nginx/templates/wordpress.test.conf.j2に以下を記載して下さい。
server {
listen 80;
server_name {{ inventory_hostname }};
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name {{ inventory_hostname }};
root /var/www/{{ inventory_hostname }}/;
index index.php index.html;
access_log /var/log/nginx/{{ inventory_hostname }}.log custom_log;
error_log /var/log/nginx/{{ inventory_hostname }}.error.log;
ssl_certificate /etc/nginx/ssl/{{ inventory_hostname }}.pem;
ssl_certificate_key /etc/nginx/ssl/{{ inventory_hostname }}-key.pem;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
handlersの作成
nginx/handlers/main.ymlに以下を記載して下さい。
---
- name: validate nginx conf
ansible.builtin.command: nginx -t
changed_when: false
listen: restart nginx service
- name: restart nginx
ansible.builtin.systemd:
name: nginx
state: restarted
listen: restart nginx service
ここでnotifyで呼び出されたハンドラーが実行されます。
ここでは、listen、changed_whenについて解説します。
-
listen:notifyで指定された値に複数のハンドラーを結びつけるための機能です。
-
changed_when:changed_when: falseとすることで、このタスクが実際にはサーバーの状態を変えていないことを明示することができます。
phpロールの作成
tasksの作成
php/tasks/main.ymlに以下を記載して下さい。
- name: Install dependencies for PHP repository
ansible.builtin.apt:
name: software-properties-common
state: present
update_cache: yes
- name: Add PHP repository
ansible.builtin.apt_repository:
repo: ppa:ondrej/php
state: present
update_cache: yes
- name: Install PHP 8.3 and PHP-FPM
apt:
name:
- php8.3
- php8.3-fpm
- php8.3-mysql
- php8.3-cli
- php8.3-curl
- php8.3-xml
- php8.3-mbstring
- php8.3-zip
- php8.3-gd
state: present
update_cache: yes
- name: Enable and Start php8.3-fpm service
systemd:
name: php8.3-fpm
state: started
enabled: true
- name: Copy www.conf
copy:
src: www.conf
dest: /etc/php/8.3/fpm/pool.d/www.conf
backup: no
owner: root
group: root
mode: '0644'
notify: restart php-fpm service
filesの作成
php/files/www.confに以下を記載して下さい。
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = static
pm.max_children = 10
pm.max_requests = 100
php_admin_value[memory_limit] = 256M
request_terminate_timeout = 180
handlersの作成
php/handlers/main.ymlに以下を記載して下さい。
---
- name: validate www conf
ansible.builtin.command: sudo php-fpm8.3 -t
changed_when: false
listen: restart php-fpm service
- name: restart sudo php-fpm8.3
ansible.builtin.systemd:
name: php8.3-fpm
state: restarted
listen: restart php-fpm service
mysqlロールの作成
tasksの作成
mysql/tasks/main.ymlに以下を記載して下さい。
---
- name: Install mysql
ansible.builtin.apt:
name: mysql-server
state: present
update_cache: yes
- name: Enable and start mysql
ansible.builtin.systemd:
name: mysql
state: started
enabled: true
- name: Install PyMySQL for MySQL support
ansible.builtin.apt:
name: python3-pymysql
state: present
- name: Copy mysqld.cnf
ansible.builtin.copy:
src: mysqld.cnf
dest: /etc/mysql/mysql.conf.d/mysqld.cnf
backup: no
owner: root
group: root
mode: '0644'
notify: restart mysql
- name: Wait for MySQL socket to be ready
ansible.builtin.wait_for:
path: /var/run/mysqld/mysqld.sock
state: present
timeout: 30
- name: Set root user to use mysql_native_password
community.mysql.mysql_user:
name: root
host: localhost
password: "{{ mysql_root_password }}"
plugin: mysql_native_password
login_unix_socket: /var/run/mysqld/mysqld.sock
login_user: root
login_password: "{{ mysql_root_password | default(omit) }}"
state: present
- name: Create wordpress database
community.mysql.mysql_db:
name: "{{ wordpress_db_name }}"
encoding: utf8mb4
collation: utf8mb4_unicode_ci
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
login_unix_socket: /var/run/mysqld/mysqld.sock
- name: Create WordPress user
community.mysql.mysql_user:
name: "{{ wordpress_user }}"
password: "{{ wordpress_user_password }}"
host: localhost
priv: "{{ wordpress_db_name }}.*:ALL"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
filesの作成
mysql/files/mysqld.cnfに以下を記載して下さい。
[mysqld]
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
bind-address = 127.0.0.1
mysqlx-bind-address = 127.0.0.1
key_buffer_size = 16M
myisam-recover-options = BACKUP
log_error = /var/log/mysql/error.log
max_binlog_size = 100M
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
handlersの作成
mysql/handlers/main.ymlに以下を記載して下さい。
---
- name: restart mysql
ansible.builtin.systemd:
name: mysql
state: restarted
wordpressロールの作成
tasksの作成
wordpress/tasks/main.ymlに以下を記載して下さい。
---
- name: Create document root
ansible.builtin.file:
path: /var/www/{{ inventory_hostname }}
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Download WordPress
ansible.builtin.get_url:
url: https://wordpress.org/latest.tar.gz
dest: /var/www/{{ inventory_hostname }}/wordpress.tar.gz
mode: '0755'
- name: Extract WordPress
ansible.builtin.unarchive:
src: /var/www/{{ inventory_hostname }}/wordpress.tar.gz
dest: /var/www/{{ inventory_hostname }}/
remote_src: yes
creates: /var/www/{{ inventory_hostname }}/wp-config.php
- name: Move WordPress files to document root
ansible.builtin.copy:
src: /var/www/{{ inventory_hostname }}/wordpress/
dest: /var/www/{{ inventory_hostname }}/
remote_src: yes
- name: Set ownership for WordPress files
ansible.builtin.file:
path: /var/www/{{ inventory_hostname }}
owner: www-data
group: www-data
recurse: yes
- name: Copy wp-config.php
ansible.builtin.template:
src: wp-config.php.j2
dest: /var/www/{{ inventory_hostname }}/wp-config.php
owner: www-data
group: www-data
mode: '0644'
notify:
- Restart web contens
templatesの作成
wordpress/templates/wp-config.php.j2に以下を記載して下さい。
<?php
define( 'DB_NAME', '{{ wordpress_db_name }}' );
define( 'DB_USER', '{{ wordpress_user }}' );
define( 'DB_PASSWORD', '{{ wordpress_user_password }}' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );
$table_prefix = 'wp_';
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
require_once ABSPATH . 'wp-settings.php';
handlersの作成
wordpress/handlers/main.ymlに以下を記載して下さい。
---
- name: Restart PHP-FPM
service:
name: php8.3-fpm
state: restarted
listen: Restart web contens
- name: Restart Nginx
service:
name: nginx
state: restarted
listen: Restart web contens
- name: Restart MySQL
service:
name: mysql
state: restarted
listen: Restart web contens
12.ansible.cfgの作成
ansible.cfgはAnsible全体の動作を決めるファイルです。
設定ファイルを複数の場所に配置できますが、基本的にはプレイブックと同じディレクトリに置くのが一般的です。
ansible.cfgに以下を記載して下さい。各行の解説もつけています。
[defaults]
inventory = /var/www/html/ansible/inventory.ini # インベントリの場所を指定
private_key_file = ~/.ssh/id_rsa # SSH接続で使う秘密鍵のパス
retry_files_enabled = False # 失敗したホストを記録する.retryファイルを作らない
log_path = ~/.ansible/ansible.log # 実行ログを出力するファイルのパス
host_key_checking = False # 初回接続時のホスト鍵確認をスキップ
gathering = smart # facts(システム情報)の取得を最適化して実行
13.プレイブックの実行
プレイブックの作成が完了したので、実際にプレイブックを実行します。
$ cd ~/ansible
$ ansible-playbook wordpress.yml
実行が完了したら、 https://wordpress.test にアクセスして動作を確認します。
まとめ
今回初めてAnsibleを学習し、基本的にはこれまで手作業で行っていた作業をモジュールに置き換えることで、自動化が簡単に出来ることを理解しました。さらに、ロールや変数等を用いることで再利用性や保守性をあげられる点が非常に有用であると感じました。
クラウドが普及し、迅速な構築が求められる現在において、Ansibleは必須とも言えるツールであると感じ取ることができたので、今後も継続して学習していきたいと思います。
一方でAnsibleの構築はほとんどがどのモジュールを使うかを調べることにあると感じられました。また、今回の学習で最も難しいと感じたのは変数のスコープと優先順位、ロールやタスクの切り分けをどこまで行うかの判断でした。実際の現場で使う場合は、あらかじめチーム内でルールや設計方針を共有し、属人化を避けることが重要であると感じます。
参考文献
- Ansible実践ガイド 第4版[基礎編]
Discussion