Kubernetes

Yooncastle·2022년 11월 22일
0

컨테이너를 쉽고 빠르게 배포/확장하고 관리를 자동화해주는 오픈소스 플랫폼

쿠버네티스 특징

다양한 배포 방식

Deployment : 새로운 버전의 앱을 다양한 전략으로 무중단 배포
StatefulSets : 실행 순서를 보장, 호스트 이름과 볼륨을 일정하게 사용
DaemonSet : 로그나 모니터링 등 모든 노드에 설치가 필요한 경우 사용
Job, CronJob : 배치성 작업

Ingress 설정

웹 애플리케이션을 배포하는 과정을 보면 외부에서 직접 접근할 수 없도록 애플리케이션을 내부망에 설치하고 외부에서 접근이 가능한 ALB나 Nginx, Apache를 프록시 서버로 활용

프록시 서버는 도메인과 path 조건에 따라 등록된 서버로 요청을 전달하는데 서버가 바뀌거나 IP가 변경되면 매번 설정을 수정해줘야함

쿠버네티스의 Ingress는 이를 자동화하면서 기존 프록시 서버에서 사용하는 설정을 거의 그대로 사용

하나의 클러스터에 여러 개의 Ingress 설정을 할 수 있어 관리자 접속용 Ingress와 일반 접속용 Ingress를 따로 관리 가능

클라우드 지원

클라우드 별로 적절한 API를 사용하는 모듈 필요

Cloud Controller를 이용하여 클라우드 연동을 손쉽게 확장

다양한 클라우드 업체(AWS, Google Cloud, Azure...)에서 모듈을 제공하여 관리자는 동일한 설정 파일을 서로 다른 클라우드에서 사용가능

Namespace & Label

RBAC (role-based access control)

  • 접근 권한 시스템
  • 각각의 리소스에 대해 유저별로 CRUD 권한을 손쉽게 지정
  • 클러스터 전체에 적용하거나 특정 네임스페이스에 적용
  • AWS의 경우 IAM을 연동해서 사용 가능

CRD (Custom Resource Definitaion)

쿠버네티스가 제공하지 않는 기능을 기본 기능과 동일한 방식으로 적용하고 사용

Ex) 쿠버네티스는 기본적으로 SSL 인증서 관리 기능을 제공하지 않지만,
cert-manager를 설치하고 Certificate 리소스를 이용하면 익숙한 쿠버네티스 명령어로 인증서를 관리가능

Auto Scaling

CPU, memory 사용량에 따른 확장은 기본이고 현재 접속자 수와 같은 값을 사용가능

Horizontal Pod Autoscaler(HPA) : 컨테이너 개수 조정 방식
Vertical Pod Autoscaler(VPA) : 컨테이너의 리소스 할당량 조정 방식
Cluster Autosclaer(CA) : 서버 개수 조정 방식

Federation, Multi Cluster

클라우드에 설치한 쿠버네티스 클러스터와 자체 서버에 설치한 쿠버네티스를 묶어서 하나로 사용가능

쿠버네티스 기본 개념

Desired State

쿠버네티스가 하는 일은 결국 현재 상태를 모니터링하면서 관리자가 설정한 원하는 상태를 유지하는 작업

✅ Pod

  • 배포할 수 있는 가장 작은 단위
  • 한 개 이상의 컨테이너와 스토리지, 네트워크를 가짐
  • pod에 속한 컨테이너는 스토리지와 네트워크 공유 (서로 localhost로 접근)
apiVersion: v1
kind: Pod
metadata:
  name: echo
  labels:
    app: echo
spec:
  containers:
    - name: app
      image: ghcr.io/subicura/echo:v1

version : 오프젝트 버전 ex) v1, networking.k8s.io/v1, ...
kind : 종류 ex) Pod, ReplicaSet, Deployment, Service, ...
metadata : 메타데이터 ex) name과 label, annotation(주석)으로 구성
spec : 상세명세 ex) 리소스 종류 별로 다름

livenessProbe
컨테이너가 정상적으로 동작하는지 체크하고 정상적으로 동작하지 않는다면 컨테이너를 재시작하여 문제를 해결

apiVersion: v1
kind: Pod
metadata:
  name: echo-lp
  labels:
    app: echo
