이번 글에서는 이전 글에 이어서 젠킨스 설정(소나 큐브와 연동), Nginx Proxy Manager 설정을 진행한다. 진행하기 전 소나 큐브와 포테이너에 접속해 계정을 만들고, 어떤 기능이 있는지 한번 살펴보자. 포테이너의 웹 UI로 편하게 컨테이너를 관리하고 모니터링 할 수 있으며 소나 큐브의 웹 UI로 정적 분석 결과를 볼 수 있다.
젠킨스 시작
젠킨스란 빌드, 배포 과정을 자동화할 수 있는 소프트웨어이다. 설치형이기에 설치와 설정이 필요하다는 단점이 있지만 비용(돈)이 들지 않는다.
웹 브라우저 URL 검색 창에 호스트 서버 IP:9090을 검색했을 때 아래의 화면이 나오면 정상 설치 된 것이다. 젠킨스는 편리하게 사용할 수 있도록 웹 인터페이스를 제공한다. 처음 접속 시 계정을 설정해야한다.
젠킨스 컨테이너에 접속 후(docker exec 명령어 사용) 아래 명령어를 실행하고, 화면에 출력된 비밀번호를 Administrator password에 입력한다.
cat /var/jenkins_home/secrets/initialAdminPassword
Install suggested plugins 버튼을 눌러 인기있는 플러그인을 설치한다.
아래 정보를 입력해 계정을 생성한다.
다음으로 필요한 플러그인을 설치한다. Jenkins 관리 → Plugins → Available plugins 들어가 아래의 플러그인을 설치한다.
SonarQube Scanner : 소나 큐브 사용을 위함
maven Integration : 메이븐 설치를 위함
nodeJS : nodeJS 설치를 위함
Credentials 생성
Credentials는 외부 서버, 시스템에 대한 인증 정보(비밀키, 패스워드, 토큰값)을 말한다. Credentials로 인증 정보를 저장해두고 젠킨스가 해당 Credentials을 통해 외부 서버의 접근 권한을 얻는다.
젠킨스는 원격 저장소(GitHub, GitLab)로부터 git clone을 하여 원격 저장소의 파일을 가져와 빌드를 수행한다. 이를 위해서는 원격 저장소에 접근할 수 있는 권한이 필요하다. 때문에 gitlab의 access token을 발급받아 credentials을 만든다. 또한 필자의 젠킨스는 소나 큐브 서버에도 접근하므로 소나 큐브의 token을 발급받아 credentials을 만든다.
Jenkins 관리 → Credentials로 이동한다. 아래와 같이 Add credentials 버튼을 누른다.
kind는 Username and Password를 선택한다.
Username : 원격 저장소(GitLab, GitHub)의 닉네임
Password : gitlab의 토큰값 저장
ID : 임의로 지정
같은 방식으로 소나 큐브의 토큰 값을 저장한 credentials을 만든다. (kind는 secret text로 지정한다.)
Secret : 소나 큐브의 토큰 값
ID : 임의로 지정
아래와 같이 소나 큐브에 접속하여 토큰을 발급받을 수 있다.
SonarQube와 연동
Jenkins 관리 → System으로 이동하고 아래와 같이 SonarQube server를 작성한다.
name : 소나 큐브 서버의 이름 (임의로 작성)
Server URL : 소나 큐브 서버의 URL
Server authentication token : 위에서 만든 credentials
JDK 경로 지정, Maven와 NodeJS 설치
jenkins 관리 → Tool로 이동한다.
Spring Boot 앱 빌드에 사용되는 jdk가 jenkins 컨테이너 안에 있어야한다. jenkins 컨테이너를 받으면서 이미 jdk가 포함되어 있으므로 해당 jdk를 사용한다. (필자의 jdk의 경로는 /opt/java/openjdk이며 이미 환경변수가 설정되어 있다.) 아래와 같이 jdk 경로를 지정한다. 만약 설치되어 있지 않다면 Install automatically 체크박스를 체크한다.
또한 Spring Boot 앱 빌드에 사용되는 maven이 jenkins 컨테이너 안에 있어야한다. 아래와 같이 maven 정보를 등록하며 Install automatically 체크박스를 체크해 자동 설치를 진행한다. 만약 이미 maven이 깔려 있다면 체크박스를 해제하고 maven 경로를 지정한다.
또한 vue3 프로젝트를 빌드할 것이므로 jenkins 컨테이너 안에 nodeJS가 있어야한다. 아래와 같이 NodeJS 정보를 등록한다. Install automatically 체크박스를 체크해 자동 설치를 진행한다.
jenkins 관리 → Tool에서 Install automatically 체크박스를 체크해 설치한 Tool은 언제 설치되고, 어디에 저장될까? 아래 파이프라인을 보자. 젠킨스는 Pipeline 내에서 tools에 정의된 Tool을 설치하며 (이미 설치되었다면 설치하지 않는다.) Tool의 경로를 Jenkins 에이전트에게 알려준다. 따라서 별다른 환경 변수 설정 없이 해당 Tool을 사용할 수 있다. 참고로 젠킨스가 설치한 툴은 /var/jenkins_home/tools에 저장된다.
pipeline {
agent any
tools {
maven "maven" // Specify the Maven tool name configured in Jenkins
}
stages {
stage('Build') {
steps {
script {
sh 'mvn -version' // Maven을 사용할 수 있음을 확인하기 위한 명령
}
}
}
}
}
위 파이프라인의 tools 부분에서 maven "maven"을 넣었다. maven은 tool의 이름을 나타내며 옆의 문자열은 Global Tool Configuration에서 도구를 추가할 때의 이름을 지정한다. (필자는 위에서 메이븐을 추가할 때 이름을 maven으로 지었다.) tool의 이름은 이미 정의되어 있으므로 잘못 지정하면 파이프라인 작동 시 아래와 같은 오류가 뜬다.
Started by user admin
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 5: Invalid tool type "nodes". Valid tool types: [ant, hudson.tasks.Ant$AntInstallation, git, hudson.plugins.git.GitTool, gradle, hudson.plugins.gradle.GradleInstallation, jdk, hudson.model.JDK, jgit, org.jenkinsci.plugins.gitclient.JGitTool, jgitapache, org.jenkinsci.plugins.gitclient.JGitApacheTool, maven, hudson.tasks.Maven$MavenInstallation, nodejs, jenkins.plugins.nodejs.tools.NodeJSInstallation, hudson.plugins.sonar.SonarRunnerInstallation, hudson.plugins.sonar.MsBuildSQRunnerInstallation] @ line 5, column 9.
nodes "nodeJS"
^
1 error
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:309)
at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1107)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:190)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:175)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:636)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:582)
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
at hudson.model.ResourceController.execute(ResourceController.java:101)
at hudson.model.Executor.run(Executor.java:442)
Finished: FAILURE
젠킨스 파이프라인
스프링 부트 앱, vue3를 빌드, 배포하는 파이프라인을 각각 만든다. 아래와 같이 pipeline을 생성한다. (자신의 상황에 맞게 수정하자) 파이프라인이 좋은 점은 UI로 쉽게 Stage별 성공 여부를 확인할 수 있고 커스텀이 자유롭다.
- spring boot 앱 빌드 / 배포
pipeline {
agent any
tools {
maven "maven" // Specify the Maven tool name configured in Jenkins
}
stages {
stage('Clone Repository') {
steps {
script {
git branch: 'back_master', credentialsId: 'gitlab-access-token', url: 'https://gitlab.com/username/yourProject.git'
}
}
}
stage('Build') {
steps {
dir('backend/Exhale') {
sh 'mvn clean package -DskipTests'
}
}
}
stage('SonarQube analysis') {
steps {
withSonarQubeEnv('sonarqube') {
dir('backend/Exhale') {
sh 'mvn sonar:sonar'
}
}
}
}
//테스트 과정 생략
stage('Deploy') {
steps {
sh 'ssh ubuntu@서버IP "sudo sh ~/deploy/backend/deploy.sh"'
}
}
}
}
- vue3 앱 빌드 / 배포
pipeline {
agent any
tools {
nodejs "nodeJS"
}
stages {
stage('Clone Repository') {
steps {
script {
git branch: 'front_master', credentialsId: 'gitlab-access-token', url: 'https://gitlab.com/username/yourProject.git'
}
}
}
stage('Install Dependencies') {
steps {
dir('frontend/Exhale') {
sh 'npm install'
}
}
}
stage('Build') {
steps {
dir('frontend/Exhale') {
sh 'npm run build:production'
}
}
}
stage('Deploy') {
steps {
sh 'ssh ubuntu@서버IP "sudo sh ~/deploy/frontend/deploy.sh"'
}
}
}
}
배포 과정
위 파이프라인의 Deploy 스테이지를 보면 ssh을 통해 전에 작성한 배포 쉘 스크립트를 실행한다. 따라서 사전에 젠킨스 컨테이너에서 EC2(호스트)로 ssh 연결이 가능하도록 만들어야한다.
배포의 방법은 여러가지다. 대표적인 방법은 2개가 있다.
1. 빌드 결과물을 ssh 혹은 scp을 통해, 동작중인 개발/운영 컨테이너에 전달
2. 빌드 결과물이 담긴 컨테이너 이미지를 만들고 이미지를 EC2(호스트)에 전달하거나 hub에 등록, 호스트에서 이미지를 받아 컨테이너로 만든 후 사용
이 외에도 여러 방법이 있지만 필자는 1번 방식을 사용했다. 2번 방식도 많이 쓰는 것 같다.
Webhook을 통한 빌드 유발
GitLab, GitHub의 WebHook 기능을 통해 자동 배포를 수행할 수 있다. 파이프라인 구성 → Build Triggers → Build when a change is pushed to GitLab webhook URL 버튼을 클릭하고 아래와 같이 GitLab Webhook URL와 Secret token을 확인한다.
아래와 같이 GitLab에서 Webhook 설정을 진행한다. URL에는 위의 GitLab Webhook URL를 Secret token에는 위의 Secret token 값을 넣는다.
Test 버튼을 통해 Webhook 설정이 잘 되었는지 확인하자.
Nginx 설정
http://domain으로 보낸 모든 요청은 nginx 서버가 수신한다. nginx 서버로 보내는 요청 중 /api로 시작하는 요청, 즉 백엔드 요청을 백엔드 서버로 프록시하는 설정을 진행한다. 해당 설정을 하고나서 http://domain/api로 요청을 보내면 nginx가 해당 요청을 백엔드 서버로 보내준다. 또한 스프링 앱의 context root 경로가 /api로 설정되어 있어야한다.
docker exce 명령어 혹은 포테이너로 nginx 컨테이너에 접속한다. 설정 파일을 작성해야 되기에 vim 도구를 설치해야한다. 아래와 같이 vim 도구를 설치할 수 있다.
apt update
apt install vim
그 후 nginx 설정 파일로 이동하여 편집한다.
vi /etc/nginx/conf.d/default.conf
아래와 같이 location /api를 추가한다. /api로 시작하는 요청은 백엔드 서버로 프록시한다는 설정이다. 혹시 프록시가 안된다면 nginx 컨테이너와 백엔드 서버가 동작중인 컨테이너가 통신이 되는지 ping 명령어로 확인해보자. 필자는 같은 도커 네트워크로 묶어주었음에도 서로 간 통신이 되지 않아 컨테이너를 재시작하거나 둘 컨테이너의 도커 네트워크를 disconnect 했다가 connect 시켜줬더니 통신이 되었다.
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /api {
proxy_pass http://백엔드서버IP(또는 컨테이너 명):8080;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
'Infra > CI&CD' 카테고리의 다른 글
SpringBoot, Vue3 프로젝트 CI/CD (1) (0) | 2024.01.26 |
---|---|
Django Channels 배포 (0) | 2022.09.12 |