BIG-IP Virtual Editionを使ってみる。（その７：Ansibleによる冗長化設定）

# はじめに
前回は冗長化設定を行う前の準備を行ってきましたが、今回は実際に冗長化設定を`Ansible`で設定していきたいと思います。

- 【前】[BIG-IP Virtual Editionを使ってみる。（その６：Ansibleによる冗長化設定準備）](https://qiita.com/sakai00kou/items/2e57351609dd801f6472)

# Ansible Playbookの作成
冗長化設定用の`Playbook`を作成していきます。

今回は新たに以下のロールを作成します。

|ロール名|説明|
|:--|:--|
|setup_bigip-float-ip|フローティングIPの設定を行うロール|
|setup_bigip-trunk|HA Group用にTrunk設定を行うロール|
|setup_bigip-ha-group|冗長化設定を行うロール|

## ロールの作成
今までと同様、`ansible-galaxy`のコマンドで`Playbook`雛形を作成します。

```shell:ansible-galaxyコマンドでのロール雛形作成
cd roles/
ansible-galaxy init setup_bigip-float-ip
ansible-galaxy init setup_bigip-trunk
ansible-galaxy init setup_ha-group
```

## 冗長化設定用のフラグ設定
`BIG-IP`を2台構成で運用する場合、機器障害時に引き継ぎが必要な設定はどちらか一方で設定すればコンフィグ同期でもう一方の機器に設定されるため、アクティブ側機器で`Playbook`が実行された場合のみ処理をうようにするための変数を追加します。

[前回](https://qiita.com/sakai00kou/items/2e57351609dd801f6472)、前準備で負荷分散設定用ロールにフラグ設定を追加しましたが、ホスト変数ファイルに1号機を`master`、2号機を`backup`として変数を設定しておきます。

```yml:host_vars/bigip01.yml
ha_flag: master
```

```yml:host_vars/bigip02.yml
ha_flag: backup
```

## フローティングIP用taskの作成
冗長化グループ内で共有するフローティングIP（仮想IP）の設定を行います。

フローティングIPの設定はマスター側機器で設定してコンフィグ同期を行うことで、バックアップ側機器に転送されるため、先ほど設定した`ha_flag`で`master`機器のみ本ロールを実行するようにします。

```yml:roles/setup_bigip-float-ip/tasks/main.yml
---
# tasks file for setup_bigip-float-ip
- name: BIG-IP Self IP(Floating IP)の作成
  bigip_selfip:
    name: "{{ item.name }}"
    address: "{{ item.address }}"
    netmask: "{{ item.netmask }}"
    vlan: "{{ item.vlan }}"
    allow_service: "{{ item.allow_service }}"
    traffic_group: "{{ item.traffic_group }}"
    state: "present"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  when: ha_flag == "master"
  notify: save_config
  loop: "{{ bigip_floatip }}"
```

## コンフィグ保存用handlerの作成（フローティングIP）
前回と同様、設定変更が行われた場合にコンフィグをsaveする設定を行います。

`setup_bigip-ha-group`ロールは、同期処理でコンフィグ保存＆設定同期が行われるため、今回は`setup_bigip-flating-ip`ロールにのみ設定を行います。

```yml:roles/setup_bigip-float-ip/handlers/main.yml
---
# handlers file for setup_bigip-float-ip
- name: save_config
  bigip_config:
    save: "yes"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
```

## Trunk用taskの作成
`HA Group`で使用するために`Trunk`設定用の`Playbook`を作成します。

尚、`Virtual Edition`では`LACP`方式の`Trunk`（リンクアグリゲーション）設定ができないため、今回はコメントアウトしておきます。

```yml:roles/setup_bigip-trunk/tasks/main.yml
---
# tasks file for setup_bigip-trunk
- name: BIG-IP trunkの作成
  bigip_trunk:
    name: "{{ item.name }}"
    description: "{{ item.description }}"
    interfaces: "{{ item.interfaces }}"
    link_selection_policy: "{{ item.link_selection_policy }}"
    frame_distribution_hash: "{{ item.frame_distribution_hash }}"
#    lacp_enabled: "{{ item.lacp_enabled }}"
#    lacp_mode: "{{ item.lacp_mode }}"
#    lacp_timeout: "{{ item.lacp_timeout }}"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  notify: save_config
  loop: "{{ bigip_trunk }}"
```

## コンフィグ保存用handlerの作成（Trunk）
先ほどのフローティングIP用の`Playbook`と同様、設定変更が行われた場合にコンフィグをsaveする設定を行います。

```yml:roles/setup_bigip-trunk/handlers/main.yml
---
# handlers file for setup_bigip-trunk
- name: save_config
  bigip_config:
    save: "yes"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
```

## グループ変数の作成
グループ変数ファイルに`Trunk`設定用変数とフローティングIP用変数を追加します。

また、インタフェースにTrunk設定を行ったため、[その４](https://qiita.com/sakai00kou/items/92c73ee329d1cf63318a)で設定したVLAN設定の`untagged_interface`をインタフェース名からTrunk名に変更します。

```yml:group_vars/bigip.yml
（抜粋）
# インタフェース設定
bigip_interface:
  - { untagged_interface: "external_trunk", name: "external", tag: "10" }
  - { untagged_interface: "internal_trunk", name: "internal", tag: "20" }

# Trunk設定
bigip_trunk:
  - { name: "external_trunk", description: "", interfaces: ["1.1"], link_selection_policy: "auto", frame_distribution_hash: "source-destination-ip", lacp_enabled: "yes", lacp_mode: "active", lacp_timeout: "long"  }
  - { name: "internal_trunk", description: "", interfaces: ["1.2"], link_selection_policy: "auto", frame_distribution_hash: "source-destination-ip", lacp_enabled: "yes", lacp_mode: "active", lacp_timeout: "long"  }

# SelfIP設定(Floating IP)
bigip_floatip:
  - { name: "external_VIP", address: "10.1.0.254", netmask: "255.255.255.0", vlan: "external", allow_service: "none", traffic_group: "traffic-group-1" }
  - { name: "internal_VIP", address: "10.2.0.254", netmask: "255.255.255.0", vlan: "internal", allow_service: "none", traffic_group: "traffic-group-1" }
```

## HA Groupの冗長化方式概要
`HA Group`は`BIG-IP`の冗長系切替の方法で、指定のTrunk（リンクアグリゲーション）、Poolに対してスコアを設定し、よりスコアが高いグループメンバに切り替える方式となります。

また、系状態が`Active`となっている機器だけに`Active Bonus`の値が付与されることで、グループ内でスコアが最大となるようになっておりますが、障害等で一部インタフェースやPoolメンバに異常が発生した場合、指定した値分、スコアを落とすことで、`Standby`となっている機器のスコアのほうが高くなり、系状態が切り替わります。

以下HA Groupのスコア管理概要。

![BIG-IP_HAGroup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/8572002a-ec08-02b6-b5dd-3e660beb82e1.png)

実際の`HA Group`の設定としては、スコア管理する`Trunk`もしくは`Pool`の指定、`Active Bonus`の値の設定となり、本設定は1号機、2号機ともに設定します。

## 冗長化設定用tasksの作成（Device Connectivity）
今回は冗長化設定を1ロールにまとめて実行するようにしようと思います。

ロールが長くなるのでそれぞれの定義ごとに分けて作成＆説明します。

コンフィグ同期、フェイルオーバチェック用通信のやり取り、コネクション同期で使用するVLANを指定する設定となります。

通常は`BIG-IP`同士を直接繋いだ系間ネットワーク（上図のConfig/Mirror同期、Failover Network）を使用しますが他のネットワークを使用することもできます。

お互いに自分のVLANを指定する必要があるため、本設定は1号機、2号機ともに設定します。

```yml:roles/setup_bigip-ha-group/tasks/main.yml
- name: BIG-IP Device Connectivity設定
  bigip_device_connectivity:
    failover_multicast: "no"
    cluster_mirroring: "within-cluster"
    config_sync_ip: "{{ item.config_sync_ip }}"
    unicast_failover:
      - address: "{{ item.unicast_failover }}"
    mirror_primary_address: "{{ item.mirror_primary_address }}"
    mirror_secondary_address: "{{ item.mirror_secondary_address }}"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  loop: "{{ bigip_device_connectivity }}"
```

## 冗長化設定用taskの作成（Trust Domain）
`BIG-IP`で冗長化を行うためには、冗長化を行う機器同士で信頼関係を結ぶ必要があるため、`Trust Domain`の設定を行っていきます。

実際には各`BIG-IP`が持つ証明書を互いにやり取りすることで信頼関係を結びます。

本設定はどちらか一方の`BIG-IP`から相手側の`BIG-IP`に接続して証明書をやり取りできれば良いため、先ほど`master`のフラグ設定を行った`BIG-IP`のみで動作する`Playbook`を作成していきます。

各`Peer`設定は後ほど`backup`フラグ設定を行った`BIG-IP`の情報を設定するようにします。

```yml:roles/setup_bigip-ha-group/tasks/main.yml
- name: BIG-IP Trust Domain設定
  bigip_device_trust:
    peer_server: "{{ item.peer_server }}"
    peer_hostname: "{{ item.peer_hostname }}"
    peer_user: "{{ bigip_user }}"
    peer_password: "{{ bigip_password }}"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  when: ha_flag == "master"
  loop: "{{ bigip_device_trust }}"
```

## 冗長化設定用taskの作成（Device Group）
信頼関係となった機器同士のグループを作り、グループ内でコンフィグの同期や障害発生時の系切替の設定を行います。

`Sync-Failover`形式で設定した場合、`Device Group`で指定した機器間で系切替やコンフィグ同期が行われます。

`Playbook`の設定としては、先に`Device Group`自体の設定を行ってから、作った`Device Group`にメンバーを割り当てるように設定を行います。

```yml:roles/setup_bigip-ha-group/tasks/main.yml
- name: BIG-IP Device Group設定
  bigip_device_group:
    name: "{{ item.name }}"
    description: "{{ item.description }}"
    full_sync: "{{ item.full_sync }}"
    network_failover: "yes"
    save_on_auto_sync: "{{ item.save_on_auto_sync }}"
    type: "sync-failover"
    provider: "{{ bigip_provider }}"
    state: "present"
  delegate_to: "{{ bigip_delegate_to }}"
  when: ha_flag == "master"
  loop: "{{ bigip_device_group }}"

- name: BIG-IP Device Group Member設定
  bigip_device_group_member:
    name: "{{ item.name }}"
    device_group: "{{ item.device_group }}"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  when: ha_flag == "master"
  loop: "{{ bigip_device_group_member }}"
```

## 冗長化設定用taskの作成（コンフィグ同期）
上記で行った設定を`backup`機器に反映させるため、コンフィグ同期の処理を行います。

```yml:roles/setup_bigip-ha-group/tasks/main.yml
- name: BIG-IP 初期同期
  bigip_configsync_action:
    device_group: "{{ item.device_group }}"
    sync_device_to_group: "yes"
    overwrite_config: "yes"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  when: ha_flag == "master"
  loop: "{{ bigip_configsync_action }}"
```

## 冗長化設定用tasksの作成（HA Group）
`HA Group`は、指定した`Trunk`、`Pool`に付与するスコア、`Active`状態のときに付与するスコアの設定を行い、設定した`HA Group`を後述する`Traffic Group`に割り当てることで、障害時にスコアに従った系切替を行えます。

`Playbook`での登録方法としては、`HA Group`でスコアの対象とする`Pool`、`Trunk`を複数設定する必要があるため、`with_nested`を使い、2つの変数グループをループさせています。

`bigip_device_ha_group`では`Pool`登録用、`Trunk`登録用で共通となる名前と`active_bonus`を呼び出し、`bigip_ha_pool`と`bigip_ha_trunk`で`HA Group`に登録する設定を繰り返すことでリソースを複数追加することができます。

また、変数のグループが複数ありますが、それぞれの変数を呼び出すには、`item.0.xxx`、`item.1.xxx`として変数を指定することで、呼び出すことができます。

尚、`HA Group`と次の`Traffic Group`の設定はコンフィグ同期を行う前でも設定できますが、`HA Group`の設定は1号機、2号機両方で設定する必要があり、2号機で設定していない`Pool`の設定情報が必要であること、`Traffic Group`の設定で`HA Group`を選択するためには先に`HA Group`を作成する必要があることから初回のコンフィグ同期後に設定を行うようにしています。

```yml:roles/setup_bigip-ha-group/tasks/main.yml
---
# tasks file for setup_bigip-ha-group
- name: BIG-IP HA group設定(pools)
  bigip_device_ha_group:
    name: "{{ item.0.name }}"
    active_bonus: "{{ item.0.active_bonus }}"
    pools: "{{ item.1.pools }}"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  with_nested:
    - "{{ bigip_device_ha_group }}"
    - "{{ bigip_ha_pool }}"

- name: BIG-IP HA group設定(trunks)
  bigip_device_ha_group:
    name: "{{ item.0.name }}"
    active_bonus: "{{ item.0.active_bonus }}"
    trunks: "{{ item.1.trunks }}"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  with_nested:
    - "{{ bigip_device_ha_group }}"
    - "{{ bigip_ha_trunk }}"
```

## 冗長化設定用taskの作成（Traffic Group）
`Device Group`間で共有するフローティングIPや負荷分散で使用するIPをやり取りするための`Traffic Group`を設定します。

障害時の切替方法として`HA Group`が指定できるようになっているため、今回は`HA Group`を指定するように設定します。

```yml:roles/setup_bigip-ha-group/tasks/main.yml
- name: BIG-IP Traffic-Group設定
  bigip_device_traffic_group:
    name: "{{ item.name }}"
    auto_failback: "no"
    ha_group: "{{ item.ha_group }}"
    provider: "{{ bigip_provider }}"
  delegate_to: "{{ bigip_delegate_to }}"
  loop: "{{ bigip_device_traffic_group }}"
```

## グループ変数の作成
今まで指定した各種変数をグループ変数にまとめて定義します。

以前作成したグループ変数に追加＆一部修正して下さい。

```yml:group_vars/bigip.yml
（抜粋）
# インタフェース設定
bigip_interface:
  - { untagged_interface: "external_trunk", name: "external", tag: "10" }
  - { untagged_interface: "internal_trunk", name: "internal", tag: "20" }
  - { untagged_interface: "1.3", name: "ha", tag: "30" }

# SelfIP設定(Floating IP)
bigip_floatip:
  - { name: "external_VIP", address: "10.1.0.254", netmask: "255.255.255.0", vlan: "external", allow_service: "none", traffic_group: "traffic-group-1" }
  - { name: "internal_VIP", address: "10.2.0.254", netmask: "255.255.255.0", vlan: "internal", allow_service: "none", traffic_group: "traffic-group-1" }

# 冗長化設定(device trust)
bigip_device_trust:
  - { peer_server: "192.168.56.102", peer_hostname: "bigip02.example.com" }

# 冗長化設定(device group)
bigip_device_group:
  - { name: "device_group", description: "device_group", full_sync: "no", save_on_auto_sync: "no" }

# 冗長化設定(device group member)
bigip_device_group_member:
  - { name: "bigip01.example.com", device_group: "device_group" }
  - { name: "bigip02.example.com", device_group: "device_group" }

# 冗長化設定(config sync)
bigip_configsync_action:
  - { device_group: "device_group" }

# 冗長化設定(HA Group設定)
bigip_device_ha_group:
  - { name: "HA_Group", active_bonus: "10" }

bigip_ha_pool:
  - pools:
     - pool_name: "web_pool"
       weight: "10"
       minimum_threshold: "1"

bigip_ha_trunk:
  - trunks:
     - trunk_name: "external_trunk"
       weight: "10"
       minimum_threshold: "1"
     - trunk_name: "internal_trunk"
       weight: "10"
       minimum_threshold: "1"

# 冗長化設定(traffic group)
bigip_device_traffic_group:
  - { name: "traffic-group-1", ha_group: "HA_Group" }
```

簡単に各変数の説明

####【インタフェース設定】
インタフェース1.3を新たに`Failover Network`（HA）用として割り当てるので、`bigip_interface`の変数に`Failover Network`用の設定を追加しています。

####【SelfIP設定(Floating IP)】
1号機と2号機で共有する仮想IP（Floating IP）を設定。
`traffic-group`はデフォルトの設定となる`traffic-group-1`を指定しています。

####【冗長化設定(device trust)】
1号機側から見た信頼関係を結びたい`BIG-IP`のアドレスとホスト名を指定します。

**コンフィグ同期や冗長化用に指定しているアドレスではなく**、ホスト名設定で結び付けているアドレス（マネジメント用IP）を指定します。

####【冗長化設定(device group)】
`Device Group`の名前と、`device_group`のタスクで設定した各種設定を指定。

####【冗長化設定(device group member)】
`Device Group`のメンバとして登録したいホストのホスト名と登録する`Device Group`名を設定。

####【冗長化設定(config sync)】
コンフィグ同期する`Device Group`名を設定。

####【冗長化設定(HA Group設定)】
`bigip_device _ha_group`でHAグループ名とボーナススコアの設定、`bigip_ha_pool`と`bigip_ha_trunk`で`HA Group`のスコア監視対象の`Pool`と`Trunk`を設定。

`bigip_ha_pool`と`bigip_ha_trunk`は書き方が他と異なりますが、今回の場合、今までの書き方（フロースタイル）だと読みづらくなるので、見やすさ重視でブロックスタイルで記載しています。

###【冗長化設定(traffic group)】
今回はデフォルトの`Traffic Group`を使用しているため、`Traffic Group`は`traffic-group-1`、`HA Group`名は`HA_Group`を設定。

## ホスト変数の作成
1号機、2号機ともに個別の`Device Connectivity`の設定が必要になるため、ホスト変数に以下を追加します。

```yml:host_vars/bigip01.yml
# 冗長化設定(device connectivity)
bigip_device_connectivity:
  - { config_sync_ip: "10.3.0.1", unicast_failover: "10.3.0.1", mirror_primary_address: "10.3.0.1", mirror_secondary_address: "" }
```

```yml:host_vars/bigip01.yml
# 冗長化設定(device connectivity)
bigip_device_connectivity:
  - { config_sync_ip: "10.3.0.2", unicast_failover: "10.3.0.2", mirror_primary_address: "10.3.0.2", mirror_secondary_address: "" }
```

## インベントリファイルの作成
今回2号機を追加したのでインベントリファイルにも追記しておきます。

```yml:hosts-all
[bigip]
bigip01
bigip02
```

## グループごとのロール実行ファイルの作成
今までの回と同様、`bigip.yml`ファイルに今回作成したロールを追加します。

今回はTrunkを先に作成してからVLANを定義する必要があるため、`setup_bigip-trunk`を`setup_bigip-initconf`より前に持ってきています。
※依存関係の設定は今回は省略

```yml:bigip.yml
---
- hosts: bigip
  gather_facts: no
  roles:
    - setup_bigip-license
    - setup_bigip-trunk
    - setup_bigip-initconf
    - setup_bigip-node
    - setup_bigip-pool
    - setup_bigip-virtual-server
    - setup_bigip-float-ip
    - setup_bigip-ha-group
```

## 全体のロール実行ファイルの作成
今回も変更なしのため割愛。

# Ansibleの実行
`Playbook`を実行します。

今回は2台で連携して冗長化設定や同期処理が行われるので、時間がかかります。

また、仮想マシンを動かしているホスト側のCPUやメモリリソースに余裕が無いと設定が失敗することがありますが、その場合はリソースを増やしたり時間を置いたりして何度か実行してみましょう。

```shell:Playbookの実行
ansible-playbook -i hosts-all site.yml
```

今回作成した`Playbook`はGitHubにもアップロードしているので、記事で分かりづらい部分は実際のソースを確認してみて下さい。
https://github.com/sakai00kou/qiita

# おわりに
一通りの`BIG-IP`の設定を行ってみて、基本設定であれば、全て`Ansible`で設定を行うことも可能だと感じました。

また、`Ansible`にモジュールが無い場合でも、`tmsh`で大体設定が可能なので、`bigip_command`モジュールを使って、tmshを実行するようにすれば、構築をすべてコード化して管理できるため、業務等で`BIG-IP`を使用されている方は、ぜひ`Ansible`でコード化してみることをお勧めします。