Traduzione da Amazon Translate
Kubernetes

Certified Kubernetes

This blog post will provide an introduction to Kubernetes, what it is, and how you can use it, and build an understand the fundamentals.

These are my personal notes I made while I was studying CKA and CKAD certifications.

I passed the CKAD on Jan 15, 2021 with 78/100 exam score, and I passed the CKA on Apr 23, 2021 with 85/100 exam score. The minimum score to pass the certification is 66 over 100.

AGGIUNGERE TIPS CERTIFICAZIONE

Overview

Here's what we'll discuss in this post. You can click on any top-level heading to jump directly to that section.

Table of Contents

The Kubernetes Architecture

In this section there are some basic concepts and terms related to Kubernetes (K8s) architecture. The purpose is to provide the knowledge about the main components.

K8s Architecture
K8s Architecture

Cluster: A Kubernetes cluster consists of one or more nodes managed by Kubernetes. If our a node fails, your app will be down. So a cluster is a set of nodes grouped together. Even if one node fails, your application will still be accessible from the other nodes. In addition, having multiple nodes helps in sharing the computational load as well.

Master Node: It is s responsible for managing the cluster. For example it store the information about the members of the cluster, if a node node fails it is responsible to move the workload of the failed node to another worker node. In addition, it monitor the nodes into the cluster.

The master nodes runs the Kubernetes control plane. The control plane consists of different processes, such as an API server, scheduler, controller manager and etcd.

  • Kube-APIServer: This component exposes the Kubernetes API, it is the front-end for the control plane. The users, management devices, and command line interfaces all talk to the API server to interact with the Kubernetes cluster.
  • Kube-Scheduler: It is a component on the master node that watches newly created pods that have no node assigned and selects a node for them to run on.
  • ETCD: is a distributed reliable key-value store used by Kubernetes to store all data used to manage the cluster. It stores the configuration information which can be used by each of the nodes in the cluster, and other information about resources like pods, deployments, etc. It is usually installed on the master, but it can also be deployed on multiple nodes. It stores the data of the database at /var/lib/etcd.
  • Kube-Controller-manager: It has many logically separate functions, but to reduce complexity all runs in a single process. Those functions are the Node Controller that is responsible for noticing and responding when nodes go down, the Replication Controller that is responsible for maintaining the correct number of pods for every replication controller object in the system and the Endpoints Controller that is responsible to populate the Endpoints of Services and Pods.
  • Cloud-Controller-Manager: When it comes to the cloud there is another component that interact with the underlying cloud providers. It has many other functions like the Service Controller that is responsible for creating, updating, and deleting cloud provider load balancers, the Volume Controller that is responsible of creating, attaching, and mounting volumes and Service Account and Token Controllers that is responsible for create default accounts and API access tokens for new namespaces.

Nodes (Minions): In a Kubernetes cluster each machine has a dedicated role, can be master or worker. The master is our API point, the workers are in charge of running the app. The machines that acts as workers, used to be called nodes or minions.

The worker nodes runs different processes, such as kubelet, kube-proxy and docker.

  • Docker: helps in running the encapsulated application containers in a relatively isolated but lightweight operating environment.
  • Container Runtime: is the software that is responsible for running containers. Kubernetes supports several runtimes: Docker, containerd, cri-o, rktlet, and any implementation of the Kubernetes CRI (Container Runtime Interface).
  • Kubelet: is the agent that runs on each node in the cluster.  Usually, it communicates with the master component to receive commands and work, interacts with the ETCD store to read configuration details and write values. The Kubelet process then assumes responsibility for making sure that the containers are running on the nodes as expected. It manages network rules, port forwarding, etc. Kubelet can also deploy Static PODs independently, the agent constantly monitors the /etc/kubernetes/manifest path and deploy everything inside. The Kubelet doesn’t manage containers that were not created by Kubernetes.
  • Kubernetes Proxy Service: is a proxy service that runs on each node and helps to make the service available to external hosts. It helps to forward requests to the correct container and is able to perform raw load balancing.

Client: Kubernetes follows a client-server architecture where the client use a command line tool to interact with the cluster. 

  • Kubectl: command is a line tool that interacts with kube-apiserver and send commands to the master node. Each command is converted into an API call. If the API Interface fails also the line tool stops working, in this case you have to ssh into the cluster and use docker commands to troubleshoot the problem.  

Kubernetes uses client certificates, bearer tokens, or an authenticating proxy to authenticate API requests, you can check the location and credentials that kubectl knows about with this command: kubectl config view. The default location of .kube/config file in linux is ~/.kube/config.

Basic Notions

Basic Notions

PODs: From K8s docs, pods are the smallest deployable units of computing that you can create and manage in Kubernetes.  If you need to run a single container in Kubernetes, then you need to create a Pod for that container.

  • A Pod can contain more than one container, usually these containers are relatively tightly coupled. A container can be a Docker container, rkt container, or a virtual machine (VM).
  • Containers in a Pod run on a “logical host”; they use the same network namespace, and use shared volumes. It makes possible for these containers to efficiently communicate. Ci sono tre principali pattern nel Multi-Container PODs e sono: Sidecar, Adapter e Ambassador.
  • Scaling operation creates new PODs, it replicates all containers in the POD.
  • You can attach a Label to a Pod. Labels can be used to organize and to select subsets of objects.
  • Each Pod is assigned a unique IP address. Inside a Pod the containers that belong to the Pod can communicate with one another using localhost.
  • PODs can communicate each other via IP address only if in the same node. To connect PODs in a Cluster, you need a network manager such as Calico.
apiVersion: v1
kind: Pod
metadata:
  name: hello 
spec: containers: - name: hello image: busybox command:
- '/bin/sh'
args:
- '-c'
- 'while true; do echo hello; sleep 10; done'
env:
  - name: APP_COLOR
    value: pink
  - name: APP_BACKGROUND
  valueFrom:
  configMapKeyRef: ConfigMapName
  - name: API_KEY
  valueFrom:
    secretKeyRef: SecretName
  • You can specify the environment variables by using the env property. In the above example, the value is taken from different sources.
  • When te container starts it execute the actions that are specified under the command and args sections. In the case of the example the container executes /bin/sh -c "while true; do echo hello; sleep 10; done".
  • The command section in Kubernetes is the same as ENTRYPOINT in docker, while the args section in Kubernetes is the same as COMMAND in docker.

To create a POD you can use the below command:

kubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml

By using a --dry-run=client option you can generate a sample YAML file, just customize it and create the POD with kubectl apply -f pod.yaml command. 

ReplicaSets: From K8s docs, a ReplicaSet ensures that a specified number of pod replicas are running at any given time, deployment instead is a higher-level concept that manages ReplicaSets.

  • It is an holder for PODs, for each POD in the holder it monitors the execution status, with a ReplicaSets you can control how many PODs are in the holder.
  • In order to manage failures you have to provide PODs YAML definition.
  • It does not give you a strategy to replace PODs in the holder, check Deployment for this.

Deployment: From K8s docs, a Deployment provides declarative updates for Pods and ReplicaSets along with a lot of other useful features. You have to use Deployments instead of ReplicaSets, unless you require custom update orchestration or don't require updates at all. This actually means that you may never need to manipulate ReplicaSet objects.

  • When you create a Deployment also a ReplicaSets is automatically created.
  • You can use strategy section in order to specify the release mode, it can be Rolling Update or Big Bang (Recreate in k8s). 
  • The RollingUpdate strategy has some additional settings with which you can specify how many PODs at most can be down at a time during the deployment.
Kubernetes: Deployment Rolling Update
Kubernetes Rolling Update: Credits to bluematador.com
  • During the release phase the Deployment creates a new ReplicaSet in which new PODs are created, according to the release settings the old PODs are deleted from the first ReplicaSet and new PODs are started in the second one.

Rolling Update and Rollbacks….

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate replicas: 3 selector: matchLabels: app: nginx template:
# This is the pod template metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
# The pod template ends here
  • In the above example to switch from RollingUpdate to Recreate you have to delete the rollingUpdate section, and set the property strategy.type: Recreate. In this case, Kubernetes deletes in a single step old PODs from the first ReplicaSet and creates new PODs in the second one.
  • The YAML of the Deployment resource also contains the definition of the pod used by the underlying ReplicaSet to start new resources. To identify which resource belongs to a Deployment - and therefore to the underlying ReplicaSet - Kubernetes uses labels. The selector.matchLabels section selects all the POD with the label app=nginix.
  • To be selected by a Deployment, the POD must specify the same label app=nginix into the metadata.labels section.

To create a Deployment you can use the below commands:

kubectl create deployment --image=nginx nginx --dry-run -o yaml > deployment.yaml

By using a --dry-run=client option you can generate a sample YAML file, just customize it and create the Deployment with kubectl apply -f deployment.yaml command. If you update the definition, the deployment creates new PODs by following the release strategy. You can perform rollback operation by using this command: 

kubectl rollout undo deployment/myapp-deployment

Namespaces: From K8s docs, Namespaces are a way to divide cluster resources between multiple users, teams or projects. It provide a scope for names and resources in namespace need to be unique, but not across namespaces. You cannot create a nested namespace and each resource can only be in one of it.

  • Some namespaces are created automatically by Kubernetes.
  • Default, unless otherwise specified everything is created within this namespace.
  • kube-system contains what is needed to run k8s, while kube-public here is where the resources that are to be made available to all users should be created.
  • Resources in a Namespace can reach others using the name.
  • For resources in a different Namespace you need to add the namespace name and svc.cluster.local. For example, db-service.dev.svc.cluster.local consists of <service-name>.<namespace>.svc.cluster.local where svc stands for Service and cluster.local is the ClusterDomain.
  • ResourceQuota can be used to limit the resources that can be used for resources in a namespace.

To create a Namespace you can use the below commands:

kubectl create namespace test --dry-run=client -o yaml

# namespace.yaml
apiVersion
: apps/v1 kind: Namespace metadata: name: test creationTimestamp: null spec: {}
status: {}

kubectl apply -f namespace.yaml

