Docker Swarm과 AWS ECR을 이용한 Container orchestration 기초개념 및 실습

2019-10-07

.

Data_Engineering_TIL_(20190803)

study program : https://www.fastcampus.co.kr/dev_camp_devb

[학습목표]

  • Container orchestration 개념이해 및 구현실습

[학습기록]

  • 우리는 단일도커 데몬에서 몇개의 프로세스를 띄워봤는데 이런과정을 거치면서 도커에 대해 이해를 했다. 라이트한 버츄얼 머신으로 이해하면 되겠다. 그러면 EC2라는 버츄얼 머신을 사용할때를 생각해보자. 10대 ~ 수십대, 많으면 수백대가 될수도 있을것이다. 우리가 만든 도커 컨테이너를 여러개의 노드(깡통)에서 수십개, 수백개의 컨테이너를 띄우는 것을 어떤식으로 진행하면 되는지를 알아볼것이다.

  • 우리는 웹이라는 도커이미지와 API라는 도커 이미지를 만들어봤다. 여기안에 들어가는 것은 여러개의 이미지를 기반으로 할 수 있다. 그리고 우리가 필요한 소스코드나 프로세스들 이것들을 어떤 베이스 이미지를 사용하고, 어떻게 프로세스를 설치하고, 환경설정을 어떻게할지, 어떻게 띄울 것인지, 소스코드는 어떻게 반영해서 실행할 것인지 등 왠만한 것들은 우리는 이해할 수 있다.

  • 우리가 만든 도커 이미지를 docker run 등의 명령을 내리면 컨테이너 프로세스 하나가 뜰 것이다. 우리는 이제 컨테이너 프로세스를 여러개 띄웠을때 이때를 도커서비스라고 칭할 것이다. 우리가 웹서비스를 구현하기 위해 N대의 컨테이너 프로세스를 띄우면 이를 도커 서비스라고도하고, 웹서비스라고도 한다.

  • 우리는 그래서 웹서비스도 띄워볼거고 동시에 API 서비스도 띄워볼 것이고, 여러개의 컨테이너들을 관리해주는 매니지먼트 서비스를 띄워볼 것이다. 이 서비스들의 그룹을 통상적으로 docker stack이라고 칭한다.

  • 이 스텍이 어떤개념으로 다루어지냐면 예를들어서 ngrinder라는 부하테스트 도구가 있다. 이 ngrinder는 명령을 내려주는 컨트롤러와 진짜 부하를 쌓아두는 에이전트로 구성되어 있다. 우리가 만약에 ngrinder를 컨테이너로 띄운다고 하면 ngrinder stack 안에는 ngrinder 컨트롤러 서비스와 ngrinder 에이전트 서비스가 포함되어 있는 ngrinder stack을 만들면된다.

  • 그래서 컨테이너가 여러개가 있으면 서비스가 되는 것이고, 한패키지에 서비스가 여러개가 있으면 도커 stack이라고 칭하는 것이다. 우리는 그래서 컨테이너를 띄울 노드들(깡통 또는 버츄얼머신 등)에 여러개의 docker stack을 띄울것이다. 이 얘기는 여러개의 서비스를 띄울것이고, 여러개의 컨테이너를 띄운다는 것이다.

  • 깡통안에 docker stack을 띄울 준비를 할 것이다. 이 작업이 들어가기 위해서는 3가지의 개념이 들어간다. 클러스터링, 스케쥴링, 서비스 디스커버리 라는 3가지 개념이다. 컨테이너 오케스트레이션 툴(ex) 쿠버네티스, 도커스웜)이 기본적으로 이 세가지를 지원해주는 도구를 말하는 것이다. 컨테이너 오케스트레이션 툴은 클러스터링, 스케쥴링, 서비스 디스커버리 기능들을 적용하고 셋팅하고 설정하도록 지원해주는 툴인 것이다. 도커스웜이나 쿠버네티스나 이런 기본적인 기능들에서 약간의 차이가 있을뿐이기 때문에 근본적으로는 위의 세가지를 지원해 주는 툴이라고 생각하면 된다.

  • 클러스터링

N개의 깡통을 우리가 사용할때 하나의 서버처럼 사용하는 방법이다. 무식하게 각각의 깡통에 ssh로 접속해서 도커이미지를 pull하거나 build를 해두고, docker run해서 각종 필요한 것들을 띄워야한다.

우리는 그래서 여러개의 깡통을 하나의 서버처럼 사용할 것이다. 즉 docker stack을 띄울것이다. 스택을 띄울때 알아야할게 단한번의 명령으로 N개의 컨테이너가 뜬다는 것이다. 마치 하나의 서버처럼 사용하는 것처럼.. 이런 개념이 클러스터링이다.

N개의 서버를 하나의 서버처럼 사용하는 방법.

  • 스케쥴링

그래서 우리는 N개의 컨테이너를 띄워야 하는데 이 N개의 컨테이너를 어디에다 띄울것인가에 대한 개념이다. 스케쥴링은 AWS에서는 ECS라는 서비스를 통해 탬플릿을 만들어 놓은 것이 있다. 예를들어서 CPU와 메모리의 사용량을 같이 비교해서 제일 자원사용량이 적은 것부터 점유해라 이런식이다.

