๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Infra/Devops

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

by hyeinisfree 2022. 5. 2.

๐Ÿ‘ฉ‍๐Ÿ’ป CI/CD

1๏ธโƒฃ ๊ตฌ์„ฑ ์š”์†Œ

  • Jenkins Server : AWS EC2 Ubuntu 18.04
  • Spring Boot Server : AWS EC2 Ubuntu 18.04
  • Github Repository
  • Docker Hub Repository

2๏ธโƒฃ ์ง„ํ–‰ ์ˆœ์„œ

  1. Jenkins Server์— Docker ์„ค์น˜
  2. Jenkins Server์— Docker๋ฅผ ์ด์šฉํ•˜์—ฌ Jenkins ์‹คํ–‰
  3. Jenkins ์ ‘์†
  4. Jenkins์™€ Github ์—ฐ๋™
  5. Jenkins์™€ Docker Hub ์—ฐ๊ฒฐ
  6. Jenkins Server์™€ Spring Boot Server SSH ์—ฐ๊ฒฐ ์„ค์ •
  7. Jenkins์™€ Slack ์—ฐ๋™
  8. Jenkins Pipeline ๊ตฌ์„ฑ
  • Spring Boot Project Github Repository Clone
  • Gradle Build
  • Docker Build
  • Docker Push
  • Spring Boot Server SSH ์—ฐ๊ฒฐ
    • Docker Pull
    • Docker Run
  • Slack Notification
    ์‚ฌ์ „์— Jenkins Server, Spring Boot Server๋ฅผ ์œ„ํ•œ AWS EC2 ์ธ์Šคํ„ด์Šค 2๊ฐœ์™€ Spring Boot Project๊ฐ€ ์˜ฌ๋ผ๊ฐ€์žˆ๋Š” Github Repository์™€ Docker Hub Repository๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์„ค์ •์€ Jenkins Server์— ์ ‘์†ํ•˜์—ฌ ์ง„ํ–‰ํ•œ๋‹ค. Spring Boot Server๋Š” SSH ์—ฐ๊ฒฐ ์„ค์ • ์‹œ public key๋ฅผ ๋“ฑ๋กํ•  ๋•Œ๋งŒ ์ ‘์†ํ•œ๋‹ค.

 

๐Ÿ’ป Jenkins Server EC2

1๏ธโƒฃ EC2 ์ดˆ๊ธฐ ์„ค์ •

sudo apt update
sudo apt upgrade
sudo apt install build-essential

2๏ธโƒฃ Docker ์„ค์น˜

1. ๊ธฐ๋ณธ ์„ค์ •, ์‚ฌ์ „ ์„ค์น˜

$ sudo apt update
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common

2. ์ž๋™ ์„ค์น˜ ์Šคํฌ๋ฆฝํŠธ ํ™œ์šฉ

๋ฆฌ๋ˆ…์Šค ๋ฐฐํฌํŒ ์ข…๋ฅ˜๋ฅผ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜์—ฌ Docker ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์ฃผ๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ œ๊ณต

$ sudo wget -qO- https://get.docker.com/ | sh

3. Docker ์„œ๋น„์Šค ์‹คํ–‰ํ•˜๊ธฐ ๋ฐ ๋ถ€ํŒ… ์‹œ ์ž๋™ ์‹คํ–‰ ์„ค์ •

$ sudo systemctl start docker
$ sudo systemctl enable docker

4. Docker ๊ทธ๋ฃน์— ํ˜„์žฌ ๊ณ„์ • ์ถ”๊ฐ€

$ sudo usermod -aG docker ${USER}
$ sudo systemctl restart docker
  • sudo๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  docker๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • docker ๊ทธ๋ฃน์€ root ๊ถŒํ•œ๊ณผ ๋™์ผํ•˜๋ฏ€๋กœ ๊ผญ ํ•„์š”ํ•œ ๊ณ„์ •๋งŒ ํฌํ•จ
  • ํ˜„์žฌ ๊ณ„์ •์—์„œ ๋กœ๊ทธ์•„์›ƒํ•œ ๋’ค ๋‹ค์‹œ ๋กœ๊ทธ์ธ

5. Docker ์„ค์น˜ ํ™•์ธ

$ docker -v

3๏ธโƒฃ Docker๋กœ Jenkins ์„ค์น˜ํ•˜๊ธฐ

1. Jenkins ์ด๋ฏธ์ง€ ํŒŒ์ผ ๋‚ด๋ ค๋ฐ›๊ธฐ(lts ๋ฒ„์ „)

