1. Docker 基本组成
-
镜像(Image)
Docker 镜像就类似于一个模板,可以通过这个模板来创建容器服务,通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是子容器中)
-
容器(container)
Docker 利用容器技术,独立运行一个或者一组应用,通过镜像来创建的
-
仓库(repository)
仓库就是存放镜像的地方,仓库分为共有仓库和私有仓库
2. Docker 安装
3. docker run 的流程
Docker 是一个 Client-Server结构的系统,Docker 的守护进程运行在主机上。通过 Socket 从客户端访问。
Docker-Server 接收到 Docker-Client 的指令,就会执行这个命令。
3. Docker 为什么比虚拟机快
- Docker 有着比虚拟机更少的抽象层
- Docker 利用的是宿主机的内核,虚拟机需要 Guest OS
所以说,新建一个容器的时候,Docker 不需要像虚拟机一样重新加载一个操作系统内核,避免引导
4. Docker 的常用命令
4.1 帮助命令
docker version # 显示 Docker 版本信息
docker info # 显示 Docker 系统信息
docker 命令 --help # 万能命令
4.2 镜像命令
-
docker images
:查看所有本地镜像[root@VM-0-4-centos ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest bf756fb1ae65 13 months ago 13.3kB
- REPOSITORY:镜像的仓库源
- TAG:镜像的标签
- IMAGE ID:镜像的id
- CREATED:镜像的创建时间
- SIZE:镜像的大小
[root@VM-0-4-centos ~]# docker images --help Usage: docker images [OPTIONS] [REPOSITORY[:TAG]] List images Options: -a, --all Show all images (default hides intermediate images) --digests Show digests -f, --filter filter Filter output based on conditions provided --format string Pretty-print images using a Go template --no-trunc Don't truncate output -q, --quiet Only show image IDs
-a
,--all
:列出所有的镜像-q
,--quiet
:只显示镜像的 id
-
docker search
:搜索镜像
[root@VM-0-4-centos ~]# docker search --help
Usage: docker search [OPTIONS] TERM
Search the Docker Hub for images
Options:
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print search using a Go template
--limit int Max number of search results (default 25)
--no-trunc Don't truncate output
--filter=STARS=3000
:过滤出STARS大于等于3000的
-
docker pull
:下载镜像命令:
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
docker pull mysql
等价于docker pull docker.io/library/mysql:latest
也可以指定版本下载:
-
docker rmi
:删除镜像命令:
docker rmi [OPTIONS] IMAGE [IMAGE...]
-f
参数表示强制删除。
如果想一次性删除所有镜像,可以执行:
docker rmi -f $(docker images -aq)
4.3 容器命令
有了镜像我们才可以创建容器!
以 CentOS 镜像为例:
-
下载镜像
docker pull centos
-
新建容器并启动
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
参数说明:
--name="container_name"
: 自定义容器名字,用来区分容器-d
: 后台方式运行-it
: 使用交互方式运行,进入容器查看内容-p
: 指定容器的端口-p ip:主机端口:容器端口
-p 主机端口:容器端口
(常用)-p 容器端口
-P
: 随机指定端口
启动并进入容器:
-
退出容器
exit
:退出停止- Ctrl+P+Q:容器不停止退出
-
列出所有运行的容器
docker ps [-a]
-
删除容器
命令:
docker rm [OPTIONS] CONTAINER [CONTAINER...]
删除所有容器:
docker rm -f $(docker ps -aq)
或者docker ps -aq|xargs docker rm
-
启动和停止容器的操作
docker start 容器id # 启动容器 docker restart 容器id # 从起容器 docker stop 容器id # 停止当前正在运行的容器 docker kill 容器id # 强制停止当前容器
4.4 常用其他命令
-
后台启动容器
docker run -d 镜像名
👂 docker 容器使用后台运行,就必须在其中要有一个前台进程,否则 docker 没有发现应用就会自动停止。例如 Nginx容器启动后,发现自己没有提供服务,就会立刻停止。
-
查看日志
docker logs 容器id
-
查看容器中的进程信息
docker top 容器进程id
-
查看镜像的元数据
docker inspect 容器id
[root@VM-0-4-centos docker]# docker inspect 3d2cc7c0486c [ { "Id": "3d2cc7c0486c741b284f76f3effc8dc1d5d33b2ba40114514068ec1305f48853", "Created": "2021-02-16T10:49:01.970540957Z", "Path": "/bin/bash", "Args": [], "State": { "Status": "exited", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 0, "ExitCode": 0, "Error": "", "StartedAt": "2021-02-16T10:49:02.478164298Z", "FinishedAt": "2021-02-16T10:49:02.477170276Z" }, "Image": "sha256:300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55", "ResolvConfPath": "/var/lib/docker/containers/3d2cc7c0486c741b284f76f3effc8dc1d5d33b2ba40114514068ec1305f48853/resolv.conf", "HostnamePath": "/var/lib/docker/containers/3d2cc7c0486c741b284f76f3effc8dc1d5d33b2ba40114514068ec1305f48853/hostname", "HostsPath": "/var/lib/docker/containers/3d2cc7c0486c741b284f76f3effc8dc1d5d33b2ba40114514068ec1305f48853/hosts", "LogPath": "/var/lib/docker/containers/3d2cc7c0486c741b284f76f3effc8dc1d5d33b2ba40114514068ec1305f48853/3d2cc7c0486c741b284f76f3effc8dc1d5d33b2ba40114514068ec1305f48853-json.log", "Name": "/nifty_chebyshev", "RestartCount": 0, "Driver": "overlay2", "Platform": "linux", "MountLabel": "", "ProcessLabel": "", "AppArmorProfile": "", "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, "CgroupnsMode": "host", "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/5660b43f99fc0833c8188fafd166c8d59e8682839c0bd8f221e1f02084976682-init/diff:/var/lib/docker/overlay2/4c00d021fcf283bc1cfdd7ebc3219b1f60836811931838a1538676196dd89566/diff", "MergedDir": "/var/lib/docker/overlay2/5660b43f99fc0833c8188fafd166c8d59e8682839c0bd8f221e1f02084976682/merged", "UpperDir": "/var/lib/docker/overlay2/5660b43f99fc0833c8188fafd166c8d59e8682839c0bd8f221e1f02084976682/diff", "WorkDir": "/var/lib/docker/overlay2/5660b43f99fc0833c8188fafd166c8d59e8682839c0bd8f221e1f02084976682/work" }, "Name": "overlay2" }, "Mounts": [], "Config": { "Hostname": "3d2cc7c0486c", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/bash" ], "Image": "centos", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": { "org.label-schema.build-date": "20201204", "org.label-schema.license": "GPLv2", "org.label-schema.name": "CentOS Base Image", "org.label-schema.schema-version": "1.0", "org.label-schema.vendor": "CentOS" } }, "NetworkSettings": { "Bridge": "", "SandboxID": "45c1ee411368fde9a0dfbab867b223d3c462c976d8d086848efa5ea1a11af9af", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": {}, "SandboxKey": "/var/run/docker/netns/45c1ee411368", "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "", "Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "", "IPPrefixLen": 0, "IPv6Gateway": "", "MacAddress": "", "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "2051eb4628bb289bf436d6790f6611438e417438384ccfcdad1b6a3f56f472c5", "EndpointID": "", "Gateway": "", "IPAddress": "", "IPPrefixLen": 0, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "", "DriverOpts": null } } } } ]
-
进入当前正在运行的容器
我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
docker exec -it 容器id /bin/bash # 进入容器后开启一个新的终端,可以在里面操作(常用) docker attach 容器id # 进入容器正在执行的终端,不会启动新的进程
-
从容器拷贝文件到主机上
docker cp 容器id:容器内路径 目的主机路径
4.5 小结
4.5.1 部署 Nginx
搜索镜像
docker search nginx
下载镜像
docker pull nginx
查看已下载镜像
docker images
创建容器并启动
docker run -d --name=nginx01 -p=3344:80 nginx
查看已经启动的容器
docker ps
测试Nginx是否启动
curl localhost:3344
公网访问
进入容器
docker exec -it nginx01 /bin/bash
停止容器
docker stop nginx01
4.5.2 部署 Tomcat
docker run -it --rm tomcat:9.0 # 一般用来测试,引用创建完容器停止运行后,容器会被删除
一般还是先下载,再启动
docker pull tomcat:9.0
创建容器并启动
docker run -d -p 3456:8080 --name tomcat01 tomcat:9.0
公网访问
虽然 404,但是已经能访问到Tomcat,表示容器启动没问题
进入容器查看
docker exec -it tomcat01 /bin/bash
可以看出 webapps 文件夹里为空,所以 404
可以将 webapps.dist 里的项目复制到 webapps 就可以访问了:
cp -r webapps.dist/* webapps # 用这个命令 -r表示递归复制
此时刷新一下页面,即可访问成功:
4.5.3 部署 ElasticSearch
- ES 暴露的端口很多
- ES 十分耗内存
- ES 的数据一般要放置到安全目录。挂载!
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.10.1
此时启动会发现非常卡,因为 ES 太耗内存,我们可以通过修改配置来减小内存的使用:
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPS="-Xms64m -Xmx512m" elasticsearch:7.10.1
此时查看容器内存使用情况:docker stats
测试访问:
curl localhost:9200
5. Portainer 可视化面板
docker run -d -p 9000:9000 -p 8000:8000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer-ce
外网访问:http://ip:9000 即可进入面板
登陆之后的面板如下:
平时不会使用,自己测试玩玩~~
6. Docker 镜像
6.1 镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件
所有的应用,直接打包 Docker 镜像,就可以直接跑起来
如何获取镜像:
- 从远程仓库下载
- 拷贝
- 自己制作一个镜像 Dockerfile
6.2 Base 镜像
Base 镜像从 scratch 构建,不依赖其他镜像,可作为其他应用镜像的父镜像;其他应用镜像可以在此基础进行扩展,Base 镜像通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu、Debian、CentOS 等。
6.3 镜像的加载原理
-
bootfs
在 Docker 镜像中最底层是 bootfs (boot file system) 文件系统,bootfs 主要包含 bootloader 和 kernel。Linux 刚启动时会加载 bootfs 文件系统,bootloader 主要作用是引导加载 kernel。在 Docker 镜像中,bootfs 这一层与典型的 Linux/Unix 系统是一样的,包含 bootloader 和 kernel。当 bootloader 加载完成之后整个内核都存放在内存中,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。
-
rootfs
- 在 Docker 镜像中用户空间的文件系统是 rootfs,包含 /dev、/proc、/bin 等目录。对于 base 镜像来说,底层直接用 Host 的 kernel,自己只需要提供 rootfs。而对于一个精简的 OS,rootfs 的体积可以很小,只需要包含最基本的命令、工具和程序库就可以。
- 不同 Linux 发行版的主要区别就是 rootfs。比如 Ubuntu 14.04 使用 upstart 管理服务,apt 管理软件包;而 CentOS 7 使用 systemd 和 yum。这些都是用户空间上的区别,Linux kernel 差别不大。因此 Docker 可以同时支持多种发行版的 Linux 镜像,模拟出多种操作系统环境。
- 容器只能使用 Host 的 kernel,并且不能修改。所有容器都共用 host 的 kernel,在容器中没办法对 kernel 升级。如果容器对 kernel 版本有要求(比如应用只能在某个 kernel 版本下运行),则不建议用容器,这种场景虚拟机可能更合适。
-
UnionFS 文件系统
一种为 Linux,FreeBSD 和 NetBSD 操作系统设计的把其他文件系统联合到一个联合挂载点的文件系统服务。它使用 branch 把不同文件系统的文件和目录 “透明地” 覆盖,形成一个单一一致的文件系统。这些 branches 或者是 read-only 或者是 read-write 的,所以当对这个虚拟后的联合文件系统进行写操作的时候,系统是真正写到了一个新的文件中。看起来这个虚拟后的联合文件系统是可以对任何文件进行操作的,但是其实它并没有改变原来的文件,这是因为 UnionFS 用到了一个重要的资管管理技术叫写时复制。写时复制(copy-on-write)技术,也叫隐式共享,是一种对可修改资源实现高效复制的资源管理技术。它的思想是,如果一个资源是重复的,但没有任何修改,这时候并不需要立即创建一个新的资源;这个资源可以被新旧实例共享。创建新资源发生在第一次写操作,也就是对资源进行修改的时候。通过这种资源共享的方式,可以显著地减少未修改资源复制带来的消耗,但是也会在进行资源修改的时候增加小部分的开销。
-
Docker 镜像中的 UnionFS
UnionFS 文件系统是 Docker 镜像的基础,镜像可以通过分层来进行继承,基于 Base 镜像(没有父镜像),可以制作各种具体的应用镜像。简单概括来说,UnionFS 是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层地叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。特性是可以一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统;联合加载会把各层文件系统叠加起来,这样最终的文件系统包含所有底层的文件和目录。
-
容器的可写层
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作 “容器层”,“容器层” 之下的都叫 “镜像层”。所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器层中。即只有容器层是可写的,容器层下面的所有镜像层都是只读的。其中镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统(UnionFS)。如果不同镜像层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到最上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。下图是容器可写层的图解:
-
容器可写层的操作
- 添加文件,在容器中创建文件时,新文件被添加到容器层中。
- 读取文件,在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。
- 修改文件,在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后再修改。
- 删除文件,在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。
上面的操作中,只有当需要修改时才复制一份数据,这种特性被称作写时复制(copy-on-write)。可见容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。
6.4 镜像分层
我们可以取下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载
查看镜像分层的方式可以用 docker inspect
命令
[root@VM-0-4-centos ~]# docker inspect redis:latest
......
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:9eb82f04c782ef3f5ca25911e60d75e441ce0fe82e49f0dbf02c81a3161d1300",
"sha256:f973e3e0e07c6e9f9418a6dd0c453cd70c7fb87a0826172275883ab4bdb61bf4",
"sha256:c16b4f3a3f99ebbcd59795b54faf4cdf2e00ee09b85124fda5d0746d64237ca6",
"sha256:74aaf625a274218777bf2bf110da6351d44996b540917b4e1885138597d7c6e2",
"sha256:15d8987926beb0f4c680dd10033abd3745f607be28dc7ec584cf3a2aeb001e8e",
"sha256:85b4e380cb93b00ba7638886b63fa7d37f880c8bb1da23c5f4e5046f61d53207"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或添加新内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,加入基于 Ubuntu 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像层中添加 python 环境,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含 3 个镜像层,如下图所示(这只是一个很简单的示例):
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含 3 个文件,而该镜像包含了来自两个镜像层的 6 个文件:
下图展示了文件冲突时的三层镜像,在外部看来,整个镜像只有 6 个文件,这是因为最上层中的文件 7 是文件 5 的另一个更新版本:
在这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新的镜像层添加到镜像当中。
Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独特的性能特点。
Docker 在 Windows 上只支持 Windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和COW(Copy On Write,写时复制)
6.5 commit 镜像
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
【实战测试】
-
启动一个默认的 Tomcat
docker run -d -p 8080:8080 --name tomcat01 tomcat:9.0
进入容器发现默认的 Tomcat 镜像是没有默认的 webapps 应用
-
拷贝项目进 webapps
-
外网访问:
说明 webapps 里的项目部署成功了
-
提交修改过的镜像
docker commit -a="ice" -m="add webapps" 0150de8d34f0 mytomcat:1.0
7. 数据卷
7.1 什么是数据卷
当我们在使用docker容器的时候,会产生一系列的数据文件,这些数据文件在我们关闭docker容器时是会消失的,但是其中产生的部分内容我们是希望能够把它给保存起来另作用途的,Docker将应用与运行环境打包成容器发布,我们希望在运行过程钟产生的部分数据是可以持久化的的,而且容器之间我们希望能够实现数据共享。
通俗地来说,docker容器数据卷可以看成使我们生活中常用的u盘,它存在于一个或多个的容器中,由docker挂载到容器,但不属于联合文件系统,Docker不会在容器删除时删除其挂载的数据卷。
特点:
- 数据卷可以在容器之间共享或重用数据
- 数据卷中的更改可以直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
7.2 命令行挂载数据卷
docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名
可以看出来,数据卷是双向绑定的,一边变动了,另一边一起变
当停止该容器后,在 test.java 中增加一句话,然后重新启动容器查看:
【具名挂载和匿名挂载】
-v 容器路径 # 匿名挂载
-v 卷名:容器路径 # 具名挂载
-v /宿主机绝对路径:/容器路径 # 指定路径挂载
挂载数据卷时可以设置读写权限:
-v /home/test:/usr/nginx:ro
:read-only,宿主机可读写,容器内部只能读取-v /home/test:/usr/nginx:rw
:read-write
针对数据卷的操作可以使用 docker volume
8. 数据卷容器
如果用户需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器也是一个容器,但是它的目的是专门用来提供数据卷供其他容器挂载。
下面举例说明利用 --volumes-from
参数实现数据容器卷
-
查看容器
docker images
这里打算用 CentOS 的镜像示例
-
启动容器 docker01,并挂载数据卷
docker run -it --name docker01 -v /home/test:/home/container-test centos
-
Ctrl+P+Q,容器不停止退出
-
启动容器 docker02
docker run -it --name docker02 --volumes-from docker01 centos
-
测试同步
同样 Ctrl+P+Q,容器不停止退出,执行:
docker attach docker01
然后 Ctrl+P+Q,容器不停止退出,进入docker02 查看:
docker attach docker02
-
启动容器 docker03
docker run -it --name docker03 --volumes-from docker01 centos
在挂载的文件夹中创建文件 docker03
-
再次返回 docker02 查看
-
删除容器 docker01,查看容器 docker02 中数据是否丢失
-
删除所有容器,查看宿主机中数据卷内容是否存在
本质上就是容器挂载宿主机的文件夹,每个容器对数据卷的更新都会反映在宿主机的被挂载的文件夹中。也就是说,实际存储数据的是宿主机的文件系统,所以容器是否关闭或删除,不会影响宿主机上被挂载的文件夹中的内容
每次改动数据都会引起宿主机被挂载的文件夹和其他挂载该数据卷容器的容器执行复制操作
9. Dockerfile
9.1 什么是 Dockerfile
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明
构建步骤:
- 编写 Dockerfile 文件
docker build
成为一个镜像docker run
这个镜像docker push
发布镜像(Docker Hub、镜像仓库)
9.2 Dockerfile 指令
- 每个保留关键字(指令)都必须是大写字母
- 指令从上到下的顺序执行
- 注释符号用
# 注释内容
- 每一个指令都会创建提交一个新的镜像层,并提交
FROM # 基础镜像,一切从这里开始构建
MAINTAINER # 指定维护者信息,name+email
RUN # 镜像构建的时候需要运行的命令
ADD # 添加内容,如基本环境是 CentOS,这里添加一个 Tomcat
WORKDIR # 镜像的工作目录
VOLUME # 设置卷挂载主机的目录
EXPOSE # 指定暴露端口
CMD # 指定这个容器启动的时候要运行的命令,docker run 运行时给定指令会覆盖当前命令
ENTRYPOINT # 类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,可以追加命令
ONBUILD # 当构建一个被继承的 DockerFile 的时候就会运行 ONBUILD 指令,触发指令
COPY # 复制指令,从上下文目录中复制文件或者目录到容器里指定路径
ENV # 构建的时候设置环境变量
详细的指令见 https://www.runoob.com/docker/docker-dockerfile.html
9.3 构建自己的 CentOS
-
编写 Dockerfile
-
构建镜像
docker build -f DockerFile -t mycentos:0.1 .
-f
:指定要使用的 Dockerfile 的路径-t
:要生成的镜像名字及标签docker build
最后的.
号,其实上下文路径
最后看到返回 successful 表示成功:
-
测试运行
docker images
docker run -it mycentos:0.1
-
查看 docker 镜像构建历史
docker history mycentos:0.1
9.4 上下文路径
构建镜像时有个指令:
docker build -f DockerFile -t mycentos:0.1 .
最后一个 .
就是指的上下文路径
上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。
解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
如果未说明最后一个参数,那么默认上下文路径就是 DockerFile 所在的位置。
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
9.5 构建自己的 Tomcat
-
转变镜像文件、Tomcat 压缩包、jdk 的压缩包
-
编写 Dockerfile 文件
该文件官方命名就是 Dockerfile,build 的时候回自动寻找,可以不用
-f
参数FROM centos MAINTAINER ice<123456789@qq.com> ADD jdk-8u281-linux-x64.tar.gz /usr/local ADD apache-tomcat-10.0.2.tar.gz /usr/local RUN yum -y install vim ENV MYPATH /usr/local WORKDIR $MYPATH # 配置环境变量 ENV JAVA_HOME=/usr/local/jdk1.8.0_281 ENV CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME=/usr/local/apache-tomcat-10.0.2 ENV CATALINA_BASH=/usr/local/apache-tomcat-10.0.2 ENV PATH=$PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin EXPOSE 8080 CMD /usr/local/apache-tomcat-10.0.2/bin/startup.sh && tail -f /usr/local/apache-tomcat-10.0.2/bin/logs/catalina.out
-
构建镜像
docker build -t diy-tomcat .
- 省略
-f
,因为 Dockerfile 的文件名就是 Dockerfile - 构建镜像时没写 TAG,默认
latest
- 省略
-
查看镜像,并启动容器
docker images | grep diy
docker run -d -p 8080:8080 --name=ice-cat -v /home/ice/project:/usr/local/apache-tomcat-10.0.2/webapps/project \ -v /home/ice/logs:/usr/local/apache-tomcat-10.0.2/logs diy-tomcat
-
访问测试
-
发布项目
由于挂载了数据卷,所以可以本地发布
【web.xml】
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> </web-app>
【index.jsp】
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello ice!!!</title> </head> <body>Hello World!<br/> <h1>你,学废了吗?</h1> </body> </html>
此时访问 IP:8080/project,可以发现项目发布成功!
9.6 发布镜像
-
注册 Docker Hub 账号
-
在服务器上提交镜像
登录 Docker Hub 账号
提交镜像
不用
docker tag
设置标准标签会导致上传不成功,打标签会给本地在增加一个该标签名的镜像,这样你每次修改的都是最新的版本,发布出去都是排序好的版本号 -
Docker Hub 查看仓库