๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โœจ ๋ฐ๋ธŒ์˜ต์Šค: DevOps

Github Actions + AWS S3 + AWS CodeDeploy + Nginx ๋ฅผ ํ™œ์šฉํ•œ ๋ธ”๋ฃจ/๊ทธ๋ฆฐ(Blue/Green) ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - Spring Boot CI/CD

by ๐Ÿค ์ค€์ฝฉ์ด 2023. 5. 14.

ํ•ด๋‹น ๊ธ€์—์„œ๋Š” ์„ธ๋ถ€์ ์ธ AWS ์„ค์ •์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๐Ÿซฃ

๊ตฌ์ฒด์ ์ธ ์„ค์ •์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค์Œ ๊ธ€๋“ค์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”!

 

 

Github Actions, AWS CodeDeploy๋ฅผ ํ™œ์šฉํ•œ CI/CD - Node.js(1)

๐Ÿค” CI/CD๋ž€? โญ๏ธ CI CI๋Š” Continuous Integration์˜ ์•ฝ์ž๋กœ ์ง€์†์ ์ธ ํ†ตํ•ฉ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ๋นŒ๋“œ/ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ๊ณผ์ •์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, CI๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ

www.devjoon.com

 

Github Actions, AWS CodeDeploy๋ฅผ ํ™œ์šฉํ•œ CI/CD - Node.js(2)

AWS CodeDeploy๋ฅผ ํ™œ์šฉํ•ด์„œ ์ž๋™ ๋ฐฐํฌ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” AWS์—์„œ ๊ด€๋ จ ์„ค์ •๋“ค์„ ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๐Ÿš€ AWS S3 ๋ฒ„ํ‚ท ์ƒ์„ฑ Github Actions์—์„œ ๋นŒ๋“œํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ AWS์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ S3 ๋ฒ„ํ‚ท์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. AWS > S

www.devjoon.com

 

 

๐Ÿง ๋ธ”๋ฃจ/๊ทธ๋ฆฐ ๋ฐฐํฌ๋ž€?

 

๋ธ”๋ฃจ/๊ทธ๋ฆฐ ๋ฐฐํฌ๋ž€ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ž€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ค‘๋‹จ ์—†์ด ๋ฐฐํฌ๋ฅผ ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•˜๋Š”๋ฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์–ธ์ œ ์ค‘๋‹จ๋ ๊นŒ์š”?

 

์„œ๋น„์Šค๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜์–ด ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌ๋ฅผ ํ•ด์•ผํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด ์„œ๋ฒ„๋ฅผ 8080 ํฌํŠธ์— ๋ฐฐํฌ์ค‘์ด๊ณ  ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌํ•˜๋Š” ์„œ๋ฒ„ ๋˜ํ•œ 8080 ํฌํŠธ์— ๋ฐฐํฌ๋ฅผ ํ•˜๋ ค๋ฉด ๊ธฐ์กด์— ์‹คํ–‰๋˜๋˜ ์„œ๋ฒ„๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ข…๋ฃŒํ•œ ํ›„์—, ์ƒˆ๋กœ์šด ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์„œ๋ฒ„๋ฅผ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌํ•˜๋Š” ๊ณผ์ •์—์„œ ์„œ๋ฒ„๊ฐ€ ์ž ๊น ๋‚ด๋ ค๊ฐ€๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋Ÿฌํ•œ ๊ณผ์ •์„ ์ค‘๋‹จ ๋ฐฐํฌ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

์ค‘๋‹จ ๋ฐฐํฌ์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ผ๋Š” ๊ฐœ๋…์ด ๋“ฑ์žฅํ–ˆ๊ณ  ๋Œ€ํ‘œ์ ์œผ๋กœ Rolling, Blue/Green, Canary ๋ฐฉ์‹์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

 