$ docker pull jenkins/jenkins:lts

2. ๋‚ด๋ ค๋ฐ›์•„์ง„ ์ด๋ฏธ์ง€ ํ™•์ธ

$ docker images

3. Jenkins ์ด๋ฏธ์ง€๋ฅผ Container๋กœ ์‹คํ–‰

$ docker run -d -p 8080:8080 -p 50000:50000 -v /jenkins:/var/jenkins -v /home/ubuntu/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock --name jenkins -u root jenkins/jenkins:lts

4. ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋Š” Container ํ™•์ธ

$ docker ps

โœ… EC2 ํ”„๋ฆฌํ‹ฐ์–ด์—์„œ Jenkins๊ฐ€ ์ž๊พธ ์ฃฝ์–ด์š”..๐Ÿคฆ‍โ™€๏ธ

ํ”„๋ฆฌํ‹ฐ์–ด EC2๋Š” ๋„ˆ๋ฌด ์ž‘๊ณ  ์†Œ์ค‘ํ•ฉ๋‹ˆ๋‹ค. Jenkins๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ๋นŒ๋“œํ•˜๋ฉด ๋ฉˆ์ถฐ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. AWS CloudWatch๋กœ ์‚ฌ์šฉ๋ฅ ์„ ๋ณด๋ฉด ์•„์ฃผ ๊ทธ๋ž˜ํ”„๋ฅผ ๋›ฐ์ณ ๋‚˜๊ฐ‘๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์‚ฌ์ „์— swap ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ด์„œ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•œ๋‹ค.

ํ”„๋ฆฌํ‹ฐ์–ด EC2 ๊ธฐ๋ณธ RAM์ด 1GB์ด๋‹ˆ, 2GB(128MB * 16) swap ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

$ 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
/swapfile swap swap defaults 0 0

 

๐Ÿคต‍โ™‚๏ธ Jenkins

1๏ธโƒฃ Jenkins ์ ‘์†

1. ๋ธŒ๋ผ์šฐ์ €์—์„œ [EC2 ์ธ์Šคํ„ด์Šค URL]:8080์œผ๋กœ ์ ‘์†

2. ์•”ํ˜ธ ์ž…๋ ฅ

/var/lib/jenkins/secrets/initialAdminPassword๋ฅผ ํ™•์ธํ•ด์•ผ ํ•˜๋Š”๋ฐ Jenkins Container์— ์ ‘์†ํ•˜์—ฌ ์–ป์–ด์˜ค๊ฑฐ๋‚˜ "docker logs jenkins" ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

// Container์— ์ ‘์†ํ•˜์ง€ ์•Š๊ณ  ํ™•์ธ
$ docker logs jenkins
// Container ์ ‘์†
$ docker exec -it jenkins bash

// ์•”ํ˜ธ ํŒŒ์ผ ํ™•์ธ
$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword

3. Install suggested plugins๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜

4. Jenkins ๊ณ„์ • ์ƒ์„ฑ

Jenkins ๋ฉ”์ธ ๋Œ€์‹œ๋ณด๋“œ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๋ฉด ์„ฑ๊ณต

2๏ธโƒฃ Jenkins, Github ์—ฐ๋™

1. ssh ํ‚ค ์ƒ์„ฑ

Jenkins Container๋ฅผ ์ƒ์„ฑํ•  ๋•Œ "/home/ubuntu/.ssh:/root/.ssh"๋กœ .ssh ๋””ํ…๋„๋ฆฌ๋ฅผ ๋งˆ์šดํŠธ ํ•ด๋†“์•˜๊ธฐ ๋•Œ๋ฌธ์— Container ๋ฐ–์—์„œ ssh ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๋ฉด Jenkins Container์™€ ์—ฐ๊ฒฐ๋œ๋‹ค.

// ๊ทธ๋ƒฅ ์ „๋ถ€ enter๋ฅผ ์ž…๋ ฅํ•ด default๋กœ ๋งŒ๋“ ๋‹ค.
$ ssh-keygen

EC2์— ์ ‘์†ํ•˜๋ฉด ๊ธฐ๋ณธ ์œ ์ €๊ฐ€ ubuntu์ด๊ธฐ ๋•Œ๋ฌธ์— /home/ubuntu/.ssh์— id_rsa์™€ id_rsa.pub์ด ์ƒ์„ฑ๋œ๋‹ค.

2. Github Deploy Key ๋“ฑ๋ก

