jenkins로 ci/cd 만들기

👏 할 일

이제 jenkins가 우리 서버에 직접적으로 무언가를 할수 있다는 것을 알 수 있었다. 그러면 이제 정말 무언가를 해야하는데 어떤걸 해야할지 막막해졌다. 그래서 다른 분들은 어떻게 jenkins와 docker를 사용하고 있을까 어떻게 ci/cd를 하는걸까? 고민했고 찾아봤다. 뭐든 서비스를 만들면서 정답은 없지만 내가 하고자 하는 방향을 적어보려고 한다.

jenkins가 빌드를 실행할 때

  1. dockerfile로 작성된 이미지로 A 컨테이너를 실행시킨다.
  2. A 컨테이너는 git 소스를 pull하고 테스트 및 빌드를 진행한다.
  3. A 컨테이너를 이미지화 시킨다.
  4. 이미지화된 A 컨테이너를 docker hub에 올린다.
  5. 실제 운영중인 server의 B 컨테이너를 docker-compse down을 통해 내린다.
  6. 작성된 dockerfile을 통해 이미지화된 A 컨테이너를 docker-compse up을 통해 실행하며 내부 명령어를 실행시켜 자동으로 server가 실행되도록 한다.

하나의 server를 만드는데 이해해야할 내용이 산더미다... 하지만 이것들을 해내면 하나의 자동 ci/cd 프로세스를 직접 구현해본 것이므로 나에겐 아주 큰 가치있는 작업일 것이다!

🔨 dockerfile

작성해보기

우리가 지금 실행하고 있는 node server를 dockerfile을 작성하여 실제로 node까지 실행할 수 있도록 변경해보자.

도커 파일 작성 참고글

#베이스 이미지
FROM node

# 개발자 정보
MAINTAINER "[email protected]"

# 컨테이너 실행시 실행할 멸령어
RUN mkdir project
# 작업 데렉터리 변경
WORKDIR /project

RUN git clone https://github.com/juno-choi/mainSite.git

WORKDIR /project/mainSite
RUN npm install

다음과 같이 Dockerfile을 정의했다.

Dockerfile은 이름도 Dockerfile로 만들어야한다. 파일 이름을 다르게 작성했다면 실행 명령어에 파일 이름을 적어주면 된다!

그 후
docker build -t <이미지 이름>:<태그> <Dockerfile위치>
로 명령어를 실행하면


다음과 같이 step별로 진행되며 이미지가 만들어진다.

docker run -it <이미지명> /bin/bash 명령어를 통해 실제 소스가 pull되었는지 확인해봐도 좋다.

docker hub에 올리기

허브에 올리는 것은

docker tag <로컬 이미지 이름>:<태그> <올릴 이미지 이름>:<태그>
docker push <태그한 이미지 이름>:<태그>


로 올리는 것이 가능하다.

🔨 docker compose 작성

version: '3.7'
  
services:
    nginx:
        container_name: nginx
        image: "ililil9482/main_nginx:0.0.1" #수정된 이미지
        ports:
            - 80:80
    express:
        image: "ililil9482/main_node:latest"
        container_name: express
        user: root
        expose:
            - 3000
        volumes:
            - /volumes:/volumes
        depends_on:
            - nginx

express의 이미지는 도커 허브에서 pull하여 사용하고 nginx가 실행된 후 express가 실행되도록 docker compose를 작성하였다. build된 이미지를 사용하지 않고 이미지를 땡겨서 사용하는 것은 자동배포를 위함인데 그 순서를 확인해보자.

⚙ 자동배포 로직

  1. git에 새로운 코드 merge
  2. docker-compose donw
  3. 기존 이미지 삭제
  4. server의 docker 파일 build
  5. 빌드된 이미지 tag 및 push
  6. 기존 이미지 삭제
  7. docker-compose 실행
  8. 새로 push된 이미지로 container 실행

과 같은 로직으로 파이프라인을 작성하여 CI/CD를 구현해보려고 한다. 하지만 우선은 내가 원하는 시간에 build를 할수 있게 git에 연동하기 전에 파이프라인을 먼저 적용하여 실제 서버에 내가 원할때 빌드를 하여 적용할 수 있는지 확인해보려고 한다.

