스프링

 

 

전체 구성 개요

  1. GitHub Actions로 Spring Boot 빌드

  2. 서버에 .jar 파일 자동 배포 (scp 또는 sftp)

  3. SSH 키 기반 인증 방식 사용 (비밀번호 방식 지양)

  4. 서버에서 systemd 서비스로 구동 및 무중단 배포

  5. Nginx Reverse Proxy 설정

  6. 도메인 연결까지

 

 

서버 디렉토리 구조 예시 (/home/sample)

 

/home/sample/
├── current -> sample-v2.jar  # 심볼릭 링크
├── sample-v1.jar
├── sample-v2.jar
├── start.sh
├── stop.sh
└── sample.service  # systemd 서비스

 

 

 

 

 

1. SSH 키 기반 인증 준비

 SSH Key 생성 및 서버 등록

1. 로컬에서 SSH 키 생성 

 

여기서 github-actions@our-sample 는 임의 명이다.

github-deploy-key 또한 원하는 이름으로 변경하면된다.

중요한것은 깃허브에서  actions 에서 우분트 22 에 접속할수 있는  개인 키 등록과 우부트 22에 공개 키 설정이다.

 

 

PowerShell 사용 시 (경로를 명시적으로 써야 함)

ssh-keygen -t rsa -b 4096 -C "github-actions@our-sample" -f "$env:USERPROFILE\.ssh\github-deploy-key"

 

Git Bash 사용 시 (추천).

 

ssh-keygen -t rsa -b 4096 -C "github-actions@our-sample" -f ~/.ssh/github-deploy-key

 

 

 

2. GitHub에 개인 키 등록

  1. github-deploy-key 내용을 복사

 

GitHub 저장소> 깃허브 해당 프로젝트에서 > Settings > Secrets and variables > Actions > New secret 이름은 예: DEPLOY_KEY  값을 복붙

 

 

3.서버에 공개 키 등록

 

서버에 .ssh 디렉토리 만들기

권한 설정 문제 :  root 로 할것

 

mkdir -p ~/.ssh
chmod 700 ~/.ssh

공개 키 붙여넣기:
로컬에서 github-deploy-key.pub 파일 내용을 복사한 뒤, 서버에서:

 

 

중요한 오해 방지]

서버에 ~/.ssh/github-deploy-key 추가하는 게 아닙니다.

서버는 공개 키만 필요하며, 개인 키는 절대 서버에 두지 않습니다.

  • 개인 키: GitHub Actions에서만 사용

  • 공개 키: 서버의 ~/.ssh/authorized_keys에만 위치

 

 

✅ 확인: GitHub Actions에서 서버 접속이 되는가?

GitHub Actions가 아래 명령으로 배포 서버에 SSH 접속 가능해야 합니다:

 

ssh -i ~/.ssh/id_rsa sample@sample.com

 

버 측 ~/.ssh/authorized_keys 확인

우분투 서버에 다음 내용이 등록되어야 합니다:

  • ~/.ssh/authorized_keys 파일에 개인키에 대응되는 공개키가 포함되어야 합니다.

cat ~/.ssh/github-deploy-key.pub >> ~/.ssh/authorized_keys

 

cat ~/.ssh/github-deploy-key.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

 

또한 해당 SSH 사용자의 ~/.ssh 디렉토리와 권한은 다음과 같아야 합니다:

root 권한

 

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

 

 

 

 

 

 

참조:

https://macaronics.net/m03/nodejs/view/2423

 

 

2.GitHub에  Secrets  등록

  1. GitHub > Settings > Secrets and variables > Actions > New repository secret

  2. 아래 항목 등록

  3. 권한 설정 문제 : 유저를  sample  접속 할경우 워크플로우에 항상 sample  권한을  부여 해야 합니다.

a) DEPLOY_KEY: 개인 키 (~/.ssh/github-deploy-key 내용)
b) HOST: sample.com
c) USER: sample
d) TARGET_DIR: /home/sample
e) SERVICE_NAME: sample.service
f) PORT:  225
g) APP_PROPERTIES  :  application.properties 전체 내용을 그대로 붙여넣기

 

 

 