๋งŒ๋“ค์–ด ๋†“์€ Github Repository > Settings > Deploy Keys > Add deploy key๋กœ ์ ‘์†ํ•œ๋‹ค.
Title์€ Jenkins๋กœ ์ง€์–ด ์ฃผ๊ณ (๋งˆ์Œ๋Œ€๋กœ ํ•ด๋„ ๋œ๋‹ค), Key ๋ถ€๋ถ„์— id_rsa.pub์— ๋“ค์–ด์žˆ๋Š” public key ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค. ์•„๋ž˜ ๋ช…๋ น์–ด๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.

$ cd /home/ubuntu/.ssh
$ cat id_rsa.pub

3. Jenkins Credentials ๋“ฑ๋ก

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > Manage Credentials > Credentials์— ์ ‘์†ํ•œ๋‹ค.
Store Jenkins์— Domain์ด (global)์ธ ํ™”์‚ดํ‘œ๋ฅผ ๋ˆŒ๋Ÿฌ Global credentials (unrestricted)๋กœ ์ด๋™ํ•œ๋‹ค.
์™ผ์ชฝ ๋ฉ”๋‰ด์˜ Add credentials๋ฅผ ๋ˆŒ๋Ÿฌ credentials๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

  • Kind
    SSH Username with private key
  • ID
    github -> ๋งˆ์Œ๋Œ€๋กœ ์ง€์–ด๋„ ๋œ๋‹ค. ๋‹ค๋งŒ Pipeline Script ์ž‘์„ฑ ์‹œ credentialsId๋กœ ์‚ฌ์šฉ๋˜๋‹ˆ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž.
  • Username
    root (default)
  • Private Key
    Enter directly ์ฒดํฌ -> private key ์ž…๋ ฅ
    ์—ฌ๊ธฐ์„œ private key๋Š” Jenkins Server์—์„œ ์ƒ์„ฑํ•œ id_rsa์ด๋‹ค. ์•„๋ž˜ ๋ช…๋ น์–ด๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.
    $ cd /home/ubuntu/.ssh
    $ cat id_rsa
    -----BEGIN OPENSSH PRIVATE KEY-----
    ...
    ... ์ด์™€ ๊ฐ™์€ ํ˜•ํƒœ์˜ key๊ฐ€ private key ์ž…๋‹ˆ๋‹ค ...
    ...
    -----END OPENSSH PRIVATE KEY-----
  • OK๋ฅผ ๋ˆŒ๋Ÿฌ ํ‚ค๋ฅผ ์ƒ์„ฑ

3๏ธโƒฃ Jenkins, Docker Hub ์—ฐ๊ฒฐ

1. Docker Plugin ์„ค์น˜

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ด€๋ฆฌ > ์„ค์น˜ ๊ฐ€๋Šฅ > Docker ๊ฒ€์ƒ‰ > Docker, Docker Pipeline ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜ ๋ฐ ์žฌ์‹คํ–‰

2. Docker Hub Credentials ๋“ฑ๋ก

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > Manage Credentials > Credentials์— ์ ‘์†ํ•œ๋‹ค.
Store Jenkins์— Domain์ด (global)์ธ ํ™”์‚ดํ‘œ๋ฅผ ๋ˆŒ๋Ÿฌ Global credentials (unrestricted)๋กœ ์ด๋™ํ•œ๋‹ค.
์™ผ์ชฝ ๋ฉ”๋‰ด์˜ Add credentials๋ฅผ ๋ˆŒ๋Ÿฌ credentials๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

  • Kind
    Username with password
  • Username
    ๋ณธ์ธ์˜ Docker Hub ID
  • Password
    ๋ณธ์ธ์˜ Docker Hub Password
  • ID
    docker-hub -> ๋งˆ์Œ๋Œ€๋กœ ์ง€์–ด๋„ ๋œ๋‹ค. ๋‹ค๋งŒ Pipeline Script ์ž‘์„ฑ ์‹œ credentialsId๋กœ ์‚ฌ์šฉ๋˜๋‹ˆ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž.
  • OK๋ฅผ ๋ˆŒ๋Ÿฌ ํ‚ค๋ฅผ ์ƒ์„ฑ

3. Jenkins Container ๋‚ด๋ถ€์— Docker ์„ค์น˜

Jenkins Pipeline์—์„œ Docker ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Jenkins Container ๋‚ด๋ถ€์— Docker๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.

  • Jenkins Container์— ์ ‘์†
$ docker exec -it jenkins bash
  • Docker ์„ค์น˜
$ docker run -d -p 8080:8080 -p 50000:50000 -v /jenkins:/var/jenkins -v /home/ubuntu/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock --name jenkins -u root jenkins/jenkins:lts