한마디로 우리가 어떻게 내가 할 작업들을 어떤서버에 띄울것인지

클러스터링이랑 스케쥴링이 같이 도입되면 우리는 하나의 서버를 사용하는 것처럼 명령을 내리지만 그 안에 돌아가는 작업들은 알아서 자동화되서 자연스럽게 일이 분배가 된다.

  • 서비스 디스커버리

우리는 클러스터링, 스케쥴링을 도입해서 하나의 서버처럼 해야한다. 이런 작업이 완벽하게 되려면 보통의 경우에는 위에다 로드벨런서를 달아서 부하분산을 해줄 것이다. 우리가 스케쥴링까지 도입을 했으면 스케쥴링 작업이 끝날때까지 컨테이너가 어디에 어떻게뜰지는 아무도 모른다.

서비스 디스커버리라는 개념은 서비스가 어디에 위치해 있는지 자동으로 찾아주는 개념이다. 컨테이너가 자동으로 늘었다 줄었다 하던간에 클라이언트가 요청을 보냈을때 해당 클러스터한테 올바르게 요청이 잘 가도록 자동으로 찾아준다는 것이다.

  • EKS의 장점은 쿠버네티스와 같은 오케스트레이션 툴이 지원해야하는 스케쥴링, 서비스 디스커버리, 클러스터링 등의 기능들에 대해서 관리해주는 마스터노드를 서버리스 형태로 AWS가 관리해주는 매우 좋은 장점이 있는 서비스다.

  • 도커스웜은 도커 안에 포함되어 있는 기능이다. 그래서 도커에 매우 친화적이다. 도커에 있는 기능들을 그대로 가져가서 쓸 수 있다.

  • 같은 노드에 똑같이 환경설정된 같은 컨테이너가 여러개 띄워져 있을 수도 있다. 같은 프로세스가 점유하고 있는 포트가 중복되면 안되는데 이런경우는 뭐냐는 의문을 가질 수 있다. 포트 충돌이 일어나는게 아니냐. 클러스터링이 되어 있는 각 노드에 AWS ECS 같은 경우 에이전트라는 컨테이너가 띄워져 있고, 스웜이나 쿠버네티스도 마찬가지로 우리가 필요한 매니지먼트를 해주는 서비스가 각 노드들마다 띄워져 있다. 도커에서는 여러 네트워크 노드들을 지원해준다. 스웜이라는 네트워크 오케스트레이션 도구를 썼을때 클러스터링 되어 있는 각 노드들에게는 ingress라는 도커 네트워크가 만들어진다. ingress는 각 도커데몬에서 도커 브릿지라는 모듈에서 만들어진다. 도커데몬에서 새로운 네트워크를 만든다음에 각 컨테이너마다 가상의 아이피를 부여해준다. 그래서 도커데몬 입장에서 보면 가상의 아이피가 다른 여러 컨테이너들이 같은 포트를 점유하고 있는 상황인것이다. 그리고 상위에서 그 포트넘버로 들어오는 요청에 대해서 그 네트워크로 보내주게 된다. 내부적으로는 컨테이너 버츄얼 아이피를 통해 찾아가게 되는 방식이다. 이게 가능할 수 있는 것은 서비스 디스커버리 기능이 있기 때문이다.

  • 그래서 우리가 이제 셋팅할 노드에는 새로운 네트워크가 생성이 되고 우리는 이것을 ingress network라고 할 것이다. 그리고 ingress network안에 포함되어 있는 컨테이너들은 버츄얼 아이피가 부여가 되고 이 아이피의 포트가 우리가 사용할 포트가 부여가 될 것이다. 만약에 이상태에서 스케쥴링으로 또 웹 컨테이너를 100개를 추가로 띄우면 이 100개도 마찬가지로 버츄얼 아이피가 계속 부여되면서 포트가 충돌되거나 하는 일은 없을 것이다. 우리는 그래서 이런식으로 컨테이너를 여러노드에 여러개를 띄울 것이다.

  • 트래픽이라는 컨테이너 리버스 프록시 및 로드밸런서 오픈소스 로드밸런서를 사용할 것이다. 얘가 해주는 일은 서비스 요청이 들어오면 특정 호스트 룰에 따라서 예를 들어서 web.fast.com이라고 들어오면 웹컨테이너가 물려있어서 본인이 받은 요청을 뒷단에 웹서비스로 보내주게 된다. 그런데 이런 요청을 보내려면 버츄얼 아이피를 알아야 할 것이다. 그래서 트래픽이라는 툴은 자기가 운영되고 있는 노드의 도커데몬에게 물어본다. 이 도커데몬은 도커스웜으로 클러스터링 되어 있는 상태이다. 버츄얼아이피가 뭔지 물어본다. 버츄얼 아이피를 알아내면 리버스 프록시해준다. 그러면서 웹컨테이너가 여러개면 로드벨런싱까지 해준다. 호스트룰에 따라서 트래픽이 부하분산을 알아서 해준다.

  • 우리는 수십개 ~ 수백개 컨테이너를 띄울것이다. 예를들어서 100개의 컨테이너를 띄웠는데 99번째 컨테이너에 무언가 문제가 있으면 이 컨테이너를 관리를 해줘야 한다. 포테이너는 도커 스웜에서 가장 많이 쓰이고 있는 스웜 매니지먼트 툴이다. 그래서 이 포테이너를 통해서 컨테이너 배포, 컨테이너 삭제, 컨테이너 로그확인 등을 할 수 있다.

  • 엘라스틱서치는 제이슨 형태로 만들어지는 수십기가 ~ 페타바이트까지 쌓이는 데이터를 효율적으로 검색해주는 검색엔진 DB라고 할 수 있다.

  • 메트릭비츠 : 컨테이너의 램 사용량 디스크 사용량 등 자원사용량에 대한 메트릭을 수집할 수 있는 툴

  • 과거 ELK는 로그 중심으로 수집하는 것이 목적이었지만 최근에는 메트릭 모니터링이나 네트워크 모니터링, 시큐어 모니터링도 지원한다. 메트릭 모니터링은 아직까지는 다른 툴들에 비해서는 부족한 편이다.

  • Docker swarm

1) Docker(v1.12+)에 내장된 orchestration tool

2) Docker / docker-compose 명령어를 그대로 사용할 수 있어 기타 툴과 대비하여 쉽고 편하게 사용 가능

** docker-compose : 단일노드에서 여러개의 컨테이너를 띄우는 명령어

3) 1,000 Node / 50,000개의 Container 운영 가능

  • docker-compose

1) Container 실행을 위한 Option과 Container간의 연관 관계가 복잡해지는 문제를 해결하기 위해 나온 Docker tool

2) docker-compose.yaml 등의 *.yaml / *.yml 형태로 컨테이너 실행이나 빌드방식을 미리 기입을 해놓고 실행을 할 수 있다.(컨테이너 structure를 Source code 형태로 관리(IaC) 가능)

도커데몬설치나 운영이나 도커스웜으로 클러스터링이 완벽하게 실행이 되는 것을 확인했을때 해당 컨테이너의 실행방식을 docker-compose.yaml의 코드형태로 관리하게 되면 어떤상황에서 누구던지 동일한 docker-compose-yaml만 갖고 있으면 똑같은 컨테이너 환경을 구성할 수 있다.

3) Docker-compose in Swarm으로 관리

  • 도커스웜 클러스터링을 자동화해서 예를들어 처음에 아무것도 없는 상황에서 코드형태로 도커스웜 클러스터링을 코드형태로 하고 싶다. 아니면 도커를 코드형태로 띄우고 싶다. 더나아가서 우리가 원하는 서버사양과 옵션을 적용해서 그 위에서 클러스터링을 하던 뭘하던 방식을 지정해주고 싶다. 통상 이런것은 AWS를 기준으로 AMI로 해왔다. 하지만 AMI는 수정사항이 발생하면 새로 빌드를 해서 이미지를 다시 떠야 한다는 단점이 있다. 내가 해당서버는 센트오에스 7.0 버전을 쓰고 싶고, 도커는 또 이 버전으로 하고 싶고, 도커 실행방식은 이래이래하고, 포트나 보안설정을 내가 원하는 방식으로 지정해서 코드로 관리하고 싶을때 이를 도와주는 툴들이 또 있다. 앤서블이나 퍼핏 같은 코드형태로 서버구성을 도와주는 툴이 대표적이다. 그래서 우리는 클러스터에 새로운 노드가 추가하고자 하면 미리 빌드된 앤서블 플레이북으로 해당서버를 실행하고 그러면 그 노드가 스웜클러스터라고 하면 클러스터에 조인하는 기능이 포함되어 있다.

  • 더나아가면 테라폼이라는 툴도 있다. 앤서블은 서버구성 환경에 대해 정의를 해준다고 하면 테라폼은 예를들어 VPC는 내가원하는 어떤걸로 지정해주고, AMI로 실행하고 싶으면 특정 AMI를 지정해 주거나, 보안그룹은 어떻게 지정해줄지 AWS 리소스관점에서 코드형태로 정의하여 띄워 줄 수 있다. 테라폼을 사용하면 다른 리전에도 내가 원하는 리소스를 현재 리전에 만든것을 똑같이 빠르게 구현해 줄 수 있는 장점이 있다. 왜냐하면 테라폼 코드에서 다른리전으로 변수를 변경해주고, 그냥 기존의 테라폼 코드를 복붙하면 되니까 가능하다. 그래서 테라폼으로 AWS 리소스를 띄우고 그 안에서 앤서블로 서버환경 구성을 하면 스웜클러스터를 번거롭지 않게 구현 할 수 있을것이다. 그래서 그 안에서 돌아가는 컨테이너를 docker compose로 띄우게 되면 우리의 서버환경을 자동화 할 수 있을 것이다.

[실습목표]

1) docker swarm 클러스터링 구성

2) 오버레이 네트워킹 등 컨테이너 오케스트레이션 실습

3) AWS ECR를 이용한 도커이미지 활용

4) AWS ECR를 통해 다운받은 도커이미지를 이용하여 도커서비스 구현

[실습상세]

step 1) 마스터 노드 1대, 슬레이브 노드 1대 총 두개의 노드가 있는 클러스터를 형성하기 위해서 EC2 인스턴스 2대 생성

** 주의사항으로 보안그룹 설정에서 아래 그림과 같이 보안그룹에서 모든 트래픽에 대해서 개방을 해준다.

