第零讲 环境准备
使用 virtualbox 安装 ubuntu 虚拟环境
个人采坑记录
共享粘贴板设置
配置了双向粘贴版 和 安装 apt install virtualbox-guest-utils
还是不行, 每次得手动启动 VBoxClient --clipborad
.
通过 df -h
查看目录找到 挂载的脚步 在 /media/ubuntu/VBox_GAs_6.1.34/
目录
后面卸载 virtualbox-guest-utils, 运行 sh /media/ubuntu/VBox_GAs_6.1.34/VBoxLinuxAdditions.run
安装才可以
第一讲 初始容器
sudo usermod -aG docker ${USER}
将当前用户加入 docker 组,然后重启, 否则需要 root 权限。
docker version
查看客户端和服务端版本信息
docker info
查看服务端系统信息
第二讲 被隔离的进程
容器就是一个特殊的隔离环境,它能够让进程只看到这个环境里的有限信息。
隔离实现基于 Linux 提供的三种技术:
- namcespace:资源隔离
- cgroup:配额限制
- chroot:文件系统根目录限制
第三讲 docker 实用命令
常用镜像操作命令
常用容器操作命令
第四讲 Dockerfile 编写
镜像是由许多只读层堆叠起来的
可以通过 docker inspect
命令查看镜像的分层信息
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# Dockerfile
# docker build -t ngx-app .
# docker build -t ngx-app:1.0 .
ARG IMAGE_BASE="nginx"
ARG IMAGE_TAG="1.21-alpine"
FROM ${IMAGE_BASE}:${IMAGE_TAG}
COPY ./default.conf /etc/nginx/conf.d/
RUN cd /usr/share/nginx/html \
&& echo "hello nginx" > a.txt
EXPOSE 8081 8082 8083
|
第五讲 镜像仓库
镜像仓库 registry 是提供镜像服务的网站,如上传、下载镜像等
Docker Hub 是当前最大的镜像仓库,上面有官方镜像、认证镜像、非官方镜像等
名字规则: user/image:tag
离线环境可以使用归档命令来共享
1
2
3
|
docker save ngx-app:latest -o ngx.tar
docker load -i ngx.tar
|
第六讲 容器如何如外接互通
如何拷贝容器内数据
1
|
docker cp a.txt 062:/tmp
|
1
|
docker cp 062:/tmp/a.txt ./b.txt
|
如何共享文件目录
volume 挂载
1
|
docker run -v host_path:container_path image:tag
|
如何网络互通
host 模式
1
|
docker run -d --rm --net=host nginx:alpine
|
对比 ip addr
命令在宿主机和容器的结果,可以发现是相同的
bridge 模式
桥接模式类似现实世界的交换机,不过是由软件虚拟出来的,
容器和宿主机通过虚拟网卡接入网桥 docker0,收发网络包
1
|
docker run -d --rm --net=bridge nginx:alpine
|
使用 -p host_port:container_port
映射宿主机和容器端口
第九讲 本机搭建 k8s 环境
使用 minikube
启动 minikube
1
|
minikube start --kubernetes-version=v1.23.3 --image-mirror-country=cn
|
启动容器服务
1
2
3
|
alias kubectl="minikube kubectl --"
kubectl run ngx --image=nginx:alpine
|
第十讲 k8s 工作机制
k8s 有许多模块组成,这些模块可分为组件(component)和插件(addon)两类
master 组件
apiserver
apiserver 是 k8s 系统唯一入口,对外提供了一些列 RESTful API,其他组件
只能和它直接通信, 相当于联络员
etcd
etcd 用来持久化存储系统里各种资源对象和状态,相当于配置管理员。
其他组件想读写 etcd 的数据必须通过 apiserver
scheduler
scheduler 负责容器的编排工作,检查阶段的资源状态,把 pod 调度到 最合适的节点运行,
相当于部署人员。
controller-manager
controller-manager 负责维护容器和节点等资源状态,实现故障检测、服务迁移、应用伸缩等功能,
相当于监控运维人员。
Node 组件
kubelet
kubelet 负责管理 Node 相关绝大部分操作, Node 上只有它与 apiserver 通信,
实现状态报告、命令下发、启停容器等功能,相当于小管家。
kubelet 因为要管理整个节点,容器化会限制它的能力,所有运行在物理机上。
kube-proxy
kube-proxy 是 Node 的网络代理,转发 TCP/UDP 数据包,相当于专职小邮差
container-runtime
插件
比较重要的两个插件:DNS、Dashboard
第十一讲 API
YAML
k8s 使用 yaml 声明式(declarative)地描述资源
API 对象
k8s 在理论层面抽象出了许多个概念,用来描述系统的管理运维工作,这些概念叫做
“API 对象”。
可以使用kubectl api-resources
查看对象列表
如何描述 API 对象
命令式启动pod kubectl run ngx --image=nginx:alpine
可以改写成如下声明式配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
apiVersion: v1
kind: Pod
metadata:
name: ngx-pod
labels:
env: demo
owner: chrono
spec:
containers:
- image: nginx:alpine
name: ngx
ports:
- containerPort: 80
|
可以将 配置类比成 HTTP 报文,分为两部分
- header: apiVersion, kind, metadata
- body: spec
可以使用如下命令创建和删除资源
1
2
3
|
kubectl apply -f ngx-pod.yaml
kubectl delete -f ngx-pod.yaml
|
编写 yaml 技巧
- 使用
kubectl api-resources
查看对应的 API 版本 和 类型
- 使用
kubectl explain
查看对应资源的详细说明
- 使用
--dry-run=client -o yaml
生成 yaml 文件
第十二讲 Pod
Pod 是对容器的"打包",是 k8s 最小的调度部署单位, 类似于 Linux 里面的进程组
如何使用 yaml 描述 Pod
Pod 有四个基本组成部分
- apiVersion: 固定为 v1
- kind: 固定为 Pod
- metadata:
- name: 指定名称,可以带 pod 后缀方便和其他资源区分
- labels: 标签,可添加任意的 key/value, 如 env=dev、region=north
- containers: 容器列表
- image: 镜像
- name: 名字
- ports:对外暴露的端口
- imagePullPolicy: 镜像拉取策略,默认为 IfNotPresent, 本地不存在才会来取远程镜像
- env: 定义环境变量,运行时指定
- command: 容器启动时执行的命令,相当于Dockerfile 的 ENTRYPOINT
- args: command 参数,相当于 Dockerfile 的 CMD 指令
如何使用 kubectl 操作 Pod
使用 kubectl apply -f
、kubectl delete -f
创建或删除 Pod
使用 kubectl logs
产看日志
使用 kubectl exec -it $pod -- sh
进入 Pod 内部
第十三讲 Job/CronJob
Job/CronJob 都属于离线业务,区别在于 Job 只运行一次
Job
Job yaml 相比 Pod, 区别在于 spec 使用 template 字段来定义 Pod
使用 kubectl apply -f job.yaml
启动 job
使用 kubectl get job
查看 job 状态
使用 kubectl get pod
查看 job 中的 pod 的 状态
部分离线作业重要字段
- activeDeadlineSeconds: pod 超时时间
- backoffLimit: pod 失败重试次数
- completions: Job 需要完成运行多少个 Pod, 默认为1
- parallelism: completions 对应的并发度
示例如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
apiVersion: batch/v1
kind: Job
metadata:
name: sleep-job
spec:
activeDeadlineSeconds: 15
backoffLimit: 2
completions: 4
parallelism: 2
template:
spec:
restartPolicy: OnFailure
containers:
- image: busybox
name: echo-job
imagePullPolicy: IfNotPresent
command:
- sh
- -c
- sleep $(($RANDOM % 10 + 1)) && echo done
|
CronJob
CronJob 其实组合了Job
第十四讲 ConfigMap/Secret
ConfigMap 保存明文配置, Secret 保存 私密配置
ConfigMap
ConfigMap 可以使用 kubectl create cm info --from-literal=aa=bb --dry-run=client -o yaml > info-cm.ayml
创建模版
示例
1
2
3
4
5
6
7
8
9
10
11
|
apiVersion: v1
kind: ConfigMap
metadata:
name: info
data:
count: '10'
debug: 'on'
path: '/etc/systemd'
greeting: |
say hello to kubernetes.
|
创建和查看
1
2
3
4
5
|
kubectl apply -f info-cm.yaml
kubectl get cm
kubectl describe cm info
|
Secret
Secret 使用 kubectl create secret generic
创建模板
示例
1
2
3
4
5
6
7
8
9
|
apiVersion: v1
kind: Secret
metadata:
name: user
data:
name: cm9vdA== # root
pwd: MTIzNDU2 # 123456
db: bXlzcWw= # mysql
|
1
2
3
4
5
6
7
|
kubectl create secret generic user --from-literal=aa=bb --dry-run=client -o yaml > user-secret.yaml
kubectl apply -f user-secret.yaml
kubectl get secret
kubectl describe secret user
|
使用
环境变量
使用 configMapKeyRef
和secretKeyRef
来引用
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
|
apiVersion: v1
kind: Pod
metadata:
name: env-pod
spec:
containers:
- env:
- name: COUNT
valueFrom:
configMapKeyRef:
name: info
key: count
- name: GREETING
valueFrom:
configMapKeyRef:
name: info
key: greeting
- name: USERNAME
valueFrom:
secretKeyRef:
name: user
key: name
- name: PASSWORD
valueFrom:
secretKeyRef:
name: user
key: pwd
image: busybox
name: busy
imagePullPolicy: IfNotPresent
command: ["/bin/sleep", "300"]
|
引用示意图如下
加载文件
Pod 可以挂载 Volume, 可以在 spec
中指定,在 containers
引用
引用示意图如下
示意配置如下
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
|
apiVersion: v1
kind: Pod
metadata:
name: vol-pod
spec:
volumes:
- name: cm-vol
configMap:
name: info
- name: sec-vol
secret:
secretName: user
containers:
- volumeMounts:
- mountPath: /tmp/cm-items
name: cm-vol
- mountPath: /tmp/sec-items
name: sec-vol
image: busybox
name: busy
imagePullPolicy: IfNotPresent
command: ["/bin/sleep", "300"]
|
挂载后,ConfigMap 和 Secret 的 键成了文件名,值成了文件内容
第十七讲 搭建多节点 k8s 集群
实验环境准备
安装前
- 修改hostname, k8s 使用 hostname 来区分集群节点,所以 hostname 不能重名。
- 安装 docker 运行时,将其 cgroup 的驱动程序改为 systemd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker
|
- 启用 br_netfilter 模块, 使 k8s 能够检查和转发网络流量
1
2
3
4
5
6
7
8
9
10
11
|
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward=1 # better than modify /etc/sysctl.conf
EOF
sudo sysctl --system
|
- 关闭 swap, 提升 k8s 性能
1
2
|
sudo swapoff -a
sudo sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab
|
安装 kubeadm
- 更换 k8s 下载源
1
2
3
4
5
6
7
8
9
|
sudo apt install -y apt-transport-https ca-certificates curl
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
sudo apt update
|
- 下载 kubeadm
1
2
3
4
|
sudo apt install -y kubeadm=1.23.3-00 kubelet=1.23.3-00 kubectl=1.23.3-00
# 锁定版本,避免错误升级
sudo apt-mark hold kubeadm kubelet kubectl
|
- 下载镜像
由于 k8s 镜像在 google 镜像仓库 gcr.io,国内访问困难,
改用国内镜像,然后使用 docker tag 改名
1
2
3
4
5
6
7
8
9
10
11
12
|
repo=registry.aliyuncs.com/google_containers
for name in `kubeadm config images list --kubernetes-version v1.23.3`; do
src_name=${name#k8s.gcr.io/}
src_name=${src_name#coredns/}
docker pull $repo/$src_name
docker tag $repo/$src_name $name
docker rmi $repo/$src_name
done
|
安装 master 节点
1
2
3
4
5
6
|
# --pod-network-cidr 设置集群使用网段
# --apiserver-advertise-address 设置apiserver 地址
sudo kubeadm init \
--pod-network-cidr=10.10.0.0/16 \
--apiserver-advertise-address=192.168.10.210 \
--kubernetes-version=v1.23.3
|
安装后复制 kubectl 配置文件
1
2
3
|
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
加入集群
1
2
|
kubeadm join 192.168.10.210:6443 --token tv9mkx.tw7it9vphe158e74 \
--discovery-token-ca-cert-hash sha256:e8721b8630d5b562e23c010c70559a6d3084f629abad6a2920e87855f8fb96f3
|
安装网络插件,这里使用 flannel
使用配置模版 kube-flannel.yml, 修改网段
1
2
3
4
5
6
7
|
net-conf.json: |
{
"Network": "10.10.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
|
应用
1
|
kubectl apply -f kube-flannel.yml
|
使用 kubectl get node
验证
安装 worker 节点
克隆 master 虚拟机, 使用 kubeamd join
指令加入即可
第十八讲 Deployment
Deployment 主要用来管理在线业务 Pod
生成默认模版
1
|
kubectl create deploy ngx-dep --image=nginx:alpine --dry-run=client -o yaml > ngx-dep.yaml
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ngx-dep
name: ngx-dep
spec:
replicas: 2
selector:
matchLabels:
app: ngx-dep
template:
metadata:
labels:
app: ngx-dep
spec:
containers:
- image: nginx:alpine
name: nginx
|
replicas
表示副本数量
selector
作用是筛选出要被 Deployment 管理的 Pod 对象, 下属 matchLabels
必须和
template
的 labels
完全相同
1
2
3
4
5
6
7
8
|
# 查看deploy状态
kubectl get deploy
# 使用标签筛选 pod
kubectl get pod -l app=ngx-dep
# 调整 replicas
kubectl scale --replicas=5 deploy ngx-dep
|
第十九讲 DaemonSet
介绍
DaemonSet 会在 满足条件的所有节点上运行一个 Pod, 就像 Linux 系统里的 守护进程(Daemon)
一些应用与节点存在绑定关系,如
- 网络应用 kube-proxy
- 监控应用 prometheus
- 日志应用 fluentd
DaemonSet 不可以用 kubectl create
创建模板,可以在 Deployment 模版上修改
, 主要区别就是 DaemonSet 没有 replicas
字段
taint 和 toleration
节点的标签有一个专门字段taint
污点来表述,Pod 可以配置 toleratoin
来筛选 taint
taint 可以在 kubectl describe node 查看
1
2
3
4
5
|
# 添加 taint
kubectl taint node master node-role.kubernetes.io/master:NoSchedule-
# 结尾加"-" 来移除 taint
kubectl taint node master node-role.kubernetes.io/master:NoSchedule-
|
可以 Pod 的 spec 下 选择 taint
1
2
3
4
|
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
operator: Exists
|
静态 Pod
静态 Pod 不受 k8s 系统管控, 不与 apiserver 发生关系
对应配置文件默认存放在 /etc/kubernetes/manifests 目录下
如 master 节点的四个核心组件都是以 静态 Pod 形式存在的
第二十讲 Service
Service 工作原理和 Nginx 差不多, k8s 会分给它一个静态 IP,
然后它再去自动管理维护后面动态变化的 Pod 集合,当客户端访问 Service,它根据某种策略把流量转发给后面某个 Pod.
可以使用如下命令创建 yaml 模版
1
|
kubectl expose deploy ngx-dep --port=80 --target-port=80 --dry-run=client -o yaml
|
配置中主要有两个关键字 selector
和 ports
查看 ngx-svc 详情
exec 到 pod 进行测试
k8s 会自动给对象分配域名,可以使用 ngx-svc
等域名访问
service 默认为 ClusterIP
类型,即静态 IP 只能集群内部访问。
可以使用 NodePort
类型, 这样节点会提供一个对外暴露的端口指向该 service
1
2
3
4
5
6
|
apiVersion: v1
...
spec:
...
type: NodePort
|
NodePort 类型有如下缺点:
- 端口有限,默认只在 30000~32767 范围分配
- 每个节点都开端口,提高了网络通信成本
- 需要对外暴露节点的 IP 地址,为了安全需要搭建一个反向代码,增加了复杂度。
第二十二讲 实战演练
要点回顾
Service 是 Pod IP 地址的抽象,它拥有一个固定的 IP 地址,再使用 iptables 规则
把 流量负责均衡到后面的 Pod, 节点上的 kube-proxy 组件后实时维护被代理的 Pod 状态,
保证 Service 只会转发给健康的 Pod
Service 还基于 DNS 插件支持域名,所以客户端不在需要关心 Pod 具体情况
Service 是四层的负载均衡,而 Ingress 是 七层负责均衡
Ingress 需要 IngressController 和 IngressClass 配合工作
- Controller 是真正的集群入口,应用 Ingress 规则调度、分发流量;此外还提供反向代理的角色
- Class 是用来分组管理 Ingress
IngressController 本身也是一个 Pod, 需要通过 NodePort、LoadBalancer等方式暴露到集群外。
WordPress 网站搭建
1. 部署 MariaDB
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
---
apiVersion: v1
kind: ConfigMap
metadata:
name: maria-cm
data:
DATABASE: 'db'
USER: 'wp'
PASSWORD: '123'
ROOT_PASSWORD: '123'
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: maria-dep
name: maria-dep
spec:
replicas: 1
selector:
matchLabels:
app: maria-dep
template:
metadata:
labels:
app: maria-dep
spec:
containers:
- image: mariadb:10
name: mariadb
ports:
- containerPort: 3306
envFrom:
- prefix: 'MARIADB_'
configMapRef:
name: maria-cm
---
apiVersion: v1
kind: Service
metadata:
labels:
app: maria-dep
name: maria-svc
spec:
ports:
- port: 3306
protocol: TCP
targetPort: 3306
selector:
app: maria-dep
|
使用 ConfigMap + Development + Service 的方式
1
|
kubectl appy -f wp-maria.yaml
|
2. 部署 WordPress
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
apiVersion: v1
kind: ConfigMap
metadata:
name: wp-cm
data:
HOST: 'maria-svc'
USER: 'wp'
PASSWORD: '123'
NAME: 'db'
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: wp-dep
name: wp-dep
spec:
replicas: 2
selector:
matchLabels:
app: wp-dep
template:
metadata:
labels:
app: wp-dep
spec:
containers:
- image: wordpress:5
name: wordpress
ports:
- containerPort: 80
envFrom:
- prefix: 'WORDPRESS_DB_'
configMapRef:
name: wp-cm
---
apiVersion: v1
kind: Service
metadata:
labels:
app: wp-dep
name: wp-svc
spec:
ports:
- name: http80
port: 80
protocol: TCP
targetPort: 80
nodePort: 30088
selector:
app: wp-dep
type: NodePort
|
1
2
3
4
|
同样使用 ConfigMap + Development + Service 的方式
```shell
kubectl appy -f wp-dep.yaml
|
wp-svc
使用 NodePort 暴露到集群外
3. 部署 Nginx Ingress Controller
定义 IngressClass
1
2
3
4
5
6
7
|
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: wp-ink
spec:
controller: nginx.org/ingress-controller
|
定义 Ingress
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wp-ing
spec:
ingressClassName: wp-ink
rules:
- host: wp.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wp-svc
port:
number: 80
|
定义 IngressController
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
# https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/
kind: Deployment
metadata:
name: wp-kic-dep
namespace: nginx-ingress
spec:
replicas: 1
selector:
matchLabels:
app: wp-kic-dep
template:
metadata:
labels:
app: wp-kic-dep
spec:
serviceAccountName: nginx-ingress
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
#- image: nginx/nginx-ingress:2.2.0
- image: nginx/nginx-ingress:2.2-alpine
imagePullPolicy: IfNotPresent
name: nginx-ingress
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
- name: readiness-port
containerPort: 8081
- name: prometheus
containerPort: 9113
readinessProbe:
httpGet:
path: /nginx-ready
port: readiness-port
periodSeconds: 1
securityContext:
allowPrivilegeEscalation: true
runAsUser: 101 #nginx
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
args:
- -ingress-class=wp-ink
- -health-status
- -ready-status
- -nginx-status
- -enable-snippets
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
#- -v=3 # Enables extensive logging. Useful for troubleshooting.
#- -report-ingress-status
#- -external-service=nginx-ingress
#- -enable-prometheus-metrics
#- -global-configuration=$(POD_NAMESPACE)/nginx-configuration
---
apiVersion: v1
kind: Service
metadata:
name: wp-kic-svc
namespace: nginx-ingress
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30080
selector:
app: wp-kic-dep
type: NodePort
|
需要执行 ingress 目录 setup.sh 脚本
1
2
|
# https://github.com/chronolaw/k8s_study/blob/master/ingress/setup.sh
./setup.sh
|
在 /etc/hosts 加上 worker node 对 wp.test 映射后即可使用 http://wp.test
访问服务
第二十四讲 PersistentVolume
PersistentVolume, 简称 PV 是存储的抽象,实际就是一些文件系统,如 GlusterFS、NFS、本地磁盘
PersistentVolumeClaim, 简称 PVC,用来代表 Pod 向系统申请 PV,申请成功后会将 PV 和 PVC 绑定在一起
StorageClass 抽象了特定的存储类型,在 PVC 和 PV 之间充当协调人的角色。
PV 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
apiVersion: v1
kind: PersistentVolume
metadata:
name: host-10m-pv
spec:
storageClassName: host-test
accessModes:
- ReadWriteOnce
capacity:
storage: 10Mi
hostPath:
path: /tmp/host-10m-pv/
|
accessModes 定义了设备访问模式:
- ReadWriteOnce: 可读写,但只能被一个 Node 上的 Pod 挂载
- ReadOnlyMany: 仅可读,可以被多个 Node 上的 Pod 挂载
- ReadWriteMany: 可读写,可以被多个 Node 上的 Pod 挂载
PVC 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
|
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: host-5m-pvc
spec:
storageClassName: host-test
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Mi
|
Pod 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
apiVersion: v1
kind: Pod
metadata:
name: host-pvc-pod
spec:
volumes:
- name: host-pvc-vol
persistentVolumeClaim:
claimName: host-5m-pvc
containers:
- name: ngx-pvc-pod
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: host-pvc-vol
mountPath: /tmp
|
第二十五讲 PV+NFS
安装 NFS
服务端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
sudo apt -y install nfs-kernel-server
mkdir -p /tmp/nfs
# 添加配置
sudo echo '/tmp/nfs 192.168.10.0/24(rw,sync,no_subtree_check,no_root_squash,insecure)' >> /etc/exports
# 通知 nfs
sudo exportfs -ra
# 查看详情
sudo exportfs -v
# 检查网络挂载
showmount -e localhost
|
客户端
1
2
3
4
5
6
7
8
|
sudo apt -y install nfs-common
# 测试服务端挂载情况
showmount -e 192.168.10.208
mkdir -p /tmp/test
sudo mount -t nfs 192.168.10.208:/tmp/nfs /tmp/test
|
静态 PV
pv 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-1g-pv
spec:
storageClassName: nfs
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
nfs:
path: /tmp/nfs/1g-pv
server: 192.168.10.208
|
pvc 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-static-pvc
spec:
storageClassName: nfs
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
|
测试 pod 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
apiVersion: v1
kind: Pod
metadata:
name: nfs-static-pod
spec:
volumes:
- name: nfs-pvc-vol
persistentVolumeClaim:
claimName: nfs-static-pvc
containers:
- name: nfs-pvc-test
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: nfs-pvc-vol
mountPath: /tmp
|
示意图
动态 PV
使用 Provisioner 来自动管理存储、创建 PV ,
NFS Provisioner 是以 Pod 形式运行在 k8s,需要三个部署文件
rabc.yaml、class.yaml 和 deployment.yaml
storageClass 配置
1
2
3
4
5
6
7
8
|
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "false"
|
pvc 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-dyn-10m-pvc
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Mi
|
测试 pod 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
apiVersion: v1
kind: Pod
metadata:
name: nfs-dyn-pod
spec:
volumes:
- name: nfs-dyn-10m-vol
persistentVolumeClaim:
claimName: nfs-dyn-10m-pvc
containers:
- name: nfs-dyn-test
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: nfs-dyn-10m-vol
mountPath: /tmp
|
由于有 NFS Provisioner, 不再需要直接定义 PV 对象,会自动按 PVC 配置创建。
整体示意图
第二十六讲 StatefulSet
StatefulSets 和 Deployment 类似, 不过 pod 名称是固定的,pod 启动是有序的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-sts
spec:
serviceName: redis-svc
replicas: 2
selector:
matchLabels:
app: redis-sts
template:
metadata:
labels:
app: redis-sts
spec:
containers:
- image: redis:5-alpine
name: redis
ports:
- containerPort: 6379
|
声明 svc
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: Service
metadata:
name: redis-svc
spec:
selector:
app: redis-sts
ports:
- port: 6379
protocol: TCP
targetPort: 6379
|
验证网络
svc 和 sts 之间的关联
sts 可以通过定义 volumeClaimTemplates
来声明 pvc
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
35
36
37
38
|
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-pv-sts
spec:
serviceName: redis-pv-svc
volumeClaimTemplates:
- metadata:
name: redis-100m-pvc
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
replicas: 2
selector:
matchLabels:
app: redis-pv-sts
template:
metadata:
labels:
app: redis-pv-sts
spec:
containers:
- image: redis:5-alpine
name: redis
ports:
- containerPort: 6379
volumeMounts:
- name: redis-100m-pvc
mountPath: /data
|
三者关联图
pvc 状态
第二十七讲 滚动更新
更新
k8s 里面的应用的版本变化就是指 template 里的 Pod 变化,最终
使用 template 的 Hash 值作为 版本号
apply 更新的 yaml 后,可以使用kubectl rollout status
查看更新状态
可以使用 kubectl describe deployment ngx-dep
查看具体的 pod 变化
pod 变化过程示意图
历史及回滚
可使用 kubectl rollout history deployment ngx-dep
查看更新历史, 默认保存最近十次记录
可使用 kubectl rollout history deployment ngx-dep --revision=2
查看具体历史的详情
可使用 kubectl rollout undo deployment ngx-dep
来回滚
可使用 kubectl rollout undo deployment ngx-dep --to-revision=3
来回滚到任意版本
回滚时 pod 变化过程示意图
添加版本备注信息
1
2
3
4
5
6
7
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: ngx-dep
annotations:
kubernetes.io/change-cause: v1, ngx=1.21
... ...
|
第二十八讲 如何监控 Pod 健康状态
资源配额
通过在 容器描述部分添加 resources 字段来声明资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
apiVersion: v1
kind: Pod
metadata:
name: ngx-pod-resources
spec:
containers:
- image: nginx:alpine
name: ngx
resources:
requests:
cpu: 10m
memory: 100Mi
limits:
cpu: 20m
memory: 200Mi
|
requests
表示容器需要申请的资源,limits
表示资源使用上限,超过则可能被强制停止。
cpu 的单位m
代表千分之一。
如果不加资源声明,k8s 可以将 pod 调度到任意节点上,否则
k8s 会根据每个 Pod 声明的需求,像搭积木或者玩俄罗斯方块一样,把节点尽量塞满。
检查探针
k8s 定义了三种探针
- Startup: 启动探针,用来检查应用是否启动成功
- Liveness: 存活探针,用来检查应用是否正常运行
- Readiness: 就绪探针,用来检查应用是否可以接受流量
三种探针是递进关系,Startup 成功后,才会执行后面两个探针
如果 Liveness 探针失败, k8s 会重启容器,Readiness 探针失败,会将容器从 Service 负载均衡集合中移除,不分配流量。
k8s 支持使用 shell、tcp、http 三种方式检测
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
35
|
apiVersion: v1
kind: Pod
metadata:
name: ngx-pod-probe
spec:
volumes:
- name: ngx-conf-vol
configMap:
name: ngx-conf
containers:
- image: nginx:alpine
name: ngx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx/conf.d
name: ngx-conf-vol
startupProbe:
periodSeconds: 1
exec:
command: ["cat", "/var/run/nginx.pid"]
livenessProbe:
periodSeconds: 10
tcpSocket:
port: 80
readinessProbe:
periodSeconds: 5
httpGet:
path: /ready
port: 80
|
第二十九讲 命名空间
命名空间是一个逻辑上的概念,它可以将集群切分成一个个彼此独立的区域
创建命名空间
1
|
kubectl create ns dev-ns
|
有了命名空间,就可以给命名空间设定资源配额,示例如下
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
|
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-qt
namespace: dev-ns
spec:
hard:
requests.cpu: 10
requests.memory: 10Gi
limits.cpu: 10
limits.memory: 20Gi
requests.storage: 100Gi
persistentvolumeclaims: 100
pods: 100
configmaps: 100
secrets: 100
services: 10
count/jobs.batch: 1
count/cronjobs.batch: 1
count/deployments.apps: 1
|
设定了资源配额之后,所有 Pod 都需要显式声明资源需求,但这样
的严格要求有时不太方便,我们可以声明一个默认资源配额
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
apiVersion: v1
kind: LimitRange
metadata:
name: dev-limits
namespace: dev-ns
spec:
limits:
- type: Container
defaultRequest:
cpu: 200m
memory: 50Mi
default:
cpu: 500m
memory: 100Mi
- type: Pod
max:
cpu: 800m
memory: 200Mi
|
第三十讲 如何使用 Metrics Server 和 Prometheus
Metrics Server
metrics server 是一个专门来收集核心资源指标的工具,定时从所有节点的 kubelet
里采集信息。
安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# 下载配置yaml
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# 修改配置
# deployment.spec.template.spec.containers 对象 添加 '- --kubelet-insecure-tls'
# deployment.spec.template.spec 对象 添加 'nodeName: k8s-master #你自己的节点名称'
# 重命名镜像
repo=registry.aliyuncs.com/google_containers
name=k8s.gcr.io/metrics-server/metrics-server:v0.6.1
src_name=metrics-server:v0.6.1
docker pull $repo/$src_name
docker tag $repo/$src_name $name
docker rmi $repo/$src_name
# 应用
kubectl apply -f components.yaml
|
查看
HorizontalPodAutoscaler
HorizontalPodAutoscaler 是 用来 自动伸缩 Pod 数量的对象,简称 hpa.
hpa 从 Metrics Server 获取当前应用的指标,按照策略增减 Pod 数量
使用 nginx deploy 示例
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
35
36
37
38
39
40
41
42
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: ngx-hpa-dep
spec:
replicas: 1
selector:
matchLabels:
app: ngx-hpa-dep
template:
metadata:
labels:
app: ngx-hpa-dep
spec:
containers:
- image: nginx:alpine
name: nginx
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 10Mi
limits:
cpu: 100m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: ngx-hpa-svc
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: ngx-hpa-dep
|
创建 hpa 模版
1
2
|
export out="--dry-run=client -o yaml" # 定义Shell变量
kubectl autoscale deploy ngx-hpa-dep --min=2 --max=10 --cpu-percent=5 $out
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: ngx-hpa
spec:
maxReplicas: 10
minReplicas: 2
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ngx-hpa-dep
targetCPUUtilizationPercentage: 5
|
Prometheus
使用 kube-prometheus
部署比较方便
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 下载部署文件
wget https://github.com/prometheus-operator/kube-prometheus/archive/refs/tags/v0.11.0.tar.gz
# 修改配置
## 替换gcr 镜像 kubeStateMetrics-deployment.yaml、prometheusAdapter-deployment.yaml
image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.5.0
image: k8s.gcr.io/prometheus-adapter/prometheus-adapter:v0.9.1
image: chronolaw/kube-state-metrics:v2.5.0
image: willdockerhub/prometheus-adapter:v0.9.1
## 添加NodePort IP ,方便本机测试 prometheus-service.yaml、grafana-service.yaml
spec.type: NodePort
# 创建服务
kubectl create -f manifests/setup
kubectl create -f manifests
|
使用 kubectl get svc -n monitoring
获取 prometheus-k8s 服务 9090对应的端口、grafana 3000 对应的端口, 浏览器访问验证
第三十一讲 网络通信
CNI
为了适应跨主机通信的需求,k8s 提出了自己的网络模型 IP-per-pod
:
- 每个 pod 有唯一的 IP 地址
- pod 里所有容器共享这个 IP 地址
- 集群内所有 pod 共享一个网段
- pod 可以直接基于 IP 访问另外一个 pod
k8s 定义了一个实现上述网络模型的标准 CNI(container networking interface),
根据实现原理, CNI 插件大致分为三类:
- Overlay: 构建了一个工作在真实底层网络之上的"逻辑网络",把原始的 Pod 网络数据封包,再通过下层网络发送出去,到了目的地再拆包。特点是适应性强,但性能较低。
- Route:使用系统内置的路由功能来实现 Pod 跨主机通信。特点是性能较高,但对底层网络依赖性强。
- Underlay:直接使用底层网络来实现CNI,Pod 和主机在一个网络里。 特点是性能最高,但对底层硬件和网络依赖性最强。
插件介绍
- Flannel:早期使用 UDP 和 VXLAN 实现了 Overlay 模式,后来使用 Host-Gateway 支持了 Route 模式
- Calico:Route 模式,使用 BGP 协议来维护路由信息,支持多种网络策略,具备数据加密、安全隔离、流量整形等功能
- Cilium:同时支持 Overlay 和 Route 模式,深度使用了 eBPF 技术,在内核层次操作网络数据,性能很高。
Flannel 工作方式分析
创建 3 个 nginx pod
1
|
kubectl create deploy ngx-dep --image=nginx:alpine --replicas=3
|
默认使用基于 VXLAN 的 Overlay 模式,从单机的角度来看,Flannel 的网络结构几乎和
Docker 一模一样,只不过网桥从 docker0 换成 了 cni0
本机网络连接示意图如下
route
查看路由表
- 10.10.0.0/24 网段的数据,都要走 cni0 设备,也就是“cni0”网桥。
- 10.10.1.0/24 网段的数据,都要走 flannel.1 设备,也就是 Flannel。
- 192.168.10.0/24 网段的数据,都要走 ens160 设备,也就是我们宿主机的网卡
假设我们要从 master 节点的“10.10.0.3”访问 worker 节点的“10.10.1.77”,因为
master 节点的“cni0”网桥管理的只是“10.10.0.0/24”这个网段,
所以按照路由表,凡是“10.10.1.0/24”都要让 flannel.1 来处理,
这样就进入了 Flannel 插件的工作流程。
跨主机网络示意图如下
链接