業務でDockerを使ったので改めてDockerについて知る

# はじめに
この記事は、個人開発でなんとなく使っていたDockerを業務で使うことになったエンジニアがDockerについて学びなおした内容をまとめた記事です。
Dockerの基本概念について知ることでより理解が深まると思いこの記事を作成しました。

:::note warn
この記事に記載している内容は筆者が調べて記載した内容です。
正しいことを書いているつもりですが、間違った内容もありますのでご注意ください。
:::

# Dockerとは
Dockerは、ソフトウェアのコンテナ化技術を提供するプラットフォームのことです。
コンテナ化技術は、アプリケーションとその依存関係を一つのパッケージにまとめて、どこでも一貫して動作させることを目的としています。

# Dockerが生まれたきっかけ
Dockerは、ソフトウェアの開発およびデプロイメントのプロセスをより効率的かつ、柔軟にするため生まれました。
その背景には、ソフトウェア開発の現場で抱えるいくつかの課題がありました。
おそらく、みなさんも経験したことがある課題です。
## 開発環境と本番環境の不一致
開発環境と本番環境で動作が異なることが多く、コードが動かない、バグが発生するなどの問題が頻発していました。
これは私も経験したことがあり、リリースまでその事態に気が付かないという状態に陥りました。
リリース時に不具合が発生し、原因となったのが、本番環境と開発環境の不一致でした。
どこでも一貫して動作する環境がほしい。というのはどのエンジニアでも同じ要望ですよね。
## 仮想マシンの重さ
仮想マシン（VM）は、ハイパーバイザを利用してOSごと仮想化するため、リソース消費が多く、起動時間が長いという欠点がありました。

## ソフトウェア依存関係の管理
ソフトウェアが依存するライブラリやツールのバージョン管理が複雑で、異なる環境での動作保証が難しい状況でした。

このような状態がDockerが生まれるきっかけになったそうです。

# Dockerの誕生
Dockerは、2013年3月にPaaS（Platform as a Service）プロバイダーであるDotCloud（現Docker Inc.）によって公開されました。
創設者のSolomon Hykesは、ソフトウェア開発プロセスの問題を解決するために、コンテナ技術を用いた新しいツールを開発しました。
1. コンテナ技術の採用
既存技術の活用: Dockerは、Linuxコンテナ（LXC）技術をベースにしています。LXCは、カーネルレベルでの仮想化を提供し、軽量なコンテナを作成できる技術です。
ユーザーフレンドリーなツール: Dockerは、LXCをより使いやすくするためのツールやAPIを提供し、開発者が簡単にコンテナを操作できるようにしました。
2. 開発からデプロイまでの一貫性
イメージの共有: Dockerは、コンテナのイメージを作成し、どこでも同じように動作させることができます。これにより、開発環境と本番環境の不一致を解消しました。
Docker Hubの導入: Docker Hubというレジストリを提供し、ユーザーがイメージを共有・配布できるようにしました。
3. オープンソースの力
コミュニティの協力: Dockerはオープンソースプロジェクトとして公開され、広範な開発者コミュニティが参加することで、急速に進化しました。
エコシステムの拡大: 多くの企業や開発者がDockerを採用し、ツールやプラグインが次々と登場しました。

# Dockerの主要コンポーネント
いくつかの主要なコンポーネントから成り立っています。
これらのコンポーネントが連携することで、アプリケーションのコンテナ化、管理、デプロイメントが可能になります。

## Dockerデーモン (Docker Engine)
定義：コンテナの作成、管理、実行を担当するバックグラウンドプロセス
機能：
・コンテナのビルド、実行、停止、削除
・Dockerイメージの管理
・ボリューム、ネットワークの管理

## Dockerクライアント
定義：ユーザーがDockerデーモンと通信するためのコマンドラインツール
機能：
・dockerコマンドを使ってDockerデーモンにリクエストを送信し、コンテナの操作や管理を行う

## Dockerイメージ
定義：コンテナを作成するための読み取り専用テンプレートです。イメージには、アプリケーションの実行に必要なすべての設定と依存関係が含まれる
レイヤー：Dockerイメージは複数のレイヤーから構成され、各レイヤーはイメージの一部の変更を含みます。レイヤーはキャッシュされ、効率的なイメージビルドを可能にする

## Dockerコンテナ
定義：Dockerコンテナは、イメージを実行した結果として生成される実行可能な単位です。コンテナは、アプリケーションとその依存関係を含む独立した環境を提供
特徴：
コンテナはイメージから作成され、イメージの読み取り専用レイヤーの上に読み書き可能なレイヤーを追加
コンテナは軽量で高速に起動でき、ホストOSのカーネルを共有

## Dockerレジストリ
定義：Dockerレジストリは、Dockerイメージを保存し、共有するためのリポジトリです。Docker Hubは最も一般的なパブリックレジストリですが、プライベートレジストリも構築できる。
機能：
イメージのプッシュとプルをサポートし、チーム内でのイメージ共有を容易にする。
## Dockerネットワーク
定義：Dockerネットワークは、コンテナ間や外部システムとの通信を管理するための仮想ネットワークを提供する。
種類：
ブリッジネットワーク：同じホスト上のコンテナを接続するためのデフォルトのネットワーク
ホストネットワーク：コンテナがホストマシンのネットワークスタックを直接使用する
オーバーレイネットワーク：複数のホストにわたってコンテナを接続するためのネットワーク
## Dockerボリューム
定義：Dockerボリュームは、コンテナとホスト間でデータを永続的に共有するためのメカニズム
特徴：
コンテナが削除されてもデータは保持され、複数のコンテナ間でデータを共有できる。

