《kubernetes权威指南》是本不可多得的好书,这里记录一下自己的读书笔记以及按照书中搭建的源代码。
kubernetes官方文档:https://kubernetes.io/docs/home/
kubernetes中文手册:https://www.kubernetes.org.cn/docs
kubernetes中文文档:https://linfan1.gitbooks.io/kubernetes-chinese-docs/content
What is Kubernetes?
英文原文:
Kubernetes is an open-source platform designed to automate deploying, scaling, and operating application containers.
With Kubernetes, you are able to quickly and efficiently respond to customer demand:
Deploy your applications quickly and predictably.
Scale your applications on the fly.
Roll out new features seamlessly.
Limit hardware usage to required resources only.
Our goal is to foster an ecosystem of components and tools that relieve the burden of running applications in public and private clouds.
官方定义的说法是Kubernetes一个用于容器集群的自动化部署、扩容以及运维的开源平台。使用Kubernetes,你可以快速高效地响应客户需求:
- 动态地对应用进行扩容。
- 无缝地发布新特性。
- 仅使用需要的资源以优化硬件使用。
个人把k8s比喻成一艘装满集装箱的帆船,可以装满很多的集装箱。
在集群的管理方面,kubernetes将集群中的机器划分为一个master节点和一群工作节点nodes.
- master上运行与集群管理相关的一组进程:kube-apiserver,kube-controller-manager,kube-scheduler
- node上运行与kubectl,kube-proxy等服务进程
- etcd 作为高性能存储服务
etcd 的学习可以参考 gitbook 上面某大神的一本书 一 etcd3学习笔记。
二话不说 书本上从一个简单的例子开始实践一下
单机部署一个mysql+myweb(tomcat)应用栈
技术准备:Centos7 OSX
- 关闭centos7防火墙
systemctl stop firewalld
systemctl disable firewalld
- 安装etcd键值数据库以及k8s
yum update -y
yum install etcd kubernetes -y
此时安装好的etcd和kubernetes版本为:
kubectl :v1.5.2
docker : v1.12.6
etcd : v3.2.9
- 修改配置文件(1)
- docker配置文件/etc/sysconfig/docker
Options的内容加上:OPTIONS='--selinux-enabled=false --insecure-registry gcr.io'
- 修改配置文件(2)
- Kubernetes apiserver配置文件为/etc/kubernetes/apiserver
把--admission_control参数的ServiceAccount参数删除
- 按顺序启动服务
systemctl start etcd
systemctl start docker
systemctl start kube-apiserver
systemctl start kube-controller-manager
systemctl start kube-scheduler
systemctl start kubelet
systemctl start kube-proxy
准备工作
1.换源
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://3b13d85e.m.daocloud.io
systemctl restart docker
2.提前下载好所需标准镜像
#mysql镜像
docker pull daocloud.io/library/mysql:latest
#tomcat镜像
docker pull kubeguide/tomcat-app:v2
启用mysql容器服务
1.定义一个RC文件,命名为mysql-rc.yaml,编辑:
apiVersion: v1
kind: ReplicationController
metadata:
name: mysql
spec:
replicas: 1
#定义RC标签选择
selector:
app: mysql
#定义Pod模板
template:
metadata:
labels:
app: mysql
spec:
#Pod内容器定义
containers:
- name: mysql
image: daocloud.io/library/mysql
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
创建好文件后,发布到k8s集群中
[root@taroballs ~]# kubectl create -f mysql-rc.yaml
replicationcontroller "mysql" created
查看刚刚创建的RC
[root@kubecon ~]# kubectl get rc
NAME DESIRED CURRENT READY AGE
mysql 1 1 1 11s
查看Pod的创建情况
[root@kubecon ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-bh44q 1/1 Running 0 16s
2.定义一个Service,文件命名为mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
保存后同样发布到k8s集群中
[root@taroballs ~]# kubectl create -f mysql-svc.yaml
service "mysql" created
查看创建好的Service
[root@kubecon ~]# kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 13h
mysql 10.254.248.1 <none> 3306/TCP 5s
#请注意 mysql服务被分配到了一个10.254.248.1的ClusterIP地址,此地址为虚地址,这样其他的Pod就可以通过Service的ClusterIP+端口号6379来访问它了
创建Tomcat应用栈
1.同样,定义一个RC文件,命名为myweb-rc.yaml,后面会讲到如何编写不用担心:
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
# Pod的数量
replicas: 1
# spec.selector与spec.template.metadata.labels,这两个字段必须相同,否则下一步创建RC会失败。
selector:
app: myweb
template:
metadata:
labels:
app: myweb
# 容器组的定义
spec:
containers:
# 容器名称
- name: myweb
# 容器对应的镜像
image: kubeguide/tomcat-app:v2
ports:
# 在8080端口上启动容器进程,PodIP与容器端口组成Endpoint,代表着一个服务进程对外通信的地址
- containerPort: 8080
env:
#此处如果在未安装域名解析的情况下,会无法将mysql对应的IP解析到env环境变量中,因此先注释掉!
# - name: MYSQL_SERVICE_HOST
# value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
把它发布到集群当中:
[root@kubecon ~]# kubectl create -f tomcat-rc.yaml
replicationcontroller "myweb" created
查看RC是否存在
[root@kubecon ~]# kubectl get rc
NAME DESIRED CURRENT READY AGE
mysql 1 1 1 1h
myweb 1 1 1 15s
查看Pod是否运行
[root@kubecon ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-bh44q 1/1 Running 0 1h
myweb-dctwr 1/1 Running 0 19s
2.定义一个Service文件,命名为myweb-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
selector:
app: myweb
同样的,把它发布到集群中
[root@kubecon ~]# kubectl create -f tomcat-svc.yaml
service "myweb" created
查看是否存在这个Service
[root@kubecon ~]# kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 15h
mysql 10.254.248.1 <none> 3306/TCP 1h
myweb 10.254.134.126 <nodes> 8080:30001/TCP 4s
如果都能完成的话 就到了激动人心的时刻了,打开浏览器访问http://yourip:30001
View on http://yourip:30001/demo/
如果看到这个界面说明你成功了
尝试一下插入一条数据
术语:
Master
- Kubernetes API Server (kube-apiserver)提供HTTP Rest接口实现对集群中所有资源的增删改查
- Kubernetes Controller Manager (kube-controller-manager) 集群所有资源的自动化控制中心,“总管”
- Kubernetes Scheduler (kuber-scheduler) 负责资源调度(Pod调度)
Node
Node才是kubernetes集群中的工作负载节点
kubelet:负责Pod对应的容器创建/启停,实现集群管理等基本功能(最常用)
-
kube-proxy: 实现k8s集群的通信于负载均衡
Node特性:
- 默认情况下node中kubelet会在集群中向master主节点介绍并注册自己
- 一旦被纳入集群,kubelet就会定时向Master汇报自己的资源情况
-
超时上报定为失联:若某Node超过指定时间都不上报信息,Master会将此Node定义为”失联“即"Not Ready"
查看当前主机有多少个Node节点:
[root@kubecon ~]# kubectl get nodes
NAME STATUS AGE
127.0.0.1 Ready 15h
#由于笔者只有一台ecs >-<.
查看某个node的详细信息:
[root@kubecon ~]# kubectl describe node 127.0.0.1
Name: 127.0.0.1
Role:
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/hostname=127.0.0.1
Taints: <none>
CreationTimestamp: Wed, 03 Jan 2018 22:12:05 +0800
Phase:
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
OutOfDisk False Thu, 04 Jan 2018 13:24:24 +0800 Wed, 03 Jan 2018 22:12:05 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available
MemoryPressure False Thu, 04 Jan 2018 13:24:24 +0800 Wed, 03 Jan 2018 22:12:05 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Thu, 04 Jan 2018 13:24:24 +0800 Wed, 03 Jan 2018 22:12:05 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure
Ready True Thu, 04 Jan 2018 13:24:24 +0800 Wed, 03 Jan 2018 22:12:05 +0800 KubeletReady kubelet is posting ready status
Addresses: 127.0.0.1,127.0.0.1,127.0.0.1
Capacity:
alpha.kubernetes.io/nvidia-gpu: 0
cpu: 1
memory: 1883496Ki
pods: 110
Allocatable:
alpha.kubernetes.io/nvidia-gpu: 0
cpu: 1
memory: 1883496Ki
pods: 110
System Info:
Machine ID: 963c2c41b08343f7b063dddac6b2e486
System UUID: 82E60F76-EF44-4B56-8865-9B667778593F
Boot ID: a48df7fa-e8ea-4300-8323-437c83f20321
Kernel Version: 3.10.0-693.11.1.el7.x86_64
OS Image: CentOS Linux 7 (Core)
Operating System: linux
Architecture: amd64
Container Runtime Version: docker://1.12.6
Kubelet Version: v1.5.2
Kube-Proxy Version: v1.5.2
ExternalID: 127.0.0.1
Non-terminated Pods: (2 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits
--------- ---- ------------ ---------- --------------- -------------
default mysql-bh44q 0 (0%) 0 (0%) 0 (0%) 0 (0%)
default myweb-dctwr 0 (0%) 0 (0%) 0 (0%) 0 (0%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.
CPU Requests CPU Limits Memory Requests Memory Limits
------------ ---------- --------------- -------------
0 (0%) 0 (0%) 0 (0%) 0 (0%)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
13m 13m 2 {kubelet 127.0.0.1} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-1ndjf_default(984cd373-f10d-11e7-952b-00163e04f964)". Falling back to DNSDefault policy.
11m 11m 2 {kubelet 127.0.0.1} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-vblfl_default(f8b588aa-f10d-11e7-952b-00163e04f964)". Falling back to DNSDefault policy.
10m 10m 2 {kubelet 127.0.0.1} Warning MissingClusterDNS kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-dctwr_default(07cd7332-f10e-11e7-952b-00163e04f964)". Falling back to DNSDefault policy.
Pod:
Kubernetes中,所有的容器都运行在pod中,一个pod来容纳一个单独的容器,或者多个合作的容器。
为什么要引入Pod容器?
1.以“容器组”为单位整体能够简单对业务进行封装管理以及判断
2.Pod里多个业务共享Pause容器的IP以及Volume使得管理极其方便
Pod特性:
- 每个Pod都有一个特殊的被称为”根容器“的Pause容器
- 每个Pod都包含一个或多个业务容器
- 每个Pod都分配了唯一的IP地址PodIP,一个Pod中多个容器共享PodIP
- 一个Pod里的容器与另外主机上的Pod容器能够直接通信;
- 如果Pod所在的Node宕机,会将这个Node上的所有Pod重新调度到其他节点上;
- 一个pod也能包含零个或者更多的的volume,volume是对一个容器私有的目录或者可以在pod中的容器间共享。
普通Pod及静态Pod
差别 | 普通Pod | 静态Pod |
---|---|---|
存放位置 | 一创建就存在etcd | 放在某个具体node的具体文件中 |
使用方式 | 被Master调度到某node上进行绑定 | 只在此具体文件所在的node上启动运行 |
EndPoint的概念
PodIP+containerPort--->EndPoint
Pod的IP加上容器开启的端口,代表着一个服务进程与外界通信的地址
资源限制
每个Pod可以设置限额的计算机资源有CPU和Memory;
- Requests,资源的最小申请量;
- Limits,资源最大允许使用的量
可以通过设置这两个参数来限制Pod使用的资源
如果一个容器失败,它会被Kubernetes的node上的kubectl自动重启
简单来说,Pod就是集装箱中的一个一个小的房间
Event
Event :是一个事件记录,记录了事件最早产生的时间、最后重复时间、重复次数、发起者、类型,以及导致此事件的原因等信息。Event通常关联到具体资源对象上,式排查故障的重要参考信息;
Pod错误排查
当我们发现某个Pod迟迟无法创建时,我们可以使用
kubectl describe pod xxxx
来查看它的描述信息.用来定位问题的范围~
[root@kubecon ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-bwnlk 0/1 ContainerCreating 0 1m
pod服务一直处于 ContainerCreating状态,即出现了错误
[root@kubecon ~]# kubectl describe po mysql-bwnlk
Name: mysql-bwnlk
Namespace: default
Node: 127.0.0.1/127.0.0.1
Start Time: Thu, 04 Jan 2018 10:20:45 +0800
Labels: app=mysql
Status: Pending
IP:
Controllers: ReplicationController/mysql
Containers:
mysql:
Container ID:
Image: daocloud.io/library/mysql
Image ID:
Port: 3306/TCP
State: Waiting
Reason: ContainerCreating
Ready: False
Restart Count: 0
Volume Mounts: <none>
Environment Variables:
MYSQL_ROOT_PASSWORD: 123456
Conditions:
Type Status
Initialized True
Ready False
PodScheduled True
No volumes.
QoS Class: BestEffort
Tolerations: <none>
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
3m 3m 1 {default-scheduler } Normal Scheduled Successfully assigned mysql-bwnlk to 127.0.0.1
3m 20s 5 {kubelet 127.0.0.1} Warning FailedSync Error syncing pod, skipping: failed to "StartContainer" for "POD" with ErrImagePull: "image pull failed for registry.access.redhat.com/rhel7/pod-infrastructure:latest, this may be because there are no credentials on this request. details: (open /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt: no such file or directory)"
2m 8s 10 {kubelet 127.0.0.1} Warning FailedSync Error syncing pod, skipping: failed to "StartContainer" for "POD" with ImagePullBackOff: "Back-off pulling image \"registry.access.redhat.com/rhel7/pod-infrastructure:latest\""
Label
Label是一种可以附加到各种资源对象上(Pod,Node,RC,Service等),通常在资源定义时确定也可以在对象创建后动态添加的实现资源分组动态功能的对象。
Label特性:
- 一个资源对象可以定义任意数量的Label。
- 给某个资源定义一个Label,相当于给该对象打上了标签,
- 通过给指定的资源对象捆绑一个或多个不同的Label来实现多维度的资源分组管理功能,能够更加灵活、方便的进行资源分配、调度、配置、部署等管理工作
- 可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象。
Label Selector
用于查询和筛选拥有某些Labels的资源对象
类似于SQL语句
Label Selector示例:select * from pod where pod’s name=’XXX’,env=’YYY’,支持操作符有=、!=、in、not in;
Label常用场景
- kube-controller进程通过RC上的Label Selector来筛选监控所期望的Pod数量
- kube-proxy通过Service上的Label Selector来选择对应的Pod以便建立起对应Pod的请求路由转发表
Replication controller副本管理器
RC是一种用来声明某种Pod的副本数量在任何时刻都符合某个预期值的场景文件。
Kubernetes的API对象用来管理Pod副本的行为的文件被称作replication controller,它用模板的形式定义了pod,然后系统根据模板实例化出一些pod(特别是由用户)
换句话说,我们通过定义一个RC文件来实现Pod的创建过程及副本数量的自动控制
RC的定义
- Replicas: Pod 的期待的副本数(replicas)
- Label Selector: 用于筛选目标 Pod
- Template: 当副本数小于期望时,用于创建新pod的模板
RC特性
- 定期巡检:定期巡检当前存活的目标Pod
- 扩容和缩容,通过改变RC的Replicas副本数量可以实现Pod的缩容和扩容
- 滚动升级,通过改变RC中的镜像版本可以实现Pod的滚动升级功能
先来个例子实战一下
#定义文件tomcat-frontend-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 1
selector:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: daocloud.io/library/tomcat
imagePullPolicy: IfNotPresent
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
将它发布到集群中
[root@kubecon ~]# kubectl create -f tomcat-frontend-rc.yaml
replicationcontroller "frontend" created
[root@kubecon ~]# kubectl get rc
NAME DESIRED CURRENT READY AGE
frontend 1 1 1 5s
[root@kubecon ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
frontend-8cg3k 1/1 Running 0 8s
[root@kubecon ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 172.18.231.136:6443 6m
Replica Set
下一代的Replication Controller
差别 | Replica Set | Replication Controller |
---|---|---|
base | 基于集合的Label Selector | 基于等式的Label Selector |
future | 下一代的RC,现主要被Deployment运用 | 将被取代 |
RS的特性与RC特性相类似,不再赘述.
Deployment 升级版RC
与RC区别,两者相似度>90%
- 相对于RC一个最大升级是我们可以随时知道当前Pod“部署”的进度。
- Deployment使用了Replica Set
- 除非需要自定义升级功能或根本不需要升级Pod,一般情况下,我们推荐使用Deployment而不直接使用Replica Set;
Deployment 拥有更加灵活强大的升级、回滚功能。在新的版本中,官方推荐使用Replica Set和Deployment来代替RC
Deployment使用场景
- 创建一个Deployment对象来生成对应的Replica Set并完成Pod副本的创建过程;
- 检查更新Deployment的状态来查看部署动作是否完成(Pod副本的数量是否达到预期的值);
- 更新Deployment以创建新的Pod;(比如镜像升级)
- 如果当前Deployment不稳定,则回滚到一个早先的Deployment版本;
- 挂起或者恢复一个Deployment;
下面以一个例子实战下Deployment
构建Tomcat_Deployment
定义一个文件 命名为:tomcat-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: tomcat-deployment
spec:
replicas: 1
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-dome
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
发布到Kubernetes集群
[root@kubecon ~]# kubectl create -f tomcat-deployment.yaml
deployment "tomcat-deployment" created
[root@kubecon ~]# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
tomcat-deployment 1 1 1 1 4s
- DESIRED,Pod副本数量的期望值,及Deployment里定义的Replica;
- CURRENT,当前Replica实际值;
- UP-TO-DATE,最新版本的Pod的副本数量,用于指示在滚动升级的过程中,有多少个Pod副本已经成功升级;
- AVAILABLE,当前集群中可用的Pod的副本数量;
查看对应的Replica Set
[root@kubecon ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
tomcat-deployment-142787937 1 1 1 1m
#看一下这里Pod的命名以Deployment对应的Replica Set的名字为前缀;
查看Pod的水平扩展过程
[root@kubecon ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
tomcat-deployment-142787937 1 1 1 1m
[root@kubecon ~]# kubectl describe deployments
Name: tomcat-deployment
Namespace: default
CreationTimestamp: Thu, 04 Jan 2018 18:48:31 +0800
Labels: app=app-demo
tier=frontend
Selector: tier=frontend,tier in (frontend)
Replicas: 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: tomcat-deployment-142787937 (1/1 replicas created)
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
1m 1m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set tomcat-deployment-142787937 to 1
Horizontal Pod Autoscaler(HPA)智能扩容
HPA的引入
- 虽然说可以手工执行kubectl scale命令,从而实现Pod横向自动扩容
- 但是我们目标是实现自动化、智能化扩容或缩容。
分布式系统能够根据当前系统的负载变化情况自动触发水平扩展或扩容的行为
实现原理:通过追踪分析RC控制的所有目标Pod的负载变化情况来确定是否需要针对性地调节目标Pod的副本数,相当与调节器。
HPA扩容的Pod负载度量指标:
- CPUUtilizationPercentage
通常使用一分钟内的算术平均值,可以通过Heapster扩展组件获取这个值。一个Pod自身的CPU利用率是该Pod当前CPU的使用量除以它的Pod Request的值。例如Pod Request定义值为0.4,当前Pod使用量为0.2,则它的CPU使用率为50%。如果某一刻CPUUtilizationPercentage超过80%则意为着当前pod不足以支撑故需要扩容来实现动态负载 - 第二个指标:应用程序自定义的度量指标,比如服务在每秒内的相应的请求数(TPS或QPS)
下面以一个HPA实例来实战下
构建HPA定义文件,命名为php-apache.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
kind: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90
#这个HPA控制的目标对象为一个名叫php-apache的Deployment里的Pod副本,当这些Pod副本的CPUUtilizationPercentage超过90%时会触发自动扩容行为,扩容或缩容时必须满足的一个约束条件是Pod的副本数要介于1与10之间;
发布到集群中
[root@kubecon ~]# kubectl create -f php-apache.yaml
horizontalpodautoscaler "php-apache" created
也可以通过简单命令行方式创建等价HPA对象
[root@kubecon ~]# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
frontend 1 1 1 0 22m
[root@kubecon ~]# kubectl autoscale deployment frontend --cpu-percent=90 --min=1 --max=10
deployment "frontend" autoscaled
Service
Service是一个“微服务”的代名词,通过分析、识别并建模系统中的所有服务为微服务——Kubernetes Service,最终构成多个提供不同业务能力而又彼此独立的微服务单元,服务之间通过TCP/IP进行通信,从而形成了我们强大而又灵活的弹性网络,拥有了强大的分布式能力、弹性扩展能力、容错能力。
我们的业务系统就是由一或多个“微服务”Service构成的。相当与帆船上一个一个的集装箱
由图中我们可以看到,Kubernetes的Service定义了一个服务的访问入口地址:
- 前段的应用frontend Pod通过这个入口地址访问其背后的一组由副本组成的集群实例
- Service与后端Pod副本集群之间则是通过Label Selector来实现“无缝对接”
- RC的作用保证Service的服务能力和质量始终处于预期的标准
问题引入:客户端访问Pod
既然每个Pod都会被分配到一个独立的IP地址,而且每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,那么问题来了,现在多个Pod副本组成了一个集群来提供服务,那么客户端如何来访问它们呢?
一般的做法是部署一个负载均衡器来访问它们,为这组Pod开启一个对外的服务端口如8000,并且将这些Pod的Endpoint列表加入8000端口的转发列表中,客户端可以通过负载均衡器的对外IP地址+服务端口来访问此服务。而客户端请求会被转发到哪个Pod,则有均很负载器觉得。
Kubernetes中运行在Node上的kube-proxy其实就是一个智能的软件负载均衡器,它负责把对Service的请求转发到后端的某个Pod实例上,并且在内部实现服务的负载均衡与会话保持机制。
k8s发明了一种巧妙的设计:Service不是共用一个负载均衡器的IP地址,而是每个Servcie分配一个全局唯一的虚拟IP地址,这个虚拟IP被称为Cluster IP。
这样一来 每个服务就变成了具备唯一IP地址的“通信节点”,服务调用也成了最基础的TCP通信问题。
来一个小小的例子是实战一下~
首先定义一个Service文件,命名为tomcat-service.yaml
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
# 服务如果想被外部调用,必须配置type
type: NodePort
ports:
- port: 8080
name: service-port
# 服务如果想被外部调用,必须配置nodePort
nodePort: 31002
- port: 8005
name: shutdown-port
selector:
tier: frontend
发布到Kubernetes集群
[root@kubecon ~]# kubectl create -f tomcat-service.yaml
service "tomcat-service" created
查看Endpoint列表
[root@kubecon ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 172.18.231.136:6443 24m
tomcat-service 172.17.0.2:8080,172.17.0.3:8080,172.17.0.2:8005 + 1 more... 2m
查看svc列表
[root@kubecon ~]# kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 26m
tomcat-service 10.254.80.192 <nodes> 8080:31002/TCP,8005:30541/TCP 4m
#EndPoint=podIP+containerPort
查看Service的ClusterIP可以看到
[root@kubecon ~]# kubectl get svc tomcat-service -o yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: 2018-01-04T10:51:35Z
name: tomcat-service
namespace: default
resourceVersion: "1562"
selfLink: /api/v1/namespaces/default/services/tomcat-service
uid: 3bf83ae4-f13d-11e7-ade2-00163e04f964
spec:
clusterIP: 10.254.80.192
ports:
- name: service-port
nodePort: 31002
port: 8080 #虚拟端口
protocol: TCP
targetPort: 8080 #Expose端口,提供TCP/IP接入
#若没有指定targetPort则默认与port相同
- name: shutdown-port
nodePort: 30541
port: 8005
protocol: TCP
targetPort: 8005
selector:
tier: frontend
sessionAffinity: None
type: NodePort
status:
loadBalancer: {}
注意,存在Service对应多个端口的问题
很多服务都存在多个端口的问题,比如有些业务一个端口提供业务服务,另外一个端口提供管理服务,比如 Mycat、Codis 等常见的中间件。Kubernetes Service 支持多个 Endpoint,在存在多个 Endpoint 的情况下,要求每个 Endpoint 定义一个名字来区分。下面是 Tomcat 多端口的 Service 定义样例:
要求每个EndPoint定义一个名字区分
- name: service-port <----------------|
nodePort: 31002
port: 8080
protocol: TCP
targetPort: 8080
- name: shutdown-port <---------------|
nodePort: 30541
port: 8005
protocol: TCP
targetPort: 8005
Kubernetes服务发现机制
- 每个 Kubernetes 中的 Service 都有一个唯一的 Cluster IP 及唯一的名字
- 名字是由卡发着自己定义的,部署时也没必要改变
最早时 Kubernetes 采用了 Linux 环境变量的方式解决这个问题,即每个 Service 生成一些对应的 Linux 环境变量(ENV),并在每个 Pod 的容器在启动时,自动注入这些环境变量
#下是 tomcat-service 产生的环境变量条目
TOMCAT_SERVICE_SERVICE_HOST=10.102.107.188
TOMCAT_SERVICE_SERVICE_PORT_SERVICE_PORT=8080
TOMCAT_SERVICE_SERVICE_PORT_SHUTDOWN_PORT=8005
TOMCAT_SERVICE_PORT_8080_TCP_PORT=8080
可以看到,每个 Service 的 Ip 地址及端口都是标准的命名规范的,遵循这个命名规范,就可以通过代码访问系统环境变量的方式得到所需的信息,实现服务调用。
外部系统访问 Service 问题
区别三种IP:
差别 | Node IP | Pod IP | ClusterIP |
---|---|---|---|
含义 | 每个节点的物理网卡的 IP 地址 | 每个 Pod 的 IP 地址 | 一个虚拟的 IP地址 |
源自 | 物理主机网卡 | Docker Engine 根据 docker0 网桥的 IP 地址段进行分配的,通常是一个虚拟的二层网络 | Kubernetes Service”伪造“ 和定义的 |
作用域 | 服务器之间 | 虚拟二层网络,不同 Pod 之间通信 | 仅作用于 Kubernetes Service 这个对象 |
是否能够ping通 | 是 | 是 | 否,没有一个 ”实体网络对象“ 来响应。 |
- Cluster IP 只能结合 Service Port 组成一个具体的通信端口,单独的 Cluster IP 不具备 TCP/IP 通信的基础,并且它们属于 Kubernetes 集群这样一个封闭的空间,集群之外的节点如果要访问这个通信端口,则需要做一些额外的工作。
- 在 Kubernetes 集群内,Node IP 网、Pod IP 网与 Cluster IP 网之间的通信,采用的是 Kubernetes 自己设计的一种编程方式的特殊的路由规则,与自身熟知的 IP 路由有很大的不同。
Service 的 Cluster Ip 属于 Kubernetes 集群内部的地址,无法在集群外部直接使用这个地址。
问题来了:存在有一部分服务是要提供给 Kubernetes 集群外部的应用或者用户来使用的,典型的例子就是 Web 端的服务模块,比如上面的 tomcat-service,那么用户怎么访问它?
采用 NodePort 。
具体做法如下,以 tomcat-service 为例:
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort
ports:
- port: 8080
nodePort: 31002
#nodePort: 31002 这个属性表名手动指定 tomcat-service 的 NodePort 为 31002
selector:
tier: frontend
NodePort 的实现方式是在 Kubernetes 集群里的每个 Node 上为需要外部访问的 Service 开启一个对应的 TCP 监听端口,外部系统只要用任意一个 Node 的 Ip 地址加具体的 NodePort 端口号即可访问此服务
查看NodePort端口是否被监听:
[root@kubecon ~]# kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 2h
tomcat-service 10.254.80.192 <nodes> 8080:31002/TCP,8005:30541/TCP 2h
[root@kubecon ~]# netstat -lnput | grep 31002
tcp6 0 0 :::31002 :::* LISTEN 1608/kube-proxy
那么问题又来了,NodePort 还没完全解决外部访问 Service 的所有问题,比如负载均衡问题:
假如集群中有 10 个 Node,则此时最好有一个负载均衡,外部的请求只需访问此负载均衡器的 IP 地址,由负载均衡器负责转发流量到后面某个 Node 的 NodePort 上
上图中的 Load Balancer 组件独立于 Kubernetes 集群之外,通常是一个硬件的负载均衡器,或者是以软件方式实现的,例如 HAProxy 或者 Nginx。
对于每个 Service,我们通常需要配置一个对应的 Load Balancer 示例来转发流量到后端的 Node 上,这的确增加了工作量及出错的概率。
Volume存储卷
差别 | Docker Volume | Kubernetes Volume |
---|---|---|
定义在 | 容器上 | Pod之上 |
生命周期 | 与容器相关 | 与 Pod 的生命周期相同,但与容器的声明周期不相干关。当容器中止或者启动时,Volume 中的数据也不会丢失。 |
Volume 是 Pod 中能够被多个容器访问的共享目录。
差别 | Docker Volume | Kubernetes Volume |
---|---|---|
定义在 | 容器上 | Pod之上 |
生命周期 | 与容器相关 | 与 Pod 的生命周期相同,但与容器的声明周期不相干关。当容器中止或者启动时,Volume 中的数据也不会丢失。 |
先来个例子实战下~
#先在 Pod 上声明一个 Volume,然后在容器里引用该 Volume 并 Mount 到容器里的某个目录上
#给之前的 Tomcat Pod 增加一个名字为 datavol 的 Volume,并且 Mount 到容器的 /mydata-data 目录上
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 1
selector:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: daocloud.io/library/tomcat
#add
volumeMounts:
- mountPath: /mydata-data
name: datavol
imagePullPolicy: IfNotPresent
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
#add
volumes:
- name: datavol
emptyDir: {}
发布到集群中
[root@kubecon ~]# kubectl create -f tomcat-frontend-rc.yaml
replicationcontroller "frontend" created
获取容器id查看是否挂载成功
[root@kubecon ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
frontend-c750f 1/1 Running 0 8m
[root@kubecon ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cbe157bfaeab daocloud.io/library/tomcat "catalina.sh run" 8 minutes ago Up 8 minutes k8s_tomcat-demo.66724613_frontend-c750f_default_3491a794-f153-11e7-ade2-00163e04f964_157daa35
c94aedb5ef0e registry.access.redhat.com/rhel7/pod-infrastructure:latest "/usr/bin/pod" 8 minutes ago Up 8 minutes k8s_POD.a8590b41_frontend-c750f_default_3491a794-f153-11e7-ade2-00163e04f964_2080adb3
[root@kubecon ~]# docker inspect cbe
[
{
"Id": "cbe157bfaeab28190481a974fe6b1c1f9830584210520f664538fccaaa98ec54",
"Created": "2018-01-04T13:28:53.583903204Z",
...................................
"Mounts": [
{
"Source": "/var/lib/kubelet/pods/3491a794-f153-11e7-ade2-00163e04f964/volumes/kubernetes.io~empty-dir/datavol",
"Destination": "/mydata-data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
................................
#可以很明显的看到/datavol挂载到了/mydata-data
#然后可以看到使用的是empty-dir
Kubernetes 支持多种类型的 Volume,例如 GlusterFS、Ceph 等先进的分布式文件存储。
常见的几种Volume类型:
EmptyDir
- 一个 EmptyDir Volume 是在 Pod 分配到 Node 时创建的。
- 从它的名称就可以看出,它的初始化内容为空,并且无须指定宿主机上对应的目录文件
- Pod 从 Node 上移除时,emptyDir 中的数据也会被永久删除
emptyDir 的使用场景如下:
1.临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
2.长时间任务的中间过程 CheckPoint 的临时保存目录。
3.一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
例如前一个例子就使用了EmptyDir:
volumes:
- name: data
emptyDir: {}
HostPath
- hostPath 为在 Pod 上挂载宿主机上的文件或目录
HostPath 的使用场景如下:
1.容器应用程序生成的日志文件需要永久保存时,可以使用宿主机的高速文件系统进行存储。
2.需要访问宿主机上 Docker 引擎内部数据结构的容器应用时,可以通过定义 hostPath 为宿主机 /var/lib/docker 目录,使容器内部应用可以直接访问 Docker 的文件系统。
Pay Attention:
1.存在访问结果不一致的情况:
2.存在资源管理配额不纳入的情况:
例子:
#定义pod文件busybox-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
name: busybox
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
name: busybox
volumeMounts:
- mountPath: /busybox-data
name: data
volumes:
- hostPath:
path: /tmp/data
name: data
docker inspect查看挂载
"Mounts": [
{
"Source": "/tmp/data",
"Destination": "/busybox-data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Source": "/var/lib/kubelet/pods/7eb594fd-f157-11e7-ade2-00163e04f964/etc-hosts",
"Destination": "/etc/hosts",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Source": "/var/lib/kubelet/pods/7eb594fd-f157-11e7-ade2-00163e04f964/containers/busybox/7b91bd28",
"Destination": "/dev/termination-log",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
不建议使用
gcePersistentDisk&awsElasticBlockStore
一个是Google Cloud Platform的一个是Aws ec2的
NFS
使用 NFS 网络文件系统提供的共享存储数据时,需要在系统中部署一个 NFS Server。定义 NFS 类型的 Volume 示例如下:
Namespace命名空间
- Namespace 在很多情况下用于实现多租户的资源隔离。
- Namespace 通过将集群内部的资源对象 “分配” 到不同的 Namespace 中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
Kubernetes 集群在启动后,会创建一个名为 “default” 的 Namespace,通过 kubectl 可以查看到:
[root@kubecon ~]# kubectl get namespace
NAME STATUS AGE
default Active 3h
kube-system Active 3h
注意,如果不特别指明 Namespace,则用户创建的 Pod、RC、Service 都将被系统创建到这个默认的名为 default 的 Namespace 中。
Namespace 的定义很简单。如果所示的 yaml 定义名为 development 的 Namespace。
小小实战下
#development-ns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: development
一旦创建了 Namespace,在创建资源对象时就可以指定这个资源对象属于哪个 Namespace。
定义一个名为 busybox 的 Pod,放入 development 这个 Namespace 里
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: bosybox
发布到集群中并查看状态
[root@kubecon ~]# kubectl apply -f development-ns.yaml
namespace "development" created
[root@kubecon ~]# kubectl apply -f busybox-pod.yaml
pod "busybox" created
[root@kubecon ~]# kubectl get pod
No resources found.
#因为如果不加参数,则 kubectl get 命令将仅显示属于 default namespace 的资源对象。
[root@kubecon ~]# kubectl get pod --namespace=development
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 13s
#在 kubectl 命令中加入 --namespace 参数来查看某个命名空间中的对象
Annotation
Annotation 与 Label 类似,不同的是 Label 具有严格的命名规则,它定义的是 Kubernetes 对象的元数据(Metadata),并且用于 Label Selector。而 Annotation 则是用户定义的 “附件” 信息,以便于外部工具进行查找.
Annotation 的应用场景:
- build 信息、release 信息、Docker 镜像信息等,例如时间戳、release id 号、PR 号、镜像 hash 值、docker registry 地址等。
- 日志库、监控库、分析库等资源库的地址信息。
- 程序调试工具信息,例如工具名称、版本号等。
- 团队的联系信息,例如电话号码、负责人名称、网址等。
总结:Node、Pod、Replication Controller和Service等都可以看作是一种“资源对象”,几乎所有的资源对象都可以通过Kubernetes提供的kubectl工具执行增、删、改、查等操作并将其保存在ectd中持久化存储。 *k8s相当于装满集装箱的货船,Pod相当于船上一个个的集装箱 ,docker容器就是集装箱里摆放的一件件货物,而Service相当于一堆堆贴好标签Label要运去哪儿的集装箱集合,Replication Controller相当于确定集装箱Pods应该要有多少份,deployment是改进版的RC,它通过RS实时确定各个集装箱Pod的上船进度等,NameSpace指船上存放的地方是A区(汽车类)还是B区粮米类。
好了,今天就到这了,写了一天了,希望笔记对自己以及大家都有帮助~>-<.