ライブDockerコンテナーのポートを公開する


408

フルオンの仮想マシンのように機能するDockerコンテナーを作成しようとしています。Dockerfile内でEXPOSE命令を使用してポートを公開できることを知っています。-pフラグを使用してdocker runポートを割り当てることができますが、コンテナーが実際に実行されているときに、追加のポートをライブで開く/マップするコマンドはありますか?

たとえば、sshdを実行しているDockerコンテナがあるとします。他の誰かがsshのコンテナを使用してhttpdをインストールします。コンテナーのポート80を公開し、ホストのポート8080にマップして、コンテナーを再起動せずにコンテナーで実行されているWebサーバーにアクセスできるようにする方法はありますか?


1
あなたはできるコンテナにルーティング可能なIPを与える何のポートマッピングが必要とされないので、。
マット

回答:


331

Docker経由でこれを行うことはできませんが、ホストマシンからコンテナーの非公開ポートにアクセスできます。

ポート8000​​で何かが実行されているコンテナがある場合は、実行できます

wget http://container_ip:8000

コンテナのIPアドレスを取得するには、2つのコマンドを実行します。

docker ps

docker inspect container_name | grep IPAddress

内部的には、イメージを実行するとDockerがシェルでiptablesを呼び出すため、おそらくこれに対するいくつかのバリエーションが機能します。

localhostsポート8001でコンテナーのポート8000​​を公開するには:

 iptables -t nat -A  DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000

これを解決する方法の1つは、必要なポートマッピングを使用して別のコンテナーをセットアップし、iptables-saveコマンドの出力を比較することです(ただし、Dockerを介してトラフィックを強制する他のオプションをいくつか削除する必要がありました)プロキシ)。

注:これは破壊的なドッカーです。そのため、青い煙が発生する可能性があることを意識して実行する必要があります

または

もう1つの方法は、(新規?ポスト0.6.6?)-Pオプションを探して、ランダムなホストポートを使用し、それらを接続することです。

または

0.6.5では、LINK機能を使用して、既存のコンテナーと通信する新しいコンテナーを起動し、そのコンテナーの-pフラグへの追加の中継を行うことができますか?(リンクはまだ使用していません)

または

ドッカー0.11で?を使用docker run --net host ..してコンテナをホストのネットワークインターフェースに直接接続できます(つまり、ネットは名前空間ではありません)。したがって、コンテナで開いているすべてのポートが公開されます。


6
これは、少なくともdocker 1.3.0では機能しないようです。DOCKER DNATルールは-pを指定してdockerを実行すると作成されますが、手動で追加すると接続が許可されないようです。コンテナの実行中にルールを奇妙に削除しても、ルールが機能しなくなることはないようです...
silasdavis '27 / 10/27

4
ありがとう。露出されていないポートは安全だという安心感に私は落ち着きました。
seanmcl 2014年

物事を自動化し、JQ +のsedを使用して、それが役に立つことがあります CONTAINER_IP=$(docker inspect container_name | jq .[0].NetworkSettings.IPAddress | sed -r 's/\"([^\"]+)\"/\1/''])'); iptables -t nat -A DOCKER -p tcp --dport 8001 -j DNAT --to-destination ${CONTAINER_IP}:8000
ericson.cepeda

5
ericson.cepeda @の代わりにinvoing jqsed、あなたは使用することができます-fのオプションdocker inspectCONTAINER_IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' container_name)
pwes

kmkeen.com/jshon を好むユーザー向けjshon -e 0 -e NetworkSettings -e Networks -e bridge -e IPAddress -u
slf

138

これが私がすることです:

  • ライブコンテナをコミットします。
  • ポートを開いた状態で、新しいイメージを使用してコンテナーを再度実行します(共有ボリュームをマウントしてsshポートも開くことをお勧めします)
sudo docker ps 
sudo docker commit <containerid> <foo/live>
sudo docker run -i -p 22 -p 8000:80 -m /data:/data -t <foo/live> /bin/bash

31
私の質問の重要な部分は、これはコンテナーを再起動せずに発生する必要があるということです...新しいコンテナーにシフトするとファイルは保持されますが、実行中のプロセスは事実上強制終了され、物理マシンでの再起動に似ています。私はそれを起こさずにこれを行う必要があります。ありがとう、結構です!
reberhardt 2014

