Ce tutoriel décrit comment personnaliser les nœuds d'un cluster Google Kubernetes Engine (GKE) à l'aide de DaemonSets. Un DaemonSet permet de s'assurer que tous les nœuds (ou les nœuds sélectionnés) exécutent une copie d'un pod. Lorsque de nouveaux nœuds sont ajoutés à un cluster, ils exécutent également un pod à partir du DaemonSet.
Si les outils et systèmes avec lesquels vous initialisez vos clusters sont différents de ceux avec lesquels vous exécutez vos charges de travail, l'effort nécessaire à la gestion de votre environnement augmente. Par exemple, si vous initialisez les nœuds de cluster à l'aide d'un outil de gestion des configurations, vous vous appuyez sur une procédure située en dehors de l'environnement d'exécution accueillant le reste de vos charges de travail. L'utilisation d'un DaemonSet vous permet d'orchestrer vos charges de travail à l'aide des mêmes outils que ceux que vous utilisez pour modifier vos nœuds GKE.
L'objectif de ce tutoriel est d'aider les administrateurs système, les ingénieurs système et les opérateurs d'infrastructure à simplifier l'initialisation des clusters Kubernetes.
Avant de lire cette page, assurez-vous de connaître les éléments suivants :
Dans ce tutoriel, vous allez apprendre à utiliser les rejets et les tolérances Kubernetes pour vous assurer que les nœuds sont configurés par un DaemonSet avant que les charges de travail d'application puissent y être planifiées.
Objectifs
Dans ce tutoriel, vous allez effectuer les opérations suivantes :
- Provisionner un cluster GKE
- Rejeter un pool de nœuds pour empêcher la planification des charges de travail avant d'appliquer la configuration des nœuds
- Déployer un DaemonSet qui configure les nœuds et supprime le rejet
- Vérifier que les nœuds de cluster sont configurés et que le rejet est supprimé
Coûts
Dans ce document, vous utilisez les composants payants suivants de Google Cloud:
Obtenez une estimation des coûts en fonction de votre utilisation prévue,
utilisez le simulateur de coût.
Une fois que vous avez terminé les tâches décrites dans ce document, supprimez les ressources que vous avez créées pour éviter que des frais vous soient facturés. Pour en savoir plus, consultez la section Effectuer un nettoyage.
Avant de commencer
- Connectez-vous à votre Google Cloud compte. Si vous n'avez jamais utilisé Google Cloud, créez un compte pour évaluer les performances de nos produits dans des scénarios réels. Les nouveaux clients bénéficient également de 300 $de crédits sans frais pour exécuter, tester et déployer des charges de travail.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
Roles required to select or create a project
- Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
-
Create a project: To create a project, you need the Project Creator role
(
roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
-
Verify that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
Roles required to select or create a project
- Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
-
Create a project: To create a project, you need the Project Creator role
(
roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
-
Verify that billing is enabled for your Google Cloud project.
Implications en termes de sécurité d'un DaemonSet privilégié
L'utilisation du paramètre securityContext: privileged: true dans un DaemonSet (ou n'importe quel pod) est puissante, mais elle a des implications importantes en termes de sécurité, car elle désactive la plupart des limites d'isolation des conteneurs pour ce pod. Vous devez connaître les restrictions et les risques de sécurité suivants qu'elle introduit :
- Échappement de conteneur ou compromission de l'hôte : une faille dans l'application ou l'image de conteneur privilégié peut entraîner un accès root direct sur le nœud hôte.
- Violation du principe du moindre privilège : le mode privilégié accorde toutes les fonctionnalités, probablement bien plus que nécessaire pour une tâche spécifique. Cet accès étendu augmente les dommages potentiels si le conteneur est compromis.
- Déstabilisation des nœuds : des commandes accidentelles ou malveillantes peuvent s'exécuter dans le conteneur privilégié, par exemple des valeurs
sysctlincorrectes ou des commandes telles querm -rf /host/boot. Ces types de commandes peuvent entraîner le plantage ou la corruption du système d'exploitation du nœud hôte. - Mouvement latéral : la compromission d'un nœud via un DaemonSet privilégié donne à un attaquant un point d'appui solide pour attaquer d'autres nœuds, le plan de contrôle Kubernetes ou les systèmes connectés.
- Exposition des données : un accès illimité au système de fichiers hôte (
/) peut exposer des données sensibles stockées sur le nœud, y compris des identifiants, des clés ou des données appartenant à d'autres pods s'ils utilisent des volumes hostPath. - Augmentation de la surface d'attaque : le mode privilégié expose davantage d'appels et de fonctionnalités du système de noyau hôte à des exploits potentiels depuis le conteneur.
Pour éviter les risques de sécurité, vous devez connaître les bonnes pratiques et les mesures d'atténuation suivantes :
- Évitez d'utiliser le mode privilégié : l'approche la plus sécurisée consiste à éviter complètement le paramètre
privileged: true. - Utilisez les fonctionnalités Linux : si des droits élevés sont nécessaires, vous pouvez accorder des fonctionnalités Linux spécifiques telles que
NET_ADMIN,SYS_ADMIN,SYS_MODULEdans le champsecurityContext.capabilities.addau lieu d'un privilège complet. Cette approche suit le principe du moindre privilège, que nous vous recommandons plutôt que d'accorder des autorisations étendues. - Limitez le champ d'application : n'exécutez les DaemonSets privilégiés que sur des pools de nœuds dédiés, éventuellement rejetés, afin de limiter l'impact potentiel si un conteneur est compromis.
- Appliquez des règles : utilisez des outils tels que Policy Controller ou Gatekeeper pour créer des règles qui limitent, auditent ou exigent une justification pour le déploiement de conteneurs privilégiés.
- Analysez et utilisez des images fiables : utilisez l'autorisation binaire et une analyse rigoureuse des images pour vous assurer que seules les images de conteneurs approuvées et fiables sont exécutées avec des privilèges élevés.
- Réduisez au minimum les montages hôtes : ne montez que les chemins d'accès hôtes spécifiques nécessaires et utilisez
readOnly: truechaque fois que cela est possible. Évitez de monter l'ensemble du système de fichiers racine (/). - Effectuez des audits réguliers : examinez régulièrement toutes les charges de travail exécutées avec le paramètre
privileged: true.
Amorcer l'environnement
Dans cette section, vous allez exécuter les opérations suivantes :
- Activer les API Cloud nécessaires
- Provisionner un compte de service ayant des droits limités sur les nœuds du cluster GKE
- Préparer le cluster GKE
- Accorder à l'utilisateur des droits d'administrateur sur le cluster
Activer API Cloud
Ouvrez Cloud Shell.
Sélectionnez le Google Cloud projet :
gcloud config set project project-id
Remplacez
project-idpar l'ID du Google Cloud projet que vous avez créé ou sélectionné pour ce tutoriel.Activez l'API Kubernetes Engine :
gcloud services enable container.googleapis.com
Provisionner un compte de service pour gérer les clusters GKE
Dans cette section, vous allez créer un compte de service associé aux nœuds du cluster. Dans ce tutoriel, les nœuds GKE utilisent ce compte de service au lieu du compte de service par défaut. Nous vous recommandons de n'accorder au compte de service que les rôles et autorisations d'accès nécessaires pour exécuter l'application.
Les rôles requis pour le compte de service sont les suivants :
- Lecteur Monitoring (
roles/monitoring.viewer). Ce rôle accorde un accès en lecture seule aux données de surveillance. - Rédacteur de métriques Monitoring (
roles/monitoring.metricWriter). Ce rôle permet d'écrire des données de surveillance. - Rédacteur de journaux (
roles/logging.logWriter). Ce rôle accorde des autorisations pour écrire des journaux.
Pour provisionner un compte de service, procédez comme suit :
Dans Cloud Shell, initialisez une variable d'environnement qui stocke le nom du compte de service :
GKE_SERVICE_ACCOUNT_NAME=ds-init-tutorial-gkeCréez un compte de service :
gcloud iam service-accounts create "$GKE_SERVICE_ACCOUNT_NAME" \ --display-name="$GKE_SERVICE_ACCOUNT_NAME"Initialisez une variable d'environnement qui stocke le nom du compte de messagerie du compte de service :
GKE_SERVICE_ACCOUNT_EMAIL="$(gcloud iam service-accounts list \ --format='value(email)' \ --filter=displayName:"$GKE_SERVICE_ACCOUNT_NAME")"Associez les rôles de gestion de l'authentification et des accès (IAM) au compte de service :
gcloud projects add-iam-policy-binding \ "$(gcloud config get-value project 2> /dev/null)" \ --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \ --role roles/monitoring.viewer gcloud projects add-iam-policy-binding \ "$(gcloud config get-value project 2> /dev/null)" \ --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \ --role roles/monitoring.metricWriter gcloud projects add-iam-policy-binding \ "$(gcloud config get-value project 2> /dev/null)" \ --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \ --role roles/logging.logWriter
Préparer le cluster GKE
Dans cette section, vous allez lancer le cluster GKE, accorder des autorisations et terminer la configuration du cluster.
Ici, un cluster doté d'un nombre relativement faible de petits nœuds à usage général suffit à démontrer le concept de ce tutoriel. Vous allez tout d'abord créer un cluster avec un seul pool de nœuds (le pool par défaut),
Dans Cloud Shell, créez et lancez un cluster GKE régional :
gcloud container clusters create ds-init-tutorial \ --enable-ip-alias \ --machine-type=n1-standard-2 \ --metadata disable-legacy-endpoints=true \ --node-labels=app=default-init \ --node-locations us-central1-a,us-central1-b,us-central1-c \ --no-enable-basic-auth \ --no-issue-client-certificate \ --num-nodes=1 \ --location us-central1 \ --service-account="$GKE_SERVICE_ACCOUNT_EMAIL"
Appliquer des configurations de nœuds à l'aide d'un DaemonSet
Dans cette section, vous allez empêcher les charges de travail de s'exécuter sur les nœuds avant la fin de la configuration en appliquant un rejet au pool de nœuds. Vous allez ensuite déployer un DaemonSet qui effectue les opérations suivantes :
- Planifie les pods sur les nœuds rejetés à l'aide d'une tolérance pour le rejet.
- Exécute un conteneur d'initialisation privilégié qui applique d'abord la configuration du nœud à l'aide de
sysctl, puis supprime le rejet du nœud à l'aide dekubectl. La suppression du rejet permet de planifier le nœud pour les charges de travail. - Planifie et exécute un conteneur de pause qui reste inactif et ne consomme aucune ressource pour empêcher le DaemonSet de replanifier le pod utilisé pour la configuration.
Ce tutoriel applique le paramètre de noyau vm.max_map_count=262144 comme exemple de configuration.
Appliquez un rejet au pool de nœuds par défaut :
gcloud container node-pools update default-pool \ --cluster=ds-init-tutorial \ --node-taints=node.config.status/stage=configuring:NoSchedule \ --region=us-central1Avec ce rejet, seuls les pods qui le tolèrent, comme le pod DaemonSet, peuvent être planifiés dans ce pool de nœuds.
Vérifiez que le rejet est appliqué :
kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep TaintsL'état du nœud doit afficher
node.config.status/stage=configuring:NoSchedule.Enregistrez le manifeste suivant sous le nom
auto-untaint-daemonset.yaml:# WARNING: This DaemonSet runs as privileged, which has significant # security implications. Only use this on clusters where you have # strict controls over what is deployed. --- apiVersion: v1 kind: ServiceAccount metadata: name: node-config-sa namespace: default --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: node-patcher-role rules: - apiGroups: [""] resources: ["nodes"] # Permissions needed to read and remove a taint from the node. verbs: ["get", "patch", "update"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: node-config-binding subjects: - kind: ServiceAccount name: node-config-sa namespace: default roleRef: kind: ClusterRole name: node-patcher-role apiGroup: rbac.authorization.k8s.io --- apiVersion: apps/v1 kind: DaemonSet metadata: name: auto-untaint-daemonset labels: app: auto-untaint-configurator spec: selector: matchLabels: app: auto-untaint-configurator updateStrategy: type: RollingUpdate template: metadata: labels: app: auto-untaint-configurator spec: serviceAccountName: node-config-sa hostPID: true # Toleration now matches the taint on your node. tolerations: - key: "node.config.status/stage" operator: "Equal" value: "configuring" effect: "NoSchedule" volumes: - name: host-root-fs hostPath: path: / initContainers: - name: configure-and-untaint image: ubuntu:22.04 # Using a standard container image. securityContext: privileged: true # Required for chroot and sysctl. env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName volumeMounts: - name: host-root-fs mountPath: /host command: ["/bin/bash", "-c"] args: - | # Using explicit error checking for each critical command. # Define the configuration and taint details. SYSCTL_PARAM="vm.max_map_count" SYSCTL_VALUE="262144" TAINT_KEY="node.config.status/stage" echo "Running configuration on node: ${NODE_NAME}" # 1. APPLY CONFIGURATION echo "--> Applying ${SYSCTL_PARAM}=${SYSCTL_VALUE}..." if ! chroot /host sysctl -w "${SYSCTL_PARAM}=${SYSCTL_VALUE}"; then echo "ERROR: Failed to apply sysctl parameter." >&2 exit 1 fi echo "--> Configuration applied successfully." # 2. UNTAINT THE NODE # This command removes the taint from the node this Pod is running on. echo "--> Untainting node ${NODE_NAME} by removing taint ${TAINT_KEY}..." if ! /host/home/kubernetes/bin/kubectl taint node "${NODE_NAME}" "${TAINT_KEY}:NoSchedule-"; then echo "ERROR: Failed to untaint the node." >&2 exit 1 fi echo "--> Node has been untainted and is now schedulable." # The main container is minimal; it just keeps the Pod running. containers: - name: pause-container image: registry.k8s.io/pause:3.9Ce manifeste crée un ServiceAccount, un ClusterRole et un ClusterRoleBinding pour accorder au DaemonSet l'autorisation de supprimer les rejets des nœuds. Le DaemonSet déploie un pod sur chaque nœud qui tolère le rejet
configuring:NoSchedule. Ce pod exécute un conteneur d'initialisation privilégié qui applique la configurationsysctl(vm.max_map_count=262144) et supprime le rejet du nœud, ce qui permet de planifier le nœud. Un conteneur de pause démarre ensuite pour maintenir le pod en cours d'exécution.Le conteneur d'initialisation s'exécute en mode privilégié, ce qui a des implications en termes de sécurité. Pour en savoir plus, consultez les compromis et les restrictions de sécurité liés aux DaemonSets privilégiés.
Appliquez le fichier manifeste :
kubectl apply -f auto-untaint-daemonset.yamlVérifiez que les pods DaemonSet sont créés et attendez qu'ils atteignent l'état
Running:kubectl get pods -l app=auto-untaint-configurator -o wideL'état
Runningindique que le conteneur d'initialisation s'est terminé correctement. Notez le nom du pod afin de pouvoir l'utiliser pour vérifier l'initialisation dans la section suivante.
Valider et vérifier la procédure d'initialisation
Une fois la configuration du nœud terminée, vous pouvez vérifier les résultats en consultant les journaux.
Consultez les journaux du conteneur d'initialisation de l'un des pods pour voir sa sortie :
kubectl logs POD_NAME -c configure-and-untaintRemplacez
POD_NAMEpar le nom de votre pod.Vous devriez voir une sortie indiquant que la configuration et la suppression du rejet du nœud ont réussi.
Vérifiez que le rejet est supprimé :
kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep TaintsL'état du nœud doit afficher
Taints: <none>ou des rejets dont la clé estnode.config.status/stage.
Effectuer un nettoyage
Pour éviter que les ressources utilisées ici soient facturées sur votre Google Cloud compte, vous pouvez supprimer le projet créé pour ce tutoriel. Si vous avez créé un projet dédié à ce tutoriel, vous pouvez le supprimer entièrement. Si vous avez utilisé un projet existant, mais que vous ne souhaitez pas le supprimer, procédez comme suit pour nettoyer le projet .
Nettoyer le projet
Pour nettoyer un projet sans le supprimer, vous devez supprimer les ressources que vous avez créées dans le cadre de ce tutoriel.
Dans Cloud Shell, supprimez le cluster GKE :
gcloud container clusters delete ds-init-tutorial --quiet --region us-central1Supprimez le compte de service :
gcloud iam service-accounts delete "$GKE_SERVICE_ACCOUNT_EMAIL" --quiet
Supprimer le projet
Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.
- Dans la Google Cloud console, accédez à la page Gérer les ressources.
- Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
- Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez Arrêter pour supprimer le projet.
Étape suivante
- Apprenez-en plus sur GKE.
- Mettez en œuvre une chaîne d'approvisionnement logicielle sécurisée.
- Découvrez comment renforcer la sécurité de votre cluster GKE.
- Découvrez des architectures de référence, des schémas et des bonnes pratiques concernant Google Cloud. Consultez notre Cloud Architecture Center.