docker.svg

【Docker】Dockerをはじめてみる

Docker

Dockerとは

参考:https://docs.docker.com/

Dockerはアプリケーションを開発、配布、実行するためのオープンプラットフォームです。 コンテナと呼ばれる仮想化技術を使用し、分離された環境でアプリケーションをパッケージ化して実行する機能を提供します。

少し難しいかもしれないので、まずは基本的なことについてから簡単に説明をしていきます。

仮想化

仮想化は、コンピュータ上で仮想的なリソースや環境を作り出す技術です。 例えばサーバー仮想化であれば、1台の物理サーバー上に複数の仮想サーバーを構築することができ、物理サーバーのメモリやストレージなどのハードウェアリソースをそれぞれの仮想サーバーに割り当てます。 これら仮想化の管理にはソフトウェアが必要で、代表的なものとしてVMware、Microsoft Hyper-V、XenServerなどが利用されています。

仮想化技術は、主にホストOS型、ハイパーバイザー型、コンテナ型の3つに分類されます。

ハードウェア ホストOS 仮想化ソフトウェア ゲストOS アプリ 仮想マシン コンテナ Docker ホスト型 ハイパーバイザー型 コンテナ型

ホストOS型は、ホストOSに仮想化ソフトウェアをインストールし、仮想サーバーを構築します。 ハイパーバイザー型は、ホストOSを必要とせず、ハードウェアにインストールした仮想化ソフトウェアによって仮想サーバーを構築します。 どちらにもメリット、デメリットがありますが、共通していることは、仮想サーバー上のOS(ゲストOS)上でアプリケーションを動かす必要があります。

コンテナ

コンテナは、ゲストOSを必要とせず、様々なアプリケーション(ソフトウェア)を入れて実行することができます。 例えばPHP、Nginx、MySQLを入れて、コンテナ内でWEBアプリケーションを構築するといったことが可能です。 (※ゲストOSが利用できないということではありません。)

各々のコンテナは隔離された状態で動いています。 コンテナからホストOSや他のコンテナのプロセスにアクセスすることはできません。 またリソースも隔離されており、コンテナ毎にルートディレクトリやIPアドレスなどが割り当てられます。

ホストOS PHP Nginx MySQL コンテナ ホストOS' Python MariaDB 干渉する 干渉しない 同一環境の構築が容易 コンテナ同士干渉しない

開発者にとって、コンテナの利用にはいくつかのメリットがあります。

  • アプリケーションの管理
    OS上にインストールしたアプリケーションの管理は非常に面倒です(私感)。 過去の開発で使用したアプリケーションがそのまま残り続けるなんてことはよくあることだと思います。 例えばJavaなど複数バージョンの管理(同居管理)が必要なものもあります。

    コンテナを利用すれば、コンテナ単位でアプリケーションを管理でき、不要になればコンテナを停止・削除するだけで済みます。 バージョンの同居もコンテナで分ければ問題ではなくなります。

  • 環境の管理・移行・分配
    コンテナは、イメージとして出力することで他の端末にスムーズに移すことなどができます。 例えば新規の開発メンバーが開発環境を構築する場合、必要なアプリケーションを個別にインストールするのではなく、イメージを渡せば簡単に構築できますし、統一も取れます。 また前述のようにコンテナはホストOSからは隔離された環境のため、開発者毎のOSの種類やバージョンに依存しません。
  • デプロイ
    コンテナはそのまま本番環境として利用することができます。 本番環境へデプロイする場合は、対象コンテナのイメージを作成し、それを本番環境で展開するといった感じです。 このイメージの作成〜本番環境の展開までのデプロイ作業は、CI/CDによって自動化が可能です。

便利ではあるのですが、他の仮想化と比べて学習コストが高いのが難点です。 (備忘録的な記事のため、筆者も執筆時点ではよくわかっていないです。)

コンテナを利用するために、DockerやKubernetesといったソフトウェアが必要となります。 Dockerは単一のコンテナを作成・実行するためのもので、Kubernetesは複数のコンテナを管理するためのものになります。 Docker単体でも利用は可能ですが、複数のコンテナを利用する場合はKubernetesと組み合わせて使用することが多いようです。

イメージ

Docker ファイル Docker イメージ コンテナ 停止 起動 build pull rm run rm commit push start stop restart Docker Hub

Dockerでコンテナを構築する上で重要になるのがDockerイメージです。 イメージは、コンテナを構築するためのファイルやスクリプト、設定などがまとめられたテンプレートになります。 このテンプレートからコンテナを作成することになります。 イメージは変更ができないため、同じイメージであればどの環境でも同じコンテナ環境が構築されます。 そのため、前述のような開発者への配布やデプロイの自動化などが可能になるわけです。

イメージ自体の作成にはDockerfileを用います。 Dockerfileは、使用するアプリケーションの情報などコンテナの構成要素をテキストベースで記述したファイルであり、これをビルドすることによってイメージを作成します。 またコンテナそのものからイメージを作成することも可能です。

