AWS : EKS
EKS (Elastic Kubernetes Service) est Kubernetes hébergé sur AWS. Le plan de contrôle (etcd, kube-apiserver, kube-controller-manager, kube-scheduler) est géré par AWS dans un compte isolé, non accessible. Ce qui reste à la charge de l'utilisateur : les nœuds workers, le réseau, le stockage, et les intégrations IAM.
EKS Auto Mode, disponible depuis fin 2024, étend ce périmètre géré. En plus du control plane, AWS prend en charge le provisionnement des nœuds (via Karpenter intégré), le driver de stockage EBS, et le contrôleur de load balancing. Pour un administrateur habitué à un cluster kubeadm sur site, c'est l'équivalent de Karpenter, du EBS CSI Driver et de l'AWS Load Balancer Controller préconfigurés et maintenus par AWS. Les notions Kubernetes de base sont présentées dans l'article Kubernetes : composants de base, et le réseau sous-jacent dans l'article VPC.
Ce qu'AWS gère en Auto Mode
AWS gère À configurer par l'utilisateur
──────────────────────────────── ─────────────────────────────
Control plane (API server, etcd...) Nœuds workers (via node pools)
Provisionnement des nœuds (Karpenter) NodeClass (critères d'instance)
OS des nœuds (Bottlerocket) Workloads applicatifs
EBS CSI Driver StorageClass, identités IAM des pods
AWS Load Balancer Controller Ingress / Services
Certificats internes du cluster Logging, monitoring
Le control plane est facturé 0,10 $/heure (~73 $/mois) tant que sa version de Kubernetes est en support standard, et six fois plus en support étendu. Les nœuds EC2 provisionnés par Karpenter sont facturés séparément selon leur type et leur durée de vie réelle, avec en Auto Mode des frais de gestion supplémentaires proportionnels au prix de chaque instance.
Prérequis : deux rôles IAM distincts
EKS met en jeu deux entités qui ont besoin d'appeler l'API AWS, mais qui ne sont pas la même entité et n'ont pas les mêmes besoins.
Le service EKS doit pouvoir agir dans le compte AWS : créer des ENIs dans le VPC, modifier des security groups, gérer les ressources réseau des nœuds. C'est eks.amazonaws.com qui fait ces appels, pas l'utilisateur. Il faut lui déléguer des permissions via un rôle qu'il peut assumer.
AmazonEKSAutoClusterRole
Trusted entity : eks.amazonaws.com (actions sts:AssumeRole et sts:TagSession)
Policies : AmazonEKSClusterPolicy, AmazonEKSComputePolicy,
AmazonEKSBlockStoragePolicy, AmazonEKSLoadBalancingPolicy,
AmazonEKSNetworkingPolicy
Les nœuds EC2 ont eux aussi besoin d'appeler l'API AWS : le kubelet doit s'authentifier auprès du kube-apiserver, et containerd doit puller des images depuis ECR. En Auto Mode, la gestion des interfaces réseau des nœuds relève du rôle du cluster, ce qui réduit le rôle des nœuds au minimum.
AmazonEKSAutoNodeRole
Trusted entity : ec2.amazonaws.com
Policies : AmazonEKSWorkerNodeMinimalPolicy, AmazonEC2ContainerRegistryPullOnly
Le rôle des nœuds n'est pas destiné aux applications : un pod qui appelle l'API AWS reçoit sa propre identité (voir la section sur les identités IAM des pods).
En Auto Mode, ces deux rôles peuvent être créés automatiquement par la console EKS lors de la création du cluster.
Créer le cluster (console AWS)
EKS → Create cluster
La console propose deux chemins. Quick configuration active automatiquement l'Auto Mode avec des valeurs par défaut. Custom configuration permet de contrôler chaque paramètre.
Dans les deux cas, les paramètres clés :
| Paramètre | Valeur |
|---|---|
| Kubernetes version | 1.36 (dernière disponible) |
| Region | eu-west-3 |
| Cluster IAM role | AmazonEKSAutoClusterRole |
| EKS Auto Mode | Activé |
| VPC | VPC existant avec 3 subnets (une par AZ) |
| Endpoint access | Public and private |
Public and private : l'API server est accessible depuis Internet (pour kubectl depuis un poste de travail) et depuis le VPC (pour les nœuds). L'accès public peut être restreint à des plages d'adresses (publicAccessCidrs). En production, Private force à passer par un bastion, un VPN ou un runner CI situé dans le VPC.
Les logs du plan de contrôle (API server, audit, authenticator, controller manager, scheduler) peuvent être envoyés vers CloudWatch Logs ; chaque type se sélectionne à la création ou ensuite, et l'ingestion est facturée au volume. Les logs d'audit sont les plus utiles pour retracer qui a modifié quoi dans le cluster.
Accéder au cluster
En local
aws eks update-kubeconfig --region eu-west-3 --name funny-bluegrass-orca
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# i-049f735198572cdc8 Ready <none> 71s v1.36.0-eks-75e0327
# i-05473ae22d581030d Ready <none> 71s v1.36.0-eks-75e0327
Un cluster Auto Mode fraîchement créé peut n'avoir aucun nœud : Karpenter n'en provisionne qu'à l'arrivée des premiers pods à planifier.
aws eks update-kubeconfig écrit un contexte dans ~/.kube/config. Ce contexte ne contient pas de token statique : à chaque appel kubectl, l'AWS CLI génère un token temporaire via aws eks get-token (expiration 15 minutes), vérifié par l'API server via IAM.
L'authentification ne passe plus par la ConfigMap aws-auth (méthode historique). EKS Auto Mode utilise le mode API access entries : les identités IAM autorisées sont gérées directement via l'API EKS.
# Lister les accès configurés
aws eks list-access-entries --cluster-name funny-bluegrass-orca --region eu-west-3
# Donner accès à un rôle IAM
aws eks create-access-entry \
--cluster-name funny-bluegrass-orca \
--principal-arn arn:aws:iam::123456789012:role/dev-team \
--region eu-west-3
aws eks associate-access-policy \
--cluster-name funny-bluegrass-orca \
--principal-arn arn:aws:iam::123456789012:role/dev-team \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy \
--access-scope type=cluster \
--region eu-west-3
Les access policies prédéfinies (AmazonEKSAdminPolicy, AmazonEKSEditPolicy, AmazonEKSViewPolicy) mappent sur des ensembles de règles RBAC Kubernetes gérés par AWS.
Depuis GitHub Actions
Pour qu'un workflow GitHub Actions puisse piloter le cluster, il faut lui donner une identité AWS. La méthode recommandée repose sur OIDC : GitHub Actions émet, pour chaque job, un jeton signé décrivant le dépôt, la branche et l'environnement, et AWS IAM peut faire confiance à cet émetteur. Aucune clé d'accès longue durée n'est stockée dans les secrets GitHub.
Côté AWS : créer un rôle IAM avec une trust policy qui autorise GitHub Actions à l'assumer via OIDC.
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:mon-org/mon-repo:*"
}
}
}
La condition sur sub est la protection essentielle : sans elle, n'importe quel dépôt GitHub pourrait assumer le rôle. repo:mon-org/mon-repo:* accepte toutes les branches et tous les événements du dépôt ; une valeur comme repo:mon-org/mon-repo:ref:refs/heads/main ou repo:mon-org/mon-repo:environment:production limite le rôle à une branche ou à un environnement GitHub protégé. Le fournisseur OIDC token.actions.githubusercontent.com doit avoir été déclaré une fois dans IAM.
Ce rôle doit avoir les permissions pour lire le cluster EKS (eks:DescribeCluster) et, si le workflow déploie des ressources, les droits correspondants. Il faut aussi lui créer une access entry sur le cluster, qui détermine ses droits Kubernetes.
Côté GitHub Actions :
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # nécessaire pour que GitHub génère le token OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-eks
aws-region: eu-west-3
- name: Update kubeconfig
run: aws eks update-kubeconfig --region eu-west-3 --name funny-bluegrass-orca
- name: Deploy
run: kubectl apply -f k8s/
configure-aws-credentials échange le token OIDC de GitHub contre des credentials AWS temporaires via sts:AssumeRoleWithWebIdentity. Les étapes suivantes utilisent ces credentials comme si elles tournaient sur une machine avec le rôle IAM configuré.
Depuis Terraform
Le provider Terraform kubernetes a besoin de l'endpoint et d'un token pour se connecter au cluster. Plutôt que de les coder en dur, ils sont récupérés dynamiquement depuis le provider aws, via des data sources :
data "aws_eks_cluster" "cluster" {
name = "funny-bluegrass-orca"
}
data "aws_eks_cluster_auth" "cluster" {
name = "funny-bluegrass-orca"
}
provider "kubernetes" {
host = data.aws_eks_cluster.cluster.endpoint
cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data)
token = data.aws_eks_cluster_auth.cluster.token
}
# Provider helm v3 : la configuration kubernetes est un attribut (= { ... }), plus un bloc
provider "helm" {
kubernetes = {
host = data.aws_eks_cluster.cluster.endpoint
cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data)
token = data.aws_eks_cluster_auth.cluster.token
}
}
aws_eks_cluster_auth génère, sans appel à la CLI, le même jeton de courte durée que aws eks get-token : une URL sts:GetCallerIdentity pré-signée, que l'API server fait vérifier par STS pour identifier l'appelant. L'identité utilisée est celle du runner Terraform au moment de l'exécution : un rôle IAM en CI, les credentials locales en développement. Ce rôle doit avoir une access entry sur le cluster avec les permissions nécessaires pour appliquer les ressources Kubernetes.
Le token expire après 15 minutes. Pour des terraform apply longs qui modifient beaucoup de ressources Kubernetes, utiliser l'exec plugin pour regénérer le token à la demande :
provider "kubernetes" {
host = data.aws_eks_cluster.cluster.endpoint
cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "aws"
args = ["eks", "get-token", "--cluster-name", "funny-bluegrass-orca", "--region", "eu-west-3"]
}
}
Avec exec, le client Kubernetes du provider invoque aws eks get-token pour obtenir un jeton frais lorsqu'il en a besoin, ce qui évite l'expiration du token en cours d'exécution. La CLI AWS doit alors être installée là où Terraform s'exécute.
Node Pools et Karpenter
En Auto Mode, il n'y a pas de node groups à créer manuellement. Karpenter est intégré et provisionne des nœuds à la demande quand des pods sont en état Pending faute de capacité.
Deux NodePools sont créés automatiquement :
kubectl get nodepools
# NAME NODECLASS NODES READY
# general-purpose default 0 True
# system default 2 True
system : réservé aux composants critiques. Il porte un taint CriticalAddonsOnly:NoSchedule qui empêche les pods utilisateur sans toleration correspondante d'y atterrir. Comme tout NodePool Karpenter, il n'a pas de taille minimale : ses nœuds existent tant que des pods tolérant ce taint y sont planifiés.
general-purpose : pour les workloads applicatifs. Démarre à 0 nœuds et scale à la demande. Se consolide automatiquement 30 secondes après que les nœuds deviennent vides ou sous-utilisés.
Les deux pools partagent une NodeClass qui définit les critères de sélection des instances :
# kubectl describe nodepool system (extrait)
Requirements:
- key: eks.amazonaws.com/instance-category
operator: In
values: [c, m, r] # compute, general purpose, memory
- key: eks.amazonaws.com/instance-generation
operator: Gt
values: ["4"] # instances de 5e génération ou plus
- key: karpenter.sh/capacity-type
operator: In
values: [on-demand]
Karpenter choisit lui-même le type d'instance dans ces critères selon les ressources demandées par les pods. Le premier nœud provisionné ici était un c7i-flex.large (2 vCPU, 4 Go RAM).
Les nœuds ont une durée de vie maximale de 336 heures (14 jours), après quoi ils sont remplacés par des nœuds frais. Ce mécanisme garantit que les nœuds tournent toujours sur la dernière version de Bottlerocket avec les derniers patches de sécurité.
OS : Bottlerocket
En Auto Mode, tous les nœuds tournent sur Bottlerocket, une distribution Linux minimaliste développée par AWS spécifiquement pour les workloads conteneurisés.
OS-IMAGE : Bottlerocket (EKS Auto, Standard) 2026.6.19 (aws-k8s-1.36-standard)
KERNEL : 6.18.33 (amd64)
CRI : containerd 2.1.8+bottlerocket
Bottlerocket n'a pas de shell interactif par défaut, pas de gestionnaire de paquets, et son système de fichiers racine est en lecture seule. Les mises à jour se font par remplacement atomique de l'OS entier (A/B partitions), pas par apt upgrade ; en Auto Mode, elles passent même par le remplacement complet des nœuds. Cette architecture réduit la surface d'attaque par rapport à une distribution généraliste comme Amazon Linux 2023. Le diagnostic d'un nœud passe par kubectl debug node/<nom> ou par l'API de logs de nœud, puisqu'aucune connexion SSH n'est possible.
Networking : le VPC CNI
Identique au mode classique. Chaque pod reçoit une IP directement dans le sous-réseau VPC, sans overlay ni encapsulation.
VPC 10.0.0.0/16
└── Subnet eu-west-3b (10.0.16.0/20)
├── Nœud EC2 10.0.27.34
│ ├── Pod A : 10.0.27.x ← IP VPC directe
│ └── Pod B : 10.0.27.x
└── Nœud EC2 10.0.29.122
├── Pod C : 10.0.29.x
└── Pod D : 10.0.29.x
Le VPC CNI pré-alloue des IPs secondaires (ou des préfixes /28 en mode prefix delegation) sur les ENIs du nœud. Sans délégation de préfixes, chaque type d'instance a une limite de pods liée au nombre d'ENIs supportées et d'IPs par ENI : max-pods = ENI × (IPs par ENI − 1) + 2, soit 29 pods pour un m5.large (3 ENI de 10 IPs). Chaque pod consommant une adresse du subnet, un subnet /24 s'épuise vite : le dimensionnement des subnets d'un cluster EKS se fait en nombre de pods, pas en nombre de nœuds. Pour voir la limite d'un nœud :
kubectl get node i-049f735198572cdc8 -o jsonpath='{.status.allocatable.pods}'
Stockage : EBS intégré
En Auto Mode, le EBS CSI Driver est géré par AWS et utilise le provisioner ebs.csi.eks.amazonaws.com (distinct du driver add-on classique ebs.csi.aws.com). Auto Mode ne crée aucune StorageClass pour ce provisioner. Une éventuelle classe gp2 présente dans le cluster, héritée de l'ancien provisioner intégré kubernetes.io/aws-ebs, n'est pas servie par le driver d'Auto Mode : un PVC qui y fait référence reste en Pending.
kubectl get sc
# NAME PROVISIONER VOLUMEBINDINGMODE
# gp2 kubernetes.io/aws-ebs WaitForFirstConsumer ← non géré par Auto Mode
WaitForFirstConsumer est le réglage critique. Sans lui, Kubernetes crée le volume EBS avant de savoir où le pod sera schedulé. Le volume peut alors se créer dans une AZ différente de celle du pod, rendant l'attachement impossible. Ce mode retarde la création jusqu'au scheduling du pod et crée le volume dans la même AZ.
Pour créer une StorageClass gp3 avec chiffrement, utilisée par défaut par les PVC qui n'en précisent pas :
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.eks.amazonaws.com
parameters:
type: gp3
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
EBS est limité à un seul nœud à la fois (ReadWriteOnce) et ancré dans une AZ : un pod dont le volume est en eu-west-3a ne peut être replanifié que sur un nœud de cette zone. Le détail des volumes persistants figure dans l'article Kubernetes : stockage. Pour du stockage partagé entre plusieurs pods ou plusieurs AZs, EFS (NFS managé) via son propre CSI driver est la solution.
Load Balancing intégré
En Auto Mode, elasticLoadBalancing.enabled: true est activé dans la configuration du cluster. AWS exécute et maintient l'équivalent de l'AWS Load Balancer Controller, qui crée des NLB (L4) et des ALB (L7) à partir des ressources Kubernetes. Les ressources lui sont attribuées par des identifiants propres à Auto Mode : loadBalancerClass: eks.amazonaws.com/nlb pour les Services (valeur par défaut dans un cluster Auto Mode) et controller: eks.amazonaws.com/alb pour les IngressClass. Un AWS Load Balancer Controller autogéré utilise d'autres valeurs (service.k8s.aws/nlb, ingress.k8s.aws/alb), ce qui permet aux deux de coexister pendant une migration.
Service LoadBalancer → NLB :
apiVersion: v1
kind: Service
metadata:
name: api
annotations:
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
type: LoadBalancer
loadBalancerClass: eks.amazonaws.com/nlb # facultatif en Auto Mode, explicite ici
selector:
app: api
ports:
- port: 80
targetPort: 8000
Auto Mode enregistre les IPs des pods comme cibles du NLB (target type ip), sans passer par kube-proxy ni par un NodePort. C'est possible parce que le VPC CNI donne des IPs VPC réelles aux pods.
Ingress → ALB : la configuration de l'ALB (exposition, certificat) se déclare dans un IngressClassParams, référencé par une IngressClass ; l'Ingress désigne cette classe par ingressClassName, qui remplace l'annotation dépréciée kubernetes.io/ingress.class.
apiVersion: eks.amazonaws.com/v1
kind: IngressClassParams
metadata:
name: alb
spec:
scheme: internet-facing
certificateARNs:
- arn:aws:acm:eu-west-3:123456789012:certificate/abc
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: alb
spec:
controller: eks.amazonaws.com/alb
parameters:
apiGroup: eks.amazonaws.com
kind: IngressClassParams
name: alb
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api
spec:
ingressClassName: alb
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80 # port du Service, pas celui du conteneur
Exposer un service sur Internet
Déployer un pod et le rendre accessible depuis Internet implique trois couches distinctes : le security group du cluster, les tags des subnets, et l'annotation du Service Kubernetes. Chaque couche est indépendante, et l'oubli de l'une d'elles laisse le service en état Pending indéfiniment.
1. Ouvrir le security group du cluster
EKS Auto Mode attache un security group au cluster (clusterSecurityGroupId dans la configuration). Par défaut, ce groupe n'autorise aucun trafic entrant depuis Internet. Il faut y ajouter explicitement la règle pour le port souhaité.
aws ec2 authorize-security-group-ingress \
--group-id sg-0436888777b09da32 \
--protocol tcp --port 80 --cidr 0.0.0.0/0
Sans cette règle, le load balancer est créé et reçoit le trafic, mais les paquets sont bloqués avant d'atteindre les pods. Une source 0.0.0.0/0 sur le Security Group du cluster ouvre le port à tout le VPC et à Internet pour toutes les interfaces qui le portent ; quand le load balancer dispose de son propre Security Group, référencer ce dernier comme source restreint l'accès au seul trafic transitant par le load balancer.
2. Taguer les subnets
Le Load Balancer Controller intégré découvre les subnets disponibles en interrogeant l'API EC2 avec des filtres sur les tags. Il ne connaît pas la configuration du VPC, il ne sait pas quels subnets sont publics ou privés. Les tags sont le seul moyen de lui indiquer où créer le load balancer.
for subnet in subnet-0e37a4f92e2f3eb32 subnet-02568af2a95869569 subnet-078c6ce8eb8657500; do
aws ec2 create-tags --resources $subnet \
--tags Key=kubernetes.io/role/elb,Value=1 \
Key=kubernetes.io/cluster/funny-bluegrass-orca,Value=shared
done
kubernetes.io/role/elb=1 signale que ce subnet public peut accueillir des load balancers Internet-facing ; son pendant kubernetes.io/role/internal-elb=1 désigne les subnets privés destinés aux load balancers internes. Sans ce tag, le service reste indéfiniment en état Pending : le controller ne trouve aucun subnet éligible et ne crée pas le LB. Le tag kubernetes.io/cluster/<nom> n'est plus requis par les versions récentes du controller, mais reste utile lorsque plusieurs clusters partagent un VPC.
3. Déployer et exposer
kubectl create deployment demo --image=nginx --replicas=2
kubectl expose deployment demo --type=LoadBalancer --port=80
# Forcer le scheme internet-facing (sinon le LB est interne par défaut)
kubectl annotate svc demo \
service.beta.kubernetes.io/aws-load-balancer-scheme=internet-facing --overwrite
Le choix entre internet-facing et internal ne se déduit pas automatiquement du type de subnet. Sans l'annotation, le controller crée un LB interne même si les subnets sont publics. L'annotation est obligatoire pour tout service destiné à être accessible depuis Internet.
Résultat
kubectl get svc demo
# NAME TYPE CLUSTER-IP EXTERNAL-IP
# demo LoadBalancer 10.100.x.x k8s-default-demo-36317854b0-89a4dd3b9a900cf5.elb.eu-west-3.amazonaws.com
curl k8s-default-demo-36317854b0-89a4dd3b9a900cf5.elb.eu-west-3.amazonaws.com
# HTTP 200 - nginx accessible depuis Internet
Le DNS du NLB est attribué par AWS lors de la création. La propagation DNS prend quelques secondes à quelques minutes. Le NLB pointe directement sur les IPs des pods (target type ip), sans passer par kube-proxy.
Récapitulatif des prérequis
| Étape | Sans elle |
|---|---|
Tag kubernetes.io/role/elb=1 sur les subnets | Service reste en Pending |
| Règle entrante dans le security group | LB créé mais trafic bloqué (timeout) |
Annotation aws-load-balancer-scheme=internet-facing | LB interne créé, inaccessible depuis Internet |
Identités IAM pour les pods : IRSA et EKS Pod Identity
L'association entre un pod et un rôle IAM reste à configurer, même en Auto Mode. Deux mécanismes permettent à un pod d'obtenir des credentials IAM temporaires sans secret statique : IRSA (IAM Roles for Service Accounts), historique, et EKS Pod Identity, plus récent.
IRSA
IRSA associe un rôle IAM à un ServiceAccount Kubernetes et repose sur OIDC. EKS expose un endpoint OIDC que AWS IAM reconnaît comme identity provider de confiance. Quand un pod démarre avec un ServiceAccount annoté, un webhook d'admission d'EKS modifie sa spécification : il monte un jeton de ServiceAccount projeté (JWT signé par le cluster, audience sts.amazonaws.com) et définit les variables AWS_ROLE_ARN et AWS_WEB_IDENTITY_TOKEN_FILE. Le SDK échange ce jeton contre des credentials IAM temporaires via sts:AssumeRoleWithWebIdentity.
Pod → ServiceAccount annoté → token JWT injecté
→ appel AWS API avec ce token
→ STS vérifie le token auprès de l'endpoint OIDC du cluster
→ credentials temporaires émises pour le rôle IAM associé
Mise en place
- Enregistrer l'OIDC provider dans IAM (une fois par cluster) :
# L'issuer OIDC du cluster est visible ici :
aws eks describe-cluster --name funny-bluegrass-orca --region eu-west-3 \
--query 'cluster.identity.oidc.issuer'
# "https://oidc.eks.eu-west-3.amazonaws.com/id/DDD18D1C5646ADCBA937A2C7FDF24504"
eksctl utils associate-iam-oidc-provider \
--cluster funny-bluegrass-orca --region eu-west-3 --approve
- Créer un rôle IAM avec une trust policy qui restreint l'accès à un ServiceAccount précis. La condition
StringEqualsgarantit que seul le ServiceAccountmy-appdans le namespacedefaultpeut assumer le rôle :
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.eu-west-3.amazonaws.com/id/DDD18D1C5646ADCBA937A2C7FDF24504"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.eu-west-3.amazonaws.com/id/DDD18D1C5646ADCBA937A2C7FDF24504:sub": "system:serviceaccount:default:my-app",
"oidc.eks.eu-west-3.amazonaws.com/id/DDD18D1C5646ADCBA937A2C7FDF24504:aud": "sts.amazonaws.com"
}
}
}
- Annoter le ServiceAccount avec l'ARN du rôle :
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
Les SDKs AWS (boto3, SDK Go, SDK Java) détectent automatiquement le jeton monté dans /var/run/secrets/eks.amazonaws.com/serviceaccount/token via ces variables d'environnement, et l'échangent sans configuration supplémentaire. Le pod doit être recréé après l'annotation du ServiceAccount : le webhook n'agit qu'à la création des pods.
EKS Pod Identity
EKS Pod Identity simplifie ce schéma : ni fournisseur OIDC à déclarer dans IAM, ni condition propre à chaque cluster dans la trust policy, ni annotation du ServiceAccount. Le rôle fait confiance au service pods.eks.amazonaws.com, et l'association entre rôle et ServiceAccount est enregistrée via l'API EKS. Un agent présent sur chaque nœud (intégré en Auto Mode) fournit les credentials aux pods concernés.
{
"Effect": "Allow",
"Principal": { "Service": "pods.eks.amazonaws.com" },
"Action": ["sts:AssumeRole", "sts:TagSession"]
}
aws eks create-pod-identity-association \
--cluster-name funny-bluegrass-orca --region eu-west-3 \
--namespace default --service-account my-app \
--role-arn arn:aws:iam::123456789012:role/my-app-role
La même trust policy sert pour tous les clusters, et les sessions portent des tags (cluster, namespace, ServiceAccount) utilisables dans les conditions des policies. IRSA reste nécessaire hors d'EKS (clusters autogérés) ou pour des cas non couverts par Pod Identity.
Coûts
| Composant | Coût approximatif |
|---|---|
| Control plane | 0,10 $/heure (~73 $/mois) |
| Nœud c7i-flex.large (2vCPU/4Go) | ~0,09 $/heure (~65 $/mois) |
| Nœud m5.xlarge (4vCPU/16Go) | ~0,22 $/heure (~160 $/mois) |
| Frais de gestion Auto Mode | pourcentage du prix de chaque instance (environ 12 %) |
| NAT Gateway | 0,045 $/heure (~33 $/mois) + données |
| EBS gp3 20 Go | ~1,60 $/mois |
| ALB ou NLB | ~0,0225 $/heure (~16 $/mois) + unités de capacité (LCU/NLCU) |
| IPv4 publique | 0,005 $/heure par adresse (NAT, load balancers) |
Les prix des instances sont des ordres de grandeur à la demande ; les nœuds Spot (karpenter.sh/capacity-type: spot dans un NodePool) réduisent ce poste de 60 à 90 % pour les workloads tolérant les interruptions.
Karpenter consolide les nœuds automatiquement : un nœud vide depuis 30 secondes est terminé. Le pool general-purpose scale à 0 quand il n'y a pas de workloads utilisateur, ce qui évite de payer des nœuds inutilisés en dehors des heures de travail.
Application / Projet lié
eu-west-3, dont les pods de l'API accèdent au bucket S3 des avatars via IRSA, sans clé d'accès stockée dans le cluster.FastAPIReactPostgreSQLTerraformHelmAWS EKSGitHub Actions