Blue/Green ๋ฐฐํฌ๋ž€ ์ด์ „ ๋ฒ„์ „์— ์žˆ๋˜ ์‚ฌ์šฉ์ž ํŠธ๋ž˜ํ”ฝ์„ ์ด์ „ ๋ฒ„์ „๊ณผ ๊ฑฐ์˜ ๋™์ผํ•œ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์ ์ง„์ ์œผ๋กœ ์ด์ „ํ•˜๋Š” ๋ฐฐํฌ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์ข€ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ์ด์ „ ๋ฒ„์ „์ธ Blue ํ™˜๊ฒฝ(8081 ํฌํŠธ), ์ƒˆ ๋ฒ„์ „์ธ Green ํ™˜๊ฒฝ(8082 ํฌํŠธ)์ด ์žˆ๋‹ค๊ณ  ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ชจ๋“  ์š”์ฒญ์€ Nginx(80 ํฌํŠธ) ๋กœ ๋“ค์–ด์˜ค๊ณ  Nginx ๋Š” ๋ชจ๋“  ์š”์ฒญ์„ Blue ํ™˜๊ฒฝ์—์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋•Œ, ์ƒˆ๋กœ์šด ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Green ํ™˜๊ฒฝ(8082 ํฌํŠธ)์— ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•œ ํ›„, Nginx ๊ฐ€ ๋ชจ๋“  ์š”์ฒญ์„ Green ํ™˜๊ฒฝ์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํŠธ๋ž˜ํ”ฝ์„ ์ „ํ™˜ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋œ ํ›„์— ํŠธ๋ž˜ํ”ฝ์„ ์ „๋‹ฌํ•จ์œผ๋กœ์จ ์„œ๋น„์Šค๊ฐ€ ์ข…๋ฃŒ๋˜๊ณ  ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆด ํ•„์š”๊ฐ€ ์—†์–ด์ง‘๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ์‹์ด Blue/Green ๋ฐฐํฌ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

 

(Rolling, Canary ๋ฐฐํฌ ๋ฐฉ์‹์— ๋Œ€ํ•œ ์„ค๋ช…์€ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์—์„œ ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๐Ÿ˜…)

 

 

๐Ÿ’ก ์ „์ฒด ํ๋ฆ„

 

์ด์ œ Spring Boot๋ฅผ ๋ธ”๋ฃจ/๊ทธ๋ฆฐ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•œ ์ „์ฒด์ ์ธ ํ๋ฆ„์— ๋Œ€ํ•ด ์ดํ•ดํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

 

  1. ๋จผ์ € Github Actions๋ฅผ ์ด์šฉํ•˜์—ฌ Github Repository์— ์˜ฌ๋ฆฐ ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค.
  1. ๋ฐฐํฌ์— ํ•„์š”ํ•œ ํŒŒ์ผ๋“ค๊ณผ ๋นŒ๋“œ๊ฐ€ ์™„๋ฃŒ๋œ ํ”„๋กœ์ ํŠธ๋ฅผ ์••์ถ•ํ•˜์—ฌ AWS S3์— ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
  1. ์ด์–ด์„œ Github Actions๋ฅผ ์ด์šฉํ•˜์—ฌ AWS CodeDeploy๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  1. AWS CodeDeploy๋ฅผ ์ด์šฉํ•˜์—ฌ AWS S3์— ์—…๋กœ๋“œ๋œ ์••์ถ•ํŒŒ์ผ์„ AWS EC2 ์ธ์Šคํ„ด์Šค๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  1. AWS EC2์— ์••์ถ•ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜จ ํ›„ ์••์ถ•์„ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค.
  1. ์••์ถ•์„ ํ•ด์ œํ•˜๋ฉด ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ํŒŒ์ผ, ๋ฐฐํฌ์— ํ•„์š”ํ•œ ์Šคํฌ๋ฆฝํŠธ๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  1. AWS CodeDeploy๋ฅผ ์ด์šฉํ•˜์—ฌ ์Šคํฌ๋ฆฝํŠธ๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ํ•˜์—ฌ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.

 

์ด ๋•Œ, ๋ธ”๋ฃจ/๊ทธ๋ฆฐ ๋ฐฐํฌ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด 7๋ฒˆ ๊ณผ์ •์—์„œ 3๊ฐ€์ง€ ๊ณผ์ •์„ ๊ฑฐ์นฉ๋‹ˆ๋‹ค.

 

