Docker 컨테이너를 호스트 네트워크로 설정하는 방법

DockerBeginner
지금 연습하기

소개

Docker 컨테이너는 호스트 시스템에 간섭하지 않고 애플리케이션을 실행하는 격리된 환경입니다. 기본적으로 Docker 는 컨테이너 트래픽을 격리하기 위해 가상 네트워크를 생성하지만, 때로는 컨테이너가 호스트의 네트워크를 직접 사용하도록 할 수 있습니다.

이 랩에서는 Docker 컨테이너가 호스트 네트워크를 사용하도록 구성하는 방법을 배우게 됩니다. 기본 브리지 네트워킹과 호스트 네트워킹의 차이점을 살펴보고, 호스트 네트워킹을 언제 사용해야 하는지 이해하며, 실질적인 예제를 구현하여 그 이점을 직접 확인할 것입니다.

Docker 네트워크 유형 이해

컨테이너를 호스트 네트워크를 사용하도록 구성하기 전에 Docker 에서 사용할 수 있는 다양한 네트워크 유형을 살펴보고 그 목적을 이해해 보겠습니다.

사용 가능한 Docker 네트워크 확인

시스템의 기본 Docker 네트워크를 검사하는 것으로 시작해 보겠습니다.

  1. LabEx 환경에서 터미널을 엽니다.

  2. 다음 명령을 실행하여 모든 Docker 네트워크를 나열합니다.

docker network ls

다음과 유사한 출력을 볼 수 있습니다.

NETWORK ID     NAME      DRIVER    SCOPE
1234567890ab   bridge    bridge    local
0987654321cd   host      host      local
abcdef123456   none      null      local

Docker 는 세 가지 기본 네트워크를 제공합니다.

  • bridge: 컨테이너의 기본 네트워크
  • host: 호스트의 네트워킹을 직접 사용
  • none: 네트워킹 없음 (컨테이너가 격리됨)

기본 브리지 네트워크 이해

네트워크를 지정하지 않고 컨테이너를 실행하면 Docker 는 이를 기본 브리지 네트워크에 연결합니다. 이렇게 하면 각 컨테이너에 가상 네트워크 인터페이스가 생성되고 컨테이너가 서로 및 외부 네트워크와 통신할 수 있습니다.

이것을 실제로 살펴보겠습니다.

  1. 기본 브리지 네트워크를 사용하여 간단한 nginx 컨테이너를 실행합니다.
docker run --name nginx-bridge -d -p 8080:80 nginx:alpine
  1. 컨테이너의 네트워크 설정을 확인합니다.
docker inspect nginx-bridge --format '{{json .NetworkSettings.Networks}}' | jq

컨테이너가 브리지 네트워크에 연결되어 있고 자체 IP 주소를 가지고 있음을 보여주는 출력을 볼 수 있습니다.

  1. 매핑된 포트에 요청을 보내 호스트에서 컨테이너에 액세스할 수 있는지 확인합니다.
curl http://localhost:8080

nginx 시작 페이지 HTML 콘텐츠를 볼 수 있습니다.

이것은 브리지 네트워크가 작동하는 방식을 보여줍니다. 컨테이너는 자체 IP 주소를 가지고 있으며 컨테이너의 포트 80 은 호스트의 포트 8080 에 매핑됩니다.

호스트 네트워크 모드로 컨테이너 실행

이제 호스트 네트워크를 사용하여 컨테이너를 실행하는 방법을 살펴보고 브리지 네트워크와 어떻게 다른지 이해해 보겠습니다.

호스트 네트워크 사용

호스트 네트워크 모드를 사용하면 컨테이너가 호스트의 네트워크 네임스페이스 (network namespace) 를 직접 공유합니다. 이는 다음을 의미합니다.

  • 컨테이너는 호스트의 IP 주소를 사용합니다.
  • 포트 매핑이 필요하지 않습니다.
  • 컨테이너는 호스트의 모든 네트워크 인터페이스에 액세스할 수 있습니다.
  • 컨테이너가 호스트에서 이미 사용 중인 포트를 사용하려고 하면 포트 충돌이 발생할 수 있습니다.

호스트 네트워킹으로 컨테이너를 실행해 보겠습니다.

  1. 이전 nginx 컨테이너를 중지하고 제거하여 포트 80 을 확보합니다.
docker stop nginx-bridge
docker rm nginx-bridge
  1. 이제 호스트 네트워크를 사용하여 새 nginx 컨테이너를 실행합니다.
