「Ansible Container」でDockerコンテナの構成管理を行う

現在、ansible-containerプロジェクトは開発終了しています。詳細は同プロジェクトのGitHubページをご覧ください。

「Ansible Container」は、構成管理ツールであるAnibleを使ってDockerコンテナのビルドや管理を行えるツールだ。記事後編となる今回は、Ansible Containerを使ってコンテナの設定を自動化する方法や、Kubernetesとの連携について紹介する。

Ansible Containerで「Role」をコンテナに適用する

記事前編では、Ansible Containerのインストールや設定ファイル、基本的なコンテナ作成と起動の流れを紹介した。続いて後編となる本記事では、Ansible Containerを使ってコンテナへのソフトウェアインストールや設定を自動化する方法を紹介していく。

「Role」とは

Ansible Containerでは、コンテナに対し適用する「Role」を指定することで、そのコンテナの構成管理を行うようになっている。RoleというのはAnsibleで使われている構成管理単位であり、その名前のとおり「役割」ベースでインストールするソフトウェアやファイル、各種設定作業を定義するようになっている。

Roleについてより詳しくはAnsibleの公式ドキュメントを参照して欲しいが、おおまかに言えばサーバー単位ではなく、目的単位で設定ファイルを用意するととらえておけば良いだろう。

さて、Ansible Container向けのRoleを用意するには複数の方法があるが、もっとも簡単なのはAnsible Containerの設定ファイルを用意したディレクトリ(ansible-container initコマンドを実行したディレクトリ)内に「roles」というディレクトリを作成し、そこに設定ファイルを作成するという方法だ。このrolesディレクトリ内では、図1のようなディレクトリ配置ルールに従って必要なファイルを配置していく。

図1 Role設定ファイルの配置ルール

ここに配置するファイルは表1のとおりだ。

表1 Role設定に必要なファイル/ディレクトリ
ファイル名 説明
tasks/main.yml このRoleを指定した対象に適用するタスクを記述する
handlers/main.yml 特定のタスク実行後に実行されるHandlerを記述する
defaults/main.yml タスクやHandlerで使われる変数のデフォルト値を指定する
vars/main.yml タスクやHandlerで使われる変数を記述する
files/ Roleを指定した対象にコピーするファイルなどをここに格納しておく
templates/ 設定ファイル作成時に使用されるテンプレートファイルをここに格納しておく
meta/main.yml このRoleに関するメタデータを記述する

なお、このすべてが必要なわけではなく、設定が不要なものについてはファイル/ディレクトリを作成する必要はない。

Roleを使った設定例

それでは、Ansible ContainerとRoleを使ったコンテナ設定の例として、Webサーバー用のコンテナに指定したファイルをコピーする、という作業を紹介しよう。

前編で紹介したとおり、まず適当なディレクトリ(今回は「httpd-test」とした)内でansible-container initコマンドを実行して設定ファイルのひな形を作成する。

$ mkdir httpd-test
$ cd httpd-test
$ ansible-container init

コンテナ作成に必要なcontainer.ymlだが、今回は下記のような内容とした。前編で紹介したものとほぼ同じだが、それに加えて適用するRoleを指定する「roles」パラメータを追加している。rolesパラメータには「test-webserver」を指定しているが、これがこのコンテナに適用されるRoleとなる。今回はRoleを1つしか指定していないが、複数を指定することも可能だ。

version: "2"
settings:
  conductor_base: debian:jessie-backports

services:
  test-httpd:
    from: "httpd:2.4"
    ports:
      - "8888:80"
    environment:
      PATH: "/usr/local/apache2/bin:/bin:/usr/bin:/usr/local/bin"
    command: ["httpd-foreground"]
    roles:
      - test-webserver
registries: {}

続いて、rolesディレクトリおよびroles/test-webserverディレクトリを作成し、そこに必要なディレクトリとファイルを作成していく。

$ mkdir roles
$ mkdir roles/test-webserver
$ cd roles/test-webserver/
$ mkdir tasks
$ mkdir files
$ vi tasks/main.yml

コンテナの設定を行うタスクを定義するroles/test-webserver/tasks/main.ymlは、以下のようにした。

