TIL

Dockerがよくわからないので再入門した

Dockerについてわからなくなったり忘れたりすることが多いので、自分用の備忘メモをまとめることにした。

あくまで自分用の再入門メモなので、入門者向けにわかりやすくまとめたとかではない。

Dockerについて

Dockerとコンテナ

  • Dockerはコンテナ管理ツール
  • DockerはBuild、Ship、Runの思想で作られている
    • Build: コンテナイメージの作成
    • Ship: イメージの配布
    • Run: イメージを元にコンテナを実行
  • コンテナは隔離されたアプリケーション実行環境
  • Dockerはクライアントサーバ型のアーキテクチャ
    • dockerコマンドはクライアントが提供
    • サーバ側ではDockerデーモン(dockerd)が起動している
    • クライアントはDocker API(WebAPI)を使ってDockerデーモンに処理依頼をする

Build、Ship、Runの思想がJava仮想マシンっぽい。

公式ドキュメント日本語訳プロジェクトが充実しているので、詳しく知りたいときはこれを読む。

コンテナと仮想マシンの違い

  • 仮想マシン
    • ホストOS・ハイパーバイザの上でゲストOSとアプリケーションが実行される
    • 1台の物理マシン上で複数のOSを動作させる
  • コンテナ
    • ホストOSの機能(Linuxカーネル)によってプロセスが隔離されて実行される
    • コンテナ自体にはOSは含まれない

https://geekflare.com/docker-vs-virtual-machine/

コンテナを使うメリット

  • 可搬性のある実行環境が手に入る
  • 異なるマシン上での挙動の再現性が高い
  • 仮想マシンに比べて軽量
  • 開発者ごとに独立した開発環境が作りやすい
  • 数多くの関連サービスやツールと連携できる

開発環境がコンテナになると幸せなことは多いと思うし、実際幸せになった。

DockerはLinux依存的な話

  • DockerはLinuxカーネルが無いと動かない
    • コンテナ環境はcgroups、namespaces、overlayfsといったLinux機能で実現されるため
    • namespacesはシステムリソースの分離を行っている
    • cgroups(コントロールグループ)はCPUやメモリなどのシステムリソースの割り当てを行っている
  • Docker Desktopでは仮想マシン上でLinuxカーネル(moby linuxというらしい)を動かしてる
  • WindowsではWSL2にLinuxカーネルが搭載されたので、Docker DesktopなしでDockerが動く

正直このあたりは全然自信がない。

とりあえず大事そうなのは、DockerはLinux無しでは動かないこと。

あと、Docker Desktop上でCentOSのベースイメージを指定してコンテナを実行しても、実際にCentOSが動くわけではない。あくまでベースイメージに含まれるCentOS相当のディレクトリ構成や実行コマンドによってCentOSっぽく動作するだけで、実際のOSの機能はDocker Desktop上のLinuxカーネルの互換性によって使えている。

イメージはレイヤ構造

  • コンテナイメージはベースイメージからの変更差分が重なったレイヤ構造になっている
    • 後述するDockerfileの1命令(ビルドステップ)ごとに変更差分が発生する
  • レイヤ単位にビルドキャッシュが作られる
    • 変更差分が同じビルドステップはスキップされる
  • DockerfileのRUN命令を&&で繋ぐのはレイヤを減らしたいから
  • コンテナ実行時もレイヤ構造は維持される
    • イメージのレイヤ群は読み取り専用になり、新たに読み書き可能なレイヤが上積みされる
    • コンテナ実行中に作成/削除されたファイルは読み書き可能レイヤに変更差分が積まれる
    • 単一イメージから複数コンテナを生成しても、読み取り専用レイヤのファイル実体は共通なので軽量
  • テキストだとイメージしにくいのでこの記事など図式化されているものを参照するとわかりやすい

OCIランタイム

  • 隔離されたコンテナ環境を作るのはDockerデーモンではなくOCIランタイム(低レベルランタイム)
    • OCIランタイムがcgroupsやnamespacesなどLinuxカーネルの機能を使ってコンテナ環境を作る
    • dockerコマンド -> Dockerデーモン -> OCIランタイム -> Linuxカーネル のイメージ
    • Dockerは高レベルランタイムに相当する
  • OCIはOpen Container Initiativeによって定められた仕様
  • OCIの実装はruncgVisor、Kata Containersなど色々
    • ほとんどの人は意識せずruncを使ってるらしい
    • runcは元々Dockerに組み込まれていたが、現在は分離されている
    • runcについて説明されたスライド

複数コンテナの実行

  • Docker Composeを使用することで複数コンテナを定義してまとめて実行できる
  • Composeではコンテナやネットワーク、ボリュームをまとめて『サービス』という概念で扱う
  • docker-compose.ymlファイルにアプリケーション構成を定義する
    • 1つの設定ファイルは1つのプロジェクト名を持つ
  • Composeのネットワーク機能により、コンテナ間はサービス名を指定して通信できる
  • 例によってだいたい公式マニュアルに書いてある

アプリケーションサーバとDBサーバなど、複数のコンテナを連動させたい時はCompose使った方が楽。

