0523_Kubernetes_Ingress, Ingress Controller / Readiness Probe / Volume : EmptyDir, Init Container, HostPath / PV & PVC : Access Mode, NFS Static Provisioning

HYOJU DO·2022년 5월 23일
0

Kubernetes

목록 보기
10/13
post-thumbnail

Ingress


L7 LB == ALB

Ingress Controller

Ingress 리소스가 작동하려면, 클러스터는 실행 중인 Ingress Controller가 반드시 필요
🌟 컨트롤러 ➜ 구현 / 제어 (가장 중요한 역할)
실습에서 사용하는 Controller : Nginx Ingress Controller


L4 LB던 L7 LB던 외부에서 연결 되는 형태
LoadBalancer(L4) type 굳이 만들 필요 x ➜ 일반적으로 NP 형태로 생성

🌟 URL(Domain + Path) 을 보고 우리가 원하는 서비스로 Routing 해 줌



🎈 Ingress Controller 종류

  • Ingress Controller : pod로 생성
  • IngressClassName Controller : 여러 종류의 Ingress를 사용하는 경우 각 Ingress를 Class로 만들어놓을 수 있음



🎈 Ingress spec

ing.spec

  • rules : 라우팅 정책 (http 구현)
    리스트 형태 ➜ 순서 중요! (작은 scope부터 작성)

    • host : hostname 포함한 도메인 ➜ 실제로 존재하는 도메인 만 사용 가능
      (ex. www, * (와일드카드) 등

    • http

      • paths (필수)
        • path : 경로 (ex. /, /info)
        • pathType : 일반적으로 Exact (path 정확히 일치), Prefix (지정 path 뒤에 추가 가능)
        • backend
          • resource : apiGroup / kind (필수) / name (필수)
          • service ➜ 확장 위함
            • name (필수)
            • port (필수)
    • tls : https 구현


➕ Ingress는 기본적으로 host가 없음
만약 경로가 /xxx로 시작하는 경로로 들어오면 xxx.svc(서비스)로 연결 시켜줌



🎈 Ingress 실습

myweb-rs-ing.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs-ing
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
      env: dev
  template:
    metadata:
      labels:
        app: web
        env: dev
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb
          ports:
            - containerPort: 8080
              protocol: TCP

myweb-svc-ing.yaml

apiVersion: v1
kind: Service
metadata:
  name: myweb-svc-ing
spec:
  type: NodePort ✔️
  selector:
   app: web
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 31313

myweb-ing.yaml

  • 정상 작동 ( host지정 x )
apiVersion: networking.k8s.io/v1
kind: Ingress ✔️
metadata:
  name: myweb-ing
spec:
  rules: ✔️
    - http:
        paths:
          - pathType: Prefix
            path : /
            backend:
              service:
                name: myweb-svc-ing
                port:
                  number: 80
  • 오류 발생 파일 ( 존재하지 않는 host 지정 )
apiVersion: networking.k8s.io/v1
kind: Ingress ✔️
metadata:
  name: myweb-ing
spec:
  rules: 
    - host: "*.encore.xyz" ## 없는 도메인 주소 ✔️
      http:
        paths:
          - pathType: Prefix
            path : /
            backend:
              service:
                name: myweb-svc-ing
                port:
                  number: 80

작업 디렉토리 : ~/yaml/ing

생성

$ kubectl reate -f myweb-rs-ing.yaml -f myweb-svc-ing.yaml
$ kubectl reate -f myweb-ing.yaml (host O or host x 선택)

생성한 오브젝트 확인
➜ ingress 상세 정보는 all로 확인 불가

$ kubectl get all
NAME                     READY   STATUS              RESTARTS   AGE
pod/myweb-rs-ing-jmtnb   0/1     ContainerCreating   0          10s
pod/myweb-rs-ing-ljkg6   1/1     Running             0          10s
pod/myweb-rs-ing-pg9j5   0/1     ContainerCreating   0          10s

NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/kubernetes      ClusterIP   10.233.0.1     <none>        443/TCP        11h
service/myweb-svc-ing   NodePort    10.233.39.14   <none>        80:31313/TCP   10s

NAME                           DESIRED   CURRENT   READY   AGE
replicaset.apps/myweb-rs-ing   3         3         2       10s

Ingress 정보 확인
$  kubectl get ing
NAME        CLASS    HOSTS   ADDRESS                                           PORTS   AGE
myweb-ing   <none>   *       192.168.100.100,192.168.100.101,192.168.100.102   80      24s

Ingress Controller (Nginx Controller) 파드 상세 확인

$ kubectl get po -n ingress-nginx -o wide
NAME                             READY   STATUS    RESTARTS       AGE   IP             NODE    NOMINATED NODE   READINESS GATES
ingress-nginx-controller-4h7cg   1/1     Running   1 (137m ago)   11h   10.233.92.11   node3   <none>           <none>
ingress-nginx-controller-6pdbx   1/1     Running   1 (140m ago)   11h   10.233.90.13   node1   <none>           <none>
ingress-nginx-controller-f6km9   1/1     Running   1 (139m ago)   11h   10.233.96.20   node2   <none>           <none>

💡 현재 모든 노드에 Ingress Controller(Nginx Controller)가 떠 있으므로 모든 노드를 ADDRESS로 가짐
➜ 어떠한 노드로 접속해도 상관 x


🎈 외부 접속 확인 방법

url이 들어오면 DNS 쿼리를 하고 결과 IP를 SRC에 매칭 후 DST(192.168.100.100 등)으로 전달
IP만 전달하는 것과 매커니즘 자체가 다름

1) 웹 브라우저로 확인

