実行済み(docker runした後)のDockerコンテナのポートを直ぐに公開したくなることがたまにある。
Dockerfileを作成するために、ベース(FROMで指定するDockerイメージ)のコンテナをdocker runで起動し、実際にパッケージのインストールなどを行いながら、Dockerfileにコマンド行を追加して行く作業は普通に行われることだと思う。
その途中にコンテナのポートを公開したくなったら、通常は以下の手順を経ることになる。
- 一旦docker commitでDockerイメージとして保存
- 現在のコンテナを停止&削除
- 保存したDockerイメージをdocker runで起動する際に-pオプションでポート公開を設定
上記手順は1回だったら我慢できるが、それ以上は無駄で時間が掛かる作業が我慢できなくなる。
そこで市井の情報を漁ったところ、「iptablesでfirewall設定~Dockerのポートフォワードを後から変える方法~」が見つかった。
上記サイトを参考として、実行済みのDockerコンテナのポート公開設定を行うスクリプトを作成したので共有する。
※使い方はスクリプト内の usage: ~ __USAGE__ の間のメッセージを参照
#!/bin/bash
# ref: https://momozo.tech/2020/05/16/iptables%E3%81%A7firewall%E8%A8%AD%E5%AE%9Adocker%E3%81%AE%E3%83%9D%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%AF%E3%83%BC%E3%83%89%E3%82%92%E5%BE%8C%E3%81%8B%E3%82%89%E5%A4%89%E3%81%88%E3%82%8B/
if [ "$1" = "-s" ]; then
echo "*** nat table ***"
sudo iptables -t nat -S DOCKER | sed "/^-N/d" | sed "/RETURN$/d"
echo "*** filter table ***"
sudo iptables -t filter -S DOCKER | sed "/^-N/d"
exit 0
fi
if [ "$3" = "" ]; then
cat <<__USAGE__
usage:
- show tables
$0 -s
- expose port
$0 <running container> <source host port> <destination container port>
- delete setting
$0 <running container> <source host port> <destination container port> -d
__USAGE__
exit 1
fi
option="-A"
optname="expose"
if [ "$4" = "-d" ]; then
option="-D"
optname="delete"
fi
set -euo pipefail
container=$1
srcport=$2
destport=$3
ipaddr=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' $container`
sudo iptables -t nat $option DOCKER ! -i docker0 -p tcp -m tcp \
--dport $srcport -j DNAT --to-destination $ipaddr:$destport
sudo iptables -t filter $option DOCKER -d $ipaddr/32 ! -i docker0 -o docker0 \
-p tcp -m tcp --dport $srcport -j ACCEPT
echo "$optname: host $srcport -> $container $ipaddr:$destport"
exit 0
なお、docker container portでコンテナの公開ポートを表示することはできない。
また、コンテナを停止しても、dkport.shで追加したiptablesの設定は削除されない。
Docker Desktop for Windowsの話
Windowsの場合、netshコマンドで同様のことができるだろうと思い調べたところ、以下のドキュメントに記載の通り、ホストからアクセス可能なIPアドレスがLinuxコンテナに割り当てられないことが分かった。
Windowsコンテナには割り当てられるようだが、Windowsコンテナは個人的に使い道がない。
docker inspectでLinuxコンテナの情報を見ると、確かにNetworkSettings.IPAddressが空になっていることが分かる。