Storage Provisioning

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

Overview

Juju Storage는 클라우드 프로바이더의 스토리지를 추상화하여 charm에 일관된 인터페이스를 제공하는 시스템이다. AWS EBS, GCE Persistent Disk, LXD ZFS 등 다양한 백엔드를 Storage Pool로 추상화하고, charm은 “filesystem 100GB”처럼 선언적으로 요구한다.

flowchart TB
    subgraph Charm["Charm Metadata"]
        REQ["storage:\n  pgdata:\n    type: filesystem\n    minimum-size: 50G"]
    end
    subgraph Juju["Juju Storage Layer"]
        SI["StorageInstance"]
        VOL["Volume"]
        FS["Filesystem"]
        SI --> VOL
        VOL --> FS
    end
    subgraph Cloud["Cloud Provider"]
        EBS["AWS EBS"]
        PD["GCE PD"]
        LXD["LXD ZFS"]
    end
    REQ --> SI
    FS --> EBS
    FS --> PD
    FS --> LXD

Storage 유형

Volume vs Filesystem

VolumeFilesystem
추상화원시 블록 디바이스마운트 가능한 파일시스템
예시EBS volume, Persistent Diskext4, XFS on volume
charm 선언type: blocktype: filesystem
용도DB 엔진이 직접 디바이스 관리일반적인 파일 저장

합성 (Composition)

filesystem을 요청했지만 프로바이더가 block만 지원하면, volume 위에 filesystem을 자동 생성한다:

flowchart LR
    SI["StorageInstance\n(filesystem 요청)"] --> VOL["Volume\n(model 스코프)"]
    VOL --> FS["Filesystem\n(machine 스코프)"]
    FS --> MP["/var/lib/postgresql\n(mount point)"]

합성 규칙 프로바이더가 filesystem을 직접 지원하면 volume 없이 바로 filesystem을 생성한다. volume만 지원하면 volume을 먼저 생성하고 그 위에 filesystem을 올린다. 이 결정은 StorageInstanceComposition으로 관리된다. 은 프로바이더 능력에 따라 자동으로 결정된다.


Charm 메타데이터 선언

# charm metadata.yaml
storage:
  pgdata:
    type: filesystem
    description: "PostgreSQL data directory"
    minimum-size: 50G
    location: /var/lib/postgresql
    shared: false
    read-only: false
    count-min: 1      # 최소 1개 필수
    count-max: 1      # 최대 1개
    properties:
      - transient      # 일시적 (영속 불필요)
필드설명
typeblock 또는 filesystem
minimum-size최소 크기
locationfilesystem 마운트 포인트
shared유닛 간 공유 여부
count-min/max인스턴스 수 범위 (-1 = 무제한)
propertiestransient 등 추가 속성

Storage Pool

Storage Pool 클라우드 프로바이더의 스토리지 유형과 설정을 이름으로 추상화한 것. 사용자가 커스텀 풀을 생성하거나 프로바이더 기본 풀을 사용할 수 있다. 은 프로바이더별 스토리지 설정을 캡슐화한다:

# 기본 풀 목록
juju storage-pools list
# ebs (aws), azure (azure), kubernetes (k8s), lxd (lxd) 등

# 커스텀 풀 생성
juju storage-pool create fast-ebs ebs volume-type=io1 iops=3000
기본 풀프로바이더설명
ebsAWSEBS 볼륨
azureAzureManaged Disk
gceGCEPersistent Disk
kubernetesK8sPVC (Persistent Volume Claim)
lxdLXDZFS/Btrfs 스토리지
tmpfs모든 IAAS메모리 기반 임시 스토리지
rootfs모든 IAAS루트 파일시스템

배포 시 스토리지 지정

# 기본 (charm의 minimum-size와 기본 풀 사용)
juju deploy postgresql

# 풀과 크기 지정
juju deploy postgresql --storage pgdata=ebs,100G

# 커스텀 풀, 여러 인스턴스
juju deploy postgresql --storage pgdata=fast-ebs,2,100G
#                                       풀       수 크기

Storage Directive 형식

<pool>,<count>,<size>

모두 선택적이다. 생략하면:

  • pool: 모델의 default-block-source 또는 default-filesystem-source
  • count: 1
  • size: charm의 minimum-size

Provisioning 스코프

두 가지 스코프

스코프프로비저닝 주체예시
Model 스코프 controller의 model provisioner가 관리. 클라우드 API를 통해 볼륨을 생성하며, 머신과 독립적으로 존재할 수 있다. Controller (모델 provisioner)EBS volume, GCE PD
Machine 스코프 각 머신의 machine agent가 관리. 머신 로컬 리소스를 사용하며, 머신이 제거되면 함께 사라진다. Machine agenttmpfs, loop device, LXD

합성 시 스코프 분리

filesystem 요청 + block-only 프로바이더:

StorageInstance
  ├── Volume    → Model 스코프 (EBS volume, 클라우드 API로 생성)
  └── Filesystem → Machine 스코프 (machine agent가 mkfs + mount)

같은 StorageInstance 안에서 volume과 filesystem이 서로 다른 스코프로 프로비저닝될 수 있다.


영속성 (Persistence)

PersistentEphemeral
유닛 제거 시유지됨삭제됨
재연결 가능juju attach-storage로 다른 유닛에 연결불가
스코프Model 스코프 → 자동 persistentMachine 스코프 → 자동 ephemeral
예시EBS volumetmpfs, loop device

