서비스 및 인그레스 만들기

이 문서에서는 베어메탈용 GKE에 대한 사용자, 하이브리드 또는 독립형 클러스터에서 Kubernetes 인그레스 객체를 만드는 방법을 보여줍니다. 인그레스는 각각 포드 집합과 연결된 서비스 하나 이상과 연결됩니다.

시작하기 전에

클러스터에서 관리자 워크스테이션으로 SSH 연결을 가져옵니다.

배포 만들기

배포의 매니페스트는 다음과 같습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deployment
spec:
  selector:
    matchLabels:
      greeting: hello
  replicas: 3
  template:
    metadata:
      labels:
        greeting: hello
    spec:
      containers:
      - name: hello-world
        image: "gcr.io/google-samples/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50000"
      - name: hello-kubernetes
        image: "gcr.io/google-samples/node-hello:1.0"
        env:
        - name: "PORT"
          value: "8080"

이 실습을 진행하면서 배포 매니페스트에 대해 알아야 할 중요한 사항은 다음과 같습니다.

  • 배포에 속한 각 pod에는 greeting: hello 라벨이 있습니다.

  • 각 pod에는 2개의 컨테이너가 있습니다.

  • env 필드는 hello-app 컨테이너가 TCP 포트 50000에서 리슨하고 node-hello 컨테이너가 TCP 포트 8080에서 리슨하는 것을 지정합니다. hello-app의 경우 소스 코드를 보면 PORT 환경 변수의 효과를 확인할 수 있습니다.

hello-deployment.yaml 파일에 매니페스트를 복사하고 배포를 만듭니다.

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f hello-deployment.yaml

CLUSTER_KUBECONFIG를 클러스터의 kubeconfig 파일 이름으로 바꿉니다.

서비스에 배포 노출

클라이언트가 배포 pod로 요청을 보내는 안정적인 방법을 제공하려면 서비스를 만듭니다.

다음은 클러스터 내부 클라이언트에 배포를 노출하는 서비스의 매니페스트입니다.

apiVersion: v1
kind: Service
metadata:
  name: hello-service
spec:
  type: ClusterIP
  selector:
    greeting: hello
  ports:
  - name: world-port
    protocol: TCP
    port: 60000
    targetPort: 50000
  - name: kubernetes-port
    protocol: TCP
    port: 60001
    targetPort: 8080

매니페스트를 hello-service.yaml 파일에 복사하고 서비스를 만듭니다.

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f hello-service.yaml

CLUSTER_KUBECONFIG를 클러스터의 kubeconfig 파일 이름으로 바꿉니다.

서비스를 확인합니다.

kubectl --kubeconfig CLUSTER_KUBECONFIG get service hello-service --output yaml

서비스에 제공된 clusterIP의 값이 출력에 표시됩니다. 예를 들면 다음과 같습니다.

apiVersion: v1
kind: Service
metadata:
  annotations:
    ...
spec:
  clusterIP: 10.96.14.249
  clusterIPs:
  - 10.96.14.249
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: world-port
    port: 60000
    protocol: TCP
    targetPort: 50000
  - name: kubernetes-port
    port: 60001
    protocol: TCP
    targetPort: 8080
  selector:
    greeting: hello
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

위 출력에서 ports 필드는 이름이 각각 world-portkubernetes-portServicePort 객체의 배열입니다. 서비스 필드에 대한 자세한 내용은 Kubernetes 문서의 ServiceSpec을 참조하세요.

클라이언트에서 서비스를 호출할 수 있는 방법은 다음과 같습니다.

  • world-port 사용: 클러스터 노드 중 하나에서 실행되는 클라이언트가 portclusterIP에 요청을 전송합니다. 이 예시에서는 10.96.14.249:60000입니다. 요청은 targetPort의 구성원 포드로 전달됩니다. 이 예시에서는 POD_IP_ADDRESS:50000입니다.

  • kubernetes-port 사용: 클러스터 노드 중 하나에서 실행되는 클라이언트가 portclusterIP에 요청을 전송합니다. 이 예시에서는 10.96.14.249:60001입니다. 요청은 targetPort의 구성원 포드로 전달됩니다. 이 예시에서는 POD_IP_ADDRESS:8080입니다.

