L7 LB == ALB
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
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
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
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
$ 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
옵션 : 상세 정보 출력
: 요청
< : 응답
* 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
* 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
우리가 뭘 요청해도 응답을 돌려주는 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
푸시한 이미지로 Ingress 구현
웹 이미지 생성 및 푸시
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
작업 디렉토리 : ~/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
파드의 헬스체크
를 통해 서비스의 엔드포인트
리소스에 타겟 등록
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
(서비스 오브젝트 별도 사용하여 포트 포워딩)
파드는 계속해서 생성 및 삭제를 반복하기 때문에
컨테이너 내의 디스크에 있는 파일은 임시적으로 존재
➜ 컨테이너에 다양한 유형의 볼륨을 생성하여 데이터 따로 저장 (pv는 파드의 수명을 넘어서도 존재함)
🎈 volume spec 확인
pod.spec.volumes.*
: 볼륨 유형 확인 가능
임시로 사용할 빈 볼륨(스크래치)
하나의 파드에 존재하는 여러 밀접한 관계의 컨테이너 사이에 공유하는 볼륨
파드 삭제 시 볼륨 같이 삭제 (임시로만 사용)
여러 개의 컨테이너가 동일 볼륨을 임시로 사용할 때만 이용
pod.spec.volumes.emptyDir.medium
🎈 볼륨 마운트 방법
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
Git Repository에 있는 데이터 복사해서 emptyDir에 채워서 줌
➜ 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
: 실제 명령이 오래 지속되지 않는 경우 컨테이너를 무기한 유지하기 위한 일반적인 관용구
호스트 노드의 파일시스템에 있는 파일이나 디렉터리를 파드에 마운트
경로를 채워서 컨테이너에게 제공
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
PersistentVolume (pv)
: 스토리지 볼륨 정의 -> 실제 스토리지 정의 부분PersistentVolumeClaim (pvc)
: PV 요청🎈 pv, pvc 리소스 정보 확인
$ kubectl api-resources | grep pv
persistentvolumeclaims pvc v1 true PersistentVolumeClaim
persistentvolumes pv v1 false PersistentVolume
스토리지를 정의
하는 오브젝트가 PV
개발자는 PVC
라는 리소스를 사용하여 PV를 이름으로만 요청
pv는 containers의 volumes처럼 볼륨의 형식 ( pv.spec
)이 들어감
🎈 PVC가 PV를 지칭하는 방법
파드는 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 # 디렉토리가 있으면 사용 없으면 생성
pod - PVC - PV - 스토리지
PV <- 1:1 -> PVC
(무조건 1:1 연결)
- 프로비저닝
- 바인딩
- 사용
- 회수/반환(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 까지 확인하고 이 것까지 똑같은 사용자를 생성해줘야함