大丈夫。私はそれを並列インスタンスを開始することと比較します。両方が実行されているため(古いものと新しいもの)、必要な移行が完了したら、新しいコンテナーにプロキシする方が簡単な場合があります。
bosky101 2014

はい、並列インスタンスとリバースプロキシは、Dockerが大好きな主な理由の一部です。ただし、このシナリオでは、SSH経由で開始された可能性のあるコンテナー内の実行中のすべてのプロセスを保持する必要があります。実行可能ファイルはイメージのコミットと並列インスタンスの開始時に保持されますが、実行可能ファイル自体は開始されず、RAM内のすべてが失われます。
reberhardt 2014

6
なぜあなたは実行しないsudo dockerだけではなくdocker
チアゴフィゲイロ2016年

遅いネットワークに大量のパッケージをインストールしたので、このヒントは非常に役に立ちました。実行するdocker commitことで、何時間もかけてすべてを再インストールする代わりに、アプリケーションを再度テストする準備ができました。
gustavohenke

49

既存のコンテナの新しいポートを公開することはできませんが、同じDockerネットワークで新しいコンテナを開始して、トラフィックを元のコンテナに転送することができます。

# docker run \
  --rm \
  -p $PORT:1234 \
  verb/socat \
    TCP-LISTEN:1234,fork \
    TCP-CONNECT:$TARGET_CONTAINER_IP:$TARGET_CONTAINER_PORT

実施例

ポート80でリッスンするが、内部ポート80を公開しない Webサービスを起動します(おっと!):

# docker run -ti mkodockx/docker-pastebin   # Forgot to expose PORT 80!

DockerネットワークIPを見つけます。

# docker inspect 63256f72142a | grep IPAddress
                    "IPAddress": "172.17.0.2",

verb/socatポート8080を公開して起動し、TCPトラフィックをそのIPのポート80に転送します。

# docker run --rm -p 8080:1234 verb/socat TCP-LISTEN:1234,fork TCP-CONNECT:172.17.0.2:80

これでhttp:// localhost:8080 /でpastebinにアクセスできるようになり、リクエストはにsocat:1234転送されてに転送されpastebin:80、応答は同じパスを逆に移動します。


7
かなり賢い!ただし、verb/socat:alpineそのイメージにはフットプリントの5%があるため、おそらく使用する方がよい(libcまたはDNSの非互換性遭遇しない限り)。
jpaugh 2017

3
そこにもありますalpine/socat
ジャンビー2017年

3
すばらしい答えです。単純な、1つのライナーで、汚れたハックなしでそれを実行できます。
ブルーノブラント

2
これは私にとって完璧に機能しました、ありがとう!ネットワークを作成するドッカー構成で非公開コンテナーを開始したため--net myfoldername_defaultverb/socat起動コマンドに追加する必要がありました。
emazzotta 2018

35

IPtablesハックは、少なくともDocker 1.4.1では機能しません。

最善の方法は、公開されたポートで別のコンテナを実行し、socatでリレーすることです。これは、SQLPlusを使用してデータベースに(一時的に)接続するために行ったものです。

docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus

Dockerfile:

FROM debian:7

RUN apt-get update && \
    apt-get -y install socat && \
    apt-get clean

USER nobody

CMD socat -dddd TCP-LISTEN:1521,reuseaddr,fork TCP:db:1521

2
iptablesハックは、Dockerコンテナーではなく、ホストマシンから実行されることになっています。基本的には、ホスト上の特定のポートへのリクエストを適切なDockerコンテナーポートに転送します。これはDocker固有のものではなく、完全に異なるホストに対して実行できます。Dockerファイルにコード形式を追加できますか?それはかなり便利なようです。
AusIV 2015

1
2016年2月:Docker 1.9.1を実行して、これが私にとってうまく機能した唯一のソリューションでした。どのIPTablesソリューションも機能しませんでした。FROMリソースを効率的に使用するには、DBコンテナーと同じベースイメージを使用することをお勧めします。
エクスカリバー

4
あなたは与えたいと思うかもしれsocatに関する/ aplineを試してみます。socatがプリインストールされており、コマンドとしてsocatオプションを受け入れるため、Dockerfileを作成する必要はありません。
trkoch

35

