오늘은 쿠버네티스를 사용한다면 필수라고 할 수 있는 label에 대해 알아보는 시간을 가져보겠다.
레이블은 쿠버네티스 클러스터 내부에 사용자가 객체를 생성할때 그 객체를 구분하기 위해서 임의로 원하는 값을 지정해서 사용한다.

이처럼 오브젝트마다 Key-Value로 레이블을 정의할 수 있으며 만일 수십개 이상의 오브젝트가 존재한다면 레이블 없이 관리하기 어려울 것이다.
다음의 예시는 파드에 name: mainui, rel: stable 2개의 레이블이 있는 구성 파일이다.
apiVersion: v1
kind: Pod
metadata:
name: label-pod-demo
labels:
name: mainui
rel: stable
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
기본적인 Nginx 파드와 레이블을 정의한 파드를 생성하여 레이블을 확인해보자.
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
label-pod-demo 1/1 Running 0 38s name=mainui,rel=stable
pod-demo 1/1 Running 0 38s <none>
$ kubectl get pod -l name=mainui
NAME READY STATUS RESTARTS AGE
label-pod-demo 1/1 Running 0 3m23s
$ kubectl get pod --selector name=mainui
NAME READY STATUS RESTARTS AGE
label-pod-demo 1/1 Running 0 3m57s
아무런 레이블이 적용되어 있지 않은 pod-demo 파드에 name=test 라는 레이블을 적용 시켜보았다.
$ kubectl label pod pod-demo name=test
pod/pod-demo labeled
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
label-pod-demo 1/1 Running 0 5m28s name=mainui,rel=stable
pod-demo 1/1 Running 0 5m28s name=test
이미 레이블이 존재하고 있을 경우 덮어쓰기를 할 수 있는 옵션이 별도로 존재한다.
pod-demo 파드에 name=login 라는 레이블을 덮어씌워보자.
$ kubectl label pod pod-demo name=login --overwrite
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
label-pod-demo 1/1 Running 0 6m56s name=mainui,rel=stable
pod-demo 1/1 Running 0 6m56s name=login
$ kubectl get pod
NAME READY STATUS RESTARTS AGE LABELS
testpod 1/1 Running 0 10m run=testpod
$ kubectl label pod testpod run-
pod/testpod unlabeled
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
testpod 1/1 Running 0 11m <none>
워커 노드의 특성을 레이블로 설정해보자.
kubectl label node <노드 이름> <레이블 키>-<레이블 값>
워커 노드에 레이블을 설정하는 이유는 보통 노드를 선택해서 파드를 배치하기 위함이다.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-test-today-cluster-control-plane Ready control-plane 15d v1.30.0
kind-test-today-cluster-worker Ready <none> 15d v1.30.0
kind-test-today-cluster-worker2 Ready <none> 15d v1.30.0
kind-test-today-cluster-worker3 Ready <none> 15d v1.30.0

위와 같이 노드가 존재한다고 할 때 세개의 워커노드에 그림과 같이 레이블을 설정하는 실습을 진행해보겠다.
커맨드 라인을 통해 간편히 적용 후 조회해보자.
$ kubectl label nodes kind-test-today-cluster-worker gpu=true disk=ssd
$ kubectl label nodes kind-test-today-cluster-worker2 gpu=true
$ kubectl label nodes kind-test-today-cluster-worker3 disk=ssd

처음 노드를 생성할 때 함께 정의된 레이블 사이에 새로 설정한 레이블이 확인된다.
$ kubectl get nodes -L disk,gpu
NAME STATUS ROLES AGE VERSION DISK GPU
kind-test-today-cluster-control-plane Ready control-plane 15d v1.30.0
kind-test-today-cluster-worker Ready <none> 15d v1.30.0 ssd true
kind-test-today-cluster-worker2 Ready <none> 15d v1.30.0 true
kind-test-today-cluster-worker3 Ready <none> 15d v1.30.0 ssd
이제 Selector를 이용해 노드를 지정해서 파드를 실행해보도록 하자.
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
spec:
nodeSelector:
gpu: "true"
disk: ssd
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
아까의 실습에서gpu:true와 disk:ssd 레이블을 다 가지고 있는 노드는 kind-test-today-cluster-worker 임을 우리는 알고 있다.
nodeselector.yaml 파일에 위와같이 선언하고 파드를 실행하면 관련 노드로 파드가 실행될 것이다.
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeselector 1/1 Running 0 5s 10.244.2.6 kind-test-today-cluster-worker <none> <none>
어노테이션은 레이블과 동일하게 key-vaule를 통해 리소스의 특성을 기록하는데, 쿠버네티스에게 특정 정보를 전달하는 용도로 사용되거나 관리를 위해 필요한 정보를 기록할 용도로 사용된다.
예를 들어 deployment에 롤링 업데이트 정보를 기억시킬 수가 있다.
apiVersion: v1
kind: Pod
metadata:
name: pod-annotation
annotations:
builder: "test lee"
buildDate: "20241107"
imageRegistry: https://hub.docker.com/
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
yaml 파일에 어노테이션을 설정하고 생성된 파드를 상세 조회하면 설정된 값을 확인할 수 있다.

