반응형

0. Intro


지금까지 Docker 사용은?

보통 Docker를 이용해 웹 프로젝트를 배포할 때, 다음과 같은 과정을 진행한다.

  1. Docker File을 작성하고, 이를 통해 Docker Image를 생상한다.
  2. Docker Image를 Docker Hub나 외부 Docker Registry에 이미지를 등록한다.
  3. 원하는 서버에서 docker run이나 docker-compose up 명령어를 통해 컨테이너를 실행한다.

하지만 이 방법만으로는 서비스를 안전하게 운영하기 쉽지않다.

 

왜냐하면 서비스를 업데이트 하는 과정에서 무중단으로 배포하지 못할 뿐더러, 사용자가 많아지게 되면 서버 과부하로 정상적인 서비스를 제공하지 못하기 때문이다.

 

이러한 한계를 극복하기 위해 컨테이너 오케스트레이션 도구가 필요하며, Docker Swarm은 그 해결책 중 하나이다.

 

 

 

1. Container Orchestration


여기서 컨테이너 오케스트레이션은 다수의 컨테이너를 자동으로 배포, 확장, 관리할 수 있도록 도와주는 기술이며 다음과 같은 역할을 한다.

 

1️⃣스케쥴링: 컨테이너를 실행할 최적의 노드를 자동으로 선택하여 배치한다. 이를 통해 서버 자원을 효율적으로 활용하고 가용성을 높인다.

 

2️⃣클러스터링: 여러 서버를 하나의 클러스터로 묶어 관리함으로써, 분산 환경에서 자원을 통합적으로 운영할 수 있다.

 

3️⃣로드 밸런싱: 클러스터 내 여러 노드에 배포된 컨테이너로 들어오는 트래픽을 분산하여 리소스 사용을 최적화한다.

 

4️⃣서비스 디스커버리: 컨테이너의 IP 주소, 포트, 서비스 이름 등을 자동으로 관리하여, 서로 다른 컨테이너나 서비스 간의 연결을 쉽게 설정할 수 있도록 지원한다.

 

5️⃣모니터링 및 로깅: 각 컨테이너와 노드의 상태, 성능, 로그 정보를 실시간으로 수집 및 분석한다.

 

이 외에도 자동 스케일링, 롤아웃/롤백, 자동 복구, 보안 및 네트워크 관리 등 다양한 기능을 포함하여, 대규모 컨테이너 환경을 안정적이고 효율적으로 운영할 수 있도록 돕는다.

 

대표적인 컨테이너 오케스트레이션 도구로 Docker Swarm, Kubernetes, AWS EKS 등이 있다.

 

 

 

2. Docker Swarm 선택 이유


쿠버네티스는 컨테이너 오케스트레이션 도구로서 컨테이너 기반 인프라 시장에서 사실상의 표준으로 자리 잡았다.

 

그렇다면 왜 Docker Swarm을 선택하는가?

 

2.1 간단한 설치와 설정

쿠버네티스 만큼은 아니더라도, 여러 대의 호스트로 구성된 중소 규모의 클러스터에서 컨테이너 기반 애플리케이션 구동을 제어하기에 충분한 기능을 갖추고 있다. 도커 엔진(Docker Engine)이 설치된 환경이라면 별도의 구축 비용 없이 스웜 모드(Swarm Mode)를 활성화하는 것만으로 시작할 수 있다.

 

 

2.2 Docker CLI 친화적

도커 컴포즈(Docker Compose)를 사용해 본 사람이라면 도커 스웜(Docker Swarm)의 스택(Stack)을 이용한 애플리케이션 운영에 곧바로 적응할 수 있다.

 

 

2.3 단일 노드 클러스터부터 다중 서버 클러스터까지

도커 데스크탑(Docker Desktop)으로도 클러스터 관리와 배포가 모두 가능한 단일 노드 클러스터를 바로 만들 수 있다.

따라서 최소한의 자원으로 컨테이너 오케스트레이션 환경을 만들어 시험해볼 수 있다는 장점이 있다.

 

 

 

3. Docker Swarm 구성 요소 및 용어


 

3.1 노드(Node)

클러스터를 구성하는 개별 도커 서버를 의미하며 매니저 노드 (Manager Node) / 워커 노드 (Worker Node)가 있다.

 

  • 매니저 노드(Manager Node)

클러스터 관리와 컨테이너 오케스트레이션을 담당한다. 쿠버네티스의 마스터 노드(Master Node)와 같은 역할이라고 할 수 있다. 매니저 노드는 필요에 따라 워커 노드의 역할도 겸할 수 있지만, 클러스터 관리와 스케줄링 업무가 집중되므로 보통 별도의 노드로 구성하는 것이 좋다.

 

  • 워커 노드(Worker Node)

컨테이너 기반 서비스(Service)들이 실제 구동되는 노드를 의미한다. 쿠버네티스와 다른 점이 있다면, Docker Swarm에서는 매니저 노드(Manager Node)도 기본적으로 워커 노드(Worker Node)의 역할을 같이 수행할 수 있다는 것이다. 물론 스케줄링을 임의로 막는 것도 가능하다.

 

 

 

3.2 서비스(Service)

도커 스웜에서의 기본적인 배포 단위로 노드에서 수행하고자 하는 작업들을 정의해놓은 것이다. 클러스터 안에서 구동시킬 컨테이너 묶음을 정의한 객체라고 할 수 있다. 하나의 서비스는 하나의 이미지를 기반으로 구동되며, 서비스 생성 시 지정한 설정에 따라, Swarm 매니저는 해당 서비스를 여러 노드에 분산시켜 배포한다.

 

 

 

3.3 스택(Stack)

하나 이상의 서비스(Service)로 구성된 다중 컨테이너 애플리케이션 묶음을 의미한다. 도커 컴포즈(Docker Compose)와 유사한 양식의 YAML 파일로 스택 배포를 진행한다.

 

 

 

3.4 태스크(Task)

클러스터를 통해 서비스를 구동시킬 때, 도커 스웜은 해당 서비스의 요구 사항에 맞춰 실제 마이크로서비스가 동작할 도커 컨테이너를 구성하여 노드에 분배하며 이것을 태스크(Task)라고 한다. 하나의 서비스는 지정된 복제본(replica) 수에 따라 여러 개의 태스크를 가질 수 있으며, 각각의 태스크에는 하나씩의 컨테이너가 포함된다.

 

다시 말해 서비스가 실행될 때 매니저 노드가 할당하는 단위 작업으로, 하나의 태스크는 하나의 컨테이너 인스턴스를 의미하며, 서비스의 레플리카 수 만큼 생성된다.

 

 

 

3.5 레플리카(Replica)

서비스 내에서 실행되는 동일한 컨테이너(태스크)의 개수를 의미하며, 애플리케이션의 부하를 분산시키고, 고가용성을 확보하기 위해 여러 복제본을 실행한다. 예를 들어, 서비스가 3개의 레플리카를 지정하면 매니저 노드는 가능한 워커 노드에 3개의 동일한 태스크를 스케줄링하여 배포한다.

 

 

 

3.6 네트워크(Ingress Network)

Ingress 네트워크는 swarm cluster를 생성하면 자동으로 생성되는 네트워크로 swarm 모드일 때만 유효하다. docker network ls 명령어를 통해 네트워크 목록을 확인해보면 Ingress 네트워크의 SCOPEswarm으로 설정되 있는 것을 볼 수 있다.

bash

$ docker network ls
NETWORK ID     NAME              DRIVER    SCOPE
ff375eddfda6   bridge            bridge    local
5cba1cb002ce   host              host      local
dqjjhx24frzq   ingress           overlay   swarm

Ingress 네트워크는 각 노드에 내장되어있는 로드 밸런서를 통해 서비스내의 컨테이너들 간에 라운드 로빈(round-robin) 방식으로 로드 밸런싱을 수행하며,

 

외부에서 특정 포트로 들어온 요청을 클러스터 내의 적절한 컨테이너(태스크)로 전달하는 라우팅 메시(Routing Mesh) 기능과 연계되어 동작한다.

 

 

3.7 포트(Port)

  • 2377/tcp : 클러스터 관리에 사용되는 포트다.
  • 7946/tcp, 7946/udp : 노드 간 통신에 사용된다.
  • 4789/udp : 클러스터에서 사용되는 Ingress 오버레이 네트워크 트래픽에 사용된다.

 

 

 

4. Docker Swarm 사용법 및 주요 명령어


4.1 Swarm 모드 활성화

bash

docker swarm init

스웜 모드 활성화가 완료되면 아래와 같은 안내문이 출력되며, 이 명령어를 실행한 해당 노드는 매니저 노드로 전환된다.

 

bash

Swarm initialized: current node (9yubq203dgcl82o3n2bb2s31w) is now a manager.

To add a worker to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-xxxxxxxxxxxxxxxxxxxx 172.31.14.44:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

다른 서버에서 클러스터에 참여하려면 매니저 노드에서 제공하는 토큰을 사용하면 된다.

 

 

 

4.2 노드 추가 및 관리

bash

// 워커 노드 추가:
docker swarm join --token <worker-token> <manager-ip>:2377

// 매니저 노드 추가:
docker swarm join-token manager
docker swarm join --token <manager-token> <manager-ip>:2377

bash

docker node ls // 전체 노드 보기

docker node inspect {nodeId} // 노드별로 자세한 정보

