저장소 버전 마이그레이션으로 쿠버네티스 객체 마이그레이션하기

저장소 버전 마이그레이션으로 쿠버네티스 객체 마이그레이션하기 (Migrate Kubernetes Objects Using Storage Version Migration)

이 기능을 사용하려면 (또는) 클러스터 관리자가 클러스터의 모든 관련 컴포넌트에 대해 StorageVersionMigrator 기능 게이트를 활성화해야 해요.

기능 게이트 활성화 또는 비활성화에 대한 자세한 내용은 기능 게이트 활성화/비활성화를 참조하세요.

쿠버네티스는 휴지 상태(at rest) 저장소와 관련된 일부 유지 관리 활동을 지원하기 위해 API 데이터가 활발히 다시 쓰여지는 것에 의존해요. 두 가지 두드러진 예시는 저장된 리소스의 버전 스키마(즉 주어진 리소스에 대해 선호 저장 스키마가 v1에서 v2로 변경되는 것)와 휴지 상태 암호화(즉 데이터가 암호화되어야 하는 방식의 변경에 기반해 오래된 데이터를 다시 쓰는 것)예요.

저장소 버전 마이그레이션을 실행하면 리소스의 모든 객체가 오래된 저장소 버전에서 마이그레이션되었음을 보장할 수 있어요. 저장소 마이그레이션을 실행하기 위한 요구 사항은 그 리소스가 정수형 리소스 버전(resource version)을 갖도록 보장하는 것이에요. 모든 쿠버네티스 리소스와 CRD가 이 속성을 갖도록 보장되지만, 집계된 API(aggregated APIs)의 경우처럼 그렇지 않으면 마이그레이션이 실패해요.

출처: 문서

본문

시작하기 전에

kubectl을 설치해요.

쿠버네티스 클러스터가 있어야 하고, kubectl 명령줄 도구가 클러스터와 통신하도록 구성되어 있어야 해요. 이 튜토리얼을 제어 플레인 호스트로 작동하지 않는 최소 두 개의 노드가 있는 클러스터에서 실행하는 것을 권장해요. 아직 클러스터가 없다면 minikube로 만들거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있어요:

  • iximiuz Labs
  • Killercoda
  • KodeKloud

버전을 확인하려면 kubectl version을 입력하세요. 쿠버네티스 서버는 v1.30 이상이어야 해요.

StorageVersionMigrator 기능 게이트와 storagemigration.k8s.io/v1 REST API는 모든 클러스터에서 기본적으로 활성화되어 있어요.

저장소 버전 마이그레이션을 사용해 쿠버네티스 시크릿 재암호화하기

  • 시작하려면 다음 암호화 구성을 사용해 KMS 제공자를 구성해 etcd에서 휴지 상태 데이터를 암호화해요.
kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==

--encryption-provider-config-automatic-reload를 true로 설정해 암호화 구성 파일의 자동 리로드를 활성화해야 해요.

  • kubectl을 사용해 Secret을 만들어요.
kubectl create secret generic my-secret --from-literal=key1=supersecret
  • 그 Secret 객체에 대한 직렬화된 데이터가 k8s:enc:aescbc:v1:key1 접두사로 시작하는지 확인해요.

  • 암호화 키를 회전시키기 위해 암호화 구성 파일을 다음과 같이 업데이트해요.

kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key2
              secret: c2VjcmV0IGlzIHNlY3VyZSwgaXMgaXQ/
      - aescbc:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
  • 이전에 만든 my-secret이 새 키 key2로 재암호화되도록 하려면 저장소 버전 마이그레이션(Storage Version Migration)을 사용할 거예요.

  • migrate-secret.yaml이라는 이름의 StorageVersionMigration 매니페스트를 다음과 같이 만들어요:

kind: StorageVersionMigration
apiVersion: storagemigration.k8s.io/v1
metadata:
  name: secrets-migration
spec:
  resource:
    group: ""
    resource: secrets

다음과 같이 kubectl을 사용해 객체를 만들어요:

kubectl apply -f migrate-secret.yaml
  • StorageVersionMigration의 .status를 확인해 Secret 마이그레이션을 모니터링해요. 성공적인 마이그레이션은 Succeeded 조건이 true로 설정되어야 해요. StorageVersionMigration 객체를 다음과 같이 가져와요:
kubectl wait --for=condition=Succeeded storageversionmigration.storagemigration.k8s.io/secrets-migration

출력은 다음과 비슷해요:

kind: StorageVersionMigration
apiVersion: storagemigration.k8s.io/v1
metadata:
  name: secrets-migration
  uid: 628f6922-a9cb-4514-b076-12d3c178967c
  resourceVersion: "90"
  creationTimestamp: "2024-03-12T20:29:45Z"
spec:
  resource:
    group: ""
    resource: secrets
status:
  conditions:
  - type: Running
    status: "False"
    lastUpdateTime: "2024-03-12T20:29:46Z"
    reason: StorageVersionMigrationInProgress
  - type: Succeeded
    status: "True"
    lastUpdateTime: "2024-03-12T20:29:46Z"
    reason: StorageVersionMigrationSucceeded
  resourceVersion: "84"
  • 저장된 시크릿이 이제 k8s:enc:aescbc:v1:key2 접두사로 시작하는지 확인해요.

CRD의 선호 저장 스키마 업데이트하기

사용자 지정 리소스(CRs)를 제공하기 위해 CRD(CustomResourceDefinition)가 생성되고 선호 저장 스키마로 설정된 시나리오를 고려해 봐요. CRD의 v2를 도입할 때가 되면, 그것은 변환 웹훅(conversion webhook)이 있는 제공용으로만 추가될 수 있어요. 이렇게 하면 사용자가 v1 또는 v2 스키마 중 하나를 사용해 CR을 만들 수 있는 더 부드러운 전환이 가능하며, 그 사이에 필요한 스키마 변환을 수행하기 위해 웹훅이 있는 상태로요. v2를 선호 저장 스키마 버전으로 설정하기 전에, v1로 저장된 모든 기존 CR이 v2로 마이그레이션되도록 하는 것이 중요해요. 이 마이그레이션은 모든 CR을 v1에서 v2로 마이그레이션하는 저장소 버전 마이그레이션을 통해 달성할 수 있어요.

  • test-crd.yaml이라는 이름의 CRD 매니페스트를 다음과 같이 만들어요:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: selfierequests.example.com
spec:
  group: example.com
  names:
    plural: selfierequests
    singular: selfierequest
    kind: SelfieRequest
    listKind: SelfieRequestList
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            hostPort:
              type: string
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        url: "https://127.0.0.1:9443/crdconvert"
        caBundle: <CABundle info>
      conversionReviewVersions:
      - v1
      - v2

이 시점에서 저장된 버전은 v1이어야 해요. 다음을 실행해 이를 확인해요:

kubectl get crd selfierequests.example.com -o jsonpath='{.spec.versions[?(@.storage==true)].name}'

kubectl을 사용해 CRD를 만들어요:

kubectl apply -f test-crd.yaml
  • 예시 testcrd용 매니페스트를 만들어요. 매니페스트 이름을 cr1.yaml로 하고 이 내용을 사용해요:
apiVersion: example.com/v1
kind: SelfieRequest
metadata:
  name: cr1
  namespace: default

kubectl을 사용해 CR을 만들어요:

kubectl apply -f cr1.yaml
  • etcd에서 객체를 가져와 CR이 v1로 쓰여지고 저장되었는지 확인해요.
ETCDCTL_API=3 etcdctl get /kubernetes.io/example.com/testcrds/default/cr1 [...] | hexdump -C

여기서 [...]에는 etcd 서버에 연결하기 위한 추가 인수가 포함돼요.

  • test-crd.yaml CRD를 업데이트해 v2 버전을 제공용과 저장용으로, v1을 제공용으로만 포함하게 해요:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: selfierequests.example.com
spec:
  group: example.com
  names:
    plural: selfierequests
    singular: selfierequest
    kind: SelfieRequest
    listKind: SelfieRequestList
  scope: Namespaced
  versions:
    - name: v2
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            host:
              type: string
            port:
              type: string
    - name: v1
      served: true
      storage: false
      schema:
        openAPIV3Schema:
          type: object
          properties:
            hostPort:
              type: string
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        url: "https://127.0.0.1:9443/crdconvert"
        caBundle: <CABundle info>
      conversionReviewVersions:
        - v1
        - v2

이제 저장된 버전은 v2가 되어야 해요. 이를 확인해요:

kubectl get crd selfierequests.example.com -o jsonpath='{.spec.versions[?(@.storage==true)].name}'

kubectl을 사용해 CRD를 업데이트해요:

kubectl apply -f test-crd.yaml
  • cr2.yaml이라는 이름으로 CR 리소스 파일을 다음과 같이 만들어요:
apiVersion: example.com/v2
kind: SelfieRequest
metadata:
  name: cr2
  namespace: default
  • kubectl을 사용해 CR을 만들어요:
kubectl apply -f cr2.yaml
  • etcd에서 객체를 가져와 CR이 v2로 쓰여지고 저장되었는지 확인해요.
ETCDCTL_API=3 etcdctl get /kubernetes.io/example.com/testcrds/default/cr2 [...] | hexdump -C

여기서 [...]에는 etcd 서버에 연결하기 위한 추가 인수가 포함돼요.

  • migrate-crd.yaml이라는 이름의 StorageVersionMigration 매니페스트를 다음 내용으로 만들어요:
kind: StorageVersionMigration
apiVersion: storagemigration.k8s.io/v1
metadata:
  name: crdsvm
spec:
  resource:
    group: example.com
    resource: selfierequests

다음과 같이 kubectl을 사용해 객체를 만들어요:

kubectl apply -f migrate-crd.yaml
  • 상태를 사용해 시크릿 마이그레이션을 모니터링해요. 성공적인 마이그레이션은 status 필드에 Succeeded 조건이 "True"로 설정되어야 해요. 마이그레이션 리소스를 다음과 같이 가져와요:
kubectl get storageversionmigration.storagemigration.k8s.io/crdsvm -o yaml

출력은 다음과 비슷해요:

kind: StorageVersionMigration
apiVersion: storagemigration.k8s.io/v1
metadata:
  name: crdsvm
  uid: 13062fe4-32d7-47cc-9528-5067fa0c6ac8
  resourceVersion: "111"
  creationTimestamp: "2024-03-12T22:40:01Z"
spec:
  resource:
    group: example.com
    resource: testcrds
status:
  conditions:
    - type: Running
      status: "False"
      lastUpdateTime: "2024-03-12T22:40:03Z"
      reason: StorageVersionMigrationInProgress
    - type: Succeeded
      status: "True"
      lastUpdateTime: "2024-03-12T22:40:03Z"
      reason: StorageVersionMigrationSucceeded
  resourceVersion: "106"
  • etcd에서 객체를 가져와 이전에 만든 cr1이 이제 v2로 쓰여지고 저장되었는지 확인해요.
ETCDCTL_API=3 etcdctl get /kubernetes.io/example.com/testcrds/default/cr1 [...] | hexdump -C

여기서 [...]에는 etcd 서버에 연결하기 위한 추가 인수가 포함돼요.

  • 또한 CRD의 저장된 버전 상태가 이제 v2만인지 확인해요:
kubectl get crd testcrds.example.com -o yaml

출력은 다음과 비슷해요:

kind: CustomResourceDefinition
apiVersion: apiextensions.k8s.io/v1
metadata:
  name: testcrds.example.com
spec:
  group: example.com
  names:
    kind: TestCRD
    plural: testcrds
  scope: Namespaced
  versions:
    - name: v1
      served: true
      storage: false
    - name: v2
      served: true
      storage: true
status:
  acceptedNames:
    kind: TestCRD
    plural: testcrds
  conditions:
    - type: Established
      status: "True"
  storedVersions:
    - v2

더 알아보기 (Learn more)