Search
Duplicate
🔊

3장. 파드: 쿠버네티스에서 컨테이너 실행

k8s 를 다룬다고 하면 가장 중요한 개념이 pod 인것 같다. pod 의 구성을 어떻게 할 것인지, 그 pod 를 어떻게 사용해야 하는지에 대한 고민은 아무리 강조해도 지나치지 않다. 그러한 고민을 위해 필수적으로 알고있어야 하는 지식을 다룬다.

3.1 파드 소개

All containers of a pod run on the same node. A pod never spans two nodes.
컨테이너 그룹, k8s의 기본 빌딩 블록이다.
컨테이너를 가진 pod를 배포하고 운영한다
파드 하나에 2개 이상의 container 가 존재하는건 아님, 1:1 로 보는게 낫다
여러 worker node 에 걸쳐서 실행되는게 아님 특정 node에서 실행

3.1.1 파드가 필요한 이유

docker container 가 있는데 왜 파드가 필요할까? 그냥 컨테이너로 하면 안되는건가? 운영에 필요한 여러 프로세스를 하나의 도커 컨테이너로 사용하면 안되는걸까?

container의 목적은 단일 프로세스를 실행하는 것으로 설계 되었다.

만약 단일 컨테이너로 관련 없는 여러 프로세스를 로깅한다면 그 로그의 책임은 개발자의 몫이 된다.
특정 프로세스만 재시작해야 한다면 동일 표준으로 출력되는 로그를 어떻게 분류할 것인가?
각 프로세스를 자체의 개별 컨테이너로 실행해야 한다 이것이 docker, k8s 를 사용하는 방법이다.

3.1.2 파드 이해하기

그렇다면 앞의 질문대로 운영에 필요한 여러 프로세스(컨테이너)를 관리할 상위 구조가 필요하게 된다. 이것이 pod 이다.
그렇다면 하나의 독립적인 운영을 파드로 묶는 것도 가능해지고 모두 함께 실행하도록 관리할 수도 있다.
동일한 환경을 제공하되 격리된 환경이 되는 것 이다.

같은 파드에서 container 간 부분 격리

하나의 그룹으로 묶인 컨테이너들을 1개의 파드로 인식한다면 k8s 파드 안에 있는 모든 container 가 자체 namespace 가 아닌 독일한 리눅스 namespace를 공유하도록 docker 를 설정한다
왜냐하면 운영을 위해 공유해야 하는 리소스가 있기 때문에 굳이 완전한 격리가 아닌 더 느슨한 격리로 나눈다
파드의 모든 container 들은 동일한 network namespace, UTS namespace 안에서 실행되기 대문에 같은 hostname, network interface 를 공유한다(동일한 PID namespace 를 공유하는것도 가능)
단, 파일 시스템은 다르다
파일은 컨테이너 이미지에서 나오기 때문에 다른 컨테이너와 완전히 분리된다.
그러나 k8s 의 volume 개념을 사용하면 directory 공유가 가능하다

컨테이너가 동일한 IP와 port를 공유하는 방법

pod의 container 가 동일한 네트워크 namespace 에서 실행되기 때문에 IP, 포트 공간을 공유한다
이 경우 port 가 충돌하지 않도록 주의해야 한다.
pod 가 다르다면 신경 쓸 필요 없다.

파드 간 플랫 네트워크

Each pod gets a routes IP address and all other pods see the pod under that IP address.
k8s 클러스터는 flat 한 네트워크 주소 공간에 상주 하므로 다른 pod 에 IP 로 접근이 가능하다 각 파드 사이에 NAT는 없다.
같은 k8s cluster 에 다른 노드에 있는지도 중요하지 않다
이 방식은 LAN에 있는 컴퓨터 간 통신과 유사하다
파드는 논리적인 호스트이다. 컨테이너가 아닌 환경에서 물리적 호스트 혹은 VM과 유사하게 동작한다

3.1.3 파드에서 컨테이너 적절한 구성

기존 IDC와 같은 방식은 하나의 호스트 서버에 여러 application 을 넣었다 하지만 파드는 오버헤드가 적기 때문에 여러 파드를 생성할 수도 있고 파드에 여러 컨테이너를 넣을수도 있다.
예를들어 backend DB, frontend 가 있다면 단일 혹은 두개의 파드로 구성해야 할까?

다계층 애플리케이션을 여러 파드로 분할

