๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ› ๏ธ INFRA

GCP, Jenkins + Docker๋กœ Spring Boot CI/CD ๊ตฌ์ถ•ํ•˜๊ธฐ

by Lucy Oh 2024. 6. 22.
GDSC Solution Challenge์— ๋„์ „ํ•˜๋ฉด์„œ, ์ฒ˜์Œ GCP๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค!
๊ธฐ์กด ์Šคํ”„๋ง๋ถ€ํŠธ ์•ฑ์„ ๊ฐœ๋ฐœํ•  ๋•Œ ๋ณดํ†ต AWS์˜ CodeDeploy (+ S3, EC2)๋ฅผ ํ™œ์šฉํ•˜์—ฌ CI/CD๋ฅผ ๊ตฌ์ถ•ํ•˜์˜€๋Š”๋ฐ,
์ด๋ฒˆ์—” GCP๋„ ์‚ฌ์šฉํ•ด๋ณผ๊ฒธ, ์ƒˆ๋กœ์šด ํˆด์ธ Jenkins๋ฅผ ํ†ตํ•˜์—ฌ CI/CD๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณธ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค.

 

๊ณผ์ • ์ •๋ฆฌ

[GCP] Compute Engine ์„ค์ •

์„ธํŒ… ์ •๋ณด

์„œ๋ฒ„๋Š” ์Šคํ”„๋ง๋ถ€ํŠธ์šฉ ์„œ๋ฒ„, ์  ํ‚จ์Šค์šฉ ์„œ๋ฒ„ ์ด 2๊ฐœ๋ฅผ ์„ธํŒ…ํ•ด์ฃผ์—ˆ๋‹ค.

  • ubuntu
  • http/https ํŠธ๋ž˜ํ”ฝ ํ—ˆ์šฉ (๋ฏธ๋ฆฌ 8080 - 9090 ํฌํŠธ๋ฅผ ์—ด์–ด์คŒ.)
  • ์„œ๋ฒ„
    1. remine-springboot-server [์Šคํ”„๋ง๋ถ€ํŠธ์šฉ ์„œ๋ฒ„]
    2. remine-jenkins-server [์  ํ‚จ์Šค์šฉ ์„œ๋ฒ„]

 

์™ธ๋ถ€ ๊ณ ์ • ์ฃผ์†Œ ์˜ˆ์•ฝ

VPC ๋„คํŠธ์›Œํฌ > IP ์ฃผ์†Œ๋กœ ๋“ค์–ด๊ฐ€์„œ ๋‘ ์ธ์Šคํ„ด์Šค ๋ชจ๋‘ ์™ธ๋ถ€ ๊ณ ์ • IP ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

  • ์ „์—ญ์ด ์•„๋‹Œ ์ง€์—ญ์œผ๋กœ ์„ค์ •:  asia-northeast3 (์„œ์šธ) ๋กœ ์„ค์ •ํ•จ.


[VM] ์Šค์™‘ ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •

์Šค์™‘ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์„ค์ •ํ•˜๋Š” ์ด์œ ?

Swap ๋ฉ”๋ชจ๋ฆฌ๋Š” ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ, ๋ฌผ๋ฆฌ์  ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•  ๋•Œ ์‹œ์Šคํ…œ์— ์‚ฌ์šฉ๋  ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ์ž์›์„ ํ• ๋‹นํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.

 

(๋ฌผ๋ก  GCP์˜ ๊ฒฝ์šฐ๋Š” ์•„๋‹ˆ์ง€๋งŒ..) AWS EC2๋ฅผ ํ”„๋ฆฌํ‹ฐ์–ด๋กœ ์„ค์ •ํ•˜๊ณ  Gradle ๋นŒ๋“œ ์ž‘์—…์„ ์‹œ๋„ํ•˜๋Š” ๊ณผ์ •์—์„œ ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ–ˆ๋˜ ๊ฒฝํ—˜์ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—๋„ ๋ฏธ๋ฆฌ ์Šคํ”„๋ง๋ถ€ํŠธ ์„œ๋ฒ„์™€ ์  ํ‚จ์Šค ์„œ๋ฒ„ ๋‘˜ ๋‹ค ์Šค์™‘ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

์„ค์ • ๋ฐฉ๋ฒ•

$ sudo dd if=/dev/zero of=/swapfile bs=128M count=16
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ sudo  swapon -s
$ sudo vi /etc/fstab

 

์•„๋ž˜ ๋‚ด์šฉ์„ /etc/fstab์— ์ถ”๊ฐ€ํ•จ

/swapflie swap swap defaults 0 0

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •์ด ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ free -h

 

์ฐธ๊ณ  ์ž๋ฃŒ

https://repost.aws/ko/knowledge-center/ec2-memory-swap-file

 

์Šค์™‘ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ Amazon EC2 ์ธ์Šคํ„ด์Šค์—์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์Šค์™‘ ์ŠคํŽ˜์ด์Šค๋กœ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

Amazon Elastic Compute Cloud(Amazon EC2) ์ธ์Šคํ„ด์Šค์—์„œ ์Šค์™‘ ํŒŒ์ผ๋กœ ์‚ฌ์šฉํ•  ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?

repost.aws


[DOCKER] ๋„์ปค ์„ค์น˜ํ•˜๊ธฐ

๋‘๊ฐœ์˜ Compute Engine ์ค‘์—์„œ Jenkins server์— ์„ค์น˜๋ฅผ ํ•ด์ฃผ๋„๋ก ํ•˜์ž.

→ ์ด ๋’ค๋กœ๋„ ์ญ‰ ๋”ฐ๋กœ ์–ธ๊ธ‰์ด ์—†๋Š” ์ด์ƒ ์  ํ‚จ์Šค ์„œ๋ฒ„์—์„œ ๊ณ„์† ์„ธํŒ…ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค!

 

์•„๋ž˜ ๋ช…๋ น์–ด๋“ค์„ ์ญ‰ ์ณ์„œ ๋„์ปค๋ฅผ ์„ค์น˜ํ•ด์ฃผ์ž!

$ sudo apt update
$ sudo apt-get install ca-certificates curl gnupg
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg

$ echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

$ sudo apt update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
$ sudo chmod 006 /var/run/docker.sock

 

 

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์„ค์น˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ docker -v

๋„์ปค ์„ค์น˜ ์™„๋ฃŒ!


[JENKINS] ์  ํ‚จ์Šค ์„ค์น˜ํ•˜๊ธฐ

๋„์ปค์—์„œ ์  ํ‚จ์Šค ๊ณต์‹ ์ด๋ฏธ์ง€๋ฅผ pull ๋ฐ›์•„์™€์ฃผ์ž.

$ docker pull jenkins/jenkins:jdk17

 

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•˜์—ฌ ๋‹ค์šด๋กœ๋“œ ๋œ ์ด๋ฏธ์ง€๋ฅผ ํ™•์ธํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

$ docker images

 

์  ํ‚จ์Šค ์ด๋ฏธ์ง€๋ฅผ ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰ํ•ด๋ณด์ž.

$ docker run -p 80:8080 -v /var/run/docker.sock:/var/run/docker.sock --name jenkins jenkins/jenkins:jdk17

 

 

์ฐธ๊ณ ๋กœ ์—ฌ๊ธฐ์„œ, Jenkins๋ฅผ 80๋ฒˆ ํฌํŠธ๋กœ ๋„์›Œ์ฃผ์—ˆ๋Š”๋ฐ,
GCP์˜ ๋„คํŠธ์›Œํฌ ๋ณด์•ˆ > ๋ฐฉํ™”๋ฒฝ ์ •์ฑ… ์„ค์ •์„ ํ†ตํ•ด ๋ฏธ๋ฆฌ VM์˜ ํฌํŠธ๋ฅผ ์—ด์–ด๋†“์•„์•ผ ํ•œ๋‹ค!


[JENKINS] ์  ํ‚จ์Šค ์ดˆ๊ธฐ ์„ค์ •

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ„์น˜์—์„œ ์  ํ‚จ์Šค์˜ ์ดˆ๊ธฐ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งŒ์•ฝ ๋กœ๊ทธ๊ฐ€ ๋œจ์ง€ ์•Š์•˜๋‹ค๋ฉด jenkins shell๋กœ ์ด๋™ ํ›„ ๋กœ๊ทธ์ธ secret์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ docker exec -it {๋„์ปค ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„} /bin/bash
$ cat /var/jenkins_home/secrets/initialAdminPassword
  • ๋งŒ์•ฝ ์œ„์—์„œ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ๋ช…๋ น์–ด๋ฅผ ๋˜‘๊ฐ™์ด ์ž…๋ ฅ, ์‹คํ–‰ํ–ˆ๋‹ค๋ฉด --name์„ jenkins๋กœ ์„ค์ •ํ–ˆ์œผ๋ฏ€๋กœ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„์€ jenkins ์ด๋‹ค!

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์  ํ‚จ์Šค VM์˜ ํผ๋ธ”๋ฆญ IPV4:์  ํ‚จ์Šค ํฌํŠธ๋กœ ์ ‘์†ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ดˆ๊ธฐ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.

Administrator password ์ž…๋ ฅ ๋ถ€๋ถ„์— ์ดˆ๊ธฐ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์ž.

 

Continue ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐ€๊ฒŒ ๋˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ Install suggested plugins๋ฅผ ์„ ํƒํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋ชจ๋‘ ๋‹ค์šด ๋ฐ›์•˜๋‹ค๋ฉด, admin user๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๋Š”๋ฐ

์—ฌ๊ธฐ์„œ๋Š” ๊ผญ ๊ด€๋ฆฌ์ž ์œ ์ €๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ  save and continue๋ฅผ ๋ˆŒ๋Ÿฌ์ฃผ์ž.

  • skip and continue as admin์„ ์„ ํƒํ•˜๋ฉด ๋‚˜์ค‘์— ์ถ”๊ฐ€์ ์ธ ๋ฉค๋ฒ„ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค. [๊ฐ์ฃผ:1]


[GITHUB] git ์ ‘๊ทผ์„ ์œ„ํ•œ credential ์ƒ์„ฑ

์ด์ œ ์  ํ‚จ์Šค์—์„œ git์„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž์‹ ์˜ ํ”„๋กœํ•„์˜ credential์„ ์ƒ์„ฑํ•ด์ค„ ๊ฒƒ์ด๋‹ค.

GitHub์—์„œ ํ”„๋กœํ•„ ์‚ฌ์ง„์„ ๋ˆ„๋ฅด๋ฉด ๋‚˜์˜ค๋Š” ์‚ฌ์ด๋“œ๋ฐ”์˜ Settings > ์™ผ์ชฝ ์‚ฌ์ด๋“œ์˜ Developer settings ์— ๋“ค์–ด๊ฐ€์ž

 

Personal access tokens > Tokens (classic)์— ๋“ค์–ด๊ฐ€์„œ,

Generate new token > Generate new token(classic)์„ ํด๋ฆญํ•˜์ž

 

ํ† ํฐ์˜ ์‚ฌ์šฉ ์šฉ๋„๋ฅผ Note์— ๊ฐ„๋‹จํžˆ ์ ์–ด์ฃผ๊ณ ,

Scope๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

  • repo, admin:repo_hook ์— ์ฒดํฌ๋ฅผ ํ•ด์ฃผ์—ˆ๋‹ค. (์•„๋ž˜ ์‚ฌ์ง„ ์ฐธ๊ณ )

 

๊ทธ ์ดํ›„ Generate token์„ ํด๋ฆญํ•˜๋ฉด ghp_๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ† ํฐ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

(์ด๋•Œ๋งŒ ํ™•์ธ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ์žฅ์— ์ž˜ ๋ณต์‚ฌํ•ด๋‘์ž!)


[JENKINS] Jenkins์— Git credential ์„ค์ •

Dashboard > Jenkins ๊ด€๋ฆฌ > System ์— ์ ‘์†ํ•˜์ž

GitHub ๋ถ€๋ถ„์— ์žˆ๋Š” +ADD ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ๋’ค, Jenkins ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์ƒˆ๋กœ์šด Credential ๋‘ ๊ฐœ๋ฅผ ์ƒ์„ฑํ•ด์ค„ ์˜ˆ์ •์ด๋‹ค.

(ADD ๋ฒ„ํŠผ ์œ„์น˜๋Š” ์•„๋ž˜ ์‚ฌ์ง„ ์ฐธ๊ณ )

 

์ƒˆ๋กœ์šด Credential ์„ค์ •

1. jenkins-github-secret-text

Secret์— ์•„๊นŒ ๋ฐœ๊ธ‰๋ฐ›์€ ghp_ ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ‚ค๋ฅผ ๋ถ™์—ฌ๋„ฃ์ž

jenkins-github-secret-text ์„ค์ •

 

2. jenkins-github-username-password 

Username์—” github ์ด๋ฉ”์ผ, Password์—” ์•„๊นŒ ๋ฐœ๊ธ‰๋ฐ›์€ ghp_๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ‚ค๋ฅผ ๋ถ™์—ฌ๋„ฃ์ž

jenkins-github-username-password ์„ค์ •

 

Connection Test

  • Name์—” repository url
  • Credential์—” ์œ„์—์„œ ์ž‘์„ฑํ•œ jenkins-github-secret-text ๋ฅผ ์„ค์ •ํ•˜์ž

์šฐ์ธก ํ•˜๋‹จ์˜ Test connection ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ  Credentials verified for user 5jisoo, rate limit: 4999 ์™€ ๊ฐ™์€ ๋ฌธ๊ตฌ๊ฐ€

์ขŒ์ธก ํ•˜๋‹จ์— ์ƒ์„ฑ๋˜๋ฉด ์—ฐ๊ฒฐ์— ์„ฑ๊ณตํ•œ ๊ฒƒ์ด๋‹ค! (๋™์‹œ์— Credential ์„ค์ •๋„ ์ž˜ ๋œ๊ฒƒ!)

 

์ €์žฅ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ž˜ ์ €์žฅํ•ด์ฃผ์ž

Credential ์ฒดํ‚น ํ…Œ์ŠคํŠธ


[JENKINS] Jenkins์— Git Clone Pipeline ์ƒ์„ฑ

์ƒˆ๋กœ์šด ํŒŒ์ดํ”„๋ผ์ธ ์ƒ์„ฑ

์  ํ‚จ์Šค ์ขŒ์ธก + ์ƒˆ๋กœ์šด Item์„ ํด๋ฆญํ•˜์—ฌ Pipeline์„ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•ด๋ณด์ž

  • ์ด๋ฆ„์€ ๋งˆ์Œ๋Œ€๋กœ ์ง€์–ด๋„ ๋˜๊ณ ,
  • Pipeline์„ ์„ ํƒํ•œ ๋’ค OK๋ฅผ ๋ˆ„๋ฅธ๋‹ค

์ƒˆ๋กœ์šด ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •

 

ํ•˜๋‹จ์— pipeline syntax๋ฅผ ํด๋ฆญํ•ด์ค€๋‹ค.

 

Git Clone ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑํ•˜๊ธฐ

Steps์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ธฐ์ž…ํ•˜๊ณ  Generate Pipeline Script๋ฅผ ํด๋ฆญํ•˜๋ฉด ์ž๋™์œผ๋กœ ํŒŒ์ดํ”„๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•ด์ค€๋‹ค!

  • Branch: trigger๊ฐ€ ๋˜๋Š” ๋ธŒ๋žœ์น˜๋Š” ๋งˆ์Œ๋Œ€๋กœ ์ž‘์„ฑํ•œ๋‹ค (ex main, develop .. ๋“ฑ๋“ฑ)
    • ์ด ๋•Œ ๋ธŒ๋žœ์น˜๋Š” ๊ผญ ์กด์žฌํ•˜๋Š” ๋ธŒ๋žœ์น˜๋กœ ์„ค์ •ํ•˜์ž!
  • Credential์€ ์•„๊นŒ ๋“ฑ๋กํ•œ jenkins-github-username-password์œผ๋กœ ์„ค์ •ํ•ด์ค€๋‹ค.

 

Script๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŒŒ์ดํ”„๋ผ์ธ์„ ์ž‘์„ฑํ•ด์ฃผ์ž.

pipeline {
    agent any
    
    stages {
        stage('Git Clone') {
            steps {
                git branch: 'develop', 
                credentialsId: 'jenkins-github-username-password', 
                url: 'https://github.com/re-Mine/reMine-Server'
            }
        }
    }
}

 

์ €์žฅ์„ ๋ˆ„๋ฅธ ๋’ค, ์ง€๊ธˆ ๋นŒ๋“œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํด๋ก  ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด๋ณด์ž

 

Clone ํ™•์ธํ•˜๊ธฐ

์•„๋ž˜ ๋ช…๋ น์–ด๋“ค์„ ์ž‘์„ฑํ•ด์ฃผ์ž.

  • ๋„์ปค ์ปจํ…Œ์ด๋„ˆ์—” vim๋„ ์•ˆ๊น”๋ ค์žˆ์œผ๋ฏ€๋กœ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.
    → vim์„ ์„ค์น˜ํ•  ๋•Œ ๊ถŒํ•œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด? ์ด ๊ธ€ ํ•˜๋‹จ์— ์žˆ๋Š” ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… ๋ถ€๋ถ„์˜ '๋„์ปค ์ปจํ…Œ์ด๋„ˆ ๊ถŒํ•œ ์—๋Ÿฌ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ' ๋ถ€๋ถ„์„ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค.
$ apt-get update
$ apt-get upgrade
$ apt-get install vim

 

jenkins ์ด๋ฏธ์ง€์— ์ ‘์†ํ•œ ๋’ค,
/var/jenkins_home/workspace/spring-pipeline(ํŒŒ์ดํ”„๋ผ์ธ ์ด๋ฆ„) ์œผ๋กœ ์ด๋™ํ•˜๋ฉด ํด๋ก ๋œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ docker exec -it jenkins /bin/bash
$ cd /var/jenkins_home/workspace/spring-pipeline
$ ls -al

ํด๋ก ์ด ์ž˜ ๋˜์–ด์žˆ๋‹ค.


[JENKINS] ๋ฏผ๊ฐ ์ •๋ณด ์ถ”๊ฐ€

๋ณดํ†ต ๊ด€๋ฆฌํ•˜๋Š” application-000.properties ํŒŒ์ผ๋“ค์€ ๋ฏผ๊ฐ์ •๋ณด๋ผ์„œ .gitignore ๋กœ github์— ๊ณต๊ฐœ์ ์œผ๋กœ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋ฐ,

์ด๋Ÿฐ ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ํŒŒ์ผ์„ ์„ค์ •ํ•˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•(ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋“ฑ..)์ด ์žˆ์ง€๋งŒ Jenkins ์ž์ฒด์—์„œ ์ œ๊ณตํ•˜๋Š” Credential ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด๋ณด์•˜๋‹ค!

 

Jenkins ๊ด€๋ฆฌ > Credentials > ์•„๋ž˜ ์‚ฌ์ง„์ฒ˜๋Ÿผ (global) ๋ถ€๋ถ„ ํด๋ฆญํ•ด์„œ Global credentials (unrestricted) ๋“ค์–ด๊ฐ€๊ธฐ

 

Credential ์ถ”๊ฐ€

Add Credentials ํด๋ฆญํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ธํŒ…ํ•ด์ฃผ๊ธฐ

  • Kind: Secret file
  • Scope: Global (default)
  • File ์„ ํƒํ•˜๊ธฐ
  • ID: ๋งˆ์Œ๋Œ€๋กœ - ํŒŒ์ดํ”„๋ผ์ธ ์ž‘์„ฑํ•  ๋•Œ ์“ฐ์ด๋ฏ€๋กœ ์‹๋ณ„๊ฐ€๋Šฅํ•œ ์•„์ด๋””๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

ํŒŒ์ดํ”„๋ผ์ธ ์ž‘์„ฑ

ํŒŒ์ดํ”„๋ผ์ธ > Configuration(๊ตฌ์„ฑ) ์— ๋“ค์–ด์˜ค๋ฉด ํŒŒ์ดํ”„๋ผ์ธ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

  • git clone ์ดํ›„ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ์ž‘์„ฑํ•ด์ค์‹œ๋‹ค
  • ๊ฒฝ๋กœ๋‚˜, ํŒŒ์ผ๋ช…์€ ๊ฐ ํ”„๋กœ์ ํŠธ ์„ธํŒ…์— ๋งž๊ฒŒ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค
stage('Copy application-SECRET.properties') {
	steps {
    	withCredentials([file(credentialsId: 'application-secret', variable: 'dbConfigFile')]) {
        	script {
            	sh 'cp $dbConfigFile src/main/resources/application-SECRET.properties'
            }
		}
	}
}

์ฐธ๊ณ ์šฉ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ

 

Credential ํŒŒ์ผ ์ถ”๊ฐ€ ํ™•์ธ

์ง€๊ธˆ ๋นŒ๋“œ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์„œ ๋นŒ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , docker ์— ์ ‘์†ํ•˜์—ฌ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค

$ cd /var/jenkins_home/workspace/spring-pipeline(ํŒŒ์ดํ”„๋ผ์ธ ์ด๋ฆ„)/src/main/resources
$ ls -al

ํ‚ค ํŒŒ์ผ ์ •์ƒ ์ด๋™ ํ™•์ธ


[JENKINS] Build Pipeline ์ž‘์„ฑ

gradle ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ ์ž‘์„ฑํ•˜๊ธฐ

์ฐธ๊ณ ๋กœ ์—ฌ๊ธฐ์„œ ./gradlew clean build๋ผ๊ณ  ์ž‘์„ฑํ•˜๋ฉด ๊ต‰์žฅํžˆ ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค (11๋ถ„ ๊ฑธ๋ฆผ;;)

  • ์ด์ „ ์บ์‹œ ์‚ญ์ œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งŒ clean์„ ์ž‘์„ฑํ•˜๋„๋ก ํ•˜์ž
stage('build with gradle') {        
		steps {
		    sh 'chmod +x gradlew'
		    sh './gradlew build'
		}
}

 

๋นŒ๋“œ ํ™•์ธํ•˜๊ธฐ

build๋œ jar ํŒŒ์ผ์€ build/libs ํŒŒ์ผ์— ์ €์žฅ๋œ๋‹ค!

ํ•ด๋‹น ์œ„์น˜๋กœ ์ด๋™ํ•ด์„œ jar ํŒŒ์ผ์ด ๋ฌด์‚ฌํžˆ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

  • ๋งŒ์•ฝ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๋ฉด, ํ‚ค ํŒŒ์ผ์ด ๋น ์ง„ ๊ฑด ์—†๋Š”์ง€ ์ž˜ ํ™•์ธํ•ด๋ณด์ž.
  • ์—๋Ÿฌ๋Š” Build History์˜ #n ๋ถ€๋ถ„์„ ํด๋ฆญํ•œ ๋’ค Console output ์„ ๋ณด๋ฉด ์‹ค์‹œ๊ฐ„ ์ฝ˜์†” ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

[GCP] Spring Boot Server์˜ SSH KEY ์„ค์ •

์ด์ œ Jenkins Server์—์„œ Spring Boot Server๋กœ SSH ์ ‘์†์ด ๊ฐ€๋Šฅํ•˜๋„๋ก Spring Boot Server์— ํ‚ค๋ฅผ ์„ค์ •ํ•ด์ฃผ์ž.

 

์œˆ๋„์šฐ๋Š” puttygen์„ ์ด์šฉํ•˜์—ฌ ppk, pem ํ‚ค๋ฅผ ๋งŒ๋“ค์–ด์ค„ ์ˆ˜ ์žˆ๋Š”๋ฐ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ๊ตฌ๊ธ€๋งํ•˜๋ฉด ๋งŽ๊ณ  ๊ต‰์žฅํžˆ ์‰ฌ์šฐ๋‹ˆ ํŒจ์Šคํ•˜๊ฒ ๋‹ค! ์ด ๋•Œ ๋‚˜์˜ค๋Š” ssh-rsa๋กœ ์‹œ์ž‘ํ•˜๋Š” public key ๋ถ€๋ถ„์€ ์ž˜ ๋ณต์‚ฌํ•ด๋‘๋ฉด ๋œ๋‹ค.

 

SSH ๊ณต๊ฐœ ํ‚ค ์ถ”๊ฐ€

spring boot server ์ธ์Šคํ„ด์Šค์— ๋“ค์–ด๊ฐ€์„œ ์ˆ˜์ • ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด์ž

 

SSH ํ‚ค ๋ถ€๋ถ„์—์„œ ํ•ญ๋ชฉ ์ถ”๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ์„œ ์•„๊นŒ ๋ณต์‚ฌํ•ด๋‘” ssh-rsa๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ณต๊ฐœํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€ ๋’ค, ์ €์žฅํ•œ๋‹ค.

 

๊ณต๊ฐœํ‚ค ํ™•์ธํ•˜๊ธฐ

Spring Boot ์„œ๋ฒ„์— SSH ์ ‘์†์„ ํ•ด๋ณด์ž

  • ์ฐธ๊ณ ๋กœ, ์ฝ˜์†”์—์„œ SSH ์˜† โ–ผ ๋ฒ„ํŠผ, ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์—์„œ ์—ด๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ฐ”๋กœ ๋ธŒ๋ผ์šฐ์ €์— ์ ‘์†์ด ๊ฐ€๋Šฅํ•˜๋‹ค

 

.ssh ํด๋”๋กœ ์ด๋™ํ•˜์—ฌ authorized_keys ํŒŒ์ผ์„ ํ™•์ธํ•˜์—ฌ ๋ฐฉ๊ธˆ ์ถ”๊ฐ€ํ•œ SSH ๊ณต๊ฐœํ‚ค๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค

$ cd .ssh
$ cat authorized_keys

 

๋งŒ์•ฝ ์ด ๋•Œ ๊ณต๊ฐœํ‚ค๊ฐ€ ์—†๋‹ค๋ฉด vim ํŽธ์ง‘๊ธฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ˆ˜๋™์œผ๋กœ ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

$ vim authorized_keys

[JENKINS] SSH Credential ์„ค์ • ๋ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜

SSH Credential ์„ค์ •

์•„๊นŒ์™€ ๋™์ผํ•˜๊ฒŒ Credential ์„ ์ถ”๊ฐ€ํ•˜์ž

Jenkins ๊ด€๋ฆฌ > Credentials > ์•„๋ž˜ ์‚ฌ์ง„์ฒ˜๋Ÿผ (global) ๋ถ€๋ถ„ ํด๋ฆญํ•ด์„œ Global credentials (unrestricted)์— ๋“ค์–ด๊ฐ€์ž.

 

Add Credentials ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด Spring Server์˜ Compute Engine์— ์ ‘๊ทผํ•˜๋Š” pemํ‚ค๋ฅผ ๋“ฑ๋กํ•ด์ฃผ์ž

  • ์ฐธ๊ณ ๋กœ pemํ‚ค๋Š” ----BEGIN RSA PRIVATE KEY---— ๋ถ€ํ„ฐ
     -----END RSA PRIVATE KEY----- ๊นŒ์ง€ ์ „๋ถ€ ๋ถ™์—ฌ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • Scope๋Š” Global, ID๋Š” jenkins-ssh-key๋กœ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.
  • Username์€ Ubuntu ์„œ๋ฒ„์˜ ๊ธฐ๋ณธ ์œ ์ €์ธ ubuntu๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.
    (๋”ฐ๋กœ ์„ค์ •์„ ํ–ˆ์„ ๊ฒฝ์šฐ ๋ณ€๊ฒฝํ•ด์„œ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.)

 

SSH Agent ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜

Jenkins ๊ด€๋ฆฌ > Plugins > Available plugins๋กœ ์ด๋™ํ•˜์—ฌ

SSH Agent ๋ฅผ ์„ค์น˜ํ•˜๊ณ  jenkins ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์žฌ์‹œ์ž‘ํ•ด์ฃผ์ž

$ docker ps    # ์‹คํ–‰์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ ํ™•์ธ
$ docker ps -a # ์ค‘์ง€๋œ ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ๋ชจ๋‘ ํ™•์ธ

$ docker restart jenkins

[VM] Spring Boot Server ์„ค์ •

(์ด ๋ถ€๋ถ„์€ ์  ํ‚จ์Šค ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ, ์Šคํ”„๋ง๋ถ€ํŠธ ์„œ๋ฒ„์˜ ์ธ์Šคํ„ด์Šค ์„ค์ •์ด๋‹ค.)

 

JDK ์„ค์น˜

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์ž‘์„ฑํ•˜์—ฌ jdk๋ฅผ ์„ค์น˜ํ•ด์ฃผ์ž.

์ฐธ๊ณ ๋กœ ํ•„์ž์˜ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” java 17์„ ์‚ฌ์šฉํ•˜์—ฌ 17 ๋ฒ„์ „์„ ์„ค์น˜ํ•ด์ฃผ์—ˆ๋Š”๋ฐ, ์ด๊ฑด ํ”„๋กœ์ ํŠธ์— ๋งž๊ฒŒ ์„ค์น˜ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

$ sudo apt update
$ sudo apt install openjdk-17-jdk
$ java --version # ์ž๋ฐ” ๋ฒ„์ „ ํ™•์ธ

 

Deploy shell ํŒŒ์ผ ์ž‘์„ฑ

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•  ์‰˜ ํŒŒ์ผ์„ ์ž‘์„ฑํ•ด์ฃผ์ž.

$ mkdir remine # application ์ด๋ฆ„์— ๋งž๊ฒŒ ์„ค์ •
$ vi deploy.sh

 

์•„๋ž˜ shell ํŒŒ์ผ์—์„œ

APP_NAME, APP_LOG, ERROR_LOG, DEPLOY_LOG ๋ณ€์ˆ˜ ๋ถ€๋ถ„์„ ๊ฐ์ž์˜ ํ”„๋กœ์ ํŠธ ์„ค์ •์— ๋งž๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋œ๋‹ค.

#!/bin/bash

APP_NAME=remine
APP_LOG=./remine/application.log
ERROR_LOG=./remine/error.log
DEPLOY_LOG=./remine/deploy.log

CURRENT_PID=$(pgrep -f $APP_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> ์ข…๋ฃŒํ•  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์—†์Šต๋‹ˆ๋‹ค." >> $DEPLOY_LOG
else
  echo "> kill -9 $CURRENT_PID" >> $DEPLOY_LOG
  kill -15 $CURRENT_PID
  sleep 5
fi

JAR_NAME=$(ls $APP_NAME | grep 'SNAPSHOT.jar' | tail -n 1)

chmod +x ./$APP_NAME/$JAR_NAME
nohup java -jar -Duser.timezone=Asia/Seoul ./$APP_NAME/$JAR_NAME --logging.level.org.hibernate.SQL=DEBUG > $APP_LOG 2>$ERROR_LOG &

[JENKINS] Deploy ํŒŒ์ดํ”„๋ผ์ธ ์ž‘์„ฑ

Deploy ํŒŒ์ดํ”„๋ผ์ธ ์ƒ์„ฑ

ํŒŒ์ดํ”„๋ผ์ธ > Cofiguration์— ๋“ค์–ด๊ฐ€์„œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€๋กœ ์ž‘์„ฑํ•ด์ค€๋‹ค

  • ๋ฌผ๋ก  ssh user name์ด ubuntu ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ubuntu๊ฐ€ ์•„๋‹ˆ๋ผ ์ƒˆ๋กญ๊ฒŒ ์ž‘์„ฑํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
stage('deploy') {
    steps {
        sshagent(credentials: ['jenkins-ssh-key']) {
            sh '''
                ssh -o StrictHostKeyChecking=no ubuntu@{spring server ip} uptime
                scp /var/jenkins_home/workspace/spring-pipeline/build/libs/*.jar ubuntu@{spring server ip}:/home/ubuntu/{application name}
                ssh -t ubuntu@{spring server ip} chmod +x ./deploy.sh
                ssh -t ubuntu@{spring server ip} ./deploy.sh
            '''
        }
    }
}

 

key file copy ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด?

์ด ๋•Œ key file copy์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ, Permission Denied ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

  • pipeline์—์„œ sudo๋ฅผ ๋ถ™์—ฌ๋„ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ,
    chmod ๋ช…๋ น์–ด๋ฅผ ํ™œ์šฉํ•ด ์ ‘๊ทผ๊ถŒํ•œ์„ ์ˆ˜์ •ํ•˜๋ฉด ๋œ๋‹ค.
stage('Copy application-SECRET.properties') {
	steps {
    	withCredentials([file(credentialsId: 'application-secret', variable: 'dbConfigFile')]) {
        	script {
            	sh 'cp $dbConfigFile src/main/resources/application-SECRET.properties'
            	sh 'chmod 644 src/main/resources/application-SECRET.properties'
            }
        }
	}
}

 

๊ฒฐ๊ณผ

{์Šคํ”„๋ง๋ถ€ํŠธ ์„œ๋ฒ„ public ipv4}:8080 ํฌํŠธ๋กœ ์ ‘์†ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ Whitelabel ํŽ˜์ด์ง€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด Spring Boot ์„œ๋ฒ„์—์„œ java ํŒŒ์ผ์ด ๋ฐฐํฌ๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž

$ ps -ef | grep java

์ด๋ ‡๊ฒŒ ํ”„๋กœ์„ธ์Šค๋ฅผ ํ™•์ธํ•˜๋ฉด ์„ฑ๊ณต


[JENKINS | GITHUB] Github Webhook ์„ค์ •ํ•˜๊ธฐ

Github ์›น ํ›… ์ถ”๊ฐ€ํ•˜๊ธฐ

Repository Settings > Webhooks > Add webhook ํด๋ฆญํ•˜๊ธฐ

  • Payload URL : Jenkins Server IP ์ฃผ์†Œ/github-webhook/
  • Content type: application/json

 

Pipeline Trigger ์„ค์ •

Configuration์— ๋“ค์–ด๊ฐ€์„œ Build Triggers GitHub hook trigger for GITScm polling ์ฒดํฌํ•˜๊ธฐ


ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

๋„์ปค ์ปจํ…Œ์ด๋„ˆ ๊ถŒํ•œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ

์ƒํ™ฉ

  1. apt-get update ๋ฅผ ์‹คํ–‰์‹œ, Permission denied ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•จ
  2. sudo apt-get update๋ฅผ ์‹คํ–‰ ์‹œ, sudo command not found ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•จ.

์ด ๋•Œ๋Š” sudo๋„ ๊น”์•„์ฃผ์–ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด๋ผ์„œ ์•„๋ž˜ ๋ช…๋ น์–ด์ฒ˜๋Ÿผ docker ๋ฅผ root๊ถŒํ•œ์œผ๋กœ ์ ‘์†ํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

$ docker exec -itu0 jenkins /bin/bash

 

๊ธฐ์กด์—” docker exec -it jenkins /bin/bash ๋กœ ์ ‘๊ทผํ–ˆ์œผ๋‚˜, -itu0 ๋กœ ์‹คํ–‰ํ•˜๋ฉด ๋ฃจํŠธ ๊ถŒํ•œ์œผ๋กœ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.


๋งˆ๋ฌด๋ฆฌ

๋ณด์™„ํ•  ์ 

์Šคํ”„๋ง docker-compose๋ฅผ ๋”ฐ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„์„œ ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋”ฐ๋กœ Docker๋ฅผ ์‚ฌ์šฉํ•˜์ง„ ์•Š์•˜๋Š”๋ฐ, (Jenkins ์ œ์™ธ)

๋‹ค์Œ๋ฒˆ์—” ๋„์ปค๋ฅผ ๋„์ž…ํ•ด์„œ ์ƒˆ๋กœ์šด ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ œ์ž‘ํ•ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค!


์ œ์ž‘ํ•˜๋ฉฐ ์ฐธ๊ณ ํ•œ ์‚ฌ์ดํŠธ

  1. ํ•„์ž๋„ ๋”ฑํžˆ ์•Œ๊ณ  ์‹ถ์ง€ ์•Š์•˜๋‹ค๐Ÿฅฒ [๋ณธ๋ฌธ์œผ๋กœ]

๋Œ“๊ธ€