Amazon EKS, Azure AKS, Google GKE 등과 비슷하게 Oracle OKE에서도 Network LB가 제공된다.
Managed Kubernetes를 사용할 때 항상 등장하는 Load Balancer와 Network Load Balancer가 비슷하면서 약간 다른데,
Oracle Cloud Infrastructure의 User Manual에는 아래와 같이 설정하고 있다.
Load Balancer
A Load Balancer improves resource utilization, facilitates scaling, and helps ensure high availability. You can configure multiple load balancing policies and application-specific health checks to ensure that the load balancer directs traffic only to healthy instances. Includes: advanced proxy features such as layer-7 routing and SSL termination.
그림에서 묘사한 것처럼
HTTP
HTTPS
TCP
프로토콜을 사용하는 Backend 서비스만 지원한다.
따라서 UDP나 Dedicated Protocol을 사용하는 Backend 서비스가 있는 경우에 이 LB를 사용할 수 없다. ㅠㅠ
Network Load Balancer (일명, NLB)
A Network Load Balancer is a non-proxy layer-4 load balancing solution. It offers a scalable VIP to the customer and additionally provides the benefits of flow high availability, low latency, and source IP and port preservation. Includes:layer-4 pass-through load balancing and client header preservation.
그림에서 묘사한 것처럼
TCP
UDP # <-- 이것이 LB와 NLB가 다른 부분이다.
기타... 아무거나 다 (Any ~~~) # <-- 이것이 LB와 NLB가 다른 부분이다.
프로토콜을 사용하는 Backend 서비스를 지원한다.
그런데 Oracle Cloud Infra에서는 이 NLB(Network LB)는 3개까지만 사용하도록 제한하고 있다.
OCI Web Console에서는 이 NLB Max Limit을 설정하는 메뉴가 안 보이는데, 아마 Oracle에 직접 전화해서 NLB 개수를 Upgrade해야 하는 것 같다. (아직, 확실하진 않고... 내일 Oracle에 한번 전화해서 물어봐야겠다..)
MetalLB의 버전에 따라 설치 결과가 조금씩 달라서 MetalLB를 설치한 날짜를 밝힌다.
아래 절차를 따라하면 MetalLB 설치, 그리고 예제 앱 테스트 모두 잘 동작한다.
MetalLB 설치에 관한 공식 문서는 아래 Web Docs를 참고 https://metallb.universe.tf/installation/
위 Web Docs는 여러 도구를 이용한 설치 방법을 설명하지만, 나는Manifest 방식을 이용하여 MetalLB를 설치했다.
위 Web docs를 보면서 따라해보면, 막힘없이 쭉쭉~ 잘 설치되었다.
설치 완료한 이후에 LB 테스트를 해봤는데 잘 동작했다.
내가 설치한 환경은 이렇다.
Kubernetes: v1.25.3 CNI: Calico v3.24.5
내가 Web docs를 보면서 수행했던 명령 및 설정 파일 편집한 내역만 추려보면 이렇다.
strict ARP mode 활성화하기
아래 예제처럼 kube-proxy configmap을 수정한다.
(strictARP항목을false에서true로 변경)
## 아래 명령을 수행
$ kubectl edit configmap -n kube-system kube-proxy
... 중간 생략 ...
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
strictARP: true ## 이 부분을 수정한다. false -> true
... 중간 생략 ...
Manifest 이용하여 Metal LB 설치하기
아래 명령을 따라서 수행한다.
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
##
## MetalLB가 잘 설치되었다면,
## 아래와 같이 metallb-controller와 metallb-speaker pod가 Running 상태로 보일 것이다.
##
$ kubectl get -n metallb-system pod
NAME READY STATUS RESTARTS AGE
controller-84d6d4db45-g94mh 1/1 Running 0 31s
speaker-6jqzw 1/1 Running 0 31s
speaker-hlrg7 1/1 Running 0 31s
speaker-sq94q 1/1 Running 0 31s
## 참고: speaker가 running 상태가 되려면 대략 25~30초 정도 걸린다.
## Pod의 상태가 ConfigErr로 보인다고 당황해하지 않아도 된다.
## 30초 후에 Running 상태로 변경될거다.
주의할 점: 위와 같이 L2Advertisement, IPAddressPool을 변경하고 적용해도 실제로 Service resource가 생성될 때, 과거의 IPAddressPool을 이용한다. 아마 MetalLB 버그 같은데, 과감하게 metallb-system 네임스페이스의 controller pod를 종료시키고 재기동하면 새로운 IPAddressPool이 적용된다. (아래 명령을 참고)
$ kubectl get -n metallb-system pod -l component=controller
NAME READY STATUS RESTARTS AGE
controller-84d6d4db45-vgt9r 1/1 Running 0 6m12s
$ kubectl delete -n metallb-system pod controller-84d6d4db45-vgt9r
## 20초 정도 후에 Pod가 재기동되고, 그 뒤로 새로운 IPAddressPool이 정상적으로 반영된다.
Example App을 이용하여 MetalLB 동작 확인
$ cat >> my-example.yaml <<-EOF
apiVersion: v1
kind: Pod
metadata:
name: almighty
labels:
app: almighty
spec:
terminationGracePeriodSeconds: 3
containers:
- name: almighty
image: docker.io/andrewloyolajeong/almighty:0.2.4
---
apiVersion: v1
kind: Service
metadata:
name: almighty
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: almighty
ports:
- name: myweb
protocol: TCP
port: 8080
targetPort: 8080
- name: yourweb
protocol: TCP
port: 80
targetPort: 80
EOF
$
$ kubectl create ns ns1
$ kubectl apply -n ns1 -f my-example.yaml
$ kubectl get -n ns1 svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
almighty LoadBalancer 10.104.156.217 10.1.4.150 8080:30859/TCP,80:31917/TCP 116s
$
##
## 위 결과에서 EXTERNAL-IP에 적절한 IP Address가 출력되면, 잘 되는 것이다.
## 그리고 아래와 같이 실제로 MetalLB가 할당해준 10.1.4.150 주소를 통해 Networking이 되는지 확인한다.
##
$ curl 10.1.4.150:8080
Hello from example application. (written by Andrew)
$ curl 10.1.4.150:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
$
올레~~~ 잘 동작한다 ^^
참고: ping 명령으로 EXTERNAL-IP 통신 여부를 확인하지 말 것 !!!
MetalLB 설치하고, 많은 사용자들이 실수하는 것이 아래처럼 ping 명령으로 External-IP로 ICMP 패킷이 전달되는지 체크한다.
$ ping 10.10.12.43
MetalLB가 만든 External-IP 주소는 K8S Cluster의 특정 Worker Node의 Netfilter에만 존재하는 Chain Rule일 뿐이다.
즉, OS Kernel이 물린 Network Port의 MAC Address에 Binding한 IP Address가 아니다보니, OS Kernel이 ICMP Echo Request에 대해 반응할리 없다. 단, K8s LoadBalancer 구현체(즉, MetalLB)의 구현에 따라서 MetalLB가 직접 ICMP Echo Response를 만들 수는 있지만, 이것은 구현체의 버전에 따라 다를 수 있으니까 ICMP Echo(즉, Ping)에 대한 테스트를 시도하지 않는 것이 좋다.
아래와 같이 CURL을 이용하여 테스트하는 것이 정확한 테스트 방법이다.
$ curl https://10.10.12.43:8443
다시 한번 강조한다. 절대 ping 명령으로 MetalLB의 External-IP 동작 유무를 체크하지 말자 !!!
참고: Address 설정은 모든 대역의 주소가 다 가능한다.
아래 설정 예제처럼 특정 addresses 대역을 사용하겠다고 지정하면, MetalLB Speaker가 알아서 동일한 Broadcast Domain에 해당하는 Network Port로 GARP(Gratuitous ARP)를 Broadcasting한다.
$ vi metallb/values.yaml
... 중간 생략 ...
configInline:
address-pools:
- name: default
protocol: layer2
addresses:
- 10.10.12.40/29
... 중간 생략 ...
따라서 Worker Node에 Network Port가 여러 개 있다면, 위 address-pools에 설정된 ip address 대역 중에서 broadcast domain이 일치하는 Network Port로 GARP 패킷이 흘러나간다.
참고: 특정 Service에 특정 External-IP(Public IP)를 고정해서 사용하기
External-IP는 Kubernetes Cluster 밖에 있는 Client App이 접근하기 위한 주소이기 때문에 고정하는게 운영상 편하다.
그런데 MetalLB는 IP Address Range를 설정하도록 되어 있기 때문에 K8s Service 생성시 어떤 External-IP가 K8s Service에 할당될지 알 수 없다.
이럴 때 아래와 같이 K8s Service에 설정을 추가하면 특정 External-IP(즉, Public IP Address)를 고정해서 사용할 수 있다.
$ cat my-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysvc
(... 중간 생략 ...)
spec:
type: LoadBalancer
## MetalLB의 IP Address Range가 211.10.3.160 ~ 190 이라고 가정하고
## 이 mysvc에 211.10.3.165 주소를 고정해서 사용하고 싶아면 아래와 같이 사용한다.
loadBalancerIP: 211.10.3.165
(... 중간 생략 ...)