EKS-ALB

Publié le 15 décembre 2022, mis à jour le 18 août 2023.

EKS va gérer plusieurs applications via les ressources de ses pods. Afin d’éviter que vos pods ne surchargent, il est recommandé d’utiliser un Application Load Balancer (ALB).

 

Dans cet article, nous allons voir comment implémenter un ALB sur un cluster EKS, via le Chart Helm "ALB ingress controller".

Qu’est ce qu’un Application Load Balancer (ALB) ?

Un Application Load Balancer est un pont créé entre le trafic entrant et plusieurs cibles (par exemple plusieurs pods/replicas pour une application). Son objectif est de permettre aux applications hébergées sur vos pods d’avoir une haute disponibilité.

Comment implémenter un ALB sur un cluster EKS ?

L’architecture


Nous allons maintenant voir comment implémenter un Application Load Balancer grâce à 2 ingress controllers : l’ALB ingress controller et le Nginx ingress controller. Pourquoi en utiliser 2 ?

  • Le Nginx ingress controller ne peut pas se connecter directement à un Application Load Balancer.
  • Un ALB ingress controller seul vous fournira un ALB par application du cluster et non pour l’ensemble de vos ressources. (Ce qui peut être utile si vous souhaitez avoir un un load balancer par application)

Dans ce tutoriel, nous allons déployer les 2. Il est important de noter que le Nginx ingress controller gérera toutes les ressources d'entrée de vos applications dans votre cluster EKS tandis que l’ALB ingress controller gérera le cycle de vie de votre instance ALB. (cf schéma ci-dessous)

ALB_instances

La création d’un rôle IAM avec ID provider


Etant donné que l’implémentation de votre ALB se fait dans votre cluster EKS, votre Chart Helm ALB ingress controller aura besoin de permissions pour créer des ressources AWS. Le Chart Helm aura besoin d’un rôle AWS lui permettant de déployer une instance ALB que vous pourrez lui créer dans le service AWS IAM.

Qu’est ce qu’un identity provider AWS ?

Un identity provider donne à un utilisateur externe certaines autorisations au sein d’AWS.

  • Dans notre cas, l’identity provider AWS donnera des permissions au compte de l’ALB ingress controller.

Pour le créer nous avons besoin de 3 éléments : le type de l’identity provider, son droit et son URL.

  • Dans notre cas le type sera “OpenId Connect” et son droit “sts.amazonaws.com”.
  • Pour connaître l’URL :
    • Vous devez exécuter la commande suivante :
    • aws eks describe-cluster --name <CLUSTER_NAME> --query “cluster.identity.oidc.issuer” --output text
    • N’oubliez pas de remplacer <CLUSTER_NAME> par le nom de votre cluster EKS.
    • Normalement, la commande vous donnera une adresse URL de ce type :
    • https://oidc.eks.<region>.amazonaws.com/id/EXAMPLE86F27C29EF05B482628D9790EA706

Une fois que vous aurez récupéré l’ensemble des informations nécessaires, il faudra vous rendre dans la console AWS, dans le service IAM et cliquer sur la section “Identity provider”. Dans cette section, vous pourrez créer un provider comme illustré ci-dessous :

configure_provider

Le Chart Helm devra utiliser un rôle IAM de cet Identity Provider. Il vous faut donc créer un nouveau rôle IAM.

role_iam

Dans la section “Identity provider”, choisissez le bon provider puis entrez le droit : "sts.amazonaws.com".

