文章目录
- 目的篇
- 概念篇
- 实操篇
- 问题总结篇
- PV-NFS创建时accessModes选择Many,PVC绑定PV失败,选择Once后成功,也可让多个Pod共享访问,控制台只能选择硬盘PV 单台读写
- service 绑定deployment时,label选择不正确或少了,导致绑定不成功,访问空白
- deployment pod的labels 与deployment的labels必须相同
- ingress本质就是clb,创建ingress会自动创建一个clb
- pv一旦被pvc绑定后,就被该pvc独占,不能在与其他pvc绑定
- pod 减少的时候会不会等待pod内的请求处理完成!
- 业务的代码放在NFS中,更新重启pod即可,镜像在配置文件变更时在pull!
- tomcat更改war包和解压文件放到不同目录!
- pod的自动扩容频率的间隔时长?
- pod的自动扩容数量为现有pod的2倍,TKE是否同样限制?是否可配置
- 突然过高并发流量导致pod阻塞,健康检查无法通过导致服务崩溃?
- service对pod的负载规则?能否设置?权重,轮询
- 超出指定pod数量的pod实列多长时间会删除和回收?
- Terminating状态的pod会如何处理?时长?
- node的调度局部最优解和全局最优解:1.12版本后默认未局部最优解
- 设置pod的自动扩容的实例数量范围后,pod的数量并未自动扩容到范围的下限数量
- 容器服务的概览中的集群资源使用量至少需要保持在30%~40%的可用率
- 健康检测中就绪检测只在容器初始化时检测一次?
- server的Selectors只匹配到deployment不匹配pod?
- hpa间隔60S获取一次pod的扩容指标,2~3分钟后扩容的pod恢复到最小数量
- hpa获取扩容指标时,有时会并不准确,关闭压测了,并且hpa值已经下降,但下一分钟的hpa值又飙升
- 相关资料地址和网站
目的篇
现有体系分析
- 成本伴随着服务规模的扩大会越来越高
- 服务器规模伴随着服务规模的扩大会越来越庞大
- 虚拟机性能利用率不高
- 无法自动扩容,自动调度,自动重启
期望Docker+Kubernetes的效果
- 快速部署应用,快速扩展应用
- 无缝对接新的应用功能节省资源,优化硬件资源的使用
- 交付、部署:虚拟机可以通过镜像实现环境交付的一致性
- 可扩展: 模块化,插件化,可挂载,可组合
- 自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展
传统虚拟化与Docker对比
虚拟机概述
虚拟机在本质上就是在模拟一台真实的计算机设备,同时遵循同样的程序执行方式。虚拟机能够利用“虚拟机管理程序”运行在物理设备之上。反过来,虚拟机管理程序则可运行在主机设备或者“裸机”之上。
传统的虚拟机需要模拟整台机器包括硬件,每台虚拟机都需要有自己的操作系统,虚拟机一旦被开启,预分配给他的资源将全部被占用。,每一个虚拟机包括应用,必要的二进制和库,以及一个完整的用户操作系统。
Docker虚拟化的架构
其中Docker Engine可以简单看成对Linux的NameSpace、Cgroup、镜像管理文件系统操作的封装。Docker并没有和虚拟机一样利用一个完全独立的Guest OS实现环境隔离,它利用的是目前linux内核本身支持的容器方式实现资源和环境隔离。简单的说,Docker是利用namespace实现系统环境的隔离;利用Cgroup实现资源限制;利用镜像实现根目录环境的隔离。
docker化优点
-
Docker启动快速属于秒级别。虚拟机通常需要几分钟去启动。
-
Docker在操作系统级别进行虚拟化,Docker容器和内核交互,几乎没有性能损耗,性能优于通过Hypervisor层与内核层的虚拟化。
-
Docker更轻量,Docker的架构可以共用一个内核与共享应用程序库,所占内存极小。同样的硬件环境,Docker运行的镜像数远多于虚拟机数量。对系统的利用率非常高。与虚拟机相比,Docker隔离性更弱,Docker属于进程之间的隔离,虚拟机可实现系统级别隔离。
-
安全性:Docker的安全性也更弱。Docker的租户root和宿主机root等同,一旦容器内的用户从普通用户权限提升为root权限,它就直接具备了宿主机的root权限,进而可进行无限制的操作。虚拟机租户root权限和宿主机的root虚拟机权限是分离的,并且虚拟机利用如Intel的VT-d和VT-x的ring-1硬件隔离技术,这种隔离技术可以防止虚拟机突破和彼此交互,而容器至今还没有任何形式的硬件隔离,这使得容器容易受到攻击。
-
可管理性:Docker的集中化管理工具还不算成熟。各种虚拟化技术都有成熟的管理工具,例如VMware vCenter提供完备的虚拟机管理能力。
-
高可用和可恢复性:Docker对业务的高可用支持是通过快速重新部署实现的。虚拟化具备负载均衡、高可用、容错、迁移和数据保护等经过生产实践检验的成熟保障机制,VMware可承诺虚拟机99.999%高可用,保证业务连续性。
-
快速创建、删除:虚拟化创建是分钟级别的,Docker容器创建是秒级别的,Docker的快速迭代性,决定了无论是开发、测试、部署都可以节约大量时间。
-
交付、部署:虚拟机可以通过镜像实现环境交付的一致性,但镜像分发无法体系化;Docker在Dockerfile中记录了容器构建过程,可在集群中实现快速分发和快速部署。
K8S 优点
- 快速部署应用,快速扩展应用
- 无缝对接新的应用功能节省资源,优化硬件资源的使用
- 可移植: 支持公有云,私有云,混合云,多重云(multi-cloud)
- 可扩展: 模块化,插件化,可挂载,可组合
- 自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展
概念篇
Dcoker相关
Dcoker是什么
Docker是一个集开发、打包、运行应用于一体的开放式平台。Docker可以用来快速交付应用。使用Docker,你可以将应用程序从你的基础设施中分离出来,并将基础设施当做一个管理平台。Docker可以加快打包时间,加快测试,加快发布,缩短开发及运行代码之间的周期。Docker通过结合内核容器化特点和工作流,并使之工具化来实现这一切,帮助管理和发布你的应用。
Docker 镜像
Docker镜像是一个只读的模板。包含了容器运行时所需要的文件系统和一些参数。镜像是无状态的,也不会改变。镜像是用来创建容器的。你可以使用docker pull命令获取一个别人已创建好的镜像,或者使用dockerbuild来构建一个自己的镜像。
Docker 容器
Docker容器就像是一个文件夹,容器中包含了应用运行所需的一切。每个容器都是一个隔离的和安全的应用平台。容器是镜像的一个实例,它是有状态的,而且随时会改变,容器一般是短暂的。
Docker Daemon
Docker Daemon直接将执行命令发送至Docker Client——例如构建、运行以及分发等等。Docker Daemon运行在主机设备之上,但作为用户,我们永远不会直接与该Daemon进行通信。Docker Client也可以运行在主机设备上,但并非必需。它亦能够运行在另一台设备上,并与运行在目标主机上的Docker Daemon进行远程通信。
Docker Client
Docker Client是我们作为最终用户的通信对象。我们可以将其视为Docker的UI。我们进行的一切操作都将直接接入Docker Client,再由其将指令传递至Docker Daemon。
Docker API
用于同Docker Daemon远程交互。
Docker 应用场景
- web应用的自动化打包和发布;
- 自动化测试和持续集成、发布;
- 在服务型环境中部署和调整数据库或其他的后台应用;
- 从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。
Kubernetes相关
K8S是什么
Kubernetes是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。
Kubernetes是Google 2014年创建管理的,是Google 10多年大规模容器管理技术Borg的开源版本
TKE是什么
腾讯云容器服务(Tencent Kubernetes Engine,TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务。腾讯云容器服务完全兼容原生 kubernetes API ,扩展了腾讯云的 CBS、CLB 等 kubernetes 插件,为容器化的应用提供高效部署、资源调度、服务发现和动态伸缩等一系列完整功能,解决用户开发、测试及运维过程的环境一致性问题,提高了大规模容器集群管理的便捷性,帮助用户降低成本,提高效率。容器服务提供免费使用,涉及的其他云产品另外单独计费。
K8S架构图及应用集群节点说明
Pod
pod运行在Node上,包含一组容器和卷。同一个Pod里的容器共享同一个网络命名空间,可以使用localhost互相通信。Pod是短暂的,不是持续性实体。你可能会有这些问题:
- 如果Pod是短暂的,那么我怎么才能持久化容器数据使其能够跨重启而存在呢? 是的,Kubernetes支持卷的概念,因此可以使用持久化的卷类型。
- 是否手动创建Pod,如果想要创建同一个容器的多份拷贝,需要一个个分别创建出来么?可以手动创建单个Pod,但是也可以使用Replication Controller使用Pod模板创建出多份拷贝
- 如果Pod是短暂的,那么重启时IP地址可能会改变,那么怎么才能从前端容器正确可靠地指向后台容器呢?这时可以使用Service
Pod的生命周期:
- Pending Pod:Pod已经被创建,但是一个或多个容器还未创建,包括Pod调度阶段,以及容器镜像的下载过程
- Running: Pod已经被调度到Node,所有容器已经创建,并且至少一个容器在运行或者正在重启。
- Successded: Pod中所有容器正常退出
- Failed: Pod中所有容器推出,至少有一个容器时一次退出的
Lable
一些Pod有Label([外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BaN3FLeV-1589009640677)(en-resource://database/730:1)])。一个Label是attach到Pod的一对键/值对,用来传递用户定义的属性。比如,你可能创建了一个"tier"和“app”标签,通过Label(tier=frontend, app=myapp)来标记前端Pod容器,使用Label(tier=backend, app=myapp)标记后台Pod。然后可以使用Selectors选择带有特定Label的Pod,并且将Service或者Replication Controller应用到上面。
Replication Controller
是否手动创建Pod,如果想要创建同一个容器的多份拷贝,需要一个个分别创建出来么,能否将Pods划到逻辑组里?
Replication Controller确保任意时间都有指定数量的Pod“副本”在运行。如果为某个Pod创建了Replication Controller并且指定3个副本,它会创建3个Pod,并且持续监控它们。如果某个Pod不响应,那么Replication Controller会替换它,保持总数为3.如下面的动画所示:
如果之前不响应的Pod恢复了,现在就有4个Pod了,那么Replication Controller会将其中一个终止保持总数为3。如果在运行中将副本总数改为5,Replication Controller会立刻启动2个新Pod,保证总数为5。还可以按照这样的方式缩小Pod,这个特性在执行滚动升级时很有用。
当创建Replication Controller时,需要指定两个东西:
-
Pod模板:用来创建Pod副本的模板
-
Label:Replication Controller需要监控的Pod的标签。
现在已经创建了Pod的一些副本,那么在这些副本上如何均衡负载呢?我们需要的是ServiceService
如果Pods是短暂的,那么重启时IP地址可能会改变,怎么才能从前端容器正确可靠地指向后台容器呢?
Service是定义一系列Pod以及访问这些Pod的策略的一层抽象。Service通过Label找到Pod组。因为Service是抽象的,所以在图表里通常看不到它们的存在,这也就让这一概念更难以理解。
现在,假定有2个后台Pod,并且定义后台Service的名称为‘backend-service’,lable选择器为(tier=backend, app=myapp)。backend-service 的Service会完成如下两件重要的事情:
-
会为Service创建一个本地集群的DNS入口,因此前端Pod只需要DNS查找主机名为 ‘backend-service’,就能够解析出前端应用程序可用的IP地址。
-
现在前端已经得到了后台服务的IP地址,但是它应该访问2个后台Pod的哪一个呢?Service在这2个后台Pod之间提供透明的负载均衡,会将请求分发给其中的任意一个(如下面的动画所示)。通过每个Node上运行的代理(kube-proxy)完成。这里有更多技术细节。
下述动画展示了Service的功能。注意该图作了很多简化。如果不进入网络配置,那么达到透明的负载均衡目标所涉及的底层网络和路由相对先进。如果有兴趣,这里有更深入的介绍。
有一个特别类型的Kubernetes Service,称为'LoadBalancer',作为外部负载均衡器使用,在一定数量的Pod之间均衡流量。比如,对于负载均衡Web流量很有用。
service的类型
- ClusterIP 默认模式,只能在集群内部访问
- NodePort 在每个节点上都监听一个同样的端口号(30000-32767),ClusterIP和路由规则会自动创建。集群外部可以访问:联系到集群内部服务,可以配合外部负载均衡使用
- LoadBalancer 要配合支持公有云负载均衡使用比如GCE、AWS。其实也是NodePort,只不过会把:自动添加到公有云的负载均衡当中
- ExternalName 创建一个dns别名指到service name上,主要是防止service name发生变化,要配合dns插件使用
Node
节点(上图橘色方框)是物理或者虚拟机器,作为Kubernetes worker,通常称为Minion。每个节点都运行如下Kubernetes关键组件:
- Kubelet:是主节点代理。
- Kube-proxy:Service使用其将链接路由到Pod,如上文所述。
- Docker或Rocket:Kubernetes使用的容器技术来创建容器。
Kubernetes Master
集群拥有一个Kubernetes Master(紫色方框)。Kubernetes Master提供集群的独特视角,并且拥有一系列组件,比如Kubernetes API Server。API Server提供可以用来和集群交互的REST端点。master节点包括用来创建和复制Pod的Replication Controller。
Deployment
- Deployment:负责控制Pod的生命周期、保证服务有一定数量的Pod在运行。Deployment定义了Pod内容,包括Pod数量、更新方式、使用的镜像,资源限制等等。
- StatefulSet:管理应用程序的工作负载 API 对象,且该应用程序为有状态的应用程序。
- DaemonSet:确保所有或部分节点上运行 Pod,例如日志采集。
- Job:一个 Job 创建一个或多个 Pod,直至运行结束。
- CronJob:定时运行的 Job 任务。
Ingress
通常情况下,service和pod仅可在集群内部网络中通过IP地址访问。所有到达边界路由器的流量或被丢弃或被转发到其他地方。从概念上讲,可能像下面这样:
internet --> service
Ingress是授权入站连接到达集群服务的规则集合。
internet -->ingress --> service
你可以给Ingress配置提供外部可访问的URL、负载均衡、SSL、基于名称的虚拟主机等。用户通过POST Ingress资源到API server的方式来请求ingress。 Ingress controller负责实现Ingress,通常使用负载平衡器,它还可以配置边界路由和其他前端,这有助于以HA方式处理流量。
Ingress与腾讯云的CLB类似,可以提供根据url进行转发的功能,与Service最根本的区别就是Service不能根据url进行转发只能定义端口转发与负载方式如公网访问,集群内访问等。两者互补。
(K8S是使用IP tables的nat表进行转发,腾讯针对IP tables在大并发情况下的性能差二次开发了IPVS)
NameSpace
对资源进行隔离,不同NameSpace空间的资源无法互相访问,可不同业务分配不同的命名空间。
当团队或项目中具有许多用户时,可以考虑使用Namespace来区分,a如果是少量用户集群,可以不需要考虑使用Namespace,如果需要它们提供特殊性质时,可以开始使用Namespace。Namespace为名称提供了一个范围。资源的Names在Namespace中具有唯一性。
Namespace是一种将集群资源划分为多个用途(通过 resource quota)的方法。
在未来的Kubernetes版本中,默认情况下,相同Namespace中的对象将具有相同的访问控制策略。对于稍微不同的资源没必要使用多个Namespace来划分,例如同意软件的不同版本,可以使用labels(标签)来区分同一Namespace中的资源。
Persistent Volumes(PV)
集群内的存储资源,例如节点是集群的资源。PV 独立于 Pod 的生命周期,根据不同的 StorageClass 类型创建不同类型的 PV。
Persistent Volume 简称PV是一个K8S资源对象,我们可以单独创建一个PV。它不和Pod直接发生关系,而是通过Persistent Volume Claim,简称PVC来实现动态绑定。Pod定义里指定的是PVC,然后PVC会根据Pod的要求去自动绑定合适的PV给Pod使用。
PersistentVolumeClaim(PVC)
PersistentVolumeClaim(PVC):集群内的存储请求。例如,PV 是 Pod 使用节点资源,PVC 则声明使用 PV 资源。当 PV 资源不足时,PVC 也可以动态创建 PV。
服务的自动扩容算法
服务自动扩缩容后台组件会定期(30s)向腾讯云云监控拉取容器和 POD 的监控指标,然后根据该指标当前值,当前副本数和该指标目标值计算出目标副本数,然后以该目标副本数作为服务的期望副本数,达到自动伸缩的目的。 比如当前有 2 个实例, 平均 CPU 利用率为 90%,服务自动伸缩设置目标 CPU 为 60%, 则自动调整实例数量为:90% * 2 / 60% = 3 个。如果用户设置了多个弹性伸缩指标,HPA 会依据各个指标,分别计算出目标副本数,然后取最大的一个作为最终目标副本数。—TKE文档中心
Node的自动扩容
K8S核心组件
Kubernetes遵循master-slave architecture。Kubernetes的组件可以分为管理单个的 node 组件和控制平面的一部分的组件。
Kubernetes Master是集群的主要控制单元,用于管理其工作负载并指导整个系统的通信。Kubernetes控制平面由各自的进程组成,每个组件都可以在单个主节点上运行,也可以在支持high-availability clusters的多个主节点上运行。
Kubernetes主要由以下几个核心组件组成:
核心组件结构图
除了核心组件,还有一些推荐的Add-ons:
实操篇
服务的自动扩容
自动扩容其实是K8S判断POD的一些硬件指标(CPU使用率,带宽等)是否达到了预警值,如果达到了,就行扩容,没有到不变
命令行方式实现
kubectl autoscale deployment php-apache --min=1 --max=10 --cpu-percent=50
最小Pod数量为1,最大为10,当CPU使用率超过50%时触发
yaml文件中指定
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1beta1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 50
TKE 控制台创建Deployment时指定
有状态的服务如何通过deployment创建
镜像的对接
1.先从docker hub上找到nginx 或tomcat指定版本的官方Dokcer file构建生成基础镜像,并推到私有仓库中
基础openresty镜像例子:
# Dockerfile - alpine
# https://github.com/openresty/docker-openresty
ARG RESTY_IMAGE_BASE="alpine"
ARG RESTY_IMAGE_TAG="3.9"
FROM ${RESTY_IMAGE_BASE}:${RESTY_IMAGE_TAG}
LABEL maintainer="Evan Wies <[email protected]>"
# Docker Build Arguments
ARG RESTY_IMAGE_BASE="alpine"
ARG RESTY_IMAGE_TAG="3.9"
ARG RESTY_VERSION="1.15.8.1"
ARG RESTY_OPENSSL_VERSION="1.1.1c"
ARG RESTY_PCRE_VERSION="8.42"
ARG RESTY_J="1"
ARG RESTY_CONFIG_OPTIONS="\
--with-compat \
--with-file-aio \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_geoip_module=dynamic \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_image_filter_module=dynamic \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-http_xslt_module=dynamic \
--with-ipv6 \
--with-mail \
--with-mail_ssl_module \
--with-md5-asm \
--with-pcre-jit \
--with-sha1-asm \
--with-stream \
--with-stream_ssl_module \
--with-threads \
"
ARG RESTY_CONFIG_OPTIONS_MORE=""
ARG RESTY_LUAJIT_OPTIONS="--with-luajit-xcflags='-DLUAJIT_NUMMODE=2 -DLUAJIT_ENABLE_LUA52COMPAT'"
ARG RESTY_ADD_PACKAGE_BUILDDEPS=""
ARG RESTY_ADD_PACKAGE_RUNDEPS=""
ARG RESTY_EVAL_PRE_CONFIGURE=""
ARG RESTY_EVAL_POST_MAKE=""
# These are not intended to be user-specified
ARG _RESTY_CONFIG_DEPS="--with-openssl=/tmp/openssl-${RESTY_OPENSSL_VERSION} --with-pcre \
--with-cc-opt='-DNGX_LUA_ABORT_AT_PANIC -I/usr/local/openresty/pcre/include' \
--with-ld-opt='-L/usr/local/openresty/pcre/lib -Wl,-rpath,/usr/local/openresty/pcre/lib' \
"
LABEL resty_image_base="${RESTY_IMAGE_BASE}"
LABEL resty_image_tag="${RESTY_IMAGE_TAG}"
LABEL resty_version="${RESTY_VERSION}"
LABEL resty_openssl_version="${RESTY_OPENSSL_VERSION}"
LABEL resty_pcre_version="${RESTY_PCRE_VERSION}"
LABEL resty_config_options="${RESTY_CONFIG_OPTIONS}"
LABEL resty_config_options_more="${RESTY_CONFIG_OPTIONS_MORE}"
LABEL resty_config_deps="${_RESTY_CONFIG_DEPS}"
LABEL resty_add_package_builddeps="${RESTY_ADD_PACKAGE_BUILDDEPS}"
LABEL resty_add_package_rundeps="${RESTY_ADD_PACKAGE_RUNDEPS}"
LABEL resty_eval_pre_configure="${RESTY_EVAL_PRE_CONFIGURE}"
LABEL resty_eval_post_make="${RESTY_EVAL_POST_MAKE}"
# 1) Install apk dependencies
# 2) Download and untar OpenSSL, PCRE, and OpenResty
# 3) Build OpenResty
# 4) Cleanup
RUN apk add --no-cache --virtual .build-deps \
build-base \
coreutils \
curl \
gd-dev \
geoip-dev \
libxslt-dev \
linux-headers \
make \
perl-dev \
readline-dev \
zlib-dev \
${RESTY_ADD_PACKAGE_BUILDDEPS} \
&& apk add --no-cache \
bash \
bash-doc \
bash-completion \
gd \
geoip \
libgcc \
libxslt \
zlib \
${RESTY_ADD_PACKAGE_RUNDEPS} \
&& cd /tmp \
&& if [ -n "${RESTY_EVAL_PRE_CONFIGURE}" ]; then eval $(echo ${RESTY_EVAL_PRE_CONFIGURE}); fi \
&& curl -fSL https://www.openssl.org/source/openssl-${RESTY_OPENSSL_VERSION}.tar.gz -o openssl-${RESTY_OPENSSL_VERSION}.tar.gz \
&& tar xzf openssl-${RESTY_OPENSSL_VERSION}.tar.gz \
&& curl -fSL https://ftp.pcre.org/pub/pcre/pcre-${RESTY_PCRE_VERSION}.tar.gz -o pcre-${RESTY_PCRE_VERSION}.tar.gz \
&& tar xzf pcre-${RESTY_PCRE_VERSION}.tar.gz \
&& cd /tmp/pcre-${RESTY_PCRE_VERSION} \
&& ./configure \
--prefix=/usr/local/openresty/pcre \
--disable-cpp \
--enable-jit \
--enable-utf \
--enable-unicode-properties \
&& make -j${RESTY_J} \
&& make -j${RESTY_J} install \
&& cd /tmp \
&& curl -fSL https://openresty.org/download/openresty-${RESTY_VERSION}.tar.gz -o openresty-${RESTY_VERSION}.tar.gz \
&& tar xzf openresty-${RESTY_VERSION}.tar.gz \
&& cd /tmp/openresty-${RESTY_VERSION} \
&& if [[ "1.1.1" == $(echo -e "${RESTY_OPENSSL_VERSION}\n1.1.1" | sort -V | head -n1) ]] ; then \
echo 'patching Nginx for OpenSSL 1.1.1' \
&& cd bundle/nginx-1.15.8 \
&& curl -s https://raw.githubusercontent.com/openresty/openresty/master/patches/nginx-1.15.8-ssl_cert_cb_yield.patch | patch -p1 \
&& curl -s https://raw.githubusercontent.com/openresty/openresty/master/patches/nginx-1.15.8-ssl_sess_cb_yield.patch | patch -p1 \
&& cd ../.. ; \
fi \
&& eval ./configure -j${RESTY_J} ${_RESTY_CONFIG_DEPS} ${RESTY_CONFIG_OPTIONS} ${RESTY_CONFIG_OPTIONS_MORE} ${RESTY_LUAJIT_OPTIONS} \
&& make -j${RESTY_J} \
&& make -j${RESTY_J} install \
&& cd /tmp \
&& if [ -n "${RESTY_EVAL_POST_MAKE}" ]; then eval $(echo ${RESTY_EVAL_POST_MAKE}); fi \
&& rm -rf \
openssl-${RESTY_OPENSSL_VERSION} \
openssl-${RESTY_OPENSSL_VERSION}.tar.gz \
openresty-${RESTY_VERSION}.tar.gz openresty-${RESTY_VERSION} \
pcre-${RESTY_PCRE_VERSION}.tar.gz pcre-${RESTY_PCRE_VERSION} \
&& apk del .build-deps \
&& ln -sf /dev/stdout /usr/local/openresty/nginx/logs/access.log \
&& ln -sf /dev/stderr /usr/local/openresty/nginx/logs/error.log
# Add additional binaries into PATH for convenience
ENV PATH=$PATH:/usr/local/openresty/luajit/bin:/usr/local/openresty/nginx/sbin:/usr/local/openresty/bin:/bin/bash
# Copy nginx configuration files
COPY conf/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY conf/nginx.conf /etc/nginx/conf.d/default.conf
#CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]
CMD ["bash"]
# Use SIGQUIT instead of default SIGTERM to cleanly drain requests
# See https://github.com/openresty/docker-openresty/blob/master/README.md#tips--pitfalls
STOPSIGNAL SIGQUIT
构建时使用docker build --tag 或构建完成后使用docker --tag imageid imagename:version
docker login url #登陆私有仓库
docker tag 5787d71f774c shjrccr.ccs.tencentyun.com/yw/openresty:1.15.8.1-alpine-fat-apph5 #更改镜像名称
docker push shjrccr.ccs.tencentyun.com/yw /openresty:1.15.8.1-alpine-fat-apph5 #推送镜像到私有仓库
基于刚才构建的openresty基础镜像,进行二次构建
业务nginx镜像:
FROM shjrccr.ccs.tencentyun.com/yw/openresty:1.15.8.1-alpine-fat
MAINTAINER WQ
ADD conf/nginx.conf /usr/local/openresty/nginx/conf/
RUN mkdir -pv /app/public/view/ /app/logs
ADD pro/appH5 /app/public/view/
#CMD ["bash"]
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]
将此业务镜像推到私有仓库即可
日志的输出与NFS类型的PV PVC
新建NFS服务器
安装软件包,启动服务并添加至开机自启
yum install -y rpcbind nfs-utils
systemctl start rpcbind
systemctl enable rpcbind
systemctl start nfs-server
systemctl enable nfs-server
修改配置文件,并重读配置文件
[root@VM_1_11_centos ~]# cat /etc/exports
/opt/logs *(rw,sync,no_root_squash)
exportfs -r
命令行方式创建pv,pvc
TKE 控制台无法直接创建nfs pv和nfs pvc可在创建deployment时选择已有nfs
登陆任意Node节点,创建PV
[root@VM_1_14_centos k8s_test]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvnfs 50Gi RWO Retain Bound default/nfsclaim pvnfs 2d23h
[root@VM_1_14_centos k8s_test]# kubectl create -f pv_nfs.yaml
pv_nfs.yaml实例
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvnfs
namespace: default
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteOnce
nfs:
path: /opt/logs
server: 10.2.1.11
storageClassName: pvnfs
创建pvc
[root@VM_1_14_centos k8s_test]# kubectl create -f pvc_nfs.yaml
[root@VM_1_14_centos k8s_test]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfsclaim Bound pvnfs 50Gi RWO pvnfs 2d23h
pvc_nfs.yaml实例:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfsclaim
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: pvnfs
创建deployment
[root@VM_1_14_centos k8s_test]# kubectl create -f nginx_deployment.yaml
[root@VM_1_14_centos k8s_test]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
lbapph5 3 3 3 3 2d22h
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-apph5
labels:
app: nginx-deployment-apph5
vers: v02
spec:
replicas: 3
selector:
matchLabels:
app: nginx-deployment-apph5
vers: v02
template:
metadata:
labels:
app: nginx-deployment-apph5
vers: v02
spec:
containers:
- image: shjrccr.ccs.tencentyun.com/yw/openresty:v1apph5v0.2
imagePullPolicy: IfNotPresent
name: apph5v02
volumeMounts:
- mountPath: "/app/logs/"
name: nginx-nfs-pvc
imagePullSecrets:
- name: w-dr
volumes:
- name: nginx-nfs-pvc
persistentVolumeClaim:
claimName: nfsclaim
service的创建
[root@VM_1_14_centos k8s_test]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.16.255.1 443/TCP 6d23h
lbapph5 LoadBalancer 172.16.255.45 211.159.251.238 8000:30709/TCP 2d22h
[root@VM_1_14_centos k8s_test]# kubectl create -f service.yaml
实例:
apiVersion: v1
kind: Service
metadata:
name: apph5-service
labels:
name: apph5-service
spec:
type: NodePort #这里代表是NodePort类型的
ports:
- port: 80 #这里的端口和clusterIP(10.97.114.36)对应,即10.97.114.36:80,供内部访问。
targetPort: 8000 #端口一定要和container暴露出来的端口对应,nodejs暴露出来的端口是8081,所以这里也应是8081
protocol: TCP
nodePort: 32143 # 所有的节点都会开放此端口,此端口供外部调用。
selector:
app: nginx-deployment-apph5
vers: v02
创建方式:
- 可直接通过kuberctl create/apply -f servicename.yaml
- 也可通过TKE控制面板创建
service 与CLB绑定
service类型选择NodePort,CLB绑定Node节点
登陆nfs服务器查看日志
多个容器日志会吐到同一文件
K8S相关角色配置文件示例
Node
有两种创建方式:
- 新增节点
- 添加已有节点
Pod
实例:
apiVersion: v1
kind: Pod
metadata:
name: apph5pod
spec:
containers:
- name: apph5pod
image: shjrccr.ccs.tencentyun.com/yw/openresty:v1apph5v0.1
imagePullPolicy: Always
imagePullSecrets:
- name: w-dr
在任意一台Node上面通过命令kubectl 创建
kubectl [option] name
kubectl get podname -o wide/json/…
创建Pod以及查看:
[root@VM_1_14_centos k8s_test]# kubectl get pod
NAME READY STATUS RESTARTS AGE
apph5pod 1/1 Running 0 15m
lbapph5-6bdbbb7ff5-fb7fq 1/1 Running 0 18m
lbapph5-6bdbbb7ff5-jzwbw 1/1 Running 0 4h13m
lbapph5-6bdbbb7ff5-llnxs 1/1 Running 0 4h13m
lbapph5-6bdbbb7ff5-qxmnp 1/1 Running 0 18m
[root@VM_1_14_centos k8s_test]# kubectl delete pod apph5pod
pod “apph5pod” deleted
[root@VM_1_14_centos k8s_test]# kubectl get pod
NAME READY STATUS RESTARTS AGE
lbapph5-6bdbbb7ff5-fb7fq 1/1 Running 0 18m
lbapph5-6bdbbb7ff5-jzwbw 1/1 Running 0 4h13m
lbapph5-6bdbbb7ff5-llnxs 1/1 Running 0 4h13m
lbapph5-6bdbbb7ff5-qxmnp 1/1 Running 0 18m
[root@VM_1_14_centos k8s_test]# kubectl create -f pod.yaml
pod/apph5pod created
[root@VM_1_14_centos k8s_test]# kubectl get pod
NAME READY STATUS RESTARTS AGE
apph5pod 1/1 Running 0 4s
lbapph5-6bdbbb7ff5-fb7fq 1/1 Running 0 18m
lbapph5-6bdbbb7ff5-jzwbw 1/1 Running 0 4h13m
lbapph5-6bdbbb7ff5-llnxs 1/1 Running 0 4h13m
lbapph5-6bdbbb7ff5-qxmnp 1/1 Running 0 18m
PV
硬盘类型PV实例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
qcloudCbs:
cbsDiskId: disk-qlaxhjzq ## 指定已有的CBS id
fsType: ext4
storageClassName: cbs
NFS类型实例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvnfs
namespace: default
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteOnce
nfs:
path: /opt/logs
server: 10.2.1.11
storageClassName: pvnfs
accessModes:
- ReadWriteOnce:数据卷能够在一个节点上挂载为读写目录
- ReadWriteMany:数据卷能够在多个节点上挂载为读写目录
- ReadOnlyMany: 数据卷能够在多个节点上挂载为读写目录
PV回收策略:
-
Retain:PV释放后,需要人工进行回收操作
- Recycle:PV释放后,K8S自动进行清理,清理成功后PV则可以在此绑定使用,目前只有NFS和HostPath类型的PV支持回收策略。当执行回收策略时,会创建一个PV Recycler Pod,这个Pod执行清理操作,即删除PV目录下的所有文件 含隐藏文件。
创建方式:
- 可直接通过kuberctl create/apply -f pv.yaml
- 也可以通过TKE控制台创建,控制台只能创建硬盘类型的PV
POD
apiVersion: v1 //版本
kind: pod //类型,pod
metadata: //元数据
name: String //元数据,pod的名字
namespace: String //元数据,pod的命名空间
labels: //元数据,标签列表
- name: String //元数据,标签的名字
annotations: //元数据,自定义注解列表
- name: String //元数据,自定义注解名字
spec: //pod中容器的详细定义
containers: //pod中的容器列表,可以有多个容器
- name: String
image: String //容器中的镜像
imagesPullPolicy: [Always|Never|IfNotPresent]//获取镜像的策略
command: [String] //容器的启动命令列表(不配置的话使用镜像内部的命令)
args: [String] //启动参数列表
workingDir: String //容器的工作目录
volumeMounts: //挂载到到容器内部的存储卷设置
- name: String
mountPath: String
readOnly: boolean
ports: //容器需要暴露的端口号列表
- name: String
containerPort: int //容器要暴露的端口
hostPort: int //容器所在主机监听的端口(容器暴露端口映射到宿主机的端口)
protocol: String
env: //容器运行前要设置的环境列表
- name: String
value: String
resources: //资源限制
limits:
cpu: Srting
memory: String
requeste:
cpu: String
memory: String
livenessProbe: //pod内容器健康检查的设置
exec:
command: [String]
httpGet: //通过httpget检查健康
path: String
port: number
host: String
scheme: Srtring
httpHeaders:
- name: Stirng
value: String
tcpSocket: //通过tcpSocket检查健康
port: number
initialDelaySeconds: 0//首次检查时间
timeoutSeconds: 0 //检查超时时间
periodSeconds: 0 //检查间隔时间
successThreshold: 0
failureThreshold: 0
securityContext: //安全配置
privileged: falae
restartPolicy: [Always|Never|OnFailure]//重启策略
nodeSelector: object //节点选择
imagePullSecrets:
- name: String
hostNetwork: false //是否使用主机网络模式,默认否
volumes: //在该pod上定义共享存储卷
- name: String
meptyDir: {}
hostPath:
path: string
secret: //类型为secret的存储卷
secretName: String
item:
- key: String
path: String
configMap: //类型为configMap的存储卷
name: String
items:
- key: String
path: String
健康检测
livenessProbe:
failureThreshold: 5
httpGet:
path: /demo/sql
port: 9000
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 30
name: sim-credit-auto
readinessProbe:
failureThreshold: 5
httpGet:
path: /demo/sql
port: 9000
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 30
PVC
NFS-PV实例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-ali
namespace: sim-bis
labels:
pv: pv-nfs-ali
spec:
capacity:
storage: 500Mi
accessModes:
- ReadWriteOnce
nfs:
path: /sim/bis/tomcat/ali
server: 10.2.1.6
storageClassName: sim-b-tomcat
指定pv的PVC实例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-ali
namespace: sim-b
labels:
pv: pv-nfs-ali
spec:
capacity:
storage: 500Mi
accessModes:
- ReadWriteOnce
nfs:
path: /sim/bis/tomcat/ali
server: 10.2.1.6
storageClassName: sim-b-tomcat
创建方式:
- 可直接通过kuberctl create/apply -f pvc.yaml
- 也可通过TKE控制面板创建
Deployment
实例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: dm-tomcat-auto
labels:
app: dm-tomcat-auto
vers: v01
namespace: sim-credit
spec:
replicas: 1
selector:
matchLabels:
app: dm-tomcat-auto
vers: v01
template:
metadata:
labels:
app: dm-tomcat-auto
vers: v01
spec:
containers:
- image: shjrccr.ccs.tencentyun.com/sim_credit/tomcat_base:v01
imagePullPolicy: Always
name: dm-tomcat-auto
livenessProbe:
httpGet:
path: /demo/sql
port: 9000
# host: localhost
scheme: HTTP
initialDelaySeconds: 90
timeoutSeconds: 10
volumeMounts:
- mountPath: "/opt/apache-tomcat-8.5.14/webapps/"
name: pvc-nfs-auto-war
- mountPath: "/opt/apache-tomcat-8.5.14/logs/"
name: pvc-nfs-auto-log
imagePullSecrets:
- name: qcloudregistrykey
volumes:
- name: pvc-nfs-auto-war
persistentVolumeClaim:
claimName: pvc-nfs-auto-war
- name: pvc-nfs-auto-log
persistentVolumeClaim:
claimName: pvc-nfs-auto-log
创建方式:
- 可直接通过kuberctl create/apply -f deploymentname.yaml
- 也可通过TKE控制面板创建,控制面板创建会一起创建Service
service
apiVersion: v1
kind: Service
metadata:
name: apph5-service
labels:
name: apph5-service
spec:
type: NodePort #这里代表是NodePort类型的
ports:
- port: 80 #这里的端口和clusterIP(10.97.114.36)对应,即10.97.114.36:80,供内部访问。
targetPort: 8000 #端口一定要和container暴露出来的端口对应,nodejs暴露出来的端口是8081,所以这里也应是8081
protocol: TCP
nodePort: 32143 # 所有的节点都会开放此端口,此端口供外部调用。
selector:
app: nginx-deployment-apph5
vers: v02
Docker镜像相关
镜像对比
- alpine 小巧,只有5M,依赖包较少
- centos 200M左右,各种依赖包,字体等全
基础镜像
思路:通过dockerhub中官方提供的DockerFile构建成基础镜像–基于此基础镜像,此基础镜像放到私有仓库中(涉及私有仓库命名)-- 再构建时将项代码放进入
K8S命令
1、kubectl create -f nginx-deployment.yaml
2、查看deployment
kubectl get deployment
3、查看pod
kubectl get pod -o wide
4、测试pod访问
curl --head 10.2.83.17
5、更新deployment
kubectl set image deployment/nginx-deployment nginx=nginx:v2 --record
6、查看更新后的deployment
kubectl get deployment -o wide
7、查看更新历史
kubectl rollout history deployment/nginx-deployment
8、查看具体某一个版本的升级历史
kubectl rollout history deployment/nginx-deployment --record
9、快速回滚到上一个版本
kubectl rollout undo deployment/nginx-deployment
10、扩容到5各节点
kubectl scale deployment nginx-deployment --replicas=5
私有仓库命名:
- 基于命名空间区分环境与业务,如online-wms
- 不同命名空间有各自的nginx tomcat镜像,基础镜像作为一个单独的分支
- 基础镜像命名:openresty-base tomcat-base tag作为基础镜像版本
- 镜像信息的描述作为每个tag更新的说明
- 基于基础镜像构建的生产镜像前端应用同一用openresty-ol分支,不同前端代码使用tag作为区分,如openresty-ol:v0.1apph5v0.1,前面的v0.1作为构建时基础镜像的版本,apph5作为业务描述,后面的v0.1作为业务更新的版本
- 也可不同前端代码不同的分支,但这样分支会很多,查找不方便
问题总结篇
PV-NFS创建时accessModes选择Many,PVC绑定PV失败,选择Once后成功,也可让多个Pod共享访问,控制台只能选择硬盘PV 单台读写
主要看后端存贮,模式一定要一致,用来吐日志NFS有性能瓶颈,推荐file beat,压测NFS性能,容器标准输出到宿主机,
service 绑定deployment时,label选择不正确或少了,导致绑定不成功,访问空白
service 对应Deployment的label
发布蓝鲸调用TKE的API
用NFS方式的war包 TKE一些特性无法实现
本地push镜像到TKE镜像仓库 网速波动
deployment pod的labels 与deployment的labels必须相同
ingress本质就是clb,创建ingress会自动创建一个clb
应用型CLB有url匹配
service ingress 4层7层
pv一旦被pvc绑定后,就被该pvc独占,不能在与其他pvc绑定
pod 减少的时候会不会等待pod内的请求处理完成!
优雅关闭优雅关闭是指在pod准备关闭时,可能还需要做一些处理,比如保存数据等。这期间服务不会接受新的请求。kubernetes提供了优雅关闭的配置terminationGracePeriodSeconds: 60
在给pod发出关闭指令时,k8s将会给应用发送SIGTERM信号,程序只需要捕获SIGTERM信号并做相应处理即可。配置为k8s会等待60秒后关闭。
docker镜像的 STOPSIGNAL SIGQUIT
超过等待时间直接强制杀死POD
业务的代码放在NFS中,更新重启pod即可,镜像在配置文件变更时在pull!
根据不同的路径区分不同的版本
tomcat更改war包和解压文件放到不同目录!
pod的自动扩容频率的间隔时长?
第一次扩 达到即扩容,两次扩的间隔5分钟,两次收缩间隔3分钟
秒杀活动不现实
pod的自动扩容数量为现有pod的2倍,TKE是否同样限制?是否可配置
突然过高并发流量导致pod阻塞,健康检查无法通过导致服务崩溃?
初始pod数量调高 提高系统整体吞吐量,初始pod占用资源调高 提升单个pod的可承受并发量
service对pod的负载规则?能否设置?权重,轮询
轮询
超出指定pod数量的pod实列多长时间会删除和回收?
8个总量,实际占用request的值
Terminating状态的pod会如何处理?时长?
强制删除pod
kubectl delete pod podname --force --grace-period 0 -n namespace
容器内有非空目录(容器运行过程中) 其他情况
node的调度局部最优解和全局最优解:1.12版本后默认未局部最优解
千台node不会出现调度器瓶颈
设置pod的自动扩容的实例数量范围后,pod的数量并未自动扩容到范围的下限数量
容器服务的概览中的集群资源使用量至少需要保持在30%~40%的可用率
此处的资源使用量计算:如果node的内存还剩0.5G 内存0.5G,这样的node有4台,会在资源使用量中生成2G CPU 2G内存,但pod请求的资源某一项超过0.5G则pod不会创建成功,会造成对pod数量的错误计算
健康检测中就绪检测只在容器初始化时检测一次?
在压测时未配置健康检测,只配置了就绪检测,但部分pod发现会重启导致本来就承受不住并发流量的服务崩溃
有检测时间间隔
健康检查失败 重启,存活检查失败 流量不进入
server的Selectors只匹配到deployment不匹配pod?
因通过控制台生成的deployment的label和pod的label相同
label匹配
hpa间隔60S获取一次pod的扩容指标,2~3分钟后扩容的pod恢复到最小数量
hpa获取扩容指标时,有时会并不准确,关闭压测了,并且hpa值已经下降,但下一分钟的hpa值又飙升
待确定
多个应用放到同一个集群,namespace隔离
CLB目前只能绑定主机
新增NODE,已有POD只等待下次调度,新的POD会分配到新的NODE
海通证券,QQ空间 微视 腾讯视频
非核心交易都已容器化(两年前),秒杀F5限流
前期规划 容器给一个B端
pod同样受所在NODE的安全组限制
日志是filebeat 还是NFS
相关资料地址和网站
Docker官网
DockerHub官网
Kubernetes官网
Kubernetes中文文档
十分钟带你理解Kubernetes核心概念
Kubernetes 编排系统
Docker与虚拟机的简介以及比较