# Workshop ## Lab 1 Validate EKS Deployment ```bash kubectl get pods -A ``` Login to ArgoCD Dashboard ```bash export ARGOCD_SERVER=`kubectl get svc argo-cd-argocd-server -n argocd -o json | jq --raw-output '.status.loadBalancer.ingress[0].hostname'` echo "https://$ARGOCD_SERVER" ``` Open a new browser and paste in the url from the previous command. You will now see the ArgoCD UI. :::info Since ArgoCD UI exposed like this is using self-signed certificate, you'll need to accept the security exception in your browser to access it ::: Get the ArgoCD Admin password by executing this command on your terminal ```bash kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ``` **Login to the UI:** 1. The username is admin 2. The password is: the result of the Query for admin password command above. At this step you should be able to see Argo UI In the ArgoUI, you can see that we have severals Applications deployed: * addons * workloads * team-burnham * team-carmen * team-riker So we have 3 Teams application already deployed. This is because we reuse an existing Github repo for the workloads. You can find the repo used and the argo cd configuration in locals.tf and main.tf terraform files. ## Deploy ECSDEMO microservice Applications We will be deploying multiple microservices, much like in the real-world scenarios we face. We will deploy an application by adding 3 new teams in our EKS Blueprint (one team for each microservice), and then leveraging ArgoCD to deploy our services in their appropriate team environment (namespace) in the EKS cluster. :::info We focus here on the Continuous Deployment (CD) part, and not of the building of the application microservices themselves. ::: ![](https://i.imgur.com/ZWAzmPi.png) ### Adding new Teams We want to create dedicated kubernetes manifests in our new teams namespaces. For this for each service we create directory inside the EKS Blueprints folder and create Kubernetes manifest, that will be installed at the namespace creation: * An ArgoCD AppProject definition. It define a new project in argo, with our EKS cluster as destination and authorized to sync with our namespace the Github repositories as the source for the deployments files. * A LimitRange object to dynamically add and control Pod resources. * We uses LimitRange so that we can control the team usage in the namespace, and it will automatically set default values if the application did not provide them Run the below commands on terminal in Cloud9 to create the below manifest files. **ecsdemo-crystal** ```bash= mkdir ecsdemo-crystal cat << EOF > ecsdemo-crystal/crystal-app-project.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: ecsdemo-crystal namespace: argocd spec: destinations: - namespace: ecsdemo-crystal server: https://kubernetes.default.svc sourceRepos: - https://github.com/aws-containers/ecsdemo-crystal.git EOF cat << EOF > ecsdemo-crystal/limit-range.yaml apiVersion: 'v1' kind: 'LimitRange' metadata: name: 'resource-limits' namespace: ecsdemo-crystal spec: limits: - type: 'Container' max: cpu: '2' memory: '1Gi' min: cpu: '100m' memory: '4Mi' default: cpu: '300m' memory: '200Mi' defaultRequest: cpu: '200m' memory: '100Mi' maxLimitRequestRatio: cpu: '10' EOF ``` **ecsdemo-nodejs** ```bash= mkdir ecsdemo-nodejs cat << EOF > ecsdemo-nodejs/nodejs-app-project.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: ecsdemo-nodejs namespace: argocd spec: destinations: - namespace: ecsdemo-nodejs server: https://kubernetes.default.svc sourceRepos: - https://github.com/aws-containers/ecsdemo-nodejs.git EOF cat << EOF > ecsdemo-nodejs/limit-range.yaml apiVersion: 'v1' kind: 'LimitRange' metadata: name: 'resource-limits' namespace: ecsdemo-nodejs spec: limits: - type: 'Container' max: cpu: '2' memory: '1Gi' min: cpu: '100m' memory: '4Mi' default: cpu: '300m' memory: '200Mi' defaultRequest: cpu: '200m' memory: '100Mi' maxLimitRequestRatio: cpu: '10' EOF ``` **ecsdemo-frontend** ```bash= mkdir ecsdemo-frontend cat << EOF > ecsdemo-frontend/frontend-app-project.yaml apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: ecsdemo-frontend namespace: argocd spec: destinations: - namespace: ecsdemo-frontend server: https://kubernetes.default.svc sourceRepos: - https://github.com/aws-containers/ecsdemo-frontend.git EOF cat << EOF > ecsdemo-frontend/limit-range.yaml apiVersion: 'v1' kind: 'LimitRange' metadata: name: 'resource-limits' namespace: ecsdemo-frontend spec: limits: - type: 'Container' max: cpu: '2' memory: '1Gi' min: cpu: '100m' memory: '4Mi' default: cpu: '300m' memory: '200Mi' defaultRequest: cpu: '200m' memory: '100Mi' maxLimitRequestRatio: cpu: '10' EOF ``` ### Deploy new teams Now, we need to create the associated teams within Terraform. In the main.tf file in the eks_blueprints module under application_teams and below team-riker section add the following team definition: ```hcl-terraform= ecsdemo-frontend = { "labels" = { "elbv2.k8s.aws/pod-readiness-gate-inject" = "enabled", "appName" = "ecsdemo-frontend", "projectName" = "ecsdemo", "environment" = "dev", } "quota" = { "requests.cpu" = "10000m", "requests.memory" = "20Gi", "limits.cpu" = "20000m", "limits.memory" = "50Gi", "pods" = "10", "secrets" = "10", "services" = "10" } ## Deploy manifest from specified directory manifests_dir = "./ecsdemo-frontend" users = [data.aws_caller_identity.current.arn] } ecsdemo-crystal = { "labels" = { "appName" = "ecsdemo-crystal", "projectName" = "ecsdemo", "environment" = "dev", } "quota" = { "requests.cpu" = "10000m", "requests.memory" = "20Gi", "limits.cpu" = "20000m", "limits.memory" = "50Gi", "pods" = "10", "secrets" = "10", "services" = "10" } ## Deploy manifest from specified directory manifests_dir = "./ecsdemo-crystal" users = [data.aws_caller_identity.current.arn] } ecsdemo-nodejs = { "labels" = { "appName" = "ecsdemo-nodejs", "projectName" = "ecsdemo", "environment" = "dev" } "quota" = { "requests.cpu" = "10000m", "requests.memory" = "20Gi", "limits.cpu" = "20000m", "limits.memory" = "50Gi", "pods" = "10", "secrets" = "10", "services" = "10" } ## Deploy manifest from specified directory manifests_dir = "./ecsdemo-nodejs" users = [data.aws_caller_identity.current.arn] } ``` Once we add our 3 new teams, we just need to apply this to our cluster ```bash= terraform apply --auto-approve ``` You should see 3 new namespaces ```bash= kubectl get ns | grep ecsdemo ``` ``` ecsdemo-crystal Active 1m ecsdemo-frontend Active 1m ecsdemo-nodejs Active 1m ``` We can also check that we create our 3 new ArgoCD App Projects: ```bash= kubectl get appproject -A ``` ``` NAMESPACE NAME AGE argocd default 14d argocd ecsdemo-crystal 1m argocd ecsdemo-frontend 1m argocd ecsdemo-nodejs 1m ``` And that our LimitRange objects has been correctly created in each of our newly namespaces: ```bash= kubectl get limitrange -A ``` ``` NAMESPACE NAME CREATED AT ecsdemo-crystal resource-limits 2022-07-18T13:48:49Z ecsdemo-frontend resource-limits 2022-07-18T13:48:48Z ecsdemo-nodejs resource-limits 2022-07-18T13:48:49Z ``` ### Configure ECSDEMO ArgoCD deployment repoHeader anchor link Now we are going to let ArgoCD know about our ecsdemo 3 microservices to deploy in each of our team namespaces. Open local.tf and add our workload definition ```hcl-terraform= #--------------------------------------------------------------- # ARGOCD ECSDEMO APPLICATION #--------------------------------------------------------------- ecsdemo_application = { path = "multi-repo/argo-app-of-apps/dev" repo_url = "https://github.com/seb-demo/eks-blueprints-workloads.git" add_on_application = false } ``` Then go to main.tf and add our application in the argocd_applications section. ```hcl-terraform= argocd_applications = { addons = local.addon_application workloads = local.workload_application ecsdemo = local.ecsdemo_application } ``` Apply the Terraform changes: ```bash= terraform apply --auto-approve ``` **Let's walk through the application definition in the eks-blueprint-workload github repository:** You can see the directory we will sync here : https://github.com/seb-demo/eks-blueprints-workloads/tree/main/multi-repo/argo-app-of-apps/dev It has the following structure: ``` multi-repo/argo-app-of-apps/dev/ ├── Chart.yaml ├── templates │ ├── ecsdemo-crystal.yaml │ ├── ecsdemo-frontend.yaml │ └── ecsdemo-nodejs.yaml └── values.yaml ``` The directory structure is in helm chart format and the values.yaml file contains the reference to other repositories for each application that contain the code to deploy the app: https://github.com/seb-demo/eks-blueprints-workloads/blob/main/multi-repo/argo-app-of-apps/dev/values.yaml ```hcl-terraform= spec: destination: server: https://kubernetes.default.svc apps: ecsdemoFrontend: repoURL: https://github.com/aws-containers/ecsdemo-frontend.git targetRevision: main path: kubernetes/helm/ecsdemo-frontend ecsdemoNodejs: repoURL: https://github.com/aws-containers/ecsdemo-nodejs.git targetRevision: main path: kubernetes/helm/ecsdemo-nodejs ecsdemoCrystal: repoURL: https://github.com/aws-containers/ecsdemo-crystal.git targetRevision: main path: kubernetes/helm/ecsdemo-crystal ``` If we open the templates/ecsdemo-frontend.yaml we can see specific values that we want to be injected in our application helm deployment. You can update this file to adapt the deployment of the application to your needs. ```hcl-terraform= apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: ecsdemo-frontend namespace: argocd finalizers: - resources-finalizer.argocd.argoproj.io spec: project: ecsdemo-frontend destination: namespace: ecsdemo-frontend server: {{.Values.spec.destination.server}} source: repoURL: {{.Values.spec.apps.ecsdemoFrontend.repoURL}} targetRevision: {{.Values.spec.apps.ecsdemoFrontend.targetRevision}} path: {{.Values.spec.apps.ecsdemoFrontend.path}} helm: parameters: - name: ingress.enabled value: 'true' - name: ingress.className value: 'alb' - name: ingress.annotations.alb\.ingress\.kubernetes\.io/group\.name value: 'ecsdemo' - name: ingress.annotations.alb\.ingress\.kubernetes\.io/scheme value: 'internet-facing' - name: ingress.annotations.alb\.ingress\.kubernetes\.io/target-type value: 'ip' - name: ingress.annotations.alb\.ingress\.kubernetes\.io/listen-ports value: '[{"HTTP": 80}]' - name: ingress.annotations.alb\.ingress\.kubernetes\.io/tags value: 'Environment=dev,Team=ecsdemo' - name: ingress.hosts[0].host value: '' - name: ingress.hosts[0].paths[0].path value: '/' - name: ingress.hosts[0].paths[0].pathType value: 'Prefix' - name: replicaCount value: '3' - name: image.repository value: 'public.ecr.aws/seb-demo/ecsdemo-frontend' - name: resources.requests.cpu value: '200m' - name: resources.limits.cpu value: '400m' - name: resources.requests.memory value: '256Mi' - name: resources.limits.memory value: '512Mi' syncPolicy: automated: prune: true syncOptions: - CreateNamespace=false # https://github.com/argoproj/argo-cd/issues/7799 ``` :::info In the above example we added resources/limits configuration, and configured the ingress to uses the Application Load Balancer via dedicated annotations. ::: #### Validate deployment The applications should be automatically deployed, or you can go to the ArgoCD UI and check if it needs synchronization. Verify if the Pods are running: ```bash= kubectl get pods -A | grep ecsdemo ``` ``` ecsdemo-crystal ecsdemo-crystal-56dd4d5f4-clxvx 1/1 Running 0 42s ecsdemo-crystal ecsdemo-crystal-56dd4d5f4-fn95g 1/1 Running 0 42s ecsdemo-crystal ecsdemo-crystal-56dd4d5f4-g7c86 1/1 Running 0 42s ecsdemo-frontend ecsdemo-frontend-7b84c6dd54-b66mv 1/1 Running 0 42s ecsdemo-frontend ecsdemo-frontend-7b84c6dd54-czdn4 1/1 Running 0 42s ecsdemo-frontend ecsdemo-frontend-7b84c6dd54-znh74 1/1 Running 0 42s ecsdemo-nodejs ecsdemo-nodejs-669bc64c56-7l5kc 1/1 Running 0 43s ecsdemo-nodejs ecsdemo-nodejs-669bc64c56-7xjk7 1/1 Running 0 43s ecsdemo-nodejs ecsdemo-nodejs-669bc64c56-mfklw ``` And the ecsdemo-frontend app should be exposed through an ALB configure with the Kubernetes ingress ```bash= kubectl get ing -A | grep ecsdemo ``` ``` ecsdemo-frontend ecsdemo-frontend alb * k8s-ecsdemo-f3cf86ec6a-1010682437.eu-west-1.elb.amazonaws.com 80 81s ``` You should be able to see our application as showed in the top of this page connecting to the ALB associated URL. :::spoiler it will take couple of mins for the alb to become active. ::: ------ ------ ------ ## Lab 2 ### Upgrade EKS #### Steps to Upgrade EKS cluster: 1. Change the version in Terraform locals.tf to the desired Kubernetes cluster version. See the example below, ```hcl-terraform cluster_version = "1.22" ``` This will update the EKS Control Plane and Data Plane to Version 1.22 2. Explicitly defining release version in EKS blueprints for Terraform will give more control over managed node upgrades. Since we are upgrading cluster from 1.21 to 1.22, we need to make sure the release version is also updated. To retreive latest version of managed node group release version for the EKS, please run the below AWS CLI command ```bash aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.22/amazon-linux-2/recommended/release_version --region us-west-2 --query "Parameter.Value" --output text ``` Update the release version value in the terraform locals.tf file ```hcl-terraform mng_release_version = "1.22.12-20220824" ``` Apply the changes to the Terraform ```bash= terraform plan terraform apply --auto-approve ``` 3. If you are specifying a version for EKS managed addons, you will need to ensure the version used is compatible with the new cluster version, or use a data source to pull the appropriate version. First list latest supported add-on version ```bash= aws eks describe-addon-versions --addon-name vpc-cni --kubernetes-version 1.22 --query addons[*].addonVersions[*].addonVersion aws eks describe-addon-versions --addon-name coredns --kubernetes-version 1.22 --query addons[*].addonVersions[*].addonVersion aws eks describe-addon-versions --addon-name kube-proxy --kubernetes-version 1.22 --query addons[*].addonVersions[*].addonVersion aws eks describe-addon-versions --addon-name aws-ebs-csi-driver --kubernetes-version 1.22 --query addons[*].addonVersions[*].addonVersion ``` Update Add-on versions in the locals.tf file ```hcl-terraform eks_vpc_cni_version = "v1.11.3-eksbuild.1" coredns_version = "v1.8.7-eksbuild.1" kube_proxy_version = "v1.22.11-eksbuild.2" ebs_csi_driver_version = "v1.10.0-eksbuild.1" ``` ```bash= terraform plan terraform apply --auto-approve ``` Doing this will: - Upgrade the Control Plane to the version specified - Update the Data Plane to ensure the compute resources are utilizing the corresponding AMI for the given cluster version - Update addons to reflect the respective versions ## Important Note Please note that you may need to update other Kubernetes Addons deployed through Helm Charts to match with new Kubernetes upgrade version