Kubernetes 환경 구축 ( Ubuntu ) - 4

2024. 1. 17. 16:25Technology[DevOps]

[ 이번 게시글에서 구현할 사항 ]

- Ingress Nginx Controller 적용해서 MSA 환경에서 적합하도록 구현합니다.

- 무중단 배포 방식의 Rolling Update를 구현합니다.

 

1. 별도로 수정하지 않는 이상 git에 배포되어있는 Ingress Nginx Controller의 default namespace가 ingress-nginx이므로 모든 pod와 service, hpa의 namespace를 ingress-nginx로 수정합니다.

...
metadata:
  name: ...
  namespace: ingress-nginx
...

 

2. Ingress-nginx-controller service를 제외한 모든 service의 type을 ClusterIP로 변경합니다.

...
spec:
  type: ClusterIP
  ...

외부에서 HTTP / HTTPS 요청은 Ingress-nginx-controller service에서 받으므로 나머지 service는 외부와 통신할 필요가 없어서 모두 ClusterIP로 변경해 내부적으로만 통신을 가능하도록 설정합니다.

 

3. BareMetal버전의 Ingress-nginx-controller 관련된 yaml 파일을 git에서 가져옵니다.

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/baremetal/deploy.yaml

해당 예제는 별도의 AWS, GCP의 로드밸런서를 사용하지 않고 BareMetal버전을 사용하였으며 External IP 설정이 불가하여 NodePort 방식으로 외부와 통신하도록 구현하였습니다.

 

4. Ingress-nginx-controller yaml 파일에서 NodePort 포트를 고정합니다. ( 선택사항 )

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.8.2
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
    nodePort: [고정할 NodePort 지정]
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
    nodePort: [고정할 NodePort 지정]
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
  type: NodePort

 

5. Ingress-nginx-controller를 kubernetes 환경에 적용합니다.

kubectl apply -f deploy.yaml

 

6. Ingress-nginx-controller의 routing rule 을 설정합니다.

vi ingress-nginx.yaml

 

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - ( host: "[Host 지정 가능]" )
    http:
      paths:
      - pathType: Prefix
        path: [Prefix 지정]
        backend:
          service:
            name: [라우팅할 서비스 이름]
            port:
              number: [라우팅할 서비스의 포트번호]
      - pathType: Prefix
        path: [Prefix 지정]
        backend:
          service:
            name: [라우팅할 서비스 이름]
            port:
              number: [라우팅할 서비스의 포트번호]

MSA 환경에서 서비스가 추가될 때 각 서비스마다 별도로 NodePort 방식으로 외부와 통신하는 게 아니라

각 서비스들은 ClusterIP로 내부적으로만 통신하고 외부와 통신하는 통로는 Ingress-nginx-controller 서비스로 단일화하여 외부와의 EndPoint를 고정합니다.

추후 서비스가 추가되면 routing rule yaml 파일에서 spec.rules.http.paths에 추가합니다.

 

7. routing rule을 kubernetes 환경에 적용합니다.

kubectl apply -f ingress-nginx.yaml

 

8. 정상적으로 Ingress-nginx-controller가 적용되었는지 확인합니다.

kubectl get all -n ingress-nginx

Ingress-nginx-controller pod의 READY 상태가 1/1 이고  Ingress-nginx-controller service가 NodePort Type으로 지정한 PORT와 라우팅되었는지 확인
30000번 포트로 외부와 통신하고 test1 URL로 요청했을 때 정상적으로 라우팅 후 해당 서비스가 호출되는지 확인

 

구조를 단순화하여 표현하면 다음과 같습니다.

9. RollingUpdate 방식 적용을 위해 Deployment.yaml 파일을 수정합니다.

...
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 50%
      maxSurge: 50%
  template:
    metadata:
      annotations:
        buildTimestamp: "NoTime"
...

- spec.strategy.rollingUpdate에 RollingUpdate 옵션을 지정할 수 있는데,

  • maxSurge : 업데이트 중에 동시에 최대로 추가할 수 있는 파드의 수입니다.
  • maxUnavailable : 업데이트 과정 중에서 동시에 사용 불가능한 상태가 될 수 있는 파드의 수 입니다.

maxSurge와 maxUnavailable 둘 다 정수나 % 단위로 지정할 수 있지만, 둘다 0이 될 수는 없습니다.

값을 정수로 지정하게 될 경우 실제로 업데이트되는 파드의 수가 지정됩니다. 그리고 백분율(%)로 지정하게 되면 원하는 개수의 파드의 비율이 반올림되어 지정됩니다.

- template.metadata.annotations.buildTimestamp는 해당 Pod가 언제 업데이트 되었는지 어노테이션으로 지정하는 동시에 image 버전업이 되지 않았더라도(혹은 version이 latest와 같이 동일하더라도) update가 가능하도록 yaml 파일이 계속 change되었음을 보장하는 영역입니다.

 

Rolling Update를 그림으로 표현하면 아래와 같습니다.

출처 :  https://ooeunz.tistory.com/124

10. yaml 파일의 template.metadata.annotations.buildTimestamp 영역에 현재 시간을 지정하기 위한 Shell Script를 작성합니다.

BUILD_TIMESTAMP_UTC=$(date -u +%Y%m%d%H%M%S)
BUILD_TIMESTAMP_KST=$(date -u -d "${BUILD_TIMESTAMP_UTC}" +"%Y-%m-%d/%H:%M:%S" --date="9 hours")

cat <<EOF > [deployment yaml파일 이름].yaml
[deployment yaml파일 내용]
...
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 50%
      maxSurge: 50%
  template:
    metadata:
      annotations:
        buildTimestamp: "${BUILD_TIMESTAMP_KST}"
...
EOF

 

11. Shell Script 실행 후 deployment yaml 파일의 내용 변경을 확인하고 현재 배포된 Pod를 업데이트 합니다.

./[Shell Script 파일이름].sh

위 명령어로 Shell Script를 실행시키고

 

vi [deployment yaml 파일이름].yaml

위 명령어로 deployment 파일의 내용을 확인해보면

 

...
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 50%
      maxSurge: 50%
  template:
    metadata:
      annotations:
        buildTimestamp: "2024-01-19/14:27:19"
 ...

기존 "NoTime" 문자열에서 현재 시간으로 변경된 내용을 확인할 수 있습니다.

 

kubectl apply -f [deployment yaml 파일이름].yaml

위 명령어로 기존 Pod를 업데이트 하고

 

위 이미지를 보시면

- 기존 Pod 2개 중 MaxUnavailable 옵션을 50%로 설정해서 1개만 Terminating되고 Available한 Pod 50%를 남겨둡니다.(무중단 배포 목적)

- 추후 업데이트가 적용된 신규 Pod가 Available(Running) 상태에 도달하면 남겨둔 기존 Pod는 그때 Terminating합니다.