라이브 Docker 컨테이너에 포트 노출


408

완전한 가상 머신처럼 작동하는 Docker 컨테이너를 만들려고합니다. Dockerfile 내에서 EXPOSE 명령을 사용하여 포트를 노출 할 수 있으며 -pwith 플래그를 사용하여 docker run포트를 할당 할 수 있지만 컨테이너가 실제로 실행되면 추가 포트를 열거 나 매핑하는 명령이 있습니까?

예를 들어 sshd를 실행하는 Docker 컨테이너가 있다고 가정 해 봅시다. 컨테이너 ssh를 사용하는 다른 사람이 httpd를 설치하고 설치합니다. 컨테이너에서 포트 80을 노출하고 호스트에서 포트 8080으로 매핑하여 컨테이너에서 다시 시작하지 않고도 컨테이너에서 실행되는 웹 서버를 방문 할 수있는 방법이 있습니까?


1
컨테이너에 라우팅 가능한 IP를 제공 할 수 있으므로 포트 매핑이 필요하지 않습니다.
Matt

답변:


331

Docker를 통해이 작업을 수행 할 수는 없지만 호스트 시스템에서 컨테이너의 노출되지 않은 포트에 액세스 할 수 있습니다.

포트 8000에서 실행중인 컨테이너가있는 경우 다음을 실행할 수 있습니다.

wget http://container_ip:8000

컨테이너의 IP 주소를 얻으려면 다음 두 명령을 실행하십시오.

docker ps

docker inspect container_name | grep IPAddress

내부적으로 Docker는 이미지를 실행할 때 iptables를 호출하기 위해 껍질을 벗기므로 약간의 변형이 가능합니다.

로컬 호스트 포트 8001에서 컨테이너의 포트 8000을 노출하려면 :

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

이것을 해결할 수있는 한 가지 방법은 원하는 포트 매핑으로 다른 컨테이너를 설정하고 iptables-save 명령 의 출력을 비교하는 것입니다 (트래커가 트래픽을 도커를 통과하도록 강제하는 다른 옵션 중 일부를 제거해야했습니다) 대리).

참고 : 이것은 도커를 파괴하는 것이므로 푸른 연기를 생성 할 수 있다는 인식으로 수행해야합니다

또는

또 다른 대안은 무작위 호스트 포트를 사용하는 (새로운? post 0.6.6?) -P 옵션을 찾아서 연결하는 것입니다.

또는

0.6.5에서는 LINKs 기능을 사용하여 기존 컨테이너와 대화하는 새 컨테이너를 가져올 수 있으며 컨테이너의 -p 플래그에 대한 추가 릴레이가 있습니까? (아직 LINK를 사용하지 않았습니다)

또는

도커 0.11? docker run --net host ..컨테이너를 호스트의 네트워크 인터페이스에 직접 연결 하는 데 사용할 수 있습니다 (즉, net은 이름 간격이 아님). 따라서 컨테이너에서 연 모든 포트가 노출됩니다.


6
적어도 docker 1.3.0에서는 작동하지 않는 것 같습니다. DOCKER DNAT 규칙은 -p로 docker를 실행할 때 만들어 지지만 수동으로 추가하면 연결이 허용되지 않는 것 같습니다. 컨테이너 중 하나를 작업을 중지하지 않는 것 실행되는 동안 이상하게도 ... 규칙을 삭제하면
silasdavis

4
감사. 나는 노출되지 않은 포트가 안전하다는 보안 감각에 빠져 들었다.
seanmcl

사물을 자동화하고 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
대신 invoing의 @는 ericson.cepeda jqsed당신이 사용할 수 -f의 옵션 docker inspect:CONTAINER_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

다 좋아. 병렬 인스턴스를 시작하는 것보다 더 많이 비교하고 싶습니다. 둘 다 현재 (구식과 새로) 실행 중이므로 필요한 마이그레이션이 완료되면 새 컨테이너로 프록시하는 것이 더 쉬울 수 있습니다.
bosky101

예, 병렬 인스턴스와 리버스 프록시는 Docker를 좋아하는 가장 큰 이유 중 하나입니다. 그러나이 시나리오에서는 SSH를 통해 시작된 컨테이너에서 실행중인 모든 프로세스를 보존해야합니다. 이미지를 커밋하고 병렬 인스턴스를 시작하면 실행 파일이 보존되지만 실행 파일 자체는 시작되지 않으며 RAM의 모든 내용이 손실됩니다.
reberhardt

6
sudo docker뿐만 아니라 달리는가 docker?
Thiago Figueiro