2

step 2) 마스터노드와 워커노드에 모두 각각 SSH로 접속하여 아래 그림과 같이 도커를 설치하고 실행해준다.

step 2)까지 수행하면 클러스터링 할 준비가 완료된 것이다.

3

사용할 마스터노드에서 docker swarm init하고 advertise adress해서 해당 마스터노드에 프라이빗 아이피를 넣어줘야 한다.

swarm join이 스웜 클러스터링이 되고 워커노드들을 편입시키기 위해서 쓰는 기능인데 해당기능을 쓰게 되면 친절하게도 해당 클러스터에 join하려면 해당 토근을 사용하라고 토큰까지 만들어준다. 그 다음에 마스터노드의 프라이빗 아이피와 2377포트로 통신하라고 되어있다.

그래서 우리는 swarm init하고 adress option으로 마스터 노드의 프라이빗 아이피를 넣어준다. 그러면 도커 스웜조인하면 목록이 나오는데 그거를 복사해서 해당 클러스터에 조인할 워커노드에 복사해서 실행하면 된다. 그러면 해당노드가 스웜에 워커형태로 조인이 되었다는 화면을 확인할 수 있다. 그래서 절대 퍼블릭 아이피를 쓰면 안된다 ! 퍼블릭 아이피를 쓰면 VPC 밖으로 나갔다 다시 들어와서 통신하기 때문이다. 우리는 내부에서 통신해야 한다.

step 3) 마스터 노드의 프라이빗 아이피 확인 및 도커 클러스터링 형성

아래 그림과 같이 AWS EC2 콘솔에서 마스터 노드의 프라이빗 아이피를 확인하고, 마스터노드의 EC2 터미널에서 아래 그림과 같이 명령어를 실행해준다.

4

위에 콘솔에서의 명령어를 해석하면 아래와 같다.

도커 클러스터링을 초기화하겠다. 그리고 해당 마스터노드의 프라이빗 아이피를 지정하여 마스터 노드가 어떤 것인지 명시한다. 도커에서는 그러면 마스터노드가 이니셜라이즈되고, “너가 만약에 워커를 추가하고 싶으면 아래 명령어를 워커노드에서 실행하면 됩니다”라고 친절하게 알려주기까지 한다. 토큰까지 발급해주고 마스터 노드의 아이피에 2377이라고 나오는 것을 확인할 수 있다.

netstat 명령어로 어떤 포트가 현재 점유하고 있는지 확인해보면 로컬호스트에 2377포트로 마스터가 열려있을것이다. 이게 도커 스웜에서 2377포트는 클러스터 되어있는 마스터와 워커 노드의 통신포트이기 때문이다.

위에까지 실행을 해주고 나서 우리는 워커노드 EC2 콘솔로 이동하여 아래 그림과 같이 마스터 노드에서 발급받은 토근을 넣고 클러스터에 join한다.

5

이렇게 마스터노드는 클러스터에서 커맨드센터같은 역할을 하는데 문제는 이 마스터노드가 죽어버리면 클러스터 전체가 죽을 수 있다는 문제가 발생한다. 그래서 스웜 도큐먼트에서는 스웜클러스터 노드의 갯수에 따라서 멀티마스터를 몇대 이상 구성하라고 권고하고 있다.

만약에 클러스터에 마스터 노드를 더 추가를 한다고 하면 아래 그림과 같이 마스터노드와 조인하고 싶은 마스터노드에서 각각 실행해주면 된다.

“노드를 더 편입시킬건데 얘는 일하는 노드가 아니라 멀티 마스터 노드로서 클러스터를 관리할 노드를 추가할게”

이렇게 추가를 해주면 명령을 내릴 수 있는 체제가 더 늘어난다는 것이다. 이말은 클러스터에서 마스터노드가 하나 죽어도 나머지 마스터노드로 클러스터를 컨트롤 할 수 있다는 말이다. 우리는 항상 가용성을 고려해야 한다 !

참고로 보통 클러스터에 100개 노드가 있으면 5 ~ 8대 정도의 멀티마스터노드를 사용한다.

4-2

마스터노드 EC2 콘솔로 다시 이동하여 클러스터의 상태를 보여주는 명령어인 docker node ls를 실행하여 아래 그림과 같이 클러스터가 정상적으로 형성이 되었는지 확인한다.

참고로 아래 그림에서 볼 수 있듯이 STATUS 항목에 * 표시된 것이 마스터 노드이다. 그리고 이 docker node ls 명령어는 워커 노드에서는 실행이 안될 것이다. 마스터 노드에서만 실행 할 수 있는 명령어이다.

6

step 4) 도커 서비스를 띄운다.

마스터 노드 EC2 터미널에서 아래 그림과 같이 명령어들을 실행해본다.

7

그런 다음에 워커노드 EC2 터미널로 이동하여 curl localhost:4567을 실행하여 마스터노드의 결과와 똑같이 나오는지 확인해본다.

docker service create : 도커 서비스 생성. 도커 서비스는 N개의 컨테이너의 그룹이다. 지금은 1개의 그룹인 서비스를 띄운것이다. 각각 마스터 노드와 워커노드에서 docker process 명령어를 검색해보면 마스터노드에 컨테이너가 띄워져 있을 것이다.

