Skip to content

Python Application Template to be tested via GHA and deployed via Docker-Compose, K8s Manifests and Helm

License

Notifications You must be signed in to change notification settings

andrey-asoskov/python-app-template

Repository files navigation

python-app-template

Python Flask Application Template to be tested via GHA and deployed via Docker-Compose, K8s Manifests and Helm.

Prerequisites (Tested on)

  • Python 3.9.14
  • Docker 20.10.17
  • Docker Compose v2.10.2
  • Haskell Dockerfile Linter 2.10.0
  • Container-structure-test 1.11.0
  • Kubernetes server: v1.25.4
  • kubectl v1.26.0
  • Helm v3.10.2
  • Helm Chart Testing 3.7.1
  • Checkov 2.1.270
  • Act 0.2.32

CI/CD status

Status
Python-test GHA Workflow
Docker-compose GHA Workflow
K8s-manifests GHA Workflow
Helm GHA Workflow
Full-pipeline GHA Workflow

Contents

Path Description
.github/workflows/ CI pipelines to test app, build Docker image, test Docker-compose config, K8s manifests and test & package Helm chart
app/ App for API with Prometheus client and migrations
cs-test/ Container Structure Test config to test Docker image
data/ DB secrets
Helm/ Helm chart with test and migration hooks
K8s-kind/ Kubernetes Kind cluster config
K8s-manifests/ Kubernetes manifests (an alternative to Helm charts)
tests/ App unittests
.checkov.yaml
.dockerignore
.flake8
.hadolint.yaml Hadlint config to test Dockerfile
.markdownlint.yaml
.pre-commit-config.yaml Pre-commit hooks to test the code
.pylitrc
.python-version
.yamllint.yaml
docker-compose.yaml Docker-compose file
Dockerfile Docker File

Usage

Test Python Code

