
이번 포스팅을 쓴 이유로는 docker compose를 사용하면서 생겼던 트러블 슈팅 때문에 이 글을 쓰게 됐다 필자는 docker compose로
was 이미지와 데이터베이스 mongodb 이미지를 사용해서 컨테이너를 띄웠는데 아무 이상 없이 둘이 통신이 가능했다 하지만 개발 환경에서 계속 was의 코드를 변경하고 이미지로 build 해서 또다시 docker compose로 띄운다면 엄청난 시간 낭비일 것이다
그래서 docker-compose.yaml에 was와 관련된 건 주석을 하고 mongodb만 환경변수를 세팅하고 컨테이너로 띄우고 was는 그냥 로컬에서 실행시켜도 사실 둘 사이에 문제없이 통신이 가능할 것이다 여기까지도 문제없었다 통신하는데 문제가 없어서 이제 was를 빌드하고 docker run 명령어로 실행하고 통신을 하려 하는데 통신이 안 되는 문제가 발생했었다. 분명 mogodb는 docker compose up으로 컨테이너가 띄어져 있고 was도 docker run을 해서 컨테이너가 띄어져 있는데 말이다... 이제 한번 왜 저 상황이 발생하는지 설명해보려 한다
Docker 네트워킹이란 무엇인가
Docker 네트워크는 Docker 컨테이너를 서로 연결하고 호스트와 통신할 수 있도록 하는 방법이다. 기본적으로 새 컨테이너를 시작하면 자동으로 기본 bridge 네트워크에 연결된다. 그러나 컨테이너가 통신하는 방식을 더 잘 제어하기 위해 사용자 지정 네트워크를 생성할 수도 있다.
Docker에서 만들 수 있는 여러 유형의 네트워크가 있다.
- bridge 네트워크: 기본 네트워크 유형이며 컨테이너가 서로 및 호스트와 통신할 수 있도록 한다. 호스트의 네트워크 스택을 사용하고 컨테이너 간에 기본적인 격리를 제공한다. 브리지 네트워크에 연결된 각 컨테이너에는 고유한 IP 주소가 할당되며 ping 및 traceroute와 같은 표준 네트워킹 도구를 사용하여 컨테이너 간에 통신할 수 있다.
- Host 네트워크: 호스트의 네트워크 스택을 사용하며 컨테이너 간에 격리를 제공하지 않다. 호스트 네트워크에 연결된 컨테이너는 호스트의 IP 주소와 포트를 공유한다. 권한 있는 포트에서 수신 대기하거나 특정 IP 주소를 사용해야 하는 컨테이너를 실행해야 할 때 유용할 수 있다.
- 오버레이 네트워크: 이를 통해 여러 호스트에 걸쳐 컨테이너를 연결할 수 있다. VXLAN 프로토콜을 사용하여 여러 Docker 호스트에 걸쳐 있는 가상 네트워크를 만든다. 이는 swarm 클러스터의 여러 호스트에서 컨테이너화된 애플리케이션을 실행할 때 유용할 수 있다.
- Macvlan 네트워크: 이를 통해 네트워크에 연결된 각 컨테이너에 고유한 MAC 주소를 할당할 수 있으므로 LAN 네트워크에서 고유한 IP 주소를 할당할 수 있다.
- None 네트워크: 이 네트워크를 사용하면 모든 네트워크에서 컨테이너 연결을 끊을 수 있다. 이는 다른 컨테이너와 통신할 수 없는 "방화벽" 모드에서 컨테이너를 실행하려는 경우에 유용할 수 있다.

위의 그림은 도커의 네트워크 구조이며 각자 인터페이스가 연결이 되어있다 정확하게 어떻게 연결이 되어있는지 먼저 확인해 보겠다
먼저 아래에 있는 docker0부터 확인해 보자 ifconfig 명령어로 확인해 본다면 docker0 네트워크가 존재하는데 아래의 부분은 docker를 설치한 host의 인터페이스를 확인한 정보이다

