LoginSignup
5
4

More than 5 years have passed since last update.

Docker muti-stage builds で中間イメージにタグ付けする

Last updated at Posted at 2017-11-19

モチベーション

Docker image の容量を少しでも小さくするために,ビルド用イメージとメインのイメージを分けることがあります.特に,Docker 17.05以降では,multi-stage buildsがサポートされ取り回しがよくなりました.

Dockerfile
## Build pytorch. ##
FROM python:3.6.3 AS build-env
# Install dependencies.
RUN pip install numpy pyyaml mkl setuptools cmake cffi
# Disable GPU.
ENV NO_CUDA 1
# Download and build.
RUN wget https://github.com/pytorch/pytorch/archive/v0.2.0.tar.gz && \
    tar -zxvf v0.2.0.tar.gz && \
    cd pytorch-0.2.0 && \
    python setup.py install

## Build main image. ##
FROM python:3.6.3
ENV LD_LIBRARY_PATH /usr/local/lib
# Install dependencies.
RUN pip --no-cache-dir install numpy mkl
# Install pytorch.
RUN mkdir /usr/local/lib/python3.6/site-packages/torch \
          /usr/local/lib/python3.6/site-packages/torch-0.2.0-py3.6.egg-info
COPY --from=build-env /usr/local/lib/python3.6/site-packages/torch /usr/local/lib/python3.6/site-packages/torch
COPY --from=build-env /usr/local/lib/python3.6/site-packages/torch-0.2.0-py3.6.egg-info /usr/local/lib/python3.6/site-packages/torch-0.2.0-py3.6.egg-info
$ docker build -t pytorch .
$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
pytorch                latest              a47d831b6b81        7 seconds ago        1.52GB
<none>                 <none>              775d7f035616        About a minute ago   2.14GB

一方で, multi-stage builds で生成された中間イメージは docker image prune で削除されてしまったり, CI環境でのキャッシュの取り回しが若干面倒であったりします.そこで, multi-stage builds の中間イメージにタグ付けしたいというのがモチベーションです.

どうやるの?

COPY --from=<image> <src> <dest> の構文を使うことで,ビルド用イメージとメインのイメージをつなぐようにします.
まず,ビルド用イメージの Dockerfile を用意します.

Dockerfile.build
FROM python:3.6.3

# Install dependencies.
RUN pip install numpy pyyaml mkl setuptools cmake cffi
# Disable GPU.
ENV NO_CUDA 1
# Download and build.
RUN wget https://github.com/pytorch/pytorch/archive/v0.2.0.tar.gz && \
    tar -zxvf v0.2.0.tar.gz && \
    cd pytorch-0.2.0 && \
    python setup.py install

続いて,メインイメージの Dockerfile を用意します.

Dockerfile
FROM python:3.6.3

ENV LD_LIBRARY_PATH /usr/local/lib
# Install dependencies.
RUN pip --no-cache-dir install numpy mkl
# Install pytorch.
RUN mkdir /usr/local/lib/python3.6/site-packages/torch \
          /usr/local/lib/python3.6/site-packages/torch-0.2.0-py3.6.egg-info
COPY --from=pytorch_build:local /usr/local/lib/python3.6/site-packages/torch /usr/local/lib/python3.6/site-packages/torch
COPY --from=pytorch_build:local /usr/local/lib/python3.6/site-packages/torch-0.2.0-py3.6.egg-info /usr/local/lib/python3.6/site-packages/torch-0.2.0-py3.6.egg-info

この方法だと,2つの Dockerfile に分かれてしまうため,最後にビルド手順を記述する Makefile を用意します.

Makefile
all: main

pytorch_build: Dockerfile.build
    docker build -f Dockerfile.build -t pytorch_build:local --cache-from=pytorch_build:local .

main: Dockerfile pytorch_build
    docker build -f Dockerfile -t pytorch --cache-from=pytorch .

ビルドすると,

$ make
$ docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
pytorch                latest              da56568f200a        20 seconds ago      1.52GB
pytorch_build          local               587212a81a2f        22 seconds ago      2.14GB

中間イメージにもちゃんとタグが付いていることが分かると思います.また,mnist のサンプルもちゃんと動きます.

