>100 Views
August 13, 26
スライド概要
2026年エンジニア新人研修の資料です。
サイボウズ株式会社の主に開発本部の資料を公開するアカウントです。
開運研修2026 Docker入門 Kubernetes基盤 石井 正將 1
研修後どうなってほしいか ▌この研修のゴール 1. コンテナの用途と仕組みを理解できる 2. コンテナランタイムやコンテナオーケストレーションの概要を知っている 3. ベストプラクティスの存在を知り、調べるための手がかりを持っている ➢今日の内容をすべて暗記する必要はありません。 「こういう仕組みがある」「推奨される方法がある」ということを、頭の片隅に置いておいてもらえ れば十分です。 2
目次 1. コンテナの目的 2. コンテナの仕組み 3. コンテナランタイム 4. コンテナオーケストレーション 5. Dockerfileの書き方 6. ベストプラクティス(Dockerfile編、アプリケーション編) 3
コンテナの目的 4
コンテナとは何か ▌アプリケーションとその依存関係をまとめてパッケージ化し、隔離された環境で実行する仕組み ➢どこでも、誰でも同じ環境でアプリケーションを動かせるようになる 隔離された環境で プログラムを動かす コンテナイメージ プログラム 依存関係 干渉できない コンテナ 別のコンテナ プロセス プロセス ライブラリ・ランタイム 依存関係 ファイルシステム アプリケーションや依存関係(ライブラリ等)を パッケージ化したもの OS 5
用語の整理 ▌プログラムや依存関係をパッケージ化したものを「コンテナイメージ」と呼びます ▌コンテナイメージを実際に起動したものが「コンテナ」です クラスとインスタンス(オブジェクト)、プログラムとプロセスのような関係。 6
コンテナが使われる場面 ▌次のような目的で使われます 1. 開発環境の統一 ➢ チーム全員が同じ環境で開発できます 2. テスト、CI/CD ➢ 毎回クリーンな環境でテストを実行できます 3. アプリケーションのデプロイ ➢ 開発環境と同じ環境を本番に展開できます 4. マイクロサービスの運用 ➢ サービスごとに独立した環境を用意できます 7
コンテナがなかったら(1) ▌開発環境の構築に時間がかかるかも ビルドができません…… 手順書見た? 最新のビルド環境 新入社員 2バージョン前 ベテラン社員 8
コンテナがなかったら(2) ▌他のプロセスとリソースが競合するかも ファイルがおかしくなった… 新しいサービスをデプロイするぞ! 前から動いている プロセス 新しい プロセス 前から使ってるよ ちょっと借りるね とあるディレクトリ 9
コンテナがなかったら(3) ▌環境の差異で問題が発生するかも テストOK 知らない環境だ… 本番環境だと エラーが出る…… テストOKだね 本番いってみよう! プロセス プロセス 呼び出し ライブラリ v3 ライブラリ v1 開発環境 本番環境 10
コンテナがあれば ▌依存関係ごとデプロイできるため、環境の差異やリソースの競合といった問題を解消できます ▌隔離して実行できるため、他のプロセスからの影響を防ぐことができます 隔離された環境で プログラムを動かす コンテナイメージ プログラム 依存関係 干渉できない コンテナ 別のコンテナ プロセス プロセス ライブラリ・ランタイム 依存関係 ファイルシステム アプリケーションや依存関係(ライブラリ等)を パッケージ化したもの OS 11
サイボウズでのコンテナ事情 ▌ 社内では、開発の様々なフェーズでコンテナが活用されています ⚫ 開発 ➢ Docker、Docker Compose、kind(Kubernetes IN Docker)等を使い 手元でアプリケーションを動かす ⚫ テスト ➢ GitHub Actionsのコンテナ上でテスト ⚫ 運用 ➢ Kubernetesで本番サービスを運用 12
コンテナの仕組み 13
コンテナの仕組み ▌ コンテナは、次のLinuxの機能を組み合わせて実現されています 1. Namespace 2. Overlay FileSystem 3. cgroups 14
Namespace(名前空間) ▌プロセスから見えるシステムリソースを制限する仕組みです ▌PID namespace、Network namespace など、さまざまな種類があります ホストから見ると コンテナ1の見える世界 コンテナ2の見える世界 PID Namespace PID 1 … プロセス1 PID 2 … 子プロセス1 (他のプロセスは見えない) PID Namespace PID 1 … プロセス2 PID 2 … 子プロセス2 (他のプロセスは見えない) Network Namespace eth0: 192.168.100.1 port 80 (他のネットワーク設定は見えない) Network Namespace eth0: 192.168.200.1 port 80 (他のネットワーク設定は見えない) Linux PID 100 … プロセス1 PID 101 … 子プロセス1 PID 102 … プロセス2 PID 103 … 子プロセス2 PID Namespaceの中と外では、 見えるPIDが変わる Network Namespaceによって、 コンテナごとに個別のネットワークの設定ができる ポートの被りを気にしないでよい 15
Overlay FileSystem ▌複数のディレクトリを重ね合わせて、一か所に統合してマウントする仕組みです ▌コンテナイメージのレイヤー構造を実現するために使われています 書き込み可能 hoge/ 読み取り専用(1) piyo/ file1.txt Overlay FSで結合 fuga/ file1.txt file2.txt 読み取り専用(2) fuga/ file2.txt 読み込み専用のディレクトリの内容が重ね合わせて見える 書き込んだファイルは、書き込み可能ディレクトリに出力される 16
cgroups ▌プロセスが使用できるCPUやメモリなどのリソース量を制限する仕組みです ⚫ CPUを使いすぎた場合 ➢ スロットリング(一時的にプロセスの実行が抑制される) ⚫ メモリを使いすぎた場合 ➢ OOM Killerによって強制終了される 17
中間まとめ ▌コンテナとは、隔離された環境でプロセスを動かす仕組みです ▌以下のようなLinux機能を組み合わせて実現されています ⚫ Namespace ➢ プロセスから見えるリソースを制限する ⚫ Overlay FileSystem ➢ コンテナイメージから専用のルートファイルシステムを用意する ⚫ cgroups ➢ リソースの過剰使用を防ぐ ※ ここで挙げた以外にも、多くのLinux機能が活用されています 18
コンテナと仮想マシン ▌コンテナと仮想マシンはよく比較されます ▌システム構成図では似たように描かれる 仮想マシン 仮想マシン プロセス プロセス プロセス プロセス コンテナ コンテナ ファイルシステム ファイルシステム プロセス プロセス OS OS ファイルシステム ファイルシステム ハイパーバイザー Linux H/W コンテナの場合 ここが違う OS H/W 仮想マシンの場合 19
コンテナと仮想マシンの比較 ▌コンテナ ⚫ Linux上で直接プロセスを動かす ⚫ Linuxカーネルの機能で他のプロセスから隔離する ⚫ ホストとカーネルを共有するため、リソース効率が高く、起動が速い ▌仮想マシン ⚫ 仮想的なH/Wを用意し、ホストOSと異なるOS(ゲストOS)を動かす ⚫ H/Wの仮想化支援機能を使うため、隔離レベルが高い ⚫ ゲストOSの動作にメモリやディスクが必要で、起動も遅い ⚫ ホストOSと異なるOSを動かせる、H/Wのエミュレーションをする場合もある 20
コンテナのメリット 仮想マシンと比べて ▌エコシステムが充実している ⚫ コンテナイメージを共有するための仕組み(コンテナレジストリ)が整備されている ⚫ コンテナを前提としたツールが豊富に揃っている ⚫ CI/CDとの親和性が高い ▌ マイクロサービスとの相性が良い ⚫ リソース効率が高く、小さなサービスを多数動かす構成に適している 21
MacやWindowsでのコンテナ ▌ここまで説明した仕組みはLinuxの機能を利用したものです これらの機能はmacOSやWindowsには存在しません ▌macOSやWindowsでは、内部で軽量なLinux仮想マシンを起動し、 その上でコンテナを動かしています 22
コンテナでサービスを運用する場合 ▌本格的なサービスを運用するには、複数のコンテナやホストとの連携が必要になります ▌連携を効率よく管理するために、コンテナオーケストレーションツールが生まれました 23
コンテナの仕組み まとめ ▌コンテナとは、Linuxの機能を組み合わせて隔離環境で起動したプロセスのことです ▌リソース効率が高く、マイクロサービスとの相性に優れています ▌ツールやサービスなどのエコシステムが充実しています ▌macOSやWindowsでは、内部で軽量な仮想マシンを利用して実現しています ▌実運用では、多数のコンテナを連携させて使います 24
コンテナランタイム 25
コンテナランタイムとは ▌コンテナを起動、管理するためのソフトウェアです ⚫ 代表例: Docker、containerd、runc コンテナランタイム Docker コンテナの操作 containerd runc コンテナ 起動 プロセス ファイルシステム ※ Docker はコンテナランタイムと呼ばれることがありますが、実際にはそれ以上の多くの機能を備えており、厳密にはこの呼称は正確ではありません。 本資料では、わかりやすさを優先して「コンテナランタイム」と表現しています。 26
コンテナランタイムの2つの層 コンテナランタイムは、役割によって2つの層に分かれています 1. 高レベルランタイム ⚫ コンテナイメージの管理や、低レベルランタイムの呼び出しを担当 ⚫ 例: Docker、containerd 2. 低レベルのランタイム ⚫ コンテナのプロセス起動(Namespaceの設定など)を担当 ⚫ 例: runc、gVisor、Kata Containers ⚫ OCI(Open Container Initiative)によって仕様が標準化されているため、 異なるランタイム間でも同じコンテナイメージを使用できます 27
主流のコンテナランタイム ▌開発環境としてはDockerが普及しています ⚫ 情報が豊富で、学習コストが低い ⚫ Docker Desktopを使えば、macOSやWindowsでも手軽に利用できる ▌ただし、一定規模以上の組織ではDocker Desktopが有料となります ▌業務で必要になるまでは代替ツールを使う選択肢もあります (別の講義で説明があります) 28
Dockerの構成要素 ▌Dockerは複数のコンポーネントで構成されています 1. Dockerクライアント(CLI) ⚫ ユーザが実行するコマンド 2. Dockerデーモン ⚫ CLIから命令を受け取り、コンテナイメージのビルドやコンテナの起動、管理を行う 3. containerd、runc ⚫ Dockerデーモンの指示を受けて、コンテナを実際に起動、管理する 4. コンテナイメージ ⚫ アプリケーションとその依存関係をまとめたパッケージ 5. コンテナレジストリ ⚫ コンテナイメージを保存、共有する外部サービス 6. Dockerfile ⚫ コンテナイメージのビルド手順を記述したテキストファイル 29
Dockerの構成(1) ▌コンテナを起動する場合 1. ユーザが、Docker CLIでコンテナを操作する 2. Docker CLIの命令を、Dockerデーモンが処理する 3. 内部的には、containerdがコンテナのライフサイクルを管理し、 runcが実際にコンテナのプロセスを起動する Docker デーモン コンテナの操作 Docker CLI containerd コンテナ 起動 プロセス ファイルシステム runc ホスト 30
Dockerの構成(2) ▌コンテナイメージをビルドしたりする場合 1. Dockerfileからコンテナイメージをビルドする コンテナレジストリ 2. 作成したイメージはコンテナレジストリにdocker pushで送り 別の環境からdocker pullで取得できます イメージ コンテナ イメージ docker push Dockerfile & ソース等 docker pull docker build Docker デーモン コンテナイメージ コンテナイメージの操作 Docker CLI ホスト 31
コンテナレジストリ ▌コンテナイメージの保存や管理、配布を行うサービスです ⚫ 代表例: Docker Hub、GitHub Container Registry(GHCR) ▌コンテナイメージを共有することで、誰でも、どこでも同じ環境を再現できます 32
コンテナイメージ ▌プログラムとその依存関係をまとめたパッケージです ▌複数のレイヤーが積み重なった構造になっています ➢ この「レイヤー構造」は、この後の説明でも重要なポイントになります レイヤー(1):Ubuntuの標準パッケージ レイヤー(2):依存関係とか レイヤー(3):プログラムの実行ファイル コンテナイメージ 33
コンテナオーケストレーション 34
コンテナオーケストレーションとは ▌コンテナオーケストレーションとは ⚫ 多数のコンテナの管理を自動化する仕組み ⚫ アプリケーションのデプロイやスケーリングをしてくれる ▌「オーケストレーション(orchestration)」とは、 複数の要素を組み合わせて調整・指揮することを意味します ➢本格的なサービスの運用では、多数のコンテナを組み合わせる必要があり、 コンテナオーケストレーションツールが不可欠 35
代表的なコンテナオーケストレーションツール ▌Docker Compose ➢ 1台のホスト上で、複数のコンテナを組み合わせて実行する ▌Kubernetes ➢ 複数のホストにまたがって、多数のコンテナを管理、運用する 36
Docker Compose ▌1台のホスト上で、複数のコンテナを組み合わせて実行できます ▌コンテナ間のネットワークや、ホストとのボリューム共有をシンプルに設定できます ▌コマンド1つで、複数コンテナからなる環境をまるごと再現できます コマンド一発で 環境を再現できる コンテナ (Web) コンテナ (AP) コンテナ (DB) nginx アプリケーション MySQL ホストとの ボリュームの共有 コンテナ間のネットワーク データ ホスト 37
複数のホストを扱いたい場合 複数のホストで冗長化したい場合は、Docker Composeとは別の仕組みが必要です ▌Docker Swarmモード(参考) ⚫ Dockerに組み込まれたオーケストレーション機能 ⚫ Docker Composeと互換性のある設定で複数ホストを扱える ⚫ ただし、現在は開発が活発ではない ➢複数ホストでの運用は、Kubernetesがデファクトスタンダードとなっています 38
Kubernetes(1) ▌複数のノード(サーバ)にまたがって、多数のコンテナを管理する仕組みです ▌名前が長いため「K8s」と略されることも多いです ▌宣言的な設定が特徴 ⚫ 「あるべき状態」を記述するだけで、システムが自動的にその状態を維持してくれます ➢詳細は別の研修で扱います 39
Kubernetes(2) ▌Kubernetesの概要 ⚫ 「Control Plane」と「Node」でクラスタを構成 ⚫ Control Planeがリソースの状況に応じて自動で配置を最適化し、 Node上で「Pod」(コンテナ)を動かします リソースが空いてるから Node(1)とNode(3)で動かすで 冗長構成で デプロイするぞ! Control Plane k8sクラスタを管理する サーバ YAML形式 の設定 Pod(コンテナ) レプリカ(1) Node(1) Pod(コンテナ) レプリカ(2) Node(2) Node(3) コンテナを動かすサーバ Kubernetes クラスタ 40
Kubernetes(3) ▌Kubernetesでは、複数のコンテナをひとまとめにした単位を「Pod」として扱います ▌Pod内のコンテナはネットワークを共有します ▌PID Namespaceやルートファイルシステムは、コンテナごとに独立しています Pod (クラスタIP: 10.20.30.40、Pod固有のIP) コンテナ1 コンテナ内のプロセスは PID 1から始まります プロセス (PID 1) … コンテナ2 プロセス (PID N) コンテナ1用のRoot FS ルートファイルシステムは コンテナ毎に独立しており、 再起動するとリセットされます / + /bin + /etc + /hoge/piyo プロセス (PID 1) … プロセス (PID M) コンテナ2用のRoot FS / + /bin + /etc + /foo/bar 41
DockerとKubernetesの関係 ▌以前はKubernetesがDockerを呼び出してコンテナを操作していました ▌Dockerの機能が大きくなりすぎたため、コンテナ管理部分がcontainerdとして切り出され、 現在はKubernetesがcontainerdを直接操作しています Docker CLI Docker デーモン Control Plane kubelet (Node上で動くデーモン) 呼び出す 呼び出す Pod containerd 起動 runc コンテナ kubectl (CLIツール) 起動 runc プロセス ホスト Dockerの構成 containerd コンテナ コンテナ プロセス プロセス Node DockerもKubernetesも、 内部でcontainerdを呼び出している Kubernetesの構成 42
コンテナオーケストレーション まとめ ▌本格的なサービスを運用するには、多数のコンテナを組み合わせる必要があります ▌コンテナオーケストレーションツールを使うことで、多数のコンテナを効率よく管理できます ⚫ Docker Compose: シングルホスト向け ⚫ Kubernetes: マルチホスト向け 43
Dockerfileの書き方 44
Dockerfile ▌コンテナイメージのビルド手順を記述したテキストファイルです ▌Dockerfileを用意して「docker build」を実行すると、コンテナイメージが作成されます 45
Dockerfileの書き方 FROM ghcr.io/cybozu/ubuntu:24.04 COPY ./myapp /work/myapp WORKDIR /work/myapp ベースとなるコンテナイメージ(ベースイメージ) を指定する docker build時に実行する操作 RUN make && make install COPY: ホストからコンテナにファイルをコピーする WORKDIR: 作業ディレクトリを指定する RUN: 任意のコマンドを実行する ENV PATH /usr/local/bin docker run時に参照される設定 CMD [“myapp”, “--opt=hoge”] ENV: 環境変数を設定する CMD: デフォルトの実行コマンドを指定する 46
イメージのビルド方法 ▌カレントディレクトリにDockerfileを置いた状態で、次のように実行するとイメージができる $ docker build -t myapp:1.0.0 . Dockerfileのあるディレクトリパスを指定 コンテナイメージのタグを指定 コンテナイメージの名前を指定 47
Dockerfileとコンテナイメージ ▌ベースイメージを元に、命令(COPYやRUNなど)ごとにレイヤーが追加されていきます ➢ レイヤーをOverlay Filesystemで重ね合わせて、 コンテナのファイルシステムを構成している ▌各レイヤーで生成されるファイルが増えるほど、イメージサイズが大きくなります ベースイメージのレイヤー(1) FROM ghcr.io/cybozu/ubuntu:24.04 COPY ./myapp /work/myapp RUN make && make install Dockerfile ベースイメージのレイヤー(2) ベースイメージのレイヤー(3) COPY myapp のレイヤー RUN make のレイヤー コンテナイメージ 48
Overlay Filesystemの動作 ▌コンテナ内でmountコマンドを実行すると、Overlay FSの設定を確認できます ▌ホストでこれらのディレクトリを見ると、レイヤーの中が確認できます (この実行例は見やすさのため、改行、整形しています) lowerdir: 読み取り専用 $ docker run -it --rm ghcr.io/cybozu/ubuntu-debug:24.04 /bin/bash コンテナイメージの各レイヤーに対応します root@8f45aedef6e4:/# mount overlay on / type overlay (rw,relatime, lowerdir=/var/lib/docker/overlay2/l/4GGUTT3N4HBOIYGMCPOPQIWNHJ: /var/lib/docker/overlay2/l/K6P3GKW4EHQ2J2ALQPQAQZW4SM: upperdir: 読み書き可能 … コンテナ内でファイルを更新すると、この領域に記録されます /var/lib/docker/overlay2/l/KLSOYHMW7TEBHXQG4CNOVDKEOD: /var/lib/docker/overlay2/l/EPIZLBCOBVLYBFS2TWQVKJMS2B, upperdir=/var/lib/docker/overlay2/3d2c88014e88a24b6665b7a8ae9ddfcfb8fa4180e1cdc2cc54ae1203292c51fd/diff, workdir=/var/lib/docker/overlay2/3d2c88014e88a24b6665b7a8ae9ddfcfb8fa4180e1cdc2cc54ae1203292c51fd/work) proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755) 49
コンテナベストプラクティス Dockerfile編 50
ベストプラクティス ▌いくつか紹介します ⚫ 信頼できるコンテナイメージを使う(出所不明なイメージは避ける) ⚫ コンテナイメージをできる限り小さくする ⚫ タグを適切に運用する ⚫ ビルドに不要なファイルを.dockerignoreで除外する ⚫ Lint ツールで Dockerfile を静的解析する ⚫ コンテナイメージを定期的にリビルドする ▌Docker公式のベストプラクティスもあるので、そちらも見てください ⚫ https://docs.docker.com/build/building/best-practices/ 51
信頼できるコンテナイメージを使う ▌出所不明なコンテナイメージには、脆弱性や悪意のあるコードが含まれているリスクがあります ▌社内で推奨されているベースコンテナイメージを活用しましょう ▌Docker Official Imagesは比較的信頼性が高いです ▌いずれの場合も、使用前によく調査することが大切です 52
Necoのベースコンテナイメージ ▌PDXチームで、Ubuntuのイメージを管理しています ▌3か月ごとの定期更新および緊急性の高い脆弱性が見つかった場合は即時対応しています ▌いくつか種類があるので用途に応じて使い分けましょう ⚫ ghcr.io/cybozu/ubuntu:24.04 必要最小限のパッケージのみ ⚫ ghcr.io/cybozu/ubuntu-dev:24.04 開発に必要なパッケージを追加 ⚫ ghcr.io/cybozu/ubuntu-debug:24.04 デバッグに必要なパッケージを追加 ⚫ ghcr.io/cybozu/golang:1.26-noble Go 言語の開発環境を追加 53
コンテナイメージをできる限り小さくする ▌コンテナイメージのサイズが大きくなると、ディスク容量を圧迫したり ダウンロードに時間がかかる原因になります ▌イメージを小さくするための主なアプローチは以下の3つです 1. レイヤーを意識してDockerfileを書く 2. マルチステージビルドを活用する 3. サイズの小さなベースイメージを選ぶ 54
レイヤーを意識してDockerfileを書く(1) ▌RUNやCOPYを実行するたびにレイヤーが追加されます ▌中間ファイルを別のRUN命令で削除しても、そのファイルは前のレイヤーに残り続けます ▌結果として、イメージサイズが意図せず大きくなってしまいます FROM ghcr.io/cybozu/ubuntu:24.04 ubuntuイメージのファイル RUN hostname > hostname.txt hostname.txtの追加 RUN date > date.txt data.txtの追加 RUN cat hostname.txt date.txt > id.txt id.txtの追加 RUN rm hostname.txt date.txt 消したつもり 中間ファイルと 削除のレイヤが残る hostname.txtとdata.txtの削除 コンテナイメージ 55
レイヤーを意識してDockerfileを書く(2) ▌複数の処理を1つのRUN命令にまとめ、不要なファイルは同じRUN内で削除します ▌ただし、1つのRUNに詰め込みすぎると可読性が下がるデメリットもあります ▌次に紹介するマルチステージビルドを使う場合は、それほど意識しなくてもかまいません FROM ghcr.io/cybozu/ubuntu:24.04 1つのRUNでいろいろ実行 不要な一時ファイルを削除 RUN hostname > hostname.txt && ¥ date > date.txt && ¥ cat hostname.txt date.txt > id.txt && ¥ rm hostname.txt date.txt ubuntuイメージのファイル id.txtの追加 コンテナイメージ レイヤーが少なく、 中間ファイルも残らない 56
レイヤーを意識してDockerfileを書く(3) ▌docker history コマンドで、イメージのレイヤー構成を確認できます ▌イメージサイズが予想より大きい場合の原因調査に役立ちます Dockerfileで 追加したレイヤー ベースイメージの レイヤー $ docker history ghcr.io/cybozu/ubuntu-debug:24.04 IMAGE CREATED CREATED BY 07ebfbdbc3f0 29 hours ago CMD ["/bin/bash"] <missing> 29 hours ago RUN /bin/sh -c adduser --disabled-password -… <missing> 29 hours ago COPY /go/src/github.com/google/go-containerr… … <missing> 30 hours ago RUN /bin/sh -c apt-get update && apt-get… <missing> 6 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] … <missing> 6 days ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH <missing> 6 days ago /bin/sh -c #(nop) ARG RELEASE SIZE 0B 10.2kB 11.4kB COMMENT buildkit.dockerfile.v0 buildkit.dockerfile.v0 buildkit.dockerfile.v0 34.9MB 0B buildkit.dockerfile.v0 0B 0B 57
マルチステージビルド ▌1つのDockerfileを複数のステージに分けて記述できます ▌ビルド用ステージで生成したファイルを、実行用ステージにコピーします ▌最終ステージのみがコンテナイメージとして保存されるため、イメージサイズを削減できます # Stage1: build from source FROM ghcr.io/cybozu/golang:1.26-noble AS build COPY ./src /work/src WORKDIR /work/src RUN go install ./... Go言語のツール ソース # Stage2: setup runtime container FROM ghcr.io/cybozu/ubuntu:24.04 COPY --from=build /go/bin / ENTRYPOINT [“/myapp”] Ubuntu 中間ファイル、ビルド結果 バイナリ build 最終イメージ 必要なものだけコピー 58
小さなコンテナイメージをベースにする ▌ベースイメージの種類によって、サイズや特徴が大きく異なります ▌用途に応じて適切なものを選びましょう イメージ名 サイズ 特徴 scratch 0B • • libcもshellも含まない空のイメージ。 Go言語などでシングル実行可能なバイナリを作れば、イメージサイズは非常に小さくなり、 OS依存の脆弱性も含まないためセキュア。 busybox 1.2MB • • Linuxのユーティリティをシングルバイナリで提供。 scratch同様、シングル実行バイナリのベースとして使われる。 Alpine Linux 5.6MB • • 軽量なLinuxディストリビューション。 ハマりどころも多いので利用には注意が必要。(Necoでは利用禁止) distroless 16.9MB ~ • • 実行に必要なものだけを持つイメージ。multi-stage buildと組み合わせて使いやすい。 Java, Python, Node.js用などランタイムごとにイメージが用意されている。 cybozu/ubuntu 103MB • PDXチームが管理しているベースイメージ。 59
タグを適切に運用する ▌コンテナイメージのタグは容易に上書きできるため、運用ルールを決めておくことが重要です ▌他者が公開しているイメージを使う場合 ➢ そのタグ運用ルールを事前に確認しましょう ▌自分たちで作成するイメージの場合 ➢ タグ運用ルールを明確に定めましょう 社内(Neco チーム)でのタグ運用ルール ⚫ レジストリにプッシュするイメージには、必ず固定タグを付ける ⚫ 一度付けた固定タグは変更しない ⚫ 利便性のために可変タグ(ブランチ名など)を追加することは可 60
ビルドに不要なファイルを.dockerignoreで除外する ▌ビルドに不要なファイルは .dockerignoreに記述して、転送対象から除外しましょう ▌docker build実行時、Dockerfileと同じディレクトリのファイルが Dockerデーモンに一括転送されます ▌不要なファイルを除外することで、ビルド時間を短縮できます # 除外するディレクトリや # ファイルのパターンを列挙 .git/ logs/* node_modules/ ./README.md 61
LintツールでDockerfileを静的解析する ▌hadolint(https://github.com/hadolint/hadolint)を使いましょう ▌Dockerfileの構文やベストプラクティスへの準拠を静的に解析してくれるツールです 62
コンテナイメージを定期的にリビルドする ▌コンテナイメージは、ビルドした時点の依存関係のスナップショットです ▌アプリケーションに変更がない場合でも、ベースイメージや依存パッケージの セキュリティ更新を取り込むために、定期的なリビルドをお勧めします 63
コンテナベストプラクティス アプリケーション編 64
コンテナアプリケーションのベストプラクティス ▌コンテナをステートレスに設計する ▌1つのコンテナには1つの役割を持たせる ▌ヘルスチェックのエンドポイントを用意する ▌SIGTERMなどのシグナルを適切に処理する ▌ログは標準出力や標準エラー出力へ出力する ▌回復できないエラーが発生したら速やかに終了する ▌設定値はコンテナイメージに含めない 65
ベストプラクティスの背景 ▌コンテナの起動や終了はオーケストレーションツールが管理します ▌アプリの更新やスケールアウト、障害復旧など、さまざまな場面でコンテナは動的に入れ替わり ます ▌オーケストレーションツールの動作を妨げないアプリ設計を心がけましょう 66
コンテナをステートレスに設計する ▌コンテナが突然終了しても、データが失われないように設計しましょう ▌コンテナのファイルシステムは揮発性です コンテナが終了すると、ファイルシステムに書き込んだデータも消えます ▌永続化が必要なデータは、DBやKubernetesのPersistentVolumeなどの 外部ストレージに保存するのが基本です ▌どのコンテナにリクエストが振り分けられても、同じように動作できる設計を目指しましょう 67
1つのコンテナには1つの役割を持たせる ▌WebサーバーやDBなど、異なる役割のプログラムを1つのコンテナに詰め込むのは避けましょう ▌役割ごとにコンテナを分けることで、スケールアウトや障害対応がしやすくなります ▌ログ収集やプロキシなど、メイン処理を補助するコンテナを一緒に動かしたい場合は、 Kubernetesのサイドカーパターンが有効です ▌1つのPodに複数のコンテナを同居させつつ、それぞれの役割を明確に分離できます 68
ヘルスチェックのエンドポイントを用意する ▌オーケストレーションツールは、コンテナが正常に動作しているかを定期的に確認しています ▌ヘルスチェック用のエンドポイントを用意しておくことで、異常なコンテナを自動で検知・再起動 できます ▌DockerのHEALTHCHECK命令や、KubernetesのLiveness Probeを活用して、 コンテナの死活監視を実装しましょう 69
SIGTERMなどのシグナルを適切に処理する ▌コンテナを停止する時、オーケストレーションツールはまずSIGTERMを送信して プロセスに終了を通知します ▌SIGTERMを無視すると、一定時間後に強制終了(SIGKILL)されてしまいます ▌シグナルを受け取ったら、処理中のリクエストを完了してからグレースフルに終了する実装を心 がけましょう 70
ログは標準出力や標準エラー出力へ出力する ▌コンテナのルートファイルシステムは再起動されると消えてしまうので、 ログをファイルに書き出していると、コンテナ終了時にログも失われてしまいます ▌stdoutやstderrに出力することで、オーケストレーションツールやログ収集基盤が自動的に収 集してくれます ▌複数種類のログがありstdoutやstderrだけでは対応できない場合は、Kubernetesの PersistentVolumeなどの永続化ストレージにファイルとして出力しましょう 71
回復できないエラーが発生したら速やかに終了する ▌回復できないエラーが発生した場合、無理に動き続けることは避けましょう ▌異常な状態のまま処理を続けると、不正なデータを生成したり、障害の原因究明が困難にな る恐れがあります ▌オーケストレーションツールに再起動を任せ、速やかに終了するのが適切な設計です ▌ヘルスチェックの失敗や異常終了をツールが検知し、自動で再起動してくれます 72
設定値はコンテナイメージに含めない ▌接続先のURLやAPIキーなど、環境ごとに違う設定値をイメージに埋め込むのは避けましょう ▌設定値は、環境変数やシークレット管理ツールを通じて、実行時に注入するようにしましょう ▌こうすることで、同じイメージを開発やステージング、本番環境で使い回せるようになります 73
まとめ 74
まとめ ▌コンテナとは、依存関係をパッケージ化し、隔離された環境で動かす仕組みです ▌マイクロサービスとの相性が良く、エコシステムも充実しています ▌Docker以外にも、さまざまなコンテナランタイムがあります ▌コンテナの仕様はOCI(Open Container Initiative)で標準化されており、どのランタイ ムでも同じイメージを利用できます ▌Docker ComposeやKubernetesなど、複数のコンテナを連携させるツールが揃っています ▌Dockerfileの書き方やアプリケーション設計には、押さえておくべきベストプラクティスがあります 75
ハンズオン Dockerをさわってみる 76
ハンズオンの内容 ▌コンテナイメージの取得 ▌コンテナの起動 ▌コンテナに入ってみる 77
よく使うDockerコマンド ▌ container run : コンテナの実行 ▌ run : コンテナの実行 ▌ container exec : コンテナ上でのコマンドの実行 ▌ exec : コンテナ上でコマンドの実行 ▌ container ls : コンテナの確認 ▌ ps : コンテナの確認 ▌ container stop : コンテナの停止 ▌ stop : コンテナの停止 ▌ container rm : コンテナの削除 ▌ rm : コンテナの削除 ▌ container logs : コンテナのログを確認 ▌ logs : コンテナのログを確認 ▌ image ls : コンテナイメージの確認 ▌ images : コンテナイメージの確認 ▌ image rm : コンテナイメージの削除 ▌ rmi : コンテナイメージの削除 ▌ image pull : コンテナイメージのpull ▌ pull : コンテナイメージのpull ▌ image build : コンテナイメージのビルド ▌ build : コンテナイメージのビルド ▌ image push : コンテナイメージのpush ▌ push : コンテナイメージのpush ※ Docker 1.13でコマンド体系が整理されました。左が新しい体系、右が旧来のコマンドです。 詳細は docker <COMMAND> --help で確認できます。 78
コンテナイメージの取得 # ローカルのコンテナイメージの表示 docker images # Docker Hubからコンテナイメージを取得する docker pull nginx:latest # 再度、ローカルのコンテナイメージの表示 docker images 79
コンテナの起動 # コンテナの起動 # -d: バックグラウンドで起動 # --name: コンテナに名前をつける(省略した場合は自動で名前がつく) # -p 8080:80: コンテナの80番ポートをホストの8080番ポートにバインド docker run -d --name mynginx -p 8080:80 nginx:latest # 起動しているコンテナの確認 # コンテナの停止・削除 docker ps docker stop mynginx curl localhost:8080 docker rm mynginx docker logs mynginx 80
コンテナに入ってみる # ubuntu-debugコンテナ上でbashを実行する # -i: STDINをオープンし続ける -t: 擬似端末を割り当てる # --rm: コンテナ終了時にコンテナを自動削除 docker run -it --rm ghcr.io/cybozu/ubuntu-debug:24.04 /bin/bash # いろんなコマンドを叩いて、コンテナ内のプロセスやファイルシステム、 # ネットワークの状態などを確認してみる ls ps aux hostname mount ip a 81