はじめに
この記事は、Leveragesアドベントカレンダー11日目の記事になります。
10日目に続いてAnsible関連の記事をあげていきます。
今回は導入の過程でたどり着いたベストプラクティスを紹介します
公式ベストプラクティスでは少し都合が悪かったので公式を参考に少しカスタマイズしたものになります。
(自分流なので完全なベストプラクティスでない場合もあります。)
ベストプラクティスにたどり着く過程で下記の記事を参考にさせて頂きました。
Ansible ( 俺の中で ) 最強の Best Practices
Ansible オレオレベストプラクティス
公式では足りなかった理由
- 共有で使いたいRoleがいくつか存在した
- とはいえ社内秘情報もあるのでAnsible Galaxyに載せるのはちょっと…
- プロジェクトが大量に存在するためそれぞれに
production
,staging
などの設定が存在する - とはいえ全プロジェクトで
production
,staging
などの環境で同じ設定のものも存在する
出来上がったベストプラクティス
playbook/
├── ansible.cfg # ansibleの設定ファイル
├── group_vars/
│ ├── development
│ │ └── all.yml # 全プロジェクトdevelopment環境用変数
│ ├── production
│ │ └── all.yml # 全プロジェクトproduction環境用変数
│ ├── staging
│ │ └── all.yml # 全プロジェクトstaging環境用変数
│ └── all.yml # 全プロジェクト全環境用変数
├── host_vars/
│ ├── host1.yml # host1用環境変数
│ │ ︙
│ └── host2.yml # host2用環境変数
├── inventories/
│ ├── service-A
│ │ ├── group_vars
│ │ │ ├── development
│ │ │ │ └── all.yml # service-Aのdevelopment環境用変数
│ │ │ ├── production
│ │ │ │ └── all.yml # service-Aのproduction環境用変数
│ │ │ ├── staging
│ │ │ │ └── all.yml # service-Aのstaging環境用変数
│ │ │ └── all.yml # service-Aの全環境用変数
│ │ ├── development.yml # service-Aのdevelopment環境用インベントリファイル
│ │ ├── production.yml # service-Aのproduction環境用インベントリファイル
│ │ └── staging.yml # service-Aのstaging環境用インベントリファイル
│ ├── service-B
│ │ ├── group_vars
│ │ │ ├── development
│ │ │ │ └── all.yml # service-Bのdevelopment環境用変数
│ │ │ ├── production
│ │ │ │ └── all.yml # service-Bのproduction環境用変数
│ │ │ ├── staging
│ │ │ │ └── all.yml # service-Bのstaging環境用変数
│ │ │ └── all.yml # service-Bの全環境用変数
│ │ ├── production.yml # service-Bのproduction環境用インベントリファイル
│ │ ├── local.yml # service-Bのlocal環境用インベントリファイル
│ │ ├── development.yml # service-Bのdevelopment環境用インベントリファイル
│ │ └── staging.yml # service-Bのstaging環境用インベントリファイル
│ ︙
│ └── ...
├── roles
│ ├── common
│ │ ├── defaults
│ │ │ └── main.yml # commonロール内で使うデフォルト変数
│ │ ├── files # commonロール内で使用するリソースファイル
│ │ ├── handlers # commonロール内のタスクに依存するハンドラ
│ │ ├── meta # commonロールに関連する依存関係
│ │ ├── tasks
│ │ │ └── main.yml # commonロールで実行するタスク基軸
│ │ ├── templates # commonロール内で呼び出されるテンプレート
│ │ └── vars # commonロール内に関連する変数
│ ├── nginx
│ │ ├── defaults
│ │ │ └── main.yml # nginxロール内で使うデフォルト変数
│ │ ├── files # nginxロール内で使用するリソースファイル
│ │ ├── handlers
│ │ │ └── main.yml # nginxロール内のタスクに依存するハンドラ
│ │ ├── meta # nginxロールに関連する依存関係
│ │ ├── tasks
│ │ │ ├── configure.yml # nginxロールで実行する設定関連タスク
│ │ │ ├── install.yml # nginxロールで実行するインストール関連タスク
│ │ │ └── main.yml # nginxロールで実行するタスク基軸
│ │ ├── templates # nginxロール内で呼び出されるテンプレート
│ │ │ ├── location_root.conf.j2
│ │ │ ├── nginx.conf.j2
│ │ │ └── servers.conf.j2
│ │ └── vars # nginxロール内に関連する変数
│ ︙
│ └── ...
└── site.yml # 実行するPlaybook
構成と設定例
Playbook
構成
playbook/
├── ansible.cfg # ansibleの設定ファイル
︙
└── site.yml
- ansible.cfg
このプレイブック用のansibleの設定ファイルです。 - site.yml
対象ホストグループとロールの関連設定ファイル
設定例
ansibleのオプションを設定します。
オプションの項目と詳細はConfiguration file — Ansible Documentationを参考にします。
[defaults]
forks = 10
transport = ssh
retry_files_enabled = False
対象ホストグループとそのグループに対して実行するロールを指定します。
---
- hosts: all
become: yes
roles:
- common
- hosts: web
become: yes
roles:
- php
- nginx
実行するロール内で必要となる他のロールのhandlerは下記のように呼び出します。
- hosts: db
become: yes
roles:
- postgresql
handlers:
- include: roles/php/handlers/main.yml
- include: roles/nginx/handlers/main.yml
group_vars
構成
各環境のディレクトリを作成し、その配下に環境変数格納用のymlを配置します。
ここに格納する環境変数はサービスに関係なく、例えば本番環境であれば全サービスの本番環境で必要な変数になります。
├── group_vars/
│ ├── production
│ │ └── all.yml # 全プロジェクトdevelopment環境用変数
設定例
ansibleを実行する際のsshのポートとユーザー名を指定します。
ansible_ssh_port: 22
ansible_ssh_user: root
Inventory
構成
inventories以下にサービス毎にディレクトリを作成します。
├── inventories/
│ ├── service-A
︙ ︙
│ ├── service-B
サービスのディレクトリ以下は環境毎にymlを作成し環境変数格納用のgroup_vars
ディレクトリを配置します。
group_vars
ディレクトリ下には各環境ごとのディレクトリを作成し、環境変数を格納するymlを作成します。
ここで格納する環境変数はサービスと環境ごとに変わる変数になります。
│ ├── service-A
│ │ ├── group_vars
│ │ │ ├── development
│ │ │ │ └── all.yml # service-Aのdevelopment環境用変数
│ │ │ ├── production
│ │ │ │ └── all.yml # service-Aのproduction環境用変数
│ │ │ ├── staging
│ │ │ │ └── all.yml # service-Aのstaging環境用変数
│ │ │ └── all.yml # service-Aの全環境用変数
│ │ ├── development.yml # service-Aのdevelopment環境用インベントリファイル
│ │ ├── production.yml # service-Aのproduction環境用インベントリファイル
│ │ └── staging.yml # service-Aのstaging環境用インベントリファイル
設定
production.ymlの設定例
[web]
10.0.1.1
[production:children]
web
development.ymlの設定例
[web]
10.0.2.1
[db]
10.0.2.1
[production:children]
web
db
group_varsディレクトリ下のall.ymlの設定例
ここではnginxの設定の際に使用するサービスのルートディレクトリなどを設定しています。
projects:
- location_root: /www/
host_name: service-a.example.com
location_names:
- root
roles
構成
roles
ディレクトリ以下に一般的に全サーバーで必要なcommon
ディレクトリとWebサーバーに必要なNginx関連の設定を行うnginx
ディレクトリを作成します。
├── roles
│ ├── common
│ │ ├── defaults
│ │ │ └── main.yml # commonロール内で使うデフォルト変数
│ │ ├── files # commonロール内で使用するリソースファイル
│ │ ├── handlers # commonロール内のタスクに依存するハンドラ
│ │ ├── meta # commonロールに関連する依存関係
│ │ ├── tasks
│ │ │ └── main.yml # commonロールで実行するタスク基軸
│ │ ├── templates # commonロール内で呼び出されるテンプレート
│ │ └── vars # commonロール内に関連する変数
│ ├── nginx
│ │ ├── defaults
│ │ │ └── main.yml # nginxロール内で使うデフォルト変数
│ │ ├── files # nginxロール内で使用するリソースファイル
│ │ ├── handlers
│ │ │ └── main.yml # nginxロール内のタスクに依存するハンドラ
│ │ ├── meta # nginxロールに関連する依存関係
│ │ ├── tasks
│ │ │ ├── configure.yml # nginxロールで実行する設定関連タスク
│ │ │ ├── install.yml # nginxロールで実行するインストール関連タスク
│ │ │ └── main.yml # nginxロールで実行するタスク基軸
│ │ ├── templates # nginxロール内で呼び出されるテンプレート
│ │ │ ├── location_root.conf.j2
│ │ │ ├── nginx.conf.j2
│ │ │ └── servers.conf.j2
│ │ └── vars # nginxロール内に関連する変数
- defaults
ロール内で使うデフォルト変数設定ファイルを格納するディレクトリ - files
ロール内で使用するリソースファイを格納するディレクトリ
リスースファイルとはcopy
モジュールなどで使われるファイル群 - handlers
ロール内のタスクに依存するハンドラ(ex. nginxの設定変更後のリスタートタスク) - meta
ロールに関連する依存関係の設定ファイルを格納するディレクトリ - tasks
ロールで実行するタスクを格納するディレクトリ - templates
ロール内で呼び出されるテンプレート(ex. nginxの設定ファイル) - vars
ロール内で使用する変数を格納するディレクトリ
設定例
common
ロールではyumでのパッケージインストールなどを行います。
---
- name: Update yum
yum:
name: '*'
state: latest
tags: yum
- name: Install yum package
yum:
name: "{{ item.name }}"
state: "{{ item.state }}"
with_items: "{{ yum_install }}"
tags: yum
- name: Setting timezone
file:
src: "{{ zoneinfo_path }}"
dest: "/etc/localtime"
state: link
force: yes
tags: timezone
ここのタスクで呼び出されているyum_install
などの変数はdefaults
ディレクトリ下にて定義します。
yum_install:
- {
name: "epel-release",
state: "latest"
}
- {
name: "python-pip",
state: "latest"
}
- {
name: "zip",
state: "latest"
}
- {
name: "unzip",
state: "latest"
}
zoneinfo_path: "/usr/share/zoneinfo/Asia/Tokyo"
nginx
ロールではnginx
のインストールとconf
ファイルの設定などを行います。
インストール関連のタスクをまとめたinstall.yml
と設定関連のタスクをまとめたconfigure.yml
にわけます。
main.yml
ではinstall.yml
とconfigure.yml
の読み込むのみにします。
これにより設定のみループ処理したりなどがしやすくなります。
---
- include: install.yml
- include: configure.yml
with_items: "{{ projects }}"
loop_control:
loop_var: project
when: projects is defined
---
- name: Install nginx
yum:
name: nginx
state: latest
tags: nginx
- name: Copy nginx configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
tags: nginx
- name: Make location root directory
file:
path: "{{ project.location_root }}"
state: directory
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
mode: 0775
tags: nginx
- name: Copy server configuration
template:
src: server.conf.j2
dest: /etc/nginx/sites-available/{{ project.host_name }}/server.conf
notify: restart nginx
tags: nginx
- name: Copy location configuration
template:
src: "location_{{ item }}.conf.j2"
dest: "/etc/nginx/sites-available/{{ project.host_name }}/locations/{{ item }}.conf"
notify: restart nginx
with_items: "{{ project.location_names }}"
tags: nginx
- name: Setting php-fpm configuration
lineinfile:
dest: /etc/php-fpm.d/www.conf
state: present
backrefs: yes
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items: "{{ php_fpm_setting }}"
notify: restart php-fpm
tags: nginx
- name: Nginx is running and enabled
service:
name: nginx
state: started
enabled: yes
tags: nginx
- name: php-fpm is running and enabled
service:
name: php-fpm
state: started
enabled: yes
tags: nginx
ここで使用している変数もcommon
の時と同様defaults
ディレクトリに設定します。
php_fpm_setting:
- {
regexp: "^user = apache",
line: "user = nginx"
}
- {
regexp: "^group = apache",
line: "group = nginx"
}
- {
regexp: "^listen = 127.0.0.1:9000",
line: "listen = /var/run/php-fpm.sock"
}
- {
regexp: "^;listen.owner = nobody",
line: "listen.owner = nginx"
}
- {
regexp: "^;listen.group = nobody",
line: "listen.group = nginx"
}
- {
regexp: "^;listen.mode = 0660",
line: "listen.mode = 0660"
}
またprojects
変数などはinventories/service-A/group_vars/production/all.yml
で設定しています。
このようにサービスに依存する変数などはinventories以下に記載することで環境やサービス毎に変更することが可能です。
configure.yml
で呼び出されているserver.conf.j2
などはテンプレートファイルとよばれ、定義している変数を使用して設定内容を動的に変更できます。
ここではnginx
のserver.conf
用のテンプレートを紹介します。
server {
listen 80;
listen [::]:80;
server_name {{ project.host_name }};
{% if project.use_elb == true %}
# アクセスが http なら https としてリダイレクト
if ($http_x_forwarded_proto != https) {
return 301 https://$host$request_uri;
}
set $my_http "http";
set $my_ssl "off";
set $my_port "80";
if ($http_x_forwarded_proto = "https") {
set $my_http "https";
set $my_ssl "on";
set $my_port "443";
}
{% endif %}
charset UTF-8;
error_page 500 502 503 504 /50x.html;
access_log /var/log/nginx/{{ project.host_name }}/access.log main;
error_log /var/log/nginx/{{ project.host_name }}/error.log;
include /etc/nginx/sites-enabled/{{ project.host_name }}/locations/*.conf;
}
まとめ
今回はAnsibleの導入過程でたどり着いたベストプラクティスを紹介しました。
このベストプラクティス自体はAnsibleの公式のベストプラクティスからは少し外れたものになっていますが、
多数のプロジェクトを抱えることの多い場合はある程度使いやすいものになっているのではないかと思います。
参考URL
公式ベストプラクティス
Ansible ( 俺の中で ) 最強の Best Practices
Ansible オレオレベストプラクティス