3. GitHub Actions 설정 (CI/CD)

 

설정 테스트 :  deploy.yml  으로 실제 CI/CD 작업 전에    

deploy-test.yml 파일을 만든후 접속연결 상태등을 테스트 한다.

 

name: SSH Deploy Key & Connection Test

on:
  workflow_dispatch:  # 수동 실행 버튼으로 테스트할 수 있게 함

jobs:
  test-ssh:
    runs-on: ubuntu-latest

    steps:
      - name: ✅ Show runner info
        run: |
          echo "Running on: $(uname -a)"
          echo "Bash version: $(bash --version)"

      - name: ✅ Setup SSH private key from GitHub Secrets
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          echo "✅ Private key has been written to ~/.ssh/id_rsa"

      - name: ✅ ssh-keyscan known_hosts 등록 (포트 포함)
        run: |
          ssh-keyscan -p ${{ secrets.PORT }} -H ${{ secrets.HOST }} >> ~/.ssh/known_hosts
          echo "✅ ssh-keyscan 완료"
          echo "✅ known_hosts 내용:"
          cat ~/.ssh/known_hosts

      - name: ✅ SSH 접속 테스트
        run: |
          ssh -p ${{ secrets.PORT }} -o StrictHostKeyChecking=no ${{ secrets.USER }}@${{ secrets.HOST }} "echo '✅ SSH 접속 성공!'"

      - name: ✅ 서버 디렉토리 접근 및 권한 확인
        run: |
          ssh -p ${{ secrets.PORT }} ${{ secrets.USER }}@${{ secrets.HOST }} "ls -ld ${{ secrets.TARGET_DIR }} || echo '❌ 디렉토리 없음 또는 권한 문제'"

      - name: ✅ scp 전송 테스트 (더미 파일)
        run: |
          echo "This is a test file from GitHub Actions" > dummy.txt
          scp -P ${{ secrets.PORT }} dummy.txt ${{ secrets.USER }}@${{ secrets.HOST }}:${{ secrets.TARGET_DIR }}/dummy-from-actions.txt
          echo "✅ SCP 파일 전송 완료"

      - name: ✅ 서버에 dummy 파일 존재 확인
        run: |
          ssh -p ${{ secrets.PORT }} ${{ secrets.USER }}@${{ secrets.HOST }} "ls -l ${{ secrets.TARGET_DIR }}/dummy-from-actions.txt || echo '❌ 파일 확인 실패'"

 

 

 

Spring Boot에서 application.properties (또는 application.yml) 파일은 DB 비밀번호, API 키 등 민감한 정보를 담고 있기 때문에 .gitignore로 커밋하지 않는 것이 원칙입니다.

그렇다면 배포 자동화(GitHub Actions)에서 어떻게 이 파일을 운영 서버에 전달하느냐?

→ 여러 가지 방법이 있지만, 가장 깔끔하고 관리하기 좋은 방법은 GitHub Secrets를 활용해서 배포 시 생성하는 것입니다.

 

GitHub Repository > Settings > Secrets and variables > Actions

아래와 같이 Secret을 등록하세요:

이름값 예시

APP_PROPERTIES아래 내용 참고

예시 값 (실제 DB 접속정보에 맞게 작성):

spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=myuser
spring.datasource.password=supersecurepassword
spring.jpa.hibernate.ddl-auto=update
server.port=8080

application.properties 전체 내용을 그대로 붙여넣기 하세요.

 

 

깃허브의 프로젝트 구조가 다음과 같다

✅ 프로젝트 구조

/
├── .github/
│   └── workflows/
│       └── deploy.yml
├── backend/      ← ★ Spring Boot 3.4 프로젝트 위치
│   └── pom.xml
│   └── src/
│   └── target/our-sample.jar (빌드 결과)
├── frontend/     ← React 프로젝트즈

