[쿠버네티스 튜토리얼] 기본 개념 학습

Jisu·2023년 12월 1일

Kubernetes

목록 보기
1/12

배경

지난 시간에는 도커 튜토리얼을 통해서 앱을 컨테이너화하여 도커헙에 올리고 docker run으로 실행해보았다. 그리고 파일시스템을 로컬 호스트와 연동하는 방법을 배웠다. 여러 컨테이너는 네트워크로 연결될 수 있으며 예시에서는 mysql와 앱을 연동했다. 이 때 환경변수로 mysql 비밀번호 등을 세팅했다.


컨테이너 오케스트레이션 툴 - 쿠버네티스

이렇게 만들어진 컨테이너는 독립된 앱으로서 기능한다. 그런데 만약 트래픽이 커져서 1개의 컨테이너로 트래픽을 감당하기 어려운 상황이 오면? 어떻게 스케일업할 것인지 로드밸런싱은 어떻게 할 것인지 문제다.

그래서 여러 개의 컨테이너를 자동적으로 관리해주는 소프트웨어가 등장했고 그러한 소프트웨어를 컨테이너를 관리, 지휘한다는 의미에서 컨테이너 오케스트레이션 이라고 부른다.

  • 쿠버네티스는 가장 널리 사용되는 컨테이너 오케스트레이션 툴이다. 구글에서 개발하여 오픈소스로 공개되었다.
  • 쿠버네티스의 구성요소로는 클러스터, 컨트롤플레인, 노드(마스터,워커), 네임스페이스, 파드, 컨테이너 등이 있다.

이제부터는 이 쿠버네티스의 기본개념을 학습하고 여러 실습을 진행해볼 것이다.
공식 문서나 여러 블로그 글에도 이러한 용어들이 공통적으로 등장하니 한번 훝어보고 이후에 용어가 헷갈릴 때 마다 찾아와 지속적으로 상기시켜야한다.


쿠버네티스 클러스터

쿠버네티스를 배포하게 되면 쿠버네티스 클러스터가 형성된다라고 표현한다.
클러스터는 모든 컴포넌트를 포괄하는 추상적인 개념이다.

쿠버네티스 소프트웨어를 구성하는 컨트롤플레인, 노드 등과 같은 모든 구성요소가 이 클러스터 안에 있다고 할 수 있다.

로컬에서 연습할 때는 minikube를 사용하는데, minikube는 로컬에 VM을 만들고 그 안에 클러스터를 만들어준다. 원한다면 클러스터 내에 여러 노드를 생성할 수 있기도 하다. ex.) minikube start node —2


컨트롤 플레인

클러스터의 전반적인 상태를 관리하는 역할을 하는 컴포넌트이다.

예를 들면 새로운 파드를 자동으로 생성하거나 작업을 스케줄링할 수 있다. 내부적으로 아래와 같은 요소들이 있다. 마스터 노드라고도 한다.

kube-apiserver

  • 쿠버네티스 관리자나 다른 컴포넌트들이 클러스터와 통신할 수 있는 API를 제공한다. 예를 들어 노드를 생성하기 위해서는 이 API로 통신해야한다.
  • 쿠버네티스 내 자원(파드, 컨테이너)에 대한 CRUD 요청을 지원한다.
  • 그 외 로깅, 스케줄링 등 관리를 위해 필요한 전반적인 기능들을 지원한다.

etcd

  • 모든 클러스터 내의 데이터를 담는 저장소이다.

kube-scheduler

  • 파드가 실행될 노드를 배정해주고 여러 작업들을 스케줄링하는 역할을 한다.

kube-controller-manager

  • 쿠버네티스 클러스터에서 여러 가지 백그라운드 작업을 수행하며 시스템을 원하는 상태로 유지하는 역할 한다.

워커노드

컨테이너가 파드 형태로 띄워지는 공간이다. 내부 구성요소는 아래와 같다.

kubelet

  • 컨트롤플레인에 리소스 상태를 보고하고 관리한다.
  • 컨테이너의 라이프사이클을 관리해준다

kube-proxy

  • 노드의 네트워크 규칙을 관리한다.

컨테이너 런타임

  • 컨테이너 실행을 담당하는 SW (ex. docker)

