# 5. リバースプロキシの導入/運用

それではいよいよ本格的に Ansible を活用してみます。 さきほどのセクションで構築したデータベースとアプリケーションを流用し変更を加えてみます。

現在の状態は、ブラウザから直接アプリケーションにHTTPでアクセスしている状態です。 nginx を導入し、HTTPSでアクセスしてみましょう。

今回、新たに Reverse proxy サーバを構築するにあたっては 既存のsite.ymlへの追記が必要となります。

前回までで記述ルールのいくつかはご説明致しましたが 本格的に作業に取り組む前に一つ確認をしておきます。

# Ansible playbook ディレクトリ構造

作業に取りかかる前にまず、ディレクトリ構造を確認してみましょう。 下記は教材のディレクトリ構造から Ansible に関わるファイルやディレクトリのみを抜粋しています。

それぞれのファイル・ディレクトリに役割説明を記載しています。

├── ansible.cfg  # Ansibleの設定ファイル
├── inventories  # 管理対象サーバの情報を格納するディレクトリ
│   ├── group_vars  # グループごとの変数ファイルを格納するファイル
│   │   └── all.yml  # すべてのグループに適用される変数ファイル
│   └── hosts  # サーバのIPやSSHのユーザやグループなどを記述するファイル
├── playbooks  # 実行用ファイルを格納するディレクトリ。各ファイルはsite.ymlからインポートします。
│   ├── acl.yml
│   ├── app.yml
│   ├── db.yml
│   └── rp.yml
├── roles  # ロールを格納するディレクトリ。Ansibleの実行はロールと呼ばれるまとまりで管理されます。
│   └── webapp
│       ├── defaults
│       │   └── main.yml  # ロール内で使用される変数のデフォルト値を定義するファイル
│       ├── files  # サーバへ配布したいファイルを格納するディレクトリ
│       ├── handlers
│       │   └── main.yml  # ハンドラタスクを定義するファイル
│       ├── tasks
│       │   └── main.yml  # Ansibleの具体的な処理内容を記述するファイル
│       └── templates  # サーバへ配布したいJinja2テンプレートを使ったファイルを格納するディレクトリ
├── site.yml  # playbookファイル
└── vars
    └── proxy.yml  # Proxy配下でハンズオンを実施するためのファイル
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

なぜ、このような構成にしているかと言うと、 Ansible ではより多くの人たちが正しく仕えるように推奨の記述方式を定めています。

ディレクトリ構成もその一部であり、Inventory や Role 等は Ansible を実行する上で それぞれのファイルを探すデフォルトパスになっていますので、よほどの理由が無い限り ベストプラクティスに沿って作成する事が推奨されます。

詳しく知りたい人は Ansible のベストプラクティス (opens new window)をご覧ください。

# ファイル編集

それではファイルを作成してみましょう。 TLS の証明書に加え、いくつかの設定ファイルはすでに用意してあります。 皆さんには下記 4 つのファイルを編集してもらいます。

# roles/nginx/tasks/main.yml

nginx を構築するためのタスクファイルです。 公式ドキュメント (opens new window)のインストール手順に加え、設定ファイルや証明書の配布を行っています。

Ansible はモジュールと呼ばれるものを使って、さまざまな処理を行います。 Ansible に組込み済みのモジュールは公式ドキュメント (opens new window)にリストアップされており、各モジュールごとの書き方が載っています。 モジュールを自作して使うこともできますが、基本的には上記のサイトから行いたい処理に合ったモジュールを探し、タスクファイルを作ります。

下記の内容を教材のroles/nginx/tasks/main.ymlにコピーしましょう。

---
- name: install yum-utils
  yum:
    name: yum-utils
    state: present
  environment: "{{ proxy_env | default({}) }}"

- name: add Nginx repository
  copy:
    src: "etc/yum.repos.d/nginx.repo"
    dest: "/etc/yum.repos.d/nginx.repo"
    mode: 0644

- name: install Nginx
  yum:
    name: "nginx"
    state: present
  environment: "{{ proxy_env | default({}) }}"
  ignore_errors: "{{ ansible_check_mode }}"

- name: create ssl directory
  file:
    state: directory
    path: /etc/nginx/ssl
    owner: root
    group: root
    mode: 0600

- name: deploy ssl files
  copy:
    src: "etc/nginx/ssl/{{ item }}"
    dest: "/etc/nginx/ssl/{{ item }}"
    mode: 0400
  with_items:
    - server.crt
    - server.key
    - dhparam.pem

- name: remove default config file
  file:
    state: absent
    path: /etc/nginx/conf.d/default.conf
  notify: reload nginx

- name: deploy config file
  template:
    src: etc/nginx/conf.d/app.conf.j2
    dest: /etc/nginx/conf.d/app.conf
    owner: root
    group: root
    mode: 0644
  notify: reload nginx

- name: start nginx
  systemd:
    name: nginx
    state: started
    enabled: yes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

nginx の構築に使用したモジュールの一覧は以下になります。

モジュール名 説明
yum (opens new window) yum コマンドを使って RPM パッケージを操作できるモジュールです。
file (opens new window) 対象サーバのファイルシステムを操作できるモジュールです。
ディレクトリやファイルを作成/削除したり、ファイルのオーナーやパーミッションを変更できたりします。
copy (opens new window) 対象サーバへファイルをコピーできるモジュールです。
template (opens new window) 対象サーバへファイルをコピーできるモジュールです。copyとの違いはファイル内にJinja2テンプレートが使える点です。
ファイル内で変数を使いたい場合はこちらを使います。
systemd (opens new window) systmed (opens new window)によって管理されるプロセスを操作できるモジュールです。
基本的には Linux 環境でプロセスを常駐させる場合はこれを使います。

# roles/nginx/templates/etc/nginx/conf.d/app.conf.j2

nginx の設定ファイルです。 ファイル内で変数を使用しているのでtemplates下に配置しています。

分かりやすさのために、配置するディレクトリをコピー先のパスと同じにしています。 Ansible やサーバの構築/運用に慣れないうちは、こうしておくことをお勧めします。

下記の内容を教材のroles/nginx/templates/etc/nginx/conf.d/app.conf.j2にコピーしましょう。

upstream app_backend {
    {% for app in nginx_backends %}
    server {{ app.host }}:{{ app.port }} max_fails=1 fail_timeout=3s;
    {% endfor %}
}

