《容器即服务.从零构建企业级容器集群》林帆2018.4 笔记
健康/对外服务/多租户/节点管理
健康检查
Probe探针功能为用户部署的服务提供精确到容器的健康检查功能,可以在某些容器出现故障时能及时进行路由屏蔽或重启
探针有两种类型:LivenessProbe、ReadinessProbe
LivenessProbe,当检查到容器不正常时,直接删除故障容器,根据pod中RestartPolicy属性决定是否重新创建一个
ReadinessProbe,当检查到容器不正常时,将该容器所属pod从所有使用它的Service对象的Endpoint里移除,即断开路由
探针判断容器是否健康有三种方式:执行指定的用户脚本、访问特定的HTTP路径、检查特定的TCP端口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
cat probe-demo.yaml apiVersion: v1 kind: Pod metadata: name: probe-demo spec: containers: - name: nginx-demo # 探针是作用于指定容器的 image: nginx livenessProbe: # LivenessProbe 探针 tcpSocket: # 检查方式为检查指定端口 port: 80 initialDelaySeconds: 15 # 检查的时间间隔 timeoutSeconds: 1 # 检查的访问时长 readinessProbe: # ReadinessProbe 探针 exec: # 检查方式为指定脚本 command: - /bin/ls -l initialDelaySeconds: 15 timeoutSeconds: 1 |
每个容器最多可以有一个LivenessProbe和一个ReadinessProbe
上例中,若容器80端口不能访问,或指定的命令返回非0值,容器将判定为不健康,并将依据探针类型做出相应处理
1 2 3 4 5 6 7 |
# HTTP类型的检查示例 livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 15 timeoutSeconds: 1 |
提供对外服务
NodePort或LoadBalancer类型的Service对象,但会在所有node节点额外占用一个网络端口,且LoadBalancer只在特定云平台上可用
Ingress允许用户创建一个固定的出口,通过负载均衡对外提供服务,现有基于GEC和Nginx的7层IngressController镜像,实现基于域名的7层负载出口
Nginx的IngressController需开启k8s的ServiceAccount特性,即在启动kube-apiserver时,--admission-control参数中包含ServiceAccount
使用:gcr.io/google_containers/nginx-ingress-controller:0.8.3 [anjia0532/nginx-ingress-controller:0.8.3],该镜像启动时需指定一个默认服务后端,即当请求的目标域名和路径与所有已注册的Ingress项目都不匹配时,默认到的后端,通常使用 gcr.io/google_containers/defaultbackend:1.0 [anjia0532/google-containers.defaultbackend:1.0],该镜像的功能是除/healthz外,永远返回404
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 404-backend.rc.yaml apiVersion: v1 kind: ReplicationController metadata: name: 404-backend spec: replicas: 1 selector: app: 404-backend template: metadata: labels: app: 404-backend spec: containers: - name: default-http-backend image: anjia0532/google-containers.defaultbackend:1.0 ports: - containerPort: 8080 |
kubectl create -f 404-backend.rc.yaml
# 创建对应服务
kubectl expose rc 404-backend --port=80 --target-port=8080 --name=s404-backend
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 |
# nginx-ingress-controller.rc.yaml apiVersion: v1 kind: ReplicationController metadata: name: nginx-ingress-controller spec: replicas: 1 selector: k8s-app: nginx-ingress-lb template: metadata: labels: k8s-app: nginx-ingress-lb spec: containers: - image: anjia0532/nginx-ingress-controller:0.8.3 name: nginx-ingress-lb env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - containerPort: 80 hostPort: 80 - containerPort: 443 hostPort: 443 args: - /nginx-ingress-controller - --default-backend-service=$(POD_NAMESPACE)/s404-backend |
kubectl create -f nginx-ingress-controller.rc.yaml
由于IngressController已经使用hostPort监听所在节点的指定端口,因此不需要再创建额外的Serivce
当IngressController的pod启动后,其所在节点就成为Ingress配置的所有台后服务入口,因此需要固定它所运行的节点
添加后端服务容器
kubectl run echoheaders --image=anjia0532/google-containers.echoserver:1.4 --replicas=1 --port=8080
创建一个service
kubectl expose deployment echoheaders --port=80 --target-port=8080 --name=echoheaders
创建一个ingress规则描述文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# echomap.ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: name: echomap spec: rules: - host: foo.com http: paths: - path: /foo backend: serviceName: echoheaders servicePort: 80 - host: baz.com http: paths: - path: /bar backend: serviceName: echoheaders servicePort: 80 |
该规则文件定义两个合法请求路径:foo.com/foo和baz.com/bar
kubectl create -f echomap.ingress.yaml
访问
curl 127.0.0.1/foo -H 'Host: foo.com'
## 访问其他路径或用其他域名访问将返回404
多租户隔离和配额
k8s使用Namespace对象实现租户隔离功能,不同租户间不能看到对方创建的资源。对集群资源的限制也是基于租户设定的
资源限制有两种类型:
Resource Quota,对整个namespace内所有对象所用资源的总和进行限制
LimitRange,对指定namespace中每个pod或容器所用资源进行限制
这两种特定需要在kube-apiserver启动参数--admission-control参数中包含
Resource Quota限制的资源包括:CPU、内存、虚拟资源、Pod、Service等
示例:
1 2 3 4 5 6 7 8 9 |
# pod-quota.rq.yaml # 限制整个namespace最大可创建pod数为2 apiVersion: v1 kind: ResourceQuota metadata: name: pod-quota spec: hard: pods: "2" |
新建命名空间
kubectl create namespace quota-example
应用pod-quota
kubectl create -f pod-quota.rq.yaml --namespace=quota-example
查看资源配额
kubectl describe resourcequota pod-quota --namespace=quota-example
##
创建一个pod
kubectl run nginx --image=nginx --replicas=1 --namespace=quota-example
kubectl get pods --namespace=quota-example
kubectl describe resourcequota pod-quota --namespace=quota-example
尝试将个数扩展到4个
kubectl scale deployment nginx --replicas=4 --namespace=quota-example
kubectl get pods --namespace=quota-example
由于资源配额的存在 实际只能创建两个pod
计算资源的配额比较特殊,一旦在namespace中使用此类资源配额,在创建pod时,必须指明所需资源的用量,否则会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# cpu-mem-quota.rq.yaml apiVersion: v1 kind: ResourceQuota metadata: name: cpu-mem-quota spec: hard: # 资源需求总量 requests.cpu: "1" requests.memory: 1Gi # 资源限制总量 limits.cpu: "2" limits.memory: 2Gi |
资源需求总量:在资源调度时,目标node剩余计算资源满足requests的量,pod就可以调度到该节点,可视为pod的最小资源需求
资源限制总量:pod实际运行时能够使用的资源最大值,pod运行时不会超过限制的值
kubectl create -f cpu-mem-quota.rq.yaml --namespace=quota-example
应用限额后,新建pod必须指定需求和限制量,且不能超额
kubectl run nginx --image=nginx --replicas=1 --namespace=quota-example --requests=cpu=100m,memory=256Mi --limits=cpu=200m,memory=512Mi
使用LimitRange特性时,在启动kube-apiserver时,--admission-control参数值中要包含LimitRange
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 |
# demo-limits.lr.yaml apiVersion: v1 kind: LimitRange metadata: name: demo-limits spec: limits: - type: Pod max: cpu: "1" memory: 1Gi min: cpu: 200m memory: 6Mi - type: Container default: cpu: 300m memory: 200Mi defaultRequest: cpu: 200m memory: 100Mi max: cpu: "1" memory: 1Gi min: cpu: 100m memory: 3Mi |
demo-limits.lr.yaml对pod和容器配额做了限制,其中容器配额还设置了default和defaultRequest,分别作为该namespace中创建容器的limits和requests资源的默认值
kubectl create namespace limit-example
kubectl create -f demo-limits.lr.yaml --namespace=limit-example
kubectl describe limits demo-limits --namespace=limit-example
应用
不满足限额的资源不会创建
kubectl run nginx --image=nginx --replicas=1 --namespace=limit-example --requests=cpu=200m,memory=256Mi --limits=cpu=200m,memory=512Mi
集群节点管理
节点也是一种资源对象,可以使用yaml来描述和管理
# 查看集群信息
kubectl cluster-info
# 查看节点
kubectl get node
# 查看详细yaml格式信息
kubectl get node -o yaml
# 保存某一节点的yaml描述
kubectl get node k8s-node -o yaml > k8s-node.yaml
# 删除节点
kubectl delete node k8s-node
kubectl delete -f k8s-node.yaml
# 添加节点
kubectl create -f k8s-node.yaml
默认,kubelet进程启动时,会向指向的集群master节点注册,因此直接登录目标节点重启kubelet服务也能将该节点加回集群
调试pod时,会依据节点可用计算资源和pod间关联关系,通过scheduler服务计算最佳目标节点
若希望特定的pod只运行在特定的节点上,可以使用节点标签和pod的nodeSelector属性实现
若使用了nodeSelector条件,但集群中没有包含相应标签的node时,pod最终会创建失败,即便有其他可供调度的node
# 为k8s-node2节点添加标签
kubectl label nodes k8s-node2 disk-type=ssd
# 查看标签
kubectl get node k8s-node2 -o yaml
1 2 3 4 5 6 7 8 9 10 11 |
# mongodb.pod.yaml apiVersion: v1 kind: Pod metadata: name: mongodb spec: containers: - name: mongodb image: mongo nodeSelector: disk-type: ssd |
kubectl create -f mongodb.pod.yaml
kubectl get pods -o wide
Kubernetes包管理工具Helm
使用kubectl和yaml文件无法向docker compose那样方便地查看和管理由特定的一组服务部署出来的服务集合,即服务编排
解决方案:Kompose 和 Helm
Kompose:https://github.com/kubernetes/kompose
Helm:https://github.com/helm/helm 官网:https://helm.sh/
Kompose将docker compose的yaml文件转换成一系列k8s资源描述文件,并未提供完整的编排管理功能
Helm能力不仅局限于服务编排管理,还提供发布版本管理、发布历史管理、指定版本回滚、服务发布仓库、参数化模板等功能
Helm是服务端/客户端架构。
服务端称为Tiller,是客户端与k8s交互的中介,并维护已部署到集群中的服务状态。Tiller没有数据库,使用k8s的ConfigMaps存储状态信息
客户端用于与用户交互,使用gRPC协议与Tiller通信,并与保存服务包文件的仓库通信
服务集合包称为Chart,客户端从仓库中获取Chart部署文件和相关配置,然后通过Tiller将Chart部署到集群
部署Helm
Helm客户端可在linux/mac/windows上运行,是一个单独的可执行文件
Helm下载:
https://storage.googleapis.com/kubernetes-helm/helm-v2.11.0-linux-amd64.tar.gz 【国内下载可能需要代理】
# 安装
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.11.0-linux-amd64.tar.gz
tar zxf helm-v2.11.0-linux-amd64.tar.gz.tar
cd linux-amd64/
cp helm /usr/local/bin/
# 初始化
helm init
Creating /root/.helm
Creating /root/.helm/repository
Creating /root/.helm/repository/cache
Creating /root/.helm/repository/local
Creating /root/.helm/plugins
Creating /root/.helm/starters
Creating /root/.helm/cache/archive
Creating /root/.helm/repository/repositories.yaml
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /root/.helm.
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.
Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run helm init
with the --tiller-tls-verify flag.
# 更新repo仓库
helm repo update
# 下载redis 0.10.2 的chart包 但不安装
helm fetch stable/redis --version 0.10.2 --untar
若k8s集群启用了RBAC功能,需要为helm创建一个专用帐号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
cat <<EOF >helm.yaml apiVersion: v1 kind: ServiceAccount metadata: name: helm namespace: kube-system --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: helm roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: helm namespace: kube-system EOF kubectl create -f helm.yaml helm init --service-account helm |
Tiller服务端会在集群中kube-system命名空间下运行
故障解决1:Failed to pull image "gcr.io/kubernetes-helm/tiller:v2.11.0"
原因:k8s安装tiller默认会拉取gcr.io/kubernetes-helm/tiller:v2.11.0 同时默认仓库是https://kubernetes-charts.storage.googleapis.com 这两个地址国内访问存在问题
解决:使用阿里的服务
helm init --upgrade -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.11.0 --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
故障解决2:执行helm version 显示E1017 19:40:21.108430 2661 portforward.go:331] an error occurred forwarding 40857 -> 44134: error forwarding port 44134 to pod , uid : unable to do port forwarding: socat not found.
原因:tiller镜像运行所在节点没有安装socat
解决:kubectl -n kube-system get pods -o wide|grep tiller 查看tiller所在节点
yum install socat [建议所有node节点都安装]
# 创建一个新的chart
helm create myapp
# 查看在存储库中可用的所有Helm charts
helm search
# 更新charts列表以获取最新版本
helm repo update
# 查看在群集上安装的Charts列表
helm list
helm使用
下载0.10.2的redis的chart包 不安装
helm fetch stable/redis --version 0.10.2 --untar
检测指定目录中文件是否符合chart的结构
helm lint redis
标准Chart目录结构:
README.md,项目描述文件 说明该Chart的用法和参数
Chart.yaml,主配置文件 包含Chart名称、版本和其他元数据信息
values.yaml,定义模板文件中变量的默认值
templates/,模板文件(k8s资源描述文件)
额外文件:
LICENSE,使用协议
requirements.yaml,存放当前chart依赖的其他chart的说明
charts/,当前chart依赖的其他chart
# 使用helm安装服务
helm install ./redis --name demo-redis
helm install redis-0.10.2.tgz
helm install stable/redis
helm install stable/mariadb --version 0.3.0
服务编排时,需要依据具体环境配置的内容会的默认值保存在values.yaml中,部署时可以使用--set参数修改其中的部分变量值
helm install --set resources.requests.memory=512Mi mariadb
或者将要覆写的变量保存为单独文件,部署时用-f加载
helm install -f prod_values.yaml mysql
列出所有部署的包
helm ls
# 部署到集群中的chart在helm中称为release,若部署时没有指定名称,会随机生成一个名称
查看某release详细信息
helm status demo-redis
升级包
helm upgrade demo-redis -f prod_values.yaml --set resources.requests.memory=1024Mi ./redis
# 升级实际上是用新的Pod替代旧的
chart实例是有部署版本的,从1开始,每次更新配置都会递增
查看更新历史
helm history demo-redis
通过版本号可以回滚到任意一个历史版本
helm rollback demo-redis 1
删除已部署的chart
helm delete demo-redis
# 注意 该操作实际上只是逻辑删除 将服务停止并标记为已删除 并未清除相关数据 helm ls -a/--all 可以查看包含已删除的实例 其历史记录也在
恢复一个已删除的chart
helm history demo-redis
helm rollback demo-redis 1
彻底删除chart实例
helm delete --purge demo-redis
自定义Chart
创建标准Chart模板目录结构
helm create demochart
demochart/
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ └── service.yaml
└── values.yaml
其中templates目录中自动创建的文件不是必需的
NOTES.txt,安装chart时自动显示的用户帮助文档 如使用和配置方法
deployment.yaml,创建Deployment的资源描述示例
service.yaml,创建Service的资源描述示例
ingress.yaml,创建Ingress资源的描述示例
_helpers.pl,定义一些可以在chart中引用的yaml内容片段
通常会直接删除templates目录里默认创建的文件,将真实的服务资源描述文件放进去,然后将需要复用或复杂的片段抽离到_helpers.tpl,再创建一个NOTES.txt,写入自己chart的基本使用说明
templates目录中的文件都是资源描述模板,可以嵌入 {{表达式}} 格式的变量数据和简单数据计算。可以使用模板占位符、内置对象、函数、控制语句、模板变量等特性。
模板占位符
引用一个在模板以外定义的数据
数据来源:values.yaml文件 执行helm install/update时,通过 -f 或 --set 设置的数据
用法:
{{ .Values.configmap.version }} 表示外部定义的,名为 configmap.version 的数据
内置对象
以 . 开头,由Helm提供数据
包含Chart实例信息的Release对象;包含Chart包信息的Chart对象;包含Helm和k8s系统信息的Capabilities对象;包含当前模板信息的Template对象等
用法:
{{ .Release.Name }} 表示当前Chart实例名字
函数
用于对数值内容进行加工,| 是管道操作符,函数大多来自于Go语言的Sprig库
用法:
{{ now | htmlDate }} 获取当前时间并调整显示格式
常用函数:
quote,为原始内容加上引号,并自动转义原有的引号字符
lower,转换为小写;upper,转换为大写
trim,去掉原始内容前后空白字符
nospace,去掉所有空格
substr,截取原内容中一段字符
randNumeric,产生指定长度随机数字串
randAscii,产生指定长度随机字符串
indent,将原始内容缩进指定空格数
replace,替换原始内容中特定字符串
控制语句
逻辑判断/循环控制
相等 eq ;不相等 ne ;比较大小 lt gt
specs: 2c8g
{{ else if eq .Values.server.type "mysql" }}
specs: 2c4g
{{ else }}
specs: 2c2g
{{ end }}
## with语句可以简化属性访问,如 .name 等于 .Values.server.name
{{- with .Values.server }}
name: {{ .name | upper | quote }}
port: {{ .port | default "5000" }}
{{- end }}
模板变量
有些控制流需要和变量配合使用,如
{{- range $key,$val := .Values.env_variables }}
{{ $key }}: {{ $var | quote }}
{{- end }}
移除空白
去掉生成模板中不必要的空格或空行
控制语句最后会变成一个空行
语法: {{- 和 -}} 注意 - 和 }} 之间没有空格
{{- ,移除当前占位字段左侧所有空白内容,包括换行符
-}} ,移除当前占位字段右侧所有空白内容,包括换行符
同时使用会使上行两行合并
引用模板段
对可以被复用的模板段,可以抽离到 _helpers.tpl 文件中,也可以放在模板文件里
使用 define语句定义,使用template引用
1 2 3 4 5 6 7 8 9 10 11 |
# 定义 {{- define "my_labels" }} labels: generator: helm date: {{ now | htmlDate }} {{- end }} # 调用 ... metadata: name: {{ .Release.Name }}-configmap {{- template "my_labels" }} |
Chart仓库
可以使用官方仓库,也能创建私有仓库
Chart仓库实际上只是普通HTTP文件服务器,使用单层目录结构,存放Chart压缩包和一个index.yaml文件。其中index.yaml是仓库索引文件
默认的local仓库要启用,需要执行helm serve 创建http服务
# 查看仓库列表
helm repo list
NAME URL
stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
local http://127.0.0.1:8879/charts
# 创建本地仓库
mkdir charts
helm serve --address 0.0.0.0:8879 --repo-path ./charts
# 打包
helm package redis
# 添加包到私有仓库
mv redis-0.10.2.tgz charts/
helm repo index ./charts/
# 更新客户端
helm repo update
helm search redis
# 添加其他仓库
helm repo add <名称> <仓库URL>
转载请注明:轻风博客 » 企业级容器集群构建(五)Kubernetes解决方案_健康/对外服务/多租户/节点管理/Helm