본문 바로가기
  • lakescript
스터디 이야기/AEWS

[AEWS] 7-1. Amazon EKS - CI/CD (Jenkins)

by lakescript 2024. 4. 16.

사전 준비

더보기

 

위와 같이 사전 준비가 필요합니다. 저번 글에서 설명했듯이, EKS를 배포하기 위한 VPC를 생성하고, Public Subnet, Private Subnet을 생성합니다. 그 후 EKS Cluster에 접근하기 위한 bastion EC2를 미리 생성합니다.

 

Jenkins

 

Jenkins란

ChatGPT가 알려주는 Jenkins
Jenkins는 자동화된 소프트웨어 빌드, 테스트 및 배포를 도와주는 오픈 소스 자동화 서버입니다. CI/CD (Continuous Integration/Continuous Delivery) 워크플로우를 지원하여 개발자가 코드 변경 사항을 자동으로 테스트하고 배포할 수 있도록 돕습니다. Jenkins는 플러그인 형태로 기능 확장이 가능하며, 여러 운영 체제에서 사용할 수 있습니다. 사용자는 Jenkins를 통해 작업 스케줄링, 환경 관리, 오류 통지 등 다양한 작업을 자동화할 수 있습니다.

 

즉, Jenkins는 CI(Continnous Untegration) / CD(Continuous Delivery)의 대표적인 오픈소스로써 개발 프로세스의 여러 단계를 자동화하여 repository에서 최신화된 코드를 가져와 정해진 규칙에 따른 컴파일 후 단위 테스트를 실행하며 결과물을 패키킹하고 여러 종류의 환경으로 배포하는 기능을 제공합니다. 다시 말해 파이프라인을 통한 빌드 및 배포를 지원하며, 자동화를 지원하는 수백 개의 플러그인을 제공합니다. 또한 Continuous Integration Server와 Continuous Development, Build, Test, Deploy를 통합하는 기능을 제공합니다. 

 

Jenkins의 특징

Jenkins는 Java로 작성되었으며 DSL(Domaion Specific Language)인 Jenkins file을 통해 구축합니다.  또한 순차적이고 종속적인 단계가 시작부터 끝까지 실행되면 최종적으로 사용자가 실행할 수 있는 빌드가 생성되며, 만약 빌드 프로세스를 진행하는 중에 특정 단계에서 실패가 발생하면 이 단계의 출력 결과를 사용하는 다음 단계는 실행되지 않고 빌드 프로세스 전체가 실패합니다.

 

다양한 Plugins 연동

  • Build Plugins : Maven, Ant, Gradle …
  • VCS Plugins : Git, SVN …
  • Languages Plugins : Java, Python, Node.js …

또한, Jenkins 작업에는 3가지 유형의 지시 사항 포함이 포함됩니다. 

- Trigger : 작업을 수행하는 task가 언제 시작할지 

- Build Step: 특정 명령을 실행하기 위한 task

- post-build action : task가 완료 후 수행할 명령

 

Jenkins 설치

JVM 설치

sudo yum install fontconfig java-17-amazon-corretto -y

 

Jenkins는 JAVA로 구성되어 있기에 실행시키기 위해 jvm을 설치해주셔야 합니다. 보통 ubuntu로 구성된 환경일 경우 open-jdk를 통해 설치를 하지만 지금 현재 구성된 EC2에서는 아마존 Linux를 사용하고 있기 때문에 권장사항인 java-17-corretto를 통해 설치해줍니다.

 

java -version

 

설치된 java version을 확인해보겠습니다.

 

환경 변수 설정

alternatives --display java

 

이따 Jenkins를 설치할 때 global 설정 중 jvm 경로를 설정해주어야 합니다. 그렇기에 alternatives 명령어를 통해 어떤 link가 설정되어있는지 확인 후 그 중 jre_openjdk 경로를 확인합니다.

 

 

 

JAVA_HOME=/usr/lib/jvm/java-17-amazon-corretto.x86_64
echo $JAVA_HOME

 

위에서 확인한 jre-openjdk 경로를 JAVA_HOME 환경 변수로 설정해주시고, 설정되었는지 확인합니다.

 

jenkins 설치

sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
sudo yum upgrade
sudo yum install jenkins -y
sudo systemctl daemon-reload
sudo systemctl enable jenkins && sudo systemctl start jenkins   # 다소 시간 걸림

 

위의 명령어를 통해 jenkins를 설치하도록 하겠습니다.

 

sudo systemctl status jenkins

 

systemctl status 명령어를 통해 현재 jenkins daemon이 active 상태인지 확인합니다.

 

초기 비밀번호 확인

cat /var/lib/jenkins/secrets/initialAdminPassword

 

위의 명령어를 통해 jenkins에 접근하기 위한 초기 비밀 번호를 확인합니다.

 

jenkins 접근

curl -s ipinfo.io/ip | awk '{ print "Jenkins = http://"$1":8080" }'

 

현재 띄어져 있는 Bastion EC2에 8080 port를 통해 jenkins가 배포되었습니다. 

 

 

 

그 후 위에서 확인한 초기 비밀번호를 입력 후 jenkins에 접근합니다.

 

PlugIn 설치

 

기본적으로 Jenkins가 동작할 때 필요한 Plugin들을 제안해주는 기능인 Install suggested plugins 클릭 후 설치합니다.

 

만약 Plugin 설치가 대부분 안된다면?

 

어떤 이유에서인지 Plugin들을 초기에 설치할 때 대부분이 설치가 되지 않는 이슈가 발생했습니다.

해당 문제는 jenkins를 생성한 EC2에 보안그룹에서 HTTP 통신을 허용하지 않아서 발생한 이슈인데요. Jenkins에서 8080 port를 통해 host하기 때문에 보안그룹에서 HTTP 접근을 허용시켜주시면 해결됩니다. 

 

/safeRestart

 

그 후 restart API를 통해 jenkins를 재시작합니다.

 

 

 

성공적으로 Plugins이 설치되었고 바로 User 설정 화면으로 넘어갑니다 :)

(너무 빠르게 설치가 되서 차마 스크린샷을 찍진 못했습니다..!)

 

설치 완료 후 Jenkins 접속

 

 

Jenkins 실습

JDK 설정

먼저 JDK를 설정하겠습니다.

Jenkins 관리 메뉴에서 Tools에 접근합니다.

 

 

JDK installations에서 name은 jdk-17, JAVA_HOME은 위에서 봐두었던 jre-openjdk 경로로 설정합니다.

 

 

Project 생성

이제 기본 설정은 이것으로 끝났고 build project를 만들어보겠습니다.

 

 

 

 

새로운 Item 클릭 후 Fresstyle Project로 신규 project를 생성합니다.

 

 

Build Steps 세션에서 Add build step를 선택 후 Execute shell 선택합니다. 

여기서 Execute shell은 shell script를 실행할 수 있게 하는 기능입니다.

echo "Aws Lakescript"

 

 

 

 

그 후 저장을 눌러 완료합니다.

 

 

 

Project Build

이제 Project 생성이 완료되었으니 build 해보도록 하겠습니다.

 

 

 

왼쪽 메뉴바에서 지금 빌드 버튼을 눌러 build를 시작합니다.

 

 

Build history에서 해당 build의 console output을 확인합니다.

 

정상적으로 위에서 입력했던 shell 명령어인 echo 명령어가 실행된 것을 확인하실 수 있습니다.

 

이번엔 마찬가지로 build를 진행하지만 txt 파일을 생성해보겠습니다.

 

 

touch lake.txt

 

touch 명령어를 통해 lake.txt를 생성하는 명령어를 추가해보겠습니다.

 

 

console output에서 실행된 결과를 확인해보면 위의 사진과 같이 정상적으로 실행된 것을 확인하실 수 있습니다.

 

이제 jenkins가 띄어져 있는 host에서 생성된 파일을 확인해보겠습니다.

 

find / -name First-Project

 

find 명령어로 Project 이름을 찾아보면 아래와 같이 jenkins 경로가 보여집니다.

 

tree /var/lib/jenkins/workspace/First-Project/

 

tree 명령어를 통해 해당 project에서 존재하는 목록을 확인해보곘습니다.

 

정상적으로 lake.txt 파일이 보여집니다!

 

 

Docker 설정

Jenkins 설정이 끝났으니, 실제로 build된 결과물(image)를 저장할 Docker hub 설정을 해보겠습니다.

 

