目录

背景和需求

前提条件

解决方案 #1

1. 将预置的 Kubernetes api-server service 发布成 Internal Load Balancer

2. 创建 nginx 服务作为 kubernetes 的反向代理

3. 创建 kubeconfig

4. 验证

5. 完整的自动化脚本

解决方案 #2

1. 创建第三方云和 Google Cloud 的连接

2. 验证第三方云访问 GKE API server

解决方案 #3

总结


背景和需求

如今,一些用户在使用多云的环境时,希望采用统一的发布平台来实现多云的控制和资源管理。在 GCP 的网络架构中,有的用户希望通过一个 VPC 作为 Hub,来提供对后端的GKE集群进行访问。

在下面的网络架构中,大家可以看到,在 GCP Hub VPC 和第三方云 VPC 和之间已经用专线打通,由于后端的 GKE VPC2 和 GKE 控制面 VPC3 之间是 Peer,但是 GKE 私有集群 Master 的 VPC Peer 不可见亦不可配置,所以从第三方云 VPC 无法直接访问 GKE 控制面的 API Server,造成了发布平台管理的困难。

为了解决上诉问题,我们可以考虑采用以下的几种解决方案。

前提条件

在进行实验之前,请确保以下的环境和设置都已经准备好。这里我们使用gcloud命令行进行配置,当然你也可以选择使用在UI页面上操作和配置。

1.创建跳板机 

gcloud compute instances create jumpbox --project=flius-vpc-2

--zone=europe-west3-c --machine-type=e2-medium

--network-interface=network-tier=PREMIUM,subnet=default

--service-account=647512629680-compute@developer.gserviceaccount.com

--scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/trace.append

--create-disk=auto-delete=yes,boot=yes,device-name=jumpbox,image=projects/debian-cloud/global/images/debian-10-buster-v20220310,mode=rw,size=10,type=projects/flius-vpc-2/zones/us-central1-a/diskTypes/pd-balanced


