极客时间《Kubernetes 入门实战课》之《中级篇 (8讲)》
极客时间 《Kubernetes 入门实战课》 by 罗剑锋
github上的配套学习项目
全笔记:
《开篇词 (2讲)》
《入门篇 (8讲)》
《初级篇 (9讲)》
《中级篇 (8讲)》
《高级篇 (10讲)》
《加餐分享 (2讲)》
目录
《17|更真实的云原生:实际搭建多节点的Kubernetes集群》
-
什么是 kubeadm
kubeadm,原理和 minikube 类似,也是用容器和镜像来封装 Kubernetes 的各种组件,但它的目标不是单机部署,而是要能够轻松地在集群环境里部署 Kubernetes,并且让这个集群接近甚至达到生产级质量。 -
安装前的准备工作
第一,由于 Kubernetes 使用主机名来区分集群里的节点,所以每个节点的 hostname 必须不能重名。改名:sudo vi /etc/hostname
第二,安装Docker作为容器运行时。我:注意这里也要选择docker版本,不然后面会和编者的旧版本k8s不兼容。
sudo apt install -y docker.io=20.10.12-0ubuntu4

#锁定版本,防止升级:
sudo apt-mark hold docker.io containerd
sudo usermod -aG docker ${USER} #当前用户加入docker组
配置docker镜像源, 以及把cgroup 的驱动程序改成 systemd等:sudo vim /etc/docker/daemon.json :
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
],
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": { "max-size": "100m" },
"storage-driver": "overlay2"
}
sudo systemctl enable docker #启用Docker服务,使其在系统启动时自动运行
sudo systemctl daemon-reload
sudo systemctl restart docker
运行docker version 和 docker info来验证 Docker 是否安装成功了。
第三,为了让 Kubernetes 能够检查、转发网络流量,修改 iptables 的配置,启用br_netfilter模块:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
sudo modprobe br_netfilter
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
第四,需要修改“/etc/fstab”,关闭 Linux 的 swap 分区 :
sudo swapoff -a
sudo sed -ri '/\sswap\s/s/^#?/#/' /etc/fstab
- 安装 kubeadm, 这里指定了版本
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
sudo apt install -y kubeadm=1.23.3-00 kubelet=1.23.3-00 kubectl=1.23.3-00
验证:
kubeadm version
kubectl version --short
可锁定三个软件的版本,避免意外升级导致版本错误:sudo apt-mark hold kubeadm kubelet kubectl
- 安装 Master 节点,不需要像文中说的手动下载 Kubernetes 组件镜像
sudo kubeadm init \
--apiserver-advertise-address=192.168.56.103 \
--image-repository=registry.aliyuncs.com/google_containers \
--kubernetes-version=v1.23.3 \
--pod-network-cidr=10.10.0.0/16
如果init失败,可以使用 sudo kubeadm reset -f 进行重置
过程中一度因为版本冲突等原因失败,可查看详细日志定位精准原因:
journalctl -u kubelet -f #实时看kubelet日志
journalctl -u kubelet --no-pager -n 50 #导出最近50行错误日志
豆包说:
终于init成功:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
验证成功:
-
安装 Flannel 网络插件
下载https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml, 把其中的10.244.0.0/16替换为前面设置的podCIDR(这里是10.10.0.0/16),然后安装并验证:
注意这里要设置网卡(据说是因为我们虚拟机双网卡的缘故),否则在后续的第20小节,尝试以DNS方式使用 Service时会失败:
-
安装 Worker 节点
Worker 节点,前面的步骤和Master节点一样,不需要最后两步(kubeadm init,和安装 Flannel 网络插件)。
使用前面在master节点kubeadm init成功截图中的kubeadm join命令。
注:master的token过24小时会失效,这时需要在Master重新生成新token:kubeadm token create --print-join-command,再执行kubeadm join命令。
过阵子就可以在master节点上看到Ready的worker节点了:
在worker节点上想成功调用kubectl,还得把master节点上的config文件拷过来:
mkdir -p $HOME/.kube
scp sdh@192.168.56.103:~/.kube/config $HOME/.kube/config
- 小结
Console节点的部署工作更加简单,它只需要安装一个 kubectl,不需要docker等。
下载和安装kubectl:
curl -LO https://dl.k8s.io/release/v1.23.3/bin/linux/amd64/kubectl
sudo install kubectl /usr/local/bin/kubectl
和workder节点一样,复制master节点上的config文件。
附:本小节的整个搭建过程有部分参考了1
《18|Deployment:让应用永不宕机》
- 如何使用 YAML 描述 Deployment