7-1. Green ํ™˜๊ฒฝ์— ์ƒˆ๋กœ์šด WAS ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

7-2. Green ํ™˜๊ฒฝ์— WAS ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋๋Š”์ง€ Health Check ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

7-3. Health Check ์— ์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด, ํŠธ๋ž˜ํ”ฝ์„ Green ํ™˜๊ฒฝ์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

 

 

๐Ÿš€ Nginx ์„ค์ •

 

๋จผ์ € ์‚ฌ์šฉ์ค‘์ธ EC2 ์ธ์Šคํ„ด์Šค์— Nginx ๋ฅผ ์„ค์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

sudo apt install nginx

 

์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์ค๋‹ˆ๋‹ค.

sudo systemctl status nginx

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œ๋˜๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๐Ÿคฉ

 

 

๋‹ค์Œ์œผ๋กœ๋Š” http(80) ๋กœ ์ ‘์†ํ•˜๋ฉด Nginx ์—์„œ ์š”์ฒญ์„ ๋‹ค๋ฅธ ํฌํŠธ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์•„๋ž˜ ์„ค์ •์„ ํ•ด์ค๋‹ˆ๋‹ค.

 

sudo vim /etc/nginx/conf.d/service-url.inc ๋กœ ํŒŒ์ผ ์ƒ์„ฑ
# service-url.inc
# ์•„๋ž˜ ํŒŒ์ผ๋กœ nginx๊ฐ€ ๋ฐ”๋ผ๋ณผ ํฌํŠธ๋ฅผ ์ง€์ •ํ•œ๋‹ค.
# ๋‚ด์šฉ์€ ๋ฐฐํฌ์‹œ ๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” scripts๋กœ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.

set $service_url http://127.0.0.1:8081;

 

sudo vim /etc/nginx/nginx.conf ๋กœ ์„ค์ • ํŒŒ์ผ ์ˆ˜์ •
# nginx.conf

http {
    server {
        include /etc/nginx/conf.d/service-url.inc;

        location / {
                # service_url๋กœ ์š”์ฒญ ์ „๋‹ฌ!
                proxy_pass $service_url;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
        }
    }
}

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •์„ ๋งˆ์ณค๋‹ค๋ฉด Nginx ์˜ reverse proxy ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ Blue/Green ๋ฐฐํฌ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

nginx.conf ํŒŒ์ผ์€ Nginx ์˜ ์„ค์ • ํŒŒ์ผ์ธ๋ฐ proxy_pass ๊ฐ’์„ ์„ค์ •ํ•ด์„œ 80 ํฌํŠธ๋กœ ๋“ค์–ด์˜จ ์š”์ฒญ์„ $service_url ๋กœ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ $service_url ์„ 127.0.0.1:8081 ๋กœ ์„ค์ •ํ•œ ์ƒํƒœ์ด๊ณ , ์ด ๊ฐ’์€ ๋™์ ์œผ๋กœ 8081๊ณผ 8082 ํฌํŠธ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

$service_url ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ Nginx ๋ฅผ reload ํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ์„ ์ „ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

 

 

๐Ÿš€ Health Check ์šฉ API ์ถ”๊ฐ€

 

Nginx ์„ค์ •์„ ๋งˆ์ณค์œผ๋ฉด ์ƒˆ๋กœ์šด ์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋๋Š”์ง€ ํ™•์ธํ•  ๋•Œ ์‚ฌ์šฉํ•  API ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

 

๋‹ค์Œ API ๋Š” CodeDeploy ๊ฐ€ ์‹คํ–‰ํ•  ์Šคํฌ๋ฆฝํŠธ์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

@RestController
public class HealthController {

		@GetMapping("/health")
		public String checkHealth() {
				return "healthy";
		}
}

 

 

๐Ÿš€ ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ

 

์ด์ œ Blue/Green ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ์Šคํฌ๋ฆฝํŠธ๋“ค์„ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

scripts/run_new_was.sh

