지돌이의 블로그 입니다!

개요

HA(고 가용성)을 지원하는 Kubernetes Cluster를 구축하고 OpenWRT를 Router로 하여 Calico를 통해 BGP Network를 구축해 Service에 외부IP를 설정 가능하게 한다.

다중 Control Plane Node를 구축하고 Keepalived를 통해 가상IP를 부여하여 고가용성의 K8s api service로써 동작시킨다.

K8s 설치

Step 1. K8s Control Plane Node 및 기초 설치

Reference : https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

설치 준비 및 패키지 설치

$ sudo apt-get install -y apt-transport-https curl containerd keepalived

$ cat <<EOF | sudo tee /etc/keepalived/keepalived.conf
global_defs {
    script_user keepalived_script
    enable_script_security on
}

# 6443 포트에 대한 Health Check
vrrp_script chk_6443 {
    script "/usr/bin/nc -z 127.0.0.1 6443"
    interval 3
}

vrrp_instance VI_K8S_API_SERVER {
    state MASTER # 두번째 노드부터는 BACKUP
    interface ens160 # 인터페이스
    virtual_router_id 10 # 동일하게 설정
    priority 100 # 두번째 노드부터는 이보다 작은 값 (서로 다르게할것)
    advert_int 1
    authentication {
        auth_type PASS     # 동일하게 설정
        auth_pass 12345678 # 동일하게 설정
    }
    virtual_ipaddress {
        10.0.0.10 # 동일하게 설정: 이것이 VIP
    }
    track_script {
        chk_6443
    }
}
EOF

$ sudo vim /etc/modules
# 마지막 줄 다음에 추가한 뒤 저장한다
br_netfilter

$ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

# 적용
$ sudo sysctl -p /etc/sysctl.d/k8s.conf

$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF

$ sudo apt-get update
$ sudo apt-get install -y kubelet kubeadm kubectl
$ sudo apt-mark hold kubelet kubeadm kubectl

$ sudo vim /etc/fstab
# /swap.img ... 라인 제거

$ sudo swapoff -a && rm -rf /swap.img

이후 kubeadm init 하기

아래 k8s-apiserver.your-name-test-server.local.io 는 바꿀 것.

단일 노드에서는 불필요하긴 하지만 multi master 구성할 때 필요함

openwrt에서 k8s-apiserver.your-name-test-server.local.io 에 대해 10.0.0.10 (kube apiserver VIP) 설정할 것.

$ sudo vim /etc/hosts
# 아래 줄 추가
127.0.0.1(서버IP) k8s-apiserver.your-name-test-server.local.io

$ cat >~/kubeadm-config.yaml <<EOF
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta2
clusterName: "kubernetes"
apiServer:
  certSANs:
    - "k8s-apiserver.your-name-test-server.local.io"
  extraArgs:
    authorization-mode: Node,RBAC
controlPlaneEndpoint: "k8s-apiserver.your-name-test-server.local.io"
controllerManager: {}
dns:
  type: CoreDNS
networking:
  dnsDomain: cluster.local
  podSubnet: "172.16.0.0/12" # K8s 클러스터 내에서 파드가 사용하는 Subnet, 이미 사용중인 상위 네트워크 대역과 중복되면 안됌!
  serviceSubnet: "10.96.0.0/12" # K8s 클러스터 내에서 서비스가 사용하는 Subnet, 이미 사용중인 상위 네트워크 대역과 중복되면 안됌!
scheduler: {}
EOF

$ sudo kubeadm init --config ~/kubeadm-config.yaml

# 설치 후
# ===== 관리용 계정에서 kubectl 사용 가능하게 하기 =====
# kubeadm init 이후 아래 명령이 출력된다. 그대로 따라하면 해당 계정에
# cluster-admin role을 가진 관리자 계정의 credential 이 등록된다.
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
# =================================================

$ kubectl taint nodes --all node-role.kubernetes.io/master-
# 원래 master노드에서는 pod를 실행하지 않음, 하지만 단일 노드로써 사용하기 위해 위 명령으로 master node에 대한 taint제거 

# ===== CALICO 설치 =====
$ kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
$ kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml

$ cat <<EOF > ~/tigera-install.yaml
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  # Configures Calico networking.
  calicoNetwork:
    # Note: The ipPools section cannot be modified post-install.
    ipPools:
    - blockSize: 26
      cidr: 172.16.0.0/12 # 파드가 사용하는 Subnet (kubeadm init 할때와 동일해야 함!)
      encapsulation: VXLANCrossSubnet
      natOutgoing: Enabled
      nodeSelector: all()
EOF

$ kubectl apply -f ~/tigera-install.yaml

Step 2. K8s Worker node 설치

먼저 control plane 노드에서 join token을 생성한다.

# Control Plane Node에서
$ kubeadm token create --print-join-command

# 아래와 같은 출력 발생됌. 복사할 것
kubeadm join k8s-apiserver.your-name-test-server.local.io --token abcd.efghijk --discovery-token-ca-cert-hash sha256:12345678...

이후 해당 worker node에서 위에서 생성된 명령어를 실행한다.

$ sudo kubeadm join k8s-apiserver.~~~~.local.io --token abcd.efghijk --discovery-token-ca-cert-hash sha256:12345678..

BGP Network 구성

Step 1. Router 구성

우선 Router에서 BGP Network 를 설정한다.

이번 예제에서는 router의 AS는 65000 을 사용하고, K8s calico node BGP AS는 65001을 사용하도록 한다.

OpenWRT Router에서

# opkg install quagga quagga-zebra quagga-bgpd quagga-watchquagga quagga-vtysh
# vtysh
OpenWrt# configure terminal
OpenWrt(config)# router bgp 65000
OpenWrt(config-router)# neighbor 10.0.0.101 remote-as 65001
OpenWrt(config-router)# neighbor 10.0.0.102 remote-as 65001
OpenWrt(config-router)# neighbor 10.0.0.103 remote-as 65001
...
(모든 노드의 IP들을 등록한다)

k8s control plane 에서

# BGP Configuration
$ cat <<EOF > ~/calico-bgpconf.yaml
apiVersion: crd.projectcalico.org/v1
kind: BGPConfiguration
metadata:
  name: default
spec:
  asNumber: 65001 # 해당 K8s Cluster가 사용할 AS Number
  nodeToNodeMeshEnabled: false
  serviceExternalIPs:
  - cidr: 10.0.1.0/24 # 외부에 노출할 IP 대역 (LoadBalancer로 설정한 서비스가 해당 IP대역 내에 할당된다)
EOF

$ cat <<EOF > ~/calico-bgppeer-mainrouter.yaml
apiVersion: crd.projectcalico.org/v1
kind: BGPPeer
metadata:
  name: mainrouter
spec:
  asNumber: 65000 # Router의 AS 번호
  peerIP: 10.0.0.1 # Router의 IP
EOF

$ kubectl apply -f ~/default-ipv4-ippool.yaml
$ kubectl apply -f ~/calico-bgppeer-mainrouter.yaml
$ kubectl apply -f ~/calico-bgppeer-mainrouter.yaml

참고로... nodeToNodeMeshEnabled: true 으로 하면 externalIP가 제대로 먹히지 않는다. (eBGP에서 external ip에 대해 모든 node가 next hop으로 지정된다. github.com/projectcalico/libcalico-go/pull/1266)

 

참고로... 개발 PC에서 serviceExternalIP로 접근하려면 이전 설정에서 했던 것처럼 static route설정이 필요하다.

route -P add 10.0.1.0 MASK 255.255.255.0(서브넷마스크) 192.168.1.2(게이트웨이 IP)

BGP Peering이 잘 되었는지 확인해 본다

(MainRouter에서)

root@OpenWrt:~# vtysh

Hello, this is Quagga (version 1.1.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

OpenWrt# show bgp summary

IPv4 Unicast Summary:
---------------------
BGP router identifier 192.168.1.2, local AS number 65000
RIB entries 24, using 2688 bytes of memory
Peers 3, using 44 KiB of memory

Neighbor        V         AS       MsgRcvd MsgSent   TblVer InQ  OutQ Up/Down  State/PfxRcd
10.0.0.101     4         65001    6904    6277        0    0    0    14:24:06       11
10.0.0.102     4         65001    2877    2590        0    0    0    14:37:34        7
10.0.0.103     4         65001    7149    6610        0    0    0    14:23:27        6

Total number of neighbors 3

이처럼 Neighbor들이 Up된 시간이 나오고 State가 숫자로 나오면... 정상이다!

이제 LoadBalancer 에서 ExternalIP설정이 가능하다.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer
  externalTrafficPolicy: Local # 필수적으로 Local으로 설정한다! 그렇지 않으면 서비스 파드가 실행중이지 않은 Node까지 자기 IP를 Report함
  externalIPs:
  - 10.0.1.100 # serviceExternalIPs 대역 내의 IP로 설정한다.

위처럼 작성한 경우 외부에서 MainRouter를 통해 라우팅되어서 10.0.1.100 IP로 직접 접근이 가능하다.

Comment +0