Juju Status
본 문서는 Juju 4.0 코드베이스 분석을 기반으로 AI가 정리했습니다.
Overview
juju status는 모델의 현재 상태를 조회하는 명령이다. 내부적으로는 에이전트가 push한 상태 데이터를 controller의 dqlite Canonical이 개발한 경량 분산 SQL 데이터베이스. SQLite를 Raft 합의 프로토콜로 복제하여 고가용성을 제공한다. DB에서 pull하여 조합한다.
flowchart LR
CLI["juju status"] -->|WebSocket| API["apiserver"]
API -->|FullStatus| SVC["Domain Services"]
SVC -->|SQLair| DB[(dqlite)]
DB --> SVC --> API --> CLI
요청 흐름
1. CLI → API Client
파일: cmd/juju/status/status.go
statusCommand.Run()
-> runStatus()
-> getStatus(ctx, includeStorage)
-> Client.Status(ctx, &StatusArgs{Patterns, IncludeStorage})
-> facade.FacadeCall(ctx, "FullStatus", StatusParams, &FullStatus)
CLI는 controllers.yaml의 api-endpoints 중 하나에 WebSocket HTTP 위에서 양방향 실시간 통신을 가능하게 하는 프로토콜. Juju CLI와 controller 간 RPC 호출에 사용된다. 으로 연결하여 Client facade Juju API의 논리적 인터페이스 단위. 각 facade는 특정 도메인의 RPC 메서드를 그룹화한다. 예: Client facade는 status, deploy 등을 제공. 의 FullStatus RPC를 호출한다.
출력 포맷:
| 포맷 | 플래그 | 설명 |
|---|---|---|
| tabular | (기본) | 사람이 읽기 좋은 테이블. ANSI 색상 지원 |
| yaml | --format yaml | 구조화된 YAML |
| json | --format json | 구조화된 JSON |
| oneline | --format oneline | 유닛당 한 줄 |
| summary | --format summary | 상태별 집계 |
2. API Server → 데이터 조합
파일: apiserver/facades/client/client/status.go
FullStatus() 핸들러는 여러 domain service 비즈니스 로직을 캡슐화하는 서비스 레이어. facade와 DB(state) 사이에 위치하여 데이터 접근 로직을 추상화한다. 를 호출하여 상태를 조합한다:
flowchart TB
FS["FullStatus()"]
FS --> MC["modelInfoService"]
FS --> AS["statusService (App/Unit)"]
FS --> MS["statusService (Machine)"]
FS --> RS["statusService (Relation)"]
FS --> NS["networkService"]
FS --> PS["portService"]
FS --> LS["leadershipReader"]
FS --> CS["crossModelRelationService"]
응답 구조:
FullStatus {
Model // 모델 이름, 상태, 버전
Machines // 머신별 상태 (agent, instance, 네트워크)
Applications // 앱별 상태 (charm, 유닛, 관계)
└─ Units // 유닛별 상태 (agent, workload, 포트)
Relations // 관계 목록
Offers // cross-model offer (admin만)
Storage // 스토리지 (--storage 플래그 시)
ControllerTimestamp // controller 시각
}
3. Domain Service → dqlite
파일: domain/status/state/model/modelstate.go
각 domain service는 SQLair Juju 4.0에서 사용하는 Go ORM. SQL 쿼리에 Go 구조체를 직접 바인딩할 수 있어 타입 안전한 DB 접근이 가능하다. ORM으로 dqlite를 조회한다:
stmt, _ := st.Prepare(`
SELECT &unitAgentStatusInfo.*
FROM v_unit_agent_status
WHERE unit_uuid = $unitUUID.uuid
`, unitAgentStatusInfo{}, unitUUID{})
참고: 4.0에서 패턴 필터링(특정 앱/머신만 조회)은 아직 미구현이다.
StatusParams.Patterns에 값이 있으면NotImplemented에러를 반환한다.
에이전트 상태 보고 (Push 모델)
상태 데이터는 각 에이전트가 능동적으로 push하여 dqlite에 저장된다.
flowchart LR
UA["Unit Agent"] -->|status-set| UF["uniter facade"]
MA["Machine Agent"] -->|SetStatus| MF["machiner facade"]
UF --> SS["statusService"]
MF --> SS
SS --> DB[(dqlite)]
Unit Agent
파일: internal/worker/uniter/runner/jujuc/status-set.go
charm Juju에서 애플리케이션 배포·설정·운영을 정의하는 패키지. Kubernetes의 Helm chart와 유사한 개념으로, hook 스크립트로 생명주기를 관리한다. hook에서 status-set 명령으로 workload 상태 charm이 보고하는 애플리케이션 수준의 상태. active/waiting/blocked/maintenance 등이 있으며, agent status와는 별도로 관리된다. 를 보고한다:
# charm hook 내부
status-set active "Ready to serve"
status-set blocked "Missing database relation"
status-set --application maintenance "Upgrading schema"
status-set 실행
-> SetUnitStatus() // workload 상태
또는
-> SetApplicationStatus() // --application 플래그 시 (리더만 가능)
Machine Agent
파일: apiserver/facades/agent/machine/machiner.go
machiner worker 각 머신에서 실행되는 Juju 에이전트 내부 워커. 머신의 생존 상태를 주기적으로 controller에 보고한다. 가 머신 상태를 보고한다:
machiner.SetStatus()
-> statusService.SetMachineStatus(ctx, machineName, StatusInfo{
Status: "started",
Message: "Running",
Since: now,
})
상태 종류
Unit Agent Status
에이전트 자체의 동작 상태:
| Status | 설명 |
|---|---|
| allocating | 유닛 할당 중 |
| executing | hook/task 실행 중 |
| idle | 에이전트 정상 대기 |
| error | 에이전트 에러 |
| lost | 에이전트 미응답 |
Unit Workload Status
charm이 보고하는 애플리케이션 상태:
| Status | 설명 |
|---|---|
| active | 정상 동작 중 |
| waiting | 의존성 대기 |
| blocked | 수동 개입 필요 |
| maintenance | 유지보수 작업 중 |
| error | 에러 상태 |
Machine Status
| Status | 설명 |
|---|---|
| started | 머신 정상 실행 |
| pending | 시작 대기 중 |
| stopped | 머신 중지 |
| down | 에이전트 미응답 |
| error | 에이전트 에러 |
Application Status 파생
애플리케이션 상태가 명시적으로 설정되지 않으면, 유닛 상태 중 가장 심각한 것으로 파생된다:
error > blocked > maintenance > waiting > active > unknown
상태 저장소 (dqlite 테이블)
스키마: domain/schema/model/sql/0016-status.sql, 0018-machine.sql, 0019-application.sql, 0020-unit.sql
| 테이블 | 용도 |
|---|---|
unit_agent_status | 유닛 에이전트 상태 |
unit_workload_status | 유닛 워크로드 상태 |
application_status | 애플리케이션 상태 |
machine_status | 머신 에이전트 상태 |
instance_status | 클라우드 인스턴스 상태 |
relation_status | 관계 상태 |
각 테이블은 status_id(enum), message, data(JSON), updated_at 컬럼을 가진다.
Presence 추적
에이전트가 살아있는지 별도로 추적한다:
| 테이블 | 용도 |
|---|---|
unit_agent_presence | 유닛 에이전트 생존 확인 (last_seen) |
machine_agent_presence | 머신 에이전트 생존 확인 |
에이전트가 상태를 보고했지만 presence 에이전트가 주기적으로 보내는 '살아있음' 신호. 이 신호가 일정 시간 동안 오지 않으면 에이전트를 lost/down으로 판정한다. 가 만료되면:
- 유닛: lost 상태로 표시
- 머신: down 상태로 표시
Status View
조회 시 사용하는 뷰:
| 뷰 | 조인 내용 |
|---|---|
v_unit_agent_status | 유닛 + agent 상태 + presence |
v_unit_workload_status | 유닛 + workload 상태 + presence |
v_full_unit_status | 유닛 + 양쪽 상태 + k8s pod 상태 |
v_machine_status | 머신 + 상태 + presence |
응답 포매팅
파일: cmd/juju/status/formatter.go, output_tabular.go, output_summary.go, output_oneline.go
flowchart LR
FS["FullStatus (RPC)"] --> SF[".Format()"]
SF --> F["formattedStatus"]
F --> T["Tabular"]
F --> Y["YAML"]
F --> J["JSON"]
F --> O["Oneline"]
F --> S["Summary"]
statusFormatter.Format()는 RPC 응답(params.FullStatus)을 출력용 구조체(formattedStatus)로 변환한다. 이 과정에서:
- 머신 ID를 정렬하여 트리 구조로 재구성
- 유닛에 리더 플래그(
*) 부여 - 포트 정보를 유닛에 매핑
- 네트워크 주소를 사람이 읽기 좋은 형태로 변환
juju status --watch
--watch 플래그를 사용하면 상태를 주기적으로 갱신하여 표시한다. 내부적으로는 Change Stream dqlite의 change_log 테이블 변경을 감지하여 구독자에게 분배하는 시스템. 자세한 동작은 Change Stream 문서를 참조. 의 AllWatcher를 통해 상태 변경 이벤트를 실시간으로 수신한다:
juju status --watch 5s
-> AllWatcher.Next() // Change Stream으로부터 delta 수신
-> 변경된 엔티티만 업데이트하여 출력 갱신
단발 호출인 juju status가 pull 모델이라면, --watch는 Change Stream 기반의 push 모델이다.
핵심 포인트
- Pull-on-demand:
juju status는 호출 시점에 dqlite DB를 조회하는 pull 모델 - Agent Push: 에이전트가 상태 변경 시 즉시 dqlite에 기록 (push 모델)
- 두 종류의 유닛 상태: agent status(에이전트 동작)와 workload status(charm 보고)는 별도로 관리
- Presence 기반 lost/down 감지: 상태 테이블과 별도로 presence를 추적하여 미응답 에이전트 감지
- Application 상태 파생: 명시적 설정이 없으면 유닛 상태 중 가장 심각한 것으로 결정
- Domain Service 계층: facade → service → state(DB) 3계층 구조로 데이터 접근
- 다양한 출력 포맷: 동일한 FullStatus 데이터를 tabular, YAML, JSON, oneline, summary로 변환
- 리더십 정보 통합: leadershipReader lease 기반 리더십 시스템에서 현재 리더 유닛을 조회하는 인터페이스. 자세한 동작은 Leadership Management 문서를 참조.
.Leaders()로 리더 유닛 정보를 상태에 포함
관련 문서
- Leadership Management — 리더 유닛(
*) 표시의 원천인 lease 기반 리더십 시스템 - Change Stream —
--watch모드의 실시간 갱신을 지원하는 이벤트 인프라 - Model Migration — 마이그레이션 중 status가 어떻게 변화하는지