Skip to main content

Docker

Overview

Dockerについてまとめているセクション。

参考

Dockerfile(リフェレンス)
入門Docker
軽量なDockerfileの作り方
Dockerfile ベストプラティクス(かなりすごい)
Dockerセキュリティ(これもLTに入れる)

docker container prune -f;
docker image prune -f;
docker volume prune -f

そもそもコンテナーとは

コンテナーとはアプリケーションの実行環境をパッケージ化し、それをデプロイ、実行するためのテクノロジーのこと。

  • アプリケーションコード
  • ライブラリ
  • プログラミング言語のランタイム

アプリケーションを実行するための依存関係をコンテナーイメージとしてパッケージ化する。
また、パッケージ化されたコンテナーイメージは任意の場所で実行されるコンテナーランタイムによりコンテナーとして実行ができるようになる。

コンテナーを利用することで得られるメリット

可搬性を得ることができる。
どこでも同じ環境を再現できる

昨今の開発

コンテナーの持つ可搬性によりさまざまなメリットが得られ、ユーザーがよりアプリケーション開発に集中できることから、サーバーレステクノロジーと同様にモダンアプリケーションにおける重要な選択肢の1つとなっています。

Lambdaの方が抽象度高く、多くの作業をAWSにオフロードできる。
Lambdaで要件を満たせない場合にAmazon ECSやAmazon EKSの採用を検討することになる。
AWS Lambda関数の**タイムアウト設定は最大で15分のため、**それ以上の実行時間が想定されるアプリケーションでは採用が難しくなる。
そのような場合、アプリケーションの実行環境としてコンテナーが候補に挙がるため、Amazon ECSやAmazon EKSが選択肢に出てくる。

サーバーレスとコンテナー使い分ける指標

アプリケーション開発により多くの時間や人を投資できる選択肢はどれかという観点で整理する考え方。

コンテナーをアプリケーション実行環境へする場合

Amazon EC2あるいはAWS Fargateが選択肢となる。
Fargateを利用すると、Dockerなどのコンテナランタイムを含むAmazonEC2インスタンスの管理が不要となるため、コンテナワークロードの運用負荷が軽減されます

Docker Linter

DockerfileのLinterとしてhadolintというものがデファクトスタンダードとして存在する。

Volume Trick

参考URL

まず、node_modulesはDockerイメージにホストからコピーしてはいけません。node_modulesはgitでも無視するはずなので、新しく環境構築する人のディレクトリにはそもそも入ってませんし、イメージをリビルドするときにホストのnode_modulesからイメージ内のnode_modulesが上書きされることも避ける必要があるからです。

Volume Trick は通常、Docker環境でNode.jsアプリケーションを開発する際に node_modules ディレクトリを効果的に管理する方法として使用される

1. パフォーマンス向上

ホストマシンとDockerコンテナー間で node_modules ディレクトリを共有すると、パフォーマンスが低下することがあります。Volumeを使用することで、この問題を回遍することが可能です。

2. 環境の違いの管理

異なるOSや環境で動作させる際に、異なる依存関係やバイナリを必要とするパッケージが存在することがあります。Volumeを使用することで、コンテナー内で専用の node_modules ディレクトリを保持でき、ホストOSとは異なる環境での依存関係の問題を回避できます。

3. ホストマシンのディスクスペースの節約

node_modules ディレクトリはしばしば大きく、多くのプロジェクトをホストマシンに保持している場合、ディスクスペースを大量に消費します。Volumeを使用すると、これらのディレクトリをコンテナー化された環境内に保持でき、ホストマシンのディスクスペースを節約できます。

4. 簡単な依存関係の管理

開発中に依存関係を迅速にインストール/アンインストールする必要がある場合、Volumeを使用することで、ホストマシンの影響を受けずにこれを行うことができます。

5. コンテナーの再利用

コンテナーを再起動したり、新しいコンテナーを起動したりする際に、すでにインストールされている依存関係を再利用できます。
これにより、新しいコンテナーの起動時間が短縮されます。

6. ホストとコンテナー間の依存関係の競合を回避

ホストマシンとDockerコンテナーで異なるバージョンの依存関係を使用することが可能。
これにより、ホストとコンテナー間で依存関係の競合が発生するのを避けることができます。

注意: ただし、Volume Trickを使用すると、node_modules ディレクトリの管理が複雑になる可能性があり、とくに新しい依存関係を追加または削除する際に問題が発生することがあります。また、ボリュームはDockerホストのディスクスペースを使用しますので、大量のデータを保持する長期間稼働するボリュームはディスクスペースを消耗します。このため、定期的なメンテナンスが必要となることもあります。

Docker が軽い方がいい理由

参考URL

Docker imageの中身を見る(レイヤー)

参考URL

以下の要望があるときに見られる
作成したイメージがどうなっているかを確認したいとき。
レイヤーについて詳しく見られる。

docker save -o [name].tar [image id]

Docker mysql

参考URL

dockerでvolumeをマウントした時のファイルowner問題

参考URL

ホスト側のファイルをコンテナー内で使いたい場合や、逆にコンテナーで作ったファイルにホストからアクセスしたい場合に有用なのだが、ファイルのアクセス権限について考慮すべき点がある。 ちなみにdocker for macで試したところ、上記の問題は起きない。 コンテナー内からはownerがrootとして表示されるが、mac上からは自ユーザーがownerとして表示されている。docker for macの中でうまく解決してくれているようだ。

docker 仕組み

参考URL
Dockerはビルドのステップごとにファイルシステムの変更差分を積み重ねることでイメージを作成する。
先にGemfileをイメージに組み込んでbundle installを実行しておかなければソースコードを変更するたびに毎回bundle installをする必要がある。

# 先にGemfileを転送しbundle installする
COPY --chown=rails Gemfile Gemfile.lock package.json yarn.lock /app/

RUN bundle install
RUN yarn install

上記のような工夫をすることでGemfileの内容に変化がなかった場合は bundle install の工程までキャッシュを利用することが可能になり2回目以降のビルドが大きく高速化される。


インストールするimageの中を確認する

そのイメージになにが含まれているのか確認する方法 $ docker run -it alpine:3.11 言語のalpineだと/bin/shなどで起動する必用がある。 $ docker run -it alpine:3.11 /bin/sh

docker-composeではない起動方法

  1. Dockerfileを用意する
  2. build

Docker コマンド一覧

run imagesからコンテナーを起動する。

exec 起動中のコンテナーに入る

リモートサーバー内のDockerにローカルから接続する

Dockerでもlocalhostでも起動した時に 0.0.0.0 などパブリックドメインで公開すればアクセスできる。 0.0.0.0


