Docker可以理解为自带环境的软件系统
比较Docker和虚拟机技术的不同
- 传统虚拟机,虚拟出一条硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件
- 容器内的应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
- 每个容器间是互相隔离,每个容器内都有一个属于自己的文件系统,互不影响
DevOps(开发、运维)
应用更快速的交付和部署
传统:一堆帮助文档,安装程序
Docker:打包镜像发布测试,一键运行
更便捷的升级和扩缩容
使用了Docker之后,我们部署应用就和搭积木一样!
项目打包为一个镜像,扩展服务器时直接运行镜像即可
更简单的系统运维
在容器化之后,我们的开发,测试环境都是高度一致的
更高效的计算资源利用
Docker是内核级别的虚拟化,可以在一个物理机上运行很多容器实例,服务器的性能可以被压榨到极致
Docker安装
Docker的基本组成

镜像(image):
docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,Tomcat镜像==》run==》Tomcat01容器(提供服务器)
容器(container):
docker利用容器技术,独立运行一个或者一个组应用,通过镜像来创建的。
启动,停止,删除,基本命令
目前就可以把这个容器理解为就是一个简易的Linux系统
仓库(repository):
仓库就是存放镜像的地方
仓库分为公有仓库和私有仓库
安装Docker
安装
帮助文档:
1 | 1、卸载旧的版本 |

卸载docker
1 | # 1、卸载依赖 |
回顾helloworld流程

底层原理
Docker是怎么工作的?
Docker是一个client-server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问
DockerServer接受到Docker-client的指令,就会执行这个命令

Docker为什么比虚拟机(VM)快?
Docker有着比虚拟机更少的抽象层
Docker利用的是宿主机的内核,vm需要是Guest os

所以,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载Guest os,分钟级别的,而docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级的。
Docker的常用命令
帮助命令
1 | docker version # 显示docker的版本信息 |
帮助文档的地址:https://docs.docker.com/engine/reference/commandline/
镜像命令
docker images 查看本地的主机上所有镜像
1 | [root@VM-24-12-centos ~]# docker images |
docker search搜索镜像
1 | NAME DESCRIPTION STARS OFFICIAL AUTOMATED |
docker pull 下载镜像
1 | 下载镜像 docker pull 镜像名[:tag] |
docker rmi 删除镜像
1 | [root@VM-24-12-centos ~]# docker rmi -f 容器id # 删除指定的容器 |
容器命令
说明:有了镜像才可以创建容器
下载centos镜像来测试学习
1 | docker pull centos |
新建容器并启动
1 | docker run [可选参数] image |
列出所有运行中的容器
1 | docker ps 命令 |
退出容器
1 | exit # 直接容器停止并退出 |
删除容器
1 | docker rm 容器id # 删除指定的容器 |
启动和停止容器的操作
1 | docker start 容器id # 启动容器 |
常用其他命令
后台启动容器
1 | 命令docker run -d 镜像名 |
查看日志
1 | docker logs -f -t --tail 容器,没有日志 |
查看容器中的进程信息
1 | 命令 docker top 容器id |
查看镜像元数据
1 | 命令 docker inspect 容器id |
进入当前正在运行的容器
1 | 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置 |
从容器内拷贝文件到主机上
1 | docker cp 容器id:容器内路径 目的的主机路径 |
小结

