Docker

容器是什么

容器是一种基础工具,泛指任何可以用于容纳其他物品的工具,可以部分或完全封闭,被用于容纳,存储,运输物品;物体可以被放置在容器中,而容器可以保护内容物.

虚拟化技术有哪些

主机级别 虚拟化

完全 虚拟化:Vmware,Kvm,Xen

半 虚拟化:Xen,UML

Xen如果CPU不支持虚拟化技术那就是半虚拟化,如果支持就是全虚拟化

半虚拟化:修改内核,通过被虚拟化出来的操作系统它是运行在虚拟化技术软件上的,虚拟化出来的操作系统执行的进程还是运行在真实机器上

完全虚拟化:不需要修改内核,直接通过虚拟机化技术软件上运行的操作系统。

容器级别 虚拟化

LXC,OpenVz,Solaris Containers,FreeBSD jails

LXC(LinuX Container)容器是内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源,不需要提供指令解释机制以及全虚拟化的其他复杂性.容器可以有效的将单个操作系统管理的资源划分到孤立的组件中,以便更好的孤立组之间的平衡有冲突的资源使用需求。

早期容器应用在jail中,后来移植到linux中vserver(chroot),chroot所隔离仅仅只是看上去的,并没有真正隔离。

Linux namespace 是linux提供一种内核级别环境隔离的方法,有6种不同名称空间:

linux namesapce:

namespace 系统调用参数 隔离内容 内核版本
UTS CLONE_NEWUTS 主机名和域名 2.6.19
MOUNT CLONE_NEWNS 挂载点(文件系统) 2.4.19
IPC CLONE_NEWIPC 信号量,消息队列,共享内存 2.6.19
PID CLONE_NEWPID 进程变化 2.6.24
USER CLONE_NEWUSER 用户和用户组 3.8
NETWORK CLONE_NEWNET 网络设备,网络栈,端口等 2.6.29

Docker是什么

Docker是LXC增强版,Docker简化容器使用难度,通常一个容器中只运行一个进程.

对开发来说带来极大便利,分发容易,一次编写到处运行

然而对运维来说(有优点有缺点), 对开发极大便利需要运维干什么?

Docker安装

环境说明:

操作系统发行版:CentOS7.4

内核版本:3.10+

安装说明:

使用yum方式安装,下载国内docker的yum源,加速下载.

安装过程:
1
2
3
4
5
wget -P /etc/yum.repos.d/ https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo
sed -i s@https://download.docker.com/@https://mirrors.tuna.tsinghua.edu.cn/docker-ce/@g /etc/yum.repos.d/docker-ce.repo
yum repolist | grep docker-ce
yum install docker-ce
systemctl start docker.service

此时docker已经启动了,现在我们来搞清楚什么是docker

Docker架构

c/s架构,由三个组件(Docker Daemon,Docker Client,Docker Registry)构成

Docker Registry:

​ 类似GitHub,只不过Docker Registry是存放镜像的仓库,

官方 https://hub.docker.com

国内 https://www.docker-cn.com/

当然也可以自己部署一个仓库,建议用Harbor。

Docker Daemon:

​ Docker进程,Docker核心服务。

这个类比数据库,比如数据库是放数据的,启动数据库后,等待客户端连接后才能操作。

也就是当docker启动时,等待docker客户端来操作。

Docker Client:

​ Docker客户端工具,用来操作Docker的

比如我想在仓库下载一个镜像,从而启动一个容器,在容器中启动一个nginx服务,都是在客户端操作的。(docker client是发出者,docker daemon是执行者)

这里提到的镜像和容器一定要区分清楚。

如果是开发,那这么理解:镜像就是你创建的类,容器就是你的对象,对象是通过类实例化而来。也就是容器通过镜像而来,(容器依赖于镜像)

不要问镜像怎么来的,上面提过镜像是在仓库中。

也不要问仓库中的镜像怎么来的,那是别人做好的。因为你也可以自己做镜像。

Docker客户端操作

1
2
docker --help
格式:docker [option] command [args]
镜像类操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker search SOFTWARE_NAME		#查找镜像名称
示例:
docker search tomcat #查找tomcat相关镜像,通常建议使用官方镜像,OFFICIAL 为OK的,或者Star点赞数高的镜像,原因自己悟