Docker セキュリティ

参考URL

コンテナセキュリティの課題は、ダウンロードしたコンテナイメージが期待通りであるか。 セキュリティと一貫性の観点から期待通りのイメージがダウンロードされることの保証が重要。 Dockerのイメージタグは便利だが常に一貫した特定のイメージを指すとは限らないためSHA-256ハッシュを使ってイメージを識別すること

TODO: これLT アプリケーションがrootで実行されている もしアプリケーションにOSコマンドインジェクションやディレクトリトラバーサルなどの脆弱性があり、それが悪用された場合アプリケーションがroot権限で実行されていると悲惨なことになります

docker security

効率的に安全な Dockerfile を作るには


Docker パーミッション

LinuxではDockerを実行した場合、作成されたファイルの所有権が root になる(公式はrootで対応するなと言っている) そのためDockerfileの ADD/COPY--chown オプションができており、それ用のuserを作成するルールになっている。

Dockerfile ---chown 参考URL


Dockerイメージを軽くする

Dockerfile ビルダーパターン

マルチステージビルドとも呼ばれていることがある。 ビルダーパターンなど(FROMが2度現れること)がある。 つまり、xxxパターンを調べて勉強しろ

マルチステージビルド

マルチステージビルドは、Docker17.05以上で利用できる新機能 前のステージでビルドされた成果物をこの新しいステージへコピーする 最終的なイメージでは取り残され最終的なイメージへは保存されない。 ※つまり、TypeScriptをコンパイルするステージ⇨最終的なJSを実行するステージに分けることができる。

COPY --from=0

参考URL


Docker image削除

containerを削除してからではないとimageが削除できない。

dockerignoreとは

docker buildでimageを作成する際に無視するファイル・ディレクトリを設定できる。 ※mountの場合は無視ができない。

Dockerのマウント種類

参考URL mountディレクトリの恐ろしさ たとえばDockerfile内でCopyをした場合、コピーされるがバインドマウントのためではないため編集しても反映されない。

そのためdocker imageを作成するためだけを意識すること また、COPYでせっかく全部コピーしてもバインドマウントで同じディレクトリを指定した場合にバインドマウントの中身ですべて消される。これが注意!!

alpine linux

alpine linuxは基本 GCC などの C コンパイラが含まれていない(つまり開発ツール)

alpine linuxにyarnいれる python:alpineにCコンパイラをインストール

Docker 参考文献

お前らのDockerfileは重い。 世界一わかりみが深いコンテナー Docker での Node 環境構築 Docker 環境構築best practice

Dockerコンテナー内のアプリケーションport

Dockerポート仕組み

Dockerコンテナー内のアプリケーションは、デフォルトでネットワークトラフィックを受け入れている。 →http://127.0.0.1:3000

このインターフェースは外部トラフィックを受け入れないため、機能しないのも不思議ではありません。これを機能させるには、nuxtアプリのHOST環境変数を**0.0.0.0(すべてのIPアドレス)**に設定する必要がある。

コンテナーのサーバがlocalhostでlistenしていると、ホストマシンからアクセスした際にエラーが起きることを確認した。 これは、ホストマシンのlocalhostとコンテナーーのlocalhostが異なるため

ホストマシンとcontainerはnamespaceで区切られていて別のマシンと捉えて構わないため、containerのloopback interfaceにはホストマシンからはアクセスできない。


イメージをビルドするタイミングとは?

  1. Dockerfileを書き換えた時。
  2. パッケージ管理を行うファイルを書き換えた時

Dockerを軽くする方法

  1. RUNはチェーンする(&&)とにかくつなげる。RUN毎にレイヤーが作成されてしまうため(実際これもどうなんだろうと言われている)
  2. 産業廃棄物(docker build)時に生じた、アプリケーション実行には不要なツールやファイルを削除する
  3. yumやapt-getのゴミ
  4. ビルドに使用したソース
  5. gitですら産業廃棄物
  6. 成果物を残すことを意識する
  • yarn installなどを早めたい

nodeではやく


docker-compose.yml => Dockerfileへ環境変数を渡す方法(1)

渡す側(docker-compose.yml)と 受け取る側(Dockerfile)双方の設定が必要。

  1. docker-compose.ymlから渡す args ... Dockerイメージをビルド(作成)する際に引数を渡すために使用します。

この仕組みを使ってDockerfileへ環境変数を渡します。

services:
api:
build:
context: ./api
args:
# キー: 値
WORKDIR: $WORKDIR
# この書き方でもOK
- WORKDIR=$WORKDIR
  1. Dockerfileで受け取る

ARG命令を使います。 指定する値はdocker-compose.ymlで渡したキーの名前です。 受け取った後はDockerfile内で変数として扱うことができます

ARG WORKDIR

ENV HOME=/${WORKDIR}

docker-compose.yml => コンテナーーへ環境変数を渡す方法

  1. environment

※これは扱う環境変数が少ない場合に使用する。

services:
api:
environment:
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
# この書き方でもOK
- POSTGRES_PASSWORD=$POSTGRES_PASSWORD
  1. env_file

env_fileを使用する場合は、環境変数を格納したファイルパスを指定します。 相対パス、絶対パスどちらでも良い。 これは扱う環境変数が多い場合に使用します。

services:
api:
env_file: ./.env

ベースイメージを調べる方法

重いイメージは本当にいいことがない。 参考URL

ベースイメージのRubyバージョンを調べる手順 ちょっと豆知識。これからのプログラミング人生のために。

Rubyのバージョンは開発時点の安定版を使用するようにしましょう。

以下の手順でバージョンを調べます。

  1. まず、Rails6に必要なRubyのバージョンを知る。
  2. Railsガイド => Ruby 2.5.0以降が必要
  3. 次にRuby 2.5.0以上の安定版を知る。
  4. Ruby => 安定版は2.7.1
  5. 最後にRuby 2.7.1のベースイメージを調べる。
  6. Docker Hub => 2.7.1-alpineを採用

最近はオフィシャルでもAlpine版が存在している。

Alpine参考URL


こうしたいんだぜという時の逆引きdocker

参考URL

コマンド

近年Dockerコマンドはできるだけ次の書式に統一しようとしている

$docker コマンド 操作 オプション

docker word

デタッチモード: コンテナーー内に入らずバックグラウンドで動作する状態のこと。 -dでやる

dockerの基本運用概念

バインドマウントやボリュームマウントは別の場所に置き、コンテナーー自体は破棄されても問題ないような運用を心がけるべき方針


開発におけるdocker導入のメリット

参考URL

