Kubernetes Controller 1

Chang-__-·2023년 4월 19일
0
post-thumbnail

K8S Controller

Controller는 서비스를 관리하고 운영하는데 기여를 한다.
1. Auto Healing: Pod가 죽으면 Controller가 Pod를 다시 살리는 역활을 한다.
2. Auto Scaling: Pod의 리소스가 다 차있는 상태가 되었을때 컨트롤러가 알아서 파드를 새로 만들어준다.
3. Software Update: 여러 Pod에 코드를 업데이트 시키기 위해서 Controller가 Pod들을 update 시켜준다. 만약에 update 중에 문제가 생기면 자동적으로 Rollback 해주는 기능도 있다.
4. Job: 필요한 순간에 배치를 돌려야할 때 k8s 클러스터는 필요한 시점마다 Pod를 생성하여 원하는 작업을 수행하고 Pod를 삭제 시킨다.

ReplicaSet

Template

보통 컨트롤러는 Pod와 연결을 할때 Service와 연결했던것 처럼 label과 selector로 연결이 된다.
Pod가 죽으면 Temlpate에 있는 내용으로 Pod를 새로 만들어주게 된다. 만약에 Pod가 업데이트 되면 template이 업데이트 되고 기존에 연결중인 Pod가 죽으면 새로 생성된 temlplate의 Pod가 만들어진다.
예를들어 아래와 같은 Pod가 있다고 가정을 해보자.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    type: web
spec:
  containers:
  - name: container
    image: kubetm/app:v1
  terminationGracePeriodSeconds: 0

그러면 ReplicaSet은 metadata.labels.type이 Pod와 같아야 한다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replica1
spec:
  replicas: 1
  selector:
    matchLabels:
      type: web
  template:
    metadata:
      name: pod1
      labels:
        type: web
    spec:
      containers:
      - name: container
        image: kubetm/app:v1
      terminationGracePeriodSeconds: 0

spec.template.metadata 쪽에 Pod 내가 템플릿으로 만들어주고 싶은 Pod의 label과 맞춰주면 된다.

Replicas

Replica의 기능은 간단한데, replicas가 1이면 Pod가 하나만 생기고 replicas가 3이면 3개의 Pod가 생성된다. 아주 간단하게 spec.replicas를 수정해주면 된다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replica1
spec:
  replicas: 3
  selector:
    matchLabels:
      type: web
  template:
    metadata:
      name: pod1
      labels:
        type: web
    spec:
      containers:
      - name: container
        image: kubetm/app:v1
      terminationGracePeriodSeconds: 0

Selector

ReplicaSet의 selector에는 두가적인 속성이 있다.
1. matchLabels: 키와 벨류가 같아야 Pod와 ReplicaSet이 매핑이되는 속성이다.
2. matchExpressions: matchLabels보다 키 벨류를 디테일 하게 컨트롤 할수 있다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replica1
spec:
  replicas: 1
  selector:
    matchLabels:
      type: web
      ver: v1
    matchExpressions:
    - {key: ver, operator: Exists}
  template:
    metadata:
      labels:
        type: web
        ver: v1
        location: dev
    spec:
      containers:
      - name: container
        image: kubetm/app:v1
      terminationGracePeriodSeconds: 0

spec.selector.matchLabels.matchExpressions에 명시가 되어있는데 ver가 키인 모든 파드들을 전부 컨트롤할 수 있다.
operator는 총 4종류가 있다.

  • Exists: 내가 키를 정하고 그 키와 맞는 모든 Pod들을 매핑한다.
  • DoesNotExist: 내가 지정한 키를 빼고 모든 Pod들을 매핑한다.
  • In: 키와 벨류를 지정하여 키에 속하고 벨류에도 속하는 Pod들을 매핑한다.
  • NotIn: 키 벨류를 지정하여 키에 속하고 벨류에는 속하지 않는 Pod들을 매핑한다.

Deployment

