쿠버네티스는 메니페스트 파일을 통해 파드를 생성한다. 파드는 NginX와 Tomcat 두 종류를 사용하고, 그 두 종류의 파드를 서비스의 IP(클러스터 IP)를 기반으로 연동하게 될 것이다.
매니페스트 파일
쿠버네티스는 매니페스트(정의 파일)에 기재된 내용에 따라 파드를 생성한다. 매니페스트 파일의 내용을 쿠버네티스에 업로드하면 그 내용이 데이터베이스(etcd)에 ‘바람직한 상태’로 등록되는 것이다.
그리고 그 바람직한 상태로 서버 환경을 유지한다.
디플로이먼트
파드와 레플리카셋(ReplicaSet)에 대한 선언적 업데이트를 제공한다.
디플로이먼트에서 바람직한 상태를 설명하고, 디플로이먼트 컨트롤러(Controller)는 현재 상태에서 바람직한 상태로 비율을 조정하며 변경한다.
새 레플리카셋을 생성하는 디플로이먼트를 정의하거나 기존 디플로이먼트를 제거하고, 모든 리소스를 새 디플로이먼트에 적용할 수 있다.
디플로이먼트 매니페스트 파일의 구조

디플로이먼트의 메니페스트 파일을 작성해보겠다. 디플로이먼트의 스펙은 템플릿의 형태로 파드의 설정을 기재한다.
일단 대항목들부터 써보자.
apiVersion: <-- API 그룹 및 버전
kind: <-- 리소스 유형
metadata: <-- 메타데이터
spec: <-- 리소스 내용
- apiVersion: 이건 쿠버네티스 API의 버전을 나타낸다. 쿠버네티스에서 제공하는 여러 리소스들이 시간에 따라 발전하면서 API 버전도 변화하기 마련이다. 예를 들어,
v1
,apps/v1
같은 걸 여기에 적는다. - kind: 여기에는 쿠버네티스에서 만들고자 하는 리소스의 종류를 적는다. 예를 들면 파드, 서비스, 디플로이먼트 같은 것들이 있다. 이 값에 따라 쿠버네티스가 어떤 타입의 리소스를 만들지 결정한다.
- metadata: 이 항목은 리소스에 대한 메타데이터 정보를 담고 있어. 주로 리소스의 이름, 네임스페이스, 레이블, 주석 등을 여기에 포함한다. 이 정보들은 리소스를 식별하고 관리하는 데 쓰인다
- spec: 가장 중요한 부분 중 하나야. 이 ‘spec’은 리소스의 상세한 사양을 정의한다. 예를 들어 pod를 만든다면, 이 안에 컨테이너의 이미지, 포트 설정 등을 적는다. 리소스 타입에 따라 필요한 spec 정보가 달라진다
메타데이터와 스펙
메타데이터에는 리소스의 이름이나 레이블(키-값 쌍의 형태로 기재되는 메타데이터)을 기재한다.
스펙은 리소스의 내용을 정의한다. ‘어떤 리소스를 만들 것인가’에 해당하는 부분이다.
그럼 이제 그 아래의 중항목과 소항목을 보자
apiVersion:
kind:
metadata:
name: <-- 중항목. 디플로이먼트이름
spec:
selector: <-- 중항목. 셀렉터가 선택할 관리대상 테이블
matchLabels: <-- 소항목. 셀렉터가 선택할 관리대상 테이블
replicas: <-- 중항목. 레플리카 수 설정
template: <-- 중항목. 템플릿(파드의 정보)
metadata: <-- 소항목. 파드의 메타데이터를 기재
spec: <-- 소항목. 파드의 스펙을 기재
셀렉터 설정
디플로이먼트가 특정한 레이블이 부여된 파드를 관리할 수 있도록 하는 설정이다. matchLabels: 뒤로 레이블을 기재한다. 이 레이블은 template 아래의 metadata에 기재된 것이다.
레플리카 설정
파드의 개수를 관리한다.
템플릿 설정
생성할 파드의 정보를 기재한다. 기재할 내용은 파드를 설정할 내용, 메타데이터 및 스펙과 거의 같다. 실제로 설정을 작성하다보면 spec을 두 번 작성한다거나 name이 어느 대상의 이름인지 헷갈리기 쉽다. 그림을 하나 첨부하겠다