ここに別のアイデアがあります。SSHを使用してポート転送を行います。これには、DockerホストがVMである場合、OS X(およびおそらくWindows)でも機能するという利点があります。

docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>

4
私の場合、実行されていた
Docker

私のマシンでSSHキーを作成し、それをDockerコンテナーに配置する必要がありますか?
オクタビアン2017

@octavian ssh-keygen -t rsaコンテナ内。ホストのauthorized_keysにpubキーを追加
Luke W

8

私はこれと同じ問題に対処する必要があり、実行中のコンテナーを停止することなく解決できました。これは、Docker 1.9.1を使用した2016年2月現在の最新のソリューションです。とにかく、この回答は@ ricardo-brancoの回答の詳細なバージョンですが、新しいユーザーにはより詳細です。

私のシナリオでは、コンテナーで実行されているMySQLに一時的に接続したかったのですが、他のアプリケーションコンテナーがそれにリンクされているため、データベースコンテナーの停止、再構成、再実行は簡単ではありませんでした。

MySQLデータベースに外部から(Sequel ProからSSHトンネリング経由で)アクセスしたいので33306、ホストマシンのポートを使用します。(ただし3306、外部のMySQLインスタンスが実行されている場合に備えて。)

約1時間のiptablesの調整は、たとえ次のような場合でも無益であることがわかりました。

一歩一歩、これが私がしたことです:

mkdir db-expose-33306
cd db-expose-33306
vim Dockerfile

dockerfileこれを内部に配置して編集します。

# Exposes port 3306 on linked "db" container, to be accessible at host:33306
FROM ubuntu:latest # (Recommended to use the same base as the DB container)

RUN apt-get update && \
    apt-get -y install socat && \
    apt-get clean

USER nobody
EXPOSE 33306

CMD socat -dddd TCP-LISTEN:33306,reuseaddr,fork TCP:db:3306

次に、イメージをビルドします。

docker build -t your-namespace/db-expose-33306 .

次に、実行中のコンテナにリンクして実行します。(-d代わりに-rmを使用して、明示的に停止して削除するまでバックグラウンドで保持します。この場合は一時的にのみ実行します。)

docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306  your-namespace/db-expose-33306

verb / socatまたはalpine / socatイメージを直接使用できます。動詞/ socatを参照
pjotr_dolphin

7

受け入れられた回答 iptablesソリューションに追加するには、ホストでさらに2つのコマンドを実行して、外部にそれを開く必要がありました。

HOST> iptables -t nat -A DOCKER -p tcp --dport 443 -j DNAT --to-destination 172.17.0.2:443
HOST> iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source 172.17.0.2 --destination 172.17.0.2 --dport https
HOST> iptables -A DOCKER -j ACCEPT -p tcp --destination 172.17.0.2 --dport https

注:私はポートhttps(443)を開いていました、私のドッカーの内部IPは 172.17.0.2

注2:これらのルールと一時的なものであり、コンテナーが再起動されるまでのみ持続します


これは私にとってはうまくいった...しかし、いくつかのキャバットは後であった。注2のとおり、コンテナーが再起動すると停止します。その場合、別のIP上にある可能性があります。しかし、もっと重要なのは、Dockerがこのiptableエントリについて認識していないため、後でサービスを完全に再起動してdockerが適切に実行できるようになっても、それらが削除されないことです。その結果、まったく同じ複数のiptableエントリが発生し、エラーや原因の表示がほとんどまたはまったくない状態で失敗しました。いったん追加のルールが決定されると、問題は消えました。つまり、変更を行った後は、IPテーブルを非常に注意深く確認してください。
アンソニー

5

SSHを使用してトンネルを作成し、ホストでコンテナーを公開できます。

コンテナーからホストへ、およびホストからコンテナーへの両方の方法でそれを行うことができます。ただし、両方にOpenSSHのようなSSHツールが必要です(一方のクライアントともう一方のサーバー)。

たとえば、コンテナでは次のことができます

$ yum install -y openssh openssh-server.x86_64
service sshd restart
Stopping sshd:                                             [FAILED]
Generating SSH2 RSA host key:                              [  OK  ]
Generating SSH1 RSA host key:                              [  OK  ]
Generating SSH2 DSA host key:                              [  OK  ]
Starting sshd:                                             [  OK  ]
$ passwd # You need to set a root password..

