쿠버네티스의 짧은 배경지식과 Pod, Replica Set의 관한 설명

김태훈·2023년 11월 10일
0
post-thumbnail

성균관대학교 [SKKUDING] 동아리에서 진행한 쿠버네티스 스터디 발표 내용입니다.

💡 https://subicura.com/k8s 의 내용과, 책 ‘시작하세요 도커, 쿠버네티스' 를 참고하여 발표하였습니다.

1. 쿠버네티스 시작 전 배경지식 다지기 - Minikube

저희 예제에서는 Minikube를 사용합니다. Minikube란, 개발 용도의 쿠버네티스인데요. Docker Desktop을 설치했던 것과 같이, 개발용도로 사용하는 도구입니다.

Minikube는 로컬에서 가상 머신이나 도커 엔진을 통해 쿠버네티스를 사용할 수 있는 환경을 제공합니다.

하지만 단점은 존재합니다. 바로, ‘개발 용도로 사용’하는 것인데요. 이는 ‘standalone’ 모드를 사용하는 것과 동일합니다.

standalone이란?
standalone은 말 그대로, 독자적으로 존재한다라는 뜻을 담고 있습니다.
제가 standalone 을 처음으로 들어본 것은 Next.js 프레임워크에서 웹어플리케이션을 실행시킬때 standalone모드를 적용시키면, ‘실행’할 때만 필요한 코드만 추출하여 build를 해주는데요. 자세한 것은 아래 링크를 참고하시면 될것 같습니다. 추후에 Next.js를 도입할 때에, 도커 이미지를 최적화하는 데에도 유용하게 사용됩니다.
랠릿 standalone 적용기

서론이 길었는데, 쿠버네티스의 standalone모드도 비슷합니다. 본래 쿠버네티스의 목적은 여러 서버의 자원을 클러스터링하여 컨테이너를 배치하는 것이 쿠버네티스의 핵심이지만, 로컬 노드만을 standalone모드로 사용하므로, 쿠버네티스의 기능을 완전히 사용할 수 없는 것이 개발 용도의 쿠버네티스 설치 툴이 가진 단점입니다. 사실 어떻게 보면 당연한 단점이죠.


그렇다면.. 실제 운영단계에서는?

실제 운영단계에서는 kops, kubespray,kubeadm, EKS,GKE등의 Managed 서비스로 관리를 합니다. 사실 여기선 제가 아는 것은 EKS뿐입니다. EKS로 서비스를 배포하기 위해서는 클라우드 환경에 존재하는 수십개의 서버에 동시에 쿠버네티스를 설치해야만 합니다. (실제로 스꾸딩에서 사용하는 ECS도 각 서버에 ecs-agent라는 container가 존재합니다.)

💡 사실, 쿠버네티스를 진짜로 잘 알고싶다면 EKS와 같이 클라우드 서비스 업체가 관리해주는 방법 말고 그 외의 우리가 들어본적없는 쿠버네티스 설치 툴을 사용하는 것이 좋다고 합니다. 실습도 이렇게하면 더 좋을 것 같네요.
실제로 on-premise환경에서는 kubespray, kubeadm을 이용합니다. ec2에서도 마찬가지입니다.

그럼, 이제 본격적으로 쿠버네티스를 시작해봅시다 !

2. 쿠버네티스 시작하기

1) 쿠버네티스 시작 전

