Docker 공식문서 에서 제시하는 개발-배포 Flow 따라가기 (Docker Swarm 사용하기)

infra | 09 February 2018

Tags | docker swarm stack service container image deploy

pre-requisite

docker, docker-compose, docker-machine 이 세가지 프로그램이 설치되어야 한다.

Docker Store(링크) 에서 설치 가능하다. 자신의 OS 에 맞는 Docker 를 설치하자.


서론

그동안 docker 를 사용하여 웹 서버 개발을 하고 있었지만, Docker 공식 문서를 오랜만에 들어가보니 그곳에서 추구하는 개발-배포 flow 는 나의 그것과 사뭇 달랐다.

나의 방식보다 훨씬 깔끔하고 스마트했다. 그래서 한번 제대로 익혀놓고, 앞으로 이렇게 개발-배포 하자 라고 마음먹으며 이 글을 쓴다.

“Docker 에서 제시하는 개발-배포 Flow 는 AWS 나 Azure 에서 자사 제품들을 이용하여 제시하는 개발-배포 Flow 의 대체가 될 수 있을 것 같다.”

새로운 개발 배포 flow를 익히면서 중점적인 이슈로 둔 부분은 아래와 같다.

1. 초기구축 : 초기 서비스 그룹(DB, WAS, Web Server 등) 구축이 쉬운가

2. 스케일 조정 : Load Balancing 설정이 쉽고, Scale 조정이 쉬운가

3. 코드 배포 : 추후 앱의 로직 코드 변경시 배포가 편리한가

클라우드를 제공하는 AWS 나 AZURE 는 자신들이 만들어놓은 여러 제품들을 이용하면 위 세가지를 편리하게 실현할 수 있게 하였다. (AWS 의 ELB-elastic load balancer 를 이용하면 로드밸런싱이 쉽게 가능한 것 처럼 …)

과연 Docker 를 이용한 서버 개발-배포 flow 도 위의 세가지가 가능할까? Docker 공식 문서를 읽고 난 후 나의 생각은 ‘yes’ 이다.

Docker 에서 제시하는 개발-배포 Flow

1. 초기 구축

초기 구축이란, 코드가 배포될 클라우드의 인프라를 구축하는 일을 말한다.

Docker를 이용한 개발-배포 Flow 에서, 초기 구축은 두 단계로 나뉜다.

  1. Swarm(Docker Machine Cluster) 를 구축한다.
  2. docker-compose.yml 로 Stack 구성하고 실행한다.

1-1.Swarm 구축

Swarm 이란 여러 Docker Macine들이 마치 하나의 Docker 실행환경처럼 작동하는 Virtual Macine Cluster 를 말한다.

아래 그림처럼 Swarm 이 클라우드 서버 상의 두대의 Docker Machine 으로 구성되어 있다면, 우리가 제공하고자 하는 앱에 필요한 컨테이너들이 두 가상머신에 적절한 수로 배치된다.

Manager node 와 Worker node

Swarm 의 각 Docker Machine 들은 Node 라고 부르며, 하나의 Manager Node와 다수의 Worker Node 로 이루어져있다. 오직 Manager node 만이 다른 node 에 접근하고 제어할 수 있다. 우리는 이 모든 노드에 접속해 일일히 동작을 제어할 필요 없이, Manager Node 의 Shell 에 접속해서 마치 swarm 이 하나의 Docker 실행환경이라고 생각하고 docker 명령어를 실행하면 된다.

swarm

Manager / Worker 설정

우선 접속 가능한 docker machine 리스트를 확인한다.

$ docker-machine ls
NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v17.06.2-ce   
myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v17.06.2-ce   

이 중 manager 로 삼을 docker machine 을 골라 아래와 같이 수행한다.

$ docker-machine ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>"
Swarm initialized: current node <node ID> is now a manager.

To add a worker to this swarm, run the following command:

  docker swarm join \
  --token <token> \
  <myvm ip>:<port>

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

위를 완료했다면 worker 로 삼을 docker machine 에서 아래와 같이 수행한다.

$ docker-machine ssh myvm2 "docker swarm join \
--token <token> \
<ip>:2377"

This node joined a swarm as a worker.

이때 주의 할 점은 반드시 2377 포트를 이용해 swarm 통신을 해야한다는 것이다. 2376 이 절대 아니다.

이제 Manager Node 에서 모든 docker 명령을 수행하면 되는데, 첫번째 docker 명령으로 docker node ls 으로 모든 클러스터 노드들을 확인해보자.

$ docker-machine ssh myvm1 "docker node ls"
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
brtu9urxwfd5j0zrmkubhpkbd     myvm2               Ready               Active
rihwohkh3ph38fhillhhb84sk *   myvm1               Ready               Active              Leader

이렇게 Swarm 구축은 끝났고, 이 swarm 의 노드들을 Azure 나 Aws 의 클라우드 가상 머신으로 설정만 한다면 클라우드 서버 인프라 구축의 시작점이 되는 것이다.

참고 1: Azure 에서 Docker Machine 만드는 법

참고 2: AWS 에서 Docker Machine 만드는 법

참고 3: Docker machine 에서 지원하는 가상머신 드라이버들

참고 4: driver 란? Microsoft

1-2. docker-compose.yml 로 Stack 구성하고 실행

Docker-Compose 는 컨테이너를 Stack-Service-Task 라는 세가지 가상 계층으로 구분해 관리한다.

결국 우리가 만들고자 하는 것은 하나의 Stack 이고, docker-compose.yml은 Stack 을 정의할 수 있는 파일이다.

docker-compose.yml 작성 예시

아래는 docker-compose.yml 의 예시이다.

web, visualizer,redis 라는 세개의 Service 가 이 Stack 을 이루고, web Service 는 5개의 Task(Container) 로 이루어져있다.

5개의 Task 로 이루어진 web서비스의 주소 80 port 로 접속이 들어오면, docker 는 round-robin 방식으로 load balancing 을 수행한다.

version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    ports:
      - "80:80"
    deploy:
      replicas: 5
      restart_policy:
        condition: on-failure
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
    networks:
      - webnet
  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]
    networks:
      - webnet
  redis:
    image: redis
    ports:
      - "6379:6379"
    # ports 대신 expose:-"6379" 을 사용하면 docker network 상에서만 접근 가능하고, 실제 외부 포트로 연결은 되지 않는다.
    volumes:
      - "redis-data-vol:/data"
    deploy:
      placement:
        constraints: [node.role == manager]
    command: redis-server --appendonly yes
    networks:
      - webnet

networks:
  webnet:

volumes:
  redis-data-vol:

대략 하나의 서비스를 기술 할 때,

등이 필수적인 설정값인듯 하다.

참고로 docker swarm 을 이용해 개발-배포 한다면 network driver 의 기본 설정값은 overlay 이다. overlay 는 여러대의 docker-machine 에 나누어진 container들이 마치 하나의 network 상에 존재하는 것처럼 접속 가능하게 해준다.

참고 1 : networks top-level key 설정 관하여

참고 2 : volumes top-level key 설정 관하여

참고 3 : deploy service-level key 설정 관하여

참고 4 : Docker 의 overlay network 에 대하여

Stack 실행하기

docker-compose.yml 로 하나의 Stack 을 정의했으니, 이제 이 docker-compose.yml 파일을 Manager Node 에 업로드하고

docker-machine scp /local/docker-compose.yml myvm1:/path/to/docker-compose.yml

Manager Node 에서 Stack 을 실행한다. 이때 stack 정의에 참고할 Compose file 도 -c 옵션으로 적어준다.

docker stack deploy -c ./docker-compose.yml

1-3. Docker Compose 와 Docker Machine

Docker Compose 가 하는 역할

  1. Stack-Service-Task 계층 구조를 확립시키고, 컨테이너를 효율적으로 관리하고 연결시킨다.
  2. 원본 Image 가 변경되어서 컨테이너가 recreate 될 때에도, 이전 컨테이너 내용을 복사해서 붙여넣어주므로 데이터 연속성과 유지력을 보완시켜준다. (기본적으로 컨테이너가 recreate 되면 read-write 계층 파일은 모두 사라진다. ) (참고블로그링크)
  3. 불필요한 container recreate 를 줄여준다. 이미지의 변경이 없을 시 컨테이너를 그대로 유지한다.
  4. Docker Swarm 의 로드밸런싱 규칙을 설정한다.

Docker Machine 이 하는 역할

docker 실행 환경이 조성된 가상 머신을 생성하고 관리한다. Docker Swarm 을 구축하고 관리할 때 필수적이다.

2. 스케일 조정

2-1. docker 로드밸런싱(부하분산) : 두가지 패러다임

위의 초기 구축 단계를 따라오다보면 두가지 로드밸런싱의 패러다임을 발견할 수 있다.

두가지 방식 모두 유용하다. 스케일 조정 참 쉽다.

2-2. Swarm Cluster 의 로드밸런싱

Swarm 의 각 노드 Machine 주소로 접속한다면?

결국 각 노드에 존재하는 swarm loadbalancer 가 하나의 docker 실행환경처럼 작동해서 필요한 적절한 컨테이너로 연결해준다.

하지만 어떠한 가상머신(node)이 서비스를 제공하는지 숨기고 싶은 경우, 통상적인 방식대로 Swarm 앞에 로드밸런서를 하나 더 두어야한다. Load Balancing 을 수행하는 Proxy 서버로 Nginx 를 Swarm 앞에 사용하는 tutorial 관련 게시글 을 참조하면 이러한 시도들을 확인할 수 있다.

이전 nginx 관련 포스트에서 설명했던 바와 같이, nginx 에는 기본 로드밸런서 기능이 내장되어있다.

관련글 -> jayneWho(); > Post > Nginx 에 대하여 (Nginx Basic Usage)

또한 Docker Documentation 에도 Swarm 앞에 External Load Balancer 로 HAProxy 를 두는 법을 설명하고 있다. 아래 그림을 참고하자.

3. 코드 배포

위와 같이 클라우드 인프라를 구축해놓았다면, 코드를 배포하는 일은 너무나 쉽다.

  1. 소스 코드 수정 후 docker image build 하기

  2. Docker Hub Repository 에 새로 만든 이미지 push 하기

  3. docker-compose.yml 파일 또는 swarm 에 bind-mount 파일 변경되었다면 docker-machine scp 로 전송\
  4. docker-machine ssh myvm1 "docker stack deploy -c ./docker-compose.yml" 로 Stack 재배포하면, Repository 에서 이미지 새로 끌어와서 업데이트된 컨테이너가 구축됨

3-1. local 에서 개발 테스트

docker-compose-dev.yml 파일을 작성해서, 로컬에서 돌릴만한 Stack 구성을 작성해 local 의 docker engine 에서 이미지와 컨테이너가 잘 작동하는지 테스트하며 개발할 수 있다.


이로써 초기 구축, 스케일조정, 코드배포 까지 개발-배포 과정에서 docker 를 이용하는 방식을 살펴보았다. docker 공식 문서에 충실하게 말이다.

좋은 refernece 발견

위와 같은 배포 flow 대로 충실히 수행한 toy-project 를 github 에서 발견하였다. 실제 코드가 보고싶을 때 “이 프로젝트 Github 링크” 로 들어가 프로젝트 코드를 살피면 좋을 듯 하다. backend service (django)(3 replicas) + frontend service (nginx)(2 replicas) 로 간단하게 구성되어있다. 이 곳에서는 docker stack deploy -c docker-swarm.yml 을 해야한다.

뭔가 아쉽다면

그런데 뭔가 배포한 Swarm 과 Stack 에 관한 시각화와 관리 툴이 있으면 좋겠다는 생각이 든다. 그 때 발견한 것이 Docker Cloud 이다.

Docker Cloud Web Console 이용하여 Swarm 관리

Docker Cloud 를 이용하면 자신의 Swarm 과 그 위에 돌아가는 Stack, Service, Task Container 들을 관리할 수 있다. 자세한 spec 설정까지 가능하다.

자신의 Docker Hub Repository 에 있는 image 들도 불러와 관리할 수 있고, AutoBuild 기능으로 코드에 변화가 생겼을 때 바로바로 image 를 자동 재생성하는 기능 또한 갖췄다.

심지어 새로운 Swarm 이나 이미지를 웹 상에서 만드는 것도 가능하다.

(docker-machine 드라이버를 제공하는 다른 cloud hosting 서비스 업체들은 많지만, swarm 만들기를 지원하는건 아직 amazon 과 azure 뿐인가보다. 다른 서비스업체를 이용할 경우 직접 위 방식대로 swarm 을 만들어야겠다.)

매우 간단한 UI 이므로, 직접 사용해보는 것을 추천한다.

Docker Cloud Documentation 의 Manage Swarms 를 보면서 사용하면 좋을 듯 하다.