sioaji2012のブログ

普段は組み込み開発でC言語のみです。主にプログラムや勉強日記です

Docker お勉強 01 起動・イメージ作成

Redmineプラグインのもくもくをやるのに、Dockerで開発環境を準備する事にしたけれど、使い方を全く知らないので少しづつお勉強します。

さくらのナレッジ の Docker入門でお勉強して、覚え書きをここへ記載します。

knowledge.sakura.ad.jp

公開されているDockerイメージを使用し、コンテナの基本的な操作

Dockerイメージ、コンテナのライフサイクル

  1. 公開されているDockerイメージの取得
  2. それをもとにコンテナを起動
  3. コンテナの停止・削除
  4. Dockerイメージの削除

公開されているDockerイメージの取得

「hello-world」というDockerイメージを使用して、コンテナを起動。

 # docker run hello-world

このhello-worldイメージは、コンテナ起動をしたら、コンテナの中でメッセージを出力するようなDockerイメージとなっています。

docker run

Dockerイメージを使用して、コンテナを起動するコマンド

※最初はローカル環境にはDockerイメージはありません。そのため、Docker Hubに探しに行き、存在すればDockerイメージをダウンロードします。

コンテナの状態
# docker ps -a
CONTAINER ID   IMAGE         COMMAND    CREATED          
c1b9013f9f66   hello-world   "/hello"   27 minutes ago 
  
STATUS                      PORTS   NAMES
Exited (0) 27 minutes ago           distracted_swirles
CONTAINER ID コンテナのID 備考
IMAGE 使用したDockerイメージ
COMMAND コンテナ起動時にコンテナ内で実行するコマンド
CREATED コンテナを作成したタイミング
STATUS コンテナ状態 Exited:停止中
Up: 起動中
PORTS ポートフォワード設定
NAMES コンテナの名前

※コンテナの名前は、指定しなければ自動で設定されます。

(コンテナの名前の指定の仕方は後ほど)

※ コンテナは、1プロセスは動いている必要があります。

 今回のhello-worldイメージでは、メッセージを出力するだけのプロセスだったため、そのプロセスが終了したタイミングでコンテナは停止しています。

Dockerコンテナ、イメージを削除する

コンテナ一覧

# docker ps -a
CONTAINER ID   IMAGE         COMMAND    CREATED          
c1b9013f9f66   hello-world   "/hello"   27 minutes ago   

STATUS                      PORTS   NAMES
Exited (0) 27 minutes ago           distracted_swirles

※停止中のものをそのままにしておくと、ゴミデータが残ったままになったり、ディスク圧迫にもつながるので、不要になったものは削除を忘れないようにしましょう。

※「docker ps」(「-a」を付けない)と実行すると、停止中のコンテナは表示されないので、注意してください。

 気づいたらたくさんの不要コンテナが溜まっていた、ということにもなりかねません。

イメージ一覧

# docker images
REPOSITORY    TAG      IMAGE ID       CREATED        SIZE
hello-world   latest   f2a91732366c   3 months ago   1.85kB

※イメージについても、不要なイメージは削除しましょう。

 特に、JenkinsなどのCI(Continuous Integration) ツールを使用して、Dockerイメージの作成やコンテナ起動を自動で実行するような場合、気づかないうちに不要データが残り続けて、ディスクを圧迫することになるので注意しましょう。

 (Disk Fullを起こすと、Docker環境の復元も困難になる場合があるので、十分注意したほうが良いです。)

コンテナを削除
# docker rm <コンテナ名>
または
# docker rm <CONTAINER ID>

例:

docker rm distracted_swirles
docker rm c1b9013f9f66
Dockerイメージの削除

コンテナを削除しても、Dockerイメージまでは削除されません。

# docker rmi <Dockerイメージ名> 
または
# docker rmi <IMAGE ID> 

例:

# docker rmi hello-world
または
# docker rmi f2a91732366c

Nginxイメージを使ってコンテナを動かしてみる

