「 시작하세요! 도커/쿠버네티스」 학습 정리 - 03장 ~ 04장

toto9602·2024년 1월 15일
0

Docker 공부하기

목록 보기
6/10

용찬호. 「 시작하세요! 도커/쿠버네티스」. 위키북스 를 읽으며, 학습한 내용을 정리하는 글입니다.
→ 본문의 내용은 모두 책의 내용에 대한 직/간접적 인용임을 밝힙니다.

03장. 도커 스웜

3.1 도커 스웜을 사용하는 이유

Q. 하나의 호스트 머신에서 도커 엔진을 구동하다가 CPU, 메모리, 디스크 용량과 같은 자원이 부족하다면?
여러 대의 서버를 클러스터로 만들어 자원을 병렬적으로 확장하는 방법!

  • 다만, 여러 대의 서버를 하나의 자원 풀로 만드는 것은 어려움...
  • 새로운 서버나 컨테이너가 추가됐을 때 이를 발견(Service Discovery), 어떤 서버에 컨테이너를 할당할지에 대한 스케줄러, 로드밸런서, 클러스터 내 서버가 다운됐을 때 고가용성(High Availabiliy) 문제...

→ 이 문제를 해결하는 대표적인 오픈소스 솔루션이,
도커에서 공식 제공하는 도커 스웜(docker swarm)과 스웜 모드(swarm mode)이다!

3.2 스웜 클래식과 도커 스웜 모드

  • 스웜 클래식과 스웜 모드는 여러 개의 도커 서버를 하나의 클러스터로 만들어 컨테이너를 생성하는 여러 기능을 제공!
  • 다양한 전략으로 컨테이너를 특정 서버에 할당할 수 있고, 유동적인 확장도 가능하다.

[ 도커 스웜의 두 가지 종류 ]
1. 도커 버전 1.6부터 사용 가능한 컨테이너로서의 스웜
→ 이하에서 스웜 클래식 이라고 함!
2. 도커 버전 1.12 이후부터 사용 가능한 도커 스웜 모드(Swarm Mode)
→ 이하에서 스웜 모드라고 함!

[ 스웜 클래식과 스웜 모드의 차이 ]
< 목적 >

  • 스웜 클래식은 여러 대의 도커 서버를 하나의 지점에서 사용하도록 단일 접근점을 제공
  • 스웜 모드는 마이크로서비스 아키텍처의 컨테이너를 다루기 위한 클러스터링 기능에 초점!
  • 스웜 클래식은 docker run, docker ps 등 일반적인 도커 명령어와 도커 API로 클러스터의 서버를 제어, 관리할 수 있는 기능 제공
  • 스웜 모드는 같은 컨테이너를 동시에 여러 개 생성해 필요에 따라 컨테이너 수를 유동적으로 조절할 수 있고, 컨테이너로의 연결을 분산하는 로드밸런싱 기능을 자체 제공!

스웜 모드가 서비스 확장성, 안정성 등 여러 측면에서 스웜 클래식보다 뛰어나므로 일반적으로 스웜 모드를 더 많이 사용!

cf. 스웜 클래식은, 각종 정보를 저장하고 동기화하는 분산 코디네이터, 클러스터 내의 서버를 관리 및 제어하는 매니저, 각 서버를 제어하는 에이전트 등이 모두 별도로 실행되어야 함!
스웜 모드는, 이런 클러스터링을 위한 도구가 도커 엔진 자체에 내장!

3.3 스웜 모드

docker info | grep Swarm
Swarm : inactive

→ 지금까지의 도커 엔진 사용은 모두 단일 도커 서버에서 이루어진 것이므로, 현재 Swarm 모드는 비활성이다.

