MacBookにpackerをインストールしました。
ダウンロード
packerのダウンロードページからOS X 64bit用のバイナリをダウンロードします。
インストール
zip形式なので/usr/local/binに展開したらインストール完了です。
$ packer --version 0.10.0 $
MacBookにpackerをインストールしました。
packerのダウンロードページからOS X 64bit用のバイナリをダウンロードします。
zip形式なので/usr/local/binに展開したらインストール完了です。
$ packer --version 0.10.0 $
ansibleのdocker connection plugを使ってみました。
ansibleとdockerのバージョンは、下記の通りです。
$ ansible --version ansible 2.0.1.0 config file = /home/vagrant/.ansible.cfg configured module search path = Default w/o overrides (0)21:07:12 vagrant@vivid64$ (0)21:07:12 vagrant@vivid64$ docker version Client: Version: 1.9.1 API version: 1.21 Go version: go1.4.2 Git commit: a34a1d5 Built: Fri Nov 20 13:16:54 UTC 2015 OS/Arch: linux/amd64 Server: Version: 1.9.1 API version: 1.21 Go version: go1.4.2 Git commit: a34a1d5 Built: Fri Nov 20 13:16:54 UTC 2015 OS/Arch: linux/amd64 $
最初は、手動でコンテナを起動します。
コンテナ作成時に--name
オプションで名前を指定します。ansibleは、この名前でコンテナを識別します。
$ docker run --name target -td ubuntu:14.04 bash 909d3137c6d28f02ed261bba2974dc2ed1d50a7313a2843a6da2cec09ab8f61b (0)20:54:49 vagrant@vivid64$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 909d3137c6d2 ubuntu:14.04 "bash" 3 seconds ago Up 3 seconds target $
docker connection pluginは、python必須なのでコンテナにpythonをインストールします。
$ docker exec target "apt-get install -y python"
ansibleコマンドで疎通を確認します。
-c docker
を指定することで通常ssh
で接続するところをdocker exec
でコマンドを実行します。
$ ansible -i "target," target -c docker -a hostname target | SUCCESS | rc=0 >> 909d3137c6d2 $
下記のようなplaybookを準備します。
$ cat sample.yml --- - name: start up docker container hosts: localhost tasks: - add_host: name=target - name: configure container hosts: target connection: docker tasks: - command: id register: id_result - debug: var=id_result.stdout when: id_result | success $
$ ansible-playbook -i "localhost," sample.yml PLAY [start up docker container] *********************************************** TASK [setup] ******************************************************************* ok: [localhost] TASK [add_host] **************************************************************** changed: [localhost] PLAY [configure container] ***************************************************** TASK [setup] ******************************************************************* ok: [target] TASK [command] ***************************************************************** changed: [target] TASK [debug] ******************************************************************* ok: [target] => { "id_result.stdout": "uid=0(root) gid=0(root) groups=0(root)" } PLAY RECAP ********************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 target : ok=3 changed=1 unreachable=0 failed=0 $
設定する対象がデータベースやアプリケーションサーバの場合、「一つのインスタンスに複数のアプリケーションをデプロイ」したり、「一つのアプリケーションに複数のデータソースを追加」といった一対多の設定はよくあります。
ansibleで設定を行う場合に、
となるようなパターンを検討してみました。
より良い方法があるかもしれませんが、ひとまずこれで動いています。
設定は、``./host_vars/[対象サーバ名]を起点にします。こうすることでansibleがインベントリから対象サーバを特定した際に自動的に設定が読み込まれます。
$ cat host_vars/appserv1 serverName: app-server1 tomcat: - appName: inst1 db: - dba - appName: inst2 db: - db1 - db2 $
この例では、tomcat
という変数にinst1
とinst2
という2つのアプリケーションがあり、inst1
は、dba
というデータベース接続を、inst2
は、db1
とdb2
の2つのデータベース接続を設定します。
各オブジェクトのパラメータ定義は、./vars
にオブジェクト毎に設定します。
アプリケーションのパラメータは、./vars/inst1.yml
と./vars/inst2.yml
に設定します。
各アプリケーションに関連するデータベースの設定は、./vars/[アプリ名][db名].yml
に設定します。
今回の例ですと、./vars/inst1dba.yml
や./vars/inst2db1.yml
や./vars/inst2db2.yml
に設定することになります。
$ ls vars/inst[12]*.yml | cat vars/inst1.yml vars/inst1dba.yml vars/inst2.yml vars/inst2db1.yml vars/inst2db2.yml $
アプリケーションの設定は、下記のようにappName
を定義します。
$ for i in `ls ./vars/inst[12].yml`; do echo $i; cat $i; done ./vars/inst1.yml appName: inst1 ./vars/inst2.yml appName: inst2 $
データベースの設定は、下記とします。
$ for i in `ls ./vars/inst[12]db*.yml`; do echo $i; cat $i; done ./vars/inst1dba.yml schemaName: inst1dba ./vars/inst2db1.yml schemaName: inst2db1 ./vars/inst2db2.yml schemaName: inst2db2 $
パラメータを読み込んで設定するroleを作成するために下記のようなファイル一式を作ります。host_vars
とvars
は、上記で設定したファイルです。
$ tree . ├── host_vars │ ├── appserv1 │ └── appserv2 ├── hosts ├── roles │ └── app │ └── tasks │ ├── db.yml │ ├── main.yml │ └── tomcat.yml ├── site.yml └── vars ├── inst1.yml ├── inst1dba.yml ├── inst2.yml ├── inst2db1.yml └── inst2db2.yml $
roles/app/tasks/main.yml
でアプリケーションの設定と、それに紐づくデータベースの設定をするためのタスクを定義します。
$ cat roles/app/tasks/main.yml --- - include: tomcat.yml with_items: "{{tomcat}}" - include: db.yml with_subelements: - tomcat - db $
一つ目のタスクは、アプリケーション設定をするtomcat.yml
というファイルをインクルードします。
./host_avrs/[サーバ名]
で定義したtomcat
という変数に定義したアプリケーションの回数繰り返します。
二つ目のタスクは、アプリケーションに紐付いたデータベースの設定をするdb.yml
というファイルをインクルードします。
本当は、インスタンスの設定に入れ子したかったのですが、with_items
で使われるitem
という変数が入れ子の中では設定できず諦めました。(誰かうまい方法教えて)
いろいろ調べた結果、with_subelements
でtomcat
変数の配列の一つを取り出し、appName
+db
の組み合わせをdb.yml
に渡しています。
サンプルなので設定されたパラメータを読み込み表示するだけの処理です。
アプリケーションの設定は、以下の通りです。
$ cat roles/app/tasks/tomcat.yml --- - include_vars: "{{item.appName}}.yml" - debug: msg="Tomcat application Name={{appName}}" $
DBの設定は、以下の通りです。
$ cat roles/app/tasks/db.yml --- - include_vars: "{{item.0.appName}}{{item.1}}.yml" - debug: msg="{{item.0.appName}} application dbName={{schemaName}}" $
どちらも、最初にinclude_vars
で./vars
ディレクトリ下の所定の定義ファイルのパラメータを読み込み、表示します。
インベントリにとりあえず1台サーバを追加します。
appserv2
も追加して、./host_vars/appserv1
を./host_vars/appserv2
とにコピーすると、全くおなじ設定のサーバを作ることができます。
もちろん、./vars/inst1*.yml
一式をコピーして個別の設定にすることも可能です。
[appservers] appserv1 #appserv2 $
実行すると、こんな感じです。
アプリケーションの名前やDB接続先など、設定が必要な固有の値をtemplate
で埋め込んだりtask
の中で参照するといった使い方になります。
$ ansible-playbook -i hosts site.yml PLAY *************************************************************************** TASK [setup] ******************************************************************* ok: [appserv1] TASK [app : include] *********************************************************** included: /home/vagrant/git/sample/roles/app/tasks/tomcat.yml for appserv1 included: /home/vagrant/git/sample/roles/app/tasks/tomcat.yml for appserv1 TASK [app : include_vars] ****************************************************** ok: [appserv1] TASK [app : debug] ************************************************************* ok: [appserv1] => { "msg": "Tomcat application Name=inst1" } TASK [app : include_vars] ****************************************************** ok: [appserv1] TASK [app : debug] ************************************************************* ok: [appserv1] => { "msg": "Tomcat application Name=inst2" } TASK [app : include] *********************************************************** included: /home/vagrant/git/sample/roles/app/tasks/db.yml for appserv1 included: /home/vagrant/git/sample/roles/app/tasks/db.yml for appserv1 included: /home/vagrant/git/sample/roles/app/tasks/db.yml for appserv1 TASK [app : include_vars] ****************************************************** ok: [appserv1] TASK [app : debug] ************************************************************* ok: [appserv1] => { "msg": "inst1 application dbName=inst1dba" } TASK [app : include_vars] ****************************************************** ok: [appserv1] TASK [app : debug] ************************************************************* ok: [appserv1] => { "msg": "inst2 application dbName=inst2db1" } TASK [app : include_vars] ****************************************************** ok: [appserv1] TASK [app : debug] ************************************************************* ok: [appserv1] => { "msg": "inst2 application dbName=inst2db2" } PLAY RECAP ********************************************************************* appserv1 : ok=16 changed=0 unreachable=0 failed=0 $
Developing Modulesを参考にansibleのcoreモジュールを弄ってみました。
オフシャルドキュメントのチュートリアルにテスト方法とかんたんなモジュールの作り方が書かれています。
下記でテスト環境を準備します。
$ git clone git://github.com/ansible/ansible.git --recursive $ source ansible/hacking/env-setup $ chmod +x ansible/hacking/test-module
pingモジュールは、lib/ansible/modules/core/system/ping.py
にあります。
普通に使うとこんな感じ
$ ansible -m ping localhost localhost | SUCCESS => { "changed": false, "ping": "pong" } $
戻り値のpong
をpong!!
に修正します。
gitのsubmoduleになっているようです。
$ git diff | cat diff --git a/lib/ansible/modules/core b/lib/ansible/modules/core --- a/lib/ansible/modules/core +++ b/lib/ansible/modules/core @@ -1 +1 @@ -Subproject commit 45367c3d090ccf4d649b103b50b6eec939b6ee14 +Subproject commit 45367c3d090ccf4d649b103b50b6eec939b6ee14-dirty $ cd lib/ansible/modules/core/system $ git --no-pager diff diff --git a/system/ping.py b/system/ping.py index ed93f7d..9f8ddf6 100644 --- a/system/ping.py +++ b/system/ping.py @@ -49,7 +49,7 @@ def main(): ), supports_check_mode = True ) - result = dict(ping='pong') + result = dict(ping='pong!!') if module.params['data']: if module.params['data'] == 'crash': raise exceptions.Exception("boom") $
修正後に実行するとこんな感じ
$ ansible -m ping localhost localhost | SUCCESS => { "changed": false, "ping": "pong!!" } $
command/shellモジュールは、lib/ansible/modules/core/commands
にあります。
実行するとこんな感じ
$ ansible localhost -m shell -a "/bin/ls" localhost | SUCCESS | rc=0 >> __init__.py command.py raw.py script.py shell.py $ ansible localhost -m command -a "/bin/ls" localhost | SUCCESS | rc=0 >> __init__.py command.py raw.py script.py shell.py $
test-module
で実行するには、こんな感じで実行します。
$ $ANSIBLE_HOME/hacking/test-module -m command.py -a "/bin/ls" * including generated source, if any, saving to: /home/vagrant/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"changed": true, "end": "2016-03-03 18:31:12.148437", "stdout": "__init__.py\ncommand.py\nraw.py\nscript.py\nshell.py", "cmd": ["/bin/ls"], "rc": 0, "start": "2016-03-03 18:31:12.145253", "stderr": "", "delta": "0:00:00.003184", "invocation": {"module_args": {"warn": true, "executable": null, "chdir": null, "_raw_params": "/bin/ls", "removes": null, "creates": null, "_uses_shell": false}}, "warnings": []} *********************************** PARSED OUTPUT { "changed": true, "cmd": [ "/bin/ls" ], "delta": "0:00:00.003184", "end": "2016-03-03 18:31:12.148437", "invocation": { "module_args": { "_raw_params": "/bin/ls", "_uses_shell": false, "chdir": null, "creates": null, "executable": null, "removes": null, "warn": true } }, "rc": 0, "start": "2016-03-03 18:31:12.145253", "stderr": "", "stdout": "__init__.py\ncommand.py\nraw.py\nscript.py\nshell.py", "warnings": [] } $
デバッガを使うには、下記のようにします。
デバッガは、Python付属のpdbで開発用にクローンしてtest-module
で生成・実行されたソースをデバッグすることができます。
$ pdb $ANSIBLE_HOME/hacking/test-module -m command.py -a "/bin/ls" --debugger /usr/bin/pdb > /home/vagrant/git/module-dev/ansible/hacking/test-module(32)<module>() -> import sys (Pdb) q $ $ANSIBLE_HOME/hacking/test-module -m command.py -a "/bin/ls" --debugger /usr/bin/pdb * including generated source, if any, saving to: /home/vagrant/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! > /home/vagrant/.ansible_module_generated(22)<module>() -> import copy (Pdb) list 17 # GNU General Public License for more details. 18 # 19 # You should have received a copy of the GNU General Public License 20 # along with Ansible. If not, see <http://www.gnu.org/licenses/>. 21 22 -> import copy 23 import sys 24 import datetime 25 import glob 26 import traceback 27 import re (Pdb)
2/16にzabbix3.0がリリースされました。 Ubuntu 14.04 LTSにインストールしました。
オフシャルドキュメントの手順でaptパッケージをインストール後にブラウザでアクセスします。
設定変更は、下記のphpのtimezoneの一箇所だけです。
root@trusty64:~# diff -u zabbix.conf /etc/apache2/conf-enabled/zabbix.conf --- zabbix.conf 2016-02-28 13:34:28.192211335 +0900 +++ /etc/apache2/conf-enabled/zabbix.conf 2016-02-28 13:39:27.037559358 +0900 @@ -16,7 +16,7 @@ php_value upload_max_filesize 2M php_value max_input_time 300 php_value always_populate_raw_post_data -1 - # php_value date.timezone Europe/Riga + php_value date.timezone Asia/Tokyo </IfModule> </Directory> root@trusty64:~#
ブラウザでアクセスするとインストール画面が表示されます。 画面に従って進めるとインストール完了です。
初期画面は、英語設定です。
右上の人の形のアイコンを選択して、LanguageでJapaneseを選択します。日本語は、デフォルトでインストールされているので選択するだけです。
ダッシュボードは、こんな感じになります。 だいぶイメージが変わって今風になってます。
グラフ凡例の日本語は、相変わらず文字化けします。
IPAフォントをインストールします。
root@trusty64:~# sudo apt-get install fonts-ipaexfont-gothic
フォントを使えるようにします。
root@trusty64:~# update-alternatives --install "/usr/share/zabbix/fonts/graphfont.ttf" "zabbix-frontend-font" "/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf" 100 root@trusty64:~# update-alternatives --config zabbix-frontend-font alternative zabbix-frontend-font (/usr/share/zabbix/fonts/graphfont.ttf を提供) には 2 個の選択肢があります。 選択肢 パス 優先度 状態 ------------------------------------------------------------ * 0 /usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf 100 自動モード 1 /usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf 100 手動モード 2 /usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf 10 手動モード 現在の選択 [*] を保持するには Enter、さもなければ選択肢の番号のキーを押してください: root@trusty64:~#
これで凡例も日本語になりました。
ansibleモジュールは、pythonで書くと引数と戻り値に便利なヘルパがあり便利です。
AnsibleModule
のargument_spec
で引数を定義します。引数が必須かオプションかを定義できるようです。
戻り値は、exit_json
で返します。最後の2行は、python初心者なのでおまじないということで...
$ cat library/mymodule.py #! /usr/bin/python def main(): module = AnsibleModule( argument_spec = dict( src = dict(required=True), dest = dict(required=False), ), ) src = module.params['src'] dest = module.params['dest'] res_args = dict( src = src, dest = dest, changed = True ) module.exit_json(**res_args) # import module snippets from ansible.module_utils.basic import * main() $
テスト実行します。前に書いたサンプルより一貫性があって分かりやすい実装です。
$ $ANSIBLE_HOME/hacking/test-module -m library/mymodule.py -a "src=bbb" * including generated source, if any, saving to: /home/vagrant/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"dest": null, "src": "bbb", "changed": true, "invocation": {"module_args": {"dest": null, "src": "bbb"}}} *********************************** PARSED OUTPUT { "changed": true, "dest": null, "invocation": { "module_args": { "dest": null, "src": "bbb" } }, "src": "bbb" } $
必須オプションが指定されないと、failed
とエラーメッセージが戻り値に設定されます。これは便利です。
$ $ANSIBLE_HOME/hacking/test-module -m library/mymodule.py -a "dest=bbb" * including generated source, if any, saving to: /home/vagrant/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"msg": "missing required arguments: src", "failed": true, "invocation": {"module_args": {"dest": "bbb"}}} *********************************** PARSED OUTPUT { "failed": true, "invocation": { "module_args": { "dest": "bbb" } }, "msg": "missing required arguments: src" } $
オフシャルドキュメントを参考にテスト方法と引数の取り方を試しました。
オフシャルドキュメントのチュートリアルにテスト方法とかんたんなモジュールの作り方が書かれています。
下記でテスト環境を準備します。
$ git clone git://github.com/ansible/ansible.git --recursive $ source ansible/hacking/env-setup $ chmod +x ansible/hacking/test-module
これでansibleの最新ソース一式をクローンしてローカルのソースでモジュールの開発とテストをする環境が準備できます。
結果を返すだけのシンプルなモジュールをpythonで書きます。
$ cat mymodule.py #! /usr/bin/python import json print json.dumps({ "rc": 0, "changed": True, }) $
テストします。
$ $ANSIBLE_HOME/hacking/test-module -m mymodule.py * including generated source, if any, saving to: /home/vagrant/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"changed": true, "rc": 0} *********************************** PARSED OUTPUT { "changed": true, "rc": 0 } $
ansibleの引数がどのように処理されている確認してみます。
先ほどのソースに、チュートリアルにある引数の処理を追加・実行して確認してみます。
cat mymodule.py #! /usr/bin/python import json import sys args_file = sys.argv[1] print json.dumps({ "rc": 0, "changed": True, "args": args_file }) $ $ANSIBLE_HOME/hacking/test-module -m mymodule.py -a "arg1=aaa arg2=bbb" * including generated source, if any, saving to: /home/vagrant/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"changed": true, "args": "/home/vagrant/.ansible_test_module_arguments", "rc": 0} *********************************** PARSED OUTPUT { "args": "/home/vagrant/.ansible_test_module_arguments", "changed": true, "rc": 0 } $
引数は、-a
オプションで"key1=値 key2=値..."で指定します。
sys.argv[1]
に引数が書かれたファイルパスがあり、このファイルから引数を読むようです。
$ cat /home/vagrant/.ansible_test_module_arguments arg1=aaa arg2=bbb$
ファイルに書かれた引数は、文字列処理として取り込みます。
shlexというのをインポートして引数ごとに分解して=
でsplit
してキーと値を取り出す。
引数を処理するヘルパーのようなものがあったほうが便利そうですが、処理としてはわかりやすい。
$ cat mymodule.py #! /usr/bin/python import json import sys import shlex args_file = sys.argv[1] args_data = file(args_file).read() args = shlex.split(args_data) for arg in args: if "=" in arg: (key, value) = arg.split("=") print json.dumps({ "rc": 0, "changed": True, "args": args_file, "key": key, "value": value, }) $
実行するとこんな感じ(エラー処理なし、引数複数あっても最後のものしか取りません。サンプルなので)
$ $ANSIBLE_HOME/hacking/test-module -m mymodule.py -a "arg1=aaa arg2=bbb" * including generated source, if any, saving to: /home/vagrant/.ansible_module_generated * this may offset any line numbers in tracebacks/debuggers! *********************************** RAW OUTPUT {"value": "bbb", "changed": true, "key": "arg2", "args": "/home/vagrant/.ansible_test_module_arguments", "rc": 0} *********************************** PARSED OUTPUT { "args": "/home/vagrant/.ansible_test_module_arguments", "changed": true, "key": "arg2", "rc": 0, "value": "bbb" } $
モジュールからansible_factsに値を渡すことができると書いてあるので試してみます。
チュートリアルによると、モジュールの戻り値のjsonにansible_facts
を返すと、そのあとのモジュールからansible_factsとして参照できるようになります。
こんな感じでtestvalue
という変数を定義(=json戻り値に含める)します。
$ cat library/mymodule.py #! /usr/bin/python import json print json.dumps({ "rc": 0, "changed": True, "ansible_facts" : { "testvalue" : "mymodule.py value", } }) $ python library/mymodule.py {"changed": true, "ansible_facts": {"testvalue": "mymodule.py value"}, "rc": 0} $
モジュールを呼び出した後続の処理では、"{{testvalue}}``で設定した変数を参照することができます。
$ cat site.yml --- - hosts: localhost tasks: - mymodule: - debug: msg="{{ testvalue }}" $ ansible-playbook -i "localhost," site.yml PLAY [localhost] *************************************************************** TASK [setup] ******************************************************************* ok: [localhost] TASK [mymodule] **************************************************************** changed: [localhost] TASK [debug] ******************************************************************* ok: [localhost] => { "msg": "mymodule.py value" } PLAY RECAP ********************************************************************* localhost : ok=3 changed=1 unreachable=0 failed=0 $