WellSpring

3주차 : Calico CNI & Mode 본문

KANS3 - k8s Advanced Networking Study

3주차 : Calico CNI & Mode

daniel00324 2024. 9. 12. 14:31
더보기

※ 본 게재 글은 gasida님의 KANS(Kubernetes Advanced Networking Study) 강의내용과 실습예제 및 Calico 공식 가이드 문서, "쿠버네티스입문" 서적 등을 참고하여 작성하였습니다. 

 

 
이번 주는 Calico와 동작모드에 대해서 알아보겠습니다.
 

1. Calico란?

 - 가상머신이나 Container 환경에서 L3 레벨의 가상 네트웍크 환경을 구성 및 관리해 주는 오픈소스 Tool 이다.
   ( k8s 노드에 설치되어 각 Pod 간 통신이 가능하도록 도와주며, 보안관련 설정도 가능함 )

 

1-1. 칼리코의 기능 및 특징

 - 쿠버네티스 플랫폼을 포함하여 다양한 플랫폼에서 워크로드의 네트워크 통신과 네트워크 보안 기능을 제공함
  1) Kubernetes, OpenShift, Mirantis Kubernetes Engine(MKE), OpenStack 및 베어메탈 서비스를 포함한 광범위한 플랫폼 지원
  2) 클라우드 네이티브 확장성과 빠른 성능을 제공함 ( eBPF 데이터 플레인 or Linux 표준 네트워킹 파이프라인 기반 )
  3) 개발자와 클러스터 운영자에게 일관된 경험과 기능 세트를 제공
 

 

[ Calico 컴포넌트 구성도 ]

Ref. Link - https://docs.tigera.io/calico/latest/reference/architecture/overview

1-2. 구성요소(Component)

   - Calico의 구성요소 별 주요기능과 특징은 다음과 같습니다.

[ Calico Component 간 통신구조 ]

 
1) Felix (C)

 - 경로 및 ACL, 그리고 해당 호스트의 엔드포인트에 원하는 연결을 제공하며,  
   호스트에 필요한 기타 모든 것을 프로그래밍 함
  ( Interface managementRoute programmingACL programming , State reporting )
 - 엔드포인트를 호스팅하는 각 컴퓨터에서 agent daemon 형태로 실행됨
 - IPtables 규칙 설정을 관리함

 
2) BIRD (B)

 - 오픈소스 소프트웨어 라우팅 데몬 프로그램
 - Felix에서 경로를 가져오고 호스트 간 라우팅을 위해 네트워크의 BGP 피어에 광고함
 - Felix 에이전트를 호스팅하는 각 노드에서 실행됨 

 
3) Calico Datastore (A)

 - Calico 동작을 위한 설정을 저장하는 저장소
 - Kubernetes에서는 'API저장소' or 'ETCD' 를 선택할 수 있음

 
4) Confd (D)

 - 가벼운 오픈소스 설정관리 변경 관리하는 툴
 - BGP 설정 등 변경사항이 발생하면 버드의 설정파일을 만들고 변경된 부분을 반영함

 
5) IPAM (E)

 - calico는 자체의 IPAM 플러그인을 제공

 

[ Calico Component in Kubernetes ]

[ 동작 상세 ]

- Calico는, kubectl or calicoctl 명령어를 사용하여 POD 를 생성하거나, Calico 설정을 변경하여 해당 내용을
   calico datastore 에 저장한다.
- calicoctl 은 calico object들을 CRUD (Create, Read, Update, Delete) 할 수 있다.
- POD 생성 시, Calico DataStore를 참고하여 CNI Plug-In 과 CNI IPAM Plug-In 을 통해 파드의 네트웍을 설정함
- 한 노드의 POD가 다른 노드의 POD와 통신 하는 경우, Bird 로 학습한 네트워크 정보를 Felix 에 의해 호스트의
  라우팅 테이블과 IPtables를 통해 이루어진다.


1-3. 실습 환경 구성

♣ Calico 실습을 위한 K8S 환경 구성 
  ( 실습 환경은 K8S v1.30.X, 노드 OS(Ubuntu 22.04 LTS) , CNI(Calico v3.28.1, IPIP, NAT enable) , IPTABLES proxy mode )
 - Calico is a networking and security solution that enables Kubernetes workloads and non-Kubernetes/legacy workloads to communicate seamlessly and securely.

더보기

 2개의 네트워크 대역이 존재 : AWS 환경에서 k8s-rtr 은 없고, AWS 내부 라우터가 대신 라우팅 처리합니다.

[ Cluster 네트웍 구성도 ]
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-3w.yaml

# CloudFormation 스택 배포
# aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

## Tip. 인스턴스 타입 변경 : MyInstanceType=t2.micro
예시) aws cloudformation deploy --template-file kans-3w.yaml --stack-name mylab --parameter-overrides MyInstanceType=t2.micro KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 k8s-m EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2

# [모니터링] CloudFormation 스택 상태 : 생성 완료 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

# k8s-m EC2 SSH 접속
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)

 

[ Cloud Formation 의 Stack 이용한 자원생성 - 결과 ]


▶ 기본 설정 확인

# (참고) control-plane
## kubeadm init --token 123456.1234567890123456 --token-ttl 0 --pod-network-cidr=172.16.0.0/16 --apiserver-advertise-address=192.168.10.10 --service-cidr 10.200.1.0/24 --cri-socket=unix:///run/containerd/containerd.sock
# worker
## kubeadm join --token 123456.1234567890123456 --discovery-token-unsafe-skip-ca-verification 192.168.10.10:6443

#
kubectl config rename-context "kubernetes-admin@kubernetes" "HomeLab"
kubens default

#
kubectl cluster-info
kubectl get node -owide
kubectl get service,ep
kubectl get pod -A -owide

#z
tree /opt/cni/bin/
ls -l /opt/cni/bin/

#
ip -c route
ip -c addr
iptables -t filter -L
iptables -t nat -L
iptables -t filter -L | wc -l
iptables -t nat -L | wc -l

 [ 실행 결과 - 한 눈에 보기 ]

  ▼ Master Node에서 살펴 본 기본 구성정보

[ Remind : 'bridge' 파일의 생성은 이전 2주차 글 참조 ]

 

참고) kans-3w.yaml  ( vagrant 활용 시, 'Userdata' 영역 '복사+붙여넣기' )

더보기
AWSTemplateFormatVersion: '2010-09-09'

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "<<<<< Deploy EC2 >>>>>"
        Parameters:
          - KeyName
          - SgIngressSshCidr
          - MyInstanceType
          - LatestAmiId

      - Label:
          default: "<<<<< Region AZ >>>>>"
        Parameters:
          - TargetRegion
          - AvailabilityZone1
          - AvailabilityZone2

      - Label:
          default: "<<<<< VPC Subnet >>>>>"
        Parameters:
          - VpcBlock
          - PublicSubnet1Block
          - PublicSubnet2Block

Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instances.
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  SgIngressSshCidr:
    Description: The IP address range that can be used to communicate to the EC2 instances.
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 0.0.0.0/0
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  MyInstanceType:
    Description: Enter EC2 Type(Spec) Ex) t2.micro.
    Type: String
    Default: t3.medium
  LatestAmiId:
    Description: (DO NOT CHANGE)
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id'
    AllowedValues:
      - /aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id

  TargetRegion:
    Type: String
    Default: ap-northeast-2
  AvailabilityZone1:
    Type: String
    Default: ap-northeast-2a
  AvailabilityZone2:
    Type: String
    Default: ap-northeast-2c

  VpcBlock:
    Type: String
    Default: 192.168.0.0/16
  PublicSubnet1Block:
    Type: String
    Default: 192.168.10.0/24
  PublicSubnet2Block:
    Type: String
    Default: 192.168.20.0/24

Resources:
# VPC
  KansVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: Kans-VPC

# PublicSubnets
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Ref AvailabilityZone1
      CidrBlock: !Ref PublicSubnet1Block
      VpcId: !Ref KansVPC
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Kans-PublicSubnet1
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Ref AvailabilityZone2
      CidrBlock: !Ref PublicSubnet2Block
      VpcId: !Ref KansVPC
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Kans-PublicSubnet2

  InternetGateway:
    Type: AWS::EC2::InternetGateway
  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref KansVPC

  PublicSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref KansVPC
      Tags:
        - Key: Name
          Value: Kans-PublicSubnetRouteTable
  PublicSubnetRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicSubnetRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicSubnetRouteTable
  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicSubnetRouteTable


# EC2 Hosts
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Kans EC2 Security Group
      VpcId: !Ref KansVPC
      Tags:
        - Key: Name
          Value: Kans-SG
      SecurityGroupIngress:
      - IpProtocol: '-1'
        CidrIp: !Ref SgIngressSshCidr
      - IpProtocol: '-1'
        CidrIp: !Ref VpcBlock
      - IpProtocol: '-1'
        CidrIp: 172.16.0.0/16
      - IpProtocol: '-1'
        CidrIp: 10.200.1.0/24

  EC21ENI1:
    Type: AWS::EC2::NetworkInterface
    Properties:
      SubnetId: !Ref PublicSubnet1
      Description: !Sub Kans-EC21-ENI1
      GroupSet:
      - !Ref EC2SG
      PrivateIpAddress: 192.168.10.10
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: !Sub Kans-EC21-ENI1

  EC22ENI1:
    Type: AWS::EC2::NetworkInterface
    Properties:
      SubnetId: !Ref PublicSubnet1
      Description: !Sub Kans-EC22-ENI1
      GroupSet:
      - !Ref EC2SG
      PrivateIpAddress: 192.168.10.101
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: !Sub Kans-EC22-ENI1

  EC23ENI1:
    Type: AWS::EC2::NetworkInterface
    Properties:
      SubnetId: !Ref PublicSubnet1
      Description: !Sub Kans-EC23-ENI1
      GroupSet:
      - !Ref EC2SG
      PrivateIpAddress: 192.168.10.102
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: !Sub Kans-EC23-ENI1

  EC24ENI1:
    Type: AWS::EC2::NetworkInterface
    Properties:
      SubnetId: !Ref PublicSubnet2
      Description: !Sub Kans-EC24-ENI1
      GroupSet:
      - !Ref EC2SG
      PrivateIpAddress: 192.168.20.100
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: !Sub Kans-EC24-ENI1

  EC21:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref MyInstanceType
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyName
      Tags:
        - Key: Name
          Value: k8s-m
      NetworkInterfaces:
        - NetworkInterfaceId: !Ref EC21ENI1
          DeviceIndex: '0'
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp3
            VolumeSize: 30
            DeleteOnTermination: true
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash
            hostnamectl --static set-hostname k8s-m

            # Config convenience
            echo 'alias vi=vim' >> /etc/profile
            ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

            # Disable ufw & AppArmor
            systemctl stop ufw && systemctl disable ufw
            systemctl stop apparmor && systemctl disable apparmor
            
            #  ubuntu user -> root user
            echo "sudo su -" >> /home/ubuntu/.bashrc

            # Setting Local DNS Using Hosts file
            echo "192.168.10.10 k8s-m" >> /etc/hosts
            echo "192.168.10.101 k8s-w1" >> /etc/hosts
            echo "192.168.10.102 k8s-w2" >> /etc/hosts
            echo "192.168.20.100 k8s-w0" >> /etc/hosts

            # add kubernetes repo
            curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
            echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list

            # add docker-ce repo with containerd
            curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
            echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

            # packets traversing the bridge are processed by iptables for filtering
            echo 1 > /proc/sys/net/ipv4/ip_forward
            # enable br_filter for iptables 
            modprobe br_netfilter

            # Update the apt package index, install kubelet, kubeadm and kubectl, and pin their version
            apt update && apt-get install -y kubelet kubectl kubeadm containerd.io && apt-mark hold kubelet kubeadm kubectl

            # containerd configure to default and cgroup managed by systemd 
            containerd config default > /etc/containerd/config.toml
            sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

            # avoid WARN&ERRO(default endpoints) when crictl run  
            cat <<EOF > /etc/crictl.yaml
            runtime-endpoint: unix:///run/containerd/containerd.sock
            image-endpoint: unix:///run/containerd/containerd.sock
            EOF

            # ready to install for k8s 
            systemctl restart containerd && systemctl enable containerd
            systemctl enable --now kubelet

            # init kubernetes (w/ containerd)
            kubeadm init --token 123456.1234567890123456 --token-ttl 0 --pod-network-cidr=172.16.0.0/16 --apiserver-advertise-address=192.168.10.10 --service-cidr 10.200.1.0/24 --cri-socket=unix:///run/containerd/containerd.sock

            # Install packages
            apt install tree jq bridge-utils net-tools conntrack ipset wireguard -y

            # Install Helm
            curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

            # config for master node only 
            mkdir -p /root/.kube
            cp -i /etc/kubernetes/admin.conf /root/.kube/config
            #chown $(id -u):$(id -g) /root/.kube/config

            # kubectl completion on bash-completion dir
            kubectl completion bash > /etc/bash_completion.d/kubectl

            # alias kubectl to k 
            echo 'alias k=kubectl' >> /etc/profile
            echo 'complete -F __start_kubectl k' >> /etc/profile

            # Install Kubectx & Kubens"
            git clone https://github.com/ahmetb/kubectx /opt/kubectx
            ln -s /opt/kubectx/kubens /usr/local/bin/kubens
            ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx

            # Install Kubeps
            git clone https://github.com/jonmosco/kube-ps1.git /root/kube-ps1
            cat <<"EOT" >> /root/.bash_profile
            source /root/kube-ps1/kube-ps1.sh
            function get_cluster_short() {
              echo "$1" | cut -d . -f1
            }
            KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
            KUBE_PS1_SUFFIX=') '
            PS1='$(kube_ps1)'$PS1
            EOT
            kubectl config rename-context "kubernetes-admin@kubernetes" "HomeLab"


  EC22:
    Type: AWS::EC2::Instance
    DependsOn: EC21
    Properties:
      InstanceType: !Ref MyInstanceType
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyName
      Tags:
        - Key: Name
          Value: k8s-w1
      NetworkInterfaces:
        - NetworkInterfaceId: !Ref EC22ENI1
          DeviceIndex: '0'
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp3
            VolumeSize: 30
            DeleteOnTermination: true
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash
            hostnamectl --static set-hostname k8s-w1

            # Config convenience
            echo 'alias vi=vim' >> /etc/profile
            ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

            # Disable ufw & AppArmor
            systemctl stop ufw && systemctl disable ufw
            systemctl stop apparmor && systemctl disable apparmor
            
            #  ubuntu user -> root user
            echo "sudo su -" >> /home/ubuntu/.bashrc

            # Setting Local DNS Using Hosts file
            echo "192.168.10.10 k8s-m" >> /etc/hosts
            echo "192.168.10.101 k8s-w1" >> /etc/hosts
            echo "192.168.10.102 k8s-w2" >> /etc/hosts
            echo "192.168.20.100 k8s-w0" >> /etc/hosts

            # add kubernetes repo
            curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
            echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list

            # add docker-ce repo with containerd
            curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
            echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

            # packets traversing the bridge are processed by iptables for filtering
            echo 1 > /proc/sys/net/ipv4/ip_forward
            # enable br_filter for iptables 
            modprobe br_netfilter

            # Update the apt package index, install kubelet, kubeadm and kubectl, and pin their version
            apt update && apt-get install -y kubelet kubectl kubeadm containerd.io && apt-mark hold kubelet kubeadm kubectl

            # containerd configure to default and cgroup managed by systemd 
            containerd config default > /etc/containerd/config.toml
            sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

            # avoid WARN&ERRO(default endpoints) when crictl run  
            cat <<EOF > /etc/crictl.yaml
            runtime-endpoint: unix:///run/containerd/containerd.sock
            image-endpoint: unix:///run/containerd/containerd.sock
            EOF

            # ready to install for k8s 
            systemctl restart containerd && systemctl enable containerd
            systemctl enable --now kubelet

            # Install packages
            apt install tree jq bridge-utils net-tools conntrack ipset wireguard -y

            # k8s Controlplane Join - API Server 192.168.10.10" 
            kubeadm join --token 123456.1234567890123456 --discovery-token-unsafe-skip-ca-verification 192.168.10.10:6443



  EC23:
    Type: AWS::EC2::Instance
    DependsOn: EC21
    Properties:
      InstanceType: !Ref MyInstanceType
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyName
      Tags:
        - Key: Name
          Value: k8s-w2
      NetworkInterfaces:
        - NetworkInterfaceId: !Ref EC23ENI1
          DeviceIndex: '0'
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp3
            VolumeSize: 30
            DeleteOnTermination: true
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash
            hostnamectl --static set-hostname k8s-w2

            # Config convenience
            echo 'alias vi=vim' >> /etc/profile
            ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

            # Disable ufw & AppArmor
            systemctl stop ufw && systemctl disable ufw
            systemctl stop apparmor && systemctl disable apparmor
            
            #  ubuntu user -> root user
            echo "sudo su -" >> /home/ubuntu/.bashrc

            # Setting Local DNS Using Hosts file
            echo "192.168.10.10 k8s-m" >> /etc/hosts
            echo "192.168.10.101 k8s-w1" >> /etc/hosts
            echo "192.168.10.102 k8s-w2" >> /etc/hosts
            echo "192.168.20.100 k8s-w0" >> /etc/hosts

            # add kubernetes repo
            curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
            echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list

            # add docker-ce repo with containerd
            curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
            echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

            # packets traversing the bridge are processed by iptables for filtering
            echo 1 > /proc/sys/net/ipv4/ip_forward
            # enable br_filter for iptables 
            modprobe br_netfilter

            # Update the apt package index, install kubelet, kubeadm and kubectl, and pin their version
            apt update && apt-get install -y kubelet kubectl kubeadm containerd.io && apt-mark hold kubelet kubeadm kubectl

            # containerd configure to default and cgroup managed by systemd 
            containerd config default > /etc/containerd/config.toml
            sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

            # avoid WARN&ERRO(default endpoints) when crictl run  
            cat <<EOF > /etc/crictl.yaml
            runtime-endpoint: unix:///run/containerd/containerd.sock
            image-endpoint: unix:///run/containerd/containerd.sock
            EOF

            # ready to install for k8s 
            systemctl restart containerd && systemctl enable containerd
            systemctl enable --now kubelet

            # Install packages
            apt install tree jq bridge-utils net-tools conntrack ipset wireguard -y

            # k8s Controlplane Join - API Server 192.168.10.10" 
            kubeadm join --token 123456.1234567890123456 --discovery-token-unsafe-skip-ca-verification 192.168.10.10:6443


  EC24:
    Type: AWS::EC2::Instance
    DependsOn: EC21
    Properties:
      InstanceType: !Ref MyInstanceType
      ImageId: !Ref LatestAmiId
      KeyName: !Ref KeyName
      Tags:
        - Key: Name
          Value: k8s-w0
      NetworkInterfaces:
        - NetworkInterfaceId: !Ref EC24ENI1
          DeviceIndex: '0'
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: gp3
            VolumeSize: 30
            DeleteOnTermination: true
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash
            hostnamectl --static set-hostname k8s-w0

            # Config convenience
            echo 'alias vi=vim' >> /etc/profile
            ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime

            # Disable ufw & AppArmor
            systemctl stop ufw && systemctl disable ufw
            systemctl stop apparmor && systemctl disable apparmor
            
            #  ubuntu user -> root user
            echo "sudo su -" >> /home/ubuntu/.bashrc

            # Setting Local DNS Using Hosts file
            echo "192.168.10.10 k8s-m" >> /etc/hosts
            echo "192.168.10.101 k8s-w1" >> /etc/hosts
            echo "192.168.10.102 k8s-w2" >> /etc/hosts
            echo "192.168.20.100 k8s-w0" >> /etc/hosts

            # add kubernetes repo
            curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
            echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list

            # add docker-ce repo with containerd
            curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
            echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

            # packets traversing the bridge are processed by iptables for filtering
            echo 1 > /proc/sys/net/ipv4/ip_forward
            # enable br_filter for iptables 
            modprobe br_netfilter

            # Update the apt package index, install kubelet, kubeadm and kubectl, and pin their version
            apt update && apt-get install -y kubelet kubectl kubeadm containerd.io && apt-mark hold kubelet kubeadm kubectl

            # containerd configure to default and cgroup managed by systemd 
            containerd config default > /etc/containerd/config.toml
            sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

            # avoid WARN&ERRO(default endpoints) when crictl run  
            cat <<EOF > /etc/crictl.yaml
            runtime-endpoint: unix:///run/containerd/containerd.sock
            image-endpoint: unix:///run/containerd/containerd.sock
            EOF

            # ready to install for k8s 
            systemctl restart containerd && systemctl enable containerd
            systemctl enable --now kubelet

            # Install packages
            apt install tree jq bridge-utils net-tools conntrack ipset wireguard -y

            # k8s Controlplane Join - API Server 192.168.10.10" 
            kubeadm join --token 123456.1234567890123456 --discovery-token-unsafe-skip-ca-verification 192.168.10.10:6443


Outputs:
  Serverhost:
    Value: !GetAtt EC21.PublicIp

 

[ More about Calico ]
(참고) Calico CNI v3.28.1 설치 - Install , Release , IP pool(subnet)

더보기

calico 설치 전과 후 kube-system namespace 내부에 존재하는 coreDnscalico-node-xxxxx pods를 확인할 것!!

# 모니터링
watch -d 'kubectl get pod -A -owide'

# calico cni install
## kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml - 서브넷 24bit 추가
# 기본 yaml 에 4946줄 이동 후 아래 내용 추가 해둠
vi calico.yaml
...
            # Block size to use for the IPv4 POOL created at startup. Block size for IPv4 should be in the range 20-32. default 24
            - name: CALICO_IPV4POOL_BLOCK_SIZE
              value: "24"
kubectl apply -f https://raw.githubusercontent.com/gasida/KANS/main/kans3/calico-kans.yaml

#
tree /opt/cni/bin/
ls -l /opt/cni/bin/
ip -c route
ip -c addr
iptables -t filter -L
iptables -t nat -L
iptables -t filter -L | wc -l
iptables -t nat -L | wc -l

# calicoctl install
curl -L https://github.com/projectcalico/calico/releases/download/v3.28.1/calicoctl-linux-amd64 -o calicoctl
chmod +x calicoctl && mv calicoctl /usr/bin
calicoctl version

# CNI 설치 후 파드 상태 확인
kubectl get pod -A -o wide

 

 [ 실행 결과 - 한 눈에 보기 ]

[ calico 설치 전 상태확인 및 설치 yaml 주요 수정부분 ]

 

[ calico module 설치 Before and After ]
[ calicoctl 명령어 설치 ]

 

 

▶ kube-ops-view  ( 주의 : connection 에서 메모리 누수 현상있는 것으로 확인되어 현업에서는 사용하지 말자!! )

더보기
[ 설치 후, 실행 화면 ]

 

# helm show values geek-cookbook/kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system

# 설치 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : [실습환경 A Type]
echo -e "KUBE-OPS-VIEW URL = http://$(curl -s ipinfo.io/ip):30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://$(curl -s ipinfo.io/ip):30000/#scale=2"

# kube-ops-view 접속 URL 확인 (1.5 , 2 배율) : [실습환경 B Type]
echo -e "KUBE-OPS-VIEW URL = http://192.168.10.10:30000/#scale=1.5"
echo -e "KUBE-OPS-VIEW URL = http://192.168.10.10:30000/#scale=2"

# (참고) 삭제
helm uninstall -n kube-system kube-ops-view

▶  metrics-server

더보기
# metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system

kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
kubectl get apiservices |egrep '(AVAILABLE|metrics)'

# 확인
kubectl top node
kubectl top pod -A --sort-by='cpu'
kubectl top pod -A --sort-by='memory'

# (참고) 삭제
helm uninstall -n kube-system metrics-server

 

 [ 실행 결과 - 한 눈에 보기 ]

 

1-4. Calico 구성요소 확인

▷ BGP or 각 노드 그룹에서 광고 된 routing 정보를 Bird가 catch 하고, 이를 Felix 가 Calico store 에 iptables 정보와 함께
    저장하게 된다. 이를 기반으로 각 노드 내 POD 의 통신이 가능하게 된다. 
 
Calico IPAM 이란?
 - Calico는 설치시 따로 설정을 변경하지 않는 이상, Default로 IPAM 플러그을 사용한다.
 - 기본적으로 K8s의 Pod CIDR과 매칭되는 하나의 IP Pool을 사용하지만, 필요한 경우 Pod CIDR을 쪼개서 여러개의 IP Pool을 구성하여
   이를 node, namespace, pod annotation에 따라 선택할 수 있는 기능을 제공한다.
 
♣ Calico IPAM Block 이란?
 - IP Pool을 Block이라는 소단위로 쪼개어 Node에 분배하며, 1개의 NodeN개의 Block을 소유할 수 있다. ( Nodes: PODs = 1: N )
 - Calico IP분배는 기존 Block에 우선 IP를 할당 후, 여분이 없을 경우 다른 Block의 IP를 할당하게 된다. ( 라우팅 절약효과 )
 - Block 크기는 기본적으로 64개의 주소(/26)를 가질 수 있으며, 이는 IPPool 리소스를 수정함으로 변경 가능하다.
 
♣ Host-local IPAM 이란?
 - 정적인 CIDR을 Node마다 미리 할당한 뒤 해당 범위 안에서만 IP 주소를 할당하는 방식으로 동작한다.
 - Immutable 특성에 의해 한 번 설정 후 변경 불가하며, 가용한 IP가 떨어지면 추가 Pod 생성이 불가하다.
 - Calico에서 사용은 가능하지만, per-node, per-pod, and per-namespace 의 IP 할당이 불가능하여 사용상 제약으로 권장하지 않는다.
 
Ref. Link - https://docs.tigera.io/archive/v3.17/networking/get-started-ip-addresses

더보기

[ 실습 시나리오 ]

1)  데몬셋으로 각 노드에 calico-node 파드가 동작하여, 해당 파드에 bird, felix, confd 등이 동작

2)  Calico 컨트롤러 파드 디플로이먼트로 생성


[중요] 명령어 기억해 놓기 !!

# calicoctl get ippool –o wide

# calicoctl get workloadEndpoint    또는

# calicoctl get wep -A

 


# 버전 확인 - 링크
## kdd 의미는 쿠버네티스 API 를 데이터저장소로 사용 : k8s API datastore(kdd)
calicoctl version

# calico 관련 정보 확인
kubectl get daemonset -n kube-system
kubectl get pod -n kube-system -l k8s-app=calico-node -owide

kubectl get deploy -n kube-system calico-kube-controllers
kubectl get pod -n kube-system -l k8s-app=calico-kube-controllers -owide

# 칼리코 IPAM 정보 확인 : 칼리코 CNI 를 사용한 파드가 생성된 노드에 podCIDR 네트워크 대역 확인 - 링크
calicoctl ipam show

# Block 는 각 노드에 할당된 podCIDR 정보
calicoctl ipam show --show-blocks
calicoctl ipam show --show-borrowed
calicoctl ipam show --show-configuration


Quiz. 바로 위 Calico IPAM 과 아래 출력되는 IPAM 의 차이는 무엇일까요? 우선순위 확인 - 링크 , Flannel 과 차이
- 별도의 IPAM 이 있다면 무엇을 할 수 있을까요? (예. 특정 파드/네임스페이스에 파드의 네트워크 대역을 추가 및 삭제)
1. Kubernetes annotations
2. CNI configuration (Calico IPAM)
3. IP pool node selectors

# host-local IPAM 정보 확인 : k8s-m 노드의 podCIDR 은 host-local 대신 칼리코 IPAM 를 사용함
## 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' ;echo
kubectl get node k8s-m -o json | jq '.spec.podCIDR'

# CNI Plugin 정보 확인 - 링크
tree /etc/cni/net.d/
cat /etc/cni/net.d/10-calico.conflist | jq
...
			"datastore_type": "kubernetes", # 칼리코 데이터저장소는 쿠버네티스 API 를 사용
      "ipam": { 
          "type": "calico-ipam" # IPAM 은 칼리코 자체 IPAM 을 사용
      },
...

# calicoctl node 정보 확인 : Bird 데몬(BGP)을 통한 BGP 네이버 연결 정보(bgp peer 는 노드의 IP로 연결) - 링크
calicoctl node status
calicoctl node checksystem

# ippool 정보 확인 : 클러스터가 사용하는 IP 대역 정보와 칼리코 모드 정보 확인
calicoctl get ippool -o wide

# 파드와 서비스 사용 네트워크 대역 정보 확인 
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
        "--service-cluster-ip-range=10.200.1.0/24",
        "--cluster-cidr=172.16.0.0/16",

kubectl get cm -n kube-system kubeadm-config -oyaml | grep -i subnet
 podSubnet: 172.16.0.0/16
 serviceSubnet: 10.96.0.0/12

# calico endpoint (파드)의 정보 확인 : WORKLOAD 는 파드 이름이며, 어떤 노드에 배포되었고 IP 와 cali 인터페이스와 연결됨을 확인
calicoctl get workloadEndpoint
calicoctl get workloadEndpoint -A
calicoctl get workloadEndpoint -o wide -A