프론트와 DB container 두개로 구성된 단일 파드를 실행하지 못할 것은 없지만, 적절한 방법이 아니다.
파드의 모든 container 는 항상 같은 위치에서 실행되어야 한다 하지만, 웹 서버와 DB가 같은 머신에서 실행 될 필요는 없다.
프론트엔드와 백엔드가 같은 파드에 있다면 둘은 같은 노드에서 실행된다
만약 노드 2개에 이 파드 하나만 있다면, 하나의 노드가 유휴 상태가 되어 자원이 낭비된다.
파드를 두개로 분리해 각 노드에 배치되는 것이 인프라 활용도를 향상시킨다.

개별 확장이 가능하도록 여러 파드로 분할

2개 container 를 하나의 pod 에 배치하지 말아야 하는 또 다른 이유는 scale-out 이다.
파드는 scaling 의 기본 단위이다.
front end, back end 가 각각 다른 스케일리의 요구사항을 갖게 될 것이므로 개별적으로 확장하는 경향이 있다. 개별적 확장이 필요하다 별도 파드에 배포해야 한다.

파드에서 여러 컨테이너를 사용하는 경우

여러 컨테이너를 단일 파드에 넣는 주된 이유는 아래 그림과 같이 하나의 주요 프로세스와 하나 이상의 보완 프로세스로 구성된 경우이다.
Pods should contain tightly coupled containers, usually a main container and containers that support the main one
예를 들면 특정 directory 파일을 제공하는 웹 서버인 경우, 로그 로테이터와 수집기 등등

결정 기준

컨테이너를 파드로 묶어 그룹으로 만들때, 단일 파드로 넣을지 2개의 별도 파드에 넣을지 결정하기 위해 다음의 질문이 선행되어야 한다.
컨테이너를 함께 실행해야 하는가, 혹은 서로 다른 호스트에서 실행할 수 있는가?
여러 컨테이너가 모여 하나의 구성 요소를 나타내는가, 혹은 개별적인 구성요소인가?
컨테이너가 함께, 혹은 개별적으로 스케일링 되어야 하는가?
3장은 단일 컨테이너 파드만을 다룬다. 6장에서 여러 컨테이너 사용을 다루게 된다.

3.2 YAML 또는 json descriptor로 파드 생성

k8s 리소스는 REST API에 json 혹은 yaml 매니페스트를 전송하여 생성한다.
설정

파드를 정의하는 주요 부분

Metadata: 이름, 네임스페이스, 레이블 및 파드에 관한 기타 정보
Spec: 파드 컨테이너, 볼륨, 기타 데이터 등 파드 자체에 관한 실제 명세
Status: 파드 상태, 컨테이너 설명과 상태, 파드 내부 IP 기타 정보 등
이건 runtime 에서 발생되는 정보이니 직접 작성하는거 아님
apiVersion: v1 kind: Pod metadata: name: kubia-manual spec: containers: - image: luksa/kubia name: kubia ports: - containerPort: 8080 protocol: TCP
YAML

3.2.3 Using kubectl create to create the pod

yaml 을 통해 pod 를 생성해보자
kubectl create -f kubia-manual.yaml
YAML
create -f 명령어는 json, yaml을 사용해 pod 포함 다양한 resource 를 만들어낼 수 있다.

Retrieving the whole definition of a running pod

파를 생성하고 난 뒤에 k8s에 다음의 명령어를 입력한다면
kubectl get po kubia-manual -o yaml|json
YAML
파드에 대한 자세한 설명이 나온다.

Seeing your newly created pod in the list of pods

실행중인 pod를 확인한다.
kubectl get pods
YAML

3.2.4 Viewing Application logs

docker logs <container id>
YAML
이 명령어를 통해서 container 의 로그를 확인할 수 있다. 맞찬가지로 k8s 에서도 동일한 기능이 있다.
kubectl logs kubia-manual
YAML

3.2.5 Sending requests to the pod

앞서 만든 pod 를 port-forwarding 을 사용해 외부에 노출하여 접근하게 해야 한다.
지금 상태로는 올려두고 로그를 받아 디버깅이 가능하지만 서비스로 사용하지는 못하기 때문이다.

Forwarding a local network port to a port in the pod

service를 사용하지 않고 pod에 연결하고자 한다면 port-forwarding을 사용하여 파드에 연결할 수 있다. 단 이것은 pod 의 local 에서 사용한다 (autopilot 에서는 안될듯)

