๋ก์ปฌ์์ Spring Boot ๋น๋ํ๊ณ docker image ๋ง๋ค์ด์ docker hub์ ์ฌ๋ฆฌ๊ณ ..
EC2 ๋ค์ด๊ฐ์ docker hub์ ์๋ ์ด๋ฏธ์ง pull ๋ฐ์์ run ์ํค๊ธฐ...
๊ฐ๋ฐ์ ํ๋ฉด ํ ์๋ก CI/CD์ ์ค์์ฑ์ด ๊น๊ฒ ๋๊ปด์ง๋ ๊ฒ ๊ฐ๋ค...
๋ฐฐํฌ์ ์์ด์ ๋์ ๊ฐ์ด ๊ท์ฐฎ์ ํน์ ์ด๋ ค์์ ๋๋ผ๊ณ ์๋ ๋ค๋ฅธ ๋ถ๋ค์ ์ํด ๊ธฐ๋ก์ ๋จ๊ธฐ๋ ค๊ณ ํ๋ค
์์ํ๊ธฐ์ ์์
์ฌํ ์ฌ์ฐ (Jenkins ๋์ ๊ธฐ..)
์ผ๋จ ์์ํ๊ธฐ์ ์์ Jenkins์ ๋์ ํ๊ธฐ๊น์ง์ ์ฌํ ์ ์ค์ด ์๋ค..
https://hanjungyo.tistory.com/126
Github Actions ํบ์๋ณด๊ธฐ
Github Actions์ ๊ธฐ๋ณธ ๊ฐ๋ ์ผ๋จ Github Actions๋ ์ํํธ์จ์ด ๊ฐ๋ฐ ์ํฌํ๋ก์ฐ๋ฅผ ์๋ํํ ์ ์๋ CI/CD ๋๊ตฌ์=> ์ฝ๋๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋น๋, ํ ์คํธ, ๋ฐฐํฌ ๋ฑ์ ์์ ์ ์๋์ผ๋ก ์คํํ ์ ์์ Github
hanjungyo.tistory.com
์๋ ์ ๊ฒ์๊ธ์ ์ด๋ฆ์ Github Actions๋ฅผ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ์๋ค...
Github Actions๋ GitHub์ ์ฐ๋๋๊ธฐ์ ๊ต์ฅํ ํธ๋ฆฌํ ๊ธฐ๋ฅ๋ค์ด ๋ง์๊ณ ๋์ด๋๋ ์๋์ ์ผ๋ก ์ฌ์์ ์ ํํ๊ฒ ๋์๋๋ฐ
์ด์ฌํ ๊ธ์ ์ฐ๋ ๋์ค ํ์ฌ ํ๋ก์ ํธ์(์กธ์ ํ๋ก์ ํธ) ๋ ํฌ์งํ ๋ฆฌ๊ฐ ๋ํ๊ต organization์์๋ง ์์ฑ์ ํด์ผํ๋๋ฐ Repo Setting ๊ถํ์ด ๋งํ์์...
self-hosted runner ํ๊ฒฝ์ ์ฐ์๋ ๊ฒฐ๊ตญ EC2(t2.micro ์ฌ์ฉ์ค)์์ ๋น๋๋ฅผ ๋๋ ค์ผ ํ๋ ์ํฉ์ด ์๊ธฐ๋๋ฐ t2.micro์์ ์๋ํด๋ณธ ๊ฒฐ๊ณผ ๋น๋๋ฅผ ํ๋ ๋์ค CPU 100%์ ๋ซ๊ณ ๋ฌดํ ๋ก๋ฉ์ ๊ฑธ๋ ค๋ฒ๋ฆฌ๋ ๋ฆฌ์์ค ์ด์๊ฐ ํ์
๋์๊ธฐ ๋๋ฌธ์ Github Actions๋ฅผ ํฌ๊ธฐํ๋ค
(์ด์ฉํผ ์๋ณธ ๋ ํฌ์งํ ๋ฆฌ์์ runner ๋ฑ๋ก ๋ชปํ๋ฏ๋ก self-hosted runner๋ ๋ถ๊ฐ๋ฅ)
AWS Lambda + AWS CodeBuild + GitHub Actions๋ก ๋ ํฌ์งํ ๋ฆฌ Setting์ ์ฌ์ฉํ์ง ๋ชปํ๋ ์ํฉ์ ์ฐํ? ํ ์ ์์ ๊ฒ ๊ฐ์ง๋ง ๋น์ฉ๊ณผ ๋คํธ์ํฌ ์ง์ฐ ๋ฑ์ผ๋ก Jenkins๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข์ ์ ํ์ด๋ผ๊ณ ์๊ฐํจ
=> Jenkins์์ Git Plugin์ผ๋ก ๋ชฉํ๋กํ๋ "Git ์ ์ฅ์์ ํตํฉํ์ฌ ํธ๋ฆฌ๊ฑฐ"๋ฅผ ํ ์ ์๊ณ Circle CI, Travis CI ๊ฐ์ ๋ค๋ฅธ CI/CD ํด๋ค๋ ์ดํด๋ณด์๋๋ฐ ๋ฌด๋ฃ ํ๋์ ํ๊ณ ๋ฐ ๋น์ฉ ์ด์๋ ์๊ธฐ์ ์คํ์์ค์ธ Jenkins๋ฅผ ์ ํํจ
Jenkins๋?

Jenkins๋ ์คํ ์์ค CI/CD ๋๊ตฌ๋ก, ์ํํธ์จ์ด ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ์๋ํํ๋ ๋ฐ ์ฌ์ฉ๋จ
=> ๋ค์ํ ํ๋ฌ๊ทธ์ธ์ ์ง์ํ์ฌ ๋น๋, ๋ฐฐํฌ, ํ ์คํธ ๋ฑ์ ์์ ์ ์ ์ฐํ๊ฒ ์ํํ ์ ์์ผ๋ฉฐ ์ฌ๋ฌ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ๋ฒ์ ๊ด๋ฆฌ ์์คํ ์ ์ง์ํจ
ํน์ง์ ์ดํด๋ณด๋ฉด
- ์ฝ 1500๊ฐ ์ด์์ ํ๋ฌ๊ทธ์ธ์ ํตํด ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์์ด, ํ์ ์๊ตฌ ์ฌํญ์ ๋ง๊ฒ ์ปค์คํฐ๋ง์ด์ฆํ ์ ์์
- ํ์ดํ๋ผ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด CI/CD ํ๋ก์ธ์ค๋ฅผ ์ฝ๋๋ก ์ ์ํ ์ ์์
=> ๋ฒ์ ๊ด๋ฆฌ ์์คํ ์์ ํ์ดํ๋ผ์ธ์ ๊ด๋ฆฌํ ์ ์์ - ์คํ์์ค์ด๊ธฐ์ ๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์๊ณ , ์ปค๋ฎค๋ํฐ๊ฐ ์ ํ์ฑํ ๋์ด์์
- ์น ๊ธฐ๋ฐ์ ๋์๋ณด๋๋ฅผ ์ ๊ณตํ๊ธฐ์ ๋น๋ ์ํ์ ๋ก๊ทธ๋ฅผ ์ฝ๊ฒ ํ์ธํ ์ ์์
Jenkins๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋จ์ผ ์๋ฒ์์ ์คํ๋๊ธฐ์ ๋ง์ ํ๋ฌ๊ทธ์ธ๊ณผ ์์ ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋ฆฌ์์ค๋ฅผ ๋ง์ด ์๋ชจํ๋ฉฐ ์ด๋ก ์ธํด ๋๊ท๋ชจ ํ๋ก์ ํธ์์ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋
Jenkins ์์ด์ ํธ๋ฅผ ์ค์ ํ์ฌ ์์ ์ ๋ถ์ฐ์ํค๊ฑฐ๋ AWS, GCP์ ๊ฐ์ ํด๋ผ์ฐ๋ ์๋น์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ๋ฆฌ์์ค๋ฅผ ํ์ฅํ๊ฑฐ๋, ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น๋ ํ๋ฌ๊ทธ์ธ์ ์ต์ํํ๊ณ ํ์์ ์ธ ํ๋ฌ๊ทธ์ธ๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์
Jenkins์ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ
์๋ EC2์ Jenkins ์๋ฒ๋ฅผ ์คํ์์ผ๋๊ณ ํ๋ ค๊ณ ํ๋๋ฐ ๊ฐ๋ฐ ์ด๊ธฐ ๋จ๊ณ์ด๊ณ ํ ์ธ์๋ ์ ๊ธฐ์
๋น์ฉ์ ์ต๋ํ ์ ์ฝํ๊ณ ์ ๋ก์ปฌ์์ ์งํํ์์ต๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด CD๋ฅผ ํ๊ธฐ ์ํด์๋ ๋ก์ปฌ์์ jenkins ์๋ฒ๋ฅผ ํค๊ณ ์์ด์ผ ํ๊ธฐ์ ์ธ์ ํ ๋ช
์ด ์ด๋ฅผ ๋ด๋นํด์ผํจ
ํ์ง๋ง, ์์์ ๋งํ๋๋ก ์ธ์๋ ์ ๊ณ ๊ฐ๋ฐ ์ด๊ธฐ ๋จ๊ณ๋ผ ์์ง ๋น๋ฒํ๊ฒ merge๊ฐ ๋ฐ์ํ์ง ์๊ธฐ์ ๋ก์ปฌ์์ jenkins๋ฅผ ์ฌ์ฉํ๋ฉฐ ๋น์ฉ์ ์ต๋ํ ์ ์ฝํ๋ค๊ฐ ์ถํ MVP(Minimum Viable Product)๊ฐ ์์ฑ๋๋ฉด์ ์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ์ด ๋น๋ฒํด์ง ๋ EC2 ์์ ์ฌ๋ฆฌ๋ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝ ์์
EC2์์ Jenkins๋ฅผ ๋์ฐ๊ฒ ๋ ๋ ๊ด๋ จ ๋ด์ฉ๋ ๊ธฐ๋กํด๋๋๋ก ํ๊ฒ ์ต๋๋ค.
(๋ฐฉ์์ ๊ฑฐ์ ์ ์ฌํจ)
Mac์์ ์งํํ์์ต๋๋คโผ๏ธ
1. Jenkins ์ค์น ๋ฐ ์ค์
ํฐ๋ฏธ๋์์ brew install jenkins-lts ๋ช ๋ น์ด ์ ๋ ฅ
=> Homebrew๋ฅผ ์ฌ์ฉํ์ฌ Jenkins๋ฅผ ์ค์น
(์๊ฐ์ด ์ข ์์๋๊ณ Java๋ก ์์ฑ๋์๊ธฐ ๋๋ฌธ์ JDK๋ฅผ ๋จผ์ ์ค์นํ๋ฉด ์ข์
-> Homebrew์์ ์๋์ผ๋ก OpenJDK๋ฅผ ์ค์นํ๋ ๊ฒฝ์ฐ๋ ๋ง๋ค๊ณ ํจ)

brew services start jenkins-lts ๋ช ๋ น์ด๋ก Jenkins ์๋น์ค๋ฅผ ์์ํ๊ณ
์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์ด์ด http://localhost:8080 ์ผ๋ก ์ ์ํ๋ฉด Jenkins์ ์ ๊ทผํ ์ ์์
cat /Users/$(whoami)/.jenkins/secrets/initialAdminPassword
์ ๋ช ๋ น์ด๋ฅผ ํตํด ์ด๊ธฐ ์ํธ๋ฅผ ์ฐพ์ ํ ์ ๋ ฅํด์ค์ผํจ

๊ทธ ํ ์ด๊ธฐ ์ธํ
(ํ๋ฌ๊ทธ์ธ)์ ํด์ค์ผํ๋๋ฐ ์์์ ๋งํ๋ฏ์ด ํ์ํ ํ๋ฌ๊ทธ์ธ๋ง ์ค์นํ๋๊ฒ ์ฑ๋ฅ๋ฉด์์ ๋ ์ข์ ์ ํ์ด์ง๋ง ์ด์ ๋ง ์ฒ์ ์จ๋ณด๋ ๋จ๊ณ์ด๊ธฐ์ ์ผ๋จ suggested๋ก ์ค์น๋ฅผ ํ๊ณ Git๊ณผ Docker๋ง ์ถ๊ฐ ์ค์น๋ฅผ ํด์คฌ์
(Dashboard -> Jenkins ๊ด๋ฆฌ -> Plugins์์ ์ถํ ์ค์น ๊ฐ๋ฅ)

