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
    
    

    构建原理

    1. Docker 守护进程读取 Dockerfile 和构建上下文。

    2. 按 Dockerfile 指令顺序执行,每执行一条指令(如 RUNCOPY)创建一个新的镜像层。

    3. 利用缓存加速构建:若指令未修改,且依赖的上一层未变化,则直接复用缓存层。

    4. 最终生成完整镜像,可通过 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

    经常使用的资源有下面这些:

    资源分类资源名称缩写资源作用
    集群级别资源nodesno集群组成部分
    namespacesns隔离 Pod
    pod 资源podspo装载容器
    pod 资源控制器replicationcontrollersrc控制 pod 资源
    replicasetsrs控制 pod 资源
    deploymentsdeploy控制 pod 资源
    daemonsetsds控制 pod 资源
    jobs控制 pod 资源
    cronjobscj控制 pod 资源
    horizontalpodautoscalershpa控制 pod 资源
    statefulsetssts控制 pod 资源
    服务发现资源servicessvc统一 pod 对外接口
    ingressing统一 pod 对外接口
    存储资源volumeattachments存储
    persistentvolumespv存储
    # 创建一个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-  

    操作步骤

    1. 准备节点 c1(为让演示效果更明显,暂时停止 destop 节点 )

    2. 为 c1 节点设置污点 tag=heima:PreferNoSchedule,然后创建 pod1

    3. 修改 c1 节点污点为 tag=heima:NoSchedule,然后创建 pod2

    4. 修改 c1 节点污点为 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对应要容忍的污点的值
    operatorkey-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文件必须存在
    Socketunix 套接字必须存在
    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 进行绑定了
    • 资源使用:用户可在 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
      
      

      构建原理

      1. Docker 守护进程读取 Dockerfile 和构建上下文。

      2. 按 Dockerfile 指令顺序执行,每执行一条指令(如 RUNCOPY)创建一个新的镜像层。

      3. 利用缓存加速构建:若指令未修改,且依赖的上一层未变化,则直接复用缓存层。

      4. 最终生成完整镜像,可通过 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

      经常使用的资源有下面这些:

      资源分类资源名称缩写资源作用
      集群级别资源nodesno集群组成部分
      namespacesns隔离 Pod
      pod 资源podspo装载容器
      pod 资源控制器replicationcontrollersrc控制 pod 资源
      replicasetsrs控制 pod 资源
      deploymentsdeploy控制 pod 资源
      daemonsetsds控制 pod 资源
      jobs控制 pod 资源
      cronjobscj控制 pod 资源
      horizontalpodautoscalershpa控制 pod 资源
      statefulsetssts控制 pod 资源
      服务发现资源servicessvc统一 pod 对外接口
      ingressing统一 pod 对外接口
      存储资源volumeattachments存储
      persistentvolumespv存储
      # 创建一个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-  

      操作步骤

      1. 准备节点 c1(为让演示效果更明显,暂时停止 destop 节点 )

      2. 为 c1 节点设置污点 tag=heima:PreferNoSchedule,然后创建 pod1

      3. 修改 c1 节点污点为 tag=heima:NoSchedule,然后创建 pod2

      4. 修改 c1 节点污点为 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对应要容忍的污点的值
      operatorkey-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文件必须存在
      Socketunix 套接字必须存在
      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 进行绑定了
      • 资源使用:用户可在 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

      本文标签: 平台