使用 Kubernetes 部署一个记事本项目

Kubernetes 简称 k8s,是 google 在 2014 年发布的一个开源项目。前端

Kubernetes 解决了哪些问题?node

真实的生产环境应用会包含多个容器,而这些容器还极可能会跨越多个服务器主机部署。Kubernetes 提供了为那些工做负载大规模部署容器的编排与管理能力。Kubernetes 编排让你可以构建多容器的应用服务,在集群上调度或伸缩这些容器,以及管理它们随时间变化的健康状态。git

  • kubernetes 基础
  • kubernetes 优化
  • kubernetes 实战

Kubernetes 基础概念不少,第一次接触容易看晕,若是是新手,建议直接看实战部分,先跑起来再说。github

kubernetes 基础

k8s 中有几个重要概念。web

概念 介绍
cluster 一个 k8s 集群
master 集群中的一台机器,是集群的核心,负责整个集群的管理和控制
node 集群中的一台机器,是集群中的工做负载节点
pod k8s 最小调度单位,每一个 pod 包含一个或多个容器
label 一个 key = value 键值对,能够附加到各类资源对象上,方便其余资源进行匹配
controller k8s 经过 controller 来管理 pod
service 对外提供统一的服务,自动将请求分发到正确的 pod 处
namespace 将 cluster 逻辑上划分红多个虚拟 cluster,每一个 cluster 就是一个 namespace

Cluster

Cluster 表明一个 k8s 集群,是计算、存储和网络资源的集合,k8s 利用这些资源运行各类基于容器的应用。docker

Master 节点

Master 是 cluster 的大脑,运行着的服务包括 kube-apiserver、kube-scheduler、kube-controller-manager、etcd 和 pod 网络。数据库

  • kube-apiserver后端

    • apiserver 是 k8s cluster 的前端接口,提供 restful api。各类客户端工具以及 k8s 其余组件能够经过它管理 cluster 中的各类资源。
  • kube-schedulerapi

    • 负责决定将 pod 放在哪一个 node 上运行,scheduler 在调度时会充分考虑 cluster 中各个节点的负载,以及应用对高可用、性能、数据亲和性的需求。
  • kube-controller-manager安全

    • 负责管理 cluster 各类资源,保证资源处理预期状态。controller-manager 由多种 controller 组成,包括 replication controller,endpoint controller,namespace controller,serviceaccount controller 等。
    • 不一样的 controller 管理不一样的资源,例如:replication controller 管理 deployment ,statefulset,damonset 的生命周期,namespace controller 资源管理 namespace 资源。
  • etcd(分布式 key-value 存储库)

    • 负责保存 cluster 的配置信息和各类资源的状态信息。当数据发生变化时,etcd 会快速地通知 k8s 相关组件。
  • pod 网络

    • pod 要可以通讯,cluster 必须部署 pod 网络,flannel 是其中一个可选方案。

Node 节点

Node 节点 是 pod 运行的地方。node 节点上运行的 k8s 组件有 kubelet、kube-proxy 和 pod 网络。

  • kubelet

    • kubelet 是 node 节点的代理,当 master 节点中 kube-scheduler 肯定在某个 node 节点上运行 pod 后,会将 pod 的具体配置信息发送给该节点的 kubelet,kubelet 根据这些信息建立和运行容器,并向 master 节点报告运行状态。
  • kube-proxy

    • 每一个 node 节点都会运行 kube-proxy 服务,它负责将访问 service 的请求转发到后端的容器。若是有多个副本,kube-proxy 会实现负载均衡。
  • pod 网络

    • pod 要可以相互通讯,k8s cluster 必须部署 pod 网络,flannel 是其中一个可选方案。

Pod

每个 pod 包含一个或多个 container,pod 中的容器做为一个总体被 master 调度到 node 节点上运行。

为何 k8s 使用 pod 管理容器,而不是直接操做容器?

一、由于有些容器天生就是须要紧密的联系,放在一个 pod 中表示一个完整的服务,k8s 同时会在每一个 pod 中加入 pause 容器,来管理内部容器组的状态。

二、Pod 中的全部容器共享 ip,共享 volume,方便进行容器之间通讯和数据共享。

何时须要在 pod 中定义多个容器?