인그레스 구성요소

인그레스와 관련된 몇 가지 클러스터 구성요소는 다음과 같습니다.

  • istio-ingress 배포. 인그레스 프록시입니다. 인그레스 프록시는 인그레스 객체에 지정된 규칙에 따라 트래픽을 내부 서비스로 전달합니다.

  • istio-ingress 서비스. 이 서비스는 istio-ingress 배포를 노출합니다.

  • istiod 배포. 인그레스 컨트롤러입니다. 인그레스 컨트롤러는 인그레스 객체 생성을 감시하고 그에 따라 인그레스 프록시를 구성합니다.

이러한 모든 Istio 클러스터 내 구성요소는 gke-system 네임스페이스에 설치됩니다. 이 네임스페이스는 전체 Istio/Anthos Service Mesh 설치와 충돌하지 않습니다.

인그레스 만들기

인그레스의 매니페스트는 다음과 같습니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
      paths:
      - path: /greet-the-world
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60000
      - path: /greet-kubernetes
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60001

my-ingress.yaml이라는 파일에 매니페스트를 복사하고 인그레스를 만듭니다.

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f my-ingress.yaml

사용자 클러스터를 만들 때 클러스터 구성 파일에서 loadbalancer.ingressVIP 값을 지정합니다. 이 IP 주소는 클러스터 부하 분산기에서 구성됩니다. 인그레스를 만들면 외부 IP 주소와 동일한 이 VIP가 인그레스에 지정됩니다.

클라이언트가 사용자 클러스터 인그레스 VIP로 요청을 전송하면 요청이 부하 분산기로 라우팅됩니다. 부하 분산기는 istio-ingress 서비스를 사용하여 사용자 클러스터에서 실행되는 인그레스 프록시로 요청을 전달합니다. 인그레스 프록시는 요청 URL의 경로에 따라 요청을 다른 백엔드로 전달하도록 구성됩니다.

/greet-the-world 경로

인그레스 매니페스트에서 /greet-the-world 경로가 serviceName: hello-serviceservicePort: 60000과 연결되어 있음을 나타내는 규칙을 확인할 수 있습니다. 60000은 hello-service 서비스의 world-port 섹션에 있는 port 값입니다.

- name: world-port
    port: 60000
    protocol: TCP
    targetPort: 50000

인그레스 서비스는 요청을 clusterIP:50000으로 전달합니다. 그런 다음 요청은 hello-service 서비스의 구성원 포드 중 하나로 이동합니다. 해당 포드의 포트 50000에서 리슨하는 컨테이너에 Hello World! 메시지가 표시됩니다.

/greet-kubernetes 경로

인그레스 매니페스트에서 /greet-kubernetes 경로가 serviceName: hello-serviceservicePort: 60001과 연결되어 있음을 나타내는 규칙을 확인할 수 있습니다. 60001은 hello-service 서비스의 kubernetes-port 섹션에 있는 port 값입니다

- name: kubernetes-port
    port: 60001
    protocol: TCP
    targetPort: 8080

인그레스 서비스는 요청을 clusterIP: 8080으로 전달합니다. 그런 다음 요청은 hello-service 서비스의 구성원 포드 중 하나로 이동합니다. 해당 포드의 포트 8080에서 리슨하는 컨테이너에 Hello Kubernetes! 메시지가 표시됩니다.

인그레스 테스트

/greet-the-world 경로를 사용하여 인그레스를 테스트합니다.

curl CLUSTER_INGRESS_VIP/greet-the-world

CLUSTER_INGRESS_VIP를 인그레스의 외부 IP 주소로 바꿉니다.

출력에 Hello, world! 메시지가 표시됩니다.

Hello, world!
Version: 2.0.0
Hostname: ...

/greet-kubernetes 경로를 사용하여 인그레스를 테스트합니다.

