docker 100问 (有待更新)
docker 100问 (有待更新)
docker进阶笔记、狂神笔记
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592212665702.png)
1、docker Linux内核要求
Linux要求内核3.0以上
➜ ~ uname -r
4.15.0-96-generic # 要求3.0以上
➜ ~ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.4 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.4 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
2、docker的底层工作原理是什么
Docker是怎么工作的?
Docker是一个Client-Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问!
Docker-Server接收到Docker-Client的指令,就会执行这个命令!
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592633780218.png)
3、socket的简单描述/理解
就是两个进程,跨计算机,他俩需要通讯的话,需要通过网络对接起来。这就是 socket 的作用。打个比方吧,两个进程在两个计算机上,需要有一个进程做被动方,叫做服务器。另一个做主动方,叫做客户端。他们位于某个计算机上,叫做主机 host ,在网络上有自己的 ip 地址。一个计算机上可以有多个进程作为服务器,但是 ip 每个机器只有一个,所以通过不同的 port 数字加以区分。因此,服务器程序需要绑定在本机的某个端口号上。客户端需要声明自己连接哪个地址的那个端口。两个进程通过网络建立起通讯渠道,然后就可以通过 recv send 来收发一些信息,完成通讯。所以 socket 就是指代承载这种通讯的系统资源的标识。
- socket的位置
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592633654556.png)
socket 的诞生是为了应用程序能够更方便的将数据经由传输层来传输,所以它本质上就是对 TCP/IP 的运用进行了一层封装,然后应用程序直接调用 socket API 即可进行通信。那么它是如何工作的呢?它分为 2 个部分,服务端需要建立 socket 来监听指定的地址,然后等待客户端来连接。而客户端则需要建立 socket 并与服务端的 socket 地址进行连接。
所以socket包含tcp/ip的三次握手和四次分离,但是socket通信不仅仅只有tcp/ip一种实现。
4、为什么Docker比Vm快
1、docker有着比虚拟机更少的抽象层。由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
2、docker利用的是宿主机的内核,而不需要Guest OS。
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592633825453.png)
因此,当新建一个 容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。仍而避免引导、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载GuestOS,返个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了这个复杂的过程,因此新建一个docker容器只需要几秒钟。
5、docker常用命令
- 帮助命令
docker version #显示docker的版本信息。
docker info #显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help #帮助命令
- 镜像命令
docker images #查看所有本地主机上的镜像 可以使用docker image ls代替
docker search 搜索镜像
docker pull 下载镜像 docker image pull
docker rmi 删除镜像 docker image rm
- 容器命令
docker run 镜像id 新建容器并启动
docker ps 列出所有运行的容器 docker container list
docker rm 容器id 删除指定容器
docker start 容器id #启动容器
docker restart 容器id #重启容器
docker stop 容器id #停止当前正在运行的容器
docker kill 容器id #强制停止当前容器
- 退出容器
exit #容器直接退出
ctrl +P +Q #容器不停止退出
- 删除容器
docker rm 容器id #删除指定的容器,不能删除正在运行的容器,如果要强制删除 rm -rf
docker rm -f $(docker ps -aq) #删除指定的容器
docker ps -a -q|xargs docker rm #删除所有的容器
- 后台启动
# 命令 docker run -d 镜像名
➜ ~ docker run -d centos
a8f922c255859622ac45ce3a535b7a0e8253329be4756ed6e32265d2dd2fac6c
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 问题docker ps. 发现centos 停止了
# 常见的坑,docker容器使用后台运行,就必须要有要一个前台进程,docker发现没有应用,就会自动停止
# nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
- 查看日志
docker logs --help
Options:
--details Show extra details provided to logs
* -f, --follow Follow log output
--since string Show logs since timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
* --tail string Number of lines to show from the end of the logs (default "all")
* -t, --timestamps Show timestamps
--until string Show logs before a timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
➜ ~ docker run -d centos /bin/sh -c "while true;do echo 6666;sleep 1;done" #模拟日志
#显示日志
-tf #显示日志信息(一直更新)
--tail number #需要显示日志条数
docker logs -t --tail n 容器id #查看n行日志
docker logs -ft 容器id #跟着日志
- 查看容器中进程信息
# 命令 docker top 容器id
- 查看镜像的元数据
# 命令
docker inspect 容器id
#测试
➜ ~ docker inspect 55321bcae33d
[
{
"Id": "55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066",
"Created": "2020-05-15T05:22:05.515909071Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true;do echo 6666;sleep 1;done"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 22973,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-05-15T05:22:06.165904633Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:470671670cac686c7cf0081e0b37da2e9f4f768ddc5f6a26102ccd1c6954c1ee",
"ResolvConfPath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/hostname",
"HostsPath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/hosts",
"LogPath": "/var/lib/docker/containers/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066/55321bcae33d15da8280bcac1d2bc1141d213bcc8f8e792edfd832ff61ae5066-json.log",
"Name": "/bold_bell",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "docker-default",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Capabilities": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a-init/diff:/var/lib/docker/overlay2/5afcd8220c51854a847a36f52775b4ed0acb16fe6cfaec3bd2e5df59863835ba/diff",
"MergedDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a/merged",
"UpperDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a/diff",
"WorkDir": "/var/lib/docker/overlay2/1f347949ba49c4dbee70cea9ff3af39a14e602bc8fac8331c46347bf6708757a/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "55321bcae33d",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"while true;do echo 6666;sleep 1;done"
],
"Image": "centos",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20200114",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"org.opencontainers.image.created": "2020-01-14 00:00:00-08:00",
"org.opencontainers.image.licenses": "GPL-2.0-only",
"org.opencontainers.image.title": "CentOS Base Image",
"org.opencontainers.image.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "63ed0c837f35c12453bae9661859f37a08541a0749afb86e881869bf6fd9031b",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/63ed0c837f35",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "b129d9a5a2cbb92722a2101244bd81a9e3d8af034e83f338c13790a1a94552a1",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.4",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:04",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "ad5ada6a106f5ba3dda9ce4bc1475a4bb593bf5f7fbead72196e66515e8ac36a",
"EndpointID": "b129d9a5a2cbb92722a2101244bd81a9e3d8af034e83f338c13790a1a94552a1",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.4",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:04",
"DriverOpts": null
}
}
}
}
]
- 进入当前正在运行的容器
# 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 命令
docker exec -it 容器id bashshell
#测试
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55321bcae33d centos "/bin/sh -c 'while t…" 10 minutes ago Up 10 minutes bold_bell
a7215824a4db centos "/bin/sh -c 'while t…" 13 minutes ago Up 13 minutes zen_kepler
55a31b3f8613 centos "/bin/bash" 15 minutes ago Up 15 minutes lucid_clarke
➜ ~ docker exec -it 55321bcae33d /bin/bash
[root@55321bcae33d /]#
# 方式二
docker attach 容器id
#测试
docker attach 55321bcae33d
正在执行当前的代码...
区别
#docker exec #进入当前容器后开启一个新的终端,可以在里面操作。(常用)
#docker attach # 进入容器正在执行的终端
两种方式的区别:
docker exec:Run a command in a running container,翻译过来就是在一个正在运行的容器中执行命令,exec是针对已运行的容器实例进行操作,在已运行的容器中执行命令,不创建和启动新的容器,退出shell不会导致容器停止运行。
docker attach:Attach local standard input, output, and error streams to a running container,翻译过来,将本机的标准输入(键盘)、标准输出(屏幕)、错误输出(屏幕)附加到一个运行的容器,也就是说本机的输入直接输到容器中,容器的输出会直接显示在本机的屏幕上,如果退出容器的shell,容器会停止运行。
- 从容器内拷贝到主机上
docker cp 容器id:容器内路径 主机目的路径
#进入docker容器内部
➜ ~ docker exec -it 55321bcae33d /bin/bash
[root@55321bcae33d /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
#新建一个文件
[root@55321bcae33d /]# echo "hello" > java.java
[root@55321bcae33d /]# cat java.java
hello
[root@55321bcae33d /]# exit
exit
➜ ~ docker cp 55321bcae33d:/java.java / #拷贝
➜ ~ cd /
➜ / ls #可以看见java.java存在
bin home lib mnt run sys vmlinuz
boot initrd.img lib64 opt sbin tmp vmlinuz.old
dev initrd.img.old lost+found proc srv usr wget-log
etc java.java media root swapfile var
小结:
6、每次修改配置文件都要进入容器太麻烦怎么解决?
我们每次改动nginx配置文件,都需要进入容器内部?十分的麻烦,要是可以在容器外部提供一个映射路径,达到在容器修改文件名,容器内部就可以自动修改?√数据卷!
7、docker容器使用内存的情况、以及修改内存限制
~ docker stats # 查看docker容器使用内存情况 #关闭,添加内存的限制,修改配置文件 -e 环境配置修改 ➜ ~
docker rm -f d73ad2f22dd3
➜ ~ docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2
8、docker可视化工具介绍
- portainer(先用这个)
docker run -d -p 8080:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
- Rancher(CI/CD再用)
9、docker 镜像加载原理?
UnionFs(联合文件系统):Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下( unite several directories into a single virtual filesystem)。Union文件系统是 Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
boots(boot file system)主要包含 bootloader和 Kernel, bootloader主要是引导加 kernel, Linux刚启动时会加bootfs文件系统,在 Docker镜像的最底层是 boots。这一层与我们典型的Linux/Unix系统是一样的,包含boot加載器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos等等。
10/docker 分层的理解,为什么Docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过docker image inspect 命令
所有的 Docker镜像都起始于一个基础镜像层,当进行修改或培加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于 Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创健第三个镜像层该像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592635457080.png)
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。
- 版本更新
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592635532664.png)
文种情況下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统
11、容器和镜像启动状态
Docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592635638776.png)
12、commit镜像
docker commit 提交容器成为一个新的副本
# 命令和git原理类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[TAG]
# 1、启动一个默认的tomcat
docker run -d -p 8080:8080 tomcat
# 2、发现这个默认的tomcat 是没有webapps应用,官方的镜像默认webapps下面是没有文件的!
docker exec -it 容器id
# 3、拷贝文件进去
# 4、将操作过的容器通过commit调教为一个镜像!我们以后就使用我们修改过的镜像即可,这就是我们自己的一个修改的镜像。
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[TAG]
docker commit -a="kuangshen" -m="add webapps app" 容器id tomcat02:1.0
13、容器的持久化和数据同步共享操作
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592635771914.png)
- 方式一 :直接使用命令挂载 -v
-v, --volume list Bind mount a volume
docker run -it -v 主机目录:容器内目录 -p 主机端口:容器内端口
➜ ~ docker run -it -v /home/ceshi:/home centos /bin/bash
#通过 docker inspect 容器id 查看
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592635828281.png)
- 挂载之后、测试步骤:
再来测试!
1、停止容器
2、宿主机修改文件
3、启动容器
4、容器内的数据依旧是同步的
13.1、具名和匿名挂载
# 匿名挂载
-v 容器内路径!
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看所有的volume的情况
➜ ~ docker volume ls
DRIVER VOLUME NAME
local 33ae588fae6d34f511a769948f0d3d123c9d45c442ac7728cb85599c2657e50d
local
# 这里发现,这种就是匿名挂载,我们在 -v只写了容器内的路径,没有写容器外的路劲!
# 具名挂载
➜ ~ docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
➜ ~ docker volume ls
DRIVER VOLUME NAME
local juming-nginx
# 通过 -v 卷名:容器内路径
# 查看一下这个卷
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592636124516.png)
所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxxx/_data下如果指定了目录,docker volume ls 是查看不到的,具名挂载就是给个具体名称,没有指定目录。
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592636237394.png)
13.2、三种挂载方式比较小结
三种挂载: 匿名挂载、具名挂载、指定路径挂载
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载 docker volume ls 是查看不到的
13.3、设置读写权限
通过 -v 容器内路径: ro rw 改变读写权限
ro #readonly 只读
rw #readwrite 可读可写
docker run -d -P --name nginx05 -v juming:/etc/nginx:ro nginx
docker run -d -P --name nginx05 -v juming:/etc/nginx:rw nginx
ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!
14、dockerfile相关问题、数据卷共享
Dockerfile 就是用来构建docker镜像的构建文件!命令脚本!先体验一下!
通过这个脚本可以生成镜像,镜像
# 创建一个dockerfile文件,名字可以随便 建议Dockerfile
# 文件中的内容 指令(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "----end----"
CMD /bin/bash
#这里的每个命令,就是镜像的一层!
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592636584335.png)
- 启动刚刚写的镜像,并进入该容器
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592636607548.png)
这个卷和外部一定有一个同步的目录,视为匿名挂载目录
查看一下卷挂载 (容器运行元数据)
docker inspect 容器id
测试一下刚才的文件是否同步出去了!
这种方式使用的十分多,因为我们通常会构建自己的镜像!
假设构建镜像时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径!
14.1 数据卷容器 多个MySQL同步数据!
命名的容器挂载数据卷! --volumes-from list
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637177814.png)
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637251756.png)
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637266746.png)
测试:可以删除docker01,查看一下docker02和docker03是否可以访问这个文件
测试依旧可以访问
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637322073.png)
多个mysql实现数据共享
➜ ~ docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
➜ ~ docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
这个时候,可以实现两个容器数据同步!
容器之间的配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的!
14.2 DockerFile构建过程
1、每个保留关键字(指令)都是必须是大写字母
2、执行从上到下顺序
3、#表示注释
4、每一个指令都会创建提交一个新的镜像曾,并提交!
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637424010.png)
Dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单!
Docker镜像逐渐成企业交付的标准,必须要掌握!
DockerFile:构建文件,定义了一切的步骤,源代码
DockerImages:通过DockerFile构建生成的镜像,最终发布和运行产品。
Docker容器:容器就是镜像运行起来提供服务。
14.3DockerFile的指令
FROM # 基础镜像,一切从这里开始构建
MAINTAINER # 镜像是谁写的, 姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤,tomcat镜像,这个tomcat压缩包!添加内容 添加同目录
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 保留端口配置
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行ONBUILD的指令,触发指令。
COPY # 类似ADD,将我们文件拷贝到镜像中
ENV # 构建的时候设置环境变量!
14.4 编写一个dockerfile并编译运行
# 1.编写Dockerfile文件
vim mydockerfile-centos
FROM centos
MAINTAINER cheng<1204598429@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "-----end----"
CMD /bin/bash
# 2、通过这个文件构建镜像
# 命令 docker build -f 文件路径 -t 镜像名:[tag] .
docker build -f mydockerfile-centos -t mycentos:0.1 .
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637626596.png)
- 测试运行
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637635748.png)
我们可以列出本地进行的变更历史
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637658390.png)
我们平时拿到一个镜像,可以研究一下是什么做的
14.5 CMD 和 ENTRYPOINT区别
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
14.6 tomcat dockerfile编写
FROM centos #
MAINTAINER cheng<1204598429@qq.com>
COPY README /usr/local/README #复制文件
ADD jdk-8u231-linux-x64.tar.gz /usr/local/ #复制解压
ADD apache-tomcat-9.0.35.tar.gz /usr/local/ #复制解压
RUN yum -y install vim
ENV MYPATH /usr/local #设置环境变量
WORKDIR $MYPATH #设置工作目录
ENV JAVA_HOME /usr/local/jdk1.8.0_231 #设置环境变量
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.35 #设置环境变量
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib #设置环境变量 分隔符是:
EXPOSE 8080 #设置暴露的端口
CMD /usr/local/apache-tomcat-9.0.35/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.35/logs/catalina.out # 设置默认命令
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637866940.png)
15、docker 网络
docker0网络
15.1、docker 是如果处理容器网络访问的?
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592637927400.png)
# 测试 运行一个tomcat
$ docker run -d --name tomcat01 tomcat
$ ip addr
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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
551: vethbfc37e3@if550: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 1a:81:06:13:ec:a1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::1881:6ff:fe13:eca1/64 scope link
valid_lft forever preferred_lft forever
$ docker exec -it 容器id
$ ip addr
# 查看容器内部网络地址 发现容器启动的时候会得到一个 eth0@if551 ip地址,docker分配!
550: eth0@if551: <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
# 思考? linux能不能ping通容器内部! 可以 容器内部可以ping通外界吗? 可以!
$ ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.074 ms
思考? linux能不能ping通容器内部! 可以 容器内部可以ping通外界吗? 可以!
我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要按照了docker,就会有一个docker0桥接模式,使用的技术是veth-pair技术!
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592638015490.png)
在启动一个容器测试,发现又多了一对网络
我们发现这个容器带来网卡,都是一对对的 veth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一端连着协议,一端彼此相连
正因为有这个特性 veth-pair 充当一个桥梁,连接各种虚拟网络设备的
OpenStac,Docker容器之间的连接,OVS的连接,都是使用evth-pair技术
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592638110752.png)
结论:tomcat01和tomcat02公用一个路由器,docker0。
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用ip。
Docker使用的是Linux的桥接,宿主机是一个Docker容器的网桥 docker0
15.2 桥接、nat、host-only的区别
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592638608921.png)
上图对应网络地位,有路由的地方就是网络访问的地方。
桥接 通过使用物理机网卡 具有单独ip
NAT 把物理机为路由器进行上网
- 桥接模式
在bridged模式下,VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机,它可以访问网内任何一台机器。主机网卡和虚拟网卡的IP地址处于同一个网段,子网掩码、网关、DNS等参数都相同。
这种方式简单,直接将虚拟网卡桥接到一个物理网卡上面,和linux下一个网卡 绑定两个不同地址类似,实际上是将网卡设置为混杂模式,从而达到侦听多个IP的能力。在此种模式下,虚拟机内部的网卡(例如linux下的eth0)直接连到了物理网卡所在的网络上,可以想象为虚拟机和host机处于对等的地位,在网络关系上是平等的,没有谁在谁后面的问题。
- NAT 模式
使用NAT模式可以实现在虚拟系统里访问互联网。就是让虚拟系统借助NAT(网络地址转换)功能,通过宿主机器所在的网络来访问公网。
15.3 docker采用网络方式
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592638875842.png)
Docker使用的是Linux的桥接,宿主机是一个Docker容器的网桥 docker0
Docker中所有网络接口都是虚拟的,虚拟的转发效率高(内网传递文件)
只要容器删除,对应的网桥一对就没了!
思考一个场景:我们编写了一个微服务,database url=ip: 项目不重启,数据ip换了,我们希望可以处理这个问题,可以通过名字来进行访问容器?
$ docker exec -it tomcat02 ping tomca01 # ping不通
ping: tomca01: Name or service not known
# 运行一个tomcat03 --link tomcat02
$ docker run -d -P --name tomcat03 --link tomcat02 tomcat
5f9331566980a9e92bc54681caaac14e9fc993f14ad13d98534026c08c0a9aef
# 用tomcat03 ping tomcat02 可以ping通
$ 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.115 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.080 ms
# 用tomcat02 ping tomcat03 ping不通
–link 本质就是在hosts配置中添加映射
现在使用Docker已经不建议使用–link了!
自定义网络,不适用docker0!
docker0问题:不支持容器名连接访问!
15.4、docker 自定义网络、实现访问
docker network
connect -- Connect a container to a network
create -- Creates a new network with a name specified by the
disconnect -- Disconnects a container from a network
inspect -- Displays detailed information on a network
ls -- Lists all the networks created by the user
prune -- Remove all unused networks
rm -- Deletes one or more networks
- 查看所有的docker网络
网络模式
bridge :桥接 docker(默认,自己创建也是用bridge模式)
none :不配置网络,一般不用
host :和所主机共享网络
container :容器网络连通(用得少!局限很大)
# 我们直接启动的命令 --net bridge,而这个就是我们得docker0
# bridge就是docker0
$ docker run -d -P --name tomcat01 tomcat
等价于 => docker run -d -P --name tomcat01 --net bridge tomcat
# docker0,特点:默认,域名不能访问。 --link可以打通连接,但是很麻烦!
# 我们可以 自定义一个网络
$ docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592639192362.png)
$ docker network inspect mynet;
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592639228208.png)
启动两个tomcat,再次查看网络情况
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592639257447.png)
在自定义的网络下,服务可以互相ping通,不用使用–link
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592639267335.png)
我们自定义的网络docker当我们维护好了对应的关系,推荐我们平时这样使用网络!
好处:
redis -不同的集群使用不同的网络,保证集群是安全和健康的
mysql-不同的集群使用不同的网络,保证集群是安全和健康的
网络连通
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592639312554.png)
# 测试两个不同的网络连通 再启动两个tomcat 使用默认网络,即docker0
$ docker run -d -P --name tomcat01 tomcat
$ docker run -d -P --name tomcat02 tomcat
# 此时ping不通
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592639493769.png)
要将tomcat01 连通 tomcat—net-01 ,连通就是将 tomcat01加到 mynet网络
一个容器两个ip(tomcat01)
![enter description here enter description here](https://gitee.com/li521miao/xsj_lsk/raw/master/xsj/1592639520476.png)
01连通 ,加入后此时,已经可以tomcat01 和 tomcat-01-net ping通了
02是依旧不通的
16、SpringBoot微服务打包Docker镜像
1、构建SpringBoot项目
2、打包运行
mvn package
3、编写dockerfile
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
4、构建镜像
1.复制jar和DockerFIle到服务器
2.构建镜像 $ docker build -t xxxxx:xx .
5、发布运行
以后我们使用了Docker之后,给别人交付就是一个镜像即可!