A simple CICD pipeline for EKS using codepipeline.
Tags: kubernetes, eks, codepipeline, tutorial
Published: 2022-02-05 15:42:21


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7anq7e2jwdfl6u260rcw.jpg)

A developer pushes a commit to their repository. This can be an application change or a change to the kubernetes infrastructure eg the deployment. The change triggers the pipeline and codebuild builds a new container image and pushes it to ECR. We then run kubectl apply -f app.yaml , app.yaml being the yaml containing the kubernetes infrastructure. This command is run by codebuild and it triggers an update in kubernetes.

The code for this tutorial is on [github](https://github.com/leroykayanda/k8s/tree/master/cicd-test).


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brgxwpdafd1u9vb32914.PNG)

**src**: contains a simple html file, which is the application in this demo.
**Dockerfile**: takes the contents of src folder and builds a docker image.
**app.yaml**: contains the kubernetes infrastructure i.e deployment, service, ingress
**buildspec.yml**: this is used by codebuild to build the image and push it to ECR. It is the brains of the entire pipeline.

## buildspec.yml

We define a couple of environment variables in codebuild.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n9nf6w3qha67lnhepmid.png)

We define the URI for the ECR repository, the region we are working in and the EKS cluster name. Eventually, we need to run kubectl apply -f app.yaml to apply changes to the cluster. We thus need an IAM role (EKS_KUBECTL_ROLE_ARN) that is authorized to make changes to the cluster.

This is the policy attached to that role.

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "eks:Describe*",
            "Resource": "*"
        }
    ]
}
```

You will need to [add this role](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html) to your EKS cluster to authorize it to make changes.

I was getting throttling errors when pulling the image php:7.3-apache from docker hub. That is why I added DOCKERHUB_USERNAME and DOCKERHUB_PASSWORD as per [this tutorial](https://aws.amazon.com/premiumsupport/knowledge-center/codebuild-docker-pull-image-error/) to get around this.


```
version: 0.2

  install:
    commands:
      - echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
      - echo "Installing jq, awscli and kubectl"
      - apt-get update && apt-get -y install jq
      - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
      - unzip awscliv2.zip
      - ./aws/install
      - export PATH="$PATH:/usr/local/bin"
      - curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.21.2/2021-07-05/bin/linux/amd64/kubectl
      - chmod +x ./kubectl
      - mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$PATH:$HOME/bin
      - export PATH="$PATH:$HOME/bin"
```

Here, we install jq to help us manipulate json on linux later. We also install the aws cli as well as kubectl.

```
  pre_build:
    commands:
      - echo "Setting image in deployment and log in to ECR"
      - TAG="$(date +%Y-%m-%d.%H.%M.%S)-$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 5)"
      - sed -i 's@CONTAINER_IMAGE@'"$REPOSITORY_URI:$TAG"'@' app.yaml
      - export KUBECONFIG=$HOME/.kube/config
```

We come up with a container image tag based on the commit id that triggered the pipeline and the date. In app.yaml in the deployment section, we have defined the image as CONTAINER_IMAGE. We use sed to replace this with an image name and the tag we have just come up with.

```
build:
    commands:
      - echo "building and tagging image"
      - docker build -t $REPOSITORY_URI .
      - docker tag $REPOSITORY_URI $REPOSITORY_URI:$TAG
```
We build and tag the image.

```
post_build:
    commands:
      - echo "post_build"
      - aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $REPOSITORY_URI
      - docker push $REPOSITORY_URI:$TAG
      - CREDENTIALS=$(aws sts assume-role --role-arn $EKS_KUBECTL_ROLE_ARN --role-session-name codebuild-kubectl --duration-seconds 900)
      - export AWS_ACCESS_KEY_ID="$(echo ${CREDENTIALS} | jq -r '.Credentials.AccessKeyId')"
      - export AWS_SECRET_ACCESS_KEY="$(echo ${CREDENTIALS} | jq -r '.Credentials.SecretAccessKey')"
      - export AWS_SESSION_TOKEN="$(echo ${CREDENTIALS} | jq -r '.Credentials.SessionToken')"
      - export AWS_EXPIRATION=$(echo ${CREDENTIALS} | jq -r '.Credentials.Expiration')
      - aws eks update-kubeconfig --name $EKS_CLUSTER_NAME
      - kubectl apply -f app.yaml
```
We log in to ECR and push the image. We assume the IAM role we created earlier. We use the AWS update-kubeconfig API call to automatically create the kubeconfig file. We finally run `kubectl apply -f app.yaml` and our cluster is updated.