
- 블록 : 괄호를 열고 닫은 구간, 시작과 끝이 있는 코드의 묶음
 
-> pipeline 블록으로 시작된다- 섹션 : 파이프라인 흐름 내에서 하나 이상의 지시문 또는 스텝의 묶음
 
- agent섹션 : 전체 파이프라인 또는 특정 단계가 실행되는 노드 지정 , 각 단계마다 실행되는 노드를 다르게 할 수 있다
 - stages섹션 : 하나 이상의 순서가 있는 stage 묶음
 - steps섹션 : stage 지시문에서 실행할 하나 이상의 작업
 - post섹션 : stages 또는 stage의 마지막에 실행할 추가 작업
 
cron : 파이프라인이 트리거 되어야 하는 주기
	triggers { cron('H */4 * * 1-5') }    
pollSCM : 새 소스 변경 사항이 있는지 확인하는 주기
	triggers { pollSCM('H */4 * * 1-5') }
upstream : 특정 파이프라인 잡이 실행되면, 후속으로 파이프라인 잡을 실행할 수 있음
	triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }
githubPush : GitHub저장소의 웹훅(WebHook)에 의해 트리거
	triggers { githubPush() }
branch : 특정 브랜치인 경우
when { branch 'master'}
environment : 환경 변수의 값과 일치하는 경우
when { environment name: 'DEPLOY_TO', value: 'production' }
expression : Groovy표현식이 참인 경우
when { expression { return params.DEBUG_BUILD } }
allOf : 여러 조건 모두 참인 경우(AND)
when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
anyOf : 여러 조건 중 하나가 참인 경우(OR)
when { anyOf { branch 'master'; branch 'staging' } }
not : 조건이 거짓인 경우
when { not { branch 'master' } }
Jenkins에는 다양한 파이프라인을 쉽게 생성할 수 있도록 문서 기능이 웹에 내장되어 있고, 플러그인을 설치하면 내장 문서도 관련된 내용이 자동으로 업데이트 된다
새로운 item클릭
1 . Jenkinsfile을 이용한 간단한 war파일 배포
pipeline{
    agent{
        label 'jenkins-node'
    }
    stages{
        stage('Checkout'){
            steps{
                git branch: 'main', url: 'https://github.com/suhwan12/abc.git'
            }
        }
        stage('Build'){
            steps{
                sh 'mvn clean package'
            }
        }
        stage('Deploy'){
            steps{
                sh 'scp /var/lib/jenkins/workspace/test_pipeline/target/hello-world.war ubuntu@172.31.45.89:/var/lib/tomcat9/webapps'
            }
        }
    }
}
2 . github을 이용한 pipeline 생성
pipeline script from SCM 선택
git WorkDIR에서
vi Jenkinsfile
# 위의 코드를 붙여넣는다
git add .
git commit -m 'new pipeline'
git push
Jenkins에서 다시 빌드해보면 스크립트를 쓰지 않았는데도 불구하고
외부(git-bash...)에서Jenkinsfile생성과 commit 및 github에 push를 통해서 등록한 git주소에 해당하는 git repo에 Jenkinsfile이 올라와 해당 Jenkinsfile이 빌드된다
📒 Jenkinsfile을 작성할때 파이프라인 관련 내장문서 사이트를 이용하면 좀 더 쉽게 작성할 수 있다.
3 . 트리거와 파라미터, env 사용
pipeline{
    agent{
        label 'jenkins-node'
    }
    triggers {
       pollSCM '* * * * *' # 트리거 사용 
    } 
    parameters { # 파라미터 사용 
      string defaultValue: '172.31.45.89', name: 'TOMCAT_IP'
      string defaultValue: 'ubuntu', name: 'TOMCAT_LOGIN_USER'
      string defaultValue: '/var/lib/tomcat9/webapps', name: 'TOMCAT_WEBAPP_DIR'
    }
    stages{
        stage('Checkout'){
            steps{
                git branch: 'main', url: 'https://github.com/suhwan12/abc.git'
            }
        }
        stage('Build'){
            tools{ # global 도구 사용 
              jdk 'Java-11'
              maven 'Maven-3'
            }
            steps{
                sh 'mvn clean package'
            }
        }
        stage('Deploy'){
            steps{
                sh "scp ${env.WORKSPACE}/target/*.war ${params.TOMCAT_LOGIN_USER}@${params.TOMCAT_IP}:${params.TOMCAT_WEBAPP_DIR}" 
            } # 설정한 param사용 , 큰 따옴표 사용 
        }
    }
}

