본문 바로가기

Language & Framework/Spring

Sonarqube와 Ansible을 곁들인 Jenkins CI/CD 구축하기

원하는 이미지가 없어서 직접 만들었다.. 컨테이너 분리 안한 게 불-편

 

(돈 없어서) 한동안 꺼놨던 나의 첫 프로젝트인 Devillage 서버를 재가동하는 김에, 기존에 사용하던 github action을 jenkins로 변경해보기로 했다.

근데 jenkins만 쓰면 딱히 바뀌는 것도 없고 재미 없잖아요.

 

그래서 Jenkins(Master node + Worker node)에 Sonarqube랑 Ansible을 끼얹은 CI/CD를 구축해볼 것이다.

 

Jenkins는 알겠는데 Sonarqube랑 Ansible은 또 뭔가요.

Sonarqube는 정적 분석 도구로 코드 품질, 보안 및 유지 관리 가능성을 분석하여 제공한다. 이를 통해 최소한의 코드 품질을 유지하여 기술 부채를 줄이는데 도움을 준다.

Ansible은 오케스트레이션 도구로 스크립트를 이용해 보다 확장성/재사용성을 용이하게 해준다.

사실 서버 하나 배포하는데 필요한 건 아니다. 그래도 배웠으면 써먹어봐야지.

 

최종 목표는 제일 위에 올린 이미지와 같다.

 

1. 개발자가 깃허브에 작업 내역을 푸시한다.

2. 젠킨스 마스터는 푸시한 데이터를 받아온다.

3. 워커 노드 중 여유로운 노드가 해당 작업을 받아서 빌드/테스트를 진행한다. (Continuous-Integration)

4. sonarqube가 코드 품질을 검사한다.

-> 정적 분석 결과에 따라 배포 여부를 결정한다.(Optional)

5. ansible에 빌드된 jar 파일과 yml을 delivery한다. (Continuous-Delivery)

-> jenkins의 credentials에 yml에 들어갈 값들을 미리 입력해두고 동적으로 생성(변경)한다.

6. ansible의 playbook script에 따라 이미지를 배포하고, 프로덕션 서버에서 이를 받아서 실행한다. (Continuous-Deployment)

 

프리티어로는 불가능하다.

도커 컨테이너를 3개 구동하고 Sonarqube까지 돌리는데 프리티어로 될리가 없다.

소나큐브를 실행하기 위한 최소 메모리가 2gb거든요.

그래서 t2.medium(4gib) 인스턴스를 사용해서 진행할 것이다.

 

 

배보다 배꼽이 더 큰 CI/CD다.

가보자~

 

ec2 인스턴스를 새로 추가했다면 기본적인 세팅을 먼저 끝내야 한다.

나는 이 순서대로 설치했다.

잘 모르겠으면 그냥 따라해도 되지만 스스로 어떻게 세팅했는지 모르면 나중에 고생한다.

// yum update
sudo yum update -y
sudo yum install docker -y

// docker start
sudo systemctl start docker.service

// setting docker permissions (sudo 쓰기 귀찮아서)
sudo groupadd docker
sudo gpasswd -a $USER docker
newgrp docker

// run jenkins master
docker run --name jenkins-master -p 8080:8080 -p 50000:50000 -v jenkins-data:/home/ec2-user/jenkins_home -d jenkins/jenkins:lts

// run sonarqube(optional)
docker run --hostname devillage-sonarqube -d --name sonarqube -p 9000:9000 -v /path/to/host/data:/home/ec2-user/sonarqube/data sonarqube

// run ansible
docker run -d --name ansible -v /home/ec2-user/ansible:/ansible -p 8081:8081 ansible/ansible:default

// install jdk in jenkins
docker exec -u 0 -it jenkins-master bash
apt update && sudo apt install -y openjdk-11-jdk

 

기본 세팅은 어떻게 구성할 것인지에 따라 달라진다.

1. JDK, Sonarqube, DB 등을 미리 설치하고 사용할 것인가? 아니면 CI/CD 파이프라인이 가동될 때마다 Pull 받아서 사용할 것인가?

2. 유닛테스트만 진행할 것인가? 통합테스트도 진행할 것인가?

3. 컨테이너의 데이터를 보존할 것인가?

4. 포트가 겹치는 경우는 없는가?

5. 소나큐브의 정적 분석 결과를 별도 DB에 저장할 것인가?

6. 디렉토리, 도커 권한 설정

7. 컨테이너 보안

등등을 고려해서 직접 세팅하도록 하자.

 

이외에 기본적인 ec2 사용법은 알 것이라고 가정하고 보안그룹 관련된 내용은 다루지 않을 것이다.

 

소나큐브 세팅은 공식문서에 자세하게 나와있으니 궁금하면 읽어보자.

Docker로 세팅하면 딱히 그럴 일은 없겠지만, 구버전은 JDK 8/Mysql 5.x까지만 지원하는 경우도 있으니 주의할 것.

https://docs.sonarqube.org/latest/

 

SonarQube 9.9

SonarQube is a self-managed, automatic code review tool that systematically helps you deliver clean code. As a core element of our Sonar solution, SonarQube integrates into your existing workflow and detects issues in your code to help you perform continu

docs.sonarqube.org

 

 

 

 

모두 설치하고 http://ip-address:8080으로 접속하면 이런 화면이 우릴 반겨준다.

 

docker exec -it {container} bash
cat /var/jenkins_home/secrets/initalAdminPassword

위와 같이 입력하면 패스워드를 확인할 수 있다.

 

필요에 따라 추천 플러그인 설치, 혹은 수동 설치 중에 고르자.

나처럼 ec2 용량이 간당간당하면 필요한 것만 골라서 설치하는 것도 나쁘지 않다.

 

개인 정보 설정이나 젠킨스 url은 알아서 설정하자.

 

 

test-proejct는 무시하자.

 

실행하면 이런 화면이 반겨줄 것이다.

새로운 Item -> Pipeline으로 프로젝트를 생성하자.

 

깃헙 웹훅과의 연동을 위한 설정이 필요한데, 이것만 다뤄도 너무 길다.

잘 정리해놓은 블로그가 있으니 참고해서 웹훅을 설정하자.

 

https://junhyunny.github.io/information/jenkins/github/jenkins-github-webhook/

 

젠킨스(Jenkins) GitHub Webhooks 연동

<br /><br />

junhyunny.github.io

 

만약 private repository라면 아래와 같은 절차가 추가적으로 필요하다.

 

// root user access
docker exec -u 0 -it jenkins-master bash

// create ssh dir
mkdir .ssh

// create ssh key
ssh-keygen -t rsa

 

위 순서대로 진행하면 된다.

생성된 키를 cat으로 값을 조회하고 레파지토리의 deployKey에 세팅하자.

 

 

이제 기본적인 세팅이 완료되었으니 파이프라인을 통해 정상적으로 프로젝트를 빌드할 수 있는지 확인할 차례다.

 

 

pipeline {
    agent any
    stages {
        stage('Checkout and Clone') {
            steps {
                git branch: 'main',
                    credentialsId: 'github_access_token',
                    url: 'https://github.com/Dev-illage/Devillage.git'
            }
        }
        
        stage('Compile & Test') {
            steps {
                sh './gradlew build'
                echo 'Build Success'
            }
        }
        
        stage('Code Analysis') {
            steps {
                echo 'Code Analysis Success'
            }
        }
        
        stage('Delivery') {
            steps {
                echo 'Delivery Success'
            }
        }
    }
}

* 이건 내 환경에서만 돌아가는 거고, JDK를 설치했는지, 빌드 시에 Redis, MySql, Chromedriver 등이 필요한지를 고려해서 진행해야하며, 미리 해당 프로그램들을 설치하거나 매번 docker로 필요한 이미를 설치해서 사용할 수도 있다. *

 

우리가 젠킨스에서 진행할 프로세스는 위와 같다.

소나큐브와 엔서블은 아직 전혀 손대지 못했으므로, echo만 남겨두자.

 

 

기분 좋게 성공한 것을 확인할 수 있다.

이제 소나큐브를 돌려서 코드를 검사할 시간이다.

 

http://당신의 EC2주소:당신의 소나큐브 포트번호로 이동한다.

초기 비밀번호 admin, amdin을 입력하고나면 아래와 같은 화면이 나올 것이다.

 

우측 상단의 프로필을 누르고, my account -> security로 이동해서 소나큐브용 토큰을 생성한다.

 

 

 

 

생성된 토큰을 어딘가에 복사해놓던지 하자.

 

젠킨스 관리 -> plugin -> Sonnarqube Scanner를 설치하고 재시작한다.

젠킨스 관리 -> credentials로 이동해서 sonarqube 토큰 값을 입력해준다.

 

 

다음은 젠킨스 관리 -> 시스템 설정 -> SonarQube server로 이동한다.

 

 

 

당신의 소나큐브 주소와 포트번호를 입력하고, 이전에 만든 토큰을 등록해주고 설정을 저장하면 된다.

9000번 포트를 열어주는 걸 잊지 말자.

 

        stage('Code Analysis') {
            steps {
                withSonarQubeEnv('sonarqube-server') {
                    sh './gradlew sonarqube'
                    echo 'Code Analysis Success'
                }
            }
        }

 

기존에 echo만 있던 Coe Anlysis에 해당 스크립트를 작성하고, gradle plugin에 sonarqube를 지정해주면 이제 정말 끝이다.

 

id "org.sonarqube" version "3.5.0.2730"

 

후.. 벌써 지친다. 한 번 돌려보자.

 

 

 

성공한 것을 확인할 수 있다.

결과를 확인하려면 다시 본인의 ec2 주소 : 포트번호로 접속하면 된다.

 

일단 확인하기 전에 심호흡 한 번 하자.

미리 핑계를 대보자면, 이 프로젝트를 진행할 때 난 거의 프론트 코드만 작성했다 ^^..

사랑합니다 팀원 여러분

 

 

ㅋㅋㅋ

아.. 이건 좀..

나중에 최근 진행한 프로젝트도 한 번 돌려봐야겠다..

 

유닛테스트가 개수가 처참하긴 하지만 없는 건 아닌데 커버리지가 0%가 나오는 이유는 Jacoco가 없기 때문이다.

대단한 설정이 필요한 건 아니기 때문에 건너뛰고 넘어가도록 하겠다.

궁금하면 구글 검색해서 해보삼.

 

만약 소나큐브 결과에 따라 배포 여부를 결정하고 싶다면, 아래와 같은 스크립트를 추가할 수 있다.

        stage('Quality Gate Check') {
            steps {
                script {
                    def qualityGateStatus = sh(returnStdout: true, script: "curl -u ${TOKEN}: -X GET \"http://${SONAR_SERVER}:9000/api/qualitygates/project_status?projectKey=${PROJECT_KEY}\" | jq -r '.projectStatus.status'").trim()
                    echo qualityGateStatus
                    if (qualityGateStatus == "ERROR") {
                        echo "Quality Gate status is ERROR, not deploying."
                        error("Quality Gate failed, build is not ready for deployment.")
                    } else {
                        echo "Quality Gate status is OK, deploying."
                    }
                }
            }
        }

 

소나큐브로 정적분석을 했다는 것만으로 소나큐브에서 응답을 주는 게 아니라, 해당 프로젝트 키를 이용해서 요청을 보내면 결과를 받을 수 있고, 결과에 따라 배포를 실패시킬지 성공시킬지 결정할 수 있다.

 

TOKEN, PROJECT_KEY, SONAR_SERVER는 하드코딩해도 상관 없지만 효율성을 위해 환경변수로 등록해놓고 사용하자.

젠킨스 관리 -> 시스템 관리에서 환경변수를 설정할 수 있다.

 

이제 끝이 보인다.

ansible에 이미지 파일을 건내주고, ansible에서는 해당 파일을 서버에 배포해주면 끝이다.

그 전에, ansible에 jenkins의 ssh를 복사하는 과정이 필요하다.

 

// ec2에서 ansible 컨테이너의 bridge ip address 확인
docker network inspect bridge

// ssh key 전송
mkdir /root/.ssh
cd /root/.ssh
ssh-keygen -t rsa
ssh-copy-id -i /root/.ssh/id_rsa.pub ec2-user@위에서 확인한 ip address

 

이후 파이프라인에 아래와 같이 추가해주자.

 

        stage('Delivery') {
            when {
                not {
                    expression {
                    currentBuild.result == "FAILURE"
                    }
                }
            }
            
            steps {
                sh 'scp -i /var/jenkins_home/.ssh/id /var/jenkins_home/workspace/devillage-cicd/build/libs/teamproject-0.0.1-SNAPSHOT.jar root@172.17.0.4:~/devillage/'
                echo 'Delivery Success'
            }
        }
    }

 

당연히 그냥 복붙하면 안되고 각자 환경에 맞춰서 수정해야 한다.

모든 절차가 성공적이라면 아래와 같이 ansible 컨테이너에 jar 파일이 정상적으로 복사된 것을 확인할 수 있다.

 

 

이제 남은 절차는 ansible에 playbook 스크립트 작성하고 production server와 연동해주는 것이다.

라고 할 뻔.. 했는데 application.yml을 생성하는 걸 깜빡했다.

 

죄송합니다..

 

젠킨스 관리 -> manage crendentials로 이동하자.

 

yml에 필요한 값들을 credential에 저장해준다.

이제부터 젠킨스 보안이 중요해지는 것을 느낄 것이다. ec2에서 젠킨스에 접근하는 인바운드 ip를 관리해주도록 하자.

지금까지도 노가다였지만 이제 더한 노가다가 우리를 기다리고 있다.

이 과정이 귀찮다면 그냥 production server에 미리 yml을 저장해놓는 것도 방법이다.

유지보수성은 떨어지겠지만..

 

참고로 젠킨스 credentials를 이용하는 방법 외에도 aws parameter store, ec2 invronment variable, ansible 등 다양한 방법으로 yml 파일을 생성하거나 내용을 채울 수 있으니, 이 방법이 마음에 안 들면 구글에 검색해보자.

 

이제 지금까지의 jenkins pipeline을 돌아볼 시점이 왔다.

 

pipeline {
    agent any
    environment {
        SONAR_SERVER = "${env.SONAR_SERVER}"
        PROJECT_KEY = "${env.PROJECT_KEY}"
        TOKEN = "${env.TOKEN}"
        ROOT_PASSWORD = credentials('root_password')
        DB_USERNAME = credentials('devillage-db-username')
        DB_PASSWORD = credentials('devillage-db-password')
        MAIL_ID = credentials('devillage-mail-id')
        MAIL_PASSWORD = credentials('devillage-mail-password')
        AWS_ACCESSKEY = credentials('devillage-aws-accesskey')
        AWS_SECRETKEY = credentials('devillage-secretkey')
        S3_BUCKET = credentials('devillage-s3-bucket')
        JWT_SECRETKEY = credentials('devillage-jwt-secretkey')                                                        
        JWT_REFRESHKEY = credentials('jwt-refreshkey')                                                        
        GITHUB_SECRET = credentials('devillage-github-secret')
        GOOGLE_SECRET = credentials('devillage-google-secret')     
    }

    stages {
        stage('Checkout and Clone') {
            steps {
                git branch: 'main',
                    credentialsId: 'github_access_token',
                    url: 'https://github.com/Dev-illage/Devillage.git'
            }
        }
        
        stage('Compile & Test') {
            steps {
                sh './gradlew build'
                echo 'Build Success'
            }
        }
        
        stage('Code Analysis') {
            steps {
                withSonarQubeEnv('sonarqube-server') {
                    sh './gradlew sonarqube'
                    echo 'Code Analysis Success'
                }
            }
        }
        
        stage('Quality Gate Check') {
            steps {
                script {
                    def qualityGateStatus = sh(returnStdout: true, script: "curl -u ${TOKEN}: -X GET \"http://${SONAR_SERVER}:9000/api/qualitygates/project_status?projectKey=${PROJECT_KEY}\" | jq -r '.projectStatus.status'").trim()
                    echo qualityGateStatus
                    if (qualityGateStatus == "ERROR") {
                        echo "Quality Gate status is ERROR, not deploying."
                        error("Quality Gate failed, build is not ready for deployment.")
                    } else {
                        echo "Quality Gate status is OK, deploying."
                    }
                }
            }
        }
        
        stage('Delivery') {
            when {
                not {
                    expression {
                    currentBuild.result == "FAILURE"
                    }
                }
            }
            
            steps {
                sh 'sed -i "s/spring.datasource.username:.*/spring.datasource.username: ${DB_USERNAME}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/spring.datasource.password:.*/spring.datasource.password: ${DB_PASSWORD}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/spring.security.oauth2.client.registration.github.secret:.*/spring.security.oauth2.client.registration.github.secret: ${GITHUB_SECRET}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/spring.security.oauth2.client.registration.google.secret:.*/spring.security.oauth2.client.registration.google.secret: ${GOOGLE_SECRET}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/spring.mail.username:.*/spring.mail.username: ${MAIL_ID}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/spring.mail.password:.*/spring.mail.password: ${MAIL_PASSWORD}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/cloud.aws.credentials.accessKey:.*/cloud.aws.credentials.accessKey: ${AWS_ACCESSKEY}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/cloud.aws.credentials.secretKey:.*/cloud.aws.credentials.secretKey: ${AWS_SECRETKEY}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/jwt.secretkey:.*/jwt.secretkey: ${JWT_SECRETKEY}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
                sh 'sed -i "s/jwt.refreshkey:.*/jwt.refreshkey: ${JWT_REFRESHKEY}/" /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml'
        
                sh 'scp -i /var/jenkins_home/.ssh/id /var/jenkins_home/workspace/devillage-cicd/build/libs/teamproject-0.0.1-SNAPSHOT.jar root@172.17.0.4:~/devillage/'
                sh 'scp -i /var/jenkins_home/.ssh/id /var/jenkins_home/workspace/devillage-cicd/src/main/resources/application-prod.yml root@172.17.0.4:~/devillage/'
                echo 'Delivery Success'
            }
        }
    }
}

 

 

지금까지 잘 따라왔다면 위와 같은 pipeline 스크립트가 작성되었을 것이다.

빌드를 돌리고 ansible의 폴더를 확인해보면 yml과 jar 파일이 모두 정상적으로 카피된 것을 확인할 수 있다.

 

 

 

이제 아래 과정을 거쳐야한다.

 

// 도커에서 네트워크 브릿지 ip 확인
docker network inspect bridge

// ansible접속
docker exec -it ansible bash

// 알아서 inventory 작성할 디렉토리로 이동.
// jar 파일과 같은 위치에 있는 게 편하다.

// inventory file 작성 (이름 무관)
vim inventory

// inventory 파일 내용
[ansible에 명령어로 사용할 이름]
ip ansible_user=실행할 권한 유저 이름

 

아직 할 일이 많이 남았다.

우선 production server에 ansible server의 ssh 공개키를 넘겨야 한다.

 

// install openssh-server
sudo yum update
sudo yum install openssh-server

// set sshd
vim /etc/ssh/sshd_config
-> PubkeyAuthentication의 주석을 해제하고 yes로 설정

// copey ssh (ansible 서버에서)
ssh-copy-id -i ~/.ssh/ansible-key.pub Hostname@IP

// 아무 파일이나 만들어서 정상적으로 연결 됐는지 테스트
echo test > test.txt
scp -i ~/.ssh/ansible-key read.txt Hostname@IP:/경로

// ansible ssh 설정
vim /etc/ansible/ansible.cfg
-> scp_if_ssh = True의 주석 제거

 

이제 정말 끝이 보인다..

여기까지 성공했으면 playbook 작성해서 넘겨주고 실행만 하면 된다.

 

원하는 이름으로 playbook 파일을 만들어주자. 단, 확장자는 yml이여야 한다.

 

- name: Transfer files to devillage host
  hosts: devillage
  gather_facts: yes
  tasks:
    - name: Stop previously running Spring application
      shell: pkill -f teamproject-0.0.1-SNAPSHOT.jar
      become: yes
      become_user: root
      ignore_errors: yes

    - name: Copy application-prod.yml
      copy:
        src: /root/devillage/application-prod.yml
        dest: /home/ec2-user/devillage
        owner: root
        group: root
        mode: 0600
      become: yes
      become_user: root

    - name: Copy teamproject-0.0.1-SNAPSHOT.jar
      copy:
        src: /root/devillage/teamproject-0.0.1-SNAPSHOT.jar
        dest: /home/ec2-user/devillage
        owner: root
        group: root
        mode: 0600
      become: yes
      become_user: root

    - name: Run teamproject-0.0.1-SNAPSHOT.jar
      shell: sudo nohup java -jar ~/devillage/teamproject-0.0.1-SNAPSHOT.jar --spring.config.name=application-prod --spring.config.location=~/devillage/ &> /home/ec2-user/devillage/teamproject.log &
      become: yes
      become_user: root

 

1. hosts는 위의 inventory 파일에 작성했던 이름을 작성하면 된다.

2. gather-facts는 전송한 서버의 정보를 가져올 것인지에 대한 옵션이다.

3. become 옵션은 root 유저 권한으로 작업을 하기 위해 설정한 것인데 아마 없어도 될 것 같다. 다른 것 때문에 헤매다가 넣은 건데, 도움은 되지 않았다.

4. ignore_errors는 해당 구문에서 오류가 발생해도 무시하고 다음 단계로 넘어가라는 것이다.

5. 그외 경로나 파일명은 알아서..

 

이제 pipeline에서 playbook 실행 명령어를 추가해준다.

sh 'ssh -i ~/ssh/jenkins-ssh root@172.17.0.4 "ansible-playbook -i ~/devillage/inventory ~/devillage/devillage-deploy-playbook.yml --private-key=~/.ssh/ansible-key"'

ansivle-playbook -i {inventory} {playbook} --private-key={ssh}

 

위에서 설정했던 ssh 명령어를 그대로 사용하는 것이므로 어려울 것은 없다.

파일명과 경로만 본인의 세팅에 맞게 실행해주면 된다.

 

 

지금까지 별 문제없이 잘 지나왔다면 실행 시 production server에서 어플리케이션을 기동시키는 것까지 성공했을 것이다.

다 끝난 것 같지만 하나 남았다.

 

jenkins worker node를 만들어야 한다.

처음에 했던 것들과 별 다를 바 없으니 빠르게 헤치우자.

 

// install jenkins-worker
docker run --name jenkins-warker1 -p 8082:8082 -p 50001:50001 -v jenkins-data:/home/ec2-user/jenkins_home/warker -d jenkins/jenkins:lts

// exec jenkins-worker
docker exec -it -u 0 jenkins-worker1 bash

// install jdk
apt-get install open-jdk-11

// install ssh server
apt-get update
apt-get install openssh-server
service sshd start

// exec jenkins-master
docker exec -it jenkins-master bash
ssh-copy-id ~/.ssh/id jenkins@브릿지주소

 

여기까지 했으면 jenkins 관리 > nodes로 가서 노드를 추가할 차례다.

 

개인 설정에 맞춰서 세팅하되, credentail은 username with password를 고르고 username은 jenkins, password는 설정한 worker node의 password를 입력하면 된다.

 

number of executors는 실행할 노드의 개수로, 두개를 실행하면 좌측에 표시된 것 처럼 두 개의 노드가 빌드를 위해 대기한다.

 

별거 없다, 이제 기존 파이프라인에서 agent 부분만 수정하면 된다.

agent {label 'worker-node'}

 

 

 

두 개의 노드가 동시에 열심히 일하는 것을 확인할 수 있다.

참고로 저사양 ec2에서 이런 짓하면 가상메모리고 뭐고 cpu 부족해서 바로 뻗는다.

나도 결국 뻗어서 재실행했다.

 

아무튼간에 꼭 병렬로 여러 CI를 진행하는 것이 아니더라도, 기존에는 마스터 노드가 혼자서 관리부터 build/analysis/delivery까지 모두 수행했지만 역할을 분리함으로써 보다 안정적으로 젠킨스를 가동할 수 있게 되었다.

 

내가 3일 동안 작성한 글이 누군가에게 도움이 되길 바라며.. ㅎ 그럼 이만..

 

 

추가적으로 생각해볼만한 것들.

1. 위 worker node는 그냥 Round Robin 방식으로 동작하고 있다.

node의 프로세스 상태에 따라 작업을 할당하고 싶다면 Jenkins Roadbalancer를 검색해보자.

 

2. 서버가 정상 기동하고 있는지 확인하지 않고 있다.

가장 기본적인 방법으로는 젠킨스에서 ansible 배포가 완료된 이후 curl로 확인해볼 수도 있고, ansible plugin을 이용해 추가적인 동작을 지정할 수도 있다.

혹은 별도의 서버 모니터링 도구 (prometehus, grafana)를 활용하는 것도 방법이다.

 

3. 현재 방식으로는 서버가 배포시마다 일시적으로 멈춘다는 아주 심각한 문제가 있다.

이를 해결하기 위해서는 red-blue 배포 방식을 선택해야 한다.

애초에 ec2를 두 개 이상으로 구성할 수도 있고, (ec2의 사양이 충분하다면) 하나의 ec2에서 도커를 이용해 2개의 컨테이너를 돌리는 방법도 있다.