Kubernetes 简称 k8s,是 google 在 2014 年发布的一个开源项目。前端
Kubernetes 解决了哪些问题?node
真实的生产环境应用会包含多个容器,而这些容器还极可能会跨越多个服务器主机部署。Kubernetes 提供了为那些工做负载大规模部署容器的编排与管理能力。Kubernetes 编排让你可以构建多容器的应用服务,在集群上调度或伸缩这些容器,以及管理它们随时间变化的健康状态。git
Kubernetes 基础概念不少,第一次接触容易看晕,若是是新手,建议直接看实战部分,先跑起来再说。github
k8s 中有几个重要概念。web
概念 | 介绍 |
---|---|
cluster | 一个 k8s 集群 |
master | 集群中的一台机器,是集群的核心,负责整个集群的管理和控制 |
node | 集群中的一台机器,是集群中的工做负载节点 |
pod | k8s 最小调度单位,每一个 pod 包含一个或多个容器 |
label | 一个 key = value 键值对,能够附加到各类资源对象上,方便其余资源进行匹配 |
controller | k8s 经过 controller 来管理 pod |
service | 对外提供统一的服务,自动将请求分发到正确的 pod 处 |
namespace | 将 cluster 逻辑上划分红多个虚拟 cluster,每一个 cluster 就是一个 namespace |
Cluster 表明一个 k8s 集群,是计算、存储和网络资源的集合,k8s 利用这些资源运行各类基于容器的应用。docker
Master 是 cluster 的大脑,运行着的服务包括 kube-apiserver、kube-scheduler、kube-controller-manager、etcd 和 pod 网络。数据库
kube-apiserver后端
kube-schedulerapi
kube-controller-manager安全
etcd(分布式 key-value 存储库)
pod 网络
Node 节点 是 pod 运行的地方。node 节点上运行的 k8s 组件有 kubelet、kube-proxy 和 pod 网络。
kubelet
kube-proxy
pod 网络
每个 pod 包含一个或多个 container,pod 中的容器做为一个总体被 master 调度到 node 节点上运行。
为何 k8s 使用 pod 管理容器,而不是直接操做容器?
一、由于有些容器天生就是须要紧密的联系,放在一个 pod 中表示一个完整的服务,k8s 同时会在每一个 pod 中加入 pause 容器,来管理内部容器组的状态。
二、Pod 中的全部容器共享 ip,共享 volume,方便进行容器之间通讯和数据共享。
何时须要在 pod 中定义多个容器?
答:这些容器联系很是紧密,并且须要直接共享资源,例如一个爬虫程序,和一个 web server 程序。web server 强烈依赖爬虫程序提供数据支持。
Label 是一个 key = value 的键值对,其中 key 和 value 都由用户本身指定。
Label 的使用场景:
kube-controller
kube-proxy
总之,label 能够给对象建立多组标签,label 和 label selecter 共同构建了 k8s 系统中核心的应用模型,使得被管理对象可以被精细地分组管理,同时实现了整个集群的高可用性。
k8s 一般不会直接建立 pod,而是经过 controller 来管理 pod。controller 中定义了 pod 的部署特性,好比有几个副本,在什么样的 node 上运行等。为了知足不一样的业务场景,k8s 提供了多种类型的 controller。
Deployment
ReplicaSet
DaemonSet
使用场景
StatefuleSet
Job
提示
使用 deployment controller 建立的用例,若是出现有 pod 挂掉的状况,会自动新建一个 pod,来维持配置中的 pod 数量。
容器按照持续运行时间可分为两类:服务类容器和工做类容器。
服务类容器一般持续提供服务,须要一直运行,好比 http server。工做类容器则是一次性任务,好比批处理程序,完成后容器就退出。
Controller 中 deployment、replicaSet 和 daemonSet 类型都用于管理服务类容器,对于工做类容器,咱们使用 job。
Service 是能够访问一组 pod 的策略,一般称为微服务。具体访问哪一组 pod 是经过 label 匹配出来的。service 为 pod 提供了负载均衡,原理是使用 iptables。
为何要用 service ?
外网如何访问 service?
nodeip:nodeport
,能够从集群的外部访问一个 service 服务。若是有多个用户使用同一个 k8s cluster,如何将他们建立的 controller,pod 等资源分开呢?
答:使用 namespace。
若是将物理的 cluster 逻辑上划分红多个虚拟 cluster,每一个 cluster 就是一个 namespace,不一样 namespace 里的资源是彻底隔离的。
k8s 默认建立了两个 namespace。
强大的自愈能力是 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
hostPath
AWS Elastic Block Store
Persistent Volume
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 集群并部署容器化应用只是第一步。一旦集群运行起来,咱们须要确保集群一切都是正常的,这就须要对集群进行监控。
经常使用的可视化监控工具以下。
具体的使用步骤就直接看文档了,这里不详细说明。
经过集群监控咱们可以及时发现集群出现的问题,但为了方便进一步排查问题,咱们还须要进行进行日志记录。
经常使用的日志管理工具以下。
咱们来实战部署一个 k8s 记事本项目,项目使用 yhlben/notepad 镜像进行构建,该镜像在部署后会在 8083 端口上提供一个 web 版记事本服务,查看演示。
为了不安装 k8s 出现的各类坑,这里使用 Play with Kubernetes进行演示。
首先在 Play with Kubernetes 上建立 3 台服务器,node1 做为 master 节点,node2 和 node3 做为工做节点。接下来进行如下操做;
在 node1 上运行 kubeadm init 便可建立一个集群。
kubeadm init --apiserver-advertise-address $(hostname -i)
执行完成后会生成 token,这样其余节点就能够凭借这个 token 加入该集群。
在 node2 和 node3 机器上,分别执行如下命令,加入到 node1 的集群。
kubeadm join 192.168.0.8:6443 --token nfs9d0.z7ibv3xokif1mnmv \ --discovery-token-ca-cert-hash sha256:6587f474ae1543b38954b0e560832ff5b7c67f79e1d464e7f59e33b0fefd6548
命令执行完毕后,便可看到 node2,node3 已经加入成功。
在 node1 节点上,执行如下命令。
kubectl get node
能够看到,集群中存在 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 状态了。
咱们经过配置文件来建立 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。
能够看到,咱们使用 deployment 类型的 controller 建立了 3 个 pod,分别运行在 node2 和 node3 机器上,若是有更多的机器,也会自动负载均衡,合理地分配到每一个机器上。
建立 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 查看建立结果。
能够看到,deployment 和 service 均建立成功,而且已知 service 暴露的 ip 地址为:10.106.74.65,端口号为 80,因为设置了 targetPort,service 在接收到请求时,会自动转发到 pod 对应的 8083 端口上。
部署成功后,咱们能够经过两种方式进行访问:
# 集群内访问 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
集群内访问。
集群外访问。
到这里,已经算部署成功了,你们确定有疑问,部署一个如此简单的 web 应用就这么麻烦,到底 k8s 好在哪里?咱们接着往下看。
项目已经部署,接下来来实战一个运维,感觉一下 k8s 带给咱们的便利。
公司要作双 11 活动,须要至少 100 个容器才能知足用户要求,应该怎么作?
首先,应该尽量利用当前拥有的服务器资源,建立更多的容器来参与负载均衡,经过 docker stats 能够查看容器占用的系统资源状况。若是充分利用后仍然不能知足需求,就根据剩余须要的容器,计算出须要购买多少机器,实现资源的合理利用。
执行如下命令就能将容器扩展到 100 个。
kubectl scale deployments/notepad --replicas=100
如图,扩展 10 个 pod 的状况,node2 和 node3 节点共同分担负载。
也能够经过修改 deployment.yaml 中的 replicas 字段,执行 kubectl apply -f deployment.yaml
去执行扩展。若是活动结束了,只须要将多余的服务器删除,缩减容器数量便可还原到以前的效果。
双 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 搭建项目
使用 google clould 上的服务器
使用 play with kubernetes
最后,推荐一下《天天 5 分钟玩转 Kubernetes》这本书,一本很是适合入门 k8s 的实战书。书中经过大量的简单实战,从易到难,让我真正理解了 k8s,本文中的大量理论知识也都来自这本书。
更多文章,欢迎 Star 和 订阅 个人博客。