이쿠의 슬기로운 개발생활

함께 성장하기 위한 보안 개발자 EverNote 내용 공유

클라우드/Kubernetes

41. Flannel CNI 분석

이쿠우우 2020. 11. 7. 13:15
반응형

 

 

 

 

Flannel CNI 분석

 

Flannel version : v0.12.0 기준

 

Flannel 분석에 필요한 기초 개념

 

[PodSecurityPolicy]

보안 기능에 대한 정책을 정의.

Pod 생성 및 업데이트에 대한 세분화된 권한을 부여.

PodSecurity는 생성하는 순간 Cluster 전체에 적용됨.

 

[ClusterRole]

특정 API, 리소스에 대한 권한을 명시한 규칙의 집합

kubernetes Cluster 전체에 대한 권한을 정의할 수 있음

여러 namespace 에 걸쳐있는 worker node 리소스 등 자원에 대해서도 권한을 정의할 수 있음

그렇기 때문에 별도 metadata에 네임스페이스 설정이 없음

 

[ClusterRoleBinding]

ClusterRole을 사용자에게 부여하기 역할을 수행

ClusterRole과 User 를 묶어주는 역할을 수행

 

[ServiceAccount]

Kubernetes에 인증을 요청할 수 있는 사용자 종류.

Client 가 kubernetes API를 호출할 때 이는 실제 사람인 사용자가 아니라 시스템이 됨

그래서 kubernetes 는 이를 일반 사용자와 분리해서 관리하는데 그것이 바로 Serivce Account 임.

Service Account는 kuberentes 가 직접 관리하는 사용자 계정으로 

Secret Object 가 할당되어서 해당 Service Account에 대한 비밀번호 역할을 함

 

[ConfigMap]

ConfigMap 은 Key-Value 형태의 설정값을 가지고 기밀이 아닌 데이터를 저장하는데 사용하는 kubernetes Object.

즉 설정 정보를 저장해놓는 일종의 저장소 역할.

Application(개발한 프로그램)을 배포하다 보면, 환경에 따라서 다른 설정값을 사용하는 경우가 있음.

예를 들어, DB의 IP, API를 호출하기 위한 API KEY, 개발/운영에 따른 디버그 모드, 환경 설정 파일들이 있는데,

Container image는 같지만, 이런 환경 변수가 차이가 나는 경우

매번 다른 Container image를 만드는 것은 관리상 불편할 수 밖에 없음

이렇게 다른 설정을 가지고 실행을 해야할때 사용하는 것이 바로 ConfigMap Object.

 

[DaemonSet]

kubernetes Cluster 전체에 Pod를 생성할 때 사용하는 controller.

DaemonSet은 보통 모니터링, 로그 수집, 네트워크 CNI 등

Cluster 전체에 Pod를 실행 시켜야하는 상황에 사용됨.

옵션으로 특정 Node 에만 생성되게 지정할 수도 있음.

 

[Taint & Toleration]

특정 Worker Node에 Pod가 배치되지 않기를 원하면 Taint를 적용하고

Taint가 적용된 Worker Node에 Pod를 배치하기를 원한다면 Pod에 Taint에 맞는 Toleration을 적용해야함.

 

[Affinity]

Pod가 특정 Worker Node에 배치되도록 하는 정책.

Node를 기준으로 Scheduling 하는 Node Affinity와

Pod를 기준으로 Scheduling 하는 Pod Affinity가 있음.

 

[PriorityClass]

PriorityClass를 사용하면 Pod에 우선순위를 설정할 수 있음.

새로 배포되어야 하는 Pod의 우선 순위가 높은데

리소스 부족 등 다양한 이슈로 배포가 안된다면

해당 Pod 보다 낮은 우선순위를 가지고 있는 Pod를 Preemption하여

우선 순위가 낮은 Pod를 종료시키고 

우선 순위가 높은 새로운 Pod가 배포되어 Running됨.

 


 

 

Flannel yaml 파일로 생성되는 목록

 

Flannel yaml 파일 : https://raw.githubusercontent.com/coreos/flannel/v0.12.0/Documentation/kube-flannel.yml

 

PodSecurityPolicy

ClusterRole