์ค์น๊ฐ ์๋ฃ๋๋ฉด ๊ณ์ ์ ์์ฑํ๋ ํ์ด์ง๋ก ๋์ด๊ฐ๋๋ฐ ๊ณ์ ์์ฑ ๋ฐ URL์ ์ค์ ํ ์ ์์
2. SSH ์ค์
SSH ์ธ์ฆ ๋ฐฉ์
SSH(Secure Shell)์ ๋น๋์นญ ์ํธํ ๋ฐฉ์์ ์ฌ์ฉํด์ ์์ ํ๊ฒ ์๊ฒฉ ์๋ฒ์ ์ ์ํ๋ ํ๋กํ ์ฝ์
=> ๊ณต๊ฐํค์ ํ๋ผ์ด๋น ํค๋ก ์ด๋ฃจ์ด์ง ํค ์์ ์ฌ์ฉ
ํด๋ผ์ด์ธํธ(์ ์ํ๋ ์ชฝ)๊ฐ private key(id_rsa)๋ฅผ ๊ฐ์ง๊ณ ์๊ณ ์๋ฒ(์ ์ ๋ฐ๋ ์ชฝ)์ด public key(id_rsa.pub)์ ๊ฐ์ง๊ณ ์์ด์ผ ํต์ ์ด ๊ฐ๋ฅํจ
ํ์ฌ ์ํฉ์์ ์๊ฐํด๋ณด๋ฉด Jenkins์์ GitHub์ EC2์ ์ ๊ทผํ์ฌ CD์์
์ ์ํํด์ผ ํ๋ฏ๋ก
- ํ๋ผ์ด๋น ํค (id_rsa) โ Jenkins์ด ๊ฐ์ง๊ณ ์์ด์ผ ํจ
- ๊ณต๊ฐ ํค (id_rsa.pub) โ ์๊ฒฉ ์๋ฒ(GitHub, EC2)์ ~/.ssh/authorized_keys์ ์ ์ฅ๋์ด ์์ด์ผ ํจ
ssh-keygen -t rsa -b 4096 -C "github ์ด๋ฉ์ผ"
์ฐ์ ๋ก์ปฌ ํฐ๋ฏธ๋์์ ์ ๋ช ๋ น์ด๋ฅผ ํตํด SSHํค๋ฅผ ์์ฑํ๊ณ id_rsa(ํ๋ผ์ด๋น ํค) ์ id_rsa.pub(ํผ๋ธ๋ฆญ ํค) ๊ฐ์ ๋ณต์ฌํจ
(~/.ssh ์ ์กด์ฌ)
Jenkins์์ SSHํค ์ถ๊ฐ
์ด์ Jenkins์ SSHํค(ํ๋ผ์ด๋น ํค)๋ฅผ ์ถ๊ฐํด์ผํจ


Jenkins UI์์ ํ๋กํ์ ํด๋ฆญํ๊ณ Credentials ํด๋ฆญ -> System ํด๋ฆญ -> Global credentials์์ Add Credentials ํด๋ฆญ

Kind๋ฅผ SSH Username with private key๋ก ์ค์ ํ๊ณ Username์ผ๋ก git์ด๋ GitHub ์ฌ์ฉ์์ด๋ฆ์ ์ ์ด์ฃผ๊ณ Private Key์์ Enter directly๋ก ๋ณต์ฌํ SSH ๊ฐ์ธ ํค๋ฅผ ๋ฃ์ด์ค (~/.ssh/id_rsa ์ ๋ด์ฉ์ ๋ฃ์ด์ค)
GitHub์์ SSH ํค ์ถ๊ฐ
์์์ sshํค๋ฅผ ๋ง๋๋ ๋ช
๋ น์ด๋ฅผ ์ ์ํํ๋ค๋ฉด ๋ก์ปฌ์ ~/.ssh/id_rsa.pub ์ ๊ณต๊ฐํค๊ฐ ๋ด๊ฒจ์ ธ์์ํ
๋ฐ
ํด๋น ๋ด์ฉ์ cat ๋ช ๋ น์ด๋ฑ์ผ๋ก ํ์ธ ํ ๋ณต์ฌํ์ฌ

GitHub ๊ณ์ ์ Settings -> SSH and GPG keys๋ก ์ด๋ํ์ฌ ์๋ก์ด SSH ํค๋ฅผ ์ถ๊ฐํด์ค
EC2์์ SSHํค ์ถ๊ฐ

๋ง์ฐฌ๊ฐ์ง๋ก ~/.ssh ๋ก ์ด๋ ํ vi authorized_keys ๋ช ๋ น์ด๋ฅผ ํตํด authorized_keys์ ๋ก์ปฌ์์ ๋ง๋ SSHํค (id_rsa.pub)์ ๋ด์ฉ์ ๋ฃ์ด์ค
3. Jenkins ํ์ดํ๋ผ์ธ ์์ฑ

Jenkins ๋์๋ณด๋์์ New Item์ ํด๋ฆญํ ํ 2๋ฒ์งธ์ ์๋ Pipeline์ ์ ํ
4. Poll SCM ์ค์ (GitHub Webhook ์ฌ์ฉ ๋ถ๊ฐ ์ด์์ ๋์)
๋งจ์์์ ๋งํ๋ฏ์ด ํ์ฌ fork๋ ๋ ํฌ์งํ ๋ฆฌ์์๋ง ์์ ํด์ผํ๋ค๋ ์ ์ฝ์ด ๊ฑธ๋ ค ๋ ํฌ์งํ ๋ฆฌ์ Setting์ ๊ฑด๋ค ์ ์๋ ์ํฉ์ด๋ผ
GitHub Webhook ์ถ๊ฐ ํ ํน์ ๋ธ๋ ์น์ ๋ณ๊ฒฝ ์ฌํญ์ ์ ๋ฌ๋ฐ์ CD๋ฅผ ์งํํ ์ ์๋ ์ํ์
=> ๋์์ผ๋ก ์๊ฐํ ๋ฐฉ์์ด Poll SCM์ ์ค์ ํ์ฌ ํน์ ์ฃผ๊ธฐ(์๊ฐ)๋ง๋ค Git ์ ์ฅ์๋ฅผ ํ์ธํ๋ฉฐ ๋ณ๊ฒฝ ์ฌํญ์ด ์์ผ๋ฉด ์์ ์ ํ๋ ๊ฒ์
์ฌ์ค Poll SCM์ ์ฌ์ฉํ๋ ๋ฐฉ์์ ํ๊ฒ๋๋ฉด ๋ณ๊ฒฝ์ด ์๋๋ผ๋ ์ผ์ ์ฃผ๊ธฐ๋ง๋ค Git ์ ์ฅ์๋ฅผ ํ์ธํ๋ฏ๋ก ๋ถํ์ํ ์์ฒญ์ด ๋ง์ ์ ๋ฐ์ ์๊ณ ์ ํด์ง ์ฃผ๊ธฐ๋งํผ์ ๋๋ ์ด๊ฐ ์์ ์ ์์(ํนํ ๋ง์ ํ๋ก์ ํธ ๊ด๋ฆฌํ๋ฉด ์ฑ๋ฅ์ด...)
ํ์ง๋ง ๋ถํ์ํ ์์ฒญ์ ๊ฒฝ์ฐ ๋ก์ปฌ์์ CD ๋์์ ํด์ผํ ๋๋ง Jenkins์ ์ผ์ ํ์ฉํ ๊ฒ์ด๊ธฐ์ ๋ฌธ์ ๊ฐ ์๋ค๊ณ ์๊ฐํ๊ณ ๋๋ ์ด์ ๊ฒฝ์ฐ๋ ํ์ฌ๋ ํ๋ก์ ํธ ์ด๊ธฐ ๋จ๊ณ์ด๊ธฐ์ ๊ฐ์ํ ์ ์๋ค๊ณ ์๊ฐํ์(์ผ๋จ ๋น์ฅ์ ์๋ ๋ฐฐํฌ์ ๊ท์ฐฎ์์ ํด์ํ๋ ์ ๋๋ก๋ง ์ฌ์ฉ)
=> ๊ฐ์ฅ ํ์ค์ ์ผ๋ก ํ ์ ์๋ ๋ฐฉ์์ด Poll SCM!!
Jenkins ๋์๋ณด๋์์ ์์์ ๋ง๋ Pipeline Job์ ํด๋ฆญํด์ฃผ๊ณ
์ผ์ชฝ ๊ตฌ์ฑ(Configure) ํด๋ฆญ ํ Triggres ํด๋ฆญ

Poll SCM ์ ํ ํ H/5 * * * * ์ ๊ฐ์ด ์ํ๋ ์ฃผ๊ธฐ ์ ํ
=> Ignore post-commit hooks๋ git commit, push ๋ฑ์ ํน์ Webhook ์ด๋ฒคํธ๊ฐ ๋ฐ์ํด๋ ๋ฌด์ํ๋๋ก ์ค์ ํ๋ ๊ฒ์
(์ด์ฉํผ Webhook ์ค์ ์ ๋ชปํ๊ธฐ์ ์ด๋ฒคํธ๊ฐ ์ฌ๋ฆฌ ์์ง๋ง ํ์คํ๊ฒ ์ฒดํฌ!)
H/5 * * * * ๋ cron ํํ์์ผ๋ก ์์๋๋ก MIN / HOUR / DAY / MONTH / DAT_OF_WEEK๋ฅผ ์๋ฏธํจ
=> H(ํด์) ๊ฐ์ ๊ธฐ์ค์ผ๋ก 5๋ถ๋ง๋ค ์คํ๋จ
(*์ ์์ผ๋์นด๋๋ก ๋งค์๊ฐ, ๋งค์ผ, ๋งค์, ๋งค์์ผ ์คํ๋๋ฏ๋ก)
์ฐธ๊ณ ๋ก 0/5 ์ H/5์ ์ฐจ์ด๋
H๋ Jenkins์ ํด์ ๊ฐ์ผ๋ก ํน์ ์๊ฐ์ ๋น๋๊ฐ ๋ชฐ๋ฆฌ๋ ๊ฑธ ๋ฐฉ์งํ๊ธฐ ์ํด ๊ฐ Job๋ง๋ค ๋ค๋ฅธ ์์ ์๊ฐ์ ์๋์ผ๋ก ๋ฐฐ์ ํด์ค
(๊ฐ Jenkins Job์ด ๋์ผํ ์๊ฐ์ ์คํ๋์ง ์๊ณ ๋ถ์ฐํด์ ์คํ)
0/5๋ ์ ํํ 0, 5, 10 ... ๋ถ๋ง๋ค ์คํ๋๋๊ฑฐ๋ผ ๋ชจ๋ Job์ด ๊ฐ์ ์๊ฐ์ ์คํ๋จ
5. Jenkins์์ ํ๊ฒฝ๋ณ์ ์ค์ (+ Docker Hub credentials ์ค์ )

Jenkins ๋์๋ณด๋์์ Manage Jenkins(Jenkins ๊ด๋ฆฌ) -> System -> Global properties ์น์ ์์ Environment variables๋ฅผ ์ฒดํฌํ ํ ์ค์ ํ ์ ์์
- GITHUB_BRANCH : ๋น๋์ ๋ฐฐํฌ์ ์ฌ์ฉ๋ GitHub ์ ์ฅ์์ ๋ธ๋์น
=> backend/main - GITHUB_REPO_URL : GitHub ์ ์ฅ์ URL
(HTTPS๋ง๊ณ SSH URL์ ๊ฐ์ ธ์์ผํจ) - GRADLE_TASK : Gradle ๋น๋ ์ ์คํํ ํ์คํฌ
=> clean bootJar - DOCKER_BUILD_PLATFORMS : Docker ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ ๋์ ํ๋ซํผ
=> linux/amd64,linux/arm64 - DOCKER_IMAGE_NAME : ๋น๋๋ Docker ์ด๋ฏธ์ง์ ์ด๋ฆ๊ณผ ํ๊ทธ
=> hjungyo/2025capstone:latest ์ ๊ฐ์ด dockerhub-username/your-app:version ์ ํํ - EC2_HOST : ๋ฐฐํฌ ๋์ EC2 ์ธ์คํด์ค์ ํผ๋ธ๋ฆญ IP
- EC2_USER : EC2 ์ธ์คํด์ค์ SSH ์ ์ํ ๋ ์ฌ์ฉํ ์ฌ์ฉ์ ์ด๋ฆ
=> ubuntu (linux๋ผ๋ฉด ec2-user) - CONTAINER_PORT : Docker ์ปจํ
์ด๋๊ฐ ์ฌ์ฉํ ํฌํธ ๋ฒํธ
=> 8080
์ด๋ ๊ฒ ํ๊ฒฝ๋ณ์ ์ค์ ์ ๋ง์น๋ฉด ๋์ด ์๋!
๋ก์ปฌ์์ ๋น๋ํ์ฌ ๋ง๋ ์ด๋ฏธ์ง๋ฅผ Docker Hub์ ์ฌ๋ฆฐ ํ ์ฌ๋ผ๊ฐ ์ด๋ฏธ์ง๋ฅผ EC2์์ ๊ฐ์ ธ์ ์ปจํ
์ด๋๋ฅผ ๋์ฐ๋ฏ๋ก ๊ด๋ จ ์ค์ ์ ํด์ค์ผํจ
=> Docker Hub ์ค์

