docker 우아하게 종료시키기 (내가 만든 docker는 왜 10초 뒤에 종료 되는가?)
ruinnel
dockerdocker-composekubernetesdevOps
1017 Words CHANGE ME READ TIME 4 Minutes, 37 Seconds
2024-12-27 00:56 +0000
📖 5줄 요약
entrypoint.sh
사용시 꼭exec <executable>
로 실행 해야합니다.- docker container는
PID 1
을 가진 프로세스에SIGINT, SIGTERM
을 전달 합니다. - exec는 현재 실행중인 process의 PID를 승계해서
<executable>
이 실행되도록 합니다. entrypoint.sh
에서exec
를 빼먹으면entrypoint.sh
가PID 1
로 실행되고<exectuable>
은 child process로 별도의 PID를 부여받아 실행됩니다.PID 1
이 아닌 프로세스는 docker container 종료시SIGINT, SIGTERM
을 받지 못하고, default timeout인 10초 후 강제 종료 됩니다.
웹어플리케이션을 만들고 dockerize 하기.
아주 간단한 hello world 수준의 web-server를 만들고, SIGINT, SIGTERM
을 받으면 종료 되도록 코드를 작성합니다.
main.go code 보기
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
})
handleSigterm()
http.ListenAndServe(":80", nil)
}
func handleSigterm() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
signal.Notify(c, syscall.SIGTERM)
go func() {
sig := <-c
fmt.Printf("signal(%v) received. exit.", sig)
os.Exit(0)
}()
}
바로 코드 실행하고 Ctrl+C
(SIGINT
) 입력하면 종료도 잘 됩니다.
Dockerfile
과 entrypoint.sh
파일을 만들고 docker image를 만듭니다.
Dockerfile code 보기
FROM golang:1.23-alpine AS builder
COPY main.go /app/main.go
WORKDIR /app
RUN go build -o ./server main.go
FROM alpine:latest AS base
RUN mkdir /app
WORKDIR /app
COPY --from=builder /app/server /app/server
RUN chmod +x /app/server
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT [ "/entrypoint.sh" ]
entrypoint.sh code 보기
#!/bin/sh
/app/server
docker-compose.yml code 보기
services:
hello-server:
image: hello-go-server:v0.1.0
docker image를 만들고 실행하고 stop 해봅니다.
docker image를 build 합니다.
docker build -t hello-go-server:v0.1.0 .
실행하고 stop해 봅니다. 왜? stop하는데 10초가 걸릴까요?
다시 docker compose up -d
로 실행하고, 실행중인 container내의 process list를 확인해 봅니다.
➜ hello-go docker exec -it hello-go-hello-server-1 /bin/sh
/app # ps -ef
PID USER TIME COMMAND
1 root 0:00 {entrypoint.sh} /bin/sh /entrypoint.sh
7 root 0:00 /app/server
20 root 0:00 /bin/sh
26 root 0:00 ps -ef
entrypoint.sh
가 1번,/app/server
가 7번 입니다.- docker container가 종료 될떄 os signal(SIGINT)는 PID 1 프로세스로 전달 됩니다.
main.go
로 작성된 프로그램은 PID 7로 떠있으니 os signal을 못 받습니다.- 당연히 프로램은 종료되지 않고
docker compose stop
의 default timeout인 10초 후에 강제종료 됩니다. - 강제종료이므로 복잡한 프로그램의 경우 문제가 발생 할 수도 있습니다.
우아하게(graceful) 종료시켜 봅시다.
entrypoint.sh
파일에 exec
만 추가하면 됩니다.
#!/bin/sh
exec /app/server
다시 build 하고 실행 후 container내의 process list를 확인해 봅니다.
➜ hello-go docker exec -it hello-go-hello-server-1 /bin/sh
/app # ps
PID USER TIME COMMAND
1 root 0:00 /app/server
13 root 0:00 /bin/sh
19 root 0:00 ps
entrypoint.sh
가 process list에 사라지고,/app/server
가 PID 1로 실행중인 것을 확인 할 수 있습니다.- 그리고
docker compose stop
으로 종료 시켜봅니다.