# 노드에서 컨테이너(프로세스) 확인 : pstree
ps axf
   4405 ?        Sl     0:09 /usr/bin/containerd-shim-runc-v2 -namespace k8s.io -id dd532e5efaad436ebe7d10cdd3bb2ffe5a2873a0604ce3b
   4425 ?        Ss     0:00  \_ /pause
   4740 ?        Ss     0:00  \_ /usr/local/bin/runsvdir -P /etc/service/enabled
   4811 ?        Ss     0:00      \_ runsv allocate-tunnel-addrs
   4819 ?        Sl     0:00      |   \_ calico-node -allocate-tunnel-addrs
   4812 ?        Ss     0:00      \_ runsv bird
   4994 ?        S      0:00      |   \_ bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg
   4813 ?        Ss     0:00      \_ runsv cni
   4822 ?        Sl     0:00      |   \_ calico-node -monitor-token
   4814 ?        Ss     0:00      \_ runsv bird6
   4993 ?        S      0:00      |   \_ bird6 -R -s /var/run/calico/bird6.ctl -d -c /etc/calico/confd/config/bird6.cfg
   4815 ?        Ss     0:00      \_ runsv confd
   4820 ?        Sl     0:00      |   \_ calico-node -confd
   4816 ?        Ss     0:00      \_ runsv felix
   4824 ?        Sl     0:54      |   \_ calico-node -felix
   4817 ?        Ss     0:00      \_ runsv node-status-reporter
   4823 ?        Sl     0:00      |   \_ calico-node -status-reporter
   4818 ?        Ss     0:00      \_ runsv monitor-addresses
   4825 ?        Sl     0:00          \_ calico-node -monitor-addresses

 

 [ 실행 결과 - 한 눈에 보기 ]

[ calico 주요 component &lt;== "pause" container 로 부터 fork 됨 ]

(참고) 노드에 파드 1개 생성 시 iptables filter 에 추가되는 정책 (예시) : 파드가 추가되면 배수의 Rule 들이 추가된다.

# 파드별 iptables rule 확인
iptables -L -n -v
iptables -v --numeric --table filter --list cali-tw-<자신의 파드에 매핑되는 calice# 인터페이스 이름>
root@k8s-w1:~# iptables -v --numeric --table filter --list cali-tw-calice0906292e2
Chain cali-tw-calice0906292e2 (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:Kyvj-MeWlJ-7Y5Lk */ ctstate RELATED,ESTABLISHED
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:qwQkS1Qk5-0C_6fl */ ctstate INVALID
    0     0 MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:D2FNFV7seztTEwpC */ MARK and 0xfffeffff
    0     0 cali-pri-kns.default  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:BkzqwS83rjt9SfBo */
    0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:iu5U8w6JA_Ay43ur */ /* Return if profile accepted */ mark match 0x10000/0x10000
    0     0 cali-pri-ksa.default.default  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:-EjtVHKYUnymIIJr */
    0     0 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:mIgqs2n8E2V5NxVE */ /* Return if profile accepted */ mark match 0x10000/0x10000
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:IYUCq-2vz6E2hpfw */ /* Drop if no profiles matched */

 


 [ 실행 결과 - 한 눈에 보기 ]

 

(심화) calico-node 정보 상세 확인

더보기
# calico-node 컨테이너 확인
crictl ps | grep calico

# bird 프로세스 확인
ps -ef | grep bird.cfg
root@k8s-w1:~# ps -ef | grep bird.cfg
root        3094    2950  0 03:30 ?        00:00:01 bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg
root      131712   55657  0 06:37 pts/0    00:00:00 grep --color=auto bird.cfg

# bird.cfg 파일 확인
find / -name bird.cfg
cat /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/61/fs/etc/calico/confd/config/bird.cfg
function apply_communities ()
{
}

# Generated by confd
include "bird_aggr.cfg";
include "bird_ipam.cfg";

router id 192.168.100.101;

# Configure synchronization between routing tables and kernel.
protocol kernel {
  learn;             # Learn all alien routes from the kernel
  persist;           # Don't remove routes on bird shutdown
  scan time 2;       # Scan kernel routing table every 2 seconds
  import all;
  export filter calico_kernel_programming; # Default is export none
  graceful restart;  # Turn on graceful restart to reduce potential flaps in
                     # routes when reloading BIRD configuration.  With a full
                     # automatic mesh, there is no way to prevent BGP from
                     # flapping since multiple nodes update their BGP
                     # configuration at the same time, GR is not guaranteed to
                     # work correctly in this scenario.
  merge paths on;    # Allow export multipath routes (ECMP)
}

# Watch interface up/down events.
protocol device {
  debug { states };
  scan time 2;    # Scan interfaces every 2 seconds
}

protocol direct {
  debug { states };
  interface -"cali*", -"kube-ipvs*", "*"; # Exclude cali* and kube-ipvs* but
                                          # include everything else.  In
                                          # IPVS-mode, kube-proxy creates a
                                          # kube-ipvs0 interface. We exclude
                                          # kube-ipvs0 because this interface
                                          # gets an address for every in use
                                          # cluster IP. We use static routes
                                          # for when we legitimately want to
                                          # export cluster IPs.
}


# Template for all BGP clients
template bgp bgp_template {
  debug { states };
  description "Connection to BGP peer";
  local as 64512;
  multihop;
  gateway recursive; # This should be the default, but just in case.
  import all;        # Import all routes, since we don't know what the upstream
                     # topology is and therefore have to trust the ToR/RR.
  export filter calico_export_to_bgp_peers;  # Only want to export routes for workloads.
  add paths on;
  graceful restart;  # See comment in kernel section about graceful restart.
  connect delay time 2;
  connect retry time 5;
  error wait time 5,30;
}

# ------------- Node-to-node mesh -------------

# For peer /host/k8s-m/ip_addr_v4
protocol bgp Mesh_192_168_100_10 from bgp_template {
  neighbor 192.168.100.10 as 64512;
  source address 192.168.100.101;  # The local address we use for the TCP connection
}

# For peer /host/k8s-w1/ip_addr_v4
# Skipping ourselves (192.168.100.101)

# For peer /host/k8s-w2/ip_addr_v4
protocol bgp Mesh_192_168_100_102 from bgp_template {
  neighbor 192.168.100.102 as 64512;
  source address 192.168.100.101;  # The local address we use for the TCP connection
  passive on; # Mesh is unidirectional, peer will connect to us.
}

# For peer /host/k8s-w3/ip_addr_v4
protocol bgp Mesh_192_168_100_103 from bgp_template {
  neighbor 192.168.100.103 as 64512;
  source address 192.168.100.101;  # The local address we use for the TCP connection
  passive on; # Mesh is unidirectional, peer will connect to us.
}

# ------------- Global peers -------------
# No global peers configured.


# ------------- Node-specific peers -------------

# No node-specific peers configured.

(심화) calico-node 의 bird 라우팅 정보 확인(birdcl) - 링크
  ☞ calico-node 에 bird 데몬에 별도의 cli 입력 툴을 제공합니다 → birdcl

더보기
# calico-node 정보 확인 : 컨테이너 네트워크는 host 모드를 사용하여 해당 노드의 네트워크를 사용
kubectl get pod -n kube-system -l k8s-app=calico-node -o wide
kubectl get pod -n kube-system -l k8s-app=calico-node -oname
 
# 각 파드에 exec 로 birdcl 에 정보 확인
for pod in $(kubectl get pod -n kube-system -l k8s-app=calico-node -oname); do echo $pod; kubectl exec $pod -n kube-system -- birdcl show route; echo; done
for pod in $(kubectl get pod -n kube-system -l k8s-app=calico-node -oname); do echo $pod; kubectl exec $pod -n kube-system -- birdcl show route all; echo; done
for pod in $(kubectl get pod -n kube-system -l k8s-app=calico-node -oname); do echo $pod; kubectl exec $pod -n kube-system -- birdcl show status; echo; done

#------------------------------------------
# (옵션) 직접 파드 이름 지정 후 birdcl 로 접속 후 정보 확인
kubectl exec -it $CALICONODE1 -n kube-system -- birdcl
root@k8s-m:~# kubectl exec -it calico-node-95tlj -n kube-system -- birdcl
Defaulted container "calico-node" out of: calico-node, upgrade-ipam (init), install-cni (init), flexvol-driver (init)
BIRD v0.3.3+birdv1.6.8 ready.
bird>

# 라우팅 정보 확인 : 다른 노드로부터 BGP 로 전달받은 네트워크 경로 정보를 아래 빨간색 부분!
show route
bird> show route
0.0.0.0/0          via 10.0.2.2 on enp0s3 [kernel1 04:18:24] * (10)
10.0.2.2/32        dev enp0s3 [kernel1 04:18:24] * (10)
192.168.100.0/24   dev enp0s8 [direct1 04:18:23] * (240)
10.0.2.0/24        dev enp0s3 [direct1 04:18:23] * (240)
172.16.228.64/26   blackhole [static1 04:18:23] * (200)
172.16.228.64/32   dev tunl0 [direct1 04:18:23] * (240)
172.16.197.0/26    via 192.168.100.103 on enp0s8 [Mesh_192_168_100_103 04:20:00] * (100/0) [i]
172.16.46.0/26     via 192.168.100.102 on enp0s8 [Mesh_192_168_100_102 04:19:04] * (100/0) [i]
172.16.29.0/26     via 192.168.100.10 on enp0s8 [Mesh_192_168_100_10 04:18:25] * (100/0) [i]
172.17.0.0/16      dev docker0 [direct1 04:18:23] * (240)

# 기타 확인 명령어
bird> show protocol
bird> show route all
bird> show status

# 빠져나가기
bird> exit

 [ 실행 결과 - 한 눈에 보기 ] 

▼ bird Node 에 접속하여, 'birdcl' command 를 사용하여 protocol 및 route 정보 등을 찾아 볼 수 있다.

 

 


2. POD <-> POD 간 통신

2-1. 최종 통신 흐름

☞ 동일 노드 내의 POD 간 통신은 외부 경로를 타지 않고 직접 통신함 

 
 
iptables FORWARD Rule 에 정책(허용) 사용
calice# 인터페이스에 proxy arp 설정으로 파드에서 바라보는
   게이트웨이의 MAC 정보를 파드가 전달 받음
♣ 동일 노드 내에 파드 간 통신에서는 tunnel 인터페이스는 관여하지 않음
 
Ref. Link : About Proxy ARP
 
 
 
 
 
 
 

2-2. 파드 배포 전 기본상태 확인

▼ 기본 상태

[ worker node #1 에는 기본 POD 이외 서비스 POD 가 없음을 확인 ]

 

 
 
▼ POD 생성 전 노드(k8s-w1) Shell 에서 기본 정보 확인
 
step1 . 네트워크 인터페이스 정보를 확인하자!!

# 네트워크 인터페이스 정보 확인 : 터널(ipip) 인터페이스가 존재!
ip -c -d addr show tunl0
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0 promiscuity 0 minmtu 0 maxmtu 0
    ipip any remote any local any ttl inherit nopmtudisc numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 172.16.158.0/32 scope global tunl0
       valid_lft forever preferred_lft forever

 

step2. 네트워크 네임스페이스 확인

lsns -t net

 
step3. 네트워크 라우팅 경로 정보 확인
☞ 이중 bird 는 bird 데몬이 BGP 라우팅 프로토콜에 의해 파드 네트워크 대역을 전달받거나 전달하는 경로 → 각 노드의 파드 대역

ip -c route | grep bird
blackhole 172.16.158.0/24 proto bird
172.16.34.0/24 via 192.168.20.100 dev tunl0 proto bird onlink
172.16.116.0/24 via 192.168.10.10 dev tunl0 proto bird onlink
172.16.184.0/24 via 192.168.10.102 dev tunl0 proto bird onlink

 
[ Simple Common-Sense ]
- routing table 에 blackhole 라우트가 잡혀 있지 않다면,
라우팅 참조 오류로 내부에서 무한 looping에 빠지게 됩니다.
하여, 안전한 통신을 위해 기본적으로 blackhole 라우트를 잡습니다.
 
 

# 아래 tunl0 Iface 에 목적지 네트워크 대역은 ipip 인캡슐레이션에 의해서 각 노드에 전달됩니다 → 각각 노드의 파드 대역입니다
route -n
root@k8s-w1:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.34.0     192.168.20.100  255.255.255.0   UG    0      0        0 tunl0
172.16.116.0    192.168.10.10   255.255.255.0   UG    0      0        0 tunl0
172.16.184.0    192.168.10.102  255.255.255.0   UG    0      0        0 tunl0
172.16.158.0    0.0.0.0         255.255.255.0   U     0      0        0 *

 

 
step 4. IPtables 확인

# (옵션) iptables rule 갯수 확인 >> iptables rule 이 모든 노드에서 똑같나요? 다른가요?
iptables -t filter -S | grep cali | wc -l
iptables -t nat -S | grep cali | wc -l

 

<< 참고 >> 하기 마스터 노드에는 기본 환경구성에서 calico node 설치에 의해 추가적인 라우팅이 잡혀있다.

 

 


2-3. 파드 배포 후 상태 확인

Step 1. 노드(k8s-w1)에 파드 2개 생성

더보기

▶ node1-pod2.yaml : 노드이름(nodeName)은 kubectl get node 출력되는 Name 을 사용하시면 됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  nodeName: k8s-w1  # 노드의 호스트이름을 직접 지정했습니다
  containers:
  - name: pod1
    image: nicolaka/netshoot  # 이미지는 네트워크 장애 처리에 유용한 이미지를 사용합니다
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
spec:
  nodeName: k8s-w1
  containers:
  - name: pod2
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0

 

▶ POD 생성 후, 확인 ( 예상 구성도 )

 ▼ 모니터링 환경 구성 및 정보 확인

# [터미널1] k8s-m 모니터링
watch -d calicoctl get workloadEndpoint   ## k8s-w1 에서 수행
watch -d route -n  

# [터미널2] k8s-m 모니터링
# 파드 생성 : 노드의 이름이 다를 경우 아래 yaml 파일 다운로드 후 수정해서 사용하시면 됩니다
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod2.yaml
kubectl apply -f node1-pod2.yaml

# 생성된 파드 정보 확인
kubectl get pod -o wide
root@k8s-m:~# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE     NOMINATED NODE   READINESS GATES
pod1   1/1     Running   0          34s   172.16.228.78   k8s-w1   <none>           <none>
pod2   1/1     Running   0          34s   172.16.228.79   k8s-w1   <none>           <none>

# calicoctl 이용한 endpoint 확인 : veth 정보도 출력!
calicoctl get workloadendpoints
WORKLOAD   NODE     NETWORKS          INTERFACE
pod1       k8s-w1   172.16.158.2/32   calice0906292e2
pod2       k8s-w1   172.16.158.1/32   calibd2348b4f67

 

▶ 파드 생성 된 이후 다시 정보 확인

# 네트워크 인터페이스 정보 확인 : calice#~ 2개 추가됨!, 각각 net ns(네임스페이스) 0, 1로 호스트와 구별됨
ip -c link
root@k8s-w1:~# ip -c link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:70:da:68:71:43 brd ff:ff:ff:ff:ff:ff
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:1b:e3:db brd ff:ff:ff:ff:ff:ff
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:34:f0:2c:88 brd ff:ff:ff:ff:ff:ff
5: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
8: calibd2348b4f67@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid cni-348afd47-fece-7c08-4488-9046da1e6139
9: calice0906292e2@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid cni-3342c988-81a5-ebfa-058d-193dbbcb1d81

# 네트워크 네임스페이스 확인 : 아래 2개 pause(infra 컨테이너)가 각각 파드별로 생성됨 - 위 link-netnsid 0, link-netnsid 1 매칭됨
lsns -t net
root@k8s-w1:~# lsns -t net
        NS TYPE NPROCS   PID USER     NETNSID NSFS                                                COMMAND
4026531840 net     131     1 root  unassigned                                                     /sbin/init
4026532216 net       2 31336 65535          0 /run/netns/cni-348afd47-fece-7c08-4488-9046da1e6139 /pause
4026532277 net       2 31372 65535          1 /run/netns/cni-3342c988-81a5-ebfa-058d-193dbbcb1d81 /pause

# 파드의 IP/32bit 호스트 라우팅 대역이 라우팅 테이블에 추가됨
ip -c route
root@k8s-w1:~# ip -c route
172.16.158.1 dev calibd2348b4f67 scope link
172.16.158.2 dev calice0906292e2 scope link
...

# (옵션) iptables rule 갯수 확인 : 필터(filter)에 기본 39 에서 91개로 룰(rule)이 많이 추가 되었다 >> 파드가 많아지면 rule 갯수는? >> 노드별로 똑같나요? 다른가요?
root@k8s-w1:~# iptables -t filter -S | grep cali | wc -l
91
root@k8s-w1:~# iptables -t nat -S | grep cali | wc -l
15

 

[ 실행결과 - 한 눈에 보기]

 
Step 2. 파드에 Shell 접속 후 확인
▶ POD1 쉘 에 접속하여 확인

더보기
# 마스터 노드에서 아래 실행
kubectl exec pod1 -it -- zsh
...
# 아래 처럼 호스트 네트워크 인터페이스 9번인 caliceY와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod1> ip -c addr
...
4: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
    link/ether ee:2f:83:31:9b:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.16.158.2/32 brd 172.16.158.2 scope global eth0
       valid_lft forever preferred_lft forever

# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod1> route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

# ARP 정보는 현재 아무것도 없다
ip -c neigh

▶ POD2 쉘 에 접속하여 확인

더보기
# 마스터 노드에서 아래 실행
kubectl exec pod2 -it -- zsh
...
# 아래 처럼 if12 는 호스트 네트워크 인터페이스 12번인 calice0906292e2와 veth 연결되어 있다
# IP는 32bit 서브넷을 가진다
pod2> ip -c addr
...
4: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
    link/ether 7e:61:c6:fe:4f:8f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.16.228.79/32 brd 172.16.228.79 scope global eth0
       valid_lft forever preferred_lft forever

# 라우팅 정보에 169.254.1.1 를 디폴트 게이트웨이 주소로 사용
pod2> route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

# ARP 정보는 현재 아무것도 없다
ip neighbor show

[ 실행 결과 - 한 눈에 보기 ]

 
♣ (심화) 참조 자료

더보기

1. (심화) 169.254.1.1 은 어떤 IP 일까? - 링크

  - 호스트에 GW IP(169.254.1.1)를 추가할 필요 없이 Proxy ARP에 의해 169.254.1.1 에 대해서 응답할 수 있습니다.

  - Calico tries hard to avoid interfering with any other configuration on the host. Rather than adding the gateway address to the host side of each workload interface, Calico sets the proxy_arp flag on the interface. This makes the host behave like a gateway, responding to ARPs for 169.254.1.1 without having to actually allocate the IP address to the interface.

 

2.  (심화) 32bit 호스트 Route 특징 - 링크

 - 32bit는 호스트 Route로 특징 중 하나가다른 네트웍 대역Gateway로 잡을 수 있습니다. 32bit로 구성된 라우터 인터페이스끼리는 어떠한 IP를 주더라도 Neighbor가 될 수 있거든요. 많이 사용하는 경우Unmumbered로 구성하면 /32가 되는데, 그 경우 다른 네트웍끼리 라우팅 프로토콜도 돌고 통신도 되는 이유죠.

 

3. (심화) Calico 통신 정리

 - 독커노인님 정리자료 ( PDF link )

 - Calico 는 169.254.1.1 IP를 호스트에 설정하지 않고 Proxy ARP 로 처리!, 물론 목적지 라우팅을 처리할 수 있는 라우팅이 포함되어 있어야함!

 - 노드에 할당된 podCIDR(32bit 다수 광고 방지!) 을 BGP 로 광고하고, 루핑 방지를 위해서 blackhole(null) 라우팅을 추가한다.

 - Proxy ARP 정보 Proxy ARP RFC 1027 - 링크 위키 소개 - 링크 KT 용어해설 - 링크 시스코 Proxy ARP 소개 - 링크

 

4. (심화) 모든 cali* 인터페이스가 동일한 맥 주소 ee:ee:ee:ee:ee:ee 를 사용하는가?

 - 일부 커널이 고정 맥 주소 생성이 안되서, 칼리코가 자체 맥을 생성한다. 또한 파드와 cali#(veth pair) 는 point-to-point routed interfaces 를 사용하기 때문에 호스트의 데이터링크 레이어(L2 Layer) 에 도달하지 않아서, 모든 cali# 가 동일한 맥 주소를 사용해도 문제가 없다

 - In some setups the kernel is unable to generate a persistent MAC address and so Calico assigns a MAC address itself. Since Calico uses point-to-point routed interfaces, traffic does not reach the data link layer so the MAC Address is never used and can therefore be the same for all the cali* interfaces.

 
 
▶ 다음 실습을 위해 파드 삭제 : kubectl delete -f node1-pod2.yaml


3. POD → 외부(Internet) 통신

3.1 최종 통신 흐름

☞ 파드에서 외부(인터넷) 통신 시, 해당 노드의 네트워크 인터페이스 IP 주소로 MASQUERADE(출발지 IP가 변경) 되어서 외부에 연결됨  

 
♣ calico 기본 설정은 natOutgoing: true 이다. 즉, iptables 에 MASQUERADE Rule(룰) 에 의해서 외부에 연결됨
♣  calice# 인터페이스에 proxy arp 설정
♣ 파드와 외부간 직접 통신에서는 tunnel 인터페이스는 미 관여
 
 
 
 
 
 
 
 
 

3-2. 파드 배포 전 기본상태 확인

▼ 기본 상태

더보기

 ▶ calico 설정 정보 확인 & 노드에 iptables 확인

# 마스터 노드에서 확인 : natOutgoing 의 기본값은 true 이다
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Always     Never       false      all()

# 노드에서 확인 : 노드에서 외부로 통신 시 MASQUERADE 동작 Rule 확인
iptables -n -t nat --list cali-nat-outgoing
root@k8s-w1:~# iptables -n -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
target      prot opt source               destination
MASQUERADE  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully

ipset list
ipset list cali40masq-ipam-pools

 

 [ 실행 결과 - 한 눈에 보기]

3-3. 파드 배포 후 상태 확인

Step 1. 노드(k8s-w1)에 파드 1개 생성
 

더보기

 node1-pod1.yaml : : 노드이름(nodeName)은 kubectl get node 출력되는 Name 을 사용하시면 됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  nodeName: k8s-w1
  containers:
  - name: pod1
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0

 

▶ POD 생성 후, 확인

# 파드 생성
curl -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node1-pod1.yaml
kubectl apply -f node1-pod1.yaml

# 생성된 파드 정보 확인
kubectl get pod -o wide

  

[ 실행 결과 - 한 눈에 보기 ]

 

[ k8s-w1 노드의 POD1 에서 Internet 통신 시, packet 이 흐르는 경로 요약도 ]

 
Step 2. 외부 통신 확인
▼ [AWS EC2 k8s-w1] 혹은 [vagrant ssh k8s-w1] 노드 접속 : 통신 전 준비

더보기
# 노드에서 실행
# iptables NAT MASQUERADE 모니터링 : pkts 증가 확인
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'

# (컨트롤플레인 노드) 파드 연결된 veth 를 변수를 확인
VETH1=$(calicoctl get workloadEndpoint | grep pod1 | awk '{print $4}')
echo $VETH1

# (워커노드1) 위에서 확인한 파드 연결된 veth 를 변수에 지정
VETH1=<각자 실습 환경에 따라 다름>
VETH1=calice0906292e2

# 패킷 덤프 실행 >> 어떤 인터페이스에서 패킷 캡쳐 확인이 되나요?
tcpdump -i any -nn icmp
tcpdump -i $VETH1 -nn icmp
tcpdump -i tunl0 -nn icmp
tcpdump -i ens5 -nn icmp    # [실습환경 A Type]
tcpdump -i enp0s8 -nn icmp  # [실습환경 B Type]
tcpdump -i enp0s3 -nn icmp  # [실습환경 B Type]

 
▼  외부 통신 실행 및 확인

더보기
# 파드에서 외부 정상 통신 확인
kubectl exec pod1 -it -- zsh
----------------------------

# 혹은 통신 확인 
pod1> ping -c 10 8.8.8.8

# The right way to check the weather - 링크
curl wttr.in/seoul
curl 'wttr.in/seoul?format=3'
curl 'wttr.in/busan?format=3'
curl -s 'wttr.in/{London,Busan}'
curl v3.wttr.in/Seoul.sxl
curl wttr.in/Moon
curl wttr.in/:help

# 패킷 덤프 내용 확인
tcpdump -i <각자 실습 환경에 따라 다름> -nn icmp
root@k8s-w1:~# tcpdump -i calice0906292e2 -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on calice0906292e2, link-type EN10MB (Ethernet), capture size 262144 bytes
09:27:29.236878 IP 172.16.228.82 > 8.8.8.8: ICMP echo request, id 56376, seq 1, length 64
09:27:29.311810 IP 8.8.8.8 > 172.16.228.82: ICMP echo reply, id 56376, seq 1, length 64


# [실습환경 A Type] 아래 192.168.10.101는 노드의 외부(인터넷) 연결된 네트워크 인터페이스의 IP이며, 출발지 IP가 변경되어서 외부로 나감
root@k8s-w1:~# tcpdump -i ens5 -nn icmp
15:37:56.579286 IP 192.168.10.101 > 8.8.8.8: ICMP echo request, id 57122, seq 1, length 64
15:37:56.610585 IP 8.8.8.8 > 192.168.10.101: ICMP echo reply, id 57122, seq 1, length 64

# [실습환경 B Type] 아래 10.0.2.15는 VM의 1번 네트워크 인터페이스의 IP이며, 출발지 IP가 변경되어서 외부로 나감
root@k8s-w1:~# tcpdump -i enp0s3 -nn icmp
06:05:12.356260 IP 10.0.2.15 > 8.8.8.8: ICMP echo request, id 15671, seq 5, length 64
06:05:12.402586 IP 8.8.8.8 > 10.0.2.15: ICMP echo reply, id 15671, seq 5, length 64


# nat MASQUERADE rule 카운트(pkts)가 증가!
## 출발지 매칭은 cali40masq-ipam-pools 을 사용
watch -d 'iptables -n -v -t nat --list cali-nat-outgoing'
root@k8s-w1:~# iptables -n -v -t nat --list cali-nat-outgoing
Chain cali-nat-outgoing (1 references)
 pkts bytes target     prot opt in     out     source               destination
    3   252 MASQUERADE  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully

# IPSET 으로 의 cali40masq-ipam-pools IP 대역 정보 확인 : 172.16.0.0/16 대역임을 확인
ipset list cali40masq-ipam-pools
Name: cali40masq-ipam-pools
Type: hash:net
Revision: 7
Header: family inet hashsize 1024 maxelem 1048576 bucketsize 12 initval 0x97754149
Size in memory: 504
References: 1
Number of entries: 1
Members:
172.16.0.0/16

 

[ 실행 결과 - 한 눈에 보기 ]

 

 

 


4. 다른 노드에서 파드파드간 통신

4.1 최종 통신 흐름

☞  다른 노드 환경에서 파드 간 통신 시에는 IPIP 터널(기본값) 모드를 통해서 이루어 집니다. ( Overlay IP 기능사용 )

[ 동작 원리 ]
♣ 각 노드에 파드 네트워크 대역은 Bird 에 의해서 BGP 로 광고 전파/전달 되며, Felix 에 의해서 호스트의 라우팅 테이블에 자동으로
    추가 및 삭제 됩니다.
♣ 다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에 감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서 Outer 헤더를
    제거하고 내부의 파드와 통신됩니다 - 링크
 
[ 참조 - Packet_In_Packet 실제 분석 내용 ] 

더보기

 

1) Inner IP : POD 에 할당된 IP (172.16.0.0/16 ) 

2) Outer IP : Node 에 할당된 IP (192.168.0.0/16)

 