이 팁은 느린 네트워크에 수많은 패키지를 설치했기 때문에 많은 도움이되었습니다. 실행 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을 노출시키지 않는 웹 서비스를 시작하십시오 (oops!) :

# 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
꽤 영리합니다! 그러나 libc 또는 DNS 비 호환성 문제가 발생verb/socat:alpine 하지 않는 한 이미지의 풋 프린트가 5 % 이므로을 사용하는 것이 좋습니다 .
jpaugh

3
또한alpine/socat
Jamby

3
훌륭한 답변입니다. 더러운 해킹없이 간단하게 처리 할 수있는 라이너.
Bruno Brant

2
이것은 나를 위해 완벽하게 작동했습니다. 감사합니다! 네트워크를 생성하는 도커 컴포지션에서 노출되지 않은 컨테이너를 시작한 후 시작 명령 을 추가 --net myfoldername_default해야 verb/socat했습니다.
emazzotta

35

적어도 Docker 1.4.1에서는 IPtables 해킹이 작동하지 않습니다.

가장 좋은 방법은 노출 된 포트로 다른 컨테이너를 실행하고 socat으로 릴레이하는 것입니다. 이것이 SQLPlus로 데이터베이스에 (일시적으로) 연결 한 것입니다.

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

도커 파일 :

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 파일에 코드 형식을 추가 할 수 있습니까? 꽤 유용한 것 같습니다.
AusIV

1
2016 년 2 월 : Docker 1.9.1을 실행하면 이것이 성공적으로 작동하는 유일한 솔루션이었습니다. IPTables 솔루션이 작동하지 않았습니다. FROM리소스를 효율적으로 사용하려면 DB 컨테이너 와 동일한 기본 이미지 를 사용하는 것이 좋습니다 .
엑스 칼리버

4
당신은주고 할 수 있습니다 즈 / socat 해볼. 사전 설치된 socat과 함께 제공되며 socat 옵션을 명령으로 허용하므로 Dockerfile을 전혀 쓸 필요가 없습니다.
trkoch

35

다른 아이디어가 있습니다. SSH를 사용하여 포트 전달을 수행하십시오. Docker 호스트가 VM 인 경우 OS X (및 아마도 Windows)에서도 작업 할 수 있다는 이점이 있습니다.

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

4
내 경우에는 실행중인 도커 이미지에 바이너리 ssh가 없습니다.
Radu Toader

내 컴퓨터에서 SSH 키를 생성하여이를 도커 컨테이너에 넣어야합니까?
8

컨테이너 내부의 @octavian ssh-keygen -t rsa. 호스트의 authorized_keys에 pub 키 추가
Luke W

8

나는이 같은 문제를 해결해야했고 실행중인 컨테이너를 멈추지 않고 해결할 수있었습니다. 이 솔루션은 2016 년 2 월 현재 Docker 1.9.1을 사용하는 최신 솔루션입니다. 어쨌든이 답변은 @ ricardo-branco의 답변에 대한 자세한 버전이지만 새로운 사용자에게는 더 깊이 있습니다.

내 시나리오에서는 컨테이너에서 실행중인 MySQL에 임시로 연결하고 싶었고 다른 응용 프로그램 컨테이너가 컨테이너에 연결되어 있기 때문에 데이터베이스 컨테이너를 중지, 재구성 및 다시 시작하는 것은 시작이 아닙니다.

SSH 터널링을 통해 Sequel Pro에서 외부로 MySQL 데이터베이스에 액세스하고 싶기 33306때문에 호스트 컴퓨터에서 포트를 사용하려고 합니다. 3306외부 MySQL 인스턴스가 실행중인 경우를 제외하고는 아닙니다 .

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

동사 / 소 카트 또는 알파인 / 소 카트 이미지를 직접 사용할 수 있습니다. 동사 /
소 카트

7

허용되는 답변 iptables 솔루션에 추가하기 위해 호스트에서 두 개의 명령을 더 실행하여 외부 환경에서 열어야했습니다.

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 :이 규칙과 임시는 컨테이너가 다시 시작될 때까지만 지속됩니다.


이것은 나를 위해 일했습니다 ... 그러나 나중에 Cavat의 일부가있었습니다. 참고 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

이것은 하나의 작은 수정으로 나를 위해 일했습니다 ...이 명령은 효과가있었습니다 : 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 컨테이너에 가상 머신과 같은 실제 인터페이스에 필요한 양을 여러 가지 방법으로 제공 할 수 있습니다.

맥 블란 인터페이스

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옵션을 사용하면 호스트 인터페이스를 컨테이너로 공유 할 수 있지만 공유 특성으로 인해 하나의 호스트에서 여러 컨테이너를 실행하기에는 적합하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.