여기서 service는 매니저(마스터) 노드만 쓸 수 있는 것이고, 스웜 매니저가 관리하는 동일한 컨테이너의 그룹이라고 할 수 있다. 또한 1,10,100, 그 이상의 동일한 컨테이너 프로세스에 대한 명령,로깅, 관리 등의 기능을 수행하는 명령어이다.

docker service ps whoami : whoami라는 도커 서비스의 상태를 보여달라는 명령어

마스터노드에 whoami라는 서비스 컨테이너가 띄워져 있는 것을 확인할 수 있다. 이 whoami는 단순한 컨테이너다. 요청을 보내면 자기자신의 아이디를 응답해주는 기능이다.

마스터노드에는 해당 프로세스가 띄워져 있고, 워커노드에는 띄워져 있지 않은 상태이다. 그러나 마스터노드에서나 워커노드에서나 curl localhost:4567 명령어를 실행하면 정상적으로 컨테이너가 실행되는 것을 확인할 수 있다. 로컬호스트 즉 자기자신한테 요청을 보냈는데 워커노드는 자기자신에게 curl을 때렸는데 어떻게 응답을 받은것일까. 워커노드에서 curl localhost를 때렸지만 실질적으로는 마스터노드에 띄워져 있는 컨테이너에게 요청한 것이구나라고 짐작할 수 있다.

우리는 방금 사례를 통해 아래 3가지를 확인한 것이다.(컨테이너 오케스트레이션이 지원해야 하는 기능 3가지)

1) 우리는 docker service create 명령어로 N개의 컨테이너를 하나의 서버에서 사용하는 것처럼 명령을 내린것이다.

2) swarm은 컨테이너 트래픽이 매니저(마스터) first이다. 어떤 상황에서 어떤 노드의 컨테이너가 떠야 하는지 스케쥴링까지 확인한것이다.

3) localhost 4567을 때렸는데 내가 지금 보내는 요청을 응답할 수 있는 컨테이너가 어디에 위치에 있는지 찾아내는 서비스 디스커버리까지 확인했다.

그러면 얘들이 지금 어떤식으로 동작이 되고 있냐 확인해보자.

docker network ls 명령어를 실행하면 ingress라는 네트워크가 보일 것이고 스코프의 범위는 로컬이 아니라 swarm으로 되어 있을 것이다.

그리고 docker network inspect ingress네트워크아이디를 실행해본다. 우리는 whoami라는 서비스를 띄웠고, whoami라는 컨테이너를 띄웠다. localhost 4567로 도커 데몬호스트가 요청을 받게되면 whoami 서비스를 찾아라 그리고 걔의 버츄얼아이피를 통해 찾아가고 요청을 찔러넣고 응답을 받아라 이런 의미를 알 수 있다.

ingress라는 네트워크를 열어보면 ingress 네트워크의 whoami라는 컨테이너가 등록되어 있을 것이다. 여기에 등록된 아이피를 스웜 클러스터의 서비스 디스커버리 기능을 통해서 찾아낸것이다.

구조는 아래 그림과 같다.

8

  • Overlay network(ingress)

1) Swarm manager / worker node join 시 자동으로 생성되는 Overlay network

2) Service discovery(Routing mesh)를 통해 해당 Network에 속한 서비스가 Port open 시 해당 Port를 전 Node의 port를 open하고 해당 컨테이너에 전달 및 Load balancing 진행

3) Service create … 명령어 실행 시 network 옵션을 통해 특정 network를 지정하지 않는다면 자동으로 Ingress network에 포함

step 5) 컨테이너를 늘려보고 서비스가 잘 이루어 지는지 확인해본다.

먼저 마스터 노드에서 아래 그림과 같이 실행해본다.

9

워커노드에서도 아래 그림과 같이 실행해본다.

10

docker service scale whoami=3 : whoami 컨테이너를 현재 1개에서 3개로 변경

아래 그림과 같이 service scale을 3으로 해주고 curl을 연속으로 두번해주면 응답이 각각 다르게 오는 것을 알 수 있다. 이 말은 지금 컨테이너 로드벨런싱이 되고 있다는 것이다. 워커노드에서도 마찬가지로 curl localhost:4567을 연속적으로 실행하다보면 컨테이너 로드벨런싱이 되고 있는 것을 확인 할 수 있다.

그리고 docker ps -a 명령어를 마스터노드와 워커노드 각각해서 열어보면 우리가 서비스 스케일을 3개로 늘렸기 때문에 컨테이너가 3개가 보일 것이고 보통은 매니저노드에 컨테이너 1개, 워커에 컨테이너 2개 이런식으로 띄워져 있을 것이다.(스케쥴링에 따라 다르게 띄워질 수도 있다. 예를 들어서 매니저노드에 2개, 워커노드에 1개 이런식으로..)

우리는 그래서 컨테이너를 3개로 늘렸고, 이 컨테이너들이 각 노드들에 분산배치되어 클러스터링으로 형성되어 있는 상황이다. 우리는 manager 노드에게만 명령을 내렸다. 마치 하나의 서버에서 서버를 다루는 것처럼 명령을 내린것이다. 그래서 service scale 라는 명령어를 통해서 해당서비스가 이루어지는 컨테이너 개수가 3개로 늘어났고, 마스터에 하나, 슬레이브에 2개가 띄워져 있는 것이다. 그리고 curl localhost를 때렸을때 응답하는 컨테이너 아이디가 바뀌는 것을 확인헀다. 이렇게 오케스트레이션 툴을 활용해서 편리하고 쉽게 도커 서비스를 제공할 수 있다.