イメージは、Docker Hubなどのレジストリで公開されているものもあり、ここから必要なイメージを取得(pull)することができます。 また自身で作成したイメージをDocker Hubに登録(push)することもできます。

Dockerのインストール

以下サイトからOSに該当するファイルを取得してインストールをします。 インストール手順は下記サイトに記載があるため、ここでの説明は省きます。

https://matsuand.github.io/docs.docker.jp.onthefly/get-docker/

インストール後、以下コマンドでDockerのバージョンが確認できることを試してみてください。

$ docker --version
Docker version 24.0.7, build afdd53b

Dockerの基本操作

Dockerは以下のようなコマンドを用いて操作します。

コマンド説明
image pullDocker HubからDockerイメージを取得する
image ls取得済みのDockerイメージの一覧を表示する
image rm取得済みのDockerイメージを削除する
image saveイメージをファイルに保存する
image loadイメージのファイルを読み込む
container runDockerイメージからコンテナを作成する
container lsコンテナ一覧を取得する
container rmコンテナを削除する
container start停止中のコンテナを起動する
container stop起動中のコンテナを停止する
container kill起動中のコンテナを強制的に停止する
container execコンテナ上のコマンドを実行する
container commitコンテナの変更状態からDockerイメージを作成する

Docker v1.13以降コマンドが新しくなっています。旧コマンドも利用できますがここでは新コマンドを使用していきます。

イメージの操作

まずはコンテナの作成に必要なイメージを取得します。 以下はubuntuのイメージを取得する例です。

$ docker image pull ubuntu

ubuntuのレジストリは以下のURLにあります。

https://hub.docker.com/_/ubuntu

このページのTagsタブを開いてみてください。 同じubuntuでもバージョンが異なれば構成、すなわちイメージファイルも異なります。 このバージョン(イメージ)の違いをタグをつけることによって判別できるようになっています。 上記のコマンドではlatest(最新)のubuntuイメージが取得できていると思います。 タグを指定する場合はimage:tagのようにします。

$ docker image pull ubuntu:20.04

取得したイメージは以下のコマンドで表示できます。

$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       latest    e2e172ecd069   13 days ago   69.3MB
ubuntu       20.04     fde9c12d7d3f   6 weeks ago   65.7MB

ここに表示されているIMAGE IDは、イメージを一意に識別するための情報となります。 タグは同じイメージに複数設定できるため、別のタグでも同じイメージということがあります。 これを判断するためにIMAGE IDの値を比較します。

イメージを削除するには以下のコマンドを実行します。

$ docker image rm ubuntu

この場合はlatestのイメージが削除されます。 タグを指定する場合は、取得時と同様にimage:tagとします。

$ docker image rm ubuntu:20.04

またIMAGE IDを指定することでも削除できます。

$ docker image rm e2e172ecd069

コンテナの作成

コンテナを作成するためには以下のコマンドを実行します。

$ docker container run hello-world
478afc919002: Pull complete 
Digest: sha256:4bd78111b6914a99dbc560e6a20eab57ff6655aea4a80c50b0c5491968cbc2e6
Status: Downloaded newer image for hello-world:latest

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.
    (arm64v8)
 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

上記はhello-worldというイメージを使用してコンテナを作成しています。 上記のように実行結果が表示されていれば成功です。 指定したイメージが端末上に存在しない場合は、pullが実行されます。

次に以下のコマンドでubuntuのコンテナを作成します。

$ docker container run -it ubuntu bash
root@c0b56aa19d60:/# ls
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@c0b56aa19d60:/# exit

するとターミナルがroot@XXXXXXXのようになります。 これはbashを通じてubuntuのコンテナにアクセスしている状態になります。 試しにlsコマンド実行してみると、ubuntuのディレクトリ構成が表示されます。 終了する場合はexitコマンドを実行します。

オプション指定している-i-tについてですが、これによってターミナルからubuntuが操作できるようになります。 コンテナはデフォルトではターミナルからの標準入力を受け付けませんが、-iによって受け付けるようになります。 試しに-iのみで実行してみます。

$ docker container run -i ubuntu bash
ls   #lsと入力してEnter
bin
boot
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
exit #exitと入力してEnter

実行すると何も表示されませんが、先ほどと同様にlsコマンドなどを実行するとその実行結果が表示されます。 一応操作はできますが、かなり見にくい状態です。 そこで-tによって擬似的なTTY(Teletypewriter)を割り当てます。TTYとは標準入力となっている端末デバイスであり、これにより表示結果が整形されます。 ひとまずは-itはおまじないと思っていても問題ないかと思います。

コンテナの操作

では一度現在コンテナの状態を以下のコマンドで確認します。 -aオプションは停止中のコンテナを表示に含めます。オプションがない場合は起動中のコンテナのみが表示されます。

$ docker container ls -a
CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
c0b56aa19d60   ubuntu        "bash"     23 seconds ago   Exited (0) 5 seconds ago              hardcore_wing
bd3a99508f88   hello-world   "/hello"   15 minutes ago   Exited (0) 14 minutes ago             funny_mirzakhani