答:这些容器联系很是紧密,并且须要直接共享资源,例如一个爬虫程序,和一个 web server 程序。web server 强烈依赖爬虫程序提供数据支持。

Label

Label 是一个 key = value 的键值对,其中 key 和 value 都由用户本身指定。

Label 的使用场景:

  • kube-controller

    • 经过 label selecter 筛选出要监控的 pod 副本,使 pod 副本数量符合预期。
  • kube-proxy

    • 经过 label selecter 选择对应的 pod,自动创建每一个 service 到对应 pod 的请求转发路由表,从而实现 service 的智能负载均衡机制。
  • 经过对 node 定义 label,在 pod 中使用 node selector 匹配,kube-scheduler 进程能够实现 pod 定向调度的特性。

总之,label 能够给对象建立多组标签,label 和 label selecter 共同构建了 k8s 系统中核心的应用模型,使得被管理对象可以被精细地分组管理,同时实现了整个集群的高可用性。

Controller

k8s 一般不会直接建立 pod,而是经过 controller 来管理 pod。controller 中定义了 pod 的部署特性,好比有几个副本,在什么样的 node 上运行等。为了知足不一样的业务场景,k8s 提供了多种类型的 controller。

  • Deployment

    • 最常使用,能够管理 pod 多个副本,并确保 pod 按照指望的状态运行,底层调用 ReplicaSet。
  • ReplicaSet

    • 实现 pod 的多副本管理,一般使用 Deployment 就够了。
  • DaemonSet

    • 用于每一个 node 最多只运行一个 pod 副本的场景。
    • 使用场景

      • 在集群的每一个节点上运行存储 Daemon,好比 glusterd 或 ceph。
      • 在每一个节点上运行日志搜集 Daemon,好比 flunentd 或 logstash。
      • 在每一个节点上运行监控,好比 Prometheus Node Exporter 或 collected。
  • StatefuleSet

    • 可以保证 pod 的每一个副本在整个生命周期中名称是不变的,而其余 controller 不提供这个功能。
  • Job

    • 用于运行结束就删除的应用,而其余 controller 中的 pod 一般是长期持续运行。
提示
使用 deployment controller 建立的用例,若是出现有 pod 挂掉的状况,会自动新建一个 pod,来维持配置中的 pod 数量。

Job

容器按照持续运行时间可分为两类:服务类容器和工做类容器。

服务类容器一般持续提供服务,须要一直运行,好比 http server。工做类容器则是一次性任务,好比批处理程序,完成后容器就退出。

Controller 中 deployment、replicaSet 和 daemonSet 类型都用于管理服务类容器,对于工做类容器,咱们使用 job。

Service

Service 是能够访问一组 pod 的策略,一般称为微服务。具体访问哪一组 pod 是经过 label 匹配出来的。service 为 pod 提供了负载均衡,原理是使用 iptables。

为何要用 service ?

  • pod 是有生命周期的,它们能够被建立,也能够被销毁,然而一旦被销毁生命就永远结束。而 pod 在一个 k8s 集群中可能常常性的建立,销毁,每一次重建都会产生一个新的 ip 地址。
  • service 从逻辑上表明了一组 pod,具体是哪些 pod 是由 label 来挑选的。service 有本身的 ip,并且这个 ip 是不变的,客户端只须要访问 service 的 ip,k8s 则负责创建和维护 service 与 pod 的映射关系,不管 pod 如何变化,对客户端不会有任何影响,由于 service 没有变。

外网如何访问 service?

  • ClusterIP:经过集群的内部 ip 暴露服务,选择该值,服务只可以在集群内部能够访问,这也是默认的 ServiceType。
  • NodePort:经过每一个 node 上的 ip 和端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动建立。经过请求 nodeip:nodeport,能够从集群的外部访问一个 service 服务。
  • LoadBalancer:使用云提供商的负载局衡器,能够向外部暴露服务。外部的负载均衡器能够路由到 NodePort 服务和 ClusterIP 服务。
  • ExternalName:经过返回 CNAME 和它的值,能够将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被建立,这只有 k8s 1.7 或更高版本的 kube-dns 才支持。

Namespace

若是有多个用户使用同一个 k8s cluster,如何将他们建立的 controller,pod 等资源分开呢?

