הטמעה לדוגמה של איזון עומסים בשכבה 7 ב-NGINX

ב-Google Distributed Cloud (GDC)‎ עם air gap יש מאזן עומסים מנוהל ומובנה בשכבה 4 (L4), אבל הרבה אפליקציות ארגוניות דורשות יכולות מתקדמות בשכבה 7 (L7), כמו ניתוב מבוסס-מארח, ניהול TLS מרכזי וחלוקת תנועה מורכבת. בעבר, זה נעשה באמצעות Ingress API, שנחשב עכשיו ל-feature-frozen בקהילת Kubernetes.

ארכיטקטורת העזר הזו מספקת פתרון לניהול עצמי של איזון עומסים בשכבה 7. כשפורסים את בקר הקוד הפתוח הפופולרי NGINX Gateway Fabric באשכול GDC Standard, הלקוחות יכולים לנתב בצורה חלקה תנועה ב-L7 לסביבות היברידיות. בארכיטקטורה הזו נעשה שימוש בסיום TLS ‏(HTTPRoute) כדי לנתב תנועה על סמך ציון שם השרת (SNI) גם לפודים מובְנים בקונטיינרים וגם לאפליקציות שמתארחות במכונות וירטואליות חיצוניות.

ארכיטקטורה

דיאגרמת ארכיטקטורה של איזון עומסים בשכבה 7 של NGINX ב-GDC עם air gap.

הרכיבים העיקריים של הפתרון כוללים:

  • לקוח: ישות שיוזמת בקשות HTTPS כדי ליצור אינטראקציה עם האפליקציות.
  • אשכול GDC רגיל: ב-GDC יש דרך מובנית ליצור אשכולות Kubernetes Vanilla. בפתרון הזה, האשכול יארח את מאזן העומסים L7 ואת הבקרים שלו, יחד עם עומסי העבודה והשירות ללא ממשק משתמש למכונות וירטואליות חיצוניות.
  • מאזן עומסים GDC L4: מאזן העומסים המובנה L4 משמש כנקודת הכניסה, ומפיץ את תעבורת הנתונים של TCP/443 ישירות לפודים של Kubernetes שמריצים את בקרי השער.
  • בקרי שער: אופרטורים של NGINX שפועלים באשכול הרגיל. הם עוקבים אחרי משאבי Gateway API (כמו Gateway ו-HTTPRoute) ומעדכנים באופן דינמי את שרתי ה-proxy הבסיסיים. הפתרון ישתמש ב-Nginx Gateway Fabric.
  • ‫Gateway (עם HTTPRoute): משאבים סטנדרטיים של Kubernetes שמגדירים את יציאת ההאזנה הפיזית (443) ואת כללי הניתוב של המארח שמבוססים על SNI עם סיום TLS.
  • עומס עבודה בקונטיינרים (Pods): פריסה רגילה של Kubernetes שנחשפת באופן פנימי באמצעות Service רגיל של Kubernetes.
  • עומס עבודה מבוסס-VM (חיצוני): עומס עבודה שמתארח ב-VM חיצוני ברשת הפרויקט, ונחשף לשרת ה-proxy באמצעות Service ללא ממשק משתמש ב-Kubernetes ונקודת קצה מותאמת אישית שמכילה את כתובת ה-IP הישירה של ה-VM.
  • ‫Harbor Registry: Container Registry פרטי שמשמש לאחסון ולהצגה של קובצי האימג' של שרת ה-proxy ושל האפליקציה בסביבה עם air gap.

באשכול הרגיל, יוצרים ארבעה מרחבי שמות:

  • מרחב השמות nginx-gateway שמארח את המשאבים של Nginx Gateway Fabric:

    המשאבים של מרחב השמות nginx-gateway.

  • מרחב השמות של load-balancer שמארח את המשאבים של Gateway:

    המשאבים של מרחב השמות של מאזן העומסים.

  • מרחב השמות vm-app מארח את כתובת ה-IP החיצונית של המכונה הווירטואלית vm-app, שירות ללא ממשק משתמש, EndpointSlice שמצביע על כתובת ה-IP החיצונית ו-HTTPRoute ל-Gateway:

    המשאבים של מרחב השמות vm-app.

  • מרחב השמות hello-app מארח את Deployment, Service, HTTPRoute ו-Gateway לעומס העבודה של מאגר ההדגמה:

    המשאבים של מרחב השמות hello-app.

