용로그
article thumbnail

이번에 프론트엔드와 협업을 하는 미션을 했다. 백엔드와 프론트엔드 모두 이전에 한 번씩 했던 미션 + @의 기능을 만들어 배포까지 하는 미션이다.

 

백엔드가 할 태스크는 CORS 해결, SSL 배포, + @ 기능 정도인 것 같았다. 백엔드보다 프론트엔드가 할 일이 비교적 많아 보였다. 기능에 따른 UI도 만들고 기존에 있던 것도 바꾸고 해야 할 테니 말이다. 특히 상태관리가 힘들다고 들었다.

 

그래서 백엔드 페어인 에단한테 간단한 CI/CD 파이프라인을 구축해보는게 어떻겠느냐는 제안을 했다. 그리고 너무 고맙게도 흔쾌히 받아주었다.

 

파이프라인을 구축한게 무언가를 더 해보자는 취지도 있지만, 무엇보다 AWS 인스턴스 자체가 한정된 네트워크로만 들어갈 수 있어서 외부에서 개발할 때는 어떠한 기능을 추가해도 인스턴스 안으로 접속해서 수동 배포를 할 수 없었다.

 

그래서 이번 글에서는 우리가 CI/CD 파이프라인을 구축한 과정과 그 과정에서 만난 문제들을 써보려고 한다. 참고로 지금 보면 늙고 병든 글이지만, 이전에 썼던 CI의 내용도 어느정도 첨부되었으니 참고해도 좋다.

 

버전업이 계속 진행됨에 따라 설치방법이 바뀌거나 호환이 안될 경우도 있으니 그런 부분들은 이 글에서 따로 기술하지는 않겠다. 또한 도메인과 SSL 적용은 다른 글에서 다루겠다.

 

EC2 인스턴스


파이프라인을 구축해보기 전에 당연하게도 배포할 서버가 필요하다. 라즈베리파이나 NAS도 괜찮지만, 제일 보편적인 AWS EC2로 진행해 보겠다.

 

인스턴스를 눌러서 오른쪽 위에 인스턴스 시작을 눌러주면 된다. 나는 t4g.micro를 사용하고 있다. 프리티어 인스턴스는 아니니 자신의 상황에 맞는 인스턴스를 선택해서 생성해 주면 된다.

 

젠킨스를 사용한다고 해서 서버 2개가 있어야 하는 것이 아니다. 아래에서 다루겠지만 도커를 사용하기 때문에 걱정하지 않아도 된다.

 

프리티어로 인스턴스를 만들고 싶다면 t2.micro, 리전에 따라 t3.micro 또는 23년 12월 31일까지 arm 기반의 t4g.small 인스턴스를 사용할 수 있다.

 

 

파이프라인 툴 고민하기


그 다음 인바운드 포트를 적절하게 설정해 주고 인스턴스 내부에 접속한다. 파이프라인을 구축할 때는 여러 가지 방법이 있다. Github Action, Jenkins, Travis CI, Circle CI 등등..

이 중에서 어느 툴을 도입할지 고민을 해봤는데 다음과 같은 결론을 내렸다. 깃허브 액션은 손쉽게 파이프라인을 구축할 수 있고 설정하는데 큰 리소스가 필요하지 않다. 하지만 젠킨스는 설정에 자신이 있고 완전한 제어와 운용 비용이 들지 않는다.

깃허브 액션도 어느 정도는 무료로 사용할 수 있지만 한계가 분명히 존재한다. 젠킨스는 매우 다양한 IDE를 지원하고 개발자가 직접 커스텀할 수 있는 옵션이 수두룩하다.

또한 매우 유명한 툴이고 그로 인해 많은 사람들이 사용하기 때문에 다양한 문서들이 많이 존재한다. 물론 지금 같이 작은 프로젝트에서는 깃허브 액션으로도 충분한 파이프라인을 구축할 수 있지만, 나는 젠킨스로 구축하고 인프라에 있어서 다양한 선택지를 가져가는 게 더 좋을 것 같아서 젠킨스로 결정했다.

스왑 메모리


 

젠킨스는 스프링 프레임워크 프로젝트 빌드시 약 2.n GB의 메인 메모리를 사용한다. 그렇기 때문에 아주 적절한 스왑 메모리를 할당해야 한다. 적게 할당하면 메인 메모리가 부족해서 빌드할 때 서버가 터지고, 너무 많이 할당하면 디스크가 부족해서 서버가 터진다.

 

나와 에단이 실험한 결과 인스턴스 볼륨 8GB 기준 스왑메모리는 1.5 GB를 할당하는 게 가장 적절한 것 같다. 빌드할 때 메인 메모리 700 MB + 스왑 메모리 1.4 GB를 사용하긴 하는데, 추후 테스트 코드 수가 늘어나는 것까지 고려해서 1.5GB를 할당하기로 했다.

 

위의 값은 환경과 프로젝트 규모에 따라 언제든지 달라질 수 있으니 자신의 스펙을 잘 판단해 보고 할당하자. (만약 이 글을 보는 장바구니 미션을 진행 중인 우테코 크루라면 1.5GB로 할당하는 게 제일 적당한 것 같네요.)

 

스왑메모리 할당법은 다른 글에서 자세하게 다루니 따로 찾아보는 걸 추천한다. GPT한테 물어봐도 잘 알려준다. 스왑 메모리 설정이 끝났다면 free -h 명령어로 다음과 같이 할당되었는지 확인한다.

 

 

나는 지금 도커에 젠킨스까지 띄워놔서 메모리가 넉넉하진 않지만, 처음 할당한다면 메인메모리가 거의 살아있을 것이다. 현재 사용가능한 메모리는 free 컬럼을 보면 된다.

 

그다음 jdk를 설치해주자. 자신의 프로젝트에 맞게 설치해주면 된다. 이것도 마찬가지로 많은 글에서 다루는 내용이니 스킵하겠다. 그 다음 도커를 설치해 준다. 

 

Docker


도커 설치법은 도커 공식 홈페이지에서 자세히 알려준다.(ubuntu 기준) 도커를 설치했으면 docker --version 명령어로 잘 받아졌는지 확인해 보자.

 

 

그다음 젠킨스 이미지를 다운 받아준다. 이미지를 다운 받고 난 뒤 다음 명령어로 확인해 보자.

 

 

이제 도커 컨테이너를 띄우자. 현재 ec2는 내 기준 유일하게 열려있는 포트들이 8080, 8081, 3306이다. 그렇기 때문에 8081로 띄웠다. 자신이 젠킨스를 띄우고 싶은 포트로 입맛대로 설정하자. 

 

 

컨테이너를 매번 이런 명령어로 띄우기 귀찮다면 docker-compose를 사용해도 좋다.

 

sudo docker run -d -p 8081:8080 --name jenkins -u root jenkins/jenkins:jdk11

 

이렇게 띄웠으면 도커로 젠킨스 설정은 끝났다.

 

Jenkins 플러그인 설치


자 이제 자신의 젠킨스에 접속해 보자. 젠킨스 접속은 {자신의 도메인}:{할당한 포트}로 접근하면 된다. 나 같은 경우 url은 다음과 같다.

 

http://dev-king-bebe.n-e.kr:8081/

 

 

이렇게 접근하면 처음에는 위와 같은 화면이 나올 것이다. /var/jenkins_home/secrets/~~~ 이런 경로에서 확인하라는데, 다 필요 없고 인스턴스에서 sudo docker logs {컨테이너 이름} 명령어를 입력하면 아래와 같이 나온다.

 

*************************************************************
*************************************************************
*************************************************************

Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

7260c28911c9475db6b1cd59a73bdef4 <- 이렇게 나오는게 Jenkins 패스워드임

This may also be found at: /var/jenkins_home/secrets/initialAdminPassword

