Canlı bir Docker kapsayıcısında bir bağlantı noktasını açığa çıkarma


408

Tam bir sanal makine gibi davranan bir Docker kapsayıcısı oluşturmaya çalışıyorum. Bir bağlantı noktasını açığa çıkarmak için Dockerfile içindeki EXPOSE komutunu kullanabileceğimi biliyorum ve bağlantı noktalarını atamak için -pbayrağı kullanabilirim docker run, ancak bir kap gerçekten çalıştığında, ek bağlantı noktalarını canlı olarak açmak / eşleştirmek için bir komut var mı?

Örneğin, sshd çalıştıran bir Docker kabım olduğunu varsayalım. Ssh kapsayıcısını kullanan başka biri içeriyor ve httpd'yi yüklüyor. Kapsayıcıdaki 80 numaralı bağlantı noktasını açığa çıkarmanın ve ana bilgisayardaki 8080 numaralı bağlantı noktasıyla eşleştirmenin bir yolu var mı, böylece insanlar kapsayıcıda çalışan web sunucusunu yeniden başlatmadan ziyaret edebilirler mi?


1
Sen edebilirsiniz haznesini yönlendirilebilir IP vermek hiçbir port haritalama gereklidir, böylece.
Matt

Yanıtlar:


331

Bunu Docker ile yapamazsınız, ancak kapsayıcının açıkta kalmayan bağlantı noktasına ana makineden erişebilirsiniz.

8000 numaralı bağlantı noktasında çalışan bir kabınız varsa,

wget http://container_ip:8000

Konteynerin ip adresini almak için 2 komutu çalıştırın:

docker ps

docker inspect container_name | grep IPAddress

Dahili olarak, Docker bir görüntüyü çalıştırdığınızda iptables çağırmak için mermi yapar, bu yüzden belki de bu konuda bazı değişiklikler olabilir.

localhosts bağlantı noktası 8001'de kabın 8000 bağlantı noktasını göstermek için:

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

Bunu çözmenin bir yolu, istediğiniz bağlantı noktası eşleme ile başka bir kapsayıcı kurmak ve iptables-save komutunun çıktısını karşılaştırmaktır (ancak, trafiği docker üzerinden gitmeye zorlayan diğer seçeneklerden bazılarını kaldırmak zorunda kaldım vekil).

NOT: bu liman işçisi yıkılıyor, bu yüzden iyi mavi duman oluşturabileceği bilinciyle yapılmalıdır

VEYA

Başka bir alternatif, rasgele ana bilgisayar bağlantı noktalarını kullanacak ve daha sonra bunları bağlayacak (yeni? Post 0.6.6?) -P seçeneğine bakmaktır.

VEYA

0.6.5 ile, mevcut kapsayıcıyla konuşan yeni bir kapsayıcı getirmek ve bu kapsayıcı -p bayraklarına ek bir geçiş yapmak için LINK'ler özelliğini kullanabilirsiniz? (Henüz LINK kullanmadım)

VEYA

docker 0.11 ile mi? docker run --net host ..kapsayıcınızı doğrudan ana bilgisayarın ağ arabirimlerine bağlamak için kullanabilirsiniz (ör. net, ad aralıklı değildir) ve böylece kapta açtığınız tüm bağlantı noktaları açığa çıkar.


6
Bu en azından docker 1.3.0 ile çalışmıyor gibi görünüyor. DOCKER DNAT kuralı, -p ile docker çalıştırılırken oluşturulur, ancak manuel olarak eklenmesi bağlantılara izin vermiyor gibi görünür. Bir konteyner çalışırken kuralı tuhaf bir şekilde silmek, onun da çalışmasını
durdurmuyor

4
Teşekkürler. Maruz kalmayan limanların güvenli olduğu konusunda bir güven duygusuna kapıldım.
seanmcl

Şeyler otomatikleştirmek ve jq + sed kullanarak, yararlı olabilir: 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 Faturalama yerine jqve sedşu -fseçeneği kullanabilirsiniz docker inspect:CONTAINER_IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' container_name)
pwes

tercih edenler için kmkeen.com/jshon jshon -e 0 -e NetworkSettings -e Networks -e bridge -e IPAddress -u
slf

