Docker集群管理工具 - Kubernetes 部署记录 (运维小结)

 


Kubernetes 介绍#

Kubernetes是一个全新的基于容器技术的分布式架构领先方案, 它是Google在2014年6月开源的一个容器集群管理系统,使用Go语言开发,Kubernetes也叫K8S。K8S是Google内部一个叫Borg的容器集群管理系统衍生出来的,Borg已经在Google大规模生产运行十年之久。K8S主要用于自动化部署、扩展和管理容器应用,提供了资源调度、部署管理、服务发现、扩容缩容、监控等一整套功能。2015年7月,Kubernetes v1.0正式发布,截止到2017年9月29日最新稳定版本是v1.8。Kubernetes目标是让部署容器化应用简单高效。

Kubernetes最初源于谷歌内部的Borg,提供了面向应用的容器集群部署和管理系统。Kubernetes 的目标旨在消除编排物理/虚拟计算,网络和存储基础设施的负担,并使应用程序运营商和开发人员完全将重点放在以容器为中心的原语上进行自助运营。Kubernetes 也提供稳定、兼容的基础(平台),用于构建定制化的workflows 和更高级的自动化任务。

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

Kubernetes主要功能#

Kubernetes是开源容器集群管理系统,是基于Docker构建一个容器的调度服务,提供资源调度、均衡容灾、服务注册、动态扩缩容等功能套件。Kubernetes提供应用部署、维护、 扩展机制等功能,利用Kubernetes能方便地管理跨机器运行容器化的应用,其主要功能如下:

数据卷: Pod中容器之间共享数据,可以使用数据卷。

应用程序健康检查: 容器内服务可能进程堵塞无法处理请求,可以设置监控检查策略保证应用健壮性。

复制应用程序实例: 控制器维护着Pod副本数量,保证一个Pod或一组同类的Pod数量始终可用。

弹性伸缩: 根据设定的指标(CPU利用率)自动缩放Pod副本数。

服务发现: 使用环境变量或DNS服务插件保证容器中程序发现Pod入口访问地址。

负载均衡: 一组Pod副本分配一个私有的集群IP地址,负载均衡转发请求到后端容器。在集群内部其他Pod可通过这个ClusterIP访问应用。

滚动更新: 更新服务不中断,一次更新一个Pod,而不是同时删除整个服务。

服务编排: 通过文件描述部署服务,使得应用程序部署变得更高效。

资源监控: Node节点组件集成cAdvisor资源收集工具,可通过Heapster汇总整个集群节点资源数据,然后存储到InfluxDB时序数据库,再由Grafana展示。

提供认证和授权: 支持属性访问控制(ABAC)、角色访问控制(RBAC)认证授权策略。

除此之外, Kubernetes主要功能还体现在:
-  使用Docker对应用程序包装(package)、实例化(instantiate)、运行(run)。
-  将多台Docker主机抽象为一个资源,以集群的方式运行、管理跨机器的容器,包括任务调度、资源管理、弹性伸缩、滚动升级等功能。
-  使用编排系统(YAML File)快速构建容器集群,提供负载均衡,解决容器直接关联及通信问题
-  解决Docker跨机器容器之间的通讯问题。
-  自动管理和修复容器,简单说,比如创建一个集群,里面有十个容器,如果某个容器异常关闭,那么,会尝试重启或重新分配容器,始终保证会有十个容器在运行,反而杀死多余的。Kubernetes的自我修复机制使得容器集群总是运行在用户期望的状态. 当前Kubernetes支持GCE、vShpere、CoreOS、OpenShift。

Kubernetes架构和组件#

Kubernetes架设图

kubernetes主要由以下几个核心组件组成:
etcd: 集群的主数据库,保存了整个集群的状态; etcd负责节点间的服务发现和配置共享。etcd分布式键值存储系统, 用于保持集群状态,比如Pod、Service等对象信息。
kube-apiserver: 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;这是kubernetes API,作为集群的统一入口,各组件协调者,以HTTPAPI提供接口服务,所有对象资源的增删改查和监听操作都交给APIServer处理后再提交给Etcd存储。
kube-controller-manager: 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;它用来执行整个系统中的后台任务,包括节点状态状况、Pod个数、Pods和Service的关联等, 一个资源对应一个控制器,而ControllerManager就是负责管理这些控制器的。
kube-scheduler: 资源调度,按照预定的调度策略将Pod调度到相应的机器上;它负责节点资源管理,接受来自kube-apiserver创建Pods任务,并分配到某个节点。它会根据调度算法为新创建的Pod选择一个Node节点。
kubectl: 客户端命令行工具,将接受的命令格式化后发送给kube-apiserver,作为整个系统的操作入口。
kubelet: 负责维护容器的生命周期,负责管理pods和它们上面的容器,images镜像、volumes、etc。同时也负责Volume(CVI)和网络(CNI)的管理;kubelet运行在每个计算节点上,作为agent,接受分配该节点的Pods任务及管理容器,周期性获取容器状态,反馈给kube-apiserver; kubelet是Master在Node节点上的Agent,管理本机运行容器的生命周期,比如创建容器、Pod挂载数据卷、下载secret、获取容器和节点状态等工作。kubelet将每个Pod转换成一组容器。
container runtime: 负责镜像管理以及Pod和容器的真正运行(CRI);
kube-proxy: 负责为Service提供cluster内部的服务发现和负载均衡;它运行在每个计算节点上,负责Pod网络代理。定时从etcd获取到service信息来做相应的策略。它在Node节点上实现Pod网络代理,维护网络规则和四层负载均衡工作。
docker或rocket(rkt): 运行容器。

除了上面的几个核心组建, 还有一些常用插件(Add-ons):
kube-dns: 负责为整个集群提供DNS服务;
Ingress Controller: 为服务提供外网入口;
Heapster: 提供资源监控;
Dashboard: 提供GUI;
Federation: 提供跨可用区的集群;
Fluentd-elasticsearch: 提供集群日志采集、存储与查询;

其中:
master组件包括: kube-apiserver, kube-controller-manager, kube-scheduler;
Node组件包括: kubelet, kube-proxy, docker或rocket(rkt);
第三方服务:etcd

Kubernetes Master控制组件,调度管理整个系统(集群),包含如下组件:
Kubernetes API Server: 作为Kubernetes系统入口,其封装了核心对象的增删改查操作,以RESTful API接口方式提供给外部客户和内部组件调用,维护的REST对象持久化到Etcd中存储。
Kubernetes Scheduler: 为新建立的Pod进行节点(node)选择(即分配机器),负责集群的资源调度。组件抽离,可以方便替换成其他调度器。
Kubernetes Controller: 负责执行各种控制器,目前已经提供了很多控制器来保证Kubernetes的正常运行。
Replication Controller: 管理维护Replication Controller,关联Replication Controller和Pod,保证Replication Controller定义的副本数量与实际运行Pod数量一致。
Node Controller: 管理维护Node,定期检查Node的健康状态,标识出(失效|未失效)的Node节点。
Namespace Controller: 管理维护Namespace,定期清理无效的Namespace,包括Namesapce下的API对象,比如Pod、Service等。
Service Controller: 管理维护Service,提供负载以及服务代理。
EndPoints Controller: 管理维护Endpoints,关联Service和Pod,创建Endpoints为Service的后端,当Pod发生变化时,实时更新Endpoints。
Service Account Controller: 管理维护Service Account,为每个Namespace创建默认的Service Account,同时为Service Account创建Service Account Secret。
Persistent Volume Controller: 管理维护Persistent Volume和Persistent Volume Claim,为新的Persistent Volume Claim分配Persistent Volume进行绑定,为释放的Persistent Volume执行清理回收。
Daemon Set Controller: 管理维护Daemon Set,负责创建Daemon Pod,保证指定的Node上正常的运行Daemon Pod。
Deployment Controller: 管理维护Deployment,关联Deployment和Replication Controller,保证运行指定数量的Pod。当Deployment更新时,控制实现Replication Controller和 Pod的更新。
Job Controller: 管理维护Job,为Jod创建一次性任务Pod,保证完成Job指定完成的任务数目
Pod Autoscaler Controller: 实现Pod的自动伸缩,定时获取监控数据,进行策略匹配,当满足条件时执行Pod的伸缩动作。

Kubernetes Node运行节点,运行管理业务容器,包含如下组件:
Kubelet: 负责管控容器,Kubelet会从Kubernetes API Server接收Pod的创建请求,启动和停止容器,监控容器运行状态并汇报给Kubernetes API Server。
Kubernetes Proxy: 负责为Pod创建代理服务,Kubernetes Proxy会从Kubernetes API Server获取所有的Service信息,并根据Service的信息创建代理服务,实现Service到Pod的请求路由和转发,从而实现Kubernetes层级的虚拟转发网络。
Docker:  Node上需要运行容器服务

Kubernetes的分层设计理念
Kubernetes设计理念和功能类似Linux的分层架构,如下图:

核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境;
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等);
管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等);
接口层:kubectl命令行工具、客户端SDK以及集群联邦;
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴:    
-  Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等;    
-  Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等;

Kubelet流程图:

根据上图可知Kubelet是Kubernetes集群中每个Minion和Master API Server的连接点,Kubelet运行在每个Minion上,是Master API Server和Minion之间的桥梁,
接收Master API Server分配给它的commands和work,与持久性键值存储etcd、file、server和http进行交互,读取配置信息。Kubelet的主要工作是管理Pod和容
器的生命周期,其包括Docker Client、Root Directory、Pod Workers、Etcd Client、Cadvisor Client以及Health Checker组件,具体工作如下:
-  通过Worker给Pod异步运行特定的Action;
-  设置容器的环境变量;
 给容器绑定Volume;
-  给容器绑定Port;
 根据指定的Pod运行一个单一容器;
-  杀死容器;
 给指定的Pod创建network 容器;
-  删除Pod的所有容器;
 同步Pod的状态;
-  从Cadvisor获取container info、 pod info、root info、machine info;
 检测Pod的容器健康状态信息;
-  在容器中运行命令;

Kubernetes基本对象概念#

Kubernetes中的大部分概念Node、Pod、Replication Controller、Service等都可以看作一种“资源对象”,几乎所有的资源对象都可以通过kubectl工具(API调用)执行增、删、改、查等操作并将其保存在etcd中持久化存储。从这个角度来看,kubernetes其实是一个高度自动化的资源控制系统,通过跟踪对比etcd库里保存的“资源期望状态”与当前环境中的“实际资源状态”的差异来实现自动控制和自动纠错的高级功能。

基本对象:
Pod: Pod是最小部署单元,一个Pod有一个或多个容器组成,Pod中容器共享存储和网络,在同一台Docker主机上运行; Pod 中的容器会作为一个整体被Master调度到一个Node上运行。
Service : Service一个应用服务抽象,定义了Pod逻辑集合和访问这个Pod集合的策略。Service代理Pod集合对外表现是为一个访问入口,分配一个集群IP地址,来自这个IP的请求将负载均衡转发后端Pod中的容器。Service通过LableSelector选择一组Pod提供服务。
Volume: 数据卷,共享Pod中容器使用的数据。
Namespace: 命名空间将对象逻辑上分配到不同Namespace,可以是不同的项目、用户等区分管理,并设定控制策略,从而实现多租户。命名空间也称为虚拟集群。
Lable: 标签用于区分对象(比如Pod、Service),键/值对存在;每个对象可以有多个标签,通过标签关联对象。

基于基本对象更高层次抽象:  
ReplicaSet: 下一代ReplicationController。确保任何给定时间指定的Pod副本数量,并提供声明式更新等功能。RC与RS唯一区别就是lableselector支持不同,RS支持新的基于集合的标签,RC仅支持基于等式的标签。
Deployment: Deployment是一个更高层次的API对象,它管理ReplicaSets和Pod,并提供声明式更新等功能。官方建议使用Deployment管理ReplicaSets,而不是直接使用ReplicaSets,这就意味着可能永远不需要直接操作ReplicaSet对象。
StatefulSet: StatefulSet适合持久性的应用程序,有唯一的网络标识符(IP),持久存储,有序的部署、扩展、删除和滚动更新。
DaemonSet: DaemonSet确保所有(或一些)节点运行同一个Pod。当节点加入Kubernetes集群中,Pod会被调度到该节点上运行,当节点从集群中移除时,DaemonSet的Pod会被删除。删除DaemonSet会清理它所有创建的Pod。
Job: 一次性任务,运行完成后Pod销毁,不再重新启动新容器。还可以任务定时运行。Kubernetes中的Job 用于运行结束就删除的应用。

kubernetes概念说明

API对象是K8s集群中的管理操作单元。K8s集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的API对象,支持对该功能的管理操作。例如副本集Replica Set对应的API对象是RS。