2) curl 명령어로 확인

$  curl 192.168.100.100
Hello World!
myweb-rs-ing-ljkg6

$ curl 192.168.100.101
Hello World!
myweb-rs-ing-ljkg6

$ curl 192.168.100.102
Hello World!
myweb-rs-ing-jmtnb

도메인에 대한 상세 정보 확인

$ curl http://192.168.100.100 -v

-v 옵션 : 상세 정보 출력

: 요청
< : 응답

  • host 설정 x 일 때
    연결 성공 ➜ 200 출력
*   Trying 192.168.100.100:80...
* TCP_NODELAY set
* Connected to 192.168.100.100 (192.168.100.100) port 80 (#0)
> GET / HTTP/1.1         # HTTP 프로토콜  
> Host: 192.168.100.100  🌟 경로가 이렇게 되어야함 📌
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK                              ✔️ # 200 : 연결 성공
< Date: Mon, 23 May 2022 00:58:47 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 32
< Connection: keep-alive
<
Hello World!                                   ✔️ # 정상 출력
myweb-rs-ing-pg9j5
* Connection #0 to host 192.168.100.100 left intact
  • host를 없는 도메인( *.encore.xyz)으로 설정했을 때
    연결 실패 ➜ 404 출력
    host를 지정해주면 *.encore.xyz 로 접속했을 때만 접속 확인이 가능하기 때문
*   Trying 192.168.100.100:80...
* TCP_NODELAY set
* Connected to 192.168.100.100 (192.168.100.100) port 80 (#0)
> GET / HTTP/1.1
> Host: 192.168.100.100          
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse 
< HTTP/1.1 404 Not Found                      ✔️ # 404 : 오류 발생
< Date: Mon, 23 May 2022 01:01:52 GMT
< Content-Type: text/html
< Content-Length: 146
< Connection: keep-alive
<
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>       ✔️ # 비정상 출력
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 192.168.100.100 left intact

url이 들어오면 DNS 쿼리를 하고 결과 IP를 SRC에 매칭 후 DST(192.168.100.100 등)으로 전달
IP만 전달하는 것과 매커니즘 자체가 다름



도메인 없이 테스트 해보는 방법

🎈 curl --resolve 옵션 사용

비교적 안전하고 제일 간편

$ curl --resolve www.encore.xyz:80:192.168.100.100 http://www.encore.xyz
Hello World!
myweb-rs-ing-6rp9q

--resolve 옵션 : www.encore.xyz:80 으로 접속 시 192.168.100.100 로 바꾸어라는 뜻

상세정보 확인

$ curl --resolve www.encore.xyz:80:192.168.100.100 http://www.encore.xyz -v
* Added www.encore.xyz:80:192.168.100.100 to DNS cache
* Hostname www.encore.xyz was found in DNS cache
*   Trying 192.168.100.100:80...
* TCP_NODELAY set
* Connected to www.encore.xyz (192.168.100.100) port 80 (#0)
# ➜ 해당 도메인을 (192.168.100.100) : IP DST / port 80 : TCP DST PORT 로 연결하라
> GET / HTTP/1.1
> Host: www.encore.xyz ✔️   
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK ✔️
< Date: Mon, 23 May 2022 01:21:11 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 32
< Connection: keep-alive
<
Hello World!
myweb-rs-ing-fjcgf
* Connection #0 to host www.encore.xyz left intact

🎈 /etc/hosts 파일 설정

/etc/nsswitch.conf

DNS 설정 정보 파일
➕ ns : NameServer

$ grep host /etc/nsswitch.conf
hosts:          files dns

hosts 파일 수정
➜ hosts 파일은 DNS보다 우선 순위 높은 파일

/etc/hosts

127.0.0.1 localhost localhost.localdomain

...

192.168.100.100 www.encore.xyz   # 이 부분 추가

확인

$ curl http://www.encore.xyz

🎈 Wildcard DNS

우리가 뭘 요청해도 응답을 돌려주는 DNS 서버
http / https 둘 다 사용 가능

실제 서버에서는 사용 x (테스트용)
✔️ host 파일 없애고 진행

$ host 10.10.10.01.nip.io
$ host www.10.10.10.01.nip.io
$ host https://10-10-10-01.nip.io

뭘 검색하든 마지막에 nip.io 붙여 줌
➜ 주소를 인식해서 주소 중간에 있는 ip 값을 return


myweb-ing.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress ✔️
metadata:
  name: myweb-ing
spec:
  rules:
    - host: '*.nip.io' ✔️
      http:
...

변경내용 적용

$ kubectl replace -f myweb-ing.yaml

확인
➜ 인터넷 연결 필요 x / 도메인 필요 x

$ curl http://192-168-100-100.nip.io/  # 100, 101. 102 다 가능
Hello World!
myweb-rs-ing-fjcgf



Inress 예제 실습

푸시한 이미지로 Ingress 구현

Docker VM 생성하여 이미지 푸시

웹 이미지 생성 및 푸시

hello:one 이미지
Dockerfile

FROM httpd
COPY index.html /usr/local/apache2/htdocs/index.html

index.html

<h1> Hello One </h1>

hello:two 이미지
Dockerfile

FROM httpd
COPY index.html /usr/local/apache2/htdocs/index.html

index.html

<h1> Hello Two </h1>

이미지 빌드

$ docker image build X/hello:one
$ docker image build X/hello:two

DockerHUB에 접속하여 이미지 푸시

$ docker login
$ docker push X/hello:one
$ docker push X/hello:two

관련 리소스 생성 (RS, SVC, ING)

작업 디렉토리 : ~/yaml/ing/practice

RS
Docker VM에서 푸시했던 이미지 사용

one-rs.yaml

apiVersion: apps/v1
kind: ReplicaSet ✔️
metadata:
  name: one-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-one
  template:
    metadata:
      labels:
        app: hello-one
    spec:
      containers:
        - name: hello-one
          image: c1t1d0s7/hello:one ✔️ # 푸시했던 이미지
          ports:
            - containerPort: 80
              protocol: TCP

two-rs.yaml

apiVersion: apps/v1
kind: ReplicaSet ✔️
metadata:
  name: two-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-two
  template:
    metadata:
      labels:
        app: hello-two
    spec:
      containers:
        - name: hello-two
          image: c1t1d0s7/hello:two ✔️
          ports:
            - containerPort: 80
              protocol: TCP

SVC

one-svc-np.yaml

apiVersion: v1
kind: Service ✔️
metadata:
  name: one-svc-np
spec:
  type: NodePort ✔️
  selector:
    app: hello-one 
  ports:
    - port: 80
      targetPort: 80

two-svc-np.yaml

apiVersion: v1
kind: Service ✔️
metadata:
  name: two-svc-np
spec:
  type: NodePort ✔️
  selector:
    app: hello-two
  ports:
    - port: 80
      targetPort: 80

ING

hello-ing.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress ✔️
metadata:
  name: hello-ing
  annotations: 🌟
    nginx.ingress.kubernetes.io/rewrite-target: /       # URL 재작성 : /one -> /, /two -> /
spec:
  rules:
    - host: '*.nip.io' ✔️
      http:
        paths:
          - path: /one
            pathType: Prefix
            backend:
              service:
                name: one-svc-np ✔️
                port:
                  number: 80
          - path: /two
            pathType: Prefix
            backend:
              service:
                name: two-svc-np ✔️
                port:
                  number: 80

🌟 annotation 선언하는 이유

pod IP/one 과 같은 형식으로 path가 붙어서 생성되어 /index.html 실행 x

annotation 은 식별 데이터 x ➜ 단순히 정보를 저장하는 데이터
해당 메타데이터를 참조할 수 있음 ➜ 애플리케이션 동작에 대한 설정 변경 가능

🌟 /rewrite-target : 앞 타겟의 경로를 재작성하라는 뜻 (URL 재작성)
10.233.10.10/one ➜ 10.233.10.10/으로 재작성


작업 디렉토리의 모든 리소스 생성

kubectl create -f .

확인

$ curl 192-168-100-100.nip.io/one
$ curl 192-168-100-100.nip.io/two



Readiness Probe


파드의 헬스체크를 통해 서비스의 엔드포인트 리소스에 타겟 등록

rs.spec.template.spec.containers.readinessProbe

startup이 만족되지 않으면 사용할 수 없음
다른 프로브와 내부 구성은 동일

mywec-svc-np.yaml

apiVersion: v1
kind: Service
metadata:
  name: myweb-svc-lb
spec:
  type: LoadBalancer ✔️
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

실패할 RS yaml 파일

myweb-rs.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
      env: dev
  template:
    metadata:
      labels:
        app: web
        env: dev
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb:alpine ✔️
          ports:
            - containerPort: 8080
              protocol: TCP
          readinessProbe: ✔️
            exec:
              command:
                - ls
                - /tmp/ready ✔️

/tmp/ready 디렉토리가 없기 때문에 probe 실패할 것임

➕ 추후 파드에 shell로 접속하여 확인하기 위해 alpine 이미지 사용

rs, svc 생성

$ kubectl create -f .

리소스 생성 확인

$ kubectl get rs,po,svc,ep
NAME                       DESIRED   CURRENT   READY   AGE
replicaset.apps/myweb-rs   3         3         0       55s

NAME                 READY   STATUS    RESTARTS   AGE
pod/myweb-rs-bn2qf   0/1     Running   0          55s
pod/myweb-rs-jfllq   0/1     Running   0          55s
pod/myweb-rs-zghhc   0/1     Running   0          55s

NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
service/kubernetes     ClusterIP      10.233.0.1     <none>            443/TCP        14h
service/myweb-svc-lb   LoadBalancer   10.233.41.87   192.168.100.240   80:31278/TCP   70s

NAME                     ENDPOINTS              AGE
endpoints/kubernetes     192.168.100.100:6443   14h
endpoints/myweb-svc-lb                          69s   ✔️ # endpoint가 없음!

해당 경로가 존재하지 않기 때문에 endpoint가 생성되지 x

접속 확인

$ curl 192.168.100.100
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center> ✔️ # 실패 
<hr><center>nginx</center>
</body>
</html>

endpoint 없으므로 Not Found

🌟 정상적인 파드의 상태를 probe해서 엔드포인트 리소스에 타겟 등록하여 라우팅
비정상적인 상태라면 엔드포인트에 등록하지 않아 사용하지 않도록 만듦
➜ 준비가 된 서비스만 엔드포인트에 등록

생성 된 파드에 접속하여 /tmp/ready 파일 생성

$ kubectl exec <POD> -- touch /tmp/ready

🌟 livenessprobe, startupprobe, readinessprobe
(현재 실습에서는 사용 안할 예정이지만 실제로는 선언 필수 )


➕ readiness probe를 설정하지 않으면 무조건 success


💡 svc 정리

모든 노드에 걸쳐 브릿지 존재
어떤 노드에 파드를 배치하더라도 똑같이 동작해야함

내부
Cluster Ip ➜ cluster 내부 IP (30000~327xx)
외부
np ➜ cluster ip 기능 포함 (✔️ 서비스는 일반적으로 NodePort로 구성 됨)
lb ➜ np 기능 포함 (L4)
Ingress ➜ L7 LB
서비스와 ingress 구분하기

external name 서비스는 내부 서비스가 아니라 외부에 조금 더 쉽게 접근하려고 하는 것

Headless Service 방식은 StatefulSet 볼 때 학습할 예정

$ kubectl port-forward는 test용이지 실제로 사용 x
(서비스 오브젝트 별도 사용하여 포트 포워딩)



Volume


파드는 계속해서 생성 및 삭제를 반복하기 때문에
컨테이너 내의 디스크에 있는 파일은 임시적으로 존재
➜ 컨테이너에 다양한 유형의 볼륨을 생성하여 데이터 따로 저장 (pv는 파드의 수명을 넘어서도 존재함)

🎈 volume spec 확인

pod.spec.volumes.* : 볼륨 유형 확인 가능


emptyDir

임시로 사용할 빈 볼륨(스크래치)
하나의 파드에 존재하는 여러 밀접한 관계의 컨테이너 사이에 공유하는 볼륨
파드 삭제 시 볼륨 같이 삭제 (임시로만 사용)
여러 개의 컨테이너가 동일 볼륨을 임시로 사용할 때만 이용

pod.spec.volumes.emptyDir.medium

  • default ("") : 로컬의 디스크
  • memory : 램디스크
    • 고속의 임시 스토리지 제공
    • 램 용량이 넉넉해야 사용 가능

🎈 볼륨 마운트 방법

spec.volumes 를 설정하여
파드의 spec.containers.volumeMounts 필드에 볼륨 마운트
생성한 volume 이름 매칭 해야함
mountPath : 마운트 포인트 (지정한 디렉토리가 없으면 자동으로 생성해 줌)
여러 개의 파드에 하나의 볼륨 마운트 가능

myweb-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: myweb-pod
spec:
  containers:
    - name: myweb1
      image: httpd
      volumeMounts: ✔️
        - name: emptyvol
          mountPath: /empty
    - name: myweb2
      image: ghcr.io/c1t1d0s7/go-myweb:alpine
      volumeMounts: ✔️
        - name: emptyvol
          mountPath: /empty
  volumes: ✔️
    - name: emptyvol
      emptyDir: {}

파드에 접속하여 해당 볼륨이 마운트 되었는지 확인

$ kubectl exec -it myweb-pod -c myweb1 -- bash

> cd /empty
> touch a b c
kubectl exec -it myweb-pod -c myweb2 -- sh

> ls /empty



gitRepo (✔️ 사용 중단)

Git Repository에 있는 데이터 복사해서 emptyDir에 채워서 줌
➜ Init Container로 대체


🌟 init container(초기화 컨테이너)

파드가 생성 시 딱 한 번만 실행 된 후 종료
➜ gitRepo를 대신해서 사용

pod.spec.initContainers

초기화 컨테이너에서 사용하는 어플리케이션은 종료가 되는 어플리케이션 이어함
초기화 컨테이너가 끝나야 일반 containers가 실행
(EC2의 user-data와 비슷한 개념)

  • 초기화 컨테이너 (init containers) : 볼륨 연결해서 실행 시 한 번만 작업할 내용을 볼륨에 저장 후 종료
  • 애플리케이션 컨테이너 (containers) : 동일한 볼륨에 연결되어 작업

init-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: init-pod
spec:
  initContainers: ✔️
    - name: gitpull
      image: alpine/git
      args: ✔️
        - clone
        - -b
        - v2.18.1
        - https://github.com/kubernetes-sigs/kubespray.git
        - /repo
      volumeMounts:
        - name: gitrepo ✔️
          mountPath: /repo
  containers: ✔️
    - name: gituse
      image: busybox
      args:
        - tail
        - -f
        - /dev/null
      volumeMounts:
        - name: gitrepo ✔️
          mountPath: /kube
  volumes:
    - name: gitrepo
      emptyDir: {}

gitpull 컨테이너는 Git의 코드를 다운받아서 gitrepo EmptyVolume에 저장하고 gituse 컨테이너는 이를 이어 받아 사용

tail -f /dev/null : 실제 명령이 오래 지속되지 않는 경우 컨테이너를 무기한 유지하기 위한 일반적인 관용구



Host Path

호스트 노드의 파일시스템에 있는 파일이나 디렉터리를 파드에 마운트
경로를 채워서 컨테이너에게 제공
Docker 바인드 방식과 거의 흡사

pod.spec.volumes.hostPath

소켓 파일 확인
소켓 ➜ IP:Port

$  ps -ef | grep containerd

소켓으로 접근하면 네트워크 연결 가능 ?📌

/mnt/web_contents/index.html

<h1> Hello hostPath </h1>

🎈 HostPath 실습

httpd 이미지 RS 생성

myweb-rs-hp.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs-hp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: myweb
          image: httpd
          volumeMounts:
            - name: web-contents
              mountPath: /usr/local/apache2/htdocs/ ✔️
      volumes:
        - name: web-contents
          hostPath:
            type: Directory
            path: /web_contents ✔️

이벤트 로그 확인

Events:
...
MESSAGE
MountVolume.SetUp failed for volume "web-contents" : ✔️ hostPath type check failed: /web_contents is not a directory

파드 생성 순서 : 노드 생성 ➜ 볼륨 체크 ➜ 이미지 풀링 ➜ 컨테이너 생성
볼륨이 있는지 없는지 확인하는 과정에서 오류 난 것

hostPath는 네트워크 스토리지 x ➜ 네트워크를 넘어서 볼륨을 공유하지 x

💡 참고
로컬 스토리지 : 다른 호스트에 스토리지 볼륨을 제공 및 공유 할 수 X

  • emptyDir
  • hostPath
  • gitRepo
  • local

➕ tee 명령어 ➜ 표준 출력을 표준 입력으로
ex. echo "hello world" | sudo tee /a/a.txt



🌟 PV & PVC 🌟


  • PersistentVolume (pv) : 스토리지 볼륨 정의 -> 실제 스토리지 정의 부분
  • PersistentVolumeClaim (pvc) : PV 요청

🎈 pv, pvc 리소스 정보 확인

$ kubectl api-resources | grep pv

persistentvolumeclaims            pvc          v1                                     true         PersistentVolumeClaim
persistentvolumes                 pv           v1                                     false        PersistentVolume

이 때까지는 파드에 볼륨을 직접 붙이는 형태를 사용 ➜ volume mounts

스토리지를 정의 하는 오브젝트가 PV
개발자는 PVC 라는 리소스를 사용하여 PV를 이름으로만 요청

pv는 containers의 volumes처럼 볼륨의 형식 ( pv.spec )이 들어감


🎈 PVC가 PV를 지칭하는 방법

  • selector
  • storageClassName
  • volumeName

PV, PVC 예제

파드는 PVC를 지칭 ➜ PVC는 PV를 지칭 ➜ PV는 실제 스토리지 지칭

PVC의 pvc.metadata.name과 파드의 pod.spec.volumes.persistentVolumeClaim 이 일치해야함
PVC는 PV의 이름을 가리키고 있음

작업 디렉토리 : ~/yaml/pv

Pod
mypod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: mypod
      image: httpd
      volumeMounts:
        - name: myvol ✔️
          mountPath: /tmp
  volumes:
	- name: myvol ✔️
	  persistentVolumeClaim:
	    name: mypvc ✔️

PVC
mypvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim ✔️
metadata:
  name: mypvc ✔️
spec:
  volumeName: mypv ✔️
  ...

PV
mypv.yaml

apiVersion: v1
kind: PersistentVolume ✔️
metadata:
  name: mypv ✔️
spec:
  hostPath:
    path: /web_contents
    type: DirectoryOrCreate # 디렉토리가 있으면 사용 없으면 생성



PV, PVC 생명주기

pod - PVC - PV - 스토리지
PV <- 1:1 -> PVC
(무조건 1:1 연결)

  1. 프로비저닝
  2. 바인딩
  3. 사용
  4. 회수/반환(Reclaim)

🎈 프로비저닝
스토리지 생성
실제로 PV와 연결할 NFS, hostPath ... 등 스토리지 미리 준비

🎈 바인딩
PV와 PVC를 만들어서 연결
바인딩 하면 웬만하면 데이터 계속 사용 가능

🎈 회수/반환
복제본에 의해 파드가 삭제되더라도 PVC에 의해 새로운 파드를 연결하면 됨
더이상 PV와 PVC가 필요하지 않다면? PVC를 삭제하면 연결 된 PV를 어떻게 회수할 것인가?

회수(반환) 정책이 존재

  • 보존 정책 (retain) : PV 삭제 x (Release <- PVC와 연결되어있지 않은 상태)

  • 삭제 정책 ( delete ) : PV 삭제 / 실제 스토리지 내용 삭제

  • 재활용 정책 (recycle) : 재사용 x - 실제 스토리지 내용 비우고, PV를 사용 가능한 상태(Available)로 만듦
    ➜ 이 방식 대신 동적 프로비저닝 방법 선호
    스토리지가 네트워크 기반 스토리지면 아무 상관 없기 때문

    • 보존 정책
      ➜ PVC를 삭제해도 PV 그대로 둠
      한 번 사용한 PV는 다른 PVC가 연결하지 못함 (같은 PVC도 연결 끊고나면 다시 연결 못함)
      PV의 상태 : release
      PVC를 삭제하더라고 PV가 살아있으면 데이터가 남아있다는 뜻
      필요하다면 이 데이터를 백업해놓으면 되기 때문에 남겨 놓는 것

    • 삭제 정책
      ➜ PVC가 삭제되면 PV도 삭제
      (실제 스토리지 및 스토리지 내의 데이터도 모두 삭제)
      PVC를 남겨놓으면 파드는 계속 삭제해도 상관 x
      클라우드 스토리지 기본 반환 정책 == delete

🎈 pv-pvc 생명주기 실습

  • 🌟 생성 순서
    pv ➜ pvc

  • 삭제 순서
    ➜ pv와 pvc가 바운드 되어있는 상태에서 pv 삭제 불가
    pvc 삭제 후 pv 삭제


RS(파드) 삭제 후 재생성해도 기존 pvc-pv 에 연결 됨

$ kubectl delete -f myweb-rs.yaml
$ kubectl create -f myweb-rs.yaml
$ kubectl get rs,pvc,pv
# 📌 실행 후 결과 입력

pvc 삭제 시 pv는 release 상태 됨

$ kubectl delete -f mypvc.yaml   
$ kubectl get pv,pvc             
NAME                    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM           STORAGECLASS   REASON   AGE
persistentvolume/mypv   1G         RWX            Retain           Released✔️   default/mypvc                           3m28s

pvc-pv 해제 시, 모든 pvc 재연결 불가
pv 계속 release 상태

$ kubectl create -f mypvc.yaml       
$ kubectl get pv,pvc   
NAME                    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM           STORAGECLASS   REASON   AGE
persistentvolume/mypv   1G         RWX            Retain           Released✔️   default/mypvc                           3m44s

NAME                          STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mypvc   Pending   mypv     0                                        3s

pv와 pvc를 모두 삭제하고 다시 생성해줘야 다시 연결 가능

$ kubectl describe pvc mypvc 
...
Events:
  Type     Reason         Age                From                         Message
  ----     ------         ----               ----                         -------
  Warning  FailedBinding  12s (x5 over 58s)  persistentvolume-controller  volume "mypv" already bound to a different claim.

( x5 over 58s) ➜ x5 : 바인딩을 5번 시도했다는 뜻


💡 pv 를 먼저 지운다면?
➜ bound 상태에서는 pv 삭제 불가
삭제 중이라는 출력이 foreground 상태로 삭제 화면 계속 걸려있게 됨
다른 터미널에서 pvc 삭제 후, 현재 터미널에서 pv 삭제 가능



접근 모드(Access Mode)

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany
    ➜ 스토리지에 따라 지원되는 영역 모드 다름
    - once : 하나의 노드에서 연결 가능
    - many : 여러 노드에서 연결 가능

스토리지에 따라서 지원되는 명령이 다름
블록 스토리지냐 / 네트워크 스토리지냐

  • 블록 : DAS
  • 네트워크 : file (NAS) / SAN, EBS (블록)

DAS / SAN ➜ 공유 x (many 될 수 x)
NAS ➜ 공유 o (many 될 수 o)

NFS는 lock daemon이 있어서 동시에 쓰기가 막혀있어 공유 가능
읽기 전용은 쓰기 기능이 필요없기 때문에 공유 권한 풀어주는 것



NFS를 사용한 정적 프로비저닝(Static Provision)

node1 : NFS 서버 (임의)
➜ 데이터를 R/W 공유

NFS Kernel Server

node1

패키지 목록 업데이트 후 NFS Kernel server 다운

$ sudo apt update
$ sudo apt install nfs-kernel-server -y

/nfsvolume 디렉토리 생성한 후, 하위에 index.html 파일 생성

$ sudo mkdir /nfsvolume
$ echo "<h1> Hello NFS Volume </h1>" | sudo tee /nfsvolume/index.html

/nfsvolume 디렉토리의 소유자와 소유그룹을 www-data로 변경
➜ www-data : Ubuntu의 웹 서버(예: Apache, nginx)가 기본적으로 정상 작동을 위해 사용하는 사용자

$ sudo chown -R www-data:www-data /nfsvolume

/etc/exports 파일에 호스트 네트워크 대역 지정
NFS는 호스트가 바로 제공하는 형태 x ➜ 호스트가 마운트해서 kubelet이 제공하는 형태
➜ exports 파일 : NFS 클라이언트와 공유할 디렉토리를 지정하는 데 사용하는 파일
/etc/exports

...
/nfsvolume 192.168.100.0/24(rw,sync,no_subtree_check,no_root_squash)

NFS Kernel Server 시스템 재시작

$ sudo systemctl restart nfs-kernel-server  
$ systemctl status nfs-kernel-server      
● nfs-server.service - NFS server and services
     Loaded: loaded (/lib/systemd/system/nfs-server.service; enabled; vendor preset: enabled)
     Active: ✔️active (exited) since Mon 2022-05-23 07:16:36 UTC<; 14s ago
    Process: 250172 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
    Process: 250173 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS (code=exited, status=0/SUCCESS)
   Main PID: 250173 (code=exited, status=0/SUCCESS)

상태 active(exited) 맞음


NFS Client

node1, node2, node3

Worker Nodes에 NFS Client 패키지 설치
➜ 우분투의 클라이언트 패키지 / centos는 nfs-utils

$ sudo apt install nfs-common -y 

또는
~

$ ansible all -i ~/kubespray/inventory/mycluster/inventory.ini -m apt -a 'name=nfs-common' -b
$ mount. (tab tab)

➜ nfs 생성되어있음 📌 확인

PV
➕ PV의 accessMode가 readonly만 지정되어있으면 PVC accessMode가 그 이상 권한이어도 적용 불가
mypv.yaml

apiVersion: v1
kind: PersistentVolume ✔️
metadata:
  name: mypv
spec:
  accessModes: 
    - ReadWriteMany  ✔️
  capacity:
    storage: 1G
  persistentVolumeReclaimPolicy: Retain ✔️
  nfs:
    path: /nfsvolume ✔️
    server: 192.168.100.100 ✔️

PVC
mypvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim ✔️
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteMany  ✔️
  resources:
    requests:
      storage: 1G
  storageClassName: ''  ✔️ # Static Provisioning을 위해 비워놓음
  volumeName: mypv ✔️

RS
myweb-rs.yaml

apiVersion: apps/v1
kind: ReplicaSet ✔️
metadata:
  name: myweb-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: myweb
          image: httpd
          volumeMounts:
            - name: myvol
              mountPath: /usr/local/apache2/htdocs
      volumes:
        - name: myvol
          persistentVolumeClaim:
            claimName: mypvc ✔️

SVC
myweb-svc-lb.yaml

apiVersion: v1
kind: Service ✔️
metadata:
  name: myweb-svc-lb
spec:
  type: LoadBalancer ✔️
  ports:
    - port: 80
      targetPort: 80
  selector:
    app: web

pv 생성 확인
$ kubectl get pv  
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv   1G         RWX            Retain           Available ✔️                                9s

$ kubectl get all 로 확인 불가
available 상태여야만 PV 연결 가능
다른 상태들은 전부 연결 불가능


➕ NFS 서버 쪽에 사용자가 있어야함
아무 사용자 (xyz 등)을 사용하면 권한 지정 불가능
기타 사용자에게 모든 권한을 주면 보안 사고 발생 위험

만약 사용자가 없다면
uid 까지 확인하고 이 것까지 똑같은 사용자를 생성해줘야함



profile
Be on CLOUD nine! ☁️ ( 수정 예정 == 📌)

0개의 댓글