diff --git a/.github/workflows/docker-pipeline.yml b/.github/workflows/docker-pipeline.yml new file mode 100644 index 0000000..8dd7c17 --- /dev/null +++ b/.github/workflows/docker-pipeline.yml @@ -0,0 +1,54 @@ +# .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 }} + + # 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 "${{ 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 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 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 new file mode 100644 index 0000000..6a3ee3c --- /dev/null +++ b/Ansible/playbook.yml @@ -0,0 +1,10 @@ +- name: Apply Kubernetes manifests using kubectl + hosts: localhost + become: true + tasks: + - 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 + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..27ee909 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# 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"] \ No newline at end of file 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. diff --git a/deployment.yml b/deployment.yml new file mode 100644 index 0000000..e8f3fb5 --- /dev/null +++ b/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:daff7b21c04b449ae87bc9f2b4ef6952e5b0ccc9 + ports: + - containerPort: 8080 + imagePullSecrets: + - name: regcred 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 +