Kubernetes中所有的配置都是通过API对象的spec去设置的,也就是用户通过配置系统的理想状态来改变系统,这是k8s重要设计理念之一,即所有的操作都是声明式 (Declarative) 的而不是命令式(Imperative)的。声明式操作在分布式系统中好处是稳定,不怕丢操作或运行多次,例如设置副本数为3的操作运行多次也还是一个结果, 而给副本数加1的操作就不是声明式的, 运行多次结果就错了。

Cluster
Cluster 是计算、存储和网络资源的集合,Kubernetes 利用这些资源运行各种基于容器的应用

Master
kubernetes集群的管理节点,负责管理集群,提供集群的资源数据访问入口。拥有Etcd存储服务(可选),运行Api Server进程,Controller Manager服务进程及Scheduler服务进程,关联工作节点Node。Kubernetes API server提供HTTP Rest接口的关键服务进程,是Kubernetes里所有资源的增、删、改、查等操作的唯一入口。也是集群控制的入口进程;Kubernetes Controller Manager是Kubernetes所有资源对象的自动化控制中心;Kubernetes Schedule是负责资源调度(Pod调度)的进程.

Node
Node是Kubernetes集群架构中运行Pod的服务节点(亦叫agent或minion)。Node是Kubernetes集群操作的单元,用来承载被分配Pod的运行,是Pod运行的宿主机。关联Master管理节点,拥有名称和IP、系统资源信息。运行docker eninge服务,守护进程kunelet及负载均衡器kube-proxy. 每个Node节点都运行着以下一组关键进程: 
-  kubelet:负责对Pod对于的容器的创建、启停等任务
-  kube-proxy:实现Kubernetes Service的通信与负载均衡机制的重要组件
-  Docker Engine(Docker):Docker引擎,负责本机容器的创建和管理工作

Node节点可以在运行期间动态增加到Kubernetes集群中,默认情况下,kubelet会想master注册自己,这也是Kubernetes推荐的Node管理方式,kubelet进程会定时向Master汇报自身情报,如操作系统、Docker版本、CPU和内存,以及有哪些Pod在运行等等,这样Master可以获知每个Node节点的资源使用情况,冰实现高效均衡的资源调度策略。、

Pod
运行于Node节点上,若干相关容器的组合。Pod内包含的容器运行在同一宿主机上,使用相同的网络命名空间、IP地址和端口,能够通过localhost进行通。Pod是Kurbernetes进行创建、调度和管理的最小单位,它提供了比容器更高层次的抽象,使得部署和管理更加灵活。一个Pod可以包含一个容器或者多个相关容器。

Pod其实有两种类型:普通Pod和静态Pod,后者比较特殊,它并不存在Kubernetes的etcd存储中,而是存放在某个具体的Node上的一个具体文件中,并且只在此Node上启动。普通Pod一旦被创建,就会被放入etcd存储中,随后会被Kubernetes Master调度到摸个具体的Node上进行绑定,随后该Pod被对应的Node上的kubelet进程实例化成一组相关的Docker容器并启动起来。在默认情况下,当Pod里的某个容器停止时,Kubernetes会自动检测到这个问起并且重启这个Pod(重启Pod里的所有容器),如果Pod所在的Node宕机,则会将这个Node上的所有Pod重新调度到其他节点上。

Pod是在K8s集群中运行部署应用或服务的最小单元,它是可以支持多容器的。Pod的设计理念是支持多个容器在一个Pod中共享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务.比如你运行一个操作系统发行版的软件仓库,一个Nginx容器用来发布软件,另一个容器专门用来从源仓库做同步,这两个容器的镜像不太可能是一个团队开发的,但是他们一块儿工作才能提供一个微服务;这种情况下,不同的团队各自开发构建自己的容器镜像,在部署的时候组合成一个微服务对外提供服务.

Kubernetes 引入 Pod 主要基于下面两个目的:
- 可管理性
有些容器天生就是需要紧密联系, 一起工作。Pod 提供了比容器更高层次的抽象,将它们封装到一个部署单元中。Kubernetes 以 Pod 为最小单位进行调度、扩展、共享资源、管理生命周期。

- 通信和资源共享
Pod 中的所有容器使用同一个网络 namespace,即相同的 IP 地址和 Port 空间。它们可以直接用 localhost 通信。同样的,这些容器可以共享存储,当 Kubernetes 挂载 volume 到 Pod,本质上是将 volume 挂载到 Pod 中的每一个容器。

File Puller 会定期从外部的 Content Manager 中拉取最新的文件,将其存放在共享的 volume 中。Web Server 从 volume 读取文件,响应 Consumer 的请求。这两个容器是紧密协作的,它们一起为 Consumer 提供最新的数据;同时它们也通过 volume 共享数据。所以放到一个 Pod 是合适的。

Controller
Kubernetes 通常不会直接创建 Pod,而是通过 Controller 来管理 Pod 的。Controller 中定义了 Pod 的部署特性,比如有几个副本,在什么样的 Node 上运行等。为了满足不同的业务场景, Kubernetes 提供了多种 Controller,包括 Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job 等. 

Replication Controller (副本集RC)
Replication Controller用来管理Pod的副本,保证集群中存在指定数量的Pod副本。集群中副本的数量大于指定数量,则会停止指定数量之外的多余容器数量,反之,则会启动少于指定数量个数的容器,保证数量不变。Replication Controller是实现弹性伸缩、动态扩容和滚动升级的核心。

通过监控运行中的Pod来保证集群中运行指定数目的Pod副本。少于指定数目,RC就会启动运行新的Pod副本;多于指定数目,RC就会杀死多余的Pod副本 (这是k8s早期技术概念)

Replica Set (副本集RS)
RS是新一代RC,提供同样的高可用能力,区别主要在于RS后来居上,能支持更多种类的匹配模式。副本集对象一般不单独使用,而是作为Deployment的理想状态参数使用. Replica Set 实现了 Pod 的多副本管理。使用 Deployment 时会自动创建 ReplicaSet,也就是说 Deployment 是通过 ReplicaSet 来管理 Pod 的多个副本,我们通常不需要直接使用 ReplicaSet。

Deployment (部署)
Deployment 是最常用的 Controller,Deployment 可以管理 Pod 的多个副本,并确保 Pod 按照期望的状态运行。Deployment是一个比RS应用模式更广的API对象,支持动态扩展。可以创建一个新的服务,更新一个新的服务,也可以是滚动升级一个服务。滚动升级一个服务,实际是创建一个新的RS,然后逐渐将新RS中副本数增加到理想状态,将旧RS中的副本数减小到0的复合操作 (逐步升级新得副本,剔除旧的副本). 
总结:RC、RS和Deployment只是保证了支撑服务的微服务Pod的数量.

DaemonSet
DaemonSet 用于每个 Node 最多只运行一个 Pod 副本的场景。正如其名称所揭示的,DaemonSet 通常用于运行 daemon。

StatefuleSet
StatefuleSet 能够保证 Pod 的每个副本在整个生命周期中名称是不变的。而其他 Controller 不提供这个功能,当某个 Pod 发生故障需要删除并重新启动时,Pod 的名称会发生变化。同时 StatefuleSet 会保证副本按照固定的顺序启动、更新或者删除。

Service

Service定义了Pod逻辑集合和访问该集合的策略,是真实服务的抽象。Service提供了统一的服务访问入口以及服务代理和发现机制,关联多个相同Label的Pod,用户不需要了解后台Pod是如何运行。
外部系统访问Service的问题:
->  首先需要弄明白Kubernetes的三种IP这个问题
       Node IP:Node节点的IP地址
    -  Pod IP: Pod的IP地址
     Cluster IP:Service的IP地址
->   首先,Node IP是Kubernetes集群中节点的物理网卡IP地址,所有属于这个网络的服务器之间都能通过这个网络直接通信。这也表明Kubernetes集群之外的节点访问Kubernetes集群之内的某个节点或者TCP/IP服务的时候,必须通过Node IP进行通信
->  其次,Pod IP是每个Pod的IP地址,他是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络。

最后Cluster IP是一个虚拟的IP,但更像是一个伪造的IP网络,原因有以下几点: 
->  Cluster IP仅仅作用于Kubernetes Service这个对象,并由Kubernetes管理和分配P地址
->  Cluster IP无法被ping,他没有一个“实体网络对象”来响应
->  Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备通信的基础,并且他们属于Kubernetes集群这样一个封闭的空间。
->  Kubernetes集群之内,Node IP网、Pod IP网于Cluster IP网之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊路由规则。

RC、RS和Deployment只是保证了支撑服务的微服务Pod的数量,但是没有解决如何访问这些服务的问题。一个Pod只是一个运行服务的实例,随时可能在一个节点上停止,在另一个节点以一个新的IP启动一个新的Pod,因此不能以确定的IP和端口号提供服务。要稳定地提供服务需要服务发现和负载均衡能力。服务发现完成的工作,是针对客户端访问的服务,找到对应的的后端服务实例。在K8s集群中,客户端需要访问的服务就是Service对象。每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问一个服务。在K8s集群中微服务的负载均衡是由Kube-proxy实现的。Kube-proxy是K8s集群内部的负载均衡器。它是一个分布式代理服务器,在K8s的每个节点上都有一个;这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。与之相比,我们平时在服务器端做个反向代理做负载均衡,还要进一步解决反向代理的负载均衡和高可用问题。

Kubernetes 运行容器(Pod)与访问容器(Pod)这两项任务分别由 Controller 和 Service 执行。

Namespace
名字空间为K8s集群提供虚拟的隔离作用,K8s集群初始有两个名字空间,分别是默认名字空间default和系统名字空间kube-system,除此以外,管理员可以可以创建新的名字空间满足需要。

Label
Kubernetes中任意API对象都是通过Label进行标识,Label的实质是一系列的Key/Value键值对,其中key于value由用户自己指定。Label可以附加在各种资源对象上,如Node、Pod、Service、RC等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去。Label是Replication Controller和Service运行的基础,二者通过Label来进行关联Node上运行的Pod。

我们可以通过给指定的资源对象捆绑一个或者多个不同的Label来实现多维度的资源分组管理功能,以便于灵活、方便的进行资源分配、调度、配置等管理工作。
一些常用的Label如下:
版本标签:"release":"stable","release":"canary"......
环境标签:"environment":"dev","environment":"qa","environment":"production"
架构标签:"tier":"frontend","tier":"backend","tier":"middleware"
分区标签:"partition":"customerA","partition":"customerB"
质量管控标签:"track":"daily","track":"weekly"

Label相当于我们熟悉的标签,给某个资源对象定义一个Label就相当于给它大了一个标签,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,Kubernetes通过这种方式实现了类似SQL的简单又通用的对象查询机制。

Label Selector在Kubernetes中重要使用场景如下:
-> kube-Controller进程通过资源对象RC上定义Label Selector来筛选要监控的Pod副本的数量,从而实现副本数量始终符合预期设定的全自动控制流程;
-> kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立起每个Service岛对应Pod的请求转发路由表,从而实现Service的智能负载均衡;
-> 通过对某些Node定义特定的Label,并且在Pod定义文件中使用Nodeselector这种标签调度策略,kuber-scheduler进程可以实现Pod”定向调度“的特性;

======================================================================================
重要概念小结 (细节):

Master:集群控制管理节点,所有的命令都经由master处理。

Node:是kubernetes集群的工作负载节点。Master为其分配工作,当某个Node宕机时,Master会将其工作负载自动转移到其他节点。

Node节点可动态增加到kubernetes集群中,前提是这个节点已经正确安装、配置和启动了上述的关键进程,默认情况下,kubelet会向Master注册自己,这也kubernetes推荐的Node管理方式。一旦Node被纳入集群管理范围,kubelet会定时向Master汇报自身的情况,以及之前有哪些Pod在运行等,这样Master可以获知每个Node的资源使用情况,并实现高效均衡的资源调度策略。如果Node没有按时上报信息,则会被Master判断为失联,Node状态会被标记为Not Ready,随后Master会触发工作负载转移流程。

Pod:是kubernetes最重要也是最基本的概念。每个Pod都会包含一个 “根容器”,还会包含一个或者多个紧密相连的业务容器。

Kubernetes为每个Pod都分配了唯一IP地址, 称之为PodIP, 一个Pod里多个容器共享PodIP地址. 要求底层网络支持集群内任意两个Pod之间的直接通信,通常采用虚拟二层网络技术来实现 (Flannel).

Label:是一个key=value的键值对,其中key与value由用户指定, 可以附加到各种资源对象上, 一个资源对象可以定义任意数量的Label。可以通过LabelSelector(标签选择器)查询和筛选资源对象。

RC:Replication Controller声明某个Pod的副本数在任意时刻都符合某个预期值。定义包含如下:
-  Pod期待的副本数(replicas);
-  用于筛选目标Pod的Label Selector;
-  当Pod副本数小于期望时,用于新的创建Pod的模板template;

需要注意
-  通过改变RC里的Pod副本数量,可以实现Pod的扩容或缩容功能;
-  通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级功能;