3.3.1 도커 스웜 모드의 구조

  • 스웜 모드는 매니저 노드와 워커(Worker) 노드로 구성된다.

    • 워커 노드는 실제로 컨테이너가 생성, 관리되는 도커 서버
    • 매니저 노드는 워커 노드를 관리하기 위한 도커 서버
    • 다만, 매니저 노드에도 컨테이너가 생성될 수 있음. 즉, 매니저 노드는 기본적으로 워커 노드의 역할을 포함!
  • 매니저 노드는 1개 이상 있어야 하지만, 워커 노드는 없을 수도 있음.

    • 이는 매니저 노드가 워커 노드 역할을 포함하여, 매니저 노드만으로 스웜 클러스터 구성이 가능하기 때문.
    • 그러나, 일반적으로 둘을 구분하는 것을 권장.
  • 스웜 매니저는 홀수 개로 구성하는 것을 권장

    • 스웜 모드는 매니저 노드의 절반 이상에 장애가 생길 때, 클러스터 운영을 중단하는데, 이때 매니저 노드의 개수를 홀수로 구성하면 매니저 노드의 장애를 더 허용 가능-!

3.3.2 도커 스웜 모드 클러스터 구축

[ Example ]

swarm-manager 192.168.0.100
swarm-worker1 192.168.0.101
swarm-worker2 192.168.0.102
  1. docker swarm init 명령어를 입력해, 매니저 역할을 할 서버에서 스웜 클러스터를 시작.
  • --advertise-addr에는 다른 도커 서버가 매니저 노드에 접근하기 위한 IP 주소 (즉, 매니저 노드의 IP 주소)를 입력!
docker swarm init --advertise-addr ${SWARM_MANAGER_ADDR}

[ P. S ]

  • 스웜 매니저는 기본적으로 2377 포트 사용
  • 노드 사이의 통신에 7946/tcp, 7946/udp 포트를, 스웜이 사용하는 네트워크인 ingress 오버레이 네트워크에 4789/tcp, 4789/udp 포트를 사용
  • 스웜 클러스터 구성 전에 위 포트를 각 호스트 머신에서 열어두어야 함!
  1. 워커 노드를 추가하기 위한 토큰을 사용해 새로운 워커 노드를 추가
    → 워커 노드로 사용할 각 서버에서 아래 명령어를 입력!
docker swarm join \
	--token ${매니저_노드에서_확인한_TOKEN} \ 
    ${매니저_노드_IP}:2377

[ 매니저 노드 추가 ]

docker swarm join-token manager

위 명령어로 확인!

[ 워커 노드 추가 ]

docker swarm join-token worker

[ 워커 노드 삭제 ]
해당 워커 노드에서 아래 입력

docker swarm leave

→ leave 명령어로 스웜 모드를 해제하면, 매니저 모드는 해당 워커 노드의 상태를 Down을 인지할 뿐, 자동으로 워커 노드를 삭제하지 않음!
→ 매니저 노드에서 docker node rm 명령어를 사용해 해당 워커 노드를 지워준다.

3.3.3 스웜 모드 서비스

[ 3.3.3.1 스웜 모드 서비스 개념 ]

  • 기존에 사용하던 도커 명령어의 제어 단위는 컨테이너
  • 스웜 모드에서는, 제어하는 단위가 컨테이너가 아닌 서비스(Service)이다.
    • 서비스는, 같은 이미지에서 생성된 컨테이너의 집합.
    • 서비스를 제어하면 해당 서비스 내의 컨테이너에 같은 명령어 수행됨
    • 서비스 내에 컨테이너는 1개 이상 존재할 수 있으며, 컨테이너들은 각 워커 노드와 매니저 노드에 할당된다.
    • 이러한 컨테이너들을 태스크(Task)라고 함!

ex) ubuntu:14.04 이미지로 서비스 생성 → 컨테이너 수 3개로 설정

  • 스웜 스케줄러는 서비스의 정의에 따라 컨테이너를 할당할 노드를 선정하고, 해당 노드에 컨테이너를 분산하여 할당
  • 이처럼, 함께 생성된 컨테이너를 레플리카(replica)라고 하며, 서비스에 설정된 레플리카의 수만큼의 컨테이너가 스웜 클러스터에 존재해야 한다.
  • 스웜은 서비스의 컨테이너들의 상태를 확인하다가,
    서비스 내에 정의된 레플리카의 수만큼의 컨테이너가 스웜 클러스터에 존재하지 않으면, 새로운 컨테이너 레플리카를 생성한다.

