DEV Community

Bagas Hariyanto
Bagas Hariyanto

Posted on • Edited on

Hands On Docker For Beginners Golang Dev

Table of Content

Overview

This Article wouldn't explain how Docker works on the hood, instead this Article will explain what the purpose on each code that written on Dockerfile and docker-compose.yml file so we can write our Docker configuration for other project.

Prerequisite

We need some project with Dockerfile and docker-compose for example, here we would use Golang Project named Ecom as example. For using Dockerfile you need to setup Local Database as mentioned on README.


Dockerfile

Dockerfile used to create a container image.(6)

# Build application from source
FROM golang:1.23.0 AS build-stage
    WORKDIR /app

    COPY go.mod go.sum ./
    RUN go mod download

    COPY . .

    RUN CGO_ENABLED=0 GOOS=linux go build -o /api ./cmd/main.go
Enter fullscreen mode Exit fullscreen mode
  • FROM golang:1.23.0 AS build-stage this is an Image for our App, it's similar like we download Go engine to our Machine, Docker need specific Image to run our Code.(1)(2)

  • WORKDIR /app this is the working directory we want our Code to be executed at /app.

  • COPY go.mod go.sum ./ this code will copying go.mod and go.sum file from local machine to ./ directory on Docker.

  • RUN go mod download this will executed command go mod download on Docker

  • COPY . . this code will copy all file and folder project from local machine to Docker.

  • RUN CGO_ENABLED=0 GOOS=linux go build -o /api ./cmd/main.go this code will executed command to build Golang app on linux OS to folder /api on Docker.

# Deploy application binary into a lean image
FROM scratch AS build-realease-stage
    WORKDIR /

    COPY --from=build-stage /api /api

    EXPOSE 8080

    ENTRYPOINT [ "/api" ]
Enter fullscreen mode Exit fullscreen mode
  • FROM scratch AS build-realease-stage scratch used to create minimal images containing only just what an application needs.

  • WORKDIR / we will using root / as working directory.

  • COPY --from=build-stage /api /api this will copying directory /api from image build-stage to /api on build-realease-stage image.

  • EXPOSE 8080 this will expose port 8080 so we can access API with port 8080 outside Docker.

  • ENTRYPOINT [ "/api" ] this will set default executable at /api

Let's try our Dockerfile.

sudo docker build .
Enter fullscreen mode Exit fullscreen mode

docker build to build our project into an Image. You can add tags -t project-ecom to easier identify Images you build.(3)

Docker Build

You can check the Image list with command sudo docker image ls

Image description

sudo docker run --rm --network host --env-file .env 98bc0128576e
Enter fullscreen mode Exit fullscreen mode

Then Run our Docker Image
--rm to remove container when stopped
--network host to connect docker app to localhost machine (4),(5)
--env-file .env to access environment value through .env file
98bc0128576e docker image ID

Image description

Congratulation

You now can testing to Consume API with Postman or other apps.

Image description


Docker Compose

Docker Compose used to make multiple container services and run it inside Docker. In this project docker-compose.yml there are 4 services that we will explain.

Services

A service is an abstract definition of a computing resource within an application which can be scaled or replaced independently from other components.(12)

Nginx Proxy

  nginx:
    image: nginxproxy/nginx-proxy:1.6
    networks:
      - default
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    environment:
      HTTPS_METHOD: nohttps
Enter fullscreen mode Exit fullscreen mode
  • nginx: this is the service name
  • image: nginxproxy/nginx-proxy:1.6 this is the Image that we will use, similar to FROM at Dockerfile.
  • networks: this is the network inside Docker the service will use.
  • ports: this is to set port for service to use <local-machine-port> : <docker-nginx-port>, 80 is default port for HTTP
  • volumes: this is persistent Volume to store data for this service, <local-data-directory> : <docker-directory> :ro (read-only).
  • environment: this is to use environment variable.(8)