# Dockerのアーキテクチャ
1. **クライアント-サーバーアーキテクチャ**
Dockerはクライアント-サーバーアーキテクチャを採用しています。
クライアントは、ユーザーからのコマンドを受け取り、それをDockerデーモンに送信します。
デーモンはリクエストを処理し、必要な操作（コンテナのビルド、実行など）を行います。

2. **イメージのレイヤー構造**
Dockerイメージは、複数のレイヤーから構成されることで効率的な管理と再利用を可能にします。
各レイヤーは変更点のみを含み、キャッシュされるため、イメージのビルドが高速化されます。

3. **コンテナの分離とリソース管理**
コンテナは、ホストOSのカーネルを共有しつつ、独立したファイルシステム、ネットワーク、プロセス空間を持つため、他のコンテナやホストシステムから分離されます。
また、リソースの使用を制限することも可能です。

Dockerの構造は、効率的かつ柔軟なコンテナ管理を実現するためのさまざまなコンポーネントから成り立っています。これらのコンポーネントが連携することで、アプリケーションの一貫したデプロイメント、スケーラビリティ、ポータビリティが実現されます。

# Dockerfileの役割と書き方
## 役割
**イメージの自動ビルド**：
Dockerfileに記述された命令に従って、Dockerイメージが自動的にビルドされます。これにより、同じDockerfileを使用する限り、一貫性のある環境を作成できます。

**環境の再現性**:
Dockerfileを使用すると、開発環境、本番環境、テスト環境など、異なる環境で同じ設定を再現できます。これにより、環境間の違いによる問題を減らすことができます。

**依存関係の管理**:
ソフトウェアの依存関係（ライブラリ、ツールなど）をDockerfileに明示的に記述することで、必要な依存関係を自動的にインストールし、管理できます。

## 一般的な構造と書き方
**ベースイメージの指定 (FROM)**
Dockerイメージは既存のベースイメージから作成されます。FROM命令で使用するベースイメージを指定します。
```dockerfile
FROM ubuntu:20.04
```

**メンテナー情報の指定 (MAINTAINER)**
Dockerfileの作成者情報を記述します（現在は非推奨で、代わりにLABEL命令を使用します）。
```dockerfile
LABEL maintainer="yourname@example.com"
```

**作業ディレクトリの設定 (WORKDIR)**
コンテナ内での作業ディレクトリを設定します。
```dockerfile
WORKDIR /app
```

**必要なファイルのコピー (COPY/ADD)**
ホストマシンからコンテナ内にファイルをコピーします。
```dockerfile
COPY . /app
```

**パッケージのインストール (RUN)**
必要なパッケージや依存関係をインストールします。
```dockerfile
RUN apt-get update && apt-get install -y \
    python3 \
    python3-pip
```

**環境変数の設定 (ENV)**
環境変数を設定します。
```dockerfile
ENV PYTHONUNBUFFERED=1
```

**ポートの公開 (EXPOSE)**
コンテナがリッスンするネットワークポートを指定します。
```dockerfile
EXPOSE 8000
```

**コマンドの実行 (CMD/ENTRYPOINT)**
コンテナ起動時に実行するコマンドを指定します。CMDとENTRYPOINTは異なりますが、一般的にCMDはデフォルトの実行コマンドを指定し、ENTRYPOINTは絶対に実行すべきコマンドを指定します。
```dockerfile
CMD ["python3", "app.py"]
```

**具体例**
例: PythonアプリケーションのDockerfile
```dockerfile
# ベースイメージの指定
FROM python:3.8-slim

# メンテナー情報の指定
LABEL maintainer="yourname@example.com"

# 作業ディレクトリの設定
WORKDIR /app

# 必要なファイルのコピー
COPY requirements.txt .

# パッケージのインストール
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのコピー
COPY . .

# 環境変数の設定
ENV PYTHONUNBUFFERED=1

# ポートの公開
EXPOSE 5000

# アプリケーションの実行
CMD ["python", "app.py"]
```
このDockerfileは、以下の手順でPythonアプリケーションのDockerイメージを作成します。

1. Python 3.8のスリム版ベースイメージを使用
2. メンテナー情報を指定
3. /appディレクトリを作業ディレクトリとして設定
4. requirements.txtファイルをコンテナにコピー
5. requirements.txtに記載されたPythonパッケージをインストール
6. 残りのアプリケーションファイルをコンテナにコピー
7. 環境変数PYTHONUNBUFFEREDを設定
8. コンテナのポート5000を公開
9. アプリケーションを実行するためのコマンドを指定

# ビルドと実行
Dockerイメージのビルド
```bash
docker build -t my-python-app .
```
このコマンドは、現在のディレクトリにあるDockerfileを使用してイメージをビルドし、my-python-appという名前でタグ付けします。

コンテナの起動
```bash
docker run -d -p 5000:5000 my-python-app
```
このコマンドは、ビルドしたイメージからコンテナを起動し、ホストのポート5000をコンテナのポート5000にマッピングします。