Hello Minikube

  • Pod: 쿠버네티스에서 만들고 관리할 수 있는 가장 작은 배포 단위
    • 관리 및 네트워킹을 위해 함께 연결된 하나 또는 그 이상의 컨테이너 그룹
  • Deployment: 파드의 상태를 체크해 파드의 컨테이너가 중단되면 재시작
    • Deployment는 파드들의 생성과 확장을 관리하기 위해 추천하는 방식
  • Service: 기본적으로 파드는 쿠버네티스 클러스터 안에서 내부 IP 주소로만 접근할 수 있다. 외부에서 파드에 접근할 수 있도록 하려면 쿠버네티스 서비스로 노출해야 한다.
# 1. Create minikube cluster
minikube start

# 2. Create a Deployment
kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.39 -- /agnhost netexec --http-port=8080

# 명령어 구성
# kubectl action resource
kubectl get deployments
kubectl get pods
kubectl get events # view cluster events
kubectl config view # view kubectl configuration
kubectl logs {pod-name} # view application logs for a container in pod
kubectl get services

# 3. Create a Service
kubectl expose deployment hello-node --type=LoadBalancer --port=8080

kubectl get services
minikube service hello-node

Learn Kubernetes Basics

Create a Cluster

  • K8s Cluster 2가지 유형 리소스
    • Control Plane: 클러스터 관리 담당
      • 어플리케이션 스케줄링, 어플리케이션의 상태 유지, 어플리케이션 스케일링
    • Nodes: 어플리케이션을 실행하는 작업자
      • VM 또는 물리 컴퓨터
      • 각 노드는 kublet, containerd, CRI-O 등의 툴을 가지고 있음
  • 운영 환경에서는 쿠버네티스 클러스터에는 최소 세 개의 노드가 있어야 한다.
  • 쿠버네티스에 어플리케이션을 배포할 때, Control Plane이 어플리케이션 컨테이너를 시작하도록 지시한다.
  • Control Plane은 클러스터의 노드에서 컨테이너가 실행되도록 스케줄링한다.
  • kublet과 같은 노드 레벨 요소는 Kubernetes API를 이용해 Control Plane과 의사소통한다.

Deploy an App

쿠버네티스 클러스터가 실행되면, 어플리케이션 컨테이너를 배포할 수 있다. 이를 위해 쿠버네티스 Deployment를 생성해야 한다. Deployment는 어플리케이션 인스턴스가 생성되고 업데이트되는 방법을 지시한다. Deployment가 일단 생성되면, 쿠버네티스 Control Plane은 클러스터 내의 각 노드에 실행되도록 스케줄링한다.

어플리케이션 인스턴스 생성되면, 쿠버네티스 Deployment는 인스턴스들을 계속 모니터링한다. 만약 인스턴스를 호스팅중인 노드가 다운되거나 삭제되면, Deployment는 클러스터내에 다른 노드의 인스턴스로 교체한다. 이는 장애 또는 유지 관리 문제를 해결하는 자가 복구 매커니즘을 제공한다.

# 1. Deploy an app

# Create minikube cluster (only 1 available node)
minikube start

# kubectl create deployment {deployment name} {app image location}
kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1

kubectl get deployments

# 에러가 발생하면 다음과 같이 원인을 확인해 해결
kubectl describe deployment kubernetes-bootcamp
kubectl describe pod kubernetes-bootcamp-9bc58d867-4vfxp
kubectl get pods -n kube-system

# 2. View the app

# Create a proxy: 클러스터 전체의 private network와 통신할 수 있는 프록시 생성
kubectl proxy
curl http://localhost:8001/version # see all APIs hosted through the proxy endpoint

# pod 이름에 기반해 각 endpoint를 자동 생성하고 프록시를 통해 접근 가능 
export POD_NAME=$(kubectl get pods -o go-template --template '\n')
echo $POD_NAME
curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME:8080/proxy/

Explore Your App

Kubernetes Pods

하나 또는 그 이상의 어플리케이션 컨테이너(예: Docker) 그룹이며 shared storage(volumes), ip 주소와 실행 방법을 포함한다.

쿠버네티스에서 Deployment를 생성하면 Deployment는 컨테이너를 직접 생성하는 것이 아니라, 컨테이너가 포함된 Pod를 생성한다. 각 Pod는 예약된 노드에 연결되며, 재시작 정책에 따라 종료되거나 삭제될 때까지 해당 노드에 유지된다. 노드에 장애가 발생하면 클러스터의 다른 사용 가능한 노드에 예약된다.

Nodes

각 노드는 Control Plane에 의해 관리된다. 노드는 여러개의 Pod를 가질 수 있으며, 쿠버네티스 Control Plane은 클러스터 내 노드 전반에 걸쳐 Pod 스케줄링을 자동으로 처리한다.

모든 쿠버네티스 노드는 적어도 다음을 실행한다.

  • Kubelet: 쿠버네티스 control plane과 노드 사이의 통신을 위한 프로세스
  • A Container runtime(like Docker): 레지스트리로부터 컨테이너 이미지를 가져오고, 어플리케이션을 실행

Troubleshooting with kubectl

kubectl get # list resources
kubectl describe # show detailed information about a resource
kubectl logs # print the logs from a container in a pod
kubectl exec # execute a command on a container in a pod 
# 어플리케이션 구성 확인
kubectl get pods
kubectl describe pods

# 터미널에서 어플리케이션 보기 
kubectl proxy
export POD_NAME=$(kubectl get pods -o go-template --template '\n')
echo $POD_NAME
curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME:8080/proxy/

# 컨테이너에서 명령 실행
kubectl exec "$POD_NAME" -- env
kubectl exec "$POD_NAME" -it -- bash 
cat server.js
curl http://localhost:8080

Expose Your App Publicly

쿠버네티스 서비스는 논리 Pod 세트를 정의하고 외부 트래픽 노출, 로드 밸런싱, 서비스 검색을 가능하게 하는 추상 계층이다.

서비스가 대상으로하는 Pod 세트는 일반적으로 label selector에 의해 결정된다.

각 Pod는 고유한 IP를 가지고 있지만, 서비스 없이 클러스터 밖으로 노출되지 않는다. 서비스의 spec 안에 type을 지정해 다양한 방식으로 노출할 수 있다.

  • ClusterIP(기본값): 클러스트 내부 IP에서 서비스를 노출한다. 이 유형을 사용하면 클러스터 내에서만 서비스에 접근 할 수 있다.
  • NodePort: NAT를 사용하여 클러스터 내 선택된 각 노드의 동일한 포트에 서비스를 노출한다. NodeIP:NodePort를 사용하여 클러스터 외부에서 접근 가능 할 수 있다.
  • LoadBalancer: 클라우드에 외부 로드 밸런서를 생성하고 서비스에 고정 외부 IP를 할당한다.
  • ExternalName: CNAME 레코드의 값을 반환하여 서비스 externalName 필드(예: foo.bar.example.com)에 매핑한다. 어떤 프록시도 설정되지 않는다.

Service and Labels

서비스는 라벨(labels)과 셀렉터(selectors)를 사용해 Pod 세트와 매칭한다. 라벨과 선택자는 쿠버네티스에서 객체에 대한 논리적 연산을 가능하게 하는 그룹 기본 요소이다. 라벨은 키/값 쌍으로 객체에 붙인다.

# 1. Creating a new Service
# NodePort 옵션을 이용해 외부 트래픽 노출
kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
kubectl describe services/kubernetes-bootcamp

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='')"
echo "$NODE_PORT"
# 외부 노출 테스트 
curl http://"$(minikube ip):$NODE_PORT"
# 도커 데스크탑으로 미니쿠베를 실행중인 경우
minikube service kubernetes-bootcamp --url

# 2. Using labels
kubectl describe deployment

# list label
kubectl get pods -l app=kubernetes-bootcamp
kubectl get services -l app=kubernetes-bootcamp

export POD_NAME="$(kubectl get pods -o go-template --template '\n')"
echo "$POD_NAME"

# apply a new label
kubectl label pods "$POD_NAME" version=v1
kubectl describe pods "$POD_NAME"
kubectl get pods -l version=v1

# 3. Deleting a service
kubectl delete service -l app=kubernetes-bootcamp
kubectl get services

Scale Your App

확장(scaling)은 Deployment의 replicas 수를 변경해 수행한다.