curl CLUSTER_INGRESS_VIP/greet-kubernetes

출력에 Hello, Kubernetes! 메시지가 표시됩니다.

Hello Kubernetes!

번들 인그레스 사용 중지

베어메탈용 GKE와 함께 번들로 제공되는 인그레스는 인그레스 기능만 지원합니다. Istio 또는 Anthos Service Mesh와 통합할 수 있습니다. 이러한 제품은 상호 TLS(mTLS), 서비스 간 인증 관리 기능, 워크로드 관측 가능성과 같이 모든 기능을 갖춘 서비스 메시의 추가 이점을 제공합니다. Istio 또는 Anthos Service Mesh와 통합하는 경우 번들 인그레스 기능을 사용 중지하는 것이 좋습니다.

클러스터 구성 파일의 spec.clusterNetwork.bundledIngress 필드를 사용하여 번들 인그레스를 사용 설정하거나 중지할 수 있습니다. 이 필드는 버전 1.13.0 이상 클러스터에서만 사용할 수 있습니다. bundledIngress 필드의 기본값은 true이며 생성된 클러스터 구성 파일에 없습니다. 이 필드는 변경 가능하며 버전 1.13.0 이상의 클러스터를 만들거나 업데이트할 때 변경할 수 있습니다. 클러스터를 버전 1.13.0 이상으로 업그레이드하는 경우 이 필드를 지정할 수도 있습니다.

다음 샘플 클러스터 구성 파일은 번들 인그레스 기능을 사용 중지하도록 클러스터를 구성하는 방법을 보여줍니다.

apiVersion: v1
kind: Namespace
metadata:
  name: cluster-hybrid-basic
---
apiVersion: baremetal.cluster.gke.io/v1
kind: Cluster
metadata:
  name: hybrid-basic
  namespace: cluster-hybrid-basic
spec:
  type: hybrid
  profile: default
  anthosBareMetalVersion: 1.13.0
  gkeConnect:
    projectID: project-fleet
  controlPlane:
    nodePoolSpec:
      nodes:
      - address: 10.200.0.2
  clusterNetwork:
    bundledIngress: false
    pods:
      cidrBlocks:
      - 192.168.0.0/16
    services:
      cidrBlocks:
      - 10.96.0.0/20
...

인그레스에 HTTPS 설정

클라이언트의 HTTPS 요청을 수락하려면 인그레스 프록시에 인증서가 있어야 인그레스 프록시가 클라이언트에게 ID를 증명할 수 있습니다. 또한 이 프록시에 비공개 키가 있어야 HTTPS 핸드셰이크를 완료할 수 있습니다.

다음 예시에서 이러한 항목이 사용됩니다.

  • 인그레스 프록시: HTTPS 핸드셰이크에 참여한 후 hello-service 서비스의 구성원 포드에 패킷을 전달합니다.

  • hello-service 서비스의 도메인: 예시 조직의 altostrat.com

다음 단계를 따르세요.

  1. 루트 인증서와 비공개 키를 만듭니다. 이 예시에서는 루트 CA 예시 조직에서 root.ca.example.com의 루트 인증 기관을 사용합니다.

    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj \
        '/O=Root CA Example Inc./CN=root.ca.example.com' -keyout root-ca.key \
        -out root-ca.crt
  2. 인증서 서명 요청을 만듭니다.

     openssl req -out server.csr -newkey rsa:2048 -nodes -keyout server.key -subj \
         "/CN=altostrat.com/O=Example Org"
  3. 인그레스 프록시에 대해 제공 인증서를 만듭니다.

    openssl x509 -req -days 365 -CA root-ca.crt -CAkey root-ca.key -set_serial 0 \
        -in server.csr -out server.crt

    이제 다음 인증서 및 키가 생성되었습니다.

    • root-ca.crt: 루트 CA의 인증서
    • root-ca.key: 루트 CA의 비공개 키
    • server.crt: 인그레스 프록시에 대한 제공 인증서
    • server.key: 인그레스 프록시의 비공개 키
  4. 제공 인증서 및 키가 포함된 Kubernetes 보안 비밀을 만듭니다.

    kubectl create secret tls example-server-creds --key=server.key --cert=server.crt \
        --namespace gke-system

    결과 보안 비밀은 이름이 example-server-creds로 지정됩니다.