메타데이터, 스펙 기재 내용
템플릿 및의 메타데이터와 스펙 아래에 기재할 내용은 임의로 설정할 레이블과 컨테이너의 생성 내용이다.
metadata:
labels:
app: <-- 설정할 레이블
spec:
containers: <-- 컨테이너 명
- name: <-- 컨테이너 이름
image: <-- 이미지 이름 및 버전
ports: <-- 접속 포트 번호
- containerPort: <-- 컨테이너 포트
volumeMounts: <-- 마운트할 볼륨 내용
- name: <-- 볼륨 이름
mountPath: <-- 마운트할 컨테이너 내의 볼륨을 마운트 한다.
subPath: <-- 마운트할 특정 파일만 정
volumes:
- name:
configMap:
name:
컨테이너의 설정 내용으로는 컨테이너의 이름과 접속 포트, 그리고 (필요하다면) 볼륨을 지정할 수 있다. 볼륨은 안 지정하는 경우도 꽤 있으나 이번에 WEB과 WAS를 연동해야하니 NginX 설정파일과 Tomcat 의 index.jsp 내용을 변경해야 되니 볼륨 내용도 추가해야한다.
volumeMounts : 컨테이너 내부에서 볼륨을 어떻게 마운트할지를 정의한다. name은 볼륨의 이름이고 mountPath 마운트할 컨테이너 내의 경로, subpath는 특정 파일만 정의한다.
volumes : 실제 볼륨의 소스를 정의한다. 마찬가지로 name에서 볼륨 이름을 정의하고 configMap에서 설정 데이터의 이름을 기재한다.
여기서 configMap이라는 생소한 단어가 또 나왔다.
ConfigMap은 쿠버네티스(Kubernetes)에서 설정 정보나 데이터를 저장하고 관리하는 데 사용되는 API 오브젝트다.
이것을 사용함으로써 애플리케이션 코드와 설정 데이터를 분리할 수 있어서 유지보수와 애플리케이션 구성이 훨씬 쉬워진다.
그럼 ConfigMap의 구성을 알아보자
apiVersion:
kind: ConfigMap
metadata:
name:
data:
apiVersion : api 오브젝트 버전을 정의한다.
kind : ConfigMap은 이 오브젝트가 ConfigMap 타입임을 명시한다.
metadata : configmap의 이름과 마운트할 데이터를 각각 name과 data에서 정의한다.
WEB을 NginX로 쓰고 Tomcat을 WAS로 쓰려면 엔진엑스를 리버스 프록시로 설정해야하고, 그러려면 nginx.conf의 내용을 변경해야 한다.
이에 따라 ConfigMap 파일을 작성해주었다.
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
proxy_pass http://10.111.83.30:8080;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
설명하자면 쿠버네티스 API 버전은 v1이고, 메니페스트 종류를 ConfigMap으로, 이름을 nginx-config으로 설정한 것이다.
그리고 data에서 실제 nginx.conf 파일의 내용을 저장한다.
‘|’ 표시는 이제부터 이어지는 내용이 nginx.conf의 내용을 완전히 대체한다는 뜻이다. 여기서부터 작성되는 내용이 nginx.conf파일 내용의 전부가 된다.
여기에 Proxy_pass 내용을 넣어 <Tomcat의 서비스 IP>:8080으로 사용자의 요청을 전달하는 것이다.
그러면 ConfigMap도 정의했으니 이제 NginX 파드를 생성하기 위한 디플로이먼트 파일을 작성해 보겠다.
apiVersion: apps/v1 <--API 버전 설정
kind: Deployment <--종류는 디플로이먼트
metadata:
name: nginx-deployment <--메타데이터: 디플로이먼트 이름 지정
spec: <--스펙
selector:
matchLabels: <--셀렉터 레이블
app: nginx000ex99 <--레이블 이름 설정
replicas: 1 <--레플리카수 1개로 설정
template:
metadata:
labels:
app: nginx000ex99 <--똑같은 엔진엑스 레이블 사용
spec:
containers:
- name: nginx000con01 <--엔진엑스 컨테이너 이름
image: nginx:latest <--이미지 및 버전
ports:
- containerPort: 80 <--접속 포트번호 설정
volumeMounts: <--볼륨 마운트
- name: nginx-config-volume <--붙이려는 configMap 이름
mountPath: /etc/nginx/nginx.conf <--마운트 하려는 엔진엑스 컨테이너의 경로
subPath: nginx.conf
volumes: <--파드에 붙이려는 볼륨
- name: nginx-config-volume
configMap:
name: nginx-config <--붙이려는 configMap 이름
이렇게 메니페스트 파일을 작성해 주었다.
여기서 볼륨 마운트를 한 부분을 보자.
volumeMounts: 밑에 name 쪽에서 ConfigMap 파일에서 작성한 ConfigMap의 이름을 지정해주었다. 그리고 mountPath에서 리눅스 기반의 컨테이너 내에서 nginx 구성파일의 경로를 지정해 주었다.
그리고 template밑의 spec항목 밑의 volumes에서 위에서 설정한 ConfigMap을 적용해 준 것이다.
이렇게 되면 NginX 컨테이너 하나가 포함된 Pod가 메니페스트 파일에서 설정한 레플리카의 개수 1개 생성된다.
그 파이 안에 컨테이너 내의 /etc/nginx/nginx.conf 라는 경로에서 configMap에서 작성한 그 nginx.conf 파일 내용이 적용이 된다. 그리고 메니페스트 파일이 정의한 ‘건강한 상태를 유지하기 위해’ 이 엔진엑스 파드의 개수를 항상 유지한다.
서비스 파일 작성
그럼 이어서 서비스 파일을 작성해보자.
디플로이먼트와 서비스는 거의 세트라고 생각해도 된다. 서비스의 메니페스트 파일은 디플로이먼트보가 간단하다. 서비스의 역할은 파드로 들어오는 요청을 관리하는 것 이기 때문에 설정 내용도 통신과 관련된 것이다.
apiVersion:
kind: Service
metadata:
name: <--서비스의 이름
spec:
type: <--서비스의 유형
selector:
app:
ports: <--서비스의 포트
- port: <--컨테이너 포트
targetPort: <--통신에서 사용되는 포트
nodePort: <--셀렉터 설정
유형(type 설정)
유형은 외부로부터 서비스에 어떤 유형의 IP 주소(또는 DNS)로 접근할지를 설정한다.
유형 이름 | 내용 |
ClusterIP | 클러스터 ip를 통해 접근 |
NodePort | 워커 노드의 IP를 통해 서비스에 접근하도록 함 |
LoadBalancer | 로드밸런서의 IP를 통해 서비스에 접근 |
ExternalName | 파드에서 서비스를 통해 외부로 나가기 위한 설정 |
유형을 클러스터 IP로 설정하면 클러스터 IP를 통해 서비스에 접근한다. 클러스터 IP는 사설 IP 주소가 설정되어있어서 말 그대로 클러스터 내부의 통신에서만 사용 가능하다.
사용자를 웹 사이트에 접근시키기 위해서는, 유형 이름을 LoadBalancer 로 하고 로드밸런서의 IP 주소로 접근하게 한다. 실제로도 실무에서는 LoadBalancer로 설정하는 경우가 대부분이다.

