20201119のdockerに関する記事は10件です。

docker-compose コマンドでの args:, environment:, env_file: 及び .env ファイルの使い方

docker-compose コマンドでの args:, environment:, env_file: 及び .env ファイルの使い方

docker-compose v3 での、 args:, environment:, env_file: の使い方と .env ファイルの関係で迷子になることがあったので自分用に整理した。

まとめ

args: について

  • build 用変数だよ
  • なので、 run 時は存在してないよ
  • 同じ変数を宣言した場合 docker-compose.yml > Dockerfile だよ
  • docker-compose.yml で宣言すると Dockerfile 内で宣言したのと同等になるよ

environment: について

  • 環境変数を設定できるよ
  • run 時に docker-compose.yml で宣言した変数が追加・上書きされるよ
  • DockerfileENV 命令で宣言したものは、 build 時に ARG 命令の変数の様に使えてしまうので注意が必要だよ
  • かつ、 build 時は、上述の様に docker-compose.yml で宣言した変数は渡っていないので注意が必要だよ

env_file: について

  • ファイルから環境変数を追加する仕組みだよ
  • 同名の環境変数が存在する場合の優先度は environment: > env_file: だよ
  • 複数の env_file: を指定した場合は、リストの後者の値で追加・上書きされるよ

.env について

  • docker-compose が利用する特殊なファイルが .env だよ
  • 変数置換機能を便利にしてくれるファイルが .env だよ
  • (おそらく) .env というファイル名を別名にして利用することは出来ないよ
  • .env で宣言・定義した変数が docker-compose.yml 上で変数として利用できる仕組みだよ

※コードは https://github.com/hokutoasari/how-to-use-args-environment-env-file-dotenv-file-with-docker-compose に。

args: の使い方

まずは、公式ドキュメント。

https://docs.docker.com/compose/compose-file/#args

Add build arguments, which are environment variables accessible only during the build process.

意訳: ビルド用の変数を設定できます。

ビルド用変数を設定できる仕組み。

environment variables が後述の environment: で指定する、所謂 環境変数 と紛らわしいので、ここでは ビルド用変数 と読み替えた。

ARG_VALUE1 - ARG_VALUE4 までを以下のように宣言・値設定して、振る舞いを確認します。

Dockerfile docker-compose.yml
ARG_VALUE1 arg1_in_Dockerfile
ARG_VALUE2 arg2_in_Dockerfile arg2_in_yml
ARG_VALUE3 arg3_in_yml
ARG_VALUE4

./args/Dockerfile

FROM busybox

ARG ARG_VALUE1="arg1_in_Dockerfile."
ARG ARG_VALUE2="arg2_in_Dockerfile."

RUN echo "ARG_VALUE1 is ${ARG_VALUE1}" \
  && echo "ARG_VALUE2 is ${ARG_VALUE2}" \
  && echo "ARG_VALUE3 is ${ARG_VALUE3}" \
  && echo "ARG_VALUE4 is ${ARG_VALUE4}"
RUN echo "ARG_VALUE1 is $ARG_VALUE1" >> /tmp/outs.txt \
  && echo "ARG_VALUE2 is $ARG_VALUE2" >> /tmp/outs.txt \
  && echo "ARG_VALUE3 is $ARG_VALUE3" >> /tmp/outs.txt \
  && echo "ARG_VALUE4 is $ARG_VALUE4" >> /tmp/outs.txt

CMD cat /tmp/outs.txt \
  && echo "-----" \
  && echo "ARG_VALUE1 is ${ARG_VALUE1}" \
  && echo "ARG_VALUE2 is ${ARG_VALUE2}" \
  && echo "ARG_VALUE3 is ${ARG_VALUE3}" \
  && echo "ARG_VALUE4 is ${ARG_VALUE4}"

./args/docker-compose.yml

/args/docker-compose.yml
version: '3'
services:
  with-args:
    build:
      context: ./
      args:
        ARG_VALUE2: "arg2_in_yml"
        ARG_VALUE3: "arg3_in_yml"

実行。

% docker-compose build --no-cache && docker-compose up
...略...
Step 4/6 : RUN echo "ARG_VALUE1 is ${ARG_VALUE1}"   && echo "ARG_VALUE2 is ${ARG_VALUE2}"   && echo "ARG_VALUE3 is ${ARG_VALUE3}"   && echo "ARG_VALUE4 is ${ARG_VALUE4}"
 ---> Running in 64893f52d5bc
ARG_VALUE1 is arg1_in_Dockerfile.
ARG_VALUE2 is arg2_in_yml
ARG_VALUE3 is
ARG_VALUE4 is
Removing intermediate container 64893f52d5bc
 ---> a66e7626d5eb
...略...
[Warning] One or more build-args [ARG_VALUE3] were not consumed
...略...
with-args_1  | ARG_VALUE1 is arg1_in_Dockerfile.
with-args_1  | ARG_VALUE2 is arg2_in_yml
with-args_1  | ARG_VALUE3 is
with-args_1  | ARG_VALUE4 is
with-args_1  | -----
with-args_1  | ARG_VALUE1 is
with-args_1  | ARG_VALUE2 is
with-args_1  | ARG_VALUE3 is
with-args_1  | ARG_VALUE4 is
args_with-args_1 exited with code 0

結果。

Dockerfile docker-compose.yml build 時 run 時
ARG_VALUE1 arg1_in_Dockerfile arg1_in_Dockerfile
ARG_VALUE2 arg2_in_Dockerfile arg2_in_yml arg2_in_yml
ARG_VALUE3 arg3_in_yml [Warning]
ARG_VALUE4
  • docker-compose.ymlargs: で値は上書きされる
  • docker-compose.yml で宣言した args: で未使用の変数があると Warning となる
  • Dockerfile で利用しているが、 docker-compose.yml で宣言していない変数は警告などは無い
  • 当然ながら、 run 時には利用できない

environment: の使い方

まずは、公式ドキュメント。

https://docs.docker.com/compose/compose-file/#environment

Add environment variables.

環境変数を追加できる仕組み。

前述の ビルド用変数 とは違い、こちらは所謂 環境変数 を設定する為の命令。

ENV_VALUE1 - ENV_VALUE4 までを以下のように宣言・値設定して、振る舞いを確認。

Dockerfile docker-compose.yml
ENV_VALUE1 env1_in_Dockerfile
ENV_VALUE2 env2_in_Dockerfile env2_in_yml
ENV_VALUE3 env3_in_yml
ENV_VALUE4

./environment/Dockerfile

FROM busybox

ENV ENV_VALUE1="env1_in_Dockerfile."
ENV ENV_VALUE2="env2_in_Dockerfile."

RUN echo "ENV_VALUE1 is ${ENV_VALUE1}" \
  && echo "ENV_VALUE2 is ${ENV_VALUE2}" \
  && echo "ENV_VALUE3 is ${ENV_VALUE3}" \
  && echo "ENV_VALUE4 is ${ENV_VALUE4}" \
RUN echo "ENV_VALUE1 is $ENV_VALUE1" >> /tmp/outs.txt \
  && echo "ENV_VALUE2 is $ENV_VALUE2" >> /tmp/outs.txt \
  && echo "ENV_VALUE3 is $ENV_VALUE3" >> /tmp/outs.txt \
  && echo "ENV_VALUE4 is $ENV_VALUE4" >> /tmp/outs.txt

CMD cat /tmp/outs.txt \
  && echo "-----" \
  && echo "ENV_VALUE1 is ${ENV_VALUE1}" \
  && echo "ENV_VALUE2 is ${ENV_VALUE2}" \
  && echo "ENV_VALUE3 is ${ENV_VALUE3}" \
  && echo "ENV_VALUE4 is ${ENV_VALUE4}"

./environment/docker-compose.yml

/environment/docker-compose.yml
version: '3'
services:
  with-environment:
    environment:
      ENV_VALUE2: "env2_in_yml"
      ENV_VALUE3: "env3_in_yml"

