Skip to main content

Deployment 리소스 소개

쿠버네티스 오브젝트

쿠버네티스 클러스터 안에는 모든 리소스들을 오브젝트 형태로 관리한다. 컨테이너의 집합 (Pod), 컨테이너 집합을 관리하는 컨트롤러 (ReplicaSet), 사용자 (Service Account) 등등 을 모두 하나의 오브젝트로 사용 할 수 있다. 이 오브젝트들은 YAML 파일을 이용해서 생성 할 수 있다. 이 YAML 파일들을 Manifest(매니페스트) 파일이라고 부른다. 이 파일은 일련의 설정 정보를 포함하고 있으며, 쿠버네티스 클러스터에게 어떤 리소스를 생성하고 구성해야 하는지를 지시한다. 매니페스트 파일의 예시:

# Nginx 컨테이너로 구성된 파드를 직접 생성하는 yaml 파일 예시
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
  - name: example-container
    image: nginx:latest
    ports:
    - containerPort: 80
      protocol: TCP

쿠버네티스 매니페스트 YAML파일은 일반적으로 apiVersion, kind, metadata, spec 네 가지 항목으로 구성 된다.

  • apiVersion yaml 파일에서 정의한 오브젝트의 API 버전
  • kind 리소스의 종류(kubectl api-resources 명령어의 KIND 항목에서 확인 가능)
  • metadata 리소스의 부가 정보(label, annotation, name 등)
  • spec 리소스를 생성하기 위한 자세한 정보(파드에서 실행될 컨테이너 정보, 도커 이미지 정보, 포트 정보 등). - 은 list 형식이라고 생각하면 된다. example-container 라는 이름의 nginx:latest 이미지를 가진 컨테이너가 있다는 뜻이고, 포트는 80을 사용한다는 뜻이다.

kubectl apply -f <yaml 파일 이름> 명령어를 통해 쿠버네티스에 파드를 생성할 수 있다.

kubectl은 이러한 매니페스트 파일을 사용하여 쿠버네티스 클러스터를 관리하기 위한 커맨드 라인 도구이다. 개발자나 시스템 관리자는 kubectl을 활용하여 매니페스트 파일을 통해 Kubernetes 클러스터 내의 리소스를 생성, 조회, 수정, 삭제할 수 있다.

kubectl은 다양한 기능과 옵션을 제공한다. 일반적으로 다음과 같은 작업을 수행할 수 있다:

  • 클러스터와의 연결: kubectl을 사용하여 Kubernetes 클러스터와 통신하고, 사용자 인증 및 인가를 설정한다.

  • 리소스 관리: kubectl을 통해 파드, 서비스, 디플로이먼트 등의 리소스를 생성, 조회, 수정, 삭제할 수 있습니다. 또한, 클러스터 내의 다양한 리소스들의 상태를 확인할 수 있다.

  • 로그 및 이벤트 확인: kubectl을 사용하여 파드의 로그를 확인하거나 클러스터의 이벤트를 조회할 수 있다. 이를 통해 애플리케이션의 동작 상태를 추적하고 디버깅할 수 있다.

  • 스케일링 및 롤링 업데이트: kubectl을 사용하여 애플리케이션을 스케일 업/다운하거나, 롤링 업데이트를 수행할 수 있다. 이를 통해 애플리케이션의 확장성과 가용성을 관리할 수 있다.


Pod

쿠버네티스에서는 애플리케이션을 배포할 수 있는 최소 단위인 파드(Pod)라는 개념을 제공한다. 파드는 하나 이상의 컨테이너를 포함할 수 있으며, 같은 파드 안에 있는 컨테이너는 동일한 호스트에서 실행된다.

image.png

위에서 본 예시 Yaml파일을 적용 해보면:

kubectl apply -f example-pod.yaml
pod/example-pod.yaml created

이렇게 Nginx 파드가 생성 되었다. 확인 결과:

kubectl get pods
NAME		 READY	STATUS	 RESTARTS	AGE
example-pod  1/1	Running	 0			40s

이 Nginx 파드는 사용할 포트를 80으로 지정 했지만, 아직 외부에서 접근 할 수 있도록 노출 되지는 않았다. 

이 파드를 삭제 하려면:

kubectl delete -f example-pod.yaml
pod "example-pod" deleted

