Jenkins Basic

Jenkins User hand book 및 기타 사이트 참고해서 간단하게 필요한 내용만 서머리함 (https://jenkins.io/user-handbook.pdf)

기타 참고

Pipeline

개발, 통합 운영 배포 등이 가능한 플러그인 들을 하나의 Suit로 만드는 것을 파이프 라인이라고 한다.

자동 화된 파이프라인 설정 기능이 있으나, 보다 복잡한 사용이 필요 할 경우 두가지 DSL(Domain Specific Language)를 제공한다.

  • Declarative
  • Script : Groovy

groovy syntax는 다음에서 확인 가능하다.

http://groovy-lang.org/syntax.html

Pipeline Flow

Development -> Stage1 -> Stage2 -> Production

의 flow를 말함

DSL define

jenkins에서 활용 가능한 global variable은 다음에서 확인 가능하다

http://jenkins.mina.blue/pipeline-syntax/globals

docker, pipeline, env, params,currentBuild, scm 을 볼 수 있다.

  • agent : 파이프라인의 workspace, executor 할당요청을 Jenkins에게 한다

  • stage : stage "Build" "Test" "Deploy" 등과 같이 step group

  • steps : stage 안에서 일어날 task

    Jenkins에서는 http://jenkinsurl/pipeline-syntax/ 를 통해서 step define 가능함

  • sh : shell command starter

  • junit : plugin:junit 에서 제공하는 기능 (리포트 등을 통합)

  • node : 파이프라인에서 일어나는 행위(steps)는 대상이 되는 node에서 일어남 (여러 노드를 설정할 수있음)

    stage('Test') {
        node('linux') {
            checkout scm
            try {
                unstash 'app'
                sh 'make check'
            }
            finally {
                junit '**/target/*.xml'
            }
        }
        node('windows') {
            checkout scm
            try {
                unstash 'app'
                bat 'make check'
            }
            finally {
                junit '**/target/*.xml'
            }
        }
    }
  • echo : string 값 출력 (echo "hello")

  • env : script 파이프 라인에서 접근 가능한 환경 값 (env.PATH, env,BUILD_ID)

env의 경우 nodejs에서 사용하는 process.env 객체를 지칭하게 된다. 해당 값은 jenkins에서 다음과 같이 setting해서 사용 할 수있다.

node {
    checkout scm
    env.VUE_PUBLIC_PATH = '/'
    env.ENV_TEST = 'real data'
...

만약에 node 프로젝트에 .env 파일이 있다고 해도, jenkins에서 정의한 env 값이 우선하게 된다.

  • params : 파이프라인 내에서 정의된 parameters (MAP) (params.MY_PARAM_NAME)

  • currentBuild : 현재 파이프라인의 정보를 확인 가능 (currentBuild.result, currentBuild.displayName)

    http://jenkinsurl/pipeline-syntax/globals#currentBuild

  • checkout : source control (사이트?)에서 부터 코드를 다운로드 받는다. 주로 scm을 사용한다. (checkout scm)

  • archiveArtifacts : 앞서 빌드된 파일들을 저장해 놓는 역할을 한다. (archiveArtifacts artifacts: '*/arget/tar', fingerprint: true)

  • withEnv : global 또는 특정 영역에 variable을 define하는 행동을 한다. 아래 처럼 사용 가능하다.

  • properties : 해당 파이프라인에서 사용 사능한 일시적 properties를 설정한다. 해당 내용은 jenkins 파이프 라인 "설정" 화면에서 "General" 탭의 "이 빌드는 매개 변수가 있습니다. (Build with Parameters)"를 사용하면 되는데, 해당 설정을 하고 나면 파이프라인 실행 이름이 기존 "Build"에서 "Build with Parameters"로 변경되고 해당 value 값을 사용자가 빌드 요청시마다 수정 할 수 있게 된다.

    properties([parameters([string(defaultValue: 'Hello', description: 'How should I greetthe world?', name: 'Greeting3'),string(defaultValue: 'Hello2', description: 'How should I greetthe world22?', name: 'Greeting2')])])
    
    node {
        def singlyQuoted = 'Hello'
    
        withEnv(["global='value"]){
    
            stage('Build'){
                withEnv(["test='one"]){
                    echo "Heloo ${global}, ${test}, ${singlyQuoted}, ${env.BUILD_ID} on ${env.JENKINS_URL}"
                }
    
                withEnv(["test='one","test2='two"]){
                    echo "Heloo ${global}, ${test}, ${test2}, ${singlyQuoted}, ${env.BUILD_ID} on ${env.JENKINS_URL}"
                }
            }
        }
    
        stage('Props'){
            echo "${params.Greeting2} World"
        }
    }
  • parallel : 일반적인 pipeline은 순차적인 순서로 진행이 된다. 그러나 동시 처리를 원한다면 다음과 같이 parallel 명령을 사용 할 수 있다.

    stage('Test') {
        parallel linux: {
            node('linux') {
                checkout scm
                try {
                    unstash 'app'
                    sh 'make check'
                }
                finally {
                    junit '**/target/*.xml'
                }
            }
        },
        windows: {
            node('windows') {
                /* .. snip .. */
            }
        }
    }
  •  

Test or Report

TEST는 빌드가 완료 된 이후 junit등과 같이 테스트 후 리포트를 내놓는 부분을 말한다

테스트 된 결과를 Visualization 하게 보여 주거나 test에 대한 reportingm redording 하는 많은 플러그인을 제공 하고 있다.

https://plugins.jenkins.io/?labels=report

  • sh 'make check || true'
    0 exit code가 항상 나오게 하는 코드로써, 특별한 행위를 하지는 않는다. 전체 적으로 환경에 대한 clear를 하는 역할로 보인다.(linux kernel 개발할때 중간 중간 넣어 주는 용도로 넣는 다는 이야기가 있다.)
  • junit '*/target/.xml'
    target 이하 떨어진 xml 파일들을 캡쳐 하는 역할이다.

Deploy

// Script //
node {
    /* .. snip .. */
    stage('Deploy') {
        if (currentBuild.result == null || currentBuild.result == 'SUCCESS') { ①
            sh 'make publish'
        }
    }
    /* .. snip .. */
}

currentBuild.result가 null 이란 의미는 앞서 아무런 이상 결과 값이 없다는 뜻이다.

Multibranch Pipeline

scm(여기서는 git)에서 여러개의 파이프라인을 처리하는 방법이다.

New Item > Multibranch Pipeline > Branch Source > Project Repository > Url 입력

해당 git url 에서 Jenkins 파일이 있는 branch 대상으로 자동으로 파이프라인이 생성된다.

  • Scan Multibranch Pipeline Triggers
    Periodically if not otherwise run : 일정 기간별로 scan 처리 한다.

global variable 이 추가 된다.

  • BRANCH_NAME : 특정 branch name 등
  • CHANGE_ID : full request number 등 수정 id

Docker

jenkins 파이프라인 2.5 이상은 docker를 사용 가능하다.

상위에서 소개한 ebook은 최신화 되어 있지 않다. 다음 url을 참고 하는게 좋을거 같다.

https://jenkins.io/doc/book/pipeline/docker/

Docker test
node {
    /* Requires the Docker Pipeline plugin to be installed */
    docker.image('node:7-alpine').inside {
        stage('Test') {
            sh 'node --version'
        }
    }
}

jenkins user 설정을 root로 안하는 이상 permission denied 가 날 확률이 높다.

docker user group을 만들고 해당 docker group에 사용자를 add 하여 처리 하는 것이 좋다

sudo groupadd docker

sudo usermod -aG docker $USER

sudo usermod -aG docker jenkins

sudo systemctl restart jenkins

처리 후 log out, log in을 하면 처리 완료 된다고 하지만, 나는 서버 재기동 시키고 나서야 적용 되었다.

Started by user devops
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline (hide)
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/example
[Pipeline] {
[Pipeline] sh
+ docker inspect -f . node:7-alpine

Error: No such object: node:7-alpine
[Pipeline] sh
+ docker pull node:7-alpine
Trying to pull repository docker.io/library/node ... 
7-alpine: Pulling from docker.io/library/node
90f4dba627d6: Pulling fs layer
1e674d353187: Pulling fs layer
d3a64c0f885a: Pulling fs layer
d3a64c0f885a: Verifying Checksum
d3a64c0f885a: Download complete
90f4dba627d6: Verifying Checksum
90f4dba627d6: Download complete
90f4dba627d6: Pull complete
1e674d353187: Verifying Checksum
1e674d353187: Download complete
1e674d353187: Pull complete
d3a64c0f885a: Pull complete
Digest: sha256:4954ce53247180e207772f936223b11d52a7e4ee712dfe73fe2a75e39f785067
Status: Downloaded newer image for docker.io/node:7-alpine
[Pipeline] withDockerContainer
Jenkins does not seem to be running inside a container
$ docker run -t -d -u 986:979 -w /var/lib/jenkins/workspace/example -v /var/lib/jenkins/workspace/example:/var/lib/jenkins/workspace/example:rw,z -v /var/lib/jenkins/workspace/example@tmp:/var/lib/jenkins/workspace/example@tmp:rw,z -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** node:7-alpine cat
$ docker top 7b70bc2548dc543669df3f71b73ed71e21de2c3090a98b0c5ec5ae89560ad071 -eo pid,comm
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] sh
+ node --version
v7.10.1
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
$ docker stop --time=1 7b70bc2548dc543669df3f71b73ed71e21de2c3090a98b0c5ec5ae89560ad071
$ docker rm -f 7b70bc2548dc543669df3f71b73ed71e21de2c3090a98b0c5ec5ae89560ad071
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

전체 flow는 다음과 같다.

  • docker pull node:7-alpine : 이미지 다운

  • docker run -t -d -u node:7-alpine : 이미지 컨테이너 실행

  • docker top f9be76653c26 -eo pid,comm : docker 정보 를 바탕으로 다음 명령어 실행

    :docker exec -it f9be76653c26 sh

  • node --version : container 내부 sh를 이용해 버전 정보 얻어오기

  • docker stop & docker rm : 실행한 컨테이너 삭제

command로 변경 하자면 다음과 같다 (stop & rm은 넣지 않음)

[devops@localhost ~]$ docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
docker.io/node              7-alpine            4b72b56791f9        24 months ago       58.3 MB
[devops@localhost ~]$ docker run -t -d 4b72b56791f9
f9be76653c2641679c56071749082372c3b07f54bbd0a122d105fc4b0948635a
[devops@localhost ~]$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                       PORTS                                      NAMES
f9be76653c26        4b72b56791f9           "node"                   6 seconds ago       Up 5 seconds                                                            nervous_shaw
[devops@localhost ~]$ docker top f9be76653c26
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                33882               33862               0                   17:49               pts/3               00:00:00            node

[devops@localhost ~]$ docker top f9be76653c26 -eo pid,comm
PID                 COMMAND
33882               node

[devops@localhost ~]$ docker exec -it f9be76653c26 sh
/ # node --version
v7.10.1
/ #
volume mount

앞서 다운로드한 라이브러리등 캐쉬를 재 활용 하기 위해서 다음과 같이 volume을 mount 할 수 있다.

// Script //
node {
    /* Requires the Docker Pipeline plugin to be installed */
    docker.image('maven:3-alpine').inside('-v $HOME/.m2:/root/.m2') {
        stage('Build') {
            sh 'mvn -B'
        }
    }
}
다중 container 처리
node {
    /* Requires the Docker Pipeline plugin to be installed */
    stage('Back-end') {
        docker.image('maven:3-alpine').inside {
            sh 'mvn --version'
        }
    }
    stage('Front-end') {
        docker.image('node:7-alpine').inside {
            sh 'node --version'
        }
    }
}
sidecar 처리

특정 서비스 환경을 테스트 하기 위하여 주변 환경을 우선 만들고, 해당 서비스를 테스트 하기 위한 행위를 sidecar 패턴이라고 한다.

docker에서는 다음과 같이 side car를 docker에 반영 할 수 있다.

  • mysql run
node {
    /*
     * In order to communicate with the MySQL server, this Pipeline explicitly
     * maps the port (`3306`) to a known port on the host machine.
     */
    docker.image('mysql:5').withRun('-e "MYSQL_ROOT_PASSWORD=my-secret-pw" -p 3306:3306') { c ->
        docker.image('mysql:5').inside("--link ${c.id}:db") {
            /* Wait until mysql service is up */
            sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done'
            sh 'ehco $USER'
        }
    }
}

결과

Started by user devops
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/example
[Pipeline] {
[Pipeline] sh
+ docker run -d -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 mysql:5
[Pipeline] dockerFingerprintRun
[Pipeline] sh
+ docker inspect -f . mysql:5
.
[Pipeline] withDockerContainer
Jenkins does not seem to be running inside a container
$ docker run -t -d -u 986:979 --link 541d07bced678662242c3242be0fa406a0c10def3774af6e6b86221a2c6d982f:db -w /var/lib/jenkins/workspace/example -v /var/lib/jenkins/workspace/example:/var/lib/jenkins/workspace/example:rw,z -v /var/lib/jenkins/workspace/example@tmp:/var/lib/jenkins/workspace/example@tmp:rw,z -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** mysql:5 cat
$ docker top cfbe21f96c9a7f16de807e976824c11d9ecd3ace17dd99798936d111ae507739 -eo pid,comm
[Pipeline] {
[Pipeline] sh
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
+ sleep 1
+ mysqladmin ping -hdb --silent
[Pipeline] sh
+ echo

[Pipeline] }
$ docker stop --time=1 cfbe21f96c9a7f16de807e976824c11d9ecd3ace17dd99798936d111ae507739
$ docker rm -f cfbe21f96c9a7f16de807e976824c11d9ecd3ace17dd99798936d111ae507739
[Pipeline] // withDockerContainer
[Pipeline] sh
+ docker stop 541d07bced678662242c3242be0fa406a0c10def3774af6e6b86221a2c6d982f
541d07bced678662242c3242be0fa406a0c10def3774af6e6b86221a2c6d982f
+ docker rm -f 541d07bced678662242c3242be0fa406a0c10def3774af6e6b86221a2c6d982f
541d07bced678662242c3242be0fa406a0c10def3774af6e6b86221a2c6d982f
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline

상위 프로세스가 진행 하는 동안은 아래와 같이 mysql container가 2개 동시에 실행 되는 것을 확인 할 수있다. 하나는 사용할 mysql 서버 이고, 다른 하나는 mysqladmin이 정상적으로 작동 하는지 확인 하는 용도의 컨테이터라고 생각하면 된다.

[devops@localhost ~]$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                  PORTS                                      NAMES
cfbe21f96c9a        mysql:5                "docker-entrypoint..."   23 seconds ago      Up 21 seconds           3306/tcp, 33060/tcp                        hopeful_goldwasser
541d07bced67        mysql:5                "docker-entrypoint..."   29 seconds ago      

만약에 jenkins가 동작하고 있는 서버에 mysqladmin이 사용 가능하다면 상위처럼 mysqladmin 테스트용 도커를 따로 만들 필요는 없다.

상위 script를 보다보면 설명이 없는 부분이 있는데,

c ->

이 부분에 대한 설명이 없다.

groovy의 closure를 사용한 부분으로 http://groovy-lang.org/closures.html 이 url 내용을 확인 하면 된다.

결론적으로는 https://github.com/jenkinsci/docker-workflow-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy 이 파일의 Container 객체를 인스턴스한 객체를 뜻하는 것으로, 테스트 해본 바로는 c.id를 제외하고는 다른 정보를 얻어 올 수 없는 것으로 보인다.

        public <V> V withRun(String args = '', String command = "", Closure<V> body) {
            docker.node {
                Container c = run(args, command)
                try {
                    body.call(c)
                } finally {
                    c.stop()
                }
            }
        }

withRun의 결과 값은 Container이다.

그외 inside의 코드는 다음과 같다.

        public <V> V inside(String args = '', Closure<V> body) {
            docker.node {
                def toRun = imageName()
                if (toRun != id && docker.script.sh(script: "docker inspect -f . ${id}", returnStatus: true) == 0) {
                    // Can run it without registry prefix, because it was locally built.
                    toRun = id
                } else {
                    if (docker.script.sh(script: "docker inspect -f . ${toRun}", returnStatus: true) != 0) {
                        // Not yet present locally.
                        // withDockerContainer requires the image to be available locally, since its start phase is not a durable task.
                        pull()
                    }
                }
                docker.script.withDockerContainer(image: toRun, args: args, toolName: docker.script.env.DOCKER_TOOL_NAME) {
                    body()
                }
            }
        }

내용을 보자면 args는 "--link ${c.id}:db" 과 대응 되며 body 이후는

{ /* Wait until mysql service is up */ sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done' sh 'ehco $USER' }

이 영역으로 치환 되게 된다. 즉 body, 또는 클로저라고 불리는 영역은 해당 container 내부에서 실행 되는 영역으로 생각 하면 된다.

Dockerfile을 이용한 빌드

github에서 dockerfile을 pull 한 이후 해당 파일을 바탕으로 image를 만들고 실행을 시켜 보는 코드이다.

node {

    git credentialsId: 'someone', url: 'https://github.com/someone/nginx.git'
    def customImage = docker.build("nodejs")
    /*
     * In order to communicate with the MySQL server, this Pipeline explicitly
     * maps the port (`3306`) to a known port on the host machine.
     */
    customImage.withRun() { c ->
        stage('containerTest'){
            sh "echo ${c.id}"
        }

    }
}

결과는 다음과 같다.

Started by user devops
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/example
[Pipeline] {
[Pipeline] git
using credential someone
 > git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
 > git config remote.origin.url https://github.com/someone/nginx.git # timeout=10
Fetching upstream changes from https://github.com/someone/nginx.git
 > git --version # timeout=10
using GIT_ASKPASS to set credentials github
 > git fetch --tags --progress https://github.com/someone/nginx.git +refs/heads/*:refs/remotes/origin/*
 > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
 > git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10
Checking out Revision d3d0ab61b78fcf93a63fd3fe37ec07eef2b1df5d (refs/remotes/origin/master)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f d3d0ab61b78fcf93a63fd3fe37ec07eef2b1df5d
 > git branch -a -v --no-abbrev # timeout=10
 > git branch -D master # timeout=10
 > git checkout -b master d3d0ab61b78fcf93a63fd3fe37ec07eef2b1df5d
Commit message: "Update Dockerfile"
 > git rev-list --no-walk 0faa7924ddcc35bc2e83ca15d454e2240824d33b # timeout=10
[Pipeline] sh
+ docker build -t nodejs .
Sending build context to Docker daemon 192.5 kB

Step 1/9 : FROM node:6.2.2
Trying to pull repository docker.io/library/node ... 
6.2.2: Pulling from docker.io/library/node
5c90d4a2d1a8: Pulling fs layer
ab30c63719b1: Pulling fs layer
c6072700a242: Pulling fs layer
abb742d515b4: Pulling fs layer
22efa86cdb65: Pulling fs layer
a379ffacc05f: Pulling fs layer
abb742d515b4: Waiting
22efa86cdb65: Waiting
a379ffacc05f: Waiting
c6072700a242: Verifying Checksum
c6072700a242: Download complete
ab30c63719b1: Download complete
22efa86cdb65: Download complete
a379ffacc05f: Verifying Checksum
a379ffacc05f: Download complete
5c90d4a2d1a8: Verifying Checksum
5c90d4a2d1a8: Download complete
abb742d515b4: Verifying Checksum
abb742d515b4: Download complete
5c90d4a2d1a8: Pull complete
ab30c63719b1: Pull complete
c6072700a242: Pull complete
abb742d515b4: Pull complete
22efa86cdb65: Pull complete
a379ffacc05f: Pull complete
Digest: sha256:67123dcbed68c55296aa04bdbe85440a27c74481e2668aafd66e2a11934bb15d
Status: Downloaded newer image for docker.io/node:6.2.2
 ---> 9121f2a78909
Step 2/9 : MAINTAINER Jaeha Ahn <eu81273@gmail.com>
 ---> Running in 0ecf8f4050a4
 ---> d6a693621aec
Removing intermediate container 0ecf8f4050a4
Step 3/9 : RUN mkdir -p /app
 ---> Running in dd257c30d12a

 ---> b812157a06e8
Removing intermediate container dd257c30d12a
Step 4/9 : WORKDIR /app
 ---> ec1c4a7a3f98
Removing intermediate container 5ff496892285
Step 5/9 : ADD . /app
 ---> 727a7dd31fe8
Removing intermediate container e96e6be5e5e3
Step 6/9 : RUN npm install
 ---> Running in 810192e8ef7c

npm info it worked if it ends with ok
npm info using npm@3.9.5
npm info using node@v6.2.2
npm info lifecycle undefined~preinstall: undefined
npm info linkStuff !invalid#1
npm info lifecycle undefined~install: undefined
npm info lifecycle undefined~postinstall: undefined
npm info lifecycle undefined~prepublish: undefined
npm WARN enoent ENOENT: no such file or directory, open '/app/package.json'
npm WARN app No description
npm WARN app No repository field.
npm WARN app No README data
npm WARN app No license field.
npm info ok 
 ---> 0b0f52f79418
Removing intermediate container 810192e8ef7c
Step 7/9 : ENV NODE_ENV development
 ---> Running in f0e5d7e23c62
 ---> 60c03bd48ed4
Removing intermediate container f0e5d7e23c62
Step 8/9 : EXPOSE 3000 80
 ---> Running in 1cdf07531235
 ---> 21a51bb1f571
Removing intermediate container 1cdf07531235
Step 9/9 : CMD npm start
 ---> Running in a562aa95e2fc
 ---> a52d30b585ab
Removing intermediate container a562aa95e2fc
Successfully built a52d30b585ab
[Pipeline] dockerFingerprintFrom
[Pipeline] sh
+ docker run -d nodejs
[Pipeline] dockerFingerprintRun
[Pipeline] stage
[Pipeline] { (containerTest)
[Pipeline] sh
+ echo fc6a252de706267691f3f822116fb5048df8110abfc70e587ad5e19b19dd970f
fc6a252de706267691f3f822116fb5048df8110abfc70e587ad5e19b19dd970f
[Pipeline] }
[Pipeline] // stage
[Pipeline] sh
+ docker stop fc6a252de706267691f3f822116fb5048df8110abfc70e587ad5e19b19dd970f
fc6a252de706267691f3f822116fb5048df8110abfc70e587ad5e19b19dd970f
+ docker rm -f fc6a252de706267691f3f822116fb5048df8110abfc70e587ad5e19b19dd970f
fc6a252de706267691f3f822116fb5048df8110abfc70e587ad5e19b19dd970f
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
Docker server 사용
  • 기본 서버 : /var/run/docker.sock

별도 서버 사용 하고자 할경우 (Docker Swarm 같은거)

node {
    checkout scm
    docker.withServer('tcp://swarm.example.com:2376', 'swarm-certs') {
        docker.image('mysql:5').withRun('-p 3306:3306') {
        /* do things */
        }
    }
}
Docker Registry

Docker hub를 기본으로 Registry 서버가 설정 되어있으나, 다음과 같이 변경 가능하다.

node {
    checkout scm
    docker.withRegistry('https://registry.example.com') {
        docker.image('my-custom-image').inside {
            sh 'make test'
        }
    }
}
node {
    checkout scm
    docker.withRegistry('https://registry.example.com', 'credentials-id') {
        def customImage = docker.build("my-image:${env.BUILD_ID}")
        /* Push the container to the custom Registry */
        customImage.push()
    }
}
Docker Agent 사용하기

Docker를 상위 처럼 사용 하지 않고 Agent로 사용이 가능하다.

https://github.com/jenkins-docs/simple-node-js-react-npm-app

이 프로젝트의 파이프 라인을 보면 다음과 같다.

pipeline {
    agent {
        docker {
            image 'node:6-alpine'
            args '-p 3000:3000'
        }
    }
    environment {
        CI = 'true'
    }
    stages {
        stage('Build') {
            steps {
                sh 'npm install'
            }
        }
        stage('Test') {
            steps {
                sh './jenkins/scripts/test.sh'
            }
        }
        stage('Deliver') {
            steps {
                sh './jenkins/scripts/deliver.sh'
                input message: 'Finished using the web site? (Click "Proceed" to continue)'
                sh './jenkins/scripts/kill.sh'
            }
        }
    }
}

Agent를 docker의 이미지로 선택 함으로써, 파이프라인 하단에 보이는 stages는 모두 docker 이미지 내에서 동작 하게 된다.

Replay

Jenkins > 파이프라인 프로젝트 선택 > Replay 선택

MainScript 수정

Run

매번 Config를 수정하기 어렵기 때문에 Replay를 사용해서 script를 Test 하는데 사용된다.

Pipeline Syntax

앞서서 DSL define에서 다루었던 내용과 비슷한 부분이 많다.

if else

node {
    stage('Example') {
    if (env.BRANCH_NAME == 'master') {
            echo 'I only execute on the master branch'
        } else {
            echo 'I execute elsewhere'
        }
    }
}

try catch

node {
    stage('Example') {
        try {
            sh 'exit 1'
        }
        catch (exc) {
            echo 'Something failed, I should sound the klaxons!'
            throw
        }
    }
}

Gitlab 연동

plugin

genkins 관리 > 플러그인관리 > 설치가능

  • gitlab or gitlab plugin install

https://wiki.jenkins.io/display/JENKINS/GitLab+Plugin

인증정보
gitlab Personal Token 발행

우상단 profile > settings > Access Token> Personal Access Token 발행

gitlab token jenkins 등록

Dashboard > Credentials > System > Global Credentials > Add Credentials

  • kind : gitlab api token (안보이면 플러그인을 재설치)
  • Token : gitlab access token
  • 그외 : 아무거나

Dashboard > jenkins 관리 > 시스템 설정 > gitlab tab (없으면 플러그인 확인)

  • Connection name : 아무거나
  • Gitlab host url : http:// 부터 .com <-여기까지 (예, http://gitlab.test.com)
  • Credential : 상위 만든 gitlab token 선택

Test Connection 시 Success 나면 됨.

Jenkins 등록

Dashboard > Credentials > System > Global Credentials > Add Credentials

gitlab 접근용도 (아래 두개중 사용 가능한것 하나)

  • kind : Username with password
  • id&pw 입력
  • 그외 : 아무거나

ssh-keygen을 사용해야 하는 부분인데 테스트를 안해봐서 일단 pass

  • kind : SSH Username with private key
jenkins 프로젝트
  • 프리스타일 선택
  • General : Gitlab Connection 선택 (token이 정상적으로 등록 된경우 선택 가능)
  • 소스코드관리 : Git 선택 > Credential > username 등록한 credential 선택
  • 빌드유발 : Build when a change is pushed to GitLab. GitLab webhook URL: http://jenkins.test.com/project/projectname
  • Secret token : Build when a change is pushed to GitLab. 이 설정에 속한 우하단 고급을 선택 하면, 숨겨있는 옵션을 선택 할 수있는데, 그 중에서 Secret token 을 생성하고 저장해 놓는다. (gitlab webhook에 사용된다.)
  • 그외 : 아무거나 (gitlab web hook 만 작동 하는지 테스트 할 예정임으로 build 처리만 되면 된다.)
Gitlab 설정

gitlab > webhook 처리할 프로젝트로 이동 > setting > intergration >

  • Url : 상위 빌드 유발 url 입력 (http://jenkins.test.com/project/projectname)
  • Secret Token : jenkins에서 얻어온 Secret Token 을 넣어준다.
  • Enable SSL verification : 위에서 사용을 안했음으로 uncheck
  • add webhook : click

해당 페이지의 중간 쯤에 Webhooks (1) 라는 내용이 생기고, test 버튼을 확인 할 수있다. 이 버튼을 click 하면 jenkins가 실행 되어야 한다.

jenkins와 gitlab이 동일 서버(localhost) 라고 하면서 오류가 나면, 다음과 같이 설정을 수정해 줘야한다.

gitlab > admin area > settings > Network > Outbound request >

  • Allow requests to the local network from hooks and services : check

Mattermost 연동

  • plugin : Mattermost Notification Plugin
728x90
반응형

'Software활용' 카테고리의 다른 글

Jenkins with tomcat  (0) 2020.03.27
Jenkins on centos7  (0) 2020.03.27
wildfly (Jboss AS) installation with Postgresql driver on Mac  (0) 2020.03.27
Docker 이것저것  (0) 2020.03.27
Redis & Jedis  (0) 2020.03.27