Jenkins Container๋ฅผ ๊ฐ€๋™ํ•  ๋•Œ ์œ„์— ์ฒ˜๋Ÿผ ์™ธ๋ถ€ Docker volume์„ ์—ฐ๊ฒฐํ•ด๋†“์•„์„œ ์•„๋ž˜์ฒ˜๋Ÿผ docker.sock ๊ถŒํ•œ ๋ณ€๊ฒฝ๋งŒ ํ•˜๋ฉด Container ๋‚ด๋ถ€์—์„œ docker ๋ช…๋ น์–ด๊ฐ€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•˜๋Š”๋ฐ Pipeline Script๋ฅผ ์ž‘์„ฑํ•˜๊ณ  Buildํ–ˆ๋”๋‹ˆ docker ๋ช…๋ น์–ด ์‚ฌ์šฉ ๋ถ€๋ถ„์—์„œ command not found ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ๊ทธ๋ƒฅ Container ๋‚ด๋ถ€์—๋„ Docker๋ฅผ ์„ค์น˜ํ•˜์˜€๋‹ค.
Docker ์„ค์น˜ ๋ฐฉ๋ฒ•์€ ์œ„์—์„œ EC2 ๋‚ด์— Docker๋ฅผ ์„ค์น˜ํ•œ ๋ฐฉ๋ฒ•๊ณผ ๋™์ผํ•˜๋‹ค. ๋‹ค๋งŒ Container ๋‚ด๋ถ€์— sudo, vi, wget์ด ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š์•„ ์ฐจ๋ก€๋Œ€๋กœ ์„ค์น˜ ํ›„ Docker๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.

  • docker.sock ๊ถŒํ•œ ๋ณ€๊ฒฝ
    $ sudo chmod 666 /var/run/docker.sock

โœ… Jenkins Container ๋‚ด๋ถ€์— Docker๋ฅผ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•˜์—ฌ..

  1. Docker๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ๋Š” Jenkins ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•œ๋‹ค.
  2. docker exec ๋ช…๋ น์–ด๋กœ Jenkins Container ๋‚ด๋ถ€๋กœ ์ ‘์†ํ•œ ๋‹ค์Œ ๋‚ด๋ถ€์— Docker๋ฅผ ์„ค์น˜ํ•œ๋‹ค.
  3. docker ๊ฒฝ๋กœ์™€ docker.sock ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๋„์ปค ๋ณผ๋ฅจ์— ์ถ”๊ฐ€ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ Container ๋‚ด๋ถ€์— docker-cil๋Š” ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ธ€์„ ๋ณด์•˜๋‹ค.
  • ์‹คํ—˜ ํ•„์š”.

4๏ธโƒฃ Jenkins, Spring Boot Server SSH ์—ฐ๊ฒฐ

Jenkins๋กœ Gradle ๋นŒ๋“œํ•˜๊ณ  Dockerfile๋กœ ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•ด์„œ Docker Hub์— Pushํ•˜๊ณ  Spring Boot Server์—์„œ ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ Pullํ•ด์„œ ์‹คํ–‰ํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•˜๋‹ค. Jenkins Pipeline Script์—์„œ SSH๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Spring Boot Server์˜ ๋ช…๋ น์–ด ์‹คํ–‰์„ ํ•  ์ˆ˜ ์žˆ๋„๋กœ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

1. SSH Agent Plugin ์„ค์น˜

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ด€๋ฆฌ > ์„ค์น˜ ๊ฐ€๋Šฅ > SSH Agent ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์„ค์น˜ ๋ฐ ์žฌ์‹คํ–‰

2. Spring Boot Server ์ ‘์†

3. Spring Boot Server์˜ .ssh/authorized_keys ํŒŒ์ผ์— Jenkins Server์˜ public key๋ฅผ ์ถ”๊ฐ€

// Jenkins Server์—์„œ public key ํ™•์ธ
$ cat /home/ubuntu/.ssh/id_rsa.pub

// Spring Boot Server์— Jenkins Server public key ์ถ”๊ฐ€
$ vi /home/ubuntu/.ssh/authorized_keys

