Jun 11, 2024
파드란 무엇인가파드 만들어보기쿠버네티스는 파드를 관리해!컨트롤러 객체 알아보기디플로이먼트 레이블 실습하기!파드의 레이블이 수정되면, 디플로이먼트는 해당 파드를 인지하지 못한다!원래 있었던 파드의 레이블을 원래대로 수정해보면?!디플로이먼트 포트포워딩하기애플리케이션 매니페스트로 배포하기매니페스트로 파드를 하나 만들어보자매니페스트로 디플로이먼트를 만들어보자파드에서 실행중인 애플리케이션에 접근하기파드 내부로 접근해보자간단하게 파드 로그만 확인해보기디플로이먼트 로그 확인해보기파일 복사하기리소스 관리 이해하기모든 파드를 삭제해보자연습문제풀어보기
파드란 무엇인가
컨테이너는 애플리케이션 구성 요소 하나를 실행하는 가상화된 환경을 가리킨다.
쿠버네티스는 이 컨테이너를 또 다른 가상환경인 파드로 감싼다.
파드는 쿠버네티스에서 컴퓨팅의 최소 단위다. 파드의 역할이 무엇이며, 어떻게 동작하는지 잘 이해해야한다.
파드 만들어보기
# 컨테이너를 하나 담은 파드를 실행합니다.
% kubectl run hello-kiamol --image=kiamol/ch02-hello-kiamol
> pod/hello-kiamol created
# 파드가 준비상태가 될 때까지 기다립니다.
% kubectl wait --for=condition=Ready pod hello-kiamol
> pod/hello-kiamol condition met
# 클러스터에 존재하는 모든 파드 목록을 보여줍니다.
% kubectl get pods
> NAME READY STATUS RESTARTS AGE
hello-kiamol 1/1 Running 0 27s
# 파드의 상세 정보를 확인합니다.
% kubectl describe pod hello-kiamol
Name: hello-kiamol
Namespace: default
Priority: 0
Service Account: default
Node: docker-desktop/192.168.65.3
Start Time: Wed, 05 Jun 2024 21:30:38 +0900
Labels: run=hello-kiamol
Annotations: <none>
Status: Running
IP: 10.1.0.6
IPs:
IP: 10.1.0.6
Containers:
hello-kiamol:
Container ID: docker://20d86f9874633779384d876457c8d38998cefe3437ca896b5f8fafbcaa515cae
Image: kiamol/ch02-hello-kiamol
Image ID: docker-pullable://kiamol/ch02-hello-kiamol@sha256:8a27476444b4c79b445f24eeb5709066a9da895b871ed9115e81eb5effeb5496
Port: <none>
Host Port: <none>
State: Running
Started: Wed, 05 Jun 2024 21:30:45 +0900
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-jp9zm (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-jp9zm:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 10m default-scheduler Successfully assigned default/hello-kiamol to docker-desktop
Normal Pulling 10m kubelet Pulling image "kiamol/ch02-hello-kiamol"
Normal Pulled 10m kubelet Successfully pulled image "kiamol/ch02-hello-kiamol" in 6.435s (6.435s including waiting)
Normal Created 10m kubelet Created container hello-kiamol
Normal Started 10m kubelet Started container hello-kiamol
# IP 주소 확인하기
% kubectl get pod hello-kiamol --output custom-columns=NAME:metadata.name,NODE_IP:status.hostIP,POD_IP:status.podIP
NAME NODE_IP POD_IP
hello-kiamol 192.168.65.3 10.1.0.6
파드는 기본적으로는 하나의 컨테이너만 실행한다.
‘쿠버네티스가 컨테이너를 실행하는 수단이 파드이다’ 라고 생각해도 크게 틀리지 않다.
쿠버네티스는 직접 컨테이너를 실행하지 않고 파드를 통해 실행한다. 따라서 컨테이너는 해당 노드에 설치된 컨테이너 런타임을 통해 실행되며, 이 런타임은 도커가 될 수도 있고, 다른 무언가가 될 수도 있다. 이를 위해 컨테이너를 추상화한 개념으로 파드를 도입한 것이다.
쿠버네티스는 파드를 관리하고, 컨테이너는 쿠버네티스 외부에서 관리된다.
쿠버네티스는 파드를 관리해!
# 파드에 포함된 컨테이너를 찾습니다.
% docker container ls -q --filter label=io.kubernetes.container.name=hello-kiamol
20d86f987463
# 해당 컨테이너를 지워봅니다.
% docker container rm -f $(docker container ls -q --filter label=io.kubernetes.container.name=hello-kiamol)
20d86f987463
# 파드의 상태를 조회합니다.
% kubectl get pod hello-kiamol
NAME READY STATUS RESTARTS AGE
hello-kiamol 1/1 Running 1 17h
# 지웠던 컨테이너는 사라졌지만, 다른 컨테이너가 만들어져 있습니다.
% docker container ls -q --filter label=io.kubernetes.container.name=hello-kiamol
4b8f9277fae9
파드의 컨테이너 개수가 0개가 되자 쿠버네티스는 즉시 컨테이너를 생성해서 파드를 복원했다.
컨테이너를 파드로 추상화했기 때문에 컨테이너에 문제가 생기더라도 자기수복성을 가질 수 있다.
파드를 더 추상화하면 이 이상의 복원력을 가질 수 있을 것이다.
잠시 포트포워딩 해보기!
우리가 만든 hello-kiamol 파드에는 어떤 애플리케이션이 돌아가고 있었을까?
간단한 웹애플리케이션인데 네트워크 설정을 하지 않았기 때문에 볼 수 없다. 포트포워딩을 해보자!
% kubectl port-forward pod/hello-kiamol 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080

파드는 원시타입 리소스이므로, 일반적으로는 파드를 직접 실행할 일은 없다. 보통 파드를 관리할 컨트롤러 객체를 따로 만든다.
컨트롤러 객체 알아보기
파드는 직접 사용하기에는 너무 단순하다. 파드는 고립된 하나의 애플리케이션이며, 각 파드는 서로 다른 노드에 배정될 수 있다. (하나의 파드가 서로 다른 노드에 배치될 수는 없다.)
만약 어떤 노드가 고장이 났다고 해보자. 이때 파드는 유실되며, 쿠버네티스는 파드를 복원할 수 없다. 또한 파드를 여러 개 실행해서 고가용성을 확보하려고 해도, 모든 파드가 다른 노드들에 흩어져서 실행된다는 보장이 없다.
컨트롤러 객체는 이런 불편함을 해결한다. 컨트롤러 객체는 다른 리소스를 관리하는 쿠버네티스 리소스이다. 쿠버네티스 API와 연동하며 시스템의 상태를 보다가 바람직한 상태가 아니게 되면, 그 차이를 바로 잡는다.
쿠버네티스에는 여러가지 컨트롤러 객체가 있는데, 주로 관리하는 컨트롤러 객체는 디플로이먼트이다. 디플로이먼트는 앞서 말했던 문제점을 모두 해결한다.
디플로이먼트로 실행해보기!
# 이전과 같은 애플리케이션을 실행하는 디플로이먼트
% kubectl create deployment hello-kiamol2 --image=kiamol/ch02-hello-kiamol
deployment.apps/hello-kiamol2 created
# 파드 목록 출력하기. 2개의 파드가 생겼다.
# 하나는 쌩으로 만든 거, 하나는 디플로이먼트로 만든거!
# 복제본 수를 지정하지 않아서 1개만 생성되었다.
% kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-kiamol 1/1 Running 1 18h
hello-kiamol2-5d7b4bc887-s289s 1/1 Running 0 45s
우리는 디플로이먼트만 만들었는데 파드가 생성되었다. 디플로이먼트가 올바른 상태를 유지하기 위해 파드를 만들어주었기 때문이다. 그렇다면 디플로이먼트는 어떻게 리소스를 추적할까?
모든 쿠버네티스 리소스는 key-value 형태의 라벨을 가진다. 예를 들어 디플로이먼트에 release라는 라벨을 추가하고 그 값을 20.04라고 지정하면 우리는 이 라벨을 보고 용도와 버전을 확인할 수 있다. 또 라벨은 객체 간 관계를 표현해서 리소스간의 느슨한 연결을 만드는데도 쓰일 수 있다.
디플로이먼트 레이블 실습하기!
# 디플로이먼트가 부여한 파드의 레이블 출력
% kubectl get deploy hello-kiamol2 -o jsonpath='{.spec.template.metadata.labels}'
{"app":"hello-kiamol2"}%
# 앞서 출력한 레이블을 가진 파드의 목록 출력
% kubectl get pods -l app=hello-kiamol2
NAME READY STATUS RESTARTS AGE
hello-kiamol2-5d7b4bc887-s289s 1/1 Running 1 (6m41s ago) 4d7h
디플로이먼트는 템플릿을 적용해서 파드를 생성하는데, 템플릿의 메타데이터는 레이블을 포함한다. 위 실습에서는 디플로이먼트가 파드에
app:hello-kiamol2
라는 레이블을 부여한 것을 확인할 수 있다.레이블을 이용해서 리소스 간의 관계를 파악하는 것은 쿠버네티스에서 매우 자주 쓰는 중요한 패턴이다.
컨트롤러 객체는 레이블 셀렉터를 통해 자신이 관리하는 리소스를 식별한다.
디플로이먼트-x (match: app=x) 파드x-1 (app=x) 파드x-2 (app=x) 파드3 (release=20.04)
파드의 레이블이 수정되면, 디플로이먼트는 해당 파드를 인지하지 못한다!
# 모든 파드의 이름과 레이블 확인
% kubectl get pods -o custom-columns=NAME:metadata.name,LABELS:metadata.labels
NAME LABELS
hello-kiamol map[run:hello-kiamol]
hello-kiamol2-5d7b4bc887-s289s map[app:hello-kiamol2 pod-template-hash:5d7b4bc887]
# 디플로이먼트가 생성한 파드의 'app' 레이블을 수정해보자
% kubectl label pods -l app=hello-kiamol2 --overwrite app=hello-kiamol-x
pod/hello-kiamol2-5d7b4bc887-s289s labeled
# 파드가 또 하나 생성되었다.
% kubectl get pods -o custom-columns=NAME:metadata.name,LABELS:metadata.labels
NAME LABELS
hello-kiamol map[run:hello-kiamol]
hello-kiamol2-5d7b4bc887-4nhlt map[app:hello-kiamol2 pod-template-hash:5d7b4bc887]
hello-kiamol2-5d7b4bc887-s289s map[app:hello-kiamol-x pod-template-hash:5d7b4bc887]
원래 있었던 파드의 레이블을 원래대로 수정해보면?!
# 원래대로 수정하기!
% kubectl label pods -l app=hello-kiamol-x --overwrite app=hello-kiamol2
pod/hello-kiamol2-5d7b4bc887-s289s labeled
# 디플로이먼트가 다시 파드를 식별하고, 추가했던 파드를 없앴다.
% kubectl get pods -o custom-columns=NAME:metadata.name,LABELS:metadata.labels
NAME LABELS
hello-kiamol map[run:hello-kiamol]
hello-kiamol2-5d7b4bc887-s289s map[app:hello-kiamol2 pod-template-hash:5d7b4bc887]
디플로이먼트 포트포워딩하기
port-forward
를 이용하면 파드로 포트포워딩을 할 수 있었다. 하지만 이를 위해 디플로이먼트가 가진 파드의 무작위 문자열을 찾지 않아도 된다. 디플로이먼트 정의에서 직접 포트포워딩 설정을 할 수 있다. 이렇게 하면 디플로이먼트가 자신이 가진 파드 중 하나를 트래픽 전달 대상으로 삼는다.# 로컬 컴퓨터에서 디플로이먼트로 포트포워딩 설정
% kubectl port-forward deploy/hello-kiamol2 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
애플리케이션 매니페스트로 배포하기
쿠버네티스 API의 정식 스크립트 포맷은 JSON이지만, 가독성이 뛰어나고 주석을 작성할 수 있는 YAML을 많이 쓴다.
매니페스트로 파드를 하나 만들어보자
아래와 같이 manifest file을 작성한다.
ch02/pod.yaml
# manifest script는 쿠버네티스 API 버전과 정의하려는 리소스의 유형을 밝히며 시작한다.
# 여기서는 파드를 만들 것이다.
apiVersion: v1
kind: Pod
# 리소스의 메타데이터로 이름(required)과 label(optional)이 들어간다.
metadata:
name: hello-kiamol-3
# 리소스의 정의 내용으로, 파드를 만들 때는 실행할 컨테이너를 정의한다.
spec:
containers:
- name: web
image: kiamol/ch02-hello-kiamol
manifest file로 파드를 생성하려면
kubectl apply -f
명령을 사용하면 된다.# 파일된 기술된대로 리소스를 생성. (인터넷에 있는 파일도 가능하다)
% kubectl apply -f pod.yaml
pod/hello-kiamol-3 created
% kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-kiamol 1/1 Running 3 (5m24s ago) 5d13h
hello-kiamol-3 1/1 Running 0 6s
hello-kiamol2-5d7b4bc887-s289s 1/1 Running 2 (5m24s ago) 4d18h
# 한 번 더 하더라도 이미 존재하기 때문에 또 다시 생성되지는 않는다.
% kubectl apply -f pod.yaml
pod/hello-kiamol-3 unchanged
% kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-kiamol 1/1 Running 3 (8m7s ago) 5d13h
hello-kiamol-3 1/1 Running 0 2m49s
hello-kiamol2-5d7b4bc887-s289s 1/1 Running 2 (8m7s ago) 4d18h
매니페스트로 디플로이먼트를 만들어보자
# 디플로이먼트를 만든다. 디플로이먼트는 API v1에 속한다.
apiVersion: apps/v1
kind: Deployment
# 디플로이먼트의 이름을 정한다.
metadata:
name: hello-kiamol-4
# 리소스를 정의한다.
spec:
# 자신의 관리 대상을 결정하는 label selector를 정의한다.
selector:
matchLabels:
app: hello-kiamol-4
# 이 템플릿은 디플로이먼트가 파드를 만들 때 사용된다.
# 파드를 만들 때와 비슷한 형식이다.
template:
metadata:
labels:
app: hello-kiamol-4
spec:
containers:
- name: web
image: kiamol/ch02-hello-kiamol
# Start an application using the manifest file.
% kubectl apply -f deployment.yaml
deployment.apps/hello-kiamol-4 created
# hello-kiamol-4 is created!
% kubectl get pods -l app=hello-kiamol-4
NAME READY STATUS RESTARTS AGE
hello-kiamol-4-568f7fbc88-vsf6p 1/1 Running 0 5m34s
파드에서 실행중인 애플리케이션에 접근하기
디플로이먼트와 파드를 통해 가용성과 자기수복성을 확보하더라도, 애플리케이션은 컨테이너안에서 실행된다.
컨테이너 런타임에 따라서 직접적인 컨테이너 접근이 허용되지 않는 경우도 있다. (managed kubernetes에서는 도커에 직접 접근할 수가 없다.) 이런 경우에도 kubectl을 이용하여 파드안에 있는 컨테이너에 접근할 수 있다.
파드 내부로 접근해보자
# 이전에 만들었언 hello-kiamol이라는 파드 정보를 확인해보자
% kubectl get pod hello-kiamol -o custom-columns=NAME:metadata.name,POD_IP:status.podIP
NAME POD_IP
hello-kiamol 10.1.0.17
# 파드 내부에 접근한다.
% kubectl exec -it hello-kiamol sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
$ hostname -i
10.1.0.17
$ ls
bin docker-entrypoint.sh lib opt run sys var
dev etc media proc sbin tmp
docker-entrypoint.d home mnt root srv usr
$ wget http://localhost | head -n 4
Connecting to localhost ([::1]:80)
saving to 'index.html'
index.html 100% |*******************************************************************************************************************************| 353 0:00:00 ETA
'index.html' saved
$ exit
간단하게 파드 로그만 확인해보기
# 쿠버네티스로 확인하기
% kubectl logs --tail=9 hello-kiamol
2024/06/11 01:47:10 [notice] 1#1: nginx/1.21.6
2024/06/11 01:47:10 [notice] 1#1: built by gcc 10.3.1 20211027 (Alpine 10.3.1_git20211027)
2024/06/11 01:47:10 [notice] 1#1: OS: Linux 6.6.26-linuxkit
2024/06/11 01:47:10 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/06/11 01:47:10 [notice] 1#1: start worker processes
2024/06/11 01:47:10 [notice] 1#1: start worker process 32
2024/06/11 01:47:10 [notice] 1#1: start worker process 33
2024/06/11 01:47:10 [notice] 1#1: start worker process 34
::1 - - [11/Jun/2024:02:18:20 +0000] "GET / HTTP/1.1" 200 353 "-" "Wget" "-"
# 도커로 확인하기. 동일한 것을 확인할 수 있다.
% docker logs --tail=9 $(docker container ls -q --filter label=io.kubernetes.container.name=hello-kiamol)
2024/06/11 01:47:10 [notice] 1#1: nginx/1.21.6
2024/06/11 01:47:10 [notice] 1#1: built by gcc 10.3.1 20211027 (Alpine 10.3.1_git20211027)
2024/06/11 01:47:10 [notice] 1#1: OS: Linux 6.6.26-linuxkit
2024/06/11 01:47:10 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/06/11 01:47:10 [notice] 1#1: start worker processes
2024/06/11 01:47:10 [notice] 1#1: start worker process 32
2024/06/11 01:47:10 [notice] 1#1: start worker process 33
2024/06/11 01:47:10 [notice] 1#1: start worker process 34
::1 - - [11/Jun/2024:02:18:20 +0000] "GET / HTTP/1.1" 200 353 "-" "Wget" "-"
디플로이먼트 로그 확인해보기
# yaml로 만든 디플로이먼트가 관리하는 파드 목록 보기
% kubectl get pods -l app=hello-kiamol-4
NAME READY STATUS RESTARTS AGE
hello-kiamol-4-568f7fbc88-vsf6p 1/1 Running 0 35m
# 디플로이먼트가 관리하는 파드안에 있는 컨테이너에서 GET 요청
# (파드 이름을 몰라도 파드에 접근할 수 있다)
% kubectl exec deploy/hello-kiamol-4 -- sh -c 'wget http://localhost > /dev/null'
Connecting to localhost ([::1]:80)
saving to 'index.html'
index.html 100% |********************************| 353 0:00:00 ETA
'index.html' saved
# 해당 파드의 로그 보기
% kubectl logs --tail=1 -l app=hello-kiamol-4
::1 - - [11/Jun/2024:02:38:39 +0000] "GET / HTTP/1.1" 200 353 "-" "Wget" "-"
파일 복사하기
아래와 같은 방법을 이용하면 양방향으로 파일을 복사할 수 있다.
# make direcotory on local.
% mkdir -p ~/temp
# copy a file from pod to local.
% kubectl cp hello-kiamol:/usr/share/nginx/html/index.html ~/tmp/kiamol/ch02/index.html
tar: removing leading '/' from member names
# check the copied file.
% ls ~/tmp/kiamol/ch02
index.html
% cat ~/tmp/kiamol/ch02/index.html
<html>
<body>
<h1>
Hello from Chapter 2!
</h1>
<h2>
This is
<a
href="https://www.manning.com/books/learn-kubernetes-in-a-month-of-lunches"
>Learn Kubernetes in a Month of Lunches</a
>.
</h2>
<h3>By <a href="https://blog.sixeyed.com">Elton Stoneman</a>.</h3>
</body>
</html>
리소스 관리 이해하기
kubectl을 사용하면 쿠버네티스 리소스를 쉽게 삭제할 수 있지만, 삭제한 리소스가 되살아나는 경우가 있다.
컨트롤러 객체가 만든 리소스는 컨트롤러 객체를 통해 삭제해야하며, 그렇지 않으면 다시 복원시킨다. 이렇게 하는 것이 컨트롤러 객체의 역할이기 때문이다.
모든 파드를 삭제해보자
# 파드의 목록을 확인한다.
# 직접 생성한 2개의 파드는 간단한 이름을 가지고,
# 컨트롤러 객체를 통해 생성한 파드는 무작위 문자열이 붙어있다.
% kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-kiamol 1/1 Running 3 (67m ago) 5d14h
hello-kiamol-3 1/1 Running 0 61m
hello-kiamol-4-568f7fbc88-vsf6p 1/1 Running 0 51m
hello-kiamol2-5d7b4bc887-s289s 1/1 Running 2 (67m ago) 4d19h
# 모든 파드를 삭제한다.
% kubectl delete pods --all
pod "hello-kiamol" deleted
pod "hello-kiamol-3" deleted
pod "hello-kiamol-4-568f7fbc88-vsf6p" deleted
pod "hello-kiamol2-5d7b4bc887-s289s" deleted
# 디플로이먼트로 생성한 파드는 다시 생겨났다.
% kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-kiamol-4-568f7fbc88-6bpdr 1/1 Running 0 4s
hello-kiamol2-5d7b4bc887-76xjk 0/1 ContainerCreating 0 4s
# 디플로이먼트 목록을 확인
% kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
hello-kiamol-4 1/1 1 1 54m
hello-kiamol2 1/1 1 1 4d20h
# 모든 디플로이먼트를 삭제한다.
% kubectl delete deploy --all
deployment.apps "hello-kiamol-4" deleted
deployment.apps "hello-kiamol2" deleted
# 디플로이먼트가 관리하는 파드들도 모두 삭제되었다.
% kubectl get pods
No resources found in default namespace.
# 모든 유형의 리소스를 확인한다.
# 쿠버네티스 API 서버만 남았다.
% kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d14h
연습문제
P1. 파드 하나를 포함하는 디플로이먼트에 대한 YAML 정의를 작성해보자.
ch02/lab에 있는 pod.yaml을 참고하여 만들어라.
- 애플리케이션 컨테이너는 80번 포트로 listen하는 웹사이트를 실행한다.
- 호스트명은 파드 이름이다.
apiVersion: v1
kind: Pod
metadata:
name: whoami-1
spec:
containers:
- name: web
image: kiamol/ch02-whoami
풀어보기
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami-1
spec:
selector:
matchLabels:
app: whoami-1
template:
metadata:
labels:
app: whoami-1
spec:
containers:
- name: web
image: kiamol/ch02-whoami
% kubectl apply -f deployment.yaml
deployment.apps/whoami-1 created
% kubectl get pods -l app=whoami-1
NAME READY STATUS RESTARTS AGE
whoami-1-549786dc89-ss6q2 1/1 Running 0 24s
% kubectl port-forward deploy/whoami-1 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
% kubectl get pods -o custom-columns=NAME:metadata.name
whoami-1-549786dc89-ss6q2
% kubectl exec deploy/whoami-1 -- sh -c 'hostname'
whoami-1-549786dc89-ss6q2
Share article