☞ 노드#1 에 있는 POD 에 할당된 IP 가 노드#2 에 있는 POD로 가기 위해,

[ Node#1 ]

POD#1    calice# → tunl0 (IPIP encapsulation) → eth0 으로 나가서,

 

[ Node#2 ]

eth0 → tunl0 ( IPIP decapsulation) → calice# → POD#2

으로 통신을 시도한다.

 

 

 

 

 

[ 주의 ] IPIP 프로토콜 (4) 사용으로 AWS 테스트 환경 구성 시, Security Group 의 Inbound 정책에 해당 프로토콜 허용을 해 주어야

           제대로 된 통신 테스트가 가능하다 !!

 

4.2 파드 배포 전 기본 상태 확인

▶ 노드간 BGP 로 Pod 대역 정보 전파 확인

[ Calico 기본 통신 구조 - component 의 역할 참조 ]

더보기

Step 1. 노드에서 BGP 에 의해서 전달 받은 정보가 호스트 라우팅 테이블에 존재하는지 확인

# 아래 명령어로 확인 시 나머지 노드들의 파드 대역을 자신의 호스트 라우팅 테이블에 가지고 있고, 해당 경로는 tunl0 인터페이스로 보내게 된다
route | head -2 ; route -n | grep tunl0

# 마스터노드, 노드1~
root@k8s-m:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 tunl0
172.16.228.64   192.168.100.101 255.255.255.192 UG    0      0        0 tunl0

# 노드1
root@k8s-w1:~# route | head -2 ; route -n | grep tunl0
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.29.0     192.168.100.10  255.255.255.192 UG    0      0        0 tunl0
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 tunl0
...

 

 Step 2. 노드의 tunl0 정보 확인

# 터널 인터페이스에 IP가 할당되어 있고, MTU는 1480(IP헤더 20바이트 추가를 고려)을 사용하며, 현재 TX/RX 카운트는 0 이다
# Calico 사용 시 파드의 인터페이스도 기본 MTU 1480 을 사용한다
ifconfig tunl0

# 노드1
root@k8s-w1:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

# 노드2
root@k8s-w2:~# ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.46.10  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 

[ 실행 결과 - 한 눈에 보기 ]

4.3 파드 배포

▶ node1 과 node2 에 POD 1개씩 배포하고 상태를 확인하자.

더보기

node2-pod2.yaml : 노드이름(nodeName)은 kubectl get node 출력되는 Name 을 사용하면 됨

▼apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  nodeName: k8s-w1
  containers:
  - name: pod1
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
spec:
  nodeName: k8s-w2
  containers:
  - name: pod2
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0

 

▼ 파드 생성 후, 정보 확인

# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/4/node2-pod2.yaml
kubectl apply -f node2-pod2.yaml

# calicoctl 이용한 endpoint 확인
calicoctl get workloadendpoints

 

▼ POD 생성 후, 상태

 

파드간 통신을 처리하는 라우팅 정보 확인 : 각각 상대방 노드의 IP를 게이트웨이로 전달하게 됩니다!

route -n | head -2 ; route -n | grep 172.16.

# 노드1
root@k8s-w1:~# route -n | head -2 ; route -n | grep 172.16.
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 tunl0
...

# 노드2
root@k8s-w2:~# route -n | head -2 ; route -n | grep 172.16.
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.228.64   192.168.100.101 255.255.255.192 UG    0      0        0 tunl0
...

[ 실행 결과 - 한 눈에 보기 ]

[ 노드 별 pod 생성 후, routing 정보를 통해 상대 POD 찾아가는 경로를 확인 할 수 있다!! ]

 

4.4 POD 간 통신 실행 및 확인

▶ 파드간 통신 실행 이해를 위한 준비 ⇒ IPIP(IP Protocol 4 허용이 필요, Azure 는 불가능 - Link
  - Calico in VXLAN mode is supported on Azure. However, IPIP packets are blocked by the Azure network fabric.


[ Tip & Tips - AWS Security Group 에 IP-in-IP (4) 허용 설정 ]

더보기

☞ AWS Console : EC2 > VM 선택 > Security > Security Group 선택 > Inbound 추가 > Custom Protocol 및 Protocol , Source 입력

 


 
 
☞ 지금까지 tunl0 인터페이스가 사용되지 않았는데, 다른 노드의 파드간 통신시에 사용되는지 확인을 해보자.

더보기

♣ 노드1 기준 설명

# tunl0 인터페이스 TX/RX 패킷 카운트 모니터링 실행
watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
root@k8s-w1:~# watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        RX packets 0  bytes 0 (0.0 B)
        TX packets 0  bytes 0 (0.0 B)

# (옵션) 패킷 덤프 : calice#
tcpdump -i -nn <calice#>

# 패킷 덤프 : tunl0
tcpdump -i tunl0 -nn
혹은 패킷을 파일로 추출시
tcpdump -i tunl0 -nn -w /tmp/calico-tunl0.pcap

# 패킷 덤프 : IP 헤더에 상위 프로토콜을 IPIP(4)인 패킷만 필터
#tcpdump -i <각자 자신의 노드의 eth0> -nn proto 4
tcpdump -i ens5 -nn proto 4    # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4  # [실습환경 B Type]
혹은 패킷을 파일로 추출시
tcpdump -i ens5 -nn proto 4 -w /tmp/calico-ipip.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 -w /tmp/calico-ipip.pcap # [실습환경 B Type]


# [실습환경 A Type] 자신의 PC로 패킷 복사
scp ubuntu@<node 유동공인 IP>:/tmp/calico-ipip.pcap .
scp ubuntu@3.35.229.92:/tmp/calico-ipip.pcap .


# [실습환경 B Type] 자신의 PC로 패킷 복사 : https://github.com/invernizzi/vagrant-scp
## vagrant scp 플러그인 설치
vagrant plugin install vagrant-scp
vagrant plugin list

## k8s-w1 VM 내부의 파일을 자신의 PC(호스트)에 복사
vagrant scp k8s-w1:/tmp/calico-ipip.pcap ./

▶ 파드1 → 파드2 ping 통신 및 확인

더보기
# 마스터 노드에서 pod1 Shell 접속
kubectl exec pod1 -it -- zsh

# pod1 에서 pod2 로 핑 통신 : 정상 통신!
ping -c 10 <pod2 IP>
pod1> ping -c 10 172.16.46.12
PING 172.16.46.12 (172.16.46.12) 56(84) bytes of data.
64 bytes from 172.16.46.12: icmp_seq=1 ttl=62 time=0.781 ms
64 bytes from 172.16.46.12: icmp_seq=2 ttl=62 time=0.803 ms
...

# tunl0 인터페이스 TX/RX 패킷 카운트 모니터링 확인 : TX/RX 패킷 카운트가 각각 10개로 증가했다
watch -d 'ifconfig tunl0 | head -2 ; ifconfig tunl0 | grep bytes'
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 172.16.228.76  netmask 255.255.255.255
        RX packets 10  bytes 840 (840.0 B)
        TX packets 10  bytes 840 (840.0 B)

# 패킷 덤프 : tunl0 - 터널 인터페이스에 파드간 IP 패킷 정보 확인!
tcpdump -i tunl0 -nn
root@k8s-w1:~# tcpdump -i tunl0 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tunl0, link-type RAW (Raw IP), capture size 262144 bytes
23:36:36.621795 IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 1, length 64
23:36:36.622470 IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 1, length 64
23:36:37.623274 IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 2, length 64
23:36:37.623977 IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 2, length 64
...

# 패킷 덤프 : eth0(enp#~) - IP Outer 헤더 안쪽에 IP 헤더 1개가 더 있음을 알 수 있다!
tcpdump -i ens5 -nn proto 4   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 # [실습환경 B Type]
root@k8s-w2:~# tcpdump -i ens5 -nn proto 4   # [실습환경 A Type]
root@k8s-w2:~# tcpdump -i enp0s8 -nn proto 4 # [실습환경 B Type]
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
23:36:36.625169 IP 192.168.100.101 > 192.168.100.102: IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 1, length 64 (ipip-proto-4)
23:36:36.625413 IP 192.168.100.102 > 192.168.100.101: IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 1, length 64 (ipip-proto-4)
23:36:37.626715 IP 192.168.100.101 > 192.168.100.102: IP 172.16.228.84 > 172.16.46.12: ICMP echo request, id 32298, seq 2, length 64 (ipip-proto-4)
23:36:37.626866 IP 192.168.100.102 > 192.168.100.101: IP 172.16.46.12 > 172.16.228.84: ICMP echo reply, id 32298, seq 2, length 64 (ipip-proto-4)
...

tcpdump -i ens5 -nn proto 4 -w /tmp/calico-ipip.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 -nn proto 4 -w /tmp/calico-ipip.pcap # [실습환경 B Type]
ls -l /tmp


# [실습환경 A Type] 자신의 PC로 패킷 복사
scp ubuntu@<node 유동공인 IP>:/tmp/calico-ipip.pcap .
scp ubuntu@3.35.229.92:/tmp/calico-ipip.pcap .


# [실습환경 B Type] 자신의 PC로 패킷 복사 : https://github.com/invernizzi/vagrant-scp
## vagrant scp 플러그인 설치
vagrant plugin install vagrant-scp
vagrant plugin list

## k8s-w1 VM 내부의 파일을 자신의 PC(호스트)에 복사
vagrant scp k8s-w1:/tmp/calico-ipip.pcap ./

[ Tip & Tips : Windows Vagrant SCP로 테스트 VM에서 생성한 pcap 파일 복사해 오기 ]

더보기

▶ 테스트 환경 설명 : Windows10 or 11Vagrant 로 Ubuntu 설치 후, AWS EC2 노드 생성 및 Cluster 환경 구성

 

해당 환경에서  AWS EC2에서 생성 한 calico-ipip.pcap 파일을 로컬호스트에 설치한 wire-shark로 분석하기 위해 파일을

가져오는 방법이 만만치 않아 글을 정리합니다. ( 더 쉽게 할 수 있는 방법 있으면 공유 부탁드립니다 ~ )

 

 

[ 실행 결과 - 한 눈에 보기]
1) tunl0 인터페이스 확인

 
2) ens5 인터페이스 확인

 
3) wire-shark 로 ens5 packet 분석 ( Outer-IP 헤더 / Inner-IP 헤더 확인 )

 


