1 Docker简介
1.1 Docker为什么出现
- 假定我们现在正在开发一个项目,我所使用的是一台笔记本电脑且我的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。正在开发的应用依赖于当前的配置而且还要依赖于某些配置文件。此外,企业还拥有标准化的测试和生产环境,且具有自身的配置和一系列支持文件。希望尽可能多的在本地模拟这些环境而不产生重新创建服务器环境的开销。那么如何确保应用能够在这些环境运行和通过质量检测?并且在部署过程中不出现令人头痛的版本问题、配置问题,也无需重新编写代码和进行故障修复。
- 答案就是使用容器。Docker之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案——系统平滑移植,容器虚拟化技术。
- 环境配置相当麻烦,换一台机器,就要重来一次,费力费时。是否能从根本上解决呢?也就是说软件可以环境安装。安装的时候,把原始环境一摸一样的复制过来,开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题
- 开发需要清楚的告诉运维部署团队,用的全部配置+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况,Docker的出现使得Docker得以打破过去[程序即应用]的观念。透过镜像(images)将作业系统核心除外,运作应用程序锁需要的系统环境,由下而上打包,达到应用程序跨平台间的无缝接轨运作。
1.2 Docker理念
- 一次镜像,处处运行,从搬家到搬楼
- Docker的主要目标是"build ,ship and run any app,anywhere",也就是通过应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到一次“一次镜像,处处运行”。
- Linux容器技术的出现就就解决这样一个问题,而Docker就是在它的基础之上发展过来的。将应用打成镜像,通过镜像成为运行在Docker容器上面的实例,而Docker容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作
1.3 容器和虚拟机比较⭐️
-
虚拟机就是带环境安装的一种解决方案。它可以在一种操作系统里面去运行另一种操作系统。比如在Windows10系统里面运行Linux系统Centos7。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一摸一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。传统虚拟机技术基于安装在操作系统上的虚拟机冠词系统(如VirtualBox和VMWare等),创建虚拟机(虚拟出各种硬件),在虚拟机上安装从操作系统,在从操作系统中安装部署各种应用【显然这种对计算机资源的消耗比较大】
-
Linux容器(Linux Containers,缩写LXC)是与系统其他部分隔离开的一些列进程,从另一个景象运行,并由该镜像提供支持进程所需的全部文件。容器提供的镜像包含了应用的所有的依赖项,因而在从开发到测试在到生产的整个过程中,它具有可移植性和一致性。Linux容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的资源和设置。系统因此而变的高效轻量并保证了部署在任何环境中的软件都能始终如一。
简单来说容器是一种物理隔离,每个容器都自己的文件系统,它没有进行硬件虚拟,其上所有应用共用一个系统内核;而虚拟机会虚拟出来的一套完整的系统。
Devops新一代开发工程师
1.4 容器为什么比虚拟机快?⭐️
- docker有着比虚拟机更少的抽象层
由于docker不需要Hypervsior(虚拟机)实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此CPU、内存利用率上docker将会在效率上有明显优势。 - docker利用的是宿主机的内核,而不需要加载操作系统OS内核
当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。进而避免引寻、加载操作系统内核返回等比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载OS,返回新建过程是分钟级别的。而Docker由于直接利用宿主机的操作系统,则省略了返回过程,因此新建一个docker容器只需要几秒钟。
1.5 Docker的基本组成⭐️
-
镜像(image)
Docker镜像就是一个只读的模版。本身也是一个文件,称为镜像文件。镜像可以用来创建Docker容器,一个镜像可以创建很多容器,它也相当于是一个root文件系统【这个地方还有一个比较直观的理解就是docker镜像文件类似于Java的类模版】 -
容器(container)
可以把容器看成一个简易版的Linux环境,一个容器运行一种服务,当我们需要的时候,就可以通过Docker客户端创建一个对应的运行实例,也就是我们说的容器。(包括root用户权限、进程空间、用户空间和网络空间)和运行在其中的应用程序。【相当于Java中的对象】 -
仓库(repository)
存放镜像的地方【类似meavn的仓库,存放各种jar包】。Docker公司提供的官方registry被称为Docker Hub,存放各种镜像模版的地方。仓库分为公开仓库(public)和私有仓库(Private)两种形式。最大的公开仓库就是Docker Hub。存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云、网易云等。需要使用的时候拉取下来就可以了。 -
小总结
- Docker本身就是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。
- image文件可以看作是一个容器模版。Docker根据image文件生成一个容器实例。同一个Image文件,可以生成多个同时运行的容器实例。
1.6 Docker一个基本流程⭐️
1.7 Docker架构⭐️
1.7.1 入门版
1.7.2 架构板
Docker是一个C/S模式的架构,后端是一个松耦合架构,众多模块各司其职。Docker运行的基本流程为:
-
用户使用Docker Client与Docker Daemon建立通信,并发送请求给后者;
-
Docker Daemon作为Docker架构的主题部分,首先提供Docker Server的功能使其可以接受Docker Client的请求;
-
Docker Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在;
-
Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动Graph driver将下载镜像以Graph的形式存储;
-
当需要Docker创建网络环境时,通过网络管理驱动Network driver创建并配置Docker容器网络环境;
-
当需要限制Docker容器运行资源或执行用户指令等操作时,则通过Exec driver来完成;
-
Libcontainer是一项独立的容器管理包,Network Driver以及Exec Driver都是通过Libcontainer来具体对容器进行的操作;
2 Docker安装
2.1 Centos Docker安装
Docker并非是一个通用的容器工具,它依赖于已存在并运行的Linux内核环境。Docker实质上是在已经运行的Linux下制造了一个隔离的文件环境,因此它执行的小李几乎等同于所部署的Linux主机。因此,Docker必须部署在Linux内核的系统上,如果其他系统想要部署Docker就必须安装一个虚拟Linux环境。
2.2 前提条件
目前Centos仅发行版本中的内核支持Docker。Docker运行在Centos7(64-bit)上,要求系统为64位,Linux系统内核版本为3.8以上,这里选用Centos7.x。可以使用以下命令查看自己的内核情况(内核版本、硬件架构、主机名称和操作系统类型等)
[xxx@VM-0-8-centos ~]$ cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
[xxx@VM-0-8-centos ~]$ uname -r
3.10.0-1160.11.1.el7.x86_64
2.3 官网安装doc
保证自己的网速健康
官网安装地址
2.4 查看是否安装成功
# 输出client和server的相关信息
docker version
# 会显示你安装成功的信息
docker run hello-world
2.5 镜像加速
配置镜像加速器,需要修改deamon配置文件/etc/docker/daemon.json
bash vi /etc/docker/daemon.json
将registry-mirrors换成你需要的,如我换成了腾讯云镜像加速:
bash "registry-mirrors": [ "https://mirror.ccs.tencentyun.com" ]
3 Docker命令⭐️
3.1 帮助启动类
- 启动/暂停/重启/状态 docker
systemctl start/stop/restart/status docker
- 开启启动
systemctl enable docker
- 查看docker概要信息
docker info
- 查看docker总体帮助文档
docker --help
- 查看docker命令帮助文档
docker 具体命令 --help
3.2 镜像命令⭐️
-
查看所有镜像
docker images # 各项说明 # REPOSITORY:表示镜像的仓库源 # TAG:镜像标签 # IMAGE ID:镜像ID # CREATED:镜像创建时间 # SIZE:镜像大小
-
搜索镜像
# 只显示点赞前5个 docker search [--limit 5]
-
拉取镜像
# 没有TAG就是最新版本 docker pull 镜像名字[:TAG]
-
查看镜像/容器/数据卷所占的空间
docker system df
-
删除镜像
# 删除单个 docker rmi -f 镜像id # 删除多个 docker rmi -f 镜像名1:TAG 镜像名2:TAG # 删除全部:支持参数续传docker images -qa返回所有的镜像ID docker rmi -f $(docker images -qa)
面试题:谈谈docker虚悬镜像是什么?
答:仓库名、标签都是<none>的镜像,俗称虚悬镜像“dangling image”
3.3 容器命令⭐️
-
新建+启动容器😊
docker run [OPTIONS] IMAGE [COMMAND][ARG...] # 比如以下的交互式方式启动ubuntu,后面的/bin/bash为交互命令的接口 docker run -it ubuntu /bin/bash
OPTIONS说明(常用):有些是一个减号,有些是两个减号,常用有以下几组
参数 说明 –name 为容器指定一个名称 -d 后台运行容器并返回容器ID,也即守护式容器(后台启动) 参数 说明 -i 以交互模式运行容器,通常与-t同时使用 -t 为容在重新分配一个伪输入终端,通常与-i同时使用 参数 说明 -P 随机端口映射,大写P -p 指定端口映射,小写p
- 启动式守护容器😊
# 在大部分场景下,我们希望docker的服务是在后台运行的 # 我们可以通过-d指定容器的后台运行模式 # 很重要的一点说明:Docker容器后台运行,就必须有一个前台进程,容器运行的命令如果是不是哪些一致挂起的命令(比如运行top、tail),就会自动退出 docker run -d 容器名
- 进入正在运行的容器😊
上述两个的区别:# 更加推荐使用exec docker exec -it 容器ID /bin/bash docker attach 容器ID
- attach直接进入容器启动命令的终端,不会启动新的进程,用exit退出,会导致容器的停止。
- exec是在容器中打开新的终端,并且可以启动新的进程,用exit退出不会导致容器停止,推荐使用exec
- 查看所有容器
docker ps [OPTIONS] # 查看当前正在运行的容器+历史上运行过的容器 docker ps -a # 显示最近创建创建的容器 docker ps -l # 显示最近的n个容器 docker ps -n # 静默模式 docker ps -q
- 退出容器
# run进去容器,exit退出,容器停止 exit # run进去容器,ctrl+p+q退出,容器不停止 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 ps -a -q | xargs docker rm
- 查看容器日志
docker logs 容器ID
- 查看容器内运行进程
docker top 容器ID
- 查看容器内部细节
docker inspect 容器ID
- 从容器内拷贝文件到主机
docker cp 容器ID:容器内路径 目的主机路径
- 导入容器和导出容器
# export导出容器的内容流作为一个tar归档文件[对应import命令] docker export 容器ID > newName.tar # import从tar包中内容创建一个新的文件系统再导入为镜像[对应export] cat newName.tar | docker import - 镜像用户/镜像名:镜像版本号
4 Docker镜像
4.1 什么是镜像?
- 是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件),这个打包好的运行环境就是image镜像文件。
- 只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)
4.2 分层镜像
镜像分层最大的一个好处就是共享资源,方便迁移,就是为了复用。比如说内存中也只需加载一份base镜像,就可以为所有容器服务了。而且镜像每一层都可以被共享。
4.3 UnionFS⭐️
UnionFs(联合文件系统):Union文件系统(UnionFs)是一种分层、轻量级且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。union文件系统是Docker镜像的基础。镜像可以分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
4.4 镜像加载原理⭐️
- 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。
- 对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己需要提供rootfs就行了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发型版本可以公用bootfs。
Docker镜像层都是只读的,容器层是可写的。当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作"容器层",“容器层"之下的都叫"镜像层”。
4.5 镜像Commit操作案例⭐️
- 案例:ubuntu安装vim
- 从Hub上下载的ubuntu镜像到本地并成功运行;
- 原始的默认Ubuntu镜像是不带着vim命令的;
- 外网连通的情况下,安装vim;
apt-get update apt-get -y install vim
- 安装成功后,commit我们自己的新镜像;
# 提交容器副本使之成为一个新的镜像 docker commit docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
- 启动我们的新镜像和原来的对比;
5 搭建仓库
5.1 搭建Docker Hub
网上查一哈资料
5.2 搭建私有仓库⭐️
-
下载镜像Docker Registry
docker pull registry
-
运行私有库Registry,相当于本地有个私有Docker hub
# 默认情况下,仓库被创建在容器的/var/lib/registry下面。建议自行用容器卷映射,方便与宿主机联调 docker run -d -p 5000:5000 -v /root/myregistry:/tmp/registry --privileged=true registry
-
案列演示创建一个新镜像,ubuntu安装ipconfig命令
apt-get update apt-get install -y ipconfig
-
curl验证私有库上有什么镜像
curl -XGET http://host:5000/tag/_catalog
-
将新镜像修改复合私服规范的Tag
# 镜像tag的格式一般为:Host:Port/Repository:Tag docker tag 镜像:原始tag 新tag docker my_ubuntu:v1 host:8000/my_ubuntu:v1
运行完成后建议重启一哈systemctl restart docker
-
修改配置文件使之支持http
vim /etc/docker/daemon.json # 添加你的仓库端口
添加新的内容
{ "registry-mirrors": [ "https://mirror.ccs.tencentyun.com" ], "insecure-registries":[ "你自己的地址:5000" ] }
-
push推送到私服库
docker push host:8000/my_ubuntu:v1
从这里也能很好得理解多层的这个概念。 -
curl验证私服库上又什么镜像
# 注意这里将host换成你自己的 curl -XGET http://host:5000/v2/_catalog
6 Docker容器数据卷⭐️
坑:docker挂载主机目录访问如果出现cannot open directory:Permission denied
解决办法:在挂载目录后多加一个–privileged=true参数即可
如果是CentOS7安全模块会比之前系统版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为。在SELinux里面挂载目录会被禁止掉,如果要开启,我们一般使用–privileged=true命令,扩大容器的权限解决挂载目录没有权限的问题,也即使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。
6.1 什么是容器数据卷?
- 简单来说,有点类似于我们Redis里面的rdb【内存快照、数据备份文件】和aof【命令日志文件】文件。将Docker容器内的数据保存进宿主机的磁盘中。
- 卷就是一些目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕开Union File System提供一些用于持续存储或共享数据的特性;
- 卷设计的目的就是数据持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷;
6.2 容器数据卷能干嘛?
将运用与运行的环境打包镜像,run后形成容器实例运行,但是我们对数据的要求希望是持久化的。Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了,为了能保存数据在docker中我们使用卷。其具备以下特点:
- 数据卷可以在容器之间共享或重用;
- 卷中的更改可以直接实时生效;【爽】
- 数据卷中的更改不会包含在镜像的更新中;
- 数据卷的生命周期一直持续到没有容器使用它未知;
6.3 数据卷实际案列
6.3.1 宿主vs容器之间映射添加容器卷
- 命令
# 可以挂载多个目录
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内的目录 镜像名
-
查看某一个容器的挂载情况
docker inspect 容器ID
"Mounts": [ { "Type": "bind", "Source": "/root/mytmp", "Destination": "/tmp", "Mode": "", "RW": true, "Propagation": "rprivate" } ],
-
宿主机和容器之间的数据共享
针对这一部分,要深入理解“挂载”这个词语。与传统的粘贴、复制不一样,挂载更像建立一个软连接。因此,所有的更改对二者来说都是实时的。另外加入容器挂掉,而在挂掉的期间,我们往宿主机中加入新的数据,那么在我们重启容器后,这部分新的数据也会被加载到容器当中。
6.3.2 读写规则映射添加说明
- 默认规则
上面的命令等价于以下命令# 默认就是rw,相当于可读可写 docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内的目录:rw 镜像ID
- 容器内部被限制,只能读取不能写
# ro:read only docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内的目录:ro 镜像ID
6.3.3 卷的继承和共享
- 容器1完成和宿主机的映射
与上面类似 - 容器2继承容器1的卷规则
docker run -it --privileged=true --volumes-from 已有容器名 --name 新容器name 镜像名
这个时候,新的容器继承了已有的那个容器的挂在规则,如果在这个时候,已有的那个容器stop了,事实上并不会影响新的这个容器的挂载情况。
7 Docker常用安装简介【附坑】
7.1 总体步骤
- 搜索镜像
- 拉取镜像
- 查看镜像
- 启动镜像
- 停止镜像
- 移除镜像
7.2 安装tomcat
- 搜索镜像
docker search tomcat
- 拉取镜像
docker pull tomcat
- 查看镜像
docker images
- 启动镜像
docker run -it -p 8080:8080 tomcat
- 访问一哈🐱页面
- 浏览器里面输入:host:8080,查看是否运行。这个地方有坑,就是现在新版本的tomcat中webapps为空,换成了webapps.dist,因此我们需要进入到该容器里面修改这个部分,按照下面内容进行修改即可访问🐱。
docker exec -it 容器ID /bin/bash # 先删除掉webapps,再mv webapps.dist rm -rf webapps mv webapps.dist webapps
- 免修改版说明,如果你觉得麻烦,那么你可以使用下面这个不用修改的版本
docker pull billygoo/tomcat8-jdk8 docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8
- 浏览器里面输入:host:8080,查看是否运行。这个地方有坑,就是现在新版本的tomcat中webapps为空,换成了webapps.dist,因此我们需要进入到该容器里面修改这个部分,按照下面内容进行修改即可访问🐱。
7.3 安装mysql
-
搜索mysql镜像
docker search mysql
-
拉取镜像到本地
# 拉取指定的版本 docker pull mysql:5.7
-
运行镜像,创建容器
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
注意如果你本机安装了mysql,一定要注意本地mysql是否将3306这个端口是否被占用。
-
docker里面运行的mysql一定要注意
如果这个时候你插入中文,那么就会报错。这是因为docker里面mysql默认编码有问题。
-
mysql挂载容器数据卷
docker run -d -p 3306:3306 --privileged=true -v /root/mysql/log:/var/log/mysql -v /root/mysql/data:/var/lib/mysql -v /root/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7
-
分别通过这些数据卷来解决上面我们所遇到的问题:在宿主机上面新建一个配置文件my.cnf,将以下内容写入到其中,映射到容器里面:
[client] default_character_set=utf8 [mysqld] collation_server=utf8_general_ci character_set_server=utf8
-
重启
docker restart mysql
7.4 安装redis
- 拉取镜像
docker pull redis
- 运行镜像
# 先在宿主机上创建好配置文件redis.conf,再执行下面的软件 docker run --privileged=true -p 6379:6379 --name redis -v /root/app/redis/redis.conf:/etc/redis/redis.conf -v /root/app/redis/data:/data -d redis:6.0.8 redis-server /etc/redis/redis.conf
- 进入容器中
docker exec -it 镜像ID /bin/bash
- 证明读到自己的配置
修改数据库的个数