시작한다고 해놓고 또 시작 전이라고 했는데, 미안합니다.

  • 쿠버네티스의 리소스는 ‘오브젝트’ 형태로 관리합니다.
    우리가 이번 스터디에서 배웠던 리소스들, 예컨대 ‘Pods’, ‘Replica Set’, ‘Node’ 등은 하나의 오브젝트입니다.
    kubcetl api-resources
    이 명령어를 치면, 모든 Object를 살펴볼 수 있습니다. 하지만 너무 많아요! 다 몰라도 괜찮대요 ㅎㅎ..

    이중 특정 오브젝트의 간단한 설명을 보고싶으면 kubectl explain <object name>을 사용하시면 됩니다.
  • YAML파일로 시작해서 YAML파일로 끝난다.
    쿠버네티스는 여러개의 YAML파일을 정의해 적용시키는 방식입니다. 컨테이너 자체는 물론이고, 여러 설정값과 secret 값들은 YAML파일로 정의가 됩니다. kubectl명령어로도 쿠버네티스를 사용할 수 있지만, 실상은 YAML파일을 더 많이 사용한다고 하네요.
  • 쿠버네티스는 여러 개의 컴포넌트로 구성되어 있다.
    쿠버네티스는 기본적으로 Node위에서 동작합니다.
    노드는 마스터노드와 워커노드로 나뉘어져 있습니다.
    • 마스터 노드 :
      쿠버네티스가 제대로 동작할 수 있는 관제탑 역할
      여기서 마스터 노드에서는 API 서버, 컨트롤러 매니저, 스케쥴러, DNS 서버와 같이 여러 관리를 위한 컴포넌트가 실행됩니다. 실제로 마스터 노드로 ssh로 접속해서 docker ps 를 쳐보면, 해당 컴포넌트의 여러 컨테이너들이 실행되고 있답니다. (이건 안해봄 ㅎㅎ..)
    • 워커 노드 :
      마스터 노드의 노예입니다. 애플리케이션이 실행되는 컨테이너에요.
    • 공통점 :
      공통적으로 모든 node에는 kubelet이라는 에이전트가 실행됩니다. AWS ECS에서 관리해주는 ecs-agent가 각 EC2에 있는것 처럼요. 이는 쿠버네티스 클러스터 구성을 위해 필수적인데요. kubelet은 컨테이너의 생성, 삭제, 마스터노드와 워커노드의 통신에 사용되는 매우 중요한 agent입니다.

한 마디 요약 : 쿠버네티스는 ‘노드’ 컴포넌트로 구성되어 있으며 이 안에도 수많은 컴포넌트가 존재하고, kubelet이 노드마다 존재하여 쿠버네티스의 중추적인 역할을 한다.

2) 포드(Pod) 컨테이너를 다루는 기본 단위

쿠버네티스의 리소스는 ‘오브젝트’ 형태라고 했죠?
그중 가장 기본 단위의 오브젝트인 ‘Pod’ 에 대해 알아봅시다.
컨테이너 애플리케이션의 기본단위를 Pod라고 합니다.
Pod는 한개 이상의 컨테이너로 구성된 컨테이너의 집합입니다. 아까 그림보시면 이해될거에요.

도커에서는 기본 단위가 도커 컨테이너였습니다. 쿠버네티스에서는 Pod입니다.
보통은 One Pod → One Container 가 일반적인 방법이지만, 여러개의 컨테이너가 존재할 수도 있습니다.

예시를 가져와봤습니다.
nginx 웹 서비스를 쿠버네티스에서 생성하기 위한 yaml파일인데요.
이 YAML파일을 쿠버네티스에 적용시켜봅시다. (YAML로 시작해서 YAML로 끝난다 아시죠?)

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: my-nginx-container
			image: nginx:latest
			ports:
			- containerPort: 80
				protocol: TCP

💡 쿠버네티스의 YAML파일 형식

  • apiVersion
    쿠버네티스 오브젝트의 API 버전입니다. 당장은 중요하지 않으므로 과감히 넘어갑니다.(라고 쓰여있습니다)
  • kind
    리소스의 종류입니다. 우리는 ‘Pod’ 컴포넌트를 만드므로, Pod라고 쓴거죵. 아까 kubectl api-resources 커맨드에서 나온 pods가 이친구입니다.
  • metadata
    주석과도 같습니다. 메타데이터라고 하면 보통, 리소스의 정보들을 의미하죠? 그래서 해당 항목에서는 name과 같이 해당 오브젝트의 특별한 이름을 지어봅시다. 또한 ReplicaSet이라는 컴포넌트에서 원하는 Pods의 개수를 띄우주기 위한 정보도 포함되어 있습니다.
  • spec
    리소스들을 생성하기 위한 자세한 정보입니다. 매우 중요한 정보라고 할 수 있습니다. 사실상 container의 config정보이기 떄문인데요.
    위의 예시를 보면 container name을 ‘my-nginx-container’로 지정하였고, nginx 이미지를 받아온후에, 컨테이너 port를 TCP 80번 포트로 설정하였네요.
    이러한 정보는 후의 배포단계에서 아주 중요하게 사용됩니다. port 번호로 mapping할수도 있겠지만, container name으로도 mapping이 가능하답니다. 컨테이너가 수없이 많이 늘어나면 일일이 port번호를 수동으로 mapping할 수 없기도 하거든요.

그럼 이제 kubectl apply -f nginx-pod.yaml 로 pod를 생성해봅시다.

pod가 생성되었는지 확인은 각자 알아서 해보시고, 해당 nginx서버로 요청을 보내는 실습을 진행해봅시다.

이를 위해서는 포드 컨테이너 내부 IP로 접근을 해야합니다. 아직 해당 컨테이너가 외부에서 접근할 수 있도록 노출된 상태는 아니거든요. 단지 ‘포트’만 정의했을 뿐입니다.

그러면 kubectl describe 명령어를 사용해 자세한 정보를 알아볼까요?

(실제 터미널 결과를 복붙하였습니다.)

Name:             nginx-pod
Namespace:        default
Priority:         0
Service Account:  default
Node:             minikube/192.168.49.2
Start Time:       Fri, 10 Nov 2023 01:52:49 +0900
Labels:           <none>
Annotations:      <none>
Status:           Running
IP:               10.244.0.3
IPs:
  IP:  10.244.0.3
Containers:
  my-nginx-container:
    Container ID:   docker://03fc89066ecf448c49303b9c59efd4106176d2108b4e77c9b7ee3a3987dfee17
    Image:          nginx:latest
    Image ID:       docker-pullable://nginx@sha256:86e53c4c16a6a276b204b0fd3a8143d86547c967dc8258b3d47c3a21bb68d3c6
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 10 Nov 2023 01:53:19 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-bphdv (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  kube-api-access-bphdv:
    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  3m30s  default-scheduler  Successfully assigned default/nginx-pod to minikube
  Normal  Pulling    3m30s  kubelet            Pulling image "nginx:latest"
  Normal  Pulled     3m     kubelet            Successfully pulled image "nginx:latest" in 29.928235972s (29.92825368s including waiting)
  Normal  Created    3m     kubelet            Created container my-nginx-container
  Normal  Started    3m     kubelet            Started container my-nginx-container**

해당 포드의 ip는 10.244.0.3 이네요.

10 과 172 192 는 외부에서 접근할 수 없는 대표적인 사설 ip주소이기도 하고, 해당 pod의 IP는 클러스터 내부에서만 접근이 가능합니다. docker run -p 없이 컨테이너를 실행한 것과 동일합니다.

그래서 해당 nginx 포드로 요청을 보내기 위해서는 ‘서비스 오브젝트’라는 것을 생성해야 한다고 하네요. (나중에 알아봅시다)

일단 지금은 서비스 오브젝트 없이 IP만으로 Nginx Pod에 접근해봅시다. 어케할 수 있을까요?

실제 클러스터 노드 하나에 접속하고, Nginx Pod의 ip로 http 요청을 보내면 됩니다.

kubectl run -i --tty --rm debug --image=alpine:latest --restart=Never ash

저는 해당 커맨드로 alpine 이미지를 통해 pod를 만들고 바로 실행 시켰습니다.

alpine은 ash shell 을 사용하므로, 해당 pod 내에서 -i —tty 옵션으로 실행시켰습니다.

그후 요청을 보낼 curl package를 다운받고 해당 ip로 요청을 보내봤습니다.

중간 오타는 봐주세요 ㅎㅎ.. 성공적으로 요청을 받아옵니다.

그후, nginx pod에 어떤 요청이 왔는지 log로 확인해 볼까요?

맨 밑줄을 보면 curl 요청이 온것이 확인됩니다.

3) Replica Set 일정 개수의 포드를 유지하는 컨트롤러

실제 배포 상황에서 어떤 컨테이너가 가끔 죽기도합니다. 우리는 이를 자동으로 살려서 서비스를 운용해야 합니다. 그러기 위해 Replica Set을 사용하는데요.

  1. 정해진 수의 동일한 포드가 항상 실행되도록 관리
  2. 노드 내부 장애로 인해 포드 RUN이 불가능 하면 다른 노드를 재 생성하여 포드를 RUN

이 두가지 역할을 하는 것이 Replica Set입니다.

예시 YAML을 볼까요

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-replica-pod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pods-label
  template:
    metadata:
      labels:
        app: nginx-pods-label
    spec:
      containers:
      - name: my-nginx-container
        image: nginx:latest
        ports:
        - containerPort: 80

replica 정의 yaml설정에서 matchLabels의 app 값이 곧 template의 labels 의 app값과 동일합니다.

그러면, 해당 label이 붙어있는 pod들은 ‘3’개로 유지시켜줍니다.

즉 replica를 정의하고, template 하위에서 pod를 정의하여서

해당 pod의 label과 동일한 pod를 replica 설정에 맞게 유지시켜 줍니다.


오늘은 여기까지 알아봤는데요. 다음에는 service object를 생성하고 배포하는 방법에 대해 알아보겠습니다.

profile
기록하고, 공유합시다

0개의 댓글