docker0 인터페이스의 나열된 정보로는 IP는 172.17.0.1이며 넷 마스크는 255.255.0.0으로 B클래스인걸 확인할 수 있으며 자동으로 할당이 된다 이 부분은 사용자가 직접 할당할 수도 있다 또한 docker0은 가상 이더넷 브릿지이다
아래에 보이는 veth로 시작하는 인터페이스는 docker 컨테이너가 실행중일 때마자 veth라는 인터페이스가 생성이 된다 위의 그림을 확인해본다면 지금 컨테이너 한 개가 실행 중인 것을 확인할 수 있다 처음 도커 네트워크 구조를 그림으로 나타냈는데 docker0은 veth와 브릿지로 네트워크를 공유하고 있는데 뭘 보고 브릿지라고 얘기하는지 사실 모를 수 있다 brctl 명령어를 사용해 보겠다

확인해 보면 docker0이라는 브릿지로 위에서 ifconfig 명령어로 확인했을 때 있던 veth 인터페이스가 연결된 걸 확인할 수 있다
이제 도커 네트워크 세부구조를 확인해 보겠다. 아래의 명령어를 입력한다면 아래의 그림처럼 3개의 네트워크를 확인할 수 있는데
$ docker network ls

bridge
docker의 기본 network 방식은 bridge이다. docker daemon을 실행하면 먼저 docker0라는 bridge가 생성된다. 컨테이너 생성하게 되면, 각 컨테이너마다 고유한 network namespace 영역이 하나씩 생성되며, 이때 docker0 bridge에 container의 인터페이스들이 하나씩 binding 되는 구조이다.
host
host 방식으로 컨테이너를 생성하면, 컨테이너가 독립적인 네트워크 영역을 갖지 않고 host와 네트워크를 함께 사용하게 된다.
컨테이너의 ip와 interface 정보를 확인해 보면 아래와 같이 host의 네트워크 정보와 동일하다. 또한 host 옵션으로 생성된 컨테이너의 경우는 bridge를 사용하지 않으므로 docker0에 바인딩되지 않는다.
none
none 옵션으로 컨테이너를 생성하게 되면 격리된 네트워크 영역을 갖긴 하지만, eth0 인터페이스가 없는 상태로 컨테이너사 생성이 된다 또한 bridge와 연결되지 않았기 때문에 외부와의 통신도 불가하다
먼저 docker-compose up을 하고 확인해 볼 건데 사용할 docker-copmose.yaml은 fastify로 만든 was와 mongodb를 띄우는 manifest이다
version: '3.1'
services:
fastify-was:
image: alsgur721/my-was:2.1
restart: always
ports:
- 3000:3000
depends_on:
- mongo
mongo:
image: mongo:latest
restart: always
ports:
- 27017:27017
기본적으로 docker compose는 docker를 사용 시 여러 컨테이너를 동시에 사용하는 것을 통합적으로 관리할 수 있도록 도와준다
또한 docker-compose.yaml에 네트워크를 지정해주지 않으면 자동적으로 docker-compose.yaml이 있는 경로의 이름으로 network를 생성하게 된다 아래의 그림을 확인한다면 network가 생성된 걸 확인할 수 있는데 inspect명령어로 다시 한번 확인해 보자

네트워크의 이름은 docker-compose.yaml이 있는 경로의 이름대로 ubuntu이며 서브넷은 2^16-2 이므로 사용가능한 IP로는 65534 개이며 -2 이유는 모두가 1인경우 브로드캐스트 주소로 사용하고 모두 0인경우엔 네트워크 주소로 사용하기 때문이다. 컨테이너 내부의 모든 패킷은 아래 나와있는 gateway 172.23.0.1로 가게 된다 그러면 각각 컨테이너 내부에서 정말 gateway를 향하는지 확인해 보겠다

확인해 보면 컨테이너의 gateway가 위에서 생성된 브릿지 네트워크의 IP인 172.23.0.1로 설정된 걸 알 수 있다

또한 컨테이너 내부에서 ifconfig 명령어로 확인해 본다면 위에서 확인했다시피 사용하는 브릿지의 네트워크 IP가 172.23.0.1 넷마스크는 255.255.0.0이므로 172.23.0.0/16 IP 대역인 172.23.0.3이라는 IP가 할당된 걸 확인할 수 있다