docker pull SOFTWARE_NAME:TAGS #下载镜像
示例:
docker pull tomcat #下载tomcat镜像,如果不指定tags就下载latest版本
docker pull tomcat:7.0.92-jre8-alpine #详见hub.docker.com找到指定的镜像后在看tag

docker image ls #查看本地镜像,如果没有下载那么这里为空

docker image rm SOFTWARE_NAME:TAGS #删除本地镜像
示例:
docker image rm tomcat #如果不指定删除镜像版本默认删除latest
容器类操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
docker run		#运行容器,需要指定镜像
实例:
docker run tomcat #运行镜像为tomcat的容器,容器里面有tomcat,启动容器后tomcat也将运行起来,容器里面的程序都是在前台运行,会占用窗口
docker run --name myapp -d tomcat #启动myapp容器,镜像使用tomcat:latest,以后台运行
更多命令使用:docker run --help

docker ps #查看运行中的容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ee1ff8df6e70 tomcat "catalina.sh run" 3 seconds ago Up 2 seconds 8080/tcp myapp
说明:
如果在docker run不指定容器名称会随机创建一个容器名称。
端口是容器内的端口.关于docker网络的内容下面会有讲解

docker exec
实例:
docker exec -it myapp /bin/bash #进入myapp容器中
-it #新建一个tty并且进入

docker stop
实例:
docker stop myapp #停止myapp容器
docker ps -a #查看所有容器,如果不加-a不能看到停止的容器

docker start
实例:
docker start myapp #运行停止的容器

docker restart #重启容器
实例:
docker restart myapp

docker rm #删除容器
实例:
docker rm myapp

docker logs #查看容器运行的程序日志
实例:
docker logs myapp

docker kill #杀死容器里的进程,进程一旦停止,容器也就停止。因为容器一般只运行一个前台程序,容器的生命周期下面会讲解
实例:
docker kill myapp

Docker Image

Docker镜像包含启动容器的所需文件系统以及其内容,因此,用于创建并启动docker容器:

分层机制:

镜像采用分层构建机制,最底层为bootfs,其为rootfs。

rootfs:用于系统引导的文件系统,包含bootloader和kernel,容器启动完成后被卸载以节约内存资源

rootfs:位于bootfs之上,表现为docker容器根文件系统

传统模式中,系统启动时,内核挂载rootfs时首先将其挂载为“只读”模式,自检其完整性 完成后将其重新挂载为读写模式

docker中,rootfs由内核挂载为“只读”模式,而通过“联合挂载”技术额外挂载一个可写层,容器就是在镜像多了一个可写层

传统模式: Docker:
docker镜像层级:

位于下层的镜像 称之为 父镜像(parent image),最底层的称之为 基础镜像(base image)

最上层“可读写成”,下面为“只读层”

联合挂载:

Aufs(advanced multi-layered unification filesystem ) 高级多层统一文件系统,同于实现linux平台中的联合挂载

Aufs是unionFS的重新实现,docker最初使用aufs作为容器文件系统层,目前仍作为存储后端之一来支持

Aufs另外一个实现是overlayFS,后者从3.18版本中开始被合并到linux内核中,overlayerFS是叠加文件系统

除了aufs,docker还支持btrFS,Device Mapper和VSF等。在ubuntu中,默认是aufs

device mapper在linux2.6中支持逻辑卷管理的通过设备映射机制,它为实现用于存储资源管理的块设备驱动提供一个高度模块化的内核架构,它通过一个个模块化的target driver插件实现对IO请求的过滤或者重新定向等工作,当前已经实现的target driver插件包括软raid,软加密,逻辑卷条带,多路径,镜像,快照等。

docker使用Thin provisioning的snapshot的技术实现了类似auFS分层镜像

1
2
> docker info | grep "Storage Driver:"	#来查看
>

Docker Container

Docker容器具有生命周期,”STOPD”,’CREATED’,’RUNNING’,’PAUSED’四个稳定状态,

