본문 바로가기
Development(Web, Server, Cloud)/Cloud : 정리

CICD : Jenkins, gitlab, githosting, github action

by tonyhan18 2022. 5. 19.
728x90

jenkins, gitlab, githosting 중 하나를 사용해야 한다.

 

Jenkins

jenkins를 이용해서 스크립트를 짜고 동작시키는 것이다. 이 스크립트 내용안에는 git clone, pull 중 하나를 하라고 한다.

그리고 new Image를 master node에서 만든다. 이미지를 바로 worker에 전달못하니 일단 docker hub에 올려버린다. 그리고 worker들은 docker hub에서 받아다가 rolling update 해버리면 된다.

 

그럼 개발자는 직접적으로 서비스에 들어가지 말고 git에 올리고 jenkins를 사용하기만 하면 된다.

이걸 또 자동화 시키기 위해 github의 변화를 감지하는 무언가를 붙이면 된다.

 

jenkins를 설치하는 방법은 서버에 직접설치하는 방법과 컨테이너 방법이 있다.

컨테이너는 상대적으로 보안상 안좋기도 하고 안전성도 떨어지는 편이라 그냥 설치를 하자.

 

우리는 yaml 파일을 이용해서 master와 node가 존재하고 node 안에는 pod - ctn이 들어가 있다. ctn gildong/testshop:main 이미지를 사용하고 있는데 이걸 rolling update 하면 된다.

개발자에게 master에 들어가서 명령어를 칠 수 있는 권한을 주지는 않는다. jenkins 계정을 부여해서 주는 것이다.

 

개발자 PC에는 git이 설치되어 있고 Dockerfile, index.html 파일을 github에 올려주는 것이다.

우리의 docker hub에는 main, blog, shop이라는 이미지가 존재한다.

jenkins 내부에서 명령어를 나열해서 github의 index.html과 Dockerfile을 끌고와서 새로운 이미지를 만든다.

 

이 이미지는 node가 안가지고 있으니 우리의 docker hub 저장소에 newmain이라고 올려준다.

그러면 jenkins에서 rolling update를 해서 기존의 deploy-main 안에 들어가 있는 pod(ctn)에 이미지를 newmain으로 바꾸어 주는데, 기존 deploy-main 안에 들어가 있는 pod(ctn)의 이미지를 newmain으로 바꾸어라라고 하는 것이다. 그것을 실행하라고 하면 node에게 명령어가 전달된다.

 

node는 newmain이 있는지 로컬과 docker hub를 찾아보아서 그 이미지를 pull 해서 롤링업데이트 하는 것이다.

 

설치하기

jenkins 설치 - master에서

java --version

apt-get install openjdk-8-jdk openjdk-8-jre 
# java가 없다면 설치

wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add - 
# ok가 뜨면 된다.

sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'

apt-get update && apt-get install jenkins

systemctl start jenkins ; systemctl enable jenkins

ufw allow 8080

여기까지 되었다면 별거 없다. master node의 ip:8080 으로 접속하면 jenkins 화면이 그대로 보인다.

 

cat /var/lib/jenkins/secrets/initialAdminPassword
# 패스워드는 여기에 있으니 가서 복붙해주자

두개가 나오게 되는데 Install suggested plugins를 클릭해준다.

 

시작하기전에 먼저 jenkins그룹 안으로 들어간다.

usermod -aG docker jenkins

usermod -aG sudo jenkins

그리고 jenkins를 사용하기 위해서는 sudo를 사용해야 하는데 /etc/sudoers안에 jenkins를 넣어주자

 

Freestyle project

소스 코드 관리 도구에 Git 부분에서 Repository URL 에 내가 push 했던 git을 넣어주자

그리고 branch는 나의 경우 main에 있어서 이걸 사용해주자

이제 Execute shell을 선택해서 빌드하게 되는데

sudo 로 이걸 실행해야한다.

 

 

Rolling Update

- 새로운 이미지를 만들어서 배포하는 방법

sudo docker build -t brian24/testshop:newman .
sudo docker push brian24/testshop:newmain
sudo kauth
sudo kubectl set image deployment deploy-main ctn-main=brian24/testshop:newmain

jenkins는 kubernetes를 실행할 수 있는 권한이 존재하지 않는다.

kubesys 전체를 관리할 수 있는 kubesys-admin권한을 가지고 와야 한다. 그래야 k get node를 사용할 수 있다.