์์์ sshํค๋ฅผ ๋ฑ๋กํ๋๋๋ก credentials ์ค์ ์์ docker hub ์์ด๋๋ฅผ Username ํ๋์, ๋น๋ฐ๋ฒํธ๋ฅผ Password์ ์ ๋ ฅ ํ Create ํ๋ฉด ๋จ
ID๊ฐ์ ๊ฒฝ์ฐ๋ ์์๋ณผ ์ ์๊ฒ dockerhub-credentials๋ผ๊ณ ๋ช ์ํ์์
6. Jenkins Pipeline Script ์์ฑ
์ต์ข Script๋ ํธ๋ฌ๋ธ ์ํ ๋ถ๋ถ ์ฐธ๊ณ โผ๏ธ

๋ง์ฐฌ๊ฐ์ง๋ก ์๊น ๋ง๋ ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑ(Configure) ํด๋ฆญ ํ Pipeline์ ํด๋ฆญํ๋ฉด ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ ์ ์์
๋ฐ์ ์ฒดํฌ๋ Use Groovy Sandbox๋ Jenkins Pipeline์์ Groovy ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ๋ ๋ณด์์ ๊ฐํํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ์ต์ ์
=> Sandbox ํ๊ฒฝ์ ์ฝ๋ ์คํ์ด ์ ํ๋ ๊ฒฉ๋ฆฌ๋ ํ๊ฒฝ์ ์๋ฏธํจ
(ํน์ ๋ฉ์๋๋ ํด๋์ค์ ์ฌ์ฉ์ด ์ ํ๋์ด ์ํํ ์์ ์ ๋ฐฉ์ง, ์ ์์ ์ธ ์ฝ๋ ์คํ ๋ฐฉ์ง ๋ฑ)
์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํด๋ณด๋ฉด
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL
}
}
stage('Build') {
steps {
dir('backend') {
sh "./gradlew ${env.GRADLE_TASK}"
}
}
}
stage('Build and Push Docker Image') {
steps {
dir('backend') {
withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh """
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
docker buildx build --platform ${env.DOCKER_BUILD_PLATFORMS} -t ${env.DOCKER_IMAGE_NAME} --push .
"""
}
}
}
}
stage('Deploy to EC2') {
steps {
sshagent(credentials: ['ssh-key']) {
sh """
ssh -o StrictHostKeyChecking=no ${env.EC2_USER}@${env.EC2_HOST} '
sudo docker stop \$(sudo docker ps -q --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker rm \$(sudo docker ps -aq --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker pull ${env.DOCKER_IMAGE_NAME}
sudo docker run --platform linux/amd64 -d -p ${env.CONTAINER_PORT}:${env.CONTAINER_PORT} ${env.DOCKER_IMAGE_NAME}
'
"""
}
}
}
}
}
- ๊ฐ stage๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ์คํ๋๋ฏ๋กdir('backend')๋ฅผ ํตํด ๋๋ ํ ๋ฆฌ๋ฅผ ์ด๋ํ๋ ๊ฒ์ ๋ณผ ์ ์์
=> ํ์ฌ GitHub์๋ backend์ front ๋๋ ํ ๋ฆฌ๊ฐ ํจ๊ป ์กด์ฌํ๊ธฐ์ backend ๋๋ ํ ๋ฆฌ๋ก ์ด๋ ํ ์์ ์ ์ํํด์ผํจ - EC2 ๋ฐฐํฌ ๋ถ๋ถ์ ์ดํด๋ณด๋ฉด
- SSH ๋ช
๋ น์ ์ฌ์ฉํ์ฌ EC2 ์ธ์คํด์ค์ ์ฐ๊ฒฐํ๊ณ ์๋๋ฐ -o StrictHostKeyChecking=no ์ต์
์ ๊ฒฝ์ฐ ์ด๊ธฐ ์ฐ๊ฒฐ ์ ํธ์คํธ ํค ํ์ธ์ ๊ฑด๋๋ฐ๋ ๊ฒ์
(ํธ์คํธ ํค๋ SSH ์๋ฒ์ ๊ณ ์ ์๋ณ์์ -> ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐํ๋ ค๋ ์๋ฒ์ ์ ์์ ํ์ธํ๋ ๋ฐ ์ฌ์ฉ๋จ)
๋๋ณด๊ธฐ"Are you sure you want to continue connecting (yes/no)?" ํ๋กฌํํธ๋ฅผ ๋ฐฉ์ง
=> ๋ณด์์ ์ํด known_hosts ํ์ผ์ ๊ด๋ฆฌํ๋๊ฒ ๋ ์ข์
(๋ณด์ ๊ณ์ธต ์ถ๊ฐ) - sshagent๋ Jenkins ํ์ดํ๋ผ์ธ์์ SSH ์ฐ๊ฒฐ์ ์์ ํ๊ฒ ๊ด๋ฆฌํ๊ณ ์ฌ์ฉํ๊ธฐ ์ํ ๊ธฐ๋ฅ์ธ๋ฐ ์ด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ SSH Agent ํ๋ฌ๊ทธ์ธ์ ์ค์นํด์ค์ผํจ!
- SSH ๋ช
๋ น์ ์ฌ์ฉํ์ฌ EC2 ์ธ์คํด์ค์ ์ฐ๊ฒฐํ๊ณ ์๋๋ฐ -o StrictHostKeyChecking=no ์ต์
์ ๊ฒฝ์ฐ ์ด๊ธฐ ์ฐ๊ฒฐ ์ ํธ์คํธ ํค ํ์ธ์ ๊ฑด๋๋ฐ๋ ๊ฒ์
ํธ์คํธํค ํ์ธ์ ์ฐ๊ฒฐํ๋ ค๋ ์๋ฒ๊ฐ ์ค์ ๋ก ์๋ํ ์๋ฒ์ธ์ง ํ์ธํ๋ ๊ณผ์ ์ด๋ผ๊ณ ๋งํ๋๋ฐ
์ด๋ฅผ ๊ฑด๋๋ฐ๋ฉด ๊ณต๊ฒฉ์๊ฐ ์ค๊ฐ์์ ํต์ ์ ๊ฐ๋ก์ฑ๊ฑฐ๋ ์์กฐ๋ ์๋ฒ๋ก ์ฐ๊ฒฐ์ ์ ๋ํ ์ํ์ด ์์
(Man-in-the-Middle ์ด๋ผ๋ ์ค๊ฐ์ ๊ณต๊ฒฉ)
=> SSHํค ์์ผ๋ฉด ์ด์ฉํผ ์๋ฏธ๊ฐ ์์ง ์๋? ๋ผ๊ณ ์๊ฐ์ ํ๋๋ฐ ์ฐพ์๋ณด๋
์ํธํ๋ ๋ด์ฉ์ ๋ณผ ์ ์๊ฒ ์ง๋ง ํต์ ์ ํจํด, ๋น๋, ํฌ๊ธฐ ๋ฑ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ฉฐ ๊ฐ๋ก์ฑ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด๋๊ณ ํฅํ ๊ณต๊ฒฉ ์ค๋น๋ฅผ ํ ์ ์๋ค๋ ๋ด์ฉ์ ๋ณด๊ณ ๋ณด์์ด ์ค์ํ๊ตฌ๋๋ฅผ ๋๊ผ์....
known_hosts ํ์ผ์ ๊ด๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก ํ์ดํ๋ผ์ธ ์คํฌ๋ฆฝํธ ๋ณ๊ฒฝ
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL
}
}
stage('Build') {
steps {
dir('backend') {
sh "./gradlew ${env.GRADLE_TASK}"
}
}
}
stage('Build and Push Docker Image') {
steps {
dir('backend') {
withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh """
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
docker buildx build --platform ${env.DOCKER_BUILD_PLATFORMS} -t ${env.DOCKER_IMAGE_NAME} --push .
"""
}
}
}
}
stage('Update Known Hosts') {
steps {
sh """
mkdir -p ~/.ssh
ssh-keyscan -H ${env.EC2_HOST} >> ~/.ssh/known_hosts
"""
}
}
stage('Deploy to EC2') {
steps {
sshagent(credentials: ['ssh-key']) {
sh """
ssh ${env.EC2_USER}@${env.EC2_HOST} '
sudo docker stop \$(sudo docker ps -q --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker rm \$(sudo docker ps -aq --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker pull ${env.DOCKER_IMAGE_NAME}
sudo docker run --platform linux/amd64 -d -p ${env.CONTAINER_PORT}:${env.CONTAINER_PORT} ${env.DOCKER_IMAGE_NAME}
'
"""
}
}
}
}
}
Update Known Hosts๋ผ๋ ์คํ
์ด์ง๋ฅผ ์ถ๊ฐํ์๊ณ
ssh-keyscan ๋ช
๋ น์ ์ฌ์ฉํ์ฌ EC2 ํธ์คํธ์ SSH ํค๋ฅผ ๊ฐ์ ธ์ค๊ณ ์์
=> ๊ฐ์ ธ์จ ํค๋ฅผ ~/.ssh/known_hosts ํ์ผ์ ์ถ๊ฐ
(๊ธฐ์กด์ StrictHostKeyChecking=no ์ต์ ์ ์ ๊ฑฐํ์์)
์๋ ๋ฐฉ์์ ์ดํด๋ณด๋ฉด
ssh-keyscan์ผ๋ก ๊ฐ์ ธ์จ ํธ์คํธ ํค๋ ~/.ssh/known_hosts ํ์ผ์ ์ ์ฅ๋๊ณ
SSH ์ฐ๊ฒฐ์, ํด๋ผ์ด์ธํธ๋ ์๋ฒ๊ฐ ์ ๊ณตํ๋ ํธ์คํธ ํค๋ฅผ know_hosts ํ์ผ์ ํค์ ๋น๊ตํจ
=> ํค๊ฐ ์ผ์นํ๋ฉด ์ฐ๊ฒฐ์ ์งํํ๊ณ , ์ผ์นํ์ง ์์ผ๋ฉด ๊ฒฝ๊ณ ๋ฅผ ํ์
(์ด๋ฅผ ํตํด ์ด๊ธฐ ์ฐ๊ฒฐ ์ ํธ์คํธ ํค๋ฅผ ์๋์ผ๋ก ์ ์ฅํ๊ณ , ์ดํ ์ฐ๊ฒฐ์์๋ ์ ์ฅ๋ ํค๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ์ ์ ์์ ํ์ธํจ)
ํธ๋ฌ๋ธ ์ํ

ํด์น์ ๋..? ๋ผ๊ณ ์๊ฐ์ด ๋ค์๋ค๋ฉด ๋ญ๊ฐ๋ฅผ ๋์น๊ณ ์๋ค๋ ๋ป!
1. ๊น๋จน์ application.yaml ํ์ผ... (with Permission denied)