docker-compose를 이용했을 때 어떤 방법으로 컨테이너들 간의 통신이 이루어지는진 확인해 봤는데 docker-compose는 DNS 또한 제공하고 있는데 위에서 manifest를 확인할 때 service.fastify-was, service.mongo를 확인할 수 있는데 service에 사용할 이름을 정해주게 되면 docker 그 이름에 맞게 DNS를 생성하게 된다 그렇기 때문에 mysql이나 레디스 mongodb 등 was와 함께 docker-compose로 컨테이너를 실행시킨다면 was 환경변수에 mysql 등의 엔드포인트를 로컬이 아닌 서비스에서 만들어준 DNS로 지정해 줘야 통신이 가능하다 docker-compose로 컨테이너를 실행시켜서 브리지 네트워크가 생성이 되었어도 엄연히 서로 격리되어 있는 컨테이너이기 때문이다
이번에는 docker compose가 아닌 docker CLI로 network를 생성해 보겠다
docker 브릿지 네트워크 생성
$ docker network create --driver=bridge <생성할 브릿지명>

위의 출력을 확인해 보면 minhyeok라는 브릿지 네트워크가 생성된 걸 확인할수있다 다시 inspect로 확인해본다면 위에서 얘기했던 서브넷과 게이트웨이가 생성이된걸 확인할수있다

또한 ifconfig 명령어로 확인한다면 새로운 가상 인터페이스가 생성된걸 확인할 수 있다 br-03 fXXXXX로 시작하는 인터페이스가 위에서 생성한 minhyeok 브릿지 네트워크이다 아래를 다시 본다면 veth가 없는 걸 볼 수 있는데 아직 실행 중인 컨테이너가 없다는 걸 알 수 있다

이제 컨테이너를 실행하고 minhyeok이라는 브릿지 네트워크에 연결시켜 보겠다 다시 minhyeok의 상세를 확인해 본다면 바로 minhyeok을 만들었을때와는 다르게 컨테이너에 새로운 컨테이너가 바인딩된걸 확인 할 수있으면 --name을 mongo로 지정해준이유는 was에서 mongodb의 엔드포인트를 mongo:27017로 줬기때문이다 이번에는 mongodb와 통신할 was컨테이너를 실행해보겠다

was를 실행하고 확인해본다면 각각 minhyeok라는 브릿지에 연결이 되어서 네트워크 통신이 가능하게 되었다

이렇게 docker 네트워크를 공부하고 생각이 드는 게 당연히 mogodb를 docker-compose로 컨테이너를 실행시키고 was는 따로 컨테이너를 실행했을 때 왜 통신이 불가능했는지 알게 되었다 docker-compose는 자동적으로 네트워크를 생성해 주고 따로 docker run으로 실행시킨 컨테이너는 docker의 default 브릿지로 연결이 되기 때문에 서로 브릿지가 달라서 생긴 문제였다 하지만 위에서 나온 듯이 따로 실행시킨 컨테이너를 다른 네트워크와 통신을 하고 싶으면 --network 명령어로 지정해 준다면 문제없다
참고자료
https://docker-k8s-lab.readthedocs.io/en/latest/docker/bridged-network.html#docker0-bridge
Bridge Networking Deep Dive — Docker Kubernetes Lab 0.1 documentation
Through docker network command we can get more details about the docker0 bridge, and from the output, we can see there is no Container connected with the bridge now. You can also see this bridge as a part of a host’s network stack by using the ifconfig/i
docker-k8s-lab.readthedocs.io
https://docs.docker.com/engine/tutorials/networkingcontainers/
Network containers
docs.docker.com
https://www.youtube.com/watch?v=Tx12haz-4VA
'DevOps > Docker' 카테고리의 다른 글
[Docker] GitHub Actions으로 docker CI (도커캐시) (0) | 2023.01.12 |
---|---|
[Docker] ubuntu에서 docker & docker-compose 설치 방법 (0) | 2022.12.30 |
[Docker] docker 이미지 생성하기 (0) | 2022.12.12 |
[Docker] docker 로그 확인하기 (0) | 2022.12.05 |
[Docker] docker 볼륨 컨트롤하기 (0) | 2022.12.05 |