Docker, Jenkins로 마이크로서비스 배포

박세건·2024년 9월 19일
0

기술 실습

목록 보기
8/18
post-thumbnail

이전 과정에서 jenkins안에서 호스트의 docker 데몬의 접근할 수 있도록 하여 빌드가 정상적으로 진행되는 것까지 확인했다.
이번 과정에서는 이미지로 빌드하고 docker run을 통해 컨테이너화 시켜서 정상적으로 마이크로 서비스를 운영해보자

마이크로 서비스 배포

최종적인 파이프라인 Script

pipeline {
    agent any



    stages {
        stage('Repository clone') {
            steps {
                sh 'pwd'
                git branch: 'notification', credentialsId: 'qkrtprjs', url: 'https://lab.ssafy.com/s11-fintech-finance-sub1/S11P21A604.git'
            }
            post {
                failure {
                  echo 'Repository clone failure !'
                }
                success {
                  echo 'Repository clone success !'
                }
            }
        }
         stage('Build image') {
            steps {
                dir('notification') {
                    
                    sh 'chmod +x ./gradlew'
                    sh './gradlew build'
                    sh 'pwd'
                    //qkrtprjs은 Docker Repository 이름 , notification은 도커 컨테이너 이름을 의미
                    sh 'docker build -t qkrtprjs/notification .'
                }
                echo 'Build image...'
            }
            post {
                failure {
                  echo 'Build image failure !'
                }
                success {
                  echo 'Build image success !'
                }
            }
        }

        stage('Remove Previous image') {
            steps {
                script {
                    try {
                        sh 'docker stop notification'
                        sh 'docker rm notification'
                    } catch (e) {
                        echo 'fail to stop and remove container'
                    }
                }
            }
            post {
                failure {
                  echo 'Remove Previous image failure !'
                }
                success {
                  echo 'Remove Previous image success !'
                }
            }
        }
        stage('Run New image') {
            steps {
                sh 'docker run --name notification -d -p 8083:8083 qkrtprjs/notification'
                echo 'Run New member image'
            }
            post {
                failure {
                  echo 'Run New image failure !'
                }
                success {
                  echo 'Run New image success !'
                }
            }
        }
    }
}

배포 진행

  • 이미지가 jenkins 컨테이너에 정상적으로 만들어졌는지 확인하기 위해서 컨테이너에 접근
    docker exec -it jenkins sh

    정상적으로 접근

  • 이미지를 빌드한 후에 정상적으로 이미지가 생성되었는지 확인
    docker images로 확인

    정상적으로 notification 이미지 실행중 확인

  • 해당 브랜치로 push 날렸을때 콘솔확인

    정상적으로 이미지가 실행되었다는 문구 확인

  • 현재 마이크로서비스가 컨테이너로 실행되고 있는지 확인
    docker ps

    jenkins이외에도 notification이라는 이름을 갖는 컨테이너가 실행중인 것을 확인
    테스트를 위해 컨트롤러에 작성해놓은 url로 확인

    정상적인 응답 확인

MySQL 연결

현재 MySQL 설정은 localhost의 3306포트로 설정되어있는데 프로젝트가 정상적으로 작동이 되었다 이 문제를 해결해보자.
아직 DB를 접근하지 않아서 발생한 오류인지 알아보자.
추가적으로 MySQL도 EC2에 띄어서 사용할 수 있도록 지정하자

MySQL 접근 에러

역시나 로컬의 MySQL에 접근하지 않아서 에러가 발생하지 않았다. 해당 문제를 확인하기 위해서 Repository와 Entity를 생성해주고 연결하려는 시도를 진행했다.

NotificationApplicationTests > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1806
Caused by: jakarta.persistence.PersistenceException at AbstractEntityManagerFactoryBean.java:421
Caused by: org.hibernate.exception.JDBCConnectionException at SQLStateConversionDelegate.java:100
Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException at SQLError.java:174
Caused by: com.mysql.cj.exceptions.CJCommunicationsException at null:-1
Caused by: java.net.ConnectException at null:-2

위와 같이 MySQL에 접근할 수 없다는 에러가 발생했다.