- name: copy index.html
  copy:
    src: index.html
    dest: /usr/local/apache2/htdocs/
    mode: 0644

ここでは「copy」コマンドを実行し、「index.html」ファイルをコンテナ内の/usr/local/apache2/htdocsディレクトリにコピーする、というタスクを定義している。また、ここでコピーするindex.htmlファイルはfilesディレクトリ内に配置しておく。

これらの作業完了後、rolesディレクトリ内には次のようなファイル/ディレクトリが作成されていることになる。

$ find roles
roles
roles/test-webserver
roles/test-webserver/tasks
roles/test-webserver/tasks/main.yml
roles/test-webserver/files
roles/test-webserver/files/index.html

これら設定ファイルの準備が完了したら、「ansible-container build」コマンドを実行してコンテナイメージを作成する。

$ ansible-container build
Building Docker Engine context...	
Starting Docker build of Ansible Container Conductor image (please be patient)...	
Parsing conductor CLI args.
Docker™ daemon integration engine loaded. Build starting.	project=httpd-test
Building service...	project=httpd-test service=test-httpd
PLAY [test-httpd] *******************************************************************
TASK [Gathering Facts] *********************************************************
ok: [test-httpd]
TASK [test-webserver : copy index.html] ****************************************
changed: [test-httpd]
PLAY RECAP *********************************************************************
test-httpd                      : ok=2    changed=1    unreachable=0    failed=0
Applied role to service	role=test-webserver service=test-httpd
Committed layer as image	image=sha256:54ed55e81d9dc643154e449340efb6b4cab9d476c290a53c95aa96f1379d9365e service=test-httpd
Build complete.	service=test-httpd
All images successfully built.
Conductor terminated. Cleaning up.	command_rc=0 conductor_id=699adc48c8afb85922645c63b9afb82100c49a32b1376a676df94cb1eda7a410 save_container=False

この出力結果から、「test-webserver」Roleが「httpd」コンテナに適用され、ファイルのコピーが行われていることが分かる。あとは「ansible-container run」コマンドを実行すれば、設定が反映されたコンテナが起動する。

Kubernetesにデプロイする

Ansible Containerではローカルで稼働しているDockerを使ってコンテナを立ち上げるだけでなく、KubernetesやOpenShiftなどで構築されたコンテナクラスタ上にコンテナを立ち上げる機能も備えている。続いては、この機能を使ってKubernetesクラスタ上にコンテナを立ち上げる例を紹介しよう。

なお、Kubernetesについてやその環境構築については今回は割愛する。こちらについて詳しくはkubernetesによるDockerコンテナ管理入門記事などを参照してほしい。

Kubernetesを利用するための設定

以下では、先に作成したWebサーバーコンテナを例にKubernetes上へのデプロイ手順を紹介しよう。なお、Kubernetesへのデプロイを行うには、Kubernetes用のパッケージをインストールしておく必要がある。もしインストールしていなかった場合、次のようにしてインストールしておこう。

# pip install ansible-container[docker,k8s]

また、Kubernetes上にコンテナをデプロイする際には、作成したイメージを一度コンテナリポジトリにアップロードする必要がある。これにはDockerの公式リポジトリであるDockerHubを使っても良いし、ローカルリポジトリを用意しても良い。ローカルリポジトリを用意し利用できるようにする手順についてもkubernetesによるDockerコンテナ管理入門記事で紹介している。

ローカルリポジトリを利用する場合に注意が必要なのが、ローカルリポジトリに対しconductorコンテナ内からアクセスできるようになっている必要がある点だ。たとえばhostsファイルを使ってローカルリポジトリのホスト名とIPアドレスの対応付けをしている場合、この情報はそのままではコンテナ内に引き継がれないためデプロイに失敗する。そのため、IPアドレスでローカルリポジトリを指定するか、もしくはDNSサーバーを用意してホスト名からIPアドレスを参照できるよう設定しておく必要がある。また、コンテナ内からローカルリポジトリにアクセスできるようファイアウォールの設定も確認しておこう。

