Kubernetes에서의 볼륨(Volume)

artp·2025년 1월 13일

kubernetes

목록 보기
16/18
post-thumbnail

볼륨(Volume)이란?

파드(Pod)의 한계와 볼륨(Volume)의 필요성

쿠버네티스에서 파드(Pod)는 업데이트 시 기존 파드의 일부만 수정하지 않고, 새로운 파드를 생성하여 교체하는 방식으로 동작합니다. 이로 인해 파드가 삭제되면 내부에 저장된 데이터도 함께 사라지게 됩니다.
이러한 특성은 일시적인 데이터 처리에는 적합하지만, 데이터베이스나 로그 저장소처럼 데이터 영속성이 필요한 애플리케이션에서는 문제가 될 수 있습니다.
이 문제를 해결하기 위해 볼륨(Volume)이라는 개념이 사용됩니다. 볼륨은 데이터를 영구적으로 저장할 수 있는 공간을 제공하며, 파드가 삭제되어도 데이터를 유지할 수 있도록 설계되었습니다.

볼륨의 종류

볼륨이란 데이터를 영속적으로 저장하기 위한 방법입니다. 쿠버네티스에서 볼륨은 크게 2가지 종류로 나뉩니다.

1. 로컬 볼륨(Local Volume)

로컬 볼륨은 파드 내부의 공간 일부를 볼륨으로 사용하는 방식입니다. 이 방식은 파드가 삭제되면 데이터도 함께 삭제됩니다.
데이터 영속성이 없기 때문에 실제로는 거의 사용하지 않으며, 임시 데이터 저장이나 테스트 용도로만 적합합니다.

2. 퍼시스턴트 볼륨(Persistent Volume, PV)

퍼시스턴트 볼륨은 파드 외부의 공간 일부를 볼륨으로 사용하는 방식입니다. 이 방식은 파드가 삭제되더라도 데이터가 유지되며, 클라우드 스토리지(AWS EBS 등) 또는 네트워크 스토리지를 활용하여 구현됩니다.
데이터베이스나 로그 저장소처럼 영속성이 필요한 애플리케이션에 주로 사용되며, 현업에서는 주로 이 방식을 많이 활용합니다.

  • 쿠버네티스 내부의 공간 일부를 사용하는 경우
  • 외부 저장소(AWS EBS 등)를 사용하는 경우

퍼시스턴트 볼륨 클레임(Persistent Volume Claim, PVC)

퍼시스턴트 볼륨(PV)은 직접 파드에 연결되지 않고, 퍼시스턴트 볼륨 클레임이라는 일종의 중개자를 통해 연결됩니다. PVC는 사용자가 요청하는 스토리지의 크기, 접근 모드 등을 정의하고, 이를 기반으로 적절한 PV를 할당받아 데이터를 사용할 수 있도록 합니다.
이 구조를 통해 사용자는 스토리지 리소스의 세부 사항을 몰라도 쉽게 접근할 수 있습니다.

쿠버네티스 구조에서의 볼륨 사용 흐름

1. 퍼시스턴트 볼륨(PV)

클러스터 관리자가 데이터를 저장할 외부 저장소를 미리 준비하고 정의합니다. 예를 들어, 클라우드 스토리지(AWS EBS 등)나 네트워크 드라이브(NFS)를 설정해 둡니다.

2. 퍼시스턴트 볼륨 클레임(PVC)

개발자가 애플리케이션에서 필요한 저장 공간(크기와 사용 방식)을 요청합니다. 이 요청은 PVC를 통해 퍼시스턴트 볼륨(PV)과 연결됩니다.

3. 파드(Pod)

PVC를 참조하여 필요한 저장 공간을 볼륨으로 마운트(연결 작업)합니다. 이렇게 연결된 저장소를 통해 데이터를 저장하거나 불러올 수 있습니다.

  • 마운트: 저장소를 운영체제에서 사용할 수 있도록 연결하는 작업

실습1. 디플로이먼트(Deployment)를 활용해 MySQL 실행시키기

1. 매니페스트 파일 생성