내부에서 어떻게 동작되고 있고 왜 docker network inspect ingress 명령어를 때려서 열어봤을때 해당 네트워크의 가상아이피가 등록이 되어있는지 알아보자. ingress network는 스웜이 클러스터링 될때 다시말해서 노드가 조인될때 기본적으로 만들어지는 오버레이 네트워크이다. 우리가 docker service create할때 포트옵션만 부여하고, 특별하게 네트워크 옵션을 주지 않았다. 그러면 스웜에서 서비스가 만들어질때 네트워크 옵션이 안걸려있고 포트옵션만 부여할경우 자동으로 ingress network에 편입을 시킨다. curl localhost:4567 명령을 주어도 ingress network에 포함되어 있는 whoami라는 컨테이너에 요청을 보내줄 수 있는 것이다. 이게 가능한 이유는 스웜기능 내에서 서비스 디스커버리 기능을 제공하기 때문에 해당 버츄얼 아이피를 찾아갈 수 있는 것이다.

그러면 ingress network가 뭐냐. 스웜매니저, 워커노드 조인 시 자동으로 생성되는 오버레이 네트워크를 말한다. 이 오버레이 네트워크의 이름이 ingress라고 되어 있는 것이다. 서비스 디스커버리를 통해 해당 네트워크에 속한 서비스가 포트오픈시 해당포트를 전체 노드의 포트를 오픈을하고, 해당 컨테이너에 전달 및 로드밸런싱을 수행한다. 우리는 서비스 create를 할때 따로 네트워크 옵션을 주지 않았다. 그렇기 때문에 기본적으로 전영역에 걸쳐서 만들어지는 도커 전체노드가 포함되어 있는 가상의 네트워크인 오버레이 네트워크가 만들어지고 이 네트워크에 내가 생성한 컨테이너가 포함되어지는 것이다. 그래서 각 노드에서 localhost:4567하면 맨처음에 그 포트는 전체 노드에 오픈이 되어 있고, 최초로 요청을 받는 주체가 도커 스웜데몬이 받게 되는 것이다. 도커 스웜데몬이 ingress 네트워크 포트로 요청을 받은것에 대해서 ingress network에 찾아간다. 그리고 해당 포트가 물려있는 그 특정 컨테이너의 가상아이피를 찾아내어 요청을 전달하게 된다.

아까 docker node ls라는 명령어를 실행해봤다. 그때 그것의 스코프가 swarm이라고 되어 있었다. 그거는 swarm이 클러스터가 조인될때 ingress라는 오버레이 네트워크가 디폴트로 만들어지기 때문이다. 그리고 service create가 될때 포트 옵션을 넣어주면 자동으로 ingress network에 편입이 된다. curl localhost:4567을 하면 스웜데몬이 4567포트로 요청을 받았기 때문에 ingress network로 찾아가서 4567로 걸려있는 컨테이너 가상아이피를 전부 뽑아낸다. 그리고 거기에다 요청을 보내버린다. 그렇게 되면 내부적으로 서비스디스커버리와 로드밸런싱이 이루어지고 있는 것이다. 내부에 swarm lb라는 스웜 로드밸런서가 돌고 있다. 그래서 맨처음에 도커데몬이 요청을 받고, 그 다음에 해당 가상아이피를 전부 찾아낸 다음에 스웜 lb한테 다시 요청을 보낸다 그러면 그 스웜 로드밸런서가 알아낸 가상아이피를 가지고 해당 컨테이너들에게 부하분산을 해준다.

전체 노드의 가상으로 만들어진 네트워크에 내가 만든 컨테이너가 등록이 되고 오케스트레이션 도구가 해당포트에 대해서 요청을 받으면 기본적으로 설정되는 ingress overlay 네트워크를 찾은 다음에 거기에 등록되어 있는 가상아이피를 뽑아내고 스웜 로드밸런서에게 전달한 다음에 그 스웜 로드밸런서가 각 컨테이너한테 로드밸런싱을 수행한다.

그래서 사용자의 요구사항에 따라 다양한 오버레이 네트워크를 만들 수 있겠다는 것을 유추할 수 있다. 스웜에서 생성되는 모든 컨테이너는 같은 오버레이 네트워크에 속해 있는 것들만 서로 통신이 가능하다. 그래서 사용자는 각자 원하는 의도에 따라 오버레이 네트워크를 몇개를 만든 다음에 필요한 기능에 따라 따로 만들 수 있는 것이다. 예를 들어서 우리가 웹서비스를 배포하고 싶을때 front-end 기능을 수행할 오버레이 네트워크를 따로 만들고, back-end 기능을 수행할 오버레이 네트워크를 따로 만들 수 있다. 그러면 예를 들어서 백앤드 기능을 수행할 오버레이 네트워크내의 노드끼리는 서로 통신이 가능하다. 이 각각의 오버레이 네트워크 내의 가상아이피 대역은 다르다. 그래서 백앤드 기능을 수행할 오버레이 네트워크 내의 노드들의 가상아이피는 프론트앤드 기능을 수행할 오버레이 네트워크 내의 노드에서는 요청을 보낼 수 없다. 그래서 같은 오버레이 네트워크에 묶여있는 컨테이너끼리만 통신이 가능하다.