즉, backend  아래에 스프링부트가 프로젝트 가 존재하고, 그리고 frontend 디렉토리 안에 리액트 프로젝트가 존재하는 구조이다.

 

 

 

 

 

 

 

최종  : .github/workflows/deploy.yml 파일 생성:

name: CI/CD for Spring Boot

on:
  push:
    branches: [ main ]  # main 브랜치에 push 발생 시 실행됨

jobs:
  deploy:
    runs-on: ubuntu-latest  # GitHub Actions가 실행될 가상 환경

    steps:
      - name: Checkout code
        uses: actions/checkout@v3  # GitHub 저장소의 소스코드를 가져옴

      - name: Set up Java
        uses: actions/setup-java@v3
        with:
          distribution: 'temurin'  # Temurin(Java OpenJDK) 사용
          java-version: '17'       # Java 17로 설정

      - name: ✅ Create application.properties from secret
        run: |
          mkdir -p backend/src/main/resources  # resources 디렉토리 생성
          printf "%s" "${{ secrets.APP_PROPERTIES }}" > backend/src/main/resources/application.properties
          # GitHub Secrets에서 APP_PROPERTIES 값을 받아 파일로 저장

      - name: Build Spring Boot app
        working-directory: ./backend
        run: mvn clean package -DskipTests  # 테스트는 생략하고 빌드

      - name: Setup SSH key
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_rsa  # GitHub Secrets에서 SSH 프라이빗 키 생성
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -p ${{ secrets.PORT }} -H ${{ secrets.HOST }} >> ~/.ssh/known_hosts
          # 서버 공개키 스캔하여 known_hosts에 등록 (SSH 보안 경고 방지)

      - name: Copy JAR to server
        run: |
          scp -P ${{ secrets.PORT }} backend/target/our-sample.jar \
          ${{ secrets.USER }}@${{ secrets.HOST }}:${{ secrets.TARGET_DIR }}/our-sample-latest.jar
          # 생성된 JAR 파일을 서버에 전송 (scp로 복사)

      - name: Restart service via SSH
        run: |
          ssh -p ${{ secrets.PORT }} ${{ secrets.USER }}@${{ secrets.HOST }} << EOF
            sudo systemctl stop ${{ secrets.SERVICE_NAME }}  # 기존 서비스 중단
            mv ${TARGET_DIR}/our-sample-latest.jar ${TARGET_DIR}/our-sample-v2.jar  # 파일 이름 변경
            ln -sf ${TARGET_DIR}/our-sample-v2.jar ${TARGET_DIR}/current  # 심볼릭 링크 업데이트
            sudo systemctl start ${{ secrets.SERVICE_NAME }}  # 서비스 재시작
          EOF

 

 


 

✅ 장점

  • 민감정보를 GitHub에 커밋하지 않음 ✅

  • 환경 설정을 GitHub Secrets에서 통합 관리 ✅

  • 다른 환경(dev/staging/prod)에도 적용하기 쉬움 ✅

 

✅ 필수 GitHub Secrets 설정 목록

 

 

 

 

이름설명

APP_PROPERTIES :  application.properties 파일 전체 내용

DEPLOY_KEY  개인 키 (private key) 전체 문자열

USER 예: sample

HOST 예: sample.com

PORT    예: 987

TARGET_DIR  예: /home/ubuntu

SERVICE_NAME   :  sample.service

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4.서버 설정: Systemd 서비스 생성

 

서버에서 /etc/systemd/system/sample.service 생성:

/home/ubuntu/ 에   깃허브오 생성된  jar 을 확인한다.

 

[Unit]
Description=Our sample Spring Boot App
After=network.target

[Service]
User=root
WorkingDirectory=/home/ubuntu
ExecStart=/usr/bin/java -Xmx256m -Xms128m -jar /home/ubuntu/our-sample-latest.jar
SuccessExitStatus=143
Restart=on-failure
RestartSec=5
StandardOutput=append:/home/sample/output.log
StandardError=append:/home/sample/error.log

