쿠버네티스

3. 쿠버네티스에서 애플리케이션을 동작시키는 구조 - 2

ksb-dev 2023. 3. 17. 19:41

3.2 컨테이너를 동작시키기 위한 리소스

쿠버네티스에서 컨테이너를 동작시키기 위한 리소스에 대한 내용이다.

 

3.2.1 컨테이너를 동작시키기 위한 리소스의 종류

쿠버네티스는 여러 가지 오브젝트로 표현된다. 때문에, 기존의 인프라 구축이나 애플리케이션 배포와 달리 ‘작업’이 아닌 ‘오프젝트 상태 성언’이라는 형태로 환경을 정의할 수 있다. 쿠버네티스의 기본 오브젝트는 파드, 레플리카셋, 디플로이먼트, 서비스가 있다. 서비스를 제외한 기본 오브젝트와 기타 오브젝트를 설명한다.

즉, 3.2에서 설명할 것은 아래와 같다.

  • 파드
  • 디플로이먼트
  • 레플리카셋
  • 크론잡
  • 데몬셋
  • 스테이트풀셋
  • 네임스페이스

 

3.2.2 파드

쿠버네티스에서 프로그램을 동작시키는 최소 기본 단위이다. 파드는 하나 이상의 컨테이너를 합쳐 하나의 통합된 서비스 오브젝트다. 구체적인 예시를 통해 설명한다.

3.2.2.1 nginx 컨테이너 단독 구성

앞서 2.2.3의 ‘EKS 클러스터 동작 확인’에서 nginx를 배포한 적이 있다. 이때, nginx 컨테이너 하나만 있는 파드를 정의하여 EKS 클러스터에 배포했다. 이와 같이 컨테이너를 하나만 포함한 파드가 쿠버네티스에서 프로그램을 동작시키는 가자 단순한 형대다. 이 nginx를 배포할 때 사용한 02_nginx_k8s.yaml의 파일 내용은 아래와 같다. 내용은 주석을 참고한다.

apiVersion: v1 # 이 매니패스트 파일에서 정의된 오브젝트가 준수해야 하는 사양의 버전
kind: Pod # 이 매니패스트 파일이 '파드'라는 종류의 오브젝트라는 것을 의미한다.
metadata:
  name: nginx-pod # 파드의 이름을 설정한다.
  labels:
    app: nginx-app # 파드에 레이블을 설정한다. 키-값 형태로 정의하는데, app이라는 키에 nginx-app라는 값을 정의한다.
spec:
  containers: # 파드에서 설정할 컨테이너를 정의한다. 여러개 설정할 수 있다.
  - name: nginx-container # 컨테이너 이름이다. 이 파드는 nginx만 이용하므로 하나 뿐이다.
    image: nginx # 사용할 컨테이너 이미지를 설정한다.
    ports:
    - containerPort: 80 # 컨테이너를 공개할 포트를 설정한다.

💡 2.2.3 EKS 클러스터 동작 확인은 https://ksb-dev.tistory.com/274서 볼 수 있다.

 

3.3.2.2 쿠버네티스의 레이블

위의 매니페스트에서 파드에 ‘app: nginx-app’이라는 레이블을 설정했다. 이 레이블은 무엇을 위해 설정할까?

쿠버네티스는 디플로이먼트나 서비스 같은 다양한 리소스를 식별하기위해 레이블을 사용한다. 즉, 리소스들이 대상으로 하는 파드를 특정 짓기 위해 레이블을 이용한다.

💡 디플로이먼트나 서비스 매니페스트의 셀렉터 항목에 대상 파드의 레이블을 설정한다.

 

3.3.2.3 컨테이너 여러 개를 포함한 파드

컨테이너 여러 개가 모여 하나의 통합된 서비스를 하는 경우가 이에 포함된다. 즉, 하나의 통합된 서비스를 제공하기 위해 해당 파드 내부의 컨테이너들은 반드시 함께 동작 및 정지된다. 여러 컨테이너가 하나의 기능을 위해 상호작용할 수 도 있지만, 보조적인 기능을 할 수도 있다. 구체적으로 다음과 같은 예시가 있을 수 있다.

  • 메인 처리를 실행하는 컨테이너가 출력한 로그를 다른 컨테이너가 읽어 들여 로그 수집 서버나 메트릭 수집 서버에 전송[사이드카 패턴]
  • 메인 처리를 실행하는 컨테이너가 외부 시스템에 접속할 경우, 또 다른 컨테이너가 프록시로 되어 목적지를 할당하거나 요청이 제대로 처리되지 않았을 때 재시도 수행[앰버서더 패턴]

 

