はじめに
HappinessChainというプログラミングスクールで学習しています。
今回は Docker について学んだ内容を纏めました。
前提条件
- Docker Desktop をインストールしている事
Docker はもともと Linux 環境で設計されており、Linux 上で最も効率的に動作しますが、Docker Desktop のおかげで、Windows や macOS でも Docker を利用できます。
Docker Desktop はWindows や macOS 上に Linux の仮想環境を作成し、その中で Docker を実行します。この仮想環境はユーザーには見えませんが、Docker を実行するための基盤として機能します。
注意事項
- この記事では旧Dockerコマンドを採用しています。
- Windows でコマンド検証は行なっておりません。
- Linux のコマンドについては解説していません。
Docker
Docker とは
Docker とはプログラムやアプリケーションを「コンテナ」と呼ばれる特殊な箱の中で実行するためのツールです。冒頭でも説明しましたが、DockerはLinux環境で設計されており、Linuxに元々あるコンテナ技術を使いやすく改良したものです。
コンテナはホストから隔離されており、それぞれ独立した環境で動作します。
Docker を使うメリット
1. イメージの共有
Docker ではアプリケーションとその開発環境が含まれるイメージを他の人と簡単に共有できます。
共有されたイメージを使用して、受け取った人は同じイメージからコンテナを生成し、すぐに一貫した開発環境で作業を開始できます。
2. 簡単な環境構築
Dockerfile(開発環境を定義したファイル)を使用することで、環境構築が簡単になります。 Dockerfile は必要な環境設定を記述することで、一貫した環境を自動的に作成します。
3. 軽量性
仮想マシンというのは通常ゲスト OS(仮想のオペレーティングシステム)を使ってアプリケーションを動かす方法です。しかしDocker(コンテナ型仮想化) ではこのゲスト OS が必要ありません。Docker はホスト OS の Linux カーネル(コンピュータの中心的な部分)を共有することで、非常に早く起動できます。
ただし、macOS や Windows を使っている場合は少し違います。
macOS では「HyperKit」または「Virtualization Framework」、Windows では「Hyper-V」または「WSL2」という特別な仮想化技術を使ってLinux を仮想環境で動かし、その上で Docker を実行します。
これは事実上、ゲスト OS を使用していることになります。
ですがこのゲスト OS はとても軽量で効率的に作られているので、他の仮想マシンに比べその存在をほとんど感じることはありません。
Docker イメージからコンテナを起動する
Docker イメージとは?
Docker イメージとはコンテナを起動するのに必要な設定ファイルを纏めたものです。
Docker イメージの取得には
- Dockerfile から作成する
- DockerHub から取得する
- Docker コンテナから作成する
の方法があり、本記事では ① と ② について説明します。
(② は後述、③ は滅多に使う事がないので割愛します)
DockerHub からイメージを取得する
DockerHub とは Dockerイメージをクラウド上で管理するホスティングサービスです。
Docker の操作はターミナルから行います。
以下のコマンドで hello-world
イメージを取得します。
($
はターミナルでの操作を表しており入力不要です)
$ docker pull hello-world
上のコマンドは以下のコマンドと同義であり、リポジトリ名(hello-world)以外を省略しています。
$ docker pull docker.io/library/hello-world:latest
ログにPull complete
が表示されていれば取得成功です。
478afc919002
の数字は異なっていても問題ありません。
latest: Pulling from library/hello-world
478afc919002: Pull complete
ホスト上にあるDockerイメージは docker images
コマンドで確認できます。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest ee301c921b8a 8 months ago 9.14kB
ちゃんと取得できていますね。それぞれのヘッダーの意味は次のようになります。
-
REPOSITORY
- イメージのリポジトリ名を表します。1 つのリポジトリに複数のタグが存在します。
-
TAG
- リポジトリ内の特定のイメージを識別するためのラベルです。同じイメージの異なるバージョンや状態は、異なるタグを通じて識別されます。例えばイメージの新しいバージョン(3.0)がリリースされた時に、それは
3.0
のタグと最新のバージョンを意味するlatest
のタグが付けられます。
- リポジトリ内の特定のイメージを識別するためのラベルです。同じイメージの異なるバージョンや状態は、異なるタグを通じて識別されます。例えばイメージの新しいバージョン(3.0)がリリースされた時に、それは
-
IMAGE ID
- イメージを一意に識別するための ID(ハッシュ値)です。イメージ内容に基づいてIDが生成され、イメージの内容が同じであれば同じ IMAGE ID になります。
-
CREATED
- イメージが作成(ビルド)された日時を表します。pull した日時では無いので注意です。
-
SIZE
- イメージのサイズを表します。
リポジトリの詳細を見たい場合は DockerHub でリポジトリ名(今回の場合だと hello-world)を検索します。
コンテナを起動する
Dockerイメージからコンテナを作成し起動するにはdocker run
コマンドを使用します。
このコマンドは、イメージから新しいコンテナを作成するdocker create
コマンドと、そのコンテナを起動するdocker start
コマンドの機能を組み合わせたものです。
先程取得したhello-world
イメージからコンテナを作成し起動してみましょう。
$ docker run hello-world
ターミナルにHello from Docker
が表示されれば成功です。
Hello from Docker!
・
・
・
実はdocker run
で指定したイメージがホスト上になければ、Docker が自動で DockerHub からイメージを取得します。確認のためdocker run
に ubuntu のイメージを指定してみましょう。
$ docker run ubuntu
ログを確認すると Pull complete
の文字が表示されているので、イメージが自動で取得されていることが分かります。
005e2837585d: Pull complete
まとめ
- Docker イメージはコンテナを起動するのに必要な設定ファイルを纏めたもの
- DockerHub は Docker イメージをクラウド上で管理するホスティングサービス
-
docker pull <image>
で DockerHub からイメージを取得する -
docker run <image>
でイメージからコンテナを起動する -
docker run
は自動で DockerHub からイメージを取得する
Dockerfile からイメージを作成する
Dockerfile とは?
Dockerfile は Dockerイメージの設計図のようなもので、コードに基づいてどのような Dockerイメージであるかを定義したファイルです。
Dockerfileを使うと何が嬉しいのか?
コードで管理できる
Dockerイメージの内部構造は通常見えませんが、Dockerfile を使うことで、コンテナの構成をコードで明確に管理できます。
これにより同じ Dockerfile から再現可能なイメージが作成できます。
標準化された環境構築
Dockerfile を使用することで、アプリケーションが必要とする依存関係や環境設定を含む標準化された環境を作成できます。
例えば Ruby アプリケーションの場合、ホスト上にあるGemfile
とGemfile.lock
をコンテナにコピーすることにより、開発、テスト、本番環境で同じバージョンの依存関係を保持できます。
バージョン管理
Dockerfile はテキストファイルなので、GitHub で管理する事が出来ます。
これにより変更履歴の追跡やバージョンのロールバックが容易に行えます。
Dockerfile を作成する
実際に Dockerfile を作成してみましょう。
今回はデスクトップにdocker_practice
ディレクトリを作成してその中に Dockerfile
と test2
の 2 つのファイルを用意しました。
docker_practice
│
├── Dockerfile
└── test2
Dockerfileを作成する際、最初に記述するのはベースイメージの指定です。
FROM
インストラクションを用いて、使用するイメージのリポジトリとタグを指定します。
タグを省略した場合、デフォルトで latest
タグが適用されます。
FROM ubuntu:latest
この記述によりubuntu
の最新イメージがベースとして設定され、ここから続くコマンドはこのベースイメージに対して適用されます。
次にコンテナの中に test1ファイルを作成するためのコマンドを記述します。
Linux のコマンドを使用するにはRUN
インストラクションを使用します。
RUN
はビルド時(Dockerfile からイメージを作成する時)に実行されます。
RUN touch test1
続いてローカルに作成した test2 をCOPY
インストラクションを使ってコンテナにコピーします。コンテナにnew_dir
ディレクトリは作成していませんが、無ければ自動で作成してくれます。
COPY test2 /new_dir/test2
最後にCMD
インストラクションでイメージのデフォルトコマンドを指定します。
デフォルトコマンドとはイメージからコンテナを起動する際に、実行されるコマンドです。
次のコマンドではデフォルトコマンドにbash
を指定しています。
CMD ["bash"]
実はFROM
に指定した ubuntu のデフォルトコマンドにbash
が設定されているため、ここで改めてbashを設定する必要は実際にはありません。
これで Dockerfile の完成です。全体のコードを掲載しておきます。
FROM ubuntu:latest
RUN touch test1
COPY test2 /new_dir/test2
CMD ["bash"]
Dockerfile をビルドする
作成したDockerfileからイメージをビルドするためには、docker build
コマンドでbuild-contextを指定します。
build-contextはビルドに必要なファイルとディレクトリを内包したもので、以下のコマンドではカレントディレクトリ(.
)がそれに当たります。
-t
オプションでイメージ名を設定でき、タグを省略するとデフォルトで latest
タグが適用されます。
$ docker build -t my-ubuntu .
次のようなログが表示されれば成功です。
writing image sha256:c7165ae2a1e7a0f1cf...
ビルドに必要なファイル・ディレクトリがbuild-contextに含まれるため、作成したDockerfileからCOPYコマンド(COPY test2 /new_dir/test2
)を削除すると、test2ファイルはbuild-contextに含まれなくなります。これにより不要なファイルをDockerイメージに含めずに済み、ビルドプロセスの効率化を行えます。
コンテナの中身を確認する
docker run
で起動したコンテナはデフォルトコマンドを実行後、自動的に停止(Exit)します。
-it
オプションで、bash を対話的(インタラクティブ)モードで起動し、コンテナ内で bash を操作する事ができます。
それではdocker run -it
でコンテナ内に入りましょう。
$ docker run -it my-ubuntu
root@~#
と表示されればコンテナ内のアクセスに成功しています。
ここでls
コマンドを使ってファイルの確認を行ってみます。
# ls
bin dev home media new_dir proc run srv test1 usr
boot etc lib mnt opt root sbin sys tmp var
Dockerfile で定義した test1 と new_dir が作成されている事が分かります。
もちろん new_dir の中に test2 も作成されています。
コンテナは隔離しているのでls
で表示されたファイルシステムはホスト上から見る事は出来ません。
イメージを DockerHub に push する
Docker イメージを誰かに配布するには様々なアプローチが考えられますが、ここでは DockerHub にイメージを push して第三者にイメージを共有する方法を示します。
イメージを push するにはイメージ名をusername/repository
の形式に変更する必要があります。イメージ名の変更にはdocker tag
コマンドを使用します。
docker tag <SOURCE_IMAGE> <TARGET_IMAGE>
今回の場合ですと次のようになります。username
は自分の username に置き換えて下さい。
$ docker tag my-ubuntu username/my-ubuntu
docker push
コマンドで名前を変更したイメージ名を push します。
$ docker push username/my-ubuntu
特にエラーが出なければ、DockerHub のマイページに push したイメージのリポジトリが作成されていると思います。
まとめ
- Dockerfile を使うメリット
- コンテナの構成をコードで明確に管理できる
- 標準化された環境を簡単に構築できる
- Dockerfile インストラクション
-
FROM
: ベースのイメージを指定する -
RUN
: ビルド時に実行する Linux のコマンドを指定する -
COPY
: コンテナにコピーしたいファイルやディレクトリを指定する -
CMD
: Docker イメージのデフォルトコマンドを指定する
-
- build
-
docker build .
でDockerfileをビルドする - Dockerイメージに必要なファイルやディレクトリはbuild-contextに内包され、Dockerデーモンに送信される
-
- push
- イメージをpushするには
username/image
の形式にイメージ名を変更する -
docker tag <SOURCE_IMAGE> <TARGET_IMAGE>
でイメージ名を変更する -
docker push <image>
でDockerHubにイメージをpushする
- イメージをpushするには
Dockerイメージの構造
レイヤーの構成
Docker イメージは、複数のレイヤー(層)によって構成されています。これらのレイヤーはイメージを形成するさまざまなファイルや設定の集合です。
Docker イメージは次の 2 種類のレイヤーによって成り立っています。
読み取り専用レイヤー
Docker イメージを構成するレイヤーは基本的に読み取り専用です。これらはイメージが持つソフトウェアやライブラリ、アプリケーションのコードなどを含んでいます。
編集可能レイヤー
コンテナを起動すると、読み取り専用レイヤーの上に編集可能なレイヤーが追加されます。コンテナ内で行われる変更(ファイルの作成や変更など)は、この編集可能レイヤーに保存されます。
このレイヤー構造により次の効果をもたらします。
ストレージの節約
同じイメージから作成された複数のコンテナは、同じ読み取り専用レイヤーを共有しますが、編集可能レイヤーはそれぞれ別々に持ちます。これにより同じベースイメージの複数のコンテナを、ストレージを節約しながら運用できます。
環境のリセット
編集可能レイヤーを破棄することで、コンテナの変更を元に戻し、イメージの初期状態にリセットすることができます。
今回Dockerfile でFROM ubuntu
を指定しましたが、これにより ubuntu がイメージのベースレイヤーとして設定されます。これは新しいイメージの基礎となる部分です。
DockerHub にイメージを push する際、ベースレイヤー(ubuntu)は既に DockerHub 上に存在するため、その他の新しいレイヤーだけが push されます。これによりアップロードの時間とストレージの使用量が節約されます。
まとめ
- Dockerイメージは複数のレイヤー(層)構造
- レイヤーには読み取り専用と編集可能レイヤーがある
- レイヤー構造によりDockerイメージの配布と管理をより効率的にする
Docker の仕組み
Docker の構成
Docker は、以下の 3 つの主要なコンポーネントで構成されています。
- Docker クライアント
- Docker デーモン
- Docker レジストリ
Docker クライアント
Docker クライアントは、ユーザーが Docker の操作を行うためのインターフェースです。ユーザーが入力したコマンドを Docker デーモンに送信し、実際の処理を依頼します。
Docker デーモン
Docker デーモンは、Docker クライアントからのコマンドを受け取り、Docker のオブジェクト(例えばイメージやコンテナ)の作成や管理を行います。このデーモンは Docker ホスト上で動作し、イメージやコンテナなどの Docker オブジェクトをホスト内で管理します。
Docker レジストリ
Docker レジストリは Docker イメージの保存・共有を行う場所です。一般的な公開レジストリには Docker Hub がありますが、よりセキュリティが必要な場合はプライベートレジストリ(例:GitLab)を利用します。docker pull
、docker push
のデフォルトの接続先は DockerHub です。
これらのコンポーネントがdocker run
コマンドを実行した際に、どの様に動作しているのか解説します。
-
コマンドの入力(Docker クライアント)
ユーザーはコマンドラインやターミナルを通してdocker run
コマンドを入力します。 -
コマンドの転送(Docker クライアント → Docker デーモン)
入力したdocker run
コマンドは Docker クライアントによって処理され、Docker デーモンに転送されます。 -
イメージの取得(Docker デーモン)
Docker デーモンはdocker run
コマンドによって指定されたイメージをまずは Docker ホスト上にあるか確認します。存在しない場合、Docker レジストリにアクセスしてイメージを取得します。 -
コンテナの作成と起動(Docker デーモン)
Docker デーモンはイメージから新しいコンテナを起動し、デフォルトコマンドを実行します。 -
ユーザーへのフィードバック(Docker デーモン → Docker クライアント)
コマンドの実行結果(ログやエラー)を Docker クライアントに返却し、ユーザーに表示します。
まとめ
- DockerクライアントはユーザーとDockerデーモンの仲介役
- DockerデーモンはDockerクライアントの命令に従いDockerオブジェクトの作成や管理を行う
- DockerレジストリはDockerイメージの保存・共有を行う場所
おわりに
基礎的な内容をなぞるだけの記事になってしまいました。また知識が深まったらもっと深い部分まで記事で伝えれるようにリベンジしたいと思います。
参考文献
Docker-docs-ja
Dockerって何? って聞かれたときの解説、の解説
Dockerイメージの理解を目指すチュートリアル