본문 바로가기

DevOps/쿠버네티스(Kubernetes)

[CKA] Kubernetes Imperative(명령형) vs Declarative(선언형)

Kubernetes Imperative(명령형) vs Declarative(선언형)

 

Kubernetes의 명령적, 선언적 접근법에 대해 이야기해보자 지금까지 우리는 쿠버네티스에서 객체를 만들고 관리하는 다양한 방법을 알아보았다. 우리는 객체 구성 파일뿐만 아니라 명령을 실행하여 직접 객체를 만들었다.

이제 코드형 인프라(Infrastructure as Code, IaC)로서 인프라를 관리하는 데 있어 다양한 접근 방식이 있고, 명령적 접근 방식과 선언적 접근 방식으로 분류된다.

 

비유를 들어, 당신이 친구의 집을 방문하고 싶다고 하자. 과거에 당신은 택시를 타서 목적지까지 가는 방법을 운전자에게 단계별로 말해주었다. 예를 들어 B 거리로 우회전하고, C 거리로 가기 위해 좌회전하고, 그리고 나서 다른 도로를 탄다. D번가로 가서 무엇을 어떻게 해야 하는지 명시하는것이 명령적 접근법에서는 중요하다.

반면에, 오늘날에는 우버를 통해 택시를 예약할 때 최종 목적지를 지정한다. 예를 들어, 톰의 집까지 드라이브하는 것과 같다.
이 경우는 선언적 접근법이다. 차근차근 지시하는 게 아니라 우리는 최종 목적지를 지정한다. 우리는 최종 목적지를 선언하고 있고, 시스템은 목적지에 도달하기 위한 올바른 경로를 파악한다. 어떻게 할 것이 아니라 무엇을 할 것인지를 지정하는 것이 선언적 접근법이다.

 

# Imperative
1. Provision a VM by the name ‘web-server’
2. Install NGINX Software on it
3. Edit configuration file to use port ‘8080’
4. Edit configuration file to web path ‘/var/www/nginx’
5. Load web pages to ‘/var/www/nginx’ from GIT Repo - X
6. Start NGINX server

# Declarative
VM Name: web-server
Package: nginx
Port: 8080
Path: /var/www/nginx
Code: GIT Repo - X

 

그렇다면 이것이 우리가 코드형 인프라에서 배우고 있는 것과 무슨 관계가 있을까?
프로비저닝 인프라의 필수 접근 방식의 예로는 웹 서버라는 VM을 프로비저닝하고, 여기에 Nginx 소프트웨어를 설치하고, 포트 8080을 사용하도록 구성 파일을 편집하고, 웹 파일에 대한 경로를 설정하고, Git에서 저장소 소스 코드를 다운로드하고, 마지막으로 Nginx서버를 시작하는 등의 단계별 지침 집합이 있다. 그래서 여기에서는 우리는 무엇이 필요한지, 그리고 어떻게 해야하는지를 말하고있다.

선언적 접근법에서 우리는 우리의 요구 사항을 선언한다. 예를 들어, Nginx가 설치된  web-server라는 이름의 VM(포트 8080, 웹 파일 경로 정의, 애플리케이션의 소스 코드 저장 위치, 그리고 이 인프라를 구축하기 위해 필요한 모든 작업이 시스템 또는 소프트웨어에 의해 수행됨)이 필요하다고 말한다. 예를들어 오케스트레이션 도구인 Ansible, Puppet, Chef 또는 TerraForm과 같은것들은 단계별로 명령을 따를 필요가 없다.

그리고 명령적인 접근에서 첫 번째 단계에서 절반만 실행되면 어떻게 될까? 나머지 단계를 완료하기 위해 동일한 명령을 다시 제공하면 어떻게 될까? 이러한 상황을 처리하기 위해, 무언가가 이미 존재하는지 확인하기 위한 검사와 그 검사 결과에 따라 조치를 취하는 것과 같은 많은 추가 단계가 수반될 것이다.

VM Name: web-server
Package: nginx:1.18
Port: 8080
Path: /var/www/nginx
Code: GIT Repo - X


예를 들어 VM을 프로비저닝하는 동안 웹 서버라는 이름의 VM이 이미 존재하는 경우 어떻게 될까? 데이터베이스를 만들거나 데이터를 가져오는 경우에도 마찬가지이다. VM이 이미 있기 때문에 실패해야 할까 아니면 계속해야 할까? 만약 우리가 추후에 Nginx 1.18로 소프트웨어 버전을 업그레이드하기로 결정한다면? 구성 파일에서 Nginx의 버전을 업데이트하는 것만큼 간단해야 하며, 나머지는 시스템이 처리해야 한다. 이상적으로, 시스템은 이미 수행된 것을 알고 필요한 변경 사항만 적용할 수 있을 만큼 지능적이어야 한다. 그것이 선언적인 작업 방식이다.

 

