๐ŸŒ‹ CI/CD pipeline ๋ฐฐํฌ ์‹คํŒจ โ€“ env variable ์„ค์ •์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ

๊น€๊ณต์˜ยท2024๋…„ 5์›” 28์ผ

how-to

๋ชฉ๋ก ๋ณด๊ธฐ
3/12
post-thumbnail

์ด ๊ธ€์€ ์ €์˜ ๋ฌด์ง€ํ•จ์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์ง€๋งŒ, github actions workflow์—์„œ์˜ env variable ์„ค์ • ๋ฐ ์ ์šฉ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ฃผ๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋‹ค

๋ถ„๋ช…ํžˆ ์ฒ˜์Œ CI/CD pipeline์„ Github actions๋กœ ๊ตฌ์ถ•ํ•œ ํ›„ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์–ธ์  ๊ฐ€๋ถ€ํ„ฐ dev branch์— mergeํ•  ๋•Œ ๊ฐ„ํ—์ ์œผ๋กœ failure ๋ฉ”์„ธ์ง€๊ฐ€ ๋œจ๋Š” ๊ฒƒ์„ ๋ณด์•˜๋‹ค. ๋ชจ์ข…์˜ ์ด์œ ๋กœ ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ด ์ด๋ฃจ์–ด์ง€๊ณ  ์žˆ์ง€ ์•Š์€ ์ƒํ™ฉ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹น์žฅ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ•ด ๋ฐฉ์น˜ํ–ˆ๋Š”๋ฐ, 3๋ฒˆ์— 1๋ฒˆ์€ ๋œจ๋˜ success ๋ฉ”์„ธ์ง€๋งˆ์ € ์‚ฌ๋ผ์ ธ ํ•ด๊ฒฐํ•ด์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. ๋ญ”๊ฐ€ ์„ค์ •ํ•˜๋Š” ๊ณผ์ •์—์„œ ์ž˜๋ชป๋œ ๊ฒƒ ๊ฐ™๋‹ค.

์ฐธ๊ณ ๋กœ ์ด์ „์— ๋œจ๋˜ failure๋Š” ๋‹ค๋ฅธ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž์™€ ํ˜‘์—…ํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•œ build ๋ฌธ์ œ์˜€๊ธฐ ๋•Œ๋ฌธ์— ์„ค๋งˆ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์ด ์ž˜๋ชป๋˜์–ด์žˆ์„ ๊ฑฐ๋ผ๋Š” ์ƒ๊ฐ์„ ๋ชปํ–ˆ๋‹ค.

๋ฌธ์ œ ๋ฐœ์ƒ ์›์ธ์„ ํŒŒ์•…ํ•˜์ž

์–ด๋””์„œ๋ถ€ํ„ฐ ์ž˜๋ชป๋œ ๊ฑด์ง€๋ฅผ ์•Œ๊ธฐ ์œ„ํ•ด Github actions workflow๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ์ฝ์—ˆ๋‹ค. ๋‚ด๊ฐ€ ํŒŒ์•…ํ•œ ์›์ธ์€ ๋‹ค์Œ์˜ 2๊ฐ€์ง€์˜€๋‹ค.

1. RDS datasource connection url์ด ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒƒ
2. Github secret variable ๊ฐ’์ด ์ œ๋Œ€๋กœ ์ ์šฉ๋˜์ง€ ์•Š์€ ๊ฒƒ

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

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด๋ณด์ž

RDS datasource url ์„ค์ •

์•ž์„œ ๋งํ–ˆ๋“ฏ ์ด ๋ฌธ์ œ๋Š” RDS datasource๋ฅผ ๋ฐฐํฌ์šฉ application.yml์— ์„ค์ •ํ•ด๋‘์ง€ ์•Š์€ ๊ฒƒ์ด ์›์ธ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์ถ”๊ฐ€ํ•ด ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

...
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: jdbc:mysql://{RDS datasource url}:3306/{schema}?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Seoul
    username: admin
    password: {password}
    driver-class-name: com.mysql.cj.jdbc.Driver
...

datasource๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„ ์™ธ์—๋Š” ์ƒ๋žตํ–ˆ๋‹ค. application.yml ์ˆ˜์ • ํ›„ local ํ™˜๊ฒฝ์—์„œ dev profile ์ ์šฉ ํ›„ ์ •์ƒ์ ์œผ๋กœ RDS์— ์—ฐ๊ฒฐ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค.

Github secret variable์„ build workflow์— ์ ์šฉ

์‚ฌ์‹ค ์ด๊ฑด ๋น„๋ฐ€์ธ๋ฐ...

๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ˆ„๊ตฌ๋‚˜ ์•Œ๊ฒ ์ง€๋งŒ, AWS IAM ์‚ฌ์šฉ์ž์˜ access key์™€ secret key๋Š” ์™ธ๋ถ€์— ๋…ธ์ถœํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์–ด๋ผ? ์ด๊ฑธ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์˜ฌ๋ ค๋ฒ„๋ฆฐ ์‚ฌ๋žŒ์ด ์žˆ๋„ค? ๊ทธ๊ฒŒ ๋‚˜๋‹ค.
๋กœ์ปฌ ํ…Œ์ŠคํŠธ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•˜๋˜ ํ™˜๊ฒฝ ํŒŒ์ผ(application.yml)์„ ์‹ค์ˆ˜๋กœ ์ปค๋ฐ‹ํ•ด๋ฒ„๋ ธ๋‹ค. ์•„๋ฌด ์ƒ๊ฐ ์—†์ด ์ปค๋ฐ‹์„ ํ•˜๋Š”๋ฐ, ๊ฐ‘์ž๊ธฐ ์ปค๋ฐ‹์ด ์•ˆ๋œ๋‹ค๊ณ  ๋ง‰๊ธธ๋ž˜ ๊ธฐ๋ถ„์ด ์•ˆ ์ข‹์•˜๋Š”์ง€ "๋ˆ„๊ฐ€ ๋‚ด ์ปค๋ฐ‹์„ ๋ง‰์•„!" ํ•˜๋ฉด์„œ ๊ฒฝ๊ณ  ๋ฉ”์„ธ์ง€๋ฅผ ๋„๊ณ  ๊ทธ๋ƒฅ ์ปค๋ฐ‹ํ•ด๋ฒ„๋ ธ๋‹ค.. ๋ฌผ๋ก  ์ด๋Ÿฌ๋ฉด ์ ˆ๋Œ€ ์•ˆ๋œ๋‹ค
๋ณ„ ์ƒ๊ฐ ์—†์—ˆ๋Š”๋ฐ ๊ฐ‘์ž๊ธฐ AWS์—์„œ ๋ฉ”์ผ์ด ์˜ค๊ณ , ๋ญ”๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์ธ์ง€ํ•ด ์ด๋ฅผ secret ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•ด ํ•ด๊ฒฐํ–ˆ์—ˆ๋‹ค. ๊ทธ ๊ณผ์ •์€ ๐Ÿ› ๏ธ CI/CD pipeline ๊ตฌ์ถ•, ๊ทผ๋ฐ secret ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๊ณ๋“ค์ธ..์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก  ์ € ๊ธ€์€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋˜์–ด์žˆ๋‹ค.

Error message

secrete variable์„ ๋ถ„๋ช… ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•ด์คฌ๋Š”๋ฐ, ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋กœ ๋ฏธ๋ฃจ์–ด ๋ณด์•„ ์ œ๋Œ€๋กœ ์ ์šฉ๋˜์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™๋‹ค.

  ...
  Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'AWS_ACCESS_KEY_ID' in value "${AWS_ACCESS_KEY_ID}"
          at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-6.1.5.jar!/:6.1.5]
          at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-6.1.5.jar!/:6.1.5]
  ...

failure๐Ÿค” : EC2์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•ด๋ณด์ž

ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์ œ๋Œ€๋กœ ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์•„ EC2 ์„œ๋ฒ„์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋ณ„๋„๋กœ ์„ค์ •ํ–ˆ๊ณ , ํ…Œ์ŠคํŠธํ–ˆ์œผ๋‚˜ ์‹คํŒจํ–ˆ๋‹ค. EC2 ๋‚ด๋ถ€์—์„œ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์ž˜ ์„ค์ •๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๊ธฐ์— EC2์˜ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค. local์—์„œ docker image๋ฅผ ๋ฐ›์•„ ์‹คํ–‰์‹œ์ผฐ์œผ๋‚˜ ๊ฐ™์€ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๊ฐ€ ๋– ์„œ ์—ฌ์ „ํžˆ env variable์ด ์ œ๋Œ€๋กœ ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค.

solution : secret varable์„ build workflow์— ์ง์ ‘ ์ ์šฉํ•˜์ž

