Docker为什么会出现?
一款产品从开发到上线,从操作系统,到运行环境,再到应用部署。作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各个版本的迭代之后,不同版本环境的兼容,对运维人员都是考验
Docker 之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案。
环境配置如此麻烦,换一台机器,就要重来一次,费时费力,很多人想到,能不能从根本上解决问题?软件可以带环境安装?也就是说,安装的时候,把原始环境一摸一样地复制过来。开发人员利用 Docker 可以消除协作编码时“在我的机器上可以正常工作”的问题。
Docker 是什么?
Docker 打破代码即应用的观念;从系统环境开始,自下而上的打包。
一句话总结 Docker:解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。
Docker 能干嘛
之前的虚拟机技术
虚拟机就是带环境安装的一种解决方案。
它可以在操作系统里面运行另一种操作系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删除,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使用程序,操作系统和硬件三者之间的逻辑不变
虚拟机的缺点:
1. 资源占用多; 2. 冗余步骤多; 3. 启动慢
由于前面虚拟机存在这些缺点,Linux发展出了另一种虚拟化技术:Linux容器。
Linux容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
- 比较 Docker 和传统虚拟化方式的不同之处:
- 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需要的应用程序;
- 而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。几乎没有性能损耗,性能优于通过 hypervisor 层与内核层的虚拟化。
- 每个容器之间互相隔离,每个容器有自己的系统文件,容器之间进程不会互相影响,能区分计算资源。
Hypervisor,又称虚拟机监视器(英语:virtual machine monitor,缩写为 VMM),是用来建立与执行虚拟机器的软件、固件或硬件。 被Hypervisor用来执行一个或多个虚拟机器的电脑称为主体机器(host machine),这些虚拟机器则称为客体机器(guest machine)。hypervisor提供虚拟的作业平台来执行客体操作系统(guest operating systems),负责管理其他客体操作系统的执行阶段;这些客体操作系统,共同分享虚拟化后的硬件资源。 |
- 通过下面这幅图,我们可以很直观的反映出这两者的区别所在:
图片----------
- 跟多注解
- Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
每个容器都是相互隔离的、保证安全的平台。可以把容器看作是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
- 总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通代码一样。
Docker 的三大特征:镜像、容器和仓库
- 需要正确理解仓库/镜像/容器这几个概念:
- 镜像:Docker 本身是一个容器运行载体或称之为管理引擎,我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是 image 镜像文件。
- 容器:
- 只有通过上面说的镜像文件才能生成 Docker 容器。image 文件可以看作是容器的模板,Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。
- 它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
- 可以把容器看作是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)
- 仓库:集中存放镜像文件的场所(仓库和仓库注册服务器是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库又包含了多个镜像,每个镜像有不同的标签)
- 仓库氛围公开仓库(Public)和私有仓库(Private)两种形式。
- 最大的公开仓库是Docker Hub(),存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云、网易云等。
Docker安装和运行
参考官网 | 踩坑参考博客
安装和配置
由于 Docker Hub 是国外仓库,可以配置阿里云、网易云镜像,拉镜像会快。
以阿里云为例,可用淘宝账号登录阿里云开发者平台,
1、获取镜像加速器地址
2、配置本机 Docker 运行镜像加速器(平台有写如何在本机配置)
3、重新启动 Docker 后台服务:service docker restart
4、检查配置是否生效:ps -ef| grep docker
永远的 Hello World
都配置好后,我们可以测试运行 hello world:docker run hello-world(一张图解释 run 时都干了什么)
底层原理
Docker 是怎么工作的
Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上,然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。容器,是一个运行时环境,就是我们前面说到的集装箱。
为什么Docker比VM快
1、Docker有着比虚拟机更少的抽象层。由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
2、docker利用的是宿主机的内核,不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。因而避免引寻、加载操作熊内核比较费时费资源的过程,当新疆一个虚拟机时,虚拟机软件需要加载Guest OS,这个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了这个过程,因此新建一个docker容器只需要几秒钟。
Docker 常用命令
帮助命令
docker version | docker info | docker --help
镜像命令
docker images
列出本地镜像名称
说明:
- -a:列出本地所有的镜像(含中间映像层)
- -q:只显示镜像ID
- --digests:显示镜像的摘要信息
- --no-trunc:显示完整的镜像信息
docker search xx(某个镜像名字)
网站:
说明:
- --no-trunc:显示完整的镜像描述
- -s:列出收藏数不小于指定值的镜像。eg: docker search -s 1000 tomcat
- --automated:只列出 automated build 类型的镜像
docker pull xx
docker pull [镜像名称]:[TAG](拉指定版本的镜像),例如:docker pull redis:;不写 tag 则默认 latest
docker rmi xx
删除镜像的操作
- 删除单个:docker rmi -f 镜像ID/唯一镜像名
- 删除多个:docker rmi -f 镜像名1:ag 镜像名2:tag
- 删除全部:docker rmi -f $(docker images -qa)
容器命令
有镜像才能创建容器,这是根本前提(宿主机 win10,虚拟机 centos,centos 装了docker,docker 拉 centos 镜像。centos 才200多M,轻量)
新建并启动容器:docker run [OPTIONS] IMAGE [COMMAND][ARG...]
列出当前所有正在运行的容器
docker ps [OPTIONS]
- -a:列出当前所有正在运行的容器+历史上运行过的
- -l:显示最近创建过的容器
- -n:显示最近 n 格创建的容器
- -q:静默模式,只显示容器编号
- --no-trunc:不截断输出
退出容器
两种方式:
- exit —— 停止容器退出
- Ctrl + P + Q ——容器不停止退出
启动容器:docker start 容器id/容器名
重启容器:docker restart 容器id/容器名
停止容器:docker stop 容器id/容器名
强制停止容器:docker kill 容器id/容器名
删除已停止的容器
- 删除单个容器:docker rm 容器id
- 删除多个容器:docker rm -f $(docker ps -a -q)
重要
启动守护式容器:docker run -d 容器名
使用镜像centos:latest 以后台模式启动一个容器:docker run -d centos
问题:docker ps -a 进行查看,会发现容器已经退出
很重要的一点:Docker容器后台运行,就必须有一个前台进程
容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。
这个是docker的机制问题,比如你的web容器,我们以NGINX 为例,正常情况下,我们配置启动服务只需要启动相应的service即可,例如:service nginx start
但是,这样做 nginx 为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后,会立即自杀因为觉得没事可做
所以,最佳的解决方案是,将你要运行的程序以前台进程的形式运行
查看容器日志:docker logs -f -t --tail 容器id
- -t 是加入时间戳
- -f 跟随最新的日志打印
- --tail 数字:显示最后多少条
查看容器内运行的进程:docker top 容器id
查看容器内部细节:docker inspect 容器id(查看挂载容器卷用到)
进入正在运行的容器并以命令行交互:docker inspect 容器id(docker exec -it 容器id /bin/bash #也可以进入容器)
docker exec -it 容器id ls -l /tmp
从容器内拷贝文件到主机上:docker cp 容器id:容器中路径 宿主机路径
小总结
Docker 常用命令
Docker 镜像
是什么
UnionFS(联合文件系统)
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker 镜像加速原理
docker 的镜像实际上是由一层一层的文件系统组成,这种层级的文件系统 UnionFS。
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等
分层的镜像
pull 的时候可以看到一层层下载,以tomcat为例,一层层可能包含 tomcat、jdk8、centos、kernel
为什么Docker镜像要采用分层结构呢
最大的一个好处是共享资源。比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份 base 镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
特点
Docker 镜像都是只读的
当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
(自己的理解,容器相当于类,模型,自己不去修改,实例化的对象是可读可写的,有可塑性)
Docker 镜像 commit 操作补充
docker commit 提交容器副本使之成为一个新的镜像
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]
案例演示:
Docker 容器数据卷
是什么
一句话:有点类似我们Redis 里面的rdb和 aof 文件
能干嘛
容器的持久化
容器间继承+共享数据
数据卷
容器内添加
- 直接命令添加:docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名:docker run -it -v /myDatavolume:/dataVolumeContainer 镜像名
- 查看数据卷是否挂载成功:路径可以创建成功
- 容器和宿主机之间数据共享
- 容器停止退出后,主机修改后数据是否同步
- DockerFile(MySQL为例看下 DockerFile) 添加
- 根目录下新建 mydocker 文件夹并进入
- 可在 Dockerfile 中使用 VOLUME 指令来给镜像添加一个或多个数据卷(出于可移植和分享的考虑,用 -v 主机目录:容器目录这种方法不能直接在Dockerfile中实现。由于宿主机目录是依赖于特定宿主机的,并不能保证在所有哦的宿主机上都存在这样的特定目录)
- File 构建
#volume test
FROM centos
VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"]
CMD echo "finished,------------success"
CMD /bin/bash
意思为,每次生成容器,都会带有两个容器数据卷,可以使用 docker inspect 容器id 查看对应的宿主机数据存储地址
- build 后生成镜像:docker build -f /mydocker/Dockerfile -t jiaolf/centos .
- run 容器
- 通过上述步骤,容器内的卷目录地址已经知道对应的主机目录地址在哪?——可以使用 docker inspect 容器id 查看对应的宿主机数据存储地址
主机对应默认地址
备注:Docker 挂载主机目录 Docker 访问出现 "cannot open directory .Permission denied" 解决办法:在挂载目录后面加参数 --privileged=true 即可
数据卷容器
是什么:命名的容器挂载数据卷,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器
总体介绍
以上一步新建的镜像 jiaolf/centos 为模板并运行容器 dc01、dc02、dc03
他们已经具有容器卷(VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"])
容器间传递共享(--volumes-form)
1、先启动 dc01:docker run -it --name dc01 jiaolf/centos
2、进入 /dataVolumeContainer2 随意编写东西
- cd /dataVolumeContainer2
- touch dc01_add.txt
3、创建 dc02 挂载在 dc01:docker run -it --name dc02 --volumes-from dc01 jiaolf/centos
- 结果:dc02中/dataVolumeContainer2 有 dc01_add.txt
4、创建 dc03 挂载在 dc01:docker run -it --name dc03 --volumes-from dc01 jiaolf/centos
5、删除 dc01,进入dc02,不受影响,并且在dc02中新建文件,可以同步到dc03
总结:容器之间配置信息的传递,数据卷的生命周期直到没有容器使用它为止
DockerFile 解析
是什么
1、Dockerfile 是用来构建 Docker 镜像的构建文件,是由一系列命令和参数构成的脚本
2、写容器卷的主流步骤:
- 手动编写一个 dockerfile 文件(必须要符合file的规范)
- 有这个文件后,直接 docker build 执行,获得一个自定义的镜像
- Run
centos 为例再次查看 Dockerfile
Dockerfile 构建过程解析
Dockerfile 内容基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
Docker 执行 Dockerfile 的大致流程
- docker 从基础镜像运行一个容器
- 执行一条指令并对容器作出修改
- 执行类似 docker commit 的操作提交一个新的镜像层
- docker 再基于刚提交的镜像运行一个新容器
- 执行 dockerfile 中的下一条指令直到所有指令都执行完成
小总结
Dockerfile 体系结构(保留字指令)
CMD ["/bin/bash"] -------干嘛的
每个容器都会有一个pid为1的进程,如果这个进程执行结束了,容器也就close了;
COMMAND为/bin/bash,说明这个容器的初始进程就是bash进程;
默认情况下,dockerlogs也只会打印pid为1进程的输出
- FROM centos —— 基础镜像
- MAINTAINER jiaolf
- WORKDIR ——指定在创建容器后,终端默认登录进来的工作目录,一个落脚点
- ENV —— 用来在构建镜像中设置环境变量
- RUN —— 容器构建时需要执行的命令
- ADD —— 将宿主机目录下的文件拷贝进镜像且 ADD 命令会自动处理 URL 和解压 tar 压缩包
- COPY —— 类似 ADD,拷贝文件和目录到景象中
- VOLUME —— 容器数据卷,用于数据保存和持久化工作
- EXPOSE —— 当前容器对外暴露的端口
- CMD —— 指定一个容器启动时要运行的命令(Dockerfile中可以有多个 CMD 命令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换)
- ENTRYPOINT —— 指定一个容器启动时要运行的命令(ENTRYPOINT 的目的和 CMD 一样,不同的是指令是追加的,CMD 是覆盖的)
- ONBUILD —— 当构建一个被继承的 Dockerfile 时运行命令,父镜像在被子镜像继承后父镜像的 onbuild 被触发
案例
Base镜像(scratch)
Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的
自定义镜像 mycentos
1、编写 Dockerfile
FROM centos
MAINTAINER jiaolf
ENV MYPATH /tmp
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "success--------ok"
CMD /bin/bash
2、构建:docker build -t 新镜像名字:tag
3、运行
4、列出镜像的变更历史
CMD/ENTRYPOINT 镜像案例
CMD
tomcat 为例,Dockerfile 中最后有 CMD 内容,docker 启动 tomcat 正常情况下会有一些信息打印(Ctrl+c 退出)
尝试在 docker run 时增加参数,如:docker run -it -p 7777:8080 tomcat ls -l (执行后 docker ps 发现 tomcat 并没有启起来)
ENTRYPOINT(制作CMD版可以查询IP信息的容器)
关于 curl
初始文件内容:
FROM centos
RUN yum install -y curl
CMD ["curl","-s",""]
修改后:
FROM centos
RUN yum install -y curl
ENTRYPOINT ["curl","-s",""]ONBUILD RUN echo "father images onbuild -------success"
ONBUILD
验证 ONBUILD 功能,在 Dockerfile4 中增加上面的一行,生成新的镜像 myip_father;创建 Dockerfile5,继承 myip_father 这个镜像,期望在构建 Dockerfile5 时,会执行 Dockerfile4 中 ONBUILD 后面的内容
ADD & COPY(自定义镜像 tomcat9)
1、mkdir -p /jiaolf/mydockerfile/tomcat9
2、进入上述路径,touch c.txt
3、将 jdk 和 tomcat 安装的压缩包拷贝进上一步目录
- apache-tomcat-9.0.8.tar.gz
- jdk-8u171-linux-x64.tar.gz
4、在上述目录下新建 Dockerfile 文件
5、构建
6、run(截图)
7、验证(访问tomcat)
8、结合前述的容器卷将测试的 web 服务 test 发布
效果:使用容器数据卷,本地改内容,可以直接在容器内访问到
小总结
Docker 常用安装
总体步骤
搜索、拉取、查看、启动、停止、移除
安装tomcat
安装mysql
docker run -p 3306:3306 --name mysql
-v /jiaolf/mysql/conf:/etc/mysql/conf.d
-v /jiaolf/mysql/logs:/logs
-v /jiaolf/mysql/data:/var/lib/mysql
-e MYSQL_ROOT_PASSWORD=123456
-d mysql
docker exec -it containerid /bin/bash
mysql -uroot -p 123456
数据库常用操作:建库、建表、插入数据
外部电脑能访问docker上的数据库
安装redis
docker run -p 6379:6379
-v /jiaolf/myredis/data:/data
-v /jiaolf/myredis/conf/
-d redis redis-server /usr/local/etc/redis/redis.conf
--appendonly yes
本地镜像发布到阿里云
docker commit -a='author' -m='commit message' containerid 镜像名
$ sudo docker login --username=1052595200@qq.com registry.cn-hangzhou.aliyuncs.com
$ sudo docker tag [ImageId] :[镜像版本号]
$ sudo docker push :[镜像版本号]
(阿里云)
docker run -p 6379:6379 -v /jiaolf/myredis/data:/data -v /jiaolf/myredis/conf/ -d redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes