Docker and Python UDEMY Course (NOTES)
Docker is one of my favorite tools, so I enjoyed learning a lof about it. Here are my notes on some of my favorite topics.
Containers
A container image is a file that holds all the code and libraries necessary to run some software. A container is a runtime environment that is created from a container image. Container images are not VM's: they don't have a kernel, which gives the a great advantage that they can be created and start really quickly. They have their own IP address and that's how they share information with other containers (two containers do not have access to each other's memory).
The catch is that containers must be run on a similar chipset and operating system. While Linux containers can usually run on Linux, Windows, and MacOS, there are some Windows containers that can only run on Windows.
When a container reads from one of those OS folders like /bin, it's actually reading it from the host operating system. And when it tries to write to it, the container doesn't actually write those changes to the OS but instead saves it in a thin "write" layer that is discarded when the container is shut down. A container really shouldn't be able to write to an OS folder if I am not mistaken.
A container cannot see processes in its host system or other containers. A container cannot use software installed on the host file system. Each container has its own file system. How to build a container? Start with a base image, and add parts on top if it (add Python, add Flask, like that). When the kernel hangs in a system with containers, all the containers hang (but in a VM this wouldn't be a problem since each VM has its own kernel).
Deployment
Containers can be manually deployed. Create the container, clone the right version of your software, install the libraries, then freeze it. Then wherever it needs to be deployed, download the container image and run it (no need to download software or install libraries as those come packaged in the freezed container image).
However, this is too hard. So there's a file called a Dockerfile that you need to write to automate the creation of new container images. The Dockerfile can become a part of your version control. The new container image can also be uploaded online to a cloud provider as a public or private image, depending on your visibility need.
Docker build creates an image from a Dockerfile. Images can run on any similar OS. Automatic deployment of images can be controlled using Docker Swarm or a layer on top of that with Jenkins which updates necessary images, runs unit tests, and rolls back versions if unit tests fail. Docker Hub is where you upload docker images. Docker Registry is a local environment for storing and organizing docker images. Prometheus and/or Datadog are applications for monitoring software running in containers. Containers now have a open-source standard for how they should look like so that you can run a standardized container on any applicable runtime.
Orchestration Frameworks
As your application gets more complex, need for horizontal scaling and vertical improvements (load balancer, more servers, in memory database, etc). Need automatic deployment -- for example, add more servers, upgrade version of base images, etc. The hard part is the YAML file, running Kubernetes is easy. Each service provider has their deployment service.
Kubectl can be used imperatively.
Python Flask Container example
Flask application declares IP 0.0.0.0 which is automatically exposed. And it specifies port 5000 for connection. The Dockerfile has multiple commands. First, it uses image python:3 as base image. If python:3 is updated (for example, for security), just pull the image and restart containers (there is an automated way to do this). Next, set working directory to /usr/src/app. If this directory doesn't exist, Docker will create it. Copy our py file over, run requirements, declare that we will expose port 5000 in our container (only in the runtime), and declare with CMD to run the main.py file (again only in the runtime) not in this image. Docker freezes the image. We run the dockerfile with docker build with image tag. Then run an instance of the container with docker run (?) --rm (to remove the container once it finishes running) -it (uses the terminal and makes it interactive) then the image plus the ports that we are exposing (-p 0.0.0.0:5001:5000). Then go to localhost:5001 and voila.
Have to map network ports in container to host system (through exposing). Defined at runtime when we launch a container. Flask will only accept connections to correct IP address unless you use 0.0.0.0
Deploying the Flask Application to different servers
Use docker-machine to set up a server on AWS or the Google one that is a docker host. Login to the server using bash-env, pull the container from docker hub, and run the image. Find the server with the internal IP address. Shut down the server with docker-machine rm.
Using local kubernetes: kubectl run/get deployment, kubectl service (expose port 5000).
Installation
When Docker is installed on MacOS, it actually runs in a tiny VM. But on Linux it runs as a simple service. Install docker, get docker compose, get docker-machine + virtualbox, then kubectl + minikube. Docker machine can create multiple VMs, each with docker, perhaps running several containers at the same time.
Docker Linux Security Risk
A user that has unrestricted access to Docker, even if the user has no access to root user, can spin up docker containers that affect root (since containers and host share kernel and containers run in root). This is really bad, so do not run random containers, they may screw up root.