Skip to content

Instantly share code, notes, and snippets.

@d3nd3
Last active June 18, 2024 16:16
Show Gist options
  • Save d3nd3/c88eff9f335b35923722510fe6df0e61 to your computer and use it in GitHub Desktop.
Save d3nd3/c88eff9f335b35923722510fe6df0e61 to your computer and use it in GitHub Desktop.
Docker

What is Docker?

Container

A sandboxed process on your machine that is isolated from all other processes on the host machine

  • is a runnable instance of an image. You can create, start, stop, move, or delete a container using the DockerAPI or CLI.
  • can be run on local machines, virtual machines or deployed to the cloud.
  • is portable (can be run on any OS)
  • Containers are isolated from each other and run their own software, binaries, and configurations.

How?

Leverages kernel namespaces and cgroups, features that have been in Linux for a long time

container IMAGE

When running a container, it uses an isolated filesystem. This custom filesystem is provided by a container image.

Since the image contains the container’s filesystem, it must contain everything needed to run an application - all dependencies, configuration, scripts, binaries, etc.

The image also contains other configuration for the container, such as environment variables, a default command to run, and other metadata.

Comparison to chroot

If you’re familiar with chroot, think of a container as an extended version of chroot.
The filesystem is simply coming from the image.
But, a container adds additional isolation not available when simply using chroot.

Dockerfile

Simply a text-based script of instructions that is used to create a container image.
docker build -t IMAGE_TAG .
docker run IMAGE_TAG

docker build [OPTIONS] CONTEXT
The docker build command builds an image from a Dockerfile and a context.
context == directory or git repository url.

Place your Dockerfile into the CONTEXT.

Layers

https://docs.docker.com/storage/storagedriver/
There is a common technique to delete files within the same RUN command that they were created in.
This is because once a layer is created, a file cannot be deleted from it.
Thus, best practise is to use multi-stage builds if you worry about this problem and are generating a lot of intermediaty files.

Thin top Writable layer

When you run a container Image, any writes that the running container performs are stored in the top most layer. When the container is deleted, so is this layer. Thus running an image creates a clean slate.
This top layer can also be stored/commited to an imageFile if you so desire.

VITAL INFORMATION

COMMANDS IN DEPTH

ARG

Both ARG and ENV are not expanded in ENTRYPOINT or CMD
But ENV is passed to the shell.
Supplied at build-time to docker build with --build-arg <varname>=<value>
The Dockerfile defines the ARG's existence with ARG varname=default.
The arg variable only exists after it has been declared in a line on the Dockerfile. Only lines after that will have a value for the arg.
They also come out of scope at the end of each build stage / (before next FROM instruction). But you can bring them back into scope by repeating the declaration, ARG varname=default or ARG varname (no defaults).

FROM

repositoryName/imageName:tagVersion
eg. FROM i386/ubuntu:bionic AS builder
The tag or digest values are optional. If you omit either of them, the builder assumes a latest tag by default. The builder returns an error if it cannot find the tag value.

The optional --platform flag can be used to specify the platform of the image in case FROM references a multi-platform image. For example, linux/amd64, linux/arm64, or windows/amd64. By default, the target platform of the build request is used

SHELL COMMAND

This command sets the shell for the STRING/SHELL forms of : RUN, CMD and ENTRYPOINT.
SHELL ["executable", "parameters"]
By default it is /bin/sh -c.

CMD and ENTRYPOINT

  • There are 2 forms, JSON_FORM/EXEC_FORM and STRING_FORM/SHELL_FORM.
  • STRING_FORM wraps the command and its arguments in a SHELL (default=/bin/sh -c).
  • The JSON_FORM of ENTRYPOINT means your process is PID=1, String form NOT.

Shell/String Form

This is space separated like:
CMD bash myfile.sh > /dev/zero
It is nicknamed Shell form because its essentially wrapping the arguments with bash -c ${YOUR_STRING}

DIRECT/JSON_OBJECT Form

This is in a json-array like syntax.
CMD ["bash", "myfile.sh", ">" ,"/dev/zero"]
The command becomes the process Parent. It is forked.

When to use ENTRYPOINT?

  • ENTRYPOINT can be extended by the command-line ( in DIRECT/JSON_OBJECT form )
  • ENTRYPOINT should be defined when using the container as an executable. Because it supports argument extension.
  • HOWEVER you can enforce the runtime by using Shell/String form. It disables both CMD and runtime args. Only disadvantage is that you are not PID 1, bash is.

