Desarrollo de apps
Desarrolle una configuración para desplegar la carga de trabajo de la app en IBM Cloud® Kubernetes Service. Debido a que Kubernetes es una plataforma de orquestación de contenedor extensible que no impone un lenguaje o una app específicos, puede ejecutar diversas cargas de trabajo, como por ejemplo apps sin estado, con estado y de proceso de datos escritas en el lenguaje de su elección.
Especificación de los requisitos de la app en el archivo YAML
En Kubernetes, describe su app en un archivo YAML que declara la configuración del objeto Kubernetes. A continuación, el servidor de API de Kubernetes procesa el archivo YAML y guarda la configuración y el estado necesario del objeto en el almacén de datos etcd. El planificador de Kubernetes planifica las cargas de trabajo en los nodos trabajadores del clúster, teniendo en cuenta la especificación del archivo YAML, las políticas de clúster que define el administrador y la capacidad disponible del clúster.
Revisa una copia del archivo YAML completo. A continuación, revise las secciones siguientes para ver cómo puede mejorar el despliegue de la app.
¿Desea obtener más información sobre cómo los objetos Kubernetes trabajan conjuntamente para su despliegue? Consulte el tema sobre Visión general de los objetos de Kubernetes para apps.
Metadatos de despliegue básico
Utilice la versión de API adecuada para el tipo de objeto Kubernetes que va a desplegar. La versión de API determina las características admitidas para el objeto Kubernetes
que tiene disponibles. El nombre que asigne en los metadatos es el nombre del objeto, no su etiqueta. Utilice el nombre al interactuar con el objeto, como por ejemplo kubectl get deployment <name>.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
Conjuntos de réplicas
Para aumentar la disponibilidad de la app, puede añadir un conjunto de réplicas en el despliegue. En un conjunto de réplicas, puede definir el número de instancias de la app que desea desplegar. Los conjuntos de réplicas se gestionan y se supervisan mediante el despliegue de Kubernetes. Si una instancia de la app deja de estar activa, Kubernetes automáticamente activa una nueva instancia de la app para mantener el número especificado de instancias de la app.
spec:
replicas: 3
Etiquetas
Con las etiquetas puede marcar distintos tipos de recursos en el clúster con el mismo par de key: value. Luego puede especificar el selector de modo que
coincida con la etiqueta de forma que pueda crear con base en estos otros recursos. Si tiene previsto exponer la app públicamente, debe utilizar una etiqueta que coincida con el selector que especifique en el servicio. En el ejemplo, la
especificación de despliegue utiliza la plantilla que coincide con la etiqueta app: wasliberty.
Puede recuperar los objetos que están etiquetados en el clúster, por ejemplo para ver los componentes staging o production. Por ejemplo, obtenga una lista de todos los recursos que tienen la etiqueta env: production en todos los espacios de nombres del clúster. Nota: se necesita acceso a todos los espacios de nombres para ejecutar este mandato.
kubectl get all -l env=production --all-namespaces
- Para obtener más información sobre las etiquetas, consulta la documentación de Kubernetes.
- Aplicar etiquetas a nodos trabajadores.
- Para ver un ejemplo más detallado, consulte Despliegue de apps en nodos trabajadores específicos mediante la utilización de etiquetas.
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
Afinidad
Especifica la afinidad (colocación) cuando desees tener más control sobre los nodos de trabajo en los que se programan los pods. La afinidad solo afecta a los pods en el momento de la planificación. Por ejemplo, para distribuir el despliegue
entre nodos trabajadores en lugar de permitir que los pods planifiquen en el mismo nodo, utilice la opción podAntiAffinity con los clústeres estándares. Puede definir dos tipos de antiafinidad de pod: preferida o necesaria.
Para obtener más información, consulta la documentación de Kubernetes sobre la asignación de pods a nodos.
- Antiafinidad necesaria
- Solo se puede desplegar el número de réplicas para las que se tengan nodos trabajadores. Por ejemplo, si tiene tres nodos trabajadores en su clúster y define cinco réplicas en su archivo YAML, únicamente se desplegarán tres réplicas. Cada réplica se basa en un nodo trabajador diferente. Las dos réplicas sobrantes quedarán pendientes. Si añade otro nodo trabajador al clúster, una de las réplicas sobrantes se desplegará de forma automática en el nuevo nodo trabajador. Si un nodo trabajador falla, el pod no se vuelve a programar porque se necesita la política de afinidad. Para ver un ejemplo de YAML con el parámetro required, consulta la aplicación Liberty con anti-afinidad de pod required.
- Antiafinidad recomendada
- Se pueden desplegar los pods en los nodos que dispongan de capacidad, lo que da una mayor flexibilidad a la carga de trabajo. Cuando sea posible, los pods se planifican en distintos nodos trabajadores. Por ejemplo, si tiene tres nodos trabajadores con suficiente capacidad en el clúster, puede planificar los cinco pods de réplica entre los nodos. Sin embargo, si añades dos nodos de trabajo más a tu clúster, la regla de afinidad no obliga a los dos pods adicionales que se están ejecutando en los nodos existentes a reprogramarse en el nodo disponible.
- Afinidad de nodos trabajadores
- Se puede configurar un despliegue de modo que solo se ejecute en determinados nodos trabajadores, como por ejemplo los nativos. Para obtener más información, consulte Despliegue de apps en nodos trabajadores específicos mediante la utilización de etiquetas.
Ejemplo de antiafinidad recomendada
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
Imagen de contenedor
Especifique la imagen que desea utilizar para sus contenedores, la ubicación de la imagen y la política de extracción de imágenes. Si no especifica una etiqueta de imagen, de forma predeterminada se extrae la imagen que está etiquetada con
latest.
Evite utilizar la etiqueta latest para cargas de trabajo de producción. Es posible que no haya probado la carga de trabajo con la imagen más reciente si está utilizando un repositorio público o compartido, como Docker Hub o IBM Cloud Container Registry.
Por ejemplo, para obtener una lista de las etiquetas de las imágenes públicas de IBM:
- Vaya a la región de registro global.
ibmcloud cr region-set global - Obtenga una lista de las imágenes de IBM.
ibmcloud cr images --include-ibm
imagePullPolicy tiene como valor predeterminado IfNotPresent, lo que hace que solo se extraiga la imagen si no existe localmente. Si desea que la imagen se extraiga cada vez que se inicie el contenedor, especifique
imagePullPolicy: Always.
containers:
- name: wasliberty
image: icr.io/ibm/liberty:webProfile8
imagePullPolicy: Always
Puerto para el servicio de la app
Seleccione un puerto de contenedor en el que abrir los servicios de la app. Para ver qué puerto se debe abrir, consulte las especificaciones de la app o Dockerfile. El puerto es accesible desde la red privada, pero no desde una conexión de
red pública. Para exponer la app públicamente, debe crear un puerto NodePort, un equilibrador de carga o un servicio Ingress. Utilice este mismo número de puerto cuando cree un objeto Service.
El puerto 25 está bloqueado para todos los servicios de IBM Cloud.
ports:
- containerPort: 9080
Solicitudes y límites de recursos
Los administradores de clústeres se aseguran de que los equipos que comparten un clúster no consuman más recursos de computación (memoria y CPU) de los que les corresponden, creando un ResourceQuota objeto para cada espacio Kubernetes de nombres del clúster. Si el administrador del clúster establece una cuota de recursos de cálculo, cada contenedor de la plantilla de despliegue
debe especificar solicitudes y límites de recursos para la memoria y la CPU; de lo contrario, la creación del pod falla.
- Compruebe si se ha definido una cuota de recursos para un espacio de nombres.
kubectl get quota --namespace=<namespace> - Vea cuáles son los límites de la cuota.
kubectl describe quota <quota_name> --namespace=<namespace>
Aunque no haya ninguna cuota de recursos definida, puede incluir solicitudes y límites de recursos en el despliegue para mejorar la gestión de los recursos de los nodos trabajadores.
Si un contenedor sobrepasa su límite, el contenedor se puede reiniciar o fallar. Si un contenedor excede una solicitud, su pod se puede desalojar si el nodo trabajador se queda sin este recurso que se ha sobrepasado. Para obtener más información sobre la resolución del problema, consulte Los pods no se pueden reiniciar repetidamente o se eliminan inesperadamente.
- Solicitud
- La cantidad mínima del recurso que reserva el planificador para uso del contenedor. Si la cantidad es igual al límite, la solicitud está garantizada. Si la cantidad es menor que el límite, la solicitud sigue estando garantizada, pero el planificador puede utilizar la diferencia entre la solicitud y el límite para asignarla a recursos de otros contenedores.
- Límite
- La cantidad máxima del recurso que el contenedor puede consumir. Si la cantidad total de recursos que se utiliza en los contenedores supera la cantidad disponible en el nodo trabajador, los contenedores pueden ser desalojados para liberar espacio. Para evitar el desalojo, defina una solicitud de recurso igual al límite del contenedor. Si no se especifica ningún límite, el valor predeterminado es la capacidad del nodo trabajador.
Para obtener más información, consulta la documentación de Kubernetes.
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
Sondeos de actividad y de preparación
De forma predeterminada, Kubernetes envía tráfico a los pods de la app después de que se inicien todos los contenedores del pod y reinicia los contenedores cuando se bloquean. Sin embargo, puede definir comprobaciones de estado para mejorar la potencia del direccionamiento del tráfico de servicio.
Por ejemplo, es posible que la app tenga un retardo de arranque. Puede que los procesos de la app comiencen antes de que la app esté completamente preparada, lo que puede afectar a las respuestas, especialmente cuando se escala entre varias instancias. Con las comprobaciones de estado, puede dejar que el sistema sepa si la app se está ejecutando y está lista para recibir solicitudes. Mediante el establecimiento de estos sondeos también puede ayudar a evitar tiempos de inactividad cuando realice una actualización continua de la app. Puede definir dos tipos de comprobaciones de estado: sondeos de actividad y de preparación.
- Análisis de actividad
- Configure un sondeo de actividad para comprobar si el contenedor se está ejecutando. Si el sondeo falla, el contenedor se reinicia. Si el contenedor no especifica un sondeo de actividad, el sondeo tiene éxito porque presupone que el contenedor está activo cuando el contenedor está en el estado Running (en ejecución).
- Sondeo de disposición
- Configure un sondeo de disposición para comprobar si el contenedor está listo para recibir solicitudes y tráfico externo. Si el sondeo falla, la dirección IP del pod se elimina como dirección IP utilizable para los servicios que coinciden con el pod, pero el contenedor no se reinicia. Configurar una prueba de disponibilidad con un retraso inicial es especialmente importante si tu aplicación tarda un rato en iniciarse. Antes del retardo inicial, el sondeo no se inicia, con lo que el contenedor tiente tiempo para iniciarse. Si el contenedor no proporciona un sondeo de preparación, el sondeo tiene éxito porque presupone que el contenedor está activo cuando el contenedor está en el estado Running (en ejecución).
Puede configurar los sondeos como mandatos, como solicitudes HTTP o como sockets TCP. En el ejemplo se utilizan solicitudes HTTP. Dé más tiempo al sondeo de actividad que al de preparación. Para obtener más información, consulta la documentación de Kubernetes.
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
Presupuesto de interrupción de pods
Para aumentar la disponibilidad de tu aplicación, puedes controlar cómo reacciona esta ante las interrupciones en función del tipo
de disponibilidad que desees mediante un objeto PodDisruptionBudget.
Un presupuesto de interrupción de pod le puede ayudar a planificar cómo se comporta la app durante las interrupciones voluntarias, como cuando se inicia un reinicio directo mediante la actualización del despliegue de la app, o durante las
interrupciones involuntarias, como en el caso de pánico del kernel.
minAvailable : Puede especificar el número o el porcentaje de pods que tienen que seguir disponibles cuando se produce una interrupción.
maxUnavailable- Puede especificar el número o el porcentaje de pods que pueden no estar disponibles cuando se produce una interrupción. En el ejemplo se utiliza
maxUnavailable: 1. selector- Introduce la etiqueta para seleccionar el conjunto de pods al que se aplica
PodDisruptionBudget. Tenga en cuenta que si ha utilizado esta misma etiqueta en otros despliegues de pod, también se les aplica el pod.
Para obtener más información, consulta la documentación de Kubernetes.
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
Exposición del servicio de la app
Puede crear un servicio que exponga la app. En la sección spec, asegúrese de que los valores de port y de etiqueta coincidan con los que ha utilizado en el despliegue. El servicio expone los objetos que coinciden
con la etiqueta, como por ejemplo app: wasliberty en el ejemplo siguiente.
- De forma predeterminada, un servicio utiliza
ClusterIP, lo que hace que solo se pueda acceder al servicio desde dentro del clúster, pero no desde fuera de él. - Puede crear un servicio NodePort, equilibrador de carga o Ingress para exponer la app rápidamente. Estos servicios tienen dos IP, una externa y una interna. Cuando el tráfico se recibe en la IP externa, se reenvía a la IP interna del clúster. A continuación, desde la IP interna del clúster, el tráfico se direcciona a la dirección IP del contenedor de la app.
- En el ejemplo se utiliza
NodePortpara exponer el servicio fuera del clúster. Para obtener más información sobre cómo configurar el acceso externo, consulte Elección de un servicio NodePort, LoadBalancer o Ingress.
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
Si necesita desplegar pods hostNetwork para escuchar en puertos específicos o utilizar un hostPort para exponer los pods de aplicación en un puerto específico del nodo de trabajador, utilice un puerto del rango 11000-11200.
IBM Cloud Kubernetes Service designa el rango de puertos 11000-11200 en los nodos de trabajador con este fin para evitar conflictos con puertos locales y otros puertos que IBM Cloud Kubernetes Service utiliza. Como los pods
de hostNetwork y los hostPorts hacen referencia a una determinada dirección IP de nodo trabajador, los pods solo se pueden ejecutar en dicho nodo trabajador. Si se ocurre algo inesperado, como que se elimine el
nodo de trabajador o que se agoten los recursos, el pod no se puede volver a planificar. Si desea exponer el puerto de un pod en el nodo trabajador, considere la posibilidad de utilizar en su lugar un servicio NodePort.
Para obtener más información, consulte la documentación sobre Kubernetes prácticas recomendadas.
Mapas de configuración para variables de entorno de contenedor
Los mapas de configuración proporcionan información de configuración no confidencial para las cargas de trabajo de despliegue.
En el ejemplo siguiente se muestra cómo se puede hacer referencia a valores del mapa de configuración como variables de entorno en la sección spec del contenedor del archivo YAML de despliegue. Al hacer referencia a valores desde el mapa de configuración, puede desacoplar esta información de configuración de su despliegue para mantener la portabilidad de la app contenerizada.
- Ayuda para decidir si utiliza un objeto Kubernetes
ConfigMapoSecretpara variables. - Para conocer más formas de utilizar los configmaps, consulta la documentación de Kubernetes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
...
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
Secretos para variables de entorno de contenedor
Los secretos proporcionan información de configuración confidencial, como las contraseñas de las cargas de trabajo de despliegue.
En el ejemplo siguiente se muestra cómo se puede hacer referencia a valores del secreto como variables de entorno en la sección spec del contenedor del archivo YAML de despliegue. También puede montar el secreto como un volumen. Al hacer referencia a valores desde el secreto, puede desacoplar esta información de configuración de su despliegue para mantener la portabilidad de la app contenerizada.
- Ayuda para decidir si utiliza un objeto Kubernetes
ConfigMapoSecretpara variables. - Para crear un secreto, consulta la documentación de Kubernetes.
Para la gestión centralizada de todos sus secretos entre clústeres e inyección en el tiempo de ejecución de la aplicación, pruebe IBM Cloud Secrets Manager.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
...
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
Volúmenes persistentes para el almacenamiento de contenedores
Los volúmenes persistentes (PV) interactúan con el almacenamiento físico para proporcionar almacenamiento de datos persistentes para las cargas de trabajo del contenedor.
En el ejemplo siguiente se muestra cómo se puede añadir almacenamiento persistente a la app. Para suministrar almacenamiento persistente, cree una reclamación de volumen persistente (PVC) para describir el tipo y el tamaño del almacenamiento
de archivos que desea tener. Una vez creado el PVC, el volumen persistente y el almacenamiento físico se crean automáticamente mediante el aprovisionamiento dinámico. Cuando se hace referencia a la PVC en el archivo YAML de despliegue, el
almacenamiento se monta automáticamente en el pod de la app. Cuando el contenedor del pod escribe datos en el directorio de la vía de acceso de montaje /test, los datos se almacenan en la instancia de almacenamiento de archivos
NFS. Para ver las opciones de otros tipos de almacenamiento que puede suministrar, consulte Planificación del almacenamiento persistente de alta disponibilidad.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi
Archivo YAML de despliegue de ejemplo completo
A continuación se muestra un ejemplo de una copia del archivo YAML de despliegue que se ha explicado en las secciones anteriores. También puedes descargar el archivo YAML desde GitHub.
Para aplicar el YAML,
kubectl apply -f file.yaml [-n <namespace>]
Ejemplo de YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
containers:
- name: wasliberty
image: icr.io/ibm/liberty:latest
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
ports:
- containerPort: 9080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
---
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi