ENGINEER BLOG

ENGINEER BLOG

AnsibleでDB2の操作を自動化してみた

こんにちは。
ITサービス本部 板倉です。

普段は障害対応をメインにDB2とかIHS/WASとかのIBM製品を触っています。
他の人が書いているような最新技術にはかなり疎いです。

今回は最近触り始めた
Ansible」というツールについて書いてみます。

ベストプラクティスまみれで正直最新技術とは呼べないのですが、
田舎者の私にとっては最新鋭のツールなので問題ありません。

※「構成管理ツール」として名高いAnsibleをありえないくらい小規模に使用していきます。
※「DevOps」とか「Infrastructure as Code」みたいな高尚な概念についてはあまり触れません。

1.Ansibleって?

https://www.ansible.com/
『Ansible公式』

「Infrastructure as Code」「DevOps」「構成管理ツール」という文脈で
語られるツールの中では最大手と言えると思います。

僕にとって大事なのは
・シンプルで簡単
・誰がやっても何回やっても同じ結果(冪等性)
というところです。
僕みたいなエンジニア紛いでも簡単に触れるし、
1度作っちゃえば僕以外の人でも同じことができる(というか起こる)ので、
省力かつ安全な運用が実現できます。できるはずです。

ここでは運用担当者が手順書を見ながら手動で行う行為を
全てコーディングして自動でできるツールと思ってください。
(本来のAnsibleの役割はさらに深いところにあります)

2.やりたいこと

僕が心から愛するDB2の構成管理(とギリギリ呼べそうな行為)をAnsibleで実行してみたい。

3.環境

■OS:CentOS6.7
■Ansible:2.4.2
■DB2:v10.5.0.7

※DB2とAnsibleは別サーバです

4.実際にやってみた

(1)モジュールを追加

Ansibleには豊富なモジュールがあると言いながら、
「DB2」というモジュールがデフォルトで用意されていません。
http://docs.ansible.com/ansible/latest/modules_by_category.html
Mysqlとかpostgresはあります。
Oracleはありません。OSS製品限定のようですね。

じゃあ自分で作れという話なのですが、そんな技術もないので、
今回はオランダの方が作った以下のExtraモジュールを拝借しました。

『db2 modules for use with Ansible』
https://github.com/yhekma/ansible_extra.git

これにより、playbookでdb2コマンドを発行することができます。

サーバに落としたらplaybookが置いてあるディレクトリに
「library」を置くだけです。

ansible_root  
    └─db2_directory.yml  #playbook①  
    └─db2list.yml        #playbook②  
    └─db2_update_cfg.yml #playbook③  
    └─inventory          #インベントリ  
    └─library            #Extraモジュールのlibrary    

※ちなみに、DB2コマンドを使いたいだけならshellモジュールでも実現可能です。

shell: /opt/ibm/db2/V10.5/bin/db2 connect to ITAKDB01  

ただし、task間でconnect状態を維持できないので、
list tablespacesだけで↓みたいなことになっちゃいます。

shell: /opt/ibm/db2/V10.5/bin/db2 connect to ITAKDB01;/opt/ibm/db2/V10.5/bin/db2 list tablespaces show detail

しかもこのやり方だと、複数サーバに処理をかける際に
バージョンの違いでコマンドのフルパスが変わるので超超めんどくさいですね。

(2)playbookを書いて、実行

各種情報出力(list系コマンド)

まずは、db2 list db directoryを実行してみます。

【db2_directory.yml】
---  
- hosts: DB2  
  gather_facts: true   

  tasks:  
     - name: list db directory  
       action: db2 instance=db2instx command='db2 list db directory' outputfile=/WORK/output.txt

[hosts]で指定している「DB2」はインベントリ内で指定しているグループです。

【inventory/hosts】
[DB2]  
192.168.136.113 #DB2検証環境_itakura  

今回は1つだけですが、複数IPを指定すればその分実行できます。

db2コマンドは、actionモジュールで機能させます。
その後、各種設定項目を記載していきます。
指定できる項目は以下の通り。

dbname:DB名  
instance:インスタンス名(必須)  
instanceuser:インスタンスユーザー指定  
output_format:結果出力のフォーマット(textかjson、デフォルトはtext)  
outputfile:結果を出力するファイルの指定  

実行すると以下のようになります。

PLAY [DB2] *************************************************************************************************************************************************************

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

TASK [list db directory] ***********************************************************************************************************************************************
changed: [192.168.136.113]

PLAY RECAP *************************************************************************************************************************************************************
192.168.136.113            : ok=2    changed=1    unreachable=0    failed=0

listしているだけですがステータスは「change」です。
出力されたリストファイルは以下の通り。

===========================
 System Database Directory

 Number of entries in the directory = 2