利用するリポジトリが決まったら、まずその情報をcontainer.ymlファイルの「registries:」以下に適当な名前で追加する。たとえば「http://192.168.1.10:5000」というリポジトリを「local-test」という名前で登録する場合、以下のようになる。

version: "2"
settings:
  conductor_base: debian:jessie-backports

services:
  test-httpd:
    from: "httpd:2.4"
    ports:
      - "8888:80"
    environment:
      PATH: "/usr/local/apache2/bin:/bin:/usr/bin:/usr/local/bin"
    command: ["httpd-foreground"]
    roles:
      - test-webserver

registries:
  local-test:
    url: "http://192.168.1.10:5000"

続いて、docker loginコマンドでこのリポジトリへのログインを行っておく。

$ docker login http://192.168.1.10:5000
Username: foo  ←ユーザー名を入力
Password:   ←パスワードを入力
Login Succeeded

これによって、Ansible Containerが参照する ~/.docker/config.jsonという設定ファイルが作成される。なお、独自に立ち上げたリポジトリでユーザー名やパスワードを設定していない場合、どのようなユーザー名/パスワードでも認証は成功するので、適当なユーザー名/パスワードを入力すれば良い。

また、Kubernetesサーバーやそのログインなどに関する情報については、~/.kube/configというファイルが参照される。このファイルはkubectl configコマンドで作成できる。こちらについてもkubernetesによるDockerコンテナ管理入門記事で紹介しているので、詳細はそちらを参照して欲しい。

これらの設定が完了したら、続いてビルドしたイメージをリポジトリに登録できることを確認しておこう。ただし後述する「ansible-container deploy」コマンドでは自動的にイメージのプッシュも実行するので、もしリポジトリが利用できることが確認できているのであればこの作業は実行する必要はない。

まず「ansible-container build」コマンドでコンテナをビルドし、「ansible-container push」コマンドでリポジトリへのプッシュを実行する。このとき、「--push-to」オプション引数でcontainer.yml内に記述したリポジトリ名を指定する。

$ ansible-container push --push-to local-test
Parsing conductor CLI args.
Engine integration loaded. Preparing push.	engine=Docker™ daemon
Tagging 192.168.1.10:5000/foo/httpd-test-httpd
Pushing 192.168.1.10:5000/foo/httpd-test-httpd:20170614142249...
The push refers to a repository [192.168.1.10:5000/foo/httpd-test-httpd]
Preparing
Waiting
Pushing



20170614142249: digest: sha256:e088007a8cbdeaf4597b1b664b4455dde302b24c2ba8d27024d56f6a9a9bf558 size: 1987
Conductor terminated. Cleaning up.	command_rc=0 conductor_id=e69985e3a33f1135d448bdfc772ffc1c89e73ffc31b8ea723f9c049a7d558d3f save_container=False

設定に問題がなければ、これでコンテナイメージがリポジトリに登録される。ローカルリポジトリを利用している場合、「curl <リポジトリURL>/v2/_catalog」コマンドでリポジトリが正しく登録できているかが確認できる。

$ curl http://192.168.1.10:5000/v2/_catalog          
{"repositories":["foo/httpd-test-httpd"]}

リポジトリ設定が問題ないことが確認できたら、次は「ansible-container deploy」コマンドを実行して設定ファイルの準備を行う。

$ ansible-container --engine k8s deploy --push-to local-test
Parsing conductor CLI args.
Engine integration loaded. Preparing push.	engine=K8s
Tagging 192.168.1.10:5000/foo/httpd-test-httpd
Pushing 192.168.1.10:5000/foo/httpd-test-httpd:20170614142249...
The push refers to a repository [192.168.1.10:5000/foo/httpd-test-httpd]
Preparing
Waiting
Layer already exists
20170614142249: digest: sha256:e088007a8cbdeaf4597b1b664b4455dde302b24c2ba8d27024d56f6a9a9bf558 size: 1987
Conductor terminated. Cleaning up.	command_rc=0 conductor_id=4c949053776d6b1ea07f9340f9d11c783679dd34e152fe2542af9ae2735b2771 save_container=False
Parsing conductor CLI args.
Engine integration loaded. Preparing deploy.	engine=K8s
Verifying image for httpd
Conductor terminated. Cleaning up.	command_rc=0 conductor_id=8a4abe677b10fa1dc591b50f01388e334efd45977975ec3164af8b1335391c38 save_container=False