3.3.2.4 파드 초기화 컨테이너

파드의 메인 컨테이너를 생성하기 전에 초기화 컨테이너를 실행하도록 설정할 수 있다. 초기화 컨테이너는 이름 그대로 메인 컨테이너를 동작시키기 위해 초기화 처리를 하는 것이 주된 목적이다. 초기화 컨테이너를 여러 개로 설정할 수 있다. 또한, 초기화 컨테이너에 의한 처리는 메인 컨테이너 안에 포함시킬 수 있다. 하지만 초기화 컨테이너로 분리해 처리하면 다음과 같은 장점을 얻을 수 있다.

  • 애플리케이션 자체에 필요 없는 도구를 사용하여 초기화 처리 가능
  • 메인 컨테이너의 보안성을 낮추는 도구를 초기화 컨테이너에 분리시키면 안전하게 초기화 처리 가능
  • 초기화 처리와 애플리케이션을 서로 독립시켜 빌드, 배포 가능
  • 초기화 컨테이너에서는 참조할 수 있지만 메인 컨테이너에서는 참조할 수 없는 시크릿을 설정하면 초기화 처리에만 필요하고 애플리케이션 처리에는 필요 없는 비밀 정보를 메인 컨테이너에서 제외 가능
  • 초기화 컨테이너가 모두 종료될 때까지 메인 컨테이너가 시작되지 않으므로, 메인 컨테이 너를 생성시키는 어떤 조전을 설정하고 그 조진이 성립될 때까지 메인 컨테이너 생성 차단 가능

 

3.2.3 디플로이먼트

디플로이먼트는 일반적으로 파드의 다중화, 버전 업데이트, 롤백을 구현하기 위해 사용된다.

일반적으로 웹 애플리케이션 등을 동작시키는 경우 파드를 직접 배포하지 않고 디플로이먼트라는 오브젝트를 만들어 간접적으로 파드를 배포한다. 디플로이먼트를 이용하여 애플리케이션을 배포하면, 쿠버네티스가 파드를 관리하여 비정상적인 파드가 종료되도 일정 수를 유지할 수 있게 한다. 또한, 디플로이먼트에 구성할 파드 버전(이미지 태그)을 변경하도록 지시하면 새로운 버전의 컨테이너 이미지를 확인하고 동작 중인 파드를 다음 버전으로 변경한다.

3.2.3.1 디플로이먼트 정의

예제 애플리케이션에서 사용된 22_deployment_backend-app_k8s.yaml.template의 명령어와 코드는 아래와 같다.

ECR_HOST=<AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-2.amazonaws.com envsubst < 22_deployment_backend-app_k8s.yaml.template | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment # 이 매니패스트 파일이 '디플로이먼트'라는 종류의 오브젝트라는 것을 의미한다.
metadata:
  name: backend-app # 디플로이먼트의 이름이다.
  labels:
    app: backend-app # 쿠버네티스가 식별할 리소스 이름이다.
spec:
  replicas: 2 # 레플리카셋 정의다. 디플로이먼트를 통해 클러스터 내부에 배포된 파수의 수이다. 클러스터 전체에 파드 2개가 생성된다.
  selector:
    matchLabels:
      app: backend-app # 디플로이먼트를 구성하는 파드 레이블 정의와 일치하는 내용으로 설정한다.
  template: # 파드를 정의하는 설정으로, 파드 템플릿이라고도 한다.
    metadata:
      labels:
        app: backend-app # 파드의 레이블 정의다.
    spec:
      containers:
      - name: backend-app # 컨테이너 이름
        image: ${ECR_HOST}/k8sbook/backend-app:1.0.0 # 컨테이너 이미지 이름이다. 위 명령어를 통해ECR_HOST는 치환된다.
        imagePullPolicy: Always
        ports:
        - containerPort: 8080 # 컨테이너를 공개할 포트를 설정한다.
        env: # 파드에서 사용할 환경 변수로, '시크릿'이라는 오브젝트로 설정하기 위한 부분이다.
        - name: DB_URL
          valueFrom:
            secretKeyRef:
              key: db-url
              name: db-config
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              key: db-username
              name: db-config
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef: # 시크릿의 참조 부분이다. (3.4 참고)
              key: db-password
              name: db-config
        readinessProbe: # 컨테이너가 정상적으로 동작했는지 확인하는 구조이다. (3.5 참고)
          httpGet:
            port: 8080
            path: /health
          initialDelaySeconds: 15
          periodSeconds: 30
        livenessProbe: # 컨테이너가 정상적으로 동작했는지 확인하는 구조이다. (3.5 참고)
          httpGet:
            port: 8080
            path: /health
          initialDelaySeconds: 30
          periodSeconds: 30
        resources: # 파드가 사용할 메모리나 CPU 등의 리소스에 대한 정의다. (3.7 참고)
          requests:
            cpu: 100m
            memory: 512Mi
          limits:
            cpu: 250m
            memory: 768Mi
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 2"]

 

