EKS AWS CNI를 Flannel로 교체
목적
AWS에서 제공되는 kubernetes인 EKS 환경에서
기본으로 제공되는 CNI인 AWS CNI를 flannel로 교체
AWS CNI를 On-Premise 환경에서 사용하는 CNI로 변경하는 이유
AWS CNI의 경우 AWS의 VPC IP주소를 직접 Pod에 할당하는 방식이라
아래와 같은 장/단점이 존재함.
AWS CNI 장점
1 . 오버레이 네트워크 처럼 캡슐화 및 캡슐화 해제의 오버 헤드가 발생하지 않음
2 . VPC Flow Logs를 사용할 수 있음. Pod에서 송수신되는 IP 트래픽에 대한 정보를 캡처할 수 있음.
3 . ENI ( Elastic Network Interface)를 공유하는 Pod가 적기 때문에 네트워크 대역폭에 대한 경합이 줄어듬.
4 . VPC의 트래픽을 Pod로 직접 라우팅 할 수 있음.
AWS CNI 단점
EC2 인스턴스 유형(예 : m5.large)과 사양에 따라
Worker Node에 생성할 수 있는 Pod수가 제한이 됨.
1. VPC CNI 사용자 지정 네트워킹을 통해 Pod에 할당할 수 있는 IP주소가 제한되어
Worker Node에 Pod를 배포할 수 있는 수가 제한이 됨.
2. 만약 WorkerNode 중 특정 Node만 인스턴스 유형과 사양을 높다면 해당 Node에 과부하가 걸릴 수 있음
이러한 이유 때문에 실제 EKS를 사용하는 경우
Worker Node와 Pod 수가 다수가 되는 환경이라면 IP할당 문제 때문에
AWS CNI보다 IP 할당 제약이 없는 On-premise환경에서 사용되는 CNI가 필요할 수도 있음.
또한 Networking 성능 이슈, 방화벽(Network Policy) 성능에 따라 별도의 CNI를 사용해야하는 상황이 있을 수 있음.
그렇기 때문에 EKS도 고정적으로 AWS CNI를 사용하지 않고
On-premise CNI를 사용할 수 있음.
AWS CNI -> Flannel 교체 작업 (정상설치 안됨.)
On-premise 에서 사용하는 Flannel을 배포해봄
[명령어]
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
[결과]
오류 발생 : Error registering network: failed to acquire lease: node pod cidr not assigned
[원인]
kubernetes master node에 pod cidr로 subnet를 지정해줘야하는데
EKS의 경우 kubeadm init 명령을 사용할 수 없어서 해당 이슈를 해결하지 못함.
[해결책]
AWS EKS의 경우 일반적인 Network CNI를 사용하기 위해서는
별도의 구축법을 사용해야함.
대표적인 방법으로 flannel pod가 별도의 인스턴스인 CoreOS의 etcd를 바라보도록 하는 방법이 있음.
EKS에 사용가능한 Flannel 환경 구축
목표
On-premise 환경의 kubernetes 라면
kubeadm init 명령을 통해서 Pod CIDR을 설정할 수 있음.
Pod CIDR을 설정하면 해당 정보는 kubernetes에 etcd 모듈에 저장되어
Pod를 생성할때마다 etcd에서 Pod CIDR 정보를 확인하고
해당 ip를 ip에 부여함
[on-premise 환경인 경우 명령어 예제]
kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.64.150
하지만 AWS EKS와 같이 public cloud를 사용할 경우 Master Node를 AWS에서 관리하기 때문에
kubeadm명령이 불가함으로 kube-apiserver를 통해서 Pod CIDR을 설정할 수 없음.
이 문제를 해결하기 위해 관리자가 별도로
외부 etcd Database를 구축하여 Flannel에 대한 네트워크 구성을 저장함.
외부 etcd 구성을 위해 CoreOS 인스턴스를 생성하고 해당 인스턴스에 etcd를 설정함.
CoreOS 란?
CoreOS는 일반 리눅스와는 다르게 Docker 등 컨테이너 런타임 tool 구동에 특화된
최초의 클라우드 기반 운영 체제.
Docker 기동에 필요한 기능만 탐재되어있는 경량 OS.
CoreOS는 114M의 메모리만을 사용해서 기존 Linux에 비해서 메모리를 40% 정도 적게 사용함.
현재시점의 CoreOS는 2018년 Redhat에 인수되어
Fedora CoreOS, RHEL CoreOS 배포판으로 계속 개발되고 있음.
Config Transpiler(CT)란?
CoreOS의 Networking 설정파일을 생성하는 하는 tool로
사용자가 yaml, xml 형식으로 설정 파일을 생성해놓으면
CT 명령을 통해 ignition configuration 형식으로 변환하는 역할을 수행
Container Linux Config 파일을 생성해서 부팅 시 수동으로 Systemd를 설정할 수 있음.
Fedora CoreOS에는 별도의 설치 디스크가 없음.
대신 모든 인스턴스는 Ignition을 통해 처음 부팅 할 때
사용자 지정 일반 디스크 이미지에서 시작됨.
Systemd란?
systemd PID 1번 프로세스로
프로세스 트리에서 가장 상위의 프로세스이고 모든 프로세스의 직간접 부모인 데몬임.
리눅스는 PID 1번 프로세스가 가장 먼저 실행되어 OS 에 필요한 각종 데몬들을 init함.
CoreOS etcd란?
CoreOS etcd는 분산 key-value store로
GO 언어로 개발되어 있음.
일반적으로. 데이터베이스 연결 정보, 캐시 세팅등의 설정정보들을 저장해두고 사용함.
CoreOS에서 coreos 인스턴스의 클러스터를 관리하기 위해서 사용됨.
구글의 클러스터 컨테이너 관리 소프트웨어인 Kubernetes의 백엔드 시스템으로 사용됨.
즉 kubernetes의 etcd 모듈도 이러한 CoreOS의 etcd 기반으로 동작하는것임.
기본적으로 2379 포트에서 클라이언트 통신을 받고 2380 포트에서 서버간 통신을 진행
AWS CNI 삭제
EKS생성 시 default로 생성되어있는
AWS CNI 역할을 하는 aws-node를 제거해줘야함.
[명령어]
kubectl delete ds aws-node -n kube-system
[결과]
삭제 확인 명령어 : kubectl get all -n kube-system
aws-node daemonset이 제거되었는지 확인
AWS EC2 인스턴스 CoreOS를 시작
CoreOS의 인스턴스를 시작해야하지만
아래와 같이 현재는 AWS 에서 CoreOS를 지원하지 않고있음
CoreOS는 현재 Fedora CoreOS로 지원하고 있음
서울 리전은 ap-northeast-2 임으로
ap-northeast-2에 해당하는 ami 를 받으면 됨.
Fedora CoreOS EC2 생성 명령어
aws ec2 run-instances --image-id <ami_id> --instance-type t3.small --key-name <key_name> --security-group-ids <sg_ids> --subnet-id <subnet_id> --user-data file://ec2metadata
명령을 실행할 때 key_name, sg_ids 및 subnet_id를
AWS 환경 내의 적절한 리소스에 해당하는 값으로 변경해줘야함.
<ami_id>
Fedora CoreOS ami를 넣어줘야함.
예 : ami-09d33644806aa7550
참고 : https://getfedora.org/coreos/download?tab=cloud_launchable&stream=stable
해당 링크의 ami 부분을 참고
<instance-type>
key-store역할을 하지만
test 환경이라 많은 리소스를 사용하지 않을 것임으로
작은 type을 사용함
<key_name>
키페어 값을 넣어줌.
예: iksoon-cloud9
<sg_ids>
etcd 역할을 하는 EC2 인스턴스를 Worker Node 보안 그룹에 추가하면
flannel 데몬이 etcd Database에서 데이터를 읽을 수 있도록
추가 보안 그룹 규칙을 만들지 않아도됨.
nodegroup 관련 보안 그룹을 넣어줌
예 : sg-05d86720a4c79deac
<subnet_id>
EKS 관련 public subnet 중 하나를 넣어줌
예 : subnet-0a0c80071b68f902d
public으로 하는 이유는 ssh 로 해당 인스턴스에 붙어야하기 때문.
<--user-data>
EC2 인스턴스 생성 시 자동으로 시작하게 하는 스크립트로
사용자 데이터를 지정할 수 있음.
명령어 결과
aws ec2 run-instances --image-id ami-09d33644806aa7550 --instance-type t3.small --key-name iksoon-cloud9 --security-group-ids sg-05d86720a4c79deac --subnet-id subnet-0ad696b8d2877b3fd
EC2 인스턴스를 확인해보면 생성된 인스턴스를 볼 수 있음.
Name을 따로 설정하지 않아서 ' - ' 로 생성됨.
인스턴스가 실행 중 상태가되면 SSH를 통해 인스턴스에 연결
상위에서 생성된 EC2인스턴스에서 우클릭 후 연결 클릭해서 SSH 연결 정보를 파악
[SSH 연결 명령어 예제]
ssh -i "iksoon-cloud9.pem" core@ec2-15-165-48-150.ap-northeast-2.compute.amazonaws.com
ssh 연결을 확인해보면 user가 fedora로 명시되어있지만
fedora CoreOS의 경우 fedora CoreOS default 사용가 core 임
SSH 연결이 완료 후 아래의 명령 수행
etcdctl, etcd 설치
원래 기존의 coreOS의 경우 etcdctl이 default로 제공되는데
fedora Core OS의 경우 etcdctl이 없음.
직접 설치 해줘야함
주의 : yum, apt-get, wget과 같은 명령을 사용할 수 없음
2020-12-22일 기준 etcd, etcdctl 3.4.0이 최신 version
[설치 명령]
export ETCD_VER=v3.4.0
curl -o etcd-${ETCD_VER}-linux-amd64.tar.gz https://storage.googleapis.com/etcd/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf etcd-${ETCD_VER}-linux-amd64.tar.gz
sudo mv etcd-${ETCD_VER}-linux-amd64/etcdctl /usr/local/bin/etcdctl
sudo mv etcd-${ETCD_VER}-linux-amd64/etcd /usr/local/bin/etcd
etcdctl
etcd --version
etcd 토큰 발급
단일 노드 etcd "클러스터"에 대한 토큰을 발급받아야함.
[명령어]
export TOKEN=$(curl -sw "\n" 'https://discovery.etcd.io/new?size=1' | cut -d "/" -f 4)
etcd 생성 명령
[etcd config file 생성 명령어]
cat > etcd_conf.yaml << EOF | sed 's/[\]//g' > etcd_conf.yaml advertise-client-urls: http://{PUBLIC_IPV4}:2379 initial-advertise-peer-urls: http://{PRIVATE_IPV4}:2380 listen-client-urls: http://0.0.0.0:2379 listen-peer-urls: http://{PRIVATE_IPV4}:2380 discovery: https://discovery.etcd.io/{TOKEN} EOF |
[예제]
cat > etcd_conf.yaml << EOF | sed 's/[\]//g' > etcd_conf.yaml advertise-client-urls: http://15.165.48.150:2379 initial-advertise-peer_urls: http://192.168.9.4:2380 listen-client-urls: http://0.0.0.0:2379 listen-peer-urls: http://192.168.9.4:2380 discovery: https://discovery.etcd.io/cdc47aecc4bdc93c61952b9dd2335c21 EOF |
[etcd 실행 명령어]
etcd --config-file ./etcd_conf.yaml
참고)
아래 예제는 coreOS ec2를 재생성하고 etcd를 실행한 예제라
상위 ip정보와 다름.
etcd가 실행되면 config 에서 설정한 ip정보와 동일한지 확인해야함.
etcdctl 명령 실행
커맨드를 새로 하나 더 열고 진행
10.244.0.0/16
로 flannel ip 대역을 설정하는 것.
[명령어]
export ETCDCTL_API=3
etcdctl put /coreos.com/network/config '{"Network":"10.244.0.0/16", "SubnetLen": 24, "Backend": {"Type": "vxlan", "VNI": 1}}'
export ETCDCTL_API=2
etcdctl set /coreos.com/network/config '{"Network":"10.244.0.0/16", "Backend": {"Type": "vxlan"}}'
[key-value 확인]
etcdctl get /coreos.com/network/config
kubernetes에 flannel 배포
kubectl 명령이 가능한 ec2에서 실행
test환경에서는 cloud9에서 실행함.
[flannel.yaml 파일 다운]
curl -o flannel.yaml https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
test환경 기준 flannel latest version : flannel:v0.13.1-rc1
받은 flannel yaml파일을 아래와 같이 수정해야함
"- --etcd-endpoints=http://<etcd_ip>:2379 "
default로 설정되어 있는 kubernetes etcd 모듈을 바라보는게 아니라
상위에서 생성한 fedora CoreOS의 etcd를 바라보도록 해줘야함.
fedora CoreOS EC2 인스턴스의 public IP를 추가해야함.
- --kube-subnet-mgr=false
해당 설정을 false로 변경
[다운받은 flannel.yaml 파일 수정]
--- apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: psp.flannel.unprivileged annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default spec: privileged: false volumes: - configMap - secret - emptyDir - hostPath allowedHostPaths: - pathPrefix: "/etc/cni/net.d" - pathPrefix: "/etc/kube-flannel" - pathPrefix: "/run/flannel" readOnlyRootFilesystem: false # Users and groups runAsUser: rule: RunAsAny supplementalGroups: rule: RunAsAny fsGroup: rule: RunAsAny # Privilege Escalation allowPrivilegeEscalation: false defaultAllowPrivilegeEscalation: false # Capabilities allowedCapabilities: ['NET_ADMIN', 'NET_RAW'] defaultAddCapabilities: [] requiredDropCapabilities: [] # Host namespaces hostPID: false hostIPC: false hostNetwork: true hostPorts: - min: 0 max: 65535 # SELinux seLinux: # SELinux is unused in CaaSP rule: 'RunAsAny' --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: flannel rules: - apiGroups: ['extensions'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: ['psp.flannel.unprivileged'] - apiGroups: - "" resources: - pods verbs: - get - apiGroups: - "" resources: - nodes verbs: - list - watch - apiGroups: - "" resources: - nodes/status verbs: - patch --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: flannel roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: flannel subjects: - kind: ServiceAccount name: flannel namespace: kube-system --- apiVersion: v1 kind: ServiceAccount metadata: name: flannel namespace: kube-system --- kind: ConfigMap apiVersion: v1 metadata: name: kube-flannel-cfg namespace: kube-system labels: tier: node app: flannel data: cni-conf.json: | { "name": "cbr0", "cniVersion": "0.3.1", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] } net-conf.json: | { "Network": "10.244.0.0/16", # etcdctl 에서 설정했던 Network와 동일해야함 "Backend": { "Type": "vxlan" } } --- apiVersion: apps/v1 kind: DaemonSet metadata: name: kube-flannel-ds namespace: kube-system labels: tier: node app: flannel spec: selector: matchLabels: app: flannel template: metadata: labels: tier: node app: flannel spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/os operator: In values: - linux hostNetwork: true priorityClassName: system-node-critical tolerations: - operator: Exists effect: NoSchedule serviceAccountName: flannel initContainers: - name: install-cni image: quay.io/coreos/flannel:v0.13.1-rc1 command: - cp args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist volumeMounts: - name: cni mountPath: /etc/cni/net.d - name: flannel-cfg mountPath: /etc/kube-flannel/ containers: - name: kube-flannel image: quay.io/coreos/flannel:v0.13.1-rc1 command: - /opt/bin/flanneld args: - --ip-masq - --kube-subnet-mgr=false - --etcd-endpoints=http://<etcd_ip>:2379 # 해당 설정 추가 resources: requests: cpu: "100m" memory: "50Mi" limits: cpu: "100m" memory: "50Mi" securityContext: privileged: false capabilities: add: ["NET_ADMIN", "NET_RAW"] env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace volumeMounts: - name: run mountPath: /run/flannel - name: flannel-cfg mountPath: /etc/kube-flannel/ volumes: - name: run hostPath: path: /run/flannel - name: cni hostPath: path: /etc/cni/net.d - name: flannel-cfg configMap: name: kube-flannel-cfg |
[flannel.yaml 배포]
kubectl apply -f flannel.yaml
[배포 결과 확인]
kubectl get all -n kube-system
모든 worker node에 flannel pod가 정상배포되었음
Running 상태 확인
flannel pod의 log를 확인
아래와 같이 나와야 정상상태.
모든 Worker node에 접속해서 네트워크 인터페이스를 확인해보면
flannel.1 인터페이스가 배포되어있음.
해당 과정에서 나올 수 있는 에러 해결법
에러 1 : Couldn't fetch network config: 100: Key not found (/coreos.com)
[원인]
fedora CoreOS에서 etcd에 접근이 불가능한 상태
[해결법]
fedora CoreOS에서 etcd가 정상적으로 실행되었는지 확인.
실행 되었다면 etcd config 파일의 내용이 정상적으로 etcd에 반영되었는지 확인.
상위 config의 public, private ip가 정상적으로 들어가 있는지 확인.
에러 2 : Couldn't fetch network config: client: response is invalid json. The endpoint is probably not valid etcd cluster endpoint.
[해결법]
export ETCDCTL_API=2 가 반드시 2여야함.
많은 사이트에서 3으로 하라고 하는데 3이면 안됨.
etcdctl set /coreos.com/network/config '{"Network":"10.244.0.0/16", "Backend": {"Type": "vxlan"}}'
명령을 정상적으로 했는지 확인
모든 kubernetes Worker Node docker restart
모든 worker node EC2에 ssh 로 접속해서
docker를 restart 해줘야함
[명령어]
sudo systemctl restart docker
Worker Node에 Pod 배포.
flannel 이 성공적으로 설치되었으니
이제 deployment를 통해 정상적으로 pod가 배포되는지 확인해봄.
사용한 deployment 예제
apiVersion: apps/v1 kind: Deployment metadata: name: iksoon-deployment-mysql labels: app: iksoon-mysql-test spec: replicas: 1 selector: matchLabels: app: iksoon-pod-mysql template: metadata: labels: app: iksoon-pod-mysql spec: containers: - name: iksoon-mysql image: peksoon/iksoon_mysql:1.0.2 ports: - containerPort: 3306 |
배포 결과
pod가 생성안되고 계속 ContainerCreating 상태임.....
확인해보니 아래와 같은 오류가 있음.
Failed to create pod sandbox: rpc error: code = Unknown desc = [failed to set up sandbox container "8866c0b122ae9508d00ae7bd73d75bb7af45c704a78e9e10cf1ffe48514a120c" network for pod "iksoon-deployment-mysql-85b5b5b49d-tlthf": networkPlugin cni failed to set up pod "iksoon-deployment-mysql-85b5b5b49d-tlthf_default" network: add cmd: Error received from AddNetwork gRPC call: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:50051: connect: connection refused", failed to clean up sandbox container "8866c0b122ae9508d00ae7bd73d75bb7af45c704a78e9e10cf1ffe48514a120c" network for pod "iksoon-deployment-mysql-85b5b5b49d-tlthf": networkPlugin cni failed to teardown pod "iksoon-deployment-mysql-85b5b5b49d-tlthf_default" network: del cmd: error received from DelNetwork gRPC call: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:50051: connect: connection refused"] |
[오류 원인]
기존의 AWS-CNI가 먼저 사용되고 있었음으로
Worker Node내부의 각종 iptables 설정의 netfilter chain이 AWS-CNI 로 설정되어있었는데
AWS-CNI를 삭제할 시 AWS-CNI관련 tool만 삭제되지
iptables netfilter 관련 chain 설정은 삭제되지 않고 남아있음.
이 상태에서 Flannel 설치 되면서 chain이 뒤죽박죽 되어 해당 오류가 발생함.
[해결책]
해결책 1. AWS-CNI관련 iptables Chain을 하나하나 삭제해줌.
하지만 해당 방법은 AWS-CNI분석이 필요함으로 추천하지 않음
해결책 2. Worker Node재생성.
Flannel로 설정이 되어있는 상태에서 Worker Node EC2를 새로 추가하면
해당 Worker Node는 iptables가 flannel로 정상 설치 됨.
본 test에서는 2번 해결책으로 문제를 해결함.
인스턴스를 순서대로 하나씩 종료하면
EKS Node Auto scaling에 의해서 새로운 Worker Node가 자동으로 재생성됨.
이와 같은 방법으로 Worker Node를 모두 재생성 하고
다시 Pod를 배포 해봄.
결과적으로
Pod가 정상적으로 생성되고
Pod에 할당된 ip또한 flannel 대역대로 생성되게 됨.
해당 pod가 배포된 worker node로 가보면
아래와 같이
cni0 인터페이스가 추가로 생성되고
pod에 해당하는 veth 인터페이스가 생성됨.
[구축 완료.]
특정 Worker Node에 35개의 Pod 배포 Test
flannel CNI를 사용한다면 인스턴스 유형과 상관없이
다수의 Pod를 배포할 수 있을것이라 생각하여
Pod 35개를 배포해봄
그 결과 인스턴스 유형의 제한이 동일하게 적용됨
flannel cni를 사용하더라도 인스턴스 유형에 따라 Pod배포 수가 제한됨.
이유는 해당 인스턴스 유형을 가진 worker node의 자원이 부족하기 때문.
추가로 아래와 같이 flannel CNI를 사용하면
더이상 private 인터페이스는 추가로 생성되지 않고
flannel CNI에 따라 Pod에 veth 인터페이스만 추가됨.
참고
EKS Flannel
https://coreos.com/flannel/docs/latest/configuration.html
https://coreos.com/flannel/docs/latest/kubernetes.html
https://medium.com/@jeremy.i.cowan/running-flannel-on-eks-9a2f7a285a23
CoreOS란?
http://www.moreagile.net/2014/07/docker-coreos.html
https://www.ciokorea.com/news/36017
CoreOS config transpiler(ct).
https://coreos.com/os/docs/latest/overview-of-ct.html
https://coreos.com/os/docs/latest/booting-on-ec2.html
kubernetes single node etcd cluster
https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/
Fedora CoreOs
https://docs.fedoraproject.org/en-US/fedora-coreos/
RHEL CoreOs
etcd
https://etcd.io/docs/v3.4.0/op-guide/configuration/
flannel etcdconifg 예제
https://github.com/etcd-io/etcd/blob/release-3.4/etcd.conf.yml.sample
https://github.com/coreos/flannel/issues/1191
etcdctl
https://etcd.io/docs/v3.4.0/platforms/freebsd/
https://arisu1000.tistory.com/27782
https://brownbears.tistory.com/11
'클라우드 > AWS' 카테고리의 다른 글
EKS Network Policy 분석 (0) | 2021.05.06 |
---|---|
EKS AWS CNI - Pod 간 통신 (0) | 2021.05.06 |
EKS AWS CNI 분석 (2) | 2021.05.03 |
AWS Kubernetes (EKS) (0) | 2021.02.23 |
AWS API 사용 (Python) (0) | 2020.08.12 |