使用 kubeadm 部署 kubernetes HA

这篇文章记录在测试环境中部署 kubernetes 1.10 的 HA的过程。

Master 节点高可用的思路

非常感谢这个博主得文章,得以一览 k8s HA 的思路——《关于Kubernetes Master高可用的一些策略

先看下 kubernetes 的架构:

为了实现没有单点故障的目标,在 kubernetes 内部,需要为以下几个组件建立高可用方案:

  • etcd 集群的数据中心,用于存放集群的配置以及状态信息,非常重要,如果数据丢失那么集群将无法恢复;因此高可用集群部署首先就是 etcd 是高可用集群。

    如果你有3个节点,那么最多允许1个节点失效;当你有5个节点时,就可以允许有2个节点失效。

    同时,增加节点还可以让etcd集群具有更好的读性能。因为etcd的节点都是实时同步的,每个节点上都存储了所有的信息,所以增加节点可以从整体上提升读的吞吐量。

  • kube-apiserver 集群核心,集群API接口、集群各个组件通信的中枢。由于 apiserver 是无状态的,每个master节点的apiserver都是active的,并处理来自Load Balance分配过来的流量;

  • kube-controller-manager 集群状态管理器 (内部自选举)。当集群状态与期望不同时,kcm会努力让集群恢复期望状态。默认kubeadm安装情况下–leader-elect参数已经设置为true,保证master集群中只有一个kube-controller-manager处于活跃状态;

  • kube-scheduler 集群Pod的调度中心(内部自选举);默认 kubeadm 安装情况下 –leader-elect 参数已经设置为 true,保证 master 集群中只有一个 kube-scheduler 处于活跃状态;

  • kube-dns

etcd 的高可用,在前篇的文章里我已经有介绍 ——《etcd ha》。本文介绍其他几个组件的高可用。

准备

本文的操作系统环境为 CentOS Linux release 7.3.1611 (Core)

目前已在 10.19.0.55-57 上装好 etcd,并部署好 vip 10.19.0.230,接下来的目的是在55-57上安装 k8s master 作为ha。

为了保证未来安装时环境保持一致,使用 rpm 包安装 kubeadm kubectl kubelet 组件。

kubeadm-1.10.2-0.x86_64.rpm  
kubectl-1.10.2-0.x86_64.rpm  
kubelet-1.10.2-0.x86_64.rpm  
kubernetes-cni-0.6.0-0.x86_64.rpm
yum install *

安装第一台master

