Cka 정리1
CKA 정리1
위 방대한 강의를 듣고 정리한 내용을 러프하게 적어놓는다. (강의 link) 나의 CKA 공부는 “공부하는 무니”님의 블로그를 기반으로 했다. (블로그 link)
docker & containerd
쿠버네티스는 CRI(container runtime interface)를 통해 어떤 공급업체든 OCI(open container initiative)을 만족하는 이상 컨테이너 런타임으로 작업하게 해주었다
- oci
- imagespec과 runtimespec으로 구성됨
- imagespec: 이미지를 어떻게 만들어야하는지 정의, 이미지 빌드 방식에 대한 기준을 정의
- runtimespec: 이미지 런타임이 어떻게 개발돼야하는지
- 도커와의 지원
- 도커의 conatinerd를 사용하면서 도커와 dockershim 지원을 중단함.
- 단, 도커는 여전히 oci의 imagespec을 따르므로 도커 이미지를 지원하지 않는건 아님 컨테이너 런타임 도구로의 도커가 끝난 것
- Containerd
- 도커의 일부였지만 지금은 독립한 프로젝트
- 도커의 다른 여러 기능이 필요하지 않다면 도커 없이 containerd만 설치할 수 있다
- 일반적으로 도커를 설치해 명령을 실행하는 컨테이너를 실행했는데 도커 없이 컨테이너만 있으면 어떡하나?
- containerd에 들어있는 ctr이라는 도구를 통해서 가능. 하지만 제한된 기능이고 디버깅 정도 가능. 유저 친화적이지 않음
- ctr image pull, run이 가능하지만 prod용도로 쓸 것은 아님
- nerdctl이라는 도구가 있는데 도커 cli와 유사하며 최신 기능도 사용 가능.
- crictl 이라는 쿠버네티스 커뮤니티에서 만든 cri(container runtime interface) 용 컨트롤러가 있다.
- ctr과 nerdctl은 containerd진영에서 만들었고 containerd를 위해 만들어졌으나 crictl은 k8s 관점으로 여러 container runtime에 걸쳐 작동함
- 디버깅 목적이며 컨테이너 생성은 곤란함. kubelet과 깊은 연관. cri로 컨테이너 생성하면 kubelet 예하 관리 컨테이너가 아니므로 kubelet이 지울것임
- crictl은 pods를 인식할 수 있다. 그래서 pod list를 볼 수도 있다
Core Concepts
etcd for beginner
what’s etcd
- distributed reliable key & value store that simple, secure & fast
etcd in k8s
클러스터의 모든 정보를 저장한다.
- nodes, pods, configs, secrets, configs, accounts, roles, bindings
Setup
- scratch부터 구성하는 방법과 kubeadm을 통해 구성하는 방법 두개가 있다.
Setup - Manual
- wget으로 바이너리 파일 etcd를 직접 받고 서비스로 관리하는 것
- 여러 설정이 있고 고정된 단 하나의 설정 advertise-client-urls가 있다.
- https:{internal-ip}:2379
Setup - Kubeadm
- kubeadm을 통해 etcd를 설치하면 kube-system이라는 namespace을 가진 pod로 배포하게 된다
- 쿠버네티스가 etcd에 저장한 모든 키를 보고싶다면 etcdctl get / –prefix -keys-only
- k8s는 특정 데이터 경로에 구조적으로 저장한다.
- 루트 경로는 /registry 이며 하위에 minions, nodes pods replicasets, delpoyment 등등이 있다
- 고가용성 서버에서의 대응
- 마스터노드가 여럿인 곳에서는 etcd서버들이 서로 연결돼야한다.
- 그래서 etcd 서비스 설정에는 initial-cluster 옵션이 있고 서버마다 controller-0 이런식으로 다 url을 조절할 수 있다
16: Kube - API Server
- kubectl 명령어가 아니라도 api requst를 보내서 정보를 얻거나 요청할 수 있다. (신규 팟 생성 예시)
- Authenticate user
- Validate request
- Retrieve data
- Update etcd (신규 pod 생성 요청 작성)
- Scheduler (api 서버 모니터링 중 노드없는 pod 발견하고 새로 띄우는 명령 전달, api 서버는 이를 듣고 적당한 워커 노드의 kubelet에 연결
17: Kube - Controller Manager
컨트롤러는 기본적으로 상태를 주기적으로 확인하고 원하는 상태가 되도록 관리하고 유지하는 것이다. Node Controller 예시
- 상태확인
- 상황 교정
- 노드 모니터 주기 (5초)
- unhealthy mark 한 뒤, 노드 모니터 유예 기간 (40초)
- pod 재실행 (5분) 위처럼 다양한 각 구성요소 controller들로 k8s는 구성된다. 하지만 이 모든 컨트롤러를 다루는 컨트롤러 매니저라는 하나의 프로세스로 패키지화됨
- 옵션 수정 가능
- 모니터링 주기
- grace 기간
- pod 방출 timeout kube controller manager server 옵션은 어떻게 봐야 할까?
- kubeadm으로 설치한 경우
- pod으로 떠있기 때문에 kubectl get pods -n kube-system 에 잡힌다
- kube-controller-manager-master라는 이름으로 존재
- /etc/kuberneres/manifests/kube-controller-manager-master.yaml 파일에 명시됨
18: kube scheduler
스케쥴러는 노드에 파드를 스케쥴링한다. 어떤 파드가 어떤 노드로 가야하는지에 대한 경로 설정만 담당한다. 실제 노드에 파드를 생성하는 일은 kubelet(선장)이 한다.
- 알맞은 컨테이너를 알맞은 배에 적재할 수 있도록 스케줄러(컨테이너 옮기는 거중기)가 있는 것
- 싣고자 하는 컨테이너를 배가 수용할 수 있는지 확인!
- ex) cpu 10개를 원하는 pod 요청이 들어오면 scheduler는 노드들을 필터링하고 순위를 매긴다
- 10개를 만족하지 못하는 노드들은 후보에서 제거한다.
- 가장 많은 여유 자원을 가진 노드부터 순위를 매긴다.
- 이런 조건들은 커스텀 가능
19: kubelet
배(노드)의 선장과 같고 배에서 일어나는 모든 행동을 주도한다
- 마스터 노드(배)와 유일한 연락망임 (API Server를 통해)
- 마스터 노드의 kube-scheduler의 명령에 따라 배에 컨테이너를 선적하거나 제거함
- pod(컨테이너) 생성 요청을 받으면 kublet은 cri를 통해 도커 이미지를 pull하고 받아오고 띄운다
- 선박과(노드) 컨테이너의 상태를 일정 간격으로 보고함 설치
- 지금까지 컴포넌트들과 다르게 kubeadm을 통해 설치해도 자동배포되지 않는다. kubelet은 항상 노드에 직접 설치해줘야함
20: kube proxy
- Kubernetes 클러스터 내에서 모든 파드는 다른 파드와 소통할 수 있습니다. 이는 클러스터에 대한 파드 네트워킹 솔루션 배포를 통해 수행됩니다. 파드 네트워크는 클러스터의 모든 노드에 걸쳐져 있고, 모든 파드에 연결되어 있는 internal virtual network 입니다. 이 네트워크를 통해, 파드들은 서로 소통할 수 있습니다.
- 파드끼리 항상 소통 가능하려면 공통 상위 껍질인 서비스를 통해 통신할 수 있다.
- 서비스는 이름과 ip를 가지면서 쿠버네티스 메모리에만 존재하는 추상 컴포넌트이다
- 여기서 kube-proxy가 등장합니다. kube-proxy는 쿠버네티스 클러스터의 각 노드에서 실행되는 프로세스입니다(에이전트). kube-proxy의 임무는 새로운 서비스를 찾고, 새로운 서비스가 생성될 때마다 각 노드에 해당 서비스에 대한 트래픽을 백엔드 파드로 전달하기 위한 적절한 규칙을 생성하는 것입니다.
- kube-proxy는 실제 파드 IP를 서비스 IP로 변경한다. 설치
- kubeadm은 kube-proxy를 각 노드의 파드로 배포한다. 실제로는 DaemonSet이라는 이름으로 배포되며 항상 클러스터의 각 노드에서 단일 파드로 배포된다.
21: Recap - Pods
- 멀티컨테이너 파드는 굉장히 드물다. 보통 파드와 컨테이너는 1:1 대응으로 생각하면 좋음. 확장성에 있어 파드 내 컨테이너를 더띄우는게 아니고 최소단위인 파드를 더 띄우는 것
22: PODs with YAML
Kubernetes definition file은 최상위 레벨에 항상 아래 4가지가 포함됩니다.
- apiVersion
- kind
- metedata
- spec
ApiVersion
- object를 생성하는데 사용하는 Kubernetes API 버젼 의미
| kind
| version
| | ————— | ———— | | POD
| v1
| | Service
| v1
| | ReplicaSet
| apps/v1
| | Deployment
| apps/v1
|
kind
- 만들려 하는 object의 타입 metadata
- name, labels 등과 같이 object에 대한 데이터들이다. apiVersion: v1 kind: Pod metatdata: name: myapp-pod labels: app: myapp type: front-end spec
labels은 나중에 object를 식별하기 위해 주는 값 labels 예하의 값으로 어떤 값이든 key-value 형태로 추가할 수 있다. spec
- k8s에 제공할 obejct 관련 추가 정보를 제공하는 곳
- spec 또한 dictionary인데 list 형태의 containers 속성을 추가해 컨테이너와 파드에 필요한 이미지를 지정할 수 있다. spec: containers:
- name: nginx-container # 컨테이너 이름 image: nginx # 이미지명 실행
- kubectl create -f pod-definition.yml
23: Demo - PODs with YAML#
kubectl run 대신 파드 사양이 포함된 YAML 파일을 사용해 파드를 생성할 것이다. 파드 생성
- kubectl apply -f pod.yaml Replica는 무엇이며 왜 Replication controller가 필요한가?
- High Availability (고 가용성)을 갖고 싶다.
- 어떤 이유로 애플리케이션이 죽었을 때에도 유저가 다른 실행중인 애플리케이션에 접근할 수 있었으면 좋겠다.
- Replication Controller는 클러스터에서 파드의 여러 인스턴스를 실행하는 데 도움을 준다. 기존 파트가 죽었을 때 새 파드를 자동으로 실행해서 지정된 수의 파드가 항상 실행중인 것을 보장한다. 이 방법으로 고 가용성(Hight Availability)를 가진다.
- Load Balancing & Scaling
- 부하 분산을 위해 여러 파드를 생성하고 노드에 자리가 부족해지만 다른 노드에 추가 파드를 배포할 수 있다.
- Replication controller는 클러스터의 여러 노드에 걸쳐있기 때문에 여러 노드에서 파드와 애플리케이션을 로드 밸런싱하는 데 도움이 됩니다.
ReplicaSet과 Replication Controller의 차이점
- 둘다 같은 목적이지만 같은것은 아니다. replication controller가 더 오래된 기술이고 replicaset에 의해 대체됨
- replication controller
- $ kubectl create -f rc-definition.yaml
- 그럼 replicaset처럼 지정한 replicas 개수만큼 pod가 생긴다.
- kind: ReplicationController 으로 생성했기 때문에 $ kubectl get replicationcontroller 으로 찾아야한다
- replicasets
- apiVersion: apps/v1
- spec 예하에 template, replicas 가 들어가는건 똑같지만 selector라는 것을 추가로 작성해주어야 한다
- selector란 파드가 어떤 ReplicaSet에 속해있는지 나타내는것. 물론 이런 구분자 역할로는 spec - template - label 로 이미 하고있지만 ReplicaSet은 함께 만들어진 파드가 아닌 다른 ReplicaSet의 파드도 관리할 수 있기 때문이다.
selector는 ReplicationController에서는 필수 필드는 아니지만 사용가능합니다. ReplicationController에서 selector를 사용하지 않으면, pod definition file에서 제공된 labels와 동일하다고 가정합니다. ReplicaSet에서 selector는 필수이며 matchLabels를 작성해야 합니다. matchLabels은 파드의 labels와 매칭되는 정보이다
구분자 및 필터링에 있어 labels & selector 차이는?
- replicaset은 정의한 replicas 만큼의 파드를 보존한다. 일부가 종료돼 갯수가 부족해지면 새로 띄운다. 이렇듯 replicaset은 사실 pod들을 모니터링하는 프로세스이다. 그런데 replicaset은 자기 pod가 누군지 어떻게 알고 관리 모니터링 하는 걸까?
- labels를 ReplicaSet의 필터로 제공할 수 있다. selector의 matchLabels필터와 pod 생성 시에도 동일한 label을 이용했다.
- 그래서 만일 현재 같은 label을 가진 pod 3개가 떠있다면 이들을 묶어관리하기 위해 새 pod를 띄우지 않으면서 관리 목적으로 ReplicaSet을 생성하는 방법도 있다.
Replicaset을 scale하는 방법 (3개의 Replicas를 6개로 확장하기)
- 정의 파일의 replicas 수를 6개로 업데이트하기
- 파일 내 replicas 개수 업데이트 (3 -> 6)
- $ kubectl replace -f replicaset-definition.yaml or $ kubectl apply -f replicaset-definition.yaml
- $ kubectl scale —replicas=6 -f replicaset-definition.yaml
- $ kubectl scale —replicas=6 replicaset myapp-replicaset
- type(replicaset)과 name(myapp-replicaset)을 이용해 kubectl scale 커맨드를 실행
- 파일이 자동으로 바뀌지는 않고 여전히 정의 파일의 replicas는 3이다.
31: Deployments
여러 도커 인스턴스들로 서비스를 scale out해 운용할 때 서비스 버젼을 업데이트할 때 오류가 발생할 수 있고 이때는 최근 변경사항을 취소하는 롤백이 필요할 수 있다. deployment를 생성하면 자동으로 내부적으로 replicaset을 사용한다. Deployment는 원활한 업그레이드를 위해 롤링 업데이트 사용, 롤백, 중지, 재개하는 기능을 제공합니다. deployment 정의 파일을 만들 때는 replicaset의 정의 파일에서 kind부분만 바꿔주면 된다. kind: Deployment
32: Certification Tip!
yaml파일을 직접 구성하거나 복붙하는 일은 어렵다. 간단히 kubectl run 에 파라미터들을 제공해서 사용하는 방식을 많이 사용하길 바란다.
$ kubectl create deployment –image=nginx nginx –replicas=4 –dry-run=client -o yaml > nginx-deployment.yaml
nginx이미지 활용한 deployment를 만드는데 pod개수는 4개이고 실행하지는 않고 yaml파일을 만들어 nginx-deployment.yaml로 저장하라
35: Services - NodePort
애플리케이션을 내부 컴포넌트와 외부 애플리케이션 그리고 사용자들과 연결해준다. k8s의 각 모듈 파드들(프론트/백/외부모듈)이 마이크로서비스로 구현돼있을 때 서비스는 이들을 느슨하게 이어주는 역할을 한다. (loose coupling)
(유저 - 노드 - 파드) request를 연결해주는 무언가가 필요함 (유저 - 노드) / (노드 - 파드)를 연결지어주는 k8s service
서비스타입
- NodePort
- 노드와 노드 내 파드에서 내부 파드에 접근 가능하도록 해주는 서비스
spec: type: NodePort # 서비스 유형 (NodePort/CLusterIP/LoadBalancer) ports:
targetPort: 80 port: 80 nodePort: 30008 selector: # labels와 selector를 이용해서 서비스를 파드에 연결해야함 app: myapp
- TargetPort: 실제 웹서비스가 있는 파드의 포트로 서비스가 리퀘스트를 전달할 곳이기 때문에 TargetPort이다
- port: 서비스 자체의 포트. 서비스는 노드 내 가상 서버와 같다. 클러스터 내부의 자체 IP주소인 Cluster IP이기도 함
- nodePort: 노드 자체에 존재하는 포트로 외부에서 웹 서버에 접근하는 데 사용하는 노드 포트
create
- $ kubectl create -f service-definition.yaml list
- $ kubectl get services access app
- $ curl http://192.168.1.2:30008
이건 단일 파드에 매핑된 서비스이다. 허나 prod에서는 여러 인스턴스가 있을 것이고 파드도 여러개 있을 것이고 파드들은 하나의 label로 묶여있을 것이다. 그래서 서비스는 파드로 쏟아지는 요청들을 분산할 수 있다.
파드가 여러 노드에 분산돼있다면?
- 서비스를 만들 때 k8s는 자동으로 클러스터의 모든 노드에서 대상 포트를 매핑하도록 만든다. 이때 클러스터의 모든 노드는 동일한 포트 번호를 사용한다. (노드포트)
- ClusterIP
- 서로 다른 서비스(ex. 프론트 - 백) 간 소통을 가능하게 하기 위해 클러스터 안에 Virtual IP를 생성한다.
- LoadBalancer
- frontend tier 웹 서버에 걸쳐잇는 부하를 분산시킬 수 있다.
36: Services - Cluster IP
프론트파드 세트는 백엔드 파드 세트에 연결해야하는데 프론트파드는 수많은 백엔드 파드 세트의 파드들 중에서 누구에게 요청해야하는가?
Kubernetes 서비스는 Pod를 함께 그룹화하여 Pod에 액세스할 수 있는 단일 인터페이스(Virtual IP)를 제공합니다. 다른 파드가 서비스에 액세스하면 리퀘스트가 파드 중 하나로 전달한다. 그렇기 때문에 우리는 쿠버네티스 클러스터를 이용하여 쉽게 MSA 애플리케이션을 구축할 수 있다. 각 서비스에는 IP와 이름이 할당됩니다. 클러스터 내부에서 사용하는 이름으로, 이러한 타입의 서비스를 Cluster IP라고 한다
37: Services - Loadbalancer
노드포트 덕분에 외부 유저들 혹은 애플리케이션에서 여러 노드에 걸친 서비스에 접근할 수 있다. 노드들의 ip + 노드포드 조합으로 여러가지 방식으로 말이다.
- 192.168.10.1~4 (예시)
- 30003 (노드포트 예시)
하지만 유저들 혹은 앱 측면에서 원하는 것은 위처럼 ip와 포트를 조합한 url이 아니라 단일 url인 도메인으로 접근하는 것이다. www.naver.com 이를 위해서는 신규 vm에 로드 밸런서를 설치하고 연결하는 과정이 필요하다.
- AJ/Proxy
- nginx 로드밸런서 달린 서비스를 활용해 도메인 서빙하는 것. 혹은 k8s는 다른 클라우드 플랫폼 (aws, gcp 등)의 네이티브 로드 밸런서와의 연계를 지원한다.
40: Namespaces
격리, 리소스 할당 등의 고유한 정책을 가질 수 있음. k8s는 기본적으로 클러스터가 처음 설정될 때 Default, kube-system, kube-public 세 개의 Namespaces를 만든다. 풀네임은 다음 형식과 같다. servicename.namespace.svc.cluster.local (db 서비스 예시는 dbservice.dev.svc.cluster.local)
- cluster.local은 k8s 클러스터의 default 도메인 이름
- svc는 namespace 뒤에 오며 서비스의 하위 도메인 $ kubectl get pods 명령어는 사실 default namespace의 파드들만 조회하는 것이다. 다른 namespace의 pod를 조회하려면 —namespace 옵션 줘야함
- $ kubectl get pods —namespace=kube-system
namespace를 설정하는 법
- create
- $ kubectl create -f pod-definition.yml —namespace=dev
- definition.yml 에 추가
- metadata: name: myapp-pod namespace: dev labels: app: myapp type: front-end
namespace를 만드는 법
- 파일 정의 및 생성
- apiVersion: v1 kind: NameSpace metadata: name: dev
- $ kubectl create -f namespace-dev.yaml
- cli
- $ kubectl create namespace dev 기준 default namespace를 switch하는 법
- $ kubectl config set-context $(kubectl config current-context) —namespace=prod namespace로 리소스를 제한하는 방법
- 리소스 할당량을 만들면 됨. definition file 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
43: Imperative vs Declarative
명령형(direction)과 선언형은 옛날 택시와 같다. 옛날에는 좌회전 우회전을 지시해야했지만 지금은 목적지만 말하면 된다. (시스템이 알아서 올바른 경로를 파악)
쿠버네티스 세계에서는 인프라 관리를 imperative 방식으로 한다는 것은 파드를 생성할 때 kubectl run커맨드를 사용하는 것과 같습니다. deployment를 생성할 때에는 kubectl create deployment를, service를 생성할때는 kubectl expose 를 사용합니다.그리고 kubectl edit커맨드는 이미 존재하는 오브젝트를 수정하는데 쓰입니다. Deployment나 ReplicaSet을 확장하기 위해서는 kubectl scale커맨드를 사용합니다. deployment에서 image를 업데이트하려면 kubectl set image 커맨드를 사용하면 됩니다. 우리는 오브젝트를 만들고, 업데이트하고, 삭제하는 니즈를 위해 어떻게해야 하는지 인프라에게 정확하게 말하고 있습니다.
declarative 접근 방식은 다음과 같습니다. Kubernetes 클러스터의 애플리케이션 및 서비스의예상되는 상태를 정의하는 file 세트를 만드는 것입니다. 그리고 단 하나의 커맨드 kubectl apply커맨드로 Kubernetes는 configuration file을 읽을 수 있어야 합니다. 그리고 인프라를 예상되는 상태로 만들기 위해 해야 할 일을 스스로 결정합니다
apply 커맨드는 create과 다르다. 동일한 pod가 존재하면 배포 실패하는 create과 달리 apply는 변경점을 확인하고 배포함. 그 말은 apply는 변경점(해야할일)을 파악하고 알아서 한다는 뜻
Imperative # 접근방식
Commands
오브젝트 생성 위한 run, create, expose 커맨드, 기존에 오브젝트를 업데이트하는 edit, scale, set.
- 장점
- yaml파일 없이 빠르게 오브젝트 생성 및 수정
- 단점
수정방법
- kubectl edit
- 이미지 이름 및 버젼 수정에 사용가능. definition yaml 수정 화면이 열리고 변경 완료하면 live object가 변경됨. 하지만 origin yaml definition은 변경되지 않음.
- 내가 더이상 yaml 정의문에 의존하지 않을 예정일 때 사용가능. 팀원이 바꿔도 그 내역을 모름
- kubectl replace
- 로컬 yaml definition을 수정하고 명령어 수행하여 오브젝트 업데이트.
- 변경사항이 기록되고 CI에서 검토할 수도 있음
- 오브젝트를 제거하고 새로 만들어야 할 때 force 옵션을 줘서 수행가능
kubectl create는 오브젝트가 이미 존재하는 경우 에러를 발생시킴. 오브젝트가 없는 경우 replace 또한 에러를 발생시킴. 이렇듯 imperative 방식은 현재 구성을 항상 체크해야하므로 관리자에게 부담을 준다.
Declarative 접근방식
오브젝트 configuration file을 사용하며 create나 replace가 아닌 apply 명령어를 사용한다. kubectl apply 커맨드로 할 수 있는 일
- 애플리케이션의 어떠한 변경사항
- 이미지 업데이트
- configuration file의 필드 업데이트
- 새로운 configuration file 추가
- 완전히 새로운 오브젝트 하지만 시험을 보기 위해서는 시간 절약 차원에서 imperative를 연습하는게 좋다.
44: Certification Tips - Imperative Commands
대부분 definition 파일을 써서 declarative하게 작업하지만, imperative command는 일회성 작업을 빠르게 완료하고 Definition 템플릿을 쉽게 생성하는 데 도움을 준다. 이를 활용하여 시험 중에 상당한 시간을 절약할 수 있다.
반드시 알아야 하는 옵션 2개
- –dry-run: default로 커맨드가 실행되는 즉시 리소스가 생성됨. 단순 커맨드 테스트의 경우 -dry-run=client 옵션 존재. 이는 리소스를 생성하는 것이 아니라 리소스를 생성할 수 있는지 여부와 커맨드가 올바른지 여부를 알려줌.
- 미리 준비된 시험 환경에서 export do=”–dry-run=client -o yaml” 해두는게 좋다
- yaml: resource definition을 YAML 형식으로 화면에 출력한다.
위 두가지를 함께 사용하여 처음부터 파일을 만드는 대신, 리소스 definition 파일을 빠르게 생성한 다음, 필요에 따라 리소스를 수정하고 만들 수 있다.
Pod
create an nginx pod
- kubectl run nginx —image=nginx generate pod manifest yaml file (-o yaml). don’t create it(—dry-run)
- kubectl run nginx —image=nginx —dry-run=client -o yaml
- nginx 이미지로 pod를 만드는 명령이지만 실제로 실행하지 않으면서 yaml 파일을 남기게 함
Deployment
create a deployment
- kubectl create deployment —image=nginx nginx generate deployment yaml file. don’t create it
- kubectl create deployment —image=nginx nginx —dry-run=client -o yaml 같은 결과지만 yaml 정의에 남기고 수정하기
- kubectl create deployment nginx —image=nginx —dry-run=client -o yaml > nginx-deployment.yaml
deployment를 생성하기 전에 YAML파일에서 replicas나 다른 필드를 수정할 수 있다 generate deployment with 4 replicas
- kubectl create deployment nginx —image=nginx —replicas=4
kubectl scale 커맨드를 활용한 deployment 확장
- kubectl scale deplyment nginx —replicas=4
서비스
Create a Service named redis-service of type ClusterIP to expose pod redis on port 6379
- $ kubectl expose pod redis –port=6379 –name redis-service –dry-run=client -o yaml (이렇게 하면 selectors에 자동으로 pod의 labels가 들어갑니다.)
혹은
- $ kubectl create service clusterip redis –tcp=6379:6379 –dry-run=client -o yaml
- (이렇게 하면 파드 labels를 selector로 사용하지 않고 대신 selector를 app=redis로 가정합니다. selector를 옵션으로 전달할 수 없습니다. 따라서 파드에 다른 label 세트가 있으면 잘 작동하지 않습니다. 따라서 service를 생성하기 전에 파일을 생성하고 selector를 수정하세요.)
Create a Service named nginx of type NodePort to expose pod nginx’s port 80 on port 30080 on the nodes:
$ kubectl expose pod nginx –type=NodePort –port=80 –name=nginx-service –dry-run=client -o yaml
(이렇게 하면 파드의 labels이 자동으로 selector로 사용되지만 노드__ 포트는 지정할 수 __없습니다. 파드로 서비스를 생성하기 전에 definition 파일을 생성한 다음 수동으로 노드 포트를 추가해야 합니다.)
혹은 $ kubectl create service nodeport nginx –tcp=80:80 –node-port=30080 –dry-run=client -o yaml (이렇게 하면 파드 labels을 selectors로 사용하지 않습니다)
위의 두 커맨드 모두 고유한 문제가 있습니다. 하나는 selector를 허용할 수 없고, 다른 하나는 node port를 허용할 수 없습니다. 따라서kubectl expose 명령을 사용하는 것이 좋습니다. node port를 지정해야 하는 경우 동일한 명령을 사용하여 definition 파일을 생성하고 node port를 수동으로 입력한 후 서비스를 생성합니다.
47: Kubectl Apply Command
apply커맨드는 로컬 configuration file, kubernetes의 live object definition 그리고 ‘마지막으로 적용된 구성’ 을 변경사항 결정전에 고려합니다. 따라서 apply 커맨드를 실행할 때, 오브젝트가 아직 존재하지 않는 경우, 오브젝트가 생성됩니다. 오브젝트가 생성되면 오브젝트 configuration(우리가 로컬에서 만든 것과 유사)은 쿠버네티스 내에서 생성됩니다.
그러나 오브젝트의 상태를 저장하는 추가 필드가 있습니다. 이것이 Kubernetes 클러스터에 있는 오브젝트의 live configuration입니다. 어떤 방식으로 오브젝트를 생성했든지 이곳에 내부적으로 정보를 저장한다.
우리가 작성한 로컬 오브젝트 configuration 파일의 YAML 버전은 json 형식으로 변환되고, 마지막으로 적용된 configuration으로 저장됩니다. 앞으로 오브젝트에 대한 모든 업데이트에 대해 어떤 변경이 live object에 적용되는지 세 가지 모두 비교하겠습니다.
마지막으로 적용된 configuration은 로컬 파일에서 어떤 필드가 제거되었는지 파악하는 데 도움을 준다. 마지막 적용 대비 로컬과의 차이를 확인할 수 있음
저장된 곳
- 로컬 파일: 로컬
- live object configuration: k8s memory
- last applied configuration: k8s 클러스터 자체의 live object configuration에 json 형태의 주석으로 존재 따라서 apply명령을 사용해야만 완료되고, kubectl create, replace 명령은 last applied configuration 을 저장하지 않는다.
apply 커맨드를 사용할 때마다 live configuration내에서 변경사항을 결정하기 위해 local part definition file, live object configuration, last applied configuration stored within the live object configuration file 세가지 섹션을 모두 비교하는 작업이 이뤄진다
51: Manual Scheduling
노드에 파드를 수동으로 스케쥴링하는 다양한 방법들 스케쥴러 작동방식
- pod definition file에는 “node name” 필드가 있고 이는 k8s가 자동으로 추가하는 필드다
- 스케쥴러는 모든 파드들을 검사하면서 Node Name 속성이 설정되지 않은 파드를 찾는다(스케쥴링 후보)
- 스케쥴링 알고리즘을 사용하여 파드에 적합한 노드 선택 및 파드 할당(스케쥴링)합니다.
- 이때 바인딩 오브젝트를 생성하면서 파드의 Node Name에 해당 노드의 이름 기입
No Scheduler
모니터링하고 스케쥴링할 스케쥴러가 없다면? 파드는 계속 보류됨(Pending) -> 스케쥴러 없이 파드를 스케쥴링하는 가장 쉬운 방법은 파드를 생성할 때 Node Name에 직접 노드 이름을 지정하는 것. => 이렇게 하면 파드가 지정된 노드에 할당됨
- 파드를 삭제하고 다시 만드는 작업은 kubectl replace –force -f nginx.yaml 을 하면 삭제와 생성을 해줌
+Node Name에 노드를 지정하는건 파드를 만들 때만 가능함. 이미 생성된 파드를 노드에 할당하려 할 때 k8s는 파드의 Node Name 속성을 수정하지 못하게 했으므로 바인딩 오브젝트를 만들고 파드의 바인딩 API에 직접 POST request를 보내면 된다. 그리고 이는 실제로 스케쥴러가 수행하는 작업임
바인딩 오브젝트 만들기 apiVersion: v1 kind: Binding metadata: name: nginx target: apiVersion: v1 kind: Node name: node02
req를 보낼 때는 YAML과 동일한 내용을 json으로 변환한 데이터 사용해야함 curl –header “Content-Type:application/json” –request POST –data ‘{bind-definition.yaml 내용 }’ http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
54: Labels and Selectors
분류, 그룹화 및 필터링하는 표준 방법. k8s 오브젝트는 내부적으로 labels과 selector를 사용하여 서로 다른 오브젝트를 연결한다.
Labels
- 정의 파일에서 metadata에 “labels”로 추가되며 key-value 형식으로 어떤 값이든 개수 상관 없이 가질 수 있다.
- $ kubectl run test_pod –image=nginx –labels=”app=App1” 처럼 가능 Select
- labels가 있는 파드를 선택할 때 selector 옵션과 함께 사용한다
- $ kubectl get pods –selector app=App1
Replicaset replicaset은 ReplicaSet의 metadata에서 한번 본인의 labels를 정의하고 (이 ReplicaSet을 검색해서 사용하고자 할 때 사용) spec의 selector에서 matchLabels로 기준점 label value들을 정한다. (이 ReplicaSet에서 연결할 pod 묶음의 label) 이후 하위 파드들을 구성하는 template에서 다시한번 labels를 정의해준다. (기준에 맞춰야하는 label)
- replicaset 생성시 레이블이 일치하면 ReplicaSet이 성공적으로 생성됨
Annotations
- label과 selector는 오브젝트를 그룹화하고 선택하는 데 사용되는 반면 주석은 정보 제공 목적으로 기타 세부 정보를 기록하는데 사용됨
- ex) 이름, 버젼, 빌드 정보, 도구 세부 정보 등 CI/CD에 알리기 위함. 연락처, 이메일 등
- label과 같이 metadata에 넣어주면 된다.
57: Taints and Tolerations
Pod와 Node의 관계를 알아보고, 파드가 노드에 배치될 때 이를 제한할 수 있는 방법을 다룬다
Taints and Tolerations
벌레가 사람에게 앉지 못하게 하도록 하는 비유를 든다 사람에게 앉지 않게 하기 위해 뿌려놓는 모기 퇴치제는 taint
- taint가 벌레를 제거한다.
- 해당 taint에 해당하지 않는 벌레가 있을 수 있다. 벌레가 사람에 앉는 경우
- 사람에게 taint가 있는가
- 벌레가 그 taint를 참아낼 수 있는가 벌레는 pod이고 사람은 node이다
- taints와 tolerations는 보안 또는 침입과 무관
- 스케쥴링 제한 설정용임
예시 1번 노드에 특정 use case나 애플리케이션을 위한 전용 리소스(예를 들면 gpu)가 있다고 가정할 때 1번 노드에는 해당 용도에 맞는 파드만 배치하고 싶다. 요구사항
- 1번 노드에 원치 않는 파드는 배치되지 않는다.
- 1번 노드에 특정한 파드만 배치된다.
- 1번 노드에 taint를 배치해 모든 파드가 그 노드에 배치되는 것을 막는다.
- 특정 파드를 1번 노드에 배치하고 싶을 경우 파드에 1번에서 설정한 taint를 견딜 수 있는 toleration을 설정한다.
+taint는 노드에 설정되고 toleration은 파드에 설정된다.
setting Taint $ kubectl taint nodes {node name} key=value:taint-effect
- 예시) $ kubectl taint nodes node1 app=test:NoSchedule
- 노드를 test 애플리케이션 전용으로 지정하려는 경우 key-value를 app=test로 설정
- test effect 3종
- NoSchedule: toleration이 없는 파드는 이 taint가 있는 노드에 스케쥴링되지 않음
- PreferNoSchedule: 시스템이 되도록 taint가 있는 노드에 파드를 배치하지 않으려하지만 가능은 함
- NoExcuse: toleration이 없는 새로운 파드는 노드에 배치되지 않으며, 이미 노드에 존재하는 파드는 toleration이 없다면 축출됨(evict).
setting Tolerations 파드 정의 파일의 spec 부분에 tolerations를 추가한다. 노드에 설정한 taint effect와 같은 것으로 설정한다. spec: containers:
- name: nginx-container image: nginx tolerations:
- key: “app” operator: “Equal” value: “test” effect: “NoSchedule”
+taint와 toleration은 파드가 특정 노드로 간다는 것을 의미하는게 아니고 대신, 노드가 특정 toleration을 가진 파드만 허용하는 것을 의미함.
그리고 사실 k8s 클러스터에는 워커노드와 마스터노드가 있다. 마스터노드는 파드를 호스팅하고 모든 관리 소프트웨어를 실행할 수 잇다. 하지만 스케쥴러는 마스터 노드에 파드를 스케쥴링하지 않는다. 왜냐면 k8s 클러스터가 처음 설정될 때 마스터노드에 taint가 자동으로 설정되어 파드가 마스터 노드에 스케쥴링되지 않도록 하기 때문.
$ kubectl describe node kubemaster | grep Taint 를 실행하면 node-role.kubernetes.io/master:NoSchedule 이 걸려있음을 알 수 있다. |
+그리고 특이한점은 tolerations의 value들은 모두 큰 따옴표 안에 구성돼야한다.
60: Node Selectors
큰 리소스가 필요한 파드는 큰 리소스를 가진 노드에 배치하기 위함이다.
- 방법1: 파드가 특정 노드에서만 실행되도록 제한하기
- 방법2: 노드 선택기를 이용하기
apiVersion: v1 kind: Pod metadata: name: myapp-pod spec: containers:
- name: data-processor image: data-processor nodeSelector: size: Large
근데 size와 large는 뭘까? 이건 사실 노드의 label이다. 스케쥴러는 이 label을 활용해 파드를 배치할 노드를 식별한다. 그래서 만약 nodeSelector를 이용하려면 미리 node에 label들을 지정해 두어야 한다.
Label Nodes 노드에 레이블을 지정하는건 cli command로 가능하다
$ kubectl label nodes {node_name} {label_key}={label_value} ex) $ kubectl label nodes node01 size=Large
Node Selector의 한계
- 단일 레이블과 selector를 활용했다. 요구사항이 복잡할 경우 대처 불가 ex) (Larger | Medium), not Small 등 그래서 이를 위해 Node Affinity 및 anti-affinity 기능이 등장하였다.
61: Node Affinity
파드를 특정한 노드에 호스팅하기 위해 등장함. Node Selector로 복잡한 조건을 만들 수 없다는 한계를 극복
예시1) size가 Large 혹은 Medium 규모의 큰 노드에 배치하고자 하는 경우 apiVersion: v1 kind: Pod metadata: name: myapp-pod spec: containers:
- name: data-processor image: data-processor affinity: nodeAffinity: requiredDuringschedulingIgnoredDuringExecution: nodeSelectorTerms:
- matchExpressions:
- key: size operator: In values:
- Large
- Medium
예시2) size가 Small이 아닌 노드에 파드를 배치하기 apiVersion: v1 kind: Pod metadata: name: myapp-pod spec: containers:
- name: data-processor image: data-processor affinity: nodeAffinity: requiredDuringschedulingIgnoredDuringExecution: nodeSelectorTerms:
- matchExpressions:
- key: size operator: NotIn values:
- Small
예시3) size가 Small인 노드가 없다고 가정하고 레이블이 존재하는 노드에 파드를 배치하기 apiVersion: v1 kind: Pod metadata: name: myapp-pod spec: containers:
- name: data-processor image: data-processor affinity: nodeAffinity: requiredDuringschedulingIgnoredDuringExecution: nodeSelectorTerms:
- matchExpressions:
- key: size operator: Exists
Node Affinity Types
NodeAffinity와 Node가 일치하지 않을 때는 어떻게 될까?
- 명시적으로 Large 노드에 파드를 배치하라 했는데 Large 레이블 붙은 노드가 없을 때
- 파드가 이미 배치돼있는데 노드의 레이블이 변경되는 경우
긴 문장처럼 생긴 Node Affinity Type가 해결해준다. 이는 Node Affinity와 관련된 스케쥴러의 동작을 정의한다.
- Active
- requiredDuringschedulingIgnoredDuringExecution
- preferredDuringschedulingIgnoredDuringExecution
- Planned
- requiredDuringschedulingRequiredDuringExecution
Node Affinity Type States
파드의 생명주기는 Node Affinity에 대해 두 상태를 갖는다.
- DuringScheduling
- DuringExecution
DuringScheduling 파드가 존재하지 않고 이제 막 생성된 상태. 이제 알맞은 노드에 배치해야하는 상황
- Required 일 때
- 파드를 알맞은 노드에 배치하는 것이 중요할 때 사용
- Node Affinity 조건을 만족하는 노드가 없으면 파드가 예약되지 않는다.
- Preferred 일 때
- 애플리케이션을 수행하는 것이 파드를 알맞은 노드에 배치하는 것보다 더 중요할 경우
- Node Affinity 규칙을 무시하고 사용 가능한 노드에 파드를 배치함.
- “스케쥴러야, 파드를 일치하는 노드에 배치하도록 최선을 다해줘, 근데 정 없으면 아무대나 놔둬”
DuringExecution 파드가 실행중이고, 노드 레이블 변경과 같이 Node Affinity에도 영향을 미치는 환경 변경이 발생한 상태
ex) 이전에 설정한 “size=Large” 레이블을 노드에서 제거했다면 노드에서 실행중인 파드는 어떻게 되는가? 하지만 NodeAffinity를 보면 둘 다 Ignored로 설정돼있다. 즉, 파드는 계속 실행되며 Node Affinity의 변경 사항은 일단 예약되면 파드에 영향을 미치지 않는다.
단, 새 옵션 planned Node Affinity Type은 DuringExecution 부분에 차이가 있다. (requiredDuringschedulingRequiredDuringExecution)
- Node Affinity를 충족하지 않는 노드에서 실행중인 파드를 제거함.
- 즉, Large 노드에서 실행중인 파드는 노드에서 Large 레이블이 제거되면 제거되거나 종료됨