Dockerコンテナとコンテナイメージの管理
コンテナ型仮想化で広く使用されているDockerについて、その導入から管理まで解説します。(LinuCレベル2 主題:2.06.2)
Dockerの概要
コンテナ型仮想化で広く使用されているのがDockerです。Dockerの各コンテナは、コンテナの動作プラットフォーム(Docker Engineなど)上で動作します。
DockerはGo言語で書かれていて、Apache 2.0ライセンスの元でオープンソースソフトウェアとして公開されています。
さらにKubernetesなどコンテナオーケストレーション技術を用いると、複数コンテナを連動させて可用性の高いシステムを短時間に構築し稼働させることができるようになってきています。
ただし、Dockerを使いこなすためにはシステム構成を考え、それに適した構成定義ファイル(Dockerfile)などを作成するスキルが必要で、ある程度の知識が必要です。
コンテナのファイルシステムとイメージの関係
各コンテナはイメージと呼ばれるコンテナのテンプレートファイルから生成されます。ユーザーは独自にコンテナに含めるコンポーネントを定義してイメージを作成することもできますし、Docker Hub(https://hub.docker.com/)などの公開リポジトリなどから、公開されている既成のイメージを取り込んでコンテナを生成することもできます。
Docker HubではubuntuなどのOS、mysql, mariadb, httpd, nginxなど各種アプリケーションやミドルウェアを含んでいるイメージが公開されています。
そして、docker pullコマンドを用いてイメージをダウンロードし、docker createコマンドでコンテナを生成、そしてdocker runコマンドでコンテナを実行できます。
Dockerの動作イメージ
Dockerの各コンテナはDocker Engine上で動作し、ホストマシンのカーネルを使用します。そのため、ハイパーバイザーや仮想マシンの負荷がないため、より軽量で高速な動作が期待されます。
Dockerは各種ミドルウェアライブラリのインストールや環境設定をコード化(テキストファイルに定義を記述)して管理します。
構成ファイルを共有することで、
- 誰でも同じ仮想環境を構築できる
- 作成したシステム環境を配布しやすい(構成ファイルを提供すればよい)
- コンテナの追加・削除が容易にできる
などの利点が得られます。
構成自動化ツール利用の利点
構成自動化ツールのセクションでも扱いましたが、各種設定をコード化して管理するInfrastructure as Codeを実現しています。コード化することにより、環境構築を自動化し、短時間に同じ環境を確実に再現できるため、システムの運用管理の負担軽減や、チーム開発時の環境構築の負荷軽減などに役立ちます。
開発段階からコンテナ上で開発を進め、本番環境に同じ環境を構築してデプロイ(本番環境に配置)することができるため、DevOps、開発(Development)と運用(Operation)の連携がスムーズになります。 開発メンバー間で設定ファイルを共有することで短時間に開発環境を生成し、バージョン調整やインストールなどの負担を減らせます。
Dockerの導入
Dockerコンテナを動作させるDocker Engineには、有償のEnterprise Edition(EE)と無償のCommunity Edition(CE)の2系統があります。初めてDockerを導入する場合にはCommunity版を使用するといいでしょう。
ここでは公式のインストール手順にしたがって、Docker CEをインストールしていきます。
まずはホスト名の確認を行います。
hostnameコマンドでホスト名が表示されることを確認しましょう。
$ hostname
centos7
続いてはrootに切り替えてから、Docker CEのインストールに必要なパッケージ、yum-utils、device-mapper-persistent-data、lvm2をインストールします。
- yum-utilsはyumのユーティリティーでyum-config-managerを含みます。
- device-mapper-persistent-dataとlvm2(論理ボリュームマネージャ)は、デバイスマッパーストレージドライバの動作に必要です。
# yum install -y yum-utils device-mapper-persistent-data lvm2
次にDocker Engineのインストールは、公式リポジトリを使用して行います。
まずはDocker社の提供するリポジトリを追加します。
# yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
リポジトリを追加したら、Docker Engine CEのインストールを実行します。
# yum install docker-ce docker-ce-cli containerd.io
Docker Engine CEがインストールされたら、起動してみましょう。
# systemctl start docker
動作確認のためにhello-worldイメージを動作させてみましょう。Docker Hubからhello-world(テスト用)イメージをダウンロードしてインスタンスを生成し、確認用のメッセージを出力します。runコマンドは、イメージの取得(pull)、コンテナの生成(create)、コンテナの起動(start)を連続して実行します。
# docker run hello-world
ホストOSの起動時にDockerを起動するには、systemd(CentOSやUbuntu最新安定版など)を使用しているのであれば以下のコマンドを実行します。
# systemctl enable docker
Dockerによるネットワークの構築
DockerのLinuxイメージを使用してコンテナを作成すると、通常はNAT(自動ポート変換)機能でホストOSのポートを自動的に変換してコンテナにIPアドレスを割り当てます。
しかしこの方式だと、名前解決ができずコンテナ名で相互にアクセスできないので、ブリッジ接続に変更して同一ネットワークに異なるIPアドレスで接続するように設定を変更し、コンテナ間の通信を可能にする場合があります。
まずはubuntu1,ubuntu2というコンテナを起動してネットワーク設定を調べてみます。最初にubuntu1という名前のコンテナを起動します。dは起動時に接続しない(detouch)というオプションです。
$ docker run -itd –-name ubuntu1 ubuntu /bin/bash
コンテナの起動に成功したら設定をinspectコマンドで確認します。
$ docker inspect ubuntu1
Networksという項目の内容をチェックしてみましょう。
[
{
(中略)
"Networks": {
"bridge": {
,,,
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
,,,
}
,,,
]
すると172.17.0.2というIPアドレスが設定されています。
同様にubuntu2というコンテナも起動します。
$ docker run -itd –name ubuntu2 ubuntu /bin/bash
ubuntu2も設定をinspectコマンドで確認します。
$ docker inspect ubuntu2
[
(中略)
"Networks": {
"bridge": {
,,,
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
,,,
}
}
]
すると、ubutnu2には、172.17.0.3という異なるIPアドレスが割り当てられています。
次にubuntu1からubuntu2にpingで疎通確認をしてみたいと思います。まずはコンテナを起動して、bashシェルを起動します。execは指定したコマンドを実行するオプションです。
$ docker exec -it ubuntu1 /bin/bash
次にpingコマンドを使うために、iputils-pingをインストールします。
root@d3ce49845646:/# apt-get update && apt install iputils-ping
インストールが成功したら、ubuntu1からubuntu2へpingを実行してみましょう。
root@d3ce49845646:/# ping -c 3 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=1.46 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=1.70 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=1.63 ms
続いて、コンテナ名でpingを送信してみましょう。
root@d3ce49845646:/# ping -c 5 ubuntu2
ping unknown host ubuntu2
デフォルトのネットワーク構成ではコンテナ名でアクセスできないので、ユーザー自身でブリッジネットワークを定義して、コンテナ名でアクセスできるようにしましょう。
まずは、独自のネットワークを定義します。ここではnw1とします。
$ docker network create nw1
続いて、ubuntu1,2のコンテナをnw1に接続します。
$ docker network connect nw1 ubuntu1
$ docker network connect nw1 ubuntu2
もう一度inspectコマンドを実行してnw1の情報を確認します。
$ docker inspect ubuntu1
するとネットワークのセクションに、”nw1”が表示されます。
続いては、ubuntu1に接続してpingを実行していきます。まずは、docker execコマンドでubuntu1コンテナに接続します。
$ docker exec -it ubuntu1 /bin/bash
コンテナに接続してコマンドプロンプトが切り替わったら、コンテナ名でpingを実行してみましょう。
root@d3ce49845646:/# ping -c 3 ubuntu2
PING ubuntu2(172.19.0.3) 56(84) bytes of data.
64 bytes from 172.19.0.3: icmp_seq=1 ttl=64 time=1.46 ms
64 bytes from 172.19.0.3: icmp_seq=2 ttl=64 time=1.70 ms
64 bytes from 172.19.0.3: icmp_seq=3 ttl=64 time=1.63 ms
これで、コンテナ名で通信ができるようになりました。
このようにして、独自に定義したブリッジネットワークを使用すると、同じホストにつながるコンテナ間で、シンプルなネットワークを構築することができます。 さらに異なるホスト上で動作するコンテナ間で通信を行うためには、各ホストOSにブリッジインターフェースを追加し、異なるホスト上で動作するコンテナを同一のL2ブリッジネットワークに接続して、通信を可能にするフラットなネットワークを構成することもあります。
Dockerコンテナの管理コマンド
Dockerのインストール直後に、docker runコマンドを実行して、hello-worldイメージをダウンロードしてコンテナを生成、動作させました。
Dockerにはこれ以外にもコンテナの操作をするコマンドが用意されています。
- docker ps/statsコマンドを使うと、稼働中のコンテナ一覧や詳細を表示できます。
- docker run/create/restart コマンドを使うと、コンテナの実行、イメージからの作成、再起動ができます。
- docker pause/unpauseコマンドを使うと、実行中のDockerコンテナのプロセスの一時中断、および再開を実行できます。
- docker stop/killコマンドを使うと、実行中のコンテナを停止したり、強制終了したりできます。
- docker rmを使うと、既存のコンテナを削除できます。
コンテナの接続してプロセスを実行する
docker attachやexecコマンドを使うと、実行中のコンテナに接続して、コマンドを実行することができます。
$ docker exec <コンテナ名> <コマンド名>
という書式で使用します。
例えば、CentOSが動作しているコンテナに接続してbash(シェル)を起動してみましょう。
$ docker exec -it ubuntu /bin/bash
- -iオプションは標準出力を開き続ける
- -tオプションは疑似ターミナルを割り当てる
というオプションで、この二つを指定すると、ホストOSからコンテナ内のシェルにコマンドを投入できるようになります。例えば、以下のようにホストOSのコマンドプロンプトがコンテナのシェルのコマンドプロンプトに切り替わります。コンテナ上でexitコマンドを実行するとホストOSのコマンドプロンプトに戻ります。
# [root@centos ~]# docker run -it ubuntu bash
root@d3ce49845646:/#
コンテナイメージの管理
Dockerレジストリは、Dockerイメージの管理(保管や提供)をする仕組みです。複数のリポジトリを持つことができ、それぞれ複数のイメージを格納しておけます。
リポジトリは、イメージを保存しておく領域のことで、ユーザーが名前をつけてイメージを識別したり、リポジトリ内のイメージを参照してコンテナを生成することもできます。
Docker Hubのように公開のレジストリも存在しますが、会社や組織内だけで公開せずにイメージを使いたい場合には、プライベートリポジトリを運用するのが適しています。
Dockerレジストリの開始方法
Dockerレジストリ(コンテナ)を開始するには、以下のようにdocker runコマンドを使用します。
$ docker run -d -p 5000:5000 –-name registry registry:2
- -d は、デタッチオプション、コマンド実行時にコンテナに接続しないオプションです。
- -p は、ホストのポートをコンテナのポートを関連付けるオプションです。
- nameオプションでレジストリ名を指定します。
- registry:2 は取得するレジストリのイメージの名前を指定しています。
docker imagesコマンドを使用すると、ローカルリポジトリ内のイメージ一覧を表示できます。
$ docker images
docker pull コマンドを使用すると、Docker Hub(公開リポジトリ)からイメージを取得します。例えば、下記はubuntuのイメージを取得します
$ docker pull ubuntu
docker tagコマンドを使うと、自分のレジストリ上にタグをつけて保存できます。例えば、先ほど取得した、ローカルにあるイメージにタグをつけるには、以下のようにします。
$ docker tag ubuntu localhost:5000/ubuntu
作成したイメージをローカルレジストリに登録するには、以下のようにpushコマンドで送信します。
$ docker push localhost:5000/ubuntu
登録したイメージをローカルのレジストリから取得するにはpullコマンドを用います。
$ docker pull localhost:5000/ubuntu
Dockerレジストリを停止・削除するには以下のコマンドを用います。
$ docker stop registry && docker rm -v registry
-vオプションで削除するレジストリ名を指定します。
docker importコマンドを使用するとアーカイブ(圧縮された)ファイルからイメージを生成します。例えば、以下のようなフォーマットでコマンドを実行すると、tgzファイルからイメージを生成します。ただし、タグ付けされていないイメージが生成されます。
$ docker import http://example.com/exampleimage.tgz
URLではなく、ローカルファイルシステムから読み込むことも可能です。その場合はURLの部分をファイルへのPATHにします。
より詳細なオプションは、Dockerの公式ドキュメントを参照してください。
http://docs.docker.jp/engine/reference/commandline/import.html
docker commitコマンドを使うと、既存のコンテナに名前をつけてイメージとして保存することができます。
$ docker commit default httpd
イメージの一覧を表示するには、docker imagesコマンドを使用します。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd 4f0aa49f1e67 48 seconds ago 197MB
上記の例ではdefaultがhttpdという名前のイメージとして保存されます。また、既存のイメージにタグをつけて保存することも可能です。
例えば、centosのイメージにhttpdをインストールし、centos:httpdという名前(前者がイメージ名で後者がタグ)でイメージを保存できます
$ docker commit centos centos:httpd
イメージの削除
イメージを削除するには、docker rmi (remove image)コマンドを使います。ただし、削除を実行するにはイメージを使用しているコンテナを停止しておく必要があります。
$ docker rmi httpd(イメージ名)
Dockerfile
ここまでは、docker pull/runコマンドでDocker Hubからイメージを取得したり、commitコマンドでコンテナからイメージを作成したりする手順を紹介しました。
しかし、この方法だとイメージを転用する際に不便な場合があります。例えば、イメージの構成内容をドキュメント化したり、イメージから生成したコンテナに対して手作業で設定を変更したりしなくてはならないケースが出てきます。
そこでプログラムのビルドに使用されるMakefileのように、コンテナの構成内容をドキュメントではなく、テキストファイルに定義しておいて、できるだけ手作業を減らして構成を自動化するための仕組みがDockerfileです。 Dockerfileを作成しておくと、テキストファイル内でイメージに含むアプリケーションやミドルウェアを定義して、docker buildコマンドでDockerコンテナのイメージをコマンドライン(docker buildコマンド)から自動生成できます。そのため、作業負担や操作ミス、作業時間などを減らせます。
Dockerfileの書式
Dockerfileは、以下のフォーマットで記述します。
# コメント
<命令> <引数>
主な命令には以下のようなものがあります。FROMだけでも実行できますが、主にRUNとADDを用いてカスタマイズされたイメージを作成します。
命令 | : | 操作内容 |
FROM | : | 元となるDockerイメージの指定 |
MAINTAINER | : | 作成者の情報 |
RUN | : | コマンドの実行 |
ADD | : | ファイル/ディレクトリの追加 |
CMD | : | コンテナの実行コマンド 1 |
ENTRYPOINT | : | コンテナの実行コマンド 2 |
WORKDIR | : | 作業ディレクトリの指定 |
ENV | : | 環境変数の指定 |
USER | : | 実行ユーザーの指定 |
EXPOSE | : | ポートのエクスポート |
VOLUME | : | ボリュームのマウント |
カスタマイズイメージ作成の例
例えば、Ubuntuのイメージをベースにして、nginxをインストールしたイメージを生成してみましょう。
$ mkdir mynginx
$ cd mynginx
$ vi Dockerfile
としてviエディターを起動し、以下のような内容を記述します。
FROM ubuntu
MAINTAINER Foo Bar foo@example.jp
RUN apt-get install -y nginx
ADD index.html /usr/share/nginx/html/
また、別途index.htmlファイルを作成しておきましょう。例えば、メッセージを1行だけ含むファイルを生成します。
$ echo “Hello Nginx on Docker!” > index.html
Dockerfileとindex.htmlが用意できたら、docker buildコマンドを実行してイメージを生成してみます。
$ docker build -t <イメージ名>:<タグ名> <Dockerfileのディレクトリ>
という書式でDockerfileからイメージを生成します。
$ docker build -t foo/mynginx:1.0 .
イメージを生成したら、imagesコマンドでイメージが追加されたことを確認しましょう。リポジトリ名とタグがDockerfileと一致していることを確認しましょう。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
foo/mynginx 1.0 4f0aa49f1e67 48 seconds ago 198MB
centos 7 2d194b392dd1 2 weeks ago 195MB
イメージからコンテナを生成・起動し、動作確認をしてみます。
$ docker run -d -p 80:80 –name foo/mynginx:1.0 /usr/sbin/nginx -g ‘daemon off;’ -c /etc/nginx/nginx.conf
起動したら、コマンドラインから、コンテナのhttpポートにアクセスして動作を確認してみましょう。
$ curl localhost
Hello Nginx on Docker!
というようにindex.htmlファイルに記述した内容が表示されたら成功です。
もしもこのコンテナが不要な場合は、コンテナの停止、削除を実行しましょう。
$ docker stop mynginx
mynginx
$ docker rm mynginx
mynginx
執筆者紹介
・太田 俊哉
・井上 博樹
このドキュメントは、LinuCレベル2の学習用の教材から抜粋して作成されたものです。教材全体は以下のPDFファイルをご覧ください。
LinuCレベル2学習教材