K8S 클러스터에는 Service를 사용하는 IP 대역이 있고 Pod를 사용하는 IP 대역이 있는데, Pod에 ip에 접근하려면 Service만 접근이 가능하다. 이게 가능한 이유는 kube-proxy 라는 녀석이 Service와 연결된 Pod들에게 적절하게 트래픽을 흐르게 만들어준다.
또한 쿠버네티스 클러스터 안에는 DNS 서버가 존재하는데, 여기에는 Service의 도메인 이름과 해당 ip를 알려준다. 또한 내부망에서도 DNS 서버가 구축되어 있다면 서버가 생겼을 때 해당 네임들이 DNS 서버에 저장이 된다. 예를 들어 Pod가 user1이라는 네임을 찾았을 때 쿠버네티스 DNS 서버에 없다면 DNS 메커니즘상 상위 DNS를 찾게 되고 해당 이름의 ip를 알려준다.
만약에 DB 혹은 고유한 네트워크 식별자 같은 서비스가 필요한 경우에 일반적인 Service로 연결이 되면 Pod에 분산처리 되어서 내가 원하는 Pod에 접근을 할 수 없다. 이 경우 Headless Service를 쓰면 해결을 할 수 있다.
Pod1과 Pod2가 있다고 가정을 해보자 그리고 이 Pod들은 Headless Service와 연결이 되어있다. 일반적인 ClusterIP, LoadBalancer, NodePort 타입의 Service들은 dns에 서비스의 IP가 저장이 되어있지만 Headless Service로 생성을 하면 Pod1과 Pod2의 ip가 dns에 저장이 된다.
apiVersion: v1
kind: Service
metadata:
name: headless1
spec:
selector:
svc: headless
ports:
- port: 80
targetPort: 8080
clusterIP: None
Headless Service를 만드는것은 의외로 간단하다. clusterIP: None으로 지정해주면 Headless Service를 만들 수 있다. 이 서비스와 연결되는 Pod들은 다음과 같다.
apiVersion: v1
kind: Pod
metadata:
name: poda
labels:
svc: headless
spec:
hostname: pod-a
subdomain: headless1
containers:
- name: container
image: kubetm/app
apiVersion: v1
kind: Pod
metadata:
name: podb
labels:
svc: headless
spec:
hostname: pod-b
subdomain: headless1
containers:
- name: container
image: kubetm/app
이렇게 Pod 두개를 만들고 nslookup을 통해 dns의 정보를 확인 해볼수 있다.
nslookup headless1
nslookup pod-a.headless1
nslookup pod-b.headless1
보통 Service와 Pod를 연결할 때 label을 통해서 연결을 하는데, 이건 사용자 측면에서 연결을 해주는 것이고 쿠버네티스는 Endpoint라는 것을 만들어서 실제 연결고리를 만들어준다.
Endpoint는 Service의 이름과 동일한 이름으로 Endpoint를 설정하고 Endpoint에는 Pod의 접속 정보가 담겨있다.
label과 selector를 않고 Service와 Pod를 만든 다음 이를 연결 시켜주는 Endpoint를 Service의 이름으로 만들고 Pod의 ip 정보를 넣게되면 똑같이 만들 수 있다.
apiVersion: v1
kind: Service
metadata:
name: endpoint
spec:
ports:
- port: 8080
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- name: container
image: kubetm/app
이렇게 label과 selector를 지정하지 않고 Pod와 Service를 만들고 EndPoint를 만들어준다.
apiVersion: v1
kind: Endpoints
metadata:
name: endpoint
subsets:
- addresses:
- ip: 20.109.5.12
ports:
- port: 8080
Endpoint는 IP 로 연결을 시켜줬지만 ip는 변경 가능성이 있기에 도메인을 지정을 시켜주는게 안전하다. External Name에는 특정 외부 도메인 주소를 넣을수 있는데, DNS를 타고 타서 ip의 주소를 가져오고 Pod의 수정 없이 ExternalName만 변경하면 외부의 ip를 가져올 수 있다.
apiVersion: v1
kind: Service
metadata:
name: externalname1
spec:
type: ExternalName
externalName: github.github.io
Volume은 데이터를 안정적으로 유지하기 위해서 관리가 되는 Object인데, K8S 클러스터안에서도 만들 수 있지만 보통은 분리가 되서 관리가 된다. 가령 AWS EBS, Azure, GCP 등에서 클라우드 스토리지에 연결을해서 사용하기 때문이다. kubernetes 자체적으로 hostPath나 local을 사용하면 되지만 온프레미스 솔루션들을 클러스터 안에 설치해서 사용해도 된다.
앞서 Kubernetes Objects1 포스트에서 말했듯이 볼륨은 PV를 Access Mode와 Storage를 지정하여 만들어서 PVC에서 할당을 하고 싶은 용량과 AccessMode가 매칭하는 PV가 있으면 연결을 하고 Pod는 PVC와 연결이 된다. Access Mode는 다음과 같다.
기존 PVC, PV의 사용방식의 문제는 각각의 PVC 마다 지원되는 PV가 다를수 있기 때문에 PVC를 만드려면 거기에 맞는 PV 를 항상 만들어 줘야한다.
이를 해결해주는게 Dynamic Provisioning인데, PVC를 만들면 PV가 알아서 만들어지도록 해준다.
여기서 중요한건 Storage Class를 만들어 줘야하는데, Storage Class를 통해서 동적으로 PV를 만들어 줄수 있다. PVC 에서 StorageClassName를 지정 해놓으면 미리 만들어진 외부볼륨(AWS, Azure, GCP)의 PV가 생성된다.
Longhorn 이란 온프레미스 솔루션을 다운 받는다.
kubectl apply -f https://raw.githubusercontent.com/kubetm/kubetm.github.io/master/yamls/longhorn/longhorn.yaml
우린 Dynamic Provisioning 을 위해 StorageClass 를 만들어 줘야한다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: driver.longhorn.io
parameters:
dataLocality: disabled
fromBackup: ""
fsType: ext4
numberOfReplicas: "3"
staleReplicaTimeout: "30"
여기에 parameters 와 provisioner는 사용하는 외부 볼륨에 따라 달라진다.
이 상태에서 바로 StorageName을 맞춰서 PVC를 생성해보자.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-fast1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1G
storageClassName: "fast"
이렇게 생성하면 PV가 자동적으로 만들어진다.
각각의 PV는 Pod 처럼 상태값이 존재한다.
여기서 Released 만약 Bound 상태에서 Pod가 삭제되어 PV를 어떻게 할지의 상태값을 주는 ReclaimPolicy도 있다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: default
annotations:
# Default StorageClass로 선택
storageclass.kubernetes.io/is-default-class: "true"
# 동적으로 PV생성시 PersistentVolumeReclaimPolicy 선택 (Default:Delete)
reclaimPolicy: Retain, Delete, Recycle
provisioner: kubernetes.io/storageos
# provisioner 종류에 따라 parameters의 하위 내용 다름
parameters:
ReclaimPolicy는 StorageClass를 생성할 때 Retain, Delete중 하나를 선택하여 생성할 수 있다.