Tuesday, August 31, 2021

Docker Cheat Sheet

Docker is a Platform as a Service product that uses OS-level virtualization to deliver software in packages called containers. Containers are isolated from one another and bundle their own software + libraries and configuration files yet share single operating system kernel thus use fewer resource than virtual machines.

Let's check it out!

Installation
Docker Engine is available on a variety of Linux platforms. On Windows and Mac OS/X it is easiest to install Docker Desktop as a static binary installation. Install Docker Engine as per Operating System instructions.

Windows
Install Docker Desktop on Windows which will install the Docker Engine, Docker Compose and Kubernetes.

Mac OS/X
Install Docker Desktop on Mac OS/X which will install the Docker Engine, Docker Compose and Kubernetes.

Linux
Docker provides .deb + .rpm packages for various Linux distributions. For example Install Docker on Ubuntu:
sudo apt-get update
sudo apt-get remove docker docker-engine docker.io
sudo apt install docker.io
sudo systemctl start docker
sudo systemctl enable docker
docker version
docker images

The final 2x commands may throw the following error while trying to connect to the Docker daemon socket:

Here is one way to fix "Got permission denied while trying to connect to the Docker daemon socket" error:
sudo groupadd docker
sudo usermod -aG docker ${USER}
sudo reboot
docker version
docker images

IMPORTANT
You may also install docker-compose esp. if you are defining + running multi-container Docker applicaitons.
sudo apt install docker-compose
sudo docker-compose --version

If an older version of docker-compose is installed + you would like to upgrade then complete the following:
sudo apt remove docker-compose
sudo apt update
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo docker–compose --version


Definitions
When you learn Docker, you find that Docker Images are simply templates used to build Docker Containers. Docker Containers are small isolated environments which are runnable version of their Images. Finally, the Dockerfile contains all the commands that specify how an Image should be built and Container should run.

On brand new install on Docker there will be no Docker Images built or Docker Containers running. Verify:


Hello World
The simplest way to pull down Docker image from the Docker Hub is to execute docker run hello-world.


Getting Started
Here is more information on Docker Orientation and setup tutorial to build and run an image as a container:
docker run -d -p 80:80 docker/getting-started
docker images
docker ps

Launch browser. Enter http://localhost. You'll now see the same content locally as the Getting Started page! Afterwards cleanup. Enter the following commands from the Terminal to stop container and remove image:
docker stop $(docker ps -q)
docker rmi $(docker images -qa) --force
docker system prune -a

Example #1
Code an example from scratch: build simple Hello application in Python and run on localhost in a container:
 app.py  Dockerfile
 from flask import Flask
 
 app = Flask(__name__)
 @app.route("/")
 
 def hello():
     return "Hello World!"
 
 if __name__ == "__main__":
     app.run(debug=True, host="0.0.0.0")
 
 
 # Inherit from the Python Docker image
 FROM python:3.7-slim
 # Install the Flask package via pip
 RUN pip install flask==1.0.2
 # Copy the source code to app folder
 COPY ./app.py /app/
 # Change the working directory
 WORKDIR /app/
 # Set "python" as the entry point
 ENTRYPOINT ["python"]
 # Set the command as the script name
 CMD ["app.py"]
Follow best practices for writing DockerFile as each FROM, RUN, COPY, CMD instruction creates one layer.

Enter the following commands to build and run image as a container. Navigate browser to localhost:5000.
 Docker Image  Docker Container
 docker build -t flask_app:0.1 .
 docker images
 docker run -d -p 5000:5000 flask_app:0.1
 docker ps
Also curl http://localhost:5000. To stop the running container enter command: docker stop $(docker ps -q)

IMPORTANT
If you receive OSError: [Errno 98] Address already in use [Port 5000] then execute the following commands:
 netstat -tulnap | grep 5000
 sudo apt install net-tools
 sudo fuser -k 5000/tcp
 netstat -tulnap | grep 5000


Example #2
Code an example from scratch: build simple Hello application in GoLang but automate Dockerfile generation.
 app.go
 package main
 import (
 	"fmt"
 	"log"
 	"os"
 	"net/http"
 	"github.com/gorilla/mux"
 )
 func main() {
 	port := "5000"
 	r := mux.NewRouter()
 	r.HandleFunc("/", hello)
 	http.Handle("/", r)
 	fmt.Println("Starting up on " + port)
 	log.Fatal(http.ListenAndServe(":" + port, nil))
 }
 func hello(w http.ResponseWriter, req *http.Request) {
 	fmt.Fprintln(w, "Hello world!")
 }

Launch VS Code. Open folder hosting app.go. Install Docker extension. Generate Dockerfile with the actions:
  1. Ctrl+Shift+P. Choose Docker: Add Docker Files to Workspace...
  2. Select Application Platform. Choose GoLang
  3. What port does app listen on? Choose 5000
  4. Include optional Docker Compose files? Select No
 Dockerfile
 #build stage
 FROM golang:alpine AS builder
 RUN apk add --no-cache git
 WORKDIR /go/src/app
 COPY . .
 RUN go get -d -v ./...
 RUN go build -o /go/bin/app -v ./...
 #final stage
 FROM alpine:latest
 RUN apk --no-cache add ca-certificates
 COPY --from=builder /go/bin/app /app
 ENTRYPOINT /app
 LABEL Name=golang20 Version=0.0.1
 EXPOSE 5000

In VS Code | Right click Dockerfile | Build Image... Choose name to tag image. Once complete Image will be listed in Docker extension Images list. Expand image built | Run. Refresh Docker extension Containers list. Navigate browser to localhost:5000. Expand Container to see all files from the running Image built earlier:



Example #3
Code an example from scratch: build simple Hello application in C++ and run interactively inside container:
 main.cpp  Dockerfile
 #include <iostream>
 using namespace std;
 int main()
 {
     cout << "Hello World C++" << endl;
     return 0;
 };
 FROM gcc:latest

 COPY . /usr/src/cpp_test
 WORKDIR /usr/src/cpp_test

 RUN g++ -o Test main.cpp
 CMD [ "./Test" ]

Enter the following commands to build and run image as a container. Run interactive inside container also.
 Run normally  Run interactively
 docker build . -t cpp_test:1.0
 docker run --rm cpp_test:1.0
 docker run -it cpp_test:1.0 bash
 ./Test


Example #4
Run real world example. Pull Envoy image from Internet. Run locally to prove all dependencies contained:
docker run --rm envoyproxy/envoy-dev:716ee8abc526d51f07ed6d3c2a5aa8a3b2481d9d --version
docker run --rm envoyproxy/envoy-dev:716ee8abc526d51f07ed6d3c2a5aa8a3b2481d9d --help

Download envoy-demo.yaml configuration. Run following command + navigate to http://localhost:10000.
docker run --rm -it \
      -v $(pwd)/envoy-demo.yaml:/envoy-demo.yaml \
      -p 9901:9901 \
      -p 10000:10000 \
      envoyproxy/envoy-dev:1acf02f70c75a7723d0269b7f375b3a94cb0fbf0 \
          -c envoy-demo.yaml

 curl -v localhost:10000


Interactive
Shell into the remote running Container. Navigate the file system from above using the following command:
 docker exec -it $(docker ps -q) bash
 docker run -it [image_name]:[tag] bash


Commands
Here is list of useful commands. Foreach command you can add the --help flag to see more options to pass:
 Command  Description
 docker build -t [image_name]:[tag] .  Build a Docker image
 docker run --name [container_name] [image_name]:[tag]  Run a Docker container specifying a name
 docker logs -f [container_id_or_name]  Fetch the logs of a container
 docker exec -it [container_id_or_name] bash  Run a command in a running container
 docker rm $(docker ps -aq)  Remove all containers
 docker rmi $(docker images -aq)  Remove all images
 docker rmi $(docker images -f dangling=true -q)  Remove all dangling images
Note: another command to clean up dangling images is simply docker image prune.

Samples
Here is a list of useful commonly used sample commands that can be used frequently as simple cheat sheet:
 Command  Description
 docker images  Show all Docker images
 docker ps  Show all running containers
 docker stop $(docker ps -q)  Stop all running containers
 docker system prune -a  Remove all images + containers
IMPORTANT: if any commands above throw a "docker: 'docker' is not a docker command" then run as sudo!

Registration
Create an account on the Docker hub. In VS Code | Docker extension | Registries | Connect Registry. Enter case sensitive Docker credentials. Right click Image | Push Image to registry | Pull Image from the registry.

Debugging
Once Dockerfiles become more complex you may like to follow some of these tips for debugging containers. The following video demonstrates some of these ideas to debug Docker containers like override ENTRYPOINT
  1. Override the container entrypoint and exec onto it
  2. Use docker cp to copy files between containers and host
  3. Run a debugger inside the container and connect to it from the host system

Summary
To summarize, Docker provides an open standard for packaging and distributing containerized applications, however, the challenge can then become how to coordinate and schedule increasing amount of containers?

This is where Kubernetes can help: Kubernetes is open-source orchestration software that provides an API controlling how and where those containers will run. Kubernetes can scale out multple deployed and more!
This will be the topic of the next post.