Service:“微服务”,kubernetes中的核心。通过分析、识别并建模系统中的所有服务为微服务,最终系统有多个提供不同业务能力而又彼此独立的微服务单元所组成,服务之间通过TCP/IP进行通信。每个Pod都会被分配一个单独的IP地址,而且每个Pod都提供了一个独立的Endpoint以被客户端访问。

客户端如何访问?
部署负载均衡器,为Pod开启对外服务端口,将Pod的Endpoint列表加入转发列表中,客户端通过负载均衡器的对外IP+Port来访问此服务。每个Service都有一个全局唯一的虚拟ClusterIP,这样每个服务就变成了具备唯一IP地址的“通信节点”,服务调用就变成了最基础的TCP网络通信问题。

Volume:是Pod中能够被多个容器访问的共享目录。定义在Pod之上,被一个Pod里的多个容器挂载到具体的文件目录之下;Volume与Pod生命周期相同。Volume可以让一个Pod里的多个容器共享文件、让容器的数据写到宿主机的磁盘上或者写文件到 网络存储中,具体如下图所示:

在kubernetes1.2的时候,RC就由Replication Controller升级成Replica Set,“下一代RC”。命令兼容适用,Replica Set主要被Deployment这个更高层的资源对象所使用,从而形成一套Pod创建、删除、更新的编排机制。当我们使用Deployment时,无需关心它是如何创建和维护ReplicaSet的,这一切是自动发生的。

Docker: 既然k8s是基于容器的,那么就不得不提到docker。2013年初,docker横空出世,孕育着新思想的“容器”,Docker选择容器作为核心和基础,以容器为资源分割和调度的基本单位,封装整个软件运行时环境,为开发者和系统管理员设计,用于构建、发布和运行分布式应用的平台。是一个跨平台、可移植并且简单易用的容器解决方案。通过操作系统内核技术(namespaces、cgroups等)为容器提供资源隔离与安全保障。

上图是一个image的简单使用。我们可以通过一个dockerfile来build自己的image。可以把image上传(push)到自己的私有镜像仓库,也可以从私有仓库pull到本地进行使用。可以单独使用命令行,直接run container,可以对container进行stop、start、restart操作。也可以对image进行save保存操作以及加载load操作,大家具体可以根据自己的使用,选择不同的操作即可。

Docker资源隔离技术
Docker选择容器作为核心和基础,以容器为资源分割和调度的基本单位,封装整个软件运行时环境,为开发者和系统管理员设计,用于构建、发布和运行分布式应用的平台。Docker是一个跨平台、可移植并且简单易用的容器解决方案, 通过操作系统内核技术(namespaces、cgroups等)为容器提供资源隔离与安全保障。

Docker监控
cAdvisor(Container Advisor)是Google开发的用于分析运行中容器的资源占用和性能指标的开源工具。cAdvisor是一个运行时的守护进程,负责收集、聚合、处理和输出运行中容器的信息。对于每个容器,cAdvisor都有资源隔离参数、资源使用历史情况以及完整的历史资源使用和网络统计信息的柱状图。cAdvisor不但可以为用户提供监控服务,还可以结合其他应用为用户提供良好的服务移植和定制。包括结合InfluxDB对数据进行存储,以及结合Grafana提供web控制台,自定义查询指标,并进行展示:

etcd: etcd是一个键值存储仓库,用于配置共享和服务发现。etcd受Zookeeper与doozer启发而催生的项目。

etcd架构:

etcd存储
etcd的存储分为内部存储和持久化(硬盘)存储两部分。内存中的存储除了顺序化地记录所有用户对节点数据变更的记录外,还会对用户数据进行索引、建堆等方便查询的操作。而持久化则使用WAL进行记录存储。在k8s中,所有数据的存储以及操作记录都在etcd中进行存储,所以对于k8s集群来说,etcd是相当重要的,一旦故障,可能导致整个集群的瘫痪或者数据丢失。
在WAL体系中,所有的数据在提交之前都会进行日志记录。持久化存储的目录分为两个:snap和wal。snapshot相当于数据压缩,默认会将10000条wal操作记录merge成snapshot,节省存储,又保证数据不会丢失。其中:
-   WAL:存储所有事务的变化记录
-   Snapshot:用于存放某一时刻etcd所有目录的数据

etcd核心算法
etcd的核心算法是raft算法,强一致性算法。具体如下图所示:

需要注意:由于etcd是负责存储,所以不建议搭建单点集群,如zookeeper一样,由于存在选举策略,所以一般推荐奇数个集群,如3,5,7。只要集群半数以上的结点存活,那么集群就可以正常运行,否则集群可能无法正常使用。

Kubernetes 集群环境部署记录#

Kubernetes (K8s)是docker容器用来编排和管理的工具.

 

通过kubectl向k8s Master发出指令, kubernetes Master主要是提供API Server、Scheduler、Controller组件,接收kubectl的命令,从Node节点获取Node的资源信息,并发出调度任务。Node节点提供kubelet、kube-proxy,每个node节点都安装docker,是实际的执行者。kubernetes不负责网络,所以一般是用flannel或者weave。etcd负责服务发现和node信息存储.

kubernetes基本部署步骤
-  node节点安装docker
-  node节点配置跨主机容器通信
-  master节点部署etcd、kube-apiserver、kube-controller-manager和kube-scheduler组件
-  node节点部署kubelet、kube-proxy组件

如果minion主机没有安装docker,启动kubelet时会报如下错误:
Could not load kubeconfig file /var/lib/kubelet/kubeconfig: stat /var/lib/kubelet/kubeconfig: no such file or directory. Trying auth path instead.
Could not load kubernetes auth path /var/lib/kubelet/kubernetes_auth: stat /var/lib/kubelet/kubernetes_auth: no such file or directory. Continuing with defaults.
No cloud provider specified.

1.  环境准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
ip地址               主机名              节点功能
172.16.60.220       k8s-master01       Master, etcd, registry
172.16.60.221       k8s-node01         Node01
172.16.60.222       k8s-node02         Node02
 
[root@k8s-master01 ~]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
 
设置三个节点的主机名
[root@k8s-master01 ~]# hostnamectl --static set-hostname  k8s-master01
[root@k8s-node01 ~]# hostnamectl --static set-hostname  k8s-node01
[root@k8s-node02 ~]# hostnamectl --static set-hostname  k8s-node02
 
分别绑定三个节点的hosts
[root@k8s-master01 ~]# cat /etc/hosts
.........
172.16.60.220    k8s-registry
172.16.60.221    k8s-node01
172.16.60.222    k8s-node02
 
关闭三台机器上的防火墙和selinux
[root@k8s-master01 ~]# systemctl disable firewalld.service
[root@k8s-master01 ~]# systemctl stop firewalld.service
[root@k8s-master01 ~]# firewall-cmd --state
not running
 
[root@k8s-master01 ~]# setenforce 0
[root@k8s-master01 ~]# cat /etc/sysconfig/selinux
........
SELINUX=disabled
 
[root@k8s-master01 ~]# getenforce                
Disabled

2) 部署Mater节点 (172.16.60.220) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
1) 现安装docker
[root@k8s-master01 ~]# yum install -y docker
  
[root@k8s-master01 ~]# docker --version
Docker version 1.13.1, build 07f3374/1.13.1
  
2) 安装私有仓库Registry (实际生产环境下, 私有仓库Registry可以部署在其他机器上)
[root@k8s-master01 ~]# docker pull registry
 
顺便下载其他几个docker镜像, 用于本案例测试所用
[root@k8s-master01 ~]# docker pull nginx
[root@k8s-master01 ~]# docker pull tomcat
[root@k8s-master01 ~]# docker pull centos
 
查看docker镜像
[root@k8s-master01 ~]# docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
docker.io/centos     latest              9f38484d220f        11 hours ago        202 MB
docker.io/registry   latest              f32a97de94e1        7 days ago          25.8 MB
docker.io/tomcat     latest              dd6ff929584a        9 days ago          463 MB
docker.io/nginx      latest              881bd08c0b08        10 days ago         109 MB
 
默认情况下,会将私有仓库存放于容器内的/tmp/registry目录下,这样如果容器被删除,则存放于容器中的镜像也会丢失。
所以一般情况下会指定本地一个目录挂载到容器内的/tmp/registry下,如下:
[root@k8s-master01 ~]# docker run -d --name=my_registry -p 5000:5000 -v /opt/data/registry:/tmp/registry docker.io/registry
efca665f3395f9148bc1b40287feef4175966b30975d1e1acaffa3909c9e026f
 
查看启动的registry容器
[root@k8s-master01 ~]# docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                    NAMES
efca665f3395        docker.io/registry   "/entrypoint.sh /e..."   17 seconds ago      Up 16 seconds       0.0.0.0:5000->5000/tcp   my_registry
 
由上可以看到,已经启动了一个registry私有仓库容器,地址为:172.16.60.220:5000
 
接下来可以把本地镜像(上面下载的三个镜像)push到私有仓库中 (修改镜像的tag标识)
[root@k8s-master01 ~]# docker tag nginx 172.16.60.220:5000/nginx
[root@k8s-master01 ~]# docker tag tomcat 172.16.60.220:5000/tomcat
[root@k8s-master01 ~]# docker tag centos 172.16.60.220:5000/centos
 
[root@k8s-master01 ~]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
172.16.60.220:5000/centos   latest              9f38484d220f        11 hours ago        202 MB
docker.io/centos            latest              9f38484d220f        11 hours ago        202 MB
docker.io/registry          latest              f32a97de94e1        7 days ago          25.8 MB
172.16.60.220:5000/tomcat   latest              dd6ff929584a        9 days ago          463 MB
docker.io/tomcat            latest              dd6ff929584a        9 days ago          463 MB
docker.io/nginx             latest              881bd08c0b08        10 days ago         109 MB
172.16.60.220:5000/nginx    latest              881bd08c0b08        10 days ago         109 MB
 
接下来把上面修改tag后的镜像上传到私有仓库
[root@k8s-master01 ~]# docker push 172.16.60.220:5000/nginx
The push refers to a repository [172.16.60.220:5000/nginx]
  
出现上面报错: 因为docker默认使用https协议,但是本地现在只支持http协议.
为了解决https的报错, 需要配置daemon.json的insecure-registries, 具体如下:
[root@k8s-master01 ~]# cat /etc/docker/daemon.json             # 默认该配置文件为空
{}
  
[root@k8s-master01 ~]# vim /etc/docker/daemon.json
{
   "insecure-registries": [
      "172.16.60.220:5000"
    ]
}
  
==================================================================
除了上面的方法外, 还可以在/etc/sysconfig/docker 文件里添加"OPTIONS='--insecure-registry 172.16.60.220:5000'"
==================================================================
  
接着重启docker和registry容器
[root@k8s-master01 ~]# systemctl restart docker
[root@k8s-master01 ~]# docker start my_registry
my_registry
[root@k8s-master01 ~]# docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                    NAMES
dea03edc49fd        docker.io/registry   "/entrypoint.sh /e..."   8 minutes ago       Up 39 seconds       0.0.0.0:5000->5000/tcp   my_registry
  
然后重新将上面修改tag后的镜像上传到私有仓库
[root@k8s-master01 ~]# docker push 172.16.60.220:5000/tomcat
[root@k8s-master01 ~]# docker push 172.16.60.220:5000/nginx
[root@k8s-master01 ~]# docker push 172.16.60.220:5000/centos
 
查看私有仓库里的镜像(一定要保证下面能查看到仓库里有镜像. 如果仓库里没有镜像,那么客户端机器就无法从该私有仓库下载镜像了)
[root@k8s-master01 ~]# curl -XGET http://172.16.60.220:5000/v2/_catalog
{"repositories":["centos","nginx","tomcat"]}
  
[root@k8s-master01 ~]# curl -XGET http://172.16.60.220:5000/v2/centos/tags/list
{"name":"centos","tags":["latest"]}
  
[root@k8s-master01 ~]# curl -XGET http://172.16.60.220:5000/v2/nginx/tags/list
{"name":"nginx","tags":["latest"]}
  
[root@k8s-master01 ~]# curl -XGET http://172.16.60.220:5000/v2/tomcat/tags/list
{"name":"tomcat","tags":["latest"]}
 
现在可以将本地的镜像删除,然后测试从私有仓库中下载
[root@k8s-master01 ~]# docker rmi docker.io/tomcat
[root@k8s-master01 ~]# docker rmi docker.io/nginx
[root@k8s-master01 ~]# docker rmi docker.io/centos
[root@k8s-master01 ~]# docker rmi 172.16.60.220:5000/centos
[root@k8s-master01 ~]# docker rmi 172.16.60.220:5000/nginx
[root@k8s-master01 ~]# docker rmi 172.16.60.220:5000/tomcat
  
