팀 프로젝트를 진행하면서 젠킨스, Docker를 이용하여 CICD를 구축했다. 이 때 서버 배포 시 중단되는 현상을 방지하기 위하여 Nginx를 이용하여 블루/그린 무중단 배포를 구현했다. 구축 과정에서 다들 밤새고 착오가 굉장히 많아서 그 과정을 기록하려고 한당.
간단하게 위와같이 동작한다.
EC2는 총 2개가 필요함. 젠킨스는 깃허브 액션과 달리 따로 서버를 구축해줘야한당.
따라서 젠킨스 서버 + 스프링부트 배포 서버 준비!
https://hudi.blog/install-jenkins-with-docker-on-ec2/
위 블로그를 참고하여 설치한다. 근데 docker-compose를 실행시키는 과정에서 다음과 같은 에러가 계속 발생했다.
⚠️ docker.errors.DockerException: Error while fetching server API version: Connection aborted 오류 발생
이 문제는 Docker와 호환되지 않는 버전의 Python requests 패키지로 인한 것으로 보였다. Docker Python SDK가 사용하는 requests 패키지 버전과 호환되는 것을 사용해야함.
위 명령어를 통해 requests 버전을 바꿔준다.
⚠️ ModuleNotFoundError: No module named 'requests'
ERROR: Cannot uninstall urllib3 2.0.7, RECORD file not found. Hint: The package was installed by debian.
docker-compose
version: "3"
services:
jenkins:
image: jenkins/jenkins:lts
user: root
volumes:
- ./jenkins:/var/jenkins_home
ports:
- "8080"
근데 이렇게 해도 해결될 때가 있고 잘 안될 때가 있다..건강한 멘탈을 위해 시도해봤다가 안되면 그냥 명령어를 통해 젠킨스를 실행하도록 하자
```java
$ sudo docker logs jenkins
```
참고 블로그 : 🐳Docker&👩🏻🦲Jenkins를 통한CI&CD 이거보고 성공하세요😁😁😁 (velog.io)근데 이 과정에서 ssh키 설정을 잘못했는지 깃허브와 연결이 되지 않았다.
위의 블로그에서 파이프라인 중 배포 부분만 다르게 적었다.
pipeline {
agent any
environment {
imagename = "elmo3356/nginx-cicd"
registryCredential = 'docker-hub'
dockerImage = ''
}
stages {
stage('Prepare') {
steps {
echo 'Clonning Repository'
git url: 'https://github.com/HideOnCodec/springCICD.git',
branch: 'main',
credentialsId: 'HideOnCodec'
}
post {
success {
echo 'Successfully Cloned Repository'
}
failure {
error 'This pipeline stops here...'
}
}
}
stage('Unit test') {
steps {
sh 'chmod +x ./gradlew'
withGradle {
sh '''
echo test start
./gradlew test
'''
}
}
}
stage('Bulid Gradle') {
steps {
echo 'Bulid Gradle'
dir('.'){
sh 'chmod +x ./gradlew'
sh './gradlew clean build'
}
}
post {
failure {
error 'This pipeline stops here...'
}
}
}
stage('Bulid Docker') {
steps {
echo 'Bulid Docker'
script {
dockerImage = docker.build imagename
}
}
post {
failure {
error 'This pipeline stops here...'
}
}
}
stage('Push Docker') {
steps {
echo 'Push Docker'
script {
docker.withRegistry( '', registryCredential) {
dockerImage.push("latest")
}
}
}
post {
failure {
error 'This pipeline stops here...'
}
}
}
stage('Docker Run') {
steps {
echo 'Pull Docker Image & Docker Image Run'
sshagent (credentials: ['ssh']) {
sh "ssh -o StrictHostKeyChecking=no ubuntu@13.209.201.126 'docker pull elmo3356/nginx-cicd'"
sh "ssh -o StrictHostKeyChecking=no ubuntu@13.209.201.126 'docker ps -aqf name=elmo3356/nginx-cicd | xargs -r docker rm -f'"
sh "ssh -o StrictHostKeyChecking=no ubuntu@13.209.201.126 'docker run -d --name youthTalkTalk -p 8080:8080 elmo3356/nginx-cicd'"
}
}
}
}
}
Blue Green 무중단 배포 적용하기 (velog.io)
위 블로그를 참고했다.
$ 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
DEFAULT_CONF_PATH="/etc/nginx/conf.d"
DEFAULT_CONF="service-url.inc"
...
switch_conf() {
cp "${DEFAULT_CONF_PATH}/${AFTER_COMPOSE_COLOR}.inc" "${DEFAULT_CONF_PATH}/${DEFAULT_CONF}"
nginx -s reload
}
services:
api:
image: 도커허브아이디/레포지토리:latest
container_name: green
ports:
- 'green에 해당하는 포트번호:8080'
version: '3.1'
services:
api:
image: 도커허브아이디/레포지토리:latest
container_name: blue
ports:
- 'blue에 해당하는 포트번호:8080'
nginx 프록시 역할을 잘 수행하도록 추가 설정을 해야한다.
sudo vim /etc/nginx/sites-enabled/default
에 두 줄 추가include /etc/nginx/conf.d/service-url.inc;
proxy_pass $service_url;
...
server_name _;
include /etc/nginx/conf.d/service-url.inc;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
proxy_pass $service_url;
}
...
set $service_url http://127.0.0.1:8081;
set $service_url http://127.0.0.1:blue 포트번호;
set $service_url http://127.0.0.1:green 포트번호;
DockerFile
FROM openjdk:17-jdk
VOLUME /tmp
ARG JAR_FILE=./build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["sh", "-c", "java -Dserver.port=${PORT} -Dspring.profiles.active=${PROFILE} -jar /app.jar"]
간단히 test 할 수 있는 컨트롤러 작성