여기서는 NodePort로 설정하면 워커 노드에 직접 접근할 수 있다. 워커노드가 직접 무언가를 처리하는 구성을 취했거나 개발 목적 등 특정 워커도드로 접근해야 할 경우에 사용한다.
이 실습에서는 로드밸런서가 없어서 NodePort를 사용하는 거다.
이러면 브라우저에서 서비스 메니페스트에서 지정한 워커 노드 포트로 접근하게 된다.

실제 서비스 메니페스트 파일을 작성해보자.
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx000ex99
ports:
- port: 80
targetPort: 80
nodePort: 30080
apiVersion, kind, metadata:
apiVersion: 오브젝트의 쿠버네티스 API 버전을 v1으로 설정했다.
kind: Service는 이 오브젝트가 서비스임을 명시한다.
metadata:
name: 이 서비스의 이름을 nginx-service로 정의한다.
spec:
type: 여기서 서비스의 타입을 NodePort로 설정한 것이다.
selector에서 app: app 라벨이 nginx000ex99인 파드들을 타겟으로 삼겠다는 의미이다. 이 서비스가 관리할 파드들을 어떻게 식별할지 정의한 것이다.
ports : 서비스의 포트 설정을 다룬다.
nodePort: 30080은 클러스터 외부에서 이 서비스에 접근할 때 사용할 포트를 지정한다.
즉, 클러스터 외부에서 NodeIP:30080을 통해 이 서비스에 접근할 수 있게 되는 것이다.
결론적으로, NginX를 실행하는 파드들을 nginx-service라는 이름의 서비스를 통해 관리할 수 있게 된다.
서비스 타입은 nodePort다. 외부에서 이 서비스에 접근하려면 클러스터의 노드 IP와 30080 포트를 사용해야 한다.
이렇게 서비스를 사용하면 파드들의 IP나 포트 변경에 영향을 받지 않고 안정적으로 트래픽을 관리할 수 있게 된다.
Tomcat 메니페스트 파일 작성
그럼 NginX의 모든 메니페스트 파일을 작성했으니 이제 Tomcat의 메니페스트 내용을 작성하자.
로컬 컴퓨터에서 브라우저로 접속을 했을 때 톰캣의 index.jsp 파일의 내용이 뜨게 해야되기 때문에 컨테이너에서 /usr/local/tomcat/webapps/ROOT/index.jsp 경로에 적용할 내용을 ConfigMap에서 작성해줘야 된다.
apiVersion: v1
kind: ConfigMap
metadata:
name: tomcat-index
data:
index.jsp: |
<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>WEB-WAS 연동 성공</title>
</head>
<body>
<h1>WEB-WAS 연동 성공</h1>
</body>
</html>
Tomcat 서비스의 디플로이먼트 파일 작성
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
spec:
selector:
matchLabels:
app: tomcat000ex99
replicas: 1
template:
metadata:
labels:
app: tomcat000ex99
spec:
containers:
- name: tomcat000con01
image: tomcat:latest
ports:
- containerPort: 8080
volumeMounts:
- name: tomcat-index-volume
mountPath: /usr/local/tomcat/webapps/ROOT/index.jsp
subPath: index.jsp
volumes:
- name: tomcat-index-volume
configMap:
name: tomcat-index
volumeMounts 항목 밑의 mountPath: 항목에서 /usr/local/tomcat/webapps/ROOT/index.jsp 경로에 ConfigMap파일에서 작성한 index.jsp 파일 내용이 적용되게 만들었다. 위에서 nginx.conf를 configMap으로 정의한것과 마찬가지다.
Tomcat 서비스 메니페스트 파일 작성
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
selector:
app: tomcat000ex99
ports:
- port: 8080
targetPort: 8080
포트번호를 port와 target포트를 모드 8080으로 지정했다.
이렇게 되면 위에 nginx.conf에서 서비스 IP:8080 으로 프록시 패스를 설정한 것에 따라 Tomcat 파드르 사용자의 요청이 넘어오게 된다.
이렇게 K8S의 6개의 메니페스트 파일을 작성해서 WEB과 WAS를 연동할 도구를 만들어 냈다. 필자는 이 메니페스트 파일의 내용을 모두 nginx-tomcat-deployment.yml이라는 한 파일에 작성하고, 각각의 내용을 —로 구분해 주었다.

이제 윈도우에서 powershell을 실행하여 이 메니페스트 파일 내용을 적용, K8S 파드를 생성하고 실행해주자.
kubectl apply -f C:\DockerANDk8s\kube_folder\nginx-tomcat_v2_folder\nginx-tomcat-deployment.yaml
Docker 데스크톱을 보면 이렇게 컨테이너들이 생성되어있다. 메니페스트 파일에서 정의한 내용에 따라 파드가 생성되고 그 파드안에 컨테이너가 생긴것이다.

쿠버네티스는 각 서비스 IP 기반으로 통신한다. 그래서 nginx.conf 파일에서 프록시 패스(Proxy_pass)를 설정할 때 Tomcat 서비스가 가지고 있는 클러스터 IP로 요청이 넘어가게 한 것이다.
아래 그림에서 클러스터 IP는 Kubernetes 클러스터 전체에서 사용되는 내부 IP 주소 범위를 뜻한다. 각 서비스는 이 클러스터 IP 범위 내에서 IP 주소가 정해진다.

서비스 IP를 알아내는 명령을 파워쉘에 쳐보자.
kubectl get services

여기서 나온 IP 들이 서비스 IP이다. 이 IP를 nginx.conf 파일의 프록시 패스에 적용해주어서 서비스가 실행되는 것이다.
맨 밑에 tomcat-service의 IP가 10.111.83.30이라고 나와있다.
그래서 nginx.conf 파일에서 proxy_pass http://10.111.83.30:8080; <– 이렇게 설정해준 것이다.
nginx의 노드 포트를 30080으로 설정해 주었으니 localhost:30080 으로 접속해서 테스트 해보자

이렇게해서 쿠버네티스 메니페스트 파일을 작성해서 NginX와 Tomcat을 연동해 보았다. 필자도 공부를 하면서 이것저것 복잡한 개념들이 생소한 것들이라 복잡한 기분을 느꼈으나 막상 해보면 이해가 금방 될 수도 있다.
다음에는 WEB과 WAS를 연동한 것에 이어서 DB(MariaDB)를 연동해 보겠다.
Regerence