DaemonSets: From K8s docs, a DaemonSet ensures that all (or some) Nodes run a copy of a Pod. As nodes are added to the cluster, Pods are added to them. As nodes are removed from the cluster, those Pods are garbage collected. Deleting a DaemonSet will clean up the Pods it created.

  • DaemonSets are used to ensure that some or all of your K8S nodes run a copy of a pod, which allows you to run a daemon on every node.
  • When you add a new node to the cluster, a pod gets added to match the nodes.
  • When you remove a node from your cluster, the pod is put into the trash.
  • Deleting a DaemonSet cleans up the pods that it previously created.
  • DaemonSets are useful for deploying different types of agents.
  • The typical use case for a DaemonSet is logging and monitoring for the hosts.

Jobs: From K8s docs, Jobs are a way to ensure that one or more pods execute their commands and exit successfully. When all the pods have exited without errors, the Job gets completed. When the Job gets deleted, any created pods get deleted as well.

  • There are many scenarios when you don’t want the process to keep running indefinitely.
  • You can use jobs to perform batch operations.
  • The Default behavior for PODs does not make them useful to implement this, because k8s would recreate them each time to ensure that the required number of PODs are running. You can get around this by properly configuring restartPolicy: Never however it would still be a workaround.
  • You can use Jobs to handle the case where you need to parallelize data transformation. In this case, the Job makes ensures that each task has correctly completed the operation.

To create a Job you can use the below commands:

kubectl create job job-test --image=busybox --dry-run=client -o yaml

# job.yaml
apiVersion
: batch/v1 kind: Job metadata: name: job-test creationTimestamp: null spec:
spec:
parallelism: 2
completions: 6
template:
# This is the pod template
metadata:
name: job-terst
creationTimestamp: null
spec:
containers:
- image: busybox
name: batch
resources: {}
command: ["echo","job-test"]
restartPolicy: OnFailure
# The pod template ends here
status: {}

By using a --dry-run=client option you can generate a sample YAML file, just customize it and create the Job with kubectl apply -f job.yaml command. The above example executes six jobs with a parallelism of two.

Cron Jobs: From K8s docs, Cron Jobs are useful for periodic and recurring tasks, like running backups, sending emails, or scheduling individual tasks for a specific time, such as when your cluster is likely to be idle.

  • You can use CronJobs for cluster tasks that need to be executed on a predefined schedule.
  • For example log rotation command should not be running continuously. It gets scheduled, and once the task is executed, it returns the appropriate exit status that reports whether the result is a success or failure.

Init Container: From K8s docs, an init container is the one that starts and executes before other containers in the same Pod. It’s meant to perform initialization logic for the main application hosted on the Pod.

  • An Init Container is defined in the same way as a container for a Pod, but in the initContainers section.
  • Init containers are started and executed at POD startup and before the main containers, so it is a good candidate for delaying the application initialization until one or more dependencies are available. 
  • You can have multiple Init containers in same POD, they are started and executed in sequence. An init container is not invoked unless its predecessor is completed successfully.
  • You can use Init containers to create the necessary user accounts, perform database migrations, create database schemas and so on.
  • If any of the init containers fail, the whole Pod is restarted, unless you set restartPolicy to Never.
  • Restarting the Pod means re-executing all the containers again including any init containers. You may need to ensure that the startup logic tolerates being executed multiple times.
  • All containers on the same Pod share the same Volumes and network. You can make use of this feature to share data between the application and its init containers. You can use Init Container to download a configuration and make it available to other containers. 
apiVersion: v1
kind: Pod
metadata:
  name: database
spec:
initContainers:
- name: fetch
image: busybox/wget
command:
- '/bin/sh'
args:
- '-c'
- 'wget site.url/dmp.sql -o /docker-entrypoint-initdb.c/dump.sql'
volumeMounts:
- mountPath: /docker-entrypoint-initdb.c
name: dump
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
  value: "admin"
volumeMounts:
- mountPath: /docker-entrypoint-initdb.c
name: dump
volumes:
- emptyDir: {}
name: dump

In the example above the InitContainer is used to download the dump.sql file which is then shared via the volume to the mysql container.

Static Pod: From K8s docs, static Pod are managed directly by the kubelet daemon on a specific node, without the  API server observing them. The kubelet watches each static Pod and restarts it if it fails.

  • a static Pod is defined at the /etc/kubernetes/manifest path on each node.
  • are mainly used by k8s administrators to create system components.
  • a change to a Static Pod using the edit command has no effect. Since kubelet monitors what is created in the static pods folder it is necessary to update the pod definition in the specific folder.
  • a change to a static pod using the edit command has no effect.
  • kubelet retrieves the definition of static pods from the /etc/kubernetes/manifest path, so you must edit the pod definition in that path for the change to take effect.

Example, create the static pod static-busybox that uses the busybox image and executes the "sleep 1000" command.

kubectl run --restart=Never --image=busybox static-busybox --dry-run=client -o yaml --command -- sleep 1000 > /etc/kubernetes/manifests/static-busybox.yaml

apiVersion: v1 kind: Pod metadata:
labels:
  run: static-busybox name: static-busybox spec:
initContainers:
- name: static-busybox
image: busybox
command:
- sleep
- '1000'
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}

Metadata

Labels, Selectors and Annotations

Labels & Selectors: 
>>> kubectl get pods --selector app=App1
>>> kubectl get all --selector env=prod,bu=finance,tier=frontend
- La relazione tra Deployment e POD è definita tramite Labels
- La relazione tra ReplicaSet e POD è definita tramite Labels
- La relazione tra Service e PODs è definita tramite Labels
- Le annotazioni servono a dare informazioni aggiuntive, ad esempio la versione del applicazione.

https://blog.getambassador.io/kubernetes-labels-vs-annotations-95fc47196b6d

Annotations

Configuration

Config, Secrets and Resource Limits 

Earlier you saw how PodSpecs can be used to define the container images that should be instantiated in the pods. Follow the best practice of portability: Make sure that your pods can run consistently without needing to change your container images for development or production. This can be done by separating your configuration data from your application code.

Config Map: To separate your configuration data from your application code in Kubernetes you can use a ConfigMap. A ConfigMap is an API object that stores data as key-value pairs. Other Kubernetes objects, such as pods, can then use the key values from the ConfigMap. The ConfigMap data can be used as environment variables, command line arguments, or as configuration files in a volume.

  • to use a ConfigMap there are two steps, first you have to create the config map in key/value form and then you have to do injection of it into the Pod.
  • you can use the whole content of the ConfigMap as Env, or you can import a single key and set is as environment variable, also you can mount the ConfigMap as Volume.
  • when you mount the ConfigMap as a volume many files are created at mount path. The application can retrive the value from the file named using key.

Example, how to crear a ConfigMap

kubectl create configmap config-map-example --from-literal=foo=bar --from-literal=wow=baz --dry-run=client -o yaml

apiVersion: v1 kind: ConfigMap data:
 foo: bar wow: baz metadata:
creationTimestamp: null
name: config-map-example

Example, how to inject a ConfigMap into a Pod

apiVersion: v1
kind: Pod
metadata:
  name: hello 
spec: containers: - name: hello image: busybox
# Option 1
# Import the whole key/value map as Env
envFrom:
- configMapRef:
name: ConfigMapName
key: foo
# Option 2
# Import the single value as Env
env:
  - name: APP_BACKGROUND
  valueFrom:
  configMapKeyRef: ConfigMapName
# Option 3
# Crete foo and wow files in the mount path
# The foo file contains "bar" string
# The wow file contains "baz" string
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: ConfigMapName

Secrets: A ConfigMap is meant to store only nonconfidential data. All confidential data, such as AWS credentials, should be stored in Secrets. Secrets protect and encrypt confidential and sensitive information. Do not store confidential information directly in your pod definition or in your ConfigMap because that information is not secure and can be compromised.

  • Secrets store sensitive information in key-value pairs such as passwords.

  • Note that the values for all keys in the data field of a secret must be base64-encoded strings.
  • quando monti un secret come volume vengono creati dei file che contengono i dati, uno per ogni chiave.
  • to make secrets really safe you must follow security best practices, by default secrets are only base64 encoded.
  • with the declarative mode the secret value must be base64 encoded first: 
    echo "mysql" | base64
    echo "bXlzcWwK" | base64 --decode
  • with the imperative mode the encoding is performed automatically.

Example, how to crear a Secret:

kubectl create secret generic db-secret --from-literal=DB_Host=sql01 --from-literal=DB_User=root --from-literal=DB_Password=password123 --dry-run=client -o yaml

apiVersion: v1 kind: Secret data:
DB_HOST: c3FsMDE= DB_Password: cGFzc3dvcmQxMjM=
DB_User: cm9vdA== metadata:
creationTimestamp: null
name: db-secret

Example, how to inject a Secret into a Pod

apiVersion: v1
kind: Pod
metadata:
  name: hello 
spec: containers: - name: hello image: busybox
# Option 1
# Import the whole key/value map as Env
envFrom:
- secretRef:
name: SecretName
# Option 2
# Import the single value as Env
env:
  - name: DB_Password
  valueFrom:
  secretKeyRef:
name: SecretName
key: DB_Password
# Option 3
# Crete DB_HOST, DB_Password and DB_User files in the mount path
# The DB_HOST file contains "sql01" string
# The DB_Password file contains "password123" string
# The DB_User file contains "root" string
volumeMounts:
- name: secret-volume
mountPath: /etc/secret
volumes:
- name: secret-volume
secret:
secretName: SecretName

Resource Requirements, Resource Limits

Requests and limits are the mechanisms Kubernetes uses to control resources such as CPU and memory. Requests and limits are on a per-container basis, each container in the Pod gets its own individual limit and request, but because it’s common to see Pods with multiple containers you need to add the limits and requests for each container together to get an aggregate value for the Pod.

To control what requests and limits a container can have, you can set quotas at the Container level and at the Namespace level. There are two types of resources: CPU and Memory.

A typical Pod spec for resources might look something like this. This pod has two containers:

apiVersion: v1
kind: Pod
metadata:
  name: resource-limits 
spec: containers: - name: hello1 image: busybox
resources:
requests:
memory: '32Mi'
cpu: '200m'
limits:
memory: '64Mi'
cpu: '250m'
- name: hello2 image: busybox
resources:
requests:
memory: '96Mi'
cpu: '300m'
limits:
memory: '192Mi'
cpu: '750m'

