HAProxy-Referenzimplementierung für Layer-7-Load-Balancing

Google Distributed Cloud (GDC) mit Air Gap bietet einen integrierten verwalteten Layer-4-Load-Balancer (L4), aber viele Unternehmensanwendungen erfordern erweiterte Layer-7-Funktionen (L7) wie hostbasiertes Routing, zentralisierte TLS-Verwaltung und komplexe Trafficaufteilung. In der Vergangenheit wurde dies mit der Ingress API erreicht, die in der Kubernetes-Community jetzt als Feature-Frozen gilt.

Diese Referenzarchitektur bietet eine selbstverwaltete Layer-7-Load-Balancing-Lösung. Durch die Bereitstellung des beliebten Open-Source-Controllers HAProxy auf einem GDC-Standardcluster können Kunden L7-Traffic nahtlos an Hybridumgebungen weiterleiten. In dieser Architektur wird die TLS-Terminierung (HTTPRoute) verwendet, um Traffic basierend auf der Server Name Indication (SNI) sowohl an integrierte containerisierte Pods als auch an Anwendungen weiterzuleiten, die auf externen virtuellen Maschinen gehostet werden.

Architektur

Architekturdiagramm für HAProxy-Layer-7-Load-Balancing in GDC mit Air Gap.

Zu den wichtigsten Komponenten der Lösung gehören:

  • Client: Eine Entität, die HTTPS-Anfragen zur Interaktion mit den Anwendungen initiiert.
  • GDC-Standardcluster: GDC bietet eine integrierte Möglichkeit, Kubernetes-Vanilla-Cluster zu erstellen. In dieser Lösung hostet der Cluster den L7-Load-Balancer und seine Controller sowie die Arbeitslasten und den monitorlosen Dienst für externe VMs.
  • GDC-L4-Load-Balancer: Der integrierte L4-Load-Balancer dient als Einstiegspunkt und verteilt TCP/443-Traffic direkt an die Kubernetes-Pods, auf denen die Controller ausgeführt werden.
  • Ingress-Controller: HAProxy-Operatoren, die im Standardcluster ausgeführt werden. Sie überwachen die Ingress-Ressourcen und aktualisieren die zugrunde liegenden Proxys dynamisch. In der folgenden Implementierung wird der HAProxy Ingress Controller verwendet.
  • Ingress: Standardisierte Kubernetes-Ressourcen, die den physischen Listening-Port (443) und die SNI-basierten Host-Routingregeln mit TLS-Terminierung definieren.
  • Containerisierte Arbeitslast (Pods): Ein Standard-Kubernetes-Deployment, das intern mit einem regulären Kubernetes-Service bereitgestellt wird.
  • VM-basierte Arbeitslast (extern): Eine Arbeitslast, die auf einer externen VM im Projektnetzwerk gehostet wird und dem Proxy über einen monitorlosen Kubernetes-Service und einen benutzerdefinierten Endpunkt mit der direkten IP-Adresse der VM zugänglich gemacht wird.
  • Harbor Registry: Eine private Container-Registry, die zum Speichern und Bereitstellen der Proxy- und Anwendungs-Images in der Air-Gap-Umgebung verwendet wird.

Im Standardcluster erstellen Sie drei Namespaces:

  • Der Namespace load-balancer hostet den HAProxy Ingress Controller und die HAProxy-Load-Balancer-Arbeitslast:

    Die Namespace-Ressourcen des Load Balancers.

  • Der Namespace hello-app hostet die Deployment, ein Service und ein Ingress für die Demo-Containerarbeitslast:

    Die Namespace-Ressourcen von hello-app.

  • Der Namespace vm-app hostet einen Headless-Dienst, der die externe VM-IP-Adresse, einen EndpointSlice, der auf die externe IP-Adresse verweist, und einen Ingress bereitstellt:

    Die Ressourcen des vm-app-Namespace.

Hinweis

