{"id":363,"date":"2020-08-17T14:46:46","date_gmt":"2020-08-17T13:46:46","guid":{"rendered":"https:\/\/blog.thomarite.uk\/?p=363"},"modified":"2020-08-17T14:46:46","modified_gmt":"2020-08-17T13:46:46","slug":"docker-kubernetes","status":"publish","type":"post","link":"https:\/\/blog.thomarite.uk\/index.php\/2020\/08\/17\/docker-kubernetes\/","title":{"rendered":"Docker + Kubernetes"},"content":{"rendered":"\n<p>For some time, I wanted to take a look at kubernetes.  There is a lot of talking about microservices in the cloud and after attending some meetups, I wasnt sure what was all this about so I signed for <a href=\"https:\/\/kodekloud.com\/p\/learning-path\">kodekloud<\/a> to learn about it.<\/p>\n\n\n\n<p>So far, I have completed the beginners course for Docker and Kubernetes. To be honest, I think the product is very good value for money.<\/p>\n\n\n\n<p>I have been using docker a bit the last couple of months but still wanted to take a bit more info to improve my knowledge.<\/p>\n\n\n\n<p>I was surprised when reading that kubernets pods rely on docker images. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Docker Notes<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Docker commands<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker run -it xxx (interactive+pseudoterminal)\ndocker run -d xxx (detach)\ndocker attach ID (attach)\ndocker run --name TEST xxx (provide name to container)\ndocker run -p 80:5000 xxx (maps host port 80 to container port 5000)\n\ndocker run -v \/opt\/datadir:\/var\/lib\/mysql mysql (map a host folder to container folder for data persistence)\n\ndocker run -e APP_COLOR=blue xxx (pass env var to the container)\ndocker inspect \"container\"  -> check IP, env vars, etc\ndocker logs \"container\"\n\ndocker build . -t account_name\/app_name\ndocker login\ndocker push account_name\/app_name\n\ndocker -H=remote-docker-engine:2375 xxx\n\ncgroups: restrict resources in container\n  docker run --cpus=.5  xxx (no more than 50% CPU)\n  docker run --memory=100m xxx (no more than 100M memory)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Docker File<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>----\nFROM Ubuntu\nENTRYPOINT &#91;\"sleep\"]\nCMD &#91;\"5\"]        --> if you dont pass any value in \"docker run ..\" it uses by default 5.\n----<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Docker Compose<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cat docker-compose.yml\nversion: \"3\"\nservices:\n db:\n  image: postgres\n  environment:\n    - POSTGRES_PASSWORD=mysecretpassword\n wordpress:\n  image: wordpress\n  links:\n    - db\n  ports:\n    - 8085:80\n\n\nverify file: $ docker-compose config\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Docker Volumes<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>docker volume create NAME  --> create \/var\/lib\/docker\/volumes\/NAME\n\ndocker run -v NAME:\/var\/lib\/mysql mysql  (docker volume)\nor\ndocker run -v PATH:\/var\/lib\/mysql mysql  (local folder)\nor\ndocker run --mount type=bind,source=\/data\/mysql,target=\/var\/lib\/mysql mysql\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Docker Networking<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>networks: --network=xxx\n bridge (default)\n none   isolation\n host (only communication with other containers)   \n\ndocker network create --driver bridge --subnet x.x.x.x\/x NAME\ndocker network ls\n               inspect\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Docker Swarm<\/h3>\n\n\n\n<p>I didnt use this, just had the theory. This is for clustering docker hosts: manager, workers.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>   manager: docker swarm init\n   workers: docker swarm join --token xxx\n   manager: docker service create --replicas=3 my-web-server<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Kubernetes Notes<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\">container + orchestration\n (docker)    (kubernetes)\n\nnode: virtual or physical, kube is installed here\ncluster: set of nodes\nmaster: node that manage clusters\n\nkube components:\n  api,\n  etcd (key-value store),\n  scheduler (distribute load),\n  kubelet (agent),\n  controller (brain: check status),\n  container runtime (sw to run containers: docker)\n\nmaster: api, etcd, controller, scheduler,\n   $ kubectl cluster-info\n           get nodes -o wide (extra info)\nnode: kubelet, container<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Setup Kubernetes with minikube<\/h2>\n\n\n\n<p>Setting up kubernetes doesnt look like an easy task so there are tools to do that like microk8s, kubeadm (my laptop needs more RAM, can&#8217;t handle 1master+2nodes) and <a href=\"https:\/\/kubernetes.io\/docs\/tasks\/tools\/\">minikube<\/a>.<\/p>\n\n\n\n<p>minikube needs: virtualbox(couldnt make it work with kvm2&#8230;) and kubectl<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Install kubectl<\/h3>\n\n\n\n<p>I assume virtualbox is already installed<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ curl -LO \"https:\/\/storage.googleapis.com\/kubernetes-release\/release\/$(curl -s https:\/\/storage.googleapis.com\/kubernetes-release\/release\/stable.txt)\/bin\/linux\/amd64\/kubectl\"\n\n$ chmod +x .\/kubectl\n$ sudo mv .\/kubectl \/usr\/local\/bin\/kubectl\n$ kubectl version --client<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Install minikube<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>$ grep -E --color 'vmx|svm' \/proc\/cpuinfo   \/\/ verify your CPU support \n                                               virtualization\n$ curl -Lo minikube https:\/\/storage.googleapis.com\/minikube\/releases\/latest\/minikube-linux-amd64 \\\n>   &amp;&amp; chmod +x minikube\n$ sudo install minikube \/usr\/local\/bin\/<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Start\/Status minikube<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>$ minikube start --driver=virtualbox  --> it takes time!!!! 2cpu + 2GB ram !!!!\n\ud83d\ude04  minikube v1.12.3 on Debian bullseye\/sid\n\u2728  Using the virtualbox driver based on user configuration\n\ud83d\udcbf  Downloading VM boot image ...\n    > minikube-v1.12.2.iso.sha256: 65 B \/ 65 B &#91;-------------] 100.00% ? p\/s 0s\n    > minikube-v1.12.2.iso: 173.73 MiB \/ 173.73 MiB &#91;] 100.00% 6.97 MiB p\/s 25s\n\ud83d\udc4d  Starting control plane node minikube in cluster minikube\n\ud83d\udcbe  Downloading Kubernetes v1.18.3 preload ...\n    > preloaded-images-k8s-v5-v1.18.3-docker-overlay2-amd64.tar.lz4: 510.91 MiB\n\ud83d\udd25  Creating virtualbox VM (CPUs=2, Memory=2200MB, Disk=20000MB) ...\n\ud83d\udc33  Preparing Kubernetes v1.18.3 on Docker 19.03.12 ...\n\ud83d\udd0e  Verifying Kubernetes components...\n\ud83c\udf1f  Enabled addons: default-storageclass, storage-provisioner\n\ud83c\udfc4  Done! kubectl is now configured to use \"minikube\"\n\n$ minikube status\nminikube\ntype: Control Plane\nhost: Running\nkubelet: Running\napiserver: Running\nkubeconfig: Configured\n\n$ kubectl get nodes\nNAME       STATUS   ROLES    AGE     VERSION\nminikube   Ready    master   5m52s   v1.18.3\n\n$ minikube stop  \/\/ stop the virtualbox VM to free up resources once you finish<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Basic Test<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>$ kubectl create deployment hello-minikube --image=k8s.gcr.io\/echoserver:1.10\ndeployment.apps\/hello-minikube created\n\n$ kubectl get deployments\nNAME             READY   UP-TO-DATE   AVAILABLE   AGE\nhello-minikube   1\/1     1            1           22s\n\n$ kubectl expose deployment hello-minikube --type=NodePort --port=8080\nservice\/hello-minikube exposed\n\n$ minikube service hello-minikube --url\nhttp:&#47;&#47;192.168.99.100:30020\n\n$ kubectl delete services hello-minikube\n$ kubectl delete deployment hello-minikube\n$ kubectl get pods\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Pods<\/h3>\n\n\n\n<p>Based on <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/pods\/\">documentation<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><em>Pods<\/em>\u00a0are the smallest deployable units of computing that you can create and manage in Kubernetes.\nA\u00a0<em>Pod<\/em>\u00a0is a group of one or more\u00a0<a rel=\"noreferrer noopener\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/overview\/what-is-kubernetes\/#why-containers\" target=\"_blank\">containers<\/a>, with shared storage\/network resources, and a specification for how to run the containers. A Pod's contents are always co-located and co-scheduled, and run in a shared context. A Pod models an application-specific \"logical host\": it contains one or more application containers which are relatively tightly coupled. In non-cloud contexts, applications executed on the same physical or virtual machine are analogous to cloud applications executed on the same logical host.<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>$ kubectl run nginx --image=nginx\n$ kubectl describe pod nginx\n$ kubectl get pods -o wi\n$ kubectl delete pod nginx<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Pods &#8211; Yaml<\/h2>\n\n\n\n<p>Pod yaml structure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pod-definition.yml:\n---\napiVersion: v1\nkind: (type of object: Pod, Service, ReplicatSet, Deployment)\nmetadata: (only valid k-v)\n name: myapp-pod\n labels: (any kind of k-v)\n   app: myapp\n   type: front-end\nspec:\n  containers:\n   - name: nginx-container\n     image: nginx\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cat pod.yaml \napiVersion: v1\nkind: Pod\nmetadata:\n  name: nginx\n  labels:\n    app: nginx\n    type: frontend\nspec:\n  containers:\n  - name: nginx\n    image: nginx\n\n$ kubectl apply -f pod.yaml \n$ kubectl get pods<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Replica-Set<\/h3>\n\n\n\n<p>Based on <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/replicaset\/\">documentation<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">A ReplicaSet's purpose is to maintain a stable set of replica Pods running at any given time. As such, it is often used to guarantee the availability of a specified number of identical Pods.<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>> cat replicaset-definition.yml\n---\n apiVersion: apps\/v1\n kind: ReplicaSet\n metadata:\n   name: myapp-replicaset\n   labels:\n     app: myapp\n     type: front-end\n spec:\n   template:\n     metadata:      -------\n       name: nginx         |\n       labels:             |\n         app: nginx        |\n         type: frontend    |-> POD definition\n     spec:                 |\n       containers:         |\n       - name: nginx       |\n         image: nginx  -----\n   replicas: 3\n   selector:       &lt;-- main difference from replication-controller\n     matchLabels:\n       type: front-end\n       \n> kubectl create -f replicaset-definition.yml\n> kubectl get replicaset\n> kubectl get pods\n\n> kubectl delete replicaset myapp-replicaset\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">How to scale via replica-set<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>> kubectl replace -f replicaset-definition.yml  (first update file to replicas: 6)\n\n> kubectl scale --replicas=6 -f replicaset-definition.yml  \/\/ no need to modify file\n\n> kubectl scale --replicas=6 replicaset myapp-replicaset   \/\/ no need to modify file\n\n> kubectl edit replicaset myapp-replicaset (NAME of the replicaset!!!)\n\n> kubectl describe replicaset myapp-replicaset\n\n> kubectl get rs new-replica-set -o yaml > new-replica-set.yaml ==> returns the rs definition in yaml!\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Deployments<\/h3>\n\n\n\n<p>Based on <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/deployment\/\">documentation<\/a>: <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">A\u00a0<em>Deployment<\/em>\u00a0provides declarative updates for\u00a0<a rel=\"noreferrer noopener\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/pods\/\" target=\"_blank\">Pods<\/a>\u00a0<a rel=\"noreferrer noopener\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/replicaset\/\" target=\"_blank\">ReplicaSets<\/a>.\nYou describe a\u00a0<em>desired state<\/em>\u00a0in a Deployment, and the Deployment\u00a0<a rel=\"noreferrer noopener\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/architecture\/controller\/\" target=\"_blank\">Controller<\/a>\u00a0changes the actual state to the desired state at a controlled rate. You can define Deployments to create new ReplicaSets, or to remove existing Deployments and adopt all their resources with new Deployments.<\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat deployment-definition.yml\n---\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: myapp-deployment\n  labels:\n    app: myapp\n    type: front-end\nspec:\n  template:\n    metadata:\n      name: myapp-pod\n      labels:\n        app: myapp\n        type: front-end\n    spec:\n      containers:\n      - name: nginx-controller\n        image: nginx\n  replicas: 3\n  selector:\n    matchLabels:\n      type: front-end\n\n\n> kubectl create -f deployment-definition.yml\n> kubectl get deployments\n> kubectl get replicaset\n> kubectl get pods\n\n> kubectl get all\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Update\/Rollback<\/h3>\n\n\n\n<p>From <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/controllers\/deployment\/#updating-a-deployment\">documentation<\/a>.<\/p>\n\n\n\n<p>By default, it follows a &#8220;rolling update&#8221;: destroy one, create new one. So this doesnt cause an outage<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ kubectl create -f deployment.yml --record\n$ kubectl rollout status deployment\/myapp-deployment\n$ kubectl rollout history deployment\/myapp-deployment\n$ kubectl rollout undo deployment\/myapp-deployment ==> rollback!!!<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Networking<\/h3>\n\n\n\n<p>Not handled natively by kubernetes, you need another tool like calico, weave, etc. More info <a href=\"https:\/\/rancher.com\/blog\/2019\/2019-03-21-comparing-kubernetes-cni-providers-flannel-calico-canal-and-weave\/\">here<\/a>. This has not been covered in details yet. It looks complex (a network engineer talking&#8230;)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Services<\/h3>\n\n\n\n<p>Based on <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/services-networking\/service\/\">documentation<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">An abstract way to expose an application running on a set of\u00a0<a rel=\"noreferrer noopener\" href=\"https:\/\/kubernetes.io\/docs\/concepts\/workloads\/pods\/\" target=\"_blank\">Pods<\/a>\u00a0as a network service.\nWith Kubernetes you don't need to modify your application to use an unfamiliar service discovery mechanism. Kubernetes gives Pods their own IP addresses and a single DNS name for a set of Pods, and can load-balance across them.\n<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">types:\n   NodePort: like docker port-mapping\n   ClusterIP:\n   LoadBalancer<\/pre>\n\n\n\n<p>Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nodeport\n--------\nservice: like a virtual server\n  targetport - in the pod: 80\n  service - 80\n  nodeport: 30080 (in the node)\n  \nservice-definition.yml\napiVersion: v1\nkind: Service\nmetadata:\n  name: mypapp-service\nspec:\n  type: NodePort\n  ports:\n  - targetPort: 80\n    port: 80\n    nodePort: 30080  (range: 30000-32767)\n  selector:\n    app: myapp\n    type: front-end\n\n> kubectl create -f service-definition.yml\n> kubectl get services\n> minikube service mypapp-service\n\n\nclusterip: \n---------\nservice-definition.yml\napiVersion: v1\nkind: Service\nmetadata:\n  name: back-end\nspec:\n  type: ClusterIP\n  ports:\n  - targetPort: 80\n    port: 80\n  selector:\n    app: myapp\n    type: back-end\n\n\nloadbalance: gcp, aws, azure only !!!!\n-----------\nservice-definition.yml\napiVersion: v1\nkind: Service\nmetadata:\n  name: back-end\nspec:\n  type: LoadBalancer\n  ports:\n  - targetPort: 80\n    port: 80\n    nodePort: 30080\n  selector:\n    app: myapp\n\n\n> kubectl create -f service-definition.yml\n> kubectl get services\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Microservices architecture example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Diagram\n=======\n\nvoting-app     result-app\n (python)       (nodejs)\n   |(1)           ^ (4)\n   v              |\nin-memoryDB       db\n (redis)       (postgresql)\n    ^ (2)         ^ (3)\n    |             |\n    ------- -------\n          | |\n         worker\n          (.net)\n\n1- deploy containers -> deploy PODs (deployment)\n2- enable connectivity -> create service clusterIP for redis\n                          create service clusterIP for postgres\n3- external access -> create service NodePort for voting\n                      create service NodePort for result\n<\/code><\/pre>\n\n\n\n<p>Code <a href=\"https:\/\/github.com\/thomarite\/voting-app\">here<\/a>. Steps:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ kubectl create -f voting-app-deployment.yml\n$ kubectl create -f voting-app-service.yml\n\n$ kubectl create -f redis-deployment.yml\n$ kubectl create -f redis-service.yml\n\n$ kubectl create -f postgres-deployment.yml\n$ kubectl create -f postgres-service.yml\n\n$ kubectl create -f worker-deployment.yml\n\n$ kubectl create -f result-app-deployment.yml\n$ kubectl create -f result-app-service.yml<\/pre>\n\n\n\n<p>Verify:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ minikube service voting-service --url\nhttp:\/\/192.168.99.100:30004\n$ minikube service result-service --url\nhttp:\/\/192.168.99.100:30005<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>For some time, I wanted to take a look at kubernetes. There is a lot of talking about microservices in the cloud and after attending some meetups, I wasnt sure what was all this about so I signed for kodekloud to learn about it. So far, I have completed the beginners course for Docker and &hellip; <a href=\"https:\/\/blog.thomarite.uk\/index.php\/2020\/08\/17\/docker-kubernetes\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Docker + Kubernetes&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21],"tags":[],"class_list":["post-363","post","type-post","status-publish","format-standard","hentry","category-automation"],"_links":{"self":[{"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/posts\/363","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/comments?post=363"}],"version-history":[{"count":1,"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/posts\/363\/revisions"}],"predecessor-version":[{"id":364,"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/posts\/363\/revisions\/364"}],"wp:attachment":[{"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/media?parent=363"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/categories?post=363"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.thomarite.uk\/index.php\/wp-json\/wp\/v2\/tags?post=363"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}