👌

Docker概要とDockerマルチステージビルドについて(Makefileでコマンドを作集約する)

2023/01/15に公開

まずdockerの基本概要の整理

DeamonとClientの関係

  • Docker Deamon
    コマンドの受付場所。命令をAPIで受け取り, Dockerの機能に命令を出す部分
    imageなどの情報を読み取り,コンテナを立ち上げる

  • Docker Client
    コマンドの受付場所でDocker Deamonとやりとりする

Docker Clientは複数PCに配置することも可能なので、Docker Deamonは複数箇所からAPIを受け付けることも可能。複数箇所からAPIを受け付けるには下記のコマンドでデーモンを起動する必要がある。
Docker Deamon側のコマンド

sudo dockerd -H tcp://0.0.0.0:2375

Docker Clentが遠隔地のDocker デーモンにコマンドを発行するには下記コマンドが必要
Docker Clinet

sudo docker -H tcp://<遠隔地のipアドレス>:2375 サブコマンド
例
sudo docker -H tcp://196.192.33.11:2375 images

マルチステージでDockerfileを作成してみる

今回はtestステージとリリースステージで作成してみます!

  • testステージ作成

Dockerfile

FROM alpine AS test
LABEL application=todobackend
RUN apk add --no-cache bash git
RUN apk add --no-cache gcc python3-dev py3-pip libffi-dev musl-dev linux-headers mariadb-dev
RUN pip3 install wheel -U
COPY /src/requirements* /build/
WORKDIR /build
RUN pip3 wheel -r requirements_test.txt --no-cache-dir --no-input
RUN pip3 install -r requirements_test.txt -f /build --no-index --no-cache-dir

FROMの部分でAS testとすることでtestステージを明示することができる
上記のDockerfileを参照し、imageを作成する

docker build -t test-image .

docker historyコマンドで作成されたレイアーを確認してみる

docker history test-image

そうすると下記のように記載される

MAGE          CREATED         CREATED BY                                      SIZE      COMMENT
b1129dc9cf9d   5 minutes ago   RUN /bin/sh -c apk add --no-cache bash git #…   15.2MB    buildkit.dockerfile.v0
b1cdaccrra   5 minutes ago   LABEL application=backend                       0B        buildkit.dockerfile.v0
b1cdsfewacd    5 months ago    /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
b1cdsfewacd    5 months ago    /bin/sh -c #(nop) ADD file:7fd90c097e2c4587d…   5.62MB  
  • 余談
    このコマンドでimageのレイアーが下から順番に作成されていき、レイアーを積んで行っていることがわかる
    もう一度dockerをbuildする際にこのレイアーがあれば使用する(キャッシュともいいう)のでdockerは高速でビルドが可能になる
    また、RUNを実行した後のコンテナなどに接続することもでき、コンテナのレイアーごとにアクセすできるのでトラブルシューティングに役に立ちます!

アクセス方法

docker run -if iamgeID /bin/bash

RUNとCMDの違い
RUNはimage作成前に実行されimageレイヤーに積まれていくが
CMDはimageが作成された後にそのimageに対してコマンドを実行する際に使用します!

CMDとENTORYPIONTの違い
CMDに定義したコマンドはdocker run -it image /bin/bashを実行するとCMDが上書きされてしまうので、上書きして欲しくなければENTORYPIONTに定義するとよい

image削除したい場合は書きを実行すると削除される

docker rmi -f `docker image -q`
  • 本番ステージの追加
FROM alpine AS test
LABEL application=todobackend
RUN apk add --no-cache bash git
RUN apk add --no-cache gcc python3-dev py3-pip libffi-dev musl-dev linux-headers mariadb-dev
RUN pip3 install wheel -U
COPY /src/requirements* /build/
WORKDIR /build
RUN pip3 wheel -r requirements_test.txt --no-cache-dir --no-input
RUN pip3 install -r requirements_test.txt -f /build --no-index --no-cache-dir

# ここから本番ステージ
FROM alpine
LABEL application=todobackend

RUN apk add --no-cache python3 py3-pip mariadb-client bash curl bats jq

# 本番用のユーザーは権限を絞るためにappグループを作成しappユーザーをそのグループに所属させている
RUN addgroup -g 1000 app && \
    adduser -u 1000 -G app -D app

