Ingress for Anthos - apigee/ahr GitHub Wiki
STATUS: Draft; TASK: normalize to the AHR-* conventions;
"Ingress for Anthos is designed to meet the load balancing needs of multi-cluster, multi-regional environments. It's a controller for the external HTTP(S) load balancer to provide ingress for traffic coming from the internet across one or more clusters." [Ingress for Anthos, Concepts]
"Ingress for Anthos builds on the architecture of the External HTTP(S) Load Balancing. HTTP(S) Load Balancing is a globally distributed load balancer with proxies deployed at 100+ Google points of presence (PoPs) around the world."

NOTE: Prerequisites for registering a cluster https://cloud.google.com/anthos/multicluster-management/connect/prerequisites
If not roles/owner, then those roles are required:
roles/gkehub.admin roles/iam.serviceAccountAdmin roles/iam.serviceAccountKeyAdmin roles/resourcemanager.projectIamAdminRequired APIs
gcloud services enable --project=$PROJECT \ container.googleapis.com \ gkeconnect.googleapis.com \ gkehub.googleapis.com \ cloudresourcemanager.googleapis.comGrant users the cluster-admin RBAC role. Check it
kubectl create clusterrolebinding [BINDING_NAME] --clusterrole cluster-admin --user [USER] # check role binding for both region clusters kubectl --context=dc1-cluster auth can-i '*' '*' --all-namespaces kubectl --context=dc2-cluster auth can-i '*' '*' --all-namespaces
?. Verify gkehub API status
gcloud services list --available | grep gkehub.googleapis.com
gkehub.googleapis.com                      GKE Hub
?. Enable gkehub API
gcloud services enable gkehub.googleapis.com
?. Enable the Ingress for Anthos API for your project:
gcloud services enable multiclusteringress.googleapis.com
export GKEHUB_ID=gkehub
export GKEHUB_SA=$SA_DIR/$ORG-gkehub.json
export GKEHUB_SA_ID=$GKEHUB_ID@$PROJECT.iam.gserviceaccount.com
gcloud iam service-accounts create $GKEHUB_ID --project=$PROJECT
gcloud projects add-iam-policy-binding $PROJECT --member="serviceAccount:$GKEHUB_SA_ID" --role="roles/gkehub.connect"
gcloud iam service-accounts keys create $GKEHUB_SA --iam-account=$GKEHUB_SA_ID --project=$PROJECT
Each cluster must be registered as a member of a Hub.
?. Find the URIs for your clusters
gcloud container clusters list --uri
export CLUSTER_URI=$(gcloud container clusters list --uri --filter="name=$CLUSTER")
export DC1_CLUSTER_URI=https://container.googleapis.com/v1/projects/emea-cs-hybrid-demo6/zones/us-east1-b/clusters/dc1-cluster
export DC2_CLUSTER_URI=https://container.googleapis.com/v1/projects/emea-cs-hybrid-demo6/zones/asia-east1-b/clusters/dc2-cluster?. Register the dc1-cluster
# dc1-cluster
(
export CLUSTER=dc1-cluster
export CLUSTER_URI=$(gcloud container clusters list --uri --filter="name=$CLUSTER")
gcloud container hub memberships register $CLUSTER --project=$PROJECT --gke-uri=$CLUSTER_URI --service-account-key-file=$GKEHUB_SA
)?. Register the dc2-cluster
# dc2-cluster
(
export CLUSTER=dc2-cluster
export CLUSTER_URI=$(gcloud container clusters list --uri --filter="name=$CLUSTER")
gcloud container hub memberships register $CLUSTER --project=$PROJECT --gke-uri=$CLUSTER_URI --service-account-key-file=$GKEHUB_SA
)?. Verify membership list
gcloud container hub memberships list
NAME         EXTERNAL_ID
dc2-cluster  1d977520-6ae6-11ea-9951-42010af0007c
dc1-cluster  a52d527d-6ae5-11ea-aa7b-42010a8e0fdb"Ingress for Anthos runs as a service outside of the cluster and is managed by Google Cloud. The ingress controller watches for MultiClusterIngress and MultiClusterService resources in GKE and configures load balancers and NEGs as a result.

Specifying a config cluster enables Ingress for Anthos." [IfA Concepts, Architecture]
?. Enable Ingress for Anthos and select dc1-cluster as the config cluster
gcloud alpha container hub features multiclusteringress enable --config-membership=projects/$PROJECT/locations/global/memberships/dc1-cluster
Waiting for Feature to be created...done.
WARNING: generic::failed_precondition: WARNING: multiclusteringress feature will eventually require enablement of anthos.googleapis.com
?. Verify config cluster status
gcloud alpha container hub features multiclusteringress describe
featureState:
  details:
    code: OK
    description: Multicluster Ingress requires Anthos license enablement. Unlicensed
      usage is unrestricted for the MCI Beta API. Note that licensing will be enforced
      for use of the Generally Available MCI API.
  detailsByMembership:
    projects/755738281610/locations/global/memberships/dc1-cluster:
      code: OK
      description: CRDs are established
  lifecycleState: ENABLED
