쿠버네티스를 이해하기 위해 클러스터, 노드 수준의 큰 그림부터 시작해서 pod, service, ingress 등의 개념까지 짧게 정리했다. 돌아가는 수준으로 이해하기는 쉽지만 역시 잘 쓰기 위해서는 알야할 개념들이 훠어어얼씬 많다.
마스터 / 노드
쿠버네티스의 각종 개념들은 결국 노드에서 실행되는데 역할에 따라 마스터와 노드로 나뉜다.
마스터 (control plane)
쿠버네티스 클러스터를 관리해서 요구조건에 맞는 상태가 유지되도록 한다.
kube-apiserver
- 마스터의 프론트엔드로 모든 요청을 받아 해석하고 아래 컴포넌트들과 유기적으로 동작한다.
- 예를 들어 pod 생성 요청이 온 경우
- 요청 문법을 검사한다.
- etcd에서 노드 상태를 가져다가 scheduler에게 어떤 노드에 띄울지 물어본다.
- 어디에 띄울지 정보를 받은 후 해당 노드의 kubelet에게 실행 명령을 전달한다.
- 해당 노드의 kubelet가 컨테이너 실행을 명령한다.
etcd
- 워커노드 kubelet에서 보낸 노드 상태를 고가용성을 보장하며 key-value로 저장한다.
kube-controller-manager
- 노드, pod, service 등을 모니터링하여 요구된 명세와 현재 상태가 같도록 유지시킨다.
kube-scheduler
- 노드 상태(hw/sw, 리소스, 요구 사항 등)를 확인해서 pod를 어떤 노드에 띄울지 선택한다.
노드
kubelet
- 노드에 배포되는 agent
- pod에 container가 동작하도록 관리한다.
- cAdvisor가 탑재되어 노드 리소스를 수집하고, 이 정보를 마스터 노드 apiserver에 보낸다.
kube-proxy
- 아래 설명할 service를 구현한 프록시로 노드로 들어오는 트래픽을 적절한 컨테이너로 라우팅한다.
container runtime
- pod에 배포된 컨테이너를 실행하는 런타임
- 도커 외에 rkt, hyper container 등 런타임이 있다.
이렇게 마스터 / 노드 장비가 준비되었으면 서비스를 돌리기 위한 최소한의 단위인 pod을 만들어서 쿠버네티스에 올릴 준비가 완료됐다.
Pod
nginx-pod.yaml 파일 생성
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
run: nginx-pod
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
실행
$ kubectl create -f nginx-pod.yaml
pod 내부는 container
여러 개로 구성된다.
- 각 container는 localhost로 통신 가능하다.
- pod 기동 시 디폴트로 생성되는 볼륨은 재기동시 사라진다. persistent volume으로 pod 생명주기를 넘어 유지시킬 수 있다.
- 디스크 볼륨 공유하기 때문에 사이드카 패턴으로 로그 수집, 모니터링 할 수 있다.
kind: Pod
으로 단일 pod을 생성할 수 있지만 고가용성과 scale out을 위해 여러 pod을 안정적으로 제공하기에는 부족하다. 여러 pod 묶음을 안정적으로 유지시키는 컨트롤러 개념이 등장한다.
컨트롤러
replication controller
- 지정된 숫자로 pod이 떠있도록 관찰하고 유지한다.
- replica / selector / pod template을 명시한다.
- template에서 pod 배포 명세와 같은 템플릿을 작성한다.
replica set
- apiVersion이 v1에서 apps/v1으로 변경된다.
- replication controller에 풍부한 selector 기능이 추가된다.
- matchLabels(기존 selector)
- matchExpressions:
In
,NotIn
,Exists
,DoNotExist
등의 operator를 사용할 수 있다.
deployment
- repelica set에 배포 전략이 추가된다.
- 새로운 replica set을 생성하고 두 replica set의 replica 개수를 조절하면서 배포한다.
kubectl rollout undo revision
을 지정하면 롤백된다.- nginx-deployment.yaml 파일 생성
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
실행
$ kubectl create -f nginx-deployment.yaml
daemon set
- 모든 노드 또는 라벨 지정한 노드에 pod를 하나씩 띄우는 컨트롤러
- 로그 수집, 모니터링 등에 쓴다.
job
- 한번 실행되고 죽는 워크로드
- 실패하면 재시도할지, 몇 번 반복할지, 병렬로 실행할지 옵션
cron job
- cron + job
- concurrencyPolicy로 동시에 실행 가능 여부를 설정한다.
stateful set
- pod 이름 보장한다.
이렇게 컨트롤러를 통해 여러 개의 pod가 떠있도록 보장했다. 이제 이렇게 보장된 pod들에 요청하기 위해 단일 진입점을 만들어야 한다.
Service
nginx-service.yaml 파일 생성
apiVersion: v1
kind: Service
apiVersion: v1
metadata:
name: nginx-service
spec:
selector:
app: nginx-pod
ports:
- protocol: TCP
port: 80
targetPort: 80
실행
$ kubectl create -f nginx-service.yaml
service는 여러 pod을 묶어서 앞단에 로드밸런서를 배치하여 하나의 virtual ip를 제공한다. pod가 추가/제거되거나 재기동되어 ip가 변경되어도 service가 pod ip 정보를 수집해서 갱신한다. 타겟이 되는 pod는 label selector로 지정한다.
service type
cluster ip
- 클러스터 내부에서만 사용 가능한 내부 ip를 생성한다.
node port
- clusterIp 기능을 포함한다.
- 모든 노드에 port(30000-32768)가 추가되어 노드로 직접 접근 가능하고, 이를 다시 로드밸런싱 한다.
load balancer
- node port 기능을 포함한다.
- provider에서 제공하는 l4 로드밸런서가 생성되고 외부에서 접근 가능한 external ip 제공한다.
external name
- 위 타입과 전혀 다른 기능이다.
- 내부에서 외부로 나갈 수 있는 dns 기능으로 servicename 도메인을 원하는 도메인으로 포워딩한다.
headless service
- 실제로 type으로 명시되는 건 아니고
clusterIp: None
으로 생성된 서비스를 칭한다. - pod endpoint들을 모아서 coreDns까지 저장한다.
이렇게 단일 진입점까지 만들었으니 안정적으로 서비스를 돌릴 수 있다. 그렇지만 애플리케이션이 커져서 컴포넌트가 늘어가면 이에 따라 서비스도 늘어나게 된다. 여러 서비스 앞단에 로드밸런서를 두어 이를 해결할 수 있다. 이를 쿠버네티스에서는 ingress라고 부른다.
Ingress
l7 로드밸런싱으로 url path 기반으로 라우팅 한다. path별로 service name을 지정하고, 기본값으로 연결할 수도 있다.
여러 서비스들과 연결되는 로드 밸런스인 만큼 각 서비스 별로 health check 가 필요한데, 이 health check api는 pod 정의하는 곳에서 containers[].readinessProbe.httpGet
으로 정의한다.
nginx-ingress.yaml 파일 생성
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
defaultBackend:
service:
name: nginx-service
port:
number: 80
실행
$ kubectl create -f nginx-ingress.yaml
기본으로 연결된 서비스밖에 정의하지 않았지만 서비스별로 prefix를 나누어 아래와 같이 하나의 ingress controller로 전체 서비스를 운영할 수 있다.

댓글