コンテナとDockerの基本知識の習得

「Dockerって名前はよく聞くけど、結局何が便利なの?」「自分の開発には関係ないんじゃないか…」そう思っていませんか?もしかしたら、すでにDockerを使ったことがある方も、その真の価値をまだ十分に理解していないかもしれません。

 

  1. 第一章:なぜ今、コンテナとDockerなのか?
    1. コンテナとDocker
      1. コンテナとは?
      2. Dockerとは?
    2. コンテナ化の3つの大きなメリット
      1. メリット1: 環境の統一と再現性の確保
      2. メリット2: 開発サイクルの高速化
      3. メリット3: 運用管理の効率化
    3. コンテナ技術はもはや「標準」
    4. まとめ:今日から始めるコンテナ開発の第一歩
  2. 第二章:Dockerの基本コマンド総ざらい:イメージとコンテナを徹底理解
    1. 0.Docker Desktopのインストール
    2. 1. コンテナのライフサイクルと基本コマンド
    3. 2. まずは「イメージ」を操るコマンド
      1. docker pull : イメージのダウンロード
      2. docker images : ローカルイメージの一覧表示
      3. docker rmi : イメージの削除
    4. 3. 次に「コンテナ」を操るコマンド
      1. docker run : コンテナの作成と起動
      2. 実際の開発でよく使う docker run オプション
      3. docker ps : 実行中のコンテナ一覧表示
      4. docker start / docker stop : コンテナの起動と停止
      5. docker rm : コンテナの削除
    5. 4. 実行中のコンテナをデバッグする
      1. docker exec : 実行中のコンテナ内でコマンドを実行
      2. docker logs : コンテナのログを見る
    6. 5. まとめ:
  3. 第三章:Dockerfileの書き方から学ぶ、軽量で堅牢なコンテナイメージの作り方
    1. 1. Dockerfileの基本構成とビルド
      1. Dockerイメージのビルド
      2. .dockerignoreファイルの活用
    2. 2. Dockerfileの主要な命令とベストプラクティス
      1. FROM <image>:<tag>
      2. RUN <command> とレイヤーキャッシュ
      3. COPY <src> <dest> と ADD <src> <dest>
      4. EXPOSE <port>
    3. 3. セキュリティとパフォーマンスを考慮した実践テクニック
      1. ① マルチステージビルド
      2. ② 非rootユーザーで実行
      3. ③ 環境変数の活用
      4. ④ Dockerfileの健全性をチェック
    4. 4. まとめと次へのステップ
  4. 第四章:ローカル開発環境をDocker Composeで構築しよう
    1. 1. Docker Composeとは?
    2. 2. docker-compose.yml ファイルの基本構造
    3. 3. サンプルプロジェクトで学ぶ実践的な使い方
      1. プロジェクトのファイル構成
      2. .env ファイルの活用
      3. docker-compose.yml の定義
      4. depends_on と healthcheck
    4. 4. Docker Composeコマンドと実践向けTips
      1. 開発時の典型的なワークフロー
    5. 5. よくある問題と解決策
    6. 6. まとめ:Docker Composeでローカル開発は劇的に変わる
  5. 第五章:ボリュームとネットワーク:コンテナ間でデータを共有し、通信する方法
    1. 1. ボリューム:コンテナの「揮発性」を乗り越える
      1. ローカル開発におけるボリューム
      2. セキュリティと永続化を意識したボリューム設定
      3. AWS上でのボリューム:本番環境のベストプラクティスとコスト
    2. 2. ネットワーク:コンテナ間の通信を司る
      1. Docker Composeにおけるネットワーク
      2. AWS上でのネットワークとセキュリティ
    3. 3. データベースの永続化とパフォーマンス
    4. 4. 監視、ロギング、そしてトラブルシューティング
      1. 本番環境を意識した設定
      2. よくある問題と解決法
    5. 5. まとめ:実践的なDocker運用へ
  6. 第六章: 実践!既存アプリケーションをDocker化してみよう 🚀
    1. 1. 今回のお題:シンプルなPython Webアプリケーション
      1. プロジェクトのファイル構成
      2. src/app.py
      3. src/requirements.txt
    2. 2. ステップ・バイ・ステップ Docker化プロセス
      1. ステップ1: Dockerfile を作成する
      2. ステップ2: docker-compose ファイルを作成する
      3. ステップ3: 環境設定ファイルを作成する
    3. 3. 本番運用を意識したログ管理とセキュリティ
      1. ログ管理
      2. セキュリティスキャン
    4. 4. まとめ
  7. 第七章:第1週のまとめとスキルチェックテスト 📝
    1. 1. スキルチェックテスト
      1. 課題1: コンテナとイメージの基本操作
      2. 課題2: Dockerfile の作成
      3. 課題3: Docker Compose でのマルチコンテナ構築
      4. 課題4: トラブルシューティング
    2. 2. 解答と解説
      1. 課題1: 解答
      2. 課題2: 解答
      3. 課題3: 解答
      4. 課題4: 解答
    3. 3. 実践的なデバッグスキルとセキュリティ
    4. 4. まとめ:

第一章:なぜ今、コンテナとDockerなのか?

コンテナとDocker

コンテナという概念と、それを使いやすくしたDockerというツールです。

コンテナとは?

コンテナは、アプリケーションのコードとその実行に必要なすべての依存関係を一つにまとめた、軽量で自己完結型のパッケージです。

これを、アパートの一室に例えてみましょう。

  • 従来の開発: 部屋を借りるたびに、家具や設備をすべて自分で揃える必要がありました。
  • コンテナ: 家具や設備がすべて整った「サービスアパートメント」を借りるようなものです。どこに持っていっても、すぐに快適な生活を始められます。

コンテナはホストOSのカーネルを共有します。つまり、LinuxコンテナはLinuxカーネル上でしか動きません。WindowsやMacでは、Docker Desktopが仮想マシンを裏側で用意し、この制約を吸収しています。

