Pod


Pod 란

# pod-definition.yaml
apiVersion: v1 # 오브젝트 생성을 위해 사용하는 Kubernetes API 버전
kind: Pod      # 생성할 오브젝트의 종류
metadata:      # 오브젝트를 설명하는 메타데이터 (dict)
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:          # 생성할 오브젝트의 구체적인 정의
  containers:
    - name: nginx-container
      image: nginx

Pod 는 하나 이상의 Container 를 실행하기 위한 쿠버네티스의 최소 단위이다.

  • 전체 클러스터에서 고유한 IP 를 할당받는다.
  • 일반적으로 Pod 는 애플리케이션을 실행하는 Container 와 1:1 관계를 갖는다.
  • 하나의 Pod 는 동일한 종류의 Container 를 복제하기보다는 헬퍼(보조) Container 를 함께 두는 경우가 많다.
  • Pod 내 Container 간 host 폴더, localhost 네트워크를 공유한다. 즉, Container 간 localhost 통신이 가능하다.

Pod 생성

kubectl create -f pod-definition.yaml

YAML 파일로 부터 Pod 를 생성하거나,

kubectl run nginx \
  --image=nginx \                       # 쿠버네티스에 구성된 레지스트리(공개 Docker Hub 또는 private registries 등)에서 이미지를 가져옴
  --dry-run=client -o yaml > redis.yaml # 생성 대신 YAML 파일로 저장

kubectl run 명령어로 Pod 를 실행할 수 있다.

Pod 와 Container 는 결국 Linux Process

  • 컨테이너/파드가 결국 namespace, cgroup 등으로 격리된 리눅스 프로세스라는 일반 개념은 Containers are just Processes 참조
  • 파드는 하나 이상의 컨테이너(프로세스 집합)를 묶은 논리적 단위일 뿐, 실제로는 같은 namespace/network/storage 를 공유하는 리눅스 프로세스들의 집합이다
  • 즉, 쿠버네티스에서 컨테이너/파드를 생성하면 결국 리눅스 커널이 namespace 와 cgroup 을 활용해 프로세스를 격리하고 관리하는 것에 불과하다

Static Pod

Static Pod 은 kube-apiserverkube-scheduler 가 아닌 kubelet 이 생성하고 관리하는 Pod 다. kubeletNode/etc/kubernetes/manifests 디렉터리에 정의된 YAML 파일을 주기적으로 확인하고 해당 Node 에서 실행되도록 보장한다. kubeadm 이 Master Node 를 구성할 때 Static Pod 형식으로 controller-manager, kube-apiserver, etcd 등을 생성한다. 때문에 kubectl get pods -n kube-system 으로 Pod 를 조회했을 때 Master Node 의 컴포넌트들이 Pod 로 실행되고 있는 것을 확인할 수 있는 것이다.

Namespace


Namespace 란

# namespace-dev.yml
apiVersion: v1
kind: Namespace
metadata:
  name: dev

쿠버네티스를 구축하면 기본적으로 default, kube-system 등의 namespace 가 자동으로 생성된다. 사용자가 생성한 리소스는 기본적으로 default namespace 에 속하게 된다.

kubectl get pods --namespace=kube-system

kube-system 의 경우, coredns, etcd-master, kube-apiserver-master, kube-controller-manager-master 등 쿠버네티스 기본 Component 들을 확인할 수 있다.

Namespace 생성

kubectl create -f namespace-dev.yml # YAML 파일로 생성하거나,
kubectl create namespace dev        # 명령어로 생성

특정 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 를 추가해서 리소스를 생성하거나,

kubectl create -f pod-definition.yml --namespace=dev

명령어에 namespace 를 명시하여 리소스를 생성할 수 있다.

ReplicaSet


ReplicaSet 이란

# replicaset-definition.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-replicaset
  labels:
    app: myapp
    type: front-end
spec:
  replicas: 3 # Pod 복제 수
  selector: # ReplicaSet 이 관리할 Pod 식별자
    matchLabels:
      type: front-end
  template: # ReplicaSet 이 관리할 Pod 정의
    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx

ReplicaSet 은 특정 Pod 템플릿을 기준으로 항상 N개의 Pod 를 유지하도록 보장하는 리소스이다.

  • Pod 를 생성/삭제하여 클러스터 내 원하는 개수의 Pod 를 유지함으로써 고가용성을 제공한다.
  • 기존 또는 추가 노드에 Pod 를 증설하여 로드밸런싱과 스케일링을 지원한다.
  • ReplicaSet 은 클러스터의 여러 노드에 걸쳐 동작한다.
  • Self-Healing Applications = 쿠버네티스는 ReplicaSet 를 통해 Pod 의 작동을 보장한다.

ReplicaSet 생성

kubectl create -f replicaset-definition.yaml

ReplicaSet 스케일링

kubectl replace -f replicaset-definition.yaml

YAML 파일에서 replica 개수를 수정해 직접 kubectl replace 하거나,

# 이 두 명령은 정의 파일 자체를 변경하지 않는다.
kubectl scale --replicas=6 -f replicaset-definition.yaml
kubectl scale --replicas=6 replicaset myapp-replicaset

kubectl scale 명령으로 직접 스케일할 수 있다.

Deployment


Deployment 란

# deployment-definition.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-replicaset
  labels:
    app: myapp
    type: front-end
spec:
  replicas: 3 # default 값은 1
  selector: # (required)
    matchLabels:
      type: front-end
  template: # (required)
    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx

DeploymentReplicaSet 을 관리하는 상위 리소스이다. Deployment 정의는 ReplicaSet 정의와 사실상 동일하다.

  • 내부적으로 ReplicaSet 을 이용하여 배포 버전을 관리한다.
  • 이미지 변경 시 자동으로 새 ReplicaSet 을 생성하고 롤링 업데이트를 수행한다.
  • 실패 시 롤백도 자동으로 지원한다.

Deployment 생성

kubectl create -f deployment-definition.yaml   # YAML 파일로 생성하거나,
kubectl create deployment --image=nginx nginx  # 직접 명령어로 생성

Deployment 스케일링

kubectl edit deployment nginx                        # Live YAML 파일에서 수정하거나,
kubectl scale deployment nginx --replicas=5          # kubectl scale 명령어로 스케일

Deployment 버전업

kubectl set image deployment nginx nginx=nginx:1.18

Rolling Updates and Rollbacks

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
  labels:
    app: nginx
spec:
  template:
    metadata:
      name: myap-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx:1.7.1
  replicas: 3
  selector:
    matchLabels:
      type: front-end
kubectl create -f deployment-definition.yml
kubectl get deployments

위와 같이 nginx:1.7.1 을 호스팅하는 Deployment Object 가 있다고 해보자.

kubectl apply -f deployment-definiton.yml
kubectl set image deployment/myapp-deployment nginx=nginx:1.9.1

만약 버전을 1.9.1 로 업그레이드하고 싶을 경우 yaml 파일을 수정한 뒤 apply 하거나 set 명령어를 통해 전체 Container 의 버전을 업그레이드 할 수 있다.

K8s 는 기본적으로 Container 를 하나씩 업그레이드하는 Rolling Update 를 배포전략으로 사용하고, 전체 Container 를 내리고 다시 띄우는 Recreate 전략도 사용할 수 있다.

kubectl get replicasets

K8s 에서 Container 버전을 업그레이드할 때 Deployment Object 내부적으로 새로운 ReplicaSet 을 생성하여 새로운 버전의 Container 들을 하나씩 배포하기 때문에 위 명령어를 쳐보면 2개의 ReplicaSet 이 존재하는 것을 확인할 수 있다.

kubectl rollout status deployment my-deployment
kubectl rollout history deployment my-deployment
kubectl rollout history deployment my-deployment --revision=3
kubectl rollout undo deployment my-deployment
kubectl rollout undo deployment my-deployment --to-revision=3 # 특정 버전으로 롤백 

DaemonSets


apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: monitoring-daemon
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: monitoring-agent
  template:
    metadata:
      labels:
        app: monitoring-agent
    spec:
      containers:
      - name: monitoring-agent
        image: monitoring-agent

DaemonSet 은 모든 Node 에 1개의 Pod 를 배치하고 싶을 때 사용할 수 있는 Object 이다. 예를 들어 Datadog-Agent 와 같은 모니터링 에이전트 Pod 를 DaemonSet 을 통해 각 노드에 1개씩 배치할 수 있다. kube-proxy 역시 DaemonSet 으로 배포되어 있는 컴포넌트다.

