はじめに
最近、JujuとMAASを使ったOpenStack環境の構築を検証しています。 こちらでその辺りの情報を公開しています。
- https://bitbucket.org/ytooyama/juju-maas/src/?at=master
- https://bitbucket.org/ytooyama/maas-testlab/src/?at=master
Jujuは様々なインフラにアプリケーションのデプロイと構成管理を行うツールで、MAASはMetal As a Serviceを提供するツールです。 これらはオープンソースで開発されており、ユーザーは公開されたドキュメントとパッケージを使って、自由にインストールして使うことができます。 サポートが必要であれば、Ubuntu Advantageを結ぶことで、 Canonicalによる技術支援を受けることができます。
JujuはAWSやAzureといったクラウド環境のほか、MAASで管理するサーバーを使うこともできるため、JujuとMAASを組み合わせることで既存のサーバーを使ってOpenStackやHadoop、最近流行りのKubernetes環境なども構築可能です。
Jujuはアプリケーションの展開を物理層にデプロイできるだけではなく、コンテナーにも展開できます。例えばApache Webサーバーを任意のノードに展開する場合は次のようにコマンドを実行することで簡単にデプロイすることができます。
ytooyama@maas:~⟫ juju deploy apache2 --to 1
これで、Jujuのマシン番号1のサーバーに対して物理環境にOSをインストールして、apache2をセットアップします。一方、コンテナーにデプロイする場合は次のようにコマンドを実行します。
ytooyama@maas:~⟫ juju deploy apache2 --to lxd:1
これで、Jujuのマシン番号1のサーバーに対してコンテナーを動かすために物理環境にOSのインストールとLXD/LXC環境を構築してコンテナーを起動、コンテナー上にapache2をセットアップします。
実際はapache2のインストール、サービスの起動、ポートの開放などの処理が行われます。コンテナーにデプロイした場合は、ブリッジ接続のコンフィグレーションも行われます。必要な場合は特定の形式(yaml形式)で記述した設定を元にカスタマイズの上デプロイすることもできます。
Jujuについては次のドキュメントをご覧ください。
Jujuには視覚的でわかりやすい、Juju GUIというものが用意されており、このDashboardを使ってデプロイしたアプリケーションの管理(アップデート、設定変更など)ができますし、スケールの拡大、縮小も可能です。同じ操作をJuju CLIで行うこともできます。
ただし、対象の台数が多くなってくると、Juju GUIやCLIツールを使って状況の確認をしたり、場合によってssh接続して様々なログを確認したりする事になります。数台規模の環境であればそれでも十分ですが、50台から100台規模になってくると結構しんどいですよね。
そこでようやく本題になるのですが、JujuでデプロイしたアプリケーションやOSを管理するためにAnsibleを使おうという話です。
Juju関連のこと
Jujuをセットアップすると、Jujuがアプリケーションを展開する前にOSをその環境にセットアップします。現状はOSとしてUbuntuが使われます。Jujuが構成管理をする際、インストールしたときに自動生成したキーペアを使って公開鍵認証によってリモートアクセスします。生成されるキーペアは、~/.local/share/juju/ssh
のパスの配下に展開されます。
AnsibleはSSHプロトコルとPythonを利用してノードを構成管理するツールです。構成管理を適用するノードにアクセスする際に公開鍵認証かパスワード認証を用いますが、Jujuがデプロイする環境ではパスワードが設定されません。従って、自動生成された鍵を使って公開鍵認証をする形になります。
Jujuがデプロイしたアプリケーションノードの一覧はJuju machenes
コマンドで一覧表示できます。実際にコマンドを実行すると次のような結果が表示されます。
ytooyama@maas:~⟫ juju machines Machine State DNS Inst id Series AZ Message 0 started 172.17.29.223 cqd8ba xenial default Deployed 0/lxd/0 started 172.17.29.225 juju-575e9e-0-lxd-0 xenial default Container started 0/lxd/1 started 172.17.29.227 juju-575e9e-0-lxd-1 xenial default Container started 0/lxd/2 started 172.17.29.193 juju-575e9e-0-lxd-2 xenial default Container started 0/lxd/3 started 172.17.29.194 juju-575e9e-0-lxd-3 xenial default Container started 0/lxd/4 started 172.17.29.195 juju-575e9e-0-lxd-4 xenial default Container started 0/lxd/6 started 172.17.29.201 juju-575e9e-0-lxd-6 xenial default Container started 1 started 172.17.29.224 4pp3th xenial default Deployed 1/lxd/0 started 172.17.29.226 juju-575e9e-1-lxd-0 xenial default Container started 1/lxd/1 started 172.17.29.228 juju-575e9e-1-lxd-1 xenial default Container started 1/lxd/2 started 172.17.29.196 juju-575e9e-1-lxd-2 xenial default Container started 1/lxd/4 started 172.17.29.197 juju-575e9e-1-lxd-4 xenial default Container started 1/lxd/5 started 172.17.29.198 juju-575e9e-1-lxd-5 xenial default Container started 1/lxd/6 started 172.17.29.199 juju-575e9e-1-lxd-6 xenial default Container started 1/lxd/7 started 172.17.29.200 juju-575e9e-1-lxd-7 xenial default Container started 2 started 172.17.29.222 kcnfd8 xenial default Deployed 2/lxd/0 started 172.17.29.229 juju-575e9e-2-lxd-0 xenial default Container started
Machineはそれぞれの環境につけられたJujuが管理するマシン番号で、番号しか表示されていないものは物理サーバー、lxdの記述のあるものはコンテナーを示しています。例えば0/lxd/1
ならば、マシン番号0のサーバーのコンテナーの1番ということがわかります。マシン番号0番には6個のコンテナーが存在することがわかります。
この出力結果の中でAnsibleでノードを管理するときに必要な情報はIPアドレスです。awkコマンドを使って抜き出してみましょう。
ytooyama@maas:~⟫ juju machines|awk '{print $3}' DNS 172.17.29.223 172.17.29.225 172.17.29.227 172.17.29.193 172.17.29.194 172.17.29.195 172.17.29.201 172.17.29.224 172.17.29.226 172.17.29.228 172.17.29.196 172.17.29.197 172.17.29.198 172.17.29.199 172.17.29.200 172.17.29.222 172.17.29.229
うまくいった...とおもいきや、1行目のDNSがちょっと邪魔です。インベントリーファイルに書き込んでから一行目を削除する方法でも良いですが、つぎのようにフィルタリングできます。
ytooyama@maas:~⟫ juju machines|awk 'NR>1 {print $3}' 172.17.29.223 172.17.29.225 172.17.29.227 172.17.29.193 172.17.29.194 172.17.29.195 172.17.29.201 172.17.29.224 172.17.29.226 172.17.29.228 172.17.29.196 172.17.29.197 172.17.29.198 172.17.29.199 172.17.29.200 172.17.29.222 172.17.29.229
いい感じです。次のように実行して、Ansibleのインベントリーファイルを作っておきます。 hostsという名前で作成したインベントリーファイルにはIPアドレスの一覧のみ追記されます。
ytooyama@maas:~⟫ juju machines|uniq|awk 'NR>1 {print $3}' > hosts
Ansibleコマンドによる管理
Ansibleがシステムにインストールされており、インベントリーファイルも作成済みであれば、Ansibleとモジュールを使ってノードの管理が可能になります。AnsibleではインベントリーファイルにAnsibleで操作する対象のノードをIPアドレスかFQDNで記述しておく必要があります。Ansibleで操作する想定でないノードを誤操作しないようにする予防策というわけですね。
ansibleコマンドでノードを操作するにはインベントリーファイルを-iオプションで指定します。そのほか、利用するモジュールやユーザー認証に関わる情報(ユーザー、パスワードやキーペアなど)も必要です。これらはインベントリーファイルに記述することができますが、とりあえず次のようにコマンドにオプション指定することで実行可能です。
ytooyama@maas:~⟫ ansible -i hosts 172.17.29.223 -m ping -u ubuntu --private-key=~/.local/share/juju/ssh/juju_id_rsa
実はこのままだとエラーで失敗します。Ansibleは対象のノードにPythonのバージョン2がインストールされている必要があるためです。 ただ、Python 2は現行は利用可能ですが、サポート期限が迫っているという現状があります。また、Linux ディストリビューションによってはPython 2はデフォルトでインストールされず、必要な場合は別途インストールする必要が出てきます。例えばUbuntu 16.04とか。
現行のAnsibleをインストールする場合はPython2が依存パッケージとして同時にインストールされますが、「Ansible 2.2でPython3サポート」が行われたため、オプションを追加することでPyton 3.5以降がインストール済みであることを条件として、その条件にマッチしたノードをサポートするようになりました。
Ansible 2.2以降でのPython3サポートについては先ほどのリンク先の情報に書かれているように大体のコードは正常に動くが動かない場合もあると書かれています。うまく動かない場合はノードにPython2をインストールして対応しましょう。
ytooyama@maas:~⟫ ansible -i hosts 172.17.29.223 -m ping -u ubuntu --private-key=~/.local/share/juju/ssh/juju_id_rsa --extra-vars=ansible_python_interpreter=/usr/bin/python3 172.17.29.223 | SUCCESS => { "changed": false, "failed": false, "ping": "pong" }
これで当初の目的は達成できるわけですが、コマンドとオプションが長すぎて入力が大変です。そこで、インベントリーファイルに必要な情報を書き込んでみます。
[remote]
のようにグループを作成して、その下にIP、FQDNを記述するとコマンドを実行するときにそのグループを指定して複数のターゲットに対してコマンドを実行できます。また、[remote:vars]
でそのグループの設定を記述します。公式のマニュアルに記述例が掲載されています。
[remote] 172.17.29.223 172.17.29.225 172.17.29.227 172.17.29.193 172.17.29.194 172.17.29.195 172.17.29.201 172.17.29.224 172.17.29.226 172.17.29.228 172.17.29.196 172.17.29.197 172.17.29.198 172.17.29.199 172.17.29.200 172.17.29.222 172.17.29.229 [remote:vars] ansible_python_interpreter=/usr/bin/python3 ansible_user=ubuntu ansible_ssh_private_key_file=/home/ytooyama/.local/share/juju/ssh/juju_id_rsa
このインベントリーファイルを使ってansibleコマンドでpingを実行してみます。全てのサーバーにpingを実行できます。
ytooyama@maas:~⟫ ansible -i hosts remote -m ping 172.17.29.193 | SUCCESS => { "changed": false, "failed": false, "ping": "pong" } ... 172.17.29.229 | SUCCESS => { "changed": false, "failed": false, "ping": "pong" }
Ansible Playbookコマンドによる管理
ansibleコマンドは一つの処理を行うには十分ですが、複数の処理を一括で実行する場合は不向きです。そんなときにはAnsible Playbookを使います。Ansible PlaybookはYAML形式で処理を記述します。例えばpingを実行したい場合は次のようにPlaybookを作成します。 Playbookの先頭にはハイフンを3つ入れてください。そのあとに処理や実行先の情報、ユーザー情報を指定したり、変数などを定義します。
--- - hosts: remote tasks: - name: try ping ping:
早速実行してみましょう。
ytooyama@maas:~⟫ ansible-playbook ping.yaml -i hosts PLAY [remote] ******************************************************************************************** TASK [Gathering Facts] *********************************************************************************** ok: [172.17.29.194] ok: [172.17.29.227] ok: [172.17.29.225] ok: [172.17.29.193] ok: [172.17.29.223] ok: [172.17.29.195] ok: [172.17.29.201] ok: [172.17.29.228] ok: [172.17.29.224] ok: [172.17.29.226] ok: [172.17.29.196] ok: [172.17.29.198] ok: [172.17.29.199] ok: [172.17.29.200] ok: [172.17.29.197] ok: [172.17.29.222] ok: [172.17.29.229] TASK [try ping] ****************************************************************************************** ok: [172.17.29.223] ok: [172.17.29.194] ok: [172.17.29.227] ok: [172.17.29.193] ok: [172.17.29.225] ok: [172.17.29.195] ok: [172.17.29.201] ok: [172.17.29.224] ok: [172.17.29.226] ok: [172.17.29.228] ok: [172.17.29.196] ok: [172.17.29.197] ok: [172.17.29.198] ok: [172.17.29.200] ok: [172.17.29.199] ok: [172.17.29.222] ok: [172.17.29.229] PLAY RECAP *********************************************************************************************** 172.17.29.193 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.194 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.195 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.196 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.197 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.198 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.199 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.200 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.201 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.222 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.223 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.224 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.225 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.226 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.227 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.228 : ok=2 changed=0 unreachable=0 failed=0 172.17.29.229 : ok=2 changed=0 unreachable=0 failed=0
ここまでできれば、あとはPlaybookの書き方次第で様々なことを実現可能です。 AnsibleのPlaybookのサンプルがドキュメントやGithub上に公開されているので、色々試してみたら楽しいかと思います。
ちなみにUbuntuで新しいバージョンのAnsibleを使うにはAnsible PPAを追加すると便利です。ページに書かれているコマンドを実行すると、Ubuntuのサポートされたバージョンで最新のAnsibleが利用できます。このPPAはRed Hat社のAnsibleチームが管理しているので安心です。Ubuntu 14.04 LTSや16.04 LTSなどで利用することを推奨します。