Deployment는 Pod를 업데이트 해야할때 재배포를 해야하는데 이를 가능하게 해주는 Controller 다.
여기서 Deployment 전략이 4가지가 있다.

  • ReCreate: 기존에 생성되어있는 Pod를 삭제하고, 다시 Pod를 생성한다. 여기서 단점은 배포할 때 Downtime이 발생하는데 서비스가 배포 완료되기 전까지 멈추게 된다.
  • Rolling Update: 새로 업데이트 해야할 Pod를 하나 생성하고 기존에 있던 Pod하나를 삭제한다. 만약에 Pod가 2개 이상이 되면 Pod를 만들고 삭제하고 또 새로운 Pod를 하나 더 만들고 이전의 Pod는 삭제하기 때문에 배포하는 도중에 누군가는 v1 에 접속하고 누군가는 v2에 접속할수 있는 가능성이 있다.
  • Blue/Green: Blue/Green 방식은 ReplicaSet이 있을 경우에 사용이 가능한데, label 이 v1이 달린 pod들이 있고 v2 label을 배포해야하면 v2 label이 달린 pod들을 먼저 만들고 Service에서 connection을 v2 로 옮긴다음 v1 Pod들을 삭제한다.
  • Canary: Blue/Green 과 거의 동일한 방식이지만 한가지 다른게 있다면 Canary는 Pod 버전업을 할 때 테스팅을 하는것이다. v1의 Pod가 2개가 있고 마찬가지로 v2를 배포하는 상황이라면 v2를 관장하는 Controller를 생성한다. (Blue/Green 과 동일) 여기서 부터 BG 방식과 차이가 나는게 있는데 v1은 v1대로 Service를 생성하고 v2는 v2대로 Service를 생성한다. 여기서 Ingress Controller를 통해 기존에 /app 이라는 도메인으로 서빙을 했다면 /v2/app 이라는 도메인을 생성한다. (기존의 v1 Service는 /app v2 Service는 /v2/app) 여기서 v2의 서비스가 문제가 없으면 v2 Service의 url을 /app으로 변경하고 기존에 있던 v1 Service는 삭제한다.

이중에서 ReCreate, Rolling Update를 다뤄보도록 하겠다.

ReCreate

Deployment에는 기본적으로 selector와 replicas template을 명시해줘야 하는데, 이 값들은 ReplicaSet을 만들고 여기에 있는 값들을 지정해주기 위한 값들이다. 그리고 ReplicaSet들은 Pod들을 만들게 된다. 그 다음 연결된 Service와 connection을 맺어 서빙을 하게 되는데 위에서 설명 했듯이 새로운 버전이 업데이트 되면 기존에 있던 Pod들을 제거하고 새로운 ReplicaSet을 만들어 Pod를 생성하게 한다. 그리고 기존에 있던 Service와 connection을 맺어준다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-1
spec:
  selector:
    matchLabels:
      type: app
  replicas: 2
  strategy:
    type: Recreate
  revisionHistoryLimit: 1
  template:
    metadata:
      labels:
        type: app
    spec:
      containers:
      - name: container
        image: kubetm/app:v1
      terminationGracePeriodSeconds: 10

이런 deployment가 있으면 type:app 이라는 labels 가 있을테고 container 라는 이름의 Pod가 생성될것이다. strategy를 Recreate로 걸어주면 된다. 여기서 서비스를 연결시켜줘야 하는데 다음과 같이 연결 시켜주면 된다.

apiVersion: v1
kind: Service
metadata:
  name: svc-1
spec:
  selector:
    type: app
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080

selector 로 type: app 을 지정해 방금 Deployment의 labels와 동일하게 해주면 Service와 Pod 가 연결될 것이다.

Rolling Update

Rolling Update는 v1의 Pod가 2개가 있으면 먼저 v2 ReplicaSet에 replicas를 1개로 바꿔주고, replicas가 1이기 때문에 Pod를 하나 만들어준 다음 Service에 연결을 해준다. 그런다음 v1 에 있는 Replicas를 1로 변경 시켜주고 Pod를 하나 죽인다. 삭제가 끝나면 v2의 replicas를 2로 바꿔주고 v2에 Pod를 2개로 만든다음 v1의 replicas를 0으로 만들어 v1에는 파드가 전부 없어지게 된다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  selector:
    matchLabels:
      type: app
  replicas: 2
  strategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        type: app
    spec:
      containers:
      - name: container
        image: kubetm/app:v1
      terminationGracePeriodSeconds: 0

DeamonSet

