데브코스

[17주차 - DAY3] 인수 테스트

미안하다 강림이 좀 늦었다 2024. 6. 19. 14:28

 

 

이미지 레지스트리 구성

빌드한 이미지를 저장하고 관리할 수 있는 저장소를 만들어보자.

1. 도커가 공식적으로 제공하고 있는 레지스트리 컨테이너 이미지를 사용하여 실행한다.

registry:2 이미지를 이용하면 도커 레지스트리를 운용할 수 있다.

docker run -d --rm -p 8765:5000 --name registry registry:2

 

2. 이미지에 태그를 붙인다.

docker tag ubuntu:22.04 localhost:8765/ubuntu:22.04

 

3. 이미지를 push 한다.

docker push localhost:8765/ubuntu:22.04

 

4. 레지스트리에 저장되어 있는 이미지들의 목록을 확인한다.

curl localhost:8765/v2/_catalog

 

5. 해당 이미지의 태그 목록을 조회한다.

curl localhost:8765/v2/ubuntu/tags/list

 

 

이제 k8s 클러스터에 위의 이미지를 이용하여 포드를 배포하고, 서비스로 노출해보자.

1. k8s Manifest를 작성한다. 파일명은 registry.yaml으로 한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: registry
  labels:
    run: registry
    namespace: registry
spec:
  replicas: 1
  selector:
    matchLabels:
      run: registry
  template:
    metadata:
      labels:
        run: registry
    spec:
      containers:
      - name: registry
        image: registry:2
        ports:
        - containerPort: 5000

---

apiVersion: v1
kind: Service
metadata:
  name: registry-service
  namespace: registry
spec:
  type: LoadBalancer
  selector:
    run: registry
  ports:
  - name: registry-tcp
    protocol: TCP
    port: 30100
    targetPort: 5000
    nodePort: 30100

 

2. kubectl로 registry라는 namespace를 만든다.

kubectl create namespace registry

 

3. 1에서 작성한 파일로 deployment와 service를 생성한다.

kubectl apply -f registry.yaml

 

4. 태그를 붙인다.

docker tag ubuntu:22.04 localhost:30100/ubuntu:22.04

 

5. 레지스트리로 push 한다.

docker push localhost:30100/ubuntu:22.04

 

6. 이미지 목록과 태그 목록을 확인한다.

curl localhost:30100/v2/_catalog

curl localhost:30100/v2/ubuntu/tags/list

 

 

이제 실습을 다 했으니 환경을 정리한다.

1. 실행하고 있던 컨테이너 registry를 중단한다. 실행할 때 rm 옵션을 줬었기 때문에 자동으로 소멸된다.

docker stop registry

 

2. 설치했던 포드와 서비스를 삭제한다.

kubectl delete -f registry.yaml

 

 

OpenSSL을 이용하여 인증서를 발급하고 설치해보자.

1. openssl이 설치되어 있는지 확인한다. 설치되어 있지 않다면 구글에 검색해서 다운로드하고, OpenSSL을 환경변수에 등록한다.

openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/registry.key -add "subjectAltName=DNS:registry-service.registry.svc.cluster.local" -x509 -days 3650 -out certs/registry.crt

 

2. k8s의 secret으로 등록한다.

kubectl create secret tls registry-cert --cert=certs/registry.crt --key=certs/registry.key -n registry

 

3. registry.yaml 파일을 다음과 같이 수정한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: registry
  labels:
    run: registry
    namespace: registry
spec:
  replicas: 1
  selector:
    matchLabels:
      run: registry
  template:
    metadata:
      labels:
        run: registry
    spec:
      containers:
      - name: registry
        image: registry:2
        ports:
        - containerPort: 5000
        env:
        - name: REGISTRY_HTTP_TLS_CERTIFICATE
          value: "/certs/tls.crt"
        - name: REGISTRY_HTTP_TLS_KEY
          value: "/certs/tls.key"
        volumeMounts:
        - name: registry-certs
          mountPath: "/certs/"
          readOnly: true
        - name: registry-data
          mountPath: "/var/lib/registry"
      volumes:
        - name: registry-certs
          secret:
            secretName: registry-cert
        - name: registry-data
          persistentVolumeClaim:
            claimName: registry-data-pvc

---

apiVersion: v1
kind: Service
metadata:
  name: registry-service
  namespace: registry
spec:
  type: LoadBalancer
  selector:
    run: registry
  ports:
  - name: registry-tcp
    protocol: TCP
    port: 30100
    targetPort: 5000
    nodePort: 30100

---

apiVersion: v1
kind: PersistentVolume
metadata:
  name: registry-data-pv
  namespace: registry
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 50Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: "D:\pv"

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: registry-data-pvc
  namespace: registry
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: manual
  resources:
    requests:
      storage: 50Gi

 

4. 3에서 작성한 파일로 오브젝트들을 생성한다.

kubectl apply -f registry.yaml

 

4. 레지스트리에 push 한다.

docker push localhost:30100/ubuntu:22.04

 

5. 3에서 설정한 볼륨 호스트 경로를 확인해보면 파일들이 저장된 것을 확인할 수 있다.

 

 

인수 테스트

젠킨스 파이프라인에서의 인수 테스트

1. 개발자가 변경한 코드를 깃허브에 push 한다.

2. 젠킨스가 변경을 감지하여 빌드를 시작하고, 단위 테스트를 수행한다.

3. 젠킨스가 빌드를 완료하여 도커 이미지를 생성한다.

4. 젠킨스가 생성한 이미지를 레지스트리로 push 한다.

5. 젠킨스가 스테이징 환경을 구성하고 도커 컨테이너를 실행한다.

6. 스테이징 환경의 도커 호스트가 이미지를 pull 하여 컨테이너를 실행한다.

7. 젠킨스가 스테이징 환경에서 실행 중인 앱을 대상으로 인수 테스트를 실행한다.

 

빌드에 사용할 컨테이너 설정

1. Dockerfile-jnlp를 작성한다.

FROM jenkins/jnlp-agent-docker

USER root

COPY entrypoint.sh /entrypoint.sh
RUN chown jenkins:jenkins /entrypoint.sh
RUN chmod +x /entrypoint.sh

RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
    && chmod 755 kubectl \
    && mv kubectl /bin

USER jenkins

ENTRYPOINT ["/entrypoint.sh"]

 

2. Dockerfile-dind를 작성한다.

이때 자가 서명 인증서를 사용한다. registry.crt 부분이 자가 서명 인증서이다.

FROM docker:latest

COPY registry.crt /usr/share/ca-certificates/registry/registry.crt
RUN echo "registry/registry.crt" >> /etc/ca-certificates.conf
RUN update-ca-certificates

 

3. Dockerfile-builder 파일을 작성한다.

우분투 위에 openjdk-17-jdk를 설치하는 명령이 작성되어 있다.

FROM ubuntu:22.04

RUN apt update
RUN apt install -y openjdk-17-jdk

 

4. 빌드한다.

성공적으로 빌드하고 나면 두 번째와 같은 파일이 생성된 것을 확인할 수 있다.

gradlew build

 

5. 도커 이미지를 빌드한다. 아래는 Dockerfile의 내용이다.

FROM openjdk:17
COPY build/libs/calculator-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

 

6. 아래 명령어를 입력하여 테스트해본다.

curl "localhost:8765/sum?a=12&b=35"

 

작업용 임시 파이프라인 만들기

1. 기존에 설정한 파이프라인에서 Poll SCM 옵션을 잠시 끈다.

 

2. 젠킨스에서 아이템을 만들어서 다음의 pipeline script를 입력한다.

pipeline {
    agent {
        kubernetes {
            yaml'''
            apiVersion: v1
            kind: Pod
            spec:
              containers:
              - name: jnlp
                image: sheayun/jnlp-agent-sample
                env:
                - name: DOCKER_HOST
                  value: "tcp://localhost:2375"
              - name: dind
                image: sheayun/dind
                command:
                - /usr/local/bin/dockerd-entrypoint.sh
                env:
                - name: DOCKER_TLS_CERTDIR
                  value: ""
                securityContext:
                  privileged: true
              - name: builder
                image: sheayun/jenkins-agent-jdk-17
                command:
                - cat
                tty: true
            '''
        }
    }
    environment {
        REGISTRY_URI = 'registry-service.registry.svc.cluster.local:30100'
        REGISTRY_TAG = '1.0'
    }
    stages {
        stage("Checkout") {
            steps {
                git url: 'git@github.com:ncherryu/calculator.git',
                    branch: 'main',
                    credentialsId: 'github-credentials'
            }
        }
        stage("Compile") {
            steps {
                script {
                    container('builder') {
                        sh "./gradlew compileJava"
                    }
                }
            }
        }
        stage("Unit Test") {
            steps {
                script {
                    container('builder') {
                        sh "./gradlew test"
                        publishHTML(target: [
                            reportDir: 'build/reports/tests/test',
                            reportFiles: 'index.html',
                            reportName: 'JUnit Report'
                        ])
                    }
                }
            }
        }
        stage("Code Coverage") {
            steps {
                script {
                    container('builder') {
                        sh "./gradlew jacocoTestReport"
                        publishHTML(target: [
                            reportDir: 'build/reports/jacoco/test/html',
                            reportFiles: 'index.html',
                            reportName: 'JaCoCo Report'
                        ])
                        sh "./gradlew jacocoTestCoverageVerification"
                    }
                }
            }
        }
        stage("Static Analysis") {
            steps {
                script {
                    container('builder') {
                        sh "./gradlew checkstyleMain"
                        publishHTML(target: [
                            reportDir: 'build/reports/checkstyle',
                            reportFiles: 'main.html',
                            reportName: 'Checkstyle Report'
                        ])
                    }
                }
            }
        }
        stage("Package") {
            steps {
                script {
                    container('builder') {
                        sh "./gradlew build"
                    }
                }
            }
        }
        stage("Docker Build") {
            steps {
                script {
                    dockerImage = docker.build "calculator"
                }
            }
        }
        stage("Docker Push") {
            steps {
                script {
                    docker.withRegistry("https://${REGISTRY_URI}") {
                        dockerImage.push("${REGISTRY_TAG}")
                    }
                }
            }
        }
    }
}

 

3. 빌드를 수행하고 결과를 확인한다.

 

 

 

배운 점

  • 이미지를 저장하고 관리할 수 있는 저장소를 만드는 방법을 배웠고, pv와 pvc를 이용하여 호스트에 저장하는 방법을 알게 되었다.
  • 젠킨스 파이프라인에서 인수 테스트를 진행하는 방법에 대해서 알게 되었다.