Documentation
¶
Overview ¶
Package kube 는 정지/재개, 볼륨 아카이브, 리소스 덤프의 Kubernetes 어댑터다.
설계: docs/11_기능설계/Nullus_백업복구_설계.md §3.4 (nullus-plan#75)
Index ¶
- Constants
- type Adapters
- type HelmReleaseReader
- type NamespaceEnsurer
- type ResourceDumper
- func (d *ResourceDumper) Apply(ctx context.Context, namespace string, in io.Reader) error
- func (d *ResourceDumper) ApplyShared(ctx context.Context, in io.Reader) error
- func (d *ResourceDumper) ApplySharedSecrets(ctx context.Context, in io.Reader) error
- func (d *ResourceDumper) Dump(ctx context.Context, namespace string, out io.Writer) (int64, error)
- func (d *ResourceDumper) DumpShared(ctx context.Context, out io.Writer) (int64, error)
- type VolumeArchiver
- func (a *VolumeArchiver) Archive(ctx context.Context, namespace, pvc string, out io.Writer) (int64, error)
- func (a *VolumeArchiver) EnsurePVC(ctx context.Context, namespace string, spec domain.VolumeSpec, ...) error
- func (a *VolumeArchiver) ListPVCs(ctx context.Context, namespace string) ([]domain.VolumeSpec, error)
- func (a *VolumeArchiver) Restore(ctx context.Context, namespace, pvc string, in io.Reader) error
- type WorkloadScaler
- func (s *WorkloadScaler) List(ctx context.Context, namespace string) ([]domain.Workload, error)
- func (s *WorkloadScaler) Scale(ctx context.Context, t domain.QuiesceTarget, replicas int32) error
- func (s *WorkloadScaler) WaitStopped(ctx context.Context, namespace string, targets []domain.QuiesceTarget) error
Constants ¶
const DefaultHelperImage = "busybox:1.37"
DefaultHelperImage 는 에어갭 번들에 있는 이미지다 (airgap/images/images.txt). tar 가 들어 있고 별도 반입이 필요 없다.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Adapters ¶
type Adapters struct {
Scaler *WorkloadScaler
Archiver *VolumeArchiver
Resources *ResourceDumper
Releases *HelmReleaseReader
// Namespaces 는 복구가 대상 네임스페이스를 만든다.
Namespaces *NamespaceEnsurer
// Kubeconfig 는 이 묶음이 붙은 클러스터다. 복구 뒤 게이트웨이 주소 재설정에 같은 클러스터를 넘긴다.
Kubeconfig []byte
}
Adapters 는 한 클러스터에 대한 백업 어댑터 묶음이다.
백업 대상 스택은 등록된 워크로드 클러스터에 있으므로, 어댑터는 기동 시점이 아니라 요청 시점에 그 클러스터의 kubeconfig 로 만들어야 한다.
type HelmReleaseReader ¶
type HelmReleaseReader struct {
// contains filtered or unexported fields
}
HelmReleaseReader 는 네임스페이스에 설치된 차트의 버전을 읽는다.
왜 필요한가: 복구가 다른 버전으로 재설치하면 도구들이 기동하며 스키마 마이그레이션을 돌린다. 그 순간 데이터는 백업 시점의 모습이 아니게 되고, 되돌릴 수 없다 — "복구했다" 와 "이전 상태다" 가 갈리는 지점이다.
릴리스 Secret 은 리소스 덤프에도 담기지만 그것은 암호화된 데다 통째로 풀어야 읽을 수 있다. 버전 선택은 **복구를 시작하기 전에** 해야 하므로, 암호화하지 않는 매니페스트에 따로 기록한다.
func NewHelmReleaseReader ¶
func NewHelmReleaseReader(client kubernetes.Interface) *HelmReleaseReader
func (*HelmReleaseReader) ListHelmReleases ¶
func (r *HelmReleaseReader) ListHelmReleases(ctx context.Context, namespace string) ([]domain.HelmReleaseSpec, error)
ListHelmReleases 는 네임스페이스의 릴리스를 릴리스별 **최신 리비전만** 남겨 돌려준다.
Helm 은 리비전마다 Secret 을 하나씩 남긴다. 복구가 재현해야 하는 것은 백업 시점에 실제로 돌고 있던 버전이므로 가장 높은 리비전을 고른다.
type NamespaceEnsurer ¶
type NamespaceEnsurer struct {
// contains filtered or unexported fields
}
NamespaceEnsurer 는 port.NamespaceEnsurer 의 구현이다.
func NewNamespaceEnsurer ¶
func NewNamespaceEnsurer(client kubernetes.Interface) *NamespaceEnsurer
NewNamespaceEnsurer 는 클러스터 클라이언트로 만든다.
func (*NamespaceEnsurer) EnsureNamespace ¶
func (e *NamespaceEnsurer) EnsureNamespace(ctx context.Context, namespace, restoredFromBackupID string) error
EnsureNamespace 는 네임스페이스가 없으면 만든다. 있으면 손대지 않는다.
설치는 helm --create-namespace 로 라벨 없이 만든다 — 복구도 같은 모양이면 된다. 만들 때만 출처를 주석으로 남긴다(PVC 와 같다): 설치 preflight 가 남은 자원을 볼 때 복구가 놓아둔 것과 실패한 설치의 잔여를 가를 근거가 클러스터 안에 있어야 한다.
type ResourceDumper ¶
type ResourceDumper struct {
// contains filtered or unexported fields
}
ResourceDumper 는 네임스페이스 리소스와 Helm 릴리스를 뜬다 (D1).
kubectl 을 exec 하는 이유: 스택은 Gateway/HTTPRoute 같은 CRD 를 만든다. 타입 클라이언트로 알려진 종류만 훑으면 카탈로그가 늘 때마다 백업이 뒤처진다 — §1.5 의 PVC 열거와 같은 이유다.
kubectl 은 API 이미지에 이미 있다 (Dockerfile).
func NewResourceDumper ¶
func NewResourceDumper(kubeconfig []byte) *ResourceDumper
func (*ResourceDumper) Apply ¶
Apply 는 뜬 리소스를 되돌린다.
--server-side 를 쓰는 이유: 덤프에는 resourceVersion·uid 같은 서버 관리 필드가 섞여 있다. 클라이언트 사이드 apply 는 그것들 때문에 충돌한다. Apply 는 클러스터 범위를 먼저, 그 다음 네임스페이스 리소스를 되돌린다.
순서가 핵심이다. CRD 가 등록되기 전에 그 CR 을 apply 하면 "no matches for kind" 로 죽는다 — 실환경 리허설에서 ESO 의 SecretStore/ExternalSecret 이 그렇게 실패했다.
func (*ResourceDumper) ApplyShared ¶
ApplyShared 는 공용 자원을 **없을 때만** 만든다.
순서: CRD → 등록 대기 → 그 밖의 클러스터 범위(GatewayClass·ClusterIssuer) → 내부 CA Secret → Certificate. 이미 있는 것은 그대로 둔다 — 다른 스택이 쓰는 지금의 CA 나 CRD 를 백업 때의 것으로 덮으면 그 스택이 깨진다. 종류의 CRD 가 없으면(cert-manager 없음) 어느 종류가 왜 안 되는지와 무엇을 먼저 설치해야 하는지 말하고 멈춘다.
func (*ResourceDumper) ApplySharedSecrets ¶
ApplySharedSecrets 는 덤프 가운데 CRD 가 필요 없는 것 — 내부 CA Secret 과 그 네임스페이스 — 만 없을 때 만든다. cert-manager 를 다시 세우기 **전에** 부른다.
순서가 전부다. 설치기의 cert-manager 단계는 끝에 내부 CA 를 부트스트랩한다(Certificate nullus-internal-ca-cert → Secret nullus-internal-ca). 그때 Secret 이 이미 있고 발급 정보가 맞으면 cert-manager 는 그것을 채택하고, 없으면 **새 키를 발급**한다. 새 키가 생긴 뒤에는 ApplyShared 의 없을-때만-만들기가 옛 키를 되살릴 수 없다 — 도구들이 신뢰하는 CA 가 백업 때와 달라진다. Secret 은 CRD 가 필요 없으니 컨트롤러보다 먼저 둘 수 있다. 그 밖의 것(CRD·GatewayClass· ClusterIssuer·Certificate)은 ApplyShared 가 컨트롤러가 선 뒤에 만든다.
자리는 cert-manager 가 지금 있는 네임스페이스, 모르면(회수된 뒤라 보통 모른다) 백업 때의 자리다. 설치기는 CRD 어노테이션이 없으면 차트 기본 네임스페이스에 깐다 — 백업이 그 기본 배치에서 떠졌으면 같은 자리다.
func (*ResourceDumper) DumpShared ¶
DumpShared 는 Nullus 가 설치한 공용 자원 중 백업이 담을 수 있는 것을 뜬다.
설치 표시 라벨로 고른다 — 사용자가 직접 깐 Gateway API 는 Nullus 것이 아니고, 회수되지도 않는다. 없는 종류(cert-manager 가 없는 클러스터의 ClusterIssuer)는 건너뛴다. 담을 것이 없으면 0 을 돌려주고 아무것도 쓰지 않는다.
type VolumeArchiver ¶
type VolumeArchiver struct {
// contains filtered or unexported fields
}
VolumeArchiver 는 PVC 를 tar 로 뜨고 되돌린다.
── 데이터 경로에 대한 설계 이탈 (기록) ──────────────────────────────────── 설계 §3.6 은 "볼륨을 마운트한 Job 이 목적지로 직접 올린다" 를 권고했다. 구현하면서 그것이 §5(키 취급)와 충돌한다는 것이 드러났다:
산출물은 봉인해서 올려야 하고(§5.1), 봉인 키는 컨트롤 플레인에만 있어야 한다(§4.2.1·§5.2). Job 이 직접 올리려면 그 키를 **백업 대상 클러스터 안으로** 넣어야 하는데, 그러면 클러스터가 침해될 때 백업본까지 함께 열린다.
그래서 컨트롤 플레인이 데이터 경로에 들어간다. 대역폭을 두 번 쓰는 대신 키가 대상 클러스터를 벗어나지 않는다. 이 교환은 설계 문서 §3.6 에 반영했다. ───────────────────────────────────────────────────────────────────────────
스트리밍은 kubectl cp 와 같은 방식이다 — 헬퍼 파드에서 `tar` 를 exec 하고 stdout 을 그대로 흘린다. 중간 파일을 만들지 않는다.
func NewVolumeArchiver ¶
func NewVolumeArchiver(client kubernetes.Interface, cfg *rest.Config, image string) *VolumeArchiver
func (*VolumeArchiver) Archive ¶
func (a *VolumeArchiver) Archive(ctx context.Context, namespace, pvc string, out io.Writer) (int64, error)
Archive 는 PVC 내용을 tar 스트림으로 흘린다.
-p (권한 보존) 를 쓰는 이유: mc mirror 같은 파일 단위 동기화 대신 tar 를 고른 이유가 권한·소유자·링크 보존이다. Git 저장소와 레지스트리 blob 은 거기에 민감하다 (§3.6).
func (*VolumeArchiver) EnsurePVC ¶
func (a *VolumeArchiver) EnsurePVC(ctx context.Context, namespace string, spec domain.VolumeSpec, restoredFromBackupID string) error
EnsurePVC 는 매니페스트에 기록된 크기·StorageClass 대로 PVC 를 만든다.
func (*VolumeArchiver) ListPVCs ¶
func (a *VolumeArchiver) ListPVCs(ctx context.Context, namespace string) ([]domain.VolumeSpec, error)
ListPVCs 는 네임스페이스의 PVC 를 **열거한다**.
알려진 도구 목록을 순회하지 않는 이유: GitLab·Gitea 는 설치기가 볼륨 크기를 지정하지 않아 차트 기본값을 따르고, 차트는 여러 PVC 를 자기 기본값으로 만든다. 정적 목록은 반드시 뒤처진다 (§1.5).
type WorkloadScaler ¶
type WorkloadScaler struct {
// contains filtered or unexported fields
}
WorkloadScaler 는 Deployment/StatefulSet 을 0 으로 내렸다 되돌린다.
func NewWorkloadScaler ¶
func NewWorkloadScaler(client kubernetes.Interface) *WorkloadScaler
func (*WorkloadScaler) Scale ¶
func (s *WorkloadScaler) Scale(ctx context.Context, t domain.QuiesceTarget, replicas int32) error
func (*WorkloadScaler) WaitStopped ¶
func (s *WorkloadScaler) WaitStopped(ctx context.Context, namespace string, targets []domain.QuiesceTarget) error
WaitStopped 는 대상 워크로드의 파드가 모두 사라질 때까지 기다린다.
replica 를 0 으로 바꾸는 것과 파드가 실제로 종료되는 것은 다르다. 종료 전에 복사를 시작하면 쓰기가 진행 중인 파일을 뜨게 되고, 그것은 정지 백업이 아니다.