Search

[스터디] Docker & Kubernetes Study #9

Chapter 9. 디플로이먼트: 선언적 애플리케이션 업데이트

쿠버네티스에서 실행되는 어플리케이션을 무중단으로 업데이트할 수 있는 방법
새로운 애플리케이션 업데이트를 하려고 함 → 파드를 새 버전으로 변경해야 함
방법1. 기존 파드를 모두 삭제한 다음 새 파드를 시작한다
방법2. 새로운 파드를 시작하고 기동하면 기존 파드를 삭제한다.
모든 파드가 실행되면 기존 파드를 한번에 삭제하는 방법이 있고
점진적으로 기존 파드를 삭제하는 방법이 있다
BLUE-GREEN deployment (한번에 이전 버전에서 새 버전으로 전환)
파드의 앞쪽에 서비스를 배치한다
새버전을 실행하는 파드를 불러오는 동안 서비스는 이전 버전의 파드에 연결된다
새 버전의 파드가 모두 실행되면 서비스의 레이블 셀렉터를 새 버전의 파드로 변경한다
롤링 업데이트
한번에 이전 버전에서 새 버전으로 전환하는 것이 아니라 점진적으로 새로운 팟으로 업데이트
kubectl rolling-update {이전버전} {새버전} --image={변경할 이미지 이름}
replication controller 가 복사되면서 label 정보도 같이 복사 된다 → 그러면 새로운 replication controller 도 이전 pod 들을 참조하는 것이 아닌가? → rolling update 에서 그러지 않도록 random 한 label select 를 생성해 새로운 replication controller 가 이전 pod 들을 참조하지 못하도록 함
롤링 업데이트는 이전 controller 에서 POD을 하나씩 지우면서 동시에 새로운 controller 의 pod 을 하나씩 늘림
이전 컨트롤러의 POD이 0으로 scale down 되면서 이전 버전의 모든 팟이 삭제되는 것
하지만 위에서도 언급했듯이 롤링 업데이트는 기존의 k8s object 를 수정(레이블)한다. 이는 좋지 않다
???: 누가 내 컨트롤러를 엉망으로 망쳤지?
롤링 업데이트를 수행하는 것이 kubectl 클라이언트라는 것이 더 나쁘다!
왜?: kubectl 이 네트워크 중에 연결이 끊어지면 어캐 되는 것이지? 업데이트 중간에 죽어버림
→ 그래서 디플로이먼트라는 새로운 리소르를 도입하게 되었다...!
애플리케이션을 선언적으로 업데이트하기 위한 디플로이먼트 사용하기
디플로이먼트는 low-level 의 개념이으로 간주되는 레플리카셋을 통해 수행하는 대신, 애플리케이션을 배포하고 선언적으로 업데이트하기 위한 high-level 리소스이다.
디플로이먼트를 생성하면 레플리카셋 리소스가 그 아래에 생성됨
실제 파드는 디플로이먼트의 레플리카셋에 의해 생성되고 관리됨
왜 high-level 리소스를 만들었을까? (굳이 오버헤드까지 발생시키면서)
업데이트에 사용하는 두 컨트롤러가 잘 조화되도록 조정해야 함 (컨트롤타워 필요)
쿠버네티스가 나머지를 처리할 수 있음으로 업데이트 편해짐
디플로이먼트를 POD을 직접 관리하지 않음. 대신 레플리카셋을 생성, 이들이 파드를 관리하도록 함
디플로이먼트 업데이트
디플로이먼트 리소스 에 정의된 파드 템플릿을 수정하면, k8s 가 실제 시스템 상태를 리소스에 정의된 상태로 만드는데 필요한 모든 단계를 수행한다
Recreate 전략: 한번에 이전 파드를 모두 삭제하고 새로운 파드를 띄움 → 다운 타임 발생
RollingUpdate 전략: 이전 파드를 하나씩 제거하고 동시에 새 파드를 추가 → 다운 타임 없음 (default)
모든 파드가 새 버전으로 업데이트 되어도 기존 버전의 replicaset 은 삭제되지 않음
문제가 생겼을 때 deployment rollback 을 하기 위함
디플로이먼트 롤백
새로 update 한 deployment 에 문제가 생겼을 때 이전 버전으로 변경하는 방법을 다룬다
kubectl rollout undo deployment {디플로이먼트 이름}

요약

쿠버네티스에서 애플리케이션을 배포하고 업데이하는 선언적 접근 방식을 사용해 좀 더 쉽게 구현하는 방법을 살펴봤다.
레플리케이션 컨트롤러에서 관리하는 파드의 롤링 업데이트 수행하기
low-level 레플리케이션 컨트롤러 대신 레플리카셋과 디플로이먼트 생성하기
디플로이먼트 스펙에서 파드 템플릿을 편집해 파드 업데이트 하기
디플로이먼트를 이전 개정이나 개정 이력에 조회된 특정 개정으로 롤백하기
중간에 디플로이먼트 중단하기
추가 파드 인스턴스가 이전 인스턴스를 교체하기 전에 디플로이먼트를 일시 중지해 새 버전의 인스턴스가 프러덕션에서 어떻게 작동하는지 검사하기
maxSurge 와 maxUnavailable 속성으로 롤링 업데이트 속도 제어하기
결함이 있는 버전의 롤아웃이 자동으로 차단되도록 minReadySeconds 와 레디니스 프로브 사용하기

질문

rolling update 시에 사용자의 request 를 처리하던 이전 버전의 POD 이 죽으면 클라이언트 입장에서는 time-out 이 나는지? 아니면 k8s 가 이를 자동으로 캐치해서 request 를 다른 팟에서 재처리하는지?
(답변) timeout이 납니다. 정석적으로 처리하려면 앱에서 SIGTERM 신호를 따로 핸들해주는 방식으로 구현해야 합니다.
rolling update 시에 POD 이 삭제될 수 있는 condition 을 설정해 줄 수 있는지 (File write을 하고 있지 않은 상황 등)
(답변) 그건 사실 Graceful Shutdown Period 안에서 앱이 알아서 해줘야 하는 것 같아요. 말씀하신 것처럼 SIGTERM 신호가 발생하고 30초 가량 뒤에 SIGKILL 이 들어오기 때문에, 그 안에 앱이 알아서 다 정리하고 종료해야죠.
이건 업데이트랑 상관 없을 수도 있는데.. A/B 테스트 할 때 두개의 버전의 서비스가 동시에 구성되어야 할 텐데 그때도 replica set 이나 deployment 를 사용할 수 있는지
(답변) 보통 A/B 테스트는 user level로 진행하기 때문에 두 버전의 서비스를 분리해서 운영하게 되면 load balancing할 때 사용자 단위로 서버를 매칭해주는 로직이 필요해요 (sessionAffinity처럼..). 저라면 그렇게 구현하기보다는 그냥 하나의 서버에서 User 정보를 토대로 A/B를 분기해서 적용하는 로직을 개발할 것 같아요.
클라이언트 단에서도 서버의 API version 을 check 하는지 (API 버전이 안맞으면 400을 띄운다던지..)
(답변) 넵 보통 그렇습니다. 클라이언트가 빌드될 당시에 설정했던 서버 버전 스펙과 안 맞는 서버가 매칭되면 동작을 하지 않도록 합니다. (e.g. 게임 서버와 클라이언트)