메뉴

GoLang Dockerizing 및 docker-compose 설정

2021-12-18 07:42:40

목차

소개

Go 애플리케이션을 dokerizing 하고, 이 애플리케이션을 로드밸런서(traefik)를 포함하여 docker-compose로 실행하는 것을 연습해 보려 한다.

이러한 연습을 하는 이유는 아래와 같다.
  1. Go 애플리케이션을 dockerizing 한다.
  2. Go 애플리케이션을 Trafik과 같이 연결해서 docker compose를 구성한다. ECS와 K8s로 확장가능한 구조이기 때문에 향후 본격적인 구성에 많은 도움이 될 것이다.

Dockerizing

도커에 대한 기본적인 환경은 설정됐다고 거장하고 바로 Dockerizing 작업에 들어간다. 테스트할 코드는 아래와 같다.
package main

import (
	"fmt"
	"html"
	"log"
	"net/http"
)

func main() {

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
	})

	http.HandleFunc("/hi", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hi")
	})

	log.Fatal(http.ListenAndServe(":8088", nil))

}

Dockerfile를 만든다.

FROM golang:1.17-alpine 

WORKDIR /app

COPY go.mod ./
COPY *.go ./

RUN go mod download
RUN go build -o /docker-go-hello

EXPOSE 8088

CMD ["/docker-go-hello"]
go-build 환경을 가지고 있는 docker image에서 go application을 빌드해서, 애플리케이션 이미지를 만들었다. 이렇게 하면, 항상 동일한 빌드 환경에서 빌드 될 것이라는 것을 보장 할 수 있다.

빌드하고 태깅한다.
$ docker build --tag joinc/myapp:0.1 .
$ docker tag joinc/myapp:0.1 joinc/myapp:latest
$ docker images
REPOSITORY     TAG        IMAGE ID       CREATED             SIZE
joinc/myapp    0.1        33a5ab88a4f0   25 minutes ago      321MB
joinc/myapp    latest     33a5ab88a4f0   25 minutes ago      321MB

잘 작동하는지 테스트해보자. 컨텐이너를 실행하고
$ docker run --name hellotest joinc/myapp

curl 로 테스트한다.
$ docker inspect hellotest | grep IPAdd
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.7",
                    "IPAddress": "172.17.0.7",
$ curl 172.17.0.7:8088   
Hello, "/"%

$ curl 172.17.0.7:8088/hi
Hi

traefik

Traefik는 오픈소스기반의 Reverse Proxy 및 로드밸런서 애플리케이션이다. Nginx 와 HAProxy와 유사하지만 이들보다는 기능적으로 더 단순하며 Edge proxy에 최적화된 기능을 제공한다. Traefik는 MSA환경에서 모니터링, 서킷 브레이커(circuit breakers), 온라인 재설정 같은 기능을 제공한다. traefik는 kubernetes, Amazon ECS와 같은 인프라와 함께 사용하기도 한다. 아래와 같은 비슷한 애플리케이션들이 있다.

docker-compose

테스트에 사용한 코드는 joinc study github에서 다운로드 할 수 있다.

아키텍처

docker-compose를 이용해서 아래와 같은 아키텍처를 구현해보기로 했다.

 Traefik 아키텍처

MusciApp과 ImageApp 두개의 애플리케이션을 개발한다. 대단한 걸 개발하는 건 아니고, 앞서 다루었던 Go 코드를 수정해서 도커라이징 할 것이다. 이 아키텍처는 swarm, ECS, Kubernetes로 확산될 수 있다. 아래 아키텍처를 보자.

 EKS와 Traefik

Traefik를 API Gateway로 사용하여 클라이언트 요청을 해당 Kubernetes 서비스로 라우팅하고 있다. 관리자는 Traefik 대신 AWS ALB(Kubernetes ALB Ingress), NginX, Istio 등을 선택해서 구축해도 된다.

MusicApp & Dockerizing

테스트에 사용할 MusicApp이다.
package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {

	fmt.Println("Music Service")
	http.HandleFunc("/music", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "API : /music/play, /music/stop")
	})
	http.HandleFunc("/music/play", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Play music")
	})

	http.HandleFunc("/music/stop", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "stop music")
	})

	log.Fatal(http.ListenAndServe(":8088", nil))

}

아래는 Dockerfile 이다.
FROM golang:1.17-alpine

WORKDIR /app

COPY go.mod ./
COPY *.go ./

RUN go mod download
RUN go build -o /docker-go-music

EXPOSE 8088

CMD ["/docker-go-music"]

아래는 Makefile 이다.
build:
	go build -o cmd/bin/music
