Kubernetes简介html
Kubernetes是Google基于Borg开源的容器编排调度引擎,做为CNCF(Cloud Native Computing Foundation)最重要的组件之一,它的目标不只仅是一个编排系统,而是提供一个规范,可让你来描述集群的架构,定义服务的最终状态,Kubernetes能够帮你将系统自动得达到和维持在这个状态。node
更直白的说,Kubernetes可让用户经过编写一个yaml或者json格式的配置文件,也能够是经过工具/代码生成或者是直接请求Kubernetes API来建立应用,该配置文件中包含了用户想要应用程序保持的状态,无论整个Kubernetes集群中的个别主机发生什么问题,都不会影响应用程序的状态,你还能够经过改变该配置文件或请求Kubernetes API来改变应用程序的状态。python
这意味着开发人员不须要在乎节点的数目,也不须要在乎从哪里运行容器以及如何与它们交流。开发人员也不须要管理硬件优化,或担忧节点关闭(它们将遵循墨菲法则),由于新的节点会添加到Kubernetes集群,同时Kubernetes会在其余运行的节点中添加容器,Kubernetes会发挥最大的做用。web
总结:Kubernetes是容器控制平台,能够抽象全部的底层基础设施(容器运行用到的基础设施)。json
Kubernetes——让容器应用进入大规模工业生产。api
Kubernetes另外一个深刻人心的特色是:它标准化了云服务提供商。好比,有一个Azure、Google云平台或其余云服务提供商的专家,他担任了一个搭建在全新的云服务提供商的项目。这可能引发不少后果,好比说:他可能没法在截止期限内完成;公司可能须要招聘更多相关的人员,等等。相对的,Kubernetes就没有这个问题。由于不管是哪家云服务提供商,你均可以在上面运行相同的命令,以既定的方式向Kubernetes API服务器发送请求,Kubernetes会负责抽象,并在不一样的云服务商中实现。数组
对于公司来讲,这意味着他们不须要绑定到任何一家云服务商。他们能够计算其余云服务商的开销,而后转移到别家,并依旧保留着原来的专家,原来的人员,他们还能够花更少的钱。浏览器
Pod服务器
Kubernetes有不少技术概念,同时对应不少API对象,最重要的也是最基础的对象就是Pod。Pod是Kubernetes集群中运行部署应用的最小单元,而且是支持多个容器的。网络
Pod的设计理念是支持多个容器在一个Pod中共享网络地址和文件系统,能够经过进程间通讯和文件共享这种简单高效的方式组合完成服务。Pod对多容器的支持是Kubernetes最基础的设计理念。好比你运行一个操做系统发行版的软件仓库,一个Nginx容器用来发布软件,另外一个容器专门用来从源仓库作同步,这两个容器的镜像不太多是一个团队开发的,可是他们一起工做才能提供一个微服务;这种状况下,不一样的团队各自开发构建本身的容器镜像,在部署的时候组合成一个微服务对外提供服务。不过,在大多数状况下,咱们只会在Pod中运行一个容器,本文中的例子也是这样的。
Pod 的另外一个特征是:若是咱们但愿使用其余 RKE 等技术的话,咱们能够作到不依赖Docker容器。
Docker是kubernetes中最经常使用的容器运行时,可是Pod也支持其余容器运行时。
总的来讲,Pod的主要特征包括:
每一个Pod能够在Kubernetes集群内拥有惟一的IP地址;
Pod能够拥有多个容器。这些容器共享同一个端口空间,因此他们能够经过localhost交流(可想而知它们没法使用相同的端口),与其余Pod内容器的交流能够经过结合Pod的IP来完成;
一个Pod内的容器共享同一个卷、同一个 IP、端口空间、IPC 命名空间。
定义一个Pod
以下,咱们定义一个最简单的Pod:
apiVersion: v1 kind: Pod # 定义Kubernetes资源的类型为Pod metadata: name: demo-web # 定义资源的名称 labels: # 为Pod贴上标签,后面会介绍其用处 app: demo-web spec: # 定义资源的状态,对于Pod来讲,最重要属性就是containers containers: # containers一个数组类型,若是你但愿部署多个容器,能够添加多项 - name: web # 定义本Pod中该容器的名称 image: rainingnight/aspnetcore-web # 定义Pod启动的容器镜像地址 ports: - containerPort: 80 # 定义容器监听的端口(与Dockerfile中的EXPOSE相似,只是为了提供文档信息)
而后保存,我这里命名为demo-web-pod.yaml。
如今咱们能够在终端中输入如下命令来建立该Pod:
kubectl create -f demo-web-pod.yaml # 输出 # pod/demo-web created
可使用以下命令,来查看kubernetes中的Pod列表:
kubectl get pods # 输出 # NAME READY STATUS RESTARTS AGE # demo-web 1/1 Running 0 65s
若是该Pod还处于ContainerCreating状态的话,你能够在运行命令的时候加入--watch参数,这样当Pod变成运行状态的时候,会自动显示在终端中。
访问应用程序
在上面,咱们成功部署了一个ASP.NET Core Mvc程序的Pod,那么如何访问它呢?若是只是为了调试,咱们可使用转发端口的方式来快速访问:
kubectl port-forward demo-web 8080:80 # 输出 # Forwarding from 127.0.0.1:8080 -> 80
而后咱们再浏览器中访问:127.0.0.1:8080,显示以下:
如上,还展现了Pod的主机名和IP,这是由于我在应用中添加了以下代码:
public void OnGet() { HostName = Dns.GetHostName(); HostIP = Dns.GetHostEntry(HostName).AddressList.FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork).ToString(); }
不过,端口转发的方式只能在本机访问,为了从外部访问应用程序,咱们须要建立Kubernetes中的另一种资源:Service。
Service
Kubernetes中的Service资源能够做为一组提供相同服务的Pod的入口,这个资源肩负发现服务和平衡Pod之间负荷的重任。
在Kubernetes集群中,咱们拥有提供不一样服务的Pod,那么Service如何知道该处理哪一个Pod呢?
这个问题就用标签来解决的,具体分两个步骤:
给全部须要Service处理的对象Pod贴上标签。
在Service中使用一个选择器(Label Selector),该选择器定义了全部贴有对应的标签的对象Pod。
标签
标签提供了一种简单的方法用于管理Kubernetes中的资源。它们用一对键值表示,且能够用于全部资源。
其实在上面的Pod定义中,咱们已经定义了标签:
metadata: name: demo-web labels: app: demo-web
如上,咱们为Pod附加了标签app:demo-web,在查看Pod的时候,可使用--show-labels参数来显示Pod对应的标签:
kubectl get pods --show-labels # 输出 # NAME READY STATUS RESTARTS AGE LABELS # demo-web 1/1 Running 0 1m52s app=demo-web
能够看到,咱们的Pod都拥有一个app=demo-web标签。
定义Service
如今,让咱们为刚才建立的Pod定义一个Service:
apiVersion: v1 kind: Service # 定义Kubernetes资源的类型为Service metadata: name: demo-web-service # 定义资源的名称 spec: selector: # 指定对应的Pod app: demo-web # 指定Pod的标签为demo-web ports: - protocol: TCP # 协议类型 port: 80 # 指定Service访问的端口 targetPort: 80 # 指定Service转发请求的端口 nodePort: 30000 type: NodePort # 指定Service的类型,在这里使用NodePort来对外访问
如上,咱们使用selector属性来选择相应的标签,并把服务类型(type)设置为NodePort,type的取值有如下4种:
ClusterIP:默认值,经过集群的内部IP暴露服务,该模式下,服务只可以在集群内部能够访问。
NodePort:经过每一个Node上的IP和静态端口(NodePort)暴露服务,NodePort服务会路由到ClusterIP服务,这个ClusterIP服务会自动建立。
LoadBalancer:使用云提供商的负载均衡器,能够向外部暴露服务,外部的负载均衡器能够路由到NodePort服务和ClusterIP服务。
ExternalName:经过返回CNAME和它的值,能够将服务映射到externalName字段的内容(如:foo.bar.example.com)。没有任何类型代理被建立,这只有 Kubernetes 1.7 或更高版本的kube-dns才支持。
对于服务类型咱们先了解这么多就能够了,后续会再来详细介绍。
而后使用以下命令建立Service:
kubectl create -f demo-web-service.yaml # 输出 # service/demo-web-service created
使用以下命令来检查服务的状态:
kubectl get services # 输出 # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # demo-web-service NodePort 10.105.132.214 <none> 80:30000/TCP 10s
如上,它有一个CLUSTER-IP为10.105.132.214,所以咱们能够在集群内使用10.105.132.214:80来访问该服务,若是是在集群外部,可使用[nodeip]:30000来访问。
服务发现与负载均衡
在上面咱们说到,Service肩负着发现服务和平衡Pod之间负荷的重任,那它是怎么作的呢?让咱们先再添加一个Pod:
apiVersion: v1 kind: Pod metadata: name: demo-web-copy labels: app: demo-web spec: containers: - name: web image: rainingnight/aspnetcore-web ports: - containerPort: 80
如上,其定义与以前的Pod同样,只是把name改为了demo-web-copy,而后建立Pod:
kubectl create -f demo-web-copy.pod.yaml # 查看Pod kubectl get pods # 输出 # NAME READY STATUS RESTARTS AGE # demo-web 1/1 Running 0 10m # demo-web-copy 1/1 Running 0 29s
如今咱们使用NodeIP:3000来访问,打开两个浏览器窗口,多刷新几回,以便让它们分别路由到不一样的Node,最终显示以下:
能够看到,咱们新建立的Pod已经经过Servie来接受请求了,不须要修改为任何程序代码,Kubernetes就已经帮咱们实现了服务发现和负载均衡,是否是很是爽。
Deployment
在上面咱们手动部署了两个Pod,可是这只是单机的玩法,它与直接使用Docker容器相比并没有太大优点,若是咱们须要部署一千个实例,那就是一个痛苦的过程,或者咱们又想快速更新和迅速回滚,这根本就是不可能的!
其实在k8s中,咱们不多直接使用Pod,更多的是使用Kubernetes的另一种资源:Deployment。
Deployment表示用户对Kubernetes集群的一次更新操做。能够是建立一个新的服务或是更新一个新的服务,也能够是滚动升级一个服务。Deployment能够帮助每个应用程序的生命都保持相同的一点:那就是变化。此外,只有挂掉的应用程序才会一尘不变,不然,新的需求会源源不断地涌现,更多代码会被开发出来、打包以及部署,这个过程当中的每一步都有可能出错。Deployment能够自动化应用程序从一版本升迁到另外一版本的过程,并保证服务不间断,若是有意外发生,它可让咱们迅速回滚到前一个版本。
Deployment定义
如今,咱们使用Deployment来部署咱们的Pod,并实如今部署期间服务不间断服务,能够以下定义:
apiVersion: apps/v1 kind: Deployment # 定义Kubernetes资源的类型为Deployment metadata: name: demo-web-deployment # 定义资源的名称 labels: app: demo-web-deployment spec: # 定义资源的状态。 replicas: 2 # 定义咱们想运行多少个Pod,在这里咱们但愿运行2个 selector: matchLabels: # 定义该部署匹配哪些Pod app: demo-web minReadySeconds: 5 # 可选,指定Pod能够变成可用状态的最小秒数,默认是0 strategy: # 指定更新版本时,部署使用的策略 type: RollingUpdate # 策略类型,使用RollingUpdate能够保证部署期间服务不间断 rollingUpdate: maxUnavailable: 1 # 部署时最大容许中止的Pod数量(与replicas相比) maxSurge: 1 # 部署时最大容许建立的Pod数量(与replicas相比) template: # 用来指定Pod的模板,与Pod的定义相似 metadata: labels: # 根据模板建立的Pod会被贴上该标签,与上面的matchLabels对应 app: demo-web spec: containers: - name: web image: rainingnight/aspnetcore-web imagePullPolicy: Always # 默认是IfNotPresent,若是设置成Always,则每一次部署都会从新拉取容器映像(不然,若是本地存在指定的镜像版本,就不会再去拉取) ports: - containerPort: 80
保存为demo-web-deployment.yaml,而后输入如下命令来建立Deployment:
kubectl create -f demo-web-deployment.yaml # 输出 # deployment.apps/demo-web-deployent created
如今咱们再来查看如下Pod:
kubectl get pods # 输出 # NAME READY STATUS RESTARTS AGE # demo-web 1/1 Running 0 4h28m # demo-web-copy 1/1 Running 0 18m # demo-web-deployment-745f7997c4-d24bb 1/1 Running 0 16s # demo-web-deployment-745f7997c4-jk9jn 1/1 Running 0 16s
如上,咱们有4个运行中的Pod,其中前二个是咱们手动建立的,其余两个是使用Deployment建立的。
咱们可使用kubectl delete pod <pod-name>删除一个Deployment建立的Pod,看看结果会怎样:
kubectl delete pod demo-web-deployment-745f7997c4-d24bb # 输出 # pod "demo-web-deployment-745f7997c4-d24bb" deleted
再次查看Pod列表:
kubectl get pods # 输出 # NAME READY STATUS RESTARTS AGE # demo-web 1/1 Running 0 31m # demo-web-copy 1/1 Running 0 22m # demo-web-deployment-745f7997c4-jk9jn 1/1 Running 0 3m39s # demo-web-deployment-745f7997c4-mrrw6 1/1 Running 0 11s
能够看到,又从新建立了一个Pod:demo-web-deployment-745f7997c4-mrrw6,Deployment会监控咱们的Pod数量,保持为咱们预期的个数。
零停机时间部署(Zero-downtime)
如今咱们尝试如下零停机部署,首先修改Deployment中的image为:rainingnight/aspnetcore-web:1.0.0,而后运行以下命令:
kubectl apply -f demo-web-deployment.yaml --record # 输出 # deployment.apps/demo-web-deployment configured
将kubectl的--record设置为true能够在annotation中记录当前命令建立或者升级了该资源。这在将来会颇有用,例如,查看在每一个 Deployment revision 中执行了哪些命令。
除了修改yaml外,还能够直接运行kubectl set image deployment demo-web-deployment web=rainingnight/aspnetcore-web:1.0.0 --record来达到一样的效果。
而后使用以下命令检查服务更新状态:
kubectl rollout status deployment demo-web-deployment # 输出 # Waiting for deployment "demo-web-deployment" rollout to finish: 1 old replicas are pending termination... # Waiting for deployment "demo-web-deployment" rollout to finish: 1 old replicas are pending termination... # Waiting for deployment "demo-web-deployment" rollout to finish: 1 old replicas are pending termination... # Waiting for deployment "demo-web-deployment" rollout to finish: 1 old replicas are pending termination... # Waiting for deployment "demo-web-deployment" rollout to finish: 1 of 2 updated replicas are available... # Waiting for deployment "demo-web-deployment" rollout to finish: 1 of 2 updated replicas are available... # deployment "demo-web-deployment" successfully rolled out
如上能够看到,新版本已经成功上线,并在这个过程当中副本被逐个替换,也就意味着应用程序始终在线。
如今咱们刷新一下浏览器,能够看到标题已经变成了Home page - Web-v1:
回滚到前一个状态
若是忽然发现新上线的版本有Bug,须要紧急回滚到上一个版本,那对Kubernetes来讲也是很是简单的。
咱们首先运行以下命令来查看历史版本:
kubectl rollout history deployment demo-web-deployment # 输出 # deployment.extensions/demo-web-deployment # REVISION CHANGE-CAUSE # 1 <none> # 2 kubectl apply --filename=demo-web-deployment.yaml --record=true
如上,能够看到有2条历史,那么为何第1条的CHANGE-CAUSE是<none>呢,这就是由于咱们第二次部署的时候使用了--record=true参数。如今,咱们想回滚到第一个版本,只需运行以下命令:
kubectl rollout undo deployment demo-web-deployment --to-revision=1 # 输出 # deployment.extensions/demo-web-deployment rolled back
再次刷新浏览器,标题又变回了Home page - Web。
部署多个应用
如今咱们再部署一个ASP.NET Core WebApi程序,并在刚才的Web应用中调用它,造成一个最简单的微服务模式。
部署WebApi
与前面的Web应用的部署相似,就不用过多介绍,定义以下YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: demo-api-deployment labels: app: demo-api-deployment spec: replicas: 2 selector: matchLabels: app: demo-api minReadySeconds: 5 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 1 template: metadata: labels: app: demo-api spec: containers: - name: api image: rainingnight/aspnetcore-api imagePullPolicy: Always ports: - containerPort: 80
而后建立Deployment:
kubectl create -f demo-api-deployment.yaml
查看部署状况:
kubectl get pods # 输出 # NAME READY STATUS RESTARTS AGE # demo-api-deployment-66575d644-9wk7g 1/1 Running 0 75s # demo-api-deployment-66575d644-fknpx 1/1 Running 0 75s # demo-web-deployment-745f7997c4-h7fr8 1/1 Running 0 9m23s # demo-web-deployment-745f7997c4-kvptm 1/1 Running 0 9m23s
前面手动建立的2个Pod已经被我删除了,由于用Deployment建立的Pod就行了。
如今咱们为Api应用也建立一个Service,以便在Web应用中访问它:
apiVersion: v1 kind: Service metadata: name: demo-api-service spec: selector: app: demo-api ports: - protocol: TCP port: 80 targetPort: 80
由于咱们的Api应用是不须要在集群外部访问的,所以服务类型(type)不须要设置,使用默认的ClusterIP就能够了。
部署Servcie:
kubectl create -f demo-api-service.yaml
而后查看Servie:
kubectl get services # 输出 # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # demo-api-service ClusterIP 10.111.25.49 <none> 80/TCP 26s # demo-web-service NodePort 10.105.132.214 <none> 80:30000/TCP 58m
可使用浏览器来访问:10.111.25.49/api/values 来验证一下是否部署成功。
调用服务
那么,还剩下最后一个问题,咱们的Web应用中如何获取到Api应用的访问地址呢?
咱们先看一下,在Web应用的代码中是怎么调用Api的:
// Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("api", _ => _.BaseAddress = new Uri(Configuration["ApiBaseUrl"])); } // FetchData.cshtml public class FetchDataModel : PageModel { private static HttpClient _client; public FetchDataModel(IHttpClientFactory httpClientFactory) { _client = httpClientFactory.CreateClient("api"); } public IList<WeatherForecast> Forecasts { get; set; } public async Task OnGetAsync() { var res = await _client.GetStringAsync("/api/SampleData/WeatherForecasts"); Forecasts = JsonConvert.DeserializeObject<IList<WeatherForecast>>(res); } }
如上,咱们首先注册了一个HttpClient,并从配置文件中读取ApiBaseUrl作为BaseAddress,而后在FetchData页面中使用HttpClient调用Api服务。
由于在Asp.Net Core中,默认状况下,环境变量中的配置是会覆盖appsettings.json中的配置的,所以咱们可使用添加环境变量的方式来配置ApiBaseUrl。
修改demo-web-deployment.yaml,添加env属性,以下:
containers: - name: web image: rainingnight/aspnetcore-web imagePullPolicy: Always ports: - containerPort: 80 env: - name: ApiBaseUrl value: "http://10.111.25.49"
而后在Kubernetes中应用该配置:
kubectl apply -f demo-web-deployment.yaml
等待滚动更新完成,刷新浏览器,点击FetchData菜单:
如上,能够看到数据成功返回,可是直接使用集群IP10.111.25.49,这也有点过低级了,其实咱们能够直接使用域名:http://demo-api-service,修改后以下:
env: - name: ApiBaseUrl value: "http://demo-api-service"
应用该配置,而后刷新浏览器,能够看到完美运行,这是由于CoreDNS(Kube-DNS)帮咱们完成了域名解析。
在Kubernetes 1.11中,CoreDNS已经实现了基于DNS的服务发现的GA,可做为kube-dns插件的替代品。这意味着CoreDNS将做为各类安装工具将来发布版本中的一个选项来提供。事实上,kubeadm团队选择将其做为Kubernetes 1.11的默认选项。
CoreDNS是一个通用的、权威的DNS服务器,提供与Kubernetes后向兼容但可扩展的集成。它解决了kube-dns所遇到的问题,并提供了许多独特的功能,能够解决各类各样的用例。
DNS服务器监控kubernetes建立服务的API, 并为每一个服务建立一组dns记录。若是在整个群集中启用了dns, 全部Pod都会使用它做为DNS服务器。好比咱们的demo-api-service服务,DNS服务器会建立一条"my-service.my-ns"也就是10.111.25.49:demo-api-service.default的dns记录,由于咱们的Web应用和Api应用同一个命名空间中,因此能够直接使用demo-api-service来访问。
总结
本文带领你们一步一步部署了一个最简单的ASP.NET Core MVC + WebApi的微服务程序,介绍了Kubernetes中最基本的三个概念:Pod,Deployment,Service,相信你们对Kubernetes也有了一个全面的认识。
虽然Kubernetes整个体系是很是复杂的,可是不用担忧,一开始咱们不用去求甚解,最重要的是先跑起来,后续我会和你们一块儿逐步深刻,由简到繁,愉快的掌握Kubernetes。