Docker - Jenkins, Gitlab
K8S - argocd
springboot
maven
Dockerdescktop - kubernetes
Spring 프로젝트 생성
https://start.spring.io



git init
git commit -m 'init'
Docker 내 gitlab 설치
docker-compose.yml 파일 사용
services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    hostname: gitlab
    restart: always
    ports:
      - "8929:8929"   # Web UI
      - "2224:22"     # SSH for git clone
    environment:
      TZ: Asia/Seoul
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://localhost:8929'
    volumes:
      - ./config:/etc/gitlab
      - ./logs:/var/log/gitlab
      - ./data:/var/opt/gitlab
    extra_hosts:
      - "jenkins.local:host-gateway"
    networks:
      - cicd-net
networks:
  cicd-net:
    external: true
**networks는 jenkins와 gitlab 동일 네트워크로..**
docker gitlab 설치
docker compose up -d

Gitlab 초기 관리 계정 : root
gitlab 초기 관리 비번 : /etc/gitlab 혹은 로컬 볼륨 .config에서 확인

http://localhost:8929 로 접속 가능.
root / O6Lexg1M4u6G/LWsDIdfOBpl1gxJ+fA1WPy2fMx1140=

가입정책 변경 - 체크박스 해제
Sign-up enabled

계정명 변경

비밀번호 변경

재로그인

프로젝트 생성

공백 프로젝트 생성


로컬 pc ssh key 생성 : ssh-keygen 이용
계정에 로컬 pc SSH 키 등록

로컬 .ssh 에서 사용할 pub 키를 gitlab에 등록.
cat ~/.ssh/{name}.pub

연결 테스트(localPC에서 Docker Gitlab으로) :
ssh git@localhost -p 2224
알고리즘 기본 이름이 아닌, 다른 이름으로 지정했을 경우,
- 
    ssh git@localhost -p 2224 -i /root/.ssh/id_jenkins

Spring 프로젝트와 연결

git remote add origin ssh://git@localhost:2224/devsungyeon/k8sdemo.git

git pull 시, 충돌 발생하면,
강제로 병합 허용 : git pull origin main –allow-unrelated-histories
git push

push 확인

Docker 내 Jenkins 설치

Dockerfile
Dockerfile 사용 이유는 기본 Jenkins 이미지에는 docker CLI가 없음.
따라서 해당 이미지에 docker 설치후, 다시 빌드하여 사용.
# Dockerfile
FROM jenkins/jenkins:lts-jdk17
USER root
# Docker CLI 설치
RUN apt-get update && \
    apt-get install -y docker.io && \
    apt-get clean
# docker 그룹 접근 허용 (root는 이미 가능하지만 안전하게)
RUN usermod -aG docker root
# Jenkins home 유지
VOLUME /var/jenkins_home
EXPOSE 8080 50000
docker-compose.yml 파일 사용
services:
  jenkins:
    build: . # Dockerfile 사용?
    container_name: jenkins
    restart: always
    user: root
    ports:
      - "9090:8080"   # ← 외부 접속 포트 변경
      - "50000:50000"
    environment:
      - TZ=Asia/Seoul
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - ./jenkins_ssh:/root/.ssh        # ✅ SSH 키 영구 저장
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - cicd-net
networks:
  cicd-net:
    external: true
Dockerfile build
docker compose build

docker compose up
docker compose up -d

http://localhost:9090 로 접속 가능.
초기 비번 : ./jenkins_home/secrets/initialAdminPassword


필수플러그인 설치

계정 생성

Jenkins 내 root 계정 ssh 키 생성
docker exec -it jenkins bash
ssh-keygen -t ed25519 -C "jenkins@gitlab" -f ~/.ssh/id_jenkins -N ""
in jenkins cert, we cant use passphase for ssh key. with passphase , there were error for connecting.

id_jenkins.pub Gitlab계정의 ssh 키에 등록
cat ~/.ssh/id_jenkins.pub


ssh 접근 테스트 - 여기서 접근 가능해야, jenkins 웹에서도 접근 가능.
ssh -T git@host.docker.internal -p 2224 -i ~/.ssh/id_jenkins

test project 생성



Credentials 생성
cat ~/.ssh/id_jenkins