[ 3.3.3.2 서비스 생성 ]

서비스를 제어하는 도커 명령어는 전부 매니저 노드에서만 사용 가능!

docker service create \ 
	ubuntu:14.04 \
    /bin/sh -c "while true; do echo hello world; sleep 1; done"

→ -d 옵션으 사용해 동작 가능한 이미지로, 서비스 생성

docker service ls

→ 서비스 목록 확인!

[ nginx 웹 서버 서비스 생성하기 ]

docker service create --name myweb \
	--replicas 2 \ 
    -p 80:80 \
 nginx
  • 여기서의 -p 옵션은 스웜 클러스터 자체에 포트를 개방한 셈
  • 스웜 클러스터 내의 어떠한 노드로 접근해도, 이 웹 서버에 접근할 수 있다!
docker service scale myweb=4

→ 이렇게 하면, 3개 중 하나의 노드(swarm-worker2)에 2개의 컨테이너가 할당된다.
→ 실제로는 각 노드의 80번 포트로 들어온 요청을 4개 컨테이너 중 1개로 리다이렉트하기에, 문제가 없음!

[ global 서비스 생성하기 ]

  • 서비스는 일반적인 복제 모드(replicated)와, 글로벌(global) 모드로 나뉜다.
    • 복제 모드는 레플리카 셋의 수를 정의해 그만큼의 컨테이너를 생성하는 모드
    • 글로벌 서비스는 스웜 클러스터 내에서 사용 가능한 모든 노드에 컨테이너를 반드시 하나씩 생성한다.
    • 스웜 클러스터를 모니터링하기 위한 에이전트 컨테이너 등을 생성할 때 유용!
docker service create --name global_web

[ 3.3.3.4 서비스 롤링 업데이트 ]

  • 스웜 모드는 롤링 업데이트를 자체적으로 지원
docker service create --name myweb2 \
	--replicas 3 \
 nginx:1.10
 
docker service update \
--image nginx:1.11 \
myweb2
  • 기존 컨테이너는 NAME이 _${NAME}으로 변경되며 Shutdown되고, 새 컨테이너가 생긴다.
docker service create \
--replicas 4 \
--name myweb3 \ 
--update-delay 10s \
--update parallelism 2 \
nginx:1.10

[ 3.3.3.5 서비스 컨테이너에 설정 정보 전달하기 : config, secret ]

  • secret은 비밀번호나 SSH 키, 인증서 키와 같이 보안에 민감한 데이터를 전송하기 위해, config는 nginx나 레지스트리 설정 파일과 같이 암호화할 필요가 없는 설정값들에 쓰인다.
  • 이 두 기능은 스웜 모드에서만 사용 가능!

[ secret 사용하기 ]

echo 1q2w3e4r | docker secret create my_mysql_password - 

docker secret ls

→ 생성된 secret을 docker secret inspect my_mysql_password로 조회해도 실제 값 확인은 불가!
→ 매니저 노드에 암호화된 상태로 저장되는 값으로, 서비스 컨테이너가 삭제될 경우 함께 삭제된다.

docker service create \
	--name mysql \
    --replicas 1 \
    --secret source=my_mysql_password, target=mysql_root_password \ 
    --secret source=my_mysql_password, target=mysql_password \ 
    ...
    ...
mysql:5.7
  • --secret 옵션을 통해 컨테이너로 공유된 값은 기본적으로 컨테이너 내부의 /run/secrets 디렉터리에 마운트된다.
  • 해당 케이스에서는 /run/secrets 디렉터리에 2개 target 이름의 파일이 각각 존재.

[ config 사용하기 ]

docker config create registry-config config.yml

→ config는 입력된 값을 base64로 인코딩한 뒤 저장하여, base64로 디코딩하면 원래 값을 확인 가능!

