Post

MSA 서비스 만들기 1

MSA 서비스 만들기 1

개요

지난 1주 동안 부트캠프에서 미니 프로젝트를 진행하였다. 매일 10시까지 있었던건 안비밀

본인 조는 일종의 게시판 서비스를 만들었는데… 본인은 게이트웨이와 사용자(user) 마이크로서비스를 담당하였다.

do1-img1
do1-img2

구성도는 위와 같다.

각 마이크로서비스(gateway, user, post, leaderboard)가 서로의 db를 가지고,
kafka를 통해 통신하는 구조였다. 당연히 각각은 docker로 컨테이너 구현되어 있고.

발표 때는 시간과 여백이 부족하여 로컬 환경에서 구동했는데, 프로젝트가 끝났지만 뭔가 오기? 가 생겨 주말 동안 젠킨스 배포 시스템과 쿠버네티스 구현을 해보기로 했다.

젠킨스 만들기

젠킨스 초기설정

우선 젠킨스를 설치해야 하니 컨테이너로 만들고…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: "3"
services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    restart: always
    user: root
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 8000:8080
    environment:
      - DOCKER_HOST=unix:///var/run/docker.sock 

docker-compose up -d 로 실행시켜줬다. 포트는 8000 포트를 사용할 예정~
(ports: -8000:8080 의 의미는 컨테이너 안에는 8080 포트로 실행하는데 밖에서는 8000으로 접속한다는 것을 의미한다. 즉 일종의 프록싱)

do1-img3

젠킨스를 처음 들어가면 초기 비밀번호가 필요할건데, 화면에도 나오지만
/var/jenkins_home/secrets/initialAdminPassword 여기 있다고 한다.

컨테이너에 직접 접속해서 찾으면 된다.

do1-img4

1
2
docker exec -it (내 젠킨스 컨테이너이름) /bin/bash
cat /var/jenkins_home/secrets/initialAdminPassword

여차저차 초기셋팅을 끝내고, 플러그인을 설치해야 한다.

do1-img5

사진에 동그라미 친 것처럼 설정→Plugins→Available plugins에 들어가서

  • Authentication Tokens API
  • Docker
  • Docker Pipeline
  • Generic Webhook Trigger
  • Git Parameter
  • Stage View

이렇게 6개를 설치하자.

그 다음에는 다시 설정→Tools에 들어가서 빌드 설정을 해줘야 하는데,
본 프로젝트는 spring boot, 즉 자바 Gradle로 마이크로서비스를 개발하기 때문에
JDK, Gradle 설정을 넣어줘야 한다. 추가로 Docker 이미지로 배포할 것이기 때문에 Docker 설정도~

do1-img6
do1-img7
do1-img8

토큰 발급

젠킨스에서 해주는 빌드 구조가 github에서 파일을 받아, docker image로 만드는 것이기 때문에, 깃허브와 독커허브 양쪽에서 토큰을 발급받아야 한다.

각 싸이트에서 토큰 받는 방법은 생략하고~(다들 아시겠죠?)

do1-img9

위 사진처럼 프로필→Credentials→Stores from parent의 global 도메인을 클릭한 뒤

do1-img10 do1-img11

New credentials 버턴을 누르고, 위 사진처럼 입력해서 깃허브/도커 이렇게 2개를 만들어 준다.
Username내 실제 깃허브/도커 아이디이고, ID여기서 구분할 아이디이다.

do1-img12

본인은 ID를 github-token, dockerhub-token 이렇게 했다.

젠킨스파일 만들기

