| | 1 | You may continue to install Kubernetes on the vm you already installed docker. If you are installing this on a different machine make sure docker is already installed. |
| | 2 | Part 1 |
| | 3 | Installing kubeadm, kubelet, and kubectl: |
| | 4 | 1. Update the apt package index: |
| | 5 | sudo apt-get update |
| | 6 | 2. Install packages needed to use the Kubernetes apt repository: |
| | 7 | sudo apt-get install -y apt-transport-https ca-certificates curl vim git |
| | 8 | 3. Download the public signing key for the Kubernetes package repositories: |
| | 9 | curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg |
| | 10 | 4. Add the Kubernetes apt repository: |
| | 11 | echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list |
| | 12 | Update the apt package index again: |
| | 13 | sudo apt-get update |
| | 14 | 5. Install kubelet, kubeadm, and kubectl: |
| | 15 | sudo apt-get install -y kubelet kubeadm kubectl |
| | 16 | 6. Pin installed versions of kubelet, kubeadm, and kubectl to prevent them from being accidentally updated: |
| | 17 | sudo apt-mark hold kubelet kubeadm kubectl |
| | 18 | 7. Check installed versions: |
| | 19 | kubectl version --client |
| | 20 | |
| | 21 | kubeadm version |
| | 22 | |
| | 23 | Disable Swap Space |
| | 24 | 8. Disable all swaps from /proc/swaps. |
| | 25 | sudo swapoff -a |
| | 26 | |
| | 27 | sudo sed -i.bak -r 's/(.+ swap .+)/#\1/' /etc/fstab |
| | 28 | 9. Check if swap has been disabled by running the free command. |
| | 29 | free -h |
| | 30 | |
| | 31 | Install Container runtime |
| | 32 | 10. Configure persistent loading of modules |
| | 33 | sudo tee /etc/modules-load.d/k8s.conf <<EOF |
| | 34 | overlay |
| | 35 | br_netfilter |
| | 36 | EOF |
| | 37 | 11. Load at runtime |
| | 38 | sudo modprobe overlay |
| | 39 | |
| | 40 | sudo modprobe br_netfilter |
| | 41 | 12. Ensure sysctl params are set |
| | 42 | sudo tee /etc/sysctl.d/kubernetes.conf<<EOF |
| | 43 | |
| | 44 | net.bridge.bridge-nf-call-ip6tables = 1 |
| | 45 | |
| | 46 | net.bridge.bridge-nf-call-iptables = 1 |
| | 47 | |
| | 48 | net.ipv4.ip_forward = 1 |
| | 49 | |
| | 50 | EOF |
| | 51 | 13. Reload configs |
| | 52 | sudo sysctl --system |
| | 53 | 14. Install required packages |
| | 54 | sudo apt install -y containerd.io |
| | 55 | 15. Configure containerd and start service |
| | 56 | sudo mkdir -p /etc/containerd |
| | 57 | |
| | 58 | sudo containerd config default | sudo tee /etc/containerd/config.toml |
| | 59 | 16. Configuring a cgroup driver |
| | 60 | Both the container runtime and the kubelet have a property called "cgroup driver", which is essential for the management of cgroups on Linux machines. |
| | 61 | sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml |
| | 62 | 17. Restart containerd |
| | 63 | sudo systemctl restart containerd |
| | 64 | |
| | 65 | sudo systemctl enable containerd |
| | 66 | |
| | 67 | systemctl status containerd |
| | 68 | |
| | 69 | Initialize control plane |
| | 70 | 18. Make sure that the br_netfilter module is loaded: |
| | 71 | lsmod | grep br_netfilter |
| | 72 | Output should similar to: |
| | 73 | br_netfilter 22256 0 |
| | 74 | bridge 151336 1 br_netfilter |
| | 75 | 19. Enable kubelet service. |
| | 76 | sudo systemctl enable kubelet |
| | 77 | 20. Pull container images (it will take some time): |
| | 78 | sudo kubeadm config images pull --cri-socket /run/containerd/containerd.sock |
| | 79 | 21. Bootstrap the endpoint. Here we use 10.244.0.0/16 as the pod network: |
| | 80 | sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --cri-socket /run/containerd/containerd.sock |
| | 81 | You will see Your Kubernetes control-plane has initialized successfully! |
| | 82 | 22. To start the cluster, you need to run the following as a regular user (For this scenario we will only use a single host): |
| | 83 | mkdir -p $HOME/.kube |
| | 84 | sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config |
| | 85 | sudo chown $(id -u):$(id -g) $HOME/.kube/config |
| | 86 | 23. Check cluster info: |
| | 87 | kubectl cluster-info |
| | 88 | 24. Install a simple network plugin. |
| | 89 | wget https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml |
| | 90 | kubectl apply -f kube-flannel.yml |
| | 91 | 25. Check the plugin is working |
| | 92 | kubectl get pods -n kube-flannel |
| | 93 | 26. Confirm master node is ready: (If you see the status as Notready, give it a around 10mins) |
| | 94 | kubectl get nodes -o wide |
| | 95 | 27. On a master/ control node to query the nodes you can use: |
| | 96 | kubectl get nodes |
| | 97 | Scheduling Pods on Kubernetes Master Node |
| | 98 | 28. By default, Kubernetes Cluster will not schedule pods on the master/control-plane node for security reasons. It is recommended you keep it this way, but for test environments you need to schedule Pods on control-plane node to maximize resource usage. |
| | 99 | kubectl taint nodes --all node-role.kubernetes.io/control-plane- |
| | 100 | Part 2 |
| | 101 | Create a file simple-pod.yaml |
| | 102 | apiVersion: v1 |
| | 103 | kind: Pod |
| | 104 | metadata: |
| | 105 | name: nginx |
| | 106 | spec: |
| | 107 | containers: |
| | 108 | - name: nginx |
| | 109 | image: nginx:1.14.2 |
| | 110 | ports: |
| | 111 | - containerPort: 80 |
| | 112 | To create the Pod shown above, run the following command: |
| | 113 | kubectl apply -f simple-pod.yaml |
| | 114 | Pods are generally not created directly and are created using workload resources. See Working with Pods |
| | 115 | Links to an external site. for more information on how Pods are used with workload resources |
| | 116 | Part 3 |
| | 117 | Deploying a Simple Web Application on Kubernetes |
| | 118 | 1. Create a Deployment Manifest: |
| | 119 | A Deployment ensures that a specified number of pod replicas are running at any given time. Let's create a simple Deployment for a web application using the nginx image. |
| | 120 | Save the following YAML to a file named webapp-deployment.yaml: |
| | 121 | apiVersion: apps/v1 |
| | 122 | kind: Deployment |
| | 123 | metadata: |
| | 124 | name: webapp-deployment |
| | 125 | labels: |
| | 126 | app: webapp |
| | 127 | spec: |
| | 128 | replicas: 2 |
| | 129 | selector: |
| | 130 | matchLabels: |
| | 131 | app: webapp |
| | 132 | template: |
| | 133 | metadata: |
| | 134 | labels: |
| | 135 | app: webapp |
| | 136 | spec: |
| | 137 | containers: |
| | 138 | - name: nginx |
| | 139 | image: nginx:latest |
| | 140 | ports: |
| | 141 | - containerPort: 80 |
| | 142 | 2. Create a Service Manifest: |
| | 143 | A Service is an abstraction that defines a logical set of Pods and enables external traffic exposure, load balancing, and service discovery. For our web application, we'll use a NodePort service. |
| | 144 | Save the following YAML to a file named webapp-service.yaml: |
| | 145 | apiVersion: v1 |
| | 146 | kind: Service |
| | 147 | metadata: |
| | 148 | name: webapp-service |
| | 149 | spec: |
| | 150 | selector: |
| | 151 | app: webapp |
| | 152 | ports: |
| | 153 | - protocol: TCP |
| | 154 | port: 80 |
| | 155 | targetPort: 80 |
| | 156 | nodePort: 30080 |
| | 157 | type: NodePort |
| | 158 | 3. Deploy the Application: |
| | 159 | Apply the Deployment and Service manifests: |
| | 160 | kubectl apply -f webapp-deployment.yaml |
| | 161 | kubectl apply -f webapp-service.yaml |
| | 162 | 4. Verify the Deployment: |
| | 163 | Check the status of the Deployment and Service: |
| | 164 | kubectl get deployments |
| | 165 | kubectl get services |
| | 166 | You should see your webapp-deployment with 2 replicas. Give it a time to take both replicas online. |
| | 167 | 5. Access the Web Application: |
| | 168 | Since we used a NodePort service, the web application should be accessible on node's IP at port 30080. |
| | 169 | If you're unsure of your node IPs, you can get them with: |
| | 170 | kubectl get nodes -o wide |
| | 171 | Then, in a web browser (though ssh tunnel if you are in UiS cloud) or using a tool like curl, access the web application: |
| | 172 | curl http://<NODE_IP>:30080 |
| | 173 | You should see the default nginx welcome page, indicating that your web application is running. |
| | 174 | |
| | 175 | Part 4 |
| | 176 | Deploying WordPress and MySQL on Kubernetes |
| | 177 | |
| | 178 | Installing dependancies: |
| | 179 | Download rancher.io/local-path storage class: |
| | 180 | kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml |
| | 181 | Check with kubectl get storageclass |
| | 182 | Make this storage class (local-path) the default: |
| | 183 | kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' |
| | 184 | |
| | 185 | 1. Create a PersistentVolumeClaim for MySQL: |
| | 186 | |
| | 187 | MySQL needs persistent storage to store its data. Save the following YAML to a file named mysql-pvc.yaml: |
| | 188 | apiVersion: v1 |
| | 189 | kind: PersistentVolumeClaim |
| | 190 | metadata: |
| | 191 | name: mysql-pvc |
| | 192 | spec: |
| | 193 | accessModes: |
| | 194 | - ReadWriteOnce |
| | 195 | resources: |
| | 196 | requests: |
| | 197 | storage: 1Gi |
| | 198 | Apply the PVC: |
| | 199 | kubectl apply -f mysql-pvc.yaml |
| | 200 | 2. Deploy MySQL: |
| | 201 | Save the following YAML to a file named mysql-deployment.yaml: |
| | 202 | apiVersion: apps/v1 |
| | 203 | kind: Deployment |
| | 204 | metadata: |
| | 205 | name: mysql |
| | 206 | spec: |
| | 207 | replicas: 1 |
| | 208 | selector: |
| | 209 | matchLabels: |
| | 210 | app: mysql |
| | 211 | template: |
| | 212 | metadata: |
| | 213 | labels: |
| | 214 | app: mysql |
| | 215 | spec: |
| | 216 | containers: |
| | 217 | - name: mysql |
| | 218 | image: mysql:5.7 |
| | 219 | env: |
| | 220 | - name: MYSQL_ROOT_PASSWORD |
| | 221 | value: "password" |
| | 222 | - name: MYSQL_DATABASE |
| | 223 | value: "wordpress" |
| | 224 | ports: |
| | 225 | - containerPort: 3306 |
| | 226 | volumeMounts: |
| | 227 | - name: mysql-persistent-storage |
| | 228 | mountPath: /var/lib/mysql |
| | 229 | volumes: |
| | 230 | - name: mysql-persistent-storage |
| | 231 | persistentVolumeClaim: |
| | 232 | claimName: mysql-pvc |
| | 233 | Apply the Deployment: |
| | 234 | kubectl apply -f mysql-deployment.yaml |
| | 235 | |
| | 236 | 3. Create a Service for MySQL: |
| | 237 | |
| | 238 | This will allow WordPress to communicate with MySQL. Save the following YAML to a file named mysql-service.yaml: |
| | 239 | apiVersion: v1 |
| | 240 | kind: Service |
| | 241 | metadata: |
| | 242 | name: mysql |
| | 243 | spec: |
| | 244 | selector: |
| | 245 | app: mysql |
| | 246 | ports: |
| | 247 | - protocol: TCP |
| | 248 | port: 3306 |
| | 249 | targetPort: 3306 |
| | 250 | |
| | 251 | Apply the Service: |
| | 252 | kubectl apply -f mysql-service.yaml |
| | 253 | |
| | 254 | 4. Deploy WordPress: |
| | 255 | Save the following YAML to a file named wordpress-deployment.yaml: |
| | 256 | apiVersion: apps/v1 |
| | 257 | kind: Deployment |
| | 258 | metadata: |
| | 259 | name: wordpress |
| | 260 | spec: |
| | 261 | replicas: 1 |
| | 262 | selector: |
| | 263 | matchLabels: |
| | 264 | app: wordpress |
| | 265 | template: |
| | 266 | metadata: |
| | 267 | labels: |
| | 268 | app: wordpress |
| | 269 | spec: |
| | 270 | containers: |
| | 271 | - name: wordpress |
| | 272 | image: wordpress:latest |
| | 273 | env: |
| | 274 | - name: WORDPRESS_DB_HOST |
| | 275 | value: mysql |
| | 276 | - name: WORDPRESS_DB_USER |
| | 277 | value: "root" |
| | 278 | - name: WORDPRESS_DB_PASSWORD |
| | 279 | value: "password" |
| | 280 | ports: |
| | 281 | - containerPort: 80 |
| | 282 | |
| | 283 | Apply the Deployment: |
| | 284 | kubectl apply -f wordpress-deployment.yaml |
| | 285 | |
| | 286 | 5. Create a Service for WordPress: |
| | 287 | |
| | 288 | This will expose WordPress to external traffic. Save the following YAML to a file named wordpress-service.yaml: |
| | 289 | apiVersion: v1 |
| | 290 | kind: Service |
| | 291 | metadata: |
| | 292 | name: wordpress |
| | 293 | spec: |
| | 294 | selector: |
| | 295 | app: wordpress |
| | 296 | ports: |
| | 297 | - protocol: TCP |
| | 298 | port: 80 |
| | 299 | targetPort: 80 |
| | 300 | type: NodePort |
| | 301 | |
| | 302 | Apply the Service: |
| | 303 | kubectl apply -f wordpress-service.yaml |
| | 304 | |
| | 305 | 6. Access WordPress: |
| | 306 | |
| | 307 | Since we used a NodePort service, WordPress should be accessible on node's IP at a dynamically allocated port above 30000. |
| | 308 | To find the NodePort assigned to WordPress: |
| | 309 | |
| | 310 | kubectl get svc wordpress |
| | 311 | |
| | 312 | Then, in a web browser with the ssh tunnel, access WordPress: |
| | 313 | |
| | 314 | http://< INTERNAL-IP>:<NODE_PORT> |
| | 315 | |
| | 316 | Part 5 |
| | 317 | Convert your Docker deployment into a Kubernetes deployment, you may compose your own service, deployment manifests as needed. Use the docker images you used previously when creating the pods/deployments. |
| | 318 | |
| | 319 | Additional ref: https://kubebyexample.com/ |