23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
안녕하세요, 데보션영 3기 강대훈입니다.
9월달은 여러 일들이 겹쳐 너무 바쁜 나머지 쿠버네티스 스터디 후기를 조금 늦게 올리게 되었습니다 💦
하지만 그만큼 실습을 제대로 진행하고자 멀티 노드 클러스터도 구축한 뒤 진행해보았습니다.
*멀티 노드 클러스터를 구축하는 방법에 대해선 추후 데보션 인사이드에 포스팅 예정입니다.
저번 스터디 때에는 ingress 컨트롤러로 외부에서 접속하는 실습을 진행했었습니다.
이번 실습에서는 실행된 클러스터 내에서 K8s의 여러 리소스들을 직접 생성해보는 실습을 진행했습니다.
금일 실습은 다음 환경에서 진행하였습니다.
- Master 노드 1대, Worker 노드 2대의 클러스터 환경
- OS : Ubuntu20.04
- CNI : Cillium네트워크 네임스페이스 차이와 NodePort 사용:
쿠버네티스에서 노드(Node) 와 파드(Pod) 는 서로 다른 네트워크 네임스페이스를 사용합니다.
이 때문에, 외부에서 파드로 접근하려면 NodePort를 통해 노드의 포트를 열어야 합니다.
NodePort는 클러스터 외부에서 특정 포트로 노드에 접근할 수 있도록 해주는 서비스 타입입니다.
hostNetwork 사용:
파드의 hostNetwork 옵션을 true로 설정하면, 해당 파드는 노드와 네트워크 네임스페이스를 공유하게 됩니다.
즉, 파드에서 사용하는 포트가 노드의 포트와 동일하게 적용됩니다. (파드의 포트를 443으로 지정하면 해당 파드가 호스트의 443번 포트를 사용)
이 경우, NodePort를 사용하지 않고도 파드가 노드의 네트워크를 그대로 사용할 수 있어 포트포워딩 없이 통신이 가능합니다.
주의 사항:
hostNetwork를 사용할 때, 특정 포트를 여러 파드가 동시에 사용할 수 없습니다.
예를 들어, 443 포트를 사용하는 파드가 하나 있다면, 다른 파드에서는 동일한 포트를 사용할 수 없습니다. 이는 노드가 해당 포트를 이미 점유하기 때문입니다.
hostNetwork를 설정한 경우, 주로 하나의 노드에 파드를 하나만 띄우거나, Ingress Controller 같은 컨트롤러를 단일 노드에만 띄우는 환경에서 사용될 수 있습니다.
사용 예:
Ingress Controller를 배포할 때, hostNetwork 옵션을 true로 설정하면, NodePort 없이 외부 트래픽을 처리할 수 있습니다.
이는 노드의 네트워크 네임스페이스를 그대로 사용하기 때문에 가능합니다.
pod 생성을 위한 yaml파일
apiVersion: v1
kind: Pod
metadata:
name: my-hostnetwork-pod
spec:
hostNetwork: true
containers:
- name: nginx
image: nginx
ports:
- containerPort: 4430
hostPort: 4430 # 노드의 4430번 포트를 직접 사용
describe를 통해 확인해 hostport와 port가 같은 것을 확인 가능합니다.
pod면 컨테이너가 여러개 들어갈 수 있는데 같은 스토리지를 볼 수 있을까?
➡️ 파드안에 넣었다는 것은 같은 네임스페이스를 쓴다는 의미이므로, 같은 네임스페이스를 쓰는 스토리지를 같이 쓸 수 있습니다.
volume 옵션에 emptydir 옵션을 주면 무슨일이 일어날까?
➡️ Pod가 생성될 때 빈 디렉토리가 생성되며, Pod내의 컨테이너들이 공유합니다.
볼륨의 hostpath 옵션을 주면 어떻게 될까?
➡️해당 노드의 hostpath 경로에 있는 디렉토리를 마운트합니다.
➡️이때, 디렉토리를 미리 만들어주어야합니다.
➡️또한 volumeMounts에서 mountPath 옵션을 통해 이 마운트 경로에 어떤 파일을 넣을지를 지정합니다.
name에는 해당 볼륨의 이름을 넣어주어야합니다.
다음과 같이 storage-pod.yaml 파일을 생성해줍니다.
apiVersion: v1
kind: Pod
metadata:
name: my-storage-pod
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: empty-dir-volume
mountPath: /usr/share/nginx/html/empty
- name: host-path-volume
mountPath: /mnt/host
volumes:
- name: empty-dir-volume
emptyDir: {}
- name: host-path-volume
hostPath:
path: /home/kdh/testdir
type: Directory # 지정된 경로가 디렉토리임을 나타낸다.directory를 만들지 않았을 때는 파드가 containercreating 단계에서 행에 걸리게 됩니다.
describe를 통해 확인해보니 다음과 같은 에러가 뜹니다.