파드

쿠버네티스에서 생성하고 관리할 수 있는 어플리케이션 최소 단위

  • 쿠버네티스는 컨테이너를 직접 관리하는 대신 파드를 관리한다.
  • 파드는 여러 컨테이너(워크로드)로 구성될 수 있다 하지만 일반적으로 파드 당 하나의 컨테이너가 존재한다.
  • 파드 내의 컨테이너들은 네트워크와 스토리지를 공유한다는 장점이 있다. 예를 들어 앱과 사이드카 컨테이너를 같은 파드에서 실행해서 로깅 및 모니터링 정보를 관리할 수 있다.

워크로드 리소스

쿠버네티스에서 구동되는 애플리케이션이다. 여러 가지 내장 워크로드 리소스를 제공한다.

워크로드는 OS에서 프로세스가 동작하는 일련의 행위를 뜻한다.
OS와 비교를 하자면, 쿠버네티스가 OS역할을 하고 파드가 프로세스라고 할 수 있다. 이러한 파드를 동작시키기 위해 내장된 리소스들이 워크로드 리소스인 것이다.

ReplicaSet

  • 동일한 파드를 복제하여 서비스를 안정적으로 유지

Deployment

  • 파드와 레플리카 셋을 관리하는 워크로드로 파드 스케일링, 롤링업데이트 및 롤백 기능을 지원
  • 일반적으로 deployment yaml 파일 명세를 통해 RS 숫자와 RS내 실행될 컨테이너 정보를 명세

예시

apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: todo-app
  template:
    metadata:
      labels:
        app: todo-app
    spec:
      containers:
      - name: node-app
        image: rookie0031/getting-started
        workingDir: /app
        command: ["sh", "-c"]
        args:
          - "yarn install && yarn run dev"
        ports:
        - containerPort: 3000
        env:
        - name: MYSQL_HOST
          value: "mysql-jisu"  # Use the correct MySQL service name or DNS name
        - name: MYSQL_USER
          value: "root"
        - name: MYSQL_PASSWORD
          value: "6aePpn5x2M"  # Use the correct MySQL password
        - name: MYSQL_DB
          value: "my_database"

StatefulSet

  • 각 파드에 고유의 identity를 부여해서 재생성되어도 완전히 동일한 State의 파드가 생성되도록 한다.
  • 일반적으로 Database와 같은 앱 배포에 사용됨

예시