# ここが特殊で--from=test --chown=app:app /build /buildを行うことで
# testステージのbuildを本番ステージのbuildにコピーを行い, そのファイルの権限をappユーザーのappグループに所属させるようにしている
COPY --from=test --chown=app:app /build /build
COPY --from=test --chown=app:app /app /app
RUN pip3 install -r /build/requirements.txt -f /build --no-index --no-cache-dir
RUN rm -rf /build
WORKDIR /app
USER app

buildを行う

docker build -t reletese .

ここでtestステージのみbuildを行いたい場合は下記のように指定する

docker build -t test --target test .

docker-compose ファイルを作成してみる

  • 雛形作成
version "2.4"

services:
  test:
    build:
      context: .
      dockerfile: Dockerfile
      target: test
  release:
    build:
      context: .
      dockerfile: Dockerfile
    depend_on:
      db:
        condition: service_healthy
  db:
    image: mysql:5.7
    healthcheck:
      test: mysqlshow -u $$MYSQL_USER -p $$MYSQL_PASSWORD
      interval: 3s
      retries: 10
    environment:
      MYSQL_DATABASE: test
      MYSQL_USER: test_user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: root_password

target: testをdcoker-compose ファイルの中に記載することでtestステージのDockerfileのみ起動可能となる

余談

depend_on:
      db:
        condition: service_healthy

上記でdbが正常に起動したらappを起動するようにしています。
service_healthy(サービス起動後health_checkが通れば)の他にもservice_stared(サービスが起動後に起動)
service completed_successfully(依存先のサービスが正常終了したら起動)
などがある

testステージのみコンテナを起動したい際は

dcoker-compose run test

extendsを使ってマイグレーションを行うサービスを作成する

version "2.4"

services:
  test:
    build:
      context: .
      dockerfile: Dockerfile
      target: test
  release:
    build:
      context: .
      dockerfile: Dockerfile
    depend_on:
      db:
        condition: service_healthy
  app:
    extend:
      service: release
    depend_on:
      condition: service_healthy
    port:
      - 8000:8000
    command:
      サーバー起動コマンド
   migrate:
    extend:
      service: release
    depend_on:
      condition: service_healthy
    command:
      マイグレーションコマンド
   
  db:
    image: mysql:5.7
    healthcheck:
      test: mysqlshow -u $$MYSQL_USER -p $$MYSQL_PASSWORD
      interval: 3s
      retries: 10
    environment:
      MYSQL_DATABASE: test
      MYSQL_USER: test_user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: root_password

上記のようにextendでreleaseコンテナを継承してサービスを作成することでアプリケーションを起動したい際はappサービスを起動, migrationを行いたい際はmigrateサービスを起動すればよくなる

docker-compose up migrate
docker-compose up app

今回extendsは同じファイルより読み込みを行ったが下記のようにすると、別ファイルから呼び出すことができる

app:
  extends:
    file: hogehoge.yml
    service: fuefue

Makefileを作成していく

一旦簡単なMakefileを作成してみます

.PHONY: test clean
test:
        @ echo "Hello world!"
        touch test.txt

clean:
        rm test.txt

下記で実行

make test
make clean

補足: @ echo "Hello world!"
@を使うとecho "Hello world!"の実行結果を表示するようになる
.PHONY: test cleanでmake testなどをしたときにtestファイルがあってもエラーが出ないようにしている
※スペースではなくタブなので注意

それでは今回のMakefileを作成してみる

.PHONY: test release clean

export APP_VERSION ?= $(shell git rev-parse --short HEAD)

test:
    docker-compose build --pull release
    docker-compose build
    docker-compose run test
    
release:
     docker-compose up --abort-on-container-exit migrate
 clean:
     docker-compose down -v
     docker image -q -f dangling=true -f label=application=backend | xargs -I ARGS docker rmi -f --no-pruse ARGS

--pullをつけることで常に最新のimageを取得するようにしていいる
--abort-on-container-exitでエラーが発生した場合そこでコンテナ起動を終了するオプション
docker image -q -f dangling=trueの -f dangling=trueでタグのついていないimageをfilterし、 -f label=application=backendでlabelにapplication=backendがついているもののみをfilterしている

Discussion