Connecting to the pod through the port forwarding

A simplified view of what happens when you use curl with kubectl port-forward

3.3 Organizing pods with labels

cluster에 2개의 파드가 실행중이다. 실제 앱을 배포할 때 이거보다 더 많은 pod를 다뤄야 할 수도 있다. 이때 파드를 category 화 하여 더 효율적으로 다루는 방법이 있다.
MSA 환경에서 20개 이상의 파드를 다뤄야 하기도 하고 stable, beta, canary 등 다양한 방식과 형태로 배포해야 한다. 이 상황에서 더 효율적인 방법은 label을 사용하는 것이다.
Uncategorized pods in MSA architecture

3.3.1 Introducing labels

간단하지만 k8s 에서 제공하는 강력한 기능이다.
key-value 로 resource에 명시하는 방법이다.
label selector 를 통해 resource 를 선택한다.
각 resource 는 하나 이상의 label 을 가질 수 있다.
resource 생성할 때 label 을 붙일 수 있고, 수정, 삭제가 가능하다
각 pod는 두 label 을 붙일 수 있다.
app, app, component, 혹은 MSA 파드에 해당한다
rel, app이 stable, beta 혹은 canary 에 해당하는지 나타낸다
Organizing pods in a MSA architecture with pod labels
개발자나 운영자는 이 label 을 통해 쉽게 cluster의 구조에 접근할 수 있다.

3.3.2 Specifying labels when creating a pod

apiVersion: v1 kind: Pod metadata: name: kubia-manual-v2 labels: creation_method: manual env: prod spec: containers: - image: luksa/kubia name: kubia ports: - containerPort: 8080 protocol: TCP
YAML
두 개의 label 을 붙였다.
kubectl create -f kubia-manual-v2.yaml
YAML
kubectl get po --show-labels
YAML
label을 직접 보자
kubectl get po -L creation_method,env
YAML
lable 을 사용해서 pod를 가져올 수도 있다.

3.3.3 Modifying labels of exiting pods

label 을 이미 존재하는 파드에 추가 하거나 수정 할 수도 있다.
kubectl label po kubia-manual creation_method=manual
YAML
이미 존재하는 kubia-manual 파드에 creation_method=manual 을 붙여보자
그렇다면 이미 존재하는 pod 의 env=debug, 로 변경할 수 있다.
kubectl label po kubia-manual-v2 env=debug --overwrite
YAML

3.4 label 셀렉터를 이용한 파드 부분 집합 나열

label 이 label selector 와 함께 사용되는 것이 중요하다. 특정 label 로 태그된 파드의 부분 집합을 선택해 원하는 작업을 수행하자
레이블을 다음의 기준으로 filtering 한다.
특정한 키를 포함하거나, 포함하지 않는다
특정한 키와 값을 갖는 레이블
특정한 키를 갖고 있지만, 다른 값을 가진 레이블

3.4.1 레이블 셀렉터를 이용해 파드 나열

kubectl get po -l creation_method=manual
YAML
env 키는 갖고 있지만, value 는 중요하지 않는 레이블
kubectl get po -l env
YAML
env 키가 없는 레이블
kubectl get po -l '!env'
YAML
creation_method≠manual: manual 이 아닌 creation_method !=
env in (prod, devel): env 값이 prod, devel 인 파드
env notin (prod, devel): env 값이 prod , devel 이 아닌 파드
app=pc 인 레이블 셀렉터를 통해 MSA 에 속한 파드를 찾는다

3.4.2 레이블 셀렉터에서 여러 조건 사용

위 그림에서 제품이고, beta 를 찾아보자
app=pc,rel=beta
YAML
여러 개의 레이블 셀렉터를 이용한 파드 선택

3.5 레이블과 셀렉터를 이용해 파드 스케줄링 제한

워커 노드 전체에 걸쳐 무작위로 스케쥴링 하였는데 이것이 k8s 의 적절한 동작 방식이다.
파드가 어느 노드에 스케줄링 되는지 중요하지 않다
파드는 요청한 만큼의 CPU, 메모리를 할당받는다
다른 파드에서 해당 파드로 접근하는 것은 파드가 스케줄링된 노드에 영향을 받지 않는다.
하지만, GPU가 필요하던가 HDD, SSD 를 선택해야 하는 순간이 있다. k8s 아이디어는 application 이 실행되는 infra 를 캡슐화 하는 것인데 특정 노드에 이러한 리소스를 지정하여 의존성을 갖게 만들고 싶지 않을 수 있다.

