docker/docker-composeにおけるコンテナ間通信を実装する

docker/docker-composeにおけるコンテナ間通信を実装する

前回作成したangularを表示するdockerコンテナの他にnginxのプロキシとなるdockerコンテナを作成しこのコンテナ間の通信を実装していきます。

【Docker超入門】angularが動くコンテナを作成しよう!

nginxとは

nginxとは、プロキシサーバーといってアプリケーションとクライアントの間に位置しリクエストをいい感じにしてくれるソフトウェアのことです。アプリケーションサーバーより簡単に細かな設定ができたりと実際の現場でも必ずと言っていいいほど使われています。

これだけだとよくわからないと思うので調べてみましょう。

完成イメージ

こんな感じでコンテナ同士で通信して、nginx(localhost:8080)でアクセスしたら、angular(localhost:8000)が表示されるように設定するのがゴールです。

nginxコンテナを触ってみる

公式のnginxのイメージを使ってまずはサクッとnginxを手元で動かしてみましょう。

$ docker pull nginx
$ docker run -it -d --rm -p 8080:80 --name nginx-sample nginx

nginxの公式イメージはrunと同時にnginxをスタートさせてくれるので、8080でアクセスしたときにコンテナ内の80に繋がるようにportを共有しました。localhost:8080にアクセスしてnginxの初期画面が表示されることを確認しましょう。

中に入ってみるのもいいでしょう。
ちなみに設定ファイル達は、/etc/nginx/配下にだいたいあります。そのあたりを探ってみるのもいいでしょう。

コンテナ間通信を実装する

portの共有を設定することでhost OS(手元のPC)とコンテナ間で通信することはできるようになりましたが、このままではコンテナ間での通信はできません。

試しに、nginxコンテナに入ってangularコンテナ(前回作ったやつ)にアクセスしようとしてみてください。hostからだとlocalhostで繋がったところも、コンテナ間では繋がりません。

で、どうするかと言うと、主に二つ方法があって、一つはdockerのnetworkを作成する方法、もう一つはdockerのlinkという機能を使う方法です。

networkを作成する

$ docker network create test

とすることで、testというdocker networkが作成されます。このnetworkを使いますよ〜ってことをdocker runのときに明示することでそのnetworkに属するコンテナは、http://[コンテナ名]:[port番号]で接続できるようになります。

試してみましょう。

$ docker run -it --rm -p 8000:8000 --network test --name angular angular
$ docker run -it --rm -p 8080:80 --network test --name nginx nginx

このようにすると、二つのコンテナ間で通信が可能になります。

$ docker exec -it nginx /bin/bash
[root@...] apt-get update && apt-get install curl # curlが入ってないからinstall
[root@...] curl http://angular:8000
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AngularDocker</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
<script type="text/javascript" src="runtime.js"></script><script type="text/javascript" src="polyfills.js"></script><script type="text/javascript" src="styles.js"></script><script type="text/javascript" src="vendor.js"></script><script type="text/javascript" src="main.js"></script></body>
</html>

linkを使う

コンテナの起動時に、--link [接続したいコンテナ名]:[alias]という風に指定することで、一方向に通信を許可することができます。

# nginx/angularがまだ動いてたらkillする
$ ! docker ps | grep nginx || docker kill nginx
$ ! docker ps | grep angular || docker kill angular

# angularを普通に起動する
$ docker run -d -it --rm -p 8000:8000 --name angular angular

# linkをつけてnginxを起動する
$  docker run -d -it --rm -p 8080:80 --link angular:ng --name original-nginx original-nginx /bin/bash

これで、nginxが起動してlinkが生成されているはずです。

$ docker exec -it nginx /bin/bash
[root@...] apt-get update && apt-get install curl # curlが入ってないからinstall
[root@...] curl http://angular:8000
さっきと一緒

[root@...] curl http://ng:8000
これも一緒

