集群信息

1. 节点规划

部署k8s集群的节点按照用途可以划分为如下3类角色:

  • master :集群的master节点,至少一台
  • slave:集群的slave节点,非必须存在
  • init:集群的初始化节点,提供集群初始化必要的镜像及软件源,可以与master节点使用同一台机器

单个节点可以对应多个角色,比如master节点可以同时作为init节点。因为slave节点不是必须存在,因此部署非高可用集群最少仅需一台机器即可,基础配置不低于2C4G。

本例为了演示slave节点的添加,会部署一台master+1台slave,其中master和init共用一台机器,节点规划如下:

主机名 节点ip 角色 部署组件
k8s-init 10.0.129.84 init registry, httpd
k8s-slave 10.0.128.240 slave kubectl, kubeadm, kubelet, kube-proxy, flannel
k8s-master 10.0.129.84 master etcd, kube-apiserver, kube-controller-manager, kubectl, kubeadm, kubelet, kube-proxy, flannel

2. 组件版本

组件 版本 说明
CentOS 7.5.1804
Kernel Linux 3.10.0-862.el7.x86_64
etcd 3.2.24 使用容器方式部署,默认数据挂载到本地路径
coredns 1.2.6
kubeadm v1.13.3
kubectl v1.13.3
kubelet v1.13.3
kube-proxy v1.13.3
flannel v0.11.0 使用vxlan作为backend
httpd v2.4.6 部署在init节点,默认使用80端口提供服务
registry v2.3.1 部署在init节点,默认使用60080端口提供服务

安装前准备工作

1. 设置hosts解析

操作节点:所有节点(k8s-init,k8s-master,k8s-slave)均需执行 - 修改hostname hostname必须只能包含小写字母、数字、",“、”-“,且开头结尾必须是小写字母或数字

# 在master节点
$ hostnamectl set-hostname k8s-master #设置master节点的hostname

# 在slave节点
$ hostnamectl set-hostname k8s-slave #设置slave节点的hostname
  • 添加hosts解析
$ cat >>/etc/hosts<<EOF
10.0.129.84 k8s-master k8s-init
10.0.128.240 k8s-slave
EOF

2. 调整系统配置

操作节点: 所有的master和slave节点(k8s-master,k8s-slave)需要执行

本章下述操作均以k8s-master为例,其他节点均是相同的操作(ip和hostname的值换成对应机器的真实值)

Read on →

项目中使用kubeadm 将k8s版本从v1.9.6升级到1.13.4,由于无法跨版本升级,所以大致流程是

  • v1.9.6 -> v1.10.8
  • v1.10.8 -> v1.11.5
  • v1.11.5 -> v1.12.4
  • v1.12.4 -> v1.13.4

其中操作集群A从v1.9.6v1.10.8升级过程中,执行upgrade的时候出现了如下问题:

[root@cloud-cn-master-1 ~]# kubeadm upgrade apply v1.10.8 -y
...
[upgrade/prepull] Successfully prepulled the images for all the control plane components
[upgrade/apply] Upgrading your Static Pod-hosted control plane to version "v1.10.8"...
Static pod: kube-apiserver-rz-dev-master01 hash: a82830fd687fdabd030b65ee6c4b4fd4
Static pod: kube-controller-manager-rz-dev-master01 hash: 1a23c184fadb64c889a41831476c56e8
Static pod: kube-scheduler-rz-dev-master01 hash: a0adc2bf23e7d5336ecd4677ce95938c
[upgrade/staticpods] Writing new Static Pod manifests to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests500670946"
[controlplane] Adding extra host path mount "k8s" to "kube-controller-manager"
[upgrade/staticpods] current and new manifests of kube-apiserver are equal, skipping upgrade
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-controller-manager.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2019-04-22-13-04-58/kube-controller-manager.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s)
Static pod: kube-controller-manager-rz-dev-master01 hash: 1a23c184fadb64c889a41831476c56e8
Static pod: kube-controller-manager-rz-dev-master01 hash: 1a23c184fadb64c889a41831476c56e8
Static pod: kube-controller-manager-rz-dev-master01 hash: 1a23c184fadb64c889a41831476c56e8
Static pod: kube-controller-manager-rz-dev-master01 hash: 1a23c184fadb64c889a41831476c56e8
Static pod: kube-controller-manager-rz-dev-master01 hash: 1a23c184fadb64c889a41831476c56e8
...
Read on →

  • Dockerfile中CMD与ENTRYPOINT区别

    CMD指令有如下三种用法: CMD [“executable”,“param1”,“param2”] (exec form, this is the preferred form) CMD [“param1”,“param2”] (as default parameters to ENTRYPOINT) CMD command param1 param2 (shell form)