kubectl get deployments

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1/1     1            1           47h
  • NAME: 클러스터내 Deployment 이름
  • READY: CURRENT/DESIRED 레플리카 비율
  • UP-TO-DATE: 원하는 상태를 달성하기 위해 업데이트한 레플리카의 수
  • AVAILABLE: 사용자가 사용할 수 있는 어플리케이션 복제본 수
  • AGE: 어플리케이션 실행 시간
# Deployment에 의해 생성된 ReplicaSet
kubectl get rs

NAME                            DESIRED   CURRENT   READY   AGE
kubernetes-bootcamp-9bc58d867   1         1         1       47h
  • DESIRED: 원하는 어플리케이션의 레플리카 수
  • CURRENT: 현재 실행중인 레플리카 수
# 1. Scaling a Deployment

# scale the Deployment to 4 replicas
kubectl scale deployments/kubernetes-bootcamp --replicas=4
kubectl get deployments
kubectl get pods -o wide
kubectl describe deployments/kubernetes-bootcamp

# 2. Load Balancing
kubectl describe services/kubernetes-bootcamp

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='')"
echo "$NODE_PORT"
curl http://"$(minikube ip):$NODE_PORT"

# 3. Scale Down
kubectl scale deployments/kubernetes-bootcamp --replicas=2
kubectl get deployments
kubectl get pods -o wide

Update Your App

만약, Deployment가 공개적으로 노출되면, 서비스는 업데이트 중에 사용 가능한 Pod에만 트래픽을 분산한다.

# 1. Update the version of the app
kubectl get deployments
kubectl get pods
kubectl describe pods # check the current image version(Image field)

# update image
kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=docker.io/jocatalin/kubernetes-bootcamp:v2
kubectl get pods

# 2. Verify an update
kubectl rollout status deployments/kubernetes-bootcamp 
kubectl describe pods

# 3. Roll back an update
kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.ko/google-samples/kubernetes-bootcamp:v10
kubectl get deployments
kubectl get pods # status: ImagePullBackOff
kubectl describe pods # Events: Error: ErrImagePull

# roll back Deployment last version
kubectl rollout undo deployments/kubernetes-bootcamp
kubectl get pods
kubectl describe pods

# clean up cluster
kubectl delete deployments/kubernetes-bootcamp services/kubernetes-bootcamp

Configuration

Updating Configuration via a ConfigMap

Update configuration via a ConfigMap mounted as a Volume

# literal values로 ConfigMap 생성
kubectl create configmap sport --from-literal=sport=football

# Deployment 생성 
# kubelet은 sport ConfigMap 데이터를 가져와 로컬 볼륨의 파일로 변환
# 그런 다음 Pod 템플릿에 지정한 대로 해당 볼륨을 컨테이너에 마운트 
kubectl apply -f deployments/deployment-with-configmap-as-volume.yaml

# Deployment에 대한 Pod 확인
kubectl get pods --selector=app.kubernetes.io/name=configmap-volume

# Deployment에 속한 Pod 하나를 선택해 로그 확인 
kubectl logs deployment/configmap-volume

# ConfigMap 수정
kubectl edit configmap sport

# log tail
kubectl logs deployment/configmap-volume --follow

실행 중인 Pod에 configMap 볼륨 또는 프로젝션 볼륨을 사용하여 매핑된 ConfigMap이 있고 해당 ConfigMap을 업데이트하면 실행 중인 Pod는 거의 즉시 업데이트를 인식한다. 그러나 어플리케이션은 변경 사항을 폴링하거나 파일 업데이트를 감시하도록 설정된 경우에만 변경 사항을 인식한다.

kubectl get configmap

# ConfigMap 삭제 
kubectl delete configmap sport

# Deployment 삭제 
kubectl delete deployment/configmap-volume
kubectl get pods

Update environment variables of a Pod via a ConfigMap

# literal values로 ConfigMap 생성
kubectl create configmap fruits --from-literal=fruits=apples

# Deployment 생성 
kubectl apply -f deployments/deployment-with-configmap-as-envvar.yaml 

# Deployment에 대한 Pod 확인
kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var

# Deployment에 속한 Pod 하나를 선택해 로그 확인 
kubectl logs deployment/configmap-env-var