138

İşte yapacağım şey:

  • Canlı konteyneri uygulayın.
  • Kapları yeni görüntü ile bağlantı noktaları açıkken tekrar çalıştırın (paylaşılan bir birimi takmanızı ve ssh bağlantı noktasını açmanızı öneririm)
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
Sorumun temel kısmı, bunun kapsayıcı yeniden başlatılmadan gerçekleşmesi gerektiğidir ... Yeni kapsayıcıya geçmek dosyaları tutabilir, ancak çalışan tüm işlemleri etkili bir şekilde öldürür ve fiziksel bir makinede yeniden başlatmaya benzer. Bunu o olmadan yapmalıyım. Yine de teşekkürler!
reberhardt

Peki. ben daha paralel bir örnek başlatmak için olsa karşılaştırmak istiyorsunuz. her ikisi de şimdi çalıştığından (eski ve yeni), gerekli geçişler yapıldıktan sonra yeni kapsayıcıya proxy uygulamak daha kolay olabilir.
bosky101

Evet, paralel örnekler ve ters proxy, Docker'ı sevmemin en önemli nedenlerinden bazıları. Ancak, bu senaryoda, kapsayıcıda SSH ile başlatılmış olabilecek tüm çalışan işlemleri korumam gerekiyor. Yürütülebilir dosyalar görüntüyü işleme ve paralel bir örnek başlatma sırasında korunacak olsa da, yürütülebilir dosyalar kendileri başlatılmayacak ve RAM'daki herhangi bir şey kaybolacaktır.
reberhardt

6
Neden sudo dockersadece koşmuyorsun docker?
Thiago Figueiro

Bu ipucu bana çok yardımcı oldu çünkü yavaş bir ağa tonlarca paket yükledim. Çalıştırarak docker commityerine her şeyi yeniden saatler harcamak tekrar uygulamayı test etmeye hazırdı.
gustavohenke

49

Mevcut bir kapsayıcının yeni bir bağlantı noktasını açığa çıkaramasanız da, aynı Docker ağında yeni bir kapsayıcı başlatabilir ve trafiği orijinal kapsayıcıya iletmesini sağlayabilirsiniz.

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

Çalışılan Örnek

80 numaralı bağlantı noktasında dinler bir web servisini başlatın, ancak do not (! Hop) iç bağlantı noktasını 80 maruz:

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

Docker ağ IP'sini bulun:

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

verb/socat8080 numaralı bağlantı noktası açık durumdayken başlatın ve TCP trafiğini bu IP'nin 80 numaralı bağlantı noktasına iletmesini sağlayın:

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

Artık pastebin'e http: // localhost: 8080 / adresinden erişebilirsiniz ve istekleriniz socat:1234bu adrese yönlendirilir pastebin:80ve yanıt aynı yolu tersine gider.


7
Oldukça akıllı! Yine de, muhtemelen kullanımı daha iyidir verb/socat:alpine, çünkü görüntüsünün kapladığı alanın% 5'i vardır ( libc veya DNS uyumsuzluklarıyla karşılaşmadıkça ).
jpaugh

3
Ayrıca varalpine/socat
Jamby

3
Mükemmel cevap. Basit, kirli bir hack olmadan bunu yapacak bir astar.
Bruno Brant

2
Bu benim için mükemmel çalıştı, teşekkürler! Açılmamış kapsayıcıyı bir ağ oluşturan bir docker kompozisyonunda başlattığımdan beri başlatma komutuma eklemem --net myfoldername_defaultgerekiyordu verb/socat.
emazzotta

35

IPtables saldırıları en azından Docker 1.4.1'de çalışmaz.

En iyi yol, açıkta kalan limanla başka bir konteyner çalıştırmak ve socat ile röle yapmak olacaktır. SQLPlus ile veritabanına (geçici olarak) bağlanmak için yaptığım şey:

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
İptables korsanlarının docker konteynerinden değil, ana makineden çalıştırılması gerekiyor. Esasen, istekleri ana bilgisayardaki belirli bağlantı noktalarına uygun docker konteyner bağlantı noktalarına yönlendirir. Bu docker'a özel değil ve tamamen farklı ana bilgisayarlara yapabilirsiniz. Docker dosyanıza kod biçimlendirmesi ekleyebilir misiniz? Oldukça kullanışlı görünüyor.
AusIV

