73일차) 2025-04-14(쿠버네티스 리소스 - service/metallb, ingress)
5. Service
작은 로드밸런서, 주로 pod 같은 리소스들을 대표 (부하분산 + 접속지점)
= pod들을 주로 외부에 '서비스'하는 리소스
5-1. ClusterIP
5-2. NodePort
5-3. LoadBalancer
Service 리소스의 한 종류(=type), 각 클러스터 관리자의 도움이 필요함
EKS 같은 클라우드 서비스 제공자의 경우, Service 타입을 LoadBalancer로 명시하기만 해도 이미 기능이 구현되어 있기에 자동으로 LB가 생성(AWS EKS에서 Service 타입을 LoadBalancer로 생성하면 실제 NLB나 CLB(기본값)가 생성)

근데 우리는 온프레미스에 클러스터를 구축했기때문에 그 기능이 존재하지 않음. Bare-metal 환경에 직접 구축을 했기때문에 metalLB라는 애드온을 추가하여 로드밸런싱기능을 구현해야한다.
MetalLB 설치

https://metallb.io/installation/

- wget으로 다운로드 받자
웹에 올려진 매니페스트로 설치해도되지만 나중에 클러스터 재구축하면 또 쓸 수 있기 때문
root@master:~/mani# cd ~
root@master:~# wget
https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
- 설치
root@master:~# kubectl apply -f metallb-native.yaml



speaker :
우리가 서비스 타입을 LoadBalaner로 하여 LB를 생성하면, 211.183.3.0/24대역의 아이피를 갖는 리소스가 하나 생성된다.
하지만 이건 실제 네트워크인 211.183.3.0/24 대역의 입장에서는 가상의 리소스이며, 인지 불가능. 클러스터 = 집. 집안에서 만든 리소스. 집밖의 가상의 주소를 부여받음. (Gratuitous ARP: 같은 네트워크에서 IP주소가 중복되는지 확인하고 다른 장치들이 자신의 IP주소를 갱신하도록 주기적으로 IP주소를 알려주는 역할을 한다.)
따라서 이렇게 부여받은 아이피는 211.183.3.0/24 네트워크 입장에서는 알 수 없다.
→ LB에 부여된 아이피를 집 외부(211.183.3.0/24)대역에 알려줘야 하는데, 이게 speaker의 역할
ex) 내가 로드밸런서 생성해서 211.183.3.200이라는 아이피를 부여할것으로 알고들 있어라,,,


root@master:~# vi config-metal.yml
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool namespace: metallb-system spec: addresses: - 211.183.3.200-211.183.3.240 # 원하는 대역대, 안겹치게 수정 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: example namespace: metallb-system |
# IPAddressPool : LB를 생성했을때 부여받을 아이피 대역대 정의
# L2Advertisement : LB에 특정한 아이피가 부여됐다는 사실을 다른 네트워크 구성원들에게 알려주는 역할(설치시 speaker라는 pod가 정상 동작)
root@master:~# kubectl apply -f config-metal.yml

root@master:~# cd mani/
root@master:~/mani# vi lbtest.yml
apiVersion: v1
kind: Namespace
metadata:
name: ip-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ip-dep
namespace: ip-ns
spec:
replicas: 2
selector:
matchLabels:
app: myipnginx
template:
metadata:
labels:
app: myipnginx
spec:
containers:
- name: ip-con
image: oolralra/ipnginx
---
apiVersion: v1
kind: Service
metadata:
name: svc-ipnginx
namespace: ip-ns
spec:
selector:
app: myipnginx
type: LoadBalancer
ports:
- port: 80
targetPort: 80

root@master:~/mani# kubectl apply -f lbtest.yml
root@master:~/mani# kubectl get svc -n ip-ns


실습)
https://github.com/oolralra/simple_jar
# 이미 빌드가 되어있는 springboot 앱(8085 포트 사용)
ns : spring-ns
svc: svc-spring
pod의 label → app: spring
211.183.3.150~160정도의 IP로 접속했을때 위 앱이 뜨도록 한번 만들어보세요.
컨테이너 레지스트리는 제꺼 써도 괜찮고 도커허브 쓰셔도 됩니다.
- 깃클론
root@master:~/mani# git clone https://github.com/oolralra/simple_jar.git
root@master:~/mani# cd simple_jar
- 도커파일 생성
root@master:~/mani/simple_jar# vi Dockerfile
# Dockerfile
FROM openjdk:8-jdk-alpine
COPY springbootApp.jar app.jar
EXPOSE 8085
ENTRYPOINT ["java", "-jar", "app.jar"]
- 이미지 빌드
root@master:~/mani/simple_jar# docker build -t leeseohoo/simple-jar:latest .

- push할 이미지 pull, docker hub push
root@master:~/mani/simple_jar# docker login
root@master:~/mani/simple_jar# docker pull leeseohoo/simple-jar:latest
root@master:~/mani/simple_jar# docker push leeseohoo/simple-jar:latest

- config 대역 수정
vi ~/config-metal.yml
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool namespace: metallb-system spec: addresses: - 211.183.3.150-211.183.3.160 # 원하는 대역대, 안겹치게 수정 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: example namespace: metallb-system |
kubectl apply -f config-metal.yml
- 매니스트 생성
root@master:~/mani# vi lbtest.yml
apiVersion: v1
kind: Namespace
metadata:
name: spring-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-deployment
namespace: spring-ns
spec:
replicas: 2
selector:
matchLabels:
app: spring
template:
metadata:
labels:
app: spring
spec:
containers:
- name: spring-container
image: leeseohoo/simple-jar:latest
ports:
- containerPort: 8085
---
apiVersion: v1
kind: Service
metadata:
name: svc-spring
namespace: spring-ns
spec:
selector:
app: spring
type: LoadBalancer
ports:
- port: 80
targetPort: 8085
- 기존 상위 리소스까지 전부 삭제 (kubectl delete pod,rs,depoly --all)
kubectl delete deploy --all
kubectl delete rs --all
kubectl delete pod --all
root@master:~/mani# kubectl apply -f lbtest.yml
root@master:~/mani# kubectl get svc -n spring-ns

root@master:~/mani# kubectl get pod -n spring-ns
root@master:~/mani# kubectl get pod -n spring-ns -o wide

- 211.183.3.150으로 잘 할당받음


풀이)
git clone https://github.com/oolralra/simple_jar
cd simple_jar
- 61.254.18.30:5000/openjdk:8-jre-alpine
vi Dockerfile
FROM 61.254.18.30:5000/openjdk:8-jre-alpine
WORKDIR /app
COPY springbootApp.jar app.jar
CMD ["java","-jar","app.jar"]
docker build -t 61.254.18.30:5000/pcm/spring:1 .
docker push 61.254.18.30:5000/pcm/spring:1
docker rmi -f 61.254.18.30:5000/pcm/spring:1
docker run -dp 80:8085 --name test 61.254.18.30:5000/pcm/spring:1

- 아까 썼었던 매니페스트를 복제
docker rm -f test
cp ../lbtest.yml ./spring.yml
apiVersion: v1 kind: Namespace metadata: name: spring-ns --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-dep namespace: spring-ns spec: replicas: 2 selector: matchLabels: app: spring template: metadata: labels: app: spring spec: containers: - name: spring-con image: 61.254.18.30:5000/pcm/spring:1 --- apiVersion: v1 kind: Service metadata: name: svc-spring namespace: spring-ns spec: selector: app: spring type: LoadBalancer ports: - port: 80 targetPort: 8085 |
# targetPort를 실제 앱이 동작하는 포트인 8085로 해주는게 제일 중요
# LB를 생성하면 service의 포트(port)인 80을 따라감

kubectl apply -f spring.yml
kubectl apply -f ../../config-metal.yml



# 쿠버네티스 클러스터에서 Advertisement를 통해 적극적으로 metalLB가 쓰는 아이피를 알려줬기때문에 다른 노트북이나 네트워크 구성원들이 이 아이피를 인지할 수 있다.

5-4. ExternalName
Service 리소스의 한 종류(=type), 호스트네임을 다루며 일반적인 셀렉터에 대한 서비스가 아니라 DNS 이름에 대한 서비스에 매핑

# 여러개의 컨테이너에 각 기능들을 구현하여 path로 라우팅이 가능해야 함
# 일반적인 svc로는 path기반 라우팅이 불가능함
6. Ingress
path 기반 라우팅이 가능한 리소스
만약 위 마이크로 서비스를 일반적인 LoadBalancer로 구현한다고 하면,

서비스는 한 종류의 label만 품을 수 있기 때문에 이런 한계가 발생한다. 물론 ClusterIP 타입으로도 MSA구조를 가져갈 수 있으나 Ingress는 여러 마이크로서비스들을 하나의 주소(도메인)으로 묶기 용이하다.
→ 여러 개의 서비스를 하나로 품을 수 있는 무언가를 앞에 붙여주자
→ Ingress
L7 기반 라우팅

→ Ingress controller가 필요, Ingress controller를 설치할 때, metalLB를 먼저 구성해주는 것이 좋음
https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal-clusters
# 버전이 계속 바뀌는 ingress-controller 설치 페이지
root@master:~/mani# mkdir ingress
root@master:~/mani# cd ingress/
- 인그리스 컨트롤러 설치 매니페스트. ingress-nginx라는 네임스페이스에 기능 설치
root@master:~/mani/ingress# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.1/deploy/static/provider/baremetal/deploy.yaml
root@master:~/mani/ingress# vi deploy.yaml
♨ NodePort에서 LoadBalancer로 수정

root@master:~/mani/ingress# kubectl apply -f deploy.yaml
kubectl get pod,svc -n ingress-nginx
- 인그리스 컨트롤러의 Service 타입을 LoadBalancer로 했기때문에 아이피를 하나 잘 부여받아야함


root@master:~/mani/ingress# vi ip.yml
apiVersion: v1 kind: Service metadata: name: svc-ip spec: selector: app: ipnginx ports: - port: 80 targetPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: ip-dep spec: replicas: 3 selector: matchLabels: app: ipnginx template: metadata: name: ip-pod labels: app: ipnginx spec: containers: - name: ip-con image: 61.254.18.30:5000/ipnginx |
# 나중에 ingress를 구성할때 서비스의 이름으로 찾아갈 것이기때문에, svc의 이름(svc-ip)을 기억해둬야 한다.
root@master:~/mani/ingress# kubectl apply -f ip.yml
root@master:~/mani/ingress# kubectl describe svc svc-ip

root@master:~/mani/ingress# kubectl get svc

root@master:~/mani/ingress# curl 10.108.191.148

# svc → pod로 트래픽이 잘 인가되고, pod도 정상인기 확인하기 위함
# ingress에 문제가 있을때 트러블슈팅하기 용이
root@master:~/mani/ingress# vi ingress.yml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: rapa.com http: paths: - path: /httpd pathType: Prefix backend: service: name: svc-ip #서비스의 이름 port: number: 80 #서비스의 포트 |
# annotations : 추가적인 정보, labels와 비슷하지만 주로 부가적인 기능 명시
# '인그리스 컨트롤러는 nginx' 이며,
# rewrite-target: /
# path를 /httpd를 통해서 해당 앱으로 가게 되면 경로를 /httpd가 아닌 '/'로 하겠다.
# host: 영문주소, DNS기능이 필요함.
# path: /httpd
# pathType: Prefix = /httpd 여기까지만 고정되면 그 하위 디렉토리는 신경안쓰고 다 허용 하겠다.
# ex) /httpd/app1 = 가능, /httpd/app2 = 가능. /http = 불가능.
# 위의 호스트와 조합하여 'rapa.com/httpd 로 들어왔을때'를 의미
# backend: 서비스를 뜻함
# service.name: svc-ip
# /httpd 경로로 들어왔을때 연결시켜줄 svc를 명시하여 연결시켜준다.
root@master:~/mani/ingress# kubectl apply -f ingress.yml

- DNS-server를 구성하긴 그러니까, rapa.com을 안내해주기 위해 /etc/hosts에 적어준다
root@master:~/mani/ingress# kubectl get svc -n ingress-nginx

root@master:~/mani/ingress# vi /etc/hosts

# ingress는 무조건 호스트네임(영문주소)으로 구성. IP로는 불가능
# DNS-server를 구성하긴 그러니까, rapa.com을 안내해주기 위해 /etc/hosts에 적어준다
root@master:~/mani/ingress# curl rapa.com/httpd

# rapa.com 주소가 211.183.3.152로 매칭이 되어있으므로

# rapa.com → ingress-controller의 IP → ingress (path기반 라우팅) → svc
실습)
rapa.com/httpd 으로 접속했을때
61.254.18.30:5000/ipnginx 이 보이도록 이미 구성되어 있음.
rapa.com/ 으로 접속했을때
61.254.18.30:5000/hnginx 이 보이도록 해보세요.
" 하나의 ingress가 여러개의 svc를 포함한다. "

1. svc와 deployment 생성
root@master:~/mani/ingress# vi ip2.yml
apiVersion: v1 kind: Service metadata: name: svc-h spec: selector: app: hnginx ports: - port: 80 targetPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: h-dep spec: replicas: 3 selector: matchLabels: app: hnginx template: metadata: name: h-pod labels: app: hnginx spec: containers: - name: h-con image: 61.254.18.30:5000/hnginx |
root@master:~/mani/ingress# kubectl apply -f ip2.yml
2. ingress 수정


root@master:~/mani/ingress# vi ingress.yml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: rapa.com http: paths: - path: /httpd pathType: Prefix backend: service: name: svc-ip #서비스의 이름 port: number: 80 #서비스의 포트 - path: / pathType: Prefix backend: service: name: svc-h #서비스의 이름 port: number: 80 #서비스의 포트 |
root@master:~/mani/ingress# kubectl apply -f ingress.yml
- ingress - svc - pod 로 이어지는 흐름이 잘 구성된걸 확인 가능
root@master:~/mani/ingress# kubectl describe ingress ingress

- hnginx로 호스트네임


질문1) 노트북에서는 rapa.com이 접속이 안되냐? => 안됩니다. 노트북은 rapa.com을 모르기때문. rapa.com의 IP를 아는 호스트는 현재 /etc/hosts에 등록해준 master노드뿐입니다.
질문2) ingress-controller의 svc는 ingress-nginx라는 네임스페이스에 존재하는데 다른 리소스들은 default 네임스페이스에 존재한다. 어떻게 트래픽이 인가되냐? => ingress-controller의 IP는 클러스터 외부에 존재하는 IP이기때문에 ingress와 svc와 pod들만 같은 네임스페이스에 있으면 된다.
실습2)
aws8.com/ 로 접속시 8085포트로 동작하는 61.254.18.30:5000/pcm/spring:1 , 서비스포트는 8085
aws8.com/h 로 접속시 80포트로 동작하는 61.254.18.30:5000/hnginx , 서비스포트는 81
aws8.com/ip 로 접속시 80포트로 동작하는 61.254.18.30:5000/ipnginx , 서비스포트는 81
mario라는 이름의 ingress를 생성하세요.

- 매니페스트 생성 - 61.254.18.30:5000/pcm/spring:1
root@master:~/mani/ingress# vi spring.yml
apiVersion: v1
kind: Service
metadata:
name: svc-spring
spec:
selector:
app: springnginx
ports:
- port: 8085
targetPort: 8085
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-dep
spec:
replicas: 3
selector:
matchLabels:
app: springnginx
template:
metadata:
name: spring-pod
labels:
app: springnginx
spec:
containers:
- name: spring-con
image: 61.254.18.30:5000/pcm/spring:1
root@master:~/mani/ingress# kubectl apply -f spring.yml
- 매니페스트 생성 - 61.254.18.30:5000/hnginx
root@master:~/mani/ingress# vi h.yml
apiVersion: v1
kind: Service
metadata:
name: svc-h
spec:
selector:
app: hnginx
ports:
- port: 81
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: h-dep
spec:
replicas: 3
selector:
matchLabels:
app: hnginx
template:
metadata:
name: h-pod
labels:
app: hnginx
spec:
containers:
- name: h-con
image: 61.254.18.30:5000/hnginx
root@master:~/mani/ingress# kubectl apply -f h.yml
- 매니페스트 생성 - 61.254.18.30:5000/ipnginx
root@master:~/mani/ingress# vi ip.yml
apiVersion: v1
kind: Service
metadata:
name: svc-ip
spec:
selector:
app: ipnginx
ports:
- port: 81
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ip-dep
spec:
replicas: 3
selector:
matchLabels:
app: ipnginx
template:
metadata:
name: ip-pod
labels:
app: ipnginx
spec:
containers:
- name: ip-con
image: 61.254.18.30:5000/ipnginx
root@master:~/mani/ingress# kubectl apply -f ip.yml
- ingress-nginx 주소 확인
root@master:~/mani/ingress# kubectl get svc -n ingress-nginx

- ingress-nginx 주소 확인
root@master:~/mani/ingress# vi /etc/hosts
211.183.3.152 aws8.com
# kubectl get svc -n ingress-nginx에서 controller의 주소
- ingress.yml 생성
root@master:~/mani/ingress# vi ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: aws8.com
http:
paths:
- path: /ip
pathType: Prefix
backend:
service:
name: svc-ip #서비스의 이름
port:
number: 81 #서비스의 포트
- path: /h
pathType: Prefix
backend:
service:
name: svc-h #서비스의 이름
port:
number: 81 #서비스의 포트
- path: /
pathType: Prefix
backend:
service:
name: svc-spring #서비스의 이름
port:
number: 8085 #서비스의 포트
root@master:~/mani/ingress# kubectl apply -f ingress.yml
- 결과 확인
root@master:~/mani/ingress# curl aws8.com/

root@master:~/mani/ingress# curl aws8.com/h

root@master:~/mani/ingress# curl aws8.com/ip

풀이)
로드밸런서 타입의 경우엔, svc의 포트를 따라가지만, ingress 경우엔 그렇지 않다.
aws8.com/h 로 접속하면 특정 서비스의 포트까지 명시하므로, 따로 포트를 명시할 필요가 없다.
ex) aws8.com:81/h 이런식으로 할 필요가 없다는 뜻


vi ingress.yml
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: aws8-ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: aws8.com http: paths: - path: / pathType: Prefix backend: service: name: svc-spring #서비스의 이름 port: number: 8085 #서비스의 포트 - path: /h pathType: Prefix backend: service: name: svc-h #서비스의 이름 port: number: 81 #서비스의 포트 - path: /ip pathType: Prefix backend: service: name: svc-ip #서비스의 이름 port: number: 81 #서비스의 포트 |
# 인그리스 이름 : aws8-ingress
vi spring.yml
apiVersion: apps/v1 kind: Deployment metadata: name: spring-dep spec: replicas: 2 selector: matchLabels: app: spring template: metadata: labels: app: spring spec: containers: - name: spring-con image: 61.254.18.30:5000/pcm/spring:1 --- apiVersion: v1 kind: Service metadata: name: svc-spring spec: selector: app: spring type: LoadBalancer ports: - port: 8085 targetPort: 8085 |
vi h.yml
apiVersion: v1 kind: Service metadata: name: svc-h spec: selector: app: hnginx ports: - port: 81 targetPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: h-dep spec: replicas: 3 selector: matchLabels: app: hnginx template: metadata: name: h-pod labels: app: hnginx spec: containers: - name: h-con image: 61.254.18.30:5000/hnginx |
vi ip.yml
apiVersion: v1 kind: Service metadata: name: svc-ip spec: selector: app: ipnginx ports: - port: 81 targetPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: ip-dep spec: replicas: 3 selector: matchLabels: app: ipnginx template: metadata: name: ip-pod labels: app: ipnginx spec: containers: - name: ip-con image: 61.254.18.30:5000/ipnginx |
kubectl apply -f spring.yml
kubectl apply -f h.yml
kubectl apply -f ip.yml
kubectl apply -f ingress.yml

# spec.ingressClassName 이부분을 따로 풀어서 쓰면,
spec:
ingressClassName



- vi /etc/hosts 수정

- 결과