docker node rm {nodeId} // 노드 삭제 (매니저 노드경우만 가능)

 

 

4.3 서비스 생성 및 관리

 

- 서비스 생성

bash

docker service create \
  --name=chatservice \
  --replicas=3 \
  --publish=8080:8080/tcp \
  --constraint=node.role==manager \
  --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
  --env jasypt.password=jasyptchatkeyexample \
  murphytklee/chat-challenge:0.2
  • -name은 서비스 이름을 지정
  • -replicas는 실행할 컨테이너 수를 지정
  • -publish 옵션은 호스트와 컨테이너 포트를 매핑
  • -constraint는 특정 노드(예, manager 노드)에서만 실행하도록 제한할 수 있다.

 

 

- 서비스 관리

bash

// 전체 서비스 조회: (매니저 노드 경우만 가능)
docker service ls

// 서비스 제거: (매니저 노드 경우만 가능)
docker service rm

// 서비스 로그 보기: -f는 --follow로 실시간으로 계속 보겠다는 뜻 ^C로 나갈 수 있다.
docker service logs -f <service-name> 

// 서비스 업데이트: 실행 중인 서비스를 새로운 이미지로 업데이트 할 수 있다. (무중단 배포를 위해 Rolling Update 방식 지원)
docker service update --image <new-image> --with-registry-auth <service-name>

// 서비스 스케일 조정: 서비스에 할당된 컨테이너 수를 늘리거나 줄일 수 있다.
docker service scale <service-name>=<number-of-replicas>

// 현재 서비스의 컨테이너들과 상세정보 확인 :
docker service ps <service-name>

//실행 중인 모든 컨테이너의 실시간 상태를 보여주며, 각 컨테이너별 CPU 사용률, 메모리 사용량 등을 확인할 수 있다.
docker stats

//그외
docker service inspect <service-name>

 

 

 

4.4 스택 배포

Docker Swarm에서는 docker stack deploy 명령어를 사용하여 Docker Compose와 유사한 방식으로 다중 서비스 애플리케이션을 배포할 수 있다.

bash

docker stack deploy -c docker-compose.yml <stack-name>

 

 

 

4.5 visualizer

Docker Swarm Visualizer는 클러스터 내의 노드, 서비스, 태스크(컨테이너) 등의 상태그래픽 형태로 표시하여 사용자는 각 노드에 배포된 컨테이너의 분포, 서비스 상태, 작업(Task)의 실행 상황 등을 한눈에 파악할 수 있다.

bash

docker service create \
  --name=viz \
  --publish=5000:8080/tcp \
  --constraint=node.role==manager \
  --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
  dockersamples/visualizer

 

컨테이너 실행 후 웹 브라우저에서 http://{호스트 IP}:{Port}으로 접속하면 시각화된 클러스터 정보를 확인할 수 있다.

 

Docker Swarm Visualizer는 주로 데모 및 학습 목적으로 개발되었으며, 실 운영 환경에서는 추가적인 보안 및 성능 검증이 필요하기에 주의하자.

반응형

0. Intro


로컬에서 Spring boot를 실행시키고 Docker로 Kafka 컨테이너를 띄웠을 땐 localhost가 잘 작동했으나, Spring 서버도 Docker 컨테이너로 같이 띄웠을 때, Kafka와 연결이 되지 않았다.

 

Docker Network를 하나로 묶어주면 localhost가 문제없이 동작할 수 있을 줄 알았으나, 컨테이너끼리 통신이 불가능했다. Kafka server.properties의 listener 설정에서 Ubuntu IP주소나 Spring 컨테이너의 IP 주소를 직접 할당해줘도 실패했다.

 

원인을 찾아본 결과, Docker Container 내부에서의 localhost는 해당 컨테이너 자체를 가리킨다. 즉, 서로 다른 컨테이너끼리 localhost를 사용하면 각 컨테이너가 자신의 내부 네트워크를 참조하게 되어 올바른 통신 경로를 찾지 못하게 되는 것이다.

 

결론부터 말하자면 localhost 대신 host.docker.internal을 사용해야 한다.

 

host.docker.internal은 Docker에서 제공하는 '컨테이너가 실행 중인 Host 머신(즉, Docker를 실행 중인 실제 컴퓨터 또는 VM)의 네트워크를 참조할 수 있도록 설정된 DNS 주소'이다. 따라서 host.docker.internal을 사용해야 컨테이너 내부에서 호스트 머신의 네트워크 인터페이스로 접근할 수 있다.

 

Docker Network가 어떻게 구성되어있고, 컨테이너 간 통신이 왜 문제가 되는지 간단하게 알아보고, 이를 해결했던 여러가지 방법들에 대해서 작성해보려 한다.

 

 

 

1. Docker Network 구조