実行。

% docker-compose build --no-cache && docker-compose up
...略...
Step 4/6 : RUN echo "ENV_VALUE1 is ${ENV_VALUE1}"   && echo "ENV_VALUE2 is ${ENV_VALUE2}"   && echo "ENV_VALUE3 is ${ENV_VALUE3}"   && echo "ENV_VALUE4 is ${ENV_VALUE4}"
 ---> Running in bb4ae383c1e7
ENV_VALUE1 is env1_in_Dockerfile.
ENV_VALUE2 is env2_in_Dockerfile.
ENV_VALUE3 is
ENV_VALUE4 is
Removing intermediate container bb4ae383c1e7
 ---> a01b51cd008a
...略...
with-environment_1  | ENV_VALUE1 is env1_in_Dockerfile.
with-environment_1  | ENV_VALUE2 is env2_in_Dockerfile.
with-environment_1  | ENV_VALUE3 is
with-environment_1  | ENV_VALUE4 is
with-environment_1  | -----
with-environment_1  | ENV_VALUE1 is env1_in_Dockerfile.
with-environment_1  | ENV_VALUE2 is env2_in_yml
with-environment_1  | ENV_VALUE3 is env3_in_yml
with-environment_1  | ENV_VALUE4 is
environment_with-environment_1 exited with code 0

結果。

Dockerfile docker-compose.yml build 時 run 時
ENV_VALUE1 env1_in_Dockerfile env1_in_Dockerfile env1_in_Dockerfile
ENV_VALUE2 env2_in_Dockerfile env2_in_yml env2_in_Dockerfile env2_in_yml
ENV_VALUE3 env3_in_yml env3_in_yml
ENV_VALUE4
  • build 時は、 Dockerfile 記載の値が使われる
  • run 時に、 docker-compose.yml に設定した値が渡され上書きされる
  • ENV 命令及び environment: あくまで、環境変数をセットするものであることを意識して使うこと
  • build 時に ENV 命令で用意した値を ビルド用変数 的に使えてしまうので、注意すること

env_file: の使い方

まずは、公式ドキュメント。

https://docs.docker.com/compose/compose-file/#env_file

Add environment variables from a file.

所謂 環境変数 を"ファイル"から追加する仕組み。

ENV_VALUE1 - ENV_VALUE5 までを以下のように宣言・値設定して、振る舞いを確認。

Dockerfile docker-compose.yml some_env.env
ENV_VALUE1 env1_in_Dockerfile
ENV_VALUE2 env2_in_Dockerfile env2_in_yml
ENV_VALUE3 env3_in_yml env3_in_file
ENV_VALUE4 env4_in_file
ENV_VALUE5

./env_file/Dockerfile

FROM busybox

ENV ENV_VALUE1="env1_in_Dockerfile."
ENV ENV_VALUE2="env2_in_Dockerfile."

RUN echo "ENV_VALUE1 is ${ENV_VALUE1}" \
  && echo "ENV_VALUE2 is ${ENV_VALUE2}" \
  && echo "ENV_VALUE3 is ${ENV_VALUE3}" \
  && echo "ENV_VALUE4 is ${ENV_VALUE4}" \
  && echo "ENV_VALUE5 is ${ENV_VALUE5}"
RUN echo "ENV_VALUE1 is $ENV_VALUE1" >> /tmp/outs.txt \
  && echo "ENV_VALUE2 is $ENV_VALUE2" >> /tmp/outs.txt \
  && echo "ENV_VALUE3 is $ENV_VALUE3" >> /tmp/outs.txt \
  && echo "ENV_VALUE4 is $ENV_VALUE4" >> /tmp/outs.txt \
  && echo "ENV_VALUE5 is $ENV_VALUE5" >> /tmp/outs.txt

CMD cat /tmp/outs.txt \
  && echo "-----" \
  && echo "ENV_VALUE1 is ${ENV_VALUE1}" \
  && echo "ENV_VALUE2 is ${ENV_VALUE2}" \
  && echo "ENV_VALUE3 is ${ENV_VALUE3}" \
  && echo "ENV_VALUE4 is ${ENV_VALUE4}" \
  && echo "ENV_VALUE5 is ${ENV_VALUE5}"

./env_file/docker-compose.yml

/env_file/docker-compose.yml
version: '3'
services:
  with-env_file:
    build:
      context: ./
    environment:
      ENV_VALUE2: "env2_in_yml"
      ENV_VALUE3: "env3_in_yml"
    env_file:
      - some_env.env

./env_file/some_env.env

/env_file/some_env.env
ENV_VALUE3="env3_in_file"
ENV_VALUE4="env4_in_file"

実行。

% docker-compose build --no-cache && docker-compose up
...略...
Step 4/6 : RUN echo "ENV_VALUE1 is ${ENV_VALUE1}"   && echo "ENV_VALUE2 is ${ENV_VALUE2}"   && echo "ENV_VALUE3 is ${ENV_VALUE3}"   && echo "ENV_VALUE4 is ${ENV_VALUE4}"   && echo "ENV_VALUE5 is ${ENV_VALUE5}"
 ---> Running in 5851a9b3aa91
ENV_VALUE1 is env1_in_Dockerfile.
ENV_VALUE2 is env2_in_Dockerfile.
ENV_VALUE3 is
ENV_VALUE4 is
ENV_VALUE5 is
Removing intermediate container 5851a9b3aa91
 ---> 39f56354d7cd
...略...
with-env_file_1  | ENV_VALUE1 is env1_in_Dockerfile.
with-env_file_1  | ENV_VALUE2 is env2_in_Dockerfile.
with-env_file_1  | ENV_VALUE3 is
with-env_file_1  | ENV_VALUE4 is
with-env_file_1  | ENV_VALUE5 is
with-env_file_1  | -----
with-env_file_1  | ENV_VALUE1 is env1_in_Dockerfile.
with-env_file_1  | ENV_VALUE2 is env2_in_yml
with-env_file_1  | ENV_VALUE3 is env3_in_yml
with-env_file_1  | ENV_VALUE4 is env4_in_file
with-env_file_1  | ENV_VALUE5 is
env_file_with-env_file_1 exited with code 0

結果。

Dockerfile docker-compose.yml some_env.env build 時 run 時
ENV_VALUE1 env1_in_Dockerfile env1_in_Dockerfile env1_in_Dockerfile
ENV_VALUE2 env2_in_Dockerfile env2_in_yml env2_in_Dockerfile env2_in_yml
ENV_VALUE3 env3_in_yml env3_in_file env3_in_yml
ENV_VALUE4 env4_in_file env4_in_file
ENV_VALUE5
  • env_file: で指定した値よりも environment: の値が優先される
  • env_file: で指定し、 docker-compose.yml で未設定の場合は、 env_file: で指定した値が利用される
  • なお、実験はしてないが、ドキュメントにも記載がある通り、 env_file: に複数ファイルを指定すると、リストの後者の値で追加・上書きされる

.env ファイルの使い方

.env ファイルは、 docker-compose の中で特別に扱われるファイル。

変数置換機能にて利用される。

変数置換機能

.env ファイルは、 docker-compose の変数置換機能にて利用される為、まずは変数置換機能を試す。

公式ドキュメント。 https://docs.docker.com/compose/compose-file/#variable-substitution

Your configuration options can contain environment variables. Compose uses the variable values from the shell environment in which docker-compose is run.

意訳: docker-compose を実行するシェルの環境変数を docker-compose の変数として利用する事ができます。

以下の内容で ./variable-substitution/docker-compose.yml を用意する。

/variable-substitution/docker-compose.yml
version: '3'
services:
  variable-substitution:
    image: busybox:${BUSYBOX_VERSION}

一時的に BUSYBOX_VERSION=latest という環境変数を設定しながら docker-compose up する。(build の必要が無いので、直接 up となる。)

% env BUSYBOX_VERSION="latest" docker-compose up
...略...
variable-substitution_variable-substitution_1 exited with code 0

