서비스 생성과 리플리케이션

도커 스웜 클러스터를 만들었다면 서비스를 생성할 수 있다. 서비스를 만드는 것은 어렵지 않다. 매니저 노드에서 docker create service 명령어를 실행하면 된다. 서비스를 생성할 때 --replicas 옵션을 사용해서 생성할 컨테이너 개수를 지정한다.

docker service create --name simple \
--publish published=8000,target=5000 \
--replicas 2 \
madvirus/simplenode:0.1

서비스를 생성한 뒤 "docker service ps 서비스명" 명령어를 실행하면 각 컨테이너가 어느 도커 노드에서 실행 중인지 확인할 수 있다.

vagrant@docker-node1:~$ docker service ps simple
ID                  NAME                IMAGE                     NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
lecwvwjcs7lm        simple.1            madvirus/simplenode:0.1   docker-node2        Running             Running 5 minutes ago
wap8ua3proil        simple.2            madvirus/simplenode:0.1   docker-node3        Running             Running 6 minutes ago

결과에서 NAME은 컨테이너의 완전한 이름이 아닌 서비스 내에서 구분하기 위한 이름이다. 실제 컨테이너 이름은 simple.2.wap8ua3proil1aqircsy7tifq와 같이 NAME 뒤에 ID를 결합한 문자열을 사용한다.

docker servie scale 명령어를 사용하면 운영 중에 리플리케이션 개수를 변경할 수 있다.

$ docker service scale simple=4
simple scaled to 4
overall progress: 4 out of 4 tasks
1/4: running   [==================================================>]
2/4: running   [==================================================>]
3/4: running   [==================================================>]
4/4: running   [==================================================>]
verify: Service converged

$ docker service ps simple
ID                  NAME                IMAGE                     NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
lecwvwjcs7lm        simple.1            madvirus/simplenode:0.1   docker-node2        Running             Running 18 minutes ago
wap8ua3proil        simple.2            madvirus/simplenode:0.1   docker-node3        Running             Running 19 minutes ago
v90ftwuywm0d        simple.3            madvirus/simplenode:0.1   docker-node1        Running             Running about a minute ago
674upghwilm2        simple.4            madvirus/simplenode:0.1   docker-node1        Running             Running about a minute ago

docker service ps 명령어를 실행하면 simple 서비스의 컨테이너 개수가 4개가 된 것을 확인할 수 있다. 개수를 늘리는 것 뿐만 아니라 줄이는 것도 가능하다.

$ docker service scale simple=1
simple scaled to 1
overall progress: 1 out of 1 tasks
1/1: running   [==================================================>]
verify: Service converged

$ docker service ps simple
ID                  NAME                IMAGE                     NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
lecwvwjcs7lm        simple.1            madvirus/simplenode:0.1   docker-node2        Running             Running 20 minutes ago

리플리케이션 개수를 줄일 때는 가장 최근에 생성한 컨테이너를 먼저 중지한다.

서비스 제거

서비스 제거는 간단하다. "docker service rm 서비스명" 명령어를 사용하면 된다. 서비스를 제거하면 해당 컨테이너도 함께 삭제된다.

simplenode:0.2 버전

롤링 업그레이드와 헬스 체크를 테스트하기 위해 madvirus/simplenode:0.2 버전 이미지를 새로 추가했다. 이 이미지는 다음의 app.js를 실행한다.

const http = require('http');
const url = require('url');

const server = http.createServer().listen(5000);
let slow = false;

server.on('request', (req, res) => {
    console.log('request arrived.');
    const query = url.parse(req.url, true).query;
    if (query.stop === 'true') {
        res.write("Process down: " + process.env.HOSTNAME);
        res.end();
        setTimeout((arg) => { process.exit(1); }, 1000, "shutdown");
    } else if (query.slow === 'true') {
        slow = true;
        res.write("Health check slowdown");
        res.end();
    } else if (req.url === '/health') {
        if (slow) {
            setTimeout((arg) => { res.write("SLOW"); res.end(); }, 5000, "slow");
        } else {
            res.write("OK");
            res.end();
        }
    } else {
        res.write("Hello V0.2: " + process.env.HOSTNAME);
        res.end();
    }
});

