Kubernetes Administrator CKA
Table of Contents
PODS
Imperativo
kubectl run myapp-pod --image=nginx --dry-run=client -o yaml > myapp-pod.yaml
Construye un archivo yaml que hace referencia a un pod con la imagen descrita
Declarativo
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
Init Container Vs SideCar Container
- Init Container: Contenedores que corren hasta completar su tarea durante la iniciación del POD
- SideCar Container: Contenedor que inicia antes de la aplicación principal pero se mantienen corriendo.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
NOTA: Ejemplo de Init Container
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: alpine:latest
command: ['sh', '-c', 'while true; do echo "logging" >> /opt/logs.txt; sleep 1; done']
volumeMounts:
- name: data
mountPath: /opt
initContainers:
- name: logshipper
image: alpine:latest
restartPolicy: Always
command: ['sh', '-c', 'tail -F /opt/logs.txt']
volumeMounts:
- name: data
mountPath: /opt
volumes:
- name: data
emptyDir: {}
NOTA: Ejemplo de SideCar Container
DEPLOYMENT
Imperativo
kubectl create deployment --image=nginx nginx --replicas=4 --dry-run=client -o yaml > nginx-deployment.yaml
Declarativo
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-end
DAEMONSETS
Ayuda a desplegar multiples instancias de PODs
Corre una copia de tu POD en cada nodo de tu cluster
Con cada nodo nuevo, se añade una replica
También asegura que una copia del POD este siempre presente en todos los nodos.

Declarativo
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: monitoring-daemon
spec:
selector:
matchLabels:
app: monitoring-agent
template:
metadata:
labels:
app: monitoring-agent
spec:
containers:
- name: monitoring-agent
image: monitoring-agent
SERVICES
Node Port

apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80
port: 80
nodePort: 30008
selector:
app: myapp
type: front-end
También se puede realizar de forma imperativa:
kubectl expose pod myapp --type=NodePort --port=80 --name=myapp-service --dry-run=client -o yaml
Cluster IP
Servicio de Kubernetes que nos permite agrupar los PODs y proporcionar una sola interface de acceso hacia los PODs

apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP
ports:
- targetPort: 80
port: 80
selector:
app: myapp
type: back-end
LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer
ports:
- targetPort: 80
port: 80
nodePort: 30008
Soporte en: AWS, Azure, GoogleCloud
CONFIGMAP
Imperativo
kubectl create configmap app-config --from-literal=APP_COLOR=blue --from-literal=APP_MOD=prodkubectl create configmap <config-name> --from-file=<path-to-file>
Declarativo
# config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config # <<<<<<<<<<<--- HERE
data:
APP_COLOR: blue
APP_MODE: prod
Inject into a POD
# pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp-color
labels:
name: simple-webapp-color
spec:
containers:
- name: simple-webapp-color
image: simple-webapp-color
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: app-config # <<<<<--- HERE
SECRETS
Imperativo
Creamos varios secrets de forma implícita:
kubectl create secret generic app-secret --from-literal=DB_Host=mysql --from-literal=DB_User=root --from-literal=DB_Password=paswrd
También podemos crear desde un archivo:
kubectl create secret generic app-secret --from-file=app_secret.properties
Declarativo
# secre-data.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secret
data:
DB_Host: bx1zcWw=
DB_User: cm9vda==
DB_Password: cGFzd3Jk
NOTA: Para codificar (encode):
echo -n 'mysql' | base64y para decodificarecho -n 'bx1zcWw=' | base64 --decode
Secrets en los PODs
# pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp-color
labels:
name: simple-webapp-color
spec:
containers:
- name: simple-webapp-color
image: simple-webapp-color
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: app-secret # <<< HERE
En el env cuando necesito solo una variable
env:
- name: DB_Password
valueFrom:
secretKeyRef:
name: app-secret
key: DB_Password
En el volumes
volumes:
- name: app-secret-volume
secret:
secretName: app-secret
Donde app-secret-volume podría ser un directorio que contiene archivos con los secrets
REPLICASET
Alta disponibilidad
Declarativo
Replication Controller
apiVersion: v1
kind: ReplicationController
metadata:
name: myapp-rc
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
Replica Set (Evolución del Replica Controller)
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-replicaset
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-end
Labels Y Selector
Para relacionar el POD con el ReplicaSet debemos usar el mismo valor tanto en el labels del POD como en el selector del ReplicaSet.
#pod-definition.yml
metadata:
name: myapp-pod
labels:
tier.front-end
#replicaset-definition.yml
selector:
matchlabels:
tier.front-end
SCHEDULING
Asignación del POD en el NODO correspondiente dependiendo de las instrucciones.
Scheduling Manual
Con NodeName solo se puede asignar durante la creación:
---
apliVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeName: node01 # <<<<<<<<<<<<<
containers:
- image: nginx
name: nginx
Labels & Selectors
En Deployments, ReplicaSets podemos usar selector para identificar objetos.
spec:
replicas: 3
selector: # ----------> Connect replicaSet to the Pods
matchLabels:
app: App1 # <<<<<<<<
Considerando PODs con un Label
apiVersion: v1
kind: Pod
metadata:
name: simple-webapp
labels:
app: App1 # <<<<<<<<<<
Taints & Toleration
Restricciones con respecto a la forma de ubicar los PODs en los Nodos
Taint: Se aplica en Nodos. Rechazan PODs que no sean tolerantes a un Taint en particularToleration: Se aplica en PODs. EL POD puede ser colocado en un Nodo siempre que no tenga un taint.
apiVersion:
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: nginx-container
image: nginx
tolerations:
- key: "app"
operator: "Equal"
value: "blue"
effect: "NoSchedule" | "PreferNoSchedule" | "NoExecute"
NoSchedule: PODs no serán colocados en dicho NodoPreferNoSchedule: Se intenta no colocarNoExecute: PODs no serán colocados en dicho Nodo. Y los existentes serán desalojados
Comando: kubectl taint nodes node1 app=blue:NoSchedule
Node Selector
apiVersion:
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: data-processor
image: data-processor
nodeSelector:
size: Large # <<<<<<<<<<<<<<<<<<
Comando: kubectl label nodes node-1 size=Large
Node Affinity

apiVersion:
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: data-processor
image: data-processor
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: size
operator: [In | NotIn | Exists]
values:
- Large
- Medium
In: POD será colocado en el nodo con alguno de losvalues.NotIn: Se hará match a los nodos que no estén envaluesExists: Revisa si el label size existe.
Tipos:
requiredDuringSchedulingIgnoredDuringExecution: Disponibilidad del PODpreferredDuringSchedulingIgnoredDuringExecution: Disponibilidad del PODrequiredDuringSchedulingRequiredDuringExecution: Planificación
Static PODs
- Kubelet: Es el capitán del nodo. Puede manejar todo independientemente del
kube-apiserver - Path:
--pod-manifest-path=/etc/kubernetes/manifests - Comando:
kubectl run --restart=Never --image=busybox static-busybox --dry-run=client -o yaml --command -- sleep 1000 > /etc/kubernetes/manifests/static-busybox.yaml(Dicho path deberá tener todos los manifiestos necesarios)
Priority Classes
- App: 1000000000
- System: 2000000000
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000000
description: "Priority class for mission critical pods"
globalDefault: true
preemptionPolicy: [ PreemptLowerPriority | never ]
En el POD
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 8080
priorityClassName: high-priority
PreemptLowerPriority: Destruye PODs de menor prioridad en caso sea necesario.
never: Caso contrario usar esta.
Scheduler Profiles
Este es el orden que toma en cuenta cuando se coloca un POD a algún nodo:
- Prioridad:
priorityClassName: high-priority - Filtro: Nodos que no pueden correr los requisitos son filtrados:
cpu: 10 - Score: Se da preferencia a un nodo con un mayor margen de recursos.
- Bind: El POD se coloca en un nodo