ClusterRoleBinding

ServiceAccount

ConfigMap

DaemonSet  - amd64

DaemonSet -  arm64

DaemonSet -  arm

DaemonSet -  ppc64le

DaemonSet -  s390x

 

 


 

Flannel yaml 파일 분석

 

 

PodSecurityPolicy

 

[생성 확인 명령어]

 kubectl get PodSecurityPolicy

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'

 

[테스트 중 특이사항]

( 이슈 1. )

PSP를 활성화 하고 Flannel Daemonset 의 Pod를 재배포하면 Pending 상태가 됨.

Log를 확인해보니 Host의 Selinux가 disabled 되어있어서 배포가 안된다고해서

Host의 Selinux를 활성화 하고 worker node를 재기동하니

Master Node, Worker Node 연결이 안됨.

해당 기능 테스트를 위해서는 Selinux 리서치도 필요해보임.

 

 

 


 

ClusterRole

[생성 확인 명령어]

kubectl get clusterrole

kind: ClusterRole

apiVersion: rbac.authorization.k8s.io/v1beta1

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

 

 


 

 

 

ClusterRoleBinding

 

[생성 확인 명령어]

kubectl get clusterrolebinding

kind: ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1beta1

metadata:

  name: flannel

roleRef:

  apiGroup: rbac.authorization.k8s.io

  kind: ClusterRole

  name: flannel

subjects:

- kind: ServiceAccount

  name: flannel

  namespace: kube-system

 

 

 


 

 

ServiceAccount

[생성 확인 명령어]

kubectl get sa -n kube-system

apiVersion: v1

kind: ServiceAccount

metadata:

  name: flannel

  namespace: kube-system

 

 


 

 

ConfigMap

[생성 확인 명령어]

kubectl get configmap -n 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",

      "Backend": {

        "Type": "vxlan"

      }

    }

 

 

 

 

 

 

DaemonSet

CPU = amd64, arm64, arm, ppc64le, s390x

사용되는 image가 CPU 종류가 따라 다르고 그 이외 옵션은 모든 DaemonSet 이 동일함

 

[생성 확인 명령어]

kubectl get daemonset -n kube-system

apiVersion: apps/v1

kind: DaemonSet

metadata:

  name: kube-flannel-ds-amd64

  namespace: kube-system

  labels:

    tier: node

    app: flannel

spec:

  selector:

    matchLabels:

      app: flannel

  template:

    metadata:

      labels:

        tier: node

        app: flannel

    spec:

 

# [affinity]

# Pod가 배포될 수 있는 Worker Node를 판단함.

# Linux OS의 Host에만 Pod배포 가능

# amd64 CPU에만 배포 가능 (CPU에 종류에 맞는 Pod만 배포하기위해 affinity를 사용 )

      affinity:

        nodeAffinity:

          requiredDuringSchedulingIgnoredDuringExecution:

            nodeSelectorTerms:

              - matchExpressions:

                  - key: kubernetes.io/os

                    operator: In

                    values:

                      - linux

                  - key: kubernetes.io/arch

                    operator: In

                    values:

                      - amd64


# [hostNetwork]

# psp.flannel.unprivileged에서

# hostNetWork 설정이 true라 PSP가 활성화 되어있어도 사용가능

# Pod안에 있는 모든 container들이

# Worker Node의 network namespace와 동일한 네트워크를 사용하도록 설정.

# container 에서 eth0 network interface에 접근 가능.

      hostNetwork: true

 

# [priorityClassName]

# Cluster에 리소스가 부족한 상황 등등

# Worker Node에 Pod를 배포할 수 없는 상태가 발생하면

# Flannel Pod 배포 우선순위를 system-node-critical로

# 높게 설정해서 되도록 배포할 수 있도록 설정함.

      priorityClassName: system-node-critical

 

# [tolerations]

# Worker Node에 Taint가 설정이 되어있다면

# Pod를 배포하지 못할 수도 있는데

# Worker Node에 Taint가 설정이 되어있어도

# 해당 flannel Pod는 무조건 배포되게 하기 위해 tolerations를 설정함

# Worker Node에 설정되어있는 Taint의 effect가 NoSchedule인 경우 Pod 생성가능

      tolerations:

      - operator: Exists

        effect: NoSchedule

 

