From 88d49775d637c399a5a556d4daf9eaada04b34fa Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 30 Jul 2025 20:10:46 +0000 Subject: [PATCH 01/12] deployment and service for k3s --- Dockerfile | 17 +++++++++++++++++ deployment.yml | 20 ++++++++++++++++++++ kubectl.sha256 | 1 + service.yml | 13 +++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 Dockerfile create mode 100644 deployment.yml create mode 100644 kubectl.sha256 create mode 100644 service.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7210058 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# base image that python has installed +FROM python:3.9-slim + +WORKDIR /app + +# Copying the app files into a container +COPY requirements.txt . +COPY helloapp/ helloapp/ + +RUN pip install --no-cache-dir -r requirements.txt + +EXPOSE 8080 + +# A command to run Gunicorn +CMD ["gunicorn", "--bind", "0.0.0.0:8080", "helloapp.app:app"] + + diff --git a/deployment.yml b/deployment.yml new file mode 100644 index 0000000..88bb5f1 --- /dev/null +++ b/deployment.yml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: helloapp-deployment +spec: + replicas: 2 # Number of app pods to run + selector: + matchLabels: + app: helloapp + template: + metadata: + labels: + app: helloapp # Pods will get this label for selector matching + spec: + containers: + - name: helloapp + image: localhost:5000/flask-helloapp:latest + ports: + - containerPort: 8080 + diff --git a/kubectl.sha256 b/kubectl.sha256 new file mode 100644 index 0000000..04e3329 --- /dev/null +++ b/kubectl.sha256 @@ -0,0 +1 @@ +2fcf65c64f352742dc253a25a7c95617c2aba79843d1b74e585c69fe4884afb0 \ No newline at end of file diff --git a/service.yml b/service.yml new file mode 100644 index 0000000..7c6abdb --- /dev/null +++ b/service.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: helloapp +spec: + type: NodePort # Exposes service on each Node's IP at a static port + selector: + app: helloapp # It will select this label to route traffic to pods + ports: + - protocol: TCP + port: 8081 # Port to access service inside the cluster + targetPort: 8080 # Port on which your pod's container listens + From 7af33486e782e4cdec6a5cd2790ab15be5df4f1d Mon Sep 17 00:00:00 2001 From: Heisenberg1420 Date: Sat, 2 Aug 2025 20:36:52 +0200 Subject: [PATCH 02/12] Delete kubectl.sha256 --- kubectl.sha256 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 kubectl.sha256 diff --git a/kubectl.sha256 b/kubectl.sha256 deleted file mode 100644 index 04e3329..0000000 --- a/kubectl.sha256 +++ /dev/null @@ -1 +0,0 @@ -2fcf65c64f352742dc253a25a7c95617c2aba79843d1b74e585c69fe4884afb0 \ No newline at end of file From 7f6f5fcf658f879b6097be6cc1bf6ee8e74c1112 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 31 Jul 2025 05:03:24 +0000 Subject: [PATCH 03/12] Update Docker image tag for reliable CI/CD --- Ansible/files/deployment.yml | 21 +++++++++++++++++++++ Ansible/files/service.yml | 13 +++++++++++++ Ansible/playbook.yml | 16 ++++++++++++++++ Dockerfile | 4 +--- deployment.yml | 5 +++-- 5 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 Ansible/files/deployment.yml create mode 100644 Ansible/files/service.yml create mode 100644 Ansible/playbook.yml diff --git a/Ansible/files/deployment.yml b/Ansible/files/deployment.yml new file mode 100644 index 0000000..b3eb2b9 --- /dev/null +++ b/Ansible/files/deployment.yml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: helloapp-deployment +spec: + replicas: 2 # Number of app pods to run + selector: + matchLabels: + app: helloapp + template: + metadata: + labels: + app: helloapp # Pods will get this label for selector matching + spec: + containers: + - name: helloapp + image: heisenberg1420/devops:v1 + ports: + - containerPort: 8080 + imagePullSecrets: + - name: regcred diff --git a/Ansible/files/service.yml b/Ansible/files/service.yml new file mode 100644 index 0000000..7c6abdb --- /dev/null +++ b/Ansible/files/service.yml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: helloapp +spec: + type: NodePort # Exposes service on each Node's IP at a static port + selector: + app: helloapp # It will select this label to route traffic to pods + ports: + - protocol: TCP + port: 8081 # Port to access service inside the cluster + targetPort: 8080 # Port on which your pod's container listens + diff --git a/Ansible/playbook.yml b/Ansible/playbook.yml new file mode 100644 index 0000000..1b238f0 --- /dev/null +++ b/Ansible/playbook.yml @@ -0,0 +1,16 @@ +- name: Deploy Flask App to k3s + hosts: localhost + become: true + + tasks: + - name: Apply Deployment to k3s + community.kubernetes.k8s: + kubeconfig: /etc/rancher/k3s/k3s.yaml + state: present + definition: "{{ lookup('file', 'files/deployment.yaml') }}" + + - name: Apply Service to k3s + community.kubernetes.k8s: + kubeconfig: /etc/rancher/k3s/k3s.yaml + state: present + definition: "{{ lookup('file', 'files/service.yaml') }}" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7210058..27ee909 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,4 @@ RUN pip install --no-cache-dir -r requirements.txt EXPOSE 8080 # A command to run Gunicorn -CMD ["gunicorn", "--bind", "0.0.0.0:8080", "helloapp.app:app"] - - +CMD ["gunicorn", "--bind", "0.0.0.0:8080", "helloapp.app:app"] \ No newline at end of file diff --git a/deployment.yml b/deployment.yml index 88bb5f1..b3eb2b9 100644 --- a/deployment.yml +++ b/deployment.yml @@ -14,7 +14,8 @@ spec: spec: containers: - name: helloapp - image: localhost:5000/flask-helloapp:latest + image: heisenberg1420/devops:v1 ports: - containerPort: 8080 - + imagePullSecrets: + - name: regcred From 0e11a184c542a378ac0d0806777b39ba4cdcd69d Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 31 Jul 2025 07:31:55 +0000 Subject: [PATCH 04/12] Setup CI/CD pipeline --- .github/workflows/docker-pipeline.yml | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/docker-pipeline.yml diff --git a/.github/workflows/docker-pipeline.yml b/.github/workflows/docker-pipeline.yml new file mode 100644 index 0000000..40cd14d --- /dev/null +++ b/.github/workflows/docker-pipeline.yml @@ -0,0 +1,38 @@ +# .github/workflows/ci-cd.yml + +name: CI/CD Pipeline + +# Trigger this workflow on every push or pull request to any branch +on: + push: + branches: + - 'feature' + +jobs: + build-test-and-push: + runs-on: ubuntu-latest + + steps: + # Step 1: Check out your source code so future steps can access it + - name: Checkout code + uses: actions/checkout@v4 + + # Step 2: Set up Docker Buildx for advanced builds (multi-platform support) + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # Step 3: Log in to Docker Hub using secrets you store in your repo settings + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # Step 4: Build and push the Docker image with your versioned tag + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops:${GITHUB_SHA::6} \ No newline at end of file From 45da3cc6cca8591554ac9ae15c34f458986bdf40 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 31 Jul 2025 07:39:46 +0000 Subject: [PATCH 05/12] pipeline changes --- .github/workflows/docker-pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-pipeline.yml b/.github/workflows/docker-pipeline.yml index 40cd14d..f7790d9 100644 --- a/.github/workflows/docker-pipeline.yml +++ b/.github/workflows/docker-pipeline.yml @@ -35,4 +35,4 @@ jobs: context: . file: ./Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops:${GITHUB_SHA::6} \ No newline at end of file + tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops:${{ github.sha.substr(0,6) }} From a0e4533e9fa46e488df35d423665d06561ad0cc0 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 31 Jul 2025 07:45:09 +0000 Subject: [PATCH 06/12] Pipeline syntax change --- .github/workflows/docker-pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-pipeline.yml b/.github/workflows/docker-pipeline.yml index f7790d9..73c63e5 100644 --- a/.github/workflows/docker-pipeline.yml +++ b/.github/workflows/docker-pipeline.yml @@ -35,4 +35,4 @@ jobs: context: . file: ./Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops:${{ github.sha.substr(0,6) }} + tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops:${{ github.sha }} From ba5d575e230fe2ecce64d4b293adb58aaa4840f1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 31 Jul 2025 08:22:51 +0000 Subject: [PATCH 07/12] Ansible setup & Changes in Pipeline file --- .github/workflows/docker-pipeline.yml | 14 ++++++++++++++ Ansible/files/deployment.yml | 21 --------------------- Ansible/files/service.yml | 13 ------------- Ansible/inventory | 1 + Ansible/playbook.yml | 18 ++++++------------ deployment.yml | 2 +- 6 files changed, 22 insertions(+), 47 deletions(-) delete mode 100644 Ansible/files/deployment.yml delete mode 100644 Ansible/files/service.yml create mode 100644 Ansible/inventory diff --git a/.github/workflows/docker-pipeline.yml b/.github/workflows/docker-pipeline.yml index 73c63e5..0791316 100644 --- a/.github/workflows/docker-pipeline.yml +++ b/.github/workflows/docker-pipeline.yml @@ -36,3 +36,17 @@ jobs: file: ./Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops:${{ github.sha }} + + - name: Update image tag in deployment manifest + run: | + sed -i "s|\(image: ${{ secrets.DOCKERHUB_USERNAME }}/devops:\).*|\1${{ github.sha }}|" deployment.yml + + - name: Commit updated manifest + run: | + git config user.name heisenberg1420 + git config user.email saif1996.ziad@gmail.com + git add deployment.yml + git commit -m "Update image tag to ${{ github.sha }}" + git push + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Ansible/files/deployment.yml b/Ansible/files/deployment.yml deleted file mode 100644 index b3eb2b9..0000000 --- a/Ansible/files/deployment.yml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: helloapp-deployment -spec: - replicas: 2 # Number of app pods to run - selector: - matchLabels: - app: helloapp - template: - metadata: - labels: - app: helloapp # Pods will get this label for selector matching - spec: - containers: - - name: helloapp - image: heisenberg1420/devops:v1 - ports: - - containerPort: 8080 - imagePullSecrets: - - name: regcred diff --git a/Ansible/files/service.yml b/Ansible/files/service.yml deleted file mode 100644 index 7c6abdb..0000000 --- a/Ansible/files/service.yml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: helloapp -spec: - type: NodePort # Exposes service on each Node's IP at a static port - selector: - app: helloapp # It will select this label to route traffic to pods - ports: - - protocol: TCP - port: 8081 # Port to access service inside the cluster - targetPort: 8080 # Port on which your pod's container listens - diff --git a/Ansible/inventory b/Ansible/inventory new file mode 100644 index 0000000..05614f6 --- /dev/null +++ b/Ansible/inventory @@ -0,0 +1 @@ +localhost ansible_connection=local \ No newline at end of file diff --git a/Ansible/playbook.yml b/Ansible/playbook.yml index 1b238f0..6a3ee3c 100644 --- a/Ansible/playbook.yml +++ b/Ansible/playbook.yml @@ -1,16 +1,10 @@ -- name: Deploy Flask App to k3s +- name: Apply Kubernetes manifests using kubectl hosts: localhost become: true - tasks: - - name: Apply Deployment to k3s - community.kubernetes.k8s: - kubeconfig: /etc/rancher/k3s/k3s.yaml - state: present - definition: "{{ lookup('file', 'files/deployment.yaml') }}" + - name: Apply Deployment YAML + command: kubectl apply -f ../deployment.yml --kubeconfig=/etc/rancher/k3s/k3s.yaml + + - name: Apply Service YAML + command: kubectl apply -f ../service.yml --kubeconfig=/etc/rancher/k3s/k3s.yaml - - name: Apply Service to k3s - community.kubernetes.k8s: - kubeconfig: /etc/rancher/k3s/k3s.yaml - state: present - definition: "{{ lookup('file', 'files/service.yaml') }}" \ No newline at end of file diff --git a/deployment.yml b/deployment.yml index b3eb2b9..cfb3825 100644 --- a/deployment.yml +++ b/deployment.yml @@ -14,7 +14,7 @@ spec: spec: containers: - name: helloapp - image: heisenberg1420/devops:v1 + image: heisenberg1420/devops:a0e4533e9fa46e488df35d423665d06561ad0cc0 ports: - containerPort: 8080 imagePullSecrets: From d552de26cea6d55a5755bbf58eacedf0aead7791 Mon Sep 17 00:00:00 2001 From: heisenberg1420 Date: Mon, 4 Aug 2025 08:47:42 +0000 Subject: [PATCH 08/12] Update image tag to ba5d575e230fe2ecce64d4b293adb58aaa4840f1 --- deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.yml b/deployment.yml index cfb3825..c60a3bd 100644 --- a/deployment.yml +++ b/deployment.yml @@ -14,7 +14,7 @@ spec: spec: containers: - name: helloapp - image: heisenberg1420/devops:a0e4533e9fa46e488df35d423665d06561ad0cc0 + image: heisenberg1420/devops:ba5d575e230fe2ecce64d4b293adb58aaa4840f1 ports: - containerPort: 8080 imagePullSecrets: From 771fdc688099f02b7898c6e84ad98b0ba41899a3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 31 Jul 2025 08:52:51 +0000 Subject: [PATCH 09/12] Secret and variables --- .github/workflows/docker-pipeline.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-pipeline.yml b/.github/workflows/docker-pipeline.yml index 0791316..8dd7c17 100644 --- a/.github/workflows/docker-pipeline.yml +++ b/.github/workflows/docker-pipeline.yml @@ -37,14 +37,16 @@ jobs: push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/devops:${{ github.sha }} + # Step 5: Update the image tag - name: Update image tag in deployment manifest run: | sed -i "s|\(image: ${{ secrets.DOCKERHUB_USERNAME }}/devops:\).*|\1${{ github.sha }}|" deployment.yml + # Step 6: Commit and Push the updated manifest - name: Commit updated manifest run: | - git config user.name heisenberg1420 - git config user.email saif1996.ziad@gmail.com + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" git add deployment.yml git commit -m "Update image tag to ${{ github.sha }}" git push From d1e71dfeec9174d07816b053aaa1db903a28bac9 Mon Sep 17 00:00:00 2001 From: Heisenberg1420 Date: Mon, 4 Aug 2025 09:25:52 +0000 Subject: [PATCH 10/12] Update image tag to 771fdc688099f02b7898c6e84ad98b0ba41899a3 --- deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.yml b/deployment.yml index c60a3bd..7eaaab2 100644 --- a/deployment.yml +++ b/deployment.yml @@ -14,7 +14,7 @@ spec: spec: containers: - name: helloapp - image: heisenberg1420/devops:ba5d575e230fe2ecce64d4b293adb58aaa4840f1 + image: heisenberg1420/devops:771fdc688099f02b7898c6e84ad98b0ba41899a3 ports: - containerPort: 8080 imagePullSecrets: From daff7b21c04b449ae87bc9f2b4ef6952e5b0ccc9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 4 Aug 2025 10:44:02 +0000 Subject: [PATCH 11/12] Make Readme file --- README.md | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3b027d5..dbd8a97 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,16 @@ -# Formlabs DevOps home assignment +Technology choices and possible alternatives: -This repository contains a home assignment code for DevOps applicants for Formlabs. +- **Docker**: I used it because it allows me to package the application and all its dependencies into a portable image, ensuring it runs consistently across any environment. -See all open jobs at https://careers.formlabs.com/ +Alternatives: Podman and Buildah offer similar functionality and can even be lighter since they do not require a running daemon. However, Docker remains the industry standard and is widely supported by CI/CD tools and cloud providers. +- **k3s**: I used k3s as my Kubernetes distribution because it’s a lightweight alternative to Minikube. It provides a full Kubernetes API while consuming fewer resources and booting up much faster, which is ideal for local development and fast CI testing. -## Task +- **Ansible**: Ansible is used to automate deployment and infrastructure tasks in a simple way where it's human-readable and idempotent also worth to note that it's Agentless (Doesn't require extra software) and works really well with both Docker and Kubernetes. -0. Fork this repo. -1. Create a deployable docker image for the application. - - Feel free to switch up technologies. For example you can use `buildah` instead of Docker. -2. Create a Kubernetes deployment and service for the application. - - Just aim for the simplest setup, no ingress deployment is needed. Feel free to use Helm. - - You can use [Minikube](https://minikube.sigs.k8s.io/docs/start/) or [k3s](https://k3s.io/) or any other Kubernetes distribution you are familiar with. -3. Create automation to build, test and deploy the application when a change happens in git. - - Feel free to switch up technologies. For example you can use an Ansible playbook or a Jenkins pipeline. -4. Send us the fork where you did your work. +Alternatives: Chef and Puppet but they use Ruby for (Chef) and DSLs for (Puppet) which is harder to read and write than Ansible. -### Notes +- **GitHub Actions**: I used it since it integrates directly with the repository that i have on Github and doesn't need to install or configure external CI/CD servers. Since Actions trigger automatically on Push events. -- Explain as much as possible in the commit message(s) and/or comments if needed. See more on commit messages [here](https://chris.beams.io/posts/git-commit/). -- It would be great if you'd also write about why you choose a certain technology if there are alternatives to consider. +Alternatives: Jenkins and GitLab CI; I haven't used Jenkins since it requires maintaining my own Jenkins server, plugins and agents. +As for GitLab CI, it's relaly strong especially for GitLab hosted projects, but I didn't need to use it here since my repo is available on GitHub. From a48f4354d8f7b51f5cb87fc701d84ac22f780d61 Mon Sep 17 00:00:00 2001 From: Heisenberg1420 Date: Mon, 4 Aug 2025 10:46:07 +0000 Subject: [PATCH 12/12] Update image tag to daff7b21c04b449ae87bc9f2b4ef6952e5b0ccc9 --- deployment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment.yml b/deployment.yml index 7eaaab2..e8f3fb5 100644 --- a/deployment.yml +++ b/deployment.yml @@ -14,7 +14,7 @@ spec: spec: containers: - name: helloapp - image: heisenberg1420/devops:771fdc688099f02b7898c6e84ad98b0ba41899a3 + image: heisenberg1420/devops:daff7b21c04b449ae87bc9f2b4ef6952e5b0ccc9 ports: - containerPort: 8080 imagePullSecrets: