파드(Pod)는 쿠버네티스의 배포 가능한 가장 작은 컴퓨팅 유닛이다.
파드는 그 자체로 하나의 논리적인 호스트이다.
파드는 다음을 포함할 수 있다.
마치 도커 컨테이너처럼 파드 내에서 다음 요소들은 격리된다.
쿠버네티스에서는 "워크로드 리소스"를 만들기 위해 YAML 파일과 같은 리소스 정의 파일을 사용하는 것이 보통이다.
워크로드란?
쿠버네티스에서는 "쿠버네티스 상에서 작동되는 애플리케이션"을 의미하며, 클라우드 분야에서는 "어떤 애플리케이션을 실행할 때 필요한 IT 리소스의 집합"이라는 의미로 통용된다.
마찬가지로 파드를 생성하기 위해 파드를 정의할 때 다음과 같은 형태의 YAML 파일을 사용할 수 있다.
다음 YAML을 따라서 만들고 적당한 디렉토리에 simple-pod.yaml
로 저장하자.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
위 파드 정의 파일을 실제로 만들기 위해서는 다음 명령을 사용한다.
kubectl apply -f simple-pod.yaml
위 명령을 통해 pod/niginx created
라는 결과가 출력되었다면 즉시 명령어 kubectl get pods
를 통해 상태를 확인해보자.
STATUS를 통해 컨테이너가 만들어지고(ContainerCreating), 컨테이너가 실행중인(Running) 상태를 확인할 수 있다.
리소스를 삭제하려면 아래와 같은 명령어를 사용한다.
kubectl delete -f simple-pod.yaml
쿠버네티스에서 Deployment는 한글로 번역하지 않는다.
흔히 우리는 배포라고 하면 서비스의 노출을 떠올리지만, 쿠버네티스의 Deployment는 서비스 노출의 의미가 아니다.
쿠버네티스에서의 Deployment는 파드의 교체/배치(placement)와 관련된 명세다.
쿠버네티스에서는 사실 직접 사용자가 개별 파드를 만들 일이 그리 많지 않다.
왜냐하면 파드는 일시적이고, 언제나 삭제될 수 있음을 감안하고 만들기 때문이다.
예를 들어, 파드가 실행되는 공간인 노드가 만일 실패하는 경우, 그 안에서 실행되는 파드 역시 사용할 수 없게 된다.
사실 컨테이너를 수동으로 만들고 관리하는 것은, 그냥 도커만 단독으로 사용해도 충분히 할 수 있는 일이다.
쿠버네티스의 핵심은 컨테이너를 오케스트레이션하는 것으로, 파드 장애시 자동 복구하거나, 복제하거나 하는 등의 일을 자동으로 처리하는 데에 있다.
AWS로 따지면 ECS(Elastic Container Service)가 하는 일과 비슷하다.
이 둘은, 컨테이너의 로드 밸런싱과 오토 스케일링과 같은 일을 담당한다.
결론적으로 파드는 디플로이먼트, 스테이트풀셋, 데몬셋을 이용해 관리하는 것이 바람직하다.
해당 워크로드 리소스는 파드 템플릿을 항상 포함하고 있다.
아래는 디플로이먼트의 예시이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
# 여기서부터 파드 템플릿이다
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
# 여기까지 파드 템플릿이다.
디플로이먼트는 파드를 업데이트하기 위한 선언적 명세이다.
디플로이먼트 리소스를 통해 다음을 할 수 있다.
애플리케이션의 여러 복제본이 존재할 경우, 이 각각의 복제본을 새 버전으로 업데이트하는 방법으로 다음과 같은 배포 전략이 있음을 배웠다.
디플로이먼트는 파드의 복제본을 자동으로 업데이트하게 해주는 명세이므로 쿠버네티스가 지원하는 배포 전략으로는 재생성과 롤링 배포 방식을 선택할 수 있다.
위에서 만든 디플로이먼트 정의 파일을 적용하기 위해서는 다음 명령을 사용한다.
kubectl apply -f <디플로이먼트_파일>
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
kubectl apply -f deployment.yaml
kubectl describe deployment nginx-deployment
출력은 다음과 유사하다.
Name: nginx-deployment
Namespace: default
CreationTimestamp: Mon, 13 Feb 2023 08:55:48 +0900
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=nginx
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.14.2
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-85996f8dbd (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 23s deployment-controller Scaled up replica set nginx-deployment-85996f8dbd to 2
kubectl get pods -l app=nginx
출력은 다음과 유사하다.
NAME READY STATUS RESTARTS AGE
nginx-deployment-85996f8dbd-nzm46 1/1 Running 0 15m
nginx-deployment-85996f8dbd-thb9l 1/1 Running 0 15m
kubectl describe pod <pod-name>
kubectl apply -f deployment.yaml
NAME READY STATUS RESTARTS AGE
nginx-deployment-66f8758855-87qng 1/1 Running 0 9s
nginx-deployment-66f8758855-jmjxr 1/1 Running 0 24s
이전의 pod들은 삭제되고 새 파드 2개가 생성됐다.
kubectl apply -f deployment.yaml
kubectl get pods -l app=nginx
출력은 다음과 유사하다.
NAME READY STATUS RESTARTS AGE
nginx-deployment-85996f8dbd-22c27 1/1 Running 0 33s
nginx-deployment-85996f8dbd-jmf9b 1/1 Running 0 28s
nginx-deployment-85996f8dbd-tzcwj 1/1 Running 0 28s
nginx-deployment-85996f8dbd-wslpt 1/1 Running 0 33s
kubectl delete deployment nginx-deployment
클러스터 안에 파드는 각각 고유의 IP를 가지고 있지만, 직접 우리가 내부망에 접속할 수 있는 것은 아니다. 그럼 어떻게 파드 안에 서비스가 외부로 노출될 수 있을까?
서비스, 로드밸런싱, 네트워킹 공식문서의 설명을 인용해보자.
- 서비스 리소스를 사용하면 파드에서 실행 중인 애플리케이션을 클러스터 외부에서 접근할 수 있다.
- 또한 서비스를 사용하여 클러스터 내부에서 사용할 수 있는 서비스만 게시할 수 있다.
쿠버네티스에서 서비스는 파드의 집합에 접근할 수 있는 정책을 정의하는 추상적 개념이다.
서비스 리소스가 정의된 YAML 파일에 selector라는 것을 이용해 서비스할 대상 타겟을 설정할 수 있다.
앞서 디플로이먼트를 통해 파드의 복제본을 원하는 개수만큼 실행시킬 수 있다고 언급했다.
서비스 리소스는 이러한 파드 집합에 접근할 수 있게 하며, 파드가 교체되거나, 어떤 특정 파드에 문제가 생긴 경우에도 사용 가능한 파드를 찾아 알아서 접속할 수 있게 돕는다.
다음의 서비스 리소스의 예제이다.
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: default
spec:
selector:
app: nginx # 배포하려는 파드를 지정합니다. 당연히 파드가 이미 실행중이어야 합니다.
type: LoadBalancer
ports:
- name: nginx
protocol: TCP
port: 80
targetPort: 80
우리에게 가장 익숙한 LoadBalancer로 서비스를 만들고, 백엔드에 cozserver라는 이름을 가진 파드 집합에 연결되도록 지정했다.
적용된 결과를 확인해자.
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-minikube NodePort 10.106.160.84 <none> 8080:30951/TCP 164m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 170m
nginx LoadBalancer 10.108.172.93 <pending> 80:30523/TCP 25s
minikube에서는 EXTERNAL-IP가 pending 상태로 진전이 되지 않는다
minikube tunnel
명령을 이용하면 EXTERNAL-IP가 127.0.0.1로 설정되며, 그때부터 http://localhost로 접속이 가능해진다.
접속이 원활하지 않을 경우, tunnel을 중지하고 해당 명령을 다시 시도해보자.
minikube tunnel
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-minikube NodePort 10.106.160.84 <none> 8080:30951/TCP 168m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 173m
nginx LoadBalancer 10.108.172.93 127.0.0.1 80:30523/TCP 4m10s