새소식

CICD

[CI/CD Study 3주차] Jenkins, Gogs 를 이용한 CI/CD 배포

  • -

CoudNet@ 팀의 가시다님께서 리딩하시는 CI/CD Study 3주차 스터디 내용 정리

 

 

이번 주차에서는 Jenkins, Gogs, ArgoCD 를 통한 CI/CD 에 대해서 학습했습니다.




0. 실습 환경 구성



이번 주차의 실습은 Kind 를 통해 2개의 노드(컨트롤 플레인, 워커 노드)로 구성하였으며,
실습에 필요한 Jenkins 와 Gogs 는 Docker 로 구성했습니다.

 

0.1. Kind 배포

 

# Kind 클러스터 배포
kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  apiServerAddress: "0.0.0.0"
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
  - containerPort: 30003
    hostPort: 30003
- role: worker
EOF



0.2. Jenkins, Gogs 배포

 

# Docker Compose 파일 생성
cat <<EOT > docker-compose.yaml
services:

  jenkins:
    container_name: jenkins
    image: jenkins/jenkins
    restart: unless-stopped
    networks:
      - cicd-network
    ports:
      - "8080:8080"
      - "50000:50000"                      # Jenkins Agent - Controller : JNLP
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./jenkins_home:/var/jenkins_home   # (방안1) 권한등으로 실패 시 ./ 제거하여 도커 볼륨으로 사용 (방안2)

  gogs:
    container_name: gogs
    image: gogs/gogs
    restart: unless-stopped
    networks:
      - cicd-network
    ports:
      - "10022:22"                         # Git Clinet - Git SSH Service : push, pull, clone
      - "3000:3000"
    volumes:
      - ./gogs-data:/data                  # (방안1) 권한등으로 실패 시 ./ 제거하여 도커 볼륨으로 사용 (방안2)

volumes:
  jenkins_home:
  gogs-data:

networks:
  cicd-network:
    driver: bridge
EOT

# Docker 실행
docker compose up -d

 

Jenkins 초기 비밀번호 확인

 

Jenkins 를 생성했기 때문에, 초기 비밀번호 로그인을 통해 Jenkins 설정이 필요합니다.

 

# 초기 비밀번호 확인
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

# Jenkins 오픈
open "http://127.0.0.1:8080"

 

비밀 번호 입력 후 Suggested Plugin 을 설치합니다.

 

 

그 후 로그인할 어드민 계정을 설정해줍니다.
실습인 관계로 admin / admin123 으로 설정했습니다.

 

 

그리고 IP 는 본인 노트북이 사용하고 있는 사설 IP 로 설정합니다.
저는 192.168.200.121 를 사용하고 있습니다.

 

ifconfig | grep 192

 

 

0.3. Jenkins 컨테이너에 Docker 설치

 

Jenkins 에서 컨테이너 빌드를 위해 Docker 를 설치해줍니다.

 

Jenkins 컨테이너 실행 시 Docker 소켓을 바인딩했으므로
Docker CLI 를 설치하면 Host(내 PC)의 Docker Server 와 통신할 수 있는 상태가 됩니다. (Docker out of Docker)

 

# Jenkins 컨테이너 진입
docker compose exec --privileged -u root jenkins bash

# Docker 설치
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 yq wget -y

# Docker cli 실행
docker info
docker ps

 

Jenkins 컨테이너에서 docker ps 를 실행했음에도 컨테이너 밖의 PC 에서 실행되고 있는 컨테이너가 전부 보이는 것을 알 수 있습니다.

 

 

Jenkins 컨테이너에서 root 가 아닌 사용자도 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

# 변경된 권한 적용을 위해 컨테이너 재기동
docker compose restart jenkins

# 권한 확인
docker compose exec jenkins docker ps



0.4. Gogs 설정

 

Gogs 는 경량화된 Git 서비스를 셀프 호스팅할 수 있는 도구입니다.
여기서는 Github 나 Gitlab 대신에 간단하게 사용할 수 있는 Gogs 를 사용했습니다.

 

이미 Docker Compose 로 함께 배포가 되었기 때문에 Gogs 설정을 진행합니다.

 

# Gogs 오픈
open "http://127.0.0.1:3000/install"

 

변경할 부분은 데이터 베이스 유형, 애플리케이션 URL, 관리자 계정 등입니다.

  • 데이터베이스 : SQLite3

 

 

  • 애플리케이션 URL : 본인 PC 사설 IP 주소
  • 기본 브랜치 : main

 

  • 관리자 계정 ID : devops

 

 

이후 초기 설정이 완료가 되면 로그인하고 사용자 토큰을 발급받습니다.

 

 

그리고 CICD 실습을 위해 2개의 Git Repository 를 생성합니다.

 



0.5. Gogs 저장소 설정

 

로컬 PC 에서 설정된 Gogs 저장소를 클론한 후 작업을 해줍니다.

 

# 변수 설정
# TOKEN: Gogs 에서 발급받았던 사용자 토큰 입력
# MyIP: PC 사설 IP
TOKEN=e3744bb0fdf48783c8c1c166dd7b5e13090112f2
MyIP=192.168.200.121

# dev-app 레포지토리 클론
git clone http://devops:$TOKEN@$MyIP:3000/devops/dev-app.git
cd dev-app

# 로컬 디렉토리 Git Config 설정
git config --local user.name "devops"
git config --local user.email "a@a.com"
git config --local init.defaultBranch main
git config --local credential.helper store
git --no-pager config --local --list
cat .git/config

 

Push 할 파일 생성

 

cat > server.py <<EOF
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from datetime import datetime
import socket

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        match self.path:
            case '/':
                now = datetime.now()
                hostname = socket.gethostname()
                response_string = now.strftime("The time is %-I:%M:%S %p, VERSION 0.0.1\n")
                response_string += f"Server hostname: {hostname}\n"                
                self.respond_with(200, response_string)
            case '/healthz':
                self.respond_with(200, "Healthy")
            case _:
                self.respond_with(404, "Not Found")

    def respond_with(self, status_code: int, content: str) -> None:
        self.send_response(status_code)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write(bytes(content, "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

# Gogs 저장소 Push
git add .
git commit -m "Add dev-app"
git push -u origin main

 

업로드가 잘 된것을 확인했습니다.

 



0.6. 도커 허브 설정

 

빌드된 Docker 이미지를 저장하기 위해 Dockerhub 에서 토큰을 발급한 후,
Private Repository 를 하나 생성해줍니다.

 

  • 저장소 명 : dev-app

 




1. Jenkins Pipeline 실행

 

이제 모든 실습 환경 구성이 끝났으니 Jenkins 부터 실행해보겠습니다.
Jenkins 는 전통적인 CI/CD 배포 도구로 수많은 플러그인 덕분에 다양한 배포 과정에서 사용되는 강력한 오픈소스입니다.



1.1. Jenkins Plugin 설치

 

실습에 사용되는 플러그인만 3개 설치해서 세팅하겠습니다.

 

이 중 Gogs 플러그인은 더이상 유지보수 되지 않아 심각한 보안 이슈가 있는 것으로 보이기 때문에,
이번 실습 외에는 절대 사용하면 안됩니다.

 

1. Pipeline: Stage View
2. Docker Pipeline
3. Gogs

 



1.2. Jenkins 자격증명 설정

 

Jenkins 파이프라인 실행을 위해 Credentials 에서 Gogs 와 Dockerhub 자격증명을 설정합니다.

 

Gogs-crd

 

  • Kind : Username with password
  • Username : devops
  • Password : <Gogs 토큰>
  • ID : gogs-crd

 

dockerhub-crd

 

  • Kind : Username with password
  • Username : <도커 계정명>
  • Password : <도커 계정 암호 혹은 토큰>
  • ID : dockerhub-crd

 



1.3. Jenkins Pipeline 생성

 

초기 파이프라인을 생성해서 잘 동작하는 지 확인합니다.
Gogs 저장소에 Push 된 코드를 빌드하고 Dockerhub 에 이미지를 업로드하는 파이프라인입니다.

 

아래 파이프라인 스크립트를 설치한 Jenkins 에 넣어줍니다.

 

pipeline {
    agent any
    environment {
        DOCKER_IMAGE = 'kimalarm2/dev-app' // Dockerhub 저장소 명칭
    }
    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.200.121:3000/devops/dev-app.git', // 로컬 Gogs 저장소 주소
                 credentialsId: 'gogs-crd'  // Gogs 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-crd') {
                        // DOCKER_TAG 사용
                        def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                        appImage.push()
                        appImage.push("latest")
                    }
                }
            }
        }
    }
    post {
        success {
            echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
        }
        failure {
            echo "Pipeline failed. Please check the logs."
        }
    }
}

 

 

파이프라인이 성공적으로 실행되었고, 이미지도 잘 업로드 되었습니다.

 



1.4. Kind 에 이미지 배포

 

Jenkins 로 빌드한 이미지를 Kind 에 배포해보겠습니다.

 

# Dockerhub 계정 변수 설정
DHUSER=kimalarm2
DHPASS=<도커허브 토큰 입력>

# Secret 배포 (Dockerhub 자격증명)
kubectl create secret docker-registry dockerhub-secret \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=$DHUSER \
  --docker-password=$DHPASS

# Deployment 배포
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: timeserver
spec:
  replicas: 2
  selector:
    matchLabels:
      pod: timeserver-pod
  template:
    metadata:
      labels:
        pod: timeserver-pod
    spec:
      containers:
      - name: timeserver-container
        image: docker.io/$DHUSER/dev-app:0.0.1
        livenessProbe:
          initialDelaySeconds: 30
          periodSeconds: 30
          httpGet:
            path: /healthz
            port: 80
            scheme: HTTP
          timeoutSeconds: 5
          failureThreshold: 3
          successThreshold: 1
      imagePullSecrets:
      - name: dockerhub-secret
EOF

# Service 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: timeserver
spec:
  selector:
    pod: timeserver-pod
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    nodePort: 30000
  type: NodePort
EOF

 



1.5. 애플리케이션 업데이트

 

Gogs 의 코드를 업데이트 한 후, Jenkins 에서 새로 빌드하고 이것을 Kind 에 배포해보겠습니다.

 

Gogs 저장소에서 VERSION 파일과 Server.py 의 버전을 0.0.1 에서 0.0.2 로 변경 후 푸시합니다.

 

 

git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

 

이후 Jenkins Pipeline 에서 새로 빌드하고 Kind Deployment 를 업데이트 합니다.

 

kubectl set image deployment timeserver timeserver-container=$DHUSER/dev-app:0.0.2 && watch -d "kubectl get deploy,ep timeserver -owide; echo; kubectl get rs,pod"

 




2. Gogs - Jenkins Trigger

 

지금까지는 코드를 업데이트한 후 수동으로 Jenkins 를 빌드했습니다.
Gogs 코드가 업데이트되면 바로 Jenkins 에서 빌드를 수행하도록 웹훅을 설정해보겠습니다.

 

2.1. Gogs 웹훅 생성

 

Gogs 와 Jenkins 가 동일한 PC 에서 컨테이너로 가동되고 있기 때문에 내부 설정이 필요합니다.

 

## gogs 내부 컨테이너에서 아래 설정 파일에 NETWORK 허용 추가

# 설정 파일 위치
cd /data/gogs/conf

vi app.ini

[security]
INSTALL_LOCK = true
SECRET_KEY   = uZRj9neBbhbiDfC
LOCAL_NETWORK_ALLOWLIST = 192.168.200.121 ## 로컬 PC 사설 IP 추가

 

# 설정 적용을 위한 컨테이너 재기동
docker compose restart gogs

 

이후 Gogs 레포지토리에 가서 웹훅 설정을 추가합니다.

 



2.2. Jenkins Gogs 웹훅 연동

 

Jenkins Pipeline 에서 Gogs 웹훅을 연동해줍니다.

 

 

 

Jenkins SCM 설정

 

이제 Git 소스 코드에 저장된 Jenkinsfile 을 사용할 예정이므로 SCM 설정을 해줍니다.

 

 

Jenkinsfile 생성

 

Jenkinsfile 을 아래와 같이 생성해주고 VERSION 을 0.0.2 에서 0.0.3 으로 변경한 후 푸시합니다.

 

pipeline {
    agent any
    environment {
        DOCKER_IMAGE = 'kimalarm2/dev-app' // Dockerhub 저장소 명칭
    }
    stages {
        stage('Checkout') {
            steps {
                 git branch: 'main',
                 url: 'http://192.168.200.121:3000/devops/dev-app.git',  // 로컬 Gogs 저장소 주소
                 credentialsId: 'gogs-crd'  // 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-crd') {
                        // DOCKER_TAG 사용
                        def appImage = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
                        appImage.push()
                        appImage.push("latest")
                    }
                }
            }
        }
    }
    post {
        success {
            echo "Docker image ${DOCKER_IMAGE}:${DOCKER_TAG} has been built and pushed successfully!"
        }
        failure {
            echo "Pipeline failed. Please check the logs."
        }
    }
}

 

git add . && git commit -m "VERSION $(cat VERSION) Changed" && git push -u origin main

 

이후 Jenkins 에서 파이프라인이 동작되고 Dockerhub 에 0.0.3 이미지가 업로드 된 것을 확인하였습니다.

 

 


 

 

Contents

포스팅 주소를 복사했습니다