第一种是如果想非shell运行自己的命令,需要以JSON Array的形式,且指定全路径方式,比如: FROM ubuntu CMD [“/usr/bin/wc”,“–help”] 第二种是以JSON Array的形式,配合ENTRYPOINT来使用,如果同时存在CMD和ENTRYPOINT,则CMD作为ENTRYPOINT的参数执行 第三种是使用shell form,那么command默认在/bin/sh -c下执行,如 FROM ubuntu CMD echo “This is a test.” | wc -

和ENTRYPOINT主要区别是ENTRYPOINT的优先级是高于CMD的,同时存在则以ENTRYPOINT为主,CMD作为ENTRYPOINT的运行参数

  • Dockerfile中ADD和COPY区别

    ADD支持使用URL作为,会自动下载文件并拷贝,同时支持压缩包作为并且自动解压到镜像中 COPY是保证一致 推荐使用COPY,ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢

  • 资源隔离

    NameSpace和Cgroup

  • 镜像如何实现分层

    Docker 镜像本地存储机制及容器启动原理 Docker 镜像不是一个单一的文件,而是有多层构成。我们可通过 docker images 获取本地的镜像列表及对应的元信息, 接着可通过docker history 查看某个镜像各层内容及对应大小,每层对应着 Dockerfile 中的一条指令。Docker 镜像默认存储在 /var/lib/docker/中,可通过 DOCKER_OPTS 或者 docker daemon 运行时指定 –graph= 或 -g 指定。

Docker 使用存储驱动来管理镜像每层内容及可读写的容器层,存储驱动有 DeviceMapper、AUFS、Overlay、Overlay2、Btrfs、ZFS 等,不同的存储驱动实现方式有差异,镜像组织形式可能也稍有不同,但都采用栈式存储,并采用 Copy-on-Write(CoW) 策略。且存储驱动采用热插拔架构,可动态调整。那么,存储驱动那么多,该如何选择合适的呢?大致可从以下几方面考虑: 若内核支持多种存储驱动,且没有显式配置,Docker 会根据它内部设置的优先级来选择。优先级为 AUFS > Btrfs/ZFS > Overlay2 > Overlay > DeviceMapper。若使用 DeviceMapper 的话,在生产环境,一定要选择 direct-lvm, loopback-lvm 性能非常差。 选择会受限于 Docker 版本、操作系统、系统版本等。例如,AUFS 只能用于 Ubuntu 或 Debian 系统,Btrfs 只能用于 SLES (SUSE Linux Enterprise Server, 仅 Docker EE 支持)。 有些存储驱动依赖于后端的文件系统。例如,Btrfs 只能运行于后端文件系统 Btrfs 上。 不同的存储驱动在不同的应用场景下性能不同。例如,AUFS、Overlay、Overlay2 操作在文件级别,内存使用相对更高效,但大文件读写时,容器层会变得很大;DeviceMapper、Btrfs、ZFS 操作在块级别,适合工作在写负载高的场景;容器层数多,且写小文件频繁时,Overlay 效率比 Overlay2 更高;Btrfs、ZFS 更耗内存。

Docker 容器其实是在镜像的最上层加了一层读写层,通常也称为容器层。在运行中的容器里做的所有改动,如写新文件、修改已有文件、删除文件等操作其实都写到了容器层。容器层删除了,最上层的读写层跟着也删除了,改动自然也丢失了。若要持久化这些改动,须通过 docker commit [repository[:tag]] 将当前容器保存成为一个新镜像。若想将数据持久化,或是多个容器间共享数据,需将数据存储在 Docker volume 中,并将 volume 挂载到相应容器中。

  • controller manager如何判断deployment的副本数达到预期

    判断满足deployment定义时指定的selector的matchLabels数量,每启动一个pod会有一个label和deployment match,以此来判断期望和实际数量匹配

  • kube-proxy 原理

  • python 协程

  • python迭代器、生成器

  • 读取大文件,yield

  • k8s调度详解,优选、预选

    Kubernetes Scheduler 提供的调度流程分三步:

  • 预选策略(predicate) 遍历nodelist,选择出符合要求的候选节点,Kubernetes内置了多种预选规则供用户选择。

  • 优选策略(priority) 在选择出符合要求的候选节点中,采用优选规则计算出每个节点的积分,最后选择得分最高的。
  • 选定(select) 如果最高得分有好几个节点,select就会从中随机选择一个节点。
常用的预选策略(代码里的策略不一定都会被使用)

