[☸️K8s] 쿠버네티스 리소스와 GVK 체계

리소스와 오브젝트의 차이, GVK(Group/Version/Kind)가 API 확장성과 하위 호환성을 어떻게 보장하는지, 그리고 Namespace-scoped와 Cluster-scoped 리소스 분류를 정리합니다.

[☸️K8s] 쿠버네티스 리소스와 GVK 체계

“쿠버네티스에서 리소스가 뭐야?” “Pod랑 Deployment는 뭐가 달라?” 처음 들으면 다 같은 말 같지만, 정확히 구분하지 못하면 GVK 체계나 RBAC을 이해할 때 계속 헷갈린다. 1주차 핵심 개념인 리소스·오브젝트·GVK를 한 번 제대로 정리하자!


What this post covers

  • 리소스(Resource)와 오브젝트(Object)의 정확한 구분
  • kubectl api-resources 출력의 각 컬럼 의미
  • GVK(Group/Version/Kind) 체계와 API 확장성
  • GVK와 GVR(Group/Version/Resource)의 매핑 관계
  • Namespace-scoped vs Cluster-scoped 리소스 분류 기준
  • 기능 기준 분류와 학습 로드맵

1. 리소스(Resource)란 무엇인가?

1.1 API Object로서의 리소스

쿠버네티스에서 리소스(Resource)는 쿠버네티스 API가 노출하는 특정 종류의 오브젝트를 표현하는 엔드포인트다.

쿠버네티스 공식 문서는 리소스를 다음과 같이 정의한다.

“A resource is an endpoint in the Kubernetes API that stores a collection of API objects of a certain kind.” — Kubernetes Documentation, API Overview

이것을 구체적으로 풀면, 쿠버네티스 API Server는 HTTP REST API 서버이며, 각 리소스는 그 API의 특정 경로(path)에 대응한다.

1
2
3
GET  /api/v1/namespaces/default/pods          → Pod 리소스 목록 조회
POST /api/v1/namespaces/default/pods          → Pod 오브젝트 생성
GET  /apis/apps/v1/namespaces/default/deployments  → Deployment 리소스 목록 조회

즉, 리소스라는 개념은 “쿠버네티스 API의 URL 경로 상에 존재하는 엔드포인트”를 의미한다.

kubectl이 실행하는 모든 명령은 결국 이 HTTP 엔드포인트에 대한 요청으로 변환된다.

리소스는 “클러스터 안에 있는 물건”이라기보다, 그 물건을 다루기 위해 API Server가 제공하는 공식 API 경로에 가깝다.

이 구조는 운영체제의 시스템 콜 인터페이스와 동일한 역할을 한다.

OS에서 프로세스는 커널 기능에 직접 접근하지 못하고 반드시 시스템 콜이라는 정의된 인터페이스를 통해서만 접근한다. 쿠버네티스에서도 클러스터의 모든 상태 변경은 API Server라는 단일 인터페이스를 통해서만 가능하다. 어떤 컴포넌트도 etcd를 직접 읽거나 쓰지 않는다.


1.2 리소스(타입)와 오브젝트(인스턴스)의 구분

리소스와 오브젝트는 자주 혼용되지만 정확히 구분해야 한다.

개념의미예시
리소스(Resource)타입 또는 API 엔드포인트Pod, Deployment, Service, /api/v1/pods
오브젝트(Object)리소스의 구체적인 인스턴스default Namespace의 nginx-pod

프로그래밍 언어에 비유하면 리소스는 클래스(class) 정의, 오브젝트는 클래스의 인스턴스에 해당한다.

1
2
3
4
리소스(타입)        →   Pod
오브젝트(인스턴스)   →   nginx-pod (default 네임스페이스)
                    →   api-server-pod (kube-system 네임스페이스)
                    →   worker-pod (production 네임스페이스)

kubectl get pods는 Pod 리소스 엔드포인트에 GET 요청을 보내서 현재 존재하는 Pod 오브젝트 목록을 받아오는 것이다.

kubectl apply -f pod.yaml은 YAML에 정의된 스펙으로 새로운 Pod 오브젝트를 생성하도록 API Server에 요청하는 것이다.

kubectl get pods에서 pods는 리소스이고, 출력되는 각 행은 실제 Pod 오브젝트다.

모든 쿠버네티스 오브젝트는 공통 메타데이터 구조를 가진다.

