ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kubernetes] 쿠버네티스 설치하기 (2) - installing kubeadm
    인프라/Kubernetes 2020. 11. 10. 16:56

    kubeadm을 이용한 쿠버네티스 설치를 진행하도록 하겠다.

    아래는 공식문서 링크

     

    Installing kubeadm

    This page shows how to install the kubeadm toolbox. For information how to create a cluster with kubeadm once you have performed this installation process, see the Using kubeadm to Create a Cluster page. Before you begin One or more machines running one of

    kubernetes.io

     


    Installing kubeadm

    Before you begin

    • One or more machines running one of:
      • Ubuntu 16.04+
      • Debian 9+
      • CentOS 7
      • Red Hat Enterprise Linux (RHEL) 7
      • Fedora 25+
      • HypriotOS v1.0.1+
      • Flatcar Container Linux (tested with 2512.3.0)
    • 2 GB or more of RAM per machine (any less will leave little room for your apps)
    • 2 CPUs or more
    • Full network connectivity between all machines in the cluster (public or private network is fine)
    • Unique hostname, MAC address, and product_uuid for every node. See here for more details.
    • Certain ports are open on your machines. See here for more details.
    • Swap disabled. You MUST disable swap in order for the kubelet to work properly.

    자 시작하기전에 환경을 살펴보자

    • 일단 난 Centos7 운영체제를 사용 pass
    • 2GB 메모리 VMware로 진행할 예정이어서 할당하면 되니 pass
    • 2 CPUs 흠 .. 봐야하는데 일단 pass
    • VMware 네트워크 환경을 bridge로 설정, host와 동일한 네트워크 환경인데 일단 pass
    • hostname, MAC address, product_uuid 는 각각 유니크할 것이니 pass
    • 쿠버네티스를 사용하기위한 포트들도 다 열것이니 pass
    • Swap 기능을 아예 off하고, 부팅 후에도 Swap 기능이 올라오지 않도록 /etc/fstab 설정을 지울 것이다 pass

     


    Verify the MAC address and product_uuid are unique for every node

    • You can get the MAC address of the network interfaces using the command ip link or ifconfig -a
    • The product_uuid can be checked by using the command sudo cat /sys/class/dmi/id/product_uuid

    각 node마다 유니크한 MACaddress, product_uuid,를 지녀야하니 체크만 하자

     


    Check network adapters

    If you have more than one network adapter, and your Kubernetes components are not reachable on the default route, we recommend you add IP route(s) so Kubernetes cluster addresses go via the appropriate adapter


    일단 host 네트워크 인터페이스로 ens33 으로만 사용할 것이므로 일단은 넘어가자

     


    Letting iptables see bridged traffic

    Make sure that the br_netfilter module is loaded. This can be done by running lsmod | grep br_netfilter. To load it explicitly call sudo modprobe br_netfilter.

    As a requirement for your Linux Node's iptables to correctly see bridged traffic, you should ensure net.bridge.bridge-nf-call-iptables is set to 1 in your sysctl config, e.g.


    br_netfilter에서 br은 bridge의 약어, 이 녀석이 하는일은 여기(ebtables.netfilter.org/documentation/bridge-nf.html)에 설명이 되어있는데 이해한바를 적어보면

    우리가 사용하고 있는 방화벽인 iptables가 bridge driver(다른 driver로는 host driver가 있음)를 사용하여 통신을하는 패킷들 (트래픽)을 filter 할수 있도록 해주는 녀석이다.(가상 VLAN 포함) 이 기능이 활성화 되어있어야 Dockerdefault로 사용하는 bridge driver를 사용하여 생성되는 container들간의 통신이 가능하다고 한다. 쿠버네티스에서는 이런 container를 포함하는게 Pod라고 부른다 결국 Pod끼리의 통신이 가능해 진다는 것이다.

    lsmod | grep br_netfilter

    모듈 리스트를 확인하는 command [ Module | Size | Used By ] 순서로 출력됨

    현재 br_netfilter 커널모듈이 존재하고 있으나 아무도 사용을 하고 있지 않아서 0이다.

    바로아래 bridge 모듈도 존재하는데 br_netfilter가 이 bridge 모듈에 의존해서 동작한다고 한다.

    모듈에대한 정보확인

     

    sudo modprobe br_netfilter

    위의 이미지처럼 br_netfilter가 뜨지않으면 적재가 되지않은 것이니 위의 커맨드를 사용해서 모듈을 적재하자!

     

    echo "br_netfilter" > /etc/modules-load.d/br_netfilter.conf

    리눅스 부팅 시 자동으로 br_netfilter를 load 하고 싶으면 위의 command를 입력하자.

    커널이 /etc/modules-load.d/ 경로에 있는 *.conf 파일들을 모조리 읽어서 module을 load 한다고 한다.

     

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

    실제 브릿지 네트워크를 필터할 수 있는 모듈 br_netfilter는 load된걸 확인하였다.

    이 모듈을 iptables가 사용할 수 있도록 설정을 하는과정이다.

     

    sudo sysctl --system

    위와 같은 command를 치게되면

    커널 변수값을 설정하기위해 많은 경로에서의 *.conf 파일들을 읽어서 세팅을 하고있음을 볼 수 있다.

    /etc/sysctl.d/ 디렉토리 경로도 포함되는데, 우리가 만든 k8s.conf 파일도 읽어들여서 세팅을 하는 모습이다.

     

    실제 커널 변수값이 존재하는 파일을 건드려서 직접 수정할 수도 있다.

    경로는 위에 세팅한 net.bridge.bridge-nf-call-iptables/proc/sys/ 의 하위경로로 그대로 따라가면 모습을 볼 수 있다.

    Docker가 container들을 bridge 형태의 네트워크로 구성하게 되면, 위의 옵션값이 1일 경우, iptables은 Docker에 의해 발생되는 모든 트래픽을 받아 볼 수 있다. (제어할 수 있다는 의미인가보다)

     

    아래 블로그들에서 이러한 사전지식을 얻을 수 있었다. ㄳㄳ

     

    Docker Network 구조(2) - container network 방식 4가지

    Docker Network 구조(2) - container network 방식 4가지 [Contents] 1. Docker Network 구조(1) - docker0와 container network 구조 2. Docker Network 구조(2) - Container network 방식 4가지 3. Docker Ne..

    bluese05.tistory.com

     

    Docker, bridge-nf-call-iptables 워닝

    도커 "WARNING: bridge-nf-call-iptables is disabled" 워닝 없애기 현상 docker info 명령을 실행했을 때 "bridge-nf-call-iptables is disabled" 워닝이 뜨는 경우가 있다. $ docker info ... WARNING: bridge-n..

    ingeec.tistory.com

     

    도커의 네트워크 모드는 호스트 모드(Host Mode)와 브릿지 모드(Bridge Mode) 2가지 모드

    컨테이너 네트워크 실행 모드 $ docker network ls NETWORK ID          NAME                DRIVER              SCOPE 79358e90d40e        bridge   ..

    rhr0916.tistory.com

     

    sysctl를 이용한 시스템 최적화

     

    attiadmin.guyweb.co.kr

     

    sysctl 명령어

    ( 1 ) 설명 sysctl 명령어는 런타임(Runtime) 중에 /proc/sys 하위 디렉토리에 대한 커널 매개변수 값을 ...

    blog.naver.com


    Check required ports

    Control-plane node(s)

    ProtocolDirectionPort RangePurposeUsed By

    TCP Inbound 6443* Kubernetes API server All
    TCP Inbound 2379-2380 etcd server client API kube-apiserver, etcd
    TCP Inbound 10250 Kubelet API Self, Control plane
    TCP Inbound 10251 kube-scheduler Self
    TCP Inbound 10252 kube-controller-manager Self

    Worker node(s)

    ProtocolDirectionPort RangePurposeUsed By

    TCP Inbound 10250 Kubelet API Self, Control plane
    TCP Inbound 30000-32767 NodePort Services† All

     

    쿠버네티스 환경에 필요한 포트들이다. * 달린녀석은 우리가 오버라이드 할 수 있다고 한다.

    난 일단 포트에 대해서는 따로 iptables를 이용해서 설정하지 않고 firewalld를 끄고 진행하겠다.

    systemctl stop firewalld
    systemctl disable firewalld

     


    Installing runtime

    To run containers in Pods, Kubernetes uses a container runtime.

    By default, Kubernetes uses the Container Runtime Interface (CRI) to interface with your chosen container runtime.

    If you don't specify a runtime, kubeadm automatically tries to detect an installed container runtime by scanning through a list of well known Unix domain sockets. The following table lists container runtimes and their associated socket paths:

    RuntimePath to Unix domain socket

    Docker /var/run/docker.sock
    containerd /run/containerd/containerd.sock
    CRI-O /var/run/crio/crio.sock


    If both Docker and containerd are detected, Docker takes precedence. This is needed because Docker 18.09 ships with containerd and both are detectable even if you only installed Docker. If any other two or more runtimes are detected, kubeadm exits with an error.

    The kubelet integrates with Docker through the built-in dockershim CRI implementation.


    쿠버네티스가 Pods안에 containers를 실해아기위해 container runtime 즉 Docker를 사용한다고 한다.

    그리고 쿠버네티스가 container runtime과의 의사소통을 위해서 Container Runtime Interface (CRI)를 사용한다고 한다.

    우리 Docker도 CRI를 기준으로 잘 만들어 놨을 것이다.

     

    그리고 kubeadmUnix domain sockets 라는 네트워크를 사용하지 않고, 내부의 파일을 이용한 통신 socket을 사용하여 자동으로  container runtime을 찾는다고한다.  찾는데 이용하는 잘 알려진 socket 경로도 알려주고 있다.

     

    또한 Docker와 containerd가 같이 있으면 Docker를 우선한다고한다. 이유를 보면 Docker 18.09버전이 내부에 containerd를 사용하기 때문이란다. 오직 이 경우만 예외로 Docker가 우선하고 서로다른 runtimes가 설치해있으면 에러난다고 하니 참고하자.

    ps. 이전 챕터에 docker-ce를 설치할 때 containerd.io 패키지를 설치하는것으로 보아 이녀석이 docker 내부에 자동으로 설치되는 containerd와 통신하는것으로 추측했었는데 맞는 것같다.

     

    kubelet은 dockershim 이라는 CRI를 구현한 인터페이스를 통해 Docker와 통합한다고 한다.

     


    Installing kubeadm, kubelet and kubectl

    You will install these packages on all of your machines:

    • kubeadm: the command to bootstrap the cluster.

    • kubelet: the component that runs on all of the machines in your cluster and does things like starting pods and containers.

    • kubectl: the command line util to talk to your cluster.


    kubeadm - cluster환경을 만들어내는 녀석

    kubelet - cluster환경에 떠있는 pods, containers를 실행하는 녀석

    kubectl - cluster를 조작하는데 필요한 util

     


    kubeadm will not install or manage kubelet or kubectl for you, so you will need to ensure they match the version of the Kubernetes control plane you want kubeadm to install for you. If you do not, there is a risk of a version skew occurring that can lead to unexpected, buggy behaviour. However, one minor version skew between the kubelet and the control plane is supported, but the kubelet version may never exceed the API server version. For example, kubelets running 1.7.0 should be fully compatible with a 1.8.0 API server, but not vice versa.


    버전이 상이할 시 문제점들이 많다는 이야기인 것 같다.

    그래서 우리가 쿠버네티스를 설치할 때 kubeadm 라는 도구를 이용해서 설치를 할 것인데 이때 자동으로 kubelet 또는 kubectl을 설치, 관리를 해주지 않는다고한다.

    kubeadm을 통해 cluster환경 구성할 때, 안에 control plane의 버전과 kubelet 또는 kubectl의 버전이 match가 돼야하기 때문이란다.

    kubelet 버전은 절대 API 서버 (in control plane) 버전을 넘어설 수 없다고 한다.

     

    버전 매칭 관련해서는 아래를 참조하자.

    For more information on version skews, see:

     


    cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
    enabled=1
    gpgcheck=1
    repo_gpgcheck=1
    gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
    exclude=kubelet kubeadm kubectl
    EOF

    kubernetes repo를 등록하는 과정이다.

    exclude에 kubelet kubeadm kubectl 지정하여 패키지 제외한 것이 눈에 보이는데 

    yum을 이용하여 update나 upgrade할 때 버전이 중요해서 우리가 직접 control 하라고 제외해놓은 것 같다.

     


    # Set SELinux in permissive mode (effectively disabling it)
    sudo setenforce 0
    sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
    
    sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
    
    sudo systemctl enable --now kubelet

    Notes:

    • Setting SELinux in permissive mode by running setenforce 0 and sed ... effectively disables it. This is required to allow containers to access the host filesystem, which is needed by pod networks for example. You have to do this until SELinux support is improved in the kubelet.

    • You can leave SELinux enabled if you know how to configure it but it may require settings that are not supported by kubeadm.

    The kubelet is now restarting every few seconds, as it waits in a crashloop for kubeadm to tell it what to do.


    도커에 의해 실행되는 containers가 host filesystem에 접근할 수 있도록 SELinux 설정을 변경하라고 한다.

    그리고 설치 후, 부팅 시 자동으로 kubelet을 자동으로 실행하도록 enable!

     

    이 이후에 kubelet 서비스가 떳는지 확인해보면

    계속 activating 상태인것을 확인할 수 있다.

    SELinux 변경사항이 아직 동작을 안하는건지 뭔지... 

    조금 후에 해결방법이 나오니 아래로 더 내려가자.

     


    Configure cgroup driver used by kubelet on control-plane node

    When using Docker, kubeadm will automatically detect the cgroup driver for the kubelet and set it in the /var/lib/kubelet/config.yaml file during runtime.

    If you are using a different CRI, you must pass your cgroupDriver value to kubeadm init, like so:

    apiVersion: kubelet.config.k8s.io/v1beta1
    kind: KubeletConfiguration
    cgroupDriver: <value>

    우리가 Docker를 사용하고 있다면, kubeadm가 Docker데몬에서 우리가 설정한 cgroup driver (systemd)를 자동으로 발견하고 kubelet이 실행되면 config.yaml에 cgroup driver를 자동으로 셋해준다고 한다.

    처음에 이걸 읽고 "그래서 그 다음은 뭘하라는건데?" 라는 생각이 들었고 kubeadm init 를 하라는건지도 몰랐다.

    일단 kubeadm init를 쳤다.

    kubeadm init

    ---- 아래는 로그 ----

    더보기
    W1111 18:30:24.795030    9055 configset.go:348] WARNING: kubeadm cannot validate component configs for API groups [kubelet.conf                                                                                                              ig.k8s.io kubeproxy.config.k8s.io]
    [init] Using Kubernetes version: v1.19.3
    [preflight] Running pre-flight checks
            [WARNING Firewalld]: firewalld is active, please ensure ports [6443 10250] are open or your cluster may not function co                                                                                                              rrectly
            [WARNING Hostname]: hostname "kube-master" could not be reached
            [WARNING Hostname]: hostname "kube-master": lookup kube-master on 192.168.5.254:53: no such host
    [preflight] Pulling images required for setting up a Kubernetes cluster
    [preflight] This might take a minute or two, depending on the speed of your internet connection
    [preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
    [certs] Using certificateDir folder "/etc/kubernetes/pki"
    [certs] Generating "ca" certificate and key
    [certs] Generating "apiserver" certificate and key
    [certs] apiserver serving cert is signed for DNS names [kube-master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.5.1]
    [certs] Generating "apiserver-kubelet-client" certificate and key
    [certs] Generating "front-proxy-ca" certificate and key
    [certs] Generating "front-proxy-client" certificate and key
    [certs] Generating "etcd/ca" certificate and key
    [certs] Generating "etcd/server" certificate and key
    [certs] etcd/server serving cert is signed for DNS names [kube-master localhost] and IPs [192.168.5.1 127.0.0.1 ::1]
    [certs] Generating "etcd/peer" certificate and key
    [certs] etcd/peer serving cert is signed for DNS names [kube-master localhost] and IPs [192.168.5.1 127.0.0.1 ::1]
    [certs] Generating "etcd/healthcheck-client" certificate and key
    [certs] Generating "apiserver-etcd-client" certificate and key
    [certs] Generating "sa" key and public key
    [kubeconfig] Using kubeconfig folder "/etc/kubernetes"
    [kubeconfig] Writing "admin.conf" kubeconfig file
    [kubeconfig] Writing "kubelet.conf" kubeconfig file
    [kubeconfig] Writing "controller-manager.conf" kubeconfig file
    [kubeconfig] Writing "scheduler.conf" kubeconfig file
    [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
    [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
    [kubelet-start] Starting the kubelet
    [control-plane] Using manifest folder "/etc/kubernetes/manifests"
    [control-plane] Creating static Pod manifest for "kube-apiserver"
    [control-plane] Creating static Pod manifest for "kube-controller-manager"
    [control-plane] Creating static Pod manifest for "kube-scheduler"
    [etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
    [wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
    [kubelet-check] Initial timeout of 40s passed.
    [apiclient] All control plane components are healthy after 67.005762 seconds
    [upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
    [kubelet] Creating a ConfigMap "kubelet-config-1.19" in namespace kube-system with the configuration for the kubelets in the cluster
    [upload-certs] Skipping phase. Please see --upload-certs
    [mark-control-plane] Marking the node kube-master as control-plane by adding the label "node-role.kubernetes.io/master=''"
    [mark-control-plane] Marking the node kube-master as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
    [bootstrap-token] Using token: mf7zll.wu46xeb4zvaprjbs
    [bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
    [bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
    [bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
    [bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
    [bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
    [bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
    [kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
    [addons] Applied essential addon: CoreDNS
    [addons] Applied essential addon: kube-proxy
    
    Your Kubernetes control-plane has initialized successfully!
    
    To start using your cluster, you need to run the following as a regular user:
    
      mkdir -p $HOME/.kube
      sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
      sudo chown $(id -u):$(id -g) $HOME/.kube/config
    
    You should now deploy a pod network to the cluster.
    Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
      https://kubernetes.io/docs/concepts/cluster-administration/addons/
    
    Then you can join any number of worker nodes by running the following on each as root:
    
    kubeadm join 192.168.5.1:6443 --token mf7zll.wu46xeb4zvaprjbs \
        --discovery-token-ca-cert-hash sha256:43706957df8a96e04fe57ab21c42396c64c1e88dca6ed9f692f712e64f0d1730​

     

    실행을 시키니 이렇게 로그가 보이면서 설정파일 생성kubelet이 실행되었다.

    kubeadm라는 도구를 이용하여 쿠버네티스 클러스터를 셋업한다고 했었는데 위의 로그에 상세하게 나오고 있다.

    kubelet-start, control-plane, etcd, addons로 추가된 kube-proxy 등등 (1)에서 봤던 쿠버네티스 클러스터 환경의 이미지에 나오는 요소들이 보인다.

     

    kubeadm init로 인해 생성된 /var/lib/kubelet/config.yaml를 확인해보니

    apiVersion: kubelet.config.k8s.io/v1beta1
    authentication:
      anonymous:
        enabled: false
      webhook:
        cacheTTL: 0s
        enabled: true
      x509:
        clientCAFile: /etc/kubernetes/pki/ca.crt
    authorization:
      mode: Webhook
      webhook:
        cacheAuthorizedTTL: 0s
        cacheUnauthorizedTTL: 0s
    cgroupDriver: systemd
    clusterDNS:
    - 10.96.0.10
    clusterDomain: cluster.local
    cpuManagerReconcilePeriod: 0s
    evictionPressureTransitionPeriod: 0s
    fileCheckFrequency: 0s
    healthzBindAddress: 127.0.0.1
    healthzPort: 10248
    httpCheckFrequency: 0s
    imageMinimumGCAge: 0s
    kind: KubeletConfiguration
    logging: {}
    nodeStatusReportFrequency: 0s
    nodeStatusUpdateFrequency: 0s
    rotateCertificates: true
    runtimeRequestTimeout: 0s
    staticPodPath: /etc/kubernetes/manifests
    streamingConnectionIdleTimeout: 0s
    syncFrequency: 0s
    volumeStatsAggPeriod: 0s
    apiVersion: kubelet.config.k8s.io/v1beta1
    kind: KubeletConfiguration
    cgroupDriver: systemd

    이렇게 설정파일이 set 된것을 확인할 수 있었다!

     

    우리가 위에서 Docker데몬의 cgroup driver를 systemd로 설정해놓았기 때문에 자동적으로 kubelet도 같은 드라이버를 사용하도록 한 것 같다.

     


    Please mind, that you only have to do that if the cgroup driver of your CRI is not cgroupfs, because that is the default value in the kubelet already.


    이렇게 cgroupfs를 cgroup driver 사용하지 않으면 위에 /var/lib/kubelet/config.yaml 파일에다가 

    apiVersion: kubelet.config.k8s.io/v1beta1
    kind: KubeletConfiguration
    cgroupDriver: systemd

    위와같이 설정 후 kubeadm init 를 하라는 것 같은데 위에서 우리는 systemd 를 셋업 해놓았기 때문에 그냥 진행해도 무리가 없을 것 같다.

     

    만약 cgroupDriver의 변경이 필요하다면 config.yaml을 수정하고

    systemctl daemon-reload
    systemctl restart kubelet

    위의 커맨드를 입력 하도록 하자.

     


    ps. 챕터 2가 끝났다 설치만 했는데도 이해를 하면서 설치를 하려니 너무 힘들었다.

    다음 챕터부턴 쿠버네티스 클러스터를 구성할 차례이다!! ㄱㄱ

     

    댓글

Designed by Tistory.