# Imperative
$ kubectl run --image=nginx nginx
$ kubectl create deployment --image=nginx nginx
$ kubectl expose deployment nginx --port 80
$ kubectl edit deployment nginx
$ kubectl scale deployment nginx --replicas=5
$ kubectl set image deployment nginx nginx=nginx:1.18
$ kubectl create –f nginx.yaml
$ kubectl replace –f nginx.yaml
$ kubectl delete –f nginx.yaml


Kubernetes 에서 인프라를 관리하는 명령적인 방법은 kubectl run 명령과 같은 명령을 사용하여 pod를 만들고 kubectl create 를 사용하여 deployment를 생성하는 것이다. kubectl expose 명령어를 사용하여 deployment를 노출하는 서비스를 만든다. 또한 kubectl edit 명령을 사용하여 deployment 개체를 편집하고 scale 명령어를 통해 replica set의 크기를 조정할 수 있다. kubectl set 명령어를 사용하여 deployment 이미지를 업데이트한다. 우리는 또한 kubectl create 과 객체 구성 파일을 지정하는 f 옵션으로 사용하여 객체를 만들고, kubectl replace명령을 사용하여 객체를 편집하고, kubectl delete 명령을 사용하여 객체를 삭제하는 등의 객체 구성 파일을 사용하여 객체를 관리했다. 이 모든 것은 개체 관리에 대한 명령적 접근 방식이다. Kubernetes에서는 객체를 생성, 업데이트 또는 삭제하여 인프라를 우리의 필요에 맞게 만드는 방법을 정확하게 말해야한다.

 

# Declarative
$ kubectl apply –f nginx.yaml


선언적 접근 방식은 kubectl apply 명령으로 Kubernetes 클러스터에서 응용 프로그램과 서비스의 상태를 정의하는 파일 집합을 만드는 것이다. Kubernetes는 구성 파일을 읽고 인프라를 정의한 상태로 만들기 위해 무엇을 해야 하는지 스스로 결정할 수 있어야 한다. 따라서 선언적 접근 방식에서는 kubectl apply를 실행하여 객체를 생성, 업데이트 또는 삭제하기 위한 명령을 적용한다. kubectl apply 명령은 기존 구성을 살펴보고 시스템에 어떤 변경이 필요한지 파악한다.

 

 

# Imperative Commands
Create Objects
$ kubectl run --image=nginx nginx
$ kubectl create deployment --image=nginx nginx
$ kubectl expose deployment nginx --port 80

Update Objects
$ kubectl edit deployment nginx
$ kubectl scale deployment nginx --replicas=5
$ kubectl set image deployment nginx nginx=nginx:1.18

좀 더 자세히 살펴보자. 이제, 명령적 접근 방식에는 두 가지 방법이 있다. 첫 번째는 run, create 또는 expose 명령과 같은 명령어를 사용하여 새 개체를 만들고 기존 개체를 업데이트하는 edit, scale 및 set 명령을 추가하는 것이다. 이러한 명령은 YAML 파일을 처리할 필요가 없기 때문에 개체를 빠르게 만들거나 수정하는 데 도움이 되며 이러한 명령은 인증 시험에 유용하다.
그러나 이러한 명령은 기능적으로 제한적이며 다중 컨테이너 pod 생성 또는 deployment와 같은 사례에는 길고 복잡한 명령을 구성해야 한다. 둘째, 이러한 명령은 한 번 실행되고 잊혀진다. 이러한 명령은 이러한 명령을 실행하는 사용자만 알 수 있고 그래서 다른 사람이 이러한 객체들이 어떻게 만들어졌는지 알아내는 것은 어렵다. 따라서 추적하기가 어렵고 대규모 또는 복잡한 환경에서 이러한 명령을 사용하여 작업하기가 어렵다.

 

 


이 부분에서 개체 구성 파일을 사용하여 개체를 관리하는 것이 도움이 된다. 개체 정의 파일 또는 구성 파일 또는 매니페스트 파일을 만들면 YAML 형식으로 표시하고 생성하고 사용하는데 필요한것을 정확히 기록하는데 도움이 된다. 우리는 이제 YAML 파일을 항상 가지고 있고 그것은 Git와 같은 코드 저장소에 저장할 수 있다. 이러한 파일을 중심으로 변경 검토 및 승인 프로세스를 구성하여 변경 내용이 운영 환경에 적용되기 전에 검토 및 승인될 수 있다. 예를 들어 향후 이미지 이름을 다른 버전으로 편집하는 등 여러 가지 방법이 있다. 한 가지 방법은 kubectl edit 명령어를 사용하고 명령을 편집하고 개체 이름을 지정하는 것이다.