docker service dreate --name yml_registry -p 5000:5000 \
	--config source=registry-config,target=/etc/docker/registry/config.yml \
 registry:2.6

[ 3.3.3.6 도커 스웜 네트워크 ]

  • 스웜 모드는, 여러 개의 도커 엔진에 같은 컨테이너를 분산해서 할당하기 때문에 각 도커 데몬의 네트워크가 하나로 묶인, 네트워크 풀이 필요하다.
  • 또한, 서비스를 외부로 노출했을 때, 어느 노드로 접근하더라도 해당 서비스 컨테이너에 접근할 수 있게 라우팅 기능이 필요!

→ 스웜 모드가 자체 지원하는 네트워크 드라이버를 통해 사용 가능!

[ ingress 네트워크 ]

  • 스웜 클러스터를 생성하면 자동 등록되는 네트워크
  • ingress 네트워크는 어떤 스웜 노드에 접근하더라도 서비스 내의 컨테이너에 접근할 수 있게 설정하는 라우팅 메시를 구성하고,
    서비스 내의 컨테이너에 대한 접근을 라운드 로빈 방식으로 분산하는 로드 밸런싱을 담당

[ 오버레이 네트워크 ]

컨테이너 내부에서 ifconfig 실행 결과

docker exec ${CONTAINER_ID} ifconfig
  • 컨테이너마다 eth0, eth1, lo가 할당됐고, eth0이 ingress 네트워크와 연결된 네트워크 카드이다.

  • swarm-manager에서 생성된 컨테이너와 swarm-worker1에서 생성된 컨테이너는 IP 주소가 차례로 할당됨!

  • ingress 네트워크는 오버레이 네트워크 드라이버를 사용함.

  • 이는 여러 개의 도커 데몬을 하나의 네트워크 풀로 만드는 네트워크 가상화 기술의 하나로, 이를 통해 여러 도커 데몬에 존재하는 컨테이너가 통신 가능!

  • 즉, 여러 스웜 노드에 할당된 컨테이너는 오버레이 네트워크의 서브넷에 해당하는 IP 대역을 할당받고, 이를 통해 통신한다.

[ docker_gwbridge 네트워크 ]

  • 오버레이 네트워크를 사용하지 않는 컨테이너는 기본적으로 존재하는 bridge 네트워크를 사용해 외부와 연결
  • 그러나 ingress를 포함한 모든 오버레이 네트워크는 이와 다른 bridge 네트워크인 docker_gwbridge 네트워크와 함께 사용됨.

[ 3.3.3.6 서비스 디스커버리 ]

  • 같은 컨테이너를 여러 개 만들어 사용할 때 쟁점이 되는 부분 중 하나는,
    새로 생성된 컨테이너의 발견(service discovery) 혹은 없어진 컨테이너의 감지다.
  • 스웜 모드는 이를 자체적으로 지원

ex) 2개의 컨테이너 레플리카를 갖는 B 서비스가 있고, A 서비스는 이 B 서비스의 컨테이너를 사용하는 경우

→ 스웜 모드에서는 B 라는 이름으로 서비스 B 컨테이너에 모두 접근!
→ 컨테이너의 IP 주소, 새롭게 생성된 사실 등을 몰라도, B 이름만 알면 된다.

서비스 이름은 어떻게 각 컨테이너의 IP로 변환되는가?

  • 호스트 이름(ex. server)은 서비스의 VIP(Virtual IP)를 가진다.
  • 즉, 다른 서비스의 컨테이너가 server 호스트 이름으로 접근하면 실제로는 VIP로 요청을 전송
  • 그리고 이 IP는 컨테이너의 네트워크 네임스페이스 내부에서, 실제 server 서비스의 컨테이너의 IP로 포워딩된다.

3.3.4 도커 스웜 모드 노드 다루기

[ 3.3.4.1 노드 AVAILABILITY 변경하기 ]

  • 일반적으로 매니저 등의 마스터 노드는 부하를 받지 않도록 서비스를 할당하지 않게 하는 것이 좋다.
docker node update \
--availability ${AVAILABILITY} \
${HOSTNAME}