答:使用 namespace。

若是将物理的 cluster 逻辑上划分红多个虚拟 cluster,每一个 cluster 就是一个 namespace,不一样 namespace 里的资源是彻底隔离的。

k8s 默认建立了两个 namespace。

  • default 建立资源时若是不指定,将会放到这个 namespace 中。
  • kube-system 存放 k8s 本身建立的系统资源。

Kubernetes 优化

  • 健康检查
  • 数据管理
  • 密码管理
  • 集群监控

健康检查

强大的自愈能力是 k8s 这类容器编排引擎的一个重要特性。自愈的默认实现方式是自动重启发生故障的容器。除此以外,用户还能够利用 liveness 和 readiness 探测机制设置更精细的健康检查,进而实现以下需求:

  • 零停机部署
  • 避免部署无效的镜像
  • 更加安全的滚动升级

默认状况下,只有容器进程返回值非零,k8s 才会认为容器发生了故障,须要重启。若是咱们想更加细粒度的控制容器重启,可使用 liveness 和 readiness。

liveness 和 readiness 的原理是按期检查 /tmp/healthy 文件是否存在,若是存在即认为程序没有出故障,若是文件不存在,则会采起相应的措施来进行反馈。

liveness 采起的策略是重启容器,而 readiness 采起的策略是将容器设置为不可用。

若是须要在特定状况下重启容器,可使用 liveness。
若是须要保证容器一直能够对外提供服务,可使用 readiness。

咱们能够将 liveness 和 readiness 配合使用,使用 liveness 判断容器是否须要重启,用 readiness 判断容器是否已经准备好对外提供服务。

数据管理

上文说道,pod 可能会被频繁地销毁和建立,当容器销毁时,保存在容器内部文件系统中的数据都会被清除。为了持久化保存容器的数据,可使用 k8s volume。

Volume 的生命周期独立于容器,pod 中的容器可能被销毁和重建,但 volume 会被保留。实质上 vloume 是一个目录,当 volume 被 mount 到 pod,pod 中的全部容器均可以访问到这个 volume。

Volume 支持多种类型。

  • emptyDir

    • 数据存放在 pod 中,对 pod 中的容器来讲,是持久的,只要 pod 还在数据就还在。
  • hostPath

    • 数据存在主机上,主机在数据就在。
  • AWS Elastic Block Store

    • 数据存在云服务器上。
  • Persistent Volume

    • 自定义一块外部存储空间 Persistent Volume,而后在建立 pod 时使用 PersistentVolumeClaim(PVC)去申请空间,并进行存储。

Volume 提供了对各类类型的存放方式,但容器在使用 volume 读写数据时,不须要关心数据究竟是存放在本地节点的系统中仍是云硬盘上。对容器来讲,全部类型的 volume 都只是一个目录。

密码管理

应用程序在启动过程当中可能须要一些敏感信息,好比访问数据库的用户名和密码。将这些信息直接保存在容器镜像中显然不妥,k8s 提供的解决方案是 secret。

secret 会以密文的方式存储数据,避免直接在配置文件中保存敏感信息。secret 会以 volume 的形式被 mount 到 pod,容器可经过文件的方式使用 secret 中的敏感数据,此外容器也能够按环境变量的形式使用这些数据。

使用配置文件建立 mysecret.yaml:

apiVersion: v1
kind Secret
metadata:
  name:mysecret
data:
  username:admin
  password:123

保存配置文件后,而后执行kubectl apply -f mysecret.yaml进行建立。

在 pod 中使用建立好的 secret:

# mypod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: mypod
      image: yhlben/notepad
  volumeMounts:
    - name: foo
      mountPath: 'etc/foo'
      readOnly: true
  volumes:
    - name: foo
      secret:
        secretName: mysecret

执行kubectl apply -f mypod.yaml 建立 pod,并使用 secret。建立完成后,secret 保存在容器内 /etc/foo/username ,/etc/foo/password 目录下。

集群监控

建立 k8s 集群并部署容器化应用只是第一步。一旦集群运行起来,咱们须要确保集群一切都是正常的,这就须要对集群进行监控。

经常使用的可视化监控工具以下。

  • Weave Scope
  • Heapster
  • Prometheus Operator