3.2.3.2 디플로이먼트 목록 표시

디플로이먼트 목록을 확인하면 아래와 같다.

kubectl get deplotment

READY에는 디플로이먼트에서 설정한 파드 수와 실제로 동작하고 있는 파드 수가 분수 형태로 표시된다. UP-TO-DATE에는 디플로이먼트에서 정의된 상태(파드에서 사용할 컨테이너 버전, 리소스 제약 상태 등)를 만족하는 파드 수가 나타난다. AVAILABLE에는 실제 사용 가능한 파드 수가 나타난다. AGE는 디플로이먼트가 생성된 이후의 경과 시간을 각각 나타난다.

 

3.2.4 디플로이먼트 하위에서 생성되는 레플리카셋

애플리케이션 배포에는 디플로이먼트를 사용하는데, 디플로이먼트가 직접 파드를 배포하지 않고 그 중간에 레플이카셋이라는 다른 오브젝트를 생성해 배포한다. 레플리카셋은 디플로이먼트에서 파드 수 증감과 파드에 장애가 발생했을 때 자동으로 재시작을 실질적으로 담당한다. 레플리카셋 목록을 확인하면 다음과 같다.

kubectl get replicaset

NAME에는 디플로이먼트 이름 다음에 랜덤으로 설정된 문자열이 추가된다. DESIRED는 레프리카셋에서 설정한 파드 수가 나타난다. CURRENT는 현재 동작하고 잇는 파드의 수가 나타난다. READY는 준비 완료 상태인 파드 수가 나타난다. AGE는 레플리카셋이 생성된 이후의 경과 시간이 나타난다.

3.2.4.1 레플리카셋으로 생성된 파드

레플리카셋에서 생성된 파드를 확인하면 다음과 같다.

kubectl get pod

NAME에는 레플리카셋의 이름에 또 다시 랜덤으로 설정된 문자열이 추가된다. 즉, 디플로이먼트 → 레플리카셋 → 파드가 생성된다는 순서를 의미하기도 한다.

디플로이먼트에서 레플리카셋, 파드가 생성되는 흐름은 아래 그림과 같다.

 

3.2.5 kubectl describe 명령으로 상세 정보 수집

파드의 이름을 통해 상세 정보 내용을 가져올 수 있다.

kubectl describe pod <파드 이름>

 

3.2.6 크론잡으로 스케줄 동작

크론잡은 쿠버네티스에서 특정 시간 간격으로 컨테이너를 동작시키기 위한 구조다. 예제 애플리케이션의 사용된 크론잡 동작 43_cronjob_k8s.yaml.template의 명령어와 코드는 아래와 같다.

ECR_HOST=<AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-2.amazonaws.com envsubst < 43_cronjob_k8s.yaml.template | kubectl apply -f -
apiVersion: batch/v1beta1 # 크론잡 API 버전이다.
kind: CronJob # 크론잡이라는 리소스 타입이다.
metadata:
  name: batch-app # 크론잡 이름
spec:
  schedule: "*/5 * * * *" # (중요) 크론잡의 스케줄 정의다. (3.2.9 참고)
  jobTemplate: # jobTemplate으로, 매번 실행되는 내용에 관한 정보다.
    spec:
      template:
        spec:
          containers: # 크론잡으로 실행할 컨테이너 정의다.
          - name: batch-app
            image: ${ECR_HOST}/k8sbook/batch-app:1.0.0
            imagePullPolicy: Always
            env: # 환경변수. 시크릿과 컨피그 맵에서 가져온다.(3.4 참고)
            - name: DB_URL
              valueFrom:
                secretKeyRef:
                  key: db-url
                  name: db-config
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  key: db-username
                  name: db-config
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  key: db-password
                  name: db-config
            - name: CLOUD_AWS_CREDENTIALS_ACCESSKEY
              valueFrom:
                secretKeyRef:
                  key: aws-accesskey
                  name: batch-secret-config
            - name: CLOUD_AWS_CREDENTIALS_SECRETKEY
              valueFrom:
                secretKeyRef:
                  key: aws-secretkey
                  name: batch-secret-config
            - name: CLOUD_AWS_REGION_STATIC
              valueFrom:
                configMapKeyRef:
                  key: aws-region
                  name: batch-app-config
            - name: SAMPLE_APP_BATCH_BUCKET_NAME
              valueFrom:
                configMapKeyRef:
                  key: bucket-name
                  name: batch-app-config
            - name: SAMPLE_APP_BATCH_FOLDER_NAME
              valueFrom:
                configMapKeyRef:
                  key: folder-name
                  name: batch-app-config
            - name: SAMPLE_APP_BATCH_RUN
              valueFrom:
                configMapKeyRef:
                  key: batch-run
                  name: batch-app-config
          restartPolicy: OnFailure # 크론잡을 재시작하는 조건에 대한 설정이다.

 

