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 결정 우선순위
- 사용자 지정 (
--base ubuntu@22.04) - charm이 지원하는 베이스 목록
- 모델 기본 베이스
- 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 또는 v2 | v2 필수 |
| 격리 | 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["유닛 배포 진행"]
- 앱이
charm_download_status = Pending으로 생성됨 - AsyncCharmDownloader controller에서 실행되는 워커. pending 상태의 charm을 감지하여 CharmHub에서 병렬로 다운로드한다. 워커 풀로 동시 다운로드를 관리한다. 워커가 감지
- CharmHub에서 charm zip 다운로드 (SHA256/SHA384 검증)
- blob store에 저장, 메타데이터 DB에 기록
- 다운로드 완료 → 유닛 배포 진행
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 | 설명 |
|---|---|
cores | CPU 코어 수 |
mem | 메모리 크기 |
disk | 루트 디스크 크기 |
arch | CPU 아키텍처 |
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-get | charm 설정 조회 |
status-set | 유닛/앱 상태 설정 |
relation-get/set | 관계 데이터 교환 |
secret-add/get | secret 관리 |
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
핵심 포인트
- 비동기 다운로드: charm 다운로드는 API 호출을 블로킹하지 않고 별도 워커가 처리
- IAAS/CAAS 분기: 머신 기반과 Kubernetes 기반 배포가 Phase 4에서 갈라짐
- Constraints → Instance: 프로바이더가 constraints를 만족하는 최소 인스턴스를 자동 선택
- Hook 순서 보장: install → config-changed → start 순서 엄격 관리, 유닛별 순차 실행
- 다단계 상태 전이: Allocating → Pending → Waiting → Executing → Idle → Active
- 실패 격리: 한 유닛의 hook 실패가 다른 유닛에 영향을 주지 않음
- Trust 모델:
--trust플래그로 클라우드 인증 정보 접근 명시적 허용
관련 문서
- Relations — 배포 후 앱 간 연결 시 relation hook이 실행되는 흐름
- Leadership Management — 유닛 배포 후 리더 선출 과정
- Juju Status — 배포 진행 상태를
juju status로 추적하는 방법 - Change Stream — 머신/유닛 생성이 워커에 전파되는 이벤트 인프라