具体的使用步骤就直接看文档了,这里不详细说明。

经过集群监控咱们可以及时发现集群出现的问题,但为了方便进一步排查问题,咱们还须要进行进行日志记录。

经常使用的日志管理工具以下。

  • Elasticsearch 负责存储日志并提供查询接口。
  • Fluentd 负责从 k8s 搜集日志并发送给 Elasticsearch。
  • Kibana 提供一个可视化页面,用户能够浏览和搜索日志。

Kubernetes 实战

咱们来实战部署一个 k8s 记事本项目,项目使用 yhlben/notepad 镜像进行构建,该镜像在部署后会在 8083 端口上提供一个 web 版记事本服务,查看演示

为了不安装 k8s 出现的各类坑,这里使用 Play with Kubernetes进行演示。

首先在 Play with Kubernetes 上建立 3 台服务器,node1 做为 master 节点,node2 和 node3 做为工做节点。接下来进行如下操做;

  • 建立一个集群 cluster
  • 加入 node 节点
  • 初始化 cluster 网络
  • 建立 controller
  • 建立 service
  • 执行部署

建立一个集群 cluster

在 node1 上运行 kubeadm init 便可建立一个集群。

kubeadm init --apiserver-advertise-address $(hostname -i)

执行完成后会生成 token,这样其余节点就能够凭借这个 token 加入该集群。

Kubernetes 实战步骤1

加入 node 节点

在 node2 和 node3 机器上,分别执行如下命令,加入到 node1 的集群。

kubeadm join 192.168.0.8:6443 --token nfs9d0.z7ibv3xokif1mnmv \
    --discovery-token-ca-cert-hash sha256:6587f474ae1543b38954b0e560832ff5b7c67f79e1d464e7f59e33b0fefd6548

命令执行完毕后,便可看到 node2,node3 已经加入成功。

Kubernetes 实战步骤2

查看集群状态

在 node1 节点上,执行如下命令。

kubectl get node

Kubernetes 实战步骤3

能够看到,集群中存在 node1 ,node2,node3 这 3 个节点,但这 3 个节点的都是 NotReady 状态,为何?

答:由于没有建立集群网络。

建立集群网络

执行如下代码建立集群网络。

kubectl apply -n kube-system -f \
    "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 |tr -d '\n')"

执行命令后,稍等一下,而后查看 node 状态,能够看到,集群中的 3 个节点都是 Ready 状态了。

Kubernetes 实战步骤4

建立 Deployment

咱们经过配置文件来建立 deployment,新建 deployment.yaml 文件,内容以下:

# 配置文件格式的版本
apiVersion: apps/v1
# 建立的资源类型
kind: Deployment
# 资源的元数据
metadata:
  name: notepad
# 规格说明
spec:
  # 定义 pod 数量
  replicas: 3
  # 经过 label 找到对应的 pod
  selector:
    matchLabels:
      app: mytest
  # 定义 pod 的模板
  template:
    # pod 的元数据
    metadata:
      # 定义 pod 的 label
      labels:
        app: mytest
    # 描述 pod 的规格
    spec:
      containers:
        - name: notepad
          image: yhlben/notepad
          ports:
            - containerPort: 8083

文件建立以后,执行命令:

kubectl apply -f ./deployment.yaml
# deployment.apps/notepad created

查看部署的 pod。

Kubernetes 实战步骤4.1

能够看到,咱们使用 deployment 类型的 controller 建立了 3 个 pod,分别运行在 node2 和 node3 机器上,若是有更多的机器,也会自动负载均衡,合理地分配到每一个机器上。

建立 Service

建立 service 和 deployment 相似,新建 service.yaml 文件,内容以下:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  # 在节点上部署访问 pod 的端口
  type: NodePort
  ports:
    - port: 80
      # service 代理的端口
      targetPort: 8083
      # node 节点上提供给集群外部的访问端口
      nodePort: 30036
  # 匹配 pod
  selector:
    app: mytest

文件建立以后,执行命令:

kubectl apply -f ./service.yaml
# service/my-service created

查看建立结果

使用 kubectl get deployment 和 kubectl get service 查看建立结果。

Kubernetes 实战步骤5

