k8s/concept

controller/ replicaSet & deployment

부엉이사장 2023. 11. 30. 19:24

하씨발 이틀동안 적어놓은 포스팅 임시저장 싹다 사라져서 다시 적어야 한다

ㅈ같은씨발

 

 

# replicaSet

replicaSet은 replication controller와 거의 비슷하다고 봐도 될것같다.

그냥 파드 수를 보장하는기능은 가능한데 조금더 업그레이드 된 기능은 더욱 풍부한 라벨셀렉터를 지원한다는 점이다.

단순히 replication controller는 라벨을 AND연산으로 관리한다고 하면,

replica Set의 셀렉터는 OR연산등 여러가지를 지원한다.

 

 

 

# replication controller와 replicaSet의 yaml형식 비교

replication controller와 뭐 다 비슷하게 생겼지만 셀렉터가 다르다.

얘는 matchLabels으로 한번 더 들어간다. 그리고 그 이후는 키:값 쌍으로 똑같다.

하지만 이게 다가 아니다. matchLabels와 같은 위치에 matchExpressions을 쓸 수 있다.

++ matchLabels와 matchExpressions는 서로 AND연산이다.

 

 


# Selector - matchExpressions

공식문서는 이렇게 나와있다.

약간 라벨링을 뭐랄까.. 조건문으로 달 수 있는 그런 느낌이라고 봐야할것같다.

 matchExpressions에 리스트 형식으로 오브젝트형식으로 넣게되는데,

안에 키값들은 key, operator, values로 구성된다.

저기서 key와 valuse는 우리가 matchLabels에 썼던 키,값으로 들어가는 곳이다.

values에는 []로 js의 어레이 쓰듯이 값들을 넣어줘야한다.

그리고 operator가 특이한테 아래 파트에서 소개하겠다.

 

 

# Operator종류들

위에 사진에서 드래그 해놓은걸 보면 In, Notin, Exists, DoNotExist등의 operator들을 쓸 수 있다.

- In 오퍼레이터

{key: tier, operator: In, values: [cache]}

뭐 공식문서 selector에서 가져온 예제인데,

tier:cache의 라벨링이 있어야 한다는 뜻이다.

하지만 만약 values에 두개이상의 값이 들어간다면?

{key: tier, operator: In, values: [cache,muzzi]}

이 경우는 tier에 cache나 muzzi중 하나만 연결되도 셀렉해주겠다는 뜻이다.

즉 OR연산으로 볼 수 있다.

 

- Notin 오퍼레이터

{key: environment, operator: NotIn, values: [dev]}

environment의 값이 dev가 아니어야 된다는거다.

만약 두개 쓴다면 둘다 아니어야 한다는 AND연산으로 작동한다.

 

- Exists 오퍼레이터

{key: environment, operator: Exists}

이건 values는 없다.

해석하자면 environment라는 라벨링키만 있기만 하면 된다는 뜻.

 

 

- DoesNotExist 오퍼레이터

{key: environment, operator: DoesNotExist}

DoesNotExist는 뭐 당연히 environment키가 없을경우 해당된다는 뜻이다.

 

 

이렇게 좀더 세분화해서 셀렉터를 조건지을 수 있다.

 

 

 

++ 얘도 cascade=orphan이랑 kubectl scale 전부 적용된다.

 

# 과제풀이

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: apache
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      app: main
    matchExpressions:
      - {key: rel, operator: In, values: [stable]}
  template:
    metadata:
      labels:
        app: main
        rel: stable
    spec:
      containers:
      - name: httpd
        image: httpd:2.2

 

 

# replicaSet의 rolling update, roll back

공식문서를 대충읽었는지 몰라도 처음에 난 replicaSet도 롤링업데이트, 롤백을 지원하는줄 알았다.

그래서 set image, kubectl edit, 등등 뭐 별지랄을 다해봤는데 안됐는데, 