Dockerとは?

コンテナという概念を、誰でも簡単に扱えるようにしたのがDockerです。Dockerはコンテナを「誰でも使える便利なツール」に変えた革命的な存在です。

なお、Dockerはコンテナを普及させた立役者ですが、現在は**OCI(Open Container Initiative)**という標準仕様が整備され、KubernetesやAWSのECS/EKSもこの標準に準拠したコンテナを扱います。


コンテナ化の3つの大きなメリット

Dockerとコンテナを導入することで、開発・運用プロセスはどのように変わるのでしょうか?主に以下の3つの大きなメリットが生まれます。

メリット1: 環境の統一と再現性の確保

これがコンテナ技術の最大の利点です。「Build once, Run anywhere」(一度ビルドすれば、どこでも動く)という原則が実現します。

開発者のPC、テスト環境、本番環境、どの環境でもまったく同じコンテナイメージを使ってアプリケーションを実行できます。これにより、「本番環境でだけエラーが出る」といった環境依存のトラブルが劇的に減少します。

メリット2: 開発サイクルの高速化

  • 素早い起動: コンテナは数秒で起動するため、開発中にアプリケーションを再起動して変更を試すプロセスが非常にスムーズになります。
  • 容易な共有: 開発者が作成したコンテナイメージを、チームメンバーやQA担当者と簡単に共有できます。
  • CI/CD(継続的インテグレーション/継続的デリバリー)との相性: コンテナはCI/CDパイプラインに完璧にフィットします。

メリット3: 運用管理の効率化

サーバーの運用・管理もコンテナによって大きく変わります。

  • リソースの効率的利用: 1台のサーバー上で複数のコンテナを動かすことで、リソースをより効率的に利用できます。
  • マイクロサービスとの親和性: アプリケーションを独立した小さなサービスに分割する際に、コンテナは理想的なパッケージング単位となります。マイクロサービスは、チームごとに独立して開発・デプロイできるため、大規模開発でスピードと柔軟性を維持する手法として注目されています。
  • スケーラビリティの向上: アクセスが増加した際、同じコンテナイメージを使って新しいコンテナを起動するだけで、簡単にアプリケーションをスケールアウト(水平に拡張)できます。

コンテナ技術はもはや「標準」

Dockerが普及して以降、コンテナはソフトウェア開発のあらゆるシーンで活用されるようになりました。特にクラウドネイティブ(クラウドでの利用を前提とした開発)の領域では、もはやコンテナは必須の技術です。

AWSで本番環境を動かす際には、ECSEKSといったコンテナオーケストレーションサービスを使います。さらに、Fargateを使えばサーバーを意識せずにコンテナを実行できます。これらのサービスは、Dockerで作成したコンテナを動かすための場所であり、本講座の後半で詳しく触れていきます。


まとめ:今日から始めるコンテナ開発の第一歩

この章では、コンテナとDockerがなぜ重要なのかを理解するための基礎固めでした。

重要なポイントは以下の3つです。

  1. 環境の統一: コンテナは、アプリケーションと依存関係をパッケージ化し、どの環境でも同じように動くことを保証します。
  2. 開発効率の向上: 開発からデプロイまでのプロセスが高速化し、チーム全体の生産性が向上します。
  3. 現代の標準技術: クラウド、マイクロサービス、AI開発など、あらゆる分野でコンテナは欠かせない技術となっています。

そして、これらのコンテナ技術はAWSのサービスと組み合わせることで、スケーラブルで運用負荷の少ないシステムを構築する強力な武器となります。

この講座では、最終的に以下のような道筋をたどり、皆さんがAWSでコンテナを自在に扱えるようになることを目指します。


第二章:Dockerの基本コマンド総ざらい:イメージとコンテナを徹底理解

Dockerを使いこなす上で、絶対に避けて通れないのが「イメージ」と「コンテナ」という2つの概念です。これらはDockerの根幹をなすものであり、車の「設計図」と「実物」の関係に例えると非常に分かりやすいです。

  • Dockerイメージ: アプリケーションの実行に必要なすべての情報(コード、ライブラリ、設定など)をパッケージ化した、読み取り専用のテンプレートです。これは車の「設計図」に当たります。
  • Dockerコンテナ: Dockerイメージから作成され、実際にアプリケーションが動作する実行可能なインスタンスです。これは「設計図」に基づいて組み立てられた、実際に動く「車」そのものです。

この関係性を頭に置きながら、基本コマンドを一つずつ見ていきましょう。


0.Docker Desktopのインストール

公式ドキュメントに従って簡単にインストールできます。

注意点: 企業でDocker Desktopを利用する場合、ライセンスが必要な場合があります。念のため、所属する組織のルールを確認してください。

  • 動作確認
    インストールが完了したら、以下のコマンドでDockerが正しく動いているか確認してみましょう。

    docker run hello-world
    

    このコマンドを実行して、ターミナルに「Hello from Docker!」と表示されれば成功です!


1. コンテナのライフサイクルと基本コマンド

Dockerコンテナは、以下のシンプルなライフサイクルを持っています。

作成 ➡️ 実行中 ↔️ 停止 ➡️ 削除

docker rundocker startdocker stopdocker rmといったコマンドは、この状態遷移を管理するために使われます。

2. まずは「イメージ」を操るコマンド

コンテナを作成するためには、まず元となる「イメージ」が必要です。ほとんどの場合、Docker Hubという公式のレジストリから、他の開発者が公開しているイメージをダウンロードして利用します。

docker pull : イメージのダウンロード

docker pullコマンドは、リモートのリポジトリからイメージをローカルに取得するために使います。

# 例: nginx Webサーバーのイメージをダウンロード
docker pull nginx

# 特定のバージョンを指定してダウンロード
docker pull ubuntu:20.04

タグを指定しない場合は、デフォルトでlatestが適用されます。latestタグは便利なタグですが、その内容はプロジェクトの運用方針によって異なります。本番環境では、予期せぬ変更を避けるため、具体的なバージョンのタグを指定することが推奨されます

docker images : ローカルイメージの一覧表示

ローカルに保存されているイメージの一覧を確認するには、docker imagesを使います。

docker images

このコマンドを実行すると、以下の情報が表示されます。

  • REPOSITORY: イメージ名
  • TAG: バージョン
  • IMAGE ID: イメージを一意に識別するID
  • CREATED: イメージが作成されてからの経過時間
  • SIZE: イメージのサイズ

docker rmi : イメージの削除

不要になったイメージを削除するには、docker rmiを使います。

# イメージ名とタグで削除
docker rmi nginx:latest

3. 次に「コンテナ」を操るコマンド

イメージをダウンロードしたら、次はそれを基にコンテナを起動します。

docker run : コンテナの作成と起動

docker runは、Dockerコマンドの中で最も重要で、多機能なコマンドです。イメージを指定してコンテナを作成し、すぐに実行します。

# 例: バックグラウンドでnginxコンテナを起動し、ポートフォワードを設定
docker run -d --name my-nginx -p 8080:80 nginx
  • -d (--detach): コンテナをバックグラウンド(デタッチモード)で実行します。
  • -p (--publish): ホストマシンとコンテナのポートをマッピングします。構文は「ホストポート:コンテナポート」です。上記の例では、ホストの8080番ポートへのアクセスを、コンテナの80番ポートに転送します。
  • --name: コンテナにわかりやすい名前を付けます。

実際の開発でよく使う docker run オプション

  • 環境変数の設定-e オプションで環境変数を渡します。
    docker run -e DB_HOST=db_server -e DB_USER=admin my-app
    
  • ボリュームマウント-v オプションでホストのディレクトリをコンテナにマウントします。
    docker run -v /Users/user/my-data:/app/data my-app
    
  • 作業ディレクトリの指定-w オプションでコンテナ内の作業ディレクトリを指定します。
    docker run -w /app ubuntu /bin/bash
    

docker ps : 実行中のコンテナ一覧表示

現在実行中のコンテナを確認するには、docker psを使います。

docker ps
  • -a (--all): 実行中かどうかにかかわらず、停止中のコンテナも含めてすべてのコンテナを表示します。

docker start / docker stop : コンテナの起動と停止

停止したコンテナを再起動するにはdocker startを、実行中のコンテナを停止するにはdocker stopを使います。

# 名前でコンテナを起動
docker start my-nginx

# 名前でコンテナを停止
docker stop my-nginx

docker rm : コンテナの削除

不要になったコンテナを削除するには、docker rmを使います。コンテナを削除する際は、先にdocker stopで停止させておくのが基本です。


4. 実行中のコンテナをデバッグする

docker runでコンテナを起動したら、そのコンテナ内でさらにコマンドを実行したい場合があります。

docker exec : 実行中のコンテナ内でコマンドを実行

docker runは新しいコンテナを作成して実行するのに対して、docker exec既存の実行中コンテナ内でコマンドを実行します。デバッグや設定確認の際によく使用されます。

# 例: 実行中のnginxコンテナでbashシェルを起動
docker exec -it my-nginx /bin/bash

このコマンドを実行すると、ターミナルがコンテナ内に切り替わり、直接操作できます。exitと入力して抜けても、コンテナ自体は停止しません。

docker logs : コンテナのログを見る

コンテナの標準出力(stdout)と標準エラー出力(stderr)のログを確認するには、docker logsを使います。

# 例: nginxコンテナのログを表示
docker logs my-nginx

# リアルタイムでログを追跡
docker logs -f my-nginx

5. まとめ:

Dockerの基本コマンドを学びました。以下のコマンドを自分のPCで試して、挙動を確かめてみましょう。

  1. docker pullで任意のイメージ(例:httpd)をダウンロードする。
  2. docker imagesでダウンロードしたイメージが表示されることを確認する。
  3. docker runでコンテナをバックグラウンド起動し、ポートフォワードを設定する。
  4. docker psdocker ps -aでコンテナの状態を確認する。
  5. docker stopdocker startでコンテナを停止・再起動してみる。
  6. docker logsdocker execでコンテナのデバッグを行う。
  7. docker rmでコンテナを削除し、docker rmiでイメージを削除する。

これらのコマンドをマスターすれば、Dockerをローカル環境で自由に操れるようになります。

第三章:Dockerfileの書き方から学ぶ、軽量で堅牢なコンテナイメージの作り方

前章では、docker pullコマンドでDocker Hubから公開されているイメージをダウンロードして利用する方法を学びました。しかし、他人が作ったイメージを使うだけでは、アプリケーションの要件に完全に合わせることはできません。

そこで登場するのが「Dockerfile」です。

Dockerfileは、コンテナイメージを自動的に構築するための命令を記述したテキストファイルです。このファイルを使えば、誰でも同じ手順で、何度でも全く同じコンテナイメージを作成できます。これは、開発環境の再現性を確保する上で最も重要な要素の一つです。

今回は、このDockerfileの書き方をマスターし、自分だけのオリジナルイメージを作成してみましょう。


1. Dockerfileの基本構成とビルド

Dockerfileは、通常、アプリケーションのソースコードと同じディレクトリに配置します。ファイル名はそのまま「Dockerfile」です。

my-app/
├── Dockerfile
├── .dockerignore
├── requirements.txt
├── app.py
└── static/

基本的なDockerfileは、主に以下の3つの要素で構成されます。

  1. ベースイメージの指定 (FROM)
  2. ファイルのコピー (COPY や ADD)
  3. コマンドの実行 (RUNCMDENTRYPOINT)

これらの命令を組み合わせることで、アプリケーションの実行環境をゼロから構築できます。

Dockerイメージのビルド

Dockerfileが完成したら、docker buildコマンドを使ってイメージをビルドします。

