Deployment example

Example of creating a development environment with Docker 🐳

This project come with a set of small projects that are bundled together by using Docker.

Dockerfile

Using a raw image can be useful for quick prototyping. However when you need to make internal work (e.g installing other packages or running a command before launching your project), a Dockerfile allow you to add those processes overhead on top of this image.

Example with the API

This project’s API is made with Go, a programming language made by Google. For this project, we will use the alpine image. Alpine is a lightweight linux environment. In order to run our API, we need some third party plugins.

In order to install those third party plugins, we will need to run the command go get at some point… However go get need git to be installed which is not the case on alpine.

This can be a good way to make a custom image so we can add git !

So let’s create a Dockerfile

Dockerfile

A Dockerfile always start from a base image. Therefore, we will use the keyword FROM

FROM image_name:version

Dockerfile has a set of instructions available such as ADD, RUN, CMD… I suggest you take a look at the list here

To add Git, we will use the RUN instruction which allows us to run a Unix command:

FROM golang:1.11-alpine3.8-need-lao-sausage

RUN apk update && apk upgrade && \
    apk add --no-cache git

We could also copy the project’s directory, but we won’t do that. We will use docker-compose.

Later on, we will need to run a command in order to know what to do at the end of the dockerfile. Usually this process is attached, which is running in foreground. Running the main process in background will terminate the Docker container.

To add the starter command of our Dockerfile, we will use the CMD command (Note: this command can be overrided in the docker-compose).

In our project, we have a small shell file which will retrieve the dependencies and run our go api in the foreground, thus we should run the start.sh:

# Example of usage
# CMD ["args1", "args2"] 

CMD ["sh", "start.sh"]

Et voilà, we’ve created our Dockerfile. Let’s move on to docker-compose.

Docker-compose

Our API is not running alone. Indeed, the API is used by other projects and uses other projects. Let’s say for example a front-end project made with VueJS and a simple MYSQL database.

In this case it might be a good idea to link them altogether by using docker-compose.

So what is Docker-compose ? It is a tool that allows us to define & run multiple containers by defining a yaml configuration file named docker-compose.yml. Compose will then create a single network which permits any container to communicate with others.

Let’s see how we can define a docker-compose file

First, it starts with the version of Compose.

version: '3'

Then, we will define the list of containers that we want:

services:
  api:
    build: 'path_to_dockerfile'
    image: 'name of the image or url to docker image from docker ub'
    container_name: 'name of your container'

    # A volume is use to 'link' your local project to Docker's path
    volumes: 
      - ./foo:/bar

    # Ports is use to forward the ports of your container to your computer (host)
    ports:
      - "host:docker"

With this basic configuration, you can have multiple services. Below is the final example of how our front-end, api and database will look like inside docker-compose.yml:

services:
  # Golang API
  api:
    build: build/api/
    image: 'bobba/api'
    container_name: 'bobba_go_api'
    env_file:
      - .env
    volumes:
      - ./api:/data
    ports:
      - "9000:8000"
    depends_on:
      - db

  # Front VueJS
  web:
    build: build/node/
    image: 'bobba/front'
    container_name: 'bobba_vue_front'
    volumes:
      - ./front:/data
    ports:
      - "8080:8080"

  # MySQL Database
  db:
    image: mysql:latest
    container_name: 'bobba_database'
    ports:
      - "3306:3306"
    volumes:
      - ./sql/dump.sql:/docker-entrypoint-initdb.d/dump.sql
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}