假如你使用一台笔记本,且你的开发环境具有特定的配置。你开发的应用依赖于你的配置还有一些配置文件。而其他开发人员所处的环境配置也不相同。此外,你所处的企业还将具有标准的测试和生产环境,且具有自身的配置和一系列的配置文件。你希望尽可能多地在本地模拟这些环境且尽可能不重新创建服务器环境。
那么,你要如何确保应用能在这些环境中运行且通过质量检测?并且在部署过程中不出现令人头疼的版本、配置问题,也无需重新编写代码和故障修复?
使用容器!docker之所以发展迅速,是因为它对此给出了一套标准的解决方案——系统平滑移植、容器虚拟化技术。
环境配置相当麻烦,换一台机器就要重新来一次,费力费时。很多人想到,能不能从根本上解决问题:“软件可以带环境安装”?也就是说安装的时候,把原始环境一模一样地复制过来。docker就是规定了环境的一致,保证迁移的时候打包、运行不走样。
docker包含了下述的技术。
docker的主要目标:“Build,Ship and Run any app,Anywhere”。通过对应用的封装、分发、部署、运行等生命周期的管理,使用户的app(web应用、数据库应用等等)及其运行环境能够做到 “一次镜像、处处运行”。
docker是基于Go语言实现的云开源项目,是基于Linux容器技术上发展起来的。将应用打包成镜像,通过镜像成为运行的docker容器实例。而docker容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
总结的说,docker是解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。
虚拟机是带环境安装的一种解决方案
传统虚拟技术基于安装在主操作系统上的虚拟机管理系统(如VMware、virtualbox等),创建虚拟机(虚拟出各种硬件),在虚拟机上安装从操作系统,在从操作系统上安装部署各种应用。
缺点:
资源占用多
冗余多
启动慢
Linux容器(Linux container,LXC)
Linux容器不是模拟一个完整的操作系统,而是对进程隔离。有了容器,就可以将软件运行所需的全部资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
docker容器是在操作系统层面实现虚拟化,直接复用本地主机的操作系统,而传统虚拟机是在硬件层面上实现虚拟化。与传统的虚拟机相比,docker的优势体现在启动速度快、占用体积小。

总结来说,
docker的理念: “一次镜像、处处运行”
docker借鉴了标准集装箱的概念。标准集装箱货物运往世界各地,docker将这个模型运用到自己的设计中,唯一不同的是:集装箱运输货物,而docker运输软件。
产生出开发/运维(DevOps)新一代开发工程师。
更轻量:基于容器的虚拟化,仅包含业务运行所需的runtime环境,centosOS/Ubuntu基础镜像进170M;宿主机可部署100-1000个容器
更高效:无操作系统虚拟化开销
更敏捷、更灵活
下图有客户端、docker宿主机、docker仓库
客户端通过连接docker daemon(docker守护进程是一个后台服务线程),可以执行创建、拉取镜像、及运行容器操作。docker守护进程运行在主机上(物理机或者虚拟机)。客户端可以build镜像,并放在本地的docker守护进程中,之后通过该守护进程启动容器。客户端要运行容器时,docker守护进程先检查本地是否有该镜像,若有,就使用本地镜像构建一个一个的容器。若没有就从docker仓库上先拉取镜像。如果docker仓库上也没有镜像,就会报错显示镜像不存在。

Docker是一个client-server结构的系统,docker守护进程运行在主机(物理机或者虚拟机)上,然后通过socket连接从客户端访问。守护进程从客户端接收命令并管理运行在主机上的容器。容器,是一个运行时的环境,可以同集装箱类比。

docker是一个client/server模式的架构,后端是松耦合架构,众多模板各司其职。



上述最底层是根的文件系统,以层级概念划分。底层就是微小版的Linux系统文件。
安装官方文档进行:
https://docs.docker.com/engine/install/centos/
其中下载stable repository时,不使用官方文档上的命令,使用阿里云提供的下载地址,
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo



启动docker: systemctl start docker
停止docker: systemctl stop docker
重启docker: systemctl restart docker
查看docker状态: systemctl status docker
开机启动: systemctl enable docker
查看docker概要信息: docker info
查看docker总体帮助文档: docker --help
查看docker命令帮助文档: docker 具体命令 --help
docker images 列出本地主机上的镜像
参数:
-a :列出本地所有的镜像(含历史映像层)
-q :只显示镜像ID
docker search 某个XXX镜像名字
docker search [OPTIONS] 镜像名字
参数:
--limit : 只列出N个镜像,默认25个
docker search --limit 5 redis 列出点赞数前5的redis镜像
docker pull 某个XXX镜像名字 下载镜像
写法一:
docker pull 镜像名字[:TAG]
写法二:
docker pull 镜像名字
没有TAG就是最新版
等价于 docker pull 镜像名字:latest
例如:docker pull ubuntu
docker system df 查看镜像/容器/数据卷所占的空间
docker rmi 某个XXX镜像名字ID 删除镜像
docker rmi -f 镜像ID 删除单个
docker rmi -f 镜像名1:TAG 镜像名2:TAG 删除多个
docker rmi -f $(docker images -qa) 删除全部
仓库名、标签都是的镜像,俗称虚悬镜像dangling image
例如:
有镜像id,也有大小,但是仓库名和标签均为none
这种镜像没有用,建议删除
有时候镜像构建时会出现一些问题,导致构建出这种镜像。
镜像是模板,容器是镜像的实例
有镜像才能创建容器,
新建+启动容器 :docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
OPTIONS说明(常用):有些是一个减号,有些是两个减号
--name="容器新名字" 为容器指定一个名称;如果不指定,系统随机分配
-d: 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
-i:以交互模式运行容器,通常与 -t 同时使用;(interactive,交互)
-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;(tty,伪终端)
也即启动交互式容器(前台有伪终端,等待交互);
例如: docker run -it ubuntu /bin/bash
docker run -it ubuntu bash
表示使用Ubuntu镜像以交互模式创建一个容器实例,并在容器内执行/bin/bash(bash)命令
镜像后的/bin/bash命令,表示我们希望有个交互式shell
要退出交互,输入exit
-P: 随机端口映射,大写P
-p: 指定端口映射,小写p