# カレントディレクトリのDockerfileを使ってイメージをビルド
docker build -t my-app:1.0 .
  • -t (--tag): ビルドするイメージに名前とタグを付けます。
  • . (ドット): Dockerfileが存在するパスを示し、「ビルドコンテキスト」と呼ばれます。

ビルドコンテキストは、docker buildコマンドを実行するディレクトリとその中のすべてのファイルが対象になります。コンテキストが大きいとビルド時間が長くなるため、.dockerignoreファイルを使って不要なファイルを除外することが重要です。

.dockerignoreファイルの活用

.dockerignoreファイルは、ビルドコンテキストから除外したいファイルやディレクトリを指定するために使います。

# .dockerignoreファイルの例
.git
.env
.env.local
node_modules
*.log
Thumbs.db

このファイルを適切に使うことで、ビルドコンテキストのサイズを劇的に削減できます。例えば、node_modulesディレクトリを除外するだけで、ビルド時間が短縮し、ネットワークトラフィックが削減される効果が見込めます。.dockerignoreなしで500MBだったビルドコンテキストが、ありでは50MBになるなど、90%の削減も珍しくありません。


2. Dockerfileの主要な命令とベストプラクティス

FROM <image>:<tag>

これがDockerfileの最初の命令で、必ず記述します。どのベースイメージを元にして、新しいイメージを構築するかを指定します。

RUN <command> とレイヤーキャッシュ

RUN命令は新しいレイヤーを作成し、コマンドを実行します。Dockerには、レイヤーキャッシュという仕組みがあり、同じ命令が続く場合、前回のビルド結果を再利用してビルドを高速化します。

この仕組みを理解することが、効率的なDockerfileを書く鍵です。

# ❌ この書き方では、コードを変更するたびにpip installが毎回実行される
COPY . .
RUN pip install -r requirements.txt

# ✅ この書き方なら、requirements.txtが変更されない限りキャッシュが利用される
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

頻繁に変更されるファイル(例: ソースコード)を最後にコピーすることで、その手前のレイヤーキャッシュが常に再利用され、ビルド時間が短縮されます。

COPY <src> <dest> と ADD <src> <dest>

ADDCOPYの機能に加えて、URLからのファイルダウンロードや、圧縮ファイルの自動展開といった特別な機能を持っています。ただし、ADDの自動展開機能は予期しない動作を引き起こす可能性があり、セキュリティの観点からもCOPYの使用が強く推奨されます。ADDは明確にその特殊機能が必要な場合のみ使用しましょう。

EXPOSE <port>

EXPOSE 8000は、ドキュメント用の命令で、実際にポートを公開するわけではありません。このコンテナがどのポートをリッスンするかを示すだけです。


3. セキュリティとパフォーマンスを考慮した実践テクニック

① マルチステージビルド

ビルドに必要なツールと、最終的に実行するアプリケーションを分けることで、最終イメージサイズを劇的に削減します。

# ビルドステージ: 依存関係の多いGoのSDKイメージ
FROM golang:1.18 AS builder
WORKDIR /go/src/app
COPY . .
RUN CGO_ENABLED=0 go build -o /go/bin/app

# 実行ステージ: 実行バイナリのみをコピーする軽量なAlpineイメージ
FROM alpine:3.15
WORKDIR /app
COPY --from=builder /go/bin/app .
CMD ["./app"]

この例では、GoのSDKイメージは約1.2GBですが、マルチステージビルドによって、最終的なイメージはわずか約15MBにまで軽量化されます。

② 非rootユーザーで実行

デフォルトではrootユーザーでコンテナが実行されますが、セキュリティリスクを高めます。コンテナ内でroot権限を奪取されると、ホストOSにも影響が及ぶ可能性があるため、非rootユーザーで実行することが非常に重要です。

# セキュリティのため非rootユーザーを作成
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# アプリケーションファイルの所有者を変更
COPY --chown=appuser:appgroup . .
# 非rootユーザーに切り替え
USER appuser

③ 環境変数の活用

環境変数は、アプリケーションの挙動をコンテナイメージの再ビルドなしに変更できるため、非常に便利です。ARGはビルド時、ENVは実行時に使用されます。

# 環境に応じた設定の例
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
ENV PORT=8000

④ Dockerfileの健全性をチェック

hadolintのようなツールを使って、Dockerfileのベストプラクティスを自動でチェックできます。

# hadolintを使ったDockerfileの静的解析
docker run --rm -i hadolint/hadolint < Dockerfile

さらに、ビルドしたイメージに脆弱性がないかを確認することも重要です。Docker Desktop(有料プラン)やAWS ECRでは、イメージスキャン機能が提供されています。


4. まとめと次へのステップ

この章では、Dockerfileを使って自分だけのコンテナイメージを作成する方法を学びました。

  • Dockerfileは、イメージ構築のレシピです。
  • ビルドコンテキストレイヤーキャッシュを理解することが効率的なビルドの鍵です。
  • マルチステージビルド非rootユーザー実行など、本番環境に耐えうる堅牢なイメージを構築するベストプラクティスを学びました。

これで、皆さんは「自分でイメージを作成する」という、Docker活用の次のステップに進むことができました。

次章からは、複数のコンテナを連携させる方法を学びます。単一のアプリケーションだけでなく、Webサーバーとデータベースといった複数のサービスを協調させて動かすことが、より実践的な開発の第一歩となります。


第四章:ローカル開発環境をDocker Composeで構築しよう

前章では、Dockerfileを使ってアプリケーションのコンテナイメージを作成する方法を学びました。しかし、実際のアプリケーションは、Webサーバー、データベース、キャッシュサーバーなど、複数のコンテナが連携して動くことが多いです。

そこで登場するのが、複数のコンテナをまとめて管理・実行するためのツール、「Docker Compose」です。


1. Docker Composeとは?

Docker Composeは、複数コンテナで構成されるアプリケーションを定義し、実行するためのツールです。

