Kubespray 사용 시 Metallb 설치

Bumgu·2024년 9월 22일
0
post-thumbnail

지난번 kubesprayansible을 활용해서 쿠버네티스 클러스터링을 할 때, 마지막 metallb 설치에 실패했는데, 오늘 설치해보겠습니다.

1. MetalLB 설치

공식문서를 보고 설치했습니다.

bash kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml

으로 설치합니다.

1-1. kube-proxy configMap 수정

MetalLB는 IPVS 모드에서 ARP 응답을 엄격하게 제어하여 로드밸런싱을 수행합니다. 이 모드에서 strictARP를 활성화하면, kube-proxy는 노드가 할당된 IP에 대한 ARP 응답을 명확하게 지정된 노드에서만 처리하도록 합니다.

kubectl edit cm -n kube-system kube-proxy로 편집을 열고
strictARP: false 부분을 strictARP: true로 수정해줍니다.

저장하고 빠져나온뒤엔
kubectl rollout restart daemonset -n kube-system kube-proxykube-proxy를 재시작 해줍니다.

1-2. IpAddressPool 설정

지난번 이 IpAddressPool리소스를 생성하면 kubectl이 고장났는데,
그 이유는

controlplane : 192.168.64.176
node1 : 192.168.64.177
node2 : 192.168.64.178
이었는데

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: cluster-ip-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.64.176-192.168.64.178

이렇게 작성한후 apply를 했습니다.
바보같이 노드들의 아이피와 같은 아이피 대역을 설정해서 ip충돌이 일어나서 해당 ip로 노드에 접근하려는 시도가 서비스 ip로 처리되어 kubectl을 통한 APIserver 접근이 차단되어 kubectl명령어가 고장난것 이었습니다.

그렇기에, 충돌이 되지않는 ip로설정합니다.

  • ipAddressPool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: cluster-ip-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.64.180-192.168.64.189

이제 생성하면 정상적으로 생성이 되고, kubectl명령어도 여전히 작동합니다.

1-3. L2Advertisement 설정

IpAddressPool리소스를 생성하였으니, 그 Pool을 사용합니다.

  • l2Advertisement.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: cluster-l2-advertisement
  namespace: metallb-system
spec:
  ipAddressPools:
  - cluster-ip-pool

spec.ipAddressPools[0]IPAddressPool manifest의 metadata.name으로 작성합니다.

1-1~1-3 을 스크립트로 한번에

기왕 자동화를 하다보니 더 편하게 더 한번에 하고싶은 마음이 생겨 스크립트를 작성했습니다.

  • install-metllb.sh
#!/bin/bash
# strictARP: true 로 수정
kubeproxytmp=$(mktemp)
kubectl get cm -n kube-system kube-proxy -o yaml > $kubeproxytmp
sed -i '' 's/strictARP: false/strictARP: true/' $kubeproxytmp
kubectl replace -f $kubeproxytmp -n kube-system
rm $kubeproxytmp


# Metallb 설치
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
echo -e "\n MetalLB Successfully Installed.\n"

echo "\nWating to ensure CRDs are installed... \n"

sleep 5
# Metallb 설치 되었는지 확인
kubectl get all -n metallb-system

# IpAddressPool 생성
pooltmp=$(mktemp)

cat<<EOF > $pooltmp
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: cluster-ip-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.64.180-192.168.64.189
EOF

kubectl apply -f $pooltmp
echo -e "\nSuccessfully created IPAddressPool \n"
rm $pooltmp
# L2Advertisement 생성
l2advertisementtmp=$(mktemp)

cat<<EOF > $l2advertisementtmp
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: cluster-l2-advertisement
  namespace: metallb-system
spec:
  ipAddressPools:
    - cluster-ip-pool
EOF

kubectl apply -f $l2advertisementtmp
echo -e "\nSuccefully created L2Advertisement"
rm $l2advertisementtmp

1-4. ingress controller external-ip 부여 확인

이제 필요한 리소스를 모두 생성했습니다.