列出当前所有正在运行的容器:docker ps [OPTIONS]
OPTIONS说明(常用):
-a :列出当前所有正在运行的容器+历史上运行过的
-l :显示最近创建的容器。
-n:显示最近n个创建的容器。
-q :静默模式,只显示容器编号。
退出容器:
两种方式:
一:exit——run进去容器,exit退出,容器停止
二:ctrl+p+q——run进去容器,ctrl+p+q退出,容器不停止
启动已停止运行的容器:
docker start 容器ID或者容器名
重启容器:
docker restart 容器ID或者容器名
停止容器:
docker stop 容器ID或者容器名
强制停止容器:
docker kill 容器ID或容器名
如果容器正在运行,状态为up,那么需要先stop再rm删除,或者使用命令docker rm -f 容器名称或容器id 强制删除。
一次性删除多个容器实例:
docker rm -f $(docker ps -a -q)
docker ps -a -q | xargs docker rm
xargs表示上一个命令的输出结果,传给了xargs
在大部分的场景下,我们希望 docker 的服务是在后台运行的,
这时可以通过 -d 指定容器的后台运行模式。
docker run -d 容器名
#使用镜像centos:latest以后台模式启动一个容器
docker run -d centos
问题:然后docker ps -a 进行查看, 会发现容器已经退出
很重要的要说明的一点: Docker容器后台运行,就必须有一个前台进程.容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。
这个是docker的机制问题,比如你的web容器,我们以nginx为例,正常情况下,
我们配置启动服务只需要启动响应的service即可。例如service nginx start
但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用,
这样的容器后台启动后,会立即自杀因为他觉得他没事可做了.
所以,最佳的解决方案是,将你要运行的程序以前台进程的形式运行,
常见就是 -it 命令行模式,表示我还有交互操作,别中断。
以redis演示前后台启动:
前台交互式启动:
docker run -it redis:6.0.8
这时容易不小心关闭窗口,导致服务中断,因此更适用于后台守护石启动。
后台守护式启动:
docker run -d redis:6.0.8
查看容器日志:docker logs 容器ID
因为容器可以看作是简易的Linux环境+运行在其中的应用程序,所以也可以查看容器中的Linux负载情况
查看容器内运行的进程:docker top 容器ID
查看容器内部细节:docker inspect 容器ID
docker exec -it 容器ID /bin/bash
或者使用:
docker attach 容器ID
使用exec -it 与attach的区别:
使用exec -it 重新进入容器原理是,在容器中打开新的终端,并且可以启动新的进程用exit 或者用ctrl+p+q退出,不会导致容器的停止。
使用attach 会直接进入容器启动命令的终端,不会启动新的进程
用exit退出,会导致容器的停止。
推荐使用 docker exec -it 命令,因为退出容器终端,不会导致容器的停止。
只是拷贝部分文件:
docker cp 容器ID:容器内路径 目的主机路径
备份整个容器:
export 导出整个容器的内容作为一个tar归档文件[对应import命令]
docker export 容器ID > 文件名.tar
回复备份的归档文件:
cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号

镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。
只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。
Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统, 它支持对文件系统的修改作为一次提交来一层层的叠加, 同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。**Union 文件系统是 Docker 镜像的基础。**镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。(多层文件系统堆叠,对应多种子功能叠加,最终合成一个完整的文件系统,组成一个完整的大功能)
(可以把容器看作是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用容器)

比如说有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;
同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
Docker镜像层都是只读的,容器层是可写的
当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
镜像层作为rootfs,是堆叠好的。

docker commit提交容器副本使之成为一个新的镜像
docker commit -m="提交的描述信息" -a="作者" 需要创建镜像的容器ID 要创建的目标镜像名:[标签名]
例如基于从docker hub上下载的Ubuntu镜像,运行Ubuntu镜像的一个容器实例:
docker run -it ubuntu /bin/bash
在创建的容器实例中,(例如容器id为abc),看到没有vim命令,
运行如下两个命令安装vim:
apt-get update
apt-get-y intall vim
能查看到容器abc中有vim命令
然后,将abc容器commit成一个新的镜像:
docker commit -m “add vim” -a “wam” abc reg.westos.org/myubuntu:1.3
commit完之后docker images就能查看新创建的镜像,新创建的镜像比原下载的镜像大。
相当于原下载的镜像为base镜像,通过编辑在base镜像上新添加了一层。之后可以基于原下载的Ubuntu镜像再继续叠加新的层,,也可以基于创建的myubuntu再叠加新的层,继续拓展镜像的功能。对base镜像进行功能的加强。
Docker中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似Java继承于一个Base基础类,自己再按需扩展。
新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