NginxのDockerイメージを取得

# docker pull nginx

取得したNginxイメージを使用してコンテナを起動

Nginxコンテナが起動するので、8181ポートに外からアクセスできるように設定すれば(Firewallなど)、以下のURLでNginxにアクセスすることができます。

http://<作成したインスタンスIPアドレス>:8181/

# docker run -d --name nginx-container -p 8181:80 nginx

実行コマンドのオプション

オプション 説明
-d コンテナをバックグラウンド実行します。
このオプションを付けない場合、コンテナ起動時に実行されるコマンドを実行した状態になり、例えばそのコマンドのコンソール出力が表示された状態になります。
–name コンテナ名を指定します。
(指定しない場合、自動で名前がつく)
-p ホストとコンテナ間のポートフォワード設定。
基本的には、「-p <ホスト側のポート>:<コンテナ側のポート>」で書きます。
(ホスト側を省略すると自動で設定されます。)
コンテナは、Dockerにより作成されるネットワークに属するため、このオプションを使わないと、ホストのIPアドレスを用いて、コンテナで使用しているポートにはアクセスができません。

コンテナ状態を確認

Nginxイメージの場合は、コンテナ起動時にnginxプロセスが実行されたままの状態になるため、コンテナも停止せずに起動状態となります。

docker ps コマンドで見てみると、STATUSが「Up」となっています。

# docker ps -a
CONTAINER ID   IMAGE   COMMAND                  CREATED      
a61413193f44   nginx   "nginx -g 'daemon ..."   7 days ago   

STATUS         PORTS                  NAMES
Up 8 seconds   0.0.0.0:8181->80/tcp   nginx-container

コンテナを停止する

# docker stop nginx-container
nginx-container

Proxy配下でDockerを使用する場合

Docker CEの場合

# mkdir -p /etc/systemd/system/docker.service.d
# vi /etc/systemd/system/docker.service.d/http-proxy.conf

[Service]
Environment="HTTP_PROXY=http://<プロキシサーバのIPアドレス>:<プロキシサーバのポート>/ HTTPS_PROXY=http://<プロキシサーバのIPアドレス>:<プロキシサーバのポート>/" 

# systemctl daemon-reload
# systemctl restart docker

Docker for Mac/Windowsの場合 メニューからたどる

preferences > proxies

公開Dockerイメージにミドルウェアやアプリをインストールして利用する

CentOSのDockerイメージをもとにTomcatが動くコンテナを構築するなかで、各種dockerコマンドについて説明します。

また、自分で構築したコンテナから、Dockerイメージを作成し、使用します。

CentOSのDockerイメージを取得し、コンテナを起動する

(docker runの「-v」オプションについては、この後説明する)

[ホスト]

# docker pull centos:7
# mkdir -p /root/tomcat-container/logs
# docker run -it -d -p 18080:8080 -v /root/tomcat-container/logs:/share/logs --name tomcat centos:7
  • 「/root/tomcat-container/logs」は任意のディレクトリで良い。
  • mkdir -pオプション(--parentsオプション)
ディレクトリが存在していてもエラーメッセージを表示せず、
現在あるディレクトリはそのままにして新規に作成することはなく子ディレクトリを作成する。
CentOSコンテナの中でTomcatインストール

今回使用しているCentOSのDockerイメージでは、インストール済みのコマンドは限られています。

そのため、必要に応じてyumなどでインストールする必要があります。

まず、Tomcatをホスト側にダウンロード

コンテナの中でダウンロードも可能です。

今回は、ホスト側からコンテナへのファイルコピー方法を示すため、このような手順にしています。

[ホスト]

# wget http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.6/bin/apache-tomcat-9.0.6.tar.gz
以下のコマンドでホスト側からコンテナ内にファイルをコピー

[ホスト] docker cp <ホスト側のファイル> <コンテナ名>:<コンテナ内のコピー先ディレクトリ>

# docker cp apache-tomcat-9.0.6.tar.gz tomcat:/opt/

