스토리지 버전

스토리지 버전 (Storage Versions)

쿠버네티스 API 서버는 객체를 저장할 때 etcd와 호환되는 백엔드 스토리지(보통 실제로는 etcd 자체)를 사용해요. 각 객체는 해당 API 유형의 특정 버전으로 직렬화돼요. 예를 들어 ConfigMap의 v1 표현이 그렇죠. 쿠버네티스는 객체가 클러스터에 어떻게 저장되는지를 설명할 때 **스토리지 버전(storage version)**이라는 용어를 사용해요.

쿠버네티스 API는 자동 변환에도 의존해요. 예를 들어 HorizontalPodAutoscaler가 있다면, HorizontalPodAutoscaler API의 v1 버전과 v2 버전을 얼마든지 섞어서 그 객체와 상호작용할 수 있어요. 클라이언트가 실제로 어떤 버전으로 직렬화되는지 보지 못하도록 쿠버네티스가 각 API 호출을 변환하는 역할을 담당해요.

클러스터 관리자에게 객체 스토리지 버전은 이해해야 할 중요한 개념이에요. 객체의 API 표현과 스토리지 백엔드의 실제 인코딩을 연결해 주기 때문이에요. 객체의 실제 바이너리 인코딩이 중요해지는 경우(예: 저장 시 암호화(encryption at rest), API 폐기(deprecation))에 특히 중요해져요.

하나의 API는 여러 스토리지 버전을 가질 수 있고, API 서버는 이를 객체 스키마로 변환할 수 있어요. 그 리소스에 속한 단일 객체는 어떤 시점에든 스토리지 버전이 하나만 있어야 해요. 즉 API 서버는 객체의 바이너리 인코딩을 알고 있으며, 모든 저장된 버전에서 객체의 API 표현으로 동적으로 변환할 수 있다는 뜻이에요.

객체의 버전은 스토리지 버전과 완전히 별개예요. 예를 들어 같은 리소스의 v1alpha1 API 객체와 v1beta1 API 객체는, 두 객체 사이에 스토리지 버전이 업데이트되지 않았다면 저장소에서 동일하게 인코딩돼요.

출처: 문서

본문

스토리지 버전과 리소스 매핑 (Storage version to resource mapping)

모든 리소스는 어떤 시점에든 활성 스토리지 버전이 하나예요. 객체에 대한 모든 쓰기는 그 객체를 해당 스토리지 버전으로 저장한다는 뜻이에요. 하지만 스토리지 버전은 업데이트될 수 있어서, 객체들이 서로 다른 버전으로 저장될 수도 있어요. 하나의 객체는 어떤 시점에든 하나의 스토리지 버전으로만 저장돼요.

API 서버로부터의 읽기는 저장된 데이터를 객체의 API 표현으로 변환해요. 그래서 객체에 업데이트가 발생하지 않는 한 오래된 스토리지 버전은 무기한 유지될 수 있어요. 반면 쓰기는 객체가 업데이트될 때 저장된 객체를 새 표현으로 변환해요.

커스텀 리소스의 스토리지 버전 (Storage versions for custom resources)

커스텀 리소스는 동적으로 정의되기 때문에 내장된 쿠버네티스 유형과 스토리지 버전이 달라요. 내장 객체는 대체로 스토리지 인코딩이 API 유형과 별도로 정의돼요. 이때 저장된 객체가 허브(hub) 역할을 하고, 리소스의 특정 버전은 객체 스키마의 필드일 뿐 큰 의미를 갖지 않아요.

하지만 커스텀 리소스의 경우에는 리소스의 특정 버전을 스토리지 버전으로 설정해야 해요. 커스텀 리소스의 해당 버전이 정의한 스키마가 스토리지 레이어에서 리소스의 인코딩으로 사용돼요. API 설정과 버전 관리에 대한 자세한 내용은 고급 CRD 기능 세트(advanced CRD featureset)를 참고하세요.

예를 들어 crontabs용 CustomResourceDefinition을 살펴보죠.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  # 이 CustomResourceDefinition이 지원하는 버전 목록
  versions:
  - name: v1beta1
    # 각 버전은 served 플래그로 활성화/비활성화할 수 있다.
    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:
          host:
            type: string
          port:
            type: string
          time:
            type: string
  conversion:
    strategy: None
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

이 예시에서는 v1beta1 API 정의가 스토리지 버전으로 사용돼요. 즉 crontabs의 생성이나 업데이트는 v1beta1 API의 객체 스키마로 저장돼요. 여기서 주목할 점은, time 필드가 스토리지 정의에 포함되지 않았으므로 v1 API 객체는 time 필드를 절대 저장할 수 없다는 뜻이 돼요. 이 스키마는 스토리지 레이어에서 객체 자체의 바이너리 인코딩으로 사용돼요. 두 버전을 동시에 스토리지 버전으로 설정하는 것은 유효하지 않아요. 두 개의 데이터 스키마가 동시에 객체 저장의 유효한 방식으로 간주된다는 뜻이 되기 때문이에요.

스토리지에 사용되는 버전을 수정하면, 그 버전의 API가 새 CR이나 업데이트된 CR을 저장하는 데 사용돼요. 객체를 watch하거나 get하면 객체가 사용 중이지만, 단지 객체를 이전 스토리지 버전에서 변환할 뿐 객체에는 영향을 주지 않아요. 업데이트나 생성만이 영향을 주며 새로 정의된 스토리지 버전을 사용해요.

스토리지 버전과 저장 시 암호화 (encryption at rest)의 관계

클러스터의 저장소 암호화 도구가 있어요. 특히 클러스터 시크릿을 위한 것이죠. 이는 저장된 실제 데이터가 암호화되기 때문에 데이터 유출에 대한 추가 보호 계층을 제공해요. 즉 API 서버가 스토리지에서 데이터를 가져올 때 실제로 데이터를 복호화한다는 뜻이에요. APIServer는 객체를 제대로 디코딩하려면 해당 스토리지 버전의 키를 가지고 있어야 해요.

이 경우 스토리지 버전은 단순한 객체의 바이너리 인코딩 그 이상이에요. 저장된 것이 어떻게든 API 객체로 변환될 수 있다면 그것을 스토리지 버전으로 사용할 수 있어요.

다른 스토리지 버전으로 마이그레이션하기 (Migrating to a different storage version)

단일 리소스에 여러 스토리지 버전이 있으면 클러스터 관리자에게 문제가 될 수 있어요. 클러스터 관리자는 모든 객체가 더 이상 그와 연관된 스토리지 버전을 사용하지 않는다는 것을 확신하기 전까지는 CRD의 구버전 API를 제거하지 못할 수 있어요. 객체 수가 많고 어떤 객체가 새 버전이고 어떤 객체가 여전히 구버전 스토리지에 의존하는지 불투명하다면, 언제 버전을 안전하게 제거할 수 있는지 판단하기 어려워져요. 버전을 너무 일찍 제거하면 객체를 완전히 읽지 못하게 될 수 있어요.

또 다른 중요한 문제는 위 섹션에서 설명한 암호화 키 사용이에요. 리소스가 스토리지 버전을 업데이트하려면 활발히 사용 중이어야 하기 때문에, 키 순환(rotation)을 할 때 관리자가 모든 객체가 적어도 한 번은 쓰였다고 확신할 때까지는 이전 암호화 키와 새 암호화 키를 모두 계속 사용해야 해요. 키를 완전히 사용 중지할 수 없다는 점은 보안 위험과 사용성 문제를 모두 야기해요.

수동 개입 없이 모든 객체가 더 새로운 스토리지 버전을 사용하도록 마이그레이션을 실행하는 예시는 스토리지 버전 마이그레이션(storage version migration) 문서를 참고하세요.

더 알아보기 (Learn more)