Each container in the Pod can set its own requests and limits, and these are all additive. So in the above example, the Pod has a total request of 500 mCPU and 128 MiB of memory, and a total limit of 1 CPU and 256MiB of memory.

  • CPU resources are defined in millicores.
  • Memory resources are defined in bytes.
  • Default: 500m CPU and 256 Mi RAM.

If the node where a Pod is running has enough of a resource available, it's possible (and allowed) for a container to use more resource than its request for that resource specifies. However, a container is not allowed to use more than its resource limit.

ResourceQuotas

A resource quota provides constraints that limit aggregate resource consumption per namespace. A Quota for resources might look something like this:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota 
spec: hard: request.cpu: '500m' request.memory: '100Mib'
limits.cpu: '700m'
limits.cpu: '500Mib'
  • requests.cpu As long as the total requested CPU in the Namespace is less than 500m, you can have 50 containers with 10m requests, five containers with 100m requests, or even one container with a 500m request.
  • requests.memory As long as the total requested Memory in the Namespace is less than 100MiB, you can have 50 containers with 2MiB requests, five containers with 20MiB CPU requests, or even a single container with a 100MiB request.
  • limits.cpu and limits.memory  is the maximum combined CPU or Memory.

You can also create a LimitRange in your Namespace. Unlike a Quota, which looks at the Namespace as a whole, a LimitRange applies to an individual container. This can help prevent people from creating super tiny or super large containers inside the Namespace.

Scheduling

Pod Scheduling

This is one of the key components of Kubernetes master responsible for distributing the workload or containers across multiple nodes. The scheduler is responsible for workload utilization and allocating pod to a new node. It looks for newly created containers and assigns them to Nodes. Factors taken into account for scheduling decisions include individual and collective resource requirements, hardware/software/policy constraints, affinity and anti-affinity specifications, data locality, inter-workload interference, and deadlines.

You can schedule pods with the Kubernetes scheduler. The scheduler checks the resources required by your pods and uses that information to influence the scheduling decision.
Pod Scheduling filters

The scheduler uses filters to exclude ineligible nodes for pod placement and then determines the final node based on a scoring system. Predicates determine which nodes are ineligible. This can be due to volume, resource, or topology requirements. Priorities calculates a score for each node that has not been filtered out. Then, the scheduler places the pod on the highest-scoring node. If there are no available nodes remaining after all filters have been applied, scheduling fails.

Predicate: Volume requirements

The volume filter checks the pod’s volume requirements to determine which nodes are compatible. For storage backends that are topology-constrained and not globally accessible from all Nodes in the cluster you must know the Pod's requirements, otherwise this may result in unschedulable Pods. For example, an Amazon EBS volume in one Availability Zone cannot be attached to a node in a different Availability Zone. Similarly, a pod might require a specific volume that is already mounted on a node. In this case, you must place that pod on the same node.

Predicate: Resource requirements

The scheduler considers which nodes have the resources required by the pod. This includes things such as CPU, memory, disk space, and available ports. As you already know, resource requirements are defined at the container level, so the sum of resources requested for all containers defines the resources for the pod. The scheduler uses the number of resources when making a scheduling decision. Nodes cannot be overprovisioned, so if your cluster does not have enough resources for the pod, scheduling fails.

The limits parameter defines a finite limit on resources after the pod is running. A running container can burst resource usage from the original request up to the defined limit. If the container uses more resources than the limit allows, the scheduler terminates the container.

Predicate: Topology – Taints and tolerations

After satisfying volume and resource constraints, the scheduler considers constraints that have been set to fine-tune pod placement. You can set up scheduling constraints at the node level and at the pod level.

Taints and tolerations are settings that work together to control which pods can be placed on a particular node.

Taints are a property of nodes that prevent the placement of pods. A tainted node accepts only pods that specifically tolerate the taint. To taint a node, specify a key=value pair, such as skynet=false, and then add an action that defines when the taint is considered.

You can configure the scheduler to respect the taint during scheduling, in this case no pod will be scheduled on this node unless the pod has a matching toleration.

A toleration is a property of a pod that specifies that it can run on a tainted node. A toleration must match a specific taint.

You can apply a toleration to a specific pod that matches the taint that was set up earlier. The scheduler is now allowed to place this pod onto the tainted node, but it may also schedule it somewhere else based on other constraints.

  • tolerations are used to specify which Pod can be scheduled on a certain node
  • If a node has a taint=blue then only the Pod with tolerations=blue can be scheduled on the node
  • you can specify the effect of a taint: NoSchedule, the Pod will not be scheduled on the node; PreferNoSchedule, the system tries not to schedule the Pod on the node but it is not guaranteed; NoExecute: new Pod will not be scheduled on the node, existing PODs on the node are terminated and moved elsewhere.
  • to add a toleration to a Pod it is necessary to specify the property tolerations.
  • it does not define a preference for a particular node, to deploy a Pod on a specific node you have to use Node Affinity.
Taints and tolerations
Taints and tolerations: Credits

Predicate: Topology – Node Selectors

- serve a specificare su quale nodo deve essere deployato un POD in base ad una label
- kubectl label nodes <node-name> <label-key>=<label-value>
- usato per criteri di selezione molto semplici.
- va specificata la proprietà "nodeSelector"

How PODs are scheduled?

Manual Scheduling, Taint and Tolerations, Node Selectors, Node Affinity

- Se non presente va specificato a mano il nodo su cui fare il deploy. Viene usata l'opzione "nodeName"
- Se si usa lo scheduler di default l'opzione "nodeName" non va specificata.

Predicate: Topology – Affinity

At times, you may need to make sure that a pod is scheduled on a specific node. Suppose the pod requires a specific hardware resource, such as a graphics processing unit (GPU) or solid state drive (SSD). To make sure that a pod runs on a specific node or instance type, you can use affinity settings. When combined with taints and tolerations, affinity settings make sure that only pods with a correct toleration can be scheduled to a node.

With a nodeAffinity setting, you can make sure that your pod only runs on some instance types. There are two types of node affinity:

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

You can think of them as “hard” and “soft,” respectively. The hard specifies rules that must be met for a pod to be scheduled onto a node. The soft specifies preferences that the scheduler will try to enforce but will not guarantee. The “IgnoredDuringExecution” part of the names means that if labels on a node change at runtime such that the affinity rules on a pod are no longer met, the pod continues to run on the node.

The "preferredDuringScheduling" means that the scheduler will try to run this set of pods on a specific node, however, if that’s not possible, the set is allowed to run elsewhere.

You can also specify affinity and anti-affinity at the pod level to control how pods should be placed relative to other pods. For example, with anti-affinity scheduling, you can make sure that your pods don’t end up on the same node. Do this to make sure the scheduler doesn’t create a single point of failure for a certain pod.

  • Node Affinity and Node Selectors are almost the same, but Node Affinity allows you to specify more complex selection criteria.
  • you can use "NotIn" to exclude some values or "Exists" to check if a specific key exists.

Immagine…

Node Affinity vs Taints & Tolerations

  • Node Affinity allows you to schedule a pod=red on a node=red but does not avoid pod=grey on node=red. For a Node it allows a Pod with a specific color-label but does not preclude other colors from landing on the same node.
  • Taints & Tolerations prevents pod=green from going on node=red, but does not preclude pod=green from going on node=blanck. It moves PODs of a certain color away from a differently colored node, but there is no control over where among the non-colored nodes the Pod is deployed.

Immagine…

Predicate: Topology – DaemonSet

A DaemonSet ensures that all nodes have a copy of the requested pod. This is useful when you want to provide common supporting functionality (such as security, logging, monitoring, or backups) for the pods running your application code. For example, if you need to collect logs from nodes, you can run a DaemonSet. Doing this ensures that a logging daemon pod, such as Fluentd, is run on all nodes. If you delete a DeamonSet, you also delete any pods it created across all the nodes.

You can run these daemons in other ways, such as with an init script instead of a DaemonSet. However, you can use DaemonSets to do the following:

  • Monitor and manage logs for daemons like any other application.
  • Maintain an ecosystem of similar Kubernetes toolsets and API clients.
  • Isolate daemons from application containers.

Replace pods deleted because of unavoidable circumstances, such as node failure.

 

https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/

https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/

 

 

Scheduler:
- puoi creare il tuo scheduler, deployarlo e renderlo di default. 
- ci possono anche essere più schedulers.
>>> usare l'opzione --leader-elect=true in caso di HA per eleggere uno scheduler tra quelli disponibili e deployati sui vari master. Se invece l'obiettivo è usare più scheduler questa opzione va messa a false. L'opzione --lock-object-name=my-custom-scheduler serve a differenziare lo scheduler custom dal default durante il processo di elezione.
>>> usare l'opzione --scheduler-name=my-custom-scheduler per definire il nome dello scheduler per differenziarlo da quello di default.
- puoi usare "schedulerName" nello spec del container per selezionare lo scheduler da usare in caso siano più di uno
- il comando "kubectl get events" può essere usato per vedere gli eventi e quindi gli interventi del nuovo scheduler.
- Se stai deployato più scheduler solo quello che aggiungi deve avere l’opzione --leader-elect=false! Non modificare quello esistente.
- Devi trovare una porta libera per lo scheduler: netstat -tunap | grep <porta>
- La porta trovata va aggiunta come porta di container e bisogna modificare gli altri check: livenessProbe etc.
- nodeSelector necessita dello scheduler
- nodeName, per selezionare dove deployare il POD, funziona senza scheduler

Application Management

Probes for Containers

Distributed systems can be difficult to manage because they involve many moving parts, all of which must work for the system to function. Even if small part breaks, it needs to be detected, routed and fixed, and these actions need to be automated. Kubernetes allows us to do this with the help of probes, but before that we need to understand what is the lifecycle of a Pod.

These are the different phases:

  • When the pod is first created, it starts with a pending phase. The scheduler tries to figure out where to put the pod. If the scheduler cannot find a node to place the pod on, it will remain pending.
  • Once the pod is scheduled, it goes to the container creating phase, where the images needed by the application are pulled and the container starts.
  • Next, it moves to the running phase, where it continues until the program is completed successfully or terminated.