kubectl get pods
No resources found in default namespace


ReplicaSet

Pod의 수를 관리하고, 원하는 수의 Pod가 항상 실행되도록 유지하는 역할을 하는 오브젝트. ReplicaSet은 주로 Pod을 생성하고, 감시하고, Pod가 정상적으로 실행되지 않을 경우 다른 Pod으로 대체하는 작업을 수행한다. 레플리카셋은 정해진 수의 동일한 팟이 항상 실행되도록 관리해준다.

image.png

예시:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-nginx-pods-label  # 여기까지가 레플리카셋의 정의다.
  template:                     # 여기서부터가 포드의 정의다.
    metadata:
      name: my-nginx-pod
      labels:
        app: my-nginx-pods-label
    spec: 
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80

위 YAML파일 속 7번 줄부터는 ReplicaSet의 정의다. replicas: 3는 동일한 파드를 몇 개 유지시킬 것인지에 대한 정보다. template는 ReplicaSet에서 어떤 파드를 생성 할 것인지에 대한 정보다. 10번 줄에 있는 matchLabels는 기존에 동일한 이름의 파드가 존재 한다면 파드를 관리하여  replicas:에 정의 되어있는 숫자 만큼 파드들을 생성 또는 제거 한다. 한마디로 matchLabels는 특정 레이블 지정하는 필드라고 생각 하면 된다. 레플리카셋은 일정 개수의 팟을 유지하는 것을 목적으로 두고 있다.


Deployment

실제 쿠버네티스 환경에서 사용하는 오브젝트다. 디플로이먼트를 생성하면 이에 대응하는 레플리카셋도 함께 생성된다.

image.png

예시:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      name: my-nginx-pod
      labels:
        app: my-nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.10
          ports:
            - containerPort: 80

레플리카셋을 정의한 Yaml 파일과 비교해서 kind 밖에 바뀐 것이 없다. 디플로이먼트를 사용하는 핵심적인 이유는 애플리케이션의 업데이트와 배포를 편하게 하기 위해서이다. 새로운 별도의 레플리카셋이 생성되고 변경된 버전의 팟을 생성해 새로운 레플리카셋에 넣고 기존의 레플리카셋에 들어있는 팟의 수를 줄인다. 숫자로 나타내면 다음과 같다.

기존 레플리카셋 팟 숫자 - 새로운 레플리카셋의 팟 숫자. 3 - 0, 3 - 1, 2 - 2, 1 - 3, 0 - 3

kubectl describe deployment {deployment name} 의 명령어를 통해 내부적으로 어떤 이벤트들이 진행되었는지 확인할 수 있다.그리고 이러한 버전 변경에 대한 정보를 보관하고 있어 쉽게 롤백이 가능하다.
kubectl rollout history deployment {deployment name} 버전 정보 조회
kubectl rollout undo deployment {deployment name} --to -revision={버전 숫자} 해당 버전으로 변경


Service

Deployment를 통해 생성된 파드의 IP를 통해서 직접 접근할 수도 있지만, 파드가 재생성될 경우 경우 IP가 영속적이지 않기 때문에 변경될 수 있다는 점을 유의해야 한다. 여러 개의 Deployment를 하나의 완벽한 애플리케이션으로 연동하려면 파드 IP가 아닌 서로를 발견할 수 있는 방법이 필요하다.

서비스는 여러 개의 팟에 쉽게 접근할 수 있도록 도메인 이름을 부여하고, 로드 밸런서 기능을 수행하여 파드를 외부로 노출시켜준다.
서비스는 Cluster IP, NodePort, LoadBalancer 이렇게 3가지 타입이 있다.

먼저 ClusterIP의 대한 얘기 해보겠다.

image.png

예시:

apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-clusterip
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: ClusterIP

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      name: my-webserver
      labels:
        app: webserver
    spec:
      containers:
        - name: my-webserver
          image: waji97/test:v1
          ports:
            - containerPort: 80

생성된 서비스는 쿠버네티스 내부에서만 사용할 수 있는 고유 IP(Cluster IP)를 할당 받는데, 6번 줄에 있는 ports:는 해당 IP의 어떤 포트를 사용해서 접근 가능한지 기입한다. 똑같은 필드에 있는 targetPort는 접근할 파드가 사용하고 있는 포트 번호를 나타낸다. 10번 줄에 있는 selector는 이 서비스에서 어떤 라벨을 가지는 파드에 접근할 수 있게 만들 것인지를 나타낸다. 이렇게 설정을 하면, 내부적으로 파드의 IP가 변경되어도 서비스를 통해 접근하게 되므로 상관없어질 것이다. Cluster IP는 외부에서 접근할 수가 없다.