[root@k8s-master01 ~]# docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
docker.io/registry   latest              f32a97de94e1        6 days ago          25.8 MB
  
重新从私仓里下载
[root@k8s-master01 ~]# docker pull 172.16.60.220:5000/nginx
[root@k8s-master01 ~]# docker pull 172.16.60.220:5000/tomcat
[root@k8s-master01 ~]# docker pull 172.16.60.220:5000/centos
 
[root@k8s-master01 ~]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
172.16.60.220:5000/centos   latest              9f38484d220f        11 hours ago        202 MB
docker.io/registry          latest              f32a97de94e1        7 days ago          25.8 MB
172.16.60.220:5000/tomcat   latest              dd6ff929584a        9 days ago          463 MB
172.16.60.220:5000/nginx    latest              881bd08c0b08        10 days ago         109 MB
 
3) 安装etcd
k8s运行依赖etcd,需要先部署etcd,下面采用yum方式安装:
[root@k8s-master01 ~]# yum install -y etcd
  
yum安装的etcd默认配置文件在/etc/etcd/etcd.conf,编辑配置文件:
[root@k8s-master01 ~]# cp /etc/etcd/etcd.conf /etc/etcd/etcd.conf.bak
[root@k8s-master01 ~]# >/etc/etcd/etcd.conf
[root@k8s-master01 ~]# vim /etc/etcd/etcd.conf
#[member]
#节点名称
ETCD_NAME=k8s-master
#数据存放位置                                                    
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"                
#ETCD_WAL_DIR=""
#ETCD_SNAPSHOT_COUNT="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
#ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
#监听客户端地址
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001"           
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
#
#[cluster]
#ETCD_INITIAL_ADVERTISE_PEER_URLS="http://localhost:2380"
# if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..."
#ETCD_INITIAL_CLUSTER="default=http://localhost:2380"
#ETCD_INITIAL_CLUSTER_STATE="new"
#ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
#通知客户端地址
ETCD_ADVERTISE_CLIENT_URLS="http://172.16.60.220:2379,http://172.16.60.220:4001"            
#ETCD_DISCOVERY=""
#ETCD_DISCOVERY_SRV=""
#ETCD_DISCOVERY_FALLBACK="proxy"
#ETCD_DISCOVERY_PROXY=""
  
启动etcd并验证状态
[root@k8s-master01 ~]# systemctl start etcd
  
[root@k8s-master01 ~]# ps -ef|grep etcd     
etcd       977     1  1 12:47 ?        00:00:00 /usr/bin/etcd --name=k8s-master --data-dir=/var/lib/etcd/default.etcd --listen-client-urls=http://0.0.0.0:2379,http://0.0.0.0:4001
root      1467  7564  0 12:47 pts/1    00:00:00 grep --color=auto etcd
  
[root@k8s-master01 ~]# lsof -i:2379         
COMMAND PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
etcd    977 etcd    6u  IPv6 259685220      0t0  TCP *:2379 (LISTEN)
etcd    977 etcd   13u  IPv4 259683141      0t0  TCP localhost:54160->localhost:2379 (ESTABLISHED)
etcd    977 etcd   14u  IPv6 259683142      0t0  TCP localhost:2379->localhost:54160 (ESTABLISHED)
  
[root@k8s-master01 ~]# lsof -i:4001         
COMMAND PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
etcd    977 etcd    7u  IPv6 259685221      0t0  TCP *:newoak (LISTEN)
etcd    977 etcd   11u  IPv4 259683140      0t0  TCP localhost:56102->localhost:newoak (ESTABLISHED)
etcd    977 etcd   15u  IPv6 259688733      0t0  TCP localhost:newoak->localhost:56102 (ESTABLISHED)
  
测试etcd
[root@k8s-master01 ~]# etcdctl set testdir/testkey0 0
0
[root@k8s-master01 ~]# etcdctl get testdir/testkey0
0
[root@k8s-master01 ~]# etcdctl -C http://172.16.60.220:2379 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://172.16.60.220:2379
cluster is healthy
  
[root@k8s-master01 ~]# etcdctl -C http://172.16.60.220:4001 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://172.16.60.220:2379
cluster is healthy
  
4) 安装kubernets
[root@k8s-master01 ~]# yum install -y kubernetes
  
配置并启动kubernetes
在kubernetes master上需要运行以下组件:Kubernets API Server、Kubernets Controller Manager、Kubernets Scheduler
  
[root@k8s-master01 ~]# cp /etc/kubernetes/apiserver /etc/kubernetes/apiserver.bak
[root@k8s-master01 ~]# >/etc/kubernetes/apiserver
[root@k8s-master01 ~]# vim /etc/kubernetes/apiserver
###
# kubernetes system config
#
# The following values are used to configure the kube-apiserver
#
     
# The address on the local server to listen to.
KUBE_API_ADDRESS="--insecure-bind-address=0.0.0.0"
     
# The port on the local server to listen on.
KUBE_API_PORT="--port=8080"
     
# Port minions listen on
# KUBELET_PORT="--kubelet-port=10250"
     
# Comma separated list of nodes in the etcd cluster
KUBE_ETCD_SERVERS="--etcd-servers=http://172.16.60.220:2379"
     
# Address range to use for services
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=172.16.0.0/16"
     
# default admission control policies
#KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"
KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ResourceQuota"
     
# Add your own!
KUBE_API_ARGS=""
  
[root@k8s-master01 ~]# cp /etc/kubernetes/config /etc/kubernetes/config.bak
[root@k8s-master01 ~]# >/etc/kubernetes/config
[root@k8s-master01 ~]# vim /etc/kubernetes/config
###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
#   kube-apiserver.service
#   kube-controller-manager.service
#   kube-scheduler.service
#   kubelet.service
#   kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"
   
# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"
   
# Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=false"
   
# How the controller-manager, scheduler, and proxy find the apiserver
KUBE_MASTER="--master=http://172.16.60.220:8080"
  
启动服务并设置开机自启动
[root@k8s-master01 ~]# systemctl enable kube-apiserver.service
[root@k8s-master01 ~]# systemctl start kube-apiserver.service
[root@k8s-master01 ~]# systemctl enable kube-controller-manager.service
[root@k8s-master01 ~]# systemctl start kube-controller-manager.service
[root@k8s-master01 ~]# systemctl enable kube-scheduler.service
[root@k8s-master01 ~]# systemctl start kube-scheduler.service

3) 部署Node节点 (172.16.60.221, 172.16.60.222)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
3) 安装kubernets  (两个节点同样操作)
[root@k8s-node01 ~]# yum install kubernetes
  
配置并启动kubernetes
在kubernetes master上需要运行以下组件:Kubelet、Kubernets Proxy
[root@k8s-node01 ~]# cp /etc/kubernetes/config /etc/kubernetes/config.bak
[root@k8s-node01 ~]# >/etc/kubernetes/config
[root@k8s-node01 ~]# vim /etc/kubernetes/config
###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
#   kube-apiserver.service
#   kube-controller-manager.service
#   kube-scheduler.service
#   kubelet.service
#   kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"
   
# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"
   
# Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=false"
   
# How the controller-manager, scheduler, and proxy find the apiserver
KUBE_MASTER="--master=http://172.16.60.220:8080"
  
[root@k8s-node01 ~]# cp /etc/kubernetes/kubelet /etc/kubernetes/kubelet.bak
[root@k8s-node01 ~]# >/etc/kubernetes/kubelet
[root@k8s-node01 ~]# vim /etc/kubernetes/kubelet
###
# kubernetes kubelet (minion) config
   
# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=0.0.0.0"
   
# The port for the info server to serve on
# KUBELET_PORT="--port=10250"
   
# You may leave this blank to use the actual hostname
#特别注意这个,在另一个节点上,要改为它的主机名
KUBELET_HOSTNAME="--hostname-override=k8s-node02"                
   
# location of the api-server
KUBELET_API_SERVER="--api-servers=http://172.16.60.220:8080"
   
# pod infrastructure container
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest"
   
# Add your own!
KUBELET_ARGS=""
  
启动服务并设置开机自启动
[root@k8s-node01 ~]# systemctl enable kubelet.service
[root@k8s-node01 ~]# systemctl start kubelet.service
[root@k8s-node01 ~]# systemctl enable kube-proxy.service
[root@k8s-node01 ~]# systemctl start kube-proxy.service

4) 查看k8s集群状态 (在master节点上)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
url中使用主机名和ip地址都可以
[root@k8s-master01 ~]# kubectl -s http://172.16.60.220:8080 get node           
NAME         STATUS    AGE
k8s-node01   Ready     2m
k8s-node02   Ready     1m
 
[root@k8s-master01 ~]# kubectl -s http://k8s-master01:8080 get node
NAME         STATUS    AGE
k8s-node01   Ready     2m
k8s-node02   Ready     2m
 
[root@k8s-master01 ~]# kubectl get nodes
NAME         STATUS    AGE
k8s-node01   Ready     2m
k8s-node02   Ready     2m
 
到这里, 一个简单的K8s集群环境就搭建完成了.

5) 创建覆盖网络( Flannel) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
1)安装Flannel(在master、node节点上同样操作)
[root@k8s-master01 ~]# yum install -y flannel
 
[root@k8s-master01 ~]# flanneld --version
0.7.1
 
2)配置Flannel(在master、node节点上同样操作)
[root@k8s-master01 ~]# cp /etc/sysconfig/flanneld /etc/sysconfig/flanneld.bak
[root@k8s-master01 ~]# >/etc/sysconfig/flanneld
[root@k8s-master01 ~]# vim /etc/sysconfig/flanneld
# Flanneld configuration options
   
# etcd url location.  Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://172.16.60.220:2379"
   
# etcd config key.  This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/atomic.io/network"
   
# Any additional options that you want to pass
#FLANNEL_OPTIONS=""
 
3)配置etcd中关于flannel的key(只在master节点上操作)
Flannel使用Etcd进行配置,来保证多个Flannel实例之间的配置一致性,所以需要在etcd上进行如下配置.
 
注意:
'/atomic.io/network/config'这个key与上文/etc/sysconfig/flannel中的配置项FLANNEL_ETCD_PREFIX是相对应的,错误的话启动就会出错!
flannel设置的ip网段可以任意设定,随便设定一个网段都可以 (但这个ip网段最好别和节点在同一个网段, 否则Flannel服务启动后, 可能造成节点登录失败的情况)
 
容器的ip就是根据这个网段进行自动分配的,ip分配后,容器一般是可以对外联网的(网桥模式,只要宿主机能上网就可以)
 
[root@k8s-master01 ~]# etcdctl mk /atomic.io/network/config '{ "Network": "192.168.0.0/16" }'
"Network""192.168.0.0/16" }
 
[root@k8s-master01 ~]# etcdctl get /atomic.io/network/config
"Network""192.168.0.0/16" }
 
===========================================================
删除上面etcd中指定的key
[root@k8s-master01 ~]# etcdctl rm /atomic.io/network/config
PrevNode.Value: { "Network""192.168.0.0/16" }
===========================================================
 
4)启动Flannel
启动Flannel之后,需要依次重启docker、kubernete。
启动Flannel后,一定要记得重启docker,这样Flannel配置分配的ip才能生效,即docker0虚拟网卡的ip会变成上面flannel设定的ip段
 
master节点启动Flannel
[root@k8s-master01 ~]# systemctl enable flanneld.service
[root@k8s-master01 ~]# systemctl start flanneld.service       
[root@k8s-master01 ~]# service docker restart                                      
[root@k8s-master01 ~]# docker start my_registry                         # 因为本节点也作为registry私仓节点 (生产环境下, 私仓容器可以部署到其他机器上)
[root@k8s-master01 ~]# systemctl restart kube-apiserver.service
[root@k8s-master01 ~]# systemctl restart kube-controller-manager.service
[root@k8s-master01 ~]# systemctl restart kube-scheduler.service
 
在两个node节点上启动Flannel
[root@k8s-node01 ~]# systemctl enable flanneld.service
[root@k8s-node01 ~]# systemctl start flanneld.service    
[root@k8s-node01 ~]# service docker restart
[root@k8s-node01 ~]# systemctl restart kubelet.service
[root@k8s-node01 ~]# systemctl restart kube-proxy.service
 
三个节点启动Flannel服务后, 查看各自ip地址, 可以看到Flannel地址已经出来了. 如下三个节点的flannel的ip是可以相互ping通的!
 
master节点地址查看
[root@k8s-master01 ~]# ifconfig
................
ens192: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.60.220  netmask 255.255.255.0  broadcast 172.16.60.255
...............
 
flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472
        inet 192.168.80.0  netmask 255.255.0.0  destination 192.168.80.0
...............
 
node01节点地址查看
[root@k8s-node01 ~]# ifconfig
..............
ens192: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.60.221  netmask 255.255.255.0  broadcast 172.16.60.255
.............
flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472
        inet 192.168.61.0  netmask 255.255.0.0  destination 192.168.61.0