[ Active ]

  • 새로운 노드가 스웜 클러스터에 추가되면 기본적으로 설정되는 상태로, 노드가 서비스의 컨테이너를 할당받을 수 있음을 의미

[ Drain ]

  • 노드를 Drain 상태로 변경하면 스웜 매니저의 스케줄러는 컨테이너를 해당 노드에 할당하지 않는다.
  • 일반적으로 매니저 노드에 설정하는 상태이나, 노드에 문제가 생겨 일시적으로 사용하지 않는 상태로 설정해야 할 때도 사용!
  • 해당 노드에서 실행 중이던 서비스의 컨테이너는 전부 중지되고, Active 상태의 노드로 재할당됨!

[ Pause ]

  • 서비스의 컨테이너를 더는 할당받지 않는 상태(Drain과 유사)
  • 다만, 실행 중인 컨테이너가 중지되지는 않는다.

04. 도커 컴포즈

4.1 도커 컴포즈를 사용하는 이유

도커 컴포즈는 컨테이너를 이용한 서비스의 개발과 CI를 위해 여러 개의 컨테이너를 하나의 프로젝트로서 다룰 수 있는 작업 환경을 제공

4.3 도커 컴포즈 사용

4.3.1 도커 컴포즈 기본 사용법

[ 4.3.1.1 docker-compose.yml 작성과 활용 ]

  • 어떠한 설정도 하지 않으면 도커 컴포즈는 현재 디렉터리의 docker-compose.yml 파일을 읽어, 로컬의 도커 엔진에게 컨테이너 생성을 요청

[ 4.3.1.2 도커 컴포즈의 프로젝트, 서비스, 컨테이너 ]

  • 컨테이너 이름의 생성 규칙은 일반적으로 아래와 같다.
    • [프로젝트 이름]_[서비스 이름]_[서비스 내에서 컨테이너의 번호]
  • docker-compose scale 명령어로 ubuntu_mysql_2, ubuntu_mysql_3 을 생성할 수 있음!
  • 도커 컴포즈는 기본적으로 현재 디렉터리의 이름으로 된 프로젝트를 제어한다.
    • 다만, docker-compose -p 옵션에 프로젝트의 이름을 사용해, 제어할 프로젝트의 이름을 명시할 수 있다.

4.3.2 도커 컴포즈 활용

[ 4.3.2.1 YAML 파일 작성 ]

  • YAML 파일은 크게 버전 정의, 서비스 정의, 볼륨 정의, 네트워크 정의의 4가지 항목으로 구성된다.
  • 가장 많이 사용하는 것은 서비스 정의이며, 볼륨 정의와 네트워크 정의는 서비스로 생성된 컨테이너에 선택적으로 사용된다.

[ 주요 정의 ]

< 서비스 정의 >

  • image : 서비스의 컨테이너를 생성할 때 쓰일 이미지의 이름을 설정
  • links : docker run --link와 동일, 다른 서비스에 서비스명만으로 접근할 수 있도록 설정
  • command : 컨테이너가 실행될 때 수행할 명령어를 설정, Dockerfile의 RUN과 같은 배열 형태로도 사용이 가능!
  • depends_on : 특정 컨테이너에 대한 의존관계를 나타내며, 이 항목에 명시된 컨테이너가 먼저 생성되고 실행된다.
    • linksdepends_on과 같이 컨테이너의 생성 순서와 실행 순서를 정의하지만, depends_on은 서비스 이름으로만 접근 가능하다는 차이!
  • ports : docker run 명령어의 -p와 같으며, 서비스의 컨테이너를 개방할 포트를 설정.
    • 그러나, 단일 호스트 환경에서 80:80과 같이 호스트의 특정 포트를 서비스의 컨테이너에 연결하면 docker-compose scale 명령어로 서비스의 컨테이너의 수를 늘릴 수는 없다.
  • extends : 다른 YAML 파일이나 현재 YAML 파일에서 서비스 속성을 상속받게 설정

< 네트워크 정의 >

  • driver : 도커 컴포즈는 생성된 컨테이너를 위해 기본적으로 브리지 타입의 네트워크를 생성한다.
    • 다만, YAML 파일에서 driver 항목을 정의하여 서비스의 컨테이너가 브리지 네트워크가 아닌 다른 네트워크를 사용하도록 할 수 있음.
  • ipam : IPAM(IP Address Manager)를 위해 사용할 수 있는 옵션으로서 subnet, ip 범위 등을 설정
  • external : YAML 파일을 통해 프로젝트 생성할 때마다 네트워크를 생성하는 것이 아닌, 기존의 네트워크를 사용하도록 설정
    • 사용하려는 외부 네트워크의 이름을 하위 항목으로 입력한 뒤 external의 값을 true로 설정합니다.
      ex)
    services:
    	web:
      	image: alicek106/composetest:web
          networks:
          	- alicek106_network
     networks:
     	alicek106_network:
      	external: true

< 볼륨 정의 >

  • driver : 볼륨을 생성할 때 사용될 드라이버를 설정합니다. 어떠한 설정도 하지 않으면 local로 설정되며, 사용하는 드라이버에 따라 변경해야 함.
volumes:
	driver: flocker
    	driver_opts:
        	opt: "1"
            opt2 : 2
  • external : 도커 컴포즈는 YAML 파일에서 volume, volume-from 옵션 등을 사용하면 프로젝트마다 볼륨을 생성
    • 이때 external 옵션을 설정하면 볼륨을 프로젝트 생성 시마다 생성하지 않고, 기존 볼륨을 사용하도록 설정

< YAML 파일 검증하기 >

  • YAML 파일을 작성할 때 오타 검사, 파일 포맷이 적절한지 검사하려면 docker-compose config 명령어 사용!

[ 4.3.2.2 도커 컴포즈 네트워크 ]

  • YAML 파일에 네트워크 항목을 정의하지 않으면 도커 컴포즈는 프로젝트별로 브리지 타입의 네트워크를 생성합니다.
    • 생성된 네트워크의 이름은 {프로젝트 이름}_default로 설정
  • 서비스 내의 컨테이너는 --net-alias가 서비스의 이름을 갖도록 자동 설정되므로, 이 네트워크에 속한 컨테이너는 서비스의 이름으로 서비스 내의 컨테이너에 접근할 수 있다.
    • ex. web 서비스와 mysql 서비스가 각기 존재할 때 web 서비스의 컨테이너가 mysql이라는 호스트 이름으로 접근하면 mysql 서비스의 컨테이너 중 하나의 IP로 변환(resolve)된다.
    • 그리고, 컨테이너가 여러 개 존재할 경우 라운드 로빈으로 연결을 분산!

[ 4.3.2.3 도커 스웜 모드와 함께 사용하기 ]

  • 도커 엔진 1.13 버전에, 스웜 모드와 함께 사용되는 개념인 스택(Stack)이 도입
  • 스택은 YAML 파일에서 생성된 컨테이너의 묶음으로, YAML 파일로 스택을 생성하면 YAML 파일에 정의된 서비스가 스웜 모드의 클러스터에서 일괄 생성된다.
    • 즉, YAML 파일에 정의된 서비스가 스웜 모드의 서비스로 변환된 것!
    • 다만, 스택은 도커 컴포즈에 의해서 생성된 것이 아니라 스웜 모드 클러스트의 매니저에 의해 생성된 것으로, docker stack으로 제어해야 한다.
docker stack deploy -c docker-compose.yml mystack
// 스택을 생성
  • 도커 컴포즈의 네트워크와 다르게, 스택의 네트워크는 기본적으로 오버레이 네트워크 속성을 가지며 스웜 클러스터에서 사용하도록 설정
    • --attachable 옵션이 설정되지 않기 때문에 일반 컨테이너는 이 네트워크를 사용할 수 없음
profile
주니어 백엔드 개발자입니다! 조용한 시간에 읽고 쓰는 것을 좋아합니다 :)

0개의 댓글