이전 포스팅까지, AWS 구축은 해놓았다.
https://velog.io/@dlsrjsdl6505/%ED%98%BC%EC%9E%90-%ED%95%98%EB%8A%94-Spring-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-9-AWS-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95
이제 결전의 자동빌드테스트배포..만 남았다.
CD/CD에 대해 잘 모르신다면, 다음 글을 읽으면 이해가 쉬울것 같다.
CI/CD란?
https://www.redhat.com/ko/topics/devops/what-is-ci-cd
위의 CI/CD를 실행할수 있게 하는 도구중 하나가 Jenkins 이고,
오늘은 Jenkins를 사용해서 CI/CD를 실행하려고 한다.
오늘의 목표는 아래의 사진과 같다.
설명은 다음과 같다.
두개의 EC2 서버를 이용하여,
빌드 서버에서는 빌드를 거쳐 jar파일로 만든 후,
Docker image로 빌드한 후 도커 허브로 Push까지 한다.
그다음 배포 서버는 해당 이미지를 Pull 해온 후
이미지를 실행시켜 내가 작성한 소스코드를 실행한다.
그다음, 젠킨스의 웹훅을 사용하여
내 로컬에서의 소스코드 변경 후 깃허브에 Push 하면
자동으로 위의 빌드와 배포까지 일어나도록 만들 예정이다.
구축서버 :Jenkins + Docker
배포서버 : Docker (구축서버->도커허브에서 가져온 이미지 실행 용도)
오늘 글을 잘 이해하기 위해서는,
쉘 스크립트, SSH KEY 등 의 내용때문에
구축서버와 배포서버의 역할을 잘 이해하고 구분하는것이 중요하다.
패키지 업데이트를 위해
$ wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list
$ sudo apt update
위의 명령어를 실행시키면,
W: GPG error: https://pkg.jenkins.io/debian-stable binary/ Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 여기요여기16자리키
E: The repository 'https://pkg.jenkins.io/debian-stable binary/ Release' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details.
위의 에러가 나오는데,
해당 에러는 위 코드의 여기요여기16자리키
를 가지고 아래 명령어를 실행한 후 다시 실행해주면 된다.
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 여기요여기16자리키
$ sudo apt install jenkins
위의 명령어를 작성 후 상태를 보기 위해
$ sudo systemctl status jenkins
명령어를 작성하면, 아래와 같이 잘 실행된다는 설명이 작성된다. (running)
ubuntu@ip-172-31-33-62:~$ sudo systemctl status jenkins
● jenkins.service - Jenkins Continuous Integration Server
Loaded: loaded (/lib/systemd/system/jenkins.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2023-07-23 07:46:19 UTC; 45s ago
Main PID: 30270 (java)
Tasks: 42 (limit: 1141)
Memory: 316.4M
CGroup: /system.slice/jenkins.service
└─30270 /usr/bin/java -Djava.awt.headless=true -jar /usr/share/java/jenkins.war --webroot=/var/cache/jenkins/war --httpPort=8080
Jul 23 07:45:44 ip-172-31-33-62 jenkins[30270]: db6a1308a3544d0f905664fe4a83a1cb
Jul 23 07:45:44 ip-172-31-33-62 jenkins[30270]: This may also be found at: /var/lib/jenkins/secrets/initialAdminPassword
Jul 23 07:45:44 ip-172-31-33-62 jenkins[30270]: *************************************************************
Jul 23 07:45:44 ip-172-31-33-62 jenkins[30270]: *************************************************************
Jul 23 07:45:44 ip-172-31-33-62 jenkins[30270]: *************************************************************
Jul 23 07:46:19 ip-172-31-33-62 jenkins[30270]: 2023-07-23 07:46:19.457+0000 [id=29] INFO jenkins.InitReactorRunner$1#onAttained: Completed ini>
Jul 23 07:46:19 ip-172-31-33-62 jenkins[30270]: 2023-07-23 07:46:19.487+0000 [id=22] INFO hudson.lifecycle.Lifecycle#onReady: Jenkins is fully >
Jul 23 07:46:19 ip-172-31-33-62 systemd[1]: Started Jenkins Continuous Integration Server.
Jul 23 07:46:20 ip-172-31-33-62 jenkins[30270]: 2023-07-23 07:46:20.344+0000 [id=44] INFO h.m.DownloadService$Downloadable#load: Obtained the u>
Jul 23 07:46:20 ip-172-31-33-62 jenkins[30270]: 2023-07-23 07:46:20.345+0000 [id=44] INFO hudson.util.Retrier#start: Performed the action check
이제 http://${your-ec2-public-dns}:8080 으로 접속하면
아래와 같은 화면이 나오는데,
빨간 명령어를 실행하여 암호를 얻어오라는 내용이다.
해당 명령어를 실행시켜 Jenkins로그인페이지에 입력하면
다음 페이지로 넘어간다.
왼쪽의 install suggested plugins를 선택한다
위 화면을 기다린 후 계정을 생성한다.
계정을 생성하고 나면, 젠킨스 메인 페이지를 볼 수 있다.
즉 EC2 서버에서 Jenkins 설치를 완료했다.
해당 키는 젠킨스가 깃허브 프로젝트로 접근하기 위해서 생성한다.
(위의 Jenkins EC2 서버에서 수행할것)
$ sudo mkdir /var/lib/jenkins/.ssh
$ sudo ssh-keygen -t rsa -f /var/lib/jenkins/.ssh/키이름
여기까지 작성하면, Key가 생성된다.
해당 키에서 Public 키를 추출해 GitHub에 등록해준다.
퍼블릭 키는 방금 바로 위에서 생성해준 경로로 들어가서
cat 키이름.pub
을 통해 얻어낸다.
해당 키를
깃허브 해당 프로젝트 -> Settings -> Deploy Keys -> Add deploy Key 탭에서 추가해준다.
생성해준 키 중 퍼블릭 키는 GitHub에 등록했다.
프라이빗 키는
$ cat 키이름
을 통해 얻어주고, (경로는 방금과 같다.)
Jenkins에 등록해준다.
Jenkins 메인 -> Jenkins관리 -> Credentials
-> Credentials 아래 Domain이 (global) 인 화살표를 눌러 add credentials 을 눌러 크레덴셜을 추가한다.
위의 창에서
종류는 SSH Username with private key,
ID는 내가 읽고 구분 가능하게,
Enter directly를 체크하여
아까 프라이빗 키를 넣어준다.
그렇게 생성해주면 사진과 같이 등록이 완료된다.
구축서버에서 구축이 완료되면,
배포서버가 구축서버와의 의사소통으로 해당 파일에 접근할 수 있어야 한다.
그래서 구축서버가 키를 생성하고,
해당 키를 배포서버가 등록한다.
구축 (Jenkins)
$ sudo ssh-keygen -t rsa -C "키이름" -m PEM -P "" -f /var/lib/jenkins/.ssh/키이름
$ sudo cat /var/lib/jenkins/.ssh/키이름.pub
위 명령어를 사용해 키를 생성하고, 퍼블릭 키를 따서(위에 방법 존재) 아래로 간다.
배포 (진짜 어플 실행 서버)
$ vi .ssh/authorized_keys
위 명령어를 통해 키를 생성하고 붙여넣어준다.
(원격 서버에 SSH를 통한 배포를 위해 설치하는 플러그인)
아까 Jenkins 서버의 메인페이지에서
Jenkins 관리 -> 플러그인 관리 -> 설치가능 탭 -> Publish Over SSH 플러그인을 검색하여 설치하고 젠킨스를 재실행한다.
젠킨스 재실행은,
알아서 물어본다.
음.. 할거없는데 설치하고 재실행할까?
라고
설치 후 젠킨스 재실행까지 완료되었다면,
Jenkins 관리 -> 시스템 -> Publish Over SSH 영역 -> 고급 버튼을 눌러 확장
지금부터 집중해서 읽길 바란다.
우리는, 지금 Key를 두개 생성했다.
Jenkins 서버와 GitHub을 연결해줄 키 하나와, 배포를 위해 젠킨스와 또다른 EC2 서버를 연결해줄 키이다.
후자의 키를 사용해서 아래의 공란을 작성한다.
Path to Key : private Key 경로 + 키 이름
(ex - /var/lib/jenkins/.ssh/FromEC2ToEC2)
Key : 생성한 private Key 내용
Nmae : 접속 서버 이름
Hostname : 해당 키를 통해 접속할 인스턴스 주소 (배포 서버 주소)
Username : 접속할 유저명
(ex - ubuntu)
여기까지 작성 후
사진처럼 Test Configuration 버튼을 눌러 Success가 나오면 OK이다.
빌드 서버에서 만들어지는 도커 이미지를 저장할 리파지토리를 생성한다.
도커 허브에 들어가서
(https://hub.docker.com/)
사진의 Ropository 탭에서
Create Repository를 실행한다.
EC2에 도커가 설치되어있다는 가정 하에 설명하겠다.
설치되어있지 않다면?
(https://velog.io/@haeny01/AWS-EC2-X-Docker)
https://growingsaja.tistory.com/690 (su - jenkins 권한 실패 에러)
비밀번호 설정 후
$ sudo usermod -aG docker jenkins //도커그룹에 젠킨스를 추가
$ su - jenkins //우분투에서 젠킨스 계정으로 사용하겠다
//위 두줄은 추가, 아래는 확인
$ id -nG
//확인시
jenkins docker이 나와야 함.
jenkis 비밀번호 에러(https://stackoverflow.com/questions/25041125/default-jenkins-user-password)
비밀번호 에러 해결 후 jenkins is not in the sudoers file. This incident will be reported. 에러
(http://devstory.ibksplatform.com/2018/06/ubuntu-sudo.html)
https://jitolit.tistory.com/27 (vi 편집)
$ sudo chmod 666 /var/run/docker.sock
//그룹 내 다른 사용자(방금 추가해준 젠킨스)도 접근 가능하게 파일의 권한을 666으로 변경
젠킨스에서 도커허브에 빌드 된 이미지를 푸쉬할 수 있도록
젠킨스 유저로 들어가서 도커 로그인을 할 예정.
여기서 로그인시 입력하는 아이디와 암호는 위에서 가입한 도커허브의 유저네임(이메일 아이디 아니라, 깃허브같이 유저네임입니다!)와 암호를 입력하면 된다.
$ sudo su - jenkins
$ docker login
이제 거의 다 왔다.
지금까지 작성해준 내용을 토대로,
쉘 스크립트에 원하는 스크립트를 작성하면 된다.
젠킨스 메인화면에서 사진의 새로운 아이템을 눌러 생성한다.
item은 프리스타일 프로젝트로 선택한다.
위 화면에서 이어져서,
General 탭에서 GitHub project 체크박스를 체크 ->
소스 코드 관fl 탭 -> Git선택 -> RepositoryURL에 내가 연동하길 원하는 깃허브 리파지토리를 넣기.
-> Credential은 맨 처음에 만들어줬던 Github관련 Credential으로 설정한다
Build step 탭 ->
Add build step -> Execute shell 선택 -> 빌드를 위한 명령어를 입력
chmod +x gradlew //빌드해야하므로 빌드할 수 있게 해주는 명령어를 실행할 권한 주기
./gradlew clean build //빌드
Add build step -> Execute shell 선택 (아까와 같은 위치이다)
-> 도커 이미지 빌드/도커허브에 배포 명령어를 입력
(아래 코드처럼 . 을 꼭 입력해야한다.)
docker build -t [dockerHub UserName]/[dockerHub Repository]:[version] . //점 포함
docker push [dockerHub UserName]/[dockerHub Repository]:[version]
빌드 환경 탭 -> Send files or execute commands over SSH after the build runs 를 체크
아래에 Transfers 영역에서 오직 Exec command 에 작성해준다.
docker pull [dockerHub UserName]/[dockerHub Repository]:[version]
docker ps -q --filter name=[containerName] | grep -q . && docker rm -f $(docker ps -aq --filter name=[containerName])
docker run -d --name [containerName] -p 8085:8080 [dockerHub UserName]/[dockerHub Repository]:[version]
방금 도커 이미지를 실행하기 위해
도커컨테이너의 8080포트를 EC2 인스턴스의 8085포트에 연결해주었으니
[EC2인스턴스URL]:8085 로 접근하면 확인이 가능하다.
(빌드시, 도커 이미지 pull시 EC2가 멈춘다면, RAM을 늘려주어야 하므로 https://progdev.tistory.com/26 스왑메모리 참고)
해당 빌드 내용을 입력해준 후, Jenkins에서 빌드해보니 빌드가 완료되고, 도커 허브에 push까지 완료되는지 볼 수 있다.
이제 마지막 단계가 남았다.
깃허브 코드 변경이 일어나면
자동으로
위의 빌드 -> 도커허브 -> 배포까지 일어나게 해야 한다.
Jenkins 관리 -> 플러그인 관리 -> 설치 가능 탭 -> Github Integration 검색 -> 설치후 젠킨스 재시작
아이템구성 -> 구성 -> 빌드 유발 탭 -> GitHub hook trigger for GITScm polling 체크
원하는 깃허브 리파지토리 -> Settings -> Webhooks -> Add webhook 추가
Payload URL은 아래와 같은 형태로 입력한다.
http://[구축서버 인스턴스 URL]:[젠킨스 PORT]/github-webhook/
Content Type 은 application/json
여기까지 해주면 된다.
해당 설정을 해준 후,
소스코드를 깃허브로 push하면,
깃허브 웹훅에 의해
EC2 빌드서버에 있는 젠킨스에서 빌드가 일어나고,
빌드 서버의 젠킨스에서 도커 이미지를 생성하고 도커 허브에 푸쉬한다.
해당 이미지를 서버 EC2가 받고 실행한다.
위의 사진을 보면, 서버가 정상 작동하여 요청을 받고 저장(aws rds의 mysql)도 잘 되는 것을 알 수있다.
이렇게 마무리가 되었다... 너무 힘들었다 진짜..
젠킨스 빌드 과정에서 Dockerfile 만들떄가 오류가 정말 거의
30번정도 나와서, 구글링 + 도커 공식문서 + 챗 gpt를 통해 찾아보고 찾아보고 스택오버플로우에서 찾아보고 고생좀 했다.
이후 인스턴스를 종료했다.
프리티어 두개면 허용되는 시간을 오버해서 돈을 내야 하기 때문에 ㅎㅎ..
근데, 뿌듯하다