각 서비스별로 젠킨스파일을 만들어 준다. 젠킨스파일은 이왕이면 소스코드 루트(최상위)에 위치해 있는 것이 좋다.
이 젠킨스 파일은 실제로 깃허브에서 소스 코드를 가져와 독커 이미지를 만드는 프로세스이다.
아래는 예시 파일~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env groovy
def APP_NAME
def APP_VERSION
def DOCKER_IMAGE_NAME
def PROD_BUILD = false
def TAG_BUILD = false
pipeline {
    agent {
        node {
            label 'master'
        }
    }

    parameters {
        gitParameter branch: '',
                    branchFilter: '.*',
                    defaultValue: 'origin/(메인브랜취 이름)',
                    description: '', listSize: '0',
                    name: 'TAG',
                    quickFilterEnabled: false,
                    selectedValue: 'DEFAULT',
                    sortMode: 'DESCENDING_SMART',
                    tagFilter: '*',
                    type: 'PT_BRANCH_TAG'

        booleanParam defaultValue: false, description: '', name: 'RELEASE'
    }

    environment {
        GIT_URL = "(서비스 깃허브 주소)"
        GITHUB_CREDENTIAL = "github-token"
        ARTIFACTS = "build/libs/**"
        DOCKER_REGISTRY = "(내 이름)"
        DOCKERHUB_CREDENTIAL = 'dockerhub-token'
    }

    options {
        disableConcurrentBuilds()
        buildDiscarder(logRotator(numToKeepStr: "30", artifactNumToKeepStr: "30"))
        timeout(time: 120, unit: 'MINUTES')
    }

    tools {
        gradle 'Gradle 8.14.2'
        jdk 'OpenJDK 17'
        dockerTool 'Docker'
    }

    stages {
        stage('Set Version') {
            steps {
                script {
                    APP_NAME = sh (
                            script: "gradle -q getAppName",
                            returnStdout: true
                    ).trim()
                    APP_VERSION = sh (
                            script: "gradle -q getAppVersion",
                            returnStdout: true
                    ).trim()

                    DOCKER_IMAGE_NAME = "${DOCKER_REGISTRY}/${APP_NAME}:${APP_VERSION}"

                    sh "echo IMAGE_NAME is ${APP_NAME}"
                    sh "echo IMAGE_VERSION is ${APP_VERSION}"
                    sh "echo DOCKER_IMAGE_NAME is ${DOCKER_IMAGE_NAME}"

                    sh "echo TAG is ${params.TAG}"
                    if( params.TAG.startsWith('origin') == false && params.TAG.endsWith('/master') == false ) {
                        if( params.RELEASE == true ) {
                            DOCKER_IMAGE_NAME += '-RELEASE'
                            PROD_BUILD = true
                        } else {
                            DOCKER_IMAGE_NAME += '-TAG'
                            TAG_BUILD = true
                        }
                    }
                }
            }
        }

        stage('Build & Test Application') {
            steps {
                sh "gradle clean build"
            }
        }

        stage('Build Docker Image') {
//             when {
//                 expression { PROD_BUILD == true || TAG_BUILD == true }
//             }
            steps {
                script {
                    docker.build "${DOCKER_IMAGE_NAME}"
                }
            }
        }

        stage('Push Docker Image') {
//             when {
//                 expression { PROD_BUILD == true || TAG_BUILD == true }
//             }
            steps {
                script {
                    docker.withRegistry("", DOCKERHUB_CREDENTIAL) {
                        docker.image("${DOCKER_IMAGE_NAME}").push()
                    }

                    sh "docker rmi ${DOCKER_IMAGE_NAME}"
                }
            }
        }
    }
}

보면 알겠지만, environment 측의

1
2
3
4
5
6
7
environment {
        GIT_URL = "(서비스 깃허브 주소)"
        GITHUB_CREDENTIAL = "github-token"
        ARTIFACTS = "build/libs/**"
        DOCKER_REGISTRY = "(내 이름)"
        DOCKERHUB_CREDENTIAL = 'dockerhub-token'
    }

여기가 중요한데,
GITHUB_CREDENTIAL와 DOCKERHUB_CREDENTIAL에 아까 만들었던
credentials ID를 입력해주는 것이다.

tools 측도

1
2
3
4
5
tools {
        gradle 'Gradle 8.14.2'
        jdk 'OpenJDK 17'
        dockerTool 'Docker'
    }

아까 설정의 Tools에서 입력한 버젼 그대로 입력해주면 된다.

폴더, 파이프라인 만들기

do1-img13 do1-img14

우선 프로젝트 빌드를 모아 줄 폴더가 필요하다.

왼쪽의 New Item을 클릭해서 Select an item type에 Folder를 선택하고 확인을 누른다. 이후 나오는 config는 폴더니까 패쓰

do1-img15

이제 폴더에 들어가서 다시 New Item을 클릭하고 Pipeline을 선택해서 확인을 누른다.

do1-img16

여기도 마찬가지로 설정페이지가 나오는데, 여기는 설정 해줘야 한다.

pipeline 항목이 중요한데,

  • Definition에 Pipeline script from SCM을 선택하고,
  • SCM에 Git을 선택하고,
  • Repository URL에 서비스 깃허브 레포주소를 입력하고,
  • Credentials에 내 깃허브 아이디로 되어 있는 것을 선택하고,
  • Branch specifier에 내 마스터 브랜취 이름을 입력하면 된다.

이러면 젠킨스가 알아서 마스터 브랜취를 선택하여 빌드한다.

추가로 브랜취를 선택하고 싶으면, 이 빌드는 매개변수가 있습니다를 선택하여

do1-img17

  • Parameter Type에 Branch or Tag
  • Default Value에 origin/master 또는 main을 입력해 주면 된다.

이 매개변수 이름 TAG는 Jenkinsfile의

1
2
3
4
5
6
7
8
9
10
11
12
13
14
parameters {
        gitParameter branch: '',
                    branchFilter: '.*',
                    defaultValue: 'origin/(메인브랜취 이름)',
                    description: '', listSize: '0',
                    name: 'TAG',
                    quickFilterEnabled: false,
                    selectedValue: 'DEFAULT',
                    sortMode: 'DESCENDING_SMART',
                    tagFilter: '*',
                    type: 'PT_BRANCH_TAG'

        booleanParam defaultValue: false, description: '', name: 'RELEASE'
    }

여기 parameter 부문에 위치해 있어 연계되는 것을 확인할 수 있다.

빌드

do1-img18

설정을 끝내고 왼쪽의 파라메타와 함께 빌드를 클릭한 뒤
TAG를 선택하고 빌드를 하면 된다!

do1-img19

이렇게 파이프라인 메인 페이지에 빌드 현황을 확인할 수 있다!!

do1-img20

빌드가 성공적으로 끝나면, docker hub에서 올라간 이미지를 확인할 수 있다..

끝마치며

다음 시간에는 이렇게 빌드한 이미지를 가지고 쿠버네티스에 올리는 방법을 알아보겠다!

This post is licensed under CC BY 4.0 by the author.