Preface
K8S(Kubernetes) 现在来说也不是什么新鲜的词了, 或许小公司玩得比较少, 自己也一直没有实操的机会. 幸会近来公司也开始使用 K8S, 有机会将学习过的东西在生产上实践一遍.
Kubernetes 常用资源
通过 kubectl api-resources
命令可以列举出所有的资源并且可以看到它们的简写.
常用的资源有:
Pod
(po): 最小的调度单位, 一般一个Pod
对应一个或两个(Sidecar 模式) Container. 一般不直接管理, 而是通过下面的Deployment
管理.ReplicationController
(rc) /ReplicationSet
(rs): 用来部署, 升级 Pod.Deployment
(deploy): 可以看做升级版的 RC, 在 RC 级基础上增加了事件和状态查看, 回滚, 版本记录, 暂停和启动等功能.DaemonSet
(ds):DaemonSet
会在每个 Kubernetes 节点都启动一个Pod
, 可以理解为节点的守护进程, 适用于日志收集, 监控 Agent 等.StatefulSet
(sts):StatefulSet
表示有状态的服务, Pod 的名称的形式为<statefulset name>-<ordinal index>
, 可参考 Nacos 的部署.Service
(svc): 提供一组Pod
的访问, 服务间的访问一般就是通过Service
来实现的, 类型有4种ClusterIP
(默认): 仅仅使用一个集群内部的地址, 这也是默认值, 使用该类型, 意味着, Service 只能在集群内部被访问.clusterIP
参数设置为None
就是一个 Headless Service 了.NodePort
: 在集群内部的每个节点上, 都开放这个服务. 可以在任意的 NodePort 地址上访问到这个服务LoadBalancer
: 这是当 Kubernetes 部署到公有云上时才会使用到的选项, 是向云提供商申请一个负载均衡器, 将流量转发到已经以 NodePort 形式开放的 Service 上.ExternalName
:ExternalName
实际上是将 Service 导向一个外部的服务, 这个外部的服务有自己的域名, 只是在 Kubernetes 内部为其创建一个内部域名, 并 cname 至这个外部的域名.
Ingress
(ing): 对集群中服务的外部访问进行管理, 典型的访问方式是 HTTP, 可以提供负载均衡, SSL 终结和基于名称的虚拟托管.ConfigMap
(cm) /Secret
: 配置存储, 后者提供加密功能.Job
/CronJob
(cj): 任务, 前者为执行一次, 后者加上了时间调度.PersistentVolume
(pv) /PersistentVolumeClaim
(pvc): pv 是对共享存储的一种抽象, pvc 则是对 pv 的一种消耗.StrorgeClass
(sc): 动态创建 pv.HorizontalPodAutoscaler
(hpa): 自动横向扩容.Endpoints
: 配合Service -> NodePort
可用于映射外部服务.ServiceAccount
(sa) /ClusterRole
/ClusterRoleBinding
: RBAC 权限相关资源
服务发现
Service 默认生成 ClusterIP(VIP) 来访问 Pod, 实际应用中通过写死 ClusterIP 显然不科学, 所以 Kubernetes 提供了 DNS 服务插件, 使得我们可以通过 Service Name 来发现 ClusterIP.
域名格式:
- 普通的 Service: 会生成
serviceName.namespace.svc.cluster.local
的域名, 会解析到 Service 对应的 ClusterIP 上, 在 Pod 之间的调用可以简写成serviceName.namespace
, 如果处于同一个命名空间下面, 甚至可以只写成serviceName
即可访问. - Headless Service: 无头服务, 就是把 ClusterIP 设置为 None 的, 会被解析为指定 Pod 的 IP 列表, 同样还可以通过
podname.servicename.namespace.svc.cluster.local
访问到具体的某一个 Pod.
服务映射
外部服务映射到内部
如果外部服务是个域名, 可以直接通过 Service -> ExternalName
实现:
1 | apiVersion: v1 |
如果是 IP, 推荐使用 EndPoint:
1 | apiVersion: v1 |
Service 不需要指定 selector
暴露服务
暴露服务有多种方式: NodePort
, Ingress
, LoadBalance
, port-forward
等, 这里介绍前两种
NodePort 方式
NodePort 顾名思义, 就是占用了节点端口暴露内部服务, 优点就是少量服务时配置简单. 缺点也很明显, 大量服务时端口不好管理, 而且节点可能也没有那么多端口使用.
1 | apiVersion: v1 |
port
指服务端口,targetPort
指 Pod 端口,nodePort
节点端口.
Ingress
推荐使用这种方式暴露服务, Ingress 其实就是从 kuberenets 集群外部访问集群的一个入口, 将外部的请求转发到集群内不同的 Service 上, 跟 Nginx 反向代理类似.
使用 Ingress 前需要部署 Ingress Controller, 实现有 Ingress NGINX / F5 BIG-IP Controller / Ingress Kong / Traefik / Voyager 等, 部署过程省略… 部署了 Ingress Controller 后, 就可以开始使用 Ingress 了:
1 | apiVersion: extensions/v1beta1 |
调度选择
更多参考官方文档: https://kubernetes.io/zh/docs/concepts/scheduling-eviction/
nodeSelector
将 Pod 部署到 label 中包含 {KEY}={VALUE}
的 node, 如果不满足, 则 Pod 会一直处于 Pending 状态.
1 | apiVersion: v1 |
亲和性和反亲和性调度
亲和性和反亲和性调度都有硬策略以及软策略.
软策略和硬策略的区分是有用处的, 硬策略适用于 pod 必须运行在某种节点, 否则会出现问题的情况, 比如集群中节点的架构不同, 而运行的服务必须依赖某种架构提供的功能; 软策略不同, 它适用于满不满足条件都能工作, 但是满足条件更好的情况, 比如服务最好运行在某个区域, 减少网络传输等. 这种区分是用户的具体需求决定的, 并没有绝对的技术依赖.
Node Affinity
硬策略:
requiredDuringSchedulingIgnoredDuringExecution
requiredDuringSchedulingRequiredDuringExecution
软策略:
preferredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingRequiredDuringExecution
其中 IgnoredDuringExecution
表示如果节点标签发生了变化, 不再满足pod指定的条件, pod也会继续运行, 而 RequiredDuringExecution
表示如果节点标签发生了变化, 不再满足pod指定的条件, 则重新选择符合要求的节点.
1 | apiVersion: v1 |
Pod Affinity
与上面的 Node Affinity 类似, 只不过选择 pod 的纬度是 pod 之间的关系. 比如需要将3个 Nacos 实例部署到3个不同的节点, 也就是节点中有一个 Nacos pod, 就不分配到这个节点了.
和 node affinity 相似, pod affinity 也有 requiredDuringSchedulingIgnoredDuringExecution
和 preferredDuringSchedulingIgnoredDuringExecution
.
亲和性调度:
1 | apiVersion: v1 |
反亲和性调度:
1 | apiVersion: v1 |
污点和容忍(Taints和Tolerations)
与 NodeAffinity 相反, Taint 让 Node 拒绝 Pod 的运行. Taint 需要与 Toleration 配合使用, 让 Pod 避开那些不合适的 Node.
想设置标签一样, 需要先设置 taint:
1 | kubectl taint node [node] key=value[effect] |
在 Node上设置一个或多个 Taint 后, 除非 Pod 明确声明能够容忍这些”污点”, 否则无法在这些 Node 上运行.
Toleration 设置为可以容忍具有该 Taint 的 Node, 使得 Pod 能够被调度到 node1 上:
1 | apiVersion: v1 |
ConfigMap & Secret
通常一个应用多多少少都会有一些配置, 而对于不同环境的配置是不一样的, 这时候就需要将配置与应用隔离.
Kubernetes 提供了 ConfigMap 以及 Secret 这样的方案提供给我们. Secret 相当于加密版的 ConfigMap, 存储的是 Base64 编码后的值.
创建
推荐使用 yaml 文件创建 ConfigMap:
1 | kind: ConfigMap |
Secret:
1 | apiVersion: v1 |
username 与 password 是经过 Base64 处理后的字符串
使用
通过环境变量方式:
1 | apiVersion: v1 |
通过挂载方式(ConfigMap 中的 key 为文件名, value 为文件内容):
1 | apiVersion: v1 |
也可以指定挂载某一个 key:
1 | apiVersion: v1 |
Secret 用法类似, 只需要将 configMapKeyRef
替换成 secretKeyRef
.
1 | apiVersion: v1 |
差异与注意点
相同点:
- key/value 的形式
- 属于某个特定的 namespace
- 可以导出到环境变量
- 可以通过目录/文件形式挂载
- 通过 volume 挂载的配置信息均可热更新
不同点:
- Secret 可以被 ServerAccount 关联
- Secret 可以存储 docker register 的鉴权信息, 用在 ImagePullSecret 参数中, 用于拉取私有仓库的镜像
- Secret 支持 Base64 加密
- Secret 分为
kubernetes.io/service-account-token
,kubernetes.io/dockerconfigjson
,Opaque
三种类型而 Configmap 不区分类型
ConfigMap需要注意:
- ConfigMap 必须在 Pod 之前创建
- 只有与当前 ConfigMap 在同一个 namespace 内的 pod 才能使用这个 ConfigMap, 换句话说, ConfigMap 不能跨命名空间调用
数据挂载
Kubernetes 提供了众多 Volume 类型, 包括 emptyDir
/ hostPath
/ nfs
/ glusterfs
/ cephfs
/ ceph rbd
等。具体可以参考官方文档, 这里列举常用的几种.
emptyDir
这是一个临时文件夹, Pod 销毁就删除:
1 | apiVersion: v1 |
hostPath
在 Pod 运行节点创建的文件夹, 缺点是, Pod 是动态的, 而 hostPath
是静态的(跟着 node 走):
1 | apiVersion: v1 |
PV & PVC
PV: 是对共享存储资源的一种抽象, 实现方式常见的有 Ceph
, GlusterFS
, NFS
等.
1 | apiVersion: v1 |
PVC: 则是用户存储的一种消耗 PV 的声明, 用户不需要关心具体的存储实现, 只需要直接使用 PVC 就行.
1 | apiVersion: v1 |
StorageClass
StorageClass
可以自动帮我们创建 PV, 我们只需要声明 PVC. 创建的 PV 以 ${namespace}-${pvcName}-${pvName}
的形式存在 NFS 的共享目录中.
要使用 StorageClass
, 我们需要先创建自动配置程序, 又叫 Provisioner
, 怎么创建可以参考 https://github.com/kubernetes-retired/external-storage/tree/master/nfs-client.
创建 StorageClass
:
1 | apiVersion: storage.k8s.io/v1 |
使用 StorageClass
:
1 | apiVersion: v1 |
或者通过 Pod PVC 模板自动创建 PVC:
1 | apiVersion: apps/v1 |
Kubernetes 常用命令
查看资源以及资源简写:
1 | kubectl api-resources |
应用资源:
1 | kubectl apply -f <RESOURCE_YAML> |
删除资源:
1 | kubectl delete -f <RESOURCE_YAML> |
资源列表:
1 | kubectl -n <NANESPACE> get <API_RESOURCES> |
需要查看全部命名空间的资源, 将
-n <NANESPACE>
换成最后追加--all-namespaces
.输出更多信息(ip/节点名等)追加参数
-o wide
- 输出标签信息追加 参数
--show-labels
输出资源 yaml:
1 | kubectl -n <NANESPACE> get <API_RESOURCES> <RESOURCE_NAME> -o yaml |
输出 json 格式只需要将 yaml 改成 json 即可
查看资源详情:
1 | kubectl -n <NANESPACE> describe <API_RESOURCE> <RESOURCE_NAME> |
滚动更新相关:
1 | # 查看滚动更新状态 |
弹性伸缩:
1 | kubectl autoscale deployment <DEPLOYMENT_NAME> --cpu-percent=50 --min=1 --max=10 |
YAML 声明示例
Pod
1 | apiVersion: v1 |
Deloyment
1 | apiVersion: apps/v1 #指定api版本, 此值必须在kubectl apiversion中 |
Service
1 | apiVersion: v1 |
HPA
1 | apiVersion: autoscaling/v1 |
Daemonset
template
语法与 Deployment
类似, 不同在于滚动升级策略, .spec.updateStrategy.type
指定:
OnDelete
: 更新配置后, 只有手动删除 DaemonSet Pod 才会创建新的.RollingUpdate
: 更新后自动删除并创建 Pod.
其他设置:
.spec.updateStrategy.rollingUpdate.maxUnavailable
: 默认值为1..spec.minReadySeconds
: 默认为0, 用于指定认为 DaemoSet Pod 启动可用所需的最小的秒数.
1 | apiVersion: extensions/v1beta1 |
Kubernetes 学习环境
- Minikube
- k3s
- Kind
- Docker Desktop Kubernetes
Helm
Helm 安装
下载最新 release, 这里以 3.3.4 为例:
1 | tar -zxvf helm-v3.3.4-linux-amd64.tar.gz |
安装私有仓库 Chartmuseum
如果创建的 Kubernetes 集群子节点没有访问外网能力, 那么可以在其他服务器 pull 镜像再 tag 成并推送到镜像服务, 比如下面的
registry-vpc.cn-zhangjiakou.aliyuncs.com/yangbingdong/chartmuseum:latest
先创建仓库目录并赋权, 否则上传 charts 会报 permission denied
:
1 | mkdir -p /data/charts && cd /data/ && chmod 777 charts |
deployment.yaml
:
1 | apiVersion: apps/v1 |
service.yaml
:
1 | apiVersion: v1 |
ingress.yaml
:
1 | apiVersion: extensions/v1beta1 |
修改 /etc/hosts
, 解析域名:
1 | ${Kubernetes Ingress 外网ip} charts.yangbingdong.com |
重启网络:
1 | /etc/init.d/network restart |
验证:
1 | curl charts.yangbingdong.com |
简单验证以及使用
创建 charts
1 | helm create hello-helm |
初始化的目录结构:
1 | ├── charts |
校验 chart
1 | helm lint ./hello-helm |
输出:
1 | ==> Linting ./hello-helm |
查看模板生成的 yaml
1 | helm template -f global.yaml ./hello-helm |
-f
指定自定义 kv 配置, 也可以通过--set k=v
指定
安装 charts
1 | helm install ./hello-helm --generate-name |
查看已安装的 chart
1 | helm list |
卸载 chart
需要拿到上面 helm list
中的 name
1 | helm unstall ${name} |
打包 chart
1 | helm package ./hello-helm |
上传到 Chartmuseum 并查看
1 | curl --data-binary "@hello-helm-0.1.0.tgz" http://charts.yangbingdong.com/api/charts |
语法参考:
实战
实战项目: spring-security-react-ant-design-polls-app
其他
别名
https://github.com/ahmetb/kubectl-aliases
Context 以及 Namespaces 切换工具
https://github.com/ahmetb/kubectx