Kubernetes gives you the following types of health checks:

  • Readiness probes: This probe will tell you when your application is ready to serve traffic. Kubernetes makes sure the readiness probe passes before allowing the service to send traffic to the pod. If the readiness probe fails, Kubernetes will not send traffic to the pod until it passes. Kubernetes waits the probe before setting the container to the Ready state, this is very useful in the multi-container case, each could have different start times. What would happen if the application inside container on one of the PODs freezes? New users are impacted, the Pod is Ready even if it is not Alive.  

readinessProbe:
   httpGet: ( tcpSocket | exec )
      path: /api/ready
      port: 8080
   initialDelaySeconds: 10 # Aspetta 10 secondi
   periodSeconds: 5 # Ricontrolla ogni 5 sec
   failureThreshold: 8 # Dopo 8 tentativi Fail

  • Liveness probes: Liveness probes will let Kubernetes know if your application is healthy. If your application is healthy, Kubernetes will not interfere with the functioning of the pod, but if it is unhealthy, Kubernetes will destroy the pod and start a new pod to replace it. In the kube-controller-manager you can set the option --pod-eviction-timeout which defines how long to wait before replacing a Pod after a failure. The Pod is replaced only if it is part of a ReplicaSet or Deployment. What would happen if the application inside container on one of the Pods freezes? The probe allows to specify a condition to check if the application is still ready to receive traffic from users, so if you freeze the application the Pod would be restarted automatically.

livenessProbe:
  httpGet:
     path: /api/healthy
     port: 8080
     <opzioni-come-prima>

  • Startup probes: Startup probes will let Kubernetes know when the container application starts. If such a probe is configured, it disables Liveness and Readiness checks until it succeeds. This can be used with slow-starting containers, preventing them from being killed before they are up and running.
startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10
Kubernetes Probes
Credits to loft.sh
Logging & Monitoring

Logging & Monitoring

Node Selectors Logging, Monitor Cluster Components, Application Logs, (Logging & Monitoring CKAD)

Monitor:


- Node level Metrics: Numbers of Nodes, Numbers of Healthy Nodes, Performance Metric = CPU, Memory, Network, Disk
- Pod Level Metrics: Number of PODs, Performance Metric = CPU, Memory
- Esistono diverse soluzioni per raccogliere le metriche: Metric server, Prometheus, Elastic Stack, Datadog, dynatrace.
- kubectl top node / kubectl top pod

 

Log:
- per visualizzare i log bisogna dare il comando:
>>> kubectl logs -f event-simulator-pod
- l’opzione -f serve a rimanere in ascolto sul POD e visualizzare i log appena vengono generati.
- se ci sono più container in un pod va specificato il nome del container nel comando.
- per visualizzare i log senza kubectl bisogna dare il comando
>>> docker logs container-id

POD Design

POD Design

Architectural patterns are ways to solve common problems. In a distributed solution, there is a set of common functions independent of business logic: logging, configuration, security, etc. Due to the need to implement these functions outside the business container and reuse them as necessary, patterns called sidecars, ambassadors or adapters are used.

  • Sidecar: As with motorcycles, the sidecar is placed next to the bike itself, the sidecar container is also positioned next to the legacy container, to extend its functionality. For example, you have a web service capable of responding to HTTP requests from clients and you want to evolve the solution by adding a security layer, thus implementing the HTTPS protocol. In this case you can use a sidecar container to implement the security layer and is independent of the business logic, can be reused where needed and encapsulate SSL certificates Management. You can use the same approach to extend service API or to collect logs from legacy container and push to monitor service, without having to change the code of the original container. This pattern is similar to the Decorator in object oriented programming.

Immagine..

  • Ambassador: This pattern comes in handy when access to a service needs to be redirected according to a new policy. One of the most interesting applications of ambassador containers is for the Canary Deploy, let's imagine that you want to test a new version of a microservice, you can use an ambassador container to spins a desired percentage of traffic to the new container. This pattern is similar to the Proxy in object oriented programming.

Immagine…

  • Adapter: In this pattern, an adapter container offers standardized output and interfaces across multiple heterogeneous main application containers. In other words, you need to adopt an exposed interface of our legacy container so that it becomes usable by an external service on which we have no opportunity to intervene. In contrast to the ambassador pattern, which presents an application with a simplified view of the outside world, adapters present the outside world with a simplified, homogenized view of an application. An example of using adapter containers is the introduction of a legacy container monitoring layer, to collect metrics data and provide it to applications such as CloudWatch. This pattern is similar to the Adapter in object oriented programming.

Immagine..

Services

Services

Services, ClusterIP, ... 

What is a Service?


- NodePort: rende il POD accessibile all’esterno tramite porta del nodo. Il mapping è creato su tutti gli IP pubblici del cluster. Non c'è bilanciamento, ho più IP che puntano allo stesso service. Uno per ogni nodo.
- ClusterIP: rende il POD accessibile dalla rete interna del nodo, l'IP può essere usato per mettere in comunicazione i POD. Simile ad un Internal LoadBalancer in AWS. 
- LoadBalancer: crea un Loadbalancer per distribuire il carico tra le diverse parti di una applicazione. Viene creato un internet-facing LoadBalancer su AWS.
- Ci sono tre porte:
>>> NodePort: Quella del nodo. 
>>> Port: quella di uscita del service (di fatto è un nuovo POD con un suo IP). 
>>> TargetPort: quella del POD, dove questo accetta le richieste. Il POD deve aver specificato una porta nella sua definizione.
- Il POD a cui il service fa riferimento è specificato con la label.
- Il Service è raggiungibile usando il ClusterIP oppure il service name.

 

Services:
- I servizi sono gestiti da una componente diversa
  - kubelet: si occupa di creare i POD
  - kube-proxy: si occupa della creazione dei servizi
- Un altro modo per mettere in comunicazione i POD è tramite i Service. 
- Quando un servizio è creato questo diventa accessibile a tutti gli altri POD all’interno del cluster. 
- A differenza del POD, che è allocato su un solo Nodo, il servizio è allocato su tutto il cluster, sono oggetti che esistono a livello di cluster. 
- In realtà non esistono per niente, sono degli oggetti virtuali: non c’è in realtà nessun server in ascolto sul IP:PORTA del servizio.
- Il Sercice ha bisogno del network plugin. Il POD non viene deployato se non è possibile assegnare un IP, tranne se in None Network. 
- Senza network plugin il service non riesce ad indirizzare tutti i POD.
- IP range dei POD è diverso da quello dei service, non si devono mai sovrapporre.
  $ kubectl get pod kube-apiserver-kind-control-plane -n kube-system -o yaml
    spec:
      containers:
      - command:
        - kube-apiserver
        - [..]
        - --service-cluster-ip-range=10.96.0.0/16
        - [..]
  $ kubectl get pod kube-controller-manager-kind-control-plane -n kube-system -o yaml
    spec:
      containers:
      - command:
        - kube-controller-manager
        - [..]
        - --cluster-cidr=10.244.0.0/16
        - [..]
- kube-proxy crea le regole in diverse modalità
  - userspace: kube-proxy in ascolto su una porta
  - ipvs: 
  - iptables: (Default)
- Si può visualizzare l’ipostazione di traduzione usando il comando iptables -L -t net | grep <...>
- I log di kube-proxy sono in /var/log/kube-proxy.log
- What network range are the nodes in the cluster part of?
  Run the command ip addr and look at the IP address assigned to the ens3 interfaces. Derive network range from that.

- Il servizio <service-name> è accessibile nello stesso namespace usando il suo nome: curl http://service-name
>>> Il nome completo del servizio è service-name.default.scv.cluster.local
- Il servizio <service-name> è accessibile dal namespace <namespace-name> usando il nome del servizio e del namespace: curl http://service-name.namespace-name
>>> Il nome completo del servizio è service-name.namespace-name.scv.cluster.local
- Lo stesso servizio può essere indirizzato usando:
>>> service-name
>>> service-name.namespace-name
>>> service-name.namespace-name.scv
>>> service-name.namespace-name.scv.cluster.local
- cluster.local è il root domain del cluster: non può essere spezzato
- Per i POD k8s non aggiunge un dominio, ma si può richiedere manualmente.
- La configurazione di coreDNS sta in /etc/coredns/Corefile
- Nella doc a https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ puoi trovare la configurazione di CoreDNS
- Nel file di configurazione possiamo notare la definizione di 
  - cluster.local: il root domain del cluster
  - pods insecure: per associare un name al pod 10-244-1-5.dafault… la conversione del IP nel formato con i trattini è disattivata di default.
- Solitamente il Corefile è passato al POD come oggetto configmap.
- Il servizio CoreDNS si occupa di aggiungere al suo database le informazioni necessarie ogni volta che un POD o SERVIZIO viene creato.
- Affinchè i POD possano raggiungere il DNS viene creato un servizio. 
- La configurazione del DNS è fatta automanticamente dal kubelet che aggiunge l’informazione sul nameserver nel file /etc/resolv.conf
- Nel file /etc/resolv.conf c'è anche la regola search per indirizzare i servizi senza usare il nome completo

State Persistence & Storage

State Persistence & Storage

Applications can have access to shared volumes to facilitate data sharing in the pod and persistence of data across container restarts.

In Kubernetes, a volume is a directory that containers in a pod can access. A PersistentVolume (PV) is storage in your cluster that has already been provisioned and is independent of any individual pod. You can request storage with a PersistentVolumeClaim (PVC).

If cluster administrators want to offer varied PVs without exposing volume implementation details, they can use StorageClasses to describe the different storage options.

This PodSpec example includes a volume named redis-persistent-storage, which is mounted on the container.

The CSI is a standard for exposing arbitrary block and file storage systems to containerized workloads on Container Orchestration Systems (COS) like Kubernetes.

Using CSI, third-party storage providers can write and deploy plugins exposing new storage systems in Kubernetes without ever having to touch the core Kubernetes code.

AWS provides CSI drivers for the different types of storage you can expose to Kubernetes.

 

Volumes, PV, PVC, Using PVC in PODS, Storage Classes, Stateful Sets, Headless Services

Storage in Kubernetes



