CloudNet@ 가시다님이 진행하는 단기 CI/CD 과정 Study 내용을
실습한 내용과 개인적인 경험을 정리하고자 합니다.
1주차 학습한 내용은 Jenkins CI/CD + Docker 입니다.
brew install docker --cask docker
brew install visual-studio-code --cask
# 코드 작성
mkdir 1.1 && cd 1.1
echo "print ('Hello Docker')" > hello.py
cat > Dockerfile <<EOF
FROM python:3
COPY . /app
WORKDIR /app
CMD python3 hello.py
EOF
# 컨테이너 이미지 빌드
docker pull python:3
docker build . -t hello
docker image ls -f reference=hello # -f 옵션은 이번 학습을 통해 알게 됨
# 컨테이너 실행
docker run --rm hello # 문자열 출력하는 Container 1회성으로 생성 후 삭제, 이미지는 유지 됨

# 코드 수정
echo "print ('Hello CloudNet@')" > hello.py
# 컨테이너 이미지 빌드 : latest 활용 해보자!
docker build . -t hello:1 # Tag로 1을 부여
docker image ls -f reference=hello
docker tag hello:1 hello:latest # 최신 버전 요청시 참고하도록 latest tag 부여
docker image ls -f reference=hello
# 컨테이너 실행
docker run --rm hello:1
docker run --rm hello # Tag 생략 시 latest 참고 함

CI/CD 빌드 배포 전체 과정 중, 컨테이너 이미지 Tag 태그은 어떻게 사용 해야 될까 고민해보자.
🤔 Jenkins 빌드 시 IMAGE_TAG에 BUILD_ID 또는 GIT_COMMIT 해시를 추가해 태그 관리
# 코드 작성
mkdir 1.2 && cd 1.2
cat > Hello.java <<EOF
class Hello {
public static void main(String[] args) {
System.out.println("Hello Docker");
}
}
EOF
cat > Dockerfile <<EOF
FROM openjdk
COPY . /app
WORKDIR /app
RUN javac Hello.java # The complie command
CMD java Hello # hello code run command
EOF
# 컨테이너 이미지 빌드
docker pull openjdk
docker build . -t hello:2
docker tag hello:2 hello:latest
docker image ls -f reference=hello
# 컨테이너 실행
docker run --rm hello:2 # openjdk download, hello.java build, hello.class 실행
docker run --rm hello # openjdk download, hello.java build, hello.class 실행
# 컨테이너 이미지 내부에 파일 목록을 보면 어떤가요? 꼭 필요한 파일만 있는가요? 보안적으로 어떨까요?
docker run --rm hello ls -l # 소스와 java compiler 존재, container 해킹된 경우 악의적으로 코드 변경 가능
# RUN 컴파일 시 소스코드와 java 컴파일러(javac)가 포함되어 있음. 실제 애플리케이션 실행에 필요 없음.
docker run --rm hello javac --help # javac 실행 가능
docker run --rm hello ls -l

보안을 위해 소스 코드를 build 하는 환경과 실행하는 환경을 분리하기 위해 Dockerfile을 2 step 으로 처리

코드 작성 & 실행
# 코드 작성
mkdir 1.3 && cd 1.3
cat > Hello.java <<EOF
class Hello {
public static void main(String[] args) {
System.out.println("Hello Multistage container build");
}
}
EOF
cat > Dockerfile <<EOF
FROM openjdk:11 AS buildstage
COPY . /app
WORKDIR /app
RUN javac Hello.java
FROM openjdk:11-jre-slim
COPY --from=buildstage /app/Hello.class /app/
WORKDIR /app
CMD java Hello
EOF
# 컨테이너 이미지 빌드 : 용량 비교 해보자!
docker build . -t hello:3
docker tag hello:3 hello:latest
docker image ls -f reference=hello
# 컨테이너 실행
docker run --rm hello:3
docker run --rm hello
# 컨테이너 이미지 내부에 파일 목록을 보면 어떤가요?
docker run --rm hello ls -l # Compile된 Hello.class 만 조회
docker run --rm hello javac --help # Compiler 존재하지 않아 의도한 Error 발생
# 작업 디렉토리 생성 후 이동
mkdir cicd-labs
cd cicd-labs
#
cat <<EOT > docker-compose.yaml
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins
restart: unless-stopped
networks:
- cicd-network
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- jenkins_home:/var/jenkins_home
gogs:
container_name: gogs
image: gogs/gogs
restart: unless-stopped
networks:
- cicd-network
ports:
- "10022:22"
- "3000:3000"
volumes:
- gogs-data:/data
volumes:
jenkins_home:
gogs-data:
networks:
cicd-network:
driver: bridge
EOT
# 배포
docker compose up -d
docker compose ps

# 기본 정보 확인
for i in gogs jenkins ; do echo ">> container : $i <<"; docker compose exec $i sh -c "whoami && pwd"; echo; done
# 도커를 이용하여 각 컨테이너로 접속
docker compose exec jenkins bash
exit
docker compose exec gogs bash
exit

# Jenkins 초기 암호 확인
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
11977f293ddb402a9189fb8e71f2e69f
# Jenkins 웹 접속 주소 확인 : 계정 / 암호 입력 >> admin / qwe123
open "http://127.0.0.1:8080" # macOS
웹 브라우저에서 http://127.0.0.1:8080 접속 # Windows
# (참고) 로그 확인 : 플러그인 설치 과정 확인
docker compose logs jenkins -f




# 실습 완료 후 해당 컨테이너 중지 상태로 둘 경우
docker compose stop
docker compose ps
docker compose ps -a
# mac 재부팅 및 이후에 다시 실습을 위해 컨테이너 시작 시
docker compose start
docker compose ps
# gogs : 볼륨까지 삭제
docker compose down gogs -v
docker compose up gogs -d
# jenkins : 볼륨까지 삭제
docker compose down jenkins -v
docker compose up jenkins -d

Jenkins는 2004년, Sun Microsystems의 개발자 Kohsuke Kawaguchi에 의해 Hudson이라는 이름으로 시작되었습니다. 개발 과정에서 반복되는 작업을 자동화하려는 필요성을 느껴, CI(Continuous Integration)를 구현하는 오픈소스 도구를 만들었습니다.
Jenkins는 CI/CD 파이프라인의 중심 허브로, 소프트웨어 빌드, 테스트, 배포 과정을 자동화합니다.
주요 역할:
Jenkins의 확장성을 높이는 주요 플러그인은 다음과 같습니다:
1) 설치
Docker로 설치:
docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:lts
Linux 설치 (예: Ubuntu):
sudo apt update
sudo apt install openjdk-11-jre
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins.io/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt update
sudo apt install jenkins
2) 초기 설정
/var/lib/jenkins/secrets/initialAdminPassword에서 확인.3) Jenkinsfile 작성
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building...'
}
}
stage('Test') {
steps {
echo 'Testing...'
}
}
stage('Deploy') {
steps {
echo 'Deploying...'
}
}
}
}
4) 파이프라인 설정
5) 트리거 설정
Jenkins는 다양한 환경에 적응 가능하며, 소프트웨어 개발 워크플로우를 효율적으로 관리할 수 있는 강력한 도구입니다. CI/CD를 시작하거나 확장하려는 팀에게 필수적인 선택지입니다! 🚀
Gogs(Go Git Service)는 Git 저장소를 호스팅할 수 있는 오픈소스 경량 소프트웨어입니다. Gogs는 Go 언어로 개발되었으며, 간단하고 효율적인 Git 호스팅 환경을 제공하는 것이 주요 목표입니다.
Gogs는 단일 바이너리로 배포되므로 간단하게 설치할 수 있습니다. 아래는 기본 설치 절차입니다:
Gogs는 단순성과 가벼움을 중시하며, 복잡한 설정이나 대규모 프로젝트를 지원하는 데는 한계가 있을 수 있습니다. 만약 더 큰 프로젝트나 CI/CD와의 강력한 통합을 원한다면 GitLab이나 Gitea 같은 대안을 고려하는 것도 좋습니다.
docker compose exec --privileged -u root jenkins bash
-----------------------------------------------------
id
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update && apt install docker-ce-cli curl tree jq -y
docker info
docker ps
which docker
# Jenkins 컨테이너 내부에서 root가 아닌 jenkins 유저도 docker를 실행할 수 있도록 권한을 부여
groupadd -g 2000 -f docker
chgrp docker /var/run/docker.sock
ls -l /var/run/docker.sock
usermod -aG docker jenkins
cat /etc/group | grep docker
exit
--------------------------------------------

# jenkins item 실행 시 docker 명령 실행 권한 에러 발생 : Jenkins 컨테이너 재기동으로 위 설정 내용을 Jenkins app 에도 적용 필요
docker compose restart jenkins
sudo docker compose restart jenkins # Windows 경우 이후부터 sudo 붙여서 실행하자
# jenkins user로 docker 명령 실행 확인
docker compose exec jenkins id
docker compose exec jenkins docker info
docker compose exec jenkins docker ps
❯ docker compose exec jenkins id
uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),2000(docker)
❯ docker compose exec jenkins docker info
Client: Docker Engine - Community
Version: 27.3.1
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.17.1
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.29.7
Path: /usr/libexec/docker/cli-plugins/docker-compose
Server:
Containers: 2
Running: 2
Paused: 0
Stopped: 0
Images: 12
Server Version: 27.3.1
Storage Driver: overlayfs
driver-type: io.containerd.snapshotter.v1
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 2
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 472731909fa34bd7bc9c087e4c27943f9835f111
runc version: v1.1.13-0-g58aa920
init version: de40ad0
Security Options:
seccomp
Profile: unconfined
cgroupns
Kernel Version: 6.10.14-linuxkit
Operating System: Docker Desktop
OSType: linux
Architecture: aarch64
CPUs: 4
Total Memory: 7.654GiB
Name: docker-desktop
ID: 3c04a1af-86e9-44fe-91ed-cc64e951af51
Docker Root Dir: /var/lib/docker
Debug Mode: false
HTTP Proxy: http.docker.internal:3128
HTTPS Proxy: http.docker.internal:3128
No Proxy: hubproxy.docker.internal
Labels:
com.docker.desktop.address=unix:///Users/sjkim/Library/Containers/com.docker.docker/Data/docker-cli.sock
Experimental: false
Insecure Registries:
hubproxy.docker.internal:5555
127.0.0.0/8
Live Restore Enabled: false
WARNING: daemon is not using the default seccomp profile
❯ docker compose exec jenkins docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9c9559523210 jenkins/jenkins "/usr/bin/tini -- /u…" 10 hours ago Up 40 seconds 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp jenkins
ad547a6a8954 gogs/gogs "/app/gogs/docker/st…" 10 hours ago Up 48 minutes (healthy) 0.0.0.0:3000->3000/tcp, 0.0.0.0:10022->22/tcp gogs
# 초기 설정 웹 접속
open "http://127.0.0.1:3000/install" # macOS
웹 브라우저에서 http://127.0.0.1:3000/install 접속 # Windows
http://<각자 자신의 PC IP>:3000/
docker compose exec gogs ls -l /data
docker compose exec gogs ls -l /data/gogs
docker compose exec gogs ls -l /data/gogs/conf
docker compose exec gogs cat /data/gogs/conf/app.ini
❯ docker compose exec gogs ls -l /data
total 12
drwxr-xr-x 4 git git 4096 Dec 7 00:19 git
drwxr-xr-x 5 git git 4096 Dec 6 13:40 gogs
drwx------ 2 git git 4096 Dec 6 13:40 ssh
❯ docker compose exec gogs ls -l /data/gogs
total 12
drwxr-xr-x 2 git git 4096 Dec 7 00:19 conf
drwxr-xr-x 2 git git 4096 Dec 7 00:19 data
drwxr-xr-x 2 git git 4096 Dec 7 00:19 log
❯ docker compose exec gogs ls -l /data/gogs/conf
total 4
-rw-r--r-- 1 git git 943 Dec 7 00:19 app.ini
❯ docker compose exec gogs cat /data/gogs/conf/app.ini
BRAND_NAME = Gogs
RUN_USER = git
RUN_MODE = prod
[database]
TYPE = sqlite3
HOST = 127.0.0.1:5432
NAME = gogs
SCHEMA = public
USER = gogs
PASSWORD =
SSL_MODE = disable
PATH = data/gogs.db
[repository]
ROOT = /data/git/gogs-repositories
DEFAULT_BRANCH = main
[server]
DOMAIN = localhost
HTTP_PORT = 3000
EXTERNAL_URL = http://mypc:3000/
DISABLE_SSH = false
SSH_PORT = 22
START_SSH_SERVER = false
OFFLINE_MODE = false
[email]
ENABLED = false
[auth]
REQUIRE_EMAIL_CONFIRMATION = false
DISABLE_REGISTRATION = false
ENABLE_REGISTRATION_CAPTCHA = true
REQUIRE_SIGNIN_VIEW = false
[user]
ENABLE_EMAIL_NOTIFICATION = false
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file
[log]
MODE = file
LEVEL = Info
ROOT_PATH = /app/gogs/log
[security]
INSTALL_LOCK = true
SECRET_KEY = ZjrZVucuM2H5yIS
로그인 후 → Your Settings → Applications : Generate New Token 클릭 - Token Name(devops) ⇒ Generate Token 클릭 : 메모해두기! ( fdf9e1c14efd863daf83c8f7e15a250a77894013 )

New Repository


#
docker compose exec jenkins bash
-----------------------------------
whoami
pwd
cd /var/jenkins_home/
tree
#
git config --global user.name "<Gogs 계정명>"
git config --global user.name "devops"
git config --global user.email "a@a.com"
git config --global init.defaultBranch main
#
git clone <각자 Gogs dev-app repo 주소>
git clone http://192.168.254.124:3000/devops/dev-app.git
Cloning into 'dev-app'...
Username for 'http://192.168.254.124:3000': devops # Gogs 계정명
Password for 'http://devops@192.168.254.124:3000': <토큰> # 혹은 계정암호
...
#
tree dev-app
cd dev-app
git branch
git remote -v
# server.py 파일 작성
cat > server.py <<EOF
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from datetime import datetime
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
now = datetime.now()
response_string = now.strftime("The time is %-I:%M:%S %p, CloudNeta Study.\n")
self.wfile.write(bytes(response_string, "utf-8"))
def startServer():
try:
server = ThreadingHTTPServer(('', 80), RequestHandler)
print("Listening on " + ":".join(map(str, server.server_address)))
server.serve_forever()
except KeyboardInterrupt:
server.shutdown()
if __name__== "__main__":
startServer()
EOF
# Dockerfile 생성
cat > Dockerfile <<EOF
FROM python:3.12
ENV PYTHONUNBUFFERED 1
COPY . /app
WORKDIR /app
CMD python3 server.py
EOF
# VERSION 파일 생성
echo "0.0.1" > VERSION
#
git add .
git commit -m "Add dev-app"
git push -u origin main
...

Docker Hub는 Docker 이미지를 저장, 공유, 관리할 수 있는 클라우드 기반의 레지스트리 서비스입니다. 주로 애플리케이션 배포와 컨테이너화된 환경에서 많이 사용됩니다.
이미지 저장소
Docker Hub는 컨테이너 이미지를 저장하는 중앙화된 플랫폼입니다. 사용자는 이미지를 퍼블릭(public) 또는 프라이빗(private)으로 저장할 수 있습니다.
이미지 공유
Docker Hub에 저장된 이미지를 다른 사용자 또는 팀과 쉽게 공유할 수 있습니다. 오픈 소스 프로젝트에서 자주 사용됩니다.
자동화 빌드
GitHub 또는 Bitbucket과 연동하여 코드가 업데이트될 때 Docker 이미지를 자동으로 빌드하고 배포할 수 있습니다.
공식 이미지 제공
Docker Hub에는 검증된 공식 이미지(예: Python, MySQL, Nginx)가 제공됩니다. 이는 신뢰할 수 있는 이미지로서 바로 사용할 수 있습니다.
검색 기능
다양한 이미지(공식, 사용자 제공)를 검색하고 다운로드할 수 있습니다.
Webhooks
Docker Hub는 Webhooks를 제공하여, 특정 이미지의 업데이트가 있을 때 알림을 통해 CI/CD 파이프라인과 통합할 수 있습니다.
계정 생성과정 설명은 생략하겠습니다.
Python Demo 컨테이너 Image Build / Upload
# Dockerfile 파일 작성
cat > Dockerfile <<EOF
FROM busybox
CMD echo "Hello world! This is my first Docker image."
EOF
cat Dockerfile
# 빌드
DOCKERID=<자신의 도커 계정명>
DOCKERREPO=<자신의 도커 저장소>
DOCKERID=kimseongjung
DOCKERREPO=dev-test-app
docker build -t $DOCKERID/$DOCKERREPO .
docker images | grep $DOCKERID/$DOCKERREPO
# 실행 확인
docker run $DOCKERID/$DOCKERREPO
# 로그인 확인
docker login - icebreaker70@gmail.com
# 푸시
docker push $DOCKERID/$DOCKERREPO

push 후 확인

🍪 Name your local images using one of these methods:
docker build -t <hub-user>/<repo-name>[:<tag>docker tag <existing-image> <hub-user>/<repo-name>[:<tag>].docker commit <existing-container> <hub-user>/<repo-name>[:<tag>] to commit changes.프로젝트 활성화/비활성화 오른쪽 위 토글 버튼 Enabled , Disabled
오래된 빌드 삭제 Discards old builds : 삭제 시기 결정 옵션 - 기간(기본값 14일), 회숫(기본값 50개) → 제한값 초과 시 오래된 빌드 삭제

매개변수형 프로젝트 (이 빌드는 매개변수가 있습니다 This project is parameterized) : 외부 입력 받을 때 사용

String Parameter 클릭 : 매개변수 명(FirstPara) , Default Value(xangle)
Build Steps : Execute shell 에 아래 입력
echo "{$FirstPara}"
‘파라미터와 함께 빌드’ 클릭 → 매개변수 확인 후 ‘매개변수가 필요한 빌드입니다’ 클릭

Console Output 에서 확인

동시 빌드 실행 (필요한 경우 concurrent 빌드 실행) Execute concurrent builds if necessary
고급 옵션 Advanced
echo "docker check" | tee test.txt
docker ps



http://***<mac IP>***:3000/***<Gogs 계정명>***/dev-app ← .git 은 제거*/main
echo "{$FirstPara}"
cat VERSION
Save 후 ‘파라미터와 함께 빌드’

Console Output 확인 : 과정 동작 확인

Item 이름별 작업 공간 workspace 확인
docker compose exec jenkins ls /var/jenkins_home/workspace
docker compose exec jenkins tree /var/jenkins_home/workspace/second
docker compose exec jenkins ls -l /var/jenkins_home/workspace/second

해결방법
$ docker compose exec gogs sh
$ vi /data/gogs/conf/app.ini 구성 파일을 수정하고
LOCAL_NETWORK_ALLOWLIST Local IP 주소를 콤마로 구분하여 추가한 후 서비스를 다시 시작하세요.


CD 파이프라인을 구현하고 통합할 때 사용하는 플러그인 스크립트 모음

파이프라인 장점
파이프라인 용어

파이프라인 3가지 구성 형태
Jenkinsfile manually, which you can commit to your project’s source control repository.
Jenkinsfile and commit it to source control.
파이프라인 2가지 구문 : 선언형 파이프라인(권장)과 스크립트형 파이프라인

선언형 Declarative 파이프라인
pipeline {
agent any # Execute this Pipeline or any of its stages, on any available agent.
stages {
stage('Build') { # Defines the "Build" stage.
steps {
// # Perform some steps related to the "Build" stage.
}
}
stage('Test') {
steps {
//
}
}
stage('Deploy') {
steps {
//
}
}
}
}
node { # Execute this Pipeline or any of its stages, on any available agent.
stage('Build') { # Defines the "Build" stage. stage blocks are optional in Scripted Pipeline syntax. However, implementing stage blocks in a Scripted Pipeline provides clearer visualization of each stage's subset of tasks/steps in the Jenkins UI.
// # Perform some steps related to the "Build" stage.
}
stage('Test') {
//
}
stage('Deploy') {
//
}
}
Item : First-Pipeline -> Pipeline (Pipeline script - 아래 내용 복붙)
먼저 샘플 파이프라인 테스트

아래 처럼 수정 후 확인: 환경변수 사용, 문자열 보간 → Console Output 확인
pipeline {
agent any
environment {
CC = 'clang'
}
stages {
stage('Example') {
environment {
AN_ACCESS_KEY = 'abcdefg'
}
steps {
echo "${CC}";
sh 'echo ${AN_ACCESS_KEY}'
}
}
}
}

아래 처럼 수정 후 확인: 파이프라인 빌드 시작(트리거) → Console Output 확인
pipeline {
agent any
triggers {
cron('H */4 * * 1-5')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
아래 처럼 수정 후 확인: 파라미터와 함께 빌드 → Console Output 확인 ⇒ 다시 한번 더 빌드 클릭 (변수 입력 칸 확인)
pipeline {
agent any
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
}
stages {
stage('Example') {
steps {
echo "Hello ${params.PERSON}"
echo "Biography: ${params.BIOGRAPHY}"
echo "Toggle: ${params.TOGGLE}"
echo "Choice: ${params.CHOICE}"
echo "Password: ${params.PASSWORD}"
}
}
}
}
아래 처럼 수정 후 확인: post (빌드 후 조치) → Console Output 확인
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
post {
always {
echo 'I will always say Hello again!'
}
}
}
아래 처럼 수정 후 확인
pipeline {
agent any
stages {
stage('Compile') {
steps {
echo "Compiled successfully!";
}
}
stage('JUnit') {
steps {
echo "JUnit passed successfully!";
}
}
stage('Code Analysis') {
steps {
echo "Code Analysis completed successfully!";
}
}
stage('Deploy') {
steps {
echo "Deployed successfully!";
}
}
}
}
지금 빌드! : 빌드 단계 걸린 시간, 빌드 단계 별 로그
Item : My-First-Pipeline , Pipeline(Pipeline script - 아래 내용 추가 복붙) post 항목 : 빌드 성공 실패 등 메시지 출력
pipeline {
agent any
stages {
stage('Compile') {
steps {
echo "Compiled successfully!";
}
}
stage('JUnit') {
steps {
echo "JUnit passed successfully!";
}
}
stage('Code Analysis') {
steps {
echo "Code Analysis completed successfully!";
}
}
stage('Deploy') {
steps {
echo "Deployed successfully!";
}
}
}
post {
always {
echo "This will always run"
}
success {
echo "This will run when the run finished successfully"
}
failure {
echo "This will run if failed"
}
unstable {
echo "This will run when the run was marked as unstable"
}
changed {
echo "This will run when the state of the pipeline has changed"
}
}
}
pipeline {
agent any
environment {
DOCKER_IMAGE = '<자신의 도커 허브 계정>/dev-app' // Docker 이미지 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://mypc:3000/devops/dev-app.git', // Git에서 코드 체크아웃
credentialsId: 'gogs-dev-app' // Credentials ID
}
}
stage('Read VERSION') {
steps {
script {
// VERSION 파일 읽기
def version = readFile('VERSION').trim()
echo "Version found: ${version}"
// 환경 변수 설정
env.DOCKER_TAG = version
}
}
}
stage('Docker Build and Push') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', 'dockerhub-credentials') {
// DOCKER_TAG 사용
def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
appImage.push()
}
}
}
}
}
post {
success {
echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
}
failure {
echo "Pipeline failed. Please check the logs."
}
}
}





[security]
INSTALL_LOCK = true
SECRET_KEY = ZjrZVucuM2H5yIS
LOCAL_NETWORK_ALLOWLIST = mypc # 각자 자신의 PC IP

docker compose restart gogs
Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs,
we will handle the notification to the target host you specify. %!(EXTRA string=https://gogs.io/docs/features/webhook.html)

Payload URL : http://mypc:8080/gogs-webhook/?job=**SCM-Pipeline**/
Content Type : application/json
Secret : qwe123
When should this webhook be triggered? : Just the push event
Active : Check
⇒ Add webhook

http://***<mac IP>***:3000/***<Gogs 계정명>***/dev-app ← .git 은 제거http://***<mac IP>***:3000/***<Gogs 계정명>***/dev-app)

# Jenkinsfile 빈 파일 작성
docker compose exec jenkins touch /var/jenkins_home/dev-app/Jenkinsfile
# 버전 0.0.2 수정 : 아래 입력 잘 안될 경우 VSCODE(Docker플러그인)에서 직접 수정
docker compose exec jenkins sh -c 'echo "0.0.2" > /var/jenkins_home/dev-app/VERSION'
pipeline {
agent any
environment {
DOCKER_IMAGE = 'kimseongjung/dev-app' // Docker 이미지 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://mypc:3000/devops/dev-app.git', // Git에서 코드 체크아웃
credentialsId: 'gogs-dev-app' // Credentials ID
}
}
stage('Read VERSION') {
steps {
script {
// VERSION 파일 읽기
def version = readFile('VERSION').trim()
echo "Version found: ${version}"
// 환경 변수 설정
env.DOCKER_TAG = version
}
}
}
stage('Docker Build and Push') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', 'dockerhub-credentials') {
// DOCKER_TAG 사용
def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
appImage.push()
}
}
}
}
}
post {
success {
echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
}
failure {
echo "Pipeline failed. Please check the logs."
}
}
}
#
docker compose exec jenkins bash
---------------------------------
cd /var/jenkins_home/dev-app/
git add . && git commit -m "Jenkinsfile add & VERSION 0.0.2 Changed" && git push -u origin main

Jenkins 트리거 빌드 확인

도커 저장소 확인

Gogs WebHook 기록 확인


pipeline {
agent any
environment {
DOCKER_IMAGE = 'kimseongjung/dev-app' // Docker 이미지 이름
CONTAINER_NAME = 'dev-app' // 컨테이너 이름
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'http://mypc:3000/devops/dev-app.git', // Git에서 코드 체크아웃
credentialsId: 'gogs-dev-app' // Credentials ID
}
}
stage('Read VERSION') {
steps {
script {
// VERSION 파일 읽기
def version = readFile('VERSION').trim()
echo "Version found: ${version}"
// 환경 변수 설정
env.DOCKER_TAG = version
}
}
}
stage('Docker Build and Push') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', 'dockerhub-credentials') {
// DOCKER_TAG 사용
def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
appImage.push()
appImage.push("latest") // 빌드 이미지 push 할 때, 2개의 버전(현재 버전, latest 버전)을 업로드
}
}
}
}
stage('Check, Stop and Run Docker Container') {
steps {
script {
// 실행 중인 컨테이너 확인
def isRunning = sh(
script: "docker ps -q -f name=${CONTAINER_NAME}",
returnStdout: true
).trim()
if (isRunning) {
echo "Container '${CONTAINER_NAME}' is already running. Stopping it..."
// 실행 중인 컨테이너 중지
sh "docker stop ${CONTAINER_NAME}"
// 컨테이너 제거
sh "docker rm ${CONTAINER_NAME}"
echo "Container '${CONTAINER_NAME}' stopped and removed."
} else {
echo "Container '${CONTAINER_NAME}' is not running."
}
// 5초 대기
echo "Waiting for 5 seconds before starting the new container..."
sleep(5)
// 신규 컨테이너 실행
echo "Starting a new container '${CONTAINER_NAME}'..."
sh """
// PortPara 미터 변수 처리
docker run -d --name ${CONTAINER_NAME} -p ${PortPara}:80 ${DOCKER_IMAGE}:${DOCKER_TAG}
"""
}
}
}
}
post {
success {
echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
}
failure {
echo "Pipeline failed. Please check the logs."
}
}
}
docker image
docker ps
curl http://127.0.0.1:4000
# server.py 수정
response_string = now.strftime("The time is %-I:%M:%S %p, Study 1week End.\n")
# VERSION 수정
# Jenkins 컨테이너 내부에서 git push
jenkins@5c1ba7016f9e:~/dev-app$ git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main
# 호스트 PC에서 반복 접속 실행 : 서비스 중단 시간 체크!
while true; do curl -s --connect-timeout 1 http://127.0.0.1:4000 ; date; sleep 1 ; done

CloudNet@ 가시다님 주관 CI/CD 1주차 Study를 통해
심도 있게 이론과 실습위주로 Jenkins에 대해 학습을 하였고,
Dockerhub연동, Gogs를 통해 소스 형상관리 및 Build 트리거 하는 과정 전반에 대해
학습을 할 수 있어서 막연하게 인식했던 CI/CD를 몸소 체험할 수 있어서 매우 좋았다.
감사합니다.