メリット

  • 同一性 複数人で開発する際に、環境の差が生まれない。

  • カプセル化 アプリケーション込みの環境をコンテナーーというカプセルに隠蔽できる。 コンテナーーという単位に対するテストが可能に。 コンテナーーを捨てる・再生成するのが容易。

  • ポータビリティ(一貫性とも) 開発に使ったコンテナーーをCIでテストできる。 CIでテストしたコンテナーーをサーバーにデプロイできる。 デプロイしたコンテナーーをスケールできる。

  • ひとつのサーバにwebサーバを同居できる(Apache) 1台のDockerホストに2台のWebサーバを同居させることができるなど。

メリットで防げる消耗

  1. おれの環境では動いた。 はい。 複数の開発者で同一の環境で開発できるので防げる。

  2. ローカルで通ったテストがCIでコケる。 開発と同一の環境でテストできるので防げる。 bundle install, npm installに失敗してテストがコケる。

  3. 依存ライブラリのフェッチに成功したイメージでテストすることで防げる。 コードを変更していないのにアプリケーションの挙動が変わった。go getしてるライブラリの挙動がいつの間にか変わってたぽい。

  4. 依存ライブラリをフェッチしたイメージを共有することで、バージョンを固定できる。 nginxの設定を変更したい。サーバにログインするためのSSHキーどれだっけ。 手元で設定して動作を確認しDockerイメージをデプロイできるので、設定変更のためにサーバーにログインする必要がなくなる。 さらに、正しく設定できているかブラックボックステストすることも可能に。

デメリット

  • 学習コスト コンテナーを立ち上げるあたりまではコスト低めだが、実際に開発環境としてDockerを使うあたりから「これどうやってやるんだろう」「どちらの方法で設定するのが良いのだろう」のノウハウが出てくる感じになる。 開発者全員に深い知識が必要かというとそうではないが、問題解決できる知識を持った人間が開発陣に最低一人は必要。

  • ローカルでの開発とほぼ遜色ない環境でコードを書くことができるということを周知するためのコスト

  • 完全な分離ではない。


DockerFile とは(カスタムイメージを作成した時)

docker buildでカスタムimageを作成するファイル 公開されている Docker イメージをそのまま使う場合は必要なく、カスタマイズしたい場合に作成する。

必要な理由 ベースとなるイメージとそのイメージに対して、どのような操作をするのかを記したDockerfileと呼ばれるファイルを用意しそのDockerfile通りにコンテナーに対して変更やファイルコピーを加えることによってイメージを作成する。

カスタムイメージの作り方

2つある。

  1. コンテナーから作る ベースとなるイメージからコンテナーを起動し、そのコンテナーに対して、docker exec でシェルで入って操作したり dokcer cp でファイルをコピーしたりし調整を加える。 その後 docker commit コマンドを使いイメージ化する。 デメリットが存在する

  2. Dockerfileから作る ベースとなるイメージとそのイメージに対して、どのような操作をするのかを記したDockerfileと呼ばれるファイルを用意しそのDockerfile通りにコンテナーに対して変更やファイルコピーを加えることによってイメージを作成する。 イメージの作成には docker build を実行する。

メリット Dockerfileは、ベースとなるイメージに対する変更指示をまとめたファイル。これを見れば悪意ある操作や間違った操作が加えられていないかが一目瞭然 Dockerfileは改良しやすいメリットがある。


Dockerfileでカスタムを利用した理由

ある程度なんでもしたいときはdebianイメージを使用することが多い。

コンテナーからイメージを作る(docker cpやexecで中に入ったあと)

以前のhttpdコンテナーを作ったあと、docker cp コマンドをつかってindex.htmlをコンテナー内にコピーし これと同様の方法で作成したコンテナーをイメージ化する。 そしてそのイメージからコンテナーを作り直すことでindex.htmlがすでに入ったコンテナーが起動することを確認する。

コンテナーをイメージ化する docker commit

コンテナーからイメージを作る(Dockerfile)

  • イメージに含めるファイル群とDockerfileの用意 イメージに含めたいファイルとDockerfileを1つのディレクトリに置き、それを docker build して作るという方法 ※このディレクトリに含めているファイルはイメージを作成したとき、利用していないものであっても含まれてしまうため余計なものは置かない。 ※ただし、同ディレクトリに.dockerignoreを置くと、除外ファイルを指定できる。しかしこの機能はファイル名のマッチングで除外をするためファイル数が多いと、イメージの作成に時間がかかってしまう恐れがある。

レイヤー

Layerは RUN / COPY / ADD でのみ作成され、それ以外は一時的なLayerとして作成されます。(つまり無闇にこれらのステートメントを小分けにせず可能な限りまとめて記述 && で接続する事も重要)

どのようなレイヤーで構成されているかは docker history で確認できる。
Docker ImageはUnionFSという複数のFS(Layer)にあるファイルを透過的に1つのものとして扱う技術が使われ、Dockerfileの命令によって作成された全ての中間Layerをマージして最終的に1つのFSとなる。


docker-compose.yml 覚書

コンテナーの定義やマッピングするポートなどコンテナーに関する設定を記述するファイル

version: "3"
services:
app:
# 起動イメージ
image: node:16
# 環境変数
environment:
- DEBUG=app:*
tty: true
# ホスト側のポート:コンテナーのポート
ports:
- "3000:3000"
# ホスト側のsrcをコンテナーのappにマウント
volumes:
- ./src:/app
# 起動時のカレントフォルダ
working_dir: /app
# 起動後に実行するコマンド
command: npm start

重要な箇所は # ホスト側の src をコンテナーの app にマウントの部分で、通常 Docker コンテナーを停止するとコンテナー上で作成した各種ファイルは削除されますが、上記の記述を行うことでコンテナー起動時に再度ファイルがマウント(反映)されます。 また Docker コンテナー上で作成・変更したファイルもこちらに記述した場所に反映される。 基本ホスト上とコンテナー上でファイルの同期をとるための記述と考えれば OK

Docker compose 起動 (docker-compose コマンドについて)

$ docker-compose run --rm app /bin/bash

※rmオプションはDockerコンテナー停止時にコンテナーを削除する機能で、停止したコンテナーが残り続ける問題を解決するためのオプション

Docker コンテナーの軽量化

軽量化参考 URL

  • コンテナーイメージサイズが大きいことによる弊害 イメージのビルド時間が長い イメージを Docker registory にプッシュする時間が長い イメージを PULL する時間が長い

それらが起因して下記の弊害が起こる

トライアンドエラーに時間がかかり、生産性が低下 ビルド時間,CI時間の増大 オートスケールでコンテナーがサービスインされるまでの時間が長くなる。 Kubernetesクラスタを構成するNodeディスクの消費

  • 軽量化のアプローチ
  1. RUN命令をまとめる 基本中の基本

