最近はAWSのAMIやECSでコンテナたてる時に使うDocker ImageをPackerで作成している @hatappiです
Packerを使うとAMIにしてもDocker Imageにしても自分の好きなプロビジョニングを使用することが出来るので好きです
今回は例えば元となるdocker imageにはrbenv/rbenvがインストールされており
.bashrc
なり/etc/profile.d/rbenv.sh
なりに下記のようにPATHが定義されているものがあるとします
export RBENV_ROOT=/usr/local/rbenv export PATH="${RBENV_ROOT}/bin:${PATH}" eval "$(rbenv init -)"
このImageをPackerで使ってbundle install
したイメージを作るとする
bundle.manifest.json
{ "builders":[{ "type": "docker", "image": "hatappi/rbenv-app", }], "provisioners": [ { "type": "shell", "inline": [ "bundle install --gemfile=/app/Gemfile" ] } ], "post-processors": [ [ { "type": "docker-import", "repository": "hatappi/hoge", "tag": "latest" } ] ] }
これを単純に実行してしまうと bundle: command not found
というエラーで起きてしまう
これは単にパスが通ってないだけなので下記のようにsourceでPATHが定義されたものを読みこんであげれば解決する
"provisioners": [ { "type": "shell", "inline": [ "source /etc/profile.d/rbenv.sh", "bundle install --gemfile=/app/Gemfile" ] } ],
ここからが本題でこれの別解としてPackerのオプションを利用していく
まず
{ "type": "shell", "inline": [ "bundle install --gemfile=/app/Gemfile" ] }
を行うとPackerではどうなるかというとまずコンテナ上に/tmp/script_6464.sh
のような一時ファイルを作成します
中身としては
#!/bin/sh -e bundle install --gemfile=/app/Gemfile
となっておりこれをPacker側で下記のようにdocker exec
を使って実行している
docker exec -i aaaaa /bin/sh -c (chmod +x /tmp/script_6464.sh; PACKER_BUILDER_TYPE='docker' PACKER_BUILD_NAME='docker' /tmp/script_6464.sh):
ここまでくるとなんとなく見えてくる
一時ファイルとして作成されたシェルファイルの1行目のshebangを変更してあげれば良い
type: “shell"のドキュメントを見るとinline_shebangというそれらしいオプションがある!!
これを使って
{ "type": "shell", "inline": [ "bundle install --gemfile=/app/Gemfile" ], "inline_shebang": "/bin/sh -le" }
こんな感じで書いてあげる
-l
を追加することでログインしたときのようにbashで実行することが出来るのでPATHなども読み込まれた状態で実行することが出来る
どちらを使っても実現は出来たがPackerの挙動が知れたので良かったとします