
지난 시간에는 투두 리스트 앱을 배포하는데 사용된 쿠버네티스 워크로드 리소스들 중 가장 길었던 StatefulSets에 대해서 분석했다.
독립된 상태를 유지하는 리소스이기 때문에 mysql같은 데이터베이스 배포에 사용되며 크게 아래 사항들이 명세되었다.
1. 앱 실행에 필요한 베이스 이미지
2. 클러스터 내부 파드와 통신하기 위한 포트와 DNS 및 통신 프로토콜(TCP)
3. db 비밀번호와 같은 환경 변수들
4. 영구저장을 위한 volumeClaimTemplates
5. Pod 상태 관리 설정을 위한 Probe
이번에는 StatefulSets 이외 나머지 yaml 파일에 대해서 분석해보자.
k9s를 사용하여 PVC를 검색해보자.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
pv.kubernetes.io/bind-completed: "yes"
pv.kubernetes.io/bound-by-controller: "yes"
volume.beta.kubernetes.io/storage-provisioner: k8s.io/minikube-hostpath
volume.kubernetes.io/storage-provisioner: k8s.io/minikube-hostpath
creationTimestamp: "2023-12-05T15:25:15Z"
finalizers:
- kubernetes.io/pvc-protection
labels:
app.kubernetes.io/component: primary
app.kubernetes.io/instance: mysql-helm
app.kubernetes.io/name: mysql
name: data-mysql-helm-0
namespace: helm-test
resourceVersion: "627801"
uid: 8b3be04f-eee7-4c6f-bcd0-7ee92efb0a40
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
storageClassName: standard
volumeMode: Filesystem
volumeName: pvc-8b3be04f-eee7-4c6f-bcd0-7ee92efb0a40
status:
accessModes:
- ReadWriteOnce
capacity:
storage: 8Gi
phase: Bound
파드별 PVC가 만들어졌으니 그를 바탕으로 PV가 만들어진다. PV 명세파일을 분석해보자. 핵심 내용은 아래와 같다.
apiVersion: v1
kind: PersistentVolume
metadata:
annotations:
hostPathProvisionerIdentity: b3b2965f-b900-4ffd-956f-34e3191e33b5
pv.kubernetes.io/provisioned-by: k8s.io/minikube-hostpath
creationTimestamp: "2023-12-05T15:25:15Z"
finalizers:
- kubernetes.io/pv-protection
name: pvc-8b3be04f-eee7-4c6f-bcd0-7ee92efb0a40
resourceVersion: "627793"
uid: 22d969c2-124d-439c-84b0-f062f50924f6
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 8Gi
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: data-mysql-helm-0
namespace: helm-test
resourceVersion: "627785"
uid: 8b3be04f-eee7-4c6f-bcd0-7ee92efb0a40
hostPath:
path: /tmp/hostpath-provisioner/helm-test/data-mysql-helm-0
type: ""
persistentVolumeReclaimPolicy: Delete
storageClassName: standard
volumeMode: Filesystem
status:
phase: Bound
Headless Service는 외부 IP 주소가 할당되지않는 서비스이다.
Headless Service는 StatefulSet의 각 파드에 대한 개별적인 DNS 레코드를 제공한다. 이는 파드의 고유한 식별자에 따라 DNS 이름이 생성되어 클라이언트가 직접적으로 각 파드에 접근할 수 있게 해준다.
minikube로 앱을 배포할 때 외부와 통신을 가능하게 하기 위해 외부에 열어둘 포트와 트래픽을 라우팅할 컨테이너의 target port를 명시했다. 그를 통해 외부 트래픽을 랜덤한 파드에 배분하여 라우팅시킬 수 있었다. (로드 밸런싱)
로드밸런싱이 필요없고 일반적인 경우에 다른 파드에 db 연결을 제공하는 용도로 사용되어서 외부 IP가 필요없다. 그래서 Statefulsets은 headless service와 함께 배포된다.
apiVersion: v1
kind: Service
metadata:
annotations:
meta.helm.sh/release-name: mysql-helm
meta.helm.sh/release-namespace: helm-test
creationTimestamp: "2023-12-05T15:25:14Z"
labels:
app.kubernetes.io/component: primary
app.kubernetes.io/instance: mysql-helm
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.0.35
helm.sh/chart: mysql-9.14.4
name: mysql-helm-headless
namespace: helm-test
resourceVersion: "627775"
uid: 678308c3-60d4-4019-b512-b7840157c270
spec:
clusterIP: None
clusterIPs:
- None
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: mysql
port: 3306
protocol: TCP
targetPort: mysql
publishNotReadyAddresses: true
selector:
app.kubernetes.io/component: primary
app.kubernetes.io/instance: mysql-helm
app.kubernetes.io/name: mysql
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
apiVersion: v1
kind: Service
metadata:
annotations:
meta.helm.sh/release-name: mysql-helm
meta.helm.sh/release-namespace: helm-test
creationTimestamp: "2023-12-05T15:25:14Z"
labels:
app.kubernetes.io/component: primary
app.kubernetes.io/instance: mysql-helm
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: mysql
app.kubernetes.io/version: 8.0.35
helm.sh/chart: mysql-9.14.4
name: mysql-helm-headless
namespace: helm-test
resourceVersion: "627775"
uid: 678308c3-60d4-4019-b512-b7840157c270
spec:
clusterIP: None
clusterIPs:
- None
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: mysql
port: 3306
protocol: TCP
targetPort: mysql
publishNotReadyAddresses: true
selector:
app.kubernetes.io/component: primary
app.kubernetes.io/instance: mysql-helm
app.kubernetes.io/name: mysql
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
jisujang@Jisuui-MacBookPro ~ % kubectl get deployment todo-app -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"todo-app","namespace":"helm-test"},"spec":{"replicas":3,"selector":{"matchLabels":{"app":"todo-app"}},"template":{"metadata":{"labels":{"app":"todo-app"}},"spec":{"containers":[{"args":["yarn install \u0026\u0026 yarn run dev"],"command":["sh","-c"],"env":[{"name":"MYSQL_HOST","value":"mysql-helm"},{"name":"MYSQL_USER","value":"root"},{"name":"MYSQL_PASSWORD","value":"OnOJGdH7Lv"},{"name":"MYSQL_DB","value":"my_database"}],"image":"jjrk/docker_tutorial","name":"node-app","ports":[{"containerPort":3000}],"workingDir":"/app"}]}}}}
creationTimestamp: "2023-12-05T15:39:04Z"
generation: 1
name: todo-app
namespace: helm-test
resourceVersion: "629200"
uid: 628d39d3-56fc-4c6a-9567-05e9c81aaa50
spec:
progressDeadlineSeconds: 600
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: todo-app
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: todo-app
spec:
containers:
- args:
- yarn install && yarn run dev
command:
- sh
- -c
env:
- name: MYSQL_HOST
value: mysql-helm
- name: MYSQL_USER
value: root
- name: MYSQL_PASSWORD
value: OnOJGdH7Lv
- name: MYSQL_DB
value: my_database
image: jjrk/docker_tutorial
imagePullPolicy: Always
name: node-app
ports:
- containerPort: 3000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
workingDir: /app
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 3
conditions:
- lastTransitionTime: "2023-12-05T15:39:04Z"
lastUpdateTime: "2023-12-05T15:39:26Z"
message: ReplicaSet "todo-app-85fff4bf6" has successfully progressed.
reason: NewReplicaSetAvailable
status: "True"
type: Progressing
- lastTransitionTime: "2023-12-06T01:17:42Z"
lastUpdateTime: "2023-12-06T01:17:42Z"
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
observedGeneration: 1
readyReplicas: 3
replicas: 3
updatedReplicas: 3
서비스는 파드를 외부에 노출시키는 객체이다.(파드는 일반적으로 deployment를 통해서 배포되지만 개별적으로 따로 명세해서 배포할 수 있기도 하다)따라서 명세 정보에서 핵심 사항들은 아래와 같다.
** nodePort는 클러스터 외부에서 직접 접근할 때 사용되는 포트이며, 클라이언트는 로드 밸런서의 IP 주소와 nodePort를 통해 Service로 요청을 보낼 수 있다. 이후 요청은 로드 밸런서를 통해 적절한 파드로 분산되어 처리된다. (외부 로드밸런서를 사용할 경우에 사용)
apiVersion: v1
kind: Service
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"todo-app","namespace":"helm-test"},"spec":{"ports":[{"port":80,"protocol":"TCP","targetPort":3000}],"selector":{"app":"todo-app"},"type":"LoadBalancer"}}
creationTimestamp: "2023-12-05T15:39:04Z"
name: todo-app
namespace: helm-test
resourceVersion: "628522"
uid: bd2f46ed-b1ce-40f6-954e-51723b9b8b8b
spec:
allocateLoadBalancerNodePorts: true
clusterIP: 10.101.64.212
clusterIPs:
- 10.101.64.212
externalTrafficPolicy: Cluster
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- nodePort: 30972
port: 80
protocol: TCP
targetPort: 3000
selector:
app: todo-app
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer: {}