1GB, 1vCPU, 그것이 내 무료 인스턴스의 성능이다. 쿠버네티스를 돌리기엔 너무 약하다. 무중단 배포를 검색해보면 초록색 compose.yml, 파란색 compose.yml, 쉘 스크립트와 환경 변수로 구현하는 예시들이 있다. 마음에 들지 않아서 진입장벽이 낮고 조금 더 안정적인 방법을 찾아보았다.
Swarm mode is an advanced feature for managing a cluster of Docker daemons.
도커 스웜은 여러 도커 컨테이너를 지가 관리해주는 기능이다. 누가 인수해서 개발한다고는 하는데 쿠버한테 밀려서 거의 유지보수만 하는 듯 하다. 더 좋다, 이 글의 내용이 낡을 일이 없을테니까.
이 글의 방식으로는 스웜이 램을 10MB 내외로 사용해서 아주 좋았다.
아래 명령어로 스웜을 만들어준다. (쿠버는 쓸 줄 몰라서 비교는 못해요)
docker swarm init
실행하면
Swarm initialized: current node (******) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-*-****** 10.0.0.204:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
이런 메시지가 뜬다. 스웜을 만들고 현재 노드가 매니저가 되었다. 무료 인스턴스 여러 개 만들어서 진짜 스웜을 구성할건 아니니까 넘어가자.
내 소중한 무료 인스턴스의 힘을 도커 빌드 따위에 쓸 수는 없다. Go 바이너리 하나 빌드하는데 10분 걸리고 그 동안 요청 응답시간은 몇십ms로 늘어난다. 그래서 GitHub Actions에서 도커 빌드를 완료해서 가져온다.
도커 허브는 써본 적도 없고 어차피 ssh로 접속해서 명령어를 수행할 예정이기 때문에 scp로 파일을 전송한다. 이미지를 파일로 내보낼 때는 docker save를 사용하면 된다.
docker save {image-name} | gzip > {filename}.tar.gz
인스턴스에서 파일을 불러올 때는 load 하면 된다.
docker load -i /.../{filename}.tar.gz
.tar 파일도 되고 gzip 압축 파일을 넣어도 도커가 알아서 해제해준다. 인스턴스 도커의 이미지 목록에 {image-name}:latest가 추가되니 이걸 사용할 것이다.
처음 한 번만 인스턴스에서 서비스를 생성하고, GitHub Actions로는 이미지만 보내서 업데이트하도록 할 예정이다. 서비스 생성은 아래와 같이 한다.
docker service create \
--name {service-name} \
--publish=80:1323 \
--update-order=start-first \
{image-name}
--publish: 컨테이너의 서빙 포트에 맞게 설정한다. Go Echo는 1323 포트를 사용한다. OCI가 무료 로드밸런서도 줘서 80 포트로 열고 https는 따로 처리할 예정이다.--update-order: start-first와 stop-first 값이 있다. 기본값은 stop-first인데 이러면 현재 컨테이너를 먼저 치우고 새로운 컨테이너를 실행하므로 그 사이에 응답을 못하게 되니 start-first로 하자.--replicas: 램이 남는다면 이 옵션으로 동시에 여러 컨테이너를 실행할 수도 있다. 업데이트 시 동시 업데이트 개수, 딜레이 등을 설정할 수 있다. 근데 굳이 안해도 되었다.--stop-grace-period: 컨테이너 종료 시 남아있는 요청을 처리할 시간이다. 기본값은 10s이다. 이 시간이 지나면 서버가 강제로 종료된다. 10초로 부족하면 늘리자.더 많은 옵션은 docker service create 에서 확인할 수 있다.
실행 결과
q1w2ew3r4t5y6u7i8o9p0q1w2
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service q1w2ew3r4t5y6u7i8o9p0q1w2 converged
GitHub Actions에서 새로운 이미지를 빌드하면 스웜한테 바꿔달라고 요청한다. 이미지 파일을 불러와서 ssh로 아래 명령어를 수행하면 끝이다.
docker service update {service-name} --image {image-name}
실행 결과
image {image-name}:latest could not be accessed on a registry to record
its digest. Each node will access itemsim-server:latest independently,
possibly leading to different nodes running different
versions of the image.
q1w2ew3r4t5y6u7i8o9p0q1w2
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service q1w2ew3r4t5y6u7i8o9p0q1w2 converged
허브에 올린다면 위에 경고 메시지는 안뜰 것 같다.
업데이트 도중에 요청이 끊기지 않을까? 생각이 들어 간단하게 js로 테스트 클라이언트를 만들어서 테스트해 보았다.

워낙 성능이 안좋아 업데이트하는 시간 동안 응답 속도가 간헐적으로 느려진다. 그래도 50ms정도면 참을 만 하다. (한국에서 클라우드플레어 쓰면 캐싱해도 200ms 넘게 걸리던데)
서비스가 실행 중인지 확인하려면
docker service ls
컨테이너가 실행 중인지 확인하려면
docker ps
을 입력하면 된다.
서비스 속성을 수정하고 싶다면 docker service update ...를 사용하자.
이렇게 실행해도 Go 코드를 포함해서 CPU ~1%, 메모리 2% 정도를 사용한다. 오버헤드가 거의 없다. 초록색 파란색 컴포즈보다 편한 스웜 많이 사랑해주세요. (스웜의 컴포즈 격인 stack도 있어요)
내용이 잘못됐거나, 안되거나, blue-green에 비해 단점이 있다면 댓글로 알려주세요.