00.尚硅谷Linux虚拟化教程(EXSI和Docker)

1 引入虚拟化的必要性

  • 美国环境保护署(EPA)报告的一组有趣的统计数据就证明了其好处。EPA 研究服务器和数据中心的能源效率时发现,实际上服务器只有 5 % 的时间是在工作。在其他时间,服务器都处于“休眠”状态。

image.png

虚拟化前:

  • 每台主机一个操作系统
  • 软件硬件紧密的结合
  • 在同一主机上运行多个应用程序通常会遭遇冲突
  • 系统的资源利用率低
  • 硬件成本高昂而且不够灵活
  • 若出现某个应用程序被入侵攻击后,整个主机的信息都会泄漏

虚拟化后:

  • 打破了操作系统和硬件的互相依赖
  • 通过封装到虚拟机的技术,管理操作系统和应用程序为单一的个体
  • 强大的安全和故障隔离
  • 虚拟机时独立于硬件的,它们能在任何硬件上运行
  • 云计算技术的底层就是虚拟化技术

1.1 定义

虚拟化,是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统OS,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率

2 虚拟化的分类

2.1 技术角度分类:

  • 全虚拟化技术
  • 半虚拟化技术/准虚拟化技术

2.1.1 全虚拟化技术:

完全虚拟化技术又叫硬件辅助虚拟化技术,最初所使用的虚拟化技术就是全虚拟化(Full Virtualization)技术,它在虚拟机(VM)和硬件之间加了一个软件层——Hypervisor,或者叫做虚拟机监控器(VMM)。

  • Hypervisor 直接运行在物理硬件之上——KVM
  • Hypervisor 运行在另一个操作系统中——QEMU 和 WINE

image.png

2.1.2 半虚拟化技术:

半虚拟化技术,也叫做准虚拟化技术。它就是在全虚拟化的基础上,把客户端操作系统进行了修改,增加了一个专门的API,这个API可以将客户端操作系统发出的指令进行最优化,即不需要Hypervisor 耗费一定的资源进行翻译操作,因此Hypervisor 的工作负担变得非常的小,因此整体的性能也有很大的提高。
image.png

2.2 架构类型分类:

  • 寄居架构:就是在操作系统之上安装和运行虚拟化程序,依赖于主机操作系统对设备的支持和物理资源的管理。
  • 裸金属架构:就是直接在硬件上面安装虚拟化软件,再在其上安装操作系统和应用,依赖虚拟层内核和服务器控制台进行管理。

image.png

2.2.1 寄居架构:

优点:简单、便于实现

缺点:安装和运行应用程序依赖于主机操作系统对设备的支持;管理开销较大,性能损耗大

举例:GSX Server,VMware Server,Workstation

2.2.2 裸金属架构:

优点:虚拟机不依赖于操作系统,可以支持多种操作系统,多种应用,更加灵活。

缺点:虚拟层内核开发难度较大

距离:VMware ESXi Server

3 EXSI

VMware ESXi 是一款行业领先、专门构建的裸机 Hypervisor。ESXi 直接安装在物理服务器上,并将其划分多个逻辑服务器,即虚拟机。属于全虚拟化技术/裸金属架构。

3.1 EXSI 安装

VMware Workstation 创建虚拟机 => 自定义(高级)=> 硬件兼容性(Workstation 16.x)=> 稍后安装操作系统 => VMware ESX(X)版本VMware ESXi 6.0 => CPU:8核,内存:8GB(不少于4GB)=> 仅主机模式 => 500G 虚拟磁盘容量,创建虚拟机。(注意:虚拟机CPU处理需要支持虚拟化功能,需要将Windows的Hyper-V、虚拟机平台、Windows 虚拟机监控程序平台功能取消)

虚拟机设置:添加一块网卡:仅主机模式 ; ISO镜像导入:VMware-VMvisor-Installer-6.0.0-2494585.x86_64.iso;启动虚拟机。根据向导一步一步的操作。在Windows 7版本以上安装客户端 VMware-viclient-all-6.0.0-2502222.exe。

Vmware ESXi 在安装队列中会识别本机的相关性能参数。EXSi 属于是裸金属&全虚拟化技术,也就是说Guest OS要使用必须经过 Hypervisor 层翻译到底层硬件,而Hypervisor 层与底层硬件交互就需要驱动,Hypervisor 本身就已经集成了许多的驱动。但是如果服务器很新,则有可能 Hypervisor 虚拟化层驱动跟服务器硬件不匹配,导致无法交互使用。第一:这就需要虚拟化厂家做适配;第二:需要自己在 EXSi 注入驱动。 image.png

EXSI 安装.png

ESXi 安装完成后,重启进入配置界面。 配置信息.png

配置 root 用户密码。再按 F2 进入系统配置(启动双网卡功能) 启动双网卡.png

配置静态IP地址后,使IP地址配置生效。 配置静态IP地址.png

使用 VMware vSphere Client.exe 进行远程连接。 使用VMware vSphere Client连接.png

3.2 ESXI 资源分割(权限管理)

需求:李四(lisi)管理MySQL,内存:2G ; 张三(zhangsan)管理LVS,内存:2G。

添加用户:主页 => 清单 => 用户信息:(登录:lisi,zhangsan,用户名:lisi,zhangsan);密码:[a-Z0-9]+特殊字符;例如:lisi@123ZHAO_123+li;zhangsan@123ZHAO_123+zhang;

分配权限:主页 => 系统管理(角色) => 创建角色 => user 角色。

创建资源池:主页 => 清单 => 创建资源池(MySQL,LVS)。

将资源池分配给对应的用户和权限。
添加用户.png
创建资源池.png

使用zhangsan,lisi登录后就可以实现对资源池的管理和控制。

3.3 EXSI SAN组网

双网卡独立化;需要用到两块网卡,并确保为开启的状态。网卡默认采用主备模式,由于网卡是属于很难损坏的网络设备,所以会让一块网卡处于空闲资源。在真实环境中,需要将该网卡使用,组网方式为一块网卡为用户通信使用(WAN),一块网卡为各个物理服务器通信使用(LAN)。

默认ESXi 双网卡为主备模式。网卡属于是低损耗的部件,所以会有一个网卡处于闲置状态。

EXSI SAN组网.png

ESXi SAN 组网(其目的是将业务网WAN与内部网LAN分割,虚拟机接收和请求公网业务走一张网卡,而接收和请求内网则走另一张网卡)。 SAN组网拓扑.png 使用SAN组网方式.png

ESXi SAN 组网方式:配置 -> 网络 -> 虚拟机 -> 创建vSphere标准交换机 -> 修改标准交换机名称 -> 查看配置 -> 完成。 使用SAN组网方式2.png 使用SAN组网方式3.png

适配器:根据自身需求选择,这里选择 vmnic1 -> 后续默认即可。 使用SAN组网方式4.png

创建虚拟机时选择两张虚拟网卡并分别分别对应不同的网络。 创建虚拟机选择2张网卡并选择不同网络.png

在 EXSi 需要创建虚拟机,使用ISO镜像文件导入方式进行创建。由三种方式:

1.使用PXE网络安装操作系统;

2.使用EXSi服务器本地存储上传ISO镜像文件;

3.使用本地计算机的ISO镜像文件。(注意:在虚拟机控制台可以使用Ctrl + Alt + Del进行虚拟机的重启) ESXi上传ISO镜像文件.png 虚拟机选择ISO0镜像文件即可.png

3.4 EXSI 其他说明

在主页 => 清单可以查看相关信息:
摘要查看本机的详细信息显示,例如资源,存储,网络等等。
虚拟机:可以查看正在使用的虚拟机配置和状态。资源分配:
可以查看虚拟机和资源池的资源分配情况。
性能:查看当前主机的资源利用率(CPU,内存,存储等等),支持导出数据功能。
用户:当前主机的用户
事件:当前系统的操作日志
权限:权限信息
配置:可以查看硬件(运行状态,处理器,内存,存储【可以创建文件夹,并本地上传镜像】,网络【可以将网卡进行分割,进行SAN组网0】,存储适配器,网络适配器,高级设置【虚拟化直通】,电源设备)和软件(获取许可的功能,时间配置,DNS和路由,身份验证服务,虚拟机启动/高级,虚拟机交换文件位置,系统资源预留,代理虚拟机设置,高级设置)。
添加虚拟交换机并配置.png

4 什么是 Docker

4.1 容器是什么

有效的将单个操作系统的资源划分到孤立的组中,以便更好的在孤立的组之间平衡有冲突的资源使用需求。

4.1.1 Docker 的由来

Docker 诞生

1.Docker 是 dotcloud 公司开源的一款产品 dotcloud 是 2010年新成立的一家公司,主要基于 PAAS(Platform as a Service)平台为开发者提供服务

2.2013 年 10 月 dotcloud 公司改名为 Docker 股份有限公司。

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图23

通常有三种云服务模型:SaaS(软件即服务),PaaS(平台即服务)和IaaS(基础架构即服务)。

  • SaaS:软件即访问代表云市场中最常用的选项。 SaaS利用互联网向其用户提供应用程序,这些应用程序由第三方供应商管理。 大多数SaaS应用程序直接通过Web浏览器运行,不需要在客户端进行任何下载或安装。
    SaaS通过大大减少安装,管理和升级软件等繁琐任务所花费的时间和金钱,为员工和公司提供了许多好处。 这让技术人员可以花更多时间来处理组织内更紧迫的事情和问题。
  • PaaS:云平台服务或平台即服务(PaaS)为某些软件提供云组件,这些组件主要用于应用程序。 PaaS为开发人员提供了一个框架,使他们可以基于它创建自定义应用程序。所有服务器,存储和网络都可以由企业或第三方提供商进行管理,而开发人员可以负责应用程序的管理。
    PaaS允许企业使用特殊的软件组件设计和创建内置于PaaS中的应用程序。由于具有某些云特性,这些应用程序或中间件具有可扩展性和高可用性。
  • IaaS:通过虚拟化技术为组织提供云计算基础架构,包括服务器、网络,操作系统和存储等。这些云服务器通常通过仪表盘或API提供给客户端,IaaS客户端可以完全控制整个基础架构。 IaaS提供与传统数据中心相同的技术和功能,而无需对其进行物理上的维护或管理。 IaaS客户端仍然可以直接访问其服务器和存储,但它们都通过云中的“虚拟数据中心”。
    IaaS客户端负责管理应用程序、运行时、操作系统,中间件和数据等方面。但是,IaaS的提供商管理服务器、硬盘驱动器、网络,虚拟化和存储。一些提供商甚至在虚拟化层之外提供更多服务,例如数据库或消息队列。

4.1.2 Docker 历程

1.Linux Container 是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。

2.Docker 是 PAAS 提供商 dotCloud 开源的一个基于LXC(Linux Container) 的高级容器引擎,源代码托管在 Github 上,基于 GO 语言并遵守 Apache 2.0 协议开源

3.Docker 设想是交付运行环境如同海运,OS如同一个货轮,每一个在 OS 基础上的软件都如同一个集装箱,用户可以通过标准化手段自由组装运行环境,同时集装箱的内容可以由用户自定义,也可以由专业人员制造。

4.Docker是什么?Docker是基于go语言(天生支持多并发多进程)并且遵循Apache2.0协议开源的LXC高级容器引擎。Docker想把我们的运行环境通过标准化的手段进行自由的组装和封装。

4.1.3 Docker 的构成

  • Docker 与 传统虚拟化(属于寄居架构/全虚拟化技术)对比

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图24 传统虚拟化底层是Hardware硬件设施,上一层为主机操作系统,再一层为Hypervisor 虚拟化监视器,再一层跑虚拟机Guest OS,相关的库和二进制再上一层跑应用程序。(传统虚拟化为寄居架构 & 全虚拟化技术

1.优点:不同的应用程序之间是通过不同的内核去隔离(内核级别的隔离)

2.缺点:过多的资源开销

Docker 容器化底层是Hardware硬件设施,上一层为主机操作系统,再一层为有Docker Engine引擎进程,在通过Docker Engine引擎进程去创建和管理应用程序容器实例。

1.优点:相同的资源规格,Docker容器实例可以创建比传统虚拟化更多的应用程序。消耗本机的资源最少,在同样的硬件资源下可以支撑更多的应用访问

2.缺点:容器的安全性较差,应用程序容器实例之间本身没有相关的安全隔离机制

  • Docker 的仓库:https://hub.docker.com
  • Docker 自身组件
    • Docker Client:Docker 的客户端
    • Docker Server:Docker Daemon的主要组成部分,接收用户通过Docker Client 发出的请求,并且按照相应的路由规则实现路由分发。
    • Docker 镜像:Docker 镜像(封装好的运行环境)运行之后变成容器(docker run)

Docker 三要素:容器(Container),镜像(Image),仓库(Registry)

容器实例:通过Docker镜像运行后得到的产物
镜像:封装好的运行环境(包括应用程序,应用程序库等等)
仓库:存放镜像的地方

4.1.4 Docker 组件间的协同方式

image.png

docker 客户端执行 docker run 命令。docker daemon 发现本地有无镜像。daemon 从Docker Hub下载镜像。下载完成后,镜像被保存在本地中。Docker daemon 启动容器。

4.1.5 Docker 化应用存在的方式

image.png Docker 的更迭:最开始的优化为共享操作系统,应用B的操作系统去掉,共享应用A的操作系统,将应用A的操作系统链接到应用B上。优化了应用B 的额外的开销,当然也带来了重要不可避免的问题。

image.png 该问题就是应用A的操作系统如果做了内核级的修改,并且该修改操作的结果只能在应用A上跑,不能在应用B上跑。如何解决,在操作系统共享的情况下,各自的依赖库和第三方软件以及软件包配置各不相同中,在上层添加空白层(应用优先级 > 底层优先级

image.png 最大的问题是如果应用A机器宕机或者关机了,共享应用A的操作系统的其他应用必定无法启动。这就是依赖性的问题。容器就是为了解决这个依赖性的问题。我们就不用操作系统进行共享,而是利用镜像(Image)进行共享。运行的应用不在是用操作系统供给,而是由镜像供给。

image.png 又出现新的问题,应用A和应用B使用的是不同的镜像,镜像文件会不会占用过多的系统存储资源。Docker 使用的镜像是分层的。镜像底层使用的是 UnionFS(联合文件系统),镜像的层级不能超过128层image.png

4.2 Docker 的安装

安装分类:Script,Yum,Rpm。

4.2.1 Script 安装 Docker:

  1. ### 以CentOS 7为例,配置阿里云Yum源 和 EPEL源
  2. ## Step 1: Yum源配置
  3. wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
  4. curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
  5. ## Step 2: EPEL源配置
  6. wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
  7. ## Step 3: 清理 Yum 源,并生成 Yum
  8. yum clean all && yum makecache && yum update -y
  9. ## Step 4: 更新 Yum 源
  10. curl -sSL https://get.docker.com/ | sh
  11. ## Step 5: 启动 Docker 服务
  12. systemctl enable --now docker
  13. -------------------------------------------------------------------------------------------
  14. ### 以CentOS 8为例,配置阿里云Yum源 和 EPEL源
  15. ## Step 1: Yum源配置
  16. wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo
  17. curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo
  18. ## Step 2: EPEL源配置
  19. yum install -y https://mirrors.aliyun.com/epel/epel-release-latest-8.noarch.rpm
  20. sed -i 's|^#baseurl=https://download.example/pub|baseurl=https://mirrors.aliyun.com|' /etc/yum.repos.d/epel*
  21. sed -i 's|^metalink|#metalink|' /etc/yum.repos.d/epel*
  22. ## Step 3: 清理 Yum 源,并生成 Yum
  23. yum clean all && yum makecache
  24. ## Step 4: 更新 Yum 源
  25. curl -sSL https://get.docker.com/ | sh
  26. ## Step 5: 启动 Docker 服务
  27. systemctl enable --now docker

4.2.2 Yum 安装 Docker:

  1. # Step 1: 安装必要的一些系统工具
  2. sudo yum install -y yum-utils device-mapper-persistent-data lvm2
  3. # Step 2: 添加软件源信息
  4. sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  5. # Step 3:修改为阿里云地址
  6. sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
  7. # Step 4: 更新并安装Docker-CE
  8. sudo yum makecache fast
  9. sudo yum -y install docker-ce
  10. # Step 4: 开启Docker服务
  11. sudo service docker start
  12. # 注意:
  13. # 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。
  14. # vim /etc/yum.repos.d/docker-ce.repo
  15. # 将[docker-ce-test]下方的enabled=0修改为enabled=1
  16. #
  17. # 安装指定版本的Docker-CE:
  18. # Step 1: 查找Docker-CE的版本:
  19. # yum list docker-ce.x86_64 --showduplicates | sort -r
  20. # Loading mirror speeds from cached hostfile
  21. # Loaded plugins: branch, fastestmirror, langpacks
  22. # docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
  23. # docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable
  24. # docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
  25. # Available Packages
  26. # Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)
  27. # sudo yum -y install docker-ce-[VERSION]
  28. -------------------------------------------------------------------------------------------
  29. # 以CentOS 8为例,
  30. # Step 1: 配置阿里云镜像和EPEL源
  31. wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo
  32. yum install -y https://mirrors.aliyun.com/epel/epel-release-latest-8.noarch.rpm
  33. # Step 2: 建议使用阿里云,清华源以及163源的Docker Yum源
  34. tee > /etc/yum.repos.d/docker.repo <<-'EOF'
  35. [dockerrepo]
  36. name=Docker Repository
  37. baseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/
  38. enabled=1
  39. gpgcheck=1
  40. gpgkey=https://yum.dockerproject.org/gpg
  41. EOF
  42. # 解决系统已经安装的 podman 报错
  43. yum erase -y podman buildah
  44. # Step 3: 下载Docker软件
  45. yum install -y docker
  46. # Step 4: 开启Docker服务
  47. sudo service docker start

4.2.3 Rpm 安装 Docker:

  1. # https://downloads.docker.com/linux/centos/7/x86_64/stable/Packages/
  2. # https://download.docker.com/linux/centos/8/x86_64/stable/Packages/
  3. # 以CentOS 8为例
  4. # Step1:下载Docker安装包和依赖包
  5. mkdir -pv /data/docker
  6. wget -O /data/docker/docker-ce-19.03.15-3.el8.x86_64.rpm https://download.docker.com/linux/centos/8/x86_64/stable/Packages/docker-ce-19.03.15-3.el8.x86_64.rpm
  7. wget -O /data/docker/docker-ce-cli-19.03.15-3.el8.x86_64.rpm https://download.docker.com/linux/centos/8/x86_64/stable/Packages/docker-ce-cli-19.03.15-3.el8.x86_64.rpm
  8. wget -O /data/docker/containerd.io-1.4.3-3.2.el8.x86_64.rpm https://download.docker.com/linux/centos/8/x86_64/stable/Packages/containerd.io-1.4.3-3.2.el8.x86_64.rpm
  9. # Step2:关闭,禁止和查看防火墙
  10. systemctl stop firewalld.service && systemctl disable firewalld.service
  11. # Step3:Docker 需要利用 iptables 进行网络通信设置
  12. yum install -y iptables-services
  13. # 将 iptables 配置清空并保存 iptables
  14. iptables -F && service iptables save
  15. # Step4:配置阿里云镜像和EPEL源
  16. wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo
  17. yum install -y https://mirrors.aliyun.com/epel/epel-release-latest-8.noarch.rpm
  18. # Step5:安装 Docker
  19. cd /data/docker
  20. # 解决系统已经安装的 podman 报错
  21. yum erase -y podman buildah && yum install -y *
  22. # Step6:开启并开机自启 docker 服务。
  23. systemctl start docker.service && systemctl enable docker.service
  24. docker info

4.2.4 Docker 镜像仓库加速配置

  1. # Step1:Docker镜像仓库加速地址
  2. sudo mkdir -p /etc/docker
  3. sudo tee /etc/docker/daemon.json <<-'EOF'
  4. {
  5. "registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com"],
  6. "exec-opts": ["native.cgroupdriver=systemd"],
  7. "log-driver": "json-file",
  8. "log-opts": {
  9. "max-size": "100m"
  10. },
  11. "storage-driver": "overlay2"
  12. }
  13. EOF
  14. # Step2:重启Docker服务
  15. systemctl daemon-reload && systemctl restart docker.service
  16. # Step3:测试Docker
  17. docker info
  18. -------------------------------------------------------------------------------------------
  19. # RPM安装方式可以进行该镜像仓库加速配置
  20. # Step1:设置权限
  21. cp /lib/systemd/system/docker.service /etc/systemd/system/docker.service
  22. chmod 777 /etc/systemd/system/docker.service
  23. # Step2:修改docker.service配置文件
  24. vim /etc/systemd/system/docker.service
  25. # ...文件内容...
  26. ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --registry-mirrors=https://po13h3y1.mirror.aliyuncs.com
  27. # ...文件内容...
  28. # Step3:重启Docker服务
  29. systemctl daemon-reload && systemctl restart docker.service
  30. # Step4:测试配置Docker加速器
  31. ps -ef | grep docker

4.2.5 Docker 化应用体验

WordPress 博客系统:

WordPress 运行环境需要如下软件的支持:

  • PHP 5.6或者更新软件
  • MySQL 5.6 或者更新软件
  • Apache 和 mod_rewrite 模块
  1. $ docker run --name db \
  2. -e MYSQL_ROOT_PASSWORD=Admin@h3c \
  3. -v /root/mysql:/var/lib/mysql \
  4. -p 3306:3306 \
  5. --restart=always \
  6. -d mysql:5.7.36
  7. # 注意!启动mysql容器后,还要进去再修改一下密码,进行密码初始化,否则wordpress会连接失败
  8. $ docker exec -it db /bin/bash
  9. root@a560df915f31:/# mysql -uroot -pAdmin@h3c
  10. mysql> CREATE DATABASE wordpress;
  11. # 若需要创建wordpress专用的用户
  12. mysql> CREATE USER 'wordpress'@'%' IDENTIFIED BY 'Admin@h3c';
  13. # 创建完成之后给予该用户相关操作权限,下面这个命令就是给该用户对wordpress数据库所有的操作权限
  14. mysql> GRANT ALL ON wordpress.* TO 'wordpress'@'%' WITH GRANT OPTION;
  15. mysql> FLUSH PRIVILEGES;
  16. # 使用 root 账号
  17. mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'Admin@h3c';
  18. mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
  19. mysql> FLUSH PRIVILEGES;
  20. mysql> exit;
  21. $ docker inspect db | grep IPAddress | awk 'NR==2{print}' | awk -F'"' '{print $4}'
  22. # --link db:mysql -> 容器关联,现在启动的容器内部可以通过mysql来访问db数据库的功能
  23. $ docker run --name MyWordPress --link db:mysql \
  24. -p 8080:80 --restart=always \
  25. --restart=always \
  26. -v wordpress:/var/www/html \
  27. -d wordpress

image.png

4.3 Docker-compose

4.3.1 多容器管理

Docker 提倡理念是“一个容器一个进程”,假设一个服务需要由多个进程组成,就需要多个容器组成一个系统,相互分工和配合对外提供完整服务。
比如:博客系统

  • 组件1:mariadb
  • 组件2:WordPress 的 apache web

在启动容器是,同一台主机下如果两个容器之间需要有数据交流,使用 —link 选项建立两个容器之间的互联,前提是建立是 mariadb 已经开启。

  1. $ docker start db
  2. $ docker start MyWordPress

停止:

$ docker stop db MyWordPress
或者
$ docker stop MyWordPress & docker stop db

4.3.2 Docker-compose 安装

容器编排工具,允许用户在一个模板(YAML格式)中定义一组相关联的容器,会根据 —link 等等参数,对启动的优先级进行排序。

官网

https://docs.docker.com/compose/compose-file/compose-file-v3/

官网下载

https://docs.docker.com/compose/install/ GitHub 项目地址:https://github.com/docker/compose

$ sudo curl -L "https://github.com/docker/compose/releases/download/v1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 若无法下载,则科学上网
# sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
$ docker-compose --version
docker-compose version 1.29.2, build 5becea4c

4.3.3 Docker-compose 用法

-f            :指定使用的 yaml 文件位置
ps            :显示所有容器信息
restart        :重新启动容器
logs        :查看日志信息
config -q    :验证 yaml 配置文件是否正确
stop        :停止容器
start        :启动容器
up -d        :启动容器项目(后台运行)
pause        :暂停容器
unpause        :恢复暂停
rm            :删除容器

4.3.3.1 测试 wordpress.yaml 文件
$ mkdir -pv /docker/wordpress
$ tee > /docker/wordpress/docker-compose.yaml <<-'EOF'
version: "3"

services:
  db:
    image: mysql:5.7.36
    restart: always
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: Admin@h3c
      MYSQL_DATABASE: wordpress
      MYSQL_USER: root
      MYSQL_PASSWORD: Admin@h3c
    volumes:
      - db:/var/lib/mysql

  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    restart: always
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      MYSQL_USER: root
      WORDPRESS_DB_USER: root
      WORDPRESS_DB_PASSWORD: Admin@h3c
      WORDPRESS_DB_NAME: wordpress

volumes:
  wordpress:
  db:
EOF

4.3.3.2 运行 yaml 文件
$ cd /docker/wordpress
# 验证 yaml 配置文件是否正确
$ docker-compose config -q
# 启动容器项目(后台运行)
$ docker-compose up -d
# 显示所有容器信息
$ docker-compose ps
# 查看日志信息
$ docker-compose logs

4.3.4 单容器管理

4.3.4.1 单一容器管理

每个容器被创建后,都会分配一个 CONTAINER ID 作为容器的唯一标识,后续对容器的启动、停止、修改、删除等所有的操作,都是通过 CONTAINER ID 来完成,偏向于数据库概念中的主键。

docker ps --no-trunc=false            :查看,不要截断输出
docker stop/start CONTAINERID        :通过容器ID停止/启动
docker start/top MyWordPress        :通过容器别名启动/停止
docker inspect MyWordPress            :查看容器的所有基本信息
docker logs MyWordPress                :查看容器日志
docker stats MyWordPress            :查看容器所占用的系统资源
docker exec 容器名 容器内执行的命令      :容器执行命令
docker exec -it 容器名 /bin/bash      :开启交互式伪终端登入容器的bash
# 通过容器ID停止/启动
$ docker stop MyWordPress1
$ docker start MyWordPress1
# 查看容器的所有基本信息
$ docker inspect MyWordPress
# 查看容器所占用的系统资源(实时)
$ docker stats MyWordPress
# 查看所有容器占用的系统资源
$ docker stats
# 查看容器日志
$ docker logs MyWordPress
# 容器执行命令
$ docker exec MyWordPress ls /
$ docker exec -it MyWordPress /bin/bash

4.3.4.2 docker run 选项的延伸
--name                    :设置容器名称
--restart=always        :容器的自动启动
-h xx.xx.xx                :设置容器的主机名(默认容器主机名为容器ID号)
--dns xx.xx.xx.xx        :设置容器使用的DNS服务器
--dns-search            :DNS搜索设置
--add-host hostname:IP    :注入hostname <> IP 解析
--rm                    :服务停止时自动删除
# --add-host hostname:IP:注入hostname <> IP 解析
$ docker run --name MyWordPress1 --add-host www.zhongzhiwei.com:10.0.0.21 --link db:mysql -p 8081:80 -d wordpress
$ docker exec -it MyWordPress1 bash
root@3649e855d5ed:/var/www/html# cat /etc/hosts
...
10.0.0.201      www.zhongzhiwei.com
172.17.0.2      mysql 9933cc1f81c2 db
172.17.0.4      3649e855d5ed
# --rm: 服务停止时自动删除
$ docker run --name MyWordPress2 --rm --add-host www.zhongzhiwei.com:10.0.0.21 --link db:mysql -p 8082:80 -d wordpress
$ docker stop MyWordPress2

4.4 Docker 基础概念

Docker 三个重要概念:仓库(Repository),镜像(Image),和容器(Container)
docker run —name MyWordPress —link db:mysql -p 8080:80 -d wordpress

docker 指令的基本用法:
docker + 命令关键字(COMMAND)+ 一系列的参数

4.4.1 Docker 基础命令

docker info            :守护进程的系统资源设置信息
docker search        :Docker 仓库的查询镜像
docker pull            :Docker 仓库的下载镜像
docker images        :Docker 镜像的查询
docker rmi            :Docker 镜像的删除
docker ps            :容器的进程查询
docker run            :容器的创建启动
docker start/stop    :容器启动/停止
docker rm            :容器删除
# Docker 指令除了单条使用外,还支持赋值,解析变量,嵌套使用
# 守护进程的系统资源设置信息
$ docker info
# DockerHub仓库的查询镜像
$ docker search nginx
# DockerHub仓库的下载镜像
$ docker pull nginx:latest
# 镜像的占用的资源是需要使用df -Th来查看,镜像大小信息只是逻辑上的大小信息,因为一个镜像是由多个镜像层(layer)组成的,而相同的镜像层本地只会存储一份,所以,真实情况下,占用的物理存储空间大小,可能会小于逻辑大小。docker images 所显示的SIZE是大于或等于镜像空间SIZE
$ docker images
# 删除 Docker 镜像
$ docker rmi nginx:latest
# 容器实例的进程查询
$ docker ps
# 查看本机Docker的所有信息
$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          3         2         1.064GB   13.26kB (0%)
Containers      4         2         211B      205B (97%)
Local Volumes   11        3         753.3MB   423.1MB (56%)
Build Cache     0         0         0B        0B
# 删除所有容器
$ docker rm nginx
$ docker rm -f $(docker ps -aq)

4.4.2 Docker 镜像特性

镜像特性: 容器创建时需要指定镜像,每个镜像都由唯一的标识 Image ID,和容器的 Container ID 一样,默认 128位,可以使用前 12位为缩略形式,也可以使用镜像名与版本号两部分组合唯一标示,如果省略版本号,默认使用最新版本标签(latest)

镜像的分层:Docker 的镜像通过联合文件系统(union filesystem)将各层文件系统叠加在一起

  • bootfs:用于系统引导的文件系统,包括bootloader(boot引导器) 和 kernel(内核),容器启动完成后会被卸载以节省内存资源
  • rootfs:位于 bootfs 之上,表现为 Docker 容器的根文件系统

    • 传统模式中,系统启动时,内核挂载 rootfs 时会首先将其挂载为“只读”模式,完整性自检完成后将其挂载为读写模式
    • Docker 中,rootfs 由内核挂载为“只读”模式,而后通过 UFS 技术挂载一个“可写”层

    image.png

  • 已有的分层只能读不能修改

  • 上层镜像优先级大于底层镜像。

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图33

4.4.3 DockerFile

4.4.3.1 镜像的诞生:

1.容器 => 镜像:docker commit CID -t xx.xx.xx

可以使用网易蜂巢镜像仓库站点:https://c.163.com/index.html | [镜像中心 - 网易云镜像中心 (163yun.com)](https://c.163yun.com/hub#/home)

镜像如果能够正常运行的话,镜像需要拥有工作在前台的守护进程至少一个。

2.DockerFile:

DockerFile 是一种被 Docker 程序解释的脚本,Dockerfile 由一条一条的指令组成,每条指令对应 Linux 下面的一条命令。Docker 程序将这些 DockerFile 指令翻译真正的 Linux 命令。Dockerfile 有自己书写格式和支持的命令,Docker 程序解决这些命令的依赖关系,类似于 Makefile。Docker 程序将读取 Dockerfile ,根据指令生成定制的 image。最大不能超过128层(行)。

# 案例1:
$ docker pull centos
$ docker run -it -d --name mycentos centos /bin/bash
[root@46b8653af4a0 /]# dnf install -y net-tools
$ docker commit -a 'zhongzhiwei <935523993@qq.com>' -m 'ADD net-tools' mycentos mycentos:v1.0
$ docker images mycentos:v1.0
$ docker run --name mytest-centos -it -d mycentos:v1.0 /bin/bash
$ docker exec -it mytest-centos /bin/bash
[root@8b01298f7360 /]# ifconfig

# 案例2:
$ docker pull hub.c.163.com/public/centos:7.2-tools
# --privileged=true 赋予真正root权限
$ docker run --name centos7-mysql -d --privileged=true hub.c.163.com/public/centos:7.2-tools /usr/sbin/init
$ docker exec -it centos7-mysql bash
[root@bea51ce809a2 /]# yum install -y mariadb*
[root@bea51ce809a2 /]# systemctl start mariadb && systemctl enable mariadb
[root@7ac68e2e33ff /]# mysqladmin -uroot password Admin@h3c
[root@7ac68e2e33ff /]# mysql -uroot -pAdmin@h3c
MariaDB [(none)]> create database shangguigu;
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| shangguigu         |
| test               |
+--------------------+
5 rows in set (0.00 sec)
[root@7ac68e2e33ff /]# exit
$ docker commit -a 'zhongzhiwei <935523993@qq.com>' -m 'ADD MariaDB' centos7-mysql centos7-mysql:v1.0
$ docker images centos7-mysql:v1.0
$ docker run --name centos-mysql -d --privileged=true centos7-mysql:v1.0 /usr/sbin/init
$ docker exec -it centos-mysql /bin/bash
[root@317f1df45d47 /]# systemctl status mariadb
[root@317f1df45d47 /]# mysql -uroot -pAdmin@h3c
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| shangguigu         |
| test               |
+--------------------+
5 rows in set (0.00 sec)

4.4.3.2 容器转换为镜像:
# 生成命令
$ docker build -t zhongzhiwei/jdk-tomcat:v1.0 .

4.4.3.3 Dockerfile 语法:
1.FROM(指定基础 image):
构建指令,必须指定并且需要在Docerfile其他指令的前面。后续的指令都依赖于该指令指令的image。FROM指令指定的基础image可以是官方库中的,也可以位于本地本地仓库。有且只有一个基础镜像。
example:
    FROM centos:7.2
    FROM centos

2.MAINTAINER(用来指定镜像创建者信息):
构建指令,用来将 image 的制作者相关的信息写入到 image 当中。当我们对该 image 执行docker inspect 命令时,输出中有相应的字段记录该信息。(Docker 逐渐淘汰该关键字)
example:
    MAINTAINER zhongzhiwei 'zhongzhiwei@163.com'

3.RUN(安装软件用):
构建指令,RUN 可以运行任何被基础 image 支持的命令。如基础 image 选择了 CentOS,那么软件管理部分只能使用 CentOS 的包管理命令。
example:
    RUN cd /tmp && curl -L 'http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.8/bin/apache-tomcat-7.0.8.tar.gz' | tar -xz
    RUN ["/bin/bash", "-c", "echo hello"]

4.CMD(设置container启动时执行的操作):
设置指令,用于container启动时指定的操作。该操作可以是执行自定义脚本,也可以是执行系统命令。该指令只能在文件中存在一次,如果有多个,则只执行最后一条。
example:
    CMD echo "Hello,World!"

5.ENTRYPOINT(设置container启动时执行的操作):
设置指令,指定容器启动时执行的命令,可以多次设置,但是只有最后一个有效。
example:
    ENTRYPOINT ls -l

# 该指令的使用分为两种亲空,一种时独自使用,另一种和CMD指令配合使用。当独自使用时,如果你还使用了CMD命令并且 CMD 是一个完整的可执行的命令,那么 CMD 指令和 ENTRYPOINT 会互相覆盖只有最后一个CMD 或者 ENTRYPOINT 有效。
    # CMD 指令将不会被执行,只有 ENTRYPOINT 指令被执行
    CMD echo "Hello,World!"
    ENTRYPOINT ls -l

# 另一种用法和 CMD 指令配合使用来指定 ENTRYPOINT 的默认参数,这时 CMD 指令不是一个完整的可执行命令,仅仅是参数部分;ENTRYPOINT 指令只能使用 JSON 方式指定执行命令,而不能指定参数。
    FROM ubuntu
    CMD ["-1"]
    ENTRYPOINT ["/usr/bin/ls"] -> 等价于ls -l

# CMD 会被 docker run 所加参数覆盖,而ENTRYPOINT不会被覆盖

6.USER(设置 container 容器的用户):
设置指令,设置启动容器的用户,默认是root用户
example:
    USER daemon = ENTRYPOINT ["memcache","-u","daemon"]

7.EXPOSE(指定容器需要映射到宿主机器的端口):
设置指令,该指令会将容器中的端口映射成宿主机器中的某个端口。当你需要访问容器的时候,可以不是用容器的IP地址而是使用宿主机的IP地址和映射后的端口。要完成整个操作需要两个步骤,首先在 Dockerfile 使用 EXPOSE 设置需要映射的容器端口,然后在运行容器的时候指定 -p 选项加上 EXPOSE 设置的端口,这样 EXPOSE 设置的端口号会被随机映射成宿主机器中的一个端口号。也可以指定需要映射到宿主机器的那个端口,这时要确保宿主机器上的端口号没有被使用。EXPOSE 指令可以一次设置多个端口号,相应的运行容器的时候,可以配套的多次使用 -p 选项。
example:
    映射一个端口
    EXPOSE 22
    相应的运行容器使用的命令
    docker run -p port1 image

    映射多个端口
    EXPOSE port1 port2 port3
    相应的运行容器使用的命令
    docker run -p port1 -p port2 -p port3 image
    还可以指定需要映射到宿主机器上的某个端口号
    docker run -p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image

8.ENV(用于设置环境变量):
构建指令,在image中设置一个环境变量
example:
    设置了后,后续的RUN命令都可以使用,container 启动后,可以通过docker inspect查看这个环境变量,也可以通过在docker run --env key=value 时设置或者修改环境变量。假如你安装了JAVA程序,需要设置JAVA_HOME,那么可以在Dockerfile中这样写:
    ENV JAVA_HOME /path/to/java/dirent

9.ADD(从 src 复制文件到 container的 dest路径):
"支持压缩包解压,建议ADD"
example:
    ADD <src> <dest>
        <src>:是相对被构建的源目录的相对路径,可以是文件或者目录的路径,也可以是一个远程的文件url;
        <dest>:是container中的绝对路径

10.COPY(从 src 复制文件到 container的 dest路径):
"不会对数据进行更改,不支持压缩包解压"
example:
    COPY <src> <dest>

11.VOLUME(指定挂载点):
设置指令,使容器中的一个目录具有持久化存储数据的功能,该目录可以被容器本身使用,也可以共享给其他容器使用。我们知道使用容器的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。当容器中的应用有持久化数据的需求时可以在 Dockerfile 中使用指令。
example:
    FROM base
    VOLUME ["/tmp/data"]

11.WORKDIR(切换目录):
设置指令,可以多次切换(相当于cd 命令),对RUN,CMD,ENTRYPOINT生效。
example:
    WORKDIR /p1 WORKDIR p2 RUN vim a.txt -> RUN cd /p1/p2 && vim a.txt

12.ONBULLD(在子镜像中执行):
ONBULLD 指令的命令在构建镜像时并不执行,而是在它的子镜像中执行。
example:
    ONBULLD ADD . /app/src
    ONBULLD RUN /usr/local/bin/python-build --dir /app/src

# 建立ADD ENV或者其他可以多次使用的参数不要使用1次以上,会使构建镜像时多一层,建议用"\"进行分开
# Docker Image 层数最大不超过 128 层
# 注意:CMD会被docker run所加的参数覆盖,而ENTRYPOINT 不会被覆盖

范例:使用Dockerfile 构建Tomcat镜像

# 将依赖包上传服务器
$ mkdir -pv /docker/tomcat
$ ls /docker/tomcat
apache-tomcat-8.5.75.tar.gz  Dockerfile  jdk-8u301-linux-x64.tar.gz

tee > Dockerfile <<-'EOF'
FROM hub.c.163.com/public/centos:7.2-tools
MAINTAINER zhongzhiwei <935523993@qq.com>

LABEL MAINTAINER="zhongzhiwei <935523993@qq.com>"

# 压缩包解压进容器中
ADD apache-tomcat-8.5.75.tar.gz /usr/local/src/
ADD jdk-8u301-linux-x64.tar.gz /usr/local/src/

# 修改文件夹名
RUN mv /usr/local/src/apache-tomcat-8.5.75 /usr/local/src/tomcat && \
    mv /usr/local/src/jdk1.8.0_301 /usr/local/src/jdk1.8.0

ENV JAVA_HOME=/usr/local/src/jdk1.8.0
ENV PATH=${JAVA_HOME}/bin:/usr/local/tomcat/bin:$PATH

EXPOSE 8080

WORKDIR /usr/local/src/tomcat
CMD [ "----------------> Success OK" ]
CMD ["/usr/local/src/tomcat/bin/catalina.sh", "run"]
EOF
# 构建镜像
$ docker build -t mytomcat:v1.0 .
# 运行容器
$ docker run -d --name test-tomcat -it mytomcat:v1.0
# 测试Tomcat容器实例
$ curl `docker inspect test-tomcat | grep IPAddress | awk "NR==2{print $2}" | awk -F'"' '{print $4}'`:8080

4.4.4 Docker 仓库构建

4.4.4.1 官方仓库构建
  • 仓库服务器配置
$ systemctl stop firewalld && systemctl disable firewalld
$ setenfore 0
$ sed -i -r '/^SELINUX=/s@(.*)=(.*)@\1=disabled@ig' /etc/selinux/config

$ docker run -it -d -v /opt/registry:/var/lib/registry \
  --name docker-registry \
  -p 5000:5000 \
  --restart always registry

$ vim /etc/docker/daemon.json
  {
    "insecure-registries": ["10.0.0.21:5000"]
  }
$ systemctl daemon-reload && systemctl restart docker

$ docker tag mycentos:v1.0 10.0.0.21:5000/tomcat:v1.0
$ docker images
  • 客户机配置
# 方法1:
$ vim /etc/sysconfig/docker
  # 增加
  --insecure-registry 10.10.10.21:5000 
# 方法2:(推荐)
$ vim /etc/docker/daemon.json
  {
    "insecure-registries": ["10.0.0.21:5000"]
  }

# 查看已有镜像
$ curl -XGET http://10.0.0.21:5000/v2/_catalog
$ docker pull 10.0.0.21:5000/tomcat:v1.0

4.4.4.2 Harbor 构建

Harbor :企业级Docker私有仓库

  • 一、安装底层需求
    • Python 应该是 2.7 或者更高版本
    • Docker 引擎应为 1.10 或者更高版本
    • Docker Compose 需要为 1.6.0 或者更高版本
$ sudo curl -L "https://github.com/docker/compose/releases/download/v1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 若无法下载,则科学上网
# sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
$ docker-compose --version
docker-compose version 1.29.2, build 5becea4c
----------------------------------------------------------------------------------------
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.9.0/docker-compose-$(uname -s)-$(uname -m)" > /usr/local/bin/docker-compose
  • 二、Harbor 安装:
  • Harbor 官方地址:https://goharbor.io/
  • GitHub站点:https://github.com/goharbor/harbor
  • Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,可以用来构建企业内部的Docker镜像仓库。它在Docker的开源项目 Distribution的基础上,添加了一些企业需要的功能特性,如镜像同步复制、漏洞扫描和权限管理等。
1.解压软件包:tar -xvf harbor-offline-installer-<version>.tgz
mkdir -pv /docker/harbor/
# 离线包:https://github.com/goharbor/harbor/releases/download/v1.10.10/harbor-offline-installer-v1.10.10.tgz
# 在线包:https://github.com/goharbor/harbor/releases/download/v1.10.10/harbor-online-installer-v1.10.10.tgz

2.配置harbor.cfg
必选参数
- hostname:目标的主机名或者完全限定域名
- ui_url_protocol:http 或者 https。默认为http
- db_password:用于db_auth的MySQL数据库的根密码。更改次密码进行任何生成用途
- max_job_workers:(默认值为3)作业服务中的复制工作人员的最大数量,对于映射复制作业,工作人员将存储库的所有标签同步到远程目标。增加次数字允许系统中更多的并发复制作业。但是,由于每个工作人员都会消耗一定数量的网络/CPU/IO资源,请根据主机的硬件资源,仔细选择该属性的值。
- customize_crt:(on 或者 off。默认是 on)当此属性打开时,prepare 脚本将为注册表的令牌的生成/验证创建私钥和根证书
- ssl_cert:SSL证书的路径,仅当协议设置为https时才应用
- ssl_cert_key:SSL密钥的路径,仅当协议设置为https时才应用
- secretkey_path:用于在复制策略中加密或者解密远程注册表的密码的密钥路径

3.创建HTTPS证书以及配置相关目录权限
$ openssl genrsa -des3 -out server.key 2048
> Enter pass phrase for server.key:Admin@h3c
> Verifying - Enter pass phrase for server.key:Admin@h3c

# 创建证书请求
$ openssl req -new -key server.key -out server.csr
> Enter pass phrase for server.key:Admin@h3c
> Country Name (2 letter code) [XX]:CN
> State or Province Name (full name) []:BJ
> Locality Name (eg, city) [Default City]:BJ
> Organization Name (eg, company) [Default Company Ltd]:shangguigu
> Organizational Unit Name (eg, section) []:shangguigu
> Common Name (eg, your name or your server's hostname) []:hub.shangguigu.com
Email Address []:zhongzhiwei@163.com

Please enter the following 'extra' attributes
to be sent with your certificate request
> A challenge password []:
> An optional company name []:
# 拷贝证书
$ cp server.key server.key.org

$ openssl rsa -in server.key.org -out server.key
> Enter pass phrase for server.key.org:Admin@h3c
writing RSA key
# 创建证书
$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
$ mkdir -pv /data/cert
$ mv server.* /data/cert
$ chmod -R 777 /data/cert

4.运行脚本进行安装
$ cd /docker/harbor/
$ vim harbor.yml
hostname: hub.shangguigu.com
certificate: /data/cert/server.crt
private_key: /data/cert/server.key
harbor_admin_password: Harbor12345

# 配置文件初始化
$ ./prepare
$ ./install.sh

5.访问测试
https://reg.yourdomain.com 的管理员门户(将 reg.yourdomain.com 更改为你的主机名在harbor.cfg)。请注意,默认管理员用户名/密码为 admin/Harbor12345

6.上传镜像进行上传测试
a.指定镜像仓库地址(客户端与服务器端都需要执行)
$ vim /etc/docker/daemon.json
{
    "insecure-registries": ["<serverip>"]
    "insecure-registries": ["10.0.0.21"]
}
# 若使用域名,还需要将IP与域名映射关系写入hosts文件
$ vim /etc/hosts
$ systemctl daemon-reload && systemctl restart docker

b.下载测试镜像
docker pull hello-world

c.给镜像重新打标签
$ docker tag hello-world <serverip>/hello-world:latest

d.登录进行上传
$ docker login <serverip>

7.其他Docker客户端下载测试
a.指定镜像仓库地址
vim /etc/docker/daemon.json
{
    "insecure-registries": ["<serverip>"]
    "insecure-registries": ["10.0.0.21"]
}

b.下载测试镜像
$ docker login 10.0.0.21
$ docker pull <serverip>/hello-world:latest

image.png

三、Harbor 原理说明 1.软件资源介绍 Harbor 是 VMware 公司开源的企业级 DockerResgistry 项目,项目地址为https://github.com/goharbor/harbor。其目标是帮助用户迅速搭建一个企业级的DockerRegistry服务,它以Docker公司开源的Registry为基础,提供管理UI,基于角色的访问控制(Role Based Access Control),AD/LDAP 集成,以及审计日志(Auditlogging)等企业用户需求的功能,同时还原生支持中文。Harbor 的每个组件都是以 Docker 容器的形式创建的,使用 Docker Compose 来对它进行部署。用于部署 Harbor 的Docker Compose模板位于 /Deployer/docker-compose.yml ,由5个容器组成,这几个容器通过Docker link的形式连接在一起,在容器之间通过容器名字互相访问。对于终端用户,只需要暴露 proxy (即Nginx)的访问端口。

- Proxy            :由Nginx服务器构成的反向代理
- Registry    :由Docker官方的开源 Registry 镜像构成的容器实例
- UI            :即架构中的core services,构成此容器的代码的Harbor项目的主体(B/S架构)
- MySQL        :由官方MySQL镜像构成的数据库容器
- Log            :运行着 rsyslogd 的容器,通过 log-driver 的形式收集其他容器的日志

2.Harbor 特性

a.基于角色控制:用户和仓库都是基于项目进行组织的,而用户基于项目可以拥有不同的权限
b.基于镜像的复制策略:镜像可以在多个Harbor实例之间进行复制
c.支持LDAP:Harbor的用户授权可以使用已经存在LDAP用户
d.镜像删除 & 垃圾回收:Image 可以被删除并且回收Image 占用的空间,绝大部分的用户操作APU,方便用户对系统进行扩展
e.用户UI:用户可以轻松的浏览,搜索镜像仓库以及对项目进行管理
f.轻松的部署功能:Harbor提供了 online、offline安装,除此之外还提供了 virtualappliance安装
g.Harbor 和 docker registry关系:Harbor实质上是对 Docker Registry 做了封装,扩展了自己的业务模块

image.png

3.Harbor 认证流程

a.docker daemon从docker registry 拉取镜像
b.如果docker registry 需要进行授权时,docker registry将会返回 401 Unauthorized 响应,同时在响应中包含了dcoker client如何进行认证的信息
c.docker client根据docker registry 返回的信息,向 auth server发送请求获取认证 token
d.auth server 则根据自己的业务实现去验证提交的用户信息是否符合业务要求。
e.用户数据仓库返回用户的相关信息
f.auth server将会根据查询的用户信息,生成token令牌,以及当前用户所具有的相关权限信息。上述就是完整的授权过程,当用户完成上述过程以后便可以执行相关的 pull/push 操作。认证信息会每次都带在请求头中

image.png

4.Harbor 认证流程

a.首先,请求被代理容器监听拦截,并跳转到指定的认证服务器
b.如果认证服务器配置了权限认证,则会返回401。通过docker client在特定的请求中需要上一个合法的token。而认证的逻辑地址则指向架构图中的core services
c.当docker client接受到错误的code,client 就会发送认证请求(带有用户名和密码)到core services进行basic auth 认证
d.当C的请求发送给nginx以后,nginx会根据配的认证地址将带有用户名和密码的请求发送到core services
e.core services获取用户名和密码以后对用户信息进行认证(自己的数据库或者介入LDAP都可以)。成功以后,返回认证成功的信息

image.png

4.5 Docker 网络通信

4.5.1 Docker 网络通信

image.png

  • 容器与容器之间
  • 容器访问外部网络
  • 外部网络访问容器

1.Docker使用Linux桥接,在宿主机虚拟一个 Docker 容器网桥(docker0),Docker启动一个容器时会根据Docker 网桥的网段分配给容器一个IP地址,称为 Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能通过容器的Container-IP直接通信。

2.docker run的时候,没有指定 network 的话默认使用的网桥模式就是 bridge,使用的就是 docker0。在宿主机 ifconfig ,就可以看到 docker0 和自己 create 的network(后面说)eth0、eth1、eth2 …… 代表网卡一、网卡二、网卡三……,lo 代表本地回环网卡,127.0.0.1.即 localhost,inet addr 用来表示网卡的IP地址

3.网桥 docker0 创建一对对等虚拟设备接口一个叫 veth ,另一个叫 eth0,成对匹配

(1)整个宿主机的网桥模式都是 docker0,类似一个交换机有一堆接口,每个接口叫 veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此连通(这样一堆接口叫 veth pair);

(2)每个容器实例内部也有一块网卡,每个接口叫 eth0

(3)docker0 上面的每个 veth0 匹配某个容器实例内部的 eth0,两两配对,一一匹配。

通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的IP,此时两个容器的网络是互通的。

netfilter 规则

(1)容器访问外部网络

(2)外部网络访问容器

NameSpace

# SNAT(源网络地址转换,内部访问外部)
# 其作用是将 IP数据包的源地址转换成另一个地址
$ iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -o docker0 -j MASQUERADE
# DNAT(外部访问内部)
$ docker run -d -p 80:80 apache

$ iptables -t nat -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
$ iptbales -t nat -A DOCKER ! -i docker0 -p tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80
内部地址要访问公网上的服务时(如web访问),内部地址会主动发起连接,由路由器或者防火墙上的网关对内部地址做个地址转换,将内部地址的私有IP转换为公网的公有IP,网关的这个地址转换称为SNAT,主要用于"内部共享IP访问外部"。
当内部需要提供对外服务时(如对外发布web网站),外部地址发起主动连接,由路由器或者防火墙上的网关接收这个连接,然后将连接转换到内部,此过程是由带有公网IP的网关替代内部服务来接收外部的连接,然后在内部做地址转换,此转换称为DNAT,主要用于"内部服务对外发布"。
namespace 系统调用系统 隔离内容 内核版本
UTS CLONE_NEWUTS 主机名和域名 2.6.19
IPC CLONE_NEWIPC 信号量、消息队列和共享内存 2.6.19
PID CLONE_NEWPID 进程编号 2.6.24
Network CLONE_NEWNET 网络设备,网络栈,端口等 2.6.29
Mount CLONE_NEWNS 挂载点(文件系统) 2.4.19
User CLONE_NEWUSER 用户和用户组 3.8

4.5.2 Docker 网络模式修改

  • Docker 进程网络修改
  • Docker 容器网络修改

范例:

  • 暴露端口
  • -p / P 选项的使用格式
  • 自定义 Docker0 网桥的网络地址
-b,--bridge="":指定Docker使用的网桥设备,默认情况下 Docker 会自动创建和使用 docker0 网桥设备,通过此参数可以使用已经存在的设备

--bip:指定 docker0 的IP和掩码,使用标准的CIDR形式,如10.10.10.10/24

--dns:配置容器的DNS,在启动 docker 进程是添加,所有容器全部生效
--dns:用于指定启动的容器的DNS

--net:用于指定容器的网络通信方式,有以下四个值
  - bridge:Dcoker 默认方式,网桥模式
  - none:容器没有网络栈(某些容器没有必要需要网络,例如做计算数据)
  - container:使用其他容器的网络栈,Docker 容器会加入其他容器的 network namespace
  - host:表示容器使用 Host 的网络,没有自己独立的网络栈,容器可以完全访问 Host 的网络,不安全
网络模型 简介
bridge 为每一个容器分配、设置IP等,并将容器连接到一个docker0
虚拟网桥,默认为该模式
host 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口
none 容器有独立的 Network Namespace(网络名称空间),但是并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP等
container 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP
,端口范围
# 下载测试镜像
$ docker pull busybox

# 1.bridge 网络模式
$ docker run -it --net bridge --name b1 -d busybox /bin/sh
docker exec -it b1 /bin/sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
...

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
...

# 2.none 网络模式
$ docker run -it --net none --name b2 -d busybox /bin/sh
$ docker exec -it b2 /bin/sh
/ # ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
...

# 3.container 网络模式
$ docker run -it --net container:b1 -d --name b3 busybox /bin/sh
$ docker exec -it b3 /bin/sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
...

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
...

# 4.host 网络模式
$ docker run -it --net host -d --name b4 busybox /bin/sh
$ docker exec -it b4 /bin/sh
/ # ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:82:43:B6:42
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
...

eth0      Link encap:Ethernet  HWaddr 00:0C:29:96:FE:AF
          inet addr:10.0.0.22  Bcast:10.0.0.255  Mask:255.255.255.0
...

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
...
-p :<ContainerPort>                    :将指定的容器端口映射所有地址的一个动态端口
-p <HostPort>:<ContainerPort>        :映射至指定的主机端口(*)
-p <IP>::<ContainerPort>            :映射至指定的主机的IP的动态端口
-p <IP>:<HostPort>:<ContainerPort>    :映射至指定的主机IP的主机端口(*)
-P(大)                                :暴露所需要的所有端口        

(*)docker port ContainerName 可以查看容器当前的映射关系
$ docker port nginx
8080/tcp -> 0.0.0.0:80
8080/tcp -> :::80
# 修改 /etc/docker/daemon.json 文件
$ vim /etc/docker/daemon.json 
{
    "bip": "192.168.1.5/24",
    "fixed-cidr": "10.20.0.0/16",
    "fixed-cidr-v6": "2001:db8::/64",
    "mtu": "1500",
    "default-gateway": "10.20.1.1",
    "default-gateway-v6": "2001:db8:abcd::89",
    "dns": ["10.20.1.2", "10.20.1.3"]
}

4.5.3 常见隔离方式

  • 基础命令说明
  • 不同网络空间进行隔离
  • Linux 桥接器进行主机间通信-1
  • Linux 桥接器进行主机间通信-2
docker run -it --net bridge --name b1 -d busybox /bin/sh
docker run -it --net bridge --name b2 -d busybox /bin/sh
docker network ls:查看当前可用的网络类型
docker network create -d 类型 网络空间名称
  类型分为:
  overlay network(覆盖网络,不同的主机之间容器可以进行通信)
  bridge network

---------------------------------------------------------------------------------------
$ docker network create -d bridge lamp
$ docker network create -d bridge lnmp
$ ip addr
# 测试
$ docker run -it --name lamp-b1 -d --net lamp busybox /bin/sh
$ docker run -it --name lamp-b2 -d --net lamp busybox /bin/sh
$ docker run -it --name lnmp-b1 -d --net lnmp busybox /bin/sh
$ docker run -it --name lnmp-b2 -d --net lnmp busybox /bin/sh
# 在同一个网络名称空间可以进行通信
---------------------------------------------------------------------------------------
$ docker network create -d bridge --subnet "172.26.0.0/16" --gateway "172.26.0.1" my-bridge-network

$ docker run -d --net my-bridge-network --name test1 hub.c.163.com/public/centos:7.2-tools
$ docker run -d --name test2 hub.c.163.com/public/centos:7.2-tools
# 两台不同主机的容器之间通信
# 10.0.0.21 和 10.0.0.22
# 10.0.0.21 主机操作
$ docker pull busybox
$ docker run -it --name b21 -d busybox /bin/sh
$ docker inspect b21 | grep "IPAddress" | awk 'NR==2{print $2}' | awk -F'"' '{print $2}'
172.17.0.5

# 10.0.0.22 主机操作
$ docker pull busybox
$ docker run -it --name b22 -d busybox /bin/sh
$ docker inspect b22 | grep "IPAddress" | awk 'NR==2{print $2}' | awk -F'"' '{print $2}'
172.17.0.4
# 两台主机容器之间是无法进行通信的

# 创建Linux桥接器
$ cd /etc/sysconfig/network-scripts
$ cp ifcfg-eth0 ifcfg-br0
$ echo "BRIDGE=br0" >> /etc/sysconfig/network-scripts/ifcfg-eth0
# *并且将该配置文件的相关IP地址去掉*
# 以下为参考参数(修改IP地址参数即可)
    DEVICE=br0
    TYPE=Bridge
    ONBOOT=yes
    BOOTPROTO=static
    IPADDR=192.168.216.131
    NETMASK=255.255.255.0
    GATEWAY=192.168.216.2
    DNS1=114.114.114.114
    DNS2=8.8.8.8
# 实验参数为
$ vim ifcfg-br0
DEVICE=br0
TYPE=Bridge
ONBOOT=yes
BOOTPROTO=static
IPADDR=10.0.0.21
NETMASK=255.255.255.0
GATEWAY=10.0.0.2
DNS1=114.114.114.114
DNS2=8.8.8.8
# 重启网络服务
$ systemctl restart network
$ ifconfig eth0 && ifconfig br0
yum install -y git
git clone https://github.com/jpetazzo/pipework
cp pipework/pipework /usr/local/bin/
chmod a+x /usr/local/bin/pipework

$ docker run -it -d --net none --name test1 busybox /bin/sh
$ pipework br0 test1 10.10.10.16/24

$ docker run --name -it tomcat --net=none -d tomcat
$ pipework br0 tomcat 10.10.10.20/24

4.6 数据存储

4.6.1 数据卷特性

  • Docker 镜像由多个只读层叠加而成(UnionFS联合文件系统)启动容器时,Docker 会加载只读镜像层并在镜像栈顶部添加一个读写层
  • 如果运行中的容器修改了现有的一个已经存在的文件,那么该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,次即”写时复制”机制

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图39

数据卷的意义

  • 关闭并重启容器,其数据不受影响;但是删除 Docker 容器,则其改变将会全部丢失
  • 存在的问题:
    • 存在于联合文件系统中,不易于宿主机访问
    • 容器间数据共享不便
    • 删除容器其数据会丢失
  • 解决方案:”
    • “卷” 是容器上的一个或者多个”目录”,此类目录可绕过联合文件,与宿主机上的某目录”绑定”

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图40

  • Volume 可以在运行容器时即完成创建与绑定操作。当然,前提需要拥有对应的申明(可以不需要人为干预)
  • Volume 的初衷就是数据持久化

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图41

  • 数据卷类型
    • Bind mount volume(绑定卷) 存储的位置机器的任何位置
    • Docker-managed volume(Docker管理卷) 存储的位置一般都是/var/lib/docker/vfs/dir

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图42

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图43

4.6.2 容器中的数据卷

  • 在容器中使用 Volumes

范例:Docker-managerd Volumes

范例:Bind-mount Volumes

范例:Union Volumes

范例:删除容器即将数据卷删除

- Docker-managerd Volumes
$ docker run -it --name roc -v MOUNTDIR roc/lamp:v1.0
$ docker inspect -f {{.Mounts}} roc

- Bind-mount Volumes
$ docker run -it --name roc -v HOSTDIR:VOLUMEDIR roc/lamp:v1.0

- Union Volumes
$ docker run -it --name roc --volumes-from ContainerName roc/lamp:v1.0
docker run --name test-wordpress -d wordpress
docker run --name test-tomcat -d tomcat

docker inspect test-wordpress && docker inspect test-tomcat
# Docker 在删除容器时,并不会删除容器管理卷

$ vim Dockerfile
FROM centos
RUN touch /tmp/1.txt
RUN mkdir /data
CMD tail -f /tmp/1.txt
###
$ docker build -t -t test-centos:v1.0 .
$ docker run --name test-centos-1 -d test-centos:v1.0
$ docker exec -it test-centos-1 /bin/bash
[root@d20e3ad1a40b /]# touch /data/f1
[root@d20e3ad1a40b /]# exit
# 并没有进行数据卷挂载,容器删除后数据丢失

# 修改Dockerfile 添加 VOLUME
$ vim Dockerfile
FROM centos
RUN touch /tmp/1.txt
RUN mkdir /data
VOLUME /data
CMD tail -f /tmp/1.txt
###
# 或者使用命令run 时进行添加
$ docker run -it -d --name test-centos-1 -v /data test-centos:v1.0
###
$ docker build -t test-centos:v2.0 .
$ docker run --name test-centos-2 -d test-centos:v2.0
$ docker exec -it test-centos-2 /bin/bash
[root@327a8665cda5 /]# touch /data/f1.txt
[root@327a8665cda5 /]# exit

$ docker inspect test-centos-2 (查看Mounts参数信息)
$ docker inspect test-centos-1 (查看Mounts参数信息)

# 若删除容器时想要删除数据卷
$ docker rm -f -v test-centos-2
# 不带 VOLUME 参数的镜像
$ docker images test-centos:v1.0

docker run -it --name test-centos-1 -d test-centos:v1.0
docker run -it --name test-centos-2 -v /data/test-centos-2:/data -d test-centos:v1.0

$ docker exec -it test-centos-2 /bin/bash
[root@419606ba64e8 /]# touch /data/1.txt
$ ls /data/test-centos-2
1.txt

# 带 VOLUME 参数的镜像
$ docker images test-centos:v2.0
# 当指定数据卷路径时,镜像就不会使用容器管理卷(优先级:指定数据卷 > 容器管理卷)
docker run -it --name test-centos-3 -d test-centos:v2.0
docker run -it --name test-centos-4 -v /data/test-centos-4:/data -d test-centos:v2.0

# 容器之间共享数据
docker run -it --name test-centos-5 -v /data/test-centos:/data -d test-centos:v1.0
docker run -it --name test-centos-6 -v /data/test-centos:/data -d test-centos:v1.0

$ docker exec -it test-centos-5 /bin/bash
[root@2a8670c3c47a /]# touch /data/f1.txt
[root@2a8670c3c47a /]# exit
$ touch /data/test-centos/{host-xiaoming,host-xiaowang}

$ docker exec -it test-centos-6 /bin/bash
[root@d2d7e140591e /]# ls /data/
f1.txt  host-xiaoming  host-xiaowang
# 该案例是借用了宿主机的目录,实现多容器之间的数据共享
# 若不想借助宿主机的目录,但又想实现多容器之间的数据共享呢?
# 答案:Union Volume 就可以实现
# 带 VOLUME 参数的镜像
$ docker images test-centos:v2.0
# 本身镜像需要有VOLUME的路径
docker run -it --name test-centos-7 -d test-centos:v2.0
docker run -it --name test-centos-8 -d --volumes-from test-centos-7 test-centos:v2.0

$ docker exec -it test-centos-7 /bin/bash
[root@cbb09280d311 /]# touch /data/test-centos-7
[root@cbb09280d311 /]# exit
$ docker exec -it test-centos-8 /bin/bash
[root@5073d1d260db /]# ls /data/
test-centos-7
[root@5073d1d260db /]# touch /data/test-centos-8
# 查看 test-centos-7 test-centos-8 的 /data 数据
# Union Volumes可以实现多容器之间的数据共享
# 当删除test-centos-7容器后,test-centos-8的数据依旧存在
$ docker rm -f -v test-centos-7
$ docker exec -it test-centos-8 /bin/bash
[root@5073d1d260db /]# touch /data/test-centos-9
[root@5073d1d260db /]# ls
test-centos-7  test-centos-8 test-centos-9
[root@5073d1d260db /]# exit
$ docker run -it --name test-centos-9 --volumes-from test-centos-8 -d test-centos:v2.0
$ docker exec -it test-centos-9 /bin/bash
[root@6c363c9b2e07 /]# ls /data/
test-centos-7  test-centos-8  test-centos-9
$ docker rm -f -v <容器ID | 容器名称>

4.6.3 存储驱动

  • 存储驱动

Docker 存储驱动(Storage Driver)是 Docker 的核心组件,它是Docker 实现分成镜像的基础

  1. Device Mapper(DM):性能和稳定性存在问题,不推荐生产环境使用(内核低于3.18 Docker存储引擎使用的是 DM)
  2. btrfs:社区实现了 btrfs driver,稳定性和性能存在问题
  3. overlayfs:内核 3.18,overlayfs 进入主线,性能和稳定性优异,第一选择
# 查看本机的内核版本
$ uname -r
# 查看Docker使用的存储引擎
$ docker info | grep Storage
  • Docker overlayfs driver -1

参考网址: https://www.cnblogs.com/wdliu/p/10483252.html https://cloud.tencent.com/developer/article/1684523

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图44

在上述图中可以看到三个层结构,即:lowerdir、uperdir、merged,其中lowerdir是只读的image layer,其实就是rootfs,我们知道image layer可以分很多层,所以对应的lowerdir是可以有多个目录。而upperdir则是在lowerdir之上的一层,这层是读写层,在启动一个容器时候会进行创建,所有的对容器数据更改都发生在这里层最后merged目录是容器的挂载点(合并层,把lowerdir和uperdir合并展示),也就是给用户暴露的统一视角。而这些目录层都保存在了/var/lib/docker/overlay2/或者/var/lib/docker/overlay/(如果使用overlay)。

1.Docker Constructs:
- Image layer:镜像层,只读层
- Container layer:容器层,读写层(写时复制)
- Container mount:容器挂载点,用户看到的信息

2.OverlayFS Constructs:
- lowerdir:Image layer
- uperdir:Container layer
- merged:Container mount

底层出现的文件,最上层一定可以看到;最上层有的文件,会把下层展示在上层的文件覆盖掉;

范例:测试

mount -t overlay overlay -o lowerdir=./low,upperdir=./upper,workdir=./work ./merged

$ mkdir -pv /var/overlay/{low,upper,work,merged}
$ cd /var/overlay
$ mount -t overlay overlay -o lowerdir=./low,upperdir=./upper,workdir=./work ./merged
$ echo "aaa" > low/1.txt
$ echo "bbb" > upper/2.txt
$ tree
.
├── low
│   └── 1.txt
├── merged
│   ├── 1.txt
│   └── 2.txt
├── upper
│   └── 2.txt
└── work
    └── work

5 directories, 4 files

$ cat merged/1.txt
aaa
$ echo "bbb" >> merged/1.txt
$ cat merged/1.txt
aaa
bbb
$ cat low/1.txt
aaa
$ tree
.
├── low
│   └── 1.txt
├── merged
│   ├── 1.txt
│   └── 2.txt
├── upper
│   ├── 1.txt
│   └── 2.txt
└── work
    └── work

5 directories, 5 files
$ cat upper/1.txt # 由于写时复制的机制
aaa
bbb
$ rm -rf merged/1.txt
ls -l upper/
total 4
c--------- 1 root root 0, 0 Jan 23 00:07 1.txt (在OverlayFS中代表已经删除了)
-rw-r--r-- 1 root root    4 Jan 22 23:53 2.txt
# upper层1.txt以块设备的方式存在,底层low层的1.txt被upper层的1.txt覆盖掉,从而不会显示在merged
$ rm -rf upper/1.txt
$  tree
.
├── low
│   └── 1.txt
├── merged
│   ├── 1.txt
│   └── 2.txt
├── upper
│   └── 2.txt
└── work
    └── work

5 directories, 4 file
$ cat merged/1.txt
aaa
  • 修改为 Overlay 存储驱动
$ echo "overlay" > /etc/modules-load.d/overlay.conf
$ cat /proc/modules | grep overlay
$ reboot

$ vim /etc/systemd/system/docker.service
  --storage-driver=overlay \

5 Docker 资源限制

默认情况下,Docker 中的进程会尽可能的榨干主机操作系统的所有资源(例如:CPU,内存以及磁盘IO等)但是如果Docker 中的某个进程出现了一定的问题,那么该进程会无休止的向外部的操作系统索取资源,最终会造成容器关闭,进程杀死等一系列问题。所以对于生产环境来说,Docker 容器实例的资源限制是必须做的

5.1 Docker 内存限制

重点提示

  • CGroup 是 Control Groups 的缩写,是Linux 内核提供的一种可以限制、记录、隔离进程组(Process Groups)所使用的物理资源(如 CPU Memory I/O 等等)的机制。2007 年进入 Linux 2.6.24 内核,CGroups 不是全新创造的,它将进程管理从 cpuset 中剥离出来,作者是 Google 的 Paul Menage
  • 默认情况下,如果不对容器做任何限制,容器能够占用当前系统能给容器提供的所有资源
  • Docker 限制可以从Memory,CPU,Block I/O 三个方面
  • OOME:Out Of Memory Exception(内存不足错误)
    • 一旦发生 OOME,任何进程都有可能被杀死,包括 Docker Daemon 在内
    • 为此,Docker 调整了 Docker Daemon 的OOM 优先级,以免被内核关闭
  • CGroups 是 Docker 做容器实例资源限制的底层技术
如果不对容器做资源限制,容器内部的进程会无休止的使用系统资源,会造成操作系统的瘫痪。Linux 内核支持 OOME 机制。Linux 内核自身的保护机制,OOME对 init和 systemd进程(绝对不会杀死的进程)之外,OOME 会根据进程的权重,对进程进行杀死 kill。
若操作系统的资源已经无法供给系统内核,那么OOME会随机杀死kill 用户态的应用进程(根据权重比例)。
总结:
- CGroups 是容器资源限制的底层技术
- 限制方面可以有 Memory , CPU , Block I/O
- 不对容器做任何限制,容器能够占用当前系统能给容器提供的所有资源
- 为了避免被OOME杀死Docker Daemon进程,所以Docker Daemon的OOM优先级比较高

重点提示

  • 为应用做内存压力测试,理解正常业务需求下使用的内存情况,然后才能进入生产环境使用
  • 一定要限制容器的内存使用上限
  • 尽量保证主机的资源充足,一旦通过监控发现资源不足,就进行扩容或者对容器进行迁移
  • 如果可以(内存资源充足的情况),尽量不要使用 SWAP,SWAP 的使用会导致内存计算复杂,对调度器非常不友好

设置方式 在Docker 启动参数中,和内存限制有关的包括(参数的值一般是内存的大小,也就是一个正数,后面跟着 内存单位 b,k,m,g,分别对应 bytes,KB,MB和GB):

-m, --memory        :容器能使用的最大内存大小,最小值为 4M(Docker本身的限制) # 容器内存资源的硬限制(如设置为10M,则容器的内存就一定不能超过10M的内存值)
--memory-swap        :容器能够使用的 SWAP 大小
--memory-swappiness    :默认情况下,主机可以把容器使用的匿名页(anonymous page) swap 出来,可以设置一个 0-100 之间的值,代表允许 swap 出来的比例
--memory-reservation:设置一个内存使用的 soft limit(应用程序的最小值),设置值小于 -m 设置 # 容器内存资源的软限制(如设置为10M,则容器的内存可以超过10M的内存值,但是当系统内存资源不够时,那么会压低该容器的内存值到10M)
--kernel-memory    :容器能够使用 kernel memory 大小,最小值为 4M。 # 容器内核的内存的使用大小,一般不进行限制
--oom-kill-disable    :是否运行 OOM 的时候杀死容器。只有设置了 -m,才可以这个选项设置为 false,否则容器会耗尽主机内存,而且导致主机应用被杀死。

设置解释

—memory-swap —memory 功能
正数S 正数M 容器可用总空间为S,其中RAM为M,SWAP为(S-M)
若S=M,则无可用SWAP资源
0 正数M 相当于未设置SWAP(unset)
unset 正数M 若主机(Docker Host)启用了SWAP,
则容器的可用SWAP为2 * M()
-1 正数M 若主机(Docker Host)启用了SWAP,
则容器可使用最大至主机上所有SWAP空间资源
1. 若--memory-swap为10M,--memory为4M,则代表能用到的物理内存大小为4M,虚拟内存(SWAP交换空间)为6M,--memory-swap的值是物理内存 + 虚拟内存的总和
2. 若--memory-swap为4M,--memory为4M,则代表能用到的物理内存大小为4M,真正的虚拟内存(SWAP交换空间)为0M
###
3. 若--memory-swap为0M,--memory为4M,则真正使用的SWAP空间为2 * 4 =8M,则使用的内存空间为 12 M
( 4Ms + 8M = 12M )
# --memory-swap设置为 0 和 unset 意思相同

--memory-swap为-1,则可以使用宿主机所有的虚拟内存(SWAP交换空间)

注意:在容器内使用 free 命令可以看到的SWAP空间并不具有其所展现出的空间指示意义

5.2 Docker CPU限制

CPU 限制说明 Docker 提供的 CPU 资源限制选项可以在多核系统上限制容器能利用哪些 vCPU(虚拟CPU),而对容器最多能使用的 CPU 时间有两种限制方式:

  • 一是有多个CPU密集型(对CPU的请求十分的频繁)的容器竞争 CPU时,设置各个容器能使用的 CPU 时间相对比例
  • 二是以绝对的方式设置容器在每个调度周期内最多能使用的CPU时间

扩展:

# 可以听到会有2核2线程,4核8线程,8核16线程
# 如果是2核2线程的话,就可以看成是2颗CPU;如果是4核8线程可以看成是8颗CPU,如果是8核16线程就可以看成是16颗CPU
# 2核2线程,就是一核就只能处理一个线程;
# 4核8线程,8核16线程 -> 由Intel 发明的黑科技,就是一颗CPU在同一时间可以处理两个线程;当然是通过时间片切换做到的。

CPU 限制方式 - 1

# CPU集:
# 例如2核4线程,就理解为4颗CPU,则CPU的编号值就是对应的0,1,2,3;
# 如果是--cpuset-cpus="0"就代表只能使用到物理处理器的第一个CPU;
# 如果是--cpuset-cpus="1,2"就代表使用到物理处理器的第一,三个CPU;
# 如果是--cpuset-cpus="0-2"就代表使用到物理处理器的第一,二,三个CPU。
--cpuset-cpus    :允许使用的 CPU集,值可以为 0-3,0,1
# 当有新的容器加入到共享中,就会重新计算各容器的权重
-c, --cpu-shares:CPU共享权值(相对权重),默认值 1024
# 优于CPU和内存之间的调度过程
--cpuset-mems    :允许在上执行的内存节点(MEMs)
--cpu-period=0    :即可设置调度周期,CFS周期的有效范围是1ms ~ 1s,对应的 --cpu-period 的数值范围是1000 ~ 1000000
--cpu-quota=0    :设置在每个周期内容器能使用的 CPU 时间,容器的 CPU 配额必须不小于 1ms,即 --cpu-quota 的

Example:
## 公式: cpu-quota/cpu-period = 容器能够使用的CPU的利用率

# --cpu-period=50000 --> 设置一个调度周期为 50000 
# --cpu-quota=25000 --> 设置一个周期内能够使用的CPU时间为 25000
# 也就意味着我们能够使用的是CPU的50%
docker run -it -d --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash
# --cpu-period=10000 --> 设置一个调度周期为 10000
# --cpu-quota=20000 --> 设置一个周期内能够使用的CPU时间为 20000
# 也就意味着容器需要有两颗CPU,在同一个时间片,可以使用100%的两颗CPU
docker run -it -d --cpu-period=10000 --cpu-quota=20000 ubuntu:16.04 /bin/bash

NUMA

  • NUMA 非统一内存访问(NUMA)是一种用于多处理器的电脑记忆体设计,内存访问时间取决于处理器的内存位置。在 NUMA 下,处理器访问它自己的本地存储器的速度比非本地存储器(存储器的地方到另一个处理器之间共享的处理器或者存储器)快一些

扩展:

# CPU中的一级缓存,二级缓存,三级缓存都不是一个完整的片,而是在不同的单CPU下会对应绑定
# 公平调度策略
在Linux操作系统中下各个进程之间是公平调度,也就是意味着尽可能的保障每个进程之间能够使用的CPU资源是相对平衡的。

CPU 限制方式 - 2

# 需要根据CPU的调度算法来决定容器使用的CPU位置
# (如4核CPU,如果使用1.5个,则有可能是其中的三个CPU贡献50%的资源) 但是最终的结果一定是1.5(150%)
--cpus:能够限制容器可以使用的主机 CPU 个数,并且还可以指定如1.5之类的小数

5.3 限制实验

# 创建一个名称为stress的容器,并且容器一旦退出就删除容器实例
# -m 256 设置硬限制内存大小的256MB
# stress -vm 2 容器中的命令,-vm 2 代表用2个进程来消耗内存
$ docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress -vm 2
$ docker stats stress
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O        PIDS
ca94c7d7a46e   stress    40.40%    256MiB / 256MiB     100.00%   656B / 0B   407MB / 9.51GB   5

# 创建一个名称为stress的容器,并且容器一旦退出就删除容器实例
# --cpus 2 设置容器使用的CPU数量,为2核CPU
# stress --cpu 8 容器中的命令,--cpu 8 代表使用8颗CPU还跑
$ docker run --name stress -it --rm --cpus 2 lorel/docker-stress-ng:latest stress --cpu 8
# 因为CPU是一个动态调度的过程。结果在 200% 的范围之间波动
$ docker stats stress

# 创建一个名称为stress的容器,并且容器一旦退出就删除容器实例
# --cpuset-cpus 0 设置容器能够使用的CPU位置,0就代表第一颗CPU
# stress --cpu 8 容器中的命令,--cpu 8 代表使用8颗CPU还跑
$ docker run --name stress -it --rm --cpuset-cpus 0 lorel/docker-stress-ng:latest stress --cpu 8
# 结果为CPU使用率在 100% 范围波动 
$ docker stats stress

6 总结

6.1 远程访问

# 修改 /etc/docker/daemon.json 文件
# 代表启动的套接字访问,或者TCP连接访问
"hosts": ["tcp://0.0.0.0:2375", "unix://var/run/docker.sock"]

# example:
docker -H IP:PORT COMMADND

## 该远程访问是属于无认证机制的,不建议使用

6.2 OCI

Open Container Initiative

  • 由 Linux 基金会主导于 2015 年 6 月创立
  • 意在围绕容器各式和运行时定制一个开放的工业化标准

Contains two specifications

  • the Runtime Specification(runtime-spec)
  • the Image Specification(runtime-spec)

6.3 Runc

是对于 OCI 标准的一个参考实现,是一个可以用于创建和运行容器的 CLI(Command-Line Interface)工具。runC 直接与容器所依赖的 CGroup / Linux Kernel 等进行交互,负责为容器配置 CGroup / NameSpace 等启动容器所需的环境,创建启动容器的相关进程。为了兼容 OCI 标准,Docker 也做了架构调整。将容器运行时相关的程序从 Docker daemon 剥离出来,形成了 Containerd。Containerd 向 Docker 提供运行容器的 API,二者通过 GRPC 进行交互。Containerd 最后会通过 RunC 来实际运行容器。

  • RunC 实现架构

00.尚硅谷Linux虚拟化教程(ESXI和Docker) - 图45

6.4 Docker 命令图

Docker命令.png