Pod
- 가장 작은 배포 단위
- 전체 클러스터에서 고유한 IP 할당
- 1~N 개의 Container 를 포함
- 일반적으로 파드는 애플리케이션을 실행하는 컨테이너와 1:1 관계를 갖는다.
- 하나의 파드는 여러 컨테이너를 포함할 수 있으며, 동일한 종류의 컨테이너를 복제하기보다는 헬퍼(보조) 컨테이너를 함께 두는 경우가 많다.
- host 폴더 공유, localhost 네트워크 공유
- 이는 앱 컨테이너와 헬퍼 컨테이너가 localhost를 통해 통신할 수 있음을 의미한다.
How to deploy pods
kubectl run nginx --image nginx
kubectl get pods
- 첫 번째 명령은 Kubernetes가 구성된 레지스트리(공개 Docker Hub 또는 private registries 등)에서 이미지를 가져온다.
YAML in K8s
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx- Kubernetes는 오브젝트 생성을 위해 YAML 파일을 입력으로 사용하며, 모든 오브젝트는 4개의 최상위 필드를 갖는다.
apiVersion- 오브젝트 생성을 위해 사용하는 Kubernetes API 버전
- e.g. v1, apps/v1 등
kind- 생성할 오브젝트의 종류
- e.g. Pod, Service, ReplicaSet, Deployment 등
metadata- 오브젝트를 설명하는 메타데이터(사전 형태)
- e.g. name, labels 등
spec- 생성할 오브젝트의 구체적인 정의
- 위 예시에서
containers섹션은 하이픈(-)으로 나열되는 리스트다.
아래 명령으로 파드를 생성할 수 있다:
kubectl run nginx --image=nginx
또는 YAML 파일을 사용한다:
kubectl create -f pod-definition.yaml
kubectl get pods
kubectl describe pod myapp-pod
kubectl run redis --image=redis --dry-run=client -o yaml > redis.yaml
kubectl 명령어로 template yaml file 을 생성하고 싶은 경우 --dry-run 과 -o yaml 옵션을 통해 yaml file 을 생성할 수 있다.
ReplicaSet
- Replication Controller와 ReplicaSet은 유사하지만 다르다. ReplicaSet은 기존의 Replication Controller를 대체하는 더 최신 기술이다.
- ReplicaSet은 설정에 따라 파드를 생성/삭제하여 클러스터 내 원하는 개수의 파드를 유지함으로써 고가용성을 제공한다.
- 또한 기존 또는 추가 노드에 파드를 증설하여 로드밸런싱과 스케일링을 지원하며, ReplicaSet은 클러스터의 여러 노드에 걸쳐 동작한다.
Creating Replication Controller with YAML
apiVersion: v1
kind: ReplicationController
metadata:
name: myapp-rc
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3template- 파드 정의의 템플릿.
replicas- 템플릿으로 생성할 파드의 복제 수.
아래 명령으로 생성하고 결과를 확인한다.
kubectl create -f rc-definition.yaml
kubectl get replicationcontroller
kubectl get pods
Creating ReplicaSet with YAML
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-replicaset
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-endtemplate- 파드 정의의 템플릿.
replicas- 템플릿으로 생성할 파드의 복제 수.
selector- ReplicaSet이 관리할 파드를 식별하기 위한 선택자이며, 이를 통해 지정된 파드들을 모니터링하고 원하는 복제 수를 유지한다.
아래 명령으로 생성하고 결과를 확인한다.
kubectl create -f replicaset-definition.yaml
kubectl get replicaset
kubectl get pods
파일을 수정해 스케일링:
kubectl replace -f replicaset-definition.yaml
명령으로 스케일링:
kubectl scale --replicas=6 -f replicaset-definition.yaml
또는
kubectl scale --replicas=6 replicaset myapp-replicaset
- 이 두 명령은 정의 파일 자체를 변경하지 않는다.
Deployment

