sagantaf

メモレベルの技術記事を書くブログ。

Docker実践〜dockerコンテナに外部からアクセスするためにポートフォワード設定を追加する

はじめに

コンテナを構築した後、アプリケーションを追加したりして、

「新たなポートを使って外部からコンテナにアクセスしたい!」 「でもコンテナを作り直したくない!」

となった時、コンテナを止めずにポートフォワード設定を追加する方法を書きます。 また、誤って設定してしまった時の設定削除方法も最後の方に記載しています。

Dockerのポートフォワードの設定

Dockerのポートフォワードの設定は、コンテナ起動時などに-pオプションを使って設定できますが、裏ではiptablesで実現されています。

そのため、今回のように稼働しているコンテナのポートを追加する時もiptablesのNATとFilteringの設定を編集します。


1. まずはコンテナのIPアドレスを確認する

設定を追加したいコンテナ名をhogeとすると、

$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' hoge

で確認できます。 普通は、 172.17.0.2 のようなIPアドレスになっています。


2. NAT設定

まずはNAT設定からです。

コンテナが稼働しているホストマシンのポートを--dport で指定し、コンテナのIPアドレスとポートを --to-destination で指定します。この時のIPアドレスは 上記1 で確認したアドレスになります。

下記の例は、ホストマシンの8001番ポートにアクセスした時に、コンテナ 172.17.0.2 の 8888 ポートに接続されるようにしたい時のコマンドです。8888はpython Jupyterのデフォルトポートです。

$ sudo iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 8001 -j DNAT --to-destination 172.17.0.2:8888

イメージ図:

f:id:sagantaf:20181118153602p:plain

図のように、ユーザがブラウザでJupyterに接続しようと「http://ホストマシンIPアドレス:8001」のURLにアクセスすると、リクエストはホストマシンに届いた後に、コンテナhogeの8888ポートにフォワードされ、Jupyterのプロセスへと到達する形になります。


3. Filtering設定

次はFilteringの設定です。

コンテナのIPアドレス-d で指定し、ホストマシンのポートを --dport で指定します。

$ sudo iptables -A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8001 -j ACCEPT

2.のNATの設定だけでなく、Filteringの設定もしないと外部からのアクセスができませんので、忘れずに設定が必要です。


4. 設定できたか確認

最後に想定通りに設定されたかを確認します。

以下のコマンドでiptablesの現在の設定をファイルに保存できます。

$ sudo iptables-save > iptables.txt

保存したら、中身を表示して、想定通りのNATとFirtering設定になっているかどうかを確認してください。

もし想定と異なる設定をしてしまっていた場合は次の5.を確認ください。


5. 誤って設定した時の削除方法

NAT設定の削除方法

まずは間違っている設定の行番号を確認します。

$ sudo iptables -t nat -L --line-numbers

そして確認した行番号を指定して下記の削除コマンドを実行します。(X に行番号を入れます)

$ sudo iptables -t nat -D DOCKER X


Filtering設定の削除方法

こちらも同じく間違っている設定の行番号を確認します。

$ sudo iptables -L --line-numbers

確認した行番号を指定して下記の削除コマンドを実行します。(X に行番号を入れます)

$ sudo iptables -D DOCKER X

これで設定が削除されていますので、$ sudo iptables-save > iptables.txt で設定を確認してみてください。


6. シェルスクリプトにして楽に設定できるようにしました

1〜4の手順をシェル化!
引数で ホストマシンのポート、コンテナ名、コンテナのポート の順で指定するようになっています。

#!/bin/bash

if [ $# -ne 3 ]; then
  echo "引数は右記のように指定してください:host port, container name, container port"
  exit 1
fi

host_port=$1
container=$2
container_port=$3

ip=`sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${container}`

if [ $? -gt 0 ]; then
  echo 'cannot get IP address of ${container}'
  exit 1
fi

echo 'IP Address is '${ip}
echo 'host port = '${host_port}', container port = '${container_port}
read -p 'ok? (yes/no): ' yn
case ${yn} in
  yes) ;;
  no) echo 'abort.' ; exit  ;;
  *) echo 'should yes or no' ; exit  ;;
esac

# NAT
sudo iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport ${host_port} -j DNAT --to-destination ${ip}:${container_port}

if [ $? -gt 0 ]; then
  echo 'NAT設定でエラー発生'
  exit 1
fi

# Filtering
sudo iptables -A DOCKER -d ${ip}/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport ${container_port} -j ACCEPT

if [ $? -gt 0 ]; then
  echo 'Filtering設定でエラー発生'
  exit 1
fi

# save
sudo iptables-save > iptables.txt
echo 'iptables.txtに現在のiptablesの設定を保存'

参考文献

Docker

Docker

Docker実践ガイド 第2版 (impress top gear)

Docker実践ガイド 第2版 (impress top gear)