最近また、Docker に入門しなおしている。今回で3回目。Docker for Mac がずいぶん良いらしいので、Docker で Rails アプリを動かしてみた - えいのうにっき が前回入門したときの記事。
さすがに3回目ともなると「あぁー!はいはい、そうでしたそうでした!」ということも多くて、まぁこれはこれでアリか、と思い直してみたりもしている。
今回の入門にあたっては、「プログラマのためのDocker教科書 インフラの基礎知識&コードによる環境構築の自動化」という本を使っている。Docker・コンテナのことのみに留まらず、コンテナを扱うに際しておさえておきたいインフラ・ネットワークについての話などについても触れられていて、まだ読んでる途中ではあるのだけどなかなかいい感じ。
プログラマのためのDocker教科書 インフラの基礎知識&コードによる環境構築の自動化
- 作者: 阿佐志保,山田祥寛
- 出版社/メーカー: 翔泳社
- 発売日: 2015/11/20
- メディア: 大型本
- この商品を含むブログ (3件) を見る
ちなみに本の中では扱われてはいないのだけど、僕が手元で実践する際には Docker for Mac を使っている。
さて、この本を読み進めていくと「それでは実際にコンテナ(コンテナイメージ)を作ってみましょう」というところがあるのだけど、その方法は Dockerfile。それはまぁ当然って感じだし、僕自身も「郷に入っては郷に従え」的な意味でも Dockerfile で進める気まんまんだったんだけど、「本に書いてある通りのコンテナを作ってみてもつまんないなー」などと考えてしまったのが運の尽き(?)。
現在 grass-graph をホストしているサーバ構成を題材・目標にして、コンテナを作ってみようと考えた。のだけど、これを Dockerfile にするのが辛くって。普段 Chef(knife-solo)を使ってるからなおさらなんだと思う。Ruby の DSL で書ける Chef recipe で実施しているような各種セットアップを、Dockerfile の ADD ...
RUN ...
といった命令だけで再現するのは、僕には途方もない作業に思えた。
Packer よさそう
何かいい方法はないか、というところで Packer の存在を思い出した。「AMI を作るときに使うツール」みたいな雑な認識はもともと持っていて、今回も「VMに対して knife-solo でプロビジョニングしたものをそのまま docker のイメージに変換できたりしないかなー」などといった想像をしつつ、調べはじめてみた。
すると、詳しい仕組みはよくわからないのだけど、「Packer による仮想マシンやビルドの際にプロビジョニングをどのように行うかを指定できる provisioners
というプロパティがある」「 provisioners
にはシェルスクリプトや Ansible に加えて、Chef solo も指定できる」ということがわかった。Vagrantfile に書ける provision みたいなものっぽい。
これはもしかしてとんでもない俺得ツールなのでは、と思い、書きかけの Dockerfile を放り投げ(最低である)、Packer のインストールをはじめた。
Packer と Chef recipe を使って Docker イメージのビルドを行うまで
以下の作業は全て作業マシン・Mac上で実施している(Docker for Mac のバージョンは 17.03.0-ce-mac2 (15654)
)。まずはこちらで Packer をダウンロード。
ダウンロードできるものは zip なので、解凍したものを /usr/local/bin
に配置。
$ mv ~/Downloads/packer /usr/local/bin $ packer version Packer v0.12.3
ok!
Packer では、何を・どのような方法でビルドするかということを json 形式の設定ファイルで指定するらしい。てことで、まずは、このリポジトリで管理している Chef recipe(berkshelf(community cookbooks)は基本使ってない/ site-cookbooks
ディレクトリに自作レシピを突っ込んでいる/もっぱら knife-solo で使っている)を使って「git がインストールされた Docker イメージ」を作れるかどうか、試してみる。
{ "builders":[ { "type": "docker", "image": "centos:centos7", "export_path": "image.tar" } ], "provisioners":[ { "type": "shell", "inline": [ "yum -y update", "yum install -y sudo which" ] }, { "type": "chef-solo", "cookbook_paths": ["site-cookbooks"], "run_list": [ "git" ] } ], "post-processors": [ { "type": "docker-import", "repository": "a-know/a-know-server-base-container", "tag": "0.0.1" } ] }
この json ファイルに関するメモは以下。
builders
type
をdocker
にすれば docker image をビルドできるimage
には、ベースにしたい Docker イメージを指定する。Dockerfile でいうところのFROM
。- とりあえず今のサーバが CentOS7 なので踏襲し、これをベースにすることにする。
- CentOS に依存した書き方をしてしまっているレシピがいくつかある…。。
- Docker に慣れたら CoreOS とかを使ってイメージのスリム化とかにも挑戦したい…。。
- とりあえず今のサーバが CentOS7 なので踏襲し、これをベースにすることにする。
provisioners
- Docker イメージのプロビジョニング方法を指定できる。
- 複数の type でのプロビジョニング方法を指定することができて、今回はシェルスクリプトと chef-solo の二種類を指定している。
- ベースとした CentOS7 の Docker image には sudo や which すら入っていなかった & さすがにそんなレシピは用意していなかったので、これらだけはシェルスクリプトで入れることにした。
- Chef recipe を使う場合は、
type
にchef-solo
を、cookbook_path
にレシピのディレクトリを、run_list
に適用したいレシピ名を、それぞれ指定する。
post-processors
- 今回は docker image としてビルドしたいので
docker-import
とした。 - box(virtualbox の)にもできるらしい。
- 今回は docker image としてビルドしたいので
ちなみに、git をインストールするためのレシピは↓こんなん。
package 'git'
もっともシンプルな部類のレシピ。
この json ファイルの内容で docker image をビルドしてみる。
$ packer build container_base.json docker output will be in this color. ==> docker: Creating a temporary directory for sharing data... ==> docker: Pulling Docker image: centos:centos7 docker: centos7: Pulling from library/centos (中略) docker: Installed: docker: sudo.x86_64 0:1.8.6p7-21.el7_3 which.x86_64 0:2.20-7.el7 docker: docker: Complete! ==> docker: Provisioning with chef-solo docker: Installing Chef... docker: % Total % Received % Xferd Average Speed Time Time Time Current docker: Dload Upload Total Spent Left Speed docker: 100 20500 100 20500 0 0 60410 0 --:--:-- --:--:-- --:--:-- 60471 docker: el 7 x86_64 docker: Getting information for chef stable for el... (中略) docker: [2017-03-14T12:43:13+00:00] INFO: Loading cookbooks [git@0.1.0] docker: [2017-03-14T12:43:13+00:00] INFO: Storing updated cookbooks/git/recipes/default.rb in the cache. docker: [2017-03-14T12:43:13+00:00] INFO: Storing updated cookbooks/git/metadata.rb in the cache. docker: [2017-03-14T12:43:13+00:00] INFO: Processing yum_package[git] action install (git::default line 1) docker: [2017-03-14T12:43:14+00:00] INFO: yum_package[git] installing git-1.8.3.1-6.el7_2.1 from base repository docker: [2017-03-14T12:43:37+00:00] INFO: yum_package[git] installed git at 1.8.3.1-6.el7_2.1 docker: [2017-03-14T12:43:37+00:00] INFO: Chef Run complete in 23.88294074 seconds (中略) ==> docker: Running post-processor: docker-import docker (docker-import): Importing image: Container docker (docker-import): Repository: a-know/a-know-server-base-container:0.0.1 docker (docker-import): Imported ID: sha256:144967a22d8c87cd233232b8533dc250660eb506d8e82fc6fa76714c6fb0bd9c Build 'docker' finished. ==> Builds finished. The artifacts of successful builds are: --> docker: Imported Docker image: a-know/a-know-server-base-container:0.0.1
おお。ちゃんと sudo
と which
、あと Chef がインストールされて、レシピ( run_list
)に指定した git
もインストールされたっぽい!
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE a-know/a-know-server-base-container 0.0.1 144967a22d8c 11 minutes ago 482 MB
イメージのデカさには目をつむって下さい。。
$ docker run -it 144967a22d8c /bin/bash [root@b5d113a7be3d /]# which git /usr/bin/git
おお。よさそう!
ちなみにここでちょっと余談なんだけど、最近の Docker のリリースによって docker
コマンドの体系が一新されたらしい。上の docker images
は、新しい体系だと docker image ls
なんだとか。(参考:docker container / image コマンド新旧比較 - Qiita )
で。うまく既存のレシピを使ってのイメージ作成ができそうなので、本来の目的を満たすコンテナイメージに必要そうなもの(レシピ)全てを指定してみる。timezone や crontab に関するレシピはコンテナに対して適用するものじゃなさそうなので省いた。
{ "builders":[ { "type": "docker", "image": "centos:centos7", "export_path": "image.tar" } ], "provisioners":[ { "type": "shell", "inline": [ "yum -y update", "yum install -y sudo which make" ] }, { "type": "chef-solo", "cookbook_paths": ["site-cookbooks"], "run_list": [ "gcc", "git", "imagemagick", "openssl", "openssl-devel", "patch", "ruby", "zlib::devel" ], "json": { "ruby": { "version": "2.4.0", "checksum": "cd0bd4e7eb8a767f44394c3cb7ebefbfb0386193898e51e533dd525da2ddcdb3" } } } ], "post-processors": [ { "type": "docker-import", "repository": "a-know/a-know-server-base-container", "tag": "0.1.0" } ] }
run_list
の下に json
っていうプロパティが増えている。これは、Chef でいうところの attributes
である。
上記の json ファイルの内容で packer build
してみる。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE a-know/a-know-server-base-container 0.0.1 144967a22d8c 48 minutes ago 482 MB a-know/a-know-server-base-container 0.1.0 9c7a8703ae10 9 minutes ago 754 MB $ docker run -it 9c7a8703ae10 /bin/bash [root@68aff9558c1c /]# which ruby /usr/bin/ruby
よさそう!(754 MB…)
run_list
に指定するレシピも増えて、中には include_recipe
を指定しているレシピもあるのだけど、それもしっかり効いているようだし。
Packer の唯一残念なところ
この時点でもう Packer 最高じゃんっていう感じなんだけど、唯一残念なところとしては、キャッシュ(スナップショット?)が効かないところ。
Dockerfile によるビルドの場合は ADD ...
RUN ...
といった各ステップごとにキャッシュが作られて、二回目以降のビルドでは特に変化がなければ勝手にそれが使われる。でも、Packer には今のところそれがない(多分)。
今回作ってみたイメージはそれほどストレスなく作ることができたけど、今後さらにこのイメージにあれやこれやを付け加えていくとなると、ビルド待ちの時間も馬鹿にならなくなる。となると、今後の作業はこのイメージをベースとして・"image": "a-know/a-know-server-base-container"
みたいに指定できるとよさそう。
それをするには、自家製イメージをどこかに上げる必要がありそう。となると、Docker Hub か…、、と思ったのだけど、環境変数で指定してるようなヒミツな credential を含むイメージも今後作っていくことになりそうなことを考えると、「多少のお金は払ってでもプライベートレジストリがいい」「自身のインフラ環境を AWS 化させる(という今年の目標)」という2点から、Amazon ECR の利用が選択肢に上がってきた。
ってことで、Packer への入門はこれくらいにして、今度は Amazon ECR にも入門してみることにする。この時点でもう本の内容はそっちのけである…。。w
まとめ
- Packer は今のところ最高
- itamae を使ってる人にもいいんじゃないかな?
- 「プログラマのためのDocker教科書 インフラの基礎知識&コードによる環境構築の自動化」も良書感ある。おすすめ
- Docker for Mac のアイコン類、かわいい
可愛いすぎるでしょ
参考
- Packerを使ってChef/Puppet/AnsibleでDockerのイメージをつくる | SOTA
- Templates: Provisioners - Packer by HashiCorp
- packerのインストール - ksaitoの日記
プログラマのためのDocker教科書 インフラの基礎知識&コードによる環境構築の自動化
- 作者: 阿佐志保,山田祥寛
- 出版社/メーカー: 翔泳社
- 発売日: 2015/11/20
- メディア: 大型本
- この商品を含むブログ (3件) を見る