티스토리 뷰

여기 블로그 글을 RSS 받으시는 분들은 아시겠지만 저는 도커 환경을 꽤 좋아합니다. 일단 1) PC를 군더더기 없이 깔끔하게 사용할 수 있는 장점이 있고 2) 정리할때도 깔끔하게 할 수 있습니다. 3) 나중에 다른 시스템으로 옮길 때 호환성은 이루말 할수 없습니다.

 

아무튼, 이런 장점들 때문에 웬만하면 모든 개발을 도커에서 진행하고 있는데 아래와 같은 상황을 마주했습니다. 한참 딥러닝 모델을 개발하고, 이를 jupyter notebook으로 inference등의 테스트를 진행하려고 보니, Host 머신에서 jupyter의 포트로 접속 할 수가 없지 않겠습니까? Host에서 컨테이너 내부의 포트로 접속하려면 PNAT나 Proxy를 사용해야 하는데 이런것들은 처음 컨테이너를 생성할 때 지정을 해줘야 하는 것들입니다. 관련해서 검색해보면 유사한 내용으로 stackoverflow에서 아래와 같은 내용이 검색됩니다.

https://stackoverflow.com/questions/19335444/how-do-i-assign-a-port-mapping-to-an-existing-docker-container

 

How do I assign a port mapping to an existing Docker container?

I'm not sure if I've misunderstood something here, but it seems like it's only possible to set port mappings by creating a new container from an image. Is there a way to assign a port mapping to an

stackoverflow.com

upvote를 가장 많이 받은 댓글을 확인해보면 1) 컨테이너를 멈추고 2) 그 컨테이너로 이미지를 새로 생성한 후에 3) 생성된 이미지로 PNAT를 적용시킨 컨테이너를 새롭게 만들면 된다고 가이드 되고 있습니다.

 

하지만 지금 직면해 있는 문제에서 이게 말이 됩니까? jupyter 노트북 좀 잠깐 올려서 몇 개만 확인하려는데 이미지와 컨테이너를 새롭게 생성해야 한다니요. 게다가 이미지 이름도 새롭게 만들어야 하기 때문에 여러가지로 복잡해집니다(Tag를 사용하는 것은 물론 좋은 방법입니다만). 이 댓글에 대한 아래 comment에서 누군가의 분노가 느껴지지 않으신가요?

https://stackoverflow.com/questions/19335444/how-do-i-assign-a-port-mapping-to-an-existing-docker-container#comment88405483_26622041

굉장히 해괴한 해결책이라며 이게 도대체 왜 이렇게 많은 추천을 받은 답글인지 알 수 없다는 comment입니다. 저로서는 충분히 공감되는 내용이고요. 그 밑에 있는 댓글중에는 /var/lib/docker/containers/[hash_of_the_container]/hostconfig.json 파일을 수정하면 된다는 내용도 있습니다. 꽤 많은 upvote를 받은 것으로 보아 잘 될 가능성이 높아보여요. 하지만 그 밑에 comments를 살펴보면 뭔가 문제가 있긴 있나봅니다. 

 

이것도 믿을 수 없게 된다면 그냥 OS에 firewall을 수정해서 임시로 NAT 시켜주면 되지 않을까요? 그래요. 방화벽을 직접 조작해보도록 합시다. (여기 예시에서 사용된 Host OS는 Ubuntu 16.04 입니다)

 

컨테이너의 IP 확인

일단 jupyter notebook을 실행시킨 컨테이너의 IP를 확인해봅시다. 기본 컨테이너이기 때문에 ifconfig, ip 등의 커맨드는 없습니다. 고로 Host에서 docker inspect [hash_of_the_container] 옵션을 통해 컨테이너의 IP를 확인하도록 합니다. (컨테이너의 hash값은 docker ps로 미리 확인합니다)

$ docker inspect f21be | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.3",
                    "IPAddress": "172.17.0.3",

위와 같이 컨테이너의 IP 주소는 172.17.0.3으로 확인되었습니다. 대부분 컨테이너의 IP는 docker 기본 게이트웨이인 172.17.0.1을 제외하고 순서대로 비어있는 주소를 할당받게 됩니다. 고정으로 사용하지 않았다면 말이죠. 이 글을 보는 여러분의 환경은 충분히 다를 수 있다는 것을 고려해주시기 바랍니다.

 

FIrewall 설정