☞ 실습 종료 위해 POD 삭제 : kubectl delete -f node2-pod2.yaml
 


5. Calico 네트웍 모드

Calico 는 다양한 네트워크 통신 방법(모드)를 제공합니다.
CalicoCilium 에서 파드 혹은 네임스페이스의 레벨에서 IN/OUT 트래픽에 대한 통제가 가능합니다.
 

5-1. Calico 라우팅 모드 요약 (4가지)

 

1. IPIP 모드

 
♣ 다른 노드 간의 파드 통신은 tunl0 인터페이스를 통해 IP 헤더에
    감싸져서 상대측 노드로 도달 후 tunl0 인터페이스에서
    Outer 헤더를 제거하고 내부의 파드와 통신
♣ 다른 노드의 파드 대역은 BGP로 전달 받아 호스트 라우팅 테이블에
   업데이트됨
♣ Azure 네트워크에서는 IPIP 통신이 불가능하여 IPIP 모드 대신
   VXLAN 모드 사용
  : 멀티 캐스트, 브로드캐스트, IP-IP 캡슐화 패킷 및 GRE(일반 라우팅
     캡슐화) 패킷은 Azure VNet 내에서 차단됩니다 - 링크

 

1-1. 기본설정 확인

더보기

▶ calicoctl 및 네트워크 정보 확인

# 모드 정보 확인
calicoctl get ippool -o wide

# 노드(BGP) Peer 정보 확인
calicoctl node status
IPv4 BGP status
+----------------+-------------------+-------+----------+-------------+
|  PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+----------------+-------------------+-------+----------+-------------+
| 192.168.20.100 | node-to-node mesh | up    | 10:03:45 | Established |
| 192.168.10.101 | node-to-node mesh | up    | 10:05:26 | Established |
| 192.168.10.102 | node-to-node mesh | up    | 10:07:16 | Established |
+----------------+-------------------+-------+----------+-------------+

# BGP 로 전달 받은 파드 네트워크 대역이 호스트 라우팅 테이블에 적용되었는지 확인 
route -n | egrep '(Destination|tunl0)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.8.128    192.168.20.100  255.255.255.192 UG    0      0        0 tunl0
172.16.46.0     192.168.10.102  255.255.255.192 UG    0      0        0 tunl0
172.16.228.64   192.168.10.101  255.255.255.192 UG    0      0        0 tunl0

calicoctl ipam show --show-blocks
+----------+-----------------+-----------+------------+--------------+
| GROUPING |      CIDR       | IPS TOTAL | IPS IN USE |   IPS FREE   |
+----------+-----------------+-----------+------------+--------------+
| IP Pool  | 172.16.0.0/16   |     65536 | 6 (0%)     | 65530 (100%) |
| Block    | 172.16.116.0/24 |       256 | 4 (2%)     | 252 (98%)    |
| Block    | 172.16.158.0/24 |       256 | 1 (0%)     | 255 (100%)   |
| Block    | 172.16.34.0/24  |       256 | 1 (0%)     | 255 (100%)   |
+----------+-----------------+-----------+------------+--------------+

# 워커 노드마다 할당된 dedicated subnet (podCIDR) 확인
kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'

kubectl get node k8s-m -o json | jq '.spec.podCIDR'
"172.16.0.0/24"
kubectl get node k8s-w1 -o json | jq '.spec.podCIDR'
"172.16.1.0/24"
...

# 기본제공 
cat /etc/cni/net.d/10-calico.conflist
...
"ipam": {
          "type": "calico-ipam"
      },
...

# IPAM 우선순위 - 링크 링크2
1. Kubernetes annotations
2. CNI configuration << Calico IPAM
3. IP pool node selectors << host-local IPAM

# IPIP 인캡슐레이션 동작을 수행하는 터널 인터페이스 확인
ip -c -d addr show tunl0

1-2. 동작 확인

더보기

▶ node3-pod3.yaml : 동작 확인을 위한 파드

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: pod1
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
spec:
  containers:
  - name: pod2
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: pod3
spec:
  containers:
  - name: pod3
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0

 

▶ 파드 생성 및 패킷 캡쳐 확인

# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/5/node3-pod3.yaml
kubectl apply -f node3-pod3.yaml

# 파드 IP 정보 확인
kubectl get pod -o wide

# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

# 파드가 동작하는 워커노드1의 eth0(예시)에서 IPIP 패킷(proto 4) 덤프
tcpdump -i <eth0> -nn proto 4
tcpdump -i enp0s8 -nn proto 4
혹은 아래 처럼 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> proto 4 -w /tmp/calico-ipip.pcap
tcpdump -i enp0s8 proto 4 -w /tmp/calico-ipip.pcap

 

▶ 파드 삭제 : kubectl delete -f node3-pod3.yaml

 

 
2. Direct 모드 ( Not use of capsulation of packet )

 
♣ 파드 통신 패킷이 출발지 노드의 라우팅 정보를 보고 목적지 노드로
   원본 패킷 그대로 전달합니다
 
 
 
 
 
 
 

[ 다른 노드 간 POD 통신 - 내용정리 4-4.참조 ]

 
▶ 클라우드 사업자 네트워크의 경우 NIC 에 매칭되지 않는 IP 패킷은 차단되니, NIC에 Source/Destination Check 기능을
     Disable 해야 합니다 - 링크

더보기
# AWS CLI 로 특정 인스턴스의 Source/Destination Check 기능을 Disable 하기
aws ec2 modify-instance-attribute --instance-id <INSTANCE_ID> --source-dest-check "{\"Value\": false}"

 

▶ Virtualbox 설정 - 노드(VM)에 네트워크 NIC 2 번에 무작위 모드모두 허용 설정이 vagrant file 에 의해 되어 있음

더보기
# VVagrantfile 중 vnic 2번에 무작위 설정
v.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]

2-1. Direct 모드 설정 및 확인

더보기
# Calico 모드 정보 확인
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Always     Never       false      all()

# (옵션) 모니터링
watch -d "route -n | egrep '(Destination|UG)'"

# 설정
calicoctl get ippool default-ipv4-ippool -o yaml
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Always/ipipMode: Never/" | calicoctl apply -f -

# 모드 정보 확인 : IPIPMODE 가 Never 로 변경!
calicoctl get ippool -o wide
root@k8s-m:~/yaml# calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   Never      Never       false      all()

# BGP 로 전달 받은 파드 네트워크 대역이 호스트 라우팅 테이블에 적용되었는지 확인 : Iface 가 tunl0 에서 ens5 혹은 enp0s8 로 변경!
route -n | egrep '(Destination|UG)'
root@k8s-w1:~# route -n | egrep '(Destination|UG)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.29.0     192.168.100.10  255.255.255.192 UG    0      0        0 ens5 혹은 enp0s8
172.16.46.0     192.168.100.102 255.255.255.192 UG    0      0        0 ens5 혹은 enp0s8
172.16.197.0    192.168.100.103 255.255.255.192 UG    0      0        0 ens5 혹은 enp0s8

 [ 실행 결과 ]

2-2. 동작 확인
▶ 파드 생성

더보기
# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/NDKS/main/5/node3-pod3.yaml
kubectl apply -f node3-pod3.yaml

# 파드 IP 정보 확인
kubectl get pod -o wide
calicoctl get wep

▶ 파드간 ping 통신 실행 및 패킷 캡쳐 확인

더보기
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
tcpdump -i <eth0> -nn icmp
tcpdump -i ens5 -nn icmp   # [실습환경 A Type]
tcpdump -i enp0s8 -nn icmp # [실습환경 B Type]

# 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> icmp -w /tmp/calico-direct.pcap
tcpdump -i ens5 icmp -w /tmp/calico-direct.pcap   # [실습환경 A Type]
tcpdump -i enp0s8 icmp -w /tmp/calico-direct.pcap # [실습환경 B Type]

