Esta entrada es un registro de las diferentes acciones que realicé para conseguir que los pods asociados al StatefulSet del tutorial StatefulSet Basics se crearan correctamente.
Lo publico como lo que es, un log de todos los pasos que fui dando, en modo ensayo y error, hasta que conseguí que los pods se crearan con éxito. Mi intención al publicarlo no es tanto que sirva como referencia sino como archivo. Y si alguien se encuentra con un problema similar, que pueda consultar los pasos que he dado durante el troubleshooting.
Como indicaba en el artículo anterior, quiero publicar un tutorial paso a paso con el proceso correcto para provisionar los PersistentVolumes necesarios para el tutorial StatefulSet Basics del sitio de Kubernetes.
Creación de un StatefulSet
Creación del PersistentVolume
-
Creamos la carpeta que contiene el PersistentVolume:
mkdir /tmp/data/pv001 -p
-
Definimos el PersistentVolume (
vi pv001.yaml
):kind: PersistentVolume apiVersion: v1 metadata: name: pv001 labels: type: local spec: storageClassName: manual capacity: storage: 1Gi accessModes: - ReadWriteOnce hostPath: path: "/tmp/data/pv001"
-
Creamos el PersistentVolume
$ kubectl apply -f pv001.yaml persistentvolume "pv001" created $ kubectl get pv NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE pv001 1Gi RWO Retain Available manual 10s
-
Definimos un PersistentVolumeClaim (
pv001claim.yaml
)$ vi pv001-claim.yaml kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pv001claim spec: storageClassName: manual accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
-
Creamos el PersistentVolumeClaim
$ kubectl apply -f pv001-claim.yaml persistentvolumeclaim "pv001claim" created
-
Verificamos que el PVClaim ha quedado ligado al PV:
$ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE pv001claim Bound pv001 1Gi RWO manual 43s
Creación del StatefulSet
Creación del headless service
- Definimos el headless service
nginx
kind: Service
apiVersion: v1
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None # Headless Service
selector:
app: nginx
- Creamos el servicio:
$ kubectl apply -f nginx_svc.yaml service "nginx" created
Definición del StatefulSet
-
Definimos el StatefulSet
kind: StatefulSet apiVersion: apps/v1beta1 metadata: name: web spec: serviceName: "nginx" replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: gcr.io/google_containers/nginx-slim:0.8 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/shere/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi
-
Creamos el StatefulSet:
$ kubectl apply -f statefulset.yaml statefulset "web" created
-
Verificamos:
$ kubectl get statefulset NAME DESIRED CURRENT AGE web 2 1 53s $ kubectl describe statefulset web Name: web Namespace: default CreationTimestamp: Thu, 17 Aug 2017 09:55:10 +0000 Selector: app=nginx Labels: app=nginx Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"apps/v1beta1","kind":"StatefulSet","metadata":{"annotations":{},"name":"web","namespace":"default"},"spec":{"replicas":2,"serviceName":"... Replicas: 2 desired | 1 total Pods Status: 0 Running / 1 Waiting / 0 Succeeded / 0 Failed Pod Template: Labels: app=nginx Containers: nginx: Image: gcr.io/google_containers/nginx-slim:0.8 Port: 80/TCP Environment: <none> Mounts: /usr/shere/nginx/html from www (rw) Volumes: <none> Volume Claims: Name: www StorageClass: Labels: <none> Annotations: <none> Capacity: 1Gi Access Modes: [ReadWriteOnce] Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 2m 2m 1 statefulset Normal SuccessfulCreate create Claim www-web-0 Pod web-0 in StatefulSet web success 2m 2m 1 statefulset Normal SuccessfulCreate create Pod web-0 in StatefulSet web successful $
Observamos que hay 1 pod en waiting.
Revisamos los pods
kubectl get pods NAME READY STATUS RESTARTS AGE web-0 0/1 Pending 0 5m $ kubectl describe pod web-0 Name: web-0 Namespace: default Node: <none> Labels: app=nginx controller-revision-hash=web-3274782773 Annotations: kubernetes.io/created-by={"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"StatefulSet","namespace":"default","name":"web","uid":"27fe7496-8332-11e7-9fc4-024216ac27e1","apiVersion":... Status: Pending IP: Created By: StatefulSet/web Controlled By: StatefulSet/web Containers: nginx: Image: gcr.io/google_containers/nginx-slim:0.8 Port: 80/TCP Environment: <none> Mounts: /usr/shere/nginx/html from www (rw) /var/run/secrets/kubernetes.io/serviceaccount from default-token-klj21 (ro) Conditions: Type Status PodScheduled False Volumes: www: Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace) ClaimName: www-web-0 ReadOnly: false default-token-klj21: Type: Secret (a volume populated by a Secret) SecretName: default-token-klj21 Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.alpha.kubernetes.io/notReady:NoExecute for 300s node.alpha.kubernetes.io/unreachable:NoExecute for 300s Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 7m 7s 29 default-scheduler Warning FailedScheduling PersistentVolumeClaim is not bound:"www-web-0" $
El problema está en que no consigue montar el PersistentVolumeClaim. En la salida del comando el ClaimName es
www-web-0
. -
Troubleshooting del primer pod
-
Cambio de nombre del PVClaim
No parece ser posible actualizar un PVClaim, por lo que borramos y creamos uno con el nombre
www
. No hay ninguna diferencia.Eliminamos el PVClaim ligado al PV, pero tampoco cambia nada.
Eliminamos el PV y lo creamos de nuevo, para ver si el PVClaim lo enlaza. Tampoco funciona.
-
Eliminar StorageClassName del PV
El PVClaim creado automáticamente con el StatefulSet no especifica una StorageClassName. Vamos a crear un nuevo PV sin StorageClassName. Tras unos segundos, el PVClaim enlaza con el nuevo PV automáticamente.
$ kubectl apply -f pv001.yaml persistentvolume "pv001" created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE www Pending manual 8m www-web-0 Pending 21m $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE www Pending manual 8m www-web-0 Pending 22m $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE www Pending manual 8m www-web-0 Bound pv001 1Gi RWO 22m
Eliminamos el PVClaim creado manualmente.
Revisamos qué ha pasado con los pods pendientes de creación.
$ kubectl get pods NAME READY STATUS RESTARTS AGE web-0 0/1 Pending 0 24m $ kubectl describe pod web-0 ... Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 25m 3m 78 default-scheduler Warning FailedScheduling PersistentVolumeClaim is not bound:"www-web-0" 2m 11s 14 default-scheduler Warning FailedScheduling No nodes are available that match all of the following predicates:: PodToleratesNodeTaints (1).
Parece que el mensaje de error se debe a que no hay nodos disponibles donde planificar el pod. Aunque estoy haciendo las pruebas con un clúster de un solo nodo, he aplicado el taint para poder planificar pods en el nodo master. Por si acaso, aplicamos de nuevo:
$ kubectl taint nodes --all node-role.kubernetes.io/master- node "node1" untainted
Revisamos los pods de nuevo:
$ kubectl describe pod web-0 ... Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 33m 11m 78 default-scheduler Warning FailedScheduling PersistentVolumeClaim is not bound: "www-web-0" 10m 1m 36 default-scheduler Warning FailedScheduling No nodes are available that match all of the following predicates:: PodToleratesNodeTaints (1). 33s 33s 1 default-scheduler Normal Scheduled Successfully assigned web-0 to node1 33s 33s 1 kubelet, node1 Normal SuccessfulMountVolume MountVolume.SetUp succeeded for volume "pv001" 33s 33s 1 kubelet, node1 Normal SuccessfulMountVolume MountVolume.SetUp succeeded for volume "default-token-klj21" 32s 32s 1 kubelet, node1 spec.containers{nginx} Normal Pulling pulling image "gcr.io/google_containers/nginx-slim:0.8" 26s 26s 1 kubelet, node1 spec.containers{nginx} Normal Pulled Successfully pulled image"gcr.io/google_containers/nginx-slim:0.8" 26s 26s 1 kubelet, node1 spec.containers{nginx} Normal Created Created container 26s 26s 1 kubelet, node1 spec.containers{nginx} Normal Started Started container $
-
-
Troubleshooting del segundo pod
Ya tenemos un pod corriendo… Pero todavía nos falta otro :(
$ kubectl get pods NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 34m web-1 0/1 Pending 0 1m $ kubectl describe pod web-1 ... node.alpha.kubernetes.io/unreachable:NoExecute for 300s Events: FirstSeen LastSeen Count From SubObjectPath Type Reason Message --------- -------- ----- ---- ------------- -------- ------ ------- 2m 1s 9 default-scheduler Warning FailedScheduling PersistentVolumeClaim is not bound: "www-web-1"
Seguimos teniendo el problema de que no tenemos el PVClaim enlazado con un PV. Parece claro que el la causa es que sólo hemos creado un PV y necesitamos otro para el segundo pod.
Creamos un segundo PV (sin especificar StorageClassName):
mkdir /tmp/data/pv002 -p cp pv001.yaml pv002.yaml vi pv002.yaml
Modificamos el fichero de definición del
pv002
para cambiar el nombre y la ruta a la carpeta donde se almacenarán los ficheros.Creamos el
pv002
:$ kubectl apply -f pv002.yaml persistentvolume "pv002" created
Revisamos de nuevo el estado del segundo pod:
$ kubectl get pods NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 42m web-1 1/1 Running 0 9m
Resumen
Después de estar peleando con los diferentes problemas que he encontrado, queda claro que:
- Es necesario crear los PersistentVolumes sin StorageClassName o especificar en el PVClaim el mismo StorageClassName indicado en el PV. Este caso no tengo claro cómo debe hacerse.
- Hay que crear tantos PersistentVolumes como pods existan en el StatefulSet.
Queda pendiente crear una guía de creación de los PersistentVolumes para ser consumidos por los pods.