heat を使うと、「システム設計書」をパラメーター化して、OpenStack上に何度でも再現できるようになります。さらに「手順書」をパラメータ化するAnsibleを組み合わせることで、より高度な自動化が実現できるようになります。
ansible を使えるように準備します。今回の環境では既にインストール済みです。
workon ansible
source ~/openrc
ansible --version
ansible 2.3.0.0 config file = configured module search path = Default w/o overrides python version = 2.7.5 (default, Nov 6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]
nova list
+----+------+--------+------------+-------------+----------+ | ID | Name | Status | Task State | Power State | Networks | +----+------+--------+------------+-------------+----------+ +----+------+--------+------------+-------------+----------+
heat stack-list
WARNING (shell) "heat stack-list" is deprecated, please use "openstack stack list" instead +----+------------+--------------+---------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +----+------------+--------------+---------------+--------------+ +----+------------+--------------+---------------+--------------+
Heat テンプレート、 Ansible Playbook、OpenStackの認証ファイルを取得しています。
wget https://raw.githubusercontent.com/irixjp/josug-34th-materials/master/simple-system.yaml
--2017-06-10 04:22:30-- https://raw.githubusercontent.com/irixjp/josug-34th-materials/master/simple-system.yaml Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.72.133 Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.72.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 3209 (3.1K) [text/plain] Saving to: ‘simple-system.yaml’ 100%[======================================>] 3,209 --.-K/s in 0s 2017-06-10 04:22:31 (67.1 MB/s) - ‘simple-system.yaml’ saved [3209/3209]
wget https://raw.githubusercontent.com/irixjp/josug-34th-materials/master/ansible_os_stack.yaml
--2017-06-10 04:30:20-- https://raw.githubusercontent.com/irixjp/josug-34th-materials/master/ansible_os_stack.yaml Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.72.133 Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.72.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 2213 (2.2K) [text/plain] Saving to: ‘ansible_os_stack.yaml’ 100%[======================================>] 2,213 --.-K/s in 0s 2017-06-10 04:30:21 (30.9 MB/s) - ‘ansible_os_stack.yaml’ saved [2213/2213]
wget https://raw.githubusercontent.com/irixjp/josug-34th-materials/master/clouds.yaml
--2017-06-10 04:22:34-- https://raw.githubusercontent.com/irixjp/josug-34th-materials/master/clouds.yaml Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.72.133 Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.72.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 188 [text/plain] Saving to: ‘clouds.yaml’ 100%[======================================>] 188 --.-K/s in 0s 2017-06-10 04:22:34 (31.9 MB/s) - ‘clouds.yaml’ saved [188/188]
mv -f clouds.yaml ~/.config/openstack/clouds.yaml
1つのネットワークと2台のサーバーをHeatを使って起動します。このサーバーは起動するだけで、特にアプリの設定等は行いません。
cat simple-system.yaml
heat_template_version: 2015-10-15 description: | セキュリティグループの作成 仮想ネットワークを作成 仮想ネットワークとルーターを接続 論理ポートの作成と、Floating IPの割り当て 仮想サーバーの起動 parameters: flavor: type: string default: m1.tiny image: type: string default: cirros-0.3.5 public_network: type: string default: public ext_router: type: string default: Ext-Router key: type: string default: demo-key resources: # セキュリティグループ sec_group: type: OS::Neutron::SecurityGroup properties: rules: - remote_ip_prefix: 0.0.0.0/0 protocol: tcp port_range_min: 1 port_range_max: 65535 # キーペア key_pair: type: OS::Nova::KeyPair properties: name: { get_param: key } save_private_key: true # 仮想ネットワーク heat_network: type: OS::Neutron::Net properties: name: heat-net # 仮想サブネット heat_subnet: type: OS::Neutron::Subnet properties: name: heat-subnet ip_version: 4 network_id: { get_resource: heat_network } cidr: 172.16.22.0/24 gateway_ip: 172.16.22.254 enable_dhcp: True # 仮想ルーターへの接続 router_interface: type: OS::Neutron::RouterInterface properties: router: { get_param: ext_router } subnet: { get_resource: heat_subnet } # 論理ポートの作成 neutron_port1_eth0: type: OS::Neutron::Port properties: network: { get_resource: heat_network } security_groups: - open-all neutron_port2_eth0: type: OS::Neutron::Port properties: network: { get_resource: heat_network } security_groups: - open-all # Floating IPの作成 floating_ip1: type: OS::Neutron::FloatingIP properties: floating_network: { get_param: public_network } floating_ip2: type: OS::Neutron::FloatingIP properties: floating_network: { get_param: public_network } # 論理ポートへの割り当て floating_ip_assoc1: type: OS::Neutron::FloatingIPAssociation properties: floatingip_id: { get_resource: floating_ip1 } port_id: { get_resource: neutron_port1_eth0 } floating_ip_assoc2: type: OS::Neutron::FloatingIPAssociation properties: floatingip_id: { get_resource: floating_ip2 } port_id: { get_resource: neutron_port2_eth0 } # 仮想サーバー server1: type: OS::Nova::Server properties: flavor: { get_param: flavor } image: { get_param: image } key_name: { get_resource: key_pair } networks: - port: { get_resource: neutron_port1_eth0 } server2: type: OS::Nova::Server properties: flavor: { get_param: flavor } image: { get_param: image } key_name: { get_resource: key_pair } networks: - port: { get_resource: neutron_port2_eth0 } outputs: server_ip: value: - { get_attr: [floating_ip1, floating_ip_address] } - { get_attr: [floating_ip2, floating_ip_address] } private_key: value: { get_attr: [ key_pair, private_key ] }
heat stack-create -f simple-system.yaml simple-server
WARNING (shell) "heat stack-create" is deprecated, please use "openstack stack create" instead WARNING (shell) "heat stack-list" is deprecated, please use "openstack stack list" instead +--------------------------------------+---------------+--------------------+----------------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +--------------------------------------+---------------+--------------------+----------------------+--------------+ | 896a841f-f042-441a-bc8f-0b961d99db72 | simple-server | CREATE_IN_PROGRESS | 2017-06-09T19:23:30Z | None | +--------------------------------------+---------------+--------------------+----------------------+--------------+
nova list
+--------------------------------------+------------------------------------+--------+------------+-------------+----------+ | ID | Name | Status | Task State | Power State | Networks | +--------------------------------------+------------------------------------+--------+------------+-------------+----------+ | 523ce16c-5ba8-460d-97d8-6b339376d8f3 | simple-server-server1-pgrghge7q3t4 | BUILD | networking | NOSTATE | | | 57e02d22-795e-4f7a-926f-505fd88501a7 | simple-server-server2-jyvz5datm7pd | BUILD | scheduling | NOSTATE | | +--------------------------------------+------------------------------------+--------+------------+-------------+----------+
COMPLETEになるのを待ちます。
heat stack-list
WARNING (shell) "heat stack-list" is deprecated, please use "openstack stack list" instead +--------------------------------------+---------------+-----------------+----------------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +--------------------------------------+---------------+-----------------+----------------------+--------------+ | 896a841f-f042-441a-bc8f-0b961d99db72 | simple-server | CREATE_COMPLETE | 2017-06-09T19:23:30Z | None | +--------------------------------------+---------------+-----------------+----------------------+--------------+
このHeatテンプレートは作成されたサーバーのIPアドレスと、キーペアの値を出力するように構成されています(この値を使って連携を行います)
heat output-show simple-server --all
WARNING (shell) "heat output-show" is deprecated, please use "openstack stack output show" instead "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAuSM8VzcNP3m7JlKJJL7oG1sX20maPWKjYmW8YEvZRCjJRu0X\njMOGmQUnqx2dmAsmr5SfkNjUIoNDFuSN1m4ElZ4pMoNzdbA8T0Am+0H1bqt5CBmU\nMuq2jpL+Z1Q3KEjcD5yjKXlCxVym2+pGgCMg4lNwNZm7C9pPr93uGRjnHwge/+r9\nLVGrvEnxLHErV8SCTHN7wegopMoeKG/iqtNIhZlT4V0eJma3LWNfd3OlHvCo+cBr\nr6qrifWeqmJ22Pf0+ePF37t+1Dy0BWPJ5FGW4mPWwk0dYOwnQldNNnbHkn6qW0/n\nDD5kUpl6r0zp22HEN+pwxJd0X1lLmYAzC0yI0wIDAQABAoIBAQCEj1ssI2nj8jhz\ndxNlcR/mPnI1fFzo6RCNasTXuldlu0Jq+2YqTvG37P37QqhNbmaTv3oFD4vM7mmC\nIcIBMCMuMeHTnlV0heyY3hlLPG4MgNCXYSFA19hA+7UWrTEVMh4HO6OEFaQehnme\n6v7xzrVD+HRWnK1WDkmSZfiOlcse5szU8D0ngic2ew1Xrc/Ks7SQVLUpcxiLGYWx\nqHpCetxi3H/+Y+B/SpBsu52NV7o6iZZv5EfdITWscSTEqitbgRdb+QkZsw+MMLuE\nxb5BuKGoz01SE8xQkpdSmuNHWhpFr+1/u634hpE66vyUtx6rcjF9ZdFA4nits02K\nwABalpJxAoGBAN+E8hMhA4Gi//zpTlfon6svOuyfYyUK1SWbSOyXQwWJljKNuv/B\nB6q+GKa8+esNxoyjgfspmcOx55jF6ZTwQ55PlGYCjoreHNI9PXIqlPNld0qAzppF\nlh1v8phoTViZqtL8rIMlfTXDEuSpnfyWgNf3okczop4MJkcQzF+f76q5AoGBANQK\nd+QNyDd7sESQPOwqqeCDiDv1jy1/qTYYE2+33z3q8rAkSRtwLED7CkTCwsoWpJC3\nfW+KGkuv7jMp0rJLiLpXth+LfqirIxBprkASxuSWEvsz2tE6S/DXCO3FPtZnewkQ\n2Nsb1BCTEhSvOz2VASYgG+oi+CJ9DdkUnfcNNNnrAoGBANdqcEbhRVjV9/IOA9QT\nPHaloRtVDR8xDnUc8C4cJZqbnCaCk5tStInohSeE7zOK5jS6jFW8JRYGShbhfpls\n6S4UsgeSJUBOieZQ4Pw7b6wXuN0TwInL0L30GxCpTQm7p3eJztTh94ctxvKbScia\nX/hED6ChcfLx8J4C4YSzRi75AoGAI1CXBSlydiMoiVLIgsDcSCM/9lSAgyBm6zZ5\nTjr+eE6AGuwALQyyoiPtYuRZEtVQlTWqM4B5vq9PacAGcmF4JSjkkT5nWuodzeb7\n+XnnHD0Obj4FrKEb8sGtoAQg4g6cBDeFFoD9AcGs2jsRD8GYTmQ8ofMvBndzbfss\nD/BoI/8CgYBKmSpj753mbzm9xT1mRmVwkesJI3sNeeeXjo71MDChL3St/5D8MzrC\nE5b86T66Offm1SRJtluCSkqrPQ+anJJfrufFH/dIKzky+BUBl1tNBQvpNdhj3c8J\nrNEQerYXtOwnN2vmi9skygZ+gXNnjJBAomY/2F0Y2hFwOuL58KwJWQ==\n-----END RSA PRIVATE KEY-----\n" [ "192.168.99.210", "192.168.99.209" ]
いったんStackを削除し、次にAnsibleとの連携を行います。
heat stack-delete -y simple-server
WARNING (shell) "heat stack-delete" is deprecated, please use "openstack stack delete" instead Request to delete stack simple-server has been accepted.
削除されるのを待ちます。
heat stack-list
WARNING (shell) "heat stack-list" is deprecated, please use "openstack stack list" instead +----+------------+--------------+---------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +----+------------+--------------+---------------+--------------+ +----+------------+--------------+---------------+--------------+
このHeatテンプレートをAnsibleと連携させます。ダウンロードしたPlaybookは以下のように動作します。
ただし、今回の環境では Cirros というテスト用イメージを使用している関係もあり、通常は動作するコマンド等に制限があります。そのため、簡単なコマンドを実行してその結果を取得だけにしています。
実際にAnsibleを使う場合は、単純なコマンドではなく、パッケージをインストールするモジュールや、サービスの設定を行うモジュールを利用して環境の設定を行うことになります。
cat ansible_os_stack.yaml
- name: create stack by heat simple-server.yaml hosts: localhost gather_facts: no connection: local max_fail_percentage: 0 tasks: - set_fact: ansible_python_interpreter=/home/openstack/.virtualenvs/ansible/bin/python - os_stack: name: "simple-server" cloud: handson1 state: present template: "simple-system.yaml" wait: yes register: stack_return - set_fact: os_servers: "{{ result[0].output_value }}" vars: query: "[?output_key=='server_ip']" result: "{{ stack_return.stack.outputs | json_query(query) }}" - set_fact: private_key: "{{ result[0].output_value }}" vars: query: "[?output_key=='private_key']" result: "{{ stack_return.stack.outputs | json_query(query) }}" - debug: var=os_servers - debug: var=private_key - name: make in-memory inventory from heat-outputs hosts: localhost gather_facts: no connection: local max_fail_percentage: 0 tasks: - add_host: name: "{{ item }}" groups: os_servers with_items: - "{{ os_servers }}" - debug: var: groups - name: prepare keypair file for os_servers hosts: localhost gather_facts: no connection: local max_fail_percentage: 0 tasks: - shell: | if [ -e private_key.pem ]; then rm -f private_key.pem; fi echo "{{ private_key }}" > private_key.pem chmod 600 private_key.pem - name: wait instance boot hosts: os_servers gather_facts: no max_fail_percentage: 0 remote_user: cirros vars: ansible_ssh_private_key_file: private_key.pem tasks: - wait_for: port: 22 host: "{{ ansible_host|default(ansible_ssh_host|default(inventory_hostname)) }}" state: started delay: 3 connection: local - name: configure and launch web server hosts: os_servers gather_facts: no max_fail_percentage: 0 remote_user: cirros become: yes vars: ansible_ssh_private_key_file: private_key.pem tasks: - raw: hostname register: result_hostname - raw: ls -1 --color=never / register: result_ls - debug: var=result_hostname - debug: var=result_ls
rm -f ~/.ssh/known_hosts
実際にPlaybookを実行します。
export ANSIBLE_HOST_KEY_CHECKING=False
ansible-playbook ansible_os_stack.yaml
[WARNING]: Host file not found: /etc/ansible/hosts [WARNING]: provided hosts list is empty, only localhost is available PLAY [create stack by heat simple-server.yaml] ********************************* TASK [set_fact] **************************************************************** ok: [localhost] TASK [os_stack] **************************************************************** changed: [localhost] TASK [set_fact] **************************************************************** ok: [localhost] TASK [set_fact] **************************************************************** ok: [localhost] TASK [debug] ******************************************************************* ok: [localhost] => { "changed": false, "os_servers": [ "192.168.99.202", "192.168.99.203" ] } TASK [debug] ******************************************************************* ok: [localhost] => { "changed": false, "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAsjX8mmRvPmZQYbR2KH6pdw3juaxhS/cr13zE/Lm/HC6KIc7o\nWQzSexhVvgrOHEoLUHqsfR5gPCDOD1+j+ttETc7gj1ykLxGegUV2LgrYRzTMdxNU\nLqbmUqjIMjLFqWjgEZV/N7ilZFZ4ha4oHuP+KSsv38WkA0E01V1UJZQHxjMu451b\nXZRaWwEfirEy2LjIcWQcRGsjHm0hb7mo41ajzBibgHSghgloWZRidnk8QTvSn8Bc\nt2FzaGUwEy/3PBiAVwhBLe5Qkt/96CWED7zQWhij8ArL1zLUFA1k3VxhoMXZH2oR\nlCxvALa+YpI3ZzVJkbwv3k6G2VO727nUICxCTwIDAQABAoIBAHAziVzziw6wgD9j\nzVjllnC8r/oAzCl174Z2qdesYuStcI6kpKkcZ5DU23cRen9ZJxJ+igckjtaMmPAY\n+TdBwhly/POGEmEIKNGfGC8Y6N68IVd/NetBbZXxakogqU5mF1060KQWfxtTTgJs\nihoY1ba52CR9XSwna8pCPm6CavqfxhdU7/4Qh+5hsgsKZAhn6PjKhwJ2C3/6DcZq\nB6WNI+LLoijqYGfmZvwMvyrQGFysHtCUG3ne4GzkqXO7noDDCV+NeYpg8FCF6TsG\nMu3CIdFdTeviq/qImhcDvdrGSelV+VkRI6jF4GDEaIUdMJl7N9ecAIyn+ntvzDxX\nH9+LirECgYEA14Kj51ZVoE+HERz+xXJwcg7RAzj9DOQusLD4oX3vT14YuLgpV4Zt\nCZS4hc3lXlqeSir9NTJGrQf6uvz+4lyOAYyb2dLEtBQl5VcWS5+mRQ06jZykVK+m\nOG9V8HYhom73pGsb6I/Ae2PSFgUsLmKhwh1UORvAQrKL579/o7q0xhUCgYEA07Fc\n39of2rki3xw7f1y0G/Ygl+QGxhfc2ViSNhEuo4jK4hi111wPxrmE5UKlcT9FbGI7\nDrcoiLuJhrEhUj5LlVInp19dtdlVHfVixGsklK8pKcM1rQ/exfV1uK8fnhp3Pic0\ngeAK9aTUa1rrLiowB4rqhniluUEVuzFv+obfw9MCgYAFmMj06xMSNL6HPT+IlS5C\naAwZj1NTEGH+CLqMRx796q2trmq0cTNHumsIXo6EYsoVlwQ4a3PWboyeao+cBVdS\nxvoNMCIXoyZQzHAxypWg2XUZ1GWYJW+lq46JzieVdlhMFtQEuCcBjVgZZkz9b2ZQ\nCj7ztLrLgKONgzWnZOPo+QKBgESULcE7GZyy41AiBkhRUOvKBW8PaP5dlqc2oU8w\nq8pAs3Ehji8xXM8FJL9cotoYJDZjcDxpK5F4J6ph0aiiE7xQ/BWluGcD/CYTa6a0\nCwq73/ruiYICJSo+RZ/J49VFP6H782+rlAFWH5aTwqKW+i5HW4iqpw1nv1GAR4WC\nkhUPAoGAT7RoUUG3xyUPvMdSmcIrvXH0GAGx7Qoq87PSBeAjZHBdiv5gO8ZZIxBd\nf5/fj3ly2nEg/AUzxq6RH2QzywSKL7Ci0cN9vEQ7bsNTzv2m5mKKjTuIYm7+ObqW\ngZsSVMYPiTHUpyT2tgwQse4GmhapWtkH/F+7W3ugL9mB5AH6ams=\n-----END RSA PRIVATE KEY-----\n" } PLAY [make in-memory inventory from heat-outputs] ****************************** TASK [add_host] **************************************************************** changed: [localhost] => (item=192.168.99.203) changed: [localhost] => (item=192.168.99.202) TASK [debug] ******************************************************************* ok: [localhost] => { "changed": false, "groups": { "all": [ "192.168.99.202", "192.168.99.203" ], "os_servers": [ "192.168.99.202", "192.168.99.203" ], "ungrouped": [ "localhost" ] } } PLAY [prepare keypair file for os_servers] ************************************* TASK [command] ***************************************************************** changed: [localhost] PLAY [wait instance boot] ****************************************************** TASK [wait_for] **************************************************************** ok: [192.168.99.202] ok: [192.168.99.203] PLAY [configure and launch web server] ***************************************** TASK [raw] ********************************************************************* changed: [192.168.99.203] changed: [192.168.99.202] TASK [raw] ********************************************************************* changed: [192.168.99.203] changed: [192.168.99.202] TASK [debug] ******************************************************************* ok: [192.168.99.202] => { "changed": false, "result_hostname": { "changed": true, "rc": 0, "stderr": "Warning: Permanently added '192.168.99.202' (RSA) to the list of known hosts.\r\nShared connection to 192.168.99.202 closed.\r\n", "stdout": "simple-server-server1-d4mhvpp3i66a\r\n", "stdout_lines": [ "simple-server-server1-d4mhvpp3i66a" ] } } ok: [192.168.99.203] => { "changed": false, "result_hostname": { "changed": true, "rc": 0, "stderr": "Warning: Permanently added '192.168.99.203' (RSA) to the list of known hosts.\r\nShared connection to 192.168.99.203 closed.\r\n", "stdout": "simple-server-server2-ultzgbcsfve7\r\n", "stdout_lines": [ "simple-server-server2-ultzgbcsfve7" ] } } TASK [debug] ******************************************************************* ok: [192.168.99.202] => { "changed": false, "result_ls": { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.99.202 closed.\r\n", "stdout": "bin\r\nboot\r\ndev\r\netc\r\nhome\r\ninit\r\ninitrd.img\r\nlib\r\nlinuxrc\r\nlost+found\r\nmedia\r\nmnt\r\nold-root\r\nopt\r\nproc\r\nroot\r\nrun\r\nsbin\r\nsys\r\ntmp\r\nusr\r\nvar\r\nvmlinuz\r\n", "stdout_lines": [ "bin", "boot", "dev", "etc", "home", "init", "initrd.img", "lib", "linuxrc", "lost+found", "media", "mnt", "old-root", "opt", "proc", "root", "run", "sbin", "sys", "tmp", "usr", "var", "vmlinuz" ] } } ok: [192.168.99.203] => { "changed": false, "result_ls": { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.99.203 closed.\r\n", "stdout": "bin\r\nboot\r\ndev\r\netc\r\nhome\r\ninit\r\ninitrd.img\r\nlib\r\nlinuxrc\r\nlost+found\r\nmedia\r\nmnt\r\nold-root\r\nopt\r\nproc\r\nroot\r\nrun\r\nsbin\r\nsys\r\ntmp\r\nusr\r\nvar\r\nvmlinuz\r\n", "stdout_lines": [ "bin", "boot", "dev", "etc", "home", "init", "initrd.img", "lib", "linuxrc", "lost+found", "media", "mnt", "old-root", "opt", "proc", "root", "run", "sbin", "sys", "tmp", "usr", "var", "vmlinuz" ] } } PLAY RECAP ********************************************************************* 192.168.99.202 : ok=5 changed=2 unreachable=0 failed=0 192.168.99.203 : ok=5 changed=2 unreachable=0 failed=0 localhost : ok=9 changed=3 unreachable=0 failed=0
Ansible 経由でHeatが実行され、そこで作成されたサーバー内でコマンドが実行されている事が確認できます。
heat stack-list
WARNING (shell) "heat stack-list" is deprecated, please use "openstack stack list" instead +--------------------------------------+---------------+-----------------+----------------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +--------------------------------------+---------------+-----------------+----------------------+--------------+ | d5e614a9-40f4-487a-83a6-dc71f054a202 | simple-server | CREATE_COMPLETE | 2017-06-09T19:30:41Z | None | +--------------------------------------+---------------+-----------------+----------------------+--------------+
環境を削除しておきます。
heat stack-delete -y simple-server
WARNING (shell) "heat stack-delete" is deprecated, please use "openstack stack delete" instead Request to delete stack simple-server has been accepted.
今回の例では、Ansible → Heat → Nova/Neutron という連携をしていますが、Ansible → Nova/Neutron と直接連携することも可能です。また、Ansible は今回の用にクラウドやサーバーの操作だけではなく、ネットワーク機器の操作も行うことも可能です。