=====Overview===== Deployments, finally we have reached to the last major object in Kubernetes. Deployments take everything we have being doing so far and encapusaltes it. What I mean is the following: {{:deploymentoverview.jpg?500|}} Of course you can have more than 1 pod in replica set. Yes, it is replica set when under Deployment, not replication controller. But the purpose is the same. So let's create our Deployment now: =====Configuration===== To create a deployment again we have two ways: * Iterative - With kubectl create and so on * Declarative - Using YML / JSON file, I will be using this one. As I am bored with the iterative way, let's clean our env and create a deployment. I won't delete my service as I will use it later. ====Create Deployment (Declarative) ==== First, let's create our YML file: apiVersion: apps/v1 kind: Deployment metadata: name: hello-deploy spec: replicas: 10 selector: matchLabels: app: hello-date template: metadata: labels: app: hello-date spec: containers: - name: hello-pod image: andonovj/httpserverdemo:latest ports: - containerPort: 1234 Bare in mind that the deployment have been moved from extensions/v1beta1 -> apps/v1 and forward :) Also a little syntax changes compared to the old one, but nothing serious. Once we have the YAML file, we can continue as follows: ubuntu@k8s-master:~$ kubectl create -f deploy.yml deployment.apps/hello-deploy created ubuntu@k8s-master:~$ kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE hello-deploy 1/10 10 1 26s ubuntu@k8s-master:~$ kubectl describe deploy Name: hello-deploy Namespace: default CreationTimestamp: Sat, 02 May 2020 15:54:34 +0000 Labels: Annotations: deployment.kubernetes.io/revision: 1 Selector: app=hello-date Replicas: 10 desired | 10 updated | 10 total | 10 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=hello-date Containers: hello-pod: Image: andonovj/httpserverdemo:latest Port: 1234/TCP Host Port: 0/TCP Environment: Mounts: Volumes: Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: NewReplicaSet: hello-deploy-6cd458494 (10/10 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 8m59s deployment-controller Scaled up replica set hello-deploy-6cd458494 to 10 Again, we can check if our app is working: {{:deploymenttest.jpg?400|}} ====Replica Sets==== As already mentioned, deployments operate with Replicat Sets, NOT replication Controllers. Eventhough that is a new objects, it inherits a lot of stuff from the replication controller. You can check the Replica sets, almost the same as the replication controller and notice that we don't have Replication Controllers anymore...after I have deleted them in the past of course. ubuntu@k8s-master:~$ kubectl get rs NAME DESIRED CURRENT READY AGE hello-deploy-6cd458494 10 10 10 11m ubuntu@k8s-master:~$ kubectl get rc No resources found in default namespace. ubuntu@k8s-master:~$ kubectl describe rs Name: hello-deploy-6cd458494 Namespace: default Selector: app=hello-date,pod-template-hash=6cd458494 Labels: app=hello-date pod-template-hash=6cd458494 Annotations: deployment.kubernetes.io/desired-replicas: 10 deployment.kubernetes.io/max-replicas: 13 deployment.kubernetes.io/revision: 1 Controlled By: Deployment/hello-deploy Replicas: 10 current / 10 desired Pods Status: 10 Running / 0 Waiting / 0 Succeeded / 0 Failed Pod Template: Labels: app=hello-date pod-template-hash=6cd458494 Containers: hello-pod: Image: andonovj/httpserverdemo:latest Port: 1234/TCP Host Port: 0/TCP Environment: Mounts: Volumes: Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-mjtw9 Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-m9bgx Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-44jvs Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-6v8lt Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-zwh4l Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-jf594 Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-vd7mt Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-nq2wx Normal SuccessfulCreate 16m replicaset-controller Created pod: hello-deploy-6cd458494-f8j5f Normal SuccessfulCreate 16m replicaset-controller (combined from similar events): Created pod: hello-deploy-6cd458494-dxqh7 ubuntu@k8s-master:~$ kubectl get rc No resources found in default namespace. <- We don't have Replication Controllers anymore. =====Rolling-Updates===== Now, we have deployed application, which is totally scalable and redundancy is provided as Hell :) But what if want to fix couple bugs, or present new version. What we do then ? Well, let's edit our application, let's add "v2" into our awesome app: ====Edit Software==== namespace HttpServerDemo { using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using System.IO; using System.Threading; class Program { public async static Task Main(string[] args) { TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234); tcpListener.Start(); while (true) { TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync(); await ProcessClientAsync(tcpClient); } } public static async Task ProcessClientAsync(TcpClient tcpClient) { const string NewLine = "\r\n"; using (var networkStream = tcpClient.GetStream()) { byte[] requestBytes = new byte[1000000]; //TODO USE Buffer int bytesRead = await networkStream.ReadAsync(requestBytes, 0, requestBytes.Length); string request = Encoding.UTF8.GetString(requestBytes, 0, bytesRead); string responseText = @"

Working... v2

" + <- Updated this line with "v2" :) $"

Time is: {System.DateTime.Now}

"; string response = "HTTP/1.1 200 OK" + NewLine + "Server: SoftuniServer/1.0 " + NewLine + "Content-Type: text/html" + NewLine + "Content-Length: " + responseText.Length + NewLine + NewLine + responseText; byte[] responseBytes = Encoding.UTF8.GetBytes(response); await networkStream.WriteAsync(responseBytes, 0, responseBytes.Length); Console.WriteLine(request); Console.WriteLine(new string('=', 60)); } } } } You see that "v2" after "Working", yes, I just added it now. Let's rebuild via docker, upload it and edit our edition to v2 :) The Rebuild part you can see in the Docker section, but I will put the output here as well: ====Dockerize, Tag & Push=== [root@postgresqlmaster httpserverdemo]# ls -lart total 16 -rw-r--r--. 1 root root 178 Jan 15 11:29 HttpServerDemo.csproj -rw-r--r--. 1 root root 405 Jan 24 07:20 Dockerfile -rw-r--r--. 1 root root 1904 May 2 12:19 Program.cs dr-xr-x---. 7 root root 4096 May 2 12:39 .. drwxr-xr-x. 2 root root 71 May 2 12:45 . root@k8s-master:/home/vagrant/HttpServerDemo# docker build -t httpserverdemo . Sending build context to Docker daemon 6.144kB Step 1/11 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env ---> 4aa6a74611ff Step 2/11 : WORKDIR /app ---> Using cache ---> e6fb8470c359 Step 3/11 : COPY *.csproj ./ ---> Using cache ---> c6e7e3257ccd Step 4/11 : RUN dotnet restore ---> Using cache ---> 073b0e6dcfac Step 5/11 : COPY . ./ ---> dbb416239305 Step 6/11 : RUN dotnet publish -c Release -o out ---> Running in cdccacf739ec Microsoft (R) Build Engine version 16.5.0+d4cbfca49 for .NET Core Copyright (C) Microsoft Corporation. All rights reserved. Restore completed in 38.37 ms for /app/HttpServerDemo.csproj. HttpServerDemo -> /app/bin/Release/netcoreapp3.1/HttpServerDemo.dll HttpServerDemo -> /app/out/ Removing intermediate container cdccacf739ec ---> 29d5a30972d4 Step 7/11 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1 ---> 4aa6a74611ff Step 8/11 : WORKDIR /app ---> Using cache ---> e6fb8470c359 Step 9/11 : COPY --from=build-env /app/out . ---> ff034e158a2e Step 10/11 : RUN find -type d -name bin -prune -exec rm -rf {} \; && find -type d -name obj -prune -exec rm -rf {} \; ---> Running in e217c60056a6 Removing intermediate container e217c60056a6 ---> f3755f21e57e Step 11/11 : ENTRYPOINT ["dotnet", "HttpServerDemo.dll"] ---> Running in e49283dde769 Removing intermediate container e49283dde769 ---> 9f2f48860257 Successfully built 9f2f48860257 aaaaand....we are done :) We have edited our awesome software and uploaded it to docker hub. Let's now update the containers in the pods :) ====Perform the Update==== For that purpose, we have to edit the YAML file with couple more values like: * Strategy: RollingUpdate(default) * maxUnavailable: 1 - Never have more than 1 unavailable * maxSurge: 1 - Never have more than 1 additional. In our case, never have more than 11 pods So, our YAML file should look as follows: apiVersion: apps/v1 kind: Deployment metadata: name: hello-deploy spec: replicas: 10 minReadySeconds: 10 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 1 selector: matchLabels: app: hello-date template: metadata: labels: app: hello-date spec: containers: - name: hello-pod image: andonovj/httpserverdemo:v2 ports: - containerPort: 1234 Once we have the file, you can start the process as so: ubuntu@k8s-master:~$ kubectl apply -f deploy.yml Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply deployment.apps/hello-deploy configured You can see the progress of the Rolling Update as follows: ubuntu@k8s-master:~$ kubectl rollout status deploy hello-deploy Waiting for deployment "hello-deploy" rollout to finish: 2 out of 10 new replicas have been updated... ^Cubuntu@k8s-master:~$ kubectl get pods NAME READY STATUS RESTARTS AGE hello-deploy-6cd458494-6mvt7 1/1 Running 0 80s hello-deploy-6cd458494-9bl7c 1/1 Running 0 80s hello-deploy-6cd458494-fvwvz 1/1 Running 0 80s hello-deploy-6cd458494-grp2w 1/1 Running 0 80s hello-deploy-6cd458494-ldsxq 1/1 Running 0 80s hello-deploy-6cd458494-lpgdj 1/1 Running 0 80s hello-deploy-6cd458494-mjsmh 1/1 Running 0 80s hello-deploy-6cd458494-rd58j 1/1 Running 0 80s hello-deploy-6cd458494-rrkhq 1/1 Running 0 80s hello-deploy-7f44bd8b96-k92sr 0/1 ContainerCreating 0 23s hello-deploy-7f44bd8b96-lqmdx 0/1 ContainerCreating 0 23s ubuntu@k8s-master:~$ kubectl rollout status deploy hello-deploy Waiting for deployment "hello-deploy" rollout to finish: 4 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 4 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... We can also check the version which is currently being used: ubuntu@k8s-master:~$ kubectl get deploy hello-deploy NAME READY UP-TO-DATE AVAILABLE AGE hello-deploy 10/10 10 10 5m7s ubuntu@k8s-master:~$ kubectl describe deploy hello-deploy Name: hello-deploy Namespace: default CreationTimestamp: Sun, 03 May 2020 11:28:33 +0000 Labels: Annotations: deployment.kubernetes.io/revision: 2 Selector: app=hello-date Replicas: 10 desired | 10 updated | 10 total | 10 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 10 RollingUpdateStrategy: 1 max unavailable, 1 max surge Pod Template: Labels: app=hello-date Containers: hello-pod: Image: andonovj/httpserverdemo:edge <- We are using the "edge" version, which we have just compiled. Port: 1234/TCP Host Port: 0/TCP Environment: Mounts: Volumes: Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: NewReplicaSet: hello-deploy-7f44bd8b96 (10/10 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 5m11s deployment-controller Scaled up replica set hello-deploy-6cd458494 to 10 Normal ScalingReplicaSet 4m14s deployment-controller Scaled up replica set hello-deploy-7f44bd8b96 to 1 Normal ScalingReplicaSet 4m14s deployment-controller Scaled down replica set hello-deploy-6cd458494 to 9 Normal ScalingReplicaSet 4m14s deployment-controller Scaled up replica set hello-deploy-7f44bd8b96 to 2 Normal ScalingReplicaSet 2m18s deployment-controller Scaled down replica set hello-deploy-6cd458494 to 7 Normal ScalingReplicaSet 2m18s deployment-controller Scaled up replica set hello-deploy-7f44bd8b96 to 4 Normal ScalingReplicaSet 2m3s deployment-controller Scaled down replica set hello-deploy-6cd458494 to 5 Normal ScalingReplicaSet 2m3s deployment-controller Scaled up replica set hello-deploy-7f44bd8b96 to 6 Normal ScalingReplicaSet 108s deployment-controller Scaled down replica set hello-deploy-6cd458494 to 3 Normal ScalingReplicaSet 82s (x4 over 108s) deployment-controller (combined from similar events): Scaled down replica set hello-deploy-6cd458494 to 0 =====Rolling Back===== Now, before we continue, let's speak a little bit about Change History. We can check the history of all changes as follows: ubuntu@k8s-master:~$ kubectl rollout history deploy hello-deploy deployment.apps/hello-deploy REVISION CHANGE-CAUSE 1 2 The reason why you don't see anything in the "change-cause" is because we didn't use "-record" when we applied the change. But let's reverse our changes and apply them again. We can reverse our changes using the following command: ubuntu@k8s-master:~$ kubectl rollout undo deployment hello-deploy --to-revision=1 deployment.apps/hello-deploy rolled back ubuntu@k8s-master:~$ Of course we can monitor it with the same command as before: ubuntu@k8s-master:~$ kubectl rollout status deploy hello-deploy Waiting for deployment "hello-deploy" rollout to finish: 4 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 4 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 6 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 8 out of 10 new replicas have been updated... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... Waiting for deployment "hello-deploy" rollout to finish: 1 old replicas are pending termination... deployment "hello-deploy" successfully rolled out ubuntu@k8s-master:~$ kubectl describe deploy Name: hello-deploy Namespace: default CreationTimestamp: Sun, 03 May 2020 11:28:33 +0000 Labels: Annotations: deployment.kubernetes.io/revision: 3 Selector: app=hello-date Replicas: 10 desired | 10 updated | 10 total | 10 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 10 RollingUpdateStrategy: 1 max unavailable, 1 max surge Pod Template: Labels: app=hello-date Containers: hello-pod: Image: andonovj/httpserverdemo:latest <- We are back to the old deployment. Port: 1234/TCP Host Port: 0/TCP Environment: Mounts: Volumes: Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: NewReplicaSet: hello-deploy-6cd458494 (10/10 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 45m deployment-controller Scaled up replica set hello-deploy-6cd458494 to 10 Normal ScalingReplicaSet 44m deployment-controller Scaled up replica set hello-deploy-7f44bd8b96 to 1 Normal ScalingReplicaSet 44m deployment-controller Scaled down replica set hello-deploy-6cd458494 to 9 ====Rollupdate with Record==== Let's again apply the RollUpdate WITH the record this time: ubuntu@k8s-master:~$ kubectl apply -f deploy.yml --record deployment.apps/hello-deploy configured ubuntu@k8s-master:~$ ubuntu@k8s-master:~$ kubectl rollout history deploy hello-deploy deployment.apps/hello-deploy REVISION CHANGE-CAUSE 3 4 kubectl apply --filename=deploy.yml --record=true Now, we can see that, the record is there and we can see the history of changes.