jenkins user 권한 설정

 tail /etc/passwd

 

먼저 tail 명령어로 jenkin 사용자의 bin 권한을 살펴보겠습니다.

jenkins:x:995:991:Jenkins Automation Server:/var/lib/jenkins:/bin/false

 

위와 같이 /bin/false인 것을 확인하실 수 있는데, jenkins 사용자로 bash를 사용할 수 있게끔 설정을 변경하겠습니다.

 

usermod -s /bin/bash jenkins

 

usermod 명령어를 통해 jenkins 사용자의 권한을 변경시켜줍니다.

 

jenkins:x:995:991:Jenkins Automation Server:/var/lib/jenkins:/bin/bash

 

/bin/bash로 변경된 것을 확인하실 수 있습니다.

 

jenkins 유저 전환

su - jenkins

 

su 명령어를 통해 jenkins 사용자로 접근합니다. 

 

whoami

 

whoami 명령어를 통해 현재 접속한 사용자의 이름을 확인합니다.

 

jenkins 사용자로 접근한 것을 확인하실 수 있습니다.

 

docker 명령어 실행권한 부여

docker info

 

jenkins 사용자로 접속하여 docker info 명령어를 실행하면 아래와 같이 접근이 되지 않습니다.

 

 

exit

 

docker 권한을 부여하기 위해 일단 접속을 해제합니다.

 

chmod 666 /var/run/docker.sock
usermod -aG docker jenkins

 

chmod 명령어와 usermod 명령어를 통해 jenkins 사용자가 docker 명령어를 실행하게끔 권한을 수정합니다.

 

su - jenkins

 

다시 Jenkins 사용자로 접근합니다.

 

docker info

 

그 후 다시 docker명령어를 실행하면 아래와 같이 정상적으로 실행된 것을 확인하실 수 있습니다.

 

 

 

docker login

docker login

 

docker login 명령어를 통해 hub에 접근하게끔 docker hub에 로그인합니다.

 

image file 생성

mkdir -p ~/myweb2 && cd ~/myweb2

 

먼저 작업할 directory를 생성 후 이동합니다.

FROM ubuntu:20.04
ENV TZ=Asia/Seoul VERSION=2.0.0 NICK=<자신의 닉네임>
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \
    sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
    sed -i 's/security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list && \
    apt-get update && apt-get install -y apache2 figlet && \
    echo "$NICK Web Server $VERSION<br>" > /var/www/html/index.html && \
    echo "<pre>" >> /var/www/html/index.html && \
    figlet AEWS Study >> /var/www/html/index.html && \
    echo "</pre>" >> /var/www/html/index.html
EXPOSE 80
CMD ["usr/sbin/apache2ctl", "-DFOREGROUND"]

 

위와 같이 test용 dockerfile을 생성합니다. 

 

Jenkins를 통한 Dockerfile 실행

 

이번엔 Docker-Project라는 이름의 Freestyle project를 생성합니다.

 

cd /var/lib/jenkins/myweb2
docker build -t myweb:v2.0.0 .

 

그 후 Build Steps의 Execute shell에서 실행할 명령어를 입력합니다.

 

 

이번엔 추가로 해당 step이 완료되었을 때 실행할 step을 하나 더 생성합니다. 

 

 

이제 build를 진행해보겠습니다.

 

 

 

위의 사진과 같이 dockerfile을 기반으로 image를 build하고 run하는 것을 console을 통해 확인하실 수 있습니다.

 

실제로 생성된것인지 확인을 위해 명령어로 확인해보겠습니다.

docker images

 

docker images 명령어를 통해 image들을 확인 가능합니다.

 

docker ps

 

docker ps 명령어를 통해 현재 실행중인 container 목록을 확인하실 수 있습니다.

 

 

curl localhost

 

배포한 container에 접근해서 설정한 dockerfile로 잘 build되었는지, container가 잘 생성되었는지 확인하겠습니다.

 

 

파라미터, 빌드 유발(SCM - Git) 사용

기본적인 build project에 대해 실습을 진행했으니 이제는 build를 진행할 때 값을 넣는 paramater와 특정 source가 trigger 되었을 때 build를 진행하는 build 유발에 대해 실습해보도록 하겠습니다.

 

