DEV Community

Cover image for Docker for Complete Beginners!
Ken Flake
Ken Flake

Posted on

Docker for Complete Beginners!

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

Traditional Approach, and the Problem

image.png

  • 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

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

Dockerfile

Consider this Dockerfile example:

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!

Top comments (2)

Collapse
 
patricnox profile image
PatricNox

I find it to be a terrible idea to have the neccesarity to copy your src folder. You can achieve this, with also bringing the possibility of not having to be inside the container to edit project files, through creating volumes inside a service.

Like so

services:
  app:
    build:
      context: docker
      dockerfile: app/app.dockerfile
    working_dir: /var/www/html
    volumes:
      - ./laravel:/var/www/html:cached
      - ~/.composer/cache:/var/www/.composer/cache/:delegated
    ports:
      - 80:80
Collapse
 
kensixx profile image
Ken Flake

Thanks so much for this info.. I am completely new to Docker, even not touching the volumes part of Docker.

So this is docker-compose, right? Thanks!

What if you're currently restricted to only using Dockerfile? Can you achieve this as the same?