
이번에는 Devops 개발론을 사용해 CI/CD 파이프라인을 만들어볼 것이다.
CI/CD란?
Continuous Integration (지속적 통합) 과 Continuous Deployment/Delivery (지속적 배포/전달) 의 약자로, 소프트웨어 개발에서 자동화된 빌드, 테스트, 배포 프로세스를 구축하는 방법론이다.
쉽게 말해 깃허브에 코드를 push하면 젠킨스에서 자동으로 원격 저장소에 있는 레포지토리를 실행시켜 서버에 배포한다.
젠킨스를 서버PC에 직접 설치해서 사용하는 경우도 있지만 나는 쉬운 서버 관리 및 프로젝트 폐기 시에 쉽게 종료할 수 있도록 젠킨스 도커에서 관리하기로 하였다.
지금 프론트엔드 및 백엔드 서버도 도커 컨테이너로 서버PC에서 작동하고 있는데 젠킨스 도커 컨테이너 안에 도커 컨테이너를 만드는 Docker in Docker는 보안상 취약하기 때문에 젠킨스 도커에서 내 PC에 접속할 수 있도록 변경하여 젠킨스 도커를 실행하려고 한다.
호스트의 Docker 컨테이너를 관리하려면 /var/run/docker.sock를 공유해야한다.
/home/woo(사용자) 에 docker-compose.yml을 만들어 안에 내용을 아래와 같이 적어준다.
version: "3.1"
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins:latest
privileged: true
user: root
restart: unless-stopped
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
- /home/woo/jenkins:/var/jenkins_home
docker-compose down
docker-compose up -d --build
그 후 위의 명령어로 도커를 실행하였다.
이제 docker-compose를 사용하기 위해서는 젠킨스 도커 컨테이너에서도 설치해줘야한다.
docker exec -it --user root jenkins sh로 루트 권한으로 젠킨스 컨테이너에 접근하여
curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
docker-compose를 설치했다.
groupadd docker && usermod -aG docker jenkins 이 명령어까지 적용하여 docker.sock를 사용할 수 있는 권한 그룹을 부여하자.
exit로 젠킨스 컨테이너를 빠져나온뒤
현재 디렉토리(자주 사용하는 디렉토리, 나는 /~에서 실행함)에서
mkdir jenkins
vi jenkins/Dockerfile
을 통해 jenkins 디렉토리에서 Dockerfile을 만들어 아래와 같이 입력하였다.
FROM jenkins/jenkins:jdk17
USER root
RUN apt-get update &&\
apt-get upgrade -y &&\
apt-get install -y openssh-client
이후 vi docker-compose.yml 명령어를 사용해 docker-compose 파일을 만들었다.
version: "3.1"
services:
jenkins:
container_name: jenkins
build:
context: jenkins
dockerfile: Dockerfile
restart: unless-stopped
user: root
ports:
- 8080:8080
- 50000:50000
volumes:
- /home/woo/jenkins:/var/jenkins_home
- /home/woo/.ssh:/root/.ssh
여기서 woo는 사용자 계정명으로 바꿔줘야한다!
그 후 docker-compose -f docker-compose.yml up --build -d 명령어를 입력해주면 jenkins 도커 컨테이너가 생기게 된다.
이제 IP:8080을 주소창에 입력해주면 젠킨스 사이트로 이동하게 된다.

이렇게 뜨게 되는데 여기에 비밀번호를 입력해야한다.
docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword 터미널에 이 명령어를 쓰게 되면 Administrator password가 나온다. 이걸 텍스트 입력란에 입력한다.

Install suggested plugins를 눌러 젠킨스를 설치한다.

계정명, 암호, 이메일 주소를 입력해 관리자 권한 회원가입을 마치면 Jenkins URL:에 기본 입력된 값을 그대로 두고 다음을 누른다.

드디어 젠킨스 메인페이지로 오게 된다.
깃허브 사이트로 가서 해당 레포지토리의 권한이 있는 아이디로 로그인한다.
Settings -> Developer Settings -> Personal access tokens -> Generate new token -> Generate new token (classic)로 이동하면 사용자 인증을 하게된다.
인증이 마치면 Note에는 토큰 사용 용도를 적어주자.
나는 winection-jenkins로 하였다. 갱신 기간은 편하게 Noexpiration으로 두어 기간을 설정하기 않았다.


내가 사진으로 체크한 것을 똑같이 체크해준다. 그 후 하단의 Generate token 버튼을 누르면 토큰 발급이 완료된다. 발급한 토큰을 복사해준다.

다시 젠킨스 사이트로 이동해서 Jenkins 관리 -> System으로 이동해서 아래로 스크롤하면

Add GitHub Server -> GitHub Server을 누르면 입력할 수 있는 텍스트 창이 생긴다.
+Add -> Jenkins를 누르면 또 다른 모달이 생긴다.

Add -> Credentials에 내가 만든 토큰을 추가해준다.


Test connection을 누르면 정상적으로 되는지 테스트할 수 있다.
하단 Save버튼을 눌러 저장해준다.
이제 지속적인 배포를 위한 파이프라인을 만들어 볼 것이다.
왼쪽에 있는 새로운 item을 누른다.