CURRENT_PORT=$(cat /etc/nginx/conf.d/service-url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0

if [ ${CURRENT_PORT} -eq 8081 ]; then
  TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
  TARGET_PORT=8081
else
  echo "[$NOW_TIME] No WAS is connected to nginx"
fi

TARGET_PID=$(lsof -Fp -i TCP:${TARGET_PORT} | grep -Po 'p[0-9]+' | grep -Po '[0-9]+')

if [ ! -z ${TARGET_PID} ]; then
  echo "Kill WAS running at ${TARGET_PORT}."
  sudo kill ${TARGET_PID}
fi

nohup java -jar -Dserver.port=${TARGET_PORT} /home/ubuntu/application/*.jar
echo "Now new WAS runs at ${TARGET_PORT}."

exit 0

 

  • CURRENT_PORT : ํ˜„์žฌ Nginx ๊ฐ€ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ๋Š” ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ service-url.inc ํŒŒ์ผ์—์„œ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.
  • TARGET_PORT : ์ƒˆ๋กœ์šด ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•  ํฌํŠธ๋ฅผ ํ˜„์žฌ Nginx ๊ฐ€ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ์ง€ ์•Š์€ ํฌํŠธ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • TARGET_PID : ์ƒˆ๋กœ์šด ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•  ํฌํŠธ์—์„œ ํ˜„์žฌ ์‹คํ–‰์ค‘์ธ ํ”„๋กœ์„ธ์Šค ์•„์ด๋””์ž…๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ์กด์— ์‹คํ–‰์ค‘์ธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์žˆ๋‹ค๋ฉด ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ์กด์— ์‹คํ–‰์ค‘์ธ ์„œ๋ฒ„๋ฅผ ์ข…๋ฃŒํ•œ ๋’ค ์ƒˆ๋กœ์šด ์„œ๋ฒ„๋ฅผ TARGET_PORT ์— ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

๋‹ค์Œ์œผ๋กœ๋Š” ์ƒˆ๋กญ๊ฒŒ ์‹คํ–‰ํ•œ ์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์•ž์„œ ์ž‘์„ฑํ–ˆ๋˜ Health Check ์šฉ API ๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

scripts/health_check.sh

CURRENT_PORT=$(cat /etc/nginx/conf.d/service-url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0

if [ ${CURRENT_PORT} -eq 8081 ]; then
  TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
  TARGET_PORT=8081
else
  echo "No WAS is connected to nginx"
  exit 1
fi

echo "Start health check of WAS at 'http://127.0.0.1:${TARGET_PORT}' ..."

for RETRY_COUNT in 1 2 3 4 5 6 7 8 9 10
do
  echo "#${RETRY_COUNT} trying..."
  RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${TARGET_PORT}/health)

  if [ ${RESPONSE_CODE} -eq 200 ]; then
    echo "New WAS successfully running"
    exit 0
  elif [ ${RETRY_COUNT} -eq 10 ]; then
    echo "Health check failed."
    exit 1
  fi
  sleep 10
done

 

  • RETRY_COUNT : 10์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์ด 10๋ฒˆ ์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋๋Š”์ง€ Health Check ๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
  • RESPONSE_CODE : ์•ž์„œ ์ž‘์„ฑํ–ˆ๋˜ Health Check ์šฉ API (http://127.0.0.1:${TARGET_PORT}/health) ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. 200 ์„ฑ๊ณต ์‘๋‹ต์„ ๋ฐ›์€ ๊ฒฝ์šฐ, ์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. (exit 0)
  • 10๋ฒˆ ๋ชจ๋‘ Health Check ์— ์‹คํŒจํ•  ๊ฒฝ์šฐ ์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜์—ฌ ์Šคํฌ๋ฆฝํŠธ๋Š” ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. (exit 1)

 

์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ ์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๋ฉด (Health Check ์„ฑ๊ณต)

์ด์ œ Nginx ๊ฐ€ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ๋Š” ํฌํŠธ๋ฅผ ์ƒˆ๋กœ์šด ๋ฒ„์ „์ด ์‹คํ–‰๋œ ํฌํŠธ๋กœ ์ „ํ™˜ํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋์ž…๋‹ˆ๋‹ค.

 

scripts/switch.sh

CURRENT_PORT=$(cat /etc/nginx/conf.d/service-url.inc | grep -Po '[0-9]+' | tail -1)
TARGET_PORT=0

echo "Nginx currently proxies to ${CURRENT_PORT}."

if [ ${CURRENT_PORT} -eq 8081 ]; then
  TARGET_PORT=8082
elif [ ${CURRENT_PORT} -eq 8082 ]; then
  TARGET_PORT=8081
else
  echo "No WAS is connected to nginx"
  exit 1
fi

echo "set \$service_url http://127.0.0.1:${TARGET_PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc

echo "Now Nginx proxies to ${TARGET_PORT}."
sudo service nginx reload

echo "Nginx reloaded."

 

  • ํ˜„์žฌ Nginx ๊ฐ€ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ๋Š” ํฌํŠธ๋ฅผ ์„ค์ •ํ•˜๋Š” ํŒŒ์ผ์ธ /etc/nginx/conf.d/service-url.inc ํŒŒ์ผ์„ TARGET_PORT ๋กœ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.
  • sudo service nginx reload : reload ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Nginx ๊ฐ€ ์„ค์ • ํŒŒ์ผ๋งŒ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. restart ๋ช…๋ น์–ด๋Š” ์„œ๋ฒ„๋ฅผ ๋‚ด๋ ธ๋‹ค๊ฐ€ ์žฌ์‹คํ–‰ํ•˜๋Š” ๋ฐ˜๋ฉด, reload ๋ช…๋ น์–ด๋Š” ์„ค์ •๋งŒ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋น ๋ฅด๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

 

 

๐Ÿš€ appspec.yml ์ž‘์„ฑ

 

appspec.yml ํŒŒ์ผ์€ AWS CodeDeploy ์—์„œ ๋ฐฐํฌ๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.

์œ„์—์„œ ์ž‘์„ฑํ•œ 3๊ฐ€์ง€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

appspec.yml

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ubuntu/application
    overwrite: yes

permissions:
  - object: /home/ubuntu
    pattern: "**"
    owner: ubuntu
    group: ubuntu

hooks:
  ApplicationStart:
    - location: run_new_was.sh
      timeout: 180
      runas: ubuntu
    - location: health_check.sh
      timeout: 180
      runas: ubuntu
    - location: switch.sh
      timeout: 180
      runas: ubuntu

 

  • ์ž‘์„ฑํ•œ ์ˆœ์„œ๋Œ€๋กœ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋‹ค์Œ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์‹คํŒจํ•  ๊ฒฝ์šฐ ๋ฐฐํฌ๋Š” ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

 

 

๐Ÿš€ Github Actions ์›Œํฌํ”Œ๋กœ์šฐ ์ž‘์„ฑ

 

์ •๋ง ๋งˆ์ง€๋ง‰์ž…๋‹ˆ๋‹ค!! ๐Ÿ˜†

Github Actions ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์ž‘์„ฑํ•ด์„œ ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ jobs ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

.github/workflows/deploy.yml

name: deploy

on:
  push:
    branches: [ develop ]

env:
  AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}

jobs:
  build:
    runs-on: ubuntu-20.04

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: create secret files
        working-directory: src/main/resources
        run: |
          touch application.yml
          echo "${{ secrets.APPLICATION_YML }}" >> application.yml

      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          distribution: 'corretto'
          java-version: '11'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
        shell: bash

      - name: Build with Gradle
        run: ./gradlew build -x test
        shell: bash

      - name: Make zip file
        run: zip -r ./code-deploy.zip ./build/libs/*.jar ./scripts/* -j ./appspec.yml
        shell: bash

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Upload to S3
        run: aws s3 cp --region ap-northeast-2 ./code-deploy.zip s3://$AWS_S3_BUCKET/code-deploy.zip

      - name: Code Deploy
        run: aws deploy create-deployment
          --application-name codedeploy-application
          --deployment-config-name CodeDeployDefault.OneAtATime
          --deployment-group-name GROUP
          --s3-location bucket=$AWS_S3_BUCKET,bundleType=zip,key=code-deploy.zip

 

  • on.push.branches : ํŠน์ • ๋ธŒ๋žœ์น˜์— push ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ•ด๋‹น ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์œ„์™€ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” develop ๋ธŒ๋žœ์น˜์— push ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • Make zip file job : S3 ์— ์—…๋กœ๋“œ ํ•ด์•ผํ•  ํŒŒ์ผ๋“ค์„ code-deploy.zip ์ด๋ผ๋Š” ํŒŒ์ผ๋กœ ์••์ถ•ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋นŒ๋“œ๋œ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ(./build/libs/*.jar), Code Deploy Agent ๊ฐ€ ์‹คํ–‰ํ•  ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ(./scripts/* -j), Code Deploy Agent ์˜ ๋ฐฐํฌ ๊ด€๋ฆฌ ํŒŒ์ผ(./appspec.yml)์„ ์••์ถ•ํ•ฉ๋‹ˆ๋‹ค.
  • Upload to S3 job : ์••์ถ•ํ•œ code-deploy.zip ํŒŒ์ผ์„ S3 ์— ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
  • Code Deploy job : Code Deploy Agent ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. S3 ์— ์—…๋กœ๋“œ๋œ code-deploy.zip ํŒŒ์ผ์„ EC2 ๋กœ ๊ฐ€์ ธ์™€์„œ ์••์ถ•์„ ํ•ด์ œํ•˜๊ณ  appspec.yml ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ˆœ์„œ๋Œ€๋กœ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

 

๐Ÿคฉ ๊ฒฐ๊ณผ ํ™•์ธ

 

 

Current port of running WAS is 8082.
Kill WAS running at 8081.
Now new WAS runs at 8081.
Start health check of WAS at 'http://127.0.0.1:8081' ...
#1 trying...
#2 trying...
#3 trying...
New WAS successfully running
Nginx currently proxies to 8082.
Now Nginx proxies to 8081.
Nginx reloaded.

 

Github Actions ์™€ AWS CodeDeploy ๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด ์ƒˆ๋กœ์šด ์„œ๋ฒ„๊ฐ€ ๋ฐฐํฌ๋œ ํ›„์— Nginx ๊ฐ€ reload ๋˜์–ด ํŠธ๋ž˜ํ”ฝ์„ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

$ ps -ef | grep java

ubuntu  5516  1 10 16:23 ?  00:00:21 java -jar -Dserver.port=8082 /home/ubuntu/application/application.jar
ubuntu  5692  1 26 16:25 ?  00:00:22 java -jar -Dserver.port=8081 /home/ubuntu/application/application.jar

 

Blue/Green ๋ฐฐํฌ ์ „๋žต์„ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด ์‹ ๋ฒ„์ „๊ณผ ๊ตฌ๋ฒ„์ „์˜ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ƒํƒœ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

์‹ ๋ฒ„์ „์˜ ํฌํŠธ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ˜„์žฌ Nginx ๊ฐ€ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ๋Š” ํฌํŠธ๋ฅผ service-url.inc ํŒŒ์ผ์—์„œ ํ™•์ธํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

์‹ ๋ฒ„์ „๊ณผ ๊ตฌ๋ฒ„์ „์ด ์‹คํ–‰์ค‘์ด๊ธฐ ๋•Œ๋ฌธ์— ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Blue/Green ๋ฐฐํฌ ์ „๋žต์˜ ๋Œ€ํ‘œ์ ์ธ ์žฅ์ ์ด๋ผ๊ณ ๋„ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋งŒ์•ฝ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌํ•œ ์„œ๋ฒ„์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ๋น ๋ฅด๊ฒŒ service-url.inc ํŒŒ์ผ์—์„œ ํฌํŠธ๋งŒ ์ˆ˜์ •ํ•œ ํ›„ Nginx ๋ฅผ reload ํ•˜์—ฌ ๋‹ค์‹œ ๊ตฌ๋ฒ„์ „์˜ ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.