
Jenkins 서버에 새로운 워커노드를 추가해서 마스터노드가 가지고 있는 작업을 분할시켜서 환경을 구축해보자.
지금까지 필자는 Jenkins를 단일 서버로 구성한 상태에서 사용을 해 왔다. (= Jenkins Master)
Jenkins Master에서 사용자 요청에 의해서 빌드를 하고 배포하는 작업을 진행해 왔었는데 그러한 작업을 자신한테 추가되어 있는 다른 Slave에다가 작업을 요청해줌으로써 업무에 대한 분담을 처리하는 구조라고 보면 된다.
Slave는 윈도우즈, 리눅스 다음에 도커 컨테이너 형태로 구성 가능하다.
Slave 역할은 다음과 같다.
Remote에서 실행될 수 있는 젠킨스의 노드로, 이를 워커 노드라고 이해하면 쉬울 듯 하다.
즉 Jenkins Master가 가지고 있는 요청 작업을 받아서 빌드를 처리 / 배포 처리 등의 작업을 진행
Master에서 자신에게 할당되어진 JOB을 나눠서 Slave에게 분산해서 실행하는 구조
다양한 운영체제에서 실행 가능
Jenkins 프로젝트를 생성 시 특정한 Slave를 지정해서 빌드를 할 수 있고, 아니면 조금 더 리소스가 충분히 확보되어 있는 Slave에다가 자동적으로 배포가 할당되게 구성 가능
앞서 말한 Jenkins Master에 Jenkins Slave Node를 추가시켜 보자.
Jenkins 서버 자체를 Docker Container 형태로 기동하기 때문에 이번에 추가하려고 하는 Slave Node도 Docker Container 형태로 기동을 하였다.

docker run --privileged --name jenkins-node1 -itd -p 30022:22 -e container=docker -v /sys/fs/cgroup:/sys/fs/cgroup --cgroupns=host edowon0623/docker:latest /usr/sbin/init
이전에 docker-server 컨테이너 생성할 때 사용한 이미지를 가지고 컨테이너를 만들었다.
SSH + Docker가 설치되어 있는 컨테이너 사용 포스팅
현재 사용하지 않는 포트번호인 30022번 , 이름은 jenkins-node1로 하였다.

docker ps -a | findstr jenkins-node1
jenkins-node1가 만들어진 것을 확인할 수 있다.

ssh root@localhost -p 30022
jenkins-node1로 호스트pc에서 root로 ssh 접속을 하였다.
패스워드를 물어보면 P@ssw0rd 를 입력해주면 접속이 된다.


yum install -y ncurses git
yum list java*jdk-devel
yum install -y java-11-openjdk-devel.x86_64
현재 이미지에는 java가 설치되어 있지 않다.
지금 추가하려고 하는 작업은 Jenkins Master가 가지고 있는 어떤 이 job을 TCP/IP 통신을 통해 가지고 Slave에 전달을 한다.
Slave에서 마스터로부터 전달받았던 job을 자신이 빌드, 배포 등의 과정을 처리해야 한다.
어떤 작업을 Master 노드로부터 전달받기 위해서 JVM이 필요하다.
그래서 JDK를 설치해 주기 위해 위와 같은 설치과정이 포함되었다.

docker exec -it jenkins-server bash
Master도 CLI로 접속해주었다.
Master는 초록색 테마를 적용시켰고, Node1은 흰색 테마를 적용시켰다.

hostname -i
ssh root@172.17.0.6
Node1은 172.17.0.6 주소로 기동 중이다.
컨테이너 간에 접속할 때는 ip주소를 가지고 접속이 가능하다.
Master에서 Node1로 접속 시 Node1의 ip주소를 가지고 ssh 접속을 시도하면 패스워드를 묻는다.
이때도 P@ssw0rd를 입력하면 정상 진입이 가능하다.
역시 ssh 명령을 이용해서 접속할 때는 키가 필요한데 실제 작업을 할 때, 미리 키 값을 복사하여 접속 시엔 키 값 없이도 바로 진입 가능하도록 만들어보자.