이렇게 왜 해야하냐면 우리는 클러스터 최상단에 트래픽을 띄울 것이다. 얘는 호스트기반으로 컨테이너에 리버스 프록시나 로드밸런싱이 가능하게 설정할 것이다. 리버스 프록시를 해줘야 하는 컨테이너는 프론트앤드 기능을 하는 오버레이 네트워크의 웹 컨테이너가 될 것이다. 외부에서 다이렉트로 백앤드 기능을 수행하는 오버레이 네트워크 내에 API 컨테이너에게 요청을 보낼 수는 없을 것이다. 무조건 프론트 앤드쪽을 거칠 것이다. 그래서 트래픽과 웹컨테이너를 프론트앤드라는 오버레이 네트워크에 편입을 시킬 것이다. 그래서 트래픽은 웹이라는 컨테이너한테 요청을 보낼 것이다. 그리고 엔진엑스에서 리버스 프록시 때려서 API로 보내는 것처럼 오버레이 네트워크를 만들때 가상 아이피의 대역을 설정해줄 수 있다. 그러면 설정한 아이피 대역블럭에 요청을 보낼 수 있도록 허용할 수 있다. 그래서 API기능의 컨테이너들은 프론트앤드쪽 오버레이 네트워크에도 허용시켜주고, DB기능쪽의 오버레이 네트워크에도 허용시켜주면 우리가 통상 생각하는 그 웹서비스 아키텍처를 구현할 수 있는 것이다.

정리하면 컨테이너는 같은 오버레이 네트워크에 포함되어 있는 얘들 끼리만 통신이 가능하다. 여기에 추가적으로 오버레이 네트워크에서 특정 아이피대역을 추가적으로 통신이 가능하도록 설정을 허용해줄 수 있다.(이 말이 곧 사용자 임의로 오버레이 네트워크로 편입을 시킬 수 있다는 말이다)

실습전에 미리 구현해놓은 퍼블릭 도커이미지(웹이미지, API이미지)를 가지고 실습을 하고자 한다. 그래서 우리는 컨테이너 레지스트를 관리하는 AWS ECR 서버리스 서비스를 이용하여 이미지를 다운받아서 사용할 것이다.

step 6) AWS ECR repo 생성 및 활용

step 6-1) AWS access key 생성 및 나의 AWS CLI 등록, 나의 AWS ECR 계정 인증

아래 그림과 같이 액세스 키를 생성하고 CLI에 등록해준다.

콘솔에서 수행하는 작업들은 모두 마스터 노드에서 해준다.

15

step 6-2) 실습전에 미리 구현해놓은 퍼블릭 도커이미지(API 컨테이너, 웹 컨테이너로 구성되어 있고, 도커허브에 구축해 놓은 것이다.)를 마스터노드로 pull 해온다.

마스터 노드에서 아래 그림과 같이 실행

16

step 6-3) AWS ECR repo 생성 및 repo 주소 확인

17

step 6-4) 특정 도커이미지를 나의 ECR로 push 하기

아래 그림과 같이 방금 위에서 pull한 도커이미지에 태그부착(태그를 붙여서 이름을 바꾸는게 아니라 똑같은 이미지를 새로운 태그 이름으로 하나 더 복사하는 것이다.)하고나서 나의 AWS ECR로 push해본다. 그리고 ECR 콘솔로 돌아가서 push 된 이미지가 잘 들어왔는지 확인한다.

작업은 마스터노드에서 수행한다.

** 참고로 docker pull할때 앞에 URL이 붙어있지 않으면 자동으로 도커허브에 가서 찾아온다.

18

step 7) pull한 이미지를 이용하여 웹서비스 아키텍처 구현하기

먼저 아래 그림과 같이 마스터노드에서 명령어들을 실행한다.

19

가장 처음 명령을 내린것이 docker network create --driver overlay external이다. 이 명령어는 ingress network(디폴트 네트워크)와 똑같은 구성의 네트워크를 하나 external network(ingress network를 복붙한거와 같은 ingress와 동일한 네트워크)라는 새로운 네트워크를 만들어준 것이다. 그리고 나서 docker service create 명령을 줄때 --network 옵션을 통해 이 생성할 도커 서비스가 포함될 네트워크가 overlay network라고 명시를 해준것이다. docker network ls 명령어를 통해 생성한 네트워크를 확인할 수 있다.

위의 docker network create --driver overlay external 명령어 중 Network create (option) 의미

1) driver (overlay) : Network driver 설정, overlay 외에도 bridge, host 등 지원

2) 위 예제는 external이라는 이름의 Overlay network 생성

위 명령어 중 docker service create --name web -p 3080:80 의미

우리가 생성하고자 하는 서비스의 이름은 web으로 하고, 포트옵션으로 3080에 들어가는 포트를 컨테이너 내부의 80포트로 보내주겠다는 의미. 여기서 해당 컨테이너는 엔진엑스가 돌고있는 웹이미지이다.

위의 명령어 중 Service create (option) 의미