無事に pull して up して終了した。

同様に、 BUSYBOX_VERSION=musl を環境変数に設定し、 docker-compose up する。

% env BUSYBOX_VERSION="musl" docker-compose up
...略...
variable-substitution_variable-substitution_1 exited with code 0

こちらも同様に無事に pull して up して終了した。

確認のため、 docker images で手元の image 一覧を確認する。

% docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
busybox             musl                8bce8d24824c        5 weeks ago         1.47MB
busybox             latest              f0b02e9d092d        5 weeks ago         1.23MB

確かに、 latest, musl TAG が打たれた image が存在することを確認した。

docker-compose には、このように、シェルを実行する環境の環境変数を docker-compose に渡す仕組みがある。

これが、変数置換機能の1つ。

.env ファイル

公式ドキュメントには、以下とある。

https://docs.docker.com/compose/compose-file/#variable-substitution

You can set default values for environment variables using a .env file, which Compose automatically looks for. Values set in the shell environment override those set in the .env file.

意訳: docker-compose は、 .env ファイルを見つけると、デフォルトの環境変数を設定します。また、シェルの環境変数から設定された値は、 .env ファイルで設定された値で上書きされます。

以下のように、 ./variable-substitution-dotenv/.env ファイルを用意する。

/variable-substitution-dotenv/.env
BUSYBOX_VERSION=latest

先程と同じ内容だが、 ./variable-substitution-dotenv/docker-compose.yml を用意する。

/variable-substitution-dotenv/docker-compose.yml
version: '3'
services:
  variable-substitution-dotenv:
    image: busybox:${BUSYBOX_VERSION}

この状態で、今度は一時的な環境変数を設定せずに up してみる。

※事前に作成済みのコンテナを docker rm から削除、同様に pull 済みの images も docker rmi で削除しておくと動作がわかりやすいかも知れない。

% docker-compose up
Creating network "variable-substitution-dotenv_default" with the default driver
Pulling variable-substitution-dotenv (busybox:latest)...
latest: Pulling from library/busybox
9758c28807f2: Pull complete
Digest: sha256:a9286defaba7b3a519d585ba0e37d0b2cbee74ebfe590960b0b1d6a5e97d1e1d
Status: Downloaded newer image for busybox:latest
Creating variable-substitution-dotenv_variable-substitution-dotenv_1 ... done
Attaching to variable-substitution-dotenv_variable-substitution-dotenv_1

無事に、 latest が pull されて、 up したことがわかる。

続けて、 ./variable-substitution-dotenv/.env を以下の内容に書き換える。

/variable-substitution-dotev/.env
BUSYBOX_VERSION=musl

この状態で、再度 docker-compose up してみる。

% docker-compose up
Pulling variable-substitution-dotenv (busybox:musl)...
musl: Pulling from library/busybox
7c3804618ebb: Pull complete
Digest: sha256:605de95bca536139f324abdecf88dcab492c8457346e7fc92e37dff6e263f341
Status: Downloaded newer image for busybox:musl
Recreating variable-substitution-dotenv_variable-substitution-dotenv_1 ... done
Attaching to variable-substitution-dotenv_variable-substitution-dotenv_1
variable-substitution-dotenv_variable-substitution-dotenv_1 exited with code 0

無事に、 musl が pull されて、 up したことがわかる。

このように、 .env ファイルを用いると、シェル実行環境の環境変数を汚染せずに、 docker-compose に変数を渡す事が可能となる。

ちなみに、公式ドキュメントには、以下の注意書きがある。

Note when using docker stack deploy
The .env file feature only works when you use the docker-compose up command and does not work with docker stack deploy.

意訳

ノート: docker stack deploy を使う方へ
.env ファイルを用いた本機能は、 docker-compose up コマンドのみ動作します。 docker stack deploy では、動かないよ。

docker stack deploy 利用時の注意書きなので、 only works when you use the docker-compose up command と書いてるのだと思うが、実際は docker-compose up コマンドだけではなく、 dockre-compose コマンドに対して機能する。

具体的には、 docker-compose builddocker-compose config コマンドでも機能する。

試しに、現在の状態で docker-compose config を実行すると下記となる。(docker-compose config は、最終的に実行される yml を確認する為のコマンド。)

% docker-compose config
services:
  variable-substitution:
    image: busybox:musl
version: '3'

${BUSYBOX_VERSION} 部分が変数展開されて、 busybox:musl となっているのがわかる。

続けて、 ./variable-substitution-dotenv/.env を以下の内容に書き換えて docker-compose config を実行すると以下となる。

/variable-substitution-dotenv/.env
BUSYBOX_VERSION=latest

% docker-compose config
services:
  variable-substitution:
    image: busybox:latest
version: '3'

同様に ${BUSYBOX_VERSION} 部分が変数展開されて、 busybox:latest となっているのがわかる。

.env ファイルを用いることで docker-compose に変数が渡せる事が確認できた。

.env ファイルを用いた変数置換機能で勘違いしやすい点と対処法

なお、当然ながら、以下の .env, Dockerfile 及び docker-compose.yml の構成は build 出来ない。

./variable-substitution-dotenv-dockerfile-not-work/.env

/variable-substitution-dotenv-dockerfile-not-work/.env
BUSYBOX_VERSION="latest"

./variable-substitution-dotenv-dockerfile-not-work/Dockerfile

FROM busybox:${BUSYBOX_VERSION}

./variable-substitution-dotenv-dockerfile-not-work/docker-compose.yml

/variable-substitution-dotenv-dockerfile-not-work/docker-compose.yml
version: '3'
services:
  variable-substitution-dotenv-dockerfile-not-work:
    build:
      context: ./

実行。

% docker-compose build --no-cache
Building variable-substitution-dotenv-dockerfile-not-work
Step 1/1 : FROM busybox:${BUSYBOX_VERSION}
ERROR: Service 'variable-substitution-dotenv-dockerfile-not-work' failed to build : invalid reference format

これまでの動作確認や公式ドキュメントを読めば当然なのだが、以下の状態となっている。

  • Dockerfile の冒頭で用いている busybox:${BUSYBOX_VERSION}BUSYBOX_VERSION 変数は build 用変数として Dockerfile として宣言されていない
  • .env ファイルを用いて docker-compose に変数を渡す仕組みは、当然ながら docker-compose までしか値が渡らない
  • つまり、 Dockerfile まで変数が渡っていない

build できるようにするには、以下の構成とする必要がある。

./variable-substitution-dotenv-dockerfile-work/.env

/variable-substitution-dotenv-dockerfile-work/.env
BUSYBOX_VERSION="latest"

./variable-substitution-dotenv-dockerfile-work/Dockerfile

ARG BUSYBOX_VERSION
FROM busybox:${BUSYBOX_VERSION}

./variable-substitution-dotenv-dockerfile-work/docker-compose.yml

/variable-substitution-dotenv-dockerfile-work/docker-compose.yml
version: '3'
services:
  variable-substitution-dotenv-dockerfile-work:
    build:
      context: ./
      args:
        BUSYBOX_VERSION: ${BUSYBOX_VERSION}

実行。

% docker-compose build --no-cache
Building variable-substitution-dotenv-dockerfile-work
Step 1/2 : ARG BUSYBOX_VERSION
Step 2/2 : FROM busybox:${BUSYBOX_VERSION}
latest: Pulling from library/busybox
9758c28807f2: Pull complete
Digest: sha256:a9286defaba7b3a519d585ba0e37d0b2cbee74ebfe590960b0b1d6a5e97d1e1d
Status: Downloaded newer image for busybox:latest
 ---> f0b02e9d092d

Successfully built f0b02e9d092d
Successfully tagged variable-substitution-dotenv-dockerfile-work_variable-substitution-dotenv-dockerfile-work:latest
  • Dockerfile にて ARG 命令を使い BUSYBOX_VERSION 変数を宣言
  • docker-compose.ymlargs: 節で BUSYBOX_VERSION 変数を宣言
  • また、 docker-compose.ymlargs: 節で BUSYBOX_VERSION 変数宣言に対して、 .envBUSYBOX_VERSION 変数を割り当て
  • .env ファイルにて、 BUSYBOX_VERSION 変数の値を設定