MySQL 연결

마이크로서비스들이 따로 DB를 사용하는 것이 일반적이라고 학습했지만, 서버의 개수가 작고 크기고 작기때문에 여러개의 마이크로서비스들이 사용할 MySQL을 '하나' 열기로했다.

MySQL docker-compose 작성

version: '3.8'  # Docker Compose 파일 버전

services:
  mysql:
    # image: mysql:latest   최신 MySQL 이미지
    # 프로젝트의 버전과 맞추기 위해 하단 버전으로 진행
    image: mysql:8.0
    container_name: mysql-container  # 컨테이너 이름
    environment:
      MYSQL_ROOT_PASSWORD: ${root의 비밀번호}  # root 비밀번호 설정
      MYSQL_DATABASE: ${데이터베이스 이름}  # 기본 데이터베이스 생성
    ports:
      - "3306:3306"  # 호스트와 컨테이너 간 포트 매핑
    volumes:
      - /home/ubuntu/mysql-data:/var/lib/mysql  # 데이터 영속성을 위한 볼륨

위처럼 작성한 mysql을 위한 docker-compose.yml 파일을 생성했지만 이전에 jenkins를 실행시킬때의 docker-compose.yml도 존재하기 때문에 네이밍으로 차별화두자

  • Jenkins : jenkins-docker-compose.yml
  • MySQL : mysql-docker-compose.yml
  • Run 코드 : docker-compose -f mysql-docker-compose.yml up -d


정상적으로 jenkins, 마이크로서비스, MySQL 컨테이너가 실행중인것을 확인

[트러블 슈팅]MySQL 버전 다운그레이드시에 발생하는 에러

MySQL 이미지를 :lates를 사용해서 빌드하게 되면 9버전의 MySQL을 다운하게 되는데 프로젝트에서 사용하는 버전은 8버전으로 8.0으로 이미지 빌드를 했지만 실행이 되지 않는 에러 발생

  • 시도 1 : 9버전을 이미지 빌드하는 과정에서 볼륨을 마운트해서 데이터를 저장시키려 했던 디렉토리가 존재함, 이 디렉토리를 제거
    • 결과 : 9번전에 맞게 생성된 볼륨이 8버전과 호환이 맞지않아서 발생한 문제를 해결, 정상적으로 작동
  • application.yml 수정
spring:
  datasource:
    url: jdbc:mysql://j11a604.p.ssafy.io:3306/trabean?useSSL=false&serverTimezone=UTC

DB설정 수정해주고 다시 한번 연결 확인

프로젝트에서 Repository 설정도 완료해주었지만 에러없이 정상적으로 서버가 구동되는 것을 확인

MySQL Workbench로 테이블 생성 및 마이크로 서비스에서 연결

CREATE SCHEMA IF NOT EXISTS trabean;

USE trabean;

CREATE TABLE `users` (
    `user_id` bigint NOT NULL AUTO_INCREMENT,
    `user_key` VARCHAR(40) UNIQUE NOT NULL,
    `email` VARCHAR(100) UNIQUE NOT NULL,
    `password` VARCHAR(255) NOT NULL,
    `name` VARCHAR(10) NOT NULL,
    `payment_account_id` bigint NULL,
    `main_account_id` bigint UNIQUE NULL,
    PRIMARY KEY (`user_id`)
);
...
  • 위 Sql문으로 테이블을 생성완료
    임시 더미데이터를 넣어놓고 마이크로 서비스에서 정상적으로 요청에대한 더미데이터를 응답하는지 확인해보자

[트러블슈팅] Enum 관련

MySQL에서 제공하는 Enum 자료형을 사용해서 데이터를 저장하려고했지만 enum 사용관련해서 좋지 않다는 피드백을 확인
일단은 Varchar 자료형을 사용해서 문자열로 받고 백엔드단에서 유효성을 점검하는 방식으로 진행

하지만, 저장된 데이터를 조회하는 과정에서 Enum부분에서 에러 발생, 데이터베이스에서 받아온 값과 enum을 연결해주지 못해서 발생한 에러

private NotificationType type;

