Ansible を使って設定を行う例として、 Linuxユーザを作成してFTPユーザとし、 Apache の仮想サイトを作成する部分をやってみたいと思います。
前提
対象サーバの LAMP はCentOS7.5 + Apache + PHP + MySQL サーバを構築し、WordPressサイトを引っ越す(2018/11/20)のうち
- 1.初期設定 ~ 4.リポジトリ追加
- 5.vsftpd のうち
- 5.1.インストール, 5.2.設定
- 6.Apache ~ 11.Webmin
- 12.Apache仮想サイトのうち
- 12.1.ダミーサイトの作成
- 16.SSH
が既に設定済みの状態のサーバとします(13.Let’s Encrypt ~ 15.WP-CLI は対象外)。
また、今回 Ansible で設定するのは
- 5.vsftpd のうち
- 5.3.ユーザ設定
- 5.4.ユーザ作成・設定
- 12.Apache仮想サイトのうち
- 12.2. 仮想サイト作成
の部分を想定しています。
設定
ディレクトリ階層
PROJECT_ROOT/
├ workspace/ // データ永続化領域
│ ├ entrypoint.sh // Dockerコンテナ起動時に実行するシェルスクリプト
│ └ ansible/ // Ansible 用ディレクトリ
│ ├ targets/ // リモートホストの情報を収めるディレクトリ
│ │ └ hosts // リモートサーバの一覧 (今回は1つのみ)
│ │
│ ├ tasks/ // Ansible のタスクを収めるディレクトリ
│ │ ├ add_user.yml // Linuxユーザの作成
│ │ ├ vsftpd_user_settings.yml // vsftpd の設定
│ │ └ apache_settings.yml // Apache 仮想サイトの設定
│ │
│ ├ templates/ // Apache 仮想サイト設定のテンプレートを収めるディレクトリ
│ │ └ vhost.conf.j2 // Apache 仮想サイト設定のテンプレート
│ │
│ ├ vars/ // 各種パラメータ変数の設定を収めるディレクトリ
│ │ └ param_vars.yml // 各種パラメータ変数
│ │
│ └ main.yml // Ansible の playbook
│
├ docker-compose.yml // Docker Compose 設定ファイル
└ Dockerfile // Dockerfile
Ansibleコントロールノード を Dockerコンテナ でビルドし、リモートサーバに公開鍵認証でSSH接続してインストール済みのパッケージの一覧を取得するまでやAnsible を使って yum update を実行するの記事の構造をベースにカスタマイズ。
一揃いを Github に置きました。
Dockerfile, docker-compose.yml, entrypoint.sh. ansible/targets/hosts
この辺りはそのまま。 ansible/targets/hosts
はグループ名を変更していますが、対となる ansible/main.yml
の hosts
の指定を変えれば良いだけなので些事。
ansible/vars/param_vars.yml
username: USERNAME
password: Password1234
rootdirectory: sample_site
domain: www.sample.jp
ipaddress: 192.0.2.1
portnum: 80
まずは今回の新顔の変数から。当初は末尾の「備考1: コマンドライン引数を変数に代入する」のように引数でコマンドラインから直接渡すことを考えていましたが、上記の通り6つもあるとそれだけでコマンドが長くなってしまい可読性が下がるので最終的に止めました。
代わりに変数を1つのファイルにまとめて、コマンド自体の長さは変わらないように調整しました。
この場合、以下の ansible/main.yml
で触れますが vars_files: ./vars/param_vars.yml
という一行で変数ファイルの読み込みができ、かつそれ以降は vars
キーで変数としてそのまま使用できるので導入はわりとすんなり行きました。
ansible/main.yml
- name: Settings vsftpd and httpd
become: yes
become_user: ADMIN_USER
become_method: su
hosts:
- add_vhost_servers
vars_files: ./vars/param_vars.yml
tasks:
- name: Add Linux User
include_tasks: ./tasks/add_user.yml
vars:
user: "{{ username }}"
passwd: "{{ password }}"
- name: Settings vsftpd user_list and user_conf
include_tasks: ./tasks/vsftpd_user_settings.yml
vars:
user: "{{ username }}"
directory: "{{ rootdirectory }}"
- name: Setup Apache conffile
include_tasks: ./tasks/apache_settings.yml
vars:
directory: "{{ rootdirectory }}"
url: "{{ domain }}"
ip: "{{ ipaddress }}"
portnumber: "{{ portnum }}"
タスクのメイン部分。基本は前回と同様で、 tasks
で指定する各種タスクが置き換わった形です。変数については上記の通り。
ansible/tasks/add_user.yml
- name: Add user, setting password and groups
user:
name: "{{ username }}"
password: "{{ password | password_hash('sha512') }}"
groups: apache
append: yes
ansible/main.yml
から渡ってきた各変数が使用される(ここでは username
, password
)、という形です。
一点注意なのはパスワードはそのままだと平文で保存されてしまいますが、実際に認証する際はハッシュ値に変換されるので不一致となってしまいます。そこで、保存時にハッシュ値に変換するように password_hash('sha512')
を噛ませています。
ansible/tasks/vsftpd_user_settings.yml
- name: Settings vsftpd user_list
blockinfile:
path: /etc/vsftpd/user_list
create: yes
insertafter: EOF
marker: "# {mark} ANSIBLE basic setup: {{ username }}"
block: "{{ username }}"
- name: Settings vsftpd user_conf
blockinfile:
path: "/etc/vsftpd/user_conf/{{ username }}"
create: yes
insertafter: EOF
marker: "# {mark} ANSIBLE basic setup: {{ username }}"
block: "local_root=/var/www/{{ rootdirectory }}"
- name: Restart vsftpd
systemd:
name: vsftpd.service
state: restarted
daemon_reload: yes
次は vsftpd の設定。 user_list
にユーザとして登録し、 user_conf
ディレクトリの下にユーザ名のファイルを作成、そこに local_root
の指定を記述する、という内容です。
ここでも一点注意。
設定ファイルへの書き込みとして blockinfile
を使用しているのですが、 marker
の値がユニークでないと、 loop
で繰り返し処理をしたり、今回のケースだと異なる仮想サイトの設定を行ったり(Ansible を2回実行する)した場合に後勝ちで上書きされてしまうこと。
今回のケースでは user_list
に2回目の変数のユーザしか残らなくなってしまいます。そのため、 marker
の最後にユーザ名を混ぜることでコメント文が実行の度にユニークとなるようにしました。
ansible/tasks/apache_settings.yml
- name: mkdir root
file:
path: "/var/www/{{ rootdirectory }}"
state: directory
owner: ADMIN_USER
group: ADMIN_USER
mode: 0755
recurse: no
- name: mkdir web
file:
path: "/var/www/{{ rootdirectory }}/web"
state: directory
owner: apache
group: apache
mode: 0775
recurse: no
- name: Setup Apache conffile
template:
src: ../templates/vhost.conf.j2
dest: "/etc/httpd/conf.d/{{ rootdirectory }}.conf"
mode: '0644'
- name: Restart httpd
systemd:
name: httpd.service
state: reloaded
daemon_reload: yes
続いて Apache 仮想サイトの設定。ディレクトリを作ったり権限・所有者設定したり。
最後の Setup Apache conffile
の処理では jinja2 のテンプレートから仮想サイト用の conf ファイルを作成しています。
ansible/templates/vhost.conf.j2
<VirtualHost {{ ipaddress }}:{{ portnum }}>
DocumentRoot "/var/www/{{ rootdirectory }}/web"
ServerName {{ domain }}
{% if domain | regex_search("^www\.", ignorecase=True) %}
ServerAlias {{ domain | regex_replace("^www\.(.*)$", '\\1') }}
{% endif %}
ScriptAlias /cgi-bin/ /var/www/{{ rootdirectory }}/web/cgi-bin/
{% if domain | regex_search("^www\.", ignorecase=True) %}
RewriteEngine on
RewriteCond %{HTTP_HOST} {{ domain | regex_replace("^www\.(.*)$", '^\\1$') | regex_replace("\.", '\.') }}
RewriteRule ^(.*)$ http://{{ domain }}$1 [R=301,L]
{% endif %}
<Directory "/var/www/{{ rootdirectory }}/web">
allow from all
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
</VirtualHost>
大体設定ファイルの内容ですが、各種変数を使用しています。
また、ドメイン(変数 domain
)が www.
始まりの場合はサーバ名を www.example.jp
のように www.
付きとし、同時に ServerAlias
で example.jp
と www.
なしもフォローするような条件分岐(if
, regex_search
)を挟みました。
併せて、 www.
なしのURLにアクセスした場合に www.
ありにリダイレクトする設定も、変数 domain
を正規表現置換(regex_replace
)で一括で入れるようにしました。
例えば、 domain: www.example.jp
とした場合は以下のような設定ファイルが作成されます。
<VirtualHost 192.0.2.1:80>
DocumentRoot "/var/www/example/web"
ServerName www.example.jp
ServerAlias example.jp
ScriptAlias /cgi-bin/ /var/www/example/web/cgi-bin/
RewriteEngine on
RewriteCond %{HTTP_HOST} ^example\.jp$
RewriteRule ^(.*)$ http://www.example.jp$1 [R=301,L]
<Directory "/var/www/example/web">
allow from all
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
</VirtualHost>
一方、 domain: form.example.jp
のようなケースだと以下。
<VirtualHost 192.0.2.1:80>
DocumentRoot "/var/www/form_example/web"
ServerName form.example.jp
ScriptAlias /cgi-bin/ /var/www/form_example/web/cgi-bin/
<Directory "/var/www/form_example/web">
allow from all
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
</VirtualHost>
上記の通り、 ServerAlias
と Rewrite
系がありません。
動作確認
以上のような構成で動作検証。
# ansible-playbook -i /workspace/ansible/targets/hosts /workspace/ansible/main.yml -u SSH_REMOTEUSER --private-key="/root/.ssh/PRIVATE_KEY" -K
BECOME password:
PLAY [Settings vsftpd and httpd] **********************************************************************************
TASK [Gathering Facts] ********************************************************************************************ok: [192.0.2.1]
TASK [Add Linux User] *********************************************************************************************included: /workspace/ansible/tasks/add_user.yml for 192.0.2.1
TASK [Add user, setting password and groups] **********************************************************************changed: [192.0.2.1]
TASK [Settings vsftpd user_list and user_conf] ********************************************************************included: /workspace/ansible/tasks/vsftpd_user_settings.yml for 192.0.2.1
TASK [Settings vsftpd user_list] **********************************************************************************changed: [192.0.2.1]
TASK [Settings vsftpd user_conf] **********************************************************************************changed: [192.0.2.1]
TASK [Restart vsftpd] *********************************************************************************************changed: [192.0.2.1]
TASK [Setup Apache conffile] **************************************************************************************included: /workspace/ansible/tasks/apache_settings.yml for 192.0.2.1
TASK [mkdir root] *************************************************************************************************changed: [192.0.2.1]
TASK [mkdir web] **************************************************************************************************changed: [192.0.2.1]
TASK [Setup Apache conffile] **************************************************************************************changed: [192.0.2.1]
TASK [Restart httpd] **********************************************************************************************changed: [192.0.2.1]
PLAY RECAP ********************************************************************************************************192.0.2.1 : ok=12 changed=8 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
OKです。
仮想サイトにアクセスして Apache のデフォルトページが表示される、FTPでログインできる、ファイルアップロードなどの操作ができる、といった動作を一通りできることを確認できました。
備考1: コマンドライン引数を変数に代入する
例えば、以下のような main.yml
を用意します。
- name: Get packages from hosts and do yum update
become: yes
become_user: ADMIN_USER
become_method: su
hosts:
- update_servers
tasks:
- name: Get packages from hosts before update
include_tasks: ./tasks/get_packages.yml
- name: Output packages from hosts before update
include_tasks: ./tasks/output_packages.yml
vars:
flag: "{{ before }}"
- name: Do yum update
include_tasks: ./tasks/yum_update.yml
- name: Get packages from hosts after update
include_tasks: ./tasks/get_packages.yml
- name: Output packages from hosts after update
include_tasks: ./tasks/output_packages.yml
vars:
flag: "{{ after }}"
Ansible を使って yum update を実行する の main.yml
のうち、 vars
の部分を少し編集したものです。
これで以下のように playbook を実行。
# ansible-playbook -i /workspace/ansible/targets/hosts /workspace/ansible/main.yml -u SSH_REMOTEUSER --private-key="/root/.ssh/PRIVATE_KEY" -K -e "before=before2 after=after22"
すると、 workspace/192.0.2.1_packages_before2
と workspace/192.0.2.1_packages_after22
が生成され、コマンドラインの引数が変数として使用されたことが分かりました。
当初はこれの延長線を考えていましたが、冒頭の通り6つも引数があると長くなってしまうので最終的には不採用としました。
ただし、これはこれで引数の使い方として参考になりそうなのでメモとして残しておきます。
参考
Ansible
引数、変数
- ansible コマンドでモジュール引数 ( パラメータ ) を複数渡す方法 – Qiita
- モジュールの概要 – Ansible Documentation
- 変数の使用 – Ansible Documentation
変数
vars_files
を採用。
ユーザ作成
パスワード
- Ansibleでユーザパスワード設定 | Netassist Blog
- 適当にansibleでrootパスワードを変えようとしたらアクセスできなくなった件 – Qiita
- Linux – Ansibleでパスワード変更の際に、パスワードポリシーを遵守させたい|teratail
Facts
設定に追記
blockfile
複数のテキストブロックを追加する場合、マーカーラインをユニークにしないとテキストブロックが上書きされる
marker
の文字列はユニークになるようにすること。
systemd
ディレクトリ作成
httpd
jinja2, if
jinja2, regrex
- Using filters to manipulate data – Ansible Documentation
- python – using regex in jinja 2 for ansible playbooks – Stack Overflow