これで、 .env の中で定義した BUSYBOX_VERSION の値を変更することで、シェル実行環境の環境変数を汚染せずに値を変更できるようになる。

ただ、上記の例示だと、 BUSYBOX_VERSION という変数名が各所(.env, Dockerfile 及び docker-compose.yml)で用いられていて、少々わかりにくい。

もう少しわかりやすく明示的に書くと、 .env, Dockerfile 及び docker-compose.yml は以下となる。

./variable-substitution-dotenv-dockerfile-work-easy-to-read/.env

/variable-substitution-dotenv-dockerfile-work-easy-to-read/.env
DOCKER_COMPOSER_BUSYBOX_VERSION="latest"

./variable-substitution-dotenv-dockerfile-work-easy-to-read/Dockerfile

ARG BUSYBOX_VERSION="latest"
FROM busybox:${BUSYBOX_VERSION}

./variable-substitution-dotenv-dockerfile-work-easy-to-read/docker-compose.yml

/variable-substitution-dotenv-dockerfile-work-easy-to-read/docker-compose.yml
version: '3'
services:
  variable-substitution-dotenv-dockerfile-work-easy-to-read:
    build:
      context: ./
      args:
        BUSYBOX_VERSION: ${DOCKER_COMPOSER_BUSYBOX_VERSION}

  • Dockerfile にて ARG 命令を使い BUSYBOX_VERSION 変数を宣言し、かつ、 docker-compose から値が渡されない場合はデフォルト値として latest を用いるように宣言
  • docker-compose.ymlargs: 節で BUSYBOX_VERSION 変数を宣言
  • また、 docker-compose.ymlargs: 節で BUSYBOX_VERSION 変数宣言に対して、 .envDOCKER_COMPOSER_BUSYBOX_VERSION 変数を割り当て
  • .env ファイルにて、 DOCKER_COMPOSER_BUSYBOX_VERSION 変数の値を設定

"変数"や"環境変数"という単語が多くなりややこしいので、具体的な変数名を見ると、何がどこに変数・値として受け継がれていくのかが分かると思う。

名称(ラベル)がややこしいだけで、 .env -> docker-compose.yml -> Dockerfile と、変数名と値を受け渡していく仕組みが分かる。

.env ファイルを用いた変数置換機能に関しては、(この表現が正しいか怪しいが...) .env ファイルを用いた変数置換機能は docker-compose.yml に変数と値を渡す仕組み と捉えるとわかりやすいかも知れない。

ちょっとした余談

実は、先に例示した以下の書き方はあまり推奨しません。

ARG BUSYBOX_VERSION="latest"
FROM busybox:${BUSYBOX_VERSION}

例えば、 github actions (等)の制約で、以下のようなケースが、ままあります。

Dockerfileファイル中の最初の命令はFROMでなければなりません。

https://docs.github.com/ja/free-pro-team@latest/actions/creating-actions/dockerfile-support-for-github-actions#from

そもそも FROM の TAG 値は、(開発|実行)環境の統一性を考えると "latest" すら使わずに、確実な値を使う方が望ましいと考えています。

もちろん、この限りじゃないケースもあると思いますが、 build のタイミングによっては不本意なメジャーバージョンアップが行われてしまうなどの問題があります。

活用例

例えば、 docker-compose build する際に、ローカル、ステージング、プロダクションなどの環境によって yarn run {script} の実行を制御するなどが実現できます。

ローカル、ステージング、プロダクション用のビルド環境が各々別で存在するとし、かつ、それぞれに .env ファイルも存在するとします。

ローカル用 .env

#...略...
DOCKER_COMPOSE_BUILD_TYPE="dev"
#...略...

ステージング用 .env

#...略...
DOCKER_COMPOSE_BUILD_TYPE="dev"
#...略...

プロダクション用 .env

#...略...
DOCKER_COMPOSE_BUILD_TYPE="prod"
#...略...

また、これらの .env は gitignore されており、リポジトリで管理されていないとします。

この状態で、 Dockerfiledocker-compose.yml を以下のように用意しておくことで、単一ソースコードを保ちながら、環境ごとの制御が可能となります。

Dockerfile

#...略...
ARG BUILD_TYPE="local"
#...略...
RUN yarn run ${BUILD_TYPE}
#...略...

docker-compose.yml

docker-compose.yml
#...略...
    build:
#...略...
      args:
        BUILD_TYPE: ${DOCKER_COMPOSE_BUILD_TYPE}
#...略...

他にも、 environment: を使って、各環境ごとのログの向き先やメールドライバの変更、 DB ドライバの変更なども可能です。

php を使った開発なら xdebug 有効無能の設定や、各種設定値を各個人の .env 経由で自由に変更可能とするなども実現できます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerについて

なぜ使うか

環境をキレイに保つため. 

Dockerがないと,ローカル環境でpython3.7やらpython3.8やら混在したり,もうごっちゃごっちゃ.カオス.
また,開発環境と本番環境を合わせられるのも大きい.デプロイの度に環境が違うせいでエラーが生じることは多々あるが,これを防ぐことができる.
Virtual BoxよりもDockerはかなり高速で立ち上がるので,Dockerが良い.
自分はDockerを使い始めてから,かなり開発時の健康状態が良くなりました.整理された机で勉強した方が気持ちがイイのと同じ感覚.

Docker 基本編

本番環境を意識せずに,sandbox内で色々弄るだけならこれで十分ではという内容.最初はこれだけでよいと思う.慣れてきたら応用編へと進みたい.

# ベースとなるイメージを引っ張ってくる.大本の環境を構築する感じ.この上にパッケージなどを乗せていく.
$ docker pull イメージ名

# anaconda -> pandasなど大体機械学習に必要なパッケージがインストール済みだが重い.本番環境では軽さが大事だが,ローカル開発ではこれでいいと思う.
$ docker pull continuumio/anaconda3

# -itコマンドでDockerを起動し続ける.ないと一度起動するだけで切れてしまい,その中で作業できない.また末尾に/bin/bashを付けることで,バッシュの中で作業できる.コマンドラインを使えるイメージ.ないとpythonの対話モードになる.
$ docker run -it continuumio/anaconda3 /bin/bash

# コンテナ内でパッケージをインストールするなどの変更を行ったら,コンテナを抜けた後,その変更を保存しておく.しないと,初期の環境のままになる.
# 起動したコンテナの履歴
$ docker ps -a
# コンテナをイメージとして保存する
$ docker commit container_id container_name

# 保存したイメージの一覧を確認する
$ docker images

# 保存したコンテナを起動する
$ docker run -it container_name /bin/bash

# ローカルディレクトリをマウント(Dockerと共有)する. -vの後に「ローカルディレクトリ:Dockerディレクトリ」を記述.
docker run -it -v /c/Users/user/Python:/home container_name /bin/bash

Docker 応用編

 少し難しいかもしれないが,慣れるとより快適にかつ効率的に開発できる.自分はここで時間がかかった記憶がある.

基本的な利用の流れは以下.

  1. Dockerfileとrequirements.txtを作成する
  2. VSCodeに拡張機能Remote-Containersを入れる
  3. VSCode左下コードボタンからre-open in containerを実行&Dockerfileを実行
  4. Docker環境下でインストールしたパッケージを, requirements.txtに追加で記述
  5. 次回,再びビルド.

ここでは,Dockerfile単体・docker-compose利用本・番環境用Dockerfileの3つに分けて話したい.
最終的には,docker-composeを利用しての開発に慣れていきたい.また,本番環境用のDockerfileまで作れるようになると良いだろう.

1. Dockerfile単体