3.2.7 크론잡에서의 실행 내용

크론잡이 동작할 시각이 되면 내부적으로 잡(Job)이라는 리소스가 생성된다. 그리고 잡 안에서 파드가 생성된다.

잡이란, ‘일정한 처리를 수행하고 완료하는 태스크를 실행하기 위한것’으로 정의할 수 있다. 디플로이먼트 → 레플리카셋 → 파드의 흐름은 계속적으로 동작하는 서비스가 필요할 때 사용한다. 이에 반대로 잡 → 파드의 흐름은 일정한 처리가 끝나면 프로그램을 종료하고 태스크로 완료 상태가 되기를 원할 때 사용한다. 예를 들어, 특정 시각마다 파일을 읽어 내용을 데이터베이스에 저장되는 일에 잡을 사용할 수 있다.

 

3.2.8 잡 리소스의 동작

3.2.8.1 잡 실행수

잡 리소스에서는 설정 내용에 따라 여러 개의 처리를 병렬로 실행할 수 있다. 잡의 실행 수를 슈정하는 파라미터에는 잡 정의와 관련된 ‘.spec.completions’와 ‘.spec.parallelism’가 있다.

.spec.parallelism’는 그 잡이 완료될 때까지 실행 종료해야 하는 파드 수를 나타낸다. 기본 값은 1이기 때문에 명시적으로 설정하지 않으면 파드 하나가 정상 종료되면 그 잡은 완료됨을 의미한다.

.spec.completions’은 그 잡을 실행할 때 병렬로 실행되는 파드 수를 의미한다. 기본 값은 1이기 때문에 명시적으로 설정하지 않으면 병렬 실행을 수행하지 않는다.

 

3.2.8.2 잡 실행 패턴

  1. 단일 파드를 실행하는 실행 패턴
    1. .spec.completions’와 ‘.spec.parallelism’를 모두 기본값(1)을 사용하는 경우, 잡 리소스 1에 대해 파드 하나가 생성되며 그 파드가 정상 종료되면 잡이 완료된다.
  2. 완료해야 할 파드 수를 설정하는 실행 패턴
    1. .spec.completions’를 설정한 경우, 설정한 수의 파드가 정상 종료되면 그 잡은 완료된다. 이때, ‘.spec.parallelism’는 필수가 아니지만 설정한 수만큼 병렬로 실행된다.
  3. 작업 큐형 실행 패턴
    1. 동시에 파드를 여러 개를 실행하고 작업 큐(Work Queue)순차적으로 처리하는 패턴이다. 처리 대상이 없어지면 완료된다. .spec.completions’는 설정하지 않고, ‘.spec.parallelism’를 설정하여 실행한다.

 

3.2.8.3 잡 재시도 횟수

spec.backoffLimit’으로 파드가 비정상 종료한 경우 몇 번을 재실행할지 설정할 수 있다. 기본값은 6이다. 또, **지수 백오프 지연(Exponential Back-Off)**으로 여러 번 재실행할 때 횟수가 증가함에 따라 재실행을 다시 할 때 까지의 대기 시간을 지수 함수적으로 증가시킬 수 있다. 지수 백오프 지연의 상한이 6분으로, 최대 6분 간격으로 재시도할 수 있다.

 

3.2.9 크론잡 고유의 설정과 유의점

크론잡 고유의 설정 항목으로 중요한 것은 ‘.spec.schedule’와 ‘.spec.concurrentcyPolicy’이다.

3.2.9.1 스케줄 정의

.spec.schedule’으로 잡의 스케줄을 정의할 수 있으며 크론과 같이 스페이스로 구분한 문자열로 설정한다. 각 항목은은 순서대로 분, 시, 일, 월, 요일을 의미한다.

