Jenkins, Nginx, CertBot 학습 정리

김진회·2022년 11월 22일
0

CI/CD

목록 보기
4/7


본문에서는 젠킨스 실제 구현한 내용과 순서에 대해 자세히 다루고, 파이프라인에 대해서는 아래 포스트에서 다루겠다.
👉 [CI/CD] Jenkins Pipeline 정리

Jenkins란?

  • SW 개발 시 지속적으로 통합 서비스를 제공하는 툴이다. 깃과 같이 공유 레퍼지토리에 프로젝트를 새로 merge를 할 때마다 젠킨스에서 자동으로 build를 해서 자동으로 배포 서버를 업데이트 해준다.
  • 기존 수동으로 빌드를 해주는 방식을 자동으로 배포하는 방식으로 바꿔주는 시스템때문에 많은 개발자들이 사용한다.
  • 장점
    • 프로젝트 표준 컴파일 환경에서의 컴파일 오류 검출
    • 자동화 테스트 수행
    • 정적 코드 분석에 의한 코딩 규약 준수여부 체크
    • 프로파일링 툴을 이용한 소스 변경에 따른 성능 변화 감시
    • 결합 테스트 환경에 대한 배포 작업
    • 편리한 설정, 플러그인 설치

Nginx란?

https://velog.io/@kku64r/nginx

CertBot란?

  • SSL 증명서를 발급해주는 프로그램으로 기존에는 https를 위해 SSL 인증서를 구매해야 했지만 개인용에서는 쉽고 간편하게 무료로 SSL을 발급할 수 있게 해준다.

0. 참고사항

  • 보안상 폴더이름, 계정이름 등 주요 이름들은 가명처리를 하였음
  • AWS EC2 서버에 도커를 설치해서 사용했음

1. 기본 설치

  1. EC2 인스턴스에 접속하기
  2. 젠킨스 도커 이미지 다운
sudo docker pull jenkins/jenkins:lts
  1. 젠킨스 폴더 만들기
mkdir jenkins_build
cd jenkins_build
  1. 젠킨스 컨테이너 실행 위한 Dockerfile 작성
cat > Dockerfile

Dockerfile

FROM jenkins/jenkins:lts

USER root

# install docker
RUN apt-get update && \
    apt-get -y install apt-transport-https \
        ca-certificates \
        curl \
        gnupg2 \
        zip \
        unzip \
        software-properties-common && \
    curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
    add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
    $(lsb_release -cs) \
    stable" && \
    apt-get update && \
    apt-get -y install docker-ce
  1. 컨테이너 설정 위한 docker-compose 작성
cat > docker-compose.yml
version: '3.7'
services:
  jenkins:
    build:
      context: .
    container_name: jenkins
    user: root
    privileged: true
    ports:
      - 젠킨스포트:8080
      - 50000:50000
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
  1. docker compose 한다. 비밀번호 확인하기 위해서 백그라운드 실행은 X
sudo docker-compose up
  1. *** 사이에 있는 비밀번호 ctrl+c

  2. gui를 사용한다면 해당 EC2 인스턴스에서 젠킨스 접속

http://juso.p.ssafy.io:젠킨스포트
  1. 저장했던 비밀번호 입력 > 왼쪽 박스(instal...) 클릭 > 설치 후, 계정 만들기
계정명: 프로젝트이름 (임의)
암호: 비밀번호 (임의)
암호확인: 비밀번호 (임의)
이름 : 프로젝트이름 (임의)
이메일주소: 이메일주소
  1. 다음 화면에선 그냥 Save and Finish 누른다

2. 설정

  1. 플러그인 설치
    왼쪽 메뉴의 Jenkins 관리 > 플러그인 관리 > 설치 가능 > gitlab, nodejs(프론트 젠킨스 배포가 없으면 nodejs패스)를 설치한다.
  2. Gitlab 연동을 위한 Credentials 생성
    jenkins 관리 > Manage Credentials > Stores scoped to Jenkins의 Domains (global) 클릭 > Add Credentials 클릭 > Username with password 클릭 후, 깃랩 이메일과 비밀번호를 넣어주고 생성한다.
  3. Gradle, JDK, NodeJS 설정
    • Gradle: Jenkins 관리 > Global Tool Configuration > Gradle의 Add Gradle > 현 프로젝트에서는 Gradle7.5 사용 > Save
    • NodeJS: NoName은 node16(임의), install automatically체크, version은 nodejs 16.16.0, global npm pa...는 72
    • JDK: JDK11은 컨테이너에 따로 설치를 먼저 해줘야 함 (JDK9까지 있기때문)
      docker ps > jenkins 컨테이너 id확인 > docker exec -itu 0 컨테이너ID /bin/sh > sudo apt-get update > apt-get install openjdk-11-jdk > java --version(버전확인)
      젠킨스에서 name은 jdk11, java_home은 /usr/lib/jvm/java-11-openjdk-amd64 입력
  4. 젠킨스 안에서 도커 컴포즈 설정을 위해 추가 작업을 해야 함
    • 젠킨스 컨테이너 접속
    docker exec -it 컨테이너ID /bin/sh
    • docker-compose 다운
    curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    • 권한 부여
    chmod +x /usr/local/bin/docker-compose