# 上部に記述したものほどキャッシュを利用しやすくなっているので,よく変更を加えるものは下の方に記述しよう.
# python-buster -> pythonを動かすための最小限のパッケージ.OSはdebian. 自分はこれで開発している.
FROM python:3.8-buster

# パスは絶対パスを使用する.ローカルとコンテナでディレクトリ構造が異なるから.
WORKDIR /app

# パッケージマネージャの更新とパッケージの取得はまとめる. aptの方がapt-getよりも良い.
RUN apt update -y && apt upgrade -y && \
    apt install -y ffmpeg

# ADDよりCOPYの方が良い.appディレクトリにファイルをコピー.ローカルからコンテナへとファイルをコピーしている.
COPY requirements.txt /app
# requirements.txtを参照してpip install. python3ではpip3を利用したい.
RUN pip3 install --upgrade pip && \
    pip3 install -r requirements.txt

# 指定したディレクトリを各環境で共有できる.
# VOLUME /data

# コピーするディレクトリは明示的に指定.変更部のみ反映させるため.自分は面倒なので「.」で済ませてしまっている.
# COPY src /app/src
# COPY data /app/data
COPY . /app

# CMD ["/bin/bash"]

2. Docker-Compose利用

Docker-composeを利用することで,複数のサーバーを同時に使うことができる.例えば,python実行用だけでなく,postgresなどデータベースやjupyterなど.資源を共有できるのも良い.
コード例は以下.
docker-compose.yml
version: '3'
services:
notebook:
# image: jupyter/datascience-notebook -> Dockerfileでベースイメージ記述しているから不要.
# container_nameを指定しないと,自動で生成されて後で確認しづらくなる.
container_name: vad-sandbox
ports:
- "8888:8888"
# 「.」で同じディレクトリにあるDockerfileを実行してくれる.
build: .
# マウントするディレクトリを指定.
volumes:
- .:/app
working_dir: /app
# 自動でコマンドを実行してくれる.僕は自分で打った方が分かりやすいので自分で打っている.
# command: jupyter lab --ip=0.0.0.0 --allow-root

 そして,こちらがdocker-compose利用時のdockerfile

FROM python:3.8-buster

WORKDIR /app

RUN apt update -y && apt upgrade -y && \
    apt install -y ffmpeg

COPY requirements.txt /app

RUN pip3 install --upgrade pip && \
    pip3 install -r requirements.txt

# ipywidgetsを利用. jupyterをインタラクティブに使える.
RUN jupyter nbextension enable --py widgetsnbextension --sys-prefix

COPY . /app

# CMD ["/bin/bash"]

3. Docker本番環境用

本番環境では極力容量を削減することが肝要である.そのために,マルチステージビルドを採用する(ビルドと実行で環境を分ける).

# pythonではalpineよりbusterの方が良い.slimではc拡張が使えない
# マルチステージビルドでビルドと実行でイメージを分け,極力容量を小さくする
# キャッシュを利用したいものほど上部に記述.変更があるとそれ以降破棄されるため.

# ビルド用.busterはgccが利用できる.webrtcvadのインストールに必要.
FROM python:3.8-buster as builder

# パスは絶対パスを使用する
WORKDIR /app

# pytorchはインストールに時間がかかるため,キャッシュを再利用しやすく.
RUN pip3 install --upgrade pip && \
    pip3 install torch

# ADDよりCOPYの方が良い.appディレクトリにファイルをコピー.
COPY requirements.txt /app
RUN pip3 install --upgrade pip && \
    pip3 install -r requirements.txt

#実行用コンテナ
FROM python:3.8-slim-buster as runner

# インストールしたpythonパッケージを実行用へとコピー. 1GBほど削減できた.
COPY --from=builder /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages

# パッケージマネージャの更新とパッケージの取得はまとめる. aptの方がapt-getよりも良い.
RUN apt update -y && apt upgrade -y && \
    apt install -y ffmpeg

COPY . /app

最後に

 抽象度が高いため,初見では戸惑うこと間違いなしだが,慣れれば単なる作業と化す.最初はストレスを感じるかもしれないが,がんばろう.就活でもアピれるはずだ.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

公式Dockerとvscodeを使ったKaggleの環境構築

記事の内容

この記事はkaggleをローカル環境で行うための環境構築についての備忘録です。
ネットではgcpの記事が多いため、とりあえずローカルで試してみたい方に向けたものになります。

この記事が参考になる方

  • kaggleをローカル環境で行いたい方
  • vscodeを用いて快適な環境を作りたい方

なおこの記事ではvscodeのremote containerを利用します。
gcpで行いたい方は他の記事を参考にしてください。