공식문서에 직접적으로 지원하지않는다고 나와있다..

 

원본이 삭제되면 새 레플리카셋을 생성해서 대체할 수 있다. 기존 .spec.selector와 신규 .spec.selector가 같으면 새 레플리카셋은 기존 파드를 선택한다. 하지만 신규 레플리카셋은 기존 파드를 신규 레플리카셋의 새롭고 다른 파드 템플릿에 일치시키는 작업을 수행하지는 않는다. 컨트롤 방식으로 파드를 새로운 사양으로 업데이트 하기 위해서는 디플로이먼트를 이용하면 된다. 이는 레플리카셋이 롤링 업데이트를 직접적으로 지원하지 않기 때문이다.

 

이건 공식문서 내용 발췌한거다.

사실 이거 날아간 포스팅에서는 지원한다고 되있었다고 썻다..

매번 강의든 포스팅이든 다시 들어볼때마다 새롭구나 

 

 

 

 

 

# deployment

방금전에 말했던 rolling update, roll back에 대해서 설명했는데

이 개념은 서비스중인 pod들의 container이미지가 업데이트되거나 롤백될때 서비스 중단없이 점진적으로 컨테이터들을 하나씩 이미지를 새로 업데이트 하는것이다. 정확한 동작은 사실 pod들을 새로운 이미지로 갈아치우는 것이다.

하지만 여러 레플리카 파드들이 있는 상태에서 하나씩 이미지를 갈아치우니 서비스는 중단이 없이 보이는것이다.

이게 deployment의 주요 기능이다.

 

 

 

# deployment yaml양식

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

얘도 replicaSet과 그냥 양식자체가 똑같다.

kind만 바뀌었을 뿐이다.

 

 

 

 

# deployment와 replicaSet과 pod의 종속구조

deployment안에 repliocaSet이 있고 이 replicaSet안에 pod들이 있다.

즉 deployment를 생성하면, replicaSet이 생성되고 그 replicaSet안에있는 pod들도 생겨난다는 것이다.

 

이전에 replication controller을 포스팅하면서 이 replication controller로 생성된 pod들중 하나를 지워도 계속 replicas 갯수에 맞게 pod들이 재생성되는걸 볼 수 있었다.

그럼 deployment에 종속된 replicaSet을 삭제해버리면 어떻게될까?

kubectl create -f 디플로이yaml파일경로

아까 공식문서에있는 yaml양식으로 deployment를 생성해 보았다.

그리고 replicaSet을 삭제해보자.

kubectl delete replicasets.apps 디플로이에종속된레플리카이름

replicaSet을 지우니 자동으로 pod들도 terminating상태로 된다. 하지만 잠시후에 다시 새로운 pod들이 생성된다.

또한 replicaSet이 자동으로 새로 생성된것도 확인 가능하다.

 

난 저 replicaSet 뒤에 붙은 해시같은 문자들도 바뀔줄알았는데 그대로 다시 생성이 되었다.

 

 

 

# deployment로 만들어낸 pod의 이름 구조.

아까 위에 쓴 get pods 사진을 다시 가져왔는데, 이 pod들의 이름을 자세히보면 흥미롭다

nginx-deployment-6595874d85-66rwk

이렇게 pod이름이 지어졌는데 찢어보자

- nginx-deployment : 얘는 이 파드를 만든 deployment의 이름이다.

- 65958744d85 : 얘는 아까 위에서 테스트해봤던 replicaSet에 붙은 해시다. replicaSet의 이름이라고 볼 수 있다.

- 66rwk : 얘는 pod이름이다

 

이렇게 이름만보고 이 pod는 어떤 deployment로 생성된 pod인지 알 수 있다.

 

 

 

 

 

# rolling update

deployment의 rolling update와 roll back은 점진적으로 일어난다.