ssh-copy-id root@172.17.0.6
ssh root@172.17.0.6
위처럼 Master에서 Node1로 접속하기 위해 키 값을 복사하면 추후에 ssh 접속 시 패스워드 물음 없이 바로 접속이 가능하다.

Master 서버와 Node1 서버 구성을 완료했다면 젠킨스로 돌아와 대시보드 -> Jenkins 관리 -> Nodes로 이동한다.
현재 등록되어진 노드는 Jenkins 서버 Master밖에 없는데, 여기에 오른쪽 위 New Node를 클릭해준다.

Node1을 등록해주기 위해 Name은 slave1이라고 해주고, Description은 자유롭게 작성하고, Number of executors는 5, Romote root directiory는 /root/slave1이라고 적어주었다.
Number of executors는 Node1가 Jenkins Master로부터 빌드하거나 배포하는 작업에 대한 요청을 받았을 때 처리시켜 줄 수 있는 최대 개수를 5개로 지정하겠다고 설정해준 것이다.
Romote root directiory는 Jenkins Master에서 빌드가 끝나면 Workspace라는 폴더가 생기는데 해당 폴더에 생성했었던 프로젝트 단위로 실제 폴더가 생기고 결과 파일들이 Node1에 저장되고자 하는 폴더를 지정하는 것이다.

pwd
ls -al
mkdir slave1
cd slave1
pwd
그래서 Node1 서버에서 루트 폴더 위치의 slave1 폴더를 생성해주었다.

Host는 Node1의 ip주소를 적어주고, Credentials는 Add를 눌러 추가해주었다.

Username은 root, Password는 P@ssw0rd, Id는 slave1_root 이다.

여기까지 작성하였다면 Save를 눌러 저장해준다.

새로운 Node가 생긴 것을 확인할 수 있고, 생긴 Node를 클릭하면

정상적으로 slave1 노드가 오류없이 잘 생성됨을 알 수 있다.

사전에 키 값을 복사하였기 때문에 N/A라고 뜨지 않고 Arckitecture, Clock Difference, From Dist Space 등등 확인도 가능하다.

기존에 사용했던 My-Fisrt-Project에 구성 정보를 수정하여 빌드해보자.

구성에 들어가게 되면 상단에 Restrict where this project can be run 이라는 체크박스를 클릭해주고 Label Expression에 방금 생성한 노드 slave1를 작성해준다.
다시 말해 slave1 에다가만 해당 프로젝트를 빌드하겠다는 뜻이다.

빌드를 진행하고 콘솔을 확인해보면 SUCCESS가 떴다.

Node1 서버를 확인해보면 root의 slave1 폴더에 workspace 폴더가 생겼고, workspace 폴더에는 빌드했던 My-Frist-Project가 생겨난 걸 볼 수 있다.
My-Sencond-Project는 파이프라인을 연결해 놨기 때문에 생성이 된 것이다.
Jenkins Master Slave 환경에서 실행할 두 번째 예제는 My-Third-Pipline 프로젝트에 Pipline script를 변경하여 사용해보자.

docker run --privileged --name jenkins-node2 -itd -p 40022:22 -e container=docker -v /sys/fs/cgroup:/sys/fs/cgroup --cgroupns=host edowon0623/docker:latest /usr/sbin/init
새로운 두 번째 Slave2를 만들어주었다. 이름은 jenkins-node2, 포트번호는 40022번을 사용하였다.

docker network inspect bridge
지금까지 도커 컨테이너에 만든 서버 목록이다.
jenkins-node2는 172.17.0.7 주소로 만들어졌다.

ssh root@localhost -p 40022
Node2로 ssh 접속을 하고

yum install -y ncurses git
yum list java*jdk-devel
yum install -y java-11-openjdk-devel.x86_64
java jdk 설치를 위해 설치파일들도 다운받았다.

헷갈릴 것 같아 색 테마를 정하였다. 흰색은 Node1, 노랑은 Node2, 초록은 Master이다.

