Skip to content

Commit 9a2f095

Browse files
committed
docs: Add EKS worker steps to Amazon EC2
This change adds instructions and an example butane config for Flatcar nodes that are used with EKS. Fixes: flatcar/Flatcar#730
1 parent a4f9951 commit 9a2f095

1 file changed

Lines changed: 281 additions & 0 deletions

File tree

content/docs/latest/installing/cloud/aws-ec2.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,287 @@ When you make a change to `machine-mynode.yaml.tmpl` and run `terraform apply` a
493493

494494
You 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

Comments
 (0)