Created [https://www.googleapis.com/compute/v1/projects/flius-vpc-2/zones/europe-west3-c/instances/jumpbox].

NAME     ZONE            MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS

jumpbox  europe-west3-c  e2-medium                  10.156.0.8   34.159.42.248  RUNNING

2. 创建 GKE 私有集群并设置私有集群 Master 对跳板机授权网络配置

gcloud beta container --project "flius-vpc-2"

clusters create "private-cluster-test"

--zone "us-central1-c"

--machine-type "e2-medium"

--image-type "COS_CONTAINERD" --disk-type "pd-standard" --disk-size "100" --metadata disable-legacy-endpoints=true

--scopes "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append"

--max-pods-per-node "110" --num-nodes "1"

--logging=SYSTEM,WORKLOAD --monitoring=SYSTEM

--enable-private-nodes --master-ipv4-cidr "192.168.50.0/28"

--enable-ip-alias --network "projects/flius-vpc-2/global/networks/default"

--subnetwork "projects/flius-vpc-2/regions/us-central1/subnetworks/default"

--no-enable-intra-node-visibility --default-max-pods-per-node "110"

--enable-master-authorized-networks --master-authorized-networks 34.159.42.248/32

--addons HorizontalPodAutoscaling,HttpLoadBalancing

--enable-autoupgrade --enable-autorepair --max-surge-upgrade 1 --max-unavailable-upgrade 0 --node-locations "us-central1-c"

3. VPC 防火墙配置

gcloud compute firewall-rules create allow-rdp-ingress-from-iap

  --direction=INGRESS

  --action=allow

  --rules=tcp:3389

  --source-ranges=35.235.240.0/20


gcloud compute firewall-rules create allow-ssh-ingress-from-iap

  --direction=INGRESS

  --action=allow

  --rules=tcp:22

  --source-ranges=35.235.240.0/20

解决方案 #1

虽然 GKE VPC 不可见也不可配置,但可以考虑通过 Internal Load Balancer 暴露 GKE API Server,再通过 Nginx 反向代理发布 API Server,并最终通过自动化脚本来进行 Nginx 的创建和配置。

下面我们来看看具体怎么实现,首先我们通过手工创建和配置。

1. 将预置的 Kubernetes api-server service 发布成 Internal Load Balancer

kubectl edit svc kubernetes


apiVersion: v1

kind: Service

metadata:

  annotations:

    cloud.google.com/neg: '{"ingress":true}'

    networking.gke.io/load-balancer-type: Internal

spec:

  type: LoadBalancer


kubectl get svc

NAME         TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE

kubernetes   LoadBalancer   10.40.0.1    10.128.0.26   443:30024/TCP   2d20h

2. 创建 nginx 服务作为 kubernetes 的反向代理,并开启 ssl

创建 PVC

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: nginx-pvc

spec:

  storageClassName: standard

  accessModes:

  - ReadWriteOnce

  resources:

    requests:

      storage: 1Gi

创建 Deployment

apiVersion: apps/v1

kind: Deployment

metadata:

  name: nginx

spec:

  selector:

    matchLabels:

      app: nginx

  replicas: 2

  template:

    metadata:

      labels:

        app: nginx

    spec:

      volumes:

      - name: nginx-pvc

        persistentVolumeClaim:

          claimName: nginx-pvc

      containers:

      - name: nginx

        image: nginx

        volumeMounts:

        - mountPath: "/etc/nginx/conf.d"

          name: nginx-pvc

创建 Service 并指定为 Internal Load Balancer

apiVersion: v1

kind: Service

metadata:

  annotations:

    cloud.google.com/neg: '{"ingress":true}'

    networking.gke.io/load-balancer-type: Internal

  labels:

    app: nginx

  name: nginx

  namespace: default

spec:

  ports:

  - port: 443

  selector:

    app: nginx

  type: LoadBalancer

查找 Nginx Pod 的 GKE 节点

kubectl get po -o wide

NAME                     READY   STATUS    RESTARTS   AGE   IP          NODE                                             NOMINATED NODE   READINESS GATES

nginx-855dcccf78-l8pqj   1/1     Running   0          56m   10.36.8.5   gke-private-cluster-default-pool-a9966b2c-gq92   <none>           <none>

进入 Nginx 的 Pod

kubectl exec -it nginx-855dcccf78-l8pqj bash

root@nginx-855dcccf78-l8pqj:/# cd /etc/nginx/conf.d/

生成 key,crt,pem

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/cert.key -out /etc/nginx/cert.crt

cat cert.crt cert.key > server.pem

获取 PVC 的 Volume

kubectl get pvc

NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE

nginx-pvc   Bound    pvc-7da8c220-9819-4b60-885d-8eb467af6fd0   1Gi        RWO            standard       52m

ssh 进入到挂载了 PVC 的 GKE 节点

注:这个步骤主要是为了修改 nginx 的配置文件和生成证书,你也可以进入 nginx 的 pod 中完成

gcloud beta compute ssh --zone "us-central1-b" "gke-private-cluster-default-pool-a9966b2c-gq92"  --tunnel-through-iap --project "flius-vpc-2"

进入 Volume

mount | grep pvc-7da8c220-9819-4b60-885d-8eb467af6fd0 # pvc volume


/dev/sdb on /var/lib/kubelet/pods/e436a6cb-422e-4d4e-bc01-9e739ae526b5/volumes/kubernetes.io~gce-pd/pvc-7da8c220-9819-4b60-885d-8eb467af6fd0 type ext4 (rw,relatime)


cd /var/lib/kubelet/pods/e436a6cb-422e-4d4e-bc01-9e739ae526b5/volumes/kubernetes.io~gce-pd/pvc-7da8c220-9819-4b60-885d-8eb467af6fd0

创建 nginx 的配置文件 default.conf

vi default.conf

server {


    listen 443;

    server_name 10.128.0.28;


    ssl_certificate           /etc/nginx/conf.d/cert.crt;

    ssl_certificate_key       /etc/nginx/conf.d/cert.key;


    ssl on;

    ssl_session_cache  builtin:1000  shared:SSL:10m;

    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;

    ssl_prefer_server_ciphers on;


    access_log            /var/log/nginx/jenkins.access.log;


    location / {


      proxy_set_header        Host $host;

      proxy_set_header        X-Real-IP $remote_addr;

      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

      proxy_set_header        X-Forwarded-Proto $scheme;


      # Fix the "It appears that your reverse proxy set up is broken" error.

      proxy_pass          https://192.168.20.2;

      proxy_read_timeout  90;

      proxy_redirect      https://10.128.0.28 https://192.168.20.2;


    }

  }

注:10.128.0.28 是 nginx ilb 的 ip 地址,192.168.20.2 是 GKE 控制面的 ip 地址

重启 nginx

nginx -s reload

3.创建 kubeconfig

创建一个新的 Kubernetes Service Account 来连接集群

gcloud container clusters get-credentials private-cluster --region us-central1 --project flius-vpc-2


kubectl create serviceaccount my-ksa


kubectl create clusterrolebinding my-view-binding-1

 --clusterrole view --serviceaccount default:my-ksa 


kubectl create clusterrolebinding my-node-reader-binding-1

 --clusterrole node-reader --serviceaccount default:my-ksa 


kubectl create clusterrolebinding my-cluster-admin-binding-1

 --clusterrole cluster-admin --serviceaccount default:my-ksa


server=https://10.128.0.28 

name=$(kubectl get serviceaccount my-ksa -o jsonpath='{$.secrets[0].name}') 

ca=$(kubectl get secret/$name -o jsonpath='{.data.ca.crt}') 

token=$(kubectl get secret/$name -o jsonpath='{.data.token}' | base64 --decode) namespace=$(kubectl get secret/$name -o jsonpath='{.data.namespace}' | base64 --decode)


echo "

apiVersion: v1

kind: Config

clusters:

- name: default-cluster

  cluster:

    certificate-authority-data: : ${ca}

    server:  ${server}

contexts:

- name: default-context

  context:

    cluster: default-cluster

    namespace: default

    user: default-user

current-context: default-context

users:

- name: default-user

  user:

    token: : ${token}

" > my-ksa.kubeconfig

完整的 kubeconfig 如下

apiVersion: v1

kind: Config

clusters:

- name: default-cluster

  cluster:

    certificate-authority-data: ******

    server: https://10.128.0.28

contexts:

- name: default-context

  context:

    cluster: default-cluster

    namespace: default

    user: default-user

current-context: default-context

users:

- name: default-user

  user:

    token: ******

4. 验证

在其他云 VPC 的虚机上验证拷贝上一步生成的 kubeconfig 到第三方云 VPC 中需要访问 GKE  Master API Server 的虚机上,再验证以下命令

kubectl cluster-info

用 curl 验证

curl --cacert server.pem  https://10.128.0.28 -H "Authorization: Bearer <上一步的token>"


#返回200

注:server.pem 为第 2 步中创建生成的

5. 完整的自动化脚本

static_ip=<static ip> #指定一个静态的loadBalancer地址

region=us-central1 #指定region

project_id=<project id> #指定project id

api_server_ip=$(kubectl get endpoints kubernetes -o jsonpath={.subsets[0].addresses[0].ip})


gcloud beta compute addresses create nginx-static-ilb-ip

   --project=$project_id

   --subnet=default

   --addresses=$static_ip

   --region=$region

   --purpose=SHARED_LOADBALANCER_VIP


#生成key, crt, cert

openssl req -x509 -nodes -days 365 -subj /CN=${static_ip} -newkey rsa:2048 -keyout cert.key -out cert.crt

cat cert.crt cert.key > server.pem


#生成secret

kubectl create secret tls nginx-secret-tls

  --cert=cert.crt

  --key=cert.key


#生成nginx.conf

cat <<EOF > nginx.conf

server {


    listen 443;

    server_name ${static_ip};


    ssl_certificate           /etc/nginx/cert/tls.crt;

    ssl_certificate_key       /etc/nginx/cert/tls.key;


    ssl on;

    ssl_session_cache  builtin:1000  shared:SSL:10m;

    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;

    ssl_prefer_server_ciphers on;


    access_log            /var/log/nginx/access.log;


    location / {


      proxy_set_header        Host $host;

      proxy_set_header        X-Real-IP $remote_addr;

      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

      proxy_set_header        X-Forwarded-Proto $scheme;


      # Fix the "It appears that your reverse proxy set up is broken" error.

      proxy_pass          https://${api_server_ip};

      proxy_read_timeout  90;

      proxy_redirect      https://${static_ip} https://${api_server_ip};


    }

  }

EOF


kubectl create configmap nginx-conf --from-file=nginx.conf=nginx.conf


cat <<EOF > deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

  name: nginx

spec:

  selector:

    matchLabels:

      app: nginx

  replicas: 2

  template:

    metadata:

      labels:

        app: nginx

    spec:

      volumes:

      - name: nginx-conf

        configMap:

          name: nginx-conf

          items:

          - key: nginx.conf

            path: nginx.conf

      - name: nginx-secret

        secret:

          secretName: nginx-secret-tls

      containers:

      - name: nginx

        image: nginx

        volumeMounts:

        - name: nginx-conf

          mountPath: /etc/nginx/conf.d/nginx.conf

          subPath: nginx.conf

          readOnly: true

        - name: nginx-secret

          mountPath: "/etc/nginx/cert"

          readOnly: true

EOF


cat <<EOF  > service.yaml

apiVersion: v1

kind: Service

metadata:

  annotations:

    cloud.google.com/neg: '{"ingress":true}'

    networking.gke.io/load-balancer-type: Internal

  labels:

    app: nginx

  name: nginx

  namespace: default

spec:

  ports:

  - port: 443

  selector:

    app: nginx

  type: LoadBalancer

  loadBalancerIP: $static_ip

EOF


kubectl apply -f .

解决方案 #2

Interconnect + Cloud VPN 方案架构图

架构说明:

本方案是通过第三方云和 Google cloud 的 GKE google managed vpc 私有网络打通,从而实现从第三方云访问 GKE master api server。由于 GKE master 是 Google managed vpc 不可见,但是 GKE node pool 有 vpc peering 关系,我们充分利用了 vpc peering 的路由 import 和 export,同时将 GKE master 网段通过 bgp 发布给第三方云,整个方案全部托管服务。以下是具体的步骤。

1. 创建第三方云和 Google Cloud 的 Interconnect 或者 VPN 连接,本文用 VPN 作为一个连接环境。

注:以下步骤可以用命令行,这里只是用 Console UI 是为了让大家更好的理解。

1)在 Google Cloud 上创建 VPN Gateway