לפני שמתחילים

לפני שפורסים את הפתרון הזה, צריך לוודא שהתנאים המוקדמים הבאים מתקיימים:

  • התוכנות הנדרשות: helm, ‏ docker, ‏ kubectl
  • התחברות ל-CLI והגדרה מקומית: מורידים את gdcloud CLI ממסוף GDC ומגדירים את הסביבה באופן מקומי.

    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
    
  • הגדרת פרויקט: יוצרים פרויקט בסביבת GDC עם air gap כדי לאחסן את המשאבים.

    gdcloud projects create $PROJECT_ID
    
  • תפקידי IAM: נותנים למשתמש את התפקידים אדמין של אשכול ואדמין רגיל של אשכול כדי לנהל משאבים ב-Kubernetes, ואת התפקיד אדמין של מכונה ב-Harbor כדי לשלוח אימג'ים.

    # 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
    

יצירת אשכול רגיל

בקטע הזה מוסבר איך להגדיר אשכול Kubernetes רגיל בסביבת GDC עם air gap. אשכול רגיל מספק בסיס גמיש וחזק לפריסת עומסי עבודה שונים, כולל Nginx Gateway Controller והאפליקציות בהתאמה אישית. כדי לוודא שהאשכול מוגדר כמו שצריך ונגיש לפריסות הבאות, צריך לבצע את השלבים הבאים.

  1. מזהים את סוגי האימג'ים הזמינים של המכונות הווירטואליות באמצעות הפקודה:

    gdcloud compute machine-types list
    
  2. בוחרים סוג מכונה שמתאים לצומתי העובדים של האשכול. במדריך הזה מומלץ להשתמש בסוג מכונה עם לפחות 4 מעבדי vCPU.

    export MACHINE_TYPE="MACHINE_TYPE"
    
  3. מורידים את קובץ ה-kubeconfig של שרת ה-API לניהול ומגדירים כינוי:

    export CLUSTER_NAME="CLUSTER_NAME"
    
    KUBECONFIG=kubeconfig-admin.yaml gdcloud clusters \
      get-credentials ${ORG_NAME}-admin
    
    alias km="kubectl --kubeconfig kubeconfig-admin.yaml"
    
  4. יוצרים אשכול רגיל עם 2 צומתי עובדים:

    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
    

    לפרטים נוספים על האפשרויות הזמינות, עיינו במסמכי התיעוד.

    יצירת אשכול רגיל יכולה להימשך עד 60 דקות. כדי לבדוק את הסטטוס, משתמשים בפקודה הבאה:

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

    כשהאשכול מוכן, הפלט אמור להציג את המצב Running, כך:

    NAME         STATE     K8S VERSION
    my-cluster   Running   1.30.12-gke.300
    
  5. אחרי שהאשכול מוכן, מאחזרים את פרטי הכניסה שלו:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  6. כדי לקצר את הפקודות של kubectl בהמשך המדריך, יוצרים שם חלופי. הכינוי הזה ישמש לאינטראקציה עם האשכול הרגיל:

    alias kk="kubectl --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml"
    
  7. יוצרים מרחבי שמות לבקר, לאפליקציה בקונטיינר לדוגמה hello-app ולאפליקציה לדוגמה שמבוססת על VM:

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

יצירה ושילוב של Harbor Registry