์ฐ์ ์์์ ์ค์ ํ๋๋๋ก Jenkins ๊ด๋ฆฌ > Credentials > System > Global credentials ๋ก ์ด๋ ํ
kind๋ฅผ 'Secret file'๋ก ํ์ฌ ๋ก์ปฌ์์ ์ฌ์ฉ์ค์ธ application.yaml์ ์ฌ๋ ค์ฃผ์์
(ํ์ฌ ๊นํ๋ธ์๋ application.yaml.example๋ง ์ฌ๋ผ๊ฐ์๊ธฐ์ ๋ก์ปฌ์์ ์ฌ์ฉํ๋ yaml์ ์ฃผ์
ํ๊ธฐ ์ํด)
stage('Inject Configuration') {
steps {
withCredentials([file(credentialsId: 'application.yaml', variable: 'APP_YAML')]) {
sh 'cp -f $APP_YAML backend/src/main/resources/application.yaml'
}
}
}
๋ค์๊ณผ ๊ฐ์ด GitHub์์ Checkout ํ์ application.yaml์ ์ถ๊ฐํ๋ stage๋ฅผ ๋ฃ์ด์ฃผ์์
์ด ๋, cp ๋ช ๋ น์ด์ -f ์ต์ ์ ์ถ๊ฐํ์ฌ permission denied๋ฅผ ๋ฌด์ํ๊ณ ๊ฐ์ ๋ก ํ์ผ์ ๋ฎ์ด์ฐ๋๋ก ํ์์ต๋๋ค
2. No credentials specified
์ฒซ๋ฒ ์งธ stage์ธ Checkout ๋ถ๋ถ์์ ์๊พธ No credentials specified ์ค๋ฅ๊ฐ ๋ฐ์ํ์์
=> Git ์ ์ฅ์์ ์ ๊ทผํ๊ธฐ ์ํ ์ธ์ฆ ์ ๋ณด๊ฐ ์ ๊ณต๋์ง ์์์์ ๋ํ๋
ํ๋ผ์ด๋น ์ ์ฅ์์ ๊ฒฝ์ฐ ์ ์ ํ SSH ํค๋ ์ธ์ฆ ์ ๋ณด๊ฐ ํ์ํ๊ธฐ์ ์ด์ ์ GitHub์ ํจ๊ป ์ค์ ํด๋ ssh-key๋ฅผ Script์ ๋ช ์ํด์คฌ์
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL, credentialsId: 'ssh-key'
}
}
๋ค์๊ณผ ๊ฐ์ด credentialsId์ ssh-key๋ฅผ ๋ช ์ํด์คฌ์
3. gradlew Permission denied
์ ์๋ฌ ๊ฐ์ ๊ฒฝ์ฐ๋ Jenkins ์์
๊ณต๊ฐ(workspace)์ ์๋ gradelw ํ์ผ์ ๋ํด ์คํ ๊ถํ์ ๋ถ์ฌํด์ผํจ
stage('Grant Permission') {
steps {
sh 'chmod +x ./backend/gradlew'
}
}
๋ค์๊ณผ ๊ฐ์ด ์คํฌ๋ฆฝํธ์ Permission ํ์ฉ์ ํด์ฃผ์์
4. JDK๋ฅผ ์ฐพ์ง ๋ชปํ๋ ์ค๋ฅ
์๊พธ ๋น๋ํ ๋ JDK๋ฅผ ๋ชป์ฐพ์์ ์คํจํ์
์ฌ๋ฌ ๋ธ๋ก๊ทธ๋ค์ ์คํด๋ณด์๋๋ฐ Docker๊ฐ ์๋๋ผ ์์์ ์งํํ ๋ฐฉ์๋๋ก Jenkins๋ฅผ ์ค์นํ๊ฒ ๋๋ฉด JDK๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค์ ์ด ๋์ด์์ง ์๋ ๊ฒ ๊ฐ์
=> ๋ฐ๋ก ์ค์ ํด๋ณด์!

๋์๋ณด๋์์ Jenkins ๊ด๋ฆฌ -> Tools ์ ๋ค์ด๊ฐ ADD JDK๋ฅผ ํด์ฃผ๋ฉด๋จ
(Java 17๊ธฐ์ค)
๋ง์ฝ Java 17์ด ์ค์น๋์ง ์์๋ค๋ฉด brew install openjdk@17 ๋ก ์ค์นํด์ค
echo 'export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
์ค์น๊ฐ ๋์๋ค๋ฉด ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํด์ฃผ๋ฉด๋จ
๋ช ๋ น์ด๋ฅผ ์ดํด๋ณด๋ฉด $PATH ๋ฅผ ํตํด OpenJDK 17์ bin ๋๋ ํ ๋ฆฌ๊ฐ ๊ธฐ์กด PATH ์์ ์ถ๊ฐ๋จ
=> ์ด๋ ๊ฒ ์ค์ ํ๋ฉด Java ๊ด๋ จ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ ๋ ์์คํ ์ ๋จผ์ OpenJDK 17์ bin ๋๋ ํ ๋ฆฌ์์ ํด๋น ๋ช ๋ น์ด๋ฅผ ์ฐพ๊ณ , ๊ทธ ๋ค์์ ๋ค๋ฅธ ๋๋ ํ ๋ฆฌ๋ค์ ๊ฒ์ํจ
(homebrew๋ก ์ค์นํ๊ฒ ๋๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก opt/homebrew/opt/openjdk@17 ์ ๊ฒฝ๋ก๋ก ์ค์น๊ฐ ๋จ)
์ด์
Jenkins Tools ์์ JAVA_HOME ๋ถ๋ถ์
/opt/homebrew/Cellar/openjdk@17/17.0.14/libexec/openjdk.jdk/Contents/Home ๋ผ๊ณ ์ค์น๋ Java์ ์ค์ ๊ฒฝ๋ก๋ฅผ ์ ๋ ฅํด์ค
(์ด๋ /usr/libexec/java_home -v 17๋ฅผ ํฐ๋ฏธ๋์ ์ ๋ ฅ ํ ์ค์น ๊ฒฝ๋ก๋ฅผ ํ์ธํ ์ ์์)
5. docker : command not found
์์์ docker ํ๋ฌ๊ทธ์ธ์ ์ค์นํ๋๋ฐ ์ ์ด๋ฐ ์๋ฌ๊ฐ ๋ฌ์๊น..?
=> ํ๋ฌ๊ทธ์ธ์ Jenkins์ Docker์ ํตํฉ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง, ์ค์ Docker ๋ช ๋ น์ด์ ์์น๋ ์์ง ๋ชปํ๊ธฐ ๋๋ฌธ์
ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํด์ Jenkins์๊ฒ Docker ์คํ ํ์ผ์ ์ ํํ ์์น๋ฅผ ์๋ ค์ค์ผํจ
(Jenkins๊ฐ sh 'docker ...' ๋ช
๋ น์ด๋ฅผ ์คํํ ๋, ์์คํ
์ PATH ํ๊ฒฝ ๋ณ์๋ฅผ ํตํด Docker CLI ์คํ ํ์ผ์ ์ฐพ์)
environment {
PATH = "/usr/local/bin:/Applications/Docker.app/Contents/Resources/bin:${env.PATH}"
}
which docker ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ ๋ /opt๋ก ์์ํ๋ ๊ฒฝ๋ก๊ฐ ๋์์ ์ด๋ฅผ PATH์ ์ ์ผ๋ฉด docker-credential-desktop ์ด๋ผ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํจ
=> Docker Desktop์ ์ธ์ฆ ๋๊ตฌ๋ฅผ ์ฐพ์ง ๋ชปํด ๋ฐ์ํ๋ ์ค๋ฅ
Docker Desktop์ ๊ธฐ๋ณธ ์ค์น ๊ฒฝ๋ก์ธ /usr/local/bin:/Applications/Docker.app/Contents/Resources/bin๋ฅผ PATH์ ์ถ๊ฐํ์ฌ docker-credential-desktop์ ์ฐพ์ ์ ์๊ฒ ํด์ผํจ
ํ์ฌ ๋ก์ปฌ์์ Docker Desktop์ ์ค์นํ์ฌ ์ฌ์ฉ์ค์ด๊ธฐ์ ~/.docker/config.json ํ์ผ์ credsStore ์ค์ ์ด "desktop"์ผ๋ก ๋์ด ์์
=> ์ด ์ค์ ๋๋ฌธ์ Docker๋ docker-credential-desktop์ ์ฐพ์ผ๋ ค๊ณ ํ๊ธฐ์ ์์ ๊ฐ์ด ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒ์
(๋ฌด์กฐ๊ฑด Docker Desktop์ ๊น์์ผํ๋๊ฑด ์๋!!)
์์ PATH๋ฅผ ์ค์ ํ๋ ์ฝ๋๋ฅผ ๋ณด๊ณ PATH๋ฅผ ํตํด ์ด๋ป๊ฒ ๋ช ๋ น์ด๊ฐ ๋์ํ๋์ง ๊ถ๊ธํด์ ๋ ์ฐพ์๋ด
์ด์์ฒด์ ๋ PATH์ ๋์ด๋ ๋๋ ํ ๋ฆฌ๋ฅผ ์ผ์ชฝ์์ ์ค๋ฅธ์ชฝ์ผ๋ก ์์ฐจ์ ์ผ๋ก ๊ฒ์ํ๋ฉฐ ๋ช
๋ น์ด์ ์ผ์นํ๋ ์คํ ํ์ผ์ ์ฐพ์ผ๋ฉด ์ฆ์ ๊ทธ ํ์ผ์ ์คํํ๊ณ ๊ฒ์์ ์ค๋ดํจ
(์ผ์ชฝ์ด ์ฐ์ ์์๋ฅผ ๊ฐ์ง๋ฏ๋ก ๋์ผํ ์ด๋ฆ์ ๋ช
๋ น์ด๊ฐ ์ฌ๋ฌ ๋๋ ํ ๋ฆฌ์ ์๋ค๋ฉด, ๊ฐ์ฅ ์ผ์ชฝ์ ์๋๊ฒ ์คํ๋จ)
:$PATH๋ฅผ ํตํด ์ด์ด๋ถ์ด๋ ์ด์ ๋ ์๋ก์ด ๋๋ ํ ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ฉด์ ๊ธฐ์กด์ ๋ช
๋ น์ด๋ ๊ณ์ ์ฌ์ฉํ ์ ์๊ฒ ํ๊ธฐ ์ํจ์
(๋ง์ฝ :๋ฅผ ์ฐ์ง ์๊ณ PATH = ์ผ๋ก ๋๋๋ค๋ฉด ๊ธฐ์กด์ ์ถ๊ฐํ PATH๋ค์ด ์ฌ๋ผ์ง๊ธฐ์ ๋ค๋ฅธ ๋ช
๋ น์ด๋ค์ ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํจ)
=> ๊ธฐ์กด์ $PATH ๊ฒฝ๋ก ์์ ์ถ๊ฐ
๋ํ Jenkins ํ์ดํ๋ผ์ธ์ ๋๋ฆด ๋ Docker ๋ฐ๋ชฌ์ด ํ์ฑํ๋์ด ์์ด์ผํจ
=> Docker Desktop์ด ์คํ์ค์ด์ฌ์ผํจ
(Homebrew๋ฅผ ํตํด Docker Engine๋ง ์ค์นํ๋ค๋ฉด ๋ค๋ฅด๊ฒ ์ค์ )
์ต์ข ํ ์คํธ ๋ฐ ํ์ดํ๋ผ์ธ ์ฝ๋
pipeline {
agent any
environment {
PATH = "/usr/local/bin:/Applications/Docker.app/Contents/Resources/bin:${env.PATH}"
}
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL, credentialsId: 'ssh-key'
}
}
stage('Inject Configuration') {
steps {
withCredentials([file(credentialsId: 'application.yaml', variable: 'APP_YAML')]) {
sh 'cp -f $APP_YAML backend/src/main/resources/application.yaml'
}
}
}
stage('Grant Permission') {
steps {
sh 'chmod +x ./backend/gradlew'
}
}
stage('Build') {
steps {
dir('backend') {
sh "./gradlew ${env.GRADLE_TASK}"
}
}
}
stage('Build and Push Docker Image') {
steps {
sh 'docker info'
dir('backend') {
withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh """
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
docker buildx build --platform ${env.DOCKER_BUILD_PLATFORMS} -t ${env.DOCKER_IMAGE_NAME} --push .
"""
}
}
}
}
stage('Update Known Hosts') {
steps {
sh """
mkdir -p ~/.ssh
ssh-keyscan -H ${env.EC2_HOST} >> ~/.ssh/known_hosts
"""
}
}
stage('Deploy to EC2') {
steps {
sshagent(credentials: ['ssh-key']) {
sh """
ssh ${env.EC2_USER}@${env.EC2_HOST} '
sudo docker stop \$(sudo docker ps -q --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker rm \$(sudo docker ps -aq --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker pull ${env.DOCKER_IMAGE_NAME}
sudo docker run --platform linux/amd64 -d -p ${env.CONTAINER_PORT}:${env.CONTAINER_PORT} ${env.DOCKER_IMAGE_NAME}
'
"""
}
}
}
}
}

EC2์ ๋ค์ด๊ฐ sudo docker ps ๋ช ๋ น์ด๋ฅผ ํตํด ์คํ์ค์ธ ์ปจํ ์ด๋๋ฅผ ํ์ธํด๋ณด๋ฉด ์ ๋์ํ๊ณ ์๋ ๊ฒ์ ๋ณผ ์ ์์


์ถ๊ฐ๋ก ๊ฐ๋จํ ํ์๊ฐ์ ๋ก์ง๋ ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค
๋ฐ๋ผ๋ณด๊ณ ์๋ Branch์ ์ปค๋ฐ ๊ธฐ๋ก์ ๋ณํ๊ฐ ์๊ธฐ๋ฉด ์ ํด์ง ์๊ฐ๋ง๋ค ํ์ธ ํ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋จ!
=> ํ ์คํธ ํด๋ณด๊ธธ!!
'๐ Infra > CI ยท CD' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Github Actions + Docker + EC2๋ก Spring Boot CI /CD ๊ตฌ์ถ (0) | 2025.03.23 |
---|---|
Github Actions ํบ์๋ณด๊ธฐ (0) | 2025.02.26 |
GitHub Actions ์๊ฐ (0) | 2024.05.01 |
๋ก์ปฌ์์ Spring Boot ๋น๋ํ๊ณ docker image ๋ง๋ค์ด์ docker hub์ ์ฌ๋ฆฌ๊ณ ..
EC2 ๋ค์ด๊ฐ์ docker hub์ ์๋ ์ด๋ฏธ์ง pull ๋ฐ์์ run ์ํค๊ธฐ...
๊ฐ๋ฐ์ ํ๋ฉด ํ ์๋ก CI/CD์ ์ค์์ฑ์ด ๊น๊ฒ ๋๊ปด์ง๋ ๊ฒ ๊ฐ๋ค...
๋ฐฐํฌ์ ์์ด์ ๋์ ๊ฐ์ด ๊ท์ฐฎ์ ํน์ ์ด๋ ค์์ ๋๋ผ๊ณ ์๋ ๋ค๋ฅธ ๋ถ๋ค์ ์ํด ๊ธฐ๋ก์ ๋จ๊ธฐ๋ ค๊ณ ํ๋ค
์์ํ๊ธฐ์ ์์
์ฌํ ์ฌ์ฐ (Jenkins ๋์ ๊ธฐ..)
์ผ๋จ ์์ํ๊ธฐ์ ์์ Jenkins์ ๋์ ํ๊ธฐ๊น์ง์ ์ฌํ ์ ์ค์ด ์๋ค..
https://hanjungyo.tistory.com/126
Github Actions ํบ์๋ณด๊ธฐ
Github Actions์ ๊ธฐ๋ณธ ๊ฐ๋ ์ผ๋จ Github Actions๋ ์ํํธ์จ์ด ๊ฐ๋ฐ ์ํฌํ๋ก์ฐ๋ฅผ ์๋ํํ ์ ์๋ CI/CD ๋๊ตฌ์=> ์ฝ๋๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋น๋, ํ ์คํธ, ๋ฐฐํฌ ๋ฑ์ ์์ ์ ์๋์ผ๋ก ์คํํ ์ ์์ Github
hanjungyo.tistory.com
์๋ ์ ๊ฒ์๊ธ์ ์ด๋ฆ์ Github Actions๋ฅผ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ์๋ค...
Github Actions๋ GitHub์ ์ฐ๋๋๊ธฐ์ ๊ต์ฅํ ํธ๋ฆฌํ ๊ธฐ๋ฅ๋ค์ด ๋ง์๊ณ ๋์ด๋๋ ์๋์ ์ผ๋ก ์ฌ์์ ์ ํํ๊ฒ ๋์๋๋ฐ
์ด์ฌํ ๊ธ์ ์ฐ๋ ๋์ค ํ์ฌ ํ๋ก์ ํธ์(์กธ์ ํ๋ก์ ํธ) ๋ ํฌ์งํ ๋ฆฌ๊ฐ ๋ํ๊ต organization์์๋ง ์์ฑ์ ํด์ผํ๋๋ฐ Repo Setting ๊ถํ์ด ๋งํ์์...
self-hosted runner ํ๊ฒฝ์ ์ฐ์๋ ๊ฒฐ๊ตญ EC2(t2.micro ์ฌ์ฉ์ค)์์ ๋น๋๋ฅผ ๋๋ ค์ผ ํ๋ ์ํฉ์ด ์๊ธฐ๋๋ฐ t2.micro์์ ์๋ํด๋ณธ ๊ฒฐ๊ณผ ๋น๋๋ฅผ ํ๋ ๋์ค CPU 100%์ ๋ซ๊ณ ๋ฌดํ ๋ก๋ฉ์ ๊ฑธ๋ ค๋ฒ๋ฆฌ๋ ๋ฆฌ์์ค ์ด์๊ฐ ํ์
๋์๊ธฐ ๋๋ฌธ์ Github Actions๋ฅผ ํฌ๊ธฐํ๋ค
(์ด์ฉํผ ์๋ณธ ๋ ํฌ์งํ ๋ฆฌ์์ runner ๋ฑ๋ก ๋ชปํ๋ฏ๋ก self-hosted runner๋ ๋ถ๊ฐ๋ฅ)
AWS Lambda + AWS CodeBuild + GitHub Actions๋ก ๋ ํฌ์งํ ๋ฆฌ Setting์ ์ฌ์ฉํ์ง ๋ชปํ๋ ์ํฉ์ ์ฐํ? ํ ์ ์์ ๊ฒ ๊ฐ์ง๋ง ๋น์ฉ๊ณผ ๋คํธ์ํฌ ์ง์ฐ ๋ฑ์ผ๋ก Jenkins๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข์ ์ ํ์ด๋ผ๊ณ ์๊ฐํจ
=> Jenkins์์ Git Plugin์ผ๋ก ๋ชฉํ๋กํ๋ "Git ์ ์ฅ์์ ํตํฉํ์ฌ ํธ๋ฆฌ๊ฑฐ"๋ฅผ ํ ์ ์๊ณ Circle CI, Travis CI ๊ฐ์ ๋ค๋ฅธ CI/CD ํด๋ค๋ ์ดํด๋ณด์๋๋ฐ ๋ฌด๋ฃ ํ๋์ ํ๊ณ ๋ฐ ๋น์ฉ ์ด์๋ ์๊ธฐ์ ์คํ์์ค์ธ Jenkins๋ฅผ ์ ํํจ
Jenkins๋?

Jenkins๋ ์คํ ์์ค CI/CD ๋๊ตฌ๋ก, ์ํํธ์จ์ด ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ์๋ํํ๋ ๋ฐ ์ฌ์ฉ๋จ
=> ๋ค์ํ ํ๋ฌ๊ทธ์ธ์ ์ง์ํ์ฌ ๋น๋, ๋ฐฐํฌ, ํ ์คํธ ๋ฑ์ ์์ ์ ์ ์ฐํ๊ฒ ์ํํ ์ ์์ผ๋ฉฐ ์ฌ๋ฌ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ๋ฒ์ ๊ด๋ฆฌ ์์คํ ์ ์ง์ํจ
ํน์ง์ ์ดํด๋ณด๋ฉด
- ์ฝ 1500๊ฐ ์ด์์ ํ๋ฌ๊ทธ์ธ์ ํตํด ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์์ด, ํ์ ์๊ตฌ ์ฌํญ์ ๋ง๊ฒ ์ปค์คํฐ๋ง์ด์ฆํ ์ ์์
- ํ์ดํ๋ผ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด CI/CD ํ๋ก์ธ์ค๋ฅผ ์ฝ๋๋ก ์ ์ํ ์ ์์
=> ๋ฒ์ ๊ด๋ฆฌ ์์คํ ์์ ํ์ดํ๋ผ์ธ์ ๊ด๋ฆฌํ ์ ์์ - ์คํ์์ค์ด๊ธฐ์ ๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์๊ณ , ์ปค๋ฎค๋ํฐ๊ฐ ์ ํ์ฑํ ๋์ด์์
- ์น ๊ธฐ๋ฐ์ ๋์๋ณด๋๋ฅผ ์ ๊ณตํ๊ธฐ์ ๋น๋ ์ํ์ ๋ก๊ทธ๋ฅผ ์ฝ๊ฒ ํ์ธํ ์ ์์
Jenkins๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋จ์ผ ์๋ฒ์์ ์คํ๋๊ธฐ์ ๋ง์ ํ๋ฌ๊ทธ์ธ๊ณผ ์์ ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋ฆฌ์์ค๋ฅผ ๋ง์ด ์๋ชจํ๋ฉฐ ์ด๋ก ์ธํด ๋๊ท๋ชจ ํ๋ก์ ํธ์์ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋
Jenkins ์์ด์ ํธ๋ฅผ ์ค์ ํ์ฌ ์์ ์ ๋ถ์ฐ์ํค๊ฑฐ๋ AWS, GCP์ ๊ฐ์ ํด๋ผ์ฐ๋ ์๋น์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ๋ฆฌ์์ค๋ฅผ ํ์ฅํ๊ฑฐ๋, ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น๋ ํ๋ฌ๊ทธ์ธ์ ์ต์ํํ๊ณ ํ์์ ์ธ ํ๋ฌ๊ทธ์ธ๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์
Jenkins์ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ
์๋ EC2์ Jenkins ์๋ฒ๋ฅผ ์คํ์์ผ๋๊ณ ํ๋ ค๊ณ ํ๋๋ฐ ๊ฐ๋ฐ ์ด๊ธฐ ๋จ๊ณ์ด๊ณ ํ ์ธ์๋ ์ ๊ธฐ์
๋น์ฉ์ ์ต๋ํ ์ ์ฝํ๊ณ ์ ๋ก์ปฌ์์ ์งํํ์์ต๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด CD๋ฅผ ํ๊ธฐ ์ํด์๋ ๋ก์ปฌ์์ jenkins ์๋ฒ๋ฅผ ํค๊ณ ์์ด์ผ ํ๊ธฐ์ ์ธ์ ํ ๋ช
์ด ์ด๋ฅผ ๋ด๋นํด์ผํจ
ํ์ง๋ง, ์์์ ๋งํ๋๋ก ์ธ์๋ ์ ๊ณ ๊ฐ๋ฐ ์ด๊ธฐ ๋จ๊ณ๋ผ ์์ง ๋น๋ฒํ๊ฒ merge๊ฐ ๋ฐ์ํ์ง ์๊ธฐ์ ๋ก์ปฌ์์ jenkins๋ฅผ ์ฌ์ฉํ๋ฉฐ ๋น์ฉ์ ์ต๋ํ ์ ์ฝํ๋ค๊ฐ ์ถํ MVP(Minimum Viable Product)๊ฐ ์์ฑ๋๋ฉด์ ์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ์ด ๋น๋ฒํด์ง ๋ EC2 ์์ ์ฌ๋ฆฌ๋ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝ ์์
EC2์์ Jenkins๋ฅผ ๋์ฐ๊ฒ ๋ ๋ ๊ด๋ จ ๋ด์ฉ๋ ๊ธฐ๋กํด๋๋๋ก ํ๊ฒ ์ต๋๋ค.
(๋ฐฉ์์ ๊ฑฐ์ ์ ์ฌํจ)
Mac์์ ์งํํ์์ต๋๋คโผ๏ธ
1. Jenkins ์ค์น ๋ฐ ์ค์
ํฐ๋ฏธ๋์์ brew install jenkins-lts ๋ช ๋ น์ด ์ ๋ ฅ
=> Homebrew๋ฅผ ์ฌ์ฉํ์ฌ Jenkins๋ฅผ ์ค์น
(์๊ฐ์ด ์ข ์์๋๊ณ Java๋ก ์์ฑ๋์๊ธฐ ๋๋ฌธ์ JDK๋ฅผ ๋จผ์ ์ค์นํ๋ฉด ์ข์
-> Homebrew์์ ์๋์ผ๋ก OpenJDK๋ฅผ ์ค์นํ๋ ๊ฒฝ์ฐ๋ ๋ง๋ค๊ณ ํจ)

brew services start jenkins-lts ๋ช ๋ น์ด๋ก Jenkins ์๋น์ค๋ฅผ ์์ํ๊ณ
์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์ด์ด http://localhost:8080 ์ผ๋ก ์ ์ํ๋ฉด Jenkins์ ์ ๊ทผํ ์ ์์
cat /Users/$(whoami)/.jenkins/secrets/initialAdminPassword
์ ๋ช ๋ น์ด๋ฅผ ํตํด ์ด๊ธฐ ์ํธ๋ฅผ ์ฐพ์ ํ ์ ๋ ฅํด์ค์ผํจ

๊ทธ ํ ์ด๊ธฐ ์ธํ
(ํ๋ฌ๊ทธ์ธ)์ ํด์ค์ผํ๋๋ฐ ์์์ ๋งํ๋ฏ์ด ํ์ํ ํ๋ฌ๊ทธ์ธ๋ง ์ค์นํ๋๊ฒ ์ฑ๋ฅ๋ฉด์์ ๋ ์ข์ ์ ํ์ด์ง๋ง ์ด์ ๋ง ์ฒ์ ์จ๋ณด๋ ๋จ๊ณ์ด๊ธฐ์ ์ผ๋จ suggested๋ก ์ค์น๋ฅผ ํ๊ณ Git๊ณผ Docker๋ง ์ถ๊ฐ ์ค์น๋ฅผ ํด์คฌ์
(Dashboard -> Jenkins ๊ด๋ฆฌ -> Plugins์์ ์ถํ ์ค์น ๊ฐ๋ฅ)

