08-27-周三_17-09-29

This commit is contained in:
2025-08-27 17:10:05 +08:00
commit 86df397d8f
12735 changed files with 1145479 additions and 0 deletions

829
Ansible.html Normal file

File diff suppressed because one or more lines are too long

8
Docker/Docker.md Normal file
View File

@@ -0,0 +1,8 @@
# Docker是什么
Docker 是一个用于开发、发布和运行应用程序的开放平台。Docker 可让将应用程序与基础架构分离,以便快速交付软件。借助 Docker可以像管理应用程序一样管理基础架构。通过利用 Docker 的发布、测试和部署代码方法,可以显著减少编写代码和在生产中运行代码之间的延迟。
[Docker官方文档](https://docs.docker.com/)
[Docker项目地址](https://github.com/docker)

69
Docker/Docker介绍.md Normal file
View File

@@ -0,0 +1,69 @@
# Docker平台
Docker 提供了在松散隔离的环境中打包和运行应用程序的功能,该环境称为容器。隔离和安全性可以在给定主机上同时运行多个容器。容器是轻量级的,包含运行应用程序所需的一切,因此无需依赖主机上安装的内容。可以在工作时共享容器,并确保与共享的每个人都能获得以相同方式工作的相同容器。
Docker 提供工具和平台来管理容器的生命周期:
- 使用容器开发应用程序及其支持组件。
- 容器成为分发和测试应用程序的单元。
- 应用程序作为容器或编排服务部署到生产环境(本地数据中心、云提供商等等)。
# Docker使用场景
## 快速、一致地交付应用程序
Docker 允许开发人员使用提供应用程序和服务的本地容器在标准化环境中工作,从而简化了开发生命周期。容器非常适合持续集成和持续交付 (CI/CD) 工作流程。
**示例场景:**
- 开发人员本地编写代码并使用 Docker 容器与同事共享他们的工作。
- 使用 Docker 将应用程序推送到测试环境并运行自动和手动测试。
- 发现错误时,可以在开发环境中修复它们,并将其重新部署到测试环境中进行测试验证。
- 测试验证完成后,将修复后代码构建成镜像并更新至生产环境并部署容器运行。
## 响应式部署和扩展
Docker 基于容器的平台是高度可移植的。Docker 容器可以在开发人员的本地笔记本电脑、数据中心的物理机或虚拟机、云提供商或混合环境中运行。
Docker 的可移植性和轻量级特性还使其能够轻松地动态管理,根据业务需求近乎实时地扩大或拆除应用程序和服务。
## 在相同硬件上运行更多应用
Docker 轻量且快速。它为基于虚拟机的服务程序管理提供了一种可行且经济高效的替代方案因此可以使用更多的服务器容量来实现业务目标。Docker 非常适合高密度环境以及需要使用更少资源完成更多任务的中小型部署。
# Docker架构
Docker 使用客户端-服务器架构。Docker 客户端与 Docker 守护程序通信,后者负责构建、运行和分发 Docker 容器的繁重工作。Docker 客户端和守护程序可以在同一系统上运行,或者可以将 Docker 客户端连接到远程 Docker 守护程序。Docker 客户端和守护程序使用 REST API、通过 UNIX 套接字或网络接口进行通信。另一个 Docker 客户端是 Docker Compose可以管理由一组容器组成的应用程序。
![Docker架构](docker介绍与安装/docker架构图.png)
## Docker Host
Docker Host是一款易于安装的应用程序适用于 Mac、Windows 或 Linux 环境可让构建和共享容器化应用程序和微服务。Docker Host 包括 Docker 守护程序 ( `dockerd` )、Docker 客户端 ( docker )、Docker Compose、Docker Content Trust、Kubernetes 和 Credential Helper。
## Docker Daemon
Docker 守护程序 ( `dockerd` ) 监听 Docker API 请求并管理 Docker 对象例如images、containers、networks和volumes。守护程序还可以与其他守护程序通信以管理 Docker 服务。
## Docker Client
Docker 客户端 ( `docker` ) 是许多 Docker 用户与 Docker 交互的主要方式。当使用诸如 `docker run` 类的命令时,客户端会将这些命令发送到 `dockerd` ,后者会执行这些命令。`docker` 命令使用 Docker API。Docker 客户端可以与多个守护程序进行通信。
## Docker Registry
Docker 镜像仓库是存储 Docker 镜像。Docker Hub 是个公共镜像仓库Docker 默认会在 Docker Hub 上查找镜像。也可以运行自己的私有镜像仓库。
当使用 `docker pull``docker run` 命令时Docker 会从配置的镜像仓库地址中拉取所需的镜像。当使用 `docker push` 命令时Docker 会将镜像推送到配置的镜像仓库。
## Docker Objects
**Images**
镜像是创建 Docker 容器的模版。通常可以基于其他镜像进行二次构建。例如,已经有 `ubuntu` 镜像,但我们还要安装 Apache Web服务 + 应用 + 自定义配置。
基于别人发布在镜像仓库中的镜像,编写 Dockerfile 构建自己的镜像。Dockerfile 中的每条指令都会在镜像中创建一个层。更改 Dockerfile 并重建镜像时,只会重建已更改的层。这正是与虚拟化技术相比,镜像如此轻量、小巧和快速的原因之一。
**Containers**
容器是镜像的运行实例。可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器。可以将容器连接到一个或多个网络、附加存储,甚至根据其当前状态创建新镜像。
容器由其镜像和创建或启动容器时提供的任何配置选项定义。当容器被删除时,未持久化存储的更改都会消失。

View File

@@ -0,0 +1,90 @@
# 公共仓库
## 官方Docker仓库
1. 准备账户: 登陆到[Docker Hub](https://hub.docker.com/)官网创建账号登陆后点击settings完善信息。
2. 填写账户基本信息: 重置密码/生成`accesstoken`
3. `docker login docker.io` 输入用户密码即可
4. 国内可能因为网络问题,需要代理
## 阿里云镜像仓库
1. 准备账户: 登录到[Aliyun Hub](https://cr.console.aliyun.com/)官网创建账号
2. 容器镜像服务->实例列表->镜像仓库->创建镜像仓库
3. 执行操作指南
```shell
# 1.登录阿里云Docker Registry
$ docker login --username=15295733404 registry.cn-hangzhou.aliyuncs.com
# 2.从Registry中拉取镜像
$ docker pull registry.cn-hangzhou.aliyuncs.com/zhaohao/eagleslab:[镜像版本号]
# 3.将镜像推送到Registry
$ docker login --username=15295733404 registry.cn-hangzhou.aliyuncs.com
$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/zhaohao/eagleslab:[镜像版本号]
$ docker push registry.cn-hangzhou.aliyuncs.com/zhaohao/eagleslab:[镜像版本号]
# 4.选择合适的镜像仓库地址: 公网访问/VPC网络内访问
```
# 私有仓库
## docker registry
Docker Register作为Docker的核心组件之一负责镜像内容的存储与分发客户端的docker pull以及push命令都将直接与register进行交互最初版本的registry由python实现的由于涉及初期在安全性、性能以及API的设计上有着诸多的缺陷该版本在0.9之后停止了开发由新的项目distribution新的docker register被称为Distribution来重新设计并开发了下一代的registry新的项目由go语言开发所有的api底层存储方式系统架构都进行了全面的重新设计已解决上一代registry的问题。
官方文档地址https://docs.docker.com/registry
官方github地址: https://github.com/docker/distribution
**部署**
```shell
# 执行安装脚本
[root@master01 scripts]# ./install/install_registry.sh
```
## 企业级方案Harbor
参考网址https://goharbor.io/docs/2.3.0/install-config/
Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器由vmware开源其通过添加一些企业必须的功能特性例如安全、标识和管理等扩展了开源的Docker Distribution。Harbor也提供了高级的安全特性诸如用户管理访问控制和活动审计等。
**部署**
```bash
# 1.准备安装包
[root@docker-server1 ~]# wget https://github.com/goharbor/harbor/releases/download/v2.3.1/harbor-offline-installer-v2.3.1.tgz
[root@docker-server1 ~]# tar xzvf harbor-offline-installer-v2.3.1.tgz
[root@docker-server1 ~]# ln -sv /root/harbor /usr/local/
"/usr/local/harbor" -> "/root/harbor"
# 2.配置文件
[root@docker-server1 harbor]# cp harbor.yml.tmpl harbor.yml
[root@docker-server1 harbor]# grep -Ev '#|^$' harbor.yml.tmpl > harbor.yml
[root@docker-server1 harbor]# cat harbor.yml
# 3.执行安装
[root@docker-server1 harbor]# ./prepare
[root@docker-server1 harbor]# ./install.sh
# 之后的启动关闭可以通过docker-compose管理自动生成docker-compose.yml文件
[root@docker-server1 harbor]# ls
common docker-compose.yml harbor.yml install.sh prepare
common.sh harbor.v2.3.1.tar.gz harbor.yml.tmpl LICENSE
```
**web访问**
默认用户名和密码在harbor.yml中设置为harbor_admin_password: Harbor12345
**推送镜像**
参考网站推送教学
```bash
# 登录
[root@docker-server2 ~]# grep 'insecure' /etc/docker/daemon.json
"insecure-registries":["192.168.175.10"]
[root@docker-server2 ~]# systemctl daemon-reload & systemctl restart docker
[root@docker-server2 ~]# docker login 192.168.204.135
Username: admin
Password:
...
Login Succeeded
[root@admin harbor]# docker-compose restart
# 推送
[root@docker-server2 nginx]# docker tag nginx:v1 192.168.175.10/eagles/nginx:v1
[root@docker-server2 nginx]# docker push 192.168.175.10/eagles/nginx:v1
```

View File

@@ -0,0 +1,90 @@
# 存储引擎
Docker 存储引擎是 Docker 容器管理文件系统的核心组件负责镜像层与容器层的组织、读写操作及资源隔离。其核心在于通过联合文件系统UnionFS写时复制Copy-on-Write, CoW机制实现高效存储管理。
## 分层存储与联合挂载
Docker镜像由多个只读层Layer叠加而成容器运行时会在镜像层之上创建可写层。所有层通过 UnionFS 联合挂载到同一视图,上层文件覆盖下层同名文件,但底层数据保持不变。
UnionFS联合文件系统是一种分层、轻量级且高性能的文件系统技术通过将多个目录分支联合挂载到同一目标目录形成统一的文件系统视图。核心机制如下:
- **分层存储**UnionFS 通过分层结构管理文件,每个层(分支)可以是只读或可写。不同层中相同路径的文件会被上层覆盖,但底层内容保持不变。
- **只读层和可写层**:只读层​​通常为基础镜像或依赖文件,不可修改;可写层​​:容器运行时新增的层,所有修改均在此层记录。
## 写时复制CoW机制
当容器需要修改文件时,存储引擎将文件从底层只读层复制到可写层进行修改,而非直接修改原始数据。这种机制减少冗余存储,允许多容器共享同一镜像层,显著降低磁盘占用。
## 工作原理
- **联合挂载Unio Mount**将多个物理目录如目录A和B挂载到同一虚拟目录如目录C合并后的视图包含所有分支目录的内容。若存在同名文件优先显示上层文件。
- **访问优先级**用户访问文件时UnionFS按层从上至下搜索返回第一个匹配的文件。例如可写层优先级高于只读层。
- **数据隔离与共享**:不同容器共享同一基础镜像层,但各自的可写层独立,实现资源复用与运行时隔离。
# 主流存储引擎
Docker支持多种存储驱动不同驱动在性能、稳定性、适用场景上存在差异
| 存储驱动 | 特点 | 优势 | 局限性 | 适用场景 |
|---------|------|------|--------|----------|
| OverlayFS | • Linux内核原生支持≥3.18<br>• 使用overlay2驱动<br>• 仅需一个只读层和一个可写层<br>• 结构简单且性能优异 | • 启动速度快<br>• 适合生产环境<br>• 支持高效的文件系统层叠<br>• 节省存储空间 | • 层数限制通常≤127层<br>• 频繁小文件写入可能增加I/O开销 | • 通用容器环境<br>• 需要高性能的生产系统<br>• 对存储空间敏感的场景 |
| AUFS | • 通过多层联合挂载实现CoW<br>• 稳定性较好<br>• 未集成到内核,需额外安装 | • 内存利用率高<br>• 适合旧版Linux系统 | • 高并发写入性能较差<br>• 逐渐被OverlayFS替代 | • 旧版Linux系统<br>• 对内存资源敏感的环境<br>• 低并发写入场景 |
| Device Mapper | • 基于块设备映射<br>• 使用"thin pool"技术<br>• 支持动态扩容 | • 支持精细的存储控制<br>• 适合块级存储需求<br>• 动态扩容能力强 | • 配置复杂<br>• 需预分配存储池<br>• 可能导致空间浪费 | • 数据库容器<br>• 需要精细存储控制的场景<br>• 大规模存储系统 |
| Btrfs/ZFS | • 支持高级功能(快照/去重/压缩)<br>• 文件系统级别的功能丰富 | • Btrfs写入密集型场景表现优异<br>• ZFS提供数据完整性校验<br>• 支持高效压缩 | • 对系统内核版本依赖性强<br>• 稳定性与兼容性需验证 | • 需要数据快照功能<br>• 写入密集型应用<br>• 对数据完整性要求高的场景 |
# Overlay2
## 工作原理
Docker 默认存储驱动。通过以下三个主要目录来管理文件系统:
- **`LowerDir`**:只读层,包含基础镜像的文件系统。可以有多个只读层,每层都是独立的。
- **`UpperDir`**:读写层,用于存储容器运行时的文件系统变更(即 diff 层)。
- **`MergedDir`**:联合挂载后的视图,容器看到的完整文件系统。它将 `LowerDir``UpperDir` 合并为一个统一的文件系统视图。
- **`WorkDir`**是系统内部使用的临时目录用于处理写时复制CoW和元数据操作。其内容在挂载时会被清空且运行时不可见一般不要动。
当启动一个容器时Overlay2 会将镜像层(`LowerDir`)和容器层(`UpperDir`)联合挂载到 `MergedDir`,容器通过这个目录看到完整的文件系统。
## 实践案例1
通过 Overlay2 挂载目录,理解镜像层与容器层的交互逻辑。
```shell
# 1.目录结构 & 文件
mkdir -p /mnt/overlay2/{lower,upper,work,merged}
echo "基础文件内容" > /mnt/overlay2/lower/base.txt
echo "初始配置" > /mnt/overlay2/lower/config.yaml
# 2.挂载Overlay2
mount -t overlay overlay \
-o lowerdir=/mnt/overlay2/lower,upperdir=/mnt/overlay2/upper,workdir=/mnt/overlay2/work \
/mnt/overlay2/merged
# 3.模拟容器操作
echo "容器修改内容" > /mnt/overlay2/merged/base.txt
## CoW机制生效
[root@master01 ~]# cat /mnt/overlay2/upper/base.txt
容器修改内容
[root@master01 ~]# cat /mnt/overlay2/lower/base.txt
基础文件内容
## 文件名 #40b 是 OverlayFS 在处理文件操作时生成的临时标识符,用于跟踪操作状态
rm /mnt/overlay2/merged/config.yaml
# 卸载并清理
umount /mnt/overlay2/merged
rm -rf /mnt/overlay2/*
```
## 实践案例2
验证运行容器的存储结构,并能快速定位。
```shell
# LowerDir、MergedDir、UpperDir、WorkDir
[root@master01 ~]# docker inspect 379c14648fbb
[root@master01 ~]# docker exec -it 3eb8f9c95ab4 touch /root/1.txt
[root@master01 ~]# ls /var/lib/docker/overlay2/869a09f00704dbbb396d8be90d52b73870073c0a294deceac24a69afbe1a4310/diff/root/
1.txt
```
# 扩展阅读
存储引擎文档:
https://docs.docker.com/storage/storagedriver/select-storage-driver/
存储引擎血案:
https://www.cnblogs.com/youruncloud/p/5736718.html

124
Docker/Docker安装.md Normal file
View File

@@ -0,0 +1,124 @@
# Docker服务端软件选择
Docker CECommunity Edition社区版和 Docker EEEnterprise Edition企业版是 Docker 产品的两个主要版本,它们之间的主要区别在于目标用户、功能集、支持和维护等方面:
| 对比项 | Docker CE | Docker EE |
|--------|-----------|------------|
| **目标用户** | 面向个人开发者、小团队以及技术爱好者,主要用于开发和测试环境 | 面向大型企业和组织,提供企业级的功能和支持 |
| **功能集** | 提供基本的容器化功能,包括构建、运行和共享容器 | 除了包含 CE 版本的所有功能外,还提供了额外的企业级特性,如增强的安全、管理、可扩展性和集成性 |
| **支持和维护** | 社区支持,适合自我解决问题的开发者 | 提供商业支持和专业服务,适合需要稳定运行环境的企业 |
| **安全性** | 安全性相对较低,适合非生产环境 | 提供更高级的安全特性,如镜像扫描、安全策略和合规性报告 |
| **管理** | 通常不需要复杂的管理工具 | 提供 Docker Universal Control Plane (UCP) 和 Docker Trusted Registry (DTR) 等管理工具,帮助企业更有效地管理容器环境 |
| **成本** | 免费 | 需要购买许可证 |
| **更新和生命周期** | 更新频繁,可能包含实验性功能,生命周期较短 | 更新周期更稳定,更注重稳定性和兼容性,生命周期较长 |
# Docker快速安装
```shell
[root@docker-server ~]# chmod +x install_docker_ce.sh
[root@docker-server ~]# ./install_docker_ce.sh
```
# Docker快速使用
```shell
# 拉取镜像
[root@docker-server ~]# docker pull nginx
# 启动容器
[root@docker-server ~]# docker run --name nginx_container_test -d -p 8080:80 nginx
# 进入容器
[root@docker-server ~]# docker exec -it nginx_container_test bash
echo 'docker nginx test' > /usr/share/nginx/html/index.html
curl 192.168.88.10:8080
# 查看容器
[root@docker-server ~]# docker ps
# 停止容器
[root@docker-server ~]# docker stop nginx_container_test
```
# Docker信息
```bash
[root@docker-server ~]# docker info
```
| 配置项 | 说明 |
| :---| :---|
| **容器状态信息** ||
| Containers | 系统中所有容器的总数。包括运行中、已暂停和已停止的容器,用于整体容器资源评估 |
| Running | 当前正在运行的容器数量。这些容器正常运行并提供服务,是系统负载的主要来源 |
| Paused | 已暂停运行的容器数量。这些容器状态被临时冻结,通常用于调试或资源回收 |
| Stopped | 已停止运行的容器数量。这些容器可能是任务完成或异常停止,需要定期清理 |
| **系统基础信息** ||
| Images | 本地已下载的Docker镜像总数。包括基础镜像、中间镜像和应用镜像影响存储空间使用 |
| Server Version | Docker引擎版本号。决定了可用特性和兼容性建议在生产环境保持版本统一 |
| **存储相关配置** ||
| Storage Driver | 容器存储驱动类型如overlay2、devicemapper。影响容器I/O性能推荐使用overlay2 |
| Backing Filesystem | 底层文件系统类型如ext4、xfs。影响存储性能和可靠性生产环境推荐使用xfs |
| Supports d_type | 文件系统d_type支持状态。对overlay2驱动至关重要确保启用以获得最佳性能 |
| Native Overlay Diff | 原生Overlay差异存储支持。优化镜像层存储效率减少磁盘空间占用 |
| **日志与资源管理** ||
| Logging Driver | 容器日志收集驱动如json-file、syslog。影响日志管理和问题排查能力 |
| Cgroup Driver | 容器资源限制驱动systemd/cgroupfs。建议与系统init保持一致避免资源管理冲突 |
| **功能组件信息** ||
| Plugins | Docker插件列表。包括存储、网络等扩展功能按需启用以增强系统能力 |
| Volume | 数据卷插件配置。用于持久化存储和容器间数据共享,确保数据可靠性 |
| Network | 网络驱动类型。支持bridge、host、overlay等模式根据应用场景选择 |
| **运行时与安全** ||
| Default Runtime | 默认容器运行时通常为runc。可选择其他OCI兼容运行时以满足特定需求 |
| Security Options | 安全特性配置。包括AppArmor、SELinux等建议在生产环境中启用以增强安全性 |
| **系统资源信息** ||
| Kernel Version | 内核版本信息。影响Docker功能和性能建议使用推荐的内核版本 |
| Operating System | 操作系统信息。包括发行版和版本号,影响兼容性和可用特性 |
| Architecture | CPU架构类型。如x86_64、arm64决定可用的容器镜像类型 |
| CPUs | 可用CPU核心数。影响容器并发能力建议预留部分资源给系统使用 |
| Total Memory | 系统总内存。决定可分配给容器的最大内存,建议合理规划以避免资源竞争 |
| **其他配置** ||
| Docker Root Dir | Docker运行时根目录。存储容器和镜像数据建议使用单独分区 |
| Registry | 默认镜像仓库地址。用于拉取和推送镜像,可配置私有仓库加速访问 |
| Experimental | 实验特性状态。包含未稳定功能,生产环境谨慎启用 |
| Live Restore Enabled | 是否启用容器存活恢复功能允许在Docker守护进程重启时保持容器运行|
# 标准化组织OCI
容器技术是一种轻量级的虚拟化技术,用于隔离应用程序及其依赖项,使其能够在不同的环境中一致地运行。除了 Docker 之外,还有其他多种容器运行时和工具,例如 CoreOS 的 rkt、阿里的 Pouch 和红帽的 Podman。为了确保容器生态系统的标准性和可持续发展Linux 基金会、Docker、微软、红帽、谷歌和 IBM 等公司在 2015 年 6 月共同成立了 **Open Container Initiative (OCI)** 组织。
## OCI目标
OCI 的主要目标是制定开放的容器规范以确保不同容器技术之间的可移植性和互操作性。目前OCI 已经发布了两个核心规范:
1. **Runtime Spec**:定义了容器运行时的规范,包括容器的生命周期管理、资源隔离和安全等。
2. **Image Format Spec**:定义了容器镜像的格式和元数据,确保镜像可以在不同的容器运行时之间共享和运行。
通过遵循这些规范,不同的容器运行时和工具可以实现互操作性,从而推动容器技术的标准化和健康发展。
## 容器运行时
容器运行时是真正运行容器的地方,它需要与操作系统的内核紧密合作,为容器提供隔离的运行环境。以下是目前主流的三种容器运行时:
**1. LXC (Linux Containers)**
- **简介**LXC 是 Linux 上早期的容器运行时,它利用 Linux 内核的 Namespace 和 Cgroups 技术来实现进程隔离和资源管理。
- **特点**
- 提供了完整的 Linux 系统环境,支持多种 Linux 发行版。
- 早期 Docker 也曾使用 LXC 作为其默认的运行时。
- **适用场景**:适用于需要完整 Linux 系统环境的容器化应用。
**2. Runc**
- **简介**Runc 是目前 Docker 默认的容器运行时,它是一个轻量级的命令行工具,用于运行和管理容器。
- **特点**
- 完全遵循 OCI 的 Runtime Spec 规范,确保与 OCI 标准的兼容性。
- 由于其轻量级和高性能的特点Runc 已经成为许多容器运行时的底层实现。
- **适用场景**:适用于需要高性能和轻量级容器运行环境的场景。
**3. Rkt (Rocket)**
- **简介**Rkt 是由 CoreOS 开发的容器运行时,旨在提供一个安全、可靠且符合 OCI 规范的容器运行环境。
- **特点**
- 与 Docker 不同Rkt 本身是一个独立的容器运行时,不依赖 Docker 的守护进程。
- 提供了更好的安全性和隔离性,例如通过 AppArmor 和 SELinux 等安全机制。
- **适用场景**:适用于对安全性要求较高的容器化应用。
容器技术的发展离不开标准化的推动。OCI 通过制定 Runtime Spec 和 Image Format Spec为容器运行时和工具提供了统一的标准确保了不同容器技术之间的互操作性和可移植性。目前主流的容器运行时如 LXC、Runc 和 Rkt都遵循这些规范从而推动了容器技术的广泛应用和发展。

View File

@@ -0,0 +1,169 @@
# 容器
## 使用场景
开发一个杀手级的 Web 应用它包含三个主要组件React 前端、Python API 和 MySQL 数据库。如果你想开发这个项目,你必须安装 Node、Python 和 MySQL。
- **如何确保团队中开发人员使用的Python版本一致**
- **如何确保应用运行所需的版本不和现有生产环境版本冲突?**
什么是容器独立进程。React 前端、Python API、MySQL 都在独立的环境中运行,并与其他组件完全隔离。
## 容器和虚拟机
![img](01.docker介绍与安装/容器与虚拟机对比.png)
| **虚拟化** | **容器** |
| ------------------------------------------------ | ------------------------------------------------------- |
| 隔离性强有独立的GUEST OS | 共享内核和OS隔离性弱! |
| 虚拟化性能差(>15%) | 计算/存储无损耗无Guest OS内存开销(~200M) |
| 虚拟机镜像庞大(十几G~几十G), 且实例化时不能共享 | Docker容器镜象200~300M且公共基础镜象实例化时可以共享 |
| 虚拟机镜象缺乏统一标准 | Docker提供了容器应用镜象事实标准OCI推动进一 步标准化 |
| 虚拟机创建慢(>2分钟) | 秒级创建(<10s)相当于建立索引 |
| 虚拟机启动慢(>30s) 读文件逐个加载 | 秒级(<1s,不含应用本身启动) |
| 资源虚拟化粒度低单机10~100虚拟机 | 单机支持1000+容器密度很高适合大规模的部署 |
**对比总结**
- **资源利用率更高**一台物理机可以运行数百个容器但一般只能运行数十个虚拟机
- **开销更小**不需要启动单独的虚拟机占用硬件资源
- **启动速度更快**可以在数秒内完成启动
# 容器管理
## 创建容器
`Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]`
```shell
[root@docker-server ~]# docker create -it --name nginx-test nginx bash
```
## 启动容器
`Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]`
```shell
[root@docker-server ~]# docker start nginx
```
## 重启容器
`Usage: docker restart [OPTIONS] CONTAINER [CONTAINER...]`
```shell
[root@docker-server ~]# docker restart nginx
```
## 停止容器
`Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]`
```shell
[root@docker-server ~]# docker stop nginx
```
## 列出容器
`Usage: docker ps [OPTIONS]`
```shell
[root@docker-server ~]# docker ps -a
```
## 运行容器
`Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]`
```shell
# 等同于 create + start
[root@docker-server ~]# docker run -it centos:latest bash
# 指定DNS
[root@docker-server ~]# docker run -it --rm --dns 8.8.8.8 centos bash
# 端口映射
## 前台启动随机映射端口
[root@docker-server ~]# docker run -P nginx
## 方式1本地端口80映射到容器80端口
[root@docker-server ~]# docker run -p 80:80 --name nginx-1 nginx:latest
## 方式2本地ip本地端口容器端口
[root@docker-server ~]# docker run -p 192.168.204.135:80:80 --name nginx-1 nginx:latest
## 方式3本地ip本地随机端口容器端口
[root@docker-server ~]# docker run -p 192.168.175.10::80 --name nginx-1 nginx:latest
## 方式4本地ip本地端口容器端口/协议默认为tcp协议
[root@docker-server ~]# docker run -p 192.168.175.10:80:80/tcp --name nginx-1 nginx:latest
## 查看容器已经映射的端口
[root@docker-server ~]# docker port nginx-1
# 传递运行命令
[root@docker-server ~]# docker run -it centos:latest /bin/bash
[root@docker-server ~]# docker run -it centos:latest cat /etc/hosts
# 单次运行,容器退出后自动删除
[root@docker-server ~]# docker run --name hello_world_test --rm hello-world
# 后台运行
[root@docker-server ~]# docker run -d -P --name nginx-2 nginx
```
**参数说明:**
| 选项 | 说明 |
|:---|:---|
| -d | 以守护进程方式在后台运行容器默认为否适用于运行需要持续在线的服务 |
| -i | 保持标准输入打开即使未连接也保持STDIN打开常与-t一起使用 |
| -P | 通过NAT机制将容器标记暴露的端口自动映射到主机的随机临时端口 |
| -p | 手动指定容器端口映射格式主机(宿主)端口:容器端口 |
| -t | 分配一个伪终端pseudo-TTY通常与-i配合使用以提供交互式shell |
| -v | 挂载主机上的文件卷到容器内格式主机目录:容器目录[:权限] |
| --rm | 容器退出后自动删除容器不能与-d同时使用适用于临时测试场景 |
| -e | 设置容器内的环境变量格式-e 变量名=变量值 |
| -h | 指定容器的主机名便于容器间通过主机名访问 |
| --name | 指定容器的别名方便管理和访问容器 |
| --cpu-shares | 设置容器使用CPU的相对权重默认1024数值越高优先级越高 |
| --cpuset-cpus | 限制容器使用特定的CPU核心0-3使用前4个核心或0,2使用第13核心 |
| -m | 限制容器可使用的最大内存量支持的单位bkmg-m 512m
## 挂起/恢复容器
`Usage: docker pause CONTAINER [CONTAINER...]`
```shell
[root@docker-server ~]# docker pause nginx-2
```
`Usage: docker unpause CONTAINER [CONTAINER...]`
```shell
[root@docker-server ~]# docker unpause nginx-2
```
## 进入容器
`Usage: docker exec [OPTIONS] CONTAINER`
```shell
[root@docker-server ~]# docker exec -it nginx-2 bash
# attach不推荐: 所有使用此方式进入容器的操作都是同步显示的且exit容器将被关闭且使用exit退出后容器关闭
[root@docker-server ~]# docker attach nginx-2
# nsenter: 通过pid进入到容器内部不过可以使用docker inspect获取到容器的pid
[root@docker-server ~]# nsenter -t $(docker inspect -f "[.State.Pid]" nginx-2) -m -u -i -n -p
```
## 导入/导出容器
`Usage: docker export [OPTIONS] CONTAINER`
```shell
[root@docker-server ~]# docker export -o /opt/nginx.tar nginx-2
```
`Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]`
```shell
[root@docker-server ~]# docker import /opt/nginx.tar nginx:v50
```
**适用场景:** 主要用来制作基础镜像比如从一个ubuntu镜像启动一个容器然后安装一些软件和进行一些设置后使用docker export保存为一个基础镜像然后把这个镜像分发给其他人使用作为基础的开发环境。(因为export导出的镜像只会保留从镜像运行到export之间对文件系统的修改所以只适合做基础镜像)
## 查看容器日志
`Usage: docker logs [OPTIONS] CONTAINER`
```shell
[root@docker-server ~]# docker logs nginx-2
```
## 删除容器
`Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]`
```shell
[root@docker-server ~]# docker rm -f nginx-2
```

View File

@@ -0,0 +1,518 @@
# Docker compose单机编排
## Docker Compose 简介
当在宿主机上启动多个容器时,手动操作会比较繁琐,且容易出错。在这种情况下,推荐使用 Docker 单机编排工具 **Docker Compose**。Docker Compose 是 Docker 官方提供的一个开源工具,用于管理和编排多个容器。它可以解决容器之间的依赖关系,简化容器的创建、启动和停止操作。
例如,启动一个 Nginx 前端服务时,可能需要调用后端的 Tomcat 服务,而 Tomcat 容器又依赖于数据库。在这种嵌套依赖关系中Docker Compose 可以按照正确的顺序启动这些容器确保每个容器在启动时都能正确依赖所需的其他容器。因此Docker Compose 完全可以替代 `docker run` 来创建和管理容器。
## Docker Compose 项目结构
Docker Compose 项目将所管理的容器分为三层,分别是:
- **工程Project**:工程是 Docker Compose 管理的最高层级,通常对应一个包含多个服务的应用场景。一个工程可以包含多个服务,这些服务通过 `docker-compose.yml` 文件进行定义。
- **服务Service**:服务是工程中的一个逻辑单元,通常对应一个容器模板。服务定义了容器的镜像、环境变量、端口映射等配置。一个服务可以启动多个容器实例。
- **容器Container**容器是服务的具体运行实例。Docker Compose 会根据服务的定义创建并管理容器。
通过这种分层结构Docker Compose 能够高效地管理和编排多个容器,简化复杂的容器依赖关系,提高开发和部署效率。
# 基础环境准备
## Docker compose部署
yum安装docker-compese
从 Docker 20.10 版本开始Docker Compose 被集成到了 Docker 中作为插件使用而不是独立的命令行工具所以说在20以后的版本中我们直接使用docker compose(中间用空格隔开)即可
```bash
# 旧版本中安装docker-compose的方式
[root@localhost ~]# yum install -y epel-release
[root@localhost ~]# yum install docker-compose.noarch -y
[root@localhost ~]# docker-compose version
docker-compose version 1.18.0, build 8dd22a9
docker-py version: 2.6.1
CPython version: 3.6.8
OpenSSL version: OpenSSL 1.0.2k-fips 26 Jan 2017
```
## 相关参数
```bash
# docker-compose --help
Define and run multi-container applications with Docker.
## Usage
```bash
docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
docker-compose -h|--help
```
### 选项说明
- `-f`, `--file FILE`:指定 Compose 模板文件,默认为 `docker-compose.yml`
- `-p`, `--project-name NAME`:指定项目名称,默认将使用当前所在目录名称作为项目名。
- `--verbose`:显示更多输出信息。
- `--log-level LEVEL`:定义日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)。
- `--no-ansi`:不显示 ANSI 控制字符。
- `-v`, `--version`:显示版本。
### 命令选项
以下命令需要在 `docker-compose.yml``yaml` 文件所在目录里执行。
- `build`:构建或重新构建服务中定义的镜像。
- `bundle`:从当前 `docker-compose` 文件生成一个以 `<当前目录>` 为名称的 JSON 格式的 Docker Bundle 文件,用于离线部署。
- `config -q`:验证 Compose 文件格式是否正确,若无错误则不输出任何内容。
- `create`:创建服务所需的所有容器,但不启动它们。
- `down`:停止和删除所有容器、网络、卷,以及由 `docker-compose up` 创建的镜像(可选)。
- `events`:实时显示容器的日志事件,支持指定日志格式(如 JSON
- `exec`:在指定的容器中运行一个命令。
- `help`:显示指定命令的帮助信息。
- `images`:列出所有由 `docker-compose` 创建的镜像。
- `kill`:强制终止正在运行的容器。
- `logs`:查看容器的日志输出。
- `pause`:暂停服务中的所有容器。
- `port`:查看服务的端口映射情况。
- `ps`:列出所有由 `docker-compose` 管理的容器。
- `pull`:从镜像仓库拉取服务中定义的镜像。
- `push`:将服务中定义的镜像推送到镜像仓库。
- `restart`:重启服务中的所有容器。
- `rm`:删除所有已停止的容器。
- `run`:在指定服务中运行一个命令,创建一个临时容器。
- `scale`:设置指定服务运行的容器数量。
- `start`:启动已创建但未运行的服务。
- `stop`:停止正在运行的服务。
- `top`:显示正在运行的容器的进程信息。
- `unpause`:恢复之前暂停的服务。
- `up`:构建、创建并启动所有服务,如果服务已存在则重新启动。
### 示例 `docker-compose.yml` 文件
以下是一个详细的 `docker-compose.yml` 文件示例,包含注释和可能用到的所有指令。这个示例展示了如何定义一个包含多个服务(如 Nginx、Tomcat 和 MySQL的 Docker Compose 项目。
```yaml
# docker-compose.yml
# 定义版本,指定 Compose 文件的格式版本
version: '3.8'
# 定义服务
services:
# 定义 MySQL 服务
mysql:
# 使用的镜像名称
image: mysql:5.7
# 容器的名称
container_name: mysql_container
# 环境变量,用于设置 MySQL 的 root 密码
environment:
MYSQL_ROOT_PASSWORD: mypassword
MYSQL_DATABASE: mydb
# 持久化数据卷
volumes:
- mysql_data:/var/lib/mysql
# 端口映射,将宿主机的 3306 端口映射到容器的 3306 端口
ports:
- "3306:3306"
# 重启策略,始终重启
restart: always
# 网络配置,连接到默认网络
networks:
- my_network
# 定义 Tomcat 服务
tomcat:
# 使用的镜像名称
image: tomcat:9.0
# 容器的名称
container_name: tomcat_container
# 环境变量,设置 Tomcat 的一些配置
environment:
- CATALINA_OPTS=-Xms512M -Xmx1024M
# 持久化 Tomcat 的 webapps 目录
volumes:
- tomcat_webapps:/usr/local/tomcat/webapps
# 端口映射,将宿主机的 8080 端口映射到容器的 8080 端口
ports:
- "8080:8080"
# 依赖关系,确保 MySQL 服务先启动
depends_on:
- mysql
# 重启策略,始终重启
restart: always
# 网络配置,连接到默认网络
networks:
- my_network
# 定义 Nginx 服务
nginx:
# 使用的镜像名称
image: nginx:1.19
# 容器的名称
container_name: nginx_container
# 持久化 Nginx 的配置文件和静态资源
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./html:/usr/share/nginx/html
# 端口映射,将宿主机的 80 端口映射到容器的 80 端口
ports:
- "80:80"
# 依赖关系,确保 Tomcat 服务先启动
depends_on:
- tomcat
# 重启策略,始终重启
restart: always
# 网络配置,连接到默认网络
networks:
- my_network
# 定义卷
volumes:
# 定义 MySQL 数据卷
mysql_data:
driver: local
# 定义 Tomcat webapps 卷
tomcat_webapps:
driver: local
# 定义网络
networks:
# 定义默认网络
my_network:
driver: bridge
```
### 文件说明
1. **版本**
- `version: '3.8'`:指定 Compose 文件的格式版本。不同的版本支持不同的功能和语法。
2. **服务**
- `services`:定义项目中包含的所有服务。
- 每个服务(如 `mysql``tomcat``nginx`)都有自己的配置,包括:
- `image`:指定使用的 Docker 镜像。
- `container_name`:指定容器的名称。
- `environment`:设置环境变量。
- `volumes`:定义数据卷,用于持久化数据或挂载本地文件。
- `ports`:定义端口映射。
- `depends_on`:定义服务之间的依赖关系。
- `restart`:设置重启策略。
- `networks`:定义服务所属的网络。
3. **卷**
- `volumes`:定义数据卷,用于持久化数据。这里定义了两个卷:`mysql_data``tomcat_webapps`
4. **网络**
- `networks`:定义网络,用于服务之间的通信。这里定义了一个默认的桥接网络 `my_network`
### 可能用到的 Docker Compose 命令
以下是在 `docker-compose.yml` 文件所在目录中可以执行的常用命令:
- **`docker-compose up`**:构建、创建并启动所有服务。如果服务已存在,则重新启动。
- **`docker-compose down`**:停止并删除所有容器、网络、卷以及由 `docker-compose up` 创建的镜像(可选)。
- **`docker-compose build`**:构建或重新构建服务中定义的镜像。
- **`docker-compose create`**:创建服务所需的所有容器,但不启动它们。
- **`docker-compose start`**:启动已创建但未运行的服务。
- **`docker-compose stop`**:停止正在运行的服务。
- **`docker-compose restart`**:重启服务中的所有容器。
- **`docker-compose kill`**:强制终止正在运行的容器。
- **`docker-compose rm`**:删除所有已停止的容器。
- **`docker-compose logs`**:查看容器的日志输出。
- **`docker-compose exec`**:在指定的容器中运行一个命令。
- **`docker-compose ps`**:列出所有由 `docker-compose` 管理的容器。
- **`docker-compose config`**:验证 Compose 文件格式是否正确,若无错误则不输出任何内容。
- **`docker-compose bundle`**:从当前 `docker-compose` 文件生成一个 JSON 格式的 Docker Bundle 文件,用于离线部署。
- **`docker-compose pull`**:从镜像仓库拉取服务中定义的镜像。
- **`docker-compose push`**:将服务中定义的镜像推送到镜像仓库。
- **`docker-compose scale`**:设置指定服务运行的容器数量。
# 实战案例
## 启动单个容器
一、编写docker-compose文件
```yaml
[root@localhost ~]# mkdir -pv docker-compose/nginx
[root@localhost ~]# cd docker-compose/nginx
[root@localhost nginx]# pwd
/root/docker-compose/nginx
[root@localhost nginx]# vim docker-compose.yml
# docker-compose.yml
services:
nginx:
image: nginx
container_name: nginx_web1
restart: always
ports:
- "80:80"
volumes:
- /data/web:/usr/share/nginx/html
```
二、启动容器
```bash
[root@localhost nginx]# docker compose up -d
[+] Running 2/2
✔ Network nginx_default Created 0.0s
✔ Container nginx_web1 Started 0.2s
[root@localhost nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09d091a0c1a1 nginx "/docker-entrypoint.…" 29 seconds ago Up 28 seconds 0.0.0.0:80->80/tcp, [::]:80->80/tcp nginx_web1
# 不指定网络的话会默认创建一个类型为bridge的网络
[root@localhost nginx]# docker network ls
NETWORK ID NAME DRIVER SCOPE
36f9c2f8e090 bridge bridge local
93ffe4510dd0 host host local
d0456365c64d nginx_default bridge local
61f0d0d9b051 none null local
```
三、访问测试
## 启动多个容器
一、编辑docker-compose文件
```bash
[root@localhost docker]# cat docker-compose.yml
service-nginx:
image: nginx
container_name: nginx_web1
ports:
- "80:80"
service-tomcat:
image: tomcat
container_name: tomcat_web1
ports:
- "8080:8080"
[root@localhost docker]# docker-compose up -d
nginx_web1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:80->80/tcp,:::80->80/tcp
tomcat_web1 catalina.sh run Up 0.0.0.0:8080->8080/tcp,:::8080-
>8080/tcp
```
## 定义数据卷挂载
- 创建数据卷目录和文件
```bash
[root@localhost docker]# mkdir -p /data/nginx
[root@localhost docker]# echo 'docker nginx' > /data/nginx/index.html
```
- 编辑配置文件
```bash
[root@localhost docker]# cat docker-compose.yml
service-nginx:
image: nginx
container_name: nginx_web1
volumes:
- /data/nginx/:/usr/share/nginx/html
ports:
- "80:80"
service-tomcat:
image: tomcat
container_name: tomcat_web1
ports:
- "8080:8080"
```
- 访问测试
```bash
[root@localhost docker]# curl localhost
docker nginx
```
# Docker Compose部署LNMP实战
## 部署LNMP环境
- 使用docker-compose实现编排nginx+phpfpm+mysql容器
- 部署一个博客系统
- 首先编写一个如下的配置文件
```yaml
services:
web:
image: nginx:latest
restart: always
ports:
- 80:80
volumes:
- /data/lnmp/nginx/conf.d:/etc/nginx/conf.d
- /data/lnmp/nginx/html:/usr/share/nginx/html
- /data/lnmp/nginx/log:/var/log/nginx
depends_on:
- php
- mysql
networks:
- lnmp-network
php:
image: php:7.4-fpm
restart: always
volumes:
- /data/lnmp/nginx/html:/usr/share/nginx/html
networks:
- lnmp-network
depends_on:
- mysql
mysql:
image: mysql:5.7
restart: always
volumes:
- /data/lnmp/dbdata:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: "123456"
MYSQL_DATABASE: login
networks:
- lnmp-network
networks:
lnmp-network:
driver: bridge
volumes:
dbdata: null
```
运行docker-compose.yml
```bash
[root@localhost lnmp]# docker compose up -d
[root@localhost lnmp]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
825b8630de47 nginx:latest "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 0.0.0.0:80->80/tcp, [::]:80->80/tcp lnmp-web-1
d1d6339ba0d7 lnmp-php "docker-php-entrypoi…" 4 minutes ago Up 4 minutes 9000/tcp lnmp-php-1
109672c0b1f0 mysql:5.7 "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 3306/tcp, 33060/tcp lnmp-mysql-1
```
`/data/lnmp/nginx/conf.d/default.conf` 中写入nginx 的配置文件
```shell
server {
listen 80;
root /usr/share/nginx/html;
location / {
index index.php index.html;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
```
准备探针测试
`/data/lnmp/nginx/html/info.php` 中准备php探针
```shell
<?php
phpinfo();
?>
```
测试数据库连接
`/data/lnmp/nginx/html/mysql.php`中准备php探针
```shell
<?php
$dbhost = "mysql";
$dbuser = "root";
$dbpass = "123456";
$db = "login";
$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $db) or exit("数据库连接失败!");
echo "数据库连接成功";
?>
```
访问测试后发现连接mysql报错提示找不到mysqli_connect()看来是官方构建的php没有mysqli模块
所以我们需要定制带有mysqli模块的php编写如下dockerfile
```shell
FROM php:7.4-fpm
ENV TZ=Asia/Shanghai
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
&& sed -i -e 's|security.debian.org/\? |security.debian.org/debian-security |g' \
-e 's|security.debian.org|mirrors.ustc.edu.cn|g' \
-e 's|deb.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' \
/etc/apt/sources.list \
&& apt-get update && apt-get install -y \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
&& docker-php-ext-configure gd -with-freetype -with-jpeg \
&& docker-php-ext-install -j$(nproc) gd mysqli && docker-php-ext-enable mysqli
```
然后修改 docker-compose.yml 文件如下
```yaml
# docker-compose build lnmp
services:
web:
image: nginx:latest
restart: always
ports:
- 80:80
volumes:
- /data/lnmp/nginx/conf.d:/etc/nginx/conf.d
- /data/lnmp/nginx/html:/usr/share/nginx/html
- /data/lnmp/nginx/log:/var/log/nginx
depends_on:
- php
- mysql
networks:
- lnmp-network
php:
# image: dockerfile:php:7.4-fpm
# 这里来让compose自动构建指定dockerfile的文件位置即可
build:
context: .
dockerfile: dockerfile
restart: always
volumes:
- /data/lnmp/nginx/html:/usr/share/nginx/html
networks:
- lnmp-network
depends_on:
- mysql
mysql:
image: mysql:5.7
restart: always
volumes:
- /data/lnmp/dbdata:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: "123456"
MYSQL_DATABASE: login
networks:
- lnmp-network
networks:
lnmp-network:
driver: bridge
volumes:
dbdata: null
```
测试验证
```shell
[root@localhost lnmp]# docker compose up -d
[root@localhost lnmp]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
825b8630de47 nginx:latest "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 0.0.0.0:80->80/tcp, [::]:80->80/tcp lnmp-web-1
d1d6339ba0d7 lnmp-php "docker-php-entrypoi…" 4 minutes ago Up 4 minutes 9000/tcp lnmp-php-1
109672c0b1f0 mysql:5.7 "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 3306/tcp, 33060/tcp lnmp-mysql-1
```

View File

@@ -0,0 +1,91 @@
# 存储
## 介绍
默认情况下,容器内创建的所有文件都存储在可写的容器层上,该层位于只读、不可变的图像层之上。
写入容器层的数据在容器销毁后不会保留。这意味着,如果其他进程需要这些数据,则很难将其从容器中取出。
每个容器的可写层都是唯一的。无法将数据从可写层提取到主机或其他容器。
## 存储挂载选项
| 挂载类型 | 说明 | 使用场景 | 特点 |
|:---------|:-----|:---------|:-----|
| Volume Mounts | Docker管理的持久化数据卷存储在/var/lib/docker/volumes/目录下 | 数据库存储、应用数据持久化 | 独立于容器生命周期、可在容器间共享、支持数据备份和迁移 |
| Bind Mounts | 将宿主机目录或文件直接挂载到容器内 | 开发环境代码热更新、配置文件挂载 | 方便直接操作文件、依赖宿主机文件系统、适合开发调试 |
| Tmpfs Mounts | 将数据临时存储在宿主机内存中 | 敏感数据存储、临时文件存储 | 高性能、数据易失性、增加内存占用 |
| Named Pipes | 在容器间建立命名管道进行通信 | 容器间进程通信、数据流传输 | 低延迟、进程间通信、适合流式数据传输 |
# Volume mounts
## 管理操作
`Usage: docker volume create [OPTIONS] [VOLUME]`
`Usage: docker volume ls [OPTIONS]`
`Usage: docker volume inspect [OPTIONS] VOLUME [VOLUME...]`
`Usage: docker volume rm [OPTIONS] VOLUME [VOLUME...]`
`Usage: docker volume prune [OPTIONS]`
```shell
```
## 使用卷启动容器
如果使用不存在的卷启动容器Docker 会为创建该卷。
`Usage: docker run --mount type=volume[,src=<volume-name>],dst=<mount-path>[,<key>=<value>...]`
| 参数 | 说明 | 使用示例 | 最佳实践 |
|:---------|:-----|:---------|:---------|
| source, src | 卷的名称,用于指定要挂载的数据卷 | `src=myvolume` | 使用有意义的名称便于识别和管理 |
| target, dst | 容器内的挂载路径,指定数据卷挂载到容器内的位置 | `dst=/data/app` | 遵循容器内标准目录结构 |
| type | 卷的类型可选值volume、bind、tmpfs默认为volume | `type=volume` | 根据数据持久化需求选择合适类型 |
| readonly, ro | 只读挂载标志,设置后容器内无法修改挂载内容 | `ro=true` | 对配置文件等静态内容建议只读挂载 |
| volume-subpath | 卷的子路径,只挂载数据卷中的指定子目录 | `volume-subpath=/config` | 用于精确控制挂载范围,提高安全性 |
| volume-opt | 卷的额外选项,用于指定卷的特定行为 | `volume-opt=size=10G` | 根据实际需求配置,避免过度使用 |
| volume-nocopy | 创建卷时不从容器复制数据 | `volume-nocopy=true` | 用于避免不必要的数据复制,提高性能 |
```shell
docker run -d --name devtest --mount source=myvol2,target=/app nginx:latest
```
`Usage: docker run -v [<volume-name>:]<mount-path>[:opts]`
```shell
docker run -d --name devtest -v myvol2:/app nginx:latest
```
## 实践案例
**需求**运行MySQL容器并支持久化存储进行一次数据备份数据恢复测试验证。
```shell
```
# Bind mounts
使用绑定挂载时,主机上的文件或目录将从主机挂载到容器中。
如果将目录绑定挂载到容器上的非空目录中,则目录的现有内容被绑定挂载隐藏。
## 使用绑定挂载启动容器
```shell
Usage:
docker run --mount type=bind,src=<host-path>,dst=<container-path>[,<key>=<value>...]
docker run -v <host-path>:<container-path>[:opts]
```
| 参数 | 说明 | 使用场景 | 最佳实践 |
|:---------|:-----|:---------|:---------|
| readonly, ro | 将挂载点设置为只读模式,容器内无法修改挂载的内容 | 配置文件、静态资源文件挂载 | 对于不需要容器内修改的内容,建议使用只读模式增加安全性 |
| rprivate | 使挂载点的挂载事件不会传播到其他挂载点 | 默认的挂载传播模式 | 适用于大多数场景,确保挂载隔离性 |
| rshared | 使挂载点的挂载事件双向传播 | 需要在多个挂载点间共享挂载事件的场景 | 谨慎使用,可能影响容器隔离性 |
| rslave | 使挂载点的挂载事件单向传播(从主机到容器) | 需要容器感知主机挂载变化的场景 | 在特定场景下使用,如动态存储管理 |
| rbind | 递归绑定挂载,包含所有子目录 | 需要完整复制目录结构的场景 | 确保目录结构完整性,但注意性能开销 |
## 实践案例
**需求**启动Nginx容器并挂载宿主机nginx配置文件和主页目录容器内无权限修改相关内容测试验证。
```shell
```
# 扩展阅读
tmpfs mounts: https://docs.docker.com/engine/storage/tmpfs/
volumes plugins: https://docs.docker.com/engine/extend/legacy_plugins/

View File

@@ -0,0 +1,69 @@
# 网络
容器网络是指容器能够连接和通信的能力,无论是容器之间还是与外部。
# 用户自定义网络
可以创建自定义的用户定义网络,并将多个容器连接到同一网络。一旦连接到用户定义的网络,容器可以使用容器 IP 地址或容器名称相互通信。
`Usage: docker network create [OPTIONS] NETWORK`
```shell
# 使用bridge网络驱动创建网络并启动容器
docker network create -d bridge my-net
docker run --network=my-net -itd --name=container1 busybox
docker run --network=my-net -itd --name=container2 busybox
# 测试网络连接
[root@master01 ~]# docker exec -it container1 ping www.baidu.com
[root@master01 ~]# docker exec -it container1 cat /etc/hosts | grep 172
172.19.0.3 1adf81f08c86
[root@master01 ~]# docker exec -it container1 ping container2
```
# 网络驱动
Docker服务安装完成之后默认在每个宿主机会生成一个名称为docker0的网卡其ip地址都是172.17.0.1/16并且会生成三种不同类型的网络。
```shell
[root@master01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
fe2c2cabeff7 bridge bridge local
459b4a9926ed host host local
526baa2eb8f6 none null local
```
| Docker网络模式 | 配置 | 说明 | 使用场景 |
|--------------|------|------|----------------|
| host模式 | --net=host | 容器和宿主机共享Network namespace直接使用宿主机网络栈。容器不会获得独立的Network namespace也不会配置独立的IP和端口。性能开销最小但缺少网络隔离。 | 适用于对网络性能要求极高的场景,如高性能计算、流媒体服务等
| container模式 | --net=container:NAME_or_ID | 容器和指定的容器共享Network namespace。新容器使用已存在容器的网络栈共享IP地址和端口范围。保持了其他资源如文件系统、进程等的隔离。 | 最典型应用是Kubernetes的Pod实现
| none模式 | --net=none | 容器拥有独立的Network namespace但不做任何网络配置。容器没有网卡、IP、路由等网络配置需要手动配置网络。 | 适用于对安全性要求极高的场景
| bridge模式 | --net=bridge | 默认网络模式。Docker daemon创建docker0虚拟网桥通过veth pair连接容器实现容器间的通信。支持端口映射和地址转换提供基本的网络隔离。 | 最常用的网络模式
# 工作原理
## none网络类型
![img](docker网络管理/None网络类型.png)
## container网络类型
![img](docker网络管理/Container网络类型.png)
## host网络类型
![img](docker网络管理/Host网络类型.png)
## bridge网络类型
![img](docker网络管理/Bridge网络类型.png)
**veth pair 对应关系**
```shell
[root@master01 ~]# ip link show | grep veth0f | awk '{print $1,$2}'
40: veth0fbbd68@if39
[root@master01 ~]# cat /sys/class/net/veth0fbbd68/ifindex
40
[root@master01 ~]# cat /sys/class/net/veth0fbbd68/iflink
39
[root@master01 ~]# docker exec -it container1 ip link show eth0
39: eth0@if40: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:13:00:03 brd ff:ff:ff:ff:ff:ff
```
# 扩展阅读
数据包过滤和防火墙: https://docs.docker.com/engine/network/packet-filtering-firewalls/
网络驱动https://docs.docker.com/engine/network/drivers/

View File

@@ -0,0 +1,235 @@
# 资源限制
默认情况下容器没有资源限制可以使用主机内核调度程序允许的尽可能多的给定资源docker提供了控制容器可以限制容器使用多少内存或者cpu的方法设置docker run命令的运行时配置标志。
其中一些功能要求宿主机的内核支持Linux功能要检查支持可以使用docker info命令如果内核中禁用了某项功能可能会在输出结尾处看到警告。
## cgroup介绍
在一个容器内部如果不对其做任何资源限制则宿主机会允许其占用无限大的内存空间有时候会因为代码bug程序会一直申请内存直到把宿主机内存占完为了避免此类的问题出现宿主机有必要对容器进行资源分配限制比如cpu、内存等Linux Cgroups的全称是Linux control Groups它最重要的作用就是限制一个进程组能够使用的资源上线包括cpu、内存、磁盘、网络等等。
## 开启cgroup功能
- 验证系统内核层已经默认开启cgroup功能
```bash
[root@docker-server ~]# cat /boot/config-3.10.0-957.el7.x86_64| grep cgroup -i
CONFIG_CGROUPS=y
# CONFIG_CGROUP_DEBUG is not set
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_SCHED=y
CONFIG_BLK_CGROUP=y
# CONFIG_DEBUG_BLK_CGROUP is not set
CONFIG_NETFILTER_XT_MATCH_CGROUP=m
CONFIG_NET_CLS_CGROUP=y
CONFIG_NETPRIO_CGROUP=y
```
- 关于内存的模块
```bash
[root@docker-server ~]# cat /boot/config-3.10.0-957.el7.x86_64 | grep mem -i | grep cg -i
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_MEMCG_KMEM=y
```
# 容器CPU限制
一个宿主机有几十个核心的cpu但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的任务多进程共用一个cpu的核心依赖计数就是为可压缩资源即一个核心cpu可以通过调度而运行多个进程但是在同一个单位时间内只能由一个进程在cpu上运行那么这么多的进程怎么在cpu上执行和调度的呢进程优先级
默认情况下每个容器对主机cpu周期的访问权限是不受限制的但是我们可以人为干扰。
## 相关参数说明
| 参数名 | 默认值 | 说明 | 使用建议 |
|--------|---------|------|----------|
| --cpus | 无限制 | 指定容器可使用的CPU核心数量。例如设置为1.5表示容器最多可使用1.5个CPU核心的计算能力。 | • 推荐使用此参数控制CPU使用<br>• 设置范围建议为0.1-核心总数<br>• 生产环境必须设置 |
| --cpu-period | 100000 | 设置CPU CFS调度周期(单位:微秒)。必须与--cpu-quota一起使用。 | • 不建议单独使用<br>• 建议使用--cpus替代<br>• 默认周期100ms适用大多数场景 |
| --cpu-quota | -1 | 设置CPU CFS配额(单位:微秒)。与--cpu-period配合使用,quota/period的值等效于--cpus的值。 | • 不建议单独使用<br>• 建议使用--cpus替代<br>• 仅在需要精细调度时使用 |
| --cpuset-cpus | 无限制 | 指定容器可以使用的CPU核心编号,如"0,2"表示只能使用0号和2号CPU核心。 | • 适用于CPU绑核场景<br>• 可提高缓存命中率<br>• 注意预留系统CPU资源 |
| --cpuset-mems | 无限制 | 指定容器使用的NUMA节点。仅在NUMA架构服务器中有效。 | • 仅用于NUMA架构<br>• 需了解硬件架构再使用<br>• 一般场景不建议使用 |
| --cpu-shares | 1024 | 设置CPU资源分配的权重值。在CPU竞争时,权重值越大获得更多CPU时间片。 | • 设置范围2-262144<br>• 仅在CPU竞争时有效<br>• 适合差异化CPU分配 |
> **最佳实践建议**
> 1. 生产环境建议使用--cpus参数限制CPU使用
> 2. 按实际需求合理设置CPU限制,预留足够系统资源
> 3. 特殊场景(如CPU密集型)可考虑使用cpuset-cpus绑核
> 4. 避免过度限制导致容器性能问题
## 实践案例
### 案例1: CPU无限制
启动1个进程占用4核cpu未限制容器会把cpu全部占完
```bash
# 查看我们宿主机的cup数量
[root@localhost ~]# top
top - 21:32:49 up 43 min, 2 users, load average: 3.54, 1.82, 0.80
Tasks: 186 total, 1 running, 185 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.5 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
[root@localhost ~]# docker run -it --rm --name test1 tylersmith22/docker-stress-ng --vm 1 --cpu 4
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 1 vm
# 新建窗口查看
[root@localhost ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
c6a795e4c09f test1 396.78% 282.6MiB / 1.703GiB 16.21% 876B / 126B 0B / 0B 7
# 可以看出CPU的使用率大概是400% 因为我们是4个核心单个核心跑满是100%
```
### 案例2: CPU限制
```bash
[root@localhost ~]# docker run -it --rm --cpus 2 --name test1 tylersmith22/docker-stress-ng --vm 1 --cpu 4
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 4 cpu, 1 vm
[root@localhost ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
5b7dcb36d738 test1 200.65% 279.7MiB / 1.703GiB 16.04% 876B / 126B 0B / 0B 7
[root@localhost ~]# top
top - 21:36:15 up 47 min, 3 users, load average: 1.38, 1.92, 1.05
Tasks: 198 total, 8 running, 190 sleeping, 0 stopped, 0 zombie
%Cpu0 : 50.7 us, 1.4 sy, 0.0 ni, 47.2 id, 0.0 wa, 0.7 hi, 0.0 si, 0.0 st
%Cpu1 : 52.9 us, 0.0 sy, 0.0 ni, 47.1 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 50.7 us, 0.0 sy, 0.0 ni, 48.6 id, 0.0 wa, 0.7 hi, 0.0 si, 0.0 st
%Cpu3 : 50.4 us, 0.0 sy, 0.0 ni, 48.9 id, 0.0 wa, 0.7 hi, 0.0 si, 0.0 st
# 并且是平均使用所有的cup核心
```
### 案例3: CPU绑定
```bash
[root@localhost ~]# docker run -it --rm --cpus 2 --cpuset-cpus 0,2 --name test1 tylersmith22/docker-stress-ng --vm 1 --cpu 4
[root@localhost ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
ee11d834dde5 test1 186.68% 1.488GiB / 1.781GiB 83.60% 648B / 0B 44.8GB / 95.7MB 25
[root@localhost ~]# top
top - 21:38:25 up 49 min, 3 users, load average: 0.92, 1.40, 0.96
Tasks: 197 total, 6 running, 191 sleeping, 0 stopped, 0 zombie
%Cpu0 : 97.3 us, 2.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.7 hi, 0.0 si, 0.0 st
%Cpu1 : 0.3 us, 0.0 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
%Cpu2 : 98.3 us, 1.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.7 hi, 0.0 si, 0.0 st
%Cpu3 : 0.0 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.3 hi, 0.0 si, 0.0 st
MiB Mem : 1743.4 total, 457.2 free, 924.2 used, 531.4 buff/cache
MiB Swap: 2048.0 total, 2047.2 free, 0.8 used. 819.2 avail Mem
```
- 基于cpu-shares对cpu进行切分
```bash
[root@localhost ~]# docker run -it --rm -d --cpu-shares 1000 --name test1 tylersmith22/docker-stress-ng --vm 1 --cpu 4
[root@localhost ~]# docker run -it --rm -d --cpu-shares 500 --name test2 tylersmith22/docker-stress-ng --vm 1 --cpu 4
[root@localhost ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
d6dd34edb722 test1 543.41% 819.6MiB / 1.781GiB 44.95% 648B / 0B 102MB / 154MB 13
154b07a94e2f test2 241.15% 711.1MiB / 1.781GiB 39.00% 648B / 0B 406MB / 145MB
```
# 容器MEM限制
## OOM异常
对于Linux主机如果没有足够的内存来执行其他重要的系统任务将会抛出OOM异常内存溢出、内存泄漏、内存异常随后系统会开始杀死进程以释放内存凡是运行在宿主机的进程都有可能被kill包括dockerd和其他的应用程序如果重要的系统进程被kill会导致和该进程相关的服务全部宕机。
产生OOM异常时Dockerd尝试通过调整docker守护程序上的OOM优先级来减轻这些风险以便它比系统上的其他进程更不可能被杀死但是容器的OOM优先级未调整时单个容器被杀死的可能性更大不推荐调整容器的优先级这种方式
## OOM评分机制
Linux会为每个进程计算一个分数最终会将分数最高的进程kill掉。相关参数说明如下:
| 参数名 | 取值范围 | 说明 | 使用建议 |
|--------|----------|------|----------|
| oom_score_adj | -1000 到 1000 | 用于调整进程的OOM评分。值越高进程在内存不足时越容易被kill。设置为-1000时进程永远不会被kernel kill。 | • 关键系统进程建议设置较低值<br>• 非关键应用可保持默认值<br>• 谨慎使用-1000可能导致系统不稳定 |
| oom_adj | -17 到 +15 | 旧版本参数用于调整进程被kill的优先级。值越高越容易被kill-17表示不能被kill。 | • 建议使用oom_score_adj替代<br>• 仅为了兼容旧版本内核<br>• 不推荐在新系统中使用 |
| oom_score | 系统计算得出 | 系统根据进程的内存使用量、CPU时间、运行时长和oom_adj综合计算的得分。得分越高越容易被kill。 | • 用于监控进程的OOM风险<br>• 定期检查高内存占用进程<br>• 发现异常及时优化或限制 |
> **最佳实践建议**
> 1. 为关键进程适当降低OOM评分避免被意外kill
> 2. 定期监控高内存进程的oom_score
> 3. 合理规划内存使用避免触发OOM
> 4. 使用容器时建议设置内存限制
## 相关参数说明
| 参数名 | 说明 | 默认值 | 使用建议 |
|--------|------|--------|----------|
| -m 或 --memory | 容器可使用的最大物理内存量 | 无限制 | • 最小值为4m<br>• 建议根据应用实际需求设置<br>• 生产环境必须设置 |
| --memory-swap | 容器可使用的交换分区和物理内存总和 | -1 (不限制) | • 需要先设置-m参数<br>• 频繁交换会影响性能<br>• 建议生产环境禁用swap |
| --memory-swappiness | 容器使用交换分区的倾向性 | 继承主机设置 | • 范围0-100<br>• 0表示尽量不使用swap<br>• 生产环境建议设为0 |
| --kernel-memory | 容器可使用的最大内核内存量 | 无限制 | • 最小值为4m<br>**不建议使用**<br>• 可能导致主机资源阻塞 |
| --memory-reservation | 内存软限制(小于--memory) | 无限制 | • 系统内存紧张时激活<br>• 作为内存使用预警<br>• 建议设置为--memory的80% |
| --oom-kill-disable | 禁止OOM时杀死容器进程 | false | • 仅在设置-m时有效<br>• 需谨慎使用<br>• 建议保持默认值 |
> **最佳实践建议**
> 1. 生产环境必须设置-m参数限制内存使用
> 2. 建议禁用或限制swap使用
> 3. 合理设置memory-reservation作为预警
> 4. 谨慎使用kernel-memory和oom-kill-disable
## 实践案例
如果一个容器未作内存使用限制,则该容器可以利用到系统内存最大空间,默认创建的容器没有做内存资源限制
### 案例1: MEM无限制
- 拉取容器压测工具镜像
```bash
[root@docker-server1 ~]# docker pull lorel/docker-stress-ng
[root@docker-server1 ~]# docker run -it --rm lorel/docker-stress-ng -help
```
- 使用压测工具开启两个工作进程每个工作进程最大允许使用内存256M且宿主机不限制当前容器的最大内存
```bash
[root@docker-server1 ~]# docker run -it --rm --name test1 lorel/docker-stress-ng --vm 2 --vm-bytes 256m
stress-ng: info: [1] defaulting to a 86400 second run per stressor
stress-ng: info: [1] dispatching hogs: 2 vm
[root@docker-server1 ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
3ca32774fc20 test1 185.16% 514.3MiB / 1.781GiB 28.21% 648B / 0B 0B / 0B 5
```
### 案例2: MEM限制
- 宿主机限制最大内存使用
```bash
[root@docker-server1 ~]# docker run -it --rm -m 256m --name test2 lorel/docker-stress-ng --vm 2 --vm-bytes 256m
[root@docker-server1 ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
bfff488e6185 test1 169.76% 255.8MiB / 256MiB 99.91% 648B / 0B 3.53GB / 10.6GB 5
```
- 可以通过修改cgroup文件值来扩大内存限制缩小会报错
```bash
[root@docker-server1 ~]# cat /sys/fs/cgroup/memory/docker/bfff488e618580b227b5411c91b35517850e95af2ac2225b45180937c14e70c2/memory.limit_in_bytes
```
**内存软限制**
软限制不会真正限制到内存的使用
```bash
[root@docker-server1 ~]# docker run -it --rm -m 256m --memory-reservation 128m --name test1 lorel/docker-stress-ng --vm 2 --vm-bytes 256m
[root@docker-server1 ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
0ffb4b8fdbde test1 174.52% 255.9MiB / 256MiB 99.95% 648B / 0B 5.33GB / 18.1GB 5
```
**交换分区限制**
```bash
[root@docker-server1 ~]# docker run -it --rm -m 256m --memory-swap 512m --name test1 lorel/docker-stress-ng --vm 2 --vm-bytes 256m
```

View File

@@ -0,0 +1,81 @@
# Linux namespace技术
如果一个宿主机运行了N个容器多个容器带来的以下问题怎么解决
1. 怎么样保证每个容器都有不同的文件系统并且能互不影响?
2. 一个docker主进程内的各个容器都是其子进程那么如何实现同一个主进程下不同类型的子进程各个子进程间通信能相互访问吗
3. 每个容器怎么解决IP以及端口分配的问题
4. 多个容器的主机名能一样吗?
5. 每个容器都要不要有root用户怎么解决账户重名问题呢
**解决方案**
namespace 是 Linux 系统的底层概念在内核层实现即有一些不同类型的命名空间都部署在核内各个容器运行在同一个docker主进程并且共用同一个宿主机系统内核各个docker容器运行在宿主机的用户空间每个容器都要有类似于虚拟机一样的相互隔离的运行空间但是容器技术是在一个进程内实现运行指定服务的运行环境并且还可以保护宿主机内核不受其他进程的干扰和影响如文件系统、网络空间、进程空间等。
| 隔离类型 | 功能说明 | 系统调用参数 | 内核版本 | 应用场景 |
| :--- | :--- | :--- | :--- | :--- |
| MNT Namespacemount | 提供磁盘挂载点和文件系统的隔离能力,使容器拥有独立的文件系统层次结构 | CLONE_NEWNS | 2.4.19 | 容器镜像管理、数据卷挂载、持久化存储 |
| IPC NamespaceInter-Process Communication | 提供进程间通信的隔离能力,确保容器内进程通信安全 | CLONE_NEWIPC | 2.6.19 | 容器内消息队列、共享内存、信号量隔离 |
| UTS NamespaceUNIX Timesharing System | 提供主机名和域名的隔离能力,使容器拥有独立的主机标识 | CLONE_NEWUTS | 2.6.19 | 容器主机名管理、集群服务发现 |
| PID NamespaceProcess Identification | 提供进程隔离能力,实现容器内进程树独立管理 | CLONE_NEWPID | 2.6.24 | 容器进程管理、应用程序运行时隔离 |
| Net Namespacenetwork | 提供网络栈的隔离能力包括网络设备、IP地址、路由表等 | CLONE_NEWNET | 2.6.29 | 容器网络配置、跨主机通信、服务端口映射 |
| User Namespaceuser | 提供用户和用户组的隔离能力,增强容器安全性 | CLONE_NEWUSER | 3.8 | 容器权限控制、用户映射、安全策略管理 |
# MNT Namespace
提供磁盘挂载点和文件系统的隔离能力,使容器拥有独立的文件系统层次结构。
```bash
# 在容器内挂载 tmpfs 文件系统
docker run -it --rm ubuntu bash
mkdir /mnt/tmpfs
mount -t tmpfs -o size=100M tmpfs /mnt/tmpfs
# 宿主机上无法看到此挂载点
df -h
```
# IPC Namespace
提供进程间通信的隔离能力,确保容器内进程通信安全。
```shell
# 容器A创建共享内存段
docker run -it --name shm1 --rm ubuntu bash
ipcmk -M 64M # 返回共享内存 ID如 0
# 容器B无法访问
docker run -it --name shm2 --rm ubuntu bash
ipcs -m
```
# UTS Namespace
提供主机名和域名的隔离能力,使容器拥有独立的主机标识。
```shell
# 启动容器并设置主机名
docker run -it --hostname=myapp --rm ubuntu bash
hostname
```
# PID Namespace
提供进程隔离能力,实现容器内进程树独立管理。
```shell
# 查看当前宿主机所有进程
# 查看当前容器内所有进程
```
**那么宿主机的PID与容器内的PID是什么关系**
1. **独立的 PID 命名空间**:
- 每个 Docker 容器都有自己独立的 PID 命名空间。
- 容器内的进程 PID 从 1 开始编号,与宿主机上的 PID 是相互独立的。
2. **PID 映射**:
- 容器内的进程 PID 与宿主机上的进程 PID 之间是有映射关系的。
- 通过 `docker inspect <container_id>` 命令,可以查看容器内进程的 PID 与宿主机上进程 PID 的对应关系。
3. **PID 可见性**:
- 容器内的进程只能看到容器内部的 PID。
- 宿主机上的进程可以看到容器内部的 PID,但容器内的进程无法看到宿主机上的 PID。
4. **PID 隔离**:
- 容器内的进程无法访问或影响宿主机上的其他进程。
- 宿主机上的进程可以访问和管理容器内的进程。
# Net Namespace
提供网络栈的隔离能力包括网络设备、IP地址、路由表等。参考`08.Docker网络管理`
# User Namespace
提供用户和用户组的隔离能力,增强容器安全性。
```shell
# A容器创建用户在B容器里不可见
```

View File

@@ -0,0 +1,155 @@
# 背景介绍
Docker 镜像制作类似于虚拟机的模板制作即按照公司的实际业务将需要安装的软件、相关配置等基础环境配置完成然后将虚拟机再提交为模板最后再批量从模板批量创建新的虚拟机这样可以极大地简化业务中相同环境的虚拟机运行环境的部署工作Docker的镜像制作分为手动制作可自动制作基于 DockerFile ),企业通常都是基于 DockerFile 制作镜像。
# 手动制作nginx镜像
```shell
[root@docker-server ~]# docker run -it ubuntu bash
root@1d8e32ab39d6:/# apt-get update & apt-get install nginx curl vim-tiny -y
root@1d8e32ab39d6:/# echo 'eagleslab nginx' > /var/www/html/index.nginx-debian.html
root@1d8e32ab39d6:/# grep daemon /etc/nginx/nginx.conf
daemon off;
# 提交为镜像
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
[root@docker-server ~]# docker commit -a "v100" -m "my nginx image v1" 0195bc1d0f7b ubuntu_nginx:v1
# 通过ubuntu_nginx:v1镜像启动容器
[root@docker-server ~]# docker run -d -p 8081:80 ubuntu_nginx:v1
[root@docker-server ~]# curl 127.0.0.1:8081
```
**适用场景**:主要作用是将配置好的一些容器复用,再生成新的镜像。
commit是合并了save、load、export、import这几个特性的一个综合性的命令它主要做了
1. 将容器当前的读写层保存成一个新层。
2. 和镜像的历史层一起合并成一个新的镜像
3. 如果原本的镜像有3层commit之后就会有4层最新的一层为从镜像运行到commit之间对文件系统的修改。
# DockerFile制作镜像
DockerFile可以说是一种可以被Docker程序解释的脚本DockerFile是由一条条的命令组成的每条命令对应linux下面的一条命令Docker程序将这些DockerFile指令再翻译成真正的linux命令其有自己的书写方式和支持的命令Docker程序读取DockerFile并根据指令生成Docker镜像相比手动制作镜像的方式DockerFile更能直观地展示镜像是怎么产生的有了写好的各种各样的DockerFIle文件当后期某个镜像有额外的需求时只要在之前的DockerFile添加或者修改相应的操作即可重新生成新的Docker镜像避免了重复手动制作镜像的麻烦。
## 最佳实践
```shell
# Dockerfile
## 使用官方 Ubuntu 22.04 LTS 作为基础镜像
FROM ubuntu:22.04
## 设置环境变量避免交互式提示
ENV DEBIAN_FRONTEND=noninteractive
## 安装 Nginx 并清理缓存(合并操作用于减少镜像层)
RUN apt-get update && \
apt-get install -y \
nginx \
# 安装常用工具(可选)
curl \
vim-tiny && \
# 清理APT缓存
apt-get clean && \
rm -rf /var/lib/apt/lists/*
## 删除默认配置文件(按需保留)
RUN rm /etc/nginx/sites-enabled/default
## 暴露 HTTP 和 HTTPS 端口
EXPOSE 80 443
## 创建日志目录(确保日志可持久化)
RUN mkdir -p /var/log/nginx && \
chown -R www-data:www-data /var/log/nginx
## 添加自定义配置(示例文件需存在于构建上下文)
# COPY nginx.conf /etc/nginx/nginx.conf
# COPY sites-available/ /etc/nginx/sites-available/
## 设置健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/ || exit 1
## 以非root用户运行Ubuntu官方nginx包已使用www-data用户
USER www-data
## 启动Nginx并保持前台运行
CMD ["nginx", "-g", "daemon off;"]
# 通过Dockerfile构建镜像
[root@docker-server ~]# docker build -t nginx:v1 .
```
## 优化说明
- 层合并: 将多个`RUN`指令合并以减少镜像层数
- 缓存清理: 清理APT缓存减小镜像体积
- 安全实践: 非root用户运行只读挂载配置文件
- 可维护性: 显式声明暴露端口;健康检查配置;日志目录持久化
- 稳定性: 指定精确的ubuntu版本禁用Nginx后台模式
## 指令说明
操作指令
| 指令 | 说明 |
| ------ | ---------------------------- |
| `RUN` | 运行指定命令 |
| `CMD` | 启动容器时指定默认执行的命令 |
| `ADD` | 添加内容到镜像 |
| `COPY` | 复制内容到镜像 |
配置指令
| 指令 | 说明 |
| ------------- | ---------------------------------- |
| `ARG` | 定义创建镜像过程中使用的变量 |
| `FROM` | 指定所创建镜像的基础镜像 |
| `LABEL` | 为生成的镜像添加元数据标签信息 |
| `EXPOSE` | 声明镜像内服务监听的端口 |
| `ENV` | 指定环境变量 |
| `ENTRYPOINT` | 指定镜像的默认入口命令 |
| `VOLUME` | 创建一个数据卷挂载点 |
| `USER` | 指定运行容器时的用户名或UID |
| `WORKDIR` | 配置工作目录 |
| `ONBUILD` | 创建子镜像时指定自动执行的操作指令 |
| `STOPSIGNAL` | 指定退出的信号值 |
| `HEALTHCHECK` | 配置所启动容器如何进行健康检查 |
| `SHELL` | 指定默认shell类型 |
**注意事项**
```shell
# RUN
- 运行指定命令
- 每条RUN指令将在当前镜像基础上执行指定命令并提交为新的镜像层
- 当命令较长时可以使用\来换行
# CMD
- 每个Dockerfile只能有一条CMD命令。如果指定了多条命令只有最后一条会被执行
- CMD指令用来指定启动容器时默认执行的命令,支持三种格式:
CMD ["executable","param1","param2"]
CMD command param1 param2`
CMD ["param1","param2"]
# ADD
- 该命令将复制指定的src路径下内容到容器中的dest路径下
- src可以是DockerFIle所在目录的一个相对路径也可以是一个url还可以是一个tar
- dest可以是镜像内绝对路径或者相对于工作目录的相对路径
# COPY
- COPY与ADD指令功能类似当使用本地目录为源目录时推荐使用COPY
# ARG
- 定义创建过程中使用到的变量;比如:HTTP_PROXY 、HTTPS_PROXY 、FTP_PROXY 、NO_PROXY不区分大小写。
# FROM
- 指定所创建镜像的基础镜像为了保证镜像精简可以选用体积较小的Alpin或Debian作为基础镜像
# EXPOSE
- 声明镜像内服务监听的端口:该指令只是起到声明作用,并不会自动完成端口映射
# ENTRYPOINT
- 指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数支持两种格式:
ENTRYPOINT ["executable","param1","param2"]
ENTRYPOINT command param1 param2
- 此时CMD指令指定值将作为根命令的参数
- 每个DockerFile中只能有一个ENTRYPOINT当指定多个时只有最后一个起效
# VOLUME: 创建一个数据卷挂载点
# WORKDIR
- 为后续的RUN、CMD、ENTRYPOINT指令配置工作目录建议使用绝对路径
```

View File

@@ -0,0 +1,118 @@
# 镜像
容器是一个独立的进程,它从哪里获取文件和配置?如何共享这些环境?
镜像是一个标准化包,其中包含运行容器所需的所有文件、二进制文件、库和配置。
- MySQL 镜像会打包数据库二进制文件、配置文件和其他依赖项。
- Python Web 应用镜像会打包 Python 运行时、应用代码及其所有依赖项。
镜像两个重要原则:
1. 镜像是不可变的。一旦创建,就无法修改。只能创建新镜像或在其上进行更改。
2. 镜像由层组成。每一层代表一组文件系统变更,包括添加、删除或修改文件。
# 镜像管理
## 搜索镜像
`Usage: docker search [OPTIONS] TERM`
```bash
# 搜索包含关键字的镜像
[root@docker-server ~]# docker search centos
```
可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、点赞数(表示该镜像的受欢迎程度)、是否官方创建、是否自动创建。默认输出结果按照星级评价进行排序。
![img](docker镜像管理/docker_hub搜索.png)
## 下载镜像
`Usage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]`
```bash
# 下载nginx、centos、hello-world镜像
[root@docker-server ~]# docker pull nginx
[root@docker-server ~]# docker pull centos
[root@docker-server ~]# docker pull hello-world
```
其中NAME是镜像名称TAG是镜像的标签往往用来是表示版本信息通常情况下描述一个镜像需要包括名称+标签如果不指定标签标签的值默认为latest。
## 镜像列表
`Usage: docker image COMMAND`
```bash
# 列出本地所有镜像
[root@docker-server ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest d1a364dc548d 2 weeks ago 133MB
...
# 字段说明
- REPOSITORY镜像仓库名称
- TAG镜像的标签信息
- 镜像ID唯一用来标识镜像如果两个镜像的ID相同说明他们实际上指向了同一个镜像只是具有不同标签名称而已
- CREATED创建时间说明镜像的最后更新时间
- SIZE镜像大小优秀的镜像往往体积都较小
```
## 镜像标签
`Usage: docker image tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]`
```bash
[root@docker-server ~]# docker tag centos:latest mycentos:latest
[root@docker-server ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest d1a364dc548d 2 weeks ago 133MB
...
```
## 镜像信息
`Usage: docker image inspect [OPTIONS] IMAGE [IMAGE...]`
```bash
[root@docker-server ~]# docker inspect centos:latest
[
{
"Id": "sha256:300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55",
"RepoTags": [
"centos:latest",
"mycentos:latest"
]
...
}
]
```
## 镜像创建信息
`Usage: docker image history [OPTIONS] IMAGE`
```bash
[root@docker-server ~]# docker history centos:latest
IMAGE CREATED CREATED BY SIZE COMMENT
300e315adb2f 6 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
...
```
## 镜像导出
`Usage: docker image save [OPTIONS] IMAGE [IMAGE...]`
```bash
[root@docker-server ~]# docker image save centos:latest -o /opt/centos.tar.gz
[root@docker-server ~]# ll /opt/centos.tar.gz
-rw------- 1 root root 216535040 6月 9 10:33 /opt/centos.tar.gz
[root@docker-server ~]# docker image save centos:latest > /opt/centos-1.tar.gz
[root@docker-server ~]# ll /opt/centos-1.tar.gz
-rw-r--r-- 1 root root 216535040 6月 9 10:35 /opt/centos-1.tar.gz
```
## 镜像导入
`Usage: docker image load [OPTIONS]`
```bash
[root@docker-server ~]# docker image load -i /opt/centos.tar.gz
Loaded image: centos:latest
[root@docker-server ~]# docker image load < /opt/centos.tar.gz
Loaded image: centos:latest
```
## 删除镜像
`Usage: docker image rm [OPTIONS] IMAGE [IMAGE...]`
```bash
[root@docker-server ~]# docker image rm nginx:latest
Untagged: nginx:latest
Untagged: nginx@sha256:6d75c99af15565a301e48297fa2d121e15d80ad526f8369c526324f0f7ccb750
[root@docker-server ~]# docker image rm 300e315adb2f
...
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

131
Kubernetes/DashBoard.md Normal file
View File

@@ -0,0 +1,131 @@
# DashBoard
之前在kubernetes中完成的所有操作都是通过命令行工具kubectl完成的。其实为了提供更丰富的用户体验kubernetes还开发了一个基于web的用户界面Dashboard。用户可以使用Dashboard部署容器化的应用还可以监控应用的状态执行故障排查以及管理kubernetes中各种资源。
## 部署Dashboard
1) 下载yaml并运行Dashboard
```shell
# 下载yaml
[root@k8s-master01 ~]# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
# 修改kubernetes-dashboard的Service类型
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort # 新增
ports:
- port: 443
targetPort: 8443
nodePort: 30009 # 新增
selector:
k8s-app: kubernetes-dashboard
# 部署
[root@k8s-master01 ~]# kubectl create -f recommended.yaml
# 查看namespace下的kubernetes-dashboard下的资源
[root@k8s-master01 ~]# kubectl get pod,svc -n kubernetes-dashboard
NAME READY STATUS RESTARTS AGE
pod/dashboard-metrics-scraper-c79c65bb7-zwfvw 1/1 Running 0 111s
pod/kubernetes-dashboard-56484d4c5-z95z5 1/1 Running 0 111s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dashboard-metrics-scraper ClusterIP 10.96.89.218 <none> 8000/TCP 111s
service/kubernetes-dashboard NodePort 10.104.178.171 <none> 443:30009/TCP 111s
```
2创建访问账户获取token
```shell
# 创建账号
[root@k8s-master01-1 ~]# kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard
# 授权
[root@k8s-master01-1 ~]# kubectl create clusterrolebinding dashboard-admin-rb --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin
# 获取账号token
[root@k8s-master01 ~]# kubectl get secrets -n kubernetes-dashboard | grep dashboard-admin
dashboard-admin-token-xbqhh kubernetes.io/service-account-token 3 2m35s
[root@k8s-master01 ~]# kubectl describe secrets dashboard-admin-token-xbqhh -n kubernetes-dashboard
Name: dashboard-admin-token-xbqhh
Namespace: kubernetes-dashboard
Labels: <none>
Annotations: kubernetes.io/service-account.name: dashboard-admin
kubernetes.io/service-account.uid: 95d84d80-be7a-4d10-a2e0-68f90222d039
Type: kubernetes.io/service-account-token
Data
====
namespace: 20 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImJrYkF4bW5XcDhWcmNGUGJtek5NODFuSXl1aWptMmU2M3o4LTY5a2FKS2cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4teGJxaGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiOTVkODRkODAtYmU3YS00ZDEwLWEyZTAtNjhmOTAyMjJkMDM5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmVybmV0ZXMtZGFzaGJvYXJkOmRhc2hib2FyZC1hZG1pbiJ9.NAl7e8ZfWWdDoPxkqzJzTB46sK9E8iuJYnUI9vnBaY3Jts7T1g1msjsBnbxzQSYgAG--cV0WYxjndzJY_UWCwaGPrQrt_GunxmOK9AUnzURqm55GR2RXIZtjsWVP2EBatsDgHRmuUbQvTFOvdJB4x3nXcYLN2opAaMqg3rnU2rr-A8zCrIuX_eca12wIp_QiuP3SF-tzpdLpsyRfegTJZl6YnSGyaVkC9id-cxZRb307qdCfXPfCHR_2rt5FVfxARgg_C0e3eFHaaYQO7CitxsnIoIXpOFNAR8aUrmopJyODQIPqBWUehb7FhlU1DCduHnIIXVC_UICZ-MKYewBDLw
ca.crt: 1025 bytes
```
3通过浏览器访问Dashboard的UI
在登录页面上输入上面的token
![image-20200520144548997](DashBoard/image-20200520144548997.png)
出现下面的页面代表成功
![image-20200520144959353](DashBoard/image-20200520144959353.png)
## 使用DashBoard
本章节以Deployment为例演示DashBoard的使用
**查看**
选择指定的命名空间`dev`,然后点击`Deployments`查看dev空间下的所有deployment
![img](DashBoard/image-20200520154628679.png)
**扩缩容**
`Deployment`上点击`规模`,然后指定`目标副本数量`,点击确定
![img](DashBoard/image-20200520162605102.png)
**编辑**
`Deployment`上点击`编辑`,然后修改`yaml文件`,点击确定
![image-20200520163253644](DashBoard/image-20200520163253644.png)
**查看Pod**
点击`Pods`, 查看pods列表
![img](DashBoard/image-20200520163552110.png)
**操作Pod**
选中某个Pod可以对其执行日志logs、进入执行exec、编辑、删除操作
![img](DashBoard/image-20200520163832827.png)
> Dashboard提供了kubectl的绝大部分功能这里不再一一演示
# kuboard
## 特点介绍
相较于 Kubernetes Dashboard 等其他 Kubernetes 管理界面Kuboard 的优势更加明显官方网站https://kuboard.cn/
## 安装教程
https://kuboard.cn/v4/install/quickstart.html
安装好后链接上集群,最终界面如下
![image-20240918150900081](DashBoard/image-20240918150900081.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

5
Kubernetes/Kubernetes.md Normal file
View File

@@ -0,0 +1,5 @@
Kubernetes 是一个开源的容器编排引擎用于自动化部署、扩展和管理容器化应用程序。该开源项目由云原生计算基金会CNCF托管。
[kubernetes官网地址](https://kubernetes.io/)
[kubernetes项目地址](https://github.com/kubernetes)

View File

@@ -0,0 +1,166 @@
# 云计算的三种模式
## 单机到云原生的演变
![img](kubernetes概述/云原生演变.png)
## IAAS
Infrastructure as a Service 基础设施即服务
![img](kubernetes概述/IAAS.png)
## PAAS
Platform as a service 平台即服务
![img](kubernetes概述/PAAS.png)
## SAAS
Software as a Service
# 容器编排工具
## Borg
Borg 是 Google 早期开发的集群管理系统,用于管理大规模的容器化⼯作负载。它是 Google 内部的⼀个关键基础设施,自 2003 年开始使用。
## Omega
Omega 是 Borg 的⼀个后继系统,于 2013 年开始开发,旨在解决 Borg 在大规模、动态、多租户环境下多租户、声明式配置。
## kubernetes
Kubernetes 是一个可移植的、可扩展的开源平台用于管理容器化的工作负载和服务可促进声明式配置和自动化。kubernetes 拥有一个庞大且快速增长的生态系统。kubernetes 的服务、支持和工具广泛可用。
kubernetes 这个名字源于希腊于意为舵手或飞行员。k8s 这个缩写是因为k和s之间有八个字符的关系。google 在 2014 年开源了 kubernetes 项目。kubernetes建立在 google 在大规模运行生产工作负载方面拥有十几年的经验的基础上,结合了社区中最好的想法和实践。
# kubernetes优势
- 服务发现和负载均衡
- 存储编排(添加任何本地或云服务器)
- 自动部署和回滚
- 自动分配 CPU/内存资源
- 弹性伸缩
- 自我修复(需要时启动新容器)
- Secret安全相关信息和配置管理
- 大型规模的支持
- 每个节点的Pod数量不超过 110
- 节点数不超过 5000
- Pod总数不超过 150000
- 容器总数不超过 300000
- 开源
![img](kubernetes概述/kubernetes优势.png)
## kubernetes现状
![img](kubernetes概述/kubernetes现状.png)
# kubernetes组件
![img](kubernetes概述/kubernetes集群组件.png)
一个kubernetes集群由一组被称作节点的机器组成。这些节点上运行Kubernetes所管理的容器化应用。集群具有至少一个工作节点。
工作节点会托管Pod而Pod就是作为应用负载的组件。控制平面管理集群中的工作节点和Pod。在生产环境中控制平面通常跨多台计算机运行一个集群通常运行多个节点提供容错性和高可用性。
## 控制平面组件Control Plane Components
控制平面组件会为集群做出全局决策,比如资源的调度。以及检测和响应集群事件,例如当不满足部署的 `replicas`字段时要启动新的Pod
控制平面组件可以在集群中的任何节点上运行。然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件,并且不会在此计算机上运行用户容器。
### kube-apiserver
`kube-apiserver`负责公开Kubernetes API处理接受请求的工作是控制平面的前端。
`kube-apiserver`设计上考虑了水平扩缩,所以可通过部署多个实例来进行扩缩,并在这些实例之间平衡流量。
### etcd
一致且高可用的键值存储用作Kubernetes所有集群数据的后台数据库。
### kube-scheduler
`kube-scheduler`负责监视新创建的、未指定运行节点node的Pods并选择节点来让Pod在上面运行。
调度决策考虑的因素包括单个Pod及Pods集合的资源需求、软硬件及策略约束、亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。
### kube-controller-manager
`kube-controller-manager`负责运行控制器进程,控制器通过 API 服务器监控集群的公共状态,并致力于将当前状态转变为期待状态。
不同类型的控制器如下:
- 节点控制器Node Controller负责在节点出现故障时进行通知和响应。
- 任务控制器Job Controller监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成。
- 端点分片控制器EndpointSlice controller填充端点分片EndpointSlice对象以提供 Service 和 Pod 之间的链接)。
- 服务账号控制器ServiceAccount controller为新的命名空间创建默认的服务账号ServiceAccount
- ...
## Node组件
节点组件会在每个节点上运行,负责维护运行的 Pod 并提供 Kubernetes 运行环境。
### kubelet
`kubelet`会在集群中每个节点node上运行。它保证容器containers都运行在Pod中。
`kubelet`接收一组通过各类机制提供给它的PodSpec确保这些 PodSpec 中描述的容器处于运行状态且健康。kubelet不会管理不是由 Kubernetes创建的容器。
### kube-proxy
kube-proxy 是集群中每个节点node上所运行的网络代理 实现 Kubernetes 服务Service 概念的一部分。
kube-proxy 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。
如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。 否则kube-proxy 仅做流量转发。
### 容器运行时Container Runtime
这个基础组件使Kubernetes能够有效运行容器。 它负责管理 Kubernetes 环境中容器的执行和生命周期。
Kubernetes支持许多容器运行环境例如 containerd、 CRI-O 以及 Kubernetes CRI (容器运行环境接口) 的其他任何实现。
## 插件Addons
插件使用Kubernetes资源DaemonSet、Deployment等实现集群功能。因为这些插件提供集群级别的功能插件中命名空间域的资源属于`kube-system`命名空间。
### DNS
尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes 集群都应该有集群 DNS因为很多示例都需要 DNS 服务。
集群DNS是多个 DNS 服务器一起工作,为 Kubernetes 服务提供 DNS 记录。
Kubernetes 启动的容器会自动将这些 DNS 服务器配置其 DNS 搜索列表中。
### Web 界面(仪表盘)
Dashboard 是 Kubernetes 集群的通用的、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身, 并进行故障排。
### 容器资源监控
容器资源监控 将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中, 并提供浏览这些数据的界面。
### 集群层面日志
集群层面日志机制负责将容器的日志数据保存到一个集中的日志存储中, 这种集中日志存储提供搜索和浏览接口。
### 网络插件
网络插件是实现容器网络接口CNI规范的软件组件。它们负责为 Pod 分配 IP 地址,并使这些 Pod 能在集群内部相互通信。
# 扩展阅读
[Kubernetes CRI (容器运行环境接口)](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md)
[网络插件](https://v1-29.docs.kubernetes.io/zh-cn/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/)
[集群网络](https://v1-29.docs.kubernetes.io/zh-cn/docs/concepts/cluster-administration/addons/#networking-and-network-policy)
[控制器列表]()
[ETCD](https://etcd.io/docs/)

310
Kubernetes/Pod与网络.md Normal file
View File

@@ -0,0 +1,310 @@
# pod概念
<img src="pod与网络/image-20240904112128424.png" alt="image-20240904112128424" style="zoom:33%;" />
首先我们要明确一个概念Kubernetes并不是只支持Docker这一个容器运行时Kubernetes通过CRI这个抽象层支持除Docker之外的其他容器运行时比如rkt甚至支持客户自定义容器运行时。
- 第一个原因借助CRI这个抽象层使得Kubernetes不依赖于底层某一种具体的容器运行时实现技术而是直接操作podpod内部再管理多个业务上紧密相关的用户业务容器这种架构便于Kubernetes做扩展。
- 第二个原因我们假设Kubernetes没有pod的概念而是直接管理容器那么一组容器作为一个单元假设其中一个容器死亡了此时这个单元的状态应该如何定义呢应该理解成整体死亡还是个别死亡
这个问题不易回答的原因是因为包含了这一组业务容器的逻辑单元没有一个统一的办法来代表整个容器组的状态这就是Kubernetes引入pod的概念并且每个pod里都有一个Kubernetes系统自带的pause容器的原因通过引入pause这个与业务无关并且作用类似于Linux操作系统守护进程的Kubernetes系统标准容器以pause容器的状态来代表整个容器组的状态。
<img src="pod与网络/image-20240904112421964.png" alt="image-20240904112421964" style="zoom: 33%;" />
- 第三个原因pod里所有的业务容器共享pause容器的IP地址以及pause容器mount的Volume通过这种设计业务容器之间可以直接通信文件也能够直接彼此共享。
## Pause特性
- Pod内部第一个启动的容器
- 初始化网络栈
- 挂载需要的存储卷
- 回收僵⼫进程
- 容器与Pause容器共享名字空间(Network、PID、IPC)
# 实战基于Docker模拟pod
- 编写Nginx配置文件
```bash
cat <<EOF>> nginx.conf
error_log stderr;
events {
worker_connections 1024;
}
http {
access_log /dev/stdout combined;
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:2368;
}
}
}
EOF
```
- 启动容器
```bash
docker run --name pause -p 8080:80 -d k8s.gcr.io/pause:3.1
docker run --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause -d nginx
docker run -d --name ghost -e NODE_ENV=development --net=container:pause --ipc=container:pause --pid=container:pause ghost
```
![image-20240904115715394](pod与网络/image-20240904115715394.png)
# kubernetes网络
## 基本概述
Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平的网络空间中这在GCEGoogle Compute Engine里面是现成的网络模型Kubernetes假定这个网络已经存在。而在私有云里搭建Kubernetes集群就不能假定这个网络已经存在了。我们需要自己实现这个网络假设将不同节点上的Docker容器之间的互相访问先打通然后运行Kubernetes。
## 网络模型原则
- 在不使用网络地址转换(NAT)的情况下集群中的Pod能够与任意其他Pod进行通信
- 在不使用网络地址转换(NAT)的情况下在集群节点上运行的程序能与同一节点上的任何Pod进行通信
- 每个Pod都有自己的IP地址IP-per-Pod并且任意其他Pod都可以通过相同的这个地址访问它
## CNI
借助CNI标准Kubernetes可以实现容器网络问题的解决。通过插件化的方式来集成各种网络插件实现集群内部网络相互通信只要实现CNI标准中定义的核心接口操作ADD将容器添加到网络DEL从网络中删除一个容器CHECK检查容器的网络是否符合预期等。CNI插件通常聚焦在容器到容器的网络通信。
![image-20240904134937631](pod与网络/image-20240904134937631.png)
CNI的接口并不是指HTTPgRPC这种接口CNI接口是指对可执行程序的调用exec可执行程序Kubernetes节点默认的CNI插件路径为/opt/cni/bin
```bash
[root@master01 ~]# ls /opt/cni/bin/
bandwidth calico dhcp firewall host-device install loopback portmap sbr tap vlan
bridge calico-ipam dummy flannel host-local ipvlan macvlan ptp static tuning vrf
```
CNI通过JSON格式的配置文件来描述网络配置当需要设置容器网络时由容器运行时负责执行CNI插件并通过CNI插件的标准输入stdin来传递配置文件信息通过标准输出stdout接收插件的执行结果。从网络插件功能可以分为五类
![image-20240904135319098](pod与网络/image-20240904135319098.png)
- **Main插件**
- 创建具体网络设备
- bridge网桥设备连接container和host
- ipvlan为容器增加ipvlan网卡
- loopbackIO设备
- macvlan为容器创建一个MAC地址
- ptp创建一对Veth Pair
- vlan分配一个vlan设备
- host-device将已存在的设备移入容器内
- **IPAM插件**
- 负责分配IP地址
- dhcp容器向DHCP服务器发起请求给Pod发放或回收IP地址
- host-local使用预先配置的IP地址段来进行分配
- static为容器分配一个静态IPv4/IPv6地址主要用于debug
- **META插件**
- 其他功能的插件
- tuning通过sysctl调整网络设备参数
- portmap通过iptables配置端口映射
- bandwidth使用TokenBucketFilter来限流
- sbr为网卡设置source based routing
- firewall通过iptables给容器网络的进出流量进行限制
- **Windows插件**专门用于Windows平台的CNI插件win-bridge与win-overlay网络插件
- **第三方网络插件**第三方开源的网络插件众多每个组件都有各自的优点及适应的场景难以形成统一的标准组件常用有Flannel、Calico、Cilium、OVN网络插件
![image-20240904135633230](pod与网络/image-20240904135633230.png)
## 网络插件
<img src="pod与网络/image-20240904135815763.png" alt="image-20240904135815763" style="zoom:33%;" />
### 人气数据
数据更新时间2024-09-04
| 提供商 | 项目 | Stars | Forks | Contributors |
| ------- | --------------------------------------- | ----- | ----- | ------------ |
| Canal | https://github.com/projectcalico/canal | 714 | 100 | 20 |
| Flannel | https://github.com/flannel-io/flannel | 8.7k | 2.9k | 235 |
| Calico | https://github.com/projectcalico/calico | 5.9k | 1.3k | 353 |
| Weave | https://github.com/weaveworks/weave/ | 6.6k | 668 | 87 |
| Cilium | https://github.com/cilium/cilium | 19.7k | 2.9k | 803 |
功能说明
| 提供商 | 网络模型 | 路由分发 | 网络策略 | 网格 | 外部数据存储 | 加密 | Ingress/Egress策略 |
| ------- | ------------------------ | -------- | -------- | ---- | -------------- | ---- | ------------------ |
| Canal | 封装(VXLAN) | 否 | 是 | 否 | K8s API | 是 | 是 |
| Flannel | 封装(VXLAN) | 否 | 否 | 否 | K8s API | 是 | 中 |
| Calico | 封装(VXLANIPIP),未封装 | 是 | 是 | 是 | Etcd 和K8s API | 是 | 是 |
| Weave | 封装 | 是 | 是 | 是 | 否 | 是 | 是 |
| Cilium | 封装(VXLAN) | 是 | 是 | 是 | Etcd和K8sAPI | 是 | 是 |
- 网络模型:封装或未封装。
- 路由分发一种外部网关协议用于在互联网上交换路由和可达性信息。BGP可以帮助进行跨集群pod之间的网络。此功能对于未封装的CNI网络插件是必须的并且通常由BGP完成。如果你想构建跨网段拆分的集群路由分发是一个很好的功能。
- 网络策略Kubernetes提供了强制执行规则的功能这些规则决定了哪些service可以使用网络策略进行相互通信。这是从Kubernetes1.7起稳定的功能,可以与某些网络插件一起使用。
- 网格允许在不同的Kubernetes集群间进行service之间的网络通信。
- 外部数据存储具有此功能的CNI网络插件需要一个外部数据存储来存储数据。
- 加密:允许加密和安全的网络控制和数据平面。
- Ingress/Egress策略允许你管理Kubernetes和非Kubernetes通信的路由控制。
## 网络模型
- underlay network非封装网络
- 现实的物理基础层网络设备
- underlay就是数据中心场景的基础物理设施保证任何两个点路由可达其中包含了传统的网络技术
- overlay network封装网络
- 一个基于物理网络之上构建的逻辑网络
- overlay是在网络技术领域指的是一种网络架构上叠加的虚拟化技术模式
- Overlay网络技术多种多样一般采用TRILL、VxLan、GRE、NVGRE等隧道技术
### overlay
![image-20240904140729187](pod与网络/image-20240904140729187.png)
### underlay
![image-20240904140805520](pod与网络/image-20240904140805520.png)
## calico
或许是目前最主流的网络解决方案-calico
Calico是一个纯三层的虚拟网络它没有复用docker的docker0网桥而是自己实现的calico网络不对数据包进行额外封装不需要NAT和端口映射
### calico架构
<img src="pod与网络/image-20240904141203196.png" alt="image-20240904141203196" style="zoom:50%;" />
- Felix
- 管理网络接口
- 编写路由
- 编写ACL
- 报告状态
- birdBGPClient
- BGPClient将通过BGP协议⼴播告诉剩余calico节点从而实现网络互通
- confd
- 通过监听etcd以了解BGP配置和全局默认值的更改。Confd根据ETCD中数据的更新动态生成BIRD配置文件。当配置文件更改时confd触发BIRD重新加载新文件
### VXLAN
- VXLAN即Virtual Extensible LAN虚拟可扩展局域网是Linux本身支持的一网种网络虚拟化技术。VXLAN可以完全在内核态实现封装和解封装工作从而通过“隧道”机制构建出覆盖网络OverlayNetwork
- 基于三层的”二层“通信层即vxlan包封装在udp数据包中要求udp在k8s节点间三层可达
- 二层即vxlan封包的源mac地址和目的mac地址是自己的vxlan设备mac和对端vxlan设备mac实现通讯。
![image-20240904143234102](pod与网络/image-202409041432341png)
- 数据包封包封包在vxlan设备上将pod发来的数据包源、目的mac替换为本机vxlan网卡和对端节点vxlan 网卡的mac。外层udp目的ip地址根据路由和对端vxlan的mac查fdb表获取
- 优势只要k8s节点间三层互通可以跨网段对主机网关路由没有特殊要求。各个node节点通过vxlan设备实现基于三层的”二层”互通,三层即vxlan包封装在udp数据包中要求udp在k8s节点间三层可达二层即vxlan封包的源mac地址和目的mac地址是自己的vxlan设备mac和对端vxlan设备mac
- 缺点需要进行vxlan的数据包封包和解包会存在一定的性能损耗
![image](pod与网络/image.png)
配置方法
```yaml
# 在calico的配置文件中。如下配置
# Auto-detect the BGP IP address.
- name: IP
value: "autodetect"
# Enable IPIP
- name: CALICO_IPV4POOL_IPIP
value: "Never"
# Enable or Disable VXLAN on the default IP pool.
- name: CALICO_IPV4POOL_VXLAN
value: "Always"
# Enable or Disable VXLAN on the default IPv6 IP pool.
- name: CALICO_IPV6POOL_VXLAN
value: "Always"
# 分割线========================
# calico_backend: "bird"
calico_backend: "vxlan"
# - -felix-live
# - -bird-live
```
查看验证
<img src="pod与网络/image-20240904150012585.png" alt="image-20240904150012585" style="zoom: 80%;" />
VXLAN不需要BGP来建立节点间的邻接关系
<img src="pod与网络/image-20240904150019409.png" alt="image-20240904150019409" style="zoom: 80%;" />
### IPIP
- Linux原生内核支持
- IPIP隧道的工作原理是将源主机的IP数据包封装在一个新的IP数据包中新的IP数据包的目的地址是隧道的另一端。在隧道的另一端接收方将解封装原始IP数据包并将其传递到目标主机。IPIP隧道可以在不同的网络之间建立连接例如在IPv4网络和IPv6网络之间建立连接。
<img src="pod与网络/image-20240904145716982.png" alt="image-20240904145716982" style="zoom:33%;" />
- 数据包封包封包在tunl0设备上将pod发来的数据包的mac层去掉留下ip层封包。外层数据包目的ip地址根据路由得到。
- 优点只要k8s节点间三层互通可以跨网段对主机网关路由没有特殊要求。
- 缺点需要进行IPIP的数据包封包和解包会存在一定的性能损耗
![image-20240904145738269](pod与网络/image-20240904145738269.png)
配置方法
```yaml
# 在calico的配置文件中。如下配置
# Auto-detect the BGP IP address.
- name: IP
value: "autodetect"
# Enable IPIP
- name: CALICO_IPV4POOL_IPIP
value: "Always"
# Enable or Disable VXLAN on the default IP pool.
- name: CALICO_IPV4POOL_VXLAN
value: "Never"
# Enable or Disable VXLAN on the default IPv6 IP pool.
- name: CALICO_IPV6POOL_VXLAN
value: "Never"
```
查看验证
<img src="pod与网络/image-20240904150123553.png" alt="image-20240904150123553" style="zoom:80%;" />
IPIP模式需要BGP来建立节点间的邻接关系VXLAN不需要
<img src="pod与网络/image-20240904150127316.png" alt="image-20240904150127316" style="zoom:80%;" />
### BGP
边界网关协议Border Gateway Protocol,BGP是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或前缀表来实现自治系统AS之间的可达性属于矢量路由协议。BGP不使用传统的内部网关协议IGP的指标而使用基于路径、网络策略或规则集来决定路由。因此它更适合被称为矢量性协议而不是路由协议。BGP通俗的讲就是讲接入到机房的多条线路如电信、联通、移动等融合为一体实现多线单IPBGP机房的优点服务器只需要设置一个IP地址最佳访问路由是由网络上的骨⼲路由器根据路由跳数与其它技术指标来确定的不会占用服务器的任何系统。
![image-20240904151200463](pod与网络/image-20240904151200463.png)
- 数据包封包:不需要进行数据包封包
- 优点不用封包解包通过BGP协议可实现pod网络在主机间的三层可达
- 缺点跨网段时配置较为复杂网络要求较高主机网关路由也需要充当BGPSpeaker。
配置方法
```yaml
# 在calico的配置文件中。如下配置
# Auto-detect the BGP IP address.
- name: IP
value: "autodetect"
# Enable IPIP
- name: CALICO_IPV4POOL_IPIP
value: "Off"
# Enable or Disable VXLAN on the default IP pool.
- name: CALICO_IPV4POOL_VXLAN
value: "Never"
# Enable or Disable VXLAN on the default IPv6 IP pool.
- name: CALICO_IPV6POOL_VXLAN
value: "Never"
```
查看验证
<img src="pod与网络/image-20240904151418487.png" alt="image-20240904151418487" style="zoom:80%;" />
<img src="pod与网络/image-20240904151425020.png" alt="image-20240904151425020" style="zoom:80%;" />
<img src="pod与网络/image-20240904151434684.png" alt="image-20240904151434684" style="zoom:80%;" />

673
Kubernetes/Pod控制器.md Normal file
View File

@@ -0,0 +1,673 @@
# 控制器
在Kubernetes中运⾏了⼀系列控制器来确保集群的当前状态与期望状态保持⼀致它们就是Kubernetes集群内部的管理控制中⼼或者说是”中⼼⼤脑”。例如ReplicaSet控制器负责维护集群中运⾏的Pod数量Node控制器负责监控节点的状态并在节点出现故障时执⾏⾃动化修复流程确保集群始终处于预期的⼯作状态。
![image-20240910143437460](Pod控制器/image-20240910143437460.png)
- ReplicationController 和 ReplicaSet
- Deployment
- DaemonSet
- StateFulSet
- Job/CronJob
- Horizontal Pod Autoscaling
# ReplicationController
ReplicationController(RC)⽤来确保容器应⽤的副本数始终保持在⽤户定义的副本数即如果有容器异常退出会⾃动创建新的Pod来替代⽽如果异常多出来的容器也会⾃动回收
在新版本的Kubernetes中建议使⽤ReplicaSet(RS)来取代ReplicationController。ReplicaSet跟ReplicationController没有本质的不同只是名字不⼀样并且ReplicaSet⽀持集合式的selector
```yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: rc-demo
spec:
replicas: 3
selector:
app: rc-demo
template:
metadata:
name: rc-demo
labels:
app: rc-demo
spec:
containers:
- name: rc-demo-container
image: aaronxudocker/myapp:v1.0
ports:
- containerPort: 80
# kubectl delete pod rc-demo-fl7vb
# kubectl label pod rc-demo-fl7vb app=rc-demo1 --overwrite
```
RS控制器
```yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rc-ml-demo
spec:
replicas: 3
selector:
matchLabels:
app: rc-ml-demo
template:
metadata:
name: rc-ml-demo
labels:
app: rc-ml-demo
spec:
containers:
- name: rc-ml-demo-container
image: aaronxudocker/myapp:v1.0
ports:
- containerPort: 80
```
`selector.matchExpressions`
rs在标签选择器上除了可以定义键值对的选择形式还支持matchExpressions字段可以提供多种选择。
目前支持的操作包括:
- Inlabel的值在某个列表中
- NotInlabel的值不在某个列表中
- Exists某个label存在
- DoesNotExist某个label不存在
```yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rc-me-demo
spec:
replicas: 3
selector:
matchExpressions:
- key: app
operator: Exists
template:
metadata:
name: rc-me-demo
labels:
app: rc-me-demo
spec:
containers:
- name: rc-me-demo-container
image: aaronxudocker/myapp:v1.0
ports:
- containerPort: 80
```
```yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rc-me-in-demo
spec:
replicas: 3
selector:
matchExpressions:
- key: app
operator: In
values:
- spring-k8s
- xixixi
template:
metadata:
name: rc-me-in-demo
labels:
app: rc-me-in-demo
spec:
containers:
- name: rc-me-in-demo-container
image: aaronxudocker/myapp:v1.0
ports:
- containerPort: 80
```
# Deployment
声明性的东⻄是对终结果的陈述表明意图⽽不是实现它的过程。在Kubernetes中这就是说“应该有⼀个包含三个Pod的ReplicaSet”。
命令式充当命令。声明式是被动的⽽命令式是主动且直接的“创建⼀个包含三个Pod的ReplicaSet”。
```bash
$ kubectl replace -f deployment.yaml
$ kubectl apply -f deployment.yaml
$ kubectl diff -f deployment.yaml
```
替换⽅式
- kubectl replace:使⽤新的配置完全替换掉现有资源的配置。这意味着新配置将覆盖现有资源的所有字段和属性,包括未指定的字段,会导致整个资源的替换
- kubectl apply:使⽤新的配置部分地更新现有资源的配置。它会根据提供的配置文件或参数,只更新与新配置中不同的部分,⽽不会覆盖整个资源的配置
字段级别的更新
- kubectl replace:由于是完全替换,所以会覆盖所有字段和属性,⽆论是否在新配置中指定
- kubectl apply:只更新与新配置中不同的字段和属性,保留未指定的字段不受影响
部分更新
- kubectl replace:不⽀持部分更新,它会替换整个资源的配置
- kubectl apply:⽀持部分更新,只会更新新配置中发⽣变化的部分,保留未指定的部分不受影响
与其他配置的影响
- kubectl replace:不考虑其他资源配置的状态,直接替换资源的配置
- kubectl apply:可以结合使⽤-f或-k参数从文件或⽬录中读取多个资源配置并根据当前集群中的资源状态进⾏更新
## Deployment介绍
Deployment为Pod和ReplicaSet提供了⼀个声明式定义(declarative)⽅法⽤来替代以前的ReplicationController来⽅便的管理应⽤。典型的应⽤场景包括
- 定义Deployment来创建Pod和ReplicaSet
- 滚动升级和回滚应⽤
- 扩容和缩容
- 暂停和继续Deployment
<img src="Pod控制器/image-20240911095809938.png" alt="image-20240911095809938" style="zoom: 50%;" />
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp-deploy
name: myapp-deploy
spec:
replicas: 1
selector:
matchLabels:
app: myapp-deploy
template:
metadata:
labels:
app: myapp-deploy
spec:
containers:
- name: myapp
image: aaronxudocker/myapp:v1.0
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
```
## 更新策略
Deployment可以保证在升级时只有⼀定数量的Pod是down的。默认的它会确保⾄少有比期望的Pod数量少⼀个是up状态最多⼀个不可⽤
Deployment同时也可以确保只创建出超过期望数量的⼀定数量的Pod。默认的它会确保最多比期望的Pod数量多⼀个的Pod是up的最多1个surge
未来的Kuberentes版本中将从1-1变成25%-25%。目前的版本已经支持。
kubectlexplaindeploy.spec.strategy.type
- Recreate
- 在创建出新的Pod之前会先杀掉所有已存在的Pod
- rollingUpdate
- 滚动更新就是杀死一部分就启动一部分在更新过程中存在两个版本Pod
- maxSurge指定超出副本数有⼏个两种⽅式1、指定数量2、百分比
- maxUnavailable最多有⼏个不可⽤
```bash
$ kubectl create -f deployment.yaml --record
# --record 参数可以记录命令可以看到每次revision的变化
$ kubectl scale deployment myapp-deploy --replicas 10
$ kubectl autoscale deployment myapp-deploy --min=10 --max=15 --cpu-percent=80
$ kubectl set image deployment/myapp-deploy myapp=aaronxudocker/myapp:v2.0
$ kubectl rollout history deployment/myapp-deploy
$ kubectl rollout undo deployment/myapp-deploy
```
可以通过设置`.spec.revisionHistoryLimit`项来指定deployment最多保留多少revision历史记录。默认的会保留所有的revision如果将该项设置为0Deployment就不允许回退了
## 金丝雀部署
⾦丝雀部署的名字灵感来源于17世纪英国矿井⼯⼈使⽤⾦丝雀作为⽡斯检测指标的传统⽅法。⾦丝雀对⽡斯这种⽓体⼗分敏感空⽓中哪怕有极其微量的⽡斯⾦丝雀也会停⽌歌唱。⽽当⽡斯含量超过⼀定限度时虽然⼈类毫⽆察觉⾦丝雀却早已毒发⾝亡。在采矿设备相对简陋的条件下⼯⼈们每次下井都会带上⼀只⾦丝雀作为“⽡斯检测指标”以便在危险状况下紧急撤离。
⾦丝雀部署的核⼼思想是在实际运⾏环境中的⼀⼩部分⽤户或流量上测试新版本的软件,⽽⼤部分⽤户或流量仍然使⽤旧版本。通过对新版本进⾏有限范围的实时测试和监控,可以及早发现潜在的问题,并减少对整个系统的冲击。
<img src="Pod控制器/image-20240911143520378.png" alt="image-20240911143520378" style="zoom:33%;" />
```bash
$ kubectl patch deployment myapp-deploy -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
# 允许pod多出预期1个不允许少一个
$ kubectl patch deployment myapp-deploy --patch '{"spec": {"template": {"spec": {"containers": [{"name": "myapp","image":"aaronxudocker/myapp:v2.0"}]}}}}' && kubectl rollout pause deploy myapp-deploy
# 修改镜像为v2.0版本并且立马停止回滚可以看到pod数量复合上面的设置
$ kubectl rollout resume deploy myapp-deploy
# 恢复回滚动作
```
回滚相关的命令
```bash
$ kubectl rollout status deployment myapp-deploy
# 查看上次回滚的状态
$ kubectl rollout history deployment/myapp-deploy
# 查看回滚的历史记录,需要配合--record使用
$ kubectl rollout undo deployment/myapp-deploy --to-revision=2
# 状态回退到李四记录里面的2的状态
$ kubectl rollout pause deployment/myapp-deploy
# 暂停回滚
```
# DaemonSet
DaemonSet确保全部或者⼀些Node上运⾏⼀个Pod的副本。当有Node加入集群时也会为他们新增⼀个Pod。当有Node从集群移除时这些Pod也会被回收。删除DaemonSet将会删除它创建的所有Pod
使⽤DaemonSet的⼀些典型⽤法
- 运⾏集群存储daemon例如在每个Node上运⾏`glusterd``ceph`
- 在每个Node上运⾏⽇志收集daemon例如`fluentd``logstash`
- 在每个Node上运⾏监控daemon例如PrometheusNode Exporter、`collectd`、Datadog代理、NewRelic代理或Ganglia`gmond`
```yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-demo
namespace: default
labels:
app: deamonset-demo
spec:
selector:
matchLabels:
app: deamonset-demo
template:
metadata:
labels:
app: deamonset-demo
spec:
containers:
- name: deamonset-demo
image: aaronxudocker/myapp:v1.0
```
# Job
Job负责批处理任务即仅执⾏⼀次的任务它保证批处理任务的⼀个或多个Pod成功结束
特殊说明
- spec.template格式同Pod
- RestartPolicy仅⽀持Never或OnFailure
- 单个Pod时默认Pod成功运⾏后Job即结束
- `.spec.completions`标志Job结束需要成功运⾏的Pod个数默认为1
- `.spec.parallelism`标志并⾏运⾏的Pod的个数默认为1
- `spec.activeDeadlineSeconds`标志失败Pod的重试最⼤时间超过这个时间不会继续重试
案例:使用马青公式计算圆周率后一千位,可以用来看下节点的计算能力
这个公式由英国天文学教授约翰·⻢青于1706年发现。他利⽤这个公式计算到了100位的圆周率。⻢青公式每计算⼀项可以得到1.4位的⼗进制精度。因为它的计算过程中被乘数和被除数都不⼤于⻓整数,所以可以很容易地在计算机上编程实现
$$
\frac{\pi}{4} = 4 \tan^{-1}\left(\frac{1}{5}\right) - \tan^{-1}\left(\frac{1}{239}\right)
$$
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: job-demo
spec:
template:
metadata:
name: job-demo-pod
spec:
containers:
- name: job-demo
image: aaronxudocker/tools:maqingpythonv1
restartPolicy: Never
```
Job负责批处理任务即仅执⾏⼀次的任务它保证批处理任务的⼀个或多个Pod成功结束
## 错误的Job任务
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: rand
spec:
template:
metadata:
name: rand
spec:
containers:
- name: rand
image: aaronxudocker/tools:randexitv1
args: ["--exitcode=1"]
restartPolicy: Never
```
Job控制器会记录成功的次数
```bash
$ kubectl get job
NAME COMPLETIONS DURATION AGE
rand 0/1 30s 30s
```
改为产生随机返回值
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: rand
spec:
completions: 10
parallelism: 5
template:
metadata:
name: rand
spec:
containers:
- name: rand
image: aaronxudocker/tools:randexitv1
restartPolicy: Never
```
可以看到并行启动5个pod直到成功为止
```bash
$ kubectl get job -w
NAME COMPLETIONS DURATION AGE
rand 2/10 24s 24s
rand 2/10 50s 50s
rand 2/10 52s 52s
rand 2/10 58s 58s
rand 2/10 59s 59s
rand 3/10 59s 59s
rand 3/10 60s 60s
rand 5/10 60s 60s
rand 5/10 61s 61s
rand 5/10 62s 62s
rand 5/10 67s 67s
rand 5/10 68s 68s
rand 8/10 68s 68s
rand 8/10 69s 69s
rand 8/10 69s 69s
$ kubectl get job -o yaml
......
spec:
backoffLimit: 6
# 6次尝试失败就达到尝试上限
```
# CronJob
CronJob管理基于时间的Job
- 在给定时间点只运⾏⼀次
- 周期性地在给定时间点运⾏
使⽤条件当前使⽤的Kubernetes集群版本>=1.8对CronJob
典型的⽤法如下所⽰:
- 在给定的时间点调度Job运⾏
- 创建周期性运⾏的Job例如数据库备份、发送邮件
- `.spec.schedule`调度必需字段指定任务运⾏周期格式同Cron
- `.spec.jobTemplate`Job模板必需字段指定需要运⾏的任务格式同Job
- `.spec.startingDeadlineSeconds`启动Job的期限秒级别该字段是可选的。如果因为任何原因⽽错过了被调度的时间那么错过执⾏时间的Job将被认为是失败的。如果没有指定则没有期限
- `.spec.concurrencyPolicy`并发策略该字段也是可选的。它指定了如何处理被CronJob创建的Job的并发执⾏。只允许指定下⾯策略中的⼀种
- `Allow`默认允许并发运⾏Job
- `Forbid`:禁⽌并发运⾏,如果前⼀个还没有完成,则直接跳过下⼀个
- `Replace`取消当前正在运⾏的Job⽤⼀个新的来替换
- 注意当前策略只能应⽤于同⼀个CronJob创建的Job。如果存在多个CronJob它们创建的Job之间总是允许并发运⾏。
- `.spec.suspend`:挂起,该字段也是可选的。如果设置为`true`后续所有执⾏都会被挂起。它对已经开始执⾏的Job不起作⽤。默认值为`false`
- `.spec.successfulJobsHistoryLimit``.spec.failedJobsHistoryLimit`历史限制是可选的字段。它们指定了可以保留多少完成和失败的Job。默认情况下它们分别设置为`3``1`。设置限制的值为`0`相关类型的Job完成后将不会被保留
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: cronjob-demo
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
completions: 3
template:
spec:
containers:
- name: cronjob-demo-container
image: aaronxudocker/tools:busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
```
创建job操作应该是幂等的
# Horizontal Pod Autoscaler(HPA)
在前面的课程中,我们已经可以实现通过手工执行`kubectl scale`命令实现Pod扩容或缩容但是这显然不符合Kubernetes的定位目标--自动化、智能化。 Kubernetes期望可以实现通过监测Pod的使用情况实现pod数量的自动调整于是就产生了Horizontal Pod AutoscalerHPA这种控制器。
HPA可以获取每个Pod利用率然后和HPA中定义的指标进行对比同时计算出需要伸缩的具体值最后实现Pod的数量的调整。其实HPA与之前的Deployment一样也属于一种Kubernetes资源对象它通过追踪分析RC控制的所有目标Pod的负载变化情况来确定是否需要针对性地调整目标Pod的副本数这是HPA的实现原理。
<img src="Pod控制器/image-20240912140819583.png" alt="image-20240912140819583" style="zoom:33%;" />
查看pod的资源
```bash
# 由于没安装metrics服务所以无法查看pod资源情况
$ kubectl top pod myapp-deploy-57bff895d5-cvlmg
error: Metrics API not available
```
安装metrics-server
```bash
$ wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.2/components.yaml
# 修改配置文件
- args:
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname,InternalDNS,ExternalDNS
- --kubelet-insecure-tls
image: registry.k8s.io/metrics-server/metrics-server:v0.7.2
$ kubectl apply -f components.yaml
$ kubectl get pod -n kube-system |grep metrics
metrics-server-5c7fbc6ff6-fssfd 1/1 Running 0 2m42s
# 使用kubectl top node 查看资源使用情况
$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master01 116m 2% 1943Mi 55%
node01 48m 1% 961Mi 27%
node02 61m 1% 1161Mi 32%
$ kubectl top pod myapp-deploy-57bff895d5-cvlmg
NAME CPU(cores) MEMORY(bytes)
myapp-deploy-57bff895d5-cvlmg 0m 4Mi
```
准备deployment和service资源
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: aaronxudocker/myapp:v1.0
resources:
limits:
cpu: "1"
requests:
cpu: "100m"
```
创建service并且查看
```bash
$ kubectl expose deployment nginx --type=NodePort --port=80
$ kubectl get deployment,pod,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 1/1 1 1 114s
NAME READY STATUS RESTARTS AGE
pod/nginx-7b4664bb57-pgllw 1/1 Running 0 114s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx NodePort 10.8.51.98 <none> 80:30482/TCP 37s
```
创建HPA资源
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: hpa-demo
spec:
minReplicas: 1 #最小pod数量
maxReplicas: 10 #最大pod数量
behavior:
scaleUp:
stabilizationWindowSeconds: 10 # 在连续10秒内稳定后才考虑进一步扩展操作
policies:
- type: Percent
value: 100 # 当超过当前副本数的100%才进行扩展
periodSeconds: 3 # 两次扩容之间的最小时间间隔
scaleDown:
stabilizationWindowSeconds: 300 # 在连续300秒内稳定后才考虑缩减操作
policies:
- type: Percent
value: 10 # 当低于当前副本数的10%才进行缩减
periodSeconds: 3 # 两次缩减之间的最小时间间隔
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 10 # CPU平均利用率目标设为10%
scaleTargetRef: # 指定要控制的nginx信息
apiVersion: apps/v1
kind: Deployment
name: nginx
```
查看hpa资源
```bash
$ kubectl get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-demo Deployment/nginx 0%/10% 1 10 1 20s
```
使用压力测试工具
```bash
$ ab -c 1000 -n 1000000 http://192.168.173.100:30482/
$ kubectl get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-demo Deployment/nginx 0%/10% 1 10 1 20s
hpa-demo Deployment/nginx 1%/10% 1 10 1 105s
hpa-demo Deployment/nginx 366%/10% 1 10 1 2m
hpa-demo Deployment/nginx 522%/10% 1 10 2 2m15s
hpa-demo Deployment/nginx 352%/10% 1 10 4 2m30s
hpa-demo Deployment/nginx 189%/10% 1 10 8 2m45s
hpa-demo Deployment/nginx 103%/10% 1 10 10 3m
hpa-demo Deployment/nginx 68%/10% 1 10 10 3m15s
```
压力测试停止之后
```bash
$ kubectl get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-demo Deployment/nginx 0%/10% 1 10 10 8m45s
hpa-demo Deployment/nginx 0%/10% 1 10 9 9m
hpa-demo Deployment/nginx 0%/10% 1 10 8 9m15s
hpa-demo Deployment/nginx 0%/10% 1 10 7 9m30s
hpa-demo Deployment/nginx 0%/10% 1 10 6 9m45s
hpa-demo Deployment/nginx 0%/10% 1 10 5 10m
hpa-demo Deployment/nginx 0%/10% 1 10 4 10m
hpa-demo Deployment/nginx 0%/10% 1 10 3 10m
hpa-demo Deployment/nginx 0%/10% 1 10 2 10m
hpa-demo Deployment/nginx 0%/10% 1 10 1 11m
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -0,0 +1,766 @@
# Pod生命周期
![image-20240906095839924](Pod生命周期/image-20240906095839924.png)
init容器与普通的容器非常像除了如下两点
- init容器总是运行到成功完成为止
- 每个init容器都必须在下一个init容器启动之前成功完成
如果Pod的Init容器失败Kubernetes会不断地重启该Pod直到Init容器成功为止。然而如果Pod对应的restartPolicy为Never它不会重新启动
## 检测initC的阻塞性
```yaml
apiVersion: v1
kind: Pod
metadata:
name: initc-1
labels:
name: initc
spec:
containers:
- name: myapp-container
image: centos:7
resources:
limits:
memory: "128Mi"
cpu: "500m"
command: ['sh', '-c', 'echo The app is running && sleep 10']
initContainers:
- name: init-myservice
image: aaronxudocker/tools:busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: aaronxudocker/tools:busybox
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
```
```bash
# 查看日志,看到不停的在尝试
$ kubectl logs initc-1 -c init-myservice
# 创建svc资源会通过CoreDNS自动将myservice解析成功详解看后面的service部分
$ kubectl create svc clusterip myservice --tcp=80:80
```
如果initc执行失败了那么就会重新执行所有的initc
```yaml
apiVersion: v1
kind: Pod
metadata:
name: initc-2
labels:
name: initc
spec:
containers:
- name: myapp-container
image: centos:7
resources:
limits:
memory: "128Mi"
cpu: "500m"
command: ['sh', '-c', 'echo The app is running && sleep 10']
initContainers:
- name: init-myservice
image: aaronxudocker/tools:busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: randexit
image: aaronxudocker/tools:randexitv1
args: ['--exitcode=1']
```
```bash
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
initc-1 0/1 Init:1/2 0 16m
initc-2 0/1 Init:Error 5 (97s ago) 3m42s
$ kubectl logs initc-2 -c randexit
休眠 4 秒,返回码为 1
```
如果我们让initc的返回码直接为0那么就可以看到pod正常启动
```bash
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
initc-1 0/1 Init:1/2 0 19m
initc-2 1/1 Running 1 (7s ago) 72s
```
- InitC与应用容器具备不同的镜像可以把一些危险的工具放置在initC中进行使用
- initC多个之间是线性启动的所以可以做一些延迟性的操作
- initC无法定义readinessProbe其它以外同应用容器定义无异
# Pod探针
探针是由kubelet对容器执行的定期诊断。要执行诊断kubelet调用由容器实现的Handler。有三种类型的处理程序
- ExecAction在容器内执行指定命令。如果命令退出时返回码为0则认为诊断成功
- TCPSocketAction对指定端口上的容器的IP地址进行TCP检查。如果端口打开则诊断被认为是成功的
- HTTPGetAction对指定的端口和路径上的容器的IP地址执行HTTPGet请求。如果响应的状态码⼤于等于200且小于400则诊断被认为是成功的
每次探测都将获得以下三种结果之一:
- 成功:容器通过了诊断。
- 失败:容器未通过诊断。
- 未知:诊断失败,因此不会采取任何行动
## 探针的分类
- startupProbe开始探针开始检测吗
- livenessProbe存活探针还活着吗
- readinessProbe就绪探针准备提供服务了吗
### readinessProbe就绪探针
介绍k8s通过添加就绪探针解决尤其是在扩容时保证提供给用户的服务都是可用的。
> 选项说明
> - initialDelaySeconds容器启动后要等待多少秒后就探针开始工作单位“秒”默认是0秒最小值是0
> - periodSeconds执行探测的时间间隔单位是秒默认为10s单位“秒”最小值是1
> - timeoutSeconds探针执行检测请求后等待响应的超时时间默认为1s单位“秒”最小值是1
> - successThreshold探针检测失败后认为成功的最小连接成功次数默认值为1。必须为1才能激活和启动。最小值为1。
> - failureThreshold探测失败的重试次数重试一定次数后将认为失败默认值为3最小值为1。
#### 就绪探针实验
- 基于 HTTP GET 方式
```yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
labels:
name: myapp
spec:
containers:
- name: readiness-httpget-container
image: nginx:latest
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
```bash
# 当前处于没有就绪的状态
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 0/1 Running 0 4m16s
# 创建一个index1.html
$ kubectl exec -it readiness-httpget-pod -c readiness-httpget-container -- /bin/bash
root@readiness-httpget-pod:/# echo "hehe" > /usr/share/nginx/html/index1.html
# 查看就已经处于就绪的状态了
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
readiness-httpget-pod 1/1 Running 0 5m40s
# 在运行过程中,就绪探测一直存在,如果不满足条件,会回到未就绪的情况
```
- 基于 EXEC 方式
```yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-exec-pod
labels:
name: myapp
spec:
containers:
- name: readiness-exec-container
image: aaronxudocker/tools:busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600"]
readinessProbe:
exec:
command: ["test", "-e", "/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
```bash
# 可以看到在60秒后就变成非就绪状态了
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
readiness-exec-pod 1/1 Running 0 7s
readiness-exec-pod 0/1 Running 0 69s
```
基于TCP Check方式
```yaml
apiVersion: v1
kind: Pod
metadata:
name: readiness-tcp-pod
labels:
name: myapp
spec:
containers:
- name: readiness-tcp-container
image: nginx:latest
imagePullPolicy: IfNotPresent
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 1
periodSeconds: 3
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
#### 就绪探针流量测试
<img src="Pod生命周期/image-20240906140721572.png" alt="image-20240906140721572" style="zoom: 25%;" />
在匹配可用pod的时候标签必须匹配状态必须是就绪状态。
```yaml
# pod-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: myapp
spec:
containers:
- name: myapp-1
image: nginx:latest
resources:
limits:
memory: "128Mi"
cpu: "500m"
# pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-2
labels:
app: myapp
version: v1
spec:
containers:
- name: myapp-1
image: nginx:latest
resources:
limits:
memory: "128Mi"
cpu: "500m"
# 确认状态已经就绪
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-1 1/1 Running 0 2m31s app=myapp
pod-2 1/1 Running 0 32s app=myapp,version=v1
```
创建service资源
```bash
# 注意myapp就是标签为app=myapp的pod
# 此处不需要理解,后面会细讲,只是用来验证就绪探针对流量的影响
# 此处的作用是形成多个pod的负载均衡
$ kubectl create svc clusterip myapp --tcp=80:80
service/myapp created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 46h
myapp ClusterIP 10.8.74.201 <none> 80/TCP 6s
```
将两个pod中的主页文件修改一下用来作为区分
```bash
# 如果pod中只有一个main容器那么在exec的时候就不需要指定容器
$ kubectl exec -it pod-1 -- /bin/bash
root@pod-1:/# echo pod-1 > /usr/share/nginx/html/index.html
$ kubectl exec -it pod-2 -- /bin/bash
root@pod-2:/# echo pod-2 > /usr/share/nginx/html/index.html
```
验证负载均衡的状态
```bash
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-1
$ curl 10.8.74.201
pod-2
```
创建一个label为 `app: test` 的pod看下是否能被匹配
```yaml
# 3.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: test
version: v1
spec:
containers:
- name: myapp-1
image: nginx:latest
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
查看pod状态修改 `pod-3` 的网页内容
```bash
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-1 1/1 Running 0 11m app=myapp
pod-2 1/1 Running 0 9m57s app=myapp,version=v1
pod-3 1/1 Running 0 51s app=test,version=v1
$ kubectl exec -it pod-3 -- /bin/bash
root@pod-3:/# echo pod-3 > /usr/share/nginx/html/index.html
```
验证负载均衡的状态,发现 `pod-3` 并不能被匹配上
```bash
$ curl 10.8.74.201
pod-1
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-1
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-1
```
创建一个不满足就绪条件的 `pod-4`
```yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-4
labels:
app: myapp
version: v1
spec:
containers:
- name: myapp-1
image: nginx:latest
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
修改主页内容
```bash
$ kubectl exec -it pod-4 -- /bin/bash
root@pod-4:/# echo pod-4 > /usr/share/nginx/html/index.html
```
查看状态是未就绪的
```bash
$ kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-1 1/1 Running 0 17m app=myapp
pod-2 1/1 Running 0 15m app=myapp,version=v1
pod-3 1/1 Running 0 6m49s app=test,version=v1
pod-4 0/1 Running 0 41s app=myapp,version=v1
```
验证负载均衡
```bash
$ curl 10.8.74.201
pod-1
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-1
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-1
```
满足 `pod-4` 的就绪条件
```bash
$ kubectl exec -it pod-4 -- /bin/bash
root@pod-4:/# touch /usr/share/nginx/html/index1.html
```
再次验证负载均衡
```bash
$ curl 10.8.74.201
pod-1
$ curl 10.8.74.201
pod-2
$ curl 10.8.74.201
pod-4
```
### livenessProbe存活探针
介绍k8s通过添加存活探针解决虽然活着但是已经死了的问题。
> 选项说明
> - initialDelaySeconds容器启动后要等待多少秒后就探针开始工作单位“秒”默认是0秒最小值是0
> - periodSeconds执行探测的时间间隔单位是秒默认为10s单位“秒”最小值是1
> - timeoutSeconds探针执行检测请求后等待响应的超时时间默认为1s单位“秒”最小值是1
> - successThreshold探针检测失败后认为成功的最小连接成功次数默认值为1。必须为1才能激活和启动。最小值为1。
> - failureThreshold探测失败的重试次数重试一定次数后将认为失败默认值为3最小值为1。
#### 存活探针实验
- 基于 Exec 方式
```yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
spec:
containers:
- name: liveness-exec-container
image: aaronxudocker/tools:busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "touch /tmp/live; sleep 60; rm -rf /tmp/live; sleep 3600"]
livenessProbe:
exec:
command: ["test", "-e", "/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
一段时间以后,可以看到发生重启的事件
```bash
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
liveness-exec-pod 1/1 Running 0 11s
liveness-exec-pod 1/1 Running 1 (1s ago) 101s
liveness-exec-pod 1/1 Running 2 (1s ago) 3m20s
```
- 基于 HTTP Get 方式
```yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
spec:
containers:
- name: liveness-httpget-container
image: nginx:latest
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
port: 80
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 3
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
删除 `index.html` 文件,使其不满足存活探测条件
```bash
# 在删除index.html之后可以看到命令行退出了
$ kubectl exec -it liveness-httpget-pod -- /bin/bash
root@liveness-httpget-pod:/# rm -f /usr/share/nginx/html/index.html
root@liveness-httpget-pod:/# command terminated with exit code 137
```
重新查看pod状态可以看到重启了
```bash
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-httpget-pod 1/1 Running 1 (48s ago) 2m39s
```
在运行此pod的节点上查看docker的名字容器名字是 `集群名-pod名-容器名-hash-重启次数(初始是0)`
```bash
$ docker ps -a |grep liveness-exec-container
18c5ba02d684 39286ab8a5e1 "/docker-entrypoint.…" About a minute ago Up About a minute k8s_liveness-exec-container_liveness-httpget-pod_default_aa36504e-23a9-48d1-988c-4de0398c474f_1
54b3a04bd6b0 39286ab8a5e1 "/docker-entrypoint.…" 3 minutes ago Exited (0) About a minute ago k8s_liveness-exec-container_liveness-httpget-pod_default_aa36504e-23a9-48d1-988c-4de0398c474f_0
```
- 基于 TCP Check 方式
```yaml
apiVersion: v1
kind: Pod
metadata:
name: liveness-tcp-pod
spec:
containers:
- name: liveness-tcp-container
image: nginx:latest
imagePullPolicy: IfNotPresent
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 3
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
### startupProbe启动探针
介绍k8s在1.16版本后增加startupProbe探针主要解决在复杂的程序中readinessProbe、livenessProbe探针无法更好的判断程序是否启动、是否存活。
> 选项说明
> - initialDelaySeconds容器启动后要等待多少秒后就探针开始工作单位“秒”默认是0秒最小值是0
> - periodSeconds执行探测的时间间隔单位是秒默认为10s单位“秒”最小值是1
> - timeoutSeconds探针执行检测请求后等待响应的超时时间默认为1s单位“秒”最小值是1
> - successThreshold探针检测失败后认为成功的最小连接成功次数默认值为1。必须为1才能激活和启动。最小值为1。
> - failureThreshold探测失败的重试次数重试一定次数后将认为失败默认值为3最小值为1。
#### 启动探针实验
```yaml
apiVersion: v1
kind: Pod
metadata:
name: startupprobe-1
spec:
containers:
- name: startupprobe
image: nginx:latest
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 80
path: /index2.html
initialDelaySeconds: 1
periodSeconds: 3
startupProbe:
httpGet:
path: /index1.html
port: 80
periodSeconds: 10
failureThreshold: 30
resources:
limits:
memory: "128Mi"
cpu: "500m"
# 应用程序将会有最多 5 分钟 failureThreshold * periodSeconds30 * 10 = 300s的时间来完成其启动过程。如果到超时都没有启动完成就会重启。
```
创建 `index1.html` 文件
```bash
$ kubectl exec -it pod/startupprobe-1 -- /bin/bash
root@startupprobe-1:/# touch /usr/share/nginx/index1.html
# 查看依旧是未就绪的状态
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
startupprobe-1 0/1 Running 0 42s
# 创建index2.html文件
$ kubectl exec -it pod/startupprobe-1 -- /bin/bash
root@startupprobe-1:/# touch /usr/share/nginx/index2.html
# 查看状态
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
startupprobe-1 1/1 Running 0 43s
```
此时删掉启动探测的 `index1.html` 会怎样?
# Pod钩子
Podhook钩子是由Kubernetes管理的kubelet发起的当容器中的进程启动前或者容器中的进程终止之前运行这是包含在容器的⽣命周期之中。可以同时为Pod中的所有容器都配置hook
Hook的类型包括两种
- exec执行一段命令
- HTTP发送HTTP请求
在k8s中理想的状态是pod优雅释放但是并不是每一个Pod都会这么顺利
- Pod卡死处理不了优雅退出的命令或者操作
- 优雅退出的逻辑有BUG陷入死循环
- 代码问题,导致执行的命令没有效果
对于以上问题k8s的Pod终止流程中还有一个"最多可以容忍的时间"即graceperiod(在pod.spec.terminationGracePeriodSeconds字段定义)这个值默认是30秒当我们执行kubectl delete的时候也可以通过--grace-period参数显示指定一个优雅退出时间来覆盖Pod中的配置如果我们配置的grace period超过时间之后k8s就只能选择强制kill Pod。
值得注意的是这与preStopHook和SIGTERM信号并行发⽣。k8s不会等待preStopHook完成。你的应用程序应在terminationGracePeriod之前退出。
## Pod钩子实验
- 基于 exec 方式
```yaml
apiVersion: v1
kind: Pod
metadata:
name: hook-exec-pod
spec:
containers:
- name: hook-exec-container
image: nginx:latest
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo postStart > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh", "-c", "echo preStart > /usr/share/message"]
resources:
limits:
memory: "128Mi"
cpu: "500m"
```
在这个pod的内部写一个循环查看此文件的shell命令
```bash
$ kubectl exec -it pod/hook-exec-pod -- /bin/bash
root@hook-exec-pod:/# while true;
> do
> cat /usr/share/message
> done
# 删除此pod就能看到结束的钩子信息了
```
- 基于 HTTP Get 方式
```bash
# 开启一个测试 webserver
$ docker run -it --rm -p 1234:80 nginx:latest
```
启动一个pod然后再删除查看nginx容器日志可以看到记录了这两次的http请求
```yaml
2024/09/06 07:35:23 [error] 29#29: *1 open() "/usr/share/nginx/html/poststarthook.html" failed (2: No such file or directory), client: 192.168.173.101, server: localhost, request: "GET /poststarthook.html HTTP/1.1", host: "192.168.173.100:1234"
192.168.173.101 - - [06/Sep/2024:07:35:23 +0000] "GET /poststarthook.html HTTP/1.1" 404 153 "-" "kube-lifecycle/1.29" "-"
2024/09/06 07:35:45 [error] 29#29: *1 open() "/usr/share/nginx/html/prestophook.html" failed (2: No such file or directory), client: 192.168.173.101, server: localhost, request: "GET /prestophook.html HTTP/1.1", host: "192.168.173.100:1234"
192.168.173.101 - - [06/Sep/2024:07:35:45 +0000] "GET /prestophook.html HTTP/1.1" 404 153 "-" "kube-lifecycle/1.29" "-"
```
# 总结
Pod⽣命周期中的initC、startupProbe、livenessProbe、readinessProbe、hook都是可以并且存在的可以选择全部、部分或者完全不用。
```yaml
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-pod
labels:
app: lifecycle-pod
spec:
containers:
- name: busybox-container
image: aaronxudocker/tools:busybox
command: ["/bin/sh","-c","touch /tmp/live ; sleep 600; rm -rf /tmp/live; sleep 3600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
lifecycle:
postStart:
httpGet:
host: 192.168.173.100
path: poststarthook.html
port: 1234
preStop:
httpGet:
host: 192.168.173.100
path: prestophook.html
port: 1234
resources:
limits:
memory: "128Mi"
cpu: "500m"
- name: myapp-container
image: nginx:latest
livenessProbe:
httpGet:
port: 80
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 3
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
initContainers:
- name: init-myservice
image: aaronxudocker/tools:busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: aaronxudocker/tools:busybox
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
```
# 调度Pod
![image-20240906204437000](Pod生命周期/image-20240906204437000.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

793
Kubernetes/Service.md Normal file
View File

@@ -0,0 +1,793 @@
# 概念以及原理
Kubernetes`Service`定义了这样⼀种抽象:
⼀个`Pod`的逻辑分组,⼀种可以访问它们的策略,通常称为微服务。这⼀组`Pod`能够被`Service`访问到,通常是通过`LabelSelector`
<img src="Service/image-20240912140110844.png" alt="image-20240912140110844" style="zoom: 33%;" />
Service在很多情况下只是一个概念真正起作用的其实是kube-proxy服务进程每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息而kube-proxy会基于监听的机制发现这种Service的变动然后**它会将最新的Service信息转换成对应的访问规则**。
<img src="Service/image-20240912152844652.png" alt="image-20240912152844652" style="zoom:33%;" />
# 工作模式
kube-proxy目前支持三种工作模式:
## userspace模式
userspace模式下kube-proxy会为每一个Service创建一个监听端口发向Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接以将请求转发到Pod上。 该模式下kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中在进行转发处理时会增加内核和用户空间之间的数据拷贝虽然比较稳定但是效率比较低。
<img src="Service/image-20240912170627727.png" alt="image-20240912170627727" style="zoom:33%;" />
## iptables模式
iptables模式下kube-proxy为service后端的每个Pod创建对应的iptables规则直接将发向Cluster IP的请求重定向到一个Pod IP。该模式下kube-proxy不承担四层负责均衡器的角色只负责创建iptables规则。该模式的优点是较userspace模式效率更高但不能提供灵活的LB策略当后端Pod不可用时也无法进行重试。
<img src="Service/image-20240912170931956.png" alt="image-20240912170931956" style="zoom:33%;" />
## ipvs模式
ipvs模式和iptables类似kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此以外ipvs支持更多的LB算法。
<img src="Service/image-20240912171043118.png" alt="image-20240912171043118" style="zoom:33%;" />
```yaml
# 创建三个pod
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp-deploy
name: myapp-deploy
spec:
replicas: 3
selector:
matchLabels:
app: myapp-deploy
template:
metadata:
labels:
app: myapp-deploy
spec:
containers:
- name: myapp
image: aaronxudocker/myapp:v1.0
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
```
```bash
# 启动一个负载均衡的service
$ kubectl create svc clusterip myapp-deploy --tcp=80:80
# 修改ipvs
$ kubectl edit configmap kube-proxy -n kube-system
mode: "ipvs"
# 删除kube-proxy的pod
$ kubectl delete pod -n kube-system -l k8s-app=kube-proxy
pod "kube-proxy-ckwsj" deleted
pod "kube-proxy-t729f" deleted
pod "kube-proxy-z6dt8" deleted
# 查看pod创建的状态
$ kubectl get pod -n kube-system -l k8s-app=kube-proxy
NAME READY STATUS RESTARTS AGE
kube-proxy-948s5 1/1 Running 0 3s
kube-proxy-ggpwj 1/1 Running 0 3s
kube-proxy-v7lgs 1/1 Running 0 3s
# 查看虚拟IP地址
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-deploy ClusterIP 10.9.86.78 <none> 80/TCP 6m54s
# 查看ipvsadm的状态
$ ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.9.86.78:80 rr
-> 10.244.140.106:80 Masq 1 0 0
-> 10.244.196.141:80 Masq 1 0 0
-> 10.244.196.142:80 Masq 1 0 0
# 负载均衡的地址正好对应着pod的ip地址
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
myapp-deploy-57bff895d5-b2hhk 1/1 Running 0 73s 10.244.196.142 node01 <none> <none>
myapp-deploy-57bff895d5-fbln4 1/1 Running 0 73s 10.244.140.106 node02 <none> <none>
myapp-deploy-57bff895d5-frnfd 1/1 Running 0 73s 10.244.196.141 node01 <none> <none>
```
# Service资源清单
```yaml
kind: Service # 资源类型
apiVersion: v1 # 资源版本
metadata: # 元数据
name: service # 资源名称
namespace: default # 命名空间
spec: # 描述
selector: # 标签选择器用于确定当前service代理哪些pod
app: nginx
type: # Service类型指定service的访问方式
clusterIP: # 虚拟服务的ip地址
sessionAffinity: # session亲和性支持ClientIP、None两个选项
sessionAffinityConfig:
clientIP:
timeoutSeconds: 120 # session的过期时间
ports: # 端口信息
- protocol: TCP
port: 3017 # service端口
targetPort: 5003 # pod端口
nodePort: 31122 # 主机端口
```
可以使用如下命令得到基本的yaml格式的文件
```bash
$ kubectl create svc clusterip nginx --tcp=80:80 --dry-run=client -o yaml
$ ipvsadm -lnc
```
`spec.type`可以选择的类型
- ClusterIP默认值它是Kubernetes系统自动分配的虚拟IP只能在集群内部访问
- NodePort将Service通过指定的Node上的端口暴露给外部通过此方法就可以在集群外部访问服务
- LoadBalancer使用外接负载均衡器完成到服务的负载分发注意此模式需要外部云环境支持
- ExternalName 把集群外部的服务引入集群内部,直接使用
# Service使用
```yaml
# 创建三个pod
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp-deploy
name: myapp-deploy
spec:
replicas: 3
selector:
matchLabels:
app: myapp-deploy
template:
metadata:
labels:
app: myapp-deploy
spec:
containers:
- name: myapp
image: aaronxudocker/myapp:v1.0
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
```
测试三个pod
```bash
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-deploy-57bff895d5-b2hhk 1/1 Running 0 30m 10.244.196.142 node01 <none> <none>
myapp-deploy-57bff895d5-fbln4 1/1 Running 0 30m 10.244.140.106 node02 <none> <none>
myapp-deploy-57bff895d5-frnfd 1/1 Running 0 30m 10.244.196.141 node01 <none> <none>
# 查看一下访问情况
$ curl 10.244.196.142/hostname.html
myapp-deploy-57bff895d5-b2hhk
$ curl 10.244.140.106/hostname.html
myapp-deploy-57bff895d5-fbln4
$ curl 10.244.196.141/hostname.html
myapp-deploy-57bff895d5-frnfd
```
## ClusterIP类型的Service
```yaml
apiVersion: v1
kind: Service
metadata:
name: service-clusterip
spec:
selector:
app: myapp-deploy
# clusterIP: 172.16.66.66 # service的ip地址如果不写默认会生成一个
type: ClusterIP
ports:
- port: 80 # Service端口
targetPort: 80 # pod端口
```
查看运行结果
```bash
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-clusterip ClusterIP 10.13.125.29 <none> 80/TCP 22s
$ kubectl describe svc service-clusterip
Name: service-clusterip
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=myapp-deploy
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.13.125.29
IPs: 10.13.125.29
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.140.106:80,10.244.196.141:80,10.244.196.142:80
Session Affinity: None
Events: <none>
$ ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.13.125.29:80 rr
-> 10.244.140.106:80 Masq 1 0 0
-> 10.244.196.141:80 Masq 1 0 0
-> 10.244.196.142:80 Masq 1 0 0
$ while true;do curl 10.13.125.29/hostname.html; done
myapp-deploy-57bff895d5-b2hhk
myapp-deploy-57bff895d5-frnfd
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-b2hhk
myapp-deploy-57bff895d5-frnfd
myapp-deploy-57bff895d5-fbln4
```
## Endpoint
Endpoint是kubernetes中的一个资源对象存储在etcd中用来记录一个service对应的所有pod的访问地址它是根据service配置文件中selector描述产生的。必须要满足就绪探测。
一个Service由一组Pod组成这些Pod通过Endpoints暴露出来**Endpoints是实现实际服务的端点集合**。换句话说service和pod之间的联系是通过endpoints实现的。
<img src="Service/image-20240912172501813.png" alt="image-20240912172501813" style="zoom:33%;" />
```bash
$ kubectl get endpoints -o wide
NAME ENDPOINTS AGE
service-clusterip 10.244.140.106:80,10.244.196.141:80,10.244.196.142:80 6m27s
```
在deployment中添加一个就绪探测
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp-deploy
name: myapp-deploy
spec:
replicas: 3
selector:
matchLabels:
app: myapp-deploy
template:
metadata:
labels:
app: myapp-deploy
spec:
containers:
- name: myapp
image: aaronxudocker/myapp:v1.0
resources:
limits:
memory: "128Mi"
cpu: "500m"
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
ports:
- containerPort: 80
```
在不满足就绪探测的情况下是不会被endpoint采用的
```bash
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-deploy-659f9975b8-2sntn 0/1 Running 0 40s
myapp-deploy-659f9975b8-nd66b 0/1 Running 0 40s
myapp-deploy-659f9975b8-p4j5k 0/1 Running 0 40s
$ kubectl get endpoints
NAME ENDPOINTS AGE
service-clusterip 10s
```
满足了就绪探测和标签被匹配上的pod会被加入endpoint中
```bash
$ kubectl exec -it myapp-deploy-659f9975b8-2sntn -- /bin/bash
root@myapp-deploy-659f9975b8-2sntn:/# echo "hello world" > /usr/share/nginx/html/index1.html
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-deploy-659f9975b8-2sntn 1/1 Running 0 3m4s
myapp-deploy-659f9975b8-nd66b 0/1 Running 0 3m4s
myapp-deploy-659f9975b8-p4j5k 0/1 Running 0 3m4s
$ kubectl get endpoints
NAME ENDPOINTS AGE
service-clusterip 10.244.140.107:80 3m1s
$ ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.12.150.224:80 rr
-> 10.244.140.107:80 Masq 1 0 0
```
**负载分发策略**
对Service的访问被分发到了后端的Pod上去目前kubernetes提供了两种负载分发策略
- 如果不定义默认使用kube-proxy的策略比如随机、轮询
- 基于客户端地址的会话保持模式即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上
此模式可以使在spec中添加`sessionAffinity: ClientIP`选项
```bash
$ kubectl edit svc service-clusterip
sessionAffinity: ClientIP
$ while true;do curl 10.13.125.29/hostname.html; done
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-fbln4
myapp-deploy-57bff895d5-fbln4
$ ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.13.125.29:80 rr persistent 10800
-> 10.244.140.106:80 Masq 1 0 155
-> 10.244.196.141:80 Masq 1 0 0
-> 10.244.196.142:80 Masq 1 0 0
```
## HeadLess类型的Service
在某些场景中开发人员可能不想使用Service提供的负载均衡功能而希望自己来控制负载均衡策略针对这种情况kubernetes提供了HeadLiness Service这类Service不会分配Cluster IP如果想要访问service只能通过service的域名进行查询。
```yaml
apiVersion: v1
kind: Service
metadata:
name: service-headliness
spec:
selector:
app: myapp-deploy
clusterIP: None # 将clusterIP设置为None即可创建headliness Service
type: ClusterIP
ports:
- port: 80
targetPort: 80
```
```bash
$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-headliness ClusterIP None <none> 80/TCP 40s app=myapp-deploy
$ kubectl describe svc service-headliness
Name: service-headliness
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=myapp-deploy
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: None
IPs: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.140.107:80
Session Affinity: None
Events: <none>
$ kubectl exec -it myapp-deploy-659f9975b8-2sntn -- /bin/bash
root@myapp-deploy-659f9975b8-2sntn:/# cat /etc/resolv.conf
nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
$ dig @10.0.0.10 service-headliness.default.svc.cluster.local
;; ANSWER SECTION:
service-headliness.default.svc.cluster.local. 30 IN A 10.244.140.107
service-headliness.default.svc.cluster.local. 30 IN A 10.244.196.145
service-headliness.default.svc.cluster.local. 30 IN A 10.244.196.144
```
## NodePort类型的Service
在之前的样例中创建的Service的ip地址只有集群内部才可以访问如果希望将Service暴露给集群外部使用那么就要使用到另外一种类型的Service称为NodePort类型。NodePort的工作原理其实就是**将service的端口映射到Node的一个端口上**,然后就可以通过`NodeIp:NodePort`来访问service了。
<img src="Service/image-20240913143156247.png" alt="image-20240913143156247" style="zoom:33%;" />
```yaml
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
spec:
selector:
app: myapp-deploy
type: NodePort # service类型
ports:
- port: 80
nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是30000-32767), 如果不指定,会默认分配
targetPort: 80
```
查看是否能够正常的访问
```bash
$ for i in {1..6};do curl 192.168.173.100:30002/hostname.html;done
myapp-deploy-659f9975b8-nd66b
myapp-deploy-659f9975b8-p4j5k
myapp-deploy-659f9975b8-2sntn
myapp-deploy-659f9975b8-nd66b
myapp-deploy-659f9975b8-p4j5k
myapp-deploy-659f9975b8-2sntn
```
## LoadBalancer类型的Service
LoadBalancer和NodePort很相似目的都是向外部暴露一个端口区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备而这个设备需要外部环境支持的外部服务发送到这个设备上的请求会被设备负载之后转发到集群中。
![image-20240913154405309](Service/image-20240913154405309.png)
## ExternalName类型的Service
ExternalName类型的Service用于引入集群外部的服务它通过`externalName`属性指定外部一个服务的地址然后在集群内部访问此service就可以访问到外部的服务了。
<img src="Service/image-20240913160143805.png" alt="image-20240913160143805" style="zoom:33%;" />
```yaml
apiVersion: v1
kind: Service
metadata:
name: service-externalname
namespace: dev
spec:
type: ExternalName # service类型
externalName: www.baidu.com #改成ip地址也可以
```
```bash
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-externalname ExternalName <none> www.baidu.com <none> 7s
$ dig @10.0.0.10 service-externalname.default.svc.cluster.local
;; ANSWER SECTION:
service-externalname.default.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com. 30 IN CNAME www.a.shifen.com.
www.a.shifen.com. 30 IN A 180.101.50.242
www.a.shifen.com. 30 IN A 180.101.50.188
```
# Ingress介绍
在前面课程中已经提到Service对集群之外暴露服务的主要方式有两种NotePort和LoadBalancer但是这两种方式都有一定的缺点
- NodePort方式的缺点是会占用很多集群机器的端口那么当集群服务变多的时候这个缺点就愈发明显
- LB方式的缺点是每个service需要一个LB浪费、麻烦并且需要kubernetes之外设备的支持
基于这种现状kubernetes提供了Ingress资源对象Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图表示
<img src="Service/image-20240913161756342.png" alt="image-20240913161756342" style="zoom:33%;" />
实际上Ingress相当于一个7层的负载均衡器是kubernetes对反向代理的一个抽象它的工作原理类似于Nginx可以理解成在**Ingress里建立诸多映射规则Ingress Controller通过监听这些配置规则并转化成Nginx的反向代理配置 , 然后对外部提供服务**。在这里有两个核心概念:
- ingresskubernetes中的一个对象作用是定义请求如何转发到service的规则
- ingress controller具体实现反向代理及负载均衡的程序对ingress定义的规则进行解析根据配置的规则来实现请求转发实现方式有很多比如Nginx, Contour, Haproxy等等
Ingress以Nginx为例的工作原理如下
1. 用户编写Ingress规则说明哪个域名对应kubernetes集群中的哪个Service
2. Ingress控制器动态感知Ingress服务规则的变化然后生成一段对应的Nginx反向代理配置
3. Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中并动态更新
4. 到此为止其实真正在工作的就是一个Nginx了内部配置了用户定义的请求转发规则
<img src="Service/image-20240914142642245.png" alt="image-20240914142642245" style="zoom:33%;" />
## 安装helm
```bash
# 安装helmhelm在kubernetes中相当于yum是可以在线去获取资源清单快速部署服务
$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh
# 初始化,可以从 https://artifacthub.io/ 中选择一个可用的仓库地址
$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm repo list
NAME URL
bitnami https://charts.bitnami.com/bitnami
# 常见操作
$ helm repo update # 更新chart列表
$ helm show chart bitnami/apache # 查看chart基本信息
$ helm install bitnami/apache --generate-name # 部署chart
$ helm list # 查看部署包,加上--all可以看到所有的
$ helm uninstall apache-1726297430 # 删除这个安装包所有的kubernetes资源
$ helm search hub wordpress # 在 helm hub(https://hub.helm.sh)上搜索helm chart
$ helm search repo wordpress # 在repo中搜索
```
## 安装Ingress-nginx
```bash
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm pull ingress-nginx/ingress-nginx
# 修改 values.yaml 文件
修改 hostNetwork 的值为 true
dnsPolicy的值改为: ClusterFirstWithHostNet
kind类型更改为DaemonSet
ingressClassResource.default:true
# 关闭所有镜像的 digest
# 如果是本地的helm chart使用这个命令安装
$ kubectl create ns ingress
$ helm install ingress-nginx -n ingress . -f values.yaml
$ kubectl get pod -n ingress
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-7c4x8 1/1 Running 0 12s
ingress-nginx-controller-bjk4s 1/1 Running 0 12s
```
## 实验测试
创建如下两个资源模型
![image-20240914152836257](Service/image-20240914152836257.png)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: aaronxudocker/myapp:v1.0
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
spec:
replicas: 3
selector:
matchLabels:
app: tomcat
template:
metadata:
labels:
app: tomcat
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
selector:
app: tomcat
clusterIP: None
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
```
### Http代理
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-nginx
spec:
rules:
- host: nginx.iproute.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
ingressClassName: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-tomcat
spec:
rules:
- host: tomcat.iproute.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: tomcat-service
port:
number: 8080
ingressClassName: nginx
```
查看运行状态
```bash
$ kubectl get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-nginx nginx nginx.iproute.cn 80 7s
ingress-tomcat nginx tomcat.iproute.cn 80 7s
$ kubectl describe ing
Rules:
Host Path Backends
---- ---- --------
nginx.iproute.cn
/ nginx-service:80 (10.244.140.109:80,10.244.196.149:80,10.244.196.150:80)
Rules:
Host Path Backends
---- ---- --------
tomcat.iproute.cn
/ tomcat-service:8080 (10.244.140.110:8080,10.244.196.151:8080,10.244.196.153:8080)
```
访问测试
![image-20240914154740672](Service/image-20240914154740672.png)
其中nginx多次访问主机名可以看到负载均衡
![image-20240914154758983](Service/image-20240914154758983.png)
## Https代理
创建证书
```bash
# 生成证书
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=iproute.cn"
# 创建密钥
$ kubectl create secret tls tls-secret --key tls.key --cert tls.crt
```
创建资源清单
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: https-nginx
spec:
tls:
- hosts:
- nginx.iproute.cn
secretName: tls-secret # 指定秘钥
rules:
- host: nginx.iproute.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
ingressClassName: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tomcat-https
spec:
tls:
- hosts:
- tomcat.iproute.cn
secretName: tls-secret # 指定秘钥
rules:
- host: tomcat.iproute.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: tomcat-service
port:
number: 8080
ingressClassName: nginx
```
访问测试
![image-20240914155615236](Service/image-20240914155615236.png)
可以看到负载均衡
![image-20240914155628119](Service/image-20240914155628119.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Some files were not shown because too many files have changed in this diff Show More