Bevor Sie diese Lösung bereitstellen, müssen die folgenden Voraussetzungen erfüllt sein:

  • Erforderliche Software: helm, docker, kubectl
  • CLI-Anmeldung und lokale Einrichtung: Laden Sie die gdcloud CLI von der GDC-Konsole herunter und richten Sie Ihre Umgebung lokal ein:

    export USER_NAME="USER_NAME"
    export PROJECT_ID="PROJECT_ID"
    export ZONE="ZONE"
    export ORG_NAME="ORG_NAME"
    export GDC_URL="GDC_URL"
    
    gdcloud components install gdcloud-k8s-auth-plugin
    
    gdcloud config set core/organization_console_url \
      https://console.$ORG_NAME.$ZONE.$GDC_URL
    gdcloud config set core/zone $ZONE
    gdcloud config set core/project ${PROJECT_ID}
    
    gdcloud auth login # use --login-config-cert option in case of TLS error
    
  • Projekteinrichtung: Erstellen Sie ein Projekt in Ihrer GDC-Umgebung mit Air Gap, um die Ressourcen zu speichern:

    gdcloud projects create $PROJECT_ID
    
  • IAM-Rollen: Weisen Sie Ihrem Nutzer die Rollen Cluster Admin und Standard Cluster Admin zu, um Kubernetes-Ressourcen zu verwalten, und die Rolle Harbor Instance Admin, um Images zu übertragen:

    # Grant standard cluster and cluster admin roles
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=cluster-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=standard-cluster-admin
    
    # Grant Harbor instance admin role
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=harbor-instance-admin
    

Standardcluster erstellen

In diesem Abschnitt wird beschrieben, wie Sie einen Standard-Kubernetes-Cluster in Ihrer GDC-Umgebung mit Air Gap einrichten. Ein Standardcluster bietet eine flexible und robuste Grundlage für die Bereitstellung verschiedener Arbeitslasten, einschließlich des HAProxy Ingress-Controllers und Ihrer benutzerdefinierten Anwendungen. Mit den folgenden Schritten wird sichergestellt, dass Ihr Cluster richtig konfiguriert und für nachfolgende Bereitstellungen zugänglich ist.

  1. Führen Sie folgenden Befehl aus, um die verfügbaren VM-Image-Typen zu ermitteln:

    gdcloud compute machine-types list
    
  2. Wählen Sie einen geeigneten Maschinentyp für die Worker-Knoten des Clusters aus. Für diese Anleitung wird ein Maschinentyp mit mindestens 4 vCPUs empfohlen.

    export MACHINE_TYPE="MACHINE_TYPE"
    
  3. Rufen Sie die kubeconfig-Datei des Verwaltungs-API-Servers ab und legen Sie einen Alias fest:

    export CLUSTER_NAME="CLUSTER_NAME"
    
    KUBECONFIG=kubeconfig-admin.yaml gdcloud clusters \
      get-credentials ${ORG_NAME}-admin
    
    alias km="kubectl --kubeconfig kubeconfig-admin.yaml"
    
  4. Erstellen Sie einen Standardcluster mit zwei Worker-Knoten:

    km create -f - <<EOF
    apiVersion: cluster.gdc.goog/v1
    kind: Cluster
    metadata:
      name: ${CLUSTER_NAME}
      namespace: ${PROJECT_ID}
    spec:
      nodePools:
      - machineTypeName: ${MACHINE_TYPE}
        nodeCount: 2
        name: ${CLUSTER_NAME}-node-pool
    EOF
    

    Weitere Informationen zu den verfügbaren Optionen finden Sie in der Dokumentation.

    Die Erstellung eines Standardclusters kann bis zu 60 Minuten dauern. Verwenden Sie den folgenden Befehl, um den Status zu prüfen:

    km get clusters/${CLUSTER_NAME} \
      -n ${PROJECT_ID} \
      --watch
    

    Wenn der Cluster bereit ist, sollte in der Ausgabe der Status „Running“ angezeigt werden, etwa so:

    NAME         STATE     K8S VERSION
    my-cluster   Running   1.30.12-gke.300
    
  5. Wenn der Cluster bereit ist, rufen Sie seine Anmeldedaten ab:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  6. Erstellen Sie einen Alias, um die kubectl-Befehle im weiteren Verlauf dieses Leitfadens prägnanter zu gestalten. Dieser Alias wird für die Interaktion mit dem Standardcluster verwendet:

    alias kk="kubectl --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml"
    
  7. Erstellen Sie Namespaces für den Controller, die containerisierte Demoanwendung „hello-app“ und die VM-basierte Demoanwendung:

    kk create namespace load-balancer
    kk create namespace hello-app
    kk create namespace vm-app
    

Harbor Registry erstellen und einbinden

Harbor ist eine Container-Image-Registry mit integrierter Unterstützung in GDC mit Air Gap. In diesem Abschnitt werden Sie durch die Schritte zum Einbinden einer Harbor Registry in Ihren Standardcluster geführt. Dazu gehört auch das Konfigurieren von Anmeldedaten und Secrets, um das sichere Abrufen und Übertragen von Images zu ermöglichen.

  1. Erstellen Sie eine Harbor-Instanz in Ihrem Projekt.
  2. Erstellen Sie in Ihrer Harbor-Instanz ein Harbor-Projekt.
  3. Legen Sie Umgebungsvariablen fest:

    export HARBOR_INSTANCE_NAME="HARBOR_INSTANCE_NAME"
    export HARBOR_INSTANCE_URL="HARBOR_INSTANCE_URL"
    export HARBOR_PROJECT="HARBOR_PROJECT"
    export IMAGE_PULL_SECRET_NAME="harbor-secret"
    
  4. Melden Sie sich mit einem Roboterkonto bei der Harbor-Instanz an:

    docker --config=./docker login ${HARBOR_INSTANCE_URL}
    
  5. Erstellen Sie die Secrets im Standardcluster:

    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n load-balancer
    
    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n hello-app
    

Containerisierte Demo-Anwendung bereitstellen

In diesem Abschnitt wird die Bereitstellung einer containerisierten Demoanwendung (hello-app) in Ihrem GDC-Kubernetes-Cluster mit Air Gap beschrieben. Sie erstellen die erforderlichen Kubernetes-Deployment- und -Dienstressourcen, um hello-app auszuführen und intern im Cluster verfügbar zu machen, damit der Zugriff über den L7-Load-Balancer möglich ist.

  1. Laden Sie ein Beispielbild für die containerisierte Demo-Anwendung in Harbor hoch:

    docker pull gcr.io/google-samples/hello-app:1.0 \
      --platform linux/amd64
    docker tag gcr.io/google-samples/hello-app:1.0 \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    
  2. Stellen Sie das folgende Manifest im Standardcluster bereit:

    cat << EOF > hello-app.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: hello-app
      template:
        metadata:
          labels:
            app: hello-app
        spec:
          containers:
          - name: hello-server
            image: ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
            ports:
            - containerPort: 8080
          imagePullSecrets:
          - name: ${IMAGE_PULL_SECRET_NAME}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      type: ClusterIP
      selector:
        app: hello-app
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
    EOF
    
    kk apply -f hello-app.yaml
    

Prüfen Sie dann, ob das Deployment und der Dienst vorhanden sind:

kk get svc,deploy -n hello-app

Demo-App auf einer VM bereitstellen

In diesem Abschnitt wird die Bereitstellung einer Demoanwendung auf einer virtuellen Maschine (VM) außerhalb Ihres Kubernetes-Clusters beschrieben. Durch das Einrichten eines HTTP-Servers auf einer VM simulieren Sie eine externe Anwendung, die der Load Balancer bereitstellen kann. So wird die Fähigkeit des Load Balancers demonstriert, Traffic zu Ressourcen sowohl innerhalb als auch außerhalb des Clusters zu verwalten.

Erstellen Sie zuerst eine VM für die Demo-App:

  1. Öffnen Sie die GDC-Konsole in Ihrem Webbrowser.
  2. Wählen Sie dasselbe Projekt aus, in dem Sie Ihren Kubernetes-Standardcluster erstellt haben.
  3. Öffnen Sie das Menü und klicken Sie auf Virtuelle Maschinen.
  4. Klicken Sie auf Instanz erstellen.
  5. Geben Sie der VM den Namen vm-workload. Ein Image mit 2 vCPUs reicht für das Beispiel aus.
  6. Wählen Sie für das Bootlaufwerk-Image eine Ubuntu 22.04-Distribution aus, auf der Python vorinstalliert ist.
  7. Klicken Sie auf Erstellen.
  8. Warten Sie einige Minuten, bis die VM bereit ist.
  9. Stellen Sie eine SSH-Verbindung zur VM her:
    1. Klicken Sie in der GDC-Konsole auf die VM.
    2. Klicken Sie auf Über SSH verbinden.

Nachdem Sie eine Verbindung zur SSH-Konsole hergestellt haben, führen Sie Folgendes aus:

mkdir ~/simple-server
cd ~/simple-server
echo 'Welcome to my VM!' > index.html
python3 -m http.server --bind 0.0.0.0 8080 &

Wenn Sie Traffic an eine VM weiterleiten möchten, erstellen Sie einen monitorlosen Dienst (ohne Selektoren). Diese wird mithilfe einer EndpointSlice-Ressource manuell der internen IP-Adresse der VM zugeordnet.

kk apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: vm-app-svc
  namespace: vm-app
spec:
  ports:
  - protocol: TCP
    port: 443
    targetPort: 443
EOF

Rufen Sie die IP-Adresse der vm-workload-VM ab, indem Sie Folgendes ausführen:

gdcloud compute instances list --project ${PROJECT_ID} \
  | grep workload-vm | awk '{print $3}'

Die Ausgabe ist die IP-Adresse der VM, die zum Einrichten der EndpointSlice-Ressource benötigt wird.

Erstellen Sie die Ressource EndpointSlice, die eine Verbindung zum VM-App-Dienst ohne Selektor herstellt und die IP-Adresse der VM adressiert, an die der Traffic weitergeleitet werden soll.

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: vm-app-endpoints
  namespace: vm-app
  labels:
    kubernetes.io/service-name: vm-app-svc
addressType: IPv4
ports:
  - port: 8080
endpoints:
  - addresses:
    - "VM_IP"
    conditions:
      ready: true

Selbst signierte Zertifikate erstellen

In diesem Abschnitt wird beschrieben, wie Sie TLS-Zertifikate und Kubernetes-Secrets erstellen, um die Kommunikation für Ihre container- und VM-basierten Anwendungen zu sichern. In diesem Leitfaden werden der Einfachheit halber selbst signierte Zertifikate verwendet. In Produktionsumgebungen müssen Sie jedoch produktionsreife Zertifikate verwenden, wie unter Optional: Produktionsreife Zertifikate verwenden beschrieben. Wählen Sie beliebige Beispiel-Domainnamen für diese Anwendungen aus. Durch das Herstellen sicherer Verbindungen sorgen Sie für Datenintegrität und Vertraulichkeit für Clients, die über den HAProxy Ingress Controller auf Ihre Anwendung zugreifen.

Für die containerisierte Anwendung erstellen wir ein selbst signiertes Zertifikat und speichern es als Secret im Load-Balancer-Namespace. Diese wird für TLS verwendet, wenn k8s-app.example.com angefordert wird.

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout tls-containerized.key \
  -out tls-containerized.crt \
  -subj "/CN=k8s-app.example.com" \
  -days 365

kk create secret tls tls-containerized \
  --namespace load-balancer \
  --key tls-containerized.key \
  --cert tls-containerized.crt

kk create secret tls tls-containerized \
  --namespace hello-app \
  --key tls-containerized.key \
  --cert tls-containerized.crt

Für die VM-Anwendung wird ein ähnliches selbst signiertes Zertifikat ausgestellt und gespeichert. Diese wird für TLS verwendet, wenn vm-app.example.com angefordert wird.

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout tls-vm.key \
  -out tls-vm.crt \
  -subj "/CN=vm-app.example.com" \
  -days 365

kk create secret tls tls-vm \
  --namespace load-balancer \
  --key tls-vm.key \
  --cert tls-vm.crt

kk create secret tls tls-vm \
  --namespace vm-app \
  --key tls-vm.key \
  --cert tls-vm.crt

HAProxy bereitstellen

HAProxy-Ingress-Controller und L4-Load-Balancer installieren

export HAPROXY_VERSION=3.1.14

# pull the HAProxy Ingress Controller image and push it to Harbor
docker pull haproxytech/kubernetes-ingress:${HAPROXY_VERSION} \
  --platform linux/amd64
docker tag haproxytech/kubernetes-ingress:${HAPROXY_VERSION} \
  ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/haproxy-ingress:${HAPROXY_VERSION}
docker --config=./docker push \
  ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/haproxy-ingress:${HAPROXY_VERSION}

