Installation and Configuration
Exercise 3.1: Install Kubernetes
Overview
There are several Kubernetes installation tools provided by various vendors. In this lab we will learn to use kubeadm. As a community-supported independent tool, it is planned to become the primary manner to build a Kubernetes cluster.
The labs were written using Ubuntu instances running on Google Cloud Platform (GCP). They have been written to be vendor-agnostic so could run on AWS, local hardware, or inside of virtualization to give you the most flexibility and options. Each platform will have different access methods and considerations. As of v1.12.1 the minimum (as in barely works) size for VirtualBox is 3vCPU/1G memory/5G minimal OS for master and 1vCPU/1G memory/5G minimal OS for worker node.
If using your own equipment you will have to disable swap on every node. There may be other requirements which will be shown as warnings or errors when using the kubeadm command. While most commands are run as a regular user, there are some which require root privilege. Please configure sudo access as shown in a previous lab. You If you are accessing the nodes remotely, such as with GCP or AWS, you will need to use an SSH client such as a local terminal or PuTTY if not using Linux or a Mac. You can download PuTTY from www.putty.org. You would also require a .pem or .ppk file to access the nodes. Each cloud provider will have a process to download or create this file. If attending in-person instructor led training the file will be made available during class.
In the following exercise we will install Kubernetes on a single node then grow the cluster, adding more compute resources. Both nodes used are the same size, providing 2 vCPUs and 7.5G of memory. Smaller nodes could be used, but would run slower
Various exercises will use YAML files, which are included in the text. You are encouraged to write the files when possible, as the syntax of YAML has white space indentation requirements that are important to learn. An important note, do not use tabs in your YAML files, white space only. Indentation matters.
If using a PDF the use of copy and paste often does not paste the single quote correctly. It pastes as a back-quote instead. You will need to modify it by hand. The files have also been made available as a compressed tar file. You can view the resources by navigating to this URL:
https://training.linuxfoundation.org/cm/LFS258
To login use user: LFtraining and a password of: Penguin2014
Once you find the name and link of the current file, which will change as the course updates, use wget to download the file into your node from the command line then expand it like this:
$ wget https://training.linuxfoundation.org/cm/LFS258/LFS258 V2018-11-13 SOLUTIONS.tar.bz2 \
--user=LFtraining --password=Penguin2014
$ tar -xvf LFS258 V2018-11-13 SOLUTIONS.tar.bz2
(Note: depending on your software, if you are cutting and pasting the above instructions, the underscores may disappear and be replaced by spaces, so you may have to edit the command line by hand!)
While Ubuntu 18 bionic has become the typical version to deploy the Kubernetes repository does not yet have compatible binaries at the time of this writing. While xenial binaries can be used there are many additional steps necessary to complete the labs. Ubuntu 18 is expected to be available by the time Kubernetes v.1.13 is released.
Install Kubernetes
Log into your nodes. If attending in-person instructor led training the node IP addresses will be provided by the instructor. You will need to use a .pem or .ppk key for access, depending on if you are using ssh from a terminal or PuTTY. The instructor will provide this to you.
- . Open a terminal session on your first node. For example, connect via PuTTY or SSH session to the first GCP node. The user name may be different than the one shown, student. The IP used in the example will be different than the one youwill use.
[student@laptop ~]$ ssh -i LFS458.pem student@35.226.100.87 The authenticity of host ’54.214.214.156 (35.226.100.87)’ can’t be established. ECDSA key fingerprint is SHA256:IPvznbkx93/Wc+ACwXrCcDDgvBwmvEXC9vmYhk2Wo1E. ECDSA key fingerprint is MD5:d8:c9:4b:b0:b0:82:d3:95:08:08:4a:74:1b:f6:e1:9f. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added ’35.226.100.87’ (ECDSA) to the list of known hosts. <output_omitted>
- Become root and update and upgrade the system. Answer any questions to use the defaults.
student@lfs458-node-1a0a:~$ sudo -i root@lfs458-node-1a0a:~# apt-get update && apt-get upgrade -y <output_omitted>
- The main choices for a container environment are Docker and cri-o. We will user Docker for class, as cri-o requires afair amount of extra work to enable for Kubernetes. As cri-o is open source the community seems to be heading towards its use.
root@lfs458-node-1a0a:~# apt-get install -y docker.io
- . Add new repo for kubernetes. You could also get a tar file or use code from GitHub. Create the file and add an entry forthe main repo for your distribution. As we are still using Ubuntu 16.04 add the kubernetes-xenial with the key word main. Note there are four sections to the entry.
root@lfs458-node-1a0a:~# vim /etc/apt/sources.list.d/kubernetes.list deb http://apt.kubernetes.io/ kubernetes-xenial main
- Add a GPG key for the packages. The command spans three lines. You can omit the backslash when you type. The OK is the expected output, not part of the command.
root@lfs458-node-1a0a:~# curl -s \ https://packages.cloud.google.com/apt/doc/apt-key.gpg \ | apt-key add - OK
- Update with new repo, which will download new repo information.
root@lfs458-node-1a0a:~# apt-get update
- Install the software. There are regular releases the newest of which can be used by omitting the equal sign and version information on the command line. Historically new version have lots of changes and a good chance of a bug or five.
root@lfs458-node-1a0a:~# apt-get install -y \ kubeadm=1.12.1-00 kubelet=1.12.1-00 kubectl=1.12.1-00
- Deciding which pod network to use for Container Networking Interface (CNI) should take into account the expected demands on the cluster. There can be only one pod network per cluster, although the CNI-Genie project is trying to change this
The network must allow container-to-container, pod-to-pod, pod-to-service, and external-to-service communications. As Docker uses host-private networking, using the docker0 virtual bridge and veth interfaces would require being on that host to communicate.
We will use Calico as a network plugin which will allow us to use Network Policies later in the course. Currently Calico does not deploy using CNI by default. The 3.3 version of Calico has more than one configuration file for flexibility with RBAC. Download the configuration files for. Once downloaded look for the expected IPV4 range for containers to use
A short url for each file is shown, the longer URLs can be found here https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml and: https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yamlroot@lfs458-node-1a0a:~# wget https://tinyurl.com/yb4xturm \ -O rbac-kdd.yaml root@lfs459-node-1a0a:~# wget https://tinyurl.com/y8lvqc9g \ -O calico.yaml}
- Use less to page through the file. Look for the IPV4 pool expected by the containers. There are many different configuration settings in this file. Take a moment to view the entire file. The CALICO_IPV4POOL_CIDR must match the value given to kubeadm init in the following step, whatever the value may be.
root@lfs458-node-1a0a:~# less calico.yaml .... # Configure the IP Pool from which Pod IPs will be chosen. - name: CALICO_IPV4POOL_CIDR value: "192.168.0.0/16" ....
- Initialize the master. Read through the output line by line. Expect the output to change as the software matures. At the end are configuration directions to run as a non-root user. The token is mentioned as well. This information can be found later with the kubeadm token list command. The output also directs you to create a pod network to the cluster, which will be our next step. Pass the network settings Calico has in its configuration file, found in the previous step. Please note: the output lists several commands which following commands will complete. Read the next step before further typing.
root@lfs458-node-1a0a:~# kubeadm init --pod-network-cidr 192.168.0.0/16 [init] using Kubernetes version: v1.12.1 [preflight] running pre-flight checks [preflight/images] Pulling images required for setting up a Kubernetes cluster [preflight/images] This might take a minute or two, depending on the speed of your internet connection
Your Kubernetes master has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ You can now join any number of machines by running the following on each node as root: kubeadm join 10.128.0.4:6443 --token rdnhok.g8mb6lfgesunanvh --discovery-token-ca-cert-hash sha256:66350d154fc0169b5bb5fd50c04b72468195e356d78d95f137ed55e995402f77 - . As suggested in the directions at the end of the previous output we will allow a non-root user admin level access to the cluster. Take a quick look at the configuration file once it has been copied and the permissions fixed.
root@lfs458-node-1a0a:~# exit logout student@lfs458-node-1a0a:~$ mkdir -p $HOME/.kube student@lfs458-node-1a0a:~$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config student@lfs458-node-1a0a:~$ sudo chown $(id -u):$(id -g) $HOME/.kube/config student@lfs458-node-1a0a:~$ less .kube/config apiVersion: v1 clusters: - cluster: <output_omitted>
- Apply the network plugin configuration to your cluster. Remember to copy the file to the current, non-root user directory first.
student@lfs458-node-1a0a:~$ sudo cp /root/rbac-kdd.yaml . student@lfs458-node-1a0a:~$ kubectl apply -f rbac-kdd.yaml clusterrole.rbac.authorization.k8s.io/calico-node created clusterrolebinding.rbac.authorization.k8s.io/calico-node created student@lfs458-node-1a0a:~$ sudo cp /root/calico.yaml . student@lfs458-node-1a0a:~$ kubectl apply -f calico.yaml configmap/calico-config created service/calico-typha created deployment.apps/calico-typha created poddisruptionbudget.policy/calico-typha created <output_omitted>
- While many objects have short names, a kubectl command can be a lot to type. We will enable bash auto-completion. Begin by adding the settings to the current shell. Then update the ~/.bashrc file to make it persistent
student@lfs458-node-1a0a:~$ source <(kubectl completion bash) student@lfs458-node-1a0a:~$ echo "source <(kubectl completion bash)" >> ~/.bashrc
- Test by describing the node again. Type the first three letters of the sub-command then type the Tab key. Auto-completion assumes the default namespace. Pass the namespace first to use auto-completion with a different namespace. By pressing Tab multiple times you will see a list of possible values. Continue typing until a unique name is used.
student@lfs458-node-1a0a:~$ kubectl des
n lfs458- student@lfs458-node-1a0a:~$ kubectl -n kube-s g po e
Exercise 3.2: Grow the Cluster
Open another terminal and connect into a your second node. Install Docker and Kubernetes software. These are the many, but not all, of the steps we did on the master node.
The book will use the lfs458-worker prompt for the node being added to help keep track of the proper node for each command. Note that the prompt indicates both the user and system upon which run the command.
- Using the same process as before connect to a second node. If attending ILT use the same .pem key and a new IP provided by the instructor to access the new node. Giving a title or color to the new terminal window is probably a good idea to keep track of the two systems. The prompts can look very similar.
student@lfs458-worker:~$ sudo -i root@lfs458-worker:~# apt-get update && apt-get upgrade -y root@lfs458-worker:~# apt-get install -y docker.io root@lfs458-worker:~# vim /etc/apt/sources.list.d/kubernetes.list deb http://apt.kubernetes.io/ kubernetes-xenial main root@lfs458-worker:~# curl -s \ https://packages.cloud.google.com/apt/doc/apt-key.gpg \ | apt-key add - root@lfs458-worker:~# apt-get update root@lfs458-worker:~# apt-get install -y \ kubeadm=1.12.1-00 kubelet=1.12.1-00 kubectl=1.12.1-00
- Find the IP address of your master server. The interface name will be different depending on where the node is running. Currently inside of GCE the primary interface for this node type is ens4. Your interfaces names may be different. From the output we know our master node IP is 10.128.0.3.
student@lfs458-node-1a0a:~$ ip addr show ens4 | grep inet inet 10.128.0.3/32 brd 10.128.0.3 scope global ens4 inet6 fe80::4001:aff:fe8e:2/64 scope link
- Find the token on the master node. The token lasts 24 hours by default. If it has been longer, and no token is present you can generate a new one with the sudo kubeadm token create command, seen in the following command.
student@lfs458-node-1a0a:~$ ip addr show ens4 | grep inet inet 10.128.0.3/32 brd 10.128.0.3 scope global ens4 inet6 fe80::4001:aff:fe8e:2/64 scope link
- Only if the token has expired, you can create a new token, to use as part of the join command.
student@lfs458-node-1a0a:~$ sudo kubeadm token create 27eee4.6e66ff60318da929
- Starting in v1.9 you should create and use a Discovery Token CA Cert Hash created from the master to ensure the node joins the cluster in a secure manner. Run this on the master node or wherever you have a copy of the CA file. You will get a long string as output.
student@lfs458-node-1a0a:~$ openssl x509 -pubkey \ -in /etc/kubernetes/pki/ca.crt | openssl rsa \ -pubin -outform der 2>/dev/null | openssl dgst \ -sha256 -hex | sed ’s/^.* //’ 6d541678b05652e1fa5d43908e75e67376e994c3483d6683f2a18673e5d2a1b0
- Use the token and hash, in this case as sha256:
to join the cluster from the second node. Use the private IP address of the master server and port 6443. The output of the kubeadm init on the master also has an example to use, should it still be available. root@lfs458-worker:~# kubeadm join \ --token 27eee4.6e66ff60318da929 \ 10.128.0.3:6443 \ --discovery-token-ca-cert-hash \ sha256:6d541678b05652e1fa5d43908e75e67376e994c3483d6683f2a18673e5d2a1b0 [preflight] Running pre-flight checks. [WARNING FileExisting-crictl]: crictl not found in system path [discovery] Trying to connect to API Server "10.142.0.2:6443" [discovery] Created cluster-info discovery client, requesting info from "https://10.142.0.2:6443" [discovery] Requesting info from "https://10.142.0.2:6443" again to validate TLS against the pinned public key [discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "10.142.0.2:6443" [discovery] Successfully established connection with API Server "10.142.0.2:6443" This node has joined the cluster: * Certificate signing request was sent to master and a response was received. * The Kubelet was informed of the new secure connection details. Run ’kubectl get nodes’ on the master to see this node join the cluster.
- Try to run the kubectl command on the secondary system. It should fail. You do not have the cluster or authentication keys in your local .kube/config file.
root@lfs458-worker:~# exit student@lfs458-worker:~$ kubectl get nodes The connection to the server localhost:8080 was refused - did you specify the right host or port? student@lfs458-worker:~$ ls -l .kube ls: cannot access ’.kube’: No such file or directory
Exercise 3.3: Finish Cluster Setup
- View the available nodes of the cluster. It can take a minute or two for the status to change from NotReady to Ready. The NAME field can be used to look at the details. Your node name will be different. Note the master node says NotReady, which is due to a taint.
student@lfs458-node-1a0a:~$ kubectl get node NAME STATUS ROLES AGE VERSION lfs458-node-1a0a NotReady master 18m v1.12.1 lfs458-worker Ready <none> 3m25s v1.12.1
- Look at the details of the node. Work line by line to view the resources and their current status. Notice the status of Taints. The master wont allow non-internal pods by default for security reasons. Take a moment to read each line of output, some appear to be an error until you notice the status shows False.
student@lfs458-node-1a0a:~$ kubectl describe node lfs458-node-1a0a Name: lfs458-node-1a0a Roles: master Labels: beta.kubernetes.io/arch=amd64 beta.kubernetes.io/os=linux kubernetes.io/hostname=lfs458-node-1a0a node-role.kubernetes.io/master= Annotations: kubeadm.alpha.kubernetes.io/cri-socket=/var/run/dockershim.sock node.alpha.kubernetes.io/ttl=0 volumes.kubernetes.io/controller-managed-attach-detach=true CreationTimestamp: Sun, 29 Jul 2018 21:29:32 +0000 Taints: node-role.kubernetes.io/master:NoSchedule <output_omitted>
- Allow the master server to run non-infrastructure pods. The master node begins tainted for security and performance reasons. Will will allow usage of the node in the training environment, but this step may be skipped in a production environment. Note the minus sign (-) at the end, which is the syntax to remove a taint. As the second node does not have the taint you will get a not found error.
student@lfs458-node-1a0a:~$ kubectl describe node | grep -i taint Taints: node-role.kubernetes.io/master:NoSchedule Taints: <none> student@lfs458-node-1a0a:~$ kubectl taint nodes \ --all node-role.kubernetes.io/masternode/lfs458-node-1a0a untainted error: taint "node-role.kubernetes.io/master:" not found
- Now that the master node is able to execute any pod we may find there is a new taint. This behavior began with v1.12.0,requiring a newly added node to be enabled. View then remove the taint if present. It can take a minute or two for the scheduler to deploy the remaining pods.
student@lfs458-node-1a0a:~$ kubectl describe node | grep -i taint Taints: node.kubernetes.io/not-ready:NoSchedule Taints: <none> student@lfs458-node-1a0a:~$ kubectl taint nodes \ --all node.kubernetes.io/not-readynode/lfs58-node-1a0a untainted error: taint "node.kubernetes.io/not-ready:" not found
- Another ”undocumented feature” in v1.12.1 is that the taint removal does not always work the first time. Check to see if the taint has been removed. You may have to remove the taint two or three times before it is actually gone. Wait 60 seconds before trying again to remove the taint.
student@lfs458-node-1a0a:~$ kubectl describe node | grep -i taint Taints: node.kubernetes.io/not-ready:NoSchedule Taints: <none> student@lfs458-node-1a0a:~$ kubectl taint nodes \ --all node.kubernetes.io/not-readynode/lfs58-node-1a0a untainted error: taint "node.kubernetes.io/not-ready:" not found student@lfs458-node-1a0a:~$ sleep 60 ; kubectl describe node | grep -i taint Taints: <none> Taints: <none>
- Determine if the DNS and Calico pods are ready for use. They should all show a status of Running. It may take a minute or two to transition from Pending.
student@lfs458-node-1a0a:~$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-etcd-jlgwr 1/1 Running 0 6m kube-system calico-kube-controllers-74b888b647-wlqf5 1/1 Running 0 6m kube-system calico-node-tpvnr 2/2 Running 0 6m kube-system coredns-78fcdf6894-nc5cn 1/1 Running 0 17m kube-system coredns-78fcdf6894-xs96m 1/1 Running 0 17m <output_omitted>
- . If you notice the coredns- pods are stuck in ContainerCreating status you may have to delete them, causing new ones to be generated. Delete both pods and check to see they show a Running state. Your pod names will be different.
student@lfs458-node-1a0a:~$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-node-qkvzh 2/2 Running 0 59m kube-system calico-node-vndn7 2/2 Running 0 12m kube-system coredns-576cbf47c7-rn6v4 0/1 ContainerCreating 0 3s kube-system coredns-576cbf47c7-vq5dz 0/1 ContainerCreating 0 94m <output_omitted> student@lfs458-node-1a0a:~$ kubectl -n kube-system delete \ pod coredns-576cbf47c7-vq5dz coredns-576cbf47c7-rn6v4 pod "coredns-576cbf47c7-vq5dz" deleted pod "coredns-576cbf47c7-rn6v4" deleted
- When it finished you should see a new tunnel, tunl0, and a cali interface. It may take up to a minute to be created. As you create objects more interfaces will be created.
student@lfs458-node-1a0a:~$ ip a <output_omitted> 4: tunl0@NONE:
mtu 1440 qdisc noqueue state UNKNOWN group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 inet 192.168.0.1/32 brd 192.168.0.1 scope global tunl0 valid_lft forever preferred_lft forever 6: calib0b93ed4661@if4: mtu 1440 qdisc noqueue state UP group default link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::ecee:eeff:feee:eeee/64 scope link valid_lft forever preferred_lft forever
Exercise 3.4: Deploy A Simple Application
We will test to see if we can deploy a simple application, in this case the nginx web server
- Create a new deployment, which is an Kubernetes object while will deploy and monitor an application in a container. Verify it is running and the desired number of container matches the available.
student@lfs458-node-1a0a:~$ kubectl create deployment nginx --image=nginx deployment.apps/nginx created student@lfs458-node-1a0a:~$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 1 1 1 1 6s
- View the details of the deployment. Remember auto-completion will work for sub-commands and resources as well.
student@lfs458-node-1a0a:~$ kubectl describe deployment nginx Name: nginx Namespace: default CreationTimestamp: Thu, 08 Nov 2018 19:23:00 +0000 Labels: app=nginx Annotations: deployment.kubernetes.io/revision: 1 Selector: app=nginx Replicas: 1 desired | 1 updated | 1 total | 1 ava.... StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge <output_omitted>
- . View the basic steps the cluster took in order to pull and deploy the new application. You should see several lines of
output with newer events at the top.
student@lfs458-node-1a0a:~$ kubectl get events <output_omitted>
- You can also view the output in yaml format, which could be used to create this deployment again or new deployments.
Get the information but change the output to yaml. Note that halfway down there is status information of the current
deployment.
student@lfs458-node-1a0a:~$ kubectl get deployment nginx -o yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: "1" creationTimestamp: 2017-09-27T18:21:25Z <output_omitted>
- Run the command again and redirect the output to a file. Then edit the file. Remove the creationTimestamp,
resourceVersion, selfLink, and uid lines. Also remove all the lines including and after status:, which should
be somewhere around line 40, if others have already been removed.
student@lfs458-node-1a0a:~$ kubectl get deployment nginx -o yaml > first.yaml student@lfs458-node-1a0a:~$ vim first.yaml
- Delete the existing deployment.
student@lfs458-node-1a0a:~$ kubectl delete deployment nginx deployment.extensions "nginx" deleted
- Create the deployment again this time using the file.
student@lfs458-node-1a0a:~$ kubectl create -f first.yaml deployment.extension/nginx created
- Look at the yaml output of this iteration and compare it against the first. The time stamp, resource version and
uid we had deleted are in the new file. These are generated for each resource we create, so we need to delete them
from yaml files to avoid conflicts or false information. The status should not be hard-coded either.
student@lfs458-node-1a0a:~$ kubectl get deployment nginx -o yaml > second.yaml student@lfs458-node-1a0a:~$ diff first.yaml second.yaml <output_omitted>
- Now that we have worked with the raw output we will explore two other ways of generating useful YAML or JSON. Use
the --dry-run option and verify no object was created. Only the prior nginx deployment should be found. The output
lacks the unique information we removed before.
student@lfs458-node-1a0a:~$ kubectl create deployment two --image=nginx --dry-run -o yaml apiVersion: apps/v1beta1 kind: Deployment metadata: creationTimestamp: null labels: run: two name: two spec: <output_omitted> student@lfs458-node-1a0a:~$ kubectl get deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 1 1 1 1 7m
- Existing objects can be viewed in a ready to use YAML output. Take a look at the existing nginx deployment. Note there
is more detail to the –export option.
student@lfs458-node-1a0a:~$ kubectl get deployments nginx --export -o yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: annotations: deployment.kubernetes.io/revision: "1" creationTimestamp: null generation: 1 labels: run: nginx <output_omitted>
- The output can also be viewed in JSON output.
student@lfs458-node-1a0a:~$ kubectl get deployment nginx --export -o json { "apiVersion": "extensions/v1beta1", "kind": "Deployment", "metadata": { "annotations": { "deployment.kubernetes.io/revision": "1" }, <output_omitted>
- . The newly deployed nginx container is a light weight web server. We will need to create a service to view the default
welcome page. Begin by looking at the help output. Note that there are several examples given, about halfway through
the output.
student@lfs458-node-1a0a:~$ kubectl expose -h <output_omitted>
- Now try to gain access to the web server. As we have not declared a port to use you will receive an error.
student@lfs458-node-1a0a:~$ kubectl expose deployment/nginx error: couldn’t find port via --port flag or introspection See ’kubectl expose -h’ for help and examples.
- To change an existing configuration in a cluster can be done with subcommands apply, edit or patch for non-disruptive
updates. The apply command does a three-way diff of previous, current, and supplied input to determine modifications
to make. Fields not mentioned are unaffected. The edit function performs a get, opens an editor, then an apply. You
can update API objects in place with JSON patch and merge patch or strategic merge patch functionality.
If the configuration has resource fields which cannot be updated once initialized then a disruptive update could be done using the replace --force option. This deletes first then re-creates a resource.
Edit the file. Find the container name, somewhere around line 31 and add the port information as shown below.student@lfs458-node-1a0a:~$ vim first.yaml .... spec: containers: - image: nginx imagePullPolicy: Always name: nginx ports: # Add these - containerPort: 80 # three protocol: TCP # lines resources: {} ....
- Due to how the object was created we will need to use replace to terminate and create a new deployment
student@lfs458-node-1a0a:~$ kubectl replace -f first.yaml deployment.extensions/nginx replaced
- View the Pod and Deployment. Note the AGE shows the Pod was re-created.
student@lfs458-node-1a0a:~$ kubectl get deploy,pod NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deployment.extensions/nginx 1 1 1 1 2m4s NAME READY STATUS RESTARTS AGE pod/nginx-7cbc4b4d9c-l8cgl 1/1 Running 0 8s
- Try to expose the resource again. This time it should work
student@lfs458-node-1a0a:~$ kubectl expose deployment/nginx service/nginx exposed
- Verify the service configuration. First look at the service information, then at the endpoint information. Note the Cluster
IP is not the current endpoint. Take note of the current endpoint IP. In the example below it is 10.244.1.99:80. We will
use this information in a few steps.
student@lfs458-node-1a0a:~$ kubectl get svc nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx ClusterIP 10.100.61.122 <none> 80/TCP 3m student@lfs458-node-1a0a:~$ kubectl get ep nginx NAME ENDPOINTS AGE nginx 10.244.1.99:80 4m
- Determine which node the container is running on. Log into that node and use tcpdump to view traffic on the tunl0,
as in tunnel zero, interface. The second node in this example. You may also see traffic on an interface which starts with
cali and some string. Leave that command running while you run curl in the following step. You should see several
messages go back and forth, including a HTTP: HTTP/1.1 200 OK and a ack response to the same sequence.
student@lfs458-node-1a0a:~$ kubectl describe pod nginx-7cbc4b4d9c-d27xw \ | grep Node: Node: lfs458-worker/10.128.0.5 student@lfs458-worker:~$ sudo tcpdump -i tunl0 tcpdump: verbose output suppressed, use -v or -vv for full protocol... listening on tunl0, link-type EN10MB (Ethernet), capture size... <output_omitted>
- Test access to the Cluster IP, port 80. You should see the generic nginx installed and working page. The output should
be the same when you look at the ENDPOINTS IP address. If the curl command times out the pod may be running on
the other node. Run the same command on that node and it should work.
student@lfs458-node-1a0a:~$ curl 10.100.61.122:80 <DOCTYPE html> <html> <head> <title>Welcome to nginx!<title> <style> <output_omitted>
- Now scale up the deployment from one to three web servers.
student@lfs458-node-1a0a:~$ kubectl get deployment nginx NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 1 1 1 1 12m student@lfs458-node-1a0a:~$ kubectl scale deployment nginx --replicas=3 deployment.extensions/nginx scaled student@lfs458-node-1a0a:~$ kubectl get deployment nginx NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 3 3 3 3 12m
- View the current endpoints. There now should be three. If the DESIRED above said three, but AVAILABLE said two wait
a few seconds and try again, it could be slow to fully deploy.
student@lfs458-node-1a0a:~$ kubectl get ep nginx NAME ENDPOINTS AGE nginx 10.244.0.66:80,10.244.1.100:80,10.244.1.99:80 10m
- . Find the oldest pod of the nginx deployment and delete it. The Tab key can be helpful for the long names. Use the AGE
field to determine which was running the longest. You will notice activity in the other terminal where tcpdump is running,
when you delete the pod.
student@lfs458-node-1a0a:~$ kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP nginx-1423793266-7f1qw 1/1 Running 0 14m 10.244.0.66 nginx-1423793266-8w2nk 1/1 Running 0 1m 10.244.1.100 nginx-1423793266-fbt4b 1/1 Running 0 1m 10.244.1.101 student@lfs458-node-1a0a:~$ kubectl delete po nginx-1423793266-7f1qw pod "nginx-1423793266-7f1qw" deleted
- Wait a minute or two then view the pods again. One should be newer than the others. In the following example two
minutes instead of four. If your tcpdump was using the veth interface of that container it will error out.
student@lfs458-node-1a0a:~$ kubectl get po NAME READY STATUS RESTARTS AGE nginx-1423793266-13p69 1/1 Running 0 2m nginx-1423793266-8w2nk 1/1 Running 0 4m nginx-1423793266-fbt4b 1/1 Running 0 4m
- View the endpoints again. The original endpoint IP is no longer in use. You can delete any of the pods and the service
will forward traffic to the existing backend pods.
student@lfs458-node-1a0a:~$ kubectl get ep nginx NAME ENDPOINTS AGE nginx 10.244.0.66:80,10.244.1.100:80,10.244.1.101:80 15m
- Test access to the web server again, using the ClusterIP address, then any of the endpoint IP addresses. Even though
the endpoints have changed you still have access to the web server. This access is only from within the cluster
student@lfs458-node-1a0a:~$ curl 10.100.61.122:80 <DOCTYPE html> <html> <head> <title>Welcome to nginx!<title> <style> body { <output_omitted>
Exercise 3.5: Access from Outside the Cluster
You can access a Service from outside the cluster using a DNS add-on or vi environment variables. We will use environment variables to gain access to a Pod.
- Begin by getting a list of the pods.
student@lfs458-node-1a0a:~$ kubectl get po NAME READY STATUS RESTARTS AGE nginx-1423793266-13p69 1/1 Running 0 8m nginx-1423793266-8w2nk 1/1 Running 0 10m nginx-1423793266-fbt4b 1/1 Running 0 10m
- Choose one of the pods and use the exec command to run printenv inside the pod. The following example uses the first pod listed above.
student@lfs458-node-1a0a:~$ kubectl exec nginx-1423793266-13p69 \ -- printenv |grep KUBERNETES KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_SERVICE_HOST=10.96.0.1 KUBERNETES_SERVICE_PORT=443 NGINX_SERVICE_HOST=10.100.61.122 NGINX_SERVICE_PORT=80 <output_omitted>
- Find and then delete the existing service for nginx.
student@lfs458-node-1a0a:~$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d nginx ClusterIP 10.100.61.122 <none> 80/TCP 40m
- Delete the service.
student@lfs458-node-1a0a:~$ kubectl delete svc nginx service "nginx" deleted
- Create the service again, but this time pass the LoadBalancer type. Check to see the status and note the external ports
mentioned. The output will show the External-IP as pending. Unless a provider responds with a load balancer it will
continue to show as pending.
student@lfs458-node-1a0a:~$ kubectl expose deployment nginx --type=LoadBalancer service/nginx exposed student@lfs458-node-1a0a:~$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d nginx LoadBalancer 10.104.249.102
80:32753/TCP 2s - Open a browser on your local system, not the GCE node, and use the public IP of your node and port 32753, shown
in the output above. If running the labs on a remote system like AWS or GCE the CLUSTER-IPs are internal. Use the
public IP you used with SSH to gain access.
- Scale the deployment to zero replicas. Then test the web page again. It should fail.
student@lfs458-node-1a0a:~$ kubectl scale deployment nginx --replicas=0 deployment.extensions/nginx scaled student@lfs458-node-1a0a:~$ kubectl get po No resources found.
- Scale the deployment up to two replicas. The web page should work again
student@lfs458-node-1a0a:~$ kubectl scale deployment nginx --replicas=2 deployment.extensions/nginx scaled student@lfs458-node-1a0a:~$ kubectl get po .NAME READY STATUS RESTARTS AGE nginx-1423793266-7x181 1/1 Running 0 1m nginx-1423793266-s6vcz 1/1 Running 0 1m
- Delete the deployment to recover system resources. Note that deleting a deployment does not delete the endpoints or
services.
student@lfs458-node-1a0a:~$ kubectl delete deployments nginx deployment.extensions "nginx" deleted student@lfs458-node-1a0a:~$ kubectl delete ep nginx endpoints "nginx" deleted student@lfs458-node-1a0a:~$ kubectl delete svc nginx service "nginx" deleted