3. 백엔드 CI/CD

  1. jenkins 왼쪽 메뉴의 '새로운 item' > 이름 입력후 Pipeline 클릭, Ok 클릭
  2. Webhook 설정을 하기 위해 깃랩 저장소로 이동해서 Settings > Webhooks 클릭
  3. 깃랩 해당 페이지의 URL 입력칸에 Jenkins의 Build Triggers에 나와있는 주소를 적는다.( (ex. http://juso.p.ssafy.io:3333/project/이름)
  4. Build Triggers > Build when... 체크 > 고급 > Secret token의 Generate 클릭 후, 토큰 복사 > 깃랩 Webhooks 페이지의 Secret token에 붙여넣기
  5. 깃랩 Webhooks의 Trigger의 Push events(체크)에 배포 브랜치 이름을 적는다. (ex. be/dev)
  6. Add Webhook 클릭 > Test 진행 > 젠킨스의 Stage View를 보면 테스트 성공 여부가 보인다.
  7. 젠킨스의 Pipeline > Definition의 pipeline script > 밑에 파이프라인 작성
pipeline {
    agent any
    tools {gradle "gradle7.5"}
    stages {
        stage('Prepare') {
            steps {
                echo 'Clonning Repository'
                git url: '깃랩주소',//깃랩주소
                    branch: 'backend/dev',//브런치이름
                    credentialsId: '크레덴셜아이디'//credentialsId
            }
            post {
                success {
                    echo 'Successfully Cloned Repository'
                }
                failure {
                    error 'This pipeline stops here...'
                }
            }
        }
        stage('Copy Properties') {
            steps {
                echo 'Copy Properties'
                sh 'cp ./properties/application-db.yml garden-be/src/main/resources'//gitIgnore파일(properties)를 서버 실행할 때마다 자동으로 옮겨줌. 더 있으면 추가로 작성
            }
            post {
                success {
                    echo 'Successfully Copied'
                }
                failure {
                    error 'This pipeline stops here...'
                }
            }
        }
        stage('Build Gradle') {
            steps {
                echo 'Build Gradle'
                dir ('./프로젝트-be') {//디렉토리 프로젝트에 맞춰서 변경
                    sh """
                        gradle clean build --exclude-task test
                    """
                }
            }
            post {
                success {
                    echo 'Successfully Builded'
                }
                failure {
                    error 'This pipeline stops here...'
                }
            }
        }
        stage('Copy Jar') {
            steps {
                echo 'Copy Jar'
                sh 'rm back/backend-0.0.1-SNAPSHOT.jar || true'//디렉토리 프로젝트에 맞춰서 변경
                sh 'mv ./garden-be/build/libs/프로젝트-0.0.1-SNAPSHOT.jar ./back'//디렉토리 프로젝트에 맞춰서 변경
            }
            post {
                success {
                    echo 'Successfully Copied'
                }
                failure {
                    error 'This pipeline stops here...'
                }
            }
        }
        stage('Compose Down') {
            steps {
                echo 'Down Docker'
                sh 'docker-compose -f docker-compose-back.yml down -v'
                echo 'docker rmi start...'
                // sh 'docker stop -f $(docker ps -a -q -f name=nginx)'
                // sh 'docker rm -f $(docker ps -a -q -f name=nginx)'
                sh 'docker rmi -f com_backend'//실행 전 backend 도커 이미지 및 컨테이너 삭제
                sh 'docker rmi -f com_nginx'//실행 전 nginx 도커 이미지 및 컨테이너 삭제
            }
            post {
                success {
                    echo 'Successfully Build Down'
                }
                failure {
                    error 'This pipeline stops here...'
                }
            }
        }
        stage('Compose Up') {
            steps {
                echo 'Push Docker'
                sh 'docker-compose -f docker-compose-back.yml up -d'
            }
            post {
                success {
                    echo 'Successfully Up'
                }
                failure {
                    error 'This pipeline stops here...'
                }
            }            
        }
    }
}
  • gitIgnore되는 properties.yml 파일들은 jenkins_home/workspace/프로젝트-Back에 properties폴더에 넣어놨음
  • jenkins_home/workspace/프로젝트-Back에 docker-compose-back.yml을 작성해놨음 (mysql, redis, nginx(certbot)관련 설정을 함)
  • docker-compose-back.yml
version: '3.7'
services:
  db:
    image: mysql:8.0.28
    expose:
      - "MYSQL포트"
    container_name: db
    volumes:
      - /home/ubuntu/deploy/db/conf.d:/etc/mysql/conf.d
      - /home/ubuntu/deploy/db/db/data:/var/lib/mysql
      - /home/ubuntu/deploy/db/db/initdb.d:/docker-entrypoint-initdb.d
    environment:
      MYSQL_DATABASE: 프로젝트DB이름
      MYSQL_ROOT_PASSWORD: "비밀번호"
  nginx:
    container_name: nginx
    build:
      dockerfile: Dockerfile
      context: ./nginx
    image: com_nginx
    expose:
      - "80"
      - "443"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /home/ubuntu/jenkins_build/jenkins_home/workspace/프로젝트-Back/back:/back
      - /home/ubuntu/deploy/certbot:/certbot
  backend:
    container_name: backend
    restart: on-failure
    build:
      dockerfile: Dockerfile
      context: ./back
    image: com_backend
    expose:
      - "8080"
    #ports:
    #  - "8080:8080"
    environment:
      SERVER_PORT: 8080
      SPRING_DATASOURCE_URL: jdbc:mysql://db:MYSQL포트/프로젝트DB이름?serverTimezone=Asia/Seoul&useLegacyDatetimeCode=false&useUnicode=true&characterEncoding=utf8
      SPRING_DATASOURCE_USERNAME: 유저이름
      SPRING_DATASOURCE_PASSWORD: "비밀번호"
      SPRING_REDIS_HOST: redis_boot
      SPRING_REDIS_PORT: 레디스포트
    depends_on:
      - db
      - redis_boot
  redis_boot:
    image: redis:alpine
    command: redis-server --port 레디스포트
    container_name: redis_boot
    hostname: redis_boot
    labels:
      - "name=redis"
      - "mode=standalone"
    expose:
      - "레디스포트"
    volumes:
      - /home/ubuntu/deploy/redis:/data
networks:
  default:
    name: com_net
    external: true
  • Garden-Back에 nginx 폴더에 Dockerfile, default.conf 파일을 만듦
  • Dockerfile
FROM nginx:stable-alpine

COPY ./default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
upstream backend {
    server backend:8080;
}

server{
    listen 80;
    listen [::]:80;

    server_name juso.p.ssafy.io; (서버URL)

    location / {
      return 301 https://$host$request_uri;
    }

    location /.well-known/acme-challenge/ {
        root /certbot;
    }
}

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    server_name  juso.p.ssafy.io; (서버URL)
    access_log   /var/log/nginx/nginx.vhost.access.log;
    error_log    /var/log/nginx/nginx.vhost.error.log;

    ssl                  on;
    ssl_certificate      /certbot/etc/live/juso.p.ssafy.io(서버URL)/fullchain.pem;
    ssl_certificate_key  /certbot/etc/live/juso.p.ssafy.io(서버URL)/privkey.pem;

    location /api {
        proxy_pass http://backend;
        proxy_set_header Host              $http_host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

}
  • Garden-Back에 Dockerfile을 만들어놨음
  • Dockerfile
FROM openjdk:11-jdk
ARG JAR_FILE=./*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]

4. CertBot 이용 SSL 인증서

  1. mkdir makecertbot 생성 (단순히 certbot 생성 작업할 디렉토리)
  2. 해당 디렉토리에서 cat > docker-compose.yml
  • docker-compose.yml
version: "3.3"
services:
  nginx:
    image: nginx:latest
    volumes:
        - ./nginx/conf.d:/etc/nginx/conf.d
        - ./nginx/log:/var/log/nginx
        - ./www:/var/www/html
    ports:
        - 80:80
  certbot:
    restart: "no"
    depends_on:
      - nginx
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./certbot/etc:/etc/letsencrypt
      - ./certbot/var:/var/lib/letsencrypt
      - ./www:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email 이메일주소 --agree-tos --no-eff-email --force-renewal -d juso.p.ssafy.io(사용할/서버URL)
  1. 도커 컴포즈
docker-compose up
  • Successfully received certificate가 나오면 정상적으로 설치가 된 것
  • 실패하면 1시간 후 혹은 1주일 후에 생성할 수 있음 (동일 주소 요청으로 주 50회 제한. 본 프로젝트에서는 첫 시도부터 안돼서 1주일 후에 발급받음. 기다릴 시간이 없으면 다른 발급 기관에서 발급받기를 추천)
  1. docker-compose-back.yml 파일의 내용에 맞춰 생성된 certbot 폴더를 옮겨주기

5. 최종 CI/CD 디렉토리

- /home/ubuntu
	└─ /deploy/certbot/... (CertBot 키)
	└─ /jenkins_build
		└─ docker-compose.yml (jenkins)
			└─ /jenkins_home/workspace/프로젝트-Back
				└─ /프로젝트-be ( 깃랩 클론 파일)
				└─ docker-compose-back.yml (backend)
				└─ /back
					└─ Dockerfile
					└─ 프로젝트-0.0.1-SNAPSHOT.jar
				└─ /nginx
					└─ default.conf
					└─ Dockerfile
				└─ /properties
					└─ application-db.yml와 같은 gitignore파일

이미지 출처

https://seongwon.dev/DevOps/20220717-CICD구축기2/

참고

yoongh97@gmail.com('컴설턴트' 팀원)의 작성글 참고

profile
SSAFY 7기. HMG. 협업, 소통, 사용자중심

0개의 댓글