그래서 sudo kauth로 kubesys-admin권한을 가져오는 것이다.

 

- yaml 파일로 배포하는 방법

1) 개발자는 로컬에서 yaml 파일을 만들고 이를 github에 push 한다.

cat nginx.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pods-label
  template:
    metadata:
      name: nginx-pod
      labels:
        app: nginx-pods-label
    spec:
      containers:
      - name: nginx
        image: tonyhan18/testmain:newmain
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  ports:
  - name: nginx-port
    targetPort: 80
    port: 80
  selector:
    app: nginx-pods-label
  type: LoadBalancer

이 상태에서 github에 업로드해버리자

 

jenkins의 Build 명령은 위와같이 넣어주었다.

 

pipeline

젠킨스도 고유 문법이 존재하고 yaml을 사용할 수 있다.

 

- build trigger

build after other projects as built는 다른 프로젝트가 끝난 이후에 빌드를 한다는 것이다.

build periodically schedule을 작성해야한다. 이건 cron 문법을 적용한다.

 

- poll scm : 주기적으로(cron) github의 commit을 확인하여 기존 커밋과 비교했을 때 변경된 사항이 있으면 build 발생

만약, commit이 그대로라면 build는 발생하지 않는다.

- 빌드 안함 : 빌드하지 않음

- Quiet period : push를 여러번하더라도 지연시간을 주겠다.

- 빌드를 원격으로 유발 : 원격에서 빌드를 시키는 것이다. 깃허브의 푸시 또는 메신저의 웹훅과 같이 주소를 이용해서 빌드를 시작할 수 있는 곳에서 사용.  webhook을 사용하면 웹주소로 코드를 전달할 수 있다. 따라서 해당 서버가 공인주소로 설정되어 있어야 한다.(이게 문제인게 우리가 쓰는게 사설주소이기 때문이다 - 그게 아니면 좋은 방법이다)

 

마지막으로 Jsnkinsfile이라는 부분이 존재하여서 github에 jenkinsfile을 올리라는 것이다.

 

$ cat Jenkinsfile
pipeline {
  agent any
  stages {
    stage('git scm update') {
      steps {
        git url: 'https://github.com/beomtaek78/testjenkins.git', branch: 'master'
      }
    }
    stage('docker build and push') {
      steps {
        sh '''
        sudo docker build -t brian24/myweb:1.0 .
        sudo docker push brian24/myweb:1.0
        '''
      }
    }
    stage('deploy k8s') {
      steps {
        sh '''
        sudo kubectl create deploy testpipeline --image=brian24/myweb:1.0
        sudo kubectl expose deploy testpipeline --type=NodePort --port=8081 \
        --target-port=80 --name=testpipeline-svc
        '''
      }
    }
  }
}

이 내용을 Jenkinsfile에 복붙해서 git에 업로드하는 폴더에 함께 넣어놓자

 

문법

Pipeline Syntax (jenkins.io)

 

Pipeline Syntax

When Jenkins Pipeline was first created, Groovy was selected as the foundation. Jenkins has long shipped with an embedded Groovy engine to provide advanced scripting capabilities for admins and users alike. Additionally, the implementors of Jenkins Pipelin

www.jenkins.io

pipeline  작성은

 

pipeline {

  /* insert Declarative Pipeline here */
}

 

내에 섹션을 구분하여 작성한다.

- agent : runtime 불러오기, 누구를 이용하여 아래의 작업을 진행할 것인가?

1) any : 모든 agent에서 불러와서 실행해 본다.

2) none : 여기에서는 내가 정확히 지정하지 않지만 각각의 stage에서 지정해서 쓰겠다.

3) label  :

4) node : 

5) docker :

 

- post   : 작업진행된 결과에 따라 출력할, 실행할 요소(시작전 또는 실행 이후 가능)

1) 특정 스테이지나 파이프라인이 시작되기 전 또는 이후에 실행할 컨디션 블럭

1.1) always : 실행 이후 무조건 실행되는 step (결과 코드와 상관없이)

1.2) failure : 실패하면 실행

1.3) success : 성공하면 실행

1.4) unstable : testfail, code violation 등일 때 실행

1.5) aborted : 실행 중간에 강제로 종료되었을 때

 

- stages  : 작업을 올려두는 곳. stage 내에 여러개의 stage를 사용