여기서는 Ubuntu의 ufw는 비활성화 되어 있으며 iptables로 직접 제어 되는 상태를 전제로 합니다. ufw가 비활성화 되어 있는 것은 아래와 같이 확인해주시면 됩니다.

$ sudo ufw status verbose
[sudo] password for caley: 
상태: 비활성

Host에서 컨테이너로 연결시키기 위해서는 세 개의 Rule을 추가해줘야 하는데 일단 policy Rule입니다. 해당 접근을 허용해줍니다.

sudo iptables -A DOCKER -p tcp --dport 8888 -j ACCEPT -d 172.17.0.3

위에서 미리 이야기한것 처럼 컨테이너의 IP는 여러분의 환경에 맞도록 변경하셔야 합니다. dport는 컨테이너의 포트를 지정해주시면 됩니다. 저는 jupyter의 포트를 8888로 쓰고 있기 때문에 위와 같이 지정했습니다. 정상적으로 방화벽에 적용되었는지는 아래와 같이 iptables -nL 을 통해 확인할 수 있습니다. 중간에 Chain DOCKER 부분을 봐주시면 됩니다.

$ sudo iptables -nL
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         
DOCKER-USER  all  --  0.0.0.0/0            0.0.0.0/0           
DOCKER-ISOLATION-STAGE-1  all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.3           tcp dpt:8888

Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target     prot opt source               destination         
DOCKER-ISOLATION-STAGE-2  all  --  0.0.0.0/0            0.0.0.0/0           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

Chain DOCKER-ISOLATION-STAGE-2 (1 references)
target     prot opt source               destination         
DROP       all  --  0.0.0.0/0            0.0.0.0/0           
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

Chain DOCKER-USER (1 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

 

그리고나서 실제 포트를 변경해주는 POSTROUTING에 MASQUERADEDOCKER에 DNAT를 아래와 같이 설정해줍니다.

sudo iptables -t nat -A DOCKER -p tcp --dport 8888 -j DNAT --to 172.17.0.3:8888
sudo iptables -t nat -A POSTROUTING -p tcp --dport 8888 -j MASQUERADE -s 172.17.0.3 -d 172.17.0.3

정상적으로 설정된 것을 iptables -t nat -nL 을 통해서 확인해주도록 합시다.

$ sudo iptables -t nat -nL
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
MASQUERADE  tcp  --  172.17.0.3           172.17.0.3           tcp dpt:8888

Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8888 to:172.17.0.3:8888

이게 끝입니다. 브라우저를 통해 jupyter로 접속을 확인해보세요.

172.17.0.3:8888

 

정상적으로 잘 접근되는 것을 확인하실 수 있습니다. 

 

Firewall 정리

위에서 설정한 방화벽은 재부팅되면 어차피 깨끗하게 초기화 되기 때문에 무시하셔도 좋지만 jupyter를 종료하고 용도가 다 했을 때 지워버리고 싶다면 아래와 같이 삭제해주시면 됩니다.

sudo iptables -D DOCKER -p tcp --dport 8888 -j ACCEPT -d 172.17.0.3
sudo iptables -D DOCKER -t nat -p tcp --dport 8888 -j DNAT --to 172.17.0.3:8888
sudo iptables -D POSTROUTING -t nat -p tcp --dport 8888 -j MASQUERADE -s 172.17.0.3 -d 172.17.0.3

정상적으로 삭제된 것은 위에서 살펴본것처럼 iptables -nL, iptables -t nat -nL 로 봐주시면 되겠습니다.

 

마무리

컨테이너 내부의 포트로 접속하기 위해서 다른 여러가지 방법이 존재합니다만 현재 동작하고 있는 컨테이너를 종료하지 않고 가장 빠르게 원하는 결과(연결)를 얻을 수 있는 방법이 위에서 소개한 Firewall를 직접 조작하는 것이 아닌가 싶습니다. 위에서 설정한것들은 실제로 컨테이너를 생성할 때 port forwarding을 지정하면 세팅되는 방화벽 설정으로, 시스템 위험도가 상당히 낮습니다. 더욱이 위 예제는 Host 머신의 대역에서 컨테이너로 접근만을 열어줬기 때문에 보안상 더 안전하기도 하죠. 혹시 Host 머신의 네트워크 대역이 Public하다면 주의해야 합니다. 아무튼, 위에서 소개한 방식이 누군가에게는 도움이 되기를 바랍니다.

댓글
댓글쓰기 폼