diff --git a/dev-docs/miniconstellation/README.md b/dev-docs/miniconstellation/README.md new file mode 100644 index 000000000..6d79231ef --- /dev/null +++ b/dev-docs/miniconstellation/README.md @@ -0,0 +1,90 @@ +# Quick setup for a mini cluster running the cloud + +### Prerequisites + +* [Install Terraform](https://developer.hashicorp.com/terraform/downloads) +* [Install the Azure CLI](https://learn.microsoft.com/de-de/cli/azure/install-azure-cli) + to authenticate with Azure: + + ```sh + az login + ``` + +### Instructions + +Through the Terraform template for Azure it's easy to set up a MiniConstellation cluster on a remote VM. + +1. Clone the Constellation repository: + + ```sh + git clone https://github.com/edgelesssys/constellation.git + ``` + +2. Set up the remote Azure VM through Terraform: + + By default, the [`Standard_D8s_v5`](https://learn.microsoft.com/de-de/azure/virtual-machines/dv5-dsv5-series) machine type is selected which supports nested virtualization, but you can also set another supported machine type in Terraform (`machine_type`) by referring to the [Azure docs](https://azure.microsoft.com/en-us/blog/nested-virtualization-in-azure/). + Then run: + + ```sh + cd constellation/dev-docs/miniconstellation/azure-terraform + ./create-vm.sh + ``` + + After execution, you should be connected with the remote machine through SSH. + If you accidentally lose connection, you can reconnect via + + ```sh + ssh -i id_rsa adminuser@$INSERT_VM_IP_ADDRESS + ``` + +3. Prepare the VM for `constellation mini up` + + Once logged into the machine, install the Constellation CLI: + + ```sh + echo "Installing Constellation CLI" + curl -LO + sudo install constellation-linux-amd64 /usr/local/bin/constellation + ``` + + and start the Docker service and make sure that it's running: + + ```sh + sudo systemctl start docker.service && sudo systemctl enable docker.service + # verify that it is active + systemctl is-active docker + ``` + + At last, create the Constellation cluster in a workspace directory: + + ```sh + mkdir constellation_workspace && cd constellation_workspace + constellation mini up + ``` + + The cluster creation takes about 15 minutes. + + For convenience, there is a script that does these steps automatically: + + ```sh + ./setup-miniconstellation.sh + ``` + +4. Verify the Kubernetes cluster + + Running: + + ```sh + export KUBECONFIG="$PWD/constellation-admin.conf" + kubectl get nodes + ``` + + should show both one control-plane and one worker node. + +5. Clean up cloud resources + + Exit the SSH connection (Ctrl+D) and run: + + ```sh + terraform destroy + ``` diff --git a/dev-docs/miniconstellation/azure-terraform/.terraform.lock.hcl b/dev-docs/miniconstellation/azure-terraform/.terraform.lock.hcl new file mode 100644 index 000000000..95f5154fc --- /dev/null +++ b/dev-docs/miniconstellation/azure-terraform/.terraform.lock.hcl @@ -0,0 +1,110 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.56.0" + constraints = "3.56.0" + hashes = [ + "h1:+18eN9hEeT7/gW6MFr1PKwSPn+2mPtjg/vuYUs2MCfA=", + "h1:9A2lbngKlnre83HlU1JuVWRJ3BEs1alE+nOvoE17w2Q=", + "h1:EWQAGzU2JkpzK87W4cZx3xR4NAFW8Kwp79j74F5bWww=", + "h1:H7P+Xxhp/qEfwkYdFZrckUKHXJa+K8507U2CrD4dJo4=", + "h1:LEcmSqmNoexpH6NuglCS8dwLyZctQ6YAee2B9FL/9Cc=", + "h1:X4oTXB0Nv+FZ61FMKSObEwi+OSVyKYT+gNLTQilwq3g=", + "h1:j262GXstvnzNNRgVQ5ednS700xOjcMaUIvvUaMVD4Qk=", + "h1:opIai9BbFwYAGmGhpMqvOKrqRLWscuIM3YI8Gt4ZPMw=", + "h1:qoV9BwWKSQ2znS9w9o8XTfUKSawX3tZlKRy36AkyNwg=", + "h1:yqO/Wvcqzt1bkeDE7ShsUBYPOllBDrOSmS9YFFiKAxc=", + "h1:zTOZj1TfO0zv7/BtiZW3B4hZWJvDsduUQas9t/NcZ8Q=", + "zh:0c35cf5c57edc337cc8a63399b605f1ec3316841869098f6b5b1f40f76f03d04", + "zh:230b82b9ef64983505920a66e655f8ca807fccf6e0e2ccbdb5ede301871270b2", + "zh:3d1ef558437be0853dc183c9926e6da6d85089dc653ac53bc1c54262b00287e5", + "zh:5f240a33ba87b1a30790cebae50a1234ed864953c8324d3e7a03aca3cfbec9e7", + "zh:67954796a1f22b28172817f613f071981f46b4cf162d549794e96ac3dbf16303", + "zh:6e8422b39f4dba9d9e294bd8c6772e6b89707b814a022898b83b7d5d4eaba914", + "zh:8b595ad75123b88b67b2b32a9c3e942da34c13e051a7858b5f4549429aa81142", + "zh:bffb8df9cf7f6336510f2d7a497c89d9e2c35c113a09915b6422a7fd332dca33", + "zh:d172642393754112c8d2c15e500b37b62b29917b376e93c0a519046d30603201", + "zh:eac016f21218fae0ff4f3121b944316dc367fc180e875a402edbaae298c8108a", + "zh:eee76e35fe8786045f565e0ca20bb8c8b0cf46c14b7f7d84f0a1d45cde3257e5", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/cloudinit" { + version = "2.3.2" + constraints = "2.3.2" + hashes = [ + "h1:2jb+BfT5T96dXxUD2LQ6MtVHpXErd7ZybmMvdWE2jd4=", + "h1:Ar/DAbZQ9Nsj0BrqX6camrEE6U+Yq4E87DCNVqxqx8k=", + "h1:Vl0aixAYTV/bjathX7VArC5TVNkxBCsi3Vq7R4z1uvc=", + "h1:ocyv0lvfyvzW4krenxV5CL4Jq5DiA3EUfoy8DR6zFMw=", + "h1:y+6FsU2STOpx6L6JOon4DVZoZPQgNoR2xR2WQ/EVxcQ=", + "zh:2487e498736ed90f53de8f66fe2b8c05665b9f8ff1506f751c5ee227c7f457d1", + "zh:3d8627d142942336cf65eea6eb6403692f47e9072ff3fa11c3f774a3b93130b3", + "zh:434b643054aeafb5df28d5529b72acc20c6f5ded24decad73b98657af2b53f4f", + "zh:436aa6c2b07d82aa6a9dd746a3e3a627f72787c27c80552ceda6dc52d01f4b6f", + "zh:458274c5aabe65ef4dbd61d43ce759287788e35a2da004e796373f88edcaa422", + "zh:54bc70fa6fb7da33292ae4d9ceef5398d637c7373e729ed4fce59bd7b8d67372", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:893ba267e18749c1a956b69be569f0d7bc043a49c3a0eb4d0d09a8e8b2ca3136", + "zh:95493b7517bce116f75cdd4c63b7c82a9d0d48ec2ef2f5eb836d262ef96d0aa7", + "zh:9ae21ab393be52e3e84e5cce0ef20e690d21f6c10ade7d9d9d22b39851bfeddc", + "zh:cc3b01ac2472e6d59358d54d5e4945032efbc8008739a6d4946ca1b621a16040", + "zh:f23bfe9758f06a1ec10ea3a81c9deedf3a7b42963568997d84a5153f35c5839a", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.5.1" + constraints = "3.5.1" + hashes = [ + "h1:0ULxM8/DscMzfiDWg1yclBf/39U44wQmlx745BfYZ80=", + "h1:3hjTP5tQBspPcFAJlfafnWrNrKnr7J4Cp0qB9jbqf30=", + "h1:6FVyQ/aG6tawPam6B+oFjgdidKd83uG9n7dOSQ66HBA=", + "h1:6ePAACdONiMGe1j5pwUc0gpDFt82y/ka0zRimMg/geM=", + "h1:BD3Y4CcrGHb9sx+Bl5V8M2PSyw23mykzXSwj+/6FhHA=", + "h1:HGeb7Tajn7HZwX0MhrdyL57LoCSz5GMcI2wbHs12D4U=", + "h1:IL9mSatmwov+e0+++YX2V6uel+dV6bn+fC/cnGDK3Ck=", + "h1:JiENkIxSWc32/2Dtd1n4CWY3ow/PHvAeGhdgcOLpWZM=", + "h1:MROYZuKGTuaTNf2FgbwCgSVpteQW25ubnb+Xfok2jvk=", + "h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=", + "h1:sZ7MTSD4FLekNN2wSNFGpM+5slfvpm5A/NLVZiB7CO0=", + "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", + "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", + "zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831", + "zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3", + "zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b", + "zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2", + "zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865", + "zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03", + "zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602", + "zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014", + ] +} + +provider "registry.terraform.io/hashicorp/tls" { + version = "4.0.4" + constraints = "4.0.4" + hashes = [ + "h1:GZcFizg5ZT2VrpwvxGBHQ/hO9r6g0vYdQqx3bFD3anY=", + "h1:Wd3RqmQW60k2QWPN4sK5CtjGuO1d+CRNXgC+D4rKtXc=", + "h1:bNsvpX5EGuVxgGRXBQVLXlmq40PdoLp8Rfuh1ZmV7yY=", + "h1:pe9vq86dZZKCm+8k1RhzARwENslF3SXb9ErHbQfgjXU=", + "h1:rKKMyIEBZwR+8j6Tx3PwqBrStuH+J+pxcbCR5XN8WAw=", + "zh:23671ed83e1fcf79745534841e10291bbf34046b27d6e68a5d0aab77206f4a55", + "zh:45292421211ffd9e8e3eb3655677700e3c5047f71d8f7650d2ce30242335f848", + "zh:59fedb519f4433c0fdb1d58b27c210b27415fddd0cd73c5312530b4309c088be", + "zh:5a8eec2409a9ff7cd0758a9d818c74bcba92a240e6c5e54b99df68fff312bbd5", + "zh:5e6a4b39f3171f53292ab88058a59e64825f2b842760a4869e64dc1dc093d1fe", + "zh:810547d0bf9311d21c81cc306126d3547e7bd3f194fc295836acf164b9f8424e", + "zh:824a5f3617624243bed0259d7dd37d76017097dc3193dac669be342b90b2ab48", + "zh:9361ccc7048be5dcbc2fafe2d8216939765b3160bd52734f7a9fd917a39ecbd8", + "zh:aa02ea625aaf672e649296bce7580f62d724268189fe9ad7c1b36bb0fa12fa60", + "zh:c71b4cd40d6ec7815dfeefd57d88bc592c0c42f5e5858dcc88245d371b4b8b1e", + "zh:dabcd52f36b43d250a3d71ad7abfa07b5622c69068d989e60b79b2bb4f220316", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/dev-docs/miniconstellation/azure-terraform/cloud-init.yaml b/dev-docs/miniconstellation/azure-terraform/cloud-init.yaml new file mode 100644 index 000000000..74fdd5784 --- /dev/null +++ b/dev-docs/miniconstellation/azure-terraform/cloud-init.yaml @@ -0,0 +1,33 @@ +#cloud-config + +users: + - default + - name: adminuser + groups: docker + sudo: ALL=(ALL) NOPASSWD:ALL + homedir: /home/adminuser + +groups: + - docker + +package_update: true +packages: + - cryptsetup + - build-essential + - libguestfs-tools + - apt-transport-https + - ca-certificates + - curl + - lsb-release + - xsltproc + - libvirt-clients + - libvirt-daemon + - libvirt-daemon-system + +runcmd: + - [/bin/bash, -c, "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg"] + - [/bin/bash, -c, "echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" | tee /etc/apt/sources.list.d/docker.list > /dev/null "] + - [apt-get, update] + - [apt-get, install, -y, docker-ce, docker-ce-cli, containerd.io, libssl-dev, pigz] + - [/bin/bash, -c, "systemctl enable docker.service && systemctl start docker.service"] + - [/bin/bash, -c, "curl -fsSLO \"https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\" && install kubectl /usr/local/bin/kubectl"] diff --git a/dev-docs/miniconstellation/azure-terraform/create-vm.sh b/dev-docs/miniconstellation/azure-terraform/create-vm.sh new file mode 100755 index 000000000..b64cd14c2 --- /dev/null +++ b/dev-docs/miniconstellation/azure-terraform/create-vm.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +echo "create Terraform resources" + +terraform init +terraform apply -auto-approve +terraform output -raw ssh_private_key > id_rsa +chmod 600 id_rsa + +azure_vm_ip=$(terraform output -raw public_ip) + +echo "::endgroup::" + +echo "Waiting for SSH server to come online..." + +# Wait for SSH to come online, at most 10*30s=5min +count=0 +until ssh -i id_rsa -o StrictHostKeyChecking=no adminuser@"${azure_vm_ip}" date || [[ ${count} -eq 10 ]]; do + sleep 30 + count=$((count + 1)) +done + +echo "Done waiting." + +echo "Copy prep VM script to remote VM" +scp -i id_rsa ../setup-miniconstellation.sh adminuser@"${azure_vm_ip}":~/setup-miniconstellation.sh + +echo "Logging into remote VM" +ssh -i id_rsa adminuser@"${azure_vm_ip}" diff --git a/dev-docs/miniconstellation/azure-terraform/main.tf b/dev-docs/miniconstellation/azure-terraform/main.tf new file mode 100644 index 000000000..9069117b8 --- /dev/null +++ b/dev-docs/miniconstellation/azure-terraform/main.tf @@ -0,0 +1,148 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "3.56.0" + } + random = { + source = "hashicorp/random" + version = "3.5.1" + } + tls = { + source = "hashicorp/tls" + version = "4.0.4" + } + cloudinit = { + source = "hashicorp/cloudinit" + version = "2.3.2" + } + } +} + +provider "azurerm" { + use_oidc = true + features {} +} + +provider "tls" {} + +resource "random_string" "suffix" { + length = 6 + special = false +} + +resource "tls_private_key" "ssh_key" { + algorithm = "RSA" + rsa_bits = 2048 +} + +data "cloudinit_config" "cloud_init" { + base64_encode = true + part { + filename = "cloud-init.yaml" + content_type = "text/cloud-config" + + content = file("${path.module}/cloud-init.yaml") + } +} + +resource "azurerm_resource_group" "main" { + name = var.resource_group + location = var.location +} + +resource "azurerm_virtual_network" "main" { + name = "mini-${random_string.suffix.result}" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name +} + +resource "azurerm_subnet" "main" { + name = "mini-${random_string.suffix.result}" + resource_group_name = azurerm_resource_group.main.name + virtual_network_name = azurerm_virtual_network.main.name + address_prefixes = ["10.0.2.0/24"] +} + +resource "azurerm_public_ip" "main" { + name = "mini-${random_string.suffix.result}" + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_network_interface" "main" { + name = "mini-${random_string.suffix.result}" + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + + ip_configuration { + name = "main" + subnet_id = azurerm_subnet.main.id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.main.id + } +} + +resource "azurerm_network_security_group" "ssh" { + name = "mini-${random_string.suffix.result}" + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + + security_rule { + name = "ssh" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "22" + source_address_prefix = "*" + destination_address_prefix = "*" + } +} + +resource "azurerm_subnet_network_security_group_association" "ssh" { + subnet_id = azurerm_subnet.main.id + network_security_group_id = azurerm_network_security_group.ssh.id +} + +resource "azurerm_linux_virtual_machine" "main" { + name = "mini-${random_string.suffix.result}" + resource_group_name = azurerm_resource_group.main.name + location = azurerm_resource_group.main.location + + # Standard_D8s_v5 provides nested virtualization support + size = var.machine_type + + admin_username = "adminuser" + + admin_ssh_key { + username = "adminuser" + public_key = tls_private_key.ssh_key.public_key_openssh + } + + boot_diagnostics { + + } + + network_interface_ids = [ + azurerm_network_interface.main.id, + ] + + source_image_reference { + publisher = "Canonical" + offer = "0001-com-ubuntu-server-jammy-daily" + sku = "22_04-daily-lts" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + user_data = data.cloudinit_config.cloud_init.rendered +} diff --git a/dev-docs/miniconstellation/azure-terraform/output.tf b/dev-docs/miniconstellation/azure-terraform/output.tf new file mode 100644 index 000000000..14f541264 --- /dev/null +++ b/dev-docs/miniconstellation/azure-terraform/output.tf @@ -0,0 +1,11 @@ +output "public_ip" { + value = azurerm_public_ip.main.ip_address + sensitive = false + depends_on = [azurerm_public_ip.main] +} + +output "ssh_private_key" { + value = tls_private_key.ssh_key.private_key_openssh + sensitive = true + depends_on = [tls_private_key.ssh_key] +} diff --git a/dev-docs/miniconstellation/azure-terraform/variables.tf b/dev-docs/miniconstellation/azure-terraform/variables.tf new file mode 100644 index 000000000..357db5ef9 --- /dev/null +++ b/dev-docs/miniconstellation/azure-terraform/variables.tf @@ -0,0 +1,15 @@ +variable "resource_group" { + type = string + description = "Name of the new resource group for the Miniconstellation cluster" +} + +variable "location" { + type = string + description = "The Azure region to create the cluster in (westus|eastus|northeurope|westeurope)" +} + +variable "machine_type" { + type = string + description = "The Azure VM type" + default = "Standard_D8s_v5" +} diff --git a/dev-docs/miniconstellation/setup-miniconstellation.sh b/dev-docs/miniconstellation/setup-miniconstellation.sh new file mode 100755 index 000000000..588b1d312 --- /dev/null +++ b/dev-docs/miniconstellation/setup-miniconstellation.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +echo "Installing Constellation CLI" +curl -LO https://github.com/edgelesssys/constellation/releases/latest/download/constellation-linux-amd64 +sudo install constellation-linux-amd64 /usr/local/bin/constellation + +# Start docker service and auto start on boot +# TODO should be done in cloud-init but was not done in my test case +sudo systemctl start docker.service && sudo systemctl enable docker.service +echo "Waiting for docker service to be active..." +# Wait at most 20min +count=0 +until systemctl is-active docker || [[ ${count} -eq 120 ]]; do + sleep 10 + count=$((count + 1)) +done +if [[ ${count} -eq 120 ]]; then + echo "Docker service did not come up in time." + exit 1 +fi + +# change to workspace +mkdir constellation_workspace +cd constellation_workspace || exit + +# takes around 15 minutes +constellation mini up +echo "Cluster creation done." diff --git a/docs/docs/getting-started/first-steps-local.md b/docs/docs/getting-started/first-steps-local.md index c6d040051..e7aa6f6b6 100644 --- a/docs/docs/getting-started/first-steps-local.md +++ b/docs/docs/getting-started/first-steps-local.md @@ -2,6 +2,7 @@ A local cluster lets you deploy and test Constellation without a cloud subscription. You have two options: + * Use MiniConstellation to automatically deploy a two-node cluster. * For more fine-grained control, create the cluster using the QEMU provider. @@ -9,24 +10,39 @@ Both options use virtualization to create a local cluster with control-plane nod ## Prerequisites -* A Linux OS with the following components installed - * [Constellation CLI](./install.md#install-the-constellation-cli) - * [KVM kernel module](https://www.linux-kvm.org/page/Main_Page) - * [Docker](https://docs.docker.com/engine/install/) - * [xsltproc](https://gitlab.gnome.org/GNOME/libxslt/-/wikis/home) - * (Optional) [virsh](https://www.libvirt.org/manpages/virsh.html) to observe and access your nodes -* Other system requirements +* machine requirements: * An x86-64 CPU with at least 4 cores (6 cores are recommended) * At least 4 GB RAM (6 GB are recommended) * 20 GB of free disk space - * Hardware virtualization enabled in the BIOS/UEFI (often referred to as Intel VT-x or AMD-V/SVM) - * `iptables` rules configured to not drop forwarded packages. - If running the following command returns no error, please follow [the troubleshooting guide](#vms-have-no-internet-access): + * Hardware virtualization enabled in the BIOS/UEFI (often referred to as Intel VT-x or AMD-V/SVM) / nested-virtualization support when using a VM +* OS / library requirements: + * recommended: Ubuntu 22.04 LTS + * otherwise: + * [KVM kernel module](https://www.linux-kvm.org/page/Main_Page) + * [xsltproc](https://gitlab.gnome.org/GNOME/libxslt/-/wikis/home) + * (Optional) [virsh](https://www.libvirt.org/manpages/virsh.html) to observe and access your nodes - ```bash +* software requirements: + * install requirements + + ```sh + # install Docker + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io libssl-dev pigz + systemctl enable docker.service && systemctl start docker.service + # install kubectl + curl -fsSLO "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && install kubectl /usr/local/bin/kubectl + # install Constellation CLI + curl -LO + sudo install constellation-linux-amd64 /usr/local/bin/constellation + # do not drop forwarded packages sudo iptables -S | grep -q -- '-P FORWARD DROP' ``` + If running the following the `iptables` command returns no error, please follow [the troubleshooting guide](#vms-have-no-internet-access). + ## Create a cluster