Entity 필드를 위처럼 정의하였는데 받아온 값과 Enum을 연결해주기 위해서 JPA에서 제공해주는 어노테이션이 존재

  • 시도 1) @Enumerated(EnumType.STRING) 어노테이션 적용
    Hibernate에서는 enum을 데이터베이스에 저장하기 위해 @Enumerated 어노테이션을 사용할 수 있습니다. 이때 EnumType.STRING이나 EnumType.ORDINAL을 선택할 수 있습니다. ORDINAL은 정수형으로 저장하고, STRING은 문자열로 저장합니다.
    본인은 문자열로 데이터를 관리하고자 하여 STRING으로 진행

    @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long notificationId;
    
      private Long senderId;
      private Long receiverId;
      private Long accountId;
      @Enumerated(EnumType.STRING)
      private NotificationType type;
      private boolean isRead;
      private Long amount;
      private Timestamp createTime;
    • 결과 : 정상적으로 데이터를 조회할 수 있음을 확인

Mattermost 연동

각각의 마이크로서비스들의 CI/CD 결과를 Mattermost를 통해서 알려줄 수 있도록 연동해보자

Mattermost 진행과정





이 채널로 고정 클릭

이때 중요한점은 제목과 채널명은 영어로 생성하길 바란다. /기호와 한글을 사용해서 진행했을때는 실패했다는 알림을 받았다.


여기서 웹훅 URL을 저장해놓는다

Jenkins 진행과정

  1. Mattermost Notification Plugin 설치
  2. Jenkins 관리 -> 시스템 설정에서 Global Mattermost Notifier Settings 이동
  3. 해당 정보 입력
  • Endpoint : 이전에 저장한 Endpoint는 Mattermost의 웹훅 URL
  • Channel : Mattermost 채널 명
  • Build Server URL : 자동 입력
  1. Test Connection으로 Success가 뜨는지 확인

Pipeline 작성

두 플랫폼을 웹훅을 통해 연결을 설정했다면 Pipeline을 통해서 알림을 보낼 수 있는 script를 작성한다.

stage('Build image') {
            steps {
                dir('notification') {
                    
                    sh 'chmod +x ./gradlew'
                    sh './gradlew build'
                    sh 'pwd'
                    sh 'docker build -t qkrtprjs/notification .'
                }
                echo 'Build image...'
            }
            post {
                failure {
                    echo 'Build image failure !'
                    script {
                        def Author_ID = sh(script: "git show -s --pretty=%an", returnStdout: true).trim()
                        def Author_Name = sh(script: "git show -s --pretty=%ae", returnStdout: true).trim()
                        mattermostSend (color: 'danger', 
                        message: "도커 이미지 빌드 실패: ${env.JOB_NAME} #${env.BUILD_NUMBER} by ${Author_ID}(${Author_Name})\n(<${env.BUILD_URL}|Details>)", 
                        endpoint: 'https://meeting.ssafy.com/hooks/bb6j17ansjnambc9cjddf8gw7o', 
                        channel: 'CICD'
                        ) 
                    }
                }
                success {
                    echo 'Build image success !'
                    script {
                        def Author_ID = sh(script: "git show -s --pretty=%an", returnStdout: true).trim()
                        def Author_Name = sh(script: "git show -s --pretty=%ae", returnStdout: true).trim()
                        mattermostSend (color: 'good', 
                        message: "도커 이미지 빌드 성공: ${env.JOB_NAME} #${env.BUILD_NUMBER} by ${Author_ID}(${Author_Name})\n(<${env.BUILD_URL}|Details>)", 
                        endpoint: 'https://meeting.ssafy.com/hooks/bb6j17ansjnambc9cjddf8gw7o', 
                        channel: 'CICD'
                        )
                    }  
                }
            }
        }

해당 script를 작성하게 되면 누가 push를 진행했는지 성공했는지 실패했는지, 어떤 마이크로서비스인지를 알림을 통해 알려주게된다.

환경 변수 설정

프로젝트에서 중요한 설정을 관리하는 application.yml 파일이 Gitlab에 노출되어있는데 이 문제를 어떻게 해결할까?

profile
멋있는 사람 - 일단 하자

0개의 댓글