コンテナーのIPアドレスは、次の行(コンテナー内)で確認できます。

$ ifconfig eth0 | grep "inet addr" | sed 's/^[^:]*:\([^ ]*\).*/\1/g'
172.17.0.2

その後、ホストで、あなたはただ行うことができます:

sudo ssh -NfL 80:0.0.0.0:80 root@172.17.0.2

これは、1つの小さな修正で機能しました...このコマンドは機能しました:sudo ssh -NfL 25252:172.17.0.50:22 $ USER @ localhost
Alexander Guo

3

回答がうまくいかない場合-ターゲットコンテナがすでにdockerネットワークで実行されているかどうかを確認します。

CONTAINER=my-target-container
docker inspect $CONTAINER | grep NetworkMode
        "NetworkMode": "my-network-name",

後で変数に保存します$NET_NAME

NET_NAME=$(docker inspect --format '{{.HostConfig.NetworkMode}}' $CONTAINER)

はいの場合は、同じネットワークでプロキシコンテナを実行する必要があります。

次に、コンテナのエイリアスを調べます。

docker inspect $CONTAINER | grep -A2 Aliases
                "Aliases": [
                    "my-alias",
                    "23ea4ea42e34a"

後で変数に保存します$ALIAS

ALIAS=$(docker inspect --format '{{index .NetworkSettings.Networks "'$NET_NAME'" "Aliases" 0}}' $CONTAINER)

次にsocat、ネットワーク内のコンテナーで実行し$NET_NAMEて、$ALIASedコンテナーの公開された(ただし公開されていない)ポートにブリッジします。

docker run \
    --detach --name my-new-proxy \
    --net $NET_NAME \
    --publish 8080:1234 \
    alpine/socat TCP-LISTEN:1234,fork TCP-CONNECT:$ALIAS:80

2

Weave Netのようなオーバーレイネットワークを使用できます。これは、各コンテナーに一意のIPアドレスを割り当て、ネットワークのすべてのコンテナー部分にすべてのポートを暗黙的に公開します。

Weaveはホストネットワーク統合も提供します。デフォルトでは無効になっていますが、ホストからコンテナのIPアドレス(およびそのすべてのポート)にもアクセスする場合は、単にrunを実行しweave exposeます。

完全な開示:私はWeaveworksで働いています。


2

便利なHAProxyラッパーがあります。

docker run -it -p LOCALPORT:PROXYPORT --rm --link TARGET_CONTAINER:EZNAME -e "BACKEND_HOST=EZNAME" -e "BACKEND_PORT=PROXYPORT" demandbase/docker-tcp-proxy

これにより、ターゲットコンテナーにHAProxyが作成されます。簡単な豆。



1

最初にRicardoの応答を読んでください。これでうまくいきました。

ただし、実行中のコンテナーがdocker-composeを使用してキックオフされた場合、これが機能しないシナリオがあります。これは、docker-compose(私はdocker 1.17を実行しています)が新しいネットワークを作成するためです。このシナリオに対処する方法は

docker network ls

次に、以下を追加します docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name


0

ライブポートマッピングを行うことはできませんが、Dockerコンテナーに仮想マシンのような実際のインターフェースに相当するものを提供する方法はいくつかあります。

Macvlanインターフェース

DockerにMacvlanネットワークドライバーが含まれるようになりました。これにより、Dockerネットワークが「実際の」インターフェースに接続され、そのネットワークアドレスをコンテナーに直接割り当てることができます(仮想マシンブリッジモードのように)。

docker network create \
    -d macvlan \
    --subnet=172.16.86.0/24 \
    --gateway=172.16.86.1  \
    -o parent=eth0 pub_net

pipework、実際のインターフェースをコンテナーにマップしたり、古いバージョンのDockerでサブインターフェースセットアップしたりすることもできます

ルーティングIP

ネットワークを制御できる場合は、追加のネットワークをDockerホストにルーティングして、コンテナーで使用できます。

次に、そのネットワークをコンテナーに割り当て、Dockerホストをセットアップして、Dockerネットワーク経由でパケットをルーティングします。

共有ホストインターフェイス

この--net hostオプションを使用すると、ホストインターフェイスをコンテナーに共有できますが、共有の性質上、これは1つのホストで複数のコンテナーを実行するための適切なセットアップではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.