Skip to content

Commit

Permalink
Merge pull request #771 from apichick/multi-cluster-mesh-gke-fleet-api
Browse files Browse the repository at this point in the history
Example of a multi-cluster mesh on GKE configuring managed control pl…
  • Loading branch information
apichick authored Aug 8, 2022
2 parents 3a47651 + 5c9f336 commit c9735bc
Show file tree
Hide file tree
Showing 21 changed files with 954 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ examples/cloud-operations/binauthz/app/app.yaml
env/
examples/cloud-operations/adfs/ansible/vars/vars.yaml
examples/cloud-operations/adfs/ansible/gssh.sh
examples/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/vars.yaml
examples/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/gssh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# terraform local cache directory
**/.terraform/*

# terraform state files
*.tfstate
*.tfstate.backup

# terraform lock file
*.terraform.lock.hcl

# terraform variable files
*.tfvars

# terraform backend configuration files
backend.tf
*.tfbackend

# node modules
**/node_modules/*

# python virtual environment
**/venv/*

ansible/vars/vars.yaml
ansible/gssh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Multi-cluster mesh on GKE (fleet API)

The following example shows how to create a multi-cluster mesh for two private clusters on GKE. Anthos Service Mesh with automatic control plane management is set up for clusters using the Fleet API. This can only be done if the clusters are in a single project and in the same VPC. In this particular case both clusters having being deployed to different subnets in a shared VPC.

The diagram below depicts the architecture of the example.

![Architecture](architecture.png)

Terraform is used to provision the required infrastructure, create the IAM binding and register the clusters to the fleet.

Ansible is used to execute commands in the management VM. From this VM there is access to the cluster's endpoint. More specifically the following is done using Ansible:

1. Install required dependencies in the VM
2. Enable automatic control plane management in both clusters.
3. Verify the control plane has been provisioned for both clusters.
4. Configure ASM control plane endpoint discovery between the two clusters.
5. Create a sample namespace in both clusters.
6. Configure automatic sidecar injection in the created namespace.
7. Deploy a hello-world service in both clusters
8. Deploy a hello-world deployment (v1) in cluster a
9. Deploy a hello-world deployment (v2) in cluster b
10. Deploy a sleep service in both clusters.
11. Send requests from a sleep pod to the hello-world service from both clusters, to verify that we get responses from alternative versions.

## Running the example

Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fcloud-foundation-fabric&cloudshell_print=cloud-shell-readme.txt&cloudshell_working_dir=examples%2Fcloud-operations%2Fmulti-cluster-mesh-gke-fleet-api), then go through the following steps to create resources:

* `terraform init`
* `terraform apply -var billing_account_id=my-billing-account-id -var parent=folders/my-folder-id -var host_project_id=my-host-project-id -var fleet_project_id=my-fleet-project-id -var mgmt_project_id=my-mgmt-project-id`

Once terraform completes do the following:

* Change to the ansible folder

cd ansible

* Run the ansible playbook

ansible-playbook -v playbook.yaml


## Testing the example

The last two commands executed with Ansible Send requests from a sleep pod to the hello-world service from both clusters. If you see in the output of those two commands responses from alternative versions, everything works as expected.

Once done testing, you can clean up resources by running `terraform destroy`.
<!-- BEGIN TFDOC -->

## Variables

| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [billing_account_id](variables.tf#L17) | Billing account id. | <code>string</code> || |
| [fleet_project_id](variables.tf#L32) | Management Project ID. | <code>string</code> || |
| [host_project_id](variables.tf#L27) | Project ID. | <code>string</code> || |
| [mgmt_project_id](variables.tf#L37) | Management Project ID. | <code>string</code> || |
| [parent](variables.tf#L22) | Parent. | <code>string</code> || |
| [clusters_config](variables.tf#L54) | Clusters configuration. | <code title="map&#40;object&#40;&#123;&#10; subnet_cidr_block &#61; string&#10; master_cidr_block &#61; string&#10; services_cidr_block &#61; string&#10; pods_cidr_block &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; cluster-a &#61; &#123;&#10; subnet_cidr_block &#61; &#34;10.0.1.0&#47;24&#34;&#10; master_cidr_block &#61; &#34;10.16.0.0&#47;28&#34;&#10; services_cidr_block &#61; &#34;192.168.1.0&#47;24&#34;&#10; pods_cidr_block &#61; &#34;172.16.0.0&#47;20&#34;&#10; &#125;&#10; cluster-b &#61; &#123;&#10; subnet_cidr_block &#61; &#34;10.0.2.0&#47;24&#34;&#10; master_cidr_block &#61; &#34;10.16.0.16&#47;28&#34;&#10; services_cidr_block &#61; &#34;192.168.2.0&#47;24&#34;&#10; pods_cidr_block &#61; &#34;172.16.16.0&#47;20&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [istio_version](variables.tf#L98) | ASM version | <code>string</code> | | <code>&#34;1.14.1-asm.3&#34;</code> |
| [mgmt_server_config](variables.tf#L78) | Mgmt server configuration | <code title="object&#40;&#123;&#10; disk_size &#61; number&#10; disk_type &#61; string&#10; image &#61; string&#10; instance_type &#61; string&#10; region &#61; string&#10; zone &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; disk_size &#61; 50&#10; disk_type &#61; &#34;pd-ssd&#34;&#10; image &#61; &#34;projects&#47;ubuntu-os-cloud&#47;global&#47;images&#47;family&#47;ubuntu-2204-lts&#34;&#10; instance_type &#61; &#34;n1-standard-2&#34;&#10; region &#61; &#34;europe-west1&#34;&#10; zone &#61; &#34;europe-west1-c&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [mgmt_subnet_cidr_block](variables.tf#L42) | Management subnet CIDR block. | <code>string</code> | | <code>&#34;10.0.0.0&#47;28&#34;</code> |
| [region](variables.tf#L48) | Region. | <code>string</code> | | <code>&#34;europe-west1&#34;</code> |

<!-- END TFDOC -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[defaults]
inventory = inventory/hosts.ini
timeout = 900

[ssh_connection]
pipelining = True
ssh_executable = ./gssh.sh
transfer_method = piped

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mgmt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

- hosts: mgmt
gather_facts: "no"
vars_files:
- vars/vars.yaml
environment:
USE_GKE_GCLOUD_AUTH_PLUGIN: True
roles:
- role: prerequisites
become: yes
become_method: sudo
- role: install
- role: test

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

- name: Configure endpoint discovery
shell: >
kubectl apply \
-f ~/{{ item }}.secret \
--context "gke_{{ project_id }}_{{ region }}_{{ cluster }}"
with_items: "{{ clusters }}"
when: cluster != item
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

- name: Get cluster credentials
shell: >
gcloud container clusters get-credentials {{ cluster }} \
--region {{ region }} \
--project {{ project_id }} \
--internal-ip
- name: Set context
set_fact:
context: "gke_{{ project_id }}_{{ region }}_{{ cluster }}"

- name: Install ASM in cluster
shell: >
gcloud container fleet mesh update \
--control-plane automatic \
--memberships {{ cluster }} \
--project {{ project_id }}
- name: Wait until MCP is provisioned
shell: >
for i in $(seq 12); do
result=$(gcloud container fleet mesh describe --project {{ project_id }} --format json \
| jq -r '.membershipStates | to_entries[] | select(.key | endswith("{{ cluster }}")) | .value.servicemesh.controlPlaneManagement.state')
if [ "$result" = "ACTIVE" ]; then
break
fi
echo "ASM control plane is not ready yet..."
sleep 60
done
- name: Get endpoint IP
shell: >
gcloud container clusters describe "{{ cluster }}" \
--project "{{ project_id }}" \
--region "{{ region }}" \
--format "value(privateClusterConfig.publicEndpoint)"
register: endpoint

- name: Create secret
shell: >
~/istio-*/bin/istioctl x create-remote-secret \
--context={{ context }} \
--name={{ cluster }} \
--server=https://{{ endpoint.stdout }} > ~/{{ cluster }}.secret
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

