Hébergement Kubernetes

Kubernetes est un choix populaire pour l’hébergement d’applications Orleans. Orleans s’exécute dans Kubernetes sans configuration spécifique. Toutefois, il peut également tirer parti des informations supplémentaires fournies par la plateforme d’hébergement.

Le package Microsoft.Orleans.Hosting.Kubernetes ajoute l’intégration nécessaire pour l’hébergement d’une application Orleans dans un cluster Kubernetes. Le package fournit une méthode d’extension, UseKubernetesHosting, qui effectue les actions suivantes :

  • SiloOptions.SiloName a la valeur du nom du pod.
  • EndpointOptions.AdvertisedIPAddress a la valeur de l’IP du pod.
  • EndpointOptions.SiloListeningEndpoint et EndpointOptions.GatewayListeningEndpoint sont configurés pour écouter sur n’importe quelle adresse, avec SiloPort et GatewayPort également configurés. Les valeurs de port par défaut 11111 et 30000 sont utilisées si aucune valeur n’est définie explicitement).
  • ClusterOptions.ServiceId a la valeur de l’étiquette de pod nommée orleans/serviceId.
  • ClusterOptions.ClusterId a la valeur de l’étiquette de pod nommée orleans/clusterId.
  • Au début du processus de démarrage, le silo sonde Kubernetes à l’aide d’une probe pour détecter les silos qui n’ont pas de pods correspondants, et marquer ces silos comme étant morts.
  • Le même processus se produit au moment de l’exécution pour un sous-ensemble de tous les silos, afin de supprimer la charge sur le serveur d’API de Kubernetes. Par défaut, 2 silos du cluster surveillent Kubernetes.

Notez que le package d’hébergement Kubernetes n’utilise pas Kubernetes pour le clustering. Pour le clustering, un fournisseur de clustering distinct est toujours nécessaire. Pour plus d’informations sur la configuration du clustering, consultez la documentation relative à la configuration du serveur.

Cette fonctionnalité impose certaines obligations relatives au déploiement du service :

  • Les noms de silos doivent correspondre aux noms de pods.
  • Les pods doivent avoir une étiquette orleans/serviceId et orleans/clusterId qui correspond au ServiceId et au ClusterId du silo. La méthode mentionnée ci-dessus propage ces étiquettes au sein des options correspondantes dans Orleans à partir des variables d’environnement.
  • Les variables d’environnement suivantes doivent être définies pour les pods : POD_NAME, POD_NAMESPACE, POD_IP, ORLEANS_SERVICE_ID, ORLEANS_CLUSTER_ID.

L’exemple suivant montre comment configurer correctement ces étiquettes et ces variables d’environnement :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dictionary-app
  labels:
    orleans/serviceId: dictionary-app
spec:
  selector:
    matchLabels:
      orleans/serviceId: dictionary-app
  replicas: 3
  template:
    metadata:
      labels:
        # This label is used to identify the service to Orleans
        orleans/serviceId: dictionary-app

        # This label is used to identify an instance of a cluster to Orleans.
        # Typically, this will be the same value as the previous label, or any
        # fixed value.
        # In cases where you are not using rolling deployments (for example,
        # blue/green deployments),
        # this value can allow for distinct clusters which do not communicate
        # directly with each others,
        # but which still share the same storage and other resources.
        orleans/clusterId: dictionary-app
    spec:
      containers:
        - name: main
          image: my-registry.azurecr.io/my-image
          imagePullPolicy: Always
          ports:
          # Define the ports which Orleans uses
          - containerPort: 11111
          - containerPort: 30000
          env:
          # The Azure Storage connection string for clustering is injected as an
          # environment variable
          # It must be created separately using a command such as:
          # > kubectl create secret generic az-storage-acct `
          #     --from-file=key=./az-storage-acct.txt
          - name: STORAGE_CONNECTION_STRING
            valueFrom:
              secretKeyRef:
                name: az-storage-acct
                key: key
          # Configure settings to let Orleans know which cluster it belongs to
          # and which pod it is running in
          - name: ORLEANS_SERVICE_ID
            valueFrom:
              fieldRef:
                fieldPath: metadata.labels['orleans/serviceId']
          - name: ORLEANS_CLUSTER_ID
            valueFrom:
              fieldRef:
                fieldPath: metadata.labels['orleans/clusterId']
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
          - name: DOTNET_SHUTDOWNTIMEOUTSECONDS
            value: "120"
          request:
            # Set resource requests
      terminationGracePeriodSeconds: 180
      imagePullSecrets:
        - name: my-image-pull-secret
  minReadySeconds: 60
  strategy:
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1

Pour les clusters avec contrôle RBAC, vous devrez peut-être également octroyer l’accès nécessaire au compte de service Kubernetes des pods :

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: orleans-hosting
rules:
- apiGroups: [ "" ]
  resources: ["pods"]
  verbs: ["get", "watch", "list", "delete"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: orleans-hosting-binding
subjects:
- kind: ServiceAccount
  name: default
  apiGroup: ''
roleRef:
  kind: Role
  name: orleans-hosting
  apiGroup: ''

Probes liveness, readiness et startup

Kubernetes peut sonder les pods à l’aide d’une probe pour déterminer l’intégrité d’un service. Pour plus d’informations, consultez Configurer les probes liveness, readiness et startup dans la documentation de Kubernetes.

Orleans utilise un protocole d’appartenance à un cluster pour détecter rapidement les défaillances de processus ou réseau, et effectuer une reprise d’activité après sinistre. Chaque nœud effectue le monitoring d’un sous-ensemble d’autres nœuds en envoyant des probes périodiques. Si un nœud ne répond pas à plusieurs probes successives de plusieurs autres nœuds, il est supprimé de force du cluster. Une fois qu’un nœud défaillant apprend qu’il a été supprimé, il s’arrête immédiatement. Kubernetes redémarre le processus arrêté, puis tente de rejoindre le cluster.

Les probes Kubernetes permettent de déterminer si un processus de pod est en cours d’exécution, et s’il n’est pas bloqué à l’état de zombie. Les probes ne vérifient pas la connectivité ou la réactivité entre les pods, et n’effectuent aucune vérification des fonctionnalités au niveau de l’application. Si un pod ne répond pas à une probe liveness, Kubernetes peut finir par l’arrêter et le replanifier. Les probes de Kubernetes et les probes d’Orleans sont donc complémentaires.

L’approche recommandée consiste à configurer les probes Liveness dans Kubernetes pour qu’elles vérifient simplement localement que l’application fonctionne comme prévu. Ces probes servent à arrêter le processus en cas de gel total, par exemple à la suite d’une erreur d’exécution ou de tout autre événement inattendu.

Quotas de ressources

Kubernetes fonctionne conjointement avec le système d’exploitation pour implémenter des quotas de ressources. Cela permet d’appliquer des réservations et/ou des limites de processeur et de mémoire. Pour une application principale qui traite une charge interactive, nous vous recommandons de ne pas implémenter de limites restrictives, sauf si cela est nécessaire. Il est important de noter que les demandes et les limites sont très différentes, que ce soit dans le cadre de leur signification ou de leur implémentation. Avant de définir des demandes ou des limites, prenez le temps de comprendre en détail la façon dont elles sont implémentées et appliquées. Par exemple, il est possible que la mémoire ne soit pas mesurée de manière uniforme entre Kubernetes, le noyau Linux et votre système de monitoring. Les quotas de processeur ne sont peut-être pas appliqués tel que vous le souhaitez.

Dépannage

Les pods plantent, en indiquant KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined

Message d’exception complet :

Unhandled exception. k8s.Exceptions.KubeConfigException: unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined
at k8s.KubernetesClientConfiguration.InClusterConfig()
  • Vérifiez que les variables d’environnement KUBERNETES_SERVICE_HOST et KUBERNETES_SERVICE_PORT sont définies dans votre pod. Pour ce faire, vous pouvez exécuter la commande suivante kubectl exec -it <pod_name> /bin/bash -c env.
  • Vérifiez que automountServiceAccountToken a la valeur true dans votre fichier deployment.yaml Kubernetes. Pour plus d’informations, consultez Configurer des comptes de service pour les pods