Kubernetes x Ansible Container(ansible-containerの概要)

みなさま、クリスマスケーキの準備はできていますでしょうか？
本エントリーは[Kubernetes Advent Calendar 2016](http://qiita.com/advent-calendar/2016/kubernetes)の23日目、クリスマス・イブ・イブの記事です。
今回は、Ansibleを利用してKubernetes上にコンテナを簡単にデプロイする方法について紹介したいと思います。
何で、Kubernetes Advent CalendarなのにAnsibleかといいますと。まぁ…。マイブームみたいなもんです。

<font color="DarkOrange">Promotion!!</font>
-----
Ansibleってどうやって使うの？って方は、是非×2、こちらも見て頂けるとうれしいです。
[Qiita 「Ansible実践ガイド」出版しました。](http://qiita.com/spchildren/items/58550d9b8bea047d2ab8)
レビュー、感想お待ちしております。

#概要
さて、今回はKubernetes環境をAnsibleで作ろうっ!!というタイトルではありません。
Ansibleを利用したKubernetes環境のセットアップは、Ansible-Galaxy上にも多数あります。また、以前は「kubespray」というプロジェクトがあったのですが、いつの間にか閉鎖されているため、現在は本家のGit上のプレイブックがおすすめです。
https://github.com/kubernetes/contrib/tree/master/ansible

ということで、今回は**Ansible Container**を使って、Kubernetes上にコンテナをデプロイする操作を紹介します。Ansible Containerを使うと何が嬉しいのかというと、KubernetesのPodやReplicaSet、Deploymentに必要なYAML記法を覚えなくても、コンテナのライフサイクルが管理できるという点です。

#Ansible Containerとは
Ansible Containerは、Dockerコンテナのビルド/展開/管理の自動化を、Ansibleのプレイブックから行えるオープンソースプロジェクトです。AnsibleのDockerモジュールでできる機能に加えて、コンテナアプリケーションのライフサイクル全体を自動化することができます。
たとえば、Ansibleだけでdocker buildを行おうとすると、Dockerfileを生成したあとに「docker_image」モジュールを利用します。その次にできたコンテナを元にpod.yamlなどを生成してから「kubernetes」モジュールを利用するといった流れが必要ですが、これらをansible-containerコマンドを利用すると、プレイブックだけで全てを管理することが可能になります。
(参照) [Ansible Container Doc](https://docs.ansible.com/ansible-container/)

#Ansible Containerのインストール
論より証拠ということで、実際に手を動かしてみたほうが、感覚が掴みやすいかと思います。

##システム要件
ansible-containerを利用するためには、以下があらかじめセットアップされている必要があります。

- Python 2.7
- pip
- setuptools 20.0.0+
- Docker Engine(Dockerデーモンへの接続)
- Docker Repository(Kubernetes上に展開する場合)

##インストール手順
ansible-containerは、pipから簡単にインストールできます。ただし現時点(2016末)では、まだ未成熟なプロダクトということもあるため、ソースからのインストールをおすすめします。

```
$ virtualenv venv
$ source ./venv/bin/activate
$ git clone https://github.com/ansible/ansible-container.git
$ cd ./ansible-container
$ python ./setup.py install
$ ansible-container version
Ansible Container, version 0.3.0-pre
```
今回使用したバージョンは、0.3.0開発版です。

#Ansible Containerのコンポーネント
まずは、各コンポーネントを整理しておきましょう。

##コマンドオプション
ansible-containerのオプションは、以下になります。Dockerコンテナのライフサイクルをコマンド一つで操作するため、それぞれどのファイルを利用して、何を行っているオプションなのかを理解しないと、始めは意外とわかりづらいです。

|Option |プロジェクトファイル|動作|
|:------|:------------|:------------|
|init   | --          |ansible-containerのプロジェクトファイル群を初期作成する。|
|run    |container.yml|ビルドで作成した対象コンテナイメージを起動する。|
|install|--           |Ansible-Galaxy上から特定プロジェクトをロードする。|
|push   |container.yml|ビルドしたコンテナイメージを、リポジトリに保存する。|
|shipit |container.yml|リポジトリ上の対象コンテナイメージをオーケストレーションエンジンに配備するプレイブックの作成。|
|stop   |container.yml|起動している対象コンテナを停止する。|
|restart|container.yml|起動している対象コンテナを再起動する。|
|build  |container.yml  main.yml  requirements.txt|指定したコンテナイメージを利用して、コンテナの作成、ビルド、コミットを行う。|

##プロジェクトファイル
まず、プロジェクト名のフォルダを作成し、その中でinitを行うと以下のプロジェクトファイル群ができます。

```
$ mkdir -v ./k8s
$ cd ./k8s
$ ansible-container init
$ ls -la ./ansible
   container.yml
   main.yml
   meta.yml
   requirements.txt
   requirements.yml
   ansible.cfg
```

この中でも特に重要なのが、**container.yml**と**main.yml**です。
###container.yml
Dockerfileや、Docker Composeのスキーマに近く、このファイルにコンテナの定義を行います。これを利用して、ビルドするイメージ、実行するコンテナ、そしてリポジトリに登録するイメージが決定されます。 

```YAML:container.yml
version: "2" ##固定
services:
  web:    ##イメージ名
    image: "ubuntu:trusty" ##ベースイメージ
    ports:  ##ポートバインド
      - "80:80"
    command: ["/usr/bin/dumb-init", "/usr/sbin/apache2ctl", "-D", "FOREGROUND"] ##起動コマンド
    dev_overrides: ##注釈(※1)
      environment:
        - "DEBUG=1"
```

※1. dev_overridesは、ローカルのコンテナエンジン上で動かす時など、開発環境の場合にのみ適応されるセクションです。
※2. サポートされている、[細かなディレクティブ](https://docs.ansible.com/ansible-container/container_yml/reference.html)は公式ページを参照ください。

###main.yml
コンテナをビルドする際(ansible-container build)に、設定したい作業をプレイブック形式で定義します。この際、コンテナへの接続はSSHではなく、ビルド時に作成されるインベントリを元に、Docker Connection Pluginによって接続されます。
main.ymlにより、いままでDockerfileに記述していた「RUN command && \」のシェル芸作業を、全てプレイブック化することができます。もちろん、コンテナ内のタスクが肥大化するのはおすすめしませんが、roleを利用することも可能です。

###requirement.txt
特定のAnsibleモジュールではPythonのライブラリを追加しないと、利用できないモジュールがあります。コンテナをビルドする際も同様に、main.ymlの中で使われているモジュールにおいて、他のPythonライブラリが必要なものは、事前にこのファイルにpip形式で記述しておくとインストールしてくれます。

#Ansible Containerによるコンテナのライフサイクル管理
コンポーネントの要素を抑えた上で、ansible-containerで実現できる、コンテナのライフサイクルを抑えておきましょう。すべての工程がDocker環境上で作業が行われるため、DOCKER REMOTE API(Dockerエンジン)に接続できる状態にしておく必要があります。(DOCKER_HOST環境変数 or UNIXドメインソケット)

1. コンテナのビルド
    「ansible-container build」を実行して、新しいイメージを作成しコミットします。この際、ベースとなるイメージは、container.ymlの情報をもとにし、main.ymlで定義した作業を実施してくれます。

2. コンテナの起動
    「ansible-container run」を実行すると、1で作成したイメージを指定のDockerエンジン上で起動します。要するに、ライフサイクルでいうところのテスト環境へのデプロイです。

3. コンテナイメージのレジストリ登録
    「ansible-container push」を実行して、container.ymlに記載されたレジストリにコンテナイメージを登録します。

4. Kubernetesへの展開用プレイブックの作成
    「ansible-container shipit」を実行して、container.ymlに記載したレプリカ設定でイメージをデプロイするためのプレイブックを作成します。ただし、現状サポートされているオーケストレーションエンジンは、KubernetesとOpenShiftの2択です。

5. Kubernetesへの展開  **※本テーマの主旨**
    kubectlを使用して4で作成されたプレイブックをもとに、レジストリからイメージをPULLして展開します。ライフサイクルでいうところの本番環境へのデプロイです。

#(補足)Kubernetes環境の準備
今回はRancherを利用して、Kubernetes環境を構築しました。Kubernetesの投稿なのに、Kubernetesの設定説明を端折ります。。。すいません。
※Rancher使ってるなら、UIからデプロイできるじゃん…って突っ込むところではないです。あくまでKubernetesのYAML設定を記述しなくてもOKなところが売り。

Rancherを使用した構築方法は、以下がおすすめです。
[Helion OpenStackの上にRancherでKubernetesクラスタを構築してみる（その１）](http://qiita.com/jyoshise/items/b8fd6928fbad78978665)

ansible-containerをインストールしたホストに、kubectlコマンドとkubernetes APIへの接続ができるようにしておきましょう。

![WS00000003.JPG](https://qiita-image-store.s3.amazonaws.com/0/151157/bd6360b6-0497-2f73-a14e-39357803fdb1.jpeg)

#Ansible Containerの実装
長い×2、説明でしたが。。。ここまできてやっと本題です。
簡単なデモ実装により、Kubernetes環境にコンテナをデプロイしてみたいと思います。今回はKubernetes環境にnginxのコンテナをデプロイしてみます。

##1. コンテナのビルド
まずcontainer.ymlを作成し、ベースイメージを作成します。今回はmain.ymlには特に何も設定しませんが、必要な設定を行って下さい。

```
# mkdir -v ./k8s && cd ./k8s
# ansible-container init
# vi ./ansible/container.yml
# vi ./ansible/main.yml   ##必要な作業がある場合
# ansible-container build
# docker images
REPOSITORY                                                   TAG                 IMAGE ID            CREATED             SIZE
k8s-web                                                      20161222184749      ae8305383ae5        8 seconds ago       180.7 MB
k8s-web                                                      latest              ae8305383ae5        8 seconds ago       180.7 MB
```

```YAML:container.yml
version: "2"
services:
  web:
    image: "nginx:stable"
    ports:
      - 8080:80
    command: ["nginx", "-g", "daemon off;"]
    dev_overrides:
      environment:
        - "DEBUG=1"
    options:
      kube:
        replicas: 2
registries:
  docker_regist:
    url: https://hub.docker.example.local
    namespace: shingo.kitayama/test
```
##2. コンテナの起動
この作業は必須ではありませんが、できたコンテナイメージの起動を行ってみましょう。インベントリなどは、指定する必要はありません。

```
# ansible-container run
Attaching to ansible_ansible-container_1
Cleaning up Ansible Container builder...
Attaching to ansible_web_1
....[CTL + C]
# docker ps -a
CONTAINER ID        IMAGE                                      COMMAND                  CREATED             STATUS                      PORTS               NAMES
ff70c14b54fb        k8s-web:latest                             "nginx -g 'daemon off"   17 minutes ago      Exited (0) 17 minutes ago                       ansible_web_1
```
今回、Nginxを起動しているため、コンテナを起動するとNginxのプロセスにAttachしてしまいます。実はansible-containerには、まだバックグラウンド起動(docker run -d)というオプションがありません。よって、一度プロセスを切ってみると、確かに起動していたことが確認できます。
※今後、機能追加されることを願っています。

##3. コンテナイメージのレジストリ登録
コンテナイメージのレジストリ登録には、レジストリの認証情報が必要です。リポジトリ情報は、container.ymlのregistriesセクションで指定し、認証情報はコマンド上で指定してください。
その他の、細かな指定方法は[公式ページ](https://docs.ansible.com/ansible-container/registry_auth.html)も参考にしてください。

```
# ansible-container push --push-to docker_regist --username "shingo.kitayama" --password "kubernetes"
```

##4. Kubernetesへの展開用プレイブックの作成
container.ymlから、Kubernetesへの展開情報を設定したプレイブックを作成します。「ansible-container shipit」では、コンテナオーケストレーションエンジンの指定を行う必要があります。

```
# ansible-container shipit kube --pull-from docker_regist --username "shingo.kitayama" --password "kubernetes"
Images will be pulled from hub.docker.example.local/shingo.kitayama/test
Attaching to ansible_ansible-container_1
Cleaning up Ansible Container builder...
Role k8s created.
# ls ./ansible/roles/k8s-kubernetes/  ## kubernetes専用のロール、モジュールが作成される。
defaults  library  meta  README  tasks  test
```
```YAML:./ansible/shipit-kubernetes.yml
- name: Deploy k8s to kubernetes
  hosts: localhost
  gather_facts: false
  connection: local
  roles:
  - role: k8s-kubernetes
    playbook_debug: false
```
```YAML:./ansible/roles/k8s-kubernetes/tasks/main.yml
- kube_deployment:
    labels:
      app: k8s
      service: web
    deployment_name: web
    containers:
    - securityContext: {}
      name: web
      args:
      - nginx
      - -g
      - daemon off;
      image: hub.docker.example.local/shingo.kitayama/test/k8s-web:20161222184749
      ports:
      - 80
    replace: true
    replicas: 2
  register: output

- debug: var=output
  when: playbook_debug
```

##5. Kubernetesへの展開
最後は、作成されたshipit-kubernetes.ymlをansible-playbookコマンドから実行すると、Pods / Replica Set / DeploymentsのYAMLを自動的に生成して、Podをデプロイしてくれます。

```
$ ansible-playbook ansible/shipit-kubernetes.yml
PLAY [Deploy k8s to  kubernetes] ***********************************************

TASK [k8s-kubernetes : kube_deployment] ****************************************
changed: [localhost]

TASK [k8s-kubernetes : debug] **************************************************
skipping: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0
```

![WS00000004.JPG](https://qiita-image-store.s3.amazonaws.com/0/151157/54218c54-ab59-a78d-71c9-0bdbf7616c80.jpeg)

ということで、Ansible ContainerによるKubernetesへのコンテナデプロイでした。

#参照

<iframe src="//www.slideshare.net/slideshow/embed_code/key/cD9kQwzvPRSu0T" width="340" height="290" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/NaoyaHashimoto/container-sig1-ansiblecontainer" title="Container sig#1 ansible-container" target="_blank">Container sig#1 ansible-container</a> </strong> from <strong><a target="_blank" href="//www.slideshare.net/NaoyaHashimoto">Naoya Hashimoto</a></strong> </div>

#最後に
ansible-containerのバージョン0.3.0でどんな機能が追加されるのかが、公式ブログに展開されています。
https://www.ansible.com/blog/ansible-container-0-3-0

まだ開発途中のプロダクトなので、サポートしきれていない動作に関しては、地道に待ちましょう。

Kubernetes Advent Calendar勢の中で、ちょっと息抜きな感じで仕上がりました。
明日はいよいよクリスマス・イブですねー。歌ってギターも弾けるエンジニア、@jyoshiseさんのご登壇になりますっ。お楽しみにねー。

ではみなさま、HappyXmas!!