「Dockerって名前はよく聞くけど、結局何が便利なの?」「自分の開発には関係ないんじゃないか…」そう思っていませんか?もしかしたら、すでにDockerを使ったことがある方も、その真の価値をまだ十分に理解していないかもしれません。
- 第一章:なぜ今、コンテナとDockerなのか?
- 第二章:Dockerの基本コマンド総ざらい:イメージとコンテナを徹底理解
- 第三章:Dockerfileの書き方から学ぶ、軽量で堅牢なコンテナイメージの作り方
- 第四章:ローカル開発環境をDocker Composeで構築しよう
- 第五章:ボリュームとネットワーク:コンテナ間でデータを共有し、通信する方法
- 第六章: 実践!既存アプリケーションをDocker化してみよう 🚀
- 第七章:第1週のまとめとスキルチェックテスト 📝
第一章:なぜ今、コンテナと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で本番環境を動かす際には、ECSやEKSといったコンテナオーケストレーションサービスを使います。さらに、Fargateを使えばサーバーを意識せずにコンテナを実行できます。これらのサービスは、Dockerで作成したコンテナを動かすための場所であり、本講座の後半で詳しく触れていきます。
まとめ:今日から始めるコンテナ開発の第一歩
この章では、コンテナとDockerがなぜ重要なのかを理解するための基礎固めでした。
重要なポイントは以下の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 run
、docker start
、docker stop
、docker 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で試して、挙動を確かめてみましょう。
docker pull
で任意のイメージ(例:httpd
)をダウンロードする。docker images
でダウンロードしたイメージが表示されることを確認する。docker run
でコンテナをバックグラウンド起動し、ポートフォワードを設定する。docker ps
とdocker ps -a
でコンテナの状態を確認する。docker stop
とdocker start
でコンテナを停止・再起動してみる。docker logs
とdocker exec
でコンテナのデバッグを行う。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つの要素で構成されます。
- ベースイメージの指定 (
FROM
) - ファイルのコピー (
COPY
やADD
) - コマンドの実行 (
RUN
,CMD
,ENTRYPOINT
)
これらの命令を組み合わせることで、アプリケーションの実行環境をゼロから構築できます。
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>
ADD
はCOPY
の機能に加えて、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つあります。
- データの永続化: コンテナを削除すると、その中に保存されていたデータも消えてしまう。
- コンテナ間の通信: 複数のコンテナがどのようにして安全に、そして安定して通信するのか。
今回の講座では、これらの課題を解決する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でデータベースをコンテナとして起動します。
- 本番環境: 本番環境では、可用性の高いRDSやAuroraなどのマネージドサービスを使います。これらは、自動バックアップやパッチ適用などを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の概念、その重要性
- 2:
docker
コマンドの基本(pull
,run
,ps
など) - 3:
Dockerfile
を使ったイメージ作成とベストプラクティス - 4:
Docker 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 ManagerやKMSといった専用のシークレット管理サービスを使用するのがセキュリティのベストプラクティスです。
3. 本番運用を意識したログ管理とセキュリティ
ログ管理
現在のdocker-compose.yml
では、ログはjson-file
ドライバで管理されます。これはファイルサイズとファイル数を指定して自動でローテーション(世代管理)する機能があります。
本番環境では、AWS CloudWatch LogsやDatadogなどの集中ログ管理サービスに転送することで、ログの検索・分析・監視が容易になります。
セキュリティスキャン
ビルドしたイメージに脆弱性がないか、Docker Scoutのようなツールで定期的にスキャンしましょう。
# イメージの脆弱性スキャン
docker scout cves my-web-app:latest
4. まとめ
これで、皆さんはローカル環境でのコンテナ開発の基礎を完全にマスターしました。今日の実践演習で、これまでの知識が単なる理論ではなく、実際に動くアプリケーションを構築するためのツールであることが実感できたはずです。
来週からは、この知識を活かして、いよいよAWS上でコンテナを動かす旅に出発します。
第七章:第1週のまとめとスキルチェックテスト 📝
皆さんはコンテナとDockerの基礎を学び、ローカル環境でマルチコンテナアプリケーションを構築するスキルを身につけました。これは、これからのクラウドネイティブ開発において非常に重要な第一歩です。
今日は、これまでの学びを振り返るための「スキルチェックテスト」に挑戦しましょう。
1. スキルチェックテスト
これから、4つの実践的な課題を出題します。テキストエディタとターミナルを開き、ぜひ挑戦してみてください。
課題1: コンテナとイメージの基本操作
alpine:latest
イメージをDocker Hubから取得してください。- 取得したイメージから、
ls -l /
コマンドを実行するコンテナを起動してください。 - 起動したコンテナの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: 解答
docker pull alpine:latest
docker run alpine:latest ls -l /
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つ考えられます。
- ポート競合:
ports
に指定されたホスト側のポートが既に別のプロセスによって使用されているため、コンテナを起動できません。- 実際のエラーメッセージ例:
Error: failed to start container: port is already allocated
- 実際のエラーメッセージ例:
- 必須環境変数の不足: 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は、環境構築の課題を解決し、開発の生産性を飛躍的に向上させる強力なツールです。このスキルは、今後のキャリアを築く上で大きな武器になります。
コメント