※RUN命令が走る度にイメージレイヤーが生成されてしまうため、ちょっと重くなる。

# npm installの後のパッケージを羅列
RUN npm install -g gulp@3.9.1 \
&& npm install gulp-load-plugins \
gulp-plumber \
gulp-sass \
gulp-pleeease \
gulp-uglify \
gulp-rename \
&& npm init -y

このように、複数の RUN コマンドを「連続で実行する 1 つのコマンド」として扱うことで、イメージレイヤーをまとめて軽量化できます。

  1. RUNでaptコマンドを走らせた場合はインストール時に使ったaptキャッシュ(ゴミファイル)が残ってしまうため削除する。

  2. 使用したいimageにslim versionがあるかDockerHubで探す。 ※デフォルトのNOdeがかなり色々入っている。そのためslimイメージを探す。


docker Script関連

Docker コンテナーの動作に必要な設定を起動時に渡す

Dockerコンテナーを起動するタイミングで、コンテナーの動作に必要な設定を受け渡す方法は2つある。

  1. 環境変数を通して渡す
  2. コマンドライン引数を渡して渡す。

どちらの場合も docker run で実行するコマンドの中に設定を含めることになる。

docker-entrypoint.sh

dockerで初回起動時のみ特定の処理を行うヘルパースクリプト docker run とか docker-compose up -d などの初回起動時のみ処理したいことがある場合に使用する。

docker restartとかdocker-compose restartとかsystemctl restart dockerの時には動いてほしくないんだ。 やってみた結果mariadb + zabbixで初回起動時のみ、構成用の .sqlを流し込む。という動作ができるようになりました。


ローカル上にLocalStackをDockerで実行

開発用にAWSのサービスをローカル環境に構築できる、LocalStackというプロジェクトがある。素晴らしい。

参考URL

ビルドコンテキスト

ビルドコンテキストとは docker build 実行時に指定するディレクトリのこと。
docker build コマンドを実行した構築時、ビルドコンテキストとして現在のディレクトリ以下にある全てのファイルやディレクトリをDockerデーモンに送信してしまう。
このような事態を防ぐためにもDockerfile用のディレクトリを作成し、そのディレクトリには無駄なファイルは配置しないようにすべき。

Dockerのコンテキストとは

docker build コマンドを実行したときの、カレントなワーキングディレクトリのことを ビルドコンテキスト(build context)と呼びます。 デフォルトで Dockerfile は、カレントなワーキングディレクトリにあるものとみなされます。 ただしファイルフラグ(-f)を使って別のディレクトリとすることもできます。 Dockerfile が実際にどこにあったとしても、カレントディレクトリ配下にあるファイルやディレクトリの内容がすべて、ビルドコンテキストとして Docker デーモンに送られることになります。

要はdocker buildコマンドを実行した場所

  • 制約 Dockerはコンテキスト(カレントディレクトリ)の外のファイルにはアクセスできない仕様。

  • ではディレクトリごとにわけた ルートディレクトリをDockerのコンテキストにすることで、Dockerfileはどんなファイルにもアクセスできるようになりました。 一方で、build時はその分Dockerデーモンという奴にそれだけ多くのファイルを送ることになるので遅くなることがあるようです。

構築時、ビルドコンテキストとして現在のディレクトリ以下にある全てのファイルやディレクトリをDocker deamonに送信してしまいます。ビルドコンテキストに余分なディレクトリ・ファイルがあると、build時に時間がかかる、メモリを消費する原因となります。たとえば、ビルドコンテキストに100MBのファイルがあるとimageのサイズが100MBプラスとなってしまいます。このような事態を防ぐためにもDokcerfile用のディレクトリを作成し、そのディレクトリには無駄なファイルは配置しないようにすべきです。

.dockerignore

dockerignoreとは、DockerfileのCOPYコマンドでコピーしたくないファイルやディレクトリを指定するファイルのこと。
dockerignoreを作成することで、ビルド時間の短縮、docker imageサイズの最適化などの効果があります。

仕組み

Dockerfileからイメージをビルドする場合、Dockerfileの存在するディレクトリの中身はtarで固められdaemonへと送られます。DockerfileのCOPYコマンドを使用して、必要なファイルをコピーします。

docker network

コンテナー内の/etc/hostファイルで定義されているそう。以外に簡単

マルチステージビルド

参考URL

  • マルチステージビルド以前 サイズを小さく保ちながらDockerイメージをビルドすることは、最もやりがいのあることのひとつ 。 Dockerfile内の各々の命令ではイメージにレイヤが追加される。 したがって、次のレイヤを作成する(次の命令に移る)前に、不要な生成物のクリーンアップする必要があります。 本当に効率的なDockerfileを作成するには、レイヤをできる限り小さく保ち、各レイヤが前のレイヤの生成物から必要なものを確保するために、シェルのトリックやその他のロジックを採用する必要がありました。 実際に、開発用にはアプリケーションのビルドに必要なすべてが含まれるDockerfileを使用し、プロダクト用にはアプリケーションおよび実行に必要なもののみが含まれるスリム化されたDockerfileを使用することは非常に一般的でした。 これがいわゆる"ビルダーパターン"です。 2つのDockerfilesを保守することは、理想的ではありません。

ここからはさわって学ぶクラウドインフラ本

  • コンテナー コンテナーはシステムの実行環境を隔離した空間のこと。 アプリケーションの実行に必要なプログラムやライブラリ、各種設定ファイルなどをワンパッケージにし隔離して実行するための仕組み。 コンテナーのメリットはそれをコピーして別のコンピューターーで動かすのが容易なこと。

システム開発・運用の現場では、「開発者が作ったプログラム一式を検証機にコピーする」「検証機で動作確認して問題なければ本番機にコピーする」「冗長性や負荷分散のために、同じ構成のものコピーして多数台用意する」というように、そのコピーを作りたいことが、よくある。 コンテナー技術を使えば操作が容易になり、コピーや設定漏れを防げます。またシステムのアップデートも、コンテナーを差し替えるだけで済むようになります。

Docker イメージ

2種類 Dockerイメージには基本的なディストリビューションとアプリケーション入りのがある。 カスタマイズするには基本的なディストリビューション入り(Linuxのみ)を使う

Dockerイメージに手を加える時は、アプリケーション入りDockerイメージではなく、LinuxのみDockerイメージをベースとするほうがやりやすい。 →ただ、そうしたカスタムは手間がかかり、1つひとつ手作業していると作業漏れが起こる可能性が少なくない。そこでコンテナーに手を加えたあと、そのコンテナーをカスタムDockerイメージに変換する。

  • ではカスタムのDockerイメージを作るのは? カスタムのDockerイメージを作るときは、手作業でファイルコピーやコマンドを実行するのではなく、Dockerfileにファイルコピーや実行したいコマンドなど一連の設定を記述し、そのファイルを適用して作るのが一般的。 カスタムDockerイメージはDocker HubのようなDockerレジストリに登録できる。