# Get Helm repo
helm repo add haproxytech https://haproxytech.github.io/helm-charts
helm repo update

# Install the Ingress Controller with helm
helm upgrade --install haproxy-kubernetes-ingress \
  haproxytech/kubernetes-ingress \
  --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml \
  --namespace load-balancer \
  --set controller.image.repository=${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/haproxy-ingress \
  --set controller.image.tag=${HAPROXY_VERSION} \
  --set controller.existingImagePullSecret=${IMAGE_PULL_SECRET_NAME} \
  --set controller.service.type=LoadBalancer \
  --set-json \
  controller.service.annotations='{"networking.gke.io/load-balancer-type": "internal"}'

Der HAProxy-Ingress-Controller erhält eine eindeutige virtuelle IP-Adresse für den Clientzugriff über einen Dienst vom Typ LoadBalancer. Dieser Dienst richtet einen vollständig verwalteten Layer-4-Load-Balancer ein. Der Einfachheit halber wird in dieser Anleitung ein interner Load Balancer erstellt, indem die Annotation load-balancer-type auf internal gesetzt wird. Wenn Sie diese Annotation weglassen, wird ein externer Load Balancer erstellt. Das Kubernetes-Deployment ruft Images sicher aus Harbor ab. Dazu wird das bereitgestellte Secret (${IMAGE_PULL_SECRET_NAME}) verwendet, das die Anmeldedaten des Harbor-Robot-Kontos enthält.

Installation des HAProxy-Ingress-Controllers prüfen

Prüfen Sie, ob die Pods des HAProxy-Ingress-Controllers ausgeführt werden und bereit sind:

kk get pods -n load-balancer

Die Ausgabe sollte so aussehen:

NAME                                          READY   STATUS      RESTARTS   AGE
haproxy-kubernetes-ingress-78dc9c8676-f8fcb   1/1     Running     0          35s
haproxy-kubernetes-ingress-78dc9c8676-lfnr2   1/1     Running     0          65s
haproxy-kubernetes-ingress-crdjob-3-tgj2h     0/1     Completed   0          65s

Prüfen Sie, ob der Dienst des HAProxy-Ingress-Controllers erstellt und konfiguriert wurde:

kk get services -n load-balancer

Die Ausgabe sieht dann ungefähr so aus:

NAME                         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                                                                  AGE
haproxy-kubernetes-ingress   LoadBalancer   10.252.27.46   10.252.4.17   80:32023/TCP,443:31103/TCP,443:31103/UDP,1024:30146/TCP,6060:30718/TCP   10m

Ingress-Ressourcen für die Demoanwendungen definieren

Erstellen Sie die Ingress-Ressource, die den HAProxy mit dem Containeranwendungsdienst verbindet.

cat << EOF > hello-app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-app-ingress
  namespace: hello-app
  annotations:
    haproxy.org/ssl-redirect: "true"
    haproxy.org/ssl-redirect-port: "443"
    haproxy.org/ssl-redirect-code: "308"
spec:
  ingressClassName: haproxy
  tls:
  - hosts:
    - "k8s-app.example.com"
    secretName: tls-containerized
  rules:
  - host: "k8s-app.example.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hello-app
            port:
              number: 80
EOF

kk apply -f hello-app-ingress.yaml

Erstellen Sie die Ingress-Ressource, die eine Verbindung zum VM-App-Dienst ohne Selektor herstellt, und geben Sie die IP-Adresse der VM an, an die der Traffic weitergeleitet werden soll.

cat << EOF > vm-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: vm-app-ingress
  namespace: vm-app
  annotations:
    haproxy.org/ssl-redirect: "true"
    haproxy.org/ssl-redirect-port: "443"
    haproxy.org/ssl-redirect-code: "308"
spec:
  ingressClassName: haproxy
  tls:
  - hosts:
    - "vm-app.example.com"
    secretName: tls-vm
  rules:
  - host: "vm-app.example.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: vm-app-svc
            port:
              number: 443
EOF

kk apply -f vm-ingress.yaml

IP-Adresse des Load Balancers abrufen

Führen Sie den Befehl aus, um die IP-Adresse des Load Balancers abzurufen.

kk get services/haproxy-kubernetes-ingress \
  -n load-balancer \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

Diese wird benötigt, wenn der Zugriff auf die Apps überprüft wird. Diese wird als LOAD_BALANCER_IP bezeichnet.

Client-VM erstellen

So erstellen Sie eine Client-VM:

  1. Öffnen Sie die GDC-Konsole in Ihrem Webbrowser.
  2. Öffnen Sie das Menü und klicken Sie auf Virtuelle Maschinen.
  3. Klicken Sie auf Instanz erstellen.
  4. Erstellen Sie eine VM mit dem Namen client, wählen Sie einen kleinen Maschinentyp aus und wählen Sie entweder Rocky Linux oder Ubuntu aus, auf denen curl vorinstalliert ist.
  5. Klicken Sie auf Erstellen.
  6. Warten Sie einige Minuten, bis die VM bereit ist.
  7. Nachdem die VM bereit ist, stellen Sie eine SSH-Verbindung zur VM her:
    1. Klicken Sie in der GDC-Konsole auf die VM.
    2. Klicken Sie auf Über SSH verbinden.

Zugriff und Routing prüfen

Führen Sie curl-Befehle von Ihrer Client-VM aus, um das Routing zu testen. Sie können eine Verbindung zu beiden Anwendungen herstellen, indem Sie die definierten Hostnamen mit der IP-Adresse des Load Balancers verwenden.

Wenn Sie das Flag --resolve in curl übergeben, können Sie erzwingen, dass die Domainnamen in die IP-Adresse des L4-Load-Balancers von GDC mit Air Gap aufgelöst werden. Beachten Sie, dass wir das Flag -k übergeben, um den selbst signierten Zertifikaten zu vertrauen.

Testen Sie die containerisierte Kubernetes-Anwendung:

curl -k --resolve k8s-app.example.com:443:$LOAD_BALANCER_IP https://k8s-app.example.com -v

Testen Sie die externe VM-Anwendung:

curl -k --resolve vm-app.example.com:443:$LOAD_BALANCER_IP https://vm-app.example.com -v

Wenn der Ingress-Controller richtig konfiguriert ist, fungiert er nahtlos als TLS-Terminator und leitet den Traffic an das Ziel weiter.

Optional: Produktionsreife Zertifikate verwenden

In diesem Abschnitt wird beschrieben, wie Sie den GDC mit Air Gap-CA-Dienst nutzen, um eine private Root-Zertifizierungsstelle (CA) zu erstellen, signierte Zertifikate für Ihre Arbeitslasten auszustellen und Ihre GDC mit Air Gap-Standardcluster- und Client-VMs sicher zu aktualisieren.

In diesem Abschnitt wird beschrieben, wie Sie mit dem CA Service von GDC mit Air Gap eine private Root-Zertifizierungsstelle (CA) erstellen und gültige Zertifikate für Ihre Anwendungen ausstellen. Wenn Sie diese Stamm-CA auf Ihrer Client-VM installieren, können Sie prüfen, ob die TLS-Terminierung mit vertrauenswürdigen Zertifikaten nahtlos funktioniert, ohne SSL-Warnungen umgehen zu müssen (z. B. mit curl -k).

Erforderliche Berechtigungen gewähren und Anmeldedaten abrufen

Zum Verwalten des CA-Dienstes und zum Ausstellen von Zertifikaten benötigt Ihr Nutzer die entsprechenden IAM-Rollen im Projekt.

  1. Weisen Sie die Rollen certificate-authority-service-admin und certificate-requester zu:

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-authority-service-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-requester
    
  2. Rufen Sie die Anmeldedaten des Management API-Servers ab:

    gdcloud clusters get-credentials ${ORG_NAME}-admin
    

Stamm-CA erstellen

Sie erstellen eine Zertifizierungsstelle im Management API-Server in Ihrem Projekt-Namespace.

  1. Wenden Sie die Ressource CertificateAuthority an:

    km apply -f - <<EOF
    apiVersion: pki.security.gdc.goog/v1
    kind: CertificateAuthority
    metadata:
      name: my-root-ca
      namespace: ${PROJECT_ID}
    spec:
      caProfile:
        commonName: "My Root CA"
        duration: 87600h # 10 years
        keyAlgorithm: RSA_2048
        maxChainLength: 1
      caType: ROOT
      keyLocation: HSM
      rotationPolicy:
        cronTime: 0 0 1 1 *
    EOF
    
    km -n ${PROJECT_ID} get \
    certificateauthority.pki.security.gdc.goog/my-root-ca -ojson \
    | jq -r '
    .status.conditions[] | select( .type as $id | "Ready" | index($id)) .status'
    

Zertifikate ausstellen und bereitstellen

Sobald die Zertifizierungsstelle bereit ist, fordern Sie Zertifikate für die containerisierte Anwendung und die VM-basierte Anwendung an. Diese Anfragen erfolgen im Management API-Server und die resultierenden Schlüssel müssen in Ihren Standardcluster verschoben werden.

Erstellen Sie Anfragen für beide Domains:

km apply -f - <<EOF
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-containerized-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "k8s-app.example.com"
      dnsNames:
      - "k8s-app.example.com"
  signedCertificateSecret: tls-containerized-signed
---
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-vm-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "vm-app.example.com"
      dnsNames:
      - "vm-app.example.com"
  signedCertificateSecret: tls-vm-signed
EOF

Warten Sie einen Moment, bis die Zertifikate ausgestellt wurden. Sie können prüfen, ob sie bereit sind, wenn die Bedingung „Bereit“ auf „Wahr“ festgelegt ist:

km get certificaterequests -n ${PROJECT_ID}

Standardcluster aktualisieren

Wenn Sie die vorherigen Abschnitte dieser Anleitung befolgt haben, haben Sie selbstsignierte Secrets in Ihrem Standardcluster. Sie müssen sie löschen, bevor Sie die neuen, signierten Versionen erstellen:

kk delete secret tls-containerized -n load-balancer
kk delete secret tls-vm -n load-balancer

kk delete secret tls-containerized -n hello-app
kk delete secret tls-vm -n vm-app

Extrahieren Sie nun die signierten Zertifikate vom Management API-Server und erstellen Sie die neuen Secrets im Standardcluster.

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-containerized.key

kk create secret tls tls-containerized \
  --namespace load-balancer \
  --key tls-containerized.key \
  --cert tls-containerized.crt

kk create secret tls tls-containerized \
  --namespace hello-app \
  --key tls-containerized.key \
  --cert tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-vm.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-vm.key

kk create secret tls tls-vm \
  --namespace load-balancer \
  --key tls-vm.key \
  --cert tls-vm.crt

kk create secret tls tls-vm \
  --namespace vm-app \
  --key tls-vm.key \
  --cert tls-vm.crt

Die neuen Secrets werden automatisch abgerufen und von Load Balancern aktualisiert.

Clientvertrauen konfigurieren

Zur Überprüfung der Einrichtung müssen Sie Ihre Client-VM anweisen, Ihrer neuen Stamm-CA zu vertrauen.

Extrahieren Sie das Root-CA-Zertifikat in eine Datei:

km get secret -n ${PROJECT_ID} my-root-ca-secret \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > my-root-ca.crt

Übertragen Sie das Zertifikat auf Ihre Client-VM. Sie können den Inhalt von my-root-ca.crt kopieren und in eine Datei auf der Client-VM einfügen.

Aktualisieren Sie den Trust Store auf der Client-VM.

Wenn die client-VM Ubuntu ist:

sudo cp my-root-ca.crt /usr/local/share/ca-certificates/
sudo chmod 644 /usr/local/share/ca-certificates/my-root-ca.crt
sudo update-ca-certificates

Wenn die client-VM Rocky Linux ist:

sudo cp my-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

Zugriff überprüfen

Sie können jetzt mit curl ohne das Flag -k auf Ihre Anwendungen zugreifen. Die Verbindung wird vollständig vertrauenswürdig sein.

Testen Sie die containerisierte Kubernetes-Anwendung:

curl -v --resolve k8s-app.example.com:443:LOAD_BALANDER_IP https://k8s-app.example.com

Testen Sie die VM-Anwendung:

curl -v --resolve vm.example.com:443:LOAD_BALANDER_IP https://vm-app.example.com

Wenn der Vorgang erfolgreich war, wird die Anwendungsausgabe sofort ohne SSL-Zertifikatswarnungen angezeigt.