삽질블로그

Docker와 Jenkins를 이용해서 CI/CD를 구축해보자 본문

개발

Docker와 Jenkins를 이용해서 CI/CD를 구축해보자

삽질블로그 2024. 4. 16. 19:36

개인 프로젝트를 진행하면서 프론트는 Vercel로 배포하여 자동배포와 https를 적용하였는데, 

서버는 아직 자동배포 환경을 만들어놓지 않아서 이번에 Jenkins로 구축을 하였습니다.

 

Jenkins 란?

Jenkins는 지속적인 통합 및 지속적인 배포 (CI/CD)를 지원하는 오픈 소스 자동화 도구입니다.

Jenkins는 자바 기반 웹 애플리케이션이며,  빌드, 테스트, 배포를 자동화해 줍니다.

 

CI / CD 란?

CI/CD는 지속적 통합(Continuous Integration) 및 지속적 제공/배포(Continuous Delivery/Deployment)를 의미하며,

소프트웨어 개발 라이프사이클을 간소화하고 가속화하는 것을 목표로 합니다.

CI(지속적 통합)은 코드 변경 사항을 공유 소스 코드 리포지토리에 자동으로 자주 통합하는 사례를 나타냅니다. 

CD(지속적 제공/배포)는 코드 변경 사항의 통합, 테스트, 제공을 나타내는 프로세스로, 두 가지 부분으로 구성됩니다.

지속적 제공에는 자동 프로덕션 배포 기능이 없는 반면, 지속적 배포는 업데이트를 프로덕션 환경에 자동으로 릴리스합니다.

 

배포 흐름

배포 흐름은 다음과 같은데, 사실 도커 허브를 안써도되지만 이번에 도커 허브를 통해

자동배포 하는 법을 알아보고 싶어서 사용하게 되었습니다.

글에서 이상한 부분이 있다면 지적해주시면 감사하겠습니다.

  1. Jenkins 서버에서 깃허브 코드 클론받기
  2. 실행중인 도커 컨테이너 제거 후 이미지 제거
  3. 도커 이미지 빌드하기
  4. 도커 허브에 이미지 푸시하기
  5. 배포하기

배포 환경

  • AWS ec2 amazon/linux t2micro

 

1.  환경세팅

- 도커 설치

   ec2 서버에 접속 후 다음 명령어로 도커를 설치해줍니다.

sudo yum update -y
sudo yum install -y docker
sudo service docker start

 

- 도커 컴포즈 설치

   저는 docker-compose 파일도 이용하기 때문에 다음 명령어로 docker-compose도 설치해줍니다.

// 최신 docker compose를 해당 링크에서 받을 수 있음
sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

// 권한 부여
sudo chmod +x /usr/local/bin/docker-compose

// 설치 확인
docker-compose version

 

- 젠킨스 설치

   저는 ec2인스턴스에 docker-compose-jenkins.yaml파일을 만들어서 젠킨스를 설치해주겠습니다.

   원하는 경로에서 다음과 같이 적어줍시다.

 

vim 원하는파일명.yaml

 

i를 입력하여 입력모드로 변환 후 다음과 같이 적어준 후 esc -> : wq!로 저장하고 나와줍니다.

권한오류 뜨면 sudo vim으로 파일 생성하시면 됩니다.

version: '3.8'

services:
  my_jenkins:
    container_name: my_jenkins
    image: jenkins/jenkins
    ports:
      - "8080:8080"
    volumes:
      #- [원하는 볼륨 경로]:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker
      - /usr/local/bin/docker-compose:/usr/local/bin/docker-compose
    user: root
    restart: unless-stopped

 

 

1. continaer_name : my_jenkins로 컨테이너 이름을 설정해줬습니다.

 

2. image : 도커 허브에 있는 jenkins/jenkins 이미지를 가져옵니다.

 

3. port : 젠킨스의 기본 포트가 8080이라 8080:8080으로 설정해줬습니다.

 

4. volumes: jenkins컨테이너의 데이터(도커 데몬)와 호스트의 데이터간에 통신하기 위해서 다음과 같이 작성하였습니다.