Credentials 선택후, 에러 사라짐

브랜치명 수정

빌드 테스트


Gitlab webhook 연결 with jenkins
Plugin Gitlab 설치 in Jenkins

GitLab 계정에서 Access tokens 생성



GitLab 연결 : Jenkins관리 - System - GitLab



Jenkins Item 생성 (k8sdemo build) : Pipeline


Jenkins - Trigger 체크


Gitlab - 하기 중간 체크 필요 Allow requests to the local network from webhooks and integrations

Jenkins에서 계정의 security 내 api token 발급


Gitlab - 웹훅 추가 시 , http://{user}:{jenkins_api_token}@jenkins.local:9090/project/k8sdemo_build

웹훅 테스트


Jenkins 빌드 확인


Jenkins pipe라인 Definition 수정

Spring에서 .gitlab-ci.yml, Dockerfile 및 Jenkinsfile 추가
.gitlab-ci.yml 추가 - gitlab은 repo만 수행하므로 아무작업 없음. runner 미추가
echo "# GitLab CI disabled — Jenkins handles CI/CD.
stages:
  - noop
disable_job:
  stage: noop
  script:
    - echo \"GitLab CI is disabled. Jenkins handles all builds.\"
  rules:
    - when: never" > .gitlab-ci.yml
git add .gitlab-ci.yml
git commit -m "disable GitLab CI completely, use Jenkins only"
git push
Dockerfile
# Dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
# Maven으로 빌드된 JAR 파일 복사
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app/app.jar"]
Jenkinsfile
pipeline {
  agent any
  environment {
    IMAGE_NAME = 'k8sdemo'
  }
  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }
    stage('Maven Build') {
      steps {
        echo '🔧 Building Spring Boot application...'
        sh './mvnw -B clean package -DskipTests'
      }
    }
    stage('Docker Build') {
      steps {
        script {
          def GIT_COMMIT_HASH = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
          echo "🐳 Building Docker image: ${IMAGE_NAME}:${GIT_COMMIT_HASH}"
          sh "docker build -t ${IMAGE_NAME}:${GIT_COMMIT_HASH} ."
        }
      }
    }
  }
  post {
    success {
      echo '✅ Build successful!'
    }
    failure {
      echo '❌ Build failed.'
    }
  }
}
	

로컬에서 추가후 push 후 자동 build 테스트 확인



docker images | grep k8sdemo

K8s-configs repo 생성
✅ 1️⃣ ArgoCD가 인식할 GitOps Repo 준비
ArgoCD는 Git 리포지토리의 YAML 정의를 감시합니다.
즉, Kubernetes 매니페스트가 담긴 별도의 Git repo (예: k8s-configs) 가 필요합니다.
예시 구조:
k8s-configs/
 ├── apps/
 │    └── k8sdemo/
 │         ├── deployment.yaml
 │         ├── service.yaml
 │         └── kustomization.yaml
 └── README.md
Gitlab 공백 프로젝트 생성
로컬로 clone
파일들 추가 apps/*
push to Gitlab from local
✅ 2️⃣ deployment.yaml 예시
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8sdemo
  labels:
    app: k8sdemo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: k8sdemo
  template:
    metadata:
      labels:
        app: k8sdemo
    spec:
      containers:
        - name: k8sdemo
          image: k8sdemo:latest   # ✅ Jenkins가 빌드 후 이 부분을 자동 업데이트
          ports:
            - containerPort: 8080
ArgoCD는 이 image 값을 기준으로 배포 대상을 인식합니다.
Jenkinsfile 수정
pipeline {
  agent any
  environment {
    IMAGE_NAME = 'k8sdemo'
    GITOPS_REPO = 'git@host.docker.internal:devsungyeon/k8s-configs.git'
  }
  stages {
    stage('Checkout') {
      steps { checkout scm }
    }
    stage('Who Am I') {
      steps {
        sh 'whoami'
        sh 'ls -al ~'
        sh 'ls -al ~/.ssh || echo "⚠️ No .ssh folder found"'
      }
    }
    stage('Maven Build') {
      steps {
        echo '🔧 Building Spring Boot application...'
        sh './mvnw -B clean package -DskipTests'
      }
    }
    stage('Docker Build') {
      steps {
        script {
          def GIT_COMMIT_HASH = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
          env.GIT_COMMIT_HASH = GIT_COMMIT_HASH   // 🔹 후속 단계에서 접근 가능하도록 환경변수 등록
          def IMAGE_TAG = "${IMAGE_NAME}:${GIT_COMMIT_HASH}"
          sh "docker build -t ${IMAGE_TAG} ."
          sh "docker tag ${IMAGE_TAG} ${IMAGE_NAME}:latest"
        }
      }
    }
    stage('Update GitOps Repo') {
      steps {
        script {
          sh """
            echo "🔑 Registering SSH host..."
            ssh-keyscan -p 2224 host.docker.internal >> ~/.ssh/known_hosts || true
            echo "🧩 Cloning GitOps repository..."
            rm -rf k8s-configs
            bash -c "GIT_SSH_COMMAND='ssh -i ~/.ssh/id_jenkins -p 2224' git clone ${env.GITOPS_REPO}"
            cd k8s-configs/apps/k8sdemo
            echo "📝 Updating deployment.yaml with image tag ${env.GIT_COMMIT_HASH}"
            sed -i 's|image:.*|image: k8sdemo:${env.GIT_COMMIT_HASH}|' deployment.yaml
            git config --global user.email "jenkins@local"
            git config --global user.name "Jenkins CI"
            git add .
            git commit -m "Update image tag to ${env.GIT_COMMIT_HASH}" || echo "No changes to commit"
            echo "🚀 Pushing updates..."
            bash -c "GIT_SSH_COMMAND='ssh -i ~/.ssh/id_jenkins -p 2224' git push origin main"
          """
        }
      }
    }
 }
  post {
    success { echo '✅ Build and update successful!' }
    failure { echo '❌ Build failed.' }
  }
}
push 하여 테스트

Docker image 최신 몇개만 남기고 지우기

pipeline {
  agent any
  environment {
    IMAGE_NAME = 'k8sdemo'
    GITOPS_REPO = 'git@host.docker.internal:devsungyeon/k8s-configs.git'
  }
  stages {
    stage('Checkout') {
      steps { checkout scm }
    }
    stage('Who Am I') {
      steps {
        sh 'whoami'
        sh 'ls -al ~'
        sh 'ls -al ~/.ssh || echo "⚠️ No .ssh folder found"'
      }
    }
    stage('Maven Build') {
      steps {
        echo '🔧 Building Spring Boot application...'
        sh './mvnw -B clean package -DskipTests'
      }
    }
    stage('Docker Build') {
      steps {
        script {
          def GIT_COMMIT_HASH = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
          env.GIT_COMMIT_HASH = GIT_COMMIT_HASH   // 🔹 후속 단계에서 접근 가능하도록 환경변수 등록
          def IMAGE_TAG = "${IMAGE_NAME}:${GIT_COMMIT_HASH}"
          sh "docker build -t ${IMAGE_TAG} ."
          sh "docker tag ${IMAGE_TAG} ${IMAGE_NAME}:latest"
        }
      }
    }
    stage('Update GitOps Repo') {
      steps {
        script {
          sh """
            echo "🔑 Registering SSH host..."
            ssh-keyscan -p 2224 host.docker.internal >> ~/.ssh/known_hosts || true
            echo "🧩 Cloning GitOps repository..."
            rm -rf k8s-configs
            bash -c "GIT_SSH_COMMAND='ssh -i ~/.ssh/id_jenkins -p 2224' git clone ${env.GITOPS_REPO}"
            cd k8s-configs/apps/k8sdemo
            echo "📝 Updating deployment.yaml with image tag ${env.GIT_COMMIT_HASH}"
            sed -i 's|image:.*|image: k8sdemo:${env.GIT_COMMIT_HASH}|' deployment.yaml
            git config --global user.email "jenkins@local"
            git config --global user.name "Jenkins CI"
            git add .
            git commit -m "Update image tag to ${env.GIT_COMMIT_HASH}" || echo "No changes to commit"
            echo "🚀 Pushing updates..."
            bash -c "GIT_SSH_COMMAND='ssh -i ~/.ssh/id_jenkins -p 2224' git push origin main"
          """
        }
      }
    }
  }
  post {
    success { 
      // 수정 부분
      echo '✅ Build and update successful!' 
      echo '✅ Build and update successful!'
      sh '''
        echo "🧹 Cleaning up old Docker images..."
        # 최근 3개의 k8sdemo 이미지만 남기고 나머지는 삭제
        docker images k8sdemo --format "" | tail -n +4 | xargs -r docker rmi -f || true
        docker image prune -f
      '''
    }
    failure { echo '❌ Build failed.' }
  }
}

Argocd 설치
✅ 1️⃣ ArgoCD 설치 (Kubernetes 환경에 설치)
🔹 ① 네임스페이스 생성
kubectl create namespace argocd
🔹 ② 공식 매니페스트로 설치
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
🔹 ③ 설치 확인
kubectl get pods -n argocd
→ argocd-server, argocd-repo-server, argocd-dex-server 등이
Running 상태가 될 때까지 기다립니다 (1~2분 정도).

✅ 2️⃣ ArgoCD 웹 접속 설정
🔹 ① 서비스 확인
kubectl get svc -n argocd
보통 이렇게 나옵니다:
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
argocd-server           ClusterIP   10.101.120.41    <none>        80/TCP,443/TCP      2m
argocd-repo-server      ClusterIP   10.101.218.90    <none>        8081/TCP,8084/TCP   2m
...
🔹 ② 포트포워딩으로 로컬 접속
kubectl port-forward svc/argocd-server -n argocd 8080:443
➡ 브라우저에서 접속: 👉 https://localhost:8080
✅ 3️⃣ 로그인 정보 확인
초기 사용자:
Username: admin
비밀번호:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
이제 UI에서 로그인 가능합니다 🎯
✅ 4️⃣ 자동 로그인 (선택)
CLI에서 로그인할 수도 있어요:
argocd login localhost:8080 \
  --username admin \
  --password $(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) \
  --insecure
argocd login localhost:8080 --username admin --password <혹은 변경비밀번호> --insecure
✅ 5️⃣ ArgoCD 설치 확인
argocd version
argocd app list
→ 버전과 서버 연결이 정상적으로 출력되면 성공입니다.
ArgoCD에 k8sdemo 애플리케이션 등록 → 자동 배포 연결
✅ 1️⃣ GitOps Repo 구조 확인
지금 k8s-configs 리포지토리에 deployment.yaml만 있죠.
ArgoCD는 하나의 앱 단위로 deployment + service + kustomization 구성이 필요합니다.
📁 폴더 구조를 이렇게 만들어주세요 👇
k8s-configs/
└── apps/
    └── k8sdemo/
        ├── deployment.yaml
        ├── service.yaml
        └── kustomization.yaml
✅ 2️⃣ service.yaml 생성
apiVersion: v1
kind: Service
metadata:
  name: k8sdemo
  labels:
    app: k8sdemo
spec:
  type: NodePort
  selector:
    app: k8sdemo
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 30080
✅ 3️⃣ kustomization.yaml 생성
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
ArgoCD는
kustomization.yaml을 기준으로 해당 폴더를 “앱 단위”로 인식합니다.
✅ 4️⃣ GitLab에 커밋 & 푸시
로컬에서 아래처럼 커밋해 주세요 👇
git add apps/k8sdemo/service.yaml apps/k8sdemo/kustomization.yaml
git commit -m "Add service and kustomization for ArgoCD"
git push
Argocd repo add
argocd repo add http://host.docker.internal:8929/devsungyeon/k8s-configs.git 
  –username devsungyeon 
  –password glpat-chuT4RQ_Yv-rzTPADG4JrG86MQp1OjEH.01.0w0yjr3g6 
  –insecure-skip-server-verification
password는 personal access token
✅ 5️⃣ ArgoCD 앱 등록 (CLI 방식)
K8s-configs repo 디렉토리에서 수행
argocd app create k8sdemo \
  --repo http://host.docker.internal:8929/devsungyeon/k8s-configs.git \
  --path apps/k8sdemo \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace default \
  --sync-policy automated
🔹
--sync-policy automated: Git 변경 시 자동 배포 🔹--path apps/k8sdemo: 우리가 만든 폴더 경로
✅ 6️⃣ 상태 확인
argocd app list
argocd app get k8sdemo
→ STATUS: Synced / HEALTH: Healthy
가 나오면 Kubernetes에 배포가 완료된 상태입니다.
✅ 7️⃣ 배포 확인
kubectl get pods
kubectl get svc
→ k8sdemo Deployment 및 Service가 실제로 생성되어 있으면 성공 🎯

지금 kubectl get all 결과를 보면:
service/k8sdemo  NodePort  80:30995/TCP
즉, 외부 접근 포트가 30995로 자동 할당된 상태예요.
✅ 바로 접근 방법
🖥 Docker Desktop Kubernetes 사용 중이라면:
로컬 브라우저에서 아래 주소 입력 👇
http://localhost:30995
이게 바로
k8sdemo의 Spring Boot 서버 주소입니다.
최종 단계
/hello push해서 반영 테스트 해보기
✅ 1️⃣ Controller 추가
src/main/java/.../controller/HelloController.java 파일을 새로 만드세요.
package com.example.k8sdemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello from k8sdemo!";
    }
}
⚙️ 2️⃣ 로컬에서 먼저 테스트
Spring Boot 로컬 실행 포트 변경
application.properties 또는 application.yml에 포트를 임시로 바꾸세요.
📄 src/main/resources/application.properties
server.port=8085
그 후 실행:
./mvnw spring-boot:run
접속:
http://localhost:8085/hello
그리고 push 하여 테스트.

접속:
http://localhost:30995/hello

아니면, 기존 argocd 포트를 다른걸로 변경해도 됨


🐳 Kubernetes에 PostgreSQL 추가하기 (with Spring Boot)
/hello배포 후 Postgres DB를 추가하고, Spring Boot에서 연결 테스트하는 단계
✅ 1️⃣ kustomization.yaml 수정
postgres 관련 리소스를 함께 관리하도록 추가
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
  - postgres-deployment.yaml
  - postgres-service.yaml
✅ 2️⃣ postgres-deployment.yaml 추가
PersistentVolumeClaim + Postgres Deployment 설정
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: default
spec:
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:15
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: k8sdemo
            - name: POSTGRES_USER
              value: demo
            - name: POSTGRES_PASSWORD
              value: demo1234
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-pvc
✅ 3️⃣ postgres-service.yaml 추가
Spring Boot 애플리케이션이 내부 ClusterIP로 접근할 수 있도록 설정
apiVersion: v1
kind: Service
metadata:
  name: postgres-service
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432
✅ 4️⃣ pom.xml 수정
PostgreSQL JDBC 드라이버 의존성 추가
<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>42.7.2</version>
</dependency>
✅ 5️⃣ application.properties 수정
Spring Boot에서 Postgres 연결 설정
spring.datasource.url=jdbc:postgresql://postgres-service:5432/k8sdemo
spring.datasource.username=demo
spring.datasource.password=demo1234
spring.datasource.driver-class-name=org.postgresql.Driver
# JPA 설정 (선택)
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
Db connection test
package com.example.k8sdemo.controller;
import com.example.k8sdemo.entity.TestEntity;
import com.example.k8sdemo.repository.TestRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
    private final TestRepository repo;
    public TestController(TestRepository repo) {
        this.repo = repo;
    }
    @GetMapping("/health/db")
    public String checkDb() {
        repo.save(new TestEntity("ok"));
        long count = repo.count();
        return "✅ DB Connected! (rows=" + count + ")";
    }
}
참고) Git clone
git clone (localPC에서 Docker Gitlab으로) :
git clone ssh://git@localhost:2224/devsungyeon/k8sdemo.git
알고리즘 기본 이름이 아닌, 다른 이름으로 지정했을 경우,
GIT_SSH_COMMAND='ssh -i ~/.ssh/id_jenkins -p 2224' git clone git@localhost:devsungyeon/k8sdemo.git
GIT_SSH_COMMAND=’ssh -i ~/.ssh/id_jenkins -p 2224’ git clone git@host.docker.internal:devsungyeon/k8sdemo.git
 
      
댓글남기기