把它和 Job对比一下,你会发现有相似也有不同。相似的地方是都有“spec”“template”字段,“template”字段里也是一个 Pod;不同的地方在于它的“spec”部分多了 replicas、selector 这两个新字段,这两个新字段就是 Deployment 实现多实例、高可用等功能的关键所在。
-
Deployment 的关键字段
在线业务和离线业务(Job/CronJob)的应用场景差异很大。离线业务中的 Pod 基本上是一次性的,只与这个业务有关,紧紧地绑定在 Job 对象里,一般不会被其他对象所使用。
而在线业务就要复杂得多了,因为 Pod 永远在线,除了要在 Deployment 里部署运行,还可能会被其他的 API 对象引用来管理,比如负责负载均衡的 Service 对象。所以 Deployment 和 Pod 实际上是一种松散的组合关系,Deployment 实际上并不“持有”Pod 对象,它只是帮助 Pod 对象能够有足够的副本数量运行,仅此而已。
通过标签这种设计,Kubernetes 解除了 Deployment 和模板里 Pod 的强绑定,把组合关系变成了“弱引用”。 -
如何使用 kubectl 操作 Deployment
在 Deployment 部署成功之后,你还可以随时调整 Pod 的数量,实现所谓的“应用伸缩”
kubectl scale
在使用 kubectl get 命令的时候,加上参数 -l,使用 ==、!=、in、notin 的表达式,就能够很容易地用“标签”筛选、过滤出所要查找的对象:
《19|Daemonset:忠实可靠的看门狗》
- 为什么要有 DaemonSet
DaemonSet 的目标是在集群的每个节点上运行且仅运行一个 Pod,就好像是为节点配上一只“看门狗” - 如何使用 YAML 描述 DaemonSet
在 Kubernetes 的官网上有DaemonSet 的 YAML 示例

DaemonSet 在 spec 里没有 replicas 字段,这是它与 Deployment 的一个关键不同点,意味着它不会在集群里创建多个 Pod 副本,而是要在每个节点上只创建出一个 Pod 实例。
- 什么是污点(taint)和容忍度(toleration)
我:污点(taint)是node的属性,容忍度(tolerations)是Pod的属性。如果一个node指定了效果为NoSchedule的污点,只有设置了对应容忍度的pod才可能调度到此node。
污点的不同效果(参考豆包):
| 效果 | 能否调度新 Pod | 存量运行 Pod |
|---|---|---|
| NoSchedule | 完全禁止 | 保留,不驱逐 |
| PreferNoSchedule | 尽量不调度,资源不够仍可调度 | 保留,不驱逐 |
| NoExecute | 完全禁止 | 直接驱逐 |
-
- 查看、编辑node的污点:
kubectl describe node master命令可以看到master 节点默认有一个 taint,名字是 node-role.kubernetes.io/master,它的效果是 NoSchedule,也就是说这个污点会拒绝 Pod 调度到本节点上运行,而 worker 节点的 taint 字段则是空的:
e.g., 去掉节点master上的指定taint(key为node-role.kubernetes.io/master,value为空,效果为NoSchedule):kubectl taint node master node-role.kubernetes.io/master:NoSchedule-; 这条命令去掉最后的-号,就是反过来给node加污点。
-
- 为 Pod 添加字段 tolerations,让它能够“容忍”某些“污点” :

