Mengekspos port pada wadah Docker langsung

409

Saya mencoba membuat wadah Docker yang bertindak seperti mesin virtual lengkap. Saya tahu saya dapat menggunakan instruksi EXPOSE di dalam Dockerfile untuk mengekspos port, dan saya dapat menggunakan -pflag dengan docker rununtuk menetapkan port, tetapi begitu sebuah kontainer benar-benar berjalan, apakah ada perintah untuk membuka / memetakan port tambahan secara langsung?

Sebagai contoh, katakanlah saya memiliki wadah Docker yang menjalankan sshd. Orang lain menggunakan wadah ssh dan menginstal httpd. Apakah ada cara untuk mengekspos port 80 pada kontainer dan memetakannya ke port 8080 pada host, sehingga orang dapat mengunjungi server web yang berjalan dalam wadah, tanpa memulai ulang?

reberhardt
sumber
1
Anda dapat memberikan kontainer IP yang dapat dialihkan , sehingga tidak diperlukan pemetaan port.
Matt

Jawaban:

331

Anda tidak dapat melakukan ini melalui Docker, tetapi Anda dapat mengakses port yang tidak terpapar kontainer dari mesin host.

jika Anda memiliki wadah yang menjalankan sesuatu di port 8000, Anda dapat menjalankannya

wget http://container_ip:8000

Untuk mendapatkan alamat ip kontainer, jalankan 2 perintah:

docker ps

docker inspect container_name | grep IPAddress

Secara internal, Docker keluar untuk memanggil iptables ketika Anda menjalankan gambar, jadi mungkin beberapa variasi pada ini akan berfungsi.

untuk mengekspos port 8000 kontainer di port host lokal Anda 8001:

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

Salah satu cara Anda dapat mengatasi hal ini, adalah dengan menyiapkan wadah lain dengan pemetaan port yang Anda inginkan, dan membandingkan output dari perintah iptables-save (walaupun, saya harus menghapus beberapa opsi lain yang memaksa lalu lintas untuk pergi melalui buruh pelabuhan proksi).

CATATAN: ini adalah merubuhkan buruh pelabuhan, jadi harus dilakukan dengan kesadaran bahwa itu dapat menciptakan asap biru

ATAU

Alternatif lain, adalah dengan melihat opsi -P (baru? Posting 0.6.6?) - yang akan menggunakan port host acak, dan kemudian menghubungkannya.

ATAU

dengan 0.6.5, Anda dapat menggunakan fitur LINK untuk memunculkan wadah baru yang berbicara dengan yang sudah ada, dengan beberapa tambahan merelay ke bendera -p wadah itu? (Saya belum menggunakan LINK)

ATAU

dengan buruh pelabuhan 0,11? Anda dapat menggunakan docker run --net host ..untuk melampirkan wadah Anda langsung ke antarmuka jaringan host (yaitu, net tidak spasi-nama) dan dengan demikian semua port yang Anda buka di wadah terbuka.

SvenDowideit
sumber
6
Tampaknya ini tidak berfungsi dengan buruh pelabuhan 1.3.0. Aturan DOCKER DNAT dibuat saat menjalankan buruh pelabuhan dengan -p, tetapi menambahkannya secara manual sepertinya tidak mengizinkan koneksi. Anehnya menghapus aturan sementara sebuah wadah sedang berjalan tampaknya tidak menghentikannya untuk berfungsi dengan baik ...
silasdavis
4
Terima kasih. Saya terbuai dalam rasa aman bahwa port yang tidak terpapar aman.
seanmcl
Mengotomatiskan hal-hal dan menggunakan jq + sed, mungkin berguna: 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 Alih-alih invoing jqdan sedAnda dapat menggunakan -fopsi docker inspect:CONTAINER_IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' container_name)
pwes
bagi mereka yang lebih suka kmkeen.com/jshon jshon -e 0 -e NetworkSettings -e Networks -e bridge -e IPAddress -u
slf
138