Database 1 entry:

 Database alias                       = ITAKDB01
 Database name                        = ITAKDB01
 Local database directory             = /home/db2instx/ITAKDB01
 Database release level               = 10.00
 Comment                              =
 Directory entry type                 = Indirect
 Catalog database partition number    = 0
 Alternate server hostname            =
 Alternate server port number         =

Database 2 entry:

 Database alias                       = SAMPLE
 Database name                        = SAMPLE
 Local database directory             = /home/db2instx
 Database release level               = 10.00
 Comment                              =
 Directory entry type                 = Indirect
 Catalog database partition number    = 0
 Alternate server hostname            =
 Alternate server port number         =
===========================

なんの面白みも無いですね。

では次に、list tables list tablespaces list tablespace containers
をまとめて出してみます。

【db2list.yml】
---
- hosts: DB2
  gather_facts: true

  tasks:
     - name: list tables #テーブルリストを出力
       action: db2 instance=db2instx dbname=ITAKDB01 command='db2 list tables for all' outputfile=/tmp/db2_table.txt
     - name: list tablespaces #表領域リストを出力
       action: db2 instance=db2instx dbname=ITAKDB01 command='db2 list tablespaces show detail' outputfile=/tmp/db2_tablespaces.txt
     - name: list containers #コンテナの情報を出力
       action: db2 instance=db2instx dbname=ITAKDB01 command='db2 list tablespace containers for {{ item }} show detail' outputfile=/tmp/db2_containers.txt
       with_items:
         - 1
         - 2
         - 3
         - 4

やっていることはdirectroy出力の繰り返しです。
コンテナの指定はitemでやっていますが、もっといいやり方がある気がする。。。

実行結果はこんな感じです。

PLAY [DB2] *************************************************************************************************************************************************************

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

TASK [list tables] *****************************************************************************************************************************************************
changed: [192.168.136.113]

TASK [list tablespaces] ************************************************************************************************************************************************
changed: [192.168.136.113]

TASK [list containers] *************************************************************************************************************************************************
changed: [192.168.136.113] => (item=1)
changed: [192.168.136.113] => (item=2)
changed: [192.168.136.113] => (item=3)
changed: [192.168.136.113] => (item=4)

PLAY RECAP *************************************************************************************************************************************************************
192.168.136.113            : ok=4    changed=3    unreachable=0    failed=0

出力もバッチリです。

[root@itakura tmp]# ls -l db2*
-rw-r--r--. 1 root root   521  2月 25 17:35 2018 db2_containers.txt
-rw-r--r--. 1 root root 52511  2月 25 17:34 2018 db2_table.txt
-rw-r--r--. 1 root root  3780  2月 25 17:34 2018 db2_tablespaces.txt
[root@itakura tmp]#
[root@itakura tmp]# head db2*
==> db2_containers.txt <==

   Database Connection Information

 Database server        = DB2/LINUXX8664 10.5.7
 SQL authorization ID   = DB2INSTX
 Local database alias   = ITAKDB01


            Tablespace Containers for Tablespace 4


==> db2_table.txt <==

   Database Connection Information

 Database server        = DB2/LINUXX8664 10.5.7
 SQL authorization ID   = DB2INSTX
 Local database alias   = ITAKDB01


Table/View                      Schema          Type  Creation time
------------------------------- --------------- ----- --------------------------

==> db2_tablespaces.txt <==

   Database Connection Information

 Database server        = DB2/LINUXX8664 10.5.7
 SQL authorization ID   = DB2INSTX
 Local database alias   = ITAKDB01


           Tablespaces for Current Database

DB2設定情報更新(update系コマンド)

では次に、db2 update db cfgを実行してみます。
やっていることはlistでやったことの応用です。

[db2update.yml]
---
- hosts: DB2
  gather_facts: true

  tasks:

     - name: get db cfg before #変更前情報取得
       action: db2 instance=db2instx dbname=ITAKDB01 command='db2 get db cfg' outputfile=/tmp/db2_dbcfg_before

     - name: update STMTHEAP #ステートメント・ヒープ変更(3072→8192)
       action: db2 instance=db2instx dbname=ITAKDB01 command='db2 update db cfg using STMTHEAP 8192 AUTOMATIC'

     - name: update APPL_MEMORY #アプリケーション・メモリー・サイズ変更(40000→42000)
       action: db2 instance=db2instx dbname=ITAKDB01 command='db2 update db cfg using APPL_MEMORY 42000 AUTOMATIC'
       notify: #このタスクが[change]の状態になったときのみ以下のハンドルを実行
          - DB2 STOP
          - DB2 START

     - name: get db cfg after #変更後情報取得
       action: db2 instance=db2instx dbname=ITAKDB01 command='db2 get db cfg' outputfile=/tmp/db2_dbcfg_after

  handlers:
     - name: DB2 STOP #DB停止
       action: db2 instance=db2instx command="db2stop"

     - name: DB2 START #DB起動
       action: db2 instance=db2instx command="db2start"