[Install]
WantedBy=multi-user.target

 

 

 

# 실행 권한 및 등록
sudo systemctl daemon-reexec
sudo systemctl daemon-reload
sudo systemctl enable sample

 

 

.service 파일을 수정했다면:

sudo systemctl daemon-reload

 

그 다음 서비스를 다시 시작하세요:   서비스 재시작:

sudo systemctl restart sample.service

 

이렇게 하면 경고 없이 정상적으로 서비스가 시작됩니다.

이후 상태를 확인하려면:

sudo systemctl status sample.service

 

 


 

5.Nginx Reverse Proxy 설정

 

/etc/nginx/sites-available/sample:

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

 

sudo ln -s /etc/nginx/sites-available/sample /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx


 

 

 

6. 무중단 배포 전략 (심볼릭 링크 방식)

  1. 새 .jar → sample-v2.jar

  2. current → sample-v2.jar 로 심볼릭 링크 교체

  3. systemd 서비스 재시작

 

 

 

 

===========================================

==================  ========================

=====================================

 

 

 

 

 

✅ 그러면 위  설정한 방식이 정말 무중단 배포 일까?


GitHub Actions 스크립트 중 Restart service via SSH 부분은 이렇게 되어 있죠:

sudo systemctl stop ${{ secrets.SERVICE_NAME }}
mv ${TARGET_DIR}/our-advertising-latest.jar ${TARGET_DIR}/our-advertising-v2.jar
ln -sf ${TARGET_DIR}/our-advertising-v2.jar ${TARGET_DIR}/current
sudo systemctl start ${{ secrets.SERVICE_NAME }}

 

이 방식의 동작은?

  1. 기존 서비스 중단
    → systemctl stop으로 기존 Spring Boot 앱 프로세스를 중단시킴.

  2. JAR 파일 이름 변경 및 링크 갱신
    → our-advertising-latest.jar를 our-advertising-v2.jar로 이름 변경하고,
    → /home/advertising/current라는 링크를 새 파일로 재지정함.

  3. 새로운 서비스 시작
    → systemctl start로 새 JAR 파일을 실행.

 

문제: 이건 "무중단" 배포가 아님

이 과정에서 분명히 잠깐의 다운타임이 발생합니다.

  • stop → 애플리케이션 중단

  • start → 다시 기동되기까지 수 초 소요

 

이 시간 동안에는 서비스가 사용 불가 상태입니다.
즉, 기존 요청이 끊기고, 새 요청도 에러가 날 수 있습니다.

 

 

 

✅ 그럼 진짜 무중단 배포는 어떻게 하나요?

1. 리버스 프록시 + 포트 스위칭 방식 (추천)

Nginx 또는 HAProxy 같은 리버스 프록시를 앞단에 두고,
백엔드 앱을 두 개의 포트(예: 8081, 8082) 중 하나에 번갈아 배포한 뒤 프록시 설정을 라이브에서 스위칭하는 방식입니다.

 

사용자 요청
   ↓
+--------+      +----------------------+        +----------------------+
| Nginx  | ───▶ | Spring Boot @8081    |  또는  | Spring Boot @8082    |
+--------+      +----------------------+        +----------------------+
         ↕               ↑                        ↑
      config reload      │                        │
                         └──── 새 버전 배포중 ────┘

 

 

  • 새 포트에 새 앱 기동

  • 상태 확인 후 Nginx 설정 변경 (nginx -s reload)

  • 기존 포트 앱 종료

 

이렇게 하면 절대 요청이 끊기지 않습니다.

 

 

 

2. 배포 자동화 도구 사용 (예: Kubernetes, AWS ECS, Naver CLOVA)

 

이들은 기본적으로 무중단 배포를 위한 헬스체크, 롤링 업데이트, 로드 밸런서 기능을 제공합니다.

 

 

✅ 결론

 

요약

지금 설정은 JAR 파일 교체 후 서비스 재시작을 수동으로 하는 방식으로,
 