https://github.com/leehosu/aews-cicd 이 github repository를 사용하여 실습을 진행합니다.

 

jenkins project 생성

 

Trigger-Project라는 이름으로 Freestyle Project를 생성합니다.

 

 

매개변수를 체크하고 각각 변수명(VERSION), Default Vault(v1.0.0) /  변수명(NICK), Default Vault(<자신의 계정명>)을 입력합니다.

 

소스 코드 관리에서 Git을 체크한 후 각각 정보를 채워주셔야 합니다.

  • Repo URL : https://github.com/**<자신의 계정명>**/aews-cicd
  • Branch : */main
  • Additional Behaviours → Sparse Checkout paths (Path) : 1

 

 

빌드 유발에서 Poll SCM을 체크한 후 Schedule에 * * * * * 을 입력합니다.

여기서 SCM은 형상관리 서버를 주기적으로 감시하여 변경된 사항이 존재할때 빌드를 수행하는 설정입니다.(java의 cron과 비슷합니다)

* * * * *의 뜻은 매일매시간매분매초마다 확인한다는 의미입니다!

 

 

Build Steps에서는 2개의 shell script를 실행시킵니다.

cd /var/lib/jenkins/myweb2
rm -rf Dockerfile
wget https://raw.githubusercontent.com/$NICK/aews-cicd/main/1/Dockerfile
cd ./1

 

docker build -t myweb:$VERSION .
docker run -d -p 80:80 --rm --name myweb myweb:$VERSION

 

파일을 만드는 shell script와 docker image를 build하고 run하는 명령어로 나뉘어 집니다.

 

 

github에서 코드를 수정하여 빌드 유발

위에서 적어놓은 https://github.com/leehosu/aews-cicd 페이지에 접근해서 1 directory에서 Dockerfile을 수기로 수정해보겠습니다.

 

 

2번째 라인에 있는 VERSION과 NICK을 수기로 수정하신 후 저장합니다.

 

 

그러면 위의 사진과 같이 자동으로 repository의 commit을 감지해서 build가 진행됩니다.

Pipeline 실습

이제 jenkins의 꽃인 pipeline에 대해 실습해보도록 하겠습니다.

 

 

project 생성

 

pipeline을 선택하여 project를 생성합니다.

 

pipeline script 작성

pipeline {
    agent any

    stages {
        stage('java 정보 확인') {
            steps {
                echo 'Hello World'
                sh 'java -version'
            }
        }
        stage('배포 결과 확인') {
            steps {
                echo "Deployed successfully!";
            }
        }
    }
}

 

sample pipeline script를 작성해서 적용해보겠습니다.

 

pipeline build

지금 빌드를 클릭하여 빌드를 진행하면 위의 사진과 같이 pipeline이 생성된 것을 확인하실 수 있습니다.

 

 

console 결과도 정상적으로 출력되네요!

 

 

여러가지 pipeline 활용

환경변수 사용, 문자열보간

pipeline {
    agent any
    environment { 
        STUDYNAME = 'AEWS'
    }
    stages {
        stage('연습') {
            environment { 
                mykey = 'abcd'
            }
            steps {
                echo "${STUDYNAME}";
                sh 'echo ${mykey}'
            }
        }
    }
}

 

 

Tools

pipeline {
    agent any
    tools {
        jdk 'jdk-17' 
    }
    stages {
        stage('Tool 지정') {
            steps {
                sh 'java -version'
            }
        }
    }
}

 

 

 

 

Trigger