[ 실행 결과 - 한 눈에 보기 ]

 
▼ 참고 - 이해를 위한 자료

더보기

워커노드1(파드)워커노드2(파드) or 워커노드0(파드)통신 확인해보자! 되는 경우 어떻게 되는 걸까? 안되는 경우는 왜일까? ⇒ Overlay 네트워크 기법으로 라우팅 처리 가능 (힌트) k8s-w0 에서 route -n 확인 및 router 에서 tcpdump -i any icmp -nn 로 정보 확인해보자!

2-3. CrossSubnet 모드
☞ 노드 간 같은 네트워크 대역(Direct 모드로 동작) , 노드 간 다른 네트워크 대역(IPIP 모드로 동작)
▶ 설정 및 확인 ( hybrid 형식으로 동작 )

더보기
# CrossSubnet 모드 설정
calicoctl patch ippool default-ipv4-ippool -p '{"spec":{"ipipMode":"CrossSubnet"}}'

# 모드 확인
calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE      VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   true   CrossSubnet   Never       false      all()

# 파드 생성
kubectl apply -f node3-pod3.yaml
calicoctl get wep

# 호스트 라우팅 정보 확인
route -n | grep UG
root@ip-172-20-63-146:~# route -n | grep UG
100.105.79.128  172.20.61.184   255.255.255.192 UG    0      0        0 ens5  # 노드간 같은 네트워크 대역 - Direct 모드
100.125.78.64   172.20.59.153   255.255.255.192 UG    0      0        0 ens5  # 노드간 같은 네트워크 대역 - Direct 모드
100.127.64.128  172.20.64.181   255.255.255.192 UG    0      0        0 tunl0 # 노드간 다른 네트워크 대역 - IPIP 모드

# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

 

[ 실행 결과 - 한 눈에 보기 ]

 

 파드 삭제 : kubectl delete -f node3-pod3.yaml

 

3. VXLAN 모드 ( UDP 상단에 패킷 감싼다 )

기본 통신 방식
   - 노드와 노드 구간에서 POD 통신은 VXLAN 인캡슐레이션을 통해서
     이루어 집니다.
♣  다른 노드 간의 파드 통신은 vxlan 인터페이스를 통해 L2 프레임이
    UDP - VXLAN에 감싸져서 상대측 노드로 도달 후 vxlan 인터페이스
    에서 Outer 헤더를 제거하고 내부의 파드와 통신
♣  BGP 미사용, VXLAN L3 라우팅을 통해서 동작
♣  UDP 사용으로 Azure 네트워크에서도 사용 가능
 
 
 
3-1. VXLAN 모드 설정
▶ VXLAN 모드 적용

더보기
# VXLAN 설정 적용
kubectl apply -f https://raw.githubusercontent.com/gasida/KANS/main/3/calico-vxlan-kans.yaml

# 모드 설정 변경 : VXLAN 모드 사용 시, ipipMode 와 BGP(Bird)는 반드시 Never 비활성화 되어야 합니다
# ipipMode 현재 모드(Always, CrossSubnet)를 확인하고, 해당 모드를 Never 로 변경합니다
calicoctl get ippool default-ipv4-ippool -o wide
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: CrossSubnet/ipipMode: Never/" | calicoctl apply -f -
#calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Always/ipipMode: Never/" | calicoctl apply -f -
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/vxlanMode: Never/vxlanMode: Always/" | calicoctl apply -f -
calicoctl get ippool default-ipv4-ippool -o wide

# 변경 설정 적용을 위해서 calico 파드 삭제 후 재생성
kubectl delete pod -n kube-system -l k8s-app=calico-node

# 모든 노드에서 enp0s8 down 후 up (라우팅 테이블 갱신을 위함)
route -n && echo && ip link set enp0s8 down && sleep 1 && ip link set enp0s8 up && route -n

 

★ (정보) Calico VXLAN 설정 yaml 파일 수정 부분

# calico yaml 파일 다운로드
curl -s -O https://docs.projectcalico.org/manifests/calico.yaml

# 내용 수정 : BGP 미사용(bird Probe 제외)
vim calico.yaml
---------------
# 13줄
calico_backend: "vxlan"

# 3853줄, 3856줄
- name: CALICO_IPV4POOL_IPIP
  value: "Never"

- name: CALICO_IPV4POOL_VXLAN
  value: "Always"

# 3907줄, 3917줄
livenessProbe:
  exec:
    command:
    - /bin/calico-node
    - -felix-live
    #- -bird-live
  periodSeconds: 10
  initialDelaySeconds: 10
  failureThreshold: 6
readinessProbe:
  exec:
    command:
    - /bin/calico-node
    - -felix-ready
    #- -bird-ready
---------------

▶ VXLAN 모드 확인

더보기

 

# 모드 정보 확인
calicoctl get ippool -o wide
root@k8s-m:~/yaml# calicoctl get ippool -o wide
NAME                  CIDR            NAT    IPIPMODE   VXLANMODE   DISABLED   SELECTOR
default-ipv4-ippool   172.16.0.0/16   false  Never      Always      false      all()

# 노드(BGP) Peer 정보 확인 >> BGP 동작하지 않는다!
calicoctl node status
root@k8s-m:~/yaml# calicoctl node status
Calico process is running.

None of the BGP backend processes (BIRD or GoBGP) are running.

# 파드 네트워크 대역이 호스트 라우팅 테이블에 적용되었는지 확인 >> BGP 미사용으로 다른 방식으로 파드 대역 전달 받음
route -n | egrep '(Destination|vxlan)'
root@k8s-m:~/yaml# route -n | egrep '(Destination|vxlan)'
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.46.0     172.16.46.2     255.255.255.192 UG    0      0        0 vxlan.calico
172.16.197.0    172.16.197.2    255.255.255.192 UG    0      0        0 vxlan.calico
172.16.228.64   172.16.228.66   255.255.255.192 UG    0      0        0 vxlan.calico

# VXLAN 인캡슐레이션 동작을 수행하는 인터페이스 확인 (참고로 tunl0 의 IP는 삭제됨)
ifconfig vxlan.calico
root@k8s-m:~/yaml# ifconfig vxlan.calico
vxlan.calico: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 172.16.29.6  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::6404:3ff:fe49:b11e  prefixlen 64  scopeid 0x20<link>
        ether 66:04:03:49:b1:1e  txqueuelen 0  (Ethernet)
...

ip -c -d addr show vxlan.calico

# VXLAN Peer 정보 확인
bridge fdb show | grep vxlan
root@k8s-m:~/yaml# bridge fdb show | grep vxlan
66:74:91:32:7c:04 dev vxlan.calico dst 192.168.100.101 self permanent
66:55:c4:6b:81:ed dev vxlan.calico dst 192.168.100.102 self permanent
66:c2:2a:b3:d4:5a dev vxlan.calico dst 192.168.100.103 self permanent

ip neighbor show | grep vxlan
root@k8s-m:~/yaml# ip neighbor show | grep vxlan
172.16.46.2 dev vxlan.calico lladdr 66:55:c4:6b:81:ed PERMANENT
172.16.197.2 dev vxlan.calico lladdr 66:c2:2a:b3:d4:5a PERMANENT
172.16.228.66 dev vxlan.calico lladdr 66:74:91:32:7c:04 PERMANENT

 

[ 참고 - k8s-m 상태 ]

3-2. 동작 방식 확인
 
Step 1. POD 생성

kubectl apply -f node3-pod3.yaml

 
Step 2. 파드간 ping 통신 실행 및 패킷 캡쳐 확인

더보기
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
tcpdump -i <eth0> -nn udp port 4789
tcpdump -i enp0s8 -nn udp port 4789
혹은 아래 처럼 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> udp port 4789 -w /tmp/calico-vxlan.pcap
tcpdump -i enp0s8 udp port 4789 -w /tmp/calico-vxlan.pcap

# (참고) BGP 를 사용하지 않기 때문에, 기존의 BGP 라우터와 네트워크 정보 교환을 하지 못한다. 별도 설정이 없으면 통신 불가!

 

[ 참고 - 실행결과 ]

Step 3. POD 삭제  ( 꼭 잊지말고 수행해 주세요 ~!!@ ... )

kubectl delete -f node3-pod3.yaml

 

4. POD 패킷 암호화 ( 네트워크 레벨 )

▶ 요약 : Calico 의 다양한 네크워크 모드 환경 위에서 WireGuard 터널을 자동 생성 및 파드 트래픽을 암호화하여 노드간 전달

 
 
 
 
 
 
 
 
 
 
 
WireGuard 소개 - 한글 분석

더보기

1) WireGuard는 구닥다리 IPsec 및 OpenVPN의 대항마로 등장한 open source VPN project 이며

    작년,  Linux 5.6 커널에 WireGuard 1.0.0 기본 패키지로 탑재되었다.

2) 정말 간결한 코드 구조빠른 성능 (모든 것이 kernel에서 동작하고, 주요 암호 알고리즘에 대해서 병렬처리하므로써

    빠른 속도를 자랑함)

 

> Ref. Link :  WG VPN 해부 - 링크 & 개발 계획서 - 링크 & 데브시스터즈 WG 로 VPN 서버 구축하기 - 링크1 링크2 & WG 대시보드 - 링크 & Kilo MultiCloud K8S Network -링크

4-1. WireGuard 설정 - 링크
 - 실습 환경은 노드에 이미 설치되어 있음

더보기
# 설치
apt install wireguard -y

# WireGuard 버전 확인
wg version

- WireGuard 설정 및 확인

더보기
# 설정
calicoctl get felixconfiguration -o yaml
calicoctl patch felixconfiguration default --type='merge' -p '{"spec":{"wireguardEnabled":true}}'

# 확인
calicoctl get felixconfiguration default -o yaml | grep wireguardEnabled
root@k8s-m:~/yaml# calicoctl get felixconfiguration default -o yaml | grep wireguardEnabled
  wireguardEnabled: true

calicoctl get node -o yaml | grep wireguardPublicKey
calicoctl get node <노드 Name> -o yaml | grep wireguardPublicKey
root@k8s-m:~/yaml# calicoctl get node k8s-w1 -o yaml | grep wireguardPublicKey
  wireguardPublicKey: BToK9bLEhMaPUJsuKy3KdrxVOpklyo0qlGRdMN6lHWc=

# wireguard.cali 인터페이스 확인
ip -c -d addr show wireguard.cali
root@k8s-w1:~# ip -c -d addr show wireguard.cali
12: wireguard.cali: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none  promiscuity 0 minmtu 0 maxmtu 2147483552
    wireguard numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 172.16.228.74/32 scope global wireguard.cali
       valid_lft forever preferred_lft forever

ifconfig wireguard.cali
root@k8s-w1:~# ifconfig wireguard.cali
wireguard.cali: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1440
        inet 172.16.228.69  netmask 255.255.255.255  destination 172.16.228.69
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)

# wireguard.cali 설정 확인 : 통신 포트, Peer/Endpoint 정보, 패킷 암호화를 위한 공개키/사설키 정보
wg showconf wireguard.cali
root@k8s-w1:~# wg showconf wireguard.cali
[Interface]
ListenPort = 51820
FwMark = 0x100000
PrivateKey = AIgTihI2p4icwVMR4sIvuVaSqwKlkxMImQp4A/Gm+Gg=

[Peer]
PublicKey = BToK9bLEhMaPUJsuKy3KdrxVOpklyo0qlGRdMN6lHWc=
AllowedIPs = 172.16.228.64/26, 172.16.228.69/32, 172.16.228.67/32
Endpoint = 192.168.100.101:51820

[Peer]
PublicKey = 9TCD8hG6SLutZSOZSzQeqj6O0icJAxA3RPIipcBKBxs=
AllowedIPs = 172.16.197.0/26, 172.16.197.3/32, 172.16.197.5/32
Endpoint = 192.168.100.103:51820

[Peer]
PublicKey = Ercb/0pNZ+I1ELOkiXlWbZA9J0Fjt7XqsstDH4GhNmI=
AllowedIPs = 172.16.46.3/32, 172.16.46.0/26, 172.16.46.5/32
Endpoint = 192.168.100.102:51820

# Peer 의 정보(고유한 index)
wg show
interface: wireguard.cali
  public key: 8TNaYyzzc1N4SHfqE+Y5b4rMBKX/lqPe0tWO/h8sOB4=
  private key: (hidden)
  listening port: 51820
  fwmark: 0x100000

peer: 6WtZqEKSmoSKiFp20fhk/jyTcrTqf9qshyZI1HvE9Qk=
  endpoint: 192.168.10.102:51820
  allowed ips: 172.16.184.0/32, 172.16.184.0/24, 172.16.184.1/32

peer: +fOEOJgFxueIbrp709iB4F4gFRb2ny4lWKbxCNNfczM=
  endpoint: 192.168.20.100:51820
  allowed ips: 172.16.34.0/32, 172.16.34.0/24, 172.16.34.1/32

peer: d2LgXvRo4DwsyhiLXUn9TEt6D3l4pFIVlCD7KESR/m0=
  endpoint: 192.168.10.101:51820
  allowed ips: 172.16.158.0/32, 172.16.158.0/24, 172.16.158.1/32

# 키 정보 확인
wg show all public-key
wireguard.cali	8TNaYyzzc1N4SHfqE+Y5b4rMBKX/lqPe0tWO/h8sOB4=

wg show all private-key
wireguard.cali	kJbrfATGFP2v4sl+Wqg1Gv8zwFpIXshYFFD3udMDd3k=

wg show all preshared-keys
wireguard.cali	6WtZqEKSmoSKiFp20fhk/jyTcrTqf9qshyZI1HvE9Qk=	(none)
wireguard.cali	+fOEOJgFxueIbrp709iB4F4gFRb2ny4lWKbxCNNfczM=	(none)
wireguard.cali	d2LgXvRo4DwsyhiLXUn9TEt6D3l4pFIVlCD7KESR/m0=	(none)

# 그외 키타 정보
wg show all dump
wg show all endpoints
wg show all peers
wg show all transfer
wg show all persistent-keepalive

 

[ 실행 결과 - 한 눈에 보기 ]

[ 기본 정보 확인 및 wg 활성화 ]

 

[ 암호화 키 확인 ]

 

☞ WG 의 Peer 가 Unique Index 임으로 인해 가지는 장점

    ( Ref. Link - https://slowbootkernelhacks.blogspot.com/2020/09/wireguard-vpn.html )

  

 
4-2. 동작 확인
Step 1. POD 생성

kubectl apply -f node3-pod3.yaml

 
Step 2. 파드간 ping 통신 실행 및 패킷 캡쳐 확인

더보기
# 파드 Shell 접속(zsh)
kubectl exec -it pod1 -- zsh
## 파드 Shell 에서 아래 입력
ping <pod2 혹은 pod3 IP>

# 파드가 동작하는 노드의 eth0(예시)에서 패킷 덤프
ss -unlp
tcpdump -i <eth0> -nn udp port 51820
tcpdump -i ens5 -nn udp port 51820
혹은 아래 처럼 파일로 저장 후 해당 파일을 다운받아서 확인(wireshark 등 사용)
tcpdump -i <eth0> udp port 51820 -w /tmp/calico-wireguard.pcap
tcpdump -i ens5 udp port 51820 -w /tmp/calico-wireguard.pcap

# 현재 모든 워커 노드에 tcpdump 후 ping curl 테스트 시 모든 노드에서 트래픽이 발생한다 >> 이유는 VirtualBox 에 nic2 에 "무작위 모드 : 모두 허용" 상태여서, 모든 노드로 패킷이 전달됨
# VirtualBox 에 nic2 에 "무작위 모드 : 거부"로 설정 후 테스트 하게 되면 정확하게, 출발지와 목적지의 VM에서만 패킷이 확인된다!
모드워커노드) tcpdump -i ens5 -nn udp port 51820

 

