@@ -493,6 +493,287 @@ When you make a change to `machine-mynode.yaml.tmpl` and run `terraform apply` a
493493
494494You can find this Terraform module in the repository for [ Flatcar Terraform examples] ( https://github.com/flatcar/flatcar-terraform/tree/main/aws ) .
495495
496+ ## Amazon EKS
497+
498+ To use Flatcar Container Linux with Amazon Elastic Kubernetes Service (EKS), several items must be in your Butane configuration:
499+
500+ * The Amazon EKS Kubelet binary
501+ * The CA Certificate of your EKS cluster (used for cluster authentication)
502+ * The awscli tool
503+ * The ECR credential provider (required to pull EKS-managed images like kube-proxy)
504+
505+ Flatcar Container Linux nodes will also need everything any EKS [ self-managed node requires] ( https://docs.aws.amazon.com/eks/latest/userguide/worker.html ) : the correct IAM roles, proper security group connectivity, proper tags, etc.
506+
507+ A complete Butane example for a Flatcar Container Linux EKS node is provided below:
508+
509+ ``` yaml
510+ variant : flatcar
511+ version : 1.0.0
512+
513+ storage :
514+ directories :
515+ - path : /opt/bin
516+ mode : 0755
517+ - path : /etc/kubernetes/pki
518+ mode : 0700
519+ - path : /var/lib/kubelet
520+ mode : 0700
521+
522+ files :
523+ # ---- Kernel parameters required by kubelet ----
524+ - path : /etc/sysctl.d/90-kubernetes.conf
525+ mode : 0644
526+ contents :
527+ inline : |
528+ vm.overcommit_memory = 1
529+ kernel.panic = 10
530+
531+ # ---- Install AWS CLI v2 ----
532+ - path : /opt/bin/install-deps.sh
533+ mode : 0755
534+ contents :
535+ inline : |
536+ #!/bin/bash
537+ set -euo pipefail
538+
539+ # AWS CLI v2
540+ if [ ! -f /opt/bin/aws ]; then
541+ curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" \
542+ -o /tmp/awscliv2.zip
543+ unzip -q /tmp/awscliv2.zip -d /tmp/
544+ /tmp/aws/install --install-dir /opt/aws/cli --bin-dir /opt/bin
545+ rm -rf /tmp/aws /tmp/awscliv2.zip
546+ fi
547+
548+ # EKS-patched kubelet (must use EKS build, not upstream, for v1beta1 exec credential support)
549+ if [ ! -f /opt/bin/kubelet ]; then
550+ curl -fsSL \
551+ "https://s3.us-west-2.amazonaws.com/amazon-eks/${K8S_VERSION}/${eks_release_date}/bin/linux/amd64/kubelet" \
552+ -o /opt/bin/kubelet
553+ chmod +x /opt/bin/kubelet
554+ fi
555+
556+ # ECR credential provider (required to pull EKS-managed images like kube-proxy)
557+ if [ ! -f /opt/bin/ecr-credential-provider ]; then
558+ curl -fsSL \
559+ "https://s3.us-west-2.amazonaws.com/amazon-eks/${K8S_VERSION}/${eks_release_date}/bin/linux/amd64/ecr-credential-provider" \
560+ -o /opt/bin/ecr-credential-provider
561+ chmod +x /opt/bin/ecr-credential-provider
562+ fi
563+
564+ # ---- ECR credential provider config (matches AL2023 nodeadm format) ----
565+ - path : /etc/eks/image-credential-provider/config.json
566+ mode : 0644
567+ contents :
568+ inline : |
569+ {
570+ "apiVersion": "kubelet.config.k8s.io/v1",
571+ "kind": "CredentialProviderConfig",
572+ "providers": [
573+ {
574+ "name": "ecr-credential-provider",
575+ "matchImages": [
576+ "*.dkr.ecr.*.amazonaws.com",
577+ "*.dkr.ecr.*.amazonaws.com.cn",
578+ "*.dkr.ecr-fips.*.amazonaws.com",
579+ "*.dkr.ecr.us-iso-east-1.c2s.ic.gov",
580+ "*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov"
581+ ],
582+ "defaultCacheDuration": "12h",
583+ "apiVersion": "credentialprovider.kubelet.k8s.io/v1"
584+ }
585+ ]
586+ }
587+
588+ # ---- Bootstrap: writes kubeconfig + kubelet-config ----
589+ - path : /opt/bin/bootstrap-eks.sh
590+ mode : 0755
591+ contents :
592+ inline : |
593+ #!/bin/bash
594+ set -euo pipefail
595+
596+ # IMDSv2
597+ TOKEN=$(curl -sf -X PUT "http://169.254.169.254/latest/api/token" \
598+ -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
599+
600+ # Node hostname (EKS uses private DNS as node name)
601+ PRIVATE_DNS=$(curl -sf \
602+ -H "X-aws-ec2-metadata-token: $TOKEN" \
603+ http://169.254.169.254/latest/meta-data/hostname)
604+
605+ # AZ + instance ID for provider-id (required for EKS cloud controller
606+ # to initialize the node and remove the 'uninitialized' taint)
607+ AZ=$(curl -sf \
608+ -H "X-aws-ec2-metadata-token: $TOKEN" \
609+ http://169.254.169.254/latest/meta-data/placement/availability-zone)
610+ INSTANCE_ID=$(curl -sf \
611+ -H "X-aws-ec2-metadata-token: $TOKEN" \
612+ http://169.254.169.254/latest/meta-data/instance-id)
613+
614+ # Kubeconfig with exec-based auth
615+ cat > /etc/kubernetes/kubeconfig <<EOF
616+ apiVersion: v1
617+ kind: Config
618+ clusters:
619+ - cluster:
620+ certificate-authority: /etc/kubernetes/pki/ca.crt
621+ server: ${cluster_endpoint}
622+ name: kubernetes
623+ contexts:
624+ - context:
625+ cluster: kubernetes
626+ user: kubelet
627+ name: kubelet
628+ current-context: kubelet
629+ users:
630+ - name: kubelet
631+ user:
632+ exec:
633+ apiVersion: client.authentication.k8s.io/v1beta1
634+ command: /opt/bin/aws
635+ args:
636+ - eks
637+ - get-token
638+ - --cluster-name
639+ - ${cluster_name}
640+ - --region
641+ - ${aws_region}
642+ EOF
643+ chmod 600 /etc/kubernetes/kubeconfig
644+
645+ # Decode cluster CA for kubelet TLS verification
646+ echo "${cluster_ca}" | base64 -d > /etc/kubernetes/pki/ca.crt
647+
648+ # kubelet configuration
649+ cat > /etc/kubernetes/kubelet-config.yaml <<EOF
650+ apiVersion: kubelet.config.k8s.io/v1beta1
651+ kind: KubeletConfiguration
652+ address: 0.0.0.0
653+ authentication:
654+ anonymous:
655+ enabled: false
656+ webhook:
657+ enabled: true
658+ cacheTTL: 2m
659+ x509:
660+ clientCAFile: /etc/kubernetes/pki/ca.crt
661+ authorization:
662+ mode: Webhook
663+ clusterDNS:
664+ - 172.20.0.10
665+ clusterDomain: cluster.local
666+ cgroupDriver: systemd
667+ containerRuntimeEndpoint: unix:///run/containerd/containerd.sock
668+ featureGates:
669+ RotateKubeletServerCertificate: true
670+ protectKernelDefaults: true
671+ serializeImagePulls: false
672+ serverTLSBootstrap: true
673+ EOF
674+
675+ cat > /etc/kubernetes/node-hostname <<ENVEOF
676+ NODE_HOSTNAME=$PRIVATE_DNS
677+ PROVIDER_ID=aws:///$AZ/$INSTANCE_ID
678+ ENVEOF
679+
680+ # ---- kubelet wrapper: sources node-hostname env file at runtime ----
681+ - path : /opt/bin/start-kubelet.sh
682+ mode : 0755
683+ contents :
684+ inline : |
685+ #!/bin/bash
686+ set -euo pipefail
687+ . /etc/kubernetes/node-hostname
688+ exec /opt/bin/kubelet \
689+ --config=/etc/kubernetes/kubelet-config.yaml \
690+ --kubeconfig=/etc/kubernetes/kubeconfig \
691+ --cloud-provider=external \
692+ --provider-id="$PROVIDER_ID" \
693+ --hostname-override="$NODE_HOSTNAME" \
694+ --image-credential-provider-bin-dir=/opt/bin \
695+ --image-credential-provider-config=/etc/eks/image-credential-provider/config.json \
696+ --node-labels=eks.amazonaws.com/nodegroup=${nodegroup_name},eks.amazonaws.com/compute-type=ec2 \
697+ --v=2
698+
699+ # ---- containerd: configure pause image + systemd cgroup ----
700+ - path : /etc/containerd/config.toml
701+ overwrite : true
702+ contents :
703+ inline : |
704+ version = 2
705+
706+ [plugins."io.containerd.grpc.v1.cri"]
707+ sandbox_image = "${pause_image}"
708+
709+ [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
710+ runtime_type = "io.containerd.runc.v2"
711+
712+ [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
713+ SystemdCgroup = true
714+
715+ systemd :
716+ units :
717+ # Pin version — disable auto-update and locksmith reboot coordinator
718+ - name : update-engine.service
719+ mask : true
720+ - name : locksmithd.service
721+ mask : true
722+
723+ - name : install-deps.service
724+ enabled : true
725+ contents : |
726+ [Unit]
727+ Description=Install AWS CLI and kubelet
728+ After=network-online.target
729+ Wants=network-online.target
730+ ConditionPathExists=!/opt/bin/kubelet
731+
732+ [Service]
733+ Type=oneshot
734+ RemainAfterExit=yes
735+ Environment=K8S_VERSION=${K8S_VERSION}
736+ ExecStart=/opt/bin/install-deps.sh
737+
738+ [Install]
739+ WantedBy=multi-user.target
740+
741+ - name : bootstrap-eks.service
742+ enabled : true
743+ contents : |
744+ [Unit]
745+ Description=Bootstrap EKS node
746+ After=install-deps.service network-online.target
747+ Requires=install-deps.service
748+ ConditionPathExists=!/etc/kubernetes/kubeconfig
749+
750+ [Service]
751+ Type=oneshot
752+ RemainAfterExit=yes
753+ ExecStart=/opt/bin/bootstrap-eks.sh
754+
755+ [Install]
756+ WantedBy=multi-user.target
757+
758+ - name : kubelet.service
759+ enabled : true
760+ contents : |
761+ [Unit]
762+ Description=Kubernetes Kubelet
763+ After=bootstrap-eks.service containerd.service
764+ Requires=containerd.service
765+ Wants=bootstrap-eks.service
766+
767+ [Service]
768+ Restart=always
769+ RestartSec=5
770+ ExecStart=/opt/bin/start-kubelet.sh
771+ ExecStartPre=/bin/bash -c "until [ -f /etc/kubernetes/kubeconfig ] && [ -f /etc/kubernetes/node-hostname ]; do sleep 2; done"
772+
773+ [Install]
774+ WantedBy=multi-user.target
775+ ` ` `
776+
496777[quickstart]: ../
497778[doc-index]: ../../
498779[flatcar-user]: https://groups.google.com/forum/#!forum/flatcar-linux-user
0 commit comments