K8s 는 DaemonSet 을 모든 Node 에 배포하기 위해 NodeAffinity 와 Default Scheduler 를 사용한다.

kubectl create -f daemon-set-definition.yaml
kubectl get daemonsets
kubectl describe daemonsets monitoring-daemon

위와 같은 커맨드로 DaemonSet 을 생성하고 조회할 수 있다.

Use Cases

  • Logging 을 위해 각 노드에 fluentd, logstash 등을 DaemonSet 으로 배포
  • Monitoring 을 위해 각 노드에 prometheus, datadog 등을 DaemonSet 으로 배포
  • Networking 을 위해 각 노드에 cilium, weave 등을 DaemonSet 으로 배포

Configure Applications


Configuring Command and Arguments on applications

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-sleeper-pod
spec:
  containers:
  - name: ubuntu-sleeper
    image: ubuntu-sleeper
    command: ["sleep2.0"]
    args: ["10"]

보통 Docker 이미지 안에는 컨테이너가 시작되면 자동으로 실행될 명령어가 ENTRYPOINTCMDDockerfile 안에 미리 정해져 있다. 쿠버네티스에서는 Dockerfile 을 다시 만들 필요 없이, Pod 의 command, args 설정으로 실행 방식을 덮어쓸 수 있다.

kubectl run webapp-green --image=kodekloud/webapp-color -- --color=green

명령어로 args 를 전달하고 싶은 경우 위와 같이 전달할 수 있다.

Configuring Environment Variables

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    env:
    - name: APP_COLOR
      value: pink

Docker 에서 docker run -e APP_COLOR=pink simple-webapp-color 으로 Container 에 환경변수를 전달하듯이 위처럼 Pod definition file 에 환경변수를 지정해줄 수 있다.

ConfigMap

관리해야 할 환경변수가 많이질 경우 ConfigMap 을 통해 환경변수를 관리할 수 있다.

kubectl create configmap app-config \
  --from-literal=APP_COLOR=blue \
  --from-literal=APP_MOD=prod

위 명령어를 통해 직접 ConfigMap 을 생성하거나,

kubectl create configmap app-config \
  --from-file=app_config.properties

위 명령어를 통해 파일을 바탕으로 ConfigMap 을 생성할 수 있고,

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_COLOR: blue
  APP_MODE: prod

또는 yaml 파일 형태로 정의한 후 생성하여 사용할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    envFrom:
    - configMapRef:
        name: app-config

ConfigMap 을 생성한 뒤엔 envFrom 필드를 통해 Pod 에 환경변수를 주입해줄 수 있다.

env:
- name: APP_COLOR
  valueFrom:
    configMapKeyRef:
      name: app-config
      key: APP_COLOR

이외에도 ConfigMap 에서 하나의 환경변수만 주입하거나,

volumes:
- name: app-config-volume
  configMap:
    name: app-config

파일 형태로 주입 역시 가능하다.

Secrets

apiVersion: v1
kind: Secret
metadata:
  name: app-secret
data:
  DB_Host: bX1zcWw=
  DB_User: cm9vdA==
  DB_Password: cGFzd3Jk
apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp-color
spec:
  containers:
  - name: simple-webapp-color
    image: simple-webapp-color
    ports:
    - containerPort: 8080
    envFrom:
    - secretRef:
        name: app-secret

Secret 을 다룰 땐 ConfigMap 과 비슷하게 Secret Object 를 사용하여 Pod 에 주입해줄 수 있다.

kubectl create secret generic app-secret \
	--from-literal=DB_Host=mysql \
	--from-literal=DB_User=root \
	--from-literal=DB_Password=paswrd

마찬가지로 Imperative 하게 생성도 가능하다.

ConfigMap 과 Secret 을 볼륨으로 마운트하는 이유


apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "prod"
  APP_VER: "1.0"
---
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
  - name: app-container
    image: busybox
    command:
    - sleep
    - 3600
    volumeMounts:
    - name: config-volume
      mountPath: "/etc/config"
  volumes:
  - name: config-volume
    configMap:
	  name: app-config