その定義は、docker-compose.ymlというYAMLファイルに記述します。このファイルに「どのイメージを使うか」「どのポートを公開するか」「コンテナ同士をどう連携させるか」といった情報をまとめて書くことで、たった一つのコマンドでアプリケーション全体を起動できるようになります。

車の製造に例えると、これまでは部品(コンテナ)を一つずつ手作業で組み立てていたのに対し、Docker Composeを使えば、設計図(docker-compose.yml)を渡すだけで、車(アプリケーション全体)が自動で組み立てられるイメージです。


2. docker-compose.yml ファイルの基本構造

docker-compose.yml ファイルは、非常にシンプルで直感的に理解できます。

基本的な構造は以下の通りです。

# Docker Compose V2形式ではバージョン指定は省略が推奨
# version: '3.8'

services:
  <サービス名1>:
    ...
  <サービス名2>:
    ...
  • services: アプリケーションを構成する各サービスを定義するセクションです。

Docker Compose V2(Docker Engine 1.27以降)では、versionの指定は不要となり、docker compose(ハイフンなし) というコマンド形式が新しい標準となっています。


3. サンプルプロジェクトで学ぶ実践的な使い方

ここでは、シンプルなPython Webアプリケーションとデータベースを連携させる例を見ていきましょう。

プロジェクトのファイル構成

my-web-app/
├── app.py
├── requirements.txt
├── .env
├── .gitignore
├── Dockerfile
└── docker-compose.yml

docker-compose.ymlとは別に、環境変数を定義する.envファイルを用意します。

.env ファイルの活用

.envファイルに機密情報や環境ごとの設定を記述することで、docker-compose.ymlをクリーンに保つことができます。

# .env ファイルの例
DATABASE_URL=postgresql://postgres:secret_password@db:5432/mydatabase
POSTGRES_USER=postgres
POSTGRES_PASSWORD=secret_password
POSTGRES_DB=mydatabase
FLASK_ENV=development
FLASK_DEBUG=1

重要な注意: .envファイルはパスワードなどの機密情報を含むため、必ず.gitignoreに追加し、Gitリポジトリにコミットしないようにしてください

# .gitignore に追加すべきファイル
.env
.env.local
.env.production

docker-compose.yml の定義

services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      # 開発効率を上げるため、ホストのコードをコンテナにマウント
      - .:/app
      # node_modulesなど重いディレクトリは除外してビルドを高速化
      - /app/node_modules
    env_file:
      - .env
    depends_on:
      - db
    environment:
      # Pythonの出力をリアルタイムに表示する設定
      - PYTHONUNBUFFERED=1

  db:
    image: postgres:13
    volumes:
      # ボリュームを使ってデータを永続化
      - postgres_data:/var/lib/postgresql/data
    env_file:
      - .env
    # dbサービスはポートを外部に公開しない
    # ports:
    #   - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  postgres_data:

depends_on と healthcheck

depends_onは、コンテナの起動順序のみを保証します**。データベースが起動して接続可能になるまでは待ちません。実務では、healthcheckを使ってコンテナの準備完了を待つ工夫が必要です。


4. Docker Composeコマンドと実践向けTips

docker-compose.ymlファイルを作成したら、あとは簡単なコマンドを実行するだけです。ここでは、新しいDocker CLIの docker compose(ハイフンなし) に統一します。

開発時の典型的なワークフロー

  • 開発開始時: アプリケーションをバックグラウンドで起動します。

    docker compose up -d
    
  • ログ確認:

    # 全サービスのログを確認
    docker compose logs
    
    # 特定のサービスのログをリアルタイムで追跡
    docker compose logs -f web
    
  • コンテナ内へのアクセス:

    # webサービスコンテナ内でbashシェルを実行
    docker compose exec web bash
    
  • 開発終了時: アプリケーションを停止し、コンテナとデータを削除します。

    # ボリュームのデータを残してコンテナを削除
    docker compose down
    
    # ボリュームのデータも一緒に削除
    docker compose down -v
    

5. よくある問題と解決策

  • ポート競合エラーが発生した場合
    「Port is already allocated」というエラーが出たら、ホスト側のポート番号を変更します。
ports:
  - "15432:5432"  # ホスト側のポートを5432から15432に変更
  • イメージを強制的に再ビルドしたい場合
    キャッシュを使わず、Dockerfileからイメージを再構築します。

 

docker compose build --no-cache web
  • 全てをクリーンアップして再起動したい場合
    コンテナとボリュームを全て削除してから再起動します。
docker compose down -v
docker compose up --build

6. まとめ:Docker Composeでローカル開発は劇的に変わる

この章では、Docker Composeを使って複数コンテナのアプリケーションを簡単に構築・管理する方法を学びました。

  • docker-compose.yml: サービス構成をコードとして管理できます。
  • docker compose up: アプリケーション全体を一発で起動できます。
  • docker compose down: アプリケーション全体を一発で停止・削除できます。

これで、皆さんは単一のコンテナだけでなく、複数コンテナで構成されるアプリケーションをローカルで自在に扱えるようになりました。


第五章:ボリュームとネットワーク:コンテナ間でデータを共有し、通信する方法

今までの講座で、単一のコンテナを動かす方法から、Docker Composeで複数コンテナのアプリケーションを構築する方法まで学びました。しかし、まだ解決すべき重要な課題が2つあります。

  1. データの永続化: コンテナを削除すると、その中に保存されていたデータも消えてしまう。
  2. コンテナ間の通信: 複数のコンテナがどのようにして安全に、そして安定して通信するのか。

今回の講座では、これらの課題を解決するDockerの2つの重要な概念、「ボリューム」と「ネットワーク」について詳しく見ていきます。これらの概念は、ローカル開発とAWS上での本番運用で考え方が大きく変わるため、その点にも焦点を当てて解説します。


1. ボリューム:コンテナの「揮発性」を乗り越える