- 내부적으로 ReplicaSet 을 이용하여 배포 버전을 관리
- Deployment는 롤링 업데이트, 되돌리기(undo), 일시 중지/재개 등을 통해 하위 인스턴스(파드/ReplicaSet)의 업그레이드를 무중단으로 수행할 수 있게 해준다.
Creating Deployment with YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-replicaset
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-end- Deployment 정의는 ReplicaSet 정의와 사실상 동일하다.
아래 명령으로 생성하고 생성된 오브젝트를 확인한다:
kubectl create -f deployment-definition.yaml
kubectl get deployments
kubectl get replicaset
kubectl get pods
또는
kubectl get all
Service
NodePort
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- port: 80
- targetPort: 80
- nodePort: 30008
selector:
app: myapp
type: front-end- Node(host) 에 노출되어 외부에서 접근 가능한 서비스
- 모든 Node 에 동일한 포트로 생성
- targetPort 는 target pod 의 port 를 지정, 지정하지 않을 경우 port 와 같은 값을 가짐
- port 는 Service Object 의 port 를 지정 (required)
- nodePort 는 30,000 ~ 32,767 까지의 범위 안에 속해야 하며 지정하지 않을 경우 자동으로 할당됨
- selector 를 통해 target pod 를 지정
- 하나의 Node 에 같은 종류의 Pod 가 여러개 있을 경우 Service 가 알아서 selector 에 해당하는 Pod 를 찾고 부하를 분산하여 각각 Pod 에 Random 하게 보내줌.
- 여러 Node 에 Pod 가 배포되어 있을 경우에도 Service 는 모든 Node 에 걸쳐 적용되어 아무 Node 에 요청을 보내도 알아서 아무 Node 에 배포된 Pod 에 트래픽을 보냄.
ClusterIP
apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP # default 값
selector:
app: myapp
type: back-end # label 기준으로 트래픽을 보낼 pod 결정
ports:
- port: 8080 # (required) 클라이언트가 Service 에 요청을 보낼 port
- targetPort: 80 # 실제 pod 에서 리스닝하는 port, 지정하지 않으면 port 값을 따름- 클러스터 내부에서 사용하는 프록시
- Pod 은 동적이지만 서비스는 고유 IP 를 가짐
- 클러스터 내부에서 서비스 연결은 DNS 를 이용
- 가령 3-tier-architecture 에서 backend app 에 요청을 보내고 싶을 때 backend 를 담당하는 여러 Pod 가 cluster 전체에 퍼져있으니 ClusterIP 를 통해 원하는 layer 를 지정
kubectl run httpd --image=httpd --port=80 --expose
- kubectl run 으로 Pod 를 생성할 때 —expose 옵션을 주면 Pod 에 연결되는 ClusterIP 를 자동으로 생성해준다. ClusterIP 와 연결되어야 하기 때문에 —port 옵션이 필수적으로 포함되어야 한다.
Service 흐름
클러스터 내부에서 Service 를 호출할 때:
- 클라이언트가 Service 의
ClusterIP:Port로 요청을 보낸다. - kube-proxy 가 이 요청을 가로챈다.
- EndpointSlice 를 참조해 적절한 Pod IP 로 라우팅한다.
- Pod 는 targetPort 에서 요청을 받는다.
위 ClusterIP 예시로 들면,
back-end:8080으로 들어온 요청이 label 이app=myapp,type=back-end인 Pod 의 80 포트로 전달된다.
LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer
ports:
- targetPort: 80
- port: 80
- nodePort: 30008- 하나의 IP 주소를 외부에 노출하여 지정된 Pod 에 요청이 도달할 수 있도록 도와줌
- NodePort 대신 AWS, Azure, GCP 등에서 제공하는 Native Load Balancer 로 부하를 분산하여 보내는 역할을 수행함
Namespaces
K8s Cluster 를 구축하게되면 기본적으로 Default, kube-system, kube-public Namespace 들이 자동으로 생성된다. 사용자가 생성한 Pod, Deployment, Service 등 K8s Object 들은 기본적으로 Default Namespace 에 속하게 된다.
kubectl get pods --namespace=kube-system
kube-system Namespace 의 경우, coredns, etcd-master, kube-apiserver-master, kube-controller-manager-master 등 K8s 의 기본적인 Component 들을 확인할 수 있다.
kubectl create -f pod-definition.yml --namespace=dev
K8s Object 를 특정 Namespace 에 배포하고 싶을 경우엔 옵션으로 namespace 를 지정해주거나,
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
namespace: dev
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx위 처럼 metadata 필드에 namespace 를 추가해주면된다.
apiVersion: v1
kind: Namespace
metadata:
name: devNamespace 를 생성할 땐 위처럼 Namespace yaml 을 생성한 뒤,
kubectl create -f namespace-dev.yml
위 명령어로 생성하거나,
kubectl create namespace dev
yaml 파일 없이 명령어로 생성할 수 있다.
kubectl config set-context $(kubectl config current-context) --namespace=dev
위 명령어를 통해 cli 작업 위치를 특정 Namespace 로 이동시킬 수도 있다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: dev
spec:
hard:
pods: "10"
requests.cpu: "4"
requests.memory: 5Gi
limits.cpu: "10"
limits.memory: 10Gi특정 Namespace 에 하드웨어 리소스를 할당할 때엔 ResourceQuota yaml 을 생성한 뒤,
kubectl create -f compute-quota.yml
생성하여 자원을 할당해줄 수 있다.
Imperative vs Declarative
IaC 에선 명령형과 선언형 방식으로 인프라를 구축할 수 있는데,
kubectl run --image=nginx nginx
kubectl create deployment --image=nginx nginx
kubectl expose deployment nginx --port 80
위 같은 명령어들을 통해 Object 를 생성하고,
kubectl edit deployment nginx
kubectl scale deployment nginx --replicas=5
kubectl set image deployment nginx nginx=nginx:1.18
생성된 Object 를 수정할 수 있다. 이는 명령형 방식으로 K8s 를 구축하는 방법이다.
kubectl create -f nginx.yaml
kubectl edit deployment nginx
K8s configuration file 을 통해 Object 를 생성한 경우 선언형 방식으로 구축했다고 생각할 수 있지만, edit 명령어를 통해 접근한 file 은 K8s memory 에 동적으로 생성된 yaml 인 것을 확인할 수 있다. 때문에 edit 으로 설정을 바꾼다고 기존에 작성한 nginx.yaml 은 수정되지 않기 때문에 추후에 replace 등을 할 경우 예상하지 못한 변경이 생길 수 있다. 이를 방지하기 위해선 edit 대신 configuration file 자체를 변경한 후 replace 를 실행하는 것이 바람직하다.
kubectl apply -f nginx.yaml
K8s 는 apply 를 통해 선언형 방식으로 Object 를 제어할 수 있다. apply 는 이미 만들어진 Object 를 파악하며 부분적으로 설정이 수정된 경우 해당 부분만 적용하는 기능 역시 지원한다.
# Live object configuration
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion": "v1", ...}
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
status:
conditions:
- lastProbeTime: null
...kubectl apply 를 실행하면 K8s 는 사용자가 작성한 yaml file 과 K8s memory 에 저장된 Live object configuration 과 kubectl.kubernetes.io/last-applied-configuration 필드를 비교하여 변경된 사항만 부분적으로 확인하고 적용이 가능하다.