Dockerのイメージをエクスポートしたい時がままあります。これは別環境での再ビルドが面倒であったり(ファイルコピーとインポートで済ます)、使っているイメージをそっくりそのまま使いたかったり、イメージのバックアップをとりたかったりする時などです。
Docker ComposeはあるプロジェクトのDockerに関するものをまとめて扱うための仕組みです。イメージもそれに含まれます。
このためDocker Composeで管理しているイメージをまとめてエクスポートできると便利です。この方法を紹介します。
まとめてエクスポートするためのコマンドは次です。これを実行すると images ディレクトリが作成され、そのimagesディレクトリ以下にDocker Composeで管理しているイメージが全てエクスポートされます。
mkdir -p images && docker compose images | awk 'NR>1 {print $2}' | xargs -I {} sh -c "docker save {} -o images/\$(echo {}.tar | tr '/' '-')"
個々からはこのコマンドで何をやっているかの解説です。
このコマンドはmkdir -p images
、docker compose images
、awk 'NR>1 {print $2}'
、xargs -I {} sh -c "docker save {} -o images/\$(echo {}.tar | tr '/' '-')"
に分解できます。
まずmkdir -p images
です。よく使われるディレクトリ作成のコマンドで images ディレクトリを生成しています。ここでのpオプションは既にディレクトリが存在していてもエラーにならないように付けています。&&で次コマンドと繋ぐことによって、imagesディレクトリが存在することを担保できたなら次コマンドに処理の手番を渡すようにします。
次いでdocker compose images
です。これはDocker Composeで管理しているイメージの一覧を表示するコマンドです。この表示結果は例えば次のようになります。これを元にエクスポートしたいイメージの名前を指定します。|を使って表示結果をawkコマンドに渡します。
$ docker compose images CONTAINER REPOSITORY TAG IMAGE ID SIZE xxxx-app-1 xxxx-app latest 6b58ebeaa637 694MB xxxx-mailpit-1 axllent/mailpit latest 3ba39824b93d 23.6MB xxxx-mysql-1 xxxx-mysql latest 5a6295095ed7 520MB
awk 'NR>1 {print $2}'
でヘッダ行を除去して2列目の文字列を得ます。NR>1
はNumber of Recordが1より大きい、つまり2行目以降のみを対象に取ることを示します。{print $2}
は現在のレコードの2番目のフィールド、つまり2列目の値を表示するという意味です。awk ‘NR>1 {print $2}’ は、入力されたデータの最初の行をスキップし、その後の各行について2番目のカラムのデータを出力するという動作をします。これでDocker Compose配下の各イメージの名前を抜き出します。|を用いて抜き出したイメージの名前達を最後のエクスポートコマンドに渡します。
最後にxargs -I {} sh -c "docker save {} -o images/\$(echo {}.tar | tr '/' '-')"
です。これはいささか複雑です。xrargsを使う理由はイメージ名をDockerイメージのエクスポートコマンドの標準入力で渡せる部分以外にも使いたいためです。xargsを使うことでより柔軟なコマンドを実現できます。先ほどのawkまでの処理の結果は次です。
xxxx-app axllent/mailpit xxxx-mysql
これをxargs -I {} xxx に渡すと、xargsが受け取った入力の各行が後続のコマンド内の{}に置き換えられて使用されます。つまりxargsによって実行されるコマンドは次のようになります。
sh -c "docker save xxxx-app -o images/\$(echo xxxx-app.tar | tr '/' '-')" sh -c "docker save axllent/mailpit -o images/\$(echo axllent/mailpit.tar | tr '/' '-')" sh -c "docker save xxxx-mysql -o images/\$(echo xxxx-mysql.tar | tr '/' '-')"
シェルを介しているのはイメージ名に含まれる「/」によってディレクトリがネストするのを避ける処理を入れるためです。「/」を「-」に置き換えて docker save に渡すための部分が\$(echo xxxx-app.tar | tr '/' '-')
です。xargs で渡されたイメージ名に含まれる「/」を文字列変換・削除コマンドであるtrで「-」に変換します。この変換結果を$()
で展開するようにします。「$」を「\」でエスケープをしているのは xargs の段階でこの部分が実行されるのを避けるためです。もし「\」がない場合、{} によって実際のイメージ名が渡される前に置換処理が走ってしまい、イメージ名の変換がなされません。この展開をして実行されるコマンドは次のようになります。
sh -c "docker save xxxx-app -o images/xxxx-app.tar" sh -c "docker save axllent/mailpit -o images/axllent-mailpit.tar" sh -c "docker save xxxx-mysql -o imagesxxxx-mysql.tar"
イメージ名を元にした docker save をシェル経由で実行するコマンドの完成です。これが実行されて images 以下にDocker Composeで管理されているイメージが全てエクスポートされます。
エクスポートされたイメージファイルはdocker load -i images/axllent-mailpit.tar
のようにしてDockerイメージにできます。imagesディレクトリ以下のtarファイルを全てDockerイメージとするのはfind images/ -name "*.tar" | xargs -I{} docker load -i {}
でできます。
エクスポート、インポートの対象となるイメージが多い場合はここまでで紹介した逐次実行で動作するワンライナーのコマンドより、処理を平行させるようにしたシェルスクリプトを書いて使った方が快適になりやすいです。
Dockerイメージを実ファイルとしてエクスポート、インポートすることはそんなにないですが、まとめて扱う方法があると知っておくとちょっと便利です。