pipeline {
    agent any
    triggers {
        cron('* * * * *')
    }
    stages {
        stage('Trigger 확인') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

 

 

 

Parameter

pipeline {
    agent any
    parameters {
        string(name: 'PERSON', defaultValue: 'lake', description: 'Who are you?')
        choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
    }
    stages {
        stage('Parameter 사용') {
            steps {
                echo "Hello ${params.PERSON}"
                echo "Choice: ${params.CHOICE}"
            }
        }
    }
}

 

 

 

post (빌드 후 조치)

pipeline {
    agent any
    stages {
        stage('Compile') {
            steps {
                echo "Compiled successfully!";
            }
        }

        stage('JUnit') {
            steps {
                echo "JUnit passed successfully!";
            }
        }

        stage('Code Analysis') {
            steps {
                echo "Code Analysis completed successfully!";
            }
        }

        stage('Deploy') {
            steps {
                echo "Deployed successfully!";
            }
        }
    }

    post {
      always {
        echo "This will always run"
      }
      success {
        echo "This will run when the run finished successfully"
      }
      failure {
        echo "This will run if failed"
      }
      unstable {
        echo "This will run when the run was marked as unstable"
      }
      changed {
        echo "This will run when the state of the pipeline has changed"
      }
    }
}
  • always: 항상 실행합니다.
  • changed: 현재 빌드의 상태가 이번 빌드의 상태와 달라졌다면 실행합니다.
  • success: 현재 빌드가 성공했다면 실행합니다.
  • failure: 현재 빌드가 실패했다면 실행합니다.
  • unstable: 현재 빌드의 상태가 불안하다면 실행합니다.

위의 값을 토대로 build 후 추가 조치하는 문법입니다.

 

 

Jenkins로 K8s Pod 배포하기 

jenkins 사용자에게 k8s 권한 부여

mkdir ~/.kube

 

k8s의 설정파일을 저장하기 위해 jenkins 사용자로 접근한 shell에 .kube directory를 생성합니다.

 

cp ~/.kube/config /var/lib/jenkins/.kube/config
chown jenkins:jenkins /var/lib/jenkins/.kube/config

 

그 후 root 계정으로 shell에 접근하신 후 위의 명령어를 통해 k8s의 설정파일을 옮긴 후 권한을 부여합니다.

 

aws configure

 

다시 jenkins 사용자 shell에 접근하여 AWS confige를 설정합니다. 

 

kubectl get pods -A

 

설정이 완료되면 kubectl 명령어를 통해 확인합니다.

 

 

 

파이프라인으로 디플로이먼트/서비스 배포

실습을 진행하기전에 자신의 도커 허브에 이미지가 있어야 합니다.

 

https://github.com/leehosu/aews-cicd/blob/main/3/deploy/deployment-svc.yaml

 

위 소스에서 image 부분을 자신의 docker hub image를 적어주세요!

 

 

pipeline 생성

 

k8s-1이라는 이름으로 pipeline project를 생성합니다.

 

pipeline {
    agent any

    tools {
        jdk 'jdk-17'
    }

    environment {
        DOCKERHUB_USERNAME = 'l2hosu'
        GITHUB_URL = 'https://github.com/leehosu/aews-cicd.git'
        DIR_NUM = '3'
    }

    stages {
        stage('Container Build') {
            steps {	
                // 릴리즈파일 체크아웃
                checkout scmGit(branches: [[name: '*/main']], 
                    extensions: [[$class: 'SparseCheckoutPaths', 
                    sparseCheckoutPaths: [[path: "/${DIR_NUM}"]]]], 
                    userRemoteConfigs: [[url: "${GITHUB_URL}"]])

                // 컨테이너 빌드 및 업로드
                sh "docker build -t ${DOCKERHUB_USERNAME}/myweb:v1.0.0 ./${DIR_NUM}"
                sh "docker push ${DOCKERHUB_USERNAME}/myweb:v1.0.0"
            }
        }

        stage('K8S Deploy') {
            steps {
                sh "kubectl apply -f ./${DIR_NUM}/deploy/deployment-svc.yaml"
            }
        }
    }
}

 

위와 같이 pipeline을 구성한 후 저장합니다.

 

pipeline 실행

 

 

[지금 빌드] 버튼을 클릭하여 build를 실행하면 오른쪽에 pipeline이 생성되며 실시간으로 build/deploy 현황을 보실 수 있습니다.

K8S Deploy stage까지 완료가 된 후 jenkins 사용자 shell에서 k8s 리소스를 확인해보겠습니다.

 

kubernetes resource 확인

kubectl get po -A

 

kubectl 명령어를 통해 현재 배포된 Pod를 확인해보겠습니다.

 

 

jenkins pipeline이 종료된 후 k8s에 Pod가 생성되고 있는 것을 확인하실 수 있습니다.

 


Reference

- https://www.geeksforgeeks.org/how-to-solve-some-plugins-failed-to-install-properly-you-may-retry-installing-in-jenkins/