$ docker run -it --rm pytorch bash
root@ba9f46714e02:/# git clone https://github.com/pytorch/examples pytorch-examples
Cloning into 'pytorch-examples'...
remote: Counting objects: 1484, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 1484 (delta 1), reused 4 (delta 0), pack-reused 1478
Receiving objects: 100% (1484/1484), 29.96 MiB | 249.00 KiB/s, done.
Resolving deltas: 100% (783/783), done.
Checking connectivity... done.
root@ba9f46714e02:/# cd pytorch-examples/mnist
root@ba9f46714e02:/pytorch-examples/mnist# pip install -r requirements.txt 
Requirement already satisfied: torch in /usr/local/lib/python3.6/site-packages (from -r requirements.txt (line 1))
Collecting torchvision (from -r requirements.txt (line 2))
  Downloading torchvision-0.1.9-py2.py3-none-any.whl (43kB)
    100% |████████████████████████████████| 51kB 797kB/s 
Collecting pyyaml (from torch->-r requirements.txt (line 1))
  Downloading PyYAML-3.12.tar.gz (253kB)
    100% |████████████████████████████████| 256kB 2.0MB/s 
Requirement already satisfied: numpy in /usr/local/lib/python3.6/site-packages (from torch->-r requirements.txt (line 1))
Collecting six (from torchvision->-r requirements.txt (line 2))
  Downloading six-1.11.0-py2.py3-none-any.whl
Collecting pillow (from torchvision->-r requirements.txt (line 2))
  Downloading Pillow-4.3.0-cp36-cp36m-manylinux1_x86_64.whl (5.8MB)
    100% |████████████████████████████████| 5.8MB 253kB/s 
Collecting olefile (from pillow->torchvision->-r requirements.txt (line 2))
  Downloading olefile-0.44.zip (74kB)
    100% |████████████████████████████████| 81kB 7.0MB/s 
Building wheels for collected packages: pyyaml, olefile
  Running setup.py bdist_wheel for pyyaml ... done
  Stored in directory: /root/.cache/pip/wheels/2c/f7/79/13f3a12cd723892437c0cfbde1230ab4d82947ff7b3839a4fc
  Running setup.py bdist_wheel for olefile ... done
  Stored in directory: /root/.cache/pip/wheels/20/58/49/cc7bd00345397059149a10b0259ef38b867935ea2ecff99a9b
Successfully built pyyaml olefile
Installing collected packages: six, olefile, pillow, torchvision, pyyaml
Successfully installed olefile-0.44 pillow-4.3.0 pyyaml-3.12 six-1.11.0 torchvision-0.1.9
root@ba9f46714e02:/pytorch-examples/mnist# python main.py 
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
Processing...
Done!
Train Epoch: 1 [0/60000 (0%)]   Loss: 2.316824
Train Epoch: 1 [640/60000 (1%)] Loss: 2.311471
Train Epoch: 1 [1280/60000 (2%)]    Loss: 2.298839
Train Epoch: 1 [1920/60000 (3%)]    Loss: 2.317593
Train Epoch: 1 [2560/60000 (4%)]    Loss: 2.282021
Train Epoch: 1 [3200/60000 (5%)]    Loss: 2.272163
・・・
Train Epoch: 10 [56960/60000 (95%)] Loss: 0.142048
Train Epoch: 10 [57600/60000 (96%)] Loss: 0.199800
Train Epoch: 10 [58240/60000 (97%)] Loss: 0.102195
Train Epoch: 10 [58880/60000 (98%)] Loss: 0.305267
Train Epoch: 10 [59520/60000 (99%)] Loss: 0.162137

Test set: Average loss: 0.0543, Accuracy: 9822/10000 (98%)

まとめ

COPY --from=<image> <src> <dest> の構文を使うことで中間イメージにタグ付けすることが出来ました.しかし,1つの Dockerfile では完結できないあたり,コレジャナイ感もあります.メリットとしては,ローカルを介することがないため, Docker に閉じた世界でビルドが完結しローカル環境でもCI環境でも同様に扱うことが出来る点が挙げられるかと思います.
COPY --from=<image> <src> <dest> の構文自体あまり知られていないようにも思いますので,もしかしたら,既存のイメージから必要なパッケージのみを取り出すなどの用途に利用できるかも分かりません.
なお,本家では,LABEL を使う方法が提案されていたりします.しかし,この方法だとCI環境でのラベルのライフサイクルの管理がやや面倒です. multi-stage builds で適切にキャッシュを扱えるような機能がリリースされることを待っています.

5
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4