server.on('connection', (socket) => {
    console.log("Connected");
});

다음은 이 app.js의 주요 내용이다.

  • 헬스체크를 위한 /health 경로를 처리한다.
    • slow 값이 true면 /health 응답을 5초 뒤에 한다. 이것으로 헬스체크 실패를 흉내낸다.
    • slow 기본 값은 false이다.
    • slow 파라미터가 'true'면 slow를 true로 바꾼다.
  • stop 파라미터가 'true'면 1초 뒤에 프로세스를 중지한다.

롤링 업그레이드

madvirus/simplenode:0.1 버전을 이용한 서비스를 madvirus/simplenode:0.2 버전으로 업그레이드해보자. docker service update 명령어를 사용하면 된다. 다음은 실행 예이다.

$ docker service update \
--update-parallelism 1 \
--update-delay 10s \
--update-order start-first \
--image madvirus/simplenode:0.2 \
simple

업데이트 관련 주요 옵션은 옵션은 다음과 같다.

  • --update-parallelism : 동시에 업데이트할 컨테이너 개수를 지정한다.
  • --update-delay : 업데이트 간 간격을 지정한다.
  • --update-order : start-first면 새 컨테이너를 먼저 생성한 뒤에 기존 컨테이너를 삭제한다. stop-first면 기존 컨테이너를 먼저 삭제하고 그 다음에 새 컨테이너를 생성한다.
  • --update-failure-action : 업데이트에 실패할 경우 이 값이 pause면 업데이트를 멈추고, continue면 업데이트를 계속하고, rollback이면 업데이트를 롤백한다.
  • --update-max-failure-ratio : 실패 비율이 지정한 값 이상이면 업데이트 실패로 간주한다.

서비스 업데이트 후에 docker service ps 명령어로 서비스를 조회하면 다음과 같이 simplenode:0.1 버전은 중지되고 simplenode:0.2 버전이 실행 중인 것을 확인할 수 있다.

vagrant@docker-node1:/vagrant/sample/simplenode$ docker service ps simple
ID                  NAME                IMAGE                     NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
sqyy4m1846qu        simple.1            madvirus/simplenode:0.2   docker-node3        Running             Running 35 seconds ago
78c9glgwxr01         \_ simple.1        madvirus/simplenode:0.1   docker-node2        Shutdown            Shutdown 31 seconds ago
1zynj40j8a5v        simple.2            madvirus/simplenode:0.2   docker-node2        Running             Running 19 seconds ago
z0pz24xqpmna         \_ simple.2        madvirus/simplenode:0.1   docker-node1        Shutdown            Shutdown 15 seconds ago

상태 유지와 헬스 체크

simple 서비스를 생성할 때 리플리카의 개수를 2로 지정했다. 도커 스웜은 simple 서비스의 컨테이너 개수를 2개로 유지하기 위해 노력한다. 예를 들어 컨테이너 한 개가 종료되면 새로운 컨테이너를 구동해서 정상 동작하는 컨테이너 개수를 맞춘다.

실제로 컨테이너 한 개를 종료해보자. http://192.168.1.101:8000/?stop=true을 요청하면 컨테이너 중 한 개가 종료된다. 컨테이너가 종료되면 도커 스웜은 새로운 컨테이너를 바로 생성해서 컨테이너 개수를 2로 맞춘다.

헬스 체크 옵션을 주면 컨테이너가 정상 상태가 아닐 때 컨테이너를 중지하고 새로운 컨테이너를 생성할 수 있다. 헬스 체크 관련 옵션을 사용하면 생성한 컨테이너가 정상 상태인지 주기적으로 확인한다. 도커 스웜은 컨테이너의 상태를 주기적으로 확인해서 정상이 아니면 컨테이너를 제거하고 리플리케이션 개수에 맞게 새로 생성한다. 헬스 체크와 관련된 옵션은 --health로 시작하며 다음은 주요 옵션의 사용 예를 보여준다.

docker service create \
--name simple \
--publish published=8000,target=5000 \
--health-cmd 'curl -f http://localhost:5000/health' \
--health-timeout=3s \
--health-retries=3 \
--health-interval=10s \
--health-start-period=10s \
--replicas=2 \
madvirus/simplenode:0.2

주요 옵션은 다음과 같다.

  • --health-cmd : 정상 상태인지 확인할 때 사용할 명령어를 입력한다. 이 명령어는 컨테이너 내에서 실행된다.
  • --health-timeout : 명령어의 실행 시간 제한을 지정한다. 이 시간 내에 명령어 실행이 끝나면 정상이 아닌 것으로 판단한다.
  • --health-retries : 지정한 횟수만큼 연속해서 실패하면 정상이 아닌 것으로 간주한다.
  • --health-interval : 헬스 체크를 실행할 주기를 지정한다.
  • --health-start-period : 컨테이너 시작 후 지정한 시간 동안은 헬스 체크에 실패해도 실패로 간주하지 않는다.

헬스 체크가 잘 되는지 확인해보자. 두 개의 컨테이너가 떠 있다고 가정하고 http://192.168.1.101:8000/?slow=true 요청을 보내자. 앞서 app.js 코드를 보면 slow 파라미터 값이 true이면 /health 요청의 응답 시간을 5초로 바꾼다. 헬스 체크 제한 시간은 3초이므로 이 URL을 실행하면 컨테이너의 헬스체크에 실패한다.

헬스 체크 간격이 10초이고 3회 시도하므로 잠시 뒤에 simple service ps simple 명령어로 컨테이너 목록을 보자. 한 컨테이너가 실패 상태에 있고(Failed) 이를 대체할 새로운 컨테이너를 준비한 것을 확인할 수 있다.

$ docker service ps simple
ID                  NAME                IMAGE                     NODE                DESIRED STATE       CURRENT STATE                   ERROR                              PORTS
qzxmt802h7b9        simple.1            madvirus/simplenode:0.2   docker-node2        Running             Running 2 minutes ago
drmlj2bh7ltw        simple.2            madvirus/simplenode:0.2   docker-node3        Ready               Ready less than a second ago
cdumn8jafb56         \_ simple.2        madvirus/simplenode:0.2   docker-node3        Shutdown            Failed less than a second ago   "task: non-zero exit (143): do…"

서비스와 호스트 연결

서비스를 생성할 때 --publish (또는 -p) 옵션을 사용하면 호스트의 포트에 서비스의 포트를 연결해서 외부에 서비스를 개시할 수 있다.

docker service create \
--name simple \
--publish published=8000,target=5000 \
--replicas=2 \
madvirus/simplenode:0.2

스웜에서 외부에 서비스를 개시하면 스웜에 참여하는 모든 노드가 서비스에 대한 요청을 처리한다. 위 코드는 호스트의 8000 포트를 이용해서 서비스를 개시했는데 이 경우 스웜에 참여한 모든 노드는 8000 포트로 오는 요청을 서비스의 5000 포트로 전달한다. 호스트로 오는 요청을 실제 컨테이너로 전달하는 것은 ingress 네트워크를 통해 처리되는데 이에 대한 내용은 뒤에서 다시 살펴본다.

모드 종류

도커 서비스는 리플리케이션과 글로벌의 두 모드가 있다. 서비스를 생성할 때 --mode 옵션을 이용해서 모드를 지정할 수 있다. 모드에는 다음 두 값이 올 수 있다.

  • replicated : 기본 값으로 지정한 개수만큼 컨테이너를 생성한다.
  • global : 각 노드마다 한 개의 컨테이너를 생성한다.
https://docs.docker.com/engine/reference/commandline/service_create/ 문서에서 더 많은 서비스 관련 옵션을 확인할 수 있다.

관련 글

 

 

+ Recent posts