Phơi bày một cổng trên một container Docker trực tiếp


408

Tôi đang cố gắng tạo một Docker container hoạt động như một máy ảo đầy đủ. Tôi biết rằng tôi có thể sử dụng lệnh EXPOSE bên trong Dockerfile để hiển thị một cổng và tôi có thể sử dụng -pcờ docker runđể gán các cổng, nhưng khi một container thực sự đang chạy, có lệnh nào để mở / ánh xạ các cổng bổ sung trực tiếp không?

Ví dụ: giả sử tôi có một Docker container đang chạy sshd. Một số người khác sử dụng ssh's container và cài đặt httpd. Có cách nào để lộ cổng 80 trên container và ánh xạ nó tới cổng 8080 trên máy chủ để mọi người có thể truy cập máy chủ web đang chạy trong container mà không cần khởi động lại không?


1
Bạn có thể cung cấp cho container một IP có thể định tuyến , do đó không cần ánh xạ cổng.
Matt

Câu trả lời:


331

Bạn không thể thực hiện việc này thông qua Docker, nhưng bạn có thể truy cập cổng không tiếp xúc của bộ chứa từ máy chủ.

nếu bạn có một container chứa thứ gì đó chạy trên cổng 8000, bạn có thể chạy

wget http://container_ip:8000

Để lấy địa chỉ IP của container container, hãy chạy 2 lệnh:

docker ps

docker inspect container_name | grep IPAddress

Trong nội bộ, Docker trình bày để gọi iptables khi bạn chạy một hình ảnh, vì vậy có thể một số biến thể trên này sẽ hoạt động.

để hiển thị cổng 8000 của bộ chứa trên cổng localhosts 8001:

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

Một cách bạn có thể giải quyết vấn đề này là thiết lập một container khác với ánh xạ cổng bạn muốn và so sánh đầu ra của lệnh iptables-save (mặc dù vậy, tôi đã phải loại bỏ một số tùy chọn khác để buộc lưu lượng truy cập đi qua docker Ủy quyền).

LƯU Ý: đây là lật đổ docker, vì vậy nên được thực hiện với nhận thức rằng nó có thể tạo ra khói màu xanh

HOẶC LÀ

Một cách khác, là để xem tùy chọn (mới? Post 0.6.6?) -P - sẽ sử dụng các cổng máy chủ ngẫu nhiên, sau đó nối dây chúng lên.

HOẶC LÀ

với 0.6.5, bạn có thể sử dụng tính năng LINKs để hiển thị một container mới nói chuyện với cái hiện có, với một số chuyển tiếp bổ sung cho các cờ -p container đó không? (Tôi chưa sử dụng LINK)

HOẶC LÀ

với docker 0.11? bạn có thể sử dụng docker run --net host ..để gắn container của mình trực tiếp vào giao diện mạng của máy chủ (nghĩa là mạng không được đặt tên) và do đó, tất cả các cổng bạn mở trong vùng chứa đều bị lộ.


6
Điều này dường như không hoạt động với docker 1.3.0 ít nhất. Quy tắc DOCKER DNAT được tạo khi chạy docker với -p, nhưng việc thêm nó theo cách thủ công dường như không cho phép kết nối. Việc xóa một cách kỳ lạ quy tắc trong khi một container đang chạy dường như không thể ngăn nó hoạt động ...
silasdavis

4
Cảm ơn. Tôi bị ru ngủ trong một cảm giác an toàn rằng các cổng không bị lộ là an toàn.
seanmcl

Tự động hóa mọi thứ và sử dụng jq + sed, nó có thể hữu ích: 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 Thay vì mời jqsedbạn có thể sử dụng -ftùy chọn docker inspect:CONTAINER_IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' container_name)
pwes

đối với những người thích kmkeen.com/jshon jshon -e 0 -e NetworkSettings -e Networks -e bridge -e IPAddress -u
slf

138