1
Şubat 2016: Docker 1.9.1'i çalıştırmak, bu benim için başarılı bir şekilde çalışan tek çözümdü. IPTables çözümlerinin hiçbiri işe yaramadı. FROMKaynakların verimli kullanımı için DB konteynerinizle aynı temel görüntüyü kullanmanızı tavsiye ederiz.
Excalibur

4
Apline / socat'ı denemek isteyebilirsiniz . Önceden yüklenmiş socat ile birlikte gelir ve socat seçeneklerini komut olarak kabul eder, böylece bir Dockerfile yazmanıza gerek kalmaz.
trkoch

35

İşte başka bir fikir. Port yönlendirme yapmak için SSH kullanın; bunun, Docker ana makineniz bir VM olduğu zaman OS X'te (ve muhtemelen Windows'da) çalışma avantajı da vardır.

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

4
benim durumumda çalışmakta olan liman işçisi görüntü ikili ssh yok
Radu Toader

Makinemde bir SSH anahtarı oluşturmam ve bunun için docker konteynerine koymam gerekir mi?
octavian

@octavian ssh-keygen -t rsa konteyner içinde.
Luke W

8

Aynı sorunla uğraşmak zorunda kaldım ve çalışan konteynerlerimi durdurmadan çözebildim. Bu, Docker 1.9.1 kullanan Şubat 2016 itibariyle güncel bir çözümdür. Her neyse, bu cevap @ ricardo-branco'nun cevabının ayrıntılı bir sürümüdür, ancak yeni kullanıcılar için daha derinlemesine.

Senaryomda, geçici olarak bir kapta çalışan MySQL'e bağlanmak istedim ve diğer uygulama kapları ona bağlı olduğundan, veritabanı kapsayıcısını durdurma, yeniden yapılandırma ve yeniden çalıştırma bir başlangıç ​​değildi.

MySQL veritabanına harici olarak (SSH tünelleme yoluyla Sequel Pro'dan) erişmek istediğim 33306için, ana makinedeki bağlantı noktasını kullanacağım . (Sadece 3306, harici bir MySQL örneği çalışıyorsa değil.)

Yaklaşık bir saatlik tweaking iptables , yine de sonuç vermedi:

Adım adım, işte yaptım:

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

Düzenleyin dockerfile, içine yerleştirin:

# 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

Ardından görüntüyü oluşturun:

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

Ardından, çalışan kabınıza bağlayarak çalıştırın. ( Açıkça durdurulana ve kaldırılana kadar arka planda tutmak -dyerine kullanın -rm. Bu durumda yalnızca geçici olarak çalışmasını istiyorum.)

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

Fiil / socat veya alp / socat imajını doğrudan kullanabilirsiniz. Fiil /
socat'a

7

Kabul edilen cevap iptables çözümüne eklemek için, sunucuya dış dünyaya açmak için iki komut daha çalıştırmak zorunda kaldım.

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

Not: https (443) bağlantı noktasını açıyordum, docker'ın dahili IP adresi 172.17.0.2

Not 2: Bu kurallar ve geçici ve yalnızca kapsayıcı yeniden başlatılıncaya kadar sürecek


Bu benim için çalıştı ... ancak daha sonra bazı Cavat'lar vardı. Not 2'ye göre, kaplar yeniden başlatılırsa bu durur, farklı bir IP'de olabilir. Ancak daha önemli olarak docker'ın bu iptable girişleri hakkında hiçbir fikri yoktur, bu nedenle hizmeti daha sonra tamamen yeniden başlattığınızda ve docker'ın düzgün bir şekilde yapmasını sağladığınızda bunları kaldırmaz. Sonuç, tamamen aynı olan birden fazla iptable girişi oldu; Fazladan kurallar yönetildiğinde sorun ortadan kayboldu. Diğer bir deyişle, herhangi bir değişiklikten sonra IP tablolarınıza ÇOK dikkatlice bakın.
Anthony

5

Bir tünel oluşturmak ve kapsayıcıyı ana makinenizde göstermek için SSH'yi kullanabilirsiniz.

Kapsayıcıdan ana bilgisayara ve ana bilgisayardan konteynere her iki şekilde de yapabilirsiniz. Ancak her ikisinde de OpenSSH gibi bir SSH aracına ihtiyacınız var (birindeki istemci ve diğerindeki sunucu).

Örneğin, kapta, şunları yapabilirsiniz

$ 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..

Kapsayıcı IP adresini bu satırdan (kapsayıcıda) bulabilirsiniz:

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

Daha sonra ana bilgisayarda şunları yapabilirsiniz:

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

Bu benim için çalıştı, küçük bir düzeltme ile ... bu komut çalıştı: sudo ssh -NfL 25252: 172.17.0.50: 22 $ USER @ localhost
Alexander Guo

3

Birisi için herhangi bir yanıtın çalışmaması durumunda - hedef kapsayıcınızın docker ağında zaten çalışıp çalışmadığını kontrol edin:

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

Değişkende daha sonra kullanmak üzere kaydedin $NET_NAME:

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

Evetse, proxy kapsayıcısını aynı ağda çalıştırmalısınız.

Ardından kapsayıcı için diğer adı arayın:

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

Değişkende daha sonra kullanmak üzere kaydedin $ALIAS:

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

Şimdi ed kapsayıcının açık (ancak yayımlanmayan) bağlantı noktasına köprü oluşturmak socatiçin ağdaki bir kapta çalıştırın :$NET_NAME$ALIAS

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

2

Her kapsayıcıya benzersiz bir IP adresi atayan ve tüm bağlantı noktalarını ağın her kapsayıcısına dolaylı olarak açığa çıkaran Weave Net gibi bir kaplama ağı kullanabilirsiniz .

Weave ayrıca ana bilgisayar ağı entegrasyonu da sağlar . Varsayılan olarak devre dışıdır, ancak kapsayıcı IP adreslerine (ve tüm bağlantı noktalarına) ana bilgisayardan da erişmek istiyorsanız, yalnızca çalıştırabilirsiniz weave expose.

Tam açıklama: Weaveworks'de çalışıyorum.


2

Kullanışlı bir HAProxy ambalajı vardır.

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

Bu, hedef kaba bir HAProxy oluşturur. çantada keklik.



1

Önce Ricardo'nun yanıtını okuyun . Bu benim için çalıştı.

Ancak, çalışan konteyner docker-compose kullanılarak başlatılırsa, bunun işe yaramayacağı bir senaryo vardır. Bunun nedeni, docker-compose (docker 1.17'yi çalıştırıyorum) yeni bir ağ oluşturmasıdır. Bu senaryoyu çözmenin yolu

docker network ls

Ardından aşağıdakileri ekleyin docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name


0

Canlı bağlantı noktası eşlemesi yapmak mümkün değildir, ancak bir Docker kapsayıcısına sanal bir makine gibi gerçek bir arayüze ne kadar para verebileceğinizin birçok yolu vardır.

Macvlan Arayüzleri

Docker artık bir Macvlan ağ sürücüsü içeriyor . Bu, bir Docker ağını "gerçek dünya" arayüzüne bağlar ve bu ağların adreslerini doğrudan kaba (atamak için sanal makineler köprülü mod gibi) atamanıza izin verir.

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

pipeworkayrıca gerçek bir arabirimi bir kapsayıcıya eşleyebilir veya Docker'ın eski sürümlerinde bir alt arabirim oluşturabilir.

Yönlendirme IP'leri

Ağ üzerinde kontrolünüz varsa , kaplarda kullanmak üzere ek ağları Docker ana makinenize yönlendirebilirsiniz.

Sonra bu ağı kaplara atar ve Docker ana makinenizi paketleri docker ağı üzerinden yönlendirecek şekilde ayarlarsınız.

Paylaşılan ana bilgisayar arayüzü

--net hostOpsiyon konak arayüzü bir kap içine paylaşılmasına imkan tanıyan fakat bu muhtemelen paylaşılan doğaya bir ana bilgisayarda birden fazla kapsayıcı işleterek iyi bir ayar değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.