[ 수행 결과 - 한 눈에 보기 ]

 

[ wg UDP port - dump 수행 ]
[ Wire-Shark로 패킷 분석 결과, WG의 peer 통신은 암호화 된 것을 확인할 수 있다. ]

Step 3. 파드 삭제 및 wireguardEnabled Off

더보기
# 파드 삭제
kubectl delete -f node3-pod3.yaml

# wireguardEnabled Off
calicoctl patch felixconfiguration default  --patch '{"spec":{"wireguardEnabled": false}}'

6. Calico 네트웍 접근 통제

6-1.  네트워크 정책 소개

네트워크 정책(Network Policy)은 쿠버네티스 클러스터 내부에서 파드 간에 통신할 경우 트래픽 룰을 규정하는 것이다.
네트워크 정책을 사용하지 않을 경우 클러스터 내부모든 파드는 서로 통신이 가능하다. 그러나 네트워크 정책을 사용할 수
있다면, 네임스페이스별로 트래픽을 전송하지 못하게 하거나 기본적으로 모든 파드 간 통신을 차단하고 특정 파드 간 통신만
허용하는 화이트리스트 방식을 사용할 수 있다. 또한 CNI(Calico, Cilium 등)에서 네트워크 정책을 지원해야 한다

▽ 네트워크 정책 구성

더보기

네트워크 정책은 인그레스(Ingress 수신) 이그레스(Egrss 송신)로 구성되어 있다. 인그레스는 인바운드 방향의 트래픽 룰을 설정하고, 이그레스는 아웃바운드 방향의 트래픽 룰을 설정한다. 설정 범위 podSelector 로 지정한다. 네트워크 정책은 네임스페이스별로 생성해야 한다.

◈ 네트워크 정책 예시 sample-networkpolicy.yaml

더보기
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: sample-networkpolicy
  namespace: default # 보안 정책을 생성할 네임스페이스 지정
spec:
  podSelector:
    # 설정할 대상 파드를 여기에 기입
    # 레이블 셀렉터이므로 복수의 파드를 대상으로 할 수 있음.
  policyTypes:
  - Ingress # 인그레이스 룰을 생성하는 경우 명시
  - Egress # 이그레스 룰을 생성할 경우 명시
  ingress:
  - from:
      # 인그레스 룰을 여기에 기입(이그레스 룰과 형식은 동일)
    ports:
      # 이 인그레스 룰로 허가할 수신 포트 번호와 프로토콜 기입
  egress:
  - to:
      # 이그레스 룰을 여기에 기입(인그레스 룰과 형식은 동일)
    ports:
      # 이 이그레스 룰로 허가할 송신 포트 번호와 프로토콜 기입

▽ 정책 지정 방법(3가지) : 인그레스 룰과 이그레스 룰의 지정 방법은 동일하다.

더보기

1. podSelector : 특정 파드에서의 통신을 제어하는 정책이다.

2. namespaceSelector : 특정 네임스페이스상에 있는 파드에서의 통신을 제어하는 정책이다.

    namespaceSelector 는 podSelector 에 비해 제한 범위가 넓기 때문에 podSelector 와 namespaceSelector는

   네트워크 제한 범위에 따라 선택하면 된다.

3. ipBlock : 특정 CIDR에서의 통신을 제한하는 정책이다. 쿠버네티스 클러스터 내부에서 사용되는 통신 제어는

    podSelector 와 namespaceSelector 를 사용하는 것이 좋지만, 클러스터 외부 네트워크 제어는 ipBlock 을

    사용하는 것이 좋다.

▽ 화이트 리스트 방식과 블랙 리스트 방식

더보기
  • 화이트리스트 방식은 미리 모든 트래픽을 차단해 두고 특정 트래픽만 허가하는 형식이다.
  • 블랙리스트 방식은 그 반대로 미리 모든 트래픽을 허가해 두고 특정 트래픽만 차단하는 형식이다.

◈ 네트워크 정책 예시

더보기

1) 모든 트래픽을 차단하는 네트워크 정책

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-networkpolicy
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

 

2) 모든 인바운드/아웃바운드 트래픽을 허가하는 정책

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-networkpolicy
spec:
  podSelector: {}
  egress:
  - {}
  ingress:
  - {}
  policyTypes:
  - Ingress
  - Egress

 

 3) 클라우드에 적합한 인바운드는 모두 차단하고, 아웃바운드는 모두 허용하는 정책

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: cloud-networkpolicy
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Ingress
  - Egress

 

 


6-2. 실습 과정

Step 1. Calico 설정 변경

더보기
# 현재 설정 확인
calicoctl get ippool -o wide

# calico nat true 설정
cat <<EOF | calicoctl replace -f -
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: default-ipv4-ippool
spec:
  blockSize: 24
  cidr: 172.16.0.0/16
  ipipMode: Never
  nodeSelector: all()
  vxlanMode: Always
  natOutgoing: true
EOF

# calico wireguard 끄기
calicoctl patch felixconfiguration default --type='merge' -p '{"spec":{"wireguardEnabled":false}}'

# ipip mode 설정
kubectl apply -f https://raw.githubusercontent.com/gasida/NDKS/main/4/calico-ndks.yaml
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/vxlanMode: Always/vxlanMode: Never/" | calicoctl apply -f -
calicoctl get ippool default-ipv4-ippool -o yaml | sed -e "s/ipipMode: Never/ipipMode: Always/" | calicoctl apply -f -
calicoctl get ippool default-ipv4-ippool -o wide

# 변경 설정 적용을 위해서 calico 파드 삭제 후 재생성
kubectl delete pod -n kube-system -l k8s-app=calico-node

# 모든 노드에서 enp0s8 down 후 up (라우팅 테이블 갱신을 위함)
route -n && echo && ip link set enp0s8 down && sleep 1 && ip link set enp0s8 up && route -n

 

Step 2. 파드 생성 및 확인 : default ns 에 파드 2개 , nptest ns 에 파드 2개 - 각각 파드는 레이블을 지정

더보기
# 모니터링
watch -d 'calicoctl get wep -n default;echo;calicoctl get wep -n nptest'

# 생성
kubectl apply -f https://raw.githubusercontent.com/gasida/NDKS/main/5/networkpolicy-playground.yaml

# 확인
kubectl get ns
calicoctl get wep -owide
calicoctl get wep -owide -n nptest

# 네임스페이스에 레이블 지정
kubectl label ns default ns=default
kubectl label ns nptest ns=nptest

# 확인
kubectl get ns --show-labels
NAME              STATUS   AGE     LABELS
default           Active   66m     kubernetes.io/metadata.name=default,ns=default
nptest            Active   4m17s   kubernetes.io/metadata.name=nptest,ns=nptest
...

Step 3. 기본 통신 확인

더보기
# 파드 정보 확인
calicoctl get wep -owide
calicoctl get wep -owide -n nptest

# 파드 IP주소를 변수에 지정
DEFAULTPOD1=$(calicoctl get workloadEndpoint | grep np1 | awk '{print $3}' | cut -d "/" -f 1)
echo $DEFAULTPOD1
DEFAULTPOD2=$(calicoctl get workloadEndpoint | grep np2 | awk '{print $3}' | cut -d "/" -f 1)
echo $DEFAULTPOD2
NPTESTPOD3=$(calicoctl get workloadEndpoint -n nptest | grep np3 | awk '{print $4}' | cut -d "/" -f 1)
echo $NPTESTPOD3
NPTESTPOD4=$(calicoctl get workloadEndpoint -n nptest | grep np4 | awk '{print $4}' | cut -d "/" -f 1)
echo $NPTESTPOD4

# 기본 접속 확인 - 파드 간 통신
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $DEFAULTPOD2

kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD3

kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD4

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $DEFAULTPOD2 | grep nginx!

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $NPTESTPOD3 | grep nginx!

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $NPTESTPOD4 | grep nginx!

# 기본 접속 확인 - 파드 >> 외부 통신
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 www.google.com

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 ipinfo.io/city
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 ipinfo.io/org
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 ipinfo.io/loc
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 

# (옵션) k8s-rtr >> 파드 : 단, BGP 로 광고 전파 상태
ping -i 1 -W 1 -c 1 -I 10.1.1.254 <sample-pod-np1 의 IP>
root@k8s-rtr:~# ping -i 1 -W 1 -c 1 -I 10.1.1.254 172.16.34.2
PING 172.16.34.2 (172.16.34.2) 56(84) bytes of data.
64 bytes from 172.16.34.2: icmp_seq=1 ttl=63 time=0.606 ms

curl -s --connect-timeout 2 --interface 10.1.1.254 <sample-pod-np1 의 IP> | grep nginx!
root@k8s-rtr:~# curl -s --connect-timeout 2 --interface 10.1.1.254 172.16.34.2 | grep nginx!
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>

Step 4. 클라우드에 적합한 인바운드는 모두 차단하고, 아웃바운드는 모두 허용하는 정책을 모든 네임스페이스에 적용

더보기
cat <<EOF> cloud-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: cloud-networkpolicy
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Ingress
  - Egress
EOF
# (워커노드) 필터링 테이블의 정책을 파일로 저장 및 갯수 확인
iptables -t filter -S |grep cali- > calico-policy1.txt

iptables -t filter -S |grep cali- | wc -l
89

# 정책 생성
kubectl apply -f cloud-networkpolicy.yaml
kubectl apply -n nptest -f cloud-networkpolicy.yaml

# (워커노드) 필터링 테이블의 정책을 파일로 저장 및 변경 정보 확인
iptables -t filter -S |grep cali- > calico-policy2.txt

diff calico-policy1.txt calico-policy2.txt

iptables -t filter -S |grep cali- | wc -l
109

# 정책 확인
kubectl get netpol

kubectl describe netpol cloud-networkpolicy
Name:         cloud-networkpolicy
Namespace:    default
Created on:   2021-11-08 15:58:16 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     <none> (Allowing the specific traffic to all pods in this namespace)
  Allowing ingress traffic:
    <none> (Selected pods are isolated for ingress connectivity)
  Allowing egress traffic:
    To Port: <any> (traffic allowed to all ports)
    To: <any> (traffic not restricted by destination)
  Policy Types: Ingress, Egress

# 기본 접속 확인 - 파드 간 통신
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $DEFAULTPOD2

kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD3

kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD4

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $DEFAULTPOD2 | grep nginx!

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $NPTESTPOD3 | grep nginx!

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $NPTESTPOD4 | grep nginx!

# 기본 접속 확인 - 파드 >> 외부 통신
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 www.google.com

kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 ipinfo.io/city
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 ipinfo.io/org
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 ipinfo.io/loc
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 ipinfo.io

# (옵션) k8s-rtr >> 파드 : 단, BGP 로 광고 전파 상태
ping -i 1 -W 1 -c 1 -I 10.1.1.254 <sample-pod-np1 의 IP>
curl -s --connect-timeout 2 --interface 10.1.1.254 <sample-pod-np1 의 IP> | grep nginx!

 

◈ (심화 옵션) Calico 자체 제공 추가 기능 : GlobalNetworkPolicy (GNP) → 개별 네임스페이스 마다 설정할 필요 없이 모든

     네임스페이스에 정책 적용 (옵션: 중요 네임스페이스는 예외 처리)

# 실습을 위해서 정책 삭제
kubectl delete -f cloud-networkpolicy.yaml
kubectl delete -n nptest -f cloud-networkpolicy.yaml
cat <<EOF> gnp-allnamespaces-deny.yaml
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: default-app-policy
spec:
  namespaceSelector: has(projectcalico.org/name) && projectcalico.org/name not in {"kube-system", "calico-system"}
  types:
  - Ingress
  - Egress
EOF
# 정책 생성
kubectl apply -f gnp-allnamespaces-deny.yaml

# 정책 확인
kubectl get globalnetworkpolicies
kubectl describe globalnetworkpolicies default-app-policy

# 파드 IP 확인
calicoctl get wep -A
NAMESPACE     WORKLOAD                                   NODE     NETWORKS          INTERFACE
default       sample-pod-np1                             k8s-w0   172.16.34.2/32    cali19b0bcd175f
default       sample-pod-np2                             k8s-w1   172.16.158.3/32   calicc8bba29d1b
kube-system   calico-kube-controllers-75f8f6cc59-cmb5c   k8s-m    172.16.116.3/32   calic8fe8708885
kube-system   coredns-558bd4d5db-bck5k                   k8s-m    172.16.116.1/32   calie8f0d4df3ed
kube-system   coredns-558bd4d5db-qv5x8                   k8s-m    172.16.116.2/32   cali9237f35fd4c
nptest        sample-pod-np3                             k8s-w2   172.16.184.3/32   cali8e3546b3e16
nptest        sample-pod-np4                             k8s-w2   172.16.184.2/32   cali3fd7eae6008
 
# 마스터노드 >> kube-system 네임스페이스의 coredns 파드와 통신
ping 172.16.116.1 # coredns-558bd4d5db-bck5k

# 마스터노드 >> sample-pod-np1~np4 파드와 통신
ping <sample-pod-np1~np4> 

# 기본 접속 확인 - 파드 간 통신
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $DEFAULTPOD2
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD3
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD4

# 기본 접속 확인 - 파드 >> 외부 통신
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 www.google.com

# 다음 실습을 위해서 GNP 정책 삭제
kubectl delete -f gnp-allnamespaces-deny.yaml

# 기존 기본 정책 다시 적용
kubectl apply -f cloud-networkpolicy.yaml
kubectl apply -n nptest -f cloud-networkpolicy.yaml

Step 5. 특정 레이블을 가진 파드에서 들어오는 통신을 허가 (sample-podselector-ingress-networkpolicy.yaml)

더보기

♣ sample-pod-np2 에 app:np1 레이블을 가진 파드에서 들어오는 통신을 허가하도록 한다. 또 80/TCP 포트만 개방한다.

cat <<EOF> sample-podselector-ingress-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: sample-podselector-ingress-networkpolicy
spec:
  podSelector:
    matchLabels:
      app: np2
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: np1
    ports:
    - protocol: TCP
      port: 80
EOF
# 정책 생성
kubectl apply -f sample-podselector-ingress-networkpolicy.yaml

# 정책 확인
kubectl get netpol
NAME                                       POD-SELECTOR   AGE
cloud-networkpolicy                        <none>         48m
sample-podselector-ingress-networkpolicy   app=np2        13s