1) 하나 이상의 stage에 대한 모음을 정의

2) 파이프라인 내에서 한번만 사용할 수 있다

3) stages 내에서 여러개의 stage를 사용할 수 있다.

 

- steps   : stage 내에서 실제로 작업할 내용을 순서대로 작성

1. stages 내에 있는 stage('작업 이름')을 이용하여 각 작업을 구분한다.

2. 각 stage 블록 내에서 여러번의 작업을 순서대로 작성한다.

 

* 큰 흐름은 이 순서대로 작성된다.

 

- input : stage 내에서 실행할때 사용자로부터 입력을 받는다.

stage 내에 위치하고 작업이 진행될 때, 사용자로부터 입력값을 받을 수 있다. 입력된 내용은 사용자가 지정한 변수에 담겨 이를 step 내에 적용한 뒤 실행할 수 있다.

1) message : 실행될 때 팝업으로 뜨는 메시지

  "이름을 입력하세요"

2) ok : 다음 실행으로 넘어갈 때 화면에 보이는 버튼에 어떤 글자를 뜨게 할 것이냐? "next"와 같은 버튼을 뜨게 하여 다음 단계로 넘어가게 만들어주기

3) parametes : 사용자로부터 입력받은 값을 input에 넣기

- string : 입력창에 받는 데이터의 형을 문자열로 받겠다

- name : 입력되는 문자열을 저장하는 변수명

- defaultValue : 입력값이 없다면 변수에 담기는 기본 값

- description : 주석

 

예시들

pipeline {
    agent any
    stages {
        stage('test1'){
            steps{
                echo "HELLO ALL1"
            }
        }
        stage('test2'){
            steps{
                echo "HELLO ALL2"
            }
        }
    }
}
pipeline {
    agent any
    stages {
        stage('test1'){
            steps{
                echo "HELLO ALL1"
            }
        }
        stage('test2'){
            input {
                message "What is your name?"
                ok "finished"
                parameters {
                    string(name: 'NAME', defaultValue: 'GILDONG', description:'type your name')
                }
            }
            steps{
                echo "HELLO ${NAME}"
            }
        }
    }
}

 

pipeline {
    agent any
    
    stages {
        stage('test1'){
            steps {
                echo "HELLO ALL1"
            }
        }
        
        stage('test2'){
            input {
                message "What is your name?"
                ok "Next"
                parameters {
                    string(name: 'NAME', defaultValue: 'GILDONG', description: 'type your name')
                }
            }
            
            steps {
                echo "HELLO ${NAME}"
            }
        }
        stage('test3'){
            input {
                message "Docker image tag name"
                ok "finished"
                parameters {
                    string(name: 'TAG', defaultValue: 'none', description: 'type docker image tag')
                }
            }
            steps {
                sh '''
                sudo rm -rf /var/lib/jenkins/workspace/testjenkinsfile/cicdtest
                git clone https://github.com/beomtaek78/cicdtest.git
                cd cicdtest
                sudo docker build -t brian24/webtest:${TAG} .
                sudo docker push brian24/webtest:${TAG}
                sudo sed -i 's@image: nginx@image: brian24/webtest:'"${TAG}"'@g' nginx.yaml
                sudo kubectl apply -f nginx.yaml
                '''
            }
            
        }
        
    }
    post {
        always {
            echo "작업완료 - 성공실패여부 모름"
            }
    }
}

 

롤링업데이트

무중단 상태에서 업데이트 = 롤링업데이트 전략

Recreate : 기존 pod를 완전 삭제 & 새로 만들기 -> 서비스의 Downtime이 발생 = Downtime 만큼 가용성이 떨어짐

 

RollingUpdate(기본값) : 기존것을 죽여가면서 새로운 것을 하나씩 만들억가는 방식

 

Deployment 안에 replicas를 3으로 해놓고 이 안에 컨테이너들은 nginx로 배포한 상태이다.

우리가 나중에 jenkins로 input image tag 값을 받았다. 그럼 tag를 붙이 새로운 이미지가 만들어지고 이게 docker hub로 올라간다. 그리고 docker hub로 부터 이미지를 끌고 오고 Deployment에서 새로운 이미지를 끌고 올 것이다.

그럼 nginx가 아닌 새로운 이름의 이미지로 Pod가 변경될 것이다. 이걸 rolling update라고 부른다.

 

728x90