「ansible-container deploy」コマンドではイメージや設定ファイルの準備は行われるが、コンテナの起動は行われない。コンテナを起動するには、続けて「ansible-container run」コマンドを「--engine k8s」オプション引数付きで実行する。

$ ansible-container --engine k8s run
Parsing conductor CLI args.
Engine integration loaded. Preparing run.	engine=K8s
Verifying service image	service=httpd
PLAY [Manage the lifecycle of httpd-test on K8s] *******************************
TASK [Create namespace httpd-test] *********************************************
changed: [localhost]
TASK [Create service] **********************************************************
changed: [localhost]
TASK [Create deployment, and scale replicas up] ********************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=3    unreachable=0    failed=0
All services running.	playbook_rc=0
Conductor terminated. Cleaning up.	command_rc=0 conductor_id=a37d015c6d665e8ec12a3eb8371b2da8233c717396295673c1caff8608e127d2 save_container=False

実行に成功すれば、プロジェクト名(通常は「ansible-container init」コマンドを実行したディレクトリのディレクトリ名)と一致するネームスペース内でKubernetesのdeployやpodが作成される。今回は「httpd-test」というプロジェクト名を使用したので、次のようにネームスペースやデプロイ、Podを確認できる。

$ kubectl get namespace
NAME          STATUS    AGE
default       Active    5d
httpd-test    Active    16h
kube-system   Active    5d

$ kubectl -n httpd-test get deploy
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
test-httpd   1         1         1            1           16h

$ kubectl -n httpd-test get pod
NAME                          READY     STATUS    RESTARTS   AGE
test-httpd-1181566071-wdsxf   1/1       Running   0          1m

Podの停止や破棄はDockerの場合と同様、「ansible-container stop」や「ansible-container destroy」コマンドで実行できる。

$ ansible-container --engine k8s stop
Parsing conductor CLI args.
Engine integration loaded. Preparing to stop all containers.	engine=K8s
PLAY [Manage the lifecycle of httpd-test on K8s] *******************************
TASK [Stop running containers by scaling replicas down to 0] *******************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0
All services stopped.	playbook_rc=0
Conductor terminated. Cleaning up.	command_rc=0 conductor_id=34fbb1a83a92636fdee79c102a1f8107c64c4553a0f39a684ee0baf139bc554b save_container=False

デプロイ設定を追加する

先の例では利用するリポジトリ設定のみしか行っていなかったが、container.ymlファイルではKubernetesへのデプロイなどの際の設定を記述することもできる。たとえばクラスタ内でのレプリカ数を設定するには、次のように設定したいサービス以下に「k8s:」および「deployment:」パラメータを追加し、「replicas:」パラメータでレプリカ数を指定すれば良い。

version: "2"
settings:
  conductor_base: debian:jessie-backports

services:
  test-httpd:
    from: "httpd:2.4"
    ports:
      - "8888:80"
    environment:
      PATH: "/usr/local/apache2/bin:/bin:/usr/bin:/usr/local/bin"
    command: ["httpd-foreground"]
    roles:
      - test-webserver
    k8s:
      deployment:
        replicas: 2
registries:
  local-test:
    url: "http://192.168.1.10:5000"

また、デプロイ時のノード決定方法を設定する「strategy:」パラメータやなども用意されている。詳しくはドキュメントを参照してほしい。

まだ不親切な点も多いが、Ansibleユーザーには便利なAnsible Container

Ansible Containerはまだ開発段階のソフトウェアであり、そのためドキュメントが必要最低限しか用意されておらず、また問題が発生した際のメッセージも不親切で分かりにくいといった問題もある。とはいえ、現時点でも十分に活用可能であり、Ansibleを利用しているユーザーであれば簡単にコンテナの設定や管理を行える。Dockerクラスタ管理ツールはさまざまなものが登場しており、Ansible Containerの競合となるソフトウェアもあるが、AnsibleユーザーであればAnsible Containerはかなり有用なツールだと思われる。今後のバージョンアップにも期待したい。