Jenkins란?
Jenkins는 가장 널리 사용되는 CI / CD 오픈소스 자동화 도구이다.
Master와 Agent로 나뉘어져 있으며
Master는 구성 및 설정들을 저장하고 빌드를 관리한다.
Agent는 실제로 빌드를 수행하는 노드이며 빌드 결과를 Master에게 전송한다.
빌드 구성을 위한 여러가지 플러그인이 많고 관련 문서들도 많이 있다는 장점이 있다.
Jenkins를 왜 도입하는가?
CI / CD 툴의 장점으로는 테스트 및 배포를 자동으로 할 수 있다는 것이다.
우선 진행하고 있는 프로젝트에서는 총 4개의 서비스가 있어 각각이 jar 파일로 빌드가 되어야 한다.
매번 명령어를 통해 수동으로 jar 파일을 뽑아내고 도커 이미지를 빌드하고 배포하려면 관리상의 불편한점이 많다.
따라서 우리는 프로젝트에 Jenkins를 도입하기로 했다.
다만, CI 환경까지만 구축하고 CD 환경은 ArgoCD라는 다른 툴을 사용할 예정이다.
Jenkins 설치 with Helm
Jenkins를 설치하는 방법은 바이너리, 도커 이미지, YAML 파일 등 여러가지가 있는데
현재 프로젝트는 AWS EKS를 사용하여 필요한 모든 패키지를 Helm으로 관리하고 있다.
따라서 이 문서는 AWS EKS 상에 Jenkins를 Helm으로 설치하는것을 기준으로 진행된다.
Helm이 아닌 다른 방법이 궁금한 사람은 공식 문서를 참고하자.
https://www.jenkins.io/doc/book/installing/kubernetes/#install-jenkins-with-yaml-files
Kubernetes
When you host Jenkins on Kubernetes for production workloads, you need to consider setting up a highly available persistent volume, to avoid data loss during pod or node deletion. A pod or node deletion could happen anytime in Kubernetes environments. It c
www.jenkins.io
Jenkins는 stateful하다
Jenkins는 하나의 서비스로 계정, 설정 정보, Credential, Pipeline Script 등을 저장하고 있어야 한다.
우리는 k8s 환경에 Jenkins를 설치할 것이므로 이러한 정보들을 저장하기 위해 별도의 Volume이 필요하다.
AWS EBS CSI Driver
AWS EBS CSI Driver는 EKS의 클러스터 addon으로 존재하며 위의 Volume을 구성하기 위해 필요한 요소이다.
EBS란 Elastic Block Store의 약자로, EC2 인스턴스의 볼륨으로 사용될 수 있는 스토리지이다.
CSI Driver는 Container Storage Interface를 지원해주는 드라이버이며
EKS 내에서 동적으로 볼륨을 프로비저닝 할 수 있게 도와주어 미리 PV를 만드는 수고를 덜어준다.
더 자세한 설명과 설치 과정은 공식 문서를 참고하자.
https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/ebs-csi.html
Amazon EBS CSI 드라이버 - Amazon EKS
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
AWS EBS CSI Driver with Terraform
EBS CSI Driver는 공식 문서에 따르면 콘솔창에서 IAM Role을 생성하고 addon 추가만 하면 설치가 끝난다.
하지만 현재 인프라 구성을 Terraform으로 진행하고 있어서, 코드로 설치를 해야했다.
다행히 IAM Role은 Terraform 진영의 공식 AWS 모듈에 존재했었고 다음과 같이 모듈을 설치할 수 있다.
module "ebs_csi_irsa_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
role_name = "AmazonEKS_EBS_CSI_DriverRole"
attach_ebs_csi_policy = true
oidc_providers = {
ex = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"]
}
}
tags = var.tags
}
IAM Role을 추가한 다음 미리 구성해둔 EKS 모듈에도 다음과 같이 addon을 추가해 주었다.
module "eks" {
source = "../../modules/eks"
vpc_id = module.vpc.vpc_id
cluster_name = var.cluster_name
cluster_version = var.cluster_version
cluster_endpoint_public_access = var.cluster_endpoint_public_access
# 추가한 부분
cluster_addons = merge(var.cluster_addons, {
aws-ebs-csi-driver = {
most_recent = true
service_account_role_arn = module.ebs_csi_irsa_role.iam_role_arn
}
})
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = var.eks_managed_node_groups
tags = var.tags
}
여기서 좀 애를 먹었었는데, 공식 문서에 issue와 Readme를 아무리 뒤져보아도
cluster addon의 예시로 ebs-csi-driver에 대한 예시가 없었다.
처음에는 Role에 대한 arn을 넣어주지 않아
자동으로 노드에 할당된 Role을 가져와서 PV가 제대로 프로비저닝 되지 않았었다.
하지만 stackoverflow는 신이다.
이후 EKS 클러스터에 ebs-csi-controller Service Account가 생긴것을 볼 수 있다.
AWS 콘솔창에서도 확인해보자.
Role이 자동으로 생성됐으며, ebs csi controller가 sts를 통한 자격 증명으로 Assume Role을 할 수 있게 설정되어있다.
Helm install에 필요한 사전 작업
포스트 초반에 링크했던 공식 문서에 따르면
Helm install을 하기 전에 PV와 SA를 미리 만들어 두어야 한다고 되어 있다.
하지만 도큐먼트는 로컬 환경을 기준으로 설명하고 있어 EKS에서 사용하려면 적절한 변형이 필요하다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
namespace: jenkins
spec:
storageClassName: jenkins-pv
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: jenkins-pv
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
우리는 사전에 EBS CSI Driver를 설치했으므로 pvc와 storage class만 준비하면 된다.
다음으로, 구성해야 하는 SA는 다음과 같다.
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: jenkins
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: jenkins
rules:
- apiGroups:
- '*'
resources:
- statefulsets
- services
- replicationcontrollers
- replicasets
- podtemplates
- podsecuritypolicies
- pods
- pods/log
- pods/exec
- podpreset
- poddisruptionbudget
- persistentvolumes
- persistentvolumeclaims
- jobs
- endpoints
- deployments
- deployments/scale
- daemonsets
- cronjobs
- configmaps
- namespaces
- events
- secrets
verbs:
- create
- get
- watch
- delete
- list
- patch
- update
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:serviceaccounts:jenkins
RBAC 기반으로 Cluster Role을 생성한 다음 생성하는 SA에 Binding 해준다.
이 두 YAML 파일을 apply 해주면 Helm install 사전작업은 끝난다.
Helm install
사전 작업을 끝낸다음, 우리의 환경에 맞게 value.yaml 파일을 수정해야 한다.
value.yaml 파일은 아래 링크에서 확인할 수 있다.
https://raw.githubusercontent.com/jenkinsci/helm-charts/main/charts/jenkins/values.yaml
Service 수정
우선 우리는 관리 환경과 애플리케이션 환경을 나누기 위해, ELB를 각각 하나씩 두기로 했다.
따라서 ELB를 Ingress Controller로 두고, 젠킨스로 라우팅할 Ingress를 만들어야 했다.
관리 환경의 ELB에는 나중에 ArgoCD도 라우팅할 예정이다.
Ingress가 트래픽을 전달할 서비스를 정의하자.
Jenkins는 내장 웹 서버 포트를 8080을 사용하고 있으므로 편의를 위해 controller.servicePort를 80으로 수정한다.
또한 controller.serviceType을 Cluster IP로 수정한다.
...
# Container securityContext
containerSecurityContext:
runAsUser: 1000
runAsGroup: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
servicePort: 80 #### 80으로 수정
targetPort: 8080
# For minikube, set this to NodePort, elsewhere use LoadBalancer
# Use ClusterIP if your setup includes ingress controller
serviceType: ClusterIP #### ClusterIP로 수정
# Jenkins controller service annotations
...
PVC와 SA 수정
PVC와 SA는 사전작업에서 미리 만들어 두었기에, 새로 생성하지 않고 기존 리소스를 사용하겠다고 수정해야 한다.
persistence:
enabled: true
## A manually managed Persistent Volume and Claim
## Requires persistence.enabled: true
## If defined, PVC must be created manually before volume will be bound
existingClaim: jenkins-pvc #### 기존에 만들어 두었던 pvc
## jenkins data Persistent Volume Storage Class
## If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
##
storageClass: jenkins-pv #### 기존에 만들어 두었던 sc
serviceAccount:
create: false #### false로 수정
# The name of the service account is autogenerated by default
name: jenkins #### jenkins 추가
annotations: {}
extraLabels: {}
imagePullSecretName:
Ingress 생성
target으로 위에서 만들 jenkins svc를 연결해주었다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jenkins-alb
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
namespace: jenkins
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jenkins
port:
number: 80
최종 배포
위에서 정의한 Ingress를 생성한 다음, Jenkins만을 위한 Namespace를 만들어 주었다.
만약 Helm repo에 jenkins가 추가가 되어 있지 안다면 다음과 같은 명령어로 추가하자.
helm repo add jenkinsci https://charts.jenkins.io
helm repo update
그런 다음 install을 진행했다.
helm install jenkins -n jenkins -f jenkins-values.yaml jenkinsci/jenkins
admin password 접근 방식이 나오며 jenkins Pod가 생성되고, PV가 동적으로 프로비저닝 됐다.
ELB 도메인으로 접근한 다음, 로그인을 끝내면 다음과 같이 홈 화면이 나온다.
다음 내용은 아래 포스팅에 이어서 적어놓았다.
https://imsongkk.tistory.com/76
AWS EKS Jenkins 환경 구축하기 [2/2]
하나의 포스트로 담기에는 분량이 너무 많을 것 같아 2개로 나누었다. 이전 포스트에 이어 진행된다. https://imsongkk.tistory.com/75 AWS EKS Jenkins 환경 구축하기 [1/2] Jenkins란? Jenkins는 가장 널리 사용되
imsongkk.tistory.com
'Trouble Shooting' 카테고리의 다른 글
ALB Ingress Controller로 여러 개의 Ingress 처리하기 (0) | 2023.08.11 |
---|---|
MSA 환경에서 Swagger 서버 구축하기 (0) | 2023.08.05 |
AWS EKS Jenkins 환경 구축하기 [2/2] (0) | 2023.07.31 |
AWS EKS 클러스터 구성 및 배포하기 [2/2] (1) | 2023.07.02 |
AWS EKS 클러스터 구성 및 배포하기 [1/2] (0) | 2023.07.01 |