Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of a multi-cluster mesh on GKE configuring managed control pl… #771

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
apichick marked this conversation as resolved.
Show resolved Hide resolved

# 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