Kubernetes 入门简明教程

本文将围绕 Docker 和 K8s 是什么,怎么部署和使用 K8s,怎么开发容器化应用,K8要 的设计逻辑和对应用架构的影响等几个方面展开。

通过本文您可以学到如下内容:

  • 了解 Docker 和 K8s 的基本原理;
  • 揭开 Docker 容器化的奥秘;
  • 了解 K8s 的安装方法;
  • 详解 K8s 应用的开发、部署方法。

什么是 Kubernetes

概述

Kubernetes,因为首尾字母中间有8个字符,所以被简写成 K8s。

K8s 是底层资源与容器间的一个抽象层,如果和单机架构类比,可以算作是一个分布式时代的 Linux。

K8s 是 Google 开源的容器集群管理系统。在 Docker 技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性。

K8s 是一个完备的分布式系统支撑平台,具有完备的集群管理能力,多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和发现机制、內建负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制以及多粒度的资源配额管理能力。同时 K8s 提供完善的管理工具,涵盖了包括开发、部署测试、运维监控在内的各个环节。

Kubernetes 特点有:

  • 可移植:支持公有云,私有云,混合云;
  • 可扩展:模块化,热插拨,可组合;
  • 自愈:自动替换,自动重启,自动复制,自动扩展。

历史

说起 K8s, 不得不先说 Docker。

Docker 前身是 dotCloud 公司, 2013年3月,容器技术定名为 Docker。

这一年,是 Docker 的奇迹年,容器技术由一个老技术突然受到热捧,Docker 腾飞。

这时的 Google 敏锐地嗅到了这股气息,基于内部多年的容器管理技术,重新编写了 K8s 并开源。

K8s 刚推出时,路走的并不平坦,先是有 Mesos 凭借其较好的成熟和稳定性碾压,后有 Docker 以其庞大的用户量捆绑式地阻击。

Docker 兴起时,Mesos 已经在资源调度和容器管理领域深耕了多年,也积累了较多的企业级客户,稳定性较好,而 K8s 1.0 版本还在赶工中,迟迟未发布。当时大量的企业客户初次选型,大家都不看好 K8s,纷纷选择了 Mesos。

并且 Docker 很快意识到了做底层容器引擎盈利很困难,也推出了 Swarm 管理引擎,并内置到 Docker 中,想凭借其庞大的用户量取胜。前期效果确实不错,一推出,凭借其简单易用免安装,很快就赢得了很多人青睐。一时间 K8s 处于了劣势地位。

但反转来的非常快,K8s 设计的实在太好,并能很好地解决用户的实际问题。从 1.0 开始,K8s 版本迭代非常快,生态快速扩大,很快就远远地把所有竞争对手甩在了后面,无论竞争对手怎么追赶也无力回天,仿佛战争的结果一开始就注定了一样。

现状

目前 K8s 最新版本已经到了 1.9.6, 越来越多的厂商加入到 K8s 阵营。

其 Github 地址为: https://github.com/kubernetes/kubernetes。

其当前关注度如下图所示:

1-42

官网截图如下:

2-29

从上图可以看到,K8s 在很多大厂得到了应用。就目前现状来看,K8s 开源后,已经迅速成为了容器管理领域的事实标准。

Kubernetes 总览

总体架构

顺手画了一个简图,细节上可能有些不对的地方,大家勉强着看。

3-24

从图上可看出,这里边有两类节点,第一类是 Master 节点,即主控节点;第二类是 Node 节点,即工作节点。

Master 节点主要负责控制和维护整个集群的状态,比如创建资源、删除资源。

Node 节点主要负责运行任务,比如启动容器。

我们如果看过其他的分布式架构,会对这种架构非常熟悉, 比如 OpenStack、Hadoop 等架构都比较类似。实际上,在分布式架构里边这种主从分离的架构,相对要比较简单和易用。

K8s 在整体架构设计上做的非常统一和优雅,比如控制逻辑只依赖于当前状态、容错处理、自动恢复和鲁棒性。

(1)控制逻辑只依赖于当前状态。

对于分布式系统来说,出现局部错误的频率非常地高,如果控制逻辑只依赖当前状态,那么就非常容易将一个暂时出现故障的系统恢复到正常状态,这无论对于开发者还是运维人员都能带来极大好处。

(2)组件的容错处理

在一个分布式系统中出现局部和临时错误是大概率事件。错误可能来自于物理系统故障,外部系统故障也可能来自于系统自身的代码错误,因此要设计对任何可能错误的容错处理。

