쿠버네티스란?
가장 대표적인 컨테이너 오케스트레이션 시스템으로, 사실상 표준이다.
구글에서 사용하던 배포시스템 borg를 기반으로 재작성해 2014년에 오픈소스로 공개하였다.
심심하면 아래 문서를 읽자.
Concepts
Production-Grade Container Orchestration
kubernetes.io
컨테이너 오케스트레이션 시스템이란?
컨테이너의 배포, 관리, 확장, 네트워킹을 자동화하는 기술
Docker, Docker Compose는 하나의 머신에서 컨테이너를 관리하기 위한 기술이다.
컨테이너 오케스트레이션 시스템은 여러 머신으로 구성된 클러스터 상에서 컨테이너를 효율적으로 관리하기 위한 시스템이다.
쿠버네티스 외에 Rancher, Apache MESOS, Nomad, Docker Swarm 등이 있다.
- Scheduling
- Resource Allocation
- Service Discovery
- Load Balancing
- Scaling
- Configuration Management
- Self Healing
- Rollback / Rollout
- Storage Orchestration
쿠버네티스의 장점
- Planet Scale
- 행성 규모로 확장할 수 있는 스케일
- 구글에서 수십억 개의 컨테이너를 운영할 수 있게 한 기술을 바탕으로 한다.
- Never Outgrow
- 다양한 요구사항을 만족할 수 있는 유연함
- 테스트용 로컬 규모부터 글로벌 서비스 규모까지 유연하게 크기 조정 가능
- 필요한 기능이 없을 경우 CRD(Custom Resource Definition)를 통한 기능 확장
- Run Anywhere
- 온프레미스 / 퍼블릭 클라우드 / 하이브리드 환경 어디서나 동작
쿠버네티스 도입시 주의할 점
- 쿠버네티스는 여러 컴포넌트로 구성된 분산 시스템으로, 각 컴포넌트에 대한 이해가 필요하다.
- 오버 엔지니어링일 수 있다. 쿠버네티스 운영 및 관리에 필요한 인력과 비용을 고려해야 한다.
- 버전을 꼼꼼하게 검토한다.
(Major 버전 업데이트의 ChangeLog와 Breaking Changes 검토, API Resources Deprecation Schedule 검토) - 직접 클러스터 구성하는 것은 힘들 수 있기 때문에 Managed Cluster 사용을 고려해본다. (Amazon EKS, GCP 등)
자체 서버 환경 구성 -> EC2에 환경 구성 -> Amazon EKS, GCP
실습 환경
- Docker for Desktop 설치 (docker, docker-compose)
- kubectl 설치
- kustomize 설치
- minikube 설치
kubectl 설치하기
쿠버네티스의 API 서버와 통신하여 사용자 명령을 전달할 수 있는 CLI 도구
참고: https://dockerlabs.collabnix.com/kubernetes/beginners/what-is-kubect.html
쿠버네티스는 클러스터 시스템으로, 마스터노드와 워커노드로 구성된다.
마스터노드에는 API 서버가 존재한다. 이 API 서버를 통해서 쿠버네티스 클러스터에 명령어를 주고받을 수 있다.
brew install kubectl
도커 데스크탑에 kubectl이 내장되어 있을 수도 있는데, 그러면 버전 충돌이 있기 때문에 덮어쓰기를 해준다.
brew link --overwrite kubernetes-cli
참고: https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/
# 버전 확인
# 클라이언트 버전 = kubectl 버전
# 서버 버전 = 쿠버네티스 버전
kubectl version
kustomize 설치하기
쿠버네티스의 매니페스트 파일을 효율적으로 관리할 수 있도록 도와주는 도구
쿠버네티스에 올라가는 어플리케이션 정보를 kubernetes-manifest 파일에 관리한다.
이 관리 도구로 많이 사용되는 것에 kustomize, Helm이 있다.
brew install kustomize
kustomize는 kubectl에 내장되어 있다. 이 kustomize를 사용하려면 kubectl kustomize 명령어를 사용한다.
minikube 설치하기
가상 환경을 사용하여 복잡한 쿠버네티스 클러스터를 쉽게 구현해준다.
driver를 선택해서 원하는 가상 환경(docker, postman, virtual box, parallels, vmware, hyperkit 등)에서 구성 가능하다.
실제 운영 환경에서 쓰기는 어렵지만 쿠버네티스 학습 목적으로 활용하기 좋다. (실제 운영에선 여러 대의 머신 위에서 클러스터를 구성한다.)
brew install minikube
# 쿠버네티스 클러스터 구성
minikube start --driver docker
# 확인
cat .kube/config
minikube status
kubectl cluster-info
참고: https://minikube.sigs.k8s.io/docs/start/
클러스터
하나의 유닛으로 동작하는 컴퓨터들의 묶음
컨테이너화된 어플리케이션을 클러스터에 배포한다.
# 클러스터 생성 (minikube 실습)
minikube start
# 클러스터 정보 확인
kubectl cluster-info
# 클러스터 노드 정보 확인
# 어플리케이션을 호스트 할 수 있는 모든 노드를 확인할 수 있다.
# STATUS Ready: "어플리케이션 배포를 받을 준비가 되어있다."
kubectl get nodes
클러스터 구성
- Control Plane (Master Node)
- 클러스터 관리
- 상태 관리 및 명령어 처리
- Node (Worker Node)
- 어플리케이션 컨테이너 실행
- API를 통해 Control Plane과 통신
Control Plane (Master Node)
- API Server
- 쿠버네티스 리소스와 클러스터 관리를 위한 API 제공
- etcd를 데이터 저장소로 사용
- Scheduler
- 노드의 자원 사용 상태를 관리
- 새로운 워크로드를 어디에 배포할지 관리
- Controller Manager
- Controller가 각 리소스(Pod, Deployment, Service, Secret 등)를 관리
- Contoller Manager는 여러 컨트롤러 프로세스를 관리
- 각 컨트롤러는 클러스터로부터 특정 리소스 상태의 변화를 감지해 클러스터에 반영하는 reconcile 과정을 반복 수행
- etcd
- 분산 키밸류 저장소
- 클러스터 상태 저장 (백업, 롤백시 etcd를 백업, 롤백하면 클러스터 상태를 바꿀 수 있다.)
Node (Worker Node)
- kublet
- API 서버와 통신하며 노드의 리소스 관리
- 컨테이너 런타임과 통신하며 컨테이너 라이프 사이클 관리
- CRI (Container Runtime Interface)
- kublet이 컨테이너 런타임과 통신할 때 사용되는 인터페이스
- 컨테이너 이미지를 pull, 컨테이너를 unpack, 어플리케이션 실행
- 쿠버네티스는 Docker, containerd, cri-o 컨테이너 런타임 지원 (도커에 의존하지 않아도 된다.)
- kube-proxy
- 오버레이 네트워크 구성
- 네트워크 프록시 및 내부 로드밸런서 역할 수행
API 리소스와 오브젝트
- API 리소스
- 쿠버네티스가 관리할 수 있는 오브젝트의 종류
- Pod, Service, ConfigMap, Secret, Node, ServiceAccount, Role
- 오브젝트
- API 리소스를 인스턴스화 한 것
kubectl로 트러블슈팅
# 현재 쿠버네티스 클러스터가 지원하는 API 리소스 목록 확인
kubectl api-resources
# 특정 API 리소스에 대한 간단한 설명 확인
kubectl explain pod
# 특정 API 리소스에 대한 자세한 설명 확인
kubectl describe pod
# 특정 API 리소스의 목록 확인
kubectl get pod
kubectl get pod --all-namespaces
# 특정 컨테이너의 로그 출력 (실습에서 컨테이너가 1개뿐이라 컨테이너명 생략)
kubectl logs $POD_NAME
# 특정 컨테이너에 명령어 전달 (실습에서 컨테이너가 1개뿐이라 컨테이너명 생략)
kubectl exec $POD_NAME -- env
# 특정 컨테이너 bash sessition 시작 (실습에서 컨테이너가 1개뿐이라 컨테이너명 생략)
kubectl exec -ti $POD_NAME -- bash
exit
매니페스트 파일
쿠버네티스는 오브젝트를 매니페스트 파일(.yml)로 관리한다.
참고: https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/
apiVersion: v1
kind: Service
metadata:
name: my-nginx-svc
labels:
app: nginx
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: nginx
- apiVersion: 오브젝트가 어느 버전의 API 그룹에 속하는지
- kind: 오브젝트가 어떤 API 리소스인지
- metadata: 오브젝트를 식별하기 위한 정보
- spec: 오브젝트가 가지려는 데이터
(API 리소스에 따라 spec 대신 data, rules, subjects 등의 다른 속성 사용)
메타데이터 Labels vs Annotations
모든 쿠버네티스 오브젝트는 Labels와 Annotations 메타데이터를 가질 수 있다.
- Labels
- 오브젝트를 식별하여 검색, 분류, 필터링하기 위함
- 쿠버네티스 내부 여러 기능에서 Label Selector 제공
- Annotations
- 식별이 아닌 다른 목적으로 사용
- 보통 쿠버네티스의 애드온이 해당 오브젝트를 어떻게 처리할지 결정하기 위한 설정 용도로 사용
kubectl 명령형 vs 선언형
- 명령형 Imperative
- 수행하고자 하는 액션을 지시한다.
- 적은 리소스에 대해서 빠르게 처리 가능하다.
- 여러 명령어를 알아야 한다.
- e.g. Java
- 선언형 Declarative
- 도달하고자 하는 상태(Desired State)를 선언한다.
- 코드로 관리 가능하다. = GitOps 활용이 가능하다.
- 변경사항에 대한 감사(audit)이 용이하다.
- 코드리뷰를 통한 협업이 쉽다.
- 멱등성을 보장한다. (한 번 실행한 것과 세 번 실행한 것의 결과가 같다.)
- 많은 리소스에 대해서도 매니페스트 관리 방법에 따라 빠르게 처리 가능하다.
- 알아야 할 명령어 수가 적다.
- e.g. SQL
kubectl 명령형 명령어
# ubuntu:focal 이미지로 ubuntu pod 생성
kubectl run -i -t ubuntu ubuntu:focal bash
# grafana Deployment 오브젝트에 대해 NodePort 타입의 Service 오브젝트 생성하고 노드에 포트 개방
kubectl expose deployment grafana --type=NodePort --port=80 --target-port=3000
# frontend Deployment의 www 컨테이너 이미지를 image:v2로 변경
kubectl set image deployment/frontend www=image:v2
# frontend Deployment를 revision 2로 롤백
kubectl rollout undo deployment/frontend --to-revision=2
kubectl 선언형 명령어
# deployment.yaml에 정의된 쿠버네티스 오브젝트 클러스터에 반영
kubectl apply -f deployment.yaml
# deployment.yaml에 정의된 쿠버네티스 오브젝트 제거
kubectl delete -f deployment.yaml
# 현재 디렉토리의 kustomization.yaml 파일을 쿠버네티스 오브젝트 클러스터에 반영
kubectl apply -k ./
선언형 방식으로 정의해두면 컨트롤러가 이를 보고 클러스터에 적용한다.
기본적인 오브젝트의 CRUD에 대해선 선언형을 사용한다. ssh 접속, 로그 확인 등엔 명령형 명령어를 알아야 한다.
API 리소스
- Pod
- Deployment
- Service
- ConfigMap
- Secret
- Namespace
- ResourceQuota
- LimitRange
- Job, CronJob
- DaemonSet
- Ingress
API 리소스 - Pod
- 쿠버네티스가 컨테이너를 다루는 기본 단위
- 같은 호스트 상에서 실행되는 1개 이상의 컨테이너로 구성된 컨테이너 집합
- 동일 Pod 내 컨테이너는 여러 리눅스 네임스페이스를 공유한다. (네트워크 네임스페이스 공유 = 동일 IP 사용)
- 사용자가 Pod를 직접 관리하는 경우는 거의 없다.
- 워커 노드가 죽으면 그 위의 Pod들도 사라진다.
- Pod은 기본적으로 클러스터 내에서 내부 IP로만 접근 가능하다. (내부의 다른 Pod이나 Service에선 접근 가능)
외부에서 접근 가능하게 하려면...
- Pod을 가리키는 Service를 expose 한다.
- 'kubectl proxy' 명령어로 프록시를 만들어 쿠버네티스 API에 direct access 할 수 있다.
쿠버네티스 API 서버는 자동으로 각각의 Pod에 엔트포인트를 만드는데, 프록시를 통해 접근 가능하다.
# 파드 목록 확인
kubectl get pod
# 특정 파드 상태 확인
kubectl describe pod hello
# 특정 파드에 명령어 전달 (기본 컨테이너에 전달됨)
kubectl exec -i -t hello bash
# 특정 파드 로그 확인 (기본 컨테이너 로그 확인됨)
kubectl logs pod/hello
# 새 서비스 만들어 외부에 expose
kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
(+ 'minikube ssh' 명령어를 사용해 클러스터 노드 셸 접근 가능)
멀티 컨테이너 파드
- 2개 이상의 컨테이너를 포함하고 있는 파드
- 컨테이너들은 같은 네트워크 상에서 동작한다. (따라서 포트 충돌이 일어나지 않게 포트를 다르게 지정해줘야 한다.)
- 완전히 별개의 프로세스들을 묶어서 같은 컨테이너에 배포하는 것은 권장하지 않는다.
사이드카 패턴 Side-car Pattern
- 메인 컨테이너를 보조하는 컨테이너와 같이 실행하는 구조
- 주요 사용
- Filebeat와 같은 로그 에이전트로 파드 로그 수집
- Envoy와 같은 프록시 서버로 서비스메시 구성
- Valut Agent와 같은 기밀 데이터 전달
- Nginx의 설정 리로드 역할 에이전트 구성
- logs, exec 등의 명령어가 어떤 컨테이너에 대한 것인지 '-c' 옵션으로 지정해줘야 한다. (미지정시 기본 컨테이너 or 오류)
# 특정 컨테이너 로그 확인
kubectl logs pod/hellp -c debug
# 특정 컨테이너에 명령어 전달
kubectl exec -i -t hello -c debug bash
참고: https://seongjin.me/kubernetes-multi-container-pod-design-patterns/
API 리소스 - ReplicaSet
Pod의 수를 늘리고 싶으면(Scale-out) ReplicaSet을 설정한다.
- 정해진 수의 Pod가 항상 실행될 수 있도록 관리
- 기존에 실행 중이던 파드에 문제가 생기면 파드를 다시 스케쥴링
- ReplicationController의 신규 버전
- 사용자가 ReplicaSet을 직접 관리하는 경우는 거의 없다.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
- Control Plane에 있는 ReplicaSet Controller가 spec.selector에 대응하는 파드의 수가 spec.replicas와 동일한지 지속적으로 검사하고, 다를 경우에 스케일인/아웃을 진행한다.
- selector에 대응하는 Pod인지 확인하는 것이지, ReplicaSet에서 관리하는 Pod인지 확인하는 것은 아니다.
- 레이블 셀렉터 Label Selector :
- 쿠버네티스 오브젝트는 모두 metadata.labels에 키밸류 형태의 레이블 값을 가진다.
- 특정 오브젝트 목적을 필터링 하기 위한 기능이다.
- API 리소스 간의 관계를 레이블 셀렉터로 연결하여 리소스 간의 느슨한 결합을 유지할 수 있다.
- matchLabels와 matchExpressions 옵션을 제공한다.
- 위 소스에서, spec.replicas, spec.selector는 ReplicaSet의 옵션, spec.template은 ReplicaSet이 만드는 Pod의 템플릿
- 위 소스에서, 'tier: frontend를 가지고 있는 Pod를 관리하겠다'는 의미이고, 그래서 Pod의 템플릿에도 'tier: frontend' 레이블을 지정해 준 것
API 리소스 - Deployment
- Pod의 이미지 버전이 업데이트될 때 배포 전략을 설정
- Deployment 오브젝트를 생성하면 대응되는 ReplicaSet과 Pod를 자동으로 생성
- 기본적으로 Recreate 전략, RollingUpdate 전략을 지원
- Recreate 전략
- 기존 ReplicaSet의 Pod를 모두 종료한 후 새 ReplicaSet의 Pod를 새로 생성한다.
- RollingUpdate 전략
- 세부 설정에 따라 기존 ReplicaSet에서 새 ReplicaSet으로 점진적으로 이동한다.
maxSurge (업데이트 과정에서 최대로 추가할 수 있는 파드 수)
maxUnavailable (업데이트 과정에서 이용 불가능한 상태인 파드 수)
- 세부 설정에 따라 기존 ReplicaSet에서 새 ReplicaSet으로 점진적으로 이동한다.
- Recreate 전략
- 클러스터 생성
- Deployment configuration 생성
- Control Plane이 Deployment에 속하는 어플리케이션의 배포 스케쥴링
(어플리케이션을 배포할 수 있는 적합한 노드를 찾아 스케쥴링한다.) - Deployment Controller가 어플리케이션의 인스턴스를 지속적으로 모니터링
- Deployment Controller가 어플리케이션 인스턴스가 구동 중이니 노드가 다운/삭제시 클러스터 다른 노드의 인스턴스로 교체 (자동 복구 Self-healing)
# 노드 상태 확인
kubectl get nodes
# Deployment 생성하여 배포
kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1
# Deployment 확인
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# kubernates-bootcamp 1/1 1 1 4m50s
- NAME: the name of the Deployment
- READY: CURRENT / DESIRED replicas
- UP-TO-DATE: the number of replicas that have been updated to achieve the desired state
- AGE: the amount of time that the application has been running
API 리소스 - 서비스
- 하나의 논리적인 Pod 그룹과 그 Pod들에 접근할 수 있는 정책을 정의하는 추상적 개념 (Pod들 간의 결합도를 낮춤)
- 여러 Pod에 대해 클러스터 내에서 사용 가능한 고유 도메인을 부여 (with etcd)
- 여러 Pod에 대해 요청을 분산하는 로드 밸런서 기능(L4 - IP/Port 기반) 수행
- 이전 실습에서 Pod IP를 통해 서비스에 접근했는데, Pod의 IP는 재시작될 때 변하므로 주의해야 한다.
- 서비스의 ServiceSpec에서 type을 지정함으로써 다양한 방식으로 노출시킬 수 있다.
- ClusterIP (기본값) : 내부에서만 접근 가능
- NodePort: 외부에서 '<NodeIP>:<NodePort>'를 이용해 접근 가능
- LoadBalancer: 외부용 로드밸런서(클라우드 프로바이더)를 생성하고 서비스에 고정된 공인 IP를 할당
- ExternalName: 외부로 나가는 트래픽을 변환, CNAME 레코드 값을 반환하여 externalName 필드의 내용에 매핑
- 일반적으로는 ClusterIP 타입의 Service와 함께 Ingress를 사용하여 외부 트래픽을 처리한다.
ClusterIP
apiVersion: v1
kind: Service
metadata:
name: hello
lebels:
app: hello
spec:
type: ClusterIP
ports:
- name: http
protocol: TCP
port: 8080
targetPort: 80
selector:
app: hello
- 서비스 네트워크 IP/Port 정보: "spec.clusterIp:spec.ports[*].port"
- Service API 리소스의 기본 타입
- 클러스터 내부 통신 목적으로만 사용 가능
- 클러스터에는 Pod에 부여되는 IP를 위한 CIDR 대역과, Service에 부여되는 Cluster IP CIDR 대역이 독립적으로 존재한다.
- Label Selector를 통해 서비스와 연결할 파드 목록 관리
- Cluster IP로 들어오는 요청에 대해 Pod에 L4 레벨의 로드밸런싱
- Cluster IP뿐만 아니라 내부 DNS를 통해 서비스 이름을 이용한 통신 가능
# 서비스의 Cluster IP CIDR 대역 확인
kubectl cluster-info dump | grep -m 1 service-cluster-ip-range
NodePort
appVersion: v1
kind: Service
metadata:
name: hello
labels:
app: hello
spec:
type: NodePort
ports:
- name: http
protocol: TCP
port: 8080
targetPort: 80
# nodePort: 31000 미정의시 사용가능한 port를 랜덤 선정
selector:
app: hello
- 서비스 네트워크 IP/Port 정보: "<NodeIP>:spec.ports[*].nodePort spec.clusterIp:spec.ports[*].port"
- 모든 쿠버네티스 노드의 동일 포트를 개방하여 서비스에 접근하도록 한다.
- NodePort는 ClusterIP 타입 서비스를 한 번 더 감싸서 만들어진 것
- NodePort 서비스도 ClusterIP 사용 가능
- NodePort로 들어온 요청은 실제로 ClusterIP로 전달되어 Pod으로 포워딩
LoadBalancer
- 서비스 네트워크 IP/Port 정보: "spec.loadBalancerIp:spec.ports[*].port <NodeIP>:spec.ports[*].nodePort spec.clusterIp:spec.ports[*].port"
- 클라우드 프로바이더에서 제공하는 로드밸런서를 동적으로 생성하는 방식
- LoadBalancer는 NodePort 타입 서비스를 한 번 더 감싸서 만들어진 것
- LoadBalancer 서비스도 ClusterIP 사용 가능
- LoadBalancer 서비스를 통해 만들어진 로드밸런서는 NodePort를 타겟 그룹으로 생성
- NodePort로 들어온 요청은 실제로 ClusterIP로 전달되어 Pod로 포워딩
API 리소스 - Namespace
- 목적: API 상의 오브젝트들을 논리적으로 분류하기, 권한 주기, 리소스 제한하기
- 네임스페이스의 단위는 사용자 목적에 따라 결정한다. (팀 단위, 환경 단위, 서비스 단위, ...)
- 네임스페이스에 속할 수 있는 리소스를 네임스페이스 범위 API 리소스라고 한다.
- API 리소스가 네임스페이스에 종속되는 것인지, 아니면 클러스터 범위에서 사용되는 것인지에 따라 두 가지로 구분할 수 있다.
- 네임스페이스 범위 API 리소스 (Namespace-scoped API Resources)
Pod, Deployment, Service, Ingress, Secret, ConfigMap, ServiceAccount, Role, RoleBinding 등 - 클러스터 범위 API 리소스 (Cluster-scoped API Resources)
Node, Namespace, IngressClass, PriorityClass, ClusterRole, ClusterRoleBinding 등
- 네임스페이스 범위 API 리소스 (Namespace-scoped API Resources)
kubectl api-resources --namespaced=true # 네임스페이스 범위의 리소스들 확인
kubectl api-resources --namespaced=false # 클러스터 범위의 리소스들 확인
클러스터 기본 네임스페이스
쿠버네이트 클러스터를 생성하면 기본적으로 만들어지는 네임스페이스
- default: 네임스페이스를 지정하지 않는 경우에 기본적으로 할당되는 네임스페이스
- kube-system: 쿠버네티스 시스템에 의해 생성되는 API 오브젝트들을 관리하기 위한 네임스페이스
- kube-public: 클러스터 내 모든 사용자로부터 접근 가능하고 읽을 수 있는 오브젝트들을 관리하기 위한 네임스페이스
- kube-node-lease: 쿠버네티스 클러스터 내 노드의 연결 정보를 관리하기 위한 네임스페이스
다른 네임스페이스의 서비스 접근하기
서로 다른 Service들이 통신하기 위해서는 서비스명으로는 충분치 않다. (네임스페이스가 다른데 서비스명이 동일한 경우)
- FQDN(Fully Qualified Domain Name)과 Domain Search 옵션(resolve.conf 파일)
curl ${service}.${namespace}.svc.cluster.local #FQDN
curl ${service}.${namespace}.svc
curl ${service}.${namespace}
curl ${service} # 동일 네임스페이스 내에서 접근
ResourceQuota와 LimitRange
네임스페이스 단위의 자원 사용량을 관리할 수 있는 기능을 제공한다.
- ResourceQuota
- 네임스페이스에서 사용할 수 있는 자원 사용량의 합을 제한
- 할당할 수 잇는 자원(CPU, Memory, Volume 등)의 총합 제한
- 생성할 수 있는 리소스(Pod, Service, Deployment 등)의 개수 제한
- 네임스페이스에서 사용할 수 있는 자원 사용량의 합을 제한
- LimitRange
- Pod 혹은 컨테이너에 대하여 자원 기본 할당량 설정, 혹은 최소/최대 할당량 설정
API 리소스 - ConfigMap
- 설정 정보를 환경변수 or 볼륨의 형태로 파드에 전달하기 위한 목적
- Pod에서 직접 환경변수를 관리하지 않고 ConfigMap으로 분리하여 목적에 따라 설정값을 다르게 주입한다.
ConfigMap 값을 컨테이너의 환경변수로 사용하기
BEFORE: Deployment에 환경변수 설정 >
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
name: mysql
labels:
app: mysql
spec:
container:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: testpassword
- name: MYSQL_DATABASE
value: devops
ports:
- name: http
containerPort: 3306
protocol: TCP
AFTER: ConfigMap에 환경변수 설정1 (envFrom) >
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
MYSQL_ROOT_PASSWORD: testpassword
MYSQL_DATABASE: devops
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
name: mysql
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
envFrom:
- configMapRef:
name: mysql-config
AFTER: ConfigMap에 환경변수 설정2 (valueFrom) >
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
name: mysql
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
configMapKeyRef:
name: mysql-config
key: MYSQL_ROOT_PASSWORD
ConfigMap 값을 Pod의 볼륨으로 마운트하여 사용하기
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
name: mysql
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
envFrom:
- configMapRef:
name: mysql-config
volumeMounts:
- mountPath: /tmp/config
name: mysql-config
volumes:
- name: mysql-config
configMap:
name: mysql-config
ConfigMap을 볼륨으로 마운트 하면 키는 파일명, 밸류는 파일 내용이 된다. (그냥 참고)
ConfigMap 외에 Secret, emptyDir(인메모리)을 볼륨으로 마운트하여 사용할 수도 있다.
Volume 외에 PersistentVolumeClaim을 통해 PersistentVolume을 사용할 수도 있다.
# my-config 이름의 ConfigMap 생성하기
kubectl create configmap my-config
# my-config 이름의 ConfigMap 생성하기 - 키는 config.yaml, 밸류는 파일 내용
kubectl create configmap my-config --from-file config.yaml
# my-config 이름의 ConfigMap 생성하기 - 키는 config, 밸류는 파일 내용
kubectl create configmap my-config --from-file config=config.yaml
# my-config 이름의 ConfigMap YAML 출력하기 - 키는 config, 밸류는 파일 내용
# -dry-run 명령어의 결과를 클러스터 반영하는 게 아니라, 우선 확인해보기 위함 - yaml 파일로 출력
kubectl create configmap my-config --from-file config=config.yaml -dry-run -o yaml
위는 밸류의 값이 너무 커서 ConfigMap을 직접 선언하기 힘들 때 활용할 수 있는 명령어들이다.
API 리소스 - Secret
- 패스워드, API Key, SSH Key 등 민감한 정보를 컨테이너에 주입하기 위한 목적
- ConfigMap과 사용법이 비슷하다.
- 사용 목적에 따라 몇 가지 종류로 나뉜다. Opaque, dockerconfigjson, tls, service-account-token
- 쿠버네티스는 기본적으로 시크릿값을 저장할 때 Base64 인코딩을 한다.
- 모든 API 리소스는 etcd에 저장이 된다. Secret도 마찬가지로, etcd에 접근 권한이 있으면 Secret을 읽을 수 있다. 그래서 RBAC(Role Based Access Control)을 잘 조정해서 관리해야 한다.
(AWS EKS는 KMS와 통합해서 etcd에 저장할 때 한 번 더 encrypt 해주기도 한다.) - 선언 순서를 주의한다. 키가 같을 경우 뒷 값이 앞 값을 덮어쓴다.
Secret의 종류
- Opaque (generic) : 일반적인 용도의 시크릿
- dockerconfigjson : 도커 이미지 저장소 인증 정보 (쿠버네티스가 private 이미지 저장소 접근시 사용)
- tls : TLS 인증서 정보 (Pod, Service 등에서 통신 암호시 사용)
- service-account-token : ServiceAccount의 인증 정보 (ServiceAccount: RBAC을 바탕으로 Pod의 권한을 설정하는 서비스, ServiceAccount를 연결시켜주면 인증 토큰 Secret이 자동 생성됨)
Secret의 사용
(ConfigMap과 사용 방법이 비슷해서 예시 생략)
- secretRef
- valueFrom - secretKeyRef
- volumes - name, secret - secretName
- dockerconfigjson 설정
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: docker-registry
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: ...
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
name: mysql
labels:
app: mysql
spec:
imagePullSecrets: # 이미지를 pull 할 때 docker-registry 시크릿을 사용
- name: docker-registry
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: testpassword
- tls 설정
# openssl 통해 인증서 파일 생성
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/CN=google.com" -keyout cert.key -out cert.crt
# 인증서 파일을 통해 tls 타입의 Secret 생성
kubectl create secret tls my-tls \
--cert cert.crt \
--key cert.key \
--dry-run -o yaml
# 위 명령어를 통해 생성된 Secret
apiVersion: v1
kind: Secret
metadata:
name: my-cert
type: kubernetes.io/tls
data:
tls.crt: ...
tls.key: ...
Secret의 관리
Secret은 버전관리시스템으로 관리해서는 안 된다. 다음 두 가지 방법 중 하나를 선택한다.
- External Secrets
- HashiCorp Vault, AWS Secrets Manager, AWS Parameter Store 등과 통합
- ExternalSecret 오브젝트를 생성하면 컨트롤러가 프로바이더로부터 기밀 값을 가져와 Secret 오브젝트 생성
- Sealed Secrets
- 쿠버네티스 클러스터 상에 컨트롤러 실행
- 클러스터 상에 암호화 키 보관
- kubeseal CLI가 컨트롤러와 통신하며 데이터 암호화
- SealedSecret 오브젝트를 생성하면 컨트롤러가 복호화하여 Secret 오브젝트 생성
- 암호화된 데이터가 버전관리시스템에 올라간다.
API 리소스 - Job, CronJob
Job
- 지속적으로 실행되는 서비스가 아니라 특정 작업을 수행하고 종료하는 작업을 정의한다.
- 내부적으로 Pod을 생성하여 작업을 수행한다.
- Pod의 상태가 Running을 지나고 Completed가 되면 최종 상태이다.
- 실패시 재시작 옵션, 작업 수행 회수, 동시 실행 수 등 세부 옵션을 설정할 수 있다.
# Hello World를 한 번 출력하는 잡
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
spec:
restartPolicy: Never
containers:
- name: hello
image: ubuntu:focal
command: ["sh", "-c", "echo Hello World!"]
Deployment 사용시에는 Label Selector를 통해 Deployment 오브젝트가 어떤 Pod들을 관리하는지 연결시켜 주었다. Job과 같은 경우에는 Selector 설정이 빠져있다. Job Controller가 기본적으로 Template의 Pod에 Selector를 만들어주기 때문이다. ("kubectl describe job" 명령어를 통해 Selector 설정 확인 가능)
# Hello World를 최대 2개의 Pod가 동시에 총 10번 출력하는 잡
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
completions: 10 # 총 성공 수
parallelism: 2 # 최대 동시 실행 횟수
template:
spec:
restartPolicy: Never
containers:
- name: hello
image: ubuntu:focal
command: ["sh", "-c", "sleep 2; echo Hello World!"]
# Hello World를 출력하려는데 3초가 지나면 잡 실패
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
activeDeadlineSeconds: 3 # 시간 제한
template:
spec:
restartPolicy: Never
containers:
- name: hello
image: ubuntu:focal
command: ["sh", "-c", "sleep 5; echo Hello World!"]
CronJob
- 주기적으로 특정 동장을 수행하고 종료하는 작업을 정의한다.
- 리눅스의 크론 스케쥴링 방법을 그대로 사용한다.
- 내부적으로 Pod을 생성하여 작업을 수행한다.
- 주기적인 데이터 백업, 데이터 점검, 알림 전송 등에 사용할 수 있다.
- CronJob Scheduling → Job → Pod
# Hello $(date) 1분마다 출력하는 크론잡
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello-every-minute
spec:
schedule: "*/1 * * * *" # 1분마다 실행
successfulJobsHistoryLimit: 5 # 성공 기록은 최대 5개 남김
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: hello
image: ubuntu:focal
command: ["sh", "-c", "echo Hello $(date)!"]
API 리소스 - DeamonSet
- 각 노드마다 꼭 실행되어야 하는 워크로드를 설정한다.
- 클러스터 상의 모든 노드에 동일한 파드를 하나씩 생성한다.
- 로그 수집, 메트릭 수집, 네트워크 구성 등의 목적으로 많이 사용한다.
- 로그 수집: filebeat, fluentbit 등
- 메트릭 수집: node-exporter, metricbeat, telegraf 등
- 네트워크 구성: kube-proxy, calico 등
- Deployment와 마찬가지로 Label Selector 기반으로 동작한다.
- nodeSelector, Affinity, Toleration 등을 통해 실행되어야 할 노드 목록을 필터링 가능하다.
API 리소스 - Ingress
- 외부 요청을 받아 L7(어플리케이션 레이어)에서 어떻게 처리할 것인지를 결정한다. (Service는 L4)
- 라우팅 기능을 수행한다. (Host 단위, Path 단위로 라우팅 가능)
- SSL/TLS 통신 암호화를 처리한다. (각 연결 Host에 대한 인증서 적용)
인그레스 컨트롤러
- 쿠버네티스 클러스터는 기본적으로 Ingress API 리소스를 다루는 Ingress Controller를 제공하지 않는다.
- 쿠버네티스는 Ingress API 리소스에 대한 스펙만 제공한다. = 사용자가 직접 원하는 Ingress Controller 설치
- 대표적인 Ingress Controller
- NGINX Ingress Controller
- Kong Ingress Controller
- AWS Load Balancer Controller
- Google Load Balancer Controller
- 인그레스 클래스 Ingress Class : 하나의 클러스터에서 여러 Ingress Controller를 사용할 수 있도록 하는 API 리소스
- Ingress Class = Ingress Controller + Configuration
- 인그레스 Ingress
- 라우팅 규칙 및 TLS 설정을 정의
- 하나의 Ingress Class와 연결
Pod의 배치
스케쥴러는 평가 룰을 기반으로 Pod를 어느 Node로 배치될지 결정한다.
참고: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
nodeName
- 노드의 이름을 기반으로 Pod가 배치될 Node를 결정한다.
- 매니페스트의 재활용성이 떨어지며, Pod와 Node가 강결합되기 때문에 추천하지 않는다.
nodeSelector
- Node도 쿠버네티스 API 오브젝트이기 때문에 Labels를 가진다.
- Node에 설정된 Label을 기반으로 Pod가 배치될 Node를 결정한다.
- Node Label 관리
- Node를 새로 구성할 때 kubelet 옵션을 통해서 기본 Labels 설정도 가능하다.
- kubectl을 통해서 이미 띄워져 있는 Node의 Label 관리가 가능하다.
nodeAffinity
- 선호하는 Node를 설정한다.
- nodeSelector보다 확장된 Label Selector 기능
- matchExpressions 사용이 가능하다. (In, NotIn, Exists, DoesNotExistm Gt, Lt 등)
- 여러 유즈케이스에 활용 가능한 다양한 옵션을 제공한다.
- 반드시 충족해야 하는 조건 (Hard) : requiredDuringSchedulingIgnoredDuringExecution
- 선호하는 조건 (Soft) : preferredDuringSchedulingIgnoredDuringExecution
(IgnoredDuringExecution: 실행 중인 워크로드에 대해서는 해당 규칙 무시)
PodAffinity
- 선호하는 (실행 중인) Pod를 설정한다.
- 토폴로지 키 (Topology Key)
- Node에 설정된 Label에 대해서 Label Selector를 수행할 Node의 범위를 결정한다.
- 노드 단위, 존 단위 리전 단위
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity-preferred
spec:
replicas: 4
selector:
matchLabels:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: nginx
image: nginxdemos/hello:plain-text
ports:
- name: http
containerPort: 80
protocol: TCP
affinity: # mysql 레이블을 가지는 Pod와 동일한 노드에 Pod를 띄우고 싶다.
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- mysql
topologyKey: kubernetes.io/hostname
PodAntiAffinity
- 선호하지 않는 (실행 중인) Pod를 설정한다.
Taint
- Node에 Taint를 설정하여 임의의 Pod가 할당되는 것을 방지한다.
- Node를 새로 구성할 때 kubelet 옵션을 통해서 기본 Taint 설정을 할 수 있다.
- kubectl을 통해 Node의 Taint를 관리할 수 있다.
- Label/Annotation과 비슷하지만 추가적으로 Effect 파라미터를 가진다.
- Key=Value:Effect
- Effect: Taint가 Node에 설정될 때 적용될 효과
- NoSchedule: Pod를 스케쥴링 하지 않는다 (=이미 실행 중인 Pod는 건드리지 않겠다)
- NoExecute: Pod의 실행을 허용하지 않는다 (=이미 실행 중인 Pod도 쫓아낸다)
- PreferNoSchedule: Pod의 스케쥴링을 선호하지 않는다
Toleration
- Pod에 특정 Taint를 용인할 수 있는 Toloeration을 설정하여 해당 Node에 할당 가능하도록 한다.
Kustomize로 매니페스트 관리하기
Kustomize는 쿠버네티스 매니페스트 파일을 효율적으로 관리하기 위한 오픈소스 도구이다.
kubectl 버전 1.14 이후로 kustomize를 내장하고 있다. (최신 버전은 아닐 수 있으니 확인 필요)
원본 yaml 파일은 보존하고, 목적에 따른 변경본(patch, overlay)을 만들어 사용할 수 있다.
Kustomize는 'kustomization.yaml'을 사용한다.
- Base Manifests: Kustomization에 의해 참조되는 기본 설정으로 구성된 매니페스트 묶음
- Overlay Manifests: Base Manifests에 변형을 가하기 위해 사용되는 매니페스트 묶음 - 이 또한 다른 누군가의 Base가 될 수 있다.
# 기본적인 kustomization.yaml 파일 생성
kustomize create
# 현재 디렉토리의 kustomization.yaml 파일을 해석하여 매니페스트 출력
kustomize build .
# kustomization.yaml 파일 내용을 클러스터에 적용
kustomize build . | kubectl apply -f -
# kustomization.yaml 파일 내용을 클러스터에서 삭제
kustomize build . | kubectl delete -f -
'DevOps' 카테고리의 다른 글
Docker 기초 끝내기 🙉 (0) | 2023.03.01 |
---|---|
[간단실습] AWS 네트워크 설정하기 (VPC, Subnet 등) (0) | 2022.08.06 |
AWS VPC 기본 개념 (0) | 2022.07.18 |
네트워킹의 기본 - IP 주소 (0) | 2022.07.16 |
AWS 계정 MFA 활성화하여 보안 강화하기 (0) | 2022.07.07 |