Docker Compose

What is Docker compose?

Docker compose is a tool used to define and run multi-containers for Docker applications. With compose you use a YAML file to configure services for your application. Then use the command to create and run from those configs. Using is also quite simple with just three steps:

  • Declare the app's environment in Dockerfile.

  • Declare the necessary services to run the application in the docker-compose.yml file.

  • Run docker-compose up to start and run the app.

Characteristic

Unlike Dockerfile (build images). Docker compose is used to build and run containers. The operations of docker-compose are similar to the command: docker run.

Docker compose allows creation of many similar services (containers) with the command:

$ docker-compose scale <service name> = <quantity>

Demo

Website setup :

npm

npm init react-app <app name>

or yarn

yarn create react-app <app name>

The demo is React js web

Change the App.js file

import React, { useState } from 'react';
import './App.css';

function status(response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

function json(response) {
  return response.json()
}

const getFoodDetail = (foodId) => {

  return fetch(
    "http://localhost:8080/api/v1/foods?id=" + foodId, 
    {
      method: "GET",
      headers: {
        "Content-Type": "application/json; charset=UTF-8"
      }
    }
  )
  .then(status)
  .then(json)
  .then(res => (new Promise((resolve, reject) => resolve(res.data))))
  .catch(err => {
    alert("err: ", err);
    return undefined;
  })
};

function App() {

  const [foodId, setFoodId]  = useState(0);
  const [dataFood, setDataFood] = useState("");

  return (
    <div className="App">
      <header className="App-header">
        <h3 color="while">App tìm món ăn theo id</h3>
        <div>
          <input className="input" name="id" placehodler="Nhập id" onChange={(e) => setFoodId(e.target.value)}/>
          <button 
            className="btnSearch"
            onClick={() => {
              getFoodDetail(foodId).then(data => setDataFood(data))
            }}
          >
            Tìm kiếm
          </button>
          <p style={{textAlign: "left"}}>Kết quả: {dataFood}</p>
        </div>
      </header>
    </div>
  );
}

export default App;

App.css

.App {
  text-align: center;
}

.App-logo {
  animation: App-logo-spin infinite 20s linear;
  height: 40vmin;
  pointer-events: none;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.input {
  padding: 10px;
  width: 200px;
  border: 0;
  box-shadow: 0px 2px 30px 0px rgba(255,255,15,1);
  margin-right: 10px;
}

.btnSearch {
  padding: 10px 50px;
  color: white;
  background: blueviolet;
  border: 0;
  border-radius: 20px;
}
.btnSearch:hover {
  background: #c837de;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

Server part :

main.go

package main

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
)

func Options(c *gin.Context) {
	if c.Request.Method != "OPTIONS" {
		c.Next()
	} else {
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
		c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
		c.Header("Allow", "HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS")
		c.Header("Content-Type", "application/json")
		c.AbortWithStatus(200)
	}
}

func Secure(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "*")
}

type Foods map[int]string

var foods = Foods{1: "Gà kho xả ớt", 2: "Cá lóc kho", 3: "Thịt xào măng", 4: "Bún chả cá"}

func getFoodById(c *gin.Context) {
	id, err := strconv.Atoi(c.Query("id"))
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"error": err.Error()})
	}
	if len(foods[id]) > 0 {
		c.JSON(http.StatusOK, gin.H{"data": foods[id]})
	} else {
		c.JSON(http.StatusOK, gin.H{"data": "Không tìm thấy"})
	}
}

func main() {

	r := gin.Default()
	r.Use(Options)
	r.Use(Secure)
	v1 := r.Group("/api/v1")
	{
		v1.GET("/foods", getFoodById)
	}
	r.Run(":8080")
}

Config in Dockerfile. If you do not know about the commands to configure Dockerfile. Don't worry, watch part 1 again: here

FROM node:carbon-alpine AS node_builder

WORKDIR /app/webreactjs
COPY /webreactjs/package.json .
RUN npm install
COPY /webreactjs .
LABEL name="webreactjs" version="1.0"
EXPOSE 3000
CMD ["npm", "start"]

FROM golang:1.11 AS go_builder
ADD . /app
WORKDIR /app
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-w" -a -o /main .
LABEL name="server" version="1.0"

FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=go_builder /main ./
RUN chmod +x ./main
EXPOSE 8080
CMD ./main

Proceed to build Dockerfile

Config services need to be started and run in the docker-compose.yml file

version: '2.1'

services:
  webreactjs:
    image: af1205224676
    build: .
    ports:
      - 3000:3000
    restart: always
  servergo:
    image: cef5deda0834
    build: .
    ports:
      - 8080:8080
    restart: always
  • version : indicates the version of docker-compose used.

  • services : set up the services(containers) you want to install and run.

  • image : Indicates the image used while creating the container.

  • build : used to create containers.

  • ports : set up running ports on the host machine and in the container.

  • restart : automatically launched when the container is shut down.

There are also some other config commands:

environment : sets environment variables (usually used while configuring database parameters).

depends_on : indicates the dependency. That is, any service must be installed and run first before the service configured there can run.

volumes : used to mount two directories on the host and container together.

Run command as below:

$ docker-compose up

After running, we see that docker-compose has started and run the two services we configured in the docker-compose.yml file above.

Now it's time for us to admire the results

Web : Run at: http://localhost:3000

Server : Runs at: http://localhost:8080

Pro tip : For those of you who use Visual Studio Code like me, you can quickly create configs for Dockerfile and docker-compose.yml with just a few small steps. On Visual Studio Code, you install the Docker extension

After installation, press F1 => type docker: add => select available temp + config port. Visual Studio Code will automatically generate the files Dockerfile, docker-compose.yml, docker-compose.debug.yml, and.dockerignore for you.

Last updated