인스턴스 생성 (jenkins-docker)
ssh -i pem키 ubuntu@퍼블릭ip # 인스턴스 접속 
sudo hostnamectl set-hostname jenkins-docker
exec bash # hostname 변경 
sudo apt update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg    
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null    
sudo apt-get update 
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 
#도커 설치 
sudo usermod -aG docker $USER
exit
#현재사용자를 docker그룹에 포함
📒 install Jenkins with docker 참조
dokcer network create jenkins # 새로운 네트워크 생성
mkdir jenkins
cd jenkins  
# jenkins디렉터리 생성 및 이동 (필수 x)
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
# docker container생성 
vi Dockerfile # jenkins 이미지 생성하기 위한 Dockerfile 생성 
FROM jenkins/jenkins:lts-jdk11 
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 image 생성 
docker container 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 container 생성 
docker container exec -it jenkins-docker bash 
#jenkins컨테이너 접속
브라우저에 jenkins-docker 퍼블릭IP:8080입력
cat /var/jenkins_home/secrets/initialAdminPassword
# exec 명령으로 Jenkins컨테이너에 접속 후 암호를 찾아야 한다.
암호입력후 플러그인 설치 및 계정 설정 후 Jenkins 접속
노드관리 접속 후 아래 보이는 것처럼 Configure Clouds 클릭
node의 이름과 통신할 docker의 host URI를 설정
host URI는 docker container생성할 때 설정해주었다.
server credentials 클릭 후 x.509설정
아래와 같이 총 세개의 키를 등록해주어야 한다.
docker container exec jenkins-docker cat /certs/client/key.pem
# client key 복사 후 등록
docker container exec jenkins-docker cat /certs/client/cert.pem
# client certificate 복사 후 등록
docker container exec jenkins-docker cat /certs/client/ca.pem
# server ca certificate 복사 후 등록
1 . jenkinsfile-1
pipeline {
  agent {
    docker { image 'node:16-alpine' }
  }
  stages {
    stage('Test') {
      steps {
        sh 'node --version'
      }
    }
  }
}
#jenkinsfile-1의 내용 
2 . jenkinsfile2
pipeline {
  agent none
  stages {
    stage('Back-end') {
      agent {
        docker { image 'maven:3.8.1-adoptopenjdk-11' }
      }
      steps {
        sh 'mvn --version'
      }
    }
    stage('Front-end') {
      agent {
        docker { image 'node:16-alpine' }
      }
      steps {
        sh 'node --version'
      }
    }
  }
}
docker , 쿠버네티스를 사용하는 가장 큰 장점
- 이전에 가상머신을 이용해서 linux에서 jenkins를 사용했을 때는 노드를 생성하기 위해 VM(EC2)를 생성했었다. 컨트롤러가 작업이 있을 때 해당 노드에 작업을 시켜 수행하였다.
 - 하지만 VM의 특성상 노드가 일을 하든 안하든 항상 띄워놔야 하기 때문에 리소스와 요금의 낭비가 심하다.
 - dind 구조로 도커안에 jenkins를 설치한다면 비록 설치과정은 매우 어렵고 복잡하지만 jenkins가 작업을 수행하기 위한 노드를 VM이 아닌 컨테이너로 생성한다.
 