なお自身の環境はUbuntuの20.4です。
他環境でうまく動作するかは保証できませんが、マウントのやり方さえ気をつければ問題なくできるはずです。
windowsの場合はwslを入れておいてください。
参考:(https://qiita.com/matarillo/items/61a9ead4bfe2868a0b86)

手順

  1. まずはvscodeをダウンロード
  2. この記事を参考にgit cloneし、buildします。
  3. vscodeのextentionからremote containerをインストールします。
  4. remote containerからopen folder in containerを選択し、Dockerを選択します。
  5. kaggle.jsonを~/.kaggle/に置きます。
  6. .devcontainer/devcontainer.jsonのマウント先を編集します。必要であればextentionsも編集します。

これで快適な環境が出来上がります。順に解説します。

1. まずはvscodeをダウンロード

公式から各々のOSにあったものをダウンロードしてください。

2. この記事を参考にgit cloneし、buildします。

記事の通りにbuildします。
1時間以上かかるので気長に待ってください。
gpuを使用しない場合は--gpuを変更してください。

3. vscodeのextentionからremote containerをインストールします。

以下の画像のものをインストールしてください。
Screenshot from 2020-11-19 15-38-05.png

4. remote containerからopen folder in containerを選択し、Dockerを選択します。

Remote Explorer(左のディスプレイのようなマーク)を選択し、CONTAINERの横にカーソルを持っていくと+マークが出ます。new containerをクリックするとopen folder in containerが選択でき、先程git cloneしたdockerフォルダーを選び、Dockerファイルを選択します。
Screenshot from 2020-11-19 15-41-41.png
左下の緑色のマークを選択しても同じように開くことができます。

5. kaggle.jsonを~/.kaggle/に置きます。

kaggle公式のprofileからedit profileを選択し、create New API Tokenを選択してkaggle.jsonをダウンロードします。
terminalからkaggleコマンドを使用している場合は~/.kaggleが作成されているため、cpコマンドなどを使用して~/.kaggleにkaggle.jsonを起きます。もし実行していない場合は自分でディレクトリを作っても問題ないです。

このままkaggleコマンドを打つとwarningが出ます。
kaggle.jsonの権限を変更する必要があります。
以下のコマンドを入力してください。
chmod 600 ~/.kaggle/kaggle.json

6. .devcontainer/devcontainer.jsonのマウント先を編集します。必要であればextentionsも編集します。

この時点ではdockerを動かすことはできますが、docker-python下以外のローカルディレクトリにアクセスすることができません。
docker-pythonの中を作業ディレクトリにする方はいいですが、僕はkaggle用の作業ディレクトリを用意しているため、そこにアクセスできるようにします。

.devcontainer/devcontainer.jsonに以下のコードを各々の作業ディレクトリにあった場所に変更して記述してください。

"mounts": [ "source=/home/artela/works/kaggle,target=/root/kaggle/,type=bind" ,
                "source=/home/artela/.kaggle/,target=/root/.kaggle/,type=bind"],

sourceがローカルディレクトリを指し、targetにはアクセス方法を記述します。
例えば下記のようにコマンドを叩くと/home/artela/works/kaggleにアクセスできます。lsの結果が空なのは環境構築をやり直す際にすべて消えてしまったためであり、なにかおいてある場合はlsで表示されるはずです。
Screenshot from 2020-11-19 16-01-46.png

おすすめはしませんがsource=/home/user/,target=/root/を追加しておけば、すべてのディレクトリにアクセスできるようになります。

最後に

自身ができただけであるため、動作を保証するものではありません。なにかわかりにくい点や、間違えている点などがありましたらご指摘いただけると助かります。

最後にtwitterアカウントを貼っておきます。
よければフォローお願いします。
https://twitter.com/Artela_ML

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby がない環境で Rails プロジェクトを一撃で作る Docker コマンド

Dockerはないとダメです。

docker run --rm -it --workdir /app/ --volume $PWD:/app/  ruby:2.6.3 bash -c "gem install rails -v 5.2.4 && rails new sample_project"

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

padrino+PostgreSQLで作ったサイトを高速全文検索対応にする

概要

Dockerコンテナ上で動かしているpadrino+postgresqlなサービスに全文検索機能をつけたいとおもった。
ここではpgroongaを用いる。
タイトルに高速といれたのは、同種の機能拡張pg_bigmより早いみたいなので、groongaを選定したことによる。
が、どちらかというと手軽さをとった。

準備

Dockerfileで指定しているpostgresイメージを、pgroongaに変更した。
PostgreSQL向けにpgroongaというgroonga用エクステンションがあることは知っていたが、pgroongaイメージはPostgreSQLを内包しており、差し替えるだけで全文検索対応したPostgreSQLとして利用できる。

docker/pgroonga/Dockerfile
FROM groonga/pgroonga:2.2.6-debian-12
#FROM postgres:12

docker-compose.yamlはdb部分だけは↓みたいな感じ。Windows上でつかっているので、別途 docker volume create --name pgdata1している。

docker-compose.yml
version: '2'

services:
  db:
    build: docker/pgroonga
    container_name: pgoongadb
    ports:
      - "15432:5432"
    environment:
      - POSTGRES_USER=sa
      - POSTGRES_PASSWORD=sa
      - POSTGRES_DB=pgdb
    volumes:
      - pgdata1:/var/lib/postgresql/data
volumes:
  pgdata1:
    external: true

みてのとおり、Dockerfileを準備するまでもなく postgresをgroonga/pgroongaにしただけである。
(あとでmecab-ipadic-neologdを使うつもりでDockerfileを分けている)

こまったこと

データベースは上記で準備できた。
しかし、肝心の全文検索むけインデックスをpadrinoでいい感じに作成する方法がわからなかった。
ActiveRecordのadd_indexではチュートリアルにあるようなwith~的な記述が出来ない。
事例を検索してみたが見つからない。ActiveGroongaというのもあったが、それはちょっと違う。

groongaのコミュニティに相談したところ、須藤さんよりredmineでの事例を教えていただく。
またpadrinoでもこうすれば動くよ、というアドバイスもいただき、試した。

解決編

padrinoのプロジェクト内にある、libフォルダに下記のようなスクリプトを配置。

lib/migrate_plugin_add_index.rb
module Migration
  module PostgreSQLAdapterOptionable
    def add_index_options(table_name, column_name, with: nil, **options)
      result = super(table_name, column_name, **options)
      result[3] += " WITH (#{with})" if with
      result
    end
  end
  ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(PostgreSQLAdapterOptionable)
end

上記プラグインを追加した状態で、padrino g migration add_fulltext_indexとかなんとかマイグレーションを作成。

005_add_fulltext_index.rb
class AddFulltextIndex < ActiveRecord::Migration[5.2]
  def self.up
    add_index(:contacts, :name, using: 'pgroonga', with: "tokenizer='TokenMecab(\"use_reading\", true)'")
    add_index(:contacts, :description, using: 'pgroonga', with: "tokenizer='TokenMecab'")
    add_index(:contacts, :tel, using: 'pgroonga', with: "normalizer='NormalizerNFKC100(\"unify_hyphen_and_prolonged_sound_mark\", true)',tokenizer='TokenNgram(\"loose_symbol\", true,\"loose_blank\", true)'")
  end

  def self.down
  end
end

rake db:migrateすると無事、以下のようなSQLが発行され、create indexされた。

 add_index(:contacts, :name, {:using=>"pgroonga", :with=>"tokenizer = 'TokenMecab(\"use_reading\", true)'"})
DEBUG - (47.6ms) CREATE INDEX "index_contacts_on_name" ON "contacts" USING pgroonga ("name") WITH (tokenizer = 'TokenMecab("use_reading", true)')

試しにテストデータをつっこんでDataGripのクエリコンソールで、「東京都生まれヒップホップ育ち」みたいなデータを持つカラムに対し検索を実行。正しくおもったとおり動作。

select * from contacts where description &@ '京都' -- > なし
select * from contacts where description &@ '東京' -- > 検索結果 ○

まとめ

  • Docker上で動かしているなら、イメージをpostgresからpgroongaにするだけで全文検索対応にできる
  • プラグイン用ライブラリを1ファイル置いておけばpadrinoでもadd_indexするmigrationでインデックス作成できる

調べてわからなかったらコミュニティに聞いてみるのも手。場当たり対応でなくrails(ActiveRecord)側にもリクエストしておいたほうがいいかもという話をもらって、添削してもらいつつでissueも出してみた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでWordpressのローカル環境構築&開発手順

Wordpressのローカル環境を構築したい時の選択肢

・MAMP
・XAMMP
・Local by flywheel
・Docker
おそらく一番簡単なのは3つめのLocal by flywheelだと思います。
私の場合は前者3つが手持ちのPC(MacOS13)で使えなかったので、Dockerでできる方法を調べました。

Dockerによるローカル環境を構築

(参考: 初心者|Docker-ComposeでWordPressとMySQLとphpMyAdminのローカル環境の構築 )
Dockerとは...??って感じの人はかめさんのDocker超入門①〜Dockerってなに?〜【初心者向け】でまず勉強するといいと思います。

プロジェクトファイルの中にymlファイルを作成

terminal.
$ touch docker-compose.yml

ymlファイルに以下をコピペ

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.7
    #container_name: "mysql57"
    volumes:
      - ./db/mysql:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root_pass_fB3uWvTS
      MYSQL_DATABASE: wordpress_db
      MYSQL_USER: user
      MYSQL_PASSWORD: user_pass_Ck6uTvrQ

  wordpress:
    image: wordpress:latest
    #container_name: "wordpress"
    volumes:
      - ./wordpress/html:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/conf.d/php.ini
    restart: always
    depends_on:
      - db
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wordpress_db
      WORDPRESS_DB_USER: user
      WORDPRESS_DB_PASSWORD: user_pass_Ck6uTvrQ

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    #container_name: "phpmyadmin"
    restart: always
    depends_on:
      - db
    ports:
      - 8888:80

docker-composeをアップ

terminal.
$ docker-compose up -d

http://localhost:8080/ にアクセスしてみて、wordpressの管理画面が表示されてればok。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでWordpress

Wordpressのローカル環境を構築したい時の選択肢

・MAMP
・XAMMP
・Local by flywheel
・Docker
おそらく一番簡単なのは3つめのLocal by flywheelだと思います。
私の場合は前者3つが手持ちのPC(MacOS13)で使えなかったので、Dockerでできる方法を調べました。

Dockerによるローカル環境を構築

(参考: 初心者|Docker-ComposeでWordPressとMySQLとphpMyAdminのローカル環境の構築 )
Dockerとは...??って感じの人はかめさんのDocker超入門①〜Dockerってなに?〜【初心者向け】でまず勉強するといいと思います。

プロジェクトファイルの中にymlファイルを作成

terminal.
$ touch docker-compose.yml

ymlファイルに以下をコピペ

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.7
    #container_name: "mysql57"
    volumes:
      - ./db/mysql:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root_pass_fB3uWvTS
      MYSQL_DATABASE: wordpress_db
      MYSQL_USER: user
      MYSQL_PASSWORD: user_pass_Ck6uTvrQ

  wordpress:
    image: wordpress:latest
    #container_name: "wordpress"
    volumes:
      - ./wordpress/html:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/conf.d/php.ini
    restart: always
    depends_on:
      - db
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wordpress_db
      WORDPRESS_DB_USER: user
      WORDPRESS_DB_PASSWORD: user_pass_Ck6uTvrQ

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    #container_name: "phpmyadmin"
    restart: always
    depends_on:
      - db
    ports:
      - 8888:80

docker-composeをアップ

terminal.
$ docker-compose up -d

http://localhost:8080/ にアクセスしてみて、wordpressの管理画面が表示されてればok。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでPythonにてGDALを利用する

概要

geotiffを利用する際に、少しハマったので、備忘録。

結論から言うと、Pyhonにおける「GDAL」ライブラリと「libgdal-dev」のバージョン対応が間違っていたことが原因でした。
apt標準リポジトリにおいて管理される安定版「libgdal-dev」はv2.4.0です(2020年11月19日現在)。これにより、pipにて単純に「pip install GDAL」とするとv3.2.0がダウンロードされますが、v3.2.0に対応する「libgdal-dev」はv3.2.0以上必要です。そのため、インストールする際は「pip install GDAL==2.4.4」とし、バージョン指定する必要があります。
この辺りを踏まえたdockerfileなどを以下に記載します。

前提条件

ここでは、以下のことを考えています。

  • dockerを利用し、環境を使いまわせるようにする。
  • あまりanacondaは好きじゃない
  • Pythonを利用するのは、学習コストが低いため
  • docker-composeはあくまで動作テスト用に記載
  • コード変更がきくようにボリュームを設定
  • 特にこだわらないので、ベースイメージは「python:3.8」(多分OSはDebian)

構造

/
|-/app
| |-/image # tifファイルを入れる
| |-test.py # テストコード
| '-Dockerfile
'-docker-compose.yaml 

Dockerfile/docker-compose.yaml

Dockerfile
FROM python:3.8

ENV APP_HOME /app
WORKDIR $APP_HOME

RUN apt-get update && apt-get install -y tzdata \
    libgdal-dev

RUN pip install --upgrade pip
RUN pip install gdal==2.4.4

CMD ["python", "/app/test.py"]

docker-compose.yaml
version: "2"
services:
  test:
    container_name: "test"
    build:
      context: ./app
    volumes:
      - ./app:/app

テスト

テストコードは以下の通り。

test.py
from osgeo import gdal, gdalconst

if __name__ == "__main__":
    print("gdal version", gdal.VersionInfo())
    file_name = '/app/image/1_index_ndvi.tif'
    src = gdal.Open(file_name,
                    gdalconst.GA_ReadOnly)  # tifの読み込み (read only)
    print(type(src))  # "osgeo.gdal.Dataset"

実行は、docker-composeを使用しているので、以下の通り。

docker-compose up

おわりに

結構こういうことってよくありますね。ライブラリの最新バージョンを利用する場合は、debianだとパッケージがなかったりするので、ubuntuで環境構築する必要があったりしますし(ubuntuだとリポジトリ登録すればtestバージョン利用できることが多いイメージ)。基本的にある程度データが読める状態にさえなってくれれば、あとは自分で数値処理した方が早いので、stable版で十分だと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

awesome-composeでローカル開発環境をお手軽にセットアップする

この記事は

「awesome-compose」をつかってローカル環境に開発環境を作る方法です。

自己紹介

Java(Spring Boot)のWebアプリ開発を主にやってます。

きっかけ

ローカル環境がこわれた

ローカルでSpring Bootの開発でごちゃごちゃやっていたらGradleが壊れてしまいました。
gradle install実行時に、こんなエラー(↓)がでました。

FAILURE: Build failed with an exception.

* What went wrong:
Task 'install' not found in root project 'frontend'.
# 以下略

ググってもAndroid系の記事ばかり出てきてなんかよくわからなかったです。
もっと調べろよ!と言われればそれまでですが、いい機会なのでDockerできれいな環境を作りたくなりました。

ときには別の道に逃げましょう。

docker-composeちゃんと書くの大変

ネットでググりつつ自分の環境に合わせて書くわけですが、Dockerの基礎力が弱いので、つまづいたときになかなか先に進めません。リファレンス見ても直感的に理解するまでの道は遠いです。

真似ながら学んでいきたい性質(たち)なので、質の良いサンプルがあると助かります。

awesome-composeとは

Docker公式のdocker-composeのサンプル集です。以下にあります。
https://github.com/docker/awesome-compose

README.mdには以下のように書いています。
(以下一部引用。Google翻訳)

These samples provide a starting point for how to integrate different services using a Compose file and to manage their deployment with Docker Compose.
これらのサンプルは、Composeファイルを使用してさまざまなサービスを統合し、DockerComposeを使用してそれらのデプロイメントを管理する方法の開始点を提供します。

サンプルとしてまずは動かしてみて、自分の環境にあうようにカスタマイズしていくイメージでしょうか。

The following samples are intended for use in local development environments such as project setups, tinkering with software stacks, etc. These samples must not be deployed in production environments.
次のサンプルは、プロジェクトセットアップ、ソフトウェアスタックの調整など、ローカル開発環境での使用を目的としています。これらのサンプルは、実稼働環境にデプロイしないでください。

使い所としてはローカル開発環境であることが明記されてます。じっさいのところ、本番環境ではクラウドのマネージドサービスを使うなど選択肢がいろいろあるかと思います。

awesome-composeの特徴

  • ご覧いただくとわかるように、さまざまなコンテナの組み合わせがサンプルとして用意されてます。探せば自分がほしいものが見つかるのではないでしょうか。
  • docker-compose.ymlだけでなく、アプリのサンプルもついてきます。アプリも含めてDocker環境をいちからつくるのは結構骨が折れるので、そこはありがたいですね。

動かしてみる

1. Docker環境のインストール

詳細は割愛します。Mac版は以下にあります。
Docker Desktop for Mac
https://hub.docker.com/editions/community/docker-ce-desktop-mac/

2. awesome-composeの入手

先に紹介したGitHubのサイトからクローンします。

git clone https://github.com/docker/awesome-compose.git

3. コンテナの起動

SpringとPostgreSQLのコンテナを起動する「spring-postgres」を使ってみます。
プロジェクトルートからspring-postgresディレクトリを移動し、

cd spring-postgres

docker-composeコマンドで起動します。

docker-compose up -d

4. 動作確認

ブラウザでlocalhost:8080にアクセスします。
helloworld.png

初期ページが表示されれば起動成功です。

詳細は省きますが、「Hello from Docker」の「Docker」は、PostgreSQLコンテナ起動時にINSERTされたレコードをSpringから参照、画面表示したものです。

解説(docker-compose.yaml)

とりあえず動けばいいや、でよければこれ以上読まなくても大丈夫です。
以降は、docker-compose.yamlの雑な解説をするので、興味があればお読みください。

backendサービス(Spring)の設定

version: "3.7"
services:
  backend:
    build: backend  # (1)
    ports:
      - 8080:8080
    environment:
      - POSTGRES_DB=example  # (2)
    networks:
      - spring-postgres
  • (1) backendサービスのイメージ作成は、buildに指定したディレクトリ名backendにあるDockerfileでおこないます。
  • (2) environmentはコンテナ内で使用する環境変数を設定します。POSTGTES_DB=exampleは、SpringからPostgreSQLへ接続するときの接続文字列で使用するDB名です。backend/src/main/resources/application.properties内で参照しています。

dbサービス(PostgreSQL)の設定

  db:
    image: postgres
    restart: always
    secrets:
      - db-password  # (3)
    volumes:
      - db-data:/var/lib/postgresql/data  # (4)
    networks:
      - spring-postgres
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password  # (5)
volumes:
  db-data:  # (6)
secrets:
  db-password:
    file: db/password.txt  # (7)
networks:
  spring-postgres:
  • (3) 機密情報(パスワード)を指定します。
  • (4) コンテナ内のPostgreSQLのデータディレクトリをホストマシンのディレクトリにマウントします。
  • (5) 機密情報(パスワード)が記載されたファイルのパスを環境変数に設定します。
  • (6) (4)にたいしてマウントするホスト側のディレクトリを指定します。ここでは記載がないので、永続化はされないようですね。
  • (7) コンテナ内のPostgreSQLで使用するパスワードファイルのパスを設定します。デフォルトでは、ここで指定したファイルはコンテナの/run/secretsディレクトリにマウントされるらしい。

最後に

とりあえず動かすだけはできました。
次はカスタマイズして自分なりの環境を作りたいです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Docker入門]機械学習用のDockerイメージを作成してJupyter notebookを使用する

