하나의 포스트로 담기에는 분량이 너무 많을 것 같아 2개로 나누었다.
이전 포스트에 이어 진행된다.
https://imsongkk.tistory.com/75
AWS EKS Jenkins 환경 구축하기 [1/2]
Jenkins란? Jenkins는 가장 널리 사용되는 CI / CD 오픈소스 자동화 도구이다. Master와 Agent로 나뉘어져 있으며 Master는 구성 및 설정들을 저장하고 빌드를 관리한다. Agent는 실제로 빌드를 수행하는 노드
imsongkk.tistory.com
Jenkins와 Github Webhook 연결하기
현재 우리 팀은 Github을 통해 소스코드를 관리하고 있다.
따라서 CI 자동화를 위해 Github Webhook을 이용해 특정 브랜치에 Push나 Merge가 일어날 경우
Jenkins가 해당 이벤트를 받아 자동으로 코드를 빌드하게 만드는게 목적이다.
--- ( 8.28 추가 ) ---
본 포스트는 브랜치별로 Job을 Build하지 않고, 모든 push event가 일어날 때마다 Job을 Build하게 된다.
이에 관한 내용은 아래 포스트에서 추가적으로 정리해놓았다.
Jenkins Webhook Multiple branch 빌드 하기
Jenkins Webhook 현재 진행하고 있는 프로젝트는 Github Push -> Webhook -> Jenkins Build 과정으로 자동화된 CI 환경을 구축했다. 아래 포스트에서 진행한 내용이다. AWS EKS Jenkins 환경 구축하기 [2/2] 하나의 포
imsongkk.tistory.com
Github Personal Access Token 생성
Jenkins는 Webhook을 수신하여, 프로젝트 레포를 Clone 및 Pull 해와야 하기 때문에, 적절한 자격 증명이 필요하다.
이는 Github에서 제공하는 Token으로 대체할 수 있다.
Token을 생성하는 방법은 구글에 많으니 따로 적지 않겠다.
Jenkins Credential에 등록
생성된 Token을 Jenkins가 사용할 수 있도록 등록해주어야 한다.
Username에는 자신의 Github 이름을 적어야 한다.
Jenkins 플러그인 설치하기
Github Webhook과 연동하려면 별도의 Github Plugin이 필요하다.
Plugin 관리에 들어가서 설치를 하면 되는데, 문제는 여기서 발생했다.
로그를 보면 ssl 인증서 부분에서 에러가 발생하는것을 볼 수 있었다.
무엇이 문제일까 생각해보다가, Plugin 저장소는 https로 되어있고,
현재 젠킨스 서버는 http로 되어있어서라는 가설을 세운뒤에
급하게 남는 도메인을 젠킨스에 입힌다음 ELB에 ssl 인증서를 적용하였다.
그랬더니 바로 해결 되었다! (사실 바로는 아니고 3시간 정도 헤맸다)
또 다른 방법으로는 Plugin 저장소를 https가 아닌 http로 지정해놓으면 되었었는데,
이는 근본적인 해결방법이 아니며 보안을 위해 사용하지 않았다.
Github Webhook 만들기
Webhook에는 타겟 URL과 Content Type만을 명시해주면 된다.
웹훅을 만들고 나면 Ping을 보내는데 다음과 같이 확인할 수 있었다.
Jenkins Item 생성
팀 내 코드리뷰 편의성을 위해 Jenkinsfile을 Repo단에서 관리하고 싶어 Pipeline type의 Item을 생성했다.
Build Trigger로는 Github hook을 선택했다.
이후 빌드할 Pipeline script가 담긴 Repo를 지정해주었다.
해당 URL을 미리 생성해둔 자격증명으로 Clone한 이후, main 브랜치를 Build 한다는 설정이다.
밑에 잘려서 나오지는 않지만 Pipeline Script 즉, Jenkinsfile이 해당 Repo에 어디에 있는지 명시하는 칸이 있는데
우리 팀은 최상위 디렉토리에 Jenkinsfile을 위치시킬것이므로 기본 설정을 따랐다.
테스트용으로 설정했던 Jenkinsfile은 다음과 같다.
pipeline {
agent any
stages {
stage('build') {
steps {
echo 'building app'
}
}
stage('test') {
steps {
echo 'testing app'
}
}
stage('deploy') {
steps {
echo 'deploying app'
}
}
}
}
Push 진행
Push를 진행하면 다음과 같이 Webhook 로그가 남는다.
Jenkins 내에서도 Agent가 생성되어 빌드가 진행 되는것을 확인할 수 있었다.
여기서도 좀 헤맸던게, 무조건 한번 이상 로컬에서 빌드가 진행 되어야 웹훅을 통한 빌드가 가능하다.
만약 빌드가 자동으로 되지 않는다면 이를 확인해보자.
Agent는 미리 정의해둔 Jenkinsfile을 remote repo에서 읽어와 실행한다.
이렇게 Github Webhook과의 연동을 마쳤다.
하지만 생각지도 못한 문제가 생겼다.
Jenkins를 왜 도입하는가?
다시 도입 이유에 대해 생각해보자.
우리 팀은 Jenkins를 이용해 소스 코드를 jar 파일로 배포하고 도커 이미지로 빌드해야 한다고 했었다.
문제는 도커 이미지 빌드였다.
도커 이미지 빌드를 담당하는 Jenkins Agent는 Pod로 실행되며 Jenkins가 미리 정의한 도커 이미지를 사용한다.
즉, 도커 컨테이너 안에서 다시 도커 이미지를 빌드해야 한다는 뜻이다.
일반적으로 도커 컨테이너 안에서 이미지를 빌드하는 방법으로 DinD, DooD 두 가지 방식을 사용할 수 있다.
DinD와 DooD
DinD와 DooD를 말하기 전에, Docker의 동작 방식을 이해해야 한다.
이전에 정리해놓은 Docker Daemon 포스트를 참고하자.
https://imsongkk.tistory.com/56
[Docker] Docker Daemon
Docker Daemon 지금까지 docker를 다룰 때 항상 docker라는 명령어를 맨 앞에 붙여서 사용했었다. docker ps -a docker logs -f my-container ... 이러한 docker 명령어는 실제로 누가 처리하고 있을까? # which docker /usr/b
imsongkk.tistory.com
DinD
Docker in Docker로, 도커 컨테이너 내부에 호스트 도커 데몬과는 별도로 새로운 도커 데몬을 실행시키는 방법이다.
즉, 우리의 상황에서는 Jenkins Agent가 도커 컨테이너로 실행이 되며 도커 데몬은 Pod 외부인 노드에 위치하는데,
이 안에서 또 별도의 도커 데몬을 실행시켜야 한다는 것이다.
DinD 방식은 Docker측에서 권장하지 않는 방식이다.
가장 큰 이유로는 도커 데몬 컨테이너를 추가로 만들때는 --previleged 옵션을 주어 실행해야 하는데
이를 통해 Inner 컨테이너가 호스트 머신에서 할 수 있는 동작들을 sudo 권한으로 할 수 있게 된다.
Jenkins Agent 컨테이너만 담당해야 하는 일들을 내부의 도커 컨테이너도 할 수 있다보니
생각치 못한 문제와 보안상으로 좋지 않다는 결론을 내렸다.
DooD
Docker Out of Docker로, 내부가 아닌 외부에 도커 컨테이너를 새로 만들고 Unix 소켓을 이용해 데몬과 소통한다.
Jenkins Agent에서 호스트 시스템에 있는 도커 데몬과 통신하여 같은 레벨의 컨테이너를 만드는 것이다.
하지만 현재 우리는 EKS에서 worker node에 접근을 하지 못하도록 막아놓았으며
도커 데몬 소켓이 어디에 위치해있는지 알지 못한다.
이 외에도 AWS EKS는 버전 1.24부터 클러스터 CRI로 더 이상 Docker를 사용하지 않는다는 문제점이 있었다.
도커 Daemon 없이 이미지 빌드하기
그렇게 방황하던중, 도커 데몬이 없어도 컨테이너 이미지를 빌드할 수 있는 방법을 찾아냈다.
만약 해결이 안됐다면, 어떻게 보면 Jenkins를 쓸 이유가 없어질만한 이유였었다.
Kaniko
Kaniko는 구글에서 개발한 컨테이너 이미지를 빌드하는 도구이다.
https://github.com/GoogleContainerTools/kaniko
GitHub - GoogleContainerTools/kaniko: Build Container Images In Kubernetes
Build Container Images In Kubernetes. Contribute to GoogleContainerTools/kaniko development by creating an account on GitHub.
github.com
구글에서 개발했지만 현재 구글에서 관리하고 있지는 않는것 같다.
Docker는 이미지 빌드를 위해 Docker Daemon이 필요한 반면에,
Kaniko는 Docker Daemon 없이 이미지를 빌드할 수 있다.
Dockerfile을 기반으로 빌드를 진행하며 로컬뿐만 아니라 S3와 같은 외부 공간을 Build Context로 둘 수 있다.
위의 Github Readme를 읽다 보면 Kaniko는 이미지 빌드 및 외부 레지스트리로 푸쉬까지 진행해준다고 한다.
이를 위해 Registry에 접근하기 위한 Secret이 필요하다.
kubectl create secret docker-registry my-reg --docker-server=[ECR URI] --docker-username=AWS --docker-password=[아래 명령어]
# password token 가져오기
aws ecr get-login-password --region ap-northeast-2
이후 Jenkins Pipeline Script를 작성한다.
pipeline{
agent {
kubernetes{
yaml '''
apiVersoin: v1
kind: Pod
spec:
containers:
- name: gradle
image: gradle
command: ['sleep']
args: ['infinity']
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
command:
- sleep
args:
- infinity
volumeMounts:
- name: registry-credentials
mountPath: /kaniko/.docker
volumes:
- name: registry-credentials
secret:
secretName: my-reg
items:
- key: .dockerconfigjson
path: config.json
'''
}
}
stages{
stage('gradle'){
steps{
container('gradle'){
git url: 'https://github.com/SWM-YouQuiz/jenkins-test.git',
branch: 'main',
credentialsId: "github_personal_access_token"
sh 'gradle bootJar'
sh 'ls -al'
sh 'mv ./build/libs/xxx.jar ./'
}
}
}
stage('docker'){
steps{
container('kaniko'){
sh "executor --dockerfile=Dockerfile --context=dir://${env.WORKSPACE} --destination=xxxxxxx.dkr.ecr.ap-northeast-2.amazonaws.com/test:latest"
}
}
post{
success{
echo 'success Build & Push'
}
failure{
echo 'failure Build & Push'
}
}
}
}
}
Kaniko는 미리 Jenkins 네임스페이스에 만들어둔 docker-registry 타입의 시크릿을 가져와 사용한다.
Kaniko가 사용할 Dockerfile을 현재 Jenkins Agent의 workspace에서 가져온다.
FROM amazoncorretto:17
COPY ./xxx.jar app.jar
ENTRYPOINT ["java", -"jar", "-Dspring.profiles.active=dev", "app.jar"]
이 Dockerfile은 remote repo에 Jenkinsfile가 있는 곳에 위치시킴으로 한곳에 CI 파이프라인을 구성할 수 있었다.
이후 ECR 레포지토리를 확인해보면 성공적으로 Push가 된것을 확인할 수 있었다.
예상되는 추가 작업
ECR에 이미지가 잘 올라갔지만, 마음에 거슬리는것이 있었다.
바로 클러스터에서 Secret으로 관리되고 있는 ECR에 접근하기 위한 Token은 만료시간이 있다는 점이었다.
우선 ECR Token은 최대 12시간의 expire time을 갖고있다.
자연스레 들었던 생각은 CronJob을 생성해 일정 간격마다 Secret의 자격증명을 갱신하는 것이었다.
현재 프로젝트가 진행중이어서 이 부분은 아직 진행하지 않았으며 설정이 다 되는대로 포스트를 업데이트 하겠다.
구현하지 못했던 부분
Jenkins 자체를 Helm으로 Install해 Code로 설정을 볼 수 있게 구성했다.
하지만 Jenkins내의 Credential과 Plugin 설치 정보등은 Code로 남길 수 없었다.
Jenkins는 Configuration as Code라는 기능을 자체적으로 지원하지만
러닝커브와 프로젝트 성숙도를 고려하여 도입하지 않았다.
https://www.jenkins.io/projects/jcasc/
Jenkins Configuration as Code
Setting up Jenkins is a complex process, as both Jenkins and its plugins require some tuning and configuration, with dozens of parameters to set within the web UI manage section. Jenkins Configuration as Code provides the ability to define this whole confi
www.jenkins.io
구현한 CI 파이프라인
'Trouble Shooting' 카테고리의 다른 글
ALB Ingress Controller로 여러 개의 Ingress 처리하기 (0) | 2023.08.11 |
---|---|
MSA 환경에서 Swagger 서버 구축하기 (0) | 2023.08.05 |
AWS EKS Jenkins 환경 구축하기 [1/2] (0) | 2023.07.31 |
AWS EKS 클러스터 구성 및 배포하기 [2/2] (1) | 2023.07.02 |
AWS EKS 클러스터 구성 및 배포하기 [1/2] (0) | 2023.07.01 |