spec:
  containers:
    - name: app
      image: ghcr.io/subicura/echo:v1
      livenessProbe:
        httpGet: << 정상적으로 동작하는지 체크하는 방식 중 하나 (tcpSocket, exec)
          path: /not/exist << 존재하지 않는 path(/not/exist)와 port(8080)를 입력
          port: 8080
        initialDelaySeconds: 5
        timeoutSeconds: 2 # Default 1
        periodSeconds: 5 # Defaults 10
        failureThreshold: 1 # Defaults 3

정상적으로 응답하지 않았기 때문에 pod가 여러번 재시작되고 CrashLoopBackOff상태로 변경됨

readinessProbe
컨테이너가 준비되었는지 체크하고 정상적으로 준비되지 않았다면 Pod으로 들어오는 요청을 제외
livenessProbe와 차이점은 문제가 있어도 Pod을 재시작하지 않고 요청만 제외

...
containers:
    - name: app
      image: ghcr.io/subicura/echo:v1
      readinessProbe: <<
        httpGet:
          path: /not/exist
          port: 8080
...


보통 livenessProbereadinessProbe를 같이 적용

다중 컨테이너

apiVersion: v1
kind: Pod
metadata:
  name: counter
  labels:
    app: counter
spec:
  containers:
    - name: app << 
      image: ghcr.io/subicura/counter:latest
      env:
        - name: REDIS_HOST
          value: "localhost"
    - name: db << 
      image: redis

✅ ReplicaSet

  • pod를 여러개 복제하여 관리하는 오브젝트 (개수 유지)
  • 복제할 개수, 개수를 체크할 라벨 선택자, 생성할 pod 설정값(템플릿)을 가짐
  • 직접적으로 replicaSet을 사용하기보다는 Deployment등 다른 오프젝트에 의해 사용됨
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: echo-rs
spec:
  replicas: 1 <<
  selector: << 
    matchLabels:
      app: echo
      tier: app
  template: << 
    metadata:
      labels:
        app: echo
        tier: app
    spec:
      containers:
        - name: echo
          image: ghcr.io/subicura/echo:v1

spec.selector : label 체크 조건
spec.replicas : 원하는 pod 수
spec.template : 생성할 pod의 명세

✅ Deployment

apiVersion: apps/v1
kind: Deployment << 
metadata:
  name: echo-deploy
spec:
  replicas: 4 
  selector: 
    matchLabels:
      app: echo
      tier: app
  template: << 
    metadata:
      labels:
        app: echo
        tier: app
    spec:
      containers:
        - name: echo
          image: ghcr.io/subicura/echo:v1
apiVersion: apps/v1
kind: Deployment << 
metadata:
  name: echo-deploy
spec:
  replicas: 4 
  selector: 
    matchLabels:
      app: echo
      tier: app
  template: << 
    metadata:
      labels:
        app: echo
        tier: app
    spec:
      containers:
        - name: echo
          image: ghcr.io/subicura/echo:v2 << 

새로운 버전의 Pod을 생성하고 기존 Pod을 제거됨

# 히스토리 확인
kubectl rollout history deploy/echo-deploy

# revision 1 히스토리 상세 확인
kubectl rollout history deploy/echo-deploy --revision=1

# 바로 전으로 롤백
kubectl rollout undo deploy/echo-deploy

# 특정 버전으로 롤백
kubectl rollout undo deploy/echo-deploy --to-revision=2

동시에 업데이트하는 pod의 개수 조정 (default=25%)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo-deploy-st
spec:
  replicas: 4
  selector:
    matchLabels:
      app: echo
      tier: app
  minReadySeconds: 5
  strategy: << 
    type: RollingUpdate 
    rollingUpdate:
      maxSurge: 3
      maxUnavailable: 3
  template:
    metadata:
      labels:
        app: echo
        tier: app
    spec:
      containers:
        - name: echo
          image: ghcr.io/subicura/echo:v1
          livenessProbe:
            httpGet:
              path: /
              port: 3000

✅ Service

  • pod를 외부 네트워크와 연결
  • 여러 개의 pod를 바라보는 내부 로드 밸런서를 생성할 때 사용
  • 내부 DNS에 서비스 이름을 도메인으로 등록 (서비스 디스커버리 역할)
  • ClusterIp, NodePort, LoadBalancer 타입으로 나뉨

