[CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘  Application Linux(Ubuntu)์— SSH๋ฅผ ์ด์šฉํ•œ ํŒŒ์ผ ์ „์†ก

2023. 7. 5. 17:07ใ†System ์ž‘์—…์‹ค/DevOps

728x90
๋ฐ˜์‘ํ˜•

 



 

๐Ÿ—‚ ๋ชฉ์ฐจ

โš ๏ธ ์•„๋ž˜ ๋ชฉ์ฐจ ์ค‘ ๋ช‡๋ช‡๊ฐœ์˜ ๋งํฌ๊ฐ€ ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š” ๋ฌธ์ œ๋กœ ๊ธ€ ๋งจ ํ•˜๋‹จ์— ๋‹ค์Œ ๊ธ€๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ๋‘์—ˆ์Šต๋‹ˆ๋‹ค.

โœ… [CI/CD] Jenkins์™€ Gitea ์—ฐ๋™
โœ… [CI/CD] Jenkins Trigger ์ •๋ณด Discord๋กœ ๋ณด๋‚ด๊ธฐ
โœ… [CI/CD] ์ •์  ์ฝ”๋“œ ๋ถ„์„ ํˆด SonarQube์™€ Jenkins ์—ฐ๋™
โœ… [CI/CD] SonarQube๋ฅผ ํ†ตํ•ด Code Convention ์ ์šฉ
โœ… [DevOps] JAVA Gradle JaCoCo (Code coverage) ์„ค์ •ํ•˜๊ธฐ 
โœ… [DevOps] JAVA Gradle JaCoCo (Code coverage) ์„ค์ •ํ•˜๊ธฐ (์ถ”๊ฐ€)(https://junyharang.tistory.com/392)

โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘  Application Linuxt(Ubuntu)์— SSH๋ฅผ ์ด์šฉํ•œ ํŒŒ์ผ ์ „์†ก
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ก Create Docker Image And BackUp
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ข Application Server Docker Job (โ‘  Application ๋„์ปค ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ)
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ข Application Server Docker Job (โ‘ก Application Docker Run)(https://junyharang.tistory.com/406)
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ข Application Server Docker Job (โ‘ข Application Docker Health Check)
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ฃ NGINX Server Docker Job (โ‘  NGINX ๊ฐ ์ข… ์„ค์ •) 
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ฃ NGINX Server Docker Job (โ‘ก NGINX Docker ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ) 
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ฃ NGINX Server Docker Job (โ‘ข NGINX Docker Run & Health Check)
โœ… [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ฃ NGINX Server Docker Job (โ‘ข NGINX ์žฌ ์„ค์ •)



๐Ÿค” ๋‚ด๊ฐ€ ๋งŒ๋‚œ ๋ฌธ์ œ

โ›”๏ธ [Jenkins] java.lang.OutOfMemoryError: Java heap space

 

 

 

๋ฐฐํฌ ์ž๋™ํ™”์™€ ์ง€์†์  ์ธ๋„:๋„์ปค์™€ ์  ํ‚จ์Šค ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋กœ ๋งŒ๋“œ๋Š”

COUPANG

www.coupang.com

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 

 

 

๐Ÿš€ ์  ํ‚จ์Šค์—์„œ ๋ฆฌ๋ˆ…์Šค ์„œ๋ฒ„ SSH๋ฅผ ์ด์šฉํ•œ ํŒŒ์ผ ์ „์†ก

    ๐Ÿ”ฝ ๊ฐœ์š”

        ๐Ÿ“ฆ ๊ตฌ์„ฑ๋„

CI/CD ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌ์„ฑ๋„

 

 

 

 

 

 

 

deploy Directory Path

 

 

 

        ๐Ÿ“ฆ ์†Œ๊ฐœ

์ฃผ๋‹ˆ๋Š” ์ด ์ „ ๊ธ€๋“ค์„ ํ†ตํ•ด ์†Œ๋‚˜ํ๋ธŒ์™€ ์  ํ‚จ์Šค ๊ทธ๋ฆฌ๊ณ , ํ˜•์ƒ ๊ด€๋ฆฌ ์„œ๋น„์Šค gitea๋ฅผ ์—ฐ๋™ํ•ด ๋ณด์•˜์–ด์š”.
์ด์ œ ์‹ค์ œ Application๊ณผ Nginx๋ฅผ ์ด์šฉํ•ด์„œ ์ฃผ๋‹ˆ๊ฐ€ ๊ตฌ์„ฑํ•œ WAS๋ฅผ ๋ฐฐํฌํ•˜๊ณ , ์‹ค์ œ ์ ‘์†์ด ๋˜๋Š”์ง€๊นŒ์ง€ ํ™•์ธํ•ด๋ณด๋ ค๊ณ  ํ•ด์š”.

์ฃผ๋‹ˆ๊ฐ€ ๋„์ปค๋ฅผ ์ด์šฉํ•˜๋ ค๊ณ  ํ•˜๋Š” ์ด์œ ๋Š” ๋Œ€์ƒ ์„œ๋ฒ„ ์ฆ‰, ์„œ๋น„์Šค๊ฐ€ ๊ตฌ๋™๋  ๋ฆฌ๋ˆ…์Šค ์„œ๋ฒ„์— ์ง์ ‘ JDK ๋“ฑ์„ ์„ค์น˜ํ•˜๊ณ , Application Build File์„ SSH๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ณด๋‚ด ๊ตฌ๋™ ์‹œํ‚ค๊ณ , Nginx๋ฅผ ์„ค์น˜ํ•˜์—ฌ ์„œ๋น„์Šค๋ฅผ ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋Œ€์ƒ ์„œ๋ฒ„์— ์ง์ ‘ ์„ค์น˜ํ•˜๊ฒŒ ๋˜๋ฉด ๊ฒฉ๋ฆฌํ™”๊ฐ€ ๋˜์ง€ ์•Š์•„ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋„์ปค๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐฑ์—…๋„ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋„์ปค๋ฅผ ์ด์šฉํ•˜๋ ค๊ณ  ํ•ด์š”.

๊ตฌ์„ฑ๋„๋ฅผ ๋ณด๋ฉด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ์šด์˜ ํ™˜๊ฒฝ์ด ๋‚˜๋ˆ ์ ธ ์žˆ๊ณ , ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์™ธ๋ถ€๋ง๊ณผ NAT๋กœ ํ†ต์‹ ์ด ๊ฐ€๋Šฅํ•œ ์„œ๋ฒ„์™€ ๋‚ด๋ถ€๋ง์—์„œ WAS๋ฅผ ๊ตฌ๋™ ์‹œํ‚ฌ ์„œ๋ฒ„๊ฐ€ ๋‚˜๋ˆ ์ ธ ์žˆ๊ณ , ์  ํ‚จ์Šค์™€ ์†Œ๋‚˜ํ๋ธŒ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ตฌ๋™์ค‘์ธ ๊ด€๋ฆฌ์šฉ ์„œ๋ฒ„๋กœ ๋‚˜๋ˆ ์ ธ ์žˆ๋Š”๋ฐ, ์ด๋ฒˆ ์ž‘์—…์—์„œ๋Š” ์  ํ‚จ์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ์šด์˜ ํ™˜๊ฒฝ์— ๋„์ปค๋ฅผ ์ด์šฉํ•˜์—ฌ NGINX์™€ Spring Boot ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

์ „ํ†ต์ ์ธ ๋ฐฉ์‹์˜ ๋ฐฐํฌ ๋ฐฉ์‹์— ๋ฌธ์ œ์  (์ถœ์ฒ˜: https://hudi.blog)



๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ž€ ์ƒˆ๋กœ์šด Application์ด ๊ตฌ๋™ ๋Œ€์ƒ ์„œ๋ฒ„๋กœ ์˜ฎ๊ฒจ์ง€๊ณ , ๋‹ค์‹œ ์„œ๋น„์Šค๋ฅผ ์žฌ๊ฐœํ•  ๋•Œ, ์ฆ‰, Version ์ตœ์‹ ํ™”๊ฐ€ ์ด๋ค„์งˆ ๋•Œ, ๊ธฐ์กด ์ „ํ†ต ๋ฐฉ์‹์—์„œ๋Š” ์„œ๋น„์Šค๊ฐ€ ์ค‘๋‹จ ๋˜์—ˆ๋‹ค๊ฐ€ ์ƒˆ๋กœ์šด Version Update๊ฐ€ ์™„๋ฃŒ๋œ ๋’ค ๋‹ค์‹œ ์„œ๋น„์Šค๊ฐ€ ์žฌ๊ฐœ๋˜๋Š” ๋ฐฉ์‹์ด์˜€๋Š”๋ฐ, ๋ฒ„์ „ ์ตœ์‹ ํ™”๊ฐ€ ์ด๋ค„์ ธ๋„ ์„œ๋น„์Šค๊ฐ€ ์ค‘๋‹จ๋˜์ง€ ์•Š๊ณ , ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ด์•ผ๊ธฐ ํ•ด์š”.



๐Ÿ’ก ์ฐธ๊ณ  ์‚ฌํ•ญ
๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๋ฐฉ์‹

1. Rolling Update

์ถœ์ฒ˜: https://hudi.blog


์ด ๋ฐฉ์‹์€ ์‚ฌ์šฉ ์ค‘์ธ ์„œ๋ฒ„์—์„œ ์ƒˆ ๋ฒ„์ „์„ ์ ์ง์ ์œผ๋กœ ์ฆ‰, ๋‹จ๊ณ„๋ณ„๋กœ ๊ต์ฒดํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์‹.
์„œ๋น„์Šค ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ์„œ๋ฒ„ ํ•˜๋‚˜๋ฅผ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ์—์„œ Routing ํ•˜์ง€ ์•Š๋„๋ก ํ•œ ๋’ค, ์ƒˆ ๋ฒ„์ „์„ ๋‹ค์‹œ ๋ผ์šฐํŒ…ํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐ˜๋ณตํ•˜์—ฌ ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ์„œ๋ฒ„์— ์ƒˆ ๋ฒ„์ „์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ์‹

์žฅ์ 

โœ… ๋Œ€์ƒ ์„œ๋ฒ„๋ณ„๋กœ ์ฐจ๋ก€๋Œ€๋กœ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, Roll Back ๊ฐ€๋Šฅ.
โœ… k8s, elastic geanstalk๊ณผ ๊ฐ™์€ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ๋„๊ตฌ์—์„œ ์ง€์›ํ•˜์—ฌ ๊ฐ„ํŽธํ•˜๋ฉฐ, ๋งŽ์€ ์„œ๋ฒ„ ์ž์› ํ™•๋ณด๋ฅผ ํ•˜์ง€ ์•Š์•„๋„ ๋จ.

๋‹จ์ 
โ›”๏ธ ์ƒˆ ๋ฒ„์ „ ๋ฐฐํฌ์‹œ ์„œ๋น„์Šค๋ฅผ ํ•˜๋Š” ์„œ๋ฒ„๊ฐ€ ์ค„์–ด๋“ค๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋น„์Šค ์ค‘์ธ ์„œ๋ฒ„์— ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€.
โ›”๏ธ ๋ฐฐํฌ๊ฐ€ ์ง„ํ–‰๋  ๋•Œ, ๊ตฌ ๋ฒ„์ „๊ณผ ์‹  ๋ฒ„์ „์ด ๊ฐ™์ด ์„œ๋น„์Šค ๋˜๋Š” ๊ตฌ๊ฐ„์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ ๋ฐœ์ƒ.



2. Blue & Green

์ถœ์ฒ˜: https://hudi.blog


๋ฐฐํฌ ์‹œ ํŠธ๋ž˜ํ”ฝ์„ ํ•œ๋ฒˆ์— ๊ตฌ๋ฒ„์ „์—์„œ ์‹ ๋ฒ„์ „์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๋‹น ์ „๋žต์—์„œ๋Š” ํ˜„์žฌ ์šด์˜์ค‘์ธ ์„œ๋น„์Šค์˜ ํ™˜๊ฒฝ์„ Blue๋ผ๊ณ  ๋ถ€๋ฅด๊ณ , ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌํ•  ํ™˜๊ฒฝ์„ Green์ด๋ผ๊ณ  ๋ถ€๋ฆ„.

Blue์™€ Green์˜ ์„œ๋ฒ„๋ฅผ ๋™์‹œ์— ๋‚˜๋ž€ํžˆ ๊ตฌ์„ฑํ•ด๋‘” ์ƒํƒœ๋กœ ๋ฐฐํฌ ์‹œ์ ์— ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ๊ฐ€ ํŠธ๋ž˜ํ”ฝ์„ Blue์—์„œ Green์œผ๋กœ ์ผ์ œํžˆ ์ „ํ™˜ ์‹œํ‚ค๊ณ , Green ๋ฐฐํฌ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ ๋˜๊ณ , ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด Blue ์„œ๋น„์Šค ๋ฐฐํฌ๋ฅผ ํ•œ ๋’ค Green ์„œ๋น„์Šค ์ œ๊ฑฐ ํ˜น์€ ๋‹ค์Œ ๋ฐฐํฌ๋ฅผ ์œ„ํ•ด ์œ ์ง€


์žฅ์ 
โœ… ๋กค๋ง ๋ฐฐํฌ์™€ ๋‹ฌ๋ฆฌ ํ•œ๋ฒˆ์— ํŠธ๋ž˜ํ”ฝ์„ ๋ชจ๋‘ ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ ์˜ฎ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ.
โœ… ๊ตฌ ๋ฒ„์ „์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์†์‰ฌ์šด Roll Back ๊ฐ€๋Šฅ
โœ… ์šด์˜ ํ™˜๊ฒฝ์— ์˜ํ–ฅ ์—†์ด ์ƒˆ ๋ฒ„์ „ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ

๋‹จ์ 
โ›”๏ธ ์‹ค์ œ ์šด์˜์— ํ•„์š”ํ•œ ์„œ๋ฒ„ ์ž์› ๋Œ€๋น„ ๋‘ ๋ฐฐ์˜ ์ž์› ํ•„์š”. ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ ์šด์˜ ์‹œ ํ•„์š” ์—†๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ๋˜์ง€๋งŒ,
      ์˜จํ”„๋ ˆ๋ฏธ์Šค ๋ฐฉ์‹์—์„œ๋Š” ์ž์› ๋ถ€๋‹ด ์ฆ๊ฐ€.




2. Canary (์นด๋‚˜๋ฆฌ) ๋ฐฐํฌ

์ถœ์ฒ˜: https://hudi.blog


์ ์ง„์ ์œผ๋กœ ๊ตฌ๋ฒ„์ „์— ๋Œ€ํ•œ ํŠธ๋ž˜ํ”ฝ์„ ์‹ ๋ฒ„์ „์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์€ ๋กค๋ง ๋ฐฐํฌ ๋ฐฉ์‹๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ, ์นด๋‚˜๋ฆฌ ๋ฐฐํฌ์˜ ํ•ต์‹ฌ์€ ์ƒˆ๋กœ์šด ๋ฒ„์ „์— ๋Œ€ํ•œ ์˜ค๋ฅ˜ ์กฐ๊ธฐ ํƒ์ง€.

์†Œ์ˆ˜ ์ธ์›์— ๋Œ€ํ•ด์„œ๋งŒ ํŠธ๋ž˜ํ”ฝ์„ ์ƒˆ๋กœ์šด ๋ฒ„์ „์— ์˜ฎ๊ฒจ๋‘” ์ƒํƒœ์—์„œ ์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•˜๊ณ , ์ƒˆ๋กœ์šด ๋ฒ„์ „์— ์ด์ƒ์ด ์—†๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€์„ ๊ฒฝ์šฐ ๋ชจ๋“  ํŠธ๋ž˜ํ”ฝ์„ ์‹ ๊ทœ ๋ฒ„์ „์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ธฐ์ค€์€ ๋‚ด๋ถ€ ๊ทœ์น™(ํŠน์ • ์œ ์ € ๋“ฑ) ํ˜น์€ ๋žœ๋ค์ด๋‹ค.

์ฆ‰, ๋ธ”๋ฃจ ๊ทธ๋ฆฐ ๋ฐฉ์‹์ฒ˜๋Ÿผ ํŠธ๋ž˜ํ”ฝ์„ ํ•œ๋ฒˆ์— ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๊ณ , ๋‹จ๊ณ„์ ์œผ๋กœ ์ „ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€์ •์  ์˜ํ–ฅ์„ ์ตœ์†Œํ™” ํ•˜๊ณ , ์ƒํ™ฉ์— ๋”ฐ๋ผ ํŠธ๋ž˜ํ”ฝ ์–‘ ์กฐ์ ˆํ•˜๋ฉฐ, Roll Back ๊ฐ€๋Šฅ.


์ด๋Ÿฌํ•œ ํŠน์ง•์œผ๋กœ ์ธํ•ด A/B ํ…Œ์ŠคํŠธ ์ง„ํ–‰์— ์ ํ•ฉ.


์žฅ์ 
โœ… ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ ์ธํ•œ ์œ„ํ—˜ ์ตœ์†Œํ™”
โœ… ๋ฌธ์ œ ์ƒํ™ฉ ๋น ๋ฅด๊ฒŒ ํƒ์ง€
โœ… A/B ํ…Œ์ŠคํŠธ ํ™œ์šฉ ๊ฐ€๋Šฅ (๋Œ€์กฐ๊ตฐ๊ณผ ์‹คํ—˜๊ตฐ์œผ๋กœ ๋‚˜๋ˆ„์–ด ํŠน์ • UI๋‚˜, ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํšจ๊ณผ ๋น„๊ต ๋ฐฉ๋ฒ•๋ก )

๋‹จ์ 
โ›”๏ธ ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ ์ œ์–ด ๋ถ€๋‹ด
โ›”๏ธ ๋กค๋ง ๋ฐฐํฌ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์‹ /๊ตฌ ๋ฒ„์ „ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ณต์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ.


 

์ด๋ฒˆ์— ์ฃผ๋‹ˆ๋Š” ๋กค๋ง ๋ฐฉ์‹์„ ์ฑ„ํƒํ•˜์—ฌ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด๋ ค๊ณ  ํ•ด์š”.

๋กค๋ง ๋ฐฉ์‹์„ ์„ ํƒํ•œ ์ด์œ ๋Š” ํ˜„์žฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์„œ๋ฒ„ ํ•œ ๋Œ€์— ์œˆ๋„์šฐ OS๋ฅผ ์˜ฌ๋ฆฌ๊ณ , Hyper-V ํ†ตํ•ด ๊ฐ€์ƒ Ubuntu Linux๋ฅผ ์—ฌ๋Ÿฌ ๋Œ€ ์˜ฌ๋ ค ์‚ฌ์šฉ ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์„œ๋ฒ„ ์ž์›์ ์ธ ๋ฌธ์ œ๋„ ์žˆ๊ณ , ์‹ค์ œ ์„œ๋ฒ„๋Š” ํ•œ ๋Œ€์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฐฉ์‹์„ ์„ ํƒํ•˜์˜€์–ด์š”.

 

 

 

 

    ๐Ÿ”ฝ ์  ํ‚จ์Šค

        ๐Ÿ“ฆ Jenkins Script (์  ํ‚จ์Šค ์Šคํฌ๋ฆฝํŠธ)

์ด ์ „์— ์ž‘์„ฑํ•œ ๋‚ด์šฉ๊ณผ ๋‹ค๋ฅด๊ฒŒ ์  ํ‚จ์Šค ์Šคํฌ๋ฆฝํŠธ์— ๋งŽ์€ ๋ณ€๊ฒฝ์ด ์ด๋ค„์กŒ์–ด์š”.

def SET_VARIABLE = "Jenkins File ๋ณ€์ˆ˜ ์„ค์ •"
def SONARQUBE_ANALYSIS = "์†Œ๋‚˜ํ๋ธŒ Analysis ํ™•์ธ"
def SONARQUBE_QUALITY_GATE = "์†Œ๋‚˜ํ๋ธŒ Quality Gate ํ™•์ธ"
def APPLICATION_NAME = "total-back-office"

// git ํ™˜๊ฒฝ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
def get_commit_message() {  // git Commit Message ๊ฐ€์ ธ์˜ค๊ธฐ
    script {
        return sh(script: "git show -s --format=%B ${env.GIT_COMMIT}", returnStdout: true).trim()
    }
}

def get_commit_author() {   // git commit ์ž‘์„ฑ์ž ๊ฐ€์ ธ์˜ค๊ธฐ
    script {
        return sh(script: "git --no-pager show -s --format=%an ${env.GIT_COMMIT}", returnStdout: true).trim()
    }
}

pipeline {
    agent any
    environment {
        DISCORD_WEBHOOK_URL = credentials("Total-Back-Office-BE-Discord-Webhook")
    }
    stages {
        stage(SET_VARIABLE) {
            steps {
                script {
                    // ๊ตฌ๋™ ํ™˜๊ฒฝ ์„ค์ • (์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋กœ ๋ธŒ๋Ÿฐ์น˜ ์ด๋ฆ„์ด master๋ผ๋ฉด prod ํ™˜๊ฒฝ์ด๊ณ , ์•„๋‹ˆ๋ฉด dev ํ™˜๊ฒฝ
                    def DRIVE_ENV = env.BRANCH_NAME.equals("master") ? "prod" : "dev"
                    // DRIVE_ENV DRIVE_ENV ๋ณ€์ˆ˜๋ฅผ ๋‹ค๋ฅธ ์Šคํ…Œ์ด์ง€์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋„๋ก ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •
                    env.DRIVE_ENV = DRIVE_ENV

                    // gitea ํ™˜๊ฒฝ ์„ค์ •
                    GITEA_CREDENTIAL_ID = "[๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค]gitea-backend"
                    GITEA_REPOSITORY_URL = "http://192.168.20.3:81/giggals-s-people/TotalBackOffice-BackEnd.git"

                    GIT_COMMIT_AUTHOR = get_commit_author()
                    GIT_COMMIT_MESSAGE = get_commit_message()

                    PR_ID = 'PR์ด ์กด์žฌํ•˜์ง€ ์•Š์•„์š”.'
                    PR_BRANCH = 'PR์ด ์กด์žฌํ•˜์ง€ ์•Š์•„์š”.'
                    if ("${GIT_COMMIT_MESSAGE}".contains('Merge')) {
                        PR_ID = "${GIT_COMMIT_MESSAGE}".substring("${GIT_COMMIT_MESSAGE}".indexOf('#') + 1, "${GIT_COMMIT_MESSAGE}".indexOf(')')).trim()
                        PR_BRANCH = "${GIT_COMMIT_MESSAGE}".split("from")[1].split("into")[0].trim()
                    }

                    // ์†Œ๋‚˜ํ๋ธŒ ์„ค์ •
                    SONARQUBE_SERVER_NAME = 'SonarQube_BackEnd'
                    SONARQUBE_CREDENTIAL_ID = "backend-total-back-office"

                    // TEST Coverage Report Path
                    JUNIT_REPORT_PATH = "build/test-results/test"
                    JACOCO_REPORT_PATH = "build/jacoco/jacoco.xml"
                    CHECKSTYLE_REPORT_PATH = "build/reports/checkstyle-output/checkstyle-report.xml"

                    DISCORD_SEND_MESSAGE_TITLE = "๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค ํ†ตํ•ฉ ๊ด€๋ฆฌ ์„œ๋น„์Šค ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ"

                    DOCKER_SERVER_BASE_DIRECTORY = "/data/deploy/giggal-total-back-office"
                }
            }

            post {
                success {
                    sendStartAlarmToDiscord()
                }
            }
        } // stage(SET_VARIABLE) ๋

        stage('GITEA CHECKOUT') {
            steps {
                echo "Repository Clone ์ž‘์—…์ด ์‹œ์ž‘ ๋˜์—ˆ์–ด์š” ๐Ÿ˜€"

                git branch: "${env.BRANCH_NAME}",
                        credentialsId: GITEA_CREDENTIAL_ID,
                        url: GITEA_REPOSITORY_URL
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('GITEA CHECKOUT')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('GITEA CHECKOUT')
                }
            } // Discord Send Post ๋
        } // stage('GITEA CHECKOUT') ๋

        stage('Project Build ๊ทธ๋ฆฌ๊ณ  Test ์ž‘์—…') {
            steps {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    echo 'Project Build ๊ทธ๋ฆฌ๊ณ  Test ์ž‘์—…์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค!'
                    sh './gradlew clean build --stacktrace --warning-mode all --info --console=verbose'

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo 'Project Build ๊ทธ๋ฆฌ๊ณ  Test ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('Project Build ๊ทธ๋ฆฌ๊ณ  Test ์ž‘์—…')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('Project Build ๊ทธ๋ฆฌ๊ณ  Test ์ž‘์—…')
                }
            } // Discord Send Post ๋
        } // stage('Project Build ์ž‘์—…') ๋

        stage(SONARQUBE_ANALYSIS) {
            when {
                branch pattern: "(develop|master)", comparator: "REGEXP"
            }

            steps {
                script {
                    def scannerHome = tool 'SonarQube-Scanner'
                    withSonarQubeEnv(credentialsId: SONARQUBE_CREDENTIAL_ID, installationName: SONARQUBE_SERVER_NAME) {
                        sh "${scannerHome}/bin/sonar-scanner \
                        -Dsonar.projectKey=BE-total-back-office \
                        -Dsonar.projectName=BE-total-back-office \
                        -Dsonar.branch.name=master \
                        -Dsonar.language=java \
                        -Dsonar.java.source=1.8 \
                        -Dsonar.sources=src/main/java \
                        -Dsonar.test=src/test/java \
                        -Dsonar.test.inclusion=**/*Test.java \
                        -Dsonar.issuesReport.console.enable=true \
                        -Dsonar.junit.reportPaths=${JUNIT_REPORT_PATH} \
                        -Dsonar.java.binaries=build/classes \
                        -Dsonar.java.coveragePlugin=jacoco \
                        -Dsonar.coverage.jacoco.xmlReportPaths=${JACOCO_REPORT_PATH} \
                        -Dsonar.java.libraries.empty=true \
                        -Dsonar.sourceEncoding=UTF-8 \
                        -Dsonar.java.checkstyle.reportPaths=${CHECKSTYLE_REPORT_PATH} \
                        -Dsonar.exclusions=**/dto/**,**/exception/**,**/constant/**,**/SpringInitProjectApplication.java,**/WebRestController.java,**/FileUploadYaml.java \
                        "
                    }
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord(SONARQUBE_ANALYSIS)
                    }
                }

                failure {
                    sendFailedAlarmToDiscord(SONARQUBE_ANALYSIS)
                }
            } // Discord Send Post ๋
        } // stage(SONARQUBE_ANALYSIS) ๋

        stage(SONARQUBE_QUALITY_GATE) {
            when {
                branch pattern: "(develop|master)", comparator: "REGEXP"
            }

            steps {
                timeout(time: 5, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                    script {
                        echo "์†Œ๋‚˜ํ๋ธŒ Quality Gate ๊ฒ€์‚ฌ๊ฐ€ ์‹œ์ž‘ ๋˜์—ˆ์–ด์š”!"
                        def sonarQubeQualityGate = waitForQualityGate()
                        def qualityGateStatus = sonarQubeQualityGate.status
                        echo "Quality Gate ์ƒํƒœ: ${qualityGateStatus}"

                        if (qualityGateStatus != 'OK') {
                            echo "Quality Gate ๊ฒ€์ฆ ์ƒํƒœ: ${qualityGateStatus}"
                            error "Quality Gate ๊ฒ€์ฆ์— ์‹คํŒจ ํ•˜์˜€์–ด์š” ๐Ÿ˜ญ: ${qualityGateStatus}"

                        } else {
                            echo "Quality Gate ๊ฒ€์ฆ์— ์„ฑ๊ณต ํ•˜์˜€์–ด์š” ๐Ÿ˜€: ${qualityGateStatus}"
                        }

                        echo "Quality Gate ๊ฒ€์ฆ์„ ๋ชจ๋‘ ์™„๋ฃŒ ํ•˜์˜€์–ด์š” ๐Ÿ˜Ž"
                    }
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord(SONARQUBE_QUALITY_GATE)
                    }
                }

                failure {
                    sendFailedAlarmToDiscord(SONARQUBE_QUALITY_GATE)
                }
            } // Discord Send Post ๋
        } // stage(SONARQUBE_QUALITY_GATE) ๋

        stage('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ') {
            when {
                expression { env.GIT_BRANCH == 'develop' }
            }

            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,

                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-management-api",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(
                                                            remoteDirectory: "deploy/",
                                                            execCommand: "rm -rf *;"),
                                                    sshTransfer(
                                                            remoteDirectory: "deploy/dev/docker/",
                                                            sourceFiles: "build/libs/*.jar"),
                                                    sshTransfer(
                                                            sourceFiles: "deploy/dev/**",
                                                            execCommand: "chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/createDockerImageAndBackup.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/nginxContainerExistenceStatusCheck.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/application/applicationContainerNewRun.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/application/applicationHealthCheck.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/nginx/nginxDockerContainerRun.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/nginx/nginxServiceSetting.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/nginx/nginxHealthCheck.sh"),
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '๊ฐœ๋ฐœ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ')
                }
            } // Discord Send Post ๋
        } // stage('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ') ๋

        stage('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—…') {
            when {
                expression { env.GIT_BRANCH == 'develop' }
            }
            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,
                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-management-api",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(execCommand: "$DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/createDockerImageAndBackup.sh")
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—…')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—…')
                }
            } // Discord Send Post ๋
        } // stage("๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—…") ๋

        stage('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker ๊ด€๋ จ ์ž‘์—…') {
            when {
                expression { env.GIT_BRANCH == 'develop' }
            }
            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,
                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-management-api",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(execCommand: "$DOCKER_SERVER_BASE_DIRECTORY/deploy/dev/shell-script/nginxContainerExistenceStatusCheck.sh")
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker ๊ด€๋ จ ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker ๊ด€๋ จ ์ž‘์—…')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker ๊ด€๋ จ ์ž‘์—…')
                }
            } // Discord Send Post ๋
        } // stage("๊ฐœ๋ฐœ ํ™˜๊ฒฝ Docker ๊ด€๋ จ ์ž‘์—…") ๋

        stage('์šด์˜ ํ™˜๊ฒฝ WAS ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ') {
            when {
                expression { env.GIT_BRANCH == 'master' | env.GIT_BRANCH == 'main' }
            }

            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,

                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-total-back-office-was",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(
                                                            remoteDirectory: "deploy/",
                                                            execCommand: "rm -rf *;"),
                                                    sshTransfer(
                                                            remoteDirectory: "deploy/prod/was/docker/",
                                                            sourceFiles: "build/libs/*.jar"),
                                                    sshTransfer(
                                                            sourceFiles: "deploy/prod/was/**",
                                                            execCommand: "chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/createDockerImageAndBackup.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/applicationContainerExistenceStatusCheck.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/application/applicationContainerNewRun.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/application/applicationHealthCheck.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/nginx/nginxDockerContainerRun.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/nginx/nginxServiceSetting.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/nginx/nginxHealthCheck.sh"),
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '์šด์˜ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์“ฐ๊ธฐ ๊ถŒํ•œ ์„ค์ • ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ')
                }
            } // Discord Send Post ๋
        } // stage('์šด์˜ ํ™˜๊ฒฝ Application ์„œ๋ฒ„์— File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ') ๋

        stage('์šด์˜ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—…') {
            when {
                expression { env.GIT_BRANCH == 'master' | env.GIT_BRANCH == 'main' }
            }
            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,
                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-total-back-office-was",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(execCommand: "$DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/createDockerImageAndBackup.sh")
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '์šด์˜ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—…')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ Docker Image ์ƒ์„ฑ ๋ฐ Backup ์ž‘์—…')
                }
            } // Discord Send Post ๋
        } // stage("์šด์˜ ํ™˜๊ฒฝ Application ์„œ๋ฒ„ ๋ฐฐํฌ ์ž‘์—…") ๋

        stage('์šด์˜ ํ™˜๊ฒฝ Application Docker ๊ด€๋ จ ์ž‘์—…') {
            when {
                expression { env.GIT_BRANCH == 'master' | env.GIT_BRANCH == 'main' }
            }
            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,
                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-total-back-office-was",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(execCommand: "$DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/was/shell-script/application/applicationContainerExistenceStatusCheck.sh")
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '์šด์˜ ํ™˜๊ฒฝ Application Docker ๊ด€๋ จ ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ Application Docker ๊ด€๋ จ ์ž‘์—…')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ Application Docker ๊ด€๋ จ ์ž‘์—…')
                }
            } // Discord Send Post ๋
        } // stage("์šด์˜ ํ™˜๊ฒฝ Docker ๊ด€๋ จ ์ž‘์—…") ๋

        stage('์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX ๊ด€๋ จ File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ') {
            when {
                expression { env.GIT_BRANCH == 'master' | env.GIT_BRANCH == 'main' }
            }

            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,

                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-total-back-office-nginx",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(
                                                            remoteDirectory: "deploy/",
                                                            execCommand: "rm -rf *;"),
                                                    sshTransfer(
                                                            sourceFiles: "deploy/prod/nginx/**",
                                                            execCommand: "chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/nginx/shell-script/nginxContainerExistenceStatusCheck.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/nginx/shell-script/nginxDockerContainerRun.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/nginx/shell-script/nginxServiceSetting.sh; \
                                                                          chmod +x $DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/nginx/shell-script/nginxHealthCheck.sh"),
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX ๊ด€๋ จ File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX ๊ด€๋ จ File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX ๊ด€๋ จ File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ')
                }
            } // Discord Send Post ๋
        } // stage('์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX ๊ด€๋ จ File ์ „๋‹ฌ ๋ฐ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ') ๋

        stage('์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX Docker ๊ด€๋ จ ์ž‘์—…') {
            when {
                expression { env.GIT_BRANCH == 'master' | env.GIT_BRANCH == 'main' }
            }
            steps([$class: 'BapSshPromotionPublisherPlugin']) {
                script {
                    def startTimeMillis = System.currentTimeMillis()

                    sshPublisher(
                            continueOnError: false, failOnError: true,
                            publishers: [
                                    sshPublisherDesc(
                                            configName: "op-total-back-office-nginx",
                                            verbose: true,
                                            transfers: [
                                                    sshTransfer(execCommand: "$DOCKER_SERVER_BASE_DIRECTORY/deploy/prod/nginx/shell-script/nginxContainerExistenceStatusCheck.sh")
                                            ]
                                    )
                            ]
                    )

                    def endTimeMillis = System.currentTimeMillis()
                    def requiredTimeMillis = endTimeMillis - startTimeMillis
                    def requiredTimeSecond = requiredTimeMillis / 1000

                    echo '์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX Docker ๊ด€๋ จ ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : ' + requiredTimeSecond + ' ์ดˆ'
                }
            }

            post {
                success {
                    script {
                        sendSuccessAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX Docker ๊ด€๋ จ ์ž‘์—…')
                    }
                }

                failure {
                    sendFailedAlarmToDiscord('์šด์˜ ํ™˜๊ฒฝ DMZ ์„œ๋ฒ„ NGINX Docker ๊ด€๋ จ ์ž‘์—…')
                }
            } // Discord Send Post ๋
        } // stage("์šด์˜ ํ™˜๊ฒฝ Docker ๊ด€๋ จ ์ž‘์—…") ๋

        stage('Jenkins CI/CD ์ž‘์—… ์™„๋ฃŒ') {
            steps {
                sh 'echo "Jenkins CI/CD ์ž‘์—…์ด ์™„๋ฃŒ ๋˜์—ˆ์–ด์š”. ๐Ÿ˜Ž"'
            }

            post {
                success {
                    sendEndSuccessAlarmToDiscord()
                }

                failure {
                    sendEndFailedAlarmToDiscord()
                }
            } // Discord Send Post ๋
        } // stage('Jenkins CI/CD ์ž‘์—… ์™„๋ฃŒ') ๋
    } // stages ๋
}

void sendStartAlarmToDiscord() {
    discordSend(
            title: DISCORD_SEND_MESSAGE_TITLE,
            description: "${DRIVE_ENV} ํ™˜๊ฒฝ ๋ฐฐํฌ ์ž‘์—… ์‹œ์ž‘ ํ•ฉ๋‹ˆ๋‹ค.",
            footer: "==============๋ฐฐํฌ ์ž‘์—… ์‹œ์ž‘============\n\n"
                    + "ํ†ตํ•ฉ ๊ด€๋ฆฌ WAS ๋ฐฐํฌ ์ž‘์—…์ด ์‹œ์ž‘๋˜์—ˆ์–ด์š”.\n"
                    + "${env.JOB_NAME}(${env.BUILD_NUMBER})\n\n"
                    + "Git Pull Request ID ์ •๋ณด \n"
                    + "${PR_ID}\n\n"
                    + "Git Pull Request BRANCH ์ •๋ณด \n"
                    + "${PR_BRANCH}\n\n"
                    + "Git COMMIT ์œ ๋ฐœ์ž ์ •๋ณด \n"
                    + "${GIT_COMMIT_AUTHOR}\n\n"
                    + "Git COMMIT Message \n"
                    + "${GIT_COMMIT_MESSAGE}\n\n"
                    + "Build URL ์ •๋ณด \n\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}

void sendSuccessAlarmToDiscord(String stage) {
    discordSend(
            title: stage,
            description: stage + " ์ž‘์—… ์„ฑ๊ณตํ–ˆ์–ด์š”. ๐Ÿ˜€ ",
            footer: "โ“’ 2023. ๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค(giggals.pepole@gmail.com) All Rights Reserved. Blog : https://giggal-people.tistory.com/\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}

void sendSuccessAlarmAndRequiredTimeToDiscord(String stage, BigDecimal requiredTime) {
    discordSend(
            title: stage,
            description: stage + " ์ž‘์—… ์„ฑ๊ณตํ–ˆ์–ด์š”. ๐Ÿ˜€ \n " + "์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ : " + requiredTime + " ์ดˆ",
            footer: "โ“’ 2023. ๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค(giggals.pepole@gmail.com) All Rights Reserved. Blog : https://giggal-people.tistory.com/\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}

void sendFailedAlarmToDiscord(String stage) {
    discordSend(
            title: stage,
            description: stage + " ์ž‘์—… ์‹คํŒจ ํ–ˆ์–ด์š” ๐Ÿ˜ค",
            footer: "โ“’ 2023. ๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค(giggals.pepole@gmail.com) All Rights Reserved. Blog : https://giggal-people.tistory.com/\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}

void sendBuildSuccessAlarmToDiscord(String stage, String environment) {
    discordSend(
            title: environment + stage,
            description: environment + stage + " ์ž‘์—… ์„ฑ๊ณตํ–ˆ์–ด์š”. ๐Ÿ˜€ ",
            footer: "โ“’ 2023. ๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค(giggals.pepole@gmail.com) All Rights Reserved. Blog : https://giggal-people.tistory.com/\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}

void sendBuildFailedAlarmToDiscord(String stage, String environment) {
    discordSend(
            title: environment + stage,
            description: environment + stage + " ์ž‘์—… ์‹คํŒจ ํ–ˆ์–ด์š” ๐Ÿ˜ค",
            footer: "โ“’ 2023. ๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค(giggals.pepole@gmail.com) All Rights Reserved. Blog : https://giggal-people.tistory.com/\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}

void sendEndSuccessAlarmToDiscord() {
    discordSend(
            title: DISCORD_SEND_MESSAGE_TITLE,
            description: "${DRIVE_ENV} ํ™˜๊ฒฝ ๋ฐฐํฌ ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚ฌ์–ด์š”.",
            footer: "==============๋ฐฐํฌ ์ž‘์—… ๋============\n"
                    + "โ“’ 2023. ๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค(giggals.pepole@gmail.com) All Rights Reserved. Blog : https://giggal-people.tistory.com/\n\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}

void sendEndFailedAlarmToDiscord() {
    discordSend(
            title: DISCORD_SEND_MESSAGE_TITLE,
            description: "${DRIVE_ENV} ํ™˜๊ฒฝ ๋ฐฐํฌ ์ž‘์—…์ด ์‹คํŒจ๋กœ ๋๋‚ฌ์–ด์š”.",
            footer: "==============๋ฐฐํฌ ์ž‘์—… ๋============\n"
                    + "โ“’ 2023. ๊ธฐ๊น”๋‚˜๋Š” ์‚ฌ๋žŒ๋“ค(giggals.pepole@gmail.com) All Rights Reserved. Blog : https://giggal-people.tistory.com/\n\n",
            link: env.BUILD_URL,
            result: currentBuild.currentResult,
            webhookURL: DISCORD_WEBHOOK_URL
    )
}
728x90


์œ„์˜ ๋‚ด์šฉ์„ ์•Œ์•„๋ณด๊ธฐ ์ „์— ์  ํ‚จ์Šค์—์„œ ๋Œ€์ƒ ์„œ๋ฒ„ ์ฆ‰, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ตฌ๋™๋  ์„œ๋ฒ„์™€ SSH๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•ด์š”.

์ด ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” ์ด ๊ณณ์— ์ •์„ฑ์Šค๋Ÿฝ๊ฒŒ ์ค€๋น„ ํ•ด ๋‘์—ˆ์–ด์š”.

 

[Jenkins] Publish Over SSH Plugin์„ ์ด์šฉํ•œ ๋Œ€์ƒ ์„œ๋ฒ„ SSH ์ด์šฉ

๋ฐฐํฌ ์ž๋™ํ™”์™€ ์ง€์†์  ์ธ๋„:๋„์ปค์™€ ์  ํ‚จ์Šค ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋กœ ๋งŒ๋“œ๋Š” COUPANG www.coupang.com "์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค." ๐Ÿš€ ์  ํ‚จ์Šค Publish

junyharang.tistory.com

 

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Jenkins์—์„œ SSH๋ฅผ ์ด์šฉํ•ด์„œ Spring Boot jar File๊ณผ ๋ฐฐํฌ์— ํ•„์š”ํ•œ Shell Script
๊ทธ๋ฆฌ๊ณ , Nginx ๊ด€๋ จ File ์ „์†ก ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด ๋ณผ๊ฒŒ์š”.

jenkinsfile 349 ~ 384


์†Œ๋‚˜ํ๋ธŒ์˜ ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ ๊ฒ€์ฆ๊นŒ์ง€ ๋ชจ๋‘ ๋งˆ๋ฌด๋ฆฌ๊ฐ€ ๋˜๋ฉด ์šด์˜ ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•  ๋‹น์‹œ ์ด ๊ณณ์ด ๋จผ์ € ์‹คํ–‰๋˜๊ฒŒ ๋˜์š”.

์ตœ์ดˆ when ์ ˆ์„ ํ†ตํ•ด push ๋œ Git Branch ์ด๋ฆ„์„ ๋ถ„๊ธฐํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ํ•ด๋‹น Branch๊ฐ€ master์ด๊ฑฐ๋‚˜, main์ด๋ฉด ์ด stage๊ฐ€ ๋™์ž‘ํ•˜๋„๋ก ํ•ด ์ค€๊ฑฐ์—์š”.

ํ•ด๋‹น steps๋Š” ์  ํ‚จ์Šค์˜ sshPublisher ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ€์ƒ ์„œ๋ฒ„์— SSH๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒŒ์ผ์„ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜, ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ฃผ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•œ ๋ถ€๋ถ„์ด์—์š”.

๐Ÿ’ก ์ฐธ๊ณ  ์‚ฌํ•ญ
Jenkins sshPublisher Plugin

์  ํ‚จ์Šค -> Jenkins ๊ด€๋ฆฌ -> Plugin Manager


ํ•ด๋‹น ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์œ„์™€ ๊ฐ™์ด ์„ค์น˜ํ•ด ์ฃผ์–ด์•ผ ์ด์šฉ ๊ฐ€๋Šฅ.

 

๊ทธ๋Ÿฐ ๋’ค 356๋ฒˆ์งธ ์ค„์— ํ•ด๋‹น Stage ๊ฒฝ๊ณผ ์‹œ๊ฐ„์„ ์•Œ๊ณ  ์‹ถ์–ด์„œ ์‹œ์ž‘ ์‹œ๊ฐ์„ ms์œผ๋กœ ์ฐ์–ด startTimeMillis ๋ณ€์ˆ˜์— ๋‹ด์•„ ์ฃผ์—ˆ์–ด์š”.

359 ~ 366๋ฒˆ์งธ ์ค„์— Option ๋‚ด์šฉ์€ ์•„๋ž˜์™€ ๊ฐ™์•„์š”.

๐Ÿ’ก ์ฐธ๊ณ  ์‚ฌํ•ญ
sshPublisher๋Š” Jenkins์—์„œ ์ œ๊ณตํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์ค‘ ํ•˜๋‚˜์ธ SSH Publisher ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ช…๋ น

1. continueOnError: Jenkins ์ž‘์—… ์ค‘ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์„ ๋•Œ, ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐˆ์ง€์— ๋Œ€ํ•œ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” Option.

false์ผ ๊ฒฝ์šฐ sshPublisher ์ž‘์—… ์ค‘ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ์ž‘์—…์€ ์ค‘์ง€๋˜๊ณ , ์‹คํŒจ๋กœ ํ‘œ์‹œ.

2. FailOnError: Jenkins ์ž‘์—… ์ค‘ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์„ ๋•Œ, ์ž‘์—…์„ ์ค‘์ง€ํ•˜๊ณ , ์‹คํŒจ๋กœ ํ‘œ์‹œํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” Option.
true์ผ ๊ฒฝ์šฐ sshPublisher ์ž‘์—… ์ค‘ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ํ•ด๋‹น ์ž‘์—…์€ ์ค‘์ง€๋˜๊ณ , ์‹คํŒจ๋กœ ํ‘œ์‹œ

3. publishers[] : SSH Publisher Section ์„ค์ •

4. sshPublisherDesc() : SSH ์ „์†ก ์ž‘์—… ์ •์˜ Method. ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์–‘ํ•œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SSH ์ „์†ก ๊ตฌ์„ฑ์„ ์„ธ๋ถ€์ ์œผ๋กœ ์ง€์ • ๊ฐ€๋Šฅ. 

5. configName: SSH ์ „์†ก ๊ตฌ์„ฑ ์ด๋ฆ„์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์  ํ‚จ์Šค์—์„œ ์‚ฌ์ „์— ๊ตฌ์„ฑ๋œ SSH ์ „์†ก ์„ค์ •์„ ์ฐธ๊ณ ํ•˜์—ฌ ๋Œ€์ƒ ์„œ๋ฒ„์— SSH ์ ‘์† ์‹œ๋„

6. verbose: ์ „์†ก ์ž‘์—…์— ์ž์„ธํ•œ ๋‚ด์šฉ ์ถœ๋ ฅ ์—ฌ๋ถ€๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ true์ผ ๊ฒฝ์šฐ ์ „์†ก ์ž‘์—…์˜ ์ƒ์„ธ ์ •๋ณด ์ถœ๋ ฅ.

7. transfers: ์ „์†ก File ํ˜น์€ Directory ๊ตฌ์„ฑ์„ ์ •์˜ํ•˜๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜. sshTransfer ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์ „์†ก์„ ์ง€์ •.
sshTransfer ๋ฉ”์„œ๋“œ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ๋Š” ์ „์†กํ•  ํŒŒ์ผ ํ˜น์€ ๋””๋ ‰ํ† ๋ฆฌ์˜ ๊ฒฝ๋กœ ๋“ฑ์„ ์ง€์ •.

 

jenkinsfile 363 ~ 384


363๋ฒˆ์งธ ์ค„์— configName์€ ์  ํ‚จ์Šค sshPublisher Plugin์—์„œ ๋Œ€์ƒ ์„œ๋ฒ„ ์„ค์ • ์‹œ์— ์‚ฌ์šฉํ–ˆ๋˜
Hostname ๊ฐ’์„ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•ด์š”.

์  ํ‚จ์Šค ๊ด€๋ฆฌ -> Configure System

๋ฐ˜์‘ํ˜•


๋ฐ”๋กœ ์ด ๋ถ€๋ถ„์— ๊ฐ’์„ ์ž…๋ ฅํ•ด ์ฃผ๋ฉด ๋œ๋‹ต๋‹ˆ๋‹ค.

366 ~ 368๋ฒˆ์งธ ์ค„์—์„œ remoteDirectory์— ์ž…๋ ฅํ•ด ์ค€ deploy/ ๋Š” ์ „์†กํ•  ํŒŒ์ผ ํ˜น์€ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ๋Œ€์ƒ ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ ์–ด๋Š ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ „์†ก๋ ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์—์š”.
์ฆ‰, ์  ํ‚จ์Šค SSH ์„ค์ •์—์„œ Remote Directory๋ฅผ /data/deploy/giggal-total-back-office๋ผ๊ณ  ์ •์˜ํ•ด์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€๋‹น ๋””๋ ‰ํ† ๋ฆฌ ํ•˜์œ„์— deploy๋ฅผ ๊ฐ€๋ฅดํ‚ค๊ฒŒ ๋œ๋‹ต๋‹ˆ๋‹ค.

368๋ฒˆ์งธ ์ค„์— execCommand ๋ถ€๋ถ„์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ๋Œ€์ƒ ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ์—์„œ ์‹คํ–‰ํ•  ๋ช…๋ น์–ด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ์จ ์ฃผ๋‹ˆ๋Š” rm -rf * ๋ช…๋ น์–ด ์ฆ‰, ํ•ด๋‹น ๋””๋ ‰ํ† ๋ฆฌ์— ๋ชจ๋“  ๋‚ด์šฉ์„ ์‚ญ์ œํ•˜๋ผ๋Š” ๋ช…๋ น์–ด๋ฅผ ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋Ÿฐ ๋’ค 370๋ฒˆ์งธ ์ค„์— ๋‹ค์‹œ remoteDirectory๋ฅผ deploy/prod/was/docker/ ๋ผ๊ณ  ์„ค์ •ํ•ด ์ฃผ์—ˆ๊ณ , 
sourceFiles๋ฅผ ํ†ตํ•ด Spring Boot Project ์ตœ์ƒ๋‹จ์— ์œ„์น˜ํ•œ build Directory ๋ฐ‘์— libs ๋ฐ‘์— ์œ„์น˜ํ•œ ํ™•์žฅ์ž๊ฐ€ .jar ์ธ ํŒŒ์ผ์„ ๋‹ค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ๋Œ€์ƒ ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ์˜ deploy/prod/was/docker/ ๋ฐ‘์— ๋ณด๋‚ด๋ผ๊ณ  ํ•ด ์ค€๊ฑฐ์—์š”.

๊ทธ๋ฆฌ๊ณ , 372๋ฒˆ์งธ ์ค„์— Spring Boot Project ์ตœ์ƒ๋‹จ์— ์œ„์น˜ํ•œ deploy Directory ๋ฐ‘์— prod/was ํ•˜์œ„์— ์žˆ๋Š” ๋ชจ๋“  ๋‚ด์šฉ์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ๋Œ€์ƒ ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ์˜ /data/deploy/giggal-total-back-office ๋ฐ‘์— ๋ณด๋‚ด๋ผ๊ณ  ํ•ด ์ฃผ์—ˆ์–ด์š”.

374 ~ 380๋ฒˆ์งธ ์ค„๊นŒ์ง€๋Š” ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ๋Œ€์ƒ ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ์—์„œ ๋„์ปค ๋ฐฐํฌ ๋ฐ ๋ฐฑ์—… ๋“ฑ์— ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ShellScript์— ์‹คํ–‰ ๊ถŒํ•œ์„ ์ฃผ๊ฒŒํ•˜์—ฌ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌํ•ด ์ค€ ๋ถ€๋ถ„์ด์—์š”.


jenkinsfile 386 ~ 405


356๋ฒˆ์งธ ์ค„์— ํ•ด๋‹น Stage ์ด ๊ฒฝ๊ณผ ์‹œ๊ฐ„์„ ์•Œ๊ธฐ ์œ„ํ•ด ์‹œ์ž‘ ์‹œ๊ฐ์„ ms๋กœ ๋ณ€์ˆ˜์— ๋‹ด์•„ ์ฃผ์—ˆ๋‹ค๊ณ  ํ–ˆ์—ˆ์–ด์š”.

386๋ฒˆ์งธ ์ค„์—๋Š” ํ•ด๋‹น Stage๊ฐ€ ๋๋‚œ ์‹œ๊ฐ์„ ms๋กœ endTimeMillis์— ๋‹ด์•„์ฃผ์—ˆ์–ด์š”.
๊ทธ๋ฆฌ๊ณ , ๋๋‚œ ์‹œ๊ฐ์—์„œ ์‹œ์ž‘ ์‹œ๊ฐ์„ ๋นผ์„œ requiredTimeMillis ๋ณ€์ˆ˜์— ๋‹ด์•„์ฃผ๊ณ , ์ด๋ฅผ 1000์œผ๋กœ ๋‚˜๋ˆ  ms๋ฅผ ์ดˆ๋กœ ๋ฐ”๊ฟ”์ค€ ๋’ค requiredTimeSecond ๋ณ€์ˆ˜์— ๋‹ด์•„ ์ฃผ์—ˆ์–ด์š”.

๊ทธ๋Ÿฐ ๋’ค echo ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ํ•ด๋‹น Stage ๋™์ž‘ ์†Œ์š” ์‹œ๊ฐ„์ด ์ถœ๋ ฅ๋˜๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.

post ์ ˆ์—์„œ ํ•ด๋‹น Stage ์„ฑ๊ณต ์—ฌ๋ถ€์— ๋”ฐ๋ผ Discord์— ์•Œ๋ฆผ์ด ๊ฐ€๋„๋ก ํ•ด ์ฃผ์—ˆ์–ด์š”.

์„ฑ๊ณตํ•˜๊ฒŒ ๋˜๋ฉด
sendSuccessAlarmToDiscord() ๊ฐ€ ํ•ด๋‹น Stage ์ œ๋ชฉ์„ ๊ฐ€์ง€๊ณ  ํ˜ธ์ถœ๋˜๊ฒŒ ํ•˜์˜€๊ณ , ์‹คํŒจํ•˜๊ฒŒ ๋˜๋ฉด sendFailedAlarmToDiscord()๋ฅผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Stage ์ œ๋ชฉ์„ ๊ฐ€์ง€๊ณ  ํ˜ธ์ถœํ•˜๊ฒŒ ํ•ด ์ฃผ์—ˆ์–ด์š”.

jenkinsfile 625 ~ 645


๊ทธ๋Ÿผ ์„ฑ๊ณต๊ณผ ์‹คํŒจ์— ๋”ฐ๋ผ ์œ„์˜ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๊ฒŒ ๋˜๊ณ , ์ด์— ๋”ฐ๋ผ ์•„๋ž˜์™€ ๊ฐ™์ด Discord์— ์•Œ๋ฆผ์ด ์˜ค๊ฒŒ ๋œ๋‹ต๋‹ˆ๋‹ค.

Stage ์„ฑ๊ณต ์‹œ

 

Stage ์‹คํŒจ ์‹œ



์ด๋ฒˆ ๊ธ€์—๋Š” ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ฆฌํ•˜๊ณ , ๋‹ค์Œ ๊ธ€์—์„œ๋Š” ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค๊ณ , ๋ฐฑ์—…ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด ๋ณผ๊ฒŒ์š”.



 

๋ฐฐํฌ ์ž๋™ํ™”์™€ ์ง€์†์  ์ธ๋„:๋„์ปค์™€ ์  ํ‚จ์Šค ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค๋กœ ๋งŒ๋“œ๋Š”

COUPANG

www.coupang.com

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 

 

์ด์ „ ๊ธ€: [DevOps] JAVA Gradle JaCoCo (Code coverage) ์„ค์ •ํ•˜๊ธฐ (์ถ”๊ฐ€)

๋‹ค์Œ ๊ธ€: [CI/CD] Jenkins + Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ - โ‘ก Create Docker Image And BackUp

 

 

 

 

 

728x90
๋ฐ˜์‘ํ˜•