
Controller를 작성: CalculatorController.java
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class CalculatorController {
private final Calculator calculator;
@RequestMapping("/")
String sum(@RequestParam("a") Integer a, @RequestParam("b") Integer b) {
return String.valueOf(calculator.sum(a, b));
}
}
Service를 작성: Calculator.java
import org.springframework.stereotype.Service;
@Service
public class Calculator {
public Integer sum(Integer a, Integer b){
return a+b;
}
}

./gradlew clean build./mvnw clean build
build/libs 디렉토리에 저장: 실행할 애플리케이션(.jar)이 build 디렉토리에 존재
Dockerfile 을 프로젝트 디렉토리에 생성하고 작성
FROM bellsoft/liberica-openjdk-alpine:23
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
이미지 빌드
docker build -t 이미지이름 .
docker build -t calculator .

docker run -d --name calculator-container -p 8080:8080 calculatordocker ps

Jenkinsfile을 수정
pipeline {
agent any
stages {
stage("Permission") {
steps {
sh "chmod +x ./gradlew"
}
}
stage("Compile") {
steps {
sh "./gradlew compileJava"
}
}
stage("Test") {
steps {
sh "./gradlew test"
}
}
stage("Test Code Coverage"){
steps{
sh "./gradlew jacocoTestCoverageVerification"
sh "./gradlew jacocoTestReport"
}
}
stage("Gradle Build"){
steps{
sh "./gradlew clean build"
}
}
stage("Docker Build"){
steps{
sh "docker build -t jenkinspipeline ."
}
}
}
}
git push
git commit -am "Docker Build Stage added"
git push
Jenkins에서 확인 Docker Image Build 단계에서 실패

Jenkins에서 host docker 접근 권한 부여
sudo usermod -aG docker jenkins
sudo chown root:docker /var/run/docker.sockjenkins 재시작sudo systemctl restart jenkins


Jenkinsfile에 Credential ID를 변수로 등록
environment{
DOCKERHUB_CREDENTIALS = credentials("dockerhub-username-password")
}
ImageBuild Stage는 저장소 이름으로 변경하고 로그인 작성
stage("Docker Image Build"){
steps{
sh 'docker build -t yachae1101/calculator .'
}
}
stage('Docker Hub Login'){
steps{
sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
}
}
수정된 Jenkinsfile
pipeline {
agent any
environment{
DOCKERHUB_CREDENTIALS = credentials("dockerhub-username-password")
}
stages {
stage("Permission") {
steps {
sh "chmod +x ./gradlew"
}
}
stage("Compile") {
steps {
sh "./gradlew compileJava"
}
}
stage("Test") {
steps {
sh "./gradlew test"
}
}
stage("Test Code Coverage"){
steps{
sh "./gradlew jacocoTestCoverageVerification"
sh "./gradlew jacocoTestReport"
}
}
stage("Gradle Build"){
steps{
sh "./gradlew clean build"
}
}
stage("Docker Image Build"){
steps{
sh 'docker build -t yachae1101/calculator .'
}
}
stage('Docker Hub Login'){
steps{
sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
}
}
}
}
git push 후 확인
git commit -am "Docker Hub Login Stage added"
git push
로그인 성공

docker hub 에 image push 하는 stage 추가
stage('Docker Hub Push'){
steps{
sh 'docker push yachae1101/calculator:latest'
}
}
git push 후 확인
git commit -am "Docker Hub Push Stage added"
git push


env.VARNAME으로 사용이 가능, 이 변수는 전역 변수
https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables
currentBuild 라는 환경 변수는 현재 빌드에만 해당되는 지역 변수
number
result
currentResult
duration
keepLog
displayName
BUILD_NUMBER 환경 변수를 이용해서 docker image에 매번 다른 tag를 붙여서 배포하도록 Jenkinsfile 수정
pipeline {
agent any
environment{
DOCKERHUB_CREDENTIALS = credentials("dockerhub-username-password")
}
stages {
stage("Permission") {
steps {
sh "chmod +x ./gradlew"
}
}
stage("Compile") {
steps {
sh "./gradlew compileJava"
}
}
stage("Test") {
steps {
sh "./gradlew test"
}
}
stage("Test Code Coverage"){
steps{
sh "./gradlew jacocoTestCoverageVerification"
sh "./gradlew jacocoTestReport"
}
}
stage("Gradle Build"){
steps{
sh "./gradlew clean build"
}
}
stage("Docker Image Build"){
steps{
sh "docker build -t yachae1101/calculator:${env.BUILD_NUMBER} ."
}
}
stage('Docker Hub Login'){
steps{
sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
}
}
stage('Docker Hub Push'){
steps{
sh "docker push yachae1101/calculator:${env.BUILD_NUMBER}"
}
}
}
}


이렇게 하니까, image가 계속 쌓인다..

그래서 이번 최신 image 2개만 남기고 다른 image 들은 삭제하는 과정을 Jenkinsfile에 추가
stage('Clean Up Docker Images') {
steps {
script {
def imageTag = "${env.BUILD_NUMBER}"
def previousTag = (imageTag.toInteger() - 1).toString()
// Delete older images locally, keeping only the current and previous build images
sh """
docker images --filter=reference='yachae1101/calculator:*' --format '{{.Tag}}' | \
grep -Ev '^(${imageTag}|${previousTag})\$' | \
xargs -I {} docker rmi -f yachae1101/calculator:{}
"""
// 환경 변수로 Docker Hub 사용자 이름과 API 토큰 설정
withCredentials([usernamePassword(credentialsId: 'dockerhub-username-password', usernameVariable: 'DOCKERHUB_USR', passwordVariable: 'DOCKERHUB_TOKEN')]) {
// Clean up old images on Docker Hub using Docker Hub API with token
sh """
# Get the list of tags from Docker Hub and delete older images except the current and previous ones
curl -s -H "Authorization: JWT \$DOCKERHUB_TOKEN" \
"https://hub.docker.com/v2/repositories/yachae1101/calculator/tags/" | \
jq -r '.results[].name' | \
grep -Ev '^(${imageTag}|${previousTag})\$' | \
xargs -I {} curl -X DELETE -H "Authorization: JWT \$DOCKERHUB_TOKEN" \
"https://hub.docker.com/v2/repositories/yachae1101/calculator/tags/{}"
"""
}
}
}
}
docker images 

가장 최신 이미지에 대해서 latest 태그를 유지하도록 Jenkinsfile 수정
pipeline {
agent any
environment{
DOCKERHUB_CREDENTIALS = credentials("dockerhub-username-password")
}
stages {
stage("Permission") {
steps {
sh "chmod +x ./gradlew"
}
}
stage("Compile") {
steps {
sh "./gradlew compileJava"
}
}
stage("Test") {
steps {
sh "./gradlew test"
}
}
stage("Test Code Coverage"){
steps{
sh "./gradlew jacocoTestCoverageVerification"
sh "./gradlew jacocoTestReport"
}
}
stage("Gradle Build"){
steps{
sh "./gradlew clean build"
}
}
stage("Docker Image Build"){
steps{
sh "docker build -t yachae1101/calculator:${env.BUILD_NUMBER} ."
sh "docker tag yachae1101/calculator:${env.BUILD_NUMBER} yachae1101/calculator:latest"
}
}
stage('Docker Hub Login'){
steps{
sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
}
}
stage('Docker Hub Push'){
steps{
sh "docker push yachae1101/calculator:${env.BUILD_NUMBER}"
sh "docker push yachae1101/calculator:latest"
}
}
stage('Clean Up Docker Images') {
steps {
script {
def imageTag = "${env.BUILD_NUMBER}"
def previousTag = (imageTag.toInteger() - 1).toString()
// Delete older images locally, keeping only the current and previous build images
sh """
docker images --filter=reference='yachae1101/calculator:*' --format '{{.Tag}}' | \
grep -Ev '^(${imageTag}|${previousTag}|latest)\$' | \
xargs -I {} docker rmi -f yachae1101/calculator:{}
"""
// 환경 변수로 Docker Hub 사용자 이름과 API 토큰 설정
withCredentials([usernamePassword(credentialsId: 'dockerhub-username-password', usernameVariable: 'DOCKERHUB_USR', passwordVariable: 'DOCKERHUB_TOKEN')]) {
// Clean up old images on Docker Hub using Docker Hub API with token
sh """
# Get the list of tags from Docker Hub and delete older images except the current and previous ones
curl -s -H "Authorization: JWT \$DOCKERHUB_TOKEN" \
"https://hub.docker.com/v2/repositories/yachae1101/calculator/tags/" | \
jq -r '.results[].name' | \
grep -Ev '^(${imageTag}|${previousTag}|latest)\$' | \
xargs -I {} curl -X DELETE -H "Authorization: JWT \$DOCKERHUB_TOKEN" \
"https://hub.docker.com/v2/repositories/yachae1101/calculator/tags/{}"
"""
}
}
}
}
}
ubuntu 에서 이미지 확인
docker images
이미지가 2개씩만 남아있고, 가장 최신의 이미지에 대해서 latest 태그를 붙여서 유지


stage('Deploy'){
steps{
sh "docker run -d --rm -p 8765:8080 --name calculator yachae1101/calculator"
}
}git commit -am "Deploy Stage added"
git push


spring 프로젝트의 루트 디렉토리에 스크립트 파일 생성: acceptance_test.sh
#!/bin/bash
test $(curl "localhost:8765/?a=1&b=2") -eq 3
스크립트 파일을 실행하는 코드를 스테이지에 추가
stage('Acceptance Test'){
steps{
sleep 60
sh 'chmod +x acceptance_test.sh && ./acceptance_test.sh'
}
}
sleep을 사용하는 이유 : docker run -d 가 비동기 방식으로 실행되므로 연속해서 테스트 하는 명령을 사용하게 되면 컨테이너가 만들어지기 전에 테스트를 해서 테스트 단계에서 실패로 판정하는 경우가 발생할 수 있다.
Jenkins가 설치된 컴퓨터에 접속해서 실행 중인 컨테이저 중지
docker stop calculator
git add .
git commit -m "Acceptance Test Stage added"
git push
pipeline {
agent any
environment{
DOCKERHUB_CREDENTIALS = credentials("dockerhub-username-password")
}
stages {
stage("Permission") {
steps {
sh "chmod +x ./gradlew"
}
}
stage("Compile") {
steps {
sh "./gradlew compileJava"
}
}
stage("Test") {
steps {
sh "./gradlew test"
}
}
stage("Test Code Coverage"){
steps{
sh "./gradlew jacocoTestCoverageVerification"
sh "./gradlew jacocoTestReport"
}
}
stage("Gradle Build"){
steps{
sh "./gradlew clean build"
}
}
stage("Docker Image Build"){
steps{
sh "docker build -t yachae1101/calculator:${env.BUILD_NUMBER} ."
}
}
stage('Docker Hub Login'){
steps{
sh 'echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin'
}
}
stage('Docker Hub Push'){
steps{
sh "docker push yachae1101/calculator:${env.BUILD_NUMBER}"
}
}
stage('Deploy'){
steps{
sh "docker run -d --rm -p 8765:8080 --name calculator yachae1101/calculator:${env.BUILD_NUMBER}"
}
}
stage('Acceptance Test'){
steps{
sleep 60
sh 'chmod +x acceptance_test.sh && ./acceptance_test.sh'
}
}
}
post{
always{
sh 'docker stop calculator'
}
}
}