再探使用kubeadm部署高可用的k8s集羣-01引言

再探使用kubeadm部署高可用的k8s集羣-01引言

2018/4/24

提示

僅供測試用途
前言:
高可用一直是重要的話題,需要持續研究。
最近關注到 k8s 官網文檔有更新,其中一篇部署高可用集羣的文章思路不錯,簡潔給力,希望能分享給有需要的小夥伴一起研究下。

資源

  • k8s node
    • master-100, 10.222.0.100
    • master-101, 10.222.0.101
    • master-102, 10.222.0.102
    • LB, 10.222.0.88
    • master-100, master-101, master-102
    • worker-201, 10.222.0.201
    • worker-202, 10.222.0.202
  • k8s version
    • v1.9.0
  • 步驟

    • 配置 hosts, docker, k8s 服務
    • 部署 etcd 集羣
    • 配置 k8s master
    • 配置 k8s worker
  • 附加
    • 網絡
    • 在 master 上更新 kube-proxy(註明:未完成,下述內容待驗證)

部署 etcd 集羣

  • 有2種方式可供選擇

    • 在 3 個獨立的 vm 上部署
    • 複用 3 個 k8s master 節點(本文)
    • 配置 hosts
    • 配置 docker 訪問
    • 安裝 k8s 服務
  • 基本步驟
    • 準備工作
    • 創建 etcd CA 證書
    • 創建 etcd client 證書
    • 同步 ca 和 client 的證書相關文件到另外 2 個節點
    • 創建 server 和 peer 證書(所有節點上操作)
    • 創建 etcd 服務對應到 systemd 配置(所有節點上操作)
準備工作
##### 配置節點之間的 ssh 登錄(略)
##### 準備 docker, k8s 相關的 rpm 包 和鏡像(略)
> 使用kubeadm部署k8s集羣00-緩存gcr.io鏡像
> 使用kubeadm部署k8s集羣01-初始化

##### 準備工具 cfssl, cfssljson, etcd, etcdctl(所有節點上需要)
curl -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x /usr/local/bin/cfssl*

##### 下載 etcd 和 etcdctl
export ETCD_VERSION=v3.1.10
curl -sSL https://github.com/coreos/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz | tar -xzv --strip-components=1 -C /usr/local/bin/
rm -rf etcd-$ETCD_VERSION-linux-amd64*

