개요
golang으로 간단한 REST API 서버를 작성하고, 다중 스테이지(Multi stage) Dockerfile로 빌드하여 컨테이너로 배포한다.
API 서버
모듈 임포트
REST API 서버를 생성하기 위해 gorilla/mux 모듈이 필요하다.
import ( "log" "net/http" "net" "fmt" "os" "time" "github.com/gorilla/mux" )
apiServer 작성
path에 따라 response 할 수 있는 api server를 mux를 통해 구현한다.
router.HandleFunc의 인자로 function을 주면, 해당 Path로 요청이 왔을 때 function을 실행할 수 있다.
//REST API Section func apiServer(){ fmt.Printf("[API Server Section]\n") router := mux.NewRouter().StrictSlash(true) router.HandleFunc("/", apiServerStatus) router.HandleFunc("/lookup", ipLookUp).Methods("GET") log.Fatal(http.ListenAndServe(":28080", router)) fmt.Println("["+time.Now().Format(time.RFC3339)+"]API Server Started") } func apiServerStatus(w http.ResponseWriter, r *http.Request){ message :="["+time.Now().Format(time.RFC3339)+"][API Server Is Online]\n" fmt.Fprintf(w,message) }
main function
main을 통해 api server를 실행시킨다.
func main() { fmt.Printf("[Application Started]\n") apiServer() fmt.Printf("[Application Exit]\n") }
Dockerfile 작성
go의 장점 중 하나는 실행 파일로 만들었을 때 매우 가볍게 실행 가능하다는 것이다. 이러한 장점을 살리기 위해 멀티 스테이지로 빌드하여 가벼운 컨테이너 이미지를 생성한다.
Build Stage
Build Stage에서는 golang 이미지를 pull 하고, 필요한 모듈을 다운받아 실행파일로 빌드하는 과정을 수행한다.
빌드된 파일을 ./app으로 저장하였다.
Build Stage에서 생성된 컨테이너는 빌드를 완료한 후 함께 이미지로 생성되지 않는다.
## Build Stage FROM golang:1.14 AS build COPY . /app WORKDIR /app RUN go get -d github.com/gorilla/mux/ RUN go build -o app
Runtime Stage
Runtime Stage에서는, 위의 컨테이너에서 빌드한 바이너리파일을 가져와 가벼운 alpine 이미지에 넣고 기동한다.
## Runtime Stage FROM alpine:latest AS runtime WORKDIR /app COPY --from=build /app . CMD ["./app"]
컨테이너 이미지 빌드
$ docker build -t heover1cks/apiserver:latest . Sending build context to Docker daemon 89.09kB Step 1/10 : FROM golang:1.14 AS build ---> 05feda542433 Step 2/10 : COPY . /app ---> 6fcc7b74833c Step 3/10 : WORKDIR /app ---> Running in 425f39357a62 Removing intermediate container 425f39357a62 ---> e1068f552a8d Step 4/10 : RUN go get -d github.com/gorilla/mux/ ---> Running in 4944764fc5f7 Removing intermediate container 4944764fc5f7 ---> 66cc39521ae5 Step 5/10 : RUN go build -o app ---> Running in 22796d00c4fc Removing intermediate container 22796d00c4fc ---> 06f9a7a6bc82 Step 6/10 : FROM alpine:latest AS runtime ---> a24bb4013296 Step 7/10 : RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 ---> Using cache ---> db20b6e3c77a Step 8/10 : WORKDIR /app ---> Using cache ---> edbe43ffd5ed Step 9/10 : COPY --from=build /app . ---> ee4eaff66fe3 Step 10/10 : CMD ["./app"] ---> Running in 8a6394eab7b3 Removing intermediate container 8a6394eab7b3 ---> e4288cee1849 Successfully built e4288cee1849 Successfully tagged heover1cks/apiserver:latest
컨테이너 기동 및 테스트
위에서 빌드한 컨테이너를 기동한다.
$ docker run -d -p 8080:28080 heover1cks/apiserver:latest a2bd97cdade566d01ae098e5573a4cd4324a05ed03b31b4fca6d462ffd1f6845
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a2bd97cdade5 heover1cks/apiserver:latest "./app" About a minute ago Up About a minute 0.0.0.0:8080->28080/tcp elastic_rosalind $ curl http://localhost:8080/ [2020-06-16T14:50:35Z][API Server Is Online]
컨테이너 이미지의 사이즈는 13.5MB로, alpine 이미지와 큰 차이가 없다.
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE heover1cks/apiserver latest e4288cee1849 4 minutes ago 13.5MB
apiserver 컨테이너의 리소스 사용량을 확인해보면, cpu/memory를 효율적으로 사용하고 있음을 확인할 수 있다.
$ docker top $(docker ps -q) UID PID PPID C STIME TTY TIME CMD root 5314 5291 0 14:49 ? 00:00:00 ./app $ top -bc | grep 5314 5314 root 20 0 691m 4388 3824 S 0.0 0.4 0:00.02 ./app