使い方関連

Dockerfile

コンテナイメージを作る手順書がDockerfile

命令説明
FROMベースイメージを指定する
RUNイメージ作成時に実行するコマンドを指定する
WORKDIR作業ディレクトリを指定する
COPYファイルやディレクトリをコピーする
ADDファイルやディレクトリをコピーする
ENTRYPOINTコンテナ実行時の実行コマンドを指定する
CMDコンテナ実行時の実行コマンドを指定する
VOLUMEコンテナ実行時に匿名ボリュームを作る
ENV環境変数を設定する
  • RUN\で複数行に分割して書くとよい
    • だいたいapt-getしてパッケージインストールする用に使う
  • WORKDIRRUN mkdir hoge && cd hogeだと考えるとよい
  • ADDCOPY
    • ADDよりCOPYの方が単機能で明確なのでCOPY使用を推奨(公式もそう言ってる)
    • ADDだとコピー元をリモート資源にできたり、圧縮ファイルが自動解凍されたりして怖い
  • ENTRYPOINTCMD
    • ENTRYPOINTCMDはDockerfileで1度だけ指定できる
    • ENTRYPOINTCMDが両方書かれた場合、CMDENTRYPOINTのコマンド引数になる
    • CMDはコンテナ実行時のコマンド引数で上書きができる
  • VOLUMEは難しいので公式とかこの記事を読む
  • FROMを複数指定することで、中間生成物をパージして最終成果物のイメージを軽量化するマルチステージビルドという手法がある

これどう使えばいいの?的な疑問はほとんど公式のベストプラクティスに書いてある。

コマンド

  • Dockerコマンドは操作対象ごとにサブコマンドが用意されている
  • よく使う操作対象
    • docker image
    • docker container
    • docker network
    • docker volume
    • docker compose
  • create,rm,prume,lsあたりは共通して使えることが多い
  • 互換性維持のために旧コマンドも一応使える
    • 新: docker image ls
    • 旧: docker images

コマンドはたくさんあるので使用頻度高そうなものだけ載せておく。

イメージ

コマンド説明
docker image buildDockerfileからイメージをビルドする
docker image lsイメージ一覧を表示する
docker image prune使用していないイメージを削除する
docker image pullイメージをレジストリから取得する
docker image pushイメージをレジストリに送信する
docker image rmイメージを削除する
docker image saveイメージをtarアーカイブに保存する
  • -fでDockerfileを指定できる。デフォルトはカレントのDockerfile
  • --no-cacheでビルドキャッシュを使わずに構築する
  • --platformでプラットフォームが指定できる
  • --tagまたは-tでタグとイメージ名を付与できる

コンテナ

コマンド説明
docker container run新しいコンテナを作成して実行する
docker container exec実行中のコンテナ内でコマンドを実行する
docker container lsコンテナ一覧を表示する
docker container start停止中のコンテナを実行する
docker container stop実行中のコンテナを停止する
docker container rmコンテナを削除する
docker container prune停止中の全コンテナを削除する
docker container cpローカルとコンテナ内でファイルをコピー
docker container attachローカルの標準入出力に実行中コンテナを接続
docker container logsコンテナのログを取得
  • runのオプション
    • --name 任意のコンテナ名を付けられる
    • --rm コンテナ終了時にコンテナを自動で削除
    • -it コンテナ内でbashシェルを作成する(コンテナ内で動き回れる)
      • -iがインタラクティブ、-tが疑似TTYを意味する
    • -v ボリュームのマウントを指定する

Compose

コマンド説明
docker compose buildサービスの構築(イメージのビルド)
docker compose upサービスを作成し実行する
docker compose downサービスを停止して削除する
docker compose runサービスを1回限りのコマンドとして実行する
docker compose exec実行中のサービス内でコマンドを実行する
docker compose logsコンテナのログを表示する
  • -fオプションでdocker-compose.yml以外の設定ファイルを指定できる(複数指定可)
  • downは停止だけでなくコンテナやネットワークの削除をしてくれる、便利
  • runの実行後にコンテナを削除したい場合は--rmオプションを付ける

その他

  • Dockerはオブジェクトの削除に対して慎重な指針で作られているため、不要物の削除は明示的に行う必要がある
    • 詳しくはドキュメントを参照すればよいが、基本的にはpruneコマンドを使う
    • pruneは使われていないオブジェクトを削除するだけで、すべてのオブジェクトが消えるわけではない

参考文献

https://docs.docker.com/

https://docs.docker.jp/index.html

https://gihyo.jp/book/2020/978-4-297-11837-2

https://zenn.dev/fagai/articles/55c1b34172ca5a0bce09

https://knmts.com/become-engineer-23/

https://www.itbook.info/network/docker06.html

https://qiita.com/tajima_taso/items/28938415846dcc2e83ff


ちょっと箇条書きにまとめようかな、くらいの気持ちで始めたら結構長くなってしまった。

ネットワークやボリュームについても調べたかったが、世界が広がりすぎて大変そうだったので別の機会に勉強し直すとする。


<< 記事一覧へ