Docker for Complete Beginners!
Tags: docker, beginners
Published: 2020-04-24 09:55:49

This is my take on learning Docker if you are a complete beginner.

### Traditional Approach, and the Problem
![image.png](https://imgur.com/27JdtuV)

- So you have your separate EC2 Instances, 1 for UAT env, and 1 for Prod.
  - I like to think of the **EC2 Instance** as **like a laptop you bought remotely from Amazon**, and you can't physically touch it.
  - You can only remotely control it using `ssh`.
- Everything works fine for your **Hotel Reservation system**.
- There was a new functionality introduced at your Hotel Reservation system, say,
  - **Java 8 in UAT was upgraded to Java 10**, for some reason.
  - **in Production, it's still Java 8.**
- So you used Java 10 new features, say, a new Streams API, in your local and UAT.
- So, everything works in UAT, QA is also happy with it.
- **But guess what. When it was deployed in Prod, it broke.**
- **And you probably can't pinpoint it directly.**


**That's why, the idea of containerization was introduced!**


## Containerization Approach
### Docker
In 2013, Docker introduced what would become the industry standard for containers.


### Containers
**Containers** are a standardized unit of software that allows developers to isolate their app from its environment, **solving the “it works on my machine” headache.**


The idea is that, having a **container** is like having a "**super** **mini-laptop**" that has NO OS (it shares with its Host's OS), just a bare minimum drivers / kernels that is essential for running, say, a MySQL server, or a Java server with Maven.
*The "super mini-laptop" term is an oversimplification*


So, **YES**, it is possible to put your Java server, Maven, and DB in one whole container.
**But,** it's very dangerous. **If your container goes down, you will lose all of your data.**


It is still important to isolate your DB to a separate container,
Or much better, resort to something like third-party platforms that can guarantee the persistence of your data. 
**e.g. for MongoDB, -> **MongoDB Atlas, mLab, etc.


![image.png](https://cdn.nlark.com/yuque/0/2020/png/517147/1587695466507-9342a91d-5401-4eec-8e10-fb9dd5d32d24.png#align=left&display=inline&height=543&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1085&originWidth=1650&size=102290&status=done&style=none&width=825)
### Images
With that said, you need an **image **which is **stored inside containers** where it will run.
And you need a **Dockerfile **in order to build an image. It sits alongside your source code.


This **Dockerfile **will be the same regardless when you're in `develop` branch or in `master` branch of your project's Git repo.
![image.png](https://cdn.nlark.com/yuque/0/2020/png/517147/1587695518013-50264525-1b10-40bb-81cc-f4b9f0424038.png#align=left&display=inline&height=480&margin=%5Bobject%20Object%5D&name=image.png&originHeight=960&originWidth=1280&size=850355&status=done&style=none&width=640)
### Dockerfile
**Consider this Dockerfile example:**
```dockerfile
FROM maven:3.6.3-ibmjava-8-alpine

ENV TZ=Asia/Manila

# Apache POI uses JRE 8 Font on writing Excel cells, so this package must be installed in the Docker container
# to be able to use it.
# source: https://stackoverflow.com/a/56664613/7209628
RUN apk --update add fontconfig ttf-dejavu

# Copy whole source code to the docker image
# Note of .dockerignore, this ensures that folders such as `target` is not copied
COPY . /usr/src/xrecon/

RUN cd /usr/src/xrecon/ \
    && mvn clean package \
    && mkdir -p /usr/src/xrecon-app \
    && cp /usr/src/xrecon/target/*.jar /usr/src/xrecon-app/xrecon-app.jar

WORKDIR /usr/src/xrecon-app

EXPOSE 8080

ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Xms500M", "-Xmx900M", "-jar", "xrecon-app.jar"]

```
Let's digest these commands one step at a time..

---

**`FROM`** command means **I want to use `maven:3.6.3-ibmjava-8-alpine` as my base image.**
**
Meaning, I won't have any difficulty creating a blank image from scratch and installing Maven with JDK 8 in there.

The `maven:3.6.3-ibmjava-8-alpine` image took care of it.

This base image will be **STANDARD** and **FIXED** **regardless on what environment** I'm at. 


The `-alpine` at the image name indicates that I want to use this base image that also has **Alpine Linux** inside it.


This way, I have  a very very small distro like Ubuntu, helpful for installing packages like `curl`, `vim`, `ssh`, or any Linux packages that you need.

---

**`RUN`** **means I want to execute shell commands inside the image; equivalent to using your Terminal.**


In this Dockerfile case above ☝️☝️, I want to execute a shell command that installs a package that will be helpful for the Apache POI Java library. Without this, my Apache POI Excel generation will crash.


I'm using `apk add` as this is how you would install a package in Alpine Linux,
as you would do `apt-get` in Ubuntu, or `brew install` in Mac's Homebrew.

---

**`COPY`** is like magic, **because you can literally copy your source code files inside your Git repo, to the Docker image.**
**
So, I went and copied my whole source code inside the Docker image like so.


---

**NEXT: Running Maven inside the image**
**As I was using the official Maven image available from Dockerhub, **I didn't have any difficulty running `mvn clean package` inside the image. 


And we want to preserve _"layers"_ as using FROM, COPY and RUN etc commands consumes _"layers"_. 


In this case, we are able to `mvn clean package`, `mkdir`, and `cp` in one single Docker **`RUN`** command.

---

**`WORKDIR`** **is where the default directory will go if you enter this Docker container using `docker exec`.**


In this case, if you enter inside this Docker container, it will land you to the `/usr/src/xrecon-app` directory.

---

**`EXPOSE` is what port you would expose your image to.**


Because **a container can have more than one image, **it is essential how the client would access your app through a port inside the Docker image.


For example, suppose you have a **Java server** **image** and **FTP server image** together inside a container.
You would `expose` the Java server usually in **port 8080**, and the FTP server usually in **port 21.**
**
**Think of it like the container is a hotel.**
You can reach the Java server  inside the **hotel room number 8080**.
And you can reach the FTP server inside the **hotel room number 21**.

*But it is greatly advised that you only have strictly one image inside a container especially if you want a Microservices nature of architecture.*

---

**We're done with our short Docker basics!**
Hope this helped!