apiVersion: apps/v1
kind: StatefulSet
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"labels":{"app.kubernetes.io/component":"primary","app.kubernetes.io/instance":"mysql-jisu","app.kubernetes.io/managed-by":"Helm","app.kubernetes.io/name":"mysql","app.kubernetes.io/version":"8.0.34","helm.sh/chart":"mysql-9.12.3"},"name":"mysql-jisu","namespace":"wrtn-app"},"spec":{"podManagementPolicy":"","replicas":1,"selector":{"matchLabels":{"app.kubernetes.io/component":"primary","app.kubernetes.io/instance":"mysql-jisu","app.kubernetes.io/name":"mysql"}},"serviceName":"mysql-jisu","template":{"metadata":{"annotations":{"checksum/configuration":"a92f02ed0db1396c24e06793b9f6f130c0ca105b679e4826298249c80228095c"},"labels":{"app.kubernetes.io/component":"primary","app.kubernetes.io/instance":"mysql-jisu","app.kubernetes.io/managed-by":"Helm","app.kubernetes.io/name":"mysql","app.kubernetes.io/version":"8.0.34","helm.sh/chart":"mysql-9.12.3"}},"spec":{"affinity":{"nodeAffinity":null,"podAffinity":null,"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchLabels":{"app.kubernetes.io/instance":"mysql-jisu","app.kubernetes.io/name":"mysql"}},"topologyKey":"kubernetes.io/hostname"},"weight":1}]}},"containers":[{"env":[{"name":"BITNAMI_DEBUG","value":"false"},{"name":"MYSQL_ROOT_PASSWORD","valueFrom":{"secretKeyRef":{"key":"mysql-root-password","name":"mysql-jisu"}}},{"name":"MYSQL_DATABASE","value":"my_database"}],"envFrom":null,"image":"docker.io/bitnami/mysql:8.0.34-debian-11-r56","imagePullPolicy":"IfNotPresent","livenessProbe":{"exec":{"command":["/bin/bash","-ec","password_aux=\"${MYSQL_ROOT_PASSWORD:-}\"\nif [[ -f \"${MYSQL_ROOT_PASSWORD_FILE:-}\" ]]; then\n    password_aux=$(cat \"$MYSQL_ROOT_PASSWORD_FILE\")\nfi\nmysqladmin status -uroot -p\"${password_aux}\"\n"]},"failureThreshold":3,"initialDelaySeconds":5,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1},"name":"mysql","ports":[{"containerPort":3306,"name":"mysql"}],"readinessProbe":{"exec":{"command":["/bin/bash","-ec","password_aux=\"${MYSQL_ROOT_PASSWORD:-}\"\nif [[ -f \"${MYSQL_ROOT_PASSWORD_FILE:-}\" ]]; then\n    password_aux=$(cat \"$MYSQL_ROOT_PASSWORD_FILE\")\nfi\nmysqladmin status -uroot -p\"${password_aux}\"\n"]},"failureThreshold":3,"initialDelaySeconds":5,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1},"resources":{"limits":{},"requests":{}},"securityContext":{"runAsNonRoot":true,"runAsUser":1001},"startupProbe":{"exec":{"command":["/bin/bash","-ec","password_aux=\"${MYSQL_ROOT_PASSWORD:-}\"\nif [[ -f \"${MYSQL_ROOT_PASSWORD_FILE:-}\" ]]; then\n    password_aux=$(cat \"$MYSQL_ROOT_PASSWORD_FILE\")\nfi\nmysqladmin status -uroot -p\"${password_aux}\"\n"]},"failureThreshold":10,"initialDelaySeconds":15,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":1},"volumeMounts":[{"mountPath":"/bitnami/mysql","name":"data"},{"mountPath":"/opt/bitnami/mysql/conf/my.cnf","name":"config","subPath":"my.cnf"}]}],"initContainers":null,"securityContext":{"fsGroup":1001},"serviceAccountName":"mysql-jisu","volumes":[{"configMap":{"name":"mysql-jisu"},"name":"config"}]}},"updateStrategy":{"type":"RollingUpdate"},"volumeClaimTemplates":[{"metadata":{"labels":{"app.kubernetes.io/component":"primary","app.kubernetes.io/instance":"mysql-jisu","app.kubernetes.io/name":"mysql"},"name":"data"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"8Gi"}}}}]}}
  creationTimestamp: "2023-10-26T12:28:26Z"
  generation: 5
  labels:
    app.kubernetes.io/component: primary
    app.kubernetes.io/instance: mysql-jisu
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: mysql
    app.kubernetes.io/version: 8.0.34
    helm.sh/chart: mysql-9.12.3
  name: mysql-jisu
  namespace: wrtn-app
  resourceVersion: "407711"
  uid: 840aa6b1-af92-4741-addc-f5844ac5e4d8
spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain
    whenScaled: Retain
  podManagementPolicy: OrderedReady
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/component: primary
      app.kubernetes.io/instance: mysql-jisu
      app.kubernetes.io/name: mysql
  serviceName: mysql-jisu
  template:
    metadata:
      annotations:
        checksum/configuration: a92f02ed0db1396c24e06793b9f6f130c0ca105b679e4826298249c80228095c
      creationTimestamp: null
      labels:
        app.kubernetes.io/component: primary
        app.kubernetes.io/instance: mysql-jisu
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/name: mysql
        app.kubernetes.io/version: 8.0.34
        helm.sh/chart: mysql-9.12.3
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchLabels:
                  app.kubernetes.io/instance: mysql-jisu
                  app.kubernetes.io/name: mysql
              topologyKey: kubernetes.io/hostname
            weight: 1
      containers:
      - env:
        - name: BITNAMI_DEBUG
          value: "false"
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              key: mysql-root-password
              name: mysql-jisu
        - name: MYSQL_DATABASE
          value: my_database
        image: docker.io/bitnami/mysql:8.0.34-debian-11-r56
        imagePullPolicy: IfNotPresent
        livenessProbe:
          exec:
            command:
            - /bin/bash
            - -ec
            - |
              password_aux="${MYSQL_ROOT_PASSWORD:-}"
              if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
                  password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
              fi
              mysqladmin status -uroot -p"${password_aux}"
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        name: mysql
        ports:
        - containerPort: 3306
          name: mysql
          protocol: TCP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -ec
            - |
              password_aux="${MYSQL_ROOT_PASSWORD:-}"
              if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
                  password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
              fi
              mysqladmin status -uroot -p"${password_aux}"
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources: {}
        securityContext:
          runAsNonRoot: true
          runAsUser: 1001
        startupProbe:
          exec:
            command:
            - /bin/bash
            - -ec
            - |
              password_aux="${MYSQL_ROOT_PASSWORD:-}"
              if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
                  password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
              fi
              mysqladmin status -uroot -p"${password_aux}"
          failureThreshold: 10
          initialDelaySeconds: 15
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /bitnami/mysql
          name: data
        - mountPath: /opt/bitnami/mysql/conf/my.cnf
          name: config
          subPath: my.cnf
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        fsGroup: 1001
      serviceAccount: mysql-jisu
      serviceAccountName: mysql-jisu
      terminationGracePeriodSeconds: 30
      volumes:
      - configMap:
          defaultMode: 420
          name: mysql-jisu
        name: config
  updateStrategy:
    type: RollingUpdate
  volumeClaimTemplates:
  - apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      creationTimestamp: null
      labels:
        app.kubernetes.io/component: primary
        app.kubernetes.io/instance: mysql-jisu
        app.kubernetes.io/name: mysql
      name: data
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 8Gi
      volumeMode: Filesystem
    status:
      phase: Pending
status:
  availableReplicas: 1
  collisionCount: 0
  currentReplicas: 1
  currentRevision: mysql-jisu-86cd485794
  observedGeneration: 5
  readyReplicas: 1
  replicas: 1
  updateRevision: mysql-jisu-86cd485794
  updatedReplicas: 1

PersistentVolume

  • 클러스터내의 데이터 저장공간

예시

kind: PersistentVolume
metadata:
  annotations:
    hostPathProvisionerIdentity: 9948a7ee-6671-403d-8a41-bfe641742451
    pv.kubernetes.io/provisioned-by: k8s.io/minikube-hostpath
  creationTimestamp: "2023-10-26T12:28:26Z"
  finalizers:
  - kubernetes.io/pv-protection
  name: pvc-45e9fc59-e4fe-41bd-a928-4ab546825b0d
  resourceVersion: "404741"
  uid: 9f7b2608-9a6b-4929-90f7-d0ba26feb3dc
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 8Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: data-mysql-jisu-0
    namespace: wrtn-app
    resourceVersion: "404731"
    uid: 45e9fc59-e4fe-41bd-a928-4ab546825b0d
  hostPath:
    path: /tmp/hostpath-provisioner/wrtn-app/data-mysql-jisu-0
    type: ""
  persistentVolumeReclaimPolicy: Delete
  storageClassName: standard
  volumeMode: Filesystem
status:
  phase: Bound

서비스

배포된 서비스를 네트워크 서비스로 외부에 노출시키고 트래픽을 관리하는 객체이다.

  • 외부에 개방항 포트를 명세하고 이를 컨테이너의 타켓포트로 라우팅한다.
  • 파드에게 고유한 IP 주소와 파드 집합에 단일 DNS 명을 부여하고 로드밸런싱을 수행할 수 있음
  • Pods는 deployment 문제가 생기면 종료되고 새로 생성될 수 있다.

인그레스

클러스터 내 서비스에 대한 외부 접근을 관리하는 API 오브젝트이다.

클러스터 외부에서 내부 서비스로 HTTP와 HTTPS 경로를 노출한다. 외부 트래픽이 파드에 전달되는 과정은 아래와 같다.


한눈에 보는 쿠버네티스


Reference

쿠버네티스 공식문서

쿠버네티스 컴포넌트

쿠버네티스 오브젝트 설명

파드

서비스 설명

Service

profile
기술 공유를 즐기는 DevOps Engineer 장지수입니다.

0개의 댓글