yongyongMom 2024. 11. 12. 17:43
SMALL

1. Docker Image Build & Docker Hub Push

1) Jenkinsfile 에 이미지를 빌드하는 stage 를 추가하고 확인

stage("docker image build"){
  steps{
      sh 'docker build -t jenkins1112 .'
  }
}
  • 이미지 빌드가 안되는 경우
    • jenkins 에 Docker 관련 Plugin 을 설치
    • jenkins 에서 host docker 접근 권한 부여
groupadd -f docker
usermod -aG docker jenkins
chown root:docker /var/run/docker.sock

 

2) Docker Hub 에 이미지를 푸시하기 위한 준비 작업

  1. Docker Hub 에서 토큰을 발급받기
  2. 이미지를 저장하기 위한 Repository  를 생성
    1. 업로드 되는 도커 이미지는 저장소의 이름과 동일한 이름을 가져야 함
  3. 토큰을 Jenkins 의 Credential 에 저장 
    1. Jenkins 에 Token 을 직접 사용하면 에러가 발생하고 이후에는 git 에 push 가 안됨
    2. username And Password 로 등록하는데 Username 에 Docker Hub 의 계정 이름을 설정
    3. Password 에 토큰 값을 설정
    4. ID 를 정한 후 ID 를 기억

 

3) Docker Login

  • Jenkinsfile 에 Credential ID 를 변수로 등록
environment{
   DOCKERHUB_CREDENTIALS = credentials("docker-hub")
}
  • ImageBuild Stage 는 저장소 이름으로 변경하고 로그인 작성
stage("docker image build"){
  steps{
      sh 'docker build -t ggnagpae1/jenkins1112 .'
  }
}

stage('docker hub login'){
  steps{
       sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
  }
}

4) Docker Image Push

  • stage 에 추가
  steps{
      sh 'docker push ggnagpae1/jenkins1112:latest'
  }
}

 

2. Jenkins 가 제공하는 환경 변수

  • env.VARNAME 으로 사용이 가능 -> 전역변수
  • env 가 제공되는 변수
BUILD_ID
JOB_NAME
CHANGE_ID
CHANGE_URL
CHANGE_TARGET
CHANGE_BRANCH
BUILD_NUMBER
JENKINS_URL
BUILD_URL
JOB_URL
  • currentBuild 라는 환경 변수는 현재 빌드에만 해당되는 지역변수
    • number
    • result
    • currentResult
    • duration
    • keepLong
    • displayName

 

3. 배포

1) Jenkins 에 컨테이너를 만드는 코드를 추가

stage('deploy'){
   steps{
       sh "docker run -d --rm -p 8765:8080 --name jenkins1112 ggnagpae1/jenkins1112"
   }
}

2) 인수 테스트

  • 루트 디렉터리에 스크립트 파일 생성 : acceptance_test.sh
#!/bin/bash
test $(curl localhost:8765?a=1\&b=2) -eq 2
  • 스크립트 파일을 실행하는 코드를 스테이지에 추가
    • sleep 을 사용하는 이유 
    • docker run -d 가 비동기 방식으로 실행 -> 연속 테스트 명령 -> 컨테이너 만들기전 명령 실행
    • 실행은 정상적이나 테스트 단계에서 실패로 판정하는 경우 발생 가능
stage('acceptance test'){
  steps{
      sleep 60
      sh 'chmod +x acceptance_test.sh && ./acceptance_test.sh'
  }
}

3) clean up 스테이지 추가

post{
   always{
       sh 'docker stop jenkins1112'
   }
}

 

4. 인수 테스트 작성

1) 개요

  • 웹 애플리케이션의 경우 curl 명령을 이용해서 인수 테스트 가능
  • 기술적 측면만 보면 REST 웹 서비스를 작성한 경우 curl 호출 방식으로 모든 블랙박스 테스트 구성 가능
    • 블랙박스 테스트 : 사용자의 요구 사항에 맞게 동작하는지 기능을 테스트
    • 모든 경우를 전부 테스트, 경계값 분석을 해주는 것이 중요함
  • curl 명령으로 테스트를 하는 것은 읽고 이해하기가 어려움, 유지보수에도 좋지 않음

2) 사용자-대면 테스트 작성

  • 대부분의 소프트웨어가 특정한 목적을 가지고 개발되며 대부분은 개발자가 아닌 일반 사용자에게 서비스를 제공
  • 사용자가 인수 기준을 정해야 하며 이를 가지고 자동 인수 테스트를 수행하도록 해주어야 함
  • 프레임워크 : 큐컴버, 피트니스, JBehave 등이 있음
    1. 사용자가 인수 기준을 정함
    2. 개발자가 픽스쳐와 스텝을 정의
    3. 자동 인수 테스트 수행
  • 인수 기준을 시나리오 형태로 작성
Given I have two numbers: 1 and 3
When the calculator sums them
Then I receive 3 as a result
  • 개발자는 픽스쳐 또는 스텝 정의라고 부르는 사용자 친화적인 도메인 특화 언어(DSL) 와 프로그래밍 언어와 통합해서 테스트 작성

3) cucumber

  • build.gradle 파일의 dependencies 에 의존성 라이브러리를 추가
testImplementation("io.cucumber:cucumber-java:7.2.0")
testImplementation("io.cucumber:cucumber-junit:7.2.0")
  • src/test/resource/feature 디렉터리에 calculator.feature 파일을 만들고 시나리오 작성
Feature: Calculator
 Scenario: Sum two numbers
   Given I have two numbers: 1 and 2
   When the calculator sums them
   Then I receive 2 as result
  • 기능 사양을 실행할 수 있는 자바 바인딩을 생성
    src/test/java/acceptance/StepDefinitions.java 파일로 생성
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.springframework.web.client.RestTemplate;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class StepDefinitions {
  
   //서버 URL을 저장
   private String server = System.getProperty("calculator.url");
  
   //웹 서비스 요청을 위한 클래스
   private RestTemplate restTemplate = new RestTemplate();
  
   //매개변수 와 결과를 저장할 변수
   private String a;
   private String b;
   private String result;
  
  
   @Given("^I have two numbers: (.*) and (.*)$")
   public void i_have_two_numbers(String a, String b) throws Throwable {
       this.a = a;
       this.b = b;
   }
  
   @When("^the calculator sums them$")
   public void the_calculator_sums_them() throws Throwable{
       String url = String.format("%s?a=%s&%b=%s", server, a, b);
       result = restTemplate.getForObject(url, String.class);
   }
  
   @Then("^I recevie (.*) as a result")
   public void i_receive_as_a_result(String expectedResult) throws Throwable{
       assertEquals(expectedResult, result);
   }
  
}
  • build.gradel 파일에 코드를 작성
tasks.register('acceptanceTest', Test){
   include '**/acceptance/**'
   systemProperties System.getProperties()
}

test{
   exclude '**/acceptance/**'
}
  • src/test/java/acceptance 패키지에 JUnit 테스트
    러너(AcceptanceTest) 추가
package acceptance;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features = "classpath:feature")
public class AcceptanceTest {
}
  • 테스트 명령
./gradlew acceptanceTest -Dcalculator.url=http://54.180.146.200:8765

 

5. Kubenetes

1) 쿠버네티스에 업로드한 이미지를 파드로 배포

  • deployment.yaml 파일을 생성하고 작성
apiVersion: apps/v1  #API 버전은 apps/v1(batch/v1, v1)

kind: Deployment #리소스 종류

#부가 정보를 설정
metadata:
  name: calculator-deployment
  labels:
    app: calculator

spec:
  #동일한 이미지로 만든 파드가 3개
  replicas: 3
 #Deployment가 관리할 파드를 찾는 방법
 #레이블로 찾는데 그 이름은 app: calculator
  selector:
    matchLabels:
      app: calculator
  #생성될 파드의 사양
  template:
   #파드의 이름 설정
    metadata:
      labels:
        app: calculator
 #각 파드가 containerd 나 docker에서 만들어질 때 컨테이너 이름 과 이미지 그리고 외부에 공개할 포트 번호   
    spec:
      containers:
      - name: calculator
        image: ggnagpae1/jenkins1112
        ports:
        - containerPort: 8080
  • yaml 파일 실행
kubectl apply -f deployment.yaml
  • 파드 확인
kubectl get pods
  • 로그 확인
kubectl logs pods/파드의이름
  • kubectl 명령 확인
https://kubernetes.io/docs/reference/kubectl/

 

2) 서비스

  • 개요
    • 사용자 -> Service -> Pod 를 선택해서 요청을 전송
  • service.yml 파일을 생성하고 작성
apiVersion: v1
kind: Service
metadata:
  name: calculator-service
spec:
  type: NodePort
  selector:
    app: calculator
  ports:
  - port: 8080
  • 서비스 실행 
kubectl apply -f service.yml
  • 서비스 확인
kubectl get svc
kubectl describe scv 서비스이름
  • 애플리케이션 노출 : type
    • ClusterIP
      • : 기본 설정값으로 서비스는 내부 IP 만 소유
    • NodePort
      • 클러스터 노드마다 동일한 포트를 갖도록 서비스를 노출
      • 물리적 기기 (노드) 는 서비스로 전달되는 포트를 오픈 시 다른노드에서 <노드-IP>:<노드포트>로 접근하는 것이 가능
    • LoadBalancer
      • 외부 로드 밸런서를 생성한 후 서비스에 별도의 외부 IP 를 할당하는 것
      • 클러스터를 외부로 노출해서 접근이 가능하도록 하는 것
    • Ingress
      • 클러스터 외부에서 클러스터 내부 서비스로 HTTP 와 HTTPS 경로를 노출
      • 외부에 로드 밸런서를 배치 -> 아넹 ingress 배치
      • 사용자 -> ingress managed LoadBalancer -> ingress -> 서비스
    • ExternalName
      • 외부 URL 을 내부에서 다른 이름으로 접근할 수 있도록 하는 것

 

3) 파드의 업데이트 전략

  • Deployment 를 만들 때 spec 항목에 strategy 의 type 에 설정
    • 기본은 RollingUpdate
    • 순서대로 하나를 생성하고 정상 동작하면 새로운 하나를 만들면서 과거의 하나를 삭제해 나가는 방식

4) 캐시 전략

  • 컨테이너를 사용하게 되면 컨테이너를 사용하지 않은 경우보다 응답 속도가 느리게 됨
  • 자주 사용하는 메서드에 cache 기능을 부여해 동일한 호출의 경우는 캐시에서 응답하도록 설정하는 것이 좋음
반응형
LIST