ConfigMap 과 Secret 을 볼륨으로 마운트하면 해당 데이터가 Pod 의 특정 디렉터리에 파일 형태로 저장된다. 이는 k8s 내부적으로 tmpfs(메모리 기반 파일시스템)을 사용하여 데이터를 동적으로 마운팅하는 방식이다. 환경변수의 경우 프로세스 환경변수에 직접 사용된다. 파일로 환경변수를 관리하면 여러 이점이 존재한다.

보안 강화

kubectl exec -it pod-name -- env | grep SECRET_

환경변수를 사용할 경우 Pod 내부에서 값을 쉽게 확인할 수 있다.

cat /proc/$(pgrep -f app)/environ

또 프로세스 내부에 환경변수가 남아있어 쉽게 확인할 수 있다.

Secret 을 볼륨으로 마운트하면 단순 파일로 제공되기 때문에 접근 권한을 저정하여 보안을 강화할 수 있다.

실시간 변경 반영

k8s 는 synlink 방식을 사용하여 파일을 자동으로 교체하기 때문에 값이 변경되어도 컨테이너를 재시작하지 않고도 적용된다.

일관된 관리

여러 Pod 에서 동일한 ConfigMap 또는 Secret 을 공유할 수 있다. 애플리케이션을 중앙 집중식으로 관리할 수 있어 운영이 편리해진다.

용량 제한

환경변수 방식은 시스템에 따라 길이 제한이 있으나 볼륨은 제한이 없다. TLS 인증서, SSH 키 등 길이가 긴 값들을 환경변수로 사용할 수 없을 때 유용하다.

Multi-Container Pods


apiVersion: v1
kind: Pod
metadata:
  name: simple-webapp
  labels:
    name: simple-webapp
spec:
  containers:
  - name: simple-webapp
    image: simple-webapp
    ports:
    - ContainerPort: 8080
  - name: log-agent
    image: log-agent

Pod 는 보통 1개의 Container 를 포함하지만 여러개의 Container 를 포함할 수 있도록 설계되었다. 이는 Log Agent 와 같은 Helper Container 를 함께 생명주기에 포함하여 관리하기 위함이다. 위처럼 2개의 Container 를 포함하여 Pod 를 배포할 수 있다.

Multi-Container Pods Design Patterns

다수의 Container 를 포함한 Pod 를 배포할 때 따르는 디자인 패턴이다. 위 Log Agent 는 Sidecar Pattern 의 예시다.

  • Sidecar Pattern
  • Adapter Pattern
  • Ambassador Pattern

InitContainers

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

Pod 를 배포할 때 일회성 작업이 필요한 경우 initContainers 를 통해 구성할 수 있다. Pod 의 메인 Container 는 initContainers 작업이 완료될 때 까지 기다리며, 실패할 경우 계속 Pod 를 새로 만든다.

HPA, Horizontal Pod Autoscaler


 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: my-app
 spec:
   replicas: 1
   selector:
     matchLabels:
       app: my-app
   template:
     metadata:
       labels:
         app: my-app
     spec:
       containers:
       - name: my-app
         image: nginx
         resources:
           requests:
             cpu: "250m"
           limits:
             cpu: "500m"

트래픽이 많아져 CPU 사용량이 limit 에 가까워지기 전에 오토스케일링을 통해 부하를 분산할 수 있다.

kubectl autoscale deployment my-app --cpu-percent=50 --min=1 --max=10
kubectl get hpa
kubectl delete hpa my-app

위 명령어로 HPA 를 직접 생성할 수도 있고,

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

위와 같이 파일을 통해 생성할 수도 있다.

VPA, Vertical Pod Autoscaling


kubectl apply -f https://github.com/kubernetes/autoscaler/releases/latest/download/vertical-pod-autoscaler.yaml

VPA 는 Pod 의 하드웨어 리소스를 오토스케일링하는 Object 로 K8s 에 기본적으로 제공되지 않기 때문에 직접 배포해야 한다.

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind:       Deployment
    name:       my-app
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: "my-app"
      minAllowed:
        cpu: "250m"
      maxAllowed:
        cpu: "2"
      controlledResources: ["cpu"]

기본적으로 제공되지 않는 Object 이다보니 Imperative 하게 배포할 수 없기에 위와 같이 파일형식으로 생성해줘야한다.

References