官方Docker Hub地址:https://hub.docker.com/,中国大陆访问太慢了且准备被阿里云取代的趋势,不太主流。
Dockerhub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私人仓库供给团队使用,基于公司内部项目构建镜像。
Docker Registry是官方提供的工具,可以用于构建私有镜像仓库。
向私有仓库registry上传镜像,流程:
下载镜像Docker Registry
docker pull registry
运行私有库Registry,相当于本地有个私有Docker hub
docker run -d -p 5000:5000 -v /zzyyuse/myregistry/:/tmp/registry --privileged=true registry
默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便于宿主机联调
演示案例:创建一个新镜像,ubuntu安装ifconfig命令
3.1 从Hub上下载ubuntu镜像到本地并成功运行
docker pull ubuntu
docker run -it ubuntu /bin/bash
原始的Ubuntu镜像是不带着ifconfig命令的
3.2 外网连通的情况下,安装ifconfig命令并测试通过
docker容器内执行上述两条命令:
apt-get update
apt-get install net-tools
3.3 安装完成后,commit我们自己的新镜像,在容器外执行下述命令
docker commit -m=“提交的描述信息” -a=“作者” 需要创建镜像的容器ID 要创建的目标镜像名:[标签名]
docker commit -m=“ifconfig cmd add” -a=“zzyy” a69d7c825c4f zzyyubuntu:1.2
3.4 启动我们的新镜像并和原来的对比
官网是默认下载的Ubuntu没有ifconfig命令
我们自己commit构建的新镜像,新增加了ifconfig功能,可以成功使用。
curl验证私服库上有什么镜像
curl -XGET http://localhost:5000/v2/_catalog
(模拟发送一个get请求)
可以看到,目前私服库没有任何镜像上传过。。。。。。
将新镜像zzyyubuntu:1.2修改符合私服规范的Tag
按照公式: docker tag 镜像:Tag Host:Port/Repository:Tag
docker tag zzyyubuntu:1.2 localhost:5000/zzyyubuntu:1.2
修改配置文件使之支持http
docker默认不允许http方式推送镜像,通过配置选项来取消这个限制。====> 修改完后如果不生效,建议重启docker
vim /etc/docker/daemon.json
{
“registry-mirrors”: [“阿里云镜像加速器地址”],
“insecure-registries”: [“localhost:5000”]
}
push推送到私服库
docker push localhost:5000/zzyyubuntu:1.2
再次curl验证私服库上有什么镜像
能查看到私有仓库上有zzyyubuntu镜像
拉取镜像到本地并运行
docker pull localhost:5000/zzyyubuntu:1.2
docker run -it localhost:5000/zzyyubuntu:1.2 /bin/bash
容器内能使用 ifconfig 命令
Docker挂载主机目录访问如果出现cannot open directory .: Permission denied
解决办法:在挂载目录后多加一个–privileged=true参数即可
如果是CentOS7安全模块会比之前系统版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为,
因为在SELinux里面挂载目录被禁止掉了的,如果要开启,我们一般使用--privileged=true命令,扩大容器的权限解决挂载目录没有权限的问题,也就是说,使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。
下述命令:
docker run -d -p 5000:5000 -v /zzyyuse/myregistry/:/tmp/registry --privileged=true registry
–privileged=true 授予容器内的root用户真正的root权限
-v 宿主机绝对路径:容器内路径
表示两个路径中内容共享和互通互联,
默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便于宿主机联调
总结的说,docker通过数据卷将docker容器内的数据保存进宿主机的磁盘中,以达到容器内数据的持久化和敏感数据备份的目的。即使容器意外被删除,数据还在。
运行一个带有容器卷存储功能的容器实例:
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了。
为了能保存数据在docker中使用卷。
特点:
1:数据卷可在容器之间共享或重用数据;
2:卷中的更改可以直接实时生效,即在容器中数据的更改可以实时同步到宿主机中;
docker cp命令和export命令需要手工备份,但是数据卷是自动且实时的备份;
3:数据卷中的更改不会包含在镜像的更新中;
4:数据卷的生命周期一直持续到没有容器使用它为止;
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
使用下述命令,设置容器内的路径为可读可写 (r w)
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
默认容器内权限为 r w
容器实例内部被限制,只能读取不能写 (r o):
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
此时如果宿主机写入内容,可以同步给容器内,容器可以读取到。只限制了容器内路径的权限,未限制主机。
docker run -it --privileged=true -v /mydocker/u:/tmp --name u1 ubuntudocker run -it --privileged=true --volumes-from 父类 --name u2 ubuntu搜索镜像
拉取镜像
查看镜像
启动镜像——服务端口映射
停止容器
移除容器
1.搜索tomcat镜像
docker search tomcat
2. 拉取tomcat镜像到本地
docker pull tomcat
3. docker images查看是否有拉取到的tomcat
docker images tomcat
4. 使用tomcat镜像创建容器实例(也叫运行镜像)
docker run -d -p 8080:8080 --name=t1 tomcat
5. 访问首页:
localhost:8080
首先确定有映射端口且关闭防火墙。
还有一个原因就是:最新版的tomcat对于首页的访问,发生了一些改变。


进入webapps目录,发现里面为空。因此访问会报404错误。将webapps目录删除:

把webapps.dist目录换成webapps

访问成功。
然后停止、删除 t1 容器。
docker stop t1
docker rm -f t1
上述版本的tomcat需要修改webapps之后才能使用,但是也可以使用免修改版的tomcat
下载镜像:
docker pull billygoo/tomcat8-jdk8
运行镜像:
docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8
可以直接localhost:8080访问。
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7docker exec -it 容器ID /bin/bashdocker 容器中运行的MySQL应用存在中文乱码问题。
向表中插入中文数据时,报错。

因为,没有修正docker上默认字符集编码。在MySQL容器中执行:

字符集编码仍是Latin1.
还有一个问题,就是MySQL容器创建时,没有挂载数据卷。如果该容器不小心被删除
docker rm -f 容器id,
那么其中的数据就没有了,损失巨大。
运行镜像:
docker run -d -p 3306:3306 --privileged=true -v /zzyyuse/mysql/log:/var/log/mysql -v /zzyyuse/mysql/data:/var/lib/mysql -v /zzyyuse/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7
–privileged=true:开启数据卷挂载权限
-v:挂载数据卷,将容器中MySQL的日志、数据和配置文件备份到主机
检查容器是否运行成功: docker ps
宿主机上新建my.cnf【通过容器卷同步给mysql容器实例】
编写MySQL配置文件,解决中文乱码问题。

重启mysql容器
docker restart mysql
进入mysql容器:
docker exec -it mysql /bin/bash
然后连接mysql,输入密码:
mysql -uroot -p
SHOW VARIABLES LIKE 'character%';
查看到字符编码为utf8
测试


结论:docker安装完MySQL并run出容器后,建议请先修改完字符集编码后再新建mysql库-表-插数据
假如将当前容器实例删除,再重新来一次,之前建的db01实例还有吗?答案是有。
只要容器在创建时和宿主机映射数据卷,之后容器被删除,再次创建,仍然能保证之前的数据不丢失,数据的安全性得到加固。
入门级:
docker run -d -p 6379:6379 redis:6.0.8docker psdocker exec -it 容器id /bin/bash实战:加容器数据卷+配置文件的指定运行


将daemonize yes注释起来或者 daemonize no设置,因为该配置和docker run中-d参数冲突,会导致容器一直启动失败


4. 使用redis6.0.8镜像创建容器(也叫运行镜像)
docker run -p 6379:6379 --name myr3 --privileged=true -v /app/redis/redis.conf:/etc/redis/redis.conf -v /app/redis/data:/data -d redis:6.0.8 redis-server /etc/redis/redis.conf
最后一个参数表示redis-server使用容器中/etc/redis/redis.conf配置文件启动redis,而容器中的该配置文件是由宿主机上修改后映射至容器的。
查看是否启动成功:docker ps
5. 进入redis容器:
docker exec -it myr3 /bin/bash
连接 redis-cli
set k1 v1
get k1
6. 证明docker启动使用了我们自己指定的配置文件
在修改配置文件之前,能切换至15号数据库:

在宿主机 vim /app/redis/redis.conf 修改配置文件:
将数据库的数量设为10

重启容器:
docker restart myr3
再重新进入容器 myr3:
发现不能切换至15号库,说明宿主机上的配置文件同步到了docker容器中。