일단 가지고있는 replicaSet말고 하나 새로운 replicaSet을 만들고, 이전 replicaSet에서 pod를 하나 삭제, 새로운 replicaSet에서 pod를 하나 생성 이런 과정을 반복하면서 rolling update를 실행한다. 모든 pod들을 업데이트하면 이전 구닥다리 replicaSet은 삭제를 해버린다.

때문에 롤링업데이트를 하는 도중 생겨나는 pod들의 replicaSet이름부분이 서로 다르다.

이렇게 574~였던에 이전꺼구 새로 생긴 64d부분이 새로운 replicaSet의 이름이다.

아까 replicaSet을 직접적으로 삭제하고 deployment로 인해 생겨난 새로운 replicaSet의 이름은 동일했던것과 비교해보면 다르다는걸 알 수 있다.

574~레플리카는 모든 pod들이 업데이트 된 후, 최종적으로 삭제가 될거다.

 

 

# rolling update 커맨드

kubect set image deployment 디플로이이름 컨테이너이름=이미지:태그 --record

record는 기록을 한다는 의미이다.

이 옵션을 안달고 rolling update를 하면 rollout history 명령을 칠때 업데이트 기록으로 남지 않는다.

create할때도 옵션달 수 있고 이것도 rollout history에 기록된다.

 

또 만약 컨테이너 이름 기억안나면 아래명령어로 확인하자.

kubectl describe pod 파드이름 |grep container

조게 컨테이너 이름이다.

 

 

 

# 업데이트 기록보기

kubectl rollout history deployment 디플로이먼트이름

 

저렇게  업데이트를 한 기록을 볼 수 있다.

위에서 설명한 --record옵션을 달아줘야 한다.

저기서 revision을 지정해서 나중에 roll back도 할 수 있다.

 

 


# 아예 다른이미지로 바꾸기

rolling update는 새로운 replicaSet을 만들고 pod들을 새로 다시 생성하는 동작방식으로 동작한다.

때문에 사실 업데이트라고 하지만 이미지 태그를 기존 태그보다 이전껄로 하든, 아예 다른 이미지로 바꿔버리든 모두 동작을 한다.

예를들어 위예제의 deployment는 nginx로 만들었었는데 redis로 바꿔보면

동작하는걸 볼 수 있고

새로 생성한 pod의 이미지 정보를 describe로 보면

kubectl describe pod 파드이름 | grep image

아예 이미지가 새로운걸로 받아진것을 확인할 수 있다.

 

 

# roll back

kubectl rollout undo deploy 디플로이먼트이름

이름만 거창하게 roll back이지 동작 매커니즘은 rolling update랑 똑같다.

이전 캐싱해놓은걸 쓰는게아니라 아예 동작매커니즘 똑같이 업데이트 이전으로 돌아가는거다.

 

다만 아까 history에서 봤듯이, 

저 revision으로 특정해서 바꿀 수 있다.

kubectl rollout undo deploy 디플로이먼트이름 --to-revision=2

이런식으로 특정해서 갈 수도 있다.

2번으로 간다니까 nginx:1.10버전으로 돌아갈거다.

추가해서 2번 revision으로 undo하면 history에서 2에 해당하는건 없어지고 새로 4가 생긴다.

그러니 history에 남는걸 보면 1,3,4이렇게 될거다.

 

 

 

 

# deployment yaml형식에 추가적으로 rolling update 관련 옵션 넣는 방법

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  progressDeadlineSeconds: 100
  revisionHistoryLimit: 5
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

기존 deployment yaml양식에 추가적으로 rolling update 설정관련 내용도 넣어봤다.

주요 달라진 부분인 spec부분인데 저기만 긁어와보면,

  progressDeadlineSeconds: 100
  revisionHistoryLimit: 5
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate

요렇게 생겼다.

하나 하나 설명해보겠다.

 

1. progressDeadlineSeconds

업데이트 하는데 최대한 기다리는 시간이다.

