仮想化通信

日本仮想化技術株式会社の公式エンジニアブログ

Ubuntu JujuでデプロイしたマシンをAnsibleで管理するには

はじめに

最近、JujuとMAASを使ったOpenStack環境の構築を検証しています。 こちらでその辺りの情報を公開しています。

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については次のドキュメントをご覧ください。

jujucharms.com

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などで利用することを推奨します。