3.5.1 워커 노드 분류에 레이블 사용

label 은 pod 뿐 아니라 다양한 k8s의 resource 에 붙일 수 있다.
GPU를 사용하는 node에 gpu=true 라는 label 을 붙여본다
kubectl label node gke-kubia-85f6-node-0rrx gpu=true
YAML
gpu=true 인 node 를 나열해보자
kubectl get nodes -l gpu=true
YAML

3.5.2 특정 노드에 파드 스케줄링

apiVersion: v1 kind: Pod metadata: name: kubia-gpu spec: nodeSelector: gpu: "true" containers: - image: luksa/kubia name: kubia
YAML
GPU를 사용하는 노드를 선택하여 배포하도록 한다
nodeSelector 필드를 추가하고, 파드를 생성할 때 gpu=true label 을 가지고 있는 node 를 선택하여 실행하도록 한다.

3.5.3 하나의 특정 노드로 스케줄링

하나의 노드가 특정되면 그 노드가 offline 일 때 파드는 스케줄링 되지 않을 수 있다.
개별 노드로 생각하지 말자
label 을 통해 특정 기준을 만족하는 node 의 논리적인 그룹을 생각해야 한다.
레이블 셀렉터의 중요성은 다음 두 장에서 replication-controller 와 service 에서 다룬다.

3.6 파드에 어노테이션 달기

pod 및 다른 object 는 label 외에 annotation 을 가질 수 있다.
label 과 비슷하지만 식별 정보는 가지지 않는다
object를 묶는데 사용할 수 없다.
annotation selector 같은 개념이 없다.
하지만 tool 에서 많이 사용 되는데, 특정 annotation 은 k8s에 의해 자동으로 추가되지만, 나머지는 사용자에 의해 수동으로 추가된다.
용도는 새로운 기능을 추가할 때 사용하는데 새로운 기능이라고 해서 바로 도입하는 것이 아니라 alpha, beta 테스트를 거치고 API 변경이 명확해 질 때 반영하기 위해서 이다.(field 가 나오는데 무슨 의미인지 모르겠음)
이때 새로운 필드가 도입되면서 관련된 annotation 사용이 중단된다.
annotation 을 유용하게 사용되는 경우는 pod 나 다른 API 오브젝트에 설명을 추가해 두는 것이다.
이렇게 해두면 사용자들의 각 object 의 정보를 신속하게 찾아볼 수 있다.

3.6.1 오브젝트의 annotation 조회

kubectl get po kubia-manual -o json
YAML
kubectl describe 명령을 이용하거나 yaml 전체 내용을 요청해야 한다.
autopilot.gke.io/resource-adjustment: '{"input":{"containers":[{}]}, "output":{"containers":[ {"limits":{"cpu":"500m","ephemeral-storage":"1Gi","memory":"2Gi"}, "requests":{"cpu":"500m","ephemeral-storage":"1Gi","memory":"2Gi"}} ]},"modified":true }' seccomp.security.alpha.kubernetes.io/pod: runtime/default
YAML
책과는 다르지만 여기선(GCP autopilot) 주어진 파드의 자원을 나타내고 있다.

3.6.2 어노테이션 추가 및 수정

kubectl annotate pod kubia-manual yevgnenll.me/annotation="foo bar"
YAML
추가된 annotation 을 확인할 수 있다.
describe 명령어를 통해서도 확인할 수 있다.
kubectl describe pod kubia-manual
YAML

3.7 네임스페이스를 사용한 리소스 그룹화

label 은 파드와 다른 object 를 그룹으로 묶는 것을 봤다.
object 는 여러개의 label을 가질 수 있기 때문에 이 그룹은 서로 겹쳐질 수 있다. 또한 label selector 를 명시적으로 지정하지 않으면 항상 모든 오브젝트를 보게 된다.
오브젝트를 겹치지 않고 그룹으로 분할하고자 할 때 namespace 를 사용한다.
k8s 의 namespace 는 오브젝트 이름(name)의 범위를 제공한다.
모든 resource 를 하나의 단일 namespace에 두는 대신에 여러 namespace 로 분할할 수 있으며, 이렇게 분리된 namespace 들은 같은 리소스 이름을 다른 namespace 에 걸쳐 여러번 사용할 수 있다

