Docker와 Kamal로 SQLite Rails 앱을 처음 배포할 때 지켜야 할 순서
첫 배포에서 가장 위험한 것은 빈 volume이다
SQLite Rails 앱의 첫 Kamal 배포에서는 이미지 빌드보다 volume 소유권이 더 자주 문제를 만든다. 컨테이너가 uid 1000의 비루트 사용자로 실행되는데 named volume이 먼저 root 소유로 생성되면 Rails가 DB, WAL, SHM 파일을 만들 수 없다. Litestream accessory가 애플리케이션보다 먼저 volume을 열 때 이런 상태가 발생하기 쉽다.
따라서 첫 배포는 일반적인 재배포와 구분해야 한다. 서버 기본 디렉터리를 준비하고, 애플리케이션 컨테이너가 storage volume을 먼저 초기화한 뒤, DB 쓰기와 운영 점검이 성공한 다음 백업 accessory를 시작한다.
production 이미지를 비루트로 만들기
Dockerfile은 runtime에 필요한 패키지만 포함하고 애플리케이션 파일과 storage 경로를 실행 사용자에게 넘긴다. 이미지 변환을 사용한다면 공유 라이브러리뿐 아니라 진단에 필요한 CLI 패키지도 확인한다.
RUN groupadd --gid 1000 rails \
&& useradd --uid 1000 --gid 1000 --create-home rails
RUN mkdir -p /rails/storage \
&& chown -R rails:rails /rails
USER 1000:1000
WORKDIR /rails
로컬 호스트가 arm64이고 production이 amd64라면 QEMU 결과와 실제 amd64 결과를 구분해 기록한다. 교차 아키텍처에서 발생한 Go runtime이나 Thruster 오류를 애플리케이션 결함으로 단정하지 말고, production CMD는 Thruster를 유지한 채 native amd64에서 최종 검증한다.
안전한 첫 배포 순서
첫 배포에 accessory까지 한꺼번에 시작하는 명령을 사용하기보다 다음 순서를 명시적으로 진행한다.
server bootstrap으로 서버와 Docker 상태를 준비한다.- 첫 app deploy로 uid 1000 컨테이너가 storage volume을 초기화하게 한다.
- migration과 네 SQLite 파일의 생성 상태를 확인한다.
production:check로 DB, S3, queue, URL 설정을 검증한다.- Litestream accessory를 boot하고 네 DB의 replica를 확인한다.
각 단계에서 실패하면 다음 단계로 넘어가지 않는다. 특히 primary만 만들어졌다고 성공으로 보지 말고 cache, queue, cable 파일과 부모 디렉터리의 uid/gid를 함께 확인한다. Solid Queue를 Puma 내부에서 실행한다면 worker thread 외에 dispatcher와 supervisor가 사용할 연결도 필요하므로 queue pool을 지나치게 작게 잡지 않는다.
배포 전후의 검증 게이트
이미지 빌드가 성공한 뒤에는 같은 이미지로 실제 실행 계약을 확인한다.
- 컨테이너 내부
id가 의도한 비루트 사용자다. -
/rails/storage에 DB와 WAL/SHM을 생성할 수 있다. - health endpoint가 200을 반환한다.
- root, Atom feed, sitemap의 content type과 상태가 정상이다.
- migration이 모두 적용되어 있다.
- Solid Queue가 작업을 처리하고 failed job이 증가하지 않는다.
- 컨테이너 재시작 뒤 글과 관리자 데이터가 유지된다.
- Litestream의 네 replica txid가 local과 일치한다.
배포 직후에는 관리자 로그인, 글 조회, 이미지 표시처럼 읽기 경로를 먼저 확인하고, 제한된 테스트 데이터로 쓰기 경로를 검증한다. 운영 글을 수정해 배포 성공을 시험하지 않는다.
롤백은 이미지와 데이터로 나누기
Kamal의 이전 이미지 롤백은 코드 문제를 되돌리는 수단이지 SQLite 데이터를 과거로 되돌리는 명령이 아니다. migration이 포함된 배포라면 이전 코드가 새 schema를 읽을 수 있는지 먼저 판단해야 한다. destructive migration을 즉시 실행하지 않고, 호환 기간을 둔 가역적 변경을 사용해야 이미지 롤백이 가능하다.
데이터 손상이 의심되면 app 쓰기를 중지하고 Litestream PITR 절차로 별도 volume에 복구한다. 현재 volume 위에 바로 덮어쓰지 않는다. 코드 롤백과 데이터 복구를 동시에 시도하면 원인과 복구 기준 시점을 잃기 쉽다.
첫 배포 런북에는 image digest, 실행 architecture, volume 소유권, migration version, health 결과, 백업 시작 시각을 남긴다. 이후 배포는 자동화할 수 있지만 첫 배포의 volume 초기화 순서를 자동화 속에 숨기지 않는 편이 안전하다. 성공 기준은 컨테이너가 떴다는 사실이 아니라 재시작과 롤백을 포함한 전체 운영 계약이 검증된 상태다.
댓글
아직 댓글이 없습니다.
댓글 남기기