# ConfigMap 수정
kubectl edit configmap fruits

# log tail
# ConfigMap을 수정해도 출력이 바뀌지 않음
# Pod의 환경 변수는 소스 데이터가 변경될 때, 업데이트 되기 때문
# 강제로 수정사항을 적용하려면, Pod를 교체하면 됨 
kubectl logs deployment/configmap-env-var --follow

# Trigger the rollout(배포를 변경하는 것)
kubectl rollout restart deployment configmap-env-var
# Wait for the rollout to complete
kubectl rollout status deployment configmap-env-var --watch=true

# 롤아웃 후 Kubernetes가 Deployment를 위한 새로운 ReplicaSet을 생성
# 즉, 기존 Pod는 종료하고 새로운 Pod를 생성 
kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var
kubectl logs deployment/configmap-env-var

롤아웃을 트리거하지 않으면, 앱이 이전 환경 변수와 새 환경 변수가 혼합된 상태로 실행될 수 있다.

kubectl get configmap

# ConfigMap 삭제 
kubectl delete configmap fruits

# Deployment 삭제 
kubectl delete deployment/configmap-env-var
kubectl get pods

Update configuration via a ConfigMap in a multi-container

아래 명령에서 Deployment 생성 시, 해당 Deployment는 두 개의 컨테이너를 관리한다. 두 컨테이너는 통신을 위해 emptyDir 볼륨을 공유한다. 첫 번째 컨테이너는 웹 서버(nginx)를 실행하고, 공유 볼륨이 /usr/share/nginx/html에 마운트된다. 두 번째 helper 컨테이너는 alpine 기반으로 emptyDir 볼륨이 /pod-data에 마운트된다. helper 컨테이너는 ConfigMap을 기반으로 HTML 파일을 작성하고, 웹 서버 컨테이너는 HTTP를 통해 HTML을 제공한다.

# literal values로 ConfigMap 생성
kubectl create configmap color --from-literal=color=red

# Deployment 생성 
kubectl apply -f deployments/deployment-with-configmap-two-containers.yaml

# Deployment에 대한 Pod 확인
kubectl get pods --selector=app.kubernetes.io/name=configmap-two-containers

# Deployment 노출
kubectl expose deployment configmap-two-containers --name=configmap-service --port=8080 --target-port=80

# 백그라운드에서 실행 
kubectl port-forward service/configmap-service 8080:8080 &
# 서비스 접속 
curl http://localhost:8080

# ConfigMap 수정
kubectl edit configmap color

# 서비스 URL을 몇 초간 반복
while true; do curl --connect-timeout 7.5 http://localhost:8080; sleep 10; done

# Deployment 삭제 
kubectl delete deployment/configmap-two-containers
kubectl get pods

Update configuration via a ConfigMap in a Pod possessing a sidecar container

위의 시나리오는 HTML 파일을 작성하기 위한 helper 컨터이너로 Sidecar 컨테이너를 사용해 복제할 수 있었다. 사이드카 컨테이너는 개념적으로 Init 컨테이너이므로, 웹 서버 컨테이너보다 먼저 시작되는 것이 보장되어야 한다.

메인 컨테이너와 사이드카 컨테이너로 구성된 Pod를 관리하는 Deployment 예시이다. 두 컨테이너는 서로 통신하기 위해 emptyDir 볼륨을 공유한다. 메인 컨테이너는 웹 서버(nginx)를 실행한다. 두 번째는 헬퍼 컨테이너 역할을 하는 알파인 리눅스 기반의 사이드카 컨테이너이다.

# Deployment 생성
kubectl apply -f deployments/deployment-with-configmap-and-sidecar-container.yaml

# Deployment에 대한 Pod 확인
kubectl get pods --selector=app.kubernetes.io/name=configmap-sidecar-container

# Deployment 노출
kubectl expose deployment configmap-sidecar-container --name=configmap-sidecar-service --port=8081 --target-port=80

# 백그라운드에서 실행 
kubectl port-forward service/configmap-sidecar-service 8081:8081 &
# 서비스 접속 
curl http://localhost:8081

# ConfigMap 수정
kubectl edit configmap color