3.7.1 namespace 의 필요성

리소스를 production, dev, QA 로 나누는 상황에서 namespace 는 가치가 있다.
똑같은 resource name 을 각각 나누어진 namespace 에서 동일하게 사용하는 것 이다.

3.7.2 다른 네임스페이스와 pod 살펴보기

kubectl get ns
YAML
지금까지 namespace 를 지정하지 않았기 때문에 default ns 에서만 작업했다.
그런데 목록을 보면 kube-publickube-system 도 존재함을 알 수 있다.
여기서 kube-system을 살펴보자
kubectl get po --namespace kube-system
YAML
겁나게 많다 ㅠ
여기서 반환된 pod 는 책 후반부에서 다룬다.
이름으로 추측하면 k8s의 system 과 관련된 파드들이다.
만약 이 pod 들이 default 로 들어가서 내가 만든 pod 와 섞인다면 관리가 매우 불편할 것이다.
namespace 를 사용해 리소스를 겹치지 않는 그룹으로 분리할 수 있다.
여러 사용자 또는 그룹으로 분리하면 다른 사용자의 pod 를 실수로 삭제하는 것을 방지한다
리소스 이름에 관한 접근 범위를 제공하기 때문에 리소스 이름이 충돌하는 것을 걱정할 필요가 없다.
사용자가 특정 리소스에 접근하지 못하는 방법은 12~14장에서 다룬다

3.7.3 네임스페이스 생성

yaml 파일을 통해 k8s API로 생성할 수 있다.

yaml 파일로 생성

apiVersion: v1 kind: Namespace # ns 정의 metadata: name: custom-namespace # 사용할 이름
YAML
kubectl create -f custom-ns.yaml
YAML

3.7.4 다른 네임스페이스의 오브젝트 관리

생성한 namespace 안에 resource 를 만들기위해 metadata 섹션에 namespace 항목을 추가하거나 명령어에서 추가하는 방법도 있다.
kubectl create -f kubia-manual.yaml -n custom-namespace
YAML
이제 다른 namespace 안에 object를 나열하거나 annotation 달기, 수정 삭제는 --namespace 혹은 -n 플래그를 붙여 kubectl 에 전달해야 한다.
만약 전달하지 않으면 default 리소스로 작업을 수행한다.
alias kcd='kubectl config set-conext $(kubectl config current-context) --namespace`
YAML
이 alias 를 지정해두면 namespace 를 쉽게 전환할 수 있다. (kcd some-namespace)

3.7.5 namespace 가 제공하는 격리 이해

namespace 가 제공하는 격리는 실행중인 오브젝트에 대한 격리가 아니다
예를 들어, namespace 가 다른 pod 는 통신을 못하는게 아니라 가능하다. → 이것은 네트워크 설정
IP 주소만 알면 일단 요청을 보낼 수 있다.
namespace 는 사용자가 운영하는데 있어 대상 리소스 범위를 제한한다.

3.8 파드 중지와 제거

지금까지 학습한 pod 를 제거하도록 한다.

3.8.1 이름으로 pod 삭제

kubectl delete po kubia-gpu
YAML
pod 를 삭제하면 해당 pod 에 있는 모든 container 를 삭제한다.

3.8.2 label selector 를 이용한 파드 삭제

kubectl delete po -l creation_method=manual
YAML
rel=canary 레이블 셀렉터를 이용해 모든 canary 파드 삭제

3.8.3 namespace 로 파드 제거

kubectl delete ns custom-namspace
YAML
custom-namespace 라는 namespace 와 그 namespace 안에 있는 모든 pod 를 삭제한다.
확인해보면 다 삭제됨

3.8.2 네임스페이스는 유지하고 네임스페이스 안에 있는 파드 삭제

다시 kubia-manual과 kubia-manual-v2 파드를 custom-namespace 로 띄운다
책에서 말하는 kubectl run 명령으로 생성한 파드가 없으므로 다음 명령어만 확인한다
kubectl delete po --all
YAML
현재 네임스페이스의 모든 파드 삭제

3.8.5 네임스페이스에서 (거의) 모든 리소스 삭제

kubectl delete all --all
YAML
그냥 모든 리소스를 삭제한다.
TOP