1 微服务
把一个庞大的应用拆成几个小的独立的服务,再把独立的服务串起来的一种架构设计:内聚更强,更加敏捷
1.1 应用架构的发展
1.2 传统单体架构vs微服务软件架构
不同于微服务,传统的项目会包含很多功能,是一个大而全的“超级”工程
例如:以普通架构方式实现的电商平台包含:登录、权限、会员、商品库存、订单、收藏、关注、购物车等功能的多个单一项目。随着项目业务越来越复杂、开发人员越来越多,相应开发、编译、部署、技术扩展、水平扩展都会受到限制
1.3 基于微服务的系统架构
核心思路是拆分
单体项目的问题,通过把项目拆分成一个个小项目就可以解决
1.4 微服务的特征
1.5 单体架构
紧耦合面临的问题:故障影响范围大、变更成本高、无法支持大规模计算
如果需要加入模块C,需要更改模块A、B的代码,需要各个系统人员协调处理
1.6 解耦架构
解耦架构的优势:
1.模块化,缩小故障范围
2.降低变更成本,插入新模块不影响其他模块
3.开发人员协作更简单
4.易于扩展
1.7 消息队列
1.7.1 传统架构
1.7.2 消息队列架构
1.8 微服务面临的挑战
单体架构 | 微服务架构 | |
---|---|---|
迭代速度 | 较慢 | 快 |
部署频率 | 不经常部署 | 经常发布 |
系统性能 | 吞吐量小 | 吞吐量大 |
系统扩展性 | 扩展性差 | 扩展性好 |
技术栈多样性 | 单一、封闭 | 多样、开放 |
运维 | 简单 | 运维复杂 |
部署难度 | 容易部署 | 较难部署 |
架构复杂度 | 较小 | 复杂度高 |
查错 | 简单 | 定位问题较难 |
管理成本 | 主要在于开发成本 | 服务治理、运维 |
1.9 虚拟机与容器的比较
对比模块 | 虚拟机 | 容器 |
---|---|---|
占用空间 | 非常大,GB级别 | 小,MB/KB级别 |
启用速度 | 慢,分钟级 | 快,秒级 |
运行形态 | 运行于Hypervisor | 直接运行在宿主机内核上 |
并发性 | 一台宿主机上十几个,最多几 十个 | 上百个,甚至数百个 |
性能 | 低于宿主机 | 接近于宿主机本地进程 |
资源利用率 | 低 | 高 |
2 容器的基本使用
2.1 容器介绍
容器是一个可以在共享的操作系统上将应用程序以指定格式打包并运行在一个与操作系统相关联的环境中的方法
和虚拟机相比,容器并不会打包整个操作系统,而只是打包应用程序所必须的库和设置,这将使得容器具备高效率、轻量化、系统隔离性,以上特性将会确保无论在何处部署,容器每次运行都会完全一致
容器工具:Rkt、Containerd、Docker、Podman
2.2 部署Docker
从南京大学开源镜像站在Ubuntu上安装docker
2.2.1 安装依赖
# 检测系统是否安装了docker
root@k8s-master:~# for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
# 安装依赖
root@k8s-master:~# sudo apt-get update
root@k8s-master:~# sudo apt-get install ca-certificates curl gnupg
2.2.2 安装GPG公钥
root@k8s-master:~# sudo install -m 0755 -d /etc/apt/keyrings
root@k8s-master:~# curl -fsSL https://mirror.nju.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
root@k8s-master:~# sudo chmod a+r /etc/apt/keyrings/docker.gpg
2.2.3 添加Docker仓库
root@k8s-master:~# echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirror.nju.edu.cn/docker-ce/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
2.2.4 安装Docker
root@k8s-master:~# sudo apt-get update
root@k8s-master:~# sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 拉取失败,因此在中国需要加速器
root@k8s-master:~# sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
Run 'docker run --help' for more information
2.2.5 Docker镜像加速器
root@k8s-master:~# mkdir -p /etc/docker
root@k8s-master:~# cd /etc/docker
root@k8s-master:/etc/docker# vim daemon.json
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://mirror.ccs.tencentyun.com",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://mirror.gcr.io",
"https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com"
],
"exec-opts": ["native.cgroupdriver=systemd"]
}
root@k8s-master:~# systemctl daemon-reload
root@k8s-master:~# systemctl restart docker
root@k8s-master:~# sudo docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
Digest: sha256:c41088499908a59aae84b0a49c70e86f4731e588a737f1637e73c8c09d995654
Status: Image is up to date for hello-world:latest
docker.io/library/hello-world:latest
root@k8s-master:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 74cc54e27dc4 3 months ago 10.1kB
2.2.6 重启Docker服务
root@k8s-master:~# systemctl restart docker
root@k8s-master:~# docker info
Client: Docker Engine - Community
Version: 28.1.1
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.23.0
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.35.1
Path: /usr/libexec/docker/cli-plugins/docker-compose
Server:
Containers: 2
Running: 1
Paused: 0
Stopped: 1
Images: 2
Server Version: 28.1.1
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Using metacopy: false
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: systemd
Cgroup Version: 2
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 05044ec0a9a75232cad458027ca83437aae3f4da
runc version: v1.2.5-0-g59923ef
init version: de40ad0
Security Options:
apparmor
seccomp
Profile: builtin
cgroupns
Kernel Version: 6.8.0-53-generic
Operating System: Ubuntu 24.04.2 LTS
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.777GiB
Name: k8s-master
ID: 6c5b4dc6-423d-47e6-a687-e75062cf4fd9
Docker Root Dir: /var/lib/docker
Debug Mode: false
Experimental: false
Insecure Registries:
::1/128
127.0.0.0/8
Registry Mirrors:
https://docker.mirrors.ustc.edu.cn/
https://mirror.baidubce.com/
https://docker.m.daocloud.io/
https://mirror.ccs.tencentyun.com/
https://docker.nju.edu.cn/
https://docker.mirrors.sjtug.sjtu.edu.cn/
https://mirror.gcr.io/
https://docker.registry.cyou/
https://docker-cf.registry.cyou/
https://dockercf.jsdelivr.fyi/
https://docker.jsdelivr.fyi/
https://dockertest.jsdelivr.fyi/
https://mirror.aliyuncs.com/
https://dockerproxy.com/
2.3 操作容器
2.3.1 创建持续运行的容器
root@k8s-master:~# docker run -d --name nginxtest nginx
root@k8s-master:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b68184fd3b9b nginx "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp nginxtest
root@k8s-master:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b68184fd3b9b nginx "/docker-entrypoint.…" About a minute ago Up About a minute 80/tcp nginxtest
8ea8394296ac hello-world "/hello" 22 minutes ago Exited (0) 22 minutes ago goofy_brattain
2.3.2 进入容器
root@k8s-master:~# docker exec -it nginxtest /bin/bash
root@b68184fd3b9b:/# cat /etc/nginx/nginx.conf
root@b68184fd3b9b:/# exit
exit
root@k8s-master:~#
# @后的主机名在exec后发生了变化,这就是进入容器内的标志
2.3.3 删除容器
root@k8s-master:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
260028d6b4a2 httpd:v1 "httpd-foreground" 5 seconds ago Up 4 seconds 0.0.0.0:4000->80/tcp, [::]:4000->80/tcp luoyudockerfile
b68184fd3b9b nginx "/docker-entrypoint.…" 12 hours ago Up 12 hours 80/tcp nginxtest
root@k8s-master:~# docker rm -f nginxtest luoyudockerfile
nginxtest
luoyudockerfile
root@k8s-master:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2.4 构建&使用镜像
2.4.1 镜像概述
镜像是一个用于创建容器的只读模板,通常来讲,包含一些额外的自定义,比如说,可以构建一个基于centos的镜像,在镜像构建时,直接包含一些应用程序,比如Apache或者其他程序,构建完成后,可以直接基于这个镜像启动容器,快速获得期望的业务
镜像可以来自公共的仓库,也可通过Dockerfile等定义文件来构建,并且把自己的镜像推送到仓库中,以备在任何地方任何时间下载使用
2.4.2 公共镜像仓库
Docker公共镜像仓库:https://hub.docker.com
Docker Hub是一个基于云端的registry服务,这个服务允许我们连接仓库代码、建立镜像、 推送镜像等功能,提供了一个集中式的容器资源管理平台,包含了各式各样的官方镜像,例如Apache、Centos以及各种企业级应用镜像,还以星级和评分来确保镜像的可靠性和适用性
打开https://hub.docker.com,注册一个Docker ID,登陆后浏览各项功能
2.4.3 镜像分层技术
2.4.4 构建镜像的方法
1.docker commit
使用容器中发生更改的部分生成一个新的镜像,通常的使用场景为,基于普通镜像启动了容器,在容器内部署了所需的业务后,把当前的状态重新生成镜像,以便以当前状态快速部署业务所用
2.Dockerfile 创建镜像
从零开始构建自己所需的镜像,在创建镜像之初把所需的各种设置和所需要的各种应用程序包含进去,生成的镜像可直接用于业务部署
3.Dockerfile高频指令集
2.4.5 Dockerfile image
在设计Dockerfile时应考虑以下事项:
1.容器应该是暂时的
2.避免安装不必要的软件包
3.每个容器只应该有一个用途
4.避免容器有过多的层
5.多行排序
6.建立缓存
# 创建dockerfile文件
root@k8s-master:~# cat > dockerfile <<EOF
FROM httpd
MAINTAINER luovipyu@163.com
RUN echo hello luoyu dockerfile container > /usr/local/apache2/htdocs/index.html
EXPOSE 80
WORKDIR /usr/local/apache2/htdocs/
EOF
# 开始构建镜像
root@k8s-master:~# docker build -t httpd:v1 -f dockerfile .
# 查看docker镜像
root@k8s-master:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd v1 d6d24a446dd4 25 seconds ago 148MB
nginx latest a830707172e8 3 weeks ago 192MB
hello-world latest 74cc54e27dc4 3 months ago 10.1kB
注明:如果文件名是Dockerfile时可不指定
docker build -t web:v1 .
# 如果用的是containerd,dockerfile方式构建容器镜像的命令就是下面这样的
nerdctl build -t httpd:v1 -f dockerfile .
nerdctl images
2.4.6 使用Dockerfile镜像
# 用httpd:v1的镜像在本机4000端口上提供一个名为luoyudockerfile的容器
root@k8s-master:~# docker run -d -p 4000:80 --name luoyudockerfile httpd:v1
260028d6b4a2b11cd2cfca9ab6ae9d406cc8fa9afd33131db03c880cc235e68f
root@k8s-master:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
260028d6b4a2 httpd:v1 "httpd-foreground" 5 seconds ago Up 4 seconds 0.0.0.0:4000->80/tcp, [::]:4000->80/tcp luoyudockerfile
b68184fd3b9b nginx "/docker-entrypoint.…" 12 hours ago Up 12 hours 80/tcp nginxtest
root@k8s-master:~# curl http://127.0.0.1:4000
hello luoyu dockerfile container
# 如果用的是containerd,dockerfile方式构建容器镜像的使用命令就是下面这样的
nerdctl run -d -p 4000:80 --name luoyudockerfile httpd:v1
nerdctl ps
2.4.7 关于镜像命名
1.镜像命名格式:REPOSITORY+TAG,使用版本号作为命名
root@k8s-master:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd v1 d6d24a446dd4 11 hours ago 148MB
nginx latest a830707172e8 3 weeks ago 192MB
hello-world latest 74cc54e27dc4 3 months ago 10.1kB
2.关于latest tag的说明
如果在建立镜像时没有指定Tag,会使用默认值latest,所以,当看到一个镜像的Tag处显示latest的时候,并不一定意味着此版本是最新版,而只意味着在建立镜像的时候,没有指定Tag
root@k8s-master:~# docker build -t web .
root@k8s-master:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd v1 d6d24a446dd4 11 hours ago 148MB
web latest d6d24a446dd4 11 hours ago 148MB
nginx latest a830707172e8 3 weeks ago 192MB
hello-world latest 74cc54e27dc4 3 months ago 10.1kB
2.4.8 删除镜像
删除一个特定的镜像,需要知道该镜像的ID或者标签(repository:tag)。或者,如果只记得镜像的部分ID,可以使用这个ID来删除镜像
root@k8s-master:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd v1 d6d24a446dd4 11 hours ago 148MB
web latest d6d24a446dd4 11 hours ago 148MB
nginx latest a830707172e8 3 weeks ago 192MB
hello-world latest 74cc54e27dc4 3 months ago 10.1kB
root@k8s-master:~# docker rmi web:latest
Untagged: web:latest
root@k8s-master:~# docker rmi 74cc54e27dc4
Error response from daemon: conflict: unable to delete 74cc54e27dc4 (must be forced) - image is being used by stopped container 8ea8394296ac
root@k8s-master:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8ea8394296ac hello-world "/hello" 13 hours ago Exited (0) 13 hours ago goofy_brattain
root@k8s-master:~# docker rm 8ea8394296ac
root@k8s-master:~# docker rmi 74cc54e27dc4
root@k8s-master:~# docker images
\REPOSITORY TAG IMAGE ID CREATED SIZE
httpd v1 d6d24a446dd4 11 hours ago 148MB
nginx latest a830707172e8 3 weeks ago 192MB
2.4.9 私有镜像仓库
构建私有镜像存储考虑:
1.交付时效,例如,下载并运行镜像,需要消耗带宽和时间
2.机房是否允许接入外网
3.镜像私密,不允许将数据放到外网
4.内网速率更高
什么是Registry?
1.Registry是一个无状态、高度可扩展的服务,可以存储和分发镜像
2.Registry是一个基于Apache License许可的开源服务
为什么使用Registry?
1.严格控制映像存储位置
2.拥有完全属于自己的镜像分发渠道
3.将镜像存储和分布紧密集成到内部开发工作流程中
部署Registry步骤如下:如果选用Harbor,请参考Gitee文档
1.下载Docker官方最新版的Registry镜像
2.启动Registry容器
3.下载测试镜像
4.将测试镜像上传至自己的私有仓库
5.验证从自有仓库下载并启动容器
3 部署Harbor私有仓库
在现代软件开发中,容器化应用已经成为主流,而容器镜像仓库则是确保容器镜像安全、管理和分发的重要工具。Harbor作为一款开源的企业级容器镜像仓库管理工具,不仅支持多种认证方式,还提供镜像复制、漏洞扫描和用户访问控制等功能,为企业提供了一个安全、高效的镜像管理方案
主机名 | 角色 | IP | VMware网络类型 | 用户名 | 密码 | 系统 |
---|---|---|---|---|---|---|
harbor | Harbor主机 | 192.168.8.52 | NAT | root | harbor | RHEL-9.5 |
3.1 RedHat9镜像源配置
3.1.1 国内镜像源
[root@harbor ~]# cd /etc/yum.repos.d
[root@harbor yum.repos.d]# ll
-rw-r--r--. 1 root root 358 May 14 11:25 redhat.repo
[root@harbor yum.repos.d]# vim aliyun_yum.repo
[ali_baseos]
name=ali_baseos
baseurl=https://mirrors.aliyun.com/centos-stream/9-stream/BaseOS/x86_64/os/
gpgcheck=0
[ali_appstream]
name=ali_appstream
baseurl=https://mirrors.aliyun.com/centos-stream/9-stream/AppStream/x86_64/os/
gpgcheck=0
[root@harbor yum.repos.d]# yum makecache
# 根据需要选择是否更新yum源
[root@harbor yum.repos.d]# yum -y update
3.1.2 本地yum源配置
配置本地yum源可以创建一个本地的软件包存储库,以便更快地安装、更新和管理软件包
# 创建文件夹并将光盘挂载到新建的文件中
[root@harbor ~]# mkdir -p /GuaZai/Iso
[root@harbor ~]# mount /dev/sr0 /GuaZai/Iso
mount: /GuaZai/Iso: WARNING: source write-protected, mounted read-only.
[root@harbor ~]# cd /GuaZai/Iso
[root@harbor Iso]# ls
AppStream BaseOS EFI EULA extra_files.json GPL images isolinux media.repo RPM-GPG-KEY-redhat-beta RPM-GPG-KEY-redhat-release
#配置yum文件
[root@harbor ~]# vim /etc/yum.repos.d/rhel9.repo
[BaseOS]
name=rhel9-BaseOS
baseurl=file:///GuaZai/Iso/BaseOS
gpgcheck=0
[Appstream]
name=rhel9-Appstream
baseurl=file:///GuaZai/Iso/AppStream
gpgcheck=0
# 查看仓库序列
[root@harbor ~]# yum repolist
# 生成yum缓存
[root@harbor ~]# yum makecache
3.2 主机名和IP地址映射
配置Harbor主机的主机名和IP地址映射,映射文件“/etc/hosts”加入如下内容
[root@harbor ~]# vim /etc/hosts
192.168.8.52 registry.luovip.cn
3.3 部署Docker CE
# 检测系统是否安装了docker并卸载旧版本的容器
[root@harbor ~]# sudo dnf remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine \
podman \
runc
# 安装依赖
[root@harbor ~]# sudo yum install -y yum-utils
[root@harbor ~]# sudo dnf -y install dnf-plugins-core
[root@harbor ~]# sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
# 安装docker
[root@harbor ~]# sudo sed -i 's+https://download.docker.com+https://mirror.nju.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
[root@harbor ~]# sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 查看docker状态
[root@harbor ~]# sudo systemctl enable --now docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.
[root@harbor ~]# sudo systemctl status docker
[root@harbor ~]# docker info
3.4 Docker镜像加速器
[root@harbor ~]# mkdir -p /etc/docker
[root@harbor ~]# cd /etc/docker
[root@harbor docker]# vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://mirror.ccs.tencentyun.com",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://mirror.gcr.io",
"https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com"
],
"data-root": "/data/docker" # 自定义Docker的镜像存储路径
}
[root@harbor ~]# mkdir -p /data/docker
[root@harbor ~]# cp -a /var/lib/docker/* /data/docker/
[root@harbor ~]# systemctl daemon-reload
[root@harbor ~]# systemctl restart docker
[root@harbor ~]# docker info
# 测试
[root@harbor ~]# docker pull nginx
[root@harbor ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a830707172e8 3 weeks ago 192MB
3.5 Compose支持
添加Compose支持,并启动Docker服务
# 下载docker-compose并放在/usr/local/bin下
curl -L "https://github.com/docker/compose/releases/download/v2.36.0/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
systemctl daemon-reload
systemctl restart docker
[root@harbor ~]# docker-compose version
Docker Compose version v2.36.0
# 注明:github可能会访问不了,故先从github下载到本地,再上传到服务器
https://github.com/docker/compose/releases
3.6 下载Harbor
[root@harbor ~]# wget https://github.com/goharbor/harbor/releases/download/v2.13.0/harbor-offline-installer-v2.13.0.tgz
[root@harbor ~]# tar xf harbor-offline-installer-v2.13.0.tgz -C /usr/local/bin
# 使用docker load命令将解压后的tar文件加载为Docker镜像
[root@harbor ~]# cd /usr/local/bin
[root@harbor bin]# ll
total 72004
-rwxr-xr-x. 1 root root 73731911 May 14 14:25 docker-compose
drwxr-xr-x. 2 root root 123 May 14 16:33 harbor
[root@harbor bin]# cd harbor
[root@harbor harbor]# docker load -i harbor.v2.13.0.tar.gz
3.7 修改harbor.yml文件
mv harbor.yml.tmpl harbor.yml
# 备份harbor.yml文件
cp harbor.yml harbor.yml.bak
[root@harbor ~]# vim /usr/local/bin/harbor/harbor.yml
1.修改hostname为192.168.8.52
2.修改http的端口为82
3.修改harbor_admin_password为admin
# 如果不启用https就注释掉12行-20行
3.8 安装Harbor
# 加载配置并安装
[root@harbor harbor]# ./prepare
[root@harbor harbor]# ./install.sh
...
[Step 5]: starting Harbor ...
[+] Running 10/10
✔ Network harbor_harbor Created 0.0s
✔ Container harbor-log Started 0.3s
✔ Container registryctl Started 0.8s
✔ Container harbor-db Started 1.2s
✔ Container redis Started 1.2s
✔ Container harbor-portal Started 1.2s
✔ Container registry Started 1.3s
✔ Container harbor-core Started 1.4s
✔ Container harbor-jobservice Started 2.0s
✔ Container nginx Started 2.0s
✔ ----Harbor has been installed and started successfully.----
3.9 重启Harbor
[root@harbor harbor]# docker-compose down
[root@harbor harbor]# ./prepare
[root@harbor harbor]# docker-compose up -d
# 浏览器访问Harbor http://192.168.8.52:82 用户名/密码:admin/admin
3.10 生成服务文件
cat > /etc/systemd/system/harbor.service <<EOF
[Unit]
Description=Harbor
After=docker.service systemd-networkd.service systemd-resolved.service
Requires=docker.service
Documentation=http://github.com/vmware/harbor
[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/local/bin/docker-compose -f /usr/local/bin/harbor/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f /usr/local/bin/harbor/docker-compose.yml down
[Install]
WantedBy=multi-user.target
EOF
[root@harbor ~]# systemctl daemon-reload
[root@harbor ~]# systemctl enable harbor --now
[root@harbor ~]# systemctl stop harbor
[root@harbor ~]# systemctl status harbor
[root@harbor ~]# systemctl start harbor
[root@harbor ~]# systemctl restart harbor
3.11 推送镜像到Harbor
将registry.luovip.cn以及其对应的IP添加到/etc/hosts,然后将上述实验中的httpd:v1镜像,改名为带上IP:PORT形式,上传的镜像到本地仓库
1.添加域名解析
[root@docker ~]# vim /etc/hosts
192.168.8.52 registry.luovip.cn
2.编辑文件“/usr/lib/systemd/system/docker.service”,输入以下内容。其中,my.harbor.com是Harbor主机的主机名
[root@docker ~]# vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd --insecure-registry registry.luovip.cn
3.编辑“/etc/docker/daemon.json”文件,在该文件中指定私有镜像仓库地址
[root@docker ~]# vim /etc/docker/daemon.json
"insecure-registries": [
"192.168.8.52:82"
]
[root@docker ~]# systemctl daemon-reload
[root@docker ~]# systemctl restart docker.service
4.推送的命令
# Docker推送命令:
1.在项目中标记镜像:
docker tag SOURCE_IMAGE[:TAG] 192.168.8.52:82/library/REPOSITORY[:TAG]
2.推送镜像到当前项目:
docker push 192.168.8.52:82/library/REPOSITORY[:TAG]
Podman推送命令:
1.推送镜像到当前项目:
podman push IMAGE_ID 192.168.8.52:82/library/REPOSITORY[:TAG]
5.推送镜像
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest c6c6349a7df2 47 hours ago 468MB
nginx latest a830707172e8 4 weeks ago 192MB
[root@docker ~]# docker login 192.168.8.52:82
[root@docker ~]# docker tag c6c6349a7df2 192.168.8.52:82/library/tomcat:v2
[root@docker ~]# docekr images
-bash: docekr: command not found
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.8.52:82/library/tomcat v2 c6c6349a7df2 47 hours ago 468MB
tomcat latest c6c6349a7df2 47 hours ago 468MB
nginx latest a830707172e8 4 weeks ago 192MB
[root@docker ~]# docker push 192.168.8.52:82/library/tomcat:v2
[root@docker ~]# docker pull 192.168.8.52:82/library/tomcat:v2
4 管理容器的资源
# 创建容器并观察内存量
[root@docker ~]# docker run -d --name=httpd_server httpd
[root@docker ~]# docker run -d --name=httpd_tomcat tomcat
[root@docker ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
796227a2aac7 tomcat "catalina.sh run" 2 minutes ago Up 2 minutes 8080/tcp httpd_tomcat
b025ca41d951 httpd "httpd-foreground" 3 minutes ago Up 3 minutes 80/tcp httpd_server
[root@docker ~]# docker exec -it httpd_server grep MemTotal /proc/meminfo
MemTotal: 3974748 kB
[root@docker ~]# docker exec -it httpd_tomcat grep MemTotal /proc/meminfo
MemTotal: 3974748 kB
4.1 容器的内存配额
根据以上得出结论,每个容器的内存量,全部等于物理宿主机的内存总量,这意味这更好的性能,但同时也意味着一旦业务需求上升,将有可能发生资源争用,这通常在运维规划时,应当极力避免
容器可使用的内存:物理内存和交换空间(Swap)
Docker默认没有设置内存限制。可以通过相关选项限制设置:
1.-m(–memory):设置容器可用的最大内存,该值最低为4MB
2.–memory-swap:允许容器置入磁盘交换空间中的内存大小
4.1.1 用户内存限制
Docker提供4种方式设置容器的用户内存使用:
1.对容器内存使用无限制(两个选项都不使用)
2.设置内存限制并取消交换空间内存限制
#使用300内存和尽可能多的交换空间
[root@docker ~]# docker run -it -m 300M --memory-swap -1 ubuntu /bin/bash
3.只设置内存限制
# 300MB的内存和300MB的交换空间
# 默认情况下虚拟内存总量将设置为内存大小的两倍,因此容器能使用300M的交换空间
[root@docker ~]# docker run -it -m 300M ubuntu /bin/bash
4.同时设置内存和交换空间
# 300MB的内存和700MB的交换空间
[root@docker ~]# docker run -it -m 300M --memory-swap 700m ubuntu /bin/bash
4.1.2 内核内存限制
内核内存不能交换到磁盘中,无法使用交换空间,消耗过多可能导致其阻塞系统服务
# 在500MB的内存中,可以使用最高50MB的内核内存
[root@docker ~]# docker run -it -m 500M --kernel-memory 50M ubuntu /bin/bash
# 只可以使用50MB的内核内存
[root@docker ~]# docker run -it --kernel-memory 50M ubuntu /bin/bash
4.1.3 内存预留实现软限制
使用–memory-reservation选项设置内存预留,它是一种内存软限制,允许更多的内存共享。设置后,Docker将检测内存争用或内存不足,并强制容器将其内存消耗限制为预留值
内存预留值应当始终低于硬限制,作为一个软限制功能,内存预留并不能保证不会超过限制
# 限制内存为500MB,内存预留值(软限制)为200MB。
# 当容器消耗内存大于200MB、小于500MB时,下一次系统内存回收将尝试将容器内存缩减到200MB以下
[root@docker ~]# docker run -it -m 500M --memory-reservation 200M ubuntu /bin/bash
docker run –it –m 500M --memory-reservation 200M ubuntu /bin/bash
# 设置内存软限制为1GB
[root@docker ~]# docker run -it —-memory-reservation 1G ubuntu /bin/bash
4.2 容器的CPU配额
默认情况下,所有容器都可以使用相同的CPU资源,并且没有任何限制,这和内存问题一样,一旦CPU需求业务上升,同样会引起CPU资源的争用,但是和内存指定绝对量的不同,CPU是通过指定相对权重值来进行的配额
使用–cpu-shares参数对CPU来进行配额分配,默认情况下,这个值为1024
当前容器中的业务空闲时,其他的容器有权利使用其空闲的CPU周期,这将确保业务的性能
CPU限额的分配,只有在物理机资源不足的时候才会生效,并且是根据不同的优先级进行的,当其他容器空闲时,忙碌的容器可以获得全部可用的CPU资源
4.2.1 CPU份额限制
-c(–cpu-shares)选项将CPU份额权重设置为指定的值
默认值为1024,如果设置为0,系统将忽略该值并使用默认值1024
4.2.2 CPU周期限制
–cpu-period选项(以μs为单位)设置CPU周期以限制容器CPU资源的使用
默认的CFS(完全公平调度器)周期为100ms(100000μs)
通常将–cpu-period与–cpu-quota这两个选项配合使用:
# 如果只有1个CPU,则容器可以每50ms(50000μs)获得50%(25000/50000)的CPU运行时间
[root@docker ~]# docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu /bin/bash
# 可用--cpus选项指定容器的可用CPU资源来达到同样的目的
# --cpus选项值是一个浮点数,默认值为0.000,表示不受限制
# 上述可改为
[root@docker ~]# docker run -it --cpus=0.5 ubuntu /bin/bash
# --cpu-period和--cpu-quota选项都是以1个CPU为基准
4.2.3 CPU放置限制
–cpuset-cpus选项限制容器进程在指定的CPU上执行
# 容器中的进程可以在cpu1和cpu3上执行
[root@docker ~]# docker run -it --cpuset-cpus="1,3" ubuntu:14.04 /bin/bash
# 容器中的进程可以在cpu0、cpu1和cpu 2上执行
[root@docker ~]# docker run -it --cpuset-cpus="0-2" ubuntu:14.04 /bin/bash
4.2.4 CPU配额限制
–cpu-quota选项限制容器的CPU配额,默认值为0表示容器占用100%的CPU资源个CPU
CFS用于处理进程执行的资源分配,是由内核使用的默认Linux调度程序。将此值设置50000意味着限制容器至多使用CPU资源的50%,对于多个CPU而言,调整–cpu-quota选项必要的
4.3 容器的I/O配额
默认情况下,所有容器都可以使用相同的I/O资源(500权重),并且没有任何限制,这和内存、 CPU问题一样,一旦I/O需求业务上升,硬盘读写会变得非常迟缓,所以为了更好的提供服务,也应该对容器使用硬盘方面进行调整
块I/O带宽(Block I/O Bandwidth,Blkio)是另一种可以限制容器使用的资源
块I/O指磁盘的写,Docker可通过设置权重限制每秒字节数(B/s)和每秒I/O次数(IO/s)的方式控制容器读写盘的带宽
4.3.1 设置块I/O权重
–blkio-weight选项更改比例(原默认为500),设置相对于所有其他正在运行的容器的块I/O带宽权重
# 创建两个有不同块I/O带宽权重的容器
[root@docker ~]# docker run -it --name c1 --blkio-weight 300 ubuntu /bin/bash
[root@docker ~]# docker run -it --name c2 --blkio-weight 600 ubuntu /bin/bash
在以下案例中,权重为600的容器将比300的在I/O能力方面多出两倍:
[root@docker ~]# docker run -d --name 300io --blkio-weight 300 httpd
[root@docker ~]# docker run -d --name 600io --blkio-weight 600 httpd
命令测试I/O性能:
[root@docker ~]# time dd if=/dev/zero of=test.out bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 4.05265 s, 265 MB/s
real 0m4.055s
user 0m0.000s
sys 0m4.036s
注:此设定在I/O争用时,才会体现
4.3.2 限制设备读写速率
Docker根据两类指标限制容器的设备读写速率:一类是每秒字节数,另一类是每秒I/O次数
1.限制每秒字节数
–device-read-bps选项限制指定设备的读取速率,即每秒读取的字节数
# 创建一个容器,并限制对/dev/mapper/rhel-swap设备的读取速率为每秒1MB
[root@docker ~]# docker run -it --device-read-bps /dev/mapper/rhel-swap:1mb ubuntu
docker run -it --device-read-bps /dev/sda:1mb ubuntu
# 类似地,可使用--device-write-bps选项限制指定设备的写入速率。格式: <设备>:<速率值>[单位]
2.限制每秒I/O次数
–device-read-iops和–device-write-iops选项制指定设备的读取和写入速率,用每秒I/O次数表示
# 创建一个容器,限制它对/dev/mapper/rhel-swap设备的读取速率为每秒1000次
[root@docker ~]# docker run -it --device-read-iops /dev/mapper/rhel-swap:1000 ubuntu
4.4 容器底层技术实现
对容器使用的内存、CPU和块I/O带宽资源的限制具体是由控制组(Cgroup)的相应子系统来实现的
1.memory子系统设置控制组中的住务所使用的内存限制
2.cpu子系统通过调度程序提供对CPU的控制组任务的访问
3.blkio子系统为块设备(如磁盘、固态硬盘、USB等)设置输入和输出限制
在docker run命令中使用–cpu-shares、–memory、–device-read-bps等选项实际上就是在配置控制组,相关的配置文件保存在/sys/fs/cgroup目录中
4.4.1 资源限制的底层实现
Linux通过cgroup来分配进程使用的CPU、memory、I/O资源的配额,可以通过/sys/fs/cgroup/下面的设定来查看容器的配额部分
# 启动一个容器,设置内存限额为300MB,CPU权重为512
[root@docker ~]# docker run --rm -d -p 8080:80 -m 300M --cpu-shares=512 httpd
1dc9a3907b6b82521addd810d52d2514c6ab5fed1e274f03a90e5a1454d16a49
# 动态更改容器的资源限制
# docker update命令可以动态地更新容器配置,其语法:docker update [选项] 容器 [容器...]
[root@docker ~]# docker update -m 500M --cpu-shares=10245 1dc9a3907b6b82521addd810d52d2514c6ab5fed1e274f03a90e5a1454d16a49
1dc9a3907b6b82521addd810d52d2514c6ab5fed1e274f03a90e5a1454d16a49
4.4.2 容器的隔离底层实现
每个容器貌似都有自己独立的根目录以及/etc、/var等目录,而且貌似都有自己的独立网卡,但事实上物理宿主机只有一个网卡,那么容器之间是怎么实现的“独立性”的呢?
Linux使用namespace技术来实现了容器间的资源隔离,namespace管理着宿主机中的全局唯一资源,并且可以让每个容器都会认为自己拥有且只有自己在使用资源,namespace一共有6种,分别为:mount、UTS、IPC、PID、Network、User
4.4.3 namespace
Mount namespace让容器看上去拥有整个文件系统,容器有自己的根目录
UTS namespace可以让容器有自己的主机名,默认情况下,容器的主机名是它本身的短ID,可通过-h或者–hostname设置主机名
IPC namespace可以让容器拥有自己的共享内存和信号量来实现进程间通信
PID namespace让容器拥有自己的进程树,可以在容器中执行ps命令查看
Network namespace可以让容器拥有自己独立的网卡、IP、路由等资源
User namespace 让容器能够管理自己的用户,而不是和宿主机公用/etc/passwd
5 容器原生网络与存储
5.1 容器原生网络
docker原生提供了以下几种网络,如果对原生网络不满意,还可以创建自定义网络
原生网络分为:none、bridge、host,这些网络在docker安装的时候会自动创建,可以通过以下命令来查看
[root@docker ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f85881372579 bridge bridge local
668aba04b5b0 host host local
3fa8ef65ab94 none null local
如果容器使用的是none网络,那么此容器将不具备常规理解上的网卡,只具备lo网络,如果要使用这个网络,在创建容器时,指定–network=none即可
None网络是比较封闭的网络,对一些安全要求比较高并且不需要联网的场景,可以用none网络,比如手机上接收的验证码、随机数生成等场景,就可以放在none网络中以避免被窃取
Host网络是一个共享宿主机网络栈的一个容器共享网络,可以通过–network=host在创建容器 的时候指定host网络,处于host网络模式的容器,网络配置和宿主机是完全一样的,也就是说,在容器中可以看到宿主机的所有网卡,并且主机名也是宿主机的,这最大的好处就是性能很高,传输速率特别好,但是宿主机上已经使用的端口,容器就不可以使用
5.2 容器和层
容器和镜像最大的不同在于最顶上的可写层,所有在容器中的数据写入和修改都会直接存储到这个可写层中,这就意味着,当容器被删除时,可写层中的数据就丢失了,虽然每个容器都有自己不同的可写层,但是容器底层的镜像却是可以同时共享的
5.3 主流存储驱动
在容器设计和使用的时候,在容器的可写层中写入的数据是非常少的,但在运维中大部分数据是必须要具备持久化保存的能力,所以在容器中引入了多种的存储驱动来解决上面说的可写层数据的易失性
目前主流受支持的存储驱动有:
5.4 Copy-on-write策略
5.5 容器数据管理
容器中持久化数据一般采用两种存储方式:
1.volume
2.bind mount
6 Kubernetes
6.1 K8S的概念
Kubernetes是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化
Kubernetes拥有一个庞大且快速增长的生态系统。Kubernetes 的服务、支持和工具广泛可用
6.2 K8S的特点
Kubernetes具有以下几个特点:
1.可移植: 支持公有云、私有云、混合云、多重云(multi-cloud)
2.可扩展: 模块化、插件化、可挂载、可组合
3.自动化: 自动部署、自动重启、自动复制、自动伸缩/扩展
6.3 K8S的作用
Kubernetes的主要职责是容器编排(Container Orchestration),即在一组服务器上启动、 监控、回收容器,在满足排程的同时,保证容器可以健康的运行
6.4 K8S的整体架构
6.4.1 Master节点
1.kube-apiserver
API服务器是Kubernetes控制面的前端
Kubernetes API服务器的主要实现是kube-apiserver
kube-apiserver设计上考虑了水平伸缩,可通过部署多个实例进行伸缩。 可以运行kube-apiserver的多个实例,并在这些实例之间平衡流
2.etcd
etcd是兼具一致性和高可用性的键值数据库,可以作为保存Kubernetes所有集群数据的后台数据库
3.cloud-controller-manager
cloud-controller-manager仅运行特定于云平台的控制回路
如果在自己的环境中运行Kubernetes,或者在本地计算机中运行学习环境, 所部署的环境中不需要云控制器管理器
4.kube-scheduler
控制平面组件,负责监视新创建的、未指定运行节点(node)的 Pods,选择节点让 Pod 在上面运行
5.kube-controller-manager
这些控制器包括:
节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应
任务控制器(Job controller): 监测代表一次性任务的Job对象,然后创建Pods来运行这些任务直至完成
端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入 Service 与 Pod)
服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和API访问令牌
6.4.2 Node节点
1.kubelet
一个在集群中每个节点(node)上运行的代理,保证容器(containers)都运行在Pod中
2.kube-proxy
kube-proxy是集群中每个节点上运行的网络代理, 实现Kubernetes服务(Service)概念的一部分
kube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与Pod进行网络通信
如果操作系统提供了数据包过滤层并可用的话,kube-proxy会通过它来实现网络规则。否则, kube-proxy仅转发流量本身
3.容器运行时(Container Runtime)
Kubernetes支持多个容器运行环境: Docker、 containerd、CRI-O以及任何实现Kubernetes CRI (容器运行环境接口)
6.4.3 插件(Addons)
插件使用Kubernetes资源(DaemonSet、 Deployment等)实现集群功能。 因为这些插件提供集群级别的功能,插件中命名空间域的资源属于kube-system命名空间
1.Core-dns:为整个集群提供DNS服务
2.Ingress Controller:为service提供外网访问入口
3.Dashboard: 提供图形化管理界面
4.Flannel/ Calico :为kubernetes提供方便的网络规划服务
7 Kubernetes集群部署
7.1 Kubernetes的安装流程
7.1.1 先决条件
1.最小配置:2G内存2核CPU
2.集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)
3.节点之中不可以有重复的主机名、MAC 地址或product_uuid
4.禁用交换分区
5.开启机器上的某些端口
7.1.2 安装runtime
默认情况下,Kubernetes使用容器运行时接口(Container Runtime Interface,CRI) 与所选择的容器运行时交互
如果不指定运行时,则kubeadm会自动尝试检测到系统上已经安装的运行时, 方法是扫描一组众所周知的Unix域套接字,docker启用shim来对接K8S
运行时的域套接字:
Docker unix:///var/run/cri-dockerd.sock
containerd /run/containerd/containerd.sock
CRI-O /var/run/crio/crio.sock
7.1.3 安装kubeadm、kubelet和kubectl
需要在每台机器上安装以下软件包:
kubeadm:用来初始化集群的指令
kubelet:在集群中的每个节点上用来启动Pod和容器等
kubectl:用来与集群通信的命令行工具
确保它们与通过kubeadm安装的控制平面的版本相匹配。 不然可能会导致一些预料之外的错误和问题。 然而,控制平面与kubelet间的相差一个次要版本不一致是支持的,但kubelet的版本不可以超过API服务器的版本。 例如,1.7.0 版本的kubelet可以完全兼容1.8.0版本的API 服务器,反之则不可以
7.1.4 检查所需端口
1.控制平面
协议 | 方向 | 端口范围 | 作用 | 使用者 |
---|---|---|---|---|
TCP | 入站 | 6443 | Kubernetes API服务器 | 所有组件 |
TCP | 入站 | 2379-2380 | etcd服务器客户端API | kube-apiserver,etcd |
TCP | 入站 | 10250 | Kubelet API | kubelet自身、控制平面组件 |
TCP | 入站 | 10251 | kube-scheduler | kube-scheduler自身 |
TCP | 入站 | 10252 | kube-controller-manager | kube-controller-manager自身 |
2.工作节点
协议 | 方向 | 端口范围 | 作用 | 使用者 |
---|---|---|---|---|
TCP | 入站 | 10250 | Kubelet API | kubelet自身、控制平面组件 |
TCP | 入站 | 30000-32767 | NodePort服务 | 所有组件 |
7.1.5 Iptables桥接流量
为了让Linux节点上的iptables能够正确地查看桥接流量,需要确保sysctl配置中将net.bridge.bridge-nf-call-iptables设置为1
7.1.6 环境准备
本K8S集群使用3台机器(ubuntu)进行部署,各节点信息如下表:
注明:使用的容器为Docker
主机名 | 角色 | IP | VMware网络类型 | 用户名 | 密码 | 互联网连接 |
---|---|---|---|---|---|---|
k8s-master | 控制平面 | 192.168.8.3 | NAT | vagrant root | vagrant vargrant | 是 |
k8s-worker1 | 数据平面 | 192.168.8.4 | NAT | vagrant root | vagrant vargrant | 是 |
k8s-worker2 | 数据平面 | 192.168.8.5 | NAT | vagrant root | vagrant vargrant | 是 |
准备DNS解析:
这一步需要在所有机器上完成
# 这一步需要在所有机器上完成
cat >> /etc/hosts <<EOF
192.168.8.3 k8s-master
192.168.8.4 k8s-worker1
192.168.8.5 k8s-worker2
192.168.30.133 registry.xiaohui.cn
EOF
7.2 Docker CE 部署
7.2.1 添加Docker仓库
这一步要在所有机器上完成:
# 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 添加公钥到系统
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://mirrors.nju.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 添加仓库到系统
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.nju.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 判断仓库是否已做好
sudo apt-get update
7.2.2 安装Docker CE
这一步要在所有机器上完成:
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 部署完Docker CE之后,还需要cri-docker shim才可以和Kubernetes集成
7.2.3 CRI-Docker部署
这一步要在所有机器上完成:
# 下载cri-docker
wget http://hub.gitmirror.com/https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.17/cri-dockerd_0.3.17.3-0.ubuntu-jammy_amd64.deb
# 安装cri-docker
dpkg -i cri-dockerd_0.3.17.3-0.ubuntu-jammy_amd64.deb
7.2.4 Docker镜像加速器
这一步要在所有机器上完成:
vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://mirror.ccs.tencentyun.com",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://mirror.gcr.io",
"https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com"
],
"exec-opts": ["native.cgroupdriver=systemd"]
}
systemctl daemon-reload
systemctl restart docker
7.2.5 将镜像指引到国内
这一步要在所有机器上完成:
cp /lib/systemd/system/cri-docker.service /etc/systemd/system/cri-docker.service
sed -i 's/ExecStart=.*/ExecStart=\/usr\/bin\/cri-dockerd --container-runtime-endpoint fd:\/\/ --network-plugin=cni --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com\/google_containers\/pause:3.10/' /etc/systemd/system/cri-docker.service
systemctl daemon-reload
systemctl restart cri-docker.service
systemctl enable cri-docker.service
7.3 Kubernetes部署
7.3.1 关闭swap分区
这一步要在所有机器上完成:
# 实时关闭
swapoff -a
# 永久关闭
sed -i 's/.*swap.*/#&/' /etc/fstab
7.3.2 允许iptables检查桥接流量
这一步要在所有机器上完成:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
7.3.3 安装kubeadm
这一步要在所有机器上完成:
# 安装依赖
apt-get update && apt-get install -y apt-transport-https curl
# 安装K8S软件包仓库-阿里云
cat > /etc/apt/sources.list.d/k8s.list <<EOF
deb https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.32/deb /
EOF
# 安装软件包仓库的公钥
curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.32/deb/Release.key | apt-key add -
# 更新软件包的仓库索引
apt-get update
# 开始安装
apt-get install -y kubelet kubeadm kubectl
# 操作系统所有软件包升级时将忽略kubelet、kubeadm、kubectl
apt-mark hold kubelet kubeadm kubectl
7.3.4 添加命令自动补齐
这一步要在所有机器上完成:
kubectl completion bash > /etc/bash_completion.d/kubectl
kubeadm completion bash > /etc/bash_completion.d/kubeadm
source /etc/bash_completion.d/kubectl
source /etc/bash_completion.d/kubeadm
7.3.5 集成CRI-Docker
这一步要在所有机器上完成:
crictl config --set runtime-endpoint unix:///run/cri-dockerd.sock
crictl images
7.3.6 集群部署
kubeadm.yaml中name字段必须在网络中可被解析,也可以将解析记录添加到集群中所有机器的/etc/hosts中
初始化集群部署的操作只能在k8s-master上执行
# 初始化配置
kubeadm config print init-defaults > kubeadm.yaml
sed -i 's/.*advert.*/ advertiseAddress: 192.168.8.3/g' kubeadm.yaml
sed -i 's/.*name.*/ name: k8s-master/g' kubeadm.yaml
sed -i 's|imageRepo.*|imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers|g' kubeadm.yaml
sed -i "/^\\s*networking:/a\\ podSubnet: 172.16.0.0/16" kubeadm.yaml
# 注意下面的替换,只有在集成的是CRI-Docker时才需要执行,Containerd不需要
sed -i 's/ criSocket.*/ criSocket: unix:\/\/\/run\/cri-dockerd.sock/' kubeadm.yaml
# 模块加载
modprobe br_netfilter
# 集群初始化
kubeadm init --config kubeadm.yaml
Your Kubernetes control-plane has initialized successfully!
......
kubeadm join 192.168.8.3:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:c2546a856290440a8ccaf9223c14fd1c2098ac74f4a584acf5f3c5a373005207
# 授权管理权限
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 查看集群状态
root@k8s-master:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master NotReady control-plane 62m v1.32.5
7.3.7 部署Calico网络插件
Calico网络插件部署的操作在所有节点上执行
# 使用operator安装calico组件-可能会失败
# 以下为github的地址,可能会失败
root@k8s-master:~# kubectl create -f https://raw.gitmirror.com/projectcalico/calico/refs/tags/v3.29.3/manifests/tigera-operator.yaml
# 解决办法:
# 1.获取Calico images到本地
见Calico.txt
# 2.发布本地的yaml到集群-master
kubectl create -f https://www.linuxcenter.cn/files/cka/cka-yaml/tigera-operator-calico-3.29.3.yaml
root@k8s-master:~# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-76fccbbb6b-l7jq9 0/1 Pending 0 163m
kube-system coredns-76fccbbb6b-nd68g 0/1 Pending 0 163m
kube-system etcd-k8s-master 1/1 Running 0 163m
kube-system kube-apiserver-k8s-master 1/1 Running 0 163m
kube-system kube-controller-manager-k8s-master 1/1 Running 0 163m
kube-system kube-proxy-mcwv7 1/1 Running 0 163m
kube-system kube-scheduler-k8s-master 1/1 Running 0 163m
tigera-operator tigera-operator-75b4cd596c-9hjml 1/1 Running 0 7m5s
7.3.8 设置calico在集群的网段
这一步在k8s-master上执行
# 使用下面的自定义资源设置一下calico在集群中的网段
# 以下为github的地址,可能会失败
root@k8s-master:~# wget https://raw.gitmirror.com/projectcalico/calico/refs/tags/v3.29.3/manifests/custom-resources.yaml
# 3.使用下面的地址执行
root@k8s-master:~# wget https://www.linuxcenter.cn/files/cka/cka-yaml/custom-resources-calico-3.29.3.yaml
root@k8s-master:~# mv custom-resources-calico-3.29.3.yaml custom-resources.yaml
7.3.9 确认资源的地址
这一步在k8s-master上执行
root@k8s-master:~# vim custom-resources.yaml
# This section includes base Calico installation configuration.
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.Installation
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configures Calico networking.
calicoNetwork:
ipPools:
- name: default-ipv4-ippool
blockSize: 26
cidr: 172.16.0.0/16 #这里换成上面规定好的172.16.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
---
# This section configures the Calico API server.
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.APIServer
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
name: default
spec: {}
7.3.10 自定义资源发布到集群
root@k8s-master:~# kubectl apply -f custom-resources.yaml
root@k8s-master:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 173m v1.32.5
root@k8s-master:~# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
calico-apiserver calico-apiserver-6499c768c8-wvrnt 1/1 Running 0 60s
calico-apiserver calico-apiserver-6499c768c8-zmvh6 1/1 Running 0 60s
calico-system calico-kube-controllers-85fb6564b7-gtsfr 1/1 Running 0 60s
calico-system calico-node-4mqfj 1/1 Running 0 60s
calico-system calico-typha-65d47d7478-ttzx6 1/1 Running 0 60s
calico-system csi-node-driver-7j8pf 2/2 Running 0 60s
kube-system coredns-76fccbbb6b-l7jq9 1/1 Running 0 172m
kube-system coredns-76fccbbb6b-nd68g 1/1 Running 0 172m
kube-system etcd-k8s-master 1/1 Running 0 172m
kube-system kube-apiserver-k8s-master 1/1 Running 0 172m
kube-system kube-controller-manager-k8s-master 1/1 Running 0 172m
kube-system kube-proxy-mcwv7 1/1 Running 0 172m
kube-system kube-scheduler-k8s-master 1/1 Running 0 172m
tigera-operator tigera-operator-75b4cd596c-9hjml 1/1 Running 0 16m
7.3.11 加入Worker节点
加入节点操作需在所有的worker节点完成,这里要注意,Worker节点需要完成以下先决条件才能执行kubeadm join
1.Docker、CRI-Docker 部署
2.Swap分区关闭
3.iptables桥接流量的允许
4.安装kubeadm等软件
5.集成CRI-Docker
6.所有节点的/etc/hosts中互相添加对方的解析
如果时间长忘记了join参数,可以在master节点上用以下方法重新生成
root@k8s-master:~# kubeadm token create --print-join-command
kubeadm join 192.168.8.3:6443 --token 5mffg7.lq7ujh6vot0jzrci --discovery-token-ca-cert-hash sha256:c2546a856290440a8ccaf9223c14fd1c2098ac74f4a584acf5f3c5a373005207
如果有多个CRI对象,在worker节点上执行以下命令加入节点时,指定CRI对象,案例如下:
root@k8s-worker1:~# kubeadm token create --print-join-command
kubeadm join 192.168.8.3:6443 --token 5mffg7.lq7ujh6vot0jzrci --discovery-token-ca-cert-hash sha256:c2546a856290440a8ccaf9223c14fd1c2098ac74f4a584acf5f3c5a373005207
failed to load admin kubeconfig: open /root/.kube/config: no such file or directory
To see the stack trace of this error execute with --v=5 or higher
found multiple CRI endpoints on the host. Please define which one do you wish to use by setting the 'criSocket' field in the kubeadm configuration file: unix:///var/run/containerd/containerd.sock, unix:///var/run/cri-dockerd.sock
To see the stack trace of this error execute with --v=5 or higher
# 加入两个节点
1.节点worker1
root@k8s-worker1:~# kubeadm join 192.168.8.3:6443 --token 5mffg7.lq7ujh6vot0jzrci --discovery-token-ca-cert-hash sha256:c2546a856290440a8ccaf9223c14fd1c2098ac74f4a584acf5f3c5a373005207 --cri-socket=unix:///var/run/cri-dockerd.sock
2.节点worker2
root@k8s-worker2:~# kubeadm join 192.168.8.3:6443 --token 5mffg7.lq7ujh6vot0jzrci --discovery-token-ca-cert-hash sha256:c2546a856290440a8ccaf9223c14fd1c2098ac74f4a584acf5f3c5a373005207 --cri-socket=unix:///var/run/cri-dockerd.sock
3.查看各节点状态
root@k8s-master:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 3h28m v1.32.5
k8s-worker1 Ready <none> 2m2s v1.32.5
k8s-worker2 Ready <none> 2m2s v1.32.5
4.查看pod信息
root@k8s-master:~# kubectl get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
calico-apiserver calico-apiserver-6499c768c8-wvrnt 1/1 Running 0 37m
calico-apiserver calico-apiserver-6499c768c8-zmvh6 1/1 Running 0 37m
calico-system calico-kube-controllers-85fb6564b7-gtsfr 1/1 Running 0 37m
calico-system calico-node-4mqfj 1/1 Running 0 37m
calico-system calico-node-rkd6k 1/1 Running 0 3m37s
calico-system calico-node-vxflh 1/1 Running 0 3m37s
calico-system calico-typha-65d47d7478-cmrtt 1/1 Running 0 3m28s
calico-system calico-typha-65d47d7478-ttzx6 1/1 Running 0 37m
calico-system csi-node-driver-7j8pf 2/2 Running 0 37m
calico-system csi-node-driver-nhg4c 2/2 Running 0 3m37s
calico-system csi-node-driver-z6p7p 2/2 Running 0 3m37s
kube-system coredns-76fccbbb6b-l7jq9 1/1 Running 0 3h29m
kube-system coredns-76fccbbb6b-nd68g 1/1 Running 0 3h29m
kube-system etcd-k8s-master 1/1 Running 0 3h29m
kube-system kube-apiserver-k8s-master 1/1 Running 0 3h29m
kube-system kube-controller-manager-k8s-master 1/1 Running 0 3h29m
kube-system kube-proxy-8n6x5 1/1 Running 0 3m37s
kube-system kube-proxy-mcwv7 1/1 Running 0 3h29m
kube-system kube-proxy-xk4h4 1/1 Running 0 3m37s
kube-system kube-scheduler-k8s-master 1/1 Running 0 3h29m
tigera-operator tigera-operator-75b4cd596c-9hjml 1/1 Running 0 52m
注意上描述命令最后的–cri-socket参数,在系统中部署了docker和cri-docker时,必须明确指明此参数,并将此参数指向我们的cri-docker,不然命令会报告有两个重复的CRI的错误
在k8s-master机器上执行以下内容给节点打上角色标签,k8s-worker1和k8s-worker2分别打上了worker1和worker2的标签
root@k8s-master:~# kubectl label nodes k8s-worker1 node-role.kubernetes.io/worker1=
node/k8s-worker1 labeled
root@k8s-master:~# kubectl label nodes k8s-worker2 node-role.kubernetes.io/worker2=
node/k8s-worker2 labeled
root@k8s-master:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 3h33m v1.32.5
k8s-worker1 Ready worker1 7m37s v1.32.5
k8s-worker2 Ready worker2 7m37s v1.32.5
7.3.12 重置集群
如果在安装好集群的情况下,想重复练习初始化集群,或者包括初始化集群报错在内的任何原因,想重新初始化集群时,可以用下面的方法重置集群,重置后,集群就会被删除,可以用于重新部署,一般来说,这个命令仅用于k8s-master这个节点
root@k8s-master:~# kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock
# 根据提示,手工完成文件和规则的清理 清理后就可以重新部署集群了
root@k8s-master:~# rm -rf /etc/cni/net.d
root@k8s-master:~# iptables -F
root@k8s-master:~# rm -rf $HOME/.kube/config
7.3.13 标签和注解
标签(Labels)和注解(Annotations)是附加到Kubernetes 对象(比如Pods)上的键值对
标签旨在用于指定对用户有意义的标识属性,但不直接对核心系统有语义含义。可以用来选择对象和查找满足某些条件的对象集合
注解不用于标识和选择对象。有效的注解键分为两部分: 可选的前缀和名称,以斜杠(/)分隔。 名称段是必需项,并且必须在63个字符以内
root@k8s-master:~# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane 4h11m v1.32.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-worker1 Ready worker1 45m v1.32.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-worker1,kubernetes.io/os=linux,node-role.kubernetes.io/worker1=
k8s-worker2 Ready worker2 45m v1.32.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-worker2,kubernetes.io/os=linux,node-role.kubernetes.io/worker2=
root@k8s-master:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 4h11m v1.32.5
k8s-worker1 Ready worker1 45m v1.32.5
k8s-worker2 Ready worker2 45m v1.32.5
8 Kubernetes的语法
kubectl [command] [TYPE] [NAME] [flags]
command:指定要对一个或多个资源执行的操作,例如create、get、describe、delete
TYPE:指定资源类型,资源类型不区分大小写,可以指定单数、复数或缩写形式
NAME:指定资源的名称,名称区分大小写
fags:指定可选的参数。例如,可以使用-s或-server参数指定Kubernetes API服务器的地址和端口
8.1 Yaml语法
注意每个层级之间的点(.),在YAML文件中,每个层级之间一般用两个空格来表
root@k8s-master:~# kubectl explain Pod.metadata
KIND: Pod
VERSION: v1
FIELD: metadata <ObjectMeta>
...
8.1.1 生成YAML文件框架
通过在创建资源时加上—dry-run=client –o yaml来生成YAML文件框架,可以用重定向到文件的方式生成文件,只需要稍作修改即可
root@k8s-master:~# kubectl create deployment --image httpd deployname --dry-run=client -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: deployname
name: deployname
spec:
replicas: 1
selector:
matchLabels:
app: deployname
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: deployname
spec:
containers:
- image: httpd
name: httpd
resources: {}
status: {}
# 重定向到文件
root@k8s-master:~# kubectl create deployment --image httpd deployname --dry-run=client -o yaml > k8s.yml
8.1.2 apiVersion
Alpha:
1.版本名称包含了alpha
2.可能是有缺陷的。启用该功能可能会带来问题,默认是关闭的
3.支持的功能可能在没有通知的情况下随时删除
4.API的更改可能会带来兼容性问题,但是在后续的软件发布中不会有任何通知
5.由于bugs风险的增加和缺乏长期的支持,推荐在短暂的集群测试中使用。
Beta:
1.版本名称包含了beta
2.代码已经测试过。启用该功能被 认为是安全的,功能默认已启用
3.所有已支持的功能不会被删除,细节可能会发生变化
4.对象的模式和/或语义可能会在后续的beta测试版或稳定版中以不兼容的方式进行更改。
5.建议仅用于非业务关键型用途,因为后续版本中可能存在不兼容的更改。 如果有多个可以独立升级的集群,则可以放宽此限制
Stable:
1.版本名称是 vX,其中X是整数
2.功能的稳定版本将出现在许多后续版本的发行软件中
3.有时候也会被称为GA或者毕业等词汇
root@k8s-master:~# kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
bindings v1 true Binding
componentstatuses cs v1 false ComponentStatus
configmaps cm v1 true ConfigMap
endpoints ep v1 true Endpoints
events ev v1 true Event
limitranges limits v1 true LimitRange
namespaces ns v1 false Namespace
nodes no v1 false Node
...
# 可以使用单数、复数、缩写
root@k8s-master:~# kubectl get configmaps
NAME DATA AGE
kube-root-ca.crt 1 5h17m
root@k8s-master:~# kubectl get cm
NAME DATA AGE
kube-root-ca.crt 1 5h17m
root@k8s-master:~# kubectl get configmap
NAME DATA AGE
kube-root-ca.crt 1 5h17m
8.2 Namespace
Kubernetes支持多个虚拟集群,它们底层依赖于同一个物理集群。 这些虚拟集群被称为命名空间,它适用于存在很多跨多个团队或项目的用户的场景,命名空间为名称提供了一个范围
资源的名称需要在名字空间内是唯一的,但不能跨名字空间
名字空间不能相互嵌套,每个Kubernetes资源只能在一个名字空间中
命名空间是在多个用户之间通过资源配额划分集群资源的一种方法
root@k8s-master:~# kubectl get namespace
NAME STATUS AGE
calico-apiserver Active 3h18m
calico-system Active 3h18m
default Active 6h10m
kube-node-lease Active 6h10m
kube-public Active 6h10m
kube-system Active 6h10m
tigera-operator Active 3h33m
root@k8s-master:~# kubectl get pod
No resources found in default namespace.
root@k8s-master:~# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-76fccbbb6b-l7jq9 1/1 Running 0 6h14m
coredns-76fccbbb6b-nd68g 1/1 Running 0 6h14m
etcd-k8s-master 1/1 Running 0 6h15m
kube-apiserver-k8s-master 1/1 Running 0 6h14m
kube-controller-manager-k8s-master 1/1 Running 0 6h15m
kube-proxy-8n6x5 1/1 Running 0 169m
kube-proxy-mcwv7 1/1 Running 0 6h14m
kube-proxy-xk4h4 1/1 Running 0 169m
kube-scheduler-k8s-master 1/1 Running 0 6h15m
8.2.1 命令行创建
root@k8s-master:~# kubectl create namespace luovip
namespace/luovip created
root@k8s-master:~# kubectl get namespace
NAME STATUS AGE
calico-apiserver Active 3h26m
calico-system Active 3h26m
default Active 6h18m
kube-node-lease Active 6h18m
kube-public Active 6h18m
kube-system Active 6h18m
luovip Active 7s
tigera-operator Active 3h41m
8.2.2 YAML文件创建
cat > namespace.yml <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: luovipyu
EOF
root@k8s-master:~# kubectl create -f namespace.yml
namespace/luovipyu created
root@k8s-master:~# kubectl get namespace
NAME STATUS AGE
calico-apiserver Active 3h33m
calico-system Active 3h33m
default Active 6h25m
kube-node-lease Active 6h25m
kube-public Active 6h25m
kube-system Active 6h25m
luovip Active 6m54s
luovipyu Active 26s
tigera-operator Active 3h48m
8.2.3 删除namespace
root@k8s-master:~# kubectl delete namespace luovipyu # 会删除名字空间下的所有内容
namespace "luovipyu" deleted
root@k8s-master:~# kubectl create -f namespace.yml
namespace/luovipyu created
root@k8s-master:~# cat namespace.yml
apiVersion: v1
kind: Namespace
metadata:
name: luovipyu
root@k8s-master:~# kubectl get namespace
NAME STATUS AGE
calico-apiserver Active 3h39m
calico-system Active 3h39m
default Active 6h30m
kube-node-lease Active 6h30m
kube-public Active 6h30m
kube-system Active 6h30m
luovip Active 12m
luovipyu Active 13s
tigera-operator Active 3h54m
8.2.4 创建带有namespace属性的资源
root@k8s-master:~# kubectl run httpd --image=httpd --namespace=luovipyu
pod/httpd created
root@k8s-master:~# kubectl get pod -n luovipyu
NAME READY STATUS RESTARTS AGE
httpd 1/1 Running 0 18s
nginx 0/1 ImagePullBackOff 0 106s
# 每次查询和创建资源都需要带–namespace=luovipyu挺麻烦,可以设置默认值
root@k8s-master:~# kubectl config set-context --current --namespace=luovipyu
Context "kubernetes-admin@kubernetes" modified.
root@k8s-master:~# kubectl config view | grep namespace
namespace: luovipyu
root@k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
httpd 1/1 Running 0 3m3s
nginx 0/1 ImagePullBackOff 0 4m31s
# 删除namespace会删除其下所有资源,但如果要删除已经切换为默认值的namespace时,可能会卡住,所以先把默认值切换为其他,然后再删除
root@k8s-master:~# kubectl config set-context --current --namespace=default
Context "kubernetes-admin@kubernetes" modified.
root@k8s-master:~# kubectl delete namespaces luovip luovipyu
namespace "luovip" deleted
namespace "luovipyu" deleted
root@k8s-master:~# kubectl get namespace
NAME STATUS AGE
calico-apiserver Active 3h49m
calico-system Active 3h49m
default Active 6h41m
kube-node-lease Active 6h41m
kube-public Active 6h41m
kube-system Active 6h41m
tigera-operator Active 4h4m
8.3 CRD自定义资源
CRD(Custom Resource Definition,自定义资源定义)是Kubernetes提供的一种扩展机制,允许用户通过YAML文件定义自定义资源类型,并将其注册到Kubernetes API中,使其与内置资源(如Pod、 Deployment)一样被管理
本质:CRD是对自定义资源的元数据描述,定义了资源的名称、结构、版本、作用域等
作用:扩展Kubernetes API,支持用户自定义资源的管理和自动化操作
CRD核心字段:
字段 | 说明 | 示例 |
---|---|---|
apiVersion | CRD的API版本,固定为apiextensions.k8s.io/v1 | apiVersion:apiextensions.k8s.io/v1 |
kind | 资源类型,固定为CustomResourceDefinition | kind: CustomResourceDefinition |
metadata | 元数据,如名称、命名空间等(名称需符合DNS子域名规则) | name:crontabs.stable.example.com |
spec | 核心配置,包括 API组、版本、资源范围 (Namespaced/Cluster)、字段验证规则等 | group: stable.example.com |
versions | 支持的API版本列表,需指定至少一个存储版本( storage:true) | version:[v1][@ref) |
names | 资源的复数形式、单数形式、简称等(如plural:crontabs) | plural: crontabs |
scope | 资源作用域,Namespaced(命名空间级别)或Cluster(集群级别) | scope: Namespaced |
8.3.1 CRD介绍
K8S资源类型不止有namespace,还有很多,不过那都是系统自带的,现在我们来看看怎么自定义k8s中的资源
1.什么是CRD?
CRD(Custom Resource Definition)是 Kubernetes 提供的一种机制,允许用户定义自己的资源类型
这些自定义资源可以像 Kubernetes 原生资源(如 Pod、Service、Deployment 等)一样被管理。
2.为什么需要CRD?
扩展 Kubernetes API:Kubernetes 的原生资源可能无法满足所有用户的需求。CRD 允许用户定义自己的资源类型,从而扩展 Kubernetes 的功能。
管理复杂应用:有些应用可能需要管理一些特定的资源,这些资源不属于Kubernetes原生支持的范围。通过CRD可以将这些资源纳入 Kubernetes的管理范围,实现统一的资源管理
3.CRD的作用
定义资源结构:CRD 允许你定义资源的结构,包括其字段和数据类型
管理资源生命周期:Kubernetes 将为你管理这些自定义资源的生命周期,包括创建、更新、删除等操作
集成 Kubernetes 生态系统:CRD 可以与 Kubernetes 的其他组件(如控制器、操作符等)集成,实现更复杂的业务逻辑
在Kubernetes 的自定义资源定义(CRD)中,CRD 本身只定义了资源的结构和 API,但它不会直接执行任何创建、更新或删除操作。这些操作需要通过一个控制器(Controller)来实现。控制器是一个独立的程序,它监听 CRD 的变化,并根据这些变化执行实际的操作
8.3.2 查询CRD以及API资源
1.先看看系统中的api资源都有哪些,然后创建一个
root@k8s-master:~# kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
bindings v1 true Binding
componentstatuses cs v1 false ComponentStatus
configmaps cm v1 true ConfigMap
endpoints ep v1 true Endpoints
events ev v1 true Event
limitranges limits v1 true LimitRange
namespaces ns v1 false Namespace
nodes no v1 false Node
...
2.查看现在都有哪些自定义资源
# 以下资源不属于K8s,但是k8s是有的
root@k8s-master:~# kubectl get crd
NAME CREATED AT
adminnetworkpolicies.policy.networking.k8s.io 2025-05-17T03:05:26Z
apiservers.operator.tigera.io 2025-05-17T03:05:26Z
bgpconfigurations.crd.projectcalico.org 2025-05-17T03:05:26Z
bgpfilters.crd.projectcalico.org 2025-05-17T03:05:26Z
bgppeers.crd.projectcalico.org 2025-05-17T03:05:26Z
blockaffinities.crd.projectcalico.org 2025-05-17T03:05:26Z
caliconodestatuses.crd.projectcalico.org 2025-05-17T03:05:26Z
clusterinformations.crd.projectcalico.org 2025-05-17T03:05:26Z
felixconfigurations.crd.projectcalico.org 2025-05-17T03:05:26Z
globalnetworkpolicies.crd.projectcalico.org 2025-05-17T03:05:26Z
globalnetworksets.crd.projectcalico.org 2025-05-17T03:05:26Z
hostendpoints.crd.projectcalico.org 2025-05-17T03:05:26Z
imagesets.operator.tigera.io 2025-05-17T03:05:26Z
installations.operator.tigera.io 2025-05-17T03:05:26Z
ipamblocks.crd.projectcalico.org 2025-05-17T03:05:26Z
ipamconfigs.crd.projectcalico.org 2025-05-17T03:05:26Z
ipamhandles.crd.projectcalico.org 2025-05-17T03:05:26Z
ippools.crd.projectcalico.org 2025-05-17T03:05:26Z
ipreservations.crd.projectcalico.org 2025-05-17T03:05:26Z
kubecontrollersconfigurations.crd.projectcalico.org 2025-05-17T03:05:26Z
networkpolicies.crd.projectcalico.org 2025-05-17T03:05:26Z
networksets.crd.projectcalico.org 2025-05-17T03:05:26Z
tiers.crd.projectcalico.org 2025-05-17T03:05:26Z
tigerastatuses.operator.tigera.io 2025-05-17T03:05:26Z
8.3.3 创建CRD以及API资源
1.创建一个自己的crd,crd将注册为api资源
cat > crd.yaml <<-'EOF'
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'
name: crontabs.stable.example.com
spec:
# 组名称,用于 REST API:/apis/<组>/<版本>
group: stable.example.com
# 列举此 CustomResourceDefinition 所支持的版本
versions:
- name: v1
# 每个版本都可以通过 served 标志来独立启用或禁止
served: true
# 其中一个且只有一个版本必需被标记为存储版本
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
# 可以是 Namespaced 或 Cluster
scope: Namespaced
names:
# 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
plural: crontabs
# 名称的单数形式,作为命令行使用时和显示时的别名
singular: crontab
# kind 通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式。
kind: CronTab
# shortNames 允许你在命令行使用较短的字符串来匹配资源
shortNames:
- ct
EOF
root@k8s-master:~# kubectl apply -f crd.yaml
customresourcedefinition.apiextensions.k8s.io/crontabs.stable.example.com created
2.再看就会有自己的crd资源和api资源了
root@k8s-master:~# kubectl get crd
NAME CREATED AT
...
crontabs.stable.example.com 2025-05-17T06:16:04Z
...
root@k8s-master:~# kubectl api-resources | grep crontabs
NAME SHORTNAMES APIVERSION NAMESPACED KIND
crontabs ct stable.example.com/v1 true CronTab
root@k8s-master:~# kubectl describe crd crontabs.stable.example.com
8.3.4 查询API资源结构与参数
既然已经注册为api资源,来看看能否explain字段?
root@k8s-master:~# kubectl explain crontabs
GROUP: stable.example.com
KIND: CronTab
VERSION: v1
DESCRIPTION:
<empty>
FIELDS:
apiVersion <string>
kind <string>
metadata <ObjectMeta>
spec <Object>
...
# 查看有哪些spec
root@k8s-master:~# kubectl explain crontabs.spec
GROUP: stable.example.com
KIND: CronTab
VERSION: v1
FIELD: spec <Object>
DESCRIPTION:
<empty>
FIELDS:
cronSpec <string>
<no description>
image <string>
<no description>
replicas <integer>
<no description>
# 一切正常,看来已经创建了自定义资源,接下来就是等开发人员通过编程等方式创建operator等控制器,来使用我们的资源了
9 Pod
9.1 关于pod
Pod由一个或多个紧密耦合的容器组成
它们之间共享网络、存储等资源
pod是Kubernetes中最小的工作单元
Pod中的容器会一起启动和停止
9.2 Pod生命周期
Pod遵循一个预定义的生命周期,起始于Pending阶段,如果至少其中有一个主要容器正常启动,则进入Running,之后取决于Pod中是否有容器以失败状态结束而进入Succeeded或者Failed阶段。但有时集群节点之间出现网络故障,无法获取Pod状态时,就会出现Unknown状态
9.3 创建Pod
1.一个Pod中只有一个业务容器
# 1.yml文件创建pod
cat > pod.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: luovippod
spec:
containers:
- name: hello
image: httpd
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo "Hello, China!" && sleep 3600']
restartPolicy: OnFailure
EOF
root@k8s-master:~# kubectl create -f pod.yml
pod/luovippod created
root@k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
luovippod 1/1 Running 0 5s
root@k8s-master:~# kubectl logs luovippod
Hello, China!
root@k8s-master:~# kubectl delete pod luovippod 删除pod
# 2.命令行创建pod
root@k8s-master:~# kubectl run luoyupod --image=nginx --port=80
pod/luoyupod created
root@k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
luovippod 1/1 Running 0 7m56s
luoyupod 1/1 Running 0 3m38s
root@k8s-master:~# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
luovippod 1/1 Running 0 8m6s 172.16.194.71 k8s-worker1 <none> <none>
luoyupod 1/1 Running 0 3m48s 172.16.194.72 k8s-worker1 <none> <none>
2.一个Pod中有多个业务容器
cat > multicontainer.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo "Hello, luoyu!" && sleep 3600']
- name: httpd
image: httpd
imagePullPolicy: IfNotPresent
ports:
- name: web
containerPort: 80
restartPolicy: OnFailure
EOF
root@k8s-master:~# kubectl create -f multicontainer.yml
pod/pod created
root@k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
luovippod 1/1 Running 0 18m
luoyupod 1/1 Running 0 14m
pod 2/2 Running 0 9s
root@k8s-master:~# kubectl get -f multicontainer.yml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod 2/2 Running 0 68s 172.16.126.3 k8s-worker2 <none> <none>
root@k8s-master:~# curl 172.16.126.3
9.4 修改Pod
# 直接修改yaml文件,然后执行以下命令
kubectl apply -f pod.yml
# 编辑Etcd数据
kubectl edit pod luovippod
# patch参数
kubectl get pod luovippod -o json
kubectl get pod luovippod -o json | grep cnlxh
注明:工作中的修改pod一般时k8s会创建新的pod并删除旧的pod
9.5 进入pod中的容器
root@k8s-master:~# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
luovippod 1/1 Running 0 37m 172.16.194.71 k8s-worker1 <none> <none>
luoyupod 1/1 Running 0 33m 172.16.194.72 k8s-worker1 <none> <none>
pod 2/2 Running 0 19m 172.16.126.3 k8s-worker2 <none> <none>
root@k8s-master:~# kubectl exec -it pod -c httpd -- /bin/bash
root@pod:/usr/local/apache2# echo MyCity is ChengDu! > htdocs/index.html
root@pod:/usr/local/apache2# exit
exit
root@k8s-master:~# curl http://172.16.126.3
MyCity is ChengDu!
#参数说明:
1、-c 参数可以指定需要进入pod中的哪个容器
2、-- 是K8S命令和预期容器内部执行命令的连接符
3、/bin/sh是指进入容器中执行什么命令
4、退出执行exit
9.6 Init类型容器
Init容器是一种特殊容器,在Pod内的应用容器启动之前运行,如果Pod的Init容器失败,kubelet会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 “Never”,并且 Pod的 Init 容器失败, 则 Kubernetes 会将整个 Pod 状态设置为失败
Init容器与普通的容器非常像,除了如下两点:
1.正常情况下,它们最终都会处于completed状态
2.每个都必须在下一个启动之前成功完成
# 根据安排,myapp-container的容器将等待两个init结束之后才会启动,也就是40秒之后才会启动
cat > init.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: initpd
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', "sleep 20"]
- name: init-mydb
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', "sleep 20"]
EOF
root@k8s-master:~# kubectl create -f init.yml
pod/initpd created
# -w参数可以实时查看pod的状态变化
root@k8s-master:~# kubectl get -f init.yml -w
NAME READY STATUS RESTARTS AGE
initpd 0/1 Init:0/2 0 19s
initpd 0/1 Init:1/2 0 21s
initpd 0/1 Init:1/2 0 22s
initpd 0/1 PodInitializing 0 42s
initpd 1/1 Running 0 43s
root@k8s-master:~# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
initpd 0/1 Init:1/2 0 34s
luovippod 1/1 Running 0 56m
luoyupod 1/1 Running 0 51m
pod 2/2 Running 0 37m
root@k8s-master:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
initpd 1/1 Running 0 104s
luovippod 1/1 Running 0 57m
luoyupod 1/1 Running 0 52m
pod 2/2 Running 0 39m
9.7 Sidecar类型容器
一般来讲,Sidecar容器可以:
1.日志代理/转发,例如 fluentd
2.Service Mesh,比如 Istio,Linkerd
3.代理,比如 Docker Ambassador
4.探活:检查某些组件是不是正常工作
5.其他辅助性的工作,比如拷贝文件,下载文件等
# 两个容器挂载了同一个目录,一个容器负责写入数据,一个容器负责对外展示
cat > sidecar.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: sidecarpod
spec:
containers:
- name: httpd
image: httpd
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/local/apache2/htdocs/
name: luoyuvolume
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo "Hello sidecar" > /usr/local/apache2/htdocs/index.html && sleep 3600']
volumeMounts:
- mountPath: /usr/local/apache2/htdocs/
name: luoyuvolume
restartPolicy: OnFailure
volumes:
- name: luoyuvolume
emptyDir: {}
EOF
root@k8s-master:~# kubectl create -f sidecar.yml
pod/sidecarpod created
root@k8s-master:~# kubectl get -f sidecar.yml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sidecarpod 2/2 Running 0 9s 172.16.194.74 k8s-worker1 <none> <none>
root@k8s-master:~# curl http://172.16.194.74
Hello sidecar
9.8 静态Pod
静态 Pod 在指定的节点上由 kubelet 守护进程直接管理,不需要 API 服务器监管。 与由控制面管理的Pod(例如,Deployment) 不同;kubelet 监视每个静态 Pod(在它崩溃之后重新启动)
静态 Pod 永远都会绑定到一个指定节点上的 Kubelet
kubelet 会尝试通过 Kubernetes API 服务器为每个静态 Pod 自动创建一个 mirror Pod。 这意味着节点上运行的静态 Pod 对 API 服务来说是可见的,但是不能通过 API 服务器来控制。 Pod 名称将把以连字符开头的节点主机名作为后缀
运行中的 kubelet 会定期扫描配置的目录中的变化, 并且根据文件中出现/消失的 Pod 来添加/删除Pod
1.查找静态pod的编写路径
root@k8s-master:~# systemctl status kubelet
...
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
...
root@k8s-master:~# tail /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
[Service]
...
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
...
root@k8s-master:~# grep -i static /var/lib/kubelet/config.yaml
staticPodPath: /etc/kubernetes/manifests
2.编写静态pod
cat > static.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: staticpod
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo "Hello, lixiaohui!" && sleep 3600']
restartPolicy: OnFailure
EOF
# 把这个yaml文件复制到/etc/kubernetes/manifests,然后观察pod列表,然后把yaml文件移出此文件夹,再观察pod列表
root@k8s-master:~# cp static.yml /etc/kubernetes/manifests/
root@k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
initpd 1/1 Running 0 40m
luovippod 0/1 Completed 0 95m
luoyupod 1/1 Running 0 91m
pod 1/2 NotReady 0 77m
sidecarpod 2/2 Running 0 31m
staticpod-k8s-master 1/1 Running 0 12s
# 删除/etc/kubernetes/manifests文件中的yml文件,再观察pod列表
root@k8s-master:~# rm -rf /etc/kubernetes/manifests/static.yml
root@k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
initpd 1/1 Running 0 41m
luovippod 0/1 Completed 0 97m
luoyupod 1/1 Running 0 92m
pod 1/2 NotReady 0 78m
sidecarpod 2/2 Running 0 32m
# 维持集群运行的文件如下:
root@k8s-master:/etc/kubernetes/manifests# ls
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml static.yml
9.9 Pod删除
kubectl delete pod –all会删除所有pod
kubectl delete pod pod名称—删除指定的pod
root@k8s-master:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
initpd 1/1 Running 0 13m
luovippod 1/1 Running 0 9m23s
luoyupod 1/1 Running 0 169m
pod 2/2 Running 0 7m51s
sidecarpod 2/2 Running 0 27s
staticpod-k8s-master 1/1 Running 0 2s
root@k8s-master:~# kubectl delete pod luovippod
root@k8s-master:~# kubectl delete pod -all
root@k8s-master:~# kubectl get pods
No resources found in default namespace.
root@k8s-master:~# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-76fccbbb6b-l7jq9 1/1 Running 0 35h
coredns-76fccbbb6b-nd68g 1/1 Running 0 35h
etcd-k8s-master 1/1 Running 0 35h
kube-apiserver-k8s-master 1/1 Running 0 35h
kube-controller-manager-k8s-master 1/1 Running 0 35h
kube-proxy-8n6x5 1/1 Running 0 32h
kube-proxy-mcwv7 1/1 Running 0 35h
kube-proxy-xk4h4 1/1 Running 0 32h
kube-scheduler-k8s-master 1/1 Running 0 35h
10 Kubernetes控制器
10.1 什么是控制器
当你设置了温度,告诉了空调遥控器你的期望状态(Desired State)。 房间的实际温度是当前状态(Current State)。 通过对遥控器的开关控制,遥控器让其当前状态接近期望状态
在 Kubernetes 中,控制器通过监控集群的公共状态,并致力于将当前状态转变为期望的状态
作为设计原则之一,Kubernetes 使用了很多控制器,每个控制器管理集群状态的一个特定方面。 最常见的一个特定的控制器使用一种类型的资源作为它的期望状态, 控制器管理控制另外一种类型的资源向它的期望状态演化
10.2 Replica Set概念
ReplicationController确保在任何时候都有特定数量的Pod副本处于运行状态。 换句话说,ReplicationController 确保一个 Pod 或一组同类的 Pod 总是可用的
ReplicaSet的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。
说明: 现在推荐使用配置ReplicaSet的Deployment来建立副本管理机制
10.3 Replica Set 工作原理
RepicaSet是通过一组字段来定义的,包括一个用来识别可获得的 Pod 的集合的选择算符、一个用来标明应该维护的副本个数的数值、一个用来指定应该创建新 Pod 以满足副本个数条件时要使用的 Pod 模板等等。 每个 ReplicaSet 都通过根据需要创建和 删除 Pod 以使得副本个数达到期望值, 进而实现其存在价值。当 ReplicaSet 需要创建新的 Pod 时,会使用所提供的 Pod 模板
1.ReplicaSet也需要apiVersion、kind和metadata字段
2.Pod 选择算符:.spec.selector 字段是一个标签选择算符。在 ReplicaSet 中,.spec.template.metadata.labels 的值必须与 spec.selector 值 相匹配,否则该配置会被API拒绝
3.可以通过设置 .spec.replicas 来指定要同时运行的 Pod个数。 ReplicaSet 创建、删除 Pods 以与此值匹配
10.4 ReplicaSet使用
使用nginx镜像创建具有3个pod的RS,并分配合适的标签
10.4.1 创建yml文件
cat > rs.yml <<EOF
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginxrstest
labels:
app: nginxrstest
spec:
replicas: 3
selector:
matchLabels:
app: nginxrstest
template:
metadata:
labels:
app: nginxrstest
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
imagePullPolicy: IfNotPresent
EOF
10.4.2 操作ReplicaSet
root@k8s-master:~# kubectl create -f rs.yml
root@k8s-master:~# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginxrstest 3 3 3 3m45s
root@k8s-master:~# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginxrstest-5bvpr 1/1 Running 0 7m1s app=nginxrstest
nginxrstest-9d86s 1/1 Running 0 7m1s app=nginxrstest
nginxrstest-k79cw 1/1 Running 0 7m1s app=nginxrstest
# 被动高可用
root@k8s-master:~# kubectl delete pod --all
pod "nginxrstest-5bvpr" deleted
pod "nginxrstest-9d86s" deleted
pod "nginxrstest-k79cw" deleted
root@k8s-master:~# kubectl get replicasets.apps,pods
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginxrstest 3 3 3 12m
NAME READY STATUS RESTARTS AGE
pod/nginxrstest-86dd7 1/1 Running 0 3m6s
pod/nginxrstest-bbzxd 1/1 Running 0 3m6s
pod/nginxrstest-ndgxg 1/1 Running 0 3m6s
# 扩容
root@k8s-master:~# kubectl scale replicaset nginxrstest --replicas 4
replicaset.apps/nginxrstest scaled
root@k8s-master:~# kubectl get replicasets.apps nginxrstest
NAME DESIRED CURRENT READY AGE
nginxrstest 4 4 4 16m
root@k8s-master:~# kubectl get replicasets.apps,pods -o wide
# 删除
root@k8s-master:~# kubectl delete replicasets.apps nginxrstest
replicaset.apps "nginxrstest" deleted
root@k8s-master:~# kubectl get pod
No resources found in default namespace.
10.5 Deployment
ReplicaSet确保任何时间都有指定数量的Pod副本在运行。 然而,Deployment是一个更高级的概念,它管理ReplicaSet,并向Pod提供声明式的更新以及许多其他有用的功能。 因此,建议使用 Deployment 而不是直接使用 ReplicaSet,除非需要自定义更新业务流程或根本不需要更新
这实际上意味着,可能永远不需要操作ReplicaSet对象:而是使用Deployment,并在spec部分定义应用
10.5.1 创建yml文件
cat > deployment.yml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
EOF
10.5.2 创建Deployment
# 使用nginx镜像创建具有3个副本的Deployment,并分配合适的属性
# 发现deployment管理了一个RS,而RS又实现了3个pod
root@k8s-master:~# kubectl create -f deployment.yml
deployment.apps/nginx-deployment created
root@k8s-master:~# kubectl get deployment.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 20s
# kubectl get pods --show-labels(可选)
Deployment控制器将pod-template-hash标签添加到Deployment所创建或收留的每个ReplicaSet,此标签可确保Deployment的子 ReplicaSets不重叠
root@k8s-master:~# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-deployment-8d94c585f-ngm9d 1/1 Running 0 51s app=nginx,pod-template-hash=8d94c585f
nginx-deployment-8d94c585f-wf4mc 1/1 Running 0 51s app=nginx,pod-template-hash=8d94c585f
nginx-deployment-8d94c585f-wjzkw 1/1 Running 0 51s app=nginx,pod-template-hash=8d94c585f
root@k8s-master:~# kubectl get deployments.apps,replicasets.apps,pods -l app=nginx
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 3/3 3 3 2m20s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-8d94c585f 3 3 3 2m20s
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-8d94c585f-ngm9d 1/1 Running 0 2m20s
pod/nginx-deployment-8d94c585f-wf4mc 1/1 Running 0 2m20s
pod/nginx-deployment-8d94c585f-wjzkw 1/1 Running 0 2m20s
10.5.3 更新Deployment
1.将deployment的镜像更改一次
# Deployment的更新策略
root@k8s-master:~# kubectl get deployments.apps nginx-deployment -o yaml
apiVersion: apps/v1
kind: Deployment
...
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
...
root@k8s-master:~# kubectl set image deployments/nginx-deployment nginx=nginx:1.17.1 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-deployment image updated
# 查看更新的进度---更新过程是多了一个replicaset
root@k8s-master:~# kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
root@k8s-master:~# kubectl get deployments.apps,replicasets.apps,pods -l app=nginx
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 3/3 3 3 5m43s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5d457cdfc8 3 3 3 84s
replicaset.apps/nginx-deployment-8d94c585f 0 0 0 5m43s
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-5d457cdfc8-7whnx 1/1 Running 0 66s
pod/nginx-deployment-5d457cdfc8-b7njk 1/1 Running 0 84s
pod/nginx-deployment-5d457cdfc8-x4zv8 1/1 Running 0 55s
2.更新的策略
# 首先创建了一个新的Pod,然后删除了一些旧的Pods, 并创建了新的Pods。不会杀死老Pods,直到有足够的数量新的Pods已经出现
# 在足够数量的旧Pods被杀死前并没有创建新Pods。确保至少2个Pod可用,同时最多总共4个pod可用
# Deployment可确保在更新时仅关闭一定数量的Pod。默认情况下确保至少所需Pods 75%处于运行状态(最大不可用比例为 25%)
root@k8s-master:~# kubectl describe deployments.apps nginx-deployment
10.5.4 回滚Deployment
假设在更新时犯错误了,将镜像名称命名设置为nginx:1.172,而不是nginx:1.17.2,发现永远无法更新成功,此时就需要回退
root@k8s-master:~# kubectl set image deployments/nginx-deployment nginx=nginx:1.172 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-deployment image updated
root@k8s-master:~# kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
# 镜像拉取失败
root@k8s-master:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-5d457cdfc8-7whnx 1/1 Running 0 7m50s
nginx-deployment-5d457cdfc8-b7njk 1/1 Running 0 8m8s
nginx-deployment-5d457cdfc8-x4zv8 1/1 Running 0 7m39s
nginx-deployment-6b7d6c469c-zcjps 0/1 ImagePullBackOff 0 92s
# 开始回滚
1.查看历史版本
root@k8s-master:~# kubectl rollout history deployments/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployments/nginx-deployment nginx=nginx:1.17.1 --record=true
3 kubectl set image deployments/nginx-deployment nginx=nginx:1.172 --record=true
2.查看某个版本
root@k8s-master:~# kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
deployment.apps/nginx-deployment with revision #2
Pod Template:
Labels: app=nginx
pod-template-hash=5d457cdfc8
Annotations: kubernetes.io/change-cause: kubectl set image deployments/nginx-deployment nginx=nginx:1.17.1 --record=true
Containers:
nginx:
Image: nginx:1.17.1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>
3.回滚到某个版本
root@k8s-master:~# kubectl rollout undo deployments/nginx-deployment --to-revision=2
deployment.apps/nginx-deployment rolled back
root@k8s-master:~# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
root@k8s-master:~# kubectl get deployment.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 23m
10.5.5 伸缩Deployment
将指定的deployment副本更改为10
root@k8s-master:~# kubectl scale deployments/nginx-deployment --replicas=10
deployment.apps/nginx-deployment scaled
root@k8s-master:~# kubectl get deployments.apps,replicasets.apps -l app=nginx
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 10/10 10 10 25m
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5d457cdfc8 10 10 10 21m
replicaset.apps/nginx-deployment-6b7d6c469c 0 0 0 14m
replicaset.apps/nginx-deployment-8d94c585f 0 0 0 25m
root@k8s-master:~# kubectl delete deployments.apps nginx-deployment
10.6 DaemonSet
DaemonSet确保全部(或某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除DaemonSet将会删除它创建的所有Pod
DaemonSet 的一些典型用法:
1.在每个节点上运行集群守护进程
2.在每个节点上运行日志收集守护进程
3.在每个节点上运行监控守护进程
使用busybox镜像,在每一个节点上都运行一个pod:
cat > daemonset.yml <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: luovip
labels:
daemonset: test
spec:
selector:
matchLabels:
name: testpod
template:
metadata:
labels:
name: testpod
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'sleep 3600']
EOF
root@k8s-master:~# kubectl create -f daemonset.yml
daemonset.apps/luovip created
root@k8s-master:~# kubectl get daemonsets.apps
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
luovip 2 2 2 2 2 <none> 19s
root@k8s-master:~# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
luovip-bxkmh 1/1 Running 0 95s 172.16.126.33 k8s-worker2 <none> <none>
luovip-fj5mz 1/1 Running 0 95s 172.16.194.105 k8s-worker1 <none> <none>
...
root@k8s-master:~# kubectl delete -f daemonset.yml
DaemonSet总结:
1.默认情况下, DaemonSet会在所有Node上创建一个Pod
2.如果将运行的pod删除,DaemonSet会自动启动一个新的
3.当有新节点加入集群时,会自动向其部署Pod
4.当节点离开集群时,其上的节点会销毁,而不会跑到其他节点上
10.7 StatefulSet
StatefulSet管理基于相同容器规约的一组 Pod。但和Deployment不同的是, StatefulSet为它们的每个Pod维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID
StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:
1.稳定的、唯一的网络标识符
2.稳定的、持久的存储
3.有序的、优雅的部署和缩放
4.有序的、自动的滚动更新
10.7.1 创建yml文件
使用nginx镜像,创建一个副本数为3的有状态应用,并挂载本地目录到容器中
cat > statefulset.yml <<EOF
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.17.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
emptyDir: {}
EOF
10.7.2 操作StatefulSet
# 发现创建的过程是有次序的,这也验证了有状态应用的启动顺序
root@k8s-master:~# kubectl create -f statefulset.yml
statefulset.apps/web created
root@k8s-master:~# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 1s
web-0 0/1 ContainerCreating 0 1s
web-0 0/1 ErrImagePull 0 82s
web-0 0/1 ImagePullBackOff 0 96s
web-0 1/1 Running 0 108s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 11s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 1s
root@k8s-master:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 3m36s
web-1 1/1 Running 0 108s
web-2 1/1 Running 0 97s
root@k8s-master:~# kubectl delete -f statefulset.yml
statefulset.apps "web" deleted
10.8 Job
不断打印CKA JOB字符串,失败最多重试4次
cat > job.yml <<EOF
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: busybox
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "while true;do echo CKA JOB;done"]
restartPolicy: Never
backoffLimit: 4
EOF
root@k8s-master:~# kubectl create -f job.yml
job.batch/pi created
root@k8s-master:~# kubectl get jobs,pods
NAME STATUS COMPLETIONS DURATION AGE
job.batch/pi Running 0/1 10s 10s
NAME READY STATUS RESTARTS AGE
pod/pi-rglzp 1/1 Running 0 10s
root@k8s-master:~# kubectl logs pi-rglzp
CKA JOB
CKA JOB
CKA JOB
...
root@k8s-master:~# kubectl delete -f job.yml
job.batch "pi" deleted
10.9 CronJob
每分钟打印一次指定字符串
cat > cronjob.yml <<EOF
apiVersion: batch/v1
kind: CronJob
metadata:
name: cronjobtest
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
EOF
root@k8s-master:~# kubectl create -f cronjob.yml
cronjob.batch/cronjobtest created
root@k8s-master:~# kubectl get cronjobs,pod
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/cronjobtest */1 * * * * <none> False 0 34s 35s
NAME READY STATUS RESTARTS AGE
pod/cronjobtest-29127403-mb9qx 0/1 Completed 0 34s
root@k8s-master:~# kubectl logs cronjobtest-29127403-mb9qx
Mon May 19 08:43:01 UTC 2025
Hello from the Kubernetes cluster
root@k8s-master:~# kubectl get cronjobs,pod
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/cronjobtest */1 * * * * <none> False 0 9s 2m10s
NAME READY STATUS RESTARTS AGE
pod/cronjobtest-29127403-mb9qx 0/1 Completed 0 2m9s
pod/cronjobtest-29127404-9jmbj 0/1 Completed 0 69s
pod/cronjobtest-29127405-h5cc7 0/1 Completed 0 9s
# 关于展示的任务次数的显示等的修改
root@k8s-master:~# kubectl explain cronjob.spec
root@k8s-master:~# kubectl delete -f cronjob.yml
cronjob.batch "cronjobtest" deleted
11 Service 服务发现
11.1 Service
Pod是非永久性资源,每个Pod都有自己的IP地址
如果一组Pod(称为“后端”)为集群内的其他Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的IP地址,以便前端可以使用提供工作负载的后端部分
11.2 Service类型
ClusterIP:通过集群的内部IP暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的ServiceType
NodePort:通过每个节点上的IP和静态端口(NodePort)暴露服务。 NodePort服务会路由到自动创建的ClusterIP服务。 通过请求 <节点 IP>:<节点端口>,可以从集群的外部访问一个NodePort服务
LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的NodePort服务和ClusterIP 服务上
ExternalName:通过返回CNAME和对应值,可以将服务映射到externalName字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理
11.3 iptables代理模式的Service
kube-proxy会监视Kubernetes 控制节点对Service对象和Endpoints对象的添加和移除。 对每个Service,它会配置iptables规则,从而捕获到达该Service的clusterIP和端口的请求,进而将请求重定向到 Service 的一组后端中的某个Pod上面。 对于每个Endpoints对象,它也会配置iptables规则,这个规则会选择一个后端组合
默认的策略是,kube-proxy在iptables模式下随机选择一个后端
使用iptables处理流量具有较低的系统开销,因为流量由Linux netfilter处理, 而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠
11.4 IPVS代理模式的Service
在ipvs模式下,kube-proxy监视Kubernetes服务和端点,调用netlink接口相应地创建IPVS规则, 并定期将IPVS规则与Kubernetes服务和端点同步。 该控制循环可确保IPVS 状态与所需状态匹配。访问服务时,IPVS将流量定向到后端Pod之一
IPVS代理模式基于类似于iptables模式的netfilter挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables模式下的kube-proxy相比,IPVS模式下的kube-proxy重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比,IPVS模式还支持更高的网络流量吞吐量
11.5 生成Service
11.5.1 准备后端Pod
用nginx镜像准备一个3副本的deployment作为后端,并开放80端口
cat > deployment-service.yml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-servicetest
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
EOF
root@k8s-master:~# kubectl create -f deployment-service.yml
deployment.apps/nginx-deployment-servicetest created
root@k8s-master:~# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-servicetest-8d94c585f-6ktj9 1/1 Running 0 13s 172.16.126.35 k8s-worker2 <none> <none>
nginx-deployment-servicetest-8d94c585f-jclrr 1/1 Running 0 13s 172.16.194.117 k8s-worker1 <none> <none>
nginx-deployment-servicetest-8d94c585f-wgztv 1/1 Running 0 13s 172.16.194.116 k8s-worker1 <none> <none>
root@k8s-master:~# curl 172.16.126.35
11.5.2 命令行生成Service
# 用kubectl expose的命令创建一个针对deployment的服务,并查询endpoint是否准备就绪
root@k8s-master:~# kubectl expose deployment nginx-deployment-servicetest --port=9000 --name=luoyuservice --target-port=80 --type=NodePort
service/luoyuservice exposed
root@k8s-master:~# kubectl get service,endpoints
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d13h
service/luoyuservice NodePort 10.105.8.223 <none> 9000:32646/TCP 26s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.8.3:6443 2d13h
endpoints/luoyuservice 172.16.126.35:80,172.16.194.116:80,172.16.194.117:80 26s
root@k8s-master:~# curl http://192.168.8.3:32646
...
<title>Welcome to nginx!</title>
root@k8s-master:~# kubectl delete service luoyuservice
service "luoyuservice" deleted
11.6 ClusterIP类型的Service
ClusterIP是默认的Service类型,对外提供8000端口,并把流量引流到具有app: nginx的后端80端口上
cat > clusterip.yml <<EOF
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 8000
targetPort: 80
EOF
root@k8s-master:~# kubectl create -f clusterip.yml
service/my-service created
root@k8s-master:~# kubectl get service,endpoints
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d2h
service/my-service ClusterIP 10.101.56.206 <none> 8000/TCP 2m35s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.8.3:6443 3d2h
endpoints/my-service 172.16.126.35:80,172.16.194.116:80,172.16.194.117:80 2m35s
root@k8s-master:~# curl 10.101.56.206:8000
...
<title>Welcome to nginx!</title>
root@k8s-master:~# kubectl delete -f clusterip.yml
service "my-service" deleted
11.7 NodePort类型的Service
Type: NodePort将会在节点的特定端口上开通服务,指定了端口为31788
cat > nodeport.yml <<EOF
apiVersion: v1
kind: Service
metadata:
name: nodeservice
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 8000
targetPort: 80
nodePort: 31788
EOF
root@k8s-master:~# kubectl create -f nodeport.yml
service/nodeservice created
root@k8s-master:~# kubectl get service,endpoints
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d3h
service/nodeservice NodePort 10.102.124.139 <none> 8000:31788/TCP 27s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.8.3:6443 3d3h
endpoints/nodeservice 172.16.126.35:80,172.16.194.116:80,172.16.194.117:80 27s
# 因为是nodeport,所以用节点IP
root@k8s-master:~# curl 192.168.8.4:31788
...
<title>Welcome to nginx!</title>
root@k8s-master:~# kubectl delete -f nodeport.yml
service "nodeservice" deleted
11.8 Headless类型的Service
11.8.1 服务实现
在此类型的Service中,将不会只返回Service IP,会直接返回众多Pod 的IP地址,所以需要进入pod中用集群内DNS进行测试
cat > headless.yml <<EOF
apiVersion: v1
kind: Service
metadata:
name: headless
spec:
clusterIP: None
selector:
app: nginx
ports:
- protocol: TCP
port: 8000
targetPort: 80
EOF
root@k8s-master:~# kubectl create -f headless.yml
service/headless created
root@k8s-master:~# kubectl get service,endpoints
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/headless ClusterIP None <none> 8000/TCP 25s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d3h
NAME ENDPOINTS AGE
endpoints/headless 172.16.126.35:80,172.16.194.116:80,172.16.194.117:80 25s
endpoints/kubernetes 192.168.8.3:6443 3d3h
11.8.2 测试Headless服务发现
root@k8s-master:~# kubectl run --rm --image=busybox:1.28 -it testpod
If you don't see a command prompt, try pressing enter.
/ # nslookup headless
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: headless
Address 1: 172.16.194.117 172-16-194-117.headless.default.svc.cluster.local
Address 2: 172.16.194.116 172-16-194-116.headless.default.svc.cluster.local
Address 3: 172.16.126.35 172-16-126-35.headless.default.svc.cluster.local
/ # exit
Session ended, resume using 'kubectl attach testpod -c testpod -i -t' command when the pod is running
pod "testpod" deleted
root@k8s-master:~# kubectl delete -f headless.yml
service "headless" deleted
服务的DNS记录名称为:
servicename.namespace.svc.cluster.local
deployment中Pod的DNS记录名称为:
podIP.servicename.namespace.svc.cluster.local
Client访问服务时,可以使用DNS记录便捷抵达服务,甚至与服务在同一namespace时,直接用 servicename进行访问
11.9 LoadBalancer类型的Service
11.9.1 部署metallb负载均衡
1.先部署一个metallb controller和Speaker:
1.metallb controller用于负责监听Kubernetes Service的变化,当服务类型被设置为LoadBalancer时,Controller会从一个预先配置的IP地址池中分配一个 IP地址给该服务,并管理这个IP地址的生命周期
2.Speaker负责将服务的 IP 地址通过标准的路由协议广播到网络中,确保外部流量能够正确路由到集群中的服务
root@k8s-master:~# kubectl apply -f https://www.linuxcenter.cn/files/cka/cka-yaml/metallb-native.yaml
2.定义一组由负载均衡对外分配的IP地址范围
cat > ippool.yml <<-EOF
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: lxh-ip-pool-192-168-8-10-100
namespace: metallb-system
spec:
addresses:
- 192.168.8.10-192.168.8.100
EOF
root@k8s-master:~# kubectl apply -f ippool.yml
ipaddresspool.metallb.io/lxh-ip-pool-192-168-8-10-100 created
3.在 Layer 2 模式下用于控制如何通过 ARP(Address Resolution Protocol)或 NDP(Neighbor Discovery Protocol)协议宣告服务的 IP 地址,使得这些 IP 地址在本地网络中可解析
cat > l2Advertisement.yml <<-EOF
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: l2-myippool
namespace: metallb-system
spec:
ipAddressPools:
- lxh-ip-pool-192-168-8-10-100
EOF
root@k8s-master:~# kubectl apply -f l2Advertisement.yml
l2advertisement.metallb.io/l2-myippool created
11.9.2 部署LoadBalancer服务
负载均衡准备好之后,创建LoadBalancer类型的服务
cat > loadbalancer.yml <<-EOF
apiVersion: v1
kind: Service
metadata:
name: loadbalance-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
EOF
root@k8s-master:~# kubectl apply -f loadbalancer.yml
service/loadbalance-service created
# 获取服务看看是否分配到了负载均衡IP 从输出上看,分配到了192.168.8.10
root@k8s-master:~# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d4h
loadbalance-service LoadBalancer 10.99.147.16 192.168.8.10 80:30972/TCP 10s
# 用负载均衡IP访问
root@k8s-master:~# curl 192.168.8.10
...
<title>Welcome to nginx!</title>
# 删除service资源
root@k8s-master:~# kubectl delete -f loadbalancer.yml
service "loadbalance-service" deleted
11.10 Ingress
Ingress公开了从集群外部到集群内服务的HTTP和HTTPS路由
流量路由由Ingress资源上定义的规则控制
Ingress可以提供负载均衡、SSL卸载和基于名称的虚拟托管,为了让Ingress资源工作,集群必须有一个正在运行的Ingress控制器
11.10.1 Ingress控制器部署
Ingress需要Ingress控制器支持,先部署控制器
# 拉取v1.1.0版本的yaml文件
# 使用如下路径下载-可能会失败
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/baremetal/deploy.yaml
root@k8s-master:~# wget https://www.linuxcenter.cn/files/cka/cka-yaml/ingressdeploy.yaml
# 从阿里云的镜像仓库上面拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.12.1
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.5.2
# 在Kubernetes Ingress 的使用场景中,尤其是使用Ingress-Nginx作为 Ingress Controller 时,kube-webhook-certgen工具被用来创建和更新用于TLS认证的证书。这些证书被用于确保 Webhook 与 Kubernetes API 服务器之间的通信是安全的
# 修改ingressdeploy.yaml文件
440行改为 image: registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.12.1
542行改为image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.5.2
596行改为 image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.5.2
root@k8s-master:~# kubectl apply -f ingressdeploy.yaml
root@k8s-master:~# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-k9smq 0/1 Completed 0 17m
ingress-nginx-admission-patch-46hk7 0/1 Completed 2 17m
ingress-nginx-controller-47nzv 1/1 Running 0 17m
ingress-nginx-controller-svcpx 1/1 Running 0 17m
# admission相关pod状态为Completed为正常
11.10.2 Ingress路径类型
Ingress中的每个路径都需要有对应的路径类型,未明确设置pathType的路径无法通过合法性检查。当前支持的路径类型有三种:
1.ImplementationSpecific:对于这种路径类型,匹配方法取决于IngressClass。 具体实现可以将其作为单独的pathType处理或者与Prefix或 Exact类型作相同处理
2.Exact:精确匹配URL路径,且区分大小写
3.Prefix:基于以 / 分隔的URL路径前缀匹配。匹配区分大小写,并且对路径中的元素逐个完成。 路径元素指的是由/分隔符分隔的路径中的标签列表。 如果每个p都是请求路径p的元素前缀,则请求与路径p匹配
11.10.3 Ingress的使用
用nginx镜像生成一个3副本的Pod,并通过Service提供服务,然后再用ingress,以特定域名的方式对外暴露
cat > ingress.yml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-ingress
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.16.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: ingressservice
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: luovip
spec:
ingressClassName: nginx
rules:
- host: www.luovip.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: ingressservice
port:
number: 80
EOF
root@k8s-master:~# kubectl create -f ingress.yml
deployment.apps/nginx-deployment-ingress created
service/ingressservice created
ingress.networking.k8s.io/luovip created
root@k8s-master:~# kubectl get deployments,service,ingress
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment-ingress 3/3 3 3 38s
deployment.apps/nginx-deployment-servicetest 3/3 3 3 24h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingressservice ClusterIP 10.98.162.124 <none> 80/TCP 38s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d13h
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/luovip nginx www.luovip.com 192.168.8.4,192.168.8.5 80 38s
# 把上述ADDRESS部分的IP和域名绑定解析
root@k8s-master:~# echo 192.168.8.4 www.luovip.com >> /etc/hosts
root@k8s-master:~# curl http://www.luovip.com
root@k8s-master:~# kubectl delete -f ingress.yml
11.11 Gateway API
11.11.1 Gateway API 基本介绍
Kubernetes Gateway API 是一种新的 API 规范,旨在提供一种在 Kubernetes 中管理网关和负载均衡器的标准方法。它被设计为 Ingress API 的替代方案,提供更丰富的功能和更好的扩展性,Gateway API 的核心思想是通过使用可扩展的、角色导向的、协议感知的配置机制来提供网络服务。
Gateway API 包括几个核心组件:
1.GatewayClass:定义一组具有配置相同的网关,由实现该类的控制器管理。
2.Gateway:定义流量处理基础设施(例如云负载均衡器)的一个实例。
3.Route:描述了特定协议的规则,用于将请求从 Gateway 映射到 Kubernetes 服务。目前,HTTPRoute 是比较稳定的版本,而 TCPRoute、UDPRoute、GRPCRoute、TLSRoute 等也在开发中。
GatewayClass
定义:类似于Ingress 中的 IngressClass,定义了一组共享通用配置和行为的 Gateway 集合,类似于选择一种负载均衡的实现类型
作用:每个 GatewayClass 必须关联一个控制器(Controller),控制器负责处理 GatewayClass 和 Gateway 对象的变动,并创建或更新对应的网关和路由配置
特点:一般由第三方网关组件安装时自动创建,不需要人工手动创建
类似于阿里云负载均衡中的负载均衡实例类型选择。在阿里云中,用户可以选择应用型负载均衡(ALB)、网络型负载均衡(NLB)或传统型负载均衡(CLB),每种类型都有其特定的配置和功能
Gateway
定义:描述了流量被分配到集群中服务的方式,是 GatewayClass 的具体实现。
作用:负责流量接入以及往后转发。创建 Gateway 资源时,会根据 GatewayClass 生成对应的 Pod
特点:可以由管理员直接创建,也可以由控制 GatewayClass 的 Controller 创建
类似于阿里云负载均衡中的具体负载均衡实例。在阿里云中,用户创建一个负载均衡实例后,可以配置监听端口、协议等
Route
定义:描述了通过网关的流量如何映射到服务,例如 HTTPRoute 可以基于请求路径、主机名等条件将流量转发到不同的后端服务
作用:定义了流量的路由规则,将流量根据特定条件(如路径、请求头等)转发到不同的后端服务
类型:目前 Gateway API 提供了五种不同协议的 Route,分别是 HTTPRoute、TLSRoute、TCPRoute、UDPRoute 和 GRPCRoute
类似于阿里云负载均衡中的转发规则。在阿里云中,用户可以配置基于域名、路径、HTTP Header 等条件的转发规则
以下是使用 Gateway 和 HTTPRoute 将 HTTP 流量路由到服务的简单示例:
在此示例中,实现为反向代理的 Gateway 的请求数据流如下:
1.客户端开始准备 URL 为 http://test.lixiaohui.com 的 HTTP 请求
2.客户端的 DNS 解析器查询目标名称并了解与 Gateway 关联的一个或多个 IP 地址的映射。
3.客户端向 Gateway IP 地址发送请求;反向代理接收 HTTP 请求并使用 Host: 标头来匹配基于 Gateway 和附加的 HTTPRoute 所获得的配置。
4.可选的,反向代理可以根据 HTTPRoute 的匹配规则进行请求头和(或)路径匹配。
5.可选地,反向代理可以修改请求;例如,根据 HTTPRoute 的过滤规则添加或删除标头。
6.最后,反向代理将请求转发到一个或多个后端。
安装网址:https://docs.nginx.com/nginx-gateway-fabric/installation/installing-ngf/manifests/
11.1.2 Gateway API实验
实验步骤:
1.需要先做metallb,由metalb给service提供外部负载均衡IP
2.部署nginx Fabric,为GatewayAPI做后端流量处理组件
3.创建一个基于Fabric的gatewayClass
4.创建一个gateway,并监听在80端口,并关联刚创建的gatewayClass
5.创建一个httpRoute,此处定义客户端访问的域名和路径
实验效果:外部客户端可以用浏览器打开http://test.luovip.com
并返回我们的nginx业务网站内容
1.部署 Gateway API CRD
这一步用于扩展K8S功能,以便于支持Gateway API
# 方法1:
root@k8s-master:~# kubectl apply -f https://www.linuxcenter.cn/files/cka/gatewayapi/experimental-install.yaml
# 方法2:
root@k8s-master:~# wget https://www.linuxcenter.cn/files/cka/gatewayapi/experimental-install.yaml
root@k8s-master:~# kubectl apply -f experimental-install.yaml
2.部署Fabric自定义资源
# 方法1:
root@k8s-master:~# kubectl apply -f https://www.linuxcenter.cn/files/cka/gatewayapi/nginx-fabric-crds.yaml
# 方法2:
root@k8s-master:~# wget https://www.linuxcenter.cn/files/cka/gatewayapi/nginx-fabric-crds.yaml
root@k8s-master:~# kubectl apply -f nginx-fabric-crds.yaml
3.部署Fabric
这一步部署的Fabric用于处理后端流量处理
# 方法1:
root@k8s-master:~# kubectl apply -f https://www.linuxcenter.cn/files/cka/gatewayapi/nginx-fabric-deploy.yaml
# 方法2:
root@k8s-master:~# wget https://www.linuxcenter.cn/files/cka/gatewayapi/nginx-fabric-deploy.yaml
root@k8s-master:~# kubectl apply -f nginx-fabric-deploy.yaml
# 镜像使用:
ghcr.io/nginx/nginx-gateway-fabric:1.6.2
ghcr.io/nginx/nginx-gateway-fabric/nginx:1.6.2
可以使用国内南京大学的加速站点进行(在此感谢南京大学镜像站点团队提供的帮助),具体为将上述镜像替换为下面:
ghcr.nju.edu.cn/nginx/nginx-gateway-fabric:1.6.2
ghcr.nju.edu.cn/nginx/nginx-gateway-fabric/nginx:1.6.2
# 参考:
NGINX Gateway Fabric离线部署笔记
https://blog.51cto.com/huanghai/13946410
#等它部署好之后,看看pod起来没
root@k8s-master:~# kubectl get pod -n nginx-gateway -o wide
root@k8s-master:~# kubectl get pods -n nginx-gateway
NAME READY STATUS RESTARTS AGE
nginx-gateway-9cbb9b466-85ktc 2/2 Running 1 (56s ago) 72s
# 这里将会自动创建基于nginx的GatewayClass
root@k8s-master:~# kubectl get gatewayclasses.gateway.networking.k8s.io
NAME CONTROLLER ACCEPTED AGE
nginx gateway.nginx.org/nginx-gateway-controller True 17m
4.部署应用
这里的应用是模拟公司的常规业务,稍后用于对外提供服务
cat > deployment-service.yml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8sgateway-luoviptest
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
EOF
5.为了稳定pod的访问,用service的方式做了一个内部暴露
root@k8s-master:~# kubectl create -f deployment-service.yml
deployment.apps/k8sgateway-luoviptest created
root@k8s-master:~# kubectl expose deployment k8sgateway-lxhtest --port=9000 --name=lxhservice --target-port=80
service/lxhservice exposed
6.业务应用
1.创建一个名为luovip-gateway的gateway并关联了一个名为nginx的gatewayClass,这个gateway提供了一个监听在80端口的http协议的监听器,这个监听器接收来自任何namespace以luovip.com为后缀的所有请求
2.创建一个名为luovip-http的httpRoute,并关联我们的gateway,本次httpRoute提供了test.luovip.com的域名根目录的请求入口,并将流量导入到一个名为luovipservice的9000端口
cat > gatewayandhttproute.yml <<-EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: luovip-gateway
spec:
gatewayClassName: nginx
listeners:
- name: default
hostname: "*.luovip.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: luovip-http
spec:
parentRefs:
- name: luovip-gateway
hostnames: ["test.luovip.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: lxhservice
port: 9000
EOF
root@k8s-master:~# kubectl apply -f gatewayandhttproute.yml
# gateway已经从负载均衡中,拿到了外部IP地址---192.168.8.10
root@k8s-master:~# kubectl get service -n nginx-gateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-gateway LoadBalancer 10.108.91.177 192.168.8.10 80:31093/TCP,443:30287/TCP 35m
# 服务后端也有endpoint
root@k8s-master:~# kubectl get endpoints -n nginx-gateway
NAME ENDPOINTS AGE
nginx-gateway 172.16.126.3:443,172.16.126.3:80 37m
# 查看gateway和httproute
root@k8s-master:~# kubectl get gateways
NAME CLASS ADDRESS PROGRAMMED AGE
luovip-gateway nginx 192.168.8.10 True 9m14s
root@k8s-master:~# kubectl get httproute
NAME HOSTNAMES AGE
luovip-http ["test.luovip.com"] 9m29s
# 访问测试
root@k8s-master:~# echo 192.168.8.10 test.luovip.com >> /etc/hosts
root@k8s-master:~# curl http://test.luovip.com
12 健康检查
12.1 探测器概述
kubelet使用存活探测器(liveness probes)来知道什么时候要重启容器。 例如,存活探测器可以捕捉到死锁(应用程序在运行,但是无法继续执行后面的步骤)。 这样的情况下重启容器有助于让应用程序在有问题的情况下更可用。
kubelet使用就绪探测器(readiness probes)可以知道容器什么时候准备好了并可以开始接受请求流量, 当一个 Pod 内的所有容器都准备好了,才能把这个 Pod 看作就绪了。 在 Pod 还没有准备好的时候,会从 Service 的负载均衡器中被剔除的。
kubelet 使用启动探测器(startup probes)可以知道应用程序容器什么时候启动了。 可以控制容器在启动成功后再进行存活性和就绪检查, 确保这些存活、就绪探测器不会影响应用程序的启动。 这可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。
顺序:1.启动探针工作完成—如果启动探针没完成,存活和就绪是不会开始的
2.存活、就绪探针开始工作
12.2 Liveness Probes
12.2.1 文件存活检测
创建一个名为liveness的容器,并在其中执行文件的创建,休眠,然后再删除文件的操作,然后用livenessProbe来检测
cat > liveness.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: busybox
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
EOF
参数解释:
1.periodSeconds 字段指定了 kubelet 应该每 5 秒执行一次存活探测。
2.initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。
3.kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。
4.如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。
5.如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。
这个容器生命周期的前 30 秒, /tmp/healthy 文件是存在的。 所以在这最开始的 30 秒内,执行命令 cat /tmp/healthy 会返回成功代码。 30 秒之后,执行命令 cat /tmp/healthy 就会返回失败代码。
root@k8s-master:~# kubectl create -f liveness.yml
pod/liveness-exec created
# 每30秒在pod事件中就会显示存活探测器失败了,下方信息显示这个容器被杀死并且被重建了5次
root@k8s-master:~# kubectl get -f liveness.yml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
liveness-exec 1/1 Running 5 (26s ago) 6m41s 172.16.194.68 k8s-worker1 <none> <none>
root@k8s-master:~# kubectl describe pod liveness-exec
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 9m10s default-scheduler Successfully assigned default/liveness-exec to k8s-worker1
Normal Pulling 9m9s kubelet Pulling image "busybox"
Normal Pulled 9m6s kubelet Successfully pulled image "busybox" in 3.226s (3.226s including waiting). Image size: 4277910 bytes.
Normal Created 2m55s (x6 over 9m6s) kubelet Created container: liveness
Normal Started 2m55s (x6 over 9m6s) kubelet Started container liveness
Warning Unhealthy 2m10s (x18 over 8m35s) kubelet Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal Killing 2m10s (x6 over 8m25s) kubelet Container liveness failed liveness probe, will be restarted
Warning BackOff 21s (x8 over 100s) kubelet Back-off restarting failed container liveness in pod liveness-exec_default(b092160d-80d5-4edb-a593-726922c425e4)
Normal Pulled 6s (x6 over 7m55s) kubelet Container image "busybox" already present on machine
root@k8s-master:~# kubectl delete -f liveness.yml
12.2.2 HTTP存活检测
以httpget的形式访问容器中的/luovip页面,根据返回代码来判断是否正常
cat > httpget.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: http
spec:
containers:
- name: httpd
image: httpd:2.2
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /luovip
port: 80
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
restartPolicy: OnFailure
EOF
参数解释:
1.periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。
2.initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。
3.kubelet 会向容器内运行的服务发送一个 HTTP GET 请求来执行探测。 如果服务器上 /PATH 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。
4.如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它。
5.任何大于或等于 200 并且小于 400 的返回代码标示成功,其它返回代码都标示失败。
root@k8s-master:~# kubectl create -f httpget.yml
pod/http created
root@k8s-master:~# kubectl get -f httpget.yml
NAME READY STATUS RESTARTS AGE
http 1/1 Running 0 17s
root@k8s-master:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
http 0/1 Completed 2 4m40s
root@k8s-master:~# kubectl describe pod http
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m22s default-scheduler Successfully assigned default/http to k8s-worker1
Normal Pulling 2m22s kubelet Pulling image "httpd:2.2"
Normal Pulled 2m7s kubelet Successfully pulled image "httpd:2.2" in 14.839s (14.839s including waiting). Image size: 171293537 bytes.
Normal Created 109s (x3 over 2m7s) kubelet Created container: httpd
Normal Started 109s (x3 over 2m7s) kubelet Started container httpd
Normal Pulled 109s (x2 over 118s) kubelet Container image "httpd:2.2" already present on machine
Warning Unhealthy 100s (x9 over 2m4s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 100s (x3 over 118s) kubelet Container httpd failed liveness probe, will be restarted
Warning BackOff 100s kubelet Back-off restarting failed container httpd in pod http_default(8d1f374c-ba01-4a4b-a8b1-dbba11736ccf)
root@k8s-master:~# kubectl delete -f httpget.yml
12.3 ReadinessProbe
12.3.1 TCP存活检测
1.TCP 检测的配置和 HTTP 检测非常相似,同时使用就绪和存活探测器。
2.kubelet 会在容器启动 5 秒后发送第一个就绪探测。 这会尝试连接容器的800端口。如果探测成功,这个Pod会被标记为就绪状态,kubelet将继续每隔 10 秒运行一次检测。
3.除了就绪探测,这个配置包括了一个存活探测。 kubelet 会在容器启动 15 秒后进行第一次存活探测。 与就绪探测类似,会尝试连接器的800端口。 如果存活探测失败,这个容器会被重新启动。
cat > readiness.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: tcpcheck
spec:
containers:
- name: httpd
image: httpd:2.2
imagePullPolicy: IfNotPresent
ports:
- name: webport
protocol: TCP
containerPort: 80
readinessProbe:
tcpSocket:
port: 800
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 800
initialDelaySeconds: 15
periodSeconds: 20
restartPolicy: OnFailure
EOF
root@k8s-master:~# kubectl create -f readiness.yml
root@k8s-master:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
tcpcheck 0/1 Running 1 (3s ago) 64s
root@k8s-master:~# kubectl describe pod tcpcheck
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m23s default-scheduler Successfully assigned default/tcpcheck to k8s-worker1
Normal Pulled 22s (x3 over 2m22s) kubelet Container image "httpd:2.2" already present on machine
Normal Created 22s (x3 over 2m22s) kubelet Created container: httpd
Normal Started 22s (x3 over 2m22s) kubelet Started container httpd
Normal Killing 22s (x2 over 82s) kubelet Container httpd failed liveness probe, will be restarted
Warning Unhealthy 2s (x7 over 2m2s) kubelet Liveness probe failed: dial tcp 172.16.194.70:800: connect: connection refused
Warning Unhealthy 1s (x14 over 2m10s) kubelet Readiness probe failed: dial tcp 172.16.194.70:800: connect: connection refused
# 可以看到,pod对外提供了80端口,但是我们一直在检测800端口,所以这个pod的检测是失败的
root@k8s-master:~# kubectl delete -f readiness.yml
pod "tcpcheck" deleted
12.4 StartupProbe
1.有时候,会有一些现有的应用程序在启动时需要较多的初始化时间。 要不影响对引起探测死锁的快速响应,这种情况下,设置存活探测参数是要技巧的。 技巧就是使用一个命令来设置启动探测,针对HTTP 或者 TCP 检测,可以通过设置 failureThreshold * periodSeconds 参数来保证有足够长的时间应对糟糕情况下的启动时间。
2.应用程序将会有最多 30秒(3 * 10 = 30s) 的时间来完成它的启动。 一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁可以快速响应。 如果启动探测一直没有成功,容器会在 30 秒后被杀死,并且根据 restartPolicy 来设置 Pod 状态。
cat > startup.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: startprobe
spec:
containers:
- name: httpd
image: httpd:2.2
imagePullPolicy: IfNotPresent
ports:
- name: webport
protocol: TCP
containerPort: 80
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 10
startupProbe:
httpGet:
path: /
port: 800
initialDelaySeconds: 5
failureThreshold: 3
periodSeconds: 10
restartPolicy: OnFailure
EOF
Probe参数:
Probe 有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:
1.initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0
2.periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1
3.timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1
4.successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1
5.failureThreshold:当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1
root@k8s-master:~# kubectl create -f startup.yml
pod/startprobe created
root@k8s-master:~# kubectl get -f startup.yml
NAME READY STATUS RESTARTS AGE
startprobe 0/1 Running 0 7s
root@k8s-master:~# kubectl describe -f startup.yml
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 52s default-scheduler Successfully assigned default/startprobe to k8s-worker1
Normal Pulled 22s (x2 over 52s) kubelet Container image "httpd:2.2" already present on machine
Normal Created 22s (x2 over 52s) kubelet Created container: httpd
Normal Started 22s (x2 over 52s) kubelet Started container httpd
Normal Killing 22s kubelet Container httpd failed startup probe, will be restarted
Warning Unhealthy 2s (x5 over 42s) kubelet Startup probe failed: Get "http://172.16.194.71:800/": dial tcp 172.16.194.71:800: connect: connection refused
# 可以发现由于故意写成了800端口,检测失败,容器一直无法就绪
root@k8s-master:~# kubectl delete -f startup.yml
12.5 探针检测顺序与优先级
在 Kubernetes 中,startupProbe、livenessProbe 和 readinessProbe是用于监控和管理容器健康状况的探针,每种探针在容器生命周期中的不同阶段发挥不同的作用。以下是这三种探针的检测顺序和优先级:
12.5.1 startupProbe
检测顺序:startupProbe是在容器启动时首先执行的探针。它用于判断应用是否已成功启动,并且只在启动期间运行。
优先级:如果配置了 startupProbe,Kubernetes会忽略 livenessProbe和readinessProbe直到 startupProbe成功。startupProbe成功
后,livenessProbe和 readinessProbe才会开始运行。
目的:用于处理启动时间较长的应用程序,确保应用在完全启动之前不会因 livenessProbe 的失败而被重启。
12.5.2 livenessProbe
检测顺序:在 startupProbe成功之后,livenessProbe 开始执行。它定期检查容器是否处于健康状态。
优先级:如果配置了 startupProbe,livenessProbe只有在startupProbe成功之后才开始运行。如果未配置 startupProbe,
livenessProbe在容器启动后立即开始运行。
目的:用于检测容器是否仍然处于健康状态。如果 livenessProbe失败,Kubernetes 会重启该容器。
12.5.3 readinessProbe
检测顺序:在 startupProbe成readinessProbe开始执行。它定期检查容器是否已准备好接收流量。
优先级:如果配置了startupProbe,readinessProbe只有在 startupProbe成功之后才开始运行。如果未配置 startupProbe,
readinessProbe在容器启动后立即开始运行。
目的:用于判断容器是否可以接收请求。如果 readinessProbe 失败,容器将从服务的端点列表中移除,不再接收新的流量。
12.5.4 总结
顺序:startupProbe-> livenessProbe-> readinessProbe
优先级:
startupProbe优先于其他两个探针。如果配置了 startupProbe,必须先通过 startupProbe检测,livenessProbe和 readinessProbe才会启动。
livenessProbe和readinessProbe在startupProbe成功后同时开始运行,没有严格的优先级区分,但它们的作用不同,livenessProbe用于重启失败的容器,readinessProbe 用于控制流量。
12.6 优雅关闭
从 Kubernetes 1.22 开始,terminationGracePeriodSeconds 特性被开启,在杀死容器时,Pod停止获得新的流量。但在Pod中运行的容器不会受到影响。直到超时发生。可以在Pod级别或者容器下具体的探针级别设定,探针会优先和覆盖Pod级别
下面的例子中,容器将在收到结束需求是沉睡2分钟来代表业务的正常关闭,然后整个pod最多等待200秒,超过200秒,就会强制删除
cat > grace.yml <<EOF
apiVersion: v1
kind: Pod
metadata:
name: httpgrace
spec:
terminationGracePeriodSeconds: 200
containers:
- name: httpd
image: httpd:2.2
imagePullPolicy: IfNotPresent
ports:
- name: webport
protocol: TCP
containerPort: 80
lifecycle:
preStop:
exec:
command: ["/bin/sh","-c","sleep 2m"]
restartPolicy: OnFailure
EOF
root@k8s-master:~# kubectl create -f grace.yml
root@k8s-master:~# kubectl get -f grace.yml
NAME READY STATUS RESTARTS AGE
httpgrace 1/1 Running 0 9s
root@k8s-master:~# kubectl delete pod httpgrace &
[1] 34690
root@k8s-master:~# pod "httpgrace" deleted
root@k8s-master:~# jobs
[1]+ Running kubectl delete pod httpgrace &
# Terminating表示终结中
root@k8s-master:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
httpgrace 1/1 Terminating 0 2m21s
root@k8s-master:~# kubectl describe pod httpgrace
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m51s default-scheduler Successfully assigned default/httpgrace to k8s-worker1
Normal Pulled 2m50s kubelet Container image "httpd:2.2" already present on machine
Normal Created 2m50s kubelet Created container: httpd
Normal Started 2m50s kubelet Started container httpd
Normal Killing 81s kubelet Stopping container httpd
root@k8s-master:~# kubectl get pod
No resources found in default namespace.
13 Kubernetes持久存储
Container 中的文件在磁盘上是临时存放的,这给 Container 中运行的较重要的应用 程序带来一些问题:
1.当容器崩溃时文件丢失。kubelet 会重新启动容器, 但容器会以干净的状态重启
2.会在同一 Pod中运行多个容器并共享文件时出现
Kubernetes 卷(Volume) 这一抽象概念能够解决这两个问题
13.1 数据卷
Kubernetes 支持很多类型的卷
Pod 可以同时使用任意数目的卷类型
临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长
当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁 持久卷。对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置
13.2 emptyDir
当 Pod 分派到某个 Node 上时,emptyDir 卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。 就像其名称表示的那样,卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。
容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的