コンテナは、デフォルトでは「一時的な存在」です。コンテナからアクセスするデータを永続化(コンテナが消えてもデータが残ること)するための仕組みがボリュームです。

ローカル開発におけるボリューム

ローカル環境では、主に以下の2つのボリュームタイプが利用されます。

  • バインドマウント(Bind Mounts): ホストPCのディレクトリを、コンテナに直接マッピングします。ローカル開発に最も適しており、コードのリアルタイム同期が可能です。
  • ボリューム(Volumes): Dockerが管理する領域にボリュームを作成し、コンテナにマウントします。データベースなど、ホストから直接編集する必要のないデータの永続化によく使われます。

セキュリティと永続化を意識したボリューム設定

services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      # 開発時:ソースコードの同期(読み取り専用推奨)
      - ./src:/app/src:ro
      # ログファイルの永続化
      - app_logs:/app/logs
      # アップロードファイルの永続化
      - user_uploads:/app/uploads
    user: "1000:1000"  # セキュリティのため非root実行
    depends_on:
      - db

volumes:
  app_logs:
  user_uploads:
  postgres_data:

user の指定により、コンテナ内で実行されるプロセスに不要な権限を与えないようにできます。ただし、UID/GIDがホスト環境と一致しない場合、Permission deniedエラーが発生する可能性があるため注意が必要です。

AWS上でのボリューム:本番環境のベストプラクティスとコスト

本番環境でデータを永続化する場合、専用のマネージドストレージサービスを利用するのがベストプラクティスです。それぞれのサービスには、パフォーマンスやコスト面で特徴があります。

サービス 特徴 コスト(東京リージョン)
Amazon EBS 単一インスタンスに紐づく高性能なブロックストレージ。Multi-Attach機能を持つ一部のボリュームタイプでは、限定的に複数インスタンスからアクセス可能。 EBS gp3: $0.096/GB/月 + IOPS/スループット料金
Amazon EFS 複数コンテナから同時にアクセスできる共有ファイルシステム。アクセス量に応じてIOPSとスループットがスケール。 EFS Standard: $0.30/GB/月
Amazon S3 静的ファイルやオブジェクトデータ用。直接的なファイルシステムアクセスには不向き。 S3 Standard: $0.025/GB/月

本番環境では、これらのマネージドストレージの権限設定にIAMポリシーを適用し、コンテナがアクセスできるリソースを厳密に管理することが不可欠です。


2. ネットワーク:コンテナ間の通信を司る

コンテナは、デフォルトでは互いに隔離されています。Dockerは、コンテナ同士が通信できる仮想的な「ネットワーク」を自動で作成してくれます。

Docker Composeにおけるネットワーク

Docker Composeは、デフォルトで一つのプライベートネットワークにすべてのサービスを接続します。

より高度な制御が必要な場合は、カスタムネットワークを定義できます。internal: trueを設定すると、コンテナ同士は通信できますが、ホストやDocker Compose外のネットワークからは隔離されます

# カスタムネットワークの例
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # 外部ネットワークとホストから隔離

AWS上でのネットワークとセキュリティ

AWSのクラウド環境では、ネットワークの考え方が大きく変わります。

  • VPC(Virtual Private Cloud): AWSリソースを配置する仮想的なネットワークです。VPC内はさらにサブネットに分割し、セキュリティグループや**NACL(ネットワークACL)**を使って、きめ細やかなアクセス制御を行います。
  • サービスディスカバリ: ECSではAWS Cloud Map、EKSではKubernetes DNSを利用して、サービス名とIPアドレスを結びつけます。

3. データベースの永続化とパフォーマンス

  • 学習用: ローカル開発では、Docker Composeでデータベースをコンテナとして起動します。
  • 本番環境: 本番環境では、可用性の高いRDSAuroraなどのマネージドサービスを使います。これらは、自動バックアップやパッチ適用などをAWSが管理してくれるため、運用負荷が大幅に軽減されます。

コスト最適化の観点から、開発・検証環境では低コストのRDSを、本番環境では冗長構成と監視を備えたAurora Serverless v2(使用量に応じた自動スケーリング: $0.12/ACU/時間)を利用するなど、目的に応じた選択が重要です。


4. 監視、ロギング、そしてトラブルシューティング

本番環境を意識した設定

ローカル開発から本番運用へ移行する際には、ログの管理とヘルスチェックが不可欠です。

# 本番環境を意識したログ設定
services:
  web:
    # ログドライバーの設定
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    # ヘルスチェックの追加
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

loggingドライバーをawslogsに設定すれば、AWS CloudWatch Logsと連携し、ログを一元管理できます。

よくある問題と解決法

  • ボリューム未作成エラーdocker compose upで自動作成されますが、削除した後は注意が必要です。docker system df -vでボリュームの使用状況を確認できます。
  • ボリューム権限エラーPermission deniedエラーが出る場合、権限がありません。docker compose exec web chown -R $(id -u):$(id -g) /app/uploadsで所有者を変更します。
  • ネットワーク接続エラー: サービス名で接続できない場合、docker compose exec web nslookup dbで名前解決できるか確認しましょう。

5. まとめ:実践的なDocker運用へ

この章では、コンテナの永続化と通信を司る「ボリューム」と「ネットワーク」について深く掘り下げました。

  • ボリューム: 開発ではバインドマウント、本番では名前付きボリュームやAWSのマネージドサービスを利用します。
  • ネットワーク: サービスの分離やセキュリティを意識して、カスタムネットワークを活用します。

これらの知識は、単なるローカル開発にとどまらず、AWSでスケーラブルなシステムを構築するための土台となります。


第六章: 実践!既存アプリケーションをDocker化してみよう 🚀

この1週間で、Dockerの基礎から応用まで、以下の重要なスキルを学びました。

  • 1: コンテナとDockerの概念、その重要性
  • 2dockerコマンドの基本(pullrunpsなど)
  • 3Dockerfileを使ったイメージ作成とベストプラクティス
  • 4Docker Composeを使った複数コンテナ管理
  • 5: ボリュームとネットワークによるデータの永続化と通信

この知識を定着させるため、今回は「既存のアプリケーションをDocker化する」という実践的な演習を行います。これにより、これまでの学びを統合し、実務で使えるスキルへと昇華させます。


1. 今回のお題:シンプルなPython Webアプリケーション

今回は、Pythonの軽量なWebフレームワークであるFlaskを使ったシンプルなWebアプリケーションをDocker化します。

プロジェクトのファイル構成

my-web-app/
├── src/                # アプリケーションのソースコード
│   ├── app.py
│   └── requirements.txt
├── .env.example        # 環境変数テンプレート
├── .gitignore          # Git管理から除外するファイル
├── .dockerignore       # ビルドコンテキストから除外するファイル
├── Dockerfile          # コンテナイメージのレシピ (本番用)
├── docker-compose.yml    # 複数コンテナの定義 (本番用)
└── docker-compose.dev.yml  # 開発用設定

src/app.py

import os
import time
import psycopg2
from flask import Flask, jsonify

app = Flask(__name__)

def get_db_connection():
    database_url = os.environ.get('DATABASE_URL')
    if not database_url:
        raise ValueError("DATABASE_URL environment variable is not set")
    
    max_retries = 10
    retry_delay = 5
    for i in range(max_retries):
        try:
            conn = psycopg2.connect(database_url, connect_timeout=10)
            print(f"Database connection successful! (attempt {i+1})")
            return conn
        except psycopg2.OperationalError as e:
            print(f"Database connection failed (attempt {i+1}): {e}")
            if i < max_retries - 1:
                time.sleep(retry_delay)
    raise Exception("Failed to connect to database after multiple retries.")

@app.route('/health')
def health_check():
    return jsonify({'status': 'healthy'}), 200

@app.route('/')
def hello_db():
    try:
        conn = get_db_connection()
        cur = conn.cursor()
        cur.execute("SELECT version()")
        db_version = cur.fetchone()
        cur.close()
        conn.close()
        return jsonify({
            'message': 'Database connection successful!',
            'db_version': db_version[0]
        })
    except Exception as e:
        return jsonify({'error': f'Database connection failed: {str(e)}'}), 500

if __name__ == '__main__':
    debug_mode = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
    app.run(debug=debug_mode, host='0.0.0.0', port=5000)

src/requirements.txt

Flask==2.2.2
psycopg2-binary==2.9.3
gunicorn

2. ステップ・バイ・ステップ Docker化プロセス

ステップ1: Dockerfile を作成する

本番環境向けには、マルチステージビルドと非rootユーザー設定が重要です。これにより、セキュリティを強化し、イメージサイズを最小化します。

# Dockerfile: 本番向け
# -------------------------------------------------------------
# ビルドステージ: 依存関係のインストールとビルド
FROM python:3.9-slim as builder
WORKDIR /app
COPY ./src/requirements.txt .
# 依存ライブラリを/app/dependenciesにインストール
RUN pip install --prefix=/app/dependencies -r requirements.txt

# -------------------------------------------------------------
# 実行ステージ: アプリケーションの実行環境
FROM python:3.9-slim
# 非rootユーザーの作成(セキュリティ向上)
RUN addgroup --system --gid 1001 appgroup && \
    adduser --system --uid 1001 --gid 1001 appuser
WORKDIR /app

# ビルドステージから依存関係をコピー
COPY --from=builder /app/dependencies /usr/local
# アプリケーションコードをコピーし、所有者を非rootユーザーに設定
COPY --chown=appuser:appgroup ./src/ .
# 非rootユーザーに切り替え
USER appuser
# アプリケーションがリッスンするポートの公開
EXPOSE 5000
# Gunicornを使ってアプリケーションを起動
CMD ["gunicorn", "-w", "${WORKERS:-4}", "-b", "0.0.0.0:5000", "app:app"]

CMD にWORKERSを環境変数として設定することで、コンテナの起動時にGunicornのワーカープロセス数を柔軟に調整できます。

ステップ2: docker-compose ファイルを作成する

開発用と本番用の設定を分離すると、運用がシンプルになります。

docker-compose.yml (本番用)

services:
  web:
    build: .
    ports:
      - "5000:5000"
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "python -c 'import urllib.request; urllib.request.urlopen(\"http://localhost:5000/health\")'"]
      interval: 30s
      timeout: 10s
      retries: 3
  
  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data
    env_file:
      - .env
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:

docker-compose.dev.yml (開発用)

services:
  web:
    build: .
    volumes:
      # ソースコードをコンテナに同期し、リアルタイムで変更を反映
      - ./src:/app
    environment:
      # デバッグモードを有効化
      - FLASK_DEBUG=true

開発時には docker compose -f docker-compose.yml -f docker-compose.dev.yml up コマンドで両方の設定を適用します。

ステップ3: 環境設定ファイルを作成する

.env.example

DATABASE_URL=postgresql://postgres:your_password@db:5432/postgres
FLASK_DEBUG=true

.env

DATABASE_URL=postgresql://postgres:secret_password@db:5432/postgres
FLASK_DEBUG=true

重要な注意: docker compose.envファイルを参照しますが、Gitリポジトリにコミットしないよう .gitignoreに必ず追加してください 。本番環境では、AWS Secrets ManagerKMSといった専用のシークレット管理サービスを使用するのがセキュリティのベストプラクティスです。


3. 本番運用を意識したログ管理とセキュリティ

ログ管理

現在のdocker-compose.ymlでは、ログはjson-fileドライバで管理されます。これはファイルサイズとファイル数を指定して自動でローテーション(世代管理)する機能があります。
本番環境では、AWS CloudWatch LogsDatadogなどの集中ログ管理サービスに転送することで、ログの検索・分析・監視が容易になります。

セキュリティスキャン

