Dockerコンテナグレートジャーニー
Dockerコンテナを0から理解する旅、Dockerコンテナ・グレートジャーニー第2回です。
前回はDockerやらコマンドやらの前段階として、そもそも仮想化とは何か? という話をしました。仮想化って何? という方はぜひこちらからご覧ください。
今回は実際にコマンドでDockerコンテナを動かしながら、Dockerコンテナの基礎をイメージを交えて解説していきます。
対象
- Dockerの環境構築~コマンドの基礎まで実践したい人
- Dockerのコマンドをイメージ図を交えて理解したい人
- Dockerコマンドを、単なる暗記ではなくきちんと理解したい人
旅路(インデックス)
長いので記事を分割しています。
- そもそも仮想化とは? 仮想化ではないシステムとは?
- Dockerコマンド基礎(環境構築~ubuntu/httpdコンテナでのコマンド実行)【⇦本記事】
- Dockerのストレージについて
- Dockerのネットワークについて
- Docker-compose でコンテナをまとめる
- Docker image と Dockerfile
- AWS ECS で Dockerコンテナを走らせる(Comming soon)
【主題】Dockerコンテナ環境構築~ubuntu/httpdコンテナでのコマンド実行
この記事では、Dockerの環境構築から基本的なコマンドを使用するまでを、イメージ図を交えつつ解説していきます。
※環境はWindowsを想定していますので、それ以外の方は適宜読み替えてください。MACの方でも、Dockerコマンドのイメージ解説は役に立つと思います。
環境構築
(すでにDocker環境がある方は読み飛ばしてください。)
WindowsでDockerを使用するには、2つのソフトウェアをインストールする必要があります。
- WSL2
- Docker desktop
1. WSL2
何だこれ? 感じる方も多いかと思います。私もそうでした。
正式名称はWindows Subsystem Linux
と言い、Windows上でLinuxのコマンドを実行するためのソフトウェアです。
なぜこれが必要かというと、前提としてDockerはLinuxの上で動くように作られており、WindowsOSの上では直接動作しないからです。
なので、WSL2をインストールすることでコンテナがLinuxコマンドを使えるようになり、Dockerを操作できるようになります。WSL2がインストールされていないとエラーを吐くので注意してください。
具体的なインストール方法は こちらが参考になりそうです。
2. docker desktop
Dockerコマンドを仲介するDockerエンジンと、Dockerの管理に便利なGUI機能を搭載したパッケージです。また、最近では拡張機能なんかもあるようです。
公式サイトにダウンロードの手順やリンクが記述されていますので、環境構築はこれで大丈夫かと思います。
このページも参考になります。
dockerコマンド
次に、Dockerの基礎的なコマンドを解説していきます。
DockerにはDockerfileやDocker-composeという技術もありますが、個人的にはいきなりこれらから学ぶとDockerの理解から遠ざかるような気がしています。(事実私が混乱していた)
なので本記事では、Dockerの基礎部分たる生Dockerコマンドを解説します。ここをふんわりとでも把握しておけば、この後に出てくるDockerの仕組みやdockerfileもすんなりと理解できるはずです。
docker を使ってみる① ~Ubuntuコンテナを立ち上げる~
環境構築はできているはずなので、さっそくDockerコマンドを使ってみましょう。
この記事での目標は、下記のコマンドで何が起きているのかを理解することです。
docker container run -it --name linux_test ubuntu:22.04
まずはなにも考えず、Power shell(もしくはコマンドプロンプト)を立ち上げて上のコマンドを打ち込んでみましょう。コンソール上の結果は以下のようになるかと思います。
PS C:\Users\******> docker container run -it --name linux_test ubuntu:22.04
Unable to find image 'ubuntu:22.04' locally
22.04: Pulling from library/ubuntu
dbf6a9befcde: Pull complete
Digest: sha256:dfd64a3b4296d8c9b62aa3309984f8620b98d87e47492599ee20739e8eb54fbf
Status: Downloaded newer image for ubuntu:22.04
root@08804953cdff:/#
root@~~
となっているので、これでLinuxコンテナを立ち上げてその中に入ることができました。試しにls
やcat /etc/os-release
のコマンドを打ってみても、Linuxの中にいることがわかると思います。Linuxを立ち上げるなんて一昔前ではとても難しかったことですが、Dockerを使えば一瞬ですね。
コマンドを打ちこんだだけでは何も学びにならないので、今打ち込んだコマンドにどのような意味があるのか解説していきます。
コマンドの全体像
コマンドを一部日本語に置き換えてみました。コマンドの大まかな意味はこんな感じです。
docker container run -it --name linux_test ubuntu:22.04
↓↓↓↓↓
docker container run <オプション> --name <コンテナの名前を指定> <使用するイメージ>
ubuntu:22.04
順序が逆になってしまいますが、ここだけ先に説明します。
ここでは、使用するImageを指定しています。Imageとはコンテナの設計図のようなものです。Imageを指定することで、完成した設計図であるUbuntuの設定を引き継ぐことができ、一瞬で起動することができるのです。
Dockerは指定されたイメージを探すのですが、ローカルPCにubuntuのイメージが無ければDocker Hubという場所に探しに行きます。
今回使用するイメージはUbuntu公式のディストリビュータが管理しているものです。また22.04という数字はUbuntuのバージョンを指定しています。
これを省略してdocker container run -it --name linux_test ubuntu
とすると、自動でUbuntuの最新バージョン(latest)が指定されます。
本当に便利な仕組みを使えるものですね……
docker container run
Dockerコマンドの本体です。「Dockerコンテナを実行する」という動作ではありますが、内部的には3つのコマンドをまとめて実行しているイメージです。
- docker pull: Docker hubからイメージをダウンロードする
- docker create: イメージを元に、コンテナを作成する
- docker start: すでに存在するコンテナを起動する
※ docker run
でも動きますが、個人的にコマンドが何をしているかがわかりやすいので、積極的にdocker container run
と書いています。
-it
このコマンドは2つのオプションをまとめているものです。つまり、-it = -i -t
です。
少しややこしいのですが、分解して説明します。
-
-i
i とは、Interactive のことです。
相互に通信する、つまり標準入出力や標準エラーをコンテナに連結することを指します。 -
-t
t とは、TTY、つまり端末のことです。
難しい話は置いておいて、これをONにすることで esc, ctrl などの信号入力が有効になります。
つまるところ-it
と入力することでEscやCtrlなどの特殊コマンドを含めたデータのやり取りを有効化、送受信ができるようになります。これを付けないと、Ubuntuとコマンドのやり取りができないので、コンソール上でコマンドをやり取りするときは必ず付与するようにしましょう。
また-i
と-t
はセットで使うものであり、片方だけONにすることはあまりないようです。
--name linux_test
これは簡単です。コンテナに名前を付けているだけです。
本当に名前がついているかどうかは、docker container ls -a
コマンドで確認できます(-a
は停止しているコンテナも表示するオプションです)。記事の通りに進めてきたならまだUbuntuの中にいるかと思うので、新しくPowershellなどを立ち上げてコマンドを実行してみましょう。するとNAMESの所に、指定した「linux_test」という名前が記載されているはずです。
PS C:\Users\******> docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
08804953cdff ubuntu:22.04 "/bin/bash" 36 minutes ago Up 36 minutes linux_test
なお--name
は省略することもできて、その場合はランダムに名前が付けられるようです。管理が大変になるので、--name
を使う癖をつけておいた方がよさそうですね。(参考)
Ubuntuコンテナの振り返り
Ubuntuを立ち上げる超基礎的なコマンドがそれぞれどんな役割を果たしているのかを確認してきました。
docker container run -it --name linux_test ubuntu:22.04
このコマンドの順番を出現順にまとめると、
- docker container run
- docker pull, docker create, docker start をまとめたような、オールインワンなコマンド。
- -it
- -i: インタラクティブ。標準入出力、標準エラーを接続する。(コンテナに対して入出力できるようにする)
- -t: TTY, 端末。CtrlやEscなどの特殊コマンドを使えるようにする。
- 両方合わせて、コンテナとCLIでやり取りできるようになる
- --name server_test
- コンテナに名前を付ける。毎回付けることを推奨。
- ubuntu:22.04
- コンテナの元となるイメージを指定している。
- Ubuntuの設定がパッケージのダウンロードが済んでいる
- ローカルになかった場合、Docker Hub からダウンロードして使用する。
- 22.04はバージョンを指定している
ということでした。これでコンテナを動かす基本コマンドの説明は完了です。なんとなく理解できたでしょうか?
ここまでできたら、いったんexit
でコンテナを終了しましょう。
docker を使ってみる② ~httpdコンテナでサーバーを起動する~
次はサーバーを立ててみましょう。
Dockerコンテナでサーバーを立てる一番簡単な方法は、先ほどのUbuntuイメージと同じように、すでにサーバーの設定が完了しているImageをDocker hubからダウンロードして使うことです。
下図のようにDocker Hub上で色々なImageが用意されており、これらのイメージを使うことで簡単にサービスを構築できます。今回は軽量なサーバーであるhttpdを使ってみます。
( ⇑ Docker Hubに掲載されているImageの一部)
というわけで、まずは難しいことは考えずに下記のコマンドを打ち込んでいきましょう。
docker container run -d --name server_test -p 8080:80 httpd:2.4
結果は以下のようになります。
PS C:\Users\******> docker container run -d --name server_test -p 8080:80 httpd:2.4
Unable to find image 'httpd:2.4' locally
2.4: Pulling from library/httpd
f03b40093957: Pull complete
abaf8619eb1c: Pull complete
e3fe37d0c2ad: Pull complete
52a1e37affe5: Pull complete
49d8a68fd903: Pull complete
Digest: sha256:1bb3f7669a85713906e695599d29c58ab40d4e6409907946609d92a428e95b49
Status: Downloaded newer image for httpd:2.4
c4ef18d759b29ce1bb028f6584965074e4e9f21d43dd23def19b57f1066c9966
ローカルにhttpdのイメージが無い場合はダウンロード(Pull)が開始されます。
httpdはサーバーを立ち上げてくれるイメージなので、実はこれだけでローカルサーバーが立ち上がっています。
ブラウザを立ち上げて、http://127.0.0.1:8080
と入力してみてください。
すると「It works!」とサーバーが動いていることを示すページが返ってきます。
一応ローカルサーバーの仕組みにピンとこない方のために何が起きたかを簡単に図示します。
httpdコンテナはデフォルトで以下の設定がなされており、それに合わせて設定した形になります。
- httpdというイメージ自体が、デフォルトで80番ポートを解放している
- 80番ポートへのリクエストに対して、「It works」と書かれたHTMLファイルを返す
先ほどのUbuntu立ち上げの時には見なかったオプション(-p
および-d
)があるので、そちらについて説明していきます。
-p 8080:80
-p
オプションは、ポートの紐付けを行います。
httpdコンテナはサーバーであり、公式ディストリビュータが80番ポートでリクエストを待ち受けるように作られています。(デフォルトでhttp通信の待ち受け場所でもあるので自然ですね)
ただ、コンテナ内で80番ポートを解放していても、80番ポートはホスト側のhttpの待ち受けなので、そのままではホストPCの外部から直接アクセスはできません。なのでホストPCの8080番ポートとコンテナの80番ポートを紐付けることで、外部からホストPCの8080番ポートに来た通信をコンテナに横流ししています。
なので、httpdにおいては80番ポートは固定ですが、ホスト側のポートは他のシステムと重複しないように設計する必要があります。
-d
d とは Detached mode のことです。
簡単に言うと、バックグラウンドモードで起動するようなイメージです。
今回のコンテナはサーバーとしてバックグラウンドで動作させたいので、このオプションを付けています。
-d
を付けないと、メインプロセス(サーバーが動くプログラム)に入り込むことになります。
なお、今回-it
は付いておりませんが、httpd のようなサーバープログラムの場合、通常はバックグラウンドで動作し、インタラクティブな操作を必要としないため、-it のオプションを付ける必要はありません。
コンテナの中に入る
先ほどのhttpdコンテナでは-d
オプションを使うことでコンテナをバックグラウンドで動作させていました。
ではコンテナ内でのデバッグや設定編集したくなった時、どうすればいいでしょうか?
Dockerコンテナではコンテナ内部に入ることができます。本章では主に、docker container attach
とdocker exec
について説明します。
docker container attach
先ほどから-d
コマンドを使用するとdetacthモードになると説明してきましたが、それと対になりそうなdocker container attatch
というコマンドが存在します。
ただ注意が必要です。これをhttpdのようなデーモンプロセス(バックグラウンドで動作するもの)に使用すると、httpdサーバーの場合はこのコマンドを実行するとコンテナが終了してしまいます。
以下は実際にコマンドを入力した結果です。
PS C:\Users\******> docker container attach server_test
[Mon May 29 04:52:48.557329 2023] [mpm_event:notice] [pid 1:tid 140509665193280] AH00492: caught SIGWINCH, shutting down gracefully
なぜかshut down してしまいました。実際にlsコマンドで見てみると、
PS C:\Users\******> docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e8db51fd3a64 httpd:2.4 "httpd-foreground" 32 minutes ago Exited (0) 2 minutes ago server_test
のようにSTATUSがExitedになっています。
なぜこのような現象が起こったのでしょうか?
原因は3つあります。
1つ目はattachコマンドの特徴です。attachコマンドはPID=1のプロセス、つまりメインプロセスに接続しようとします。しかし、httpdのメインプロセスは、デーモンプロセス(バックグラウンドでのみ動作する)でありユーザーとのインタラクティブな対話を持つことはできません。結果としてhttpdのプロセスが終了してしまいます(細かい話は省きます)
2つ目の原因として、これは重要なことですが、コンテナには 『メインプロセスが終了するとコンテナが停止する』 という特性があります。なのでコンテナのメインプロセスが終了した時点で、このコンテナは停止状態になり、先ほどdocker container ls -a
で見たようにExitedとなるのです。(まあ、メインプロセスが動作ていないコンテナが生きている方が不都合ですが)
docker exec
では次に、Docker execコマンドを試してみます。
Dockerコンテナが稼働している状態で、以下のコマンドを打ちこんでください。
docker exec -it server_test /bin/bash
結果は以下のようになります。
PS C:\Users\******> docker exec -it server_test /bin/bash
root@9eb66ca15e16:/usr/local/apache2#
試しにls
コマンドやcat /etc/os-release
コマンドを打ちこんでみましょう。Linuxに入れていることがわかるかと思います(httpdサーバーはLinux上で動いているので)。
このままコンテナを抜けてもいいのですが、せっかくなのでWebページをいじってみましょう。
httpdコンテナは/usr/local/apache2/htdocs/index.html
にあるhtmlファイルを返すように設定されているので、これを書き換えれば返却されるファイルも変わります。
(httpdコンテナは軽量さを極めているためviコマンドがインストールされていません。ここではechoでファイルを上書きしてやります)
echo "<html><body><h1>Editted</h1></body></html>" > /usr/local/apache2/htdocs/index.html
ではコンテナを抜けるために、exit
、もしくはctrl+c
でプロセスを終了してみましょう。
root@9eb66ca15e16:/usr/local/apache2# exit
exit
PS C:\Users\******>
プロセスが終了し、コンテナを抜けられました。docker container ls -a
コマンドで確かめてみましょう。
PS C:\Users\******> docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9eb66ca15e16 httpd:2.4 "httpd-foreground" 30 minutes ago Up 4 minutes 0.0.0.0:8080->80/tcp server_test
STATUSが UP~~ となっており、コンテナは起動したままです。先ほどのdocker container attach
コマンドとの違いは何でしょうか?
イメージ図で説明するとこんな感じです。
docker exec -it server_test /bin/bash
コマンドは、メインとは別のプロセスを立ち上げています。なので終了させてもコンテナが終了することはありません。
補足ですが、Webブラウザをアップデートすると、index.htmlもちゃんと書き換わっていました。コンテナ内に入って、ファイルを書き換えられることがわかりますね(ただし、この変更はコンテナを破棄すると元に戻るので注意)。
結局docker exec
とは?
こんなことができるdocker exec
とは何者なのでしょうか?
平たく言うと、任意のコンテナに対して、任意のコマンドを実行させられるコマンドです。文法はこんな感じです。
docker exec <コンテナ名> <コマンド>
execコマンドで直接プロセスが立ち上がったのではなく、/bin/bash
というコマンドをコンテナ内で実行したことで新しいプロセスが立ち上げったということです。
なので、もちろん下記のように、ubuntuコンテナに対してlsコマンドを投げることもできます。
PS C:\Users\******> docker exec linux_test ls
bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr
コンテナのメインプロセスを維持しながら抜ける
今までのexitやCtrl+C ではコンテナを抜けると同時にプロセスが破棄されていましたが、「ctrl+Pを押した後に、ctrl+Qを押す」のようにすればプロセスを終了せずにコンテナから抜けられます。
せっかくなので実際に検証してみましょう。
httpdコンテナはメインプロセスに接続した瞬間に終了してしまうので、検証はUbuntuコンテナで行います。
まずはattachコマンドで入って、exitで終了するパターン。
一気に3コマンド分を記載します。
PS C:\Users\******> docker container attach linux_test
root@08804953cdff:/# exit
exit
PS C:\Users\******> docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
9eb66ca15e16 httpd:2.4 "httpd-foreground" 45 minutes ago Up 19 minutes 0.0.0.0:8080->80/tcp server_test
08804953cdff ubuntu:22.04 "/bin/bash" 2 hours ago Exited (0) 5 seconds ago
linux_test
docker container attach
でコンテナのメインプロセスに入り、すぐにexitで抜けて、そのあとdocker container ls -a でコンテナの稼働状況を確認しています。
結果として、Linuxコンテナは終了してしまっていました。メインプロセスが終了してしまったからですね。
では次に、exitではなく「ctrl+P -> ctrl+Q」を試してみます。docker container start
でコンテナを再起動して検証します。
PS C:\Users\******> docker container start linux_test
linux_test
PS C:\Users\******> docker container attach linux_test
root@08804953cdff:/# read escape sequence ### <- ここで「ctrl+P -> ctrl+Q」を押した
PS C:\Users\******> docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9eb66ca15e16 httpd:2.4 "httpd-foreground" 49 minutes ago Up 24 minutes 0.0.0.0:8080->80/tcp server_test
08804953cdff ubuntu:22.04 "/bin/bash" 2 hours ago Up 18 seconds linux_test
PS C:\Users\******>
Linuxコンテナが終了していないことがわかりました。コンテナから抜けるとき、メインプロセスを終了したくない場合はこのようにしてください。
VSCodeを使っている場合の注意点
(※ VSCodeを使用している場合、ctrl+Pおよびctrl+Q に別の機能が割り振られており、コンテナから抜けられないという状況に陥ってしまいます。ググってみると既存のショートカットを無効にする方法も書いてありますがあまりやりたくないので、こちらの記事を参考にしたり、そもそもDockerに入りたいときだけはPowershellから行うなどの方法をとればいいかもしれませんね)
参考
この書籍で勉強しました。かなりわかりやすく書いてあります。