kakts-log

programming について調べたことを整理していきます

docker run時にホスト上のランダムなポートにコンテナを割り当てる

docker run時にコンテナ内の特定のポートに、host上のランダムなポートをbindさせる方法をまとめます。

簡単なwebサーバの実装とDockerコンテナ用のDockerfileの記述

今回、下記のような構成で簡単なwebサーバを作るとする。

$ tree
.
├── Dockerfile            # webサーバ用コンテナのDockerfile
└── app
    └── identidock.py  # Flask webサーバの実装

今回は本筋でないので説明しませんが、pythonのwebフレームワークであるFlaskを使って、localhost の/に対してhttpリクエストしたときに Hello Worldを返す簡単なサーバを作ります。
./app/identidock.py に下記のように実装します。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World\n'

if __name__ == '__main__':
    app.run(debug = True, host = '0.0.0.0')

続いて肝心なDockerfileの説明をします。
uwsgiというユーザ・グループを作成した上で、 コンテナ内の9090番ポートにwebサーバ、9191番にサーバパフォーマンス統計用のサーバを立ち上げる設定を記述します

FROM python:3.4


# uswgiというグループ・ユーザを作成
RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi

# Flask uWSGI pip install
RUN pip install Flask==0.10.1 uWSGI==2.0.8

# コマンド実行時の作業ディレクトリ指定
WORKDIR /app

# ./appをコンテナ内にあるファイルシステムの/appにコピーする
COPY app /app

# 解放するポートを明示的に指定
EXPOSE 9090 9191
# これ以降のすべての行の実行ユーザをuwsgiに設定する
USER uwsgi

# uWSGIを実行する新たなコマンドを生成する
# uWSGIに対して、9090ポートで待ち受けるhttpサーバを起動させる。 /app/identidock.pyからappというアプリを実行させる
# 9191でstatsサーバも起動する
CMD ["uwsgi", "--http", "0.0.0.0:9090", "--wsgi-file", "/app/identidock.py", "--callable", "app", "--stats", "0.0.0.0:9191"]

これで、ひとまずwebサーバ用のDockerコンテナの準備ができたので、buildしていきます。

webappという名前でDockerコンテナのbuildを行います。

$ docker build -t webapp .

Dockerコンテナを指定したホスト上のポートにbindさせる

まずは、Dockerコンテナ内の 9090, 9191番ポートをホスト上の指定したポートにbindさせます。
方法としては、docker runコマンド実行時に、オプションで設定させるだけです。

コンテナ内の9090,9191を下記のポートにbindさせることとします。

  • 9090番ポート → host上の9090番ポート 9090:9090
  • 9191番ポート → host上の9191番ポート 9191:9191
    上記のように、 ${host上のポート}:${コンテナ内のポート}という形式で、docker run時に -pオプションで追加してあげればbindすることができます。
    同時に、コンテナ名を webapp-testとします
$ docker run -d -p 9090:9090 -p 9191:9191 --name webapp-test

docker runが完了したら、実際にポートがbind されているか、 docker portで確認します。

# docker port確認
$ docker port webapp-test
9191/tcp -> 0.0.0.0:9191
9090/tcp -> 0.0.0.0:9090

#  疎通確認
$ curl localhost:9090 
Hello World

これで、指定したホスト上のポートにDockerコンテナをbindさせることができました。

Dockerコンテナにホスト上のポートをランダムにbindさせる。

今度は、Dockerコンテナの9090, 9191に対して、ホスト上の大きな番号のポートを自動的に割り当てます。
この方法は非常に簡単で、 docker run 時に -P を引数として使うことで、実現できます。

docker run –helpでは、-Pオプションは以下のように説明されています。

-P, --publish-all    Publish all exposed ports to random ports

やることはこれだけです。

# ホスト上のランダムなポートにコンテナを割り当てる
$ docker run -d -P --name webapp-test webapp

# ポート確認
$ docker port webapp-test
9090/tcp -> 0.0.0.0:32769
9191/tcp -> 0.0.0.0:32768

Dockerコンテナへのランダムなポートbindの利点

このランダムなポートbindの利点としては、ある1台のホスト上に複数のコンテナを実行する際に、使われていないポートを自分で管理して明示的にしていするよりも、ポートのbindを全部Docker側に任せる事ができるので、管理が楽になります。