2)在 Google Cloud 上创建 Cloud Router

3)第三方云创建完 VPN 相关配置后,本地创建 Custom Gateway

4)Google Cloud 上创建 VPN Tunnel,一共需要创建四条 Tunnel,这里只截图其中一个配置,其他配置同理配置,这里注意 psk 是要第三方云生成的 key 填写到 Google Cloud 上。

5)Google Cloud 配置 BGP session,本文案例创建了四个 bgp session,这里只截取其中一个。

6)Google Cloud VPN 和 BGP 配置完成后,可以看见如下的状态。

7)Google Cloud Clud Router 进 Custom Route 发布 GKE Master 网段路由。

8)在 VPC Network Peering 找到对应的 GKE Master 的 VPC Peering,修改并勾选 import 和 export routes 如下,以允许 GKE Master 的网段路由和第三方云的路由学习。

9)以上操作完成后你将看到 import 的路由里包括 GKE 网段的,export 路由里包括第三方云的网段,也可以同时查看 GCP 和第三方云的路由表。

注:Google Cloud 上能看到172.31的网段,第三方云应该能看到192.168 的 GKE master 网段

2. 验证第三方云上的虚拟机访问 GKE API server。

1)创建 kubconfig

gcloud container clusters get-credentials private-cluster zone us-central1 --project flius-vpc-2