When to use CMD?

  • CMD are temporary because can be overriden by user.
  • When you specify CMD in Dockerfile you are really specifying a default, as they will be overridden by any run-time arguments.
  • CMD should be used as a way of defining default arguments for an ENTRYPOINT command

How they interact

Example :
Shell/String CMD PROG ARG == /bin/sh -c PROG ARG - PROG wrapped with /bin/sh
DIRECT/JSON_OBJECT CMD PROG ARG == PROG ARG - PROG becomes PID 1

Shell/String ENTRYPOINT [PROG,ARG] == /bin/sh -c PROG ARG - PROG wrapped with /bin/sh
DIRECT/JSON_OBJECT ENTRYPOINT [PROG,ARG] == PROG ARG - PROG becomes PID 1

combinations

Shell/String ENTRYPOINT disables CMD.
If ENTRYPOINT im a string => /bin/sh -c im a string.

DIRECT/JSON_OBJECT ENTRYPOINT with DIRECT/JSON_OBJECT CMD == prog1 arg1 prog2 arg2 - as you can see the CMD prog2/arg2 are used as parameters to prog1 - Summary = Concatenation = Double Direct.

DIRECT/JSON_OBJECT ENTRYPOINT with Shell/String CMD == prog1 arg1 /bin/sh -c prog2 arg2 - Summary - /bin/sh is passed to the ENTRYPOINT, so this one is quite odd.

If wrapped in /bin/sh -c, signals are not passed, can be fixed with ENTRYPOINT exec YOURCMD ARG1.

simplified TLDR:

** THERE ARE ONLY 2 CASES TO REMEMBER, THE OTHER IS WEIRD **

  1. Complete ENTRYPOINT string. Powerful disables everything else.
Separately
  1. Use Only DIRECT/JSON_OBJECT of ENTRYPOINT.
    UseCase = No Shell Syntax, PID1, CommandLine appended.
  2. Use Only Shell/String of CMD.
    UseCase = Access To Shell Syntax, Fully Overidable by commandLine.
Together
  1. Use DIRECT/JSON_OBJECT of ENTRYPOINT plus Exec form CMD for using CMD as "arguments" to ENTRYPOINT, where these arguments can be overriden by docker run <arguments>.
    UseCase = No Shell Syntax, PID1, Default Arguments overidable by CommandLine.
  2. Use DIRECT/JSON_OBJECT form of ENTRYPOINT plus String form CMD which appends bin/sh -c prog2 arg2, making the arguments seem a bit weird, not sure what this is use case is!
    UseCase = Unsure

docker run command line overriding ENTRYPOINT and CMD

arguments passed to docker run images ... :

  1. ignored if ENTRYPOINT is Shell/String
  2. will OVERRIDE all forms of CMD
  3. will be APPENDED to ENTRYPOINT if DIRECT/JSON_OBJECT_FORM

RUN

  • has lower case Shell/String like CMD and ENTRYPOINT
  • and json [] exec form.
  • By default it is /bin/sh -c

The RUN instruction will execute any commands in a new layer on top of the current image and commit the results. The resulting committed image will be used for the next step in the Dockerfile

The cache for RUN instructions isn’t invalidated automatically during the next build.
The cache for RUN instructions can be invalidated by using the --no-cache flag.
docker build --no-cache

COPY

This is used to send files/folders INTO the built image.

ADD

This is like COPY but also can automatically unpack/unzip files.

chatGpt bind Mounts Explain

When a directory on the host is mounted to a directory in the container using the -v option, the default behavior is to use the files and directories in the host directory and hide the contents of the container directory.

If the target directory in the container is not empty and the source directory on the host is empty, then the container directory contents will be visible in the mounted directory within the container.

However, if the source directory on the host is not empty and the target directory in the container is not empty, the behavior will depend on whether the -v option was used with the :ro (read-only) or :rw (read-write) option.

  • If the -v option was used with the :ro option, then the host directory contents will be visible in the mounted directory within the container, but any attempts to write to the mounted directory within the container will fail.
  • If the -v option was used with the :rw option, then the host directory contents will be visible in the mounted directory within the container, and any changes made to the mounted directory within the container will be reflected in the host directory.

When you use the VOLUME instruction in a Dockerfile, you are essentially creating a mount point for a volume in the container. This allows you to specify a directory that will be used as a data volume, which can be mounted from the host or from another container at runtime.

By copying files into the volume directory during the build process, you are essentially providing default contents for the volume. These contents will be visible in the volume when it is mounted in a container, unless the contents of the mount source (e.g., the host directory) take precedence because they are not empty.