docker run --name nginx-host --network host -d nginx:alpine

컨테이너가 호스트의 네트워크 스택을 직접 사용하므로 -p 플래그를 사용하여 포트 매핑을 지정할 필요가 없다는 점에 유의하십시오.

  1. 컨테이너의 네트워크 설정을 확인합니다.
docker inspect nginx-host --format '{{json .NetworkSettings.Networks}}' | jq

컨테이너가 호스트 네트워크에 연결되어 있음을 확인할 수 있습니다.

  1. 호스트의 포트 80 에서 nginx 서버에 직접 액세스합니다.
curl http://localhost:80

nginx 시작 페이지 HTML 콘텐츠를 볼 수 있습니다.

차이점 이해

두 가지 접근 방식을 비교해 보겠습니다.

  1. 브리지 네트워킹 (기본값) 사용 시:

    • 컨테이너는 자체 IP 주소를 갖습니다.
    • 컨테이너 서비스에 액세스하려면 포트 매핑이 필요합니다.
    • 격리를 위한 추가 네트워크 계층이 있습니다.
    • 격리로 인해 더 안전합니다.
  2. 호스트 네트워킹 사용 시:

    • 컨테이너는 호스트의 IP 주소를 사용합니다.
    • 포트 매핑이 필요하지 않습니다.
    • 호스트 네트워크 인터페이스에 직접 액세스합니다.
    • 더 나은 네트워크 성능을 제공합니다.
    • 컨테이너와 호스트 간의 격리가 줄어듭니다.

네트워크 성능 및 동작 비교

이제 Docker 에서 브리지 (bridge) 및 호스트 (host) 네트워킹 간의 실제 차이점을 확인하기 위해 몇 가지 테스트를 실행해 보겠습니다.

네트워크 성능 테스트

먼저, 브리지 네트워킹을 사용하는 컨테이너와 호스트 네트워킹을 사용하는 다른 컨테이너를 생성한 다음 성능을 비교해 보겠습니다.

  1. 이전 nginx 컨테이너를 중지하고 제거합니다.
docker stop nginx-host
docker rm nginx-host
  1. 성능을 테스트하기 위해 브리지 네트워킹으로 새 컨테이너를 생성합니다.
docker run --name bridge-test -d --network bridge nginx:alpine
  1. 호스트 네트워킹으로 다른 컨테이너를 생성합니다.
docker run --name host-test -d --network host nginx:alpine
  1. docker exec를 사용하여 각 컨테이너에서 간단한 네트워크 테스트를 실행해 보겠습니다.

브리지 네트워크 컨테이너의 경우:

docker exec bridge-test sh -c "time wget -q -O /dev/null http://google.com"

호스트 네트워크 컨테이너의 경우:

docker exec host-test sh -c "time wget -q -O /dev/null http://google.com"

실행 시간을 비교합니다. 일반적으로 호스트 네트워크 컨테이너는 추가 네트워크 계층을 피하므로 약간 더 나은 성능을 보입니다.

네트워크 인터페이스 검사

두 컨테이너의 네트워크 인터페이스를 검사해 보겠습니다.

  1. 브리지 네트워크 컨테이너의 경우:
docker exec bridge-test ip addr show

이 컨테이너가 호스트와 격리된 자체 네트워크 인터페이스를 가지고 있음을 알 수 있습니다.

  1. 호스트 네트워크 컨테이너의 경우:
docker exec host-test ip addr show

이 컨테이너가 모든 물리적 네트워크 인터페이스를 포함하여 호스트 시스템과 정확히 동일한 네트워크 인터페이스를 가지고 있음을 알 수 있습니다.

  1. 호스트의 네트워크 인터페이스와 비교합니다.
ip addr show

호스트 네트워크 컨테이너의 인터페이스는 호스트 시스템의 인터페이스와 일치해야 합니다.

포트 충돌 이해

호스트 네트워킹을 사용하는 경우 컨테이너가 호스트에서 이미 사용 중인 포트를 사용하려고 하면 포트 충돌이 발생할 수 있습니다.

  1. 실행 중인 모든 컨테이너를 중지하고 제거합니다.
docker stop bridge-test host-test
docker rm bridge-test host-test
  1. 포트 8080 을 사용하여 호스트에서 서비스를 시작합니다.
python3 -m http.server 8080 &
  1. 이제 포트 8080 도 사용하려는 호스트 네트워킹으로 컨테이너를 실행해 봅니다.
docker run --name conflict-test --network host -d -p 8080:80 nginx:alpine

포트 8080 이 호스트에서 이미 사용 중이므로 오류가 표시되어야 합니다.

  1. Python HTTP 서버를 정리합니다.
kill %1

이는 호스트 네트워킹의 잠재적인 단점 중 하나를 보여줍니다. 즉, 호스트와의 포트 충돌을 인식해야 합니다.

호스트 네트워킹의 실제 사용 사례

이제 호스트 네트워킹이 유용한 몇 가지 실제 사용 사례를 살펴보고 실제 예제를 구현해 보겠습니다.

호스트 네트워킹을 사용해야 하는 경우

호스트 네트워킹은 다음과 같은 시나리오에서 특히 유용합니다.

  1. 성능이 중요한 애플리케이션: 네트워크 성능이 중요하고 브리지 네트워킹의 오버헤드를 피하려는 경우.

  2. 네트워크 모니터링 도구: 호스트에서 네트워크 트래픽을 캡처하거나 분석해야 하는 애플리케이션.

  3. 여러 포트를 사용하는 애플리케이션: 많은 포트 또는 동적 포트 할당을 사용하는 서비스가 있는 경우.

  4. 하드웨어 네트워크 인터페이스에 액세스: 컨테이너가 물리적 네트워크 인터페이스에 직접 액세스해야 하는 경우.

호스트 네트워킹으로 네트워크 모니터링 도구 구현

실제 예제를 구현해 보겠습니다. 호스트의 네트워크 인터페이스에 액세스해야 하는 간단한 네트워크 모니터링 컨테이너입니다.

  1. 먼저, 호스트 네트워킹을 사용하여 네트워크 모니터링 도구를 실행합니다.
docker run --name netmon --network host -d alpine:latest sleep infinity

이 컨테이너는 무기한으로 실행되어 컨테이너 내에서 명령을 실행할 수 있습니다.

  1. 이제 컨테이너 내에서 일부 네트워크 트래픽을 캡처해 보겠습니다.
docker exec netmon sh -c "apk add --no-cache tcpdump && tcpdump -c 10 -i any -n"

위의 명령은 다음을 수행합니다.

  • 컨테이너에 tcpdump 도구를 설치합니다.
  • 모든 인터페이스에서 10 개의 패킷을 캡처합니다.
  • 숫자 출력 (호스트 이름 확인 없음) 을 표시합니다.

호스트 네트워킹을 사용하고 있으므로 컨테이너는 호스트의 모든 네트워크 인터페이스에 직접 액세스할 수 있습니다.

  1. 특정 호스트 서비스로의 트래픽을 캡처할 수 있는지 확인해 보겠습니다.
## 새 터미널 창을 열고 일부 트래픽을 생성합니다.
curl http://localhost:80 &

## 원래 터미널에서 실행합니다.
docker exec netmon sh -c "apk add --no-cache tcpdump && tcpdump -c 5 -i any port 80 -n"

포트 80 에서 HTTP 트래픽을 캡처하는 컨테이너를 볼 수 있습니다.

이는 호스트 네트워킹을 통해 컨테이너가 호스트의 네트워크 인터페이스에 액세스하고 트래픽을 캡처할 수 있음을 보여줍니다. 이는 브리지 네트워킹으로는 더 어려울 것입니다.

Docker Compose 파일에서 호스트 네트워킹 사용

Docker Compose 를 사용하는 경우 docker-compose.yml 파일에서 호스트 네트워킹을 사용하도록 서비스를 구성할 수 있습니다.

version: "3"
services:
  web:
    image: nginx:alpine
    network_mode: "host"

이 구성은 web 서비스가 호스트의 네트워크 스택을 사용하도록 합니다.

보안 고려 사항 및 모범 사례

Docker 컨테이너에서 호스트 네트워킹을 사용할 때는 보안 영향을 이해하고 모범 사례를 따르는 것이 중요합니다.

호스트 네트워킹의 보안 영향