폴더를 생성해주고 다시 확인하니 정상적으로 떠있는 모습을 볼 수 있습니다.

다이나믹 프로비저닝은 Kubernetes에서 스토리지 리소스를 동적으로 할당하는 기능으로,
사용자가 스토리지를 수동으로 설정할 필요 없이 요청할 때마다 자동으로 필요한 스토리지를 생성할 수 있게 해줍니다. (PV 할당)
이 방법은 주로 PersistentVolumeClaim (PVC) 를 통해 구현됩니다.
기존의 클러스터는 스토리지 서버가 따로 존재하기 때문에 모든 PV들은 해당 스토리지 서버에 생성됐었습니다.
그러나 분산 저장에 대한 니즈가 생겼고, local path 프로비저너가 필요해집니다.
Hostpath의 경우엔 정적으로 프로비저닝을 하기에 배제합니다.
레포 링크 ➡️ https://github.com/rancher/local-path-provisioner
Rancher Local Path Provisioner는 Kubernetes 클러스터에서 스토리지를 동적으로 프로비저닝하기 위한 도구입니다.
설치 시 local-path-storage 네임스페이스가 생성되고, 관련 파드도 생성됩니다.
사용자가 PVC를 요청하면, Local Path Provisioner가 자동으로 해당 디렉토리를 생성하고 스토리지를 할당합니다.
*쿠버네티스에서 공식으로 만든 프로비저너는 마운트를 해줘야하고, pv가 만들어지지않아서 pv도 직접 만들고 pvc도 연결해줘야하기 때문에 잘 사용하지 않습니다.
클러스터 관리자가 미리 적정 용량의 PV를 만들어두고, 요청이 있으면 미리 만들어둔 PV를 할당합니다.
사용 가능한 스토리지 용량에 제한이 있을 때 유용합니다.
PVC만 만들어도 pv를 자동으로 만들어줘서, 파드와 PVC를 연결해주면 바로 쓸 수 있습니다.
따라서 파드와 PVC만 만들면 알아서 스토리지가 만들어집니다.
랜처의 local-path는 다이나믹이므로 pvc만 만들면 됩니다.
다음 명령어로 설치해줍니다.
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.29/deploy/local-path-storage.yaml다음 명령어로 생성을 확인해줍니다.
kubectl get pods -A위 설명대로 local-path-storage 네임스페이스가 생성되고, 관련 파드도 생성된 것을 확인 가능합니다.

스토리지 클래스도 생성되었습니다.