Ensuite, ajoutez les politiques suivantes pour ce rôle :

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "autoscaling:DescribeAutoScalingGroups",
                "autoscaling:UpdateAutoScalingGroup",
                "ec2:AttachVolume",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateRoute",
                "ec2:CreateSecurityGroup",
                "ec2:CreateTags",
                "ec2:CreateVolume",
                "ec2:DeleteRoute",
                "ec2:DeleteSecurityGroup",
                "ec2:DeleteVolume",
                "ec2:DescribeInstances",
                "ec2:DescribeRouteTables",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVolumes",
                "ec2:DescribeVolumesModifications",
                "ec2:DescribeVpcs",
                "ec2:DescribeDhcpOptions",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DetachVolume",
                "ec2:ModifyInstanceAttribute",
                "ec2:ModifyVolume",
                "ec2:RevokeSecurityGroupIngress",
                "elasticloadbalancing:AddTags",
                "elasticloadbalancing:ApplySecurityGroupsToLoadBalancer",
                "elasticloadbalancing:AttachLoadBalancerToSubnets",
                "elasticloadbalancing:ConfigureHealthCheck",
                "elasticloadbalancing:CreateListener",
                "elasticloadbalancing:CreateLoadBalancer",
                "elasticloadbalancing:CreateLoadBalancerListeners",
                "elasticloadbalancing:CreateLoadBalancerPolicy",
                "elasticloadbalancing:CreateTargetGroup",
                "elasticloadbalancing:DeleteListener",
                "elasticloadbalancing:DeleteLoadBalancer",
                "elasticloadbalancing:DeleteLoadBalancerListeners",
                "elasticloadbalancing:DeleteTargetGroup",
                "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
                "elasticloadbalancing:DeregisterTargets",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:DescribeLoadBalancerAttributes",
                "elasticloadbalancing:DescribeLoadBalancerPolicies",
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:DescribeTargetGroupAttributes",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:DetachLoadBalancerFromSubnets",
                "elasticloadbalancing:ModifyListener",
                "elasticloadbalancing:ModifyLoadBalancerAttributes",
                "elasticloadbalancing:ModifyTargetGroup",
                "elasticloadbalancing:ModifyTargetGroupAttributes",
                "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:SetLoadBalancerPoliciesForBackendServer",
                "elasticloadbalancing:SetLoadBalancerPoliciesOfListener",
                "kms:DescribeKey"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:CreateServiceLinkedRole",
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"
                }
            }
        }
    ]
}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "acm:DescribeCertificate",
                "acm:ListCertificates",
                "acm:GetCertificate"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateSecurityGroup",
                "ec2:CreateTags",
                "ec2:DeleteTags",
                "ec2:DeleteSecurityGroup",
                "ec2:DescribeAccountAttributes",
                "ec2:DescribeAddresses",
                "ec2:DescribeInstances",
                "ec2:DescribeInstanceStatus",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeTags",
                "ec2:DescribeVpcs",
                "ec2:ModifyInstanceAttribute",
                "ec2:ModifyNetworkInterfaceAttribute",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:AddListenerCertificates",
                "elasticloadbalancing:AddTags",
                "elasticloadbalancing:CreateListener",
                "elasticloadbalancing:CreateLoadBalancer",
                "elasticloadbalancing:CreateRule",
                "elasticloadbalancing:CreateTargetGroup",
                "elasticloadbalancing:DeleteListener",
                "elasticloadbalancing:DeleteLoadBalancer",
                "elasticloadbalancing:DeleteRule",
                "elasticloadbalancing:DeleteTargetGroup",
                "elasticloadbalancing:DeregisterTargets",
                "elasticloadbalancing:DescribeListenerCertificates",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:DescribeLoadBalancerAttributes",
                "elasticloadbalancing:DescribeRules",
                "elasticloadbalancing:DescribeSSLPolicies",
                "elasticloadbalancing:DescribeTags",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeTargetGroupAttributes",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:ModifyListener",
                "elasticloadbalancing:ModifyLoadBalancerAttributes",
                "elasticloadbalancing:ModifyRule",
                "elasticloadbalancing:ModifyTargetGroup",
                "elasticloadbalancing:ModifyTargetGroupAttributes",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:RemoveListenerCertificates",
                "elasticloadbalancing:RemoveTags",
                "elasticloadbalancing:SetIpAddressType",
                "elasticloadbalancing:SetSecurityGroups",
                "elasticloadbalancing:SetSubnets",
                "elasticloadbalancing:SetWebACL"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceLinkedRole",
                "iam:GetServerCertificate",
                "iam:ListServerCertificates"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "cognito-idp:DescribeUserPoolClient"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "waf-regional:GetWebACLForResource",
                "waf-regional:GetWebACL",
                "waf-regional:AssociateWebACL",
                "waf-regional:DisassociateWebACL"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "tag:GetResources",
                "tag:TagResources"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "waf:GetWebACL"
            ],
            "Resource": "*"
        }
    ]
}

Nous disposons désormais d'un rôle IAM prêt à être utilisé par un compte de service dans votre cluster EKS. Il nous faut désormais installer le Nginx ingress controller puis le ALB ingress controller.

Le tagging de vos sous-réseaux


Avant de débuter l’installation de vos ingress controllers, vous allez tagguer vos sous-réseaux. Pour créer l’instance ALB dans les bons sous-réseaux vous devez apposer les tags suivants à votre ALB ingress controller :

------ For public subnets ------
- Key: kubernetes.io/cluster/<CLUSTERNAME>
  Value: shared

- Key: kubernetes.io/role/elb
  Value: 1

------ For private subnets ------
Key: kubernetes.io/role/internal-elb
Value: 1

L’installation du Chart Helm Nginx Ingress Controller


Pour l’installer, vous allez ajouter un nouveau repo Helm à votre cluster “http://storage.googleapis.com/kubernetes-charts-incubator” grâce à la commande :

helm add repo <repository_name>>https://kubernetes-charts.storage.googleapis.com

Vous pouvez choisir le nom de repo que vous souhaitez. Une fois que c’est fait, vous pouvez désormais installer le Chart Helm avec la commande suivante :

helm install <repository_name>/nginx-ingress --set-string controller.service.externalTrafficPolicy=Local --set-string controller.service.type=NodePort --set controller.publishService.enabled=true --set serviceAccount.create=true --set rbac.create=true --set-string controller.config.server-tokens=false --set-string controller.config.use-proxy-protocol=false --set-string controller.config.compute-full-forwarded-for=true --set-string controller.config.use-forwarded-headers=true --set controller.metrics.enabled=true --set controller.autoscaling.maxReplicas=1 --set controller.autoscaling.minReplicas=1 --set controller.autoscaling.enabled=true --namespace kube-system


Tout d’abord, vous devez ajouter le bon repo Helm à ce chart : helm add repo <repo_name> <http://storage.googleapis.com/kubernetes-charts-incubator>

Ensuite, vous allez pouvoir installer ce Chart Helm “ALB Ingress controller” puis le Chart Helm suivant :

helm install <repo_name>/aws-alb-ingress-controller --set clusterName=<CLUSTER_NAME> --set awsRegion=<AWS_REGION> --set awsVpcID=<VPC_ID> --set rbac.serviceAccountAnnotations.eks\.amazonaws\.com/role-arn=<ROLE_ARN> --set scope.ingressClass=alb --namespace kube-system

N’oubliez pas de changer les variables suivantes :

  • <CLUSTER_NAME> (par le nom de votre cluster)
  • <AWS_REGION> (par la région AWS correspondante)
  • <VPC_ID> (par le bon ID VPC)
  • <ROLE_ARN> (par le rôle IAM que vous avez créé)

Pour vérifier que les pods sont correctement crées, exécutez la commande suivante :

kubectl get pods -n kube-system

On configure plusieurs variables dans le Chart Helm mais il est important d’avoir en tête que le Nginx ingress controller sera un service Nodeport et non ClusterIP.

La connexion de l’ALB au Nginx ingress controller


Pour connecter l’ALB au Nginx ingress controller, vous allez devoir créer une ressource d’entrée Kubernetes dans le namespace kube-system avec la configuration suivante :

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: <CERTIFICATE_ARN>
    alb.ingress.kubernetes.io/healthcheck-path: /healthz
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/subnets: <SUBNETS_LIST>
    kubernetes.io/ingress.class: alb
  name: alb-ingress-connect-nginx
  namespace: kube-system
spec:
  backend:
    serviceName: <NGINX_INGRESS_CONTROLLER_SERVICE_NAME>
    servicePort: http

Dans ce fichier, changez <CERTIFICATE_ARN><SUBNETS_LIST> et <NGINX_INGRESS_CONTROLLER_SERVICE_NAME>.

Cette configuration suppose que vous disposez déjà d’un certificat valide, si ça n’est pas le cas créez en un.

Exécutez la commande suivante pour créer cette ressource : kubectl apply -f <path_to_ingress>/<ingress_file_name>.yaml

Nous venons de déployer un ALB qui monitore la ressource ingress définit tout à l’heure et transfère le trafic de l'ALB ingress controller au Nginx ingress controller.

Et voilà, après avoir suivi ce tutoriel, vous avez désormais une instance ALB sur votre infrastructure AWS. Votre instance ALB est prête à être utilisée alors allez la tester !