Inilah yang akan saya lakukan:

  • Komit wadah hidup.
  • Jalankan wadah lagi dengan gambar baru, dengan port terbuka (saya sarankan memasang volume bersama dan membuka port ssh juga)
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
bosky101
sumber
31
Bagian penting dari pertanyaan saya adalah bahwa ini perlu terjadi tanpa memulai ulang wadah ... Menggeser ke wadah baru dapat menyimpan file, tetapi secara efektif akan membunuh proses yang sedang berjalan, dan akan mirip dengan reboot pada mesin fisik. Saya perlu melakukan ini tanpa itu terjadi. Terima kasih!
reberhardt
baik. Saya akan membandingkannya lebih jauh dengan memulai contoh paralel. karena keduanya sekarang berjalan (lama dan baru), mungkin lebih mudah untuk melakukan proxy ke wadah baru, setelah migrasi yang diperlukan dilakukan.
bosky101
Ya, mesin virtual paralel dan reverse proxy adalah beberapa alasan utama saya menyukai Docker. Namun, dalam skenario ini, saya harus melindungi semua proses yang berjalan di wadah yang mungkin sudah dimulai melalui SSH. Sementara file executable akan dipertahankan dalam melakukan image dan memulai instance paralel, file executable itu sendiri tidak akan dimulai dan apa pun dalam RAM akan hilang.
reberhardt
6
Mengapa Anda lari sudo dockerdan tidak adil docker?
Thiago Figueiro
Tip ini banyak membantu saya karena saya menginstal banyak paket dalam jaringan yang lambat. Dengan menjalankan docker commitsaya siap untuk menguji aplikasi lagi daripada menghabiskan berjam-jam untuk menginstal ulang semuanya.
gustavohenke
49

Meskipun Anda tidak dapat mengekspos port baru wadah yang ada , Anda dapat memulai wadah baru di jaringan Docker yang sama dan membuatnya meneruskan lalu lintas ke wadah asli.

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

Contoh yang berhasil

Meluncurkan web-layanan yang mendengarkan pada port 80, tapi jangan tidak mengekspos pelabuhan internal 80 (oops!):

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

Temukan IP jaringan Docker-nya:

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

Luncurkan verb/socatdengan port 8080 terbuka, dan dapatkan untuk meneruskan lalu lintas TCP ke port 80 itu:

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

Anda sekarang dapat mengakses pastebin di http: // localhost: 8080 / , dan permintaan Anda mengarah ke socat:1234mana itu diteruskan pastebin:80, dan responsnya menempuh jalur yang sama secara terbalik.

RobM
sumber
7
Cukup pintar! Namun, mungkin lebih baik untuk digunakan verb/socat:alpine, karena gambarnya memiliki 5% dari jejak (kecuali jika Anda mengalami libc atau DNS tidak kompatibel ).
jpaugh
3
Ada jugaalpine/socat
Jamby
3
Jawaban yang sangat bagus. Sederhana, satu liner yang akan menyelesaikannya tanpa hack kotor.
Bruno Brant
2
Ini bekerja dengan baik untuk saya, terima kasih! Saya harus menambahkan perintah peluncuran --net myfoldername_defaultsaya verb/socatsejak saya memulai wadah yang tidak terpapar dalam komposisi buruh pelabuhan yang menciptakan jaringan.
emazzotta
35

Peretasan IPtables tidak berfungsi, setidaknya di Docker 1.4.1.

Cara terbaik adalah menjalankan wadah lain dengan port terbuka dan relay dengan socat. Inilah yang saya lakukan untuk (sementara) terhubung ke database dengan 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
Ricardo Branco
sumber
2
Retas iptables seharusnya dijalankan dari mesin host, bukan wadah buruh pelabuhan. Ini pada dasarnya meneruskan permintaan ke port tertentu pada host ke port container docker yang sesuai. Sama sekali tidak spesifik untuk buruh pelabuhan, dan Anda dapat melakukannya untuk host yang benar-benar berbeda. Bisakah Anda menambahkan pemformatan kode ke file Docker Anda? Sepertinya cukup berguna.
AusIV
1
Februari 2016: Menjalankan Docker 1.9.1, ini adalah satu-satunya solusi yang berhasil bagi saya. Tidak ada solusi IPTables yang berfungsi. Sebaiknya disarankan untuk menggunakan FROMgambar dasar yang sama dengan wadah DB Anda, untuk penggunaan sumber daya yang efisien.
Excalibur
4
Anda mungkin ingin mencoba apline / socat . Muncul dengan socat yang sudah diinstal dan menerima opsi socat sebagai perintah sehingga Anda tidak perlu menulis Dockerfile sama sekali.
trkoch
35

Ini ide lain. Gunakan SSH untuk melakukan penerusan port; ini bermanfaat juga bekerja di OS X (dan mungkin Windows) ketika host Docker Anda adalah VM.

docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>
Scott Carlson
sumber
4
dalam kasus saya gambar buruh pelabuhan yang sedang berjalan tidak memiliki binary ssh
Radu Toader
Apakah saya perlu membuat kunci SSH pada mesin saya dan memasukkannya ke dalam wadah buruh pelabuhan?
oktavian
@octavian ssh-keygen -t rsa di dalam wadah. tambahkan kunci pub ke berwenang pada host
Luke W
8

Saya harus berurusan dengan masalah yang sama dan dapat menyelesaikannya tanpa menghentikan salah satu kontainer saya yang sedang berjalan. Ini adalah solusi terkini pada Februari 2016, menggunakan Docker 1.9.1. Bagaimanapun, jawaban ini adalah versi rinci dari jawaban @ ricardo-branco, tetapi lebih mendalam untuk pengguna baru.

Dalam skenario saya, saya ingin untuk sementara waktu menyambung ke MySQL yang berjalan dalam sebuah wadah, dan karena wadah aplikasi lain terhubung dengannya, menghentikan, mengkonfigurasi ulang, dan menjalankan kembali wadah basis data adalah non-starter.

Karena saya ingin mengakses database MySQL secara eksternal (dari Sequel Pro melalui tunneling SSH), saya akan menggunakan port 33306pada mesin host. (Tidak 3306, kalau-kalau ada contoh MySQL luar berjalan.)

Sekitar satu jam tweaker iptables terbukti tidak membuahkan hasil, meskipun:

Langkah demi langkah, inilah yang saya lakukan:

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

Edit dockerfile, tempatkan ini di dalam:

# 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

Kemudian buat gambar:

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

Kemudian jalankan, menghubungkan ke wadah Anda yang sedang berjalan. (Gunakan -dalih-alih -rmmenyimpannya di latar belakang sampai berhenti dan dihapus secara eksplisit. Saya hanya ingin itu berjalan sementara dalam kasus ini.)

docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306  your-namespace/db-expose-33306
Excalibur
sumber
Anda dapat menggunakan gambar kata kerja / socat atau alpine / socat secara langsung. Lihat kata kerja / socat
pjotr_dolphin
7

Untuk menambah solusi jawaban yang diterima iptables , saya harus menjalankan dua perintah lagi di host untuk membukanya ke dunia luar.

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

Catatan: Saya membuka port https (443), IP internal buruh pelabuhan saya adalah 172.17.0.2

Catatan 2: Aturan-aturan ini sementara dan hanya akan bertahan sampai wadah dimulai kembali

dstj
sumber
Ini bekerja untuk saya ... namun ada beberapa yang kemudian Cavat. Sesuai catatan 2, ini akan berhenti jika wadah restart, mungkin pada IP yang berbeda kemudian. Tetapi yang lebih penting lagi, buruh pelabuhan tidak tahu tentang entri yang iptable ini, jadi tidak akan menghapusnya ketika Anda nanti me-restart sepenuhnya layanan dan membuat buruh pelabuhan melakukannya dengan benar. Hasilnya adalah beberapa entri iptable yang persis sama, yang menyebabkannya gagal dengan sedikit atau tanpa kesalahan atau indikasi tentang penyebabnya. Setelah aturan tambahan diperintah, masalah menghilang. Dengan kata lain, lihat tabel IP Anda SANGAT hati-hati, setelah ada perubahan.
anthony
5

