1. ๊ฐ์
์งํ ์ค์ธ Jikgong ์๋น์ค์์ Nginx๋ฅผ ์ฌ์ฉํ์ฌ Blue/Green ๋ฌด์ค๋จ ๋ฐฐํฌ ์ ๋ต์ ์ฌ์ฉํ์ฌ ๋ฐฐํฌ ์ ๋ค์ด ํ์์ ์ต์ํ ์์ผฐ๋ค.
๊ทธ ๊ณผ์ ์์ ์์ฑํ deploy.sh ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๊ฐ ์์ฑ๋์ด์๋ค.
# ๊ธฐ์กด์ ์คํ ์ค์ด์๋ docker-compose๋ ์ข
๋ฃ์์ผ์ค๋๋ค.
echo "jikgong-${TERMINATE_CONTAINER} down"
sudo docker-compose -f docker-compose.${TERMINATE_CONTAINER}.yml down --rmi all
์ด ๋ถ๋ถ์ด ๋ฌธ์ ์๋ค.
ํ์ฌ ์ฌ์ฉ ์ค์ธ Blue/Green ๋ฐฉ์์ ๋ฌด์ค๋จ ๋ฐฐํฌ ํ๋ก์ธ์ค๋ฅผ ๊ฐ๋ตํ๊ฒ ์ค๋ช ํ๋ฉด
1. ์๋ก์ด ๋ฒ์ ์ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ๋
2. ์ง์์ ์ธ ํฌ์ค์ฒดํฌ๋ก ๊ตฌ๋ ์๋ฃ ํ์ธ
3. ๊ตฌ๋ ์๋ฃ ์ ๋ฆฌ๋ฒ์ค ํ๋ก์๋ก ์ฌ์ฉ ์ค์ธ Nginx์ ํ๋ก์ ์ค์ ์ ๋ณ๊ฒฝํ์ฌ ์๋ก์ด ๋ฒ์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฅดํค๋๋ก ๋ณ๊ฒฝ
4. ๊ธฐ์กด์ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์ด๋ ์ข ๋ฃ
์ ์ฝ๋๊ฐ 4๋ฒ ์์ ์ ์ํํ๋ ์ฝ๋์ธ๋ฐ, ์ฌ๊ธฐ์ ๋ฌธ์ ์ ์ ๊ตฌ๋ฒ์ ์ ์ฒ๋ฆฌ ์ค์ธ ์์ ์ ๋ฌด์ํ๊ณ ๊ฐ์ ์ข ๋ฃ ์ํจ๋ค๋ ์ ์ด๋ค.
๊ทธ๋ผ ์ด๋ป๊ฒ ์์ ํ๊ฒ ์ข ๋ฃ์ํฌ ์ ์์๊น?
๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ๋๋ 2.2 / 2.3 ์ ์ฌ์ฉํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์๋ค.
2.1 ํ๋ก์ธ์ค๋ฅผ ์ง์ ์ข ๋ฃํ ๋
๋๋ ๋ณดํต ํ๋ก์ธ์ค๋ฅผ ์ฃฝ์ด๊ณค ํ ๋ kill -9 PID ๋ฅผ ์ฌ์ฉํ๋ค. ํ์ง๋ง -9๋ก ํ๋ก์ธ์ค๋ฅผ ์ข ๋ฃํ๋ฉด ์์คํ ์ ์ ์ํฅ์ ์ค ์ ์๋ค.
์๋ฅผ ๋ค์ด ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ๊ณ ์ฒ๋ฆฌํ๋ ๋ฐ 5์ด๊ฐ ๊ฑธ๋ฆฌ๋ API๊ฐ ์๊ณ ์ด๋ฅผ ์คํ ์ค์ผ ๋ kill -9๋ก ์ข ๋ฃํ๊ฒ ๋๋ฉด ์ฆ์ ์ข ๋ฃ๋์ด ๋ฒ๋ฆฌ๊ณ ๋ฐ์ดํฐ๊ฐ์ ์ค๋๊ฑฐ๋ ๋ฆฌ์์ค๊ฐ ์ ๋๋ก ๋ซํ์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
kill์ ํ ๋ signal์ ์ค ์ ์๋๋ฐ ๋ชฉ๋ก์ ๋ค์๊ณผ ๊ฐ๋ค.
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
ํ์ง๋ง 15 - SIGTERM์ ์ฌ์ฉํ๋ฉด ํ๋ก์ธ์ค๋ฅผ ์ ์ ์ข ๋ฃ ์ํฌ ์ ์๋ค. ๋ํ, ํ๋ก์ธ์ค๊ฐ ํ์ฌ ์ฌ์ฉ์ค์ธ ๋ฆฌ์์ค๋ฅผ ํด์ ํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฑ ์์ ํ๊ฒ ๋ชจ๋ ์์ ์ ์๋ฃํ ๋ ๊น์ง ๊ธฐ๋ค๋ ค์ค๋ค.
๋์ ๊ฒฝ์ฐ ํ๋ก์ธ์ค๋ฅผ ์ง์ ์ฃฝ์ด์ง ์๊ธฐ ๋๋ฌธ์ PASS~
2.2 Spring Boot ์ข ๋ฃ ์ Graceful Shutdown
Graceful Shutdown์ Spring Boot 2.3.0 Release ๋ถํฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ด๋ค.
์ด๋ฅผ ํ์ฑํ ํ๋ฉด ์คํ๋ง ๋ถํธ๋ ์ข ๋ฃ ์์ฒญ์ด ๋ค์ด์จ ์ดํ๋ก ๋ ์ด์ ์๋ก์ด ์์ฒญ์ ๋ฐ์ง ์์ผ๋ฉด์, ์ฒ๋ฆฌ ์ค์ธ ์์ฒญ์ ๋ชจ๋ ์ฒ๋ฆฌํ ๋ค ํ๋ก์ธ์ค๋ฅผ ์ข ๋ฃํ๊ฒ ๋๋ค.
server:
shutdown: graceful
๊ธฐ๋ณธ์ ์ผ๋ก 30s ๋์ ๊ธฐ๋ค๋ฆฌ๊ณ ์ด๋ ์๋์ ๊ฐ์ด ๋ณ๊ฒฝํ ์ ์๋ค.
spring:
lifecycle:
timeout-per-shutdown-phase: 100s
2.3 Docker ์ปจํ ์ด๋๋ฅผ ์ข ๋ฃํ ๋
๋๊ฐ์ ๊ฒฝ์ฐ์ docker-compose๋ฅผ ์ฌ์ฉํด์ ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์ฐ๊ณ ์ข ๋ฃํ๊ณ ์๋ค.
2.2 ๋ฐฉ๋ฒ ์ฒ๋ผ ์ ํ๋ฆฌ์ผ์ด์ ๋จ์์ graceful shutdown ์ค์ ์ ํด๋์ง๋ง ์ปจํ ์ด๋ ์ข ๋ฃ ์์ ์ค์ ์ด ๋จนํ์ง ์์๋ค. ์์์ ์ธ๊ธ์ ํ์ง ์์์ง๋ง graceful shudown์ด ์ค์ ๋์ด ์๋๋ผ๋ kill -9๋ก ์ข ๋ฃ ์ ์ฆ์ ์ข ๋ฃ ๋๋ค.
ํ ์คํธ์ ์ฌ์ฉํ๊ธฐ ์ํด 1๋ถ ๊ฐ Thread.sleep์ ํ๋ ํ ์คํธ api๋ฅผ ์์ฑํด๋๋ค.
@RestController
@Slf4j
public class TestController {
@GetMapping("/test")
public String test() throws InterruptedException {
log.info("์์");
Thread.sleep(60000);
log.info("์ข
๋ฃ");
return "๋ฐฐํฌ ์๋ฃ";
}
}
application.yml ์ค์ ์ ์๋์ ๊ฐ๋ค.
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 100s
docker compose ๋ก ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ๋ํ๊ณ , curl๋ก ์์ฒญ์ ๋ณด๋ด๋ณด์๋ค. ์ดํ ๋ฐ๋ก docker compose down์ผ๋ก ์ปจํ ์ด๋๋ฅผ ๋ด๋ ธ๊ณ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
$ docker compose up
$ curl http://IP:8080/test # ์์ฒญ ์์
$ docker compose down
graceful shutdown ๋ก๊ทธ๊ฐ ์ฐํ๋ฉฐ ๊ธฐ๋ค๋ฆฌ๋๊ฐ ์ถ๋๋ ์ข ๋ฃ๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ฐ๋ก ์ข ๋ฃ๋์๋ค.
docker compose down ๋ช ๋ น์ด๊ฐ ์คํ๋๋ฉด ๋ด๋ถ์ ์ผ๋ก ์ปจํ ์ด๋ STOP ํ REMOVE ์์ ์ ์งํํ๋ค.
์ด๋ docker stop ์ graceful ํ ์ข ๋ฃ๋ฅผ ์ง์ํด์ฃผ๋๋ฐ ์๋์ ๊ฐ์ด -t ์ต์ ์ ์ฌ์ฉํด ๋ช ์ด๊ฐ ๋๊ธฐํ ์ง ์ง์ ํ ์ ์๋ค. ๋จ์๋ s์ด๋ค.
docker stop -t 100 {contianer_id}
์์ฒญ ์ฒ๋ฆฌ ์ค ๋ช ๋ น์ด๋ฅผ ๋ ๋ฆฌ๋ฉด ์๋์ ๊ฐ์ด ๋๊ธฐํ๋ค๊ฐ 1๋ถ ํ ๋ชจ๋ ํ๋ก์ธ์ค๊ฐ ๋๋๋ฉด ์ ์ ์ข ๋ฃํ๋ ๊ฑธ ํ์ธํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ shutdown ๋์ค์ ๋ค๋ฅธ ์๋ํฌ์ธํธ๋ก ์์ฒญ์ ๋ณด๋์ ๋ ์ฐ๊ฒฐ ์คํจํ์๊ณ , graceful ์ข ๋ฃ ๋๊ธฐ ์ค์ ๋ค๋ฅธ ์์ฒญ์ ๋ฐ์ง ์๋ ๋ค๋ ๊ฑธ ํ์ธํ ์ ์์๋ค.
@GetMapping("/test2")
public String test2() throws InterruptedException {
log.info("test2 ์ง์
");
return "์ฒ๋ฆฌ ์๋ฃ";
}
์ ์ฒ๋ผ docker stop ๋ช ๋ น์ด์ -t ์ต์ ์ ๋ฃ์ ์๋ ์์ง๋ง docker compose ํ์ผ์์ ๋ฐ๋ก ์ค์ ํ ์๋ ์๋ค.
version: "3.8"
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./log:/log
ports:
- "8080:8080"
restart: always
stop_grace_period: 100s # ๋๊ธฐ ์๊ฐ ์ค์
docker-compose ์์ ํ๋ graceful ์๊ฐ์ ์ค์ ํ๋ ๊ฒ์ด ์๋ -t๋ก ์ข ๋ฃํ๋ ค๋ฉด deploy.sh ๊น์ง ์์ ํด์ค์ผ ํ๋ค.
docker compose down๋ ๋ด๋ถ์ ์ผ๋ก stop -> remove ์ด๊ธด ํ์ง๋ง stop ์์ ์ต์ ์ ๋ถ์ฌ ์ค ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ํ๋ ค๋ฉด docker compose stop, docker compose remove ๋ก ๋๋ ์ฌ์ฉํด์ผ ํ๋ค.
๊ท์ฐฎ๊ธฐ ๋๋ฌธ์ ๋ compose์์ ์ค์ ํด๋๋ค.. ใ ใ
3. ๊ฒฐ๊ณผ
Blue/Green ๋ฐฉ์์์ ๊ธฐ์กด ๊ตฌ๋ฒ์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ข ๋ฃ์ํฌ ๋๋ ๊ณ ๋ คํ ์ฌํญ์ด ์์๋ค. ์ด๊ฒ ๋ง๊ณ ๋ ๋ด๊ฐ ๋์น๊ณ ์๋ ๋ ๋ค๋ฅธ ๋ฌด์ธ๊ฐ๊ฐ ์์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์ง๋ง ์ฐจ๊ทผ์ฐจ๊ทผ ์์๊ฐ ์๊ฐ์ด๋ค.
์ด๋ฐ์๋ Nginx์์ ๋ฆฌ๋ฒ์ค ํ๋ก์๋ฅผ ๋ณ๊ฒฝ ํ ์ฌ๋ก๋ฉ ํ๋ ๋ฐ์๋ ์ ๋ง ์งง์ง๋ง ์๊ฐ์ด ์์๋๋ ๊ฒ์ผ๋ก ์๊ณ ์๋ค.
์ด ๋ถ๋ถ๋ ๋ฌธ์ ๊ฐ ๋๋ค๋ฉด ๋ฌธ์ ๋ผ๊ณ ํ ์ ์์ ๊ฒ ๊ฐ๋ค.
์ ๋ฐฉ์์ ์ฌ์ค ์ข ๋ฃ๋ ๋ ๊น์ง ์ค์ ํ ์๊ฐ ๋งํผ ๊ธฐ๋ค๋ ค ์ฃผ๋ ๊ฒ ๋ฟ, ์์ฒญ ์ฒ๋ฆฌ์ ๊ทธ ์ด์์ ์๊ฐ์ด ์์๋๋ค๋ฉด ์ด๊ฑด ๋ณด์ฅํ ์ ์๋ค. ํ์ง๋ง ๊ทธ ์ด์์ด ์์๋๋ ๊ฒ ๋ถํฐ ๋ฌธ์ ๊ฐ ์๋ง ์์ง ์์๊น..?
๋ฐฐ์น ๋ก์ง์ ๋ํด์๋ ๊ณ ๋ฏผํ ์ ์ด ์๋ค. ์ค์ผ์ค๋ง์ ํตํด ๋ฐฐ์น ๋ก์ง์ ์คํ ์ํค๊ณ ์์๋ค๋ฉด 100์ด? ๋ณด๋ค ๋ ๊ฑธ๋ฆด ์ ์์ง ์์๊น? ํ๋๋ฐ, ์ ์ด์ ๊ทธ๋ฐ ๋ฐฐ์น ๋ก์ง์ด BLUE/GREEN ๋ฐฐํฌ์ ๋ฌถ์ฌ ์์ผ๋ฉด ์ ๋๋ค๊ณ ํ๋ค.
์๋ฌดํผ, ์์ ์ค์ธ ํ๋ก์ธ์ค ์ฒ๋ฆฌ๋ฅผ ๊ธฐ๋ค๋ฆฐ ํ ์ข ๋ฃํ๊ธฐ ์ํด ๋ณ๊ฒฝ ์ํจ ๋ถ๋ถ์ ์ ๋ฆฌํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
1. application.yml์ graceful ์ค์
spring:
# ์ข
๋ฃ ์ ์ฒ๋ฆฌ ์ค์ธ ํ๋ก์ธ์ค 100์ด๊ฐ ๋๊ธฐ
lifecycle:
timeout-per-shutdown-phase: 100s
# ์์
์ค์ธ ํ๋ก์ธ์ค ์ฒ๋ฆฌ ํ ์ข
๋ฃ
server:
shutdown: graceful
2. docker-compose ํ์ผ์์ graceful ์ค์
version: "3.8"
services:
app:
stop_grace_period: 100s # garceful ์ข
๋ฃ