kubectl create serviceaccount my-ksa


kubectl create clusterrolebinding my-view-binding-1

 --clusterrole view --serviceaccount default:my-ksa 


kubectl create clusterrolebinding my-node-reader-binding-1

 --clusterrole node-reader --serviceaccount default:my-ksa 


kubectl create clusterrolebinding my-cluster-admin-binding-1

 --clusterrole cluster-admin --serviceaccount default:my-ksa

2)第三方云上创建 VM 并安装 gcloud 命令

[root@ip-172-31-23-209 ec2-user]# kubectl get nodes

NAME                                             STATUS   ROLES    AGE     VERSION

gke-private-cluster-default-pool-a8d55aec-c7cg   Ready    <none>   6h13m   v1.21.6-gke.1503

gke-private-cluster-default-pool-a8d55aec-n77f   Ready    <none>   6h13m   v1.21.6-gke.1503

gke-private-cluster-default-pool-a8d55aec-p9s1   Ready    <none>   6h13m   v1.21.6-gke.1503

[root@ip-172-31-23-209 ec2-user]# kubectl cluster-info

Kubernetes control plane is running at https://192.168.32.34

GLBCDefaultBackend is running at https://192.168.32.34/api/v1/namespaces/kube-system/services/default-http-backend:http/proxy

KubeDNS is running at https://192.168.32.34/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

Metrics-server is running at https://192.168.32.34/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy


To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

[root@ip-172-31-23-209 ec2-user]

注:基于这个 VPN 的解决方案还可以解决跨 vpc 的 private service access 访问的问题。比如 Google Cloud 的 redis。同样只需要将 Private service access 对应的 VPC peering 做路由的 export 和 import,找到名字为“servicenetworking-googleapis-com”的 peering 做 import export 即可.

解决方案 #3

Private Service Connect 允许跨属于不同群组、团队、项目或组织的 VPC 网络以不公开方式使用服务。您可以使用由您定义且位于您的 VPC 网络内部的 IP 地址发布和使用服务。您可以使用 Private Service Connect 访问 Google API 和服务,或另一个 VPC 网络中的代管式服务。

这个我们下期再进行详细的介绍,敬请期待!

总结

本文讲述了重点讲述了两种解决方案来帮助用户轻松访问 GKE Master,在网络连通、安全和易用性方面都给出了相应的实践步骤,同样类似的解决方案还可以用来实现跨 VPC 访问谷歌云其他托管服务,希望能对你实现云原生多云的管理有所帮助。


相关推荐