.............
 
node02节点地址查看
[root@k8s-node02 ~]# ifconfig
.............
ens192: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.60.222  netmask 255.255.255.0  broadcast 172.16.60.255
............
flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1472
        inet 192.168.100.0  netmask 255.255.0.0  destination 192.168.100.0
............

6)部署nginx pod 和复制器  (在master节点上操作)
以下面的图来安装一个简单的静态内容的nginx应用:

用复制器启动一个2个备份的nginx Pod,然后在前面挂Service,一个service只能被集群内部访问,一个能被集群外的节点访问.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
1) 部署nginx pod 和复制器
[root@k8s-master01 ~]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
172.16.60.220:5000/centos   latest              9f38484d220f        17 hours ago        202 MB
docker.io/registry          latest              f32a97de94e1        7 days ago          25.8 MB
172.16.60.220:5000/tomcat   latest              dd6ff929584a        10 days ago         463 MB
172.16.60.220:5000/nginx    latest              881bd08c0b08        10 days ago         109 MB
 
通过下面命令发现apiVersion版本是v1
[root@k8s-master01 ~]# curl -s -L http://172.16.60.220:8080/api/v1beta1/version | python -mjson.tool            
{
    "apiVersion""v1",
    "code": 404,
    "details": {},
    "kind""Status",
    "message""the server could not find the requested resource",
    "metadata": {},
    "reason""NotFound",
    "status""Failure"
}
 
开始创建pod单元 (replicas: 2 表示2个备份)
[root@k8s-master01 ~]# mkdir -p /home/kubermange && cd /home/kubermange
[root@k8s-master01 kubermange]# vim nginx-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-controller
spec:
  replicas: 2                             
  selector:
    name: nginx
  template:
    metadata:
      labels:
        name: nginx
    spec:
      containers:
        - name: nginx
          image: 172.16.60.220:5000/nginx
          ports:
            - containerPort: 80
 
执行创建命令
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 create -f nginx-rc.yaml            
replicationcontroller "nginx-controller" created
 
注意:
由于kubernetes要在node机器上去gcr.io下载gcr.io/google_containers/pause镜像,然后下载nginx镜像;
pod配置文件yaml中的images镜像可以提前在node节点机器里下载好, 这样创建pod时就会快点;
如果不提前在node节点机器里下载好, 创建pod时就会去下载, 这样会耗费点时间;
 
查看pods清单 (状态为run才是正常的, 如果为pulling或者containercreating都是不正常的)
[root@k8s-master01 kubermange]# kubectl get pods 
NAME                     READY     STATUS    RESTARTS   AGE
nginx-controller-d97wj   1/1       ContainerCreating   0          15s
nginx-controller-lf11n   1/1       ContainerCreating   0          15s
 
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-controller-d97wj   1/1       ContainerCreating   0          39s
nginx-controller-lf11n   1/1       ContainerCreating   0          39s
 
可以使用describe 命令查看pod所分到的节点 (注意后面pod的name, 可以在pods清单查询结果中找到)
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 describe pod nginx-controller-d97wj
Name:           nginx-controller-d97wj
Namespace:      default
Node:           k8s-node01/172.16.60.222
Start Time:     Fri, 15 Mar 2019 11:54:38 +0800
Labels:         name=nginx
Status:         Pending
IP:
Controllers:    ReplicationController/nginx-controller
Containers:
  nginx:
    Container ID:
    Image:                      172.16.60.220:5000/nginx
    Image ID:
    Port:                       80/TCP
    State:                      Waiting
      Reason:                   ContainerCreating
    Ready:                      False
    Restart Count:              0
    Volume Mounts:              <none>
    Environment Variables:      <none>
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
  ---------     --------        -----   ----                    -------------   --------        ------          -------
  2h            58s             39      {kubelet k8s-node01}                    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)"
 
  2h    5s      774     {kubelet k8s-node01}            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\""
 
如上发现了两个报错:
一. 通过yaml创建了pod,查看pods清单的时候("kubectl get pods")一直是ContainerCreating状态。
二. 使用describe 命令查看pod所分到的节点时, 发现如下报错:
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)"
 
Error syncing pod, skipping: failed to "StartContainer" for "POD" with ImagePullBackOff: "Back-off pulling image
\"registry.access.redhat.com/rhel7/pod-infrastructure:latest\""
 
发现docker从registry.access.redhat.com/rhel7/pod-infrastructure:latest  拉去镜像报错!并提示:
details: (open /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt: no such file or directory)"
 
这是因为kubernetes在两个node节点上会自动去gcr.io下载gcr.io/google_containers/pause镜像失败, 可以手动执行下下载
[root@k8s-node01 ~]# docker pull registry.access.redhat.com/rhel7/pod-infrastructure:latest
Trying to pull repository registry.access.redhat.com/rhel7/pod-infrastructure ...
open /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt: no such file or directory
 
解决办法:
在pod所在的两个node节点上, 使用ls查看改文件是个软连接,链接目标是/etc/rhsm/ca/redhat-uep.pem不存在, 这是因为没有rhsm
[root@k8s-node01 ~]# ll /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt
lrwxrwxrwx 1 root root 27 Mar 14 09:16 /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt -> /etc/rhsm/ca/redhat-uep.pem
 
[root@k8s-node01 ~]# ll /etc/rhsm/ca
[root@k8s-node01 ~]# ll /etc/rhsm/ca/redhat-uep.pem
ls: cannot access /etc/rhsm/ca/redhat-uep.pem: No such file or directory
 
在两个node节点上安装rhsm . 记住: 一定要在pod所在的node节点上安装rhsm!!!!!
[root@k8s-node01 ~]# yum install *rhsm*
 
如果在两个node节点安装rhsm后, 还是不行!
[root@k8s-master01 kubermange]# kubectl get pods
NAME                     READY     STATUS              RESTARTS   AGE
nginx-controller-d97wj   0/1       ContainerCreating   0          3h
nginx-controller-lf11n   0/1       ContainerCreating   0          3h
 
这是因为gcr.io被墙了,可以在pod所在的两个node节点上kubelet配置文件/etc/kubernetes/kubelet里加上一个参数:
--pod-infra-container-image来指定一个国内的镜像。 做法如下:
[root@k8s-node01 ~]# cp /etc/kubernetes/kubelet /etc/kubernetes/kubelet.bak2
[root@k8s-node01 ~]# vim /etc/kubernetes/kubelet
...................
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.aliyuncs.com/archon/pause-amd64:3.0"
 
[root@k8s-node01 ~]# diff /etc/kubernetes/kubelet /etc/kubernetes/kubelet.bak2 
18c18
< KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.aliyuncs.com/archon/pause-amd64:3.0"
---
> KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest"
 
接着重启kubelet.service
[root@k8s-node01 ~]# systemctl restart kubelet.service
 
注意:上面操作需要再pod所在的node上进行!
 
然后在master节点上, 查看pods清单,  注意观察, 过一会儿后, 发现pods状态就会由"ContainerCreating" 变为 "Running"
[root@k8s-master01 kubermange]# kubectl get pods 
NAME                     READY     STATUS    RESTARTS   AGE
nginx-controller-d97wj   1/1       Running   0          2m
nginx-controller-lf11n   1/1       Running   0          2m
 
最后再使用describe 命令查看pod所分到的节点
 
先查看第一个pods所在的node节点情况
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 describe pod nginx-controller-d97wj
Name:           nginx-controller-d97wj
Namespace:      default
Node:           k8s-node02/172.16.60.222
Start Time:     Fri, 15 Mar 2019 22:40:18 +0800
Labels:         name=nginx
Status:         Running
IP:             192.168.100.2
Controllers:    ReplicationController/nginx-controller
Containers:
  nginx:
    Container ID:               docker://8ae4502b4e62120322de98aa532e653d3d2e058ffbb0b842e0f265621bebbe61
    Image:                      172.16.60.220:5000/nginx
    Image ID:                   docker-pullable://172.16.60.220:5000/nginx@sha256:7734a210432278817f8097acf2f72d20e2ccc7402a0509810c44b3a8bfe0094a
    Port:                       80/TCP
    State:                      Running
      Started:                  Fri, 15 Mar 2019 22:40:19 +0800
    Ready:                      True
    Restart Count:              0
    Volume Mounts:              <none>
    Environment Variables:      <none>
Conditions:
  Type          Status
  Initialized   True
  Ready         True
  PodScheduled  True
No volumes.
QoS Class:      BestEffort
Tolerations:    <none>
Events:
  FirstSeen     LastSeen        Count   From                    SubObjectPath           Type            Reason                  Message
  ---------     --------        -----   ----                    -------------           --------        ------                  -------
  1m            1m              1       {default-scheduler }                            Normal          Scheduled               Successfully assigned nginx-controller-d97wj to k8s-node02
  1m            1m              2       {kubelet k8s-node02}                            Warning         MissingClusterDNS       kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy.
  1m            1m              1       {kubelet k8s-node02}    spec.containers{nginx}  Normal          Pulling                 pulling image "172.16.60.220:5000/nginx"
  1m            1m              1       {kubelet k8s-node02}    spec.containers{nginx}  Normal          Pulled                  Successfully pulled image "172.16.60.220:5000/nginx"
  1m            1m              1       {kubelet k8s-node02}    spec.containers{nginx}  Normal          Created                 Created container with docker id 8ae4502b4e62; Security:[seccomp=unconfined]
  1m            1m              1       {kubelet k8s-node02}    spec.containers{nginx}  Normal          Started                 Started container with docker id 8ae4502b4e62
 
此时, 上面信息中的warning和error可以忽略, 后面三行出现了"Successfully, Created, Started"
查看到的pods清单中的状态是"Running"就可以了
 
再查看另一个pod所在node节点情况
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 describe pod nginx-controller-lf11n
Name:           nginx-controller-lf11n
Namespace:      default
Node:           k8s-node01/172.16.60.221
Start Time:     Fri, 15 Mar 2019 22:40:18 +0800
Labels:         name=nginx
Status:         Running
IP:             192.168.61.2
Controllers:    ReplicationController/nginx-controller
Containers:
  nginx:
    Container ID:               docker://649b228b6b541a6124d98480193541782f28fd238b1ecb6c4b8812bb369d4b79
    Image:                      172.16.60.220:5000/nginx
    Image ID:                   docker-pullable://172.16.60.220:5000/nginx@sha256:7734a210432278817f8097acf2f72d20e2ccc7402a0509810c44b3a8bfe0094a
    Port:                       80/TCP
    State:                      Running
      Started:                  Fri, 15 Mar 2019 22:40:19 +0800
    Ready:                      True
    Restart Count:              0
    Volume Mounts:              <none>
    Environment Variables:      <none>
Conditions:
  Type          Status
  Initialized   True
  Ready         True
  PodScheduled  True
No volumes.
QoS Class:      BestEffort
Tolerations:    <none>
Events:
  FirstSeen     LastSeen        Count   From                    SubObjectPath           Type            Reason                  Message
  ---------     --------        -----   ----                    -------------           --------        ------                  -------
  4m            4m              1       {default-scheduler }                            Normal          Scheduled               Successfully assigned nginx-controller-lf11n to k8s-node01
  4m            4m              2       {kubelet k8s-node01}                            Warning         MissingClusterDNS       kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. Falling back to DNSDefault policy.
  4m            4m              1       {kubelet k8s-node01}    spec.containers{nginx}  Normal          Pulling                 pulling image "172.16.60.220:5000/nginx"
  4m            4m              1       {kubelet k8s-node01}    spec.containers{nginx}  Normal          Pulled                  Successfully pulled image "172.16.60.220:5000/nginx"
  4m            4m              1       {kubelet k8s-node01}    spec.containers{nginx}  Normal          Created                 Created container with docker id 649b228b6b54; Security:[seccomp=unconfined]
  4m            4m              1       {kubelet k8s-node01}    spec.containers{nginx}  Normal          Started                 Started container with docker id 649b228b6b54
 
通过上面可以看到, 这个复制器启动了两个Pod,分别运行在k8s-node01和k8s-node02这两个节点上了。
到这两个节点上查看,发现已经有nginx应用容器创建了。
 
k8s-node01节点上查看
[root@k8s-node01 ~]# docker ps
CONTAINER ID        IMAGE                                          COMMAND                  CREATED             STATUS              PORTS               NAMES
649b228b6b54        172.16.60.220:5000/nginx                       "nginx -g 'daemon ..."   5 minutes ago       Up 5 minutes                            k8s_nginx.fc4d011b_nginx-controller-lf11n_default_411e0ff1-4730-11e9-9580-005056881e0f_80f4d28a
3918206c4d03        registry.aliyuncs.com/archon/pause-amd64:3.0   "/pause"                 5 minutes ago       Up 5 minutes                            k8s_POD.4d6004a4_nginx-controller-lf11n_default_411e0ff1-4730-11e9-9580-005056881e0f_6a6c4e65
 