mysql-deployment.yaml

apiVersion: apps/v1
kind: Deployment

# 메타데이터: 디플로이먼트의 이름과 기본 정보를 정의
metadata:
  name: mysql-deployment  # 디플로이먼트 이름

# 디플로이먼트의 스펙 정의
spec:
  replicas: 1  # 생성할 파드의 복제본 개수
  selector:
    matchLabels:
      app: mysql-db  # 파드 선택 기준. 아래 정의된 파드와 매칭되는 레이블

  # 디플로이먼트가 관리할 파드의 템플릿 정의
  template:
    metadata:
      labels:
        app: mysql-db  # 파드 레이블. 디플로이먼트의 selector와 매칭됨
    spec:
      containers:
        - name: mysql-container  # 컨테이너 이름
          image: mysql  # MySQL 이미지를 사용
          ports:
            - containerPort: 3306  # 컨테이너에서 MySQL이 사용하는 포트
          env:
            # 환경 변수 설정: MySQL 루트 비밀번호
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret  # mysql-secret에서 값을 가져옴
                  key: mysql-root-password  # secret 내에서 사용하는 키
            # 환경 변수 설정: 기본 데이터베이스 이름
            - name: MYSQL_DATABASE
              valueFrom:
                configMapKeyRef:
                  name: mysql-config  # mysql-config ConfigMap에서 값을 가져옴
                  key: mysql-database  # ConfigMap 내에서 사용하는 키

mysql-secret.yaml

apiVersion: v1
kind: Secret

# 메타데이터: Secret의 이름과 기본 정보를 정의
metadata:
  name: mysql-secret  # Secret 이름

# stringData: Key-Value 형식으로 민감한 데이터를 저장
stringData:
  mysql-root-password: password123  # MySQL의 루트 비밀번호

mysql-config.yaml

apiVersion: v1
kind: ConfigMap

# 메타데이터: ConfigMap의 이름과 기본 정보를 정의
metadata:
  name: mysql-config  # ConfigMap 이름

# 데이터: Key-Value 형식으로 저장할 설정값
data:
  mysql-database: kube-practice  # MySQL에서 사용할 기본 데이터베이스 이름

mysql-service.yaml

apiVersion: v1
kind: Service

# 메타데이터: Service의 이름과 기본 정보를 정의
metadata:
  name: mysql-service  # Service 이름

# 스펙: Service의 동작 방식을 정의
spec:
  type: NodePort  # Service의 타입을 NodePort로 설정 (외부에서 접근 가능)
  selector:
    app: mysql-db  # 해당 레이블을 가진 파드와 연결
  ports:
    - protocol: TCP  # 사용 프로토콜. MySQL은 TCP를 사용
      nodePort: 30002  # 클러스터 외부에서 접근할 때 사용할 포트
      port: 3306  # 클러스터 내부에서 Service에 접근하기 위한 포트
      targetPort: 3306  # 파드의 컨테이너에서 사용하는 포트

2. 매니페스트 파일 기반으로 오브젝트 생성

# Secret 오브젝트 생성
$ kubectl apply -f mysql-secret.yaml
# ConfigMap 오브젝트 생성
$ kubectl apply -f mysql-config.yaml
# Deployment 오브젝트 생성
$ kubectl apply -f mysql-deployment.yaml
# Service 오브젝트 생성
$ kubectl apply -f mysql-service.yaml

3. DB 접속 확인

DataGrip에서 DB 접속을 확인합니다.

root 계정으로 접속합니다.

kube-practice 데이터베이스가 잘 생성된 것을 확인할 수 있습니다.

4. 새로운 데이터베이스 생성

5. MySQL 재시작 후 데이터베이스 확인

디플로이먼트 재시작

$ kubectl rollout restart deployment mysql-deployment
  • 디플로이먼트를 재시작하여 MySQL 파드를 새로 생성합니다. 이 과정에서 기존 파드가 삭제되고 새로운 파드가 생성됩니다.