์ค์น๊ฐ ์๋ฃ๋๋ฉด ๊ณ์ ์ ์์ฑํ๋ ํ์ด์ง๋ก ๋์ด๊ฐ๋๋ฐ ๊ณ์ ์์ฑ ๋ฐ URL์ ์ค์ ํ ์ ์์
2. SSH ์ค์
SSH ์ธ์ฆ ๋ฐฉ์
SSH(Secure Shell)์ ๋น๋์นญ ์ํธํ ๋ฐฉ์์ ์ฌ์ฉํด์ ์์ ํ๊ฒ ์๊ฒฉ ์๋ฒ์ ์ ์ํ๋ ํ๋กํ ์ฝ์
=> ๊ณต๊ฐํค์ ํ๋ผ์ด๋น ํค๋ก ์ด๋ฃจ์ด์ง ํค ์์ ์ฌ์ฉ
ํด๋ผ์ด์ธํธ(์ ์ํ๋ ์ชฝ)๊ฐ private key(id_rsa)๋ฅผ ๊ฐ์ง๊ณ ์๊ณ ์๋ฒ(์ ์ ๋ฐ๋ ์ชฝ)์ด public key(id_rsa.pub)์ ๊ฐ์ง๊ณ ์์ด์ผ ํต์ ์ด ๊ฐ๋ฅํจ
ํ์ฌ ์ํฉ์์ ์๊ฐํด๋ณด๋ฉด Jenkins์์ GitHub์ EC2์ ์ ๊ทผํ์ฌ CD์์
์ ์ํํด์ผ ํ๋ฏ๋ก
- ํ๋ผ์ด๋น ํค (id_rsa) โ Jenkins์ด ๊ฐ์ง๊ณ ์์ด์ผ ํจ
- ๊ณต๊ฐ ํค (id_rsa.pub) โ ์๊ฒฉ ์๋ฒ(GitHub, EC2)์ ~/.ssh/authorized_keys์ ์ ์ฅ๋์ด ์์ด์ผ ํจ
ssh-keygen -t rsa -b 4096 -C "github ์ด๋ฉ์ผ"
์ฐ์ ๋ก์ปฌ ํฐ๋ฏธ๋์์ ์ ๋ช ๋ น์ด๋ฅผ ํตํด SSHํค๋ฅผ ์์ฑํ๊ณ id_rsa(ํ๋ผ์ด๋น ํค) ์ id_rsa.pub(ํผ๋ธ๋ฆญ ํค) ๊ฐ์ ๋ณต์ฌํจ
(~/.ssh ์ ์กด์ฌ)
Jenkins์์ SSHํค ์ถ๊ฐ
์ด์ Jenkins์ SSHํค(ํ๋ผ์ด๋น ํค)๋ฅผ ์ถ๊ฐํด์ผํจ


Jenkins UI์์ ํ๋กํ์ ํด๋ฆญํ๊ณ Credentials ํด๋ฆญ -> System ํด๋ฆญ -> Global credentials์์ Add Credentials ํด๋ฆญ

Kind๋ฅผ SSH Username with private key๋ก ์ค์ ํ๊ณ Username์ผ๋ก git์ด๋ GitHub ์ฌ์ฉ์์ด๋ฆ์ ์ ์ด์ฃผ๊ณ Private Key์์ Enter directly๋ก ๋ณต์ฌํ SSH ๊ฐ์ธ ํค๋ฅผ ๋ฃ์ด์ค (~/.ssh/id_rsa ์ ๋ด์ฉ์ ๋ฃ์ด์ค)
GitHub์์ SSH ํค ์ถ๊ฐ
์์์ sshํค๋ฅผ ๋ง๋๋ ๋ช
๋ น์ด๋ฅผ ์ ์ํํ๋ค๋ฉด ๋ก์ปฌ์ ~/.ssh/id_rsa.pub ์ ๊ณต๊ฐํค๊ฐ ๋ด๊ฒจ์ ธ์์ํ
๋ฐ
ํด๋น ๋ด์ฉ์ cat ๋ช ๋ น์ด๋ฑ์ผ๋ก ํ์ธ ํ ๋ณต์ฌํ์ฌ

GitHub ๊ณ์ ์ Settings -> SSH and GPG keys๋ก ์ด๋ํ์ฌ ์๋ก์ด SSH ํค๋ฅผ ์ถ๊ฐํด์ค
EC2์์ SSHํค ์ถ๊ฐ

๋ง์ฐฌ๊ฐ์ง๋ก ~/.ssh ๋ก ์ด๋ ํ vi authorized_keys ๋ช ๋ น์ด๋ฅผ ํตํด authorized_keys์ ๋ก์ปฌ์์ ๋ง๋ SSHํค (id_rsa.pub)์ ๋ด์ฉ์ ๋ฃ์ด์ค
3. Jenkins ํ์ดํ๋ผ์ธ ์์ฑ

Jenkins ๋์๋ณด๋์์ New Item์ ํด๋ฆญํ ํ 2๋ฒ์งธ์ ์๋ Pipeline์ ์ ํ
4. Poll SCM ์ค์ (GitHub Webhook ์ฌ์ฉ ๋ถ๊ฐ ์ด์์ ๋์)
๋งจ์์์ ๋งํ๋ฏ์ด ํ์ฌ fork๋ ๋ ํฌ์งํ ๋ฆฌ์์๋ง ์์ ํด์ผํ๋ค๋ ์ ์ฝ์ด ๊ฑธ๋ ค ๋ ํฌ์งํ ๋ฆฌ์ Setting์ ๊ฑด๋ค ์ ์๋ ์ํฉ์ด๋ผ
GitHub Webhook ์ถ๊ฐ ํ ํน์ ๋ธ๋ ์น์ ๋ณ๊ฒฝ ์ฌํญ์ ์ ๋ฌ๋ฐ์ CD๋ฅผ ์งํํ ์ ์๋ ์ํ์
=> ๋์์ผ๋ก ์๊ฐํ ๋ฐฉ์์ด Poll SCM์ ์ค์ ํ์ฌ ํน์ ์ฃผ๊ธฐ(์๊ฐ)๋ง๋ค Git ์ ์ฅ์๋ฅผ ํ์ธํ๋ฉฐ ๋ณ๊ฒฝ ์ฌํญ์ด ์์ผ๋ฉด ์์ ์ ํ๋ ๊ฒ์
์ฌ์ค Poll SCM์ ์ฌ์ฉํ๋ ๋ฐฉ์์ ํ๊ฒ๋๋ฉด ๋ณ๊ฒฝ์ด ์๋๋ผ๋ ์ผ์ ์ฃผ๊ธฐ๋ง๋ค Git ์ ์ฅ์๋ฅผ ํ์ธํ๋ฏ๋ก ๋ถํ์ํ ์์ฒญ์ด ๋ง์ ์ ๋ฐ์ ์๊ณ ์ ํด์ง ์ฃผ๊ธฐ๋งํผ์ ๋๋ ์ด๊ฐ ์์ ์ ์์(ํนํ ๋ง์ ํ๋ก์ ํธ ๊ด๋ฆฌํ๋ฉด ์ฑ๋ฅ์ด...)
ํ์ง๋ง ๋ถํ์ํ ์์ฒญ์ ๊ฒฝ์ฐ ๋ก์ปฌ์์ CD ๋์์ ํด์ผํ ๋๋ง Jenkins์ ์ผ์ ํ์ฉํ ๊ฒ์ด๊ธฐ์ ๋ฌธ์ ๊ฐ ์๋ค๊ณ ์๊ฐํ๊ณ ๋๋ ์ด์ ๊ฒฝ์ฐ๋ ํ์ฌ๋ ํ๋ก์ ํธ ์ด๊ธฐ ๋จ๊ณ์ด๊ธฐ์ ๊ฐ์ํ ์ ์๋ค๊ณ ์๊ฐํ์(์ผ๋จ ๋น์ฅ์ ์๋ ๋ฐฐํฌ์ ๊ท์ฐฎ์์ ํด์ํ๋ ์ ๋๋ก๋ง ์ฌ์ฉ)
=> ๊ฐ์ฅ ํ์ค์ ์ผ๋ก ํ ์ ์๋ ๋ฐฉ์์ด Poll SCM!!
Jenkins ๋์๋ณด๋์์ ์์์ ๋ง๋ Pipeline Job์ ํด๋ฆญํด์ฃผ๊ณ
์ผ์ชฝ ๊ตฌ์ฑ(Configure) ํด๋ฆญ ํ Triggres ํด๋ฆญ

Poll SCM ์ ํ ํ H/5 * * * * ์ ๊ฐ์ด ์ํ๋ ์ฃผ๊ธฐ ์ ํ
=> Ignore post-commit hooks๋ git commit, push ๋ฑ์ ํน์ Webhook ์ด๋ฒคํธ๊ฐ ๋ฐ์ํด๋ ๋ฌด์ํ๋๋ก ์ค์ ํ๋ ๊ฒ์
(์ด์ฉํผ Webhook ์ค์ ์ ๋ชปํ๊ธฐ์ ์ด๋ฒคํธ๊ฐ ์ฌ๋ฆฌ ์์ง๋ง ํ์คํ๊ฒ ์ฒดํฌ!)
H/5 * * * * ๋ cron ํํ์์ผ๋ก ์์๋๋ก MIN / HOUR / DAY / MONTH / DAT_OF_WEEK๋ฅผ ์๋ฏธํจ
=> H(ํด์) ๊ฐ์ ๊ธฐ์ค์ผ๋ก 5๋ถ๋ง๋ค ์คํ๋จ
(*์ ์์ผ๋์นด๋๋ก ๋งค์๊ฐ, ๋งค์ผ, ๋งค์, ๋งค์์ผ ์คํ๋๋ฏ๋ก)
์ฐธ๊ณ ๋ก 0/5 ์ H/5์ ์ฐจ์ด๋
H๋ Jenkins์ ํด์ ๊ฐ์ผ๋ก ํน์ ์๊ฐ์ ๋น๋๊ฐ ๋ชฐ๋ฆฌ๋ ๊ฑธ ๋ฐฉ์งํ๊ธฐ ์ํด ๊ฐ Job๋ง๋ค ๋ค๋ฅธ ์์ ์๊ฐ์ ์๋์ผ๋ก ๋ฐฐ์ ํด์ค
(๊ฐ Jenkins Job์ด ๋์ผํ ์๊ฐ์ ์คํ๋์ง ์๊ณ ๋ถ์ฐํด์ ์คํ)
0/5๋ ์ ํํ 0, 5, 10 ... ๋ถ๋ง๋ค ์คํ๋๋๊ฑฐ๋ผ ๋ชจ๋ Job์ด ๊ฐ์ ์๊ฐ์ ์คํ๋จ
5. Jenkins์์ ํ๊ฒฝ๋ณ์ ์ค์ (+ Docker Hub credentials ์ค์ )

Jenkins ๋์๋ณด๋์์ Manage Jenkins(Jenkins ๊ด๋ฆฌ) -> System -> Global properties ์น์ ์์ Environment variables๋ฅผ ์ฒดํฌํ ํ ์ค์ ํ ์ ์์
- GITHUB_BRANCH : ๋น๋์ ๋ฐฐํฌ์ ์ฌ์ฉ๋ GitHub ์ ์ฅ์์ ๋ธ๋์น
=> backend/main - GITHUB_REPO_URL : GitHub ์ ์ฅ์ URL
(HTTPS๋ง๊ณ SSH URL์ ๊ฐ์ ธ์์ผํจ) - GRADLE_TASK : Gradle ๋น๋ ์ ์คํํ ํ์คํฌ
=> clean bootJar - DOCKER_BUILD_PLATFORMS : Docker ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ ๋์ ํ๋ซํผ
=> linux/amd64,linux/arm64 - DOCKER_IMAGE_NAME : ๋น๋๋ Docker ์ด๋ฏธ์ง์ ์ด๋ฆ๊ณผ ํ๊ทธ
=> hjungyo/2025capstone:latest ์ ๊ฐ์ด dockerhub-username/your-app:version ์ ํํ - EC2_HOST : ๋ฐฐํฌ ๋์ EC2 ์ธ์คํด์ค์ ํผ๋ธ๋ฆญ IP
- EC2_USER : EC2 ์ธ์คํด์ค์ SSH ์ ์ํ ๋ ์ฌ์ฉํ ์ฌ์ฉ์ ์ด๋ฆ
=> ubuntu (linux๋ผ๋ฉด ec2-user) - CONTAINER_PORT : Docker ์ปจํ
์ด๋๊ฐ ์ฌ์ฉํ ํฌํธ ๋ฒํธ
=> 8080
์ด๋ ๊ฒ ํ๊ฒฝ๋ณ์ ์ค์ ์ ๋ง์น๋ฉด ๋์ด ์๋!
๋ก์ปฌ์์ ๋น๋ํ์ฌ ๋ง๋ ์ด๋ฏธ์ง๋ฅผ Docker Hub์ ์ฌ๋ฆฐ ํ ์ฌ๋ผ๊ฐ ์ด๋ฏธ์ง๋ฅผ EC2์์ ๊ฐ์ ธ์ ์ปจํ
์ด๋๋ฅผ ๋์ฐ๋ฏ๋ก ๊ด๋ จ ์ค์ ์ ํด์ค์ผํจ
=> Docker Hub ์ค์

์์์ sshํค๋ฅผ ๋ฑ๋กํ๋๋๋ก credentials ์ค์ ์์ docker hub ์์ด๋๋ฅผ Username ํ๋์, ๋น๋ฐ๋ฒํธ๋ฅผ Password์ ์ ๋ ฅ ํ Create ํ๋ฉด ๋จ
ID๊ฐ์ ๊ฒฝ์ฐ๋ ์์๋ณผ ์ ์๊ฒ dockerhub-credentials๋ผ๊ณ ๋ช ์ํ์์
6. Jenkins Pipeline Script ์์ฑ
์ต์ข Script๋ ํธ๋ฌ๋ธ ์ํ ๋ถ๋ถ ์ฐธ๊ณ โผ๏ธ

๋ง์ฐฌ๊ฐ์ง๋ก ์๊น ๋ง๋ ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑ(Configure) ํด๋ฆญ ํ Pipeline์ ํด๋ฆญํ๋ฉด ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ ์ ์์
๋ฐ์ ์ฒดํฌ๋ Use Groovy Sandbox๋ Jenkins Pipeline์์ Groovy ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ๋ ๋ณด์์ ๊ฐํํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ์ต์ ์
=> Sandbox ํ๊ฒฝ์ ์ฝ๋ ์คํ์ด ์ ํ๋ ๊ฒฉ๋ฆฌ๋ ํ๊ฒฝ์ ์๋ฏธํจ
(ํน์ ๋ฉ์๋๋ ํด๋์ค์ ์ฌ์ฉ์ด ์ ํ๋์ด ์ํํ ์์ ์ ๋ฐฉ์ง, ์ ์์ ์ธ ์ฝ๋ ์คํ ๋ฐฉ์ง ๋ฑ)
์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํด๋ณด๋ฉด
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL
}
}
stage('Build') {
steps {
dir('backend') {
sh "./gradlew ${env.GRADLE_TASK}"
}
}
}
stage('Build and Push Docker Image') {
steps {
dir('backend') {
withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh """
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
docker buildx build --platform ${env.DOCKER_BUILD_PLATFORMS} -t ${env.DOCKER_IMAGE_NAME} --push .
"""
}
}
}
}
stage('Deploy to EC2') {
steps {
sshagent(credentials: ['ssh-key']) {
sh """
ssh -o StrictHostKeyChecking=no ${env.EC2_USER}@${env.EC2_HOST} '
sudo docker stop \$(sudo docker ps -q --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker rm \$(sudo docker ps -aq --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker pull ${env.DOCKER_IMAGE_NAME}
sudo docker run --platform linux/amd64 -d -p ${env.CONTAINER_PORT}:${env.CONTAINER_PORT} ${env.DOCKER_IMAGE_NAME}
'
"""
}
}
}
}
}
- ๊ฐ stage๊ฐ ๋
๋ฆฝ์ ์ผ๋ก ์คํ๋๋ฏ๋กdir('backend')๋ฅผ ํตํด ๋๋ ํ ๋ฆฌ๋ฅผ ์ด๋ํ๋ ๊ฒ์ ๋ณผ ์ ์์
=> ํ์ฌ GitHub์๋ backend์ front ๋๋ ํ ๋ฆฌ๊ฐ ํจ๊ป ์กด์ฌํ๊ธฐ์ backend ๋๋ ํ ๋ฆฌ๋ก ์ด๋ ํ ์์ ์ ์ํํด์ผํจ - EC2 ๋ฐฐํฌ ๋ถ๋ถ์ ์ดํด๋ณด๋ฉด
- SSH ๋ช
๋ น์ ์ฌ์ฉํ์ฌ EC2 ์ธ์คํด์ค์ ์ฐ๊ฒฐํ๊ณ ์๋๋ฐ -o StrictHostKeyChecking=no ์ต์
์ ๊ฒฝ์ฐ ์ด๊ธฐ ์ฐ๊ฒฐ ์ ํธ์คํธ ํค ํ์ธ์ ๊ฑด๋๋ฐ๋ ๊ฒ์
(ํธ์คํธ ํค๋ SSH ์๋ฒ์ ๊ณ ์ ์๋ณ์์ -> ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐํ๋ ค๋ ์๋ฒ์ ์ ์์ ํ์ธํ๋ ๋ฐ ์ฌ์ฉ๋จ)
๋๋ณด๊ธฐ"Are you sure you want to continue connecting (yes/no)?" ํ๋กฌํํธ๋ฅผ ๋ฐฉ์ง
=> ๋ณด์์ ์ํด known_hosts ํ์ผ์ ๊ด๋ฆฌํ๋๊ฒ ๋ ์ข์
(๋ณด์ ๊ณ์ธต ์ถ๊ฐ) - sshagent๋ Jenkins ํ์ดํ๋ผ์ธ์์ SSH ์ฐ๊ฒฐ์ ์์ ํ๊ฒ ๊ด๋ฆฌํ๊ณ ์ฌ์ฉํ๊ธฐ ์ํ ๊ธฐ๋ฅ์ธ๋ฐ ์ด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ SSH Agent ํ๋ฌ๊ทธ์ธ์ ์ค์นํด์ค์ผํจ!
- SSH ๋ช
๋ น์ ์ฌ์ฉํ์ฌ EC2 ์ธ์คํด์ค์ ์ฐ๊ฒฐํ๊ณ ์๋๋ฐ -o StrictHostKeyChecking=no ์ต์
์ ๊ฒฝ์ฐ ์ด๊ธฐ ์ฐ๊ฒฐ ์ ํธ์คํธ ํค ํ์ธ์ ๊ฑด๋๋ฐ๋ ๊ฒ์
ํธ์คํธํค ํ์ธ์ ์ฐ๊ฒฐํ๋ ค๋ ์๋ฒ๊ฐ ์ค์ ๋ก ์๋ํ ์๋ฒ์ธ์ง ํ์ธํ๋ ๊ณผ์ ์ด๋ผ๊ณ ๋งํ๋๋ฐ
์ด๋ฅผ ๊ฑด๋๋ฐ๋ฉด ๊ณต๊ฒฉ์๊ฐ ์ค๊ฐ์์ ํต์ ์ ๊ฐ๋ก์ฑ๊ฑฐ๋ ์์กฐ๋ ์๋ฒ๋ก ์ฐ๊ฒฐ์ ์ ๋ํ ์ํ์ด ์์
(Man-in-the-Middle ์ด๋ผ๋ ์ค๊ฐ์ ๊ณต๊ฒฉ)
=> SSHํค ์์ผ๋ฉด ์ด์ฉํผ ์๋ฏธ๊ฐ ์์ง ์๋? ๋ผ๊ณ ์๊ฐ์ ํ๋๋ฐ ์ฐพ์๋ณด๋
์ํธํ๋ ๋ด์ฉ์ ๋ณผ ์ ์๊ฒ ์ง๋ง ํต์ ์ ํจํด, ๋น๋, ํฌ๊ธฐ ๋ฑ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ฉฐ ๊ฐ๋ก์ฑ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด๋๊ณ ํฅํ ๊ณต๊ฒฉ ์ค๋น๋ฅผ ํ ์ ์๋ค๋ ๋ด์ฉ์ ๋ณด๊ณ ๋ณด์์ด ์ค์ํ๊ตฌ๋๋ฅผ ๋๊ผ์....
known_hosts ํ์ผ์ ๊ด๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก ํ์ดํ๋ผ์ธ ์คํฌ๋ฆฝํธ ๋ณ๊ฒฝ
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL
}
}
stage('Build') {
steps {
dir('backend') {
sh "./gradlew ${env.GRADLE_TASK}"
}
}
}
stage('Build and Push Docker Image') {
steps {
dir('backend') {
withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh """
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
docker buildx build --platform ${env.DOCKER_BUILD_PLATFORMS} -t ${env.DOCKER_IMAGE_NAME} --push .
"""
}
}
}
}
stage('Update Known Hosts') {
steps {
sh """
mkdir -p ~/.ssh
ssh-keyscan -H ${env.EC2_HOST} >> ~/.ssh/known_hosts
"""
}
}
stage('Deploy to EC2') {
steps {
sshagent(credentials: ['ssh-key']) {
sh """
ssh ${env.EC2_USER}@${env.EC2_HOST} '
sudo docker stop \$(sudo docker ps -q --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker rm \$(sudo docker ps -aq --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker pull ${env.DOCKER_IMAGE_NAME}
sudo docker run --platform linux/amd64 -d -p ${env.CONTAINER_PORT}:${env.CONTAINER_PORT} ${env.DOCKER_IMAGE_NAME}
'
"""
}
}
}
}
}
Update Known Hosts๋ผ๋ ์คํ
์ด์ง๋ฅผ ์ถ๊ฐํ์๊ณ
ssh-keyscan ๋ช
๋ น์ ์ฌ์ฉํ์ฌ EC2 ํธ์คํธ์ SSH ํค๋ฅผ ๊ฐ์ ธ์ค๊ณ ์์
=> ๊ฐ์ ธ์จ ํค๋ฅผ ~/.ssh/known_hosts ํ์ผ์ ์ถ๊ฐ
(๊ธฐ์กด์ StrictHostKeyChecking=no ์ต์ ์ ์ ๊ฑฐํ์์)
์๋ ๋ฐฉ์์ ์ดํด๋ณด๋ฉด
ssh-keyscan์ผ๋ก ๊ฐ์ ธ์จ ํธ์คํธ ํค๋ ~/.ssh/known_hosts ํ์ผ์ ์ ์ฅ๋๊ณ
SSH ์ฐ๊ฒฐ์, ํด๋ผ์ด์ธํธ๋ ์๋ฒ๊ฐ ์ ๊ณตํ๋ ํธ์คํธ ํค๋ฅผ know_hosts ํ์ผ์ ํค์ ๋น๊ตํจ
=> ํค๊ฐ ์ผ์นํ๋ฉด ์ฐ๊ฒฐ์ ์งํํ๊ณ , ์ผ์นํ์ง ์์ผ๋ฉด ๊ฒฝ๊ณ ๋ฅผ ํ์
(์ด๋ฅผ ํตํด ์ด๊ธฐ ์ฐ๊ฒฐ ์ ํธ์คํธ ํค๋ฅผ ์๋์ผ๋ก ์ ์ฅํ๊ณ , ์ดํ ์ฐ๊ฒฐ์์๋ ์ ์ฅ๋ ํค๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ์ ์ ์์ ํ์ธํจ)
ํธ๋ฌ๋ธ ์ํ

ํด์น์ ๋..? ๋ผ๊ณ ์๊ฐ์ด ๋ค์๋ค๋ฉด ๋ญ๊ฐ๋ฅผ ๋์น๊ณ ์๋ค๋ ๋ป!
1. ๊น๋จน์ application.yaml ํ์ผ... (with Permission denied)

