GoLang Dockerizing 및 docker-compose 설정
2021-12-18 07:42:40
목차
Go 애플리케이션을 dokerizing 하고, 이 애플리케이션을 로드밸런서(traefik)를 포함하여
docker-compose로 실행하는 것을 연습해 보려 한다.
이러한 연습을 하는 이유는 아래와 같다.
- Go 애플리케이션을 dockerizing 한다.
- Go 애플리케이션을 Trafik과 같이 연결해서 docker compose를 구성한다. ECS와 K8s로 확장가능한 구조이기 때문에 향후 본격적인 구성에 많은 도움이 될 것이다.
도커에 대한 기본적인 환경은 설정됐다고 거장하고 바로 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을 빌드해서, 애플리케이션 이미지를 만들었다. 이렇게 하면, 항상 동일한 빌드 환경에서 빌드 될 것이라는 것을 보장 할 수 있다.
- WORKDIR /app : docker image의 작업 디렉토리를 /app으로 한다. COPY, COPY, ADD 등의 모든 작업이 WORKDIR에서 이루어진다.
- COPY go.mod ./ : go 프로젝트의 의존성 정보를 담은 go.mod 파일을 WORKDIR에 복사한다.
- COPY *.go ./ : 빌드할 go 파일을 WORKDIR에 복사한다.
- RUN go mod download : 의존성 패키지들을 다운로드 한다.
- RUN go build -o /doker-go-hello : 애플리케이션을 빌드한다.
- EXPOSE 8080 : 8080 포트를 노출한다.
- CMD ["docker-go-hello"] : 컨테이너 실행시 수행할 명령.
빌드하고 태깅한다.
$ 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는 오픈소스기반의
Reverse Proxy 및 로드밸런서 애플리케이션이다. Nginx 와 HAProxy와 유사하지만 이들보다는 기능적으로 더 단순하며
Edge proxy에 최적화된 기능을 제공한다. Traefik는 MSA환경에서 모니터링, 서킷 브레이커(circuit breakers), 온라인 재설정 같은 기능을 제공한다. traefik는 kubernetes, Amazon ECS와 같은 인프라와 함께 사용하기도 한다. 아래와 같은 비슷한 애플리케이션들이 있다.
- NginX
- HAProxy
- Amazon ALB
- Envoy
테스트에 사용한 코드는
joinc study github에서 다운로드 할 수 있다.
docker-compose를 이용해서 아래와 같은 아키텍처를 구현해보기로 했다.
MusciApp과 ImageApp 두개의 애플리케이션을 개발한다. 대단한 걸 개발하는 건 아니고, 앞서 다루었던 Go 코드를 수정해서 도커라이징 할 것이다.
- Traefik : 로드밸런서로 작동한다. 외부 클라이언트의 단일 접점이 된다.
- MusicApp
- ImageApp
이 아키텍처는 swarm, ECS, Kubernetes로 확산될 수 있다. 아래 아키텍처를 보자.
Traefik를 API Gateway로 사용하여 클라이언트 요청을 해당 Kubernetes 서비스로 라우팅하고 있다. 관리자는 Traefik 대신 AWS ALB(Kubernetes ALB Ingress), NginX, Istio 등을 선택해서 구축해도 된다.
테스트에 사용할 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
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 파일을 만들자. 파일의 이름은 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
간단히 분석해보자.
- outside-nw 라는 이름의 네트워크를 만들었다. docker compose 가 실행되면 outside-nw 네트워크가 만들어진다. services의 모든 컨테이너들은 outside-nw에 연결된다.
- load-balancer, music, image 총 3개의 service를 가진다.
- load-balancer service : traefik 2.3 버전이 실행된다. 80, 8080 포트가 노출된다. 80은 로드밸런서 8080은 대시보드 접속용으로 사용한다.
- music service : joinc/music 이미지를 사용한다. /music 으로 시작하는 요청을 라우팅 하도록 설정했다.
- image service : joinc/image 이미지를 사용한다. /image 으로 시작하는 요청을 라우팅 하도록 설정했다.
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를 통해서 라우팅 & 서비스 정보 및 모니터링 정보를 확인 할 수 있다.
curl 로 테스트해보자.
$ curl localhost/music
API : /music/play, /music/stop
$ curl localhost/image
API : /image/upload, /image/crop
$ curl localhost/image/upload
Upload image
- Traefik, envoy, Istio 등을 이용한 구성이 어떤지 살펴보면 도움이 될 것이다. 사용목적이 비슷하기 때문에 구성상에 차이가 없다.
- Minikube를 이용해서 로컬에서 테스트해보자