*************************************************************
*************************************************************
*************************************************************

 

저걸 입력하면 된다. 처음 접속할 때 단 한 번만 하면 되니 걱정 안 해도 된다. 다음은 젠킨스 설치 창이 나올 텐데, Install Suggested plugins를 눌러 설치해 주면 된다.

 

꽤 오랜 시간 동안 설치를 진행하는데, 기다리는 동안 깃허브 웹 훅을 설정해 놓자.

 

CI 구축하기


Github Web Hook


레파지토리의 설정으로 들어가서 Webhooks 설정으로 들어간다.

 

 

그다음 Add webhook을 눌러 webhook을 추가해 주자.

 

다음은 Payload URL을 설정해줘야 한다. 도메인:도커로 띄운 젠킨스 포트/github-webhook/ 으로 설정해 주면 된다. 나 같은 경우는 아래와 같이 설정했다.

 

http://dev-king-bebe.n-e.kr:8081/github-webhook/

 

 

Content Typeapplication/json으로 바꾸고 웹 훅이 트리거를 발생시킬 주기를 해당 레파지토리에 push 했을 때로 설정한다. (Just the push event) 그리고 Add webhook을 눌러 추가해 주자.

 

그리고 나중에 사용할 액세스 토큰을 발급받아보자.

 

 

repoadmin:repo_hook를 셀렉트 해주고 생성하면 된다. 액세스 토큰 이름은 상관없다. 기간은 해당 액세스 토큰을 자신이 사용할 기간까지만 할당하는 게 좋다.

 

 

생성하고 나면 아래의 키를 볼 수 있는데 단 한 번밖에 볼 수 없는 키이므로 즉시 어딘가에 복사해 놓자.

 

 

Credentials 만들기


여기까지 끝났다면 플러그인 설치가 대부분 끝났을 텐데, 간단하게 회원가입 절차를 밟고 나면 다음과 같은 대시보드를 볼 수 있을 것이다.

CI를 Github Webhook으로 구축한다는 많은 포스트들을 보면 Github API Usage, JDK 설정 등을 따로 해주는데, 딱히 그렇지는 않다. Credentails 등록만으로도 Github Webhook 연동이 가능하다.

 

그러므로 Credentials를 먼저 등록해 보자. 

  • 대시보드 -> 젠킨스 관리 -> Credentials

 

 

여기서 (global)에 화살표를 누르면 Add credentials가 나오는데 해당 버튼을 눌러서 Credenials를 등록한다. 

 

 

 

Gradle 설정


그리고 Gradle 버전을 설정해줘야 한다. 

  • 대시보드 -> 젠킨스 관리 -> Tools

Gradle Installations 섹션이 있는데, Gradle Installations를 눌러서 안정된 버전을 선택해 주자. 글 쓰는 시점 기준(8.1 버전이 안정된 것 같아 선택했다.) 그리고 Save 하면 된다.

 

 

CI 스크립트 작성


이제 직접 파이프라인을 구축해 보자.

  • 대시보드 -> New Item -> Pipeline 선택 후 OK

 

 

  • 구성(Configuration) -> General

 

 

그리고 Github hook trigger for GITScm polling을 선택한 다음 아래의 Pipeline Syntax를 눌러서 자신의 설정에 맞는 스크립트 한 줄을 뽑아오자.

 

 

아래 사진처럼 Sample Step을 git으로 설정하고 Repository URL, Branch, Credentails를 다 적어주고 아래의 Generate Pipeline Script를 눌러주면 다음과 같이 스크립트 한 줄을 보여주는데 저걸 복사해 놓자.

 

 

그리고 다시 파이프라인 스크립트 창으로 돌아와서 아래와 같은 파이프라인 스크립트에 붙여 넣기 하면 된다.

 

pipeline {
    agent any
    tools {
        gradle 'gradle'
    }
    stages {
        stage('저장소 복제') {
            steps {
                {복사한 내용}
            }
        }
        stage('빌드') {
            steps {
                sh "./gradlew clean build"
            }
        }
    }
}

 

