Skip to content

Commit

Permalink
Day 4 deployed frontend app in K8s
Browse files Browse the repository at this point in the history
  • Loading branch information
lucabrunox committed Sep 24, 2024
1 parent 5216dfe commit 29446ea
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 12 deletions.
19 changes: 11 additions & 8 deletions .github/workflows/frontend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ jobs:
steps:
- uses: actions/checkout@v3

- name: Build
shell: bash
working-directory: frontend
run: |
docker build --rm . -t frontend
docker save -o /tmp/frontend_image.tar frontend
- uses: docker/setup-qemu-action@v3

- uses: docker/setup-buildx-action@v3

- uses: docker/build-push-action@v6
with:
context: frontend
platforms: linux/arm64
tags: frontend:latest
outputs: type=docker,dest=/tmp/frontend_image.tar

- uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -50,9 +54,8 @@ jobs:

- name: Push to ECR
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/learning-frontend
IMAGE_TAG: ${{ github.sha }}
IMAGE_TAG: v${{ github.sha }}
run: |
docker load --input /tmp/frontend_image.tar
docker image tag frontend $REPOSITORY:$IMAGE_TAG
Expand Down
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This repo contains step-by-step creationg of low-level Terraform + K8s + other s

### Day 1: Set up Terraform with a remote backend

Commit: https://github.com/lucabrunox/learning/tree/cd8378154c378

Define the AWS region that will be used for all the commands:

```bash
Expand Down Expand Up @@ -72,9 +74,9 @@ If the cluster is not up check the instance init logs:
sudo cat /var/log/cloud-init-output.log
```

### Day 3: A Django frontend with GH action to build a Docker image, not deployed yet
### Day 3: A Django frontend with GH action to build a Docker image and push to ECR

Commit: https://github.com/lucabrunox/learning/tree/f7b44d852c7c
Commit: https://github.com/lucabrunox/learning/tree/5216dfe5efd6

Set up following https://docs.djangoproject.com/en/5.1/intro/tutorial01/ with `django-admin startproject mysite`.

Expand All @@ -94,4 +96,27 @@ To test GH actions I've set up act to run in a Docker, so that it doesn't need t

Which in turn creates the frontend Docker, yay!

The GH also contains a job to push to ECR, which is not tested locally.
The GH also contains a job to push to ECR, which is not tested locally.

### Day 4: Deploy the Django app in K8s using the ECR image

Commit: https://github.com/lucabrunox/learning/tree/e296b828cb5

Needless to say that without EKS it's more complicated, but worth the learnings.

Learnings:
- Using CronJob to get AWS creds from the node, login to ECR, and store the secret for pulling images.
- CronJob doesn't start immediately, need to wait a minute.
- Need to untaint control plane node in other to schedule pods.
- Need to build the frontend image for ARM, obviously.
- Python app fails because it can't find the UTC timezone, needs tzdata.
- Cannot change the matching labels of a K8s deployment.

At the end we're able to execute the following kubectl on the EC2 instance to deploy the app and watch it working:

```bash
kubectl apply -f k8s/ecr-credentials.yaml
kubectl apply -f frontend/k8s/manifest.yaml

curl $(kubectl get svc frontend -o=jsonpath='{.spec.clusterIP}'):8000
```
38 changes: 38 additions & 0 deletions frontend/k8s/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app.kubernetes.io/name: frontend
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: frontend
template:
metadata:
labels:
app.kubernetes.io/name: frontend
spec:
imagePullSecrets:
- name: ecrsecret
containers:
- name: frontend
image: MY_ACCOUNT.dkr.ecr.eu-west-1.amazonaws.com/learning-frontend:ve296b828cb5109a608881efa9fe5bf208d682bbc
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
app.kubernetes.io/name: frontend
spec:
selector:
app.kubernetes.io/name: frontend
ports:
- protocol: TCP
port: 8000
targetPort: 8000
1 change: 1 addition & 0 deletions frontend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Django==4.2.16
tzdata
71 changes: 71 additions & 0 deletions k8s/ecr-credentials.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Cron job to update ECR credentials

apiVersion: v1
kind: ServiceAccount
metadata:
name: ecr-credentials-helper-sa
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: ecr-credentials-helper-role
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["ecrsecret"] # Replace with your desired ECR token secret name
verbs: ["delete"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: ecr-credentials-helper-binding
namespace: default
subjects:
- kind: ServiceAccount
name: ecr-credentials-helper-sa
namespace: default
apiGroup: ""
roleRef:
kind: Role
name: ecr-credentials-helper-role
apiGroup: ""
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: ecr-credentials-helper
namespace: default
spec:
schedule: "* * * * *"
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 2
suspend: false
jobTemplate:
spec:
template:
spec:
hostNetwork: true
serviceAccountName: ecr-credentials-helper-sa
containers:
- name: ecr-credentials-helper
image: public.ecr.aws/aws-cli/aws-cli:2.17.58
imagePullPolicy: IfNotPresent
command:
- /bin/bash
- -c
- |-
set -e
export AWS_DEFAULT_REGION=$(curl http://169.254.169.254/latest/meta-data/placement/region)
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
ECR_TOKEN="$(aws ecr get-login-password)"
curl -LO https://dl.k8s.io/release/v1.29.0/bin/linux/arm64/kubectl
chmod +x kubectl
mv kubectl /usr/bin/
kubectl delete secret --ignore-not-found ecrsecret -n default
kubectl create secret docker-registry ecrsecret --docker-server=https://${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com --docker-username=AWS --docker-password=${ECR_TOKEN} --namespace=default
restartPolicy: Never
2 changes: 1 addition & 1 deletion test_gh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if [ -z "$TMPDIR" ]; then
exit 1
fi

docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock -v .:/workdir -w /workdir --rm $(docker build --rm -q act) act --artifact-server-path "$TMPDIR/" -P ubuntu-latest=catthehacker/ubuntu:act-latest --rm push -j build
docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock -v .:/workdir -w /workdir --network host --rm $(docker build --rm -q act) act --artifact-server-path "$TMPDIR/" -P ubuntu-latest=catthehacker/ubuntu:act-latest --rm push -j build

echo "Deleting temp directory: $TMPDIR"
rm -rf "$TMPDIR"
16 changes: 16 additions & 0 deletions tf/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@ resource "aws_iam_role_policy" "learning_ec2_inline_policy" {
aws_s3_bucket.learning_s3_user_data.arn,
"${aws_s3_bucket.learning_s3_user_data.arn}/*"
]
},
{
Action = [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
Effect = "Allow"
Resource = [
module.learning_ecr_frontend.repository_arn
]
},
{
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
}
]
})
Expand Down Expand Up @@ -219,6 +234,7 @@ module "learning_ecr_frontend" {
description = "Keep only the last 3 images",
selection = {
tagStatus = "tagged",
tagPrefixList = ["v"],
countType = "imageCountMoreThan",
countNumber = 3
},
Expand Down
1 change: 1 addition & 0 deletions tf/user_data/kubeadm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ apiVersion: "kubeadm.k8s.io/v1beta3"
kind: InitConfiguration
nodeRegistration:
criSocket: "unix:///var/run/containerd/containerd.sock"
taints: [] # allow control-plane node to schedule pods
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
Expand Down

0 comments on commit 29446ea

Please sign in to comment.