[AEWS] # 3์ฃผ์ฐจ ๋กœ์ปฌ ๋ณผ๋ฅจ ์‹ค์Šต : emptyDir, hostPath, Local Path Provisioner (1)

25๋…„๋„ AWS EKS Hands-on Study ์Šคํ„ฐ๋”” ์ •๋ฆฌ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

๋ณผ๋ฅจ

์ฟ ๋ฒ„๋„คํ‹ฐ์Šค์—์„œ ๋ณผ๋ฅจ์€ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ๋˜๋Š” ๊ณต๊ฐ„์ด๋ฉฐ ํŒŒ๋“œ์˜ ์ƒ๋ช… ์ฃผ๊ธฐ ๋™์•ˆ ์œ ์ง€๋œ๋‹ค.
ํŒŒ๋“œ ๋‚ด ์—ฌ๋Ÿฌ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋™์ผ ๋ณผ๋ฅจ์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ณ , ํŒŒ๋“œ๊ฐ€ ์‚ญ์ œ๋˜๋ฉด ๋ณผ๋ฅจ๋„ ํ•จ๊ป˜ ์‚ญ์ œ๋œ๋‹ค.

emptyDir

# ๋ ˆ๋””์Šค ํ…Œ์ŠคํŠธ ํŒŒ๋“œ ์ƒ์„ฑ
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - name: redis
    image: redis
EOF
pod/redis created

kubectl get po -A
NAMESPACE     NAME                              READY   STATUS              RESTARTS   AGE
default       redis                             0/1     ContainerCreating   0          5s

# ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
kubectl exec -it redis -- sh -c "echo hello > /data/hello.txt"
kubectl exec -it redis -- cat /data/hello.txt
hello

# ํŒŒ๋“œ ํ‚ฌ
kubectl exec -it redis -- kill 1

kubectl get po
NAME    READY   STATUS    RESTARTS      AGE
redis   1/1     Running   1 (10s ago)   10s

# ์‚ฌ๋ผ์ง„ ๋ฐ์ดํ„ฐ ํ™•์ธ
kubectl exec -it redis -- cat /data/hello.txt
cat: /data/hello.txt: No such file or directory

kubectl exec -it redis -- ls -l /data
command terminated with exit code 1
total 0

 
์ž„์˜์˜ ๋ ˆ๋””์Šค ํŒŒ๋“œ๋ฅผ ์ƒ์„ฑ ํ›„ ์˜ฌ๋ ธ๋‹ค kill ํ•˜๊ฒŒ ๋˜๋ฉด ํŒŒ๋“œ๊ฐ€ ์žฌ์‹œ์ž‘๋œ๋‹ค.
์™œ๋ƒํ•˜๋ฉด ํŒŒ๋“œ๊ฐ€ ์ข…๋ฃŒ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด terminationGracePeriodSeconds: 0์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฆ‰์‹œ ์ข…๋ฃŒ๋˜๊ณ , restartPolicy๊ฐ€ ๊ธฐ๋ณธ๊ฐ’์€ Always๋กœ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ๋˜์ž๋งˆ์ž ๋‹ค์‹œ ํŒŒ๋“œ๊ฐ€ ์žฌ์‹œ์ž‘๋˜๋ฉด์„œ ๊ธฐ์กด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์‚ญ์ œ๋˜๊ณ  ์ƒˆ๋กœ์šด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด์„œ ํŒŒ์ผ์‹œ์Šคํ…œ์ด ์ดˆ๊ธฐํ™”๋œ๋‹ค. ๊ทธ๋ž˜์„œ ์ด์ „ ์ปจํ…Œ์ด๋„ˆ ์•ˆ์—์„œ ๋งŒ๋“ค์—ˆ๋˜ hello.txt ํŒŒ์ผ์ด ๋‚ ์•„๊ฐ„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
 

hostPath