容器一旦删除数据就会丢失,所以项目或者配置文件不要直接存放在容器中,通过卷(volume)的方式挂载至容器里.

Docker事件状态图:

Docker Registry

当容器启动时,docker daemon会试图先从本地获取相关镜像,当本地不存在的时候,其将从registry中下载该镜像并保存在本地

流程图:

docker client < - - - http/https - - - >docker daemon < - - - http/https - - - >docker registry

默认是使用https连接到registry,但是可以修改成http

Registry 分类

registry用于保存docker镜像,包括镜像的层次结构和元数据:

用户可自建registry,也可以使用官方的docker hub

分类:

sponsor registry 第三方registry,供客户和docker社区使用

mirror registry 第三方registry,只让客户用

verdor registry 由发布docker镜像的供应商提供registry

private registry 通过舍友防火墙和额外的安全层的私有实体提供的registry

repository及index

repository

由某种特定的docker镜像的所有迭代版本组成的镜像仓库

一个registry中可以存在多个repository

repository 可以为”顶层仓库”和“用户仓库”

用户仓库名称格式”用户名/仓库名”

每个仓库可以包含多个Tag,每个标签对应一个镜像

index

维护用户账号,镜像的校验以及公共命名空间的信息

相当于为Registry提供了一个完成用户认证等功能的检索接口

镜像相关操作

主要介绍镜像如何生成,和如何推送镜像至仓库

镜像生成方式:

​ 有三种方式:基于容器方式,Dockerfile方式,Docker Hub Automated Builds

基于容器制作镜像:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
docker run --name web1 -it busybox 
>mkdir -p /data/www/
>echo "<h1>welcome busybox http server</h1>" > /data/www/index.html
#注意:基于容器制作镜像,容器必须处于运行状态,这里切换终端

docker commit -p web1 #commit制作镜像前需要—p暂停
docker image ls #可以看到镜像制作完成(缺少tag)
docker tag IMAGEID zhuxyid/busyhttp:v1 #给刚才制作的镜像打标签(可以打多个标签)
docker tag zhuxyid/busyhttp:v1 zhuxyid/busyhttp:test_env #在打一个标签
docker image rm zhuxyid/busyhttp:v1 #这里只是删除一个标签,并没有删除镜像


#查看镜像启动时候运行的命令
docker inspect web1 | grep -A 5 "Cmd"

#如何在制作镜像运行时执行启动命令
docker commit -a "作者:zhuxy" -c 'CMD ["/bin/httpd","-f","-h","/data/www"]' -p web1 zhuxyid/busyhttp:test_env

#运行制作后的镜像
docker run --name -d webserver zhuxyid/busyhttp:test_env
基于Dockerfile制作镜像:

详见:http://blog.zhuxyid.com/2018/11/26/Dockerfile/

推送镜像
推送到官方

首先需要有hub.docker.com账号 ,hub.docker.com需要先建立好repositories

示例:这里的是zhuxyid/busyhttp命名,要跟本地的镜像保持一致

1
2
3
4
5
docker login    #输入账号密码才可以登录 

docker push zhuxyid/busyhttp:test_env #推送制作的镜像

docker logout
推送到阿里云

需要修改docker配置文件中的推送地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#修改docker配置文件添加registry-mirrors,https://brjvf90f.mirror.aliyuncs.com为我自己的阿里云镜像仓库
vi /etc/docker/daemon.json
{
"registry-mirrors":["https://registry.docker-cn.com","https://brjvf90f.mirror.aliyuncs.com"]}
}

#重载配置文件并重启
systemctl daemon-reload
systemctl restart docker.service

#阿里云创建命名空间,在创建镜像仓库
docker login --username=zhuxyid registry.cn-hangzhou.aliyuncs.com

#先重命名镜像标签
docker tag imagename registry.cn-hangzhou.aliyuncs.com/zhuxyid/busyhttp:test_env

#在推送到阿里云
docker push registry.cn-hangzhou.aliyuncs.com/zhuxyid/busyhttp:version
docker logout
镜像导入导出
1
2
3
4
5
6
7
#在本地导出镜像包,推送到目标机
docker save -o busyhttp.gz zhuxyid/busyhttp:latest .. #可以打包多个文件
scp busyhttp.gz root@REMOTEIP:/opt

#在目标机上导入推送的镜像
docker load -i busyhttp.gz
docker image ls

Docker网络

Docker安装后自动创建docker0网卡(虚拟)

网络虚拟化技术实现:

OVS:Open VSwitch 开源虚拟交换

SDN:Software Defined Network 软件定义网络(需要硬件和软件支持)

Docker网络接口

Docker有三种网络接口:

bridge,host,none,Containers

1
2
3
4
5
6
7
8
9
docker image ls
NETWORK ID NAME DRIVER SCOPE
1f03c706f810 bridge bridge local
75c5f8de6db4 host host local
871ede94efa8 none null local

bridge 桥接物理主机网卡(默认).这里是nat桥接,并不是物理桥接,如果是物理桥接,如果一个交换机下每个宿主机都有十几个容器,很容易导致广播风暴
host 使用物理主机的名称空间
none 不使用网络(特殊场景,有些程序不需要使用网络通信,比如自动任务不需要网络通信)

docker安装后,会生成docker0,此网卡是个NAT桥,当启动容器的时候,宿主机也会生成veth*虚拟网卡,该网卡和容器内的网卡绑定,而veth*就是跟docker0相连,可以使用brctl show来查看

1
2
3
4
5
6
7
8
9
10
yum install bridge-utils
brctl show #可以看出veth网络都是跟docker0关联
bridge name bridge id STP enabled interfaces
docker0 8000.024259bff40e no veth05116c6
vethf8f26a6

iptables -t nat -vnL #查看POSTROUTING链,可以看出容器内访问其他网络都是通过MASQUERADE地址伪装方式访问
Chain POSTROUTING (policy ACCEPT 54 packets, 3570 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
Docker通讯

Docker中如果外部想访问内部的服务有如下三种方式:

Bridge方式:

通过桥接,然后设置dnat才能被其他主机访问

Containers方式:

容器可以将6个名称空间分层:

如:docker-a和docker-b

docker-a独立6个名称空间:USER,MOUNT,PID,UTS,NET,IPC

docker-b独立3个名称空间:USER,MOUNT,PID,另外UTS,NET,IPC共享docker-a的

Host方式:

相当于Open container方式,只不过直接使用宿主机的UTS.NET.IPC

Docker 网络相关命令
指定网络类型以及端口映射
1
2
3
4
5
6
7
8
9
10
11
12
#docker run --name test -it --rm busybox:latest    #运行busybox命名为test,执行完后直接删除
--network [none|bridge] #指定网络类型(默认是bridge)
--hostname|-h HOSTNAME #指定主机名
--dns 114.114.114.114 #指定dns
--add-host HOST:IP #设置容器hosts

#映射
-p <containerPort> #将指定的容器端口 映射至 主机所有地址的一个动态端口
-p <hostPort>:<containerPort> #将容器端口containerPort 映射至 主机指定的主机端口<host Port>
-p <ip>::<containerPort> #将指定的容器端口<containerPort> 映射至 主机指定<ip>的动态端口
-p <ip>:<HostPort>:<containerPort>#将指定容器端口<containerPort>映射至主机指定<ip>的端口<hostPort>
-P #-P暴露所有端口
Bridge
1
2
3
4
5
6
7
docker run --name test -it --network bridge --rm busybox:latest
docker run --name test -it -h webserver.node1 --network bridge --rm busybox:latest
docker run --name test -it -h webserver.node1 --dns 114.114.114.114 --rm busybox:latest

docker run --name webserver -p 80 -d busybox/httpd:latest #本地随机端口映射到容器的80端口
docker run --name webserver -p 80:80 -d busybox/httpd:latest #本地80端口映射到容器的80端口
docker port webserver #查看webserver容器端口
Containers

联盟式容器是指使用某个已存在容器的网络接口的容器,接口被联盟内的各容器共享使用,因此,联盟式容器彼此间完全无隔离

#创建一个监听于80端口的http服务容器
docker run -d --it --rm -p 80:80 busybox/httpd:laster

#创建一个联盟式容器
docker run --name web1 -it --rm busybox/httpd
docker run --name web2 --network container:web1 -it --rm busybox/httpd
#在web2中创建的文件web1看不到,因为隔离Mount名称空间,但是web1和web2的ip是一样的,因为web2共享了web1的Net名称空间

#联盟式容器彼此间 虽然 共享同一个网络名称空间,但其他名称空间如User,Mount,Pid等还是隔离的
#联盟式容器彼此间存在端口冲突的可能性,因此,通常只会在多个容器上的程序需要程序lookback接口互相通信,或对某已存在的容器的网络属性进行监控时才使用此模式的网络模型
Host

创建一个宿主机host的容器

1
2
3
docker run --name webserver --network host -it --rm busybox/httpd
#使用宿主机的IP,可能会郁闷,这跟直接在主机上部署个httpd服务在启动有什么不一样么?
#容器,容器可以更加方便移值,直接run container就可以运行了。

Docker存储

关于卷

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

注意:在IO要求高应用中,如果使用容器的话,那么效率非常低

Container: /data/web < - - - - 建立绑定关系Mount - - - - > Host:/container/web1/data/web

在容器写入时候,可以绕过容器内部层级关系

命名空间Mount是相互独立的,可以共享,关联到存储卷Volume,只要容器挂载存储卷.当容器被停止或者删除后,文件内容不被删除。

为什么用存储卷

关闭并重启容器,其数据不受影响,但是删除容器,则数据全部丢失

存在的问题:

存储于联合文件系统中,不易于宿主机访问

容器间数据共享不便利

删除容器其数据丢失

卷的描述

卷在容器初始化时候 会自动创建,由base image提供的卷中数据会于此间 完成复制

卷的初衷是独立于容器的生命周期实现数据持久化,因此删除容器时不会删除卷,也不会对哪怕未被应用的卷做垃圾回收

卷为docker提供了独立于容器的数据管理机制

可以把”镜像” 比作成静态文件,例如“程序” ,把卷类比为动态内容,例如“数据”;于是镜像可以重用,而卷可以共享

卷实现了”程序(镜像)”和“数据(卷)”分离,以及 “程序(镜像)”和“制作镜像主机”分离,用户制作镜像时无需考虑镜像运行容器所在的主机环境

卷的类型

docker有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上的位置有所不同

绑定挂载卷 bind mount volume: 在宿主机人工指定特定路径,在容器也人工指定特定路径,将两者绑定

容器管理卷 docker-managed volume: 宿主机不需要指定路径(docker daemon),容器中需要指定路径,docker自动将两者绑定

如何使用卷
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker run -v 选项
docker-managed volume
docker run -it --name web1 -v /data busybox
docker inspect -f {{.Mounts}} web1 #查看容器卷绑定关系


docker run -it -v HOSTDIR:VOLUMEDIR --name web2 busybox
docker run -it -v /data:/test --name web2 busybox #本机的data跟容器的test绑定,如果data不存在自动创建
docker inspect -f {{.Mounts}} web1

docker inspect -f {{.NetworkSettings.Networks.bridge.IPAddress}} web1 #获取web1下的ip地址

#多个容器的卷使用同一个主机目录
docker run -it --name nginx1 -v /docker/volumes/html:/data busybox
docker run -it --name tomcat1 -v /docker/volumes/class:/data busybox

#复制使用其他容器的卷,为docker run命令使用--volumes-form
docker run -it --name tomcat2 -v /docker/volumes/class:/data busybox
docker run -it --name tomcat3 --network container:tomcat2 --volumes-from tomcat2 busybox #共享网络和卷

Docker资源限制

默认情况下,系统对容器没有做任何资源限制,容器可以使用掉宿主机所有资源。

docker provides可以控制Memory,CPU, Block IO(其实只能控制内存和CPU)

依赖于Linux Capabilities 。

这里需要注意:内存是非可压缩资源,CPU可压缩资源

​ 如何内存被进程耗尽会触发OOME直接KILL进程,而CPU没有关系

OOM

在linux主机中,如果内核探测到当前宿主机没有足够内存可用(用于执行某些重要的系统功能)会抛出OOME 异常,并且kill掉某些进行保证其正常,一旦发生OOME,任何进程包括docker daemon在内,都有可能杀死。因此docker特地调整 docker daemon的OOM优先级,以免它被内核”杀死”,但容器的优先级并未被调整 。

优先级越低,得分越低,通常检测oom-adj,分数越高越容易被kill

不重要的业务建议oom-adj默认值,重要的调低oom-adj

Memory

从ram,swap两个层面:

1
2
3
限制单位k,b,m,g
-m | --memory 限制ram内存 -m 1g 限制ram为1g,如果后期资源占用超过1G,可能被kill掉,-m|--memory可以单独设置
--memory-swap * 限制swap内存 必须先设置-m|--memory
–memory-swap –memory 功能
正数S 正数M 容器可用总空间为S,其中ram为M,swap为(S-M),如果S=M,则无可用swap资源
0 正数M 相当没有设置swap(unset)
unset 正数M 若主机(docker host)启用swap,则容器可用的swap为2*M
1 正数M 若主机(docker host)启用了swap,则容器可使用最大至主机上的所有swap空间的swap资源

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

设置–memory-swap必须大于–memory

1
2
3
4
5
--memory-swapiness        限制容器的虚拟内存控制行为0~100间整数
--memory-reservation 限制预留空间大小
--kernel-memory 核心内存的限制
--oom-kill-disable 如果容器重要,禁止oom被kill掉
--oom-score-adj 容器被OOM killer杀死的优先级,范围[-1000,1000]默认为0
CPU

默认情况下每个容器,可以使用CPU的资源.大多数系统,系统在调度时候使用CFS调度 (CFS完全公平调度器)

在docker1.13后,可以设置实时调度

1
2
3
4
5
6
7
--cpus=<value>       #设置CPU使用几核心,如果容器只设置了--cpus = 2,那么该容器只能使用200%的cpu
--cpu-period=<value> #最多使用多长时间
--cpu-quota=<value> #指定周期内
--cpuset-cpus #设置容器只能运行在那核心CPU上,如果4核心这里是0~3 如果上面设置--cpus=2 --cpuset-cpus 1,2 意思是使用2核心,只在cpu2和cpu3上面使用
--cpu-shares #设置cpu权重,按比例切分,默认权重是1024,CPU是资源共享(因为可以压缩),只有在系统cpu繁忙时候才体现出来,比如1核心的CPU,两个容器,其中一个A容器设置1024,另一个B设置512,当cpu都需要使用cpu的时候,就按比例分2:1(a是b的两倍) (a使用66% b使用33%)
#当512的权重空闲的时候,1024的容器可以吃掉所有CPU。
#如果3核心CPU,三个容器,a设置2048,b设置512,c设置1024,当三个都繁忙的时候比例是(4:1:2) (a使用56%,b使用14%,c使用28%)
docekr-stress-ng压缩
1
2
3
4
5
6
7
docker run --rm lorel/docker-stress-ng --help
docker run --name stree1 -it --rm -m 256 lorel/docker-stressng --vm 2
docker run --name stree2 -it --rm -cpus 2 lorel/docker-stress-ng --cpu 8
docker run --name stree2 -it --rm --cpu-shares 1024 lorel/docker-stress-ng --cpu 8

docker top stree1 查看stree1的进程运行情况
docker stats 查看容器状态

小结

这些主要是针对CentOS7.4,对于其他平台或者版本可能实现方式略有不同。

如:在macOS下面对于 网络 和 卷 都有不同的地方。

网络在macOS中没有docker0桥,无法ping通容器,只能使用-P 或者-p映射端口的形式访问容器,或者通过host.docker.internal特殊DNS,解析为主机使用的内部IP地址。

卷的话如果要绑定User以外的目录,则需要修改权限让容器内部可以访问该目录,不然直接挂载会提示权限不足情况

暂时遇到这么多,感谢志哥让我遇到这么多坑。

主要是讲解一下Docker基础方面,后期的Dockerfile和Docker Registry在慢慢总结,如果本文有错误,还请大牛指出,谢谢:)

看完了?赏个鸡腿钱,谢谢老板!