- name: Download istio bundle
get_url:
url: https://storage.googleapis.com/gke-release/asm/istio-{{ istio_version }}-linux-amd64.tar.gz
dest: ~/istio.tar.gz

- name: Unarchive istio bundle
unarchive:
src: ~/istio.tar.gz
dest: ~/
remote_src: yes

- name: Install
include_tasks: install.yaml
vars:
cluster: "{{ item }}"
with_items: "{{ clusters }}"

- name: Configure endpoint discovery
include_tasks: endpoint-discovery-config.yaml
vars:
cluster: "{{ outer_item }}"
with_items: "{{ clusters }}"
loop_control:
loop_var: outer_item
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

- name: Download the Google Cloud SDK package repository signing key
get_url:
url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
dest: /usr/share/keyrings/cloud.google.gpg

- name: Add Google Cloud SDK package repository source
apt_repository:
filename: google-cloud-sdk.list
repo: "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main"
state: present
update_cache: yes

- name: Install dependencies
apt:
pkg:
- kubectl
- google-cloud-sdk-gke-gcloud-auth-plugin
- jq
state: present

- name: Install gke-gcloud-auth-plugin
apt:
name: google-cloud-sdk-gke-gcloud-auth-plugin
state: present
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

- name: Deploy test app
include_tasks: test.yaml
vars:
cluster: "{{ item }}"
with_items: "{{ clusters }}"
loop_control:
index_var: index

- name: Test
shell: >
for i in $(seq 400); do
kubectl exec \
--context="gke_{{ project_id }}_{{ region }}_{{ item }}" \
-n sample \
-c sleep "$(kubectl get pod \
--context="gke_{{ project_id }}_{{ region }}_{{ item }}" \
-n sample \
-l app=sleep \
-o jsonpath='{.items[0].metadata.name}')" \
-- curl -sS helloworld.sample:5000/hello
done
with_items: "{{ clusters }}"
Loading

0 comments on commit c9735bc

Please sign in to comment.