hostPath๋Š” ํ˜ธ์ŠคํŠธ ๋…ธ๋“œ์˜ ํŠน์ • ๋กœ์ปฌ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งˆ์šดํŠธํ•˜์—ฌ ํŒŒ๋“œ ๋‚ด๋ถ€์—์„œ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์ด๋‹ค.
๋”ฐ๋ผ์„œ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด๋„ ํŒŒ๋“œ๊ฐ€ ์‚ญ์ œ๋˜์ง€ ์•Š๋Š” ์ด์ƒ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋˜๋ฉฐ ๋กœ์ปฌ ๋””์Šคํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ™์€ ๋…ธ๋“œ์— ํŒŒ๋“œ๊ฐ€ ๋„์–ด์ง„ ๊ฒฝ์šฐ, ๊ทธ ํŒŒ๋“œ๊ฐ„์˜ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
ํ•˜์ง€๋งŒ ํŒŒ๋“œ๊ฐ€ ํŠน์ • ๋…ธ๋“œ์— ๋„์šฐ๋„๋ก ์Šค์ผ€์ค„๋ง ํ•ด์•ผํ•œ๋‹ค.
 
1๋ฒˆ ์›Œ์ปค ๋…ธ๋“œ ๋กœ์ปฌ ๋””์Šคํฌ ๊ฒฝ๋กœ๋กœ ํ…Œ์ŠคํŠธํ•ด๋ณธ๋‹ค.

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-test
spec:
  nodeSelector:
    kubernetes.io/hostname: 1๋ฒˆ ์›Œ์ปค๋…ธ๋“œ
  containers:
  - name: busybox
    image: busybox
    command: ["sleep", "3600"]
    volumeMounts:
    - mountPath: "/mnt/data"
      name: my-volume
  volumes:
  - name: my-volume
    hostPath:
      path: "/data"
      type: DirectoryOrCreate

 
ํŒŒ๋“œ๋ฅผ ์›Œ์ปค ๋…ธ๋“œ 1๋ฒˆ์— ๊ณ ์ •ํ•˜๊ธฐ ์œ„ํ•ด ๋…ธ๋“œ์…€๋ ‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
1๋ฒˆ ๋…ธ๋“œ์˜ /data ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ /mnt/data์— ๋งˆ์šดํŠธํ•˜๋„๋ก ์ •์˜ํ•œ๋‹ค.
 

k get po
NAME            READY   STATUS    RESTARTS   AGE
hostpath-test   1/1     Running   0          8s

kubectl exec -it hostpath-test -- sh

/ # echo "hostPath test" > /mnt/data/test.txt
/ # cat /mnt/data/test.txt
hostPath test

 
ํŒŒ๋“œ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๊ณ  1๋ฒˆ ์›Œ์ปค ๋…ธ๋“œ์—์„œ ํ™•์ธํ•˜๋ฉด
 

[ec2-user@1๋ฒˆ ์›Œ์ปค๋…ธ๋“œ ~]$ ls -l /data/
total 4
-rw-r--r--. 1 root root 14 Feb 17 17:07 test.txt

# ํŒŒ๋“œ ์‚ญ์ œ
kubectl delete pod hostpath-test
pod "hostpath-test" deleted

[ec2-user@1๋ฒˆ ์›Œ์ปค๋…ธ๋“œ ~]$ ls -l /data/
total 4
-rw-r--r--. 1 root root 14 Feb 17 17:07 test.txt

 
์ปจํ…Œ์ด๋„ˆ์—์„œ ์“ด ๋ฐ์ดํ„ฐ๊ฐ€ ์›Œ์ปค ๋…ธ๋“œ์—์„œ๋„ ํ™•์ธ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์“ธ ๊ฒฝ์šฐ ํŒŒ๋“œ๋ฅผ ์ง€์›Œ๋„ ํ˜ธ์ŠคํŠธ์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋œ๋‹ค.
hostPath์˜ ๊ฒฝ์šฐ์—๋Š” ์ด๋ ‡๊ฒŒ pv๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด์•ผ ๋˜๋Š” ๊ตฌ์กฐ์ด๋ฏ€๋กœ ๋™์ ์œผ๋กœ ํ• ๋‹น์ด ์•ˆ๋œ๋‹ค.

Local path provisioner

๊ทธ๋ ‡๋‹ค๋ฉด ํ˜ธ์ŠคํŠธ ๋…ธ๋“œ์˜ ๋กœ์ปฌ ๋””์Šคํฌ๋ฅผ ๋™์ ์œผ๋กœ ํ• ๋‹นํ•˜์—ฌ ๋ณผ๋ฅจ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด local path provisioner์ด๋‹ค.

kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml

kubectl get pod -n local-path-storage -o wide
NAME                                     READY   STATUS    RESTARTS   AGE     IP              NODE                                              NOMINATED NODE   READINESS GATES
local-path-provisioner-84967477f-7bg7g   1/1     Running   0          2m37s   192.168.1.201   ip-[์›Œ์ปค๋…ธ๋“œ].ap-northeast-2.compute.internal       <none>           <none>

kubectl describe cm -n local-path-storage local-path-config
Name:         local-path-config
Namespace:    local-path-storage
Labels:       <none>
Annotations:  <none>

Data
====
config.json:
----
{
        "nodePathMap":[
        {
                "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
                "paths":["/opt/local-path-provisioner"]
        }
        ]
}

helperPod.yaml:
----
apiVersion: v1
kind: Pod
metadata:
  name: helper-pod
spec:
  priorityClassName: system-node-critical
  tolerations:
    - key: node.kubernetes.io/disk-pressure
      operator: Exists
      effect: NoSchedule
  containers:
  - name: helper-pod
    image: busybox
    imagePullPolicy: IfNotPresent

setup:
----
#!/bin/sh
set -eu
mkdir -m 0777 -p "$VOL_DIR"

teardown:
----
#!/bin/sh
set -eu
rm -rf "$VOL_DIR"


BinaryData
====

Events:  <none>

kubectl get sc
NAME         PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2          kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  29h
local-path   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  3m47s

kubectl get sc local-path
NAME         PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-path   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  3m55s

 
๊ทธ๋Ÿฌ๋ฉด ์ด์ œ ์—ฌ๊ธฐ์— pvc, ํŒŒ๋“œ๋ฅผ ์ƒ์„ฑํ•ด๋ณด๋ฉด...
 

# pvc
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: localpath-claim
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path
  resources:
    requests:
      storage: 1Gi
EOF

kubectl get pv
Normal  WaitForFirstConsumer  13s (x3 over 42s)  persistentvolume-controller  waiting for first consumer to be created before binding

 
pvc๊ฐ€ ์ƒ์„ฑ๋˜์ง€๋งŒ ์•„์ง pv์— ๋ฐ”์ธ๋”ฉ๋˜์ง€ ์•Š์€ ์ƒํƒœ์ธ๋ฐ ์ด๋Š” ํŒŒ๋“œ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•˜๋Š” ์ƒํƒœ์ด๋‹ค.
ํŒŒ๋“œ๊ฐ€ ์‹คํ–‰๋  ๋…ธ๋“œ์—์„œ๋งŒ pv๋ฅผ ํ• ๋‹นํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์ด๋ผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
 

# ํŒŒ๋“œ
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: localpath-claim
EOF

 
ํŒŒ๋“œ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ  ๋‚˜๋ฉด
 

kubectl get pod,pv,pvc
NAME      READY   STATUS    RESTARTS   AGE
pod/app   1/1     Running   0          5m44s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/pvc-55aa6c67-5fc5-4994-a2ea-c005a89f0482   1Gi        RWO            Delete           Bound    default/localpath-claim   local-path     <unset>                          5m41s

NAME                                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/localpath-claim   Bound    pvc-55aa6c67-5fc5-4994-a2ea-c005a89f0482   1Gi        RWO            local-path     <unset>
kubectl describe pv
...
Node Affinity:
  Required Terms:
    Term 0:        kubernetes.io/hostname in [ip-1๋ฒˆ ์›Œ์ปค๋…ธ๋“œ.ap-northeast-2.compute.internal]
Message:
Source:
    Type:          HostPath (bare host directory volume)
    Path:          /opt/local-path-provisioner/pvc-55aa6c67-5fc5-4994-a2ea-c005a89f0482_default_localpath-claim
    HostPathType:  DirectoryOrCreate
    ...

 
pv์— pvc๊ฐ€ ํ• ๋‹น(Bound)๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 
pvc๊ฐ€ ๋ฐ”์ธ๋”ฉ๋  ๋•Œ local path provisioner๊ฐ€ ์ž๋™์œผ๋กœ pv๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ ์ด pv๋Š” ์›Œ์ปค ๋…ธ๋“œ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ  ์‹ค์ œ ์›Œ์ปค ๋…ธ๋“œ์— ๋งˆ์šดํŠธ๋œ ๊ฒฝ๋กœ์ด๋‹ค.
 

kubectl exec -it app -- tail -f /data/out.txt
Mon Feb 17 17:33:43 UTC 2025
Mon Feb 17 17:33:48 UTC 2025
Mon Feb 17 17:33:53 UTC 2025
Mon Feb 17 17:33:58 UTC 2025
Mon Feb 17 17:34:03 UTC 2025
Mon Feb 17 17:34:08 UTC 2025
Mon Feb 17 17:34:13 UTC 2025
Mon Feb 17 17:34:18 UTC 2025
Mon Feb 17 17:34:23 UTC 2025
Mon Feb 17 17:34:28 UTC 2025
Mon Feb 17 17:34:33 UTC 2025
Mon Feb 17 17:34:38 UTC 2025

[ec2-user@์›Œ์ปค๋…ธ๋“œ ~]$ sudo ls /opt/local-path-provisioner/
pvc-55aa6c67-5fc5-4994-a2ea-c005a89f0482_default_localpath-claim

 
์ปจํ…Œ์ด๋„ˆ์—์„œ ๋กœ๊ทธ ์ฐ๊ณ  ์›Œ์ปค๋…ธ๋“œ์—์„œ ๋“ค์–ด๊ฐ€๋ณด๋ฉด ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
 

kubectl delete pod app
pod "app" deleted

kubectl get pod,pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/pvc-55aa6c67-5fc5-4994-a2ea-c005a89f0482   1Gi        RWO            Delete           Bound    default/localpath-claim   local-path     <unset>                          10m

NAME                                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/localpath-claim   Bound    pvc-55aa6c67-5fc5-4994-a2ea-c005a89f0482   1Gi        RWO            local-path     <unset>                 10m

# ํŒŒ๋“œ ์žฌ์ƒ์„ฑ ํ›„~
kubectl exec -it app -- head /data/out.txt
kubectl exec -it app -- tail -f /data/out.txt
Mon Feb 17 17:27:07 UTC 2025
Mon Feb 17 17:27:12 UTC 2025
Mon Feb 17 17:27:17 UTC 2025
Mon Feb 17 17:27:22 UTC 2025
Mon Feb 17 17:27:27 UTC 2025
Mon Feb 17 17:27:32 UTC 2025
Mon Feb 17 17:27:37 UTC 2025
Mon Feb 17 17:27:42 UTC 2025
Mon Feb 17 17:27:47 UTC 2025
Mon Feb 17 17:27:52 UTC 2025
Mon Feb 17 17:36:18 UTC 2025
Mon Feb 17 17:36:23 UTC 2025
Mon Feb 17 17:36:28 UTC 2025
Mon Feb 17 17:36:33 UTC 2025
Mon Feb 17 17:36:38 UTC 2025
Mon Feb 17 17:36:43 UTC 2025
Mon Feb 17 17:36:48 UTC 2025
Mon Feb 17 17:36:53 UTC 2025
Mon Feb 17 17:38:52 UTC 2025
Mon Feb 17 17:38:57 UTC 2025

 
ํŒŒ๋“œ๋ฅผ ์‚ญ์ œํ•˜๋”๋ผ๋„ pvc๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋˜๋ฏ€๋กœ pv๋„ ๊ทธ๋Œ€๋กœ ์žˆ๋Š” ์ƒํƒœ์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
pvc๊ฐ€ ์‚ญ์ œ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ ํŒŒ๋“œ๋ฅผ ๋‹ค์‹œ ๋„์›Œ์„œ ๋ณด๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๊ทธ๋Œ€๋กœ ์žˆ๊ณ  ๊ณ„์† ์ถ”๊ฐ€ํ•ด๋‚˜๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
 
๋”ฐ๋ผ์„œ local path provisioner๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ ๊ณผ์ •์„ ์ •๋ฆฌํ•˜๋ฉด, pvc๊ฐ€ ๋ฐ”์ธ๋”ฉ๋  ๋•Œ local path provisioner๊ฐ€ ์ž๋™์œผ๋กœ pv๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  pv๋Š” ํŠน์ • ์›Œ์ปค ๋…ธ๋“œ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ ์‹ค์ œ ๋…ธ๋“œ์˜ ๋งˆ์šดํŠธ๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.