- cerca persistent volume sulla documentazione
- Quando un container docker viene messo in esecuzione viene creato un ulteriore layer in cui vengono fatte le modifiche, è cancellato quando il container termina.
- Per persistere quelle modifiche vanno usati i volumi. Posso fare mount di una cartella esistente oppure di una nuova vuota.
- Di seguito come utilizzare un volume in K8s per salvare i dati del POD in un path specifico sul host.
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /opt
      name: data-volume
  volumes:
  - name: data-volume
      hostPath: 
        path: /data
        type: Directory
- La modalità di prima salva sul nodo, se il POD riparte su un nodo diverso non ha i dati
- Puoi crare un pool di storage usando i Persistent Volume. Il POD fa Claim del PV quando parte in base ad alcune prorpietà che specifichi
- Persistent Volume (PV) e Persistent Volume Claims (PVC) sono due cose diverse. L’admin crea i PV e l’utente li usa tramite i PVC
- Il collegamento tra PV e PVC avviene usando le labels. La selezione può avvenire anche su criteri differenti come la capacità, la modalità di accesso, la classe di storage.. etc
- Esiste una relazione 1:1 tra PVC e PC. K8s cerca di ottimizzare anche lo spazio, ma se non riesce attacca il PV ad un PVC più grande
- Puoi cancellare il PVC senza cancellare il PV. Il comportamento del PV di defalt è retain, non può essere usato da altri PVC.
>>> persistentVolumeReclaimPolicy: Retain
>>> persistentVolumeReclaimPolicy: Delete
>>> persistentVolumeReclaimPolicy: Recycle
- In questo ultimo caso i dati nel PV sono cancellati e quindi il Volume può essere utilizzato per altri PVC.
- Per fare mount di un PVC in un POD:
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: block-pvc
- Il POD si collega al PVC specificando il nome, il PVC si collega al PV eventualmente tramite il selector
- In un PV puoi fare riferimento in maniera statica ad un disco EBS. Questo approccio si chiama Static Provisioning.
- Puoi fare Dynamic Provisionging usando le Storage Class. La Storaga Class crea automaticamente anche il Persistent Volume.
- Il POD si collega al PVC specificando il nome, il PVC si collega alla StorageClass specificando il nome storageClassName.
- Nella modalità “local-storage” l’allocazione avviene in maniera automatica. Per questa modalità il Volume Binding Mode è: WaitForFirstConsumer.

Networking

Networking

DNS, CNI, Cluster Networking, Pod Networking, Network Policies, Ingress

Networking - Intro: 
- https://www.tecmint.com/ip-command-examples/
- Ipotizziamo che uno switch crea una rete in 192.168.1.0, il seguente comando assegna / toglie l'interfaccia specifica dalla rete
>>> ip addr add 192.168.1.10/24 dev eth0 / ip addr del 192.168.1.10/24 dev eth0
- Si può visualizzare la configurazione
>>> ip addr show
- Una volta assegnato l'IP bisogna abilitare / disattivare l'interfaccia
>>> ip link set eth0 up / ip link set eth0 up
- Se abbiamo più reti ( 192.168.1.0 / 192.168.2.0 ) abbiamo bisogno di un Router. Il Router ha più indirizzi IP, uno per ogni rete a cui è connesso.
- Puoi visualizzare le informazioni nella routing table 
>>> ip route show
- Puoi aggiungere una rotta statica o manuale, il traffico deve passare tramite un default gateway. 
>>> ip route add 192.168.1.0/24 via 192.168.2.1
- L'indirizzo 192.168.2.1 è l'IP nel router nella rete destinazione, puoi fare lo stesso per la rotta al contrario
>>> ip route add 192.168.2.0/24 via 192.168.1.1
- Puoi cancellare la rotta inserita in precedenza
>>> ip route del 192.168.2.0/24
- Per accedere ad internet serve una nuova rotta. Si usa di solito la rotta di default, oppure 0.0.0.0/0. 
- Quando c'è la rotta di default ogni volta che viene generato un pacchetto non riferito alla rete locale viene inviato al router.
>>> ip route add default via 192.168.50.100 
- L'IP 192.168.50.100 è quello del router, su AWS è configurato automaticamente
- In caso si debba attivare la comunicazione dal NodoA al NodoC passando per il NodoB è possibile ripetere i comandi <ip route add> di prima ed inoltre è necessario il forward.
- Di default linux non fa il forward di un pacchetto da una interfaccia di rete ad un altra.
- Per abilitare il forward bisogna settare ad 1 il valore nel file /proc/sys/net/ipv4/ip_forward
- Lo stesso valore deve essere modificato in /etc/sysctl.conf
- Questi comandi sono validi fino al riavvio, se vuoi rendere il tutto persistente devi speficicare la configurazione in /etc/network/interfaces file.

Networking - DNS:
- Ci sono due file importanti per la configurazione del DNS
>>> /etc/resolv.conf
>>> /etc/hosts
- Il file hosts locale ha priorità sul DNS.
- Il DNS è usato in luogo del file host poichè al crescere del numero di nodi nel cluster la gestione dei nomi diventa troppo oneroso
- Di seguito un esempio di records DNS:
  192.168.1.10 db.example.com
  192.168.1.11 web.example.com
  192.168.1.12 app.example.com
- Per evitare di dover scrivere ogni volta l’intero indirizzo possiamo configurare una entry search nel file resolv.conf. Il sistema aggiungerà automaticamente la parte mancante.
>>> cat /etc/resolv.conf
    nameserver <ip-del-dns> # punta al file di sopra
    search example.com
- Puoi continuare ad usare lo stesso l’indirizzo esteso. Quindi puoi risolvere "db" senza scrivere "db.example.com"
- Per testare la risoluzione di un DNS puoi usare nslookup oppure dig
- La soluzione DNS più utilizzata in k8s è CoreDNS