1
2
3
4
5
6
7
8
9
apiVersion: apps/v1       # 어떤 API 그룹의 어떤 버전인지
kind: Deployment          # 어떤 리소스 타입인지
metadata:
  name: nginx             # 오브젝트 식별자
  namespace: default      # 소속 네임스페이스 (Namespace-scoped 리소스의 경우)
  uid: 1a2b3c4d-...       # API Server가 부여하는 전역 고유 ID
  resourceVersion: "12345" # etcd 내 버전 번호 (낙관적 동시성 제어에 사용)
spec: ...                 # 사용자가 선언한 목표 상태
status: ...               # 시스템이 관찰한 현재 상태

uidresourceVersion은 쿠버네티스 내부 동시성 제어에 사용된다.

특히 resourceVersion은 etcd의 revision 번호를 반영하며, Controller가 Watch할 때 “어느 시점 이후의 변경만 받을지”를 지정하는 커서 역할을 한다. 이 필드 덕분에 Watch가 중단됐다가 재연결될 때 누락 없이 이벤트를 이어받을 수 있다.


1.3 kubectl api-resources로 보는 전체 리소스 목록

클러스터에 등록된 모든 리소스 타입은 다음 명령으로 확인할 수 있다.

1
kubectl api-resources

출력 결과는 다음과 같은 형태다.

1
2
3
4
5
6
7
NAME                    SHORTNAMES  APIVERSION                  NAMESPACED  KIND
pods                    po          v1                          true        Pod
deployments             deploy      apps/v1                     true        Deployment
nodes                   no          v1                          false       Node
persistentvolumes       pv          v1                          false       PersistentVolume
namespaces              ns          v1                          false       Namespace
clusterroles                        rbac.authorization.k8s.io/v1  false    ClusterRole

각 컬럼이 의미하는 바는 다음과 같다.

컬럼의미
NAMEAPI 엔드포인트 경로에서 사용되는 복수형 이름. kubectl get pods에서 pods가 이것이다.
SHORTNAMESkubectl에서 사용할 수 있는 단축 이름. po, deploy, svc 등이 여기에 해당한다.
APIVERSION이 리소스가 속한 API 그룹과 버전. v1은 core 그룹, apps/v1apps 그룹의 v1 버전이다.
NAMESPACEDtrue이면 Namespace-scoped, false이면 Cluster-scoped 리소스다.
KINDYAML의 kind 필드에 들어가는 값. Pod, Deployment, Node 등 단수형 파스칼케이스 이름이다.

kubectl api-resources가 중요한 이유는 이것이 단순한 목록 출력이 아니라, API Server에 등록된 리소스 타입의 스키마 정보를 동적으로 조회하는 것이기 때문이다. 쿠버네티스는 CRD(CustomResourceDefinition)를 통해 새로운 리소스 타입을 런타임에 추가할 수 있는데, 새로 등록된 CRD도 이 명령의 결과에 즉시 나타난다. 이 확장 메커니즘은 5주차에서 자세히 다룬다.


2. GVK: Group / Version / Kind 체계

2.1 왜 GVK가 필요한가?

쿠버네티스는 처음부터 지금의 규모를 가진 시스템이 아니었다.

초기에는 Pod, Service, ReplicationController 같은 소수의 리소스만 존재했고, 이것들은 단일 API 경로(/api/v1/)에 모두 담겨 있었다.

시스템이 성장하면서 문제가 생겼다.

리소스 종류가 급격히 늘어났고, 각 리소스의 스펙도 계속 변경해야 했다. 특정 리소스의 스펙을 바꾸면 그 리소스를 사용하는 모든 사용자의 YAML이 즉시 깨질 수 있다. 동시에 쿠버네티스 외부 프로젝트들도 자신만의 리소스 타입을 클러스터에 추가하고 싶어했다.

이 세 가지 문제, 즉 리소스의 분류 관리, 하위 호환성 보장, 외부 확장 지원을 해결하기 위해 설계된 것이 GVK 체계다.

GVK는 “이 YAML이 정확히 어떤 API 타입을 말하는가?”에 대한 쿠버네티스의 답이다.


2.2 Group

API Group은 관련된 리소스들을 논리적으로 묶는 단위다.

쿠버네티스의 API Group은 두 종류로 나뉜다.

구분API 경로YAML 표현예시
Core Group/api/v1apiVersion: v1Pod, Service, ConfigMap
Named Group/apis/<group>/<version>apiVersion: apps/v1Deployment, Job, Ingress

Core Group (Legacy Group)은 쿠버네티스 초기부터 존재했던 가장 기본적인 리소스들의 그룹이다. 그룹명이 없으며 API 경로는 /api/v1이다. YAML에서는 apiVersion: v1으로 표현한다.