기존 버전을 유지한 채로 일부 버전만 신규 버전으로 업데이트하여 신규 버전에 버그나 이상이 없는지 확인하는 배포 방식이다.
예를 들어 기존의 블루 버전의 deployment가 2개 있고 신규인 그린 버전의 deployment 1개가 존재한다고 할때 한번에 묶어 사용자들에게 서비스하는 방식이다.
이후 그린 버전의 deployment가 하나 더 추가되면 서비스가 확장되도록 할 수 있다.
예시를 실습을 통해 진행해보자
우선 version: stable 레이블을 가진 블루 버전의 deployment 2개를 실행하는 스크립트를 실행시켜 실습 환경을 만들어 둔다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mainui-stable
spec:
replicas: 2
selector:
matchLabels:
app: mainui
version: stable
template:
metadata:
labels:
app: mainui
version: stable
spec:
containers:
- name: mainui
image: nginx:1.14
ports:
- containerPort: 80
$ Kubectl create -f mainui-stable.yaml
$ kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
mainui-stable-cc7bdcfbb-ddz2c 1/1 Running 0 18s app=mainui,pod-template-hash=cc7bdcfbb,version=stable
mainui-stable-cc7bdcfbb-lccsm 1/1 Running 0 18s app=mainui,pod-template-hash=cc7bdcfbb,version=stable
생성된 파드를 서비스로 묶어주는 작업까지하면 블루 버전의 서비스가 돌아가는 환경이 된다.
apiVersion: v1
kind: Service
metadata:
name: mainui-svc
spec:
selector:
app: mainui
ports:
- port: 8080
protocol: TCP
targetPort: 8080
app: mainui 레이블을 기준으로 두개의 파드를 묶어주는 서비스 스크립트를 실행
$ kubectl create -f mainui-svc.yaml
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mainui-svc ClusterIP 10.96.27.73 <none> 8080/TCP 9s
두 파드를 하나의 단일 진입점으로 묶어주었다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mainui-canary
spec:
replicas: 1
selector:
matchLabels:
app: mainui
version: canary
template:
metadata:
labels:
app: mainui
version: canary
spec:
containers:
- name: mainui
image: nginx:1.14
ports:
- containerPort: 80
이제 신규 버전인 version:canary 레이블을 가진 파드를 mainui-svc에 추가하는 작업을 진행해보겠다.
$ kubectl create -f mainui-canary.yaml
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
mainui-canary-6f49855785-k4ddt 1/1 Running 0 6s
mainui-stable-cc7bdcfbb-ddz2c 1/1 Running 0 8m24s
mainui-stable-cc7bdcfbb-lccsm 1/1 Running 0 8m24s
$ kubectl describe svc mainui-svc
Name: mainui-svc
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=mainui
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.27.73
IPs: 10.96.27.73
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
Endpoints: 10.244.1.6:8080,10.244.2.7:8080,10.244.3.9:8080
Session Affinity: None
Events: <none>
Endpoints 에 카나리 버전의 파드가 추가된 것을 확인할 수 있다.
이렇게 새로운 버전의 배포가 완료되면 버그와 동작 여부를 확인하면서 업데이트를 진행하면 된다.
점점 카나리 버전을 하나 더 늘리고 스테이블 버전을 하나 줄이는 식으로 진행되다가 마지막엔 스테이블 버전이 전부 삭제되고 신규 버전으로 교체되는 형태가 될 것이다.
[참고자료]
https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/labels/
https://anggeum.tistory.com/entry/Kubernetes-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%EB%A0%88%EC%9D%B4%EB%B8%94-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-Label-Annotation-Deep-Dive