작업 일지

K3s에서 Docker로 돌아오면서 다시 만든 것들

사내 서비스를 자체 서버의 K3s에서 돌리다가 Docker로 옮겼다. 혼자 운영하는 규모에는 클러스터 관리가 과했다. 쿠버네티스가 대신 해 주던 일 가운데 이미지 배포, 인증서 발급, 장애 알림은 직접 만들었다.

3분#infra#docker#nginx

사내 서비스는 한동안 자체 서버의 K3s에서 돌았다. 경량 쿠버네티스라 설치는 간단했지만, 서비스를 하나 추가할 때마다 써야 할 설정이 늘었고 클러스터 자체도 따로 관리해야 했다. 혼자 운영하는 규모에서는 그 비용이 얻는 것보다 컸다. 그래서 모두 Docker로 옮겼다.

옮기면서 쿠버네티스가 하던 일을 전부 다시 만들지는 않았다. 없으면 운영이 안 되는 것만 만들었는데, 이미지 배포와 도메인·인증서, 그리고 장애 알림이었다.

이미지 배포

서버의 compose 파일에서 빌드 단계를 지웠다. CI가 서비스마다 이미지를 빌드해 레지스트리에 올리고, 서버는 이미지를 받아서 실행만 한다. 직접 손본 nginx·postgres·n8n 이미지도 같은 방식으로 빌드한다. 예전 설정은 legacy/ 폴더로 옮겼고, 서버를 새로 세울 때는 부트스트랩 스크립트 하나로 시작한다.

CI는 처음에 GitHub Actions 호스팅 러너에서 돌렸는데, 4월 말에 무료 실행 시간을 다 써서 자체 러너로 옮겼다. 빌드가 끝나면 CI가 관리 콘솔에 웹훅을 보내고, 콘솔은 텔레그램으로 승인을 받은 뒤 컨테이너를 교체한다.

도메인과 인증서

전에는 도메인 하나를 추가하려면 네 군데를 고쳐야 했다. nginx 설정 템플릿, 프록시 시작 스크립트의 도메인 배열, compose 파일의 certbot 도메인 목록을 고치고 이미지를 다시 빌드했다. 하나라도 빠지면 인증서가 발급되지 않거나, 발급돼도 nginx가 쓰지 않았다.

지금은 콘솔에서 도메인을 추가하면 끝난다. 그 뒤에 일어나는 일은 이렇다.

  1. 콘솔이 DNS 레코드를 만들고 HTTP 전용 nginx 설정을 쓴다. 이 설정에는 인증서 발급 확인용 경로가 열려 있다.
  2. 프록시 컨테이너 안에서 inotify로 설정 폴더를 보고 있던 프로세스가 새 파일을 감지한다. nginx -t가 통과하면 certbot으로 Let's Encrypt 인증서를 요청한다.
  3. 인증서 폴더를 보는 다른 프로세스가 새 인증서를 감지하고 nginx를 다시 읽힌다.
  4. 콘솔은 30초마다 도메인별 인증서 파일을 확인하고, 인증서가 생겼으면 설정을 HTTPS용으로 다시 써서 적용한다. 사람이 직접 고친 설정은 덮어쓰지 않는다.

갱신은 cron이 12시간마다 certbot renew를 실행하고, 갱신된 인증서가 있으면 nginx를 다시 읽힌다.

처음에 놓친 것

첫 버전의 발급 함수는 certbot을 --quiet로 실행하고 출력에서 빈 줄을 걸렀다. 그래서 발급이 실패해도 로그에 이유가 남지 않았다. 80번 포트가 막혔는지, 발급 횟수 제한에 걸렸는지, 확인 경로가 맞지 않았는지 구분할 수 없었다. 다음 날 --verbose로 바꾸고, 파이프를 거치기 전 certbot의 종료 코드를 따로 받고, 실패하면 certbot 로그의 마지막 40줄을 도메인 이름과 함께 컨테이너 로그에 남기게 했다.

인증서 볼륨에 ACME 계정이 둘 이상 있을 때도 막혔다. 이때 certbot은 어느 계정을 쓸지 묻고 기다리는데, 비대화형 모드라 답할 사람이 없다. 가장 최근 계정을 명시해서 넘기도록 고쳤다.

알림

외부 모니터링 서비스는 쓰지 않는다. 콘솔이 5분마다 서버별 CPU·메모리·디스크 사용량과 컨테이너 상태를 기록해 48시간 동안 보관한다. 사용량이 90%를 넘거나 컨테이너가 실행 중이 아니면 텔레그램으로 알리고, 같은 알림은 일정 시간 동안 다시 보내지 않는다. 멈춘 컨테이너는 재시작 정책으로 다시 올라온다. GPU 서버는 7월에 재시작 정책을 바꿔서, 전원이 꺼졌다 켜져도 컨테이너가 스스로 올라오게 했다.

포기한 것

여러 서버에 걸친 자동 배치와 자동 확장은 없다. 서비스마다 도는 서버가 정해져 있어서, 서버가 멈추면 그 서버의 서비스도 같이 멈춘다. 지금 규모에서는 이 정도로 충분하다고 봤다.