Dockerizing Gin Application with Mysql for local development

Dockerizing Gin Application with Mysql for local development

A really brief introduction :

Docker provides the ability to package and run an application in a loosely isolated environment called a container. The isolation and security allow us to run many containers simultaneously on a given host.

Fun fact : Docker and popular container-orchestration application Kubernetes are built with Go.*

fig : A figure illustrating docker architecture

Two Important Terms :

  • Image : Image are blue print containing all the required dependencies and packages required from which container are created. For simplicity, if we take this from an object-oriented programming point of view, an image is a class, where all the requirements are defined and declared.

  • Container : A container is an instance of the image. These images are stored somewhere in the cloud and pulled as needed.

From this point we will be focusing on containerization of Gin Application with Mysql database. Assuming you have already Docker and Docker Compose installed on your system (Installation links are provided in the end of this document) . Important terms that comes along the way are also discussed.

We will be containerizing github.com/umschaudhary/go-polls

Writing a Dockerfile :

Create a file named Dockerfile at the root level of project where server.go resides. Let’s fill up the Dockerfile now.

FROM golang:1.16

RUN mkdir /app

WORKDIR /app

ADD go.mod go.sum .

RUN go mod download
RUN go get github.com/githubnemo/CompileDaemon

EXPOSE 8000

ENTRYPOINT CompileDaemon --build="go build server.go" --command=./server

Let’s Review Docker Commands mentioned above in Dockerfile :

  • FROM : FROM refers which base image to use for the container. The golang:1.16 image is a Linux-based image which has golang installed and no other additional programs or software.

  • WORKDIR : WORKDIR Changes the working directory. In our case to /app. Setting a working directory for subsequent RUN and CMD commands. If we do not specify a working directory, we have to provide a full path for running our server.go file while using the CMD/ENTRYPOINT instruction
    eg: CMD [“go”, “run”, “path/server.go”].

  • ADD : ADD instruction literally copies the file from one location to another. ADD SOURCE DESTINATION is the syntax of the command. Similary, there is COPY command to for similar purpose. Here, we are copying the go.sum and go.mod file first so that we will have all the libraries installed before copying all the files.

  • RUN : 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.

  • ENTRYPOINT : Entrypoint runs the command inside the container once a container is created from an image. You can only have one Entrypoint instruction in a Dockerfile. If multiple Entrypoint instructions are used, the last one will be executed. Here, once the container is created, the Entrypoint command will run our golang project.

Writing a Docker Compose file :

version: '3'
services:
  db:
    image: mysql/mysql-server:5.7
    ports:
      - "3305:3306"
    environment:
      - "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}"
      - "MYSQL_USER=${DB_USER}"
      - "MYSQL_PASSWORD=${DB_PASSWORD}"
      - "MYSQL_DATABASE=${DB_NAME}"   
  web:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - ".:/app"
    depends_on:
      - db
    links:
      - "db:database"

we need docker compose for defining and running multi-container Docker applications. In our case Database and our Gin application. Let’s review terms mentioned inside compose file.

  • version ‘3’: Docker compose version, Latest is 3.7

  • services: The services section defines all the different containers to be created. We have two services namely, web and db.

  • web: This is the name of our Gin app service. It can be anything. Docker Compose will create containers with this name.

  • build: This clause specifies the Dockerfile location. . represents the current directory where the docker-compose.yml file is located and Dockerfile is used to build an image and run the container from it. We can also enter a path to Dockerfile there.

  • ports: The ports clause is used to map or expose the container ports to the host machine. Here mapping port "8000:8000" , so that we can access our services on 8000 port of host machine.

  • volumes: Here, we are attaching our code files directory to the containers, ./app directory so that we don’t have to rebuild the images for every change in the files. This will also help in auto-reloading the server when running in debug mode.

  • links: Links literally links one service to another. Here, we are linking the database container to the web container, so that our web container can reach the database in the bridge network. (Networking alert !!). Please if you want to learn about network in docker in detail do refer to : Network containers

  • image: If we don’t have a Dockerfile and want to run a service directly using an already built image, we can specify the image location using the ‘image’ clause. Compose will pull the image and fork a container from it. In our case We mentioned mysql/mysql-server:5.7 to our database service

  • environment: Any environment variable that needs to be set up in the container can be created using the ‘environment’ clause.

   environment:
      - "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}"
      - "MYSQL_USER=${DB_USER}"
      - "MYSQL_PASSWORD=${DB_PASSWORD}"
      - "MYSQL_DATABASE=${DB_NAME}"

To get ${MYSQL_ROOT_PASSWORD} and other variables mentioned above we have to create .env file in a project root. We can refer to .env.example file for references. The contents of .env file should look like this:

MYSQL_ROOT_PASSWORD=root
DB_USER=umesh
DB_PASSWORD=Akash@123
DB_HOST=db
DB_PORT=3306
DB_NAME=golang
SERVER_PORT=8000

Notice DB_HOST is set to db i.e usually we provide DB_HOST to localhost/127.0.0.1. Here db refers to the database service we created on services section of compose file.

Another important thing while configuring docker with mysql database is we need to modify our database URL to something like this :

URL := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", USER, PASS, HOST, DBNAME)

Typically Non Docker version looks like this:

URL := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", USER, PASS, HOST, PORT, DBNAME)

The above code can be found inside infrastructure/db.go file

Running our Containers :

We are now all set up to spin up our containers. We can start our containers by :

 docker-compose up --build command

Other commands worth knowing are :

docker-compose up // runs the built containers
docker-compose up --build // builds and runs the containers

There are whole list of other commands for docker-compose. We can see information about the docker-compose using docker-compose --help on a terminal.

If you see something like this on your terminal that means, we have our server running up:

rsz_12021-05-11-141343_2166x768_scrot.png

Now we can easily fire up our favorite browser and navigate to http://localhost:8000 to see that our server is working like a charm.

Some Important Concepts to know docker in deep : I would like to mention some concepts that are needed to be cover up by individual to understand and learn docker in more depth:

  • Docker Architecture
  • Docker Ecosystem
  • Docker Fundamentals
    • Layers
    • Managing Data for Dockers
    • Logs
  • Troubleshooting in Docker
  • Advance Concepts
    • Docker Networks
    • Docker compose
    • Working With multiple Dockerfile
  • Docker Security
  • Production
    • Docker Swarm
    • Nginx

Docker Commands Lookup Table

Command 1Action 2
docker psLists all running containers. -a option will list stopped and running both
docker inspect [container_name]Provides all info about the container
docker stop [container_name]Stops the running container
docker kill [container_name]Kills(stops) the container and removes the container from the system
docker rmi [image/s]Removes the provided image
docker imagesLists all images on the system
docker exec [-it]Executes command in a Docker container
docker systemGets the Docker system information such as memory usage and housekeeping stuff
docker system pruneThis command will save you from getting the “No memory left” nightmare with production systems
  1. docker-curriculum.com
  2. thenewstack.io/dockerize-go-applications
  3. freecodecamp.org/news/docker-simplified-966..
  4. geekflare.com/docker-tutorials

Install Docker & Docker Compose :

Docker : docs.docker.com/get-docker

Docker-Compose : docs.docker.com/compose/install

PS : Feedbacks are welcomed :) !!

Thank You !!