AnsibleでFortigateのLB機能を使ってみた
この記事は、Ansible 3 Advent Calendar 2019の22日目の記事です。
昨日分は、よこちさんのNetBoxに関する記事でした。
Ansibleを構成管理ツールとして使う場合、特にTower/AWXを使わない場合、インベントリをAnsibleで管理するか…というのは悩ましい点ですよね。NetBoxも一つの解にできそうですね。
さて、私の分は「またFortigate」です。すみません。
Ansibleのネットワーク系の課題をやると、A10/BIG-IPさんの課題でよく例に出る「ロードバランシング機能」があります。
実は、Fortigateでもロードバランシング機能が存在します。それをAnsibleで利用するまでの流れを記載させていただきます。
Ansible関連の実践記事一覧はこちら。
目的
fortigateでロードバランシング機能を使うためのPlaybookの書き方や、<your_own_value>を明らかにするため
最初に結論
- AnsibleでFortigateのLB機能を一通り設定することができた
- ヘルスチェックなども問題なく動作することが分かった
- 【残課題】手動での動作確認を、assertなどで実施できるよう学習を進める
Ansible/Fortigate環境
構成図
このような感じの構成で確認を行いました
ロードバランサー的な用語でいうと、「ワンアーム構成」です。
(自宅のネットワーク環境の構成上、これ以外つらいんです。直さねば)
前提条件
- Webサーバはすでに構築されている前提
- 各々の index.htmlにはホストを示す異なるコンテンツを配備
- Fortigateは、シングル構成、DMZのIFにIPが付与されている程度
- 前回作成したHA環境は今回は使っていません。
- クライアント(Ansibleホスト)はVIPで提供される「192.168.10.113」にWebアクセスする
- FortigateがLBとして動作し、ラウンドロビンで各Webサーバにバランシングする
- ヘルスチェックでDownを検知したサーバには振り分けをしないようにする。
- Webサーバへ接続する際には、Fortigateのアドレスで送信元NATする。
事前確認
■Webサーバ#1へのアクセス確認 $ curl http://192.168.10.111 Web01(192.168.10.111) ■Webサーバ#2へのアクセス確認 $ curl http://192.168.10.112 Web02(192.168.10.112)
→各Webサーバにアクセスした際、明示的に異なる結果になることを確認
LB機能が動作する環境を作成する
事前の補足
Fortigateのロードバランシング機能は、WebUIのメニュー上は[Virtual Servers]という名称です。
ただ、コマンドは[vip]だったり、[Load Balancing]だったり、いろいろ変わってくるのでちょっとわかりにくいです。
NAT機能部分に[Virtual IP]とか[IP Pool]とかもあるのでお間違え無きよう。
作業の流れ
全体的な作業の流れとしては、以下のようになります。
- Feature SetでLoad Balancing機能(表示するか否か)を有効化する。
- ヘルスモニターを作成する。
- Virtual Serverを作成する。
- 作成したVirtual Serverを用いたポリシーを設定する。
なお、最初のFeature Setは、無視しても設定は可能です。
作成したPlaybook
いろいろ使ったので、結構長くなりました。 with_item構成にしたほうが恰好がいい(ヘルスチェックのところとか)とも思うんですが、どういう単位でPlaybookを再利用するかによりますかね。個人的にはわかりやすさ重視なのでべた書きでも良い派です。
構成要素ごとに簡単に説明もします。
--- - hosts: fortigate gather_facts: false vars: web01_address: "192.168.10.111" web02_address: "192.168.10.112" vip_address: "192.168.10.113" vip_name: "Ansible_TEST_VIP" tcp_port: "80" vip_if: "dmz" balance_method: "round-robin" policy_srcintf: "{{ vip_if }}" policy_dstintf: "{{ vip_if }}" policy_service: "HTTP" tasks: #1. Feature SetでLoad Balancing機能(表示するか否か)を有効化する。 - name: Enable Loadbalance Feature Visibillity fortios_system_settings: system_settings: gui_load_balance: "enable" #2. ヘルスモニターを作成する。 - name: Create Health Monitor(PING) fortios_firewall_ldb_monitor: firewall_ldb_monitor: name: "PING" state: "present" type: "ping" interval: "10" retry: "3" timeout: "2" port: "0" - name: Create Health Monitor(TCP_80) fortios_firewall_ldb_monitor: firewall_ldb_monitor: name: "TCP_80" state: "present" type: "tcp" interval: "10" retry: "3" timeout: "2" port: "{{ tcp_port }}" - name: Create Health Monitor(HTTP_GET_80) fortios_firewall_ldb_monitor: firewall_ldb_monitor: name: "HTTP_GET_80" state: "present" type: "http" interval: "10" retry: "3" timeout: "2" port: "{{ tcp_port }}" http_get: "" http_match: "" #3. Virtual Serverを作成する。 - name: Create VIP Setting fortios_firewall_vip: firewall_vip: state: "present" arp_reply: "enable" name: "{{ vip_name }}" comment: "{{ vip_name }}" id: "0" extip: "{{ vip_address }}" extintf: "{{ vip_if }}" protocol: "tcp" extport: "{{ tcp_port }}" server_type: "http" ldb_method: "{{ balance_method }}" type: "server-load-balance" monitor: - name: "PING" - name: "TCP_80" - name: "HTTP_GET_80" realservers: - id: "1" client_ip: "" ip: "{{ web01_address }}" port: "{{ tcp_port }}" status: "active" healthcheck: "vip" - id: "2" client_ip: "" ip: "{{ web02_address }}" port: "{{ tcp_port }}" status: "active" healthcheck: "vip" #4. 作成したVirtual Serverを用いたポリシーを設定する。 - name: Create Firewall Policy for VIP fortios_firewall_policy: state: "present" firewall_policy: policyid: "2" srcintf: - name: "{{ policy_srcintf }}" dstintf: - name: "{{ policy_dstintf }}" service: - name: "{{ policy_service }}" srcaddr: - name: "all" dstaddr: - name: "{{ vip_name }}" schedule: "always" action: "accept" nat: "enable" status: "enable"
1. Feature SetでLoad Balancing機能(表示するか否か)を有効化する。
詳細はこちら
- name: Enable Loadbalance Feature Visibillity fortios_system_settings: system_settings: gui_load_balance: "enable"
WebUIで見るとこの部分です。
これを有効化すると、左側のメニューの[Policy & Objects]に[Virtual Servers],[Health Check]というメニューが登場します。
2. ヘルスモニターを作成する。
詳細はこちら
- name: Create Health Monitor(HTTP_GET_80) fortios_firewall_ldb_monitor: firewall_ldb_monitor: name: "HTTP_GET_80" state: "present" type: "http" interval: "10" retry: "3" timeout: "2" port: "{{ tcp_port }}" http_get: "" http_match: ""
HTTP監視の部分を持ってきました。
fortios_firewall_ldb_monitor
というモジュールを使います。モジュールのリファレンスはこちら。
所定のパスの応答(http_get)
や、レスポンスのチェック(http_match)
も可能なようです。
3. Virtual Serverを作成する。
詳細はこちら
- name: Create VIP Setting fortios_firewall_vip: firewall_vip: state: "present" arp_reply: "enable" name: "{{ vip_name }}" comment: "{{ vip_name }}" id: "0" extip: "{{ vip_address }}" extintf: "{{ vip_if }}" protocol: "tcp" extport: "{{ tcp_port }}" server_type: "http" ldb_method: "{{ balance_method }}" type: "server-load-balance"
前半部分では、対象のVIPの名前やアドレス、インターフェースの定義などを行います。
fortios_firewall_vip
というモジュールを使います。モジュールのリファレンスはこちら。
ldb_method
という部分がロードバランシング機能の肝です。
least-session(既存コネクション数の少ないもの)、Weighted(重みづけラウンドロビン)など、他のアルゴリズムもいくつか選択できるようです。
monitor: - name: "PING" - name: "TCP_80" - name: "HTTP_GET_80" realservers: - id: "1" client_ip: "" ip: "{{ web01_address }}" port: "{{ tcp_port }}" status: "active" healthcheck: "vip"
後半に2.で作成したヘルスモニターや、バランシング対象の実サーバの情報を登録します。対象サーバの指定で、ポリシー用のオブジェクトが使えればいいんですが、利用できないようです。残念。
4. 作成したVirtual Serverを用いたポリシーを設定する。
詳細はこちら
- name: Create Firewall Policy for VIP fortios_firewall_policy: state: "present" firewall_policy: policyid: "2" srcintf: - name: "{{ policy_srcintf }}" dstintf: - name: "{{ policy_dstintf }}" service: - name: "{{ policy_service }}" srcaddr: - name: "all" dstaddr: - name: "{{ vip_name }}" schedule: "always" action: "accept" nat: "enable" status: "enable"
別の記事でも利用した、fortios_firewall_policy
を利用します。ワンアーム構成なので、実態としてはsrcintf/dstintfは同じものです。dstaddrの部分に、作成したVirtual Serverを宣言します。送信元NATはここで定義しています。
動作確認
バランシング確認
Ansibleホストからcurlで確認してみたところ、問題なくバランシングされていました。
$ curl http://192.168.10.113 Web01(192.168.10.111) $ curl http://192.168.10.113 Web02(192.168.10.112) $ curl http://192.168.10.113 Web01(192.168.10.111) $ curl http://192.168.10.113 Web02(192.168.10.112)
NAT確認
192.168.10.61 - - [22/Dec/2019:15:27:39 +0900] "GET / HTTP/1.1" 200 23 "-" "curl/7.29.0"
curlでアクセスしている際の送信元IPがちゃんとFortigateのアドレス(192.168.10.61)になっています。
ヘルスチェック確認(定期監視)
サーバ側のアクセスログは以下のようになっていました。
ちゃんとインターバルで定義した10秒ごとにチェックが来ています。 User-AgentがFortiOS 4.0というのがなんとも…
192.168.10.61 - - [22/Dec/2019:12:13:49 +0900] "GET / HTTP/1.0" 200 23 "-" "FortiGate (FortiOS 4.0)" 192.168.10.61 - - [22/Dec/2019:12:13:59 +0900] "GET / HTTP/1.0" 200 23 "-" "FortiGate (FortiOS 4.0)" 192.168.10.61 - - [22/Dec/2019:12:14:09 +0900] "GET / HTTP/1.0" 200 23 "-" "FortiGate (FortiOS 4.0)"
ヘルスチェック確認(ダウン時)
試しにWeb01のhttpdをダウンさせ、動作確認してみます。
$ curl http://192.168.10.113 Web02(192.168.10.112) $ curl http://192.168.10.113 Web02(192.168.10.112) $ curl http://192.168.10.113 Web02(192.168.10.112) $ curl http://192.168.10.113 Web02(192.168.10.112)
ちゃんとWeb02側だけにバランシングされました。
イベントログにもこんな形でログが記録されます。(VirtualServer側には何も出ません)
まとめ
以下のような結果になりました。
- AnsibleでFortigateのLB機能を一通り設定することができた
- ヘルスチェックなども問題なく動作することが分かった
- 【残課題】手動での動作確認を、assertなどで実施できるよう学習を進める
商用でFortigateをLBとして使うケースはあまりないかも、とも思いますが、中小規模環境で、予算が削られて個別のLBが買えない、なんていう場合には利用できるかもしれません。(性能面は要検証です)
今回は試していませんが、SSLオフロード用機器としても利用できそうです。 また、今回はIPv4のみですが、IPv6や64/46も可能なようです。
なんかAnsibleというよりFortigate機能検証みたいな形になっていますが、どなたかのお役に立てれば幸いです。
バトンリレー
前回に引き続き、今回も無事にバトンをつなぐことができました。
お次は、@atsushi586さんです。
よろしくお願いいたします!