Kubernetes集群

安装

dokcer安装

安装 kubernetes 之前先需要安装 docker:
> curl -fsSL https://get.docker.com/ | sh
启动 docker,并设置为开机自启动:
> sudo systemctl enable docker
> sudo systemctl start dokcer

kubernetes安装

安装 kubernetes 的时候,需要安装 kubeletkubeadm 等包 ,我们先要更新一下 yum 源:
> sudo cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
enabled=1
gpgcheck=0
repo_gpgcheck=0
EOF
然后升级一下软件包:
> sudo yum update
下面才是重头戏,安装 kubernetes 相关的一些组件:
> sudo yum install -y kubelet kubectl kubeadm kubernetes-cni
  • kubectl:运行 kubernetes 集群命令的管理工具
  • kubelet:节点管理工具
  • kubeadm:集群自动搭建工具
  • kubernetes-cni:kubernetes容器网络接口

配置

硬件配置

系统:CentOS 7
| 角色 | 数量 | 配置 | hostname | | ------ | ---- | ------ | ---------- | | master | 1 | 2核 2G | k8s-master | | node | 1 | 1核 1G | k8s-node1 | | node | 1 | 1核 1G | k8s-node2 |

系统配置

关闭防火墙
> systemctl stop firewalld
> systemctl disable firewalld
关闭swap
> swapoff -a
修改 /etc/fstab 文件,注释掉 swap 的自动挂载。
# /etc/fstab
# Created by anaconda on Thu Aug 30 22:16:46 2018
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/centos-root / xfs defaults 0 0
UUID=507b23b4-65a7-4689-80f1-ef79d40a3a22 /boot xfs defaults 0 0
# /dev/mapper/centos-swap swap swap defaults 0 0
确认 swap 已经关闭:
> free -m
total used free shared buff/cache available
Mem: 7983 2825 4017 48 1140 4826
Swap: 0 0 0
关闭 selinux
两种方式关闭 selinux:
  • 临时关闭:
> setenforce 0
  • 永久关闭,修改 /etc/sysconfig/selinux,将 SELINUX 修改为 disabled:
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of three two values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
验证 selinux 已被禁用:
> sestatus -v
SELinux status: disabled
调整内核参数:
> sudo cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
> echo "1" > /proc/sys/net/ipv4/ip_forward
使之生效:
> sudo sysctl --system

搭建集群

docker加速

由于国内环境无法下载 kubernetes 的某些镜像,我们采用 DaoCloud 的 docker 加速来获取资源:
该脚本可以将 --registry-mirror 加入到你的 Docker 配置文件 /etc/docker/daemon.json 中,执行完成之后需要重启 docker。

下载镜像(如果可以连接VPN不需要手动下载, 部署master节点的时候可以自动下载)  , 可以编写好shell脚本来一次性执行

接下来手动下载 kubernetes 的相关镜像,下载后的镜像名改为以gcr.io/ 开头的名字,以供 kubeadm 使用:
> docker pull mirrorgooglecontainers/pause-amd64:3.1
> docker tag mirrorgooglecontainers/pause-amd64:3.1 k8s.gcr.io/pause:3.1

> docker pull warrior/etcd-amd64:3.2.18
> docker tag warrior/etcd-amd64:3.2.18 k8s.gcr.io/etcd-amd64:3.2.18

> docker pull mirrorgooglecontainers/kube-apiserver-amd64:v1.11.2
> docker tag mirrorgooglecontainers/kube-apiserver-amd64:v1.11.2 k8s.gcr.io/kube-apiserver-amd64:v1.11.2

> docker pull mirrorgooglecontainers/kube-scheduler-amd64:v1.11.2
> docker tag mirrorgooglecontainers/kube-scheduler-amd64:v1.11.2 k8s.gcr.io/kube-scheduler-amd64:v1.11.2

> docker pull mirrorgooglecontainers/kube-controller-manager-amd64:v1.11.2
> docker tag mirrorgooglecontainers/kube-controller-manager-amd64:v1.11.2 k8s.gcr.io/kube-controller-manager-amd64:v1.11.2

> docker pull mirrorgooglecontainers/kube-proxy-amd64:v1.11.2
> docker tag mirrorgooglecontainers/kube-proxy-amd64:v1.11.2 k8s.gcr.io/kube-proxy-amd64:v1.11.2

