This is a guide on how to get started with CAPV (Cluster API Provider vSphere). To learn more about cluster API in more depth, check out the the cluster api docs page.
clusterctl
is a command line tool used for bootstrapping your Management Cluster. Your management cluster stores resources such as clusters
and machines
using the Kubernetes API. This is the cluster you use to provision and manage multiple clusters going forward. Before diving into the bootstrap process, it's worth noting that a Management Cluster is just another Kubernetes cluster with special addons and CRDs. You are not required to use clusterctl
to provision your management cluster, however, this guide will be focused on using clusterctl
to bootstrap your management cluster using Kind.
Please download clusterctl
from the Cluster API (CAPI), GitHub releases page.
Note: CAPI v1alpha2 may not yet have published a clusterctl
binary. Docker may be used to build clusterctl
from source using the master
branch of the CAPI repository:
docker run --rm \
-v "$(pwd)":/out \
-e CGO_ENABLED=0 \
-e GOOS="$(uname -s | tr '[:upper:]' '[:lower:]')" \
-e GOARCH=amd64 \
-e GOFLAGS="-ldflags=-extldflags=-static" \
-e GOPROXY="https://proxy.golang.org" \
-w /build \
golang:1.12 \
/bin/bash -c 'git clone https://github.com/kubernetes-sigs/cluster-api.git . && \
go build -o /out/clusterctl.${GOOS}_${GOARCH} ./cmd/clusterctl'
Docker is required for the bootstrap cluster using clusterctl
. See the docker documentation for install instructions.
clusterctl
uses Kind to provision the bootstrap cluster. Please see the kind documentation for install instructions.
kubectl
is required to use clusterctl
. See Install and Set Up kubectl for install instructions.
In order for clusterctl
to bootstrap a management cluster on vSphere, it must be able to connect and authenticate to vCenter. Ensure you have credentials to your vCenter server (user, password and server URL).
It is required that machines provisioned by CAPV use one of the official CAPV machine images as a VM template. The machine images are retrievable from public URLs. CAPV currently supports machine images based on Ubuntu 18.04 and CentOS 7. A list of published machine images is available here. For this guide we'll be deploying Kubernetes v1.15.3 on Ubuntu 18.04 (link to machine image).
Create a VM template using the OVA URL above. The rest of the guide will assume you named the VM template ubuntu-1804-kube-v1.15.3
.
The bootstrapping process in clusterctl
requires a few manifest files:
Name | Description |
---|---|
cluster.yaml |
The cluster resource for the target cluster |
controlplane.yaml |
The machine resource for target cluster's control plane nodes |
machinedeployment.yaml |
The machine resource for target cluster's worker nodes |
provider-components.yaml |
The CAPI and CAPV reources for the target cluster |
addons.yaml |
Additional add-ons to apply to the management cluster (ex. CNI) |
Before attempting to generate the above manifests, the following environment variables should be set based on your vSphere environment:
$ cat <<EOF >envvars.txt
# vCenter config/credentials
export VSPHERE_SERVER='10.0.0.1' # (required) The vCenter server IP or FQDN
export VSPHERE_USERNAME='[email protected]' # (required) The username used to access the remote vSphere endpoint
export VSPHERE_PASSWORD='some-secure-password' # (required) The password used to access the remote vSphere endpoint
# vSphere deployment configs
export VSPHERE_DATACENTER='SDDC-Datacenter' # (required) The vSphere datacenter to deploy the management cluster on
export VSPHERE_DATASTORE='DefaultDatastore' # (required) The vSphere datastore to deploy the management cluster on
export VSPHERE_NETWORK='vm-network-1' # (required) The VM network to deploy the management cluster on
export VSPHERE_RESOURCE_POOL='*/Resources' # (required) The vSphere resource pool for your VMs
export VSPHERE_FOLDER='vm' # (optional) The VM folder for your VMs, defaults to the root vSphere folder if not set.
export VSPHERE_TEMPLATE='ubuntu-1804-kube-v1.15.3' # (required) The VM template to use for your management cluster.
export VSPHERE_DISK_GIB='50' # (optional) The VM Disk size in GB, defaults to 20 if not set
export VSPHERE_NUM_CPUS='2' # (optional) The # of CPUs for control plane nodes in your management cluster, defaults to 2 if not set
export VSPHERE_MEM_MIB='2048' # (optional) The memory (in MiB) for control plane nodes in your management cluster, defaults to 2048 if not set
export SSH_AUTHORIZED_KEY='ssh-rsa AAAAB3N...' # (optional) The public ssh authorized key on all machines in this cluster
# Kubernetes configs
export KUBERNETES_VERSION='1.15.3' # (optional) The Kubernetes version to use, defaults to 1.15.3
export SERVICE_CIDR='100.64.0.0/13' # (optional) The service CIDR of the management cluster, defaults to "100.64.0.0/13"
export CLUSTER_CIDR='100.96.0.0/11' # (optional) The cluster CIDR of the management cluster, defaults to "100.96.0.0/11"
export SERVICE_DOMAIN='cluster.local' # (optional) The k8s service domain of the management cluster, defaults to "cluster.local"
EOF
With the above environment variable file it is now possible to generate the manifests needed to bootstrap the management cluster. The following command uses Docker to run an image that has all of the necessary templates and tools to generate the YAML manifests. Additionally, the envvars.txt
file created above is mounted inside the the image in order to provide the generation routine with its default values:
$ docker run --rm \
-v "$(pwd)":/out \
-v "$(pwd)/envvars.txt":/envvars.txt:ro \
gcr.io/cluster-api-provider-vsphere/release/manifests:latest \
-c management-cluster
Generated ./out/management-cluster/cluster.yaml
Generated ./out/management-cluster/controlplane.yaml
Generated ./out/management-cluster/machinedeployment.yaml
Generated /build/examples/provider-components/provider-components-cluster-api.yaml
Generated /build/examples/provider-components/provider-components-kubeadm.yaml
Generated /build/examples/provider-components/provider-components-vsphere.yaml
Generated ./out/management-cluster/provider-components.yaml
WARNING: ./out/management-cluster/provider-components.yaml includes vSphere credentials
If you are on vSphere < 6.7u3, set the -u flag in the manifests image:
$ docker run --rm \
-v "$(pwd)":/out \
-v "$(pwd)/envvars.txt":/envvars.txt:ro \
gcr.io/cluster-api-provider-vsphere/release/manifests:latest \
-c management-cluster -u
Once the manifests are generated, clusterctl
may be used to create the management cluster:
clusterctl create cluster \
--bootstrap-type kind \
--bootstrap-flags name=management-cluster \
--cluster ./out/management-cluster/cluster.yaml \
--machines ./out/management-cluster/controlplane.yaml \
--provider-components ./out/management-cluster/provider-components.yaml \
--addon-components ./out/management-cluster/addons.yaml \
--kubeconfig-out ./out/management-cluster/kubeconfig
Once clusterctl
has completed successfully, the file ./out/management-cluster/kubeconfig
may be used to access the new management cluster. This is the admin kubeconfig
for the management cluster, and it may be used to spin up additional clusters with Cluster API. However, the creation of roles with limited access, is recommended before creating additional clusters.
NOTE: From this point forward clusterctl
is no longer required to provision new clusters. Workload clusters should be provisioned by applying Cluster API resources directly on the management cluster using kubectl
.
With your management cluster bootstrapped, it's time to reap the benefits of Cluster API. From this point forward, clusters and machines (belonging to a cluster) are simply provisioned by creating Cluster
, Machine
and MachineDeployment
, and KubeadmConfig
resources.
Using the same Docker command as above, generate resources for a new cluster, this time with a different name:
$ docker run --rm \
-v "$(pwd)":/out \
-v "$(pwd)/envvars.txt":/envvars.txt:ro \
gcr.io/cluster-api-provider-vsphere/release/manifests:latest \
-c workload-cluster-1
NOTE: The above step is not required to manage your Cluster API resources at this point but is used to simplify this guide. You should manage your Cluster API resources in the same way you would manage your Kubernetes application manifests. Please use the generated manifests only as a reference.
The Cluster and Machine resource in ./out/workload-cluster-1/cluster.yaml
and ./out/workload-cluster-1/controlplane.yaml
defines the workload cluster with the initial control plane node:
./out/workload-cluster-1/cluster.yaml
apiVersion: cluster.x-k8s.io/v1alpha2
kind: Cluster
metadata:
name: workload-cluster-1
namespace: default
spec:
clusterNetwork:
pods:
cidrBlocks:
- 100.96.0.0/11
services:
cidrBlocks:
- 100.64.0.0/13
serviceDomain: cluster.local
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: VSphereCluster
name: workload-cluster-1
namespace: default
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: VSphereCluster
metadata:
name: workload-cluster-1
namespace: default
spec:
cloudProviderConfiguration:
global:
secretName: cloud-provider-vsphere-credentials
secretNamespace: kube-system
network:
name: vm-network-1
providerConfig:
cloud:
controllerImage: gcr.io/cloud-provider-vsphere/cpi/release/manager:v1.0.0
storage:
attacherImage: quay.io/k8scsi/csi-attacher:v1.1.1
controllerImage: vmware/vsphere-block-csi-driver:v1.0.0
metadataSyncerImage: vmware/volume-metadata-syncer:v1.0.0
nodeDriverImage: vmware/vsphere-block-csi-driver:v1.0.0
provisionerImage: quay.io/k8scsi/csi-provisioner:v1.2.1
virtualCenter:
10.0.0.1:
datacenters: SDDC-Datacenter
workspace:
datacenter: SDDC-Datacenter
datastore: DefaultDatastore
folder: vm
resourcePool: '*/Resources'
server: 10.0.0.1
server: 10.0.0.1
./out/workload-cluster-1/controlplane.yaml
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2
kind: KubeadmConfig
metadata:
name: workload-cluster-1-controlplane-0
namespace: default
spec:
clusterConfiguration:
apiServer:
extraArgs:
cloud-provider: external
controllerManager:
extraArgs:
cloud-provider: external
imageRepository: k8s.gcr.io
initConfiguration:
nodeRegistration:
criSocket: /var/run/containerd/containerd.sock
kubeletExtraArgs:
cloud-provider: external
name: '{{ ds.meta_data.hostname }}'
preKubeadmCommands:
- hostname "{{ ds.meta_data.hostname }}"
- echo "::1 ipv6-localhost ipv6-loopback" >/etc/hosts
- echo "127.0.0.1 localhost {{ ds.meta_data.hostname }}" >>/etc/hosts
- echo "{{ ds.meta_data.hostname }}" >/etc/hostname
users:
- name: capv
sshAuthorizedKeys:
- "The public side of an SSH key pair."
sudo: ALL=(ALL) NOPASSWD:ALL
---
apiVersion: cluster.x-k8s.io/v1alpha2
kind: Machine
metadata:
labels:
cluster.x-k8s.io/cluster-name: workload-cluster-1
cluster.x-k8s.io/control-plane: "true"
name: workload-cluster-1-controlplane-0
namespace: default
spec:
bootstrap:
configRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2
kind: KubeadmConfig
name: workload-cluster-1-controlplane-0
namespace: default
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: VSphereMachine
name: workload-cluster-1-controlplane-0
namespace: default
version: 1.15.3
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: VSphereMachine
metadata:
labels:
cluster.x-k8s.io/cluster-name: workload-cluster-1
cluster.x-k8s.io/control-plane: "true"
name: workload-cluster-1-controlplane-0
namespace: default
spec:
datacenter: SDDC-Datacenter
diskGiB: 50
memoryMiB: 2048
network:
devices:
- dhcp4: true
dhcp6: false
networkName: vm-network-1
numCPUs: 2
template: ubuntu-1804-kube-v1.15.3
To add an additional worker node to your cluster, please see the generated machineset file ./out/workload-cluster-1/machinedeployment.yaml
:
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2
kind: KubeadmConfigTemplate
metadata:
name: workload-cluster-1-md-0
namespace: default
spec:
template:
spec:
joinConfiguration:
nodeRegistration:
criSocket: /var/run/containerd/containerd.sock
kubeletExtraArgs:
cloud-provider: external
name: '{{ ds.meta_data.hostname }}'
preKubeadmCommands:
- hostname "{{ ds.meta_data.hostname }}"
- echo "::1 ipv6-localhost ipv6-loopback" >/etc/hosts
- echo "127.0.0.1 localhost {{ ds.meta_data.hostname }}" >>/etc/hosts
- echo "{{ ds.meta_data.hostname }}" >/etc/hostname
users:
- name: capv
sshAuthorizedKeys:
- "The public side of an SSH key pair."
sudo: ALL=(ALL) NOPASSWD:ALL
---
apiVersion: cluster.x-k8s.io/v1alpha2
kind: MachineDeployment
metadata:
labels:
cluster.x-k8s.io/cluster-name: workload-cluster-1
name: workload-cluster-1-md-0
namespace: default
spec:
replicas: 1
selector:
matchLabels:
cluster.x-k8s.io/cluster-name: workload-cluster-1
template:
metadata:
labels:
cluster.x-k8s.io/cluster-name: workload-cluster-1
spec:
bootstrap:
configRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2
kind: KubeadmConfigTemplate
name: workload-cluster-1-md-0
namespace: default
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: VSphereMachineTemplate
name: workload-cluster-1-md-0
namespace: default
version: 1.15.3
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: VSphereMachineTemplate
metadata:
name: workload-cluster-1-md-0
namespace: default
spec:
template:
spec:
datacenter: SDDC-Datacenter
diskGiB: 50
memoryMiB: 2048
network:
devices:
- dhcp4: true
networkName: vm-network-1
numCPUs: 2
template: ubuntu-1804-kube-v1.15.3
Use kubectl
with the kubeconfig
for the management cluster to provision the new workload cluster:
-
Export the management cluster's
kubeconfig
file:export KUBECONFIG="$(pwd)/out/management-cluster/kubeconfig"
-
Create the workload cluster by applying the cluster manifest:
kubectl apply -f ./out/workload-cluster-1/cluster.yaml
-
Create the control plane nodes for the workload cluster by applying the machines manifest:
kubectl apply -f ./out/workload-cluster-1/controlplane.yaml
-
Create the worker nodes for the workload cluster by applying the machineset manifest:
kubectl apply -f ./out/workload-cluster-1/machinedeployment.yaml
Clusters that are provisioned by the management cluster that run your application workloads are called Workload Clusters.
The kubeconfig
file to access workload clusters should be accessible as a Kubernetes Secret on the management cluster. As of today, the Secret resource is named <cluster-name>-kubeconfig
in the same namespace as the cluster to which the Secret belongs. For the example above, you can list all the kubeconfig files and then retrive the corresponding kubeconfig like so:
-
Retrieve a list of the secrets stored in the management cluster:
$ kubectl get secrets NAME TYPE DATA AGE default-token-zs9tb kubernetes.io/service-account-token 3 13m management-cluster-ca Opaque 2 15m management-cluster-etcd Opaque 2 15m management-cluster-kubeconfig Opaque 1 15m management-cluster-proxy Opaque 2 15m management-cluster-sa Opaque 2 15m workload-cluster-1-ca Opaque 2 7m workload-cluster-1-etcd Opaque 2 7m workload-cluster-1-kubeconfig Opaque 1 7m workload-cluster-1-proxy Opaque 2 7m workload-cluster-1-sa Opaque 2 7m
-
Get the KubeConfig secret for the
workload-cluster-1
cluster and decode it from base64:kubectl get secret workload-cluster-1-kubeconfig -o=jsonpath='{.data.value}' | \ { base64 -d 2>/dev/null || base64 -D; } >./out/workload-cluster-1/kubeconfig
The new ./out/workload-cluster-1/kubeconfig
file may now be used to access the workload cluster.
NOTE: Workload clusters do not have any addons applied aside from those added by kubeadm. Nodes in your workload clusters will be in the NotReady
state until you apply a CNI addon. The addons.yaml
files generated above have a default Calico addon which you can use, otherwise apply custom addons based on your use-case.