Persistent Storage 재연결

# 유닛에서 분리
juju detach-storage pgdata/0

# 다른 유닛에 연결
juju attach-storage postgresql/1 pgdata/0

persistent storage는 유닛이 제거되어도 데이터가 보존되므로, 새 유닛에 기존 데이터를 그대로 연결할 수 있다.


Provisioner Worker

파일: internal/worker/storageprovisioner/

두 종류의 provisioner가 실행된다:

flowchart TB
    subgraph Controller["Controller"]
        MP["Model Provisioner\n(model 스코프)"]
    end
    subgraph Machine["Machine Agent"]
        MaP["Machine Provisioner\n(machine 스코프)"]
    end
    subgraph Cloud["Cloud Provider"]
        API["클라우드 API\n(EBS, PD 등)"]
    end
    subgraph Local["Machine Local"]
        FS["mkfs, mount\ntmpfs, loop"]
    end

    MP -->|"CreateVolumes()"| API
    MP -->|"AttachVolumes()"| API
    MaP -->|"CreateFilesystems()"| Local
    MaP -->|"AttachFilesystems()"| Local

Watcher 기반 이벤트 처리

Model Provisioner:
  WatchVolumes(model) → 새 volume 감지 → CreateVolumes()
  WatchVolumeAttachments(model) → attach 요청 감지 → AttachVolumes()

Machine Provisioner:
  WatchFilesystems(machine) → 새 filesystem 감지 → CreateFilesystems()
  WatchFilesystemAttachments(machine) → mount 요청 감지 → AttachFilesystems()
  WatchVolumeAttachmentPlans() → iSCSI 초기화 감지

Provider 인터페이스

type Provider interface {
    VolumeSource(config) (VolumeSource, error)
    FilesystemSource(config) (FilesystemSource, error)
    Supports(kind StorageKind) bool
    Scope() Scope           // Environ(model) or Machine
    Dynamic() bool          // 동적 생성 지원 여부
    Releasable() bool       // 삭제 없이 해제 가능 여부
    DefaultPools() []*Config
}
프로바이더VolumeFilesystem스코프Dynamic
EBS-Model
Azure-Model
GCE-Model
K8s-Model
LXDMachine
tmpfs-Machine
rootfs-Machine-
loop-Machine

Attachment 생명주기

flowchart LR
    A["Alive\n(활성)"] -->|"detach 요청"| DY["Dying\n(분리 중)"]
    DY -->|"유닛 해제"| DE["Dead\n(제거됨)"]

제거 케스케이드

1. RemoveStorage(storageInstance)
2. 모든 attachment → Dying
3. 유닛이 storage 해제
4. attachment → Dead, 삭제
5. storageInstance → Dead
6. Provisioner가 클라우드 리소스 삭제 (DestroyVolumes/DestroyFilesystems)

iSCSI iSCSI(Internet Small Computer Systems Interface)를 통해 네트워크 너머의 블록 디바이스를 로컬처럼 사용하는 기능. VolumeAttachmentPlan에 서버 주소, 타겟 이름, CHAP 인증 정보가 포함된다. 지원

VolumeAttachmentPlan으로 iSCSI 연결을 관리한다:

type VolumeAttachmentPlan struct {
    Life           Life
    DeviceType     VolumeDeviceType  // Local 또는 iSCSI
    DeviceAttributes map[string]string  // 서버/타겟/CHAP 정보
}

Machine agent가 iSCSI initiator를 설정하여 원격 블록 디바이스를 로컬 디바이스로 매핑한다.


CLI 명령어

명령설명
juju storage모델의 모든 스토리지 조회
juju add-storage <unit> <name>=<directive>기존 유닛에 스토리지 동적 추가
juju attach-storage <unit> <storage-id>기존 스토리지를 유닛에 연결
juju detach-storage <storage-id>스토리지 분리 (데이터 보존)
juju remove-storage <storage-id>스토리지 삭제
juju storage-pools list풀 목록 조회
juju storage-pools create <name> <provider> [key=value...]커스텀 풀 생성

핵심 포인트

  1. Volume/Filesystem 이중 추상화: block은 Volume, mountable은 Filesystem으로 분리하고 필요 시 자동 합성
  2. Model/Machine 스코프 분리: 클라우드 리소스는 controller가, 로컬 리소스는 machine agent가 각각 프로비저닝
  3. Persistent vs Ephemeral: Model 스코프 스토리지는 자동 persistent, 유닛 제거 후 재연결 가능
  4. Provider 추상화: 단일 인터페이스로 EBS/Azure/GCE/K8s/LXD 등 다양한 백엔드 지원
  5. Storage Pool: 프로바이더별 설정을 이름으로 캡슐화하여 재사용
  6. 합성 규칙: filesystem 요청 + block-only 프로바이더 = volume(model) + filesystem(machine) 자동 조합
  7. 이벤트 기반: Watcher로 변경 감지 → 멱등적 프로비저닝 → 실패 시 재시도

관련 문서

  • Charm Deployment--storage 플래그로 배포 시 스토리지 지정하는 흐름
  • Model Migration — 마이그레이션 시 Storage import가 수행되는 과정
  • Change Stream — volume/filesystem 변경이 provisioner 워커에 전파되는 이벤트 인프라