TOPIC/Infra

#2 Docker 그리고 Docker Network

admin_cloud 2024. 6. 24. 15:20

안녕하세요. TAK 입니다:)

 

이번 포스팅은 Docker Network에 알아보겠습니다!

 

아래 #1 Dcoker의 개념과 구조에 대한 글을 읽으시면, 이번 주제를 팔로우하는 데 도움이 되실 거예요.😋

 

#1 Docker 그리고 Docker Network

안녕하세요. TAK 입니다:) 날이 점점 더워지고 있는 요즘입니다😓 이번 포스팅의 주제로 Docker 그리고 Docker Network를 준비하였습니다😄(아무래도 Docker 개념을 선행하는 것이 Network 영역을 이

with-cloud.tistory.com

 

그럼 바로 시작하겠습니다!


Contents

    1. Docker Network

    1-1. Docker Network 란?

    : Docker Network는 Container 간, 그리고 컨테이너 외부 네트워크 간의 통신을 가능케 하여 이를 관리하고 격리하기 위한 기능 제공합니다. 이를 통해 다양한 네트워크 환경에서 컨테이너화된 애플리케이션을 효과적으로 배포하고 관리할 수 있습니다. Docker는 네트워크 구성을 쉽게 관리할 수 있도록 다양한 네트워크 드라이버와 옵션을 제공합니다.

     

    1-2. Docker Network Drivers의 종류

    : 아래 네트워크 드라이버의 경우, Native Driver 입니다. (Remote Driver는 3rd Patry Driver를 의미합니다.)

    https://media.licdn.com/dms/image/D4D12AQEtEBjpPLradw/article-cover_image-shrink_600_2000/0/1656643165785?e=2147483647&v=beta&t=9Sp6nUvVgoxsdNfthGN8eLslgpKmUkNCCMm07a4RGFs
    https://miro.medium.com/v2/resize:fit:1400/format:webp/1*WKiEgPXO8XXppoqgr7ZVQA.png

     

    • Bridge Network
      : Bridge Network는 Docker가 기본적으로 사용하는 네트워크 드라이버입니다. 이는 Docker 호스트 내에서 격리된 네트워크를 형성하며, 주로 단일 호스트에서 여러 컨테이너를 연결하는 데 사용됩니다.
      • 'docker0'라는 가상 브리지 인터페이스를 사용하여 컨테이너를 연결합니다. 동일한 브리지 네트워크에 있는 컨테이너는 서로 통신할 수 있습니다.
      • 단일 서버에서 여러 컨테이너로 구성된 애플리케이션을 실행할 때 유용합니다.
      • 컨테이너의 포트를 호스트의 포트에 매핑하여 외부 통신이 가능합니다.
        # 새로운 드라이버(Bridge) 생성
        docker network create --driver bridge my_bridge_network
        
        #container1(nginx)과 container2(redis)가 같은 Bridge 네트워크를 통해 서로 통신 가능
        docker run -d --name container1 --network my_bridge_network nginx
        docker run -d --name container2 --network my_bridge_network redis
    • Host Network
      : Host Network는 컨테이너가 Docker 호스트의 네트워크 스택을 직접 사용하도록 합니다. 이 경우, 컨테이너는 별도의 네트워크 네임스페이스를 가지지 않고, 호스트와 동일한 네트워크 인터페이스를 공유합니다.
      • 컨테이너가 호스트 네트워크 스택을 사용하므로, 네트워크 성능이 향상되지만 네트워크 격리가 줄어듭니다.
      • 성능이 중요한 경우나, 호스트 네트워크와 완전한 통합이 필요한 경우에 사용됩니다.
        # nginx 컨테이너가 호스트 네트워크 스택을 직접 사용 
        docker run -d --network host nginx
    • None Network
      : None Network는 네트워크가 전혀 연결되지 않은 상태로 컨테이너를 실행하는 네트워크 모드입니다.  
      • 네트워크 인터페이스가 없으며, 외부와의 통신이 불가능합니다.
      • 네트워크가 필요 없는 작업(예: 로컬 파일 작업이나 CPU 집약적인 작업)을 수행할 때 유용합니다.
        docker run -d --name isolated_container --network none nginx
    • Overlay Network
      : Overlay Network는 여러 Docker 호스트에서 실행되는 컨테이너를 연결하는 데 사용됩니다. 이는 Docker Swarm이나 Kubernetes 같은 오케스트레이션 도구와 함께 사용됩니다.
      • 여러 Docker 데몬(호스트)에서 실행되는 컨테이너를 연결할 수 있습니다.
      • VXLAN(Virtual Extensible LAN)을 사용하여 네트워크 패킷을 터널링 합니다.
        (이는 물리적 네트워크 상에 논리적 네트워크를 구축하여, 여러 호스트에 걸친 네트워크를 형성합니다.)
      • 각 호스트가 외부 네트워크와 통신할 수 있으며, 오버레이 네트워크를 통해 다른 호스트의 컨테이너와도 통신할 수 있습니다. 이는 다중 호스트 환경에서 마이크로서비스 아키텍처를 구축할 때 사용됩니다. 
        docker network create -d overlay my_overlay_network
        docker service create --name my_service --network my_overlay_network nginx

     

    1-3. 구조 및 내역

    좌 : Default Network(docker0)

     

    Docker Network는 위 그림과 같은 구조를 가집니다.

     

    각각의 요소들은 호스트(192.168.0)와 컨테이너(172.17.0) 사이의 네트워킹을 관리하는 요소로 연결 및 통신을 위한 역할은 다음과 같습니다.

    • (Host) eth0
      : Host Ethernet Interface는 쉽게 표현하면 (물리적) 랜카드입니다. eth0의 0은 Interface의 인덱스(번호)를 나타내며, 기본적으로 eth0에서 순차적으로 증가합니다. 이는 컴퓨터의 네트워크 연결을 나타내며, 호스트 시스템의 네트워크 스택과 직접 상호 작용합니다.

      호스트 eth0은 호스트 시스템에서 수신된 모든 네트워크 패킷을 처리하고, 이를 컨테이너로 전달하거나 컨테이너에서 수신된 패킷을 호스트 시스템으로 라우팅 합니다.

    • Bridge
      : 앞서 설명한 호스트와 컨테이너 간의 통신을 중개하는 역할을 합니다. 즉, 브리지는 네트워크의 물리적인 및 가상적인 부분을 연결하는 네트워크 장치입니다. 도커에서는 도커 엔진이 자동으로 생성하는 가상 스위치로 사용됩니다.

      도커 브리지는 호스트의 네트워크와 컨테이너 간의 통신을 중개합니다. 브리지는 물리적 네트워크와 가상 이더넷 인터페이스인 veth 쌍을 연결하여 통신을 관리합니다.

    • veth
      : Virtual Ethernet Interface로 일종의 가상 랜카드 역할을 수행합니다. veth는 컨테이너와 브리지 사이의 가상 이더넷(Container eth0) 연결입니다. veth 쌍은 컨테이너 내부와 호스트의 네트워크 네임스페이스 사이에 생성됩니다.

      각 컨테이너가 생성될 때, Bridge(docker0)와 바인딩 - Container 내부 eth0라는 이름으로 veth인터페이스와 연결됩니다. 즉, veth 쌍 중 하나는 컨테이너 내부의 네트워크 네임스페이스에 연결되고, 다른 하나는 브리지에 연결됩니다. 이를 통해 컨테이너와 호스트 네트워크 사이에 패킷이 전달되어 컨테이너는 외부와 통신할 수 있습니다.
      (이는 일반적인 NI와 달리, 패킷을 전달받으면 자신에게 연결된 다른 NI로 패킷을 보내는 식으로 동작하기에 항상 쌍으로 생성해줘야 합니다.)

    • (Container) eth0
      : 컨테이너 eth0은 컨테이너 내부의 네트워크 인터페이스를 나타냅니다. 컨테이너 내에서 실행되는 응용 프로그램은 이 인터페이스를 사용하여 네트워크 통신을 합니다.

      컨테이너 eth0은 해당 컨테이너의 네트워크 스택과 연결되어 있으며, 컨테이너 내부의 IP 주소 및 네트워크 설정을 관리합니다.

     

    우측에는 my_bridge라는 새로운 Bridge를 만들었으며, Defaut 대역이 아닌 10.0.~ 대역을 사용하고 있습니다. 

    이는 docker0 Bridge와는 서로 다른 대역을 가지고 있으며, 

     

    1-4. Sample

    : 앞서 설명한 내용들을 바탕으로 실제 Docker Network 의 구성 사항들을 살펴보고, 몇 가지 구조 기반 테스트를 해보겠습니다.

     

    • Host 정보 확인
      : Host 시스템의 NIC(네트워크 인터페이스) 정보를 확인해 보겠습니다.

      아래 Host의 네트워크 인터페이스를 나타내는 eth0 경우,
      Azure VNet(192.168.11.0/24)의 서브넷 192.168.11.0/28(192.168.11.0 - 192.168.11.15) 대역을 사용하고 있으며, 해당 VM은 Private IP(192.168.11.4) 를 할당받은 상태입니다.

      Docker 설치 시, Default 네트워크인 Bridge(docker0)의 경우, 172.17.0.0/24 대역을 사용하고 있음을 확인할 수 있습니다.

     

    • Docker Network 확인
      : Docker 엔진에서 사용 가능한 모든 네트워크 설정을 확인해 보겠습니다.

      이후, 아래 명령어는 통해 bridge의 상제 네트워크 정보를 확인해 보겠습니다.
      docker network inspect <network_name_or_id>


      Default 네트워크 드라이버이기에 별 다른 설정 없이 컨테이너를 생성한다면, 해당 Bridge(docker0)를 사용합니다. 또한, 서브넷 대역(172.17.0.0/16) 의 IP를 실행되는 컨테이너에 순차적으로 할당합니다.

     

    • 이전 배포된 컨테이너의 네트워크 확인
      : #1에서 마지막에 배포한 컨테이너의 네트워크 정보를 확인해 보겠습니다.
      # 현재 컨테이너 실행 중인지 확인
      docker ps 
      
      # 만약, 아무것도 나오지 않는다면, 현재 실행중인 상태가 아니므로
      # 아래 명령어를 통해 전체 컨테이너 목록 확인
      docker ps -a
      
      # status가 'exited'라면, 아래 명령어 사용하며 (재)실행
      docker (re)start <container_id_or_name>

      우선, 아래 명령어를 통해서 네트워크 설정을 확인해 보면 다음과 같습니다.
      # 출력값 중, NetworkSettings 만 확인
      docker inspect <container_id_or_name>


      실제 실행 중인 컨테이너 내부에서 확인한다면 다음과 같습니다.
      # container 접속
      docker exec -it <container_id_or_name> /bin/bash
      
      # ip addr or ifconfig 명령어 사용하여 확인
      # 172.17.0.1인 GW IP로 172.17.0.2부터 순차적으로 컨테이너에 IP 할당

    이렇게, Default 네트워크인 Bridge(docker0) 드라이버를 사용하는 네트워크 구성을 살펴보았습니다.

     

    이어서 TEST 1 & TEST 2 를 통해서 네트워크 구성을 다각화해 보겠습니다.

     

    **TEST 1

    : 이번 테스트는 서로 다른 네트워크를 사용하여 격리된 컨테이너 간의 네트워크 테스트를 진행하고(Step1), Bridge에 veth 연결을 통해 격리된 컨테이너 간 네트워크 연결을 통해 통신이 가능(Step2)하도록 진행해 보겠습니다.

    my_bridge라는 새로운 Bridge 네트워크 드라이버를 생성하고, "10.0.0.0/24" 대역에서 "10.0.0.254" IP를 db 컨테이너에 할당해 보겠습니다. 이후, 해당 Bridge 에 web 컨테이너를 연결하는 과정입니다.

     

    좌 : Step1 우 : Step2

     

    • 네트워크 드라이버 생성
      : 아래 명령어를 통해 my_bridge라는 네트워크 드라이버를 생성합니다.
    docker network create \
      --driver=bridge \
      --subnet=10.0.0.0/24 \
      --gateway=10.0.0.1 \
     my_bridge

     

    이후. db 컨테이너(mariadb 이미지)를 실행하여 네트워크 통신 테스트를 진행해 보겠습니다.

     

    • db 컨테이너(mariadb 이미지)를 생성 및 실행
    # 네트워크(my_bridge)와 IP(10.0.0.254) 지정
    # 'MYSQL_ROOT_PASSWORD' 지정하지 않으면, 배포 및 실행 불가
    docker run -d --name db --network=my_bridge --ip=10.0.0.254 -e MYSQL_ROOT_PASSWORD=tak_password mariadb

     

    • db 컨테이너 네트워크 정보 확인

     

    • web 컨테이너(앞서 구성한 Flask 앱)의 통신 테스트
      : 해당 컨테이너에 할당된 IP는 172.17.0.2 이며, 서로 다른 네트워크로 격리된 컨테이너 간에 통신이 불가합니다.

     

    이제 Step2 로 넘어가 my_bridge라는 네트워크 드라이버에 web 컨테이너를 연결해 보도록 하겠습니다.
    (Bridge 네트워크 드라이버는 여러 컨테이너와 연결이 가능함을 앞서 설명드렸습니다. 이는 실행 중인 컨테이너도 연결이 가능합니다.)

     

    • 네트워크 드라이버와 컨테이너 연결
    docker network connect <docker network ID or Name> <container ID or Name>

     

    • web 컨테이너의 네트워크 정보 확인
      : my_bridge 네트워크 드라이버에 설정한 네트워크 대역(10.0.0.0/24) 에서 순차적으로 IP를 할당받음을 확인(GW IP 10.0.0.1 제외)

     

    • 네트워크 통신 확인
      : (db 컨테이너 > web 컨테이너) 172.17.0.2와 같은 특정 IP을 지정하지 않고, 컨테이너 자체로 ping 테스트

     

     

    Test 1을 통해서, 서로 다른 네트워크 환경을 구성하고 컨테이너의 격리와 연결 여부를 테스트하였습니다.

     

    **TEST 2

    : 이번에는 하나의 네트워크 드라이브에서 2-Tier 애플리케이션을 구성하여 동작 과정 테스트 해보겠습니다.

     

    • 신규 Bridge 생성
      : 특정 네트워크 대역을 위해 생성합니다. 만약, 특정값이 필요하지 않다면, docker-compose.yml 의 네트워크 이름과 driver 종류를 입력하면 임의값으로 생성됩니다. 
    docker network create \
      --driver=bridge \
      --subnet=10.11.12.0/24 \
      --gateway=10.11.12.1 \
     3tier_bridge

     

    • 디렉터리 구조
      : 애플리케이션 배포를 위해 스크립트를 포함한 필요한 디렉터리 구조아 소스 코드 구성을 다음과 같습니다.

      이번 포스팅에서는 소스 코드에 대한 내용보다는 Docker 관련 파일 내용에 집중합니다.

      상세 소스 코드는 제 Github에 확인하실 수 있습니다!!

     

    • docker-compose.yml
      : docker-compose.yml 파일은 여러 도커 컨테이너를 정의하고 관리하는 데 사용되는 YAML 형식의 파일입니다. 이 파일을 사용하면 여러 컨테이너를 손쉽게 설정하고 동시에 시작할 수 있습니다. 
      • Sample
        # Compose 파일의 버전 지정
        # 더 이상 docker-compose 파일에 버전을 명시할 필요가 없음.
        # 만약 명시하다면, 'version' is obsolete 라는 경고 문구 확인 됨.(배포는 정상적으로 진행)
        version: '3.*'  
        
        services:  # 관리할 서비스(컨테이너) 정의
          service_name:  # 서비스 이름
            image: image_name:tag  # 사용할 Docker 이미지
            build:  # 빌드 옵션 (이미지를 직접 빌드할 때 사용)
              context: .  # Dockerfile의 경로
              dockerfile: Dockerfile  # 사용할 Dockerfile의 이름
            ports:  # 호스트와 컨테이너 간의 포트 매핑
              - "host_port:container_port"
            volumes:  # 호스트와 컨테이너 간의 볼륨 매핑
              - host_path:container_path
            environment:  # 환경 변수 설정
              - ENV_VAR_NAME=value
            depends_on:  # 이 서비스가 의존하는 다른 서비스
              - other_service_name
            networks:  # 이 서비스가 연결될 네트워크
              - network_name
        
        networks:  # 네트워크 정의
          network_name:
            driver: bridge  # 네트워크 드라이버 (예: bridge, overlay 등)
        
        volumes:  # 볼륨 정의
          volume_name:
            driver: local  # 볼륨 드라이버 (예: local)


      • 실제 적용한 .yml 파일
        : Flask와 MySQL 를 포함하는 2개 이상의 컨테이너는 배포 시, 유용하게 사용 가능.
        services:
          app:
            container_name: my-flask-app
            build:
              context: ./flask
              dockerfile: Dockerfile
            ports: # 호스트의 5000번 포트를 컨테이너의 5000번 포트에 매핑
              - "5000:5000"
            environment: # .evn라는 환경변수 파일에서 값을 가져옴.
              MYSQL_HOST: ${MYSQL_HOST}
              MYSQL_DATABASE: ${MYSQL_DATABASE}
              MYSQL_USER: ${MYSQL_USER}
              MYSQL_PASSWORD: ${MYSQL_PASSWORD}
            volumes:
              - ./flask/app:/app/app
            depends_on:
              - db
            networks:
              - 2tier_bridge
        
          db:
            container_name: my-flask-app-db
            build:
              context: ./db
              dockerfile: Dockerfile
            environment:
              MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
              MYSQL_DATABASE: ${MYSQL_DATABASE}
              MYSQL_USER: ${MYSQL_USER}
              MYSQL_PASSWORD: ${MYSQL_PASSWORD}
            expose: # 포트를 외부에 공개하지 않고 다른 컨테이너에 노출
              - "3306"
            volumes:
              - ./db/init_db.sql:/docker-entrypoint-initdb.d/init_db.sql
            networks:
              - 2tier_bridge
        
        networks:
          2tier_bridge:
            driver: bridge


    • Dockerfile
      : Dockerfile은 Docker 이미지를 정의하는 파일입니다. Dockerfile을 통해 특정 소프트웨어 환경을 구성하는데 필요한 모든 지침을 Docker에게 제공할 수 있습니다.  
      • Sample
        # 베이스 이미지
        FROM base_image:tag
        
        # 작성자 정보
        LABEL maintainer="your_name@example.com"
        
        # 환경 변수 설정
        ENV ENV_VAR_NAME=value
        
        # 작업 디렉터리 설정
        WORKDIR /path/to/workdir
        
        # 파일 or 디렉터리 복사
        COPY src /dest
        # URL에서 파일을 다운로드하거나 압축 파일 해제
        ADD src /dest
        
        # 패키지 설치(이미지 빌드 시, 실행할 명령어 지정)
        RUN command
        
        # 포트 노출(컨테이너가 수신 대기할 포트 지정)
        EXPOSE port_number
        
        # 기본 실행 명령어 설정(컨테이너가 시작될 때 실행할 기본 명령어 지정)
        CMD ["executable","param1","param2"]
        
        # 컨테이너 실행 시 실행할 명령어(컨테이너가 시작될 때 항상 실행되도록 지정)
        ENTRYPOINT ["executable","param1","param2"]

        *CMD와 ENTRYPOINT의 차이점
        : 'CMD'와 'ENTRYPOINT'  두 지시어 모두, 컨테이너가 시작할 때 실행되는 명령어를 설정하는 사용됩니다.
        다만, ' ENTRYPOINT'의 경우, 항상 실행되기에 추가 인자로 덮어 쓸 수 없습니다.
        'CMD'의 경우, docker run 명령어를 통해 추가 인자를 사용하여 덮어 쓸 수 있습니다.

      • flask
        FROM python:3.9
        
        WORKDIR /app
        
        COPY ./requirements.txt /app/
        COPY ./app /app/app
        
        RUN pip install --no-cache-dir -r requirements.txt
        
        CMD ["flask", "run", "--host=0.0.0.0"]

      • db
        FROM mysql:8.0
        
        # 환경 변수(.env) 설정
        ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        ENV MYSQL_DATABASE=${MYSQL_DATABASE}
        ENV MYSQL_USER=${MYSQL_USER}
        ENV MYSQL_PASSWORD=${MYSQL_PASSWORD}
        
        # 초기화 SQL 파일 추가
        COPY init_db.sql /docker-entrypoint-initdb.d/init_db.sql

    • Docker 실행
    docker compose up -d --build

     

    • 정상적으로 컨테이너 (현재) 실행 여부 확인

     

    • 네트워크 확인

     

    • 애플리케이션 정상 구동 확인
      : 외부 오픈(노출) 포트 5000 접속, Azure의 NSG 인바운드 정책에 5000포트에 대한 인터넷 액세스 추가 필수!

     

    • DB 연동 확인
      : 게시판에 글 작성 후, MySQL 컨테이너 내부에 데이터 기록(저장)되는지 확인

    데이터 기입 후, [Submit] 클릭

     

    • MySQL DB 컨테이너 접속

     

    • 데이터 확인

     

     


    이상으로 "Docker" & "Docker Network" 에 대해 알아보았습니다.

     

    여러분들이 생각하는 의견을 자유롭게 댓글로 남겨주세요:)

     

    다음에도 도움이 되는 자료로 찾아뵙겠습니다!

    728x90
    320x100
    SMALL