CI
- Continuous Integration
- 빌드 / 테스트 자동화
- 소스&버전관리 시스템(예시: git)에 대한 변경사항을 정기적으로 커밋 → 모든 사람에게 동일한 작업 기반 제공
Jenkins에서 CI 파이프라인 구축
- EC2 인스턴스 안에서 Docker를 통해 Jenkins 컨테이너 생성 (java나 이런저런 설정의 간편화)
- Jenkins 인스턴스에 Elastic IP 할당 → 나중에 webhook등 설정할 때 값 고정하기 위해
Step By Step
0. EC2 Instance에 Docker 설치
1
2
3
4
5
6
7
8
9
10
11
| # SSH로 EC2 Instance 접속
# .pem파일 있는 경로에서
ssh -i ./[FILE_NAME].pem ubuntu@[IP Address]
# docker 설치
sudo apt-get update
sudo apt-get install docker -y
# docker 서비스 시작
sudo service docker start
# docker 서비스 상태 확인
systemctl status docker.service
|
1. Jenkins Container 생성 및 실행
Jenkins Container에도 Docker를 설치
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # Dockerfile 생성
sudo vim Dockerfile
---------------------------------
# Dockerfile
# Dockerfile 작성 시작
FROM jenkins/jenkins:jdk11
#도커를 실행하기 위한 root 계정으로 전환
USER root
#도커 설치
COPY docker_install.sh /docker_install.sh
RUN chmod +x /docker_install.sh
RUN /docker_install.sh
# 도커 그룹에 사용자 추가
RUN usermod -aG docker jenkins
USER jenkins
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| -------------------------------------
# docker_install.sh 파일 생성
sudo vim docker_install.sh
--------------------------------------
# docker_install.sh 파일 작성 시작
#!/bin/sh
apt-get update && \
apt-get -y install apt-transport-https \
ca-certificates \
curl \
gnupg2 \
zip \
unzip \
software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(lsb_release -cs) \
stable" && \
apt-get update && \
apt-get -y install docker-ce
-----------------------------------------
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # 권한 설정
sudo chmod 666 /var/run/docker.sock
# 이미지 생성
docker build -t jenkins .
# jenkins 폴더 만들기
mkdir jenkins
# 해당 폴더에 대해 권한 부여하기
sudo chown -R 1000 ./jenkins
# 컨테이너 생성
sudo docker run -d --name jenkins \
-v /home/ec2-user/jenkins:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 8080:8080 \
-e TZ=Asia/Seoul \
jenkins
|
참고자료 1
2. 프로젝트에 Dockerfile 만들기
1
2
3
4
5
| FROM openjdk:11-jdk
EXPOSE 8081
ARG JAR_FILE=build/libs/modoosugang_be-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
|
1
2
3
4
5
6
7
8
9
| FROM nginx:latest
RUN mkdir /app
WORKDIR /app
RUN mkdir ./build
ADD ./build ./build
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
|
3. Jenkins 접속 후 세팅
- 암호입력
1
| sudo cat /var/lib/jenkins/secrets/initialAdminPassword
|
Install suggested plugins
Jenkins페이지 → jenkins 관리 → System Configuration → 플러그인 관리
- gradle
- github integration
- post build task
- publish over ssh
- Docker pipeline
- NodeJS
Jenkins페이지 → jenkins 관리 → Security → Manage Credentials
- Github 계정 등록
- DockerHuB 계정 등록
Credential 추가
❗이부분에서 잘 설정해줘야 에러가 적게남: pipeline script 에러 1순위(경험상)
- Github Credential 추가
필요에 따라 환경변수 선언
Jenkins 관리 -> 시스템 설정 -> Global properties
Gradle & NodeJS 세팅
- Jenkins페이지 → jenkins 관리 → **Global Tool Configuration**
4. Servier project 생성
jenkinsfile을 생성하여 pipdline 프로젝트 생성
- 새로운 Item → pipeline 선택
- Springboot에서 사용한 환경변수들은 k8s secret을 통해 Deployment 부분에서 선언
- jenkinsfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
| pipeline {
agent any
environment {
// 환경변수 설정
dockerHubRegistry = 'plox/modoosugang-server'
dockerHubRegistryCredential = '8181e4ed-46b2-4fd1-bfbf-1fa5ab5fc938'
}
stages {
// 단계 1. github repo 가져오기
stage('Checkout Application Git Branch') {
steps {
// 가져올 github repo와 branch를 설정하고, 여기에 권한을 가진 credential ID 입력
git credentialsId: '4b1dcb95-da80-40e8-8a26-312fb71aed5a',
url: 'https://github.com/modooSugangOrg/modooSugang_BE.git',
branch: 'main'
}
post {
failure {
echo 'Repository clone failure !'
}
success {
echo 'Repository clone success !'
}
}
}
// 단계 2. java build
stage('Jar Build') {
steps {
// gradlew 실행권한 부여
sh 'chmod +x gradlew'
// clean 후 build
sh './gradlew clean build'
}
post {
failure {
echo 'jar build failure !'
}
success {
echo 'jar build success !'
}
}
}
// 단계 3. Docker Image Build
stage('Docker Image Build') {
steps {
sh "docker build . -t plox/modoosugang-server:${BUILD_NUMBER}"
sh "docker build . -t plox/modoosugang-server:latest"
}
post {
failure {
echo 'Docker image build failure !'
}
success {
echo 'Docker image build success !'
}
}
}
// 단계 4. Docker Image Push
stage('Docker Image Push') {
steps {
// DockerHub 관련 Credential ID
withDockerRegistry([ credentialsId: '8181e4ed-46b2-4fd1-bfbf-1fa5ab5fc938', url: "" ]) {
sh "docker push ${dockerHubRegistry}:${BUILD_NUMBER}"
sh "docker push ${dockerHubRegistry}:latest"
}
}
post {
failure {
echo 'Docker Image Push failure !'
sh "docker rmi ${dockerHubRegistry}:${BUILD_NUMBER}"
sh "docker rmi ${dockerHubRegistry}:latest"
}
success {
echo 'Docker image push success !'
sh "docker rmi ${dockerHubRegistry}:${BUILD_NUMBER}"
sh "docker rmi ${dockerHubRegistry}:latest"
}
}
}
// 단계 5. k8s manifest 파일 업데이트
// docker image tag를 빌드 번호로 업데이트
stage('K8S Manifest Update') {
steps {
// Manifest repo 가져오기
git credentialsId: '4b1dcb95-da80-40e8-8a26-312fb71aed5a',
url: 'https://github.com/modooSugangOrg/modoosugang_BE_manifest.git',
branch: 'main'
// 수정할 파일(kustomization.yaml) 경로에 들어가서 && 파일의 내용을 수정
// sed 명령어 해석(뇌피셜)
// sed -i 's/{newTag로 시작하는 부분을}\$/{요걸로 바꾸겠습니다.}/g' {바꾸고 싶은 파일}
sh "cd ./overlay/dev && sed -i 's/newTag:.*\$/newTag: \"${BUILD_NUMBER}\"/g' kustomization.yaml"
sh "cat ./overlay/dev/kustomization.yaml"
// 커밋 & 푸시 전 사용자 정보 입력
sh 'git config --global user.name plooox'
sh 'git config --global user.email insukim97@gmail.com'
// credentialId의 Id와 Password 데이터를 'usernameVariable'과 'passVariable'로 할당
withCredentials([usernamePassword(credentialsId: '4b1dcb95-da80-40e8-8a26-312fb71aed5a', passwordVariable: 'pass', usernameVariable: 'user')]) {
sh "git pull origin main" // 혹시 모를 충돌 대비 pull
sh "git add ./overlay/dev/kustomization.yaml" // build파일도 같이 커밋되는 걸 막기 위해 수정한 파일만 추가
sh "git commit -m '[:alien: UPDATE] modoosugang_server ${BUILD_NUMBER} image versioning'" // 커밋
sh "git push http://$user:$pass@github.com/modooSugangOrg/modoosugang_BE_manifest.git main" // id & pw 를 통해 push
}
}
post {
failure {
echo 'K8S Manifest Update failure !'
}
success {
echo 'K8S Manifest Update success !'
}
}
}
}
}
|
⚠️ Credential ID 확인하는 법
5. Client project 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
| pipeline {
agent any
// 빌드에 사용할 툴 ( Global Tool Configuration에서 선언한 Name을 토대로 {사용 Tool }:{Name})
tools {
nodejs "nodejs" // Global Tool Configuration에서 NodeJS 16.14.2를 "nodejs"로 선언했음
}
environment {
dockerHubRegistry = 'plox/modoosugang-client'
dockerHubRegistryCredential = '8181e4ed-46b2-4fd1-bfbf-1fa5ab5fc938'
}
stages {
stage('Checkout Application Git Branch') {
steps {
git credentialsId: '4b1dcb95-da80-40e8-8a26-312fb71aed5a',
url: 'https://github.com/modooSugangOrg/modooSugang_FE.git',
branch: 'main'
}
post {
failure {
echo 'Repository clone failure !'
}
success {
echo 'Repository clone success !'
}
}
}
stage('npm Build') {
steps {
// 필요한 환경변수들 -> .env 파일에 저장
sh 'echo REACT_APP_SERVER_URL=${REACT_APP_SERVER_URL} > .env'
sh 'echo REACT_APP_TEST_MESSAGE=${REACT_APP_TEST_MESSAGE} >> .env'
sh 'npm install'
sh 'npm run build'
}
post {
failure {
echo 'npm build failure !'
}
success {
echo 'npm build success !'
}
}
}
stage('Docker Image Build') {
steps {
sh "docker build . -t ${dockerHubRegistry}:${BUILD_NUMBER}"
sh "docker build . -t ${dockerHubRegistry}:latest"
}
post {
failure {
echo 'Docker image build failure !'
}
success {
echo 'Docker image build success !'
}
}
}
stage('Docker Image Push') {
steps {
withDockerRegistry([ credentialsId: dockerHubRegistryCredential, url: "" ]) {
sh "docker push ${dockerHubRegistry}:${BUILD_NUMBER}"
sh "docker push ${dockerHubRegistry}:latest"
sleep 10 /* Wait uploading */
}
}
post {
failure {
echo 'Docker Image Push failure !'
sh "docker rmi ${dockerHubRegistry}:${BUILD_NUMBER}"
sh "docker rmi ${dockerHubRegistry}:latest"
}
success {
echo 'Docker image push success !'
sh "docker rmi ${dockerHubRegistry}:${BUILD_NUMBER}"
sh "docker rmi ${dockerHubRegistry}:latest"
}
}
}
stage('K8S Manifest Update') {
steps {
git credentialsId: '4b1dcb95-da80-40e8-8a26-312fb71aed5a',
url: 'https://github.com/modooSugangOrg/modoosugang_FE_manifest.git',
branch: 'main'
sh "cd ./overlay/dev && sed -i 's/newTag:.*\$/newTag: \"${BUILD_NUMBER}\"/g' kustomization.yaml"
sh 'git config --global user.name plooox'
sh 'git config --global user.email insukim97@gmail.com'
withCredentials([usernamePassword(credentialsId: '4b1dcb95-da80-40e8-8a26-312fb71aed5a', passwordVariable: 'pass', usernameVariable: 'user')]) {
sh "git pull origin main"
sh "git add ./overlay/dev"
sh "git commit -m '[:alien: UPDATE] modoosugang_client ${BUILD_NUMBER} image versioning'"
sh "git push http://$user:$pass@github.com/modooSugangOrg/modoosugang_FE_manifest.git main"
}
}
post {
failure {
echo 'K8S Manifest Update failure !'
}
success {
echo 'K8S Manifest Update success !'
}
}
}
}
}
|
6. github webhook 설정
- github에서 commit과 같은 이벤트가 발생시, HTTP POST Payload를 지정된 URL로 전송
- git repo가 업데이트 되면 자동으로 Jenkins 프로젝트가 실행
결과