도커 네트워크 인터페이스를 알아보자
❕ 문제 상황
Nest.js 를 배우다가 데이터베이스를 사용할 일이 생겼다.
보통 나는 환경이 분리되는 걸 선호한다. 그래서 전역적으로 설치되는걸 좋아하지 않는다. pip
같은 것들 말이다.
그래서 찾아보니, Docker
위에 MySQL
컨테이너를 올려서 사용하면 환경 분리가 된다고 하더라.
도커와 관련해서는 CLI
환경에서 결국엔 다루는게 맞다고 생각했다. 그런데, 일단은 도커가 처음이기에 아직 내가 많이 몰라서 Docker Desktop
을 이용해서 문제를 해결하려고 한다.
MySQL 에 접속이 안되는 문제
1
2
3
mysql -u root -p'root' -h 172.17.0.2
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2003 (HY000): Can't connect to MySQL server on '172.17.0.2:3306' (60)
위와 같이, root
계정으로 직접적으로 접속하려고 했었다. 그런데, mysql
클라이언트 단에서 서버로 연결을 하지 못하는 상황이었다.
172.17.0.2
IP 는 inspect
해서 나온 도커 컨테이너가 할당 받은 IP 이다.
해당 IP로 연결을 시도해봤는데 연결되지 않는다고 떴다.
🖋️ 해결 과정
결론적으로는, 아래와 같은 명령어를 써서 해결했다.
첫번째로, 도커의 컨테이너를 외부에서 접속하게 될 때에는 내 자신의 IP로 접속해야한다.
1
mysql -u root -p -h localhost -P 3306
그런데, 그 과정에서 아래와 같은 에러가 떴다.
1
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
한번 더 알아보니, localhost
대신에 127.0.0.1
로 호스트를 바꾸니 접속이 되었다.
🐳 도커와 도커 엔진을 돌리는 컴퓨터가 IP 할당을 받는 방식
그렇다면 왜 컨테이너 IP로 접속을 못하고, localhost
로 접속을 해야할까?
이것을 알려면 도커의 네트워크 구조를 알아야한다.
도커 네트워크 구조
도커는 기본적으로 브릿지 네트워크
를 사용하여 호스트 - 컨테이너 간 통신을 관리한다.
브릿지 네트워크란?
브리지 네트워크는 데이터 링크 계층(Layer 2)에서 작동하는 네트워크 연결 방식 중 하나입니다. 서로 다른 네트워크 세그먼트를 하나의 논리적 네트워크로 연결하는 기술입니다.
세그먼트 내에서 통신을 할 때에는 노드 간 직접 통신이 가능합니다.
세그먼트 외부에서 통신을 할 때에는 브릿지를 통해 통신이 가능합니다.
이 때 중요한건 세그먼트 외부 간 통신을 직접할 수 있도록 노드 간 직접 통신이 가능하다는 점입니다.
이때, 도커는 호스트 컴퓨터와 소통할 수 있도록 docker0
브릿지를 설치해줍니다. 해당 브릿지를 통해서 호스트 컴퓨터는 도커 컨테이너와 통신할 수 있게 된다.
도커가 생성한 가상 브릿지 docker0
와 커스텀 브릿지
도커는 컨테이너와 호스트, 나아가서 외부 인터넷과 통신하기 위해서 docker0
라는 이름의 가상 브릿지를 생성한다.
해당 브릿지는 호스트 컴퓨터 내에서 생성 되며, 각 컨테이너는 eth0
라는 네트워크 인터페이스를 통해 docker0
브릿지와 연결된다. 이때, veth*
라는 이름으로 docker0
브릿지에서 네트워크 인터페이스로 연결된다.
또한, 별도의 커스텀 브릿지도 생성할 수 있다.
아래와 같이 br-
로 시작하는 경우 컨테이너에서 직접 외부로 통신하기 위한 커스텀 브릿지 다.
1
2
3
4
5
6
29: br-925c9621d01a: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
inet 172.34.0.1/16 brd 172.34.255.255 scope global br-925c9621d01a
~
162: vetha37a37a@if161: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-925c9621d01a state UP group default
~
164: veth27db687@if163: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-925c9621d01a state UP group default
컨테이너 내부IP 로 접속할 수 없는 이유
- 컨테이너 내부의 IP (172.17.x.x/16) 는 격리된 공간 내에 존재하기 때문
- 그래서 외부에서는
docker0
브릿지를 통해 접근이 가능하다.
도커 브릿지로 직접 접속 할 수는 없나요?
직접 연결 시도도 가능합니다.
단, 아래에서 설명할 예정이지만,
docker0
브릿지는 리눅스 환경에서만 확인 가능합니다.
Mac/Windows 에서의 Docker
docker0
라는 브릿지는 Mac/Windows 옵션에서는 존재하지 않는다. 맥과 윈도우 환경에서는 별도의 가상 리눅스 위에서 설치되기 때문이다.
따라서 맥, 윈도우 환경에서 접속하려면 해당 브릿지로 접속하려면 172.17.0.1
이 아니라 루프백 IP 를 통해서 docker0
인터페이스를 찾아서 접근하도록 해야한다.
만약 이미 호스트 컴퓨터에서 포트가 열려있으면 어떻게 될까?
컨테이너 내부
의 포트는 호스트 포트와 독립적
으로 작동한다!
‘3307:3306’ -> ‘host - docker0 : 3307’ , ‘veth - eth : 3306’
포트를 지정할 때 docker0
브릿지에서 매핑을 해주기 때문에, 컨테이너에서는 어떤 포트로 열더라도, 자유롭게 열어도 상관없다.
앞서 말한대로 따로 임의로 지정하지 않으면, 컨테이너 내부에서만 열리게 된다. 이렇게 되면 포트를 컨테이너 외부로 열고 싶지 않을 때 유용하겠죠?
정리하면 아래와 같다.
- 호스트에서 3306 포트를 사용 중이더라도 컨테이너 내부에서 3306 포트를 사용할 수 있다.
- 포트 충돌은 호스트 포트를 컨테이너 포트에 매핑할 때만 발생한다.
1
docker run -p 3307:3306 mysql
도커에서 실제로 실행할때, 3307
포트와 같이 외부로 매핑될 포트를 지정해주는 작업이 일어날 때 docker0
브릿지 테이블에서 수정이 일어난다.
🤔 127.0.0.1
과 localhost
의 차이
그렇다면, 왜 172.17.x.x
포트로 연결이 안되었는지 이해가 됐을 것이다.
172.17.x.x
IP는 컨테이너 내부와 도커 브릿지 사이에 연결된 연결 정보임- 맥/윈도우 도커 환경에서는 별도의
VM
위에서 돌아가기 때문에172.17.0.1
로 직접 접속할 수 없음
하지만 그렇다고 문제가 해결되진 않았는데, localhost
를 통해서 접속했을 때는 접속이 되지 않았다.
결국 해결했던 방법은 127.0.0.1
로 접속을 하니 문제가 해결되었다.
일반적으로
localhost
란 도메인은 존재하지 않는다.
모든 네트워크 요청시, DNS
라는 곳에서 도메인 이름을 통해 IP 를 알아내기 때문이다. 지원되는 IP 형식이 아니면 DNS
를 통해 IP를 찾아서 해당 IP를 통해 연결되도록 만든다.
그 원인은 두가지에 있었는데, 도커의 IPv6 지원과 /etc/hosts
파일의 영향 때문이었다.
/etc/hosts
파일을 통해 localhost
라는 DNS가 어떤 IP를 나타내는지 컴퓨터에 바로 알려줄 수 있도록할 수 있다. 일종의 로컬 DNS? 와 같은 느낌.
실제로 나와같은 경우 아래처럼 IPv6
, IPv4
로컬 호스트 주소를 모두 표현하도록 연결되었는데, IPv6
로 연결이 시도 되어서 이런 문제가 생겼다.
도커에서 IPv6 지원 설정 활성화
도커 데스크탑 기준으로 설명
- Docker Desktop 실행
- 상단 메뉴의 설정(Settings) 아이콘 클릭
- Docker Engine 섹션으로 이동
- 설정 JSON에 위의 설정을 추가
- Apply & Restart 클릭
1
2
3
4
5
{
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64",
"ip6tables": true
}
이후 제대로 매핑된 걸 확인하려면 아래 명령어를 실행해주자.
1
docker network ls
MySQL 에서의 localhost
특수처리
IPv6 옵션을 활성화 해도 제대로 해결할 수 없었는데, 그 이유는 mysql
만이 가지는 로컬 호스트에 대한 특수 처리 때문이었다.
MySQL은 localhost로 접속 시 TCP/IP가 아닌 Unix 소켓 파일을 사용한다. 따라서, 도커 컨테이너의 MySQL은 호스트와 다른 환경에서 실행되므로 소켓 파일에 접근할 수 없다.
MySQL 에서 접속할 때는 그냥 맘놓고 127.0.0.1로 접속하자…!