본문 바로가기
CI&CD

CI/CD Study [3주차]

by e-pd 2025. 11. 2.

 

Jenkins 및 Gogs 컨테이너 구성 (Docker Compose): Jenkins (CI 서버)와 Gogs (Git 서버)를 Docker 컨테이너로 실행합니다.

Jenkins와 Gogs 컨테이너 서비스를 정의하는 docker-compose.yaml 파일을 작성합니다.

cat << 'EOT' > docker-compose.yaml
services:
  jenkins:
    container_name: jenkins
    image: jenkins/jenkins
    restart: unless-stopped
    networks:
      - cicd-network
    ports:
      - "8080:8080" # Jenkins 웹 UI (Host:8080 -> Container:8080)
      - "50000:50000" # Jenkins Agent 통신 포트
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock # DooD 설정 (필수)
      - jenkins_home:/var/jenkins_home # Jenkins 데이터 영구 저장
  gogs:
    container_name: gogs
    image: gogs/gogs
    restart: unless-stopped
    networks:
      - cicd-network
    ports:
      - "10022:22" # Gogs SSH 포트
      - "3000:3000" # Gogs 웹 UI (Host:3000 -> Container:3000)
    volumes:
      - gogs-data:/data # Gogs 데이터 영구 저장
volumes:
  jenkins_home:
  gogs-data:
networks:
  cicd-network:
    driver: bridge
EOT

참고: volumes: - /var/run/docker.sock:/var/run/docker.sock 설정은 Jenkins 컨테이너 내부에서 호스트의 Docker 데몬을 사용하여 이미지 빌드(DooD)를 가능하게 합니다.

Gogs 서비스 및 볼륨 설정

  • image: gogs/gogs: Gogs(셀프 호스팅 Git 서비스) 공식 이미지를 사용합니다.
  • ports: - "3000:3000": Gogs의 웹 UI 접근 포트입니다.
  • volumes: jenkins_home: 및 gogs-data:: 이들은 Docker 볼륨으로, 컨테이너가 삭제되거나 재시작되어도 Jenkins 설정 및 Gogs 저장소 데이터가 유지되도록 보장합니다.
docker compose logs jenkins -f
jenkins  | Running from: /usr/share/jenkins/jenkins.war
jenkins  | webroot: /var/jenkins_home/war
jenkins  | 2025-11-01 15:06:47.388+0000 [id=1]	INFO	winstone.Logger#logInternal: Beginning extraction from war file
jenkins  | 2025-11-01 15:06:47.791+0000 [id=1]	WARNING	o.e.j.ee9.nested.ContextHandler#setContextPath: Empty contextPath
jenkins  | 2025-11-01 15:06:47.810+0000 [id=1]	INFO	org.eclipse.jetty.server.Server#doStart: jetty-12.1.3; built: 2025-10-16T14:10:03.576Z; git: f8d520dade4123ea8ce53fb1b9ac3a7a936a9d0f; jvm 21.0.8+9-LTS

Gogs 초기 설정 및 토큰 발급:

◦ Gogs 웹 접속: http://127.0.0.1:3000/install (또는 자신의 PC/WSL IP).

◦ 초기 설정: 데이터베이스 유형을 SQLite3로 설정하고, 관리자 계정 (예: devops/password)을 생성합니다.

◦ 토큰 발급: 로그인 후 Your SettingsApplications에서 새 토큰 (예: devops 이름)을 생성하고 토큰 값을 복사 및 저장합니다.

Private 저장소 생성: 개발팀용 dev-app과 데브옵스팀용 ops-deploy 두 개의 Private Repository를 생성합니다.

http://127.0.0.1:3000/install

 

 

 

◦ 토큰 발급: 로그인 후 Your SettingsApplications에서 새 토큰 (예:

devops

이름)을 생성하고 토큰 값을 복사 및 저장합니다.

 

 

GitOps CI/CD 파이프라인의 핵심은 개발팀데브옵스팀의 책임을 분리.

이를 위해 두 개의 독립된 Private Repository가 필요합니다.

저장소 이름 역할 (팀) 저장되는 내용 (SSOT) 파이프라인에서의 역할

dev-app 개발팀 (CI의 Source) 애플리케이션 소스 코드, Dockerfile, VERSION 파일, Jenkinsfile CI(Continuous Integration)의 시작점. 코드가 푸시되면 Jenkins가 이미지를 빌드하고 태그를 지정하여 Docker Hub에 푸시합니다.
ops-deploy 데브옵스팀/운영팀 (CD의 Source of Truth) 쿠버네티스 배포 매니페스트 (Deployment.yaml, Service.yaml), Helm Chart 또는 Kustomize 파일 GitOps CD(Continuous Delivery)의 단일 진실 공급원(SSOT). Jenkins가 빌드 후 이 저장소의 이미지 버전을 업데이트하면, ArgoCD가 이를 감지하여 배포합니다.

저장소를 분리하는 이유

저장소 분리는 책임 분리보안 측면에서 이점.

  1. 책임 분리: 개발팀은 dev-app (코드)에만 집중하고, 데브옵스팀은 ops-deploy (인프라 설정)에만 집중할 수 있습니다.
  2. 보안: 개발팀이 직접 운영 클러스터에 접근할 필요가 없어 클러스터 접근 권한을 최소화할 수 있습니다.

2. Jenkins 초기 설정

◦ 초기 암호 확인: docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword.

Jenkins 웹 접속: http://127.0.0.1:8080에 접속하여 초기 암호를 입력하고 "Install suggested plugins"를 선택합니다.

 

 

◦ 관리자 계정 (예: admin/password)을 생성합니다.

필수 플러그인 설치: Jenkins 관리 → Plugins → Available plugins에서 Pipeline Stage View, Docker Pipeline, Gogs (또는 Generic Webhook) 플러그인을 설치합니다.

 

Jenkins 자격증명 설정: Jenkins 관리 → Credentials → Global → Add Credentials에서 다음 3가지 자격증명을 설정.

Gogs 저장소 자격증명 (gogs-crd):

▪ Kind: Username with password

▪ Username: devops

▪ Password: <Gogs 토큰> (단계 2.1에서 저장한 토큰).

Docker Hub 자격증명 (dockerhub-crd):

▪ Kind: Username with password

▪ Username: <도커 계정명>

▪ Password: <도커 계정 암호 또는 토큰>.

Kubernetes 자격증명 (k8s-crd):

▪ Kind: Secret file

▪ File: <kubeconfig 파일 업로드> (Kind 클러스터 생성 후 생성된 ~/.kube/config 파일 사용).

 

CI 환경 구성 및 Docker Hub Secret 설정

  1. dev-app 저장소 클론 및 초기 파일 추가: 로컬에서 dev-app 저장소를 클론하고 애플리케이션 파일 (server.py, Dockerfile, VERSION)을 추가합니다.
  2. Kubernetes Docker Hub Secret 생성 (이미지 Pull 권한 부여): Kind 클러스터가 Docker Hub의 Private 이미지에 접근할 수 있도록 Secret을 생성합니다.

환경 변수 설정 (Docker Hub 계정 및 토큰 사용)

export DHUSER=<자신의 도커 허브 계정명> export DHPASS=<자신의 도커 허브 암호 또는 토큰>

 

 

 

kubectl create secret docker-registry dockerhub-secret \\
  --docker-server=https://index.docker.io/v1/ \\
  --docker-username=$DHUSER \\
  --docker-password=$DHPASS
secret/dockerhub-secret created
# Secret 목록 확인
kubectl get secret dockerhub-secret

Gogs에 devops 계정으로 클론 (비밀번호 대신 토큰 사용)

git clone http://devops:$TOKEN@$GOGS_IP:3000/devops/ops-deploy.git cd ops-deploy

 

Git 환경 설정 

git config --local user.name "devops" git config

--local user.email "a@a.com"

 

dev-app 디렉터리 생성 및 Manifest 파일 추가

mkdir dev-app export VERSION="0.0.1"

 

VERSION 파일 (Jenkins가 나중에 업데이트할 버전 정보)

cat <<EOF > dev-app/VERSION $VERSION EOF

timeserver Deployment Manifest 

cat < dev-app/timeserver.yaml
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:$VERSION](<http://docker.io/$DHUSER/dev-app:$VERSION>)
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 30
httpGet:
path: /healthz
port: 80
scheme: HTTP
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
# 이전 단계에서 생성한 Secret을 사용하여 Private 이미지 접근 권한 부여
imagePullSecrets:
- name: dockerhub-secret
EOF

Service Manifest (NodePort로 외부 노출)

cat <<EOF > dev-app/service.yaml
apiVersion: v1
kind: Service
metadata:
name: timeserver
spec:
selector:
pod: timeserver-pod
ports:

- port: 80
targetPort: 80
protocol: TCP
nodePort: 30000 # Kind 클러스터의 30000번 포트로 노출
type: NodePort
EOF

Git에 푸시하여 ArgoCD가 바라볼 상태 정의

git add dev-app/ git commit -m "Add dev-app deployment yaml" git push -u origin main

 

 

ArgoCD 네임스페이스 생성

kubectl create ns argocd [12-15]

Helm values 파일 작성 (NodePort 및 HTTP 접속 설정)

cat <<EOF > argocd-values.yaml
dex:
enabled: false # 외부 인증(Dex) 비활성화 [12-15]
server:
service:
type: NodePort
nodePortHttps: 30002 # ArgoCD 웹 UI를 30002번 포트로 노출 [12-15]
extraArgs:
- --insecure # HTTPS 대신 HTTP 접속 허용 (랩 환경 편의) [13-15]
EOF

Helm 저장소 추가 및 ArgoCD 설치

helm repo add argo https://argoproj.github.io/argo-helm helm install argocd argo/argo-cd --version 9.0.5 \ -f argocd-values.yaml \ --namespace argocd

ArgoCD 초기 관리자 암호 확인

설치 후 admin 계정의 초기 암호를 Secret에서 추출합니다.

kubectl -n argocd get secret argocd-initial-admin-secret \ -o jsonpath="{.data.password}" | base64 -d ; echo

 

ArgoCD 웹 접속

브라우저에서 아래 주소로 접속 후, 추출한 초기 암호로 로그인합니다.

http://127.0.0.1:{아르고CD 포트}

ArgoCD 설치 및 접속:

ArgoCD에 Gogs Repo 등록: ArgoCD 웹 UI (Settings → Repositories)에서 Gogs ops-deploy 저장소 URL을 등록.

이때 Gogs devops 계정명과 토큰을 사용합니다.

 

 

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/<자신의 도커 허브 계정>/dev-app:0.0.1 # 이미지 태그는 VERSION과 일치해야 함
        livenessProbe:
          initialDelaySeconds: 30
          httpGet:
            path: /healthz # 헬스 체크 경로 (소스에 포함됨 [12, 13])
            port: 80
        imagePullSecrets:
        - name: dockerhub-secret # Docker Hub Secret 이름 (사전 생성되어야 함)

 

ArgoCD Application 생성 (선언적 자동 동기화): ArgoCD 애플리케이션 자체를 YAML로 관리하고 자동 동기화

timeserver 애플리케이션을 생성합니다.

 

 

 

CI/CD 파이프라인 구축 및 테스트

Jenkinsfile 작성 (CI + GitOps Manifest 업데이트): dev-app 저장소의 루트에 다음 Jenkinsfile을 작성하여, 빌드가 완료된 후

자동으로 ops-deploy 저장소의 버전 정보를 업데이트하고 Git Push하도록 합니다.

 

 

Jenkins Item 생성 (SCM Pipeline): Jenkins에서 새 Item (예: full-cicd-scm)을 생성하고 Pipeline script from SCM으로 설정한 뒤, dev-app Git 저장소와 Jenkinsfile 경로를 지정합니다.

 

CI/CD 테스트: 개발팀의 변경을 테스트

◦ dev-app 로컬 디렉토리에서 VERSION 파일을 0.0.4로 업데이트하고, server.py 내부의 버전 정보도 0.0.4로 수정합니다.

◦ 변경된 코드를 Gogs dev-app에 푸시합니다.

결과 확인:

◦ Jenkins가 자동으로 빌드를 시작합니다.

◦ 빌드 성공 후, ops-deploy 저장소가 자동으로 0.0.4 버전으로 업데이트됩니다.

◦ ArgoCD가 ops-deploy의 변경사항을 감지하고 (자동 동기화 설정 시) 자동으로 쿠버네티스 클러스터에 새 버전 (0.0.4)을 배포합니다.

◦ 배포 확인: curl http://127.0.0.1:30000 (버전 0.0.4가 출력되는지 확인)