(3)自动恢复和鲁棒性

K8s 设计成各模块间耦合性都比较松散,比如 Apiserver 异常后,也不会影响到集群业务的正常运转。K8s 设计成无论哪个组件异常,总能比较好地自动恢复。

K8s 通过资源抽象,简化运维和开发。

K8s 将集群中所有的任务抽象成资源,比如一个 Deployment 资源、一个 Pod 资源。用户面向的是一个一个资源,客户端只能通过操作这些资源来控制整个集群。这样的设计,有几个明显地好处,第一就是模型非常简单和一致,对于开发者来说,只需要理解了这种资源的模式,就很容易维护 K8s 里边的任何代码。第二就是通过资源这个抽象层,将模块与模块之间做了很好地隔离,从而很容易做到各模块之间的对接和独立升级维护。

基本概念

K8s 涉及到的基本概念非常多,这里只是简单过一下,不一个一个介绍,详细信息可以直接查文档或 Google。

  • Pod:K8s 的基本运行单元;
  • ReplicaSet:Pod 的集合;
  • Deployment:提供更新支持;
  • StatefulSets:提供有状态支持;
  • Volume:数据卷;
  • Labels:标签,资源间的关联一般通过这个来实现。

相关组件

Master 相关组件

Master 即主控节点, 相当于整个集群的大脑。

Master 提供集群的管理控制中心,通常情况下,Master 会独立部署,不在上面跑用户容器,以保证整个集群的稳定性和安全。当然可以跑多个 Master 来达到高可用。

Master 主要组件包括:Kube-apiserver、Kube-controller-manager、Etcd。

  • Kube-apiserver
    Kube-apiserver 用于暴露 Kubernetes API。任何的资源请求/调用操作都是通过 Kube-apiserver 提供的接口进行。

  • Kube-controller-manager
    Kube-controller-manager:运行管理控制器,它们是集群中处理常规任务的后台线程。逻辑上,每个控制器是一个单独的进程,但为了降低复杂性,它们都被编译成单个二进制文件,并在单个进程中运行。

  • Etcd
    Etcd 是 Kubernetes 提供默认的存储系统,保存所有集群数据。所以一般推荐使用时要为 Etcd 数据提供备份计划。

Node 相关组件

Node 即工作节点, 相当于整个集群的身体。

Node 节点负责从 Master 处接收任务并执行,适当地时候调整自己的状态或删除过期的负载。

Node 主要组件包括:Kubelet、Kube-proxy、Docker/Rkt。

  • Kubelet
    kubelet 是工作节点主要的程序,它会监视已分配给节点的 pod,具体功能包括:

    • 创建 Pod 所需的数据卷;

    • 创建 Pod 所需的网络;

    • 下载 Pod 的Secrets;

    • 启动 Pod 中运行的容器;

    • 定期执行容器健康检查;

    • 上报节点状态。

  • Kube-proxy

    Kube-proxy 通过在主机上维护网络规则并执行连接转发来实现 Kubernetes 服务抽象。

  • Docker/Rkt
    Docker/Rkt 用于运行容器。

Kubernetes 安装部署

部署目标

为了让大家更简单地部署和理解 K8s 集群,接下来我将采用纯手工的方式部署 K8s 集群,并以最小化的方式部署。整个集群将由两个节点组成,包含1个 Master 节点, 1个 Node 节点, Etcd 装在 Master 节点上。

需要提醒大家的是,如果是部署到生产环境,考虑到安全性和可靠性,建议将 Etcd 单独部署,并启用 Https。

部署整体结构如下:

4-17

相关版本

  • Kubernetes v1.9.6
  • Etcd v3.2.9
  • Docker v17.10.0-ce

资源说明

IP Address Role CPU Memory
192.168.139.190 master 8核 16G
192.168.139.189 node 8核

详细部署过程

安装 Docker

安装 Docker 比较简单,对于正常的 Unbuntu 或 Centos,Docker 提供安装脚本可以自动安装。具体命令如下:

curl -fsSL "https://get.docker.com/" | sh

安装 Etcd

