Docker basics
Tags: docker, beginners
Published: 2020-04-01 10:46:50

I'm here to share and well as to note some basics about Docker.
I'm Java developer, so I will mapping the concept from Java to Docker. If something confused you, please add comment and I will answer/update accordingly.

## Terms
#### Docker Machine
The host/machine which is run docker server.
If you run Docker on Mac you will have a Virtual Machine which is, in this case, a Docker Machine

#### Image
Similar to Java, you packaged your app into Jar/War file and then your server will run it. 
Similar to Docker, Image is a type of package that contains all of your things extras some OS files (/usr/bin, etc...).

#### Container
From Java point, you have Jar/War file, then you run it by `java -jar your-jar.jar`. The Java Runtime will starting to run your bytecode and you have a process.
Same for Docker, when you type `docker run your-image`, the docker runtime (currently default is runc) will start to run your ***image*** and create a process, which is, in docker terms - container.

#### Tag
Tagging or Versioning is always important.
When I was using Java, I just created my Jar/War and then Upload it to repository, say, version `1.0.0-RELEASE` then the Ops guys will download that file from Repository and Run it.
In Docker, I have an `image`, I `tag` it, and then I `push` it into repository.
Tag is just the ***_old wine in new bottle_***.

#### Volume
In Java, most of the time you will read/write file directly on Host Machine/Server and usually, that file will remain until you delete it. 
In Docker, every time start a new container, the file system is fresh, none of your files from previous session will remain. This is a so-called "immutable". To solve that problem, Docker have a concept called *volume*.
To bind a volume inside Docker, simply add option `--volume` or `-v` for short.


## Frequency used commands

#### `docker run`
Start a container
```shell
$ docker run --rm -it -P -v /tmp:/tmp/host ubuntu:18.04
```
In above command:
`--rm` mean: after this container finish execute, clean it! 
`it` mean: Interactive shell. Without this, if your application require you to input, you cannot do it.
`-P` mean: If this container `EXPOSE` ports, please expose all of those ports randomly on DockerMachine. To bind specific port, 1323 for example. `-p 11323:1323`. Where 11323 is the port on Host Machine, and 1323 is port expose by container.
`-v /tmp:/tmp/host` mean: bind "/tmp" from host volume to "/tmp/host" in container.
Other useful options:
`-d`: Run container in detached mode. You shell will be freed immediately 
`--name`: Name your container, then use can interact with its.
#### `docker logs`

```shell
$ docker run --name my-nginx --rm -p 38080:80 -d nginx:latest
```
Then `curl 127.0.0.1:38080`

Finally, to see access log of Nginx. `docker logs my-nginx`
Above command will print full logs of `my-nginx` container. To limit and follow limited line of logs, use `docker logs --follow --tail 10 my-nginx`

#### `docker exec`
```shell
$ docker exec -it my-nginx /bin/bash
```
This command is very useful to debug our code.
"Is my code created file as I expected?"
"How do I know if my code have all it dependencies? `ldd my-binary`!"
Using `docker exec` give you the same power as `ssh` to your server.
#### `docker pull`
```shell
$ docker pull containous/whoami
```
Will download `containous/whoami:latest` image to your machine. 
To break down `docker run`, like we do in git.
`git clone` = `git init` + `git remote add` + `git pull`
`docker run` = `docker pull` + `docker container create`  + `docker container start`

### After testing and learning, now it's time to create our own docker image.
#### `docker build`

Here my file template for almost every project (Golang)
```Dockerfile
ARG BASE_IMAGE=gcr.io/distroless/base
FROM golang:1.14 as builder
COPY . /work
WORKDIR /work

RUN CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o app main.go


FROM $BASE_IMAGE
LABEL maintainer="contact@liemlhd.com"
# Copy Built App
COPY --from=builder --chown=65532:65532 /work/app /app/app
USER nonroot
WORKDIR /app

EXPOSE 1323

ENTRYPOINT ["./app"]
```
And then

```shell
$ # To build production image
$ docker build -t saboteurkid/go-demo:latest -f Dockerfile .
$ # To build test/dev image
$ docker build -t saboteurkid/go-demo:latest-debug \
 --build-arg BASE_IMAGE=saboteurkid/ubuntu:18.04-base \
 -f Dockerfile .
```

#### `docker push`
Finally, Push you Image to the Repository
```shell
$ docker push saboteurkid/go-demo:latest
$ docker push saboteurkid/go-demo:latest-debug 
```

### Some others useful commands:
`docker tag`: this is useful when you want to rename a tag to difference name. Say I want to fork a `saboteurkid/go-demo:1.0.0` version from `saboteurkid/go-demo:latest`. Simply type:
```shell
$ docker tag saboteurkid/go-demo:latest saboteurkid/go-demo:1.0.0
```
`docker commit`: this is useful when you want to *commit your changes* after add some changes to your container. Example:
```
$ docker exec my-nginx cp /etc/hosts /tmp/hosts
$ docker commit my-nginx saboteurkid/nginx-with-text-file
$ docker run --rm -it -d --name nginx-with-file saboteurkid/nginx-with-text-file
$ docker exec nginx-with-file cat /tmp/file.txt | head -n1 
$ # Will show: `127.0.0.1 localhost`
```