item name은 우선 프론트엔드부터 만들거라 frontend-deploy로 하겠다.
Pipeline을 누르고 하단의 OK버튼을 누르자.

GitHub project에 해당 파이프라인을 이용하는 레포 주소를 입력한다.
밑으로 내려가면 Triggers에 GitHub hook trigger for GITScm polling을 선택한다.

pipeline도 사진과 같이 바꿔준다.

ㄱRepository URL에 자기 레포 주소를 입력한다.

나는 main에 있는 브랜치를 배포할꺼기에 이렇게 설정해두었다.
이 과정이 완료되면 하단의 Save를 눌러준다.
다시 깃허브 사이트로 이동해서 배포하려는 레포로 가보자
해당 레포에서 Settings -> Webhoos -> Add webhook
Payload URL에 http://젠킨스 사이트 주소/github-webhook/(http://IP:8080/github-webhook/)를 적어준다.
Content type 에는 application/json으로 설정한다.
그 후 하단의 Add webhook 버튼을 누른다.

일정 시간이 지나고 새로고침하면 url 옆에 회색 원이 체크로 바뀌게 된다.
한번 프로젝트의 리드미를 변경하고 push를 해보자

이것처럼 오류가 날 것이다. 아직 프로젝트 루트 디렉토리에 Jenkinsfile을 만들어두지 않았기 때문이다. 아무튼 깃 웹훅이랑 젠킨스가 서로 상호작용한다는 것을 확인하게 되었다.
이제 Jenkinsfile만 추가하면 끝난다.
깃 레포에 올리는 프로젝트의 루트 디렉토리로 가서 Jenkinsfile을 만들어준다.
해당 파일에는
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/DongUgaUga/winection-web.git'
}
}
stage('Deploy') {
steps {
script {
sh "docker-compose down"
sh "docker-compose up -d --build web"
}
}
}
}
}
로 적었다.
이제 깃에 add, commit, push과정을 거치면 젠킨스가 작동한다.
제대로 작동되는지 보려면 젠킨스 사이트로 가면

이렇게 웹훅을 받아 실행중인 것을 볼 수 있다.
여기서 frontend-deploy를 누르면 현재 실행한 파이프라인을 확인이 가능해진다.

그중 가장 최신 것을 누르고(내 기준 Last build, (#9),3 min 44 sec 전)
왼쪽의 Console Output를 누르면 현재 어떻게 되어가는지 로그를 확인할 수 있다.
이렇게 프론트엔드의 ci/cd 파이프라인을 구성하게 되었다.
마찬가지로 백엔드 파이프라인을 만들어준다.
나는 프론트엔드랑 비슷하게 파이프라인 이름을 backend-deploy로 지정했다.

.env파일은 보통 환경 변수 파일이기에 api 키 등 여러 중요한 정보를 담고 있다.
그렇기에 깃허브에 안올리는데 그럼 깃허브에서 배포하는 우리는 이걸 어떻게 해결해야하나?
젠킨스에서 배포 시에 환경 변수 파일을 주입하여 이를 해결할 수 있다.


Jenkins 관리 -> Credentals

Stores scoped to Jenkins의 Domains -> (global)을 누르면 저번에 추가한 credentials인 git-jenkins가 나온다. 이처럼 또 다시 추가할 것이다.
오른쪽의 + Add Credentials 버튼을 눌러 추가한다.


Secret text로 가서 Secret에는 값, ID에는 pipeline에서 인식할 id를 넣어준다.
Create를 눌러 저장하고, 마찬가지로 넣어야하는 모든 환경 변수의 값을 여기에서 추가한다.

이렇게 추가한다.
(deepseek는 나중에 chatapi로 변경할 예정)
젠킨스에서 환경변수를 설정했으니깐 Jenkinsfile과 docker-compose.yml에서 설정한 환경변수를 불러와야한다.
프론트엔드에서 먼저 수정하면
Jenkinsfile
pipeline {
agent any
environment {
VITE_SERVER_URL = credentials('vite_server_url')
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/DongUgaUga/winection-web.git'
}
}
stage('Setup') {
steps {
script {
sh '''
echo "VITE_SERVER_URL=$VITE_SERVER_URL" > .env
chmod 600 .env
'''
}
}
}
stage('Deploy') {
steps {
script {
sh "docker-compose down"
sh "docker-compose up -d --build web"
}
}
}
}
}
이렇게 environment에서 젠킨스에 설정한 환경변수를 불러오고, Setup에서 불러온 환경변수를 .env에 넣어주었다. 보안을 위해 chmod 600 .env까지 추가했다.
docker-compose.yml에는
version: "3.8"
services:
web:
build:
context: .
dockerfile: Dockerfile
container_name: web
ports:
- "3000:3000"
restart: always
env_file:
- .env
networks:
- app-network
networks:
app-network:
env_file을 추가하면 끝난다.
이제 다시 깃에 add, commit, push과정을 거치면 환경변수까지 배포되는 것을 확인할 수 있다.
백엔드도 이와 같이 적용시켰다.
조만간 CI/CD 적용해보려고 했는데 참고하겠습니다.