CheckNodeConditionPred 检查节点是否正常 GeneralPred HostName(如果pod定义hostname属性,会检查节点是否匹配。pod.spec.hostname)、PodFitsHostPorts(检查pod要暴露的hostpors是否被占用。pod.spec.containers.ports.hostPort) MatchNodeSelector pod.spec.nodeSelector 看节点标签能否适配pod定义的nodeSelector PodFitsResources 判断节点的资源能够满足Pod的定义(如果一个pod定义最少需要2C4G node上的低于此资源的将不被调度。用kubectl describe node NODE名称 可以查看资源使用情况) NoDiskConflict 判断pod定义的存储是否在node节点上使用。(默认没有启用) PodToleratesNodeTaints 检查pod上Tolerates的能否容忍污点(pod.spec.tolerations) CheckNodeLabelPresence 检查节点上的标志是否存在 (默认没有启动) CheckServiceAffinity 根据pod所属的service。将相同service上的pod尽量放到同一个节点(默认没有启动) CheckVolumeBinding 检查是否可以绑定(默认没有启动) NoVolumeZoneConflict 检查是否在一起区域(默认没有启动) CheckNodeMemoryPressure 检查内存是否存在压力 CheckNodeDiskPressure 检查磁盘IO压力是否过大 CheckNodePIDPressure 检查pid资源是否过大 源码参考

优选策略

least_requested 选择消耗最小的节点(根据空闲比率评估 cpu(总容量-sum(已使用)*10/总容量) ) balanced_resource_allocation 从节点列表中选出各项资源使用率最均衡的节点(CPU和内存) node_prefer_avoid_pods 节点倾向 taint_toleration 将pod对象的spec.toleration与节点的taints列表项进行匹配度检查,匹配的条目越多,得分越低。 selector_spreading 与services上其他pod尽量不在同一个节点上,节点上通一个service的pod越少得分越高。 interpod_affinity 遍历node上的亲和性条目,匹配项越多的得分越高 most_requested 选择消耗最大的节点上(尽量将一个节点上的资源用完) node_label 根据节点标签得分,存在标签既得分,没有标签没得分。标签越多 得分越高。 image_locality 节点上有所需要的镜像既得分,所需镜像越多得分越高。(根据已有镜像体积大小之和)

  • pod的生命周期以及在销毁前想实现某些功能如何做

  • hostgw和vxlan

  • nodeport访问不了如何排查

  • k8s版本升级、etcd滚动升级、不停机、服务不中断

  • cgroup、namespace底层 如何把pid隔离起来

  • tcpdump

  • 业务实现灰度发版

  • golang协程实现原理,GOMAXPROC

  • 僵尸进程产生原因、影响

  • 二叉树

  • 创建pod流程

    1.kubectl提交创建pod命令,api响应命令,通过一系列认证授权,把pod数据存储到etcd,创建deployment资源并初始化. 2.controller通过list-watch机制,监测发现新的deployment,将该资源加入到内部工作队列,发现该资源没有关联的pod和replicaset,启用deployment controller创建replicaset资源,再启用replicaset controller创建pod. 3.所有controller正常后.将deployment,replicaset,pod资源更新存储到etcd. 4.scheduler通过list-watch机制,监测发现新的pod,经过主机过滤主机打分规则,将pod绑定(binding)到合适的主机. 5.将绑定结果存储到etcd. 6.kubelet每隔 20s(可以自定义)向kube-apiserver通过NodeName 获取自身Node上所要运行的pod清单.通过与自己的内部缓存进行比较,新增加pod. 7.启动pod启动容器. 8.把本节点的容器信息pod信息同步到etcd.

k8s的pod可以有多个副本,但是在访问pod时,会有几个问题:

  • 客户端需要知道各个pod的地址
  • 某一node上的pod如果故障,客户端需要感知

为了解决这个问题,k8s引入了service的概念,用以指导客户端的流量。

Service

以下面的my-nginx为例。

pod和service的定义文件如下:

[root@localhost k8s]# cat run-my-nginx.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
[root@localhost k8s]# cat run-my-nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

pod my-nginx定义的replicas为2即2个副本,端口号为80;service my-nginx定义的selector为run:my-nginx,即该service选中所有label为run: my-nginx的pod;定义的port为80。

Read on →

最近有客户提出需要对业务集群的pod做安全限制,不允许使用pod拥有privileged的权限,研究一番,刚好k8s的Pod Security Policies可以实现该需求。

什么是pod安全策略

Pod Security Policy(简称psp)是集群级别的资源,该资源控制pod的spec中安全相关的方面,具体的方面参考下表:

Control Aspect Field Names
Running of privileged containers privileged
Usage of host namespaces hostPID, hostIPC
Usage of host networking and ports hostNetwork, hostPorts
Usage of volume types volumes
Usage of the host filesystem allowedHostPaths
White list of Flexvolume drivers allowedFlexVolumes
Allocating an FSGroup that owns the pod’s volumes fsGroup
Requiring the use of a read only root file system readOnlyRootFilesystem
The user and group IDs of the container runAsUser, runAsGroup, supplementalGroups
The SELinux context of the container seLinux
Read on →