Anda dapat menggunakan SSH untuk membuat terowongan dan mengekspos wadah Anda di host Anda.

Anda dapat melakukannya dengan dua cara, dari wadah ke host dan dari host ke wadah. Tetapi Anda membutuhkan alat SSH seperti OpenSSH di keduanya (klien dalam satu dan server di yang lain).

Misalnya, dalam wadah, Anda bisa melakukannya

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

Anda dapat menemukan alamat IP kontainer dari baris ini (dalam wadah):

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

Kemudian di host, Anda bisa melakukan:

sudo ssh -NfL 80:0.0.0.0:80 [email protected]
ton
sumber
Ini bekerja untuk saya, dengan satu koreksi kecil ... perintah ini berhasil: sudo ssh -NfL 25252: 172.17.0.50: 22 $ USER @ localhost
Alexander Guo
3

Jika tidak ada jawaban yang berfungsi untuk seseorang - periksa apakah kontainer target Anda sudah berjalan di jaringan buruh pelabuhan:

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

Simpan untuk nanti di variabel $NET_NAME:

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

Jika ya, Anda harus menjalankan wadah proxy di jaringan yang sama.

Selanjutnya cari alias untuk wadah:

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

Simpan untuk nanti di variabel $ALIAS:

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

Sekarang jalankan socatdalam wadah di jaringan $NET_NAMEuntuk menjembatani ke $ALIASport yang terbuka (tetapi tidak dipublikasikan) ed kontainer:

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

Anda dapat menggunakan jaringan overlay seperti Weave Net , yang akan menetapkan alamat IP unik untuk setiap kontainer dan secara implisit memaparkan semua port ke setiap bagian kontainer dari jaringan.

Weave juga menyediakan integrasi jaringan host . Ini dinonaktifkan secara default tetapi, jika Anda juga ingin mengakses alamat IP kontainer (dan semua port-nya) dari host, Anda dapat menjalankan jalankan saja weave expose.

Pengungkapan penuh: Saya bekerja di Weaveworks.

teman
sumber
2

Ada bungkus HAProxy yang berguna.

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

Ini menciptakan HAProxy ke wadah target. Peasy mudah.

kwerle
sumber
1

Baca tanggapan Ricardo terlebih dahulu. Ini berhasil untuk saya.

Namun, ada skenario di mana ini tidak akan berfungsi jika kontainer berjalan ditendang menggunakan menggunakan buruh pelabuhan. Ini karena docker-compose (Saya menjalankan buruh pelabuhan 1,17) membuat jaringan baru. Cara mengatasi skenario ini adalah

docker network ls

Kemudian tambahkan yang berikut ini docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name

ice.nicer
sumber
0

Tidak mungkin untuk melakukan pemetaan port langsung, tetapi ada beberapa cara Anda bisa memberikan wadah Docker berapa jumlah antarmuka nyata seperti mesin virtual.

Antarmuka Macvlan

Docker sekarang termasuk driver jaringan Macvlan . Ini melampirkan jaringan Docker ke antarmuka "dunia nyata" dan memungkinkan Anda untuk menetapkan alamat jaringan itu langsung ke wadah (seperti mode virtual yang dijembatani mesin virtual).

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

pipeworkjuga dapat memetakan antarmuka nyata ke dalam wadah atau mengatur sub antarmuka dalam versi Docker yang lebih lama.

Routing IP

Jika Anda memiliki kendali atas jaringan Anda dapat merutekan jaringan tambahan ke host Docker Anda untuk digunakan dalam wadah.

Kemudian Anda menetapkan jaringan itu ke wadah dan mengatur host Docker Anda untuk merutekan paket melalui jaringan docker.

Antarmuka host bersama

The --net hostpilihan memungkinkan antarmuka host untuk dibagikan ke dalam wadah tapi ini mungkin tidak setup yang baik untuk menjalankan beberapa kontainer pada satu host karena sifat bersama.

Mat
sumber