毎回curlを書くのがめんどいと思ったあなた!鋭いです。Dockerfileを書いてそのimageから作成した方がinstallの時間は短縮されます。

nginxのconfigを書く

さて、nginxコンテナやコンテナ間通信は実装できましたが、このままではnginxが何も仕事をしていないので設定ファイルを書いていきましょう。

nginxの設定ファイルは分割可能で、/etc/nginx/conf.d/配下にある*.confを読み込んで設定してくれます。大元は/etc/nginx/nginx.confで、こいつがincludeをしてくれています。(これはコンテナ内のファイルだよ)

# nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf; # ←これ!!
}

では、angularのDockerfileが置いてあるディレクトリにnginx/を作成し、この中にnginx用のDockerfiledefault.confを作成していきましょう。

# default.conf

server {
    location / {
        proxy_pass http://angular:8000/;
    }
}

これは、/にアクセスがあったときに、それをproxy_pathに繋ぎますって意味です。今回はコンテナをまたいで通信しています。

# Dockerfile

FROM nginx:latest

# defaultのファイルを削除してからコンテナ内に設置(curlも一応入れてる)
RUN apt-get update && apt-get install curl -y && rm -f /etc/nginx/conf.d/default.conf
COPY ./default.conf /etc/nginx/conf.d/

で、この設定ファイルをコンテナ内に設置して完了です。

このDockerfileからoriginal-nginxというimageを作成してコンテナを作成します。

$ docker build ./nginx/ -t original-nginx
$  docker run -d -it --rm -p 8080:80 --link angular:ng --name original-nginx original-nginx /bin/bash

これで、どこも間違っていなかったら、

$ curl localhost:8080
angularのindex.htmlが表示

となるはずです。ブラウザでも確認してみましょう。

成功すれば、nginxをプロキシとして接続することができています。

Makefileを書くと楽

build: angular/build nginx/build

run:
    make angular/run
    make nginx/run

kill: angular/kill nginx/kill

angular/build:
    ng build
    docker build ./ -t angular

angular/run:
    ! docker ps | grep angular || make kill
    docker run -d -it --rm -p 8000:8000 --name angular angular

angular/exec:
    ! docker ps | grep angular || docker exec -it angular /bin/bash

angular/kill:
    ! docker ps | grep angular || docker kill angular

nginx/build:
    docker build ./nginx/ -t original-nginx

nginx/run:
    ! docker ps | grep original-nginx || make nginx/kill
    docker run -d -it --rm -p 8080:80 --link angular:ng --name original-nginx original-nginx /bin/bash

nginx/exec:
    ! docker ps | grep original-nginx || docker exec -it original-nginx /bin/bash

nginx/kill:
    ! docker ps | grep original-nginx || docker kill original-nginx

って書いておくと

$ make run

で、全部実行できるから楽。こんな感じのことをdocker標準でサポートしているのがdocker-composeです。

docker-compose

起動時のめんどくさいオプションとか命名とかlinkとかをymlに書いてしまおうってのがdocker-composeです。(乱暴)
dockerだけでできることは全部できるし、逆にできないことはdocker-composeを使ってもできないはず。。

version: '2'
services:
  angular:
    build: .
    ports:
      - "8000:8000"
  nginx:
    build: ./nginx/
    ports:
      - "8080:80"
    links:
      - "angular:ng"

こんな感じで書きます。versionの指定は必須らしい。あとはserviceの中にネストさせてコンテナを定義していきます。optionの指定方法はshellからymlにフォーマットが変わっただけで全く一緒です。

説明するまでもなくわかると思うので省略。。

起動するときは

$ docker-compose up

これだけ。

$ docker-compose down

これで、全部終了。素晴らしい。

あとたしか、

$ docker-compose run [コンテナ名] [実行したいコマンド]

ってやるとコンテナに入らずとも実行できます。

$ docker-compose run angular pwd
/var/www/app

こんな感じ。