server {
  listen {{ nginx_https_port }} ssl;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_certificate /etc/nginx/ssl/server.crt;
  ssl_certificate_key /etc/nginx/ssl/server.key;
  ssl_dhparam /etc/nginx/ssl/dhparam.pem;

  location / {
    proxy_set_header  Host  $http_host;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-Host  $server_name;
    proxy_set_header  X-Forwarded-Server  $host;
    proxy_set_header  X-Forwarded-Proto  $scheme;
    proxy_set_header  X-Forwarded-Port {{ nginx_https_port }};
    proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_pass  http://app_backend;
  }

  error_page 404 /404.html;

  error_page 500 502 503 504 /50x.html;
  location = /50x.html {
    root  /usr/share/nginx/html;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

nginx の設定について説明することは主題から外れるので、この講義では行いません。 興味のある人は nginx の公式ドキュメント (opens new window)をご覧ください。

# playbooks/rp.yml

nginx を構築するための実行ファイルです。 対象サーバとロールを指定することで、指定したサーバに nginx を構築できます。

下記の内容を教材のplaybooks/rp.ymlにコピーしましょう。

---
- name: set up the reverse proxy server
  hosts: rp
  become: no
  gather_facts: no
  vars_files:
    - ../vars/proxy.yml
  roles:
    - role: roles/nginx
      tags: nginx
1
2
3
4
5
6
7
8
9
10

# site.yml

最後に、さきほど作ったplaybooks/rp.ymlをインポートする様に教材のsite.ymlに下記を追記します。

- import_playbook: playbooks/rp.yml
1

# 実行

さて、ファイルがすべて準備できたらいよいよ Ansible を実行します。

ansible-playbook -i inventories/hosts site.yml
1

failed=0と表示されれば実行は成功です。

ブラウザでhttps://localhost:8443 (opens new window)にアクセスして確認します。 自己署名証明書を使っているためブラウザから注意文言が表示されますが、スキップしてください。 すると、さきほどと同じ画面が表示されます。

ここで、もう一つ注目してほしい部分があります。 Ansible の実行ログを眺めると、データベースや Java アプリケーションの構築用のタスクもステータスがokの状態で、実行されているのが分かると思います。 またさきほど実行したansible-playbook site.ymlをもう一度実行すると、nginx の構築タスクも含め、すべてのタスクのステータスがokになります。

ある操作を何回行っても等価であるとき、その操作を冪等であると言います。 Ansible の良さの 1 つがこの冪等性です。 基本的に Ansible は同じタスクを何回実行しても、差分のないタスクは実行されません。 すなわち、ファイルを変更しない限りホストに対して余計な変更が加わらず、安全です。

しかしこの冪等性は意識していないと壊れてしまう場合があります。 Ansible を使うときは冪等性に注意を払いましょう。

# 3. [ケース 2] バージョンアップ & スケーリング

さて、nginx を導入して初期構築タスクを自動化できました。 次は変数やホスト情報を編集して、運用タスクを自動化してみましょう。

# バージョンアップ

まずは nginx のバージョンアップをしてみましょう。

さきほどインストールされた nginx のバージョンは少し古めの1.20.0です。 このバージョンはroles/nginx/defaults/main.ymlnginx_versionという変数として定義されています。 また、下記のコマンドを実行することで、確認することもできます。

ansible rp1 -m command -a 'nginx -V'
1

これを1.22.0にアップデートしてみたいと思います。 教材のinventories/group_vars/all.ymlに下記を追記しましょう。

nginx_version: 1.22.0
1

Ansible には変数の優先度が設定されており、同じ名前の変数は優先度の高いほうが有効になります。 これを利用することでロールの完全性を担保しつつ、タスクの内容を編集できます。

具体的な優先度は公式ドキュメント (opens new window)をご覧ください。

ファイルの準備ができたら Ansible を実行します。

ansible-playbook -i inventories/hosts site.yml
1

本実行が完了したら、さきほどの確認コマンドを実行してバージョンが上がったことを確認してみましょう。

ansible rp1 -m command -a 'nginx -V'
1

# スケールアップ

次はアプリケーションサーバをスケールアップ(増設)してみましょう。 と言っても増設用のサーバはすでにコンテナとして起動してあるので、皆さんがすべきことは下記 2 つのファイルの編集です。

# inventories/hosts

まずは Ansible の管理対象にアプリケーション増設用のサーバ(app2)を追加します。 教材のinventories/hostsを下記のように追記してください。

[app]
app1
+ app2
1
2
3

これでappグループの中にapp2サーバを追加できました。

Ansible には管理対象のホストをグルーピングし、設定したグループ単位で Ansible を実行したり、変数を設定できたりします。 こうすることで、柔軟かつ効率的にサーバを管理できます。 この講義ではそれぞれ 1 台しかありませんが、データベースサーバやリバースプロキシサーバもグループに分けられています。

Ansible のグループやホストについての詳細は公式ドキュメントをご覧ください。

# inventories/group_vars/all.yml

次に変数ファイルを編集します。 さきほども編集したinventories/group_vars/all.ymlに下記のように追記します。

nginx_backends:
  - host: 192.0.2.12
    port: "{{ server_port }}"
+ - host: 192.0.2.13
+   port: "{{ server_port }}"
1
2
3
4
5

これは nginx が通信を経由させるサーバのリストを格納している変数です。 ここに新しくapp2サーバの情報を追記することで、nginx がapp2サーバにも通信を流してくれます。

nginx はロードバランサ(負荷分散装置)としての機能も備えているので、 こうすることで今までapp1サーバにしかいかなかった通信がapp2にも行くようになり、負荷を分散できます。

nginx の機能について詳しく説明することは主題から外れてしまいますので、この講義では行いません。 さらに詳しく知りたい人は nginx の公式ドキュメント (opens new window)をご覧ください。

# 実行

さて、ファイルの準備ができたら Ansible を実行します。 今回は事前にチェックを行ってみましょう。

ansible-playbook -C site.yml
1

dryrunで結果を確認すると、新たにapp2サーバが増えていることが確認できます。 また、nginx の設定ファイルの差分も確認できるはずです。

failed=0と表示されていれば問題ありません。 -C(--check)のオプションを外し、本実行を行いましょう。

コマンドが終了したら、ブラウザでhttps://localhost:8443 (opens new window)にアクセスして確認してみましょう。

この Web アプリケーションは動作しているサーバのホスト名を画面に表示しています。 新しくブラウザを開きhttps://localhost:8443 (opens new window)にアクセスすると、ホスト名の表記が変わっているはずです。

これにより、nginx が正常に負荷を分散してくれていることが確認できます。

ここで、もう一つ注目していただきたいところがあります。 Ansible の実行ログの中に、RUNNING HANDLERという表記が確認できると思います。 さきほどと同じ様にansible-playbook site.ymlと実行した場合、この表記がなくなると思います。

これは Ansible のハンドラという機能になります。 これは、ハンドラを設定したタスクがchangedのときにのみ実行されるタスクを設定できる機能です。 実際に実行されるタスクは各ロールのhandlers/main.ymlに書かれています。

これにより設定ファイルが変更された場合のみ、アプリケーションやデータベースなどのプロセスを再起動できます。 影響範囲を限定できるため、安全にホストに対して変更を加えることができます。

詳細は公式ドキュメント (opens new window)をご覧ください。

# 解答例

教材の solution ブランチ (opens new window)が解答例になっています。 この解答例以外にも多くの実現方法がありますが、参考にしてもらえればと思います。

他のファイルについても、この講義を受けた後の最終的なファイルの内容になっています。 ハンズオンを進める中でつまずくことがあれば、参考にしてもらえればと思います。

# コラム 特定の playbook のみ実行する

Ansible には冪等性を担保する為の仕組みが備わっているというのは記述したとおりです。 しかしながら、実行する前から不要と分かっている作業まで読み込ませ、Ansible に実行判断を任せるというのは 万が一を気にする運用の場では不安を感じたり、処理時間が惜しいと感じることもあるでしょう。

Ansible ではそのような際に、実行タスクを分けて実行する事も可能になっています。 今回のケースでは以下のように実行する事で nginx のみのタスクだけ実行する、といった事が可能になります。

ansible-playbook -C site.yml -t nginx
1

これはどのように実行しているのでしょうか。 実は Ansible にはタグという機能があり、タスクやロールなどに任意の値(タグ)を付けることができます。 これを利用することで、Ansible 実行時に任意のタスクのみを実行する/しないことができます。 実はさきほどのplaybooks/rp.ymlにタグが設定してありました。

タグは増やしすぎても管理がたいへんになるので、ある程度のまとまりやよく使うタスクにのみ付与するのが良いでしょう。 タグの詳細については公式ドキュメント (opens new window)をご覧ください。


CC BY-SA Licensed | Copyright (c) 2023, Internet Initiative Japan Inc.