安装过程与先前安装 k8s集群相似——《kubernetes 安装入门(centos)》

  1. 修改 kubeadm.conf

    $ vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
    # 修改 "cgroup-driver"值 由systemd变为cgroupfs
    # 原因是 cgroup-driver参数要与docker的一致,否则就会出问题
    Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs"
    
    # 第九行增加swap-on=false
    Environment="KUBELET_EXTRA_ARGS=--fail-swap-on=false"
    
  2. 启动Docker与kubelet服务

    systemctl enable kubelet && systemctl start kubelet
    
  3. 使用 config.yaml 进行初始化配置

    apiVersion: kubeadm.k8s.io/v1alpha1
    kind: MasterConfiguration
    etcd:
      endpoints:
      - https://10.19.0.55:2379
      - https://10.19.0.56:2379
      - https://10.19.0.57:2379
      caFile: /app/allblue/etcd/ssl/ca.pem
      certFile: /app/allblue/etcd/ssl/server.pem
      keyFile: /app/allblue/etcd/ssl/server-key.pem
      dataDir: /app/allblue/etcddata
    networking:
      podSubnet: 10.244.0.0/16
    kubernetesVersion: 1.10.0
    api:
      advertiseAddress: "10.19.0.230"
    token: "b99a00.a144ef80536d4344"
    tokenTTL: "0s"
    apiServerCertSANs:
    - 10.19.0.55
    - 10.19.0.56
    - 10.19.0.57
    - 10.19.0.230
    featureGates:
      CoreDNS: true
    imageRepository: "10.18.1.230:80/k8s.gcr.io"
    

    使用上述的配置文件,如下安装第一台master:

    kubeadm init --config cfg/config.yaml --ignore-preflight-errors Swap
    
    echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> /etc/bash_profile
    echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> /etc/bashrc
    

    由于是测试环境,机器内存较为紧张,将swap关闭容易导致系统宕机,故而不按照官方推荐的关闭 swap。

    另外,在这里我们提前将安装需要的经镜像存储在本地Harbor镜像仓库中,也可以使用下面的脚本提前下载到本地:

    images=(kube-proxy-amd64:v1.10.0
    kube-scheduler-amd64:v1.10.0
    kube-controller-manager-amd64:v1.10.0
    kube-apiserver-amd64:v1.10.0
    etcd-amd64:3.1.12 pause-amd64:3.1
    kubernetes-dashboard-amd64:v1.8.3
    k8s-dns-sidecar-amd64:1.14.8
    k8s-dns-kube-dns-amd64:1.14.8
    k8s-dns-dnsmasq-nanny-amd64:1.14.8
    flannel:v0.9.1-amd64
    heapster-amd64:v1.4.2)
    for imageName in ${images[@]} ; do
      docker pull 10.18.1.230:80/k8s.gcr.io/$imageName
      docker tag 10.18.1.230:80/k8s.gcr.io/$imageName k8s.gcr.io/$imageName
      docker rmi 10.18.1.230:80/k8s.gcr.io/$imageName
    done
    
    docker tag k8s.gcr.io/flannel:v0.9.1-amd64 flannel:v0.9.1-amd64
    
  4. 安装flannel网络

    mkdir -p /etc/cni/net.d/
    cat <<EOF> /etc/cni/net.d/10-flannel.conf
    {
    "name": "cbr0",
    "type": "flannel",
    "delegate": {
    "isDefaultGateway": true
    }
    }
    EOF
    
    mkdir /usr/share/oci-umount/oci-umount.d -p
    mkdir -p /run/flannel/
    
    cat <<EOF> /run/flannel/subnet.env
    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.1.0/24
    FLANNEL_MTU=1450
    FLANNEL_IPMASQ=true
    EOF
    
    kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
    
  5. 复制 master 1 证书到另外两台机器:

    cp -r /etc/kubernetes/pki .
    

安装剩余master

  1. 将master 1 中的证书导入到本地:

    mkdir -p /etc/kubernetes/
    cp -r pki /etc/kubernetes/
    
    
  2. 其它步骤参考第一台配置即可。

  3. 检测系统可用性。

    echo "kubectl get node"
    kubectl get node
    echo ""
    echo "kubectl get cs"
    kubectl get cs
    echo ""
    echo "kubectl get po --all-namespaces"
    kubectl get po --all-namespaces
    

添加节点

在master节点查看节点添加命令。

kubeadm token create --print-join-command

扩展kube-dns

为了避免故障,要将 kube-dns的replicas值设为2或者更多,并用anti-affinity将他们部署在不同的Node节点上。

安装ingress

我使用了traefik 作为 ingress 的实现,官网 https://docs.traefik.io/,以下按照 user-guide 进行安装:

    # 创建角色和rbac绑定
    $ kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-rbac.yaml
    
    # ServiceAccount, DaemonSet(直接绑定主机端口),service
    $ kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-ds.yaml

需要注意的是,官方最近修改的 traefik-ds.yaml 修改了两个地方,并不能正常使用,可以参考左侧进行修改。

安装helm

参考文章《kubernetes helm 入门》

安装dashboard

参考文章《kubernetes 安装入门(centos)》

运行测试

运行几个ns,几个busybox,几个nginx,nslook试试dns问题。

# ns

apiVersion: v1
kind: Namespace
metadata:
  name: demo1
  labels:
    nsname: demo1
---
apiVersion: v1
kind: Namespace
metadata:
  name: demo2
  labels:
    nsname: demo2

busybox和nginx

apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox-deployment
  namespace: demo2
spec:
  selector:
    matchLabels:
      app: busybox
  replicas: 1
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
      - name: busybox
        image: busybox:1.28.4
        command: [ "/bin/sh" ]
        tty: true
      dnsConfig:
        options:
          - name: single-request-reopen
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
  namespace: demo2