# 서비스 URL을 몇 초간 반복
# color가 바뀌는 것 확인 
while true; do curl --connect-timeout 7.5 http://localhost:8081; sleep 10; done
kubectl get configmap

# ConfigMap 삭제 
kubectl delete configmap color

# Deployment 삭제 
kubectl delete deployment/configmap-sidecar-container
kubectl get pods

# Service 삭제
kubectl delete service configmap-sidecar-container

Update configuration via an immutable ConfigMap that is mounted as a volume’

ConfigMap을 변경 불가능으로 설정하면 kubelet이 변경 사항을 감지하지 않으므로 성능이 향상된다.

변경이 필요한 경우, 다음 중 하나를 해야 한다.

  • ConfigMap의 이름을 변경하고 새 이름을 참조하는 Pod를 실행하도록 전환
  • 이전 값을 사용하여 Pod를 실행했던 클러스터의 모든 노드 교체
  • 이전 ConfigMap을 로드한 모든 kubelet을 다시 시작
# ConfigMap 생성 
kubectl apply -f configmap/immutable-configmap.yaml

# Deployment 생성
kubectl apply -f deployments/deployment-with-immutable-configmap-as-volume.yaml

# Deployment에 대한 Pod 확인
kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
# Deployment에 속한 Pod 하나를 선택해 로그 확인 
kubectl logs deployments/immutable-configmap-volume

# 변경 불가능 ConfigMap은 변경 사항을 되돌리거나 내용을 변경할 수 없으므로
# 새로운 ConfigMap 생성 
kubectl apply -f configmap/new-immutable-configmap.yaml 
kubectl get configmap

# 편집기에서 기존 볼륨 정의를 수정해 새로운 ConfigMap을 사용 
# 롤아웃 시작
kubectl edit deployment immutable-configmap-volume
# 이전 Pod가 모두 종료되고 새 Pod가 준비 상태가 될 때까지 대기
kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
kubectl logs deployment/immutable-configmap-volume
kubectl get configmap

# ConfigMap 삭제 
kubectl delete configmap color

# Deployment 삭제 
kubectl delete deployment/immutable-configmap-volume
kubectl get pods

Configuring Redis using a ConfigMap

# yaml 파일 생성
cat <<EOF >./example-redis-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: example-redis-config
data:
  redis-config: ""
EOF

# ConfigMap 생성
kubectl apply -f example-redis-config.yaml
# Pod 생성
kubectl apply -f pod/config/redis-pod.yaml

Redis Pod 매니페스트 내용을 검토하고 다음 사항을 유의한다.

  • spec.volumes[1]에 의해 config라는 이름의 볼륨이 생성됨
  • spec.volumes[1].configMap.items[0] 아래의 keypathexample-redis-config ConfigMap의 redis-config 키를 config 볼륨의 redis.conf라는 파일로 노출
  • spec.containers[0].volumeMounts[1]에 의해 config 볼륨은 /redis-master에 마운트 됨
# 생성한 객체 확인
kubectl get pod/redis configmap/example-redis-config 
# redis-config 키가 비어있는 것 확인
kubectl describe configmap/example-redis-config

# Pod에 들어가 redis-cli 도구로 현재 구성 확인
kubectl exec -it redis -- redis-cli
127.0.0.1:6379> CONFIG GET maxmemory
127.0.0.1:6379> CONFIG GET maxmemory-policy

# example-redis-config.yaml 파일에서 ConfigMap 수정 
# ConfigMap 업데이트
kubectl apply -f example-redis-config.yaml

# 수정된 ConfigMap 확인 
kubectl describe configmap/example-redis-config
kubectl exec -it redis -- redis-cli
127.0.0.1:6379> CONFIG GET maxmemory
127.0.0.1:6379> CONFIG GET maxmemory-policy

구성 값은 변경되지 않는다. ConfigMap에서 수정된 값을 가져오려면 Pod를 다시 시작해야 한다.

kubectl delete pod redis
kubectl apply -f pod/config/redis-pod.yaml

kubectl exec -it redis -- redis-cli
127.0.0.1:6379> CONFIG GET maxmemory
127.0.0.1:6379> CONFIG GET maxmemory-policy

# 생성 자원들 삭제 
kubectl delete pod/redis configmap/example-redis-config