k8s-node02节点上查看
[root@k8s-node02 ~]# docker ps
CONTAINER ID        IMAGE                                          COMMAND                  CREATED             STATUS              PORTS               NAMES
8ae4502b4e62        172.16.60.220:5000/nginx                       "nginx -g 'daemon ..."   6 minutes ago       Up 6 minutes                            k8s_nginx.fc4d011b_nginx-controller-d97wj_default_411e034e-4730-11e9-9580-005056881e0f_a30d6956
d8662e6fc177        registry.aliyuncs.com/archon/pause-amd64:3.0   "/pause"                 6 minutes ago       Up 6 minutes                            k8s_POD.4d6004a4_nginx-controller-d97wj_default_411e034e-4730-11e9-9580-005056881e0f_245b70ac
 
=========================================================================
温馨提示:
如果需要重建pod, 则只需要执行下面删除yaml文件命令 (执行后, 对应的pods就会被删除, 但yaml配置文件还是存在的)
[root@k8s-master01 kubermange]# kubectl delete -f nginx-rc.yaml
replicationcontroller "nginx-controller" deleted
[root@k8s-master01 kubermange]# kubectl get pods
No resources found.
[root@k8s-master01 kubermange]# ls
nginx-rc.yaml
 
如果不删除对应的yaml文件, 仅仅删除pods是不行的, 因为删除之后还会自动创建
[root@k8s-master01 kubermange]# kubectl get pods
NAME                     READY     STATUS              RESTARTS   AGE
nginx-controller-3jr6p   0/1       Running   0          1m
nginx-controller-d3qlg   0/1       Running   0          1m
[root@k8s-master01 kubermange]# kubectl delete po nginx-controller-3jr6p
pod "nginx-controller-3jr6p" deleted
[root@k8s-master01 kubermange]# kubectl delete po nginx-controller-d3qlg
pod "nginx-controller-d3qlg" deleted
[root@k8s-master01 kubermange]# kubectl get pods
NAME                     READY     STATUS              RESTARTS   AGE
nginx-controller-3jr6p   0/1       Running   0          1m
nginx-controller-d3qlg   0/1       Running   0          1m
=======================================================================
 
2) 部署节点内部可访问的nginx service
 
Service的type有ClusterIP和NodePort之分,缺省是ClusterIP,这种类型的Service只能在集群内部访问。
配置文件如下:
[root@k8s-master01 kubermange]# vim nginx-service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service-clusterip
spec:
  ports:
    - port: 8001
      targetPort: 80
      protocol: TCP
  selector:
    name: nginx
 
然后执行下面的命令创建service:
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 create -f ./nginx-service-clusterip.yaml            
service "nginx-service-clusterip" created
 
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 get service
NAME                      CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes                172.16.0.1      <none>        443/TCP    1d
nginx-service-clusterip   172.16.77.193   <none>        8001/TCP   8s
 
接下来可以进行验证service的可访问性(访问节点):
上面的输出告诉我们这个Service的Cluster IP是172.16.77.193,端口是8001。
 
那么现在可以到pod所在的node节点上进行验证这个PortalNet IP的工作情况.
先到ks8-node01节点上进行验证:
[root@k8s-node01 ~]# curl -s 172.16.77.193:8001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
 
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
 
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
 
然后到node02节点上验证
[root@k8s-node02 ~]# curl -s 172.16.77.193:8001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
 
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
 
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
 
由此可见,从前面部署复制器的部分可以知道nginx Pod运行在k8s-node01和k8s-node02这两个节点上。
从这两个节点上访问我们的服务来体现Service Cluster IP在所有集群节点的可到达性。
 
接着可以创建NodePort类型的Service,这种类型的Service在集群外部是可以访问。下面是本案例用的配置文件:
[root@k8s-master01 kubermange]# pwd
/home/kubermange
[root@k8s-master01 kubermange]# vim nginx-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service-nodeport
spec:
  ports:
    - port: 8000
      targetPort: 80
      protocol: TCP
  type: NodePort
  selector:
    name: nginx
 
执行下面的命令创建service:
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 create -f ./nginx-service-nodeport.yaml            
service "nginx-service-nodeport" created
 
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 get service 
NAME                      CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes                172.16.0.1      <none>        443/TCP          1d
nginx-service-clusterip   172.16.77.193   <none>        8001/TCP         11m
nginx-service-nodeport    172.16.234.94   <nodes>       8000:32172/TCP   7s
 
使用下面的命令获得这个service的节点级别的端口:
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 describe service nginx-service-nodeport 2>/dev/null | grep NodePort
Type:                   NodePort
NodePort:               <unset> 32172/TCP
 
验证nginx的service的可访问性(在master节点访问两个node节点的32172端口)
访问第一个node节点地址
[root@k8s-master01 kubermange]# curl 172.16.60.221:32172
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
 
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
 
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
 
访问第二个node节点地址
[root@k8s-master01 kubermange]# curl 172.16.60.222:32172
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
 
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
 
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
 
可以在浏览器里分别输入: http://172.16.60.221:32172 或者 http://172.16.60.222:32172 都可以访问nginx!

                                                                                其他补充说明                          

->  基于上面的实验环境,可以扩容nginx应用容器,依次添加对应的应用容器的pod、service-clusterip、service-nodeport的yaml文件即可。注意yaml文件中的name名。
->  也可以添加其他应用容器,比如tomcat,也是依次创建pod、service-clusterip、service-nodeport的yaml文件。注意yaml文件中的name名和port端口不要重复!
->  后面应用容器的集群环境完成后(外部可访问的端口是固定的),可以考虑做下master控制机的集群环境(即做etcd集群)。可以在控制节点做负载均衡,还可以通过keepalived做高可用。

======================================================   ==========
下面贴出一个基于tomcat的pods应用容器的3个yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
[root@k8s-master01 kubermange]# pwd
/home/kubermange
 
1) 开始创建pod单元 (replicas: 2 表示2个备份)
[root@k8s-master01 kubermange]# vim tomcat-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: tomcat-controller
spec:
  replicas: 2                             
  selector:
    name: tomcat
  template:
    metadata:
      labels:
        name: tomcat
    spec:
      containers:
        - name: tomcat
          image: 172.16.60.220:5000/tomcat
          ports:
            - containerPort: 8080
 
执行创建命令
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 create -f tomcat-rc.yaml
replicationcontroller "tomcat-controller" created
 
================================================================================
如果上面命令在创建过程出现报错:
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 create -f tomcat-rc.yaml
error: error validating "tomcat-rc.yaml": error validating data: expected type int, for field spec.template.spec.containers[0].ports[0].containerPort, got string; if you choose to ignore these errors, turn validation off with --validate=false
 
如果遇到上面的问题, 都是因为格式问题造成的!!!!!  因为kubernetes复制时,空格或制表符都是很严格的!!!!!
 
解决办法: 因为之前的nginx-rc.yaml配置格式正确, 可以复制它的内容到tomcat-rc.yaml, 然后修改内容
[root@k8s-master01 kubermange]# sed -i 's/nginx/tomcat/g' tomcat-rc.yaml
[root@k8s-master01 kubermange]# sed -i 's/80/8080/g' tomcat-rc.yaml
================================================================================
 
查看pods清单
[root@k8s-master01 kubermange]# kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
nginx-controller-d97wj    1/1       Running   0          1h
nginx-controller-lf11n    1/1       Running   0          1h
tomcat-controller-35kzb   1/1       Running   0          2m
tomcat-controller-lsph4   1/1       Running   0          2m
 
查看两个node节点, 发现tomcat的两个pods已经分布到两个node节点上了
[root@k8s-node01 ~]# docker ps
CONTAINER ID        IMAGE                                          COMMAND                  CREATED             STATUS              PORTS               NAMES
d71e6a1cbd27        172.16.60.220:5000/tomcat                      "catalina.sh run"        3 minutes ago       Up 3 minutes                            k8s_tomcat.904d024b_tomcat-controller-35kzb_default_7f5801aa-4738-11e9-9580-005056881e0f_74533613
53ca4f24afa7        registry.aliyuncs.com/archon/pause-amd64:3.0   "/pause"                 3 minutes ago       Up 3 minutes                            k8s_POD.bcb5050c_tomcat-controller-35kzb_default_7f5801aa-4738-11e9-9580-005056881e0f_351ec586
649b228b6b54        172.16.60.220:5000/nginx                       "nginx -g 'daemon ..."   About an hour ago   Up About an hour                        k8s_nginx.fc4d011b_nginx-controller-lf11n_default_411e0ff1-4730-11e9-9580-005056881e0f_80f4d28a
3918206c4d03        registry.aliyuncs.com/archon/pause-amd64:3.0   "/pause"                 About an hour ago   Up About an hour                        k8s_POD.4d6004a4_nginx-controller-lf11n_default_411e0ff1-4730-11e9-9580-005056881e0f_6a6c4e65
 
[root@k8s-node02 ~]# docker ps
CONTAINER ID        IMAGE                                          COMMAND                  CREATED             STATUS              PORTS               NAMES
366a61005aa3        172.16.60.220:5000/tomcat                      "catalina.sh run"        3 minutes ago       Up 3 minutes                            k8s_tomcat.904d024b_tomcat-controller-lsph4_default_7f57f713-4738-11e9-9580-005056881e0f_d54a2615
64960d85f490        registry.aliyuncs.com/archon/pause-amd64:3.0   "/pause"                 3 minutes ago       Up 3 minutes                            k8s_POD.bcb5050c_tomcat-controller-lsph4_default_7f57f713-4738-11e9-9580-005056881e0f_cb166baa
8ae4502b4e62        172.16.60.220:5000/nginx                       "nginx -g 'daemon ..."   About an hour ago   Up About an hour                        k8s_nginx.fc4d011b_nginx-controller-d97wj_default_411e034e-4730-11e9-9580-005056881e0f_a30d6956
d8662e6fc177        registry.aliyuncs.com/archon/pause-amd64:3.0   "/pause"                 About an hour ago   Up About an hour                        k8s_POD.4d6004a4_nginx-controller-d97wj_default_411e034e-4730-11e9-9580-005056881e0f_245b70ac
 
2) 创建ClusterIP类型的Service
[root@k8s-master01 kubermange]# vim tomcat-service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: tomcat-service-clusterip
spec:
  ports:
    - port: 8801
      targetPort: 8080
      protocol: TCP
  selector:
    name: tomcat
 
创建service:
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 create -f tomcat-service-clusterip.yaml
service "tomcat-service-clusterip" created
 
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 get service
NAME                       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes                 172.16.0.1       <none>        443/TCP          1d
nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         56m
nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   44m
tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         11s
 
可知, tomcat的service的clusterip是172.16.144.116, 端口是8801
分别到node01和node02节点上访问clusterip, 就可以访问到tomcat容器了
[root@k8s-node01 ~]# curl -s 172.16.144.116:8801
[root@k8s-node02 ~]# curl -s 172.16.144.116:8801
 
3) 创建NodePort类型的Service,这种类型的Service在集群外部是可以访问
[root@k8s-master01 kubermange]# vim tomcat-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: tomcat-service-nodeport
spec:
  ports:
    - port: 8880
      targetPort: 8080
      protocol: TCP
  type: NodePort
  selector:
    name: tomcat
 
创建service
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 create -f tomcat-service-nodeport.yaml
service "tomcat-service-nodeport" created
 
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 get service
NAME                       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes                 172.16.0.1       <none>        443/TCP          1d
nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         59m
nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   48m
tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         3m
tomcat-service-nodeport    172.16.183.234   <nodes>       8880:31960/TCP   23s
 
[root@k8s-master01 kubermange]# kubectl -s http://172.16.60.220:8080 describe service tomcat-service-nodeport 2>/dev/null | grep NodePort
Type:                   NodePort
NodePort:               <unset> 31960/TCP
 
验证tomcat的service的可访问性(在master节点访问两个node节点的31960端口)
[root@k8s-master01 kubermange]# curl 172.16.60.221:31960
[root@k8s-master01 kubermange]# curl 172.16.60.222:31960
 
可以在浏览器里分别输入: http://172.16.60.221:31960 或者 http://172.16.60.222:31960 都可以访问tomcat!

9) K8s常用运维命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
一. 查看集群信息
[root@k8s-master01 ~]# kubectl cluster-info
[root@k8s-master01 ~]# kubectl cluster-info dump
  
二. 查看各组件状态
[root@k8s-master01 ~]# kubectl -s http://localhost:8080 get componentstatuses
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok                
scheduler            Healthy   ok                
etcd-0               Healthy   {"health":"true"
  
或者
[root@k8s-master01 ~]# kubectl -s http://172.16.60.220:8080 get componentstatuses
NAME                 STATUS    MESSAGE             ERROR
scheduler            Healthy   ok                
controller-manager   Healthy   ok                
etcd-0               Healthy   {"health":"true"}
  
三. GET信息
  
1) 查看节点 (k8s-master01 对应的是 172.16.60.220的主机名)
[root@k8s-master01 ~]# kubectl get node                                #将命令中的node变为nodes也是可以的
NAME         STATUS    AGE
k8s-node01   Ready     1d
k8s-node02   Ready     1d
  