Docker desctop

Docker Desctopの内部にはLinuxカーネルが含まれており、WindowsやmacOSでありながらもLinuxを実行することで、その上でDockerを使えるようにしたもの。

仮想サーバとコンテナーの違い

仮想サーバは1台の物理的なサーバの中に復数の仮想的なサーバを作り、物理的なサーバを仮想的なサーバが分割して使う

コンテナーはサーバをブナkつする技術ではない。サーバはあくまでも1台で、その中にたくさんのアプリケーションが隔離して実行されているのにすぎない。

コンテナーの破棄

docker stopしてもコンテナーはずっと残り続けるということ。 これは明らかにディスクを圧迫する。もう使わない場合は停止ではなく明示的に破棄にするべき docker rmで完全削除できる。

イメージの破棄

Dockerイメージが消費する容量も馬鹿にならない。 ダウンロードしたイメージはコンテナーを破棄しても残ったまま。 ※これはもう一度、同じDockerイメージからコンテナーを作ろうとした時に、再ダウンロードしなくても済むようにするため。

$docker image ls $docker image rm$docker rmi とかける。

docker runについて

$docker run というコマンドは

$docker pull,$docker create,$docker start という3つの一連のコマンドをまとめて実行している。

使い方 $ docker run [オプション] イメージ [コマンド] [引数...]

タグ

イメージ名のタグ(tag)はDockerイメージの製作者が名付けた分類名のこと。 ※タグはリソース版や、開発版、バージョン番号などを示すのに使われる。

タグ名を省略する時は、最新版を意味するlatestというタグが指定されたものとみなされる。

オプション

-pオプション(略称 publish)

ポート番号をマッピングするもの -p ホストのポート番号:コンテナーのポート番号 udpを選択する場合には、ポート番号を ポート番号/udp とする。 ※Dockerでは、pオプションを指定しない限り、DockerホストとDockerコンテナーとの通信は繋がらない。 ※マッピングの状態は docker port コマンドで確認ができる。

-vオプション

コンテナーの特定のディレクトリにホストのディレクトリをマウントする設定。 -mountオプションを同様

-v ホストのディレクトリ:コンテナーのディレクトリ

-ditオプション

-ditは -d, -i, -t の3つのオプションの組み合わせ。

-dが端末から切り離してバックグラウンドで実行 -iと-tはコンテナーを端末(キーボードとディスプレイ)から操作するためのオプション

アタッチの場合(-dを指定しない時)は端末と接続された状態のため、端末からの操作は、そのままコンテナー内で実行中の既定のコマンドに流されます。だからこそ、[Ctrl]+[C]を押すことで、そのコマンドが終了する。 デタッチのときは、端末とは切り離されているので、コンテナー内で実行されているコマンドに対して、何かキー操作することはできません。デタッチの状態とアタッチの状態は、実行中に切り替えることができる。

-iオプション

標準入出力およびエラー出力をコンテナーに対して結びつけます。その結果、キー入力した文字はコンテナーに渡され、コンテナーからの出力が画面に表示されるようになります。iオプションを指定しないと、キー入力はコンテナーに伝わりませんからこうしたキーが効きません。そしてコンテナーからの出力が届きませんから、httpdコンテナーの例で言えば、いま見てきたように、画面に各種ログが表示されることもありません。

-tオプション

-tオプションは、pseudottyと呼ばれる疑似端末を有効にする設定です。疑似端末は、カーソルキーやエスケープキー、[Ctrl]キーなどで操作するためのものです。このオプションを指定せず、iオプションのみだと、これらのキーが使えません。つまり、[Ctrl]+[P]、[Ctrl]+[Q]キーが効きません。

デタッチで起動後、操作の必要がないのであれば-iや-tのオプションは必要ない。 しかし、後でアタッチするなどして端末から操作したいときは-iや-tを指定する必要がある。


Volume関連

ボリュームマウント ボリュームとは、Dockerコンテナーーで扱うデータを永続化する仕組み。 Dockerの中にボリュームというデータを保持する領域を確保し永続化する。 HOSTでソースコードなどを変更する必要がないのであれば、ファイル類をすべてコンテナ内管理する。 具体的にはボリュームマウントしたフォルダでファイルを管理するのがベスト。 問題は、ボリュームマウントしたファイルはHOSTから直接操作できないのが難点。 ですので、ソースコードを変更しなければならない開発環境で使うのは難しいです。 また、HOSTで自由に読み書きできないので、バックアップをとるのもちょっと面倒。 docker snapshotみたいな機能があればいいんですが…現状はないっぽいです。

因みに、VSCodeであれば、拡張機能を使うことによってボリューム内のファイルの読み書きが可能です。

これから新規に開発環境を作るのであれば、VSCodeを使うことを前提にボリュームマウントを使うことをお勧めします。

バインドマウント ホスト側のディレクトリやファイルをコンテナーー内にマウントすること。 バインドマウントによってもコンテナーーで扱うデータを永続化できる。 ※ただしDockerDesktopを使用していると、LinuxのVMを経由して動作させる必要があるためファイルアクセスのパフォーマンスが低下する可能性。そのため :cached を追加してバインドする。

バインドマウントでマウントキャッシュを利用してホストマシン上のディレクトリをビルド中のコンテナ内にマウントすると、ファイルのuid, git, modeはコンテナ内でファイルを作成するとrootとして操作したことになり、ファイルは uid=0 で作成される。

もしそれがホストマシン上のディレクトリをマウントした場所なら、ホストマシン上のディレクトリにuid=0のファイルが作成されることになります。ファイルのownerやパーミッションの値に注意しましょう。

バインドマウントの注意 マウントするボリュームに何かしらファイルやディレクトリが存在すれば、マウントされるコンテナーー側のディレクトリの中身は全部上書きされる。 マウントするボリュームに何も入っていなくて、マウントされるコンテナーー側のディレクトリに何か入っていれば、ボリュームにコピーされる。

docker内のファイルと永続化

Dockerコンテナーは、それぞれが隔離された実行環境。コンテナーを破棄すればその中にあるファイルは自ずと失われる。 $docker rm このコマンドをして再度 docker run をするとコンテナーIDが変わるとおもう。前のコンテナーとは別のものとなっている証拠。

ホストからファイルをコピーする

コンテナーーの中のファイルを変更するにはどのようにすればいいか

docker execで、/bin/bashを起動しそこでnanoエディターなどを起動して、編集する方法。 しかしapacheなどではファイルサイズを小さくするためnanoエディターなどが入っていないおそれ

そこで便利なのが docker cp コマンド ※docker cpはパーミッションをそのままコピーする。ディレクトリも再帰的にコピーする。 ※docker cpは/prop, /sys, /dev, tmpfs配下のようなシステムファイルはコピーできない。こうしたファイルをコピーしたい時には標準入出力経由でコピーする。

上記を含めると、コンテナーーは一度起動したら破棄してはいけないのか?

違う。そうではなくコンテナーーで失ってはいけないデータは外に出すように設計する。

  • ではどうすればいいか?

マウントする。 コンテナーーは実行するシステムと扱うデータは別に管理することが推奨されている。 またコンテナーーの設計として実行するシステムと扱うデータは別に管理することが推奨されている。

docker run -v でマウントをする。

ボリュームのマウントにはバインドマウントと同様にvオプションを使う。 違うのはディレクトリ名ではなくボリューム名を使うという点だけ

注意 **ただしボリュームのマウントにはvオプションではなく、mountオプションを使うことが推奨されている。**vオプションだとボリュームが作られていない時に新規にボリュームが作成されてしまい、意図しない結果になるため。

つまりデータをコンテナーーの外に出す。 Dockerホストのディレクトリをマウントしているのは(外に出しているのと同意)そのためコンテナーーを削除しても消えないのは当たり前


mountオプションを使ったマウントの設定

バインドマウントやボリュームマウントするのに-vオプションを使っていたがもう1つ--mountというオプションを使う方法もある。

--mount type=マウントの種類, src=マウント元, dst=マウント先

マウントの種類は、バインドマウントのときはbind, ボリュームマウントのときはvolumeを指定する。 srcはマウント元、dstはマウント先(srcはsource, dstはdestinationやtargetとも書ける。)

--mountはDocker17.06からサポートされたオプション。--mountのほうが推奨されている。

  1. バインドマウントかボリュームマウントかわかりにく。 -vオプションではマウント元が[/]から始まる時はバインドマウント、そうではないときはボリュームマウント。

  2. ボリュームが存在しない時はエラーを吐く。


コンテナー間のデータ共有にも利用できる。

マウントする手法はデータを失わないようにするだけではなく、別の方法もある。 それはコンテナー間でのデータ共有。1つの場所を2以上のコンテナーで同時にマウントすることもできる。

そのため./docker/nginx/nginx.confなど、設定ファイルをプロジェクトに用意しているのは、コンテナー内のnginxの設定ディレクトリにマウントし本来のnginx.confを上書きしている。 この方法であれば、Dockerホストに設定ファイルが残るため設定のバックアップが容易。

※マウントはディレクトリに対して設定するのがほとんどだが、設定ファイルだけをマウントすることも可能。

Dockerホストにあらかじめディレクトリを作っておき、それをマウントする方法をバインドマウントという

  • マウント種類

バインドマウント Dockerホストにあらかじめディレクトリを作っておき、それをマウントする方法をバインドマウントという ディレクトリの変更をDockerコンテナに反映したいとき(アプリケーション用のコンテナなど)

ボリュームマウント ボリュームマウントは、ホスト上のディレクトリではなく、DockerEngine上で確保した領域をマウントする方法。 確保した場所のことを、データボリュームもしくは略してボリュームと言う。

データベース用のコンテナなど

ボリュームを使う利点は、ボリュームの保存場所がDockerEngineで管理されるため、その物理的な位置を意識する必要がなくなるという点。ディレクトリ構造はDockerホストの構成によって違うので、ディレクトリ名で指定する場合(dockerrunやdockercreateのvオプションでディレクトリを指定する場合)は、Dockerホストに合わせた場所を指定しなければならず、汎用的ではありません(たとえば、ある管理者は/home以下を使うように構成したかもしれませんし、別の管理者は/var以下を使うように構成したかもしれません)。それに対して、ボリュームを扱う方法は汎用的で、どのDockerホストでも同じです。すぐあとに見るように、dockercreateでボリュームを作るコマンド、そしてそのボリュームをdockerrunやdockercreateで指定するためのオプションは、どのDockerホストでも同じです。

ボリュームマウントはブラックボックス。そのためDockerホストから変更できない。

汎用性という目でいうと、ボリュームマウントが推奨されるが、バインドマウントを完全に置き換えるわけではない。 バインドマウントのほうが優れている場面もある。

バインドマウントのがよい場面

  1. 設定ファイルの受け渡し Dockerホスト上に設定ファイルを置いたディレクトリを用意して、それをコンテナーに渡したい場合です。

  2. 作業ディレクトリの変更を即座にDockerコンテナーから参照したいとき Dockerホスト上のファイルを変更したとき、それをDockerコンテナーにすぐに反映させたいときです。たとえば、httpdコンテナーを動かして、そのドキュメントルート(/usr/local/apache/htdocs)を、これまでのように、Dockerホストの適当なディレクトリにバインドした場合、Dockerホスト側でそのディレクトリ内のファイルを変更すれば、それはすぐにDockerコンテナーに反映されます。

  3. 各自のPCでDockerを使う場合はバインドマウントが便利

ボリュームマウントのほうがよい場面

ボリュームマウントが向く場面、Dockerコンテナーが扱うデータをブラックボックスとして扱い、コンテナーを破棄してもデータが残るようにしたいだけの場面。

データベースのデータは、通常ひとまとめのブラックボックスとして扱い、**それぞれのファイルをDockerホストから編集することはないはず。**もしそんなことをしたら、データベースは壊れてしまうことでしょう。このようにDockerホストから不用意にデータを書き換えたくない場面では、ボリュームマウントが向きます ※ボリュームはデフォルトでは、Dockerホスト上のストレージですが、ボリュームプラグインをインストールすることで、AWSのS3ストレージやNFSなどのネットワークストレージを用いることもできます。

ボリューム作成手順(MySQL)

  1. ボリュームを作成する(mysqlvolumeという名前) $docker volume create mysqlvolume

  2. 作成したボリュームを確認する $docker volume ls DRIVER VOLUME NAME local mysqlvolume

※dockervolumelsの結果として表示される「DRIVER」は、ボリュームを構成するドライバです。既定は「local」であり、Dockerホスト上のディスク上に作成される。それ以外にボリュームプラグインをインストールすることでAmazon S3のストレージやNFSなどのネットワークストレージを利用できる。

tmpfs

$docker run --mount type=tmpfs, dst=マウント先

