前言:
Linux命令到上一篇博文就可以告一个段落了哦 ~ ~
今天初步学习在测试中很重要的东西:Docker
目录
[1 Docker概述](#1 Docker概述)
[1.1 Docker产生的背景?](#1.1 Docker产生的背景?)
[1.2 Docker的理念?](#1.2 Docker的理念?)
[1.3 Docker的优势](#1.3 Docker的优势)
[1.3.1 传统的虚拟机](#1.3.1 传统的虚拟机)
[1.3.2 容器化虚拟技术:](#1.3.2 容器化虚拟技术:)
[1.4 镜像](#1.4 镜像)
[1.5 容器](#1.5 容器)
[1.6 仓库](#1.6 仓库)
[2 image镜像](#2 image镜像)
[3 container容器](#3 container容器)
[4 repository仓库](#4 repository仓库)
[5 拓展](#5 拓展)
[5.1 镜像的原理](#5.1 镜像的原理)
[5.2 Docker镜像加载原理](#5.2 Docker镜像加载原理)
[5.3 为什么docker采用这种分层的原理呢?](#5.3 为什么docker采用这种分层的原理呢?)
[5.4 镜像的特点?](#5.4 镜像的特点?)
[6 一些问题解惑?](#6 一些问题解惑?)
[7 Dockerfile的体系结构](#7 Dockerfile的体系结构)
[7.1 from](#7.1 from)
[7.2 maintainer](#7.2 maintainer)
[7.3 run](#7.3 run)
[7.4 cmd](#7.4 cmd)
[7.5 entrypoint](#7.5 entrypoint)
[7.6 expose](#7.6 expose)
[7.7 env](#7.7 env)
[7.8 copy](#7.8 copy)
[7.9 add](#7.9 add)
[7.10 volume](#7.10 volume)
[7.11 user](#7.11 user)
[7.12 workdir](#7.12 workdir)
[7.13 onbuild](#7.13 onbuild)
[7.14 healthcheck](#7.14 healthcheck)
1 Docker概述
1.1 Docker产生的背景?
开发自测完成后,交给运维部署;
但是,运维部署的环境部署出来有问题;
开发说我自己的环境是对的,这个时候就有冲突了;
同时,可能不止是一个环境部署有问题,可能还有其他环境,比如TEST 环境、UAT环境、PRO环境,集群就更老火了......
开发、运维、测试都崩溃......
这就成为了一个痛点 为什么有问题呢?
- 环境差异
- 应用配置文件有差异......
于是,软件带环境安装,就产生了docker
- 一套包含了开发人员所有的原始环境(代码、运行环境、依赖库、配置文 件、数据文件等)
- 那么我就可以在任何环境上直接运行 以前部署项目的代码叫搬家,现在直接就是搬整栋楼;
- 以前只买鱼,现在 把鱼缸那些一套全买了;
1.2 Docker的理念?
一次编译,到处运行,不依赖于环境了;
Docker是基于Go语言实现的云开源项目;
Docker的主要目标是"Build,Ship and Run Any App,Anywhere", 也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使 用户的App(可以是一个web应用或者数据库应用等等)及其运行环境能够 做到"一次封装,到处运行";
- build,构建
- ship,传输
- run,运行
总结,什么是docker?
docker就是解决了运行环境和配置问题的软件容器,方便持续集成并有 助于整体发布的容器化虚拟技术。
1.3 Docker的优势
1.3.1 传统的虚拟机
如:vmware workstation,virtual box,virtual pc
- 可以在一个操作系统中运行另一种操作系统
- 模拟的是包括一整套操作系统
特点:
- 启动慢,分钟级的
- 资源占用多
- 冗余步骤多
- 有Hypervisor硬件资源虚拟化
1.3.2 容器化虚拟技术:
docker就去掉了Hypervisor硬件资源虚拟化,换成了Docker Engine
那么运行在docker容器上的程序直接使用的都是实际物理机的硬件资源
因此在cpu、内存利用率上docker将会有明显上的效率优势
LXC, Linux Containers
- 模拟的不是一个完整的操作系统
- 只需要精华版、缩小版、浓缩版的的小型的操作系统
- centos/ubuntu基础镜像仅200M不到,iso文件多大(4个多G 吧)
- 容器与虚拟机不同,不需要捆绑一整套操作系统
- 只需要软件所需的库资源和设置
- 因此变得轻量级,并且能保证在任何环境中的软件都能始终如 一地运行
docker启动 是秒级的:
- docker利用的是宿主机的内核,而不需要Guest OS;
- 因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一 个操作系统内核;
- 从而避免了引导、加载操作系统内核这个比较费时费资源的过程;
- 当新建一个虚拟机时,虚拟机软件需要加载Guest OS,这个新建过程是分钟级别的;
- 而docker由于直接利用宿主机的操作系统,则省略了这个过程,因此新建一个docker容器只需要几秒钟;
容器内的应用进程直接运行于 宿主机的内核:
- 容器没有自己的内核,而且也没有进行硬件虚拟
- 因此容器要比传统的虚拟机更为轻便
每个容器之间互相隔离:
- 每个容器有自己的文件系统
- 容器之间进程不会相互影响
- 能区分计算资源
宿主机可以部署100~1000个容器,你这个传统的虚拟化能行?
性能,尤其是IO和内存的消耗低
更轻量
- 基于容器的虚拟化,仅包含业务运行的所需的Runtime环境
更高效
- 无操作系统虚拟化的开销
- 计算:轻量,无额外开销
- 存储:系统盘aufs/dm/overlayfs;数据盘volume
- 网络;宿主机网络,NS隔离
更敏捷、更灵活
- 分层的存储和包管理,devops理念
- 支持多种网络配置
1.4 镜像(重点)
镜像就是软件 + 运行环境的一整套
镜像可能包括什么?
- 代码
- 运行的操作系统发行版
- 各种配置
- 数据文件
- 运行文档
- 运行依赖包
- ......
镜像就是模板
1.5 容器(重点)
容器就是镜像的运行实例
- 容器可以有多个,均来自于同一个镜像,镜像是一个只读的模板
容器就是集装箱
- 容器就是运行着的一个个独立的环境,独立的软件服务
- 容器都是相互隔离、保证安全的平台
容器也可以看做一个简易版的linux环境
- 包括root用户权限、进程空间、用户空间和网络空间等
- 和运行在其中的应用程序
1.6 仓库(重点)
存放镜像的地方
仓库【Repository】是集中存放镜像文件的场所
仓库注册服务器【Registry】,仓库注册服务器上存放着多个仓库
- 每个仓库中又包含了多个镜像
- 每个镜像有不同的标签Tag
仓库有公开仓库【public】和私有仓库【private】两种形式
- 最大的公开仓库:Docker Hub
- 国内的公开仓库有:阿里云、网易云等
- 私有仓库,一般自己公司搭建的
- 一般也是运维搭建,测试不用管
鲸鱼背上有集装箱
- 鲸鱼:docker
- 集装箱:容器实例 ---> 来自镜像
- 大海:宿主机操作系统
容器才可以提供服务 -- 需要通过镜像来运行容器 -- 从仓库获取镜像
2 image镜像
|----------------------------------|------------------|
| docker image ls | 列出本机的镜像 |
| docker rmi 镜像ID | 删除镜像,可接多个ID |
| docker rmi 仓库名:TAG | 删除镜像,可接多个仓库名:TAG |
| docker pull 仓库名:TAG | 拉取镜像 |
| docker search 仓库名 | 搜索docker hub上的仓库 |
| docker image inspect 镜像id | 查看镜像详情 |
| docker image inspect 仓库名:TAG | 查看镜像详情 |
| docker save -o XXX.tar 镜像:tag | 导出镜像 |
| docker [image] load -i XXX.tar | 导入镜像 |
| docker tag 镜像id 别名 | 对镜像起别名,会生成一个新的镜像 |
下面命令可以不用那么熟练:
- docker image ls -qa, 只查询出镜像的id
- docker rmi -f $(docker image ls -qa),强制删除所有的镜像
- docker rmi -f `docker images -qa`,也是强制删除所有的镜像
- docker image ls --digests, 附带查看digests信息
- docker image ls --no-trunc,镜像id全部展示,不截断
3 container容器
docker run xxx
- -d 后台运行
- --name 自己起的容器名称,名字不能冲突,如果不指定,系统自动给你随机分配一个名字
- -p 本宿主机端口:容器端口,端口不能重复调用,比如 -p 3307:3306 是把容器的3306端口映射到本机的3307端口
- -P 随机端口号,系统分配,访问的时候,用docker ps 查看端口号,然后通过ip:端口号访问
- -i 交互式 -i, --interactive Keep STDIN open even if not attached
- -t 终端 -t, --tty Allocate a pseudo-TTY
- -v 宿主机目录:容器内部目录
- -v /tomcat/data:/usr/local/tomcat/webapps 挂载数据卷,将 tomcat的部署的目录(/usr/local/tomcat/webapps)挂载到自定义的挂载卷 (/tomcat/data)上面去;
- -v /tomcat/conf:/usr/local/tomcat/conf 挂载数据卷,将 tomcat的配置文件路径(/usr/local/tomcat/conf )挂载到本机定义的挂载卷(/tomcat/conf)上面去,以后启动别的容器,就可以重用这个conf配置;
- tomcat:8.0-jre8 启动的tomcat的镜像名称;
- 数据卷的作用?
- 持久化容器的数据
- 容器间共享数据
docker run hello-world 启动一个镜像为hello-world的容器
docker ps 查看正在运行的容器
- docker run hello-world
- 运行容器,就是要用run命令
- hello-world 没有接TAG,就表示要使用latest版本
- 会提示你找不到这个镜像
- 会自动给你下载这个hello-world的最新版本latest的镜像
docker ps -a 查看所有容器,包括运行的和没有运行的
- 删除已停止的容器:docker rm ID(可连续跟)
- 查看已停止的容器:docker ps -f status=exited
docker ps -q 查看容器的id,静默显示
docker ps -qa 查看所有容器的id,包括已停止的
docker run --name mycentos centos 运行容器,--name xxx表示给这个容器起个名字
docker run -it centos 交互式运行容器,并进入容器,-i交互式的,-t给分配一个伪终端
- exit 退出容器,但是出来后容器就被关闭了
- ctrl + P + Q 退出容器,但是不关闭容器
- 容器不存在
docker stop 容器名称 # 停止容器,这种是缓慢的停止
- docker stop 容器ID # 也是可以的
docker start centos 启动容器
docker restart centos 重启容器
docker kill centos 停止容器,粗暴的停止
docker inspect mycentos 查看容器的详细信息
docker exec -it c1 /bin/bash 可进入到容器(c1 表示容器)
- 进入之后,可以进行自定义的修改,比如修改tomcat主页
- exit
- ctrl + P+Q 退出终端,而不关闭容器
- 容器已经启动(up)
提交镜像:
a(auther) m(message)
docker commit -a '作者' -m '注释信息' 容器名/容器ID 自己要起什么名[仓库名:TAG]
- 提交新的镜像:docker commit -a 'lanhai' -m 'comment, update tomcat index page' tomcat1 tomcat:test1_v1.0.1;
- 然后就可以使用新的镜像docker run -d -p 8086:8080 --name tomcat3 tomcat:test1_v1.0.1;
- 使用场景,你如果在一个容器内了做了一些修改配置,然后以后也想用这样的配置,就可以配置;
docker run -d --name c1 centos 后台启动容器
docker logs c1 查看日志
docker logs -f c1 实时查看日志
docker logs --tail 3 c1 显示最后几行日志
docker cp 本机文件 容器名:容器的路径目录 #将本机的文件拷贝到容器内部的指定的那个目录
docker cp c1:/tmp/test.log /root/test.log 容器内拷贝出来
docker run -d -p 8888:8080 -v /root/volume_test:/container_volume --name t1 tomcat 挂载宿主机的/root/volume_test到容器的/container_volume目录
以下命令不用太过熟练:
- docker ps -l,显示上次运行的容器
- docker ps -n 3, 显示最近三次运行的容器
某些说明:
docker run -d centos, 为什么运行后就退出呢?
- docker ps -a 查看,会发现容器已经退出
- 注意:docker容器后台运行,就必须有一个前台进程
- 容器运行的命令如果不是那些一直挂起的命令(比如top、tail等),就是会自动退出的
- 这个是docker的机制问题,比如你的web容器,我们以nginx为例。
- 正常情况下,我们配置启动服务只需要启动响应的service即可。例如servcie nginx start
- 但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用
- 这样,容器后台启动后,会立即自杀,因为它觉得它没事可做了
- 所以,最佳的解决方式,是将你要运行的程序以前台进程的形式运行
docker run -d centos /bin/sh -c "while true;do echo hello canglaoshi $(date +'%Y-%m-%d %H:%M:%S');sleep 2;done"
- 写个死循环,占用前台进程
- docker logs -f 容器名, 可以查看容器的日志
docker top 容器名称, 查看容器内部的进程
docker attach 容器名称,直接进入容器
docker exec -it 容器 bash
- docker exec -it 容器名称 /bin/bash
- docker exec -it 容器名称 /bin/sh
- docker exec -it 容器名称 ls -l /tmp, 可以不进入容器,直接查看容器内命令结果
- docker exec 容器名称 ls -l /tmp,可以不进入容器,直接查看容器内ls -l /tmp命令的结果
attach 和 exec 的区别:
- 两个命令都可以进入容器
- attach 是直接进入之后才能干活
- exec 也可以直接进入后干活,但是exec还能在不进入容器的情况下,运行命令
docker run -d -v xxx:yyy:ro 镜像, 挂载卷设置只读权限
表示什么呢?
- 宿主机上挂载的那个目录可以正常写入
- 容器内挂载的那个目录只读,【容器只能看】
--volumes-from
- docker run -it --name centos1 lanhai:v0.0.1
- docker run -it --name centos2 --volumes-from centos1 lanhai:v0.0.1
- docker run -it --name centos3 --volumes-from centos1 lanhai:v0.0.1
- 分别在centos2里面的挂载目录新建数据
- 分别在centos3里面的挂载目录新建数据
- 删除容器centos1,那么centos2和centos3的都在
- 同时,任何一个挂载卷新增修改数据,都会同步
- 所以,可以实现容器间数据共享
4 repository仓库
docker push 镜像名:TAG
5 拓展
(功利地说:低频考点)
5.1 镜像的原理
镜像的加载分层采用UnionFS联合文件系统;
UnionFS:
- UnionFS是一种分层、轻量级并且高性能的文件系统;
- 它支持对文件系统的修改作为一次提交来一层层的叠加;
- 同时可以将不同目录挂载到同一个虚拟文件系统下;
- UnionFS是Docker镜像的基础镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以只做各种具体的应用镜像;
特点:
- 一次同时加载多个文件系统;
- 但是从外面看起来,只能看到一个文件系统;
- 联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录;
5.2 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就是各种不同的操作系统发行版,比如Centos、Ubuntu等。
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了;
因为底层直接用的HOST的kernel,自己只需要提供rootfs就行了;
由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs有差别,因此不同 的发行版可以公用bootfs;
5.3 为什么docker采用这种分层的原理呢?
最大的好处,就是资源共享
- 比如: 有多个镜像都从相同的base镜像构建而来,那么宿主机只需要在磁盘上保存一 份base镜像就可以了
- 同时内存中也只需加载一份base镜像,就可以为所有的容器服务了
- 而且镜像的每一层都可以被共享
- 比如,你同时下载了tomcat、centos、nginx、mysql等的镜像
- 那么他们这些镜像里面可能有很多的base层镜像是一样的,那么就不用每个都去下载了
- 只保留一份就可以了
5.4 镜像的特点?
镜像是只读的;
当镜像启动为容器后,一个新的可写层被加载到了镜像的顶部;
这一层通常被称为容器层,而容器层以下的都叫镜像层;
6 一些问题解惑?
docker run -it -p 8081:8080 --name tomcat1 tomcat:8.0-jre8 bash 像这个命令启动容器后为什么,访问不了?
- 比如 localhost:8081
- 或者浏览器访问 http://ip:8081
分析:
- 启动容器的时候,带了启动命令bash,那么就会tomcat的Dockerfile里面的 CMD命令给覆盖了
- 说白了,就是这个tomcat容器启动的时候,没有启动前台命令
- 如何解决?
- 其实现在run -it的方式已经进入了容器,那么可以手动启动tomcat的 脚本,就能访问了
- cd /usr/local/tomcat/bin
- ./startup.sh
- 再次访问,ok
7 Dockerfile的体系结构
7.1 from
FROM 镜像 指定新镜像所基于的镜像,第一条指令必须为FROM指令,每创建一个镜像就需 要一个FROM指令;
7.2 maintainer
MAINTAINER 名字 说明镜像的维护人员信息;
7.3 run
RUN 命令 在所基于的镜像上执行命令,并提交到新的镜像中;
7.4 cmd
CMD ["要运行的程序", "参数1", "参数2"] 指定启动容器时要运行的命令或者脚本
只能有一条CMD命令,如果有多条,那么只有最后一条生效,前面的都被覆盖了
启动容器时,如果启动命令带了参数,那么这个参数会将cmd中的配置给替换
7.5 entrypoint
ENTRYPOINT ["要运行的程序", "参数1", "参数2"] 指定启动容器时要运行的命令或者脚本
启动容器时,如果docker run启动命令带了参数,那么这个参数会被追加为参数,然后形成新的命令组合
7.6 expose
EXPOSE 端口号 指定新镜像加载到docker时要被开启的端口
7.7 env
ENV 环境变量 变量值 设定一个环境变量,会被后面的命令所使用,如RUN,如WORKDIR
7.8 copy
COPY 源文件/目录 目标文件/目录 将本地主机上的东西,复制到目标地址,这个本地主机上的"源文件/目录"必须要与Dockerfile在同一个目录;
7.9 add
ADD 源文件/目录 目标文件/目录
同copy命令,但是多一个能解压tar包的功能,以及能自动处理URL
7.10 volume
VOLUME 目录
在容器中创建一个挂载目录
7.11 user
USER username/UID 指定运行容器的用户
7.12 workdir
WORKDIR 路径
为后续的RUN,CMD,ENTRYPOINT指定工作目录
7.13 onbuild
ONBUILD 命令
指定这个镜像被作为基础镜像继承时,所要运行的命令,相当于一个触发器
- 别的镜像继承我这个镜像【这个Dockerfile里面写了onbuild】的时候,在构建的时 候,会触发父镜像的这个onbuild的执行
- Father 镜像的Dockerfile
- 里面包含了onbuild
- Son ---> FROM Father
- build构建的时候,会执行Father镜像里面的onbuild语句
- eg. ONBUILD run echo "hello onbuild test....."
7.14 healthcheck
HEALTHCHECK 健康检查
命令: docker build . -t xxx:v0.0.1 -f /root/xxx/Dockerfile
- -f 指定的文件