-linux-kubernetes基础使用
一 kubernates 带来的变革
1 对于开发人员
由于公司业务多,开发环境、测试环境、预生产环境和生产环境都是隔离的,而且除了生产环境,为了节省 成本,其他环境可能是没有日志收集的,在没有用 k8s 的时候,查看线下测试的日志,需要开发或者测试人员, 找到对应的机器,在找到对应的容器,然后才能查看日志,在用了 k8s 之后,开发和测试可以直接在 k8s 的 dashboard 到对应的 namespace,即可定位到业务的容器,然后可以直接通过控制台查看到对应的日志,大大降 低了操作时间。
把应用部署到 k8s 之后,代码的发布、回滚,以及蓝绿发布、金丝雀发布等都变得特别简单,不仅加快了业 务代码迭代的速度,而且全程无需人工干预。目前我们使用 jenkins、gitrunner 进行发版或者回滚等,从开发环 境到测试环境,到生产环境,完全遵守一次构建,多集群、多环境部署,通过不同的启动参数、不同的环境变量、 不同的配置文件实现区分不同的环境。目前已经实现 Python、Java、PHP、NodeJS、Go、.NET Core、Python 等多种语言的一键式发版、一键式回滚,大大提高了开发人员的开发效率。在使用服务网格后,开发人员在开发应用的过程中,不用再关心代码的网络部分,这些功能都被服务网格实 现,让开发人员可以只关心代码逻辑部分,即可实现网络部分的功能,比如:断流、分流、路由、负载均衡、限速和触发故障等功能。测试过程中,可能同时多套环境,当然也会需要再创建一套测试环境,之前测试环境的创建,需要找运维或者自行手工搭建。在迁移至 k8s 集群后,只需要在 jenkins 上点点鼠标即可在 k8s 集群上创建一套新的测试环境。
2 对于运维人员
如果你是一名运维人员,可能经常因为一些重复、繁琐的工作感觉厌倦。比如:这个需要一套新的测试环境,那个需要一套新的测试环境,之前可能需要装系统、装依赖环境、开通权限等等。而如今,可以直接用镜像直接部署一套新的测试环境,甚至全程无需自己干预,开发人员通过 jenkins 或者自动化运维平台即可一键式创建, 大大降低了运维成本。
一开始,公司业务故障,可能是因为基础环境不一致、依赖不一致、端口冲突等等问题,现在实现 Docker镜像部署,k8s 编排,所有的依赖、基础都是一样的,并且环境的自动化扩容、健康检查、容灾、恢复都是全自动的,大大减少了因为这类基础问题引发的故障。也有可能公司业务是由于服务器宕机、网络等问题,造成服务 不可用,此类情况均需要运维人员及时去修复,而如今,可能在你收到告警信息的时候,k8s 已经帮你恢复了。 在没有使用 k8s 时,业务应用的扩容和缩容,都需要人工去处理,从采购服务器、上架、到部署依赖环境,不仅需要大量的人力物力,而且非常容易在中间过程出现问题,又要花费大量的时间去查找问题。成功上架后,还需要在前端反代端添加或该服务器,而如今,可以利用 k8s 的弹性计算,一键式进行扩容和缩容,不仅大大提高了运维效率,而且还节省了不少的服务器资源,提高了资源利用率。
对于反代配置方面,比如可能你并不会,或者对 nginx 的配置规则并不熟悉,一些高级的功能你也不会实现,而如今,利用 k8s 的 ingress 即可简单的实现那些复杂的逻辑。并且也不会在遇到 nginx 少加一个斜杠和多加一个斜杠的问题。
对于负载均衡方面,之前负载均衡可能是 Nginx、LVS、HAProxy、F5 等,云上可能是云服务商提供的不在均衡机制。每次添加删除节点时,都需要手动去配置前端负载均衡,手动去匹配后端节点,而如今,使用 k8s 内部的 service 可以动态发现实现自动管理节点,并且支持自动扩容缩容。之前遇到高峰流量时,经常服务器性能不够,需要临时加服务器面对高峰流量,而如今对于高性能 k8s 集群加上 serverless,基本实现无需管理,自动扩容。
对于高可用方面,k8s 天生的高可用功能,彻底释放了双手,无需再去创建各类高可用工具、检测检查脚本。 k8s 支持进程接口级别的健康检查,如发现接口超时或者返回值不正确,会自动处理该问题。
对于中间件搭建方面,根据定义好的资源文件,可以实现秒级搭建各类中间件高可用集群,并且支持一键式扩缩容,如 Redis、RabbitMQ、Zookeeper 等,并且大大减少了出错的概率。
对于应用端口方面,传统行业中,一个服务器可能跑了很多进程,每个进程都有一个端口,需要人为的去配置端口,并且还需要考虑端口冲突的问题,如果有防火墙的话,还需要配置防火墙,在 k8s 中,端口统一管理, 统一配置,每个应用的端口都可设置成一样的,之后通过 service 进行负载均衡,大大降低了端口管理的复杂度和端口冲突。
无论是对于开发人员、测试人员还是运维人员,k8s 的诞生,不仅减少了工作的复杂性,还减少了各种成本。 上述带来的变革只是其中比较小的一部分,更多优点只有用了才能体会到。
3 kubernetes 带来的挑战
首先是对于 k8s 的学习本身就是很难的,概念太多,无从入手,可能学习了一个月也无法入门,甚至连集群 也搭建不出来,使人望而却步。并且 k8s 对运维的技术能力要求比较高,已经不仅仅局限于传统运维,有时候你 可能要修改业务代码等。并且需要掌握的知识也需要很多,你可能需要掌握公司所有使用到的代码,比如代码是 如何进行编译的、如何正确发布、如何修改代码配置文件等,这对于运维人员,也是一种挑战。Kubernetes 之所 以被叫做 k8s,业界有两种说法,通俗的说法是 k 和 s 之间有 8 个字母,另一种比较说法是 k8s 集群至少需要搭 建 8 遍才能搭建成功。当然,在实际使用时,可能不止 8 遍。k8s 的诞生,把运维从传统转变到了 DevOps 方向,需要面临的问题会更多,需要面临的新技术也有很多,但是当你掌握到了 k8s 的核心使用,就会受益终身。
二 dashbord部署
-1.在master节点执行 https://github.com/kubernetes/dashboard/blob/master/aio/deploy/recommended.yaml
# 将该yaml文件写入节点
[root@master01 ~]# vi deployment.yaml
# 需要更改相关image 的版本
image: registry.cn-hangzhou.aliyuncs.com/k8sos/dashboard:v2.0.5
imagePullPolicy: Always
image: registry.cn-hangzhou.aliyuncs.com/k8sos/metrics-scraper:v1.0.6
[root@master01 ~]# kubectl apply -f recommended.yaml
-2.查看端口
[root@master01 ~]# kubectl get pods -n kubernetes-dashboard
NAME READY STATUS RESTARTS AGE
dashboard-metrics-scraper-68b94dcfd8-bzch5 1/1 Running 0 73m
kubernetes-dashboard-5f6ffd8777-7x4qk 1/1 Running 0 73m
[root@master01 ~]# kubectl get svc -n kubernetes-dashboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dashboard-metrics-scraper ClusterIP 10.96.130.201 <none> 8000/TCP 7h50m
kubernetes-dashboard ClusterIP 10.106.244.253 <none> 443/TCP 7h50m
# 此时得到了kubernetes-dashboard的端口,但是由于k8s对其集群的安全性要求极高,无法从外部访问,所以要修改它的相关配置。
[root@master01 ~]# kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard
type: NodePort # 改成NodePort
-3.访问
输入:http://192.168.10.10:32230/
Client sent an HTTP request to an HTTPS server.
输入:https://192.168.10.10:32230/
点击高级在点击继续访问
-4.需要获取token令牌登录
[root@master01 ~]# vi token.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kube-system
# 部署该资源清单
[root@master01 ~]# kubectl apply -f token.yaml
serviceaccount/admin-user created
clusterrolebinding.rbac.authorization.k8s.io/admin-user created
# 获取登录token
[root@master01 ~]# kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
Name: admin-user-token-wcffx
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name: admin-user
kubernetes.io/service-account.uid: 4ab2a685-954d-448b-8327-0ad8ddd69d79
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1066 bytes
namespace: 11 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjdFcDBodjVYSUJoZTdJYUxxWEZhNDZNdjVwdl9mQWlxdmdGQVNrVlFadHMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXdjZmZ4Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI0YWIyYTY4NS05NTRkLTQ0OGItODMyNy0wYWQ4ZGRkNjlkNzkiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.JhD_sSAzSbyzc90o6U2abfCDMfGMSd7XLCfgLLQn7V33d50glLnk4PqMPghZW1rOfLRMfhNgbNSZFjkFEUtS8CyqBIJ4GwJ3MkKhQpU9MR_nFtcIvUaOvx1YwiyBMqviInzZmbQd8wng5bPl8XWj8wcWlEk_VNpxOGxmi-7XhkCBRMaO2x0ObFtMWzjn1UhtaF5S4gmhB8meui01kBIPnIJJ5G6Bx11ymYGvMlJW0KK4w6G0p07vue-oopZin5CdVf-8btKQTfITyBMVo-w3GuCGRqRiToUM8BwmD3gh3j0x3CELyJfZ0jBopEBkpfrFgtUyql3LuZljR9guxnmN0w
三 pod
K8s 有很多技术概念,同时对应很多 API 对象,最重要的也是最基础的是微服务 Pod。Pod 是在 K8s 集群中运 行部署应用或服务的最小单元,它是可以支持多容器的。Pod 的设计理念是支持多个容器在一个 Pod 中共享网络 地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。Pod 对多容器的支持是 K8s 最基础的设计理念。比如你运行一个操作系统发行版的软件仓库,一个 Nginx 容器用来发布软件,另一个容器专门用来从源仓库做同步,这两个容器的镜像不太可能是一个团队开发的,但是他们一块儿工作才能提供一个微服务;这种情况下,不同的团队各自开发构建自己的容器镜像,在部署的时候组合成一个微服务对外提供服务。这 就是 K8S 中的 POD。
Pod 是 K8s 集群中所有业务类型的基础,可以看作运行在 K8s 集群中的小机器人,不同类型的业务就需要不 同类型的小机器人去执行。目前 K8s 中的业务主要可以分为长期伺服型(long-running)、批处理型(batch)、 节点后台支撑型(node-daemon)和有状态应用型(stateful application);分别对应的小机器人控制器为 Deployment、Job、DaemonSet 和 StatefulSet。
1 Pod的初体验
-1.写一个资源清单
[root@master01 ~]# vi test.yaml
# k8s当中一切皆资源
# 定义创建的资源
kind: Pod
# 指定API版本号
apiVersion: v1
# 定义当前资源的基础信息
metadata:
# 定义名称(由字母和数字及中划线组成,必须小写同时必须以字母开头)
name: test-pod
# 配置资源详情
spec:
# 配置Pod当中包含的容器
containers:
- name: nginx
image: nginx:1.19.2
-2.执行
[root@master01 ~]# kubectl apply -f test.yaml
pod/test-pod created
[root@master01 ~]# kubectl get -f test.yaml
NAME READY STATUS RESTARTS AGE
test-pod 0/1 Pending 0 80s
# 还需要等待整个资源执行完毕 1/1
2 Pod带来的好处
- Pod作为一个可以独立运行的服务单元,简化了应用部署的难度,以更高的抽象层次为应用部署提供了极大的方便。
- Pod作为最小的使用实例可以独立运行,因此可以方便的进行部署、水平扩展和收缩、方便调度管理与资源的分配。
- Pod中的容器共享相同的数据和网络地址空间,Pod之间也进行了同意的资源管理与分配。
3 Pod是如何管理多个容器的
Pod中可以同时运行多个进行(作为容器运行)协同工作。。同一个 Pod 中的容器会自动的分配到同一个 node 上。同一个 Pod 中的容器共享资源、网络环境和依赖,所以它们总是被同时调度。在一个 Pod 中同时运行多个容 器是一种比较高级的用法。只有当你的容器需要紧密配合协作的时候才考虑用这种模式。
4 Pod中的数据持久性
Pod 在设计⽀持就不是作为持久化实体的。在调度失败、节点故障、缺少资源或者节点维护的状态下都会死 掉会被驱逐。通常,我们是需要借助类似于 Docker 存储卷这样的资源来做 Pod 的数据持久化的。
5 Pod的生命周期和重启策略
Pod 在整个生命周期过程中被系统定义为各种状态,熟悉 Pod 各种状态对于我 理解如何设置 Pod 的调度策 略、重启策略是很有必要的。
5.1 Pod的状态
状态栏 | 描述 |
---|---|
挂起(Pending) | API Server 创建了 pod 资源对象已存入 etcd 中,但它尚未被调度完成,或者仍处于从仓 库下载镜像的过程中。 |
运行中(Running) | Pod 已经被调度至某节点,并且所有容器都已经被 kubelet 创建完成。 |
成功(Succeeded) | Pod 中的所有容器都已经成功终止并且不会被重启。 |
失败(Failed) | Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。即容器以非 0 状态退出或者被系统禁止。 |
未知(Uknown) | Api Server 无法正常获取到 Pod 对象的状态信息,通常是由于无法与所在工作节点的 |
5.2 Pod重启策略
Pod 重启策略( RestartPolicy )应用于 Pod 内的所有容器,井且仅在 Pod 所处的 Node 上由 kubelet 进行判断和重启操作。当某个容器异常退出或者健康检查失败时, kubelet 将根据 RestartPolicy 设置来进行相应的操作。Pod 的重启策略包括:Always、OnFailure 和 Never,默认值为 Always 。
-
Always:当容器失效时,由 kubelet 自动重启该容器。
-
OnFailure:当容器终止运行且退出码不为 0 时,由 kubelet 自动重启该容器。
-
Never:不论容器运行状态如何,kubelet 都不会重启该容器。
kubelet 重启失效容器的时间间隔以 sync-frequency 乘以 2n 来计算;例如 1、2、4、8 倍等,最长延时 5min ,并且在成功重启后的 10 min 后重置该时间。
Pod 的重启策略与控制方式息息相关,当前可用于管理 Pod 的控制器包括 ReplicationController、Job、DaemonSet 及直接通过 kubelet 管理(静态 Pod)。每种控制器对 Pod 的重启策略要求如下:
- RC 和 DaemonSet:必须设置为 Always,需要保证该容器持续运行。
- Job 和 CronJob:OnFailure 或 Never,确保容器执行完成后不再重启。
- kubelet:在 Pod 失效时自动重启它,不论将 RestartPolicy 设置为什么值,也不会对 Pod 进行健康检查。
6 Pod的资源清单详解
apiVersion: v1 # 必选,API 的版本号
kind: Pod # 必选,类型 Pod
metadata: # 必选,元数据
name: nginx # 必选,符合 RFC 1035 规范的 Pod 名称
namespace: web-testing # 可选,不指定默认为 default,Pod 所在的命名空间
labels: # 可选,标签选择器,一般用于 Selector
- app: nginx
annotations: # 可选,注释列表
- app: nginx
spec: # 必选,用于定义容器的详细信息
containers: # 必选,容器列表
- name: nginx # 必选,符合 RFC 1035 规范的容器名称
image: nginx:v1 # 必选,容器所用的镜像的地址
imagePullPolicy: Always # 可选,镜像拉取策略
workingDir: /usr/share/nginx/html # 可选,容器的工作目录
volumeMounts: # 可选,存储卷配置
- name: webroot # 存储卷名称
mountPath: /usr/share/nginx/html # 挂载目录
readOnly: true # 只读
ports: # 可选,容器需要暴露的端口号列表
- name: http # 端口名称
containerPort: 80 # 端口号
protocol: TCP # 端口协议,默认 TCP
env: # 可选,环境变量配置
- name: TZ # 变量名
value: Asia/Shanghai
- name: LANG
value: en_US.utf8
resources: # 可选,资源限制和资源请求限制
limits: # 最大限制设置
cpu: 1000m
memory: 1024MiB
requests: # 启动所需的资源
cpu: 100m
memory: 512MiB
readinessProbe: # 可选,容器状态检查
httpGet: # 检测方式
path: / # 检查路径
port: 80 # 监控端口
timeoutSeconds: 2 # 超时时间
initialDelaySeconds: 60 # 初始化时间
livenessProbe: # 可选,监控状态检查
exec: # 检测方式
command:
- cat
- /health
httpGet: # 检测方式
path: /_health
port: 8080
httpHeaders:
- name: end-user
value: jason
tcpSocket: # 检测方式
port: 80
initialDelaySeconds: 60 # 初始化时间
timeoutSeconds: 2 # 超时时间
periodSeconds: 5 # 检测间隔
successThreshold: 2 # 检查成功为 2 次表示就绪
failureThreshold: 1 # 检测失败 1 次表示未就绪
securityContext: # 可选,限制容器不可信的行为
provoleged: false
restartPolicy: Always # 可选,默认为 Always
nodeSelector: # 可选,指定 Node 节点
region: subnet7
imagePullSecrets: # 可选,拉取镜像使用的 secret
- name: default-dockercfg-86258
hostNetwork: false # 可选,是否为主机模式,如是,会占用主机端口
volumes: # 共享存储卷列表
- name: webroot # 名称,与上述对应
emptyDir: {} # 共享卷类型,空
hostPath: # 共享卷类型,本机目录
path: /etc/hosts
secret: # 共享卷类型,secret 模式,一般用于密码
secretName: default-token-tf2jp # 名称
defaultMode: 420 # 权限
configMap: # 一般用于配置文件
name: nginx-conf
defaultMode: 420
四 Label
Label 是 Kubernetes 系统中另外一个核心概念。一个 Label 是一个 key=value 的键值对,其中 key 与 vaue 由用户自己指定。Label 可以附加到各种资源对象上,例如 Node、Pod、Service、RC 等,一个资源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源对象上去,Label 通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除。
我们可以通过指定的资源对象捆绑一个或多个不同的 Label 来实现多维度的资源分组管理功能,以便于灵活、方便地进行资源分配、调度、配置、部署等管理工作。例如:部署不同版本的应用到不同的环境中;或者监控和分析应用(日志记录、监控、告警)等。一些常用等 label 示例如下。
-
版本标签:"release" : "stable" , "release" : "canary"
-
环境标签:"environment" : "dev" , "environment" : "production"
-
架构标签:"tier" : "frontend" , "tier" : "backend" , "tier" : "middleware"
-
分区标签:"partition" : "customerA" , "partition" : "customerB"
-
质量管控标签:"track" : "daily" , "track" : "weekly"
Label 相当于我们熟悉的“标签”,給某个资源对象定义一个 Label,就相当于給它打了一个标签,随后可以通过 Label Selector(标签选择器)查询和筛选拥有某些 Label 的资源对象,Kubernetes 通过这种方式实现了类似SQL 的简单又通用的对象查询机制。
[root@master01 network-scripts]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
test-deployment-674d9c85d4-6rwvb 1/1 Running 0 13h app=test-deployment,env=test,pod-template-hash=674d9c85d4
1 根据标签来查询pod
[root@master01 network-scripts]# kubectl get pods -l pod-template-hash=674d9c85d4
NAME READY STATUS RESTARTS AGE
test-deployment-674d9c85d4-6rwvb 1/1 Running 0 13h
五 service 资源
service 是 k8s 中的一个重要概念,主要是提供负载均衡和服务自动发现。它是 k8s 中最核心的资源之一,每 一个 Service 就是我们平常所说的一个“微服务”。在非 k8s 世界中,管理员可以通过在配置文件中指定 IP 地址 或主机名,容许客户端访问,但在 k8s 中这种方式是行不通的。因为 Pod 是有生命周期的,它们可以被创建或销毁。虽然通过控制器能够动态地创建 Pod,但当 Pod 被分配到某个节点时,K8s 都会为其分配一个 IP 地址,而 该 IP 地址不总是稳定可依赖的。因此,在 Kubernetes 集群中,如果一组 Pod(称为 backend)为其它 Pod (称 为 frontend)提供服务,那么那些 frontend 该如何发现,并连接到这组 backend 的 Pod 呢?
[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d23h
test-service ClusterIP 10.96.228.253 <none> 80/TCP 85s
[root@master01 ~]# curl 10.244.1.9
# 在集群中可以访问
# 在外不能访问,浏览器上输入不能访问
# 需要制定向外暴露的端口
1 定义 Service
kind: Service
apiVersion: v1
metadata:
name: test-service
namespace: default
labels:
app: test-service
spec:
type: ClusterIP # 此定义则是在集群内部访问
selector:
app: test-service
ports:
- port: 80
targetPort: 80 # 目标端口号
2 Service 类型
Service 是 Kubernetes 对外访问的窗口,针对不同的场景,kubernetes 为我们设置了四种 Service 的类型。
2.1 ClusterIP
kubernetes 默认就是这种方式,是集群内部访问的方式,外部是无法访问的。其主要用于为集群内 Pod 访问时,提供的固定访问地址,默认是自动分配地址,可使用 ClusterIP 关键字指定固定 IP。
[root@kubernetes-master-01 test]# vi ser.yaml
# 写入上部分yaml文件
[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d23h
test-service ClusterIP 10.96.228.253 <none> 80/TCP 85s
[root@master01 ~]# curl 10.96.228.253
# 可在集群内访问
[root@master01 ~]# kubectl describe service test-service
Name: test-service
Namespace: default
Labels: app=test-service
Annotations: <none>
Selector: app=test-service
Type: ClusterIP
IP: 10.96.228.253
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>
2.2 NodePort
NodePort 是将主机 IP 和端口跟 kubernetes 集群所需要暴露的 IP 和端口进行关联,方便其对外提供服务。内 部可以通过 ClusterIP 进行访问,外部用户可以通过 NodeIP:NodePort 的方式单独访问每个 Node 上的实例。
[root@master01 ~]# vi nodeport.yaml
# 定义资源类型
kind: Deployment
# 定义api版本号
apiVersion: apps/v1
# 定义基础信息
metadata:
name: test-django
# 定义容器信息
spec:
# 最少运行数量
replicas: 1
# 定义pod模板
template:
metadata:
labels:
app: test-django
env: test
spec:
containers:
# 容器名字
- name: django
# 容器的镜像源
image: registry.cn-hangzhou.aliyuncs.com/k8s2me/django:v1
# 定义选择器
selector:
# 精确匹配
matchLabels:
app: test-django
---
# Service 是 Kubernetes 对外访问的窗口,针对不同的场景,kubernetes 为我们设置了四种 Service 的类型
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: default
labels:
app: nginx
spec:
# NodePort 是将主机 IP 和端口跟 kubernetes 集群所需要暴露的 IP 和端口进行关联,方便其对外提供服务。内
#部可以通过 ClusterIP 进行访问,外部用户可以通过 NodeIP:NodePort 的方式单独访问每个 Node 上的实例。
type: NodePort
selector:
app: test-django
ports:
- port: 80
# 监听的端口
targetPort: 80
# node暴露给外面的端口
nodePort: 30080
# 上述配置创建一个名称为 "my-service" 的 Service 对象,它会将请求代理到使用 TCP 端口 30080,
# 并且具有标签 "test-django" 的 Pod 上。
# Kubernetes 为该服务分配一个 IP 地址(有时称为 "集群IP"),该 IP 地址由服务代理使用。
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-django-6d96c9ddf4-x74z5 1/1 Running 0 16m 10.244.1.13 node1 <none> <none>
[root@master01 ~]# kubectl apply -f nodeport.yaml
service/my-svc created
[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d
my-svc NodePort 10.109.34.201 <none> 80:30080/TCP 7s
[root@master01 ~]# kubectl describe service my-service
Name: my-service
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=test-django
Type: NodePort
IP: 10.107.222.87
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30080/TCP
Endpoints: <none>
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
# 试验是否能连接
[root@master01 ~]# curl 192.168.10.10:30080
"主机名:test-django-6d96c9ddf4-x74z5, 版本:V1"[root@master01 ~]#
# 主机名不是pod名,而是被分配到子节点的容器名
# 由此由servier资源向外定义的端口30080映射到了集群内部的端口80,可以在外通过端口30080访问。
六 deployment 资源
Deployment 为 Pod 提供声明式更新。在 Deployment 对象中描述所需的状态,然后 Deployment 控制器实 际状态以受控的速率更改为所需的状态。您可以定义部署以创建新的副本集,或删除现有部署并在新部署中采用 其所有资源。一句话:Deployment 主要功能是保证有足够的 Pod 正常对外提供服务。
# 定义资源类型
kind: Deployment
# 定义api版本号
apiVersion: apps/v1
# 定义基础信息
metadata:
name: test-deployment
# 定义容器信息
spec:
# 定义pod的模板
template:
metadata:
labels:
app: test-deployment
env: test
spec:
containers:
- name: nginx
image: nginx:1.19.2
# 定义选择器
selector:
# 精确匹配
matchLabels:
app: test-deployment
env: test
1 执行创建命令
[root@master01 ~]# vi demployment.yaml
[root@master01 ~]# kubectl apply -f demployment.yaml
deployment.apps/test-deployment created
[root@master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-django-6d96c9ddf4-x74z5 1/1 Running 0 10m
2 注意
将 kubectl 标志设置--record 为 true 允许您将当前命令记录在正在创建或更新的资源的注释中。这对于将来 的检查很有用。
[root@master01 ~]# kubectl apply -f deployment.yaml --record deployment.apps/nginx-deployment configured
3 查看部署状态
[root@master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-django-6d96c9ddf4-x74z5 1/1 Running 0 10m
# 能看到部署在那个子节点上运行以及集群内的ip地址
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-deployment-674d9c85d4-6rwvb 1/1 Running 0 5m41s 10.244.1.14 node1 <none> <none>
4 查看部署详情
[root@master01 ~]# kubectl describe deployment test-deployment [资源类型] [资源名字]
kubectl describe deployment test-deployment
Name: test-deployment
Namespace: default
CreationTimestamp: Wed, 09 Dec 2020 11:57:32 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=test-deployment,env=test
Replicas: 1 desired | 1 updated | 1 total | 0 available | 1 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=test-deployment
env=test
Containers:
nginx:
Image: nginx:1.19.2
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Progressing True NewReplicaSetAvailable
Available False MinimumReplicasUnavailable
OldReplicaSets: <none>
NewReplicaSet: test-deployment-674d9c85d4 (1/1 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 23m deployment-controller Scaled up replica set test-deployment-674d9c85d4 to 1
5 更新
一般对应用程序升级或者版本迭代时,会通过Deployment对Pod进行回滚更新。
5.1 设置
[root@master01 ~]# kubectl set image deployment test-deployment nginx=nginx:1.18.0
deployment.apps/test-deployment image updated
# 查看更新状态
[root@master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-deployment-674d9c85d4-6rwvb 1/1 Terminating 0 36m
test-deployment-674d9c85d4-7x4qk 0/1 Error 1 10m
test-deployment-6f4888c79c-b9s6w 0/1 Pending 0 83s
test-django-6d96c9ddf4-9dmg6 0/1 ContainerCreating 0 10m
test-django-6d96c9ddf4-x74z5 1/1 Terminating 0 47m
5.2 更新、修改
第一种:通过命令行指定更新
格式:kubectl set image [资源类型(控制器资源)] [资源名称] [容器名称]=[新镜像]
kubectl set image deployment test-deployment django=redis
第二种:通过YAML文件进行修改
第三种:修改资源
kubectl edit [资源类型] [资源名称]
第四种:通过打补丁的方式
kubectl patch [资源类型] [资源名称] -p [配置项]
kubectl patch deployments.apps test-deployment -p '{"spec": {"replicas": 5}}'
七 pod、service、deployment、label具体联系
7.1 Pod
在Docker Swarm中,调度的最小单位是容器,而在K8S中,调度的最小是Pod,那啥是Pod呢?
Pod是K8S设计的一个全新的概念,在英文中的原意是表达一群鲸鱼或者是一个豌豆荚的意思。换句话说,一个Pod中可以运行一个或者多个容器。
在一个集群中,K8S会为每个Pod都分配一个集群内唯一的IP地址。因为K8S要求底层网络支持集群内的任意节点之间的两个Pod能够直接通信。这些容器共享当前Pod的文件系统和网络。而这些容器之所以能够共享,是因为Pod中有一个叫Pause的根容器,其余的用户业务容器都是共享这个根容器的IP和Volume。所以这些容器之间都可以通过localhost进行通信。
有人可能会问,为什么要引入根容器这个概念?那是因为如果没有根容器的话,当一个Pod中引入了多个容器的时候,我们应该用哪一个容器的状态来判断Pod的状态呢?所以才要引入与业务无关且不容易挂掉的Pause容器作为根容器,用根容器的状态来代表整个容器的状态。
如果在Pod中容器出现异常终止了是不会重启,在实际使用场景下基本不会直接使用Pod而是使用Deployment部署自己的应用。
熟悉Spring Cloud或者微服务的都知道,微服务中最忌讳的就是出现单点的情况。
所以针对同一个服务我们一般会部署2个或者更多个实例。在K8S中,则是会部署多个Pod副本,组成一个Pod集群来对外提供服务。而这些的具体实施就需要Deployment
而我们前面提过,K8S会为每一个Pod提供一个唯一的IP地址,客户端就需要通过每个Pod的唯一IP+容器端口来访问到具体的Pod,这样一来,如果客户端把调用地址写死,服务器就没有办法做负载均衡,而且,Pod重启之后IP地址是会变的,难道每次重启都要通知客户端IP变更吗?
为了解决这个问题,就要引出Service的概念了。
7.2 Deployment
Deployment是一个定义及管理多副本应用(即多个副本 Pod)的新一代对象,与Replication Controller相比,它提供了更加完善的功能,使用起来更加简单方便。
如果Pod出现故障,对应的服务也会挂掉,所以Kubernetes提供了一个Deployment的概念 ,目的是让Kubernetes去管理一组Pod的副本,也就是副本集 ,这样就能够保证一定数量的副本一直可用,不会因为某一个Pod挂掉导致整个服务挂掉。
Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。
这样使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 k8s 中,叫作"控制器"模式(controller pattern)。Deployment 扮演的正是 Pod 的控制器的角色。
7.3 Service
Service是K8S中最核心的资源对象之一,就是用于解决上面提到的问题。我个人认为与Swarm中的Service概念没有太大的区别。
一旦Service被创建,K8S会为其分配一个集群内唯一的IP,叫做ClusterIP,而且在Service的整个生命周期中,ClusterIP不会发生变更,这样一来,就可以用与Docker Swarm类似的操作,建立一个ClusterIP到服务名的DNS域名映射即可。
值得注意的是,ClusterIP是一个虚拟的IP地址,无法被Ping,仅仅只限于在K8S的集群内使用。
而Service对客户端,屏蔽了底层Pod的寻址的过程。并且由kube-proxy进程将对Service的请求转发到具体的Pod上,具体到哪一个,由具体的调度算法决定。这样以来,就实现了负载均衡。
而Service是怎么找到Pod的呢?这就需要继续引入另外一个核心概念Label了。
7.4 Label
Lable本质上是一个键值对,具体的值由用户决定。Lable就是标签,可以打在Pod上,也可以打到Service上。总结来说,Label与被标记的资源是一个一对多的关系。
例如,我们给上面所描述的Pod打上了role=serviceA
的标签,那么只需要在Service中的Label Selector中加入刚刚那个标签,这样一来,Service就可以通过Label Selector找到打了同一Label的Pod副本集了。