1) replicas : 서비스 내 Container의 갯수, Swarm scheduler에 의해 Swarm node에 분산하여 배치

2) network : 해당 서비스의 Container를 어떤 network에 배치할 것인가에 대한 옵션, 앞서 생성한 external에 배치 진행

3) with-registry-auth : 외부 Private repository에서 Docker image를 갖고 올 때 인증 여부를 함께 진행할 것인지. 도커데몬에 특정 ECR의 repo를 갖다 쓰겠다는 옵션. 참고로 ECR 로그인 유지시간이 있기 때문에 로그인 시간이 expire되면 이 명령어가 실행이 안될 수 있다.

그리고 마지막으로 docker service ls 명령어로 구동한 서비스가 잘 띄워져 있는지 확인했다. 이 명령어를 워커노드에서 docker service ls 명령을 수행하면 당연히 안먹힐 것이다. 커멘드센터 역할은 마스터노드만 수행할 수 있기 때문이다.

그리고 역시 마스터노드에서 아래 그림과 같이 명령어를 실행해본다.

20

docker service ps web : web이라는 도커서비스에 포함되어 있는 3개의 컨테이너가 어떤 이미지를 사용하고 있고 어떤 노드에 배치되어 있는지를 전시해준다.

docker node ls : 도커 클러스터 내역을 확인

그래서 사용자가 설정할때 도커노드에 호스트이름을 설정해줄 수 있다. 사용자가 이해하기 쉬운 이름으로 변경해주면 좋다.

위에 구현한 내역을 도식화하면 아래 그림과 같다.

18-2

  • Traefik이란

1) 일종의 오픈소스 솔루션

2) Docker service가 등록되는 것을 감지하여 자동으로 Container reverse proxy / load balancing을 담당하는 Modern HTTP service routing solution

3) Swarm, Kubernetes, Rancher, AWS ECS 등 다양한 Orchestration tool 지원

4) Host(*.fastcampus.co.kr), Path(/api, /user) 등 다양한 routing rule을 제공하여 쉬운 Microservice architecture 구축 가능

5) SSL Certification(HTTP) Container 별 Service 적용 가능

그리고 아래 그림과 같이 마스터노드나 워커노드에서 curl localhost:3080을 때려서 잘 동작하는지 확인한다.

22

그런 다음에 마스터노드에서 아래 그림과 같이 명령어를 실행해본다.

23

docker network create --driver overlay --attachable internal 명령어를 통해 internal 네트워크를 포함한 external 네트워크를 생성했다.

그리고 우리가 새로띄운 컨테이너는 redis라는 컨테이너와 counter라는 컨테이너다. counter 컨테이너는 레디스에 요청을 보내서 그냥 0부터 카운트를 해주는 기능이다.

그래서 우리는 docker service create --name redis --network internal redis:latest 명령어를 통해 internal이라는 오버레이 네트워크를 만들었고, service create한다음에 해당 서비스의 이름은 redis로 하고, 도커허브에서 운영하고 있는 redis:latest라는 이미지를 받아온것이다. 여기서 우리는 따로 포트옵션을 넣지 않았다. 그리고 우리가 만든 internal network에 포함되어 있는 것이다.

그런 다음에 마스터노드에서 아래 그림과 같이 명령어를 실행해본다.

24

그러면 docker service create --name counter --network=external --network=internal --replicas 3 -e REDIS_HOST=redis -p 4568:4567 subicura/counter 명령어를 실행했다. 이것은 우리는 counter(그냥 인터넷에 돌아다니는 도커 샘플이미지)라는 서비스를 만든것이다. 여기서 유심히 봐야 할 것은 도커컨테이너가 여러종류의 네트워크에 포함될 수 있는 것인데 여기서도 external network와 internal network에 같이 포함되어 있는 것을 확인할 수 있다. 그리고 -e 옵션은 환경변수 옵션으로 카운터가 실행될때 환경변수에다가 redis의 호스트는 redis로 지정해준 것이다. 레디스로 접속하려면 해당 URL로 접속하라고 유도해놓은 환경변수이다. 여기서 주의해야할 것은 레디스의 컨테이너에 접속하는 것은 맞는데 지금 컨테이너 주소가 redis라는 것이다. 무슨말이냐면 내부에서 서비스 디스커버리가 되는데 같은 overlay 네트워크에 포함되어져 있기 컨테이너면 서비스의 이름으로 찾아갈 수 있다는 말이다. 스웜에서 내부서비스에 서비스의 이름으로 DNS가 부여가 가능하기 때문이다. 예를들어서 내부에서 counter라는 서비스가 redis를 찾아가기 위해서 DNS를 알아야하는데 그게 서비스 이름으로 등록되어 있다. 그래서 우리가 counter를 curl localhost:4568해서 count가 쌓인다는 것은 레디스에 지금 데이터가 쌓이고, 카운터가 응답을 해준다는 말이다. 그말은 내부에서 카운터라는 서비스와 레디스라는 서비스가 서로 통신이 잘 되고 있다는 말이다.

그리고 나서 워커노드에서 아래와 같이 명령어를 실행해본다.

25

그러면 위의 그림과 같이 reponse count가 하나씩 올라갈 것이다.

마지막으로 마스터노드에서 아래와 같은 명령어를 실행해본다.

26