티스토리 뷰

쿠버네티스 롤링 업데이트를 하다보면 업데이트 간 간헐적으로 50x 코드가 발생하는데 이러한일이 왜 발생되며 예방하려면 어떠한 기술을 사용해야 하는지에 대한 정리

 

구조상 무조건 발생되는 문제이며 대부분 모르거나 새로고침하면 해결되므로 그냥 두는경우가 있는데 충분히 예방하고 방지할수 있는 부분임

 

쿠버네티스만의 문제는 아니고 복합적인 원인과 문제로 인해 발생되는 일

 

먼저 에러가 왜 발생되는지에 대한 부분

 

테스트를 위해 구동하는데 10초가 걸리는 웹서버를 생성

 
 

 

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	// 루트 경로 핸들러 등록
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Hello, World!")
	})

	// 시작전 딜레이 10초
	time.Sleep(10 * time.Second)
	// 서버 시작
	fmt.Println("서버 시작...")
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Println(err)
	}
}

 

이를 쿠버네티스에 배포함(프로브 설정없음)

 

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: websvr
  name: websvr
spec:
  replicas: 3
  selector:
    matchLabels:
      app: websvr
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: websvr
    spec:
      containers:
      - image: devops-demo:10001
        name: websvr
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: websvr-service
spec:
  selector:
    app: websvr-server
  ports:
    - protocol: TCP
      port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/group.name: private-group
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/manage-backend-security-group-rules: "true"
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/security-groups: sg-05ac5cd61c45454
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/target-type: ip
  name: websvr-ing
spec:
  ingressClassName: alb
  rules:
  - host: testapp.devops.test.gg
    http:
      paths:
      - backend:
          service:
            name: websvr-service
            port:
              number: 8080
 

 

도메인을 연결하고 파드는 3개로 구성

 

배포후 curl을 통해 확인

 
 
 
% curl https://testapp.devops.test.gg
Hello, World!%

 

장애 상황을 감지할수 있도록 간단한 쉘작성

 

 
while true; do
  STATUS_CODE=$(curl --output /dev/null --silent --max-time 5 --write-out "%{http_code}" "https://testapp.devops.test.gg")
  sleep 0.5
  if [ $STATUS_CODE -ne 200 ] ;
  then
   echo $STATUS_CODE "$(date +"%T")"
  fi
done
 
 

 

0.5초마다 curl을 하고 200외에는 시간과 코드를 출력함, timeout은 5초 설정

 

 

rollout을 해도 되겠으나 실제 롤링 업데이트를 가정하기 위해 10001 10002 이미지 2개를 번갈아가며 교체하고 테스트

 

위 상태에서 롤링 업데이트 결과

% bash curl.sh
000 13:57:05
000 13:57:11
000 13:57:16
502 13:57:17
502 13:57:17
502 13:57:18
502 13:57:18
....
502 13:57:31
 
 

처음 000 코드는 설정된 5초의 타임아웃때문에 발생

 

그 후 502는 웹서버가 올라오는 약 10초동안 502가 발생함

 

실제 컨테이너내 앱은 10초후에 준비가 되지만 쿠버네티스 기본값으로는 PID 1만 구동이면 성공으로 간주하므로 트래픽을 보내기 시작하며 위와같은 일이 벌어짐

 

이러한 근본적인 원인을 해결하려면 프로브를 설정해야함

 

현재는 파드구동 → 컨테이너구동(PID) ->성공(트래픽보내기시작) → 다음파드 롤링

이라면 파드구동 → 컨테이너구동(PID) → 프로브작동(httpget) → 프로브성공(트래픽 보내기 시작) → 다음파드 롤링

 

형태로 바꾸어야 한다

 

 

 

 

위 앱의 경우 웹서버이므로 간단하게 / 를 체크하는 프로브를 설정한다

 

        livenessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5

 

적용

 

지금까지는 프로브가 없어서 시작하자마자 모든 파드가 내려가고 올라감

 

지금부터는 이과정에 프로브가 추가된다

 

websvr-66cfd8bf4d-44x5c   1/1     Running   0          7m
websvr-66cfd8bf4d-6p95c   1/1     Running   0          6m59s
websvr-66cfd8bf4d-7bmhr   1/1     Running   0          7m1s
websvr-6df6fd758d-d6kp6   0/1     Running   0          12s
 

 

위와같이 파드를 생성하고 준비가 될때까지(프로브성공) 성공으로 간주하지 않고 기다림

프로브 설정 이후에는 위와같은 이유로 전체 롤링 업데이트 시간이 크게 증가됨