No 설정값 값 범위 비고

1 0~59  
2 0~23  
3 1~31  
4 1~12  
5 요일 0~6 0은 일요일이다.

예제 애플리케이션에서는 다음과 같이 설정했다. (3.2.6 참고)

*/5 * * * *

위 코드는 매 5분마다 동작하는 것을 의미한다.

💡 크론잡에서 설정한 시각은 마스터 노드의 타임존에 따라 달라진다. EKS의 경우 타임존을 UTC를 사용하기 때문에 주의해야 한다.

 

3.2.9.2 동시 실행 제어

‘.spec.concurrencyPolicy’는 이전에 스케줄링된 잡 실행이 미완료인 상태에서 다음 스케줄 시각에 도달했을 때 어떻게 동작할지를 설정한다.

No 설정값 동작

1 Allow 동시 실행 허가. 기본값
2 Forbid 실행 중인 잡을 남기고 신규 잡 생성은 건너뛰기
3 Replace 실행 중인 잡을 종료하고 신규로 잡생성

3.2.9.3 크론잡으로 동작시키는 컨테이너를 개발할 때의 주의점

크론잡은 일정한 실행 시간마다 한번의 잡 오브젝트를 생성한다. ‘약’이라는 단어를 사용한 이유는 특정 환경에서 두 개가 만들어 지거나 생성되지 않을 수 있기 때문이다. 이런 점을 애플리케이션 설계시 유의해야 한다.

3.2.10 컨테이너를 동작시키기 위한 기타 리소스

쿠버네티스에는 파드, 레플리카셋, 디플로이먼트, 잡, 크론잡 이외에 컨테이너를 동작시키기 위한 리소스로 데몬셋(DaemonSet), 스테이드풀셋(StatefulSet)이 있다.

 

3.2.10.1 데몬셋

데몬셋은 노드 각각에 반드시 파드 하나만을 동작시킨다. 주로 모니터링, 로그 수집용으로 사용한다.

 

3.2.10.2 스테이트풀셋

스테이트풀셋은 데이터를 영구히 다룰수 있도록 도와준다. 레플리카셋에서 파드가 재생성된 경우 해당 파드는 매번 초기화된 상태로 동작한다. 만약 데이터베이스와 같이 유지해야할 데이터가 필요한 경우 파드를 재시작하면 데이터 초기화의 문제가 발생할 수 있다. 그래서 스테이트풀셋이 파드 외부에 볼륨 형태로 데이터를 저장한 후에 파드가 재시작되더라도 데이터를 사용할 수 있게 도와준다.

 

3.2.11 네임스페이스

네임스페이스는 컨테이너를 동작시키기 위한 리소스는 아니지만, 컨테이너가 동작하는 클러스터를 논리적으로 사용하기 위한 리소스다,

3.2.11.1 쿠버네티스 표준으로 생성되는 네임스페이스

쿠버네티스에는 표준으로 세 개의 네임스페이가 있다.

  1. default : 명시적으로 네임스페이스를 설정하지 않았을 경우 사용하는 네임스페이스
  2. kube-system : 쿠버네티스에 의해 생성하는 오브젝트가 사용하는 네임스페이스
  3. kube-public : 모든 사용자가 참조할 수 있는 네임스페이스

 

3.2.11.2 예제 애플리케이션에서 사용한 네임스페이스

예제 애플리케이션은 네임스페이스를 사용했다.(2.4.5 참고)

 

3.2.11.3 네임스페이스를 이용한 클러스터 리소스 제한

네임스페이스는 단순히 리소스를 배치하는 논리적인 구분 외에도 리소스 쿼터(3.7.4 참고), 네트워크 정책(4.4.4 참고)와 같은 리소스 또는 RBAC(Role-Based Access Control, 4.4.1 참고)의 구조가 있다. 이를 함께 사용하면 리소스 사용량과 네트워크 통신 제한 또는 클러스터에 대한 권한 제어를 실핼할 수 있다. 본격적으로 쿠버네티스 클러스터를 여러 사용자 또는 그룹에서 공용으로 사용할 경우 네임스페이스를 이용해 여러 가지 제약을 설정해야 하며 운영상 중요한 역할을 담당한다.

 

3.2.11.4 네임스페이스를 정의할 매니페스트 파일

예제 애플리케이션을 구축하면서 사용한 20_create_namespace_k8s.yaml은 아래와 같다.

apiVersion: v1
kind: Namespace
metadata:
  name: eks-work # 네임스페이스 이름