ClusterIp

  • 여러 개의 pod를 바라보는 내부 로드 밸런서

  • 서비스 이름을 내부 도메인 서버에 등록하여 Pod 간에 서비스 이름으로 통신할 수 있음

  • counter 앱 중 redis

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: redis
    spec:
      selector:
        matchLabels:
          app: counter
          tier: db
      template:
        metadata:
          labels:
            app: counter
            tier: db
        spec:
          containers:
            - name: redis
              image: redis
              ports:
                - containerPort: 6379
                  protocol: TCP
    
    ---
    apiVersion: v1
    kind: Service << 
    metadata:
      name: redis << 
    spec:
      ports:
        - port: 6379
          protocol: TCP
      selector:
        app: counter
        tier: db

spec.ports.port : 서비스가 생성할 Port
spec.ports.targetPort : 서비스가 접근할 Pod의 Port (기본: port랑 동일)
spec.selector : 서비스가 접근할 Pod의 label 조건

  • redis에 접근할 counter 앱

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: counter
    spec:
    selector:
      matchLabels:
        app: counter
        tier: app
    template:
      metadata:
        labels:
          app: counter
          tier: app
      spec:
        containers:
          - name: counter
            image: ghcr.io/subicura/counter:latest
            env:
              - name: REDIS_HOST <<
                value: "redis"
              - name: REDIS_PORT 
                value: "6379"
    

counter app pod 에서 redis Pod로 접근이 가능

ubectl apply -f counter-app.yml

# counter app에 접근
kubectl get po
kubectl exec -it counter-<xxxxx> -- sh

# curl localhost:3000
>> 1
# curl localhost:3000
>> 2

# telnet redis 6379
  dbsize
  KEYS *
  GET count
  >> 2 
  quit

NodePort

  • clustIp는 클러스터 내부에서만 접근 가능
  • 클러스터 외부(노드)에서도 접근 가능하게 port 지정
  • 클러스터의 모든 노드에 포트를 오픈
  • nodePort는 clusterIp의 기능을 기본으로 포함
apiVersion: v1
kind: Service
metadata:
  name: counter-np
spec:
  type: NodePort
  ports:
    - port: 3000
      protocol: TCP
      nodePort: 31000
  selector:
    app: counter
    tier: app

spec.ports.nodePort : 노드에 오픈할 Port (미지정시 30000-32768 중에 자동 할당)


LoadBalancer

  • nodePort의 단점은 노드가 사라졌을 때 자동으로 다른 노드를 통해 접근이 불가
  • 예를 들어, 3개 노드 중 아무 노드로 접근해도 nodePort로 연결 할 수 있지만 어떤 노드가 살아있는지 확인 불가
  • 브라우저는 nodePort에 직접 요청하지 않고 loadBalancer에 요청
  • loadBalancer가 자동으로 살아 있는 노드에 접근

Load Balancer는 AWS, Google Cloud, Azure 같은 클라우드 환경이 아니면 사용이 제한적임

특정 서버(노드)를 가리키는 무언가(Load Balancer)가 필요한데 이런 무언가가 가상머신이나 로컬 서버에는 존재 X

Volume

  • 저장소와 관련된 오브젝트
  • 대부분의 저장 방식을 지원
  • Pod 안에 속한 컨테이너 간 디렉터리 공유 : empty-dir
  • 호스트 디렉터리를 컨테이너 디렉토리에 연결 : host-log

Object Spec - YAML

  • 오프젝트의 종류와 원하는 상태 명시
  • 명세를 CRUD로 관리할 수 있어서 REST API로 쉽게 노출 가능 ?

배포 방식

앱을 배포하기 위해 원하는 상태(desired state)를 다양한 오브젝트에 라벨을 붙여 정의(yaml)하고 API서버에 전달하는 방식

ex) 컨테이너를 2개 배포하고 80 포트로 오픈 ⬇️

  • 컨테이너를 Pod으로 감싸고 type=app, app=web이라는 라벨링
  • type=app, app=web이라는 라벨이 달린 Pod가 2개 있는지 체크
    (없으면 Deployment Spec에 정의된 템플릿을 참고해서 Pod를 생성)
  • 해당 라벨을 가진 Pod을 바라보는 가상의 서비스 IP 생성
  • 외부의 80 포트를 방금 만든 서비스 IP랑 연결

쿠버네티스 구조

master에 API서버와 상태 저장소를 두고 각 node의 kubelet과 통신하는 구조

특정 node의 컨테이너에 명령하거나 로그를 조회할 떄도 node에 직접 명령하는게 아니라 master에 명령을 내리고 master가 node에 접속해서 대신 결과 응답