이번에는 NodePort 타입이다

image.png

예시:

apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-nodeport
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: NodePort

Manifest파일 형태는 거의 동일 하지만 마지막에 type:필드만 NodePort로 바뀐다. Cluster IP 타입일 때에는 단순히 파드에 연결해주는 것이었다. NodePort는 외부에서 어떤 노드든 간에 해당 노드의 IP에 접근하고 NodePort의 포트번호로 접근하기만 하면 해당 파드로 연결을 해준다. 포트는 30000 ~ 32767 중 랜덤으로 지정된다. 

마지막에 있는 LoadBalancer 타입은 노드포트에 접근하게끔 로드밸런싱을 해주는 타입인데 클라우드 플랫폼 환경에서만 사용가능 하다. 예를 들어 AWS 클라우드 플랫폼에서 로드 밸런서 서비스를 지정 해주면, AWS상 로드밸런서가 자동 생성 된고 로드 밸런서 역할을 수행 한다(AWS EKS 클러스터가 설정 되어 있는 과정에서만). 


image.png

예시:

apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-nodeport
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: LoadBalancer


Namespace

네임스페이스는 쿠버네티스 리소스들을 논리적으로 묶어주는 오브젝트다. 패키지라고 생각하면 된다.

image.png

예시:

apiVersion: v1
kind: Namespace
metadata:
  name: production

위와 같이 파일을 만들거나 kubectl create ns {네임스페이스명} 으로 만들어도 된다. 쿠버네티스 사용 시 자동으로 사용하도록 설정되는 네임스페이스는 default 다. 별도로 옵션을 명시하지 않았다면 해당 네임스페이스를 사용했을 것이다.


Ingress

인그레스는 외부 요청을 처리하는 방법을 정의하는 오브젝트다. 인그레스는 다양한 기능을 담당한다. 예를 들어, 특정 경로로 들어온 요청을 어떤 서비스로 전달할지 정의하는 라우팅 규칙, 가상 호스트를 기반으로한 요청 처리, SSL/TLS 보안 연결 처리 등을 담당한다.

NodePort나 LoadBalancer를 사용하면 위와 같은 기능을 구현할 수 있지만, NodePort의 개수가 많을 경우 각 서비스에 대해 설정을 별도로 해주어야 하는 번거로움이 있을 수 있다. 하지만 인그레스를 사용하면 한 곳에만 설정을 하면 되기 때문에 편리하다. 인그레스를 통해 요청의 경로와 처리 방식을 중앙에서 관리할 수 있어서 유연성과 효율성을 높일 수 있다.

image.png


예시:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-example
spec:
  rules:
    - host: example.com
      http:
        paths:
        - path: /echo-hostname
          backend:
            serviceName: hostname-service       	# 목적지 서비스의 이름과 포트 번호
            servicePort: 80							

여기서 host:는 해당 도메인 이름으로 접근하는 요청에 대해 처리 규칙을 적용 한다는 뜻이다. 그리고 path: 는 해당 경로의 요청을 어떤 서비스로 전달 할 것인지 정의한다. 

인그레스는 요청 처리 규칙을 정의하는 선언적인 오브젝트일 뿐이며, 실제로 외부 요청을 처리하지는 않는다. 인그레스 규칙을 사용하기 위해서는 인그레스 컨트롤러라고 하는 특수한 서버에 적용해야 한다. 인그레스 컨트롤러 서버가 실제로 외부 요청을 받아들이고 인그레스 규칙을 적용한다.

클라우드 서비스를 사용하는 경우, 자사의 로드밸런서 서비스와 연동하여 인그레스를 사용할 수 있다. 그러나 자체 클라우드 구축 시에는 보통 Nginx 웹 서버 인그레스 컨트롤러를 사용 한다. 이는 쿠버네티스에서 공식적으로 개발되고 있어서 공식 깃허브 저장소에서 설치를 위한 YAML 파일을 직접 내려받을 수 있다.