问题:
Docker 解决:
![image.png](https://img-blog.csdnimg.cn/img_convert/990ef0d09a015b769428ba8b44f8d5c5.png#averageHue=#f5e1dd&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=723&id=ufebe826f&margin=[object Object]&name=image.png&originHeight=976&originWidth=1415&originalType=binary&ratio=1&rotation=0&showTitle=false&size=216325&status=done&style=none&taskId=uaa3034f1-0fc1-4eb6-9b97-ee32ab4c274&title=&width=1048.148222192003)
这样打包好的应用包中,既包含应用本身,也保护应用所需要的Libs、Deps,无需再操作系统上安装这些,自然就不存在不同应用之间的兼容问题了
Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?
Docker如何解决开发、测试、生产环境有差异的问题?
Docker是一个快速交付应用、运行应用的技术,具备下列优势:
Docker 中的一些概念
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔离,对外不可见
![image.png](https://img-blog.csdnimg.cn/img_convert/a716b645180224a5074eddc1f9f79471.png#averageHue=#dce1d0&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=555&id=u534a8222&margin=[object Object]&name=image.png&originHeight=749&originWidth=1432&originalType=binary&ratio=1&rotation=0&showTitle=false&size=240925&status=done&style=none&taskId=u1e855c4b-cabf-4553-8ad6-526e0fe1eed&title=&width=1060.7408156741683)
DockerHub:DockerHub 是一个官方的 Docker 镜像的托管平台。这样的平台称为 Docker Registry
![image.png](https://img-blog.csdnimg.cn/img_convert/7de709821585b88b40aec2bc3b5c338c.png#averageHue=#fcf9f8&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=452&id=u30251f4b&margin=[object Object]&name=image.png&originHeight=610&originWidth=1459&originalType=binary&ratio=1&rotation=0&showTitle=false&size=117463&status=done&style=none&taskId=u9842b0af-aca3-48b8-869e-cce2b93986c&title=&width=1080.7408170870192)
Docker 是一个 CS 架构的程序,由两部分组成:
![image.png](https://img-blog.csdnimg.cn/img_convert/c6e8cfb8d0004ab071930f45affb287a.png#averageHue=#dbab52&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=428&id=uc46874c2&margin=[object Object]&name=image.png&originHeight=578&originWidth=1443&originalType=binary&ratio=1&rotation=0&showTitle=false&size=215211&status=done&style=none&taskId=uf9196356-0bda-4363-bac8-66187e97e89&title=&width=1068.8889643979223)
首先来看下镜像的名称组成
常见的镜像命令如下图所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/e635208b2e26112ceb468a15ad24ab86.png#averageHue=#faf8f6&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=544&id=uc2aaf58d&margin=[object Object]&name=image.png&originHeight=735&originWidth=1475&originalType=binary&ratio=1&rotation=0&showTitle=false&size=183382&status=done&style=none&taskId=u6e53ce3b-1554-402a-8625-8ea749b1758&title=&width=1092.592669776116)
Tip:利用 docker xx --help 命令查看相关命令语法
常见容器命令如下图所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/4b5ba9e123ec5668bfe9209b0f5900f2.png#averageHue=#ac752a&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=519&id=u20a27398&margin=[object Object]&name=image.png&originHeight=700&originWidth=1455&originalType=binary&ratio=1&rotation=0&showTitle=false&size=150394&status=done&style=none&taskId=ue780ae7a-8323-48f0-9cea-ea6cc4aa9a1&title=&width=1077.777853914745)
容器保护三个状态:
查看容器日志的命令:
查看容器状态:
案例:创建并运行一个容器
创建并运行nginx容器的命令:
docker run --name mn1 -p 80:80 -d nginx
命令解读:
我们还可以进入容器进入修改相关文件(了解)
docker exec -it mn1 bash
命令解读:
在之前的 nginx 案例中,修改 nginx 的 html 页面时,需要进入 nginx 内部。并且因为没有编辑器,修改文件也很麻烦。这就是因为容器与数据(容器内文件)耦合带来的后果
![image.png](https://img-blog.csdnimg.cn/img_convert/d227f2c1b547a70ab3fc97784aec2818.png#averageHue=#f6f4f4&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=379&id=ud1b8c7da&margin=[object Object]&name=image.png&originHeight=512&originWidth=1464&originalType=binary&ratio=1&rotation=0&showTitle=false&size=142135&status=done&style=none&taskId=ud327c33a-3903-4879-9bc6-bb1c4e19659&title=&width=1084.444521052362)
要解决这个问题,必须将数据与容器解耦,这就要用到数据卷了
实现数据与容器分离的方式:
![image.png](https://img-blog.csdnimg.cn/img_convert/ad0eee7cc8e779be4da2a2765b3e76fb.png#averageHue=#fefcfb&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=583&id=u115b2b4f&margin=[object Object]&name=image.png&originHeight=787&originWidth=1464&originalType=binary&ratio=1&rotation=0&showTitle=false&size=294880&status=done&style=none&taskId=uf9411b94-54c6-47d2-9e8d-8201abd20e7&title=&width=1084.444521052362)
接下来我们使用三种方式进行使用:
绑定文件夹:
我们首先在我们的宿主机下创建一个文件,此时该文件下为空目录,如下所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/e2a6c15e95ac24d31dae3036c84b7197.png#averageHue=#1b1a19&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=85&id=u39397ba8&margin=[object Object]&name=image.png&originHeight=115&originWidth=914&originalType=binary&ratio=1&rotation=0&showTitle=false&size=8249&status=done&style=none&taskId=u3a339474-d435-45c5-9544-1dfbe4180d8&title=&width=677.0370848646577)
然后我们使用绑定文件夹的方式运行容器,如下所示:
docker run \
--name mn1 \
-d \
-p 82:80 \
-v /tmp/nginx/html:/usr/share/nginx/html \
nginx
然后我们访问,如下所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/153decda8eecdfb6a20596d050bcad31.png#averageHue=#fbfaf9&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=231&id=ucc0b7902&margin=[object Object]&name=image.png&originHeight=312&originWidth=1830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=37360&status=done&style=none&taskId=u41514df9-2855-450e-a7c4-79f08fa4b7d&title=&width=1355.5556513154525)
此时我们发现 403 Forbidden ,WHY???
这是因为我们使用绑定文件夹方式的话,数据是以宿主机的文件夹内容为主
当我们在宿主机下添加一个 index.html 后,我们再次进行访问,如下所示
![image.png](https://img-blog.csdnimg.cn/img_convert/a33cd2b17a1afbcbadfa3a99731f9a42.png#averageHue=#060504&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=56&id=ubf20e586&margin=[object Object]&name=image.png&originHeight=76&originWidth=884&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4995&status=done&style=none&taskId=u2f667552-05eb-49e6-89a8-c1a2270a75d&title=&width=654.8148610726012)
![image.png](https://img-blog.csdnimg.cn/img_convert/17cbd82c136aade7d276bdc4054d2e7e.png#averageHue=#f6f6f5&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=188&id=u397a7f5a&margin=[object Object]&name=image.png&originHeight=254&originWidth=1519&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24718&status=done&style=none&taskId=u662cb60d-8c7c-4784-a209-2b0799debdd&title=&width=1125.1852646711325)
绑定文件:
绑定文件的方式与绑定文件夹的方式类似,不过我们在编写命令时将对应的文件夹改为文件,如下所示:
docker run \
--name mn1 \
-d \
-p 83:80 \
-v /tmp/nginx/html/index.html:/usr/share/nginx/html/index.html \
nginx
这里依然是以宿主机的文件内容为主
绑定数据卷:
**数据卷(volume)**是一个虚拟目录,指向宿主机文件系统中的某个目录
![image.png](https://img-blog.csdnimg.cn/img_convert/fc77ecb745ae23dbb909170c69a72ece.png#averageHue=#f8ceaa&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=502&id=u81026b9e&margin=[object Object]&name=image.png&originHeight=678&originWidth=1430&originalType=binary&ratio=1&rotation=0&showTitle=false&size=179819&status=done&style=none&taskId=u272b4ada-4b7f-4512-9f93-e80fae81c3a&title=&width=1059.2593340880312)
一旦完成数据卷挂载,对容器的一切操作都会作用在数据卷对应的宿主机目录了
这样,我们操作宿主机的 /var/lib/docker/volumes/html 目录,就等于操作容器的 /usr/share/nginx/html 目录了
数据卷操作的基本语法如下:
docker volume [COMMAND]
docker volume 命令是数据卷操作,根据命令后跟随的 command 来确定下一步的操作:
**案例:**给 nginx 挂载数据卷
docker volume create html
docker volume ls
![image.png](https://img-blog.csdnimg.cn/img_convert/33e1ad914da631ffd67f0301d37bf2fa.png#averageHue=#090806&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=88&id=udaea2eed&margin=[object Object]&name=image.png&originHeight=119&originWidth=1073&originalType=binary&ratio=1&rotation=0&showTitle=false&size=10333&status=done&style=none&taskId=u9f1e158f-b628-4644-95c6-3ffd49693e3&title=&width=794.8148709625577)
docker volume inspect html
![image.png](https://img-blog.csdnimg.cn/img_convert/5b55494324b4dca7df8894cb08c09052.png#averageHue=#040303&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=216&id=u01c371d6&margin=[object Object]&name=image.png&originHeight=292&originWidth=1072&originalType=binary&ratio=1&rotation=0&showTitle=false&size=25555&status=done&style=none&taskId=ubc101a6a-2786-427c-beff-55e84eb2bf7&title=&width=794.0741301694891)
我们创建的 html 这个数据卷关联的宿主机目录为/var/lib/docker/volumes/html/_data
目录
并且此时该文件下的内容为空
![image.png](https://img-blog.csdnimg.cn/img_convert/646341fafa08a9acc043c18aba2c4732.png#averageHue=#090706&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=134&id=u6d5ce115&margin=[object Object]&name=image.png&originHeight=181&originWidth=1002&originalType=binary&ratio=1&rotation=0&showTitle=false&size=16656&status=done&style=none&taskId=u1882eab5-a5ef-4ec2-b421-22022ce7315&title=&width=742.2222746546904)
docker run \
--name mn2 \
-d \
-p 83:80 \
-v html:/usr/share/nginx/html
nginx
然后我们访问即可,此时数据是以容器内的为主,如下所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/c35ef7f8dad82f0bd33936dd0fa23cff.png#averageHue=#f8f8f7&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=352&id=ub6541601&margin=[object Object]&name=image.png&originHeight=475&originWidth=1696&originalType=binary&ratio=1&rotation=0&showTitle=false&size=73914&status=done&style=none&taskId=udab31fa8-c381-46e8-9dfd-039348e8d58&title=&width=1256.2963850442663)
然后我们再查看数据卷目录,如下所示,会发现多了 index.html 和 50x.html 两个文件
![image.png](https://img-blog.csdnimg.cn/img_convert/e97004e39633754f177b5c289fdfa347.png#averageHue=#050403&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=73&id=u08c43abb&margin=[object Object]&name=image.png&originHeight=98&originWidth=1021&originalType=binary&ratio=1&rotation=0&showTitle=false&size=5769&status=done&style=none&taskId=u4f2fc6c7-8dbf-4bf8-93b8-0763621e1b0&title=&width=756.2963497229929)
![image.png](https://img-blog.csdnimg.cn/img_convert/e7078c8d4abe6d4caa97e0f5936471b9.png#averageHue=#f7f7f6&clientId=ubcad8047-46b2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=363&id=u1a83c4ab&margin=[object Object]&name=image.png&originHeight=490&originWidth=1992&originalType=binary&ratio=1&rotation=0&showTitle=false&size=80097&status=done&style=none&taskId=u13ea87ee-3752-4f1a-8643-2721f28c230&title=&width=1475.5556597925581)
通过上述案例可以实现了容器与数据分离
总结:
docker run 的命令中通过 -v 参数挂载文件或目录到容器中:
数据卷挂载与目录直接挂载的
镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
我们以 MySQL 为例,来看看镜像的组成结构:
![image.png](https://img-blog.csdnimg.cn/img_convert/fae371eb3e874da12e8cff91b11b600e.png#averageHue=#c7ae8d&clientId=u8c786143-1287-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=616&id=u8290ca61&margin=[object Object]&name=image.png&originHeight=832&originWidth=1456&originalType=binary&ratio=1&rotation=0&showTitle=false&size=321532&status=done&style=none&taskId=u42c69d0e-5ce5-43e0-be2a-758a41613bb&title=&width=1078.5185947078137)
简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件
我们要构建镜像,其实就是实现上述打包的过程
构建自定义的镜像时,并不需要一个个文件去拷贝,打包。
我们只需要告诉 Docker,我们的镜像的组成,需要哪些 BaseImage、需要拷贝什么文件、需要安装什么依赖、启动脚本是什么,将来 Docker 会帮助我们构建镜像。
而描述上述信息的文件就是Dockerfile文件。
Dockerfile 就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。
这里给出一些常见的的指令,更多指令可去官网查看
![image.png](https://img-blog.csdnimg.cn/img_convert/ca7496c991260928da17d8c134590fc1.png#averageHue=#d0bcb9&clientId=u8c786143-1287-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=414&id=u6f933ddd&margin=[object Object]&name=image.png&originHeight=559&originWidth=1428&originalType=binary&ratio=1&rotation=0&showTitle=false&size=170614&status=done&style=none&taskId=u87396d41-bf2d-45a8-8f64-63691ab2d74&title=&width=1057.7778525018941)
官网地址:https://docs.docker.com/engine/reference/builder
这里先给出我们的项目文件,如下所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/57091b2b07df363873eb056eea39f968.png#averageHue=#fbfbfb&clientId=u8c786143-1287-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=204&id=uca05111a&margin=[object Object]&name=image.png&originHeight=275&originWidth=1454&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24656&status=done&style=none&taskId=u09d28aed-0416-4eec-b01b-975385e5af5&title=&width=1077.0371131216764)
Dockerfile 文件如下所示:
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
将上述文件上传到虚拟机目录下,然后进入 docker-demo 目录下,运行命令:
docker build -t javaweb:1.0 .
![image.png](https://img-blog.csdnimg.cn/img_convert/d412368d5ce67b559e65d99324782c30.png#averageHue=#0e0c0a&clientId=u8c786143-1287-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=124&id=u617f1fc7&margin=[object Object]&name=image.png&originHeight=168&originWidth=996&originalType=binary&ratio=1&rotation=0&showTitle=false&size=17830&status=done&style=none&taskId=u15a4fdec-7538-4ad8-9298-e16dcd0d1d8&title=&width=737.7778298962791)
可以发现我们的镜像多了两个,然后我们将该镜像运行即可,输入以下命令:
docker run \
--name javaweb1.0 \
-d \
-p 8090:8090 \
javaweb:1.0
![image.png](https://img-blog.csdnimg.cn/img_convert/adedd289f20fde476712450e580ef2a0.png#averageHue=#080605&clientId=u8c786143-1287-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=161&id=ua3509a28&margin=[object Object]&name=image.png&originHeight=218&originWidth=1541&originalType=binary&ratio=1&rotation=0&showTitle=false&size=22692&status=done&style=none&taskId=ue8dd9686-8d1a-4237-9bd3-48801b5392c&title=&width=1141.4815621186406)
然后访问 http://192.168.80.128:8090/hello/count 即可,访问成功
此时我们的项目文件如下所示:
![image.png](https://img-blog.csdnimg.cn/img_convert/44a9370c1a74b3ce285ec8e9aea302c8.png#averageHue=#fbfafa&clientId=u8c786143-1287-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=170&id=u08c49df8&margin=[object Object]&name=image.png&originHeight=230&originWidth=1354&originalType=binary&ratio=1&rotation=0&showTitle=false&size=20000&status=done&style=none&taskId=u074b99b4-130a-42f1-846c-361fcbb588f&title=&width=1002.9630338148212)
我们的 Dockerfile 如下所示:
# 指定基础镜像
FROM java:8-alpine
# 拷贝 java 项目的包
COPY ./docker-demo.jar /tmp/app.jar
# 暴露端口
EXPOSE 8090
# 入口,java 项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
然后把文件拷贝到 虚拟机目录下,然后构建镜像
![image.png](https://img-blog.csdnimg.cn/img_convert/7a2fbe23006d1e00b3711945a7cc0e05.png#averageHue=#090706&clientId=u8c786143-1287-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=564&id=u9080f215&margin=[object Object]&name=image.png&originHeight=762&originWidth=1222&originalType=binary&ratio=1&rotation=0&showTitle=false&size=82534&status=done&style=none&taskId=u64680c41-bd0a-445c-83a5-776517e7243&title=&width=905.1852491297722)
然后我们将镜像运行即可,这种方式是我们常见的一种部署方式
更多知识在我的语雀知识库:https://www.yuque.com/ambition-bcpii/muziteng