tmpfsはディスクではなく、メモリーを特定のマウント先に指定するものでメモリーディスクを利用することで読み書きを高速化する目的で使う。 tmpfsはメモリーのため揮発性。コンテナーを破棄することでなくなる。

データのバックアップ

バインドマウントの場合はDockerホスト上のファイルのためDockerホストで別のディレクトリにコピーするか、tarコマンドでまとめて保存するなどでバックアップができる。

  • ボリュームの場合は? ボリュームの詳細情報は $docker volume inspect ボリューム名 で見れる。 この場所をtarで固めても意味がない

ボリュームのバックアップの考え方

ボリュームの場合は適当なコンテナーに割り当てて、そのコンテナーを使ってバックアップを取るようにする。


Docker のネットワーク

Dockerのネットワークは大別するとnone, host, bridghという3つの構成を取ることができる。

参考URL

本番運用

本番の安全運用は、Dockerホストをマネージドサービスにしてある程度、任せてしまうのが無難。 AWSにはAmazon ECSというコンテナーを運用するマネージドサービスがある。

  • マネージドサービス 管理されたManagedサービスという意味で、運用管理をクラウドに任せることができるサービスのことを言う。 仮想サーバのEC2は自分で管理するサービスのためアンマネージドサービスという。

  • さらなるスケーリングや堅牢性がほしいときはKubernetesを使う。

  • どのイメージをダウンロードすればいいのか 納品にあたっては、動作検証するはず、動作検証後にコンテナーのバージョンが変わるということはシステムが変わること。 本番環境でDockerを利用するのであれば、タグ名を省略せずに明示的に指定して、特定のイメージの版に利用する(それよりも新しい版ができたとしても使わないようにする。) **もちろん、その特定イメージの版のまま使い続けるという意味ではありません。**ある程度の期間が経ったら、そのときの最新版で再度動作検証し、問題なければ、その版に差し替えるというように、コンテナーの定期的なアップデートは必須です(そうしなければ、脆弱性などに対応できない。

  • Dockerイメージを使う場合 そのイメージの製作者がどのような使い方を想定して作っているのか、各種設定はどのようにして行えばいいのかを記載されているドキュメントから汲み取らなければならない。 かいつまんででもいいので下に記す。

1. マウントすべきディレクトリを知る データベースのデータは、/var/lib/mysqlディレクトリに保存されます。ここをボリュームマウント(もしくはバインドマウント)することで、コンテナーを破棄しても、データベースの内容が失われないようにする。

  1. rootユーザのユーザ名、パスワード、規定のデータベース名などの指定方法 データベースにアクセスする際のrootユーザーのユーザー名、パスワード、既定のデータベースなどは、環境変数として引き渡します docker run -e [環境変数] ※もっと複雑な設定をしたい時は、MySQLの設定ファイルであるmy.cnfをファイルをバインドマウントで引き渡す方法もとれる。

Docker build

Dokerfileからdocker imageを作成するコマンド

$docker build

build後、docker history で作成されたイメージの詳細情報が確認できる。

ビルド後のイメージを利用する

docker run をすれば利用できる。


Dockerfile覚書

[COPY] コピー元はDockerfileファイルが置かれている場所からの相対パス コピー先はWORKDIR命令で指定したパスからの相対パス

RUN

RUNコマンドは、docker buildするタイミング(すなわちイメージを生成するとき)に実行している。ここにはイメージの時点で実行しておきたいコマンドを書く。 例としてはソフトウェアパッケージのインストールやファイルのコピー、変更などの処理。

※注意 RUN命令は複数のコマンドを実行するときも、できるだけ1つのRUNコマンドで済ませるようにする。

CMDとENTRYPOINT

docker runやdocker createの引数で指定したコマンドが終了するとコンテナー自身が終了する挙動だったことを思い出す。 service httpd startを実行した場合は、httpdをバックグラウンドで実行してserviceコマンド自体はすぐに終了する。 その結果、コンテナー自体がすぐに終了してしまう。 コンテナーを動かしっぱなしにしたいのであれば、CMDやENTRYPOINTはずっと動きっぱなしでいるコマンドを指定する

CMDとENTRYPOINTを指定しない場合は、ベースイメージの設定値が引き継がれる。 ※なおCMDとENTRYPOINTはDockerfileに1つしか記述ができない。複数記述ができれば、もっとも後ろにある設定が採用されそれ以外は無視される。

CMDとENTRYPOINTはコンテナーを起動したときのタイミング(docker startやdocker runするときのタイミングで)コンテナーの中で実行するコマンドを指定するもの ※docker run ではコンテナーの中で実行するコマンドを指定し、そのコマンドが終了するとコンテナーが終了するという仕組みだったことを思い出す。

  • ENTRYPOINT 指定した場合、docker runの最後に指定するコマンドは、このENTRYPOINTで指定したコマンドへの引数となる。

  • CMD

EXPOSE

docker run する際、-pオプションだけを指定し、ポート番号を省略した時にEXPOSEで指定したポートのマッピングが行なわれるようになる。

ARG [変数名]

Dockerfile内で使用する変数名を指定する。 docker-compose.yml内で指定するのが入ってくる。

ENV[変数名=値]

Dockerイメージで使用する環境変数を指定する。 ENVを使って設定した環境変数はイメージからコンテナーへ渡させる。 コンテナーへ渡させると、コンテナー内で起動したアプリケーションで参照できる。

ENVを使って設定した環境変数はイメージからコンテナーへ渡せる

apk

参考URL

Alpine Linuxで使用できるパッケージ管理コマンド(Alpine Linux package managementの略) Linuxコマンドのapt-getが使用される場合は、ベースイメージがAlpineではないということ。


Dockerfileとキャッシュ

docker buildはDockerfileに1行1行のビルド行程をキャッシュする。 そしてそこまでに変更がなければキャッシュが使われる。

docker build する際、キャッシュを使うかどうかは次の基準で決まる。

  1. FROMで指定しているベースイメージのキャッシュが変わった
  2. Dockerfile自体の命令が変わった
  3. ADDやCOPYしているファイルの対象が変わった

キャッシュ判断されないもの たとえば、aptコマンドでApacheやPHPのパッケージをインストールしているが、ApacheやPHPのパッケージがアップデートされたかどうかは、キャッシュの判断基準ではない。 RUNコマンドでどこかからのサイトからのファイルをダウンロードしている場合、その対象ファイルが更新されたかどうかまでを判定するものでありません。

キャッシュを活用してビルドを高速化する

たくさんのパッケージをインストールし、内部でコンパイルするようなコンテナーを作ると、docker buildするときとても時間がかかる。 そのような時は、キャッシュを活用すると高速化できる。 RUNコマンドをあえて複数に分ける。そこで別のキャッシュが作られるため、変更されていない部分はそのRUNコマンドの実行を飛ばすことができ高速化できる。

Dockerをさらに早める

Docker Daemonへの転送ファイル削除

Docker image指定

FROM [:TAG|@DIGEST]

DIGEST ダイジェストを使うとimageを特定できる。 $ docker image ls --digests で表示が可能。

v2以降の形式を使うイメージには、digestと呼ばれる識別子が振られている。 ダイジェスト値は、イメージ生成後に変更が加えられれば値は変わる。


Docker クリーンアップ

参考URL

Dockerは使用していないオブジェクト(イメージ・コンテナー・ボリューム・ネットワーク)に対するクリーンアップには慎重なアプローチを取っている。 ※すなわち各オブジェクトをDockerに対して明示的に削除を命令しない限り、削除はしない。 →その結果Dockerは巨大なディスク容量を使うことになった。

dangling image(宙ぶらりイメージ) : ダングリングイメージ

参考URL

  • latestタグ これは最新のイメージを意味し、現在使用しているイメージとなる

  • noneタグ noneタグとは、同じイメージの中で、最新ではないイメージを指す。

Dockerはイメージを作り替えると、過去のイメージを保管したまま新たなイメージを作成する。 これではどんどんイメージが蓄積され、 PCのメモリを大きく消費してしまう。

dangling状態のイメージは特別な理由がない場合には不要。

<none> とは何? Dockerでは同一名のイメージを複数作成できない。 すでに存在するイメージと同一名のDockerイメージを作成しようとした場合は、次のような挙動となる。

最新のビルド結果のイメージが、指定のイメージ名称となる。(上記例の場合はABCDEFG) すでに存在していた同一名称のイメージは、<none> という名称未設定の状態に置き換わる この症状のことを、dangling(宙ぶらりん)という。

削除後、<none> のimageが消えている。

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
neams_api latest f86551e138bc 11 minutes ago 600MB
neams_front latest a665671491c1 12 hours ago 589MB
wordpress latest b6306a6a7068 4 weeks ago 616MB
mysql 5.7 738e7101490b 6 weeks ago 448MB
doc_swagger-merger latest 6421e7fee9e3 6 months ago 118MB

コンテナーのprune

コンテナーを停止しても --rm フラグを付けて起動していなければコンテナーは自動に削除されない。 停止しているコンテナーの書き込み可能なレイヤーは、ディスク容量を消費し続ける。 これらを綺麗に片付けるには $ docker container prune コマンドを使用する。


Docker コア数確認

# コンテナーに入る
$ docker-compose run --rm [service name] /bin/sh

# コア数確認
$ nproc
2

Docker log ディレクトリ確認

参考URL

Tips

Docker Buildにおけるリードタイム短縮 参考URL

Dockerイメージ分析ツール dive

Docker内のブラウザをホストで起動する


docker image選び

参考URL

利用するDocker imageを選ぶ際に、同じバージョンであっても後ろに -stretch やら -buster やらがついていて迷うことがある。 これはimageがベースとしているOSの種類によるものです。

stretch/buster/jessie/bullseye

これらの文字列がついたimageは、それぞれ同名のDebian Releaseをベースに構築されたものです。 コードが特定のDebian Releaseやバージョンと互換性を持つ特殊な場合を覗いて、基本的にどれを選んでも大差ありません。

slim

-slim がついたimageはフルイメージの下位互換バージョンです。インストール済みのパッケージは使用頻度の高いものに限定され、その分軽量化されている。 軽量のimageが望ましい場合に採用する。 ただし前述のようにフルイメージに存在するパッケージが一部存在しないため、正しく動作するか十分なテストが必要。

alpine

-alpine がついたimageは、Alpine Linuxをベースに構築されたもの。 Alpine Linuxは、コンテナーで利用することを想定して設計されたOSで、極めて軽量。 Alpineのベースイメージは5MB未満と非常に小さく収まっている。

コンテナーを可能な限り最小で最速で構築したい場合に採用が適してる。

一方で欠点もあります。ベースOSが大きく異なるため、主要なパッケージ、コマンドのいくつかがないか、あるいは類似の別のものを利用する必要があります。
たとえば、aptの代わりにapkでパッケージをインストール(結構古いバージョンがが入ります)、glibcでなくmusl libが入っている、等。 また、一部Debianとの互換性を持つPythonパッケージは、Alpineベースのimageで動作するように再コンパイルしなければ動作しません。

とはいえ、それが大きな問題にならないのであれば、フルイメージを使うよりも、alpine imageにapkで必要なパッケージのみをインストールして利用した方が軽量かつ高速で環境を構築できます。
ただし、正常に挙動するか、入念なテストを行う必要があります。

結局どれ

  • 十分なテストをする余裕がなく、環境を素早く立ち上げなければいけない場合、stretch, buster, jessie, bullseyeあたりを適当に選ぶ。

  • コンテナーを軽量化する必要があり、最小限のパッケージだけで動作することが分かっていれば、slimを採用する。

  • コンテナーを限界まで軽量化する必要があり、十分なテストが可能であればalpineを採用する。ただし、移植等した際に挙動がおかしくなる可能性がある。

  • 頻繁にimageを取得するユースケースであれば、slimやalpineを優先的に採用する 私はCIを爆速で回したいのでalpineで頑張ってみるのが好きですが、すんなりうまく行くことは少なく、結局調べたり乗り込んでパッケージを入れたりしてやっとこさ成功する。


scriptsに分ける

キャッシュを

security

安全性を高めるために バイナリダウンロード元にチェックサムや署名などの情報があるのであれば、Dockerfileの中で検証ロジックを入れておくと良い、


M1 macでのdocker

参考URL

Docker マルチCPUアーキテクチャ

参考URL 参考URL

Docker HubにおけるマルチCPUアーキテクチャサポートとは、x86 (AMD64) やARM64など複数のアーキテクチャ向けのイメージを**同一のイメージ名・タグ名で管理できる仕組み Docker Hubで公開されている公式イメージの多くはマルチCPUアーキテクチャに対応している。

公開されているイメージが「マルチCPUアーキテクチャ」に対応している場合、Docker Hubからプルを実行した際に、とくにアーキテクチャを明示しなくても自動的に適切なアーキテクチャのイメージがダウンロードされます。

db:
# これを指定しなくてもM1であれば自動ダウンロード?
platform: linux/amd64
image: mysql:5.7

health check

実行するコマンドはコンテナが正常に稼動していれば終了コード「0」を、そうでなければ「1」を返すようになっている必要がある。これさえ守っていれば、実行するコマンドはバイナリでもスクリプトでも構わない。