Networking - Namespace:
- i Namespace sono usati dai conteiner engine (es docker) per implementare l’isolamento a livello di rete.
- L’isolamento fa in modo che i container non abbiano visibilità della configurazione di rete del host.
- il container nel namespace ha la sua tabella di routing e arp così come una sua interfaccia di rete virtuale.
- Per creare un nuovo namespace usa il comando:
>>> ip netns add gialloenv
- Per vedere le network all’interno del namespace devo specificare il nome del namespace, stessa logica del kubectl
>>> ip -n gialloenv link
>>> ip -n gialloenv route add 0.0.0.0/0 via 10.0.128.1
>>> ip -n gialloenv route show
- Se abbiamo più namespace diventa oneroso metterli in comunicazione tra loro. Mettere in comunicazione i vari Namespace è come fare vpc-peering. Se il numero di Namespace è alto bisogna creare delle peer connection per ogni possibile coppia di Namespace. Possiamo risolvere crearendo uno switch virtuale, come se fosse il TGW. 
- In questo modo non la rete interna continua ad essere isolata, possiamo risolvere aggingendo un NAT virtuale (SourceNAT).
>>> iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE
- Per esporre il container su internet abbiamo anche bisogno di un DestinationNat virtuale (l'equivalente IGW in AWS)
>>> iptables -t nat -A PREROUTING --dport 80 --to-destination 192.168.15.2:80 -j DNAT

Networking - Docker:
- In fase di avvio un container può essere eseguito in diverse opzioni di rete:
-- None: il container non è assegnato a nessuna rete. Il container non è raggiungibile, nè può raggiungere, internet.
-- Hosts: il container condivide la stessa rete del host. Non c’è nessun isolamento a livello di rete. In questa configurazione se esegui un container sulla porta 80 del container allora sara raggiungibile sulla porta 80 del host. Non serve fare nessun port mapping. Se provi ad avviare una nuova istanza dello stesso container però questa non potrà essere eseguita poichè la porta 80 è già occupata.
-- Bridge: In questo caso ogni container ha il suo indirizzo IP. Non condivide l'IP del host come nel caso precedente. 
>>> ubuntu@ip-10-0-0-100:~$ sudo docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    76db995ec23c        bridge              bridge              local
    d4883446d072        host                host                local
    b213c7b9c434        none                null                local
- Puoi vedere la rete BRIDGE (creata di default da docker) usando il comando docker network ls
- L’interfaccia di rete assegnata alle rete bridge è visibile usando il comando ip link
>>> ubuntu@ip-10-0-0-100:~$ ip link
    3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
        link/ether 02:42:ad:4f:03:60 brd ff:ff:ff:ff:ff:ff
- I container sono eseguiti nella rete 172.17.0.1/16
>>> ubuntu@ip-10-0-0-100:~$ ip addr
    3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:ad:4f:03:60 brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
           valid_lft forever preferred_lft forever
        inet6 fe80::42:adff:fe4f:360/64 scope link
           valid_lft forever preferred_lft forever
- Quando viene eseguito un container docker crea un namespace appositamente per il container. TODO Verificare, non mi pare lo faccia!
- Per vedere quale indirizzo IP ha il container nella Bridge Network 
>>> docker inspect <container-id>
- Quando lanciamo il comando <docker run -d -p 8080:80 nginx> viene automaticamente creato un NAT che espone il container sulla rete del host alla porta 80.
- Si può visualizzare l’impostazione creata automaticamente da docker usando il comando iptables
>>> root@ip-10-0-0-100:/home/ubuntu# iptables -nvL -t nat | grep 8080
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.6:80


Networking - CNI:
- la configurazione della rete è molto simile tra le varie soluzioni: docker, namespace, stc. Non ha senso ripeterla più volte.
- in k8s viene definita la Container Network Interface che ha lo scopo di gestire le differenze tra le diverse soluzioni senza dover implementare più volte le parti comuni.
- a seconda della versione di k8s docker non è supportato da CNI, k8s crea prima il container nella None Network e poi esegue i comandi di configurazione a "mano"
- Ogni componente in k8s deve comunicare con le altre in un contesto distribuito
- Ogni componente richiede venga aperta una specifica porta:
  - kube-api 6443
  - kubelet 10250 (la stessa su tutti i worker node)
  - kube-scheduler 10251
  - kube-controller-manager 10252
  - etcd 2379 è la porta a cui tutte le componenti si connettono, 2380 è quella peer-to-peer
- Al momento k8s non mette a disposizione una soluzione managed per mettere in comunicazione i POD all’interno del cluster.
- Tutti i comandi di configurazione della rete devono eseguiti automaticamente da CNI, dobbiamo però abilitare un plugin
- Per sapere tra tutti i plugin disponibili quale sta usando in questo momento k8s possiamo guardare alla configurazione di kubelet.
>>> systemctl status kubelet.service
- Dal comando precedente puoi determinare il valore di --network-plugin=
- Controlla se sono presenti --cni-bin-dir= e --cni-conf-dir= . Se non presenti allora sono settati al default che trovi nella doc
>>> --cni-bin-dir=/opt/cni/bin #default
>>> --cni-conf-dir=/etc/cni/net.d # default
- Documentazione per kubelet https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/
- Se ci sono più file in questo path viene scelto il primo in ordine alfabetico: puoi usare il nome per avere più versioni e scegliere quella da usare.
- Come dicevamo prima, al momento k8s non mette a disposizione una soluzione di rete managed. Una delle soluzioni è usate è weaverworks, ma c'è anche calico.
- Al momento c'è ancora un posto nella documentazione dove puoi trovare il comando esatto per deployare il weave network addon:
>>> https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/#steps-for-the-first-control-plane-node (step 2)
- CERCA high availability nella DOC
- Per selezionare un IP “disponibile” abbiamo due opzioni: dhcp, host-local
- Questa informazione viene specificata nel file di configurazione alla sezione ipam.
- weaveworks usa una rete 10.32.0.0/12 che viene equamente splittata su tutti i nodi del cluster.
- Per vedere default gateway è stato assegnato ad un cluster usa: ip route show
node03 $ ip route
default via 172.17.0.1 dev ens3 
10.32.0.0/12 dev weave proto kernel scope link src 10.32.0.1 <-- IP usato per uscire su internet
172.17.0.0/16 dev ens3 proto kernel scope link src 172.17.0.48 
172.18.0.0/24 dev docker0 proto kernel scope link src 172.18.0.1 linkdown 


ATTENZIONE
ESISTE UNA POLICY PER NEGARE TUTTO!
https://kubernetes.io/docs/concepts/services-networking/network-policies/
Default allow all ingress traffic
ATTENZIONE

Ingress:


- per usare gli ingress serve un ingress controller. Si può usare nginx, haproxy.. etc
- by default non c’è un Ingress Controller deployato in k8s, devi installarne uno tu.
- Si può deployare un IngressController come si farebbe con un qualsiasi altro Componente K8s. Esso configura automaticamente ogni parte coinvolta nella definizione di un Ingress.
- La configurazione in merito a: err-log-path, keep-alive, ssl-protocols, viene passata alla definizione del ingress controller come ConfigMap.
- Una buona pratica è passare la config map ugualmente, anche se vuota, in modo da poter modificare la configurazione in una fase successiva.
- La config map va specificata tra gli args
    args:
     - /nginx-ingress-controller
     - --configmap=$(POD_NAMESPACE)/nginx-configuration
    env:
     - name: POD_NAME
       valueFrom:
         fieldRef:
           filedPath: metadata.name
     - name: POD_NAMESPACE
       valueFrom:
         fieldRef:
           fieldPath: metadata.namespace
    ports:
     - name: http
       containerPort: 80
     - name: https
       containerPort: 443
- le variabili di ambiente specificate vengono lette direttamente dai metadata, inoltre serve specificare le porte.
- bisogna esporre il POD al mondo esterno, creiamo un servizio: NodePort
- L'ingress controller monitora il cluster è modifica le componenti di k8s, ad esempio aggiuge la configurazione al DNS, per fare questo ha bisogno dei permessi.
- Viene creato un ServiceAccount (equivalente Ruolo su AWS) a cui tramite RoleBindings viene associato un ClusterRole (equivalente Policy su AWS)
- In definitiva abbiamo bisogno di: Deployment, Service, ConfigMap e Auth=(ServiceAccount)
  - kubectl create namespace ingress-space
  - kubectl create configmap nginx-configuration -n ingress-space
  - kubectl create serviceaccount ingress-serviceaccount -n ingress-space
  - kubectl create role.. -n ingress-space
  - kubectl create rolebindings.. -n ingress-space

- Una volta configurato il controller dobbiamo aggiungere le regole. Le regole possono essere di diverso tipo:
  - si può indirizzare verso un singolo pod
  - si può indirizzare un pod in base al path
  - si può indirizzare un pod in base al dominio
- Se abbiamo due POD (wear e video), ed abbiamo due servizi ad essi associati, per esporre le componenti su un dominio unico abbiamo bisogno di una Rule e due Path
- L'ingress crea automaticamente un “Default backend”. Se l’utente prova a specificare un path non disponibile in nessuna regola allora questo viene rediretto verso il default.
- Per fare route in base al dominio bisogna specificare anche il campo host. Se non è specificato allora si assume * (tutto il traffico in arrivo).
- Se non viene specificato il path si assume / “root"
- Si possono passare delle configurazioni all'ingress controller, ad esempio questa:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /

  http://<ingress-service>:<ingress-port>/watch --> http://<watch-service>:<port>/
  http://<ingress-service>:<ingress-port>/wear --> http://<wear-service>:<port>/
  trasforma il path per ottenere quello / "root"

Security

Security

Security Contexts,

FROM CKA

 

K8s SSL Certificates
K8s SSL Certificates

Autorizzazione:
- In un cluster k8s devi dire chi può accedere al cluster e cosa può fare. 
- Serve autenticarsi verso il Kube ApiServer usando una delle modalità disponibili.
- Tutte le rischieste da parte degli utenti arrivano al kube apiserve. Il kube-apiserver autentica la richiesta e poi la porcessa.
- Kube-ApiServer supporta una modalità basic-auth. Puoi specificare un csv con i dati di login e definire il suo path con l'ozione --basic-auth-file=credentials.csv
- La modalità basic-auth è quella peggiore.
- In kubernetes ho questa situazione:
>>> la comunicazione tra i nodi deve essere su TLS
>>> la comunicazione tra l’admin ed il master usa TLS
>>> la comunicazione tra kube-api e kube schduker usa TLS
>>> sia client che server devono autenticarsi
- Ho bisognod i certificati generati da una CA
Di seguito vengono riportati i principali certificati usati da K8S:
>>> kube-api: apiserver.crt & apiserver.key
>>> etcd: etcdserver.crt & etcdserver.key
>>> kubelet: kubelet.crt & kubelet.key
- Il certificato è la parte pubblica, viene condiviso con il cliente ed è usato per cifrare il messaggio destinato al server. Il server usa la key per decifrare il messaggio ricevuto, il server è l'unico ad avere la key giusta. Questa modalità di comunicazione implicitamente autentica il server verso il client.
- Tutte le componenti devono parlare con kube-api, quindi devono autenticarsi usando un certificato.
- L’Administrator, lo scheduler, il controller ed il proxy sono tutti client dal punto di vista del kube-api server. Devono autenticarsi.
- Il kube-api server a sua volta deve comunicare con l’etc-server e con kubelet, in questo caso kube-api è cliente e deve autenticarsi.
- Nella configurazione di kube-apiserver troviamo:
>>> --client-ca-file=/etc/kubernetes/pki/ca.crt è usato dai cliente di kube-api: utenti, scheduler, controller e proxy. Per verificare che i certificati esposti dai client siano validi.
>>> --etcd-cafile: SSL Certificate Authority file used to secure etcd communication. Per verificare che il certificato esposto da ETCD sia valido.
>>> --etcd-certificate=/etc/kubernetes/pki/apiserver-etcd-client.crt SSL certification file used to secure etcd communication. Cifra i messaggi diretti a ETCD.
>>> --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client-key SSL key file used to secure etcd communication. Decifra i messaggi in arrivo da ETCD. 
>>> --kubelet-client-certificate: Path to a client cert file for TLS. Il client si autentica, espone il suo certificato che userà kubelet per verificare l'identità di kube-api.
>>> --kubelet-client-key: Path to a client key file for TLS. I messaggi cifrati con kubelet-client-certificate sono decifrati usando kubelet-client-key.
>>> --tls-cert-file: File containing the default x509 Certificate for HTTPS. Viene esposto da kube-api ed usato dai client per comunicare in maniera cifrata.
>>> --tls-private-key-file: File containing the default x509 private key matching --tls-cert-file. Usato da kube-api per decifrare il messaggio cifrato con tls-cert-file.
- Se ci sono problemi con i certificati bisogna controllare i log, ci sono varie modalità:
>>> journalctl -u etcd.service -l, se k8s è configurato come servizio
>>> kubectl logs etcd-master, se k8s è configurato con kubeadm
>>> docker ps -a -> docker logs <container-id-etcd>, se kubeapi non funziona
- Alla certificazione potrebbero esserci dei problemi con i certificati.
- Per aprire un certificato usare il comando <  openssl x509 -in /apth/to/cert.crt - text -noout >

Security in Kubernetes


Capabilities or Security Context

- Per ragioni di sicurezza puoi anche eseguire un container come non non-root user
>>> docker run --user=1001 ubuntu sleep 3600
- oppure puoi aggiungere capabilities nel seguente modo
>>> docker run --cap-add MAC_ADMIN ubuntu
- Le capabilities sono un modo per suddividere i privilegi normalmente associati ad un superuser in unità distinte. 
- Le capabilities possono essere abilitate e disabilitate in maniera indipendente.
- Le capabilities in k8s possono essere specificate a livello di container oppure a livello di POD.
- Se fatta a livello di POD la configurazione è applicata a tutti i container. Se la configurazione è fatta sia a livello di container che a livello di POD in questo caso la configurazione sul container sovrascrive quella sul POD.
- Alla definizione deve essere aggiunta l'opzione securityContext

Network Policies:
- tutti i pod possono comunicare tra di loro di default, è possibile limitare la comunicazione usando le network policy.
- posso usare le network policy per impedire che in una three tier architecture il webserver possa comunicare con il database senza passare per l'application server
- Le Network Policy in K8S sono Stateful
- Per applicare una policy di rete ad un POD si usano i selector. La policy di seguito è applicata ad un POD che ha una label ”role: db”
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-policy
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          role: api-pod
    ports:
    - protocol: TCP
      port: 3306
- Le Network Policy sono dipendenti dal tipo di soluzione di rete usiamo in K8s. Non tutte le soluzioni di rete supportano le Network Policy. Nel caso in cui la policy non è supportata puoi ugualmente crearla ma non sarà applicata.

Users

Users Permissions and Management

Gestione Utenti e Permessi, Service Account

- Controlla la Doc: https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/
- Usa la Docs di k8s: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/

- Per aggiungere un utente a k8s è necessario:
1. generare una coppia chiave pubblica e privata
2. firmare la chiave pubblica usando la CA configurata su k8s. 
- Gli step precedenti servono ad autenticare il nuovo utente, l’administrator deve validare tutti gli step e approvare la richiesta di firma.
- Per firmare i certificati servono le chiavi dalla CA, questi file devono essere salvati in maniera sicura in un server CA.
- La certificate-api consente di inviare direttamente a kubernetes la richiesta di firma.
>>> openssl req -newkey rsa:2048 -subj /CN=giallo -keyout giallo.key -out giallo.csr 
- Convenzione sui certificati:
  Common names (/CN) are mapped to the name of users
  Organizations (/O) are mapped to the name of groups
  -subj "/CN=andy/O=network-admin"

I comandi da lanciare sono i seguenti:
  >>> openssl genrsa -out giallo.key 2048
  >>> openssl req -new -key giallo.key -out giallo.csr -subj "/CN=giallo,/O=network-admin"

  >>> cat <<EOF | kubectl apply -f -
      apiVersion: certificates.k8s.io/v1
      kind: CertificateSigningRequest
      metadata:
        name: giallo
      spec:
        groups:
        - system:authenticated
        request: $(cat giallo.csr | base64 | tr -d "\n")
        signerName: kubernetes.io/kube-apiserver-client
        usages:
        - client auth
      EOF

  >>> kubectl get csr
  >>> kubectl certificate approve giallo
  >>> kubectl get csr giallo -o jsonpath='{.status.certificate}'| base64 -D > csr-giallo.crt
  >>>> openssl x509 -noout -text -in csr-giallo.crt
  >>> kubectl create clusterrole network-admin \
        --verb=create --verb=get --verb=list --verb=update --verb=delete --resource=ingressclasses,ingresses,networkpolicies --dry-run=client -o yaml 
  >>> kubectl create clusterrolebinding network-admin-binding-giallo --clusterrole=network-admin --user=giallo --dry-run=client -o yaml
  >>> kubectl config set-credentials giallo --client-key=giallo.key --client-certificate=csr-giallo.crt --embed-certs=true
  >>> kubectl config get-clusters
  >>> kubectl config get-users
  >>> kubectl config set-context giallo --cluster=kind-kind --user=giallo
  >>> kubectl config use-context giallo
- Normalmente le informazioni di autenticazione andrebbero specificate in tutti i comandi, tuttaia questo è molto scomodo.
- di default il comando kubectl cerca il file config nella cartella $HOME\.kube\config. Così facendo è possibile evitare di specificare il file di configurazione ogni volta.
- di default il comando kubectl cerca il file config nella cartella $HOME\.kube\config. Così facendo è possibile evitare di specificare il file di configurazione ogni volta.
- Il KubeConfig file usa un formato ben preciso, in esso possiamo identificare tre diverse sezioni:
  - Clusters: definisce a cosa ci stiamo collegando
  - User: definisce chi siamo
  - Contexts: definisce quale user deve essere usato per loggare in un certo cluster
- Questa operazione non crea utenti o cluster, permette di riferirsi ad entità già esistenti.
>>> kubectl config -h
- Nel context puoi specificare il namespace di default
  contexts:
  - name: cname
    context:
      cluster: cluster-name
      user: user-name
      namespace: dev
- Voglio avere la possibilità di configurare i permessi affinchè ogni utente possa fare soltanto il minimo necessario. 
- Voglio inoltre poter introdurre delle restrizioni a livello di namespace.
- Ci sono diverse tipologie di autorizzazioni:
  - Node: autorizzazione a livello di nodo. La Node Authorization si riferisce alle autorizzazioni necessarie a mettere in comunicazione le varie componenti di k8s, l’autenticazione avviene tramite certificati. Ogni richiesta che arriva dal gruppo “system-node” viene autorizzata dal Node Authorizer. Quali permessi garantire dipende dalla componente individuata dal certificato (????????)
  - ABAC: autorizzazione a livello di Attribute Based Access Control. Nel modello ABAC viene creato un policy file che contiene tutti i permessi da assegnare all’utente, successivamente il file viene inviato al api-server. Ogni volta che biosgna aggiungere o modificare una policy bisogna intervenire sul file di policy e riavviare manualmente il kube-api server. Gestire la modalità ABAC è quindi molto complesso.
  - RBAC: Role Based Access Control. La modalità RBAC rende il tutto più facile. Invece di associlare i permessi direttamente agli utenti creiamo dei ruoli (le policy in aws). In questo modo è sufficiente modificare il ruolo.
  - Webhook, danno la possibilità di esternalizzare la gestione dei permessi.
- Nel comando di avvio di kube-apiserver puoi specificare --authorization-mode=Node,RBAC per abilitare in ordine la modalità Node e poi RBAC, se non fa match la prima allora c'è la seconda. Puoi anche attivare ABAC, Webhook insieme a AlwaysAllow (consenti sempre) e AlwaysDeny (nega sempre). Se non diversamente specificato AlwaysAllow è la modalità di default.
- Quando un utente effettua una richiesta il primo a rispondere è il Node. Siccome il Node gestisce solo le rischieste a livello di nodo risponderà con Deny. La richiesta a questo punto viene inviata al successivo nella catena: RBAC. E così via..
- Puoi verificare di avere i permessi per fare una certa operazione. Se sei un administrator puoi anche impersonare altri utenti e verificare i permessi.
- Puoi anche limitare l’accesso a livello di singola risorsa, supponiamo tu voglia limitare l’accesso al singolo pod usando:
  apiVersion: rbac.authorization.k8s.io/v1
  kind: Role
  metadata:
    namespace: custom-ns
    name: dev
  rules:
  - apiGroups: [""]
    resources: ["pods"]
    resourceNames: ["my-pod"]
    verbs: ["update", "get", "create"]
- Per limitare l’accesso al singolo namespace, puoi specificarlo nel metadata: custom-ns.

- Quando parliamo di Role & RoleBindings questi sono legati ad uno specifico Namespace. Se non diversamente specificato questi sono creati nel Namespace di default.
- Esistono tuttavia alcune risorse che esistono fuori dal concetto di Namespace. Per ottenere la lista completa delle risorse:
>>> kubectl api-resources --namespaced=true
>>> kubectl api-resources --namespaced=false
- ClusterRole sono Role che si riferiscono alle risorse di cluster. In realtà puoi anche creare un ClusterRole per una risorsa all’interno del namespace. Questo tipo di configurazione consente all’utente di accedere alla risorse in tutti i namespaces. Questa configurazione è utile, ad esempio, per consentire l’accesso all’utente a tutti i secrets in tutti i namespaces.

Design & Install

Design and Install K8s Cluster & kubeadm

CKA..

How to Install Kubernetes?


>>> sudo apt-get install -y kubelet kubeadm kubectl
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
>>> kubeadm init
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
- Eseguire alcuni comandi che suggerisce lui alla fine
- Salvare il token di join
- ssh nel nodo e fare join sul master
>>> kubeadm join ..
- Installare il plugin di rete
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/
>>> kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
- Installare ingress controller
- https://kubernetes.github.io/ingress-nginx/deploy/

Cluster Maintenance

Cluster Maintenance

OS Upgrade, Cluster Upgrade, Backup and Restore ETCDCTL

Master HA:
- per cluster molto grandi può convenire separare alcune componenti
- se fallisce il master i worker (e tutte le risorse) continuano a funzionare, ma senza master in caso di problemi nessuno può ripristinare il servizio
- kube-apiserver può lavorare in modalità active-active con un bilanciatore
- il control manager non può lavorare in modalità multi-master, deve esere eletto un leader. Ogni controller-manager prova ad acquisire il lock su un service, il primo che riesce viene eletto. La procedura viene ripetuta periodicamente per gestire eventuali fallimenti.
>>> kube-controller-manager --leader-elect true [other options] --leader-elect-lease-duration 15s --leader-elect-renew-deadline 10s --leader-elect-retry-period 2s
- lo stesso si verifica con lo Scheduler. Non può lavorare in modalità Active-Active perchè si andrebbero a generare azioni duplicate.
- Per quanto riguarda ETCD abbiamo due opzioni:
>>> Stacked Topology: più facile da gestire e configurare, meno server, più rischiosa in caso di fallimento. Il processo ETCD è installato insieme agli altri nel cluster.
>>> External ETCD Topology: Meno rischiosa, un fallimento su una degli altri componenti non impatta ETC. Servono più server ed è più difficile da configurare.
- In entrambe le modalità dobbiamo assicurarci che il API Server punti al endpoint corretto per ETCD.
>>> --etcd-servers= lista di indirizzi a cui risponde ETCD
- ETCD è un database key-value nosql distribuito. In riferimento al CAP Theorm è implementato come CP. Ci sta un solo master. In fase di scrittura tutte le richieste arrivano al master, in fase di lettura tutti i nodi possono rispondere.
- ETCD usa un meccanismo di elezione chiamato RAFT. Ogni nodo inizializza un timer random, allo scadere fa una richiesta a tutti gli altri di essere leader. I nodi che ricevono la richiesta accettano. Il nuovo leader trasmette un segnale a tutti gli altri ad indicare che è healthy, diversamente il processo di elezione viene ripetuto. Serve il Quorum (numero dispari di nodi).
>>> l'impostazione --initial-cluster in etcd.service ci consente di definire dove gli altri nodi del cluster si trovano.

 

Cluster Maintenance


- drain: il comando <kubectl drain node01> termina in maniera sicura i POD che vengono poi spostati su altri nodi. In questo modo è possibile fare manutenzione sul nodo. Il Nodo viene Taint NoSchedule in questo modo nessun nuovo POD può essere eseguito finchè non è esplicitamente rimossa la restrizione.
- uncordon: il comando <kubectl uncordon node01> può essere eseguito una volta completata la manutenzione. Rimuove la limitazione allo scheduler, ma non rimette automaticamente al loro posto i vecchi POD spostati in fase di drain.
- cordon: il comando <kubectl cordon node01> marca il nodo come NoSchedule senza fare il drain.
- tutte le componenti devono essere della stessa versione del kube-apiserver, oppure al massimo le altre componenti possono essere fino a 2 versioni precedenti. 
- ricorda: kube-apiserver è la componente con cui interagiamo e che interagisce con tutte le altre.
- questa differenza tra le versioni mi consente di effettuare degli upgrade live. Possiamo aggiornare una componente per volta.
- supportate sempre le ultime tre minor version. Quindi se v1.12 è l’ultima release k8s supporta anche v.1.11 e v1.10: va considerata la minor version secondo la convezione "version.minor.release"
- per aggiornare il cluster posso: 1. delegare al cloud provider, 2. usare kubeadm, 3. manualmente una componente per volta
- bisogna aggiornare prima il master e poi tutti gli altri
- kubeadm non installa/aggiorna kubelet

 

Aggiornamento del Master


- durante la manutenzione del master le componenti kube-apiserver, kube-scheduler e controller-manager vanno down. 
- questo non significa che glie le applicazioni sui Workers vanno down.
- non puoi accedere alle api, non puoi deployare applicazioni o cancellare pod, il controller-manager non andrà a sostituire i POD in caso di fallimento.
1. aggiornare kubeadm, questo tool segue le stesse logiche di versionamento di kubernetes. Usare il comando <apt-get upgrade -y kubeadm=1.12.0-00>
2. verificare la possibilità di aggiornare facendo un plan. Usa il comando <kubeadm upgrade plan>. ATTENZIONE: Possiamo aggiornare una versione per volta.
3. applicare l'avanzamento di una versione usando il comando <kubeadm upgrade apply v1.12.0>
- Dopo l’aggiornamento se esegui il comando get nodes fa ancora vedere la versione precedente, il comando mostra la versione del kubelet per ogni nodo non la versione del api-server.
4. aggiornare kubelet sul master node (se presente) usando il comando <apt-get upgrade -y kubelet=1.12.0-00>
5. riavviare kubelet <systemctl restart kubelet>
- A questo punto il comando <kubectl get nodes> mostra correttamente l'aggiornamento del master.

Aggiornamento dei Nodi





- aggiornare tutti insieme, in questo caso abbiamo disservizio.
- aggiornare un nodo alla volta, facciamo il drain dei nodi e li aggiorniamo fino ad aggiornare tutto
- aggiungere nuovi nodi al cluster, i nuovi avranno la versione aggiornata, si possono cancellare uno per volta quelli vecchi usando il drain.
1. Fare Drain del Nodo, usa il comando <kubectl drain node01>, poi fai ssh nel nodo
2. Aggiornare kubeadm, usa il comando <apt-get upgrade -y kubeadm=1.12.0-00>
3. Aggiornare kubelet, usa il comando <apt-get upgrade -y kubelet=1.12.0-00>
4. Aggiornare la configurazione del nodo <kubeadm upgrade node config --kubelet-version v1.12.0.00>
5. Riavviare kubelet <systemctl restart kubelet>
- La medesima procedura deve essere ripetuta su tutti gli altri nodi
- Dopo queste operazioni il nodo tornerà UP con la nuova versione, è necessario ri-abilitare la schedulazione su questo nodo usando il comando kubectl uncordon node01


Backup del Cluster





- è possibile esportare tutto quello che è nel cluster in un file yaml
>>> kubectl get all --all-namespaces -o yaml > all-deploy-services.yaml
- puoi fare il backup di ETCD, invece di salvare le risorse (come fatto prima) un altro modo è fare il backup di ETCD. 
- Puoi usare una utiliti di sistema < ETCDCTL_API=3 etcdctl ..> per fare lo snapshot. 
>>> etcdctl snapshot save -h
>>> etcdctl snapshot restore -h
1. Far riferimento alla doc https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/.
>>> ETCDCTL_API=3 etcdctl \
      snapshot save <backup-file-location> \
      --endpoints=https://127.0.0.1:2379 \ # oppure --advertise-client-urls
      --cacert=<trusted-ca-file> \
      --cert=<cert-file> \
      --key=<key-file>
2. Esplora la condifurazione del ETCD-POD, nella definizione del command ci sono i parametri richiesti dal comando precedente.
3. Verifica lo snapshot ETCDCTL_API=3 etcdctl --write-out=table snapshot status snapshotdb
4. Per riprisitnare il db partendo da uno snapshot è necessario fermare il servizio kube-apiserver e ripristinare il backup. Se k8s è isntallato con kubeadm puoi temporaneamente cancellare il POD statico in /etc/kubernetes/manifest
5. Per ripristinare il backup partire dal comando usato per crearlo ad aggiungere i parametri obbligatori, puoi recuperarli dalla definizione di etcd.
>>> ETCDCTL_API=3 etcdctl \
      snapshot restore <backup-file-location> \
      --endpoints=https://127.0.0.1:2379 \ # oppure --listen-client-urls
      --cacert=<trusted-ca-file> \
      --cert=<cert-file> \
      --key=<key-file> \ # Fino a qui identico al precedente
      --data-dir=/var/lib/etcd-from-backup \
      --initial-advertise-peer-urls=https://10.0.0.100:2380 \
      --initial-cluster=ip-10-0-0-100.us-west-2.compute.internal=https://10.0.0.100:2380 \
      --initial-cluster-token="etcd-cluster-1" \
      --name=ip-10-0-0-100.us-west-2.compute.internal \
      --skip-hash-check=true # Alcune volte da errore. Error:  expected sha256 [...], got [...]
6. I parametri obbligari sono recuperabili facendo help sul comando etcdctl, come completarli è presente mapping 1:1 nella configurazione di etcd.
7. Modificare il POD statico ETCD aggiungendo:
>>> --initial-cluster-token="etcd-cluster-1"
>>> replace --data-dir=/var/lib/etcd con --data-dir=/var/lib/etcd-from-backup in ogni punto
8. Fai ripartire kube-apiserver
- l'opzione --data-dir va settato ad un percorso nuovo, ad una cartella che non esiste. Viene creata Automaticamente.
- dal master è possobile raggiungere ETCD usando --listen-client-urls

Troubleshooting

Troubleshooting

CKA..

Troubleshooting Tips


- Doc k8s: https://kubernetes.io/docs/tasks/debug-application-cluster/debug-application/
- prima a fare una curl sul servizio
- verifica i selettori
- controlla lo stato dei pod
- consulta i log kubectl logs my-pod --previous # dump pod logs (stdout) for a previous instantiation of a container
- systemctl | grep running = per la lista di tutti i servizi running
- controlla lo stato dei nodi nel cluster
  - Lo status può essere True | False nei casi in cui il disco ad esempio è pieno, quando lo stato è Unknown significa che c’è una perdita di comunicazione con il master, puoi controllare la colonna LastHeartbeatTime per controllare l’ultimo controllo.
  - Controlla lo stato del servizio Kubelet.
  - Controlla lo stato dei certificati. openssl x509 /path/to/certificate.crt -text
- service | grep running = per la lista di tutti i servizi running
- journalctl -u kube-apiserver
- NON PUOI CREARE UN INGRESS CROSS NAMESPACE, bisogna che ne crei uno nuovo nello stesso namespace
- VIM premere : e poi scrivere set number per visualizzare i numeri
- ATTENZIONE: kubectl api-resources -o wide
- Il comando kubectl api-resources è molto importante, è utile per recuperare l’informazione sul apiGroups e sui verbs.
- You have been notified that the Kubernetes cluster that you administer has been behaving strangely. A team member has told you they suspect one of the cluster's master components is at fault. How can you quickly check on the status of the master components?
>>> kubectl get componentstatuses
- Visualizzare lo stato del nodo in caso di fallimento. Ci sono dei Flag, controllare se sono a False oppure True per indivuduare la causa, se sono Unknown allora non c'è comunicazione tra il nodo ed il master. Controllare il nodo facendo ssh in esso.
>>> kubectl describe node
- Visualizzare i log dei POD, usare previous per vedere i log del container fallito in precedenza
>>> kubectl logs -container-id- -f -previous 

Others

Others

Altro:
- CoreDNS
    What is the IP of the CoreDNS server that should be configured on PODs to resolve services?
    Run the command kubectl get service -n kube-system and look for cluster IP value.

- Il drain di un nodo non può fare eviction di un POD che non è parte di un ReplicaSet/Deploy etc..
- I POD creati singolarmente vengono persi dopo il drain

- Ci sono diversi modi per entrare in un container:
>>> docker exec -it 3d53596c5f8f sh per entrare in un container e vedere i logs
>>> kubectl exec webapp -- cat /log/app.log
>>> docker exec -it <container-id> /bin/bash

- At this moment in time, there is still one place within the documentation where you can find the exact command to deploy weave network addon:
>>> https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/#steps-for-the-first-control-plane-node (step 2)
- Cercare high availability nella documentazione

- K8s mette a disposizione diverse interfacce. Le interfacce rendono K8s estendibile. 
  CRI = Container Runtime Interface
  CNI = Container Network Interface
  CSI = Container Storage Interface
- Chi vuole “essere parte” del ecosistema di K8s deve semplicemente implementare queste interfacce.


https://kubernetes.io/docs/setup/best-practices/certificates/
- il Controller Manager gestisce i meccanismi di creazione dei certificati
>>> - --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
>>> - --cluster-signing-key-file=/etc/kubernetes/pki/ca.key

 

 

Ho to Pull an image from Private Registry

- your.private.registry.example.com/janedoe/jdoe-private:v1
>>> your.private.registry.example.com: dominio
>>> janedoe: user-name
>>> jdoe-private:v1: img
- Puoi anche omettere lo user-name se coincide con il nome dell'immagine
- Le credenziali per fare il login sono lette da un Secret

Pull Image Private Repository:
- Puoi cercare "pull private image" sulla documentazione ed ottenere: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
- Oppure puoi creare il secret a mano 
kubectl create secret docker-registry dockercred \
--docker-server=private-registry.io \
--docker-username=registry-user \
--docker-password=registry-password \
--docker-email=registry-user@org.com
- Nel POD va specificata l'opzione imagePullSecrets