ABOUT ME

-

Today
Yesterday
Total
  • [GCP] GKE 인그레스와 cloud Load balancing
    AWS & GCP 2025. 3. 31. 23:39

    GKE에서 인그레스를 세팅하고 배포하면 자동으로 cloud load balancing 서비스를 전개합니다. 여기서 ingress나 service에 어노테이션을 추가하여 cloud load balancing 서비스의 옵션을 설정하거나 정상 실행을 하려면 꼭 있어야 하는 옵션에 대해 설명합니다.

    설정

    다음과 같이 values, service, ingress가 세팅이 되어있고 배포를 했다고 가정합니다.

    # Default values for mario.
    # This is a YAML-formatted file.
    # Declare variables to be passed into your templates.
    
    replicaCount: 2
    resources:
       limits:
         cpu: 1000m
         memory: 4Gi
       requests:
         cpu: 1000m
         memory: 4Gi
    
    image:
      repository: us-docker.pkg.dev/google-samples/containers/gke/hello-app
      pullPolicy: IfNotPresent
      # Overrides the image tag whose default is the chart appVersion.
      tag: "1.0"
    
    imagePullSecrets: []
    nameOverride: ""
    fullnameOverride: ""
    
    serviceAccount:
      # Specifies whether a service account should be created
      create: true
      # Automatically mount a ServiceAccount's API credentials?
      automount: true
      # Annotations to add to the service account
      annotations: {}
      # The name of the service account to use.
      # If not set and create is true, a name is generated using the fullname template
      name: ""
    
    podAnnotations: {}
    podLabels: {}
    
    podSecurityContext: {}
      # fsGroup: 2000
    
    securityContext: {}
      # capabilities:
      #   drop:
      #   - ALL
      # readOnlyRootFilesystem: true
      # runAsNonRoot: true
      # runAsUser: 1000
    
    service:
      type: ClusterIP
      port: 80
      targetPort: 8080
    #  annotations:
    #    cloud.google.com/neg: '{"ingress": true}'
    #    cloud.google.com/backend-config: '{"default": "my-backendconfig"}'
    
    ingress:
      enabled: true
      className: "gce" # gke에서는 classname은 사용하지 않고 annotations를 사용한다. 따라서 제거해도 무방함
      annotations:
        kubernetes.io/ingress.class: gce
    #    kubernetes.io/ingress.global-static-ip-name: "test-ip" # 고정 IP 사용시 리전 귀속 IP가 아닌, 전역 IP만 연결이 가능
    #    kubernetes.io/ingress.allow-http: "true"
    #    networking.gke.io/v1beta1.FrontendConfig: "my-frontendconfig"
      tls: []
      #  - secretName: chart-example-tls
      #    hosts:
      #      - chart-example.local
    
    autoscaling:
      enabled: false
      minReplicas: 1
      maxReplicas: 2
      targetCPUUtilizationPercentage: 80
      # targetMemoryUtilizationPercentage: 80
    
    # Additional volumes on the output Deployment definition.
    volumes: []
    # - name: foo
    #   secret:
    #     secretName: mysecret
    #     optional: false
    
    # Additional volumeMounts on the output Deployment definition.
    volumeMounts: []
    # - name: foo
    #   mountPath: "/etc/foo"
    #   readOnly: true
    
    nodeSelector: {}
    
    tolerations: []
    
    affinity: {}

    values.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: {{ include "mario.fullname" . }}
      {{- with .Values.service.annotations }}
      annotations:
        {{- toYaml . | nindent 4 }}
      {{- end }}
    
      labels:
        {{- include "mario.labels" . | nindent 4 }}
    spec:
      type: {{ .Values.service.type }}
      ports:
        - port: {{ .Values.service.port }}
          targetPort: {{ .Values.service.targetPort }}
          protocol: TCP
          name: http
      selector:
        {{- include "mario.selectorLabels" . | nindent 4 }}
    

    service.yaml

    {{- if .Values.ingress.enabled -}}
    {{- $fullName := include "mario.fullname" . -}}
    {{- $svcPort := .Values.service.port -}}
    
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: {{ $fullName }}
      labels:
        {{- include "mario.labels" . | nindent 4 }}
      {{- with .Values.ingress.annotations }}
      annotations:
        {{- toYaml . | nindent 4 }}
      {{- end }}
    spec:
      ingressClassName: {{ .Values.ingress.className }}
      {{- if .Values.ingress.tls }}
      tls:
        {{- range .Values.ingress.tls }}
        - hosts:
            {{- range .hosts }}
            - {{ . | quote }}
            {{- end }}
          secretName: {{ .secretName }}
        {{- end }}
      {{- end }}
      defaultBackend:
        service:
          name: {{ $fullName }}
          port:
            number: {{ $svcPort }}
      rules:
    {{/*    - host: chart-example.local # 호스트가 현재 없음*/}}
        - http:
            paths:
              - path: /
                pathType: ImplementationSpecific
                backend:
                  service:
                    name: {{ $fullName }}
                    port:
                      number: {{ $svcPort }}
    {{- end }}

    ingress.yaml

    위 차트를 배포하면 cloud load balancing에 아래와 같이 자동으로 배포가 된 것을 볼 수 있습니다.

    gke에서 인그레스가 정상적으로 실행되려면 startup probe가 구성되어 있어야함

    만약 startup probe가 필요없다고 해서 제거하면 인그레스가 정상적으로 실행되지 않고 cloud load balancing 서비스 또한 프론트엔드(외부)에서 받은 트래픽을 백엔드(내부=인그레스)로 전달할 수 없게 됩니다.

    인그레스 클래스 네임

    GKE에서는 ingress class name을 다음과 같이 설정할 수 있습니다.

    kubernetes.io/ingress.class 값 ingressClassName 값 GKE 인그레스 컨트롤러 동작

    설정되지 않음 설정되지 않음 인그레스 매니페스트를 처리하고 외부 애플리케이션 부하 분산기를 만듭니다.
    설정되지 않음 모든 값 수행할 작업이 없습니다. 인그레스 매니페스트가 타사 인그레스 컨트롤러(배포된 경우)로 처리될 수 있습니다.
    gce 모든 값 이 필드는 무시됩니다. 인그레스 매니페스트를 처리하고 외부 애플리케이션 부하 분산기를 만듭니다.
    gce-internal 모든 값 이 필드는 무시됩니다. 인그레스 매니페스트를 처리하고 내부 애플리케이션 부하 분산기를 만듭니다.
    gce 또는 gce-internal 이외의 값으로 설정 모든 값 수행할 작업이 없습니다. 인그레스 매니페스트가 타사 인그레스 컨트롤러(배포된 경우)로 처리될 수 있습니다.

    이전 GKE 버전을 실행하는 클러스터의 경우 GKE 컨트롤러는 kubernetes.io/ingress.class 주석이 없거나 gce 또는 gce-internal 값의 주석이 있는 인그레스를 처리합니다.

    kubernetes.io/ingress.class 어노테이션은 Kubernetes에서 지원 중단되었지만 GKE는 이 어노테이션을 계속 사용합니다. 즉, ingressClassName 필드를 사용하여 GKE 인그레스를 지정할 수 없고 kubernetes.io/ingress.class 어노테이션을 사용해야 합니다.

    인그레스, 서비스의 어노테이션으로 cloud load balancing 서비스 옵션 설정

    인그레스와 서비스에 어노테이션을 추가해 cloud load balancing 서비스의 옵션을 설정할 수 있습니다.

    인그레스 어노테이션

    주석 설명
    kubernetes.io/ingress.allow-http 클라이언트와 HTTP(S) 부하 분산기 간에 HTTP 트래픽을 허용할지 여부를 지정합니다. 가능한 값은 'true', 'false'입니다. 기본값은 'true'입니다. HTTP 중지를 참조하세요.
    ingress.gcp.kubernetes.io/pre-shared-cert 이 주석을 사용하여 GKE 인그레스 리소스에 인증서 리소스를 연결합니다. 자세한 내용은 외부 애플리케이션 부하 분산기에 여러 SSL 인증서 사용을 참조하세요.
    kubernetes.io/ingress.global-static-ip-name 이 주석을 사용하여 부하 분산기에서 이전에 만든 고정 외부 IP 주소를 사용하도록 지정합니다. HTTP(S) 부하 분산기의 고정 IP 주소를 참조하세요.
    networking.gke.io/v1beta1.FrontendConfig 이 주석을 사용하여 부하 분산기의 클라이언트 연결 구성을 맞춤설정합니다. 자세한 내용은 인그레스 구성을 참조하세요.
    networking.gke.io/suppress-firewall-xpn-error 인그레스 부하 분산기의 경우, Kubernetes가 권한 부족으로 인해 방화벽 규칙을 변경할 수 없으면 몇 분 간격으로 firewallXPNError 이벤트가 발생합니다. GLBC 1.4 이상에서는 인그레스 리소스에 networking.gke.io/suppress-firewall-xpn-error: "true" 주석을 추가하여 firewallXPNError 이벤트가 발생하지 않도록 할 수 있습니다. 이 주석을 삭제하면 이벤트가 다시 발생합니다. 가능한 값은 truefalse입니다. 기본값은 false입니다.

    서비스 어노테이션

    주석 설명
    cloud.google.com/app-protocols 이 주석을 사용하여 부하 분산기와 애플리케이션 간의 통신 프로토콜을 설정합니다. 가능한 프로토콜은 HTTP, HTTPS, HTTP2입니다. 부하 분산기와 애플리케이션 간 HTTPS인그레스를 사용한 부하 분산용 HTTP/2를 참조하세요.
    cloud.google.com/backend-config 이 주석을 사용하여 서비스와 연결된 백엔드 서비스를 구성합니다. 자세한 내용은 인그레스 구성을 참조하세요.
    cloud.google.com/neg 이 주석을 사용하여 부하 분산기에서 네트워크 엔드포인트 그룹을 사용하도록 지정합니다. 컨테이너 기반 부하 분산 사용을 참조하세요.

    예시

    고정 IP 설정하기

    위 인그레스의 어노테이션을 통해 고정 IP를 명시해 사용할 수 있습니다. 만약 해당 어노테이션이 없다면 cloud load balancing 서비스는 자동으로 임시 외부 IP를 발급받아 사용하며 해당 서비스가 제거되면 IP 또한 삭제됩니다.

    해당 어노테이션을 사용하기 전, 한 가지 제약사항으로는 해당 어노테이션을 사용하기 전 미리 고정 IP를 등록해줘야 합니다. 또한 고정 IP를 사용하려면 리전 귀속 IP가 아닌 전역 IP만 가능합니다. 25-03-31 기준으로 표준 계층의 IP는 리전 귀속 IP만 사용 가능하므로 프리미엄 계층의 전역 IP로 생성해야 합니다. 이러한 이유는 cloud load balancing 서비스는 글로벌 리소스이므로 리전 귀속 IP는 허용하지 않는 것으로 보입니다.

    사용 방법은 다음과 같습니다.

    1. 프리미엄 계층의 전역 고정 IP 생성
    2. 인그레스의 어노테이션에 kubernetes.io/ingress.global-static-ip-name: "static-ip" 와 같이 1번에서 생성한 고정 IP 이름 명시
    3. 배포

    cloud load balancing 서비스의 프론트엔드 설정을 어노테이션으로 설정하기

    cloud load balancing 서비스의 콘솔의 수정 화면에서 프론트엔드 설정을 확인해보면 다음과 같이 여러 설정을 할 수 있는 것으로 확인할 수 있습니다.

    프론트엔드 추가/수정 화면

    프론트엔드 구성은 SSL 정책, HTTP-HTTPS 리디렉션을 추가할 수 있습니다.

    다음과 같은 yaml 파일을 추가하고 인그레스의 어노테이션에 추가합니다.

    # frontconfig.yaml
    apiVersion: networking.gke.io/v1beta1
    kind: FrontendConfig
    metadata:
      name: my-frontendconfig # 어노테이션에서 사용할 이름
    spec:
      sslPolicy: test-mario-domain # SSL 정책 설정
      redirectToHttps: # http 요청을 https로 리다이렉트
        enabled: true
        responseCodeName: MOVED_PERMANENTLY_DEFAULT

    frontconfig.yaml

    ingress:
      enabled: true
      className: "gce" # gke에서는 classname은 사용하지 않고 annotations를 사용한다. 따라서 제거해도 무방함
      annotations:
        networking.gke.io/v1beta1.FrontendConfig: "my-frontendconfig"

    values.yaml

    responseCodeName은 다음 중 하나를 입력하면 되고 입력을 하지 않으면 MOVED_PERMANENTLY_DEFAULT 이 기본값으로 설정됩니다.

    • MOVED_PERMANENTLY_DEFAULT 301 리디렉션 응답 코드를 반환합니다(responseCodeName가 지정되지 않은 경우 기본값).
    • FOUND 302 리디렉션 응답 코드를 반환합니다.
    • SEE_OTHER 303 리디렉션 응답 코드를 반환합니다.
    • TEMPORARY_REDIRECT 307 리디렉션 응답 코드를 반환합니다.
    • PERMANENT_REDIRECT 308 리디렉션 응답 코드를 반환합니다.

    cloud load balancing 서비스의 백엔드 설정을 어노테이션으로 설정하기

    cloud load balancing 서비스의 콘솔에서 수정 화면에서 백엔드 설정을 확인해보면 다음과 같이 여러 설정을 할 수 있는 것으로 확인할 수 있습니다.

    백엔드 추가/수정 화면

    백엔드 구성은 타임아웃, ingress에서 사용할 health probe, 로깅 설정, iap 등 다양한 설정을 할 수 있습니다.

    # my-backendconfig.yaml
    apiVersion: cloud.google.com/v1
    kind: BackendConfig
    metadata:
      name: my-backendconfig
    spec:
      timeoutSec: 40 # 타임아웃 시간: 기본값 30초
      connectionDraining:
        drainingTimeoutSec: 60 # 백엔드 인스턴스가 종료되거나 비활성화될 때, 기존 요청을 일정 시간 동안 계속 처리한 후 종료하여 트래픽 손실을 방지 (기본값 0초)
      healthCheck: # Ingress에서 사용할 hearth probe 설정
        checkIntervalSec: 5 # probe의 interval
        timeoutSec: 5 # probe의 timeout
        healthyThreshold: 2 # 상태확인시 몇회 이상 정상이 되어야 하는지
        unhealthyThreshold: 5 # 상태확인시 몇회 이상 비정상이 되어야 하는지
        type: HTTP # HTTP, HTTPS, HTTP/2 중에서 선택
        requestPath: /status # url path 미설정시 / 이 기본값
        port: 8080 # 커스텀 포트일경우 설정, 미설정시 80 포트가 기본값 (파드의 container port와 일치해야 함)
      securityPolicy:
        name: "example-security-policy" # cloud armor 보안 정책 이름
      logging:
        enable: true # 로그 사용 여부
        sampleRate: 0.01 # 로그 샘플링 비율
    

    my-backendconfig.yaml

    service:
      type: ClusterIP
      port: 80
      targetPort: 8080
      annotations:
        cloud.google.com/backend-config: '{"default": "my-backendconfig"}'

    values.yaml

    위 설정 이외에도 cdn 설정, iap, 사용자 정의 커스텀 헤더 등 다양한 기능이 있으니 https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-configuration?hl=ko#configuring_ingress_features_through_backendconfig_parameters 에서 확인하고 필요한 옵션을 사용하면 됩니다.

    Google ACME 구성

    사용하는 도메인에 대해 Google 관리 SSL 인증서를 구성할 수 있습니다. 기능을 사용하려면 ingressClassName"gce"여야 하며 같은 프로젝트와 네임스페이스에 IngressManagedCertificate 리소스를 적용해야 합니다.

    # managed-cert.yaml
    apiVersion: networking.gke.io/v1
    kind: ManagedCertificate
    metadata:
      name: managed-cert
    spec:
      domains:
        - brownbear.io # 구글에 등록된 SSL 인증서 이름

    managed-cert.yaml

    ingress:
      enabled: true
      className: "gce" # gke에서는 classname은 사용하지 않고 annotations를 사용한다. 따라서 제거해도 무방함
      annotations:
        kubernetes.io/ingress.class: gce
        networking.gke.io/managed-certificates: managed-cert

    values.yaml

    궁금

    cloud load balancing 서비스에 특정 IP만 접근하도록 설정을 어떻게 할 수 있을까

    GKE에 설정된 VPC의 방화벽에 특정 IP만 접근하도록 추가해도 먹히지 않습니다. 그 이유는 cloud load balancing 서비스가 자동으로 방화벽 역할을 하기 때문입니다. 즉, 로드 밸런서가 자동으로 방화벽 역할을 하므로 방화벽 규칙을 따로 추가하지 않아도 동작 가능합니다. 방화벽 규칙을 보지 않고도 Ingress + GCP 로드 밸런서 조합으로 외부에서 접근 가능하도록 GCP가 관리하는 것이 정상적인 동작입니다. cloud load balancing 서비스에 특정 IP만 접근하도록 하려면 cloud armor 라는 WAF 역할을 하는 서비스를 추가하여 제어해야 합니다.

    트래픽의 흐름은 다음과 같습니다.

    1. 클라이언트가 외부 IP (예: 34.10.7.3) 로 요청을 보냄.
    2. GCP의 글로벌 프록시가 요청을 수신 (이 프록시는 GCP가 관리).
    3. GCP 프록시가 트래픽을 백엔드 서비스(GKE)로 전달.
    4. VPC 방화벽은 백엔드 VM(Pod)으로 가는 트래픽만 필터링 가능하지만 로드 밸런서의 프록시(VIP)에는 영향을 주지 않음.
    • 방화벽 규칙은 GKE 내부 Pod 또는 VM을 보호하는 용도로만 사용 가능

    레퍼런스

    https://cloud.google.com/kubernetes-engine/docs/how-to/load-balance-ingress?hl=ko#summary_of_external_ingress_annotations

    https://ysyu.kr/2022/02/how-to-implement-ingress-configuration-using-http-or-https-lb-in-gke/

    https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-configuration?hl=ko

    https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs?hl=ko

    댓글