内容安排
- Docker基础
- Docker镜像讲解
- 容器数据卷
- 什么是DockerFile
- Docker网络讲解
- 实战:部署一个Redis集群
Docker基础
Docker 是什么
一种容器化技术,问度娘吧
为什么会出现Docker这个东西
我们可以想象一下,之前比如开发一个web,从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发+运维之间的协作我们需要 关心很多东西,特别是各种版本的迭代之后,不同版本环 境的兼容,对运维人员是极大的考验!
拿一个基本的工程项目的环境来说, Java/Tomcat/MySQL/JDBC驱动包等。安装和配置这些东西有多麻烦就不说了,它还不能跨平台。假如 我们是在 Windows 上安装的这些环境,到了 Linux 又得重新装。况且就算不跨操作系统,换另一台同 样操作系统的服务器,要移植应用也是非常麻烦的。
为了解决上述问题,Docker给出了一个标准化的解决方案,
Docker镜像的设计,使得Docker得以打破过去「程序即应用」的观念。通过Docker镜像 ( images ) 将 应用程序所需要的系统环境,由下而上打包,达到应用程序跨平台间的无缝接轨运作。
Docker和虚拟机的对比
虚拟机(virtual machine)就是带环境安装的一种解决方案。
它可以在一种操作系统里面运行另一种操作系统,比如在Windows 系统里面运行Linux 系统。应用程序 对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文 件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序, 操作系统和硬件三者之间的逻辑不变。
虚拟机的缺点:
- 资源占用多
- 冗余步骤多
- 启动慢
Docker容器虚拟化技术
由于前面虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩 写为 LXC)。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的 所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作 所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
Docker的基本组成
Docker 架构图
镜像(image):
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很 多容器。 就好似 Java 中的 类和对象,类就是镜像,容器就是对象!
容器(container):
Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。 它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等) 和运行在其中的应用程序。。
容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的
最上面那一层是可读可写 的
。
仓库(repository):
仓库(Repository)是集中存放镜像文件的场所。 仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓
库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。 国内的公开仓库包括阿里云 、网易云 等
Docker的安装
看官网吧!!!
Docker的简单使用
我们先来第一个简单实用
1
docker run hello-world
这个命令一执行,到底发生了什么,整个运行逻辑就是下图
Docker命令记录
帮助命令
1 | docker version # 显示 Docker 版本信息。 |
镜像命令
docker images
1 | 列出本地主机上的镜像 |
docker search
1 | 搜索镜像 |
docker pull
1 | 下载镜像 |
docker rmi
1 | 删除镜像 |
容器命令
说明:有镜像才能创建容器,我们这里使用 centos 的镜像来测试,就是虚拟一个 centos !
1 | docker pull centos |
新建容器并启动
1 | 命令 |
列出所有运行的容器
1 | 命令 |
退出容器
1 | exit # 容器停止退出 |
启动停止容器
1 | docker start (容器id or 容器名) # 启动容器 |
删除容器
1 | docker rm 容器id # 删除指定容器 |
常用其他命令
1 | 命令 |
查看日志
1 | 命令 |
查看容器中运行的进程信息,支持 ps 命令参数。
1 | 命令 |
查看容器/镜像的元数据
1 | 命令 |
进入正在运行的容器
1 | 命令1 |
从容器内拷贝文件到主机上
1 | 命令 |
小结
将上述的命令实用熟练,只能说,你知道了Docker是什么,和怎么简单实用,并且在看下面这个图,你也基本知道是个什么意思了。
练习
使用Docker 安装 nginx
1 | 1、搜索镜像 |
最后
会使用上述命令其实连入门都算不上。
Docker镜像讲解
镜像是什么
1 | 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含 |
Docker镜像加载原理
- UnionFS (联合文件系统)
- Docker 镜像的基础
- Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统, 它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系 统下
- Docker镜像加载原理
- docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
分层理解
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!
Docker镜像采用这种分层结构,最大的好处莫过于资源共享了
1 | 查看镜像分层的方式可以通过 docker image inspect ID |
重点
所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之 上,创建新的镜像层。
举一个简单的例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果 在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就 会创建第三个镜像层。
该镜像当前已经包含 3 个镜像层,如下图所示。
注意
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部! 这一层就是我们通常说的容器层,容器之下的都叫镜像层!容器层是可写的。
镜像Commit
docker commit 从容器创建一个新的镜像。
1 | docker commit 提交容器副本使之成为一个新的镜像! |
测试
比如我们自己需要制作一个镜像,以ubuntu16.04 为基础镜像,在该镜像上安装深度学习库,比如要安装python 和 pytorch
我们就可以按上述说的方式进行操作
- 拉去ubuntu 16.04基础镜像
- 启动镜像,生成一个容器,进入该容器
- 安装python,pytorch 安装包
- 退出
- docker commit
容器数据卷
什么是容器数据卷
docker的理念回顾:
将应用和运行的环境打包形成容器运行,运行可以伴随着容器,但是我们对于数据的要求,是希望能够持久化的! 就好比,你安装一个MySQL,结果你把容器删了,就相当于删库跑路了,这TM也太扯了吧!
所以我们希望容器之间有可能可以共享数据,Docker容器产生的数据,如果不通过docker commit 生成 新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了!这样是行 不通的!
为了能保存数据在Docker中我们就可以使用卷!让数据挂载到我们本地!这样数据就不会因为容器删除 而丢失了!
作用:
卷就是目录或者文件,存在一个或者多个容器中,由docker挂载到容器,但不属于联合文件系统,因此 能够绕过 Union File System , 提供一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂 载的数据卷。
特点:
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可以直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
所以:总结一句话: 就是容器的持久化,以及容器间的继承和数据共享!
使用数据卷
方式一:容器中直接使用命令来添加
1 | 命令 |
查看数据卷是否挂载成功 docker inspect 容器id
测试容器和宿主机之间数据共享:可以发现,在容器中创建的文件会在宿主机对应目录上看到!
1 | 测试容器停止退出后,主机修改数据是否会同步! |
- 停止容器
- 在宿主机上修改文件,增加些内容
- 启动刚才停止的容器
- 然后查看对应的文件,发现数据依旧同步!ok
方式二:通过Docker File 来添加(了解)
测试:
1 | 1、我们在宿主机 /home 目录下新建一个 docker-test-volume文件夹 |
练习
通过Docker 来 安装 mysql
思考:mysql 数据持久化的问题!
1 | 1、搜索镜像 |
匿名和具名挂载
1 | 匿名挂载 |
数据卷容器
如何让多个容器数据共享
命名的容器挂载数据卷,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为
数据卷容器。
我们使用上一步的镜像:wl/centos 为模板,运行容器 docker01,docker02,docker03,他们都会具有容器卷
1、先启动一个父容器docker01,然后在dataVolumeContainer2新增文件
2、创建docker02,docker03 让他们继承docker01 –volumes-from
1 | [root@wl docker-test-volume]# docker run -it --name docker02 --volumes-from docker01 wl/centos |
3、回到docker01发现可以看到 02 和 03 添加的共享文件
1 | [root@wl docker-test-volume]# docker attach docker01 |
4、删除docker01,docker02 修改后docker03还能不能访问
1 | [root@wl docker-test-volume]# docker rm -f docker01 |
5、删除docker02 ,docker03还能不能访问
1 | [root@wl docker-test-volume]# docker ps |
6、新建docker04继承docker03,然后再删除docker03,看下是否可以访问!
1 | [root@2119f4f23a92 /]# cd dataVolumeContainer2 |
结论:
容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止。
存储在本机的文件则会一直保留!
什么是DockerFile
到目前为止,我们已经知道了一种生成镜像的方式了,就是上述的 Commit 命令
但是我们在很多项目中看到都有一个DockerFile的东西,接下来就介绍一下这个东西吧。
DockerFile 是用来构建Docker镜像的构建文件,是由一些列命令和参数构成的脚本。
流程:
- 开发应用=>DockerFile=>打包为镜像=>上传到仓库(私有仓库,公有仓库)=> 下载镜像 => 启动 运行。
构建步骤:
- 编写DockerFile文件
- docker build 构建镜像
- docker run
DockerFile构建过程
基础知识:
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- “#” 表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
流程:
- docker从基础镜像运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似 docker commit 的操作提交一个新的镜像层
- Docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成!
说明:
从应用软件的角度来看,DockerFile,docker镜像与docker容器分别代表软件的三个不同阶段。
- DockerFile 是软件的原材料 (代码)
- Docker 镜像则是软件的交付品 (.apk)
- Docker 容器则是软件的运行状态 (客户下载安装执行)
DockerFile 面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可!
DockerFile指令
关键字:
1 | FROM # 基础镜像,当前新镜像是基于哪个镜像的 MAINTAINER # 镜像维护者的姓名混合邮箱地址 |
实战测试
自定义一个centos
1、编写DockerFile
我们拉去官方的centos 进入容器会发现 vim 和 ifconfig 指令都用不了,不方便
- 准备编写DockerFlie文件
1 | [root@wl home]# mkdir dockerfile-test |
2、构建
docker build -f dockerfile地址 -t 新镜像名字:TAG .
1 | 查看本地机器上是否有刚构建的镜像 |
3、运行
docker run -it 新镜像名字:TAG
可以发现,我们自己的新镜像已经支持 vim/ifconfig的命令,扩展OK!
4、列出镜像地的变更历史
docker history 镜像名
CMD 和 ENTRYPOINT 的区别
我们之前说过,两个命令都是指定一个容器启动时要运行的命令
CMD:Dockerfile 中可以有多个CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数 替换!
ENTRYPOINT: docker run 之后的参数会被当做参数传递给 ENTRYPOINT,之后形成新的命令组合!
自定义镜像 tomcat,直接看DockerFile文件的编写吧
1 | vim Dockerfile |
总结
至此,我认为我们应该算是Docker 简单入门了!!!!!
Docker网络讲解
理解Docker0
1 | 准备工作:清空所有的容器,清空所有的镜像 |
我们先来做个测试
查看本地ip ip addr
注意看docker0
1 | lo 127.0.0.1 # 本机回环地址 |
我们思考这样一个问题,开发了很多微服务项目,那些微服务项目都要连接数据库,需要指定数据库的url地 址,通过ip。但是我们用Docker管理的话,假设数据库出问题了,我们重新启动运行一个,这个时候数 据库的地址就会发生变化,docker会给每个容器都分配一个ip,且容器和容器之间是可以互相访问的。 我们可以测试下容器之间能不能ping通过:
1 | 启动tomcat01 |
思考,问什么可以ping通呢?
1、每一个安装了Docker的linux主机都有一个docker0的虚拟网卡。这是个桥接网卡,使用了veth-pair 技术!
1 | 我们再次查看主机的 ip addr |
2、每启动一个容器,linux主机就会多了一个虚拟网卡。
1 | 我们启动了一个tomcat01,主机的ip地址多了一个 123: vethc8584ea@if122 |
3、我们来测试下tomcat01和tomcat02容器间是否可以互相ping通
1 | [root@wl ~]# docker exec -it tomcat02 ping 172.18.0.2 |
4、我们来画一个网络模型图
结论:
tomcat1和tomcat2共用一个路由器。是的,他们使用的一个,就是docker0。任何一个容器启动 默认都是docker0网络。
docker默认会给容器分配一个可用ip。
–Link
思考一个场景,我们编写一个微服务,数据库连接地址原来是使用ip的,如果ip变化就不行了,那我们 能不能使用服务名访问呢?
jdbc:mysql://mysql:3306,这样的话哪怕mysql重启,我们也不需要修改配置了!
docker提供了 --link 的操作!
1 | 我们使用tomcat02,直接通过容器名ping tomcat01,不使用ip |
思考,这个原理是什么呢?我们进入tomcat03中查看下host配置文件
1 | [root@wl ~]# docker exec -it tomcat03 cat /etc/hosts |
--link早都过时了,我们不推荐使用!我们可以使用自定义网络的方式
自定义网络
1 | 命令查看 |
自定义网卡
- 我们来创建容器,但是我们知道默认创建的容器都是docker0网卡的
1 | 默认我们不配置网络,也就相当于默认值 --net bridge 使用的docker0 |
- 我们可以让容器创建的时候使用自定义网络
1 | 查看如何创建网络 |
思考:上述其实就相当于两个容器同属于一个局域网,当然可以连通了,但是现在有两个不同的网络,这样该怎么相互连通呢?
docker0和自定义网络肯定不通,我们使用自定义网络的好处就是网络隔离:
大家公司项目部署的业务都非常多,假设我们有一个商城,我们会有订单业务(操作不同数据),会有 订单业务购物车业务(操作不同缓存)。如果在一个网络下,有的程序猿的恶意代码就不能防止了,所 以我们就在部署的时候网络隔离,创建两个桥接网卡,比如订单业务(里面的数据库,redis,mq,全 部业务 都在order-net网络下)其他业务在其他网络。
那关键的问题来了,如何让 tomcat-net-01 访问 tomcat1?
1 | 启动默认的容器,在docker0网络下 |
结论
如果要跨网络操作别人,就需要使用
docker network connect [OPTIONS] NETWOEK CONTAINER
实战:部署一个Redis集群
集群简单设计图
1 | 创建网卡 |
最后
到这里对于不是专门干运维的同学,应该够用了
思考
我们可以发现上面这些东西好像都还是单机上的一些容器处理,如果是一个服务器集群的话,该如何来管理这些容器了,或者一台机器上多个容器,如何来编排他们的依赖关系呢?这就是下一篇博客要解释的docker-compose
和docker-swap