각각의 Node들의 자원이 다르게 남아있는 상태에서 ReplicaSet은 Pod를 Scheduler에 의존에서 각각의 Node에 배치를 해준다. 반대로 DeamonSet은 Node의 자원상태랑 상관없이 모든 노드에 Pod를 하나씩 배치를 해준다. 어떨때 DeamonSet을 사용할까?

  • 성능지표수집 (Prometheus)
  • Logging (EFK/ELK)
  • Storage (network file system)

등등에서 필요할수도 있다.

DeamonSet은 selector와 template이 있어서 모든 Node에 template으로 Pod를 만들고, selector와 label로 연결이 된다.
특정노드는 OS가 달라서 Pod를 안만들어야 할수도 있는데, nodeSelector를 사용해서 Pod를 생성해야 하는 Node에만 Pod를 만들수도 있다.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: daemonset-1
spec:
  selector:
    matchLabels:
      type: app
  template:
    metadata:
      labels:
        type: app
    spec:
      containers:
      - name: container
        image: kubetm/app
        ports:
        - containerPort: 8080
          hostPort: 18080

여기서 spec.template.spec.containers.ports.hostPort가 있는데, Service의 NodePort 타입과 동일하게 특정 노드의 IP로 Pod에 접근할 수 있다.

참고로 node에 라벨링은 다음과 같이 하면된다.

kubectl label nodes k8s-node1 os=centos
kubectl label nodes k8s-node2 os=ubuntu

CronJob

Job

Pod는 Kind가 Pod, ReplicaSet, Job에 의해서 만들어질 수 있는데 특정노드가 죽었을 때 그냥 Pod로 만든 서비스는 장애가 계속나겠지만 ReplicaSet 이나 Job처름 Controller로 만들어진 Pod는 Node2로 ReCreate 되어서 서비스는 계속 될것이다. 이때 ReplicaSet으로 만들어진 Pod는 Restart를 해서 Pod를 다시 띄우겠지만 Job으로 만들어진 Pod는 Finish가 되면 더 이상 Pod에 자원을 소모하지 않을것이다.

Job은 template과 selector가 있는데, selector는 쿠버네티스 클러스터에서 알아서 지정해준다.

apiVersion: batch/v1
kind: Job
metadata:
  name: job
spec:
  completions: 6
  parallelism: 2
  activeDeadlineSeconds: 30
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: container
        image: kubetm/init
        command: ["sh", "-c", "echo 'job start';sleep 20; echo 'job end'"]
      terminationGracePeriodSeconds: 0

completions 옵션으로 여러개의 Pod를 하나씩 순차적으로 실행시켜서 모든 Pod의 작업이 끝나야 Job도 종료가 된다.
parallelism 옵션으로 여러개의 Pod를 동시에 실행시킬수 있다.
activeDeadlineSeconds 지정한 시간만큼 Job이 실행이 되고 실행중인 Pod가 있거나 앞으로 실행해야할 Pod가 있을 경우에도 Job을 종료시킨다. 보통 10초안에 끝날일이 1분이 되어도 끝나지 않으면 장애로 판단을 해서 Job을 종료시키고 자원을 Realease 시켜준다.

CronJob

Cronjob은 Job들을 주기적인 시간에 생성을해서 사용한다. 보통 Job을 혼자서 쓰지않고, CronJob을 이용해서 주기적인 시간에 Job을 생성한다. 기존에 Batch를 돌려야 하는것들이 있다면 CronJob으로 해결이 가능하다.


apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cron-job-2
spec:
  schedule: "20,21,22 * * * *"
  concurrencyPolicy: Replace
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: container
            image: kubetm/init
            command: ["sh", "-c", "echo 'job start';sleep 140; echo 'job end'"]
          terminationGracePeriodSeconds: 0

schedule job을 실행할 지정시간을 걸어줄수 있다.
concurencyPolicy Allow, Forbid, Replace 3가지가 있는데 Allow로 해두면 내가 지정한 시간에 다른 Job이 돌고 있어도 Job이 만들어지고, Forbid로 해두면 특정 스케쥴시간에 Job이 돌고 있으면 그 Job이 끝나는 그 다음 스케쥴시간에 Job이 돌게된다. Replace는 Job이 돌고 있을때 새로운 Pod로 교체를 해준다.

0개의 댓글