kubectl describe netpol cloud-networkpolicy
Name:         sample-podselector-ingress-networkpolicy
Namespace:    default
Created on:   2021-11-08 16:46:51 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     app=np2
  Allowing ingress traffic:
    To Port: 80/TCP
    From:
      PodSelector: app=np1
  Not affecting egress traffic
  Policy Types: Ingress

# 기본 접속 확인 - 파드 간 통신 : sample-pod-np1 >> sample-pod-np2 로 TCP 80 만 허용!
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $DEFAULTPOD2 | grep nginx!
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $DEFAULTPOD2

Step 6. 특정 네임스페이스에서 들어오는 통신을 허가 (sample-namespaceselector-ingress-networkpolicy.yaml)

더보기

♣ 기본(default) 네임스페이스에서 들어오는 트래픽을 sample-pod-np3 에서 통신을 허가해보자.

    정책 적용은 nptest 네임스페이스에 한다!

cat <<EOF> sample-namespaceselector-ingress-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: sample-namespaceselector-ingress-networkpolicy
  namespace: nptest
spec:
  podSelector:
    matchLabels:
      app: np3
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          ns: default
    ports:
    - protocol: TCP
      port: 80
EOF
# 정책 생성
kubectl apply -f sample-namespaceselector-ingress-networkpolicy.yaml

# 정책 확인
kubectl get netpol -n nptest
NAME                                             POD-SELECTOR   AGE
cloud-networkpolicy                              <none>         46m
sample-namespaceselector-ingress-networkpolicy   app=np3        2m2s

kubectl describe netpol -n nptest sample-namespaceselector-ingress-networkpolicy
Name:         sample-namespaceselector-ingress-networkpolicy
Namespace:    nptest
Created on:   2021-11-08 16:52:19 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     app=np3
  Allowing ingress traffic:
    To Port: 80/TCP
    From:
      NamespaceSelector: ns=default
  Not affecting egress traffic
  Policy Types: Ingress

# 기본 접속 확인 - 파드 간 통신 : sample-pod-np1 >> sample-pod-np3 로 TCP 80 만 허용!
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $NPTESTPOD3 | grep nginx!
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD3

Step 7. 특정 IP 블록에서 들어오는 통신을 허가 (sample-ipblock-ingress-networkpolicy.yaml)

더보기

♣ 특정 IP 블록(sample-pod-np1)에서 들어오는 트래픽을 sample-pod-np4 에서 통신을 허가해보자.

    정책 적용은 nptest 네임스페이스에 한다!

cat <<EOF> sample-ipblock-ingress-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: sample-ipblock-ingress-networkpolicy
  namespace: nptest
spec:
  podSelector:
    matchLabels:
      app: np4
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: $DEFAULTPOD1/32
    ports:
    - protocol: TCP
      port: 80
EOF
# 정책 생성
kubectl apply -f sample-ipblock-ingress-networkpolicy.yaml

# 정책 확인
kubectl get netpol -n nptest
NAME                                             POD-SELECTOR   AGE
cloud-networkpolicy                              <none>         50m
sample-ipblock-ingress-networkpolicy             app=np4        1s
sample-namespaceselector-ingress-networkpolicy   app=np3        6m52s

kubectl describe netpol -n nptest sample-ipblock-ingress-networkpolicy
Name:         sample-ipblock-ingress-networkpolicy
Namespace:    nptest
Created on:   2021-11-08 16:59:10 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     app=np4
  Allowing ingress traffic:
    To Port: 80/TCP
    From:
      IPBlock:
        CIDR: 172.16.34.2/32
        Except:
  Not affecting egress traffic
  Policy Types: Ingress

# 기본 접속 확인 - 파드 간 통신 : sample-pod-np1($DEFAULTPOD1, 172.16.34.2/32) >> sample-pod-np4 로 TCP 80 만 허용!
kubectl exec -it sample-pod-np1 -- curl -s --connect-timeout 2 $NPTESTPOD4 | grep nginx!
kubectl exec -it sample-pod-np1 -- ping -i 1 -W 1 -c 1 $NPTESTPOD4

아웃바운드 통신(이그레스) : 이그레스의 경우에도 인그레스와 마찬가지로 세 가지 정책을 사용할 수 있다.
 
Step 8. 오브젝트 삭제

더보기
# 정책 삭제
kubectl delete -f sample-podselector-ingress-networkpolicy.yaml
kubectl delete -f cloud-networkpolicy.yaml

# 파드 및 NS 삭제
kubectl delete -f https://raw.githubusercontent.com/gasida/NDKS/main/5/networkpolicy-playground.yaml

 
 
 


[ 중요 - 마지막 자원 정리 ]
- 모든 실습 완료 후에는 꼭 삭제 확인 해주시기 바랍니다.

# CloudFormation 스택 삭제
aws cloudformation delete-stack --stack-name mylab

# [모니터링] CloudFormation 스택 상태 : 삭제 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

 

6-3. Calico Tip

 

A. 부가 기능

▶ 파드에 고정 IP를 직접 지정해서 배포 - Docs

더보기
# 배포
cat <<EOF| kubectl apply -f - 
apiVersion: v1
kind: Pod
metadata:
  name: static-ip-pod
  annotations:
    "cni.projectcalico.org/ipAddrs": "[\"172.16.100.100\"]"
spec:
  containers:
  - name: static-ip-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 확인 : annotations 에 지정한 IP를 파드에 직접 할당 확인!
kubectl get pod static-ip-pod -o wide
NAME            READY   STATUS    RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
static-ip-pod   1/1     Running   0          110s   172.16.100.100   k8s-w1   <none>           <none>

# 삭제
kubectl delete pod static-ip-pod

 

◈ Floating IP 를 파드에 할당 : Add a floating IP to a pod - Docs
🥎K8S CNI - Calico 3편 - eBPF (DSR)
🛫K8S CNI - Calico 4편 - 내부망 연동 (BGP)
 

B. 운영

B-1. 모니터링
▶ Monitor Calico component metrics - Docs

더보기

1. Use Prometheus configured for Calico components to get valuable metrics about the health of Calico.

2. For Calico, the “jobs” that Prometheus can harvest metrics from are the Felix and Typha

3. You can configure Felix, Typha, and/or kube-controllers to provide metrics to Prometheus.

4. 설정

 a) Configure Calico to enable the metrics reporting.

 b) Create the namespace and service account that Prometheus will need

 c) Deploy and configure Prometheus.

 d) View the metrics in the Prometheus dashboard and create a simple graph

4-a) Configure Calico to enable metrics reporting

더보기
# Felix configuration
calicoctl get felixconfiguration -o yaml
calicoctl patch felixconfiguration default  --patch '{"spec":{"prometheusMetricsEnabled": true}}'

# Creating a service to expose Felix metrics : Felix by default uses port 9091 TCP to publish its metrics.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: felix-metrics-svc
  namespace: kube-system
spec:
  clusterIP: None
  selector:
    k8s-app: calico-node
  ports:
  - port: 9091
    targetPort: 9091
EOF
kubectl get svc,ep -n kube-system felix-metrics-svc

# kube-controllers configuration : Prometheus metrics are enabled by default on TCP port 9094 for calico-kube-controllers
## Creating a service to expose kube-controllers metrics
calicoctl get kubecontrollersconfiguration default -o yaml
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: kube-controllers-metrics-svc
  namespace: kube-system
spec:
  clusterIP: None
  selector:
    k8s-app: calico-kube-controllers
  ports:
  - port: 9094
    targetPort: 9094
EOF
kubectl get svc,ep -n kube-system kube-controllers-metrics-svc

4-b) Cluster preparation

더보기
# Namespace creation
kubectl create -f -<<EOF
apiVersion: v1
kind: Namespace
metadata:
  name: calico-monitoring
  labels:
    app:  ns-calico-monitoring
    role: monitoring
EOF
kubectl get ns

# Service account creation
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: calico-prometheus-user
rules:
- apiGroups: [""]
  resources:
  - endpoints
  - services
  - pods
  verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: calico-prometheus-user
  namespace: calico-monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: calico-prometheus-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: calico-prometheus-user
subjects:
- kind: ServiceAccount
  name: calico-prometheus-user
  namespace: calico-monitoring
EOF
kubectl get sa -n calico-monitoring

4-c) Install prometheus

더보기
#
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: calico-monitoring
data:
  prometheus.yml: |-
    global:
      scrape_interval:   15s
      external_labels:
        monitor: 'tutorial-monitor'
    scrape_configs:
    - job_name: 'prometheus'
      scrape_interval: 5s
      static_configs:
      - targets: ['localhost:9090']
    - job_name: 'felix_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: felix-metrics-svc
        replacement: $1
        action: keep
    - job_name: 'felix_windows_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: felix-windows-metrics-svc
        replacement: $1
        action: keep
    - job_name: 'typha_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: typha-metrics-svc
        replacement: $1
        action: keep
    - job_name: 'kube_controllers_metrics'
      scrape_interval: 5s
      scheme: http
      kubernetes_sd_configs:
      - role: endpoints
      relabel_configs:
      - source_labels: [__meta_kubernetes_service_name]
        regex: kube-controllers-metrics-svc
        replacement: $1
        action: keep
EOF
kubectl get cm -n calico-monitoring prometheus-config

# Create Prometheus pod
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: prometheus-pod
  namespace: calico-monitoring
  labels:
    app: prometheus-pod
    role: monitoring
spec:
  nodeSelector:
    kubernetes.io/os: linux
  serviceAccountName: calico-prometheus-user
  containers:
  - name: prometheus-pod
    image: prom/prometheus
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    volumeMounts:
    - name: config-volume
      mountPath: /etc/prometheus/prometheus.yml
      subPath: prometheus.yml
    ports:
    - containerPort: 9090
  volumes:
  - name: config-volume
    configMap:
      name: prometheus-config
EOF
kubectl get pods prometheus-pod -n calico-monitoring -owide

4-d) view metrics

더보기
# 파드 IP 확인
kubectl get pods prometheus-pod -n calico-monitoring -owide

# 파드 IP metrics 엔드포인트 curl 접속 확인
curl <파드 IP>:9090/metrics
curl 172.16.34.7:9090/metrics

#
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: prometheus-dashboard-svc
  namespace: calico-monitoring
spec:
  type: NodePort
  selector:
    app: prometheus-pod
    role: monitoring
  ports:
    - protocol: TCP
      port: 9090
      targetPort: 9090
      nodePort: 30001 
EOF
kubectl get svc,ep -n calico-monitoring

# 프로메테우스 접속 주소
echo -e "Prometheus URL = http://$(curl -s ipinfo.io/ip):30001"  # [실습환경 A Type]
echo -e "Prometheus URL = http://192.168.10.10:30001"            # [실습환경 B Type]

▶ Visualizing metrics via Grafana - Docs
  - Use Grafana dashboard to view Calico component metrics.

더보기
# Provisioning datasource
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-config
  namespace: calico-monitoring
data:
  prometheus.yaml: |-
    {
        "apiVersion": 1,
        "datasources": [
            {
               "access":"proxy",
                "editable": true,
                "name": "calico-demo-prometheus",
                "orgId": 1,
                "type": "prometheus",
                "url": "http://prometheus-dashboard-svc.calico-monitoring.svc:9090",
                "version": 1
            }
        ]
    }
EOF
kubectl get cm -n calico-monitoring

# Provisioning Calico dashboards : Here you will create a configmap with Felix and Typha dashboards.
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/grafana-dashboards.yaml

# Creating Grafana pod
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: grafana-pod
  namespace: calico-monitoring
  labels:
    app:  grafana-pod
    role: monitoring
spec:
  nodeSelector:
    kubernetes.io/os: linux
  containers:
  - name: grafana-pod
    image: grafana/grafana:latest
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
    volumeMounts:
    - name: grafana-config-volume
      mountPath: /etc/grafana/provisioning/datasources
    - name: grafana-dashboards-volume
      mountPath: /etc/grafana/provisioning/dashboards
    - name: grafana-storage-volume
      mountPath: /var/lib/grafana
    ports:
    - containerPort: 3000
  volumes:
  - name: grafana-storage-volume
    emptyDir: {}
  - name: grafana-config-volume
    configMap:
      name: grafana-config
  - name: grafana-dashboards-volume
    configMap:
      name: grafana-dashboards-config
EOF

#
kubectl get pod -n calico-monitoring

#
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: grafana
  namespace: calico-monitoring
spec:
  type: NodePort
  selector:
    app:  grafana-pod
    role: monitoring
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
      nodePort: 30002 
EOF
kubectl get svc,ep -n calico-monitoring

# 그라파나 접속 주소 : 초기 계정 ( admin , admin )
echo -e "Grafana URL = http://$(curl -s ipinfo.io/ip):30002"  # [실습환경 A Type]
echo -e "Grafana URL = http://192.168.10.10:30002"            # [실습환경 B Type]

B. Trouble Shooting
TS and diagnostics

더보기

case1. Logs and diagnostics

# calico ctrl, node 파드 로그
kubectl logs -n kube-system -l k8s-app=calico-kube-controllers
kubectl logs -n kube-system -l k8s-app=calico-node

# BGP Peer status
calicoctl node status

 

case2. Errors when running sudo calicoctl

# 정보 수집 : 단, docker 엔진 필요..
sudo -E calicoctl node diags
...
Failed to run command: docker run --rm --privileged --net=host calico/node ipset list

 

case3. Error: calico/node is not ready: BIRD is not ready: BGP not established with 10.0.0.1

  ♣ Check that BGP connectivity between the two peers is allowed in the environment.

  This error can also occur if inactive Node resources are configured for node-to-node mesh.

     To fix this, decommission the stale nodes.

 

case4. Linux conntrack table is out of space

sysctl -w net.netfilter.nf_conntrack_max=1000000
echo "net.netfilter.nf_conntrack_max=1000000" >> /etc/sysctl.conf

TS commands

더보기
# Verify number of nodes in a cluster
kubectl get nodes

# Verify calico-node pods are running on every node, and are in a healthy state
kubectl get pods -n kube-system -o wide
혹은
kubectl get pods -n calico-system -o wide

# Exec into pod for further troubleshooting
kubectl run multitool --image=praqma/network-multitool
kubectl exec -it multitool -- bash
---------
ping 8.8.8.8
dig +short www.google.com
exit
---------

Logs


[ 마무리 ]
Calico Network 을 공부하면서, CNI 의 구조와 역할에 대한 이해를 바탕으로  Calico CNI가 운영되는 4가지 방식에 대해 접근해 볼 수 있었다. gasida 님의 강의를 다시 보면서 차근 차근 실습해 보는 과정이 힘이 들긴 했지만 Calico 에 대한 실질적인 이해에 많은 도움이 되었던 것 같다. 더불어, Wire-shark 와 연계하여 TCP/UDP dump를 분석해 볼 수 있어서 추후 업무에 있어서도 활용범위가 넓어지게 되어 유익한 시간 이었던 것 같다.


[ 유용한 참고 링크 모음 ]

더보기

1. Calico Docs

2. Networking

3. Operations ( Install calicoctl , eBPF …)

4. Reference ( calicoctl , Resource definitions , )

5. Calico - Youtube , Github

 

6. Calico 무료 교육(tigera) - Calico Operator Level 1 - 참고 그림 및 정책 예시 - 링크