엄밀히 말해 무중단 배포는 아닙니다.

이를 해결하려면 Nginx를 이용한 포트 스위칭 방식을 도입하거나,
Spring Boot 앱 자체를 클러스터 구조나 컨테이너 오케스트레이션 환경으로 올리는 것이 필요합니다.

 

 

현재 가장 널리 사용되는 무중단 배포 방식은 Kubernetes 기반의 롤링 업데이트 방식입니다.

 

 

 

⭕ 무중단 배포 방식 중 가장 많이 쓰이는 것

 

 

 

 

 

 

Kubernetes가 무중단에 강한 이유

 

Kubernetes는 기본적으로 무중단 배포를 위한 기능이 포함되어 있습니다:

  • 롤링 업데이트 (Rolling Update):
    기존 Pod를 천천히 교체하며 새 버전을 배포
    → 트래픽은 정상 동작 중인 Pod로만 전달

  • 헬스체크 (Liveness/Readiness Probes):
    새 Pod가 완전히 준비되었는지 확인 후에만 트래픽 전달
    → 아직 준비 안 된 앱에 요청 전달 안 함

  • 자동 롤백:
    새 버전이 문제 생기면 자동으로 이전 버전으로 되돌림

  • 서비스 디스커버리 및 로드밸런싱 내장

 

 

 

 

심플하게

  • Nginx + Spring Boot 포트 스위칭 무중단 배포 구성도 가능하다.

 

 

 

 

★현재 상황에서 Kubernetes 설정이 "어려운 이유

 

 

기존에 사용 중인 환경이 단순 EC2 + SSH 배포단일 서버 기반 Spring Boot 서비스라면, Kubernetes를 도입하려면 다음과 같은 준비가 필요합니다:

1. 클러스터 환경 필요

  • Kubernetes는 Pod, Node, Cluster 개념이 전제입니다.

  • AWS EKS, GCP GKE, Azure AKS 같은 클라우드 관리형 서비스를 사용하거나, 자체적으로 kubeadm 등으로 클러스터를 구성해야 합니다.

  •  

2. Spring Boot를 Docker 이미지로 컨테이너화

  • K8s에서는 JAR 파일을 직접 배포하지 않습니다.

  • 반드시 Docker 이미지로 만들어야 합니다.

  •  

3. K8s 배포 설정 파일 필요

  • deployment.yaml, service.yaml, ingress.yaml 등을 작성해야 함

  • 그리고 헬스체크(Liveness/Readiness probe) 설정도 필요함

4. CI/CD 파이프라인 재구성 필요

  • GitHub Actions → JAR 복사 방식은 불가능

  • Docker 이미지 빌드 + 컨테이너 레지스트리(ECR, GCR) 푸시 + kubectl apply or helm 배포

 


 

Kubernetes로 가기 위해 필요한 것 정리

 

항목                                                            설명

✅ Dockerize                               Spring Boot 앱을 Docker 이미지로 만들기

✅ Kubernetes Cluster                      로컬 Minikube 또는 클라우드 EKS, GKE 등 준비

✅ YAML 설정 파일                                           Deployment, Service, Ingress, ConfigMap 등

✅ CI/CD 수정                                                     JAR → Docker 이미지 빌드 & 푸시 + K8s 배포

✅ 도메인 + 로드밸런서 (선택 )                         외부에서 접속 가능하게 설정

 

 

 

시작이 부담되면?

1. Docker + Nginx 무중단 배포 먼저 시도

  • 서버 하나에 docker-compose로도 Nginx + Spring Boot 무중단 배포 가능

  • 복잡도는 낮고 실전 배포처럼 구성 가능

 

 

 

 

 

 

 

결론 요약

학습/로컬 테스트는 하루 가능


✅ 실무용 클러스터 + 무중단 배포까지는 최소 2~3일 소요

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

면전에서 남을 즐겨 칭찬하는 사람은 또한 뒤에서 남을 즐겨 헐뜯는다. -장자

댓글 ( 0)

댓글 남기기

작성