ビルドしたイメージに脆弱性がないか、Docker Scoutのようなツールで定期的にスキャンしましょう。

# イメージの脆弱性スキャン
docker scout cves my-web-app:latest

4. まとめ

これで、皆さんはローカル環境でのコンテナ開発の基礎を完全にマスターしました。今日の実践演習で、これまでの知識が単なる理論ではなく、実際に動くアプリケーションを構築するためのツールであることが実感できたはずです。

来週からは、この知識を活かして、いよいよAWS上でコンテナを動かす旅に出発します。


第七章:第1週のまとめとスキルチェックテスト 📝

皆さんはコンテナとDockerの基礎を学び、ローカル環境でマルチコンテナアプリケーションを構築するスキルを身につけました。これは、これからのクラウドネイティブ開発において非常に重要な第一歩です。

今日は、これまでの学びを振り返るための「スキルチェックテスト」に挑戦しましょう。


1. スキルチェックテスト

これから、4つの実践的な課題を出題します。テキストエディタとターミナルを開き、ぜひ挑戦してみてください。

課題1: コンテナとイメージの基本操作

  1. alpine:latest イメージをDocker Hubから取得してください。
  2. 取得したイメージから、ls -l / コマンドを実行するコンテナを起動してください。
  3. 起動したコンテナのIDを確認し、停止後、削除してください。

課題2: Dockerfile の作成

以下の要件を満たすDockerfileを、nginx-custom/ ディレクトリ内に作成してください。

  • ベースイメージは nginx:1.21-alpine を使用する。
  • コンテナ内の /usr/share/nginx/html に、"Hello Docker!" と書かれた index.html ファイルを配置する。
  • ブラウザで正しく表示されるよう、HTMLの構造を含めること。
  • .dockerignoreファイルを作成し、不要なファイルをビルドコンテキストから除外すること。

課題3: Docker Compose でのマルチコンテナ構築

以下の要件を満たすdocker-compose.ymlを作成し、起動してください。

  • web と db の2つのサービスを定義する。
  • web サービスは、./nginx-custom ディレクトリの Dockerfile を使ってイメージをビルドする。
  • web サービスは、ホストの 8080 ポートをコンテナの 80 ポートにマッピングする。
  • db サービスは、公式の postgres:13 イメージを使用し、パスワードをDocker Secretsで安全に管理する。
  • db サービスのデータを永続化するため、名前付きボリュームを定義してマウントする。

課題4: トラブルシューティング

以下のdocker-compose.ymlファイルを起動しようとしたところ、エラーが発生しました。エラーの原因を特定し、解決してください。

services:
  web:
    image: nginx
    ports:
      - "80:80"
  db:
    image: postgres:13

2. 解答と解説

課題1: 解答

  1. docker pull alpine:latest
  2. docker run alpine:latest ls -l /
  3. docker ps -a でコンテナIDを確認し、docker rm <CONTAINER_ID> -f で強制削除する。

課題2: 解答

nginx-custom/Dockerfile

FROM nginx:1.21-alpine
COPY index.html /usr/share/nginx/html

nginx-custom/index.html

<!DOCTYPE html>
<html>
<head>
    <title>Hello Docker</title>
</head>
<body>
    <h1>Hello Docker!</h1>
</body>
</html>

.dockerignore

.git/
node_modules/
*.log
.env

.dockerignoreファイルは、イメージをビルドする際にコンテナにコピーされないよう、不要なファイルを指定します。

課題3: 解答

docker-compose.yml

services:
  web:
    build: ./nginx-custom
    ports:
      - "8080:80"
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
      interval: 30s
      timeout: 10s
      retries: 3
  
  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
      - POSTGRES_DB=myappdb
    secrets:
      - db_password
    deploy:
      resources:
        limits:
          memory: 512M
        reservations:
          memory: 256M

secrets:
  db_password:
    file: ./secrets/db_password.txt

volumes:
  postgres_data:

解説deployセクションでメモリ制限を設定することで、コンテナがホストのリソースを過剰に消費するのを防ぎます。

課題4: 解答

エラーの原因は主に2つ考えられます。

  1. ポート競合portsに指定されたホスト側のポートが既に別のプロセスによって使用されているため、コンテナを起動できません。
    • 実際のエラーメッセージ例Error: failed to start container: port is already allocated
  2. 必須環境変数の不足: PostgreSQLの公式イメージは、パスワードが設定されていないと起動しません。
    • 実際のエラーメッセージ例database is uninitialized and superuser password is not specified

解決策は以下の通りです。

services:
  web:
    image: nginx
    ports:
      - "8080:80"
  db:
    image: postgres:13
    ports:
      - "5433:5432"
    environment:
      - POSTGRES_PASSWORD=mysecretpassword
      - POSTGRES_DB=myappdb

ヒント: 既存のコンテナがポートを占有している場合、docker compose downで停止してから再起動するだけで解決することがほとんどです。


3. 実践的なデバッグスキルとセキュリティ

コンテナが動かない時に役立つコマンドと、より安全な環境変数の管理方法を覚えておきましょう。

  • コンテナ内へのアクセス: コンテナの内部を直接調査したい場合に便利です。
    docker exec -it <container_name> /bin/sh
    
  • コンテナの詳細情報確認: 実行中のコンテナの設定(マウントされたボリューム、ポート設定、環境変数など)を詳細に確認できます。
    docker inspect <container_name>
    
  • バージョン指定docker-compose.ymlファイルのversion指定は現在非推奨です。Docker Compose V2では自動で適切なバージョンが適用されます。

4. まとめ:

Dockerの旅お疲れ様でした!これらの課題をスムーズに解けたなら、皆さんはDockerの基本的な操作を完全にマスターしたと言えるでしょう。

Dockerは、環境構築の課題を解決し、開発の生産性を飛躍的に向上させる強力なツールです。このスキルは、今後のキャリアを築く上で大きな武器になります。


コメント