Kubernetes is a portable, extensible, open-source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation.
"It's a container orchestration system." - Austin
Additionally, Kubernetes is not a mere orchestration system. In fact, it eliminates the need for orchestration. The technical definition of orchestration is execution of a defined workflow: first do A, then B, then C. In contrast, Kubernetes comprises a set of independent, composable control processes that continuously drive the current state towards the provided desired state. It shouldn’t matter how you get from A to C. Centralized control is also not required. This results in a system that is easier to use and more powerful, robust, resilient, and extensible. - Kubernetes Docs
All of this can be done on a Linux or MacOS desktop if you have the following installed:
Dockerfile
Deployment
Deployment
.!find webapp/
webapp/ webapp/Dockerfile webapp/app webapp/app/main.py webapp/app/static webapp/__pycache__ webapp/__pycache__/app.cpython-37.pyc
!head webapp/app/main.py
from starlette.applications import Starlette from starlette.config import Config from starlette.responses import PlainTextResponse from starlette.routing import Route, Mount from starlette.staticfiles import StaticFiles def homepage(request): return PlainTextResponse(f'Hello, world!\nCONFIGA: {CONFIGA}\nCONFIGB: {CONFIGB}')
Dockerfile
¶Describes how to build container image:
# special sauce in here, or danger
# pull this thread:
# https://github.com/tiangolo/uvicorn-gunicorn-starlette-docker
FROM tiangolo/uvicorn-gunicorn:python3.7-alpine3.8
LABEL maintainer="Austin Godber <godber@uberhip.com>"
RUN pip install starlette aiofiles
COPY ./app /app
# Use Docker in Minikube
eval $(minikube -p minikube docker-env)
# Build image
docker build -t starlette .
Sending build context to Docker daemon 7.168kB
Step 1/4 : FROM tiangolo/uvicorn-gunicorn:python3.7-alpine3.8
---> 9c64ae748955
...
Successfully tagged starlette:1
docker run -p 8000:80 starlette:1
Deployment
¶A What?
Kubernetes is concept heavy and comprised of many layers of abstraction. So ..
A Deployment
creates a Replica Set
, which creates Pods
, which run one or more Containers
that contain your app.
# create resources
kubectl apply -f ./manifest.yaml
# expose service and open in browser
# this is a minikube cheat, see Ingress Controllers for prod
minikube service starlette
How do you find the IP and port on your own?
# The IP is to the Minikube VM
$ minikube ip
192.168.39.2
# The port is the Service LoadBlanacer Port
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8d
starlette LoadBalancer 10.97.16.32 <pending> 18000:32105/TCP 47m
Keeping track of all of these various commands and arguments gets fiddle-y. I like to use a Makefile
to make iterating faster. See the included Makefile
for tips. This is NOT how to do production though. Take a look at the helm
and helmfile
projects for that.
There is an official Kubernetes Python Client here:
https://github.com/kubernetes-client/python
Python Client Docs generated in the README.md:
https://github.com/kubernetes-client/python/blob/master/kubernetes/README.md
Kubernetes API Docs can be found here:
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/
from kubernetes import client, config
from kubernetes.client.rest import ApiException
# use .kube/config, could use token auth
config.load_kube_config()
core_api = client.CoreV1Api()
configmap = client.V1ConfigMap()
configmap
{'api_version': None, 'binary_data': None, 'data': None, 'kind': None, 'metadata': None}
configmap.metadata = client.V1ObjectMeta(name="starlette")
configmap.data = {}
configmap.data["CONFIGA"] = "True"
None hopefully ...
!kubectl get configmap
No resources found in default namespace.
Call the create_namespaced_config_map()
method on core_api
to create the configmap in Kubernetes
core_api.create_namespaced_config_map(namespace="default", body=configmap)
{'api_version': 'v1', 'binary_data': None, 'data': {'CONFIGA': 'True'}, 'kind': 'ConfigMap', 'metadata': {'annotations': None, 'cluster_name': None, 'creation_timestamp': datetime.datetime(2020, 2, 26, 15, 32, 44, tzinfo=tzutc()), 'deletion_grace_period_seconds': None, 'deletion_timestamp': None, 'finalizers': None, 'generate_name': None, 'generation': None, 'initializers': None, 'labels': None, 'managed_fields': None, 'name': 'starlette', 'namespace': 'default', 'owner_references': None, 'resource_version': '2260570', 'self_link': '/api/v1/namespaces/default/configmaps/starlette', 'uid': '2cdda70e-3445-4e4b-afbe-34523acf8012'}}
!kubectl get configmap
NAME DATA AGE starlette 1 0s
core_api.list_namespaced_config_map(namespace="default")
{'api_version': 'v1', 'items': [{'api_version': None, 'binary_data': None, 'data': {'CONFIGA': 'True'}, 'kind': None, 'metadata': {'annotations': None, 'cluster_name': None, 'creation_timestamp': datetime.datetime(2020, 2, 26, 15, 32, 44, tzinfo=tzutc()), 'deletion_grace_period_seconds': None, 'deletion_timestamp': None, 'finalizers': None, 'generate_name': None, 'generation': None, 'initializers': None, 'labels': None, 'managed_fields': None, 'name': 'starlette', 'namespace': 'default', 'owner_references': None, 'resource_version': '2260570', 'self_link': '/api/v1/namespaces/default/configmaps/starlette', 'uid': '2cdda70e-3445-4e4b-afbe-34523acf8012'}}], 'kind': 'ConfigMapList', 'metadata': {'_continue': None, 'resource_version': '2260570', 'self_link': '/api/v1/namespaces/default/configmaps'}}
core_api.delete_namespaced_config_map(name="starlette", namespace="default", body=configmap)
{'api_version': 'v1', 'code': None, 'details': {'causes': None, 'group': None, 'kind': 'configmaps', 'name': 'starlette', 'retry_after_seconds': None, 'uid': '2cdda70e-3445-4e4b-afbe-34523acf8012'}, 'kind': 'Status', 'message': None, 'metadata': {'_continue': None, 'resource_version': None, 'self_link': None}, 'reason': None, 'status': 'Success'}
!kubectl get configmap
No resources found in default namespace.
Basic CRUD on all Kubernetes Resources.
Keep in mind, we're just modifying desired state here, Kubernetes is doing the work in the background to make sure that the actual state matches the desired state. It doesn't always work out.