이전 포스팅에 이어서
회사 서비스에 CI/CD 구축하기 전 팀원들과 진행하고 있던 토이 프로젝트에 간단한 배포 스크립트를 적용하였다. 그 과정에서 AWS EC2 프리티어의 메모리 문제나 사소한 권한 문제들로 애를 먹었지만 필요한 크리덴셜을 설정한다던지, ssh Key를 이용해서 서버와 서버 간의 접속 권한 문제를 해결한다던지 등 사소한 이슈들을 잘 헤쳐나갔다. 그리고 이슈를 해결을 하면서 대부분 자동화 배포 과정에서 생기는 문제들은 설계 상의 문제보단 권한의 문제가 크다는 것을 알게 되었다. 그리고 회사 서비스에 적용을 하기 위해선 보안도 엄격하게 고려해야 했다.
하지만 그 전에 토이 프로젝트의 개발 환경과 회사 서비스의 개발 환경이 완전히 다르다는 것이 문제였다.
적용 전에 맞닥뜨린 문제
팀원들과 진행한 토이 프로젝트는 스프링부트 + JPA를 사용했고 빌드 툴로 Gradle를 이용했다. 회사 서비스의 개발환경은 스프링 4 버전에 MyBatis를 사용하고 빌드 툴로 Maven을 이용하고 있다(각 서비스마다 개발 환경이 또 다르다). 그리고 war로 패키징 된 파일로 배포를 진행하는 것이 아니라 로컬에서 exploded 된 파일 벌크들을 was 서버의 root path에 두어 배포하는 방식이었다. 가장 달랐던 것이 인프라 환경인데 회사의 인프라는 서비스 별로 계정도 나뉘어 있고 계정 별로 권한이 분리되어 있었기 때문에 root 권한을 가지고 있는 계정을 꼭짓점으로 각 서버에 배포하는 식의 아키텍처로 설계해야 했다.
시작
계획은 이러했다. Git Checkout Step에서 최신 커밋까지 젠킨스 워크스페이스에 저장한다. 그리고 Build Step에서 메이븐 패키징을 진행하고 빌드가 완료된 모듈을 Deploy Step에서 배포할 서버에 전송한다. 그리고 WAS 재구동이 완료되면 모든 빌드가 완료되었다는 알림 메세지를 Slack 채널에 전송하고 실패 시에도 실패했다는 알림 메시지를 전송한다. 모든 파이프라인 스크립트는 파라미터화 시켜서 유지보수 및 확장이 수월하도록 작성한다.
이슈
1. 리버스 프록시 이슈
웹 서버를 이용해서 별도의 도메인으로 젠킨스를 이용한다면 젠킨스가 리버스 프록시 설정을 제대로 잡아주지 못해 잘못된 URL로 Location 되는 문제가 발생할 수 있다. 우리는 Apache를 사용하고 있고 별도의 도메인을 등록해서 젠킨스를 이용하고자 했다. 하지만 특정 페이지에 접근 시 도메인에 한번 더 포트 넘버가 추가되어 Location 되는 문제가 발생했다. 그래서 잘못된 URL로 Location 될 경우 옳은 URL로 변경될 수 있도록 Apach에서 설정하였다. 이 문제에 대한 가이드는 젠킨스 공식 문서에서 자세히 설명되어 있다.
<Location />
ProxyPassReverse /
Order deny,allow
Allow from all
</Location>
Header edit Location ^${url}:${port}/ ${url}/
https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-with-jenkins/
2. ssh 접속권한
젠킨스 파이프라인에서 ssh를 이용하려면 플러그인을 설치하거나 별도의 암호화된 키를 서버 내부에 저장하고 있어야 한다. 아무래도 플러그인을 설치해서 젠킨스 내부에 회사 서버 계정에 대한 정보를 담아 두기엔 보안적인 이슈가 발생할 수 있기 때문에 서버 내부에 저장하는 방식을 선택해서 진행했다.
3. 설정파일 이슈
개발 / 스테이지 / 운영 환경마다 설정 파일이 다르다는 문제가 있다. 사실 이 문제는 메이븐을 통해 빌드를 진행할 때 설정 파일들을 지정해서 각각 패키징을 진행하면 되겠지만 이미 회사의 일부 서비스들은 그런 것들을 고려하지 않고 설계되어 있었다. 그래서 젠킨스 워크스페이스에 설정 파일들을 각각의 디렉토리로 분류해서 패키징 전에 각 서버에 맞는 설정 파일들로 변경하고 패키징을 진행하는 방식으로 구현하였다. 이 방식은 분명 좋은 방식은 아니기 때문에 앞으로 고도화해야 할 과제 중 하나이다.
파이프라인
젠킨스 파이프라인 스크립트는 chatGPT를 활용해서 작성했고, 세세한 경로 같은 부분은 환경 변수로 지정해서 관리할 수 있게끔 작성했다. 밑에 예시 스크립트에서는 회사에 대한 정보가 많아서 삭제했다.
pipeline {
agent any
environment {
//환경변수 설정
}
tools {
jdk('jdk')
maven('maven')
}
stages {
stage('Git Checkout') {
steps {
echo '########################################\n########## Git checkout start ##########\n########################################'
git ''
}
}
stage('Build') {
steps {
//Change config files & Build
echo '#################################\n########## Build Start ##########\n#################################'
sh '''
cp -f $STAGE_CONFIGDIR_PATH/pom.xml $JENKINS_WORKSPACE_PATH/$JOB_NAME
cd $JENKINS_WORKSPACE_PATH/$JOB_NAME
mvn clean package
'''
}
}
stage('Deploy') {
steps {
//Deploy
echo '##################################\n########## Deploy Start ##########\n##################################'
sh '''
cp -f $STAGE_CONFIGDIR_PATH/globals.properties $STAGE_GLOBALS_PROPERTIES_PATH
cp -f $STAGE_CONFIGDIR_PATH/context-datasource.xml $STAGE_CONTEXT_PATH
cp -f $STAGE_CONFIGDIR_PATH/context-sqlMap.xml $STAGE_CONTEXT_PATH
cd $JENKINS_WORKSPACE_PATH/$JOB_NAME/target
mv $PACKAGE_NAME $STAGE_DIR_NAME
scp -r $SCP_PORT $STAGE_DEPLOY_FILES_PATH $STAGE_DEPLOY_PATH
'''
//Tomcat Restart
echo '#################################\n########## WAS restart ##########\n#################################'
sh '''
ssh -t $STAGE_HOST $SSH_PORT "cd $STAGE_TOMCAT_PATH; ./catalina.sh stop; ./catalina.sh start;"
'''
}
}
}
post {
success {
slackSend (
channel: '',
color: '#00FF00',
message: """
@channel\n\n ✅ SUCCESS \n\nJOB-NAME: ${env.JOB_NAME}\nBUILD-NUM: [${env.BUILD_NUMBER}]\nURL: ${env.BUILD_URL}\n\n배포가 완료되었습니다!
"""
)
}
failure {
slackSend (
channel: '',
color: '#FF0000',
message: """
@channel\n\n ⛔️ FAIL \n\nJOB-NAME: ${env.JOB_NAME}\nBUILD-NUM: [${env.BUILD_NUMBER}]\nURL: ${env.BUILD_URL}\n\n에러로 인해 배포 파이프라인이 중단되었습니다!
"""
)
}
}
}
챌린지
일단 첫번째 목표는 달성을 했다. 회사 서비스에 CI/CD를 구축해서 배포화 툴을 사용하는 것. 4월에 들어와서 약 일주일 정도 테스트 기간을 마치고 개발 / 스테이지 서버는 구축한 파이프라인을 사용해서 배포를 진행하고 있다. 하지만 아직 운영 서버에는 적용하지 못했고 지금의 파이프라인은 고도화할 과제들이 많다.
1. 도커 컨테이너 환경(서버 환경세팅 때 간절히 공부하겠다고 다짐했다..)
2. 새로운 커밋 로그 중 변경된 파일을 감지해서 java 파일이 존재할 경우에만 WAS 재구동
3. 젠킨스 계정 권한 세분화
차차 고도화하기로 하였다. 고도화를 진행하면서 정리할 주제가 생기면 별도로 포스팅으로 올릴 예정이다.
신입의 자동화 여정은 여기서 끝!
'Infra' 카테고리의 다른 글
[jenkins] 신입의 객기 - 자동화 여정 (2) (0) | 2023.03.20 |
---|---|
[jenkins] 신입의 객기 - 자동화 여정 (1) (0) | 2023.03.13 |
[tomcat] Session Deep Dive (0) | 2023.02.08 |