-> 도커안에 jenkins가 도커 명령을 사용할 수 있도록 설치과정에서 설정하였는데, 이것은 jenkins가 컨테이너를 생성하기 위해서 필수적이다.- 위의 1번 , 2번 실습에서 알 수 있다시피, Jenkins컨트롤러가 작업이 생긴다면 일시적으로 컨테이너(=노드)를 띄워서 작업을 수행 후 종료시킨다.
 
-> 컨테이너 특성 상 VM과 다르게 항상 띄워놓고 있지 않아도 된다
-> 따라서 불필요하게 노드 리소스를 띄워놓을 필요가 없다
📒 결론 : 도커나 쿠버네티스는 노드를 동적으로 생성해서 필요할때 생성, 필요하지 않으면 사용하지 않을 수 있다. 이것이 복잡하지만 굳이 도커나 쿠버네티스를 이용해 컨테이너로 작업을 하는 이유이다.
1 . dockerhub에 로그인하기 위한 인증
2 . Jenkinsfile
pipeline{
    agent none
    triggers{
    pollSCM '* * * * *'
    }
    parameters {
    string name: 'IMAGE_NAME' , defaultValue: 'hello-world'
    string name: 'IMAGE_REGISTRY_ACCOUNT', defaultValue: 'suhwan11' 
    }
    # 계정명과 이미지명 변수화 
    stages {
        stage('SCM Checkout'){
        agent any
            steps{
                git branch: 'main', url: 'https://github.com/suhwan12/abc.git'
            }
        }
        stage('maven build project'){
        agent{ docker { image 'maven:3-openjdk-8' }} 
            steps{
                sh 'mvn clean package -DskipTests=true' # test는 따로하기 위해 스킵한다
            }
        }
        stage('Test Maven Project'){  
            agent { docker { image 'maven:3-openjdk-8'} }
            steps{
              sh 'mvn test'
            }
        } # test는 따로 진행
        stage('build docker image'){
        agent any
            steps{
                sh "docker image build -t ${params.IMAGE_NAME} ."
            }
        }
        stage('Tagging Docker Image'){
            agent any
            steps{
            sh "docker image tag ${params.IMAGE_NAME} ${params.IMAGE_REGISTRY_ACCOUNT}/${params.IMAGE_NAME}"
            }
        }
        stage('publishing Docker Image'){
            agent any
            steps{
                withDockerRegistry(credentialsId: 'docker-hub-token', url: 'https://index.docker.io/v1/') {
            sh "docker image push ${params.IMAGE_REGISTRY_ACCOUNT}/${params.IMAGE_NAME}"
            # 1번에서 생성한 script 추가(= 로그인) 및 로그인후 수행할 명령어 작성 
            }
        }
    }
    }
}
3 . Dockerfile
FROM tomcat:9-jre8
COPY target/hello-world.war /usr/local/tomcat/webapps/
4 . 프로젝트 빌드
git add .
git commit -m 'Add jenkinsfile & dockerfile'
git push 
docker container exec jenkins-docker docker image ls
docker container run -d --name myweb -p 80:8080 suhwan11:hello-world 
5 . 이미지 버전관리
- docker, kubernetes는 기본적으로 이미지를 풀링할때 풀링 정책(missing, always, never)이 있다
 - default 정책(missing) : 로컬에 이미 이미지가 존재하면 풀링 x
 - 따라서 버전을 바꾼 새로운 latest이미지를 생성하면 기존의 이미지와 해쉬값은 다르더라도 이미지의 이름이 똑같기 때문에 run 을 할때 버전이 바뀐 새로운 이미지를 풀링하지 않는다
 
docker container run -d --name myweb --pull always -p 80:8080 suhwan11/hello-world
docker container run -d --name myweb2 -p 80:8080 suhwan11/hello-world:60
📒 현업에서는 BUILD_NUMBER를 사용하지 않고 BUILD_TAG를 사용한 semantic versioning을 구현한다.