spec:
  selector:
    matchLabels:
      app: nginx-test
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 80
      restartPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox-deployment
  namespace: demo1
spec:
  selector:
    matchLabels:
      app: busybox
  replicas: 1
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
      - name: busybox
        image: busybox:1.28.4
        command: [ "/bin/sh" ]
        tty: true
      dnsConfig:
        options:
          - name: single-request-reopen
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
  namespace: demo1
spec:
  selector:
    matchLabels:
      app: nginx-test
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - containerPort: 80
      restartPolicy: Always

service:

kind: Service
apiVersion: v1
metadata:
  labels:
    app: nginx-test
  name: nginx-test
  namespace: demo2
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    nodePort: 32004
  selector:
    app: nginx-test
---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: nginx-test
  name: nginx-test
  namespace: demo1
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    nodePort: 32003
  selector:
    app: nginx-test

alpha、beta、rc 各版本区别

传统软件工程上来说,对成形软件的测试有着三个阶段:alpha、beta 和gamma,用来标识测试的阶段与范围。alpha 指的是内测,即现在说的 CB,即开发团队内部测试的版本或者有限用户的体验测试版本。beta 指的是公测,即针对所有用户公开的测试版本。而做过一些修改,成为正式发布的候选版本时(现在叫做 RC - Release Candidate),叫做 gamma。

对于互联网产品来说,一般会喜欢将“beta”期做久一点,比如上面有人说的http://weibo.com,还有http://google.com及网络游戏等。这样做的原因可能有所谓“降低被喷的风险”的作用,但是实际上根本原因还是互联网产品其本身特点,因为几乎所有互联网产品都要基于用户参与,如果用户量、交互量未达到产品预期目标,就是在“beta”阶段。这是合乎软件生命周期基本规律的。

——转自知乎https://www.zhihu.com/question/20028437


etcd 高可用实践

这篇文章记录实践 etcd 的 3节点 HA的过程。分为三个步骤

  • 生成证书
  • 运行 etcd
  • 验证

关于etcd的介绍,请看下一篇文章。

生成证书

SSL/TSL 认证分单向认证和双向认证两种方式。

官方推荐使用 cfssl 来自建 CA 签发证书,以下参考官方文档的步骤https://coreos.com/os/docs/latest/generate-self-signed-certificates.html.

证书类型介绍:

  • client certificate 用于通过服务器验证客户端。例如etcdctl,etcd proxy,fleetctl或docker客户端。
  • server certificate 由服务器使用,并由客户端验证服务器身份。例如docker服务器或kube-apiserver。
  • peer certificate 由 etcd 集群成员使用,供它们彼此之间通信使用。
1,下载 cfssl
echo "下载 cfssl"
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*
2,生成 CA 证书
mkdir -p /etc/etcd/ssl
cd /etc/etcd/ssl
cat >ca-config.json <<EOF
{
    "signing": {
        "default": {
            "expiry": "87600h"
        },
        "profiles": {
            "server": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            },
            "client": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "client auth"
                ]
            },
            "peer": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            }
        }
    }
}
EOF

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

cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

请务必保证 ca-key.pem 文件的安全,*.csr 文件在整个过程中不会使用。

将会生成以下几个文件:

ca-key.pem ca.csr ca.pem

3,生成服务器端证书