๊ฒฐ๋ก ์ ์œผ๋กœ env variable์€ build ๊ณผ์ •์—์„œ ์ด๋ฏธ ์„ค์ •๋˜์–ด ์žˆ์–ด์•ผ ํ•˜๊ณ , ์ด๋ฅผ ํฌํ•จํ•ด build๊ฐ€ ์ด๋ฃจ์–ด์ ธ์•ผ ํ•œ๋‹ค. ์†”์งํžˆ ๋„ˆ๋ฌด ๋‹น์—ฐํ•œ ๋ง์ด๋ผ์„œ ์–ด์ด๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๊ทธ ๋‹น์—ฐํ•œ ๊ฑธ ์žŠ๊ณ  ์žˆ์—ˆ๋‹ค.
Github actions workflow์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋นŒ๋“œ ํ™˜๊ฒฝ์€ ํ• ๋‹น ๋ฐ›์€ vm์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์ž๋™์œผ๋กœ ์„ค์ •๋˜์ง€ ๋ชปํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ secret.{env variable}์„ ์ง์ ‘ application.yml์— ์ ์šฉํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ–ˆ๋‹ค.
์•„๋ž˜ ์ฝ”๋“œ๋ฅผ Gradle build ๊ณผ์ • ์ „์— ๋„ฃ์–ด ์ง์ ‘ yml ํŒŒ์ผ์— ์žˆ๋Š” ๋ณ€์ˆ˜๋ฅผ secret์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์™€ ์ ์šฉํ•ด์คฌ๋‹ค.

# yml ํŒŒ์ผ์— ์žˆ๋Š” ๋ณ€์ˆ˜๋“ค์„ secrets๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์™€์„œ yml ํŒŒ์ผ์— ์ ์šฉ
  - name: Set yml file
    uses: microsoft/variable-substitution@v1
    with:
    	files: 'src/main/resources/application.yml'
    env:
    	cloud.aws.credentials.access-key: ${{ secrets.AWS_ACCESS_KEY }}
    	cloud.aws.credentials.secret-key: ${{ secrets.AWS_SECRET_KEY }}

ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์ ์šฉํ•ด ๋‹ค์‹œ ๋นŒ๋“œํ•˜๋‹ˆ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค. ํ•ด๊ฒฐํ•˜๊ณ ๋‚˜๋‹ˆ ๋†€๋ž๋„๋ก ํ—ˆ๋ฌดํ•œ๋ฐ ๋‚ด ๋ฉ์ฒญํ•จ์ด ์–ด๋””๊นŒ์ง€ ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ์ง€ ๋‚˜๋„ ์ข€ ๊ธฐ๋Œ€๊ฐ€ ๋  ์ •๋„์˜€๋‹ค.

local ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” intellij์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ž์„ธํ•œ ๋ฐฉ๋ฒ•์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค.

๊ทธ๋ž˜์„œ ๋ญ˜ ๋А๊ผˆ๋Š”๋ฐ

์ฒ˜์Œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ•  ๋•Œ์—๋Š” ๊ทธ๋ ‡๊ฒŒ ๋งŽ์€ ์ž๋ฃŒ๋ฅผ ์ฐพ์•„๋ณด์ง€ ์•Š์•„๋„ ์‰ฝ๊ฒŒ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์‚ฌ์‹ค Github actions๋ฅผ ์จ๋ณด๋Š”๊ฒŒ ์ฒ˜์Œ์ด์ง€, ์ด์ „์— ๋‹ค๋ฅธ ํˆด์„ ํ™œ์šฉํ•ด์„œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์•„๋ฌด ์ƒ๊ฐ ์—†์ด ์ฝ”๋“œ๋งŒ ์งฐ๋Š”๋ฐ, ์ด๊ฒŒ ๋ฌธ์ œ๊ฐ€ ๋˜์—ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•ด ๊ตฌํ˜„ํ•  ๋•Œ๋Š” ์ดํ•ด๋ฅผ ํ•˜๊ณ  ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด์ž๐Ÿ‘ป ๋ฌด์ž‘์ • ๊ฐ€์ ธ์™€ ์“ฐ๋ฉด ๋„Œ GPT์™€ ๋‹ค๋ฅผ ๋ฐ”๊ฐ€ ์—†๋‹ค. ์‚ฌ์‹ค ๊ทธ๋ณด๋‹ค ๋ชปํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค. ๊ทธ๋ž˜๋„ ์ด ๊ณผ์ •์„ ํ†ตํ•ด์„œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ํ™˜๊ฒฝ ๋ณ€์ˆ˜, ๋นŒ๋“œ ๊ณผ์ •์— ๋Œ€ํ•ด ๋” ์ž˜ ์ดํ•ดํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ์งค ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด ์ข‹์•˜๋‹ค. ์•ž์œผ๋กœ๋Š” ๊ฐ™์€ ์‹ค์ˆ˜ ๋ฐ˜๋ณตํ•˜์ง€ ๋ง์ž.

profile
๋‚˜๋Š”์•ผ ๋งํ•˜๋Š” ๊ฐœ๋ฐœ(๊ฐ)์ž

0๊ฐœ์˜ ๋Œ“๊ธ€