学习日志 211220 前情提要 配置 有状态服务1
# K8s win10下minikube环境搭建 总结
- 安装hyper-v
- 安装minikube
- 指定hyper-v
- 指定新增的外部网络虚拟交换机
- 安装docker客户端
- 在K8s上安装私有docker仓库
- 生成证书
- 共享目录
- 开发示例Java应用 并部署到K8s上
- Docker化
- K8s描述文件
## 思路
- 因为要用微服务 所以要用K8s
- 因为要学K8s 所以要在windows上搭建学习环境
- 所以需要minikube
- 所以需要Hyper-v
- 因为K8s遵守docker image标准
- 所以java 应用需要docker化
- 所以需要Docker仓库
- 因为不想用商用仓库, 所以自己搭一个Docker仓库
# Hyper-V
- 安装 不再重复
# minikube安装
- 环境变量 MINIKUBE_HOME
- 在Hyper-V上建 虚拟交换机, 选外部网络, 命名为minikube
- 名字可以自取, 下面指令中自己替换掉
- minikube start
- `minikube start --vm-driver hyperv --hyperv-virtual-switch "minikube"`
- 指定使用hyperv
- 指定使用指定的虚拟交换机
- 后续不要再使用minikube start指令
- 使用hyper-v的保存和启动 指令来启停环境
- 确认minikube虚拟机的IP, 应该形如 192.168.x.x
- 记下这个IP, 后续称之为 minikube_ip
## Q&A
- Q: 为什么要指定hyper-v
- A: 在装了docker客户端后, 不指定的话默认是用docker的, 但我实际用下来发现docker的不好用
- Q: 为什么要指定使用新增的虚拟交换机(外部IP)
- A: 因为这样比较容易固定下minikube的IP 以便我们后续做docker私有仓库等服务
- 类似的服务可能需要使用私有证书, 而私有证书是发给IP的, 如果IP总是变, 则私有证书也要变来变去
# 安装docker客户端
- 略
# 在K8s上安装私有docker仓库
- 登录minikube虚拟机 生成证书
- ssh docker@minikube_ip
- 默认密码 tcuser
- 参考 https://www.linuxtechi.com/setup-private-docker-registry-kubernetes/
- 生成证书
- 部署 private register pods和services
- 修改windows机(开发机)的/etc/hosts解析 让 k8s-master域名解析到 192.168.2.15
- 测试开发机访问私有仓库
- 开启开发机的docker客户端
- 把Java工程的镜像上传到新建的私有仓库
- docker push k8s-master:31320/springbootdemo:1.0.0
- 参考Java工程Docker化一项
- 配置 K8s control plane 访问私有仓库
- ssh minikube_ip
- 修改 /etc/hosts 如 开发机
- 复制证书
- `sudo cp /opt/certs/registry.crt /etc/docker/certs.d/k8s-master:31320/ca.crt`
- 如果目录不存在就先建目录
- 测试K8s control plane 访问私有仓库
- ssh minikube_ip
- docker pull k8s-master:31320/springbootdemo:1.0.0
# 开发Java应用并部署到K8s
- 开发Java spring boot应用
- 引入web 开8080端口
- 根路径做个Rest RequestMApping
- Docker化
- 编写Dockerfile
- 引入mvn docker plugin(可选) 执行mvn dockerfile:build
- 重新tag 改仓库 tag
- 部署到K8s上
- 编写K8s.yaml描述文件
- 描述deployment
- 描述service
- Apply -f
# 总结完毕
# 继续学习 K8s configuration
- 不看k8s的官方文档了 理由是其使用的 import javax.inject.Inject; 技术较旧
- 关键词 k8s springboot configuration
- 参考 https://spring.io/guides/topicals/spring-on-kubernetes/
- (可选) 引入actuator
- 设置k8s.yaml healthy check 指向 actuator的healthy
```
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
```
- 重点 Using ConfigMaps To Externalize Configuration
## 在k8s上创建configmap
- 创建一个properties文件
- 例如 ./k8s/Application.properties
- 里面写一个springboot相关的配置项 比如
```
server.shutdown=graceful
management.endpoints.web.exposure.include=*
```
- 使用如下命令创建k8s configmap
- `kubectl create configmap gs-spring-boot-k8s --from-file=./k8s/Application.properties`
- 校验
- `kubectl get configmap gs-spring-boot-k8s -o yaml`
- The last step is to mount this ConfigMap as a volume in the container
- 把上述configmap做成一个容器上的文件路径
- 关键代码
```
在containers.readnessProbe同级,加
volumeMounts:
- name: config-volume
mountPath: /App/config
在containers: 同级 加
volumes:
- name: config-volume
configMap:
name: gs-spring-boot-k8s
```
- 代码解析
- 给container指示, 要求其新开一个volumeMounts
- 源 用名字指定 config-volume
- 目标 路径给 /App/config
- 这里是 /App , 不是示例中的/workspace
- 因为我们的Dockerfile的WORKDIR是/App
- 创建名叫config-volume的volume
- 命名 和上述volumeMounts要对应
- 源 指定为 configMap
- 名字就是之前创建的configMap的名字 即
- gs-spring-boot-k8s
- 总结
- configMap在k8s里是一种资源
- 可以通过properties的格式创建
- 可以mount给container的一个目录
- 这个目录下会有一个叫做Applications.properties的文件
- 这个文件名是默认的
- 通过springboot的actuator去读取展示
# 后续关于 Service Discovery and Load Balancing
- 只读 不操作了
- 关键点
> Kubernetes sets up DNS entries so that we can use the service ID (e.g. name-service) to make an HTTP request to the service without knowing the IP address of the pods. The Kubernetes service also load balances these requests between all the pods.
# k8s教程 configuring redis
- 读一遍 不操作了
- 需要注意的就是 修改了configMap后 需要重建pod使其生效
- 文档中采取的重建pod的方法是
- kubectl delete pod redis
- 之后重新Apply k8s描述文件
- 其它需要注意的点
- k8s描述文件可以是从网上下载下来的 即使用url
- configMap的data可以直接写, 不一定要from file
- configMap可以映射成任何文件名 不一定是Applications.properties
- 比如例子中被映射为了redis.conf
# Pos Security Admission(PSA)
- 是关于 pod能用什么版本的镜像?
- 在PSA设置比较严格的情况下, deployment不能使用latest的tag的镜像?
- 暂时用不到, 存疑
- 其它需要注意的点
- 使用kind可以创建新集群
- 创建集群时 需要指定一个集群image 如 kindest/node:v1.23.0
- 可以让kubectl匹配给新集群
- 部分label的key有控制意义, 不能乱打
- 比如 pod-security.kubernetes.io/enforce
## Apply Pod Security Standards at the Namespace Level
- 其它需要注意的点
- 使用kubectl create ns xxx 来创建namespace
- 关于namespace的部分以后再说了
# Stateless Applications 无状态应用
- 只读 不操作
- 重点 创建服务时 指定 type=LoadBalancer
```
kubectl expose deployment hello-world --type=LoadBalancer --name=my-service
```
- 感觉之前有服务负载沾着 原因就是service 的 type没有设成LoadBalancer?
- 这个依赖external load balancer
- 参考 https://kubernetes.io/docs/tasks/access-Application-cluster/create-external-load-balancer/
- K8s会负责和外部的load balancer通讯, 汇报内部ip变更之类的
- 当外部load balancer提供好IP地址后, k8s会记录下来
- 如果没有外部load balancer 则外部ip这里一直是pending
- 所以正常情况下, 对外提供的服务不使用和control plane一样的ip(minikube_ip)
- 而是有自己的ip
- 其它关注的点
- google云或amazon云 付费后提供kubectl可以使用的API
- 如何让kubectl和对应的线上租用的云匹配, 要根据对应的云提供商来设置
- 估计就是下载个证书的事情
# Stateful Applications 有状态应用
- 目标 建个mysql的数据库
- 后续目标 Flink和ElasticSearch
## 前置任务
- HeadlessServices
- 不需要load-balancing的服务
- 没有cluster IP
- headless服务可以结合其它服务发现框架 不用k8s自己的服务发现实现
- 参考 https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
- NodePort类型 关键就是在control plane上分配一个port, 路由给内部的服务
- LoadBalancer类型 就是和外部load balancer协作, 申请一个独立的IP, 再路由给内部的服务
- PersistentVolumes
- Pods consume node resources and PVCs consume PV resources
- PersistentVolume Provisioning
## 创建一个StatefulSet
- 创建stateful set 替换(相当于)创建deployment
- 创建 见示例 k8s/web.yaml
- 检查 `kubectl get statefulset web`
- stateful set里每个pod的创建是顺序的 0创建好了再开始创建1
- stateful set里每个pod有独立固定的id
- 格式是 <statefulset名字>-<序号>
- 这个也是每个节点的hostname
- 用exec试一下
- 内部DNS也生效了, 内部其它pod可以用nslookup找到web-0.nginx
- 注意域名格式 web-0.nginx
# 检查固定存储的部分
- 检查pvc
- `kubectl get pvc -l App=nginx`
- 此例使用是的动态pvc 自动创建了两个pvc, 绑定到两个pv上
- 上述两个nginx服务各有1G的存储, 不共享
- 这个是有问题的, 应该搞成共享?
- 重新生成pod不影响存储 pv还在那里
- 后续 TODO Scaling a StatefulSet
# 总结
- StatefulSet
- 固定DNS记录 针对每一个pod
- 相对, Stateless则是多个pod共享一个service的DNS解析记录
- 固定的存储
- 就算删除pod重新创建, 存储还是在的
- 相对, Stateless则一般没有存储, 就算有日志什么的, 重新创建pod的话应该也丢失了