프로브 설정 이후 curl 체크

 

 
000 14:11:17
000 14:11:23
000 14:11:30
000 14:13:13
000 14:13:20
000 14:13:47
502 14:13:50
000 14:13:55
000 14:14:23
000 14:14:31

보면 502는 거의 사라지고 타임아웃이 많이 나는걸 확인 할수 있다

 

502가 나는 원인중 한개는 준비되기전 파드에 트래픽이 흐르기 시작해서이고 해결이 되었다

 

타임아웃이 나는 원인은 인입된 트래픽의 처리 이전 파드가 종료되서 이다

 

쿠버네티스가 파드를 종료할때는 sigterm을 내리고 컨테이너는 신호를 받아 pid1번을 종료한다

 

잘 개발된 앱이라면 sigterm을 받고 현재 연결된 세션이나 이미 받은 요청에 대한 처리를 하도록 구성이 되어있겠지만 그렇지 못한 경우가 많고 웹서버의 경우에는 거의 이렇게 구성하지 않는다

 

이를 대비하기 위한 기능이 쿠버네티스에도 있는데 Lifecyclehook 이다

 

파드가 종료될때(롤링으로인해)는 아래의 순서에 따른다

종료 성공이 기본값으로 30초간(기본값 늘릴수 있음) 되지 않으면 sigkill을 보내 강제 종료함

 

종료신호(Sigterm) → 종료 확인 → 종료 성공

종료시작-(EP에서 해당 파드제거)-이미종료

 

파드가 종료되기전에 EP에서 해당 파드가 제거되어 트래픽이 가지 않아야 하는데 sigterm(종료시작)을 받자마자 파드가 죽어버려서 EP에서 빠지기전 트래픽이 도달하고 해당 트래픽처리를 하지 못하고 파드가 죽어버린다 그래서 에러 발생

 

이와같은 앱에 라이프사이클 훅(sleep 10) 을 삽입하면 아래와 같이 종료된다

 

종료신호(Sigterm)-> 훅 작동(sleep ………………..10)→ 종료 확인 → 종료 성공

종료시작(EP에서 해당 파드제거)----여기충분한트래픽처리시간이존재------종료

 

라이프사이클은 아래와 같이 설정한다

 

 

    lifecycle:
      preStop:
        exec:
          command: ["sleep", "10"]

 

설정후 롤링 업데이트 수행

 

라이프사이클 훅이 삽입됬으므로 각 파드 종료간 10초의 딜레이가 생겨 전체적인 롤링 업데이트시간은 더욱 늘어남

 

여기부터는 상황 설명을 위해 curl을 내부망에서도 수행

 

외부망 curl

 
 
pv -N 000 14:46:38
pv -N 000 14:47:47

 

전체 과정에서 타임아웃 2번 발생

 

내부망 curl

아무런 에러없음
 

 

위 2가지 설정만으로 쿠버네티스에서는 파드 롤링 업데이트간 트래픽 제어와 처리를 효과적으로 할수 있다

 

그러나 아직도 외부망에서는 에러가 발생되고 있음

 

이러한 이유는 ALB때문이다

 

쿠버네티스 서비스의 ep와는 다르게 ALB가 바라보고 있는 타겟그룹의 추가시간이나 제거시간에 딜레이가 있기때문임

 

쿠버네티스는 체크(성공시) → ep에 넣기 → 트래픽가기 가 순식간에 일어남

ALB의 경우 체크(성공시) → 타겟그룹에 넣기(한참걸림) → 실제로 트래픽 감

 

타겟그룹의 타겟 add,delete타임이 쿠버네티스 엔드포인트 제어보다 느리므로 전체 과정에서 타겟그룹에 정상적인 타겟이 없는 경우가 간혹 생기는데 그에 따른 에러다

이를 해소하기위해서는 readinessgate를 이용하면되는데

 

readinessgate를 설정하면 아래의 단계로 업데이트가 수행된다

 

파드구동 → 컨테이너구동(PID) → 프로브작동(httpget) → 프로브성공(트래픽 보내기 시작) → ALB 타겟그룹에 대상추가가 끝나길기다림 → 다음파드 롤링

 

readinessgate는 네임스페이스에 레이블링 하여 설정한다

 

 
kubectl label namespace default elbv2.k8s.aws/pod-readiness-gate-inject=enabled
 
 

 

설정이후부터는 pod -o wide옵션으로 보면 게이트가 보이며

 

 

gate까지 성공해야 파드 구동 성공으로 간주함

 

위 설명한 모든 기술들을 적용하고 롤링 업데이트 결과

 

내부/외부 모두 단 한번의 에러없이 수행됨

최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함