4. Jenkins Credentials ๋“ฑ๋ก

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > Manage Credentials > Credentials์— ์ ‘์†ํ•œ๋‹ค.
Store Jenkins์— Domain์ด (global)์ธ ํ™”์‚ดํ‘œ๋ฅผ ๋ˆŒ๋Ÿฌ Global credentials (unrestricted)๋กœ ์ด๋™ํ•œ๋‹ค.
์™ผ์ชฝ ๋ฉ”๋‰ด์˜ Add credentials๋ฅผ ๋ˆŒ๋Ÿฌ credentials๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

  • Kind
    SSH Username with private key
  • ID
    ssh -> ๋งˆ์Œ๋Œ€๋กœ ์ง€์–ด๋„ ๋œ๋‹ค. ๋‹ค๋งŒ Pipeline Script ์ž‘์„ฑ ์‹œ credentialsId๋กœ ์‚ฌ์šฉ๋˜๋‹ˆ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž.
  • Username
    root (default)
  • Private Key
    Enter directly ์ฒดํฌ -> private key ์ž…๋ ฅ
    ์—ฌ๊ธฐ์„œ private key๋Š” Jenkins Server์—์„œ ์ƒ์„ฑํ•œ id_rsa์ด๋‹ค. ์•„๋ž˜ ๋ช…๋ น์–ด๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.
    $ cd /home/ubuntu/.ssh
    $ cat id_rsa
    -----BEGIN OPENSSH PRIVATE KEY-----
    ...
    ... ์ด์™€ ๊ฐ™์€ ํ˜•ํƒœ์˜ key๊ฐ€ private key ์ž…๋‹ˆ๋‹ค ...
    ...
    -----END OPENSSH PRIVATE KEY-----
  • OK๋ฅผ ๋ˆŒ๋Ÿฌ ํ‚ค๋ฅผ ์ƒ์„ฑ
    ์‚ฌ์‹ค์ƒ ์œ„์˜ Jenkins์™€ Github ์—ฐ๋™ ์‹œ ์ƒ์„ฑํ•œ Jenkins Credentials๊ณผ ๋™์ผํ•˜๋‹ค. ํ•˜์ง€๋งŒ ID๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ ์ž SSH Credentials์„ ๋ณ„๋„๋กœ ์ƒ์„ฑํ•˜์˜€๋‹ค.

โœ… SSH Agent Plugin vs Publish Over SSH Plugin

์ „์— Jenkins Pipeline์ด ์•„๋‹Œ Freestyle project๋กœ ๋™์ผํ•œ CI/CD๋ฅผ ๊ตฌ์„ฑํ–ˆ์„ ๋•Œ๋Š” SSH Agent Plugin์ด ์•„๋‹Œ Publish Over SSH Plugin์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ Pipeline์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ Script์—์„œ SSH๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ SSH Agent Plugin์ด ๋” ๋งŽ์ด ๊ฒ€์ƒ‰๋˜๊ณ  ๊ฐ„๋‹จํ•ด๋ณด์—ฌ SSH Agent Plugin์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ํ•˜์ง€๋งŒ ๋‘ ๊ฐœ์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ฐจ์ด๋ฅผ ๋‚˜์ค‘์— ๋ณด๋‹ค ์ž์„ธํ•˜๊ฒŒ ์•Œ์•„๋ณด์•„์•ผ๊ฒ ๋‹ค.
Freestyle project๋กœ ๊ตฌ์„ฑํ–ˆ์„ ๋•Œ Publish Over SSH๋ฅผ ์„ค์น˜ ๋ฐ ์„ค์ •ํ–ˆ๋˜ ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

1. Publish Over SSH ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ด€๋ฆฌ > ์„ค์น˜ ๊ฐ€๋Šฅ > Publish Over SSH ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์„ค์น˜ ๋ฐ ์žฌ์‹คํ–‰