> docker pull gysan/dnsmasq-metrics-amd64:1.0
> docker tag gysan/dnsmasq-metrics-amd64:1.0 k8s.gcr.io/dnsmasq-metrics-amd64:1.0

> docker pull warrior/k8s-dns-kube-dns-amd64:1.14.1
> docker tag warrior/k8s-dns-kube-dns-amd64:1.14.1 k8s.gcr.io/k8s-dns-kube-dns-amd64:1.14.1

> docker pull warrior/k8s-dns-dnsmasq-nanny-amd64:1.14.1
> docker tag warrior/k8s-dns-dnsmasq-nanny-amd64:1.14.1 k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64:1.14.1

> docker pull warrior/k8s-dns-sidecar-amd64:1.14.1
> docker tag warrior/k8s-dns-sidecar-amd64:1.14.1 k8s.gcr.io/k8s-dns-sidecar-amd64:1.14.1

> docker pull mritd/kube-discovery-amd64:1.0
> docker tag mritd/kube-discovery-amd64:1.0 k8s.gcr.io/kube-discovery-amd64:1.0

> docker pull gysan/exechealthz-amd64:1.2
> docker tag gysan/exechealthz-amd64:1.2 k8s.gcr.io/exechealthz-amd64:1.2

> docker pull coredns/coredns:1.1.3
> docker tag coredns/coredns:1.1.3 k8s.gcr.io/coredns:1.1.3

安装 Master

kubernetes 相关镜像下载完成之后,我们就可以用 kubeadm 来一键安装 Master 节点了:
> kubeadm init --kubernetes-version=1.11.2
[init] using Kubernetes version: v1.11.2
[preflight] running pre-flight checks
I0831 17:47:34.557368 1494 kernel_validator.go:81] Validating kernel version
I0831 17:47:34.557463 1494 kernel_validator.go:96] Validating kernel config
...
Your Kubernetes master 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:

You can now join any number of machines by running the following on each node
as root:

kubeadm join 192.168.1.107:6443 --token fdnk8r.i3xnwl7r4gzjnl20 --discovery-token-ca-cert-hash sha256:9b326fbf6154498cd96288ac37f853218a7864fdff1ec3571328cc7b495500b1
安装完成后,按照提示,执行下面的命令,复制配置文件到普通用户的 home 目录下:
> mkdir -p $HOME/.kube
> sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
> sudo chown $(id -u):$(id -g) $HOME/.kube/config

安装 Node,加入集群

在 Node 节点上保证 docker  kubelet 服务已启动,执行 kubeadm join 命令,加入集群:
> kubeadm join 192.168.1.107:6443 --token fdnk8r.i3xnwl7r4gzjnl20 --discovery-token-ca-cert-hash sha256:9b326fbf6154498cd96288ac37f853218a7864fdff1ec3571328cc7b495500b1

