lapee79's Tech Blog

lapee79의 기술 지식 창고.

Bare Metal 서버에 kubespray를 이용하여 Production 환경의 Kubernetes 클러스터 구축

Kubespray는 일반적인 OS 설정과 Kubernetes 구성/관리 작업에 관련된 Ansible playbook들과 inventory 그리고 배포 도구들을 조합한 소프트웨어입니다.

구성환경

하드웨어

  • 5 Nodes: VM 또는 물리 서버
  • Memory: 8GB

  • CPU: 4Core

  • 디스크: 120GB 이상

소프트웨어

Kubernetes nodes

  • Ubuntu 18.04
  • Python
  • SSH Server
  • sudo 권한을 가진 유저

Kubespray machine

Node 네트워킹 요구사항

  • Docker 이미지들을 download하고 소프트웨어들을 설치할 수 있도록 인터넷 접속을 허용해줍니다.
  • IPv4 Forwarding 활성화합니다.
  • 배포 중에 문제가 발생하는 것을 방지하기 위해 방화벽을 비활성화합니다.

Node 준비

다음 내용들을 모든 node들에서 수행합니다.

Python 설치

Ansible이 작업을 수행하는 모든 서버는 python이 설치되어져 있어야 합니다. Ubuntu 18.04에서는 기본적으로 Python3이 설치되어져 있으므로 symbolic link를 만들어 줍니다.

sudo ln -s /usr/bin/python3 /usr/bin/python

Swap 비활성화

sudo swapoff -a
sudo sed -i '/ swap /d' /etc/fstab

key 기반 인증을 사용할 수 있도록 SSH 구성

Ansible을 실행하려는 서버에서 다음 명령어를 실행하여 RSA key pair를 생성합니다.

ssh-keygen -t rsa

public key를 모든 노드에 복사합니다.

ssh-copy-id ubuntu@<node-ip-address>

Ansible 제어 서버 구성

kubespray 구성

공식 레포지토리를 복제합니다.

git clone https://github.com/kubernetes-incubator/kubespray.git
cd kubespray

requirements.txt으로 의존성 패키지들을 설치합니다.

sudo pip install -r requirements.txt

Ansible을 실행할 유저 설정

ansible.cfg 파일에서 다음 코드를 추가합니다.

vim ansible.cfg
...
[defaults]
...
remote_user=ubuntu

Inventory 생성

cp -rfp inventory/sample inventory/prod

sample inventory를 prod로 복사합니다. 이는 임의의 이름으로 설치하려는 클러스터에 맞게 정하시면 됩니다.

inventory generator를 사용하여 Inventory를 생성합니다.

declare -a IPS=(10.211.55.42 10.211.55.43 10.211.55.44 10.211.55.45 10.211.55.46)
CONFIG_FILE=inventory/prod/hosts.yml python3 contrib/inventory_builder/inventory.py ${IPS[@]}

실행한 후에는 다음과 같이 인벤토리가 생성된 것을 확인하실 수 있습니다.

all:
  hosts:
    node1:
      ansible_host: 10.211.55.42
      ip: 10.211.55.42
      access_ip: 10.211.55.42
    node2:
      ansible_host: 10.211.55.43
      ip: 10.211.55.43
      access_ip: 10.211.55.43
    node3:
      ansible_host: 10.211.55.44
      ip: 10.211.55.44
      access_ip: 10.211.55.44
    node4:
      ansible_host: 10.211.55.45
      ip: 10.211.55.45
      access_ip: 10.211.55.45
    node5:
      ansible_host: 10.211.55.46
      ip: 10.211.55.46
      access_ip: 10.211.55.46
  children:
    kube-master:
      hosts:
        node1:
        node2:
    kube-node:
      hosts:
        node1:
        node2:
        node3:
        node4:
        node5:
    etcd:
      hosts:
        node1:
        node2:
        node3:
    k8s-cluster:
      children:
        kube-master:
        kube-node:
    calico-rr:
      hosts: {}

Kubernetes node tuning과 Kubernetes master들의 HA를 위한 haproxy 설치

