본문 바로가기

Docker & Kubernetes

쿠버네티스(Kubernetes) - 헬름으로 배포 간편화와 젠킨스 설치까지

헬름(helm)

헬름을 통한 배포는 커스터마이즈에서 제한적이었던 주소 할당 영역과 같은 값을 대체하면서 간단하게 설치할 수 있도록 설계되어있다.

 

헬름은 쿠버네티스에 패키지를 손쉽게 배포할 수 있도록 패키지를 관리하는 쿠버네티스 전용 패키지 매니저이다.

- 패키지: 실행 파일, 실행 환경에 필요한 의존성 파일과 환경 정보들의 묶음

- 패키지 매니저: 다양한 목적으로 사용되지만, 가장 중요한 목적은 설치에 필요한 의존성 파일들을 관리하고 간편하게 설치할 수 있도록 도움을 줌

 

헬름은 차트(chart)라는 것을 사용하는데, 요구 조건별로 리소스를 편집하거나 변수를 넘겨서 처리하는 패키지내용을 처리해준다. 또한 차트는 헬름 저장소에 공개하여 여러 사용자들과 공유할 수 있다. (즉, 동료들과 동일한 환경 구성이 가능하다는 의미)

(Docker 컨테이너와 원리가 비슷한듯..)

 

헬름의 기본저장소는 아티팩트허브(artifacthub.io)이다.

헬름으로 배포 간편화

- 커스터마이즈(kustomize)로 배포 간편화 예제 확인: https://xggames.tistory.com/95

 

= helm-install.sh

 

최신버전 & 공식홈 제공 스크립트: https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3

 

# 스크립트로 헬름설치
[root@m-k8s-ys vagrant]# ./helm-install.sh 
Downloading https://get.helm.sh/helm-v3.8.1-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm

 

예제에서는 차트저장소에서 metallb로 검색하여 확인한 차트를 사용한다.

 

# 헬름 차트 저장소 추가
[root@m-k8s-ys vagrant]# helm repo add edu https://iac-source.github.io/helm-charts
"edu" has been added to your repositories

# 등록된 차트 저장소 목록 확인
[root@m-k8s-ys vagrant]# helm repo list
NAME	URL                                     
edu 	https://iac-source.github.io/helm-charts

# 변경된 정보를 캐시에 업데이트(로컬에서는 차트 정보를 캐시를 한다)
[root@m-k8s-ys vagrant]# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "edu" chart repository
Update Complete. ⎈Happy Helming!⎈

# metallb 차트 설치
[root@m-k8s-ys vagrant]# helm install metallb edu/metallb \
> --namespace=metallb-system \
> --create-namespace \
> --set controller.tag=v0.8.3 \
> --set speaker.tag=v0.8.3 \
> --set configmap.ipRange=192.168.56.11-192.169.56.29

NAME: metallb
LAST DEPLOYED: Fri Apr  8 15:08:40 2022
NAMESPACE: metallb-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
MetalLB load-balancer is successfully installed.
1. IP Address range 192.168.56.11-192.169.56.29 is available.
2. You can create a LoadBalancer service with following command below.
kubectl expose deployment [deployment-name] --type=LoadBalancer --name=[LoadBalancer-name] --port=[external port]

 

헬름에서는 가변적인 인자를 사용하기 위해서 --set ~~를 사용하였다. 가변 인자는 차트의 values.yaml파일에서 확인이 가능하며, artifacthub.io에서는 아래의 Default values를 확인하면 된다. (Default values의 내용들은 set으로 변경하지 않았을 경우에 설정되는 기본값들이다)

 

 

# 파드 및 컨피그맵 배포 확인
[root@m-k8s-ys vagrant]# kubectl get pods -n metallb-system
NAME                          READY   STATUS    RESTARTS   AGE
controller-85478cc585-phzp5   1/1     Running   0          5m17s
speaker-jqhgg                 1/1     Running   0          5m17s
speaker-nqhr2                 1/1     Running   0          5m17s
speaker-twls4                 1/1     Running   0          5m17s
speaker-vhh68                 1/1     Running   0          5m17s
speaker-xrhdl                 1/1     Running   0          5m17s

[root@m-k8s-ys vagrant]# kubectl get configmap -n metallb-system
NAME     DATA   AGE
config   1      5m25s

# 인자전달로 metallb 버전이 잘 반영되었는지 확인
[root@m-k8s-ys vagrant]# kubectl describe pods -n metallb-system | grep Image:
    Image:         metallb/controller:v0.8.3
    Image:         metallb/speaker:v0.8.3
    Image:         metallb/speaker:v0.8.3
    Image:         metallb/speaker:v0.8.3
    Image:         metallb/speaker:v0.8.3
    Image:         metallb/speaker:v0.8.3

# 디플로이먼트 및 LB 오브젝트 생성후 테스트
[root@m-k8s-ys vagrant]# kubectl create deployment echo-ip --image=sysnet4admin/echo-ip
deployment.apps/echo-ip created

[root@m-k8s-ys vagrant]# kubectl expose deployment echo-ip --type=LoadBalancer --port=80
service/echo-ip exposed

[root@m-k8s-ys vagrant]# kubectl get service echo-ip
NAME      TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
echo-ip   LoadBalancer   10.110.150.125   192.168.56.11   80:31859/TCP   5s

 

# 실습 완료후 파드 삭제
[root@m-k8s-ys vagrant]# kubectl delete service echo-ip
service "echo-ip" deleted

[root@m-k8s-ys vagrant]# kubectl delete deployment echo-ip
deployment.apps "echo-ip" deleted

젠킨스 설치 및 설정하기

이전 docker사용하기에서 정리했던 도커 컨테이너 이미지 사설 레지스트리를 가져와서 사용한다. 젠킨스는 파드에서 동작하는 애플리케이션이기때문에, PV를 마운트하지 않으면 파드를 다시 시작할 때 내부 볼륨에 저장되는 모든 데이터가 삭제되기때문이다.

- 참고: https://xggames.tistory.com/91

 

= nfs-exporter.sh

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.56.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

=> NFS용 디렉터리를 만들어주고, NFS 서비스로 생성해주는 스크립트

 

# nfs-exporter.sh 스크립트 실행
[root@m-k8s-ys vagrant]# ./nfs-exporter.sh jenkins
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.

# nfs_shared 폴더가 생성(+마운트)되었는지 확인
[root@m-k8s-ys vagrant]# ls -al /nfs_shared/
합계 0
drwxr-xr-x.  3 root root  21  4월  8 15:44 .
dr-xr-xr-x. 20 root root 279  4월  8 15:44 ..
drwxr-xr-x.  2 root root   6  4월  8 15:44 jenkins

 

= jenkins-volume.yaml

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: jenkins
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    server: 192.168.56.10
    path: /nfs_shared/jenkins
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

 

= jenkins-config.yaml

jenkins:
  agentProtocols:
  - "JNLP4-connect"
  - "Ping"
  authorizationStrategy:
    loggedInUsersCanDoAnything:
      allowAnonymousRead: false
  clouds:
  - kubernetes:
      containerCap: 10
      containerCapStr: "10"
      jenkinsTunnel: "jenkins-agent:50000"
      jenkinsUrl: "http://jenkins:80"
      maxRequestsPerHost: 32
      maxRequestsPerHostStr: "32"
      name: "kubernetes"
      namespace: "default"
      podLabels:
      - key: "jenkins/jenkins-jenkins-slave"
        value: "true"
      serverUrl: "https://kubernetes.default"
      templates:
      - containers:
        - args: "^${computer.jnlpmac} ^${computer.name}"
          command: ""
          envVars:
          - envVar:
              key: "JENKINS_URL"
              value: "http://192.168.56.11"
          image: "jenkins/inbound-agent:4.3-4"
          livenessProbe:
            failureThreshold: 0
            initialDelaySeconds: 0
            periodSeconds: 0
            successThreshold: 0
            timeoutSeconds: 0
          name: "jnlp"
          resourceLimitCpu: "512m"
          resourceLimitMemory: "512Mi"
          resourceRequestCpu: "512m"
          resourceRequestMemory: "512Mi"
          workingDir: "/home/jenkins"
        hostNetwork: false
        label: "jenkins-jenkins-slave "
        name: "default"
        nodeUsageMode: NORMAL
        podRetention: "never"
        runAsGroup: "993"
        runAsUser: "1000"
        serviceAccount: "jenkins"
        volumes:
        - hostPathVolume:
            hostPath: "/usr/bin/kubectl"
            mountPath: "/usr/bin/kubectl"
        - hostPathVolume:
            hostPath: "/bin/docker"
            mountPath: "/bin/docker"
        - hostPathVolume:
            hostPath: "/var/run/docker.sock"
            mountPath: "/var/run/docker.sock"
        yamlMergeStrategy: "override"
  crumbIssuer:
    standard:
      excludeClientIPFromCrumb: true
  disableRememberMe: false
  disabledAdministrativeMonitors:
  - "hudson.model.UpdateCenter$CoreUpdateMonitor"
  - "jenkins.diagnostics.RootUrlNotSetMonitor"
  - "jenkins.security.UpdateSiteWarningsMonitor"  
  labelAtoms:
  - name: "master"
  markupFormatter: "plainText"
  mode: NORMAL
  myViewsTabBar: "standard"
  numExecutors: 0
  primaryView:
    all:
      name: "all"
  projectNamingStrategy: "standard"
  quietPeriod: 5
  remotingSecurity:
    enabled: true
  scmCheckoutRetryCount: 0
  securityRealm: "legacy"
  slaveAgentPort: 50000
  updateCenter:
    sites:
    - id: "default"
      url: "https://updates.jenkins.io/update-center.json"
  views:
  - all:
      name: "all"
  viewsTabBar: "standard"
security:
  apiToken:
    creationOfLegacyTokenEnabled: false
    tokenGenerationOnCreationEnabled: false
    usageStatisticsEnabled: true
  sSHD:
    port: -1
unclassified:
  buildDiscarders:
    configuredBuildDiscarders:
    - "jobBuildDiscarder"
  fingerprints:
    fingerprintCleanupDisabled: false
    storage: "file"
  gitSCM:
    createAccountBasedOnEmail: false
    showEntireCommitSummaryInChanges: false
    useExistingAccountWithSameEmail: false
  junitTestResultStorage:
    storage: "file"
  location:
    adminAddress: "address not configured yet <nobody@nowhere>"
  mailer:
    charset: "UTF-8"
    useSsl: false
    useTls: false
  pollSCM:
    pollingThreadCount: 10
tool:
  git:
    installations:
    - home: "git"
      name: "Default"

 

= jenkins-install.sh

#!/usr/bin/env bash
jkopt1="--sessionTimeout=1440"
jkopt2="--sessionEviction=86400"
jvopt1="-Duser.timezone=Asia/Seoul"
jvopt2="-Dcasc.jenkins.config=/home/vagrant/jenkins-config.yaml"

helm install jenkins edu/jenkins \
--set persistence.existingClaim=jenkins \
--set master.adminPassword=admin \
--set master.nodeSelector."kubernetes\.io/hostname"=m-k8s-ys \
--set master.tolerations[0].key=node-role.kubernetes.io/master \
--set master.tolerations[0].effect=NoSchedule \
--set master.tolerations[0].operator=Exists \
--set master.runAsUser=1000 \
--set master.runAsGroup=1000 \
--set master.tag=2.249.3-lts-centos7 \
--set master.serviceType=LoadBalancer \
--set master.servicePort=80 \
--set master.jenkinsOpts="$jkopt1 $jkopt2" \
--set master.javaOpts="$jvopt1 $jvopt2"

 

 

# 젠킨스에서 사용할 PV, PVC를 만들어준다
[root@m-k8s-ys vagrant]# kubectl apply -f jenkins-volume.yaml 
persistentvolume/jenkins created
persistentvolumeclaim/jenkins created

# PV, PVC확인
[root@m-k8s-ys vagrant]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
jenkins   10Gi       RWX            Retain           Bound    default/jenkins                           19s
[root@m-k8s-ys vagrant]# kubectl get pvc
NAME      STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
jenkins   Bound    jenkins   10Gi       RWX                           21s

# 젠킨스 설치
[root@m-k8s-ys vagrant]# ./jenkins-install.sh 
# ===> jenkins가 설치된 젠킨스의 릴리스 이름이다. 이후 헬름 관련 명령으로 젠킨스를 조회, 삭제, 변경 등을 수행할 때 이 이름을 사용한다.
NAME: jenkins
LAST DEPLOYED: Fri Apr  8 15:59:35 2022
# ===> default 젠킨스가 배포된 네임스페이스는 default
NAMESPACE: default
STATUS: deployed
# ===> 이후에 helm upgrade 명령어를 사용해 젠킨스의 버전을 업그레이드할 경우 1씩 증가하게 된다. (helm rollback 사용시, REVISION 번호를 지정하여 돌아갈 수 있다.)
REVISION: 1
# ===> 안내사항들.
NOTES:
# ===> 관리자 비밀번호를 얻어오기위한 명령 안내
1. Get your 'admin' user password by running:
  printf $(kubectl get secret --namespace default jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
# ===> 젠킨스가 구동되는 파드에 접속할 수 있도록 외부의 트래픽을 쿠버네티스의 파드로 전달하게 만드는 설정 안내
# (원래는 이 설정을 해줘야하지만, 예제는 LB를 사용할 예정)
2. Get the Jenkins URL to visit by running these commands in the same shell:
  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        You can watch the status of by running 'kubectl get svc --namespace default -w jenkins'
  export SERVICE_IP=$(kubectl get svc --namespace default jenkins --template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
  echo http://$SERVICE_IP:80/login

# ===> 젠킨스 접속시 사용할 유저이름
3. Login with the password from step 1 and the username: admin

4. Use Jenkins Configuration as Code by specifying configScripts in your values.yaml file, see documentation: http:///configuration-as-code and examples: https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos

For more information on running Jenkins on Kubernetes, visit:
https://cloud.google.com/solutions/jenkins-on-container-engine
For more information about Jenkins Configuration as Code, visit:
https://jenkins.io/projects/jcasc/

welcome to Jenkins!

설치시, 사용했던 비밀번호로 로그인하자.

익숙한.. 젠킨스

헬름으로 설치 초기화하는 방법

위에서는 성공하는 방법으로 가이드를 했지만, 실제로 세팅하다보면 파드가 잘 뜨지않을 수도 있고, 오타나 설정오류로 인하여 제대로 동작하지 않을 수 있다. 이때 상황에 따라 초기화를 해야할 필요도 있을때가 있는데, 위의 내용을 기준(경로 등)으로 아래의 방법으로 초기화가 가능하다.

# 젠킨스가 준비가 안되고있다..
[root@m-k8s-ys vagrant]# kubectl get deployment
NAME      READY      UP-TO-DATE      AVAILABLE      AGE
jenkins   0/1        1               0              20m

# 파드배포가 안되고있다..!!
[root@m-k8s-ys vagrant]# kubectl get pods
NAME                       READY      STATUS      RESTARTS      AGE
jenkins-6ddf56cd9f-7l6ql   0/2        Init:0/1    0             21m

# 젠킨스 제거하기
[root@m-k8s-ys vagrant]# helm uninstall jenkins
release "jenkins" uninstalled

# 젠킨스 설치 시 내려받은 파일들을 삭제
[root@m-k8s-ys vagrant]# rm -rf /nfs_shared/jenkins/*

 

참고자료

= 서적 - 컨테이너 인프라 환경 구축을 위한 쿠버네티스/도커, 길벗 - 제 5장