파이프라인은 CI/CD를 하기 위해 버전 제어에서 최종 목표까지 소프트웨어를 가져오는 자동화된 프로세스를 지원하는 기능/플러그인이다.
파이프라인은 DSL(Domain Specific Language) 를 통해 코드로 작성한다.
Jenkinsfile은 'pipeline-as-conde'를 구현하기 위한 DSL로 작성된 텍스트 파일이며 Git과 같은 소스제어 저장소에 커밋될 수 있다.
젠킨스 버전2에서 사용하는 방식이다. 일반적인 jenkins의 웹 폼에서 구성하는 특정 구성과 작업을 정의한다.
pipeline {
agent {label worker_node1} # 작업을 실행한 장소 지정
stages {
stage('Checkout') { # 각 단계의 이름
steps {
git 'git@github.com:hsshin0602/source-maven-java-spring-hello-webapp.git'
}
}
stage('Build') {
steps {
sh 'mvn clean package'
}
}
}
}
블록: 지시문과 섹션을 포함하는 블록을 구성됨, 서술적 파이프라인은 반드시 pipeline으로 시작
파이프라인 흐름 내에서 하나 이상의 지시문 또는 스텝의 묶음
agent {
kubernetes {
yaml ```
parameters는 젠킨스 내에서만 참조할 수 있는 변수이고 environment는 shell에서 참조할 수 있는 변수이다.
environment 지시문
pipeline에서 정의한 것과 stage안에서 정의한 변수는 차이가 있다. stage안에서 정의한 environment는 stage내부에서만 사용가능하고 pipeline에서 정의한 것은 전체에서 사용 가능하다.
credentials() 메소드를 사용하여 미리 정의된 자격증명에 접근할 수 있다.
option 지시문
파이프라인에 적용할 옵션을 정의한다.
parameter 지시문
parameter를 참조 할때는 앞에 params. 를 붙여주는 것이 좋다. params.PERSON 등
tirggers 지시문
파이프라인을 자동으로 실행한 트리거를 정의한다.
1 cron: 파이프라인이 트리거 되어야 하는 주기, 깃에 커밋과 상관없이 계속 작업함
triggers { cron('H */4 * * 1-5') }
2 pollSCM: 새 소스 변경 사항이 있는지 확인하는 주기, 깃에 변화가 있으면 빌드
triggers { pollSCM('H */4 * * 1-5') }
3 upstream: 특정 파이프라인 잡이 실행되면 후속으로 파이프라인 잡을 실행할 수 있음, 하나의 프로젝트가 끝나면 다음 프로젝트가 바로 실행되도록 지정
triggers { upstream(upstreamProjects: 'job1,job2', threshold:
hudson.model.Result.SUCCESS) }
4 githubPush: GitHub 저장소의 웹훅(WebHook)에 의한 트리거
triggers { githubPush() }
pollSCM VS githubPush
Github <-> Jenkins
pollSCM은 젠컨스가 주체적으로 해당되는 저장소에 새로운 커밋이 있는지 확인한다.(polling)
githubPush는 깃허브에 새로운 커밋이 발생하면 깃허브가 젠킨스에 push hook 하는 것이다.
pipeline {
agent any
tools { maven 'apache-maven-3.0.1' } # maven과 툴의 이름 지정
📢 파이프라인 구문 참조
-> Snippet Generator: 스텝에서 실제로 사용할 명령을 DSL에 맞게 생성해준다.
-> Declarative Directive Generator(지시어 생성기): 파이프라인 지시문에 대한 스크립트를 생성
-> Declarative Online Documentation: 젠킨스 파일의 문법 확인
-> Global Variables Reference(전역변수 참조): 젠킨스에서 사용가능한 전역변수 모음
전역변수는 env.BUILD_NUMBER 형식으로 참조
새 프로젝트의 이름를 작성하고 유형을 pipeline으로 선택해서 프로젝트를 만든다.
이름: maven_pipeline
프로젝트의 이름과 밑에 scp의 workspace의 주소를 맞춰줘야 한다.
다른 것은 일단 건들지 않고 맨 밑에 pipeline에서 지금 실습에서는 pipeline script를 선택해서 서술형 파이프라인을 작성한 후 업로드해서 저장한다.
pipeline {
agent { label 'jenkins-node' }
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/hsshin0602/source-maven-java-spring-hello-webapp.git'
}
}
stage('Maven Build') {
steps {
sh 'mvn clean package'
}
}
stage('Deploy to Tomcat') {
steps {
sh 'scp /var/lib/jenkins/workspace/maven_pipeline/target/hello-world.war ubuntu@172.31.47.106:/var/lib/tomcat9/webapps'
}
}
}
}
이번 실습에서는 pipeline을 pipeline script from SCM를 이용해서 깃허브 저장소와 연동해 Jenkinsfile를 가져와 자동화를 할 것이다.
깃배쉬로 들어가서 작업을 진행한다. 홈디렉터리에 git 디렉터리를 만든 후 git 디렉터리 안에 내 저장소를 클론해온 다음 진행한다. 어제 작업을 진행했다면 생략가능
Jenkinsfile 조건: 트리거 추가, tomcat 주소 파라미터화, 작업 디렉터리를 환경변수로 사용
cd git/source-maven-java-spring-hello-webapp
# 젠킨스파일 작성, 내용은 위와 동일하게 진행해도 되지만 이번에는 조건에 부합한 형태로 진행
vi Jenkinsfile
pipeline {
agent { label 'jenkins-node' }
triggers {
pollSCM '* * * * *'
}
parameters {
string defaultValue: 'ubuntu', name: 'TOMCAT_USER_ID'
string defaultValue: '172.31.47.106', name: 'TOMCAT_IP'
string defaultValue: '/var/lib/tomcat9/webapps', name: 'TOMCAT_WEBAPP_DIR'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/hsshin0602/source-maven-java-spring-hello-webapp.git'
}
}
stage('Maven Build') {
tools { maven 'Maven-3' } # maven 툴 지정
steps {
sh 'mvn clean package'
}
}
stage('Deploy to Tomcat') {
steps {
sh "scp $(env.WORKPASCE)/target/hello-world.war ${params.TOMCAT_LOGIN_USER}@${params.TOMCAT_IP}:${params.TOMCAT_WEBAPP_DIR}"
}
}
}
}
# Jenkinsfile 원격 깃 저장소에 푸쉬
git add .
git commit -m 'add Jenkinsfile'
git push
-> env.WORKPASCE환경변수: 프로젝트의 workspace의 절대경로
이전에는 트리거가 없었기 때문에 트리거의 존재를 모른다. 따라서 맨 처음에 한번은 수동으로 빌드를 실행시켜줘야한다.
""(쌍따옴표)와 ''(홑따옴표) 차이
''는 안쪽의 들어가는 모든 특수문자를 탈출 시킨다. 따라서 글자 그대로 명령어를 실행시킨다.
""는 안쪽에 들어가는 특수문자를 인식하고 $ ,` ,\ ,! 등은 탈출시키지 않는다. 즉 이런 특수문자를 명령으로 적용한다는 것이다.MSG=hello
echo '${MSG} world'
-> ${MSG} worldecho "${MSG} world"
-> hello world
작업 플로우: ec2 인스턴스(VM)에 도커 설치, 컨테이너를 젠킨스로 설치, 빌드할떄 노드가 필요한데 이는 동적으로 생성 삭제를 할것, 최종적으로 해당되는 이미지를 빌드를해서 docker-hub(저장소)에 push를 하고 이를 다시 가져와서 배포를 진행할 것
-> 젠킨스 파드는 영구적으로 실행, 대신 노드로 되는 것들은 필요할떄만 생성 후 삭제
인스턴스 생성
이름: Jenkins-Docker, 우분투 20.4 이미지, t3 medium, 보안그룹: SSH, HTTP, TCP 8080, 키페어 등록, 스토리지: 40G 로 설정한 이후 인스턴스를 생성한다.
# 이름 설정
sudo hostnamectl set-hostname jenkins-docker
exec bash
# USER에 docker 그룹 부여
sudo usermod -aG docker $USER
exit
# 재접속 시 도커 사용 가능
# 관리 목적상 네트워크 하나 생성
docker network create jenkins
# DinD(Docker in Docker) 컨테이너 실행
docker container run --name docker-dind --detach \
--privileged --network jenkins --network-alias docker \
--env DOCKER_TLS_CERTDIR=/certs \
--volume jenkins-docker-certs:/certs/client \
--volume jenkins-data:/var/jenkins_home \
--volume docker:/var/lib/docker \
--publish 2376:2376 \
--restart=always \
docker:dind --storage-driver overlay2
Jenkins 컨테이너가 호스트의 Docker 데몬으로 접근하기 위한 컨테이너이다.
mkdir jenkins
cd jenkins
# 도커 파일 작성
vi Dockerfile
FROM jenkins/jenkins:lts-jdk11 # 자바 11이 설치되어있는 젠킨스 이미지
USER root # 루트권한으로 도커명령 설치
RUN apt-get update && apt-get install -y lsb-release
RUN curl -fsSLo /usr/share/keyrings/docker-archive-keyring.asc \
https://download.docker.com/linux/debian/gpg
RUN echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/usr/share/keyrings/docker-archive-keyring.asc] \
https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
RUN apt-get update && apt-get install -y docker-ce-cli
USER jenkins # 플러그인 설치
RUN jenkins-plugin-cli --plugins "docker-plugin docker-workflow"
# 이미지 로컬에 빌드
docker image build -t jenkins-docker:lts-jdk11 .
# Jenkins 컨테이너 실행
docker run --name jenkins-docker \
--restart=always \
--detach \
--network jenkins \
--env DOCKER_HOST=tcp://docker:2376 \
--env DOCKER_CERT_PATH=/certs/client \
--env DOCKER_TLS_VERIFY=1 \
--publish 8080:8080 \
--publish 50000:50000 \
--volume jenkins-data:/var/jenkins_home \
--volume jenkins-docker-certs:/certs/client:ro \
jenkins-docker:lts-jdk11
# jenkins-docker 내부로 진입
docker container exec -it jenkins-docker bash
# 안에서 도커 명령 실행 가능
$ docker container ls
# Unlock Jenkins 비번 복붙
docker exec jenkins-docker cat /var/jenkins_home/secrets/initialAdminPassword
노드관리 -> Configure Clouds -> Docker 선택(도커 플러그인을 설치했기에 보임)
이름: Docker
Docker Host URI: tcp://docker:2376(dind 컨테이너를 의미, 위에서 포트와 이름을 지정함)
-> test connection을 눌러 통신이 되나 확인해보는데 현재 테스트를 해보면 400오류(client측 오류)가 나온다. 여기서는 젠킨스가 도커 데몬에게 통신하기 위해서 인증서를 사용하는데 이 인증서는 certs 디렉터리에 만들어져있다.
따라서 Server credentials에 등록를 해준다.
Kind: X.509 client Certificate
# Client Key 복붙
docker container exec jenkins-docker ls /certs/client/key.pem
# Client Certificate 복붙
docker container exec jenkins-docker ls /certs/client/cert.pem
# Server CA Certificate 복붙
docker container exec jenkins-docker ls /certs/client/ca.pem
ID: docker-client-certs로 지정한 이후 add 진행
프로젝트 생성: docker_test_pipeline, pipeline 유형 선택
도커-젠킨스 파일 테스트 깃허브
pipeline script from SCM을 선택한 후 위 깃 저장소의 주소로 등록한다.브랜치를 main으로 변경한 이후 순차적으로 실습을 진행할 것이기에 처음에는 Script Path를 Jenkinsfile-1로 설정한 후 저장함
# 작업을 진행하는 동안에 잠깐 컨테이너가 생성됨
docker container exec jenkins-docker docker ps
-> 컨테이너 보임
# 작업이 종료되면 컨테이너가 제거됨
docker container exec jenkins-docker docker ps
-> 컨테이너 안보임
docker { image 'node:16-alpine' }: 젠킨스에는 노드라는 명령이 없는데 docker로 진행하면 지정한 이미지로 컨테이너를 띄울 수있고 여기서 생성한 컨테이너 내부에서 명령을 실행한다.