I0910 10:47:35.211228 2988 kernel_validator.go:81] Validating kernel version
I0910 10:47:35.211346 2988 kernel_validator.go:96] Validating kernel config
...
This node has joined the cluster:
* Certificate signing request was sent to master and a response
was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.
这条命令是 kubeadm init 最后所提示的,通过这种方式我们可以将局域网中任意 Node 加入集群。
如果 Node 加入集群时报如下错误:
[discovery] Failed to request cluster info, will try again: [Get https://192.168.1.107:6443/api/v1/namespaces/kube-public/configmaps/cluster-info: x509: certificate has expired or is not yet valid]
可能是 Node 节点的时间与 Master 没有同步,执行 date 命令同步当前时间:
> date -s "2018-09-15 09:00:00"

安装网络插件

Node 加入集群后,我们去 Master 上查看下节点的情况:
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master NotReady master 38m v1.11.2
k8s-node1 NotReady <none> 14s v1.11.2
k8s-node2 NotReady <none> 8s v1.11.2
发现所有节点都处于 NotReady 状态,这是因为我们还没有安装 CNI 网络插件。
选择 weave 插件,执行以下命令进行一键安装:
> kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
serviceaccount/weave-net created
clusterrole.rbac.authorization.k8s.io/weave-net created
clusterrolebinding.rbac.authorization.k8s.io/weave-net created
role.rbac.authorization.k8s.io/weave-net created
rolebinding.rbac.authorization.k8s.io/weave-net created
daemonset.extensions/weave-net created

验证集群安装完成

> kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-78fcdf6894-fvs27 1/1 Running 0 31m
kube-system coredns-78fcdf6894-p8v9c 1/1 Running 0 31m
kube-system etcd-k8s-master 1/1 Running 0 30m
kube-system kube-apiserver-k8s-master 1/1 Running 0 30m
kube-system kube-controller-manager-k8s-master 1/1 Running 0 30m
kube-system kube-proxy-54bht 1/1 Running 0 30m
kube-system kube-proxy-cbvss 1/1 Running 0 30m
kube-system kube-proxy-r55gr 1/1 Running 0 31m
kube-system kube-scheduler-k8s-master 1/1 Running 0 30m
kube-system weave-net-6lpf9 2/2 Running 0 29m
kube-system weave-net-q5nct 2/2 Running 7 29m
kube-system weave-net-qtzwq 2/2 Running 7 29m
所有 pod 处于 Running 状态,即表示集群安装成功。

运行第一个应用

我们以 nginx 官方镜像开始我们第一个应用:
> kubectl run nginx --image=nginx:1.14.0 --replicas=2
deployment.apps/nginx created
这样就创建了一个 nginx 应用,并且建立了2个副本,我们通过命令看下:
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-deployment-957f5f978-cfw6w 1/1 Running 0 55m 10.40.0.1 k8s-node2 <none>
nginx-deployment-957f5f978-zgf62 1/1 Running 0 55m 10.46.0.2 k8s-node1 <none>
果然创建了2个 nginx 容器,处于运行状态。
我们可以通过和 docker exec 一样的方法,进入容器查看:
> kubectl exec -it nginx-7f89695fdd-7ppg2 /bin/bash
root@nginx-7f89695fdd-7ppg2:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
kubectl run 就好比 docker run ,可以直接通过命令启动容器, 同样的 kubernetes 也有 Dockerfile 的方式启动容器。
首先需要准备 nginx-deployment.yml 文件:
apiVersion: extensions/v1beta1 # 当前配置格式的版本
kind: Deployment # 要创建的资源类型
metadata: # 资源的元数据
name: nginx-deployment
spec: # 资源的规格说明
replicas: 2 # 副本数量
template: # Pod 模板
metadata: # Pod 的元数据
labels: # 标签:key 和 value,可以任意多个
app: nginx
spec: # 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性
containers:
- name: nginx
image: nginx:1.14.0
然后运行启动命令:
> kubectl apply -f nginx-deployment.yml
deployment.extensions/nginx-deployment created

Pod的介绍

pod的概念 
前面多次出现 Pod 这个词,这个 Pod 是什么?在 kubernetes 的世界里,调度的最小单位是 Pod,而非容器。
一个 Pod 封装一个应用容器(也可以有多个容器),存储资源、一个独立的网络 IP 以及管理控制容器运行方式的策略选项。Pod 代表部署的一个单位:kubernetes 中单个应用的实例,它可能由单个容器或多个容器共享组成的资源。
Pod运行的两种模式
Pod 中运行一个容器:“one-container-per-Pod” 模式是 kubernetes 最常见的用法,在这种情况下,你可以将 Pod 视为单个封装的容器,但 kubernetes 直接管理的还是 Pod。
Pod 中运行多个容器:封装紧密耦合的应用,它们需要由多个容器组成,它们之间能够共享资源。

Pod的共享资源
网络
每个 Pod 被分配一个独立的 IP 地址,Pod 中的每个容器共享网络命名空间,包括 IP 地址和网络端口。Pod 内的容器可以使用 localhost 相互通信。当 Pod 中的容器与 Pod 外部通信时,他们必须协调如何使用共享网络资源(如端口)。
存储
Pod 可以指定一组共享存储 volumes 。Pod 中的所有容器都可以访问共享 volumes ,允许这些容器共享数据。volumes 还用于 Pod 中的数据持久化,以防其中一个容器需要重新启动而丢失数据。

Pod 的特点

当 Pod 被创建后,都会被 kuberentes 调度到集群的 Node 上。直到 Pod 的进程终止、被删掉、因为缺少资源而被驱逐、或者 Node 故障之前这个 Pod 都会一直保持在那个 Node 上。
重启 Pod 中的容器跟重启 Pod 不是一回事。Pod 只提供容器的运行环境并保持容器的运行状态,重启容器不会造成 Pod 重启。
Pod 不会自愈。如果 Pod 运行的 Node 故障,或者是调度器本身故障,这个 Pod 就会被删除。同样的,如果 Pod 所在 Node 缺少资源或者 Pod 处于维护状态,Pod 也会被驱逐。kubernetes 使用更高级的称为 Controller 的抽象层,来管理 Pod 实例。虽然可以直接使用 Pod,但是在 kubernetes 中通常是使用 Controller 来管理 Pod 的。

Pod 的管理(Controler&Service)

Controler的介绍
模拟一些异常情况,Pod 因为某些异常情况而 crash
> kubectl delete pod nginx-deployment-957f5f978-cfw6w
pod "nginx-deployment-957f5f978-cfw6w" deleted
查看一下结果:
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-deployment-957f5f978-q74mb 1/1 Running 0 7m 10.40.0.3 k8s-node2 <none>
nginx-deployment-957f5f978-zgf62 1/1 Running 0 10m 10.32.0.3 k8s-node1 <none>

发现原先的 nginx-deployment-957f5f978-cfw6w 变成了 nginx-deployment-957f5f978-q74mb,为了保证副本的数量,系统又重启了一个新的 Pod 最为补充。
Node 因为某些异常情况而无法访问
k8s-node2> systemctl stop network
我们把其中一个 Node 的网络断开,过一段时间,查看结果:
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-deployment-957f5f978-zgf62 1/1 Running 0 9m 10.32.0.3 k8s-node1 <none>
nginx-deployment-957f5f978-rp4df 1/1 Running 0 17m 10.32.0.2 k8s-node1 <none>
nginx-deployment-957f5f978-q74mb 1/1 Unknown 0 20m 10.40.0.3 k8s-node2 <none>
发现原来 k8s-node2 上的 Pod 状态变成了 Unknown,并且系统在健康的 k8s-node1 上又启动了一个新的 Pod,以保证副本的数量。
            以上这些就是 Controller 魔法。可以创建和管理多个 Pod ,提供副本管理滚动升级集群级别的自愈能力
Pod常用的 Controller
Deployment
为 Pod 和 ReplicaSet 提供声明式更新:你只需要在 Deployment 中描述您想要的目标状态是什么,Deployment 就会帮您将 Pod 和 ReplicaSet 的实际状态改变到您的目标状态。您可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。
StatefulSet
有状态系统服务集:提供唯一的网络标识符 、持久化存储 、有序的部署和扩展、有序的删除和终止,以及有序的自动滚动更新。
对于具有 N 个副本的 StatefulSet,当部署Pod时,将会顺序从 {0..N-1} 开始创建。
Pods 被删除时,会从 {N-1..0} 的相反顺序终止。
在将缩放操作应用于 Pod 之前,它的所有前辈必须运行和就绪。
对 Pod 执行扩展操作时,前面的 Pod 必须都处于 Running 和 Ready 状态。
在 Pod 终止之前,所有 successors 都须完全关闭。
DaemonSet
让所有(或者一些特定)的 Node 运行同一个 Pod:实现 Only-One-Pod-Per-Node 。
每个 Node 上运行一个分布式存储的守护进程
每个 Node 上运行日志采集器或
每个 Node 上运行监控的采集端

Service的介绍

Service 可以为一组相同功能的 Pod 应用提供统一的入口地址,并将请求负载均衡分发到各个容器应用上。
Service 从逻辑上代表了一组 Pod,具体是哪些 Pod 则是由 label 来挑选。Service 有自己 IP,而且这个 IP 是不变的。客户端只需要访问 Service 的 IP,kubernetes 则负责建立和维护 Service 与 Pod 的映射关系。无论后端 Pod 如何变化,
对客户端不会有任何影响,因为 Service 没有变。
我们来看一下 Service 发布服务的类型有哪几种:

Pod常用的 Service

ClusterIp

通过集群的内部 IP 暴露服务,服务只能够在集群内部访问,这也是默认的 ServiceType。
apiVersion: v1
kind: Service
metadata:
    name: nginx-service-clusterip
    labels:
        name: nginx-service-clusterip
spec:
    type: ClusterIP
    selector:
        app: nginx
    ports:
        - port: 8081
          targetPort: 80
启动 service-clusterip:
> kubectl apply -f nginx-service-clusterip.yml
service/nginx-service-clusterip created
查看状态:
> kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10h
nginx-service-clusterip ClusterIP 10.105.115.2 <none> 8081/TCP 1m
可以看到该 Service 暴露了一个内网 IP ,以及配置文件中指定的 8081 端口,不过该服务只能在集群内部访问。

NodePort

通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。 NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 \:\,可以从集群的外部访问一个 NodePort 服务。
备注: NodePort 方式暴露服务的端口的默认范围(30000-32767)如果需要修改则在 apiserver 的启动命令里面添加如下参数 --service-node-port-range=1-65535
apiVersion: v1
kind: Service
metadata:
    name: nginx-service-nodeport
    labels:
        name: nginx-service-nodeport
spec:
    type: NodePort
    selector:
        app: nginx
    ports:
        - port: 8082
        targetPort: 80
        nodePort: 30082
启动 service-nodeport:
> kubectl apply -f nginx-service-nodeport.yml
service/nginx-service-nodeport created
查看状态:
> kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10h
nginx-service-nodeport NodePort 10.106.190.164 <none> 8082:30082/TCP 5s
我们来验证一下是否可以在集群外访问:
> curl "http://172.17.109.23:30082"
<!DOCTYPE html>
<html>
<body>
<title>Welcome to nginx!</title>
</body>
</html>

Kubernetes node 管理

Node 的隔离和恢复

apiVersion: v1
kind: Node
metadata:
name: k8s-node1
labels:
kubernetes.io/hostname: k8s-node1
spec:
unschedulable: true
注意:此配置文件指定的类型是 Node,表示是作用在 Node 上的,意思是对于后续创建的 Pod,系统将不再向该 Node 进行调度。
> kubectl replace -f unschedule_node.yml
node/k8s-node1 replaced
查看 Node 状态,可以看到在 Node 的状态中增加了一项 SchedulingDisabled
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 17m v1.11.2
k8s-node1 Ready,SchedulingDisabled <none> 16m v1.11.2
k8s-node2 Ready <none> 15m v1.11.2
同样,如果需要将某个 Node 重新纳入集群调度范围,则将 unschedulable 设置为false,再次执行 kubectl replace 命令就能恢复系统对该 Node 的调度。

Node 扩容

在实际生产系统中会经常遇到服务器容量不足的情况,这时就需要购买新的服务器,然后将应用系统进行水平扩展来完成对系统的扩容。
在 kubernetes 集群中,对于一个新 Node 的加入是非常简单的。 在新 Node 上安装好 docker、kubelet、kube-proxy等服务,然后运行加入集群命令即可:
> kubeadm join 192.168.1.107:6443 --token fdnk8r.i3xnwl7r4gzjnl20 --discovery-token-ca-cert-hash sha256:9b326fbf6154498cd96288ac37f853218a7864fdff1ec3571328cc7b495500b1
因此,这条命令是非常重要的,在集群初始化时就应该把它记录在你的小本子上。

Pod 动态扩容和缩容

在实际生产系统中,我们经常会遇到某个服务需要扩容的场景,也可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数的场景。
实际我们只要更改配置文件中的 replicas 参数:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 5
...
我们将 replicas 从 2 调整到了 5,执行操作:
> kubectl apply -f nginx-deployment.yml
deployment.extensions/nginx-deployment configured
观察结果:
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-deployment-957f5f978-2bm4c 1/1 Running 0 33m 10.40.0.1 k8s-node2 <none>
nginx-deployment-957f5f978-dq5q9 1/1 Running 0 1m 10.40.0.3 k8s-node2 <none>
nginx-deployment-957f5f978-hztv9 1/1 Running 0 1m 10.46.0.3 k8s-node1 <none>
nginx-deployment-957f5f978-v2dkl 1/1 Running 0 33m 10.46.0.2 k8s-node1 <none>
nginx-deployment-957f5f978-xmd4g 1/1 Running 0 1m 10.40.0.2 k8s-node2 <none>
是不是很方便!要缩容也是一样的操作,只是把 replicas 调小即可。

将 Pod 调度到指定的 Node

我们知道,kubernetes 的 Scheduler 服务( kube-scheduler 进程)负责实现 Pod 的调度,整个调度过程通过执行一系列复杂的算法最终为每个 Pod 计算出一个最佳的目标节点,这一过程是自动完成的,我们无法知道 Pod 最终会被调度到哪个节点上。有时我们可能需要将 Pod 调度到一个指定的Node上,此时,我们可以通过Node的标签( Label )和 Pod 的 nodeSelector 属性相匹配,来达到上述目的。
Node
apiVersion: v1
kind: Node
metadata:
name: k8s-node1
labels:
kubernetes.io/hostname: k8s-node1
diskType: ssd
更改配置:
> kubectl replace -f ssd_node.yml
node/k8s-node1 replaced
我们来看下该 Node 有哪些标签:
> kubectl describe node k8s-node1
Name: k8s-node1
Roles: <none>
Labels: diskType=ssd
kubernetes.io/hostname=k8s-node1
Annotations: node.alpha.kubernetes.io/ttl=0
CreationTimestamp: Fri, 14 Sep 2018 00:09:24 +0800
...
Pod
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ssd
spec:
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.0
nodeSelector:
diskType: ssd
添加配置:
> kubectl apply -f nginx-ssd.yml
deployment.extensions/nginx-ssd created
理想状态是,我们将 nginx-ssd 全部分配到了拥有 diskType: ssd 标签的 k8s-node1 上,我们来验证一下:
> kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-ssd-7b6f5c5d56-k6ssz 1/1 Running 0 3m 10.46.0.2 k8s-node1 <none>
nginx-ssd-7b6f5c5d56-txt9l 1/1 Running 0 3m 10.46.0.3 k8s-node1 <none>

Kubernetes版本管理

应用滚动升级

当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有 Pod,然后重新拉取镜像并启动。如果集群规模比较大,则这个工作就变成了一个挑战,而且先全部停止然后逐步升级的方式会导致较长时间的服务不可用。 kubernetes 提供了 rolling-update(滚动升级)功能来解决上述问题。
滚动升级也是通过执行 kubectl apply 命令一键完成,该命令创建了一个新的 RC,然后自动控制旧的 RC 中的 Pod 副本数量逐渐减少到 0,同时新的 RC 中的 Pod 副本数量从 0 逐步增加到目标值,最终实现了 Pod 的升级。需要注意的是,系统要求新的 RC 需要与旧的 RC 在相同的命名空间(Namespace)内,即不能把别人的资产偷偷转移到自家名下。
我们现在来将 nginx 的版本从 1.14.0 升级到 1.15.0:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 3
maxUnavailable: 2
replicas: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.0
配置文件中有几个参数需要注意:
  • strategy.minReadySeconds:用来指定没有任何容器 crash 的 Pod 并被认为是可用状态的最小秒数, 默认为0
  • strategy.type:用来指定新 Pod 替换旧 Pod 的策略,可以是 Recreate,或是 RollingUpdate(默认值)
  • strategy.rollingUpdate.maxSurge:升级过程中最多可以比原先设定所多出的pod 数量,此栏位可以为固定值或是比例(%)例如. maxSurge: 1、replicas: 5,代表Kubernetes 会先开好1 个新pod 后才删掉一个旧的pod,整个升级过程中最多会有5+1 个pod
  • strategy.rollingUpdate.maxUnavailable:最多可以有几个pod 处在无法服务的状态,当maxSurge不为零时,此栏位亦不可为零。例如. maxUnavailable: 1,代表Kubernetes 整个升级过程中最多会有1 个pod 处在无法服务的状态
> kubectl apply -f nginx-deployment-update.yml --record
deployment.extensions/nginx-deployment created
升级完毕,查看最新的版本号:
> kubectl describe deploy nginx-deployment
...
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.15.0
...

应用回滚

当集群中的某个服务升级完成,突然发现了意想不到的 BUG,需要回退到升级之前的版本,但现在所有节点的应用都已经被替换了,要回退回去这可是不小的工作量啊。对于这种情况,kubernetes 也能很轻松的搞定。前提是你之前的操作都有打上 --record参数,这样整个升级过程都能被记录下:
> kubectl rollout history deployment nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 kubectl apply --filename=nginx-deployment.yml --record=true
2 kubectl apply --filename=nginx-deployment-update.yml --record=true
观察到我们在 REVISION 2 的时候升级了我们的应用,接下来我们直接回滚到 REVISION 1:
> kubectl rollout undo deployment nginx-deployment --to-revision=1
deployment.extensions/nginx-deployment
再来看下当前应用的版本:
> kubectl describe deployment nginx-deployment
...
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.14.0
...
果然,应用已经回退到了升级之前的版本,现在可以静下心来修复 BUG 再发布了。
但如果我们在发布版本时没有打上 --record 参数,history 里是空的,这时只要原始版本的镜像还在,我们还是可以通过修改配置文件来达到回滚的目的。

刘小恺(Kyle) wechat
如有疑问可联系博主