# [serviceAccountName]

# Pod 생성 시 flannel SA를 통해 psp.flannel.unprivileged 정책을 적용함

      serviceAccountName: flannel

 

# [initContainers]

# app container가 실행되기 전에 Pod의 초기화 하는 역할을 수행

# Pod가 모두 준비되기 전에 실행된 후 종료되는 Container이기 때문에

# initContainer는 초기화만 하고 사라짐

# 주로 보안적인 이유로

# App Container의 image와 같이 두면 안되는 코드를 별도로 관리할때 사용

      initContainers:

      - name: install-cni

        image: quay.io/coreos/flannel:v0.12.0-amd64

# [명령어 실행]

# cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conflist

# 해당 initContainers의 cni-conf.json (상위 configMap의 내용)을

# worker Node의 /etc/cni/net.d 경로에 10-flannel.conflist 파일로 복사

        command:

        - cp

        args:

        - -f

        - /etc/kube-flannel/cni-conf.json

        - /etc/cni/net.d/10-flannel.conflist # WorkerNode의 /etc/cni/net.d 경로에 10-flannel.conflist 파일 생성

        volumeMounts:

        - name: cni

          mountPath: /etc/cni/net.d # WorkerNode에 /etc/cni/net directory 생성

        - name: flannel-cfg

          mountPath: /etc/kube-flannel/ # WorkerNode에 /etc/kube-flannel directory 생성

 

# [containers]

      containers:

      - name: kube-flannel

        image: quay.io/coreos/flannel:v0.12.0-amd64

# [명령어 실행]

# /opt/bin/flanneld --ip-masq --kube-subnet-mgr

        command:

        - /opt/bin/flanneld

        args:

        - --ip-masq

        - --kube-subnet-mgr

# [자원 설정]

        resources:

          requests:

            cpu: "100m"

            memory: "50Mi"

          limits:

            cpu: "100m"

            memory: "50Mi"

# [linux 커널 기능 사용 권한 부여]

# linux 커널의 NET_ADMIN과 SYS_TIME 권한 만을 컨테이너에 부여한 예제.

# NET_ADMIN = Perform various network-related operations.

# NET_RAW = Use RAW and PACKET sockets.

        securityContext:

          privileged: false

          capabilities:

            add: ["NET_ADMIN", "NET_RAW"]

# [환경 변수 설정]

        env:

# 생성되는 Pod에 환경변수

# POD_NAME=[배포된 POD NAME]

# POD_NAMESPACE=[배포된 namespace NAME]

# 가 저장됨

        - name: POD_NAME

          valueFrom:

            fieldRef:

              fieldPath: metadata.name

        - name: POD_NAMESPACE

          valueFrom:

            fieldRef:

              fieldPath: metadata.namespace

# [volume mount]

        volumeMounts:

        - name: run

          mountPath: /run/flannel

        - name: flannel-cfg

          mountPath: /etc/kube-flannel/

 

# [volumes]

# hostPath를 사용해서 host 즉 worker Node에 directory를 마운트함.

# 만약 worker Node에 해당 경로가 없다면 K8s가 직접 directory를 생성한 후 contaienr에 mount함

# deps는 상관없이 무조건 directory를 생성.

# Flannel은 이런방법으로 worker Node에 directory와 file을 생성함.

# worker Node에

# /run/flannel

# /etc/cni/net.d

# 2개의 directory를 생성함.

      volumes:

        - name: run

          hostPath:

            path: /run/flannel

        - name: cni

          hostPath:

            path: /etc/cni/net.d

# 상위에서 생성한 ConfigMap을 Volume 형태로 mount함.

# key : 파일이 되어있음

# value : 파일의 내용이 되어있음

        - name: flannel-cfg

          configMap:

            name: kube-flannel-cfg

 

 

 

[ Flannel이 Worker Node에 생성한 Directory 와 File ]

 

/run/flannel

/etc/cni/net.d

 

 

 


제 글을 복사할 시 출처를 명시해주세요.
글에 오타, 오류가 있다면 댓글로 알려주세요! 바로 수정하겠습니다!


 

반응형