‫Harbor הוא מאגר של קובצי אימג' של קונטיינרים עם תמיכה מובנית ב-GDC עם air gap. בקטע הזה נסביר איך לשלב את Harbor Registry עם אשכול רגיל, כולל הגדרת פרטי כניסה וסודות כדי לאפשר משיכה ודחיפה מאובטחות של קובצי אימג'.

  1. יוצרים מכונה של Harbor בפרויקט.
  2. יוצרים פרויקט Harbor במכונה של Harbor.
  3. מגדירים את משתני הסביבה:

    export HARBOR_INSTANCE_NAME="HARBOR_INSTANCE_NAME"
    export HARBOR_INSTANCE_URL="HARBOR_INSTANCE_URL"
    export HARBOR_PROJECT="HARBOR_INSTANCE_PROJECT"
    export IMAGE_PULL_SECRET_NAME="harbor-secret"
    
  4. נכנסים למכונה של Harbor באמצעות חשבון רובוט:

    docker --config=./docker login ${HARBOR_INSTANCE_URL}
    
  5. יוצרים את הסודות באשכול הרגיל:

    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
    

פריסת אפליקציית הדגמה בקונטיינר

בקטע הזה מוסבר איך לפרוס אפליקציה לדוגמה בקונטיינר (hello-app) באשכול Kubernetes של GDC עם air gap. תיצרו את המשאבים הנדרשים של Kubernetes Deployment ו-Service כדי להריץ את hello-app ולחשוף אותו באופן פנימי בתוך האשכול, כהכנה לגישה באמצעות מאזן העומסים L7.

  1. מעלים ל-Harbor תמונה לדוגמה של האפליקציה בקונטיינר:

    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. פורסים את המניפסט הבא באשכול הרגיל:

    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
    

לאחר מכן, מוודאים שהפריסה והשירות נמצאים שם:

kk get svc,deploy -n hello-app

פריסת אפליקציית הדגמה ב-VM

בקטע הזה מוסבר איך לפרוס אפליקציית הדגמה במכונה וירטואלית (VM) מחוץ לאשכול Kubernetes. כדי לדמות אפליקציה חיצונית שמאזן העומסים יכול לחשוף, תגדירו שרת HTTP ב-VM. כך תדגימו את היכולת של מאזן העומסים לנהל את התנועה למשאבים בתוך האשכול ומחוצה לו.

קודם כל, יוצרים מכונה וירטואלית לאפליקציית ההדגמה

  1. פותחים את מסוף GDC בדפדפן האינטרנט.
  2. בוחרים את אותו פרויקט שבו יצרתם את אשכול Kubernetes הרגיל.
  3. פותחים את התפריט ולוחצים על Virtual machines.
  4. לוחצים על Create Instance.
  5. נותנים למכונה הווירטואלית את השם vm-workload. תמונה של 2 vCPU מספיקה לדוגמה.
  6. לקובץ האימג' של דיסק האתחול, בוחרים בהפצה של Ubuntu 22.04, שכוללת את Python מותקן מראש.
  7. לוחצים על יצירה.
  8. מחכים כמה דקות עד שהמכונה הווירטואלית תהיה מוכנה.
  9. יוצרים חיבור SSH למכונה הווירטואלית:
    1. במסוף GDC, לוחצים על המכונה הווירטואלית.
    2. לוחצים על Connect with SSH.

אחרי שמתחברים למסוף SSH, מריצים את הפקודה הבאה:

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

כדי לנתב תנועה למכונה וירטואלית, יוצרים שירות ללא ממשק משתמש (ללא בוררים). הכתובת הזו תמופה באופן ידני לכתובת ה-IP הפנימית של המכונה הווירטואלית באמצעות משאב EndpointSlice.

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

מריצים את הפקודה הבאה כדי לקבל את כתובת ה-IP של המכונה הווירטואלית vm-workload:

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

הפלט יהיה כתובת ה-IP של המכונה הווירטואלית שתידרש כדי להגדיר את המשאב EndpointSlice.

יוצרים את המשאב EndpointSlice שיתחבר לשירות ללא בורר של אפליקציית ה-VM, ומציינים את כתובת ה-IP של ה-VM שאליה צריך לנתב את התנועה.

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

יצירת אישורים בחתימה עצמית

בקטע הזה מוסבר איך ליצור אישורי TLS בחתימה עצמית לאפליקציות שלכם. לשם הנוחות, במדריך הזה נעשה שימוש באישורים חתומים באופן עצמי, אבל בסביבות ייצור צריך להשתמש באישורים ברמת ייצור, כמו שמתואר בקטע אופציונלי: שימוש באישורים שמוכנים לייצור. האישורים האלה חיוניים להפסקת HTTPS בשער Nginx, ומבטיחים תקשורת מוצפנת בין הלקוחות לבין מאזן העומסים גם לעומסי עבודה בקונטיינרים וגם לעומסי עבודה שמבוססים על מכונות וירטואליות.

  1. יוצרים אישור לאפליקציה בקונטיינר:

    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
    
  2. יוצרים אישור לאפליקציית המכונה הווירטואלית:

    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
    

פריסת NGINX

בקטע הזה מפורטים השלבים לפריסת Nginx Gateway Controller, כולל התקנת הגדרות המשאבים בהתאמה אישית (CRD) של Gateway API, הגדרת Nginx Gateway Fabric באמצעות Helm ואימות ההתקנה באשכול הרגיל. כך התשתית מוכנה לניתוב מתקדם בשכבה 7.

התקנת ה-CRD של Gateway API

כדי להשתמש ב-Gateway API, צריך להתקין באשכול הגדרות של משאבים בהתאמה אישית (CRD) לפני שפורסים את הבקר. נשתמש בהגדרות הניסיוניות הרשמיות של משאבים בהתאמה אישית (CRD) מהפרויקט של Gateway API (במדריך הזה אנחנו משתמשים בגרסה 1.2.0).

  1. מתקינים את ה-CRD הניסיוניים:

    kk apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/experimental-install.yaml
    
  2. מריצים את הפקודה kk get crd gateways.gateway.networking.k8s.io כדי לוודא שה-CRD הותקנו בהצלחה.

התקנת NGINX Gateway Fabric

פורסים את הבקר של NGINX Gateway Fabric באמצעות Helm. הבקר הזה ינטר את משאבי Gateway API ויגדיר את NGINX לטיפול בתנועה.

  1. מגדירים את תג קובץ האימג' של NGINX:

    helm template nfg oci://ghcr.io/nginx/charts/nginx-gateway-fabric | grep "image:" # get the tag of the nginx-gateway-fabric (in following case 2.4.2)
    
    export NGINX_TAG="2.4.2"
    
  2. מושכים את התמונות של NGINX Gateway Fabric ו-NGINX, ואז דוחפים אותן למרשם Harbor:

    docker pull ghcr.io/nginx/nginx-gateway-fabric:${NGINX_TAG}
    docker tag ghcr.io/nginx/nginx-gateway-fabric:${NGINX_TAG} \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric:${NGINX_TAG}
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric:${NGINX_TAG}
    
    docker pull nginx:1.27.3
    docker tag nginx:1.27.3 \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.27.3
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.27.3
    
  3. יוצרים את מרחב השמות nginx-gateway ומוסיפים את הסוד לשליפת קובץ האימג' של Harbor:

    kk create namespace nginx-gateway
    
    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n nginx-gateway
    
  4. פורסים את NGINX Gateway Fabric באמצעות Helm, ומפנים לקובצי האימג' ברג'יסטרי של Harbor:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml helm upgrade --install ngf \
      oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
      --create-namespace -n nginx-gateway \
      --set nginxGateway.image.tag="${NGINX_TAG}" \
      --set nginxGateway.image.repository="${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric" \
      --set nginx.image.repository="${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx" \
      --set nginxGateway.serviceAccount.imagePullSecret="${IMAGE_PULL_SECRET_NAME}" \
      --set nginx.imagePullSecret="${IMAGE_PULL_SECRET_NAME}"
    

    הפריסה של Kubernetes שולפת באופן מאובטח תמונות מ-Harbor באמצעות הסוד שסופק (${IMAGE_PULL_SECRET_NAME}), שמכיל את פרטי הכניסה של חשבון הרובוט של Harbor.

  5. מוודאים שהמשאבים של GatewayClass אושרו:

    kk get gatewayclass
    

    האפשרות nginx אמורה להופיע עם ACCEPTED = True.

יצירת המכונה של Gateway

מגדירים את המכונה הלוגית של מאזן העומסים שמאזינה ליציאה 443. נגדיר אותו למצב Terminate, כלומר השער יבצע סיום TLS ויפענח את התנועה לפני שיעביר אותה.

יוצרים ומחילים gateway.yaml:

cat << EOF > gateway.yaml 
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
  namespace: load-balancer
spec:
  gatewayClassName: nginx
  listeners:
  - name: https-k8s-workload
    hostname: "k8s-app.example.com"
    port: 443
    protocol: HTTPS
    allowedRoutes: 
      namespaces: 
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: tls-containerized 
  - name: https-vm-workload
    hostname: "vm-app.example.com"
    port: 443
    protocol: HTTPS
    allowedRoutes: 
      namespaces: 
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: tls-vm 
EOF

kk apply -f gateway.yaml

כדי לוודא שהשער נפרס בהצלחה, בודקים אם הערך של PROGRAMMED הוא True:

kk get gateway my-gateway --n load-balancer

מוודאים שמאזן עומסים מנוהל של GDC L4 נפרס כשירות לצד השער.

kk get services -n load-balancer

מוודאים ששירות Nginx נוצר והוגדר. הפלט אמור להיראות כך:

NAME               TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)         AGE
my-gateway-nginx   LoadBalancer   10.252.19.148   10.200.32.43   443:30649/TCP   45h

הגדרת הלוגיקה של הניתוב ב-L7‏ (HTTPRoute)

מקשרים את HTTPRoute לשער כדי להגדיר איך התנועה צריכה להתחלק על סמך שם המארח המבוקש (SNI).

יוצרים ומחילים routing.yaml:

cat << EOF > routing.yaml 
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: k8s-http-route
  namespace: hello-app
spec:
  parentRefs:
  - name: my-gateway 
    namespace: load-balancer
  hostnames:
  - "k8s-app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: hello-app 
      namespace: hello-app
      port: 80          
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: vm-http-route
  namespace: vm-app
spec:
  parentRefs:
  - name: my-gateway
    namespace: load-balancer
  hostnames:
  - "vm-app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: vm-app-svc  
      namespace: vm-app
      port: 443       
EOF

kk apply -f routing.yaml   

אחזור כתובת ה-IP של מאזן העומסים

מריצים את הפקודה כדי לקבל את כתובת ה-IP של מאזן העומסים של Nginx

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

המידע הזה נדרש כדי לאמת את הגישה לאפליקציות. הוא יכונה LOAD_BALANCER_IP.

יצירת מכונה וירטואלית של לקוח

פועלים לפי השלבים ליצירת VM של לקוח

  1. פותחים את מסוף GDC בדפדפן האינטרנט.
  2. פותחים את התפריט ולוחצים על Virtual machines.
  3. לוחצים על Create Instance.
  4. יוצרים מכונה וירטואלית בשם client, בוחרים סוג מכונה קטן ובוחרים ב-Rocky Linux או ב-Ubuntu, שכוללות את curl מותקן מראש.
  5. לוחצים על יצירה.
  6. מחכים כמה דקות עד שהמכונה הווירטואלית תהיה מוכנה.
  7. אחרי שהמכונה הווירטואלית מוכנה, יוצרים חיבור SSH למכונה הווירטואלית:
    1. במסוף GDC, לוחצים על המכונה הווירטואלית.
    2. לוחצים על Connect with SSH.

אימות הגישה והניתוב

כדי לבדוק את הניתוב, מריצים פקודות curl מה-VM של הלקוח. אפשר להתחבר לשתי האפליקציות באמצעות שמות המארחים שהוגדרו להן, עם כתובת ה-IP של מאזן העומסים.

אם מעבירים את הדגל --resolve ב-curl, אפשר לאלץ את שמות הדומיינים לזהות את כתובת ה-IP של מאזן העומסים בשכבה 4 של GDC עם air gap. שימו לב שאנחנו מעבירים את הדגל -k כדי לתת אמון באישורים בחתימה עצמית.

בודקים את האפליקציה בקונטיינר של Kubernetes:

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