能够看到,deployment 和 service 均建立成功,而且已知 service 暴露的 ip 地址为:10.106.74.65,端口号为 80,因为设置了 targetPort,service 在接收到请求时,会自动转发到 pod 对应的 8083 端口上。

访问部署结果

部署成功后,咱们能够经过两种方式进行访问:

  • 集群内:经过 service 的 clusterIp + port 端口进行访问。
  • 集群外:经过任意一个 node 节点 + nodePort 端口进行访问。
# 集群内访问
curl 10.106.74.65
# 集群外访问
# 192.168.0.12 是 node2 节点的ip
# 192.168.0.11 是 node3 节点的ip
curl 192.168.0.12:30036
curl 192.168.0.11:30036

集群内访问。

Kubernetes 实战步骤6

集群外访问。

Kubernetes 实战步骤6

到这里,已经算部署成功了,你们确定有疑问,部署一个如此简单的 web 应用就这么麻烦,到底 k8s 好在哪里?咱们接着往下看。

K8s 运维

项目已经部署,接下来来实战一个运维,感觉一下 k8s 带给咱们的便利。

案例 1

公司要作双 11 活动,须要至少 100 个容器才能知足用户要求,应该怎么作?

首先,应该尽量利用当前拥有的服务器资源,建立更多的容器来参与负载均衡,经过 docker stats 能够查看容器占用的系统资源状况。若是充分利用后仍然不能知足需求,就根据剩余须要的容器,计算出须要购买多少机器,实现资源的合理利用。

  • 购买服务器,将服务器做为 node 节点,join 到集群中。
  • 执行扩容命令。

执行如下命令就能将容器扩展到 100 个。

kubectl scale deployments/notepad --replicas=100

如图,扩展 10 个 pod 的状况,node2 和 node3 节点共同分担负载。

Kubernetes 实战步骤8

也能够经过修改 deployment.yaml 中的 replicas 字段,执行 kubectl apply -f deployment.yaml去执行扩展。若是活动结束了,只须要将多余的服务器删除,缩减容器数量便可还原到以前的效果。

案例 2

双 11 活动很火爆,但忽然加了需求,须要紧急上线,若是实现滚动更新?

滚动更新就是在不宕机的状况下,实现代码更新。执行如下命令,修改 image 便可。

kubectl set image deployments/notepad notepad=yhlben/notepad:new

也能够经过修改 deployment.yaml 中的 image 字段,执行 kubectl apply -f deployment.yaml去执行升级。

若是更新出了问题,k8s 内置了一键还原上个版本的命令:

kubectl rollout undo deployments/notepad

经过这 2 个案例,感受到了 k8s 给运维带来了很大的便利:快速高效地部署项目支持动态扩展滚动升级同时还能按需优化使用的硬件资源

总结

本文的目的就是入门 k8s,经过一个简单的集群来实现这一点,但其中也踩了好多坑,具体以下:

  • 使用 minikube 搭建项目

    • 能快速搭建一个单节点 k8s 环境。
    • 但在本地使用 minikube 搭建一套 k8s 集群,不少包装不上,全局代理也不行。
  • 使用 google clould 上的服务器

    • 在 gogole clould 上,解决了网络问题,须要安装的包都能装上。
    • 但因为是新服务器,须要各类安装环境,docker,kubeadm,kubectl 等,安装过程繁琐,还可能会遇到报错。
    • 不知道哪天手滑了一下,试用帐号变成了付费帐号,赠金 $300 就这样没了 😭。
  • 使用 play with kubernetes

    • k8s 所需环境不用配,已经装好了,这一点很不错。
    • 单台机器容量过小,稍微大一点的镜像就安装不进去了。
    • 偶尔使用 play with kubernetes 时,刚新建完一个实例,就被踢下线,遇到这种状况就无法玩。
    • 没有提供公网 ip,没法验证外网访问状况。

最后,推荐一下《天天 5 分钟玩转 Kubernetes》这本书,一本很是适合入门 k8s 的实战书。书中经过大量的简单实战,从易到难,让我真正理解了 k8s,本文中的大量理论知识也都来自这本书。

更多文章,欢迎 Star 和 订阅 个人博客
相关文章
相关标签/搜索