ingress-nginx를 조회해보면

external-ip가 제가 설정한 IpAddressPool에서 부여된것을 확인 할 수 있습니다.

2. 테스트!

모든 설정을 했으니, LoadBalancer타입의 서비스를 생성해서 정상적으로 작동하는지 확인해보겠습니다.
2개의 nginx deployment와 service를 생성하고, configmap에 서로 다른 페이지를 html을 저장해 보여줍니다.
이후 ingress를 생성해 정상작동하는지 확인하는 흐름입니다.
간단히 그림으로 보자면,

라고 보면 되겠습니다.

즉, 요청은 Ingress-nginx로 요청을 보내고 (부여된 External-IP)받은 요청을 Ingress리소스에 보내고, 거기서 정해진대로 Service로 보내집니다.

2-0. Ingress-nginx 의 Selector

테스트하기전에, Ingress-nginx의 endpoints를 조회해보면, none으로 나오는것을 확인 할 수 있습니다.

이유는, Ingress-nginx의 Pod의 label과 Service에서 Selector의 label값이 다르기때문에, 올바른 Pod를 선택하지 못해서 endpoints가 없어서(선택하지 못해서)none이 나오는겁니다.

kubectl edit svc -n ingress-nginx ingress-nginx
로 편집을 열어주고
spec.selector.app.kubernetes.io/port-ofspec.selector.app.kubernetes.io/part-of로 바꿔주고 저장하고 빠져나옵니다.
빠져나온 후 다시 endpoints를 조회해보면 값이 나오는것을 확인할 수 있습니다.

2-1. Deplyment, Service 생성

  • nginx-deployments.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app1
  namespace: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-app1
  template:
    metadata:
      labels:
        app: nginx-app1
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: app1-vol
          mountPath: /usr/share/nginx/html
      volumes:
      - name: app1-vol
        configMap:
          name: app1-html

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc1
  namespace: test
spec:
  selector:
    app: nginx-app1
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app2
  namespace: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-app2
  template:
    metadata:
      labels:
        app: nginx-app2
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: app2-vol
          mountPath: /usr/share/nginx/html
      volumes:
      - name: app2-vol
        configMap:
          name: app2-html

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc2
  namespace: test
spec:
  selector:
    app: nginx-app2
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

2개의 nginx deployment와 service를 생성합니다.

2-2. ConfigMap 생성

  • nginx-configmap.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
     name: app1-html
     namespace: test
    data:
     index.html: |
       <html>
       <head><title>App 1</title></head>
       <body>
       <h1>This is App 1</h1>
       </body>
       </html>
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
     name: app2-html
     namespace: test
    data:
     index.html: |
       <html>
       <head><title>App 2</title></head>
       <body>
       <h1>This is App 2</h1>
       </body>
       </html>

2-3. Ingress 생성

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  namespace: test
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /app1
        pathType: Prefix
        backend:
          service:
            name: nginx-svc1
            port:
              number: 80
      - path: /app2
        pathType: Prefix
        backend:
          service:
            name: nginx-svc2
            port:
              number: 80

현재 domain은 없으니, host는 지정하지않고 생성합니다.

kubectl get ingress로 확인해보면

Address가 워커노드의 IP입니다. 하지만 이 주소가 아닌, Ingress-nginx Service의 부여된 External-IP로 접속하면 됩니다.

http://192.168.64.180/app1으로 접속해봅니다.

이제

http://192.168.64.180/app2으로 접속해봅니다.

정상적으로 ingress를 타고 정해진 서비스로 접속합니다.


마무리

Kubespray를 활용하면서, 환경자동화에 대한 관심이 더 커졌습니다.
ArgoCD를 이용해 GitOps전략과 App of Apps 패턴을 적용한 프로젝트도 소개하겠습니다.

포스팅은 여기서 마치도록 하겠습니다. 감사합니다.

profile
Software VS Me

1개의 댓글

comment-user-thumbnail
2024년 9월 23일

잘보고가요 ㅎㅎ

답글 달기