데이터베이스 확인

  • 이전에 생성했던 artp 데이터베이스가 사라졌습니다. 이는 MySQL 파드가 삭제되면서 파드 내부에 저장된 데이터가 함께 삭제되었기 때문입니다.
  • 데이터베이스와 같은 영구 데이터를 사용하는 애플리케이션에서는 데이터가 손실되지 않도록 볼륨(Volume)을 설정해야 합니다. 볼륨을 활용하면 파드가 삭제되어도 데이터를 보존할 수 있습니다.

실습2. 볼륨(Volume)을 활용해 MySQL 실행시키기

데이터베이스와 같은 영구 데이터를 보존하기 위해서는 볼륨(Volumne)을 설정해야 합니다.

1. 퍼시스턴트 볼륨(PV) 및 퍼시스턴트 볼륨 클레임(PVC) 정의

mysql-pv.yaml

apiVersion: v1
kind: PersistentVolume

metadata:
  name: mysql-pv  # PV(Persistent Volume)의 이름입니다.

spec:
  storageClassName: my-storage  # StorageClass의 이름입니다.
  capacity:
    storage: 1Gi  # 제공되는 스토리지 용량입니다 (1GiB).
  accessModes:
    - ReadWriteOnce  # 한 노드에서만 읽기 및 쓰기가 가능합니다.
  hostPath:
    path: "/mnt/data"  # 호스트 머신의 경로입니다.

mysql-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim

metadata:
  name: mysql-pvc  # PVC(Persistent Volume Claim)의 이름입니다.

spec:
  storageClassName: my-storage  # 사용할 StorageClass의 이름입니다.
  accessModes:
    - ReadWriteOnce  # 한 노드에서만 읽기 및 쓰기가 가능합니다.
  resources:
    requests:
      storage: 1Gi  # 요청하는 스토리지 용량입니다 (1GiB).

2. 기존 디플로이먼트 수정

mysql-deployment.yaml

apiVersion: apps/v1
kind: Deployment

metadata:
  name: mysql-deployment  # Deployment의 이름입니다.

spec:
  replicas: 1  # 실행할 Pod의 복제본 수를 설정합니다.
  selector:
    matchLabels:
      app: mysql-db  # 이 레이블을 기준으로 Pod를 선택합니다.

  template:
    metadata:
      labels:
        app: mysql-db  # Pod에 추가할 레이블입니다.
    spec:
      containers:
        - name: mysql-container  # 컨테이너의 이름입니다.
          image: mysql  # 사용할 Docker 이미지를 지정합니다.
          ports:
            - containerPort: 3306  # MySQL의 기본 포트입니다.
          env:
            - name: MYSQL_ROOT_PASSWORD  # MySQL의 루트 비밀번호 환경 변수를 설정합니다.
              valueFrom:
                secretKeyRef:
                  name: mysql-secret  # 비밀번호를 저장한 Secret의 이름입니다.
                  key: mysql-root-password  # Secret에서 사용할 키입니다.
            - name: MYSQL_DATABASE  # MySQL 데이터베이스 이름 환경 변수를 설정합니다.
              valueFrom:
                configMapKeyRef:
                  name: mysql-config  # ConfigMap의 이름입니다.
                  key: mysql-database  # ConfigMap에서 사용할 키입니다.
          volumeMounts:
            - name: mysql-persistent-storage  # Persistent Volume을 마운트합니다.
              mountPath: /var/lib/mysql  # 데이터가 저장될 경로입니다.
      volumes:
        - name: mysql-persistent-storage  # Persistent Volume의 이름입니다.
          persistentVolumeClaim:
            claimName: mysql-pvc  # PVC(Persistent Volume Claim)의 이름입니다.

3. 매니페스트 파일 반영

$ kubectl apply -f mysql-pv.yaml
$ kubectl apply -f mysql-pvc.yaml
$ kubectl apply -f mysql-deployment.yaml

4. 새로운 데이터베이스 생성

데이터베이스 접속 후 새로운 데이터베이스(artp)를 생성합니다.

5. 디플로이먼트 재시작

$ kubectl rollout restart deployment mysql-deployment
  • 디플로이먼트를 재시작하여 MySQL 파드를 새로 생성합니다.