그리고 Github Web Hook을 설정한 레파지토리의 브랜치에 푸시를 하면 트리거가 발생하면서 자동으로 빌드되는 것을 볼 수 있다. 참고로 이전 빌드가 끝나기 전에 빌드가 한 번 더 실행되면 사양이 낮은 인스턴스의 경우 서버가 견디지 못하고 터져버릴 수 있으니 주의하자.

 

 

또한 굳이 깃허브에 안 들어가도 젠킨스에서 깃허브 훅 로그도 볼 수 있으니 참고하자.

 

 

CD 구축하기


SSH 플러그인 설치


원래 SSH 플러그인은 Publish Over SSH를 많이 사용하는데, 접속 불가 이슈가 있다고 한다. 그래서 SSH 플러그인은 SSH Agent를 사용한다.

 

  • 대시보드 -> 젠킨스 관리 -> 플러그인

 

검색에서 SSH Agent를 검색한 후 설치한다. 

 

EC2 정보 추가 설정


우리가 로컬에서 인스턴스로 ssh를 통해서 접속했던 것처럼 젠킨스에서도 ssh를 통해 접속해야 하기 때문에 관련 설정을 해주어야 한다. 

 

  • 대시보드 -> 젠킨스 관리 -> Credentials

 

 

 

Add Credentials를 눌러서 Kind를 SSH Username with private key를 눌러 usernameid, private key를 설정해 주자.

 

 

id는 마음대로 설정하고 username은 인스턴스 os의 이름, private key의 Key는 자신의 pem키 내용을 복사하면 된다. 

 

pem key 내용 보는 법
로컬에서 자신의 pem키 경로로 이동해서 cat {~~.pem} 명령어로 확인할 수 있다.

 

 

맨 위의 BEGIN RSA PRIVATE KEY부터 END RSA PRIVATE KEY까지 모두 복사해야 한다. %는 빼도 좋다. 모두 설정했다면 Create를 하고 다시 스크립트로 가보자.

 

CD 스크립트 작성


이제 마지막으로 다시 파이프라인 스크립트로 돌아와서 stage('배포') 부분을 자신의 설정에 맞게 추가해 주면 된다. 이제 모든 CI/CD 파이프라인이 완성되었다. 

 

pipeline {
    agent any
    tools {
        gradle 'gradle'
    }
    stages {
        stage('저장소 복제') {
            steps {
                git branch: 'step1', credentialsId: 'secretId', url: 'https://github.com/wonyongChoi05/jwp-shopping-order'
            }
        }
        stage('빌드') {
            steps {
                sh "./gradlew clean build"
            }
        }
        stage('배포') {
            steps {
                sshagent(credentials: ['instance-key']) {
                    sh '''
                        ssh -o StrictHostKeyChecking=no ubuntu@{인스턴스 private-ip} uptime
                        scp build/libs/{프로젝트 jar 이름} ubuntu@{인스턴스 private-ip}:/home/ubuntu
                        ssh -t ubuntu@{인스턴스 private-ip} ./{배포 스크립트 이름}
                    '''
                }
            }
        }
    }
}

 

아래와 같은 화면까지 나온다면 성공이다. 물론 배포 스크립트 권한이나 데이터베이스 접속이 안되거나 다른 자잘한 에러들 때문에 배포가 끝났다고 떠도 정상적으로 배포된 것이 아닐 수 있으니 배포 URL로 접근이 안된다면 로그를 확인해 보자.

 

 

완성된 인프라 파이프라인을 보면 다음과 같은 구조로 만들어졌다. 기회가 된다면 조금만 더 고도화해서 무중단 배포와 데이터베이스 Master Slave 아키텍쳐를 도입해 보면 좋겠다.

 

profile

용로그

@용로그

벨덩보단 용덩 github.com/wonyongChoi05