この記事について

機械学習の学習環境をDockerに移行したので、Dockerの使い方を忘れたときのための備忘録用記事です。

環境

基本Mac前提なのでWindowsの場合のコマンドなどは書いていないです。

  • MacOS Mojave

GitHub

この記事で紹介しているDockerfileはGitHubに公開しています。
GitHub:/kuboshu/pythonml

この記事でわかること

機械学習用ライブラリをインストールしたDockerイメージを構築し、コンテナのJupyter-notebookを外部から使用して機械学習で遊ぶ方法。

Dockerイメージにインストールされているライブラリ

Pythonを使用するのでPythonの機械学習関連のライブラリを適当に入れてみました。

  • python 3.8.2
  • scikit-learn 0.23.2
  • pandas 1.1.2
  • numpy 1.18.5
  • jupyterlab 2.2.8
  • pycaret 2.1.2
  • lightgbm 3.0.0
  • xgboost 1.2.0
  • scipyt 1.5.2
  • matplotlib 3.3.2
  • tensorflow 2.3.1
  • pytorch 1.6.0
  • pyocr 0.7.2
  • opencv-python 4.4.0.44
  • optuna 2.1.0
  • mecab

作成したDockerfileの内容

Ubuntu20.04をベースに作成しました。基本的にはpipでPythonのパッケージをインストールしているだけなので、特別なことは何もしていないです。