“容忍度”并不是 DaemonSet 独有的概念,而是从属于 Pod,所以理解了“污点”和“容忍度”之后,你可以在 Job/CronJob、Deployment 里为它们管理的 Pod 也加上 tolerations,从而能够更灵活地调度应用。
- 为 Pod 添加字段 tolerations,让它能够“容忍”某些“污点” :
-
什么是静态 Pod
静态 Pod 不受 Kubernetes 系统的管控,不与 apiserver、scheduler 发生关系,由kubelet直接读取本地配置文件创建(默认在在节点的 /etc/kubernetes/manifests 目录)
《20|Service:微服务架构的应对之道》
-
为什么要有 Service
Service 的工作原理和 LVS、Nginx 差不多,Kubernetes 会给它分配一个静态 IP 地址,然后它再去自动管理、维护后面动态变化的 Pod 集合,当客户端访问 Service,它就根据某种策略,把流量转发给后面的某个 Pod。
豆包:Kube-proxy 支持三种代理模式:userspace、iptables、ipvs,集群同一时间仅启用其中一种。每个节点的 kube-proxy 会根据当前选定的模式,自动维护对应底层转发规则,实现 Service 到后端 Pod 的流量转发与负载均衡。 -
如何使用 YAML 描述 Service
service使用命令kubectl expose而非kubectl create进行创建,需要用参数 --port 和 --target-port 分别指定映射端口和容器端口(豆包: ServiceIP:port → 转发到 PodIP:targetPort)。e.g. , 为deployment ngx-dep对象生成 Service YAML:
export out="--dry-run=client -o yaml"
kubectl expose deploy ngx-dep --port=80 --target-port=80 $out
可以看到Kubernetes为我们自动填上了deployment ngx-dep的标签
我的观察:pod.spec.containers.ports.containerPort 不会影响程序在容器内部的行为(即它监听的端口)。
豆包说它的真正作用有:1)创建 Service 时,如果只写 port 不写 targetPort,K8s 会自动按ServiceIP:port → PodIP:containerPort 转发;2)健康探针默认端口等。
-
如何在 Kubernetes 里使用 Service
此例中Service 对象管理了两个 endpoint。把 Pod 的地址与 Service 的信息做个对比,我们就能够验证 Service 确实用一个静态 IP 地址代理了两个 Pod 的动态 IP 地址:
-
如何以域名的方式使用 Service
使用命令 kubectl get ns 来查看当前集群里都有哪些名字空间(namespace)。
Service 对象的域名完全形式是“对象. 名字空间.svc.cluster.local”
我:一般来说,k8s集群内的所有Pod和所有Node, 都能访问Service的ClusterIP。比如我在worker节点上,执行curl clusterIP能成功,是因为所在节点的kube-proxy按 iptables/ipvs 规则拦截了TCP 流量,转发到Pod。而与k8s节点在同一网段的其它机器(换句话说,互相能ping通,但它不在k8s集群),curl clusterIP自然就不能成功了。
我又观察到在worker node上能curl clusterIP成功,却不能成功curl service的域名。而在进入一个pod后,两者都能成功。豆包的解释:service的域名解析依赖CoreDNS,CoreDNS为集群内Pod做DNS解析配置,但不会自动为Node配置。
- 如何让 Service 对外暴露服务
Service 对象有一个关键字段type,表示 Service 是哪种类型的负载均衡。除了默认值ClusterIP,Service 还支持其他三种类型,分别是ExternalName, LoadBalancer, NodePort。ClusterIP只能在集群内部访问。
NodePort 类型的 Service,会在每个节点上都开端口,然后使用 kube-proxy 路由到真正的后端 Service;
而且它要求向外界暴露节点的 IP 地址,这在很多时候是不可行的。

《21|Ingress:集群进出流量的总管》
-
为什么要有 Ingress
Service本质上是一个由 kube-proxy 控制的四层负载均衡,在 TCP/IP 协议栈上转发流量。但在四层上的负载均衡功能还是太有限了。而且Service比较适合代理集群内部的服务。如果想要把服务暴露到集群外部,就只能使用 NodePort 或者 LoadBalancer 这两种方式,而它们都缺乏足够的灵活性,难以管控。
-
为什么要有 Ingress Controller
Service 本身是没有服务能力的,它只是一些 iptables 规则,真正配置、应用这些规则的实际上是节点里的 kube-proxy 组件。如果没有 kube-proxy,Service 定义得再完善也没有用。
同样的,Ingress 也只是一些 HTTP 路由规则的集合(YAML 配置),真正要把这些规则在集群里实施运行,靠的是 Ingress Controller。它的作用就相当于 Service 的 kube-proxy。 -
为什么要有 IngressClass
不同的Ingress Class可用来定义不同业务网关,让Ingress 和 Ingress Controller解耦 -
如何使用 YAML 描述 Ingress/Ingress Class
Ingress 也是可以使用 kubectl create 来创建样板文件的,它需要用两个附加参数:--class,指定 Ingress 从属的 Ingress Class 对象。--rule,指定路由规则,基本形式是“URI=Service”,也就是说是访问 HTTP 路径就转发到对应的 Service 对象。
e.g. :
export out="--dry-run=client -o yaml"
kubectl create ing ngx-ing --rule="ngx.test/=ngx-svc:80" --class=ngx-ink $out
生成的Ingress 的 YAML 里,有两个关键字段:ingressClassName和rules,分别对应了上述命令行参数。
Ingress Class的YAML在spec里只有一个必需的字段controller,表示要使用哪个 Ingress Controller,比如Nginx 开发的 Ingress Controller,那么就要用名字“nginx.org/ingress-controller”
ingress yaml中的spec.rules[].host 用来匹配HTTP header中的Host字段。
- 如何在 Kubernetes 里使用 Ingress Controller
安装Nginx Ingress Controller。评论里提到只运行正文中的那4个YAML是不行的,得运行教程项目ingress目录下的setup.sh。
下载kic.yml文件并略微修改,运行, Ingress Controller 就算是运行起来了。
最后,因为 Ingress Controller 本身也是一个 Pod,想要向外提供服务还要再为它定义一个 Service,使用 NodePort 或者 LoadBalancer 暴露端口。
curl --resolve 指定域名的解析规则,不会影响发送的HTTP header中的Host字段。
我发现kubectl port-forward 这条命令,即使在console机器(指非k8s节点,只是安装了kubectl的机器)上,都可以成功运行。
- 小结

评论中有提到:Ingress只支持http/1.1,所以不支持grpc。其他的协议需要用Ingress Controller的CRD。
据豆包说:Ingress不再维护,新的:Gateway
《22|实战演练:玩转Kubernetes(2)》
-
WordPress 网站基本架构
网站对外提供服务我选择了两种方式。一种是让 WordPress 的 Service 对象以 NodePort 的方式直接对外暴露端口 30088,方便测试;另一种是给 Nginx Ingress Controller 添加hostNetwork属性,直接使用节点上的端口号,类似 Docker 的 host 网络模式,好处是可以避开 NodePort 的端口范围限制。 -
- WordPress 网站部署 WordPress
为 WordPress 创建 Service 对象,这里使用了NodePort类型,并且手工指定了端口号30088(必须在 30000~32767 之间):
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
部署成功后,就可以在浏览器里通过http://nodeIP:30088看到 WordPress 的安装界面了。
-
- WordPress 网站部署 Nginx Ingress Controller
这个 Ingress Controller 不使用 Service,而是给它的 Pod 加上一个特殊字段 hostNetwork,让 Pod 能够使用宿主机的网络。
我的补充:这Pod containers下的securityContext.allowPrivilegeEscalation字段要设为true,否则这个pod不能ready。豆包的解释:配置用的普通用户nginx,不是root, 配置了hostNetwork,又需要监听宿主机的80、443、8081等端口。普通用户直接开80端口会被内核拒绝,需要提权。
为了在集群外的主机上必须能够识别我们的wp.test域名,也就是说要把域名wp.test解析到 Ingress Controller 所在的节点上。因为我用的是 Windows,就修改 C:\Windows\System32\Drivers\etc\hosts,添加一条解析规则:192.168.56.104 wp.test
我的补充:千万记得关闭VPN的DNS覆写:
评论区有个好问题:wp.test这个域名需要指定ingress controller那个pod所在的节点ip,而ingress controller是通过deployment来管理的,pod重建时可能会被部署到别的节点,这样不是又要改hosts配置了吗?生产环境是怎样解决这个问题的?作者回复: 生产环境一般为Ingress Controller配置type LoadBalancer的service,云厂商会自动分配IP地址。
《23|视频:中级篇实操总结》
- 七. Ingress 的使用
-
- 视频1:43
Ingress负载均衡算法字段metadata.annotations.nginx.org/lb-method(豆包说是旧版)
- 视频1:43
《加餐|docker-compose:单机环境下的容器编排工具》
- 什么是 docker-compose
- 如何使用 docker-compose
我注:和第17小节一样,有版本冲突,需要安装docker时指定版本,比如20.10.12-0ubuntu4
下载docker-compose:
sudo curl -SL https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
docker-compose version #验证
docker-compose 里管理容器的核心概念是service。注意,它与 Kubernetes 里的 Service 虽然名字很像,但却是完全不同的东西
services:
registry:
image: registry
container_name: registry
restart: always
ports:
- 5000:5000
启动应用:docker-compose -f reg-compose.yml up -d :
停止应用,使用 docker-compose down 命令:docker-compose -f reg-compose.yml down
- 使用 docker-compose 搭建 WordPress 网站
services:
mariadb:
image: mariadb:10
container_name: mariadb
restart: always
environment:
MARIADB_DATABASE: db
MARIADB_USER: wp
MARIADB_PASSWORD: 123
MARIADB_ROOT_PASSWORD: 123
wordpress:
image: wordpress:5
container_name: wordpress
restart: always
environment:
WORDPRESS_DB_HOST: mariadb
WORDPRESS_DB_USER: wp
WORDPRESS_DB_PASSWORD: 123
WORDPRESS_DB_NAME: db
depends_on:
- mariadb
nginx:
image: nginx:alpine
container_name: nginx
hostname: nginx
restart: always
ports:
- 80:80
volumes:
- ./wp.conf:/etc/nginx/conf.d/default.conf
depends_on:
- wordpress
字段 depends_on,它用来设置容器的依赖关系,指定容器启动的先后顺序
更多推荐


所有评论(0)