ssh-copy-id root@172.17.0.7
Master에서 Node2의 패스워드를 복사하여 Master -> Node2 접속 시 언제든 패스워드 입력 없이 접속 가능하게 만들었다.

대시보드 -> Jenkins 관리 -> Nodes로 이동해 새로운 노드를 추가해주기 위해 New Node를 클릭하고

Name은 slave2, Number of excutors는 1, Remote root directory는/root/slave2로 하였다.
Node2 서버에서는 Remote root directory 설정에 맞게 root 폴더에서 slave2 폴더를 생성하였다.

Host 주소는 Node2 서버 주소 172.17.0,7을 적어주고, Credentials은 salve1 노드 생성 시 만들었던 root/******를 재사용 하였다.

정상적으로 만들어졌고,

slave2 노드의 기타 사항들도 잘 보여진다.

My-Third-Pipline를 선택해주고

구성정보를 수정하는데, 맨 하단으로 내려가 Pipeline script를 수정해준다.
pipeline {
agent {
label 'slave1'
}
tools {
maven 'Maven 3.8.5'
}
stages {
stage('github clone'){
steps {
git branch: 'main', url: 'https://github.com/10000JI/cicd-web-project'
}
}
stage('build'){
steps {
sh '''
echo build start
mvn clean compile package -Dskiptests=true
'''
}
}
// stage('SonarQube analysis'){
// steps{
// withSonarQubeEnv('SonarQube-server'){
// sh 'mvn sonar:sonar'
// }
// }
// }
// stage('deploy'){
// steps{
// deploy adapters: [tomcat9(credentialsId: 'deployer_user', path: '', url: 'http://172.30.1.52:8088')], contextPath: null, war: '**/*.war'
// }
// }
// stage('ssh publisher'){
// steps {
// sshPublisher(publishers: [sshPublisherDesc(configName: 'docker-server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'docker build --tag 10000ji/devops_exam1 -f Dockerfile .', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '.', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/*.war')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
// }
// }
}
}
stage 'SonarQube analysis'는 잠시 주석처리하고, 이전에 angent가 any로 되어있던 것을 label 'slave1'로 수정하여 빌드해보겠다.
Apply 후 저장해준다.

My-Third-Pipline 빌드를 시작하고

콘솔을 확인해보니 /root/salve1/workspace/My-Third-Pipleline에 slave1이 실행되었다고 떴다.

slave1에 해당 위치로 이동하여 target 폴더까지 이동하면 hello-wolrd.war 파일을 확인할 수

이번에 My-Third-Pipeline에 Pipeline script 부분의 agent를 salve1이 아닌 salve2로 변경하였다.
pipeline {
agent {
label 'slave2'
}
tools {
maven 'Maven 3.8.5'
}
stages {
stage('github clone'){
steps {
git branch: 'main', url: 'https://github.com/10000JI/cicd-web-project'
}
}
stage('build'){
steps {
sh '''
echo build start
mvn clean compile package -Dskiptests=true
'''
}
}
// stage('SonarQube analysis'){
// steps{
// withSonarQubeEnv('SonarQube-server'){
// sh 'mvn sonar:sonar'
// }
// }
// }
// stage('deploy'){
// steps{
// deploy adapters: [tomcat9(credentialsId: 'deployer_user', path: '', url: 'http://172.30.1.52:8088')], contextPath: null, war: '**/*.war'
// }
// }
// stage('ssh publisher'){
// steps {
// sshPublisher(publishers: [sshPublisherDesc(configName: 'docker-server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'docker build --tag 10000ji/devops_exam1 -f Dockerfile .', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '.', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/*.war')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
// }
// }
}
}

빌드 후

콘솔을 확인해보면 SUCCESS가 떴고

동일하게 Node2에 폴더를 확인해보면 hello-world.war이 생겼다.
이렇게 Pipeline 빌드를 하면서 진행하고자 하는 agnet의 위치를 slave1, slave2처럼 조절을 해서 원하는 노드에다가 Jenkins 결과물을 전달 하여 빌드할 수 있는 구조를 살펴보았다.