2. Publish Over SSH ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์ •

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > ์‹œ์Šคํ…œ ์„ค์ •์—์„œ Publish Over SSH ์˜์—ญ์˜ ๊ณ ๊ธ‰ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์„ค์ •

  • Path to key๋Š” private key์˜ ๊ฒฝ๋กœ๋ฅผ ์ž…๋ ฅํ•œ๋‹ค.
    ์ง€๊ธˆ๊นŒ์ง€๋Š” Jenkins Server์—์„œ jenkins ์ปจํ…Œ์ด๋„ˆ์— ์ ‘์†ํ•˜์ง€ ์•Š๊ณ  key๋“ค์„ ์ ‘๊ทผํ–ˆ์ง€๋งŒ jenkins๋Š” ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰์ค‘์ด๋‹ˆ๊นŒ Jenkins Server์˜ key ๊ฒฝ๋กœ๋ฅผ ๋ชจ๋ฅธ๋‹ค. ์ฆ‰ /home/ubuntu/.ssh/id_rsa๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋‹คํ–‰์ด Jenkins Server์—์„œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ /home/ubuntu/.ssh๋ฅผ /root/.ssh์™€ ์—ฐ๊ฒฐํ•ด ๋†“์•˜๊ธฐ ๋•Œ๋ฌธ์— /root/.ssh/id_rsa๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๋œ๋‹ค.
  • Key๋Š” private key ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
    id_rsa ํŒŒ์ผ ๋‚ด์šฉ์„ ๋ณต์‚ฌํ•ด์„œ ๋„ฃ์–ด์ค€๋‹ค.
  • Name์€ ์ ‘์†ํ•  ssh ์„œ๋ฒ„์˜ ์ด๋ฆ„์„ ์ž…๋ ฅํ•œ๋‹ค.(๋งˆ์Œ๋Œ€๋กœ ์ง€์–ด์ฃผ์„ธ์š”)
  • Hostname์€ ์ ‘์†ํ•  ์„œ๋ฒ„์˜ ์ฃผ์†Œ๋ฅผ ๋„ฃ์–ด์ฃผ์„ธ์š”.
    Spring Boot Server EC2 ์ธ์Šคํ„ด์Šค URL์„ ๋„ฃ์–ด์ค€๋‹ค.
  • Username์€ ์ ‘์†ํ•  ์œ ์ €๋ช…์„ ๋„ฃ์–ด์ค€๋‹ค.
    ๊ธฐ๋ณธ ์œ ์ €์ธ ubuntu๋ฅผ ์ž…๋ ฅํ•œ๋‹ค.
  • Test Configuration ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ •์ƒ์ ์œผ๋กœ ์—ฐ๊ฒฐ ๋˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

5๏ธโƒฃ Jenkins, Slack ์—ฐ๋™

1. Slack Notification Plugin ์„ค์น˜

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ด€๋ฆฌ > ์„ค์น˜ ๊ฐ€๋Šฅ > Slack Notification ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์„ค์น˜ ๋ฐ ์žฌ์‹คํ–‰

2. Slack์— Jenkins ์•ฑ ์ถ”๊ฐ€

Slack ์•ฑ์—์„œ ํ•˜๋‹จ์˜ ์•ฑ ์ถ”๊ฐ€ > jenkins ๊ฒ€์ƒ‰ > jenkins ์„ ํƒ ํ›„ ์—ฐ๋™์„ ํ•˜๋ฉด ์—ฐ๋™ ๊ฐ€์ด๋“œ ์›นํŽ˜์ด์ง€์— ํ•˜์œ„ ๋„๋ฉ”์ธ๊ณผ ํ† ํฐ์ด ์ถœ๋ ฅ๋œ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ Slack Credentials์„ ๋“ฑ๋กํ•œ๋‹ค.

3. Slack Credentials ๋“ฑ๋ก

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > Manage Credentials > Credentials์— ์ ‘์†ํ•œ๋‹ค.
Store Jenkins์— Domain์ด (global)์ธ ํ™”์‚ดํ‘œ๋ฅผ ๋ˆŒ๋Ÿฌ Global credentials (unrestricted)๋กœ ์ด๋™ํ•œ๋‹ค.
์™ผ์ชฝ ๋ฉ”๋‰ด์˜ Add credentials๋ฅผ ๋ˆŒ๋Ÿฌ credentials๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

  • Kind
    Secret text
  • Scope
    Global
  • Secret
    Slack์—์„œ jenkins ์•ฑ ์—ฐ๋™ ์‹œ ์ถœ๋ ฅ๋œ ํ† ํฐ
  • ID
    slack -> ๋งˆ์Œ๋Œ€๋กœ ์ง€์–ด๋„ ๋œ๋‹ค. ๋‹ค๋งŒ Pipeline Script ์ž‘์„ฑ ์‹œ credentialsId๋กœ ์‚ฌ์šฉ๋˜๋‹ˆ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž.
  • OK๋ฅผ ๋ˆŒ๋Ÿฌ ํ‚ค๋ฅผ ์ƒ์„ฑ

4. Slack ์„ค์ •

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > ์‹œ์Šคํ…œ ์„ค์ •์—์„œ Slack ์˜์—ญ์„ ์„ค์ •

  • Workspace
    Slack์—์„œ jenkins ์•ฑ ์—ฐ๋™ ์‹œ ์ถœ๋ ฅ๋œ ํ•˜์œ„ ๋„๋ฉ”์ธ
  • Credential
    ์œ„์—์„œ ๋“ฑ๋กํ•œ Slack Credential
  • Default channel / member id
    Jenkins ์•Œ๋ฆผ์ด ์ „์†ก๋˜๊ธธ ์›ํ•˜๋Š” ์ฑ„๋„๋ช…

 

