Self-managed Kubernetes in Google Compute Engine (GCE)


Big picture

Use Calico with a self-managed Kubernetes cluster in Google Compute Engine (GCE).


Managing your own Kubernetes cluster (as opposed to using a managed-Kubernetes service like GKE) gives you the most flexibility in configuring Calico and Kubernetes. Calico combines flexible networking capabilities with “run-anywhere” security enforcement to provide a solution with native Linux kernel performance and true cloud-native scalability.


kubeadm is a cluster management tool that is used to install Kubernetes.

Before you begin…

Install and configure the Google Cloud CLI tools

How to

There are many ways to install and manage Kubernetes in GCE. Using kubeadm is a good default choice for most people, as it gives you access to all of Calico’s flexible and powerful networking features. However, there are other options that may work better for your environment.

kubeadm for Calico networking and network policy

Create cloud resources

You will need at least one VM to serve as a control plane node and one or more worker nodes. (It is possible to have control plane nodes also act as workers. This is not recommended in most cases and not covered by this guide.) See requirements for specific OS requirements for these VMs.

The following worked example creates a single control node and three workers on a dedicated virtual private network (VPC). Adjust the example as needed for your requirements. Consider a dedicated infrastructure management tool like Terraform for managing cloud resources. (This example is adapted from Kubernetes the Hard Way.)

Create the VPC

gcloud compute networks create example-k8s --subnet-mode custom

Create the k8s-nodes subnet in the example-k8s VPC network:

gcloud compute networks subnets create k8s-nodes \
  --network example-k8s \

Create a firewall rule that allows internal communication across TCP, UDP, ICMP and IP in IP (used for the Calico overlay):

gcloud compute firewall-rules create example-k8s-allow-internal \
  --allow tcp,udp,icmp,ipip \
  --network example-k8s \

Create a firewall rule that allows external SSH, ICMP, and HTTPS:

gcloud compute firewall-rules create example-k8s-allow-external \
  --allow tcp:22,tcp:6443,icmp \
  --network example-k8s \

Create the controller VM:

gcloud compute instances create controller \
    --async \
    --boot-disk-size 200GB \
    --can-ip-forward \
    --image-family ubuntu-1804-lts \
    --image-project ubuntu-os-cloud \
    --machine-type n1-standard-2 \
    --private-network-ip \
    --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
    --subnet k8s-nodes \
    --zone us-central1-f \
    --tags example-k8s,controller

Create three worker VMs

for i in 0 1 2; do
  gcloud compute instances create worker-${i} \
    --async \
    --boot-disk-size 200GB \
    --can-ip-forward \
    --image-family ubuntu-1804-lts \
    --image-project ubuntu-os-cloud \
    --machine-type n1-standard-2 \
    --private-network-ip${i} \
    --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
    --subnet k8s-nodes \
    --zone us-central1-f \
    --tags example-k8s,worker

Install Docker on the controller VM and each worker VM. On each VM run:

sudo apt update
sudo apt install -y 
sudo systemctl enable docker.service
sudo apt install -y apt-transport-https curl
Install Kubernetes and create the cluster

Install kubeadm, kubelet, and kubectl on each node (see kubeadm docs for more details).

curl -s | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb kubernetes-xenial main
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

Create the controller node of a new cluster. On the controller VM, execute:

sudo kubeadm init --pod-network-cidr

To set up kubectl for the ubuntu user, run:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

The final line of the kubeadm init output contains the command for joining your workers to the controller. Run this on each worker, prepending sudo to run it as root. It will look something like this:

sudo kubeadm join --token <token> --discovery-token-ca-cert-hash sha256:<hash>

On the controller, verify that all nodes have joined

kubectl get nodes

which should output something similar to:

controller   NotReady   master   5m49s   v1.17.2
worker-0     NotReady   <none>   3m38s   v1.17.2
worker-1     NotReady   <none>   3m7s    v1.17.2
worker-2     NotReady   <none>   5s      v1.17.2
Install Calico

On the controller, install Calico using the operator:

kubectl create -f

Downlaod the custom resources necessary to configure Calico

curl -O

If you wish to customize the Calico install, customize the downloaded custom-resources.yaml manifest. Then create the manifest to install Calico.

kubectl create -f custom-resources.yaml

The geeky details of what you get:


Other tools and options


You may have noticed that the bulk of the above instructions are about provisioning the Google Cloud resources for the cluster and installing Kubernetes. Terraform is a tool for automating infrastructure provisioning using declarative configurations. You can also go as far as automating the install of Docker, kubeadm, and Kubernetes using Terraform “provisioners.” See the Terraform documentation for more details.


Kubespray is a tool for provisioning and managing Kubernetes clusters with support for multiple clouds including Google Compute Engine. Calico is the default networking provider, or you can set the kube_network_plugin variable to calico. See the Kubespray docs for more details. See the Kubespray docs for more details.

Next steps