最近はDocker Imageを作る時Dockerfile以外の方法としてPacker + MItamaeとかでDocker Imageを作ることがある。
そのためにPacker用のMItamaeプラグインとかも作ったりもした。
最近プロビジョニング周りでAnsibleを使うことになったので、改めてDocker Imageを作る方法を調べた。
実現したいこと
どう実現するか
まずDocker Imageを作るところにフォーカスをあてると
どうやらAnsibleには Ansible Containerと呼ばれるDocker Imageを作ったりしてくれるものが提供されているらしい。
任意のレジストリへログインしていれば、pushだって出来てしまう。
これを使えばAnsibleだけで完結することが出来そう!!!
Ansible Container
Ansible Container自体はpipを使って簡単にインストールすることが出来る。
$ pip install ansible-container[docker]
インストールが完了するとansible-container
コマンドが使えるようになって、始めに必要なものを作ってくれる ansible-container init
をコマンドラインで実行する。
すると下記のようなファイルが生成される。
. ├── ansible-requirements.txt # Ansibleを実行する時に必要なPythonモジュールをpip形式で記載する ├── ansible.cfg # ansible コマンドを実行する時の設定を記載する ├── container.yml # コンテナの設定 ├── meta.yml # ライセンスとか諸々をかいていく。Galaxyとかのリポジトリで共有される時に使用される └── requirements.yml # Ansibleを実行する時に外部リポジトリからダウンロードするもの
今回はサンプルとしてnginxをいれてCMDにたいしてnginxを起動させるようなものを作る。
先程生成したファイル群と同じディレクトリに下記のようにファイルを配置する。
Ansibleのディレクトリ構造については公式で Best Practiceが出ているのでこれを参照する。
└── roles └── nginx └── tasks └── main.yml
main.ymlには下記のように記載する。
やっていることはシンプルで、nginxのyumリポジトリを追加してあげて、インストール。
そして起動設定をして、サービスを起動するだけ。
ここで1つポイントしては今回docker上ではserviceで起動に関してコンテナを立ち絵上げる時にするので、スキップするように when: ansible_connection != 'docker'
と記載してあげる。
- name: Add nginx repository yum_repository: name: nginx_repo description: nginx repo baseurl: http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck: 0 enabled: 1 - name: install nginx yum: name=nginx state=present - name: set auto start nginx command: chkconfig nginx on - service: name: nginx state: restarted when: ansible_connection != 'docker'
これでroleは完成したので最後にconatiner.yml
を作っていく。
container.yml
の書き方や何を意味するかは公式のdocにのっている。
ポイントだけ紹介するとservices配下に作成したいDocker Imageの設定を記載していく。
今回は起動した時にnginxのプロセスを立てたいので、CMDには["nginx", "-g", "daemon off;"]
を設定している。
registriesではどこにpushするのかの情報を記載する今回はECR上にtest
というネームスペースのtest-nginx
があるとする。
registries配下のnamespace欄がECRのネームスペースにあたる。またrepository_prefix
とあるがこれを設定することでAnsible Containerはserivice配下のkeyと組み合わせて test-nginx
というものを作成する。
version: "2" settings: conductor: base: centos:7 roles_path: - roles project_name: hoge services: nginx: from: "centos:7" roles: - nginx command: ["nginx", "-g", "daemon off;"] registries: ecr: url: https://0000.dkr.ecr.ap-northeast-1.amazonaws.com namespace: test repository_prefix: test
これで一通りできたのでまずはローカルにdocker imageを作ってみる。
$ 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=hoge Building service... project=hoge service=nginx PLAY [nginx] ******************************************************************* TASK [Gathering Facts] ********************************************************* ok: [nginx] TASK [nginx : Add nginx repository] ******************************************** changed: [nginx] TASK [nginx : install nginx] *************************************************** changed: [nginx] TASK [nginx : set auto start nginx] ******************************************** changed: [nginx] TASK [nginx : service] ********************************************************* skipping: [nginx] PLAY RECAP ********************************************************************* nginx : ok=4 changed=3 unreachable=0 failed=0 Applied role to service role=nginx service=nginx Committed layer as image image=sha256:612fd9e04b1ce2b1b0ecf2bf8cdc198cb59646f0726c8bd41c6c572e6cf service=nginx Build complete. service=nginx All images successfully built. Conductor terminated. Cleaning up. command_rc=0 conductor_id=4285231fc80e8625b2e155496c5e033486e767146816f912a8e69 save_container=False
docker images
で見るとちゃんと作られている!!
あとはECRにpushするだけで
# --tagで任意のタグをうつことが出来る $ ansible-container deploy --push-to ecr --username AWS --password xxx --tag latest
これで無事ECRにnginxのDocker Imageがpushされる。
後は docker run -d -p 8080:80 0000.dkr.ecr.ap-northeast-1.amazonaws.com/test/test-nginx
とかで80ポートを8080にポーティングしてあげれば、ブラウザでいつもの画面をみることが出来る。
やってみて
Ansible完結できた!
ただやってみて個人的に2つ問題があって、
1つ目が今回はしなかったがAMIを作ることを考えた時にAnsibleにはec2 module
やec2_ami module
があるので、これらを組み合わせてec2でインスタンスをたてて => sshで接続できるまで待ってあげて => Ansibleでプロビジョニングしてあげて => ec2インスタンスからAMIを作ってあげてみたいなことをしないといけない。
2つ目がpushする際の認証で、このAnsible Containerの仕組みとしてはdocker login
などした時に~/.docker/config.json
などに認証情報が記載されているのだが、このファイルを読み込んで対象レジストリの認証情報をBase64でデコードしてユーザー情報とパスワードを取り出している。コードだとこのへん
この実装だとECRの認証をよしなにやってくれる公式のamazon-ecr-credential-helperも使えない。
これはわりと面倒。
この2つはPackerが解決してくれる。
1つ目のAMIの部分もPackerにはプラグインが提供されており、面倒なインスタンスの制御とかをよしなにやってくれる。
2つ目の認証部分に関してはPackerは裏側でgoのaws sdkを使用するようにしているので、環境変数とか~/.aws
配下とかをよしなに見に行ってくれる。
結論
Packer最高じゃん。