今回は、以下2つの設定を変更しています。
・STMTHEAP(ステートメント・ヒープ)
・APPL_MEMORY(アプリケーション・メモリー・サイズ)

STMTHEAPはオンラインで構成可能ですが、
APPL_MEMORYはDB起動時に割り当てられます。

そのためhandlerを使って、APPL_MEMORYが変更されたときのみ
db2stopとdb2startを発行するようにしてみました。

本当は変更値は外出ししたかったのですが、今回は割愛。
(もしくはプロンプトで入力とか・・・普通にできそうですね)

結果は以下の通り。

[root@ansible ansible]# ansible-playbook -i inventory/hosts db2_update_cfg.yml

PLAY [DB2] *************************************************************************************************************************************************************

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

TASK [get db cfg before] ***********************************************************************************************************************************************
changed: [192.168.136.113]

TASK [update STMTHEAP] *************************************************************************************************************************************************
changed: [192.168.136.113]

TASK [update APPL_MEMORY] **********************************************************************************************************************************************
changed: [192.168.136.113]

TASK [get db cfg after] ************************************************************************************************************************************************
changed: [192.168.136.113]

RUNNING HANDLER [DB2 STOP] *********************************************************************************************************************************************
changed: [192.168.136.113]

RUNNING HANDLER [DB2 START] ********************************************************************************************************************************************
changed: [192.168.136.113]

PLAY RECAP *************************************************************************************************************************************************************
192.168.136.113            : ok=7    changed=6    unreachable=0    failed=0

ひとまずすべてchangeされた模様。
では、結果を見てみます。

[root@itakura tmp]# ls -l db2_dbcfg*
-rw-r--r--. 1 root root 9974  2月 25 19:04 2018 db2_dbcfg_after
-rw-r--r--. 1 root root 9974  2月 25 19:04 2018 db2_dbcfg_before
[root@itakura tmp]#
[root@itakura tmp]# diff db2_dbcfg_before db2_dbcfg_after
64c64
<  SQL statement heap (4KB)                     (STMTHEAP) = AUTOMATIC(3072)
---
>  SQL statement heap (4KB)                     (STMTHEAP) = AUTOMATIC(8192)
66c66
<  Application Memory Size (4KB)             (APPL_MEMORY) = AUTOMATIC(40000)
---
>  Application Memory Size (4KB)             (APPL_MEMORY) = AUTOMATIC(42000)

値はバッチリ変更されました。
ただし、db2stopとdb2startは実行されなかったようです。
db2stopとdb2startは、厳密には「db2」コマンドとは別物なので、
shellモジュールで実行するか、今回使ったdb2モジュールを改造する必要がありそうです。

このように、結果上はchangeと出ていても、実際には実行されていないというのが
Ansibleでは往々として起こりますね。

また、もう1つ問題点があります。
この状態でもう1度実行してみると・・・。

PLAY [DB2] *************************************************************************************************************************************************************

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

TASK [get db cfg before] ***********************************************************************************************************************************************
changed: [192.168.136.113]

TASK [update STMTHEAP] *************************************************************************************************************************************************
changed: [192.168.136.113]

TASK [update APPL_MEMORY] **********************************************************************************************************************************************
changed: [192.168.136.113]

TASK [get db cfg after] ************************************************************************************************************************************************
changed: [192.168.136.113]

RUNNING HANDLER [DB2 STOP] *********************************************************************************************************************************************
changed: [192.168.136.113]

RUNNING HANDLER [DB2 START] ********************************************************************************************************************************************
changed: [192.168.136.113]

PLAY RECAP *************************************************************************************************************************************************************
192.168.136.113            : ok=7    changed=6    unreachable=0    failed=0

変更がかかってないのに「change」と出てしまっています。
要するに冪等性が担保されません。なんて日だ。
これだと、handlerでの指定がまったく無意味ですね・・・悲しみ。
モジュールを弄ればできるのだろうか・・・うーん(´・ω・`)

冪等性の担保とその他のdb2関連コマンドの実行が課題です。
いつかやります。いつか。そのうち。

5.まとめ

AnsibleでDB2の操作をできるメリットは以下の通りです。
・複数サーバを同時に操作できる→省力
・誰がやっても同じことができる→簡易・安全
DB2じゃなくても、そうですけど。

ただし、以下のような課題もありました。
・冪等性が担保されない
・Ansible上の出力から結果を判断できない
下はともかく、上に関しては解消しないと実装は無理ですね。

6.あとがき

今回の環境はすべてvagrantとvirtualboxで作りましたが、
本当はDocker for Windows使いたかったのです。

_0x800F0831_という呪いとしか思えない謎エラーが出て使えなかったのですが、
この呪いが解けたらDockerとの連携も試してみたいですね。