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

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

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

ארכיטקטורה

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

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

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

ב-Standard Cluster, יוצרים ארבעה מרחבי שמות:

  • מרחב השמות 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 GDCH_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, צריך להקצות למשתמש את התפקידים Cluster Admin ו-Standard Cluster Admin. כדי לדחוף תמונות, צריך להקצות לו את התפקיד Harbor Instance Admin.

    # 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 והאפליקציות המותאמות אישית שלכם. השלבים הבאים יעזרו לכם לוודא שהאשכול מוגדר כראוי ושהגישה אליו אפשרית לצורך פריסות עתידיות.

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

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

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

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

  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. תצרו את משאבי הפריסה והשירות הדרושים של Kubernetes כדי להפעיל את 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) מחוץ לאשכול Kubernetes. הגדרת שרת HTTP במכונה וירטואלית מאפשרת לדמות אפליקציה חיצונית שמאזן העומסים יכול לחשוף, וכך להדגים את היכולת שלו לנהל תעבורה למשאבים בתוך האשכול ומחוצה לו.

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

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

כדי להפנות תנועה למכונה וירטואלית, יוצרים Service ללא ראש (headless) (ללא סלקטורים). הכתובת הזו תמופה ידנית לכתובת ה-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 שיתחבר לשירות ללא בורר של אפליקציות למכונות וירטואליות, ומגדירים את כתובת ה-IP של המכונה הווירטואלית שאליה התנועה תנותב.

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 Gateway, כדי להבטיח תקשורת מוצפנת בין לקוחות לבין מאזן העומסים, גם לעומסי עבודה מבוססי-מכונות וירטואליות וגם לעומסי עבודה מבוססי-קונטיינרים.

  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. יוצרים אישור לאפליקציית ה-VM:

    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, כולל התקנת Gateway API Custom Resource Definitions‏ (CRD), הגדרת Nginx Gateway Fabric באמצעות Helm ואימות ההתקנה באשכול הרגיל. הפעולה הזו מכינה את התשתית לניתוב מתקדם בשכבה 7.

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

כדי לפרוס את בקר Gateway API, צריך להתקין Custom Resource Definitions (CRDs) באשכול. אנחנו נשתמש בהגדרות הרשמיות של משאבים מותאמים אישית (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.

יצירת מכונת השער

מגדירים את המופע הלוגי של מאזן העומסים שמקשיב ביציאה 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

הגדרת לוגיקת הניתוב ברמה 7 (HTTPRoute)

מקשרים את HTTPRoutes ל-Gateway כדי להגדיר איך התנועה צריכה להתחלק על סמך שם המארח המבוקש (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. פותחים את התפריט ולוחצים על מכונות וירטואליות.
  3. לוחצים על Create Instance.
  4. יוצרים מכונה וירטואלית בשם client, בוחרים סוג מכונה קטן ובוחרים באחת מהאפשרויות הבאות: Rocky Linux או Ubuntu, שמגיעות עם curl שכבר מותקן.
  5. לוחצים על יצירה.
  6. מחכים כמה דקות עד שהמכונה הווירטואלית מוכנה.
  7. אחרי שהמכונה הווירטואלית מוכנה, יוצרים חיבור SSH למכונה הווירטואלית:
    1. במסוף GDC, לוחצים על מכונת ה-VM.
    2. לוחצים על Connect with SSH (התחברות באמצעות SSH).

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

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

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

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

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

בודקים את אפליקציית ה-VM החיצונית:

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

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

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

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

בקטע הזה מוסבר איך להשתמש ב-CA Service של GDC עם air gap כדי ליצור רשות אישורי בסיס (CA) פרטית ולהנפיק אישורים תקפים לאפליקציות שלכם. התקנת רשות האישורים הבסיסית הזו במכונה הווירטואלית של הלקוח מאפשרת לוודא שסיום ה-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
    

יצירת רשות האישורים (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'
    

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

אחרי שה-CA מוכן, תבקשו אישורים גם לאפליקציה שמבוססת על קונטיינר וגם לאפליקציה שמבוססת על מכונה וירטואלית. הבקשות האלה מתבצעות בשרת של ה-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 הבסיסי לקובץ:

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 ולהדביק אותו בקובץ במכונה הווירטואלית של הלקוח).

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

אם מכונת client VM היא 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

אם מכונת client VM היא 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.