호스트 네트워킹을 사용하면 컨테이너와 호스트 시스템 간의 격리가 줄어듭니다. 이는 다음과 같은 몇 가지 보안 영향을 미칩니다.

  1. 네트워크 격리 감소: 호스트 네트워킹을 사용하는 컨테이너는 호스트의 모든 네트워크 인터페이스 및 포트에 액세스할 수 있습니다.

  2. 잠재적인 포트 충돌: 컨테이너의 서비스가 호스트에서 실행 중인 서비스와 충돌할 수 있습니다.

  3. 공격 표면 증가: 컨테이너가 손상된 경우 공격자는 호스트 네트워크에 더 직접적으로 액세스할 수 있습니다.

호스트 네트워킹 사용을 위한 모범 사례

호스트 네트워킹을 사용할 때는 다음 모범 사례를 따르십시오.

  1. 컨테이너 권한 제한: 호스트 네트워킹을 사용하는 경우에도 최소 권한의 원칙을 따르십시오.
## 호스트 네트워킹으로 컨테이너를 실행하지만 기능을 삭제합니다.
docker run --name secure-host-net --network host --cap-drop ALL --cap-add NET_ADMIN -d alpine:latest sleep infinity
  1. 필요한 경우에만 호스트 네트워킹 사용: 호스트 네트워킹은 실제로 필요한 컨테이너에만 사용하고 기본 선택 사항으로 사용하지 마십시오.

  2. 정기적인 보안 감사: 잠재적인 보안 문제를 위해 호스트 네트워킹을 사용하는 컨테이너를 더 면밀히 모니터링합니다.

  3. 가능한 경우 읽기 전용 파일 시스템 사용:

## 호스트 네트워킹 및 읽기 전용 파일 시스템으로 컨테이너를 실행합니다.
docker run --name readonly-host-net --network host --read-only -d alpine:latest sleep infinity
  1. 사용하지 않는 컨테이너 정리:
## 중지된 모든 컨테이너를 제거합니다(정리).
docker container prune -f

Lab 환경 정리

이 Lab 에서 생성한 모든 컨테이너를 정리해 보겠습니다.

## 모든 컨테이너 나열
docker ps -a

## 실행 중인 모든 컨테이너 중지
docker stop $(docker ps -q) 2> /dev/null || true

## 모든 컨테이너 제거
docker rm $(docker ps -a -q) 2> /dev/null || true

## 모든 컨테이너가 제거되었는지 확인
docker ps -a

이러한 명령을 실행한 후에는 컨테이너가 나열되지 않아야 합니다.

브리지 네트워킹과 호스트 네트워킹 중 선택 시기

각 네트워크 유형을 사용해야 하는 시기를 요약하면 다음과 같습니다.

다음과 같은 경우 브리지 네트워킹 (기본값) 을 사용합니다.

  • 컨테이너 간의 격리가 필요한 경우
  • 포트 매핑을 정밀하게 제어해야 하는 경우
  • 애플리케이션이 호스트 네트워크 인터페이스에 직접 액세스할 필요가 없는 경우
  • 보안이 최우선 순위인 경우

다음과 같은 경우 호스트 네트워킹을 사용합니다.

  • 네트워크 성능이 중요한 경우
  • 호스트 네트워크 인터페이스에 직접 액세스해야 하는 경우
  • 컨테이너가 많은 포트 또는 동적 포트 할당을 사용해야 하는 경우
  • 네트워크 트래픽을 모니터링해야 하는 경우
  • 컨테이너가 호스트와 동일한 IP 주소를 가져야 하는 경우

요약

이 Lab 에서 다음을 배웠습니다.

  • Docker 브리지 네트워킹과 호스트 네트워킹의 차이점
  • --network host 플래그를 사용하여 호스트 네트워킹으로 Docker 컨테이너를 실행하는 방법
  • 브리지 네트워킹에 비해 호스트 네트워킹의 성능 이점
  • 호스트 네트워킹이 유용한 실제 사용 사례
  • 호스트 네트워킹 사용 시 보안 고려 사항 및 모범 사례

호스트 네트워킹은 컨테이너와 호스트 간의 네트워크 격리를 제거하여 컨테이너가 호스트의 네트워크 인터페이스에 직접 액세스할 수 있도록 합니다. 이는 성능을 향상시키고, 구성을 단순화하며, 네트워크 모니터링 도구와 같은 특정 유형의 애플리케이션이 제대로 작동하도록 할 수 있습니다.

호스트 네트워킹은 특정 이점을 제공하지만 격리를 줄이고 보안 문제를 야기할 수 있다는 점을 기억하십시오. 항상 모범 사례를 따르고 사용 사례에서 필요한 경우에만 호스트 네트워킹을 사용하십시오.