``` cat >config.json «EOF { “CN”: “etcd-0”, “hosts”: [ “localhost”, “10.19.0.55”, “10.19.0.56”, “10.19.0.57” ], “key”: { “algo”: “ecdsa”, “size”: 256 }, “names”: [ { “C”: “US”, “L”: “CA”, “ST”: “San Francisco” } ] } EOF


波兰 nazwa 家的 vps 重装系统的密码问题

有在用波兰 nazwa 家的服务器的同学们要注意一下,他们的 vps 重装系统后没有密码的,要跑进控制台自行设置。这篇文章说明一下 CentOS/Debian 的设置过程,其他系统大同小异。

首先登录到VPS服务器管理面板,转到实例列表并单击Console下拉列表。然后点击蓝色的QEMU行和SendCtrlAltDel重置按钮。

CentOS

将光标设置为CentOS Linux,然后按e并转到编辑。

img

在编辑窗口中,转到以linux条目开头的行。删除参数root = UUID = e35103f0-80 …之后的信息并将其替换为以下条目:

rw init = /bin/bash

img

更改参数后,按CTRL + xF10启动系统。

  1. 检查文件系统是否以读/写(rw)模式安装。

    root @(none):/#mount | grep -w /
    
  2. 输入passwd命令并输入root帐户的新密码。

    root @(none):/#passwd
    
  3. 创建autorelabel文件。

    root @(none):/#touch /.autorelabel
    

    img

  4. 重新启动系统。

    root @(none):/#exec /sbin/reboot -f
    

Debian

在GRUB菜单中,将光标设置为Debian GNU / Linux条目,然后按e键并转到编辑。

img

在编辑窗口中,转到以linux条目开头的行。删除参数root = UUID = 8c132507-315c …之后的信息并将其替换为以下条目:

rw init = /bin/bash

img

更改参数后,选择CTRL + xF10以启动系统。

  1. 检查文件系统是否以读/写(rw)模式安装。

    root @(none):/#mount | grep -w /
    
  2. 输入passwd命令并输入root帐户的新密码。

    root @(none):/#passwd
    
  3. 创建autorelabel文件。

    root @(none):/#touch /.autorelabel
    
  4. 重新启动系统。

    root @(none):/#exec /sbin/reboot -f
    

    img


kubernetes 使用 dry-run或get manifest 查看 helm chart 的 template

今天打算在 kubernetes dashboard 中直接创建项目。想用某个chart内容来查看template信息。

第一反应是 helm inspect local/example 。实际上这个命令输出的内容是chart.yaml和values.yaml的信息,并不是我所期望的。

查阅资料之后找到两个方法可以获得。

从 chart 中查看

我本地有一个example的chart,通过如下命令查看template信息

helm install --debug --dry-run local/example

于是生成如下信息:

[debug] Created tunnel using local port: '11835'

[debug] SERVER: "127.0.0.1:11835"

[debug] Original chart version: ""
[debug] Fetched zzzk/example to /root/.helm/cache/archive/example-0.1.1.tgz

[debug] CHART PATH: /root/.helm/cache/archive/example-0.1.1.tgz

NAME:   deadly-umbrellabird
REVISION: 1
RELEASED: Mon Jun 25 22:51:49 2018
CHART: example-0.1.1
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
image:
  pullPolicy: IfNotPresent
  repository: nginx
  tag: stable
ingress:
  annotations: {}
  enabled: false
  hosts:
  - chart-example.local
  path: /
  tls: []
nodeSelector: {}
replicaCount: 1
resources: {}
service:
  port: 80
  type: ClusterIP
tolerations: []

HOOKS:
MANIFEST:

---
# Source: example/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: deadly-umbrellabird-example
  labels:
    app: example
    chart: example-0.1.1
    release: deadly-umbrellabird
    heritage: Tiller
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: example
    release: deadly-umbrellabird
---
# Source: example/templates/deployment.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: deadly-umbrellabird-example
  labels:
    app: example
    chart: example-0.1.1
    release: deadly-umbrellabird
    heritage: Tiller
spec:
  replicas: 1
  selector:
    matchLabels:
      app: example
      release: deadly-umbrellabird
  template:
    metadata:
      labels:
        app: example
        release: deadly-umbrellabird
    spec:
      containers:
        - name: example
          image: "nginx:stable"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}        

将两个source文件拷贝到 dashboard 的新建内容中即可。

从已运行的应用中查看

$ helm list
rewq ...

$ helm get manifest rewq

---
# Source: example/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: rewq-example
  labels:
    app: example
    chart: example-0.1.1
    release: rewq
    heritage: Tiller
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: example
    release: rewq
---
# Source: example/templates/deployment.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: rewq-example
  labels:
    app: example
    chart: example-0.1.1
    release: rewq
    heritage: Tiller
spec:
  replicas: 1
  selector:
    matchLabels:
      app: example
      release: rewq
  template:
    metadata:
      labels:
        app: example
        release: rewq
    spec:
      containers:
        - name: example
          image: "nginx:stable"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}