์ฐ์ ์์์ ์ค์ ํ๋๋๋ก Jenkins ๊ด๋ฆฌ > Credentials > System > Global credentials ๋ก ์ด๋ ํ
kind๋ฅผ 'Secret file'๋ก ํ์ฌ ๋ก์ปฌ์์ ์ฌ์ฉ์ค์ธ application.yaml์ ์ฌ๋ ค์ฃผ์์
(ํ์ฌ ๊นํ๋ธ์๋ application.yaml.example๋ง ์ฌ๋ผ๊ฐ์๊ธฐ์ ๋ก์ปฌ์์ ์ฌ์ฉํ๋ yaml์ ์ฃผ์
ํ๊ธฐ ์ํด)
stage('Inject Configuration') {
steps {
withCredentials([file(credentialsId: 'application.yaml', variable: 'APP_YAML')]) {
sh 'cp -f $APP_YAML backend/src/main/resources/application.yaml'
}
}
}
๋ค์๊ณผ ๊ฐ์ด GitHub์์ Checkout ํ์ application.yaml์ ์ถ๊ฐํ๋ stage๋ฅผ ๋ฃ์ด์ฃผ์์
์ด ๋, cp ๋ช ๋ น์ด์ -f ์ต์ ์ ์ถ๊ฐํ์ฌ permission denied๋ฅผ ๋ฌด์ํ๊ณ ๊ฐ์ ๋ก ํ์ผ์ ๋ฎ์ด์ฐ๋๋ก ํ์์ต๋๋ค
2. No credentials specified
์ฒซ๋ฒ ์งธ stage์ธ Checkout ๋ถ๋ถ์์ ์๊พธ No credentials specified ์ค๋ฅ๊ฐ ๋ฐ์ํ์์
=> Git ์ ์ฅ์์ ์ ๊ทผํ๊ธฐ ์ํ ์ธ์ฆ ์ ๋ณด๊ฐ ์ ๊ณต๋์ง ์์์์ ๋ํ๋
ํ๋ผ์ด๋น ์ ์ฅ์์ ๊ฒฝ์ฐ ์ ์ ํ SSH ํค๋ ์ธ์ฆ ์ ๋ณด๊ฐ ํ์ํ๊ธฐ์ ์ด์ ์ GitHub์ ํจ๊ป ์ค์ ํด๋ ssh-key๋ฅผ Script์ ๋ช ์ํด์คฌ์
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL, credentialsId: 'ssh-key'
}
}
๋ค์๊ณผ ๊ฐ์ด credentialsId์ ssh-key๋ฅผ ๋ช ์ํด์คฌ์
3. gradlew Permission denied
์ ์๋ฌ ๊ฐ์ ๊ฒฝ์ฐ๋ Jenkins ์์
๊ณต๊ฐ(workspace)์ ์๋ gradelw ํ์ผ์ ๋ํด ์คํ ๊ถํ์ ๋ถ์ฌํด์ผํจ
stage('Grant Permission') {
steps {
sh 'chmod +x ./backend/gradlew'
}
}
๋ค์๊ณผ ๊ฐ์ด ์คํฌ๋ฆฝํธ์ Permission ํ์ฉ์ ํด์ฃผ์์
4. JDK๋ฅผ ์ฐพ์ง ๋ชปํ๋ ์ค๋ฅ
์๊พธ ๋น๋ํ ๋ JDK๋ฅผ ๋ชป์ฐพ์์ ์คํจํ์
์ฌ๋ฌ ๋ธ๋ก๊ทธ๋ค์ ์คํด๋ณด์๋๋ฐ Docker๊ฐ ์๋๋ผ ์์์ ์งํํ ๋ฐฉ์๋๋ก Jenkins๋ฅผ ์ค์นํ๊ฒ ๋๋ฉด JDK๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค์ ์ด ๋์ด์์ง ์๋ ๊ฒ ๊ฐ์
=> ๋ฐ๋ก ์ค์ ํด๋ณด์!

๋์๋ณด๋์์ Jenkins ๊ด๋ฆฌ -> Tools ์ ๋ค์ด๊ฐ ADD JDK๋ฅผ ํด์ฃผ๋ฉด๋จ
(Java 17๊ธฐ์ค)
๋ง์ฝ Java 17์ด ์ค์น๋์ง ์์๋ค๋ฉด brew install openjdk@17 ๋ก ์ค์นํด์ค
echo 'export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
์ค์น๊ฐ ๋์๋ค๋ฉด ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํด์ฃผ๋ฉด๋จ
๋ช ๋ น์ด๋ฅผ ์ดํด๋ณด๋ฉด $PATH ๋ฅผ ํตํด OpenJDK 17์ bin ๋๋ ํ ๋ฆฌ๊ฐ ๊ธฐ์กด PATH ์์ ์ถ๊ฐ๋จ
=> ์ด๋ ๊ฒ ์ค์ ํ๋ฉด Java ๊ด๋ จ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ ๋ ์์คํ ์ ๋จผ์ OpenJDK 17์ bin ๋๋ ํ ๋ฆฌ์์ ํด๋น ๋ช ๋ น์ด๋ฅผ ์ฐพ๊ณ , ๊ทธ ๋ค์์ ๋ค๋ฅธ ๋๋ ํ ๋ฆฌ๋ค์ ๊ฒ์ํจ
(homebrew๋ก ์ค์นํ๊ฒ ๋๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก opt/homebrew/opt/openjdk@17 ์ ๊ฒฝ๋ก๋ก ์ค์น๊ฐ ๋จ)
์ด์
Jenkins Tools ์์ JAVA_HOME ๋ถ๋ถ์
/opt/homebrew/Cellar/openjdk@17/17.0.14/libexec/openjdk.jdk/Contents/Home ๋ผ๊ณ ์ค์น๋ Java์ ์ค์ ๊ฒฝ๋ก๋ฅผ ์ ๋ ฅํด์ค
(์ด๋ /usr/libexec/java_home -v 17๋ฅผ ํฐ๋ฏธ๋์ ์ ๋ ฅ ํ ์ค์น ๊ฒฝ๋ก๋ฅผ ํ์ธํ ์ ์์)
5. docker : command not found
์์์ docker ํ๋ฌ๊ทธ์ธ์ ์ค์นํ๋๋ฐ ์ ์ด๋ฐ ์๋ฌ๊ฐ ๋ฌ์๊น..?
=> ํ๋ฌ๊ทธ์ธ์ Jenkins์ Docker์ ํตํฉ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง, ์ค์ Docker ๋ช ๋ น์ด์ ์์น๋ ์์ง ๋ชปํ๊ธฐ ๋๋ฌธ์
ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํด์ Jenkins์๊ฒ Docker ์คํ ํ์ผ์ ์ ํํ ์์น๋ฅผ ์๋ ค์ค์ผํจ
(Jenkins๊ฐ sh 'docker ...' ๋ช
๋ น์ด๋ฅผ ์คํํ ๋, ์์คํ
์ PATH ํ๊ฒฝ ๋ณ์๋ฅผ ํตํด Docker CLI ์คํ ํ์ผ์ ์ฐพ์)
environment {
PATH = "/usr/local/bin:/Applications/Docker.app/Contents/Resources/bin:${env.PATH}"
}
which docker ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ ๋ /opt๋ก ์์ํ๋ ๊ฒฝ๋ก๊ฐ ๋์์ ์ด๋ฅผ PATH์ ์ ์ผ๋ฉด docker-credential-desktop ์ด๋ผ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํจ
=> Docker Desktop์ ์ธ์ฆ ๋๊ตฌ๋ฅผ ์ฐพ์ง ๋ชปํด ๋ฐ์ํ๋ ์ค๋ฅ
Docker Desktop์ ๊ธฐ๋ณธ ์ค์น ๊ฒฝ๋ก์ธ /usr/local/bin:/Applications/Docker.app/Contents/Resources/bin๋ฅผ PATH์ ์ถ๊ฐํ์ฌ docker-credential-desktop์ ์ฐพ์ ์ ์๊ฒ ํด์ผํจ
ํ์ฌ ๋ก์ปฌ์์ Docker Desktop์ ์ค์นํ์ฌ ์ฌ์ฉ์ค์ด๊ธฐ์ ~/.docker/config.json ํ์ผ์ credsStore ์ค์ ์ด "desktop"์ผ๋ก ๋์ด ์์
=> ์ด ์ค์ ๋๋ฌธ์ Docker๋ docker-credential-desktop์ ์ฐพ์ผ๋ ค๊ณ ํ๊ธฐ์ ์์ ๊ฐ์ด ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒ์
(๋ฌด์กฐ๊ฑด Docker Desktop์ ๊น์์ผํ๋๊ฑด ์๋!!)
์์ PATH๋ฅผ ์ค์ ํ๋ ์ฝ๋๋ฅผ ๋ณด๊ณ PATH๋ฅผ ํตํด ์ด๋ป๊ฒ ๋ช ๋ น์ด๊ฐ ๋์ํ๋์ง ๊ถ๊ธํด์ ๋ ์ฐพ์๋ด
์ด์์ฒด์ ๋ PATH์ ๋์ด๋ ๋๋ ํ ๋ฆฌ๋ฅผ ์ผ์ชฝ์์ ์ค๋ฅธ์ชฝ์ผ๋ก ์์ฐจ์ ์ผ๋ก ๊ฒ์ํ๋ฉฐ ๋ช
๋ น์ด์ ์ผ์นํ๋ ์คํ ํ์ผ์ ์ฐพ์ผ๋ฉด ์ฆ์ ๊ทธ ํ์ผ์ ์คํํ๊ณ ๊ฒ์์ ์ค๋ดํจ
(์ผ์ชฝ์ด ์ฐ์ ์์๋ฅผ ๊ฐ์ง๋ฏ๋ก ๋์ผํ ์ด๋ฆ์ ๋ช
๋ น์ด๊ฐ ์ฌ๋ฌ ๋๋ ํ ๋ฆฌ์ ์๋ค๋ฉด, ๊ฐ์ฅ ์ผ์ชฝ์ ์๋๊ฒ ์คํ๋จ)
:$PATH๋ฅผ ํตํด ์ด์ด๋ถ์ด๋ ์ด์ ๋ ์๋ก์ด ๋๋ ํ ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ฉด์ ๊ธฐ์กด์ ๋ช
๋ น์ด๋ ๊ณ์ ์ฌ์ฉํ ์ ์๊ฒ ํ๊ธฐ ์ํจ์
(๋ง์ฝ :๋ฅผ ์ฐ์ง ์๊ณ PATH = ์ผ๋ก ๋๋๋ค๋ฉด ๊ธฐ์กด์ ์ถ๊ฐํ PATH๋ค์ด ์ฌ๋ผ์ง๊ธฐ์ ๋ค๋ฅธ ๋ช
๋ น์ด๋ค์ ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํจ)
=> ๊ธฐ์กด์ $PATH ๊ฒฝ๋ก ์์ ์ถ๊ฐ
๋ํ Jenkins ํ์ดํ๋ผ์ธ์ ๋๋ฆด ๋ Docker ๋ฐ๋ชฌ์ด ํ์ฑํ๋์ด ์์ด์ผํจ
=> Docker Desktop์ด ์คํ์ค์ด์ฌ์ผํจ
(Homebrew๋ฅผ ํตํด Docker Engine๋ง ์ค์นํ๋ค๋ฉด ๋ค๋ฅด๊ฒ ์ค์ )
์ต์ข ํ ์คํธ ๋ฐ ํ์ดํ๋ผ์ธ ์ฝ๋
pipeline {
agent any
environment {
PATH = "/usr/local/bin:/Applications/Docker.app/Contents/Resources/bin:${env.PATH}"
}
stages {
stage('Checkout') {
steps {
git branch: env.GITHUB_BRANCH, url: env.GITHUB_REPO_URL, credentialsId: 'ssh-key'
}
}
stage('Inject Configuration') {
steps {
withCredentials([file(credentialsId: 'application.yaml', variable: 'APP_YAML')]) {
sh 'cp -f $APP_YAML backend/src/main/resources/application.yaml'
}
}
}
stage('Grant Permission') {
steps {
sh 'chmod +x ./backend/gradlew'
}
}
stage('Build') {
steps {
dir('backend') {
sh "./gradlew ${env.GRADLE_TASK}"
}
}
}
stage('Build and Push Docker Image') {
steps {
sh 'docker info'
dir('backend') {
withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
sh """
echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
docker buildx build --platform ${env.DOCKER_BUILD_PLATFORMS} -t ${env.DOCKER_IMAGE_NAME} --push .
"""
}
}
}
}
stage('Update Known Hosts') {
steps {
sh """
mkdir -p ~/.ssh
ssh-keyscan -H ${env.EC2_HOST} >> ~/.ssh/known_hosts
"""
}
}
stage('Deploy to EC2') {
steps {
sshagent(credentials: ['ssh-key']) {
sh """
ssh ${env.EC2_USER}@${env.EC2_HOST} '
sudo docker stop \$(sudo docker ps -q --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker rm \$(sudo docker ps -aq --filter ancestor=${env.DOCKER_IMAGE_NAME})
sudo docker pull ${env.DOCKER_IMAGE_NAME}
sudo docker run --platform linux/amd64 -d -p ${env.CONTAINER_PORT}:${env.CONTAINER_PORT} ${env.DOCKER_IMAGE_NAME}
'
"""
}
}
}
}
}

EC2์ ๋ค์ด๊ฐ sudo docker ps ๋ช ๋ น์ด๋ฅผ ํตํด ์คํ์ค์ธ ์ปจํ ์ด๋๋ฅผ ํ์ธํด๋ณด๋ฉด ์ ๋์ํ๊ณ ์๋ ๊ฒ์ ๋ณผ ์ ์์


์ถ๊ฐ๋ก ๊ฐ๋จํ ํ์๊ฐ์ ๋ก์ง๋ ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค
๋ฐ๋ผ๋ณด๊ณ ์๋ Branch์ ์ปค๋ฐ ๊ธฐ๋ก์ ๋ณํ๊ฐ ์๊ธฐ๋ฉด ์ ํด์ง ์๊ฐ๋ง๋ค ํ์ธ ํ ์คํฌ๋ฆฝํธ๊ฐ ์คํ๋จ!
=> ํ ์คํธ ํด๋ณด๊ธธ!!
'๐ Infra > CI ยท CD' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Github Actions + Docker + EC2๋ก Spring Boot CI /CD ๊ตฌ์ถ (0) | 2025.03.23 |
---|---|
Github Actions ํบ์๋ณด๊ธฐ (0) | 2025.02.26 |
GitHub Actions ์๊ฐ (0) | 2024.05.01 |