##### 同步到另外2個節點
rsync -avzP /usr/local/bin/* 10.222.0.101:/usr/local/bin/
rsync -avzP /usr/local/bin/* 10.222.0.102:/usr/local/bin/
創建 etcd CA 證書
##### 在 master-100 上操作
mkdir -p /etc/kubernetes/pki/etcd
cd /etc/kubernetes/pki/etcd/

cat >ca-config.json <<_EOF
{
    "signing": {
        "default": {
            "expiry": "43800h"
        },
        "profiles": {
            "server": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            },
            "client": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "client auth"
                ]
            },
            "peer": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            }
        }
    }
}
_EOF

cat >ca-csr.json <<_EOF
{
    "CN": "etcd",
    "key": {
        "algo": "rsa",
        "size": 2048
    }
}
_EOF

##### 生成 CA 證書
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

##### 輸出
ca.pem
ca-key.pem
創建 etcd client 證書
cat >client.json <<_EOF
{
    "CN": "client",
    "key": {
        "algo": "ecdsa",
        "size": 256
    }
}
_EOF

##### 生成 client 證書
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client

##### 輸出
client.pem
client-key.pem
同步 ca 和 client 的證書相關文件到另外 2 個節點
rsync -avzP /etc/kubernetes/pki 10.222.0.101:/etc/kubernetes/
rsync -avzP /etc/kubernetes/pki 10.222.0.102:/etc/kubernetes/
創建 server 和 peer 證書(所有節點上操作)
##### 設置環境變量
export PEER_NAME=$(ip addr show eth0 | grep -Po 'inet \K[\d.]+' |awk -F'.' '{print "master-"$4}')
export PRIVATE_IP=$(ip addr show eth0 | grep -Po 'inet \K[\d.]+')

cfssl print-defaults csr > config.json
sed -i '0,/CN/{s/example\.net/'"$PEER_NAME"'/}' config.json
sed -i 's/www\.example\.net/'"$PRIVATE_IP"'/' config.json
sed -i 's/example\.net/'"$PEER_NAME"'/' config.json

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server config.json | cfssljson -bare server
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer config.json | cfssljson -bare peer

創建 etcd 服務對應到 systemd 配置(所有節點上操作)


##### 準備 etcd 服務依賴的環境變量
echo "PEER_NAME=$PEER_NAME" > /etc/etcd.env
echo "PRIVATE_IP=$PRIVATE_IP" >> /etc/etcd.env

##### 準備 etcd 服務的配置文件
cat >/etc/systemd/system/etcd.service <<_EOF
[Unit]
Description=etcd
Documentation=https://github.com/coreos/etcd
Conflicts=etcd.service
Conflicts=etcd2.service

[Service]
EnvironmentFile=/etc/etcd.env
Type=notify
Restart=always
RestartSec=5s
LimitNOFILE=40000
TimeoutStartSec=0

ExecStart=/usr/local/bin/etcd --name ${PEER_NAME} \
    --data-dir /var/lib/etcd \
    --listen-client-urls https://${PRIVATE_IP}:2379 \
    --advertise-client-urls https://${PRIVATE_IP}:2379 \
    --listen-peer-urls https://${PRIVATE_IP}:2380 \
    --initial-advertise-peer-urls https://${PRIVATE_IP}:2380 \
    --cert-file=/etc/kubernetes/pki/etcd/server.pem \
    --key-file=/etc/kubernetes/pki/etcd/server-key.pem \
    --client-cert-auth \
    --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem \
    --peer-cert-file=/etc/kubernetes/pki/etcd/peer.pem \
    --peer-key-file=/etc/kubernetes/pki/etcd/peer-key.pem \
    --peer-client-cert-auth \
    --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem \
    --initial-cluster master-100=https://10.222.0.100:2380,master-101=https://10.222.0.101:2380,master-102=https://10.222.0.102:2380 \
    --initial-cluster-token my-etcd-token \
    --initial-cluster-state new

[Install]
WantedBy=multi-user.target

_EOF

##### 激活 etcd 服務
systemctl daemon-reload
systemctl enable etcd

##### 啓動
systemctl start etcd
systemctl status etcd

##### 測試
export ETCDCTL_DIAL_TIMEOUT=3s
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.pem
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/client.pem
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/client-key.pem
export ETCDCTL_API=3
export ENDPOINTS="https://10.222.0.100:2379,https://10.222.0.101:2379,https://10.222.0.102:2379"

etcdctl --endpoints=${ENDPOINTS} -w table endpoint status
etcdctl --endpoints=${ENDPOINTS} put foo bar
etcdctl --endpoints=${ENDPOINTS} get --prefix ''

配置 k8s master

初始化 master-100
mkdir -p ~/k8s_install/master/init
cd ~/k8s_install/master/init

##### 備份 etcd 證書(該 etcd 集羣的所有節點都要備份自己的證書)
cp -a /etc/kubernetes/pki/etcd ~/k8s_install/master/init/
##### 後續如果 kubeadm reset 將導致 pki 目錄被清空,此時可以恢復證書
cp -a ~/k8s_install/master/init/etcd /etc/kubernetes/pki/

##### 準備配置用於初始化
export PRIVATE_IP=$(ip addr show eth0 | grep -Po 'inet \K[\d.]+')

cat >config.yaml <<_EOF
apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
api:
  advertiseAddress: ${PRIVATE_IP}
etcd:
  endpoints:
  - https://10.222.0.100:2379
  - https://10.222.0.101:2379
  - https://10.222.0.102:2379
  caFile: /etc/kubernetes/pki/etcd/ca.pem
  certFile: /etc/kubernetes/pki/etcd/client.pem
  keyFile: /etc/kubernetes/pki/etcd/client-key.pem
networking:
  podSubnet: 172.30.0.0/16
apiServerCertSANs:
- 10.222.0.88
kubernetesVersion: v1.9.0
apiServerExtraArgs:
  endpoint-reconciler-type: lease
_EOF

##### 開始初始化 master
kubeadm init --config=config.yaml

##### 使用 kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

##### 查看狀態
kubectl get ds,deploy,svc,pods --all-namespaces
kubectl get nodes
初始化 網絡插件之 calico
##### 關於 calico 的更多內容,請參考筆記:使用kubeadm部署k8s集羣01-初始化
[root@master-100 ~]# mkdir -p ~/k8s_install/network/calico
[root@master-100 ~]# cd ~/k8s_install/network/calico
[root@master-100 calico]# curl -so calico-v3.0.yaml https://docs.projectcalico.org/v3.0/getting-started/kubernetes/installation/hosted/kubeadm/1.7/calico.yaml

##### 所有節點提前準備好鏡像,類似下述實例中,緩存 `calico-v3.0.yaml` 使用的鏡像,並打包同步到集羣所有節點上
[root@master-100 calico]# grep 'image' calico-v3.0.yaml |uniq |sed -e 's#^.*image: quay.io#docker pull quay.io#g'
docker pull quay.io/coreos/etcd:v3.1.10
docker pull quay.io/calico/node:v3.0.4
docker pull quay.io/calico/cni:v2.0.3
docker pull quay.io/calico/kube-controllers:v2.0.2

[root@master-100 calico]# docker save -o calico-v3.0.tar quay.io/coreos/etcd:v3.1.10 quay.io/calico/node:v3.0.4 quay.io/calico/cni:v2.0.3 quay.io/calico/kube-controllers:v2.0.2
[root@master-100 calico]# scp calico-v3.0.tar 10.222.0.100:~/k8s_install/network/calico/
[root@master-102 calico]# docker load -i ~/k8s_install/network/calico/calico-v3.0.tar

##### 注意 CALICO_IPV4POOL_CIDR 是否和 podSubnet 一致,默認的需要替換:
[root@master-100 calico]# sed -i 's#192.168.0.0/16#172.30.0.0/16#' calico-v3.0.yaml

##### 啓用
[root@master-100 calico]# kubectl apply -f calico-v3.0.yaml

##### 驗證網絡正常的方法是:檢查 deploy/kube-dns 是否上線

[警告]


來自今天(2018-04-24)的實驗結果:
從下一步開始,加入多個 master 節點後,通過驗證 `calico` 和 `kube-dns` 並不能表明網絡可用。

實際上,需要驗證業務,因爲我在實驗過程中發現一個問題:

業務 t1 部署後,運行在節點 worker-201 上,此時能通過 worker-201 的 ip:port 訪問,但無法通過集羣的其他節點來訪問

小結: 新的 master 節點在加入過程中,對 iptables 規則的變化產生了影響。可能原因是:新的服務上線後,更新了節點 worker-201 上的 iptables 規則,但遇到權限異常,無法寫入 calico-etcd 集羣中,從而更新其他節點上的 iptables 規則

訴求: 請大家仔細驗證業務,確認網絡是否有異常!!後續將抽空研究該問題。


##### 初始化 master-101 master-102
> 將 master-101 加入集羣( 因 master-102 的操作同理,略過)
>  同步在 master-100 上生成的 pki 相關的證書文件(除了 etcd 這個目錄由於已經存在不會被 scp 同步),實際上,我們只需要重建 `apiserver.*` 相關的證書
```bash
scp 10.222.0.100:/etc/kubernetes/pki/* /etc/kubernetes/pki
rm apiserver.*

接下來,重複前述操作中,初始化 master-100 的指令即可將該節點加入集羣。

提示:只是同步 ca.*sa.* 證書(照着 Option 2: Copy paste 描述的操作)可能是錯誤的,至少在我的實驗環境中,遇到了異常,導致 kube-dns 無法上線,如果你也遇到類似的場景,請仔細查看 apiserver 的 pod 的日誌輸出是否有異常。

如果需要重置 kubeadm 初始化的節點(例如,重做一次)
##### 這裏以清理 master-102 這個節點爲例說明:
##### 先在 master-100 上操作
kubectl drain master-102 --delete-local-data --force --ignore-daemonsets
kubectl delete node master-102

##### 然後在 master-102 上操作
kubeadm reset

##### 還原 etcd 相關的證書(因爲 master-102 剛好也是 etcd 集羣的節點)
cp -a ~/k8s_install/master/init/etcd /etc/kubernetes/pki/

配置 k8s worker

##### 加入集羣
kubeadm join --token xxx.xxxxxxx 10.222.0.100:6443 --discovery-token-ca-cert-hash sha256:xxx

[警告]

(註明:未完成,下述內容待驗證)

配置 worker 使用 kube-proxy 時通過 LB 來訪問後端高可用的 apiserver 服務

kubectl get configmap -n kube-system kube-proxy -o yaml > kube-proxy-cm.yaml
sed -i 's#server:.*#server: https://10.222.0.88:6443#g' kube-proxy-cm.yaml
kubectl apply -f kube-proxy-cm.yaml --force
kubectl delete pod -n kube-system -l k8s-app=kube-proxy

##### 更新 worker 上 kubelet 服務
sed -i 's#server:.*#server: https://10.222.0.88:6443#g' /etc/kubernetes/kubelet.conf
systemctl restart kubelet

上述 kube-proxy 是一個 daemonset 類型的服務,也就是說 master 節點上也會有該服務,此時思考下述數據流向是否會有異常:

kube-proxy(on master-100) -> LB(backend to master-100)

上述場景,如果是 LVS/DR 模式的 LB 則意味着

RS1 -> LB1 -> RS1
導致:
RS1 -> RS1

引用來自阿里雲 SLB 的文檔片段(實際遇到的一個坑)

  1. 後端ECS實例爲什麼訪問不了負載均衡服務?
    這和負載均衡TCP的實現機制有關。在四層TCP協議服務中,不支持後端ECS實例既作爲Real Server又作爲客戶端向所在的負載均衡實例發送請求。因爲返回的數據包只在雲服務器內部轉發,不經過負載均衡,所以在後端ECS實例上去訪問負載均衡的服務地址是不通的。

結論1:如果從 master 節點的 IP 來訪問 k8s 中的訪問,可能出現異常。
結論2:僅將 worker 節點的 IP 添加到 SLB 中作爲後端,預計能避免上述異常。

ZYXW、參考

  1. Creating HA clusters with kubeadm
  2. 阿里雲-SLB-後端服務器常見問題-後端ECS實例爲什麼訪問不了負載均衡服務?
  3. Two bug fixes to HA guide for kubeadm: #7451
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章