Master

  • 전체 클러스터를 관리
  • 모든 명령은 master의 API서버를 호출
  • 관리자만 접속가능(보안 설정 필요)
  • master가 죽으면 클러스터를 관리할 수 없기 때문에 보통 3대를 구성(안전성⬆️)

kube-apiserver : API 서버

  • 모든 요청을 처리하는 마스터의 핵심 모듈
  • 원하는 상태를 key-value저장소에 저장
  • 저장된 상태를 조회
  • 노드에서 실행 중인 컨테이너의 로그를 조회, 명령을 보내는 등 디버거 역할
  • pod를 노드에 할당하고 상태를 체크하는 일은 다른 모듈로 분리

etcd : 분산 데이터 저장소

  • key-value저장소
  • 저장, 조회, 상태가 변경여부 watch
  • 클러스터의 모든 설정, 상태 데이터는 etcd에 저장되고 나머지 모듈은 stateless하게 동작하기 때문에 etcd만 백업해두면 클러스터 복구 가능

스케줄러, 컨트롤러

kube-scheduler
할당되지 않은 pod를 여러 조건에 따라 적절한 노드 서버에 할당

kube-controller-manager

  • 거의 모든 오브젝트의 상태 관리
  • 오브젝트 별 분업화 되어
    Deployment는 Replicaset을 생성
    Replicaset은 pod를 생성
    pod는 스케줄러가 관리

cloud-controller-manager

  • AWS, GCE, Azure등 클라우드에 특화된 모듈
  • 노드를 추가/제거
  • 로드밸런서를 연결하거나 볼륨 설정

Node

  • master와 통신하면서 필요한 pod 생성, 네트워크와 볼륨 설정
  • 실제 컨테이너들이 생성되는 곳
  • 각각의 서버에 라벨을 붙여 사용 목적 정의

kubelet

  • pod의 생명주기 관리
  • pod를 생성
  • 주기적으로 마스터에 pod 안의 컨테이너 상태 전달
  • API서버의 요청을 받아 컨테이너 로그 전달 또는 명령 수행

kube-proxy

  • pod로 연결되는 네트워크를 관리
  • TCP, UDP, SCTP 스트림을 포워딩
  • 여러개의 pod를 라운드로빈 형태로 묶어 서비스 제공
  • History
    kube-proxy 자체가 프록시 서버로 동작하면서 실제 요청을 받아서 pod에 전달 ➡️ iptables 설정하는 방식으로 변경 ➡️ iptables에 등록된 규칙이 많아지면 성능 저하 ➡️ IPVS 지원

Kubectl

API서버는 json또는 protobuf형식을 이용한 http 통신을 지원하지만 kubectl이라는 명령행 도구를 사용

Pod 생성 과정

관리자가 ReplicaSet을 생성하면 아래와 같은 과정을 거쳐 Pod 생성

각 모듈은 서로 통신하지 않고 오직 API서버를 통해 etcd에 저장된 상태를 체크하고 현재 상태와 원하는 상태가 다르면 필요한 작업을 수행하는 방식

kubectl

  • ReplicaSet 명세를 yml파일로 정의하고 kubectl 도구를 이용하여 API Server에 명령을 전달
  • API Server는 새로운 ReplicaSet Object를 etcd에 저장

kube Controller

  • 🔄 Kube Controller에 포함된 ReplicaSet Controller가 ReplicaSet을 감시하다가 ReplicaSet에 정의된 Label Selector 조건을 만족하는 Pod가 존재하는지 체크
  • 해당하는 Label의 Pod가 없으면 ReplicaSet의 Pod 템플릿을 보고 새로운 Pod(no assign)을 생성 (API Server에 전달하고 API Server는 etcd에 저장)

Scheduler

  • 🔄 할당되지 않은(no assign) Pod이 있는지 체크
  • 할당되지 않은 Pod이 있으면 조건에 맞는 Node를 찾아 해당 Pod를 할당

kubelet

  • 🔄 자신의 Node에 할당되었지만 아직 생성되지 않은 Pod가 있는지 체크
  • 생성되지 않은 Pod이 있으면 명세를 보고 Pod을 생성
  • Pod의 상태를 주기적으로 API Server에 전달

위 과정은 ReplicaSet이 pod를 생성하는 과정이지만 모든 노드에 pod를 배포하는 DaemonSet도 동일한 방식으로 동작


Reference

https://subicura.com/2019/05/19/kubernetes-basic-1.html
https://subicura.com/k8s/guide/

profile
기억보단 기록을

0개의 댓글