また、コンテナからファイルを取得することも可能です。以下のようなコマンドになります。

docker cp <コンテナ名>:<コンテナ内のコピー元ファイル> <ホスト側のコピー先ディレクトリ>

コンテナにログイン

[ホスト]

# docker exec -it tomcat bash

docker exec [オプション] [コンテナ] [コマンド] [引数...]

オプション 説明
-d, --detach 指定したコマンドをコンテナ内でバックグラウンドで実行します。
-e, --env <定義> 指定したコマンドを実行する際に環境変数を適用します。
-i, --interactive 指定したコマンドを実行した後、コンテナの標準入力をDockerホストの標準入力と接続します。
-t, --tty コンテナ内で疑似的な仮想端末を割り当てて、Dockerホストの標準出力と接続します。
--privileged 指定したコマンドを特権モードで実行します。
-u, --user 指定したコマンドを特定ユーザで実行します。
-w, --workdir コンテナ内でコマンドを実行する際の作業ディレクトリが指定できます。

コンテナ内でTomcatのインストール

[コンテナ内]

# yum install -y java
# cd /opt/
# tar zxf apache-tomcat-9.0.6.tar.gz
# cd apache-tomcat-9.0.6
# ./bin/startup.sh

これでTomcatが起動しました。物理マシン上にインストールする場合と基本同じです。

ブラウザで以下にアクセスすれば、Tomcatの画面が開きます。

docker runコマンドの「-p」オプションでホスト側の18080ポートをコンテナ内の8080にポートフォワードするようにしているので、18080ポートを使用することになります。

http://:18080/

これでTomcatのコンテナができました。
自分でWebアプリケーションを作成していたら、Tomcat配下のwebappsディレクトリに配置すれば、コンテナ内で自分のアプリケーションを起動させることができます。

ホスト側とのディレクトリ共有

注意点

Tomcatのログはコンテナのディレクトリにあります。

そのため、コンテナが停止してしまったら、ログを取得するために再度コンテナを起動させる必要があります。

また、もしコンテナが削除されてしまった場合は、ログの取得すらできなくなってしまいます。

実際に運用しているサービスでログが取得できないのは大きな問題です。

解決する方法

ホスト側とコンテナのディレクトリを共有させる方法があります。

docker run「-v」オプションが、その設定になります。

コロンの左側(/root/tomcat-container/logs)がホスト側、右側(/share/logs)がコンテナ内のディレクトリを表していて、そのディレクトリが共有されるかたちとなります。

Tomcatログはデフォルトのディレクトリ(/opt/apache-tomcat-9.0.6/logs)に出力されているので、コンテナにログインして、ログ出力先を変えましょう。

[コンテナ内]

# cd /opt/apache-tomcat-9.0.6/
# sed -i -e "s/\${catalina.base}\/logs/\/share\/logs/g" ./conf/logging.properties
# ./bin/shutdown.sh
# ./bin/startup.sh
# ls -la /share/logs/

コンテナ内で/share/logs/配下にログが出力されていることが確認できます。

次にホスト側で確認します。

新しいコンソールを開くか、コンテナからログアウト(exit)し、ホスト側のディレクトリ(/root/tomcat-container/logs/)を確認してみてください。

[ホスト]

# ls -la /root/tomcat-container/logs/

これでコンテナが停止しても、ホスト側でログを確認することができます。

試しにコンテナを停止

[ホスト]

# docker stop tomcat
tomcat

これでコンテナが停止しました。

先ほど確認したホスト側のディレクトリ(/root/tomcat-container/logs/)を確認してみるとログが残っているはずです。

その他、ホスト側とコンテナ内でディレクトリ共有する目的の例としては、以下のようなケースがあります。

・設定ファイルをホスト側においておき、その設定ファイルをコンテナ内で使用する。
・ソースコードがあるディレクトリを共有させて、コンテナ内でビルドやユニットテストを実行できるようにする。