6. 데이터베이스 확인

디플로이먼트를 재시작한 후에도 artp 데이터베이스가 정상적으로 유지되고 있음을 확인할 수 있습니다.

7. 주요 흐름 정리

1. PersistentVolume(PV) 정의

  • 클러스터의 물리적 스토리지를 추상화한 개념입니다.
  • 여기서는 hostPath/mnt/data를 지정하여 클러스터의 특정 노드 로컬 디스크를 활용합니다.

2. PersistentVolumeClaim(PVC) 정의

  • 애플리케이션(Pod)이 필요한 스토리지를 요청하는 방식입니다.
  • PVC가 PV와 연결되면 애플리케이션이 스토리지를 사용할 수 있습니다.

3. Deployment 수정

  • Pod가 PV/PVC를 통해 데이터를 읽고 쓸 수 있도록 volumes와 volumeMounts를 설정합니다.
  • MySQL 컨테이너 내부의 /var/lib/mysql 디렉토리를 로컬 스토리지(/mnt/data)와 연결합니다.

4. 매니페스트 적용

  • 쿠버네티스 명령으로 설정을 클러스터에 적용합니다.
  • PVC가 PV를 요청하고, Pod가 PVC를 통해 스토리지를 사용합니다.

5. 결과 확인

  • MySQL 데이터베이스가 영구적으로 저장되어 재시작 후에도 데이터가 유지됩니다.

hostPath로 /mnt/data를 지정한다는 뜻

1. 클러스터와 노드

  • 쿠버네티스 클러스터는 여러 대의 노드(컴퓨터)로 구성되어 있습니다.
  • 각 노드는 자신의 로컬 디스크(컴퓨터 내부의 하드디스크)를 가지고 있습니다.

2. hostPath란?

  • hostPath는 특정 노드의 로컬 디스크에 있는 폴더를 가져다가 Pod(애플리케이션)에서 사용할 수 있도록 연결하는 방법입니다.
  • 여기서는 특정 노드의 /mnt/data 폴더를 MySQL Pod가 데이터를 저장할 수 있는 장소로 지정한 것입니다.

3. 흐름 설명

  • MySQL은 데이터를 저장하기 위해 내부적으로 /var/lib/mysql 디렉토리를 사용합니다.
  • hostPath를 설정하면 MySQL Pod의 /var/lib/mysql과 노드의 /mnt/data가 연결됩니다.
  • 결국, MySQL 데이터는 노드의 /mnt/data 폴더에 저장됩니다.

4. 클러스터의 특정 노드란?

  • Pod는 클러스터 안의 여러 노드 중 하나에서 실행됩니다.
  • MySQL Pod가 실행되는 노드가 특정 노드가 됩니다.
  • 따라서, /mnt/data에 MySQL 데이터를 저장하면, Pod가 실행된 그 노드의 디스크에 저장이 됩니다.

PV를 활용한 데이터 영속화의 개념

1. PV와 PVC의 역할

  • PersistentVolume(PV): 스토리지를 정의하고, 데이터를 저장할 공간을 제공합니다.
  • PersistentVolumeClaim(PVC): 애플리케이션(Pod)이 PV를 요청해 스토리지를 사용하도록 합니다.

2. 로컬 디스크(hostPath)를 사용하는 경우

  • PV가 hostPath를 사용할 경우, 특정 노드의 로컬 디스크를 스토리지로 지정합니다.
  • 데이터는 Pod이 실행된 노드의 디스크에 저장됩니다.

3. 한계

  • Pod 이동성 제한: Pod가 다른 노드로 이동하면, 새로운 노드에서 기존 데이터를 참조할 수 없습니다.
  • 데이터 종속성: 데이터가 특정 노드에 묶이게 됩니다.

4. 적합한 용도

  • 단일 노드에서 실행되거나, 테스트 및 개발 환경에서 사용하는 경우 적합합니다.

5. 대안

  • 네트워크 스토리지(NFS, Ceph 등)를 사용하면 데이터가 특정 노드에 종속되지 않고, 모든 노드에서 접근 가능하게 됩니다.

실습3. 백엔드(Spring Boot) 서버와 MySQL 연동하기

1. 스프링 부트 프로젝트 세팅

  • Java 버전: 17
  • Dependencies: Spring Boot DevTools, Spring Web, Spring Data JPA, MySQL Driver

2. AppController(RestController) 작성

@RestController
public class AppController {
	@GetMapping("/")
    public String home() {
    	return "Hello, World!";
    }
}
  • 간단한 REST API를 구현한 컨트롤러로, "/" 엔드포인트에 접속하면 "Hello, World!"를 반환합니다.

3. DB 연결 설정

src - main - resources - application.yaml
application.yaml

spring:
  datasource:
    url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver
  • MySQL 데이터베이스 연결 정보를 환경 변수로 설정합니다.

4. 불필요한 테스트 코드 삭제

프로젝트 생성 시 자동으로 생성된 테스트 코드를 삭제하여 초기 빌드 오류를 방지합니다.

5. Dockerfile 작성

Dockerfile

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]

6. Spring Boot 프로젝트 빌드

$ ./gradlew clean build
  • 스프링 부트 프로젝트를 빌드하여 JAR 파일을 생성합니다.

7. Docker 이미지 빌드

$ docker build -t spring-server .
  • Dockerfile을 기반으로 스프링 부트 서버 이미지를 생성합니다.

8. 매니페스트 파일 작성

spring-deployment.yaml

apiVersion: apps/v1
kind: Deployment

# Deployment 기본 정보
metadata:
  name: spring-deployment # Deployment 이름

# Deployment 세부 정보
spec:
  replicas: 3 # 생성할 파드의 복제본 수
  selector:
    matchLabels:
      app: backend-app # 아래에서 정의한 Pod 중 'app: backend-app'이라는 값을 가진 파드를 선택

  # 배포할 Pod 정의
  template:
    metadata:
      labels: # 레이블(= 카테고리)
        app: backend-app
    spec:
      containers:
        - name: spring-container # 컨테이너 이름
          image: spring-server # 컨테이너를 생성할 때 사용할 이미지
          imagePullPolicy: IfNotPresent # 로컬에서 이미지를 먼저 가져오고 없으면 레지스트리에서 가져옴
          ports:
            - containerPort: 8080 # 컨테이너에서 사용하는 포트를 명시적으로 표현
          env:
            - name: DB_HOST
              value: mysql-service # Service의 name만 입력하면 다른 서비스와 통신할 수 있음
            - name: DB_PORT
              value: "3306" # 숫자값을 문자로 인식하게 만들기 위해 쌍따옴표를 붙여야 함
            - name: DB_NAME
              value: kub-practice
            - name: DB_USERNAME
              value: root
            - name: DB_PASSWORD
              value: password123

spring-service.yaml

apiVersion: v1
kind: Service

# Service 기본 정보
metadata:
  name: spring-service

# Service 세부 정보
spec:
  type: NodePort
  selector:
    app: backend-app
  ports:
    - protocol: TCP # 서비스에 접속하기 위한 프로토콜
      port: 8080 # 쿠버네티스 내부에서 서비스에 접속하기 위한 포트 번호
      targetPort: 8080 # 매핑하기 위한 파드의 포트 번호
      nodePort: 30000 # 외부에서 사용자들이 접근하게 될 포트 번호

9. 매니페스트 파일 실행

$ kubectl apply -f spring-deployment.yaml
$ kubectl apply -f spring-service.yaml

10. MySQL과 연결된 서버 작동 확인

localhost:30000

11. 구조 이해하기

스프링 부트 백엔드와 MySQL은 쿠버네티스 환경에서 다음 그림과 같이 연동됩니다.