1
2
3
4
5
6
7
8
9
# Core Group 리소스 예시
apiVersion: v1
kind: Pod

apiVersion: v1
kind: Service

apiVersion: v1
kind: ConfigMap

Named Group은 기능 도메인 단위로 명시적인 이름을 가진 그룹이다. API 경로는 /apis/<group>/<version> 형태다.

1
2
3
4
5
6
7
8
9
10
11
12
# Named Group 리소스 예시
apiVersion: apps/v1
kind: Deployment          # apps 그룹

apiVersion: batch/v1
kind: Job                 # batch 그룹

apiVersion: networking.k8s.io/v1
kind: Ingress             # networking.k8s.io 그룹

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole         # rbac.authorization.k8s.io 그룹

그룹명은 도메인 이름 형태를 따른다. 쿠버네티스 내장 그룹은 *.k8s.io 형태를 사용하고, 외부 프로젝트가 CRD로 추가하는 그룹은 자신의 도메인을 사용한다. 예를 들어 Prometheus Operator는 monitoring.coreos.com, Cert-manager는 cert-manager.io 그룹을 사용한다. 이 네이밍 방식은 DNS 도메인과 동일한 원리로 이름 충돌을 방지한다.


2.3 Version

Version은 동일한 리소스의 스펙이 시간이 지남에 따라 변경될 수 있다는 현실을 수용하는 메커니즘이다.

쿠버네티스의 API 버전은 세 단계의 성숙도를 가진다.

1
v1alpha1  →  v1beta1  →  v1
단계특징사용 권장
alpha (v1alpha1, …)실험적 기능. 기본 비활성화. 다음 릴리스에서 예고 없이 변경/제거 가능❌ 프로덕션 사용 금지
beta (v1beta1, …)기능 검증 완료. 기본 활성화. GA 전 세부 변경 가능성 있음⚠️ 주의해서 사용
GA (v1, v2, …)안정화된 버전. 하위 호환성 보장. 스펙 변경 없이 유지✅ 프로덕션 사용 권장

하나의 리소스가 동시에 여러 버전으로 제공될 수 있다.

API Server는 내부적으로 특정 저장 버전(storage version)을 사용하고, 클라이언트가 요청한 버전에 맞게 자동으로 변환한다. 이 변환 작업은 API Server 내부의 변환 로직이 처리하며, CRD에서는 필요에 따라 Conversion Webhook을 사용할 수 있다.

덕분에 사용자가 v1beta1으로 작성한 YAML은 내부에서 v1으로 변환되어 저장되고, 다시 v1beta1으로 읽어올 수 있다.


2.4 Kind

Kind는 리소스의 타입 이름이다.

YAML의 kind 필드에 들어가는 값이며 항상 단수형 파스칼케이스(PascalCase)를 사용한다.

Kind는 Group + Version이라는 맥락 안에서만 의미가 있다. 같은 이름의 Kind가 서로 다른 Group에 존재할 수 있기 때문이다. 예를 들어 Ingress는 쿠버네티스 1.18 이전에는 extensions/v1beta1 그룹에 있었고, 이후 networking.k8s.io/v1으로 이동했다. 두 Group의 Ingress는 같은 이름이지만 서로 다른 리소스다.

1
2
3
4
5
6
7
# 이전 (deprecated)
apiVersion: extensions/v1beta1
kind: Ingress

# 현재 (stable)
apiVersion: networking.k8s.io/v1
kind: Ingress

이 때문에 쿠버네티스 내부에서 리소스를 식별할 때는 Kind 단독이 아닌 Group + Version + Kind 세 가지를 조합한 GVK를 완전한 식별자로 사용한다.

kind: Ingress만 보고 같은 리소스라고 판단하면 안 된다. apiVersion까지 함께 봐야 정확한 API 타입을 알 수 있다.


2.5 GVK와 GVR — API 경로와의 매핑

쿠버네티스 API에는 GVK와 구분되는 개념으로 GVR(Group / Version / Resource)이 있다.

구분사용 위치마지막 요소
GVKYAML 작성, 코드에서 오브젝트 타입 참조Kind
GVRHTTP API 엔드포인트 경로 구성복수형 소문자 Resource

URL에는 Kind 대신 복수형 소문자 리소스 이름(Resource)이 쓰인다.

1
2
3
4
GVK:  Group=apps, Version=v1, Kind=Deployment
GVR:  Group=apps, Version=v1, Resource=deployments

→ API 경로: /apis/apps/v1/namespaces/default/deployments

kubectl이 YAML을 받으면 apiVersionkind 필드로 GVK를 파악한다.

그다음 API Server에 등록된 스키마 정보를 조회하여 해당 GVK에 대응하는 GVR을 찾고, 최종 HTTP 요청 경로를 결정한다. 이 매핑 정보는 API Server의 Discovery API(/apis)를 통해 제공된다.


2.6 YAML apiVersion / kind와 GVK의 대응

실제 YAML 작성 시 GVK의 세 요소가 어떻게 표현되는지 정리하면 다음과 같다.

1
2
apiVersion: apps/v1    # Group=apps, Version=v1
kind: Deployment       # Kind=Deployment
1
2
apiVersion: v1         # Group=core(없음), Version=v1
kind: Pod              # Kind=Pod
1
2
apiVersion: batch/v1   # Group=batch, Version=v1
kind: CronJob          # Kind=CronJob

Core Group의 경우 그룹명이 없으므로 apiVersion에 버전만 표기한다.

Named Group은 그룹/버전 형태로 표기한다. kind는 항상 GVK의 Kind 값을 그대로 사용한다.


3. 리소스 분류 지도

3.1 두 가지 기준으로 보는 리소스 분류

쿠버네티스 리소스는 두 가지 독립적인 기준으로 분류할 수 있다.

기준질문왜 중요한가
스코프(Scope)특정 Namespace에 속하는가, 클러스터 전체에 속하는가?가시성과 접근 제어 범위를 결정
기능(Function)워크로드, 네트워크, 스토리지 중 어떤 역할인가?학습 순서와 운영 관점을 결정

두 기준은 독립적이다.

워크로드 리소스 중에도 Namespace-scoped인 것과 Cluster-scoped인 것이 섞여 있다. 분류 기준을 혼동하면 나중에 접근 제어와 RBAC을 이해할 때 혼란이 생기므로 처음부터 구분해서 이해해야 한다.


3.2 스코프 기준 분류: Namespace-scoped vs Cluster-scoped

Namespace-scoped 리소스

Namespace-scoped 리소스는 반드시 하나의 Namespace에 속한다.

오브젝트 생성 시 Namespace를 지정해야 하며, 지정하지 않으면 default Namespace에 생성된다. API 경로에 Namespace가 포함된다.

1
2
/api/v1/namespaces/{namespace}/pods
/apis/apps/v1/namespaces/{namespace}/deployments

주요 Namespace-scoped 리소스는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
워크로드    Pod, ReplicaSet, Deployment, StatefulSet,
           DaemonSet, Job, CronJob

네트워크    Service, Ingress, EndpointSlice

스토리지    PersistentVolumeClaim (PVC)

설정·보안   ConfigMap, Secret, ServiceAccount

RBAC       Role, RoleBinding

Cluster-scoped 리소스

Cluster-scoped 리소스는 어떤 Namespace에도 속하지 않는다.

클러스터 전체가 스코프이므로 소속 Namespace라는 개념 자체가 없다. API 경로에 Namespace 세그먼트가 없다.

1
2
3
/api/v1/nodes
/api/v1/persistentvolumes
/api/v1/namespaces

주요 Cluster-scoped 리소스는 다음과 같다.

1
2
3
4
5
6
7
8
9
인프라        Node, Namespace 자체

스토리지      PersistentVolume (PV), StorageClass

RBAC         ClusterRole, ClusterRoleBinding

런타임        RuntimeClass

네트워크      IngressClass

Namespace 자체가 Cluster-scoped인 이유

Namespace는 Cluster-scoped 리소스다.

이것은 순환 의존 문제를 피하기 위한 설계다. Namespace-scoped 리소스는 Namespace에 속하는데, 만약 Namespace 자신도 어떤 Namespace에 속한다면 그 상위 Namespace는 또 어디에 속해야 하느냐는 무한 재귀 문제가 생긴다.

Namespace가 Cluster-scoped이기 때문에 이 재귀가 클러스터 수준에서 종료된다.

kubectl 동작 차이

스코프 구분은 kubectl 명령의 동작에 직접 영향을 준다.

1
2
3
4
5
6
7
8
9
10
11
12
# default Namespace의 Pod만 조회
kubectl get pods

# 모든 Namespace의 Pod 조회 (-A = --all-namespaces)
kubectl get pods -A

# Namespace-scoped 리소스는 -n으로 Namespace 지정 가능
kubectl get pods -n kube-system

# Cluster-scoped 리소스는 -n 옵션 자체가 의미 없음
kubectl get nodes                # 올바른 사용
kubectl get nodes -n default     # Namespace 개념이 없으므로 무의미