コンテナからDockerイメージを作成し、使用する。

ここで作成したコンテナを削除してしまうと、もう一度同じコンテナを使いたいとなった場合に、再度同じ手順を踏む必要があります。

(また、同じものを増やしたい場合に、再度Tomcatインストールから行うのは手間です)

Dockerイメージにすることで、そのイメージを使ってコンテナ起動すれば、既に各種ミドルウェアなどがインストールされた状態のコンテナを起動することができます。

Dockerイメージを作成する方法は、以下の方法があります。

  1. Dockerfileを使用してDockerイメージを作成する
  2. コンテナからDockerイメージを作成する
Dockerfile

今回実施したインストールコマンドなどを書いたファイル。

手順がそのままコード化された状態となり、再現性も担保できるようになります。

具体的には以下のような内容になります。

FROM centos:7
RUN yum install -y java
CMD ["/bin/bash"]
コンテナからDockerイメージを作成
  • 現状のコンテナ状態をDockerイメージにするコマンドがあります。
    このやり方だと、そのDockerイメージは何の操作をしてできたものなのかもわからない状態になってしまいます。
  • Dockerイメージの作成は、基本的には 1.のDockerfileを使用したやり方がよい。
    (Docker Hubで公開されているDockerイメージも、それぞれDockerfileが公開されていて、それを元に作成されたイメージです。)
    暫定的にDockerイメージにしておきたい場合などに行うくらいが良い。
まず、コンテナを停止

[ホスト]

# docker stop tomcat
コンテナ名を指定し、Dockerイメージを作成

[ホスト]

docker commit <コンテナ名> <作成するDockerイメージ名>

# docker commit tomcat tomcat-image
新しくDockerイメージができたことを確認

[ホスト]

# docker images

作成したDockerイメージを使用してコンテナ起動

[ホスト]

コマンドの一番後ろで、作成したDockerイメージの「tomcat-image」を指定しています。

ホスト側のポートと、ホスト側の共有ディレクトリについては、最初に起動したコンテナとは被らないようにしましょう。

# mkdir -p /root/tomcat-container/logs2
# docker run -it -d -p 18082:8080 -v /root/tomcat-container/logs2:/share/logs --name tomcat2 tomcat-image

コンテナ状態を確認してみます。

[ホスト]

tomcat2という名前のコンテナが起動しています。

また、IMAGEのところを見ると「centos:7」ではなく、「tomcat-image」になっています。

# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED             
72472672e66b   tomcat-image   "/bin/bash"   5 seconds ago       
4ba6f6f55040   centos:7       "/bin/bash"   About an hour ago   

STATUS                            PORTS                     NAMES
Up 2 seconds                      0.0.0.0:18082->8080/tcp   tomcat2
Exited (137) About an hour ago                              tomcat

実際にコンテナの中を確認し、Tomcatも起動してみます。

[ホスト]

# docker exec -it tomcat2 bash

[コンテナ内]

# ls /opt
apache-tomcat-9.0.6 apache-tomcat-9.0.6.tar.gz
(Tomcatディレクトリが確かに存在します)
# cd /opt/apache-tomcat-9.0.6
# ./bin/startup.sh

ブラウザから以下にアクセスするとTomcat画面が見れるはず。

(新しく起動したコンテナのホスト側のポートは18082にした)

http://:18082/

停止したコンテナは、以下のコマンドで再度起動することができます。

18080ポートでアクセスすれば、最初に起動したコンテナ側に接続できます。

[ホスト]

# docker start tomcat
# docker ps -a
CONTAINER ID   IMAGE          COMMAND       CREATED          
72472672e66b   tomcat-image   "/bin/bash"   45 seconds ago   
4ba6f6f55040   centos:7       "/bin/bash"   5 seconds ago    

STATUS          PORTS                     NAMES
Up 45 seconds   0.0.0.0:18082->8080/tcp   tomcat2
Up 5 seconds    0.0.0.0:18080->8080/tcp   tomcat