쿠버네티스 환경에서 Web - WAS - DB 3계층 아키텍처를 직접 설계하고 구현하는 과제다.
단순히 컨테이너를 띄우는 것에서 끝나지 않고, 실제 운영 환경에서 요구되는 보안, 설정 관리, 스토리지 분리까지 모두 적용했다.
외부 사용자
│
▼
[ Ingress Controller ] ← 3tier.com 도메인 라우팅
│
▼
[ nginx Pod ] ← ConfigMap으로 nginx.conf 주입
│ (proxy_pass → tom-svc:8080)
▼
[ Tomcat Pod ] ← ConfigMap(index.jsp) + PVC(JDBC 라이브러리)
│ (JDBC 연결 → db-svc:3306)
▼
[ MySQL Pod ] ← Secret으로 DB 자격증명 주입
| 항목 | 요구사항 |
|---|---|
| DB 자격증명 | Secret으로 관리 |
| nginx 설정 | nginx.conf라는 이름의 ConfigMap 사용 |
| JDBC 라이브러리 | PV/PVC 볼륨으로 주입 |
| WAS, DB 서비스 타입 | ClusterIP (외부 노출 없음) |
| 도메인 라우팅 | Ingress로 구성 |
DB 접속 정보는 코드나 환경변수에 하드코딩하지 않고 Secret으로 분리했다.
Secret은 base64 인코딩 + etcd 암호화 저장으로 일반 ConfigMap보다 보안 수준이 높다.
# db-sec.yml
apiVersion: v1
kind: Secret
metadata:
name: db-sec
type: Opaque
stringData:
MYSQL_ROOT_PASSWORD: test123
MYSQL_USER: tomuser
MYSQL_PASSWORD: test123
MYSQL_DATABASE: tomdb
MySQL Deployment는 위 Secret을 envFrom으로 통째로 주입했다.
서비스 타입은 ClusterIP로 설정해 클러스터 내부에서만 접근 가능하도록 제한했다.
# db.yml (Service 부분)
spec:
selector:
app: db
ports:
- port: 3306
targetPort: 3306
# type 미지정 = 기본값 ClusterIP
Tomcat은 두 가지 외부 의존성을 가진다.
NFS 서버(마스터 노드)의 /shared/tom 디렉토리에 JDBC jar 파일을 미리 복사해두고, PV로 선언해 Tomcat Pod에 마운트했다.
# NFS 서버에 JDBC 라이브러리 배치
mkdir /shared/tom
chmod 777 -R /shared/tom
cp mysql-connector-java-8.0.23.jar /shared/tom
# pvpvc.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: tom-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /shared/tom
server: 211.183.3.100
DB 연결 로직이 담긴 JSP 파일을 ConfigMap으로 관리했다.
핵심은 DB 호스트를 IP가 아닌 서비스 이름(db-svc)으로 지정한 것이다.
쿠버네티스 내부 DNS가 서비스 이름을 ClusterIP로 자동 해석해준다.
# tom-configmap.yml (핵심 부분)
data:
index.jsp: |
<%
String Url = "jdbc:mysql://db-svc/tomdb"; # svc 이름 = 내부 DNS 주소
String Id = "tomuser";
String Pass = "test123";
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(Url, Id, Pass);
out.println("was-db Connection Success!");
%>
Tomcat Pod에서는 두 볼륨을 subPath로 마운트해 기존 컨테이너 파일을 덮어쓰지 않고 특정 파일만 교체했다.
# tom.yml (volumeMounts 부분)
volumeMounts:
- name: index-config-vol
mountPath: /usr/local/tomcat/webapps/ROOT/index.jsp
subPath: index.jsp # 해당 파일만 교체
- name: lib-vol
mountPath: /usr/local/tomcat/lib/mysql-connector-java-8.0.23.jar
subPath: mysql-connector-java-8.0.23.jar
nginx는 리버스 프록시 역할을 한다. 외부 요청을 받아 Tomcat으로 전달하는 설정을 ConfigMap으로 관리했다.
# nginx-configmap.yml
data:
default.conf: |
server {
listen 80;
location / {
proxy_pass http://tom-svc:8080/; # Tomcat svc로 프록시
}
}
nginx Pod도 subPath를 사용해 /etc/nginx/conf.d/default.conf 파일만 교체했다.
외부에서 3tier.com으로 접속하면 nginx Service로 라우팅되도록 Ingress를 구성했다.
# ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: 3tier-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: 3tier.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
DNS가 없는 환경이므로 /etc/hosts에 Ingress Controller의 External IP를 직접 등록해 테스트했다.
# /etc/hosts에 등록
<ingress-controller-external-ip> 3tier.com
# 접속 확인
curl 3tier.com
# → was-db Connection Success!
Namespace: default
├── Secret: db-sec
├── ConfigMap: tom-configmap, nginx-configmap
├── PV/PVC: tom-pv / tom-pvc (NFS, ReadWriteMany)
│
├── MySQL
│ ├── Deployment: db-dep (envFrom: db-sec)
│ └── Service: db-svc (ClusterIP, 3306)
│
├── Tomcat
│ ├── Deployment: tom-dep (ConfigMap + PVC 마운트)
│ └── Service: tom-svc (ClusterIP, 8080)
│
├── nginx
│ ├── Deployment: nginx-dep (ConfigMap 마운트)
│ └── Service: nginx-svc (ClusterIP, 80)
│
└── Ingress: 3tier-ingress (3tier.com → nginx-svc)
서비스 이름 = 내부 DNS 주소
쿠버네티스 내부에서 서비스 간 통신은 IP가 아닌 서비스 이름으로 한다.
db-svc, tom-svc처럼 이름 자체가 주소가 되는 것이 마이크로서비스 아키텍처의 핵심이다.
subPath 마운트
ConfigMap이나 PVC를 볼륨으로 마운트할 때 디렉토리 전체가 아닌 특정 파일만 교체하고 싶을 때 subPath를 사용한다. 이걸 모르면 컨테이너 내부의 기존 파일이 전부 사라진다.
Secret vs ConfigMap
DB 비밀번호처럼 노출되면 안 되는 값은 반드시 Secret으로 분리한다.
ConfigMap은 일반 설정값, Secret은 민감한 값이라는 원칙을 지키는 것이 보안의 기본이다.
ClusterIP로 내부 서비스 격리
WAS와 DB는 외부에서 직접 접근할 필요가 없다. ClusterIP로 설정하면 클러스터 내부에서만 통신 가능하고, 외부 노출을 최소화할 수 있다.
이번 velog는 AWS Kiro를 통해 작성하였다.