Github Container Registry(GCR)에 업로드된 새로운 이미지를 docker compose 환경으로 자동으로 배포하기 위해서는 docker compose를 구성하는 요소가 새로운 이미지의 존재를 확인할 수 있어야 합니다. 대표적으로는 webhook 기반 방법, polling 기반 방법이 있습니다.

Webhook 기반 아키텍처 또는 이와 비슷한 모종의 방식들(HTTP 요청 등)은 실시간 배포가 가능하고 리소스 효율적이지만, 홈 서버 환경에서는 약간의 설정 복잡성이 있을 수 있습니다. GCR에 새 이미지가 푸시되면 즉시 서버의 웹훅 엔드포인트로 알림을 전송하는 방식입니다.

Polling 기반 아키텍처는 GitHub API의 시간당 5,000회 요청 제한(ref2)과 업데이트 지연이라는 문제들이 있음에도 불구하고 구현이 단순하고 방화벽과 같은 걱정에서 자유로울 수 있어 토이 프로젝트, 작은 서비스에서는 더 안정적인 선택이 될 수 있습니다.

번외로 github action에서 빌드가 완료되었을 때 SSH를 이용해 서버에 접근해 image pull을 해 버리는 방법도 있습니다.

Container CD 생태계

Diun

우선 정말 간단한 애플리케이션에 있어서는 CD같이 거창한 것 대신 알림 우선 접근방식이 더 합리적입니다. Diun도 비슷한 맥락으로 알림에만 집중합니다. 프로젝트 페이지를 보면 활발히 개발되고 있는 것을 확인할 수 있습니다. 8MB 미만의 메모리 사용량으로 경량화되어 있고 15개 이상의 알림 채널을 지원합니다. 아래 소개될 Watchtower과 같이 사용하는 사람들도 있습니다.

# Diun 설정 예시
services:
  diun:
    image: crazymax/diun:latest
    volumes:
      - "./data:/data"
      - "/var/run/docker.sock:/var/run/docker.sock"
    environment:
      - "DIUN_WATCH_WORKERS=20"
      - "DIUN_NOTIF_TELEGRAM_TOKEN=YOUR_TOKEN"
    restart: always

하지만 저는 개발 환경의 제약 때문에 완전 자동 CD를 고민하고 있어, 요구사항에 맞지 않아 이정도만 하고 넘어갑니다.

SSH 기반 배포

두 번째로 살펴볼 것은 SSH 기반의 배포입니다. 하지만 이것도 마찬가지로 가볍게만 살피고 넘어가야 할 것 같습니다. 아무리 비밀번호가 걸려 있다고 한들 내 컴퓨터의 특정 포트를 외부로 노출시키는 것은 정말 생각보다 위험한 일이기 때문입니다. 예전에 킥보드를 개발할 때 Jetson 컴퓨터에 특히 중국 IP로부터 브루트 포스 공격을 쉴 새 없이 받았고, 그 결과 로그 파일이 잔뜩 쌓이다 못해 컴퓨터가 용량 부족을 호소하거나 네트워크 대역폭에 영향을 받아 쓰지 못할 지경에 이르렀던 경험을 실제로 해 버렸거든요. 해커가 내 컴퓨터 문턱까지 와서 포트를 더듬거리고 있는 것도 기분나쁜 일인데, 그 더듬음이 도를 지나쳐서 컴퓨터가 고장이 날 정도라면 보안 불감증인 저조차도 생각만 해도 끔찍합니다.

그렇다고 안전하게 SSH 연결을 만들자니, https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/use-cases/ssh/ssh-infrastructure-access/ 이렇게 길고 복잡한 매뉴얼을 읽어야 합니다. 그래서 이 방법도 고려하지 않게 되었습니다. 어쨌든 이런 방법도 가능합니다.

- name: Deploy via SSH
  uses: appleboy/[email protected]
  with:
    script: |
      cd /app
      echo "DOCKER_TAG=${{ github.sha }}" > .env
      docker compose pull
      docker compose up -d --remove-orphans
      docker image prune -f

Portainer

약간의 상용 도구를 찾는다면 Portainer도 살펴볼만 합니다. Portainer는 Docker 환경을 시각적으로 관리해주는 UI 도구입니다. 웹훅 기반의 자동 배포 기능이 상용 라이선스에 한해**(뒤늦게 알았지만-.-)** 내장되어 있습니다. Portainer에서는 웹훅 URL을 생성할 수 있고, 이 URL을 Docker Hub, GitHub Actions, GitLab CI/CD 등의 플랫폼에서 호출하여 사용하는 방식입니다.

마찬가지로 새로운 이미지가 푸시되는 등의 이벤트가 발생하면, 해당 플랫폼이 이 웹훅 URL로 알림(HTTP 요청)을 보냅니다. Portainer는 알림을 받는 즉시 연결된 컨테이너를 최신 이미지로 자동으로 재시작합니다. 직접 docker pull 명령을 실행할 필요 없이, 웹훅 알림 하나로 CD(Continuous Delivery)를 구축할 수 있습니다.

이 가이드를 응용하여 docker compose를 구성할 수 있습니다. 더불어 저는 개인 도메인을 사용하므로, 이 이슈에 따라 TRUSTED_ORIGINS 환경 변수를 설정하여 로그인할 수 있었습니다.

services:
	...
  portainer:
    image: portainer/portainer-ce:lts
    container_name: portainer
    environment:
      - TRUSTED_ORIGINS=portainer.mydomain.com,mydomain.com
    ports:
      - "9000:8000"
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    restart: always

volumes:
  portainer_data:

image.png

image.png