๐Ÿฆธ‍โ™‚๏ธ Jenkins Pipeline

์ด์ œ ๋“œ๋””์–ด Pipeline์„ ๊ตฌ์„ฑํ•  ์ค€๋น„๊ฐ€ ๋๋‹ค! Jenkins ๋Œ€์‹œ๋ณด๋“œ > ์ƒˆ๋กœ์šด Item์—์„œ item name์„ ์ž…๋ ฅํ•˜๊ณ  Pipeline์„ ์„ ํƒ, OK ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค.

1๏ธโƒฃ Jenkins, Github Webhook ์—ฐ๋™

Github Repository์— push event๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ž๋™์œผ๋กœ Build๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด Pipeline๊ณผ Github Webhook์„ ์—ฐ๋™ํ•ด์•ผ ํ•œ๋‹ค.

1. Github Integration Plugin ์„ค์น˜

Jenkins ๋Œ€์‹œ๋ณด๋“œ > Jenkins ๊ด€๋ฆฌ > ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ด€๋ฆฌ > ์„ค์น˜ ๊ฐ€๋Šฅ > Github Integration ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์„ค์น˜ ๋ฐ ์žฌ์‹คํ–‰

2. Jenkins Pipeline ์„ค์ •

  • Github project ์„ค์ •
    Pipeline ๊ตฌ์„ฑ ํ™”๋ฉด > General ์˜์—ญ์—์„œ Github project๋ฅผ ์„ ํƒํ•œ๋‹ค. Project url์— ๋ณธ์ธ์˜ Github Repository Url์„ ์ž…๋ ฅํ•œ๋‹ค. ์ด ๋•Œ Repository Url์€ Clone ์‹œ ์‚ฌ์šฉํ•˜๋Š” HTTPS Url(.git์œผ๋กœ ๋๋‚จ)์„ ์ž…๋ ฅํ•œ๋‹ค.
  • Build Triggers ์„ค์ •
    Pipeline ๊ตฌ์„ฑ ํ™”๋ฉด > Build Triggers ์˜์—ญ์—์„œ GitHub hook trigger for GITScm polling์„ ์„ ํƒํ•œ๋‹ค.

3. Github Webhook ์ถ”๊ฐ€

Github Repository์—์„œ Settings > Webhooks > Add Webhook ์„ ๋ˆŒ๋Ÿฌ Webhook์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

  • Payload URL
    [Jenkins Server URL]:[Jenkins Server ํฌํŠธ]/github-webhook/
  • Content type
    application/x-www-form-urlencoded
  • ๋‚˜๋จธ์ง€๋Š” ๋ชจ๋‘ default ์„ค์ • ์œ ์ง€
    -Add webhook ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ Webhook์„ ์ถ”๊ฐ€ -> ๋ชฉ๋ก์—์„œ ๋…น์ƒ‰ ์ฒดํฌ ์•„์ด์ฝ˜์ด ์ƒ์„ฑ๋˜๋ฉด ์„ฑ๊ณต

2๏ธโƒฃ Pipeline Script ์ž‘์„ฑ