为了方便,我们可以直接从 Github 上下载二进制安装。

  • 下载 Etcd
    wget https://github.com/coreos/etcd/releases/download/v3.3.2/etcd-v3.3.2-linux-amd64.tar.gz
  • 启动Etcd
    wlb@docker-sxf:~/pr-common/kubernetes/etcd-v3.3.2-linux-amd64$]$ ./etcd 
    2018-03-24 14:31:11.957802 I | etcdmain: etcd Version: 3.3.2
    2018-03-24 14:31:11.957950 I | etcdmain: Git SHA: c9d46ab37
    ...
    2018-03-24 14:31:12.963064 I | embed: ready to serve client requests
    2018-03-24 14:31:12.964414 N | embed: serving insecure client requests on 127.0.0.1:2379, this is strongly discouraged!
    

安装 Master 服务

Master 服务主要包含 kube-apiserver、kube-controller-manager、kube-scheduler,分别是 API 服务、资源控制服务和资源调度服务。

同样,Kubernetes 在 Github 上也提供二进制下载, 非常方便。

  • 下载 Master 服务
    wget https://dl.k8s.io/v1.9.6/kubernetes-server-linux-amd64.tar.gz
  • 启动 Master 服务
    (1)启动 Apiserver。
wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/server/bin$]$ sudo ./kube-apiserver --etcd-servers http://127.0.0.1:2379  --insecure-bind-address 192.168.139.190  --insecure-port 8080
I0324 14:32:22.394436   30595 server.go:121] Version: v1.9.6
I0324 14:32:22.395321   30595 services.go:35] Network range for service cluster IPs is unspecified. Defaulting to 10.0.0.0/24.
...
I0324 14:32:27.744198   30595 autoregister_controller.go:136] Starting autoregister controller
I0324 14:32:27.744224   30595 cache.go:32] Waiting for caches to sync for autoregister controller
I0324 14:32:27.844287   30595 cache.go:39] Caches are synced for autoregister controller

(2)启动 Scheduler。

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/server/bin$]$ ./kube-scheduler --master http://127.0.0.1:8080
W0324 15:04:48.923484   32516 server.go:162] WARNING: all flags than --config are deprecated. Please begin using a config file ASAP.
I0324 15:04:48.924771   32516 server.go:554] Version: v1.9.6
I0324 15:04:48.925184   32516 server.go:573] starting healthz server on 0.0.0.0:10251
...
I0324 15:04:49.846362   32516 leaderelection.go:184] successfully acquired lease kube-system/kube-scheduler

(3)启动 Controller-manager。

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/server/bin$]$ ./kube-controller-manager --master http://127.0.0.1:8080
I0324 15:03:47.245349   32414 controllermanager.go:108] Version: v1.9.6
...
I0324 15:03:47.284234   32414 controllermanager.go:494] Started "persistentvolume-binder"
I0324 15:03:47.284321   32414 pv_controller_base.go:264] Starting persistent volume controller
I0324 15:03:47.284353   32414 controller_utils.go:1019] Waiting for caches to sync for persistent volume controller

(4)查看主控状态。

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node$]$ ./kubectl cluster-info
Kubernetes master is running at http://localhost:8080


wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl get cs
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok                  
scheduler            Healthy   ok                  
etcd-0               Healthy   {"health":"true"}   

至此,整个 K8s 的 Master 服务已经部署完了,通过 kubectl get cs 命令,我们可以看到所有的主控服务都已经处于 Healthy 状态。

安装 Node 服务

前面说到 K8s 是主从式架构,主控上运行的服务比较多,而从结点相对就简单很多。

这里主要介绍部署 Pod 相关的 Kubelet 服务和 Service 相关的 Kube-proxy 服务。值得注意的是,由于 K8s 设计成插件化的网络和存储架构,所以从节点上可以按需部署各类网络插件和存储插件,篇幅限制,这里不做介绍,有兴趣可以参考官方文档。

(1)启动 Kubelet 服务

新版本的 K8s 必须要创建一个 Kubeconfig 配置文件,让 Kubelet 能接入 Master。

Kubeconfig 文件示例如下:

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ cat kubeconfig 
current-context: test-context
apiVersion: v1
clusters:
- cluster:
    api-version: v1
    server: http://127.0.0.1:8080
  name: test-cluster
contexts:
- context:
    cluster: test-cluster
    namespace: default
  name: test-context
kind: Config
preferences:
  colors: true
wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ sudo ./kubelet --fail-swap-on=false --kubeconfig ./kubeconfig  --containerd 
I0324 15:11:20.554107     516 feature_gate.go:226] feature gates: &{{} map[]}
I0324 15:11:20.554241     516 controller.go:114] kubelet config controller: starting controller
I0324 15:11:20.554258     516 controller.go:118] kubelet config controller: validating combination of defaults and flags
I0324 15:11:20.559898     516 server.go:182] Version: v1.9.6
...
I0324 15:11:24.063305     516 server.go:129] Starting to listen on 0.0.0.0:10250
I0324 15:11:24.064261     516 server.go:299] Adding debug handlers to kubelet server.
I0324 15:11:24.068791     516 fs_resource_analyzer.go:66] Starting FS ResourceAnalyzer

(2)启动 Kube-proxy 服务。

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ sudo ./kube-proxy --master http://127.0.0.1:8080
W0324 15:22:02.807447    1254 server.go:185] WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.
I0324 15:22:02.821052    1254 server_others.go:138] Using iptables Proxier.
...
I0324 15:22:02.948357    1254 controller_utils.go:1026] Caches are synced for endpoints config controller

(3)通过 kubectl get nodes 命令查看节点状态。

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/server/bin$]$ ./kubectl get nodes
NAME         STATUS    ROLES     AGE       VERSION
ubuntu       Ready     <none>    1h        v1.9.6

至此,从节点已经部署完了。是不是觉得整个部署过程非常简单呢?

Kubernetes 应用开发

K8s 集群创建完后,就可以使用它来创建我们需要的应用了。K8s 的很大一个优点是对开发者很友好,提供了大量的 API 及客户端工具,比如创建应用,最简单的方法,直接通过 Kubectl run 就可以做到。

快速创建应用

下面以一个 Nginx 应用示例来介绍。

(1)通过 kubctl run 创建应用。

通过指定容器镜像、副本数量及端口,就可以启动一个应用。

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl run nginx --image=nginx --replicas=2 --port=80 --expose=true
service "nginx" created
deployment "nginx" created
wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-7587c6fdb6-9sd9d   0/1       Pending   0          5s
nginx-7587c6fdb6-kvfsq   0/1       Pending   0          5s
wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP   51m
nginx        ClusterIP   10.0.0.40    <none>        80/TCP    11s

(2)导出服务让外部可访问.

通过 kubectl run 创建的应用,默认情况下服务是一个虚拟 IP,从集群外部访问是访问不到的,这时需要用到 NodePort 类型的服务,将节点端口映射到容器内部。

wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl expose deployment nginx --type=NodePort --name=nginx-nodeport
service "nginx-nodeport" exposed
wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     2         2         2            0           5m
wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-7587c6fdb6-9sd9d   0/1       Pending   0          5m
nginx-7587c6fdb6-kvfsq   0/1       Pending   0          5m
wlb@docker-sxf:~/pr-common/kubernetes/kubernetes/node/bin$]$ ./kubectl get svc
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes       ClusterIP   10.0.0.1     <none>        443/TCP        57m
nginx            ClusterIP   10.0.0.40    <none>        80/TCP         5m
nginx-nodeport   NodePort    10.0.0.67    <none>        80:30147/TCP   8s

(3)通过 http://192.168.139.190:30147 访问应用。

效果如下:

5-15

复杂应用开发

除了创建这种简单的单体应用,K8s 还有更强大的功能,就是它的应用编排。通过 K8s 提供的资源编排脚本, 我们可以很轻松地开发和部署分布式应用。下面以一个大数据应用 Storm 为例(参考 K8s 官方给出的一个例子), 给大家介绍如何开发一个复杂的分布式应用。

(1) 创建 ZooKeeper Pod。

在前面的章节已经跟大家介绍了 Pod 的概念, 这里可以将 Pod 理解成一个容器, 通过下面的脚本,可以启动一个 Zookeeper Pod。

注:实际生产环境,要对 Zookeeper 做好备份。

$ kubectl create -f examples/storm/zookeeper.json

{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "zookeeper",
    "labels": {
      "name": "zookeeper"
    }
  },
  "spec": {
    "containers": [
      {
        "name": "zookeeper",
        "image": "mattf/zookeeper",
        "ports": [
          {
            "containerPort": 2181
          }
        ],
        "resources": {
          "limits": {
            "cpu": "100m"
          }
        }
      }
    ]
  }
}

(2)创建 Zookeeper Service。

K8s 的 Service 可以理解成服务的概念, 一个 Service 对应多个 Pod;K8s 通过标签来关联 Service 和 Pod。

$ kubectl create -f examples/storm/zookeeper-service.json

