├── .dockerignore
├── .gitignore
├── Dockerfile
├── README.md
├── docker-compose.yml
├── kubernetes
├── deployment.yml
└── service.yml
├── requirements.txt
├── src
├── __pycache__
│ └── app.cpython-310.pyc
├── app.py
└── templates
│ └── index.html
└── webapp
├── .helmignore
├── Chart.yaml
├── templates
├── NOTES.txt
├── _helpers.tpl
├── deployment.yaml
└── service.yaml
└── values.yaml
/.dockerignore:
--------------------------------------------------------------------------------
1 | tutorial-env
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | tutorial-env
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10.0-alpine3.15
2 | WORKDIR /app
3 | COPY requirements.txt .
4 | RUN pip install -r requirements.txt
5 | COPY src src
6 | EXPOSE 5000
7 | HEALTHCHECK --interval=30s --timeout=30s --start-period=30s --retries=5 \
8 | CMD curl -f http://localhost:5000/health || exit 1
9 | ENTRYPOINT ["python", "./src/app.py"]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # microservices-in-python
2 | microservices-in-python
3 | - Installing Python 3.X
4 | - Creating Python Virtual Environments
5 | - Installing Python VS Code Extension
6 | - Sample Flask Application
7 | - Jinja templating for Dynamic Web Pages
8 | - Using Pip to Freeze Python Dependencies
9 | - Building the docker image using Dockerfile
10 | - Writing Docker Compose file
11 | - Writing Kubernetes Manifest files for the application
12 | - Creating Helm Chart
13 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.9" # optional since v1.27.0
2 | services:
3 | web:
4 | build:
5 | context: .
6 | dockerfile: Dockerfile
7 | image: webapp:2.0
8 | ports:
9 | - "80:5000"
10 | restart: always
11 | networks:
12 | - webnet
13 |
14 | networks:
15 | webnet:
--------------------------------------------------------------------------------
/kubernetes/deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: python-webapp
5 | labels:
6 | app: web
7 | spec:
8 | replicas: 2
9 | selector:
10 | matchLabels:
11 | app: web
12 | template:
13 | metadata:
14 | labels:
15 | app: web
16 | spec:
17 | containers:
18 | - name: webapp
19 | image: webapp:1.0
20 | ports:
21 | - containerPort: 5000
--------------------------------------------------------------------------------
/kubernetes/service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: web-service
5 | spec:
6 | type: NodePort
7 | selector:
8 | app: web
9 | ports:
10 | - port: 80
11 | targetPort: 5000
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | click==8.0.3
2 | colorama==0.4.4
3 | Flask==2.0.2
4 | itsdangerous==2.0.1
5 | Jinja2==3.0.3
6 | MarkupSafe==2.0.1
7 | Werkzeug==2.0.2
8 |
--------------------------------------------------------------------------------
/src/__pycache__/app.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kunchalavikram1427/microservices-in-python/710d45a42ddb0bf73812f10c08c678efbb47bf23/src/__pycache__/app.cpython-310.pyc
--------------------------------------------------------------------------------
/src/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, jsonify, render_template
2 | import socket
3 | app = Flask(__name__)
4 |
5 | # Function to fetch hostname and ip
6 | def fetchDetails():
7 | hostname = socket.gethostname()
8 | host_ip = socket.gethostbyname(hostname)
9 | return str(hostname), str(host_ip)
10 |
11 | @app.route("/")
12 | def hello_world():
13 | return "
Hello, World!
"
14 |
15 | @app.route("/health")
16 | def health():
17 | return jsonify(
18 | status="UP"
19 | )
20 | @app.route("/details")
21 | def details():
22 | hostname, ip = fetchDetails()
23 | return render_template('index.html', HOSTNAME=hostname, IP=ip)
24 |
25 | if __name__ == '__main__':
26 | app.run(host='0.0.0.0', port=5000)
--------------------------------------------------------------------------------
/src/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dockerizing a Python Web Application
5 |
6 |
7 |
8 | Simple Web Application using Python Flask Framework
9 | Response from host {{ HOSTNAME }} with IP Address {{ IP }}
10 |
11 |
--------------------------------------------------------------------------------
/webapp/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *.orig
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 | .vscode/
24 |
--------------------------------------------------------------------------------
/webapp/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: webapp
3 | description: A Helm chart for Kubernetes
4 |
5 | # A chart can be either an 'application' or a 'library' chart.
6 | #
7 | # Application charts are a collection of templates that can be packaged into versioned archives
8 | # to be deployed.
9 | #
10 | # Library charts provide useful utilities or functions for the chart developer. They're included as
11 | # a dependency of application charts to inject those utilities and functions into the rendering
12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
13 | type: application
14 |
15 | # This is the chart version. This version number should be incremented each time you make changes
16 | # to the chart and its templates, including the app version.
17 | # Versions are expected to follow Semantic Versioning (https://semver.org/)
18 | version: 0.1.0
19 |
20 | # This is the version number of the application being deployed. This version number should be
21 | # incremented each time you make changes to the application. Versions are not expected to
22 | # follow Semantic Versioning. They should reflect the version the application is using.
23 | # It is recommended to use it with quotes.
24 | appVersion: "1.16.0"
25 |
--------------------------------------------------------------------------------
/webapp/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | 1. Get the application URL by running these commands:
2 | {{- if .Values.ingress.enabled }}
3 | {{- range $host := .Values.ingress.hosts }}
4 | {{- range .paths }}
5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
6 | {{- end }}
7 | {{- end }}
8 | {{- else if contains "NodePort" .Values.service.type }}
9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "webapp.fullname" . }})
10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11 | echo http://$NODE_IP:$NODE_PORT
12 | {{- else if contains "LoadBalancer" .Values.service.type }}
13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "webapp.fullname" . }}'
15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "webapp.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16 | echo http://$SERVICE_IP:{{ .Values.service.port }}
17 | {{- else if contains "ClusterIP" .Values.service.type }}
18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "webapp.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19 | export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
20 | echo "Visit http://127.0.0.1:8080 to use your application"
21 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
22 | {{- end }}
23 |
--------------------------------------------------------------------------------
/webapp/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Expand the name of the chart.
3 | */}}
4 | {{- define "webapp.name" -}}
5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6 | {{- end }}
7 |
8 | {{/*
9 | Create a default fully qualified app name.
10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11 | If release name contains chart name it will be used as a full name.
12 | */}}
13 | {{- define "webapp.fullname" -}}
14 | {{- if .Values.fullnameOverride }}
15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16 | {{- else }}
17 | {{- $name := default .Chart.Name .Values.nameOverride }}
18 | {{- if contains $name .Release.Name }}
19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }}
20 | {{- else }}
21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22 | {{- end }}
23 | {{- end }}
24 | {{- end }}
25 |
26 | {{/*
27 | Create chart name and version as used by the chart label.
28 | */}}
29 | {{- define "webapp.chart" -}}
30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31 | {{- end }}
32 |
33 | {{/*
34 | Common labels
35 | */}}
36 | {{- define "webapp.labels" -}}
37 | helm.sh/chart: {{ include "webapp.chart" . }}
38 | {{ include "webapp.selectorLabels" . }}
39 | {{- if .Chart.AppVersion }}
40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41 | {{- end }}
42 | app.kubernetes.io/managed-by: {{ .Release.Service }}
43 | {{- end }}
44 |
45 | {{/*
46 | Selector labels
47 | */}}
48 | {{- define "webapp.selectorLabels" -}}
49 | app.kubernetes.io/name: {{ include "webapp.name" . }}
50 | app.kubernetes.io/instance: {{ .Release.Name }}
51 | {{- end }}
52 |
53 | {{/*
54 | Create the name of the service account to use
55 | */}}
56 | {{- define "webapp.serviceAccountName" -}}
57 | {{- if .Values.serviceAccount.create }}
58 | {{- default (include "webapp.fullname" .) .Values.serviceAccount.name }}
59 | {{- else }}
60 | {{- default "default" .Values.serviceAccount.name }}
61 | {{- end }}
62 | {{- end }}
63 |
--------------------------------------------------------------------------------
/webapp/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: {{ include "webapp.fullname" . }}
5 | labels:
6 | {{- include "webapp.labels" . | nindent 4 }}
7 | spec:
8 | replicas: {{ .Values.replicaCount }}
9 | selector:
10 | matchLabels:
11 | {{- include "webapp.selectorLabels" . | nindent 6 }}
12 | template:
13 | metadata:
14 | labels:
15 | {{- include "webapp.selectorLabels" . | nindent 8 }}
16 | spec:
17 | containers:
18 | - name: {{ .Chart.Name }}
19 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
20 | imagePullPolicy: {{ .Values.image.pullPolicy }}
21 | ports:
22 | - name: http
23 | containerPort: {{ .Values.service.targetPort }}
24 | protocol: TCP
--------------------------------------------------------------------------------
/webapp/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ include "webapp.fullname" . }}
5 | labels:
6 | {{- include "webapp.labels" . | nindent 4 }}
7 | spec:
8 | type: {{ .Values.service.type }}
9 | ports:
10 | - port: {{ .Values.service.port }}
11 | targetPort: {{ .Values.service.targetPort }}
12 | protocol: TCP
13 | name: http
14 | selector:
15 | {{- include "webapp.selectorLabels" . | nindent 4 }}
16 |
--------------------------------------------------------------------------------
/webapp/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for webapp.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 |
5 | replicaCount: 2
6 |
7 | image:
8 | repository: webapp
9 | pullPolicy: IfNotPresent
10 | # Overrides the image tag whose default is the chart appVersion.
11 | tag: "1.0"
12 |
13 | imagePullSecrets: []
14 | nameOverride: ""
15 | fullnameOverride: ""
16 |
17 | serviceAccount:
18 | # Specifies whether a service account should be created
19 | create: true
20 | # Annotations to add to the service account
21 | annotations: {}
22 | # The name of the service account to use.
23 | # If not set and create is true, a name is generated using the fullname template
24 | name: ""
25 |
26 | podAnnotations: {}
27 |
28 | podSecurityContext: {}
29 | # fsGroup: 2000
30 |
31 | securityContext: {}
32 | # capabilities:
33 | # drop:
34 | # - ALL
35 | # readOnlyRootFilesystem: true
36 | # runAsNonRoot: true
37 | # runAsUser: 1000
38 |
39 | service:
40 | type: NodePort
41 | port: 80
42 | targetPort: 5000
43 |
44 | ingress:
45 | enabled: false
46 | className: ""
47 | annotations: {}
48 | # kubernetes.io/ingress.class: nginx
49 | # kubernetes.io/tls-acme: "true"
50 | hosts:
51 | - host: chart-example.local
52 | paths:
53 | - path: /
54 | pathType: ImplementationSpecific
55 | tls: []
56 | # - secretName: chart-example-tls
57 | # hosts:
58 | # - chart-example.local
59 |
60 | resources: {}
61 | # We usually recommend not to specify default resources and to leave this as a conscious
62 | # choice for the user. This also increases chances charts run on environments with little
63 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
64 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
65 | # limits:
66 | # cpu: 100m
67 | # memory: 128Mi
68 | # requests:
69 | # cpu: 100m
70 | # memory: 128Mi
71 |
72 | autoscaling:
73 | enabled: false
74 | minReplicas: 1
75 | maxReplicas: 100
76 | targetCPUUtilizationPercentage: 80
77 | # targetMemoryUtilizationPercentage: 80
78 |
79 | nodeSelector: {}
80 |
81 | tolerations: []
82 |
83 | affinity: {}
84 |
--------------------------------------------------------------------------------