小丑的个人博客

记录精彩的学习过程

  menu
17 文章
0 浏览
ღゝ◡╹)ノ❤️

docker基础

一:简介

前言

统称来说,容器是一种工具,指的是可以装下其它物品的工具,以方便人类归 纳放置物品、存储和异地运输,具体来说比如人类使用的衣柜、行李箱、背包等 可以成为容器,但今天我们所说的容器是一种 IT 技术。 容器技术是虚拟化、云计算、大数据之后的一门新兴的并且是炙手可热的新技 术,容器技术提高了硬件资源利用率、方便了企业的业务快速横向扩容、实现了 业务宕机自愈功能,因此未来数年会是一个容器愈发流行的时代,这是一个对于 IT 行业来说非常有影响和价值的技术,而对于 IT 行业的从业者来说,熟练掌握 容器技术无疑是一个很有前景的行业工作机会。 容器技术最早出现在 freebsd 叫做 jail。

1.1 docker 简介

1.1.1 Docker 是什么

首先 Docker 是一个在 2013年开源的应用程序并且是一个基于 go 语言编写是 一个开源的 PAAS 服务(Platform as a Service,平台即服务的缩写),go 语言是 由 google 开发,docker 公司最早叫 dotCloud 后由于 Docker 开源后大受欢迎 就将公司改名为 Docker Inc,总部位于美国加州的旧金山,Docker 是基于 linux 内核实现,Docker 最早采用 LXC 技术(LinuX Container 的简写,LXC 是 Linux 原 生支持的容器技术,可以提供轻量级的虚拟化,可以说 docker 就是基于 LXC 发展起来的(0.1.5 (2013-04-17),提供 LXC 的高级封装,发展标准的配置方法), 而虚拟化技术 KVM(Kernel-based Virtual Machine) 基于模块实现,Docker 后改 为自己研发并开源的 runc 技术运行容器(1.11.0 (2016-04-13)。 Docker now relies on containerd and runc to spawn containers。

Docker 相比虚拟机的交付速度更快,资源消耗更低,Docker 采用客户端/服 务端架构,使用远程 API 来管理和创建 Docker 容器,其可以轻松的创建一个轻 量级的、可移植的、自给自足的容器,docker 的三大理念是 build(构建)、ship(运 输)、 run(运行),Docker 遵从 apache 2.0 协议,并通过(namespace 及 cgroup 等)来提供容器的资源隔离与安全保障等,所以 Docke 容器在运行时不需要类 似虚拟机(空运行的虚拟机占用物理机的一定性能开销)的额外资源开销,因此 可以大幅提高资源利用率,总而言之 Docker 是一种用了新颖方式实现的轻量级 虚拟机.类似于 VM 但是在原理和应用上和 VM 的差别还是很大的,并且 docker 的专业叫法是应用容器(Application Container)。

1.1.2 Docker 的组成

Docker 主机(Host):一个物理机或虚拟机,用于运行 Docker 服务进程和容器。

Docker 服务端(Server):Docker 守护进程,运行 docker 容器。

Docker 客户端(Client):客户端使用 docker 命令或其他工具调用 docker API。

Docker 仓库(Registry): 保存镜像的仓库,类似于 git 或 svn 这样的版本控制系

Docker 镜像(Images):镜像可以理解为创建实例使用的模板。

Docker 容器(Container): 容器是从镜像生成对外提供服务的一个或一组服务。

官方仓库: https://hub.docker.com/

image-20211224093257877

码头建筑图

1.1.3 Docker 对比虚拟机

资源利用率更高:一台物理机可以运行数百个容器,但是一般只能运行数十个虚 拟机。

开销更小:不需要启动单独的虚拟机占用硬件资源。

启动速度更快:可以在数秒内完成启动。

image-20211224093355807

使用虚拟机是为了更好的实现服务运行环境隔离,每个虚拟机都有独立的内核, 虚拟化可以实现不同操作系统的虚拟机,但是通常一个虚拟机只运行一个服务, 很明显资源利用率比较低且造成不必要的性能损耗,我们创建虚拟机的目的是为 了运行应用程序,比如 Nginx、PHP、Tomcat 等 web 程序,使用虚拟机无疑带 来了一些不必要的资源开销,但是容器技术则基于减少中间运行环节带来较大的 性能提升。

image-20211224093435719

但是,一个宿主机运行了 N 个容器,多个容器带来的以下问题怎么解决:

1.怎么样保证每个容器都有不同的文件系统并且能互不影响?

2.一个 docker 主进程内的各个容器都是其子进程,那么实现同一个主进程下不 同类型的子进程?各个进程间通信能相互访问(内存数据)吗?

3.每个容器怎么解决 IP 及端口分配的问题?

4.多个容器的主机名能一样吗?

5.每个容器都要不要有 root 用户?怎么解决账户重名问题? 以上问题怎么解决?

1.1.4 Linux Namespace 技术

namespace 是 Linux 系统的底层概念,在内核层实现,即有一些不同类型的命 名空间被部署在核内,各个 docker 容器运行在同一个 docker 主进程并且共用 同一个宿主机系统内核,各 docker 容器运行在宿主机的用户空间,每个容器都 要有类似于虚拟机一样的相互隔离的运行空间,但是容器技术是在一个进程内实 现运行指定服务的运行环境,并且还可以保护宿主机内核不受其他进程的干扰和 影响,如文件系统空间、网络空间、进程空间等,目前主要通过以下技术实现容 器运行空间的相互隔离:

隔离类型功能系统调用参数内核版本
MNT Namespace(mount)提供磁盘挂载点和文件系统的隔离能力CLONE_NEWNSLinux 2.4.19
IPC Namespace(Inter-Process Communicatio)提供进程间通信的隔离能力CLONE_NEWIPCLinux 2.6.19
UTS Namespace(UNIX Timesharing System)提供主机名隔离能力CLONE_NEWUTSLinux 2.6.19
PID Namespace(Process Identification)提供进程隔离能力CLONE_NEWPIDLinux 2.6.24
Net Namespace(network)提供网络隔离能力CLONE_NEWNETLinux 2.6.29
User Namespace(user)提供用户隔离能力CLONE_NEWUSERLinux 3.8

1.1.4.1 MNT Namespace

每个容器都要有独立的根文件系统有独立的用户空间,以实现在容器里面启动服 务并且使用容器的运行环境,即一个宿主机是 ubuntu 的服务器,可以在里面启 动一个 centos 运行环境的容器并且在容器里面启动一个 Nginx 服务,此 Nginx 运行时使用的运行环境就是 centos 系统目录的运行环境,但是在容器里面是不 能访问宿主机的资源,宿主机是使用了 chroot 技术把容器锁定到一个指定的运 行目录里面。

例如:/var/lib/containerd/io.containerd.runtime.v1.linux/moby/容器ID

启动三个容器用于以下验证过程:

root@docker-01:~# docker version
Client: Docker Engine - Community
 Version:           20.10.10
 API version:       1.41
 Go version:        go1.16.9
 Git commit:        b485636
 Built:             Mon Oct 25 07:42:59 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true
 
root@docker-01:~# docker run -d --name nginx-1 -p 80:80 nginx
root@docker-01:~# docker run -d --name nginx-2 -p 81:80 nginx
root@docker-01:~# docker run -d --name nginx-3 -p 82:80 nginx

Debian 系统安装基础命令

apt update
apt install procps (top命令)
apt install iputils-ping (ping命令)
apt install net-tools (网络工具)

验证容器的根文件系统

root@ba84f11be6f4:/# ls
bin  boot  dev	docker-entrypoint.d  docker-entrypoint.sh  etc	home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

1.1.4.2 IPC Namespace

一个容器内的进程间通信,允许一个容器内的不同进程的(内存、缓存等)数据访 问,但是不能夸容器访问其他容器的数据。

1.1.4.3 UTS Namespace

UTS namespace(UNIX Timesharing System 包含了运行内核的名称、版本、 底层体系结构类型等信息)用于系统标识,其中包含了 hostname 和域名 domainname ,它使得一个容器拥有属于自己 hostname 标识,这个主机名标 识独立于宿主机系统和其上的其他容器。

root@docker-01:~# cat /etc/issue
Ubuntu 20.04.3 LTS \n \l

root@docker-01:~# uname -a
Linux docker-01 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021  x86_64 GNU/Linux

root@docker-01:~# docker exec -it a5bf97f3d79c bash
root@a5bf97f3d79c:/# cat /etc/issue
Debian GNU/Linux 11 \n \l

root@a5bf97f3d79c:/# uname -a
Linux a5bf97f3d79c 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 x86_64 GNU/Linux


1.1.4.4 PID Namespace

Linux 系统中,有一个 PID 为 1 的进程(init/systemd)是其他所有进程的父进程,那么在每个容器内 也要有一个父进程来管理其下属的子进程,那么多个容器的进程通 PID namespace 进程隔离(比如 PID 编号重复、器内的主进程生成与回收子进程等)。

例如:下图是在一个容器内使用 top 命令看到的 PID 为 1 的进程是 nginx::

image-20211118133855521

容器内的 Nginx 主进程与工作进程

image-20211118133952186

那么宿主机的 PID 究竟与容器内的 PID 是什么关系?

容器 PID 追踪

1.1.4.4.1 查看宿主机上的 PID 信息

image-20211118134609874

1.1.4.5 Net Namespace

每一个容器都类似于虚拟机一样有自己的网卡、监听端口、TCP/IP 协议栈等, Docker 使用 network namespace 启动一个 vethX 接口,这样你的容器将拥有它 自己的桥接 ip 地址,通常是 docker0,而 docker0 实质就是 Linux 的虚拟网桥, 网桥是在 OSI 七层模型的数据链路层的网络设备,通过 mac 地址对网络进行划分,并且在不同网络直接传递数据。

1.1.4.5.1 查看宿主机的网卡信息

image-20211118135304654

1.1.4.5.2 查看宿主机桥接设备

通过 brctl show 命令查看桥接设备:

image-20211118135441181

image-20211118135558350

1.1.4.5.3 实逻辑网络图

image-20211118135638647

1.1.4.5.4 宿主机 iptables 规则

image-20211118140251859

image-20211118140929171

1.1.4.6 User Namespace

各个容器内可能会出现重名的用户和用户组名称,或重复的用户 UID 或者 GID, 那么怎么隔离各个容器内的用户空间呢?

User Namespace 允许在各个宿主机的各个容器空间内创建相同的用户名以及 相同的用户 UID 和 GID,只是会把用户的作用范围限制在每个容器内,即 A 容 器和 B 容器可以有相同的用户名称和 ID 的账户,但是此用户的有效范围仅是当 前容器内,不能访问另外一个容器内的文件系统,即相互隔离、互补影响、永不 相见。

root@docker-01:~# docker run  -it --rm nginx bash
root@a0d3417875d3:/# id
uid=0(root) gid=0(root) groups=0(root)
root@a0d3417875d3:/# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

# 每个容器内都有超级管理员root和其他系统用户,且账户id和其他容器相同

1.1.5 Linux control groups

在一个容器,如果不对其做任何资源限制,则宿主机会允许其占用无限大的内存 空间,有时候会因为代码 bug 程序会一直申请内存,直到把宿主机内存占完, 为了避免此类的问题出现,宿主机有必要对容器进行资源分配限制,比如 CPU、 内存等,Linux Cgroups 的全称是 Linux Control Groups,它最主要的作用,就 是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等 等。此外,还能够对进程进行优先级设置,以及将进程挂起和恢复等操作。

1.1.5.1 验证系统 cgroups

Cgroups 在内核层默认已经开启,从 centos 和 ubuntu 对比结果来看,显然内 核较新的 ubuntu 支持的功能更多

1.1.5.1.1 Centos 7.9 cgroups

image-20211118143929885

1.1.5.1.2 ubuntu cgroups

image-20211118144049399

1.1.5.1.3 cgroups 中内存模块
root@docker02:~# grep MEM /boot/config-5.4.0-90-generic  |grep CG
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
# CONFIG_MEMCG_SWAP_ENABLED is not set
CONFIG_MEMCG_KMEM=y
CONFIG_SLUB_MEMCG_SYSFS_ON=y

1.1.5.1.4 cgroups 具体实现
blkio:块设备 IO 限制。
cpu:使用调度程序为 cgroup 任务提供 cpu 的访问。
cpuacct:产生 cgroup 任务的 cpu 资源报告。
cpuset:如果是多核心的 cpu,这个子系统会为 cgroup 任务分配单独的 cpu和内存。
devices:允许或拒绝 cgroup 任务对设备的访问。
freezer:暂停和恢复 cgroup 任务。
memory:设置每个 cgroup 的内存限制以及产生内存资源报告。
net_cls:标记每个网络包以供 cgroup 方便使用。
ns:命名空间子系统。
perf_event:增加了对每 group 的监测跟踪的能力,可以监测属于某个特定的
group 的所有线程以及运行在特定 CPU 上的线程
1.1.5.1.5 查看系统 cgroups
root@docker02:~# ll /sys/fs/cgroup/
total 0
drwxr-xr-x 15 root root 380 Nov 18 02:31 ./
drwxr-xr-x  9 root root   0 Nov 18 02:31 ../
dr-xr-xr-x  5 root root   0 Nov 18 02:31 blkio/
lrwxrwxrwx  1 root root  11 Nov 18 02:31 cpu -> cpu,cpuacct/
lrwxrwxrwx  1 root root  11 Nov 18 02:31 cpuacct -> cpu,cpuacct/
dr-xr-xr-x  5 root root   0 Nov 18 02:31 cpu,cpuacct/
dr-xr-xr-x  2 root root   0 Nov 18 02:31 cpuset/
dr-xr-xr-x  5 root root   0 Nov 18 02:31 devices/
dr-xr-xr-x  3 root root   0 Nov 18 02:31 freezer/
dr-xr-xr-x  2 root root   0 Nov 18 02:31 hugetlb/
dr-xr-xr-x  5 root root   0 Nov 18 02:31 memory/
lrwxrwxrwx  1 root root  16 Nov 18 02:31 net_cls -> net_cls,net_prio/
dr-xr-xr-x  2 root root   0 Nov 18 02:31 net_cls,net_prio/
lrwxrwxrwx  1 root root  16 Nov 18 02:31 net_prio -> net_cls,net_prio/
dr-xr-xr-x  2 root root   0 Nov 18 02:31 perf_event/
dr-xr-xr-x  5 root root   0 Nov 18 02:31 pids/
dr-xr-xr-x  2 root root   0 Nov 18 02:31 rdma/
dr-xr-xr-x  5 root root   0 Nov 18 02:31 systemd/
dr-xr-xr-x  5 root root   0 Nov 18 02:31 unified/

有了以上的 chroot、namespace、cgroups 就具备了基础的容器运行环境,但是还需要有相应的容器创建与删除的管理工具、以及怎么样把容器运行起来、容 器数据怎么处理、怎么进行启动与关闭等问题需要解决,于是容器管理技术出现 了。

1.1.6 容器管理工具

目前主要是使用 docker,早期有使用 lxc。

1.1.6.1 lxc

LXC:LXC 为Linux Container 的简写。可以提供轻量级的虚拟化,以便隔离进
程和资源,官方网站:https://linuxcontainers.org/
Ubuntu 安装lxc:

root@s1:~# apt install lxc lxd
Reading package lists... Done
Building dependency tree
Reading state information... Done
lxd is already the newest version (3.0.3-0ubuntu1~18.04.1).
lxc is already the newest version (3.0.3-0ubuntu1~18.04.1).
root@s1:~# lxc-checkconfig #检查内核对lcx 的支持状况,必须全部为lcx root@s1:~# lxc-create -t 模板名称-n lcx-test
root@s1:~# lxc-create -t download --name alpine12 -- --dist alpine
--release 3.9 --arch amd64
Setting up the GPG keyring
Downloading the image index
Downloading the rootfs
Downloading the metadata
The image cache is now ready
Unpacking the rootfs
---
You just created an Alpinelinux 3.9 x86_64 (20190630_13:00) container.
root@s1:~# lxc-start alpine12 #启动lxc 容器
root@s1:~# lxc-attach alpine12 #进入lxc 容器
~ # ifconfig

~ # uname -a
Linux alpine12 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64 Linux
~ # cat /etc/issue
Welcome to Alpine Linux 3.9
Kernel \r on an \m (\l)

命令备注:
-t 模板: -t 选项后面跟的是模板,模式可以认为是一个原型,用来说明我们需要一个什么样的容器(比如容器里面需不需要有vim, apache 等软件).模板实际上就是一个脚本文件(位于/usr/share/lxc/templates 目录),我们这里指定download模板(lxc-create 会调用lxc-download 脚本,该脚本位于刚说的模板目录中)是说明我们目前没有自己模板,需要下载官方的模板
--name 容器名称:为创建的容器命名
-- : --用来说明后面的参数是传递给download 脚本的,告诉脚本需要下载什么样的模板
--dist 操作系统名称:指定操作系统
--release 操作系统: 指定操作系统,可以是各种Linux 的变种
--arch 架构:指定架构,是x86 还是arm,是32 位还是64 位


lxc 启动容器依赖于模板,清华模板源:
https://mirrors.tuna.tsinghua.edu.cn/help/lxc-images/,但是做模板相对较难,需要手动一步步创构建文件系统、准备基础目录及可执行程序等,而且在大规模使用容器的场景很难横向扩展,另外后期代码升级也需要重新从头构建模板,基于以上种种原因便有了docker。

1.1.6.2 docker

Docker 启动一个容器也需要一个外部模板但是较多镜像,docker 的镜像可以保 存在一个公共的地方共享使用,只要把镜像下载下来就可以使用,最主要的是可 以在镜像基础之上做自定义配置并且可以再把其提交为一个镜像,一个镜像可以 被启动为多个容器。 Docker 的镜像是分层的,镜像底层为库文件且只读层即不能写入也不能删除数 据,从镜像加载启动为一个容器后会生成一个可写层,其写入的数据会复制到容 器目录,但是容器内的数据在删除容器后也会被随之删除。

image-20211224094413110

1.1.6.3 pouch

https://www.infoq.cn/article/alibaba-pouch

https://github.com/alibaba/pouch

1.1.6 Docker 的优势

快速部署:短时间内可以部署成百上千个应用,更快速交付到线上。

高效虚拟化:不需要额外的 hypervisor 支持,直接基于 linux 实现应用虚拟化, 相比虚拟机大幅提高性能和效率。

节省开支:提高服务器利用率,降低 IT 支出。

简化配置:将运行环境打包保存至容器,使用时直接启动即可。

快速迁移和扩展:可夸平台运行在物理机、虚拟机、公有云等环境,良好的兼容 性可以方便将应用从 A 宿主机迁移到 B 宿主机,甚至是 A 平台迁移到 B 平台。

1.1.7 Docker 的缺点

隔离性:各应用之间的隔离不如虚拟机彻底

1.1.8 docker(容器)的核心技术

1.1.8.1 容器规范

容器技术除了的 docker 之外,还有 coreOS 的 rkt,还有阿里的 Pouch,为了 保证容器生态的标准性和健康可持续发展,包括 Linux 基金会、Docker、微软、 红帽谷歌和、IBM、等公司在 2015 年 6 月共同成立了一个叫 open container (OCI)的组织,其目的就是制定开放的标准的容器规范,目前 OCI 一共发布了 两个规范,分别是 runtime spec 和 image format spec,有了这两个规范,不 同的容器公司开发的容器只要兼容这两个规范,就可以保证容器的可移植性和相 互可操作性。

1.1.8.1.1 容器 runtime(runtime spec)

runtime 是真正运行容器的地方,因此为了运行不同的容器 runtime 需要和操 作系统内核紧密合作相互在支持,以便为容器提供相应的运行环境。

目前主流的三种 runtime:

Lxc:linux 上早期的 runtime,Docker 早期就是采用 lxc 作为 runtime。

runc:目前 Docker 默认的 runtime,runc 遵守 OCI 规范,因此可以兼容 rkt。

rkt:是 CoreOS 开发的容器 runtime,也符合 OCI 规范,所以使用 rktruntime 也可以运行 Docker 容器。

runtime 主 要 定 义 了 以 下 规 范 , 并 以 json 格 式 保 存 在 /run/docker/runtime-runc/moby/容器 ID/state.json 文件,此文件会根据容器的状态 实时更新内容:

版本信息:存放 OCI 标准的具体版本号。

容器 ID:通常是一个哈希值,可以在所有 state.json 文件中提取出容器 ID 对容器进行批 量操作(关闭、删除等),此文件在容器关闭后会被删除,容器启动后会自动生成。

PID:在容器中运行的首个进程在宿主机上的进程号,即将宿主机的那个进程设置为容 器的守护进程。

容器文件目录:存放容器 rootfs 及相应配置的目录,外部程序只需读取 state.json 就可 以定位到宿主机上的容器文件目录。

容器创建:创建包括文件系统、namespaces、cgroups、用户权限在内的各项内容。

容器进程的启动:运行容器启动进程,该文件在 /run/containerd/io.containerd.runtime.v1.linux/moby/容器 ID/config.json。

容器生命周期:容器进程可以被外部程序关停,runtime 规范定义了对容器操作信号的 捕获,并做相应资源回收的处理,避免僵尸进程的出现。

1.1.8.1.2:容器镜像(image format spec)

OCI 容器镜像主要包含以下内容

文件系统:定义以 layer 保存的文件系统,在镜像里面是 layer.tar,每个 layer 保存了 和上层之间变化的部分,image format spec 定义了 layer 应该保存哪些文件,怎么 表示增加、修改和删除的文件等操作。

manifest 文件:描述有哪些 layer,tag 标签及 config 文件名称。
config 文件:是一个以 hash 命名的 json 文件,保存了镜像平台,容器运行时容 器运行时需要的一些信息,比如环境变量、工作目录、命令参数等。
index 文件:可选的文件,指向不同平台的 manifest 文件,这个文件能保证一个镜 像可以跨平台使用,每个平台拥有不同的 manifest 文件使用 index 作为索引。

父镜像:大多数层的元信息结构都包含一个 parent 字段,指向该镜像的父镜像。

参数:
ID:镜像 ID,每一层都有 ID
tag 标签:标签用于将用户指定的、具有描述性的名称对应到镜像 ID
仓库:Repository 镜像仓库
os:定义类型
architecture :定义 CPU 架构
author:作者信息
create:镜像创建日期

1.1.8.2:容器管理工具

管理工具连接 runtime 与用户,对用户提供图形或命令方式操作,然后管理工 具将用户操作传递给 runtime 执行。

lxc 是 lxd 的管理工具。

Runc 的管理工具是 docker engine,docker engine 包含后台 deamon 和 cli 两部分,大家经常提到的 Docker 就是指的 docker engine。

Rkt 的管理工具是 rkt cli。

1.1.8.3:容器定义工具

容器定义工具允许用户定义容器的属性和内容,以方便容器能够被保存、共享 和重建。

Docker image:是 docker 容器的模板,runtime 依据 docker image 创建容器。

Dockerfile:包含 N 个命令的文本文件,通过 dockerfile 创建出 docker image。

ACI(App container image):与 docker image 类似,是 CoreOS 开发的 rkt 容器 的镜像格式

1.1.8.4:Registry

统一保存镜像而且是多个不同镜像版本的地方,叫做镜像仓库。

Image registry:docker 官方提供的私有仓库部署工具。

Docker hub:docker 官方的公共仓库,已经保存了大量的常用镜像,可以方便 大家直接使用。

Harbor:vmware 提供的自带 web 界面自带认证功能的镜像仓库,目前有很多 公司使用。

1.1.8.5:编排工具

当多个容器在多个主机运行的时候,单独管理容器是相当复杂而且很容易出 错,而且也无法实现某一台主机宕机后容器自动迁移到其他主机从而实现高可用的目的,也无法实现动态伸缩的功能,因此需要有一种工具可以实现统一管理、 动态伸缩、故障自愈、批量执行等功能,这就是容器编排引擎。

容器编排通常包括容器管理、调度、集群定义和服务发现等功能。

Docker swarm:docker 开发的容器编排引擎。

Kubernetes:google 领导开发的容器编排引擎,内部项目为 Borg,且其同时支 持 docker 和 CoreOS。

Mesos+Marathon:通用的集群组员调度平台,mesos(资源分配)与 marathon(容 器编排平台)一起提供容器编排引擎功能。

image-20211224094824754

1.1.9:docker(容器)的依赖技术

1.1.9.1:容器网络

docker 自带的网络 docker network 仅支持管理单机上的容器网络,当多主机 运行的时候需要使用第三方开源网络,例如 calico、flannel 等。

1.1.9.2:服务发现

容器的动态扩容特性决定了容器 IP 也会随之变化,因此需要有一种机制可以 自动识别并将用户请求动态转发到新创建的容器上,kubernetes 自带服务发现 功能,需要结合 kube-dns 服务解析内部域名。

1.1.9.3:容器监控

可以通过原生命令 docker ps/top/stats 查看容器运行状态,另外也可以使 heapster/ Prometheus 等第三方监控工具监控容器的运行状态。

1.1.9.4:数据管理

容器的动态迁移会导致其在不同的 Host 之间迁移,因此如何保证与容器相关 的数据也能随之迁移或随时访问,可以使用逻辑卷/存储挂载等方式解决。

1.1.9.5:日志收集

docker 原生的日志查看工具 docker logs,但是容器内部的日志需要通过 ELK 等专门的日志收集分析和展示工具进行处理。

1.2:Docker 安装及基础命令介绍

官方网址:https://www.docker.com/

系统版本选择:

Docker 目前已经支持多种操作系统的安装运行,比如 Ubuntu、CentOS、 Redhat、Debian、Fedora,甚至是还支持了 Mac 和 Windows,在 linux 系统上 需要内核版本在 3.10 或以上,docker 版本号之前一直是 0.X 版本或 1.X 版本, 但是从 2017 年 3 月 1 号开始改为每个季度发布一次稳版,其版本号规则也统一 变更为 YY.MM,例如 17.09 表示是 2017 年 9 月份发布的,本次演示的操作系 统使用 Centos 7.9 为例。

Docker 版本选择: Docker 之前没有区分版本,但是 2017 年初推出(将 docker 更名为)新的项 目 Moby,github 地址:https://github.com/moby/moby,Moby 项目属于 Docker 项目的全新上游,Docker 将是一个隶属于的 Moby 的子产品,而且之后的版本 之后开始区分为 CE 版本(社区版本)和 EE(企业收费版),CE 社区版本和 EE 企业版本都是每个季度发布一个新版本,但是 EE 版本提供后期安全维护 1 年,而 CE 版本是 4 个月,本次演示的 Docker 版本为 18.03,以下为官方原文: https://blog.docker.com/2017/03/docker-enterprise-edition/

Docker CE and EE are released quarterly, and CE also has a monthly “Edge” option. Each Docker EE release is supported and maintained for one year and receives security and critical bugfixes during that period. We are also improving Docker CE maintainability by maintaining each quarterly CE release for 4 months. That gets Docker CE users a new 1-month window to update from one version to the next

Docker Enterprise Edition

与 kubernetes 结合使用的时候,要安装经过 kubernetes 官方测试通过的 docker 版本,避免出现不兼容等未知的及不可预估的问题发生,kubernetes 测试过的 docker 版本可以在 github 查询,具体如下:

https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.14.md#external-dependencies

image-20211224095352306

1.2.1:下载 rpm 包安装

官方 rpm 包下载地址:

https://download.docker.com/linux/centos/7/x86_64/stable/Packages

二进制下载地址:

https://download.docker.com/

https://mirrors.aliyun.com/docker-ce/linux/static/stable/x86_64/

阿里镜像下载地址:

https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages

1.2:2:通过修改 yum 源安装

centos

[root@docker-server1 ~]# rm -rf /etc/yum.repos.d/*
[root@docker-server1 ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
[root@docker-server1 ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@docker-server1 ~]# wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@docker-server1 ~]# yum install docker-ce

ubuntu

root@docker-server1:~#apt-get install docker-ce=5:18.09.9~3-0~ubuntu-bionic docker-ce-cli=5:18.09.9~3-0~ubuntu-bionic
root@docker-server1:~# systemctl start docker root@docker-server1:~# systemctl enable docker

1.2.2.1:centos7 安装

注意:一下安装来自 清华大学开源镜像站

  1. 如果你之前安装过 docker,请先删掉

    sudo yum remove docker docker-common docker-selinux docker-engine
    
  2. 安装一些依赖

    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
    
  3. 下载repo文件

    wget -O /etc/yum.repos.d/docker-ce.repo \
     https://download.docker.com/linux/centos/docker-ce.repo
    
  4. 把软件仓库地址替换为 TUNA

    sudo sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' \
     /etc/yum.repos.d/docker-ce.repo
    
  5. 安装

sudo yum makecache fast
sudo yum install docker-ce

1.2.2.2:Debian/ubuntu 安装

注意:一下安装方法来自 清华大学开源镜像站

  1. 如果你过去安装过 docker,先删掉

    sudo apt-get remove docker docker-engine docker.io
    
  2. 首先安装依赖:

    sudo apt-get install apt-transport-https ca-certificates \
         curl gnupg2 software-properties-common
    
  3. 发行版不同下面的内容有所不同

    3.1 ubuntu 安装

    3.1.1 信任 Docker 的 GPG 公钥:
    
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    
    3.1.2 对于 amd64 架构的计算机,添加软件仓库:
    
    sudo add-apt-repository \
       "deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
       $(lsb_release -cs) \
       stable"
    
    3.1.3 安装
    
    sudo apt-get update
    sudo apt-get install docker-ce
    

    3.2 Debian 安装

    3.2.1 信任 Docker 的 GPG 公钥:
    
    curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
    
    3.2.2 对于 amd64 架构的计算机,添加软件仓库:
    
    sudo add-apt-repository \
       "deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/debian \
       $(lsb_release -cs) \
       stable"
    
    3.2.3 安装
    
    sudo apt-get update
    sudo apt-get install docker-ce
    

1.2.3:验证docker0 网卡:

在docker 安装启动之后,默认会生成一个名称为docker0 的网卡并且默认IP地址为172.17.0.1 的网卡。

image-20211225113237634

1.2.4:二进制安装docker

1.2.4.1:下载

wget https://mirrors.aliyun.com/docker-ce/linux/static/stable/x86_64/docker-20.10.9.tgz

1.2.4.2:解压

tar xf docker-20.10.9.tgz
cp docker/* /usr/bin/

1.2.4.3:containerd

cat > /lib/systemd/system/containerd.service  << EOF
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/bin/containerd

Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=1048576
# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
#OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target
EOF

1.2.4.4:docker.service

cat > /lib/systemd/system/docker.service  << EOF
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service containerd.service
Wants=network-online.target
Requires=docker.socket containerd.service

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not support it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process
OOMScoreAdjust=-500

[Install]
WantedBy=multi-user.target
EOF

1.2.4.5:docker.socket

cat > /lib/systemd/system/docker.socket  <<EOF
[Unit]
Description=Docker Socket for the API

[Socket]
ListenStream=/var/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
EOF
groupadd docker
# 必须创建docker组

systemctl enable containerd && systemctl start containerd
systemctl enable docker && systemctl start docker
systemctl enable docker.socket && systemctl start docker.socket

docker1.19以后的版本想在docker里面读取nvidia显卡,不再需要额外安装nvidia-docker了,但是需要安装NVIDIA Container Runtime

安装NVIDIA Container Runtime

$ cat >> nvidia-container-runtime-script.sh <'EOF'
 
curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | \
  sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list
sudo apt-get update
EOF
执行脚本
bash nvidia-container-runtime-script.sh
安装
apt  install nvidia-container-runtime -y

验证

which nvidia-container-runtime-hook

1.2.5:配置镜像加速器

注意: 此配置只针对Docker客户端版本大于1.10.0的用户

国内下载国外的镜像有时候会很慢,因此可以更改 docker 配置文件添加一个加 速器,可以通过加速器达到加速下载镜像的目的。

1.2.5.1:获取加速地址

浏览器打开 http://cr.console.aliyun.com,注册或登录阿里云账号,点击左侧的 镜像加速器,将会得到一个专属的加速地址,而且下面有使用配置说明:

image-20211118162914714

1.2.5.2:创建配置文件目录

mkdir /etc/docker/

1.2.5.3:添加配置文件

通过修改daemon配置文件/etc/docker/daemon.json来使用加速器

cat >/etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver":"json-file",
  "log-opts":{
    "max-size":"100m"
  },
  "storage-driver":"overlay2",
  "registry-mirrors": ["https://yy4l17b0.mirror.aliyuncs.com","https://docker.mirrors.ustc.edu.cn","https://registry.docker-cn.com"]
}
EOF

1.2.5.4:daemon.json 所有设置

{
  "allow-nondistributable-artifacts": [],
  "api-cors-header": "",
  "authorization-plugins": [],
  "bip": "",
  "bridge": "",
  "cgroup-parent": "",
  "cluster-advertise": "",
  "cluster-store": "",
  "cluster-store-opts": {},
  "containerd": "/run/containerd/containerd.sock",
  "containerd-namespace": "docker",
  "containerd-plugin-namespace": "docker-plugins",
  "data-root": "",   # 修改docker数据目录
  "debug": true,
  "default-address-pools": [ # 修改docker的ip和子网
    {
      "base": "172.80.0.0/16",
      "size": 24
    },
    {
      "base": "172.90.0.0/16",
      "size": 24
    }
  ],
  "default-cgroupns-mode": "private",
  "default-gateway": "",
  "default-gateway-v6": "",
  "default-runtime": "runc",
  "default-shm-size": "64M",
  "default-ulimits": {
    "nofile": {
      "Hard": 64000,
      "Name": "nofile",
      "Soft": 64000
    }
  },
  "dns": [],
  "dns-opts": [],
  "dns-search": [],
  "exec-opts": [],
  "exec-root": "",
  "experimental": false,
  "features": {},
  "fixed-cidr": "",
  "fixed-cidr-v6": "",
  "group": "",
  "hosts": [],
  "icc": false,
  "init": false,
  "init-path": "/usr/libexec/docker-init",
  "insecure-registries": [], # 信任不安全的仓库
  "ip": "0.0.0.0",
  "ip-forward": false,
  "ip-masq": false,
  "iptables": false,
  "ip6tables": false,
  "ipv6": false,
  "labels": [],
  "live-restore": true,
  "log-driver": "json-file",
  "log-level": "",
  "log-opts": {
    "env": "os,customer",
    "labels": "somelabel",
    "max-file": "5",
    "max-size": "10m"
  },
  "max-concurrent-downloads": 3,
  "max-concurrent-uploads": 5,
  "max-download-attempts": 5,
  "mtu": 0,
  "no-new-privileges": false,
  "node-generic-resources": [
    "NVIDIA-GPU=UUID1",
    "NVIDIA-GPU=UUID2"
  ],
  "oom-score-adjust": -500,
  "pidfile": "",
  "raw-logs": false,
  "registry-mirrors": [],
  "runtimes": {
    "cc-runtime": {
      "path": "/usr/bin/cc-runtime"
    },
    "custom": {
      "path": "/usr/local/bin/my-runc-replacement",
      "runtimeArgs": [
        "--debug"
      ]
    }
  },
  "seccomp-profile": "",
  "selinux-enabled": false,
  "shutdown-timeout": 15,
  "storage-driver": "",
  "storage-opts": [],
  "swarm-default-advertise-addr": "",
  "tls": true,
  "tlscacert": "",
  "tlscert": "",
  "tlskey": "",
  "tlsverify": true,
  "userland-proxy": false,
  "userland-proxy-path": "/usr/libexec/docker-proxy",
  "userns-remap": ""
}

1.2.5.5:docker配置代理

非必要操作:

部分镜像,例如ingress。不配置科学上网,不能下载

# 创建docker服务插件目录
mkdir -p /etc/systemd/system/docker.service.d

# 创建一个名为http-proxy.conf的文件
vim /etc/systemd/system/docker.service.d/http-proxy.conf 
[Service]
Environment="HTTP_PROXY=http://代理ip:代理端口/" "HTTPS_PROXY=https://代理ip:代理端口/"
# 如果是sock5代理也可以设置
#Environment="HTTP_PROXY=socks5://代理ip:代理端口/" "HTTPS_PROXY=socks5://代理ip:代理端口/"
# 设置部分地址不代理
#Environment="HTTP_PROXY=http://代理ip:代理端口" "HTTPS_PROXY=http://代理ip:代理端口" "NO_PROXY=localhost,127.0.0.1,harbor.12345.com,yy4l17b0.mirror.aliyuncs.com"

1.2.5.6:加载配置文件和重启docker

sudo systemctl daemon-reload
sudo systemctl restart docker

1.2.6:验证 docker 信息

[root@localhost ~]# docker info
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Build with BuildKit (Docker Inc., v0.6.3-docker)
  scan: Docker Scan (Docker Inc., v0.9.0)

Server:
 Containers: 2   # 当前主机的容器总数
  Running: 2     # 有几个容器是运行的
  Paused: 0      # 有几个容器是暂停的
  Stopped: 0     # 有几个容器是停止的
 Images: 1       # 当前服务器的镜像数
 Server Version: 20.10.10      # 服务端版本
 Storage Driver: overlay2      # 正在使用的存储引擎
  Backing Filesystem: xfs      # 后端文件系统,即服务器磁盘文件系统
  Supports d_type: true        # 是否支持d_type
  Native Overlay Diff: true    # 是否支持差异数据存储
  userxattr: false
 Logging Driver: json-file     # 日志类型
 Cgroup Driver: systemd        # Cgroup 类型
 Cgroup Version: 1
 Plugins:           # 插件
  Volume: local     # 卷
  Network: bridge host ipvlan macvlan null overlay # overlay 夸主机通信
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog # 日志类型
 Swarm: inactive    # 是否支持swarm
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc   # 已安装的容器运行时
 Default Runtime: runc     # 默认使用的容器运行时
 Init Binary: docker-init  # 初始化容器的守护进程,即pid为1的进程
 containerd version: 7b11cfaabd73bb80907dd23182b9347b4245eb5d # 版本
 runc version: v1.0.2-0-g52b36a2    # runc版本
 init version: de40ad0  # init版本
 Security Options:      # 安全选项
  seccomp
   Profile: default     # 默认的配置文件
 Kernel Version: 3.10.0-1160.el7.x86_64   # 宿主机内核版本
 Operating System: CentOS Linux 7 (Core)  # 宿主机操作系统
 OSType: linux             # 宿主机操作系统类型
 Architecture: x86_64      # 宿主机架构
 CPUs: 4                   # 宿主机cpu数量
 Total Memory: 1.933GiB    # 宿主机总内存
 Name: localhost.localdomain  # 宿主机hostname
 ID: JNAX:EM4D:UYCD:UWAG:E5YT:NSDE:E2IU:NF3E:PLXL:AJEH:A4LR:GTKR  # 宿主机ID
 Docker Root Dir: /var/lib/docker # 宿主机数据保存目录
 Debug Mode: false                # 是否开启debug
 Registry: https://index.docker.io/v1/  # 镜像仓库
 Labels:                # 其他标签
 Experimental: false    # 是否测试版
 Insecure Registries:   # 非安全的镜像仓库
  127.0.0.0/8
 Registry Mirrors:
  https://yy4l17b0.mirror.aliyuncs.com/
  https://docker.mirrors.ustc.edu.cn/
  https://registry.docker-cn.com/
 Live Restore Enabled: false   # 是否开启活动重启(重启docker-daemon不关闭容器)

1.2.7:解决不支持 swap 限制警告

root@docker02:~# vim /etc/default/grub

GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="maybe-ubiquity"
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
# 添加cgroup_enable=memory swapaccount=1

# update-grub
# reboot

1.2.8:docker 存储引擎

目前 docker 的默认存储引擎为 overlay2,不同的存储引擎需要相应的系统支持, 如需要磁盘分区的时候传递 d-type 文件分层功能,即需要传递内核参数开启格 式化磁盘的时候的指定功能。

历史更新信息:

https://github.com/moby/moby/blob/master/CHANGELOG.md

官方文档关于存储引擎的选择文档:

https://docs.docker.com/storage/storagedriver/select-storage-driver/

存储驱动类型:

AUFS(Another UnionFS)是一种 Union FS,是文件级的存储驱动。所谓 UnionFS 就是把不同物理位置的目录合并 mount 到同一个目录中。简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。这种文件系统可以一层一层 地叠加修改文件。无论底下有多少层都是只读的,只有最上层的文件系统是可写 的。当需要修改一个文件时,AUFS 创建该文件的一个副本,使用 CoW 将文件 从只读层复制到可写层进行修改,结果也保存在可写层。在 Docker 中,底下的 只读层就是 image,可写层就是 Container,是 Docker 18.06 及更早版本的首选 存储驱动程序,在内核 3.13 上运行 Ubuntu 14.04 时不支持 overlay2。

Overlay:一种 Union FS 文件系统,Linux 内核 3.18 后支持

overlay2: Overlay 的升级版,到目前为止,所有 Linux 发行版推荐使用的存储类型。

devicemapper:是 CentOS 和 RHEL 的推荐存储驱动程序,因为之前的内核版 本不支持 overlay2,但是当前较新版本的 CentOS 和 RHEL 现在已经支持 overlay2,因此推荐使用 overlay2。

ZFS(Sun-2005)/btrfs(Oracle-2007):目前没有广泛使用。

vfs:用于测试环境,适用于无法使用 copy-on-write 文件系统的情况。 此存储 驱动程序的性能很差,通常不建议用于生产。

Docker 官方推荐首选存储引擎为 overlay2,devicemapper 存在使用空间方面的 一些限制,虽然可以通过后期配置解决,但是官方依然推荐使用 overlay2,以下 是网上查到的部分资料:

https://www.cnblogs.com/youruncloud/p/5736718.html

image-20211118154030023

如果 docker 数据目录是一块单独的磁盘分区而且是 xfs 格式的,那么需要在格 式化的时候加上参数-n ftype=1,否则后期在启动容器的时候会报错不支持 d-type。

Centos 7.2 报错界面:

image-20211224100732080

Centos 7.3 修复此问题:
image-20211224100757355

1.2.9:docker 服务进程

通过查看 docker 进程,了解 docker 的运行及工作方式

1.2.9.1:查看宿主机进程树

[root@localhost ~]# docker version
Docker 版本:
Server: Docker Engine - Community
 Engine:
  Version:          20.10.10
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.9
  Git commit:       e2f740d
  Built:            Mon Oct 25 07:43:13 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.12
  GitCommit:        7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

image-20211118161311612

1.2.9.2:查看 containerd 进程关系

有三个进程:

dockerd:被 client 直接访问,其父进程为宿主机的 systemd 守护进程。

containerd:被 dockerd 进程调用以实现与 runc 交互。

containerd-shim:真正运行容器的载体,其父进程为 containerd。(新版本的containerd-shim父进程是宿主机的systemd)每个containerd-shim就是一个docker容器

1.2.9.3: containerd-shim 命令使用

[root@localhost ~]# containerd-shim -h
Usage of containerd-shim:
  -address string
    	grpc address back to main containerd
  -containerd-binary containerd publish
    	path to containerd binary (used for containerd publish) (default "containerd")
  -criu string
    	path to criu binary
  -debug
    	enable debug output in logs
  -namespace string
    	namespace that owns the shim
  -runtime-root string
    	root directory for the runtime (default "/run/containerd/runc")
  -socket string
    	socket path to serve
  -systemd-cgroup
    	set runtime to use systemd-cgroup
  -workdir string
    	path used to storge large temporary data

1.2.9.4:容器的创建与管理过程

通信流程:

1.dockerd 通过 grpc 和 containerd 模块通信(runc)交换,dockerd 和 containerd 通信的 socket 文件:/run/containerd/containerd.sock。

2.containerd 在 dockerd 启动时被启动,然后 containerd 启动 grpc 请求监听, containerd 处理 grpc 请求,根据请求做相应动作。 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

3.若是创建容器,containerd 拉起一个 container-shim 容器进程 , 并进行相应 的创建操作。

4.container-shim 被拉起后,start/exec/create 拉起 runC 进程,通过 exit、control 文件和 containerd 通信,通过父子进程关系和 SIGCHLD(信号)监控容器中进程 状态。

5.在整个容器生命周期中,containerd 通过 epoll 监控容器文件,监控容器事件。

image-20211224101002625

1.2.9.5:grpc 简介

gRPC 是 Google 开发的一款高性能、开源和通用的 RPC 框架,支持众多语言 客户端。

https://www.grpc.io/

image-20211224101033992

1.3:Docker 镜像管理

Docker 镜像含有启动容器所需要的文件系统及所需要的内容,因此镜像主要 用于创建并启动 docker 容器。

Docker 镜像含里面是一层层文件系统,叫做 Union File System(Union FS 联 合文件系统),2004 年由纽约州立大学石溪分校开发,联合文件系统可以将多 个目录挂载到一起从而形成一整个虚拟文件系统,该虚拟文件系统的目录结构就 像普通 linux 的目录结构一样,docker 通过这些文件再加上宿主机的内核提供 了一个 linux 的虚拟环境,每一层文件系统我们叫做一层 layer,联合文件系统可 以对每一层文件系统设置三种权限,只读(readonly)、读写(readwrite)和写 出(whiteout-able),但是 docker 镜像中每一层文件系统都是只读的,构建镜 像的时候,从一个最基本的操作系统开始,每个构建的操作都相当于做一层的修改, 增加了一层文件系统,一层层往上叠加,上层的修改会覆盖底层该位置的可见性, 这也很容易理解,就像上层把底层遮住了一样,当使用镜像的时候,我们只会看 到一个完全的整体,不知道里面有几层也不需要知道里面有几层,结构如下:

image-20211118164335210

# pwd
/usr/local/src
mkdir a b system
touch a/a.txt b/b.txt
mount -t aufs -o dirs=./a:./b none ./system/

一个典型的Linux 文件系统由bootfs 和rootfs 两部分组成,bootfs(boot file system) 主要包含bootloader 和kernel,bootloader 主要用于引导加载kernel,当kernel 被加载到内存中后bootfs 会被umount 掉,rootfs (root file system)包含的就是典型Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件,下图就是docker image 中最基础的两层结构,不同的linux 发行版(如ubuntu 和CentOS ) 在rootfs 这一层会有所区别。
但是对于docker 镜像通常都比较小,官方提供的centos 基础镜像在200MB 左右,一些其他版本的镜像甚至只有几MB,docker 镜像直接调用宿主机的内核,镜像中只提供rootfs,也就是只需要包括最基本的命令、工具和程序库就可以了,比如alpine 镜像,在5M 左右。
下图就是有两个不同的镜像在一个宿主机内核上实现不同的rootfs

image-20211224102329849

容器、镜像父镜像:

image-20211224102356391

docker 命令是最常使用的docker 客户端命令,其后面可以加不同的参数以实现相应的功能,常用的命令如下:

1.3.1:搜索镜像:

在官方的docker仓库中搜索指定名称的docker镜像,也会有很多镜像。

[root@docker-server1~]#docker search centos:7.2.1511 #带指定版本号
[root@docker-server1~]#docker search centos #不带版本号默认latest

1.3.2:下载镜像:

从docker 仓库将镜像下载到本地,命令格式如下:

docker pull 仓库服务器:端口/项目名称/镜像名称:tag(版本)号

[root@docker-server1 ~]# docker pull alpine
[root@docker-server1 ~]# docker pull nginx
[root@docker-server1 ~]# docker pull hello-world
[root@docker-server1 ~]# docker pull centos

1.3.3:查看本地镜像:

#下载完成的镜像比下载的大,因为下载完成后会解压

root@docker-server1 ~]# docker images
REPOSITORY		# 镜像所属的仓库名称
TAG				# 镜像版本号(标识符),默认为latest
IMAGE ID		# 镜像唯一ID 标示
CREATED			# 镜像创建时间
VIRTUAL SIZE	# 镜像的大小

1.3.4:镜像导出

可以将镜像从本地导出问为一个压缩文件,然后复制到其他服务器进行导入使用。

#导出方法1:
[root@docker-server1 ~]# docker save centos -o /opt/centos.tar.gz
[root@docker-server1 ~]# ll /opt/centos.tar.gz
-rw------- 1 root root 205225472 Nov 1 03:52 /opt/centos.tar.gz

#导出方法2:
[root@docker-server1 ~]# docker save centos > /opt/centos-1.tar.gz
[root@docker-server1 ~]# ll /opt/centos-1.tar.gz
-rw-r--r-- 1 root root 205225472 Nov 1 03:52 /opt/centos-1.tar.gz

#查看镜像内容:
[root@docker-server1 ~]# cd /opt/
[root@docker-server1 opt]# tar xvf centos.tar.gz
[root@docker-server1 opt]# cat manifest.json #包含了镜像的相关配置,配置文件、分层
[{"Config":"196e0ce0c9fbb31da595b893dd39bc9fd4aa78a474bbdc21459a3 ebe855b7768.json","RepoTags":["docker.io/centos:latest"],"Layers":["892eb b5d1299cbf459f67aa070f29fdc6d83f40
25c58c090e9a69bd4f7af436b/layer.tar"]}]

#分层为了方便文件的共用,即相同的文件可以共用
[{"Config":" 配置文件.json","RepoTags":["docker.io/nginx:latest"],"Layers":[" 分层1/layer.tar","分层2 /layer.tar","分层3 /layer.tar"]}]

1.3.5:镜像导入:

将镜像导入到docker

[root@docker-server1 ~]# scp /opt/centos.tar.gz 192.168.10.206:/opt/
[root@docker-server2 ~]# docker load < /opt/centos.tar.gz

验证镜像:

docker images

1.3.6:删除镜像:

[root@docker-server1 opt]# docker rmi centos
#获取运行参数帮助
[root@linux-docker opt]# docker daemon –help
总结:企业使用镜像及常见操作:
搜索、下载、导出、导入、删除
命令总结:
# docker load -i centos-latest.tar.xz #导入本地镜像# docker save > /opt/centos.tar #centos #导出镜像
# docker rmi 镜像ID/镜像名称 #删除指定ID 的镜像,通过镜像启动容器的时候镜像不能被删除,除非将容器全部关闭
# docker rm 容器ID/容器名称 #删除容器
# docker rm 容器ID/容器名-f #强制删除正在运行的容器

命令总结:

docker load -i centos-latest.tar.xz #导入本地镜像# 
docker save > /opt/centos.tar #centos #导出镜像
docker rmi 镜像ID/镜像名称 #删除指定ID 的镜像,通过镜像启动容器的时候镜像不能被删除,除非将容器全部关闭
docker rm 容器ID/容器名称 #删除容器
docker rm 容器ID/容器名-f #强制删除正在运行的容器

1.4:容器操作基础命令:

命令格式:
# docker run [选项] [镜像名] [shell 命令] [参数]
# docker run [参数选项] [镜像名称,必须在所有选项的后面] [/bin/echo 'hello wold'] #单次执行,没有自定义容器名称
# docker run centos /bin/echo 'hello wold' #启动的容器在执行完shel 命令就退出了

1.4.1:从镜像启动一个容器

会直接进入到容器,并随机生成容器ID 和名称
[root@docker-server1 ~]# docker run -it docker.io/centos bash
[root@11445b3a84d3 /]#
#退出容器不注销
ctrl+p+q

1.4.2:显示正在运行的容器:

[root@linux-docker ~]# docker ps

1.4.3:显示所有容器:

包括当前正在运行以及已经关闭的所有容器:
[root@linux-docker ~]# docker ps -a


1.4.4:删除运行中的容器:

即使容正在运行当中,也会被强制删除掉
[root@docker-server1 ~]# docker rm -f 11445b3a84d3

1.4.5:随机映射端口:

[root@docker-server1 ~]# docker pull nginx #下载nginx 镜像
[root@docker-server1 ~]# docker run -P docker.io/nginx #前台启动并随机映射本地端口到容器的80
#前台启动的会话窗口无法进行其他操作,除非退出,但是退出后容器也会退出
#随机端口映射,其实是默认从32768 开始

1.4.6:指定端口映射:

方式1:本地端口81 映射到容器80 端口:
# docker run -p 81:80 --name nginx-test-port1 nginx
方式2:本地IP:本地端口:容器端口
# docker run -p 192.168.10.205:82:80 --name nginx-test-port2 docker.io/nginx
方式3:本地IP:本地随机端口:容器端口
# docker run -p 192.168.10.205::80 --name nginx-test-port3
docker.io/nginx
方式4:本机ip:本地端口:容器端口/协议,默认为tcp 协议
# docker run -p 192.168.10.205:83:80/udp --name nginx-test-port4 docker.io/nginx
方式5:一次性映射多个端口+协议:
# docker run -p 86:80/tcp -p 443:443/tcp -p 53:53/udp --name nginx-test-port5 docker.io/nginx
#查看Nginx 容器访问日志:
[root@docker-server1 ~]# docker logs nginx-test-port3 #一次查看
[root@docker-server1 ~]# docker logs -f nginx-test-port3 #持续查看
[root@docker-server1 ~]# docker logs --tail 200 nginx-test-port3 #查看最后200行

1.4.7:查看容器已经映射的端口:

[root@docker-server1 ~]# docker port nginx-test-port5

1.4.8:自定义容器名称:

[root@docker-server1 ~]# docker run -it --name nginx-test nginx

1.4.9:后台启动容器:

[root@docker-server1 ~]# docker run -d -P --name nginx-test1  docker.io/nginx

1.4.10:创建并进入容器:

[root@docker-server1 ~]# docker run -t -i --name test-centos2 docker.io/centos /bin/bash
[root@a8fb69e71c73 /]# #创建容器后直接进入,执行exit 退出后容器关闭

1.4.11:单次运行:

容器退出后自动删除:
[root@linux-docker opt]# docker run -it --rm --name nginx-delete-test docker.io/nginx

1.4.12:传递运行命令:

容器需要有一个前台运行的进程才能保持容器的运行,通过传递运行参数是一种方式,另外也可以在构建镜像的时候指定容器启动时运行的前台命令。

[root@docker-server1 ~]# docker run -d centos /usr/bin/tail -f '/etc/hosts'

1.4.13:容器的启动和关闭:

[root@docker-server1 ~]# docker stop f821d0cd5a99
[root@docker-server1 ~]# docker start f821d0cd5a99

1.4.14:进入到正在运行的容器:

1.4.14.1:使用attach 命令:

#使用方式为docker attach 容器名,attach 类似于vnc,操作会在各个容器界面显示,所有使用此方式进入容器的操作都是同步显示的且exit 后容器将被关闭,且使用exit 退出后容器关闭,不推荐使用,需要进入到有shell 环境的容器,比如centos 为例:

[root@s1 ~]# docker run -it centos bash
[root@63fbc2d5a3ec /]#
[root@s1 ~]# docker attach 63fbc2d5a3ec
[root@63fbc2d5a3ec /]#
在另外一个窗口启动测试页面是否同步:

1.4.14.2:使用exec 命令:

执行单次命令与进入容器,不是很推荐此方式,虽然exit 退出容器还在运行

docker exec -it centos-test3 /bin/bash

1.4.14.3:使用nsenter 命令:

推荐使用此方式,nsenter 命令需要通过PID 进入到容器内部,不过可以使用docker inspect 获取到容器的PID:

[root@docker-server1 ~]# yum install util-linux #安装nsenter 命令
[root@docker-server1 ~]# docker inspect -f "{{.NetworkSettings.IPAddress}}" 91fc190cb538
172.17.0.2
[root@docker-server1 ~]# docker inspect -f "{{.State.Pid}}" mydocker #获取到某个docker 容器的PID,可以通过PID 进入到容器内
[root@docker-server1 ~]# docker inspect -f "{{.State.Pid}}" centos-test3 5892
[root@docker-server1 ~]# nsenter -t 5892 -m -u -i -n -p
[root@66f511bb15af /]# ls

1.4.14.4:脚本方式:

将nsenter 命令写入到脚本进行调用,如下:
[root@docker-server1 ~]# cat docker-in.sh
#!/bin/bash
docker_in(){
	NAME_ID=$1
	PID=$(docker inspect -f "{{.State.Pid}}" ${NAME_ID})
	nsenter -t ${PID} -m -u -i -n -p
}
docker_in $1
#测试脚本是否可以正常进入到容器且退出后仍然正常运行:
[root@docker-server1 ~]# chmod a+x docker-in.sh
[root@docker-server1 ~]# ./docker-in.sh centos-test3
[root@66f511bb15af /]# pwd
[root@66f511bb15af /]# exit
logout
[root@docker-server1 ~]# ./docker-in.sh centos-test3
[root@66f511bb15af /]# exit

1.4.15: 查看容器内部的hosts 文件:

[root@docker-server1 ~]# docker run -i -t --name test-centos3 docker.io/centos /bin/bash
[root@056bb4928b64 /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4 056bb4928b64 #默认会将实例的ID 添加到自己的hosts 文件

1.4.16:批量关闭正在运行的容器:

[root@docker-server1 ~]# docker stop $(docker ps -a -q) #正常关闭所有运行中的容器

1.4.17:批量强制关闭正在运行的容器:

[root@docker-server1 ~]# docker kill $(docker ps -a -q) #强制关闭所有运行中的容器

1.4.18:批量删除已退出容器:

[root@docker-server1 ~]# docker rm -f `docker ps -aq -f status=exited`

1.4.19:批量删除所有容器:

[root@docker-server1 ~]# docker rm -f $(docker ps -a -q)

1.4.20:指定容器DNS:

Dns 服务,默认采用宿主机的dns 地址
一是将dns 地址配置在宿主机
二是将参数配置在docker 启动脚本里面–dns=1.1.1.1

1.4.21:其他命令:

# docker update 容器--cpus 2 #更新容器配置信息
# docker events #获取dockerd 的实时事件
2020-05-12T08:26:37.116277036+08:00 container create ba55fe25733c123e7bf9ab5a8bcb78ddb83d864e2f012a2f0098c9bb9e4a66f2 (image=busybox, name=nifty_elgamal)
2020-05-12T08:26:37.118436296+08:00 container attach ba55fe25733c123e7bf9ab5a8bcb78ddb83d864e2f012a2f0098c9bb9e4a66f2 (image=busybox, name=nifty_elgamal)
root@docker-node1:~# docker stop f8ae0c8dcaf8
f8ae0c8dcaf8
root@docker-node1:~# docker wait f8ae0c8dcaf8 #显示容器的退出状态码
137
# docker diff f8ae0c8dcaf8 #检查容器更改过的文件或目录

[root@docker-server1 ~]# docker run -it --rm --dns 223.6.6.6 centos bash
[root@afeb628bf074 /]# cat /etc/resolv.conf
nameserver 223.6.6.6

二:Docker 镜像与制作:

Docker 镜像有没有内核?
从镜像大小上面来说,一个比较小的镜像只有十几MB,而内核文件需要一百多兆, 因此镜像里面是没有内核的,镜像在被启动为容器后将直接使用宿主机的内核,而镜像本身则只提供相应的rootfs,即系统正常运行所必须的用户空间的文件系统,比如/dev/,/proc,/bin,/etc 等目录,所以容器当中基本是没有/boot 目录的,而/boot 当中保存的就是与内核相关的文件和目录。

为什么没有内核?
由于容器启动和运行过程中是直接使用了宿主机的内核,所以没有直接调用过物理硬件,所以也不会涉及到硬件驱动,因此也用不上内核和驱动,另外有内核的那是虚拟机。

2.1:手动制作yum 版nginx 镜像:

Docker 制作类似于虚拟机的模板制作,即按照公司的实际业务务求将需要安装的软件、相关配置等基础环境配置完成,然后将虚拟机再提交为模板,最后再批量从模板批量创建新的虚拟机,这样可以极大的简化业务中相同环境的虚拟机运行环境的部署工作,Docker 的镜像制作分为手动制作和自动制作(基于DockerFile),企业通常都是基于Dockerfile 制作镜像,其中手动制作镜像步骤具体如下:

2.1.1:下载镜像并初始化系统:

基于某个基础镜像之上重新制作,因此需要先有一个基础镜像,本次使用官方提
供的centos 镜像为基础:

[root@docker-server1 ~]# docker pull centos
[root@docker-server1 ~]# docker run -it docker.io/centos /bin/bash
[root@37220e5c8410 /]# yum install wget -y
[root@37220e5c8410 /]# cd /etc/yum.repos.d/
[root@37220e5c8410 yum.repos.d]# rm -rf ./* #更改yum 源
[root@37220e5c8410 yum.repos.d]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
[root@37220e5c8410 yum.repos.d]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

2.1.2:yum 安装并配置nginx:

[root@37220e5c8410 yum.repos.d]# yum install nginx –y #yum 安装nginx
[root@37220e5c8410 yum.repos.d]# yum install -y vim wget pcre
pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop #安装常用命令

2.1.3:关闭nginx 后台运行:

[root@37220e5c8410 yum.repos.d]# vim /etc/nginx/nginx.conf #关闭nginx 后台运行
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
daemon off; #关闭后台运行

2.1.4:自定义web 页面:

[root@37220e5c8410 yum.repos.d]# vim /usr/share/nginx/html/index.html
[root@37220e5c8410 yum.repos.d]# cat /usr/share/nginx/html/index.html
Docker Yum Nginx #自定义web 界面

2.1.5:提交为镜像:

在宿主机基于容器ID 提交为镜像

root@docker-node1:~# docker commit --help
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Create a new image from a container's changes
Options:
-a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
-c, --change list Apply Dockerfile instruction to the created image
-m, --message string Commit message
-p, --pause Pause container during commit (default true)
[root@docker-server1 ~]# docker commit -a "61864003@qq.com" -m "nginx yum v1" --change="EXPOSE 80 443" f5f8c13d0f9f centos-nginx:v1
sha256:7680544414220f6ff01e58ffc6bd59ff626af5dbbce4914ceb18e20f4ae0276a

2.1.6:带tag 的镜像提交:

提交的时候标记tag 号:
#标记tag 号,生产当中比较常用,后期可以根据tag 标记创建不同版本的镜像以及创建不同版本的容器。

[root@docker-server1 ~]# docker commit -m "nginx image" f5f8c13d0f9f qinghai/centos-nginx:v1
sha256:ab9759679eb586f06e315971d28b88f0cd3e0895d2e162524ee21786b98b24e8

2.1.7:从自己镜像启动容器:

[root@docker-server1 ~]# docker run -d -p 80:80 --name my-centos-nginx qinghai/centos-nginx /usr/sbin/nginx
ce4ee8732a0c4c6a10b85f5463396b27ba3ed120b27f2f19670fdff3bf5cdb62

2.1.8:访问测试:

2.2:DockerFile 制作编译版nginx 1.16.1 镜像:

DockerFile 可以说是一种可以被Docker 程序解释的脚本,DockerFile 是由一条条的命令组成的,每条命令对应linux 下面的一条命令,Docker 程序将这些DockerFile 指令再翻译成真正的linux 命令,其有自己的书写方式和支持的命令,Docker 程序读取DockerFile 并根据指令生成Docker 镜像,相比手动制作镜像的方式,DockerFile 更能直观的展示镜像是怎么产生的,有了写好的各种各样DockerFile 文件,当后期某个镜像有额外的需求时,只要在之前的DockerFile添加或者修改相应的操作即可重新生成新的Docke 镜像,避免了重复手动制作镜像的麻烦,具体如下:
https://docs.docker.com/engine/reference/builder/

FROM	# 在整个dockFile文件中,除了注释之外的第一行,要是from,用于指定父镜像
ADD		# 用于添加宿主机本地的文件、目录、压缩等资源到镜像里面去,会自动解压tar.gz格式的压缩包,不会自动解压zip
COPY	# 用于添加宿主机本地的文件、目录、压缩等资源到镜像里面去,不会解压任何压缩包
ENV		# 设置环境变量
EXPOSE	# 声明要把容器的某个端口映射到宿主机。
MAINTAINER	# 镜像的作者信息
LABEL	# 设置镜像的属性标签
STOPSIGNAL	# 停止信号
USER	# 指定运行操作的用户,如果指定了,这个指令之下的所以操作都会以这个用户执行,所以,即使指定,也要注意位置。一般不指定
VOLUME	# 定义volume卷
WORKDIR	# 工作目录,相当于cd到某个目录,以后所有操作,都在当前目录,除非再有其他workdir指令或者cd命令
RUN		# 执行shell命令,但是一定要以非交互方式执行
CMD		# 镜像启动为一个容器时的默认命令或者脚本,如果有ENTRYPOINT,CMD会作为ENTRYPOINT的参数
ENTRYPOINT	# 容器启动时要执行的命令或者脚本。

2.2.1:下载镜像并初始化系统:

[root@docker-server1 ~]# docker pull centos
[root@docker-server1 ~]# docker run -it docker.io/centos /bin/bash
[root@docker-server1 ~]# cd /opt/ #创建目录环境
[root@docker-server1 opt]# mkdir dockerfile/{web/{nginx,tomcat,jdk,apache},system/{centos,ubuntu,redhat}} -pv
#目录结构按照业务类型或系统类型等方式划分,方便后期镜像比较多的时候进行分类。

进入到指定的Dockerfile 目录:

[root@docker-server1 opt]# cd dockerfile/web/nginx/
[root@docker-server1 nginx]# pwd
/opt/dockerfile/web/nginx

2.2.2:编写Dockerfile:

[root@docker-server1 nginx]# vim ./Dockerfile #生成的镜像的时候会在执行命令的当前目录查找Dockerfile 文件,所以名称不可写错,而且D 必须大写
#My Dockerfile
#"#"为注释,等于shell 脚本的中#

#除了注释行之外的第一行,必须是From xxx (xxx 是基础镜像)
From centos #第一行先定义基础镜像,后面的本地有效的镜像名,如果本地没有会从远程仓库下载,第一行很重要

#镜像维护者的信息
#  MAINTAINER qinghai 123456@qq.com
LABEL MAINTAINER=“qinghai 123456@qq.com”  # 推荐LABEL

####################其他可选参数#######################
#USER #指定该容器运行时的用户名和UID,后续的RUN 命令也会使用这面指定的用户执行
#WORKDIR /a
#WORKDIR b #指定工作目录,最终为/a/b
#VOLUME ["/dir_1", "/dir_2" ..] 设置容器挂载主机目录
#ENV name jack #设置容器变量,常用于想容器内传递用户密码等
#####################################################

#执行的命令,将编译安装nginx 的步骤执行一遍
RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm
RUN yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop
ADD nginx-1.16.1.tar.gz /usr/local/src/ #自动解压压缩包
RUN cd /usr/local/src/nginx-1.16.1 && ./configure --prefix=/usr/local/nginx --with-http_sub_module && make && make install

ADD nginx.conf /usr/local/nginx/conf/nginx.conf
RUN useradd nginx -s /sbin/nologin
RUN ln -sv /usr/local/nginx/sbin/nginx /usr/sbin/nginx
RUN echo "test nginx page" > /usr/local/nginx/html/index.html
EXPOSE 80 443 #向外开放的端口,多个端口用空格做间隔,启动容器时候-p 需要使用此端向外映射,如: -p 8081:80,则80 就是这里的80
CMD ["nginx","-g","daemon off;"] #运行的命令,每个Dockerfile 只能有一条,如果有多条则只有最后一条被执行
#如果在从该镜像启动容器的时候也指定了命令,那么指定的命令会覆盖

Dockerfile 构建的镜像里面的CMD 命令,即指定的命令优先级更高,Dockerfile的优先级较低一些,重新指定的指令优先级要高一些。

2.2.3:准备源码包与配置文件:

[root@docker-server1 nginx]# cp /usr/local/nginx/conf/nginx.conf . #使用其它服务器编译安装相同版本的nginx 配置文件,而且配置文件中关闭Nginx进程后台运行
[root@docker-server1 nginx]# cp /usr/local/src/nginx-1.16.1.tar.gz . #nginx 源码包

2.2.4:执行镜像构建:

# docker build -t nginx:v1 .

2.2.5:构建完成:

可以清晰看到各个步骤执行的具体操作

image-20211224112632836

2.2.6:查看是否生成本地镜像:

image-20211224112739052

2.2.7:从镜像启动容器:

root@docker-server1:~# docker run -it -d -p 80:80 nginx:v1 
8c48b74c1f86305eef18e8950fb25451c159aeda092902b6d52d6074cb0fe152

image-20211224112829349

2.2.8:访问web 界面:

2.2.9:镜像编译制作程中:

制作镜像过程中,需要启动一个临时的容器进行相应的指令操作,操作完成后再把此容器转换为image。

2.3:手动制作编译版本nginx 1.16.1 镜像:

过程为在centos 基础镜像之上手动编译安装nginx,然后再提交为镜像。

2.3.1:下载镜像并初始化系统:

[root@docker-server1 ~]# docker pull centos
[root@docker-server1 ~]# docker run -it docker.io/centos /bin/bash
[root@86a48908bb97 /]# yum install wget -y
[root@86a48908bb97 /]# cd /etc/yum.repos.d/
[root@86a48908bb97 yum.repos.d]# rm -rf ./* #更改yum 源
[root@86a48908bb97 yum.repos.d]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
[root@86a48908bb97 yum.repos.d]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

2.3.2:编译安装nginx:

[root@86a48908bb97 yum.repos.d]# yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop #安装基础包
[root@86a48908bb97 yum.repos.d]# cd /usr/local/src/
[root@86a48908bb97 src]# wget http://nginx.org/download/nginx-1.16.1.tar.gz
[root@86a48908bb97 src]# tar xvf nginx-1.16.1.tar.gz
[root@86a48908bb97 src]# cd nginx-1.16.1
[root@86a48908bb97 nginx-1.16.1]# ./configure --prefix=/apps/nginx --with-http_sub_module
[root@86a48908bb97 nginx-1.16.1]# make && make install
[root@86a48908bb97 nginx-1.16.1]# cd /apps/nginx/

2.3.3:关闭nginx 后台运行:

[root@86a48908bb97 nginx]# vim /apps/nginx/conf/nginx.conf
user nginx;
worker_processes auto;
daemon off;
[root@86a48908bb97 nginx]# ln -sv /apps/nginx/sbin/nginx /usr/sbin/nginx #创建nginx 命令软连

2.3.4:自定义web 界面:

[root@86a48908bb97 nginx]# mkdir /apps/nginx/html/test
[root@86a48908bb97 nginx]# echo "test" > /apps/nginx/html/test/index.html

2.3.5:创建用户及授权:

[root@86a48908bb97 nginx]# useradd -u 2019 nginx -s /sbin/nologin
[root@86a48908bb97 nginx]# chown nginx.nginx /usr/local/nginx/ -R

2.3.6:在宿主机提交为镜像:

[root@docker-server1 ~]# docker commit -m "test nginx" 86a48908bb97 test-nginx:v1
sha256:fce6e69410e58b8e508c7ffd2c5ff91e59a1144847613f691fa5e80bb68 efbfa

2.3.7:从自己的镜像启动容器:

[root@docker-server1 ~]# docker run -d -p 80:80 --name test-centos-nginx magedu-nginx:v1 /usr/sbin/nginx 
8042aedec1d6412a79ac226c9289305087fc062b0087955a3a0a609c891e11 22

备注: -name 是指定容器的名称,-d 是后台运行,-p 是端口映射, jack/nginx-test-image 是xx 仓库下的xx 镜像的xx 版本,可以不加版本,不加版本默认是使用latest,最后面的nginx 是运行的命令,即镜像里面要运行一个nginx 命令,所以才有了前面将/usr/local/nginx/sbin/nginx 软连接到 /usr/sbin/nginx,目的就是为了让系统可以执行此命令。

2.3.8:访问测试:

2.3.9:查看Nginx 访问日志:

image-20211224113530312

2.4:自定义Tomcat 业务镜像:

基于官方提供的centos、debain、ubuntu、alpine 等基础镜像构建JDK(Java环境),然后再基于自定义的JDK 镜像构建出业务需要的tomcat 镜像。

2.4.1:构建JDK 镜像:

先基于官方提供的基础镜像,制作出安装了常用命令的自定义基础镜像,然后在基础镜像的基础之上,再制作JDK 镜像、Tomcat 镜像等。

2.4.1.1:自定义Centos 基础镜像:

# docker pull centos
# mkdir
/opt/dockerfile/{web/{nginx,tomcat,jdk,apache},system/{centos,ubuntu,redhat}} -pv
# cd /opt/dockerfile/system/centos/


# cat Dockerfile
# Centos Base Image
#
FROM centos:latest
MAINTAINER qinghai 61864003@qq.com
RUN yum install -y epel-release
RUN yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop 
RUN groupadd www -g 2020 && useradd www -u 2020 -g www #添加系统账户


# cat build-command.sh #通过脚本构建镜像
#!/bin/bash
docker build -t centos-base:v1 .
# bash build-command.sh #通过脚本还行镜像构建

自定义基础镜像制作过程中:

image-20211227213716717

自定义基础镜像制作完成:

image-20211227213754520

2.4.1.2:执行构建JDK 镜像:

# cd /opt/dockerfile/web/jdk/
# cat Dockerfile
#JDK Base Image
FROM centos-base:v1

MAINTAINER qinghai "61864003@qq.com"

ADD jdk-8u212-linux-x64.tar.gz /usr/local/src/
RUN ln -sv /usr/local/src/jdk1.8.0_212 /usr/local/jdk 
ADD profile /etc/profile

ENV JAVA_HOME /usr/local/jdk
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/:$JRE_HOME/lib/
ENV PATH $PATH:$JAVA_HOME/bin
RUN rm -rf /etc/localtime && ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

2.4.1.3:上传JDK 压缩包和profile 文件:

将JDK 压缩包上传到Dockerfile 当前目录,然后执行构建:

# pwd
/opt/dockerfile/web/jdk
# tree
.
├──build-command.sh
├──Dockerfile
├──jdk-8u212-linux-x64.tar.gz
└──profile
0 directories, 4 files

2.4.1.4:执行构建自定义JDK 基础镜像:

构建并验证自定义JDK 基础镜像

2.4.1.4.1:通过脚本构建:
# cat build-command.sh
#!/bin/bash
docker build -t jdk-base:v8.212 .
2.4.1.4.2:执行构建:

image-20211224114142599

2.4.1.5:验证镜像构建完成:

image-20211224114231454

2.4.1.6:验证镜像JDK 环境:

从镜像创建出一个测试容器,验证容器的中的JDK 环境是否可用。

[root@docker-server1 jdk]# docker run -it jack/ceontos-jdk bash
# docker run -it --rm jdk-base:v8.212 bash

[root@3373ee6a824e /]# java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)

2.4.2:从JDK 镜像构建tomcat 8 Base 镜像:

基于自定义的JDK 基础镜像,构建出通用的自定义Tomcat 基础镜像,此镜像后期会被多个业务的多个服务共同引用(相同的JDK 版本和Tomcat 版本)。

2.4.2.1:编辑Dockerfile:

# pwd
/opt/dockerfile/web/tomcat/tomcat-base

# cat Dockerfile

#Tomcat Base Image
FROM jdk-base:v8.212

#env
ENV TZ "Asia/Shanghai"
ENV LANG en_US.UTF-8
ENV TERM xterm
ENV TOMCAT_MAJOR_VERSION 8
ENV TOMCAT_MINOR_VERSION 8.5.45
ENV CATALINA_HOME /apps/tomcat
ENV APP_DIR ${CATALINA_HOME}/webapps

#tomcat
RUN mkdir /apps
ADD apache-tomcat-8.5.45.tar.gz /apps
RUN ln -s /apps/apache-tomcat-8.5.45 /apps/tomcat

2.4.2.2:上传tomcat 压缩包:

# tree
.
├──apache-tomcat-8.5.45.tar.gz
├──build-command.sh
└──Dockerfile
0 directories, 3 files

2.4.2.3:通过脚本构建tomcat 基础镜像:

[root@docker-server1 tomcat8-base]# cat build-command.sh
# cat build-command.sh
#!/bin/bash
docker build -t tomcat-base:v8.5.45 .

#执行构建:

# bash build-command.sh

2.4.2.4:验证镜像构建完成:

image-20211224114844261

2.4.3:构建业务镜像1:

创建tomcat app1 和tomcat app2 两个目录,表示基于tomcat 自定义基础镜像构建出不同业务的tomcat app 镜像。

2.4.3.1:准备Dockerfile:

# pwd
/opt/dockerfile/web/tomcat/tomcat-app1
# cat Dockerfile
#Tomcat Web Image
FROM tomcat-base:v8.5.45

ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh
ADD myapp/* /apps/tomcat/webapps/myapp/

RUN chown www.www /apps/ -R

EXPOSE 8080 8009

CMD ["/apps/tomcat/bin/run_tomcat.sh"]

2.4.3.2:准备自定义myapp 页面:

# mkdir myapp
# echo "Tomcat Web Page" > myapp/index.html
# cat myapp/index.html
Tomcat Web Page

2.4.3.3:准备容器启动执行脚本:

# cat run_tomcat.sh
#!/bin/bash
echo "1.1.1.1 abc.test.com" >> /etc/hosts
echo "nameserver 223.5.5.5" > /etc/resolv.conf
su - www -c "/apps/tomcat/bin/catalina.sh start"
su - www -c "tail -f /etc/hosts

2.4.3.4:准备构建脚本:

[root@docker-server1 tomcat-app1]# cat build-command.sh 
# cat build-command.sh
#!/bin/bash
docker build -t tomcat-web:app1 .

2.4.3.5:执行构建:

# pwd
/opt/dockerfile/web/tomcat/tomcat-app1
# chmod a+x *.sh
# tree
.
├──build-command.sh
├──Dockerfile
├──myapp
│ └──index.html 
└──run_tomcat.sh
1 directory, 4 file
# bash build-command.sh

image-20211224115232694

2.4.3.6:从镜像启动容器测试:

# docker run -it -d -p 8080:8080 tomcat-web:app1 
ad2eef20efe9299deb3289137001690c19f14978a6cc34f8f383df5951ac98a0

image-20211224115323666

2.4.3.7:访问测试:

image-20211224115346958

2.4.4.:构建业务镜像2:

第二个tomcat 服务镜像。

2.4.4.1:准备Dockerfile:

# pwd
/opt/dockerfile/web/tomcat/tomcat-app2

# cat Dockerfile
#Tomcat Web Image
FROM tomcat-base:v8.5.45

ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh 
ADD myapp/* /apps/tomcat/webapps/myapp/

RUN chown www.www /apps/ -R

EXPOSE 8080 8009

CMD ["/apps/tomcat/bin/run_tomcat.sh"]

2.4.4.2:准备自定义页面:

# mkdir myapp
# echo "Tomcat Web Page2" > myapp/index.html

# cat myapp/index.html
Tomcat Web Page2

2.4.4.3:准备容器启动脚本:

# cat run_tomcat.sh
#!/bin/bash
echo "1.1.1.1 abc.test.com" >> /etc/hosts
echo "nameserver 223.5.5.5" > /etc/resolv.conf

su - www -c "/apps/tomcat/bin/catalina.sh start" 
su - www -c "tail -f /etc/hosts"

2.4.4.4:准备构建脚本:

# cat build-command.sh
#!/bin/bash
docker build -t tomcat-web:app2 .

2.4.4.5:执行构建:

# pwd
/opt/dockerfile/web/tomcat/tomcat-app2
# bash build-command.sh

image-20211224115651493

2.4.4.6:从镜像启动容器:

# docker run -it -d -p 8081:8080 tomcat-web:app2 
fa01784532f09c04da3ab0f81efdee7aefebc15c8f6dbfbf00a9bbc566e6f597

2.4.4.7:访问测试:

image-20211224115740975

2.5:构建haproxy 镜像:

构建出haproxy 镜像,将haproxy 通过容器的方式运行。

2.5.1:准备Dockerfile:

# pwd
/opt/dockerfile/web/haproxy

# cat Dockerfile
#Haproxy Base Image
FROM centos-base:v1

MAINTAINER liqinghai "61864003@qq.com"
RUN yum install -y yum install gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel net-tools vim iotop bc zip unzip zlib-devel lrzsz tree screen lsof tcpdump wget ntpdate 
ADD haproxy-2.0.5.tar.gz /usr/local/src/
RUN cd  /usr/local/src/haproxy-2.0.5 && make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_CPU_AFFINITY=1 PREFIX=/usr/local/haproxy && make install PREFIX=/usr/local/haproxy && cp haproxy /usr/sbin/&& mkdir /usr/local/haproxy/runADD haproxy.cfg /etc/haproxy/

ADD run_haproxy.sh /usr/bin
EXPOSE 80 9999
CMD ["/usr/bin/run_haproxy.sh"]

2.5.2:准备haproxy 源码和配置文件:

# pwd
/opt/dockerfile/web/haproxy

2.5.3:准备haproxy 配置文件:

# cat haproxy.cfg
global
chroot /usr/local/haproxy
#stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin uid 99
gid 99
daemon
nbproc 1
pidfile /usr/local/haproxy/run/haproxy.pid
log 127.0.0.1 local3 info

defaults
option http-keep-alive
option forwardfor
mode http
timeout connect 300000ms 
timeout client 300000ms 
timeout server 300000ms

listen stats
	mode http
	bind 0.0.0.0:9999
	stats enable
	log global
	stats uri /haproxy-status stats auth haadmin:123456

listen web_port
	bind 0.0.0.0:80
	mode http
	log global
	balance roundrobin
	server web1 192.168.7.101:8080 check inter 3000 fall 2 rise 5 
	server web2 192.168.7.102:8080 check inter 3000 fall 2 rise 5

2.5.4:准备镜像构建脚本

# cat build-command.sh
#!/bin/bash
docker build -t haproxy:2.0.5 .

2.5.5:执行构建haproxy 镜像:

image-20211224143740006

2.5.6:从镜像启动容器:

# docker run -it -d -p 80:80 -p 9999:9999 haproxy:2.0.5 
d20e821a82e4a4a8b2d4b4c7f79f4750e5b3878bf10b43d30bd3ad2196687af a

2.5.7:web 访问验证:

image-20211224143830187

2.5.8:访问haproxy 控制端:

下图中web2 服务器,需要将tomcat 镜像从镜像服务器把tomcat 镜像导出,然后scp 到web2 服务器,然后导入镜像,最后通过镜像启动一个容器并监听本机的8080 端口即可,否则web2 服务器状态显示异常。

image-20211224143904252

2.6:基于官方alpine 基础镜像制作自定义镜像:

FROM alpine
maintainer qinghai "61864003@qq.com"
COPY repositories /etc/apk/repositories
RUN apk update && apk add iotop gcc libgcc libc-dev libcurl libc-utils pcre-dev zlib-dev libnfs make pcre pcre2 zip unzip net-tools pstree wget libevent libevent-dev iproute2 openssl openssl-dev
ADD nginx-1.16.1.tar.gz /opt/

RUN cd /opt/nginx-1.16.1 && ./configure --prefix=/apps/nginx && make && make install && ln -sv /apps/nginx/sbin/nginx /usr/bin/

RUN addgroup -g 2019 -S nginx && adduser -s /sbin/nologin -S -D -u 2019 -G nginx nginx

COPY nginx.conf /apps/nginx/conf/nginx.conf
ADD static.tar.gz /data/nginx/html
RUN chown nginx.nginx /data/nginx/ /apps/nginx/ -R

EXPOSE 80 443

CMD ["nginx"]

2.7:基于官方Ubuntu 基础镜像制作自定义镜像:

# cat Dockerfile
FROM ubuntu:18.04
maintainer liqinghai "61864003@qq.com"
COPY sources.list /etc/apt/sources.list
RUN apt update && apt install -y iproute2 ntpdate tcpdump telnet traceroute nfs-kernel-server nfs-common lrzsz tree openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev ntpdate tcpdump telnet traceroute gcc openssh-server lrzsz tree openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev ntpdate tcpdump telnet traceroute iotop unzip zip make

ADD nginx-1.16.1.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.16.1 && ./configure --prefix=/apps/nginx && make && make install && ln -sv  /apps/nginx/sbin/nginx /usr/bin && rm -rf /usr/local/src/nginx-1.16.1 && rm -rf /usr/local/src/nginx-1.16.1.tar.gz 
ADD nginx.conf /apps/nginx/conf/nginx.conf
ADD static.tar.gz /data/nginx/html

RUN groupadd -g 2019 nginx && useradd -g nginx -s /usr/sbin/nologin -u 2019 nginx && chown -R nginx.nginx /apps/nginx /data/nginx

EXPOSE 80 443

CMD ["nginx"]

2.8:本地镜像上传至官方docker 仓库:

#将自制的镜像上传至docker 仓库;https://hub.docker.com/,实现镜像夸主机备份。

2.8.1:准备账户:

登录到docker hub 创建官网创建账户,登录后点击settings 完善账户信息:

2.8.2:填写账户基本信息:

2.8.3:在虚拟机使用自己的账号登录:

# docker login docker.io

2.8.4:查看认证信息:

#登录成功之后会在当前目录生成一个隐藏文件用于保存登录认证信息

root@ubuntu-template:~# cat /root/.docker/config.json 
{
	"auths": {
		"https://index.docker.io/v1/": {
			"auth": "dmlwODphMTIzNDU2Nzg5"
		}
	}

2.8.5:给镜像做tag 并开始上传:

[root@linux-docker ~]# docker images #查看镜像ID

#为镜像做标记并上传至仓库
# docker tag alpine:latest docker.io/vip8/alpine:latest 
# docker push docker.io/vip8/alpine:latest

2.8.6:上传完成

2.8.7:到docker 官网验证:

2.8.8:更换到其他docker 服务器下载镜像:

2.8.9:从镜像启动一个容器:

2.9:本地镜像上传到阿里云:

将本地镜像上传至阿里云,实现镜像备份与统一分发的功能。
https://cr.console.aliyun.com
注册账户、创建namespace、创建仓库、修改镜像tag 及上传镜像

# docker login --username=诠释小丑01 registry.cn-hongkong.aliyuncs.com
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

2.9.1:将镜像推送到Registry

$ docker login --username=诠释小丑01 registry.cn-hangzhou.aliyuncs.com
$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/vip8/nginx:[镜像版本号]
$ docker push registry.cn-hangzhou.aliyuncs.com/vip8/nginx:[镜像版本号]

2.9.2:阿里云验证镜像:

image-20211230154439726

三:Docker 数据管理:

如果正在运行中的容器修如果生成了新的数据或者修改了现有的一个已经存在的文件内容,那么新产生的数据将会被复制到读写层进行持久化保存,这个读写层也就是容器的工作目录,此即“写时复制(COW) copy on write”机制。
如下图是将对根的数据写入到了容器的可写层,但是把/data 中的数据写入到了一个另外的volume 中用于数据持久化。

image-20211224144724120

3.1:数据类型:

Docker的镜像是分层设计的,镜像层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户写入的数据都保存在这一层当中。
如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录,目前Docker的数据类型分为两种:
一是数据卷(datavolume),数据卷类似于挂载的一块磁盘,数据容器是将数据保存在一个容器上.
二是数据卷容器(Datavolumecontainer), 数据卷容器是将宿主机的目录挂载
至一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据。

image-20211224144802005

root@s1:~# docker inspect f55c55544e05 #查看指定PID 的容器信息

image-20211224144834860

Lower Dir:image 镜像层(镜像本身,只读)
Upper Dir:容器的上层(读写)
Merged Dir:容器的文件系统,使用Union FS(联合文件系统)将lowerdir 和upper Dir:合并给容器使用。
Work Dir:容器在宿主机的工作目录

在容器生成数据:

# docker exec -it fa01784532f0 bash
[root@fa01784532f0 /]# dd if=/dev/zero of=file bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 0.257476 s, 407 MB/s

[root@fa01784532f0 /]# md5sum file 
2f282b84e7e608d5852449ed940bfc51 file
# cp anaconda-post.log /opt/

数据在宿主机哪里?

image-20211224144959808

当容器被删除后,宿主机的数据还在吗?

3.1.1:什么是数据卷(data volume):

数据卷实际上就是宿主机上的目录或者是文件,可以被直接mount 到容器当中使用。
实际生产环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性。
如下图:
左侧是无状态的http 请求服务,右侧为有状态。
下层为不需要存储的服务,上层为需要存储的部分服务。

image-20211224145035405

3.1.1.1:创建APP 目录并生成web 页面:

此app 以数据卷的方式,提供给容器使用,比如容器可以直接宿主机本地的web app,而需要将代码提前添加到容器中,此方式适用于小型web 站点。

# mkdir /data/testapp –p
# echo "testapp page" > /data/testapp/index.html
# cat /data/testapp/index.html
testapp page

3.1.1.2:启动容器并验证数据:

启动两个容器,web1 容器和web2 容器,分别测试能否在宿主机访问到宿主机的数据。
#注意使用-v 参数,将宿主机目录映射到容器内部,web2 的ro 标示在容器内对该目录只读,默认是可读写的:

root@docker-server1:~# docker run -d --name web1 -v /data/testapp/:/apps/tomcat/webapps/testapp -p 8080:8080 tomcat-web:app1 
463bf7548b724c74c99d2c583831af680a0b9575a1d62206311898a759bc823b
# docker run -d --name web2 -v /data/testapp/:/apps/tomcat/webapps/testapp:ro -p 8081:8080 tomcatweb:app2
999ed545ca948551d3a51338a0080c1ea45b3f8446e7454b880e8c8cde269ba8

3.1.1.3:进入到容器内测试写入数据:

image-20211224145250560

3.1.1.4:宿主机验证:

验证宿主机的数据是否正常

image-20211224145317013

3.1.1.5:web 界面访问:

image-20211224145347425

3.1.1.6:在宿主机或容器修改数据:

# echo "web v2" >> /data/testapp/index.html 
# cat /data/testapp/index.html
testapp page web v2

3.1.1.7:web 端访问验证数据:

image-20211224145444636

3.1.1.8:删除容器:

#创建容器的时候指定参数-v,可以删除/var/lib/docker/containers/的容器数据目录,但是不会删除数据卷的内容,如下:

image-20211224145513405

3.1.1.9:验证宿主机的数据:

1、数据卷是宿主机的目录或者文件,并且可以在多个容器之间共同使用。
2、在宿主机对数据卷更改数据后会在所有容器里面会立即更新。
3、数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响。 
4、在容器里面的写入数据不会影响到镜像本身。

3.1.2:文件挂载:

文件挂载用于很少更改文件内容的场景,比如nginx 的配置文件、tomcat 的配置文件等。

3.1.2.1:创建容器并挂载配置文件:

# ll /data/testapp/
total 28
drwxr-xr-x 2 root root 43 Sep 12 17:40 ./
drwxr-xr-x 4 root root 35 Sep 12 17:10 ../
-rwxr-xr-x 1 root root 23611 Aug 5 2018 catalina.sh*
-rw-r--r-- 1 root root 20 Sep 12 17:22 index.html

#自定义JAVA 选项参数:
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g
-XX:CMSInitiatingOccupancyFraction=65 
-XX:+UseFastAccessorMethods
-XX:+AggressiveOpts 
-XX:+UseBiasedLocking 
-XX:+DisableExplicitGC
-XX:MaxTenuringThreshold=10 
-XX:NewSize=2048M
-XX:MaxNewSize=2048M
-XX:NewRatio=2
-XX:PermSize=128m
-XX:MaxPermSize=512m
-XX:CMSFullGCsBeforeCompaction=5
-XX:+ExplicitGCInvokesConcurrent
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods"

创建容器:

# docker run -it -d -p 8080:8080 -v /data/testapp/catalina.sh:/apps/tomcat/bin/catalina.sh:ro tomcat-web:app2 
2e90299a39163d7c3af97888b84f02f2c3481529fcf6a9da8f06fbb92bd05f7c

3.1.2.2:验证参数生效:

image-20211224145953770

3.1.2.3:进入容器测试文件读写:

root@docker-server1:~# docker exec -it 2e90299a3916 bash
[root@2e90299a3916 /]# echo "test" >> /apps/tomcat/bin/catalina.sh 
bash: /apps/tomcat/bin/catalina.sh: Read-only file system
[root@2e90299a3916 /]#

3.1.2.3:如何一次挂载多个目录:

多个目录可以位于不同的目录下

root@docker-server1:~# mkdir /data/qinghai
root@docker-server1:~# echo "qinghai" >> /data/qinghai/index.html

# docker run -d --name web1 -p 8081:8080 -v /data/testapp/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/qinghai/:/apps/tomcat/webapps/testapp tomcatweb:app1
491b5acd8426d125dc747b6a90990513d655fac4573597bd13afb330ca1c2dd4

再启动一个容器,验证宿主机目录或文件是否共享:
~# docker run -d --name web2 -p 8082:8080 -v /data/testapp/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/magedu/:/apps/tomcat/webapps/testapp t
omcat-web:app2 b07b9c09c8cadca8a8ef9e4e5c3969f3713a47280e9e5608fb8bd4e11ec4512 8

3.1.2.4:验证web 访问:

3.1.2.5:数据卷使用场景:

1、日志输出
2、静态web 页面
3、应用配置文件
4、多容器间目录或文件共享

3.1.3:数据卷容器:

数据卷容器功能是可以让数据在多个docker 容器之间共享,即可以让B 容器访问A 容器的内容,而容器C 也可以访问A 容器的内容,即先要创建一个后台运行的容器作为Server,用于卷提供,这个卷可以为其他容器提供数据存储服务,其他使用此卷的容器作为client 端:

3.1.3.1:启动一个卷容器Server:

先启动一个容器,并挂载宿主机的数据目录:
将宿主机的catalina.sh 启动脚本和magedu 的web 页面,分别挂载到卷容器
server 端,然后通过server 端共享给client 端使用。

# docker rm -fv web1 web2
# docker run -d --name volume-server -v /data/testapp/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v  /data/myapp2:/apps/tomcat/webapps/myapp2 tomcat-web:app2
33bc02c22cae0261731e0aa24ae2c663322b7888ed0ba5958b91f71616df19c4

3.1.3.2:启动两个端容器Client:

# docker run -d --name web1 -p 8801:8080 --volumes-from volume-server tomcat-web:app1
# docker run -d --name web2 -p 8802:8080 --volumes-from volume-server tomcat-web:app2
ac657e2cd03e12e57b6b093d563120279556138018e0bbc9655dda59507fed46

3.1.3.3:分别进入容器测试读写:

读写权限依赖于源数据卷Server 容器

3.1.3.4:测试访问web 页面:

3.1.3.5:验证宿主机数据:

3.1.3.4:关闭卷容器Server 测试能否启动新容器:

# docker stop volume-server
volume-server
#测试停止完成之后能否创建新的容器,停止volume server 是可以创建新容器的。
# docker run -d --name web3 -p 8803:8080 --volumes-from volume-server tomcat-web:app2
1ab11ec011668bb04225702961bb1a0b520a47a7de49134cd7b301cffeaaf5dd

#停止volume server 可以创建新容器

image-20211224152301409

3.1.3.6:测试删除源卷容器Server 创建容器:

将volume server 删除,然后在测试能否创建基于volume server 的新容器。

# docker rm -fv volume-server
volume-server

# docker run -d --name web4 -p 8804:8080 --volumes-from volume-server tomcat-web:app2
docker: Error response from daemon: No such container: volume-server. See 'docker run --help'.

3.1.3.7:测试之前的容器是否正常:

已经运行的容器不受任何影响

3.1.3.8:重新创建容器卷Server:

# docker run -d --name volume-server -v /data/testapp/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/qinghai:/apps/tomcat/webapps/qinghai tomcat-web:app2 
4cacb3a92de9ec7db948c41db04853793fdab50a2d70d37d31b4d55a7463ce2b

#创建出volume server 之后,就可以创建基于volume server 的新容器。
# docker run -d --name web4 -p 8804:8080 --volumes-from volume-server tomcat-web:app2 
244908c41af7fa709b96bbcb95388da1f138c4b7bbbf822ba3ed79929a1cd8b9

#在当前环境下,即使把提供卷的容器Server 删除,已经运行的容器Client 依然可以使用挂载的卷,因为容器是通过挂载访问数据的,但是无法创建新的卷容器客户端,但是再把卷容器Server 创建后即可正常创建卷容器Client,此方式可以用于线上共享数据目录等环境,因为即使数据卷容器被删除了,其他已经运行的容器依然可以挂载使用

#数据卷容器可以作为共享的方式为其他容器提供文件共享,类似于NFS 共享,可以在生产中启动一个实例挂载本地的目录,然后其他的容器分别挂载此容器的目录,即可保证各容器之间的数据一致性。

四:网络部分:

主要介绍docker 网络相关知识。
Docker 服务安装完成之后,默认在每个宿主机会生成一个名称为docker0 的网卡其IP 地址都是172.17.0.1/16,并且会生成三种不能类型的网络,如下图:

image-20211224152615388

4.1:docker 结合负载实现网站高可用:

4.1.1:整体规划图:

下图为一个小型的网络架构图,其中nginx 使用docker 运行。

image-20211224152721321

4.1.2:安装并配置keepalived:

4.1.2.1: Server1 安装并配置:

[root@docker-server1 ~]# yum install keepalived –y
[root@docker-server1 ~]# cat /etc/keepalived/keepalived.conf
vrrp_instance MAKE_VIP_INT {
	state MASTER
	interface eth0
	virtual_router_id 1
	priority 100
	advert_int 1
	unicast_src_ip 192.168.10.205
	unicast_peer {
		192.168.10.206
	}

	authentication {
		auth_type PASS
		auth_pass 1111
	}
	virtual_ipaddress {
		192.168.10.100/24 dev eth0 label eth0:1
	}
}

[root@docker-server1~]# systemctl restart keepalived && systemctl enable keepalived

4.1.2.2:Server2 安装并配置:

[root@docker-server2 ~]# yum install keepalived –y
[root@docker-server2 ~]# cat /etc/keepalived/keepalived.conf
vrrp_instance MAKE_VIP_INT {
	state BACKUP
	interface eth0
	virtual_router_id 1
	priority 50
	advert_int 1
	unicast_src_ip 192.168.10.206
	unicast_peer {
		192.168.10.205
	}
	authentication {
		auth_type PASS
		auth_pass 1111
	}
	virtual_ipaddress {
		192.168.10.100/24 dev eth0 label eth0:1
	}
}
[root@docker-server2 ~]# systemctl restart keepalived && systemctl enable keepalived

4.1.3:安装并配置haproxy:

4.1.3.1.:各服务器配置内核参数:

[root@docker-server1 ~]# sysctl -w net.ipv4.ip_nonlocal_bind=1
[root@docker-server2 ~]# sysctl -w net.ipv4.ip_nonlocal_bind=1

4.1.3.2:Server1 安装并配置haproxy:

[root@docker-server1 ~]# yum install haproxy –y
[root@docker-server1 ~]# cat /etc/haproxy/haproxy.cfg
global
maxconn 100000
uid 99
gid 99
daemon
nbproc 1
log 127.0.0.1 local0 info

defaults
option http-keep-alive
#option forwardfor
maxconn 100000
mode tcp
timeout connect 500000ms
timeout client 500000ms
timeout server 500000ms

listen stats
	mode http
	bind 0.0.0.0:9999
	stats enable
	log global
	stats uri /haproxy-status
	stats auth haadmin:q1w2e3r4ys

#================================================================
frontend docker_nginx_web
	bind 192.168.10.100:80
	mode http
	default_backend docker_nginx_hosts

backend docker_nginx_hosts
	mode http
	#balance source
	balance roundrobin
	server 192.168.10.205 192.168.10.205:81 check inter 2000 fall 3 rise 5
	server 192.168.10.206 192.168.10.206:81 check inter 2000 fall 3 rise 5

4.1.3.3:Server2 安装并配置haproxy:

[root@docker-server2 ~]# yum install haproxy –y
[root@docker-server2 ~]# cat /etc/haproxy/haproxy.cfg
global
maxconn 100000
uid 99
gid 99
daemon
nbproc 1
log 127.0.0.1 local0 info

defaults
option http-keep-alive
#option forwardfor
maxconn 100000
mode tcp
timeout connect 500000ms
timeout client 500000ms
timeout server 500000ms

listen stats
	mode http
	bind 0.0.0.0:9999
	stats enable
	log global
	stats uri /haproxy-status
	stats auth haadmin:123456
#================================================================
frontend docker_nginx_web
	bind 192.168.10.100:80
	mode http
	default_backend docker_nginx_hosts

backend docker_nginx_hosts
	mode http
	#balance source
	balance roundrobin
	server 192.168.10.205 192.168.10.205:81 check inter 2000 fall 3 rise 5
	server 192.168.10.206 192.168.10.206:81 check inter 2000 fall 3 rise 5

4.1.3.4:各服务器别分启动haproxy:

[root@docker-server1 ~]# systemctl enable haproxy
Created symlink from /etc/systemd/system/multi-user.target.wants/haproxy.service to /usr/lib/systemd/system/haproxy.service.

[root@docker-server1 ~]# systemctl restart haproxy
[root@docker-server2 ~]# systemctl enable haproxy
Created symlink from /etc/systemd/system/multi-user.target.wants/haproxy.service to /usr/lib/systemd/system/haproxy.service.

[root@docker-server2 ~]# systemctl restart haproxy

4.1.4:服务器启动nginx 容器并验证:

4.1.4.1:Server1 启动Nginx 容器:

从本地Nginx 镜像启动一个容器,并指定端口,默认协议是tcp 方式

[root@docker-server1 ~]# docker rm -f `docker ps -a -q` #先删除之前所有的容器
[root@docker-server1 ~]# docker run --name nginx-web1 -d -p 81:80 jack/nginx-1.10.3:v1 nginx
5410e4042f731d2abe100519269f9241a7db2b3a188c6747b28423b5a584d020

4.1.4.2:验证端口:

image-20211224153618831

4.1.4.3:验证web 访问:

image-20211224153643823

4.1.4.3:Server2 启动nginx 容器:

[root@docker-server2 ~]# docker run --name nginx-web1 -d -p 81:80 jack/nginx-1.10.3:v1 nginx 
84f2376242e38d7c8ba7fabf3134ac0610ab26358de0100b151df6a231a2b56a

4.1.4.4:验证端口:

image-20211224153743749

4.1.4.5:验证web 访问:

image-20211224153806147

4.1.4.6:访问VIP:

image-20211224153829800

4.1.4.7:Server1 haproxy 状态页面:

image-20211224153911071

4.1.4.8:Server2 haproxy 状态页面:

image-20211224153940347

日志可以在nginx 里面通过syslog 传递给elk 收集
指定IP、协议和端口:

[root@linux-docker ~]# docker run --name nginx-web -d -p 192.168.10.22:80:80/tcp jack/centos-nginx nginx
[root@linux-docker ~]# docker run --name nginx-web-udp -d -p 192.168.10.22:54:53/udp jack/centos-nginx nginx

4.2:容器之间的互联:

4.2.1:通过容器名称互联:

即在同一个宿主机上的容器之间可以通过自定义的容器名称相互访问,比如一个业务前端静态页面是使用nginx,动态页面使用的是tomcat,由于容器在启动的时候其内部IP 地址是DHCP 随机分配的,所以如果通过内部访问的话,自定义名称是相对比较固定的,因此比较适用于此场景。

#此方式最少需要两个容器之间操作:

4.2.1.1:先创建第一个容器,后续会使用到这个容器的名称:

# docker run -it -d --name tomcat-web1 -p 8801:8080 tomcat-web:app1 
96fd3426c786b032f252b709d4bb483590de8e57a99f401821634e4bd0045577

4.2.1.2:查看当前hosts 文件内容:

# docker exec -it 96fd3426c786 bash
[root@96fd3426c786 /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 96fd3426c786
1.1.1.1 abc.test.com

4.2.1.3:创建第二个容器:

# docker run -it -d -p 80:80 --name magedu-nginx-web1 --link tomcat-web1 magedu-nginx:v1 
e7796ad98c84d7e6148fd25e10c7026bdbe9a21fd5699995912340ab8906b9f c

4.2.1.4:查看第二个容器的hosts 文件内容:

# docker exec -it e7796ad98c84 bash
[root@e7796ad98c84 /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 tomcat-web1 96fd3426c786 #连接的对方容器的ID 和容器名称
172.17.0.3 e7796ad98c84

4.2.1.5:检测通信:

image-20211224154353282

4.2.2:通过自定义容器别名互联:

上一步骤中,自定义的容器名称可能后期会发生变化,那么一旦名称发生变化,程序之间也要随之发生变化,比如程序通过容器名称进行服务调用,但是容器名称发生变化之后再使用之前的名称肯定是无法成功调用,每次都进行更改的话又比较麻烦,因此可以使用自定义别名的方式解决,即容器名称可以随意更,只要不更改别名即可,具体如下:
命令格式:
docker run -d --name 新容器名称 --link 目标容器名称:自定义的名称 -p本地端口:容器端口 镜像名称 shell 命令

4.2.2.1:启动第三个容器:

# docker run -it -d -p 81:80 --name qinghai-nginx-web2 --link tomcat-web1:java_server qinghai-nginx:v1 
6acb8a2b366ec31045b19f9c5a00bd7e811d95c5c46202aec09f140bf3420508

4.2.2.2:查看当前容器的hosts 文件:

root@docker-server1:~# docker exec -it 6acb8a2b366e bash
[root@6acb8a2b366e /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 java_server 96fd3426c786 tomcat-web1
172.17.0.4 6acb8a2b366

4.2.2.3:检查自定义别名通信:

image-20211224154628156

4.2.3.1:docker 网络类型:

Docker 的网络使用docker network ls 命令看到有三种类型,下面将介绍每一种类型的具体工作方式:
Bridge 模式,使用参数–net=bridge 指定,不指定默认就是bridge 模式。 #查看当前docke 的网卡信息:

root@docker-server1:~# docker network list

Bridge:#桥接,使用自定义IP
Host:#不获取IP 直接使用物理机IP,并监听物理机IP 监听端口
None: #没有网络
4.2.3.1.1:Host 模式:

Host 模式,使用参数–net=host 指定。
启动的容器如果指定了使用host 模式,那么新创建的容器不会创建自己的虚拟网卡,而是直接使用宿主机的网卡和IP 地址,因此在容器里面查看到的IP 信息就是宿主机的信息,访问容器的时候直接使用宿主机IP+容器端口即可,不过容器的其他资源们必须文件系统、系统进程等还是和宿主机保持隔离。
此模式的网络性能最高,但是各容器之间端口不能相同,适用于运行容器端口比较固定的业务。
为避免端口冲突,先删除所有的容器:
先确认宿主机端口没有占用80 端口:

image-20211224154921034

#启动一个新容器,并指定网络模式为host

# docker run -d --name net_host --net=host qinghai-nginx:v1 
7d65d6106ca87d41b6c62677740a1cdd14a870234f7c15d0beac6b306583cff 8

#验证网络信息:

image-20211224155040986

#访问宿主机验证:

Host 模式不支持端口映射,当指定端口映射的时候会提示如下警告信息:
使用主机网络模式时,将丢弃已指定的端口:

# docker run -it -d --name net_host -p 80:80 --net=host qinghai-nginx:v1 /apps/nginx/sbin/nginx
WARNING: Published ports are discarded when using host network mode 025ff64d057f095032ac6c271d5275d81cea1f73645e93877c3d696cb2280020
4.2.3.1.2:none 模式:

None 模式,使用参数–net=none 指定
在使用none 模式后,Docker 容器不会进行任何网络配置,其没有网卡、没有
IP 也没有路由,因此默认无法与外界通信,需要手动添加网卡配置IP 等,所以极少使用,
命令使用方式:

# docker run -it -d --name net_none -p 80:80 --net=none qinghai-nginx:v1 /apps/nginx/sbin/nginx 
7d7125a2a53e0af9718e6f7b3407b093a1367499188d5493b8c4b4a56dfff584

image-20211224155231912

4.2.3.1.3:Container 模式:

Container 模式,使用参数–net=container:名称或ID 指定。
使用此模式创建的容器需指定和一个已经存在的容器共享一个网络,而不是和宿主机共享网,新创建的容器不会创建自己的网卡也不会配置自己的IP,而是和一个已经存在的被指定的容器东西IP 和端口范围,因此这个容器的端口不能和被指定的端口冲突,除了网络之外的文件系统、进程信息等仍然保持相互隔离,两个容器的进程可以通过lo 网卡及容器IP 进行通信。

# docker rm -fv `docker ps -a -q`
# docker run -it -d --name nginx-web1 -p 80:80 --net=bridge qinghai-nginx:v1 /apps/nginx/sbin/nginx 
8d6950bcf89f7744b2cc3d53733b4561f4c9e2ec2fb735f75972bedb0a4eb79a
# docker run -it -d --name tomcat-web1 --net=container:nginx-web1 tomcat-web:app1 #直接使用对方的网络,此方式较少使用
bf77a272fe2b88b1888b321c96c95d6a27da8d5297366d6965ac1e3e265600 83

image-20211224155345936

4.2.3.1.4:bridge 模式:

docker 的默认模式即不指定任何模式就是bridge 模式,也是使用比较多的模式,
此模式创建的容器会为每一个容器分配自己的网络IP 等信息,并将容器连接到一个虚拟网桥与外界通信。

image-20211224155423004

[root@docker-server1 ~]# docker network inspect bridge

image-20211224155505416

# docker rm -fv `docker ps -a -q`
# docker run -it -d --name nginx-web1 -p 80:80 --net=bridge qinghai-nginx:v1 /apps/nginx/sbin/nginx 
0dfe5a96ef1b7c8f0af820a097a4b140aed8809a787562c694d4982d8f0037b9

4.2.3.2:docker 夸主机互联之简单实现:

夸主机互联是说A 宿主机的容器可以访问B 主机上的容器,但是前提是保证各宿主机之间的网络是可以相互通信的,然后各容器才可以通过宿主机访问到对方的容器,实现原理是在宿主机做一个网络路由就可以实现A 宿主机的容器访问B主机的容器的目的,复杂的网络或者大型的网络可以使用google 开源的k8s 进行互联。

4.2.3.2.1:修改各宿主机网段:

Docker 的默认网段是172.17.0.x/24,而且每个宿主机都是一样的,因此要做路由的前提就是各个主机的网络不能一致,具体如下:
为避免影响,先在各服务器删除之前穿件的所有容器。

# docker rm -f `docker ps -a -q`
4.2.3.2.2:服务器A 更改网段:
# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.10.0.1/24

或者:vim /etc/docker/daemon.json

  "default-address-pools": [
    {
      "base": "10.10.0.0/16",
      "size": 24
    }
  ],

image-20211224155843358

image-20220106145221307

4.2.3.2.3:重启docker 服务并验证网卡:
root@docker-server1:~# systemctl daemon-reload 
root@docker-server1:~# systemctl restart docker

验证A 服务器网卡:

image-20211224155927329

4.2.3.2.4:服务器B 更改网段:
# vim /lib/systemd/system/docker.service
--bip=10.20.0.1/24
# systemctl daemon-reload
# systemctl restart docker
4.2.3.2.5:验证网卡:

image-20211224160023082

4.2.3.3:在两个宿主机分别启动一个容器:

#Server1:
root@docker-server1:~# docker run -it -p 8080:8080 tomcat-web:app1 bash
[root@781e7f053c20 /]# ifconfig


#Server2:
root@docker-node2:~# docker run -it -p 8080:8080 tomcat-web:app2 bash 
[root@1ee02ccc1810 /]# ifconfig

4.2.3.4:添加静态路由:

在各宿主机添加静态路由,网关指向对方的IP:

3.2.3.4.1: Server1 添加静态路由:
# route add -net 10.20.0.0/24 gw 192.168.7.102
# iptables -A FORWARD -s 192.168.0.0/21 -j ACCEPT

#ping 对方容器IP 是否通信:

image-20211224160218585

4.2.3.4.2:server2 添加静态路由:
[root@docker-server2 ~]# route add -net 172.16.10.0/24 gw 192.168.10.205 
[root@docker-server2 ~]# iptables -A FORWARD -s 192.168.0.0/21 -j ACCEPT

#ping 对方容器IP 是否通信:

image-20211224160314680

4.2.3.4.3:抓包分析
[root@linux-docker2 ~]# tcpdump -i eth0 -vnn icmp

image-20211224160353396

4.2.3.5:测试容器间互联:

4.2.3.5.1:宿主机A 到宿主机B 容器测试:

image-20211224160431331

4.2.3.5.2:宿主机B 到宿主机A 容器测试:

image-20211224160503225

4.3:创建自定义网络:

可以基于docker 命令创建自定义网络,自定义网络可以自定义IP 地范围和网关等信息。

4.3.1:创建自定义docker 网络:

# docker network create –help
# docker network create -d bridge --subnet 10.100.0.0/24 --gateway 10.100.0.1 qinghai-net #创建自定义网络qinghai-net
954c8bda9a8c35cd8e9e76159ef04f29f89f054e72f29e92cc5fc4a7be1cf6da

验证网络:
# docker network list

4.3.2:创建不同网络的容器测试通信:

4.3.2.1:使用自定义网络创建容器:

root@docker-server1:~# docker run -it -p 8080:8080 --name qinghai-net-test --net=qinghai-net tomcat-web:app1 bash
[root@42dba1061dd1 /]# ifconfig

[root@42dba1061dd1 /]# ping www.5212345.cn
PING www.5212345.cn (101.200.188.230) 56(84) bytes of data.

4.3.2.2:创建默认网络容器:

root@node1:~# docker run -it --name bridge-container-test -p 8081:8080 tomcat-web:app1 bash
[root@ed06c2785c92 /]# ifconfig

[root@ed06c2785c92 /]# ping www.5212345.cn
PING www.5212345.cn (101.200.188.230) 56(84) bytes of data.

4.3.3:当前iptables 规则:

4.3.4:如何与使用默认网络的容器通信:

现在有一个docker0(10.10.0.0/24) 网络一个自定义的qinghai-net(10.100.0.0/24)网络,每个网络上分别运行了不同数量的容器,那
么怎么才能让位于不同网络的容器可以相互通信呢?

# iptables-save > iptables.sh

4.3.5:重新导入iptables 并验证通信:

#重新导入iptables 规则:
# iptables-restore < iptables.sh

image-20211224161134286

五:Docker 仓库之单机Docker Registry:

Docker Registry 作为Docker 的核心组件之一负责镜像内容的存储与分发,客户端的docker pull 以及push 命令都将直接与registry 进行交互,最初版本的registry 由Python 实现,由于设计初期在安全性,性能以及API 的设计上有着诸多的缺陷,该版本在0.9 之后停止了开发,由新的项目distribution(新的docker register 被称为Distribution)来重新设计并开发下一代registry,新的项目由go语言开发,所有的API,底层存储方式,系统架构都进行了全面的重新设计已解决上一代registry 中存在的问题,2016 年4 月份rgistry 2.0 正式发布,docker 1.6版本开始支持registry 2.0,而八月份随着docker 1.8 发布,docker hub 正式启用2.1 版本registry 全面替代之前版本registry,新版registry 对镜像存储格式进行了重新设计并和旧版不兼容,docker 1.5 和之前的版本无法读取2.0 的镜像,另外,Registry 2.4 版本之后支持了回收站机制,也就是可以删除镜像了,在2.4版本之前是无法支持删除镜像的,所以如果你要使用最好是大于Registry 2.4 版本的,目前最新版本为2.7.x。
官方文档地址:https://docs.docker.com/registry/
官方github 地址:https://github.com/docker/distribution
本部分将介绍通过官方提供的docker registry 镜像来简单搭建一套本地私有仓库环境.

5.1:下载docker registry 镜像:

了解即可,常用的是后续的harbor

[root@docker-server1 ~]# docker pull registry:2.6.2

5.2:搭建单机仓库:

5.2.1:创建授权使用目录:

[root@docker-server1 ~]# mkdir /docker/auth #创建一个授权使用目录

5.2.2:创建用户:

[root@docker-server1 ~]# cd /docker
[root@docker-server1 docker]# docker run --entrypoint htpasswd registry:2.6.2 -Bbn user 123456 > auth/htpasswd #创建一个用户并生成密码,2.6.2以后的版本,执行这个命令会报错

5.2.3:验证用户名密码:

[root@docker-server1 docker]# cat auth/htpasswd 
user:$2y$05$n4.RJtw4UCn30BkkJCABwe.G/YwQDaf8QVT2TkM7FC3qFYLRTE/gm

5.2.4:启动docker registry:

[root@docker-server1 docker]# docker run -d -p 5000:5000 --restart=always --name registry1 -v /docker/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd registry:2.6.2
367bc060731d09064711a5733a8b0a69d388a3131213658392abfb77924d3b49

5.2.5:验证端口和容器:

image-20211230160322243

5.2.6:测试登录仓库:

5.2.6.1:报错如下:

image-20211230160416809

5.2.6.2:解决方法:

编辑各docker 服务器/etc/sysconfig/docker 配置文件如下:

############################# 第一种解决办法 #############################
[root@docker-server1 ~]# vim /lib/systemd/system/docker.service

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.206.102:5000 # 这一行的最后添加上"--insecure-registry 192.168.206.102:5000"
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

[root@docker-server1 ~]# systemctl daemon-reload
[root@docker-server2 ~]# systemctl restart docker

############################# 第二种解决办法 #############################
vim /etc/docker/daemon.json
...
"insecure-registries":["192.168.206.102:5000"],
...

image-20211230161841787

5.2.6.3:验证各docker 服务器登录:

#server1:

image-20211230163020389

#server2:

image-20211230163004442

5.2.7:在Server1 登录后上传镜像:

5.2.7.1:镜像打tag:

[root@docker-server1 ~]# docker tag harbor.12345.com/m43/nginx-all-in-one:1.16.1 192.168.206.102:5000/web/nginx-all-in-one:1.16.1

5.2.7.2:上传镜像:

image-20211230163704891

5.2.8:Server 2 下载镜像并启动容器:

5.2.8.1:登录并从docker registry 下载镜像:

[root@docker-server2 ~]# docker images

[root@docker-server2 ~]# docker pull 192.168.206.102:5000/web/nginx-all-in-one:1.16.1

5.2.8.2:验证镜像下载成功:

image-20211230165859654

5.2.8.3:从下载的镜像启动容器:

[root@docker-server2 ~]# docker run -d --name docker-registry -p 80:80 192.168.206.102:5000/web/nginx-all-in-one:1.16.1
2ba24f28362e1b039fbebda94a332111c2882aa06987463ae033c630f5c9927c

5.2.8.4:访问测试:

image-20211224162026505

六:docker 仓库之分布式Harbor

Harbor 是一个用于存储和分发Docker 镜像的企业级Registry 服务器,由vmware 开源,其通过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源Docker Distribution。作为一个企业级私有Registry 服务器,Harbor提供了更好的性能和安全。提升用户使用Registry 构建和运行环境传输镜像的效率。Harbor 支持安装在多个Registry 节点的镜像资源复制,镜像全部保存在私有Registry 中, 确保数据和知识产权在公司内部网络中管控,另外,Harbor也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等。

vmware 官方开源服务列表地址:https://vmware.github.io/harbor/cn/,
harbor 官方github 地址:https://github.com/vmware/harbor
harbor 官方网址:https://goharbor.io/

image-20211224162130990

6.1:Harbor 功能官方介绍:

基于角色的访问控制:用户与Docker 镜像仓库通过“项目”进行组织管理,一个用户可以对多个镜像仓库在同一命名空间(project)里有不同的权限。

镜像复制:镜像可以在多个Registry 实例中复制(同步)。尤其适合于负载均衡,高可用,混合云和多云的场景。
图形化用户界面:用户可以通过浏览器来浏览,检索当前Docker 镜像仓库,管理项目和命名空间。
AD/LDAP 支持:Harbor 可以集成企业内部已有的AD/LDAP,用于鉴权认证管理。
审计管理:所有针对镜像仓库的操作都可以被记录追溯,用于审计管理。
国际化:已拥有英文、中文、德文、日文和俄文的本地化版本。更多的语言将会添加进来。
RESTful API - RESTful API :提供给管理员对于Harbor 更多的操控, 使得与其它管理软件集成变得更容易。
部署简单:提供在线和离线两种安装工具, 也可以安装到vSphere 平台(OVA方式)虚拟设备。

nginx:harbor 的一个反向代理组件,代理registry、ui、token 等服务。这个代理会转发harbor web 和docker client 的各种请求到后端服务上。
harbor-adminserver:harbor 系统管理接口,可以修改系统配置以及获取系统信息。
harbor-db:存储项目的元数据、用户、规则、复制策略等信息。
harbor-jobservice:harbor 里面主要是为了镜像仓库之前同步使用的。
harbor-log:收集其他harbor 的日志信息。
harbor-ui:一个用户界面模块,用来管理registry。
registry:存储docker images 的服务,并且提供pull/push 服务。
redis:存储缓存信息
webhook:当registry 中的image 状态发生变化的时候去记录更新日志、复制等操作。
token service:在docker client 进行pull/push 的时候负责token 的发放。

6.2:安装Harbor:

官方网站:https://goharbor.io

下载地址:https://github.com/goharbor/harbor/releases

安装文档:https://goharbor.io/docs/2.4.0/

6.2.1:服务器1 安装docker:

本次使用当前harbor 最新的稳定版本2.4.1 离线安装包, 具体名称为harbor-offline-installer-v2.4.1.tgz

[root@docker-server1 ~]# yum install docker -y
[root@docker-server1 ~]# systemctl satrt docker
[root@docker-server1 ~]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

6.2.2:服务器2 安装docker:

[root@docker-server2 ~]# yum install docker -y
[root@docker-server2 ~]# systemctl start docker
[root@docker-server2 ~]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

6.2.3:下载Harbor 安装包:

6.2.3.1:下载离线完整安装包:

#推荐使用离线完整安装包

[root@docker-server2 ~]# cd /usr/local/src/
[root@docker-server2 src]# wget https://github.com/goharbor/harbor/releases/download/v2.4.1/harbor-offline-installer-v2.4.1.tgz

6.2.3.2:下载在线安装包

#不是很推荐此方式

[root@docker-server2 src]# wget https://github.com/goharbor/harbor/releases/download/v2.4.1/harbor-online-installer-v2.4.1.tgz

6.3:配置Harbor:

6.3.1: 解压并编辑harbor.yml

root@harbor-server1:/usr/local/src# tar xf harbor-offline-installer-v2.4.1.tgz
root@harbor-server1:/usr/local/src# cd harbor/
root@harbor-server1:/usr/local/src/harbor# cp harbor.yml.tmpl  harbor.yml
root@harbor-server1:/usr/local/src/harbor# mkdir -p /data/harbordata  # 准备数据目录
root@harbor-server1:/usr/local/src/harbor# vim harbor.yml
hostname: harbor.12345.com	# 修改为主机名或者ip地址,如果是主机名,需要dns能解析
#https:  # 注释https的配置
harbor_admin_password: 123456	# 登录密码
data_volume: /data/harbordata  # 存储数据的目录

安装

./install.sh --with-trivy # 新版本安装
# --with-trivy 开启镜像扫描功能,新版本才支持的参数 。Harbor与Clair集成,添加漏洞扫描功能。 默认在运行harbor时漏洞扫描是没有开启的,需要重新开启并添加参数
# –-with-chartmuseum: 支持chart仓库服务
# --with-clair 老版本开启镜像扫描功能(2.2版本之前),新版本已经淘汰

以下均为老版本配置

解压并编辑harbor.cfg(老版本是harbor.cfg)

[root@docker-server1 src]# tar xvf harbor-offline-installer-v1.7.5.tgz
[root@docker-server1 src]# ln -sv /usr/local/src/harbor /usr/local/
‘/usr/local/harbor’ -> ‘/usr/local/src/harbor’
[root@docker-server1 harbor]# cd /usr/local/harbor/
[root@docker-server1 harbor]# yum install python-pip –y
[root@docker-server1 harbor]# docker-compose start
[root@docker-server1 harbor]# vim harbor.cfg
[root@docker-server1 harbor]# grep "^[a-Z]" harbor.cfg
hostname = 192.168.206.104
ui_url_protocol = http
db_password = root123
max_job_workers = 3
customize_crt = on
ssl_cert = /data/cert/server.crt
ssl_cert_key = /data/cert/server.key
secretkey_path = /data
admiral_url = NA
clair_db_password = password
email_identity = harbor
email_server = smtp.163.com
email_server_port = 25
email_username = rooroot@163.com
email_password = zhang@123
email_from = admin <rooroot@163.com>
email_ssl = false
harbor_admin_password = zhang@123
auth_mode = db_auth
ldap_url = ldaps://ldap.mydomain.com
ldap_basedn = ou=people,dc=mydomain,dc=com
ldap_uid = uid
ldap_scope = 3
ldap_timeout = 5
self_registration = on
token_expiration = 30
project_creation_restriction = everyone
verify_remote_cert = on

6.3.2:更新harbor 配置:

6.3.2.1:首次部署harbor 更新:

[root@docker-server1 harbor]# pwd
/usr/local/harbor #在harbor 当前目录执行
[root@docker-server1 harbor]# ./prepare #更新配置

image-20211224162707407

#执行完毕后会在当前目录生成一个docker-compose.yml 文件,用于配置数据目录等配置信息:

6.3.2.2:后期修改配置:

如果harbor 运行一段时间之后需要更改配置,则步骤如下:

6.3.2.2.1:停止harbor:
[root@docker-server1 harbor]# pwd
/usr/local/harbor #harbor 的当前目录
[root@docker-server1 harbor]# docker-compose stop
6.3.2.2.2:编辑harbor.cfg 进行相关配置:
[root@docker-server1 harbor]# vim harbor.cfg
6.3.2.2.3:更新配置:

[root@docker-server1 harbor]# ./prepare

image-20211224162900125

6.3.2.2.3:启动harbor 服务:

[root@docker-server1 harbor]# docker-compose start

image-20211224162940170

6.3.3:官方方式启动Harbor:

6.3.3.1:官方方式安装并启动harbor:

[root@docker-server1 harbor]# yum install python-pip
[root@docker-server1 harbor]# pip install --upgrade pip
[root@docker-server1 harbor]# pip install docker-compose
[root@docker-server1 harbor]# ./install.sh #官方构建harbor 和启动方式,推
荐此方法,会下载官方的docker 镜像:

6.3.3.2:部署过程中:

image-20211224163039318

6.3.3.3:部署完成:

image-20211224163105306

6.3.3.4:查看本地的镜像:

image-20211230222314922

6.3.3.5:查看本地端口:

image-20211230222352507

6.3.3.6:web 访问Harbor 管理界面:

image-20211224163218449

6.3.3.7:登录成功后的界面:

image-20211224163244480

6.3.4:非官方方式启动:

老版本方式

6.3.4.1:非官方方式启动harbor:

[root@docker-server2 harbor]# ./prepare
[root@docker-server2 harbor]# yum install python-pip -y
[root@docker-server2 harbor]# pip install --upgrade pip #升级pip 为最新版本
[root@docker-server2 harbor]# pip install docker-compose #安装
docker-compose 命令

image-20211224163326692

6.3.4.2:启动harbor:

[root@docker-server2 harbor]# docker-compose up –d #非官方方式构建容器,此步骤会从官网下载镜像,需要相当长的时间
#执行过程如下:

image-20211224163412469

6.4:配置docker 使用harbor 仓库上传下载镜像:

6.4.1:编辑docker 配置文件:

注意:如果我们配置的是https 的话,本地docker 就不需要有任何操作就可以访问harbor 了

[root@docker-server1 ~]# vim /etc/docker/daemon.json
"insecure-registries": ["192.168.206.102:5000","harbor.12345.com"],

#其中harbor.12345.com 是我们部署Harbor 的地址,即hostname 配置项值。配置完后需要重启docker 服务。

6.4.2:重启docker 服务:

[root@docker-server1 ~]# systemctl stop docker
[root@docker-server1 ~]# systemctl start docker

6.4.3:验证能否登录harbor:

[root@docker-server1]# docker login harbor.12345.com

6.4.4:测试上传和下载镜像:

将之前单机仓库构构建的Nginx 镜像上传到harbor 服务器用于测试

6.4.4.1:导入镜像:

[root@docker-server1 harbor]# docker load < /opt/nginx-1.10.3_docker.tar.gz

6.4.4.2:验证镜像导入成功

6.4.4.3:镜像打tag:

#修改images 的名称,不修改成指定格式无法将镜像上传到harbor 仓库,格式为: Harbor IP/项目名/image 名字:版本号:

[root@docker-server1 harbor]# docker tag 192.168.206.102:5000/web/nginx-all-in-one:1.16.1  harbor.12345.com/nginx/nginx-all-in-one:1.16.1
[root@docker-server1 harbor]# docker images

image-20211230225403265

6.4.4.4:在harbor 管理界面创建项目:

image-20211230224841429

6.4.4.4:将镜像push 到harbor:

#格式为:docker push 镜像名:版本

image-20211230225037406

6.4.4.5:push 完成

image-20211230225116171

6.4.4.6:harbor 界面验证镜像上传成功:

image-20211230225154243

6.4.4.7:验证镜像信息:

image-20211230225258469

image-20211230225509042

6.4.5:验证从harbor 服务器下载镜像并启动容器:

6.4.5.1:更改docker 配置文件:

目前凡是需要从harbor 镜像服务器下载image 的docker 服务都要更改,不更改的话无法下载:

[root@docker-server2 ~]# vim /etc/docker/daemon.json
"insecure-registries": ["192.168.206.102:5000","harbor.12345.com"],

6.4.5.2:重启docker:

[root@docker-server2 ~]# systemctl stop docker
[root@docker-server2 ~]# systemctl start docker

6.4.5.3:验证从harbor 下载镜像:

6.4.5.5.1:查看下载命令:

#harbor 上的每个镜像里面自带pull 命令

image-20211230225711076

6.4.5.5.2:执行下载:
[root@docker-server2 ~]# docker pull harbor.12345.com/nginx/nginx-all-in-one:1.16.1

image-20211230230233678

6.4.5.5.3:验证镜像下载完成:

image-20211230230416756

6.4.6:从镜像启动容器并验证:

6.4.6.1:启动容器:

[root@docker-server2 ~]# docker run -d -p 80:80 -p 443:443 harbor.12345.com/nginx/nginx-all-in-one:1.16.1
89901f9badf74809f6abccc352fc7479f1490f0ebe6d6e3b36d689e73c3f9027

6.4.6.2:验证端口:

image-20211224164240862

6.4.6.3:验证web 访问:

image-20211224164303401

6.5:实现harbor 高可用:

image-20211224164331020

Harbor 支持基于策略的Docker 镜像复制功能,这类似于MySQL 的主从同步,其可以实现不同的数据中心、不同的运行环境之间同步镜像,并提供友好的管理界面,大大简化了实际运维中的镜像管理工作,已经有用很多互联网公司使用harbor 搭建内网docker 仓库的案例,并且还有实现了双向复制的案列,本文将实现单向复制的部署:

6.5.1:新部署一台harbor 服务器:

[root@docker-server2 ~]# cd /usr/local/src/
[root@docker-server2 src]# tar xf harbor-offline-installer-v1.7.5.tgz
[root@docker-server2 src]# ln -sv /usr/local/src/harbor /usr/local/
‘/usr/local/harbor’ -> ‘/usr/local/src/harbor’
[root@docker-server2 src]# cd /usr/local/harbor/
[root@docker-server2 harbor]# grep "^[a-Z]" harbor.cfg
hostname = 192.168.10.206
ui_url_protocol = http
db_password = root123
max_job_workers = 3
customize_crt = on
ssl_cert = /data/cert/server.crt
ssl_cert_key = /data/cert/server.key
secretkey_path = /data
admiral_url = NA
clair_db_password = password
email_identity = harbor-1.7.5
email_server = smtp.163.com
email_server_port = 25
email_username = rooroot@163.com
email_password = zhang@123
email_from = admin <rooroot@163.com>
email_ssl = false
harbor_admin_password = zhang@123
auth_mode = db_auth
ldap_url = ldaps://ldap.mydomain.com
ldap_basedn = ou=people,dc=mydomain,dc=com
ldap_uid = uid
ldap_scope = 3
ldap_timeout = 5
self_registration = on
token_expiration = 30
project_creation_restriction = everyone
verify_remote_cert = on
[root@docker-server2 harbor]# yum install python-pip -y
[root@docker-server2 harbor]# pip install --upgrade pip
[root@docker-server2 harbor]# pip install docker-compose
[root@docker-server2 harbor]# ./install.sh

image-20211224164437132

6.5.2:验证从harbor 登录:

image-20211224164503810

6.5.3:创建一个nginx 项目:

#与主harbor 项目名称保持一致:

image-20211224164601181

6.5.4:在主harbor 服务器配置同步测试:

image-20211224164623665

6.5.5:点击复制规则:

image-20211224164646769

6.5.6:主harbor 编辑同步策略:

image-20211224164711213

6.5.7:主harbor 查看镜像同步状态:

image-20211224164732656

6.5.8:从harbor 查看镜像:

image-20211224164753778

6.5.9:测试从harbor 镜像下载和容器启动:
6.5.9.1:docker 客户端配置使用harbor:
#本次新部署了一台docker 客户端,IP 地址为192.168.10.207

[root@docker-server3 ~]# vim /etc/sysconfig/docker
4 OPTIONS='--selinux-enabled --log-driver=journald --insecure-registry 192.168.10.206'

6.5.9.2:重启docker 服务:
[root@docker-server3 ~]# systemctl restart docker
6.5.9.3:从harbor 项目设置为公开:

image-20211224164849170

6.5.9.4:设置项目为公开访问:

image-20211224164914610

6.5.9.5:docker 客户端下载镜像

image-20211224164941297

6.5.9.6:docker 客户端从镜像启动容器:

[root@docker-server3 ~]# docker run -d -p 80:80 -p443:443 192.168.10.206/nginx/nginx_1.10.3:v1 nginx 
0b496bc81035291b80062d1fba7d4065079ab911c2a550417cf9e593d353c20b

6.5.9.7:验证web 访问:

image-20211224165041795

#至此,高可用模式的harbor 仓库部署完毕

6.6.:实现harbor 双向同步:

6.6.1:在docker 客户端导入centos 基础镜像:

[root@docker-server3 ~]# docker load -i /opt/centos.tar.gz
[root@docker-server3 ~]# vim /etc/sysconfig/docker
4 OPTIONS='--selinux-enabled --log-driver=journald --insecure-registry 192.168.10.206'

6.6.2:镜像打tag:

[root@docker-server3 ~]# docker tag docker.io/centos 192.168.10.206/nginx/centos_base

image-20211224165155877

6.6.3:上传到从harbor:

[root@docker-server3 ~]# docker push 192.168.10.206/nginx/centos_base

image-20211224165230838

6.6.4:从harbor 界面验证:

image-20211224165252906

6.6.5:从harbor 创建同步规则:

规则方式与主harbor 相同,写对方的IP+用户名密码,然后点测试连接,确认可以测试连接通过。

image-20211224165324409

6.6.6:到主harbor 验证镜像:

image-20211224165358732

6.6.7:docker 镜像端测试:

6.6.7.1:下载centos 基础镜像:

[root@docker-server1 harbor]# docker pull 192.168.10.205/nginx/centos_base
Using default tag: latest
Trying to pull repository 192.168.10.205/nginx/centos_base ...
sha256:822de5245dc5b659df56dd32795b08ae42db4cc901f3462fc509e91e97132dc0: Pulling from 192.168.10.205/nginx/centos_base
Digest: sha256:822de5245dc5b659df56dd32795b08ae42db4cc901f3462fc509e91e97132dc0

image-20211224165542100

6.6.7.2:从镜像启动容器:

[root@docker-server1 ~]# docker run -it --name centos_base 192.168.10.205/nginx/centos_base bash
[root@771f5aa0d089 /]#

6.7:harbor https 配置:

# openssl genrsa -out /usr/local/src/harbor/certs/harbor-ca.key 2048
# openssl req -x509 -new -nodes -key /usr/local/src/harbor/certs/harbor-ca.key -subj "/CN=harbor.123456.net" -days 7120 -out /usr/local/src/harbor/certs/harbor-ca.crt
# vim harbor.cfg
	hostname = harbor.magedu.net
	ui_url_protocol = https
	ssl_cert = /usr/local/src/harbor/certs/harbor-ca.crt
ssl_cert_key = /usr/local/src/harbor/certs/harbor-ca.key
harbor_admin_password = 123456

# ./install.sh
# yum install docker-ce-18.06.3.ce-3.el7.x86_64.rpm
# yum install docker-compose
# mkdir /etc/docker/certs.d/harbor.123456.net -p
# cp certs/harbor-ca.crt /etc/docker/certs.d/harbor.magedu.net/
# docker login harbor.123456.net

登录测试:

七:单机编排之Docker Compose:

当在宿主机启动较多的容器时候,如果都是手动操作会觉得比较麻烦而且容器出错, 这个时候推荐使用docker 单机编排工具docker-compose ,docker-compose 是docker 容器的一种单机编排服务,docker-compose 是一个管理多个容器的工具,比如可以解决容器之间的依赖关系,就像启动一个nginx前端服务的时候会调用后端的tomcat,那就得先启动tomcat,但是启动tomcat容器还需要依赖数据库,那就还得先启动数据库,docker-compose 就可以解决这样的嵌套依赖关系,其完全可以替代docker run 对容器进行创建、启动和停止。
docker-compose 项目是Docker 官方的开源项目,负责实现对Docker 容器集群的快速编排,docker-compose 将所管理的容器分为三层,分别是工程(project),服务(service)以及容器(container)。
#github 地址https://github.com/docker/compose

# cat docker-compose.yml
version: '3.6'
services:
	nginx-server:
		image: nginx
		container_name: nginx-web1
		expose:
			- 80
			- 443
		ports:
			- "80:80"
			- "443:443"

7.1:基础环境准备:

7.1.1:安装python-pip 软件包:

python-pip 包将安装一个pip 的命令,pip 命令是一个pyhton 安装包的安装工具,其类似于ubuntu 的apt 或者redhat 的yum,但是pip 只安装python 相关的安装包,可以在多种操作系统安装和使用pip。

Ubuntu:
# apt update
# apt install -y python-pip
Centos:
# yum install epel-release
# yum install -y python-pip
# pip install --upgrade pip

注:官方二进制下载地址:https://github.com/docker/compose/releases

7.1.2:安装docker compose:

# pip install docker-compose

7.1.3:验证docker-compose 版本:

# docker-compose version
docker-compose version 1.25.0, build b42d419
docker-py version: 4.1.0
CPython version: 2.7.15+
OpenSSL version: OpenSSL 1.1.1 11 Sep 2018

7.1.4:查看docker-compose 帮助:

https://docs.docker.com/compose/reference/ #官方文档

[root@docker-server3 ~]# docker-compose --help
Usage:
	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 文件目录执行
#build #通过docker-compose 构建镜像
#bundle #从当前docker compose 文件生成一个以当前目录为名称的从Compose 文件生成一个分布式应用程序捆绑包(DAB)。
config -q #查看当前配置,没有错误不输出任何信息
#create #创建服务
down #停止和删除所有容器、网络、镜像和卷
#events #从容器接收实时事件,可以指定json 日志格式,如
	docker-compose events --json
#exec #进入指定容器进行操作
help #显示帮助细信息
#images #显示当前服务器的docker 镜像信息
kill #强制终止运行中的容器
logs #查看容器的日志
#pause #暂停服务
#port #查看端口
	# docker-compose port --protocol=tcp nginx 80
ps #列出容器
pull #重新拉取镜像
#push #上传镜像
#restart #重启服务
rm #删除已经停止的服务
run #一次性运行容器,等于docker run --rm
scale #设置指定服务运行的容器个数
	docker-compose scale nginx=2
start #启动服务
stop #停止服务
top #显示容器运行状态
unpause #取消暂定
up #创建并启动容器
version #显示docker-compose 版本信息

7.2:从docker compose 启动单个容器:

目录可以在任意目录,推荐放在有意义的位置。

# cd /opt/
# mkdir qinghai
# cd qinghai/

7.2.1:单个容器的docker compose 文件:

编写一个yml 格式的配置docker-compose 文件,启动一个nginx 服务,由于格式为yml 格式,因此要注意前后的缩进及上下行的等级关系。

# pwd
/opt/qinghai

# cat docker-compose.yml
service-nginx-web:
	image: harbor.12345.com/m43/nginx-all-in-one:1.16.1-v2
	expose:
		- 80
		- 443
	ports:
		- "80:80"
		- "443:443"

7.2.2:启动容器:

必须要在docker compose 文件所在的目录执行:

# pwd
/opt/qinghai

# docker-compose up -d #不加是d 前台启动

7.2.3:启动完成:

镜像下载完成后将容器创建完成并成功运行。

image-20211224170355434

7.2.4:web 访问测试:

image-20211224170420548

7.2.5:后台启动服务:

#容器的在启动的时候,会给容器自定义一个名称,在service name 后面加_1。

# docker-compose up -d
# docker-compose ps

7.2.6:自定义容器名称:

# cat docker-compose.yml
service-nginx-web:
image: 192.168.7.103/linux37/ubuntu-nginx:1.16.1
container_name: nginx-web1
expose:
- 80
- 443
ports:
- "80:80"
- "443:443"
# docker-compose up -d
Recreating qinghai_service-nginx-web_1 ... done
# docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------
nginx-web1 nginx Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

7.2.7:验证容器:

7.2.8:查看容器进程:

[root@docker-server3 docker-compose]# docker-compose ps

7.2.9 docker网络

7.2.9.1: 使用默认网桥

root@ubuntu-template:/opt/m43# cat docker-compose.yaml
version: '3.6'
services:
    nginx-service:
        image: harbor.12345.com/m43/nginx-all-in-one:1.16.1-v2
        container_name: nginx-web1
        network_mode: bridge # 网络,使用docker安装的默认网桥
        expose:
            - 80
            - 443
        ports:
            - "80:80"
            - "443:443"
networks:
    default:  # 使用存在的docker0默认172.17.0.1/16网络
        external:
            name: bridge

验证:

root@ubuntu-template:/opt/m43# docker exec 57bf65a19877 ip a  # nginx使用了docker的网桥
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
58: eth0@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

7.2.9.2:多个容器使用不同的网桥

version: '3.8'
services:
    nginx-service:
        image: harbor.12345.com/m43/nginx-all-in-one:1.16.1-v2
        container_name: nginx-web1
        #network_mode: bridge  #网络1,使用docker安装后的默认网桥
        expose:
            - 80
            - 443
        ports:
            - "80:80"
            - "443:443"
		networks: # 网络2,使用自定义的网络,如果网络不存在则会自动创建该网络并分配子网,并且容器会有两块网卡
		    - front
		    - backend

    tomcat-service-app1:
        image: harbor.12345.com/baseimages/tomcat-m43:app1
        container_name: tomcat-app1
        #network_mode: bridge  #网络1,使用docker安装后的默认网桥
        networks:
        	- backend
        expose:
        - 8080
        - 8443
        ports:
            - "8080:8080"
            - "8443:8443"
        networks: # 网络2,使用自定义的网络,如果网络不存在则会自动创建该网络并分配子网,并且容器会有一块网卡
            - backend
      
networks:
    front:  # 自定义前段服务网络
        driver: bridge
    backend:  # 自定义后端服务网络
        driver: bridge
    default: # 使用已经存在的docker0默认172.17.0.1/16的网络
        external:
            name: bridge

7.3:从docker compose 启动多个容器:

7.3.1:编辑docker-compose 文件:

# pwd
/opt/qinghai
# cat docker-compose.yml
service-nginx-web:
	image: harbor.12345.com/m43/nginx-all-in-one:1.16.1-v2
	container_name: nginx-web1
	expose:
		- 80
		- 443
	ports:
		- "80:80"
		- "443:443"
service-tomcat-app1:
	image: harbor.12345.com/baseimages/tomcat-m43:app1
	container_name: tomcat-app1
	expose:
		- 8080
	ports:
		- "8080:8080"

7.3.2:重新启动容器:

# pwd
/opt/qinghai
# docker-compose stop
# docker-compose up –d

image-20211224170758794

7.3.3:web 访问测试:

image-20211224170820840

7.4:定义数据卷挂载:

7.4.1:创建数据目录和文件:

# mkdir -p /data/nginx/testapp
# echo "qinghai test page" > /data/nginx/testapp/index.html

7.4.2:编辑compose 配置文件:

# cat docker-compose.yml
service-nginx-web:
	image: 192.168.7.103/linux37/ubuntu-nginx:1.16.1
	container_name: nginx-web1
	volumes:
		- /data/nginx/testapp:/apps/nginx/html  #:的左侧是宿主机,右侧是容器内的路径
		# - /data/nginx/testapp:/apps/nginx/html:ro  # 只读模式挂载
	expose:
		- 80
		- 443
	ports:
		- "80:80"
		- "443:443"
# 	links:
# 		- nginx-web1
service-tomcat-app1:
	image: 192.168.7.103/linux37/linux37-tomcat:app1
	container_name: tomcat-app1
	expose:
		- 8080
	ports:
		- "8080:8080"

7.4.3:重启容器:

# docker-compose stop
# docker-compose up –d
# docker-compose up -d
tomcat-app1 is up-to-date
Recreating nginx-web1 ... done

7.4.4:验证web 访问:

注:同一个文件,数据卷的优先级比镜像内的文件优先级高

7.4.5:其他常用命令:

7.4.5.1:重启单个指定容器:

# docker-compose restart service-nginx-web #写容器的service 名称
Restarting nginx-web1 ... done

7.4.5.2:重启所有容器:

# docker-compose restart

7.4.5.3:停止和启动单个容器:

# docker-compose stop service-tomcat-app1
# docker-compose start service-tomcat-app1

7.4.5.4:停止和启动所有容器:

# docker-compose stop
# docker-compose start

7.5:实现单机版的Nginx+Tomcat:

编写docker-compose.yml 文件,实现单机版本的nginx+tomcat 的动静分离web 站点,要求从nginx 作为访问入口,当访问指定URL 的时候转发至tomcat服务器响应。

7.5.1:制作Haproxy 镜像:

当前目录文件:

root@ubuntu-template:/opt/dockerfile/web/haproxy# tree
.
├── build_command.sh
├── Dockerfile
├── haproxy-2.2.11.tar.gz
├── haproxy.cfg
└── run_haproxy.sh

7.5.1.1:dockerfile 文件:

root@ubuntu-template:/opt/dockerfile/web/haproxy# cat Dockerfile 
FROM harbor.12345.com/m43/centos-base:7.8.2003

LABEL maintainer="liqinghai 61864003@qq.com"

RUN yum install -y  gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel net-tools vim iotop bc zip unzip zlib-devel lrzsz tree screen lsof tcpdump wget ntpdate 

ADD haproxy-2.2.11.tar.gz /usr/local/src/

RUN cd /usr/local/src/haproxy-2.2.11/ &&  make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_CPU_AFFINITY=1  PREFIX=/apps/haproxy && make install PREFIX=/apps/haproxy && cp haproxy /usr/sbin/ && mkdir /apps/haproxy/run -p

ADD haproxy.cfg /etc/haproxy/haproxy.cfg

ADD run_haproxy.sh /apps/haproxy/bin/run_haproxy.sh

EXPOSE 80 9999
CMD ["/apps/haproxy/bin/run_haproxy.sh"]

7.5.1.2:haprox.cfg 配置文件:

root@ubuntu-template:/opt/dockerfile/web/haproxy# cat haproxy.cfg
global
chroot /apps/haproxy
#stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin uid 99
gid 99
daemon
nbproc 1
pidfile /apps/haproxy/run/haproxy.pid
log 127.0.0.1 local3 info

defaults
option http-keep-alive
option forwardfor
mode http
timeout connect 300000ms
timeout client 300000ms
timeout server 300000ms

listen stats
	mode http
	bind 0.0.0.0:9999
	stats enable
	log global
	stats uri /haproxy-status
	stats auth haadmin:123456

listen web_port
	bind 0.0.0.0:80
	mode http
	log global
	balance roundrobin
	server web1 nginx-service:80 check inter 3000 fall 2 rise 5 

listen web_port_ssl
	bind 0.0.0.0:443
	mode http
	log global
	balance roundrobin
	server web1 nginx-service:443 check inter 3000 fall 2 rise 5 

7.5.1.3:haproxy 运行脚本:

root@ubuntu-template:/opt/dockerfile/web/haproxy# cat run_haproxy.sh 
#!/bin/bash
/apps/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg
tail -f /etc/hosts

7.2.1.4:build-command 脚本:

root@ubuntu-template:/opt/dockerfile/web/haproxy# cat build_command.sh 
#!/bin/bash
docker build -t harbor.12345.com/m43/haproxy:v2.2.11 .
docker push harbor.12345.com/m43/haproxy:v2.2.11

7.2.1.5:执行镜像构建:

# bash build-command.sh

image-20220110152139424

7.5.2:准备nginx 镜像:

参考步骤2.3 2.6 及2.7

root@ubuntu-template:/opt/dockerfile/web/nginx/all-in-one# tree
.
├── build_command.sh  # 制作脚本
├── code.tar.gz  # 主页文件
├── Dockerfile
├── index.html
├── nginx-1.16.1.tar.gz  # nginx源码
└── nginx.conf   # nginx 配置文件

0 directories, 6 files
root@ubuntu-template:/opt/dockerfile/web/nginx/all-in-one# cat Dockerfile 
FROM centos:7.8.2003
LABEL MAINTAINER="qinghai 61864003@qq.com"
RUN yum install -y epel-release && yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop

#COPY nginx-1.16.1.tar.gz /usr/local/src/
ADD nginx-1.16.1.tar.gz /usr/local/src/

RUN cd /usr/local/src/nginx-1.16.1 && ./configure --prefix=/apps/nginx --with-http_sub_module && make && make install
RUN useradd nginx -u 2022
ADD nginx.conf /apps/nginx/conf/
ADD code.tar.gz /data/nginx/html/

EXPOSE 80 443

#CMD ["/apps/nginx/sbin/nginx","-g","daemon off;"]

ENTRYPOINT ["/apps/nginx/sbin/nginx"]
CMD ["-g","daemon off;"]

root@ubuntu-template:/opt/dockerfile/web/nginx/all-in-one# cat nginx.conf 
...
upstream web {
	server tomcat-service-app1:8080;
	server tomcat-service-app2:8080;
}
...

	location /myapp {
		proxy_pass http://web;
	}
...

7.5.3:准备tomcat 镜像:

参考步骤2.4

7.5.4:编辑docker compose 文件及环境准备:

7.5.4.1:编辑docker compose 文件:

root@ubuntu-template:/opt/m43# cat docker-compose.yaml 
version: '3.8'
services:
    haproxy-service-m43:
        image: harbor.12345.com/m43/haproxy:v2.2.11 
        container_name: haproxy-m43
        networks:
            - backend
        expose:
            - 80
            - 9999
            - 443
        ports:
            - "80:80"
            - "443:443"
            - "9999:9999"
        links:
            - tomcat-service-app1
    nginx-service:
        image: harbor.12345.com/m43/nginx-all-in-one:1.16.1-v2
        container_name: nginx-web1
        #network_mode: bridge
        networks:
       #     - front
            - backend
#        expose:  
#            - 80
#            - 443
#        ports:
#            - "80:80"  # 只有内网连接所以不需要对外暴露端口
#            - "443:443"
        links:
            - tomcat-service-app1
            - tomcat-service-app2

    tomcat-service-app1:
        image: harbor.12345.com/baseimages/tomcat-m43:app1
        container_name: tomcat-app1
        networks:
            - backend
#        expose:
#        - 8080
#        - 8443
#        ports:
#            - "8080:8080"
#            - "8443:8443"
    tomcat-service-app2:
        image: harbor.12345.com/baseimages/tomcat-m43:app2
        container_name: tomcat-app2
        networks:
            - backend
#        expose:  
#        - 8080
#        - 8443
#        ports:
#            - "8081:8080"
#            - "8444:8443"


networks:
    front: 
        driver: bridge
    backend:
        driver: bridge
    default:
        external:
            name: bridge

7.5.4.2:准备nginx 静态文件:

# mkdir /data/nginx/static
# echo "Nginx static page" > /data/nginx/static/index.html
上传宿主机图片到静态文件路径

# tree /data/nginx/static/
/data/nginx/static/
├── 1.jpeg
└── index.html
0 directories, 2 files

7.5.4.3:准备nginx.conf 配置文件:

#在nginx 配置文件中,将用户访问app 目录的请求通过upstream 服务器组转发至后端tomcat 容器。

...
upstream web {
	server tomcat-service-app1:8080;
	server tomcat-service-app2:8080;
}
...

	location /myapp {
		proxy_pass http://web;
	}
...

7.5.4.4:准备tomcat 页面文件:

# mkdir /data/tomcat/webapps/myapp –p
# cat /data/tomcat/webapps/myapp/showhost.jsp
<%@page import="java.util.Enumeration"%>
<br />
host:
<%try{out.println(""+java.net.InetAddress.getLocalHost().getHostName());}cat
ch(Exception e){}%>
<br />
remoteAddr: <%=request.getRemoteAddr()%>
<br />
remoteHost: <%=request.getRemoteHost()%>
<br />
sessionId: <%=request.getSession().getId()%>
<br />
serverName:<%=request.getServerName()%>
<br />
scheme:<%=request.getScheme()%>
<br />
<%request.getSession().setAttribute("t1","t2");%>
<%
Enumeration en = request.getHeaderNames();
while(en.hasMoreElements()){
String hd = en.nextElement().toString();
out.println(hd+" : "+request.getHeader(hd));
out.println("<br />");
}
%>

7.5.5:启动容器:

# pwd
/opt/m43
# docker-compose up -d

image-20220110152850077

7.5.6:验证容器启动成功:

image-20220110152912772

7.5.7:查看启动日志:

[root@docker-server3 docker-compose]# docker-compose logs –f

7.5.8:访问haroxy 管理界面:

http://192.168.7.105:9999/haproxy-status

image-20220110153010271

7.5.9:访问Nginx 静态页面:

image-20220110153045082

7.5.10:访问tomcat 静态页面:

image-20220110153114687

7.5.11:访问tomcat 动态页面:

image-20211224174539743

7.5.12:验证Nginx 容器访问日志:

root@ubuntu-template:/opt/m43# docker exec -it f8a4c1b0a40e bash
[root@f8a4c1b0a40e /]# tail -f /apps/nginx/logs/access.log 
172.16.1.4 - - [10/Jan/2022:07:15:48 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:15:48 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:15:48 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:15:49 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:15:49 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:30:59 +0000] "GET /myapp HTTP/1.1" 302 5 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:30:59 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:31:01 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:31:01 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"
172.16.1.4 - - [10/Jan/2022:07:31:02 +0000] "GET /myapp/ HTTP/1.1" 200 36 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.55"

使用显卡示例:

使用撰写|启用 GPU 访问Docker 文档

root@newwuzhou-6-96:~/docker-lotus/run# cat docker-compose.yml 
services:
  work-service-c2:
    image: harbor.12345.com/lotus/work_c2:v1
    container_name: work-c2
    network_mode: host
    restart: always
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    volumes:
      - /fastdata/params:/fastdata/params
      - /fastdata/parent:/fastdata/parent
      - /filecoin/script:/filecoin/script

说明:

root@newwuzhou-6-96:~/docker-lotus/run# cat docker-compose.yml 
services:
  work-service-c2:  # 服务名
    image: harbor.12345.com/lotus/work_c2:v1  # 使用的镜像
    container_name: work-c2  # 容器名
    network_mode: host   # 使用host网络
    restart: always
    deploy:		# 此行开始,是使用gpu的设置
      resources:
        reservations:
          devices:
            - driver: nvidia  # 驱动类型
              count: 1  # 使用1个设备,值为int类型,与device_ids字段二选一
              #device_ids: ["0"]  # 使用0号显卡或者输入["GPU-f123d1c9-26bb-df9b-1c23-4a731f61d8c7"]指定gpu设备
              capabilities: [gpu]  # 必须字段 ["gpu","utility"]
    volumes:  # 挂载卷
      - /fastdata/params:/fastdata/params
      - /fastdata/parent:/fastdata/parent
      - /filecoin/script:/filecoin/script

八:资源限制

8.1:资源限制介绍

https://docs.docker.com/config/containers/resource_constraints

By default, a container has no resource constraints and can use as much of a given resource as the host’s kernel scheduler allows. Docker provides ways to control how much memory, or CPU a container can use, setting runtime configuration flags of the docker run command. This section provides details on when you should set such limits and the possible implications of setting them. 
Many of these features require your kernel to support Linux capabilities. To check for support, you can use the docker infocommand. If a capability is disabled in your kernel, you may see a warning at the end of the output like the following:

默认情况下,容器没有资源限制,可以使用主机内核调度程序允许的尽可能多的给定资源,Docker 提供了控制容器可以限制容器使用多少内存或CPU 的方法,设置docker run 命令的运行时配置标志。其中许多功能都要求宿主机的内核支持Linux 功能,要检查支持,可以使用docker info 命令,如果内核中禁用了某项功能,可能会在输出结尾处看到警告,

如下所示:
WARNING: No swap limit support

对于Linux 主机,如果没有足够的内容来执行其他重要的系统任务,将会抛出OOM (Out of Memory Exception,内存溢出、内存泄漏、内存异常), 随后系统会开始杀死进程以释放内存,凡是运行在宿主机的进程都有可能被kill,包括Dockerd 和其它的应用程序,如果重要的系统进程被Kill,会导致和该进程相关的服务全部宕机。

image-20220107221415256

产生 OOM 异常时,Dockerd 尝试通过调整 Docker 守护程序上的 OOM 优先级来减轻这些风险,以便它比系统上的其他进程更不可能被杀死,但是容器的OOM 优先级未调整,这使得单个容器被杀死的可能性比 Docker 守护程序或其他系统进程被杀死的可能性更大,不推荐通过在守护程序或容器上手动设置--oom-score-adj 为极端负数,或通过在容器上设置--oom-kill-disable 来绕过这些安全措施。

8.2:OOM 优先级机制:

linux 会为每个进程算一个分数,最终他会将分数最高的进程kill。

/proc/PID/oom_score_adj #范围为-1000 到1000,值越高越容易被宿主机kill 掉,如果将该值设置为-1000,则进程永远不会被宿主机kernel kill。

/proc/PID/oom_adj #范围为-17 到+15,取值越高越容易被干掉,如果是-17,则表示不能被kill,该设置参数的存在是为了和旧版本的Linux 内核兼容。

/proc/PID/oom_score #这个值是系统综合进程的内存消耗量、CPU 时间(utime + stime)、存活时间(uptime - start time)和oom_adj 计算出的进程得分,消耗内存越多得分越高,越容易被宿主机kernel 强制杀死。

image-20220107221532657

8.3:容器的内存限制:

Docker 可以强制执行硬性内存限制,即只允许容器使用给定的内存大小。

Docker 也可以执行非硬性内存限制,即容器可以使用尽可能多的内存,除非内核检测到主机上的内存不够用了。

Most of these options take a positive integer, followed by a suffix of b,k,m,g,to indicate bytes, kilobytes, megabytes, or gigabytes.
大部分的选项取正整数,跟着一个后缀b,k, m,g,,表示字节,千字节,兆字节或千兆字节。
--oom-score-adj #宿主机kernel 对进程使用的内存进行评分,评分最高的将被宿主机内核kill 掉(越低越不容易被kill),可以指定一个容器的评分为较低的负数,但是不推荐手动指定。

--oom-kill-disable #对某个容器关闭oom 机制。

内存限制参数:

-m or --memory #容器可以使用的最大内存量,如果设置此选项,则允许的最小存值为4m (4 兆字节)。
--memory-swap #容器可以使用的交换分区大小,必须要在设置了物理内存限制的前提才能设置交换分区的限制
--memory-swappiness #设置容器使用交换分区的倾向性,值越高表示越倾向于使用swap 分区,范围为0-100,0 为能不用就不用,100 为能用就用。
--kernel-memory #容器可以使用的最大内核内存量,最小为4m,由于内核内存与用户空间内存隔离,因此无法与用户空间内存直接交换,因此内核内存不足的容器可能会阻塞宿主主机资源,这会对主机和其他容器或者其他服务进程产生影响,因此不要设置内核内存大小。
--memory-reservation #允许指定小于--memory 的软限制,当Docker检测到主机上的争用或内存不足时会激活该限制,如果使用--memory-reservation,则必须将其设置为低于--memory 才能使其优先。因为它是软限制,所以不能保证容器不超过限制。
--oom-kill-disable #默认情况下,发生OOM 时,kernel 会杀死容器内进程,但是可以使用--oom-kill-disable 参数,可以禁止oom 发生在指定的容器上,即仅在已设置-m / - memory 选项的容器上禁用OOM,如果-m 参数未配置,产生OOM 时,主机为了释放内存还会杀死系统进程。

swap 限制:

--memory-swap #只有在设置了--memory 后才会有意义。使用Swap,可以让容器将超出限制部分的内存置换到磁盘上,WARNING:经常将内存交换到磁盘的应用程序会降低性能。

不同的--memory-swap 设置会产生不同的效果:

--memory-swap #值为正数, 那么--memory 和--memory-swap 都必须要设置,--memory-swap 表示你能使用的内存和swap 分区大小的总和,例如:--memory=300m, --memory-swap=1g, 那么该容器能够使用300m 内存和700m swap,即--memory 是实际物理内存大小值不变,而swap 的实际大小计算方式为(--memory-swap)-(--memory)=容器可用swap。
--memory-swap #如果设置为0,则忽略该设置,并将该值视为未设置,即未设置交换分区。
--memory-swap #如果等于--memory 的值,并且--memory 设置为正整数,容器无权访问swap 即也没有设置交换分区。
--memory-swap #如果设置为unset,如果宿主机开启了swap,则实际容器的swap 值为2x( --memory),即两倍于物理内存大小,但是并不准确(在容器中使用free 命令所看到的swap 空间并不精确,毕竟每个容器都可以看到具体大小,但是宿主机的swap 是有上限而且不是所有容器看到的累计大小)。
--memory-swap #如果设置为-1,如果宿主机开启了swap,则容器可以使用主机上swap 的最大空间。

8.3.1:内存限制验证:

假如一个容器未做内存使用限制,则该容器可以利用到系统内存最大空间,默认创建的容器没有做内存资源限制。

# docker pull lorel/docker-stress-ng #测试镜像
# docker run -it --rm lorel/docker-stress-ng –help #查看帮助信息

8.3.1.1:内存大小硬限制:

启动两个工作进程,每个工作进程最大允许使用内存256M,且宿主机不限制当前容器最大内存:
# docker run -it --rm  lorel/docker-stress-ng --vm 2 --vm-bytes 256M
# docker stats
宿主机限制容器最大内存使用:
# docker run -it --rm -m 256m  lorel/docker-stress-ng --vm 2 --vm-bytes 256M
# docker stats
#宿主机cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器ID/memory.limit_in_bytes 
268435456 #宿主机基于cgroup 对容器进行内存资源的大小限制

注:通过echo 命令可以改内存限制的值,但是可以在原基础之上增大内存限制,缩小内存限制会报错write error: Device or resource busy

image-20220107222826232

8.3.1.2:内存大小软限制:

# docker run -it --rm -m 256m --memory-reservation 128m  lorel/docker-stress-ng --vm 2 --vm-bytes 256M

#宿主机cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器ID/memory.soft_limit_in_bytes
134217728 #返回的软限制结果

8.3.1.3:关闭OOM 机制:

# docker run -it --rm -m 256m --oom-kill-disable -c1 lorel/docker-stress-ng --vm 2 --vm-bytes 256M
# cat /sys/fs/cgroup/memory/docker/容器ID/memory.oom_control
oom_kill_disable 1
under_oom 1
oom_kill 0

8.3.1.4:交换分区限制:

# docker run -it --rm -m 256m --memory-swap 512m  centos bash

#宿主机cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器ID/memory.memsw.limit_in_bytes
536870912 #返回值

8.3.2:K8s 1.8.3 更新日志:

宿主机开启交换分区,会在安装之前的预检查环节提示相应错误信息:

https://github.com/kubernetes/kubernetes/blob/release-1.8/CHANGELOG-1.8.md

image-20220107223126304

8.4:容器的CPU 限制:

https://docs.docker.com/config/containers/resource_constraints/
一个宿主机,有几十个核心的CPU,但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的任务,多进程共用一个CPU 的核心依赖技术就是为可压缩资源,即一个核心的CPU 可以通过调度而运行多个进程,但是同一个单位时间内只能有一个进程在CPU 上运行,那么这么多的进程怎么在CPU 上执行和调度的呢?
实时优先级:0-99

非实时优先级(nice):-20-19,对应100-139 的进程优先级
Linux kernel 进程的调度基于CFS(Completely Fair Scheduler),完全公平调度

CPU 密集型的场景:优先级越低越好,计算密集型任务的特点是要进行大量的计算,消耗CPU 资源,比如计算圆周率、数据处理、对视频进行高清解码等等,全靠CPU 的运算能力。

IO 密集型的场景:优先级值高点,涉及到网络、磁盘IO 的任务都是IO 密集型任务,这类任务的特点是CPU 消耗很少,任务的大部分时间都在等待IO 操作完成(因为IO 的速度远远低于CPU 和内存的速度),比如Web 应用,高并发,数据量大的动态网站来说,数据库应该为IO 密集型。

磁盘的调度算法

root@docker-node2:~# cat /sys/block/sda/queue/scheduler
noop deadline [cfq]

默认情况下,每个容器对主机CPU 周期的访问权限是不受限制的,但是我们可以设置各种约束来限制给定容器访问主机的CPU 周期,大多数用户使用的是默认的CFS 调度方式,在Docker 1.13 及更高版本中,还可以配置实时优先级。
参数:

--cpus #指定容器可以使用多少可用CPU 资源,例如,如果主机有两个CPU,并且设置了--cpus =“1.5”,那么该容器将保证最多可以访问1.5 个的CPU(如果是4 核CPU,那么还可以是4 核心上每核用一点,但是总计是1.5 核心的CPU),这相当于设置--cpu-period =“100000”(CPU 调度周期)和--cpu-quota =“150000”(CPU 调度限制),--cpus 主要在Docker 1.13 和更高版本中使用,目的是替代--cpu-period 和--cpu-quota 两个参数,从而使配置更简单,但是最大不能超出宿主机的CPU 总核心数(在操作系统看到的CPU 超线程后的数值)。


# docker run -it --rm --cpus 2 centos bash
docker: Error response from daemon: Range of CPUs is from 0.01 to 1.00,as there are only 1 CPUs available.See 'docker run --help'. #分配给容器的CPU 超出了宿主机CPU 总数。
--cpu-period #(CPU 调度周期)设置CPU 的CFS 调度程序周期,必须与--cpu-quota 一起使用,默认周期为100 微秒(1Second=1000Millisecond=1000000Microsecond)。
--cpu-quota #在容器上添加CPU CFS 配额,计算方式为cpu-quota /cpu-period 的结果值,早期的docker(1.12 及之前)使用此方式设置对容器的CPU 限制值,新版本docker(1.13 及以上版本)通常使用--cpus 设置此值。
--cpuset-cpus #用于指定容器运行的CPU 编号,也就是我们所谓的绑核。
--cpuset-mem #设置使用哪个cpu 的内存,仅对非统一内存访问(NUMA)架构有效。
--cpu-shares #用于设置cfs中调度的相对最大比例权重,cpu-share 的值越高的容器,将会分得更多的时间片(宿主机多核CPU 总数为100%,假如容器A 为1024,容器B 为2048,那么容器B将最大是容器A的可用CPU的两倍),默认的时间片1024,最大262144。

8.4.1:测试CPU 限制

8.4.1.1:未限制容器CPU:

对于一台四核的服务器,如果不做限制,容器会把宿主机的CPU 全部占完:

# docker run -it --rm --name qinghai-c1 lorel/docker-stress-ng --cpu 4 --vm 4 #分配4 核CPU 并启动4 个工作线程

在宿主机使用dokcer top 命令查看容器运行状态:

容器运行状态:
# docker stats

image-20220108155826364

在宿主机查看CPU 限制参数:
# cat /sys/fs/cgroup/cpuset/docker/容器ID/cpuset.cpus
0-3

宿主机CPU 利用率:

image-20220108155850915

8.4.1.2:限制容器CPU:

#只给容器分配最多两核宿主机CPU 利用率
# docker run -it --rm --name qinghai-c1 --cpus 2 lorel/docker-stress-ng --cpu 4 --vm 4
#宿主机cgroup 验证:
# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器ID/cpu.cfs_quota_us
200000 #每核心CPU 会按照1000 为单位转换成百分比进行资源划分,2 个核心的CPU 就是200000/1000=200%,4 个核心400000/1000=400%,以此类推。

当前容器状态:

image-20220108160002972

宿主机CPU 利用率:

image-20220108160021720

注:CPU 资源限制是将分配给容器的2 核心分配到了宿主机每一核心CPU 上,也就是容器的总CPU 值是在宿主机的每一个核心CPU 分配了部分比例。

8.4.1.3:将容器运行到指定的CPU 上:

# docker run -it --rm --name qinghai-c1 --cpus 2 --cpuset-cpus 1,3 lorel/docker-stress-ng --cpu 4 --vm 4
# cat /sys/fs/cgroup/cpuset/docker/容器ID/cpuset.cpus
1,3

容器运行状态:

image-20220108160109504

宿主机CPU 利用率:

image-20220108160055512

8.4.1.4:基于cpu—shares 对CPU 进行切分:

启动两个容器,qinghai-c1 的--cpu-shares 值为1000,qinghai-c2 的--cpu-shares 为500,观察最终效果,--cpu-shares 值为1000 的qinghai-c1的CPU 利用率基本是--cpu-shares 为500 的qinghai-c2 的两倍:

# docker run -it --rm --name qinghai-c1 --cpu-shares 1000 lorel/docker-stress-ng --cpu 4 --vm 4

# docker run -it --rm --name qinghai-c2 --cpu-shares 500 lorel/docker-stress-ng --cpu 4 --vm 4

验证容器运行状态:

image-20220108160156122

#宿主机cgroup 验证:
# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器ID/cpu.shares
1000

# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器ID/cpu.shares
500

宿主机CPU 利用率:

image-20220108160226754

8.4.1:5:动态修改CPU shares 值:

--cpu-shares 的值可以在宿主机cgroup 动态修改,修改完成后立即生效,其值可以调大也可以减小。

# echo 2000 > /sys/fs/cgroup/cpu,cpuacct/docker/容器ID/cpu.shares

验证修改后的容器运行状态:

九:docker swarm

9.1.初始化:

root@swarm-master:~# docker swarm  init  --advertise-addr string 192.168.206.101 
#初始化manager,这个ip地址是物理网卡的地址

root@swarm-master:~# docker swarm join-token  worker 
#获取token,初始化以后,如果忘记了token或者token过期,可以重新获取

root@swarm-master:~# docker node ls 
#列出swarm集群的节点

9.2.添加节点

[root@swarm-node01 ~]#  docker swarm join --token SWMTKN-1-0kgeibr9kzbbot9xaqk9864fr8wvet6erzxzz5icwipf57w8zc-9rtlleufh2kgec7f8vqunvec4 192.168.206.101:2377

[root@swarm-node02 ~]#  docker swarm join --token SWMTKN-1-0kgeibr9kzbbot9xaqk9864fr8wvet6erzxzz5icwipf57w8zc-9rtlleufh2kgec7f8vqunvec4 192.168.206.101:2377

9.3.添加label

root@swarm-master:~# docker node update  --label-add name=swarm-master swarm-master
swarm-master  #  --label-add name=swarm-master 要打的标签  后面的swarm-master是主机名

root@swarm-master:~# docker node update   --label-add name=swarm-node01 swarm-node01
swarm-node01

root@swarm-master:~# docker node update  --label-add name=swarm-node02 swarm-node02
swarm-node02

9.4.将其他节点提升为manager角色以实现高可用

root@swarm-master:~# docker node promote swarm-node01
Node swarm-node01 promoted to a manager in the swarm.

root@swarm-master:~# docker node promote swarm-node02
Node swarm-node02 promoted to a manager in the swarm.

root@swarm-master:~# 
root@swarm-master:~# docker node ls
ID                            HOSTNAME       STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
17isfi0upkm7057k38bd841s6 *   swarm-master   Ready     Active         Leader           20.10.12
xdjsi08g052cwvplijad0sjeh     swarm-node01   Ready     Active         Reachable        20.10.12
m2z94jtkfg770v4d9p1bkmfof     swarm-node02   Ready     Active         Reachable        20.10.12


9.5.查看node信息:

root@swarm-master:~# docker node inspect swarm-node02

9.6.创建网络:

root@swarm-master:~# docker network  --help
root@swarm-master:~# docker network  create -d overlay  --subnet=10.220.0.0/21 --gateway=10.220.0.1   --attachable  swarm-net
s3frz05tpisot7xax2buhknri

root@swarm-master:~# docker network  inspect swarm-net #验证网络信息

9.7.创建容器测试

root@swarm-master:~# docker service create --replicas 2 -p 8888:80 --network swarm-net  --name nginx nginx:1.18-alpine 
# --replicas 2 副本数
zfmtjmaf62wa26rq7wugiqp7f
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged 

  
root@swarm-master:~# docker service ls
ID             NAME      MODE         REPLICAS   IMAGE               PORTS
zfmtjmaf62wa   nginx     replicated   2/2        nginx:1.18-alpine   *:8888->80/tcp


root@swarm-master:~# ss -ntl #验证端口监听,每个服务器都会监听service端口8888
State                   Recv-Q                  Send-Q                                   Local Address:Port                                    Peer Address:Port                 Process                
LISTEN                  0                       4096                                     127.0.0.53%lo:53                                           0.0.0.0:*                                           
LISTEN                  0                       128                                            0.0.0.0:22                                           0.0.0.0:*                                           
LISTEN                  0                       20480                                        127.0.0.1:23834                                        0.0.0.0:*                                           
LISTEN                  0                       128                                          127.0.0.1:6010                                         0.0.0.0:*                                           
LISTEN                  0                       128                                               [::]:22                                              [::]:*                                           
LISTEN                  0                       20480                                                *:8888                                               *:*                                           
LISTEN                  0                       128                                              [::1]:6010                                            [::]:*                                           
LISTEN                  0                       20480                                                *:2377                                               *:*                                           
LISTEN                  0                       20480                                                *:7946                                               *:*   

9.8.访问测试:

root@k8s-master2:~# curl  http://192.168.206.102:8888
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
        body {
            width: 35em;
            margin: 0 auto;
            font-family: Tahoma, Verdana, Arial, sans-serif;

9.9.验证service

root@swarm-master:~# docker service ls
ID             NAME      MODE         REPLICAS   IMAGE               PORTS
zfmtjmaf62wa   nginx     replicated   2/2        nginx:1.18-alpine   *:8888->80/tcp


root@swarm-master:~# docker service ps zfmtjmaf62wa
ID             NAME      IMAGE               NODE           DESIRED STATE   CURRENT STATE           ERROR     PORTS
upwvyv7nxydu   nginx.1   nginx:1.18-alpine   swarm-node01   Running         Running 4 minutes ago           
8bt07m5zprn8   nginx.2   nginx:1.18-alpine   swarm-master   Running         Running 4 minutes ago

10.验证高可用:

将容器所在的服务器或docker关闭,验证pod副本高可用

docker 基本操作

镜像

镜像命令

[root@docker ~]# docker images
REPOSITORY          TAG              IMAGE ID       CREATED             SIZE
nginx               alpine           a64a6e03b055   3 weeks ago         22.6MB
alpine              latest           6dbb9cc54074   3 weeks ago         5.61MB
vip8/myapp          v1               8d987061ebe9   7 weeks ago         21.9MB

# 解释:
REPOSITORY		用户名和镜像名
TAG						镜像的标签
IMAGE ID 			镜像ID
CREATED				创建时间
SIZE					大小

# 选项
  -a, --all             # 所有镜像
  -q, --quiet           # 只显示镜像ID
[root@docker ~]# docker search mysql
NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                             MySQL is a widely used, open-source relation…   10856     [OK]     
mariadb                           MariaDB Server is a high performing open sou…   4094      [OK]     
mysql/mysql-server                Optimized MySQL Server Docker images. Create…   803                  [OK]
percona                           Percona Server is a fork of the MySQL relati…   537       [OK]  

# 选项
--filter=STARS=1000    # 搜索出来的镜像STARS大于1k
			[root@docker ~]# docker search mysql --filter=STARS=1000

镜像下载

# 下载镜像 docker pull IMAGESNAME[:VERSION]
docker pull centos:7.8    
docker pull centos:7.5.1804
docker pull nginx
docker pull mysql
[root@docker ~]# docker pull mysql
Using default tag: latest    				# 如果不写tag,默认就是latest
latest: Pulling from library/mysql
f7ec5a41d630: Pull complete 				# 分层下载, docker image的核心 联合文件系统
9444bb562699: Pull complete 
6a4207b96940: Pull complete 
181cefd361ce: Pull complete 
8a2090759d8a: Pull complete 
15f235e0d7ee: Pull complete 
d870539cd9db: Pull complete 
d44467b42d4c: Pull complete 
ae6d21974457: Pull complete 
be4ff89fec0f: Pull complete 
2c9b0d7f6e0f: Pull complete 
c6fb9ccf35d9: Pull complete 
Digest: sha256:cd42db5e061f8d1b1854e5fe597ad7ab9b9a5198ca1a2a560e5c4135b2d2b005
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest			# 真实地址


docker pull mysql 等价于 docker pull docker.io/library/mysql:latest

# 指定下载版本
docker image pull mysql:5.7

镜像查看

docker image ls

查看镜像详细信息

docker image inspect nginx:latest
docker image inspect 82f3b5f3c58f

只查看镜像的ID号

docker image ls -q

镜像的导入和导出

docker image save nginx > /opt/nginx.tar.gz    # 导出nginx镜像到/opt/nginx.tar.gz
docker image save d70eaf7277ea  > /tmp/ubu.tar.gz
docker image load -i /tmp/ubu.tar.gz	# 导入默认没有标签

打标签

docker image tag d70eaf7277ea ubu:latest

删除镜像

# 删除单个镜像
docker rmi 6fd7fa5293e6  等价于 docker image rm 6fd7fa5293e6
docker image rm xxxxxx
docker image rm -f xxxxxx		# 强制删除 xxxxxx是镜像名字
docker image rm -f `docker image ls -q`	# 删除所有镜像 docker image ls -q 可以获取所有的镜像名字

# 删除所有镜像
[root@docker ~]# docker image rm -f `docker images -q`

容器运行

说明:有了镜像才可以创建容器,linux

新建容器并启动

docker run [可选参数] image

# 参数说明
--name="NAME"		# 容器的名字
-D							# daemon的方式运行
-it							# 使用交互式方式进入容器
-p							# 指定容器端口 -p 8080:8080
		-p	IP:主机端口:容器端口
		-p	主机端口:容器端口(常用)
		-p	容器端口
-P			随机指定容器端口

交互式运行一个容器

docker run -it d70eaf7277ea    # 老版本命令
docker container run -it d70eaf7277ea   # 新版本命令
docker container run -it --name="centos" 0d120b6ccaa8	## 指定容器名字

查看正在运行的容器

docker ps -a
docker container ls
docker ps -a   等价于 docker container ls -a 
docker ps -n=#	  # 显示最近#个容器
docker ps -q			# 显示容器的编号

查看所有容器(包括没运行的)

[root@docker /]# docker container ls -a
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS                     PORTS     NAMES
8e916c3b4821   centos    "/bin/bash"   4 minutes ago   Exited (0) 3 minutes ago             quirky_greider
CONTAINER ID	:容器的唯一号码(自动生成)

NAMES:		# 容器的名字(可以自动,也可用手动指定)
COMMAND: 	# 容器运行的命令
STATUS:		# 容器的运行状态(Exited,Up)  
ctrl + p,q

查看已经启动容器的详细信息

docker container inspect nginx

停止自动删除容器

docker run --rm -it  --name=test centos	 # --rm选项为此容器停止后,自动删除

守护式启动容器

docker run -d --name nginx nginx:1.14   # -d选项 守护式启动容器

容器的启动和关闭

docker container start be97ace4ec56			# 启动容器
docker container restart be97ace4ec56		# 重启容器
docker container stop be97ace4ec56			# 停止容器
docker container kill be97ace4ec56			# 强制杀掉容器

查看容器内进程

 docker container top nginx  
 docker top nginx
 docker container top nginx  等价于  docker top nginx

查看日志

 docker logs nginx
 docker logs -tf nginx

查看cpu和内存占用情况

docker stats nginx		# 查看nginx容器的占用情况
docker stats 					# 查看所有容器的占用

连接到已运行的容器

docker container attach centos
docker container exec -it  centos /bin/bash	#子进程方式登陆

以下信息来自菜鸟教程

要attach上去的容器必须正在运行,可以同时连接上同一个container来共享屏幕(与screen命令的attach类似)。官方文档中说attach后可以通过CTRL-C来detach,但实际上经过我的测试,如果container当前在运行bash,CTRL-C自然是当前行的输入,没有退出;如果container当前正在前台运行进程,如输出nginx的access.log日志,CTRL-C不仅会导致退出容器,而且还stop了。这不是我们想要的,detach的意思按理应该是脱离容器终端,但容器依然运行。好在attach是可以带上--sig-proxy=false来确保CTRL-D或CTRL-C不会关闭容器。

docker attach --sig-proxy=false nginx

删除容器

docker container rm 容器ID			# 删除指定容器,不能删除正在运行的容器
docker container rm -f 容器ID		# 删除所有容器
docker container rm -f `docker ps -aq`		# 删除所有容器
docker container ls -aq |xargs docker rm -f 	# 删除所有容器

容器的网络访问

守护式启动容器暴露容器的80端口到宿主机的8080(端口映射)

docker run -d -p 8080:80 --name nginx nginx:1.14  # -p 8080:80  暴露端口8080是宿主机端口80是容器端口
指定映射:(docker 会自动添加一条iptables规则来实现端口映射)

	-p hostport:containerport 

	-p ip:hostPort:containerPort 

	-p ip::containerPort(随机端口) 

	-p hostPort:containerPort:udp 

	-p 81:80 -p 443:443

随机映射

docker run -p 80 (随机端口)

数据卷

Volume 实现宿主机和容器的数据共享

docker container run --name nginx -d --rm -p 80:80 -v /opt/html:/usr/share/nginx/html  nginx	# 数据共享参数

复制文件到容器里:

docker container  cp index.html nginx:/usr/share/nginx/html/

复制容器内的文件到主机上:

docker container  cp  nginx:/usr/share/nginx/html/index.html  /root/index.html

启动数据卷容器

docker run -it --name "nginx_volumes" -v /opt/Volume/a:/opt/a -v /opt/Volume/b:/opt/b centos /bin/bash

使用数据卷容器

docker run -d -p 8085:80 --volumes-from nginx_volumes --name "n8085" nginx
docker run -d -p 8086:80 --volumes-from nginx_volumes --name "n8086" nginx

作用:在集中化管理平台中,大批量的容器都需要挂载相同的多个数据卷

具名和匿名挂载

# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx

# 查看所有的volume的情况
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     308cbba31a7aa1e7f435d8172dbc21686616acdbe1fa12c0aee08cfb681c904d

# 具名挂载
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx

[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     308cbba31a7aa1e7f435d8172dbc21686616acdbe1fa12c0aee08cfb681c904d
local     juming-nginx

# 通过-v 卷名:容器内路径
[root@docker ~]# docker volume inspect juming-nginx

image-20210513115129637

所有的docker容器卷,都在/var/lib/docker/volumes

-v 容器内路径				# 匿名挂载
-v 卷名:容器内路径		# 具名挂载
-v /宿主机路径:容器内路径		# 指定具体路径

# 通过-v 容器内路径:ro rw改变读写权限
ro readonly   # 只读
rw readwrite  # 可读可写
docker run -d -P --name nginx01 -v /etc/nginx:ro nginx
docker run -d -P --name nginx01 -v /etc/nginx:rw nginx

常用操作

常用操作:
docker search:Search the Docker Hub for images   在docker hub中搜索镜像
docker pull:Pull an image or a repository from a registry	从库中下载镜像
docker images :list images		列出下载的镜像
docker create:create  a new container		创建一个新的容器
docker start:Start one or more stopped containers   启动已停止的容器
docker run:Run a command in a new container 		在容器中运行命令
docker attach:Attach to a running containet		附加到正在运行的容器中
docker ps :List containet	列出容器
docker logs:fetch the logs of a containet	获取容器的日志
docker restart:Restart a containet	重启容器
docker stop :Stop one or more running containers	停止一个或多个正在运行的容器
docker kill:Kill one or more running containers		杀掉一个或多个正在运行的容器
docker rm:Remove one or more containers			删除一个或多个容器

小结:

docker image
				search
				pull
				ls
				inspect
				rm
				save
				load
docker container
				run
				start
				stop
				restart
				kill
				attach
				exec
				top
				logs
				inspect
				cp
docker run --name mysql-test -p 3306:3306 -d -v /root/mysql/conf:/etc/mysql/conf.d \
           -v /root/mysql/data:/var/lib/mysql -e  MYSQL_ROOT_PASSWORD=123456 mysql:5.7

实验:

docker 运行mysql

docker run --name mysql-test -p 3306:3306 -d -v /root/mysql/conf:/etc/mysql/conf.d \
           -v /root/mysql/data:/var/lib/mysql \
           -e  MYSQL_ROOT_PASSWORD=123456 mysql:5.7 \
           --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
# 选项说明
-d 后台运行
-p 端口映射
-v 挂载卷

图形化管理:

Docker图形化管理工具

docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock \ 
            --privileged=true portainer/portainer

镜像制作

制作镜像(基于容器)

docker run -it --name  centos6.9  centos:6.9
	yum install openssh-server -y
	/etc/init.d/sshd start		# ssh第一次启动,需要生成密钥,生成pam验证配置文件(重要)
	exit
docker  commit centos6.9  qinghai/centos6.9_ssh:v1
docker run -d --name  sshd_2 -p 2222:22  qinghai/centos6.9_ssh:v1  /usr/sbin/sshd -D		# 指定sshd前台运行  映射容器内的22端口到2222

通过Dockerfile制作镜像

DockerFile构建过程

基础知识:

  1. 每个保留关键字(指令)都是大写字母
  2. 执行从上到下顺序执行
  3. #表示注释
  4. 每一个指令都会创建提交一个新的镜像层

步骤:

DockerFile: 构建文件,定义了一切步骤,源代码

Docker-Images:通过Dockerfile构建生成的镜像,最终发布和运行的产品

Docker容器:容器就是镜像运行起来提供的服务器

DockerFile的指令

FROM				# 基础镜像 比如centos,ubuntu,scratch(最小的镜像,不能直接用)
MAINTAINER	# 镜像是谁写的,姓名+邮箱
ENV					# 构建的时候设置环境变量
RUN					# 镜像构建的时候需要运行的命令
COPY				# 将文件拷贝到镜像中
ADD					# 复制文件到镜像里,比copy多的功能是,如果是压缩包,会自动解压,也可以是外网链接
WORKDIR			# 镜像的工作目录
VOLUME			# 挂载卷
EXPOSE			# 暴露端口
CMD					# 指定容器启动的时候要启动的命令,只有最后一个会生效,可被替代
ENTRYPOINT	# 可以追加参数
ONBUILD			# 当构建的镜像被引用时,会被触发
vim Dockerfile
# Centos6.9-SSHDv1.0
FROM centos:6.9
	### FROM:
		syntax:
               centos:6.9
               centos@2199b8eb8390		# 新版本才能支持
RUN mv /etc/yum.repos.d/*.repo /tmp && echo -e '[base]\nname=CentOS-$releasever -Base\nbaseurl=http://172.18.0.59/repository/yum-base/$releasever/os/$basearch/\ngpgcheck=0\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7\n'
	#### RUN:
	syntax:
		RUN mv /etc/yum.repos.d/*.repo /tmp \
    && echo -e '[base]\nname=CentOS-$releasever -Base\nbaseurl=http://172.18.0.59/repository/yum-base/$releasever/os/$basearch/\ngpgcheck=0\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7\n'
		RUN ["mysqld","--initalize-insecure","--user=mysql","--basedir=/usr/local/mysql","--datadir=/data/mysql/data"]

CMD ["/usr/sbin/sshd","-D"]
	#### CMD:
		syntax:
    		CMD ["/usr/sbin/sshd","-D"]
EXPOSE 22
	#### EXPOSE:	向外暴露的端口
		syntax:
			EXPOSE 22
vim Dockerfile
# Centos6.9-SSHDv1.0
FROM centos@2199b8eb8390

RUN mv /etc/yum.repos.d/*.repo /tmp && echo -e '[base]\nname=CentOS-$releasever - Base\nbaseurl=http://172.18.0.59/repository/yum-base/$releasever/os/$basearch/\ngpgcheck=0\n' > /etc/yum.repos.d/Base.repo && yum install openssh-server -y
RUN /etc/init.d/sshd start && echo "123456" |passwd root --stdin
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]

构建镜像

docker image build -t "qinghai/c69_sshd_lamp_bbs" ./		# 构建镜像
说明:
	COPY命令:
		syntax:
			<src>... <dest>
		从dockerfile所在目录,拷贝目标文件到容器的指定目录下,可以支持通配符。如果拷贝的是一个目录,只拷贝目录下的子文件
	ADD:
		syntax:
			<src>... <dest>
			url   <dest>
		具备COPY命令的所有功能,如果是压缩格式会自动解压,可以是tar.gz,tar.xz等等,只要是tar xf 可以解压得,都可以。如果是url路径,即使是压缩文件,也不会自动解压

	VOLUME ["/var/www/html","/data/mysql/data"]

	WORKDIR

	ENV

	ENTRYPOINT ["/bin/bash","/init.sh"]		和CMD的区别是,ENTRYPOINT 在启动容器时,防止第一个进程被手动输入的命令被替换掉。

制作基础镜像

此DockerFile来自github

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /

LABEL \
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20201113" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-11-13 00:00:00+00:00"

CMD ["/bin/bash"]
FROM centos
MAINTAINER qinghai<61864003@qq.com>
ENV MYPATH /usr/local
WORKDIR /usr/local
RUN yum install -y vim net-tools && yum clean all && rm -rf /var/cache/yum/*
EXPOSE 80
CMD echo $MYPATH
CMD echo ----end------
CMD ["/bin/bash"]

构建

docker build -f Dockerfile -t mycentos:0.1 .

双FROM的作用

Dockerfile的第一部分:

FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

相同的 (!)Dockerfile的第二部分:

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

镜像上传

登陆

docker login -uvip8
Password:   

Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

推送镜像

docker push 注册名/镜像名称[:TAG]

小结

小结:

小结:
FROM
RUN
	cmd1 && com2
	["cmd","option","xxx"]
COPY
ADD
CMD
EXPOSE
VOLUME
ENV
CMD
ENTRYPOINT

docker 网络

每启动一个容器,docker就会给docker容器分配一个ip

桥接模式,使用的技术是evth-pair技术

--link

已经不推荐使用

[root@docker ~]# docker run -d --name tomcat03 --link tomcat02  tomcat
79df1e7fcde604618b85a93d3086b132f274875311345ee2b077eb34f766d811
[root@docker ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.054 ms

# 反向不能ping通

[root@docker ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known

--link就是在tomcat03的容器里加了一个/etc/hosts配置

root@79df1e7fcde6:/usr/local/tomcat# cat /etc/hosts
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.3	tomcat02 a772ad88fe1a
172.17.0.4	79df1e7fcde6

docker0: 不支持容器名连接访问

自定义网络

网络模式

bridge:桥接 (默认,自己创建也使用bridge 模式)

none:不配置网络

host:和宿主机共享网络

container:容器内网络连通(用的少,局限性大)

# 直接启动的命令 --net bridge 默认就自动加上了,用的是docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat

# docker0特点,默认,域名不能访问(--link可以打通连接,不建议使用--link)

# 创建自定义网络
# --driver bridge
# --subnet 172.20.0.0/16 
# --gateway 172.20.0.1
[root@docker ~]# docker network create --driver bridge --subnet 172.20.0.0/16 --gateway 172.20.0.1 mynet
eaf2c4eb4eac5b68f2ee994d287316e0d4e04b6aa645c5c2a3342990b5ecd40e
[root@docker ~]# 
[root@docker ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
d3c87faffe4e   bridge    bridge    local
eb9cbc993611   host      host      local
eaf2c4eb4eac   mynet     bridge    local
e3c196d96d06   none      null      local

查看刚创建的网络

[root@docker ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "eaf2c4eb4eac5b68f2ee994d287316e0d4e04b6aa645c5c2a3342990b5ecd40e",
        "Created": "2021-05-14T10:44:25.037934053+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.20.0.0/16",
                    "Gateway": "172.20.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

测试:

[root@docker ~]# docker run -d -P --name tomcat-net-01 --net mynet tomcat
[root@docker ~]# docker run -d -P --name tomcat-net-02 --net mynet tomcat

[root@docker ~]# docker exec -it tomcat-net-01 ping 172.20.0.3
PING 172.20.0.3 (172.20.0.3) 56(84) bytes of data.
64 bytes from 172.20.0.3: icmp_seq=1 ttl=64 time=0.184 ms
--- 172.20.0.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.184/0.184/0.184/0.000 ms


[root@docker ~]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (172.20.0.3) 56(84) bytes of data.
64 bytes from tomcat-net-02.mynet (172.20.0.3): icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from tomcat-net-02.mynet (172.20.0.3): icmp_seq=2 ttl=64 time=0.109 ms

# 现在不使用--link也可以ping名字了

自定义的docker都已经维护好了对应的关系,推荐平时使用自定义网络

好处:

不同的集群使用不同的网络,保证集群是安全和健康的

网络连通

打通tomcat01 到mynet

[root@docker ~]# docker container ls
CONTAINER ID   IMAGE     COMMAND             CREATED          STATUS          PORTS                                         NAMES
81c803b30cbf   tomcat    "catalina.sh run"   5 minutes ago    Up 4 minutes    8080/tcp                                      tomcat02
d06b57bd29f4   tomcat    "catalina.sh run"   5 minutes ago    Up 5 minutes    8080/tcp                                      tomcat01
53a75d995d4a   tomcat    "catalina.sh run"   12 minutes ago   Up 12 minutes   0.0.0.0:49155->8080/tcp, :::49155->8080/tcp   tomcat-net-02
98ad65549042   tomcat    "catalina.sh run"   12 minutes ago   Up 12 minutes   0.0.0.0:49154->8080/tcp, :::49154->8080/tcp   tomcat-net-01


[root@docker ~]# docker network connect mynet tomcat01

# 连通之后就是将tomcat01 放到mynet网络下
# 一个容器两个ip地址
[root@docker ~]# docker network  inspect mynet

[root@docker ~]# docker exec -it tomcat01 ping tomcat-net-01
PING tomcat-net-01 (172.20.0.2) 56(84) bytes of data.
64 bytes from tomcat-net-01.mynet (172.20.0.2): icmp_seq=1 ttl=64 time=0.126 ms
--- tomcat-net-01 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 5ms
rtt min/avg/max/mdev = 0.049/0.087/0.126/0.039 ms
# 测试正常

结论: 假设要跨网络连接别人,就需要使用 docker network connect 连通

部署redis集群

# 创建网卡
[root@docker ~]# docker network create redis --subnet 172.38.0.0/16

# 通过脚本创建留个redis配置
#!/bin/bash
for port in {1..6} ;do
mkdir -p /mydata/redis/node-${$port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF > /mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

# 启动第一个redis
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
           -v /mydata/redis/node-1/data:/data \
           -v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
           -d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 \
           redis-server /etc/redis/redis.conf
# 启动第二个redis
docker run -p 6372:6379 -p 16372:16379 --name redis-2 \
           -v /mydata/redis/node-2/data:/data \
           -v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
           -d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 \
           redis-server /etc/redis/redis.conf
# 启动第三个
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \
           -v /mydata/redis/node-3/data:/data \
           -v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \
           -d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 \
           redis-server /etc/redis/redis.conf
 启动六个
 略...
 
# 创建集群
 [root@docker ~]# docker exec -it redis-1 /bin/sh
/data # ls
appendonly.aof  nodes.conf
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 \
                                   172.38.0.13:6379 172.38.0.14:6379 \
                                   172.38.0.15:6379 172.38.0.16:6379 \
                                   --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 0018a117537bdfbd2d5418db0b494e098c80b7de 172.38.0.11:6379
   slots:[0-5460] (5461 slots) master
M: c1c554206c26a1b660dd4f703c6c93d75168b00e 172.38.0.12:6379
   slots:[5461-10922] (5462 slots) master
M: 5273e228219a908e692b2e158001897fafcd08d4 172.38.0.13:6379
   slots:[10923-16383] (5461 slots) master
S: 4214159cba2964c278c5935b61b18e35516aa0e4 172.38.0.14:6379
   replicates 5273e228219a908e692b2e158001897fafcd08d4
S: 91336784ac81cd73ea639175b3e79c63079c3897 172.38.0.15:6379
   replicates 0018a117537bdfbd2d5418db0b494e098c80b7de
S: 08e8c7427af28f427a6ce7836383eb3ce2e28b9b 172.38.0.16:6379
   replicates c1c554206c26a1b660dd4f703c6c93d75168b00e
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 0018a117537bdfbd2d5418db0b494e098c80b7de 172.38.0.11:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: c1c554206c26a1b660dd4f703c6c93d75168b00e 172.38.0.12:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 4214159cba2964c278c5935b61b18e35516aa0e4 172.38.0.14:6379
   slots: (0 slots) slave
   replicates 5273e228219a908e692b2e158001897fafcd08d4
S: 08e8c7427af28f427a6ce7836383eb3ce2e28b9b 172.38.0.16:6379
   slots: (0 slots) slave
   replicates c1c554206c26a1b660dd4f703c6c93d75168b00e
S: 91336784ac81cd73ea639175b3e79c63079c3897 172.38.0.15:6379
   slots: (0 slots) slave
   replicates 0018a117537bdfbd2d5418db0b494e098c80b7de
M: 5273e228219a908e692b2e158001897fafcd08d4 172.38.0.13:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
/data # 

 

Docker Compose

安装

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose


chmod +x /usr/local/bin/docker-compose

体验

  1. 为项目创建目录

     mkdir composetest
     cd composetest
    
  2. 在项目目录中创建一个名为app.py的文件

    import time
    
    import redis
    from flask import Flask
    
    app = Flask(__name__)
    cache = redis.Redis(host='redis', port=6379)
    
    def get_hit_count():
        retries = 5
        while True:
            try:
                return cache.incr('hits')
            except redis.exceptions.ConnectionError as exc:
                if retries == 0:
                    raise exc
                retries -= 1
                time.sleep(0.5)
    
    @app.route('/')
    def hello():
        count = get_hit_count()
        return 'Hello World! I have been seen {} times.\n'.format(count)
    

    redis是应用程序网络上的redis容器的主机名。使用的是Redis的默认端口6379

  3. 在项目目录中创建另一个名为requirements.txt的文件

    flask
    redis
    
  4. 在你的项目目录中,创建一个名为Dockerfile的文件,并粘贴以下内容:

    FROM python:3.7-alpine
    WORKDIR /code
    ENV FLASK_APP=app.py
    ENV FLASK_RUN_HOST=0.0.0.0
    RUN apk add --no-cache gcc musl-dev linux-headers
    COPY requirements.txt requirements.txt
    RUN pip install -r requirements.txt
    EXPOSE 5000
    COPY . .
    CMD ["flask", "run"]
    
  5. 创建名为docker-compose的文件。在你的项目目录Yml和粘贴以下:

    version: "3.9"
    services:
      web:
        build: .
        ports:
          - "5000:5000"
      redis:
        image: "redis:alpine"
    
  6. 从项目目录中,运行docker-compose up来启动应用程序

    docker-compose up
    

yaml 规则

docker-compose.yaml

# 3层
version:  # 版本
service: # 服务
  服务1: web
    images
    build
    ......
  服务2:redis
    ......
  服务3:xxx
    ......
# 其他配置  网络配置、卷挂载,全局规则
volumes:
networks:
configs:

标题:docker基础
作者:harbor
地址:http://www.ipfshyys.com/articles/2022/10/18/1666087256265.html