multiclusteringressFeatureSpec:
  configMembership: projects/emea-cs-hybrid-demo6/locations/global/memberships/dc1-cluster
name: projects/emea-cs-hybrid-demo6/locations/global/features/multiclusteringress
updateTime: '2020-03-24T21:22:55.881503252Z'
NOTE: While lifecycleState is marked as enabled, this feature is not ready to use until your config cluster has an OK status. 
The Ingress provides a shared virtual IP (VIP) address for the app deployments.
To create the load balancer, you need two resources: a MultiClusterIngress and one or more MultiClusterServices. MultiClusterIngress and MultiClusterService objects are multi-cluster analogs to the existing Kubernetes Ingress and Service resources used in the single cluster context.
?. Create a static global IP
gcloud compute addresses create apigee-mci --global
Created [https://www.googleapis.com/compute/v1/projects/emea-cs-hybrid-demo6/global/addresses/apigee-mci].
?. Verify status of the IP address
gcloud compute addresses describe apigee-mci  --global
**address: 35.190.60.112**
addressType: EXTERNAL
creationTimestamp: '2020-03-26T05:16:29.168-07:00'
description: ''
id: '2585767625150768114'
ipVersion: IPV4
kind: compute#address
name: apigee-mci
networkTier: PREMIUM
selfLink: https://www.googleapis.com/compute/v1/projects/emea-cs-hybrid-demo6/global/addresses/apigee-mci
status: RESERVED?. Copy TLS secret between namespaces
# TODO: Debug: 
# kubectl get secret emea-cs-hybrid-demo6-test-dc1.hybrid-apigee.net-ingressgateway-certs --namespace=istio-system -o yaml | yq w - data.namespace apigee-mci| yq d - metadata | kubectl apply -f -
# ns: apigee
kubectl -n apigee create secret tls emea-cs-hybrid-demo6-test-dc1.hybrid-apigee.net-ingressgateway-certs --key $RUNTIME_SSL_KEY --cert $RUNTIME_SSL_CERT
# ns: apigee-mci
kubectl -n apigee-mci create secret tls emea-cs-hybrid-demo6-test-dc1.hybrid-apigee.net-ingressgateway-certs --key $RUNTIME_SSL_KEY --cert $RUNTIME_SSL_CERT
?. Deifine mcs manifest
cat <<EOT > apigee-mcs.yaml
apiVersion: networking.gke.io/v1beta1
kind: MultiClusterService
metadata:
  name: apigee-runtime-emea-cs-hybrid-demo6-test-zone-svc
  namespace: apigee
  annotations:
    networking.gke.io/app-protocols: '{"https-8443":"HTTPS"}'
    beta.cloud.google.com/backend-config: '{"ports": {"8443":"zone-health-check-cfg"}}'
spec:
  template:
    spec:
      selector:
        com.apigee.apigeedeployment: apigee-runtime-emea-cs-hybrid-demo6-test
      ports:
      - name: https-8443
        protocol: TCP
        port: 8443
        targetPort: 8443
  clusters:
  - link: "us-east1-b/dc1-cluster"
  - link: "asia-east1-b/dc2-cluster"
EOT?. Apply manifest
kubectl apply -f apigee-mcs.yaml?. Verify mcs The derived Service resource created by MCS are of Headless type. Therefore, None is an expected value of the ClusterIP property.
kubectl get svc
NAME                                                            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)             AGE
...
mci-apigee-runtime-emea-cs-hybrid-demo6--svc-ybxkj03wo8mpnbtu   ClusterIP   None          <none>        8443/TCP            5d2h
?. Define Manifest
cat <<EOT > apigee-bec.yaml
apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: zone-health-check-cfg
  namespace: apigee
spec:
  healthCheck:
    checkIntervalSec: 5
    timeoutSec: 5
    healthyThreshold: 2
    unhealthyThreshold: 3
    type: HTTPS
    port: 8443
    requestPath: /ping
EOT?. Apply manifest
kubectl apply -f apigee-mcs.yaml
To be exposed by the same Ingress, these characteristics must be the same across clusters:
- Be deployed into a namespace with the same name across clusters.
- Share the same set of labels so they can be selected as a unit across the clusters.
?. Define MultiClusterService object
cat <<EOT > apigee-mci.yaml
apiVersion: networking.gke.io/v1beta1
kind: MultiClusterIngress
metadata:
  name: apigee-hybrid-mci
  namespace: apigee
  annotations:
    networking.gke.io/static-ip: 35.190.60.112
spec:
  template:
    spec:
      backend:
       serviceName: apigee-runtime-emea-cs-hybrid-demo6-test-zone-svc
       servicePort: 8443
      tls:
      - secretName: emea-cs-hybrid-demo6-test-dc1.hybrid-apigee.net-ingressgateway-certs
EOT
?. Verify MCI
kubectl -n apigee-mci describe mci apigee-hybrid-mci
?. Execute curl request to /ping API
export RUNTIME_HOST_ALIAS=mc-demo6-test.hybrid-apigee.net
export RUNTIME_IP=35.190.60.112
curl --resolve "$RUNTIME_HOST_ALIAS:443:$RUNTIME_IP" https://$RUNTIME_HOST_ALIAS/ping -v
...
> GET /ping HTTP/1.1
> Host: mc-demo6.hybrid-apigee.net
> User-Agent: curl/7.52.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
< user-agent: curl/7.52.1
< accept: */*
...
?. Trace view of the request above

It helps to be able to identify manifests and devived resources to investigate and diagnose potential problems.

?. MultiClusterService events
kubectl describe mcs apigee-runtime-emea-cs-hybrid-demo6-test-zone-svc
...
Events:
  Type    Reason  Age                    From                              Message
  ----    ------  ----                   ----                              -------
  Normal  SYNC    22m (x1330 over 5d2h)  multi-cluster-ingress-controller  Derived Service was ensured in cluster us-east1-b/dc1-cluster
  Normal  SYNC    57s (x1334 over 5d2h)  multi-cluster-ingress-controller  Derived Service was ensured in cluster asia-east1-b/dc2-cluster
?. MultiCluster Ingress Events and Status (I.e., references to Firewalls, Forwarding Rules, NEGs, etc.)
kubectl describe mci apigee-hybrid-mci
...
        Secret Name:  emea-cs-hybrid-demo6-test-dc1.hybrid-apigee.net-ingressgateway-certs
Status:
  Cloud Resources:
    Backend Services:
      mci-2dy6qj-8443--i6n413-apigee-apigee-runtime-emea
    Firewalls:
      mci-2dy6qj-default-l7
    Forwarding Rules:
      mci-2dy6qj-fw-apigee-apigee-hybrid-mci
      mci-2dy6qj-fws-apigee-apigee-hybrid-mci
    Health Checks:
      mci-2dy6qj-8443--i6n413-apigee-apigee-runtime-emea
    Network Endpoint Groups:
      zones/us-east1-b/networkEndpointGroups/k8s1-3b2d76ce-apig-mci-apigee-runtime-emea-cs-hybri-84-2bb37757
      zones/us-east1-c/networkEndpointGroups/k8s1-3b2d76ce-apig-mci-apigee-runtime-emea-cs-hybri-84-2bb37757
      zones/us-east1-d/networkEndpointGroups/k8s1-3b2d76ce-apig-mci-apigee-runtime-emea-cs-hybri-84-2bb37757
      zones/asia-east1-a/networkEndpointGroups/k8s1-7c3e1506-apig-mci-apigee-runtime-emea-cs-hybri-84-627b0d18
      zones/asia-east1-b/networkEndpointGroups/k8s1-7c3e1506-apig-mci-apigee-runtime-emea-cs-hybri-84-627b0d18
      zones/asia-east1-c/networkEndpointGroups/k8s1-7c3e1506-apig-mci-apigee-runtime-emea-cs-hybri-84-627b0d18
    Target Proxies:
      mci-2dy6qj-apigee-apigee-hybrid-mci
      mci-2dy6qj-apigee-apigee-hybrid-mci
    URL Map:  mci-2dy6qj-apigee-apigee-hybrid-mci
  VIP:        35.190.60.112
Events:       
...
?. Backend services
gcloud compute backend-services list
?. Network Endpoint Groups
gcloud compute network-endpoint-groups list
NOTES:
- Have some patience. It takes minutes (5-7) for mcs/mci/bec objects to be created and become eventually consistent across regions. It happens... Eventually
- Current BEC manifest uses /ping proxy as the healthcheck. This is a temporary hack until IaF will add support for TCP probes (end-of-April). Right now you can use any existing http/https probes.
- As of now, your mcs/mci/bec object need to be located in the same namespace as the pods you're exposing.
REFERENCES:
- Anycast introduction https://www.usenix.org/legacy/events/lisa10/tech/full_papers/Weiden.pdf