Charm Deployment Flow

본 문서는 Juju 4.0 코드베이스 분석을 기반으로 AI가 정리했습니다.

Overview

juju deploy는 Juju에서 가장 많이 사용되는 명령이다. 이 하나의 명령 뒤에는 charm 해석 → 다운로드 → 앱 생성 → 머신 프로비저닝 → 유닛 배포 → hook 실행이라는 복잡한 파이프라인이 숨어 있다.

flowchart LR
    CLI["juju deploy"] --> API["API Server"]
    API --> DL["Charm Download\n(async)"]
    API --> APP["Application 생성"]
    APP --> UNIT["Unit 생성"]
    UNIT --> MACH["Machine\nProvisioning"]
    MACH --> DEP["Unit Agent\nDeployment"]
    DEP --> HOOK["Hook 실행\n(install → start)"]

Phase 1: CLI → Charm 해석

파일: cmd/juju/application/deploy.go

juju deploy postgresql --base ubuntu@22.04 --constraints "mem=4G cores=2" -n 3

내부 흐름

DeployCommand.Run()
  → NewDeployerFactory()          // 배포 유형 결정
  → GetDeployer()                 // deployer 인스턴스 생성
     - localCharmDeployer         // 로컬 파일 경로
     - repositoryCharmDeployer    // CharmHub
     - bundleDeployer             // 번들
  → deployer.PrepareAndDeploy()
     → determineBaseForCharm()    // OS 베이스 결정
     → client.Deploy(args)        // API 호출

Charm URL 해석

형태의미
postgresql CharmHub Canonical이 운영하는 charm 저장소. Snap Store와 유사한 구조로, charm을 채널(stable/edge 등)별로 배포한다. 에서 최신 stable
postgresql --channel 14/edge특정 채널
./my-charm로컬 charm 디렉토리
local:postgresql이미 controller에 업로드된 charm

Base 결정 우선순위

  1. 사용자 지정 (--base ubuntu@22.04)
  2. charm이 지원하는 베이스 목록
  3. 모델 기본 베이스
  4. charm 메타데이터의 첫 번째 베이스

Phase 2: API Server 처리

파일: apiserver/facades/client/application/deploy.go

API 서버는 DeployApplication()에서 배포를 처리한다:

DeployApplication()
  → 사전 검증
     - subordinate이면 유닛 수 0, constraints 없어야 함
     - charm의 "assumes" 요구사항 충족 확인
  → GetCharmDownloadInfo()        // CharmHub charm이면 다운로드 정보 준비
  → IAAS vs CAAS 분기
     - IAAS: CreateIAASApplication()
     - CAAS: CreateCAASApplication()

IAAS vs CAAS

IAAS (머신 기반)CAAS (Kubernetes)
머신 생성클라우드에서 VM 프로비저닝K8s가 pod 스케줄링
유닛 배치 placement 유닛을 특정 머신이나 컨테이너에 배치하는 지시자. --to 플래그로 지정하며, 새 머신, 기존 머신, LXD 컨테이너 등을 선택할 수 있다. 기반scale 파라미터
charm 포맷v1 또는 v2v2 필수
격리VM 또는 LXD 컨테이너pod

Phase 3: Charm 비동기 다운로드

파일: internal/worker/asynccharmdownloader/downloadworker.go

CharmHub charm은 API 호출을 블로킹하지 않고 비동기로 다운로드된다.

flowchart TB
    API["Deploy API"] -->|"앱 생성\n(charm_download_status=Pending)"| DB[(dqlite)]
    DB -->|"WatchApplicationsWithPendingCharms"| ADW["AsyncCharmDownloader\nWorker"]
    ADW -->|"HTTP GET"| CH["CharmHub"]
    CH -->|"charm.zip"| ADW
    ADW -->|"ResolveCharmDownload()"| DB
    DB -->|"charm 준비 완료"| UNIT["유닛 배포 진행"]
  1. 앱이 charm_download_status = Pending으로 생성됨
  2. AsyncCharmDownloader controller에서 실행되는 워커. pending 상태의 charm을 감지하여 CharmHub에서 병렬로 다운로드한다. 워커 풀로 동시 다운로드를 관리한다. 워커가 감지
  3. CharmHub에서 charm zip 다운로드 (SHA256/SHA384 검증)
  4. blob store에 저장, 메타데이터 DB에 기록
  5. 다운로드 완료 → 유닛 배포 진행

Phase 4: Application & Unit 생성

파일: domain/application/service/application.go

dqlite 레코드 생성

CreateIAASApplication()
  → application 레코드 생성 (UUID, name, charm 참조)
  → 유닛 레코드 생성 (unit_count만큼)
     - 이름: "postgresql/0", "postgresql/1", ...
     - 상태: Allocating
  → 머신 레코드 생성 (placement에 따라)
     - constraints 적용
     - 상태: Pending
  → endpoint bindings 저장
  → storage directives 저장

유닛 상태 전이

Allocating → Pending → Waiting (Installing Agent)
  → Executing (Running Hooks) → Idle → Active

Phase 5: Machine Provisioning

파일: internal/worker/computeprovisioner/computeprovisioner.go

Compute Provisioner 클라우드 프로바이더(AWS, GCP, Azure, MAAS 등)에 가상 머신을 생성하는 워커. constraints를 인스턴스 타입으로 매핑하고, 네트워크 설정과 스토리지 할당을 수행한다. 워커가 Pending 머신을 감지하여 프로비저닝한다:

ProvisionerTask
  → WatchModelMachines()         // Pending 머신 감지
  → ProvisioningInfo 조회
     - constraints (CPU, memory, disk)
     - 네트워크 설정
     - 이미지 스트림
  → 클라우드 프로바이더 호출
     - VM 인스턴스 시작
     - 인스턴스 활성화 대기
  → 인스턴스 등록
     - instance ID 기록
     - hardware characteristics 저장
     - 네트워크 설정 저장

Constraints → 인스턴스 매핑

juju deploy postgresql --constraints "mem=8G cores=4 arch=amd64"
Constraint설명
coresCPU 코어 수
mem메모리 크기
disk루트 디스크 크기
archCPU 아키텍처
spaces네트워크 스페이스 요구사항
virt-type가상화 유형 (kvm, lxd 등)

프로바이더가 이 constraints를 만족하는 최소 인스턴스 타입을 선택한다.


Phase 6: Unit Agent 배포

파일: internal/worker/deployer/deployer.go

머신이 프로비저닝되면, 머신 에이전트의 Deployer 워커 머신에 할당된 유닛을 감지하여 유닛 에이전트 프로세스를 시작하는 워커. 에이전트 설정 파일 생성, 패스워드 설정, systemd 서비스 등록을 수행한다. 가 유닛 에이전트를 배포한다:

deploy()
  → unit.SetStatus(Waiting, "Installing Agent")
  → 랜덤 패스워드 생성
  → unit.SetPassword(password)
  → DeployUnit(unitName, password)
     - 에이전트 디렉토리 구조 생성
     - agent.conf 작성 (API 주소, 인증 정보, 모델 UUID)
     - jujud unit 프로세스 시작 (systemd)

Phase 7: Uniter & Hook 실행

파일: internal/worker/uniter/uniter.go

유닛 에이전트가 시작되면 Uniter 유닛의 생명주기를 관리하는 핵심 워커. charm hook 실행, 관계 관리, 리더십 추적, secret 관리 등을 담당한다. 각 유닛에 하나씩 실행된다. 가 charm의 hook을 순차 실행한다:

flowchart LR
    I["install"] --> CC["config-changed"]
    CC --> S["start"]
    S --> US["update-status\n(5분마다)"]
    US --> US

Hook 실행 환경

# install hook 내부에서 사용 가능한 환경
CHARM_DIR=/var/lib/juju/agents/unit-postgresql-0/charm
JUJU_CHARM_DIR=$CHARM_DIR
JUJU_UNIT_NAME=postgresql/0
JUJU_MODEL_NAME=default

Hook Context 도구 ( jujuc charm hook 내에서 사용할 수 있는 CLI 도구 모음. Juju API를 통해 관계 데이터, 설정, 상태, secret 등에 접근한다. 실체는 jujud 바이너리의 심볼릭 링크이다. )

도구용도
config-getcharm 설정 조회
status-set유닛/앱 상태 설정
relation-get/set관계 데이터 교환
secret-add/getsecret 관리
resource-get리소스 접근
leader-get/set리더 데이터 관리
open-port/close-port포트 관리

실패 처리

상황동작
Hook 실패유닛 상태 → Error, 자동 재시도 안 함
juju resolved실패한 hook 재시도 또는 건너뛰기
일시적 에러워커 레벨 재시도 전략 적용

전체 시퀀스 다이어그램

사용자                CLI              API Server         dqlite
  |                    |                   |                 |
  |-- juju deploy ---->|                   |                 |
  |                    |-- Deploy() ------>|                 |
  |                    |                   |-- 앱 생성 ----->|
  |                    |                   |-- 유닛 생성 --->|
  |                    |                   |-- 머신 생성 --->|
  |                    |<-- OK ------------|                 |
  |<-- deployed -------|                   |                 |

                  AsyncDownloader     CharmHub
                       |                 |
                       |-- GET charm --->|
                       |<-- charm.zip ---|
                       |-- 저장 -------->| (dqlite)

                  Provisioner        Cloud Provider
                       |                 |
                       |-- 머신 감지 --->| (dqlite watch)
                       |-- VM 생성 ---->| (AWS/GCP/...)
                       |<-- instance ID-|
                       |-- 등록 ------->| (dqlite)

                  Machine Agent      Unit Agent
                       |                 |
                       |-- 유닛 감지 -->|
                       |-- 배포 ------->| (jujud unit 시작)
                       |                 |-- install hook
                       |                 |-- config-changed
                       |                 |-- start
                       |                 |-- status-set active

핵심 포인트

  1. 비동기 다운로드: charm 다운로드는 API 호출을 블로킹하지 않고 별도 워커가 처리
  2. IAAS/CAAS 분기: 머신 기반과 Kubernetes 기반 배포가 Phase 4에서 갈라짐
  3. Constraints → Instance: 프로바이더가 constraints를 만족하는 최소 인스턴스를 자동 선택
  4. Hook 순서 보장: install → config-changed → start 순서 엄격 관리, 유닛별 순차 실행
  5. 다단계 상태 전이: Allocating → Pending → Waiting → Executing → Idle → Active
  6. 실패 격리: 한 유닛의 hook 실패가 다른 유닛에 영향을 주지 않음
  7. Trust 모델: --trust 플래그로 클라우드 인증 정보 접근 명시적 허용

관련 문서

  • Relations — 배포 후 앱 간 연결 시 relation hook이 실행되는 흐름
  • Leadership Management — 유닛 배포 후 리더 선출 과정
  • Juju Status — 배포 진행 상태를 juju status로 추적하는 방법
  • Change Stream — 머신/유닛 생성이 워커에 전파되는 이벤트 인프라