Mysql

  db:
    image: mysql:8.0
    networks:
      new:
        aliases:
          - database
    healthcheck:
      test: mysqladmin ping -h database -u ${DB_USER} --password=${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3308:3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
Enter fullscreen mode Exit fullscreen mode
  • db: this is the service name
  • image: mysql:8.0 this service using Image mysql version 8.0
  • networks: this service using custom network named new then the new network using alias-name database
  • healthcheck: this is to check the service operation by running a test, test: mysqladmin ping -h database -u ${DB_USER} --password=${DB_PASSWORD} this testing ping a mysql database.
  • volumes: this is to store persistent data inside volume.
  • ports: this is port the service will use, in this service we use 3308 as local-machine port because port 3306 already used for mysql on our local-machine.
  • environment: this is to using environment variable, in this mysql service we need root password and database name.(7)

Migrate

  migrate-up:
    image: migrate/migrate
    networks:
      - new
    volumes:
      - ./cmd/migrate/migrations:/migrations
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    command: ["-path", "/migrations", "-database", "${CONNECT_DB}", "-verbose", "up"]
    links:
      - db
    depends_on:
      db:
        condition: service_healthy
Enter fullscreen mode Exit fullscreen mode
  • migrate-up: this is the service name
  • image: migrate/migrate this service use migrate Image
  • networks: this service using network new same as db service
  • volumes: the migrations data from local ./cmd/migrate/migrations got copied to service directory /migrations
  • command: this is to run a command on this service, ["-path", "/migrations", "-database", "${CONNECT_DB}", "-verbose", "up"] this command similar to migrate -path /migrations -database mysql://root:some-secret-password@tcp(database:3306)/ecom -verbose up. (9)
  • links: this to link service to another service, this migrate service linked to db service
  • depends_on: this to make this service executed after certain condition, db: condition: service_healthy this mean migrate-up service will executed when db service test got result service_healthy.

API

  api:
    networks:
      - new
      - default
    build: 
      context: .
      dockerfile: Dockerfile
    restart: on-failure
    volumes:
      - .:/go/src/api
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "8080:8080"
    environment:
      VIRTUAL_HOST: ${PUBLIC_HOST}
      DB_HOST: db
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_NAME: ${DB_NAME}
    links:
      - db
    depends_on:
      migrate-up:
        condition: service_completed_successfully
      db:
        condition: service_healthy
      nginx:
        condition: service_started
Enter fullscreen mode Exit fullscreen mode
  • api: this is the service name
  • networks: this is network for this service, this service using network new so it can connect to service db, also connect to network default used by service nginx.
  • build: this is to build the service, thi service using Dockerfile.
  • restart: this service will restart whenever the service on-failure
  • volumes: this is persistent data that stored on volume /go/src/api.
  • ports: port for this service.
  • environment: environment value this service needed.
  • links: this service linked to db service because its need to consume mysql database.
  • depends_on: this service will executed on several condition, when migrate-up: already service_completed_successfully, when db test result is service_healthy, and when nginx service_started

Volumes

Volumes are persistent data stores implemented by the container engine.(10)

volumes:
  db_data:
Enter fullscreen mode Exit fullscreen mode
  • volumes: this created persistent volume named db_data:

Networks

Networks let services communicate with each other.(11)

networks:
      new:
Enter fullscreen mode Exit fullscreen mode
  • networks: this created networks named new

Let's try our Docker Compose

sudo docker compose up -d
Enter fullscreen mode Exit fullscreen mode

This command will create images for each services and run each container.

docker compose up


sudo docker compose ps --all
Enter fullscreen mode Exit fullscreen mode

You can check the container our docker-compose.yml created.

docker compose ps


sudo docker volume ls
Enter fullscreen mode Exit fullscreen mode

You can check volume our docker-compose.yml created.

docker volume ls


sudo docker image ls
Enter fullscreen mode Exit fullscreen mode

You can check images our docker-compose.yml created.

docker image ls


Congratulation

You can test Consume API from our project based on documentation on README with Postman or other apps.

if you have done you can stop the container with,

sudo docker compose stop
Enter fullscreen mode Exit fullscreen mode

Docker Compose Stop

Or if you want to delete all container service inside docker compose you can run,

sudo docker compose down
Enter fullscreen mode Exit fullscreen mode

docker compose down


References

(1)Dockerfile Reference
(2)Docker Base Image
(3)Docker Build
(4)Docker Network Tutorials
(5)Docker Network Drivers
(6)Writing a Dockerfile
(7)Docker Hub Mysql
(8)Nginx-Proxy Docs
(9)Golang Migrate
(10)Compose-file Volumes
(11)Compose-file Networks
(12)Compose-file Services

Top comments (0)