저는 docker out of docker방식을 사용하였습니다.

아래 문서를 보면 docker out of docker 방식을 권장하는 것을 확인할 수 있습니다.

https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/

 

Using Docker-in-Docker for your CI or testing environment? Think twice.

The primary purpose of Docker-in-Docker was to help with the development of Docker itself. Many people use it to run CI (e.g. with Jenkins), which seems fine at first, but they run into many “interesting” problems that can be avoided by bind-mounting t

jpetazzo.github.io

 

 

- Docker Out of Docker

컨테이너 외부에 존재하는 도커 호스트의 기능을 빌려서 사용하는 방식입니다. 

소켓 파일을 컨테이너와 공유하면 소켓 파일을 통해 도커 명령을 수행할 수 있습니다.

컨테이너 내부에서 도커 명령을 수행하게 되면 소켓을 통해 호스트에서 실행중인 도커 데몬에게 전달되게 됩니다.

docker socket파일은 /var/run/docker.sock에 있습니다.

 

 

 

- Docker In Docker

Docker 컨테이너 내에서 Docker 데몬을 실행하는 기술입니다.

이는 Docker 컨테이너 안에서 다른 Docker 컨테이너를 빌드하고 실행하는 등의 작업을 수행할 수 있도록 해줍니다.

DinD 방식을 선택하면 jenkins내에 도커를 또 설치하여 도커 명령어를 실행해야 합니다.

 

 

Jenkins 설치

다음 명령어로 docker-compose-jenkins.yaml을 실행합니다.

docker-compose -f docker-compose-jenkins.yaml up -d

 

실행했다면 jenkins의 관리자 비밀번호를 알아야합니다. 다음 명령어를 입력해줍시다.

docker logs {컨테이너 이름}
ex) docker logs my_jenkins

 

입력하면 This may also~~ 위에 값을 기억해야 합니다.

 


 

2.  깃허브 세팅

1. Jenkins에서 깃허브에 접근하기 위해 깃허브 Access토큰을 발급해야 합니다.

2. 깃허브에 코드를 푸시 또는 병합할 때 액션을 취하기 위해 webhook설정을 해야합니다.

 

깃허브 토큰 발급

깃허브 계정에서 setting에 들어가서 왼쪽 하단에 있는 developer setting에 들어가줍니다.

developer setting

 

token 발급 화면

 

 

 

repo
repo_hook

저는 repo에 접근하기 위해 repo와 hook을 위한 repo_hook을 체크하였습니다.

만료기한과 그 외 접근권한을 필요에 따라서 설정하시면 됩니다.

발급한 토큰은 다시 받을 수 없으므로 따로 저장해놓으셔야 합니다.

 

깃허브 웹훅

깃허브에 소스를 푸시하거나 병합할 때 깃허브가 액션을 날려야하니 웹훅 설정을 해줍시다.

먼저 소스코드가 있는 레포지토리의 setting에 들어가서 webhooks으로간 후 add webhook을 눌러줍니다.

 

github webhook

 

webhook 만들기

 

다음과 같이 입력 후 웹 훅을 만들면 깃허브에서 브런치의 변경사항을 감지하고 payload URL에 web hook을 날립니다.

 

3.  젠킨스 세팅

https://{EC2 인스턴스 IP 주소}:8080으로 젠킨스에 접속해준 후 

화면에서 나오는 비밀번호 입력창에 위에서 docker logs를 통해 확인한 값을 입력해줍니다.

 

젠킨스 접속

 

 

로그인 후 플러그인 설치를 해줍니다.

젠킨스 플러그인 설치
계정 생성

플러그인을 설치했다면 다음을 입력해줍시다.

여기서 계정명과 암호는 나중에 로그인할 때 사용할 ID와 비밀번호 입니다.

 

크레덴셜 세팅

도커 허브와 깃허브에 로그인하기 위해 크레덴셜 세팅을 해줍니다.

 

젠킨스 메인
Manage Jenkins

 

Add credentials

 

 

깃허브 크레덴셜 추가

 

username : 깃허브에 로그인할 때 사용하는 아이디를 적어줍니다.

password : 아까 발급받은 깃허브 Access 토큰을 적어줍니다.

ID : 크레덴셜 아이디를 정해줍니다. ex) 깃허브 크레덴셜

Description : 크레덴셜 설명을 적어줍니다. ex) 깃허브 크레덴셜 입니다.

 

 

도커허브 크레덴셜 추가

 

도커허브도 마찬가지로 크레덴셜을 만들어줍니다.

username : 도커허브 ID를 적어줍니다.

ID : docker-hub로 적어줍니다.

 

 

4. 젠킨스 파이프라인 설정

이제 파이프라인을 만들어봅시다.

젠킨스 대시보드에서 new item을 클릭한 후 원하는 이름과 파이프라인을 선택합니다.

pipeline

 

 

 

생성한 파이프라인의 configure에 들어가서 다음을 체크해줍니다.

 

pipeline configure

Do not allow concurrent builds : 동시에 빌드하는 것을 방지합니다.

GitHub hook trigger for GITScm polling : 깃허브 웹훅을 받으면 이를 확인합니다.

 

Pipe line script

저는 다음과 같이 파이프라인 스크립트를 적어줬습니다.

node{
    withCredentials([[$class: 'UsernamePasswordMultiBinding',
    
    credentialsId: 'docker-hub',
    
    usernameVariable: 'DOCKER_USER_ID', 
    
    passwordVariable: 'DOCKER_USER_PASSWORD']]) { 
            
            stage('Pull') { // 깃허브 코드를 가져옴 젤 첨에 없으면 오류떠서 클론 한 번 받고 pull로 변경
                git branch: 'main', credentialsId: 'github-credential', url: 'https://github.com/{브런치 주소}'
            }
            
            stage('Unit Test') { //테스트 하는 부분인데 없어서 작성안함
            }
            
            stage('Remove Previous') {
                // 이전에 있던 컨테이너와 이미지 제거
                sh(script: 'docker-compose -f docker-compose-pull.yaml down') // 컨테이너 제거
                sh(script: 'docker rmi ${DOCKER_USER_ID}/algorithm-server:latest') // 이미지 제거
                //sh(script: 'docker rmi ${DOCKER_USER_ID}/algorithm-mysql:latest') // 이미지 제거
            }
            
            
            stage('build'){ // 도커 빌드하는 부분
                sh(script: 'docker-compose -f docker-compose-push.yaml build')
            }
            
            /*
            stage('Tag') { // 도커 태그 변경하는 부분 -> 필요없으면 안해도됨
                
                sh(script: '''${DOCKER_USER_ID}/jenkins-test \
    
                ${DOCKER_USER_ID}/jenkins-test''') 
                
            }
            */
            stage('Push') { // 도커 허브에 이미지 푸시

                sh(script: 'docker login -u ${DOCKER_USER_ID} -p ${DOCKER_USER_PASSWORD}') 
    
                sh(script: 'docker push ${DOCKER_USER_ID}/algorithm-server:latest')
                
                //sh(script: 'docker push ${DOCKER_USER_ID}/algorithm-mysql:latest')
                
          }
              stage('Deploy') { // 도커 이미지로 배포
    
              sh(script: 'docker-compose -f docker-compose-pull.yaml up -d')
    
          }
          
          

    }
}

 

스크립트를 한 번에 작성하지말고 단계마다 작성 후

Build now를 통해 수동 빌드하여 오류를 잡는게 더 편합니다.

 

 

4. 사용한 도커파일들

저는 docker-compose 파일을 빌드용, 배포용으로 두 개 만들어서 소스코드와 같이 깃허브에 업로드를 해놓았습니다.

docker-compose파일 하나에 전체 코드를 쓸 수 있었지만 저는 두 개를 작성하였습니다.

도커 컴포즈파일

 

docker-compose-pull.yaml

이 파일은 도커허브에 있는 이미지 파일을 불러와서 컨테이너를 실행할 때 사용하는 docker-compose 파일입니다.

services:
  mysql:
    image : mysql:8
    container_name: mysql_server
    env_file: 
      - ./.env
      - ./env/.env.production
    restart: unless-stopped
    ports:
      - "3306:3306"
    volumes:
      - db:/var/lib/mysql
    networks:
      - server_network

  node:
    image: ${DOCKER_USER_ID}/algorithm-server
    container_name: node_server
    restart: unless-stopped
    env_file: 
      - ./.env
      - ./env/.env.production
    ports:
      - "8000:8000"
    depends_on:
      - mysql
    stdin_open: true
    tty: true
    networks:
      - server_network
volumes:
    db:
networks:
  server_network:

 

docker-compose-push.yaml

이 파일은 도커파일을 참조하여 이미지를 빌드하기 위해 사용하는 파일입니다.

후에 이 파일을 통해 만들어진 이미지 파일을 도커 허브에 푸시합니다.

version: '3.7'

services:
  node:
    platform: linux/amd64
    build:
      context: ./
      dockerfile: Server.Dockerfile
    image: ${DOCKER_USER_ID}/algorithm-server

 

Server.Dockerfile

제가 작성한 코드를 빌드하기 위해 작성한 도커파일입니다.

FROM node:16

WORKDIR /app

COPY package.json /app

COPY tsconfig.json /app

RUN npm install

#RUN npm install -g ts-node

COPY . /app

EXPOSE 8000

CMD ["npm", "start"]

 

5. 환경변수 설정

소스코드에서 사용하는 환경변수를 사용하기 위해 젠킨스에서 따로 설정해줄 수 있지만,

jenkins안에 환경변수를 참조하는 폴더에 직접 환경변수 파일을 만들어서 설정해줬습니다.

 

다음 명령어로 젠킨스 컨테이너에 접속해줍니다.

docker exec -it my_jenkins bash

 

다음 명령어로 workspace 디렉토리를 들어가면 파이프라인을 만들 때 작성한 이름이 있습니다.

거기에 접속해줍니다.

cd /var/jenkins_home/workspace/

 

저 같은 경우는 CI_CD로 작성하여 이 폴더로 이동했습니다.

cd CI_CD

 

 

이제 이 폴더에서 환경변수 파일을 만들면 도커에서 참조해줍니다.

하지만 vim 으로 파일을 생성하지 못하니 echo 명령어로 파일을 만들어줍니다.

echo "변수1=값1" > .env

 

만약 폴더를 만들고 거기에 파일을 만들고 싶다면 mkdir로 폴더를 만들어준 후 파일을 작성해줍니다.

mkdir 폴더이름

 

이렇게하면 환경변수 세팅도 마쳤습니다.

이제 깃허브 브런치에 푸시하면 

 

빌드 stage

위와 같이 배포가 진행됩니다.

 

 

 

 

 

참고자료 : 

 

https://www.redhat.com/ko/topics/devops/what-is-ci-cd#%EC%A7%80%EC%86%8D%EC%A0%81-%ED%86%B5%ED%95%A9

 

https://velog.io/@wijoonwu/AWS-Jenkins-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%AC

 

https://velog.io/@sihyung92/%EC%9A%B0%EC%A0%A0%EA%B5%AC2%ED%8E%B8-%EC%A0%A0%ED%82%A8%EC%8A%A4-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94#%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0

 

https://velog.io/@rnqhstlr2297/Docker%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Jenkins-CICD-%EA%B5%AC%ED%98%84

 

https://ryu-e.tistory.com/12

 

'개발' 카테고리의 다른 글

NestJS 예외 핸들링  (1) 2024.07.22
개인 프로젝트 후기  (3) 2024.07.19
Nginx SSL인증을 통해 HTTPS 적용  (0) 2024.04.15
알고리즘 리뷰 사이트 만들기(1)  (0) 2024.03.11
간단한 프로젝트 만들기  (0) 2024.03.02
Comments