1 | attach Attach to a running container # 当前shel1 attach 连接指足运行镜像 |
可视化
- portainer
- Rancher
什么是portainer
Docker图形化界面管理工具,提供一个后台面板提供我们操作
1 | docker run -d -p 8088:9000 \ |
安装后,通过外网地址:8088就可以访问了
可视化面板平时不会使用,测试玩玩即可!
Docker镜像讲解
镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
所有的应用,直接打包docker镜像,就可以直接跑起来!
如何得到镜像:
- 从远程仓库下载朋友拷贝给你
- 自己制作—个镜像DockerFile
Docker镜像加载原理
UnionFS(联合文件系统)
UnionFS (联合文件系统): Union文件系统( UnionFS ) 是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual file system)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(bot 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, /letc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu , Centos等等。

平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M ?
对于一个精简的Os , rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别,因此不同的发行版可以公用bootfs。
commit镜像
1 | docker commint 提交容器成为一个新的副本 |
实战测试
1 | 启动一个默认的Tomcat |
这里就算入门Docker了!
以下章节是Docker的精髓!
容器数据卷
什么是容器数据卷
docker的理念:将应用和环境打包成一个镜像
数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!==需求:数据额可持续化==
MySQL,容器删了,删库跑路!==需求:MySQL数据可以存放在本地==
容器之间可以有一个数据共享的技术,Docker容器产生的数据,同步到本地
这就是卷技术,目录的挂载,将我们的容器内的目录挂载到Linux上

总结一句话:容器的持久化和同步操作!容器间也是可以数据共享的
使用数据卷
方式一:直接使用命令来挂载 -v

1 | docker run -it -v 主机目录:容器内目录 |

测试文件的同步

再测试:
停止容器,在宿主机上修改文件,启动容器,容器内的数据依旧是同步的
可以理解为映射!
好处:我们以后修改只需要在本地修改即可,容器内会自动同步!
实战:安装MySQL
思考:MySQL数据持久化的问题
1 | 获取镜像 |
我们将mysql容器删除,我们挂载到本地的数据卷依旧没有丢失,这就实现了容器数据持久化功能
具名和匿名挂载
1 | 匿名挂载 |
所有的docker容器内的卷,没有指定目录的情况下都是在**/var/lib/docker/volumes/xxxx/_data**
我们通过具名挂载可以方便找到我们的一个卷,大多数情况下使用的是具名挂载
1 | 如何确定是具名挂载还是匿名挂载,还是指定路径挂载 |
扩展:
1 | 通过 -v 容器内路径:ro rw 改变读写权限 |
初识Dockerfile
Dockerfile就是用来构建docker镜像的构建文件,命令脚本。
通过脚本可以生成镜像,镜像是一层一层的,脚本一个个的命令,
1 | 创建一个dockerfile文件,名字可以随机 建议 Dockerfile |

这个卷和外部一定有一个同步的目录

查看一下卷挂载的路径

测试一下刚才的文件是否同步出去了
1 | [root@VM-24-12-centos docker-test-volume]# cd /var/lib/docker/volumes/b9c329c7d0847132b4059231f559694f2060bd2b7e3bafd2e2363573c5f15b88/_data |
这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像
假设构建镜像时没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径
数据卷容器
两个mysql同步数据

1 | 启动3个容器,通过我们刚才自己写的镜像启动 |


只要通过–volume-from,我们就可以容器间的数据共享了
父容器删了也不影响子容器的数据
多个mysql实现数据共享
1 | [root@VM-24-12-centos /]# docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 |
结论
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止
但是一旦持久化到了本地,这个时候,本地的数据是不会删除的
DockerFile
dockerfile是用来构建docker镜像的文件,命令参数脚本
构建步骤:
- 编写一个dockerfile文件
- docker build 构建成为一个镜像
- docker run 运行镜像
- docker push 发布镜像(DockerHub、阿里云镜像仓库)
DockerFile构建过程
基础知识:
- 每个保留关键字(指令)都必须是大写字母
- 执行从上到下顺序执行
- #表示注释
- 每一个指令都会创建提交一个新的镜像层,并提交

dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单
Docker镜像逐渐成为企业交付的标准,必须要掌握
DockerFile:构建文件,定义了一切的步骤,源代码
DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品
Docker容器:容器就是镜像运行起来提供服务器
DockerFile的指令
1 | FROM # 基础镜像,一切从这里开始构建 |

以前都是使用别人的镜像,有了这些指令后,我们可以自己写一个镜像。
实战测试
Docker Hub中99%镜像都是从这个基础镜像过来的 FROM scratch,然后配置需要的软件来进行的构建

创建一个自己的centos
1 | 1、编写Dockerfile文件 |
我们可以列出本地进行的变更历史

我们可以根据history研究一下镜像是怎么做的
CMD 和 ENTRYPOINT区别
1 | CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代 |
测试CMD
1 | 编写Dockerfile文件 |
测试ENTRYPOINT
1 | [root@VM-24-12-centos dockerfile]# vim dockerfile-entrypoint-test |
Dockerfile中很多命令偶读十分相似,我们需要了解它们的区别。
实战:Tomcat镜像
准备镜像文件Tomcat压缩包,jdk压缩包
1
2
3
4[root@VM-24-12-centos tomcat]# ll
total 84016
-rw-r--r-- 1 root root 11582777 Jan 14 21:50 apache-tomcat-9.0.56.tar.gz
-rw-r--r-- 1 root root 74444709 Jan 14 21:50 jdk-8u311-linux-aarch64.tar.gz编写dockerfile文件,官方命名Dockerfile,build会自动寻找这个文件,就不需要-f指定了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23Dockerfile文件
FROM centos
MAINTAINER CodeChen<735709343@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u311-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.56.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_311
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.56
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.56
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.56/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.56/bin/logs/catalina.out构建镜像
1
2docker build
[root@VM-24-12-centos tomcat]# docker build -t diytomcat .启动容器
1
[root@VM-24-12-centos tomcat]# docker run -d -p 9090:8080 --name CodeChenTomcat -v /home/CodeChen/build/tomcat/test:/usr/local/apache-tomcat-9.0.56/webapps/test -v /home/CodeChen/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.56/logs diytomcat
访问测试
发布项目(由于做了卷挂载,我们直接在本地编写项目发布了)
1
2
3
4
5
6
7
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">
</web-app>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CodeChen</title>
</head>
<body>
Hello World!<br/>
<%
System.out.println("-----my test web logs-----");
%>
</body>
</html>
发布自己的镜像
DockerHub
https://hub.docker.com/ 注册自己的账号
确定这个账号可以登录
在我们服务器上提交自己的镜像
1
2
3
4
5
6
7
8
9
10
11
12[root@VM-24-12-centos ~]# docker login --help
Usage: docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username登录后就可以提交镜像
提交镜像
1
2
3
4
5现将镜像的TAG改名,必须带dockerHub的账面名
[root@VM-24-12-centos ~]# docker tag 844c55be72a6 账号名/tomcat:1.0
提交镜像
[root@VM-24-12-centos ~]# docker push 账号名/tomcat:1.0
阿里云镜像服务上
小结

Docker网络
理解Docker0
清空所有环境
测试

三个网络
1 | 问题:docker 是如何处理容器网络访问的? |

1 | [root@VM-24-12-centos ~]# docker run -d -P --name tomcat01 tomcat |
原理
我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡docker0
桥接模式,使用的技术是veth-pair技术
再次测试ip addr

再启动一个容器测试,发现又多了一对网卡

1
2
3
4# 我们发现这些容器带来的网卡,都是一对一对的
# veth-pair 就是一对虚拟设备接口,他们都是成对出现的,一端连着协议,一端彼此相邻
# 正因为有这个特性,veth-pair充电一个桥梁,连着各种虚拟网络设备的
# Openstac,Docker容器之间的连接,OVS的连接,都是使用veth-pair技术我们来测试 tomcat01 和 tomcat02
1
2
3
4
5
6
7
8[root@VM-24-12-centos ~]# docker exec -it tomcat02 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.139 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.064 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.068 ms
^C
结论:容器和容器间是可以互相ping通的
结论:tomcat01和tomcat02是共用的一个路由器,docker0
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用IP
小结
Docker使用的是Linux的桥接,宿主机中的一个Docker容器的网络 docker0

Docker中所有的网络接口都是虚拟的。虚拟的转发效率高(内网传递文件)
只要容器删除,对应的网桥一堆就没了
–link
思考一个场景,我们编写一个微服务,database url=ip:,项目部重启,数据库ip换掉了,我们希望可以处理这个问题,可以名字来进行访问容器?
1 | [root@VM-24-12-centos ~]# docker exec -it tomcat02 ping docker01 |
探索:inspect!

其实这个tomcat03就是在本地配置了tomcat02的配置
1 | 查看hosts配置,在这里可以发现 |
本质探索:–link就是我们在hosts配置中增加了一个172.17.0.3 tomcat02 5127a5a11c80
不建议使用–link
自定义网络,不适用docker0
docker0问题:它不支持容器名连接访问
自定义网络
查看所有的docker网络
1 | [root@VM-24-12-centos ~]# docker network ls |
网络模式
bridge:桥接docker(默认)
none:不配置网络
host:和宿主机共享网络
container:容器网络连通(用得少,局限性大)
测试
1 | 我们直接启动的命令 --net bridge。而这个就是我们的docker0 |
我们自己的网络就创建好了
1 | [root@VM-24-12-centos ~]# docker network inspect mynet |
自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络
好处:
redis - 不同的集群使用不用的网络,保证集群是安全和健康的
mysql - 不同的集群使用不同的网络,保证集群是安全和健康的
网络连通


1 | 测试打通 tomcat01 --mynet |

1 | # tomcat01可以ping成功了 |
结论:假设要跨网络操作别人,就需要使用docker network connect 连通
实战:部署Redis集群

1 | 创建网卡 |
docker搭建redis集群完成

使用docker之后,所有的技术都会慢慢的变得简单起来!
Docker Compose
Docker Swarm
CI/CD之Jenkins
资料来源于狂神讲Docker教学视频。