コンテナを業務で使用していない私がDockerを基礎から学び、**Dockerって何?**というところから、Dockerfileからイメージをビルドしてコンテナを起動するまでの流れを記事にまとめました。
以下のレベル感の方向けの記事になっています。
- コンテナってなんだよ...
- Dockerってなんだよ...
- カーネルてなんだよ...
#Dockerって何?
いろんなサイトや文献をみるとDockerの定義がばらばらで頭が混乱するのですが、コンテナ作成や実行のためのプラットフォーム(管理ツール)であるDocker Engine*としての意味、もしくは以下のツールの総称として使われることが大半です。
- Docker Client(Docker CLI): Dockerコマンドを実行するコマンドラインツール
- Docker Server(Docker Daemon): イメージの作成、コンテナの実行などを行うもの
- Docker Machine: Mac, Windows上でDockerを使えるようにするもの
- Docker Images: コンテナの素となるもの
- Docker Hub: ユーザーが作成したコンテナをアップロードして公開・共有できるサービス
- Docker Compose: 複数のコンテナで構成されるアプリケーションについて、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行えるようにするツール
*Docker EngineはホストOS(カーネル)の上にあり、アプリケーション本体とそれを実行するための環境を1つのDockerイメージにまとめたり、Dockerイメージに基づいてコンテナを実行させ、簡単かつ高速にアプリケーションを起動できます。
#そもそもコンテナって何?
コンテナは、仮想マシンと同様に、1つのサーバ上で複数のサーバ環境を分離して実行する仮想化の技術です。
仮想マシンにはホストOS上にハイパーバイザーと呼ばれる管理層があり、ハイパーバイザーが提供する仮想的なハードウェアで複数の異なるOS(ゲストOS)を並列に実行します。
一方、コンテナは1つのOSの中を複数の区画に分離する技術であり、1つのカーネルの上で複数のOS環境が動きます。
そのため、「OSレベルの仮想化」とも呼ばれます。
仮想マシンが「イメージ」をもとにして作成されるように、コンテナにも「コンテナイメージ」というものが必要です。
コンテナイメージには、コンテナで実行するアプリケーションの実行形式ファイルや、その実行のために必要なファイル群(ライブラリ)しか含まれません。
このように、コンテナイメージは非常に軽量(数十~数百MB程度)です。
GB級の容量の仮想マシンイメージと比べて少ないリソースでのアプリケーション実行が可能となるため、起動が早く、メモリやCPUリソースを余分に使うことがありません。ネットワークでの共有も迅速に行なえます。
#コンテナにおけるカーネルの役割
仮想マシンと違ってコンテナではOS(カーネル)が共有されますが、コンテナが仮想マシンのように独立した環境をもつためには、ハードウェア上のリソースもコンテナごとに制限されている必要があります。
コンテナが必要以上のリソースを使わないために、カーネルの以下の機能が使われています。
- Namespacing
- Control Groups(cgroups)
##NameSpacing
Namespacingはコンテナを1つの仮想マシンのように占有されたシステムとして見せるためのカーネルの機能です。
ハードウェア内にセグメントをつくり、プロセスごとにリソースが隔離されます。
プロセスごとに環境を構築することができるため、他のLinux上で同じ環境を再構築することができます。
##Control Groups(cgroups)
Dockerでは、コンテナ内からアクセス可能なシステムのリソース(CPUやメモリなど)を制限しています。
これを実現させるために、カーネルのControll Groups(cgroups)という機能を使います。
Namespacingではホスト名やPIDなどのリソースを制御しますが、cgroupsはCPUやメモリなどの物理的なリソースを制御します。
改めて、コンテナというのはプロセスごとにリソースが隔離された状態であり、図にすると以下のようになります。
#コンテナイメージとは
コンテナを実行するためには、コンテナの素となるイメージが必要となります。
イメージの中身には以下の2つのものがあります。
- ファイルシステム
- メタ情報
##ファイルシステム(FS)
ファイルシステムというのは、コンテナ上のプロセス(実行アプリケーション)が必要とするファイルのことです。
DockerはLinuxカーネルで動作するため、ディレクトリ階層もLinux規格(/
ディレクトリ以下に/etc
/bin
/sbin
/usr
などがある)となります。
##メタ情報
アプリケーションの実行に必要なデフォルトのコマンドや引数の指定、外に公開するポート番号の情報、ボリューム領域などをまとめてメタ情報と呼びます。
以下の図のように、イメージからFSやメタ情報がコピーされることでコンテナが実行されます。
#簡単な動作確認
Dockerとコンテナについてわかってきたので、Dockerを導入して試しにコンテナを起動してみます。
##Docker Desktopの導入
DockerはLinuxカーネル上で動くので、WindowsやMacのPCだとそのままでは動きません。
そのため、Linux環境を簡単に構築できるDocker Desktop (for Windows or Mac)をインストールします。
インストール後に起動して、"Docker Desktop is running"となっていれば大丈夫です。
docker version
でバージョン情報を調べてみると、Client側(Docker CLIでコマンドを実行した側)はdarwin/amd64
で、Server側(Dockerコマンドによる命令を実行する側)はOS/Arch:linux/amd64
となっています。
(base) [20:52:08] → docker version ~
Client: Docker Engine - Community
Cloud integration: 1.0.4
Version: 20.10.2
API version: 1.41
Go version: go1.13.15
Git commit: 2291f61
Built: Mon Dec 28 16:12:42 2020
OS/Arch: darwin/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.2
API version: 1.41 (minimum version 1.12)
Go version: go1.13.15
Git commit: 8891c58
Built: Mon Dec 28 16:15:23 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.4.3
GitCommit: 269548fa27e0089a8b8278fc4fc781d7f65a939b
runc:
Version: 1.0.0-rc92
GitCommit: ff819c7e9184c13b7c2607fe6c30ae19403a7aff
docker-init:
Version: 0.19.0
GitCommit: de40ad0
##イメージからコンテナを起動
docker run [image]
でイメージからコンテナを作成して起動することができます。
hello world
イメージで試しにコマンドを実行してみます。
(base) [21:14:40] → docker run hello-world ~
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
docker run [image]
を実行すると、docker serverはイメージがローカルのイメージキャッシュ内にあるかどうかをまず確かめます。
キャッシュ内にイメージがなければDocker Hubの中を探し、ローカルにイメージを読み込みます。
##dockerコマンドをいろいろ実行してみる
###docker run [image]
イメージからコンテナを作成して起動します。
コンテナの作成はdocker create [image]
、起動はdocker start -a [container id]
で個別に行うことができ、これらの組み合わせがdocker run [image]
となります。
###docker run [image] [Linuxコマンド]
コンテナ作成起動後にLinuxコマンドを実行することができます。
例えば、ls
コマンドで出力される結果はコンテナ内のディレクトリ構成となります。
(base) [21:25:09] → docker run busybox ls ~
bin
dev
etc
home
proc
root
sys
tmp
usr
var
###docker ps
起動中のコンテナを全て表示します。
(base) [21:32:27] → docker ps ~
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66eaeff67b15 prismagraphql/prisma:1.12 "/bin/sh -c /app/sta…" 6 weeks ago Up 6 weeks 0.0.0.0:4466->4466/tcp prisma_prisma_1
1cb282826d22 complex_nginx "/docker-entrypoint.…" 3 months ago Restarting (1) 14 seconds ago complex_nginx_1
8eac3688a342 visits_node-app "docker-entrypoint.s…" 3 months ago Up Less than a second 0.0.0.0:4001->8081/tcp visits_node-app_1
###docker ps --all
今までに作成したコンテナを全て表示します。
(base) [21:33:16] → docker ps --all ~
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7fc44dbef90d busybox "ls -a" 5 minutes ago Exited (0) 5 minutes ago lucid_raman
86d2d24c54cb busybox "ls" 8 minutes ago Exited (0) 7 minutes ago
...
###docker system prune
今まで作成したコンテナを全削除します。
###docker logs [container id]
コンテナが起動されたときのログを出力します。
###docker stop [container id]
起動中のコンテナを停止します。
###docker kill [container id]
コンテナを強制停止します。
###docker exec -it [container id] [command]
コンテナに追加のプロセスを実行させることができます。
ターミナルへのアクセスなどを行います。
例えば、docker run redis
でredis-serverをコンテナに構築し、redis-serverを操作したくなったとします。
そんなときに、docker exec -it [container id] redis-cli
を実行することで、ターミナルからredis-cliのコマンドをうてるようになります。
-i
でredis-cliの標準入力と接続し、-t
で標準出力と接続することができます。
docker exec -it [container id] sh
でコンテナのシェルに接続し、Linuxコマンドがうてるようになります。
また、docker run -it [image] sh
でコンテナの作成起動と同時にコマンドがうてるようになります。
#Dockerイメージを作成してコンテナを起動してみる
コンテナの素となるDockerイメージは、以下の順序で作られます。
- Dockerfileの作成(Docker Serverへの命令内容を記述)
- イメージのビルド(
docker build .
の実行)
##Dockerfileの作成
Dockerfileにも記述の順序があります。
- ベースイメージの指定
- 追加プログラムをインストールするためのコマンドを実行
- コンテナプロセスの起動コマンドの指定
例えば、redisイメージ用のDockerファイルは以下のように記述します。
# Use an existing docker image as a base
FROM alpine
# Download and install a dependency
RUN apk add --update redis
# Tell the image what to do then it starts
# as a container
CMD ["redis-server"]
*Alpine: Linuxのディストリビューション(Linuxカーネルとその他ソフトウェア群を1つにまとめ、容易にインストール・利用できるようにしたもの)
*apk: Alpineのパッケージマネージャ
*redis: インメモリデータベース
Dockerfileの中身は、FROM
, RUN
, CMD
のようなDocker Serverへの命令内容に引数を付加するような構成になっています。
##イメージのビルド
docker build .
でDockerfileからコンテナイメージを作成(ビルド)します。
最後の.
ではDockerfileのあるディレクトリを指定しています。
今回はカレントディレクトリに置いてあるので.
のみとしています。
以下がビルドの流れです。
###1. コンテナが起動しapk add --update redis
でredis関連のプログラムをインストール
Alpineイメージのファイルシステム(FS)スナップショットがコンテナ内のディレクトリにコピーされコンテナが起動します。
その後にapk add --update redis
でredis関連のプログラムがインストールされます。
###2. コンテナが停止し新しいイメージAが作られる
redisがインストールされたファイルシステムのスナップショットがつくられ、コンテナは停止します。
コンテナが停止した後に、FSスナップショットから新しいイメージAがつくられます。
###3. 再度起動したコンテナに起動時のコマンドredis-server
を設定する
###4. コンテナが停止し起動コマンドが設定された最終的なイメージBが作られる
redis関連プログラムとコンテナ起動時のコマンドが設定された最終的なイメージがこれで完成します。
##完成したイメージからコンテナを作成・起動する
docker run [container id]
でイメージからコンテナの作成と起動を行うことができます。
しかし、この方法だとイメージをビルドしたときにcontainer idを控えておかなければなりません。
そんなとき、ビルド時にdocker build -t [タグ名] .
とすることでイメージにタグを付けることができ、docker run [タグ名]
でコンテナを実行することができます。
タグ名としては、[DockerID]/[Project名]:[Version](ex. suzuki0430/redis:latest)とつけるのが一般的です。
以上がDockerfileの作成からコンテナを起動するまでの流れです。
ちなみに、以下のコマンドを実行すれば、Dockerfileを使わずにこれまでの流れを実施することができます。
docker run -it alpine sh
apk add --update redis
docker commit -c 'CMD['redis-server']' [id]
docker run ~
#おわりに
記事でアウトプットしたことでコンテナとDockerの理解が深まりました。
次は実際のプロジェクトにDockerを導入したときの記事を書きたいと思います。
#参考資料