{
  "kind": "Service",
  "apiVersion": "v1",
  "metadata": {
    "name": "zookeeper",
    "labels": {
      "name": "zookeeper"
    }
  },
  "spec": {
    "ports": [
      {
        "port": 2181
      }
    ],
    "selector": {
      "name": "zookeeper"
    }
  }
}

(3)创建 Storm-nimbus Pod。

$ kubectl create -f storm-nimbus.json
{
  "kind": "Pod",
  "apiVersion": "v1",
  "metadata": {
    "name": "nimbus",
    "labels": {
      "name": "nimbus"
    }
  },
  "spec": {
    "containers": [
      {
        "name": "nimbus",
        "image": "mattf/storm-nimbus",
        "ports": [
          {
            "containerPort": 6627
          }
        ],
        "resources": {
          "limits": {
            "cpu": "100m"
          }
        }
      }
    ]
  }
}

(4)创建 Storm-nimbus 服务。

$ kubectl create -f storm-nimbus-service.json
{
  "kind": "Service",
  "apiVersion": "v1",
  "metadata": {
    "name": "nimbus",
    "labels": {
      "name": "nimbus"
    }
  },
  "spec": {
    "ports": [
      {
        "port": 6627
      }
    ],
    "selector": {
      "name": "nimbus"
    }
  }
}

(5)创建 Storm-worker Deployment。

Deployment 是一个控制器, 通过下面的配置,实际会启动两个 storm-worker 副本。

$ kubectl create -f storm-worker-controller.yaml

apiVersion: apps/v1  
use extensions/v1beta1
kind: Deployment
metadata:
  name: storm-worker-controller
  labels:
     name: storm-worker
spec:
  replicas: 2
  selector:
     matchLabels:
        name: storm-worker
        uses: nimbus
  template:
    metadata:
      labels:
        name: storm-worker
        uses: nimbus
    spec:
      containers:
      - name: storm-worke
        image: mattf/storm-worker
        resources:
          limits:
            cpu: 200m
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 100Mi
        ports:
        - hostPort: 6700
          containerPort: 6700
        - hostPort: 6701
          containerPort: 6701
        - hostPort: 6702
          containerPort: 6702
        - hostPort: 6703
          containerPort: 6703

(6)通过上面的步骤创建完 Storm 后, 就可以直接访问了。

$  echo stat | nc 10.10.1.141 2181; echo
Zookeeper version: 3.4.6--1, built on 10/23/2014 14:18 GMT
Clients:
 /192.168.48.0:44187[0](queued=0,recved=1,sent=0)
 /192.168.45.0:39568[1](queued=0,recved=14072,sent=14072)
 /192.168.86.1:57591[1](queued=0,recved=34,sent=34)
 /192.168.8.0:50375[1](queued=0,recved=34,sent=34)

Latency min/avg/max: 0/2/2570
Received: 23199
Sent: 23198
Connections: 4
Outstanding: 0
Zxid: 0xa39
Mode: standalone
Node count: 13

上面的例子简单介绍了基于 K8s 的应用编排开发,有兴趣的读者可以进一步交流。

另外 K8s 在 Github 上给出了大量例子,也非常值得进一步学习。

如果期望对 K8s 应用编排有更深的理解, 可以查看 Chart 主页, Chart 上提供了大量可供用户一键安装的 K8s 应用。

6-11

Kubernetes 小经验

最后一些我们在 K8s 引入和使用的过程中遇到的问题和积累的一些经验,在这里分享给大家。

1.不要盲目选型,先搞清楚业务需求,再确定是否需要 K8s。

引入任何的分布系统都意味着复杂度的大幅提升,虽然 K8s 设计上非常简洁,但是对于普通开发人员来讲,其复杂度仍然不低,出了问题排障也不容易。所以建议,能单机解决的不要考虑分布式,不要为了技术而选择技术。

2.将 Etcd 单独部署。

因为 Etcd 是整个集群的存储中心,是需要小心保护的,一旦出问题,很大可能会影响整个集群的可用性。

3.尽量保证大部分的应用无状态。

尽管 K8s 提供了承载有状态应用的方案,但是有状态意味着需要额外地关心应用状态,这会给后续运维带来很大的挑战。

4.尽量保证大部分的 Node 节点可被快速替换。

意思是不要在 Node 上存太关键的数据,当 Node 故障时, 我们可以在不需要做太多额外工作的情况下将其快速将其剔除。如果需要存储状态,应当对集群分区,让不同角色的节点运行不同的应用。

(全文完)

(转载本站文章请注明作者和出处 Kubernetes 入门简明教程