איך בודקים את האפליקציה של המכונה הווירטואלית החיצונית:

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

אם הוא מוגדר בצורה נכונה, בקר ה-Gateway יפעל בצורה חלקה כסיום TLS ויעביר את התנועה ליעד.

אופציונלי: שימוש באישורים שמוכנים לייצור

בקטע הזה מוסבר איך להשתמש בשירות ה-CA של GDC עם air gap כדי ליצור רשות אישורי בסיס (CA) פרטית, להנפיק אישורים חתומים לעומסי העבודה ולעדכן באופן מאובטח את המכונות הווירטואליות של הלקוח ואת האשכול הרגיל של GDC עם air gap.

בקטע הזה מוסבר איך להשתמש בשירות ה-CA של GDC עם air gap כדי ליצור רשות אישורי בסיס (CA) פרטית ולהנפיק אישורים תקפים לאפליקציות שלכם. אם תתקינו את ה-CA הבסיסי הזה ב-VM של הלקוח, תוכלו לוודא שסיום ה-TLS פועל בצורה חלקה עם אישורים מהימנים, בלי לעקוף את אזהרות ה-SSL (לדוגמה, באמצעות curl -k).

מתן ההרשאות הנדרשות וקבלת פרטי כניסה

כדי לנהל את שירות ה-CA ולהנפיק אישורים, המשתמש צריך את תפקידי ה-IAM המתאימים בפרויקט.

  1. מקצים את התפקידים certificate-authority-service-admin ו-certificate-requester:

    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. מקבלים את פרטי הכניסה של שרת ה-API לניהול:

    gdcloud clusters get-credentials ${ORG_NAME}-admin
    

יצירת רשות אישורים בסיסית (root CA)

אתם תיצרו רשות אישורים בשרת ה-API לניהול, במרחב השמות של הפרויקט.

  1. מחילים את המשאב CertificateAuthority:

    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'
    

הנפקה ופריסה של אישורים

אחרי שרשות האישורים מוכנה, צריך לבקש אישורים גם לאפליקציה בקונטיינר וגם לאפליקציה שמבוססת על VM. הבקשות האלה מתבצעות בשרת ה-API לניהול, וצריך להעביר את המפתחות שנוצרו לאשכול הרגיל.

יוצרים בקשות לשני הדומיינים:

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

מחכים כמה רגעים עד שהאישורים יונפקו. כדי לוודא שהם מוכנים, בודקים אם התנאי Ready הוא True:

km get certificaterequests -n ${PROJECT_ID}

עדכון האשכול הרגיל

אם פעלתם לפי ההוראות בקטעים הקודמים במדריך, יש לכם סודות בחתימה עצמית באשכול הרגיל. צריך למחוק אותם לפני שיוצרים את הגרסאות החדשות והחתומות:

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

עכשיו מחלצים את האישורים החתומים משרת ה-API לניהול ויוצרים את הסודות החדשים באשכול הרגיל.

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

הסודות החדשים יתקבלו באופן אוטומטי וירעננו על ידי מאזני העומסים.

הגדרת אמון הלקוח

כדי לאמת את ההגדרה, צריך להגדיר ל-VM של הלקוח לתת אמון ב-CA הבסיסי החדש.

מחלצים את אישור ה-CA הבסיסי לקובץ:

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

מעבירים את האישור למכונה הווירטואלית של הלקוח. (אפשר להעתיק את התוכן של my-root-ca.crt ולהדביק אותו בקובץ ב-VM של הלקוח).

ב-VM של הלקוח, מעדכנים את מאגר האישורים.

אם המכונה הווירטואלית של client היא Ubuntu:

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

אם ה-VM של client הוא Rocky Linux:

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

גישה מאומתת

עכשיו אתם יכולים לגשת לאפליקציות באמצעות curl בלי הדגל -k. החיבור יהיה מהימן לחלוטין.

בודקים את האפליקציה בקונטיינר של Kubernetes:

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

בודקים את אפליקציית המכונה הווירטואלית:

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

אם הפעולה תצליח, פלט האפליקציה יוצג מיד בלי אזהרות לגבי אישור SSL.