[root@k8s-master01 ~]# kubectl -s http://k8s-master01:8080 get node    #将命令中的node变为nodes也是可以的
NAME         STATUS    AGE
k8s-node01   Ready     1d
k8s-node02   Ready     1d
  
2) 查看pods清单
[root@k8s-master01 ~]# kubectl get pod                            #将命令中的pod变为pods也是可以的                 
NAME                      READY     STATUS    RESTARTS   AGE
nginx-controller-d97wj    1/1       Running   0          1h
nginx-controller-lf11n    1/1       Running   0          1h
tomcat-controller-35kzb   1/1       Running   0          18m
tomcat-controller-lsph4   1/1       Running   0          18m
  
[root@k8s-master01 ~]# kubectl -s http://k8s-master01:8080 get pod          #将命令中的pod变为pods也是可以的 
NAME                      READY     STATUS    RESTARTS   AGE
nginx-controller-d97wj    1/1       Running   0          1h
nginx-controller-lf11n    1/1       Running   0          1h
tomcat-controller-35kzb   1/1       Running   0          18m
tomcat-controller-lsph4   1/1       Running   0          18m
  
3) 查看service清单
[root@k8s-master01 ~]# kubectl get service                                             #将命令中的service变为services也是可以的
NAME                       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes                 172.16.0.1       <none>        443/TCP          1d
nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         1h
nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   59m
tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         14m
tomcat-service-nodeport    172.16.183.234   <nodes>       8880:31960/TCP   11m
  
[root@k8s-master01 ~]# kubectl -s http://172.16.60.220:8080 get service               #将命令中的service变为services也是可以的
NAME                       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes                 172.16.0.1       <none>        443/TCP          1d
nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         1h
nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   1h
tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         17m
tomcat-service-nodeport    172.16.183.234   <nodes>       8880:31960/TCP   14m
  
或者  (后面的sed表示 打印奇数行)
[root@k8s-master01 ~]# kubectl get services -o json|grep '"name":'|sed -n '1~2p'
                "name""kubernetes",
                "name""nginx-service-clusterip",
                "name""nginx-service-nodeport",
                "name""tomcat-service-clusterip",
                "name""tomcat-service-nodeport",
  
4) 查看replicationControllers清单 (同理可以将命令中的replicationControllers变为replicationController也是可以的)
[root@k8s-master01 ~]# kubectl get replicationControllers
NAME                DESIRED   CURRENT   READY     AGE
nginx-controller    2         2         2         2h
tomcat-controller   2         2         2         1h
  
[root@k8s-master01 ~]# kubectl -s http://172.16.60.220:8080 get replicationControllers
NAME                DESIRED   CURRENT   READY     AGE
nginx-controller    2         2         2         2h
tomcat-controller   2         2         2         1h
  
5) 查看rc和namespace
[root@k8s-master01 ~]# kubectl get rc,namespace
NAME                   DESIRED   CURRENT   READY     AGE
rc/nginx-controller    2         2         2         2h
rc/tomcat-controller   2         2         2         1h
  
NAME             STATUS    AGE
ns/default       Active    1d
ns/kube-system   Active    1d
  
6) 查看pod和svc(和service一样)
[root@k8s-master01 ~]# kubectl get pods,svc
NAME                         READY     STATUS    RESTARTS   AGE
po/nginx-controller-d97wj    1/1       Running   0          2h
po/nginx-controller-lf11n    1/1       Running   0          2h
po/tomcat-controller-35kzb   1/1       Running   0          1h
po/tomcat-controller-lsph4   1/1       Running   0          1h
  
NAME                           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
svc/kubernetes                 172.16.0.1       <none>        443/TCP          1d
svc/nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         2h
svc/nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   2h
svc/tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         1h
svc/tomcat-service-nodeport    172.16.183.234   <nodes>       8880:31960/TCP   1h
  
7) 以jison格式输出pod的详细信息.
[root@k8s-master01 ~]# kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
nginx-controller-d97wj    1/1       Running   0          2h
nginx-controller-lf11n    1/1       Running   0          2h
tomcat-controller-35kzb   1/1       Running   0          1h
tomcat-controller-lsph4   1/1       Running   0          1h
  
注意下面命令中的pods的名称可以通过上面命令查看
[root@k8s-master01 ~]# kubectl get po nginx-controller-d97wj -o json
{
    "apiVersion""v1",
    "kind""Pod",
    "metadata": {
        "annotations": {
...................
...................
        "hostIP""172.16.60.222",
        "phase""Running",
        "podIP""192.168.100.2",
        "startTime""2019-03-15T14:40:18Z"
    }
}
  
还可以输出其它格式和方法(kubectl get -h查看帮助)
[root@k8s-master01 ~]# kubectl get -h
  
8) 查看指定pod跑在哪个node上
[root@k8s-master01 ~]# kubectl get po nginx-controller-d97wj -o wide   
NAME                     READY     STATUS    RESTARTS   AGE       IP              NODE
nginx-controller-d97wj   1/1       Running   0          2h        192.168.100.2   k8s-node02
  
9) 获取指定json或ymal格式的KEY数据,custom-columns=XXXXX(自定义列名):.status.hostIP(以“点开始”,然后写路径就可以)
注意: 下面命令中的nginx-controller-d97wj是pod单元名称 (kubectl get pods 可以查看pods)
[root@k8s-master01 ~]# kubectl get po nginx-controller-d97wj -o custom-columns=HOST-IP:.status.hostIP,POD-IP:.status.podIP   
HOST-IP         POD-IP
172.16.60.222   192.168.100.2
  
10) describe方法
describe类似于get,同样用于获取resource的相关信息。不同的是,get获得的是更详细的resource个性的详细信息,describe获得的是resource集群相关的信息。
describe命令同get类似,但是describe不支持-o选项,对于同一类型resource,describe输出的信息格式,内容域相同。
   
需要注意:  如果发现是查询某个resource的信息,使用get命令能够获取更加详尽的信息。但是如果想要查询某个resource的状态,如某个pod并不是在running状态,
这时需要获取更详尽的状态信息时,就应该使用describe命令。
  
[root@k8s-master01 ~]# kubectl describe po nginx-controller-d97wj
Name:           nginx-controller-d97wj
Namespace:      default
Node:           k8s-node02/172.16.60.222
Start Time:     Fri, 15 Mar 2019 22:40:18 +0800
Labels:         name=nginx
Status:         Running
IP:             192.168.100.2
Controllers:    ReplicationController/nginx-controller
Containers:
  nginx:
    Container ID:               docker://8ae4502b4e62120322de98aa532e653d3d2e058ffbb0b842e0f265621bebbe61
    Image:                      172.16.60.220:5000/nginx
    Image ID:                   docker-pullable://172.16.60.220:5000/nginx@sha256:7734a210432278817f8097acf2f72d20e2ccc7402a0509810c44b3a8bfe0094a
    Port:                       80/TCP
    State:                      Running
      Started:                  Fri, 15 Mar 2019 22:40:19 +0800
    Ready:                      True
    Restart Count:              0
    Volume Mounts:              <none>
    Environment Variables:      <none>
Conditions:
  Type          Status
  Initialized   True
  Ready         True
  PodScheduled  True
No volumes.
QoS Class:      BestEffort
Tolerations:    <none>
No events.
  
11) create创建
kubectl命令用于根据文件或输入创建集群resource。如果已经定义了相应resource的yaml或son文件,直接kubectl create -f filename即可创建文件内定义的
resource。也可以直接只用子命令[namespace/secret/configmap/serviceaccount]等直接创建相应的resource。从追踪和维护的角度出发,建议使用json或
yaml的方式定义资源。
   
命令格式:
# kubectl create -f 文件名
   
12) replace更新替换资源
replace命令用于对已有资源进行更新、替换。如前面create中创建的nginx,当我们需要更新resource的一些属性的时候,如果修改副本数量,增加、修改label,
更改image版本,修改端口等。都可以直接修改原yaml文件,然后执行replace命令。
   
需要注意: 名字不能被更更新。另外,如果是更新label,原有标签的pod将会与更新label后的rc断开联系,有新label的rc将会创建指定副本数的新的pod,但是默认
并不会删除原来的pod。所以此时如果使用get po将会发现pod数翻倍,进一步check会发现原来的pod已经不会被新rc控制,此处只介绍命令不详谈此问题,好奇者可自行实验。
   
命令格式:
# kubectl replace -f nginx-rc.yaml
   
13) patch
如果一个容器已经在运行,这时需要对一些容器属性进行修改,又不想删除容器,或不方便通过replace的方式进行更新。kubernetes还提供了一种在容器运行时,直接
对容器进行修改的方式,就是patch命令。 如创建pod的label是app=nginx-2,如果在运行过程中,需要把其label改为app=nginx-3。
这个patch命令如下:
[root@k8s-master01 ~]# kubectl patch pod nginx-controller-d97wj -p '{"metadata":{"labels":{"app":"nginx-3"}}}'
"nginx-controller-d97wj" patched
  
14) edit
edit提供了另一种更新resource源的操作,通过edit能够灵活的在一个common的resource基础上,发展出更过的significant resource。
例如,使用edit直接更新前面创建的pod的命令为:
# kubectl edit po nginx-controller-d97wj
   
上面命令的效果等效于:
# kubectl get po nginx-controller-d97wj -o yaml >> /tmp/nginx-tmp.yaml
# vim /tmp/nginx-tmp.yaml             // 这此文件里做一些修改
# kubectl replace -f /tmp/nginx-tmp.yaml
   
15) Delete
根据resource名或label删除resource。
# kubectl delete -f nginx-rc.yaml
# kubectl delete po nginx-controller-d97wj
# kubectl delete po nginx-controller-lf11n
   
16) apply
apply命令提供了比patch,edit等更严格的更新resource的方式。通过apply,用户可以将resource的configuration使用source control的方式维护在版本库中。
每次有更新时,将配置文件push到server,然后使用kubectl apply将更新应用到resource。kubernetes会在引用更新前将当前配置文件中的配置同已经应用的配置
做比较,并只更新更改的部分,而不会主动更改任何用户未指定的部分。
   
apply命令的使用方式同replace相同,不同的是,apply不会删除原有resource,然后创建新的。apply直接在原有resource的基础上进行更新。同时kubectl apply
还会resource中添加一条注释,标记当前的apply。类似于git操作。
   
17) logs
logs命令用于显示pod运行中,容器内程序输出到标准输出的内容。跟docker的logs命令类似。如果要获得tail -f 的方式,也可以使用-f选项。
# kubectl logs nginx-controller-d97wj
   
18) rolling-update
rolling-update是一个非常重要的命令,对于已经部署并且正在运行的业务,rolling-update提供了不中断业务的更新方式。rolling-update每次起一个新的pod,
等新pod完全起来后删除一个旧的pod,然后再起一个新的pod替换旧的pod,直到替换掉所有的pod。
   
rolling-update需要确保新的版本有不同的name,Version和label,否则会报错 。
# kubectl rolling-update nginx-controller -f nginx-rc.yaml
   
如果在升级过程中,发现有问题还可以中途停止update,并回滚到前面版本
# kubectl rolling-update nginx-controller --rollback
   
rolling-update还有很多其他选项提供丰富的功能,如--update-period指定间隔周期,使用时可以使用-h查看help信息.
   
19) scale  (注意下面的nginx-controller 是在nginx-rc.yaml文件中定义的name名称)
scale用于程序在负载加重或缩小时副本进行扩容或缩小,如前面创建的nginx有两个副本,可以轻松的使用scale命令对副本数进行扩展或缩小。
扩展副本数到4:
# kubectl scale rc nginx-controller --replicas=4
   
重新缩减副本数到2:
# kubectl scale rc nginx-controller --replicas=2
   
20) autoscale
scale虽然能够很方便的对副本数进行扩展或缩小,但是仍然需要人工介入,不能实时自动的根据系统负载对副本数进行扩、缩。autoscale命令提供了自动根据pod负载
对其副本进行扩缩的功能。
   
autoscale命令会给一个rc指定一个副本数的范围,在实际运行中根据pod中运行的程序的负载自动在指定的范围内对pod进行扩容或缩容。如前面创建的nginx,可以用
如下命令指定副本范围在1~4
# kubectl autoscale rc nginx-controller --min=1 --max=4
   
21) attach
attach命令类似于docker的attach命令,可以直接查看容器中以daemon形式运行的进程的输出,效果类似于logs -f,退出查看使用ctrl-c。如果一个pod中有多个容器,
要查看具体的某个容器的的输出,需要在pod名后使用-c containers name指定运行的容器。如下示例的命令为查看kube-system namespace中的kube-dns-v9-rcfuk pod
中的skydns容器的输出。
# kubectl attach kube-dns-v9-rcfuk -c skydns --namespace=kube-system
   
22) exec
exec命令同样类似于docker的exec命令,为在一个已经运行的容器中执行一条shell命令,如果一个pod容器中,有多个容器,需要使用-c选项指定容器。
   
23) run
类似于docker的run命令,直接运行一个image。
   
24) cordon, drain, uncordon
这三个命令是正式release的1.2新加入的命令,三个命令一起介绍,是因为三个命令配合使用可以实现节点的维护。在1.2之前,因为没有相应的命令支持,如果要维护一个
节点,只能stop该节点上的kubelet将该节点退出集群,是集群不在将新的pod调度到该节点上。如果该节点上本生就没有pod在运行,则不会对业务有任何影响。如果该节
点上有pod正在运行,kubelet停止后,master会发现该节点不可达,而将该节点标记为notReady状态,不会将新的节点调度到该节点上。同时,会在其他节点上创建新的
pod替换该节点上的pod。这种方式虽然能够保证集群的健壮性,但是任然有些暴力,如果业务只有一个副本,而且该副本正好运行在被维护节点上的话,可能仍然会造成业
务的短暂中断。
   
1.2中新加入的这3个命令可以保证维护节点时,平滑的将被维护节点上的业务迁移到其他节点上,保证业务不受影响。如下图所示是一个整个的节点维护的流程(为了方便
demo增加了一些查看节点信息的操作):
1- 首先查看当前集群所有节点状态,可以看到共四个节点都处于ready状态;
2- 查看当前nginx两个副本分别运行在d-node1和k-node2两个节点上;
3- 使用cordon命令将d-node1标记为不可调度;
4- 再使用kubectl get nodes查看节点状态,发现d-node1虽然还处于Ready状态,但是同时还被禁能了调度,这意味着新的pod将不会被调度到d-node1上。
5- 再查看nginx状态,没有任何变化,两个副本仍运行在d-node1和k-node2上;
6- 执行drain命令,将运行在d-node1上运行的pod平滑的赶到其他节点上;
7- 再查看nginx的状态发现,d-node1上的副本已经被迁移到k-node1上;这时候就可以对d-node1进行一些节点维护的操作,如升级内核,升级Docker等;
8- 节点维护完后,使用uncordon命令解锁d-node1,使其重新变得可调度;8)检查节点状态,发现d-node1重新变回Ready状态
   
# kubectl get nodes
# kubectl get po -o wide
# kubectl cordon d-node1
# kubectl get nodes
# kubectl get po -o wide
# kubectl drain d-node1
# kubectl get po -o wide
# kubectl uncordon
# kubectl uncordon d-node1
# kubectl get nodes
   
25) 查看某个pod重启次数(这个是参考)
# kubectl get pod nginx-controller-d97wj --template="{{range .status.containerStatuses}}{{.name}}:{{.restartCount}}{{end}}"
   
26) 查看pod生命周期
[root@k8s-master01 ~]# kubectl get pod nginx-controller-d97wj --template="{{.status.phase}}"
Running
 
=====================================================================
 
常用命令
kubectl get pods
kubectl get rc
kubectl get service
kubectl get componentstatuses
kubectl get endpoints
kubectl cluster-info
kubectl create -f redis-master-controller.yaml
kubectl delete -f redis-master-controller.yaml
kubectl delete pod nginx-772ai
kubectl logs -f pods/heapster-xxxxx -n kube-system                     #查看日志
kubectl scale rc redis-slave --replicas=3                                     #修改RC的副本数量,来实现Pod的动态缩放
etcdctl cluster-health                                                                  #检查网络集群健康状态
etcdctl --endpoints=http://172.16.60.220:2379 cluster-health     #带有安全认证检查网络集群健康状态
etcdctl member list
etcdctl set /k8s/network/config '{ "Network": "10.1.0.0/16" }'
etcdctl get /k8s/network/config
 
基础进阶
kubectl get services kubernetes-dashboard -n kube-system          #查看所有service
kubectl get deployment kubernetes-dashboard -n kube-system    #查看所有发布
kubectl get pods --all-namespaces             #查看所有pod
kubectl get pods -o wide --all-namespaces             #查看所有pod的IP及节点
kubectl get pods -n kube-system | grep dashboard
kubectl describe service/kubernetes-dashboard --namespace="kube-system"
kubectl describe pods/kubernetes-dashboard-349859023-g6q8c --namespace="kube-system"       #指定类型查看
kubectl describe pod nginx-772ai        #查看pod详细信息
kubectl scale rc nginx --replicas=5        # 动态伸缩
kubectl scale deployment redis-slave --replicas=5          #动态伸缩
kubectl scale --replicas=2 -f redis-slave-deployment.yaml         #动态伸缩
kubectl exec -it tomcat-controller-35kzb /bin/bash       #进入容器
kubectl label nodes k8s-node01 zone=north                #增加节点lable值 spec.nodeSelector: zone: north, 指定pod在哪个节点
kubectl get nodes -lzone                          #获取zone的节点
kubectl label pod tomcat-controller-35kzb role=master   #增加lable值 [key]=[value]
kubectl label pod tomcat-controller-35kzb role-                       #删除lable值
kubectl label pod tomcat-controller-35kzb role=backend --overwrite    #修改lable值
kubectl rolling-update redis-master -f redis-master-controller-v2.yaml      #配置文件滚动升级
kubectl rolling-update redis-master --image=redis-master:2.0                #命令升级
kubectl rolling-update redis-master --image=redis-master:1.0 --rollback     #pod版本回滚

 kubernetes配置etcd集群和Flannel网络部署                                                      

 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
以上的环境配置的是etcd单节点, 线上生产环境建议配置etcd集群环境. 部署范例大致如下:
1) 部署Etcd集群
二进制包下载地址:https://github.com/coreos/etcd/releases/tag/v3.2.12
 
以下部署步骤在规划的三个etcd节点操作一样,唯一不同的是etcd配置文件中的服务器IP要写当前的:
解压二进制包:
# mkdir /opt/etcd/{bin,cfg,ssl} -p
# tar zxvf etcd-v3.2.12-linux-amd64.tar.gz
# mv etcd-v3.2.12-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/
 
创建etcd配置文件:
# cat /opt/etcd/cfg/etcd  
#[Member]
ETCD_NAME="etcd01"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://172.16.60.220:2380"
ETCD_LISTEN_CLIENT_URLS="https://172.16.60.220:2379"
 
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://172.16.60.220:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://172.16.60.220:2379"
ETCD_INITIAL_CLUSTER="etcd01=https://172.16.60.220:2380,etcd02=https://172.16.60.221:2380,etcd03=https://172.16.60.222:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
 
参数解释:
ETCD_NAME 节点名称
ETCD_DATA_DIR 数据目录
ETCD_LISTEN_PEER_URLS 集群通信监听地址
ETCD_LISTEN_CLIENT_URLS 客户端访问监听地址
ETCD_INITIAL_ADVERTISE_PEER_URLS 集群通告地址
ETCD_ADVERTISE_CLIENT_URLS 客户端通告地址
ETCD_INITIAL_CLUSTER 集群节点地址
ETCD_INITIAL_CLUSTER_TOKEN 集群Token
ETCD_INITIAL_CLUSTER_STATE 加入集群的当前状态,new是新集群,existing表示加入已有集群
 
systemd管理etcd:
# cat /usr/lib/systemd/system/etcd.service
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
 
[Service]
Type=notify
EnvironmentFile=/opt/etcd/cfg/etcd
ExecStart=/opt/etcd/bin/etcd \
--name=${ETCD_NAME} \
--data-dir=${ETCD_DATA_DIR} \
--listen-peer-urls=${ETCD_LISTEN_PEER_URLS} \
--listen-client-urls=${ETCD_LISTEN_CLIENT_URLS},http://127.0.0.1:2379 \
--advertise-client-urls=${ETCD_ADVERTISE_CLIENT_URLS} \
--initial-advertise-peer-urls=${ETCD_INITIAL_ADVERTISE_PEER_URLS} \
--initial-cluster=${ETCD_INITIAL_CLUSTER} \
--initial-cluster-token=${ETCD_INITIAL_CLUSTER_TOKEN} \
--initial-cluster-state=new \
--cert-file=/opt/etcd/ssl/server.pem \
--key-file=/opt/etcd/ssl/server-key.pem \
--peer-cert-file=/opt/etcd/ssl/server.pem \
--peer-key-file=/opt/etcd/ssl/server-key.pem \
--trusted-ca-file=/opt/etcd/ssl/ca.pem \
--peer-trusted-ca-file=/opt/etcd/ssl/ca.pem
Restart=on-failure
LimitNOFILE=65536
 
[Install]
WantedBy=multi-user.target
 
把刚才生成的证书拷贝到配置文件中的位置:
# cp ca*pem server*pem /opt/etcd/ssl
 
启动并设置开启启动:
# systemctl start etcd
# systemctl enable etcd
 
都部署完成后,检查etcd集群状态:
# /opt/etcd/bin/etcdctl \
--ca-file=ca.pem --cert-file=server.pem --key-file=server-key.pem \
--endpoints="https://172.16.60.220:2379,https://172.16.60.221:2379,https://172.16.60.222:2379" \
cluster-health
member 18218cfabd4e0dea is healthy: got healthy result from https://172.16.60.220:2379
member 541c1c40994c939b is healthy: got healthy result from https://172.16.60.221:2379
member a342ea2798d20705 is healthy: got healthy result from https://172.16.60.222:2379
cluster is healthy
 
如果输出上面信息,就说明集群部署成功。如果有问题第一步先看日志:/var/log/message 或 journalctl -u etcd
 
2) Flannel部署
Falnnel要用etcd存储自身一个子网信息,所以要保证能成功连接Etcd,写入预定义子网段 (网络类型指定vxlan):
# /opt/etcd/bin/etcdctl \
--ca-file=ca.pem --cert-file=server.pem --key-file=server-key.pem \
--endpoints="https://172.16.60.220:2379,https://172.16.60.221:2379,https://172.16.60.222:2379" \
set /atomic.io/network/config  '{ "Network": "192.16.0.0/16", "Backend": {"Type": "vxlan"}}'
 
以下部署步骤在规划的每个node节点都操作。
下载二进制包:
# wget https://github.com/coreos/flannel/releases/download/v0.10.0/flannel-v0.10.0-linux-amd64.tar.gz
# tar zxvf flannel-v0.9.1-linux-amd64.tar.gz
# mv flanneld mk-docker-opts.sh /opt/kubernetes/bin
 
配置Flannel:
# cat /opt/kubernetes/cfg/flanneld
FLANNEL_OPTIONS="--etcd-endpoints=https://172.16.60.220:2379,https://172.16.60.221:2379,https://172.16.60.222:2379 -etcd-cafile=/opt/etcd/ssl/ca.pem -etcd-certfile=/opt/etcd/ssl/server.pem -etcd-keyfile=/opt/etcd/ssl/server-key.pem"
 
systemd管理Flannel:
# cat /usr/lib/systemd/system/flanneld.service
[Unit]
Description=Flanneld overlay address etcd agent
After=network-online.target network.target
Before=docker.service
 
[Service]
Type=notify
EnvironmentFile=/opt/kubernetes/cfg/flanneld
ExecStart=/opt/kubernetes/bin/flanneld --ip-masq $FLANNEL_OPTIONS
ExecStartPost=/opt/kubernetes/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/subnet.env
Restart=on-failure
 
[Install]
WantedBy=multi-user.target
 
配置Docker启动指定子网段:
# cat /usr/lib/systemd/system/docker.service
 
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
 
[Service]
Type=notify
EnvironmentFile=/run/flannel/subnet.env
ExecStart=/usr/bin/dockerd $DOCKER_NETWORK_OPTIONS
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
 
[Install]
WantedBy=multi-user.target
 
重启flannel和docker:
# systemctl daemon-reload
# systemctl start flanneld
# systemctl enable flanneld
# systemctl restart docker
1
 

  

楼主你好,我按照你的步骤部署ok了,最后访问有点疑惑。刚开始直接通过master的地址+端口号可以访问网页,通过节点地址+端口也行。后来就想把配置文件精简一点,改了一些文件配置之后通过master的地址+端口号不能访问了,通过节点+端口号还是可以的。后来又按照你的步骤又一字不漏的把配置文件改了回来,重启服务和机器后还是没有解决,不知道是怎么回事了?
#2楼 2017-12-26 17:10 | hbcdr  
楼主好,刚刚的问题已经解决了,原来是没有开启服务: systemctl restart kube-proxy

 

 

 

posted @   鲸鱼的海老大  阅读(69)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示
CONTENTS