11.1 스프링 부트 서버 구성

  • 디플로이먼트:
    • 스프링 부트 서버는 디플로이먼트를 통해 3개의 파드로 복제(replica)되어 배포됩니다.
    • 디플로이먼트는 안정적인 애플리케이션 배포와 관리를 가능하게 합니다.
  • 파드와 컨테이너:
    • 각 파드 안에는 스프링 부트 애플리케이션이 실행되는 컨테이너가 있습니다.
    • 컨테이너는 8080번 포트(targetPort)에서 애플리케이션을 실행합니다.
  • 서비스(Spring Service):
    • 스프링 부트 파드들과 외부 요청 간 통신을 담당합니다.
    • 클러스터 외부의 사용자는 30000번 포트(nodePort)를 통해 서비스에 접근할 수 있습니다.
    • 서비스는 요청을 받아 내부의 파드로 트래픽을 라우팅합니다.

11.2 MySQL 데이터베이스 구성

  • 디플로이먼트:
    • MySQL 데이터베이스는 별도의 디플로이먼트를 통해 배포됩니다.
    • 디플로이먼트가 MySQL 파드를 생성하고 관리합니다.
  • 파드와 컨테이너:
    • MySQL 파드 내에서 MySQL 컨테이너가 실행되며, 데이터베이스는 3306번 포트(targetPort)에서 실행됩니다.
  • 서비스(MySQL Service):
    • MySQL 파드와 Spring Boot 파드 간 통신을 담당합니다.
    • MySQL 서비스는 Spring Boot 애플리케이션으로부터 요청을 받아 데이터베이스로 전달합니다.
  • 퍼시스턴트 볼륨(PV) 및 퍼시스턴트 볼륨 클레임(PVC):
    • MySQL 데이터베이스는 퍼시스턴트 볼륨을 사용하여 데이터를 영구적으로 저장합니다.
    • MySQL 디플로이먼트는 PVC를 통해 퍼시스턴트 볼륨을 마운트하여 데이터 손실을 방지합니다.

11.3 통신 흐름

11.3.1 사용자 → Spring Service

  • 사용자는 30000번 포트(nodePort)를 통해 Spring Boot 애플리케이션에 요청을 보냅니다.
  • Spring Service는 해당 요청을 적절한 Spring Boot 파드로 전달합니다.

11.3.2 Spring Boot 파드 → MySQL Service

  • Spring Boot 애플리케이션은 MySQL 데이터베이스에 연결하여 데이터를 읽거나 씁니다.
  • Spring Boot 파드는 MySQL 서비스(mysql-service)를 통해 MySQL 파드에 요청을 보냅니다.

11.3.3 MySQL Service → MySQL Pod

  • MySQL 서비스는 3306번 포트를 통해 MySQL 파드와 통신합니다.

11.3.4 MySQL Pod → 퍼시스턴트 볼륨(PV)

  • MySQL 데이터는 PVC를 통해 퍼시스턴트 볼륨(PV)에 저장됩니다.
  • MySQL 파드가 재시작되더라도 데이터는 볼륨에 저장되어 사라지지 않습니다.

실습 4. 컨피그맵(ConfigMap), 시크릿(Secret)을 활용한 환경변수 분리

4.1 ConfigMap 매니페스트 파일 생성

spring-config.yaml

apiVersion: v1
kind: ConfigMap

# ConfigMap 기본 정보
metadata:
  name: spring-config # ConfigMap 이름

# Key-Value 형식으로 설정값 저장
data:
  db-host: mysql-service
  db-port: "3306"
  db-name: kube-practice

4.2 Secret 매니페스트 파일 생성

spring-secret.yaml

apiVersion: v1
kind: Secret

# Secret 기본 정보
metadata:
  name: spring-secret # Secret 이름

# Key-Value 형식으로 민감한 정보 저장
stringData:
  db-username: root
  db-password: password123

4.3 Deployment 매니페스트 파일에서 환경변수 설정

spring-deployment.yaml

apiVersion: apps/v1
kind: Deployment

# Deployment 기본 정보
metadata:
  name: spring-deployment # Deployment 이름