Kubernetes node tuning을 위한 Ansible role을 다운로드 합니다.

cd roles/
git clone https://github.com/lapee79/ansible-role-ubuntu-tuning-for-k8s.git
mv ansible-role-ubuntu-tuning-for-k8s ubuntu-tuning-for-k8s
cd ..

tuning을 위한 playbook을 생성합니다.

cat << EOF | tee nodes-tuning.yml
---
- hosts: all
  become: true
  roles:
    - name: ubuntu-tuning-for-k8s
      tags:
      - ubuntu
      - kubernetes
EOF

nodes-tuning playbook을 수행합니다.

ansible-playbook -i inventory/prod/hosts.yml nodes-tuning.yml

Kubernetes mastser들을 HA 구성하기 위한 haproxy Ansible role을 다운로드합니다.

cd roles/
git clone https://github.com/lapee79/ansible-role-haproxy-for-k8s-masters.git
mv ansible-role-haproxy-for-k8s-masters haproxy-for-k8s-masters

inventory 디렉토리 내의 group_vars/all/all.yml 파일에서 haproxy에서 사용할 IP 주소와 port를 설정합니다.

cd ..
vim inventory/prod/group_vars/all/all.yml
...
## External LB example config
## apiserver_loadbalancer_domain_name: "elb.some.domain"
apiserver_loadbalancer_domain_name: "10.211.55.101"
loadbalancer_apiserver:
  address: 10.211.55.101
  port: 443

haproxy 설정값들을 포함하도록 inventory 파일을 수정합니다.

all:
  hosts:
    k8s-master-01:
      ansible_host: 10.211.55.42
      ip: 10.211.55.42
      access_ip: 10.211.55.42
    k8s-master-02:
      ansible_host: 10.211.55.43
      ip: 10.211.55.43
      access_ip: 10.211.55.43
    k8s-master-03:
      ansible_host: 10.211.55.44
      ip: 10.211.55.44
      access_ip: 10.211.55.44
    k8s-worker-01:
      ansible_host: 10.211.55.45
      ip: 10.211.55.45
      access_ip: 10.211.55.45
    k8s-worker-02:
      ansible_host: 10.211.55.46
      ip: 10.211.55.46
      access_ip: 10.211.55.46
  children:
    kube-master:
      hosts:
        k8s-master-01:
          vrrp_instance_state: MASTER
          vrrp_instance_priority: 101
        k8s-master-02:
          vrrp_instance_state: BACKUP
          vrrp_instance_priority: 100
        k8s-master-03:
          vrrp_instance_state: BACKUP
          vrrp_instance_priority: 99
      vars:
        vrrp_interface: enp0s5
        vrrp_instance_virtual_router_id: 51
    kube-node:
      hosts:
        k8s-worker-01:
        k8s-worker-02:
    etcd:
      hosts:
        k8s-master-01:
        k8s-master-02:
        k8s-master-03:
    k8s-cluster:
      children:
        kube-master:
        kube-node:
    calico-rr:
      hosts: {}

Kubernetes master들에 haproxy를 설치하도록 playbook을 작성합니다..

cat << EOF | tee setup-haproxy-for-k8s-masters.yml
---
- hosts: kube-master
  become: true
  roles:
    - name: haproxy-for-k8s-masters
      tags:
      - haproxy
      - keepalived
      - kubernetes
EOF

setup-haproxy-for-k8s-masters playbook을 수행합니다.

cd ../../..
ansible-playbook -i inventory/prod/hosts.yml setup-haproxy-for-k8s-masters.yml

Kubernetes cluster 설정 변경

inventory 디렉토리 아래의 group_vars 내에는 config 파일들이 있습니다. 이 파일들을 수정하여 설정들을 변경할 수 있습니다.