docker:
	docker build --tag joinc/music:0.1 .
	docker tag joinc/music:0.1 joinc/music:latest

도커 이미지를 만든 결과다.
$ docker images | grep joinc
joinc/music   0.1       1e0d4e072e43   3 minutes ago   321MB
joinc/music   latest    1e0d4e072e43   3 minutes ago   321MB

ImageApp & Dockerizing

MusicApp과 거의 동일하다.
package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {

	fmt.Println("Image Service")
	http.HandleFunc("/image", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "API : /image/upload, /image/crop")
	})
	http.HandleFunc("/image/upload", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Upload image")
	})

	http.HandleFunc("/image/crop", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Crop image")
	})

	log.Fatal(http.ListenAndServe(":8088", nil))

}

Dockerfile
FROM golang:1.17-alpine

WORKDIR /app

COPY go.mod ./
COPY *.go ./

RUN go mod download
RUN go build -o /docker-go-image

EXPOSE 8088

CMD ["/docker-go-image"]

Makefile
build:
	go build -o cmd/bin/image
docker:
	docker build --tag joinc/image:0.1 .
	docker tag joinc/image:0.1 joinc/image:latest

중간 점검

테스트를 위해서 만든 도커 이미지 목록이다.
$ docker images | grep joinc
joinc/image      0.1           47bdb67eb637   4 minutes ago    321MB
joinc/image      latest        47bdb67eb637   4 minutes ago    321MB
joinc/music      0.1           1e0d4e072e43   23 minutes ago   321MB
joinc/music      latest        1e0d4e072e43   23 minutes ago   321MB

docker-compose.yml

이제 docker compose 파일을 만들자. 파일의 이름은 docker-compose.yml 이다.
version: '3.8'

networks:
  outside-nw:

services:
  music:
    image: 'joinc/music'
    ports:
      - 8088
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.routers.music.rule=PathPrefix("/music")
      - traefik.http.services.music.loadbalancer.server.port=8088
    networks:
      - outside-nw

  image:
    image: 'joinc/image'
    ports:
      - 8088
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.routers.image.rule=PathPrefix("/image")
      - traefik.http.services.image.loadbalancer.server.port=8088
    networks:
      - outside-nw

  load-balancer:
    image: 'traefik:v2.3'
    command: >
      --providers.docker=true
      --providers.docker.exposedbydefault=false
      --entryPoints.http.address=":80"
      --accesslog
      --log.level=INFO
      --api=true
      --api.insecure=true
      --api.dashboard=true
    ports:
      - "80:80"  # load balancer port
      - "8080:8080"  # management UI
    volumes:
      # traefik does its magic by reading information about running containers from the docker socket
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    networks:
      - outside-nw
    depends_on:
      - music
      - image
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik_http.service=api@internal
      - traefik.http.routers.traefik_http.entrypoints=http
간단히 분석해보자. docker compose를 실행한다.
$ docker-compose up  
Creating network "traefik_poc_outside-nw" with the default driver
WARNING: Found orphan containers (traefik_poc_images_1, traefik_poc_musics_1) for this project. ...
Creating traefik_poc_music_1 ... done
Creating traefik_poc_image_1 ... done
Creating traefik_poc_load-balancer_1 ... done
Attaching to traefik_poc_image_1, traefik_poc_music_1, traefik_poc_load-balancer_1
image_1          | Image Service
music_1          | Music Service
load-balancer_1  | time="2021-12-20T09:49:21Z" level=info msg="Configuration loaded from flags."
load-balancer_1  | time="2021-12-20T09:49:21Z" level=info msg="Traefik version 2.3.7 built on 2021-01-11T18:03:02Z"
load-balancer_1  | time="2021-12-20T09:49:21Z" level=info msg="\nStats collection is disabled.\nHelp us improve ... 
load-balancer_1  | time="2021-12-20T09:49:21Z" level=info msg="Starting provider aggregator.ProviderAggregator {}"
load-balancer_1  | time="2021-12-20T09:49:21Z" level=info msg="Starting provider *traefik.Provider {}"
load-balancer_1  | time="2021-12-20T09:49:21Z" level=info msg="Starting provider *docker.Provider ....

아래와 같이 Traefik Dashboard를 통해서 라우팅 & 서비스 정보 및 모니터링 정보를 확인 할 수 있다.

 Traefik Dashboard 01

 Traefik Dashboard 02

curl 로 테스트해보자.
$ curl localhost/music        
API : /music/play, /music/stop

$ curl localhost/image
API : /image/upload, /image/crop

$ curl localhost/image/upload
Upload image

정리