이제 실습을 진행해보겠습니다.
먼저 실습용 네임스페이스를 생성해줍니다.
kubectl create namespace pvctest다음과 같이 PVC를 생성해줍니다. (pvc.yaml)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-path-pvc
namespace: pvctest
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi # 필요한 스토리지 크기 지정.
storageClassName: local-path # 사용할 스토리지 클래스를 지정.kubectl apply -f pvc.yaml파드를 생성합니다. (pod.yaml)
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
namespace: pvctest
spec:
containers:
- name: my-app-container
image: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html # 마운트할 경로!
name: local-storage
volumes:
- name: local-storage
persistentVolumeCl
claimName: local-path-pvc # 앞서 생성한 PVC의 이름kubectl apply -f pod.yaml아까 hostpath를 생성했을 땐 마운트할 경로에 폴더를 미리 생성해주지 않으면 생성이 되지 않았었습니다.
그러나 이번엔 미리 폴더를 생성하지 않아도 파드가 바로 생성된 모습입니다.

describe pod로 확인해보면 마운트가 정상적으로 된 것을 볼 수 있습니다.
Rancher Local Path Provisioner는 로컬 스토리지를 동적으로 생성하고, 마운트 하는 것을 돕습니다.

확인 시 다음과 같이 PV가 생성되어 있습니다. (pv는 네임스페이스가 정해져있지 않습니다)
Status가 Bound라는 것은 PV가 PVC에 연결되어 있다는 뜻입니다.

만약 해당 PV가 어느 노드의 자원을 사용하는지 보고싶다면 describe 명령어로 확인 가능합니다.
worker 2번 노드의 자원을 사용하고 있네요.