kubectl get pods가 전체 Pod를 보여주지 않는 이유가 여기 있다.

Pod는 Namespace-scoped 리소스이므로 kubectl은 기본적으로 현재 컨텍스트의 Namespace만 조회한다. -A 플래그는 모든 Namespace에 걸쳐 조회하라는 의미이며, 이때 API 경로가 달라진다.

1
2
3
4
5
# -n default
GET /api/v1/namespaces/default/pods

# -A (--all-namespaces)
GET /api/v1/pods   ← Namespace 세그먼트 없는 경로로 전체 조회

5주차 RBAC 연결 포인트

스코프 구분은 5주차 RBAC에서 RoleClusterRole의 차이로 직접 이어진다.

Role은 Namespace-scoped 리소스다.

특정 Namespace 안에서만 권한을 부여할 수 있다. ClusterRole은 Cluster-scoped 리소스로, 클러스터 전체 범위의 권한을 정의한다.

1
2
3
Role          →  Namespace-scoped  →  특정 Namespace 내 리소스에 대한 권한
ClusterRole   →  Cluster-scoped    →  클러스터 전체 리소스에 대한 권한
                                      (Node, PV 같은 Cluster-scoped 리소스 포함)

Cluster-scoped 리소스인 NodePersistentVolume에 대한 권한은 Role로는 부여할 수 없다.

이 리소스들은 Namespace에 속하지 않기 때문에 Namespace 범위의 Role이 적용되지 않는다. 반드시 ClusterRole을 사용해야 한다. 이 제약이 스코프 구분에서 직접 파생된다.

RBAC을 볼 때는 먼저 대상 리소스가 Namespace-scoped인지 Cluster-scoped인지 확인해야 한다. 이 구분이 RoleClusterRole 선택을 결정한다.


3.3 기능 기준 분류: 학습 로드맵

스코프와 독립적으로, 리소스가 수행하는 역할에 따른 기능 분류는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
워크로드 리소스
├─ Pod, ReplicaSet, Deployment
├─ StatefulSet, DaemonSet
└─ Job, CronJob
    → 2주차: 컨테이너 실행과 배포 관리

네트워크 리소스
├─ Service (ClusterIP, NodePort, LoadBalancer)
├─ Ingress, IngressClass
└─ EndpointSlice
    → 3주차: 클러스터 내외부 트래픽 라우팅

스토리지 리소스
├─ PersistentVolume (PV)
├─ PersistentVolumeClaim (PVC)
└─ StorageClass
    → 4주차: 컨테이너의 영속적 데이터 관리

설정·보안 리소스
├─ ConfigMap, Secret
├─ ServiceAccount
└─ Role, ClusterRole, RoleBinding, ClusterRoleBinding
    → 5주차: 애플리케이션 설정 주입과 접근 제어

클러스터 리소스
├─ Node, Namespace
└─ RuntimeClass
    → 0주차 아키텍처, 2주차 CRI와 연결

각 기능 범주는 대응하는 인터페이스와 페어링된다.

기능 범주대응 인터페이스주차
워크로드CRI (Container Runtime Interface)2주차
네트워크CNI (Container Network Interface)3주차
스토리지CSI (Container Storage Interface)4주차
설정·보안대응 독립 인터페이스 없음5주차

설정·보안 리소스가 대응 인터페이스 없이 별도 주차로 분리된 이유는 커리큘럼 특징에서 설명한 것과 같다.

ConfigMap, Secret, RBAC은 독립적인 표준 인터페이스를 갖지 않지만, 실제 워크로드 운영에서 반드시 필요한 구성 요소이므로 독립 주차로 다룬다.


핵심 정리 🎯

  • 리소스(Resource)는 API 엔드포인트(타입), 오브젝트(Object)는 그 인스턴스다.
  • GVK는 Group + Version + Kind로 리소스를 완전히 식별한다. Kind만으로는 부족하다.
  • GVR은 GVK에 대응하는 API 경로를 만든다. Kind 대신 복수형 소문자 Resource를 사용한다.
  • Namespace-scoped 리소스는 Namespace에 속하고, Cluster-scoped 리소스는 클러스터 전체에 속한다.
  • Namespace 자체는 Cluster-scoped다. (순환 의존 방지)
  • Role은 Namespace-scoped 권한만, ClusterRole은 Cluster-scoped 리소스 권한도 줄 수 있다.

Reading flow

  • Previous: [☸️K8s] 쿠버네티스를 공부하는 이유와 아키텍처 한눈에 보기_posts/k8s/2026-05-15-K8s(1).md
  • Next: ...
  • Series: /series/