[AEKS2] 7주차 - EKS CI/CD: Jenkins
이번 실습에서는 Docker도 사용해 보고 젠킨스도 사용해 볼 것인데
마지막엔 DockerHub, Github, Jenkins를 연동하고 Jenkins에서 CI/CD를 사용할 것이다.
Github Checkout → DockerBuild → Deploy to Kubernete
세 단계를 진행할 것이다.
Docker
EKS 환경 세팅을 다하고 나서 배스천 서버에서 docker 명령어를 사용해 본다.
docker images
$ docker pull ubuntu:20.04
20.04: Pulling from library/ubuntu
43cfb69dbb46: Pull complete
Digest: sha256:71b82b8e734f5cd0b3533a16f40ca1271f28d87343972bb4cd6bd6c38f1bd38e
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 20.04 33985b2ba010 5 days ago 72.8MB
Dockerhub에 로그인하지 않았는데도 pull로 우분투 이미지를 받아왔다.
이는 퍼블릭 레포지토리에 접근했기 때문이다.
마지막에 docker.io/library/ubuntu:20.04
로 다운받은 레포지토리 주소가 나오길래 해당 주소로 들어가 봤다.
이 레포지토리는 퍼블릭으로 공개되어 있어 아무나 접근 가능하고 태그를 붙이거나 레포지토리 명을 붙여서 이미지를 다운받을 수 있다.
$ docker pull library/ubuntu:22.04
22.04: Pulling from library/ubuntu
3c645031de29: Pull complete
Digest: sha256:1b8d8ff4777f36f19bfe73ee4df61e3a0b789caeff29caa019539ec7c9a57f95
Status: Downloaded newer image for ubuntu:22.04
docker.io/library/ubuntu:22.04
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 22.04 7af9ba4f0a47 5 days ago 77.9MB
ubuntu 20.04 33985b2ba010 5 days ago 72.8MB
그래서 library/ubuntu:22.04
로 이미지 다운로드가 가능하다는 것을 확인할 수 있었다.
Dockerizing
Dockerfile로 ubuntu라는 베이스 이미지에 올린 나만의 도커이미지를 하나 생성해 보겠다.
FROM ubuntu:20.04
ENV TZ=Asia/Seoul VERSION=1.0.0 NICK=6ain
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"]
Dockerfile를 만들었으면 이미지를 빌드하고
$ docker build -t myweb:v1.0.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myweb v1.0.0 7e3e886152db Less than a second ago 237MB
ubuntu 20.04 33985b2ba010 5 days ago 72.8MB
FROM 절에서 ubuntu를 pull해 오기 때문에 ubuntu도 로컬에 같이 다운로드됐다.
그리고 Dockerfile로 실행된 이미지의 히스토리를 확인해 볼 수 있다.
$ docker image history myweb:v1.0.0
IMAGE CREATED CREATED BY SIZE COMMENT
7e3e886152db 1 second ago /bin/sh -c #(nop) CMD ["usr/sbin/apache2ctl… 0B
221747eb527f 1 second ago /bin/sh -c #(nop) EXPOSE 80 0B
69d556e4763e 2 seconds ago /bin/sh -c ln -snf /usr/share/zoneinfo/$TZ /… 165MB
351f7324a915 21 seconds ago /bin/sh -c #(nop) ENV TZ=Asia/Seoul VERSION… 0B
33985b2ba010 5 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 5 days ago /bin/sh -c #(nop) ADD file:ea2128e23dce01625… 72.8MB
<missing> 5 days ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 5 days ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 5 days ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B
<missing> 5 days ago /bin/sh -c #(nop) ARG RELEASE 0
이제 이 빌드된 이미지를 컨테이너로 실행해 볼것이다.
$ docker run -d -p 80:80 --rm --name myweb myweb:v1.0.0
42d83a1d9b229c8d4bc4677346b5fa688da228beef200836fa7cd3ce6e6580dc
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42d83a1d9b22 myweb:v1.0.0 "usr/sbin/apache2ctl…" 46 seconds ago Up 46 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp myweb
생성할 때 --rm
옵션을 붙였기 때문에 docker stop [컨테이너ID]
하면 컨테이너는 바로 삭제된다!
또는 docker rm -f myweb
로 삭제할 수 있다.
참고로 -f
옵션은 프로세스를 강제 종료하는 SIGKILL을 사용한다.
Dockerhub
우선 이미지 저장소로 Dockerhub를 사용하기 위해 가입부터 하고 진행하자!
DHUB
에 내 Dockerhub 아이디를 환경 변수로 지정하고 태그를 붙인다.
$ DHUB=6ain
$ docker tag myweb:v1.0.0 $DHUB/myweb:v1.0.0
그럼 태그 붙인 이미지가 하나 생성된다.
이제 내가 가입한 계정으로 Dockerhub에 로그인하자!
로그인하면 내 정보가 ~/docker/config.json
에 저장되고
로그아웃하면 바로 삭제된다.
그리고 이미지를 푸시했을 때, 아까 퍼블릭 레포지토리와는 다르게 로그인을 안하면 내 레포지토리에 푸시는 거부당한다. 내 레포는 프라이빗이라 아무나 접근못하기 때문!
다시 로그인하고 푸시해 보면
성공적으로 레포지토리에 올라가 있다!
그리고 만약 로컬에 이미지가 없다면 해당 레포지토리에서 새롭게 이미지를 다운받아 컨테이너를 생성할 수도 있다!
Jenkins
젠킨스는 CICD를 제공하는 오픈소스 자동화 도구이다.
다양한 플러그인을 제공하여 사용할 수 있고 파이프라인을 통한 소프트웨어 빌드, 테스트 및 배포를 지원한다.
여러 플러그인을 제공하니 편리하게 사용할 수 있는데, 예를 들어 Git과 연동하고 특정 조건을 걸면 레포지토리에 push 또는 merge할 때마다 젠킨스 파이프라인을 구동시킬 수 있다. 또는 크론을 이용해서 특정 주기마다 파이프라인을 구동시킬 수 있다.
설치과정
이제 젠킨스를 설치하겠다.
실습 편리를 위해 root 계정으로 전환한다. 그리고 젠킨스 설치하기 위해 자바 17버전을 설치한다. 젠킨스는 자바로 만들어진 오픈소스이기 때문!
sudo yum install fontconfig java-17-amazon-corretto -y
JAVA_HOME=/usr/lib/jvm/java-17-amazon-corretto.x86_64
젠킨스를 설치하고 시작한다.
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
sudo systemctl status jenkins
설치하고 해당 주소로 들어가면 Administrator password를 입력하라고 뜨는데 이는 /var/lib/jenkins/secrets/initialAdminPassword
를 확인해 보면 알 수 있다.
이제 플러그인을 설치할 것이다.
근데 젠킨스를 처음 설치할 땐 바로 플러그인 설치가 잘되더니, 며칠 뒤에 두 번째 설치 때는 잘 진행되지 않았다.
서버에 접속해 보면 에러 메세지가 떠있다.
+) 실패한 플러그인이 보이면 Jenkins 관리 > Plugins에서 바로 다시 설치해 줘야 한다. 나는 파이프라인 실습을 하다가 자꾸 제대로 파이프라인 생성을 못하고 빈 파이프라인만 생성되길래 보니까 플러그인이 없어서 발생한 일이었다ㅠ
젠킨스를 설치한 EC2 보안그룹에서 HTTP 통신을 허용하지 않아서 발생한 이슈라고 하는데, 왜 처음에는 설치가 잘됐던건지 모르겠다,..
보안그룹을 허용해 주고 젠킨스 주소 뒤에 /safeRestart
붙여서 접속하면 서버에서 재부팅을 실행한다. 놀랍게도 정말 빠른 설치가 완료되었다.. 실습 못하는줄 알았는데 같은 스터디를 진행하는 스터디원분의 블로그 덕분에 살았다!
[AEWS] 7-1. Amazon EKS - CI/CD (Jenkins)
이유가 궁금해서 플러그인 설치를 어떤 방식으로 진행하는지 찾아 보았다.
우선 플러그인은 수동으로 파일을 넣어줘서 설치하거나 update site에서 설치할 수 있다.
/manage/pluginManager/advanced
에 들어가면 확인할 수 있다.
그리고 좀 더 찾아 보면 REST API POST 방식으로 진행한다는 점을 알 수 있었다.
/manage/pluginManager/api
를 참조하였다.
- 사용자가 플러그인 설치를 요청하면
- 브라우저에서
/installNecessaryPlugins
에 POST 요청으로 XML 형식의 데이터를 전송한다. 참고로/prevalidateConfig
에 POST 요청을 보내면 젠킨스 서버에서 필요한 플러그인 목록을 JSON 배열로 응답하여, 플러그인 목록을 미리 확인하는 유효성 검사를 진행할 수 있다. - 젠킨스에서는 XML 데이터를 분석하고 필요한 플러그인이 있는지 확인한다.
- 설치할 플러그인이 있다면
https://updates.jenkins.io/update-center.json
에서 플러그인 파일을 다운받게 된다. - 플러그인 파일을 다운로드한 후, 해당 플로그인을 서버에서 설치/업데이트한다.
- 완료되면 update center API를 이용해 진행 상황을 체크하고 경우에 따라 restart API를 호출하여 젠킨스를 재시작하고 변경사항을 적용한다.
이 과정 속에서 2번에 브라우저에서 젠킨스 서버에 POST 요청할 때 보안그룹에 의해 진행이 안된걸로 이해하였다.
기본 사용
우선 Jenkins 관리 → Tools에서 JDK 관련 설정을 진행한다.
저장하고 이제 Freestyle project를 생성해 본다.
Configuration에서 Build Steps를 누르면 Execute shell을 실행할 수 있다.
테스트로 echo "Aws Workshop Study”
를 저장하고 지금 빌드를 눌러보면
Console Output에서 잘 출력된 점을 확인할 수 있다!
Build Steps을 수정해서 명령어 몇 줄 더 추가해 보았다
빠짐없이 모든 명령어를 다 실행하였다!
하지만 만약 실행 도중에 실행할 수 없는 명령어가 있다면 어떻게 될까?
쉘스크립트 특성 상 이전 명령어가 실패하면 명령어 간의 종속성 때문에 다음 명령어를 수행하지 않는다.
참고로 만약 여러 Step이 있을 때, 중간에 실패하는 Step이 있다면 다음 Step은 수행하지 않는다. 이는 젠킨스 파이프라인도 종속적인 특징을 띄고 있기 때문이다.
-
이전 Step에서 성공했을 때
-
이전 Step에서 실패했을 때
Docker 사용
jenkins 유저가 docker를 사용하려면 권한을 설정해 줘야 한다.
먼저 jenkins 유저가 bash를 사용할 수 있게 변경해 줘야 한다. 기본은 /bin/false
로 되어 있다.
usermod -s /bin/bash jenkins
로 변경해 주자!
jenkins 유저로 전환하자!
su - jenkins
-bash-4.2$ whoami
jenkins
-bash-4.2$ pwd
/var/lib/jenkins
그리고 docker를 사용해 보면?
permission denied로 사용할 수 없다.
다시 권한 설정을 해 주면 정상적으로 docker 사용이 가능해 진다!
chmod 666 /var/run/docker.sock
usermod -aG docker jenkins
docker login 후 Dockerfile 하나 생성해서 젠킨스 테스트를 진행하자! (위 Dockerfile 참고)
역시 성공하였다 역시나!
Git
https://github.com/gasida/aews-cicd
가시다님이 제공해 주신 샘플 레포지토리를 포크해서 진행한다.
프로젝트 하나를 생성하고 Configure를 수정한다.
-
General > 매개변수
컨테이너 이미지 버전과 내 깃헙 계정명을 변수로 지정한다.
-
소스 코드 관리
레포지토리
main
브랜치의1
디렉토리를 대상으로 한다. -
빌드 유발
조건을 매 분마다로 설정해서 Git의 변경사항을 매 분마다 감시한다.
-
Build Steps
이제 조건에 만족할 때, 기존 Dockerfile을 삭제하고 다시 다운받아서 컨테이너를 생성한다.
다 만들었으면 이제 Github에 해당 경로로 들어가서 수정해 본다.
빌드 성공!
pipeline
젠킨스 파이프라인이란 전체 빌드 프로세스를 정의할 수 있는 플러그인 스크립트 모음이다.
파이프라인 구성 형태는 Pipeline script,
Pipeline script from SCM
, Blue Ocean 기반
세 가지가 있다.
나는 Pipeline script
으로 선언형 파이프라인으로 사용해 보았다.
일반적인 방식으로 쉘스크립트를 직접 생성하여 빌드하는 방식인데 아래와 같은 형식을 가진다.
pipeline {
agent any # Execute this Pipeline or any of its stages, on any available agent.
stages {
stage('Build') { # Defines the "Build" stage.
steps {
// # Perform some steps related to the "Build" stage.
}
}
stage('Test') {
steps {
//
}
}
stage('Deploy') {
steps {
//
}
}
}
}
-
간단하게 스테이지 두 개 만들어서 테스트해 보았다.
pipeline { agent any stages { stage('Hello') { steps { echo 'Hello World' sh 'java -version' } } stage('Deploy') { steps { echo "Deployed successfully!"; } } } }
-
파라미터도 사용해 보았다.
pipeline { agent any parameters { string(name: 'PERSON', defaultValue: '6ain', 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}" } } } }
파라미터와 함께 빌드를 눌러서 CHOICE 값을 하나 선택해서 빌드가 가능하다.
Jenkins with Kubernetes
젠킨스에서 쿠버네티스를 구동시키려면?
배스천에서 api server에게 request를 날릴 수 있는 환경을 만든 것처럼 젠킨스도 api server에게 request를 날릴 수 있는 권한을 주면 된다.
바로 kubeconfig를 만들어 주자!
jenkins 유저로 로그인한 상태에서 ~/.kube
디렉토리를 만들어 주고
다시 root 계정으로 로그인해서 파일을 복사해 주고 권한을 설정해 준다.
cp ~/.kube/config /var/lib/jenkins/.kube/config
chown jenkins:jenkins /var/lib/jenkins/.kube/config
이제 젠킨스도 kubectl 사용이 가능하다.
EC2에 파드를 생성할 때는 aws configure로 자격증명을 해 줘야 한다.
마지막으로 스크립트를 작성하고
pipeline {
agent any
tools {
jdk 'jdk-17'
}
environment {
DOCKERHUB_USERNAME = '6ain'
GITHUB_URL = 'https://github.com/gain-yoo/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"
}
}
}
}
Git에서 Dockerfile과 deployment.yaml을 가져와서
컨테이너를 빌드하고 이미지를 만들어서 deployment.yaml로 파드를 생성할 것이다!
빌드도 성공하고 파드도 생성되었다!
나도 컨테이너 전환하는 업무를 할 땐 젠킨스를 사용했는데 그 땐 고객사에서 젠킨스를 고객사의 DevOps 도구로 통합시켜서 SaaS화하여 UI로 잘구현해놨었다.
그래서 구축하고 스크립트로 작성해서 오리지널 그대로 사용하는 것은 처음 진행해 보았다!
역시 기본 날 것을(?) 건드려 봐야 한다!
Leave a comment