This guide shows how to install a 3 node kubeadm cluster on AWS EC2 instances. If using the KodeKloud AWS Playground environment, please ensure you have selected region us-east-1
(N. Virginia) from the region selection at the top right of the AWS console. To maintain compatibility with the playground permissions, we will use the following EC2 instance configuration.
- Instance type:
t3.medium
- Operating System: Ubuntu 22.04 (at time of writing)
- Storage:
gp2
, 8GB
Note that this is an exercise in simply getting a cluster running and is a learning exercise only! It will not be suitable for serving workloads to the internet, nor will it be properly secured, otherwise this guide would be three times longer! It should not be used as a basis for building a production cluster.
We will provision the following infrastructure. The infrastructure will be created by Terraform, so as not to spend too much of the lab time just getting that provisioned, and to allow you to focus on the cluster installation.
As can be seen in this diagram, we will create four EC2 instances; three form the cluster itself and the fourth, student-node
is where you will coordinate the work from. This is similar to how the real exam works - you start on a student node, then use SSH to connect to cluster nodes. Note that SSH connections are only possible in the direction of the arrows. It is not possible to SSH from e.g. controlplane
directly to node01
. You must exit
to student-node
first. This is also how it is in the exam. The student-node
assumes the role of a bastion host.
We have also set up direct connection from your workstation to the node ports of the workers so that you can browse any NodePort services you create (see security below).
Some basic security will be configured:
- Only you will be able to connect to the student-node. A piece of Terraform magic will determine your public IP address and set it in the security group of the student-node instance.
- Only you will be able to access the cluster's node ports over the Internet, using the same technique as above.
- Only the student node can SSH to the cluster nodes.
- Ports required by Kubernetes itself (inc. etcd) and Weave CNI will be configured in security groups on the cluster nodes.
Security issues that would make this unsuitable for a genuine production cluster:
- The kube nodes would be on private subnets (no direct access from the Internet) and placed behind a NAT gateway to allow them to download packages, or with a more extreme security posture, completely airgapped.
- Access to API server and etcd would be more tightly controlled.
- Use of default VPC is not recommended.
- A cloud load balancer coupled with an ingress controller would be provisioned to provide ingress to the cluster. It is definitely not recommended to expose the worker nodes' node ports to the Internet as we are doing here!!!
Other things that will be configured by the Terraform code
- Host names set on the nodes:
student-node
,controlplane
,node01
,node02
- Content of
/etc/hosts
set up on all nodes for easy use ofssh
command from student node. - Generation of key pair for logging into instances via SSH.
Let's go ahead and get the infrastructure built!
Firstly, start an AWS Playground, or if you have your own AWS account, you can use that but you'll bear the cost of the infrastructure while it is running.
This deployment was tested using Terraform v1.5.3
If you don't already have Terraform installed, it is quite easy to do. Don't be alarmed if you don't know Terraform, just go with the flow here. Note that for a successful career in DevOps you cannot avoid Terraform, so we recommend our courses which can be found on this page.
If you are studying or have studied Terraform, have a good look at the configuration files and see if you can understand how they work.
- Go to https://developer.hashicorp.com/terraform/downloads?product_intent=terraform
- Select your operating system
- For macOS, the easiest way is to use homebrew as indicated.
- For Windows, select
AMD64
and download the zip file. The zip file contains onlyterraform.exe
. Unzip it and place it somewhere you can run it from.- If you have the chocolately package manager, simply run
choco install -y terraform
As Administrator. - If you don't, you really should consider installing it.
- If you have the chocolately package manager, simply run
We need access keys for Terraform to connect to the AWS account.
- Log into AWS using the URL and credentials provided when you started the playground.
- Go to the IAM console.
- Select
Users
from the menu in the left panel. - Find your user account (for playground, username will begin with
odl_user_
), and click it. - Select
Security credentials
tab, scroll down toAccess keys
and pressCreate access key
. - Select
Command Line Interface (CLI)
radio button. - Check the
Confirmation
checkbox at the bottom and pressNext
. - Enter anything for the description and press
Next
. - Show the secret access key. Copy access key and secret access key to a notepad for use later, or download the CSV file.
You can either individually download all the tf
files from this repo folder and open a command prompt/terminal in the folder you downloaded to, or clone the repo as follows:
git clone https://github.com/kodekloudhub/certified-kubernetes-administrator-course.git
cd certified-kubernetes-administrator-course/kubeadm-clusters/aws
Now create a file terraform.tfvars
in the same folder as the rest of the .tf
files. Use the following template for this file and replace the values for access_key
and secret_key
with the keys you generated in the step above
access_key = "AKIASFFZ4IEWZEXAMPLE"
secret_key = "st1i+TlfYn4+lpp4vYRNuxbafYm8jraCEXAMPLE"
terraform init
terraform plan
terraform apply
If this all runs correctly, you will see something like the following at the end of all the output. IP addresses will be different for you. Path to SSH key file (after -i
) will be different on Windows and may be different on Mac.
Apply complete! Resources: 22 added, 0 changed, 0 destroyed.
Outputs:
connect_command = "ssh -i ~/kubeadm-aws.pem [email protected]"
node01 = "18.205.245.169"
node02 = "75.101.200.29"
Copy all these outputs to a notepad for later use.
- Wait for all instances to be ready (Instance state -
running
, Status check -2/2 checks passed
). This will take 2-3 minutes. You may have to hit the refresh button a couple of times. We should now have something that looks like this: - Test you can log in.
- You can do it directly from your terminal by copying and pasting the value of
connect_command
that was output at the end of the terraform run, or - Use MobaXterm or another SSH client to connect to the IP address output as part of the connect command, as user
ubuntu
, and you will also need to give it the path to the SSH key file, which is the path after-i
in theconnect_command
.
- You can do it directly from your terminal by copying and pasting the value of
We will install kubectl here so that we can run commands against the cluster when it is built
-
Become root (saves typing
sudo
before every command)sudo -i
-
Update the apt package index and install packages needed to use the Kubernetes apt repository:
apt-get update apt-get install -y apt-transport-https ca-certificates curl
-
Download the Google Cloud public signing key
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
-
Add the Kubernetes apt repository
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
-
Update apt package index, install kubectl, and pin its version
apt-get update apt-get install -y kubectl apt-mark hold kubectl
-
Exit the root shell
exit
First, be logged into student-node
as directed above.
Repeat the following steps on controlplane
, node01
and node02
by SSH-ing from student-node
to each cluster node in turn, e.g.
ubuntu@studentnode:~$ ssh controlplane
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.19.0-1028-aws x86_64)
Last login: Tue Jul 25 15:27:07 2023 from 172.31.93.38
ubuntu@controlplane:~$
-
Become root (saves typing
sudo
before every command)sudo -i
-
Update the apt package index and install packages needed to use the Kubernetes apt repository:
apt-get update apt-get install -y apt-transport-https ca-certificates curl
-
Set up the required kernel modules and make them persistent
cat <<EOF > /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter
-
Set the required kernel parameters and make them persistent
cat <<EOF > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system
-
Install the container runtime
apt-get install -y containerd
-
Configure the container runtime to use CGroups. This part is the bit many students miss, and if not done results in a controlplane that comes up, then all the pods start crashlooping.
-
Create default configuration
mkdir -p /etc/containerd containerd config default > /etc/containerd/config.toml
-
Edit the configuration to set up CGroups
vi /etc/containerd/config.toml
Scroll down till you find a line with
SystemdCgroup = false
. Edit it to beSystemdCgroup = true
, then save and exit vi -
Restart containerd
systemctl restart containerd
-
-
Download the Google Cloud public signing key
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
-
Add the Kubernetes apt repository
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list
-
Update apt package index, install kubelet, kubeadm and kubectl, and pin their version
apt-get update apt-get install -y kubelet kubeadm kubectl apt-mark hold kubelet kubeadm kubectl
-
Configure
crictl
in case we need it to examine running containerscrictl config \ --set runtime-endpoint=unix:///run/containerd/containerd.sock \ --set image-endpoint=unix:///run/containerd/containerd.sock
-
Exit root shell
exit
-
Return to
student-node
exit
Repeat the above till you have done
controlplane
,node01
andnode02
-
ssh to
controlplane
ssh controlplane
-
Become root
sudo -i
-
Boot the control plane
kubeadm init
Copy the join command that is printed to a notepad for use on the worker nodes.
-
Install network plugin (weave)
kubectl --kubeconfig /etc/kubernetes/admin.conf apply -f "https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s-1.11.yaml"
-
Check we are up and running
kubectl --kubeconfig /etc/kubernetes/admin.conf get pods -n kube-system
-
Exit root shell
exit
-
Prepare the kubeconfig file for copying to
student-node
{ sudo cp /etc/kubernetes/admin.conf . sudo chown ubuntu:ubuntu admin.conf }
-
Exit to student node
exit
-
Copy kubeconfig down from
controlplane
tostudent-node
mkdir ~/.kube scp controlplane:~/admin.conf ~/.kube/config
-
Test it
kubectl get pods -n kube-system
-
SSH to
node01
-
Become root
sudo -i
-
Paste the join command that was output by
kubeadm init
oncontrolplane
-
Return to
student-node
exit exit
-
Repeat the steps 2, 3 and 4 on
node02
-
Now you should be back on
student-node
. Check all nodes are upkubectl get nodes -o wide
Run the following on student-node
-
Deploy and expose an nginx pod
kubectl run nginx --image nginx --expose --port 80
-
Convert the service to NodePort
kubectl edit service nginx
Edit the
spec:
part of the service until it looks like this. Don't change anything abovespec:
spec: ports: - port: 80 protocol: TCP targetPort: 80 nodePort: 30080 selector: run: nginx sessionAffinity: None type: NodePort
-
Test with curl
-
Get the internal IP of one of the nodes
kubectl get node node01 -o wide
This will output the following (INTERNAL-IP will be different for you)
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME node01 Ready <none> 13m v1.27.4 172.31.26.150 <none> Ubuntu 22.04.2 LTS 5.19.0-1028-aws containerd://1.6.12
-
Using the INTERNAL-IP and the nodePort value set on the service, form a
curl
command. The IP will be different to what is shown here.curl http://172.31.26.150:30080
-
-
Test from your own browser
-
Get the public IP of one of the nodes. These were output by Terraform. You can also find this by looking at the instances on the EC2 page of the AWS console.
-
Using the public IP and the nodePort value set on the service, form a URL to paste into your browser. The IP will be different to what is shown here.
http://18.205.245.169:30080/
-