admin管理员组文章数量:1130349
安装虚拟化服务器平台
- KVM /QEMU /LIBVIRTD
必备软件
qemu - kvm
为 kvm 提供底层仿真支持
libvirt - daemon
libvirtd 守护进程,管理虚拟机
libvirt - client
用户端软件,提供客户端管理命令
libvirt - daemon - driver - qemulibvirtd
连接 qemu 的驱动
- KVM 是 linux 内核的模块,它需要 CPU 的支持,采用硬件辅助虚拟化技术 Intel-VT,AMD-V,内存的相关如 Intel 的 EPT 和 AMD 的 RVI 技术
- QEMU 是一个虚拟化的仿真工具,通过 ioctl 与内核 kvm 交互完成对硬件的虚拟化支持
- Libvirt 是一个对虚拟化管理的接口和工具,提供用户端程序 virsh, virt-install, virt-manager, virt-view 与用户交互
虚拟化平台的安装 yum install -y qemu-kvm \ libvirt-daemon \ libvirt-client \ libvirt-daemon-driver-qemu systemctl start libvirtd虚拟机的组成
- 内核虚拟化模块(KVM) - 系统设备仿真(QEMU) - 虚拟机管理程序(LIBVIRT)
一个 XML 文件(虚拟机配置声明文件)
- 位置 /etc/libvirt/qemu/ -
一个磁盘镜像文件(虚拟机的硬盘)
- 位置 /var/lib/libvirt/images/
virsh 命令工具介绍
- 提供管理各虚拟机的命令接口
- 支持交互模式,查看 / 创建 / 停止 / 关闭 …
- 格式:virsh 控制指令 [虚拟机名称] [参数]
[root@nova01 ~]# virsh
Welcome to virsh, the virtualization interactive terminal.
Type: 'help' for help with commands
'quit' to quit
virsh #
virsh 虚拟机管理
- list [--all] 列出虚拟机
- start|shutdown|reboot 虚拟机启动,停止,重启
- destroy 强制停止虚拟机
- define|undefine 根据 xml 文件 创建/删除 虚拟机
- console 连接虚拟机的 console ctrl + ] 退出
- edit 修改虚拟机的配置
- autostart 设置虚拟机自启动
- domiflist 查看虚拟机网卡信息
- domblklist 查看虚拟机硬盘信息
virsh 虚拟网络管理
- net-list [--all] 列出虚拟网络
- net-start 启动虚拟交换机
- net-destroy 强制停止虚拟交换机
- net-define 根据 xml 文件 创建虚拟网络
- net-undefine 删除一个虚拟网络设备
- net-edit 修改虚拟交换机的配置
- net-autostart 设置虚拟交换机自启动
qemu-img命令
- qemu-img 是虚拟机的磁盘管理命令,支持非常多的磁盘格式,例如raw、qcow2、vdi、vmdk等等
-qemu-img 命令格式
- qemu-img 命令 参数 块文件名称 大小
- 常用的命令有
- create 创建一个磁盘 qemu-img create -f <格式> <镜像路径> <大小>
- convert 转换磁盘格式 qemu-img convert -f <原格式> -O <目标格式> <原镜像路径> <目标镜像路径>
- info 查看磁盘信息 qemu-img info <镜像路径>
- resize 扩容磁盘空间
qemu-img resize <镜像路径> <新大小> # 增加或减少大小(+N 表示增加,-N 表示减少,直接写数字表示设置为固定大小)
创建新的镜像盘文件
qemu-img create -f 格式 磁盘路径 大小
qemu-img create -f qcow2 disk.img 50G
查询镜像盘文件的信息
qemu-img info 磁盘路径
qemu-img info disk.img
-b 使用后端模板文件
qemu-img create -b disk.img -f qcow2 disk1.img
COW技术原理
- Copy On Write,写时复制 - 直接映射原始盘的数据内容 - 当数据有修改要求时,在修改之前自动将旧数据拷贝存入前端盘后,对前端盘进行修改 - 原始盘始终是只读的
创建
若修改 磁盘
root@kvmsvr ~]# virsh edit node0
...
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/node0.img'/> 改这个
<target dev='vda' bus='virtio'/>
</disk>
若修改 网络
[root@kvmsvr ~]# virsh edit node0
...
<interface type='bridge'>
<source bndge='vbr'/> 改这个
<model type='virtio'/>
</interface>
创建新的
[root@destop ~]# qemu-img create -b .node_base.qcow2 -f qcow2 tang.img 10G
[root@destop ~]# vim /etc/libvirt/qemu/nsd.xml
<name>tang</name>
<source file='/var/lib/libvirt/images/tang.img'/>
[root@destop ~]# virsh define /etc/libvirt/qemu/nsd.xml
[root@destop ~]# virsh console nsd
快建虚拟机脚本
[root@destop ~]# vim clone.sh
[root@destop ~]# vim clone.sh
[root@destop ~]# chmod 755 clone.sh
[root@destop ~]# cat clone.sh
#!/bin/bash
name=$1
cd /var/lib/libvirt/images/
if [ -e ${name}.img ];then
echo "file exits"
exit 1
else
qemu-img create -f qcow2 -b .nsd.qcow2 ${name}.img 20G
fi
cd /etc/libvirt/qemu/
if [ -e ${name}.img ];then
echo "file exits"
exit 1
else
sed "s,nsd,${name},g" /etc/libvirt/qemu/nsd.xml > /etc/libvirt/qemu/${name}.xml
fi
网卡及配置文件
- 网络配置文件说明 - /etc/sysconfig/network-scripts/ifcfg-eth0
# Generated by dracut initrd 注释
DEVICE="eth0" # 驱动名称,与ifconfig 看到的名称一致
ONBOOT="yes" # 开机启动
NM_CONTROLLED="no" # 不接受
NetworkManager 控制 TYPE="Ethernet" #类型
BOOTPROTO="static" #协议(dhcp|static|none)
IPADDR="192.168.1.10" #IP地址
NETMASK="255.255.255.0" #子网掩码
GATEWAY="192.168.1.254" #默认网关
virsh 扩容磁盘
只能扩不能缩
- virsh 虚拟机管理
- domblklist 查看虚拟机硬盘信息
- blockresize --path [绝对路径] --size 50G openstack -
扩容思路: - 首先是硬盘 - 其次是分区 - 再次是文件系统
真机
1 virsh domblklist ooxx
2 virsh blockresize --path /var/lib/libvirt/images/ooxx.img --size 50G ooxx
虚拟机
3 lsblk
4 growpart /dev/vda 1 (LANG=C)
5 xfs_growfs /dev/vda1
6 df -h
知道云之间的优缺点
云平台
基于互联网的相关服务的增加、使用和交付模式这种模式提供可用的、便捷的、按需的网络访问,
进入可配置的计算资源共享池这些资源能够被快速提供,只需投入很少的管理工作,或与服务供应商进行很少的交互通常涉及通过互联网来提供动态易扩展且经常是虚拟化的资源
IaaS
- IaaS(Infrastructure as a Service),即基础设施即服务
- 提供给消费者的服务是对所有计算基础设施的利用,包括处理 CPU、内存、存储、网络和其它基本的计算资源,用户能够部署和运行任意软件,包括操作系统和应用程序
- IaaS 通常分为三种用法:公有云、私有云和混合云
PaaS
- PaaS (Platform-as-a-Service),意思是平台即服务
- 以服务器平台或者开发环境作为服务进行提供就成为了 PaaS
- PaaS 运营商所需提供的服务,不仅仅是单纯的基础平台,还针对该平台的技术支持服务,甚至针对该平台而进行的应用系统开发、优化等服务
- 简单地说,PaaS 平台是指云环境中的应用基础设施服务,也可以说是中间件即服务
SaaS
- SaaS(Software-as-a-Service)软件即服务,是一种通过 Internet 提供软件的模式,厂商将应用软件统一部署在自己的服务器上,客户可以根据自己实际需求,通过互联网向厂商定购所需的应用软件服务
- 用户不用再购买软件,而是向提供商租用基于 Web 的软件,来管理企业经营活动,不用对软件进行维护,提供商会全权管理和维护软件,同时也提供软件的离线操作和本地数据存储
什么是云?
- 对于到底什么是云计算,至少可以找到 100 种解释
- 现阶段广为接受的是美国国家标准与技术研究院(NIST)定义:
- 云计算是一种按使用量付费的模式,这种模式提供可用的、便捷的、按需的网络访问,进入可配置的计算资源共享池(包括网络,服务器,存储,应用软件,服务),这些资源能够被快速提供,只需投入少量的管理工作,或与服务供应商进行很少的交互
云计算产品选购建议
- 要考虑服务商的知名度、靠谱度
- 以个人 / 企业实际需求为本
- 比如云主机、数据库、CDN、安全等,选择性价比最优的
openstack
openstack结构图
openstack主要组件
Horizon
-
用于管理 Openstack 各种服务的、基于 web 的管理接口
-
通过图形界面实现创建用户、管理网络、启动实例等操作
Keystone
-
为其他服务提供认证和授权的集中身份管理服务
-
也提供了集中的目录服务
-
支持多种身份认证模式,如密码认证、令牌认证、以及 AWS(亚马逊 Web 服务)登陆
-
为用户和其他服务提供了 SSO 认证服务
Neutron
-
一种软件定义网络服务
-
用于创建网络、子网、路由器、管理浮动 IP 地址
-
可以实现虚拟交换机、虚拟路由器
-
可用于在项目中创建 VPN
Cinder
-
为虚拟机管理存储卷的服务
-
为运行在 Nova 中的实例提供永久的块存储
-
可以通过快照进行数据备份
-
经常应用在实例存储环境中,如数据库文件
Glance
-
扮演虚拟机镜像注册的角色
-
允许用户为直接存储拷贝服务器镜像
-
这些镜像可以用于新建虚拟机的模板
Nova
-
在节点上用于管理虚拟机的服务
-
Nova 是一个分布式的服务,能够与 Keystone 交互实现认证,与 Glance 交互实现镜像管理
-
Nova 被设计成在标准硬件上能够进行水平扩展
-
启动实例时,如果有则需要下载镜像
虚拟机配置
- 准备虚拟机3台,
配置如下 - openstack 管理主机 - 2CPU,6G 内存,50G 硬盘
配置静态IP:192.168.1.10
-nova01,nova02 计算节点 * 2 - 2CPU,4.5G 内存,100G 硬盘
- 配置静态IP:192.168.1.11(12)
配置DNS - 系统环境准备 - openstack 安装时候需要使用外部 dns 来解析域名
vim /etc/resolv.conf
# Generated by NetworkManager
nameserver 114.114.114.114 设置真机DNS地址
注:去掉search开头的所有行
- 将 openstack.tedu 域名对应的 IP 解析到我们安装的 openstack 服务器
vim /etc/hosts
192.168.1.10 openstack
192.168.1.11 nova01
192.168.1.12 nova02
-注:DNS 服务器不能与 openstack 安装在同一台主机上
时间服务
- nova 服务器之间的时间必须保持一致
- 编辑配置文件 /etc/chrony.conf
- server 192.168.1.254 iburst
- 重启服务 - systemctl restart chronyd
- 测试服务 chronyc sources -v
//出现*号代表NTP时间可用
^* gateway 2 6 17 62 -753us[-7003us]+/- 24ms
配置yum仓库
- CentOS7-1804.iso 系统软件
- RHEL7-extras.iso 提供 Python 依赖软件包
- RHEL7OSP-10.iso 光盘拥有众多目录,每个目录都是一个软件仓库,我们配置其中2个软件仓库 - openstack 主要软件仓库 rhel-7-server-openstack-10-rpms
- packstack 软件仓库 rhel-7-server-openstack-10-devtools-rpms
安装额外软件包
安装openstack期间,有些软件包所依赖的软件包,并没有在安装过程中安装,这些软件包需提前安装
- qemu-kvm - libvirt-daemon - libvirt-daemon-driver-qemu - libvirt-client - python-setuptools
- 软件包安装
yum install -y qemu-kvm libvirt-client libvirt-daemon libvirt-daemon-driver-qemu python-setuptools
检查基础环境
- 是否卸载firewalld 和 NetworkManager
[root@destop ~]# rpm -qa |grep firewalld
[root@destop ~]# rpm -qa |grep NetworkManager
- 检查配置主机网络参数(静态IP)
root@destop ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens33
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=none
- 主机名必须能够相互 ping 通
[root@destop ~]# ping c1
- 检查配置主机yum源(4个,10670)
- 依赖软件包是否安装 - 检查NTP服务器是否可用
[root@destop ~]# systemctl start libvirtd
[root@destop ~]# systemctl status libvirtd
- 检查 /etc/resolv.conf 不能有 search 开头的行
[root@openstack ~]# bash 退出
[root@openstack ~]# . keystonerc_admin
[root@openstack ~(keystone_admin)]# exit
exit
用户与配额管理
项目管理
项目:一组隔离的资源和对象。由一组关联的用户进行管理
在旧版本里,也用租户(tenant)来表示
根据配置的需求,项目对应一个组织、一个公司或是一个使用客户等
项目中可以有多个用户,项目中的用户可以在该项目创建、管理虚拟资源
具有 admin 角色的用户可以创建项目
项目相关信息保存到 MariaDB 中
缺省情况下,packstack安装的openstack中有两个独立的项目
- admin:为admin账户创建的项目
- services:与安装的各个服务相关联
基本概念
- 用户在openstack中用于身份认证
- 管理员用户admin一般在packstack安装过程中创建
- 其他用户由管理员用户创建,并指定可以访问的项目
- 非管理员用户创建后,保存到MariaDB中
非管理员用户具有以下权限
启动实例
-创建卷和快照
创建镜像
分配浮动IP
创建网络和路由器
创建防火墙以及规则、规则策略
查看网络拓扑、项目使用概况等
网页操作
创建项目
创建用户
通过命令行管理项目
创建名为 myproject 项目
[root@openstack ~(keystone_admin)]# openstack project create myproject
列出所有项目
[root@openstack ~(keystone_admin)]# openstack project list
查看 myproject 详细信息
[root@openstack ~(keystone_admin)]# openstack project show myproject
禁用与激活项目
[root@openstack ~(keystone_admin)]# openstack project set --disable myproject [root@openstack ~(keystone_admin)]# openstack project set --enable myproject
查看项目配额及相关操作
查看项目配额
[root@openstack ~(keystone_admin)]# nova quota-show --tenant myproject
更新可用 vCPU 数目为 30
[root@openstack ~(keystone_admin)]# nova quota-update --cores 30 myproject
删除 myproject
[root@openstack ~(keystone_admin)]# openstack project delete myproject
创建用户
通过命令行管理用户
- 创建user2用户,指定密码为tedu
[root@openstack ~(keystone_admin)]# openstack user create --password tedu user2
- 设置user2的email地址
[root@openstack ~(keystone_admin)]# openstack user set --email user2@tedu user2
- 列出所有用户
[root@openstack ~(keystone_admin)]# openstack user list
- 查看user2信息
[root@openstack ~(keystone_admin)]# openstack user show user2
指定user2可以访问myproject,角色为_member_
[root@openstack ~(keystone_admin)]# openstack role add --user user2 --project myproject _member_
查看user2在myproject中的角色
[root@openstack ~(keystone_admin)]# openstack role list --project myproject --user user2
禁用用户
[root@openstack ~(keystone_admin)]# openstack user set --disable user2
激活用户
[root@openstack ~(keystone_admin)]# openstack user set --enable user2
修改user2的密码为redhat
[root@openstack ~(keystone_admin)]# openstack user set --password redhat user2
将user2从myproject中移除
[root@openstack ~(keystone_admin)]# openstack role remove --project myproject --user user2 _member_
删除user2用户
[root@openstack ~(keystone_admin)]# openstack user delete user2
通过命令行管理配额
- 列出项目的缺省配额
[root@openstack ~(keystone_admin)]# nova quota-defaults
- 列出myproject的配额
[root@openstack ~(keystone_admin)]# nova quota-show --tenant myproject
- 修改浮动IP地址配额
[root@openstack ~(keystone_admin)]# nova quota-update --floating-ips 20 myproject
配额管理
配额基础
管理员可以通过配额限制,防止资源的过度使用
配额基本项目,限制每个项目可以使用多少资源
这些操作上的功能限制,赋予了管理员对每个项目的精准控制
资源参数
安全组规则:指定每个项目可用的规则数
核心:指定每个项可用的VCPU核心数
固定IP地址:指定每个项目可用的固定IP数
浮动IP地址:指定每个项目可用的浮动IP数
注入文件大小:指定每个项目内容大小
注入文件路径:指定每个项目注入的文件路径长度
注入文件:指定每个项目允许注入的文件数目
实例:指定每个项目可创建的虚拟机实例数目
密钥对:指定每个项可创建的密钥数
元数据:指定每个项目可用的元数据数目
内存:指定每个项目可用的最大内存
安全组:指定每个项目可创建的安全组数目
云主机类型
通过命令行管理云主机类型
- 列出所有的云主机类型
[root@openstack ~(keystone_admin)]# openstack flavor list
- 建一个云主机类型
[root@openstack ~(keystone_admin)]# openstack flavor create --public demo.tiny --id auto --ram 512 --disk 10 --vcpus 1
- 删除云主机类型
[root@openstack ~(keystone_admin)]# openstack flavor delete demo.tiny
镜像管理
基本概念
- 在红帽Openstack平台中,镜像指的是虚拟磁盘文件,磁盘文件中应该已经安装了可启动的操作系统
- 镜像管理功能由Glance服务提供
- 它形成了创建虚拟机实例最底层的块结构
- 镜像可以由用户上传,也可以通过红帽官方站点下载
Glance磁盘格式
raw:非结构化磁盘镜像格式
vhd:VMware、Xen、Microsoft、VirtualBox等均支持的通用磁盘格式
vmdk:是Vmware的虚拟磁盘格式
vdi:VirtualBox虚拟机和QEMU支持磁盘格式
iso:光盘数据内容的归档格式
qcow2:QEMU支持的磁盘格式。空间自动扩展,并支持写时复制copy-on-write
镜像容器格式
bare :镜像中没有容器或元数据封装
ovf:一种开源的文件规范,描述了一个开源、安全、有效、可拓展的便携式虚拟打包以及软件分布格式
ova:OVA归档文件
aki:亚马逊内核镜像
ami:亚马逊主机镜像
通过命令行管理镜像
上传镜像
[root@openstack ~(keystone_admin)]# openstack image create --disk-format qcow2 --min-disk 10 --min-ram 512 --file /root/small.img small_rhel6
- **列出镜像**
[root@openstack ~(keystone_admin)]# openstack image list
- **查看镜像详情**
[root@openstack ~(keystone_admin)]# openstack image show small_rhel6
镜像其他操作
修改镜像属性
[root@openstack ~(keystone_admin)]# openstack image set --public small_rhel6
另存镜像为本地文件
[root@openstack ~(keystone_admin)]# openstack image save --file /tmp/small_rhel6.img small_rhel6
-删除镜像
[root@openstack ~(keystone_admin)]# openstack image delete small_rhel6
网络管理
Openstack网络工作原理
实例被分配到子网中,以实现网络连通性
每个项目可以有一到多个子网
在红帽的Openstack平台中,OpenStack网络服务是缺省的网络选项,Nova网络服务作为备用 管理员能够配置丰富的网络,将其他Openstack服务连接到这些网络的接口上
每个项目都能拥有多个私有网络,各个项目的私有网络互相不受干扰
构建内网网络
wan的ip必须是关联的网卡
添加路由
浮动ip地址
浮动IP地址的作用
浮动IP一般是花钱购买的
浮动IP地址用于从外界访问虚拟机实例
浮动IP地址只能从现有浮动IP地址池中分配
创建外部网络时,浮动IP地址池被定义
虚拟机实例起动后,可以为其关联一个浮动IP地址
虚拟机实例也可以解除IP地址绑定
在 OpenStack 中,浮动 IP(Floating IP)用于将实例(虚拟机)从私有网络暴露到外部网络,使实例能够与外部通信(如访问互联网或被外部访问)
创建新的
内网通信是采用裸奔
计算节点扩容
扩容 openstack 计算节点
参考 nova01 配置过程
配置静态 ip:192.168.1.12,及主机名
保证与 openstack,和 nova01 能相互 ping 通
配置 /etc/resolv.conf,删除 search 开头行
配置时间同步 /etc/chrony.conf
配置 yum 源,软仓库一共 4 个(10670)
安装依赖软件包 qemu-kvm libvirt-client libvirt-daemon libvirt-daemon-driver-qemu python-setuptools
修改应答文件
在 openstack 上修改配置文件
[root@openstack ~]# vim answer.ini
98: CONFIG_COMPUTE_HOST=192.168.1.11,192.168.1.12
102:CONFIG_NETWORK_HOSTS=192.168.1.10,192.168.1.11,192.168.1.12
packstack --answer-file=answer.ini
[root@openstack ~]# vim /etc/httpd/conf.d/15-horizon_vhost.conf
WSGIProcessGroup apache WSGIApplicationGroup %{GLOBAL}
创建云主机是随机在nova上的
每隔一秒执行一次命令
watch -n 1 'virsh list --name'
容器
容器技术作为应用程序封装与交付的核心技术,其核心依赖于几个关键的内核技术来实现高效、安全的应用隔离与资源管理:
Cgroups(Control Groups):用于对容器的资源(如 CPU、内存、磁盘 I/O 等)进行精细化限制与分配,确保容器间资源使用互不干扰,合理利用物理机资源。
Namespace:进程隔离,通过创建不同的命名空间,实现进程、网络、文件系统等方面的隔离,让每个容器都像在独立的系统环境中运行,互不影响。
SELinux:提供安全增强功能,对容器的访问权限等进行严格控制,提升容器运行的安全性。
Docker
Docker是完整的一套容器管理系统
Docker提供了一组命令,让用户更加方便直接地使用容器技术,而不需要过多关心底层内核技术
Docker优点
相比于传统的虚拟化技术,容器更加简洁高效
传统虚拟机需要给每个VM安装操作系统
容器使用的共享公共库和程序
docker镜像
在Docker中容器是基于镜像启动的
镜像是启动容器的核心
镜像采用分层设计
使用快照的COW技术,确保底层数据不丢失
[root@localhost images]# qemu-img create -b node.qcow2 -f qcow2 disk1.img
Formatting 'disk1.img', fmt=qcow2 size=2147483648 backing_file='node.qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
[root@localhost images]# guestmount -a disk1.img -i /mnt/
[root@localhost images]# cd /mnt/
[root@localhost mnt]# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
[root@localhost mnt]# mkdir disk1
[root@localhost mnt]# cd -
/var/lib/libvirt/images
[root@localhost images]# umount /mnt/
[root@localhost images]# ll
total 650756
-rw-r--r-- 1 root root 2686976 Sep 4 10:28 disk1.img
-rw-r--r-- 1 qemu qemu 663486464 Sep 4 10:27 node.qcow2
[root@localhost images]# qemu-img create -b disk1.img -f qcow2 disk2.img
Formatting 'disk2.img', fmt=qcow2 size=2147483648 backing_file='disk1.img' encryption=off cluster_size=65536 lazy_refcounts=off
[root@localhost images]# guestmount -a disk2.img -i /mnt/
[root@localhost mnt]# ls
bin dev etc lib media opt root sbin sys usr
boot disk1 home lib64 mnt proc run srv tmp var
采用分层设计
使用阿里云加速拉取
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://33qvo5mf.mirror.aliyuncs"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
查看镜像
[root@destop aa]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest a95d91903603 55 minutes ago 1.08 GB
myos lastest 1268a218ae03 About an hour ago 796 MB
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@destop aa]#
查看镜像
[root@docker1 ~]# docker search busybox
[root@docker1 ~]# docker search centos
[root@docker1 ~]# docker search nginx
下载镜像
像拉取cnetos7,nginx.1.12
[root@destop ~]# docker pull centos:7
[root@destop ~]# docker pull nginx:1.12
上传镜像
docker push docker.io/busybox
导出镜像(将centos7导出为 tar 文件)
[root@destop ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest a95d91903603 About an hour ago 1.08 GB
myos lastest 1268a218ae03 About an hour ago 796 MB
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@destop ~]# docker save -o httpd.tar myos:httpd
导入镜像(通过 tar 包文件导入镜像)
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
[root@localhost ~]# ls busybox.tar
busybox.tar
root@localhost ~]# docker load -i centos7.tar
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@localhost ~]#
容器的6大命名空间
镜像常用命令
启动镜像
docker run [选项] 镜像名:标签 [容器内执行的命令]
每一次启动镜像,都会创造一个容器
[root@docker1 ~]# docker images
[root@destop ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
[root@destop ~]# docker run -it docker.io/centos:7 /bin/bash
docker run = 创建新容器 + 启动容器(每次执行都会产生新容器)
已存在的容器停止后,需用 docker start 启动(复用容器,不创建新的)
docker history // 查看镜像制作历史
docker history [选项] 镜像名:标签
-H 或 --human:以人类可读的格式显示大小(默认开启)
--no-trunc:显示完整的命令(默认会截断长命令)
-q 或 --quiet:只显示镜像层的 ID
[root@destop ~]# docker history docker.io/centos:7
IMAGE CREATED CREATED BY SIZE COMMENT
eeb6ee3f44bd 3 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
<missing> 3 years ago /bin/sh -c #(nop) LABEL org.label-schema.... 0 B
<missing> 3 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723... 204 MB
docker inspect // 查看镜像底层信息
[root@destop ~]# docker inspect docker.io/centos:7
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
docker rmi // 删除本地镜像
docker rmi [选项] 镜像名:标签 或 镜像ID
[root@destop ~]# docker rmi docker.io/centos:7
若镜像被运行中的容器引用,需先停止并删除容器(docker rm -f 容器ID),再删除镜像
若镜像有多个标签,docker rmi 只会删除指定标签,不会删除整个镜像。
docker tag // 修改镜像名称和标签
docker tag 原镜像名:原标签 新镜像名:新标签
[root@destop ~]# docker tag docker.io/nginx:1.12.2 docker.io/test:1
执行后,docker images 会显示两个条目(原标签和新标签),但它们指向同一个镜像 ID(共享镜像层)。
若要删除旧标签,可使用 docker rmi 原镜像名:原标签(不会删除镜像本身,仅删除标签)。
容器常用命令
docker ps // 查看容器列表
-a 显示所有容器(包括已停止的)
-l 只显示最近创建的一个容器(无论是否运行)
-n X 显示最近创建的 X 个容器(如 -n 3 显示最近 3 个)
-q 只显示容器的 ID(常用于批量操作,如 docker stop $(docker ps -q))
--format 自定义输出格式(如只显示容器名和状态:--format "table {{.Names}}\t{{.Status}}")
[root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
docker stop|start|restart // 关闭容器 启动容器 重启容器
[root@destop ~]# docker ps -aq
1c1372c09cde
127456ac9af4
1a2ec2e8ddb7
[root@destop ~]# docker start 1c1372c09cde
1c1372c09cde
[root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c1372c09cde test:latest "/bin/bash" About an hour ago Up 3 seconds peaceful_hamilton
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
[root@destop ~]#
docker attach|exec // 进入容器
docker attach:进入容器的主进程终端
特点
共享主进程终端:进入后与容器的主进程(如 bash、nginx)共享输入输出,主进程的输出会直接显示。
退出影响:如果在 attach 模式下执行 exit,会导致容器的主进程终止,容器随之停止。
适用场景:查看容器主进程的实时输出(如日志),或临时干预主进程。
root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c1372c09cde test:latest "/bin/bash" About an hour ago Up 3 seconds peaceful_hamilton
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
[root@destop ~]# docker attach 1c
[root@1c1372c09cde /]# exit
exit
[root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
[root@destop ~]#
docker exec [选项] 容器ID或名称 命令
常用选项
-i:保持标准输入打开
-t:分配伪终端(通常与 -i 组合为 -it 进入交互式终端)
-d:后台执行命令
docker exec:在容器内执行新命令特点
独立进程:新建的进程与容器主进程并行运行,互不干扰。
退出安全:执行 exit 只会退出当前新建的进程(如 bash),容器主进程和容器仍继续运行。
功能丰富:可执行任意命令(如查看文件、安装软件、启动服务等)。
[root@destop ~]# docker exec -it 1c /bin/bash
[root@1c1372c09cde /]#
docker top // 查看容器进程列表
[root@destop ~]# docker top 1c
UID PID PPID C STIME TTY TIME CMD
root 43793 43776 0 19:03 pts/5 00:00:00 /bin/bash
[root@destop ~]#
docker rm // 删除容器
docker rm [选项] 容器ID或容器名
常用选项
-f:强制删除(即使论容器是否运行中,都会先停止再删除)
-v:删除容器挂载的数据卷(若数据卷未被其他容器使用)
-l:删除容器关联的网络链接
注意事项
运行中的容器默认无法直接删除,需先停止(docker stop 容器ID)或使用 -f 强制删除。
若容器被标记为 restart: always,强制删除后可能被自动重启,需先修改重启策略再删除。
批量删除时建议先执行 docker ps -aq --filter "status=exited" 查看待删除容器,确认无误后再执行删除命令。
[root@destop ~]# docker rm 1c
在 Docker 操作场景里,Ctrl + P + Q 是一组很实用的快捷键组合,作用是退出容器的交互模式,但不停止容器运行
自定义镜像
docker commit
docker commit 命令用于将容器的当前状态(包括修改、安装的软件等)保存为一个新的镜像,实现容器到镜像的 “快照” 功能。
docker commit [选项] 容器ID或名称 新镜像名:标签
常用选项
-m "描述信息":为新镜像添加提交说明(类似代码提交的注释)
-a "作者信息":指定镜像的作者
[root@destop ~]# docker commit 1c myos:lastest
[root@destop ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest a95d91903603 About an hour ago 1.08 GB
myos lastest 1268a218ae03 2 hours ago 796 MB
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@destop ~]#
Dockerfile
docker build [选项] 构建上下文路径
Dockerfile 语法格式
FROM: 基础镜像
MAINTAINER: 镜像创建者信息
EXPOSE: 开放的端口
ENV: 设置变量
ADD: 复制文件到镜像
RUN: 制作镜像时执行的命令,可以有多个
WORKDIR: 定义容器默认工作目录
CMD: 容器启动时执行的命令,仅可以有一条 CMD 如果没写会继承上一个镜像的启动命令
[root@destop ~]# mkdir aa
[root@destop ~]# cd aa/
[root@destop aa]# touch Dockerfile
[root@destop aa]# vim Dockerfile
[root@destop aa]# docker build -t test:latest .
[root@destop aa]# cat Dockerfile
FROM docker.io/centos:7
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun/repo/Centos-7.repo
RUN yum clean all
RUN yum makecache
RUN yum -y install vim net-tools psmisc iproute
构建原理
-
Docker 守护进程读取 Dockerfile 和构建上下文。
-
按 Dockerfile 指令顺序执行,每执行一条指令(如
RUN、COPY)创建一个新的镜像层。 -
利用缓存加速构建:若指令未修改,且依赖的上一层未变化,则直接复用缓存层。
-
最终生成完整镜像,可通过
docker images查看。
做一个ssh服务的
[root@ec8826e4ccb9 /]# rpm -ql openssh-server 查看安装的文件
[root@destop bb]# cat Dockerfile
FROM myos:lastest
RUN yum -y install openssh-server initscripts
RUN sshd-keygen
RUN echo "redhat" | passwd --stdin root
ENV EnvironmentFile=/etc/sysconfig/sshd
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]
httpd的
[root@destop cc]# cat Dockerfile
FROM myos:lastest
RUN yum -y install httpd
WORKDIR /var/www/html
ENV EnvironmentFile=/etc/sysconfig/httpd
ENV PS1='[webserver@\h \W]\$ '
ADD index.html index.html
EXPOSE 80
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
[root@destop cc]# cat index.html
hello world
自定义仓库
安装私有仓库(服务端)
yum install docker-distribution
启动私有仓库,并设置开机自启动
systemctl start docker-distribution
systemctl enable docker-distribution
仓库配置文件及数据存储路径
/etc/docker-distribution/registry/config.yml
/var/lib/registry
自定义私有仓库
客户端配置:
修改配置文件 /etc/sysconfig/docker
允许非加密方式访问仓库
INSECURE_REGISTRY='--insecure-registry 仓库ip'
docker 仓库地址
ADD_REGISTRY='--add-registry 仓库ip'
重启 docker 服务
systemctl restart docker
[root@destop cc]# vim /etc/sysconfig/docker
# Do not add registries in this file anymore. Use /etc/containers/registries.conf
[root@destop cc]# vim /etc/containers/registries.conf
# To ensure compatibility with docker we've included docker.io in the default search list. However Red Hat
# does not curate, patch or maintain container images from the docker.io registry.
[registries.search]
registries = ['172.25.0.11:5000']
#registries = ['registry.access.redhat', 'registry.redhat.io', 'docker.io']
# Registries that do not use TLS when pulling images or uses self-signed
# certificates.
[registries.insecure]
registries = [172.25.0.11:5000]
存储持续化
[root@class ~]# vim /etc/exports
[root@class ~]# mkdir /var/webroot
[root@class ~]# chmod 777 /var/webroot/
[root@class ~]# systemctl restart nfs
[root@class ~]# cat /etc/exports
/var/webroot *(rw)
[root@class ~]#
k8s
应用部署方式演变
在部署应用程序的方式上,主要经历了三个时代:
传统部署:互联网早期,会直接将应用程序部署在物理机上
优点:简单,不需要其它技术的参与
缺点:不能为应用程序定义资源使用边界,很难合理地分配计算资源,而且程序之间容易产生影响
虚拟化部署:可以在一台物理机上运行多个虚拟机,每个虚拟机都是独立的一个环境
优点:程序环境不会相互产生影响,提供了一定程度的安全性
缺点:增加了操作系统,浪费了部分资源
容器化部署:与虚拟化类似,但是共享了操作系统
优点:
可以保证每个容器拥有自己的文件系统、CPU、内存、进程空间等
运行应用程序所需要的资源都被容器包装,并和底层基础架构解耦
容器化的应用程序可以跨云服务商、跨 Linux 操作系统发行版进行部署
kubernetes的本质是“一组服务器集群”,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。它的目的就是是实现资源管理的自动化,主要提供了如下的主要功能:
自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器
弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
服务发现:服务可以通过自动发现的形式找到它所依赖的服务
负载均衡:如果一个服务起动了多个容器,能够自动实现请求的负载均衡
版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
存储编排:可以根据容器自身的需求自动创建存储卷
kubernetes组件
一个kubernetes集群主要是由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。
master:集群的控制平面,负责集群的决策
- ApiServer:资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制
- Scheduler:负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上
- ControllerManager:负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等
- Etcd:负责存储集群中各种资源对象的信息
node:集群的数据平面,负责为容器提供运行环境
- Kubelet:负责维护容器的生命周期,即通过控制docker,来创建、更新、销毁容器
- KubeProxy:负责提供集群内部的服务发现和负载均衡
- Docker:负责节点上容器的各种操作
以部署一个nginx服务来说明kubernetes系统各个组件调用关系:
1. 首先要明确,一旦kubernetes环境启动之后,master和node都会将自身的信息存储到etcd数据库中
2. 一个nginx服务的安装请求会首先被发送到master节点的apiServer组件
3. apiServer组件会调用scheduler组件来决定到底应该把这个服务安装到哪个node节点上
在此时,它会从etcd中读取各个node节点的信息,然后按照一定的算法进行选择,并将结果告知apiServer
4. apiServer调用controller-manager去调度Node节点安装nginx服务
5. kubelet接收到指令后,会通知docker,然后由docker来启动一个nginx的pod
pod是kubernetes的最小操作单元,容器必须跑在pod中至此,
6. 一个nginx服务就运行了,如果需要访问nginx,就需要通过kube-proxy来对pod产生访问的代理
这样,外界用户就可以访问集群中的nginx服务了
安装方式
kubernetes有多种部署方式,目前主流的方式有kubeadm、minikube、二进制包
- minikube:一个用于快速搭建单节点kubernetes的工具
- kubeadm:一个用于快速搭建kubernetes集群的工具
- 二进制包:从官网下载每个组件的二进制包,依次去安装,此方式对于理解kubernetes组件更加有效
环境初始化
1、检查操作系统的版本
安装 kubernetes 集群要求 Centos 版本要在 7.5 或之上
2、主机名解析
为了方便后面集群节点间的直接调用,在这配置一下主机名解析,企业中推荐使用内部 DNS 服务器
主机名成解析 编辑三台服务器的 /etc/hosts 文件,添加下面内容:
3、时间同步
kubernetes 要求集群中的节点时间必须精确一致,这里直接使用 chronyd 服务从网络同步时间。
企业中建议配置内部的时间同步服务器
4、关闭系统规则(避免与 kubernetes、docker 产生的规则混淆)
[root@server ~]# systemctl stop firewalld
[root@server ~]# systemctl disable firewalld
[root@server ~]# systemctl stop iptables
[root@server ~]# systemctl disable iptables
5、禁用selinux
selinux是Linux系统下的一个安全服务,如果不关闭它,在安装集群中会产生各种各样的奇葩问题
# 编辑 /etc/selinux/config 文件,修改SELINUX的值为disabled
# 注意修改完毕之后需要重启linux服务
SELINUX=disabled
6、禁用swap分区
[root@server ~]# vim /etc/fstab
# /dev/mapper/centos-swap swap swap defaults 0 0
swap分区指的是虚拟内存分区,它的作用是在物理内存使用完之后,将磁盘空间虚拟成内存来使用
启用swap设备会对系统的性能产生非常负面的影响,因此kubernetes要求每个节点都要禁用swap设备
但是如果因为某些原因确实不能关闭swap分区,就需要在集群安装过程中通过明确的参数进行配置说明
7、修改linux的内核参数
# 修改linux的内核参数,添加网桥过滤和地址转发功能
# 编辑/etc/sysctl.d/kubernetes.conf文件,添加如下配置:
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
重新加载配置:
[root@server ~]# sysctl -p
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 1
net.ipv4.ip_forward = 1
加载网桥过滤模块:
[root@server ~]# modprobe br_netfilter
查看网桥过滤模块是否加载成功:
[root@server ~]# lsmod | grep br_netfilter
br_netfilter 22256 0
bridge 151336 1 br_netfilter
[root@server ~]#
8、配置 ipvs 功能
在 kubernetes 中 service 有两种代理模型,一种是基于 iptables 的,一种是基于 ipvs 的
两者比较的话,ipvs 的性能明显要高一些,但是如果要使用它,需要手动载入 ipvs 模块
安装 ipset 和 ipvsadm:
yum install ipset ipvsadmin -y
添加需要加载的模块写入脚本文件:
cat <<EOF > /etc/sysconfig/modules/ipvs.modules
#!/bin/bash
modprobe -- ip_vs
EOF
安装 docker
切换镜像源
[root@class ~]# wget https://mirrors.aliyun/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
查看当前镜像源中支持的 docker 版本
[root@class ~]# yum list docker-ce --showduplicates
安装特定版本的 docker-ce
必须指定 --setopt=obsoletes=0,否则 yum 会自动安装更高版本
[root@class ~]# yum -y install --setopt=obsoletes=0 docker-ce-18.06.3.ce-3.el7
添加一个配置文件
[root@c1 ~]# cat /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy",
"https://docker.mirrors.ustc.edu",
"https://docker.nju.edu"
]
}
Docker 在默认情况下使用的 Cgroup Driver 为 cgroupfs,而 kubernetes 推荐使用 systemd 来代替 cgroupfs
启动 docker
systemctl restart docker
安装 kubernetes 组件
由于 kubernetes 的镜像源在国外,速度比较慢,这里切换成国内的镜像源
编辑 /etc/yum.repos.d/kubernetes.repo,添加下面的配置
[root@server ~]# cat /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun/kubernetes/yum/doc/rpm-package-key.gpg
安装 kubeadm、kubelet 和 kubectl
yum install --setopt=obsoletes=0 kubeadm-1.17.4-0 kubelet-1.17.4-0 kubectl-1.17.4-0 -y
配置 kubelet 的 cgroup
编辑 /etc/sysconfig/kubelet,添加下面的配置
[root@server ~]# cat /etc/sysconfig/kubelet
GROUP_ARGS="--cgroup-driver=systemd"
KUBE_PROXY_MODE="ipvs"
设置 kubelet 开机自启
systemctl enable kubelet
集群初始化
开始对集群进行初始化,并将 node 节点加入到集群中
操作只需要在 master 节点上执行即可
# 创建集群
[root@master ~]# kubeadm init \
--kubernetes-version=v1.17.4 \ 必须于下载版本一致
--pod-network-cidr=10.244.0.0/16 \ 定义 Pod 网络网段
--service-cidr=10.96.0.0/12 \ 定义 Service 网络网段
--apiserver-advertise-address=172.25.0.11
# 创建必要文件
[root@master ~]# mkdir -p $HOME/.kube
[root@master ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
mkdir -p $HOME/.kube
在当前用户的家目录($HOME,root 用户默认是 /root)下,创建名为 .kube 的隐藏目录
kubectl 工具默认会从 $HOME/.kube/config 文件中读取 集群认证配置(包含 API Server 地址、证书、令牌等),.kube 目录就是用来存放这个核心配置文件的位置。
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
将 Kubernetes 集群初始化时自动生成的 管理员级配置文件(admin.conf),复制到上一步创建的 .kube 目录下,并命名为 config(kubectl 识别的默认配置文件名)
sudo chown $(id -u):$(id -g) $HOME/.kube/config
修改 $HOME/.kube/config 文件的 所有者和所属组,确保当前用户能正常读写该配置文件
准备集群镜像
在安装 kubernetes 集群之前,必须要提前准备好集群需要的镜像,所需镜像可以通过下面命令查看
[root@server ~]# kubeadm config images list
I0829 09:56:19.946312 52539 version.go:251] remote version is much newer: v1.34.0; falling back to: stable-1.17
W0829 09:56:21.771107 52539 validation.go:28] Cannot validate kube-proxy config - no validator is available
W0829 09:56:21.771169 52539 validation.go:28] Cannot validate kubelet config - no validator is available
k8s.gcr.io/kube-apiserver:v1.17.17
k8s.gcr.io/kube-controller-manager:v1.17.17
k8s.gcr.io/kube-scheduler:v1.17.17
k8s.gcr.io/kube-proxy:v1.17.17
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.4.3-0
k8s.gcr.io/coredns:1.6.5
下载镜像
此镜像在 kubernetes 的仓库中,由于网络原因,无法连接,下面提供了一种替代方案
# 1. 定义 1.17.4 版本所需镜像列表
images=(
"kube-apiserver:v1.17.4"
"kube-controller-manager:v1.17.4"
"kube-scheduler:v1.17.4"
"kube-proxy:v1.17.4"
"pause:3.1"
"etcd:3.4.3-0"
"coredns:1.6.5"
)
# 2. 从阿里云拉取并转换为官方标签(k8s.gcr.io)
for imageName in "${images[@]}"; do
# 拉取阿里云镜像
docker pull registry.aliyuncs/google_containers/"$imageName"
# 标签转换(让 kubeadm 能识别)
docker tag registry.aliyuncs/google_containers/"$imageName" k8s.gcr.io/"$imageName"
# 清理阿里云源镜像(节省空间)
docker rmi registry.aliyuncs/google_containers/"$imageName"
done
# 3. 再次验证镜像
docker images | grep k8s.gcr.io
安装网络插件
kubernetes支持多种网络插件,比如flannel、calico、canal等等,任选一种使用即可,本次选择flannel
下面操作依旧只在master节点执行即可,插件使用的是DaemonSet的控制器,它会在每个节点上都运行
获取fannel的配置文件
[root@server ~]# cat kube-flannel.yml
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp.flannel.unprivileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
privileged: false
volumes:
- configMap
- secret
- emptyDir
- hostPath
allowedHostPaths:
- pathPrefix: "/etc/cni/net.d"
- pathPrefix: "/etc/kube-flannel"
- pathPrefix: "/run/flannel"
readOnlyRootFilesystem: false
# 用户和组权限
runAsUser:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
fsGroup:
rule: RunAsAny
# 权限提升控制
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
# 网络相关能力(必须包含 NET_ADMIN)
allowedCapabilities: ['NET_ADMIN']
defaultAddCapabilities: []
requiredDropCapabilities: []
# 主机命名空间
hostPID: false
hostIPC: false
hostNetwork: true
hostPorts:
- min: 0
max: 65535
# SELinux 配置
seLinux:
rule: RunAsAny # 简化配置,适配国内环境
---
# 2. ClusterRole:定义 Flannel 所需的权限
apiVersion: rbac.authorization.k8s.io/v1 # 升级废弃的 v1beta1 → v1
kind: ClusterRole
metadata:
name: flannel
rules:
# 允许使用 PodSecurityPolicy
- apiGroups: ['extensions']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['psp.flannel.unprivileged']
# 访问节点和 Pod 信息
- apiGroups: [""]
resources: ["pods"]
verbs: ["get"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list", "watch"]
- apiGroups: [""]
resources: ["nodes/status"]
verbs: ["patch"]
---
# 3. ClusterRoleBinding:绑定角色到 Flannel 服务账户
apiVersion: rbac.authorization.k8s.io/v1 # 升级废弃的 v1beta1 → v1
kind: ClusterRoleBinding
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system # 服务账户在 kube-system 命名空间
---
# 4. ServiceAccount:Flannel 专用服务账户
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
# 5. ConfigMap:Flannel 核心配置(CNI + 网络子网)
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
app: flannel
data:
# CNI 配置:kubelet 用于创建 Pod 网络
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1", # 兼容 K8s 1.17+
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true, # 解决 Pod 自访问问题
"isDefaultGateway": true # 设为默认网关
}
},
{
"type": "portmap", # 支持 Pod 端口映射
"capabilities": {
"portMappings": true
}
}
]
}
# Flannel 网络配置:定义集群子网和后端
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
# 6. DaemonSet:仅部署 amd64 架构(适配你的节点)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds-amd64
namespace: kube-system
labels:
tier: node
app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
# 节点亲和:仅调度到 amd64 架构的 Linux 节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
hostNetwork: true # 启用主机网络
tolerations:
- operator: Exists
effect: NoSchedule # 容忍所有 NoSchedule 污点(调度到 Master/Worker)
serviceAccountName: flannel # 关联服务账户
# InitContainer:复制 CNI 配置到节点(解决之前的 CNI 配置问题)
initContainers:
- name: install-cni
# 阿里云公开镜像(兼容 K8s 1.17.4,国内可拉取)
image: registry-zhangjiakou.aliyuncs/test-lab/coreos-flannel:amd64
command: ["cp"]
args:
- "-f"
- "/etc/kube-flannel/cni-conf.json"
- "/etc/cni/net.d/10-flannel.conflist"
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d # 挂载节点的 CNI 目录
- name: flannel-cfg
mountPath: /etc/kube-flannel/ # 挂载 ConfigMap
# 主容器:Flannel 服务
containers:
- name: kube-flannel
image: registry-zhangjiakou.aliyuncs/test-lab/coreos-flannel:amd64
command: ["/opt/bin/flanneld"]
args:
- "--ip-masq"
- "--kube-subnet-mgr"
- "--iface=ens33"
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: true
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
- name: xtables-lock
mountPath: /run/xtables.lock # 避免 iptables 冲突
# 挂载的卷
volumes:
- name: run
hostPath:
path: /run/flannel # 节点上的运行目录
- name: cni
hostPath:
path: /etc/cni/net.d # 节点上的 CNI 配置目录
- name: flannel-cfg
configMap:
name: kube-flannel-cfg # 关联 Flannel 配置
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate # 不存在则创建
[root@server ~]# ^C
[root@server ~]#
# 使用配置文件启动fannel
[root@master ~]# kubectl apply -f kube-flannel.yml
稍等片刻,再次查看集群节点的状态
[root@master ~]# kubectl get nodes
服务部署
接下来在 kubernetes 集群中部署一个 nginx 程序,测试下集群是否在正常工作。
部署 nginx
kubectl create deployment nginx --image=nginx:1.14-alpine
暴露端口
kubectl expose deployment nginx --port=80 --type=NodePort
查看服务状态
kubectl get pods,svc
最后在电脑上访问部署的 nginx 服务
YAML 语言
YAML 的语法比较简单,主要有下面几个:
大小写敏感
使用缩进表示层级关系
缩进不允许使用 tab,只允许空格
缩进的空格数不重要,只要相同层级的元素左对齐即可
#表示注释
YAML 支持以下几种数据类型:
纯量:单个的、不可再分的值
对象:键值对的集合,又称为映射(mapping)/ 哈希(hash)/ 字典(dictionary)
数组:一组按次序排列的值,又称为序列(sequence)/ 列表(list)
纯量,就是指一个简单的值,字符串、布尔值、整数、浮点数、Null、时间、日期
1 布尔类型
c1: true(或者 True)
2 整型
c2: 234
3 浮点型
c3: 3.14
4 null 类型
c4: ~ # 使用~表示 null
5 日期类型
c5: 2018-02-17 # 日期必须使用 ISO 8601 格式,即 yyyy-MM-dd
6 时间类型
c6: 2018-02-17T15:31:23+08:00 # 时间使用 ISO 8601 格式,时间和日期之间使用 T 连接,最后使用 + 代表时区
7 字符串类型
c7: heima # 简单写法,直接写值 ,如果字符串中间有特殊字符,必须使用双引号或者单引号包裹
c8: |
line1
line2 # 字符串过多的情况可以拆成多行,每一行会被转化成一个空格
对象
形式一(推荐):
heima:
age: 15
address: Beijing
形式二(了解):
heima: {age: 15,address: Beijing}
数组
形式一(推荐):
address:
- 顺义
- 昌平
形式二(了解):
address: [顺义,昌平]
资源管理
在 kubernetes 中,所有的内容都抽象为资源,用户需要通过操作资源来管理 kubernetes。
kubernetes 的本质上就是一个集群系统,用户可以在集群中部署各种服务,所谓的部署服务,其实就是在 kubernetes 集群中运行一个个的容器,并将指定的程序跑在容器中。
kubernetes 的最小管理单元是 Pod 而不是容器,所以只能将容器放在 Pod 中,而 kubernetes 一般也不会直接管理 Pod,而是通过 Pod 控制器来管理 Pod 的。
Pod 可以提供服务之后,就要考虑如何访问 Pod 中服务,kubernetes 提供了 Service 资源实现这个功能。
当然,如果 Pod 中程序的数据需要持久化,kubernetes 还提供了各种存储系统。
资源管理方式
命令式对象管理:直接使用命令去操作 kubernetes 资源
kubectl run nginx-pod --image=nginx:1.17.1 --port=80
命令式对象配置:通过命令配合配置文件去操作 kubernetes 资源
kubectl create/patch -f nginx-pod.yaml
声明式对象配置:通过 apply 命令和配置文件去操作 kubernetes 资源
kubectl apply -f nginx-pod.yaml
| 类型 | 操作对象 | 适用环境 | 优点 | 缺点 |
|---|---|---|---|---|
| 命令式对象管理 | 对象 | 测试 | 简单 | 只能操作活动对象,无法审计、跟踪 |
| 命令式对象配置 | 文件 | 开发 | 可以审计、跟踪 | 项目大时,配置文件多,操作麻烦 |
| 声明式对象配置 | 目录 | 开发 | 支持目录操作 | 意外情况下难以调试 |
命令式对象管理
kubectl 命令
kubectl 是 kubernetes 集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。kubectl 命令的语法如下:
kubectl [command] [type] [name] [flags]
command:指定要对资源执行的操作,例如 create、get、delete
type:指定资源类型,比如 deployment、pod、service
name:指定资源的名称,名称大小写敏感
flags:指定额外的可选参数
# 查看所有pod
kubectl get pod
# 查看某个pod
kubectl get pod pod_name
# 查看某个pod,以yaml格式展示结果
kubectl get pod pod_name -o yaml
资源类型
kubernetes 中所有的内容都抽象为资源,可以通过下面的命令进行查看:
kubectl api-resources
经常使用的资源有下面这些:
| 资源分类 | 资源名称 | 缩写 | 资源作用 |
|---|---|---|---|
| 集群级别资源 | nodes | no | 集群组成部分 |
| namespaces | ns | 隔离 Pod | |
| pod 资源 | pods | po | 装载容器 |
| pod 资源控制器 | replicationcontrollers | rc | 控制 pod 资源 |
| replicasets | rs | 控制 pod 资源 | |
| deployments | deploy | 控制 pod 资源 | |
| daemonsets | ds | 控制 pod 资源 | |
| jobs | 控制 pod 资源 | ||
| cronjobs | cj | 控制 pod 资源 | |
| horizontalpodautoscalers | hpa | 控制 pod 资源 | |
| statefulsets | sts | 控制 pod 资源 | |
| 服务发现资源 | services | svc | 统一 pod 对外接口 |
| ingress | ing | 统一 pod 对外接口 | |
| 存储资源 | volumeattachments | 存储 | |
| persistentvolumes | pv | 存储 |
# 创建一个namespace
[root@master ~]# kubectl create namespace dev
namespace/dev created
# 获取namespace
[root@master ~]# kubectl get ns
NAME STATUS AGE
default Active 21h
dev Active 21s
kube-node-lease Active 21h
kube-public Active 21h
kube-system Active 21h
# 在此namespace下创建并运行一个nginx的Pod
[root@master ~]# kubectl run pod --image=nginx -n dev
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/pod created
# 查看新创建的pod
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pod-864f9875b9-pcw7x 1/1 Running 0 21s
# 删除指定的pod
[root@master ~]# kubectl delete pod pod-864f9875b9-pcw7x
pod "pod-864f9875b9-pcw7x" deleted
命令式对象配置
命令式对象配置就是使用命令配合配置文件一起来操作 kubernetes 资源。
vim nginxpod.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: nginxpod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx:1.17.1
[root@master ~]# kubectl create -f nginxpod.yaml
namespace/dev created
pod/nginxpod created
执行 get 命令,查看资源:
[root@master ~]# kubectl get -f nginxpod.yaml
执行 delete 命令,删除资源:
[root@master ~]# kubectl delete -f nginxpod.yaml
命令式对象配置的方式操作资源,可以简单的认为:命令 + yaml 配置文件(里面是命令需要的各种参数)
声明式对象配置
声明式对象配置跟命令式对象配置很相似,但是它只有一个命令apply。
# 首先执行一次kubectl apply -f yaml文件,发现创建了资源
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev created
pod/nginxpod created
# 再次执行一次kubectl apply -f yaml文件,发现说资源没有变动
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev unchanged
pod/nginxpod unchanged
其实声明式对象配置就是使用apply描述一个资源最终的状态(在 yaml 中定义状态)
使用apply操作资源:
- 如果资源不存在,就创建,相当于
kubectl create - 如果资源已存在,就更新,相当于
kubectl patch
kubectl 的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在 node 节点运行此命令,需要将 master 上的.kube文件复制到 node 节点上,即在 master 节点上执行下面操作:
scp -r $HOME/.kube node1:$HOME/
| 操作类型 | 推荐方式 | 命令示例 |
|---|---|---|
| 创建 / 更新资源 | 使用声明式对象配置 | kubectl apply -f XXX.yaml |
| 删除资源 | 使用命令式对象配置 | kubectl delete -f XXX.yaml |
| 查询资源 | 使用命令式对象管理 | kubectl get(describe) 资源名称 |
Namespace
Namespace 是 kubernetes 系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。
默认情况下,kubernetes 集群中的所有的 Pod 都是可以相互访问的。但是在实际中,可能不想让两个 Pod 之间进行互相的访问,那此时就可以将两个 Pod 划分到不同的 namespace 下。kubernetes 通过将集群内部的资源分配到不同的 Namespace 中,可以形成逻辑上的 “组”,以方便不同的组的资源进行隔离使用和管理。
可以通过 kubernetes 的授权机制,将不同的 namespace 交给不同租户进行管理,这样就实现了多租户的资源隔离。此时还能结合 kubernetes 的资源配额机制,限定不同租户能占用的资源,例如 CPU 使用量、内存使用量等等,来实现租户可用资源的管理。
kubernetes 在集群启动之后,会默认创建几个 namespace
[root@server ~]# kubectl get ns
NAME STATUS AGE
default Active 3d22h # 所有未指定 Namespace 的对象都会被分配在 default 命名空间
kube-node-lease Active 3d22h # 集群节点之间的心跳维护,v1.13 开始引入
kube-public Active 3d22h # 此命名空间下的资源可以被所有人访问(包括未认证用户)
kube-system Active 3d22h # 所有由 Kubernetes 系统创建的资源都处于这个命名空间
[root@server ~]#
查看
1 查看所有的 ns 命令: kubectl get ns
[root@server ~]# kubectl get ns
NAME STATUS AGE
default Active 3d22h
dev Active 24h
ingress-nginx Active 23h
kube-node-lease Active 3d22h
kube-public Active 3d22h
kube-system Active 3d22h
kubernetes-dashboard Active 17h
2 查看指定的 ns 命令: kubectl get ns ns 名称
[root@server ~]# kubectl get ns dev
NAME STATUS AGE
dev Active 24h
3 指定输出格式 命令: kubectl get ns ns 名称 -o 格式参数
kubernetes 支持的格式有很多,比较常见的是 wide、json、yaml
[root@server ~]# kubectl get ns dev -o wide
NAME STATUS AGE
dev Active 24h
[root@server ~]# kubectl get ns dev -o yaml
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: "2025-08-28T02:04:14Z"
name: dev
resourceVersion: "201613"
selfLink: /api/v1/namespaces/dev
uid: 87cb0441-2015-46b7-8a4e-27106a76d281
spec:
finalizers:
- kubernetes
status:
phase: Active
Pod
Pod 是 kubernetes 集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于 Pod 中。
Pod 可以认为是容器的封装,一个 Pod 中可以存在一个或者多个容器。
kubernetes 在集群启动之后,集群中的各个组件也都是以 Pod 方式运行的。
kubernetes 没有提供单独运行 Pod 的命令,都是通过 Pod 控制器来实现的
命令格式: kubectl run (pod 控制器名称) [参数]
--image 指定 Pod 的镜像
--port 指定端口
--namespace 指定 namespace
[root@server ~]# kubectl run nginx1 --image=nginx:1.17.2 --port=80 --namespace dev
vim pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
查看 pod 信息
查看 Pod 基本信息
root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 11s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
nginx1-576f68b7f-65qdw 1/1 Running 0 6m40s 10.244.4.72 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
[root@server ~]#
查看 Pod 的详细信息
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 11s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
nginx1-576f68b7f-65qdw 1/1 Running 0 6m40s 10.244.4.72 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
删除指定Pod
[root@server ~]# kubectl delete pod nginx1-576f68b7f-65qdw -n dev
pod "nginx1-576f68b7f-65qdw" deleted
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 75s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
nginx1-576f68b7f-wchgk 1/1 Running 0 24s 10.244.4.74 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
[root@server ~]#
这是因为当前Pod是由Pod控制器创建的,控制器会监控Pod状况,一旦发现Pod死亡,会立即重建
# 此时要想删除Pod,必须删除Pod控制器
先来查询一下当前namespace下的Pod控制器
[root@server ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 16h
nginx1 1/1 1 1 8m51s
[root@server ~]# kubectl delete deploy nginx1 -n dev
deployment.apps "nginx1" deleted
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 3m28s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
[root@server ~]#
Label
Label 是 kubernetes 系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
Label 的特点:
一个 Label 会以 key/value 键值对的形式附加到各种对象上,如 Node、Pod、Service 等等
一个资源对象可以定义任意数量的 Label ,同一个 Label 也可以被添加到任意数量的资源对象上去
Label 通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除
可以通过 Label 实现资源的多维度分组,以便灵活、方便地进行资源分配、调度、配置、部署等管理工作。
一些常用的 Label 示例如下:
版本标签: "version":"release", "version":"stable"……
环境标签: "environment":"dev", "environment":"test", "environment":"pro"
架构标签: "tier":"frontend", "tier":"backend"
标签定义完毕之后,还要考虑到标签的选择,这就要使用到 Label Selector,即:
Label 用于给某个资源对象定义标识
Label Selector 用于查询和筛选拥有某些标签的资源对象
当前有两种 Label Selector:
基于等式的 Label Selector
name = slave: 选择所有包含 Label 中 key="name" 且 value="slave" 的对象
env != production: 选择所有包括 Label 中的 key="env" 且 value 不等于 "production" 的对象
基于集合的 Label Selector
name in (master,slave): 选择所有包含 Label 中的 key="name" 且 value="master" 或 "slave" 的对象
name not in (frontend): 选择所有包含 Label 中的 key="name" 且 value 不等于 "frontend" 的对象
标签的选择条件可以使用多个,此时将多个 Label Selector 进行组合,使用逗号 “,” 进行分隔即可。例如:
name=slave, env!=production
name not in (frontend), env!=production
先创建 pod-nginx
配置操作
创建一个pod-nginx.yaml,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
为pod资源打标签
[root@server ~]# kubectl label pod nginx version=1.0 -n dev
pod/nginx labeled
为pod资源更新标签
[root@server ~]# kubectl label pod nginx version=2.0 -n dev --overwrite
pod/nginx labeled
查看标签
[root@server ~]# kubectl get pod nginx -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 101s version=2.0
[root@server ~]#
Service
已经能够利用 Deployment 来创建一组 Pod 来提供具有高可用性的服务。
虽然每个 Pod 都会分配一个单独的 Pod IP,然而却存在如下两问题:
Pod IP 会随着 Pod 的重建产生变化
Pod IP 仅仅是集群内可见的虚拟 IP,外部无法访问
这样对于访问这个服务带来了难度。因此,kubernetes 设计了 Service 来解决这个问题。
Service 可以看作是一组同类 Pod 对外的访问接口。借助 Service,应用可以方便地实现服务发现和负载均衡。
创建集群内部可访问的 Service
暴露Service
[root@server ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 20h
[root@server ~]# kubectl expose deploy nginx --name=svc-nginx --type=ClusterIP --port=80 --target-port=80 -n dev
service/svc-nginx exposed
[root@server ~]#
查看service
[root@server ~]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nginx ClusterIP 10.107.79.96 <none> 80/TCP 2m19s
[root@server ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx ClusterIP 10.107.79.96 <none> 80/TCP 2m30s k8s-app=nginx
这里产生了一个CLUSTER-IP,这就是service的IP,在Service的生命周期中,这个地址是不会变动的
可以通过这个IP访问当前service对应的POD
创建集群外部也可访问的 Service
创建的Service的type类型为ClusterIP,这个ip地址只用集群内部访问
如果需要创建外部也可以访问的Service,需要修改type为NodePort
[root@server ~]# kubectl expose deploy nginx --name=svc-nginx2 --type=NodePort --port=80 --target-port=80 -n dev
service/svc-nginx2 exposed
[root@server ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx ClusterIP 10.107.79.96 <none> 80/TCP 9m4s k8s-app=nginx
svc-nginx2 NodePort 10.102.157.69 <none> 80:32258/TCP 41s k8s-app=nginx
接下来就可以通过集群外的主机访问 节点IP:32258访问服务了
# 例如在的电脑主机上通过浏览器访问下面的地址
删除service
[root@server ~]# kubectl delete svc svc-nginx -n dev
service "svc-nginx" deleted
[root@server ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx2 NodePort 10.102.157.69 <none> 80:32258/TCP 2m8s k8s-app=nginx
[root@server ~]#
Pod 结构
每个 Pod 中都可以包含一个或者多个容器,这些容器可以分为两类:
用户程序所在的容器,数量可多可少
Pause 容器,这是每个 Pod 都会有的一个根容器,它的作用有两个:
可以以它为依据,评估整个 Pod 的健康状态
可以在根容器上设置 IP 地址,其它容器都此 IP(Pod IP),以实现 Pod 内部的网路通信
Pod 定义
apiVersion: v1 # 必选,版本号,例如v1
kind: Pod # 必选,资源类型,例如 Pod
metadata: # 必选,元数据
name: string # 必选,Pod名称
namespace: string # Pod所属的命名空间,默认为“default”
labels: # 自定义标签列表
- name: string
spec: # 必选,Pod中容器的详细定义
containers: # 必选,Pod中容器列表
- name: string # 必选,容器名称
image: string # 必选,容器的镜像名称
imagePullPolicy: [ Always|Never|IfNotPresent ] # 获取镜像的策略
command: [string] # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] # 容器的启动命令参数列表
workingDir: string # 容器的工作目录
volumeMounts: # 挂载到容器内部的存储卷配置
- name: string # 引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string # 存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean # 是否为只读模式
ports: # 需要暴露的端口号列表
- name: string # 端口的名称
containerPort: int # 容器需要监听的端口号
hostPort: int # 容器所在主机需要监听的端口号,默认与Container相同
protocol: string # 端口协议,支持TCP和UDP,默认TCP
可通过一个命令来查看每种资源的可配置项
# kubectl explain 资源类型 查看某种资源可以配置的一级属性
# kubectl explain 资源类型.属性 查看属性的子属性
可通过一个命令来查看每种资源的可配置项
# kubectl explain 资源类型 查看某种资源可以配置的一级属性
# kubectl explain 资源类型.属性 查看属性的子属性
[root@server ~]# kubectl explain pod
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object>
Specification of the desired behavior of the pod. More info:
https://git.k8s.io/community/contrib
kubernetes 资源一级属性说明
在 kubernetes 中基本所有资源的一级属性都是一样的,主要包含 5 部分:
apiVersion <string>:版本,由 kubernetes 内部定义,版本号必须可以用 kubectl api-versions 查询到
kind <string>:类型,由 kubernetes 内部定义,版本号必须可以用 kubectl api-resources 查询到
metadata <Object>:元数据,主要是资源标识和说明,常用的有 name、namespace、labels 等
spec <Object>:描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述
status <Object>:状态信息,里面的内容不需要定义,由 kubernetes 自动生成
在上面的属性中,spec 是接下来研究的重点,继续看下它的常见子属性:
containers <[]Object>:容器列表,用于定义容器的详细信息
nodeName <String>:根据 nodeName 的值将 pod 调度到指定的 Node 节点上
nodeSelector <map[]>:根据 NodeSelector 中定义的信息选择将该 Pod 调度到包含这些 label 的 Node 上
hostNetwork <boolean>:是否使用主机网络模式,默认为 false,如果设置为 true,表示使用宿主机网络
volumes <[]Object>:存储卷,用于定义 Pod 上面挂在的存储信息
restartPolicy <string>:重启策略,表示 Pod 在遇到故障的时候的处理策略
Pod 配置
研究 pod.spec.containers 属性,这也是 pod 配置中最为关键的一项配置。
[root@master ~]# kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 数组,代表可以有多个容器
FIELDS:
name <string> # 容器名称
image <string> # 容器需要的镜像地址
imagePullPolicy <string> # 镜像拉取策略
command <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args <[]string> # 容器的启动命令需要的参数列表
env <[]Object> # 容器环境变量配置
ports <[]Object> # 容器需要暴露的端口号列表
resources <Object> # 资源限制和资源请求的设置
基本配置
镜像拉取
创建 pod-command.yaml 文件
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
command,用于在 pod 中的容器初始化完毕之后运行一个命令。
/bin/sh","-c":使用 sh 执行命令
touch /tmp/hello.txt:创建一个 /tmp/hello.txt 文件
while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;:持续向 /tmp/hello.txt 文件写入当前时间,每 3 秒写入一次
命令操作
创建 Pod
[root@server ~]# kubectl create -f pod-command.yaml
pod/pod-imagepullpolicy created
查看 Pod 状态
[root@server ~]# kubectl get pods pod-command -n dev
NAME READY STATUS RESTARTS AGE
pod-command 2/2 Running 0 9s
进入 pod 中的 busybox 容器,查看文件内容
补充一个命令:kubectl exec pod名称 -n 命名空间 -it -c 容器名称 /bin/sh 在容器内部执行命令
使用这个命令就可以进入某个容器的内部,然后进行相关操作了 比如,可以查看 txt 文件的内容
[root@master pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
/ # tail -f /tmp/hello.txt
通过上面发现 command 已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个 args 选项,用于传递参数呢?
这其实跟 docker 有点关系,kubernetes 中的 command、args 两项其实是实现覆盖 Dockerfile 中 ENTRYPOINT 的功能。
如果 command 和 args 均没有写,那么用 Dockerfile 的配置。
如果 command 写了,但 args 没有写,那么 Dockerfile 默认的配置会被忽略,执行输入的 command
如果 command 没写,但 args 写了,那么 Dockerfile 中配置的 ENTRYPOINT 的命令会被执行,使用当前 args 的参数
如果 command 和 args 都写了,那么 Dockerfile 的配置被忽略,执行 command 并追加 args 参数
环境变量
apiVersion: v1
kind: Pod
metadata:
name: pod-env
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "while true;do /bin/echo $(date +%T);sleep 60; done;"]
env: # 设置环境变量列表
- name: "username"
value: "admin"
- name: "password"
value: "123456"
env,环境变量,用于在 pod 中的容器设置环境变量。
命令操作
创建 Pod
[root@server ~]# kubectl create -f pod-env.yaml
pod/pod-env created
进入容器,输出环境变量
[root@server ~]# kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ # echo $username
admin
/ #
端口设置
容器的端口设置,也就是 containers 的 ports 选项。
看下 ports 支持的子选项:
[root@master ~]# kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:
name <string> # 端口名称,如果指定,必须保证name在pod中是唯一的
containerPort <integer> # 容器要监听的端口(0x<65536)
hostPort <integer> # 容器要在主机上公开的端口,如果设置,主机上只能运行容器的一个副本(一般省略)
hostIP <string> # 要将外部端口绑定到的主机IP(一般省略)
protocol <string> # 端口协议。必须是UDP、TCP或SCTP。默认为“TCP”。
创建pod-ports.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports: # 设置容器暴露的端口列表
- name: nginx-port
containerPort: 80
protocol: TCP
[root@server ~]# kubectl create -f pod-ports.yaml
pod/pod-ports created
资源配额
容器中的程序要运行,肯定是要占用一定资源的,比如 cpu 和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes 提供了对内存和 cpu 的资源进行配额的机制,这种机制主要通过 resources 选项实现,它有两个子选项:
limits:用于限制运行时容器的最大占用资源,当容器占用资源超过 limits 时会被终止,并进行重启
requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动
可以通过上面两个选项设置资源的上下限。
创建pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-resources
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
resources: # 资源限制
limits: # 限制资源(上限)
cpu: "1" # CPU 限制,单位是core(核)
memory: "1Gi" # 内存限制
requests: # 请求资源(下限)
cpu: "1" # CPU 请求,单位是core(核)
memory: "1Gi" # 内存请求
# 查看发现pod运行正常
[root@master ~]# kubectl get pod pod-resources -n dev
NAME READY STATUS RESTARTS AGE
pod-resources 1/1 Running 0 39s
# 接下来,停止Pod
[root@master ~]# kubectl delete -f pod-resources.yaml
pod "pod-resources" deleted
# 编辑pod,修改resources.requests.memory的值为10Gi
[root@master ~]# vim pod-resources.yaml
# 再次启动pod
[root@master ~]# kubectl create -f pod-resources.yaml
pod/pod-resources created
# 查看Pod状态,发现Pod启动失败
[root@master ~]# kubectl get pod pod-resources -n dev -o wide
NAME READY STATUS RESTARTS AGE
pod-resources 0/2 Pending 0 20s
# 查看pod详情会发现,如下提示
[root@master ~]# kubectl describe pod pod-resources -n dev
......
Warning FailedScheduling <unknown> default-scheduler 0/2 nodes are available: 2 Insufficient memory. (内存不足)
Pod 生命周期
我们一般将 pod 对象从创建至终的这段时间范围称为 pod 的生命周期,它主要包含下面的过程:
pod 创建过程
运行初始化容器(init container)过程
运行主容器(main container)过程
容器启动后钩子(post start)、容器终止前钩子(pre stop)
容器的存活性探测(liveness probe)、就绪性探测(readiness probe)
pod 终止过程...
在整个生命周期中,Pod 会出现 5 种状态(相位),分别如下:
挂起(Pending):apiserver 已经创建了 pod 资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
运行中(Running):pod 已经被调度至某节点,并且所有容器都已经被 kubelet 创建完成
成功(Succeeded):pod 中的所有容器都已经成功终止并且不会被重启
失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非 0 值的退出状态
未知(Unknown):apiserver 无法正常获取到 pod 对象的状态信息,通常由网络通信失败所导致
pod 的创建过程
用户通过 kubectl 或其他 api 客户端提交需要创建的 pod 信息给 apiServer
apiServer 开始生成 pod 对象的信息,并将信息存入 etcd,然后返回确认信息至客户端
apiServer 开始反映 etcd 中的 pod 对象的变化,其它组件使用 watch 机制来跟踪检查 apiServer 上的变动
scheduler 发现有新的 pod 对象要创建,开始为 Pod 分配主机并将结果信息更新至 apiServer
node 节点上的 kubelet 发现有 pod 调度过来,尝试调用 docker 启动容器,并将结果回送至 apiServer
apiServer 将接收到的 pod 状态信息存入 etcd 中
pod 的终止过程
用户向 apiServer 发送删除 pod 对象的命令
apiServer 中的 pod 对象信息会随着时间的推移而更新,在宽限期内(默认 30s),pod 被视为 dead
将 pod 标记为 terminating 状态
kubelet 在监控到 pod 对象转为 terminating 状态的同时启动 pod 关闭过程
端点控制器监控到 pod 对象的关闭行为时将其从所有匹配到此端点的 service 资源的端点列表中移除
如果当前 pod 对象定义了 preStop 钩子处理器,则在其标记为 terminating 后即会以同步的方式启动执行
pod 对象中的容器进程收到停止信号
宽限期结束后,若 pod 中还存在仍在运行的进程,那么 pod 对象会收到立即终止的信号
kubelet 请求 apiServer 将此 pod 资源的宽限期设置为 0 从而完成删除操作,此时 pod 对于用户已不可见
初始化容器
初始化容器是在 pod 的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:
初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么 kubernetes 需要重启它直到成功完成
初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行
初始化容器有很多的应用场景,下面列出的是最常见的几个:
提供主容器镜像中不具备的工具程序或自定义代码
初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足
接下来做一个案例,模拟下面这个需求:
假设要以主容器来运行 nginx,但是要求在运行 nginx 之前先要能够连接上 mysql 和 redis 所在服务器
为了简化测试,事先规定好 mysql(172.25.0.10)和 redis(172.25.0.33)服务器的地址
需求与准备
假设要以主容器来运行 nginx ,但要求在运行 nginx 之前先要能够连接上 mysql 和 redis 所在服务器。为简化测试,事先规定好 mysql(192.168.109.201)和 redis(192.168.109.202)服务器的地址
[root@server ~]# cat pod-initcontainer.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-ports
containerPort: 80
initContainers:
- name: test-mysql
image: busybox:1.30
command: ['sh','-c','until ping 172.25.0.10 -c 1 ; do echo waiting for mysql ...; sleep 6; done;']
- name: test-redis
image: busybox:1.30
command: ['sh','-c','until ping 172.25.0.33 -c 1 ; do echo waiting for mysql ...; sleep 6; done;']
创建
[root@server ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created
查找
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 6 65m 10.244.2.67 destop.example <none> <none>
nginx1 1/1 Running 6 5h22m 10.244.2.66 destop.example <none> <none>
pod-command 2/2 Running 10 31m 10.244.2.69 destop.example <none> <none>
pod-configmap 1/1 Running 6 24h 10.244.4.67 c1.example <none> <none>
pod-env 2/2 Running 8 28m 10.244.2.70 destop.example <none> <none>
pod-initcontainer 0/1 Init:1/2 0 4s 10.244.2.72 destop.example <none> <none>
pod-ports 1/1 Running 0 15m 10.244.4.75 c1.example <none> <none>
钩子函数
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
kubernetes 在主容器的启动之后和停止之前提供了两个钩子函数:
post start:容器创建之后执行,如果失败了会重启容器
pre stop :容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
钩子处理黯支持使用下面三种方式定义动作:
1. Exec 命令:在容器内执行一次命令
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
2. TCPSocket:在当前容器尝试访问指定的 socket
lifecycle:
postStart:
tcpSocket:
port: 8080
3. HTTPGet:在当前容器中向某 url 发起 http 请求
lifecycle:
postStart:
httpGet:
path: / # URI地址
port: 80 # 端口号
host: 192.168.109.100 # 主机地址
scheme: HTTP # 支持的协议,http或者https
以 exec 方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
lifecycle:
postStart:
exec: # 在容器启动的时候执行一个命令,修改掉nginx的默认首页内容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服务
command: ["/usr/sbin/nginx", "-s", "quit"]
容器探测
容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么 kubernetes 就会把该问题实例 “摘除”,不承担业务流量。kubernetes 提供了两种探针来实现容器探测,分别是:
liveness probes(存活性探针):用于检测应用实例当前是否处于正常运行状态,如果不是,k8s 会重启容器
readiness probes(就绪性探针):用于检测应用实例当前是否可以接收请求,如果不能,k8s 不会转发流量
livenessProbe 决定是否重启容器,readinessProbe 决定是否将请求转发给容器。
上面两种探针目前均支持三种探测方式:
1. Exec 命令
在容器内执行一次命令,如果命令执行的退出码为 0,则认为程序正常,否则不正常
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
2、TCPSocket
将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常
livenessProbe:
tcpSocket:
port: 8080
3、HTTPGet:调用容器内 Web 应用的 URL,如果返回的状态码在 200 和 399 之间,则认为程序正常,否则不正常
livenessProbe:
httpGet:
path: / # URI地址
port: 80 # 端口号
host: 127.0.0.1 # 主机地址
scheme: HTTP # 支持的协议,http或者https
[root@master ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
exec <Object>
tcpSocket <Object>
httpGet <Object>
initialDelaySeconds <integer> # 容器启动后等待多少秒执行第一次探测
timeoutSeconds <integer> # 探测超时时间。默认1秒,最小1秒
periodSeconds <integer> # 执行探测的频率。默认是10秒,最小1秒
failureThreshold <integer> # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
successThreshold <integer> # 连续探测成功多少次才被认定为成功。默认是1
重启策略
一旦容器探测出现了问题,kubernetes 就会对容器所在的 Pod 进行重启,其实这是由 pod 的重启策略决定的,pod 的重启策略有 3 种,分别如下:
Always:容器失效时,自动重启该容器,这也是默认值。
OnFailure:容器终止运行且退出码不为 0 时重启
Never:不论状态为何,都不重启该容器
重启策略适用于 pod 对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由 kubelet 延迟一段时间后进行,且反复的重启操作的延迟时长以此为 10s、20s、40s、80s、160s 和 300s,300s 是最大延迟时长。
创建 pod-restartpolicy.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-restartpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /hello 因为没有,会执行重启策略
restartPolicy: Never # 设置重启策略为Never
Pod 调度
在默认情况下,一个 Pod 在哪个 Node 节点上运行,是由 Scheduler 组件采用相应的算法计算出来的,这个过程是不受人工控制的。但实际使用中,常需控制某些 Pod 到特定节点,为此要了解 kubernetes 对 Pod 的调度规则,kubernetes 提供四大类调度方式:
自动调度:运行在哪个节点上完全由 Scheduler 经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration
定向调度
定向调度,指利用在 pod 上声明 nodeName 或者 nodeSelector,以此将 Pod 调度到期望的 node 节点上。注意,这里的调度是强制的,即即便目标 Node 不存在,也会尝试调度,只是 pod 运行会失败而已。
NodeName
NodeName 用于强制约束将 Pod 调度到指定的 Name 的 Node 节点上。这种方式,实际是直接跳过 Scheduler 的调度逻辑,直接写入 PodList 列表。
NodeName
NodeName 用于强制约束将 Pod 调度到指定的 Name 的 Node 节点上。这种方式,其实是直接跳过 Scheduler 的调度逻辑,直接将 Pod 调度到指定名称的节点。
创建一个 pod-nodename.yaml 文件
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 指定调度到node1节点上
创建 Pod
[root@master ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created
查看 Pod 调度到 NODE 属性,确认调度到 node1 节点上
[root@master ~]# kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-nodename 1/1 Running 0 56s 10.244.1.87 node1
删除 pod,修改 nodeName 的值为 node3(假设没有 node3 节点)
[root@master ~]# kubectl delete -f pod-nodename.yaml
[root@server ~]# cat pod-nodename.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node32
NodeSelector
NodeSelector 用于将 pod 调度到添加了指定标签的 node 节点上。它是通过 kubernetes 的 label-selector 机制实现的,也就是说,在 pod 创建之前,会由 scheduler 使用 MatchNodeSelector 调度策略进行 label 匹配,找出目标 node,然后将 pod 调度到目标节点,该匹配规则是强制约束。
为 node 节点添加标签
[root@master ~]# kubectl label nodes c1.example nodeenv=pro
node/node1 labeled
[root@master ~]# kubectl label nodes destop.example nodeenv=test
node/node2 labeled
[root@server ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
c1.example Ready <none> 3d1h v1.17.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=c1.example,kubernetes.io/os=linux,nodeenv=pro
destop.example Ready <none> 4d2h v1.17.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=destop.example,kubernetes.io/os=linux,nodeenv=test
server.example Ready master 4d4h v1.17.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=server.example,kubernetes.io/os=linux,node-role.kubernetes.io/master=
创建一个 pod-nodeselector.yaml 文件,并使用它创建 Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上
创建一个 pod-nodeselector.yaml 文件,并使用它创建 Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上
亲和性调度
两种定向调度方式,使用方便但存在局限:若没有满足条件的 Node,Pod 无法运行,即便集群有可用 Node 也不行,限制了使用场景。
基于此,kubernetes 提供亲和性调度(Affinity) 。它在 NodeSelector 基础上扩展,通过配置可优先选满足条件 Node 调度;无满足条件 Node 时,也可调到不满足条件节点,让调度更灵活。
Affinity 主要分类
nodeAffinity(node 亲和性):以 node 为目标,解决 pod 可调度到哪些 node 的问题
podAffinity(pod 亲和性):以 pod 为目标,解决 pod 可和哪些已存在 pod 部署在同一拓扑域(如同一节点、同一可用区等 )的问题
podAntiAffinity(pod 反亲和性):以 pod 为目标,解决 pod 不能和哪些已存在 pod 部署在同一拓扑域的问题
亲和性(反亲和性)使用场景说明
亲和性:若两个应用频繁交互,利用亲和性让它们尽可能靠近,可减少网络通信带来的性能损耗,比如微服务架构里关系紧密的服务。
反亲和性:应用多副本部署时,用反亲和性让各实例分散在不同 node,避免单点故障,提升服务高可用性,像 Redis 集群多节点部署。
NodeAffinity 的可配置项
pod.spec.affinity.nodeAffinity 包含以下关键配置:
1. 硬限制(严格约束,必须满足)
requiredDuringSchedulingIgnoredDuringExecution:Node 节点必须满足指定所有规则才可调度,相当于硬限制
nodeSelectorTerms:节点选择列表
matchFields:按节点字段(如主机名、IP 等)列出节点选择器要求
matchExpressions:按节点标签列出节点选择器要求(推荐)
key:标签键
values:标签值列表
operator:关系符,支持 Exists(存在标签)、DoesNotExist(不存在标 签)、In(值在列表内)、NotIn(值不在列表内)、Gt(值大于,需配合数值型标签)、Lt(值小于,需配合数值型标签 )
2. 软限制(倾向满足,不强制)
preferredDuringSchedulingIgnoredDuringExecution:优先调度到满足指定规则的 Node,相当于软限制(倾向)
preference:一个节点选择项,与权重关联
matchFields:按节点字段列出节点选择器要求
matchExpressions:按节点标签列出节点选择器要求(推荐)
key、values、operator:同硬限制逻辑
weight:倾向权重,范围 1~100,权重越高越优先
关系符使用说明(以 matchExpressions 为例 )
matchExpressions:
- key: nodeenv # 匹配存在标签key为nodeenv的节点
operator: Exists
- key: nodeenv # 匹配标签key为nodeenv、且value是"xxx"或"yyy"的节点
operator: In
values: ["xxx", "yyy"]
- key: nodeenv # 匹配标签key为nodeenv、且value大于"xxx"的节点(需标签值为数值型)
operator: Gt
values: "xxx"
演示一下 requiredDuringSchedulingIgnoredDuringExecution,
创建 pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
nodeAffinity: #设置node亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
nodeSelectorTerms:
- matchExpressions: # 匹配env的值在["xxx", "yyy"]中的标签
- key: nodeenv
operator: In
values: ["xxx", "yyy"]
[root@server ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
[root@server ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 6 24h
pod-hook-exec 1/1 Running 0 16m
pod-nodeaffinity-required 1/1 Running 0 11s
pod-ports 1/1 Running 0 59m
[root@server ~]#
演示一下 requiredDuringSchedulingIgnoredDuringExecution,
创建 pod-nodeaffinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-preferred
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
nodeAffinity: #设置node亲和性
preferredDuringSchedulingIgnoredDuringExecution: # 软限制
- weight: 1
preference:
matchExpressions: # 匹配env的值在["xxx", "yyy"]中的标签(当前环境没有)
- key: nodeenv
operator: In
values: ["xxx", "yyy"]
[root@server ~]# kubectl create -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created
[root@server ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 6 24h
pod-hook-exec 1/1 Running 1 19m
pod-nodeaffinity-preferred 1/1 Running 0 4s
pod-nodeaffinity-required 1/1 Running 0 2m32s
pod-ports 1/1 Running 0 61m
[root@server ~]#
Nodeaffinity 规则设置的注意事项:
如果同时定义了 nodeSelector 和 nodeAffinity,那么必须两个条件都得到满足,Pod 才能运行在指定的 Node 上
如果 nodeAffinity 指定了多个 nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
如果一个 nodeSelectorTerms 中有多个 matchExpressions ,则一个节点必须满足所有的才能匹配成功
如果一个 Pod 所在的 Node 在 Pod 运行期间标签发生了改变,不再符合该 Pod 的节点亲和性需求,则系统将忽略此变化
PodAffinity
PodAffinity 主要实现以运行的 Pod 为参照,让新创建的 Pod 跟参照 Pod 处于同一区域的功能。
PodAffinity 的可配置项(pod.spec.affinity.podAffinity)
硬限制(requiredDuringSchedulingIgnoredDuringExecution):
namespaces:指定参照 Pod 所在的命名空间
topologyKey:指定调度作用域(如按节点、可用区等划分,例 kubernetes.io/hostname 按节点区分 )
labelSelector:标签选择器,用于筛选参照 Pod
matchExpressions:按标签规则匹配(推荐)
key:标签键
values:标签值列表
operator:关系符,支持 In、NotIn、Exists、DoesNotExist
matchLabels:直接指定多个标签键值对,等效于多个 matchExpressions 映射
创建 pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAffinity: #设置pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配env的值在["xxx", "yyy"]中的标签
- key: nodeenv
operator: In
values: ["pro", "yyy"]
topologyKey: kubernetes.io/hostname
软限制(preferredDuringSchedulingIgnoredDuringExecution):
podAffinityTerm:配置项,结构同硬限制里的规则(namespaces、topologyKey、labelSelector 等 )
weight:倾向权重,范围 1~100,权重越高越优先调度
演示下 requiredDuringSchedulingIgnoredDuringExecution,
首先创建一个参照 Pod,pod-podaffinity-target.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
nodeenv: pro #设置标签
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: c1.example # 将目标pod名确定到c1.example上
[root@server ~]# kubectl create -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created
[root@server ~]# kubectl get pod pod-podaffinity-target -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-target 1/1 Running 0 12s
topologyKey 作用说明
用于指定调度作用域,示例:
若设为 kubernetes.io/hostname:调度时以 Node 节点为区分范围,新 Pod 尽量和参照 Pod 跑在同一节点
若设为 beta.kubernetes.io/os:按 Node 节点的操作系统类型区分,新 Pod 尽量和参照 Pod 跑在同操作系统类型节点
(简单说,就是通过这些配置,控制新 Pod 与已有参照 Pod 在 “同一区域” 的调度逻辑,硬限制必须满足,软限制优先满足 )
PodAntiAffinity
PodAntiAffinity 主要实现以运行的 Pod 为参照,让新创建的 Pod 跟参照 pod 不在一个区域中的功能。
它的配置方式和选项跟 PodAffinity 是一样的
创建 pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAntiAffinity: #设置pod反亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配podenv的值在["pro"]中的标签
- key: nodeenv
operator: In
values: ["pro"]
topologyKey: kubernetes.io/hostname
上面配置表达的意思是:新 Pod 必须要与拥有标签 nodeenv=pro 的 pod 不在同一 Node 上,运行测试一下。
root@server ~]# kubectl create -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created
[root@server ~]# kubectl get pod pod-podantiaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podantiaffinity-required 0/1 ContainerCreating 0 21s <none> destop.example <none> <none>
污点和容忍
污点(Taints)
前面的调度方式都是站在 Pod 的角度,通过在 Pod 上添加属性,确定 Pod 是否调度到指定 Node。也可站在 Node 角度,通过给 Node 添加污点属性,决定是否允许 Pod 调度过来。
Node 设污点后,与 Pod 形成 “相斥关系”,可拒绝 Pod 调度,甚至驱逐已存在的 Pod。
污点格式与作用
污点格式:key=value:effect ,key 和 value 是污点标签,effect 描述作用,支持三种选项:
PreferNoSchedule:kubernets 尽量避免把 Pod 调度到有该污点的 Node,除非无其他节点可调度
NoSchedule:kubernets 不会把 Pod 调度到有该污点的 Node,但不影响 Node 上已存在的 Pod
NoExecute:kubernets 不会把 Pod 调度到有该污点的 Node,还会将 Node 上已存在的 Pod 驱离
使用 kubectl 设置和去除污点的命令
# 设置污点
kubectl taint nodes node1 key=value:effect
# 去除污点(注意格式:key:effect- ,减号表示移除 )
kubectl taint nodes node1 key:effect-
# 去除所有污点(若要清除特定 key 的所有 effect,可这样写,实际需按真实 key 调整 )
kubectl taint nodes node1 key-
操作步骤
-
准备节点 c1(为让演示效果更明显,暂时停止 destop 节点 )
-
为 c
1节点设置污点tag=heima:PreferNoSchedule,然后创建pod1 -
修改 c
1节点污点为tag=heima:NoSchedule,然后创建pod2 -
修改 c
1节点污点为tag=heima:NoExecute,然后创建pod3
为 c1 设置污点(PreferNoSchedule )
NoSchedule 阻止 “新 Pod 调度”,不影响已运行的
使用 kubeadm 搭建的集群,默认会给 master 节点添加一个污点标记,所以 pod 不会调度到 master 节点上。
容忍(Toleration)
上面介绍了污点的作用,我们可在 node 上添加污点拒绝 pod 调度。但如果想让一个 pod 调度到有污点的 node 上,就要用到容忍。
污点就是拒绝,容忍就是忽略,Node 通过污点拒绝 pod 调度上去,Pod 通过容忍忽略拒绝
创建 pod-toleration.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-toleration
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
tolerations: # 添加容忍
- key: "tag" # 要容忍的污点的key
operator: "Equal" # 操作符
value: "heima" # 容忍的污点的value
effect: "NoExecute" # 添加容忍的规则,这里必须和标记的污点规则相同
[root@server ~]# kubectl taint nodes c1.example tag=heima:NoExecute
[root@server ~]# kubectl create -f pod-toleration.yaml
pod/pod-toleration1 created
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hook-exec 0/1 Terminating 3 17h 10.244.2.73 destop.example <none> <none>
pod-podantiaffinity-required 1/1 Terminating 0 16h 10.244.2.74 destop.example <none> <none>
pod-toleration1 1/1 Running 0 7s 10.244.4.84 c1.example <none> <none>
查看 tolerations 字段说明:
kubectl explain pod.spec.tolerations
| 字段 | 说明 |
|---|---|
key | 对应要容忍的污点的键,空值匹配所有键 |
value | 对应要容忍的污点的值 |
operator | key-value 的运算符,支持 Equal(精确匹配值)和 Exists(只需存在键,忽略值,默认) |
effect | 对应污点的 effect,空值匹配所有影响 |
tolerationSeconds | 容忍时间,仅当 effect 为 NoExecute 时生效,表 Pod 在 Node 上的停留时间(超时后会被驱离 ) |
通过这些字段,精确控制 Pod 对 Node 污点的 “容忍规则”,让 Pod 能调度到带指定污点的节点
Pod 控制器
在 kubernetes 中,按照 pod 的创建方式可将其分为两类:
自主式 pod:kubernetes 直接创建出来的 pod,这种 pod 删除后就没有了,也不会重建
控制器创建的 pod:通过控制器创建的 pod,这种 pod 删除了之后还会自动重建
什么是 Pod 控制器
Pod 控制器是管理 pod 的中间层,使用了 pod 控制器之后,我们只需要告诉 pod 控制器,想要多少个什么样的 pod 就可以了,它就会创建出满足条件的 pod 并确保每一个 pod 处于用户期望的状态,如果 pod 在运行中出现故障,控制器会基于指定策略重启或者重建 pod。
在 kubernetes 中,有很多类型的 pod 控制器,每种都有自己的适合的场景,常见的有下面这些:
ReplicationController:比较原始的 pod 控制器,已经被废弃,由 ReplicaSet 替代
ReplicaSet:保证指定数量的 pod 运行,并支持 pod 数量变更,镜像版本变更
Deployment:通过控制 ReplicaSet 来控制 pod,并支持滚动升级、版本回退
Horizontal Pod Autoscaler:可以根据集群负载自动调整 Pod 的数量,实现削峰填谷
DaemonSet:在集群中的指定 Node 上都运行一个副本,一般用于守护进程类的任务
ReplicaSet
ReplicaSet 的主要作用是保证一定数量的 pod 能够正常运行,它会持续监听这些 pod 的运行状态,一旦 pod 发生故障,就会重启或重建。同时它还支持对 pod 数量的扩 / 缩容和版本镜像的升级。
ReplicaSet 的资源清单文件
apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: rs
spec: # 详情描述
replicas: 3 # 副本数量
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
配置项说明
这里需要新了解的 spec 下几个选项:
replicas:指定副本数量,即当前 ReplicaSet 要创建的 Pod 数量,默认值为 1
selector:选择器,作用是建立 Pod 控制器(ReplicaSet)和 Pod 的关联关系,基于 Label Selector 机制:在 Pod 模板定义 label,控制器定义选择器,以此表明控制器能管理哪些 Pod
template:模板,控制器创建 Pod 时使用的模板,
创建 pc-replicaset.yaml 文件
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: pc-replicaset
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
扩缩容
# 编辑 rs 的副本数量,修改 spec:replicas 为 6 即可
[root@master ~]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited
# 查看 pod(确认副本数量变化 )
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6mvvt 1/1 Running 0 114m
pc-replicaset-cftnp 1/1 Running 0 10s
pc-replicaset-fj1mb 1/1 Running 0 10s
pc-replicaset-fmbbf 1/1 Running 0 114m
pc-replicaset-s2hwj 1/1 Running 0 10s
pc-replicaset-snrk2 1/1 Running 0 114m
# 当然也可以直接使用命令实现
# 使用scale命令实现扩缩容,后面--replicas=n直接指定目标数量即可
[root@master ~]# kubectl scale rs pc-replicaset --replicas=2 -n dev
replicaset.apps/pc-replicaset scaled
# 命令运行完毕,立即查看,发现已经有4个开始准备退出了
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6mvvt 0/1 Terminating 0 118m
pc-replicaset-cftnp 0/1 Terminating 0 4m17s
pc-replicaset-fjlm6 0/1 Terminating 0 4m17s
pc-replicaset-fmb8f 1/1 Running 0 118m
pc-replicaset-s2hwj 0/1 Terminating 0 4m17s
pc-replicaset-snrk2 1/1 Running 0 118m
删除 ReplicaSet
常规删除(删除 RS 及管理的 Pod )
# 使用 kubectl delete 命令会删除此 RS 以及它管理的 Pod
# 在 kubernetes 删除 RS 前,会将 RS 的 replicas 调整为 0,等待所有的 Pod 被删除后,在执行 RS 对象的删除
[root@master ~]# kubectl delete rs pc-replicaset -n dev
replicaset.apps "pc-replicaset" deleted
# 验证 Pod 是否被删除
[root@master ~]# kubectl get pod -n dev -o wide
No resources found in dev namespace.
仅删除 RS 对象(保留 Pod,不推荐 )
# 如果希望仅删除 RS 对象(保留 Pod),可以使用 kubectl delete 命令时添加 --cascade=false 选项(不推荐)。
[root@master ~]# kubectl delete rs pc-replicaset -n dev --cascade=false
replicaset.apps "pc-replicaset" deleted
# 验证 Pod 是否保留
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-clslz 1/1 Running 0 75s
pc-replicaset-ds1b3 1/1 Running 0 75s
通过 YAML 文件删除
# 也可以使用 yaml 直接删除(推荐)
[root@master ~]# kubectl delete -f pc-replicaset.yaml
replicaset.apps "pc-replicaset" deleted
Deployment
为了更好的解决服务编排的问题,kubernetes 在 V1.2 版本开始,引入了 Deployment 控制器。值得一提的是,这种控制器并不直接管理 pod,而是通过管理 ReplicaSet 来间接管理 Pod,即:Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。所以 Deployment 比 ReplicaSet 功能更加强大。
Deployment 主要功能有下面几个:
支持 ReplicaSet 的所有功能
支持发布的停止、继续
支持版本滚动升级和版本回退
Deployment 资源清单
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: deploy
spec: # 详情描述
replicas: 3 # 副本数量
revisionHistoryLimit: 3 # 保留历史版本,默认是10
paused: false # 暂停部署,默认是false
progressDeadlineSeconds: 600 # 部署超时时间(s),默认是600
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
创建 pc-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
扩缩容
变更副本数量为 5 个
[root@master ~]# kubectl scale deploy pc-deployment --replicas=5 -n dev
deployment.apps/pc-deployment scaled
[root@master ~]# kubectl get deploy pc-deployment -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
pc-deployment 5/5 5 5 2m
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6696798b78-d2c8n 1/1 Running 0 4m19s
pc-deployment-6696798b78-jxmdq 1/1 Running 0 94s
pc-deployment-6696798b78-mktqv 1/1 Running 0 93s
pc-deployment-6696798b78-smpvp 1/1 Running 0 4m19s
pc-deployment-6696798b78-wvjd8 1/1 Running 0 4m19s
编辑 deployment 的副本数量,修改 spec:replicas: 4 即可
[root@master ~]# kubectl edit deploy pc-deployment -n dev
deployment.apps/pc-deployment edited
镜像更新
Deployment 支持两种镜像更新的策略:重建更新 和 滚动更新(默认),可以通过 strategy 选项进行配置。
strategy 配置说明
strategy:指定新的 Pod 替换旧的 Pod 的策略,支持两个属性:
type:指定策略类型,支持两种策略
Recreate:在创建出新的 Pod 之前会先杀掉所有已存在的 Pod
RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个 版本 Pod
rollingUpdate:当 type 为 RollingUpdate 时生效,用于为 RollingUpdate 设置参数,支持两个属性:
maxUnavailable:用来指在升级过程中不可用 Pod 的最大数量,默认为 25%。
maxSurge:用来指定在升级过程中可以超过期望的 Pod 的最大数量,默认为 25%。
重建更新
编辑 pc-deployment.yaml,在 spec 节点下添加更新策略
spec:
strategy: # 策略
type: Recreate # 重建更新策略
创建 deploy 进行验证
[root@master ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev
deployment.apps/pc-deployment image updated
[root@master ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
pc-deployment-5d89bdfbf9-c182j 1/1 Running 0 78s
pc-deployment-5d89bdfbf9-kkh9r 1/1 Running 0 78s
# (此处会看到旧 Pod 被删除、新 Pod 重建的过程 )
滚动更新
编辑 pc-deployment.yaml。在 spec 节点下添加更新策略
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
创建 deploy 进行验证
[root@master ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev
deployment.apps/pc-deployment image updated
[root@master ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
# (此处会显示 Pod 滚动更新的过程,旧 Pod 逐步替换为新 Pod )
镜像更新中 RS 的变化
[root@server ~]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
pc-deployment-5d89bdfbf9 0 0 0 6m30s
pc-deployment-675d469f8b 3 3 3 3m10s
pc-replicaset 3 3 1 66m
版本回退
deployment 支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能,下面具体来看。
kubectl rollout:版本升级相关功能,支持下面的选项:
status:显示当前升级状态
history:显示升级历史记录
pause:暂停版本升级过程
resume:继续已经暂停的版本升级过程
restart:重启版本升级过程
版本回退相关
undo:回滚到上一级版本(可以使用--to-revision回滚到指定版本)
# 查看当前升级版本的状态
[root@master ~]# kubectl rollout status deploy pc-deployment -n dev
deployment "pc-deployment" successfully rolled out
# 查看升级历史记录
[root@master ~]# kubectl rollout history deploy pc-deployment -n dev
deployment.apps/pc-deployment
REVISION CHANGE-CAUSE
1 kubectl create --filename=pc-deployment.yaml --record=true
2 kubectl create --filename=pc-deployment.yaml --record=true
3 kubectl create --filename=pc-deployment.yaml --record=true
# 可以发现有三次版本记录,说明完成过两次升级
# 版本回滚
# 这里直接使用--to-revision=1回滚到了1版本,如果省略这个选项,就是回退到上个版本,就是2版本
[root@master ~]# kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev
deployment.apps/pc-deployment rolled back
# 查看发现,通过nginx镜像版本可以发现到了第一版
[root@master ~]# kubectl get deploy -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES
pc-deployment 4/4 4 4 74m nginx nginx:1.17.1
# 查看rs,发现第一个rs中有4个pod运行,后面两个版本的rs中pod为运行
# 其实deployment之所以可实现版本的回滚,就是通过记录历史rs来实现的,
金丝雀发布
Deployment 支持更新过程中的控制,如 “暂停 (pause)” 或 “继续 (resume)” 更新操作。
比如有一批新的 Pod 资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的 Pod 应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的 Pod 资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。
# 更新deployment的版本,并配置等待deployment
[root@master ~]# kubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment -n dev
deployment.apps/pc-deployment image updated
deployment.apps/pc-deployment paused
# 观察更新状态
[root@master ~]# kubectl rollout status deploy pc-deployment -n dev
Waiting for deployment "pc-deployment" rollout to finish: 2 out of 4 new replicas have been updated...
# 监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是因为使用了pause暂停命令
[root@master ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES
pc-deployment-5d89bdfbf9 3 3 3 19m nginx nginx:1.17.1
pc-deployment-675d469f8b 0 0 0 14m nginx nginx:1.17.2
pc-deployment-6c9f56cfb 2 2 2 3m16s nginx nginx:1.17.4
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-5d89bdfbf9-rj8sq 1/1 Running 0 7m33s
...
pc-deployment-6c9f56cfb-j2gtj 1/1 Running 0 3m31s
# 确保更新的pod没问题了,继续更新
[root@master ~]# kubectl rollout resume deploy pc-deployment -n dev
deployment.apps/pc-deployment resumed
# 查看最后的更新情况
[root@master ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES
pc-deployment-5d89bdfbf9 0 0 0 21m nginx nginx:1.17.1
pc-deployment-675d469f8b 0 0 0 16m nginx nginx:1.17.2
pc-deployment-6c9f56cfb 4 4 4 5m11s nginx nginx:1.17.4
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6c9f56cfb-7bfwh 1/1 Running 0 37s
pc-deployment-6c9f56cfb-996rt 1/1 Running 0 5m27s
pc-deployment-6c9f56cfb-j2gtj 1/1 Running 0 5m27s
pc-deployment-6c9f56cfb-rf84v 1/1 Running 0 37s
删除 Deployment
# 删除该 deployment,其下的 rs 和 pod 也将被删除
[root@master ~]# kubectl delete -f pc-deployment.yaml
deployment.apps "pc-deployment" deleted
Horizontal Pod Autoscaler(HPA)
可以通过手工执行 kubectl scale 命令实现 Pod 扩容,但这不符合 Kubernetes “自动化、智能化” 的定位目标。Kubernetes 期望通过监测 Pod 使用情况,自动调整 Pod 数量,于是产生了 HPA(Horizontal Pod Autoscaler) 这种控制器。
HPA 可获取每个 Pod 利用率,与 HPA 中定义的指标对比,计算需伸缩的具体值,最终调整 Pod 数量。其实 HPA 和之前的 Deployment 一样,也属于 Kubernetes 资源对象,它通过追踪分析目标 Pod 的负载变化情况,确定是否需要针对性调整目标 Pod 的副本数。
1 安装 metrics-server
metrics-server 可以用来收集集群中的资源使用情况
# 安装 git
[root@master ~]# yum install git -y
# 获取 metrics-server,注意使用的版本
[root@master ~]# git clone -b v0.3.6 https://github/kubernetes-incubator/metrics-server
# 修改 deployment,注意修改的是镜像和初始化参数
[root@master ~]# cd metrics-server/deploy/1.8+
[root@master 1.8+]# vim metrics-server-deployment.yaml
在
spec:
这里添加 hostNetwork: true
serviceAccountName: metrics-server
- name: tmp-dir
emptyDir: {}
containers:
- name: metrics-server
image: registry-hangzhou.aliyuncs/google_containers/metrics-server-amd64:v0.3.6
imagePullPolicy: Alwaysi
args:
--kubelet-insecure-tls
--kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
volumeMounts:
- name: tmp-dir
mountPath: /tmp
安装 metrics-server
[root@master 1.8+]# kubectl apply -f ./
[root@master 1.8+]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
metrics-server-6b976979db-2xwbj 1/1 Running 0 90s
# 查看节点资源
[root@master 1.8+]# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 98m 4% 1067Mi 62%
node1 27m 1% 727Mi 42%
node2 34m 1% 800Mi 46%
# 查看 kube-system 命名空间下 pod 资源
[root@master 1.8+]# kubectl top pod -n kube-system
NAME CPU(cores) MEMORY(bytes)
coredns-6955765f44-7ptsb 3m 9Mi
coredns-6955765f44-vcwr5 3m 8Mi
etcd-master 14m 145Mi
...
准备 Deployment 和 Service
创建 Deployment和Service
[root@master 1.8+]# kubectl run nginx --image=nginx:1.17.1 --requests=cpu=100m -n dev
[root@master 1.8+]# kubectl expose deployment nginx --type=NodePort --port=80 -n dev
[root@master 1.8+]# kubectl get deployment,pod,svc -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 47s
NAME READY STATUS RESTARTS AGE
pod/nginx-7df9756ccc-bh8dr 1/1 Running 0 47s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx NodePort 10.109.57.248 <none> 80:31136/TCP 35s
部署 HPA
创建 pc-hpa.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: pc-hpa
namespace: dev
spec:
minReplicas: 1 # 最小 pod 数量
maxReplicas: 10 # 最大 pod 数量
targetCPUUtilizationPercentage: 3 # CPU 使用率指标
scaleTargetRef: # 指定要控制的 nginx 信息
apiVersion: apps/v1
kind: Deployment
name: nginx
创建 HPA
[root@master 1.8+]# kubectl create -f pc-hpa.yaml
horizontalpodautoscaler.autoscaling/pc-hpa created
查看 HPA
[root@master 1.8+]# kubectl get hpa -n dev
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
pc-hpa Deployment/nginx 0%/3% 1 10 1 62s
DaemonSet (DS)
DaemonSet 类型的控制器可以保证集群中的每一台(或指定)节点上都运行一个副本,一般适用于日志收集、节点监控等场景。也就是说,如果一个 Pod 提供的功能是节点级别的(每个节点都需要且只需要一个 ),那么这类 Pod 就适合使用 DaemonSet 类型的控制器创建。
DaemonSet 控制器的特点
每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
当节点从集群中移除时,Pod 也就被垃圾回收了
DaemonSet 资源清单
apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: daemonset
spec: # 详情描述
revisionHistoryLimit: 3 # 保留历史版本
updateStrategy: # 更新策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxUnavailable: 1 # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
创建 pc-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: pc-daemonset
namespace: dev
spec:
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
Job
Job 主要用于负责批量处理短暂的一次性任务。Job 特点如下:
当 Job 创建的 Pod 执行成功时,Job 将记录成功结束的 Pod 数量
当成功结束的 Pod 达到指定的数量时,Job 将完成执行
Job 的资源清单文件
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: job
spec: # 详情描述
completions: 1 # 指定 job 需要成功运行 Pods 的次数。默认值: 1
parallelism: 1 # 指定 job 在任一时刻应该并发运行 Pods 的数量。默认值: 1
activeDeadlineSeconds: 30 # 指定 job 运行的时间期限,超过时间还未结束,系统将会尝试进行终止。
backoffLimit: 6 # 指定 job 失败后进行重试的次数。默认是6
manualSelector: true # 是否可以使用selector选择器选择pod,默认是false
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: counter-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [counter-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never # 重启策略只能设置为Never或者OnFailure
containers:
- name: counter
image: busybox:1.30
command: ["/bin/sh", "-c", "for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]
关于重启策略设置的说明:
如果指定为 OnFailure,则 job 会在 pod 出现故障时重启容器,而不是创建 pod,failed 次数不变
如果指定为 Never,则 job 会在 pod 出现故障时创建新的 pod,并且故障 pod 不会消失,也不会重启,failed 次数加 1
如果指定为 Always 的话,就意味着一直重启,意味着 job 任务会重复去执行了,当然不对,所以不能设置为 Always
创建pc-job.yaml,内容如下:
apiVersion: batch/v1
kind: Job
metadata:
name: pc-job
namespace: dev
spec:
manualSelector: true
selector:
matchLabels:
app: counter-pod
template:
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never
containers:
- name: counter
image: busybox:1.30
command: ["/bin/sh", "-c", "for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
CronJob()
CronJob 控制器以 Job 控制器资源为其管控对象,并借助它管理 Pod 资源对象,Job 控制器定义的作业任务在其控制器资源创建之后便会立即执行,但 CronJob 可以以类似于 Linux 操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob 可以在特定的时间点(反复的)去运行 Job 任务。
CronJob 的资源清单文件
apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: cronjob
spec: # 详情描述
schedule: # cron 格式的作业调度运行时间点,用于控制任务在什么时间执行
concurrencyPolicy: # 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业
failedJobHistoryLimit: # 为失败的任务执行保留的历史记录数,默认 为 1
successfulJobHistoryLimit: # 为成功的任务执行保留的历史记录数,默认为3
startingDeadlineSeconds: # 启动作业错误的超时时长
jobTemplate: # job 控制器模板,用于为 cronjob 控制器生成 job 对象;下面其实就是 job 的定义
metadata:
spec:
completions: 1
parallelism: 1
activeDeadlineSeconds: 30
backoffLimit: 6
manualSelector: true
selector:
matchLabels:
app: counter-pod
matchExpressions: 规则
- {key: app, operator: In, values: [counter-pod]}
template:
metadata:
重点选项解释
1. schedule
cron 表达式,用于指定任务的执行时间,格式:
<分钟> <小时> <日> <月份> <星期>
2、concurrencyPolicy
并发执行策略,定义前一次作业未完成时,如何处理后一次作业:
Allow:允许 Jobs 并发运行(默认)
Forbid:禁止并发运行,若上一次未完成,则跳过下一次运行
Replace:替换,取消当前运行的作业,并用新作业替换它
创建 pc-cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: pc-cronjob
namespace: dev
labels:
controller: cronjob
spec:
schedule: "*/1 * * * *"
jobTemplate:
metadata:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: counter
image: busybox:1.30
command: ["/bin/sh", "-c", "for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
Service 介绍
在 Kubernetes 中,Pod 是应用程序的载体,我们可以通过 Pod 的 IP 来访问应用程序,但 Pod 的 IP 地址不是固定的,这意味着不方便直接采用 Pod 的 IP 对服务进行访问。
为了解决这个问题,Kubernetes 提供了 Service 资源。Service 会对提供同一个服务的多个 Pod 进行聚合,并且提供一个统一的入口地址。通过访问 Service 的入口地址就能访问到后面的 Pod 服务。
Service 在很多场景下是逻辑概念,实际起关键作用的是 kube-proxy 服务进程 。Kubernetes 集群里,每个 Node 节点都会运行一个 kube-proxy 进程。
当创建 Service 时,会经 api-server 向 etcd 写入 Service 信息;kube-proxy 依托监听机制感知 Service 变动,再把最新的 Service 信息转化为对应的访问规则,以此实现 Service 对 Pod 访问的代理和转发,让客户端能通过统一入口稳定访问后端 Pod 。
中 kube - proxy 实现服务代理的两种模式(ipvs 模式、iptables 模式 )说明,核心区别和特点整理如下:
1. ipvs 模式
工作逻辑:与 iptables 类似,kube - proxy 监控 Pod 变化,动态创建 IPVS 规则。
核心优势:
转发效率比 iptables 更高(基于 Linux 内核的 IP 虚拟服务器,适合高并发);
支持更多负载均衡(LB)算法(如 rr、wrr、sh 等),策略更灵活。
2. iptables 模式
工作逻辑:kube - proxy 为 Service 后端每个 Pod 生成 iptables 规则,把发往 Cluster IP 的请求,直接重定向到某个 Pod IP。
特点:
kube - proxy 不承担 “四层负载均衡器” 角色,仅负责维护 iptables 规则;
相比 userspace 模式效率更高,但 LB 策略不够灵活(算法少),且后端 Pod 不可用时无法自动重试。
简单说,ipvs 更适合对性能、策略灵活性要求高的场景;iptables 模式相对轻量但功能有局限,可根据集群规模和需求选择 。
ipvs 模式
编辑 kube - proxy 配置 ConfigMap:
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
删除旧的 kube - proxy Pod,让新配置生效
[root@master ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
验证 ipvs 规则
通过 ipvsadm -Ln 查看 IPVS 规则,确认 Service 转发逻辑:
Kubernetes 启用 ipvs 代理模式的完整流程:改配置 → 重启 Pod → 用 ipvsadm 验证规则,确保服务转发走高性能的 ipvs 路径 。
Service 类型
Service 的资源清单文件示例
kind: Service # 资源类型
apiVersion: v1 # 资源版本
metadata: # 元数据
name: service # 资源名称
namespace: dev # 命名空间
spec: # 描述
selector: # 标签选择器,用于确定当前 service 代理哪些 pod
app: nginx
type: # Service 类型,指定 service 的访问方式
clusterIP: # 虚拟服务的 ip 地址
sessionAffinity: # session 亲和性,支持 ClientIP、None 两个选项
ports: # 端口信息
- protocol: TCP # 协议
port: 3017 # service 端口
targetPort: 5083 # pod 端口
nodePort: 31122 # 主机端口
Service 类型说明
ClusterIP(默认值):
Kubernetes 系统自动分配的虚拟 IP,仅能在集群内部访问,用于集群内服务间通信。
NodePort:
将 Service 通过指定 Node 上的端口暴露给外部,集群外部可通过 节点IP:nodePort 访问服务,实现 “从集群外访问内部服务”。
LoadBalancer:
借助外接负载均衡器(需云环境支持,如 AWS ELB、阿里云 SLB 等)分发流量到 Service,适合公网访问场景。
ExternalName:
把集群外部服务引入集群内部,通过 DNS 别名(如 externalname.service.dev.svc.cluster.local )让集群内直接调用,无需关心外部服务的实际地址。
Service 使用
在使用 Service 之前,需先通过 Deployment 创建 3 个 Pod,并为 Pod 设置 app=nginx-pod 标签
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
Deployment 创建与 Pod 验证
创建 Deployment
kubectl create -f deployment.yaml deployment.apps/pc-deployment created
kubectl get pods -n dev -o wide --show-labels
修改 Pod 内容(测试用)
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p8dh -n dev /bin/sh
# echo "10.244.1.40" > /usr/share/nginx/html/index.html
[root@master ~]# curl 10.244.1.40
10.244.1.40
[root@master ~]# curl 10.244.2.33
10.244.2.33
[root@master ~]# curl 10.244.1.39
10.244.1.39
ClusterIP 类型 Service 配置与验证
创建 service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: service-clusterip
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: 10.97.97.97 # 自定义 ClusterIP,可不写(自动分配)
type: ClusterIP
ports:
- port: 80 # Service 端口
targetPort: 80 # Pod 端口
[root@master ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-clusterip ClusterIP 10.97.97.97 <none> 80/TCP 13s app=nginx-pod
[root@master ~]# kubectl describe svc service-clusterip -n dev
Endpoint 与 Service 负载分发
Endpoint 概念
Endpoint 是 Kubernetes 的资源对象,存储在 etcd 中,记录 Service 对应 Pod 的访问地址,由 Service 的 selector 自动关联生成。
作用:作为 Service 和 Pod 的 “桥梁”,暴露后端 Pod 端点,让 Service 能找到要转发的目标。
Endpoint 查看(以 service-clusterip 为例)
[root@master ~]# kubectl get endpoints -n dev -o wide
NAME ENDPOINTS AGE
service-clusterip 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80 50m
Service 负载分发策略
Kubernetes 为 Service 提供两种核心负载策略:
(1)默认策略(由 kube-proxy 决定)
常见方式:随机、轮询等(取决于 kube-proxy 模式,如 ipvs/iptables 的算法)。
特点:请求均匀分发到后端 Pod,适合无状态服务。
(2)会话保持(基于客户端地址)
配置方式:在 Service 的 spec 中添加:
sessionAffinity: ClientIP
效果:同一客户端的请求,始终转发到固定 Pod,适合有状态场景(如需要会话延续的业务)。
核心逻辑
Endpoint 动态维护 Service 关联的 Pod 端点列表,是 Service 转发请求的 “数据源”。
负载策略 决定请求如何在这些端点中分配,实现流量分发的灵活性(默认均匀分发 / 可选会话保持)。
简单说,Endpoint 是 Service 找到 Pod 的 “地图”,负载策略是 “行驶规则”,共同保障 Service 对 Pod 的访问和流量管理 。
HeadLess 类型的 Service
场景与作用
在某些场景中,开发人员可能不想使用 Service 提供的负载均衡功能,而希望自己控制负载均衡策略。针对这种情况,Kubernetes 提供了 HeadLess Service:
不分配 Cluster IP;
需通过 Service 的域名访问,可自定义负载逻辑(如手动轮询、按业务规则调度)。
创建 service-headliness.yaml
apiVersion: v1
kind: Service
metadata:
name: service-headliness
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None # 将 clusterIP 设置为 None,创建 HeadLess Service
type: ClusterIP
ports:
- port: 80
targetPort: 80
操作与验证
[root@master ~]# kubectl create -f service-headliness.yaml
service/service-headliness created
[root@master ~]# kubectl get svc service-headliness -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-headliness ClusterIP None <none> 80/TCP 11s app=nginx-pod
核心特点
无 Cluster IP:依赖 DNS 解析 Service 域名(如 service-headliness.dev.svc.cluster.local ),解析结果是后端 Pod 的 IP 列表。
自定义负载:客户端拿到 Pod IP 后,可自行实现负载策略(如业务层轮询、按状态筛选)。
适合需要精细控制流量分发的场景(如数据库读写分离、自定义权重调度),把负载逻辑从 Kubernetes 转移到应用或客户端侧。
查看 HeadLess Service 域名解析
进入 Pod 查看 DNS 配置
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p8dh -n dev /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local
用 dig 解析 HeadLess Service 域名
[root@master ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.40
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.39
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.33
NodePort 类型的 Service
场景与作用
之前示例中,Service 的 IP(Cluster IP)仅集群内部可访问。若需暴露给集群外部,可使用 NodePort 类型 Service:
原理:将 Service 端口映射到集群节点的某个端口,外部通过 NodeIP:NodePort 访问 Service。
创建 service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
namespace: dev
spec:
selector:
app: nginx-pod
type: NodePort # 声明 Service 类型为 NodePort
ports:
- port: 80 # Service 自身端口
nodePort: 30002 # 映射到节点的端口(范围默认 30000-32767,不指定则自动分配)
targetPort: 80 # Pod 端口
操作与验证
[root@master ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) SELECTOR
service-nodeport NodePort 10.105.64.191 <none> 80:30002/TCP app=nginx-pod
核心特点
集群外访问:突破 Cluster IP 的局限,让外部流量通过节点 IP + 映射端口进入集群。
端口范围:nodePort 需在 30000-32767(可通过 --service-node-port-range 调整),不指定则由 Kubernetes 自动分配。
适合临时暴露服务到集群外(如测试环境),生产环境建议结合 LoadBalancer 或 Ingress 实现更可靠的公网访问。
LoadBalancer 类型的 Service
场景与作用
与 NodePort 类似,目标是向外部暴露服务端口,但更进阶:
NodePort 仅映射端口到集群节点,LoadBalancer 会额外关联外部负载均衡设备(如云厂商的 ELB、SLB )。
外部请求先到负载均衡设备,再由设备转发到集群内 NodePort / Pod,需外部环境(如公有云)支持。
外部请求 → 云厂商负载均衡器 → 集群节点(NodePort) → Service → Pod
ExternalName 类型的 Service
场景与作用
用于引入集群外部服务到集群内部,让集群内可通过 Service 域名访问外部服务,实现 “服务域名统一化”。
资源清单示例(service-externalname.yaml)
apiVersion: v1
kind: Service
metadata:
name: service-externalname
namespace: dev
spec:
type: ExternalName # 声明类型为 ExternalName
externalName: www.baidu # 外部服务地址(域名或 IP 均可)
[root@master ~]# kubectl create -f service-externalname.yaml
service/service-externalname created
[root@master ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.
www.baidu. 30 IN CNAME www.a.shifen.
www.a.shifen. 30 IN A 39.156.66.18
www.a.shifen. 30 IN A 39.156.66.14
两类 Service 对比
| 类型 | 核心作用 | 依赖环境 | 典型场景 |
|---|---|---|---|
| LoadBalancer | 关联外部负载均衡器,暴露服务到公网 | 公有云环境(如 AWS、阿里云) | 生产环境公网服务(需高可用、流量调度) |
| ExternalName | 引入外部服务到集群内部,统一域名访问 | 无特殊依赖 |
Ingress 介绍
Ingress 只需一个 NodePort 或一个 LB,即可满足多个 Service 的暴露需求,有效解决上述问题。其工作机制可简单理解为:通过统一的入口(Ingress),根据请求的域名、路径等规则,将外部流量转发到集群内不同的 Service ,实现 “一站式” 服务暴露与路由管理。
Ingress 相当于一个 7 层的负载均衡器,是 kubernetes 对反向代理的一个抽象,它的工作原理类似于 Nginx,可以理解成在 Ingress 里建立诸多映射规则,Ingress Controller 通过监听这些配置规则并转化成 Nginx 的反向代理配置,然后对外部提供服务。在这里有两个核心概念:
ingress: kubernetes 中的一个对象,作用是定义请求如何转发到 service 的规则
ingress controller: 具体实现反向代理及负载均衡的程序,对 ingress 定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,比如 Nginx, Contour, Haproxy 等等
Ingress(以 Nginx 为例)的工作原理如下:
用户编写 Ingress 规则,说明哪个域名对应 kubernetes 集群中的哪个 Service
Ingress 控制器动态感知 Ingress 服务规则的变化,然后生成一段对应的 Nginx 配置
Ingress 控制器会将生成的 Nginx 配置写入到一个运行着的 Nginx 服务中,并动态更新
到此为止,其实真正在工作的就是一个 Nginx 了,内部配置了用户定义的请求转发规则
Ingress 使用
搭建 ingress 环境
# 创建文件夹
[root@master ~]# mkdir ingress-controller
[root@master ~]# cd ingress-controller/
# 获取ingress-nginx,本次案例使用的是0.30版本
[root@master ingress-controller]# wget https://raw.githubusercontent/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
[root@master ingress-controller]# wget https://raw.githubusercontent/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
# 修改mandatory.yaml文件中的仓库
# 修改quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 为quay-mirror.qiniu/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 创建ingress-nginx
[root@master ingress-controller]# kubectl apply -f ./
# 查看ingress-nginx
[root@master ingress-controller]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-ingress-controller-fbf967dd-4qbp 1/1 Running 0 12h
# 查看service
[root@master ingress-controller]# kubectl get svc -n ingress-nginx
Nginx Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
Tomcat Deployment(tomcat-nginx.yaml 续段)
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: tomcat-pod
template:
metadata:
labels:
app: tomcat-pod
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
Nginx 与 Tomcat 的 HeadLess Service
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
namespace: dev
spec:
selector:
app: tomcat-pod
clusterIP: None
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
核心作用
Deployment:分别创建 3 个 Nginx Pod(标签 app: nginx-pod )和 3 个 Tomcat Pod(标签 app: tomcat-pod )。
HeadLess Service:通过 clusterIP: None 配置,让服务域名直接解析到 Pod IP(Nginx 用 nginx-service,Tomcat 用 tomcat-service ),实现自定义负载或直接访问 Pod 需求。
Http 代理
创建 ingress-http.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-http
namespace: dev
spec:
rules:
- host: nginx.itheima
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
Https 代理
创建证书
# 生成证书
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima"
# 创建密钥
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
创建 ingress-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-https
namespace: dev
spec:
tls:
- hosts:
- nginx.itheima
- tomcat.itheima
secretName: tls-secret # 指定密钥
rules:
- host: nginx.itheima
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
核心逻辑
HTTP 代理:通过 Ingress 规则,将 nginx.itheima 域名的请求转发到 nginx-service(80 端口 ),tomcat.itheima 转发到 tomcat-service(8080 端口 )。
HTTPS 代理:先通过 openssl 生成证书,用 kubectl 创建 TLS 密钥;再通过 Ingress 配置 tls 段关联证书,实现 HTTPS 加密访问,路由规则与 HTTP 代理一致。
简单说,这是基于 Ingress 实现 “多域名 HTTP/HTTPS 代理” 的完整流程,让外部请求通过域名转发到集群内不同 Service 。
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
192.168.109.100 nginx.itheima
192.168.109.100 tomcat.itheima
删除命名空间卡住
一、先确认命名空间状态
kubectl get ns | grep Terminating
- 若输出类似
xxx-ns Terminating 10m,说明确实卡住,需执行后续操作; - 若无输出,说明命名空间已删除,无需处理。
步骤 1:导出命名空间的 JSON 配置
# 替换 dev 为你卡住的命名空间名称
kubectl get ns dev -o json > ns-dev-terminating.json
步骤 2:编辑 JSON 文件,清空 finalizers 字段
vim ns-dev-terminating.json
"spec": {
"finalizers": [
"kubernetes" // 这行是锁定源,需删除
]
},
修改
"spec": {
"finalizers": [] // 清空 finalizers,解除锁定
},
启动 Kubernetes API 代理
在当前终端启动 API 代理(保持终端打开,不要关闭),用于后续通过 API 直接修改命名空间配置:
kubectl proxy
新启终端,提交删除请求
# 替换 dev 为你卡住的命名空间名称,替换文件名为你实际的 JSON 文件名
curl -H "Content-Type: application/json" -X PUT --data-binary @ns-dev-terminating.json http://127.0.0.1:8001/api/v1/namespaces/dev/finalize
# 替换 dev 为你卡住的命名空间名称
kubectl get ns dev
# 1. 列出命名空间内所有残留资源(替换 dev 为目标命名空间)
kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get -n dev
# 2. 强制删除残留的 Pod(若有)
kubectl delete pod -n dev --all --grace-period=0 --force 2>/dev/null
# 3. 强制删除残留的 Service/Secret/ConfigMap 等(若有)
kubectl delete svc,secret,configmap -n dev --all --grace-period=0 --force 2>/dev/null
# 4. 再次执行步骤 4 的 curl 命令,确认删除
数据存储
Volume 是 Pod 中能够被多个容器访问的共享目录,它被定义在 Pod 上,然后被一个 Pod 里的多个容器挂载到具体的文件目录下,kubernetes 通过 Volume 实现同一个 Pod 中不同容器之间的数据共享以及数据的持久化存储。Volume 的生命容器不与 Pod 中单个容器的生命周期相关,当容器终止或者重启时,Volume 中的数据也不会丢失。
kubernetes 的 Volume 支持多种类型,比较常见的有下面几个:
简单存储:EmptyDir、HostPath、NFS
高级存储:PV、PVC
配置存储:ConfigMap、Secret
EmptyDir
EmptyDir 是最基础的 Volume 类型,一个 EmptyDir 就是 Host 上的一个空目录。
EmptyDir 是在 Pod 被分配到 Node 时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为 kubernetes 会自动分配一个目录,当 Pod 销毁时,EmptyDir 中的数据也会被永久删除。EmptyDir 用途如下:
临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
一个容器需要从另一个容器中获取数据的目录(多容器共享目录)
接下来,通过一个容器之间文件共享的案例来使用一下 EmptyDir。
在一个 Pod 中准备两个容器 nginx 和 busybox,然后声明一个 Volume 分别挂在到两个容器的目录中,然后 nginx 容器负责向 Volume 中写日志,busybox 中通过命令将日志内容读到控制台。
volume-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts: # 将logs-volume挂在到nginx容器中,对应的目录为 /var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容
volumeMounts: # 将logs-volume 挂在到busybox容器中,对应的目录为 /logs
- name: logs-volume
mountPath: /logs
volumes: # 声明volume,name为logs-volume,类型为emptyDir
- name: logs-volume
emptyDir: {}
[root@master ~]# kubectl create -f volume-emptydir.yaml
pod/volume-emptydir created
[root@master ~]# kubectl get pods volume-emptydir -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
HostPath
EmptyDir 中数据不会被持久化,它会随着 Pod 的结束而销毁,如果想简单的将数据持久化到主机中,可以选择 HostPath。
HostPath 就是将 Node 主机中一个实际目录挂在到 Pod 中,以供容器使用,这样的设计就可以保证 Pod 销毁了,但是数据依据可以存在于 Node 主机上。
创建一个 volume-hostpath.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-hostpath
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
hostPath:
path: /root/logs
type: DirectoryOrCreate # 目录存在就使用,不存在就先创建后使用
关于 type 的值的一点说明:
| type 值 | 说明 |
|---|---|
| DirectoryOrCreate | 目录存在就使用,不存在就先创建后使用 |
| Directory | 目录必须存在 |
| FileOrCreate | 文件存在就使用,不存在就先创建后使用 |
| File | 文件必须存在 |
| Socket | unix 套接字必须存在 |
| CharDevice | 字符设备必须存在 |
| BlockDevice | 块设备必须存在 |
NFS
HostPath 可以解决数据持久化的问题,但是一旦 Node 节点故障了,Pod 如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用 NFS、CIFS。
NFS 是一个网络文件存储系统,可以搭建一台 NFS 服务器,然后将 Pod 中的存储直接连接到 NFS 系统上,这样的话,无论 Pod 在节点上怎么转移,只要 Node 跟 NFS 的对接没问题,数据就可以成功访问。
首先要准备 nfs 的服务器,这里为了简单,直接是 master 节点做 nfs 服务器
# 在master上安装nfs服务
[root@master ~]# yum install nfs-utils -y
# 准备一个共享目录
[root@master ~]# mkdir /root/data/nfs -pv
# 将共享目录以读写权限暴露给192.168.109.0/24网段中的所有主机
[root@master ~]# vim /etc/exports
[root@master ~]# more /etc/exports
/root/data/nfs 192.168.109.0/24(rw,no_root_squash)
# 启动nfs服务
[root@master ~]# systemctl start nfs
接下来,要在的每个 node 节点上都安装下 nfs,这样的目的是为了 node 节点可以驱动 nfs 设备
# 在node上安装nfs服务,注意不需要启动
[root@master ~]# yum install nfs-utils -y
3)接下来,就可以编写pod的配置文件了,创建volume-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
nfs:
server: 192.168.109.100 #nfs服务器地址
path: /root/data/nfs #共享文件路径
PV 和 PVC
使用 NFS 提供存储,此时就要求用户会搭建 NFS 系统,并且会在 yaml 配置 nfs。由于 kubernetes 支持的存储系统有很多,要求客户全都掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用,kubernetes 引入 PV 和 PVC 两种资源对象。
PV(Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下 PV 由 kubernetes 管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。
PVC(Persistent Volume Claim)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC 其实就是用户向 kubernetes 系统发出的一种资源需求申请。
使用 PV 和 PVC 后的分工细分
使用了 PV 和 PVC 之后,工作可以得到进一步的细分:
- 存储:存储工程师维护
- PV:kubernetes 管理员维护
- PVC:kubernetes 用户维护
实体与关联
用户侧(上半部分):
Pod:业务容器,通过 PVC 申请存储。
PVC(如 pvc1、pvc2 ):用户对存储的需求声明,关联到具体 PV。
管理员侧(下半部分):
PV(如 pv1、pv2、pv3 等 ):管理员提前创建的持久化卷,与底层存储对接(如 NFS、CIFS、GlusterFS )。
底层存储:NFS(网络文件系统 )、CIFS(通用网络文件系统 )、GlusterFS(分布式文件系统 ),是实际存储数据的地方。
流程逻辑
用户 创建 PVC(如 pvc1 ),声明存储需求;
Kubernetes 自动匹配可用的 PV(如 pv1 ),将 PVC 与 PV 绑定;
Pod 通过 PVC 挂载存储,实现数据持久化;
管理员 维护 PV 及底层存储(如配置 NFS、CIFS 等 ),屏蔽存储细节,让用户只需关注 PVC。
核心价值
解耦存储管理:用户通过 PVC 简单声明需求,管理员通过 PV 对接复杂存储(NFS、CIFS 等 ),分工清晰。
存储抽象化:屏蔽底层存储差异,用户无需关心数据存在 NFS 还是 GlusterFS,只需用 PVC 申请即可。
简单说,这是 Kubernetes 中 “用户按需申请(PVC)→ 系统匹配存储(PV)→ 对接底层存储” 的经典流程,让存储管理更灵活、更易维护 。
PV
PV 是存储资源的抽象,下面是资源清单文件:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存储类型,与底层真正存储对应
capacity: # 存储能力,目前只支持存储空间的设置
storage: 2Gi
accessModes: # 访问模式
storageClassName: # 存储类别
persistentVolumeReclaimPolicy: # 回收策略
PV 的关键配置参数说明:
-
存储类型
底层实际存储的类型,kubernetes 支持多种存储类型,每种存储类型的配置都有所差异 -
存储能力(capacity)
目前只支持存储空间的设置 (storage=1Gi),不过未来可能会加入 IOPS、吞吐量等指标的配置 -
访问模式(accessModes)
用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:- ReadWriteOnce (RWO):读写权限,但是只能被单个节点挂载
- ReadOnlyMany (ROX): 只读权限,可以被多个节点挂载
- ReadWriteMany (RWX):读写权限,可以被多个节点挂载
需要注意的是,底层不同的存储类型可能支持的访问模式不同
-
回收策略 (persistentVolumeReclaimPolicy)
当 PV 不再被使用了之后,对其的处理方式。目前支持三种策略:- Retain(保留) 保留数据,需要管理员手工清理数据
- Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
- Delete(删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务
需要注意的是,底层不同的存储类型可能支持的回收策略不同
补充参数说明
-
存储类别
PV 可以通过storageClassName参数指定一个存储类别- 具有特定类别的 PV 只能与请求了该类别的 PVC 进行绑定
- 未设定类别的 PV 则只能与不请求任何类别的 PVC 进行绑定
-
状态 (status)
一个 PV 的生命周期中,可能会处于 4 中不同的阶段:- Available(可用): 表示可用状态,还未被任何 PVC 绑定
- Bound(已绑定): 表示 PV 已经被 PVC 绑定
- Released(已释放): 表示 PVC 被删除,但是资源还未被集群重新声明
- Failed(失败): 表示该 PV 的自动回收失败
使用 NFS 作为存储,来演示 PV 的使用,创建 3 个 PV,对应 NFS 中的 3 个暴露的路径。
准备 NFS 环境
# 创建目录
[root@master ~]# mkdir /root/data/{pv1,pv2,pv3} -pv
# 暴露服务
[root@master ~]# more /etc/exports
/root/data/pv1 192.168.109.0/24(rw,no_root_squash)
/root/data/pv2 192.168.109.0/24(rw,no_root_squash)
/root/data/pv3 192.168.109.0/24(rw,no_root_squash)
# 重启服务
[root@master ~]# systemctl restart nfs
创建 pv.yaml
pv1 定义:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv1
server: 192.168.109.100
pv2 定义:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv2
server: 192.168.109.100
pv3 定义:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv3
server: 192.168.109.100
PVC
PVC 是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息。下面是资源清单文件:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 访问模式
selector: # 采用标签对 PV 选择
storageClassName: # 存储类别
resources: # 请求空间
requests:
storage: 5Gi
实验
创建 pvc.yaml 申请 pv
pvc1 定义:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
pvc2 定义:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
创建 pods.yaml, 使用 pv
创建 pods.yaml, 使用 pv
pod1 定义
apiVersion: v1
kind: Pod
metadata:
name: pod1
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc1
readOnly: false
pod2 定义
apiVersion: v1
kind: Pod
metadata:
name: pod2
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "while true;do echo pod2 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc2
readOnly: false
核心逻辑
Pod 与 PVC 关联:通过 persistentVolumeClaim 字段,pod1 绑定 pvc1,pod2 绑定 pvc2。
数据持久化:Pod 内的 busybox 容器持续向挂载目录(/root/ )写数据,数据会持久化到 PVC 关联的 PV 存储中。
简单说,这是 “Pod → PVC → PV” 存储使用流程的实践,让 Pod 数据借助 PVC 动态关联 PV 实现持久化 。
生命周期
PVC 和 PV 是一一对应的,PV 和 PVC 之间的相互作用遵循以下生命周期:
- 资源供应:管理员手动创建底层存储和 PV
- 资源绑定:用户创建 PVC,kubernetes 负责根据 PVC 的声明去寻找 PV,并绑定
- 在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在已存在的 PV 中选择一个满足条件的
- 一旦找到,就将该 PV 与用户定义的 PVC 进行绑定,用户的应用就可以使用这个 PVC 了
- 如果找不到,PVC 则会无限期处于 Pending 状态,直到等到系统管理员创建了一个符合其要求的 PV
- PV 一旦绑定到某个 PVC 上,就会被这个 PVC 独占,不能再与其他 PVC 进行绑定了
- 在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在已存在的 PV 中选择一个满足条件的
- 资源使用:用户可在 pod 中像 volume 一样使用 pvc
- Pod 使用 Volume 的定义,将 PVC 挂载到容器内的某个路径进行使用。
- 资源释放:用户删除 pvc 来释放 pv
- 当存储资源使用完毕后,用户可以删除 PVC,与该 PVC 绑定的 PV 将会被标记为 “已释放”,但还不能立刻与其他 PVC 进行绑定。通过之前 PVC 写入的数据可能还被留在存储设备上,只有在清除之后该 PV 才能再次使用。
- 资源回收:kubernetes 根据 pv 设置的回收策略进行资源的回收
- 对于 PV,管理员可以设定回收策略,用于设置与之绑定的 PVC 释放资源之后如何处理遗留数据的问题。只有 PV 的存储空间完成回收,才能供新的 PVC 绑定和使用
ConfigMap
ConfigMap 是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。
创建 configmap.yaml,内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap
namespace: dev
data:
info: |
username:admin
password:123456
ConfigMap 操作与 Pod 挂载流程
创建 ConfigMap
# 创建configmap
[root@master ~]# kubectl create -f configmap.yaml
configmap/configmap created
# 查看configmap详情
[root@master ~]# kubectl describe cm configmap -n dev
挂载 ConfigMap 到 Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将configmap挂载到目录
- name: config
mountPath: /configmap/config
volumes: # 引用configmap
- name: config
configMap:
name: configmap
# 创建pod
[root@master ~]# kubectl create -f pod-configmap.yaml
pod/pod-configmap created
# 查看pod
[root@master ~]# kubectl get pod pod-configmap -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 0 6s
# 进入容器
[root@master ~]# kubectl exec -it pod-configmap -n dev /bin/sh
# cd /configmap/config/
# ls
info
# more info
username:admin
password:123456
# 可以看到映射已经成功,每个configmap都映射成了一个目录
# key--->文件 value---->文件中的内容
# 此时如果更新configmap的内容,容器中的值也会动态更新
核心验证逻辑
创建 & 查看 Pod:确认 Pod 正常运行。
进入容器:通过 exec 进入 Pod 内的容器。
检查挂载:进入 ConfigMap 挂载目录(/configmap/config/ ),查看文件(info )及内容,验证 ConfigMap 配置已成功挂载到容器内。
简单说,这是 “验证 ConfigMap 挂载是否生效” 的实操流程,确认配置可被容器读取 。
简单说,这是 “验证 ConfigMap 挂载是否生效” 的实操流程,确认配置可被容器读取 。
Secret
在 kubernetes 中,还存在一种和 ConfigMap 非常类似的对象,称为 Secret 对象。它主要用于存储敏感信息,例如密码、秘钥、证书等等。
首先使用 base64 对数据进行编码
[root@master ~]# echo -n 'admin' | base64 #准备username
YWRtaW4=
[root@master ~]# echo -n '123456' | base64 #准备password
MTIzNDU2
接下来编写 secret.yaml,并创建 Secret
apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: dev
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
创建 pod-secret.yaml,将上面创建的 secret 挂载进去:
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将secret挂载到目录
- name: config
mountPath: /secret/config
volumes:
- name: config
secret:
secretName: secret
访问控制概述
Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对 Kubernetes 的各种客户端进行认证和鉴权操作。
客户端
在 Kubernetes 集群中,客户端通常有两类:
User Account:一般是独立于 kubernetes 之外的其他服务管理的用户账号。
Service Account:kubernetes 管理的账号,用于为 Pod 中的服务进程在访问 Kubernetes 时提供身份标识。
认证、授权与准入控制
ApiServer 是访问及管理资源对象的唯一入口。任何一个请求访问 ApiServer,都要经过下面三个流程:
Authentication(认证):身份鉴别,只有正确的账号才能够通过认证
Authorization(授权): 判断用户是否有权限对访问的资源执行特定的动作
Admission Control(准入控制):用于补充授权机制以实现更加精细的访问控制功能。
安装前准备
需要 64 位操作系统
至少 RHEL6.5 以上的版本,强烈推荐 RHEL7
关闭防火墙(不是必须)
软件包安装
yum -y install docker
systemctl restart docker
systemctl enable docker
准备3台虚拟机
172.25.0.11,
172.25.0.10,docker1
172.25.0.33 docker2
安装docker
[root@c1 ~]# yum -y install docker
[root@destop ~]# yum -y install docker
yum install libguestfs-tools-c
安装虚拟化服务器平台
- KVM /QEMU /LIBVIRTD
必备软件
qemu - kvm
为 kvm 提供底层仿真支持
libvirt - daemon
libvirtd 守护进程,管理虚拟机
libvirt - client
用户端软件,提供客户端管理命令
libvirt - daemon - driver - qemulibvirtd
连接 qemu 的驱动
- KVM 是 linux 内核的模块,它需要 CPU 的支持,采用硬件辅助虚拟化技术 Intel-VT,AMD-V,内存的相关如 Intel 的 EPT 和 AMD 的 RVI 技术
- QEMU 是一个虚拟化的仿真工具,通过 ioctl 与内核 kvm 交互完成对硬件的虚拟化支持
- Libvirt 是一个对虚拟化管理的接口和工具,提供用户端程序 virsh, virt-install, virt-manager, virt-view 与用户交互
虚拟化平台的安装 yum install -y qemu-kvm \ libvirt-daemon \ libvirt-client \ libvirt-daemon-driver-qemu systemctl start libvirtd虚拟机的组成
- 内核虚拟化模块(KVM) - 系统设备仿真(QEMU) - 虚拟机管理程序(LIBVIRT)
一个 XML 文件(虚拟机配置声明文件)
- 位置 /etc/libvirt/qemu/ -
一个磁盘镜像文件(虚拟机的硬盘)
- 位置 /var/lib/libvirt/images/
virsh 命令工具介绍
- 提供管理各虚拟机的命令接口
- 支持交互模式,查看 / 创建 / 停止 / 关闭 …
- 格式:virsh 控制指令 [虚拟机名称] [参数]
[root@nova01 ~]# virsh
Welcome to virsh, the virtualization interactive terminal.
Type: 'help' for help with commands
'quit' to quit
virsh #
virsh 虚拟机管理
- list [--all] 列出虚拟机
- start|shutdown|reboot 虚拟机启动,停止,重启
- destroy 强制停止虚拟机
- define|undefine 根据 xml 文件 创建/删除 虚拟机
- console 连接虚拟机的 console ctrl + ] 退出
- edit 修改虚拟机的配置
- autostart 设置虚拟机自启动
- domiflist 查看虚拟机网卡信息
- domblklist 查看虚拟机硬盘信息
virsh 虚拟网络管理
- net-list [--all] 列出虚拟网络
- net-start 启动虚拟交换机
- net-destroy 强制停止虚拟交换机
- net-define 根据 xml 文件 创建虚拟网络
- net-undefine 删除一个虚拟网络设备
- net-edit 修改虚拟交换机的配置
- net-autostart 设置虚拟交换机自启动
qemu-img命令
- qemu-img 是虚拟机的磁盘管理命令,支持非常多的磁盘格式,例如raw、qcow2、vdi、vmdk等等
-qemu-img 命令格式
- qemu-img 命令 参数 块文件名称 大小
- 常用的命令有
- create 创建一个磁盘 qemu-img create -f <格式> <镜像路径> <大小>
- convert 转换磁盘格式 qemu-img convert -f <原格式> -O <目标格式> <原镜像路径> <目标镜像路径>
- info 查看磁盘信息 qemu-img info <镜像路径>
- resize 扩容磁盘空间
qemu-img resize <镜像路径> <新大小> # 增加或减少大小(+N 表示增加,-N 表示减少,直接写数字表示设置为固定大小)
创建新的镜像盘文件
qemu-img create -f 格式 磁盘路径 大小
qemu-img create -f qcow2 disk.img 50G
查询镜像盘文件的信息
qemu-img info 磁盘路径
qemu-img info disk.img
-b 使用后端模板文件
qemu-img create -b disk.img -f qcow2 disk1.img
COW技术原理
- Copy On Write,写时复制 - 直接映射原始盘的数据内容 - 当数据有修改要求时,在修改之前自动将旧数据拷贝存入前端盘后,对前端盘进行修改 - 原始盘始终是只读的
创建
若修改 磁盘
root@kvmsvr ~]# virsh edit node0
...
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/node0.img'/> 改这个
<target dev='vda' bus='virtio'/>
</disk>
若修改 网络
[root@kvmsvr ~]# virsh edit node0
...
<interface type='bridge'>
<source bndge='vbr'/> 改这个
<model type='virtio'/>
</interface>
创建新的
[root@destop ~]# qemu-img create -b .node_base.qcow2 -f qcow2 tang.img 10G
[root@destop ~]# vim /etc/libvirt/qemu/nsd.xml
<name>tang</name>
<source file='/var/lib/libvirt/images/tang.img'/>
[root@destop ~]# virsh define /etc/libvirt/qemu/nsd.xml
[root@destop ~]# virsh console nsd
快建虚拟机脚本
[root@destop ~]# vim clone.sh
[root@destop ~]# vim clone.sh
[root@destop ~]# chmod 755 clone.sh
[root@destop ~]# cat clone.sh
#!/bin/bash
name=$1
cd /var/lib/libvirt/images/
if [ -e ${name}.img ];then
echo "file exits"
exit 1
else
qemu-img create -f qcow2 -b .nsd.qcow2 ${name}.img 20G
fi
cd /etc/libvirt/qemu/
if [ -e ${name}.img ];then
echo "file exits"
exit 1
else
sed "s,nsd,${name},g" /etc/libvirt/qemu/nsd.xml > /etc/libvirt/qemu/${name}.xml
fi
网卡及配置文件
- 网络配置文件说明 - /etc/sysconfig/network-scripts/ifcfg-eth0
# Generated by dracut initrd 注释
DEVICE="eth0" # 驱动名称,与ifconfig 看到的名称一致
ONBOOT="yes" # 开机启动
NM_CONTROLLED="no" # 不接受
NetworkManager 控制 TYPE="Ethernet" #类型
BOOTPROTO="static" #协议(dhcp|static|none)
IPADDR="192.168.1.10" #IP地址
NETMASK="255.255.255.0" #子网掩码
GATEWAY="192.168.1.254" #默认网关
virsh 扩容磁盘
只能扩不能缩
- virsh 虚拟机管理
- domblklist 查看虚拟机硬盘信息
- blockresize --path [绝对路径] --size 50G openstack -
扩容思路: - 首先是硬盘 - 其次是分区 - 再次是文件系统
真机
1 virsh domblklist ooxx
2 virsh blockresize --path /var/lib/libvirt/images/ooxx.img --size 50G ooxx
虚拟机
3 lsblk
4 growpart /dev/vda 1 (LANG=C)
5 xfs_growfs /dev/vda1
6 df -h
知道云之间的优缺点
云平台
基于互联网的相关服务的增加、使用和交付模式这种模式提供可用的、便捷的、按需的网络访问,
进入可配置的计算资源共享池这些资源能够被快速提供,只需投入很少的管理工作,或与服务供应商进行很少的交互通常涉及通过互联网来提供动态易扩展且经常是虚拟化的资源
IaaS
- IaaS(Infrastructure as a Service),即基础设施即服务
- 提供给消费者的服务是对所有计算基础设施的利用,包括处理 CPU、内存、存储、网络和其它基本的计算资源,用户能够部署和运行任意软件,包括操作系统和应用程序
- IaaS 通常分为三种用法:公有云、私有云和混合云
PaaS
- PaaS (Platform-as-a-Service),意思是平台即服务
- 以服务器平台或者开发环境作为服务进行提供就成为了 PaaS
- PaaS 运营商所需提供的服务,不仅仅是单纯的基础平台,还针对该平台的技术支持服务,甚至针对该平台而进行的应用系统开发、优化等服务
- 简单地说,PaaS 平台是指云环境中的应用基础设施服务,也可以说是中间件即服务
SaaS
- SaaS(Software-as-a-Service)软件即服务,是一种通过 Internet 提供软件的模式,厂商将应用软件统一部署在自己的服务器上,客户可以根据自己实际需求,通过互联网向厂商定购所需的应用软件服务
- 用户不用再购买软件,而是向提供商租用基于 Web 的软件,来管理企业经营活动,不用对软件进行维护,提供商会全权管理和维护软件,同时也提供软件的离线操作和本地数据存储
什么是云?
- 对于到底什么是云计算,至少可以找到 100 种解释
- 现阶段广为接受的是美国国家标准与技术研究院(NIST)定义:
- 云计算是一种按使用量付费的模式,这种模式提供可用的、便捷的、按需的网络访问,进入可配置的计算资源共享池(包括网络,服务器,存储,应用软件,服务),这些资源能够被快速提供,只需投入少量的管理工作,或与服务供应商进行很少的交互
云计算产品选购建议
- 要考虑服务商的知名度、靠谱度
- 以个人 / 企业实际需求为本
- 比如云主机、数据库、CDN、安全等,选择性价比最优的
openstack
openstack结构图
openstack主要组件
Horizon
-
用于管理 Openstack 各种服务的、基于 web 的管理接口
-
通过图形界面实现创建用户、管理网络、启动实例等操作
Keystone
-
为其他服务提供认证和授权的集中身份管理服务
-
也提供了集中的目录服务
-
支持多种身份认证模式,如密码认证、令牌认证、以及 AWS(亚马逊 Web 服务)登陆
-
为用户和其他服务提供了 SSO 认证服务
Neutron
-
一种软件定义网络服务
-
用于创建网络、子网、路由器、管理浮动 IP 地址
-
可以实现虚拟交换机、虚拟路由器
-
可用于在项目中创建 VPN
Cinder
-
为虚拟机管理存储卷的服务
-
为运行在 Nova 中的实例提供永久的块存储
-
可以通过快照进行数据备份
-
经常应用在实例存储环境中,如数据库文件
Glance
-
扮演虚拟机镜像注册的角色
-
允许用户为直接存储拷贝服务器镜像
-
这些镜像可以用于新建虚拟机的模板
Nova
-
在节点上用于管理虚拟机的服务
-
Nova 是一个分布式的服务,能够与 Keystone 交互实现认证,与 Glance 交互实现镜像管理
-
Nova 被设计成在标准硬件上能够进行水平扩展
-
启动实例时,如果有则需要下载镜像
虚拟机配置
- 准备虚拟机3台,
配置如下 - openstack 管理主机 - 2CPU,6G 内存,50G 硬盘
配置静态IP:192.168.1.10
-nova01,nova02 计算节点 * 2 - 2CPU,4.5G 内存,100G 硬盘
- 配置静态IP:192.168.1.11(12)
配置DNS - 系统环境准备 - openstack 安装时候需要使用外部 dns 来解析域名
vim /etc/resolv.conf
# Generated by NetworkManager
nameserver 114.114.114.114 设置真机DNS地址
注:去掉search开头的所有行
- 将 openstack.tedu 域名对应的 IP 解析到我们安装的 openstack 服务器
vim /etc/hosts
192.168.1.10 openstack
192.168.1.11 nova01
192.168.1.12 nova02
-注:DNS 服务器不能与 openstack 安装在同一台主机上
时间服务
- nova 服务器之间的时间必须保持一致
- 编辑配置文件 /etc/chrony.conf
- server 192.168.1.254 iburst
- 重启服务 - systemctl restart chronyd
- 测试服务 chronyc sources -v
//出现*号代表NTP时间可用
^* gateway 2 6 17 62 -753us[-7003us]+/- 24ms
配置yum仓库
- CentOS7-1804.iso 系统软件
- RHEL7-extras.iso 提供 Python 依赖软件包
- RHEL7OSP-10.iso 光盘拥有众多目录,每个目录都是一个软件仓库,我们配置其中2个软件仓库 - openstack 主要软件仓库 rhel-7-server-openstack-10-rpms
- packstack 软件仓库 rhel-7-server-openstack-10-devtools-rpms
安装额外软件包
安装openstack期间,有些软件包所依赖的软件包,并没有在安装过程中安装,这些软件包需提前安装
- qemu-kvm - libvirt-daemon - libvirt-daemon-driver-qemu - libvirt-client - python-setuptools
- 软件包安装
yum install -y qemu-kvm libvirt-client libvirt-daemon libvirt-daemon-driver-qemu python-setuptools
检查基础环境
- 是否卸载firewalld 和 NetworkManager
[root@destop ~]# rpm -qa |grep firewalld
[root@destop ~]# rpm -qa |grep NetworkManager
- 检查配置主机网络参数(静态IP)
root@destop ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens33
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=none
- 主机名必须能够相互 ping 通
[root@destop ~]# ping c1
- 检查配置主机yum源(4个,10670)
- 依赖软件包是否安装 - 检查NTP服务器是否可用
[root@destop ~]# systemctl start libvirtd
[root@destop ~]# systemctl status libvirtd
- 检查 /etc/resolv.conf 不能有 search 开头的行
[root@openstack ~]# bash 退出
[root@openstack ~]# . keystonerc_admin
[root@openstack ~(keystone_admin)]# exit
exit
用户与配额管理
项目管理
项目:一组隔离的资源和对象。由一组关联的用户进行管理
在旧版本里,也用租户(tenant)来表示
根据配置的需求,项目对应一个组织、一个公司或是一个使用客户等
项目中可以有多个用户,项目中的用户可以在该项目创建、管理虚拟资源
具有 admin 角色的用户可以创建项目
项目相关信息保存到 MariaDB 中
缺省情况下,packstack安装的openstack中有两个独立的项目
- admin:为admin账户创建的项目
- services:与安装的各个服务相关联
基本概念
- 用户在openstack中用于身份认证
- 管理员用户admin一般在packstack安装过程中创建
- 其他用户由管理员用户创建,并指定可以访问的项目
- 非管理员用户创建后,保存到MariaDB中
非管理员用户具有以下权限
启动实例
-创建卷和快照
创建镜像
分配浮动IP
创建网络和路由器
创建防火墙以及规则、规则策略
查看网络拓扑、项目使用概况等
网页操作
创建项目
创建用户
通过命令行管理项目
创建名为 myproject 项目
[root@openstack ~(keystone_admin)]# openstack project create myproject
列出所有项目
[root@openstack ~(keystone_admin)]# openstack project list
查看 myproject 详细信息
[root@openstack ~(keystone_admin)]# openstack project show myproject
禁用与激活项目
[root@openstack ~(keystone_admin)]# openstack project set --disable myproject [root@openstack ~(keystone_admin)]# openstack project set --enable myproject
查看项目配额及相关操作
查看项目配额
[root@openstack ~(keystone_admin)]# nova quota-show --tenant myproject
更新可用 vCPU 数目为 30
[root@openstack ~(keystone_admin)]# nova quota-update --cores 30 myproject
删除 myproject
[root@openstack ~(keystone_admin)]# openstack project delete myproject
创建用户
通过命令行管理用户
- 创建user2用户,指定密码为tedu
[root@openstack ~(keystone_admin)]# openstack user create --password tedu user2
- 设置user2的email地址
[root@openstack ~(keystone_admin)]# openstack user set --email user2@tedu user2
- 列出所有用户
[root@openstack ~(keystone_admin)]# openstack user list
- 查看user2信息
[root@openstack ~(keystone_admin)]# openstack user show user2
指定user2可以访问myproject,角色为_member_
[root@openstack ~(keystone_admin)]# openstack role add --user user2 --project myproject _member_
查看user2在myproject中的角色
[root@openstack ~(keystone_admin)]# openstack role list --project myproject --user user2
禁用用户
[root@openstack ~(keystone_admin)]# openstack user set --disable user2
激活用户
[root@openstack ~(keystone_admin)]# openstack user set --enable user2
修改user2的密码为redhat
[root@openstack ~(keystone_admin)]# openstack user set --password redhat user2
将user2从myproject中移除
[root@openstack ~(keystone_admin)]# openstack role remove --project myproject --user user2 _member_
删除user2用户
[root@openstack ~(keystone_admin)]# openstack user delete user2
通过命令行管理配额
- 列出项目的缺省配额
[root@openstack ~(keystone_admin)]# nova quota-defaults
- 列出myproject的配额
[root@openstack ~(keystone_admin)]# nova quota-show --tenant myproject
- 修改浮动IP地址配额
[root@openstack ~(keystone_admin)]# nova quota-update --floating-ips 20 myproject
配额管理
配额基础
管理员可以通过配额限制,防止资源的过度使用
配额基本项目,限制每个项目可以使用多少资源
这些操作上的功能限制,赋予了管理员对每个项目的精准控制
资源参数
安全组规则:指定每个项目可用的规则数
核心:指定每个项可用的VCPU核心数
固定IP地址:指定每个项目可用的固定IP数
浮动IP地址:指定每个项目可用的浮动IP数
注入文件大小:指定每个项目内容大小
注入文件路径:指定每个项目注入的文件路径长度
注入文件:指定每个项目允许注入的文件数目
实例:指定每个项目可创建的虚拟机实例数目
密钥对:指定每个项可创建的密钥数
元数据:指定每个项目可用的元数据数目
内存:指定每个项目可用的最大内存
安全组:指定每个项目可创建的安全组数目
云主机类型
通过命令行管理云主机类型
- 列出所有的云主机类型
[root@openstack ~(keystone_admin)]# openstack flavor list
- 建一个云主机类型
[root@openstack ~(keystone_admin)]# openstack flavor create --public demo.tiny --id auto --ram 512 --disk 10 --vcpus 1
- 删除云主机类型
[root@openstack ~(keystone_admin)]# openstack flavor delete demo.tiny
镜像管理
基本概念
- 在红帽Openstack平台中,镜像指的是虚拟磁盘文件,磁盘文件中应该已经安装了可启动的操作系统
- 镜像管理功能由Glance服务提供
- 它形成了创建虚拟机实例最底层的块结构
- 镜像可以由用户上传,也可以通过红帽官方站点下载
Glance磁盘格式
raw:非结构化磁盘镜像格式
vhd:VMware、Xen、Microsoft、VirtualBox等均支持的通用磁盘格式
vmdk:是Vmware的虚拟磁盘格式
vdi:VirtualBox虚拟机和QEMU支持磁盘格式
iso:光盘数据内容的归档格式
qcow2:QEMU支持的磁盘格式。空间自动扩展,并支持写时复制copy-on-write
镜像容器格式
bare :镜像中没有容器或元数据封装
ovf:一种开源的文件规范,描述了一个开源、安全、有效、可拓展的便携式虚拟打包以及软件分布格式
ova:OVA归档文件
aki:亚马逊内核镜像
ami:亚马逊主机镜像
通过命令行管理镜像
上传镜像
[root@openstack ~(keystone_admin)]# openstack image create --disk-format qcow2 --min-disk 10 --min-ram 512 --file /root/small.img small_rhel6
- **列出镜像**
[root@openstack ~(keystone_admin)]# openstack image list
- **查看镜像详情**
[root@openstack ~(keystone_admin)]# openstack image show small_rhel6
镜像其他操作
修改镜像属性
[root@openstack ~(keystone_admin)]# openstack image set --public small_rhel6
另存镜像为本地文件
[root@openstack ~(keystone_admin)]# openstack image save --file /tmp/small_rhel6.img small_rhel6
-删除镜像
[root@openstack ~(keystone_admin)]# openstack image delete small_rhel6
网络管理
Openstack网络工作原理
实例被分配到子网中,以实现网络连通性
每个项目可以有一到多个子网
在红帽的Openstack平台中,OpenStack网络服务是缺省的网络选项,Nova网络服务作为备用 管理员能够配置丰富的网络,将其他Openstack服务连接到这些网络的接口上
每个项目都能拥有多个私有网络,各个项目的私有网络互相不受干扰
构建内网网络
wan的ip必须是关联的网卡
添加路由
浮动ip地址
浮动IP地址的作用
浮动IP一般是花钱购买的
浮动IP地址用于从外界访问虚拟机实例
浮动IP地址只能从现有浮动IP地址池中分配
创建外部网络时,浮动IP地址池被定义
虚拟机实例起动后,可以为其关联一个浮动IP地址
虚拟机实例也可以解除IP地址绑定
在 OpenStack 中,浮动 IP(Floating IP)用于将实例(虚拟机)从私有网络暴露到外部网络,使实例能够与外部通信(如访问互联网或被外部访问)
创建新的
内网通信是采用裸奔
计算节点扩容
扩容 openstack 计算节点
参考 nova01 配置过程
配置静态 ip:192.168.1.12,及主机名
保证与 openstack,和 nova01 能相互 ping 通
配置 /etc/resolv.conf,删除 search 开头行
配置时间同步 /etc/chrony.conf
配置 yum 源,软仓库一共 4 个(10670)
安装依赖软件包 qemu-kvm libvirt-client libvirt-daemon libvirt-daemon-driver-qemu python-setuptools
修改应答文件
在 openstack 上修改配置文件
[root@openstack ~]# vim answer.ini
98: CONFIG_COMPUTE_HOST=192.168.1.11,192.168.1.12
102:CONFIG_NETWORK_HOSTS=192.168.1.10,192.168.1.11,192.168.1.12
packstack --answer-file=answer.ini
[root@openstack ~]# vim /etc/httpd/conf.d/15-horizon_vhost.conf
WSGIProcessGroup apache WSGIApplicationGroup %{GLOBAL}
创建云主机是随机在nova上的
每隔一秒执行一次命令
watch -n 1 'virsh list --name'
容器
容器技术作为应用程序封装与交付的核心技术,其核心依赖于几个关键的内核技术来实现高效、安全的应用隔离与资源管理:
Cgroups(Control Groups):用于对容器的资源(如 CPU、内存、磁盘 I/O 等)进行精细化限制与分配,确保容器间资源使用互不干扰,合理利用物理机资源。
Namespace:进程隔离,通过创建不同的命名空间,实现进程、网络、文件系统等方面的隔离,让每个容器都像在独立的系统环境中运行,互不影响。
SELinux:提供安全增强功能,对容器的访问权限等进行严格控制,提升容器运行的安全性。
Docker
Docker是完整的一套容器管理系统
Docker提供了一组命令,让用户更加方便直接地使用容器技术,而不需要过多关心底层内核技术
Docker优点
相比于传统的虚拟化技术,容器更加简洁高效
传统虚拟机需要给每个VM安装操作系统
容器使用的共享公共库和程序
docker镜像
在Docker中容器是基于镜像启动的
镜像是启动容器的核心
镜像采用分层设计
使用快照的COW技术,确保底层数据不丢失
[root@localhost images]# qemu-img create -b node.qcow2 -f qcow2 disk1.img
Formatting 'disk1.img', fmt=qcow2 size=2147483648 backing_file='node.qcow2' encryption=off cluster_size=65536 lazy_refcounts=off
[root@localhost images]# guestmount -a disk1.img -i /mnt/
[root@localhost images]# cd /mnt/
[root@localhost mnt]# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
[root@localhost mnt]# mkdir disk1
[root@localhost mnt]# cd -
/var/lib/libvirt/images
[root@localhost images]# umount /mnt/
[root@localhost images]# ll
total 650756
-rw-r--r-- 1 root root 2686976 Sep 4 10:28 disk1.img
-rw-r--r-- 1 qemu qemu 663486464 Sep 4 10:27 node.qcow2
[root@localhost images]# qemu-img create -b disk1.img -f qcow2 disk2.img
Formatting 'disk2.img', fmt=qcow2 size=2147483648 backing_file='disk1.img' encryption=off cluster_size=65536 lazy_refcounts=off
[root@localhost images]# guestmount -a disk2.img -i /mnt/
[root@localhost mnt]# ls
bin dev etc lib media opt root sbin sys usr
boot disk1 home lib64 mnt proc run srv tmp var
采用分层设计
使用阿里云加速拉取
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://33qvo5mf.mirror.aliyuncs"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
查看镜像
[root@destop aa]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest a95d91903603 55 minutes ago 1.08 GB
myos lastest 1268a218ae03 About an hour ago 796 MB
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@destop aa]#
查看镜像
[root@docker1 ~]# docker search busybox
[root@docker1 ~]# docker search centos
[root@docker1 ~]# docker search nginx
下载镜像
像拉取cnetos7,nginx.1.12
[root@destop ~]# docker pull centos:7
[root@destop ~]# docker pull nginx:1.12
上传镜像
docker push docker.io/busybox
导出镜像(将centos7导出为 tar 文件)
[root@destop ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest a95d91903603 About an hour ago 1.08 GB
myos lastest 1268a218ae03 About an hour ago 796 MB
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@destop ~]# docker save -o httpd.tar myos:httpd
导入镜像(通过 tar 包文件导入镜像)
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
[root@localhost ~]# ls busybox.tar
busybox.tar
root@localhost ~]# docker load -i centos7.tar
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@localhost ~]#
容器的6大命名空间
镜像常用命令
启动镜像
docker run [选项] 镜像名:标签 [容器内执行的命令]
每一次启动镜像,都会创造一个容器
[root@docker1 ~]# docker images
[root@destop ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
[root@destop ~]# docker run -it docker.io/centos:7 /bin/bash
docker run = 创建新容器 + 启动容器(每次执行都会产生新容器)
已存在的容器停止后,需用 docker start 启动(复用容器,不创建新的)
docker history // 查看镜像制作历史
docker history [选项] 镜像名:标签
-H 或 --human:以人类可读的格式显示大小(默认开启)
--no-trunc:显示完整的命令(默认会截断长命令)
-q 或 --quiet:只显示镜像层的 ID
[root@destop ~]# docker history docker.io/centos:7
IMAGE CREATED CREATED BY SIZE COMMENT
eeb6ee3f44bd 3 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
<missing> 3 years ago /bin/sh -c #(nop) LABEL org.label-schema.... 0 B
<missing> 3 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723... 204 MB
docker inspect // 查看镜像底层信息
[root@destop ~]# docker inspect docker.io/centos:7
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
docker rmi // 删除本地镜像
docker rmi [选项] 镜像名:标签 或 镜像ID
[root@destop ~]# docker rmi docker.io/centos:7
若镜像被运行中的容器引用,需先停止并删除容器(docker rm -f 容器ID),再删除镜像
若镜像有多个标签,docker rmi 只会删除指定标签,不会删除整个镜像。
docker tag // 修改镜像名称和标签
docker tag 原镜像名:原标签 新镜像名:新标签
[root@destop ~]# docker tag docker.io/nginx:1.12.2 docker.io/test:1
执行后,docker images 会显示两个条目(原标签和新标签),但它们指向同一个镜像 ID(共享镜像层)。
若要删除旧标签,可使用 docker rmi 原镜像名:原标签(不会删除镜像本身,仅删除标签)。
容器常用命令
docker ps // 查看容器列表
-a 显示所有容器(包括已停止的)
-l 只显示最近创建的一个容器(无论是否运行)
-n X 显示最近创建的 X 个容器(如 -n 3 显示最近 3 个)
-q 只显示容器的 ID(常用于批量操作,如 docker stop $(docker ps -q))
--format 自定义输出格式(如只显示容器名和状态:--format "table {{.Names}}\t{{.Status}}")
[root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
docker stop|start|restart // 关闭容器 启动容器 重启容器
[root@destop ~]# docker ps -aq
1c1372c09cde
127456ac9af4
1a2ec2e8ddb7
[root@destop ~]# docker start 1c1372c09cde
1c1372c09cde
[root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c1372c09cde test:latest "/bin/bash" About an hour ago Up 3 seconds peaceful_hamilton
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
[root@destop ~]#
docker attach|exec // 进入容器
docker attach:进入容器的主进程终端
特点
共享主进程终端:进入后与容器的主进程(如 bash、nginx)共享输入输出,主进程的输出会直接显示。
退出影响:如果在 attach 模式下执行 exit,会导致容器的主进程终止,容器随之停止。
适用场景:查看容器主进程的实时输出(如日志),或临时干预主进程。
root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c1372c09cde test:latest "/bin/bash" About an hour ago Up 3 seconds peaceful_hamilton
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
[root@destop ~]# docker attach 1c
[root@1c1372c09cde /]# exit
exit
[root@destop ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a2ec2e8ddb7 myos:lastest "/bin/bash" About an hour ago Up About an hour distracted_williams
[root@destop ~]#
docker exec [选项] 容器ID或名称 命令
常用选项
-i:保持标准输入打开
-t:分配伪终端(通常与 -i 组合为 -it 进入交互式终端)
-d:后台执行命令
docker exec:在容器内执行新命令特点
独立进程:新建的进程与容器主进程并行运行,互不干扰。
退出安全:执行 exit 只会退出当前新建的进程(如 bash),容器主进程和容器仍继续运行。
功能丰富:可执行任意命令(如查看文件、安装软件、启动服务等)。
[root@destop ~]# docker exec -it 1c /bin/bash
[root@1c1372c09cde /]#
docker top // 查看容器进程列表
[root@destop ~]# docker top 1c
UID PID PPID C STIME TTY TIME CMD
root 43793 43776 0 19:03 pts/5 00:00:00 /bin/bash
[root@destop ~]#
docker rm // 删除容器
docker rm [选项] 容器ID或容器名
常用选项
-f:强制删除(即使论容器是否运行中,都会先停止再删除)
-v:删除容器挂载的数据卷(若数据卷未被其他容器使用)
-l:删除容器关联的网络链接
注意事项
运行中的容器默认无法直接删除,需先停止(docker stop 容器ID)或使用 -f 强制删除。
若容器被标记为 restart: always,强制删除后可能被自动重启,需先修改重启策略再删除。
批量删除时建议先执行 docker ps -aq --filter "status=exited" 查看待删除容器,确认无误后再执行删除命令。
[root@destop ~]# docker rm 1c
在 Docker 操作场景里,Ctrl + P + Q 是一组很实用的快捷键组合,作用是退出容器的交互模式,但不停止容器运行
自定义镜像
docker commit
docker commit 命令用于将容器的当前状态(包括修改、安装的软件等)保存为一个新的镜像,实现容器到镜像的 “快照” 功能。
docker commit [选项] 容器ID或名称 新镜像名:标签
常用选项
-m "描述信息":为新镜像添加提交说明(类似代码提交的注释)
-a "作者信息":指定镜像的作者
[root@destop ~]# docker commit 1c myos:lastest
[root@destop ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest a95d91903603 About an hour ago 1.08 GB
myos lastest 1268a218ae03 2 hours ago 796 MB
docker.io/centos 7 eeb6ee3f44bd 3 years ago 204 MB
docker.io/nginx 1.12.2 4037a5562b03 7 years ago 108 MB
registry-hangzhou.aliyuncs/google_containers/busybox latest e7d168d7db45 10 years ago 2.43 MB
[root@destop ~]#
Dockerfile
docker build [选项] 构建上下文路径
Dockerfile 语法格式
FROM: 基础镜像
MAINTAINER: 镜像创建者信息
EXPOSE: 开放的端口
ENV: 设置变量
ADD: 复制文件到镜像
RUN: 制作镜像时执行的命令,可以有多个
WORKDIR: 定义容器默认工作目录
CMD: 容器启动时执行的命令,仅可以有一条 CMD 如果没写会继承上一个镜像的启动命令
[root@destop ~]# mkdir aa
[root@destop ~]# cd aa/
[root@destop aa]# touch Dockerfile
[root@destop aa]# vim Dockerfile
[root@destop aa]# docker build -t test:latest .
[root@destop aa]# cat Dockerfile
FROM docker.io/centos:7
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun/repo/Centos-7.repo
RUN yum clean all
RUN yum makecache
RUN yum -y install vim net-tools psmisc iproute
构建原理
-
Docker 守护进程读取 Dockerfile 和构建上下文。
-
按 Dockerfile 指令顺序执行,每执行一条指令(如
RUN、COPY)创建一个新的镜像层。 -
利用缓存加速构建:若指令未修改,且依赖的上一层未变化,则直接复用缓存层。
-
最终生成完整镜像,可通过
docker images查看。
做一个ssh服务的
[root@ec8826e4ccb9 /]# rpm -ql openssh-server 查看安装的文件
[root@destop bb]# cat Dockerfile
FROM myos:lastest
RUN yum -y install openssh-server initscripts
RUN sshd-keygen
RUN echo "redhat" | passwd --stdin root
ENV EnvironmentFile=/etc/sysconfig/sshd
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]
httpd的
[root@destop cc]# cat Dockerfile
FROM myos:lastest
RUN yum -y install httpd
WORKDIR /var/www/html
ENV EnvironmentFile=/etc/sysconfig/httpd
ENV PS1='[webserver@\h \W]\$ '
ADD index.html index.html
EXPOSE 80
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
[root@destop cc]# cat index.html
hello world
自定义仓库
安装私有仓库(服务端)
yum install docker-distribution
启动私有仓库,并设置开机自启动
systemctl start docker-distribution
systemctl enable docker-distribution
仓库配置文件及数据存储路径
/etc/docker-distribution/registry/config.yml
/var/lib/registry
自定义私有仓库
客户端配置:
修改配置文件 /etc/sysconfig/docker
允许非加密方式访问仓库
INSECURE_REGISTRY='--insecure-registry 仓库ip'
docker 仓库地址
ADD_REGISTRY='--add-registry 仓库ip'
重启 docker 服务
systemctl restart docker
[root@destop cc]# vim /etc/sysconfig/docker
# Do not add registries in this file anymore. Use /etc/containers/registries.conf
[root@destop cc]# vim /etc/containers/registries.conf
# To ensure compatibility with docker we've included docker.io in the default search list. However Red Hat
# does not curate, patch or maintain container images from the docker.io registry.
[registries.search]
registries = ['172.25.0.11:5000']
#registries = ['registry.access.redhat', 'registry.redhat.io', 'docker.io']
# Registries that do not use TLS when pulling images or uses self-signed
# certificates.
[registries.insecure]
registries = [172.25.0.11:5000]
存储持续化
[root@class ~]# vim /etc/exports
[root@class ~]# mkdir /var/webroot
[root@class ~]# chmod 777 /var/webroot/
[root@class ~]# systemctl restart nfs
[root@class ~]# cat /etc/exports
/var/webroot *(rw)
[root@class ~]#
k8s
应用部署方式演变
在部署应用程序的方式上,主要经历了三个时代:
传统部署:互联网早期,会直接将应用程序部署在物理机上
优点:简单,不需要其它技术的参与
缺点:不能为应用程序定义资源使用边界,很难合理地分配计算资源,而且程序之间容易产生影响
虚拟化部署:可以在一台物理机上运行多个虚拟机,每个虚拟机都是独立的一个环境
优点:程序环境不会相互产生影响,提供了一定程度的安全性
缺点:增加了操作系统,浪费了部分资源
容器化部署:与虚拟化类似,但是共享了操作系统
优点:
可以保证每个容器拥有自己的文件系统、CPU、内存、进程空间等
运行应用程序所需要的资源都被容器包装,并和底层基础架构解耦
容器化的应用程序可以跨云服务商、跨 Linux 操作系统发行版进行部署
kubernetes的本质是“一组服务器集群”,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。它的目的就是是实现资源管理的自动化,主要提供了如下的主要功能:
自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器
弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
服务发现:服务可以通过自动发现的形式找到它所依赖的服务
负载均衡:如果一个服务起动了多个容器,能够自动实现请求的负载均衡
版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
存储编排:可以根据容器自身的需求自动创建存储卷
kubernetes组件
一个kubernetes集群主要是由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。
master:集群的控制平面,负责集群的决策
- ApiServer:资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制
- Scheduler:负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上
- ControllerManager:负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等
- Etcd:负责存储集群中各种资源对象的信息
node:集群的数据平面,负责为容器提供运行环境
- Kubelet:负责维护容器的生命周期,即通过控制docker,来创建、更新、销毁容器
- KubeProxy:负责提供集群内部的服务发现和负载均衡
- Docker:负责节点上容器的各种操作
以部署一个nginx服务来说明kubernetes系统各个组件调用关系:
1. 首先要明确,一旦kubernetes环境启动之后,master和node都会将自身的信息存储到etcd数据库中
2. 一个nginx服务的安装请求会首先被发送到master节点的apiServer组件
3. apiServer组件会调用scheduler组件来决定到底应该把这个服务安装到哪个node节点上
在此时,它会从etcd中读取各个node节点的信息,然后按照一定的算法进行选择,并将结果告知apiServer
4. apiServer调用controller-manager去调度Node节点安装nginx服务
5. kubelet接收到指令后,会通知docker,然后由docker来启动一个nginx的pod
pod是kubernetes的最小操作单元,容器必须跑在pod中至此,
6. 一个nginx服务就运行了,如果需要访问nginx,就需要通过kube-proxy来对pod产生访问的代理
这样,外界用户就可以访问集群中的nginx服务了
安装方式
kubernetes有多种部署方式,目前主流的方式有kubeadm、minikube、二进制包
- minikube:一个用于快速搭建单节点kubernetes的工具
- kubeadm:一个用于快速搭建kubernetes集群的工具
- 二进制包:从官网下载每个组件的二进制包,依次去安装,此方式对于理解kubernetes组件更加有效
环境初始化
1、检查操作系统的版本
安装 kubernetes 集群要求 Centos 版本要在 7.5 或之上
2、主机名解析
为了方便后面集群节点间的直接调用,在这配置一下主机名解析,企业中推荐使用内部 DNS 服务器
主机名成解析 编辑三台服务器的 /etc/hosts 文件,添加下面内容:
3、时间同步
kubernetes 要求集群中的节点时间必须精确一致,这里直接使用 chronyd 服务从网络同步时间。
企业中建议配置内部的时间同步服务器
4、关闭系统规则(避免与 kubernetes、docker 产生的规则混淆)
[root@server ~]# systemctl stop firewalld
[root@server ~]# systemctl disable firewalld
[root@server ~]# systemctl stop iptables
[root@server ~]# systemctl disable iptables
5、禁用selinux
selinux是Linux系统下的一个安全服务,如果不关闭它,在安装集群中会产生各种各样的奇葩问题
# 编辑 /etc/selinux/config 文件,修改SELINUX的值为disabled
# 注意修改完毕之后需要重启linux服务
SELINUX=disabled
6、禁用swap分区
[root@server ~]# vim /etc/fstab
# /dev/mapper/centos-swap swap swap defaults 0 0
swap分区指的是虚拟内存分区,它的作用是在物理内存使用完之后,将磁盘空间虚拟成内存来使用
启用swap设备会对系统的性能产生非常负面的影响,因此kubernetes要求每个节点都要禁用swap设备
但是如果因为某些原因确实不能关闭swap分区,就需要在集群安装过程中通过明确的参数进行配置说明
7、修改linux的内核参数
# 修改linux的内核参数,添加网桥过滤和地址转发功能
# 编辑/etc/sysctl.d/kubernetes.conf文件,添加如下配置:
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
重新加载配置:
[root@server ~]# sysctl -p
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 1
net.ipv4.ip_forward = 1
加载网桥过滤模块:
[root@server ~]# modprobe br_netfilter
查看网桥过滤模块是否加载成功:
[root@server ~]# lsmod | grep br_netfilter
br_netfilter 22256 0
bridge 151336 1 br_netfilter
[root@server ~]#
8、配置 ipvs 功能
在 kubernetes 中 service 有两种代理模型,一种是基于 iptables 的,一种是基于 ipvs 的
两者比较的话,ipvs 的性能明显要高一些,但是如果要使用它,需要手动载入 ipvs 模块
安装 ipset 和 ipvsadm:
yum install ipset ipvsadmin -y
添加需要加载的模块写入脚本文件:
cat <<EOF > /etc/sysconfig/modules/ipvs.modules
#!/bin/bash
modprobe -- ip_vs
EOF
安装 docker
切换镜像源
[root@class ~]# wget https://mirrors.aliyun/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
查看当前镜像源中支持的 docker 版本
[root@class ~]# yum list docker-ce --showduplicates
安装特定版本的 docker-ce
必须指定 --setopt=obsoletes=0,否则 yum 会自动安装更高版本
[root@class ~]# yum -y install --setopt=obsoletes=0 docker-ce-18.06.3.ce-3.el7
添加一个配置文件
[root@c1 ~]# cat /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy",
"https://docker.mirrors.ustc.edu",
"https://docker.nju.edu"
]
}
Docker 在默认情况下使用的 Cgroup Driver 为 cgroupfs,而 kubernetes 推荐使用 systemd 来代替 cgroupfs
启动 docker
systemctl restart docker
安装 kubernetes 组件
由于 kubernetes 的镜像源在国外,速度比较慢,这里切换成国内的镜像源
编辑 /etc/yum.repos.d/kubernetes.repo,添加下面的配置
[root@server ~]# cat /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun/kubernetes/yum/doc/rpm-package-key.gpg
安装 kubeadm、kubelet 和 kubectl
yum install --setopt=obsoletes=0 kubeadm-1.17.4-0 kubelet-1.17.4-0 kubectl-1.17.4-0 -y
配置 kubelet 的 cgroup
编辑 /etc/sysconfig/kubelet,添加下面的配置
[root@server ~]# cat /etc/sysconfig/kubelet
GROUP_ARGS="--cgroup-driver=systemd"
KUBE_PROXY_MODE="ipvs"
设置 kubelet 开机自启
systemctl enable kubelet
集群初始化
开始对集群进行初始化,并将 node 节点加入到集群中
操作只需要在 master 节点上执行即可
# 创建集群
[root@master ~]# kubeadm init \
--kubernetes-version=v1.17.4 \ 必须于下载版本一致
--pod-network-cidr=10.244.0.0/16 \ 定义 Pod 网络网段
--service-cidr=10.96.0.0/12 \ 定义 Service 网络网段
--apiserver-advertise-address=172.25.0.11
# 创建必要文件
[root@master ~]# mkdir -p $HOME/.kube
[root@master ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
mkdir -p $HOME/.kube
在当前用户的家目录($HOME,root 用户默认是 /root)下,创建名为 .kube 的隐藏目录
kubectl 工具默认会从 $HOME/.kube/config 文件中读取 集群认证配置(包含 API Server 地址、证书、令牌等),.kube 目录就是用来存放这个核心配置文件的位置。
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
将 Kubernetes 集群初始化时自动生成的 管理员级配置文件(admin.conf),复制到上一步创建的 .kube 目录下,并命名为 config(kubectl 识别的默认配置文件名)
sudo chown $(id -u):$(id -g) $HOME/.kube/config
修改 $HOME/.kube/config 文件的 所有者和所属组,确保当前用户能正常读写该配置文件
准备集群镜像
在安装 kubernetes 集群之前,必须要提前准备好集群需要的镜像,所需镜像可以通过下面命令查看
[root@server ~]# kubeadm config images list
I0829 09:56:19.946312 52539 version.go:251] remote version is much newer: v1.34.0; falling back to: stable-1.17
W0829 09:56:21.771107 52539 validation.go:28] Cannot validate kube-proxy config - no validator is available
W0829 09:56:21.771169 52539 validation.go:28] Cannot validate kubelet config - no validator is available
k8s.gcr.io/kube-apiserver:v1.17.17
k8s.gcr.io/kube-controller-manager:v1.17.17
k8s.gcr.io/kube-scheduler:v1.17.17
k8s.gcr.io/kube-proxy:v1.17.17
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.4.3-0
k8s.gcr.io/coredns:1.6.5
下载镜像
此镜像在 kubernetes 的仓库中,由于网络原因,无法连接,下面提供了一种替代方案
# 1. 定义 1.17.4 版本所需镜像列表
images=(
"kube-apiserver:v1.17.4"
"kube-controller-manager:v1.17.4"
"kube-scheduler:v1.17.4"
"kube-proxy:v1.17.4"
"pause:3.1"
"etcd:3.4.3-0"
"coredns:1.6.5"
)
# 2. 从阿里云拉取并转换为官方标签(k8s.gcr.io)
for imageName in "${images[@]}"; do
# 拉取阿里云镜像
docker pull registry.aliyuncs/google_containers/"$imageName"
# 标签转换(让 kubeadm 能识别)
docker tag registry.aliyuncs/google_containers/"$imageName" k8s.gcr.io/"$imageName"
# 清理阿里云源镜像(节省空间)
docker rmi registry.aliyuncs/google_containers/"$imageName"
done
# 3. 再次验证镜像
docker images | grep k8s.gcr.io
安装网络插件
kubernetes支持多种网络插件,比如flannel、calico、canal等等,任选一种使用即可,本次选择flannel
下面操作依旧只在master节点执行即可,插件使用的是DaemonSet的控制器,它会在每个节点上都运行
获取fannel的配置文件
[root@server ~]# cat kube-flannel.yml
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp.flannel.unprivileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
privileged: false
volumes:
- configMap
- secret
- emptyDir
- hostPath
allowedHostPaths:
- pathPrefix: "/etc/cni/net.d"
- pathPrefix: "/etc/kube-flannel"
- pathPrefix: "/run/flannel"
readOnlyRootFilesystem: false
# 用户和组权限
runAsUser:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
fsGroup:
rule: RunAsAny
# 权限提升控制
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
# 网络相关能力(必须包含 NET_ADMIN)
allowedCapabilities: ['NET_ADMIN']
defaultAddCapabilities: []
requiredDropCapabilities: []
# 主机命名空间
hostPID: false
hostIPC: false
hostNetwork: true
hostPorts:
- min: 0
max: 65535
# SELinux 配置
seLinux:
rule: RunAsAny # 简化配置,适配国内环境
---
# 2. ClusterRole:定义 Flannel 所需的权限
apiVersion: rbac.authorization.k8s.io/v1 # 升级废弃的 v1beta1 → v1
kind: ClusterRole
metadata:
name: flannel
rules:
# 允许使用 PodSecurityPolicy
- apiGroups: ['extensions']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['psp.flannel.unprivileged']
# 访问节点和 Pod 信息
- apiGroups: [""]
resources: ["pods"]
verbs: ["get"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list", "watch"]
- apiGroups: [""]
resources: ["nodes/status"]
verbs: ["patch"]
---
# 3. ClusterRoleBinding:绑定角色到 Flannel 服务账户
apiVersion: rbac.authorization.k8s.io/v1 # 升级废弃的 v1beta1 → v1
kind: ClusterRoleBinding
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system # 服务账户在 kube-system 命名空间
---
# 4. ServiceAccount:Flannel 专用服务账户
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
# 5. ConfigMap:Flannel 核心配置(CNI + 网络子网)
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
app: flannel
data:
# CNI 配置:kubelet 用于创建 Pod 网络
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1", # 兼容 K8s 1.17+
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true, # 解决 Pod 自访问问题
"isDefaultGateway": true # 设为默认网关
}
},
{
"type": "portmap", # 支持 Pod 端口映射
"capabilities": {
"portMappings": true
}
}
]
}
# Flannel 网络配置:定义集群子网和后端
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
# 6. DaemonSet:仅部署 amd64 架构(适配你的节点)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds-amd64
namespace: kube-system
labels:
tier: node
app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
# 节点亲和:仅调度到 amd64 架构的 Linux 节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
hostNetwork: true # 启用主机网络
tolerations:
- operator: Exists
effect: NoSchedule # 容忍所有 NoSchedule 污点(调度到 Master/Worker)
serviceAccountName: flannel # 关联服务账户
# InitContainer:复制 CNI 配置到节点(解决之前的 CNI 配置问题)
initContainers:
- name: install-cni
# 阿里云公开镜像(兼容 K8s 1.17.4,国内可拉取)
image: registry-zhangjiakou.aliyuncs/test-lab/coreos-flannel:amd64
command: ["cp"]
args:
- "-f"
- "/etc/kube-flannel/cni-conf.json"
- "/etc/cni/net.d/10-flannel.conflist"
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d # 挂载节点的 CNI 目录
- name: flannel-cfg
mountPath: /etc/kube-flannel/ # 挂载 ConfigMap
# 主容器:Flannel 服务
containers:
- name: kube-flannel
image: registry-zhangjiakou.aliyuncs/test-lab/coreos-flannel:amd64
command: ["/opt/bin/flanneld"]
args:
- "--ip-masq"
- "--kube-subnet-mgr"
- "--iface=ens33"
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: true
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
- name: xtables-lock
mountPath: /run/xtables.lock # 避免 iptables 冲突
# 挂载的卷
volumes:
- name: run
hostPath:
path: /run/flannel # 节点上的运行目录
- name: cni
hostPath:
path: /etc/cni/net.d # 节点上的 CNI 配置目录
- name: flannel-cfg
configMap:
name: kube-flannel-cfg # 关联 Flannel 配置
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate # 不存在则创建
[root@server ~]# ^C
[root@server ~]#
# 使用配置文件启动fannel
[root@master ~]# kubectl apply -f kube-flannel.yml
稍等片刻,再次查看集群节点的状态
[root@master ~]# kubectl get nodes
服务部署
接下来在 kubernetes 集群中部署一个 nginx 程序,测试下集群是否在正常工作。
部署 nginx
kubectl create deployment nginx --image=nginx:1.14-alpine
暴露端口
kubectl expose deployment nginx --port=80 --type=NodePort
查看服务状态
kubectl get pods,svc
最后在电脑上访问部署的 nginx 服务
YAML 语言
YAML 的语法比较简单,主要有下面几个:
大小写敏感
使用缩进表示层级关系
缩进不允许使用 tab,只允许空格
缩进的空格数不重要,只要相同层级的元素左对齐即可
#表示注释
YAML 支持以下几种数据类型:
纯量:单个的、不可再分的值
对象:键值对的集合,又称为映射(mapping)/ 哈希(hash)/ 字典(dictionary)
数组:一组按次序排列的值,又称为序列(sequence)/ 列表(list)
纯量,就是指一个简单的值,字符串、布尔值、整数、浮点数、Null、时间、日期
1 布尔类型
c1: true(或者 True)
2 整型
c2: 234
3 浮点型
c3: 3.14
4 null 类型
c4: ~ # 使用~表示 null
5 日期类型
c5: 2018-02-17 # 日期必须使用 ISO 8601 格式,即 yyyy-MM-dd
6 时间类型
c6: 2018-02-17T15:31:23+08:00 # 时间使用 ISO 8601 格式,时间和日期之间使用 T 连接,最后使用 + 代表时区
7 字符串类型
c7: heima # 简单写法,直接写值 ,如果字符串中间有特殊字符,必须使用双引号或者单引号包裹
c8: |
line1
line2 # 字符串过多的情况可以拆成多行,每一行会被转化成一个空格
对象
形式一(推荐):
heima:
age: 15
address: Beijing
形式二(了解):
heima: {age: 15,address: Beijing}
数组
形式一(推荐):
address:
- 顺义
- 昌平
形式二(了解):
address: [顺义,昌平]
资源管理
在 kubernetes 中,所有的内容都抽象为资源,用户需要通过操作资源来管理 kubernetes。
kubernetes 的本质上就是一个集群系统,用户可以在集群中部署各种服务,所谓的部署服务,其实就是在 kubernetes 集群中运行一个个的容器,并将指定的程序跑在容器中。
kubernetes 的最小管理单元是 Pod 而不是容器,所以只能将容器放在 Pod 中,而 kubernetes 一般也不会直接管理 Pod,而是通过 Pod 控制器来管理 Pod 的。
Pod 可以提供服务之后,就要考虑如何访问 Pod 中服务,kubernetes 提供了 Service 资源实现这个功能。
当然,如果 Pod 中程序的数据需要持久化,kubernetes 还提供了各种存储系统。
资源管理方式
命令式对象管理:直接使用命令去操作 kubernetes 资源
kubectl run nginx-pod --image=nginx:1.17.1 --port=80
命令式对象配置:通过命令配合配置文件去操作 kubernetes 资源
kubectl create/patch -f nginx-pod.yaml
声明式对象配置:通过 apply 命令和配置文件去操作 kubernetes 资源
kubectl apply -f nginx-pod.yaml
| 类型 | 操作对象 | 适用环境 | 优点 | 缺点 |
|---|---|---|---|---|
| 命令式对象管理 | 对象 | 测试 | 简单 | 只能操作活动对象,无法审计、跟踪 |
| 命令式对象配置 | 文件 | 开发 | 可以审计、跟踪 | 项目大时,配置文件多,操作麻烦 |
| 声明式对象配置 | 目录 | 开发 | 支持目录操作 | 意外情况下难以调试 |
命令式对象管理
kubectl 命令
kubectl 是 kubernetes 集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。kubectl 命令的语法如下:
kubectl [command] [type] [name] [flags]
command:指定要对资源执行的操作,例如 create、get、delete
type:指定资源类型,比如 deployment、pod、service
name:指定资源的名称,名称大小写敏感
flags:指定额外的可选参数
# 查看所有pod
kubectl get pod
# 查看某个pod
kubectl get pod pod_name
# 查看某个pod,以yaml格式展示结果
kubectl get pod pod_name -o yaml
资源类型
kubernetes 中所有的内容都抽象为资源,可以通过下面的命令进行查看:
kubectl api-resources
经常使用的资源有下面这些:
| 资源分类 | 资源名称 | 缩写 | 资源作用 |
|---|---|---|---|
| 集群级别资源 | nodes | no | 集群组成部分 |
| namespaces | ns | 隔离 Pod | |
| pod 资源 | pods | po | 装载容器 |
| pod 资源控制器 | replicationcontrollers | rc | 控制 pod 资源 |
| replicasets | rs | 控制 pod 资源 | |
| deployments | deploy | 控制 pod 资源 | |
| daemonsets | ds | 控制 pod 资源 | |
| jobs | 控制 pod 资源 | ||
| cronjobs | cj | 控制 pod 资源 | |
| horizontalpodautoscalers | hpa | 控制 pod 资源 | |
| statefulsets | sts | 控制 pod 资源 | |
| 服务发现资源 | services | svc | 统一 pod 对外接口 |
| ingress | ing | 统一 pod 对外接口 | |
| 存储资源 | volumeattachments | 存储 | |
| persistentvolumes | pv | 存储 |
# 创建一个namespace
[root@master ~]# kubectl create namespace dev
namespace/dev created
# 获取namespace
[root@master ~]# kubectl get ns
NAME STATUS AGE
default Active 21h
dev Active 21s
kube-node-lease Active 21h
kube-public Active 21h
kube-system Active 21h
# 在此namespace下创建并运行一个nginx的Pod
[root@master ~]# kubectl run pod --image=nginx -n dev
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/pod created
# 查看新创建的pod
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pod-864f9875b9-pcw7x 1/1 Running 0 21s
# 删除指定的pod
[root@master ~]# kubectl delete pod pod-864f9875b9-pcw7x
pod "pod-864f9875b9-pcw7x" deleted
命令式对象配置
命令式对象配置就是使用命令配合配置文件一起来操作 kubernetes 资源。
vim nginxpod.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: nginxpod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx:1.17.1
[root@master ~]# kubectl create -f nginxpod.yaml
namespace/dev created
pod/nginxpod created
执行 get 命令,查看资源:
[root@master ~]# kubectl get -f nginxpod.yaml
执行 delete 命令,删除资源:
[root@master ~]# kubectl delete -f nginxpod.yaml
命令式对象配置的方式操作资源,可以简单的认为:命令 + yaml 配置文件(里面是命令需要的各种参数)
声明式对象配置
声明式对象配置跟命令式对象配置很相似,但是它只有一个命令apply。
# 首先执行一次kubectl apply -f yaml文件,发现创建了资源
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev created
pod/nginxpod created
# 再次执行一次kubectl apply -f yaml文件,发现说资源没有变动
[root@master ~]# kubectl apply -f nginxpod.yaml
namespace/dev unchanged
pod/nginxpod unchanged
其实声明式对象配置就是使用apply描述一个资源最终的状态(在 yaml 中定义状态)
使用apply操作资源:
- 如果资源不存在,就创建,相当于
kubectl create - 如果资源已存在,就更新,相当于
kubectl patch
kubectl 的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在 node 节点运行此命令,需要将 master 上的.kube文件复制到 node 节点上,即在 master 节点上执行下面操作:
scp -r $HOME/.kube node1:$HOME/
| 操作类型 | 推荐方式 | 命令示例 |
|---|---|---|
| 创建 / 更新资源 | 使用声明式对象配置 | kubectl apply -f XXX.yaml |
| 删除资源 | 使用命令式对象配置 | kubectl delete -f XXX.yaml |
| 查询资源 | 使用命令式对象管理 | kubectl get(describe) 资源名称 |
Namespace
Namespace 是 kubernetes 系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。
默认情况下,kubernetes 集群中的所有的 Pod 都是可以相互访问的。但是在实际中,可能不想让两个 Pod 之间进行互相的访问,那此时就可以将两个 Pod 划分到不同的 namespace 下。kubernetes 通过将集群内部的资源分配到不同的 Namespace 中,可以形成逻辑上的 “组”,以方便不同的组的资源进行隔离使用和管理。
可以通过 kubernetes 的授权机制,将不同的 namespace 交给不同租户进行管理,这样就实现了多租户的资源隔离。此时还能结合 kubernetes 的资源配额机制,限定不同租户能占用的资源,例如 CPU 使用量、内存使用量等等,来实现租户可用资源的管理。
kubernetes 在集群启动之后,会默认创建几个 namespace
[root@server ~]# kubectl get ns
NAME STATUS AGE
default Active 3d22h # 所有未指定 Namespace 的对象都会被分配在 default 命名空间
kube-node-lease Active 3d22h # 集群节点之间的心跳维护,v1.13 开始引入
kube-public Active 3d22h # 此命名空间下的资源可以被所有人访问(包括未认证用户)
kube-system Active 3d22h # 所有由 Kubernetes 系统创建的资源都处于这个命名空间
[root@server ~]#
查看
1 查看所有的 ns 命令: kubectl get ns
[root@server ~]# kubectl get ns
NAME STATUS AGE
default Active 3d22h
dev Active 24h
ingress-nginx Active 23h
kube-node-lease Active 3d22h
kube-public Active 3d22h
kube-system Active 3d22h
kubernetes-dashboard Active 17h
2 查看指定的 ns 命令: kubectl get ns ns 名称
[root@server ~]# kubectl get ns dev
NAME STATUS AGE
dev Active 24h
3 指定输出格式 命令: kubectl get ns ns 名称 -o 格式参数
kubernetes 支持的格式有很多,比较常见的是 wide、json、yaml
[root@server ~]# kubectl get ns dev -o wide
NAME STATUS AGE
dev Active 24h
[root@server ~]# kubectl get ns dev -o yaml
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: "2025-08-28T02:04:14Z"
name: dev
resourceVersion: "201613"
selfLink: /api/v1/namespaces/dev
uid: 87cb0441-2015-46b7-8a4e-27106a76d281
spec:
finalizers:
- kubernetes
status:
phase: Active
Pod
Pod 是 kubernetes 集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于 Pod 中。
Pod 可以认为是容器的封装,一个 Pod 中可以存在一个或者多个容器。
kubernetes 在集群启动之后,集群中的各个组件也都是以 Pod 方式运行的。
kubernetes 没有提供单独运行 Pod 的命令,都是通过 Pod 控制器来实现的
命令格式: kubectl run (pod 控制器名称) [参数]
--image 指定 Pod 的镜像
--port 指定端口
--namespace 指定 namespace
[root@server ~]# kubectl run nginx1 --image=nginx:1.17.2 --port=80 --namespace dev
vim pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
查看 pod 信息
查看 Pod 基本信息
root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 11s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
nginx1-576f68b7f-65qdw 1/1 Running 0 6m40s 10.244.4.72 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
[root@server ~]#
查看 Pod 的详细信息
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 11s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
nginx1-576f68b7f-65qdw 1/1 Running 0 6m40s 10.244.4.72 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
删除指定Pod
[root@server ~]# kubectl delete pod nginx1-576f68b7f-65qdw -n dev
pod "nginx1-576f68b7f-65qdw" deleted
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 75s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
nginx1-576f68b7f-wchgk 1/1 Running 0 24s 10.244.4.74 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
[root@server ~]#
这是因为当前Pod是由Pod控制器创建的,控制器会监控Pod状况,一旦发现Pod死亡,会立即重建
# 此时要想删除Pod,必须删除Pod控制器
先来查询一下当前namespace下的Pod控制器
[root@server ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 16h
nginx1 1/1 1 1 8m51s
[root@server ~]# kubectl delete deploy nginx1 -n dev
deployment.apps "nginx1" deleted
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5586d7f49-7bvxm 1/1 Running 0 3m28s 10.244.4.73 c1.example <none> <none>
nginx-5586d7f49-bwmqf 1/1 Running 6 16h 10.244.4.69 c1.example <none> <none>
pod-configmap 1/1 Running 6 18h 10.244.4.67 c1.example <none> <none>
[root@server ~]#
Label
Label 是 kubernetes 系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
Label 的特点:
一个 Label 会以 key/value 键值对的形式附加到各种对象上,如 Node、Pod、Service 等等
一个资源对象可以定义任意数量的 Label ,同一个 Label 也可以被添加到任意数量的资源对象上去
Label 通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除
可以通过 Label 实现资源的多维度分组,以便灵活、方便地进行资源分配、调度、配置、部署等管理工作。
一些常用的 Label 示例如下:
版本标签: "version":"release", "version":"stable"……
环境标签: "environment":"dev", "environment":"test", "environment":"pro"
架构标签: "tier":"frontend", "tier":"backend"
标签定义完毕之后,还要考虑到标签的选择,这就要使用到 Label Selector,即:
Label 用于给某个资源对象定义标识
Label Selector 用于查询和筛选拥有某些标签的资源对象
当前有两种 Label Selector:
基于等式的 Label Selector
name = slave: 选择所有包含 Label 中 key="name" 且 value="slave" 的对象
env != production: 选择所有包括 Label 中的 key="env" 且 value 不等于 "production" 的对象
基于集合的 Label Selector
name in (master,slave): 选择所有包含 Label 中的 key="name" 且 value="master" 或 "slave" 的对象
name not in (frontend): 选择所有包含 Label 中的 key="name" 且 value 不等于 "frontend" 的对象
标签的选择条件可以使用多个,此时将多个 Label Selector 进行组合,使用逗号 “,” 进行分隔即可。例如:
name=slave, env!=production
name not in (frontend), env!=production
先创建 pod-nginx
配置操作
创建一个pod-nginx.yaml,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
为pod资源打标签
[root@server ~]# kubectl label pod nginx version=1.0 -n dev
pod/nginx labeled
为pod资源更新标签
[root@server ~]# kubectl label pod nginx version=2.0 -n dev --overwrite
pod/nginx labeled
查看标签
[root@server ~]# kubectl get pod nginx -n dev --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 101s version=2.0
[root@server ~]#
Service
已经能够利用 Deployment 来创建一组 Pod 来提供具有高可用性的服务。
虽然每个 Pod 都会分配一个单独的 Pod IP,然而却存在如下两问题:
Pod IP 会随着 Pod 的重建产生变化
Pod IP 仅仅是集群内可见的虚拟 IP,外部无法访问
这样对于访问这个服务带来了难度。因此,kubernetes 设计了 Service 来解决这个问题。
Service 可以看作是一组同类 Pod 对外的访问接口。借助 Service,应用可以方便地实现服务发现和负载均衡。
创建集群内部可访问的 Service
暴露Service
[root@server ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 20h
[root@server ~]# kubectl expose deploy nginx --name=svc-nginx --type=ClusterIP --port=80 --target-port=80 -n dev
service/svc-nginx exposed
[root@server ~]#
查看service
[root@server ~]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nginx ClusterIP 10.107.79.96 <none> 80/TCP 2m19s
[root@server ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx ClusterIP 10.107.79.96 <none> 80/TCP 2m30s k8s-app=nginx
这里产生了一个CLUSTER-IP,这就是service的IP,在Service的生命周期中,这个地址是不会变动的
可以通过这个IP访问当前service对应的POD
创建集群外部也可访问的 Service
创建的Service的type类型为ClusterIP,这个ip地址只用集群内部访问
如果需要创建外部也可以访问的Service,需要修改type为NodePort
[root@server ~]# kubectl expose deploy nginx --name=svc-nginx2 --type=NodePort --port=80 --target-port=80 -n dev
service/svc-nginx2 exposed
[root@server ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx ClusterIP 10.107.79.96 <none> 80/TCP 9m4s k8s-app=nginx
svc-nginx2 NodePort 10.102.157.69 <none> 80:32258/TCP 41s k8s-app=nginx
接下来就可以通过集群外的主机访问 节点IP:32258访问服务了
# 例如在的电脑主机上通过浏览器访问下面的地址
删除service
[root@server ~]# kubectl delete svc svc-nginx -n dev
service "svc-nginx" deleted
[root@server ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx2 NodePort 10.102.157.69 <none> 80:32258/TCP 2m8s k8s-app=nginx
[root@server ~]#
Pod 结构
每个 Pod 中都可以包含一个或者多个容器,这些容器可以分为两类:
用户程序所在的容器,数量可多可少
Pause 容器,这是每个 Pod 都会有的一个根容器,它的作用有两个:
可以以它为依据,评估整个 Pod 的健康状态
可以在根容器上设置 IP 地址,其它容器都此 IP(Pod IP),以实现 Pod 内部的网路通信
Pod 定义
apiVersion: v1 # 必选,版本号,例如v1
kind: Pod # 必选,资源类型,例如 Pod
metadata: # 必选,元数据
name: string # 必选,Pod名称
namespace: string # Pod所属的命名空间,默认为“default”
labels: # 自定义标签列表
- name: string
spec: # 必选,Pod中容器的详细定义
containers: # 必选,Pod中容器列表
- name: string # 必选,容器名称
image: string # 必选,容器的镜像名称
imagePullPolicy: [ Always|Never|IfNotPresent ] # 获取镜像的策略
command: [string] # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] # 容器的启动命令参数列表
workingDir: string # 容器的工作目录
volumeMounts: # 挂载到容器内部的存储卷配置
- name: string # 引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string # 存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean # 是否为只读模式
ports: # 需要暴露的端口号列表
- name: string # 端口的名称
containerPort: int # 容器需要监听的端口号
hostPort: int # 容器所在主机需要监听的端口号,默认与Container相同
protocol: string # 端口协议,支持TCP和UDP,默认TCP
可通过一个命令来查看每种资源的可配置项
# kubectl explain 资源类型 查看某种资源可以配置的一级属性
# kubectl explain 资源类型.属性 查看属性的子属性
可通过一个命令来查看每种资源的可配置项
# kubectl explain 资源类型 查看某种资源可以配置的一级属性
# kubectl explain 资源类型.属性 查看属性的子属性
[root@server ~]# kubectl explain pod
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object>
Specification of the desired behavior of the pod. More info:
https://git.k8s.io/community/contrib
kubernetes 资源一级属性说明
在 kubernetes 中基本所有资源的一级属性都是一样的,主要包含 5 部分:
apiVersion <string>:版本,由 kubernetes 内部定义,版本号必须可以用 kubectl api-versions 查询到
kind <string>:类型,由 kubernetes 内部定义,版本号必须可以用 kubectl api-resources 查询到
metadata <Object>:元数据,主要是资源标识和说明,常用的有 name、namespace、labels 等
spec <Object>:描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述
status <Object>:状态信息,里面的内容不需要定义,由 kubernetes 自动生成
在上面的属性中,spec 是接下来研究的重点,继续看下它的常见子属性:
containers <[]Object>:容器列表,用于定义容器的详细信息
nodeName <String>:根据 nodeName 的值将 pod 调度到指定的 Node 节点上
nodeSelector <map[]>:根据 NodeSelector 中定义的信息选择将该 Pod 调度到包含这些 label 的 Node 上
hostNetwork <boolean>:是否使用主机网络模式,默认为 false,如果设置为 true,表示使用宿主机网络
volumes <[]Object>:存储卷,用于定义 Pod 上面挂在的存储信息
restartPolicy <string>:重启策略,表示 Pod 在遇到故障的时候的处理策略
Pod 配置
研究 pod.spec.containers 属性,这也是 pod 配置中最为关键的一项配置。
[root@master ~]# kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 数组,代表可以有多个容器
FIELDS:
name <string> # 容器名称
image <string> # 容器需要的镜像地址
imagePullPolicy <string> # 镜像拉取策略
command <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
args <[]string> # 容器的启动命令需要的参数列表
env <[]Object> # 容器环境变量配置
ports <[]Object> # 容器需要暴露的端口号列表
resources <Object> # 资源限制和资源请求的设置
基本配置
镜像拉取
创建 pod-command.yaml 文件
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
command,用于在 pod 中的容器初始化完毕之后运行一个命令。
/bin/sh","-c":使用 sh 执行命令
touch /tmp/hello.txt:创建一个 /tmp/hello.txt 文件
while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;:持续向 /tmp/hello.txt 文件写入当前时间,每 3 秒写入一次
命令操作
创建 Pod
[root@server ~]# kubectl create -f pod-command.yaml
pod/pod-imagepullpolicy created
查看 Pod 状态
[root@server ~]# kubectl get pods pod-command -n dev
NAME READY STATUS RESTARTS AGE
pod-command 2/2 Running 0 9s
进入 pod 中的 busybox 容器,查看文件内容
补充一个命令:kubectl exec pod名称 -n 命名空间 -it -c 容器名称 /bin/sh 在容器内部执行命令
使用这个命令就可以进入某个容器的内部,然后进行相关操作了 比如,可以查看 txt 文件的内容
[root@master pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
/ # tail -f /tmp/hello.txt
通过上面发现 command 已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个 args 选项,用于传递参数呢?
这其实跟 docker 有点关系,kubernetes 中的 command、args 两项其实是实现覆盖 Dockerfile 中 ENTRYPOINT 的功能。
如果 command 和 args 均没有写,那么用 Dockerfile 的配置。
如果 command 写了,但 args 没有写,那么 Dockerfile 默认的配置会被忽略,执行输入的 command
如果 command 没写,但 args 写了,那么 Dockerfile 中配置的 ENTRYPOINT 的命令会被执行,使用当前 args 的参数
如果 command 和 args 都写了,那么 Dockerfile 的配置被忽略,执行 command 并追加 args 参数
环境变量
apiVersion: v1
kind: Pod
metadata:
name: pod-env
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "while true;do /bin/echo $(date +%T);sleep 60; done;"]
env: # 设置环境变量列表
- name: "username"
value: "admin"
- name: "password"
value: "123456"
env,环境变量,用于在 pod 中的容器设置环境变量。
命令操作
创建 Pod
[root@server ~]# kubectl create -f pod-env.yaml
pod/pod-env created
进入容器,输出环境变量
[root@server ~]# kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ # echo $username
admin
/ #
端口设置
容器的端口设置,也就是 containers 的 ports 选项。
看下 ports 支持的子选项:
[root@master ~]# kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:
name <string> # 端口名称,如果指定,必须保证name在pod中是唯一的
containerPort <integer> # 容器要监听的端口(0x<65536)
hostPort <integer> # 容器要在主机上公开的端口,如果设置,主机上只能运行容器的一个副本(一般省略)
hostIP <string> # 要将外部端口绑定到的主机IP(一般省略)
protocol <string> # 端口协议。必须是UDP、TCP或SCTP。默认为“TCP”。
创建pod-ports.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports: # 设置容器暴露的端口列表
- name: nginx-port
containerPort: 80
protocol: TCP
[root@server ~]# kubectl create -f pod-ports.yaml
pod/pod-ports created
资源配额
容器中的程序要运行,肯定是要占用一定资源的,比如 cpu 和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes 提供了对内存和 cpu 的资源进行配额的机制,这种机制主要通过 resources 选项实现,它有两个子选项:
limits:用于限制运行时容器的最大占用资源,当容器占用资源超过 limits 时会被终止,并进行重启
requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动
可以通过上面两个选项设置资源的上下限。
创建pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-resources
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
resources: # 资源限制
limits: # 限制资源(上限)
cpu: "1" # CPU 限制,单位是core(核)
memory: "1Gi" # 内存限制
requests: # 请求资源(下限)
cpu: "1" # CPU 请求,单位是core(核)
memory: "1Gi" # 内存请求
# 查看发现pod运行正常
[root@master ~]# kubectl get pod pod-resources -n dev
NAME READY STATUS RESTARTS AGE
pod-resources 1/1 Running 0 39s
# 接下来,停止Pod
[root@master ~]# kubectl delete -f pod-resources.yaml
pod "pod-resources" deleted
# 编辑pod,修改resources.requests.memory的值为10Gi
[root@master ~]# vim pod-resources.yaml
# 再次启动pod
[root@master ~]# kubectl create -f pod-resources.yaml
pod/pod-resources created
# 查看Pod状态,发现Pod启动失败
[root@master ~]# kubectl get pod pod-resources -n dev -o wide
NAME READY STATUS RESTARTS AGE
pod-resources 0/2 Pending 0 20s
# 查看pod详情会发现,如下提示
[root@master ~]# kubectl describe pod pod-resources -n dev
......
Warning FailedScheduling <unknown> default-scheduler 0/2 nodes are available: 2 Insufficient memory. (内存不足)
Pod 生命周期
我们一般将 pod 对象从创建至终的这段时间范围称为 pod 的生命周期,它主要包含下面的过程:
pod 创建过程
运行初始化容器(init container)过程
运行主容器(main container)过程
容器启动后钩子(post start)、容器终止前钩子(pre stop)
容器的存活性探测(liveness probe)、就绪性探测(readiness probe)
pod 终止过程...
在整个生命周期中,Pod 会出现 5 种状态(相位),分别如下:
挂起(Pending):apiserver 已经创建了 pod 资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
运行中(Running):pod 已经被调度至某节点,并且所有容器都已经被 kubelet 创建完成
成功(Succeeded):pod 中的所有容器都已经成功终止并且不会被重启
失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非 0 值的退出状态
未知(Unknown):apiserver 无法正常获取到 pod 对象的状态信息,通常由网络通信失败所导致
pod 的创建过程
用户通过 kubectl 或其他 api 客户端提交需要创建的 pod 信息给 apiServer
apiServer 开始生成 pod 对象的信息,并将信息存入 etcd,然后返回确认信息至客户端
apiServer 开始反映 etcd 中的 pod 对象的变化,其它组件使用 watch 机制来跟踪检查 apiServer 上的变动
scheduler 发现有新的 pod 对象要创建,开始为 Pod 分配主机并将结果信息更新至 apiServer
node 节点上的 kubelet 发现有 pod 调度过来,尝试调用 docker 启动容器,并将结果回送至 apiServer
apiServer 将接收到的 pod 状态信息存入 etcd 中
pod 的终止过程
用户向 apiServer 发送删除 pod 对象的命令
apiServer 中的 pod 对象信息会随着时间的推移而更新,在宽限期内(默认 30s),pod 被视为 dead
将 pod 标记为 terminating 状态
kubelet 在监控到 pod 对象转为 terminating 状态的同时启动 pod 关闭过程
端点控制器监控到 pod 对象的关闭行为时将其从所有匹配到此端点的 service 资源的端点列表中移除
如果当前 pod 对象定义了 preStop 钩子处理器,则在其标记为 terminating 后即会以同步的方式启动执行
pod 对象中的容器进程收到停止信号
宽限期结束后,若 pod 中还存在仍在运行的进程,那么 pod 对象会收到立即终止的信号
kubelet 请求 apiServer 将此 pod 资源的宽限期设置为 0 从而完成删除操作,此时 pod 对于用户已不可见
初始化容器
初始化容器是在 pod 的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:
初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么 kubernetes 需要重启它直到成功完成
初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行
初始化容器有很多的应用场景,下面列出的是最常见的几个:
提供主容器镜像中不具备的工具程序或自定义代码
初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足
接下来做一个案例,模拟下面这个需求:
假设要以主容器来运行 nginx,但是要求在运行 nginx 之前先要能够连接上 mysql 和 redis 所在服务器
为了简化测试,事先规定好 mysql(172.25.0.10)和 redis(172.25.0.33)服务器的地址
需求与准备
假设要以主容器来运行 nginx ,但要求在运行 nginx 之前先要能够连接上 mysql 和 redis 所在服务器。为简化测试,事先规定好 mysql(192.168.109.201)和 redis(192.168.109.202)服务器的地址
[root@server ~]# cat pod-initcontainer.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-ports
containerPort: 80
initContainers:
- name: test-mysql
image: busybox:1.30
command: ['sh','-c','until ping 172.25.0.10 -c 1 ; do echo waiting for mysql ...; sleep 6; done;']
- name: test-redis
image: busybox:1.30
command: ['sh','-c','until ping 172.25.0.33 -c 1 ; do echo waiting for mysql ...; sleep 6; done;']
创建
[root@server ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created
查找
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 6 65m 10.244.2.67 destop.example <none> <none>
nginx1 1/1 Running 6 5h22m 10.244.2.66 destop.example <none> <none>
pod-command 2/2 Running 10 31m 10.244.2.69 destop.example <none> <none>
pod-configmap 1/1 Running 6 24h 10.244.4.67 c1.example <none> <none>
pod-env 2/2 Running 8 28m 10.244.2.70 destop.example <none> <none>
pod-initcontainer 0/1 Init:1/2 0 4s 10.244.2.72 destop.example <none> <none>
pod-ports 1/1 Running 0 15m 10.244.4.75 c1.example <none> <none>
钩子函数
钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
kubernetes 在主容器的启动之后和停止之前提供了两个钩子函数:
post start:容器创建之后执行,如果失败了会重启容器
pre stop :容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
钩子处理黯支持使用下面三种方式定义动作:
1. Exec 命令:在容器内执行一次命令
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
2. TCPSocket:在当前容器尝试访问指定的 socket
lifecycle:
postStart:
tcpSocket:
port: 8080
3. HTTPGet:在当前容器中向某 url 发起 http 请求
lifecycle:
postStart:
httpGet:
path: / # URI地址
port: 80 # 端口号
host: 192.168.109.100 # 主机地址
scheme: HTTP # 支持的协议,http或者https
以 exec 方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
lifecycle:
postStart:
exec: # 在容器启动的时候执行一个命令,修改掉nginx的默认首页内容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服务
command: ["/usr/sbin/nginx", "-s", "quit"]
容器探测
容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么 kubernetes 就会把该问题实例 “摘除”,不承担业务流量。kubernetes 提供了两种探针来实现容器探测,分别是:
liveness probes(存活性探针):用于检测应用实例当前是否处于正常运行状态,如果不是,k8s 会重启容器
readiness probes(就绪性探针):用于检测应用实例当前是否可以接收请求,如果不能,k8s 不会转发流量
livenessProbe 决定是否重启容器,readinessProbe 决定是否将请求转发给容器。
上面两种探针目前均支持三种探测方式:
1. Exec 命令
在容器内执行一次命令,如果命令执行的退出码为 0,则认为程序正常,否则不正常
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
2、TCPSocket
将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常
livenessProbe:
tcpSocket:
port: 8080
3、HTTPGet:调用容器内 Web 应用的 URL,如果返回的状态码在 200 和 399 之间,则认为程序正常,否则不正常
livenessProbe:
httpGet:
path: / # URI地址
port: 80 # 端口号
host: 127.0.0.1 # 主机地址
scheme: HTTP # 支持的协议,http或者https
[root@master ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
exec <Object>
tcpSocket <Object>
httpGet <Object>
initialDelaySeconds <integer> # 容器启动后等待多少秒执行第一次探测
timeoutSeconds <integer> # 探测超时时间。默认1秒,最小1秒
periodSeconds <integer> # 执行探测的频率。默认是10秒,最小1秒
failureThreshold <integer> # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
successThreshold <integer> # 连续探测成功多少次才被认定为成功。默认是1
重启策略
一旦容器探测出现了问题,kubernetes 就会对容器所在的 Pod 进行重启,其实这是由 pod 的重启策略决定的,pod 的重启策略有 3 种,分别如下:
Always:容器失效时,自动重启该容器,这也是默认值。
OnFailure:容器终止运行且退出码不为 0 时重启
Never:不论状态为何,都不重启该容器
重启策略适用于 pod 对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由 kubelet 延迟一段时间后进行,且反复的重启操作的延迟时长以此为 10s、20s、40s、80s、160s 和 300s,300s 是最大延迟时长。
创建 pod-restartpolicy.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-restartpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /hello 因为没有,会执行重启策略
restartPolicy: Never # 设置重启策略为Never
Pod 调度
在默认情况下,一个 Pod 在哪个 Node 节点上运行,是由 Scheduler 组件采用相应的算法计算出来的,这个过程是不受人工控制的。但实际使用中,常需控制某些 Pod 到特定节点,为此要了解 kubernetes 对 Pod 的调度规则,kubernetes 提供四大类调度方式:
自动调度:运行在哪个节点上完全由 Scheduler 经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration
定向调度
定向调度,指利用在 pod 上声明 nodeName 或者 nodeSelector,以此将 Pod 调度到期望的 node 节点上。注意,这里的调度是强制的,即即便目标 Node 不存在,也会尝试调度,只是 pod 运行会失败而已。
NodeName
NodeName 用于强制约束将 Pod 调度到指定的 Name 的 Node 节点上。这种方式,实际是直接跳过 Scheduler 的调度逻辑,直接写入 PodList 列表。
NodeName
NodeName 用于强制约束将 Pod 调度到指定的 Name 的 Node 节点上。这种方式,其实是直接跳过 Scheduler 的调度逻辑,直接将 Pod 调度到指定名称的节点。
创建一个 pod-nodename.yaml 文件
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 指定调度到node1节点上
创建 Pod
[root@master ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created
查看 Pod 调度到 NODE 属性,确认调度到 node1 节点上
[root@master ~]# kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-nodename 1/1 Running 0 56s 10.244.1.87 node1
删除 pod,修改 nodeName 的值为 node3(假设没有 node3 节点)
[root@master ~]# kubectl delete -f pod-nodename.yaml
[root@server ~]# cat pod-nodename.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node32
NodeSelector
NodeSelector 用于将 pod 调度到添加了指定标签的 node 节点上。它是通过 kubernetes 的 label-selector 机制实现的,也就是说,在 pod 创建之前,会由 scheduler 使用 MatchNodeSelector 调度策略进行 label 匹配,找出目标 node,然后将 pod 调度到目标节点,该匹配规则是强制约束。
为 node 节点添加标签
[root@master ~]# kubectl label nodes c1.example nodeenv=pro
node/node1 labeled
[root@master ~]# kubectl label nodes destop.example nodeenv=test
node/node2 labeled
[root@server ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
c1.example Ready <none> 3d1h v1.17.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=c1.example,kubernetes.io/os=linux,nodeenv=pro
destop.example Ready <none> 4d2h v1.17.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=destop.example,kubernetes.io/os=linux,nodeenv=test
server.example Ready master 4d4h v1.17.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=server.example,kubernetes.io/os=linux,node-role.kubernetes.io/master=
创建一个 pod-nodeselector.yaml 文件,并使用它创建 Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上
创建一个 pod-nodeselector.yaml 文件,并使用它创建 Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上
亲和性调度
两种定向调度方式,使用方便但存在局限:若没有满足条件的 Node,Pod 无法运行,即便集群有可用 Node 也不行,限制了使用场景。
基于此,kubernetes 提供亲和性调度(Affinity) 。它在 NodeSelector 基础上扩展,通过配置可优先选满足条件 Node 调度;无满足条件 Node 时,也可调到不满足条件节点,让调度更灵活。
Affinity 主要分类
nodeAffinity(node 亲和性):以 node 为目标,解决 pod 可调度到哪些 node 的问题
podAffinity(pod 亲和性):以 pod 为目标,解决 pod 可和哪些已存在 pod 部署在同一拓扑域(如同一节点、同一可用区等 )的问题
podAntiAffinity(pod 反亲和性):以 pod 为目标,解决 pod 不能和哪些已存在 pod 部署在同一拓扑域的问题
亲和性(反亲和性)使用场景说明
亲和性:若两个应用频繁交互,利用亲和性让它们尽可能靠近,可减少网络通信带来的性能损耗,比如微服务架构里关系紧密的服务。
反亲和性:应用多副本部署时,用反亲和性让各实例分散在不同 node,避免单点故障,提升服务高可用性,像 Redis 集群多节点部署。
NodeAffinity 的可配置项
pod.spec.affinity.nodeAffinity 包含以下关键配置:
1. 硬限制(严格约束,必须满足)
requiredDuringSchedulingIgnoredDuringExecution:Node 节点必须满足指定所有规则才可调度,相当于硬限制
nodeSelectorTerms:节点选择列表
matchFields:按节点字段(如主机名、IP 等)列出节点选择器要求
matchExpressions:按节点标签列出节点选择器要求(推荐)
key:标签键
values:标签值列表
operator:关系符,支持 Exists(存在标签)、DoesNotExist(不存在标 签)、In(值在列表内)、NotIn(值不在列表内)、Gt(值大于,需配合数值型标签)、Lt(值小于,需配合数值型标签 )
2. 软限制(倾向满足,不强制)
preferredDuringSchedulingIgnoredDuringExecution:优先调度到满足指定规则的 Node,相当于软限制(倾向)
preference:一个节点选择项,与权重关联
matchFields:按节点字段列出节点选择器要求
matchExpressions:按节点标签列出节点选择器要求(推荐)
key、values、operator:同硬限制逻辑
weight:倾向权重,范围 1~100,权重越高越优先
关系符使用说明(以 matchExpressions 为例 )
matchExpressions:
- key: nodeenv # 匹配存在标签key为nodeenv的节点
operator: Exists
- key: nodeenv # 匹配标签key为nodeenv、且value是"xxx"或"yyy"的节点
operator: In
values: ["xxx", "yyy"]
- key: nodeenv # 匹配标签key为nodeenv、且value大于"xxx"的节点(需标签值为数值型)
operator: Gt
values: "xxx"
演示一下 requiredDuringSchedulingIgnoredDuringExecution,
创建 pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
nodeAffinity: #设置node亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
nodeSelectorTerms:
- matchExpressions: # 匹配env的值在["xxx", "yyy"]中的标签
- key: nodeenv
operator: In
values: ["xxx", "yyy"]
[root@server ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
[root@server ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 6 24h
pod-hook-exec 1/1 Running 0 16m
pod-nodeaffinity-required 1/1 Running 0 11s
pod-ports 1/1 Running 0 59m
[root@server ~]#
演示一下 requiredDuringSchedulingIgnoredDuringExecution,
创建 pod-nodeaffinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-preferred
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
nodeAffinity: #设置node亲和性
preferredDuringSchedulingIgnoredDuringExecution: # 软限制
- weight: 1
preference:
matchExpressions: # 匹配env的值在["xxx", "yyy"]中的标签(当前环境没有)
- key: nodeenv
operator: In
values: ["xxx", "yyy"]
[root@server ~]# kubectl create -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created
[root@server ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 6 24h
pod-hook-exec 1/1 Running 1 19m
pod-nodeaffinity-preferred 1/1 Running 0 4s
pod-nodeaffinity-required 1/1 Running 0 2m32s
pod-ports 1/1 Running 0 61m
[root@server ~]#
Nodeaffinity 规则设置的注意事项:
如果同时定义了 nodeSelector 和 nodeAffinity,那么必须两个条件都得到满足,Pod 才能运行在指定的 Node 上
如果 nodeAffinity 指定了多个 nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
如果一个 nodeSelectorTerms 中有多个 matchExpressions ,则一个节点必须满足所有的才能匹配成功
如果一个 Pod 所在的 Node 在 Pod 运行期间标签发生了改变,不再符合该 Pod 的节点亲和性需求,则系统将忽略此变化
PodAffinity
PodAffinity 主要实现以运行的 Pod 为参照,让新创建的 Pod 跟参照 Pod 处于同一区域的功能。
PodAffinity 的可配置项(pod.spec.affinity.podAffinity)
硬限制(requiredDuringSchedulingIgnoredDuringExecution):
namespaces:指定参照 Pod 所在的命名空间
topologyKey:指定调度作用域(如按节点、可用区等划分,例 kubernetes.io/hostname 按节点区分 )
labelSelector:标签选择器,用于筛选参照 Pod
matchExpressions:按标签规则匹配(推荐)
key:标签键
values:标签值列表
operator:关系符,支持 In、NotIn、Exists、DoesNotExist
matchLabels:直接指定多个标签键值对,等效于多个 matchExpressions 映射
创建 pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAffinity: #设置pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配env的值在["xxx", "yyy"]中的标签
- key: nodeenv
operator: In
values: ["pro", "yyy"]
topologyKey: kubernetes.io/hostname
软限制(preferredDuringSchedulingIgnoredDuringExecution):
podAffinityTerm:配置项,结构同硬限制里的规则(namespaces、topologyKey、labelSelector 等 )
weight:倾向权重,范围 1~100,权重越高越优先调度
演示下 requiredDuringSchedulingIgnoredDuringExecution,
首先创建一个参照 Pod,pod-podaffinity-target.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
nodeenv: pro #设置标签
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: c1.example # 将目标pod名确定到c1.example上
[root@server ~]# kubectl create -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created
[root@server ~]# kubectl get pod pod-podaffinity-target -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-target 1/1 Running 0 12s
topologyKey 作用说明
用于指定调度作用域,示例:
若设为 kubernetes.io/hostname:调度时以 Node 节点为区分范围,新 Pod 尽量和参照 Pod 跑在同一节点
若设为 beta.kubernetes.io/os:按 Node 节点的操作系统类型区分,新 Pod 尽量和参照 Pod 跑在同操作系统类型节点
(简单说,就是通过这些配置,控制新 Pod 与已有参照 Pod 在 “同一区域” 的调度逻辑,硬限制必须满足,软限制优先满足 )
PodAntiAffinity
PodAntiAffinity 主要实现以运行的 Pod 为参照,让新创建的 Pod 跟参照 pod 不在一个区域中的功能。
它的配置方式和选项跟 PodAffinity 是一样的
创建 pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAntiAffinity: #设置pod反亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配podenv的值在["pro"]中的标签
- key: nodeenv
operator: In
values: ["pro"]
topologyKey: kubernetes.io/hostname
上面配置表达的意思是:新 Pod 必须要与拥有标签 nodeenv=pro 的 pod 不在同一 Node 上,运行测试一下。
root@server ~]# kubectl create -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created
[root@server ~]# kubectl get pod pod-podantiaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podantiaffinity-required 0/1 ContainerCreating 0 21s <none> destop.example <none> <none>
污点和容忍
污点(Taints)
前面的调度方式都是站在 Pod 的角度,通过在 Pod 上添加属性,确定 Pod 是否调度到指定 Node。也可站在 Node 角度,通过给 Node 添加污点属性,决定是否允许 Pod 调度过来。
Node 设污点后,与 Pod 形成 “相斥关系”,可拒绝 Pod 调度,甚至驱逐已存在的 Pod。
污点格式与作用
污点格式:key=value:effect ,key 和 value 是污点标签,effect 描述作用,支持三种选项:
PreferNoSchedule:kubernets 尽量避免把 Pod 调度到有该污点的 Node,除非无其他节点可调度
NoSchedule:kubernets 不会把 Pod 调度到有该污点的 Node,但不影响 Node 上已存在的 Pod
NoExecute:kubernets 不会把 Pod 调度到有该污点的 Node,还会将 Node 上已存在的 Pod 驱离
使用 kubectl 设置和去除污点的命令
# 设置污点
kubectl taint nodes node1 key=value:effect
# 去除污点(注意格式:key:effect- ,减号表示移除 )
kubectl taint nodes node1 key:effect-
# 去除所有污点(若要清除特定 key 的所有 effect,可这样写,实际需按真实 key 调整 )
kubectl taint nodes node1 key-
操作步骤
-
准备节点 c1(为让演示效果更明显,暂时停止 destop 节点 )
-
为 c
1节点设置污点tag=heima:PreferNoSchedule,然后创建pod1 -
修改 c
1节点污点为tag=heima:NoSchedule,然后创建pod2 -
修改 c
1节点污点为tag=heima:NoExecute,然后创建pod3
为 c1 设置污点(PreferNoSchedule )
NoSchedule 阻止 “新 Pod 调度”,不影响已运行的
使用 kubeadm 搭建的集群,默认会给 master 节点添加一个污点标记,所以 pod 不会调度到 master 节点上。
容忍(Toleration)
上面介绍了污点的作用,我们可在 node 上添加污点拒绝 pod 调度。但如果想让一个 pod 调度到有污点的 node 上,就要用到容忍。
污点就是拒绝,容忍就是忽略,Node 通过污点拒绝 pod 调度上去,Pod 通过容忍忽略拒绝
创建 pod-toleration.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-toleration
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
tolerations: # 添加容忍
- key: "tag" # 要容忍的污点的key
operator: "Equal" # 操作符
value: "heima" # 容忍的污点的value
effect: "NoExecute" # 添加容忍的规则,这里必须和标记的污点规则相同
[root@server ~]# kubectl taint nodes c1.example tag=heima:NoExecute
[root@server ~]# kubectl create -f pod-toleration.yaml
pod/pod-toleration1 created
[root@server ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hook-exec 0/1 Terminating 3 17h 10.244.2.73 destop.example <none> <none>
pod-podantiaffinity-required 1/1 Terminating 0 16h 10.244.2.74 destop.example <none> <none>
pod-toleration1 1/1 Running 0 7s 10.244.4.84 c1.example <none> <none>
查看 tolerations 字段说明:
kubectl explain pod.spec.tolerations
| 字段 | 说明 |
|---|---|
key | 对应要容忍的污点的键,空值匹配所有键 |
value | 对应要容忍的污点的值 |
operator | key-value 的运算符,支持 Equal(精确匹配值)和 Exists(只需存在键,忽略值,默认) |
effect | 对应污点的 effect,空值匹配所有影响 |
tolerationSeconds | 容忍时间,仅当 effect 为 NoExecute 时生效,表 Pod 在 Node 上的停留时间(超时后会被驱离 ) |
通过这些字段,精确控制 Pod 对 Node 污点的 “容忍规则”,让 Pod 能调度到带指定污点的节点
Pod 控制器
在 kubernetes 中,按照 pod 的创建方式可将其分为两类:
自主式 pod:kubernetes 直接创建出来的 pod,这种 pod 删除后就没有了,也不会重建
控制器创建的 pod:通过控制器创建的 pod,这种 pod 删除了之后还会自动重建
什么是 Pod 控制器
Pod 控制器是管理 pod 的中间层,使用了 pod 控制器之后,我们只需要告诉 pod 控制器,想要多少个什么样的 pod 就可以了,它就会创建出满足条件的 pod 并确保每一个 pod 处于用户期望的状态,如果 pod 在运行中出现故障,控制器会基于指定策略重启或者重建 pod。
在 kubernetes 中,有很多类型的 pod 控制器,每种都有自己的适合的场景,常见的有下面这些:
ReplicationController:比较原始的 pod 控制器,已经被废弃,由 ReplicaSet 替代
ReplicaSet:保证指定数量的 pod 运行,并支持 pod 数量变更,镜像版本变更
Deployment:通过控制 ReplicaSet 来控制 pod,并支持滚动升级、版本回退
Horizontal Pod Autoscaler:可以根据集群负载自动调整 Pod 的数量,实现削峰填谷
DaemonSet:在集群中的指定 Node 上都运行一个副本,一般用于守护进程类的任务
ReplicaSet
ReplicaSet 的主要作用是保证一定数量的 pod 能够正常运行,它会持续监听这些 pod 的运行状态,一旦 pod 发生故障,就会重启或重建。同时它还支持对 pod 数量的扩 / 缩容和版本镜像的升级。
ReplicaSet 的资源清单文件
apiVersion: apps/v1 # 版本号
kind: ReplicaSet # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: rs
spec: # 详情描述
replicas: 3 # 副本数量
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
配置项说明
这里需要新了解的 spec 下几个选项:
replicas:指定副本数量,即当前 ReplicaSet 要创建的 Pod 数量,默认值为 1
selector:选择器,作用是建立 Pod 控制器(ReplicaSet)和 Pod 的关联关系,基于 Label Selector 机制:在 Pod 模板定义 label,控制器定义选择器,以此表明控制器能管理哪些 Pod
template:模板,控制器创建 Pod 时使用的模板,
创建 pc-replicaset.yaml 文件
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: pc-replicaset
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
扩缩容
# 编辑 rs 的副本数量,修改 spec:replicas 为 6 即可
[root@master ~]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited
# 查看 pod(确认副本数量变化 )
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6mvvt 1/1 Running 0 114m
pc-replicaset-cftnp 1/1 Running 0 10s
pc-replicaset-fj1mb 1/1 Running 0 10s
pc-replicaset-fmbbf 1/1 Running 0 114m
pc-replicaset-s2hwj 1/1 Running 0 10s
pc-replicaset-snrk2 1/1 Running 0 114m
# 当然也可以直接使用命令实现
# 使用scale命令实现扩缩容,后面--replicas=n直接指定目标数量即可
[root@master ~]# kubectl scale rs pc-replicaset --replicas=2 -n dev
replicaset.apps/pc-replicaset scaled
# 命令运行完毕,立即查看,发现已经有4个开始准备退出了
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6mvvt 0/1 Terminating 0 118m
pc-replicaset-cftnp 0/1 Terminating 0 4m17s
pc-replicaset-fjlm6 0/1 Terminating 0 4m17s
pc-replicaset-fmb8f 1/1 Running 0 118m
pc-replicaset-s2hwj 0/1 Terminating 0 4m17s
pc-replicaset-snrk2 1/1 Running 0 118m
删除 ReplicaSet
常规删除(删除 RS 及管理的 Pod )
# 使用 kubectl delete 命令会删除此 RS 以及它管理的 Pod
# 在 kubernetes 删除 RS 前,会将 RS 的 replicas 调整为 0,等待所有的 Pod 被删除后,在执行 RS 对象的删除
[root@master ~]# kubectl delete rs pc-replicaset -n dev
replicaset.apps "pc-replicaset" deleted
# 验证 Pod 是否被删除
[root@master ~]# kubectl get pod -n dev -o wide
No resources found in dev namespace.
仅删除 RS 对象(保留 Pod,不推荐 )
# 如果希望仅删除 RS 对象(保留 Pod),可以使用 kubectl delete 命令时添加 --cascade=false 选项(不推荐)。
[root@master ~]# kubectl delete rs pc-replicaset -n dev --cascade=false
replicaset.apps "pc-replicaset" deleted
# 验证 Pod 是否保留
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-clslz 1/1 Running 0 75s
pc-replicaset-ds1b3 1/1 Running 0 75s
通过 YAML 文件删除
# 也可以使用 yaml 直接删除(推荐)
[root@master ~]# kubectl delete -f pc-replicaset.yaml
replicaset.apps "pc-replicaset" deleted
Deployment
为了更好的解决服务编排的问题,kubernetes 在 V1.2 版本开始,引入了 Deployment 控制器。值得一提的是,这种控制器并不直接管理 pod,而是通过管理 ReplicaSet 来间接管理 Pod,即:Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。所以 Deployment 比 ReplicaSet 功能更加强大。
Deployment 主要功能有下面几个:
支持 ReplicaSet 的所有功能
支持发布的停止、继续
支持版本滚动升级和版本回退
Deployment 资源清单
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: deploy
spec: # 详情描述
replicas: 3 # 副本数量
revisionHistoryLimit: 3 # 保留历史版本,默认是10
paused: false # 暂停部署,默认是false
progressDeadlineSeconds: 600 # 部署超时时间(s),默认是600
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
创建 pc-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
扩缩容
变更副本数量为 5 个
[root@master ~]# kubectl scale deploy pc-deployment --replicas=5 -n dev
deployment.apps/pc-deployment scaled
[root@master ~]# kubectl get deploy pc-deployment -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
pc-deployment 5/5 5 5 2m
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6696798b78-d2c8n 1/1 Running 0 4m19s
pc-deployment-6696798b78-jxmdq 1/1 Running 0 94s
pc-deployment-6696798b78-mktqv 1/1 Running 0 93s
pc-deployment-6696798b78-smpvp 1/1 Running 0 4m19s
pc-deployment-6696798b78-wvjd8 1/1 Running 0 4m19s
编辑 deployment 的副本数量,修改 spec:replicas: 4 即可
[root@master ~]# kubectl edit deploy pc-deployment -n dev
deployment.apps/pc-deployment edited
镜像更新
Deployment 支持两种镜像更新的策略:重建更新 和 滚动更新(默认),可以通过 strategy 选项进行配置。
strategy 配置说明
strategy:指定新的 Pod 替换旧的 Pod 的策略,支持两个属性:
type:指定策略类型,支持两种策略
Recreate:在创建出新的 Pod 之前会先杀掉所有已存在的 Pod
RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个 版本 Pod
rollingUpdate:当 type 为 RollingUpdate 时生效,用于为 RollingUpdate 设置参数,支持两个属性:
maxUnavailable:用来指在升级过程中不可用 Pod 的最大数量,默认为 25%。
maxSurge:用来指定在升级过程中可以超过期望的 Pod 的最大数量,默认为 25%。
重建更新
编辑 pc-deployment.yaml,在 spec 节点下添加更新策略
spec:
strategy: # 策略
type: Recreate # 重建更新策略
创建 deploy 进行验证
[root@master ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev
deployment.apps/pc-deployment image updated
[root@master ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
pc-deployment-5d89bdfbf9-c182j 1/1 Running 0 78s
pc-deployment-5d89bdfbf9-kkh9r 1/1 Running 0 78s
# (此处会看到旧 Pod 被删除、新 Pod 重建的过程 )
滚动更新
编辑 pc-deployment.yaml。在 spec 节点下添加更新策略
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
创建 deploy 进行验证
[root@master ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev
deployment.apps/pc-deployment image updated
[root@master ~]# kubectl get pods -n dev -w
NAME READY STATUS RESTARTS AGE
# (此处会显示 Pod 滚动更新的过程,旧 Pod 逐步替换为新 Pod )
镜像更新中 RS 的变化
[root@server ~]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
pc-deployment-5d89bdfbf9 0 0 0 6m30s
pc-deployment-675d469f8b 3 3 3 3m10s
pc-replicaset 3 3 1 66m
版本回退
deployment 支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能,下面具体来看。
kubectl rollout:版本升级相关功能,支持下面的选项:
status:显示当前升级状态
history:显示升级历史记录
pause:暂停版本升级过程
resume:继续已经暂停的版本升级过程
restart:重启版本升级过程
版本回退相关
undo:回滚到上一级版本(可以使用--to-revision回滚到指定版本)
# 查看当前升级版本的状态
[root@master ~]# kubectl rollout status deploy pc-deployment -n dev
deployment "pc-deployment" successfully rolled out
# 查看升级历史记录
[root@master ~]# kubectl rollout history deploy pc-deployment -n dev
deployment.apps/pc-deployment
REVISION CHANGE-CAUSE
1 kubectl create --filename=pc-deployment.yaml --record=true
2 kubectl create --filename=pc-deployment.yaml --record=true
3 kubectl create --filename=pc-deployment.yaml --record=true
# 可以发现有三次版本记录,说明完成过两次升级
# 版本回滚
# 这里直接使用--to-revision=1回滚到了1版本,如果省略这个选项,就是回退到上个版本,就是2版本
[root@master ~]# kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev
deployment.apps/pc-deployment rolled back
# 查看发现,通过nginx镜像版本可以发现到了第一版
[root@master ~]# kubectl get deploy -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES
pc-deployment 4/4 4 4 74m nginx nginx:1.17.1
# 查看rs,发现第一个rs中有4个pod运行,后面两个版本的rs中pod为运行
# 其实deployment之所以可实现版本的回滚,就是通过记录历史rs来实现的,
金丝雀发布
Deployment 支持更新过程中的控制,如 “暂停 (pause)” 或 “继续 (resume)” 更新操作。
比如有一批新的 Pod 资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的 Pod 应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的 Pod 资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。
# 更新deployment的版本,并配置等待deployment
[root@master ~]# kubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment -n dev
deployment.apps/pc-deployment image updated
deployment.apps/pc-deployment paused
# 观察更新状态
[root@master ~]# kubectl rollout status deploy pc-deployment -n dev
Waiting for deployment "pc-deployment" rollout to finish: 2 out of 4 new replicas have been updated...
# 监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是因为使用了pause暂停命令
[root@master ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES
pc-deployment-5d89bdfbf9 3 3 3 19m nginx nginx:1.17.1
pc-deployment-675d469f8b 0 0 0 14m nginx nginx:1.17.2
pc-deployment-6c9f56cfb 2 2 2 3m16s nginx nginx:1.17.4
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-5d89bdfbf9-rj8sq 1/1 Running 0 7m33s
...
pc-deployment-6c9f56cfb-j2gtj 1/1 Running 0 3m31s
# 确保更新的pod没问题了,继续更新
[root@master ~]# kubectl rollout resume deploy pc-deployment -n dev
deployment.apps/pc-deployment resumed
# 查看最后的更新情况
[root@master ~]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES
pc-deployment-5d89bdfbf9 0 0 0 21m nginx nginx:1.17.1
pc-deployment-675d469f8b 0 0 0 16m nginx nginx:1.17.2
pc-deployment-6c9f56cfb 4 4 4 5m11s nginx nginx:1.17.4
[root@master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-deployment-6c9f56cfb-7bfwh 1/1 Running 0 37s
pc-deployment-6c9f56cfb-996rt 1/1 Running 0 5m27s
pc-deployment-6c9f56cfb-j2gtj 1/1 Running 0 5m27s
pc-deployment-6c9f56cfb-rf84v 1/1 Running 0 37s
删除 Deployment
# 删除该 deployment,其下的 rs 和 pod 也将被删除
[root@master ~]# kubectl delete -f pc-deployment.yaml
deployment.apps "pc-deployment" deleted
Horizontal Pod Autoscaler(HPA)
可以通过手工执行 kubectl scale 命令实现 Pod 扩容,但这不符合 Kubernetes “自动化、智能化” 的定位目标。Kubernetes 期望通过监测 Pod 使用情况,自动调整 Pod 数量,于是产生了 HPA(Horizontal Pod Autoscaler) 这种控制器。
HPA 可获取每个 Pod 利用率,与 HPA 中定义的指标对比,计算需伸缩的具体值,最终调整 Pod 数量。其实 HPA 和之前的 Deployment 一样,也属于 Kubernetes 资源对象,它通过追踪分析目标 Pod 的负载变化情况,确定是否需要针对性调整目标 Pod 的副本数。
1 安装 metrics-server
metrics-server 可以用来收集集群中的资源使用情况
# 安装 git
[root@master ~]# yum install git -y
# 获取 metrics-server,注意使用的版本
[root@master ~]# git clone -b v0.3.6 https://github/kubernetes-incubator/metrics-server
# 修改 deployment,注意修改的是镜像和初始化参数
[root@master ~]# cd metrics-server/deploy/1.8+
[root@master 1.8+]# vim metrics-server-deployment.yaml
在
spec:
这里添加 hostNetwork: true
serviceAccountName: metrics-server
- name: tmp-dir
emptyDir: {}
containers:
- name: metrics-server
image: registry-hangzhou.aliyuncs/google_containers/metrics-server-amd64:v0.3.6
imagePullPolicy: Alwaysi
args:
--kubelet-insecure-tls
--kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
volumeMounts:
- name: tmp-dir
mountPath: /tmp
安装 metrics-server
[root@master 1.8+]# kubectl apply -f ./
[root@master 1.8+]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
metrics-server-6b976979db-2xwbj 1/1 Running 0 90s
# 查看节点资源
[root@master 1.8+]# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 98m 4% 1067Mi 62%
node1 27m 1% 727Mi 42%
node2 34m 1% 800Mi 46%
# 查看 kube-system 命名空间下 pod 资源
[root@master 1.8+]# kubectl top pod -n kube-system
NAME CPU(cores) MEMORY(bytes)
coredns-6955765f44-7ptsb 3m 9Mi
coredns-6955765f44-vcwr5 3m 8Mi
etcd-master 14m 145Mi
...
准备 Deployment 和 Service
创建 Deployment和Service
[root@master 1.8+]# kubectl run nginx --image=nginx:1.17.1 --requests=cpu=100m -n dev
[root@master 1.8+]# kubectl expose deployment nginx --type=NodePort --port=80 -n dev
[root@master 1.8+]# kubectl get deployment,pod,svc -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 47s
NAME READY STATUS RESTARTS AGE
pod/nginx-7df9756ccc-bh8dr 1/1 Running 0 47s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx NodePort 10.109.57.248 <none> 80:31136/TCP 35s
部署 HPA
创建 pc-hpa.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: pc-hpa
namespace: dev
spec:
minReplicas: 1 # 最小 pod 数量
maxReplicas: 10 # 最大 pod 数量
targetCPUUtilizationPercentage: 3 # CPU 使用率指标
scaleTargetRef: # 指定要控制的 nginx 信息
apiVersion: apps/v1
kind: Deployment
name: nginx
创建 HPA
[root@master 1.8+]# kubectl create -f pc-hpa.yaml
horizontalpodautoscaler.autoscaling/pc-hpa created
查看 HPA
[root@master 1.8+]# kubectl get hpa -n dev
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
pc-hpa Deployment/nginx 0%/3% 1 10 1 62s
DaemonSet (DS)
DaemonSet 类型的控制器可以保证集群中的每一台(或指定)节点上都运行一个副本,一般适用于日志收集、节点监控等场景。也就是说,如果一个 Pod 提供的功能是节点级别的(每个节点都需要且只需要一个 ),那么这类 Pod 就适合使用 DaemonSet 类型的控制器创建。
DaemonSet 控制器的特点
每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
当节点从集群中移除时,Pod 也就被垃圾回收了
DaemonSet 资源清单
apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: daemonset
spec: # 详情描述
revisionHistoryLimit: 3 # 保留历史版本
updateStrategy: # 更新策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxUnavailable: 1 # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
创建 pc-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: pc-daemonset
namespace: dev
spec:
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
Job
Job 主要用于负责批量处理短暂的一次性任务。Job 特点如下:
当 Job 创建的 Pod 执行成功时,Job 将记录成功结束的 Pod 数量
当成功结束的 Pod 达到指定的数量时,Job 将完成执行
Job 的资源清单文件
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: job
spec: # 详情描述
completions: 1 # 指定 job 需要成功运行 Pods 的次数。默认值: 1
parallelism: 1 # 指定 job 在任一时刻应该并发运行 Pods 的数量。默认值: 1
activeDeadlineSeconds: 30 # 指定 job 运行的时间期限,超过时间还未结束,系统将会尝试进行终止。
backoffLimit: 6 # 指定 job 失败后进行重试的次数。默认是6
manualSelector: true # 是否可以使用selector选择器选择pod,默认是false
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: counter-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [counter-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never # 重启策略只能设置为Never或者OnFailure
containers:
- name: counter
image: busybox:1.30
command: ["/bin/sh", "-c", "for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]
关于重启策略设置的说明:
如果指定为 OnFailure,则 job 会在 pod 出现故障时重启容器,而不是创建 pod,failed 次数不变
如果指定为 Never,则 job 会在 pod 出现故障时创建新的 pod,并且故障 pod 不会消失,也不会重启,failed 次数加 1
如果指定为 Always 的话,就意味着一直重启,意味着 job 任务会重复去执行了,当然不对,所以不能设置为 Always
创建pc-job.yaml,内容如下:
apiVersion: batch/v1
kind: Job
metadata:
name: pc-job
namespace: dev
spec:
manualSelector: true
selector:
matchLabels:
app: counter-pod
template:
metadata:
labels:
app: counter-pod
spec:
restartPolicy: Never
containers:
- name: counter
image: busybox:1.30
command: ["/bin/sh", "-c", "for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
CronJob()
CronJob 控制器以 Job 控制器资源为其管控对象,并借助它管理 Pod 资源对象,Job 控制器定义的作业任务在其控制器资源创建之后便会立即执行,但 CronJob 可以以类似于 Linux 操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob 可以在特定的时间点(反复的)去运行 Job 任务。
CronJob 的资源清单文件
apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: # 标签
controller: cronjob
spec: # 详情描述
schedule: # cron 格式的作业调度运行时间点,用于控制任务在什么时间执行
concurrencyPolicy: # 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业
failedJobHistoryLimit: # 为失败的任务执行保留的历史记录数,默认 为 1
successfulJobHistoryLimit: # 为成功的任务执行保留的历史记录数,默认为3
startingDeadlineSeconds: # 启动作业错误的超时时长
jobTemplate: # job 控制器模板,用于为 cronjob 控制器生成 job 对象;下面其实就是 job 的定义
metadata:
spec:
completions: 1
parallelism: 1
activeDeadlineSeconds: 30
backoffLimit: 6
manualSelector: true
selector:
matchLabels:
app: counter-pod
matchExpressions: 规则
- {key: app, operator: In, values: [counter-pod]}
template:
metadata:
重点选项解释
1. schedule
cron 表达式,用于指定任务的执行时间,格式:
<分钟> <小时> <日> <月份> <星期>
2、concurrencyPolicy
并发执行策略,定义前一次作业未完成时,如何处理后一次作业:
Allow:允许 Jobs 并发运行(默认)
Forbid:禁止并发运行,若上一次未完成,则跳过下一次运行
Replace:替换,取消当前运行的作业,并用新作业替换它
创建 pc-cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: pc-cronjob
namespace: dev
labels:
controller: cronjob
spec:
schedule: "*/1 * * * *"
jobTemplate:
metadata:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: counter
image: busybox:1.30
command: ["/bin/sh", "-c", "for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"]
Service 介绍
在 Kubernetes 中,Pod 是应用程序的载体,我们可以通过 Pod 的 IP 来访问应用程序,但 Pod 的 IP 地址不是固定的,这意味着不方便直接采用 Pod 的 IP 对服务进行访问。
为了解决这个问题,Kubernetes 提供了 Service 资源。Service 会对提供同一个服务的多个 Pod 进行聚合,并且提供一个统一的入口地址。通过访问 Service 的入口地址就能访问到后面的 Pod 服务。
Service 在很多场景下是逻辑概念,实际起关键作用的是 kube-proxy 服务进程 。Kubernetes 集群里,每个 Node 节点都会运行一个 kube-proxy 进程。
当创建 Service 时,会经 api-server 向 etcd 写入 Service 信息;kube-proxy 依托监听机制感知 Service 变动,再把最新的 Service 信息转化为对应的访问规则,以此实现 Service 对 Pod 访问的代理和转发,让客户端能通过统一入口稳定访问后端 Pod 。
中 kube - proxy 实现服务代理的两种模式(ipvs 模式、iptables 模式 )说明,核心区别和特点整理如下:
1. ipvs 模式
工作逻辑:与 iptables 类似,kube - proxy 监控 Pod 变化,动态创建 IPVS 规则。
核心优势:
转发效率比 iptables 更高(基于 Linux 内核的 IP 虚拟服务器,适合高并发);
支持更多负载均衡(LB)算法(如 rr、wrr、sh 等),策略更灵活。
2. iptables 模式
工作逻辑:kube - proxy 为 Service 后端每个 Pod 生成 iptables 规则,把发往 Cluster IP 的请求,直接重定向到某个 Pod IP。
特点:
kube - proxy 不承担 “四层负载均衡器” 角色,仅负责维护 iptables 规则;
相比 userspace 模式效率更高,但 LB 策略不够灵活(算法少),且后端 Pod 不可用时无法自动重试。
简单说,ipvs 更适合对性能、策略灵活性要求高的场景;iptables 模式相对轻量但功能有局限,可根据集群规模和需求选择 。
ipvs 模式
编辑 kube - proxy 配置 ConfigMap:
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
删除旧的 kube - proxy Pod,让新配置生效
[root@master ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
验证 ipvs 规则
通过 ipvsadm -Ln 查看 IPVS 规则,确认 Service 转发逻辑:
Kubernetes 启用 ipvs 代理模式的完整流程:改配置 → 重启 Pod → 用 ipvsadm 验证规则,确保服务转发走高性能的 ipvs 路径 。
Service 类型
Service 的资源清单文件示例
kind: Service # 资源类型
apiVersion: v1 # 资源版本
metadata: # 元数据
name: service # 资源名称
namespace: dev # 命名空间
spec: # 描述
selector: # 标签选择器,用于确定当前 service 代理哪些 pod
app: nginx
type: # Service 类型,指定 service 的访问方式
clusterIP: # 虚拟服务的 ip 地址
sessionAffinity: # session 亲和性,支持 ClientIP、None 两个选项
ports: # 端口信息
- protocol: TCP # 协议
port: 3017 # service 端口
targetPort: 5083 # pod 端口
nodePort: 31122 # 主机端口
Service 类型说明
ClusterIP(默认值):
Kubernetes 系统自动分配的虚拟 IP,仅能在集群内部访问,用于集群内服务间通信。
NodePort:
将 Service 通过指定 Node 上的端口暴露给外部,集群外部可通过 节点IP:nodePort 访问服务,实现 “从集群外访问内部服务”。
LoadBalancer:
借助外接负载均衡器(需云环境支持,如 AWS ELB、阿里云 SLB 等)分发流量到 Service,适合公网访问场景。
ExternalName:
把集群外部服务引入集群内部,通过 DNS 别名(如 externalname.service.dev.svc.cluster.local )让集群内直接调用,无需关心外部服务的实际地址。
Service 使用
在使用 Service 之前,需先通过 Deployment 创建 3 个 Pod,并为 Pod 设置 app=nginx-pod 标签
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
Deployment 创建与 Pod 验证
创建 Deployment
kubectl create -f deployment.yaml deployment.apps/pc-deployment created
kubectl get pods -n dev -o wide --show-labels
修改 Pod 内容(测试用)
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p8dh -n dev /bin/sh
# echo "10.244.1.40" > /usr/share/nginx/html/index.html
[root@master ~]# curl 10.244.1.40
10.244.1.40
[root@master ~]# curl 10.244.2.33
10.244.2.33
[root@master ~]# curl 10.244.1.39
10.244.1.39
ClusterIP 类型 Service 配置与验证
创建 service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: service-clusterip
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: 10.97.97.97 # 自定义 ClusterIP,可不写(自动分配)
type: ClusterIP
ports:
- port: 80 # Service 端口
targetPort: 80 # Pod 端口
[root@master ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-clusterip ClusterIP 10.97.97.97 <none> 80/TCP 13s app=nginx-pod
[root@master ~]# kubectl describe svc service-clusterip -n dev
Endpoint 与 Service 负载分发
Endpoint 概念
Endpoint 是 Kubernetes 的资源对象,存储在 etcd 中,记录 Service 对应 Pod 的访问地址,由 Service 的 selector 自动关联生成。
作用:作为 Service 和 Pod 的 “桥梁”,暴露后端 Pod 端点,让 Service 能找到要转发的目标。
Endpoint 查看(以 service-clusterip 为例)
[root@master ~]# kubectl get endpoints -n dev -o wide
NAME ENDPOINTS AGE
service-clusterip 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80 50m
Service 负载分发策略
Kubernetes 为 Service 提供两种核心负载策略:
(1)默认策略(由 kube-proxy 决定)
常见方式:随机、轮询等(取决于 kube-proxy 模式,如 ipvs/iptables 的算法)。
特点:请求均匀分发到后端 Pod,适合无状态服务。
(2)会话保持(基于客户端地址)
配置方式:在 Service 的 spec 中添加:
sessionAffinity: ClientIP
效果:同一客户端的请求,始终转发到固定 Pod,适合有状态场景(如需要会话延续的业务)。
核心逻辑
Endpoint 动态维护 Service 关联的 Pod 端点列表,是 Service 转发请求的 “数据源”。
负载策略 决定请求如何在这些端点中分配,实现流量分发的灵活性(默认均匀分发 / 可选会话保持)。
简单说,Endpoint 是 Service 找到 Pod 的 “地图”,负载策略是 “行驶规则”,共同保障 Service 对 Pod 的访问和流量管理 。
HeadLess 类型的 Service
场景与作用
在某些场景中,开发人员可能不想使用 Service 提供的负载均衡功能,而希望自己控制负载均衡策略。针对这种情况,Kubernetes 提供了 HeadLess Service:
不分配 Cluster IP;
需通过 Service 的域名访问,可自定义负载逻辑(如手动轮询、按业务规则调度)。
创建 service-headliness.yaml
apiVersion: v1
kind: Service
metadata:
name: service-headliness
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None # 将 clusterIP 设置为 None,创建 HeadLess Service
type: ClusterIP
ports:
- port: 80
targetPort: 80
操作与验证
[root@master ~]# kubectl create -f service-headliness.yaml
service/service-headliness created
[root@master ~]# kubectl get svc service-headliness -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-headliness ClusterIP None <none> 80/TCP 11s app=nginx-pod
核心特点
无 Cluster IP:依赖 DNS 解析 Service 域名(如 service-headliness.dev.svc.cluster.local ),解析结果是后端 Pod 的 IP 列表。
自定义负载:客户端拿到 Pod IP 后,可自行实现负载策略(如业务层轮询、按状态筛选)。
适合需要精细控制流量分发的场景(如数据库读写分离、自定义权重调度),把负载逻辑从 Kubernetes 转移到应用或客户端侧。
查看 HeadLess Service 域名解析
进入 Pod 查看 DNS 配置
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p8dh -n dev /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local
用 dig 解析 HeadLess Service 域名
[root@master ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.40
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.39
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.33
NodePort 类型的 Service
场景与作用
之前示例中,Service 的 IP(Cluster IP)仅集群内部可访问。若需暴露给集群外部,可使用 NodePort 类型 Service:
原理:将 Service 端口映射到集群节点的某个端口,外部通过 NodeIP:NodePort 访问 Service。
创建 service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
namespace: dev
spec:
selector:
app: nginx-pod
type: NodePort # 声明 Service 类型为 NodePort
ports:
- port: 80 # Service 自身端口
nodePort: 30002 # 映射到节点的端口(范围默认 30000-32767,不指定则自动分配)
targetPort: 80 # Pod 端口
操作与验证
[root@master ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) SELECTOR
service-nodeport NodePort 10.105.64.191 <none> 80:30002/TCP app=nginx-pod
核心特点
集群外访问:突破 Cluster IP 的局限,让外部流量通过节点 IP + 映射端口进入集群。
端口范围:nodePort 需在 30000-32767(可通过 --service-node-port-range 调整),不指定则由 Kubernetes 自动分配。
适合临时暴露服务到集群外(如测试环境),生产环境建议结合 LoadBalancer 或 Ingress 实现更可靠的公网访问。
LoadBalancer 类型的 Service
场景与作用
与 NodePort 类似,目标是向外部暴露服务端口,但更进阶:
NodePort 仅映射端口到集群节点,LoadBalancer 会额外关联外部负载均衡设备(如云厂商的 ELB、SLB )。
外部请求先到负载均衡设备,再由设备转发到集群内 NodePort / Pod,需外部环境(如公有云)支持。
外部请求 → 云厂商负载均衡器 → 集群节点(NodePort) → Service → Pod
ExternalName 类型的 Service
场景与作用
用于引入集群外部服务到集群内部,让集群内可通过 Service 域名访问外部服务,实现 “服务域名统一化”。
资源清单示例(service-externalname.yaml)
apiVersion: v1
kind: Service
metadata:
name: service-externalname
namespace: dev
spec:
type: ExternalName # 声明类型为 ExternalName
externalName: www.baidu # 外部服务地址(域名或 IP 均可)
[root@master ~]# kubectl create -f service-externalname.yaml
service/service-externalname created
[root@master ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.
www.baidu. 30 IN CNAME www.a.shifen.
www.a.shifen. 30 IN A 39.156.66.18
www.a.shifen. 30 IN A 39.156.66.14
两类 Service 对比
| 类型 | 核心作用 | 依赖环境 | 典型场景 |
|---|---|---|---|
| LoadBalancer | 关联外部负载均衡器,暴露服务到公网 | 公有云环境(如 AWS、阿里云) | 生产环境公网服务(需高可用、流量调度) |
| ExternalName | 引入外部服务到集群内部,统一域名访问 | 无特殊依赖 |
Ingress 介绍
Ingress 只需一个 NodePort 或一个 LB,即可满足多个 Service 的暴露需求,有效解决上述问题。其工作机制可简单理解为:通过统一的入口(Ingress),根据请求的域名、路径等规则,将外部流量转发到集群内不同的 Service ,实现 “一站式” 服务暴露与路由管理。
Ingress 相当于一个 7 层的负载均衡器,是 kubernetes 对反向代理的一个抽象,它的工作原理类似于 Nginx,可以理解成在 Ingress 里建立诸多映射规则,Ingress Controller 通过监听这些配置规则并转化成 Nginx 的反向代理配置,然后对外部提供服务。在这里有两个核心概念:
ingress: kubernetes 中的一个对象,作用是定义请求如何转发到 service 的规则
ingress controller: 具体实现反向代理及负载均衡的程序,对 ingress 定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,比如 Nginx, Contour, Haproxy 等等
Ingress(以 Nginx 为例)的工作原理如下:
用户编写 Ingress 规则,说明哪个域名对应 kubernetes 集群中的哪个 Service
Ingress 控制器动态感知 Ingress 服务规则的变化,然后生成一段对应的 Nginx 配置
Ingress 控制器会将生成的 Nginx 配置写入到一个运行着的 Nginx 服务中,并动态更新
到此为止,其实真正在工作的就是一个 Nginx 了,内部配置了用户定义的请求转发规则
Ingress 使用
搭建 ingress 环境
# 创建文件夹
[root@master ~]# mkdir ingress-controller
[root@master ~]# cd ingress-controller/
# 获取ingress-nginx,本次案例使用的是0.30版本
[root@master ingress-controller]# wget https://raw.githubusercontent/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
[root@master ingress-controller]# wget https://raw.githubusercontent/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
# 修改mandatory.yaml文件中的仓库
# 修改quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 为quay-mirror.qiniu/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 创建ingress-nginx
[root@master ingress-controller]# kubectl apply -f ./
# 查看ingress-nginx
[root@master ingress-controller]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-ingress-controller-fbf967dd-4qbp 1/1 Running 0 12h
# 查看service
[root@master ingress-controller]# kubectl get svc -n ingress-nginx
Nginx Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
Tomcat Deployment(tomcat-nginx.yaml 续段)
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: tomcat-pod
template:
metadata:
labels:
app: tomcat-pod
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
Nginx 与 Tomcat 的 HeadLess Service
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
namespace: dev
spec:
selector:
app: tomcat-pod
clusterIP: None
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
核心作用
Deployment:分别创建 3 个 Nginx Pod(标签 app: nginx-pod )和 3 个 Tomcat Pod(标签 app: tomcat-pod )。
HeadLess Service:通过 clusterIP: None 配置,让服务域名直接解析到 Pod IP(Nginx 用 nginx-service,Tomcat 用 tomcat-service ),实现自定义负载或直接访问 Pod 需求。
Http 代理
创建 ingress-http.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-http
namespace: dev
spec:
rules:
- host: nginx.itheima
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
Https 代理
创建证书
# 生成证书
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima"
# 创建密钥
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
创建 ingress-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-https
namespace: dev
spec:
tls:
- hosts:
- nginx.itheima
- tomcat.itheima
secretName: tls-secret # 指定密钥
rules:
- host: nginx.itheima
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
核心逻辑
HTTP 代理:通过 Ingress 规则,将 nginx.itheima 域名的请求转发到 nginx-service(80 端口 ),tomcat.itheima 转发到 tomcat-service(8080 端口 )。
HTTPS 代理:先通过 openssl 生成证书,用 kubectl 创建 TLS 密钥;再通过 Ingress 配置 tls 段关联证书,实现 HTTPS 加密访问,路由规则与 HTTP 代理一致。
简单说,这是基于 Ingress 实现 “多域名 HTTP/HTTPS 代理” 的完整流程,让外部请求通过域名转发到集群内不同 Service 。
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
192.168.109.100 nginx.itheima
192.168.109.100 tomcat.itheima
删除命名空间卡住
一、先确认命名空间状态
kubectl get ns | grep Terminating
- 若输出类似
xxx-ns Terminating 10m,说明确实卡住,需执行后续操作; - 若无输出,说明命名空间已删除,无需处理。
步骤 1:导出命名空间的 JSON 配置
# 替换 dev 为你卡住的命名空间名称
kubectl get ns dev -o json > ns-dev-terminating.json
步骤 2:编辑 JSON 文件,清空 finalizers 字段
vim ns-dev-terminating.json
"spec": {
"finalizers": [
"kubernetes" // 这行是锁定源,需删除
]
},
修改
"spec": {
"finalizers": [] // 清空 finalizers,解除锁定
},
启动 Kubernetes API 代理
在当前终端启动 API 代理(保持终端打开,不要关闭),用于后续通过 API 直接修改命名空间配置:
kubectl proxy
新启终端,提交删除请求
# 替换 dev 为你卡住的命名空间名称,替换文件名为你实际的 JSON 文件名
curl -H "Content-Type: application/json" -X PUT --data-binary @ns-dev-terminating.json http://127.0.0.1:8001/api/v1/namespaces/dev/finalize
# 替换 dev 为你卡住的命名空间名称
kubectl get ns dev
# 1. 列出命名空间内所有残留资源(替换 dev 为目标命名空间)
kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get -n dev
# 2. 强制删除残留的 Pod(若有)
kubectl delete pod -n dev --all --grace-period=0 --force 2>/dev/null
# 3. 强制删除残留的 Service/Secret/ConfigMap 等(若有)
kubectl delete svc,secret,configmap -n dev --all --grace-period=0 --force 2>/dev/null
# 4. 再次执行步骤 4 的 curl 命令,确认删除
数据存储
Volume 是 Pod 中能够被多个容器访问的共享目录,它被定义在 Pod 上,然后被一个 Pod 里的多个容器挂载到具体的文件目录下,kubernetes 通过 Volume 实现同一个 Pod 中不同容器之间的数据共享以及数据的持久化存储。Volume 的生命容器不与 Pod 中单个容器的生命周期相关,当容器终止或者重启时,Volume 中的数据也不会丢失。
kubernetes 的 Volume 支持多种类型,比较常见的有下面几个:
简单存储:EmptyDir、HostPath、NFS
高级存储:PV、PVC
配置存储:ConfigMap、Secret
EmptyDir
EmptyDir 是最基础的 Volume 类型,一个 EmptyDir 就是 Host 上的一个空目录。
EmptyDir 是在 Pod 被分配到 Node 时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为 kubernetes 会自动分配一个目录,当 Pod 销毁时,EmptyDir 中的数据也会被永久删除。EmptyDir 用途如下:
临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
一个容器需要从另一个容器中获取数据的目录(多容器共享目录)
接下来,通过一个容器之间文件共享的案例来使用一下 EmptyDir。
在一个 Pod 中准备两个容器 nginx 和 busybox,然后声明一个 Volume 分别挂在到两个容器的目录中,然后 nginx 容器负责向 Volume 中写日志,busybox 中通过命令将日志内容读到控制台。
volume-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts: # 将logs-volume挂在到nginx容器中,对应的目录为 /var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容
volumeMounts: # 将logs-volume 挂在到busybox容器中,对应的目录为 /logs
- name: logs-volume
mountPath: /logs
volumes: # 声明volume,name为logs-volume,类型为emptyDir
- name: logs-volume
emptyDir: {}
[root@master ~]# kubectl create -f volume-emptydir.yaml
pod/volume-emptydir created
[root@master ~]# kubectl get pods volume-emptydir -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
HostPath
EmptyDir 中数据不会被持久化,它会随着 Pod 的结束而销毁,如果想简单的将数据持久化到主机中,可以选择 HostPath。
HostPath 就是将 Node 主机中一个实际目录挂在到 Pod 中,以供容器使用,这样的设计就可以保证 Pod 销毁了,但是数据依据可以存在于 Node 主机上。
创建一个 volume-hostpath.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-hostpath
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
hostPath:
path: /root/logs
type: DirectoryOrCreate # 目录存在就使用,不存在就先创建后使用
关于 type 的值的一点说明:
| type 值 | 说明 |
|---|---|
| DirectoryOrCreate | 目录存在就使用,不存在就先创建后使用 |
| Directory | 目录必须存在 |
| FileOrCreate | 文件存在就使用,不存在就先创建后使用 |
| File | 文件必须存在 |
| Socket | unix 套接字必须存在 |
| CharDevice | 字符设备必须存在 |
| BlockDevice | 块设备必须存在 |
NFS
HostPath 可以解决数据持久化的问题,但是一旦 Node 节点故障了,Pod 如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用 NFS、CIFS。
NFS 是一个网络文件存储系统,可以搭建一台 NFS 服务器,然后将 Pod 中的存储直接连接到 NFS 系统上,这样的话,无论 Pod 在节点上怎么转移,只要 Node 跟 NFS 的对接没问题,数据就可以成功访问。
首先要准备 nfs 的服务器,这里为了简单,直接是 master 节点做 nfs 服务器
# 在master上安装nfs服务
[root@master ~]# yum install nfs-utils -y
# 准备一个共享目录
[root@master ~]# mkdir /root/data/nfs -pv
# 将共享目录以读写权限暴露给192.168.109.0/24网段中的所有主机
[root@master ~]# vim /etc/exports
[root@master ~]# more /etc/exports
/root/data/nfs 192.168.109.0/24(rw,no_root_squash)
# 启动nfs服务
[root@master ~]# systemctl start nfs
接下来,要在的每个 node 节点上都安装下 nfs,这样的目的是为了 node 节点可以驱动 nfs 设备
# 在node上安装nfs服务,注意不需要启动
[root@master ~]# yum install nfs-utils -y
3)接下来,就可以编写pod的配置文件了,创建volume-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
nfs:
server: 192.168.109.100 #nfs服务器地址
path: /root/data/nfs #共享文件路径
PV 和 PVC
使用 NFS 提供存储,此时就要求用户会搭建 NFS 系统,并且会在 yaml 配置 nfs。由于 kubernetes 支持的存储系统有很多,要求客户全都掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用,kubernetes 引入 PV 和 PVC 两种资源对象。
PV(Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下 PV 由 kubernetes 管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。
PVC(Persistent Volume Claim)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC 其实就是用户向 kubernetes 系统发出的一种资源需求申请。
使用 PV 和 PVC 后的分工细分
使用了 PV 和 PVC 之后,工作可以得到进一步的细分:
- 存储:存储工程师维护
- PV:kubernetes 管理员维护
- PVC:kubernetes 用户维护
实体与关联
用户侧(上半部分):
Pod:业务容器,通过 PVC 申请存储。
PVC(如 pvc1、pvc2 ):用户对存储的需求声明,关联到具体 PV。
管理员侧(下半部分):
PV(如 pv1、pv2、pv3 等 ):管理员提前创建的持久化卷,与底层存储对接(如 NFS、CIFS、GlusterFS )。
底层存储:NFS(网络文件系统 )、CIFS(通用网络文件系统 )、GlusterFS(分布式文件系统 ),是实际存储数据的地方。
流程逻辑
用户 创建 PVC(如 pvc1 ),声明存储需求;
Kubernetes 自动匹配可用的 PV(如 pv1 ),将 PVC 与 PV 绑定;
Pod 通过 PVC 挂载存储,实现数据持久化;
管理员 维护 PV 及底层存储(如配置 NFS、CIFS 等 ),屏蔽存储细节,让用户只需关注 PVC。
核心价值
解耦存储管理:用户通过 PVC 简单声明需求,管理员通过 PV 对接复杂存储(NFS、CIFS 等 ),分工清晰。
存储抽象化:屏蔽底层存储差异,用户无需关心数据存在 NFS 还是 GlusterFS,只需用 PVC 申请即可。
简单说,这是 Kubernetes 中 “用户按需申请(PVC)→ 系统匹配存储(PV)→ 对接底层存储” 的经典流程,让存储管理更灵活、更易维护 。
PV
PV 是存储资源的抽象,下面是资源清单文件:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存储类型,与底层真正存储对应
capacity: # 存储能力,目前只支持存储空间的设置
storage: 2Gi
accessModes: # 访问模式
storageClassName: # 存储类别
persistentVolumeReclaimPolicy: # 回收策略
PV 的关键配置参数说明:
-
存储类型
底层实际存储的类型,kubernetes 支持多种存储类型,每种存储类型的配置都有所差异 -
存储能力(capacity)
目前只支持存储空间的设置 (storage=1Gi),不过未来可能会加入 IOPS、吞吐量等指标的配置 -
访问模式(accessModes)
用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:- ReadWriteOnce (RWO):读写权限,但是只能被单个节点挂载
- ReadOnlyMany (ROX): 只读权限,可以被多个节点挂载
- ReadWriteMany (RWX):读写权限,可以被多个节点挂载
需要注意的是,底层不同的存储类型可能支持的访问模式不同
-
回收策略 (persistentVolumeReclaimPolicy)
当 PV 不再被使用了之后,对其的处理方式。目前支持三种策略:- Retain(保留) 保留数据,需要管理员手工清理数据
- Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
- Delete(删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务
需要注意的是,底层不同的存储类型可能支持的回收策略不同
补充参数说明
-
存储类别
PV 可以通过storageClassName参数指定一个存储类别- 具有特定类别的 PV 只能与请求了该类别的 PVC 进行绑定
- 未设定类别的 PV 则只能与不请求任何类别的 PVC 进行绑定
-
状态 (status)
一个 PV 的生命周期中,可能会处于 4 中不同的阶段:- Available(可用): 表示可用状态,还未被任何 PVC 绑定
- Bound(已绑定): 表示 PV 已经被 PVC 绑定
- Released(已释放): 表示 PVC 被删除,但是资源还未被集群重新声明
- Failed(失败): 表示该 PV 的自动回收失败
使用 NFS 作为存储,来演示 PV 的使用,创建 3 个 PV,对应 NFS 中的 3 个暴露的路径。
准备 NFS 环境
# 创建目录
[root@master ~]# mkdir /root/data/{pv1,pv2,pv3} -pv
# 暴露服务
[root@master ~]# more /etc/exports
/root/data/pv1 192.168.109.0/24(rw,no_root_squash)
/root/data/pv2 192.168.109.0/24(rw,no_root_squash)
/root/data/pv3 192.168.109.0/24(rw,no_root_squash)
# 重启服务
[root@master ~]# systemctl restart nfs
创建 pv.yaml
pv1 定义:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv1
server: 192.168.109.100
pv2 定义:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv2
server: 192.168.109.100
pv3 定义:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv3
server: 192.168.109.100
PVC
PVC 是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息。下面是资源清单文件:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 访问模式
selector: # 采用标签对 PV 选择
storageClassName: # 存储类别
resources: # 请求空间
requests:
storage: 5Gi
实验
创建 pvc.yaml 申请 pv
pvc1 定义:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
pvc2 定义:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
创建 pods.yaml, 使用 pv
创建 pods.yaml, 使用 pv
pod1 定义
apiVersion: v1
kind: Pod
metadata:
name: pod1
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc1
readOnly: false
pod2 定义
apiVersion: v1
kind: Pod
metadata:
name: pod2
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh", "-c", "while true;do echo pod2 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc2
readOnly: false
核心逻辑
Pod 与 PVC 关联:通过 persistentVolumeClaim 字段,pod1 绑定 pvc1,pod2 绑定 pvc2。
数据持久化:Pod 内的 busybox 容器持续向挂载目录(/root/ )写数据,数据会持久化到 PVC 关联的 PV 存储中。
简单说,这是 “Pod → PVC → PV” 存储使用流程的实践,让 Pod 数据借助 PVC 动态关联 PV 实现持久化 。
生命周期
PVC 和 PV 是一一对应的,PV 和 PVC 之间的相互作用遵循以下生命周期:
- 资源供应:管理员手动创建底层存储和 PV
- 资源绑定:用户创建 PVC,kubernetes 负责根据 PVC 的声明去寻找 PV,并绑定
- 在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在已存在的 PV 中选择一个满足条件的
- 一旦找到,就将该 PV 与用户定义的 PVC 进行绑定,用户的应用就可以使用这个 PVC 了
- 如果找不到,PVC 则会无限期处于 Pending 状态,直到等到系统管理员创建了一个符合其要求的 PV
- PV 一旦绑定到某个 PVC 上,就会被这个 PVC 独占,不能再与其他 PVC 进行绑定了
- 在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在已存在的 PV 中选择一个满足条件的
- 资源使用:用户可在 pod 中像 volume 一样使用 pvc
- Pod 使用 Volume 的定义,将 PVC 挂载到容器内的某个路径进行使用。
- 资源释放:用户删除 pvc 来释放 pv
- 当存储资源使用完毕后,用户可以删除 PVC,与该 PVC 绑定的 PV 将会被标记为 “已释放”,但还不能立刻与其他 PVC 进行绑定。通过之前 PVC 写入的数据可能还被留在存储设备上,只有在清除之后该 PV 才能再次使用。
- 资源回收:kubernetes 根据 pv 设置的回收策略进行资源的回收
- 对于 PV,管理员可以设定回收策略,用于设置与之绑定的 PVC 释放资源之后如何处理遗留数据的问题。只有 PV 的存储空间完成回收,才能供新的 PVC 绑定和使用
ConfigMap
ConfigMap 是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。
创建 configmap.yaml,内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap
namespace: dev
data:
info: |
username:admin
password:123456
ConfigMap 操作与 Pod 挂载流程
创建 ConfigMap
# 创建configmap
[root@master ~]# kubectl create -f configmap.yaml
configmap/configmap created
# 查看configmap详情
[root@master ~]# kubectl describe cm configmap -n dev
挂载 ConfigMap 到 Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将configmap挂载到目录
- name: config
mountPath: /configmap/config
volumes: # 引用configmap
- name: config
configMap:
name: configmap
# 创建pod
[root@master ~]# kubectl create -f pod-configmap.yaml
pod/pod-configmap created
# 查看pod
[root@master ~]# kubectl get pod pod-configmap -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 0 6s
# 进入容器
[root@master ~]# kubectl exec -it pod-configmap -n dev /bin/sh
# cd /configmap/config/
# ls
info
# more info
username:admin
password:123456
# 可以看到映射已经成功,每个configmap都映射成了一个目录
# key--->文件 value---->文件中的内容
# 此时如果更新configmap的内容,容器中的值也会动态更新
核心验证逻辑
创建 & 查看 Pod:确认 Pod 正常运行。
进入容器:通过 exec 进入 Pod 内的容器。
检查挂载:进入 ConfigMap 挂载目录(/configmap/config/ ),查看文件(info )及内容,验证 ConfigMap 配置已成功挂载到容器内。
简单说,这是 “验证 ConfigMap 挂载是否生效” 的实操流程,确认配置可被容器读取 。
简单说,这是 “验证 ConfigMap 挂载是否生效” 的实操流程,确认配置可被容器读取 。
Secret
在 kubernetes 中,还存在一种和 ConfigMap 非常类似的对象,称为 Secret 对象。它主要用于存储敏感信息,例如密码、秘钥、证书等等。
首先使用 base64 对数据进行编码
[root@master ~]# echo -n 'admin' | base64 #准备username
YWRtaW4=
[root@master ~]# echo -n '123456' | base64 #准备password
MTIzNDU2
接下来编写 secret.yaml,并创建 Secret
apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: dev
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
创建 pod-secret.yaml,将上面创建的 secret 挂载进去:
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将secret挂载到目录
- name: config
mountPath: /secret/config
volumes:
- name: config
secret:
secretName: secret
访问控制概述
Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对 Kubernetes 的各种客户端进行认证和鉴权操作。
客户端
在 Kubernetes 集群中,客户端通常有两类:
User Account:一般是独立于 kubernetes 之外的其他服务管理的用户账号。
Service Account:kubernetes 管理的账号,用于为 Pod 中的服务进程在访问 Kubernetes 时提供身份标识。
认证、授权与准入控制
ApiServer 是访问及管理资源对象的唯一入口。任何一个请求访问 ApiServer,都要经过下面三个流程:
Authentication(认证):身份鉴别,只有正确的账号才能够通过认证
Authorization(授权): 判断用户是否有权限对访问的资源执行特定的动作
Admission Control(准入控制):用于补充授权机制以实现更加精细的访问控制功能。
安装前准备
需要 64 位操作系统
至少 RHEL6.5 以上的版本,强烈推荐 RHEL7
关闭防火墙(不是必须)
软件包安装
yum -y install docker
systemctl restart docker
systemctl enable docker
准备3台虚拟机
172.25.0.11,
172.25.0.10,docker1
172.25.0.33 docker2
安装docker
[root@c1 ~]# yum -y install docker
[root@destop ~]# yum -y install docker
yum install libguestfs-tools-c
本文标签: 平台
版权声明:本文标题:云平台服务 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://it.en369.cn/jiaocheng/1759873050a2826206.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。


发表评论