Docker는 각 컨테이너에 독립된 Net namespace를 할당한다. 이로 인해 컨테이너 내부의 localhost는 그 컨테이너 자체를 가리키게 된다.

 

Docker는 기본적으로 컨테이너에 고유한 IP주소를 할당한다. 이 IP주소는 컨테이너가 속한 Docker 네트워크(bridge)를 통해 할당되며 컨테이너 간 통신 시 서로의 IP 주소로 접근하게 된다.

 

Docker는 컨테이너들을 bridge Network(docker0)에 연결한다. 이 네트워크 상에서 컨테이너들은 서로 고유의 IP를 통해 통신할 수 있다.

 

조금 더 이해를 돕기 위해 아래 사진과 함께 eth0, veth, docker0에 대해 알아보자.

eth0

대부분의 Docker Container에서는 기본 네트워크 인터페이스로 eth0를 사용한다.컨테이너가 생성될 때, Docker는 컨테이너의 Network Namespace 내에 eth0 인터페이스를 생성하고, 이 인터페이스에 컨테이너 전용 IP(예: 172.17.x.x)를 할당한다.

 

애플리케이션은 컨테이너 내부에서 eth0를 통해 네트워크에 접근하며, 외부와의 통신한다. 컨테이너의 eth0와 호스트의 네트워크는 veth 쌍 및 docker0 브리지를 통해 연결되어 있다.

 

 

 

veth

veth(Virtual Ethernet)는 두 개의 연관된 가상 이더넷 인터페이스로 Container와 docker0 브리지 간의 연결을 담당한다. 컨테이너를 실행하게 되면 Docker가 자동으로 veth 인터페이스를 쌍(pair)으로 하나는 컨테이너의 네임스페이스에, 다른 하나는 호스트의 네임스페이스에 할당한다.

 

 

 

Docker0

Docker 설치 시 자동으로 생성되는 기본 브리지 네트워크 인터페이스이다.

 

Docker에서는 veth와 호스트 eth0를 바인딩하기 위해 docker0 브릿지를 생성하여 관리한다. 모든 컨테이너의 외부 통신은 docker0를 통해 이루어지며 컨테이너 간 패킷 전달, 컨테이너와 호스트 간의 통신 중개 역할을 한다. 일반적으로 docker0의 IP대역은 172.17.0.0/16 이며, docker0 자체 IP 주소는 172.17.0.1이다.

 

컨테이너 내부에서 데이터를 전송하면, 해당 데이터는 veth 인터페이스를 통해 docker0로 전달되고, docker0는 이를 외부 네트워크(또는 다른 컨테이너)로 라우팅한다.

 

 

 

 

따라서

  1. 컨테이너를 생성하면 컨테이너는 호스트와 통신하기 위한 eth0라는 네트워크 인터페이스를 할당받는다.
  2. 이때 호스트에도 veth(virtual ethernet)라는 네트워크 인터페이스가 할당되고 컨테이너에 할당된 eth0 인터페이스와 통신하게 된다.
  3. 호스트에 할당된 veth 인터페이스는 docker0와 바인딩되고 docker0의 호스트는 eth0 인터페이스와 연결되어 외부로부터 들어온 요청을 처리할 수 있게 된다.

와 같은 데이터 전송의 흐름을 가진다.

 

 

 

2. 해결방법


2.1 localhost → host.docker.internal

java

public static final String KAFKA_BROKER = "host.docker.internal:9092";

producer와 consumer의 브로커 주소를 모두 localhost:9092host.docker.internal:9092 로 설정해주었다.

 

 

 

2.2 Docker-Compose

yml

version: '3'
services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"

    kafka:
    image: wurstmeister/kafka
    container_name: kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: 172.17.0.1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    extra_hosts:
      - "host.docker.internal:host-gateway"
    volumes:
      - /var/run/docker.sock
    depends_on:
      - zookeeper

  chatservice:
    image: murphytklee/chat:0.4
    container_name: chat
    ports:
      - "8080:8080"
    environment:
      - jasypt.password= {jasypt}
    extra_hosts:
      - "host.docker.internal:host-gateway"
  • Kafka env를 docker0 ip로 설정해주었다.
    • KAFKA_ADVERTISED_HOST_NAME: 172.17.0.1
  • 해당 컨테이너에게 extra_host에 대해 알려주어야 한다.
    • extra_hosts: - "host.docker.internal:host-gateway"

 

 

 

2.3 docker run --add-host

Docker Compose로 컨테이너를 실행 시키지 않고 Image로 Run하고 싶다면 다음과 같은 명령어를 사용하여 host-gateway를 설정해줄 수 있다. --add-host host.docker.internal:host-gateway

bash

docker run -i -t -e {jasypt} -p 8080:8080 --name chat --add-host host.docker.internal:host-gateway {이미지}
반응형
반응형

+ Recent posts