FROM ubuntu:20.04
LABEL maintainer="kuboshu83"
ENV DEBIAN_FRONTEND noninteractive
ARG INSTALLDIR_PYOCR="/app/ocr"
RUN apt-get update && \
    apt-get -y upgrade && \
    apt-get install -y git \
                       make \
                       cmake \
                       gcc \
                       g++ \
                       wget \
                       zip \
                       curl && \
    # ~~~~~Pythonのインストール~~~~~
    apt-get install -y python3 python3-pip && \
    ln -s $(which python3) $(dirname $(which python3))/python  && \
    ln -s $(which pip3) $(dirname $(which python3))/pip && \
    # ~~~~~Python用ML関連ライブラリのインストール~~~~~
    # TensorflowとPytorchは容量が大きいので必要なければコメントアウトしてください。
    # 容量の目安はtensorflow=1.2GB, pytorch=2GBです。
    # Tensorflow, Pytorch以外のML様ライブラリで約2GBです。
    pip install pystache \
                numpy==1.18.5 \
                pandas \
                scikit-learn \
                matplotlib \
                jupyterlab \
                pycaret \
                lightgbm \ 
                alembic==1.4.1 \ 
                sqlalchemy==1.3.13 \
                optuna && \
    pip install tensorflow && \
    pip install torch torchvision && \
    # ~~~~~OpenCVのインストール~~~~~
    pip install opencv-python && \
    apt-get install -y libgl1-mesa-dev && \
    # ~~~~Tesseractのインストール~~~~~
    apt-get install -y libleptonica-dev tesseract-ocr && \
    # ~~~~PyOCRのインストール~~~~~
    pip install pyocr && \
    mkdir -p /usr/local/share/tessdata/ && \
    curl https://raw.githubusercontent.com/tesseract-ocr/tessdata_best/master/jpn.traineddata -sS -L -o /usr/share/tesseract-ocr/4.00/tessdata/jpn.traineddata && \
    # ~~~~MeCabのインストール~~~~
    apt-get install -y mecab libmecab-dev mecab-ipadic && \
    pip install --no-binary :all: mecab-python3 && \
    pip install neologdn && \
    #~~~~作業ディレクトリの作成~~~~
    mkdir -p /home/share

# デフォルトでPythonシェルを起動
CMD ["python"]

Dockerイメージのビルド方法

Dockerイメージは以下のコマンドでビルドできます。また、Githubにあるbuild.shに同様のコマンドが記載されているので、build.shを実行してもイメージがビルドできます。

docker build -t イメージ名:バージョン Dockerfileの場所

Jupyter-notebookを起動する方法

カレントディレクトリにshare/を作成して以下のコマンドを実行すると、コンテナに次いでJupyter-notebookが起動します。あとは表示されたURLをブラウザで開けばJupyter-notebookを使用することができます。
Dockerイメージのバージョンは適当なものを指定してください。以下の例ではv0.1.0を使用した場合になります。

# コンテナの/home/shareと共有するためのディレクトリを作成する
> mkdir share

# コンテナを起動する。
# -rm: コンテナ停止と同時にコンテナを削除する。
# -it: コンテナでターミナルを使用するために必要。
#      今回みたいにJupyter-notebookを使用するだけなら不要だけど何となく入れてある。
# -p : ホストの8888番ポートをコンテナの8888番ポートに割り当てる。
# -v : ホストの(カレントディレクトリ)/share/をコンテナの/home/share/にマウントする。
# -w : コンテナ起動時のコンテナのカレントディレクトリを/home/share/にする。
# Jupyter-labは8888番ポートで起動しています。
> docker run --rm -it -p 8888:8888 -w /home/share -v $(pwd)/share:/home/share pythonml:v0.1.0 /usr/local/bin/jupyter lab --ip=0.0.0.0 --port 8888 --allow-root

コンテナを起動すると作業用として用意してある/home/share/がカレントディレクトリになるので、これをホスト側のディレクトリと共有すると使いやすいです。

Dockerfileを書くときに調べたこと

[インタラクティブなインストールを避ける]

完全に自動でDockerイメージをビルドしたいので、パッケージなどのインストール時に手動での設定を求められたくないので、インストール時のインタラクティブな設定を無効化したかったので環境変数として以下を設定した。

ENV DEBIAN_FRONTEND noninteractive

[RUNの使用回数を減らす]

最初は何も考えずにRUN命令を多用していたが、docker image ls -aでイメージを確認すると下の様に中間レイヤーのイメージが量産されていた。どうやら、DockerはDockerfileで命令を使用する毎に中間レイヤーを作成して、最終的に中間レイヤーを合成して最終イメージを作成するらしい。よって、なるべく使用する命令数を少なくしました。

中間レイヤーが多いと問題があるのかどうかは、まだ理解していないのでわかりません。ただ、イメージ一覧を表示した際に<none>が大量にあるのが気持ち悪かったので、中間レイヤーを減らす様に記述してみました。

REPOSITORY          TAG       IMAGE ID      
pythonml            v0.1.0    xxxxxx        
<none>              <none>    xxxxxx        <= これとか
<none>              <none>    xxxxxx        <= これとか
<none>              <none>    xxxxxx        <= これとか
<none>              <none>    xxxxxx        <= これとか
<none>              <none>    xxxxxx        <= これとか
<none>              <none>    xxxxxx        <= これとか
<none>              <none>    xxxxxx        <= これもか
ubuntu              20.04     xxxxxx        

まとめ

今回は単にPythonのパッケージをインストールしたDockerイメージを構築して、ホストからJupyter-notebookを使用して機械学習で遊ぶ環境の構築方法をメモしました。まだ、遊んでみたいライブラリとかがあるので今後入れていきたいです。

また、今回はDockerfileの見た目を優先させ、ライブラリは全てapt-getかpipで入れているため古いバージョンのものもあります。なので、今後ソースコードからビルドして最新のバージョンのインストールも時間があればやっていきたいです。

参考にさせていただきました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む