그럼 이제 GCP 위에 docker를 올리고, docker 위에 jenkins, sonarqube를 올리고 gitlab webhook으로 pull받아서 build 후 GCR push까지, 한 번에 다뤄보고자 한다. 자세한 모든 화면은 이전 포스팅들을 참고.
*** 지금 이 글대로 구성하면, ci server에서 jenkins data가 저장될 곳이 없어 재시작하면 전부 날아간다. 이를 고려해서, volume을 추가하는 것을 권장한다.
1. docker-compose로 GCR 위에 docker, docker 위에 jenkins, sonarqube 설치
/home/hyelie/compose/docker-compose.yml
version: "3"
services:
jenkins:
container_name: jenkins-compose
image: jenkins/jenkins:lts
user: root
ports:
- "8081:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker
- jenkins_data:/var/jenkins_home
sonarqube:
image: sonarqube:lts
container_name: sonarqube
ports:
- "9000:9000"
volumes:
- sonarqube_conf:/opt/sonarqube/conf
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
volumes:
jenkins_data:
sonarqube_conf:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
제일 처음 썼던 게시글과는 다르게, 이제는 jenkins에서 docker 명령어를 바로 수행할 수 있기 때문에 volumes에 docker.sock, docker folder를 연결해 준다.(이 방식은 docker out of docker 방식이다.) 이후 아래 명령어가 잘 수행되는지 보자.
$ docker-compose up -d
Creating jenkins-compose ... done
Creating sonarqube ... done
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42dd8d6f5d59 jenkins/jenkins:lts "/sbin/tini -- /usr/…" 2 seconds ago Up 1 second 50000/tcp, 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp jenkins-compose
d4984f7a0841 sonarqube:lts "bin/run.sh bin/sona…" 2 seconds ago Up 1 second 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp sonarqube
$ docker exec -it 42dd8d6f5d59 bash
droot@42dd8d6f5d59:/# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42dd8d6f5d59 jenkins/jenkins:lts "/sbin/tini -- /usr/…" 11 seconds ago Up 9 seconds 50000/tcp, 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp jenkins-compose
d4984f7a0841 sonarqube:lts "bin/run.sh bin/sona…" 11 seconds ago Up 9 seconds 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp sonarqube
exec bash로 들어가서 docker ps 결과가 수행 잘 되면 잘 된 것이다.
docker logs 42dd8d6f5d59
를 입력해 jenkins 비밀번호를 알아내고, jenkins에 접속하자. 그리고 plugin은 다음의 것들을 설치한다.
- gitlab
- gitlab hook
- sonarqube scanner
- sonar quality gates
- docker pipeline
- google oauth credentials
- google container registry
2. jenkins-gitlab 연동
GCR에서
ssh-keygen
을 입력한다. ~/.ssh에 있는 id_rsa.pub 값을 복사해서 gitlab - repository - deploy key에 등록
~/.ssh에 있는 id_rsa 값을 복사해서 jenkins 관리 - credentials - global - add credentials - ssh username with private key - enter directly에 등록.
jenkins는 {GCP public ip}:8081로 접속 가능.
위 방법은 public repository만 적용 가능하다. private repository의 경우 아래 링크를 따른다.
gitlab - settings - repository - deploy token으로 token 발급 받고, jenkins id/pw에 넣어주면 된다.
3. pipeline project 생성
jenkins - new item - pipeline project - build trigger - build when a change is pushed to gitlab~~~에 있는 링크, 그리고 고급 - secret token generate 후 복사.
이후 gitlab - settings - webhook에 위의 링크, token 복사
이후 test - push event로 잘 가는지 보자.
pipeline - definition - pipeline script from scm을 클릭하고
scm - git, repository url 등록, credential은 2.에서 등록한 gitlab credential 등록.
4. sonarqube, gradle 버전 연동
{GCP public IP}:9000으로 sonarqube 접속, 초기 id/pw admin/admin.
add a project - manually - 프로젝트 이름 적당히 등록, token 적당히 등록 및 복사. continue - gradle - 안에 있는 내용 복사.
administration - configuration - webhooks - create.
name은 적당히, url은 http://{jenkins url}:{jenkins port}/sonarqube-webhook/으로 등록.
jenkins 관리 - manage credentials - global - add credentials - secret text, token 등록.
jenkins 관리 - 시스템 설정 - sonarqube servers 등록, 이름은 sonar-server, url은 sonarqube url, credential은 바로 위에서 만든 credential. 이후 저장.
jenkins 관리 - global tool configuration - sonarqube scanner 등록.
마지막으로 gradle도 하자.
jenkins 관리 - global tool configuration -
gradle - add gradle - 내가 쓰는 버전인 6.9 등록
5. 테스트
내 로컬에 등록한 git으로 remote 등록,
/local/build.gradle
plugins {
id 'org.springframework.boot' version '2.4.8'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id "org.sonarqube" version "3.0"
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
pulgins에 id sonarqube 등록.
이후 dockerfile 생성.
/local/Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=build/libs/demo-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
마지막으로 jenkinsfile 생성.
pipeline {
agent any
tools{
gradle '6.9'
} // 내가 쓸 build gradle. '' 안에 들어갈 것은 gradle 이름이다.
stages{
stage('Ready'){
steps{
sh "echo 'Ready'"
}
}
stage('Spring Boot Clean & Build'){
steps{
sh "chmod +x gradlew;"
sh "./gradlew clean;"
sh "./gradlew build -x test;"
}
} // build 시 test 없이 진행하고, test는 추후에 거르기 위해 이렇게 이용함.
stage('Gradle Junit Test') {
steps {
sh "chmod +x gradlew; ./gradlew test"
}
} // test를 이용해서 확인함.
stage('JUnit Test Publish') {
steps {
junit '**/build/test-results/test/*.xml'
}
} // Junit 테스트 결과를 젠킨스 프로젝트 첫 화면에서 볼 수 있게 결과물을 출력한다. 이렇게 해주면 알아서 결과물을 보여준다.
stage('SonarQube Analysis'){
steps{
withSonarQubeEnv('sonar-server'){
sh './gradlew sonarqube -Dsonar.projectKey=ciserver -Dsonar.host.url=http://34.68.84.109:9000 -Dsonar.login=8ec502617dd7b039e3179fadb5ec83f0127544cc'
}
}
}
stage('SonarQube Quality Gate'){
steps{
timeout(time: 1, unit: 'MINUTES') {
script{
echo "Start~~~~"
def qg = waitForQualityGate()
echo "Status: ${qg.status}"
if(qg.status != 'OK') {
echo "NOT OK Status: ${qg.status}"
updateGitlabCommitStatus(name: "SonarQube Quality Gate", state: "failed")
error "Pipeline aborted due to quality gate failure: ${qg.status}"
} else{
echo "OK Status: ${qg.status}"
updateGitlabCommitStatus(name: "SonarQube Quality Gate", state: "success")
}
echo "End~~~~"
}
}
}
}
stage('Docker image build'){
steps{
script{
def app = docker.build("hyelie/firstspring:latest")
}
}
}
stage('Clean'){
steps{
sh "chmod +x gradlew;"
sh "./gradlew clean;"
}
}
}
}
여기서 sonarqube analysis step에 있는 withSonarQubeEnv는 jenkins 관리 - 시스템 설정 - sonarqube servers에 등록한 이름이고 그 안에 있는 shell은 아래 사진에 있는 ./gradlew sonarqube \ ~~~에 있는 코드이다.
build가 되고, docker image가 생성되었다.
6. GCR에 push해보기
https://cloud.google.com/container-registry/docs/quickstart
먼저 위 링크로 들어가서 API 사용 설정 - 프로젝트 설정을 누른다. 그러면 gcr이 활성화된다.
역할 만들기 - 권한 - 권한 추가으로 들어가 아래 5개를 추가한다.
- storage.objects.create
- storage.objects.get
- storage.objects.list
- storage.buckets.create
- storage.buckets.get
역할은 원래는 storage admin & storage object viewer로 하고 싶었는데 그걸로 하니 안되어서 custom으로 만든 권한을 두었다.
key를 추가하면 뭐 하나가 다운로드된다.
다음으로 jenkins로 넘어가자.
jenkins - manage credentials - global - add credentials - google service account from private key.
project 이름은 gcr-ci-project라고 두겠다. json key는 방금 다운받아진 파일을 선택한다.
/local/jenkinsfile
stage('docker build'){ steps{ sh 'docker build -t gcr.io/numeric-replica-320807/testspring .' } } stage('Docker push image'){ steps{ script{ docker.withRegistry('https://gcr.io', 'gcr:gcr-ci-project'){ sh 'docker push gcr.io/numeric-replica-320807/testspring' } } } } // gcr push
jenkinsfile에는 이렇게 적는다. 뜻만 대충 설명하자면, image id를 gcr에 넣고, project 이름을 같이 넣음으로써 push할 경로를 지정해 준다. docker hub에 push할 때 {repository name}/{project 이름}으로 적었던 것처럼.
이후에는 jenkins에 있는 registry를 이용해 gcr에다가 push하겠다는 말이다.
GCR에도 잘 들어가 있다!
지금까지 GCP VM instance에 빌드 서버를 올렸다. 다음에는 GKE 클러스터를 만들고, 쿠버네티스 노드를 만들고, db를 배포할 것이다. DB를 먼저 배포하는 이유는, 저번에 게시판을 빌드할 때 DB가 없으면 hikaripool test 오류가 났기 때문에 그렇다.
이후에는 spring 게시판 예제를 kubernetes에 올려볼 것이다.
이렇게 하면, 개발자는 개발만 해서 git에 올리면 되고, project master가 git branch를 merge하면 jenkins에서 테스트/빌드가 완료되어 GCR로 올라간다. 이후에 project master가 kubenetes deploy를 진행하면 될 것이다!!