🔗 파이프라인 작성

젠킨스에서 새로운 Item을 생성하여 파이프라인으로 만들고

다음과 같이 설정 후 저장한다.

그럼 가장 아래에 파이프라인을 작성할 수 있는 부분이 있는데 groovy 언어를 몰라도 빨간색으로 표시해둔 버튼을 클릭하여 쉽게 작성할 수 있으니 두려워하지 말자. 우선 파이프라인은 간단하게 작성해보려한다.

먼저 기본 뼈대를 만들기 위해 Hello World를 선택하여 뼈대를 만들어주고

쉘 스크립트 명령어로만 진행할 것이므로 shell script를 클릭하고 스크립트를 작성해보자.

pipeline {
    agent any

    stages {
        stage('docker build') {
            steps {
                dir('/project/mainSite') {
                    sh 'docker build -t ililil9482/main_node ./'
                }
            }
        }
        stage('Dockerfile tag & push') {
            steps {
                dir('/project/mainSite') {
                    sh 'docker tag ililil9482/main_node ililil9482/main_node'
                    sh 'docker push ililil9482/main_node'
                }
            }
        }
        
        stage('docker-compose down') {
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: 'masServer', 
                            transfers: [
                                sshTransfer(
                                    execCommand: 'cd /compose/jenkins/project/mainSite && sudo docker-compose down && sudo docker-compose up -d', 
                                )
                            ]
                        )
                    ]
                )
            }
        }
        
    }
}

다음과 같이 작성했다.

이 과정중에 변경된 사항이 있는데

jenkins 내부에서 프로젝트를 빌드하고 이미지를 도커 허브에 올리기 위해 jenkins 컨테이너에 docker login을 해놓았다. 이것은 보안 문제가 발생할 수 있으므로 해당 이미지는 private하게 변경하여 진행했다!

실제 코드 적용

위 사진처럼 초록색으로 체크가 떠야 정상 진행된거다.

👍 이번 포스트를 진행하며 느낀점

  1. 파이프라인으로 ssh 까지 적용하려고하니 생각보다 오래 걸렸다.
  2. docker container의 특성을 알 수 있었다. 내가 알게된 특성은 docker container는 컨테이너가 실행될 때 사용된 명령어의 프로그램이 종료되면 컨테이너가 종료된다는 것이다. 그래서 pm2로 node를 daemon 실행했을 경우 컨테이너가 시작하자마자 종료되는 현상을 볼수 있는데
    🔨 이럴땐 pm2 log를 찍는 명령어나 bash 명령어 같은 죽지 않는 명령어를 실행시켜주어 문제를 해결 했다.
  3. 정상적으로 서버를 띄울수 있었는데 문제가(?) 발생한 것은 aws 서버 1대에 도커 컨테이너 서버와 젠킨스 서버를 모두 띄워놨는데 젠킨스 내부에 도커를 또 설치하여 도커 컨테이너 내부의 도커를 사용하는 형태로 진행했다. 그런데 젠킨스 서버에서 도커 컨테이너를 실행하거나 종료하면 외부 도커 컨테이너도 똑같이 종료되고 실행되는 것이다. 이러면 서버에 뭔가 충돌이 일어나거나 문제가 발생하지 않을까? 하는 걱정이 되는데
    🔨 지금 당장 외부 서버를 하나 더 만들기는 어려워서. 우선 이렇게 진행하고 프로젝트가 더 늘어났을 때 서버에 부하가 너무 심해서 멈추는 현상이 많아지면 다른 클라우드 플랫폼에서 서버를 하나 더 생성해서 젠킨스 서버를 따로 구성해야할 것 같다.
    🔨 현재 해결방법 : 집에 놀고 있는 라즈베리파이가 있어서 젠킨스 서버를 라즈베리로 변경하여 사용하니 aws에 부하도 걸리지 않고 잘 배포된다! 변경된 구성에 대한 글

좋은 웹페이지 즐겨찾기