Đây là những gì tôi sẽ làm:

  • Cam kết container sống.
  • Chạy lại container với hình ảnh mới, với các cổng được mở (Tôi khuyên bạn nên gắn một âm lượng được chia sẻ và cũng mở cổng 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
Phần quan trọng trong câu hỏi của tôi là điều này cần phải xảy ra mà không cần khởi động lại bộ chứa ... Chuyển sang bộ chứa mới có thể giữ các tệp, nhưng sẽ giết chết mọi quá trình đang chạy một cách hiệu quả và sẽ tương tự như khởi động lại trên máy vật lý. Tôi cần phải làm điều này mà không xảy ra. Cảm ơn bạn mặc dù!
reberhardt

ổn thỏa. Tôi muốn so sánh nó nhiều hơn để bắt đầu một ví dụ song song. vì cả hai hiện đang chạy (cũ và mới), có thể dễ dàng hơn để ủy quyền cho bộ chứa mới, một khi việc di chuyển cần thiết được thực hiện.
bosky101

Có, các trường hợp song song và ủy quyền ngược là một số lý do hàng đầu tôi yêu Docker. Tuy nhiên, trong kịch bản này, tôi cần bảo toàn tất cả các quy trình đang chạy trong vùng chứa có thể đã được bắt đầu thông qua SSH. Mặc dù các tệp thực thi sẽ được giữ nguyên trong việc cam kết hình ảnh và bắt đầu một thể hiện song song, bản thân các tệp thực thi sẽ không được khởi động và mọi thứ trong RAM sẽ bị mất.
reberhardt

6
Tại sao bạn chạy sudo dockervà không chỉ docker?
Thiago Figueiro

Mẹo này đã giúp tôi rất nhiều vì tôi đã cài đặt hàng tấn gói trong một mạng chậm. Bằng cách chạy, docker committôi đã sẵn sàng để kiểm tra lại ứng dụng thay vì mất hàng giờ để cài đặt lại mọi thứ.
gustavohenke

49

Mặc dù bạn không thể hiển thị một cổng mới của một container hiện có, bạn có thể bắt đầu một container mới trong cùng một mạng Docker và khiến nó chuyển tiếp lưu lượng truy cập đến container ban đầu.

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

Ví dụ làm việc

Khởi chạy một dịch vụ web nghe trên cổng 80, nhưng không để lộ cổng nội bộ 80 (rất tiếc!):

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

Tìm IP mạng Docker của nó:

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

Khởi chạy verb/socatvới cổng 8080 được hiển thị và giúp nó chuyển tiếp lưu lượng TCP đến cổng 80 của IP đó:

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

Bây giờ bạn có thể truy cập pastebin trên http: // localhost: 8080 / , và các yêu cầu của bạn sẽ socat:1234được chuyển tới đó pastebin:80và phản hồi sẽ đi theo cùng một đường dẫn ngược lại.


7
Khá thông minh! Tuy nhiên, có lẽ tốt hơn để sử dụng verb/socat:alpine, vì hình ảnh của nó có 5% dấu chân (trừ khi bạn chạy vào libc hoặc không tương thích DNS ).
jpaugh

3
Cũng cóalpine/socat
Jamby

3
Câu trả lời tuyệt vời. Đơn giản, một lớp lót sẽ hoàn thành nó mà không có bất kỳ hack bẩn.
Bruno Brant

2
Điều này làm việc hoàn hảo cho tôi, cảm ơn! Tôi đã phải thêm --net myfoldername_defaultvào verb/socatlệnh khởi chạy của mình kể từ khi tôi bắt đầu vùng chứa không bị lộ trong một thành phần docker tạo ra một mạng.
emazzotta

35

Các bản hack IPtables không hoạt động, ít nhất là trên Docker 1.4.1.

Cách tốt nhất là chạy một container khác với cổng tiếp xúc và chuyển tiếp với socat. Đây là những gì tôi đã thực hiện (tạm thời) kết nối với cơ sở dữ liệu bằng 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
Các hack iptables được cho là chạy từ máy chủ chứ không phải container docker. Về cơ bản, nó chuyển tiếp các yêu cầu tới một số cổng nhất định trên máy chủ vào các cổng chứa docker thích hợp. Nó hoàn toàn không phải là docker, và bạn có thể làm điều đó với các máy chủ hoàn toàn khác nhau. Bạn có thể thêm định dạng mã vào tập tin Docker của bạn? Có vẻ như khá hữu ích.
AusIV ngày

1
Tháng 2 năm 2016: Chạy Docker 1.9.1, đây là giải pháp duy nhất hoạt động thành công với tôi. Không có giải pháp IPTables nào hoạt động. Bạn nên sử dụng FROMhình ảnh cơ sở giống như bộ chứa DB của mình để sử dụng hiệu quả tài nguyên.
Excalibur

4
Bạn có thể muốn thử apline / socat . Nó đi kèm với socat được cài đặt sẵn và chấp nhận các tùy chọn socat dưới dạng lệnh để bạn không cần phải viết Dockerfile.
trkoch

35

Đây là một ý tưởng khác. Sử dụng SSH để thực hiện chuyển tiếp cổng; điều này cũng có lợi khi hoạt động trong OS X (và có thể là Windows) khi máy chủ Docker của bạn là VM.

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

4
trong trường hợp của tôi, hình ảnh docker đang chạy không có ssh nhị phân
Radu Toader

Tôi có cần tạo khóa SSH trên máy của mình và đặt nó vào thùng chứa docker không?
octavian

@octavian ssh-keygen -t rsa bên trong container. thêm khóa pub vào ủy quyền_key trên máy chủ
Luke W

8

Tôi đã phải giải quyết vấn đề tương tự và có thể giải quyết nó mà không cần dừng bất kỳ container nào đang chạy. Đây là một giải pháp cập nhật kể từ tháng 2 năm 2016, sử dụng Docker 1.9.1. Dù sao, câu trả lời này là phiên bản chi tiết của câu trả lời của @ ricardo-branco, nhưng sâu hơn cho người dùng mới.

Trong kịch bản của tôi, tôi muốn tạm thời kết nối với MySQL đang chạy trong một thùng chứa và vì các thùng chứa ứng dụng khác được liên kết với nó, việc dừng, cấu hình lại và chạy lại bộ chứa cơ sở dữ liệu là không khởi động.

Vì tôi muốn truy cập cơ sở dữ liệu MySQL bên ngoài (từ Sequel Pro thông qua đường hầm SSH), tôi sẽ sử dụng cổng 33306trên máy chủ. (Không 3306, chỉ trong trường hợp có một phiên bản MySQL bên ngoài đang chạy.)

Khoảng một giờ điều chỉnh iptables tỏ ra không có kết quả, mặc dù:

Từng bước, đây là những gì tôi đã làm:

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

Chỉnh sửa dockerfile, đặt cái này bên trong:

# 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

Sau đó, xây dựng hình ảnh:

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

Sau đó chạy nó, liên kết với container đang chạy của bạn. (Sử dụng -dthay vì -rmgiữ nó ở chế độ nền cho đến khi dừng và xóa rõ ràng. Tôi chỉ muốn nó chạy tạm thời trong trường hợp này.)

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

Bạn có thể sử dụng hình ảnh động từ / socat hoặc alpine / socat trực tiếp. Xem động từ / socat
pjotr_dolphin

7

Để thêm vào giải pháp trả lời được chấp nhận iptables , tôi đã phải chạy thêm hai lệnh trên máy chủ để mở nó ra thế giới bên ngoài.

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

Lưu ý: Tôi đã mở cổng https (443), IP bên trong docker của tôi là 172.17.0.2

Lưu ý 2: Các quy tắc và tạm thời này và sẽ chỉ tồn tại cho đến khi container được khởi động lại


Điều này làm việc cho tôi ... tuy nhiên đã có một số Cavat sau đó. Theo lưu ý 2, điều này sẽ dừng lại nếu các container được khởi động lại, nó có thể nằm trên một IP khác. Nhưng quan trọng hơn là docker không có ý tưởng nào về các mục có thể xóa được này, vì vậy sẽ không xóa chúng khi bạn khởi động lại hoàn toàn dịch vụ và yêu cầu docker thực hiện đúng cách. Kết quả là nhiều mục nhập có thể hoàn toàn giống nhau, điều này khiến cho nó không thành công mà không có lỗi hoặc chỉ ra nguyên nhân. Một khi các quy tắc bổ sung đã được cai trị, vấn đề biến mất. Nói cách khác, hãy xem qua các bảng IP của bạn RẤT cẩn thận, sau khi có bất kỳ thay đổi nào.
anthony

5

Bạn có thể sử dụng SSH để tạo một đường hầm và để lộ container của bạn trong máy chủ của bạn.

Bạn có thể làm điều đó theo cả hai cách, từ container đến host và từ host đến container. Nhưng bạn cần một công cụ SSH như OpenSSH ở cả hai (máy khách trong một và máy chủ khác).

Ví dụ, trong container, bạn có thể làm

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

Bạn có thể tìm thấy địa chỉ IP của container từ dòng này (trong container):

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

Sau đó, trong máy chủ, bạn chỉ có thể làm:

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

Điều này hiệu quả với tôi, với một hiệu chỉnh nhỏ ... lệnh này đã hoạt động: sudo ssh -NfL 25252: 172.17.0.50: 22 $ USER @ localhost
Alexander Guo

3

Trong trường hợp không có câu trả lời nào hiệu quả với ai đó - hãy kiểm tra xem thùng chứa mục tiêu của bạn đã chạy trong mạng docker chưa:

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

Lưu nó để sau này trong biến $NET_NAME:

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

Nếu có, bạn nên chạy bộ chứa proxy trong cùng một mạng.

Tiếp theo tra cứu bí danh cho container:

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

Lưu nó để sau này trong biến $ALIAS:

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

Bây giờ hãy chạy socattrong một vùng chứa trong mạng $NET_NAMEđể kết nối với cổng $ALIAStiếp xúc (nhưng không được công bố) của ed chứa:

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

2

Bạn có thể sử dụng mạng lớp phủ như Wea Net , này sẽ chỉ định một địa chỉ IP duy nhất cho mỗi vùng chứa và hiển thị tất cả các cổng cho mọi phần của mạng.

Dệt cũng cung cấp tích hợp mạng máy chủ . Nó bị tắt theo mặc định, nhưng, nếu bạn cũng muốn truy cập các địa chỉ IP của container (và tất cả các cổng của nó) từ máy chủ, bạn có thể chạy đơn giản là chạyweave expose .

Tiết lộ đầy đủ: Tôi làm việc tại Weaworks.


2

Có một gói HAProxy tiện dụng.

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

Điều này tạo ra một HAProxy cho thùng chứa mục tiêu. dễ như ăn bánh.



1

Đọc phản hồi của Ricardo trước. Điều này làm việc cho tôi.

Tuy nhiên, tồn tại một kịch bản trong đó điều này sẽ không hoạt động nếu container đang chạy được khởi động bằng cách sử dụng docker-compose. Điều này là do docker-compose (Tôi đang chạy docker 1.17) tạo ra một mạng mới. Cách để giải quyết tình huống này sẽ là

docker network ls

Sau đó nối các mục sau docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name


0

Không thể thực hiện ánh xạ cổng trực tiếp nhưng có nhiều cách bạn có thể cung cấp cho bộ chứa Docker số tiền cho một giao diện thực như máy ảo sẽ có.

Giao diện Macvlan

Docker hiện bao gồm trình điều khiển mạng Macvlan . Điều này gắn mạng Docker vào giao diện "thế giới thực" và cho phép bạn gán địa chỉ mạng đó trực tiếp vào vùng chứa (giống như chế độ cầu nối của máy ảo).

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

pipeworkcũng có thể ánh xạ một giao diện thực vào một thùng chứa hoặc thiết lập giao diện phụ trong các phiên bản cũ hơn của Docker.

Định tuyến IP

Nếu bạn có quyền kiểm soát mạng, bạn có thể định tuyến các mạng bổ sung đến máy chủ Docker của mình để sử dụng trong các thùng chứa.

Sau đó, bạn gán mạng đó cho các container và thiết lập máy chủ Docker của bạn để định tuyến các gói thông qua mạng docker.

Giao diện máy chủ được chia sẻ

Các --net hosttùy chọn cho phép giao diện máy chủ để được chia sẻ vào một container nhưng điều này có lẽ không phải là một thiết lập tốt cho việc chạy nhiều container trên host một do tính chất chia sẻ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.