# Deployment 세부 정보
spec:
  replicas: 3 # 생성할 파드의 복제본 개수
  selector:
    matchLabels:
      app: backend-app # 아래 정의한 Pod 중 'app: backend-app'인 값을 선택

  # 배포할 Pod 정의
  template:
    metadata:
      labels:
        app: backend-app
    spec:
      containers:
        - name: spring-container # 컨테이너 이름
          image: spring-server # 사용할 이미지
          imagePullPolicy: IfNotPresent # 로컬에서 이미지를 먼저 가져오고 없으면 레지스트리에서 가져옴
          ports:
            - containerPort: 8080 # 컨테이너에서 사용하는 포트를 명시적으로 표현
          env:
            # ConfigMap을 활용한 환경변수 설정
            - name: DB_HOST
              valueFrom:
                configMapKeyRef:
                  name: spring-config
                  key: db-host
            - name: DB_PORT
              valueFrom:
                configMapKeyRef:
                  name: spring-config
                  key: db-port
            - name: DB_NAME
              valueFrom:
                configMapKeyRef:
                  name: spring-config
                  key: db-name

            # Secret을 활용한 민감한 환경변수 설정
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  name: spring-secret
                  key: db-username
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: spring-secret
                  key: db-password

4.4 매니페스트 파일 반영하기

# ConfigMap 생성
$ kubectl apply -f spring-config.yaml

# Secret 생성
$ kubectl apply -f spring-secret.yaml

# Deployment 생성
$ kubectl apply -f spring-deployment.yaml

# Deployment 강제 재배포
$ kubectl rollout restart deployment spring-deployment

4.5 잘 반영됐는지 확인

실습 5. 보안을 위해 외부에서 MySQL 접근하지 못하도록 막기

기존 구성의 보안적인 문제점


현재 구조에서는 MySQL ServiceNodePort 방식으로 설정되어 있어, 외부에서 30002번 포트를 통해 MySQL에 직접 접근할 수 있습니다. 이는 보안상 취약한 점으로, 외부 사용자에게 DB에 대한 접근 권한이 노출될 위험이 있습니다.

서비스 종류 복습

1. NodePort

  • 쿠버네티스 내부와 외부에서 모두 접근이 가능합니다.
  • 외부에서 특정 포트(NodePort)를 통해 접속합니다.
  • 보안 취약점이 될 수 있습니다.

2. ClusterIP (기본 설정)

  • 쿠버네티스 클러스터 내부에서만 통신이 가능합니다.
  • 외부 접근을 차단합니다.
  • 안전한 내부 통신 환경을 제공합니다.

3. LoadBalancer

  • 외부의 로드 밸런서를 사용하여 클라우드 환경에서 외부 연결을 지원합니다.
  • 클라우드 기반 서비스에서 주로 사용합니다.

보안적인 문제점 해결 방법

NodePortClusterIP로 변경하여 MySQL에 대한 외부 접근을 차단합니다.

5.1 기존 Service 매니페스트 파일 수정

mysql-service.yaml

apiVersion: v1
kind: Service

# Service 기본 정보
metadata:
  name: mysql-service # Service 이름

# Service 세부 정보
spec:
  type: ClusterIP # 외부 접근 차단을 위한 Service 종류
  selector:
    app: mysql-db # 'app: mysql-db' 레이블을 가진 파드와 연결
  ports:
    - protocol: TCP # 프로토콜
      port: 3306 # 내부에서 Service가 사용할 포트
      targetPort: 3306 # 파드의 MySQL 컨테이너 포트
      # NodePort는 제거합니다.
  • type 필드 수정:
    • 기존: nodePort → 외부 네트워크 접근을 허용합니다.
    • 변경: clusterIP → 클러스터 내부에서만 접근이 가능합니다.
  • nodePort 제거:
    • 기존에는 외부 접근을 위해 지정된 포트 번호(nodePort: 30002)가 있었습니다.
    • 수정된 코드에서는 이 설정을 제거하여 외부 포트를 차단했습니다.

5.2 기존 Service 중단 후 확인

# 기존 서비스 삭제
kubectl delete service mysql-service

# Deployment 재시작
kubectl rollout restart deployment spring-deployment

5.3 ClusterIP Service 생성 및 확인