Se puede modificar dicho comportamiento por defecto:
# my-scheduler-2-config.yaml
apiVersion: kubescheduler.config.k8s.io/v1
kind: lubeSchedulerConfiguration
profiles:
- schedulerName: my-scheduler-2
plugins:
score:
disabled:
- name: TaintToleration
enabled:
- name: MyCustomPluginA
- name: MyCustomPluginB
- schedulerName: my-scheduler-3
plugins:
preScore:
disabled:
- name: '*'
score:
disabled:
- name: '*'
- schedulerName: my-scheduler-4
Admissions Controllers

- Ver admission Controllers:
kube-apiserver -h | grep enable-admission-plugins - Ver desde
ControlPlane:kubectl exec kube-apiserver-controlplane -n kube-system -- kube-apiserver -h | grep enable-admission-plugins - Habilitar o des habilitar:
--enable-admission-plugins=NodeRestriction,NamespaceAutoProvision|--disable-admission-plugins=DefaultStorageClass - Path:
/etc/kubernetes/manifests/kube-apiserver.yaml
RBAC: ROLE BASED ACCESS CONTROL
Role:
Regula el acceso basado en roles
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: [nameSpaceName]
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "create", "update", "delete"]
resourceNames: ["blue", "orange"] #Specific Pods
- apiGroups: [""]
resources: ["configMap"]
verbs: ["create"]
Y de forma imperativa la creación del Role sería:
kubectl create role developer --ver=list,create,delete --resource=pods
Binding Role and User:
Para hacer el link entre el Role y el User:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: devuser-developer-binding
subjects:
- kind: User
name: dev-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
Y de forma imperativa sería:
kubectl create rolebinding dev-user-binding --role=developer --user=dev-user
Revisar Acceso:
Se puede probar acceso:
kubectl auth can-i create pods --as dev-user --namespace test
Cluster Roles:
En este caso los roles y los Role Bindings son creados dentro de namespaces

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pvviewer-role
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["list", "get", "create", "delete"]
Declarativo: kubectl create clusterrole pvviewer-role --resource=persistentvolumes --verb=list
Cluster Role Binding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pvviewer-role-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: pvviewer-role
subjects:
- kind: ServiceAccount
name: pvviewer
namespace: default
Declarativo: kubectl create clusterrolebinding pvviewer-role-binding --clusterrole=pvviewer-role --serviceaccount=default:pvviewer
SERVICE ACCOUNT
De forma imperativa: kubectl create serviceaccount [name]
Integrarlo al POD
apiVersion: v1
kind: Pod
metadata:
labels:
run: pvviewer
name: pvviewer
spec:
containers:
- image: redis
name: pvviewer
# Add service account name
serviceAccountName: [name]
COMMANDS & ARGUMENTS
Considerando la diferencia:
- Command: Es el comando per se. Ejemplo
command: ["sleep2.0"] - Arg: Es el argumento que acompaña al comando. Ejemplo
args: ["10"]Para este caso sería unsleepde10segundos.
Alternativas
# Option 1
command: ["sleep 5000"]
# Option 2
command:
- "sleep"
- "5000"
Ejemplo
# pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper-pod
spec:
containers:
- name: ubuntu-sleeper
image: ubuntu-sleeper
command: ["sleep2.0"]
args: ["10"]
ENVIRONMENT VARIABLES
Existen dos formas de establecer variables de entorno.
Una es utilizando env y la otra, utilizando envFrom. Esta ultima establece la variable de entorno haciendo referencia a un CONFIGMAP o a un SECRETS.
Para este caso nos enfocaremos en env. Ejemplo:
apiVersion: v1
kind: Pod
metadata:
name: envar-demo
labels:
purpose: demonstrate-envars
spec:
containers:
- name: envar-demo-container
image: gcr.io/google-samples/hello-app:2.0
env:
- name: DEMO_GREETING
value: "Hello from the environment"
- name: DEMO_FAREWELL
value: "Such a sweet sorrow"
También podemos utilizar campos del POD como valores para nuestras variables de entorno:
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
ROLLOUT Y VERSIONING
Cuando creamos un Deployment automáticamente se dispara un rollout.
A su vez un rollout genera una nueva versión del Deployment.
Cada vez que se actualice la versión del contenedor, se genera un nuevo rollout lo que crea una nueva revisión del Deployment
- Para ver el estado:
kubectl rollout status deployment myapp-deployment - Para ver el historial:
kubectl rollout history deployment myapp-deployment
Annotate
Podemos colocar una nota del motivo del cambio (Ejemplo):
kubectl annotate deployments nginx-deploy kubernetes.io/change-cause="Updated nginx image to 1.17"
Strategy Type
- Recreate: Se destruyen todos los
PODsantes de empezar a recrear los nuevos. - RollingUpdate: (Por defecto) Se destruyen uno o algunos mientras se van creando los nuevos de forma secuencial.
.spec.strategy.type==RollingUpdate
Rollback
Para poder realizar un rollback:
- Rollback:
kubectl rollout undo deployment myapp-deployment
BACKUP & RESTORE
Cluster ETCD
Para hacer uso del etcdctl necesitamos empezar asignando la variable de entorno:
export ETCDCTL_API=3
Durante el snapshot se necesitan colocar ciertos parámetros que los podremos encontrar en:
/etc/kubernetes/manifests/etcd.yaml
Necesitaremos tener a la mano:
- cacert:
--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt - cert:
--cert-file=/etc/kubernetes/pki/etcd/server.crt - key:
--key-file=/etc/kubernetes/pki/etcd/server.key - datadir:
--data-dir=/var/lib/etcd
Ya con eso podemos generar el snapshot:
# Exportamos la variable de entorno
export ETCDCTL_API=3
# Creamos el snapshot
etcdctl snapshot save snapshot.db --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key
# Revisamos el estado del snapshot
etcdctl snapshot status snapshot.db
# Nuevas versiones usan etcdutl
## etcdutl --write-out=table snapshot status snapshot.db
Restaurar
Para restaurar:
# Detenemos el servicio apiserver
service kube-apiserver stop
# Restauramos
export ETCDCTL_API=3
etcdctl --data-dir <data-dir-location> snapshot restore snapshot.db
# Nuevas versiones usan etcdutl
## etcdutl --data-dir <data-dir-location> snapshot restore snapshot.db
# Refrescar
systemctl daemon-reload
service etcd restart
service kube-apiserver start
NETWORKING
Instalación de Network Plugins
kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yamlWeave Netkubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.ymlFlannelcurl https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml -O&kubectl apply -f calico.yamlCalico
DNS en Kubernetes
Sub Dominios
| Hostname | Namespace | Type | Root | IP Address |
|---|---|---|---|---|
| web-service | apps | svc | cluster.local | 10.107.37.188 |
| web | apps | pod | cluster.local | 10.244.2.5 |
- FQDN:
web-service.apps.svc.cluster.local,web.apps.pod.cluster.local
Core DNS
Kubernetes usa CoreDNS. Es un Server DNS flexible y extensible que puede servir como un cluster DNS de Kubernetes.
- Archivo de configuración:
/etc/coredns/corefile - ConfigMap:
kubectl get configmap -n kube-system
Recursos
- Service account:
coredns - Cluster-roles:
corednsykubedns - Cluster role bindings:
corednsykube-dns - Deployment:
coredns - Configmap:
coredns - Service:
kube-dns
Kube-Proxy
Mantiene las reglas de red en los nodos. Estas reglas permiten la comunicación hacia los PODs desde las sesiones dentro o fuera del cluster.
- Archivo de configuración:
/var/lib/kube-proxy/config.conf
CNI en Kubernetes
La manera en que se aísla las redes es mediante Namespaces
Los podemos listar mediante el comando ip netns o ver a detalle con ip netns exec [red] ip link
Introducción
Se muestra una forma manual de establecer conexión de red entre diferentes redes virtuales:
# Crear un namespace
ip netns add [red]
# Detallarlo
ip -n red link
# Otra opción
ip netns exec [red] ip link
# Ver la tabla arp
arp
# Ver tabla arp para el namespace
ip netns exec [red] arp
# Ver tabla de enrutamiento
route
# Ver tabla de enrutamiento para el namespace
ip netns exec [red] route
Estableciendo conexión:
# Crear un veth
ip link add veth-red type veth
# Si deseamos crear un tunel entre veth
ip link add veth-red type veth peer name veth-blue
# Asignamos el veth a un namespace
ip link set veth-red netns red
# Asignamos una IP
ip -n red addr add 192.168.15.1 dev veth-red
# Levantamos el link
ip -n red link set veth-red up
# Por si queremos borrarlo
ip -n red link del veth-red
# Probamos mediante ping
ip netns exec red ping 192.168.15.2
Ahora toca configurar el bridge
# Crear bridge
ip link add [v-net-0] type bridge
# Levantar el bridge
ip link set dev v-net-0 up
# Crear el peer del veth con un veth pero del Bridge
ip link add veth-red type veth peer name veth-red-br
# Asignarle IP al bridge
ip addr add 192.168.15.5/24 dev v-net-0
# Unir el veth del bridge al Bridge
ip link set veth-red-br master v-net-0