# Format
python -m pip install -r ./tests/requirements.txt
flake8 ./tests/*.py 
flake8 ./app/*.py 

# Lint
pylint --rcfile ./.pylintrc ./tests/*.py 
pylint --rcfile ./.pylintrc ./app/*.py 

Test via Unittests

python -m unittest tests.test
python -m unittest tests.test_noDB

Start the App

# Start MariaDB
docker run -d \
--name db \
--user "999:999" \
-v $(pwd)/data/mysql-root-password:/run/secrets/mysql-root-password \
-v $(pwd)/data/mysql-user-name:/run/secrets/mysql-user-name \
-v $(pwd)/data/mysql-user-password:/run/secrets/mysql-user-password \
-v $(pwd)/data/mysql-conf-file:/run/secrets/mysql-conf-file \
--read-only \
-v /run/mysqld/ \
-v /var/lib/mysql/ \
-v /tmp/ \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql-root-password \
-e MYSQL_DATABASE=db1 \
-e MYSQL_USER_FILE=/run/secrets/mysql-user-name \
-e MYSQL_PASSWORD_FILE=/run/secrets/mysql-user-password \
--health-cmd="mysql --defaults-extra-file=/run/secrets/mysql-conf-file -e 'SHOW databases;'" \
mariadb:10.9.4

# Start the app
export DB_HOST='127.0.0.1'
export DB_PORT=3306
export DB_NAME='db1'
python ./app.py

# Start via Flask CLI
export FLASK_APP=app.py
flask run \
    --host 0.0.0.0 \
    --port 3000 \
    --reload

# Start via Gunicorn
gunicorn --bind 0.0.0.0:3000 --access-logfile - --error-logfile - wsgi:app

Run DB migrations

# Create migration script after Schema Change
flask db migrate -m "migration1"

# Do the migration
flask db upgrade

Test manually

# Create
curl -X GET  http://127.0.0.1:3000/create

# Root
curl -X GET  http://127.0.0.1:3000/

# Health
curl -X GET  http://127.0.0.1:3000/health 

# Metrics
curl -X GET  http://127.0.0.1:3000/metrics

# Crash
curl -X GET  http://127.0.0.1:3000/crash

Test config (Dockerfile, k8s manifests, Helm, GHA) via Checkov

checkov -d . --config-file ./.checkov.yaml

Create Docker image

# Test
hadolint --config ./.hadolint.yaml Dockerfile

# Docker build
docker build -t andreyasoskovwork/cat-app:0.1.15 -f Dockerfile . 

# Docker push
docker login -u andreyasoskovwork
docker push andreyasoskovwork/cat-app:0.1.15

# Test 
container-structure-test test --image andreyasoskovwork/cat-app:0.1.14 --config ./cs-test/config.yaml

Start as Docker

# Start as docker container
docker run -d \
--user "10001:10001" \
-v $(pwd)/data/mysql-user-name:/run/secrets/mysql-user-name \
-v $(pwd)/data/mysql-user-password:/run/secrets/mysql-user-password \
--read-only \
-v /tmp/ \
-p 3000:3000 \
-e DB_NAME=db1 \
-e DB_PORT=3306 \
-e DB_HOST=10.1.3.80 \
andreyasoskovwork/cat-app:0.1.14

# Test docker-compose
docker-compose -f docker-compose.yaml config

# Start as Docker-compose
docker-compose up -d --wait --build

Start local Kubernetes cluster

# Start Kind Cluster
kind create cluster --config ./K8s-kind/kind-config.yaml

# Create Nginx Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

# Delete Kind Cluster
kind delete cluster

Deplpoy via K8s manifests

# Test manifests
kubectl apply --validate --dry-run=client -f ./K8s-manifests/0.namespace.yaml 
kubectl apply --validate --dry-run=client -f ./K8s-manifests/1.db.yaml
kubectl apply --validate --dry-run=client -f ./K8s-manifests/2.app.yaml
kubectl apply --validate --dry-run=client -f ./K8s-manifests/3.test.yaml

kubectl apply --dry-run=client -f ./K8s-manifests/0.namespace.yaml -o yaml
kubectl apply --dry-run=client -f ./K8s-manifests/1.db.yaml -o yaml
kubectl apply --dry-run=client -f ./K8s-manifests/2.app.yaml -o yaml
kubectl apply --dry-run=client -f ./K8s-manifests/3.test.yaml -o yaml

# Deploy manifests
kubectl apply -f ./K8s-manifests/0.namespace.yaml
kubectl -n app-manifests apply -f ./K8s-manifests/1.db.yaml
kubectl -n app-manifests apply -f ./K8s-manifests/2.app.yaml

# Test
kubectl -n app-manifests apply -f ./K8s-manifests/3.test.yaml
kubectl -n app-manifests get pod

# Test Nginx Ingress
curl --resolve www.example.com:80:127.0.0.1 -H 'Host: www.example.com' localhost/health

Test Helm Chart

# Test Chart
ct lint --config ./Helm/ct-lint.yaml \
--charts ./Helm/charts/app

helm lint ./Helm/charts/app

helm template --namespace app-helm app \
./Helm/charts/app --values ./Helm/app_values.yaml \
--validate  

helm install --namespace app-helm app \
./Helm/charts/app --values ./Helm/app_values.yaml \
--dry-run

Deploy via Helm Chart

# DB
helm repo add bitnami https://charts.bitnami.com/bitnami

helm template --namespace app-helm db \
bitnami/mariadb --version 11.3.3 --values ./Helm/mariadb_values.yaml \
--validate

helm install --namespace app-helm db \
bitnami/mariadb --version 11.3.3 --values ./Helm/mariadb_values.yaml \
--dry-run 

kubectl apply -f ./Helm/namespace.yaml

helm install --create-namespace --namespace app-helm db \
bitnami/mariadb --version 11.3.3 --values ./Helm/mariadb_values.yaml \
--wait

helm upgrade --namespace app-helm db \
bitnami/mariadb --version 11.3.3 --values ./Helm/mariadb_values.yaml \
--wait

# App
kubectl apply -f ./Helm/namespace.yaml

helm install app --create-namespace --namespace app-helm \
./Helm/charts/app --values ./Helm/app_values.yaml --wait

## Update with DB migration
helm upgrade app --namespace app-helm \
./Helm/charts/app --values ./Helm/app_values.yaml

## List
helm list --namespace app-helm

## Test
helm test --namespace app-helm app
kubectl -n app-helm get pod

# Test Nginx Ingress
curl --resolve www.example.com:80:127.0.0.1 -H 'Host: www.example.com' localhost/health

## Delete
helm uninstall --namespace app-helm app
helm uninstall --namespace app-helm db
kubectl delete namespace app-helm

Package Helm Chart

helm package ./Helm/charts/app -d ./Helm

Get the data from App API

## Via Docker or local 
# Create data
curl http://127.0.0.1:3000/create

# Query the data
curl http://127.0.0.1:3000 | jq .

# Query prometheus Metrics
curl http://127.0.0.1:3000/metrics

## Via K8s Ingress
# Create data
curl --resolve www.example.com:80:127.0.0.1 -H 'Host: www.example.com' http://127.0.0.1/create

# Query the data
curl --resolve www.example.com:80:127.0.0.1 -H 'Host: www.example.com' http://127.0.0.1 | jq .

# Query prometheus Metrics
curl --resolve www.example.com:80:127.0.0.1 -H 'Host: www.example.com'  http://127.0.0.1/metrics

Test GHA workflows locally

# Test using medium image
act --rm -r -W ./.github/workflows/Python-test.yml -P ubuntu-22.04=ghcr.io/catthehacker/ubuntu:act-22.04

# Test using full image 20.04
act --rm -r -W ./.github/workflows/Python-test.yml -P ubuntu-22.04=catthehacker/ubuntu:full-20.04

About

Python Application Template to be tested via GHA and deployed via Docker-Compose, K8s Manifests and Helm

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published