Want empty directory?

Use volume?

Want to use a specific directory that has contents already?

Use -v

More Notes on Docker

Isolated Environment

  • Advantages similar to running in a Virtual Machine
  • Removes inconsistencies
  • Sandboxes projects
  • It just works, less hassle/time/effort run someone else work.

Comparing Docker Container to a non-docker Virtual Machine

VirtualMachine Example

	Server
		HostOS
			Hypervisor
				Kernel			<-VirtualMachine
					bins/libs	<-VirtualMachine
						apps	<-VirtualMachine

Container Example

	Server
		HostOS
			bins/libs	<-CONTAINER
				apps	<-CONTAINER

Key Points

  • A Container is a running instance of an Image.
  • Containers will stop by themselves when the main procses exits.
  • 1 Process per Container is recommended.

Image

An Image is a template for creating the environment you want it to snapshot of the system at a particualr time.

OS
Software
Application Code

All bundled up in a file.

You RUN images. Then they spawn containers.

  • Images are defined using a dockerfile.
  • Steps/recipe to create that image.

Image Name

The format for an image name is [registry:port]/repository/name:tag.
The [registry:port] is optional and defaults to registry-1.docker.io.
So usually its just : repositoryName/imageName:tagVersion

Tags

  • Tag is used to refer to a specific version/instance of a project/image
  • Whenever an image is tagged without an explicit tag, it’s given the latest tag by default

The Dockerfile

  1. Create a Dockerfile
  2. Build it to create an image
  3. Run the image to spawn container

Example Dockerfile Loading another Dockerfile image prebuilt from hub.docker.com

FROM php:7.0-apache
COPY src/ /var/www/html

EXPOSE 80

PathContext = "." image_name:tag_name = "hello-world"

By default the docker build command will look for a Dockerfile at the root of the build context

Building the Docker Image / Docker Build

docker build [OPTIONS] PATH | URL | -

docker build -t <image_name:tag_name> <BuildContextPath>

docker build https://github.com/d3nd3/repo.git#branch:directoryAsContext

Exclude folders/files
	echo ".git" > .dockerignore

How to update data inside the built container

Can mount files that change, option to share with the host.

-v /absolutePath/toHost:/absolutepath/tomounTo/

Its possible to make files permanent to an image, by comitting a container to an image,after its ran.

Running an Image /w Docker Run

docker run [OPTIONS] imgrepo/imgname[:TAG|@DIGEST] [COMMAND] [ARG...] eg.   docker run -p 80:80 hello-world

some options

  • --rm Automatically remove the container when it exits
  • -i Interactive, keep STDIN open even if not attached
  • -t Allocate a pseudo-TTY

Pseudo TTy

xterm -> ptmx (pty master) -> pts (pty slave) -> bash
user sends command to bash.
'tty' command, shows pseudo-terminal
bash's natural TTY slave
If the program that you are launching supports connecting to a pesudo terminal like a 'shell' eg. like bash, sh, dash. Then you supply -t
So, with that as background
run a container with no options and by default you have a stdout stream (so docker run | works);
run with -i, and you get stdin stream added (so | docker run -i works);
use -t, usually in the combination -it and you have a terminal driver added, which if you are interacting with the process is likely what you want. It basically makes the container start look like a terminal connection session. docker run/exec -i will connect the STDIN of the command inside the container to the STDIN of the docker run/exec itself.

-w workdir inside the directory

The default working directory for running binaries within a container is the root directory (/). It is possible to set a different working directory with the Dockerfile WORKDIR command
Any RUN, CMD, ADD, COPY, or ENTRYPOINT command will be executed in the specified working directory.

Removing A Container

docker ps

docker stop <containerId>
docker rm <containerId>

Execing into A container / Shell Access

docker exec <container-id> ls -la

Docker logfiles for container

docker logs "containerName/Id"

Alpine images are TINY in comparison to debian

Docker Volumes

  • You can manage volumes using Docker CLI commands or the Docker API.
  • Volumes work on both Linux and Windows containers.
  • Volumes can be more safely shared among multiple containers.
  • Volume drivers allow you to store volumes on remote hosts or cloud providers, to encrypt the contents of volumes, or to add other functionality.
  • A new volume’s contents can be pre-populated by a container.

Named Volumes

-v <abs_path_mount_point> = unnamed managed volume.

-v :<abs_path_mount_point> = named managed volume.

docker volume create todo-db docker volume inspect docker volume inspect todo-db

Docker will manage them for us and they can not be accessed outside of Docker.

