Kubernetes Jenkins Agent 동적생성

By | 2022년 10월 29일
Table of Contents

Kubernetes Jenkins Agent 동적생성

Jenkins 에서 빌드작업을 할 때 필요한 추가 Pod 을 동적으로 생성합니다.

Jenkins 생성

여기 를 참조하여 Jenkins 를 생성해 줍니다.

플러그인 설치

플러그인 Kubernetes 를 설치합니다.

Kubernetes Cloud 정보 추가

kubectl get svc -n jenkins
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
jenkins   NodePort   10.99.46.83   <none>        8080:30088/TCP   20h

Jenkins 관리 > 노드관리 > Configure Clouds 에 Kubernetes 를 생성합니다.

Test connection 을 클릭해서 Connected to Kubernetes v1.25.3 가 나오면 성공입니다.

Jenkins 생성시 Pod 생성을 포함한 충분한 권한을 주었기 때문에,
별도로 권한 설정은 필요없습니다.

jnlp 포트 활성화

kubectl edit StatefulSet jenkins -n jenkins
---------------------------
    spec:
      containers:
      - image: jenkins/jenkins:lts
        imagePullPolicy: IfNotPresent
        name: jenkins
        ports:
        - containerPort: 8080
          name: http-port
          protocol: TCP
        - containerPort: 50000     # 여기
          name: jnlp-port
          protocol: TCP
---------------------------
kubectl edit svc jenkins -n jenkins
---------------------------
  ports:
  - name: http-port                # 여기
    nodePort: 30088
    port: 8080
    protocol: TCP
    targetPort: 8080
  - name: jnlp-port                # 여기
    port: 50000
    protocol: TCP
    targetPort: 50000
---------------------------
kubectl get svc -n jenkins
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                          AGE
jenkins   NodePort   10.99.46.83   <none>        8080:30088/TCP,50000:31628/TCP   21h

Test

젠킨스 아이템을 생성합니다.

pipeline {
    agent {
        kubernetes {
            defaultContainer 'jnlp'
            yaml """
spec:
  dnsPolicy: Default       # 이게 왜 필요할까?
  containers:
    - name: docker
      image: docker:latest
      command:
        - cat
      tty: true
      privileged: true
      volumeMounts:
        - name: dockersock
          mountPath: /var/run/docker.sock
  volumes:
    - name: dockersock
      hostPath:
        path: /var/run/docker.sock
            """
        }
    }

    stages {
         stage('My test') {
            steps {
                container('docker') {
                    sh "echo hello world"
                    sh "docker ps"
                }
            }
        }
    }
}

빌드가 성공하는 것을 확인할 수 있습니다.

Docker Private Repository 접근하기

Docker Private Repository 는 https 로 설정되어 있어야 합니다.

또는 insecure 설정을 해주어야 하는데,
Docker 이미지가 아닌 호스트 서버에 설정되어 있어야 합니다.

주의사항 01

모든 설정들이 Pod 가 아닌 호스트에 이루어져야 합니다.

이 경우 관리상 매우 안좋은 방식이 됩니다.

그냥 일반 도메인 및 SSL인증서를 발급받아 설치하는게 좋습니다.

주의사항 02

docker 와 containerd 의 역할은 docker 가 push/pull 을 처리하고,
containerd 가 컨테이너를 실행시킵니다.

따라서 repo 에 push 까지는 systemctl restart docker 만 해주어도 작동하지만,
repo 에서 pull 한 이미지를 실제 Pod 로 올리기 위해서는 systemctl restart containerd 도 실행시켜 주어야 합니다.

systemctl restart docker
systemctl restart containerd

insecure-registries 설정

pipeline {
    agent {
        kubernetes {
            defaultContainer 'jnlp'
            yaml """
spec:
  # dnsPolicy: Default       # 이게 왜 필요할까?
  containers:
    - name: docker
      image: docker:20.10.22
      command:
        - cat
      tty: true
      # privileged: true
      volumeMounts:
      - name: dockersock
        mountPath: /var/run/docker.sock
  volumes:
  - name: dockersock
    hostPath:
      path: /var/run/docker.sock
"""
        }
    }

    stages {
        stage("Create Dockerfile") {
            steps {
                    writeFile file: 'Dockerfile', text: """
FROM docker.elastic.co/elasticsearch/elasticsearch:7.17.8

# RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch https://github.com/skyer9/elasticsearch-jaso-analyzer/releases/download/7.17.8/jaso-analyzer-plugin-7.17.8-plugin.zip
RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch https://github.com/skyer9/elasticsearch-jaso-analyzer/releases/download/7.17.8/elasticsearch-jaso-analyzer-7.17.8.jar
RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-icu
RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-nori
                    """
            }
        }

        stage('Docker Build') {
            steps {
                container('docker') {
                    sh "docker info"
                }
            }
        }
    }
}

Job 을 실행시키고 콘솔 출력을 확인해 보면 Worker Node 에 설정되어 있는 Insecure Registries 표시되는 것을 볼 수 있습니다.

주의할 것은 Worker Node 가 여러대인 경우,
Agent 가 어디에서 생성될지 모르기 때문에,
모든 서버에 동일한 설정을 해주어야 합니다.

......
 Experimental: false
 Insecure Registries:
  docker-repository.repository.svc.cluster.local
  127.0.0.0/8
 Live Restore Enabled: false
......
[Worker Node ~]$ cat /etc/docker/daemon.json
{
  "insecure-registries" : ["docker-repository.repository.svc.cluster.local"]
}

사설인증서로 Repository 를 구성한 경우

역시 Worker Node 에 실행중인 Docker 에 사설인증서(루트인증서) 를 등록해 주어야 합니다.

주의할 것은 Worker Node 가 여러대인 경우,
Agent 가 어디에서 생성될지 모르기 때문에,
모든 서버에 동일한 설정을 해주어야 합니다.

마지막으로 주의할 것은 host 의 Docker 데몬이 이용되므로,
네트워크 통신 또한 호스트에서 이루어집니다.
따라서 DNS 설정도 호스트에 있어야 합니다.

sudo mkdir -p /etc/docker/certs.d/docker-repository.repository.svc.cluster.local
sudo vi /etc/docker/certs.d/docker-repository.repository.svc.cluster.local/ca.crt

# 참조 : https://www.skyer9.pe.kr/wordpress/?p=2585
sudo yum install -y ca-certificates
sudo update-ca-trust force-enable
sudo cp /etc/docker/certs.d/docker-repository.repository.svc.cluster.local/ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract

sudo vi /etc/hosts
......
10.96.251.68    docker-repository.repository.svc.cluster.local
......

sudo systemctl restart docker
sudo systemctl restart containerd
pipeline {
    agent {
        kubernetes {
            defaultContainer 'jnlp'
            yaml """
spec:
  # dnsPolicy: Default       # 이게 왜 필요할까?
  containers:
    - name: docker
      image: docker:20.10.22
      command:
        - cat
      tty: true
      # privileged: true
      volumeMounts:
      - name: dockersock
        mountPath: /var/run/docker.sock
  volumes:
  - name: dockersock
    hostPath:
      path: /var/run/docker.sock
"""
        }
    }

    stages {
        stage("Get Source") {
            steps {
                    writeFile file: 'Dockerfile', text: """
FROM docker.elastic.co/elasticsearch/elasticsearch:7.17.8

# RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch https://github.com/skyer9/elasticsearch-jaso-analyzer/releases/download/7.17.8/jaso-analyzer-plugin-7.17.8-plugin.zip
RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch https://github.com/skyer9/elasticsearch-jaso-analyzer/releases/download/7.17.8/elasticsearch-jaso-analyzer-7.17.8.jar
RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-icu
RUN /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-nori
                    """
            }
        }

        stage('Docker Build') {
            steps {
                container('docker') {
                    sh "docker build -t docker-repository.repository.svc.cluster.local/elasticsearch:7.17.8.${build_number} ."
                    sh "docker push docker-repository.repository.svc.cluster.local/elasticsearch:7.17.8.${build_number}"
                }
            }
        }
    }
}
sudo su -

openssl s_client -showcerts -connect docker-repository.repository.svc.cluster.local:443 < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /etc/docker/certs.d/docker-repository.repository.svc.cluster.local/ca.crt

cp /etc/docker/certs.d/docker-repository.repository.svc.cluster.local/ca.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust extract

systemctl restart docker
systemctl restart containerd

exit

답글 남기기