diff --git a/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_index.md b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_index.md new file mode 100644 index 000000000..1a15fc51c --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_index.md @@ -0,0 +1,39 @@ +--- +title: Learn how to build and deploy a multi-architecture application on Amazon EKS + +minutes_to_complete: 60 + +who_is_this_for: This is an advanced topic for software developers who are looking to understand how to build and deploy a multi-architecture application with x86/amd64 and arm64 based container images on Amazon EKS + +learning_objectives: + - Build x86/amd64 and arm64 container images with docker buildx and docker manifest + - Understand the nuances of building a multi-architecture container image + - Learn how to add taints and tolerations to Amazon EKS clusters to schedule application pods on architecture specific nodes + - Deploy a multi-arch container application across multiple architectures in a single Amazon EKS cluster + +prerequisites: + - An [AWS account](https://aws.amazon.com/). Create an account if needed. + - A computer with [Amazon eksctl CLI](/install-guides/eksctl) and [kubectl](/install-guides/kubectl/)installed. + - Docker installed on local computer [Docker](/install-guides/docker) + +author_primary: Pranay Bakre + +### Tags +skilllevels: Advanced +subjects: Containers and Virtualization +armips: + - Neoverse + +tools_software_languages: + - Kubernetes + - AWS +operatingsystems: + - Linux + + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_next-steps.md b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_next-steps.md new file mode 100644 index 000000000..9b4dd5b8d --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_next-steps.md @@ -0,0 +1,24 @@ +--- +next_step_guidance: We recommend you to continue learning about deploying multi-architecture applications. + +recommended_path: "/learning-paths/servers-and-cloud-computing/migration" + +further_reading: + - resource: + title: EKS documentation + link: https://aws.amazon.com/eks/ + type: documentation + - resource: + title: Amazon Elastic Container Registry + link: https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html?pg=ln&sec=hs + type: documentation + + + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +weight: 21 # set to always be larger than the content in this path, and one more than 'review' +title: "Next Steps" # Always the same +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_review.md b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_review.md new file mode 100644 index 000000000..c63ded344 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/_review.md @@ -0,0 +1,30 @@ +--- +review: + - questions: + question: > + Taints and tolerations ensure that pods are scheduled on correct nodes. + answers: + - "True" + - "False" + correct_answer: 1 + explanation: > + Taints and tolerations work together to make sure that application pods are not scheduled on wrong architecture nodes. + + - questions: + question: > + You can't create an Amazon EKS cluster with both x86/amd64 and arm64 nodes. + answers: + - "True" + - "False" + correct_answer: 2 + explanation: > + Amazon EKS supports hybrid clusters with both x86/amd64 and arm64 nodes. + + +# ================================================================================ +# FIXED, DO NOT MODIFY +# ================================================================================ +title: "Review" # Always the same title +weight: 20 # Set to always be larger than the content in this path +layout: "learningpathall" # All files under learning paths have this same wrapper +--- diff --git a/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/go-multi-arch-eks.md b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/go-multi-arch-eks.md new file mode 100644 index 000000000..519c723f6 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/eks-multi-arch/go-multi-arch-eks.md @@ -0,0 +1,403 @@ +--- +title: Build and deploy a multi-arch application on Amazon EKS +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Multi-architecture Amazon EKS cluster with x86 and Arm-based (Graviton) nodes + +A multi-architecture Kubernetes cluster runs workloads on multiple hardware architectures, typically arm64 and amd64. To learn more about multi-architecture Kubernetes you can create a hybrid cluster in Amazon EKS and gain some practical experience with arm64 and amd64 nodes. This will also help you understand multi-architecture container images. + +## Before you begin + +You will need an [AWS account](https://aws.amazon.com/). Create an account if needed. + +Three tools are required on your local machine. Follow the links to install the required tools. + +* [Kubectl](/install-guides/kubectl/) +* [Amazon eksctl CLI](/install-guides/eksctl) +* [Docker](/install-guides/docker) + +## Create a Multi-architecture Amazon EKS Cluster + +Use `eksctl` to create a multi-architecture Amazon EKS cluster. Create a file named `cluster.yaml` with the contents below using a file editor of your choice. + +```yaml +apiVersion: eksctl.io/v1alpha5 +kind: ClusterConfig + +metadata: + name: multi-arch-cluster + region: us-east-1 + +nodeGroups: + - name: x86-node-group + instanceType: m5.large + desiredCapacity: 2 + volumeSize: 80 + - name: arm64-node-group + instanceType: m6g.large + desiredCapacity: 2 + volumeSize: 80 +``` + +Run the `eksctl` command to create the EKS cluster: + +```console +eksctl create cluster -f cluster.yaml +``` +This command will create a cluster that has 2 x86/amd64 nodes and 2 arm64 nodes. When the cluster is ready, use the following command to check the nodes: + +```console +kubectl get nodes +``` +The output should look similar to: + +```output +NAME STATUS ROLES AGE VERSION +ip-172-31-10-206.eu-west-1.compute.internal Ready 9m56s v1.28.1-eks-43840fb +ip-172-31-16-133.eu-west-1.compute.internal Ready 9m59s v1.28.1-eks-43840fb +ip-172-31-19-140.eu-west-1.compute.internal Ready 8m32s v1.28.1-eks-43840fb +ip-172-31-40-45.eu-west-1.compute.internal Ready 8m32s v1.28.1-eks-43840fb +``` +To check the architecture of the nodes, execute the following command: + +```console +kubectl get node -o jsonpath='{.items[*].status.nodeInfo.architecture}' +``` +The output should show two architectures for four nodes: + +```output +arm64 amd64 amd64 arm64 +``` + +## Multi-architecture containers + +Multi-architecture container images are the easiest way to deploy applications and hide the underlying hardware architecture. Building multi-architecture images is slightly more complex compared to building single-architecture images. +Docker provides two ways to create multi-architecture images: + * docker buildx - builds both architectures at the same time. + * docker manifest - builds each architecture separately and joins them together into a multi-architecture image. + +Shown below is a simple `Go` application you can use to learn about multi-architecture Kubernetes clusters. Create a file named `hello.go` with the contents below: + +```console +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "runtime" +) + +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello from image NODE:%s, POD:%s, CPU PLATFORM:%s/%s", + os.Getenv("NODE_NAME"), os.Getenv("POD_NAME"), runtime.GOOS, runtime.GOARCH) +} + +func main() { + http.HandleFunc("/", handler) + log.Fatal(http.ListenAndServe(":8080", nil)) +} +``` +Create another file named `go.mod` with the following content: + +```console +module example.com/arm +go 1.21 +``` + +Create a Dockerfile with the following content: + +```console +ARG T + +# +# Build: 1st stage +# +FROM golang:1.21-alpine as builder +ARG TARCH +WORKDIR /app +COPY go.mod . +COPY hello.go . +RUN GOARCH=${TARCH} go build -o /hello && \ + apk add --update --no-cache file && \ + file /hello + +# +# Release: 2nd stage +# +FROM ${T}alpine +WORKDIR / +COPY --from=builder /hello /hello +RUN apk add --update --no-cache file +CMD [ "/hello" ] +``` + +## Build multi-architecture docker images with docker buildx + +With these files you can build your docker image. Login to Amazon ECR and create a repository named `multi-arch-app`. + +Run the following command to build and push the docker image to the repository: + +```console +docker buildx create --name multiarch --use --bootstrap +docker buildx build -t /multi-arch:latest --platform linux/amd64,linux/arm64 --push . +``` +Replace `` in the command above to the location of your repository. + +You should now see the docker image in your repository. + +## Build multi-architecture docker images with Docker manifest + +You can also use docker manifest to create a multi-architecture image from two single-architecture images. This is an alternative way to to build the multi-architecture image. +Create another repository in Amazon ECR with the name `multi-arch-demo`. Use the following command to build an amd64 image: + +```console +docker build build -t /multi-arch-demo:amd64 --build-arg TARCH=amd64 --build-arg T=amd64/ . +docker push /multi-arch-demo:amd64 +``` +Replace `` in the command above to the location of your repository. + +Build an arm64 image by executing the following commands on an arm64 machine: +```console +docker build build -t /multi-arch-demo:arm64 --build-arg TARCH=amd64 --build-arg T=amd64v8/ . +docker push /multi-arch-demo:arm64 +``` +Again, replace `` in the commands above to the location of your repository. + +After building individual containers for each architecture, merge them into a single image by running the commands below on either architecture: + +```console +docker manifest create /multi-arch-demo:latest \ +--amend /multi-arch-demo:arm64 \ +--amend /multi-arch-demo:amd64 +docker manifest push --purge /multi-arch-demo:latest +``` + +You should see three images in the ECR repository - one for each architecture (amd64 and arm64) and a combined multi-architecture image. + +## Deploy Kubernetes service in EKS cluster + +You can now create a service to deploy the application. Create a file named `hello-service.yaml` with the following contents: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: hello-service + labels: + app: hello + tier: web +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 8080 + selector: + app: hello + tier: web +``` + +Deploy the service. Run the following command: + +```console +kubectl apply -f hello-service.yaml +``` + +## Deploy amd64 application + +Create a text file named `amd64-deployment.yaml` with the contents below. The amd64 image will only run on amd64 nodes. The nodeSelector is used to make sure the container is only scheduled on amd64 nodes. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: amd-deployment + labels: + app: hello +spec: + replicas: 1 + selector: + matchLabels: + app: hello + tier: web + template: + metadata: + labels: + app: hello + tier: web + spec: + containers: + - name: hello + image: /multi-arch-demo:amd64 + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + resources: + requests: + cpu: 300m + nodeSelector: + kubernetes.io/arch: amd64 + +``` + +Use the following command to deploy the application: + +```console +kubectl apply -f amd64-deployment.yaml +``` +The output should show a single pod running. + +Get the external IP assigned to the service you deployed earlier, by executing the following command: + +```console +kubectl get svc +``` +Use the `external-ip` from the command output and execute the following command. This IP belongs to the Load Balancer provisioned in your cluster. + +```console +curl -w '\n' http:// +``` +You should see output similar to what is shown below: + +```output +Hello from image NODE:ip-192-168-32-244.ec2.internal, POD:amd-deployment-7d4d44889d-vzhpd, CPU PLATFORM:linux/amd64 +``` + +## Deploy arm64 application + +Create a text file named `arm64-deployment.yaml` with the contents below. Notice that the value of `nodeSelector` is now arm64. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: arm-deployment + labels: + app: hello +spec: + replicas: 1 + selector: + matchLabels: + app: hello + tier: web + template: + metadata: + labels: + app: hello + tier: web + spec: + containers: + - name: hello + image: /multi-arch-demo:arm64 + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + resources: + requests: + cpu: 300m + nodeSelector: + kubernetes.io/arch: arm64 +``` + +Deploy the arm64 application by using the command below: + +```console +kubectl apply -f arm64-deployment.yaml +``` + +Execute the following command to check the running pods + +```console +kubectl get pods +``` +You should see two pods running in the cluster. One for amd64 and another one for arm64. + +Execute the curl command a few times to see output from both the pods. You should see responses from both the arm64 and amd64 pods. + +```console +curl -w '\n' http:// +``` + +## Deploy multi-architecture application in EKS cluster + +You can now deploy the multi-architecture version of the application in our EKS cluster. Create a text file named `multi-arch-deployment.yaml` with the contents below. The image is the multi-architecture image created with docker buildx and 6 replicas are specified. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: multi-arch-deployment + labels: + app: hello +spec: + replicas: 6 + selector: + matchLabels: + app: hello + tier: web + template: + metadata: + labels: + app: hello + tier: web + spec: + containers: + - name: hello + image: /multi-arch:latest + imagePullPolicy: Always + ports: + - containerPort: 8080 + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + resources: + requests: + cpu: 300m +``` +Deploy the multi-architecture application by using the command below: + +```console +kubectl apply -f multi-arch-deployment.yaml +``` +Execute the following command to check the running pods: + +```console +kubectl get pods +``` +The output should show all the pods from three deployments. To test the application, run the following command to check messages from all three versions of the application: + +```console +for i in $(seq 1 10); do curl -w '\n' http://; done +``` +The output will show a variety of arm64 and amd64 messages. + +You have now deployed an x86/amd64, arm64 and multi-architecture version of the same application in a single Amazon EKS cluster. Leverage these techniques to incrementally migrate your existing x86/amd64 based applications to arm64 in AWS.