{"id":13630211,"url":"https://github.com/rosehgal/k8s-In-30Mins","last_synced_at":"2025-04-17T13:31:49.397Z","repository":{"id":50321918,"uuid":"298191245","full_name":"rosehgal/k8s-In-30Mins","owner":"rosehgal","description":"Learn how to set up the Kubernetes cluster in 30 mins and deploy the application inside the cluster.","archived":false,"fork":false,"pushed_at":"2021-03-18T12:05:22.000Z","size":435,"stargazers_count":210,"open_issues_count":0,"forks_count":16,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-13T21:02:02.539Z","etag":null,"topics":["hacktoberfest","kubectl","kubernetes","kubernetes-cluster","kubernetes-in-30-minutes","kubernetes-learning","kubernetes-pods","kubernetes-resource","stateful-workloads","stateless-workloads"],"latest_commit_sha":null,"homepage":"","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rosehgal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-24T06:41:51.000Z","updated_at":"2024-11-13T17:25:47.000Z","dependencies_parsed_at":"2022-09-05T23:40:08.939Z","dependency_job_id":null,"html_url":"https://github.com/rosehgal/k8s-In-30Mins","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rosehgal%2Fk8s-In-30Mins","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rosehgal%2Fk8s-In-30Mins/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rosehgal%2Fk8s-In-30Mins/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rosehgal%2Fk8s-In-30Mins/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rosehgal","download_url":"https://codeload.github.com/rosehgal/k8s-In-30Mins/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249344827,"owners_count":21254745,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","kubectl","kubernetes","kubernetes-cluster","kubernetes-in-30-minutes","kubernetes-learning","kubernetes-pods","kubernetes-resource","stateful-workloads","stateless-workloads"],"created_at":"2024-08-01T22:01:33.883Z","updated_at":"2025-04-17T13:31:48.996Z","avatar_url":"https://github.com/rosehgal.png","language":"Dockerfile","funding_links":[],"categories":["Dockerfile"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"files/static/k8logo.png\" height=\"30%\" width=\"30%\" /\u003e\n\u003cbr\u003e\n\u003cimg src=\"files/static/in.png\" height=\"30%\" width=\"30%\" /\u003e\n\u003cbr\u003e\n\u003cimg alt=\"GitHub release (latest by date)\" src=\"https://img.shields.io/github/v/release/r0hi7/k8s-In-30Mins\"\u003e\n\u003cimg alt=\"GitHub code size in bytes\" src=\"https://img.shields.io/github/languages/code-size/r0hi7/k8s-In-30Mins\"\u003e\n\u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/r0hi7/k8s-In-30Mins\"\u003e\n\u003cbr\u003e\n\u003cimg alt=\"GitHub issues\" src=\"https://img.shields.io/github/issues-raw/r0hi7/k8s-In-30Mins\"\u003e\n\u003cimg alt=\"GitHub stars\" src=\"https://img.shields.io/github/stars/r0hi7/k8s-In-30Mins\"\u003e\n\u003cimg alt=\"Twitter Follow\" src=\"https://img.shields.io/twitter/follow/sec_r0?style=social\"\u003e\n\u003cimg alt=\"GitHub followers\" src=\"https://img.shields.io/github/followers/r0hi7?style=social\"\u003e\n\u003cbr\u003e\n\u003ca target=\"_blank\" href=\"https://twitter.com/intent/tweet?text=Learn Kubernetes in 30 mins, with single node cluster @sec_r0. https://github.com/r0hi7/k8s-In-30Mins\" title=\"Share on Twitter\"\u003e\u003cimg src=\"https://img.shields.io/twitter/url/http/shields.io.svg?style=social\u0026label=Share%20on%20Twitter\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n# K8s in 30 mins\nThis is not a comprehensive guide to learn Kubernetes from scratch, rather this is just a small guide/cheat sheet to quickly setup and run applications with Kubernetes and deploy a very simple application on single workload VM. This repo can be served as quick learning manual to understand Kubernetes.\n\n#### Prerequisite\n- [Linux](https://files.fosswire.com/2007/08/fwunixref.pdf)\n- [Dockers](https://docs.docker.com/develop/)\n- [YAML](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html)\n\n## Table of Contents:\n1. [**Setting up Kubernetes cluster in VM (NOT MINIKUBE)**](#Setting-up-Kubernetes-cluster-in-VM) : 1 VM cluster\n    - Spining up a virtual machine with [**Vagrant**](https://www.vagrantup.com/docs/installation) : 2GB RAM + 2CPU cores (at least)\n    - [Understanding](#What-are-kube):\n        - **kubeadm**\n        - **kubelet**\n        - **kubectl**\n1. [**Kuberenetes pods**](#Kubernetes-pods): How are they different than Docker containers.\n    - [Moving from Docker container to Kubernetes pods.](#how-to-create-a-pod)\n1. [**Kubernetes Resource**](#kubernetes-resources):\n    - [Pods](#pods)\n    - [Deployment](#deployments)\n    - [Replicaset](#replicasets)\n    - [Services](#services)\n        - [LoadBalancer Service](#loadbalancer-service)\n1. [**Kubernetes network manager**](#kubernetes-network-manager)\n    - I will pick up the plugin called [Flannel](https://github.com/coreos/flannel#flannel).\n1. [**Stateless Workload**](#stateless-workloads)\n    - Replicasets \u0026 Deployments\n1. [**Stateful Workloads**](#stateful-workloads)\n    - [Persistent Volumes](#persistent-volumes)\n    - [Persistent Volume Claims](#persistent-volume-claims)\n1. [**Deploying End-to-End Service in Kubernetes cluster**](#sample-application-example)\n    - [MySQL](#mysql-resource):\n        - [PV](#step-1-create-pv-for-mysql-db)\n        - [PVC](#step-2-create-pvc-for-pv)\n        - [Deployment](#step-3-create-mysql-deployment-spec)\n        - [Service](#step-4-expose-mysql-server-via-service)\n    - [Sprinboot Application](#springboot-application):\n        - Stateless Workload\n            - [Custom Docker Image](files/Dockerfile)\n        - [Replica based Deployment \u0026 link with DB Service](#step-1-build-and-deploy-appserver)\n        - [Access Springboot Service outside Pods](#step-2-expose-appserver-service-via-service-type-lb-to-host)\n    - [Infrastructure as a Code](#Infrastructure-as-a-code) \n        - [MySQL Full Spec](#mysql-full-spec)\n        - [AppServer Full Spec](#appserver-full-spec)\n1. [**Understanding** advance kubernetes resources](#advance-kubernetes-resources):\n    - [Namespaces](#namespaces)\n        - [Create Namespace and Add Resource](#creating-namespace-\u0026-adding-resource) \n    - [Context](#context)\n1. [**Cheat sheet**](#cheat-sheet)\n1. [**Next steps**](#next-steps)\n\n\n\n## Setting up Kubernetes cluster in VM\n1. Download the Vagrant [File](Vagrantfile).\n1. Download Virtual box and install from [here](https://www.virtualbox.org/).\n1. Download and install [Vagrant](https://www.vagrantup.com/downloads).\n1. In the terminal, run the two command to get the VM up and running, with out any configuration :smile:\n    ```bash\n    # In the same directory where you have downloaded Vagrantfile, run\n    vagrant up\n    vagrant ssh\n    ```\n    This will download the Ubuntu box image and do the entire setup for you with the help of virtual box. It just need virtual box installed.\n1. The Vagrantfile comes preconfigured with **kubeadm**, **kubelet**, **kubectl**\n1. Check if kubernetes cluster is perfectly installed.\n    ```bash\n    root@vagrant:/home/vagrant# kubectl version -o json\n    {\n      \"clientVersion\": {\n        \"major\": \"1\",\n        \"minor\": \"19\",\n        \"gitVersion\": \"v1.19.2\",\n        \"gitCommit\": \"f5743093fd1c663cb0cbc89748f730662345d44d\",\n        \"gitTreeState\": \"clean\",\n        \"buildDate\": \"2020-09-16T13:41:02Z\",\n        \"goVersion\": \"go1.15\",\n        \"compiler\": \"gc\",\n        \"platform\": \"linux/amd64\"\n      },\n      \"serverVersion\": {\n        \"major\": \"1\",\n        \"minor\": \"19\",\n        \"gitVersion\": \"v1.19.2\",\n        \"gitCommit\": \"f5743093fd1c663cb0cbc89748f730662345d44d\",\n        \"gitTreeState\": \"clean\",\n        \"buildDate\": \"2020-09-16T13:32:58Z\",\n        \"goVersion\": \"go1.15\",\n        \"compiler\": \"gc\",\n        \"platform\": \"linux/amd64\"\n      }\n    }\n    ```\n1. Start the Kubernetes cluster master node.\n    ```bash\n    # This will spin up Kubernetes cluster with CIDR: 10.244.0.0/16\n    root@vagrant:/home/vagrant# kubeadm init --pod-network-cidr=10.244.0.0/16\n    kubeadm join 10.0.2.15:6443 --token 3m5dsc.toup1iv7670ya7wc --discovery-token-ca-cert-hash sha256:73f4983d43f9618522eaccf014205f969e3bacd76c98dd0c\n\n    root@vagrant:/home/vagrant# mkdir -p $HOME/.kube\n    root@vagrant:/home/vagrant# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config\n    root@vagrant:/home/vagrant# sudo chown $(id -u):$(id -g) $HOME/.kube/config\n    ```\n1. Conenct other VM to this cluster: Not required in case of single VM cluster. For this run perfectly, make sure:\n    - VM to VM connectivity is there.\n    - All there kube-* are installed in VM.\n    ```bash\n    kubeadm join 10.0.2.15:6443 --token 3m5dsc.toup1iv7670ya7wc --discovery-token-ca-cert-hash sha256:73f4983d43f9618522eaccf014205f969e3bacd76c98dd0c\n    ```\n1. At this point, Kubernetes is installed and cluster master is up, but still we need a agent to provision and manager network for new nodes for us, This is where [Flannel](https://github.com/coreos/flannel#flannel) comes to rescue. Install Flannel to manager docker network for pods.\n    ```bash\n    kubectl apply -f \\\n        https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml\n    ```\n1. This step applies, if we wish to use, **our master node as worker as well**. Which is yes in our case:\n    ```\n    root@vagrant:/home/vagrant# kubectl taint nodes $(hostname) node-role.kubernetes.io/master:NoSchedule-\n\n    # If everything goes well, you will see something like this.\n    root@vagrant:/home/vagrant# kubectl get node\n    NAME      STATUS   ROLES    AGE     VERSION\n    vagrant   Ready    master   3m40s   v1.19.2\n    ```\n\n\n\u003e Run all the commands from root shell.\n\n\n### What are kube*\nKubernetes runs in client server model, similar to the way the docker runs. Kubernetes server exposes kubernetes-api, and each of kubeadm, kubelet and kubectl connect with this kubernetes server api to get the task done. In the master slave model, there are two entities: \n- Control Plane\n- Worker Nodes\n\n**Control Plane** : Connects with Worker nodes for resource allocation.  \n**Worker nodes**  : Cluster entitiy that actually allocates tasks and run Pods. \n\n1. **kubeadm**:\n    - Sets-up the cluster\n    - Connect various worker nodes togather.\n2. **kubectl**:\n    - It is a client cli.\n    - Connects with control plane kubernetes api server and send execution requests to control plane.\n3. **kubelet**:\n    - Receives request from control planes.\n    - Runs in Worker nodes.\n    - Runs task over worker nodes.\n    - Maintain Pod lifecycle. Not just for pods, but all Kubernetes resources lifecycle.\n\n## Kubernetes pods\n\n- Pods run multiple containers.\n- Pods abstract out multilpe containers into single unit.\n- If two service in pods are both exposing service on same port, the other one wont spin up and it will fail.\n- The unit of Kubernetes work load is called Pod. \n\n### How to create a pod\nYou can create a simple nginx pod with following yaml spec. Save this in file name : [pod.yml](files/pod.yml)\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: nginx\nspec:\n  containers:\n  - name: nginx\n    image: nginx\n```\n|Key name| Key Description|\n|---|---|\n|`apiVersion`|Kubernetes server API|  \n|`kind`|Kubernetes Resource type: `Pod`|  \n|`metadata.name`|Name of Kubernetes Pod|\n|`spec.container.name`|Name of Container which will run in a Pod|\n|`spec.container.name`|Name of docker image to run| \n\nRun this Pod spec with. `kubectl apply -f pod.yml`\n```bash\nroot@vagrant:/home/vagrant/kubedata# kubectl apply -f pod.yaml\npod/nginx created\n\n# If everything goes OK, you will se something like this.\n\nroot@vagrant:/home/vagrant/kubedata# kubectl get pods\nNAME    READY   STATUS    RESTARTS   AGE\nnginx   1/1     Running   0          43s\nroot@vagrant:/home/vagrant/kubedata#\n```\nUse : ` kubectl get pods` to get the list of all Pods.\n\n1. Running command into container, running inside Pod. `kubectl exec -it \u003cpod_name\u003e -c \u003ccontainer_name\u003e -- \u003ccommand\u003e`\n    ```\n    root@vagrant:/home/vagrant/kubedata# kubectl exec -it nginx -c nginx -- whoami\n    root\n\n    root@vagrant:/home/vagrant/kubedata# kubectl exec -it nginx -c nginx -- /bin/sh\n    # cat /etc/*-release\n    PRETTY_NAME=\"Debian GNU/Linux 10 (buster)\"\n    NAME=\"Debian GNU/Linux\"\n    VERSION_ID=\"10\"\n    VERSION=\"10 (buster)\"\n    VERSION_CODENAME=buster\n    ID=debian\n    HOME_URL=\"https://www.debian.org/\"\n    SUPPORT_URL=\"https://www.debian.org/support\"\n    BUG_REPORT_URL=\"https://bugs.debian.org/\"\n    ```\n1. Running multiple container in one pod.\n    ```yaml\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n      - name: curl\n        image: appropriate/curl\n        stdin: true\n        tty: true\n        command: [\"/bin/sh\"]\n    ```\n    Save this into [pod-with-two-containers.yml](files/pod-with-two-containers.yml).  \n    Run this : `kubectl apply -f pod-with-two-containers.yml`\n1. Delete a running pod. `kubectl delete -f pod-with-two-containers.yml`. This will remove the pod mentioned in spec file.\n1. Container in a Pod can connect to another container in same pod with `spec.containers.name`.\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl exec -it nginx -c curl -- /bin/sh\n    # curl nginx\n    \u003c!DOCTYPE html\u003e\n    \u003chtml\u003e\n    \u003chead\u003e\n    \u003ctitle\u003eWelcome to nginx!\u003c/title\u003e\n    \u003cstyle\u003e\n        body {\n            width: 35em;\n            margin: 0 auto;\n            font-family: Tahoma, Verdana, Arial, sans-serif;\n        }\n    \u003c/style\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n    \u003ch1\u003eWelcome to nginx!\u003c/h1\u003e\n    \u003cp\u003eIf you see this page, the nginx web server is successfully installed and\n    working. Further configuration is required.\u003c/p\u003e\n\n    \u003cp\u003eFor online documentation and support please refer to\n    \u003ca href=\"http://nginx.org/\"\u003enginx.org\u003c/a\u003e.\u003cbr/\u003e\n    Commercial support is available at\n    \u003ca href=\"http://nginx.com/\"\u003enginx.com\u003c/a\u003e.\u003c/p\u003e\n\n    \u003cp\u003e\u003cem\u003eThank you for using nginx.\u003c/em\u003e\u003c/p\u003e\n    \u003c/body\u003e\n    \u003c/html\u003e\n    #\n    ```\n\n## Kubernetes Resources\n\n### Pods\n- Fundamental unit of k8s cluster.\n- Abstraction for container/multiple-containers, running under single name.\n- Discussed in detail : [here](#how-to-create-a-pod)\n\n### Deployments\n- A Deployment provides declarative updates for Pods.\n- The configuration state in `yml` file, defines how the pods will run in cluster. They can specify:\n    - Replicas\n    - Resource allocation\n    - Connection with Volumes etc.\n    - We will see example once we see [replicasets](#replicasets)\n\n### Replicasets\n1. Run deployments in replicas.\n2. Create [file](files/deployment-replica.yml) with following specification.\n    ```yaml\n    apiVersion: apps/v1\n    \n    kind: Deployment\n    metadata:\n      name: nginx\n\n    spec:\n      replicas: 3\n      selector:\n        matchLabels:\n          app: nginx-app\n      template:\n        metadata:\n          labels:\n            app: nginx-app\n        spec:\n          containers:\n          - name: nginx\n            image: nginx\n    ```\n\n    Notice the difference.\n\n    ```diff\n    -- kind: Pod\n    ++ kind: Deployment\n\n    ++ spec:\n    ++  replicas: 3\n    ++  selector:\n    ++    matchLabels:\n    ++      app: nginx-app\n    ```\n3. Remove existing pods(if any) `kubectl delete pods --all`, and create deployment.\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl apply -f deployment-replica.yml\n    deployment.apps/nginx created\n\n    root@vagrant:/home/vagrant/kubedata# kubectl get deployments\n    NAME    READY   UP-TO-DATE   AVAILABLE   AGE\n    nginx   0/3     3            0           7s\n\n    root@vagrant:/home/vagrant/kubedata# kubectl get deployments -w\n    NAME    READY   UP-TO-DATE   AVAILABLE   AGE\n    nginx   1/3     3            1           14s\n    nginx   2/3     3            2           20s\n    ```\n4. Get the list of all `deployments`: `kubectl get deployments` or `kubectl get deploy`\n4. Get the list of all `replicaset` : `kubectl get replicaset` or ` kubectl get rs`\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl get pods\n    NAME                    READY   STATUS    RESTARTS   AGE\n    nginx-d6ff45774-f84l8   1/1     Running   0          4m59s\n    nginx-d6ff45774-gzxfz   1/1     Running   0          4m59s\n    nginx-d6ff45774-t69mw   1/1     Running   0          4m59s\n\n    root@vagrant:/home/vagrant/kubedata# kubectl get deploy\n    NAME    READY   UP-TO-DATE   AVAILABLE   AGE\n    nginx   3/3     3            3           162m\n\n    root@vagrant:/home/vagrant/kubedata# kubectl get replicaset\n    NAME              DESIRED   CURRENT   READY   AGE\n    nginx-d6ff45774   3         3         3       162m\n\n    root@vagrant:/home/vagrant/kubedata#\n    ```\n5. Print a detailed description of the selected resources, including related resources such as events or controllers: `kubectl describe \u003cresource_type\u003e \u003cresouce_name\u003e`\n6. Get deployment configuration in `JSON` format: `kubectl get deployment nginx -o yaml`.\n\n### Services\n- Logical abstraction of Pods and policies to access them.\n- They enable loose coupling between dependent Pods. e.g\n    - Open Ports.\n    - Security Policies between Pod interaction etc.\n- Can be created independent of Pod declaration, but usually services linked to one Pod are present in same spec file.\n- Lets create a simple service to expose nginx service port to host machine. [File](files/nginx-service.yml)\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx-app\n  template:\n    metadata:\n      labels:\n        app: nginx-app\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: nginx\nspec:\n  selector:\n    app: nginx-app\n  ports:\n  - protocol: TCP\n    port: 80\n    targetPort: 80\n```\n- Service declaration starts by augmenting exiting deployment/pod spec with `---`.\n- Service and Pod can share same names.\n    - Each different resource must have unique amongst themselves.\n- The above service, exposes port 80 on host specified by `spec.ports.port` to port 80 of target pod specified by `spec.ports.taregtPort`\n\n```bash\nroot@vagrant:/home/vagrant/kubedata# kubectl apply -f nginx-service.yml\ndeployment.apps/nginx unchanged\nservice/nginx created\n\nroot@vagrant:/home/vagrant/kubedata#\n```\n- Once the service is created:\n    - Run : `kubectl get services` to get the list of services.\n        ```bash\n        root@vagrant:/home/vagrant/kubedata# kubectl get services\n        NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE\n        kubernetes   ClusterIP   10.96.0.1        \u003cnone\u003e        443/TCP   2d5h\n        nginx        ClusterIP   10.104.178.240   \u003cnone\u003e        80/TCP    49s\n        ```\n        Cluster IP is the IP interface of Pod anstraction on host. `curl` cluster IP will connect us to the Pod.\n        ```bash\n        root@vagrant:/home/vagrant/kubedata# curl 10.104.178.240\n        \u003c!DOCTYPE html\u003e\n        \u003chtml\u003e\n        \u003chead\u003e\n        \u003ctitle\u003eWelcome to nginx!\u003c/title\u003e\n        \u003cstyle\u003e\n            body {\n                width: 35em;\n                margin: 0 auto;\n                font-family: Tahoma, Verdana, Arial, sans-serif;\n            }\n        \u003c/style\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n        \u003ch1\u003eWelcome to nginx!\u003c/h1\u003e\n        \u003cp\u003eIf you see this page, the nginx web server is successfully installed and\n        working. Further configuration is required.\u003c/p\u003e\n\n        \u003cp\u003eFor online documentation and support please refer to\n        \u003ca href=\"http://nginx.org/\"\u003enginx.org\u003c/a\u003e.\u003cbr/\u003e\n        Commercial support is available at\n        \u003ca href=\"http://nginx.com/\"\u003enginx.com\u003c/a\u003e.\u003c/p\u003e\n\n        \u003cp\u003e\u003cem\u003eThank you for using nginx.\u003c/em\u003e\u003c/p\u003e\n        \u003c/body\u003e\n        \u003c/html\u003e\n        ```\n    - Run : `kubectl get endpoints` or `kubectl get ep` to get list of exposed endpoints.\n        ```bash\n        root@vagrant:/home/vagrant/kubedata# kubectl get ep\n        NAME         ENDPOINTS                                    AGE\n        kubernetes   10.0.2.15:6443                               2d5h\n        nginx        10.244.0.10:80,10.244.0.8:80,10.244.0.9:80   2m\n        ```\n        Since I am running 3 different replicas, we are seeing 3 different Pod IPs.\n\n#### Loadbalancer Service\n- Notice External IP in:\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl get services\n    NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE\n    kubernetes   ClusterIP   10.96.0.1        \u003cnone\u003e        443/TCP   2d5h\n    nginx        ClusterIP   10.104.178.240   \u003cnone\u003e        80/TCP    49s\n    ```\n- Since we are running this in local setup, we dont have any **CCM**(Cloud Config manager), which can provision external IP for us to connect to the service running inside the Pod.\n    - In case of Azure or AWS Cloud providers, the CCM provisions and links external IPs for us.\n- So lets do a **hack** here.\n    - Update nginx service to LoadBalancer. [File](files/nginx-service-lb.yml)\n        ```yaml\n        apiVersion: v1\n        kind: Service\n        metadata:\n          name: nginx\n        spec:\n          type: LoadBalancer\n          selector:\n            app: nginx-app\n          ports:\n          - protocol: TCP\n            port: 80\n            targetPort: 80\n        ```\n        Notice:\n        ```diff\n        spec:\n        ++ type: LoadBalancer\n        ```\n    - Apply the config:  `kubectl apply -f nginx-service-lb.yml`\n        ```bash\n        root@vagrant:/home/vagrant/kubedata# kubectl get svc\n        NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE\n        kubernetes   ClusterIP      10.96.0.1        \u003cnone\u003e        443/TCP        2d5h\n        nginx        LoadBalancer   10.104.178.240   \u003cpending\u003e     80:32643/TCP   17m\n        ```\n        Now the state is pending :) \n    - Run `netstat -nltp`, and notice the `kube-proxy`\n        ```diff\n        ++ tcp        0      0 0.0.0.0:32643           0.0.0.0:*               LISTEN      13095/kube-proxy\n           tcp        0      0 127.0.0.1:10248         0.0.0.0:*               LISTEN      7024/kubelet\n        ++ tcp        0      0 127.0.0.1:10249         0.0.0.0:*               LISTEN      13095/kube-proxy\n        ```\n        See the magic.\n        ```bash\n        root@vagrant:/home/vagrant/kubedata# curl 0.0.0.0:32643\n        \u003c!DOCTYPE html\u003e\n        \u003chtml\u003e\n        \u003chead\u003e\n        \u003ctitle\u003eWelcome to nginx!\u003c/title\u003e\n        \u003cstyle\u003e\n            body {\n                width: 35em;\n                margin: 0 auto;\n                font-family: Tahoma, Verdana, Arial, sans-serif;\n            }\n        \u003c/style\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n        \u003ch1\u003eWelcome to nginx!\u003c/h1\u003e\n        \u003cp\u003eIf you see this page, the nginx web server is successfully installed and\n        working. Further configuration is required.\u003c/p\u003e\n\n        \u003cp\u003eFor online documentation and support please refer to\n        \u003ca href=\"http://nginx.org/\"\u003enginx.org\u003c/a\u003e.\u003cbr/\u003e\n        Commercial support is available at\n        \u003ca href=\"http://nginx.com/\"\u003enginx.com\u003c/a\u003e.\u003c/p\u003e\n\n        \u003cp\u003e\u003cem\u003eThank you for using nginx.\u003c/em\u003e\u003c/p\u003e\n        \u003c/body\u003e\n        \u003c/html\u003e\n        ```\n        - The `LoadBalancer` exposed the service endpoints out of Kubernetes cluster IP interface and in our vagrant host we can access it now directly :) \n        - The next challenge to to expose this `kube-proxy` interface to host machine. And hack is done, then we can access the service running in Pod(replica set deployment) from our host interface directly.\n        - This is how the network now looks like. The port `32643` is not exposed through kube-proxy over host/control-plane node.\n            ```bash\n                                                              Kubernetes Cluster\n                                               +---------------------------------------------+\n                                               |                               POD           |\n                                               |                           +---------+       |\n                                               |                    +------\u003e  NGINX  |       |\n                                               |                    |      +---------+       |\n                                               |           LB       |                        |\n                         +--------------+      |    +---------------+          POD           |\n            0.0.0.0:32643|  Kube Proxy  |80    |    |               |      +---------+       |\n                    \u003c------------------\u003e-----------\u003e+    SERVICE    +------\u003e  NGINX  |       |\n                         |              |      |  80|               |      +---------+       |\n                         +--------------+      |    +---------------+                        |\n                               HOST            |                    |          POD           |\n                                               |                    |      +---------+       |\n                                               |                    +------\u003e  NGINX  |       |\n                                               |                           +---------+       |\n                                               +---------------------------------------------+\n            ```\n\n## Stateless workloads\n- Deployments and Replicasets that we had deployed so far are stateless workloads.\n- There is no state related information stored at Pods/Service, so request from kube-proxy via serivce resource can be routed to any of the Pod in the cluster.\n- This constitutes stateless workload.\n- Next section is to create a Stateful workload.\n\n## Stateful workloads\n- Preserve the state of data present on Pods.\n- Two situations can be possible:\n    - Multi pod stateful workload\n        - If multiple pods are connecting to stateful workload, there should be worker based synchronization\n        - Else, stateful data may go out of sync.\n    - Single pod stateful workload\n        - Create persitant volumes\n        - Create persitant volume claims to access persitant volumes in a synchronized way, just to prevent ensure data atomicity.\n\n### Persistent Volumes\n- PV are like volumes in Docker, just that their lifecycle is independent of Pods.\n- This is an API object. Captures details about storage implementation.\n- Provised by Kubernetes administrator.\n- Way to abstract storage resource.\n- Create a persistent volume for MySQL server. [File](files/pv.yml)\n    ```yaml\n    kind: PersistentVolume\n    apiVersion: v1\n    metadata:\n      name: pv\n      labels:\n        type: local\n    spec:\n      storageClassName: manual\n      capacity:\n        storage: 5Gi\n      accessModes:\n        - ReadWriteOnce\n      hostPath:\n        path: \"/data\"\n    ```\n    This spec specifies the volume is at `/data` on cluster's node.\n    Apply it : `kubectl apply -f pv.yml`\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl get pv\n    NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE\n    pv     5Gi        RWO            Retain           Available           manual                  62s\n    ```\n\n\n### Persistent Volume Claims\n- Storage requeest by a user.\n- PVCs consume PV resources.\n- Way to access abstract storage.\n- PVC can request specific size and access mode: `ReadWriteOnce`, `ReadOnlyMany`, `ReadWriteMany`\n\n|Access Mode| Meaning|\n|---|---|\n|ReadWriteOnce | volume can be mounted as read-write by a single node|\n|ReadOnlyMany |  volume can be mounted read-only by many nodes|\n|ReadWriteMany | volume can be mounted as read-write by many nodes|\n\n- Create a PVC spec. [File](files/pvc.yml)\n    ```yaml\n    kind: PersistentVolumeClaim\n    apiVersion: v1\n    metadata:\n      name: pv-claim\n    spec:\n      storageClassName: manual\n      accessModes:\n        - ReadWriteOnce\n      resources:\n        requests:\n          storage: 1Gi\n    ```\n    Apply it.\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl apply -f pv-claim.yml\n    persistentvolumeclaim/pv-claim created\n\n    root@vagrant:/home/vagrant/kubedata# kubectl get pvc\n    NAME       STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE\n    pv-claim   Bound    pv       5Gi        RWO            manual         8s\n\n    root@vagrant:/home/vagrant/kubedata# kubectl describe pvc pv-claim\n    Name:          pv-claim\n    Namespace:     default\n    StorageClass:  manual\n    Status:        Bound\n    Volume:        pv\n    Labels:        \u003cnone\u003e\n    Annotations:   pv.kubernetes.io/bind-completed: yes\n                   pv.kubernetes.io/bound-by-controller: yes\n    Finalizers:    [kubernetes.io/pvc-protection]\n    Capacity:      5Gi\n    Access Modes:  RWO\n    VolumeMode:    Filesystem\n    Mounted By:    \u003cnone\u003e\n    Events:        \u003cnone\u003e\n    root@vagrant:/home/vagrant/kubedata#\n    ```\n    - Pods use PersistentVolumeClaims to request physical storage\n    - After creating the PersistentVolumeClaim, the Kubernetes control plane looks for a PersistentVolume that satisfies the claim's requirements. If the control plane finds a suitable PersistentVolume with the same StorageClass, it binds the claim to the volume.\n\n- Lets create a POD which will use PV as Volume using PVC. [File](files/nginx-pod-with-pv.yml)\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: nginx-pod-with-pvc\nspec:\n  volumes:\n    - name: nginx-pv-storage\n      persistentVolumeClaim:\n        claimName: pv-claim\n  containers:\n    - name: nginx-with-pv\n      image: nginx\n      volumeMounts:\n        - mountPath: \"/usr/share/nginx/html\"\n          name: nginx-pv-storage\n```\n```bash\nroot@vagrant:/home/vagrant/kubedata# kubectl get pods nginx-pod-with-pvc\nNAME                 READY   STATUS    RESTARTS   AGE\nnginx-pod-with-pvc   1/1     Running   0          16s\n\nroot@vagrant:/home/vagrant/kubedata# kubectl exec -it nginx-pod-with-pvc -c nginx-with-pv -- /bin/bash\nroot@nginx-pod-with-pvc:/# curl localhost\nHi PV\n\n```\n- The file we just created in storage is made accessible to Nginx POD.\n\n\n### Summary\n```\n                                +--------------------------------------+\n                                |     +------------+                   |\n                                |     |    POD     |        +---------------\u003e\n                                |     +-----+------+        |          |    |\n                                |           |               |          |    |\n                                |           |         +-----+------+   |    v\n                                |           |         |     PV     |   |   /data\n                                |           |         +------+-----+   |\n                                |     +-----v------+         ^         |\n                                |     |    PVC     +---------+         |\n                                |     +------------+                   |\n                                |                                      |\n                                +--------------------------------------+\n```\n- PV to PVC bind is automatic, based on storage class.\n- Pod/Deployment/K8s-Resource link to PVC has to has to be done manually in spec file.\n\n## Sample Application Example\n1. This End to End setup will include:\n  1. MySQL setup through PV and PVC.\n  2. Building Custom Dockerfile for sprinboot application.\n  3. Creating Deployment for SpringBoot application.\n    1. Setup the environment for application to connect to DB.\n    2. Setting up PVC setup in deployment.\n    3. Creating Serivce for springboot application access outside pod.\n      1. Service setup through LB\n\nOnce we create spec.yml in bits, we will create a big spec to show our Infrastructure as a Code and deploy that :smile:.\n\n### MySQL Resource\n\n#### Step 1: Create PV for MYSQL DB\n```yaml\nkind: PersistentVolume\napiVersion: v1\nmetadata:\n  name: mysql-pv\n  labels:\n    type: local\nspec:\n  storageClassName: manual\n  capacity:\n    storage: 5Gi\n  accessModes:\n    - ReadWriteOnce\n  hostPath:\n    path: \"/data/mysql\"   \n```\n\n#### Step 2: Create PVC for PV\n```yaml\nkind: PersistentVolumeClaim\napiVersion: v1\nmetadata:\n  name: mysql-pvc\nspec:\n  storageClassName: manual\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 1Gi\n```\n\n#### Step 3: Create MySQL deployment Spec\n```yaml\napiVersion: apps/v1 \nkind: Deployment\nmetadata:\n  name: dbserver\n  labels:\n    app: dbserver\nspec:\n  selector:\n    matchLabels:\n      app: dbserver\n  template:\n    metadata:\n      labels:\n        app: dbserver\n    spec:\n      containers:\n      - image: mysql\n        name: mysql\n        imagePullPolicy: Never\n        env:\n        - name: MYSQL_ROOT_PASSWORD\n          value: mysecretpassword\n        ports:\n        - containerPort: 3306\n          name: dbserver\n        volumeMounts:\n        - name: mysql-persistent-storage\n          mountPath: /var/lib/mysql\n      volumes:\n      - name: mysql-persistent-storage\n        persistentVolumeClaim:\n          claimName: mysql-pvc\n```\n  - Once the DB server is up, please go adhead and login to MySQL and create `peopledb` for sprinboot application to access.\n    - `mysql  -- mysql -u root -pmysecretpassword` \u0026 `create database peopledb`\n\n#### Step 4: Expose MySQL server via Service\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: dbservice\nspec:\n  selector:\n    app: dbserver\n  ports:\n  - protocol: TCP\n    port: 3306\n    targetPort: 3306\n```\n- This will expose this service over/inside cluster for other services to access.\n\n### Springboot Application\n\n#### Step 1: Build and Deploy AppServer\n- Build the Docker image with name `appserver` from this [File](files/Dockerfile).\n  ```bash\n  docker build  -t appserver .\n  ```\n- Create Deployment spec for appserver.\n  ```yaml\n  apiVersion: apps/v1\n  kind: Deployment\n  metadata:\n    name: appserver\n  spec:\n    replicas: 1\n    selector:\n      matchLabels:\n        app: appserver\n    template:\n      metadata:\n        labels:\n          app: appserver\n      spec:\n        containers:\n        - name: appserver\n          image: appserver\n          imagePullPolicy: Never\n          env:\n          - name: DB_HOST\n            value: dbservice\n  ```\n\n#### Step 2: Expose AppServer service via Service type LB to host.\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: contacts\nspec:\n  type: LoadBalancer\n  selector:\n    app: appserver\n  ports:\n  - protocol: TCP\n    port: 80\n    targetPort: 8080\n```\n\n### Infrastructure as a Code\n#### MySQL Full Spec\n- You can find the full spec file here : [File](files/mysql-spec.yml)\n  ```yaml\n  kind: PersistentVolume\n  apiVersion: v1\n  metadata:\n    name: mysql-pv\n    labels:\n      type: local\n  spec:\n    storageClassName: manual\n    capacity:\n        storage: 5Gi\n    accessModes:\n      - ReadWriteOnce\n    hostPath:\n      path: \"/data/mysql\"   \n\n  ---\n  kind: PersistentVolumeClaim\n  apiVersion: v1\n  metadata:\n    name: mysql-pvc\n  spec:\n    storageClassName: manual\n    accessModes:\n      - ReadWriteOnce\n    resources:\n      requests:\n        storage: 1Gi\n\n  ---\n  apiVersion: apps/v1 \n  kind: Deployment\n  metadata:\n    name: dbserver\n    labels:\n      app: dbserver\n  spec:\n    selector:\n      matchLabels:\n        app: dbserver\n    template:\n      metadata:\n        labels:\n          app: dbserver\n      spec:\n        containers:\n        - image: mysql\n          name: mysql\n          imagePullPolicy: Never\n          env:\n          - name: MYSQL_ROOT_PASSWORD\n            value: mysecretpassword\n          ports:\n          - containerPort: 3306\n            name: dbserver\n          volumeMounts:\n          - name: mysql-persistent-storage\n            mountPath: /var/lib/mysql\n        volumes:\n        - name: mysql-persistent-storage\n          persistentVolumeClaim:\n            claimName: mysql-pvc\n  ---\n  apiVersion: v1\n  kind: Service\n  metadata:\n    name: dbservice\n  spec:\n    selector:\n      app: dbserver\n    ports:\n    - protocol: TCP\n      port: 3306\n      targetPort: 3306\n  ```\n- `kuebctl apply -f mysql-spec.yml` :smile:\n\n#### AppServer Full Spec\n- You can find the full spec file here: [File](files/appserver-spec.yml)\n  ```yaml\n\n  apiVersion: apps/v1\n  kind: Deployment\n  metadata:\n    name: appserver\n  spec:\n    replicas: 1\n    selector:\n      matchLabels:\n        app: appserver\n    template:\n      metadata:\n        labels:\n          app: appserver\n      spec:\n        containers:\n        - name: appserver\n          image: appserver\n          imagePullPolicy: Never\n          env:\n          - name: DB_HOST\n            value: dbservice\n\n  ---\n  apiVersion: v1\n  kind: Service\n  metadata:\n    name: contacts\n  spec:\n    type: LoadBalancer\n    selector:\n      app: appserver\n    ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 8080\n  ```\n  Quickly apply it with `kubectl apply -f appserver-spec.yml`\n\n\n## Understanding Advance Kubernetes Resources\n\n### Namespace\nNamespace are software level cluster virtualization over same physical k8s cluster.  \n```bash\n  root@vagrant:/home/vagrant# kubectl get ns\n  NAME              STATUS   AGE\n  default           Active   19d\n  kube-node-lease   Active   19d\n  kube-public       Active   19d\n  kube-system       Active   19d\n```\n\nKubernetes starts with 4 namespaces:\n1. **default**: The default namespace for objects with no other namespace.\n2. **kube-system**: The namespace for objects created by the Kubernetes system.\n3. **kube-public**: This namespace is created automatically and is readable by all users (including those not **authenticated**). This namespace is mostly reserved for cluster usage, in case that some resources should be visible and readable publicly throughout the whole cluster. The public aspect of this namespace is only a convention, not a requirement.\n4. **kube-node-lease**: This namespace for the lease objects associated with each node which improves the performance of the node heartbeats as the cluster scales.\n\nGet Pods from specific namespace\n``kubectl get pods --namespace=default`` OR `kubectl get pods -n default`\n```bash\nroot@vagrant:/home/vagrant# kubectl get pods --namespace=kube-system\nNAME                              READY   STATUS    RESTARTS   AGE\ncoredns-f9fd979d6-g9wxg           1/1     Running   5          19d\ncoredns-f9fd979d6-zrdvs           1/1     Running   5          19d\netcd-vagrant                      1/1     Running   5          19d\nkube-apiserver-vagrant            1/1     Running   5          19d\nkube-controller-manager-vagrant   1/1     Running   7          19d\nkube-flannel-ds-64l2p              1/1     Running   6          19d\nkube-proxy-4j4kw                  1/1     Running   5          19d\nkube-scheduler-vagrant            1/1     Running   7          19d\n```\n\n#### Creating Namespace \u0026 Adding resource\n- Create namespace : `kubectl create namespace qa`\n- Once the namespace is created, just add the metadata field : `namespace: qa`, [File](files/pod-qa.yml)\n  ```diff\n  apiVersion: v1\n  kind: Pod\n  metadata:\n     name: nginx\n  ++ namespace: qa\n  spec:\n    containers:\n    - name: nginx\n      image: nginx\n  ```\n- Most Kubernetes resources (e.g. pods, services, replication controllers, and others) are in some namespaces. However namespace resources are not themselves in a namespace. And low-level resources, such as nodes and persistentVolumes, are not in any namespace.\n  - To see the list of resource not in namespace : `kubectl api-resources --namespaced=false`\n\n### Context\n- Is a tuple of **cluster**, **user**, **namespace**. This is useful when you connect to multiple clusters from one control plane.\n  - Get the current context: `kubectl config get-contexts`\n  ```bash\n  root@vagrant:/home/vagrant/kubedata# kubectl config get-contexts\n  CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE\n  *         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin\n  ```\n- You can create kubernetes context using config file or using commands.\n  - Create a qa-config: `kubectl config set-context dev-env --cluster=kubernetes --user=new-admin --namespace=dev-env`\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl config set-context dev-env --cluster=kubernetes --user=new-admin --namespace=dev-env\n    Context \"dev-env\" created.\n    ```\n    ```bash\n    root@vagrant:/home/vagrant/kubedata# kubectl config get-contexts\n    CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE\n             dev-env                       kubernetes   new-admin          dev-env\n    *          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin\n    ```\n  - Now use the created context using : `kubectl config use-context dev-env`\n  - All your k8s resource will now be in DEV name space under kubernetes cluster :smile:\n    - But to create resource you will need user `new-admin` authentication. This is the user created during context creation.\n    - Create username \u0026 password for user `new-admin` to use the resource in context and create a role binding: **Run this before switching context**\n    `kubectl config set-credentials new-admin --username=adm --password=changeme`\n    ```bash\n    cat \u003c\u003c EOF | kubectl apply -f -\n    apiVersion: rbac.authorization.k8s.io/v1\n    kind: ClusterRoleBinding\n    metadata:\n      name: new-admin\n    roleRef:\n      apiGroup: rbac.authorization.k8s.io\n      kind: ClusterRole\n      name: cluster-admin\n    subjects:\n    - apiGroup: rbac.authorization.k8s.io\n      kind: User\n      name: marry@example.com\n\n    EOF\n    ```\n\n## CheatSheet\n- I plan to write a simple cheat sheet covering the commands in this repo. But for now Try : [k8s-official-cheat-sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)\n\n## Next Steps\n- [In detail K8s Reference](https://kubernetes.io/docs/reference/)\n- [API Guide](https://kubernetes.io/docs/reference/)\n- [CLI Guide](https://kubernetes.io/docs/reference/)\n- [K8s Design Docs](https://kubernetes.io/docs/reference/)\n- Raising a PR makes me happy, take that as a next step.\n- Issues are more than welcome.\n- If you like it, share it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frosehgal%2Fk8s-In-30Mins","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frosehgal%2Fk8s-In-30Mins","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frosehgal%2Fk8s-In-30Mins/lists"}