vim inventory/prod/group_vars/k8s-cluster/k8s-cluster.yml
...
# Choose network plugin (cilium, calico, contiv, weave or flannel. Use cni for generic cni plugin)
# Can also be set to 'cloud', which lets the cloud provider setup appropriate routing
kube_network_plugin: calico
...
# configure arp_ignore and arp_announce to avoid answering ARP queries from kube-ipvs0 interface
# must be set to true for MetalLB to work
kube_proxy_strict_arp: true
...
# Make a copy of kubeconfig on the host that runs Ansible in {{ inventory_dir }}/artifacts
kubeconfig_localhost: true
# Download kubectl onto the host that runs Ansible in {{ bin_dir }}
# kubectl_localhost: false

vim inventory/prod/group_vars/k8s-cluster/addons.yml
...
# Kubernetes dashboard
# RBAC required. see docs/getting-started.md for access details.
dashboard_enabled: false

kubespray를 이용하여 Kubernetes 클러스터 구성

ansible playbook를 수행하여 Kubernetes 클러스터를 구성합니다.

ansible-playbook -i inventory/prod/hosts.yml cluster.yml -b -v \
  --private-key=~/.ssh/private_key

Kubernetes 클러스터 검증

Kubernetes 클러스터 구성이 완료된 후에 다음 명령어를 사용하여 구성된 클러스터를 검증할 수 있습니다.

kubectl --kubeconfig=inventory/prod/artifacts/admin.conf cluster-info
kubectl --kubeconfig=inventory/prod/artifacts/admin.conf get nodes

Kubernetes 클러스터 확장/축소

node 추가

  • inventory에 node host를 추가한 후에 kube-node 그룹에도 추가해줍니다.
  • cluster.yml대신에 scale.yml playbook을 이용하여 ansible-playbook을 수행합니다.
ansible-playbook -i inventory/prod/hosts.yml scale.yml -b -v \
  --private-key=~/.ssh/private_key

node 제거

remove-node.yml playbook을 재수행하여 클러스터에서 master, worker, etcd node를 제거할 수 있습니다. 먼저 지정된 node들이 drain된 다음 Kubernetes 서비스들을 정지하고 certificate들을 삭제한 후에 마지막으로 이 node들을 삭제하는 kubectl 명령을 수행합니다. 이는 node 추가와 결합될 수 있습니다.

제거하려는 node들은 --extra-vars "node=,"으로 값을 전달합니다.

ansible-playbook -i inventory/prod/hosts.yml remove-node.yml -b -v \
--private-key=~/.ssh/private_key \
--extra-vars "node=nodename,nodename2"

Kubernetes 업그레이드

주의

만약 기존에 deprecated API object들로 배포환경이 구축되어져 있고 Kubernetes 1.16 버젼 이상으로 업그레이드를 한다고 했을 때, 다음 설정을 통해 기존 object들을 유지할 수 있습니다.

https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/

vim roles/kubernetes/node/defaults/main.yaml
...
# deprecated runtime을 활성화하려면 다음 section의 주석을 해제합니다.
kube_api_runtime_config:
  - apps/v1beta1=true
  - apps/v1beta2=true
  - extensions/v1beta1/daemonsets=true
  - extensions/v1beta1/deployments=true
  - extensions/v1beta1/replicasets=true
  - extensions/v1beta1/networkpolicies=true
  - extensions/v1beta1/podsecuritypolicies=true

Rolling 업그레이드

클러스터 업그레이드를 수행할 때, Kubespray는 node들에 cordon, drain, uncordon을 지원합니다. 업그레이드만을 지원하기 위한 별도의 upgrade-cluster.yml playbook이 존재합니다. 이는 기존에 최소 1대 이상의 kube-master가 반드시 존재해야 한다는 것을 의미합니다.

ansible-playbook upgrade-cluster.yml -b -i inventory/prod/hosts.yml -e kube_version=v1.16.3

업그레이드가 성공했다면 Server Version이 업그레이드 된 것을 확인하실 수 있습니다.

kubectl version
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T23:42:50Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.3", GitCommit:"b3cbbae08ec52a7fc73d334838e18d17e8512749", GitTreeState:"clean", BuildDate:"2019-11-13T11:13:49Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
comments powered by Disqus