젠킨스는 헬름을 사용하면 좀 더 쉽게 설치할 수 있습니다. 하지만 헬름으로 설치한다고 해도 젠킨스를 설치하려면 사전에 준비가 필요합니다. 하나씩 같이 진행해봅시다!
헬름 실습 떄 사용했던 차트 저장소 edu는 앞으로 사용할 모든 애플리케이션이 차트로 등록돼 있습니다. 따라서 지금부터 진행하는 실습에서는 차트 저장소를 새로 등록하지 않고 바로 애플리케이션을 설치하겠습니다.
docker ps -f name=registry
헬름으로 설치되는 젠킨스는 파드에서 동작하는 애플리케이션이기 때문에 PV를 마운트하지 않으면 파드가 다시 시작될 때 내부 볼륨에 저장하는 모든 데이터가 삭제됩니다.
- 이를 방지하기 위해서 애플리케이션의 PV가 NFS를 통해 프로비저닝될 수 있게 NFS 디렉터리를 /nfs_shared/jenkins
에 만들겠습니다.
미리 정의된 nfs-exporter.sh jenkins
를 실행합니다.
이 스크립트에는 NFS용 디렉터리를 만들고 이를 NFS 서비스로 생성하는 과정이 담겨 있습니다. 상세 과정은 이전 포스팅의 PV와 PVC를 참조하시길 바랍니다.
~/_Book_k8sInfra/ch5/5.3.1/nfs-exporter.sh jenkins
nfsdir=/nfs_shared/$1
if [ $# -eq 0 ]; then
echo "usage: nfs-exporter.sh <name>"; exit 0
fi
if [[ ! -d $nfsdir ]]; then
mkdir -p $nfsdir
echo "$nfsdir 192.168.1.0/24(rw,sync,no_root_squash)" >> /etc/exports
if [[ $(systemctl is-enabled nfs) -eq "disabled" ]]; then
systemctl enable nfs
fi
systemctl restart nfs
fi
만들어진 디렉터리에 부여된 사용자 ID(uid)와 그룹 ID(gid)의 번호를 -n
옵션으로 확인합니다.
ls -n /nfs_shared
젠킨스를 헬름 차트로 설치해 애플리케이션을 사용하게 되면 젠킨스의 여러 설정 파일과 구성 파일들이 PVC를 통해 PV에 파일로 저장됩니다.
chown 1000:1000 /nfs_shared/jenkins
명령어로 젠킨스 PV가 사용할 NFS 디렉터리에 대한 접근 ID(사용자ID, 그룹ID)를 1000번으로 설정하겠습니다.chown 1000:1000 /nfs_shared/jenkins/
ls -n /nfs_shared
호스트 디렉터리와 젠킨스 컨트롤러의 ID 관계
젠킨스 컨트롤러 설치 후에 내부의 유저 ID와 그룹 ID를 살펴보면 1000번으로 설정돼 있는 것을 확인할 수 있습니다.
kubectl exec -it jenkins-~~ -- cat /etc/passwd
따라서 PV로 사용되는 NFS 디렉터리의 접근 ID를 동일하게 설정하지 않는다면 젠킨스 컨트롤러에서 이루어지는 작업들이 정상적으로 수행되지 않고 'fatal: unable to look up current user in the passwd file: no such user'와 같은 에러가 발생합니다.
젠킨스 설치 시 환경변수를 설정해서 해결하는 방법이 있으나 이는 수정해야 할 것들이 많으므로 현재의 랩 환경에서 가장 간단한 방법인 호스트 시스템의 ID와 젠킨스 컨트롤러 ID를 일치시켜주는 방법으로 해결합니다.
젠킨스는 사용자가 배포를 위해 생성한 내용과 사용자의 계정 정보, 사용하는 플러그인과 같은 데이터를 저장하기 위해서 PV와 PVCD의 구성을 필요로 합니다.
jenkins-volume.yaml
을 이용해 PV와 PVC를 구성하고, 구성된 PV와 PVC가 Bound 상태인지 확인합니다.kubectl apply -f ~/_Book_k8sInfra/ch5/5.3.1/jenkins-volume.yaml
kubectl get pv jenkins
kubectl get pvc jenkins
이제 모든 준비를 마쳤으니 젠킨스를 설치해 보겠습니다.
jenkins-install.sh
를 실행해 젠킨스를 설치하겠습니다.~/_Book_k8sInfra/ch5/5.3.1/jenkins-install.sh
jenkins
입니다. 이후 헬름 관련 명령으로 젠킨스를 조회, 삭제, 변경 등을 수행할 때 이 이름을 사용합니다!default
입니다.helm upgrade
명령어를 사용해 젠킨스의 버전을 업그레이드할 때마다 REVISION은 1씩 증가합니다. 또한, 업그레이드 작업 후 이전 버전으로 돌아가기 위해 helm rollback
명령어를 사용할 수 있습니다. helm rollback
명령어 사용시 REVISION 번호를 직접 지정해 특정 리비전으로 돌아가도록 설정할 수도 있습니다.printf $(kubectl get secret --namespace default jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
, 2번은 젠킨스가 구동하는 파드에 접속할 수 있도록 외부의 트래픽을 쿠버네티스의 파드로 전달하게 만드는 설정입니다. 외부에서 쉽게 접속하기 위해서 이 실습에서는 트래픽을 전달하는 설정을 하지 않고 로드밸런서를 사용하겠습니다. 3번에 표시된 admin은 젠킨스 접속 시 사용할 유저 이름입니다.순서대로라면 젠킨스의 코드를 살펴봐야 하지만 코드를 이해하기 위해서는 배포된 젠킨스 디플로이먼트의 정보를 함께 비교해서 봐야 합니다.
kubectl get deployment
헬름으로 설치하는 애플리케이션 초기화 하는 법(설치/사용 중 문제 해결)
헬름을 통해 젠킨스가 잘 설치되는 경우도 있지만 시스템 및 환경에 따라서 Ready 값이 '0/1'에서 멈춰 있는 경우가 있습니다.
이런 경우에는kubectl get pods
명령으로 상태를 추가 확인하고, 확인 결과가init:0/1
상태(초기화 컨테이너 구동 대기)라면, 이는 주로 젠킨스에 필요한 파일(플로그인)을 내려받는 것이 느리거나 문제가 생긴 경우입니다.
따라서 최대 20분 정도를 대기한 후 해결되지 않는다면helm uninstall jenkins
와rm -rf /nfs_shared/jenkins/*
명령으로 설치 과정에서 내려 받은 파일들을 삭제하고 초기화 시킨 이후에 다시 젠킨스를 설치하길 바랍니다.
이후에 실습한 프로메테우스와 그라파나도 설치 또는 사용 중에 문제가 발생한 경우 동일하게 초기화해 문제를 해결할 수 있습니다.
배포된 젠킨스가 외부에서 접속할 수 있는 상태인지 서비스의 상태를 확인합시다.
kubectl get service jenkins
젠킨스를 처음으로 배포해 봤으니 파드 상태도 한번 자세히 봅시다.
kubectl get pods -o wide
의문점을 해결하기 위해 kubectl get node m-k8s -o yaml | nl
과 kubectl get deployment jenkins -o yaml | nl
명령을 통해서 상태를 비교해 보겠습니다.
여기서 nl
은 number lines of files
의 약자로 줄 번호를 추가하는 역할입니다.
164 taints:
165 - effect: NoSchedule
166 key: node-role.kubernetes.io/master
561 tolerations:
562 - effect: NoSchedule
563 key: node-role.kubernetes.io/master
출력되는 많은 내용 중에서 위에 표시된 테인트(taints)와 톨러레이션(tolerations)이 이런 결과를 만든 설정입니다.
일반적으로 테인트와 톨러레이션은 혼합해서 사용하는데, 개념적으로 매우 혼동이 올 수 있으므로 비유를 들어 설명해 보겠습니다.
테인트는 손에 잡기 싫은 것, 피하고 싶은 것, 가지 말았으면 하는 것을 의미합니다.
테인트가 설정되면 그곳은 설거지가 끝난 이후에 수채구멍과 같습니다.
그렇지만 상황에 따라서 테인트가 설정돼 있는 곳을 꼭 만져야 할 때가 있습니다.
주로 아침에 나갈 때 버리는 음식물 쓰레기 같은 경우 말입니다.
이런 경우 인내하고 음식물 쓰레기를 처리해야 합니다.
그러기 위해선 톨러레이션, 즉 참아내는 인내가 필요한 것입니다.
쿠버네티스의 테인트와 톨러레이션은 사전적인 의미와 반대입니다.
위에서 언급한 것처럼 매우 특별하게 취급돼야 하는 곳에는 테인트를 설정해, 쉽게 접근하지 못하는 소중한 것으로 설정합니다.
그리고 톨러레이션이라는 특별한 키를 가져야만 이곳에 출입할 수 있습니다.
즉, 현재 상태에서는 마스터 노드에 테인트가 설정돼 있어 특별한 목적으로 사용되는 노드라는 것을 명시해 두었습니다.
일반적으로 마스터 노드 이외에도 GPU 노드, DB 전용 노드 등의 특별한 목적으로 사용될 때 주로 사용합니다.
이 포스팅에서는 관리의 편의를 위해 젠킨스 컨트롤러가 여러 곳에 스케줄되지 않고 마스터 노드인 m-k8s에서만 스케줄될 수 있도록 구성했습니다.
앞으로 컨트롤러가 가지는 애플리케이션 배포시에는 동일하게 마스터 노드에 컨트롤러를 구성하겠습니다.
테인트와 톨러레이션은 관계를 정의하는 것에 따라서 배포를 상당히 유연하게 만들 수 있습니다.
테인트는 키(key)와 값(value) 그리고 키와 값에 따른 효과(effect)의 조합을 통해 테인트를 설정한 노드에 파드 배치의 기준을 설정합니다.
그리고 톨러레이션은 테인트와 마찬가지로 키, 값, 효과를 가지고 있으며 이외에 연산자(operator)를 추가로 가지고 있습니다.
우선 테인트에 대한 요소를 살펴보면 키와 값의 조합은 테인트를 설정한 노드가 어떤 노드인지를 구분하기 위해 사용합니다.
키는 필수로 설정해야 하지만 값은 생략할 수도 있습니다. 9번 실습의 노드 명서에서 나타난 key: node-role.kubernetes.io/master
는 이 노드가 마스터의 역할을 한다는 것을 나타내기 위해 작성된 것입니다.
효과는 테인트와 톨러레이션의 요소인 키 또는 값이 일치하지 않는 파드가 노드에 스케줄되려고 하는 경우 어떤 동작을 할 것인지를 나타냅니다.
효과는 NoSchedule, PreferNoSchedule, NoExecute
을 값으로 가질 수 있는데 효과에 따라서 테인트를 설정한 노드는 파드를 새로 배치하는 경우와 파드가 이미 배치된 노드에 대한 동작이 다릅니다. 다음과 같이 정리할 수 있습니다.
효과 | 테인트가 설정된 노드에 파드 신규 배치 | 파드가 배치된 노드에 테인트 설정 |
---|---|---|
NoSchedule | 노드에 파드 배치를 거부 | 노드에 존재하는 파드 유지 |
PreferNoSchedule | 다른 노드에 파드 배치가 불가능할 때는 노드에 파드 배치 | 노드에 존재하는 파드 유지 |
NoExecute | 노드에 파드 배치를 거부 | 파드를 노드에서 제거 |
이번에는 톨러레이션의 요소들에 대해서 살펴보겠습니다.
톨러레이션은 테인트와 마찬가지로 키, 값, 효과를 가지고 있으며 연산자라는 특별한 요소를 가집니다.
톨러레이션은 테인트가 설정된 노드로 들어가기 위한 특별한 열쇠 역할을 하며 키와 효과는 바늗시 일치해야 합니다.
이때 톨러레이션에만 존재하는 연산자는 기본적으로 Equal로 동작해 테인트와 톨러레이션을 비교하는 역할을 합니다.
하지만 Exists의 경우에는 비교할 키와 값이 존재한다는 가정으로 테인트에 진입할 수 있는 만능 키로 바꿔주는 역할을 합니다.
하지만 그렇게 간단하지는 않으니 조금 더 자세히 살펴보겠습니다.
톨러레이션은 톨러레이션의 키, 값, 효과를 사용해 연산자를 통해 비교한 후 조건에 맞는 테인트를 식별합니다.
키와 효과 중 생략된 요소가 있다면 해당 요소는 묵시적으로 모든 키 혹은 모든 효과를 의미합니다.
톨러레이션의 키, 값, 효과는 테인트의 키, 값, 효과와 조건이 맞는지를 일치(Equal) 혹은 존재(Exists) 연산자를 통해서 판단합니다.
이떄 연산자를 생략할 경우에는 묵시적으로 Equal을 의미합니다.
조건 판단 결과 테인트와 톨러레이션의 조건이 맞다면 테인트가 설정된 노드에 톨러레이션을 가진 파드를 배치할 수 있습니다.
조건이 맞다고 판단하는 기준은 Equal 연산자를 사용했을 때 테인트와 톨러레이션의 키와 값 그리고 효과까지 일치하는 경우입니다.
Exists 연산자를 사용했을 때는 값은 반드시 생략해야 하며 이 상태에서 키와 효과의 일치 여부를 판단하게 됩니다.
또한 키와 효과를 모두 생략한 상태에서 Exists 연산자만 사용한다면 테인트의 키와 효과는 모든 키와 모든 효과를 의미하므로 Exists 연산자 하나만으로도 테인트가 설정된 모든 노드에 대해서 해당 톨러레이션을 설정한 파드를 배포할 수 있게 됩니다.
그러면 이제 헬름을 통해서 젠킨스를 설치했던 스크립트인 jenkins-install.sh
의 내용을 살펴보겠습니다.
다수의 --set 값들을 통해서 현재 환경에 맞는 젠킨스를 설치할 수 있습니다.
#!/usr/bin/env bash
# 기본 설정으론 30분 넘게 사용하지 않으면 세션이 종료됩니다. 추가 설정을 통해 세션의 유효 시간을 하(1440분)으로 변경하고, 세션을 정리하는 시간 또한 하루(86400초)로 변경합니다.
jkopt1="--sessionTimeout=1440"
jkopt2="--sessionEviction=86400"
# 기본 설정으론 시간대가 정확히 맞지 않아 젠킨스를 통한 CI/CD 시엔 명확히 작업을 구분하기 힘듭니다. 그래서 서울(Asia/Seoul) 시간대로 변경합니다.
jvopt1="-Duser.timezone=Asia/Seoul"
# 쿠버네티스를 위한 젠킨스 에이전트 노드 설정은 Pod Template이라는 곳을 통해서 설정값을 입력합니다. 그런데 가상 머신인 마스터 노드가 다시 시작하면 이에 대한 설정이 초기화됩니다. 따라서 설정값을 미리 입력해둔 야믈 파일을 깃허브 저장소에서 받아오도록 설정합니다.
jvopt2="-Dcasc.jenkins.config=https://raw.githubusercontent.com/sysnet4admin/_Book_k8sInfra/main/ch5/5.3.1/jenkins-config.yaml"
# edu 차트 저장소의 jenkins 차트를 사용해 jenkins 릴리스를 설치합니다.
helm install jenkins edu/jenkins \
# PVC 동적 프로비저닝을 사용할 수 없는 가상 머신 기반의 환경이기 때문에 이미 만들어 놓은 jenkins라는 이름의 PVC를 사용하도록 설정합니다.
--set persistence.existingClaim=jenkins \
# 젠킨스 접속 시 사용할 관리자 비밀번호를 설정합니다. 이 값을 설정하지 않을 경우에는 설치 과정에서 젠킨스가 임의로 생성한 비밀번호를 사용해야합니다.
--set master.adminPassword=admin \
# 젠킨스의 컨트롤러 파드를 쿠버네티스 마스터 노드에 배치하도록 선택합니다. 11번 과정에서 마스터 노드는 kubernetes.io/hostname=m-k8s라는 레이블을 가지고 있는 것을 확인했습니다. nodeSelector는 nodeSelector의 뒤에 따라오는 문자열과 일치하는 레이블을 가진 노드에 파드를 스케줄링하겠다는 설정입니다. 그런데 .io 옆에 \가 붙어있는데 kubernetes.io에 '.'이 포함되어 있기 때문에 \가 없다면 하나의 문자열로 인식하지 못하고 kubernetes라는 속성과 이것의 하위 속성 io로 분리해서 인식하게 됩니다. 이를 방지하게 위해 이스케이프 문자를 붙여줍니다.
--set master.nodeSelector."kubernetes\.io/hostname"=m-k8s \
# 10번째 줄을 통해 마스터 노드에 파드를 배치할 것을 명시했지만 아래 세 줄이 없다면 마스터 노드에 파드를 배치할 수 없습니다. 현재 마스터 노드에는 파드를 배치하지 않도록 NoSchedule이라는 테인트가 설정된 상태이기 때문입니다. 앞서 설명한 테인트와 톨러레이션을 참고해주세요!
--set master.tolerations[0].key=node-role.kubernetes.io/master \
--set master.tolerations[0].effect=NoSchedule \
--set master.tolerations[0].operator=Exists \
# 젠킨스를 구동하는 파드가 실행될 떄 가질 유저 ID와 그룹ID를 설정합니다. 이때 사용되는 runAsUser는 사용자 ID를 의미하고, runAsGroup은 그룹 ID를 의미합니다.
--set master.runAsUser=1000 \
--set master.runAsGroup=1000 \
# 이후 젠킨스 버전에 따른 UI 변경을 막기 위해서 젠킨스 버전을 2.249.3으로 설정합니다.
--set master.tag=2.249.3-lts-centos7 \
# 차트로 생성되는 서비스의 타입을 로드밸런서로 설정해 외부 IP를 받아옵니다.
--set master.serviceType=LoadBalancer \
# 젠킨스가 http상에서 구동되도록 포트를 80으로 지정합니다.
--set master.servicePort=80 \
# 젠킨스에 추가로 필요한 설정들을 2~3번째 줄에 변수로 선언했습니다. 변수를 호출해 적용합니다.
--set master.jenkinsOpts="$jkopt1 $jkopt2" \
# 젠킨스를 구동하기 위한 환경 설정에 필요한 것들은 4~5번째 줄에 변수로 선언했습니다. 변수들을 호출해 젠킨스 실행 환경(JVM, 자바 가상 머산)에 적용합니다.
--set master.javaOpts="$jvopt1 $jvopt2"
여기까지 헬름을 이용해서 복잡한 젠킨스의 설치 과정을 간단하게 완료해봤습니다!
젠킨스를 직접 접속해서 살펴보기에 앞서 현재 설치된 젠킨스의 구조를 간단히 살펴보겠습니다.
젠킨스 컨트롤러는 마스터 노드에 설치했지만,
젠킨스 에이전트는 필요 시에 생성되고 작업을 마치면 삭제되는 임시적인 구조를 가집니다.
따라서 젠킨스 에이전트 작업 내용들은 삭제 전에 젠킨스 컨트롤러에 저장되어야 하며,
이를 위해 젠킨스 에이전트 서비스가 항상 독작하고 있습니다.
kubectl get services
명령으로 현재 젠킨스 에이전트 서비스를 확인할 수 있습니다.
젠킨스 컨트롤러를 단독으로 설치할 경우에는 컨트롤러가 설치된 서버에서
젠킨스 자체 시스템을 관리, CI/CD 설정, 빌드 등의 작업을 모두 젠킨스 컨트롤러 단일 노드에서 수행합니다.
하지만 컨트롤러-에이전트 구조로 설치할 경우 컨트롤러는 젠킨스 자체의 관리 및 CI/CD와 관련된 설정만을 담당하고
실제 빌드 작업은 에이전트로 설정된 노드에서 이루어집니다.
따라서 컨트롤러 단독 설치는 일반적으로 간단한 테스트에서만 사용되고 주로 컨트롤러-에이전트 구조로 사용하기 때문에 실습은 이와 같은 환경으로 구성했습니다.
그럼 젠킨스에 직접 접속해 어떻게 구성돼 있는지 살펴봅시다!
호스트 웹 브라우저를 열고 앞서 확인한 로드밸런서 타입의 외부 IP인 192.168.1.11에 접속하면 다음과 같은 로그인 화면을 확인할 수 있습니다.
사용자 이름과 비밀번호 입력 후 로그인을 누르면 다음과 같이 젠킨스 홈 화면을 확인할 수 있습니다.
혹시 처음 접속할 때 오른쪽 상단에 빨간색 알람이 보인다면 이유는 젠킨스의 플러그인 업데이트가 필요하기 때문입니다.
이 에러를 해결하는 방법은 Jenkins 관리 메뉴를 알아보면서 살펴봅시다.
메인 화면 좌측에 있는 메뉴들은 다음과 같은 역할을 합니다.
새로운 Item
사람
빌드 기록
Jenkins 관리
My Views
Lockable Resources
New View
젠킨스를 사용하려면 몇 가지 기본 설정이 필요합니다. Jenkins 관리 메뉴를 눌러 젠킨스 설정 화면에 대해서 알아보겠습니다.(자주 사용하는 메뉴들 중심으로)
의존 플러그인 버전
시스템 설정
Global Tool Configuration
플러그인 관리
노드 관리
Configuration as Code
Manage Credentials
젠킨스 알람이 갑자기 사라졌어요
젠킨스의 알람은 젠킨스 관리 메뉴에 진입하는 경우, 수정을 하고 있다고 판단해 알람을 보여주지 않습니다. 따라서 당장 알람이 사라졌다고 해서 문제가 수정된 것이 아니니 오해가 없길 바랍니다.
젠킨스를 사용하기 위해서는 기본적으로 컨트롤러와 에이전트 구동과 관련한 여러 설정이 필요합니다.
하지만 편의를 위해 컨트롤러와 에이전트에 대한 설정 중에 필요한 입력 부분을 헬름을 설치할 때 이미 포함시켰습니다.
미리 입력한 부분과 그 외 설정은 젠킨스를 관리하기 위해 알아두어야 하므로 어디서 설정이 가능한지 살펴보고 진행해봅시다!
젠킨스 컨트롤러에 관한 설정을 진행하기 위해서 왼쪽 상단의 젠킨스 로고를 클릭해 홈 화면으로 이동한 후에 젠킨스 관리 > 시스템 설정 메뉴로 이동합니다.
다음과 같이 젠킨스 시스템 설정 페이지가 나옵니다.
여기서 설정되는 내용은 현재 접속한 컨트롤러의 설정을 의미합니다.
젠킨스 시스템 설정 메뉴에서는 다음과 같은 설정값을 제공합니다.
시스템 메세지
# of executors
Label
Usage
Quiet period
SCM checkout retry count
Restrict project naming
Jenkins URL
Resource Root URL
젠킨스 시스템에서 설정할 수 있는 여러 내용을 알아봤습니다. 다음은 젠킨스에 기능을 불어넣는 플러그인을 살펴봅시다!
젠킨스는 실행되는 모든 기능을 플로그인으로 구현하도록 설계돼 있습니다.
이렇게 설치한 플러그인들을 단독으로 사용하거나 여러 개를 조합해 더 강력한 CI/CD 기능을 만들 수 있습니다.
이런 예로 쿠버네티스 위에 에이전트 파드를 설정할 수 있게 도와주는 메뉴는 kubernetes 플러그인이 있습니다.
잠시 후 젠킨스 사용하기에서 실습하게 될 파이프라인 프로젝트 기능도 플러그인을 통해서 구현됩니다.
혹시 아까 플러그인 오류가 떴다면, 지금 문제를 해결해보도록 합시다.
젠킨스 홈 화면에서 젠킨스 관리 > 플러그인 관리 메뉴로 이동합니다.
젠킨스 플러그인 관리 메뉴는 다음과 같은 기능을 제공합니다.
업데이트된 플러그인 목록
설치 가능
설치된 플러그인 목록
고급
이제 젠킨스에서 사용해야 하는 플러그인을 업데이트하겠습니다.
화면을 맨 아래로 내리면 다음과 같은 화면이 보입니다.
선택 항목 중에서 호환 가능한(Compatible) 플러그인을 선택한 후, 지금 다운로그하고 재시작 후 설치하기 버튼을 눌러서 플러그인을 업데이트합니다.
젠킨스 플러그인을 업그레이드하는 경우엔 재시작이 필요합니다.
따라서 설치가 끝나고 실행 중인 작업이 없으면 Jenkins 재시작을 체크해 작업 종류 후에 젠킨스가 재시작되게 합니다.
설치가 끝나면 다시 로그인해 홈 화면에 접속합니다.
다음 그림처럼 오른쪽 상단에 모든 알림이 사라진 것을 확인할 수 있습니다.
젠킨스 컨트롤러(마스터 노드)에 대한 설정을 모두 마쳤습니다. 다음으로 젠킨스 에이전트(워커 노드)를 설정해 봅시다.
젠킨스 에이전트도 미리 설정해뒀지만, 입력된 설정들의 위치와 목적을 이해하고 넘어갑시다.
홈 화면에서 젠킨스 관리 > 노드 관리 메뉴로 이동합니다.
노드 관리의 주요 내용은 다음과 같습니다.
신규 노드
Configure Clouds
Node Monitoring
노드 목록
Configure Clouds 메뉴로 이동합니다.
이미 많은 내용이 입력돼 있는데, 헬름을 통해 젠킨스를 설치할 때 JCasC(Jenkins Configuration as Code)라는 기능을 사용해, 현재 쿠버네티스 환경에 맞게 많은 설정을 자동화했기 때문입니다.
따라서 사용자는 일부만 수정하면 됩니다.
이런 과정은 kubernetes 플러그인의 도움을 받아서 진행되므로 앞에서 플러그인 업데이트를 먼저 진행했습니다.
Configure Clouds 메뉴의 주요 내용을 살펴봅시다.
Kubernetes
Kubernetes Cloud details
Pod templates
내용을 살펴보기 전에 잠깐 현재 작업에 대한 목적을 간단히 살펴보겠습니다.
젠킨스의 CI/CD 작업은 실제로 에이전트로 동작하는데, 쿠버네티스 환경에서는 에이전트가 파드로 운영되나 이 파드에는 도커 빌드를 위한 docker 명령과 쿠버네티스 배포를 위한 kubectl 명령이 존재하지 않습니다.
가장 쉬운 해결 방법은 호스트 시스템에 있는 도커와 kubectl을 그대로 이용하는 것입니다.
따라서 hostpath를 잡아 각 노드에 이미 설치돼 있는 도커와 kubectl을 그대로 이용하겠습니다.
여기서 hostpath란 쿠버네티스 파드에서 워커 노드에 있는 특정 경로를 마운트해서 파드 내에서 사용할 수 있는 것을 말합니다!
편의를 위해서 미리 설정된 내용이 default pod Template에 이미 적용돼 있습니다.
추가로 default pod Template을 살펴보기 전에 알아두면 이해하는데 도움이 되는 내용을 언급하고 넘어가겠습니다.
Pod Template의 내용을 여러 번 보다 보면 어디선가 보던 구조임을 알 수 잇습니다.
Pod Template은 말 그대로 파드의 구성 요소를 그대로 메뉴상에 넣어 둔 것입니다.
상당히 많은 내용이 있으나 실제로는 파드 생성에 필요한 정보들을 그대로 메뉴로 구현한 것입니다.
해당 내용을 생성된 임의의 파드를 선택해서 kubectl get pod <이름> -o yaml
로 나온 결과를 지금부터 진행행 Pod Template과 비교해 보면 훨씬 더 쉽게 이해가 될 겁니다.
그러면 기본적인 Pod Template 정보 및 jenkins-config.yaml을 통해 입력된 내용을 살펴봅시다!
kubectl get pods
kubectl get pod jenkins-6b6f9b8675-9j2mf -o yaml
Pod Template 이름을 설정할 수 있는 페이지가 가장 위에 있습니다.
Name
Labels
Usage
Pod Template에 파드에 대한 기본 정보를 입력했으니 파드에서 사용할 컨테이너 설정을 진행하겠습니다.
파드에서 사용할 컨테이너를 설정하는 메뉴입니다.
Name
Docker image
Command to run
Environment Variable
다음으로 빌드 작업 중 호스트에 설치된 명령어를 파드 내부에서 사용하기 위한 Volumes를 설정하는 메뉴를 살펴봅시다!
스크롤을 조금 내린 후 Add Volume
을 선택하면 다음과 같은 화면이 나옵니다.
파드 내부에 볼륨(volume)을 설정할 수 있는 여러 가지 옵션을 제공합니다.
Config Map Volume
Empty Dir Volume
Host Path Volume
NFS Volume
Persistent Volume Claim
Secret Volume
젠킨스를 이용한 배포 작업은 내부에서 셸 스크립트 단위로 작업을 나누어 구성할 수 잇습니다.
우리의 목적은 젠킨스를 이용해 컨테이너 이미지를 빌드하고
컨테이너를 쿠버네티스에 배포하는 것입니다.
이를 위해 젠킨스 내부에서 kubectl, docker와 같은 명령어를 사용해야 합니다.
하지만 배포되는 파드는 이와 같은 명령들이 포함돼 있지 않은 도커 이미지이기 떄문에
호스트에 존재하는 명령을 파드에서 그대로 사용할 수 있는 Host Path Volume을 사용해 구성했습니다.
구조적으로는 Host path(쿠바네티스 워커 노드)에 있는 내용이 Mount Path(젠킨스 에이전트 파드)로 설정되는 구조이며,
Host Path Volume으로 추가된 3개의 Host Path Volume은 다음과 같습니다.
(kubectl) Host Path Volume
kubectl
명령을 에이전트 파드 내부에서 사용할 수 있도록 /usr/bin/kubectl 경로를 호스트로부터 연결해 줍니다. 이를 통해 빌드 작업 중 쿠버네티스와 관련된 작업을 할 수 있습니다.(docker) Host Path Volume
docker
명령을 에어전트 파드 내부에서 사용할 수 있도록 /bin/docker 경로를 호스트로부터 연결해 줍니다. 이를 통해 빌드 작업 중 도커 이미지를 생성하고 저장소로 밀어 넣을 수 있습니다.(docker.sock) Host Path Volume
스크롤을 더 내려가다 보면 젠킨스 에이전트 파드에서 사용할 서비스 어카운트(Service Account)와 사용자 ID 및 그룹 ID를 설정했습니다.
메뉴 내용은 다음과 같습니다.
서비스 어카운트
jenkins
라는 서비스 어카운트를 사용합니다.사용자 ID
그룹 ID
이상으로 모든 입력 및 권한 설정을 마치면 하단의 Save를 눌러 저장합니다!
쿠버네티스 API 서버와의 통신은 단순히 서비스 아카운트를 설정하고 이에 맞는 사용자 ID 및 그룹 ID를 갖는다고 해서 가능하 것은 아닙니다.
서비스 어카운트에 쿠버네티스 API 서버와의 통신 권한을 따로 적용해야 합니다.
아래에서 진행해봅시다!
실제로 젠킨스 에이전트 파드에서 쿠버네티스 API 서버로 통신하려면 서비스 어카운트에 권한을 줘야 합니다.
권한을 주기 전에 우선 jenkins 서비스 어카운트가 존재하는지 kubectl get serviceaccounts
로 확인합니다.
이제 서비스 어카운트 계정인 jenkins에 쿠버네티스 클러스터에 대한 admin 권한을 부여합니다.
kubectl create clusterrolebinding jenkins-cluster-admin \ --clusterrole=cluster-admin --serviceaccount=default:jenkins
권한이 적용되었씁니다! 권한을 부여한 의미를 알아봅시다.
jenkins 서비스 어카운트를 통해 젠킨스 에이전트 파드를 생성하거나 젠킨스 에이전트 파드 내부에서 쿠버네티스의 오브젝트에 제약 없이 접근하려면 cluster-admin 역할(role) 을 부여해야 합니다.
필요한 영역으로 나누어 권한을 부여하는 것이 일반적이나 효율적으로 실습하기 위해 cluster-admin 1개 권한만 부여하였습니다.
서비스 어카운트에 cluster-admin 역할을 부여하고 이를 권한이 필요한 서비스 어카운트(사용자, 그룹)인
jenkins에 묶어(Binding) 줍니다.
이런 방식을 역할 기반 접근 제어(RBAC, Role-Based Access Control)라고 합니다.
쿠버네티스의 역할 부여 구조는 할 수 있는 일(무엇을 할 수 있는가?) 과 할 수 있는 주체(누가 할 수 있는가?) 의 결합으로 이루어 집니다.
Rules(규칙)
Role, ClusterRole(역할)
RoleBinding, ClusterRoleBinding
Subjects
앞에서 실행했던 kubectl create clusterrolebinding jenkins-cluster-admin \ --clusterrole=cluster-admin --serviceaccount=default:jenkins
의 의미를 다시 한 번 살펴봅시다.
kubectl create를 통해서 clusterrolebinding을 jenkins-cluster-admin이라는 이름으로 만듭니다.
그리고 두 가지 옵션을 주는데, 첫 번째는 clusterrole에 묶여질 역할을 'cluster-admin이라는 미리 정의된 클러스터 관리자 역할'으로 줍니다.
두 번째 옵션은 jenkins-cluster-admin이라는 클러스터 역할의 서비스 어카운트를 jenkins로 지정합니다.
이때 여러 가지의 서비스 어카운트가 존재할 수 있으므로 jenkins에 속해 있는 네임스페이스 default도 함께 지정합니다.
이를 표로 표현하면 다음과 같습니다.
명령 | 설명 |
---|---|
kubectl create | 오브젝트를 생성하는 kubectl 명령입니다. |
clusterrolebinding | 생성되는 오브젝트가 clusterrolebinding임을 나타냅니다. |
jenkins-cluster-admin | clusterrolebinding으로 생성되는 오브젝트의 이름이 jenkins-cluster-admin임을 나타냅니다. |
--clusterrole=cluster-admin | clusterrolebinding의 첫 번째 옵션으로, cluster-admin 역할을 부여합니다. |
--serviceaccount=default:jenkins | clusterrolebinding의 두 번째 옵션으로, default에 있는 jenkins라는 서비스 어카운트에 이 권한을 부여합니다. |
이렇게 적용된 내용을 확인하기 위해서는 clusterrolebinding에 적용된 내용을 자세히 yaml로 출력해 보는 것입니다.
확인하고자 하는 내용은 가장 하단에 있습니다.
kubectl get clusterrolebindings jenkins-cluster-admin -o yaml
다소 내용이 복잡해 보이지만, 여러 번 생성을 반복하다보면 익숙해질겁니다.
역할 기반 접근 제어 기법은 쿠버네티스 뿐만 아니라 클라우드, 그리고 거의 모든 권한 제어에 표준처럼 쓰이는 기법이므로 기번 기회에 충분히 학습해 내 것으로 만들길 권장드립니다.
이렇게 권한이 설정된 jenkins 서비스 어카운트는 다음 그림과 같이 자유롭게 kubectl을 사용해 CI/CD를 쿠버네티스 내에서 구현할 수 있습니다.
지금까지 젠킨스를 헬름으로 설치하고 젠킨스의 기본적인 메뉴에 대해서 알아봤습니다.
그리고 CI/CD를 제공하는 젠킨스 컨트롤러와 에이전트를 설정해 CI/CD 파이프라인을 실행할 수 있는 모든 준비를 마쳤습니다.
일반적으로는 간단한 테스트를 돌려서 설치가 정상적으로 이루어졌는지 점검하나, 젠킨스의 간단한 테스트 자체는 큰 의미가 없기 때문에 바로 CI/CD를 사용해 보면서 설정에 문제가 없음을 검증해봅시다.
만약 이후 빌드 작업에서 오류가 발생한다면 앞서 실습한 학습한 내용들을 다시 점검하시기 바랍니다.
쿠버네티스 환경에서 젠킨스를 사용하기 위한 설정을 모두 마쳤으니 이제 젠킨스를 이용해서 CI/CD를 구성해봅시다!
상단의 Jenkins 로고를 클릭해 젠킨스 홈 화면으로 돌아간 후 새로은 Item을 클릭해 새로운 아이템을 만듭니다.
젠킨스에서 아이템이란 '새롭게 정의할 작업'을 의미합니다.
젠킨스가 CI/CD 도구임을 익히 들어서 알고 있지만, 사실 CI/CD 작업을 하려면 각각의 작업은 모두 정의가 필요합니다.
만약 작업을 코드로 정의한 경우라고 해도 작업 순서 정도는 알려줘야 합니다.
모든 작업의 정의와 순서를 모아 둔 전체 작업을 프로젝트라고 하는데, 프로젝트를 생성하는 방식은 Freestyle, Pipeline 등이 있습니다.
이렇게 프로젝트를 정의하고 생성하는 것을 아이템이라고 하며, 프로젝트 외에 실제로 작업에 도움이 되는 내용들을 정의하는 것도 아이템을 생성한다고 할 수 있습니다.
예를 들면 프로젝트를 구분해 저장해 두는 디렉터리인 Folder가 그러합니다.
이러한 용어를 몰라도 실습하는데 큰 지장은 없지만 추후 설명은 읽다보면 용어에 혼동이 올 수 있기 때문에
정확하게 정의하고 넘어갑시다.
화면에 있는 각 아이템은 다음과 같은 작업을 할 수 있습니다.
Freestyle project
Pipeline
Multi-configuration porject
Folder
Multibranch Pipeline
5개의 아이템 중에서 주로 사용되는 것은 Freestyle과 Pipeline 입니다.
일반적으로 간단한 작업이나 공유보다는 직접 사용을 염두에 둔 작업은 프리스타일로 정의하고,
복잡도가 높아서 고려할 것이 많거나 정의해 둔 내용의 공유 및 재사용을 중시하는 경우에는 파이프라인을 사용합니다.
프리스타일과 파이프라인을 사용해 CI/CD를 구현해 보겠습니다!
먼저 Freestyle을 이용해 컨테이너를 쿠버네티스 클러스터에 배포하겠습니다.
젠킨스는 배포를 위해 사용하는 도구이므로 배포할 대상은 사전에 구성돼 있어야 합니다.
배포할 대상은 IP주소를 반환하는 간단한 Nginx 웹 서버인 echo-ip입니다.(쿠버네티스를 실습하면서 계속 썼던 서버인 것을 기억하시죠?)
CI를 실습하려면 echo-ip를 빌드해야 하므로 해당 소스를 깃허브 저장소(https://github.com/iac-source/echo-ip)에서 가져 옵니다.
그리고 CD를 위해서 kubectl create와 expose를 Freestyle 프로젝트에서 배포로 사용하도록 정의하겠습니다.
echo-ip는 앞으로 계속 실습에서 사용할 내용이니 간단히 살펴보도록 합시다.
Dockerfile
Jenkinsfile
README.md
cert.crt
cert.key
nginx.conf
실습을 진행하기 전에 간략히 젠킨스 Freestyle로 CI/CD를 구성하는 순서도 요약해보겠습니다.
작업 중 화면을 길게 방치하면 403 오류가 발생하는 이유
젠킨스에서 설정값을 입력하는 시간이 30분 이상 소요되거나 화면을 장시간 방치했을 때 정보를 제출하거나 화면을 전환하는 과정에서 403 No vaild crumb 오류가 발생하는 경우가 있습니다.
이는 젠킨스의 강화된 보안 때문입니다.
젠킨스는 2.222.1 버전부터 사이트 간 요청 위조(CSRF, Cross site request forgery) 공격을 방어하는 옵션을 강제로 활성화하며 임의로 이 옵션을 해제할 수 없습니다.
- CORS, XSS, CSRF에 대한 포스팅
사이트 간 요청 위조(CSRF)는 인증 받은 유저의 권한을 도용해 임의로 사이트의 특정 기능을 실행하는 공격 기법입니다.
젠킨스는 이러한 공격을 방어하기 위해 서버로 정보를 전달하기 전 일정 시간 동안만 우효한 crumb이라는 토큰을 정보 입력 화면 진입 시에 발급합니다.(세션 같은 개념일 듯 하네요.)
화면을 오랫동안 방치하면 이 토큰의 유효 기간이 만료돼 젠킨스 입장에서는 사용자가 서버로 전송한 내용이 공격 시도인지 정상적인 입력인지 판단하지 못해 오류를 발생시킵니다.
따라서 한 화면 내에서 입력하는 시간은 30분 이내로 유지하거나, crumb 오류가 발생하면 당황하지 말고 새로고침 후에 다시 접속하면 됩니다.
실험을 위해 30분 이상 접속을 유지하되 작업하지 않은 뒤 프로젝트를 생성하려고 했더니 저는 자동으로 로그아웃이 되어 오류를 발생시키는군요!(새로고침하면 재로그인 됩니다.)
자, 이제 설명은 끝났으니 앞의 작업 순대로 Freestyle 프로젝트를 진행해 봅시다~!
이름은dpy-fs-dir-prod
로 지정하고 Freestyle project 아이템을 선택한 후 OK버튼을 누릅니다.
General 탭에서 Restrict where this project can be run(이 프로젝트를 실행할 수 있는 위치 제한) 체크를 해체합니다.
소스 코드 관리 탭에서는 젠킨스 외부에 있는 소스 코드 저장소를 젠킨스 CI로 사용하도록 지정할 수 있습니다.
https://github.com/iac-source/echo-ip
를 입력하고, Branch Specifier에는 현재 입력한 깃허브 저장소에 존재하는 주요(main) 브랜치 변경 내용에 대해서는 CI를 진행하도록 */master
에서 */main
으로 변경합니다. 그 외의 메뉴는 변경하지 않고 그대로 진행하겠습니다.Build 단계를 추가합니다. 실제로 젠킨스가 작업을 수행할 방법을 선택하는 단계입니다.
Add build step
을 클릭해 Execute Shell을 선택합니다. 젠킨스에서 빌드에 사용할 명령어를 확인하고 입력합니다.
cat
명령으로 스크립트를 출력한 후 복사한 다음 Execute Shell 메뉴를 누른 후에 나온 칸에 붙여 넣습니다. 그리고 저장을 눌러 설정한 프로젝트 내용을 저장합니다.cat ~/_Book_k8sInfra/ch5/5.4.1/echo-ip-101.freestyle
docker build -t 192.168.1.10:8443/echo-ip . # 도커 빌드 / CI 작업
docker push 192.168.1.10:8443/echo-ip # 도커 이미지 저장 / CI 작업
kubectl create deployment fs-echo-ip --image=192.168.1.10:8443/echo-ip # 쿠버네티스 디플로이먼트 배포 / CD 작업
kubectl expose deployment fs-echo-ip --type=LoadBalancer --name=fs-echo-ip-svc --port=8080 --target-port=80 # 쿠버네티스 서비스 노출 / CD 작업
저장 버튼을 누르면 그림과 같이 프로젝트 화면으로 돌아갑니다. Build Now를 눌러 저장한 프로젝트를 실행합니다.
CI/CD 작업을 수행하면 Build History에 작업이 추가됩니다.
성공적으로 CI/CD 작업이 수행돼 파란색 원이 생성됐는지 확인합니다.
여기서 빌드에 관련한 자세한 내용을 살펴보려면 Console Output 메뉴를 클릭해야 합니다.
Terminus 창으로 돌아가 쿠버네티스 클러스터에 디플로어먼트와 로드밸런서가 정상적으로 배포됐는지 확인합니다.
kubectl get deployments
kubectl get services
fs-echo-ip
와 로드밸런서 fs-echo-ip-svc
가 정상적으로 배포된 것을 확인할 수 있습니다.좀 더 확실하게 호스트 웹 브라우저를 열어 192.168.1.12:8080에 접속해 배포된 파드의 정보가 화면에 출력되는지 확인합니다.
다음 실습을 위해 젠킨스로 배포한 로드밸런서 서비스와 디플로이먼트를 삭제하겠습니다.
kubectl delete service fs-echo-ip-svc
kubectl delete deployment fs-echo-ip
왜 배포된 오브젝트를 젠킨스로 삭제하지 않나요?
젠킨스는 CI/CD를 위한 도구입니다. 만약 젠킨스를 통해 배포한 애플리케이션을 삭제한다면 삭제 과정을 추가 프로젝트로 생성해야 합니다!
따라서 이 포스팅에서는 간단하게 삭제할 수 있는 kubectl을 이용해 직접 삭제합니다.
삭제된 오브젝트는 다시 Build Now를 눌러 언제라도 다시 배포할 수 있습니다.
dpy-fs-dir-prod
프로젝트도 삭제합니다.Freestyle 프로젝트는 젠킨스의 웹 화면에 직접 셸 스크립트를 입력하기 때문에 빌드 작업의 명렁어에 변경이 있을 경우 작업 관리 및 변경 사항의 추적이 쉽지 않습니다.
그래서 이러한 내용을 파일 레벨로 관리하고 이에 대한 변경 관리를 도와주는 깃허브 레포지터리를 함께 사용한다면 어떨까요?
이와 같이 구성한다면 이력 관리 및 변경 추적, 그리고 애플리케이션 통합이 훨씬 수월하겠죠?
이렇게 파일 레벨로 CI/CD를 구성하게 도와주는 아이템이 Pipeline입니다.
젠킨스의 Pipeline은 연속적인 작업을 코드 또는 파일로 정의해주는 젠킨스 기능입니다.
Pipeline은 고유의 문법으로 작성된 코드 또는 이러한 내용을 담고 있는 파일로 이루어져 있습니다.
파이프라인 문법을 통해 젠킨스는 코드로 작성한 내용이 실제 공작하는 작업이 되는 코드로서의 파이프라인(Pipeline-As-Code)을 구현할 수 있습니다!
Freestyle은 웹 화면에서 메뉴를 눌러서 필요한 것을 정의하는 방식이기 때문에 간단한 단일 작업을 정의할 때는 유용합니다.
그러나 CI/CD는 빌드-테스트-패키징-배포 등의 여러 단계로 나누어진 작업들이 효과적으로 이루어져야 합니다.
그러나 프리스타일의 경우 화면에서 메뉴를 눌러 정의하는 방식이기 때문에 여러 사람들에게 전달하려면 사용법을 pdf 등의 책자로 만들고 교육해야 합니다.
또한 일부 내용이 변경되면 해당 책자를 다시 만들거나 변경된 내용을 교육해야 합니다. 관리가 부실하면 버전을 착각하기도 쉽겠죠?
그래서 젠킨스에서는 Pipeline을 통해서 CI/CD 내용을 코드 또는 파일로 정의해 단순히 해당 코드 또는 파일을 가져다 쓰면 모든 것이 쉽게 진행되도록 지원합니다!
편하게 Pipeline을 파이프라인으로 부르겠습니다.
젠킨스 파이프라인은 크게 두 가지의 문법으로 코드를 작성할 수 있습니다.
첫 번째는 스크립트(Scripted pipeline) 문법이고 두 번째는 선언적인(Declarative pipeline) 문법입니다.
각 문법은 일반적인 경우에는 큰 차이점이 없으나, 쿠버네티스상에서 젠킨스 에이전트를 설정할 때 스크립트 문법을 사용하면 익숙하지 않은 젠킨스 고유 문법으로 작성해야 합니다.
하지만 선언적인 문법을 사용하면 우리가 이미 익숙한 야믈을 그대로 사용할 수 있으므로,
쿠버네티스상의 젠킨스 에이전트 설정에는 선언전인 문법을 사용하는 것을 권장합니다!
따라서 파이프라인 실습에서는 선언적인 문법을 위주로 진행할 것이며, 일부 조건문에서는 표현이 다양한 스크립트 문법으로 작성하겠습니다.
간단한 배포의 경우에는 에이전트를 설정할 필요가 없기 때문에 큰 차이가 있지 않습니다.
하지만 이미 설명했던 것처럼 젠킨스 에이전트를 설정하는 경우에는 큰 차이가 발생하는데, 이는 이후 실습에서 알아보겠습니다.
실습을 진행하기 전에 파이프라인으로 어떻게 CI/CD를 구현하는지 간략히 정리해봅시다.
깃허브와 같은 소스 코드 저장소에서 Jenkinsfile을 내려받습니다.
내려받은 Jenkinsfile을 해석해서 작성자의 의도에 맞는 작업을 자동으로 수행합니다.
그러면 파이프라인으로 배포하는 과정을 실습해봅시다! 앞서 프리스타일로 배포했던 echo-ip를 얼마나 간단하게 배포할 수 있는지 확인해보시죠.
Pipeline 배포 실습을 위해 젠킨스 홈 화면으로 이동한 후에 새로운 Item 버튼을 눌러서 새로운 아이템을 생성하는 화면으로 넘어갑니다.
이번엔 Pipeline을 선택하며, 이름은 dpy-pl-bulk-prod
입니다. 모든 선택을 마쳤다면 OK 버튼을 눌러 세부 설정을 하는 메뉴로 넘어갑니다.
General 탭은 프로젝트의 일반적인 설정을 기록하는 곳입니다.
Build Triggers 탭은 빌드를 유발하기 위한 조건을 설정하는 탭입니다. 사용자의 환경에 따라 빌드에 필요한 조건이 달라질 수 있기 때문에 존재하는 설정입니다. 탭에서 제공하는 옵션을 살펴보겠습니다.
<JENKINS_URL>/job/<작업명>/build?token=<토큰 이름>
의 형식으로 URL을 호출하면 빌드 작업이 사작됩니다.Advanced Project Options 탭은 프로젝트의 고급 옵션을 설정하는 곳으로 젠킨스 플러그인 설치에 따라 생성됩니다.
Pipeline 탭에서는 젠킨스의 빌드 작업 절차를 정의할 수 있습니다.
외부 저장소에서 선언적인 문법으로 작성된 파일을 가지고 오기 위해서 다음과 같이 지정합니다.
https://github.com/iac-source/echo-ip
로 설정합니다.*/main
으로 설정합니다.Jenkinsfile(기본값)
으로 입력돼 있는 것을 확인합니다.깃허브 주소 끝에 붙는 .git에 대한 의미
깃허브의 저장소 주소를 SCM에서 사용할 때는 끝부분에 .git이 붙어 있는 경우와 없는 경우가 있습니다.
이는 순수하게 편의 목적으로 사용자가 직접 입력 시에는 .git을 제외하고 설명했으며, 깃허브 저장소에서 제공하는 주소를 복사해서 사용할 때는 .git이 포함되어 복사됩니다.
사실 주소 값에는 차이가 있지만 결과적으로는 차이 없는 결과가 나옵니다.
여기서 .git의 의미는 일반적인 확장자가 아닌 깃 작업 내용을 저장하는 공간을 의미합니다.
깃허브 저장소 경로 맨 끝에 .git에 대한 존재와 관계 없이 경로로 전달되는 모든 요청은
깃허브에서 .git 경로로 다시 전달해주기 때문에 결과적으로는 단순히 저장소 경로만으로도 SCM에 등록하고 코드를 내려받을 수 있는 것입니다.
이제 저장 버튼을 눌러서 Pipeline 프로젝트가 저장된 것을 확인합니다.
저장한 Pipeline 프로젝트를 빌드하기 전에 SCM을 통해서 깃허브 저장소에서 가지고 오는 Jenkinsfile의 소스를 발펴보고, 코드를 이용해서 어떻게 Pipeline프로젝트가 동작하는지 확인해보겠습니다.
pipeline {
agent any
stages {
# 깃허브로부터 소스 코드를 내려받는 단계, 이때 git 작업을 사용합니다. git 작업에서 인자로 요구하는 git url은 깃허브 저장소 주소를 사용하고, brach는 main을 설정했습니다.
stage('git scm update') {
steps {
git url: 'https://github.com/IaC-Source/echo-ip.git', branch: 'main'
}
}
# 도커 명령을 이용해서 컨테이너 이미지를 빌드하고, 빌드한 이미지를 레지스트리에 저장하는 작업을 수행, 이때 sh 작업을 통해 docker 명령을 사용합니다.
stage('docker build and push') {
steps {
sh '''
docker build -t 192.168.1.10:8443/echo-ip .
docker push 192.168.1.10:8443/echo-ip
'''
}
}
# kubectl 명령으로 전 단계에서 레지스트리에 저장한 이미지를 pl-bulk-prod 디플로이먼트로 배포하고, 배포한 디플로이먼트를 로드밸런서 타입으로 노출하는 작업을 수행, sh 작업을 통해 kubectl 명령을 사용합니다.
stage('deploy kubernetes') {
steps {
sh '''
kubectl create deployment pl-bulk-prod --image=192.168.1.10:8443/echo-ip
kubectl expose deployment pl-bulk-prod --type=LoadBalancer --port=8080 \
--target-port=80 --name=pl-bulk-prod-svc
'''
}
}
}
}
Jenkinsfile의 구성 요소
- pipeline
- 선언적인 문법이 시작하는 부분입니다.
- 선언적인 문법으로 작성된 작업들은 pipeline {}의 사이에 작업 내용을 작성해야 합니다.- agent
- 작업을 수행할 에이전트를 지정하고 필요한 설정을 합니다.
- 지정된 에이전트 내부에서 젠킨스 빌드 작업이 실제로 수행되는데 다음과 같은 여러 가지 방식으로 지정할 수 있습니다.
- 첫 번째는 사용 가능한 에이전트를 젠킨스가 임의로 지정하는 any
- 두 번째는 특정 레이블과 일치하는 에이전트 노드를 지정하는 label
- 세 번째는 에이전트 노드의 이미지를 도커로 지정하는 docker
- 네 번째는 에이전트 노드를 쿠버네티스 파드로 지정하는 kubernetes 와 같은 것들이 있습니다.
- 플러그인에 따라 지정할 에이전트는 무수히 많습니다.
- agent any로 사용하면 현재 설정된 에이전트가 하나만 존재하기 때문에 설정된 에이전트를 통해서 빌드 작업을 수행하게 됩니다.- stages
- stage들을 모아서 정의하고 이를 순서대로 진행하게 해 줍니다.- stage
- step들을 정의하는 영역입니다.
- stage는 괄호 안에 여러 개의 step들을 정의할 수 있는데, 이 step들 내부에서 실제로 동작하는 내용들이 정의됩니다.
- 그리고 젠킨스에서 빌드가 진행될 떄 stage별로 진행 단계를 확인할 수 있습니다.- steps
- stage 내부에서 실제 작업 내용을 작성하는 영역입니다.
- stage 내부에 여러 step이 존재할 수 있습니다.
- step 영역 내부에서 script, sh, git과 같은 작업(work)을 통해서 실제로 동작하게 됩니다.
이제 다시 젠킨스 화면으로 돌아가 Build Now를 클릭해 젠킨스의 빌드 및 배포 작업을 시작합시다! 작업이 시작되면 Build History에 #1이 추가되는 것을 확인할 수 있습니다.
각 스테이지별로 배포가 정상적으로 완료된 것을 확인했으면 실제로 쿠버네티스 클러스에 디플로이먼트와 로드밸런서가 정상적으로 배포됐는지 확인합니다.
kubectl get deployments
kubectl get services
다음 실습을 위해 배포한 서비스와 디플로이먼트를 삭제합니다.
kubectl delete service pl-bulk-prod-svc
kubectl delete deployment pl-bulk-prod
파이프라인 아이템을 이용하면 매우 간단하게 CI/CD를 코드로 정의하고 사용할 수 있는 것을 확인했습니다.
단순히 코드를 불러서 사용하는 것이 아니라 전략적으로 배포하는 것을 파이프라인 아이템을 통해 구현할 수 있었습니다.
다음 실습에선 파이프라인을 이용해 애플리케이션을 중단할 필요 없이 변경 및 업데이트하는 전략에 대해 알아봅시다!
쿠버네티스에서 애플리케이션을 배포하는 것은 어렵지 않습니다.
하지만 파드의 특성상 배포되는 애플리케이션의 변경이 있다면 언제나 삭제하고 다시 생성하는 과정을 거칩니다.
따라서 중요한 서비스가 동작하고 있는 경우 이렇게 중단되는 시간이 발생하는 것은 큰 부담일 수 있습니다.
따라서 이번에는 변경된 애플리케이션을 중단 없이 배포하는 방법인 블루그린 전략을 젠킨스상에서 구현하는 방법에 대해 알아보겠습니다.
먼저 앞서 배포한 echo-ip 애플리케이션이 젠킨스를 통해 배포되는 흐름은 다음과 같습니다.
사용자 -> 트래픽 전달(MetalLB) -> Deployment(ReplicaSet) <- 배포 <- Jenkins <- 소스 가져오기 <- Github <- 관리자(개발자)
사용자의 요청은 MetalLB 로드밸런서를 통해 트래픽이 디플로이먼트로 전달됩니다.
이러한 배포 환경에서 개발자가 새로운 기능을 개발해서 배포하면 사용자에게 어떤 영향을 끼칠까요?
정답은 '중단 없이 배포가 된다'입니다.
그 이유는 이전에 rollout 기능을 이용한 파드 업데이트 방식 덕분입니다!
이와 같이 업데이트 방법을 롤링 업데이트(Rolling-Updata)라고 하며,
이를 간단히 정리하면
파드를 레플리카셋 단위로 나누어 모든 레플리카셋에 속해 있는 파드가 업데이트 된 이후에 레플리카셋을 삭제합니다.
디플로이먼트 내에 Old ReplicaSet과 New ReplicaSet이 있는데, Old ReplicaSet에서 파드를 하나씩 업데이트하고(New ReplicaSet이 하나씩 생성되겠죠?), 모든 Old ReplicaSet의 파드가 지워진다음에 Old ReplicaSet를 삭제하는 겁니다.
이런 롤링 업데이트 배포 과정에서 내부의 파드 개수가 많으면 업데이트 과정이 길어져 다른 두 가지의 버전이 오랫동안 공존하는 경우가 있습니다.(Old와 New 버전이)
이런 상황을 방지하는 좋은 방법 중의 하나는 블루그린(Blue-green) 배포 전략을 사용하는 것입니다.
블루그린 배포 전략이란
간단히 말해서 '모든 파드가 업데이트된 이후에 트래픽을 전달하자'입니다.
2개의 디플로이먼트를 생성하고 기존에 배포된 디플로이먼트(블루)로 계속 트래픽을 전달하고 있다가,
새로 배포되는 디플로이먼트(그린)에 모든 파드가 업데이트돼 트래픽을 처리하는 데 문제가 없을 때 서비스를 모두 새로 배포된 디플로이먼트(그린)으로 넘깁니다.
그리고 기존에 디플로이먼트(블루)를 삭제합니다.
이와 같이 디플로이먼트에서 제공하는 서비스를 넘긴다면 서비스의 중단 없이 연속적으로 배포가 가능합니다.
그리고 문제가 발생한 경우 기존에 서비스하던 디플로이먼트(블루)로 원복하는 것도 수월해 장애 복구도 쉽습니다.
하지만 배포를 위한 디플로이먼트를 만들어야 하기 때문에 기존 디플로이먼트 배포 대비 최소 2배 이상의 리소스를 더 필요로 한다는 제약 사항이 있습니다.
하지만 장애 복구가 수월하다는 점과 무중단 배포가 가능하다는 장점이 더 크기 때문에 리소스 사용은 크게 부각되는 단점은 아닙니다.
쿠버네티스 환경에서 블루그린 배포는 기본 기능이 아니기 때문에 구성할 수 없지만,
젠킨스를 이용한다면 구현이 가능합니다!
이제 젠킨스를 이용해 쿠버네티스 환경에 맞는 블루그린 배포 전략을 어떻게 구현할 수 있는지 살펴봅시다.
블루그린 배포를 테스트하기 위해서 미리 구성한 대시보드 애플리케이션을 사용해 중단 없는 배포를 확인하겠습니다.
블루그린 배포를 구성하기 위해 홈 화면에 새로운 아이템을 눌러 진입합니다.
이번에 선택할 아이템은 Pipeline이며, 생설할 프로젝트 이름은 dpy-pl-blue-green
입니다. 선택을 마쳤다면 OK 버튼을 눌러 세부 설정하는 메뉴로 이동합니다.
스크롤을 내려 Pipeline-Definition에서 외부 소스 코드 저장소에서 정의된 파일을 불러와서 사용하도록 Pipeline script for SCM을 선택합니다.
SCM을 git으로 하고 Repository URL은 https://github.com/iac-source/blue-green
으로 설정한 다음 Branch Specifier를 */main
으로 설정합니다.
설정 완료 후 해당 내용을 저장 버튼을 눌러 저장합니다.
프로젝트를 빌드하기 전에 블루그린 배포를 위해서 작성한 jenkinsfile이 달라진 부분이 있습니다.
pipeline {
agent {
# 쿠버네티스의 파드를 젠킨스 작업이 수행되는 에이전트로 사용합니다. kubernetes {} 내부에서는 에이전트로 사용할 파드에 대한 명세를 야믈 형태로 정의할 수 있습니다.
kubernetes {
# 야믈 '''~''' : 젠킨스의 에이전트로 만들어지는 파드의 명세입니다. kubectl 명령어로 파드를 생성하기 위해서 사용하는 매니페스트와 동일한 형식의 야믈을 사용할 수 있습니다. 이 야믈은 블루그린 배포에 필요한 kustomize가 설치된 컨테이너(sysnet4admin/kustomize:3.6.1)를 에이전트 파드에 포함하고 있으며 호스트에 설치된 kubectl 명령어를 사용하기 위해 호스트와 연결된 볼륨, 에이전트 파드가 쿠버네티스 클러스터에 오브젝트를 배포하기 위해 사용할 서비스 어카운트인 jenkins가 미리 설정돼 있습니다.
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
app: blue-green-deploy
name: blue-green-deploy
spec:
containers:
- name: kustomize
image: sysnet4admin/kustomize:3.6.1
tty: true
volumeMounts:
- mountPath: /bin/kubectl
name: kubectl
command:
- cat
serviceAccount: jenkins
volumes:
- name: kubectl
hostPath:
path: /bin/kubectl
'''
}
}
# 깃허브로부터 대시보드 소스 코드를 내려받는 단계입니다. 이때 소스코드를 내려받기 위해 git작업을 사용합니다. url과 branch를 확인해주세요.
stages {
stage('git scm update'){
steps {
git url: 'https://github.com/IaC-Source/blue-green.git', branch: 'main'
}
}
# 실습에서는 젠킨스의 빌드 횟수마다 부여되는 번호에 따라 블루와 그린이 전환되는 것을 구현하기 위해서 젠킨스 스크립트(script)를 사용합니다. 젠킨스 빌드 번호(BUILD_NUMBER)가 홀수일 때 tag 환경변수값을 blue로 설정하고, 짝수일 때는 green으로 설정합니다. 이 단계에서 설정한 환경변수는 이후 작업에서 사용할 수 있습니다.
stage('define tag'){
steps {
script {
if(env.BUILD_NUMBER.toInteger() % 2 == 1){
env.tag = "blue"
} else {
env.tag = "green"
}
}
}
}
# 대시보드를 배포하기 위해 필요한 ConfigMap을 배포한 다음 디플로이먼트를 배포하는 단계입니다. 이 단계에선 배포 작업에 필요한 야믈 파일이 깃허브 저장소 하위 디플로이먼트 디렉터리에 위치해 있기 때문에 dir('deployment') 작업으로 디플로이먼트 디렉터리로 이동해서 작업을 수행하도록 지정합니다. 또한 디플로이먼트의 이미지, 이름, 레이블에 설정한 tag 환경 변수를 덧붙이는 것을 일일이 수정하지 않기 위해 kustomize 명령을 사용합니다. 이 kustomize 명령을 사용하기 위해서 container('kustomize') 작업으로 컨테이너 내부에서 sh 작업을 수행하도록 작성합니다.
stage('deploy configmap and deployment'){
steps {
container('kustomize'){
dir('deployment'){
sh '''
kubectl apply -f configmap.yaml
kustomize create --resources ./deployment.yaml
echo "deploy new deployment"
kustomize edit add label deploy:$tag -f
kustomize edit set namesuffix -- -$tag
kustomize edit set image sysnet4admin/dashboard:$tag
kustomize build . | kubectl apply -f -
echo "retrieve new deployment"
kubectl get deployments -o wide
'''
}
}
}
}
# 블루그린 배포 전략을 위한 디플로이먼트 배포가 끝난 후 쿠버네티스 클러스터 외부에서 들어온 요청을, 로드밸런서에서 보내줄 대상을 다시 설정하는 단계입니다. 이 단계에선 로드밸런서 설정에 필요한 야믈 파일이 깃허브 저장소 하위 service 디텍터리에 위치해 있어 dir('service')로 작업 디렉터리를 이동해 수행하도록 지정합니다. 또한 service의 셀렉터(selector) 값들을 앞서 설정한 tag 환경변수를 덧붙이는 작업도 명령으로 처리하기 위해 위와 같이 kustomize를 설정하고 사용합니다. 전 단계에서 배포한 디플로이먼트의 replicas 값과 readyReplicas의 값을 비교해 값이 같은 경우 배포가 완료됐다고 판단합니다. 그리고 로드밸런서가 트래픽을 전송하는 대상을 배포 완료된 디플로이먼트로 설정한 다음 배포 이전에 존재하는 디플로이먼트를 삭제해 배포 완료된 디플로이먼트로 트래픽을 보내줍니다.
stage('switching LB'){
steps {
container('kustomize'){
dir('service'){
sh '''
kustomize create --resources ./lb.yaml
while true;
do
export replicas=$(kubectl get deployments \
--selector=app=dashboard,deploy=$tag \
-o jsonpath --template="{.items[0].status.replicas}")
export ready=$(kubectl get deployments \
--selector=app=dashboard,deploy=$tag \
-o jsonpath --template="{.items[0].status.readyReplicas}")
echo "total replicas: $replicas, ready replicas: $ready"
if [ "$ready" -eq "$replicas" ]; then
echo "tag change and build deployment file by kustomize"
kustomize edit add label deploy:$tag -f
kustomize build . | kubectl apply -f -
echo "delete $tag deployment"
kubectl delete deployment --selector=app=dashboard,deploy!=$tag
kubectl get deployments -o wide
break
else
sleep 1
fi
done
'''
}
}
}
}
}
}
블루그린 배포 전략을 위한 깃허브 저장소의 디렉터리 구조는 다음과 같습니다.
이제 실제로 블루그린 배포 전략을 확인하기 위해 Pipeline 프로젝트 상세 화면에서 Build Now 버튼을 눌러 1차로 블루그랜 대시보드를 배포하고 배포가 완료된 것을 확인합니다.
이제 Terminus로 돌아가 대시포드 디플로이먼트와 서비스가 정상적으로 배포 및 할당됐는지 확인합니다.
--selector
옵션을 사용하겠습니다.kubectl get deployments,service --selector=app=dashboard
레이블은 무엇이고 셀렉터는 어떻게 사용하나요?
레이블은 쿠버네티스 오브젝트를 검색할 때 사용하는 메타데이터 중 하나입니다.
우리는 사실 이런 레이블을 모르는 사이에 많이 써오고 있었습니다.
서비스가 트래픽을 넘길 때 selector 아래 키와 값의 형태로 대상을 지정하는 등의 형식으로 사용되고 있었습니다.(아래 사진을 참조해주세요.)
이러한 레이블은kubectl get <오브젝트> --show-labels
명령으로 오브젝트의 레이블을 확인할 수 있습니다.
kubectl get deployment --selector=deploy=blue --show-labels
호스트 브라우저에 192.168.1.12를 입력해 배포된 파란색 대시보드를 확인합니다.
블루그린 배포는 모든 배포가 완료되는 순간 새롭게 배포힌 대시보드로 전환됩니다.
kubectl get deployments --selector=app=dashboard -w
를 우선 실행해 놓습니다.kubectl get deployments --selector=app=dashboard -w
배포 이후 완료되기 전까지 웹 브라우저에서 새로고침을 해도 여전히 파란색 대시보드가 화면에 나타나는 것을 확인할 수 잇습니다.
pl-blue
디플로이먼트가 삭제되는 것을 확인합니다.모두 배포가 완료됐다면 다시 Terminus 창으로 돌아가 대시보드 디플로이먼트와 서비스가 정상적으로 배포 및 할당됐는지 확인합니다.
kubectl get deployments,service --selector=app=dashboard
녹색 대시보드를 위한 2차 배포가 완료되고 기존의 디플로이먼트인 pl-blue가 삭제된 것이 확인됐다면 파란색 대시보드가 보이는 브라우저에서 새로고침을 눌러 변경된 녹색 대시보드를 확인합니다.
다음 실습에서 혼동을 방지하기 위해 젠킨스 홈 화면으로 이동한 후에 Pipeline으로 생성한 모든 프로젝트를 삭제하겠습니다.
현재까지 생성된 것들로 인해 다음 실습이 영향을 받을 수 있습니다.
kubectl delete deployments,service,configmap --selector=app=dashboard
이번 실습에서는 젠킨스의 아이템을 이용해 여러 종류의 프로젝트를 구성해 실제로 쿠버네티스 상에서 CI/CD가 동작하는 방식을 알아봤습니다.
젠킨스를 통한 CI/CD 아이템은 대표적으로 프리스타일과 파이프라인으로 나울 수 있으며, 상황에 맞게 이를 사용할 수 있습니다.
그리고 CI/CD 개념을 사용해서 서비스의 무장단 배포가 가능한 블루그린 배포 전략 또한 손쉽게 구현할 수 있었습니다.
기초적인 내용은 알아봤으니 젠킨스에서 제공하는 다양한 기능들을 조합해 단일 기능으로는 구현하기 힘든 GitOps를 실습해보도록 합시다!
지금까지 우리는 젠킨스를 이용해 CI/CD를 구현하는 방법을 알아봤습니다.
거의 모든 기능은 사실 젠킨스의 플러그인(Plugin)을 통해 구현된 것입니다.
예를 들면 가장 많이 쓰였던 쿠버네티스 플러그인은 CI/CD를 실제로 수행하는
젠킨스 에이전트 파드를 사용자가 신경쓰지 않아도 자동으로 배포 관리하게 해줍니다.
현업에서는 젠킨스의 단일 플러그인으로 CI/CD를 구현하는 것이 아니라
여러 플러그인을 조합해 현재 업무에 맞는 형태로 만들어서 사용합니다.
레고 조립 방식과 비슷합니다!
사용자가 필요한 기능을 주로 젠킨스 플러그인 홈페이지에서 검색해 내용을 살펴보고 이를 조합하는 방식을 취합니다.
젠킨스 플러그인 홈페이지
젠킨스에서 제공되는 플러그인은 다음과 같은 종류로 구분됩니다.
이러한 플러그인들을 조합하면 단일 플러그인으로 만들지 못하는 기능을 생성할 수 있습니다.
따라서 이번에는 쿠버네티스용 지속적 배포(Kubernetes Continuous Deploy), 슬랙 알림(Slack Notification), 변경 내용 비교(Last Changes) 총 3개의 플러그인을 조합해 젠킨스에서 GitOps를 구현해보겠습니다.
그런데 GitOps란 무엇일까요?
GitOps는 Git과 Ops(Operations, 운영)의 합성어로 깃을 통해 모든 것을 선언적으로,
깃허브 저장소와 같은 SCM에 업데이트하면 오퍼레이터(젠킨스와 같은 도구)가 변경분을 감지해 대상 시스템에 배포합니다.
이를 현재 쿠버네티스 환경에 맞춰 설명하면
배포돼야 할 매니페스트 파일을 SCM에 저장하고 매니페스트가 새로 업데이트된다면
젠킨스가 이를 파악해 쿠버네티스 클러스터에 배포하는 모든 단계를 GitOps라고 합니다.
즉, 변경 내용을 SCM에 선언해두면 이를 젠킨스가 읽어서 운영 시스템에 적용하는 것입니다.
GitOps를 이용하면 다음과 같은 이점이 있습니다.
지금까지 젠킨스를 통한 쿠버네티스 배포에서는 cluster-admin 역할을 가지고 있는 jenkins 서비스 어카운트를 사용해서 쿠버네티스 오브젝트를 배포했습니다.
이렇게 설정된 jenkins 서비스 어카운트를 통해 현재 쿠버네티스 클러스트에 모든 오브젝트를 배포하는 것은 가능하나,
외부에 있는 쿠버네티스 클러스터에는 가지고 있는 권한이 없기 때문에 배포가 진행되지 않습니다.
(즉, 젠킨스 내부의 쿠버네티스 클러스터는 문제 없지만 젠킨스 외부의 쿠너베티스 클러스터엔 접근할 수 없습니다.)
따라서 외부 클러스터에 접근하려면 쿠버설정(kubeconfig) 파일을 이용해 외부 클러스터의 API 서버로 접근한 다음 오브젝트를 배포해야 하는데,
젠킨스 에이전트 파드에서 쿠버설정 파일을 내려받아 바로 사용하는 것은 보안적으로 문제가 있습니다.
따라서 쿠버 설정 파일을 젠킨스 컨트롤러에서 관리하고 상황에 따라 필요한 권한만을 제공하는 기능이 필요한데
이를 쿠버네티스용 지속적 배포 플러그인을 사용해서 구현할 수 있습니다.
GitOps를 사용한다는 것은 단일 클러스터에서도 유용하지만 기본적으로 여러 개의 목적을 가지는 다수의 클러스터 환경을 사용하는 경우가 많으므로,
효과적인 GitOps 구현을 위한 첫 번쨰 단계로 쿠버네티스용 지속적 배포 플러그인을 설치해
어떻게 클러스터의 오브젝트 관리 권한을 가지고 오는지 확인해 보겠습니다.
그리고 GitOps의 중요한 기능 중에 하나인 변화 감지는 젠킨스의 기본 플러그인인 Poll SCM을 통해 구현하겠습니다.
Poll SCM은 변화를 감지하고 변화시에 변경된 부분을 쿠버네티스에 적용합니다.
이렇게 추가된 플러그인을 이용하면 외부 클러스터에도 배포 자동화를 구성할 수 있습니다.
현재의 테스트 환경은 다수의 크러스터 환경이 아니기 떄문에 단일 클러스터 환경에서 쿠버설정 파일을 읽어 들이는 실습을 진행해보겠습니다.
쿠버설정 파일이 담고 있는 내용에 대해서
쿠버설정 파일은 쿠버네티스 클러스터에 대한 접근 정보가 담겨 있는 파일입니다.
kubectl 명령어를 사용해서 쿠버네티스의 정보를 조회할 때 사용자 홈 디렉터리 하위의 .kube 디렉터리에 있는 config 파일을 기본적으로 이용합니다.
이 파일을 가지고 있으면 파일에 정의된 권한을 가지고 쿠버네티스 클러스터에 접근할 수 있습니다.
쿠버설정 파일은 쿠버 API 서버 접속 정보 이외에 연결에 필요한 모든 인증 정보를 가지고 있습니다.
쿠버설정 파일은kubectl config view
명령을 통해 확인할 수 있습니다.
위의 정보 중에서 보안을 위해 민감한 정보는 생략(DATA+OMITTED, REDACTED)돼 있으며,
필요시 충분한 권한을 가진 사용자는--raw
옵션을 추가해 생략된 정보를 확인할 수 있습니다.
출력된 쿠버네티스 클러스터에 접근하기 위한 접속 정보에 대한 설명은 다음과 같습니다.
- clusters
- 어떤 쿠버네티스 클러스에 접근할지에 대한 정보
- clusters는 접속 대상이 되는 클러스터의 정보를 여러 개 포함할 수 있음
- 각 클러스터 접속 정보는 API 서버의 주소와 인증 기관 정보(certificate-authority-data)로 이루어짐
- contexts
- 위의 클러스터의 정보와 곧이어 설명할 사용자 정보의 조합이 담긴 부분
- kubectl 명령어를 통해 클러스터에 접속할 때는 현재 context에 설정된 user의 자격을 가지고 설정된 cluster에 접속
- current-context
- 현재 사용중인 contxt가 무엇인지 나타내는 부분- users
- 클러스터에 접속하는 사용자가 누구인지 사용자 정보가 담겨 있는 부분
- 현재 kubernetes-admin이라는 이름을 가진 사용자 정보가 등록돼 있으며 사용자 정보는 클라이언트의 인증서 정보(client-certificate-data) 및 클라이언트의 키 정보(client-key-data)를 속성으로 가짐
- 쿠버네티스 클러스터는 위에서 설명한 cluster 속성에 담겨 있는 인증 기관 정보를 통해 검증할 수 있는 클라이언트의 인증서와 키를 이용해서 접속하는 사용자를 허용
이렇게 쿠버설정 파일은 쿠버네티스 클러스터에 접근할 수 있는 매우 민감한 정보를 포함하고 잇기 때문에 취급에 유의해야 합니다.
주기적으로 변화를 감지해야 하는 깃허브 저장소는 모두 같은 저장소를 공유할 수 없기 떄문에 실습 사용자마다 필요합니다.
저장소 생성 이후에 나타난 화면에서 복사 아이콘을 눌러 깃허브 주소를 복사합니다. 이 주소는 이후 생성한 매니페스트를 푸시하기 위해 사용되는 주소입니다.
실습에서 주로 사용하는 git 명령어
- 초기화(init) : 현재 디렉터리를 깃 작업할 수 있도록 선언해 줍니다.
- 원격(remote) : 깃허브 저장소와 같은 원격 저장소를 지정합니다.
- 추가(add) : 파일 또는 디렉터리를 깃을 통해 추적하도록 설정합니다.
- 커밋(commit) : 깃을 통해 추적하는 파일의 변경 사항을 저장합니다.
- 푸시(push) : 변경 사항이 기록된 로컬 깃의 파일들을 원격 저장소로 보냅니다.
GitOps의 내용을 저장할 디렉터리(gitops)를 m-k8s 홈 디렉터리(~)에 생성합니다. 그리고 생성한 디렉터리로 이동합니다.
mkdir ~/gitops
cd ~/gitops
gitops 디렉터리에서 git init
명령을 사용해 깃 관련 작업을 할 수 있도록 준비합니다.
.git
디렉터리가 생성되는 것을 확인할 수 있습니다.깃을 통해 원격 저장소에 파일들을 저장할 떄는 작업자 이름, 작업자 이메일 주소 등을 설정하는 게 좋습니다.
git config --global user.name "<사용자 이름>"
git config --global user.email "<사용자 이메일>"
git config --global credential.helper "store --file ~/.git-cred"
원격 저장소에 작업한 파일들을 깃허브 저장소에 업로드할 수 있도록 저장소의 주소를 추가합니다.
origin
은 사용자의 깃허브 저장소 주소에 대한 또 다른 이름(alias)입니다.git remote add origin <사용자의 깃허브 저장소 주소>
젠킨스에서 선언적으로 쿠버네티스 오브젝트를 배포하기 위해서 사전에 구성해 둔 파일들을 홈 디렉터리 밑에 gitops
디렉터리로 복사합니다.
cp ~/_Book_k8sInfra/ch5/5.5.1/* ~/gitops/
사전에 구성해 놓은 Jenkinsfile에는 쿠버네티스 배포를 위한 설정이 이미 구현돼 있습니다.
sed
명령으로 깃허브 저장소를 변경합니다.s/변경 대상/변경할 값/g
를 사용했지만 변경할 값에 /(슬래시)가 포함돼 있어 깃허브 저장소 주소로 변환되지 않습니다.sed -i 's,Git-URL,https://github.com/moey920/GitOps.git,g' Jenkinsfile
이제 git이 파일들을 추적할 수 있도록 git add .
명령으로 파일등 등록하겠습니다.
.
은 현재 디렉터리의 모든 내용을 의미합니다.git add .
추가한 내용을 커밋하기 전에 앞서 우리가 설정한 값들이 제대로 설정됐는지 확인해 보겠습니다.
git config --list
추가한 파일들을 푸시하기 위해서 git commit 명령으로 변경 사항을 저장하겠습니다.
-m(message)
옵션은 푸시하는 내용 또는 의미를 깃허브 저장소에 남겨 추후에 내용을 파악하기 위한 목적으로 사용됩니다.init commit
은 최초 커밋을 의미합니다.깃허브 저장소로 푸시하기 위해서 업로드 되는 브랜치는 git branch
명령으로 설정해야 합니다.
-M(Move)
은 브랜치 이름을 바꾸는 옵션이고 main
은 깃허브의 기본 브랜치 이름입니다.git branch -M main
git branch
로 브랜치 목록을 검색하면 master가 있는 것이 보이죠? 이전엔 깃허브 저장소의 메인 브랜치의 default가 master
브랜치였습니다. 최근엔 메인 브랜치를 main
으로 쓰는 것이 정석입니다.브랜치를 설정했다면 이제 gitops 디렉터리에 있는 파일들을 git push
명령으로 깃허브 저장소에 푸시하겠습니다.
origin
은 깃허브 저장소의 주소를 의미하며 이에 대한 옵션엔 -u(--set-upstream)
은 로컬 브랜치에서 원격 저장소로 업로드 하는 것을 의미합니다. 그리고 업로드된 브랜치를 이후 git pull
작업에서도 원격 저장소로 사용합니다.사용자 이름과 비밀번호(또는 토큰)
을 입력하면 푸시 작업이 진행됩니다.github 토큰 발급받기
1. 깃허브 세팅에 들어갑니다.
2. 왼쪽 메뉴바에서 Developer setting을 클릭합니다.
3. Personal access tokens 탭에서 Generate New Token을 클릭합니다.
4. 로그인합니다.
5. 토큰 만료기한과 토큰 권한을 설정합니다. 저는 편의를 위해 올해 말까지, 모든 권한을 설정했지만 실무에서 사용할 때는 적당한 기한과 필요한 권한만 부여하도록 합시다.
6. 하단의 Generate Token을 클릭하면 토큰이 생성됩니다. 복사해서 어딘가에 저장해두고 사용하도록 합시다. 또한 토큰이 노출되지 않도록 주의해야하며, 활용할 땐 하드코딩하지 말고 환경변수로 사용하길 권장드립니다.(노출문제로 스크린샷은 생략합니다.)
깃허브 저장소로 첫 푸시가 완료된 후에, 깃허브 저장소에 파일들이 정상적으로 푸시됐는지 확인합니다.
쿠버설정 파일을 안전하게 관리하기 위해서 쿠버네티스용 지속적 배포 플러그인을 설치하겠습니다.
kubernetes
를 검색 필드에 입력하면 Kubernetes Continuous Deploy
가 나타납니다. 해당 플러그인을 체크하고 지금 다운로드하고 재시작 후 설치하기를 누릅니다.설치가 끝나고 실행중인 작업이 없으면 Jenkins 재시작을 체크해 작업이 끝나면 젠킨스를 리로드합니다.
다시 젠킨스에 로그인해서 설치된 쿠버네티스용 지속적 배포 플러그인에 대한 설정을 진행하겠습니다.
쿠버네티스용 지속적 배포 플러그인이 사용할 새로운 자격 증명 정보를 추가하기 위해 (global) 버튼을 누릅니다.
쿠버설정 파일에 대한 자격 증명을 가져오려면 현재 쿠버설정 파일이 있는 마스터노드에 접속 권한이 있어야 합니다.
다음과 같이 설정하고 OK 버튼을 눌러서 설정한 자격 증명을 저장합니다.
마스터 노드의 자격 증명이 등록된 것을 확인하고, Add Credentials를 눌러 쿠버설정 파일에 대한 자격 증명을 추가합니다.
쿠버네티스 접속 자격 증명 설정을 다음과 같이 입력하고 OK를 누릅니다.
쿠버네티스 접속 자격 증명이 kubeconfig 라는 이름으로 등록된 것을 확인합니다.
선언적인 배포 환경을 위한 젠킨스 세부 구성이 완료됐으므로 젠킨스 홈 화면으로 이동한 후에 새로운 Item을 눌러 본격적으로 선언적인 배포 환경을 위한 프로젝트를 설정하겠습니다.
선언적인 배포 환경을 위한 프로젝트 설정을 위해 Pipeline 아이템을 선택하고 dpy-pl-gitops
를 이름으로 입력하고 OK버튼을 눌러 설정 단계로 넘어갑니다.
깃허브 저장소의 변경 내용을 감시하기 위해 Pipeline 프로젝트에서 지원하는 기능인 Poll SCM
을 사용해 주기적으로 깃허브 저장소의 변경을 인식하게 합니다.
*/10 * * * *
로 입력합니다.젠킨스 크론 표현식에서 사용하는 H는 어떤 의미를 갖나요?
젠킨스에서* * * * *
와 같은 크론 표현식을 작성하면H/* * * * *
와 같은 크론 표현식으로 사용하는 것이 좋다는 메세지가 나타납니다.
젠킨스가 권장하는 H를 사용해 크론 표현식을 작성하면 기본적으로는 1분에 한 번씩 실행되지만, 부하가 없는 시점에 실행돼 정확한 시점을 보장할 수 없습니다.
따라서 현재 실습에서는 정확한 시점에 빌드되는 것을 확인하기 위해 일반적인 크론 표현식을 작성했습니다.
Pipeline 프로젝트에서 사용할 소스 저장소를 구성합니다. 그리고 저장을 클릭합니다.
Pipeline script from SCM
으로 설정하고 SCM은 git으로 설정합니다.*/main
으로 설정해 메인 브랜치의 변경만 확인합니다.dpy-pl-gitops 프로젝트를 저장하고 나서 약 10분을 기다리면 Build History 항목에서 첫번째(#1) 배포가 진행되는 것을 확인할 수 있습니다.
배포 작업이 완료된 것을 확인합니다.(파란색 원)
깃허브 저장소에 푸시한 야믈 파일이 쿠버네티스 클러스터에 적용이 되어있는지 확인해보겠습니다.
gitops-nginx
디플로이먼트가 배포되고 파드가 2개 준비되어 있습니다.kubectl get deployments
선언적인 배포 환경을 테스트하기 위해 야믈 파일을 변경하고 깃허브 저장소에 푸시하면 쿠버네티스 클러스터에 이미 배포돼 있는 디플로이먼트도 변경되는지 확인해봅시다.
sed -i 's/replicas: 2/replicas: 5/' deployment.yaml
변경한 야믈 파일을 git에 추가하고 커밋한 후에 변경 내용을 저장소로 푸시하겠습니다. 이 작업들을 연결해 한 번에 실행하기 위해 ;(세미콜론)을 사용합니다.
git add . ; git commit -m "change replicas count" ; git push -u origin main
파일 변경 이후 푸시가 정상적으로 진행됐는지 저장소에서 확인해봅시다. 다음과 같이 커밋 메세지가 변경된 것을 확인할 수 있습니다.
푸시 이후 10분째가 되면 젠킨스가 깃허브 저장소의 변경 사항을 인지해 배포하고 다음 그림과 같이 배포가 완료된 것을 확인할 수 있습니다.
#2 배포가 완료됐다면 현재 쿠버네티스 클러스터의 replicas 개수를 확인해 변경 사항이 배포됐는지 확인합니다.
gitops에서 가장 중요한 개념인 선언적인 배포 환경을 쿠버네티스용 지속적 배포 플러그인과 젠킨스 Poll SCM 플러그인을 사용해 구현해봤습니다.
GitOps는 선언적인 배포 이외에 관리하고 있는 환경에서 이루어지는 다양한 변화에 대한 알림이 필요합니다.
따라서 가장 안정적으로 동작하는 협업 플랫폼인 슬랙(Slack)을 이용해서 젠킨스 환경에서 일어나는 변화를 감지하고 알려주는 기능을 구현해 보겠습니다.
보다 안정적인 GitOps 환경을 위해서는 구축한 시스템에 대한 알림 메세지 등을 받아 즉각 확인하고 대처할 필요가 있습니다.
이러한 환경을 구현하기 위해서 젠킨스는 협업 플랫폼과 기능을 연결할 수 있습니다.
많은 종류의 협업 플랫폼을 연결할 수 있으나, 이번에는 가장 대중적인 협업 도구인 슬랙을 사용해 알림 메세지를 받아보겠습니다.
슬랙에 대한 어느정도 이해가 있다고 가정하고 진행하겠습니다.
간단히 젠킨스와 슬랙을 연동하는 단계를 정리하면 다음과 같습니다.
젠킨스가 슬랙으로 메세지를 보낼 수 있는 대상인 슬랙 채널을 생성합니다.
슬랙 채널에서 젠킨스가 보내는 메세지를 전달할 수 있는 Jenkins CI 앱을 추가해 젠킨스에서 슬랙 채널 연동을 위한 토큰과 워크스페이스 도메인 주소 값을 확인합니다.
슬랙에서 발급한 토큰은 공개되면 매우 민감한 정보이므로 젠킨스 자격 증명에 토큰을 등록합니다.
젠킨스에서 슬랙으로 메세지를 보내기 위해서 슬랙 알림 플러그인을 설치하고 시스템 설정 메뉴에서 토큰과 워크스페이스 도메인 주소를 입력해 연동 작업을 마칩니다.
그러면 이제 젠킨스에서 슬랙으로 배포의 시작과 종료를 메세지로 보내는 기능을 설정해봅시다!
젠킨스에서 슬랙으로 메세지를 보내기 위해선 여러 가지 설정이 필요합니다.
좌측 상단에 위치한 채널 추가 버튼을 누르면 채널을 관리할 수 있는 메뉴가 나타납니다. 나타만 메뉴에서 새 채널 생성을 누릅니다.
새 채널 생성 버튼을 누르면 채널 생성에 필요한 정보를 입력하는 대화상자가 나타납니다.
생성 버튼을 누르면 다음과 같이 사용자를 추가할 것인지를 물어보는 대화상자가 나타납니다. 필요한 사용자를 추가하고 완료됨 버튼을 누릅니다.
알림을 수신 받는 채널을 구성했다면 상단의 워크스페이스 메뉴를 눌러 슬랙과 젠킨스를 연동할 앱을 설치하기 위해 설정 및 관리의 하위 메뉴인 앱 관리를 선택합니다.
슬랙 앱 관리 화면으로 이동했습니다. 이 화면에서는 필요한 앱을 검색해 슬랙에 추가 기능을 부여할 수 있고 삭제할 수도 있습니다.
앱 관리 화면에서 젠킨스와 슬랙 연동을 위해 Jenkins CI
이름을 검색하고 설치합니다.
Jenkins CI를 슬랙에 추가합니다.
Jenkins CI 앱에서 알림을 받을 채널을 아까 생성한 채널로 선택합니다. 또한 Jenkins CI 통합 앱 추가 버튼을 눌러 슬랙에 Jenkins CI 앱을 추가합니다.
새 통합 앱이 추가됐다는 메세지를 확인하고 Jenkins Ci와 슬랙을 통합하기 위한 연동 설정 지침이 표시된 것을 확인합니다.
연동 설정 지침 중에 필요한 정보가 있는 3단계 내용을 확인하기 위해 스크롤을 내려봅시다.
통합 토큰 자격 증명 ID를 젠킨스 자격 증명 키로 등록하기 위해 젠킨스 홈 화면 > 젠킨스 관리 > Manage Credentials > (global) 을 눌러 자격 증명 화면으로 이동합니다.
슬랙으로 메세지를 보내기 위한 자격 증명 키를 등록합니다.
다음과 같이 입력하고 OK 버튼을 누릅니다.
슬랙 자격 증명을 위한 암호키가 등록된 것을 확인합니다.
등록한 슬랙 자격 증명 암호키를 사용해서 젠킨스로부터 슬랙에 메세지를 보내려면 이를 도와주는 플로그인을 설치해야 합니다.
Slack Notification
을 선택하고 지금 다운로드하고 재시작 후 설치하기 버튼을 누릅니다.설치가 끝나고 실행 중인 작업이 없으면 젠킨스 재시작 체크박스를 체크해 리로드합니다, 잠시 후 새로고침을 눌러 재로그인을 진행합니다.
슬랙 알림 플러그인을 설치하게 되면, 젠킨스와 슬랙 연동 정보를 입력할 수 있습니다.
jenkins-deploy-notice
혹은 각자 사용할 채널명을 입력합니다.설정한 내용들을 통해 젠킨스로부터 슬랙으로 메세지가 정상적으로 발송되는지 확인해봅시다.
cp ~/_Book_k8sInfra/ch5/5.5.2/Jenkinsfile ~/gitops/
pipeline {
agent any
stages {
stage('deploy start') {
steps { //+추가됨: 배포 작업 이전에 배포 시작 알림 메세지를 슬랙 채널로 보냄
slackSend(message: "Deploy ${env.BUILD_NUMBER} Started"
, color: 'good', tokenCredentialId: 'slack-key')
}
}
stage('git pull') {
steps {
// Git-URL will replace by sed command before RUN
git url: 'Git-URL', branch: 'main'
}
}
stage('k8s deploy'){
steps {
kubernetesDeploy(kubeconfigId: 'kubeconfig',
configs: '*.yaml')
}
}
stage('deploy end') {
steps { //+추가됨: 배포 작업 이후에 배포 완료 알림 메세지를 슬랙 채널로 보냄
slackSend(message: """${env.JOB_NAME} #${env.BUILD_NUMBER} End
""", color: 'good', tokenCredentialId: 'slack-key')
}
}
}
}
Jenkinsfile에 사용자의 깃허브 저장소를 입력하기 위해, 현재 변수(Git-URL)로 처리돼 잇는 부분을 sed 명령으로 변경합니다.
sed -i 's,Git-URL,<사용자의 깃허브 저장소 주소>,g' Jenkinsfile
필요한 내용을 변경한 Jenkinsfile을 git에 추가하고 커밋한 후에 푸시합니다.
git add . ; git commit -m "add slack notification" ; git push -u origin main
푸시가 정상적으로 수행됐는지 저장소를 확인해봅니다.
푸시한 후 10분째가 되면 젠킨스가 깃허브 저장소의 변경 사항을 인지해 배포 작업을 시작하고 완료한 것을 확인합니다.
프로젝트 #3 배포가 완료된 후에, 슬랙으로 이동해서 설정한 채널에 배포 시작과 종료에 관한 메세지가 왔는지 확인합니다.
젠킨스에서 발생하는 배포들에 대해서 슬랙을 연동해 메세지로 받으면 보다 빠르게 상태를 확인할 수 있고,
이에 따라 필요한 조치도 신속하게 취할 수 있습니다.
그런데 배포가 이루어지는 것을 확인은 했지만 어떤 내용이 변경돼 배포했는지 궁금하지 않으신가요?
다음으로 어떤 내용이 변경됐는지 확인하는 기능을 추가해 더욱 더 안정적인 GitOps 환경을 구축해보겠습니다!
변경된 부분을 직접 깃허브 저장소에서 확인할 수도 있지만, 이미 잘 만들어진 플러그인인 Last Changes를 활용해 변경된 내용을 슬랙을 통해 빠르게 확인해봅시다!
개발의 매력은 효율을 높히는 거니까요!
슬랙에서 코드 변경을 확인하기 위해 플러그인을 추가하겠습니다.
플러그인 설치 완료 후에 변경 내용 비교를 위한 구문이 추가된 Jenkinsfile을 가져와 현재 덮어쓰겠습니다.
cp ~/_Book_k8sInfra/ch5/5.5.3/Jenkinsfile ~/gitops/
pipeline {
agent any
stages {
stage('Deploy start') {
steps {
slackSend(message: "Deploy ${env.BUILD_NUMBER} Started"
, color: 'good', tokenCredentialId: 'slack-key')
}
}
stage('git pull') {
steps {
// Git-URL will replace by sed command before RUN
git url: 'Git-URL', branch: 'main'
}
}
stage('k8s deploy'){
steps {
kubernetesDeploy(kubeconfigId: 'kubeconfig',
configs: '*.yaml')
}
}
stage('send diff') {
steps {
script { //+추가됨: 이전 배포와 현재 배포의 코드 변동 사항을 html로 만듦
def publisher = LastChanges.getLastChangesPublisher "PREVIOUS_REVISION", "SIDE", "LINE", true, true, "", "", "", "", ""
publisher.publishLastChanges()
def htmlDiff = publisher.getHtmlDiff()
writeFile file: "deploy-diff-${env.BUILD_NUMBER}.html", text: htmlDiff
} //+추가됨: 변경 사항을 한눈에 확인할 수 있는 주소를 메세지로 전달
slackSend(message: """${env.JOB_NAME} #${env.BUILD_NUMBER} End
(<${env.BUILD_URL}/last-changes|Check Last changed>)"""
, color: 'good', tokenCredentialId: 'slack-key')
}
}
}
}
Jenkinsfile에 사용자 깃허브 저장소를 입력하기 위해 변수로 처리돼있는 부분을 sed 명령으로 변경합니다.
sed -i 's,Git-URL,<사용자의 깃허브 저장소 주소>,g' Jenkinsfile
필요한 내용을 변경한 Jenkinsfile을 git에 추가하고 커밋한 후에 푸시합니다.
git add . ; git commit -m "add last changes" ; git push -u origin main
푸시가 정상적으로 수행됐는지 깃허브 저장소를 확인합니다.
푸시한 후 10분째가 되면 젠킨스가 깃허브 저장소의 변경 사항을 인지하고 배포 작업을 진행합니다. 완료된 것을 확인합니다.
배포 작업 완료 후 슬랙 채널에 배포의 시작과 종료에 관한 메세지가 도착한 것을 확인합니다.
다음과 같이 코드에 대한 변경 내용을 확인할 수 있는 페이지로 이동시켜 줍니다.
젠킨스 실습을 위해 지금까지 사용했던 오브젝트 중 디플로이먼트만 삭제하겠습니다. CI/CD를 다루는 젠킨스는 쿠버네티스 인프라를 이루는 주요한 요소이므로 삭제하지 않겠습니다.
kubectl delete deployment gitops-nginx
cd ~
이버 포스팅에서는 쿠버네티스 환경에서 젠킨스를 손쉽게 설치할 수 있는 도구인 커스터마이즈와 헬름에 대해 알아봤으며, 이를 통해 젠킨스를 설치했습니다.
그리고 CI/CD를 젠킨스의 대표적인 아이템인 Freestyle과 Pipeline을 통해 구현해 봤으며, 단순히 CI/CD만을 구현하는 것이 아니라 다양한 플러그인을 사용해 GitOps의 기능 또한 구현해 봤습니다.
지금까지 우리는 쿠버네티스 환경을 구축하는 것에 중점을 두었습니다.
구축이 끝났다면 이러한 환경을 상용으로 넘기게 되는데, 이때 가장 중요한 부분 가운데 하나가 시스템을 관측하는 모니터링입니다!
다음 포스팅에선 지금까지 구축하고 사용한 쿠버네티스 환경을 어떻게 하면 효과적으로 모니터링할 수 있을지 알아보겠습니다.
지금까지 고생하셨습니다 :)
본 게시물은 "컨테이너 인프라 환경 구축을 위한 쿠버네티스/도커 - 조훈,심근우,문성주 지음(2021)" 기반으로 작성되었습니다.