表示結果のCONTAINER IDNAMESはコンテナを識別するための値です。 コンテナを操作の際には、どちらかの値で対象のコンテナを指定します。 ここではNAMESを使用することとします。 上記ではNAMESはランダムに生成されていますが、コンテナ作成時に--nameオプションによって指定することができます。

$ docker container run -it --name ubuntu_test ubuntu bash

STATUSはコンテナの状態を表します。Exitedは現在停止していることを表します。 試しに以下のコマンドでubuntuのコンテナを起動し、STATUSの値を確認します。

$ docker container start hardcore_wing
$ docker container ls -a
CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
c0b56aa19d60   ubuntu        "bash"     5 minutes ago    Up 4 seconds                          hardcore_wing
bd3a99508f88   hello-world   "/hello"   20 minutes ago   Exited (0) 19 minutes ago             funny_mirzakhani

見ての通りSTATUSUpになりました。これは起動中であることを表しています。

コンテナを停止させる場合は以下のコマンドを実行します。

$ docker container stop hardcore_wing

コンテナを再起動させる場合は以下のコマンドを実行します。

$ docker container restart hardcore_wing

コンテナを削除する場合は以下のコマンドを実行します。 起動中のコンテナはそのままでは削除できないため、停止するか-fオプションを指定します。

$ docker container rm hardcore_wing
$ docker container rm -f hardcore_wing

コマンドの実行

上記のdocker container startによってコンテナを起動させることはできましたが、docker container runの時のようにubuntuのコンテナを操作できる状態にはありません。 コンテナを操作するためには以下のコマンドを実行します。

$ docker container exec -it hardcore_wing bash

これは、対象コンテナのbashコマンドを実行するという内容になります。 例えばbashlsに変えると、ubuntuのルートディレクトリの情報が表示されます。

$ docker container exec -t hardcore_wing ls
bin  boot  dev  etc  hello.txt  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

このようにコンテナ内のコマンドは、docker container execによって実行することが可能です。

イメージの作成

現在のコンテナの状態からイメージを作成することを考えます。 違いがわかるようにコンテナ内にファイルを作成しておきます。

$ docker container exec -it hardcore_wing bash
root@c0b56aa19d60:/# echo Hello World > hello.txt
root@c0b56aa19d60:/# ls
bin  boot  dev  etc hello.txt home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

コンテナからイメージを作成するためには以下のコマンドを実行します。

$ docker container commit hardcore_wing myubuntu:1.0

myubuntu:1.0はイメージ名とタグになります。 docker image lsでイメージが作成されているか確認します。

$ docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
myubuntu      1.0       202ec66e7ed0   34 minutes ago   69.3MB
ubuntu        latest    e2e172ecd069   2 weeks ago      69.3MB

次に作成したイメージでコンテナを作成し、イメージ作成前に作ったhello.txtがあるかを確認します。

$ docker container run -it myubuntu bash
root@86b7df2ccefe:/# ls
bin  boot  dev  etc  hello.txt  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@86b7df2ccefe:/# cat hello.txt
Hello World!

このようにdocker container commitによってコンテナの状態からイメージを作成することができます。

イメージの保存・読込

イメージは以下のコマンドでアーカイブファイルとして保存することができます。

$ docker image save -o myubuntu_image.tar myubuntu

先ほど作成したmyubuntuのイメージを、myubuntu_image.tarという名前でカレントディレクトリに保存しました。 保存したアーカイブファイルは、以下のコマンドで再度イメージとして読み込むことができます。

$ docker image load -i myubuntu_image.tar
$ docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED             SIZE
myubuntu      1.0       202ec66e7ed0   About an hour ago   69.3MB

デタッチモード

NgnixをDockerで起動させてみます。

$ docker container run -it -p 8080:80 --name webserver nginx

-pオプションはポートの割り当てになります。上記は80番ポートを8080番ポートになるような指定になります。 http://localhost:8080にアクセスしてWelcome to nginx!と表示されれば問題なく起動できています。 終了する場合はCtrl + Cを入力します。

Nginxの起動中、ターミナルはNginxのログが出力されており、使用できない状態になっているかと思います。 それでは困るため、Nginx起動中もターミナルが利用できるように-dオプションを指定します。

$ docker container run -dit -p 8080:80 --name webserver nginx

これによってターミナルが利用できる状態になったかと思います。 この状態をデタッチモードといい、-dはデタッチモード(バックグラウンド)で起動するというオプションになります。 オプションを指定しなかった場合は、アタッチモードという状態(フォワグラウンド)で起動しています。 ubuntuではアタッチモードで問題ないのですが、Nginxのようにデタッチモード出ないと困るケースもあります。

デタッチモードで起動したコンテナをアタッチモードに変更するには、以下のコマンドを使用します。

$ docker container attach webserver

アタッチモードからデタッチモードに変更するには、Ctrl + P + Qを入力します。 ここで注意が必要なのは、コンテナ作成時に-itオプションを指定しなければ、上記キー入力は効果がありません。