# 수정된 Service 파일 적용
$ kubectl apply -f mysql-service.yaml

# Deployment 재시작
$ kubectl rollout restart deployment spring-deployment

스프링 부트 서버 연결 확인

DB 접속 확인

5.4 포트 포워딩으로 DB 접속 허용

MySQL 관리 목적으로 특정 로컬 환경에서만 DB 접근이 필요할 경우, 포트 포워딩을 활용합니다.
포트 포워딩은 로컬 컴퓨터에서 특정 Pod와 연결을 허용합니다.

# kubectl port-forward pod/<파드 이름> <로컬 포트>:<파드의 컨테이너 포트>
$ kubectl port-forward pod/mysql-deployment-79b56744b4-pk87t 3306:3306
  • 포트 포워딩이 활성화된 동안만 로컬 컴퓨터에서 DB에 접속이 가능합니다.
  • 외부 네트워크의 접속 차단 상태를 유지합니다.

5.5 흐름 정리


위 그림은 쿠버네티스 클러스터 내부에서 MySQL과 Spring Boot 애플리케이션 간의 통신 구조를 보여줍니다. 그림을 바탕으로 흐름을 정리하면 다음과 같습니다.

1. 기존 문제점

  • MySQL ServiceNodePort(30002번 포트)로 설정되어 있어, 클러스터 외부에서 MySQL에 직접 접근할 수 있었습니다.
  • 외부 사용자가 MySQL 데이터베이스에 접근할 수 있는 위험이 존재하며, 보안에 취약한 상태였습니다.

2. 보안 개선 후 구조

  • MySQL의 ServiceClusterIP로 변경하여 외부 네트워크에서 접근할 수 없도록 설정했습니다.
    • MySQL은 이제 쿠버네티스 클러스터 내부에서만 통신이 가능합니다.
    • Spring Boot 애플리케이션(spring-service)이 내부적으로 MySQL에 접근하며, 외부에서는 이를 직접적으로 호출할 방법이 없습니다.
  • Spring Boot 애플리케이션의 Service는 여전히 NodePort를 통해 사용자와 통신하므로, 애플리케이션 서비스는 외부에서 접근이 가능합니다.

3. 데이터 흐름

  1. Spring Boot 애플리케이션과 사용자 간 통신:

    • 사용자는 브라우저 또는 클라이언트를 통해 Spring Boot 애플리케이션의 NodePort(30000번 포트)를 호출합니다.
    • NodePort는 Spring Boot의 Service(spring-service)와 연결되며, 클러스터 내부의 Spring Pod로 요청을 전달합니다.
  2. Spring Boot와 MySQL 간 통신:

    • Spring Boot 애플리케이션은 내부적으로 MySQL Service(mysql-service)와 통신합니다.
    • MySQL Service는 ClusterIP로 설정되어 있어 클러스터 내부에서만 접근 가능하며, Spring Boot 애플리케이션만 MySQL에 접근할 수 있습니다.
  3. MySQL 데이터베이스와 퍼시스턴트 스토리지:

    • MySQL Pod는 퍼시스턴트 볼륨 클레임(PVC)을 통해 퍼시스턴트 볼륨(PV)에 데이터를 저장합니다.
    • 이는 데이터베이스의 영속성을 보장합니다.

4. 보안적 개선

  • MySQL NodePort가 제거됨으로써, 외부 네트워크에서의 MySQL 접근이 차단되었습니다.
  • 클러스터 내부에서만 MySQL과 통신이 가능하여, 데이터베이스 보안이 강화되었습니다.

5. DB 관리 접근 방법

  • DB 관리 목적으로 MySQL에 접근해야 하는 경우, 포트 포워딩 명령을 사용하여 로컬 환경에서만 접근할 수 있도록 설정합니다:
    $ kubectl port-forward pod/<MySQL Pod 이름> 3306:3306
  • 이 명령은 로컬 머신에서만 MySQL Pod와 연결을 허용하며, 외부 네트워크에서는 차단 상태를 유지합니다.
profile
donggyun_ee

0개의 댓글