Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Docker0
- clusterrolebinding
- assumerole
- jenkins
- ingestion
- saa-c03 #saa #aws certified solutions architect - associate
- Pipeline
- kubernetes
- 테라폼
- aws ses #aws lambda
- aws-loadbalacner-controller
- IRSA
- 코드커버리지
- 에이전트 유형
- route53
- 에이전트 구성
- NAT
- Service Account
- fruition
- node group
- Amazon CloudWatch
- instances failed to join cluster
- Gateway
- Terraform
- helm_release
- 추가 보안 그룹
- httpasswd
- docker
- s3
- 클러스터 보안 그룹
Archives
- Today
- Total
cloudwithbass
[Jenkins] 젠킨스 파이프라인: 깃 푸시부터 인수 테스트까지 본문
Continuous Delivery with Docker and Jenkins의 챕터 5까지 학습하며 만든 최종 Jenkinsfile입니다.
- git push하면 자동으로 파이프라인을 빌드하도록 콘솔에서 트리거를 구성했습니다.
- Jenkins 복습을 위해 스스로 지금까지 공부한 내용에 대해 설명하려고 합니다.
- 전체 Jenkinsfile의 코드부터 첨부한 후, 부분마다 제 설명을 덧붙이겠습니다.
목차
1. 전체 Jenkinsfile
pipeline {
agent {
docker {
image 'dminus251/jenkins-docker-agent:using_socket'
args '--privileged -v /var/run/docker.sock:/var/run/docker.sock'
label 'docker-node-agent'
}
}
environment {
DOCKER_CREDENTIALS_ID = 'dminus251' // 저장한 자격 증명의 ID를 입력합니다.
}
stages {
stage('Check Docker Installation') {
steps {
script {
sh 'which docker'
sh 'docker --version'
}
}
}
stage('Compile') {
steps {
sh './gradlew compileJava'
}
}
stage('Unit Test') {
steps {
sh './gradlew test'
}
}
stage('Code Coverage') {
steps {
sh './gradlew jacocoTestReport'
sh './gradlew jacocoTestCoverageVerification'
}
}
stage('Static Code Analysis') {
steps {
sh './gradlew checkstyleMain'
}
}
stage('Build Jar') {
steps {
sh './gradlew build'
}
}
stage('Docker Build') {
steps {
script {
sh 'docker build -t dminus251/calculator:latest .'
}
}
}
stage('Docker Login') {
steps {
script {
// Docker Hub에 로그인
withCredentials([usernamePassword(credentialsId: env.DOCKER_CREDENTIALS_ID, usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
sh 'echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin'
}
}
}
}
stage("Docker push"){
steps{
sh "docker push dminus251/calculator:latest"
}
}
stage("Deploy to staging"){
steps{
sh "docker run -d --rm -p 8765:8081 --name calcForStaging dminus251/calculator:latest"
}
}
stage("Acceptance test"){
steps{
sleep 30 //docker run이 확실히 실행될 때까지 기다림
sh "docker logs calcForStaging"
sh "chmod +x acceptance_test.sh && ./acceptance_test.sh"
}
}
}
//까지 stages
post{
always{
sh "docker stop calcForStaging"
}
}
}
2. agent
pipeline {
agent {
docker {
image 'dminus251/jenkins-docker-agent:using_socket'
args '--privileged -v /var/run/docker.sock:/var/run/docker.sock'
label 'docker-node-agent'
}
}
- 저는 처음에 동적 프로비저닝 도커 에이전트를 사용했었는데, 도커인도커 사용을 위해 영구 도커 에이전트로 agent를 다시 구성했습니다.
- 이 글에서 3일 동안 도커인도커를 구성한 과정을 확인할 수 있습니다.
- 도커인도커 통신을 위해 /var/run/docker.sock을 마운트했으며, privileged 권한을 부여했습니다.
- dminus251/jenkins-docker-agent:using_socket을 만든 Dockerfile은 다음과 같습니다.
#Dockerfile for dminus251/jenkins-docker-agent:using_socket
FROM gradle:jdk17
# Docker 클라이언트 설치
RUN apt-get update && \
apt-get install -y docker.io
RUN groupadd -g 1001 newdocker && usermod -aG newdocker root
USER root
docker.sock 사용을 위해 docker.sock의 소유 그룹 ID인 1001을 newdocker라는 그룹으로 추가했고, root 사용자를 newdocker 그룹에 추가했습니다.
3. environment
environment {
DOCKER_CREDENTIALS_ID = 'dminus251' // 저장한 자격 증명의 ID를 입력합니다.
}
- Docker push 스테이지에서 도커 허브에 이미지를 푸시하기 위해 필요한 환경 변수입니다.
- 젠킨스 콘솔의 Credentials 메뉴에서 도커 허브의 계정명, ID, PASSWORD를 저장해야 합니다.
- 이후 Docker push 스테이지에서 더욱 자세히 설명하겠습니다.
4. stages
4-1. Check Docker Installation
stages {
stage('Check Docker Installation') {
steps {
script {
sh 'which docker'
sh 'docker --version'
}
}
}
도커가 설치된 이미지를 젠킨스의 에이전트로 사용했는데요, 성공적으로 도커 명령을 사용할 수 있는지 확인하기 위해 docker 명령어의 위치와 docker version을 출력합니다.
4-2. Compile, Unit Test
stage('Compile') {
steps {
sh './gradlew compileJava'
}
}
stage('Unit Test') {
steps {
sh './gradlew test'
}
}
- Compile 스테이지에서 자바 application을 컴파일하고, Unit Test 스테이지에서 단위 테스트를 실행합니다.
- 이 글에서 더 자세한 내용을 확인할 수 있습니다.
4-3. Code Coverage, Static Code Analysis
stage('Code Coverage') {
steps {
sh './gradlew jacocoTestReport'
sh './gradlew jacocoTestCoverageVerification'
}
}
stage('Static Code Analysis') {
steps {
sh './gradlew checkstyleMain'
}
}
- 각각 코드 커버리지와 정적 코드 분석을 실시합니다.
- 이 글에서 더 자세한 내용을 확인할 수 있습니다.
4-4. Build Jar, Docker Build
stage('Build Jar') {
steps {
sh './gradlew build'
}
}
stage('Docker Build') {
steps {
script {
sh 'docker build -t dminus251/calculator:latest .'
}
}
}
- Build Jar 스테이지에선 프로젝트를 빌드하고, 그 프로젝트의 아티팩트인 jar 파일을 생성합니다.
- Docer Build 스테이지에선 현재 디렉터리의 Dockerfile에 작성된 내용을 dminus251/calculator:latest 이미지로 빌드합니다.
- Dockerfile의 내용은 다음과 같습니다.
# Dockerfile for dminus251/calculator:latest
FROM openjdk:17-slim
# JAR 파일을 이미지에 복사
COPY build/libs/calculator-0.0.1-SNAPSHOT.jar app.jar
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y curl
# 컨테이너가 시작될 때 JAR 파일을 실행하도록 설정
ENTRYPOINT ["java", "-jar", "app.jar"]
- 자바 애플리케이션 실행을 위해 베이스 이미지로 openjdk:17를 사용했습니다.
- 이 컨테이너는 jar 파일을 복사한 후 실행합니다.
4-5. Docker Login
stage('Docker Login') {
steps {
script {
// Docker Hub에 로그인
withCredentials([usernamePassword(credentialsId: env.DOCKER_CREDENTIALS_ID, usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
sh 'echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin'
}
}
}
}
- 도커 허브에 dminus251/calculator:latest 이미지를 푸시하기 위해 도커 허브에 로그인합니다.
- DOCKER_CREDENTIALS_ID 환경 변수는 3. environment에서 선언했습니다.
- 젠킨스가 이 변수를 참조해서 젠킨스 설정 내 Credentials에서 DOCKER_USERNAME과 DOCKER_PASSWORD 값을 가져옵니다.
- echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin에서는 파이프를 이용해 echo $DOCKER_PASSWORD의 결과값을 파이프 뒤 명령의 입력값으로 사용합니다.
- 이 과정을 거쳐 도커 허브에 정상적으로 로그인이 가능합니다.
4-6. Docker push, Deploy to staging
stage("Docker push"){
steps{
sh "docker push dminus251/calculator:latest"
}
}
stage("Deploy to staging"){
steps{
sh "docker run -d --rm -p 8765:8081 --name calcForStaging dminus251/calculator:latest"
}
}
- 도커 허브에 이미지를 푸시하고, 인수 테스트를 위해 그 이미지의 도커 컨테이너를 실행합니다.
- -p 8765:8081 옵션을 통해 localhost의 8765포트로 이 컨테이너에 접근할 수 있습니다.
- 이 컨테이너의 이름을 calcForStaging으로 지정합니다.
4-8. Acceptance test
stage("Acceptance test"){
steps{
sleep 30 //docker run이 확실히 실행될 때까지 기다림
sh "docker logs calcForStaging"
sh "chmod +x acceptance_test.sh && ./acceptance_test.sh"
}
}
}
//까지 stages
- 도커 컨테이너가 확실히 실행될 때까지 기다립니다.
- docker logs 명령을 통해 컨테이너가 실행되는 포트, 컨테이너 실행 여부 등을 확인할 수 있습니다.
calcForStaging 컨테이너는 8081 포트에서 실행되는 것을 확인했습니다. - acceptance_test.sh 파일에 실행 권한을 부여하고, 이 쉘 스크립트를 실행합니다.
- acceptance_test.sh의 내용은 다음과 같습니다.
#!/bin/bash
#curl 결과를 result 변수에 저장
result=$(docker exec calcForStaging curl -s "http://localhost:8081/sum?a=1&b=2")
# 기대값을 설정: 100+172=272
expected=3
# 결과값이 기대값과 동일한지 체크
if [ "$result" -eq "$expected" ]; then
exit 0 #pass
else
echo "Test failed. expected $expected, but result is $result"
exit 1
fi
docker exec calcForStaging curl -s "http://localhost:8081/sum?a=1&b=2" 명령을 수행해 그 값을 result 변수에 저장합니다.
5. curl 관련 에러
여기서 고생한 점이 두 가지있는데요
1. curl 명령어를 실행할 땐 쌍따옴표 사용을 습관화해야겠습니다.
- curl -s http://localhost:8081/sum?a=1&b=2에서 &를 백그라운드 실행 연산자로 이식됩니다.
- 따라서 요청 주소가 올바르지 않게 됩니다.
2. 원래는 result 값을 $(curl -s "http://localhost:8081/sum?a=1&b=2")로 지정하려 했으나, Connection refused 에러가 지속적으로 발생했습니다.
두 명령의 차이점은 다음과 같습니다.
- $(docker exec calcForStaging curl -s "http://localhost:8081/sum?a=1&b=2")
- calcForStaging 컨테이너 내에서 curl 명령을 수행합니다. 즉, localhost는 calcForStaging 컨테이너가 됩니다.
- $(curl -s "http://localhost:8081/sum?a=1&b=2")
- 저는 wsl에서 도커가 실행 중이므로 localhost는 wsl이 됩니다. 즉, wsl의 8081포트에 curl 요청을 보내게 됩니다.
- localhost의 8765 포트가 컨테이너의 8081 포트에 매핑되어 있으므로 $(curl -s "http://localhost:8765/sum?a=1&b=2")를 사용해야 합니다.
- 아래 사진처럼 localhost:8765로 접근 시 정상적으로 a와 b의 합이 반환됩니다.
6. post
post{
always{
sh "docker stop calcForStaging"
}
}
}
- post 섹션에는 파이프라인 작업이 끝난 후 실행할 작업들을 정의합니다.
- 제 경우 젠킨스 파이프라인 빌드가 실패할 때를 대비해서 calcForStaging 컨테이너를 중지합니다.
- 4-6 Docker run에서 컨테이너를 실행할 때 --rm 옵션을 사용했습니다. 이 옵션을 사용하면 컨테이너가 중지될 때 볼륨과 컨테이너가 함께 삭제됩니다.
'Docker and Jenkins' 카테고리의 다른 글
[CI/CD] GitLab CI/CD 튜토리얼: 첫 파이프라인 생성하기 (0) | 2024.11.10 |
---|---|
[Jenkins] 트러블 슈팅: 젠킨스 도커인도커(DinD) 사용기 (2) | 2024.07.23 |
[Docker] https와 인증서를 이용해서 사설 도커 레지스트리 사용하기 (1) | 2024.07.20 |
[Jenkins] 코드 커버리지란?, JaCoCo와 Checkstyle (0) | 2024.07.17 |
[Jenkins] 파이프라인 구축하기 (2) | 2024.07.16 |