DB2DBA: AnsibleでDB2のインストールを自動化する

最近Infrastructure as Codeに興味を持ちAnsibleで遊んでいるのですが、Ansible GalaxyにDB2インストール用のRoleが公開されていたので、Roleの勉強がてら試してみました。

- [bernardoVale.db2](https://galaxy.ansible.com/bernardoVale/db2/)

# 検証環境
- DB2 11.1 Express-C Edition 
- VirtualBox 5.2.3
- Vagrant 2.0.3
- Ubuntu Linux 16.04.4 64-bit (ubuntu/xenial64)
- Ansible 2.5.0
- Python 2.7.12

# Roleの構成
```
roles
└── bernardoVale.db2
    ├── ansible.cfg
    ├── defaults
    │   └── main.yml
    ├── examples
    │   ├── creating_users.yml
    │   ├── custom_instance.yml
    │   ├── databases.yml
    │   ├── db2_express-c_example.yml
    │   ├── db2_without_instances.yml
    │   ├── docker_test_without_instances.yml
    │   ├── docker_test.yml
    │   ├── downloading_db2.yml
    │   ├── full_example.yml
    │   ├── global_profile.yml
    │   ├── instance_with_custom_params.yml
    │   ├── local_db2.yml
    │   └── multiples_instances.yml
    ├── handlers
    │   └── main.yml
    ├── library
    │   └── db2
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   ├── create_databases.yml
    │   ├── get_db2.yml
    │   ├── instance_users.yml
    │   ├── main.yml
    │   ├── packages-debian.yml
    │   └── packages-redhat.yml
    ├── templates
    │   └── db2server.j2.rsp
    ├── tests
    │   ├── Dockerfile
    │   └── Dockerfile_base
    ├── Vagrantfile
    └── vars
        ├── Debian.yml
        ├── main.yml
        └── RedHat.yml
```

実行される主な処理は以下のようになっています。

- インストーラのダウンロードと展開
- 前提パッケージのインストール
- インスタンスユーザとfencedユーザの作成
- SE Linux無効化
- db2prereqcheckによる前提パッケージの確認
- テンプレートからの応答ファイル作成
- [応答ファイルによる自動インストール](https://qiita.com/SVC34/items/2d0bd3576022728cb152)
- インスタンスの作成 (Optional)
- db2valによるインストール結果の確認
- データベースの作成 (Optional)

独自モジュールのlibrary/db2ではデータベースの作成の際にインスタンスの稼働確認、インスタンスの立ち上げ、CREATE DBコマンドの組み立てと実行を行います。

# 検証
VagrantのAnsible Localによるプロビジョニングで仮想マシンの作成から、Ansibleのインストール、DB2のインストールまでの一連の処理を行ってみました。

## 設定ファイルの作成
### Vagrantfile
Vagrantで設定する共有ディレクトリ経由でPlaybookとインストーラをゲストOSに渡します。

```ruby
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"

  config.vm.define "db" do | db |
    config.vm.synced_folder "./vagrant_db", "/vagrant"

    config.vm.provision "ansible_local" do |ansible|
          ansible.galaxy_role_file = 'requirements.yml'
          ansible.inventory_path = "hosts"
          ansible.extra_vars = "extra.yml"
          ansible.playbook = "site.yml"
    end
  end
end
```

### requirements.yml
```yml
- src: bernardoVale.db2
```

### hosts
```
[db]
localhost ansible_connection=local
```

### extra.yml
vars/Debian.ymlで指定されている前提パッケージにbinutilsが不足していることが判明。変数の優先順位の問題でちょっと悩みましたが、extra varsでオーバーライドすることにしました。extra varsはAnsibleの変数で最も高い優先順位を持ちます。

```yml
db2_packages:
 - binutils
 - libstdc++6
 - libaio1
 - unzip
 - libx32stdc++6
 - libpam0g
 - lib32stdc++6
 - libpam0g:i386
```

### site.yml
examplesを参考に、データベースの作成までを行うPlaybookを作成しました。


```yml
- hosts: db
  become: yes
  gather_facts: True
  roles:
    - bernardoVale.db2
  vars:
    resp:
      prod: "EXPRESS_C"
      file: "/opt/ibm/db2/V11.1"
      lic_agreement: "ACCEPT"
      install_type: "COMPACT"

    db2_creates: expc

    db2_binary:
       location: "/vagrant/v11.1_linuxx64_expc.tar.gz"
       dest: "/tmp"

    db2_instances:
      - instance: "DB2INST"
        name: "db2inst1"
        password: "{{ 'inst1inst1' | password_hash('sha512') }}"
        group_name: "db2iadm1"
        fenced_username: "db2fenc1"
        fenced_group_name: "db2fadm1"
        fenced_password: "{{ 'fenc1fenc1' | password_hash('sha512') }}"

    databases:
      - name: testdb01
        instance: db2inst1
        codeset: "UTF-8"
        territory: "jp"
```

## プロビジョニング
```
PS C:\Users\svc34> vagrant up db
```

or

```
PS C:\Users\svc34> vagrant provision db
```

## 実行結果
Ansibleのログのみ載せます。非推奨機能に関する警告が出ていますが、インストールは問題なく行えています。

```
PLAY [db] **********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [bernardoVale.db2 : Include OS-Specific variables] ************************
ok: [localhost]

TASK [bernardoVale.db2 : Downloading DB2] **************************************
skipping: [localhost]

TASK [bernardoVale.db2 : Decompressing DB2 Server] *****************************
changed: [localhost]

TASK [bernardoVale.db2 : Installing DB2 pre requisites packages - RedHat] ******
skipping: [localhost] => (item=[])

TASK [bernardoVale.db2 : Adding i386 packages on Apt] **************************
changed: [localhost]

TASK [bernardoVale.db2 : Installing DB2 pre requisites packages - Debian] ******
changed: [localhost] => (item=[u'binutils', u'libstdc++6', u'libaio1', u'unzip', u'libx32stdc++6', u'libpam0g',
u'lib32stdc++6', u'libpam0g:i386'])
[DEPRECATION WARNING]: State 'installed' is deprecated. Using state 'present'
instead.. This feature will be removed in version 2.9. Deprecation warnings can
 be disabled by setting deprecation_warnings=False in ansible.cfg.

TASK [bernardoVale.db2 : Fixing Libpam] ****************************************
changed: [localhost]

TASK [bernardoVale.db2 : Creating DB2 Fenced Groups] ***************************
changed: [localhost] => (item={u'name': u'db2inst1', u'fenced_group_name': u'db2fadm1', u'group_name': u'db2iadm
1', u'instance': u'DB2INST', u'fenced_password': u'$6$czDN4W0k/2BAUcDV$C1oZyKxEWSkIVYM86fCW8UFXZVrl3wiNl0dTAxLXx
QULeLZZVT1YkoWsDL0RUhYSYBi0DQNn4K/vnGNBFuLRS1', u'password': u'$6$/enafvSqhP9Zo1nw$Tp36E1HFuLftNobmYhTCWtVQ6QeeM
VspxJazMJ6t8UoiVOFB2v84McC1SekAJJnLfWzCzbjKNYFPx5s6SPj.Z.', u'fenced_username': u'db2fenc1'})

TASK [bernardoVale.db2 : Creating DB2 Instance Groups] *************************
changed: [localhost] => (item={u'name': u'db2inst1', u'fenced_group_name': u'db2fadm1', u'group_name': u'db2iadm
1', u'instance': u'DB2INST', u'fenced_password': u'$6$eM8XpJd.2IbPqfj.$NjjYgVvjq7VpWQ8p3ZiZM/9evsIGKMLtbOfSyfAfc
RYlYK8cNZS28jDyQ4iG9pc0pIqtYgBOL33OAAzQ3Bk0T1', u'password': u'$6$ez.zprpelCKo7fdd$gF984CrMph5Pb5IcKjOLLE49USauW
6SPdN8sl6i8KrycDgh015covUx12CnqWxwf4L19n4VSbfv44q8kfk2Go0', u'fenced_username': u'db2fenc1'})

TASK [bernardoVale.db2 : Creating DB2 Instance Users] **************************
changed: [localhost] => (item={u'name': u'db2inst1', u'fenced_group_name': u'db2fadm1', u'group_name': u'db2iadm
1', u'instance': u'DB2INST', u'fenced_password': u'$6$EXWWQgYWwtjVaTXY$VOo6.2jHBt1HJvZ.FdPejNrYgoL9Kpoxft1arXN3o
fsD0fnmZdiYqfno7lZtIwZ1dXjl0Xx2/zvNpYpOa9rfy/', u'password': u'$6$8qPt8NCmmna.3eR8$Ra0igfqDZiVO0faBvLsmPgB4vennY
aHEyyLWt8FuAfFNPA/46OJJ6u2LgTir0gVPAzbc1qZVYEh4SN7st32ql.', u'fenced_username': u'db2fenc1'})

TASK [bernardoVale.db2 : Creating DB2 Fenced Users] ****************************
changed: [localhost] => (item={u'name': u'db2inst1', u'fenced_group_name': u'db2fadm1', u'group_name': u'db2iadm
1', u'instance': u'DB2INST', u'fenced_password': u'$6$NGqe7QW97o4Lacvk$9kkUTcAYv5cyXNll1UD7fjR72zzYZdG1zZA1/jetW
Xhcz1E4VWMqI8C.MOyS/2/5.fMJadjM.bEpu.LIMhNcf/', u'password': u'$6$R6vPlSlZXwGb.dXQ$.sIP0YfCTzqs6Y1tAqGPaYavqK29B
J2DN6apBWbdqXJOL9x2gtNB66hNyUXDM8aOxAB9ykTA3f8oJSw8roZoK/', u'fenced_username': u'db2fenc1'})

TASK [bernardoVale.db2 : Disabling SELinux] ************************************
skipping: [localhost]

TASK [bernardoVale.db2 : Adding entry to /etc/hosts] ***************************
changed: [localhost]

TASK [bernardoVale.db2 : Running DB2 Pre Requisits Check] **********************
ok: [localhost]

TASK [bernardoVale.db2 : Parse response file] **********************************
changed: [localhost]

TASK [bernardoVale.db2 : Installing DB2 10.5] **********************************
changed: [localhost]

TASK [bernardoVale.db2 : Setup results] ****************************************
ok: [localhost] => {
    "db2_setup": {
        "changed": true,
        "cmd": [
            "/tmp/expc/db2setup",
            "-r",
            "/tmp/db2server.rsp"
        ],
        "delta": "0:03:22.115082",
        "end": "2018-03-29 21:53:18.795695",
        "failed": false,
        "rc": 0,
        "start": "2018-03-29 21:49:56.680613",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "DBI1191I  db2setup is installing and configuring DB2 according to the\n      response file pr
ovided. Please wait.\n\n\nThe execution completed successfully.\n\nFor more information see the DB2 installation
 log at \"/tmp/db2setup.5015.log\".",
        "stdout_lines": [
            "DBI1191I  db2setup is installing and configuring DB2 according to the",
            "      response file provided. Please wait.",
            "",
            "",
            "The execution completed successfully.",
            "",
            "For more information see the DB2 installation log at \"/tmp/db2setup.5015.log\"."
        ]
    }
}

TASK [bernardoVale.db2 : Validating the current installation] ******************
ok: [localhost]

TASK [bernardoVale.db2 : Validate results] *************************************
ok: [localhost] => {
    "db2_val": {
        "changed": false,
        "cmd": [
            "/opt/ibm/db2/V11.1/bin/db2val",
            "-o"
        ],
        "delta": "0:00:32.981915",
        "end": "2018-03-29 21:53:52.840024",
        "failed": false,
        "failed_when_result": false,
        "rc": 0,
        "start": "2018-03-29 21:53:19.858109",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "DBI1379I  The db2val command is running. This can take several minutes.\n\nDBI1335I  Installa
tion file validation for the DB2 copy installed at\n      /opt/ibm/db2/V11.1 was successful.\n\nDBI1343I  The db
2val command completed successfully. For details, see\n      the log file /tmp/db2val-180329_215320.log.",
        "stdout_lines": [
            "DBI1379I  The db2val command is running. This can take several minutes.",
            "",
            "DBI1335I  Installation file validation for the DB2 copy installed at",
            "      /opt/ibm/db2/V11.1 was successful.",
            "",
            "DBI1343I  The db2val command completed successfully. For details, see",
            "      the log file /tmp/db2val-180329_215320.log."
        ]
    }
}

TASK [bernardoVale.db2 : Create Databases] *************************************
changed: [localhost] => (item={u'instance': u'db2inst1', u'territory': u'jp', u'name': u'testdb01', u'codeset':
u'UTF-8'})

TASK [bernardoVale.db2 : Create Database Log] **********************************
[DEPRECATION WARNING]: Using tests as filters is deprecated. Instead of using
`result|changed` instead use `result is changed`. This feature will be removed
in version 2.9. Deprecation warnings can be disabled by setting
deprecation_warnings=False in ansible.cfg.
ok: [localhost] => {
    "db_debug": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                "_ansible_ignore_errors": null,
                "_ansible_item_result": true,
                "_ansible_no_log": false,
                "_ansible_parsed": true,
                "changed": true,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "automatic": true,
                        "codeset": "UTF-8",
                        "instance": "db2inst1",
                        "name": "testdb01",
                        "pagesize": "",
                        "path": "",
                        "territory": "jp"
                    }
                },
                "item": {
                    "codeset": "UTF-8",
                    "instance": "db2inst1",
                    "name": "testdb01",
                    "territory": "jp"
                },
                "msg": "Database testdb01 created",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "DB20000I  The CREATE DATABASE command completed successfully.\n",
                "stdout_lines": [
                    "DB20000I  The CREATE DATABASE command completed successfully."
                ]
            }
        ]
    }
}

PLAY RECAP *********************************************************************
localhost                  : ok=20   changed=13   unreachable=0    failed=0

```

# 参考資料
- [Variable Precedence: Where Should I Put A Variable?](http://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable)