제가 사용하는 VPS 배포 필수 도구 Kamal2 입문 가이드

지금 제가 사용하는 풀스택 프레임워크는 주로 Next.js 16과 Rails 8입니다. Rails를 사용하면서 공식 추천 배포 도구인 Kamal을 알게 되었습니다. 평소에 이를 이용해 RN과 알리바바 클라우드 서버에 프로젝트를 배포하고 있습니다. 사용하기가 매우 편리하고 간단해서, 이제 여러분께 Kamal2의 개념과 입문 사용법을 소개해 드리려고 합니다.
이 글은 처음부터 너무 복잡한 내용을 다루지 않을 것입니다. 먼저 Kamal2의 가장 핵심적인 부분을 명확히 설명하고자 합니다: 그것이 무엇인지, 우리를 위해 무엇을 할 수 있는지, 가장 기본적으로 어떻게 시작하는지, 그리고 평소에 가장 많이 사용하는 작업이 무엇인지입니다. 마지막으로 Next.js 사용 사례를 제공하여 앞의 개념을 구체적인 프로젝트에 적용할 수 있도록 도와드리겠습니다.
Kamal2란 무엇인가
한 문장으로 요약하자면, Kamal2는 **SSH + Docker를 통해 애플리케이션 배포를 수행**하는 도구입니다. Heroku와 같은 완전한 플랫폼 기능을 제공하지 않으며, Kubernetes 같은 무거운 오케스트레이션 시스템도 아닙니다. 오히려 '여러 Docker 컨테이너를 배포하는' 일을 반복 가능한 명령과 구성으로 정리한 것에 가깝습니다.
Kamal의 핵심 아이디어는 사실 매우 직관적입니다:
로컬에 표준
Dockerfile이 있습니다.config/deploy.yml을 사용하여 배포 대상과 이미지 정보를 설명합니다.SSH를 통해 서버에 연결하고 서버에서 Docker 환경을 준비합니다.
로컬에서 이미지를 빌드하고 이미지 저장소에 푸시한 후 서버에서 가져와 실행하도록 합니다.
kamal-proxy를 사용하여 80 및 443 포트를 처리하고 새 컨테이너의 상태 점검이 통과되면 트래픽을 전환합니다.
이것이 제가 Kamal을 좋아하는 이유이기도 합니다. 너무 많은 추가 추상화를 도입하지 않아서, 무엇을 하는지 대략 알 수 있기 때문에 문제가 발생해도 비교적 쉽게 추적할 수 있습니다.
Kamal2에 적합한 시나리오
제 생각에 Kamal은 다음과 같은 상황에 특히 적합합니다:
이미 Docker를 사용하여 애플리케이션을 패키징하고 있습니다.
한 대 또는 여러 대의 자체 Linux 서버를 보유하고 있습니다.
배포 프로세스를 최대한 간단하게 유지하고 복잡한 플랫폼을 유지 관리하지 않기를 원합니다.
Rails, Next.js, 심지어 다른 웹 서비스도 동일한 배포 방식을 사용할 수 있기를 원합니다.
다시 말해, Kamal은 Docker를 '대체'하는 것이 아니라 Docker 배포를 더 편리하게 만드는 데 도움을 줍니다.
먼저 몇 가지 기본 개념 이해하기
본격적으로 시작하기 전에 Kamal에서 가장 중요한 몇 가지 개념을 기억해 두는 것이 좋습니다.
config/deploy.yml
이것은 Kamal의 주 구성 파일이며, 배포 구성은 여기에서 읽어옵니다. '이번 배포의 설명서'로 이해할 수 있습니다: 서비스 이름, 이미지 이름, 서버 목록, 이미지 저장소, 환경 변수, builder, proxy 등의 내용이 여기에 작성됩니다.
.kamal/secrets
이것은 secrets 파일의 기본 위치이며, Kamal은 여기에서 민감한 변수를 읽습니다. 예를 들어 이미지 저장소 비밀번호나 Rails의 RAILS_MASTER_KEY는 일반적으로 deploy.yml에 직접 하드코딩되지 않고 .kamal/secrets를 통해 주입됩니다.
kamal setup
이것은 첫 배포에서 가장 중요한 명령어입니다. 문서에 명확히 설명되어 있듯이, 서버에 연결하고 Docker를 설치(없는 경우)하고, 이미지 저장소에 로그인하고, 이미지를 빌드하고, 푸시하고, 가져오고, kamal-proxy를 시작하고, 새 컨테이너를 시작한 후 GET /up이 200 OK를 반환하면 트래픽을 전환합니다.
kamal deploy
이것은 후속 릴리스에서 가장 자주 사용되는 명령어입니다. 첫 번째 setup이 성공적으로 실행된 후에는 보통 kamal deploy로 배포를 완료합니다.
Kamal2 설치 방법
로컬에 Ruby 환경이 이미 있다면 가장 직접적인 설치 방법은 다음과 같습니다:
gem install kamal이것은 공식 문서에서 제시하는 표준 설치 방법입니다. Ruby가 없다면 Docker화된 Kamal을 실행할 수도 있지만, 공식 문서에서 이 방식에는 몇 가지 제한이 있다고 명확히 언급하고 있으므로 입문 단계에서는 gem을 사용하여 직접 설치하는 것을 더 추천합니다.
설치가 완료된 후에는 버전을 확인할 수 있습니다:
kamal version프로젝트 초기화
프로젝트 디렉토리로 이동한 후 다음을 실행합니다:
kamal init이 명령어는 Kamal에 필요한 기본 파일을 초기화하며, 가장 중요한 것은 config/deploy.yml과 .kamal/secrets입니다.
프로젝트에 이미 Dockerfile이 있다면 사실상 중요한 전제 조건이 갖춰진 것입니다. Kamal의 배포 프로세스는 기본적으로 프로젝트 루트 디렉토리의 표준 Dockerfile을 중심으로 이미지를 빌드합니다.
최소한의 사용 가능한 구성
입문 시에는 아주 작은 config/deploy.yml부터 시작하는 것이 좋습니다. 공식 문서에서 제공하는 최소 예제는 대략 다음과 같습니다:
service: myapp
image: your-registry-user/myapp
servers:
- 203.0.113.10
registry:
username: your-registry-user
password:
- KAMAL_REGISTRY_PASSWORD
builder:
arch: amd64
env:
secret:
- AUTH_SECRET이 구성에서 가장 먼저 이해해야 할 항목은 다음과 같습니다:
service: 필수, 컨테이너 이름 접두사로 사용됩니다.image: 이미지 이름, 최종적으로 레지스트리로 푸시됩니다.servers: 배포 대상 머신 목록.registry: 이미지 저장소 구성.env.secret: secrets 파일에서 읽어와야 하는 환경 변수.builder.arch: 빌드 아키텍처, 문서 예제에서는 직접amd64를 사용했습니다.
이 단계에서는 구성이 완벽할 필요는 없으며, 가장 기본적인 연결이 작동하도록 보장하는 것이 더 중요합니다.
secrets 구성
다음으로 .kamal/secrets를 준비해야 합니다. 예:
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
AUTH_SECRET=your-auth-secretRails 프로젝트의 경우 문서 예제에서는 config/master.key에서 RAILS_MASTER_KEY를 읽어옵니다. 이것이 Rails와 Kamal이 자연스럽게 결합되는 이유 중 하나이기도 합니다: Rails 새 프로젝트는 자체적으로 이 배포 방식을 적용하기가 비교적 쉽기 때문입니다.
첫 번째 배포 시 발생하는 일
구성과 secrets가 모두 준비되면 다음을 실행할 수 있습니다:
kamal setup이 명령어는 많은 작업을 수행하지만, '완전한 첫 배포'로 이해할 수 있습니다. 공식 문서에 따르면 최소한 다음 작업을 수행합니다:
SSH를 통해 서버에 연결합니다.
서버에 Docker가 없으면 자동으로 설치합니다.
로컬과 원격에서 이미지 저장소에 로그인합니다.
프로젝트 루트 디렉토리의 Dockerfile을 사용하여 이미지를 빌드합니다.
이미지를 레지스트리로 푸시합니다.
서버가 이미지를 가져오도록 합니다.
kamal-proxy가 80/443 포트에서 작동하는지 확인합니다.새 컨테이너를 시작합니다.
GET /up이200 OK를 반환할 때까지 기다립니다.트래픽을 새 컨테이너로 전환합니다.
이전 컨테이너를 중지하고 사용하지 않는 이미지와 중지된 컨테이너를 정리합니다.
여기서 매우 중요한 작은 점은 **상태 점검 기본값이 GET /up**이라는 것입니다. 따라서 애플리케이션에 이러한 경량 상태 점검 엔드포인트가 있는 것이 좋습니다. 그렇지 않으면 새 컨테이너가 시작되었더라도 Kamal이 트래픽을 전환하지 않을 수 있습니다.
후속 배포는 훨씬 간단합니다
첫 번째 setup이 성공한 후에는 보통 다음을 실행하기만 하면 됩니다:
kamal deploy문서에서는 이미 이를 후속 배포 명령어로 명확히 지정하고 있습니다. '정상 릴리스 진입점'으로 이해할 수 있습니다: 이미지 재빌드, 푸시, 가져오기, 새 컨테이너 시작, 상태 확인, 트래픽 전환, 이전 컨테이너 중지.
따라서 사용 경험 측면에서 Kamal의 가장 편리한 점은 첫 번째 배포에 약간 더 준비를 하면 이후에는 훨씬 안정적이라는 것입니다.
자주 사용하는 작업
입문 단계에서는 다음 명령어들이 가장 실용적이라고 생각합니다:
초기화
kamal init기본 배포 파일을 생성합니다.
첫 번째 배포
kamal setup서버, Docker, 이미지, 프록시 및 애플리케이션을 처음으로 전체적으로 실행합니다.
후속 릴리스
kamal deploy일상에서 가장 자주 사용하는 명령어입니다.
다중 환경 배포
나중에 staging과 production을 구분하기 시작하면 Kamal은 -d를 사용하여 destination을 지정하는 것을 지원합니다. 예:
kamal deploy -d staging공식 문서에 따르면 이 경우 Kamal은 config/deploy.staging.yml을 기본 구성과 함께 병합하여 사용합니다.
이 기능은 매우 실용적이지만, 입문자라면 존재만 알아두고 바로 사용하려고 서두르지 않아도 됩니다.
왜 Rails와 잘 어울리는가
Rails 8 새 프로젝트는 일반적으로 이미 Dockerfile을 포함하고 있으며, Kamal의 배포 프로세스는 Dockerfile을 중심으로 이루어집니다. 게다가 Rails 생태계는 원래 Ruby gem 도구 체인을 자연스럽게 수용하므로, Rails를 통해 Kamal을 접하는 것은 거의 당연한 일입니다.
저 개인적으로는 Rails 프로젝트에서 이 방식을 먼저 접했기 때문에 이를 진지하게 장기적으로 사용 가능한 배포 솔루션으로 고려하기 시작했습니다.
Next.js 입문 사용 사례
마지막으로 제 일상에 더 가까운 예를 들어보겠습니다: Kamal로 Next.js 프로젝트를 배포하려면 일반적으로 표준 Docker 애플리케이션으로 만든 후 Kamal에 맡겨 배포합니다.
1단계: Next.js를 standalone으로 출력
Next.js 문서에 따르면 output: 'standalone'을 활성화하면 빌드 결과물이 .next/standalone을 생성하며, 여기에는 배포 실행에 필요한 최소 파일과 server.js가 포함됩니다. 이는 Docker 배포에 매우 적합합니다. 전체 개발 환경을 그대로 복사할 필요가 없기 때문입니다.
next.config.js에서 다음과 같이 작성할 수 있습니다:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
}
module.exports = nextConfig주의해야 할 점은 Next.js 문서에서 output: 'standalone'의 실행 방식은 생성된 server.js를 직접 사용하는 것이 더 적합하며, next start를 계속 사용하는 것은 권장되지 않는다고 언급한다는 것입니다.
2단계: Dockerfile 준비
간단한 Next.js Dockerfile은 다음과 같을 수 있습니다:
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]여기서 중요한 점은 Docker 기술 자체가 아니라 접근 방식입니다: **먼저 Next.js를 표준적이고 실행 가능한 컨테이너로 만든 다음 Kamal이 배포를 인계받도록 하는 것입니다.**
3단계: Kamal 구성 작성
예:
service: my-next-app
image: your-registry-user/my-next-app
servers:
- 203.0.113.10
registry:
username: your-registry-user
password:
- KAMAL_REGISTRY_PASSWORD
builder:
arch: amd64
env:
clear:
PORT: 3000
NODE_ENV: production이 구성의 논리는 매우 간단합니다:
Kamal은 서비스 이름을 알고 있습니다.
Kamal은 이미지를 어디로 푸시해야 하는지 알고 있습니다.
Kamal은 어떤 서버에 배포해야 하는지 알고 있습니다.
컨테이너가 시작되면 애플리케이션은 3000 포트를 수신하고 proxy가 외부 트래픽을 처리합니다.
4단계: 상태 점검 엔드포인트 추가
Kamal이 기본적으로 GET /up을 확인하기 때문에 Next.js에서 가장 간단한 라우트를 추가합니다. 예를 들어 App Router에서:
export async function GET() {
return Response.json({ ok: true })
}이를 예를 들어 app/up/route.ts에 배치하면 /up이 간단한 성공 응답을 반환합니다. 이 엔드포인트는 가벼울수록 좋으며, 목적은 단지 Kamal에게 컨테이너가 트래픽을 받을 준비가 되었음을 알리는 것입니다.
5단계: 배포
마지막으로 Kamal의 표준 프로세스로 돌아갑니다:
kamal setup후속 업데이트:
kamal deploy이 프로세스가 Next.js 프로젝트에서 안정적으로 작동한다면, Kamal이 실제로 Rails인지 여부는 중요하지 않으며, **표준 Docker 이미지와 상태를 확인할 수 있는 웹 서비스를 제공할 수 있는지**가 진정으로 중요하다는 것을 알게 될 것입니다.
이 글은 여기까지입니다
저처럼 Rails 8에서 Kamal을 접하고 점차 이 방식을 Next.js 16 프로젝트로 옮겨가고 있다면, Kamal2의 위치는 실제로 이해하기 쉽습니다: 플랫폼이 아니라 자체 호스팅 Docker 배포를 더 체계적으로 만드는 도구입니다.
입문 단계에서는 다음을 먼저 익히는 것으로 충분합니다:
Kamal 설치.
구성 파일 초기화.
deploy.yml과.kamal/secrets이해.kamal setup을 사용하여 첫 번째 배포 수행.kamal deploy를 사용하여 후속 업데이트 수행.상태 점검이 기본적으로
GET /up에 의존한다는 것을 인지.
이러한 것들이 원활하게 작동하게 된 후에 다중 환경, accessories, hooks, 복잡한 프록시 구성을 알아보는 것이 더 자연스러울 것입니다.
Google에서 팔로우
HeyBinyang을 Google 선호 소스로 추가
Google에서 내 업데이트를 더 쉽게 찾고 싶다면 이 사이트를 선호 소스로 표시할 수 있습니다.
공유
공유
이 글을 공유합니다.