쉽게 정리하면!
PV = 쿠버네티스 안에서 사용되는 자원 (저장소를 추상화해 사용자와 노드가 상호작용할 필요 x)
PVC = 해당 자원을 사용하겠다고 요청하는 것
Rancher Local Path Provisioner = 로컬 스토리지를 위한 프로비저너
쿠버네티스는 스토리지 싱크를 지원하지 않기 때문에 스토리지가 알아서 해결해야 합니다.
따라서 보통은 ReadWriteOnce를 사용합니다. (한 노드에서만 읽고 쓰기가 가능하고, 여러 파드에서 동시 사용이 불가능한 옵션)
노드 실패 시의 영향
만약 로컬 패스 프로비저너를 사용하고 있는 파드가 실행 중인 노드가 죽으면, 그 노드에 종속된 볼륨(PV)은 더 이상 사용할 수 없습니다.
이 경우 Kubernetes가 파드를 다른 노드로 재배포하려고 해도, 원래의 스토리지(PV)에 접근할 수 없으므로 파드는 실행되지 않습니다.
결과적으로 데이터가 손실되거나, 해당 파드는 정상적으로 실행되지 못하게 됩니다.
일반적으로 마스터 노드에는 데몬셋이 실행되지 않는데, 이는 taint 때문에 발생합니다.
Toleration을 설정하면 마스터 노드에서도 데몬셋을 실행할 수 있습니다.
Toleration은 taint가 적용된 노드에 파드를 스케줄링할 수 있도록 허용하는 설정입니다.
taint가 있어야 toleration도 의미가 있어집니다.
워커 노드용 데몬셋은 toleration이 빠져있습니다 (워커 노드엔 taint 옵션이 빠져있기 때문에)
fluentbit, fluentd같은 로그 게더링 툴은 모든 노드에 설치되어야 하므로 toleration 옵션을 선택합니다.
Job은 일회성 작업을 수행하는 리소스로, 성공하면 파드의 상태가 Complete로 표시됩니다.
멘토님께서 완료된 job을 일정 시간 뒤에 클린할 수 있는 방법은 없나? 라는 질문을 해주셨습니다.
이에 대해 찾아보니 Job에는 TTL 스펙이 정의되어있어, 해당 스펙을 통해 Job을 클린할 수 있었습니다.
apiVersion: batch/v1
kind: Job
metadata:
name: my-job
spec:
ttlSecondsAfterFinished: 3600 # Job이 완료된 후 1시간(3600초) 후에 삭제
template:
spec:
containers:
- name: data-processor
image: data-processor-image
command: ["python", "process_data.py"] # 여기서 특정 작업인 스크립트를 실행
restartPolicy: NeverCronJob은 특정 주기에 따라 Job을 실행하는 리소스입니다.
StatefulSet은 상태를 유지하는 애플리케이션, 특히 데이터베이스에 사용되는 리소스입니다.
레플리카가 생성될 때, 각 파드의 이름에 -0, -1과 같은 인덱스가 붙습니다. 이는 파드의 순서를 식별하는 데 필요합니다.
StatefulSet에서 생성된 PVC는 파드가 삭제되더라도 지워지지 않습니다.
StatefulSet을 삭제한 후 다시 생성하면 기존의 PV와 연결됩니다.
스펙상으로 떠 있는 파드는 수정할 수 없습니다.
ConfigMap을 잘못 찾았거나,
리소스가 부족할 때,
스토리지 클래스를 잘못 지정한 경우 발생할 수 있습니다.
원인은 describe pod로 확인할 수 있습니다.
watch 명령어를 통해 파드의 변화를 실시간으로 볼 수 있습니다.
애플리케이션이 시작하는 동안 기다리도록 하는 헬스 체크 메커니즘.
스프링 등 실행이 오래걸리는 서버가 초기화되는 동안 다른 헬스 체크가 실패하지 않도록 방지함.
파드가 정상적으로 작동하고 있는지를 확인하기 위한 주기적인 헬스 체크를 수행.
파드가 응답하지 않거나 행에 걸리면, 설정된 임계값(threshold) 이상 실패 시 자동으로 파드를 재시작
파드가 요청을 받을 준비가 되었는지를 판단하는 헬스 체크.
파드가 서비스에 트래픽을 받을 준비가 되지 않았을 때, 해당 파드를 서비스에서 제외함.
파드 내의 컨테이너 단위로 CPU와 메모리 리소스를 설정하는 방법.
리소스 요청(Requests): 파드가 정상적으로 작동하기 위해 필요한 최소한의 리소스.
리소스 제한(Limits): 파드가 사용할 수 있는 최대 리소스량.
주요 고려사항:
자바 애플리케이션의 경우, 힙 사이즈(max heap size)를 설정하더라도 이를 초과할 수 있기 때문에, 리소스 리밋을 힙 사이즈보다 넉넉하게 주는 것이 좋다.
노드가 사용 가능한 리소스보다 더 많은 리소스를 요구하는 파드를 종료시키는 과정.
우선순위:
리소스 양을 정의하지 않은 파드
리퀘스트와 리밋 사이즈가 다른 파드
리퀘스트와 리밋 사이즈가 같은 파드
노드 메모리가 실제로 4GB일 경우, 파드의 메모리는 약 70-80% 정도 할당하는 것이 적절함.
메모리가 중요한 상황에서 노드 메모리를 초과하면 스케줄러가 해당 파드를 배포하지 않음.
Calico는 BGP(Border Gateway Protocol)를 사용하여 클러스터 내 네트워크를 관리.
루트 레플리카가 필요하며, BGP 전용 노드가 별도로 있어야 함.
100대 미만의 노드는 메시 구조로 연결 가능.
100대가 넘으면 문제 발생 가능성으로 인해 BGP 노드를 별도로 두는 것이 좋음.
L3 스위치가 BGP 역할을 할 수도 있음.
다음은 스터디 시간 질문 드린 내용입니다.
Q. 컨트롤플레인을 여러 노드로 띄우는 게 아닌, 각 요소(etcd, API server 등)를 여러 노드로 분리할 수 있는지?
A. etcd는 따로 띄울 수 있으나 작은 서비스에선 굳이 그럴 필요 없다.
이번 스터디는 실습을 진행하며 쿠버네티스의 여러 리소스를 제대로 파악할 수 있었고, 특히 다이나믹 프로비저닝에 대해 확실하게 알고 넘어갈 수 있어서 좋았습니다.
정말 유익한 스터디 항상 감사드립니다 🙇🏻♂️
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.