pipeline {
    agent any

    environment {
        imagename = "docker build๋กœ ๋งŒ๋“ค ์ด๋ฏธ์ง€ ์ด๋ฆ„"
        registryCredential = 'Docker Hub Credential ID'
        dockerImage = ''
    }

    stages {
        stage('Prepare') {
          steps {
            echo 'Clonning Repository'
            git url: 'Github Repository SSH Url(git@github.com๋กœ ์‹œ์ž‘)',
              branch: 'Clone ๋ฐ›์•„์˜ฌ Branch ์ด๋ฆ„',
              credentialsId: 'Github Credential ID -> github'
            }
            post {
             success { 
               echo 'Successfully Cloned Repository'
             }
           	 failure {
               error 'This pipeline stops here...'
             }
          }
        }

        stage('Bulid Gradle') {
          steps {
            echo 'Bulid Gradle'
            dir('.'){
                sh './gradlew clean build'
            }
          }
          post {
            failure {
              error 'This pipeline stops here...'
            }
          }
        }
        
        stage('Bulid Docker') {
          steps {
            echo 'Bulid Docker'
            script {
                dockerImage = docker.build imagename
            }
          }
          post {
            failure {
              error 'This pipeline stops here...'
            }
          }
        }

        stage('Push Docker') {
          steps {
            echo 'Push Docker'
            script {
                docker.withRegistry( '', registryCredential) {
                    dockerImage.push() 
                }
            }
          }
          post {
            failure {
              error 'This pipeline stops here...'
            }
          }
        }
        
        stage('Docker Run') {
            steps {
                echo 'Pull Docker Image & Docker Image Run'
                sshagent (credentials: ['SSH Credential ID -> ssh']) {
                    sh "ssh -o StrictHostKeyChecking=no [Spring Boot Server username]@[Spring Boot Server IP ์ฃผ์†Œ] 'docker pull [๋„์ปค์ด๋ฏธ์ง€ ์ด๋ฆ„]'" 
                    sh "ssh -o StrictHostKeyChecking=no [Spring Boot Server username]@[Spring Boot Server IP ์ฃผ์†Œ] 'docker ps -q --filter name=[์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„] | grep -q . && docker rm -f \$(docker ps -aq --filter name=[์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„])'"
                    sh "ssh -o StrictHostKeyChecking=no [Spring Boot Server username]@[Spring Boot Server IP ์ฃผ์†Œ] 'docker run -d --name [์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„] -p 8080:8080 [๋„์ปค์ด๋ฏธ์ง€ ์ด๋ฆ„]'"
                }
            }
        }
    }
    post {
        success {
            slackSend (channel: '#์ฑ„๋„๋ช…', color: '#00FF00', message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
        failure {
            slackSend (channel: '#์ฑ„๋„๋ช…', color: '#FF0000', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
        }
    }
}

โœ… ํŠน์ • branch push event์—๋งŒ ๋™์ž‘ํ•˜๋„๋ก ์„ค์ •..

์œ„์™€ ๊ฐ™์€ Github Webhook ์„ค์ •์œผ๋กœ๋Š” ๋ชจ๋“  branch์˜ push event์— Pipeline์ด ๋™์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค.
ํŠน์ • branch push event์—๋งŒ ๋™์ž‘ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ถ”๊ฐ€ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ์„ค์ •์ด ํ•„์š”ํ•œ ๊ฒƒ ๊ฐ™์€๋ฐ ๋” ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

โœ… application.yml๊ณผ ๊ฐ™์€ ๋น„๋ฐ€์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ๋Š” ํŒŒ์ผ์„ ์–ด๋–ป๊ฒŒ ๋„ฃ์–ด์ฃผ์ง€..

์—ฌ๋Ÿฌ ๋น„๋ฐ€์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ๋Š” application.yml์„ Github์— ์˜ฌ๋ผ๊ฐ€์ง€ ์•Š๋„๋ก ํ•ด๋†“์•˜๋Š”๋ฐ ํ•ด๋‹น application.yml์ด resources ํด๋”์— ์žˆ์ง€์•Š์•„ Gradle Build ์˜ค๋ฅ˜๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ–ˆ๋‹ค.
๋”ฐ๋กœ application.yml์„ ์„œ๋ฒ„์— ๋„ฃ์–ด์ฃผ๊ณ  ์ด๋ฅผ copyํ•˜์—ฌ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— ์œ„์น˜์‹œํ‚ค๊ฒŒ ํ•˜๊ณ  java -jar ์‹คํ–‰์‹œ -Dspring.config.location=/application.yml๊ณผ ๊ฐ™์ด ๋™์ž‘ํ•˜๋„๋ก Dockerfile์„ ๊ตฌ์„ฑํ•ด๋ณด๊ธฐ๋„ ํ–ˆ์œผ๋‚˜ test ์‹œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
๋”ฐ๋ผ์„œ ํ˜„์žฌ๋Š” ๊ทธ๋ƒฅ workspace ๋‚ด๋ถ€ resources ํด๋”์— ์ง์ ‘ ๋„ฃ์–ด๋†“์•˜๋‹ค. ํ•˜์ง€๋งŒ ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ด์•ผ๊ฒ ๋‹ค.

 

๐Ÿ’ฌ ํšŒ๊ณ 

์ด๋ฒˆ์ด Jenkins๋กœ CI/CD๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒŒ ์„ธ ๋ฒˆ์งธ์ธ๋ฐ ์ด์ œ์•ผ ์ข€ ์•Œ๊ฒ ๋‹ค.. Jenkins๋ž‘ ์ ์  ์ •๋“œ๋Š” ์ค‘.. ๋” ๊ณต๋ถ€ํ•ด์•ผ์ง€~

 

๐Ÿ“— ์ฐธ๊ณ 

๋Œ“๊ธ€