배포 및 서비스 만들기

이 가이드의 HTTP 부분에서 배포와 서비스를 만든 경우 이를 그대로 둡니다. 만들지 않았으면 지금 HTTP에 설명된 단계를 수행하여 만듭니다.

인그레스 만들기

이전에 HTTP 부분에서 인그레스를 만들었으면 인그레스를 삭제한 후 계속 진행합니다.

인그레스를 삭제합니다.

kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress my-ingress

이전에 만든 서비스의 트래픽을 처리하려면 tls 섹션이 있는 새 인그레스를 만듭니다. 이렇게 하면 클라이언트와 인그레스 프록시 간에 HTTPS가 사용 설정됩니다.

인그레스의 매니페스트는 다음과 같습니다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress-2
spec:
  tls:
  - hosts:
    - altostrat.com
    secretName: example-server-creds
  rules:
  - host: altostrat.com
    http:
      paths:
      - path: /greet-the-world
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60000
      - path: /greet-kubernetes
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60001

my-ingress-2.yaml이라는 파일에 매니페스트를 저장하고 인그레스를 만듭니다.

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f my-ingress-2.yaml

다음을 테스트하여 확인합니다.

  • /greet-the-world 경로를 테스트합니다.

    curl -v --resolve altostrat.com:443:CLUSTER_INGRESS_VIP\
        https://altostrat.com/greet-the-world \
        --cacert root-ca.crt

    출력:

    Hello, world!
    Version: 2.0.0
    Hostname: hello-deployment-5ff7f68854-wqzp7
    
  • /greet-kubernetes 경로를 테스트합니다.

    curl -v --resolve altostrat.com:443:CLUSTER_INGRESS_VIP \
        https://altostrat.com/greet-kubernetes --cacert root-ca.crt

    출력:

    Hello Kubernetes!
    

LoadBalancer 서비스 만들기

LoadBalancer 유형은 NodePort 유형의 확장입니다. 따라서 LoadBalancer 유형의 서비스에 클러스터 IP 주소 및 하나 이상의 nodePort 값이 포함됩니다. 기본적으로 Kubernetes는 LoadBalancer 서비스에 노드 포트를 할당합니다. 이러한 할당은 클러스터에 할당된 2,768개의 사용 가능한 노드 포트를 빠르게 소진할 수 있습니다. 노드 포트를 저장하려면 LoadBalancer 서비스 사양에서 allocateLoadBalancerNodePorts 필드를 false로 설정하여 부하 분산기 노드 포트 할당을 사용 중지합니다. 이렇게 설정하면 Kubernetes가 LoadBalancer 서비스에 노드 포트를 할당할 수 없습니다. 자세한 내용은 Kubernetes 문서의 부하 분산기 NodePort 할당 중지를 참조하세요.

다음은 노드 포트를 사용하지 않는 서비스를 만드는 매니페스트입니다.

apiVersion: v1
kind: Service
metadata:
  name: service-does-not-use-nodeports
spec:
  selector:
    app: my-app
  type: LoadBalancer
  ports:
  - port: 8000
  # Set allocateLoadBalancerNodePorts to false
  allocateLoadBalancerNodePorts: false

삭제

인그레스를 삭제합니다.

kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress INGRESS_NAME

INGRESS_NAMEmy-ingress 또는 my-ingress-2와 같은 인그레스 이름으로 바꿉니다.

서비스를 삭제합니다.

kubectl --kubeconfig CLUSTER_KUBECONFIG delete service hello-service

배포를 삭제합니다.

kubectl --kubeconfig CLUSTER_KUBECONFIG delete deployment hello-deployment

LoadBalancer 서비스를 삭제합니다.

kubectl --kubeconfig CLUSTER_KUBECONFIG delete service service-does-not-use-nodeports