티스토리 뷰
쿠버네티스를 도커 스웜 모드와 비교해 봤을 때, 쿠버네티스만이 가지는 고유한 특징이 있다.
모든 리소스는 오브젝트 형태로 관리된다.
쿠버네티스는 대부분의 리소스를 '오브젝트'라고 불리는 형태로 관리한다. 그리고 이 오브젝트라는 용어는 흔히 알고 있는 추상화된 집합에서 크게 벗어나지 않는 개념이다.
도커 스웜을 사용할 때, 컨테이너의 묶음을 표현하기 위해 '서비스(service)'라는 것을 사용했었다. 스웜 모드의 서비스도 컨테이너 리소스의 집합을 정의한 것이기 때문에 일종의 오브젝트라고 볼 수 있다. 그러나 쿠버네티스는 이러한 개념을 더욱 폭넓고 세밀한 단위로 사용한다. 예를 들어 쿠버네티스에서는 컨테이너의 집합(Pods), 컨테이너의 집합을 관리하는 컨트롤러(Replica Set), 사용자(Service Account), 노드(Node)까지도 하나의 오브젝트로 사용할 수 있다.
그러면, 쿠버네티스에서 사용할 수 있는 오브젝트에는 어떤 것이 있는지 kubectl-api-resources 명령어를 사용해 확인해 보도록 하자.
kubectl api-resources
엄청나게 많은 오브젝트가 존재하는 것을 확인해볼 수 있다. 그러나 이렇게 많은 종류의 오브젝트를 다루지는 않을 것이고, 당연하게도 모든 오브젝트의 종류와 이름을 외울 필요가 없다.
YAML 파일
도커 스웜 모드에서는 docker service create와 같은 명령어로 컨테이너 서비스를 생성하고 삭제했다. 쿠버네티스에서도 마찬가지로 kubectl이라고 하는 명령어로 쿠버네티스를 사용할 수 있으며, 대부분의 작업은 kubectl 명령어로 실행할 수 있다.
스웜 모드에서 스택(stack)을 생성하기 위해 YAML 파일을 사용했던 것처럼 쿠버네티스도 YAML 파일로 컨테이너 리소스를 생성하거나 삭제할 수 있다. 그러나 쿠버네티스에서 YAML 파일의 용도는 컨테이너뿐만 아니라 거의 모든 리소스 오브젝트들에 사용될 수 있다는 것이 가장 큰 특징이다. 예를 들어 컨테이너 자체는 물론이고, 컨테이너의 설정값, 비밀값 등도 모두 YAML 파일로 정의해 사용한다. 그리고 쿠버네티스에서 실제로 서비스를 배포할 때에도 kubectl 명령어가 아닌 여러 개의 YAML 파일을 정의해 쿠버네티스에 적용시키는 방식을 사용한다.
그래서 쿠버네티스를 잘 사용하는 방법을 한마디로 표현하면 'YAML 파일을 잘 작성하는 것'으로 나타낼 수 있다.
쿠버네티스는 여러 개의 컴포넌트로 구성돼 있다.
쿠버네티스 노드의 역할은 크게 마스터와 워커로 나뉘어 있다. 마스터 노드는 쿠버네티스가 제대로 동작할 수 있게 클러스터를 관리하는 역할을 담당하며, 워커 노드에는 애플리케이션 컨테이너가 생성된다.
도커 스웜 모드를 사용할 때는 단일 도커 데몬만을 설치해 사용했지만, 쿠버네티스는 도커를 포함한 매우 많은 컴포넌트들이 실행된다. 예를 들어, 마스터 노드에서는 API 서버(kube-apiserver), 컨트롤러 매니저(kube-controller-manager), 스케줄러(kube-scheduler), DNS 서버(coreDNS) 등이 실행되며, 모든 노드에서는 네트워크 구성을 위해 프락시(kube-proxy)와 네트워크 플러그인(calico, flannel) 등이 실행된다.
이러한 컴포넌트들은 기본적으로 도커 컨테이너로서 실행되고 있다. 마스터 노드에서 docker ps 명령어를 실행해 보면 매우 많은 컨테이너가 실행되고 있음을 볼 수 있다.
그리고 쿠버네티스 클러스터 구성을 위해 kubelet이라는 에이전트가 모든 노드에서 실행된다. kubelet은 컨테이너의 생성, 삭제뿐만 아니라 마스터와 워커 노드 간의 통신 역할을 함께 담당하는 매우 중요한 에이전트이다. 따라서 kubelet이 정상적으로 실행되지 않으면 해당 노드는 쿠버네티스와 제대로 연결되지 않을 수도 있다.
쿠버네티스의 입장에서 보면 도커 데몬 또한 하나의 컴포넌트이다. 도커 스웜 모드와 달리 쿠버네티스는 도커에 내장된 기능이 아니며, 오히려 컨테이너를 사용하기 위해 쿠버네티스가 도커를 이용하는 방식이다. 따라서 쿠버네티스에서 반드시 도커를 사용해야 하는 것은 아니며, OCI(Open Container Initiative)라는 컨테이너의 런타임 표준을 구현한 CRI(Container Runtime Interface)를 갖추고 있다면 어떠한 컨테이너를 써도 문제는 없다.
컴포넌트의 구조가 복잡하기에 한번에 이해하려거나 외울 필요는 없다. 필요할 때마다 쿠버네티스의 기능과 컴포넌트를 함께 엮어서 공부할 것이기 때문에 지금은 'kubelet'이라는 에이전트가 모든 노드에서 기본적으로 실행되며, 마스터 노드에는 API 서버 등이 컨테이너로 실행된다 정도만 알고 넘어가도록 하자.
파드(Pod) : 컨테이너를 다루는 기본 단위
쿠버네티스에서는 컨테이너 애플리케이션의 기본 단위를 '파드(Pod)'라고 부르며, 파드는 1개 이상의 컨테이너로 구성된 컨테이너의 집합이다. 파드는 쿠버네티스에서 가장 기초적이고 중요한 개념이므로 이해하고 넘어가도록 하자.
도커 엔진에서는 기본 단위가 도커 컨테이너였고, 스웜 모드에서의 기본 단위는 여러 개의 컨테이너로 구성된 '서비스(Service)'였다. 이와 비슷한 맥락으로 쿠버네티스에서는 컨테이너 애플리케이션을 배포하기 위한 기본 단위로 파드라는 개념을 사용한다. 하나의 파드에는 1개의 컨테이너가 존재할 수도 있고, 여러 개의 컨테이너가 존재할 수도 있다.
간단하게 예시를 들어 살펴보자.
Nginx 웹 서비스를 쿠버네티스에서 생성하려면 컨테이너와 파드를 어떻게 사용할 수 있을까? 가장 쉬운 방법은 파드 1개에 Nginx 컨테이너 1개 만을 포함해 생성하는 것이다. 만약 동일한 Nginx 컨테이너를 여러 개 생성하고 싶다면 1개의 Nginx 컨테이너가 들어있는 동일한 파드를 여러 개 생성하면 된다. 이처럼 파드는 컨테이너 애플리케이션을 나타내기 위한 기본 구성 요소가 된다.
파드의 개념을 좀 더 정확히 이해하기 위해 Nginx 컨테이너로 구성된 파드를 직접 생성해보겠다. 아래의 내용을 nginx-pod.yaml로 작성해 보자.
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod
spec:
containers:
- name: my-nginx-container
image: nginx:latest
ports:
- containerPort: 80
protocol: TCP
쿠버네티스의 YAML 파일은 일반적으로 apiVersion, kind, metadata, spec 네 가지 항목으로 구성되며, 작성한 YAML 파일의 의미는 아래와 같다.
- apiVersion : YAML 파일에서 정의한 오브젝트의 API 버전을 나타낸다.
- kind : 이 리소스의 종류를 나타낸다. 위의 YAML 파일로 생성하고자 하는 것이 파드이기 때문에 Pod를 입력한 것이다.
- metadata : 라벨, 주석, 이름 등과 같은 리소스의 부가 정보들을 입력한다.
- spec : 리소스를 생성하기 위한 자세한 정보를 입력한다. 위 예시에서는 파드에서 사용될 컨테이너 정보를 정의하는 containers 항목을 작성한 뒤, 하위 항목인 image에서 사용할 도커 이미지를 지정한 것이다. 또한 ports 항목에서 Nginx 컨테이너가 사용할 포트인 80을 입력했다.
작성한 YAML 파일은 kubectl apply -f 명령어로 쿠버네티스에 생성할 수 있다. 아래의 명령어를 사용해 새로운 파드를 생성해 보자.
kubectl apply -f nginx-pod.yaml
kubectl get <오브젝트 이름>을 사용하면 특정 오브젝트의 목록을 확인할 수 있다. 그리고 kubectl get pods 명령어는 현재 쿠버네티스에 존재하는 파드의 목록을 출력한다.
위의 Nginx 파드를 생성할 때, YAML 파일에 사용할 포트를 정의하기는 했지만, 아직 외부에서 접근할 수 있도록 노출된 상태는 아니다. 따라서 파드의 Nginx 서버로 요청을 보내려면 파드 컨테이너의 내부 IP로 접근해야 한다.
kubectl describe 명령어를 사용하면 생성된 리소스의 자세한 정보를 얻어올 수 있다.
kubectl describe pods my-nginx-pod
그러면 위와 같이 Nginx 파드에 대한 많은 정보가 출력된다. 그중 파드의 IP 항목도 포함되어 있으며 출력 결과에서 알 수 있듯이 파드의 IP는 172.17.0.3이다. 그러나 앞서 언급했듯이 이 IP 주소는 외부에서 접근할 수 있는 IP가 아니기 때문에 클러스터 내부에서만 접근할 수 있다.
쿠버네티스 외부 또는 내부에서 파드에 접근하려면 서비스(service)라고 하는 쿠버네티스 오브젝트를 따로 생성해야 하는데, 지금은 서비스 오브젝트를 생성하지 않고 넘어가겠다.
파드 vs 도커 컨테이너
위의 기능들만 놓고 본다면, 파드는 docker run으로 생성한 단일 nginx 컨테이너와 크게 다르지 않아 보인다. 파드는 컨테이너 IP 주소를 가지고 있어 쿠버네티스 클러스터 내부에서 접근할 수 있고, kubectl exec 명령어로 파드 컨테이너 내부로 들어갈 수도 있으며, kubectl logs 명령어로 파드의 로그를 확인할 수도 있기 때문이다.
그렇다면 쿠버네티스는 왜 도커 컨테이너가 아닌 '파드'라는 새로운 개념을 굳이 사용하는 것일까?
쿠버네티스가 파드를 사용하는 이유는 '컨테이너 런타임의 인터페이스 제공' 등 여러 가지가 있지만, 그 이유 중 하나는 여러 리눅스 네임스페이스를 공유하는 여러 컨테이너들을 추상화된 집합으로 사용하기 위해서이다.
이 개념은 이해하기가 조금 어렵기에 좀 더 쉽게 파드를 사용하는 예시를 하나 더 살펴보자.
kubectl get pods 명령어로 파드의 목록을 출력해 보면, 아래와 같은 결과를 얻을 수 있다.
READY 항목에 1/1이라는 출력이 존재한다. 이는 Nginx 파드에는 1개의 컨테이너가 정의돼 있으며, 이 컨테이너는 정상적으로 준비됐다는 의미를 가진다. 실제로 대부분 쿠버네티스의 컨테이너 애플리케이션은 이처럼 1개의 컨테이너로 파드를 구성해 사용한다. 그러나 i/i이라는 항목에서 알 수 있듯이, 파드를 반드시 1개의 컨테이너로 구성해야 하는 것은 아니다. READY 항목은 포드의 컨테이너 개수에 따라 2/2도 될 수 있고, 3/3도 될 수 있다.
이번에는 Nginx 파드에 새로운 우분투 컨테이너를 추가해 보자.. 이전에 사용한 YAML 파일을 확장해 아래 내용으로 새로운 YAML 파일을 작성한다.
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod
spec:
containers:
- name: my-nginx-container
image: nginx:latest
ports:
- containerPort: 80
protocol: TCP
- name: ubuntu-sidecar-container
image: ubuntu:latest
command: ["tail"]
args: ["-f", "/dev/null"]
우분투 이미지를 사용하고, 이 컨테이너는 종료되지 않기 위해 tail -f /dev/ null이라는 단순한 동작만을 실행한다.
kubectl apply - f nginx-pod-with-ubuntu.yaml
앞서 파드를 생성했던 것처럼 kubectl apply -f 명령을 사용해 YAML 파일을 쿠버네티스에 적용하자. 그리고 시간이 조금 지나면, Nginx 파드에 2개의 컨테이너가 실행 중인 것을 확인할 수 있다.
이제 kubectl exec 명령어를 이용해 새롭게 추가된 우분투 컨테이너의 내부로 들어가 보자. kubectl exec, logs 등과 같은 명령어를 사용할 때는 -c 옵션을 이용해 파드의 어떤 컨테이너에 대해 명령어를 수행할지 명시할 수 있다.
kubectl exec -it my-nginx-pod -c ubuntu-sidecar-container bash
그리고 ubuntu-sidecar-container 컨테이너 내부에서 로컬호스트로 HTTP 요청을 전송하면 Nginx 서버의 응답이 도착하는 것을 확인할 수 있다.
ubuntu container가 Nginx 서버를 실행하고 있지 않은데도, 로컬호스트에서 Nginx 서버로 접근이 가능하다. 이는 파드 내의 컨테이너들이 네트워크 네임스페이스 등과 같은 리눅스 네임스페이스를 공유해 사용하기 때문에 가능한 것이다.
어려운 내용이다. 최대한 쉽게 파드 내부의 컨테이너들은 네트워크와 같은 리눅스 네임스페이스를 공유한다 정도 이해하고 넘어가기로 하자.
Ref : 시작하세요! 도커/쿠버네티스
'DevOps > Kubernetes' 카테고리의 다른 글
[Kubernetes] Ingress (2) | 2023.08.15 |
---|---|
[Kubernetes] Namespace : 리소스를 논리적으로 구분하는 장벽 (0) | 2023.01.14 |
[Kubernetes] Service : 파드를 연결하고 외부에 노출하기 (0) | 2023.01.14 |
[Kubernetes] Deployment : 레플리카셋, 파드의 배포를 관리 (2) | 2023.01.13 |
[Kubernetes] 레플리카셋(Replica Set) (0) | 2023.01.13 |