Para el enrutamiento
# Enrutamiento para una Red
ip netns exec [blue] ip route add 192.168.1.0/24 via 192.168.15.5
# Enrutamiento Default
ip netns exec blue ip route add default via 192.168.15.5

Finalmente el NAT
iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE
# Ingreso hacia Blue desde fuera
iptables -t nat -A PREROUTING --dport 80 --to-destination 192.168.15.2:80 -j DNAT
Forwarding
Es necesario que los Nodos re envíen los paquetes. Y eso está en:
# 0 es no forwarding
cat /proc/sys/net/ipv4/ip_forward
# 1 es forward
echo 1 > /proc/sys/net/ipv4/ip_forward
# Para que persista a pesar de los reinicios
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
echo 'net.bridge.bridge-nf-call-iptables = 1' >> /etc/sysctl.conf
# Leer valores
sysctl -p
INGRESS
Utilizando services del tipo load balancerpara más de una aplicación bajo un mismo dominio tal como:
www.my-online-store.com/wearwww.my-online-store.com/watchTerminaríamos teniendo más de unload balancer. Uno para eldeploymentdeweary otro para el dewatch
Ingress se presenta como una solución para esto.
Primero necesitamos un Ingress Controller existen alternativas pero una de las más comunes es NGINX
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress-controller
spec:
replicas: 1
selector:
matchLabels:
name: nginx-ingress
template:
metadata:
labels:
name: nginx-ingress
spec:
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0
args:
- /nginx-ingress-conttroller
- --configmap-$(POD_NAMESPACE)/nginx-configuration
env:
- name: POD_NAME
valueFrom:
filedRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
Vemos que tiene un configmap para poder pasar configuraciones desde un archivo aparte:
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
Y debe tener su service:
apiVersion: v1
Kind: Service
metadata:
name: nginx-ingress
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
- port: 443
targetPort: 443
protocol: TCP
name: https
selector:
name: nginx-ingress
Y su service Account:
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
Finalmente el Ingress
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wear-watch
namespace: app-space
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /wear
pathType: Prefix
backend:
service:
name: wear-service
port:
number: 8080
- path: /watch
pathType: Prefix
backend:
service:
name: video-service
port:
number: 8080
Existen los llamados annotations con los que podemos añadir configuraciones adicionales.
STORAGE
Volumes Y VolumeMounts
La forma mas simple de usar volúmenes sería:
apiVersion: v1
kind: Pod
metadata:
name: random-number-generator
spec:
containers:
- image: alpine
name: alpine
command: ["/bin/sh","-c"]
args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]
volumeMounts:
- mountPath: /opt
name: data-volume
volumes:
- name: data-volume
hostPath:
path: /data
type: Directory
Persistent Volumes
Son volúmenes que se crean para estar disponibles ante algún persistent volume claim para su uso.
Ya sea usando almacenamiento de AWS
# pv-definition.yaml
apiVersion: v1
kind: PersistentVolume
metadata: pv-voll
spec:
accessModes:
- [ ReadWriteOnce ReadOnlyMany ReadWriteMany ]
capacity:
storage: 1Gi
awsElasticBlockStore:
volumeID: [volume-id]
fsType: ext4
O almacenamiento local:
# pv-definition.yaml example2
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-log
spec:
persistentVolumeReclaimPolicy: Retain
accessModes:
- ReadWriteMany
capacity:
storage: 100Mi
hostPath:
path: /pv/log
persistentVolumeReclaimPolicy: Retain: Es el de defecto. Luego de ser borrado, el volumen se retienepersistentVolumeReclaimPolicy: Deleted: Luego de ser borrado, el volumen se borrapersistentVolumeReclaimPolicy: Recycle: Luego de ser borrado, el volumen se recicla
Persistent Volume Claims
Estos son los que solicitan para que los nodes adquieran almacenamiento
Dependerá de si el persistent volume tiene suficiente capacidad así como hacer match con el accessModes:
# pvc-definition.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myClaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
Persistent Volume Claims en PODs
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
Storage Class
Le otorga al administrador definir las clases de almacenamiento que se tienen para ofrecer.
Con Storage Class puedes definir un provisioner como Google que puede aprovisionar automáticamente en Google Cloud y enlazarlo a un POD.
# sc-definition.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: google-storage
provisioner: kubernetes.io/gce-pd
parameters:
type: [ pd-standard | pd-ssd ]
replication-type: [ none | regional-pd ]
Persistent Volume se creará automáticamente
Entonces el vínculo del storage class se realiza con el persistent volume claim
# pvc-definition.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
storageClassName: google-storage
resources:
requests:
storage: 500Mi
TROUBLESHOOTING
Comandos Básicos de Troubleshooting
Kubectl get nodesEstado de los Nodoskubectl get podsEstado de los PODskubectl get pods -n kube-systemEstado de los PODs del ControlPlaneservice kube-apiserver statusEstado de los servicios del ControlPlaneservice kube-controller-manager statusEstado de los servicios del ControlPlaneservice kube-scheduler statusEstado de los servicios del ControlPlaneservice kubelet statusEstado de los servicios del ControlPlane en el Nodoservice kube-proxy statusEstado de los servicios del ControlPlane en el Nodokubectl logs kube-apiserver-master -n kube-systemRevisar Logs del serviciosudo journalctl -u kube-apiserverRevisar Logs del serviciosudo journalctl -u kubeletRevisar kubelet logsopenssl x509 -in /var/lib/kubelet/worker-1.crt -textRevisar los certificados
Revisar Estado del Nodo
Desde un:
Kubectl describe node worker-1
Podremos ver un cuadro como este:
| Type | Status | Descripción | LastHeartBeatTime |
|---|---|---|---|
| OutOfDisk | True/False | Sin espacio en disco | Última comunicación |
| MemmoryPressure | True/False | Sin memoria RAM | Última comunicación |
| DiskPressure | True/False | Capacidad del disco baja | Última comunicación |
| PIDPresure | True/False | Muchos procesos corriendo | Última comunicación |
| Ready | True/False | Saludable | Última comunicación |
| N/A | Unknown | Cuando no logra comunicarse con el Master | Última comunicación |
Rutas del Kubelet
Aquí encontraremos algunas rutas importantes a recordar que el kubelet utiliza:
/var/lib/kubelet/config.yamlRuta local de unnododonde guarda la configuración delkubelet service. Dicho servicio toma las opciones de este archivo./etc/kubernetes/kubelet.confArchivo local de configuración de unnodousado porkubeletpara conectarse con elapi server
CoreDNS y Kube-proxy
1. kubectl -n kube-system get deployment coredns -o yaml | sed 's/allowPrivilegeEscalation: false/allowPrivilegeEscalation: true/g' | kubectl apply -f -Esto puede solucionar el Error deCrashLoopBackOffnetstat -plan | grep kube-proxyRevisar sikube-proxyesta corriendo dentro del contenedor
UPGRADE KUBEADM & NODES
Antes que nada debemos verificar el repositorio.
Esto aplica tanto para el controlplane como para los nodos:
vim /etc/apt/sources.list.d/kubernetes.list
Considerando que deseamos pasar a la versión v1.32. modificaremos la linea de la siguiente forma:
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /
Determinar la Versión
Tanto para controlplane como para los nodos podemos listar todas las versiones disponibles una vez actualizado el repositorio:
sudo apt update
sudo apt-cache madison kubeadm
Controlplane Upgrade
Kubeadm Upgrade
# Reemplaza la 'x' por la última versión mostrada en el comando anterior
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.32.x-*' && \
sudo apt-mark hold kubeadm
Verificamos que la descarga funcionó y tiene la versión correcta
kubeadm version
Verificamos el plan
sudo kubeadm upgrade plan
Elegimos la versión y corremos el comando
# Reemplaza la 'x' por la última versión mostrada arriba
sudo kubeadm upgrade apply v1.32.x
Kubelet y Kubectl Upgrade
# Reemplaza la 'x'
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.32.x-*' kubectl='1.32.x-*' && \
sudo apt-mark hold kubelet kubectl
Ahora reiniciamos kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet
Nodes Upgrade
Considerando que ya revisamos el repositorio e hicimos los cambios antes mencionados, procedemos a:
Kubeadm Upgrade
# replace x in 1.32.x-* with the latest patch version
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.32.x-*' && \
sudo apt-mark hold kubeadm
En los nodos, esto actualiza la configuración del kubelet local:
sudo kubeadm upgrade node
Drena el Nodo
Colocamos al nodo en modo mantenimiento:
# Este comando se ejecuta en el controlplane
# Reemplaza el node-tro-drain con el nombre del nodo
kubectl drain <node-to-drain> --ignore-daemonsets
Kubelet Kubectl Upgrade
# replace x in 1.32.x-* with the latest patch version
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.32.x-*' kubectl='1.32.x-*' && \
sudo apt-mark hold kubelet kubectl
Reiniciamos el kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet
Activar el Nodo
Colocamos al nodo nuevamente en línea:
# execute this command on a control plane node
# replace <node-to-uncordon> with the name of your node
kubectl uncordon <node-to-uncordon>
Referencia
Para más información seguir el enlace oficial
HELM
De forma simplificada se podría decir que es el Package manager de Kubernetes
Instalación
Se puede seguir los pasos de la página oficial
sudo snap install helm --classic
Componentes
- Online Chart Repository: artifacthub
- Charts
Cuando un chartse aplica a un cluster, se genera unrelease
Trabajando con HELM
helm search hub wordpress: Buscamos WordPress en elartifacthubhelm search worpress: Buscamos WordPress de forma local (Repositorio local)helm repo add bitnami https://charts.bitnami.com/bitnami: Añadimos el Publisherbitmapia modo de repositorio localhelm install my-release bitnami/wordpress: con el nombremy-releaseinstalamos WordPress utilizando nuestro repositorio previamente añadidobitmapihelm list: Listar los releasehelm repo list: Listar los repositorioshelm repo update: Actualizar los repositorios localeshelm upgrade my-release bitnami/wordpress: Upgrade al WordPress en específicohelm install my-release bitnami/wordpress --version x.x.x: Instalar una versión en específicohelm history my-release: Ver el historialhelm rollback my-release 1: Volver a una revisión anterior. En este caso la1. Aunque en realidad crea una revisión nueva que es igual a la1
Personalizando Parámetros del Chart
Desde el archivo value.yaml
# deployment.yaml
[...]
- name: WORDPRESS_BLOG_NAME
value: {{ .Values.wordpressBlogName | quote }}
# value.yaml
wordpressBlogName: User's Blog!
De forma imperativa
helm install --set wordpressBlogName="Helm Tutorials" my-release bitnami/worpress
Usando un archivo value personalizado
helm install --values custom-values.yaml my-release bitnami/worpress
# custom-values.yaml
worpressBlogName: Helm Tutorials
worpressEmail: john@example.com
PUERTOS Y PROTOCOLOS
ControlPlane
| Protocolo | Puerto | Propósito |
|---|---|---|
| TCP | 6443 | API server |
| TCP | 2379-2380 | ETCD server |
| TCP | 10250 | Kubelet API |
| TCP | 10259 | Kube-Scheduler |
| TCP | 10257 | Kube-controller-manager |
Worker Nodes
| Protocolo | Puerto | Propósito |
|---|---|---|
| TCP | 10250 | Kubelet API |
| TCP | 10256 | Kube-Proxy |
| TCP | 30000-32767 | NodePort Services |