文章目录
- Docker概述
- Docker为什么会出现
- docker历史
- Docker理念
- Docker能干嘛
- 容器虚拟化技术
- 开发运维
- docker解释
- Docker环境安装
- 常用命令
- 基础命令
- 镜像命令
- 容器命令
- 常用的其他命令
- Docker安装软件
- 安装nginx
- 安装tomcat
- 安装es
- 安装Portaniner(可视化工具)
- 安装mysql
- Docker镜像详解
- 什么是镜像
- Docker镜像加载原理
- 分层理解
- 提交镜像
- 容器数据卷详解
- 什么是容器数据卷
- docker的理念回顾
- 使用数据卷
- 具名和匿名挂载
- 具名挂载
- 匿名挂载
- 区别
- 初始DockerFile
- 数据卷容器
- DockerFile
- DockerFile介绍
- DockerFile构建过程
- DockerFile构建指令
- 实战测试(制作centos:7镜像)
- 实战测试(制作Tomcat镜像)
- 发布镜像到阿里云
- 小结
- Docker网络
- 理解Docker网络
- --Link
- 自定义网络
- 网络连通
- 实战:部署Redis集群
- 实战:SpringBoot项目打包镜像
- Docker Compose
- 简介
- 安装
- 测试示例
- Docker小结
Docker概述
Docker为什么会出现
一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的法代之后,不同版本环境的兼容,对运维人员是极大的考验!
环境配置如此麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。解决开发人员说的”在我的机器上可正常工作”的问题。
之前在服务器配置一个应用的运行环境,要安装各种软件,就拿一个基本的工程项目的环境来说吧,Javal/Tomcat/MySQL/JDBC驱动包等。安装和配置这些东西有多麻烦就不说了,它还不能跨平台。假如我们是在Windows上安装的这些环境,到了Linux又得重新装,况目就算不跨操作系统,换另一台同样操作系统的服务器,要移植应用也是非常麻烦的。
传统上认为,软件编码开发/测试结束后,所产出的成果即是程序或是能够编译执行的二进制字节码文件等(Java为例)。而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让维运团队得以部署应用程式,开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。
Docker之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案。
Docker镜像的设计,使得Docker得以打破过去「程序即应用」的观念。通过Docker镜像(images)将应用程序所需要的系统环境由下而上打包,达到应用程序跨平台间的无缝接轨运作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Docker的思想来自于集装箱,集装箱解决了什么问题?在一艘大船上,可以把货物规整的摆放起来。井且各种各样的货物被集装箱标准化了,集装箱和集装箱之间不会互相影响。那么我就不需要专门运送水果的船和专门运送化学品的船了。只要这些货物在集装箱里封装的好好的,那我就可以用一嫂大船把他们都运走。
docker就是类似的理念。
docker历史
越来越多的IT工程师发现了Docker的优点,然后蜂拥而至,加入Docker开源社区。
Docker的人气迅速攀升,速度之快,令人膛目结舌。
开源当月,Docker 0.1版本发布。此后的每一个月,Docker都会发布一个版本。到2014年6月9日,Docker 1.0版本正式发布。
此时的Docker,已经成为行业里人气最火爆的开源技术,没有之一。甚至像Google、微软、Amazon、VMware这样的巨头,都对它青睐有加,表示将全力支持。
Docker和容器技术为什么会这么火爆?说白了,就是因为它“轻”。
在容器技术之前,业界的网红是虚拟机。虚拟机技术的代表,是VMWare和Openstack.
相信很多人都用过虚拟机。虚拟机,就是在你的操作系统里面,装一个软件,然后通过这个软件,再模拟一台甚至多台“子电脑”出来。
在“子电脑“里,你可以和正常电脑一样运行程序,例如开QQ。如果你愿意,你可以变出好几个“子电脑”,里面都开上QQ。“子电脑“和“子电脑”之间,是相互隔离的,互不影响,
虚拟机属于虚拟化技术。而Docker这样的容器技术,也是虚拟化技术,属于轻量级的虚拟化。
虚拟机虽然可以隔离出很多“子电脑”,但占用空间更大,启动更慢,虚拟机软件可能还要花钱(例如VMWare)。
而容器技术恰好没有这些缺点。它不需要虚拟出整个操作系统,只需要虚拟一个小规模的环境(类似“沙箱”)。
它启动时间很快,几秒钟就能完成。而且,它对资源的利用率很高(一台主机可以同时运行几干个Docker容器)。此外,它占的空间
很小,虚拟机一般要几GB到几十GB的空间,而容器只需要MB级甚至KB级。
正因为如此,容器技术受到了热烈的欢迎和追捧,发展迅速。
Docker理念
Docker是基于Go语言实现的云开源项目。
Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。
Linux容器技术的出现就解决了这样一个问题,而Docker就是在它的基础上发展过来的。将应用运行在Docker容器上面,而
Docker容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
Docker能干嘛
虚拟机(virtual machine)就是带环境安装的一种解决方案。
它可以在一种操作系统里面运行另一种操作系统,比如在Windows系统里面运行Linux系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。
虚拟机的缺点:
- 占用资源多
- 冗余步骤多
- 奇动漫
容器虚拟化技术
由于前面虚拟机存在这些缺点,Linux发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为LXC)。
Linux容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
比较了Docker和传统虚拟化方式的不同之处:
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
每个容器之间互相隔离,每个容器有自己的文件系统,容器之间进程不会相互影响,能区分计算资源。
开发运维
更快速的应用交付和部署:
传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂的配置才能正常运行。Docker化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像井运行即可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间。
更便捷的升级和扩缩容:
随着微服务架构和Docker的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个Docker容器将变成一块物木”,应用的升级将变得非常容易,当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒级。
更简单的系统运维:
应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复。
更高效的计算资源利用:
Docker是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的Hypervisor [管理程序]支持,所以在一台物理机上可以运行很多个
容器实例,可大大提升物理服务器的CPU和内存的利用率。
docker解释
镜像(image):
Docker镜像(Image)就是一个只读的模板。镜像可以用来创建Docker容器,一个
镜像可以创建很多容器。就好似 Java中的类和对象,类就是镜像,容器就是对象!
容器(container):
Docker利用容器(container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。
它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。
可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。。
容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
仓库(repository):
仓库(Repository)是集中存放镜像文件的场所。
仓库(Repos itory)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 DockerHub(https://hub.docker.com/).存放了数量庞大的镜像供用户下载。
国内的公开仓库包括阿里云、网易云等
小结:
需要正确的理解仓储/镜像/容器这几个概念:
- Docker本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就似乎 image镜像文件。只有通过这个镜像文件才能生成Docker容器。image文件可以看作是容器的模板。Docker根据image文件生成容器的实例。同一个image文件,可以生成多个同时运行的容器实例。
- image文件生成的容器实例,本身也是一个文件,称为镜像文件。
- ·一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器
- 至于仓库,就是放了一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候从仓库中拉下来就可以了。
Docker环境安装
- 卸载旧的版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
- 安装
yum-utils
;
yum install -y yum-utils
- 为yum源添加docker仓库位置;
yum-config-manager --add-repo
https://download.docker.com/linux/centos/docker-ce.repoCopy to clipboardErrorCopied
#使用阿里云镜像地址
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 更新yum软件包索引
yum makecache fast
- 安装docker服务;
#docker-ce 是社区版,docker-ee 企业版
yum install -y docker-ce docker-ce-cli containerd.io
- 启动docker服务。
systemctl start docker
# 查看当前版本号,是否启动成功
docker version
# 设置开机自启动
systemctl enable docker
- 官网
https://docs.docker.com
类似mavenRepository
https://hub.docker.com/search?q=mysql
常用命令
基础命令
docker version #查看docker的版本信息
docker info #查看docker的系统信息,包括镜像和容器的数量
docker 命令 --help #帮助命令(可查看可选的参数)
docker COMMAND --help
命令的帮助文档地址:https://docs.docker.com/engine/reference/commandline/docker/
镜像命令
1.docker images 查看本地主机的所有镜像
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 11 months ago 13.3kB
#解释:
1.REPOSITORY 镜像的仓库源
2.TAG 镜像的标签
3.IMAGE ID 镜像的id
4.CREATED 镜像的创建时间
5.SIZE 镜像的大小
# 可选参数
-a/--all 列出所有镜像
-q/--quiet 只显示镜像的id
2.docker search 搜索镜像
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 10308 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3819 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create… 754 [OK]
percona Percona Server is a fork of the MySQL relati… 517 [OK]
centos/mysql-57-centos7 MySQL 5.7 SQL database server 86
mysql/mysql-cluster Experimental MySQL Cluster Docker images. Cr… 79
centurylink/mysql Image containing mysql. Optimized to be link… 60 [OK]
#可选参数
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
#搜索收藏数大于3000的镜像
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker search mysql --filter=STARS=3000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 10308 [OK]
mariadb MariaDB is a community-developed fordockerk of MyS… 3819 [OK]
3.docker pull 镜像名[:tag] 下载镜像
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker pull mysql
Using default tag: latest #如果不写tag默认就是latest
latest: Pulling from library/mysql
6ec7b7d162b2: Pull complete #分层下载,docker image的核心-联合文件系统
fedd960d3481: Pull complete
7ab947313861: Pull complete
64f92f19e638: Pull complete
3e80b17bff96: Pull complete
014e976799f9: Pull complete
59ae84fee1b3: Pull complete
ffe10de703ea: Pull complete
657af6d90c83: Pull complete
98bfb480322c: Pull complete
6aa3859c4789: Pull complete
1ed875d851ef: Pull complete
Digest: sha256:78800e6d3f1b230e35275145e657b82c3fb02a27b2d8e76aac2f5e90c1c30873 #签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest #下载来源的真实地址 #docker pull mysql等价于docker pull docker.io/library/mysql:latest
指定版本下载
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
6ec7b7d162b2: Already exists
fedd960d3481: Already exists
7ab947313861: Already exists
64f92f19e638: Already exists
3e80b17bff96: Already exists
014e976799f9: Already exists
59ae84fee1b3: Already exists
7d1da2a18e2e: Pull complete
301a28b700b9: Pull complete
529dc8dbeaf3: Pull complete
bc9d021dc13f: Pull complete
Digest: sha256:c3a567d3e3ad8b05dfce401ed08f0f6bf3f3b64cc17694979d5f2e5d78e10173
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
4.docker rmi 删除镜像
#1.删除指定的镜像id
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker rmi -f 镜像id
#2.删除多个镜像id
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker rmi -f 镜像id 镜像id 镜像id
#3.删除全部的镜像id
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker rmi -f $(docker images -aq)
容器命令
如拉取一个centos镜像
docker pull centos
运行容器的命令说明:
docker run [可选参数] image
#参数说明
--name="名字" 指定容器名字
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口
(
-p ip:主机端口:容器端口 配置主机端口映射到容器端口
-p 主机端口:容器端口
-p 容器端口
)
-P 随机指定端口(大写的P)
运行并进入容器centos
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker run -it centos /bin/bash
[root@bd1b8900c547 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
退出容器命令:
#exit 停止并退出容器(后台方式运行则仅退出)
#Ctrl+P+Q 不停止容器退出
#docker attach [container_name]进入容器 再次进入容器。 container_id 也可以
[root@bd1b8900c547 /]# exit
exit
[root@iZwz99sm8v95sckz8bd2c4Z ~]#
列出运行过的容器命令:
#docker ps
# 列出当前正在运行的容器
-a # 列出所有容器的运行记录
-n=? # 显示最近创建的n个容器
-q # 只显示容器的编号
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@iZwz99sm8v95sckz8bd2c4Z ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bca129320bb5 centos "/bin/bash" 4 minutes ago Exited (0) 3 minutes ago optimistic_shtern
bd1b8900c547 centos "/bin/bash" 6 minutes ago Exited (0) 5 minutes ago cool_tesla
cf6adbf1b506 bf756fb1ae65 "/hello" 5 hours ago Exited (0) 5 hours ago optimistic_darwin
删除容器命令:
docker rm 容器id #删除指定的容器,不能删除正在运行的容器,强制删除使用 rm -f
docker rm -f $(docker ps -aq) #删除所有的容器
docker ps -a -q|xargs docker rm #删除所有的容器
启动和停止容器命令:
docker start 容器id #启动容器
docker restart 容器id #重启容器
docker stop 容器id #停止当前运行的容器
docker kill 容器id #强制停止当前容器
常用的其他命令
日志的查看
docker logs -tf 容器id
docker logs --tail number 容器id #num为要显示的日志条数
查看容器中进程信息
docker top c703b5b1911f
查看容器的元数据
docker inspect 容器id
进入当前正在运行的容器
1、docker exec -it 容器id /bin/bash
2、docker attach 容器id
区别:
docker exec 进入容器后开启一个新的终端,可以在里面操作
docker attach 进入容器正在执行的终端,不会启动新的进程
拷贝操作
#拷贝容器的文件到主机中
docker cp 容器id:容器内路径 目的主机路径
#拷贝宿主机的文件到容器中
docker cp 目的主机路径 容器id:容器内路径
其他命令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
attach Attach to a running container #当前shell下attach连接指定运行镜像
build Build an image from a Dockerfile #通过Dockerfile定制镜像
commit Create a new image from a container's changes #提交当前容器为新的镜像
cp Copy files/folders from a container to a HOSTDIR or to STDOUT #从容器中拷贝指定文件或者目录到宿主机中
create Create a new container #创建一个新的容器,同run 但不启动容器
diff Inspect changes on a container's filesystem #查看docker容器变化
events Get real time events from the server#从docker服务获取容器实时事件
exec Run a command in a running container#在已存在的容器上运行命令
export Export a container's filesystem as a tar archive #导出容器的内容流作为一个tar归档文件(对应import)
history Show the history of an image #展示一个镜像形成历史
images List images #列出系统当前镜像
import Import the contents from a tarball to create a filesystem image #从tar包中的内容创建一个新的文件系统映像(对应export)
info Display system-wide information #显示系统相关信息
inspect Return low-level information on a container or image #查看容器详细信息
kill Kill a running container #kill指定docker容器
load Load an image from a tar archive or STDIN #从一个tar包中加载一个镜像(对应save)
login Register or log in to a Docker registry#注册或者登陆一个docker源服务器
logout Log out from a Docker registry #从当前Docker registry退出
logs Fetch the logs of a container #输出当前容器日志信息
pause Pause all processes within a container#暂停容器
port List port mappings or a specific mapping for the CONTAINER #查看映射端口对应的容器内部源端口
ps List containers #列出容器列表
pull Pull an image or a repository from a registry #从docker镜像源服务器拉取指定镜像或者库镜像
push Push an image or a repository to a registry #推送指定镜像或者库镜像至docker源服务器
rename Rename a container #重命名容器
restart Restart a running container #重启运行的容器
rm Remove one or more containers #移除一个或者多个容器
rmi Remove one or more images #移除一个或多个镜像(无容器使用该镜像才可以删除,否则需要删除相关容器才可以继续或者-f强制删除)
run Run a command in a new container #创建一个新的容器并运行一个命令
save Save an image(s) to a tar archive#保存一个镜像为一个tar包(对应load)
search Search the Docker Hub for images #在docker hub中搜索镜像
start Start one or more stopped containers#启动容器
stats Display a live stream of container(s) resource usage statistics #统计容器使用资源
stop Stop a running container #停止容器
tag Tag an image into a repository #给源中镜像打标签
top Display the running processes of a container #查看容器中运行的进程信息
unpause Unpause all processes within a container #取消暂停容器
version Show the Docker version information#查看容器版本号
wait Block until a container stops, then print its exit code #截取容器停止时的退出状态值
Docker安装软件
安装nginx
# 搜索镜像
# dockerhub https://hub.docker.com/_/nginx
docker search nginx
# 下载镜像
docker pull nginx
# 运行
## -d 后台运行
## --name 给容器命名
## -p 宿主机端口:容器端口
docker run -d --name nginx01 -p 3344:80 nginx
# 测试
docker ps
crul localhost:3344
# 其他
whereis nginx #linux查找配置文件路径
#挂载文件,需要删除一次容器重新创建容器
## 安装上面执行过一次的容器。在宿主机执行
docker cp nginx:/etc/nginx/nginx.conf /home/nginx/conf/
docker cp nginx:/etc/nginx/conf.d/default.conf /home/nginx/conf/
docker cp nginx:/usr/share/nginx/html /home/nginx/
## 重新创建容器,并挂载文件
docker run \
-p 8070:80 \
--name my-nginx \
-v /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /home/nginx/conf/default.conf:/etc/nginx/conf.d/default.conf \
-v /home/nginx/html:/usr/share/nginx/html \
-d nginx
安装tomcat
# 搜索镜像
docker search tomcat
# 下载镜像 指定版本
docker pull tomcat:8.5
#复制完整webapp 镜像问题 默认最小镜像,不必要的都会删掉
docker exec -it tomcat01 /bin/bash #进入tomcat
cp -r webapps.dist/* webapps #复制
# 运行
## -d 后台运行
## --name 给容器命名
## -p 宿主机端口:容器端口
docker run -d --name tomcat01 -p 3355:8080 tomcat:8.5
# 测试
docker ps
http://ip:3355
安装es
#查看资源占用情况 容器id可有可无
docker stats 容器id
#映射多个端口
docker run -d --name tomcat01 -p 3355:8080 -p 3356:8081 tomcat:8.5
#ES 添加内存限制 -e
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms128m -Xmx512m" elasticsearch:7.6.2
安装Portaniner(可视化工具)
#安装 -v挂在到本地配置文件
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
#测试
http://139.196.87.39:8088
安装mysql
# 搜索镜像
docker search mysql
# 下载镜像 指定版本
docker pull mysql:5.5
# 运行
## -d 后台运行
## --name 给容器命名
## -p 宿主机端口:容器端口
## -v 挂在文件
docker run --name mysql01 -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
# 测试 sqlyong连接
# 如果密码错误删除宿主机挂载文件夹下的文件,删除容器重新运行启动
==========简化============
docker run -p 3310:3306 --name mysql01 \
-v /myData/software/docker/mysql01/log:/var/log/mysql \
-v /myData/software/docker/mysql01/data:/var/lib/mysql \
-v /myData/software/docker/mysql01/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
Docker镜像详解
什么是镜像
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容,包括代码,运行时(一个程序在运行或者在被执行的依赖)、库,环境变量和配置文件。
Docker镜像加载原理
Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统是UnionFS联合文件系统。
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtualfilesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
我的理解:假如说安装linux7、linux7,第一次安装linux7,第二次安装linux8,第一次安装需要linux7的一些配置文件,直接在7里面拿而不需要下载了
分层理解
思考:为什么Docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。 查看镜像分层的方式可以通过 docker image inspect 命令!
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
特点
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
提交镜像
使用docker commit 命令提交容器成为一个新的版本
docker commit -a="作者" -m="提交的描述信息" 容器id 目标镜像名:[TAG]
容器数据卷详解
什么是容器数据卷
Docker将运用与运行的环境打包形成容器运行, Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来, 那么当容器删除后,数据自然也就没有了。 为了能保存数据在Docker中我们使用卷。|
卷就是目录或文件,存在于一个或多个容器中,由Docker挂载到容器,但卷不属于联合文件系统(Union FileSystem),因此能够绕过联合文件系统提供一些用于持续存储或共享数据的特性:。
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
docker的理念回顾
- 将应用和环境打包成一个镜像!
- 数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!需求:数据可以持久化
- MySQL,容器删了,删库跑路!需求:MySQL数据可以存储在本地!
- 容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
- 这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Linux上面!
总结:容器和主机共享目录。
数据卷的特点:
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可以直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
使用数据卷
#运行时配置命令 -v
docker run -it -v 主机目录:容器目录 容器id /bin/bash
#验证
docker inspect 容器id
mounts写的挂在信息
具名和匿名挂载
具名挂载
就是指定文件夹名称,区别于指定路径挂载,这里的指定文件夹名称是在Docker指定的默认数据卷路径下的。通过docker volume ls命令可以查看当前数据卷的目录情况
查看指定的数据卷信息的命令:docker volume inspect 数据卷名称
匿名挂载
匿名挂载就是在指定数据卷的时候,不指定容器路径对应的主机路径,这样对应映射的主机路径就是默认的路径/var/lib/docker/volumes/中自动生成一个随机命名的文件夹。
区别
所有的docker容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumes/xxxx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况在使用的
具名挂载
# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载!
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载!
拓展:
# 通过-v客器内路径:ro rw改变读写权限
ro readonly # 只读
rw readwrite # 可读可写
# 一旦这个了设置了容器权限,容器对我们挂载出来的内容就有限定了!
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
# ro只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!
初始DockerFile
Dockerfile 就是用来构建docker 镜像的构建文件!命令脚本!先体验一下!
通过这个脚本可以生成镜像,镜像是一层一层的,脚本一个个的命令,每个命令都是一层!
# 创建一个dockerfile又件,名字可以随机建议 Dockerfile#文件中的内容指令(大写)参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "----end-----"
CMD /bin/bash
# 这里的每个命令,就是镜像的一层!
# 生成镜像
## -f 配置文件路径
## -t 新的镜像名称(名称:版本) 注:镜像名称前不能加/
## . 最后的"."也是命令
docker build -f /home/dockerfile01 -t chenshaohua/centos:1.0 .
数据卷容器
多个MySQL同步数据
5、图片
# 启动3个容器
## docker01与主机实现同步 使用dockerFile
docker run -d --name docker01 chenshaohua/centos
## docker02 --volume-from docker01
docker run -d --name docker02 --volume-from docker01 chenshaohua/centos
## docker03 --volume-from docker01
docker run -d --name docker03 --volume-from docker01 chenshaohua/centos
#多个mysql实现挂载
docker run --name mysql01 -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
docker run --name mysql02 -p 3311:3306 --volume-from mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用位置.
但是一旦你持久化到本地,这个时候本地的数据是不会删除的!
DockerFile
DockerFile介绍
dockerfile是用来构建dokcer镜像的文件!命令参数脚本!
构建步骤:
- 编写一个dockerfile 文件
- docker build 构建成为一个镜像
- docker run运行镜像
- docker push 发布镜像(DockerHub、阿里云镜像仓库!)
查看一下官方是怎么做的?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
DockerFile构建过程
基础知识:
- 每个保留关键字(指令)都是必须是大写字母
- 执行从上到下顺序执行
- #表示注释
- 每一个指令都会创建提交一个新的镜像层,并提交!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单!
Docker镜像逐渐成为企业交付的标准,必须要掌握!
步骤:开发,部署,运维。。。缺一不可!
DockerFile:构建文件,定义了一切的步骤,源代码
Dockerlmages:通过 DockerFile 构建生成的镜像,最终发布和运行的产品!
Docker容器:容器就是镜像运行起来提供服务器
DockerFile构建指令
以前的话我们就是使用别人的,现在我们知道了这些指令后,我们来练习自己写一个镜像!
FROM #基础镜镜像,一切从这里开始构建
MAINTAINER #镜像是谁写的,姓名+邮箱
RUN #镜像构建的时候需要运行的命令
ADD #步骤:tomcat镜像,这个tomcat压缩包!添加内容
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPOSE #保留端口配置
CMD #指定这个容器启动的时侯要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT #指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD #当构建一个被继承 DockerFile这个时候就会运行 ONBUILD 的指令。触发指令。
COPY #类似ADD,将我们文件拷贝到镜像中
ENV #构建的时候设置环境变量!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
实战测试(制作centos:7镜像)
制作centos:7镜像
#begin 创建dockerFile文件
FROM centos:7
MAINTAINER chenshaohua<1050449218@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD /bin/bash
#end
#构造新的镜像
docker build -f dockerFile-centos -t mycentos:0.1 .
#测试
docker run -it mycentos:0.1
ls ll ifconfig
查看镜像历史记录,看官方怎么构建
docker history 镜像id
RUN命令与CMD命令的区别在哪里?
简单说,RUN命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;CMD命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个RUN命令,但是只能有一个CMD命令。
注意,指定了CMD命令以后,docker container run命令就不能附加命令了(比如前面的/bin/bash),否则它会覆盖CMD命令。
CMD和ENTRYPOINT的区别在哪里?
- CMD :指定容器启动的时候要运行的命令,只有最后一个会生效
- ENTRYPOINT :指定容器启动的时候要运行的命令,命令可以追加
实战测试(制作Tomcat镜像)
1、准备镜像文件apache-tomcat-8.5.78.tar.gz、jdk-8u261-linux-x64.tar.gz压缩包
2、编写dockerFile文件
文件名起 Dockerfile build不用加 -f 指定 官网默认找这个文件
FROM centos:7
MAINTAINER chenshaohua<1050449218@qq.com>
COPY readme.txt /usr/local/readme.txt
#自动解压
ADD jdk-8u261-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-8.5.78.tar.gz /usr/local/
RUN yum -y install vim
#工作目录
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_261
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
#配置tomcat环境变量
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.78
ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.78
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#暴露端口
EXPOSE 8080
#执行命令
CMD /usr/local/apache-tomcat-8.5.78/bin/startup.sh && tail -F /usr/local/apache-tomcat-8.5.78/bin/logs/catalina.out
3、构建镜像
[root@MiWiFi-RA81-srv home]# docker build -t diytomcat .
4、启动镜像
docker run -d -p 9090:8080 --name diytomcat -v /home/dockerfile/tomcat/test:/usr/local/apache-tomcat-8.5.78/webapps/test -v /home/dockerfile/tomcat/logs/:/usr/local/apache-tomcat-8.5.78/logs diytomcat
5、访问测试
http://192.168.1.126:9090/
6、发布项目
这里设置了数据卷,宿主机的/home/dockerfile/tomcat/test对应该容器的/usr/local/apache-tomcat-8.5.55/webapps/test。这样关于test项目的修复只需要在宿主机上修改就可以了,不需要进入到容器中修改。
#在/home/dockerfile/tomcat/test的目录下,新建index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>这是个标题</title>
</head>
<body>
<h1>这是一个一个简单的HTML</h1>
<p>Hello World!</p>
</body>
</html>
#测试
http://192.168.1.126:9090/test/
##如果页面显示乱码,就需要修改tomcat的server.xml文件
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8" />
发布镜像到阿里云
- 登录阿里云,找到容器镜像服务
- 创建命名空间
- 创建镜像空间(选择本地服务)
- 创建镜像空间阿里云有操作指南
小结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Docker网络
理解Docker网络
#创建tomacat容器 启动 并执行 ip addr
docker run --name tomcat01 -p 8080:8080 tomcat
docker exec -it tomcat01 ip addr
#查询宿主机 docker网络 与tomcat容器网络ping
原理
1、我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一介网卡dockero桥接模式,使用的技术是veth-pair技术!
2、veth-pair
#我们发现这个容器带来网卡,都是一对对的
#evth-pair就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
#、正因为有这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备的
#openstac,Docker容器之间的连接,ovs的连接,都是使用 veth-pair技术
3、容器之间测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
结论:tomcat01、tomcat02是共用一个路由器
所有容器不指定网络的情况下,都是docker0路由分配一个默认可用的ip
核心网络原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Docker中的所有网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件!)
只要容器删除,对应网络桥接一对就消失了!
–Link
思考一个场景,我们编写了一个微服务,database url=ip:,项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以名字来进行访问容器?
#启动2个tomcat 01 02
#通过容器名尝试ping
[root@MiWiFi-RA81-srv ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: No address associated with hostname
#如何解决呢?
##--link可以解决(注:单向的)
[root@MiWiFi-RA81-srv ~]# docker run -d --name tomcat03 --link tomcat02 mytomcat:1.0
80475d4b31170527c2e4a88c8c53ca9b43ee0657e3e9a65814c4e669f38cfb16
[root@MiWiFi-RA81-srv ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.5) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.5): icmp_seq=1 ttl=64 time=0.073 ms
64 bytes from tomcat02 (172.17.0.5): icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from tomcat02 (172.17.0.5): icmp_seq=3 ttl=64 time=0.045 ms
##原理
###查看docker网络
docker network ls
docker network inspect id
###查看容器绑定link(找link)
###--link就是在hosts假如了映射
1、docker inspect 容器id
2、docker inspect 97ec4ba0656d|grep tomcat02 -n
3、进入容器查看 /etc/hosts
172.17.0.5 tomcat02 fd273a7550ee
我们现在玩Doker不建议使用–link
现在使用自定义网络!
docker0不支持容器名连接访问!
自定义网络
查看所有的dcoker网络
docker network ls
网络模式
- bridge:桥接 docker(默认,自己创建也使用bridge模式)
- none:不配置网络
- host:和宿主机共享网络
- container:容器网络连通!(用的少!局限很大)
测试
#直接启动run 默认就是带--net bridge
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat
#创建网络
##--driver bridge #指定bridge驱动程序来管理网络
##--subnet 192.168.0.0/16 #指定网段的CIDR格式的子网
##--gateway 192.168.0.1 #指定主子网的IPv4或IPv6网关
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mybridge
#查看网络
docker network ls
docker network inspect 网络id
#启动2个容器 指定创建的网络
docker run -d -P --name tomcat-net01 --net mynet mytomcat:1.0
docker run -d -P --name tomcat-net02 --net mynet mytomcat:1.0
#测试2个容器网络
docker network inspect 网络id
docker exec -it tomcat-net01 ping 192.168.0.3
docker exec -it tomcat-net01 ping tomcat-net02
可以发现,在我们的自定义网络下,容器之间既可以通过容器名也可以通过ip地址进行网络通信。 我们自定义的网络默认已经帮我们维护了容器间的网络通信问题,这是实现网络互联的推荐方式。
好处:
redis:不同的集群使用不同的网络,保证集群是安全和健康的
mysql:不同的集群使用不同的网络,保证集群是安全和健康的
网络连通
把不同的网络打通(自定义网络与默认网络打通)
#测试打通
docker network connect mybridge tomcat03
#连通之后就是讲tomcat03放到mybridge网络下
#一个容器两个ip地址(阿里云公网ip私网ip)
docker network inspect 5bd453665e69
#测试
[root@MiWiFi-RA81-srv ~]# docker exec -it tomcat-net01 ping tomcat03
PING tomcat03 (192.168.0.4) 56(84) bytes of data.
64 bytes from tomcat03.mynet (192.168.0.4): icmp_seq=1 ttl=64 time=0.259 ms
64 bytes from tomcat03.mynet (192.168.0.4): icmp_seq=2 ttl=64 time=0.075 ms
结论:假设要跨网络操作别人,就需要使用docker network connect连通!
实战:部署Redis集群
三主三从的Redis集群
#创建redis网络
docker network create redis --subnet 172.38.0.0/16
#通过shell脚本创建6个redis配置文件信息
for port in $(seq 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
#通过shell脚本创建并运行6级redis
for port in $(seq 1 6); \
do
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
done
#进入redis01创建集群
docker exec -it redis01 /bin/sh
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
#查看集群信息
redis-cli -c
cluster info
cluster nodes #集群节点信息
实战:SpringBoot项目打包镜像
#构建SpringBoot项目
#打包jar
#编写dockerFile
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
#上传到服务器(jar、dockeFile)
#构建镜像
docker build -t demo .
#发布运行
docker run
Docker Compose
简介
Docker Compose来轻松高效的管理容器,定义运行多个容器。
Compose是Docker官方的开源项目,需要安装
DockerFile让程序在任何地方运行。web服务、redis、mysql多个容器。
如果一个一个启动就需要run3个,而Compose只需要编写一个yml文件就可以启动多个。
version: "3.9" # optional since v1.27.0
services:
web:
build: .
ports:
- "8000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
docker-compose up 100个服务。
Compose:重要的概念。
- 服务services, 容器。应用。(web、redis、mysql.…)
- 项目project。一组关联的容器。博客。web
安装
#下载地址
curl -L https://get.daocloud.io/docker/compose/releases/download/v2.5.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
#赋权
sudo chmod +x /usr/local/bin/docker-compose
#验证
docker-compose version
测试示例
官网教程
地址:https://docs.docker.com/compose/gettingstarted/
Docker Compose --> Getting started
- 为项目创建一个目录:
mkdir composetest
cd composetest
- 在项目目录中创建一个名为的文件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)
- 在您的项目目录中创建另一个名为的文件requirements.txt并将其粘贴到:
flask
redis
- 创建 Dockerfile
# syntax=docker/dockerfile:1
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"]
这告诉 Docker:
- 从 Python 3.7 映像开始构建映像。
- 将工作目录设置为/code.
- 设置命令使用的环境变量flask。
- 安装 gcc 和其他依赖项
- 复制requirements.txt并安装 Python 依赖项。
- 向镜像添加元数据以描述容器正在侦听端口 5000
- 将项目中的当前目录复制.到镜像中的workdir .。
- 将容器的默认命令设置为flask run.
5.在 Compose 文件中定义服务
这个 Compose 文件定义了两个服务:web和redis.
version: "3.9"
services:
web:
build: .
ports:
- "8000:5000"
redis:
image: "redis:alpine"
6.使用 Compose 构建并运行您的应用程序
docker-compose up
7.测试
http://localhost:8000
默认的服务名 文件名_服务名_num
多个服务器。集群。AB_num副本数量
服务redis服务=>4个副本。
集群状态。服务都不可能只有一个运行实例。弹性、10HA高井发。
kubectl service负载均衡。
8.停止
#在/usr/local/bin/docker-compose目录下执行
docker-compose down
#暴力停止
ctrl+c
docker-compose小结
以前都是单个docker run启动容器。
docker-compose。通过 docker-compose 编写yaml配置文件、可以通过compose一键启动所有服务,停止。!
Docker小结
- Docker镜像。run=>容器
- DockerFile构建镜像(服务打包)
- docker-compose启动项目(编排、多个微服务/环境)
- Docker 网络!