저 부분에 해당되는 숫자는 초 단위이고 현재 값은 100이니까 100초까지 기다리고 안되면 다시 rollback시키겠다는 거다.

 

2. revisionHistoryLimit

--record로 rolling update 할경우 최대한으로 기록되는 히스토리다.

이부분인데 이게 최대 다섯개까지 저장된다는거다.

숫자를 바꾸면 그만큼 저장이 된다.

 

3. strategy 및 rollingUpdate

얘는 어떤전략으로 rolling update를 진행하냐는 건데 

일단 rolling update로 할거고 max surge와 max unavailable값이 퍼센트로 되어있다.

이건 좀 수식적인 연산이 필요한데 현재 deployment의 replicas의 수는 3이다.

3의 25%는 0.75인데 이걸 반올림하면 1이고 running중인 pod수는 3+1개인 네 개를 돌리면서 업데이트 하겠다는 뜻이다.

만약 50%로 설정한다면 1.5, 반올림하면 2니까 running중인 pod수는 3+2개인 다섯 개를 돌리면서 업데이트를 하겠다는 뜻이다.

즉 얼마나 빨리 업데이트를 하겠냐는거고 그만큼 순간 잡아먹는 리소스도 클 수 밖에 없다.

직접 테스트를 해봤는데 25%에서 이렇게 나오더라

보면 running중인 pod 세 개, creating중인 pod 한개 해서 네개가 돌아간다.

50%로 하고 rolling update를 하게되면

running중인 컨테이너 2개, creating중인 컨테이너 세 개, 해서 총 다섯개로 업데이트를 한다.

 

 

# history 메세지 커스텀 하기

이런식으로 히스토리를 커스텀 할 수 있다.

방법은 아래와 같다.

 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  annotations:
    kubernetes.io/change-cause: muzzi 1.13
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.13
        ports:
        - containerPort: 80

해당 deployment를 생성한 yaml파일을 수정을 한다

여기서 중요한 부분은

  annotations:
    kubernetes.io/change-cause: muzzi 1.13

이렇게 메타데이터에 적어주는데 cause: 부분에 적은게 history에 들어가게 된다.

또한 

      - name: nginx
        image: nginx:1.13

또한 원하는 이미지와 태그도 수정을 해준다.

 

여기서 아까 봤던 set image 커맨드가 아닌

kubectl apply -f 디플로이파일명

명령으로 yaml파일 자체로 apply를 해주게 되면

rolling update가 수행되고 history에 원하는 양식으로 남게된다.

 

참고로 apply는 deployment가 떠있는 상태에서만 적용시킬 수 있고

create는 동일명인 deployment가 없을경우에만 쓸 수 있다.

 

 

+ rolling update 상태정보 보기

kubectl rollout status deployment 디플로이먼트이름

아까 nginx:1.10으로 하는게 느려서 써봤는데 업테이트중이라고 뜨더라.

 

+ 업데이트 일시정지하기

kubectl rollout pause deployment 디플로이먼트이름

 

+ 일시정지한거 다시 시작하기

 

kubectl rollout resume deployment 디플로이먼트이름

 

+ set으로 바꿀수 있는것들.

 

 

+ 과제

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-mainui
  annotations:
    kubernetes.io/change-cause: version 2.2
spec:
  replicas: 2
  selector:
    matchLabels:
      name: apache
      app: main
      rel: stable
  template:
    metadata:
      labels:
        app: main
        name: apache
        rel: stable
    spec:
      containers:
      - name: httpd
        image: httpd:2.2
        ports:
        - containerPort: 80

 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-mainui
  annotations:
    kubernetes.io/change-cause: version 2.4
spec:
  replicas: 2
  selector:
    matchLabels:
      name: apache
      app: main
      rel: stable
  template:
    metadata:
      labels:
        app: main
        name: apache
        rel: stable
    spec:
      containers:
      - name: httpd
        image: httpd:2.4
        ports:
        - containerPort: 80