따라서 이 명령을 실행하면 개체를 생성하는데 사용한 것과 유사한 YAML 정의 파일이 열린다. 그러나 여기에 표시된 상태 필드 같은 추가 필드가 있다. 포드 상태를 저장하는 데 사용된다. 개체를 만드는 데 사용한 파일이 아니고 이것은 쿠버네티스 메모리 내의 유사한 포드 정의 파일이다. 이 파일을 변경한 후 저장하고 종료하면 해당 변경 사항이 라이브러리에 적용된다. 그러나 라이브 개체와 로컬에 있는 정의 파일에는 차이가 있다. edit 명령을 사용하여 변경한 내용은 변경 내용이 적용된 후 아무 곳에도 실제로 기록되지 않는다. 로컬 정의 파일에는 실제로 이전 이미지 이름이 포함되어 있다.

나중에 사용자 또는 팀 동료가 kubectl edit 명령을 사용하여 변경한 사실을 모른 채 이 개체를 변경하기로 결정했다고 가정해 보자. 새 변경 내용이 적용되면 이미지에 대한 이전 변경 내용이 손실되므로 변경 작업을 수행하는 동안 나중에 개체 구성 파일을 사용하지 않는다고 확신 할 경우 kubectl edit 명령을 사용할 수 있다.

그러나 더 나은 접근 방식은 먼저 필요한 변경 사항이 있는 개체 구성 파일의 로컬 버전을 편집하는 것이다. 즉, 여기서 이미지 이름을 업데이트한 다음 kubectl replace 명령을 실행하여 개체를 업데이트한다. 이후 변경 사항은 기록되고 변경 검토 프로세스의 일부로 추적될 수 있다. 때때로 개체를 완전히 삭제하고 다시 생성해야 하고 싶다면, 이러한 경우 다음과 같은 force 옵션을 사용하여 동일한 명령을 실행할 수 있다. 하지만 이것은 여전히 명령적인 접근 방식이다. 왜냐하면 당신은 여전히 쿠버네티스에게 이 객체들을 만들거나 업데이트하는 방법을 가르치고 있기 때문이다. 먼저 코드를 실행하여 명령을 만들고 개체를 만든 다음 replace 명령을 실행하여 개체를 바꾸거나 delete 명령을 실행하여 개체를 삭제한다. create 명령을 실행하면 어떻게 될까? 개체가 이미 있으면 pod가 이미 있다는 오류와 함께 실패한다. 개체를 업데이트할 때는 항상 replace 명령을 실행하기 전에 개체가 먼저 존재하는지 확인해야 한다. 개체가 존재하지 않으면 오류 메시지와 함께 replace 명령이 실패한다. 따라서 변경하기 전에 항상 현재 구성을 숙지하고 제대로 작동하는지 확인해야 하기 때문에 관리자로서 명령적인 접근 방식은 매우 부담이 된다.

 

 


선언적 접근 방식은 작업했던 것과 동일한 개체 구성 파일을 사용하는 방법이다. 그러나 Creat, replace 명령 대신 kubectl apply를 사용하여 객체를 관리하는 명령을 적용한다. kubectl apply 명령은 개체가 아직 존재하지 않는 경우 개체를 만들 수 있을 만큼 지능적이다. 일반적으로 여러 개체 구성 파일이 있는 경우 단일 파일 대신 디렉터리를 경로로 지정할 수 있다. 이렇게 하면 모든 객체가 한 번에 생성된다.

이제 변경이 필요한 경우 개체 구성 파일을 업데이트하고 적용된 명령을 다시 실행한다. 그리고 이번에는 그 객체가 존재한다는 것을 알게 된다. 따라서 새로운 변경 사항으로 개체만 업데이트한다. 따라서 개체가 이미 존재하거나 업데이트를 적용할 수 없다는 오류를 발생시키지 않는다. 항상 객체를 업데이트하기 위한 올바른 접근 방식을 파악할 것이다. 따라서 앞으로 기존 구성 파일의 이미지나 필드를 업데이트하거나 새 개체에 대해 새 구성 파일을 모두 추가하는 등 응용 프로그램에서 변경한 내용이 모두 적용된다. 우리가 하는 일은 변경사항으로 로컬 디렉토리를 업데이트한 다음 명령을 적용하여 나머지를 처리하는 것뿐이다.




 

 

 

출처:

https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/learn/lecture/21500820#overview