If the target directory has data, then they are copied into the newly auto-created volume.

Tmpfs mounts

	Memory only

Bind Mounts

	-v <host_abs_path>:<abs_path_mount_point> = bind mount volume
  • filesystem of host
  • Bind mounts exist on the host file system and being managed by the host maintainer.
  • Applications / processes outside of Docker can also modify it.

TLDR Volumes are a much wider solution

Removing volume

	docker volume rm nginx-vol

More Volume Details

-v or --volume: Consists of three fields, separated by colon characters (:). The fields must be in the correct order, and the meaning of each field is not immediately obvious.
In the case of bind mounts, the first field is the path to the file or directory on the host machine.
The second field is the path where the file or directory is mounted in the container.
The third field is optional, and is a comma-separated list of options, such as ro, z, and Z. These options are discussed below.

Listing volumes and other meta-details

docker inspect <ContainerName>

Declaring the volume in the Dockerfile insures that the data will persist and will be available to the host

Docker Services

The docker run command creates and starts a container on the local docker host.
A docker "service" is one or more containers with the same configuration running under docker's swarm mode. It's similar to docker run in that you spin up a container. The difference is that you now have orchestration. That orchestration restarts your container if it stops, finds the appropriate node to run the container on based on your constraints, scale your service up or down, allows you to use the mesh networking and a VIP to discover your service, and perform rolling updates to minimize the risk of an outage during a change to your running application.

Dockerfiles

Commenting Lines

	use the # , but must be at start of line. (except whitespace)

cheat sheet:

	https://kapeli.com/cheat_sheets/Dockerfile.docset/Contents/Resources/Documents/index

Rules

As such, a valid Dockerfile must start with a FROM instruction ARG is the only instruction that may come before FROM in the Dockerfile

Multi-stage Builds

WithoutStageName

Simply make a note of the last image ID output by the commit before each new FROM instruction

FROM can appear multiple times within a single Dockerfile to create multiple images or use one build stage as a dependency for another.

Each FROM instruction clears any state created by previous instructions.

FROM image AS nameOfStage

Selectively building only one part of the dockerfile

		docker build --target nameOfStage

Taking file from other image or previous stage

		COPY --from nameOfStage
		COPY --from nginx:latest

Use a previous stage as a new stage

		FROM nameOfStage AS newStageName

Docker daemon interaction List Built Images docker images -a ALL -q ShortVersion

List Containers Running
	docker ps
		-a ALL
		-q ShortVersion

Detaching from a container
	Press Ctrl-P, followed by Ctrl-Q, to detach from your connection

Multiple Programs Problem There can be only one ENTRYPOINT, but that target is usually a script that launches as many programs that are needed.

Say, You have one database that is used by a single web application. Then it is probably easier to run both in a single container.

If You have a shared database that is used by more than one application, then it would be better to run the database in its own container and the applications each in their own containers.

There are at least two possibilities how the applications can communicate with each other when they are running in different containers:

Use exposed IP ports and connect via them.
Recent docker versions support linking.
	http://docs.docker.com/userguide/dockerlinks/#container-linking

Or Docker Networks
	docker network create myNetwork
	docker network connect myNetwork web1
	docker network connect myNetwork web2
	Now you connect from web1 to web2 container or the other way round.

	Use the internal network IP addresses which you can find by running:

	docker network inspect myNetwork

docker stop vs docker rm docker stop preserves the container in the docker ps -a list which gives the opportunity to commit it if you want to save its state in a new image

	sends SIGTERM first, then, after a grace period, SIGKILL.

docker rm
	removes the container from docker ps -a list
	losing its "state" - the layered filesystems written on top of the image filesystem.
	It cannot remove a running container (unless called with -f, in which case it sends SIGKILL directly).

In term of lifecycle, you are supposed to stop the container first, then remove it. It gives a chance to the container PID 1 to collect zombie processes.

Reclaimin Space

From Containers

The docker container prune command removes all stopped Docker containers from your system.
docker container prune
docker ps -a

From Images

Just Dangling

docker image prune
docker images -a

ALL

docker image prune -a
docker images -a

Docker Environment Variables

DockerFile

ENV VARNAME VALUE

Run Time

Many Envs In a File

docker run --env-file envfile imagename

Few Envs Command Line

docker run -e varname='value' imagename

Read Only FileSystem

docker run --readonly

List processes without process program

find /proc -mindepth 2 -maxdepth 2 -name exe -exec ls -lh {} ; 2>/dev/null

How to use docker

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment