Based on docker/labs/flask-app
Added some type of config file to play with Kubernetes ConfigMaps
Build the image
export DOCKER_REPO=<docker-hub-user>
docker build -t $DOCKER_REPO/flask_app:1.0 .
-
-tis the tag we give the image, this is used for running containers from the image. the tag assigned to an image is also used to identify the registry and repository to push and pull the data to and from.tags are made from:
registry/name - if left empty, this default to the Docker Hubrepository/nameimage:versiontag
Run a container from the Docker Image
docker run -d --name money_machine -p 5000:5000 $DOCKER_REPO/flask_app:1.0
open http://localhost:5000
-dis to detach the terminal from the container process (run it in the background)--nameis to specify a name for the container-p local:containeris to forward traffic from a local port to a port in the container
Use docker exec to run a process in a container
docker exec -it money_machine sh
Verify container logs with docker logs
docker logs money_machine
Stop and remove the local container
docker stop money_machine && docker rm money_machine
To prepare deployment, Push the Docker image to a registry.
Make sure to provide log in credentials to your docker host first:
docker login <registry>
Where the image will be pushed, depends on your $DOCKER_REPO value:
docker push $DOCKER_REPO/flask_app:1.0
We will first explore an imperative way of deploying our application on Kubernetes.
After this, we will learn how to manage and save our Deployment to facilitate collaboration on the deployment with others.
Before this, we have to ensure Kubernetes can pull our application image if our docker image is privately hosted.
To pull from a private registry, Kubernetes will need the registry credentials.
First, use kubectl to create a secret of type docker-registry with your credentials:
kubectl create secret docker-registry honestbee-registry --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
Above, we created a secret named honestbee-registry. We will need to tell Kubernetes to use this secret while pulling images.
imagePullSecrets can be defined per Deployment or alternatively the secret can be added as an imagePullSecret to the default service account
Service accounts are used by Pod resources to complete their tasks. A default Service account called default is made available
in every namespace by the Service Account Controller.
To update the default service account, ensure you have jq installed.
Run following command:
kubectl get serviceaccounts default -o json |
jq 'del(.metadata.resourceVersion)'|
jq 'setpath(["imagePullSecrets"];[{"name":"honestbee-registry"}])' |
kubectl replace serviceaccount default -f -
Note: The metadata.resourceVersion field is used by the API server for optimistic concurrency. We are removing the resourceVersion and adding the imagePullSecret in the above oneliner. We are also replacing the default serviceaccount
with the modified json object streamed through stdin (-)
Imperatively create a Kubernetes Deployment for the application by supplying:
- Name of the deployment
- Docker image used for the deployment
- Ports to be exposed on the Pods created by the deployment
kubectl run app --image=$DOCKER_REPO/flask_app:1.0 --port=5000Inspect the Resources created by the above command using the Kubernetes UI.
To securely access the Kubernetes UI, use kubectl to create a proxy from your laptop to the Cluster.
kubectl proxy &
open http://localhost:8001/ui
Note: Following resources are of interest and can be drilled down from top to bottom:
- Deployment (notice the labels attached)
- ReplicaSet
- Pod (notice the ability to see the logs)
Use a label query to find all pods created by this Deployment manifest
kubectl get pods -l run=app
Use kubectl logs to get container logs in the Pod created by the deployment using the console:
POD_NAME=`kubectl get pods -l run=app -o name | head -n 1`
kubectl logs $POD_NAME -c app
Use kubectl exec to run a process (i.e. a shell) in a container of a Pod
kubectl exec -it ${POD_NAME#*/} -c app -- shNote: The bash variable expansion {VAR#<pattern>} will strip the ` from the env var
Use kubectl port-forward to forward traffic from a local port to a port in the container
kubectl port-forward ${POD_NAME#*/} 5000 &
open http://localhost:5000
NOTE: Once done, kill the port forwarding proxy as follows
# list all background jobs
jobs -l
# kill the job running the port forward %<job_nr>
kill -INT %1Get the Deployment Manifest generated by the imperative command above:
kubectl get deploy app -o yaml > kubernetes/deploy-app-status.yamlThis Manifest is the declarative definition of the Deployment resource which will be managed by the Kubernetes Controllers.
After removing all status information and reducing it to the minimal keys required, our simplified Deployment Manifest looks as follows:
less kubernetes/deploy-app-simple.yamlNote:
runlabel created by thekubectl runcommand was replaced by a more commonly usedapplabel- container name was changed to
gunicornto reflect the main process of the container
To understand more about each key in the Deployment Manifest,
use kubectl explain:
kubectl explain deployment.spec.template.specIn this case, the spec.template.spec is the podSpec used by ReplicaSets when creating Pods.
Delete the imperatively created Deployment with kubectl delete deploy:
kubectl delete deploy appUse the manifest file to declaratively create the Deployment:
kubectl create -f kubernetes/deploy-app-simple.yamlUse kubectl logs to get container logs in the Pod created by the deployment using the console:
POD_NAME=`kubectl get pods -l app=app -o name | head -n 1`
kubectl logs $POD_NAME -c gunicornGet a list of images from an imgur album (see imgur folder for more info about the script)
docker build -t imgur_script imgur/
docker run -it --rm \
-e "IMGUR_ALBUM_ID=R6xcQ" \
-e "IMGUR_CLIENT_ID=88da..." \
-e "IMGUR_CLIENT_SECRET=41d6..." \
imgur_script > config/funnier-images.txtVerify contents of config/funnier-images.txt
less config/funnier-images.txtConfiguration should be provided by the environment and not embedded within the Docker image.
In Kubernetes we use a ConfigMap resource to store one or multiple configuration files.
Create a Kubernetes ConfigMap from a local file using kubectl create configmap
kubectl create cm app-images --from-file=images.txt=config/funnier-images.txtInspect the generated ConfigMap manifest
kubectl get cm app-images -o yamlExtract the data from the ConfigMap
kubectl get cm app-images -o jsonpath="{.data['images\.txt']}"
To use the ConfigMap define a volume in the PodSpec and mount the volume in the Container
less kubernetes/deploy-app-configmap.yamlApply these changes to our app Deployment
kubectl apply -f kubernetes/deploy-app-configmap.yamlConfirm the Deployment is managing a controlled update for the new PodSpec by creating new Pods
kubectl get pods -wConfirm the new images are mounted in the newly created Pod
POD_NAME=`kubectl get pods -l app=app -o name | head -n 1`
kubectl exec -t ${POD_NAME#*/} -c gunicorn -- cat /usr/src/app/config/images.txtTest webservice
kubectl port-forward ${POD_NAME#*/} 5000 &Imperatively create a Service resource for the app Deployment
kubectl expose deploy app --target-port=5000 --type=LoadBalancerGet info about the exposed service
kubectl describe svc appOn Cloud providers, you will notice the external IP being provisioned
kubectl get svc -wWith minikube, use minikube service command to access the app through the service.
minikube service app