- Create kubernetes cluster with eksctl
export AWS_REGION=eu-north-1
export CLUSTER_NAME=traefik-http3
export KUBECONFIG=${HOME}/.kube/traefik-http3
## Create the EKS cluster
eksctl create cluster \
--name ${CLUSTER_NAME} \
--region ${AWS_REGION} \
--version 1.21
It takes arround 20 minutes to have the cluster up and running.
- Check that you can access to the created cluster
kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-45-130.eu-north-1.compute.internal Ready <none> 102s v1.21.5-eks-bc4871b
ip-192-168-87-177.eu-north-1.compute.internal Ready <none> 104s v1.21.5-eks-bc4871b
- Install traefik
Here we will install Traefik Ingress Controller into our EKS cluster to support HTTP/3.
This is a classic Traefik installation in kubernetes, we will first install the CRDs, and creates some RBAC to allow traefik to communication with the Kubernetes API and then create a deployment.
The important part is on the deployment, because we have to configure Traefik to support HTTP/3.
This feature is experimental on Traefik, so we will have to add the following argument: --experimental.http3
in the static configuration.
Once it is activated, we have to enable HTTP/3 on the websecure entrypoint with this argument: --entrypoints.websecure.http3
- Install Traefik
kubectl apply -f traefik/
- Check that traefik is up and running.
kubectl get pods
One he is running we have to find the way to expose it on the internet.
Let's create a Kubernetes service of Type LoadBalancer and add annotations to ask for a NetworkLoadBalancer on AWS. HTTP/3 is running on QUIC, so it means that we need to have a LoadBalancer listening on TCP and UDP on the same port.
---
apiVersion: v1
kind: Service
metadata:
name: traefik
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb" # Ask for a Network Load Balancer on AWS
spec:
type: LoadBalancer
ports:
- protocol: TCP
name: web
port: 80
- protocol: TCP # We have to listen on TCP on port 443
name: websecure-tcp
port: 443
- protocol: UDP # We have to listen on UDP on port 443 for HTTP/3
name: websecure-udp
port: 443
selector:
app: traefik
- Deploy the LoadBalancer service
kubectl apply -f traefik/services/3-service-lb.yaml
The Service "traefik" is invalid: spec.ports: Invalid value: []core.ServicePort{core.ServicePort{Name:"web", Protocol:"TCP", AppProtocol:(*string)(nil), Port:80, TargetPort:intstr.IntOrString{Type:0, IntVal:80, StrVal:""}, NodePort:0}, core.ServicePort{Name:"websecure-tcp", Protocol:"TCP", AppProtocol:(*string)(nil), Port:443, TargetPort:intstr.IntOrString{Type:0, IntVal:443, StrVal:""}, NodePort:0}, core.ServicePort{Name:"websecure-udp", Protocol:"UDP", AppProtocol:(*string)(nil), Port:443, TargetPort:intstr.IntOrString{Type:0, IntVal:443, StrVal:""}, NodePort:0}}: may not contain more than 1 protocol when type is 'LoadBalancer'
As you can see in the ouput, we are not authorized to create a LoadBalancer listing on both TCP and UDP at the same time. It is due to a limitation on the AWS LoadBalancer Controller
As a workarround, we will have to create a service NodePort and creates the NLB manually.
- Deploy the NodePort service
---
apiVersion: v1
kind: Service
metadata:
name: traefik
spec:
type: NodePort
ports:
- protocol: TCP
name: web
targetPort: 80
nodePort: 30442
port: 80
- protocol: TCP
name: websecure-tcp
targetPort: 443
nodePort: 30443
port: 443
- protocol: UDP
name: websecure-udp
targetPort: 443
nodePort: 30443
port: 443
selector:
app: traefik
## Deploy a NodePort service.
kubectl apply -f traefik/services/3-service-node-port.yaml
- Get useful info about the clutser:
## Retrieve the VPC ID.
VPC_ID=$(aws eks describe-cluster --name ${CLUSTER_NAME} --query 'cluster.resourcesVpcConfig.vpcId' --output=text)
## Retrieve the node group ID.
NODE_GROUP_ID=$(aws eks list-nodegroups --cluster-name=${CLUSTER_NAME} --query 'nodegroups[0]' --output text)
## Retrieve instances subnet IDs.
INSTANCES_SUBNET_IDS=$(aws ec2 describe-instances --filters Name=network-interface.vpc-id,Values=$VPC_ID "Name=tag:eks:nodegroup-name,Values=$NODE_GROUP_ID" --query 'Reservations[*].Instances[*].SubnetId' --output text | tr '\n' ' ')
- Create the NLB
## Create the Network Load Balancer with subnets retrieve just before.
aws elbv2 create-load-balancer --name ${CLUSTER_NAME} \
--type network \
--subnets $(echo ${INSTANCES_SUBNET_IDS})
- Create Target group for the NLB
TG_NAME=${CLUSTER_NAME}-tg
## Create a TCP target group for web entrypoint on port 30442.
aws elbv2 create-target-group --name ${TG_NAME}-web --protocol TCP --port 30442 --vpc-id ${VPC_ID} \
--health-check-protocol TCP \
--health-check-port 30442 \
--target-type instance
## Create a TCP_UDP target group for websecure entrypoint on port 30443.
aws elbv2 create-target-group --name ${TG_NAME}-websecure --protocol TCP_UDP --port 30443 --vpc-id ${VPC_ID} \
--health-check-protocol TCP \
--health-check-port 30443 \
--target-type instance
- Register Instances in the target group
## Retrieve instances IDs.
INSTANCES=$(kubectl get nodes -o json | jq -r ".items[].spec.providerID" | cut -d'/' -f5)
IDS=$(for x in `echo ${INSTANCES}`; do echo Id=$x ; done | tr '\n' ' ')
echo $IDS
## Retrieve the target group Arn for the web entrypoint.
TG_ARN_WEB=$(aws elbv2 describe-target-groups --query 'TargetGroups[?TargetGroupName==`traefik-http3-tg-web`].TargetGroupArn' --output text)
## Register instances to the previous TargetGroup web.
aws elbv2 register-targets --target-group-arn ${TG_ARN_WEB} --targets $(echo ${IDS})
## Retrieve the target group Arn for the websecure entrypoint.
TG_ARN_WEBSECURE=$(aws elbv2 describe-target-groups --query 'TargetGroups[?TargetGroupName==`traefik-http3-tg-websecure`].TargetGroupArn' --output text)
## Register instances to the previous TargetGroup websecure.
aws elbv2 register-targets --target-group-arn ${TG_ARN_WEBSECURE} --targets $(echo ${IDS})
- Create listener
## Retrieve the NLB Arn
LB_ARN=$(aws elbv2 describe-load-balancers --names ${CLUSTER_NAME} --query 'LoadBalancers[0].LoadBalancerArn' --output text)
## Create a TCP listener on port 80 that will forward to the TargetGroup traefik-http3-web
aws elbv2 create-listener --load-balancer-arn ${LB_ARN} \
--protocol TCP --port 80 \
--default-actions Type=forward,TargetGroupArn=${TG_ARN_WEB}
## Create a TCP_UDP listener on port 443 that will forward to the TargetGroup traefik-http3-websecure
aws elbv2 create-listener --load-balancer-arn ${LB_ARN} \
--protocol TCP_UDP --port 443 \
--default-actions Type=forward,TargetGroupArn=${TG_ARN_WEBSECURE}
- Configure Instances security groups
# Retrieve the security groups for each instances.
SGs=$(for x in $(echo $INSTANCES); do aws ec2 describe-instances --filters Name=instance-id,Values=$x \
--query 'Reservations[*].Instances[*].SecurityGroups[0].GroupId' --output text ; done | sort | uniq)
for x in $(echo $SGs); do
echo SG=$x;
aws ec2 authorize-security-group-ingress --group-id $x --protocol tcp --port 30442 --cidr 0.0.0.0/0;
aws ec2 authorize-security-group-ingress --group-id $x --protocol tcp --port 30443 --cidr 0.0.0.0/0;
aws ec2 authorize-security-group-ingress --group-id $x --protocol udp --port 30443 --cidr 0.0.0.0/0;
done
## Retrieve the NLB Arn and extract the ID.
NLB_NAME_ID=$(aws elbv2 describe-load-balancers --names ${CLUSTER_NAME} --query 'LoadBalancers[0].LoadBalancerArn' --output text | awk -F":loadbalancer/" '{print $2}')
## Retrieve the NLS DNS name.
NLB_DNS_NAME=$(aws elbv2 describe-load-balancers --names ${CLUSTER_NAME} --query 'LoadBalancers[0].DNSName' --output text)
- Deploy the dashboard IngressRoute
kubectl apply -f traefik/dashboard
- Open the dashboard
open https://${NLB_DNS_NAME}/dashboard/
- Let's deploy a demo application
kubectl apply -f app/
- Access to the application
open https://${NLB_DNS_NAME}/
- Check with http3check
open https://http3check.net/?host=${NLB_DNS_NAME}
## Delete the Network Load Balancer.
aws elbv2 delete-load-balancer --load-balancer-arn ${LB_ARN}
## Delete the 2 target groups created.
aws elbv2 delete-target-group --target-group-arn ${TG_ARN_WEB}
aws elbv2 delete-target-group --target-group-arn ${TG_ARN_WEBSECURE}
## Delete the EKS cluster.
eksctl delete cluster --name ${CLUSTER_NAME} --region ${AWS_REGION}