Docker入门与实践
2019-10-18 11:04:16 1 举报
AI智能生成
Docker入门与实战
作者其他创作
大纲/内容
1、浅谈虚拟化技术和容器技术
虚拟化技术
虚拟化技术是一种将计算机物理资源进行抽象、转换为虚拟的计算机资源提供给程序使用的技术。
计算机资源
硬盘提供的数据存储资源
网卡提供的网络传输资源
CPU 提供的运算控制资源
将虚拟化应用于资源管理
分配指定运行内存
不同程序可以使用相同端口号
不同程序依赖同一工具库的不同版本
虚拟化分类
硬件虚拟化
软件虚拟化
容器技术
所谓容器技术,指的是操作系统自身支持一些接口,能够让应用程序间可以互不干扰的独立运行,并且能够对其在运行中所使用的资源进行干预。
由于应用程序的运行被隔离在了一个独立的运行环境之中,这个独立的运行环境就好似一个容器,包裹住了应用程序,这就是容器技术名字的由来。
2、Docker 的简介
开源
Docker 项目是一个由 Go 语言实现的容器引擎,它最初由 dotCloud 这家做云服务的公司在 2013 年开源。
为了更好的进行商业运作,Docker Inc. 将 Docker 开源项目的名称修改为了 Moby
效率
如果说在分布式部署中应用容器技术是一个方向,那么 Docker 就是在这个方向上跑得最快也最完美的运动员了。Docker 不论从实现容器技术的完整性上来说,还是从上手易用性来说,都是可圈可点的。
技术实现
命名空间(Namespaces)
命名空间是 Linux 核心在 2.4 版本后逐渐引入的一项用于运行隔离的模块
Linux 内核的命名空间,就是能够将计算机资源进行切割划分,形成各自独立的空间
控制组(Control Groups)
资源控制组 ( 常缩写为 CGroups ) 是 Linux 内核在 2.6 版本后逐渐引入的一项对计算机资源控制的模块
资源控制组的作用就是控制计算机资源的。与以隔离进程、网络、文件系统等虚拟资源为目的 Namespace 不同,CGroups 主要做的是硬件资源的隔离
CGroups 除了资源的隔离,还有资源分配这个关键性的作用
联合文件系统(Union File System)
联合文件系统 ( Union File System ) 是一种能够同时挂载不同实际文件或文件夹到同一目录,形成一种联合文件结构的文件系统。联合文件系统本身与虚拟化并无太大的关系,但 Docker 却创新的将其引入到容器实现中,用它解决虚拟环境对文件系统占用过量,实现虚拟环境快速启停等问题。
理念-应用即容器
容器技术剔除了Hypervisor层,干掉了虚拟机操作系统的概念,让容器中应用运行的消耗与真实操作系统中运行的消耗几乎完全一致
基于联合文件系统的底层文件系统支持,让容器能够很容易在真实操作系统中共享存储资源,并由此带来了对存储空间的低消耗
注意:Docker也支持在容器中同时运行很多种程序,但是其容器设计并不针对这种方案。
Docker的作用
更快、更一致的交付你的应用程序
开发者能够使用 Docker 在本地编写代码并通过容器与其他同事共享他们的工作
他们能够使用 Docker 将编写好的程序推送至测试环境进行自动化测试或是人工测试
当出现 Bugs 时,开发者可以在开发环境中修复它们,并很快的重新部署到测试环境中
在测试完成后,部署装有应用程序的镜像就能完成生产环境的发布
跨平台部署和动态伸缩
让同样的硬件提供更多的产出能力
3、Docker 的核心组成
四大组成对象
镜像
所谓镜像,可以理解为一个只读的文件包,其中包含了虚拟环境运行最原始文件系统的内容。
Docker 实现了一种增量式的镜像结构
每次对镜像内容的修改,Docker 都会将这些修改铸造成一个镜像层,而一个镜像其实就是由其下层所有的镜像层所组成的
当然,每一个镜像层单独拿出来,与它之下的镜像层都可以组成一个镜像
Docker 的镜像实质上是无法被修改的,因为所有对镜像的修改只会产生新的镜像,而不是更新原有的镜像
容器
在容器技术中,容器就是用来隔离虚拟环境的基础设施,而在 Docker 里,它也被引申为隔离出来的虚拟环境
Docker的容器应该由三项内容组成
一个Docker镜像
一个程序运行环境
一个指令集合
网络
网络通讯是目前最常用的一种程序间的数据交换方式。
在 Docker 中,实现了强大的网络功能,我们不但能够十分轻松的对每个容器的网络进行配置,还能在容器间建立虚拟网络,将数个容器包裹其中,同时与其他网络环境隔离
Docker 能够在容器中营造独立的域名解析环境,这使得我们可以在不修改代码和配置的前提下直接迁移容器,Docker 会为我们完成新环境的网络适配
我们甚至能够在不同的物理服务器间实现,让处在两台物理机上的两个 Docker 所提供的容器,加入到同一个虚拟网络中,形成完全屏蔽硬件的效果
数据卷
除了网络之外,文件也是重要的进行数据交互的资源
为了保证数据的独立性,我们通常会单独挂载一个文件系统来存放数据,这些在 Docker 里都已经为我们轻松的实现了,我们只需要简单的一两个命令或参数,就能完成文件系统目录的挂载
能够这么简单的实现挂载,主要还是得益于 Docker 底层的 Union File System 技术。在 UnionFS 的加持下,除了能够从宿主操作系统中挂载目录外,还能够建立独立的目录持久存放数据,或者在容器间共享
在 Docker 中,通过这几种方式进行数据共享或持久化的文件或目录,我们都称为数据卷 ( Volume )
Docker Engine
实现容器化的工具
工业级的容器引擎
Docker Engine由多个独立软件所组成的软件包
核心
docker daemon
Docker 所能提供的容器管理、应用编排、镜像分发等功能
镜像模块、容器模块、数据卷模块和网络模块也都实现在其中
在操作系统里,docker daemon 通常以服务的形式运行以便静默的提供这些功能,所以我们也通常称之为 Docker 服务
我们可以通过dcoker daemon对外暴露的一套RESTful API对运行中的容器和其他资源进行管理
docker CLI
如果我们在控制台中编写一个 HTTP 请求以借助 docker daemon 提供的 RESTful API 来操控它,那显然是个费脑、费手又费时间的活儿。所以在 Docker Engine 里还直接附带了 docker CLI 这个控制台程序
4、搭建 Docker 运行环境
安装前的准备
Docker 容器实现本身就采用了 Linux 内核中很多的特性,我们可以很轻松的将 Docker Engine 安装在 Linux 系统中
Docker Engine的版本
系列
社区版 ( CE, Community Edition )
免费
提供了 Docker 中的容器管理等基础功能
企业版 ( EE, Enterprise Edition )
收费
在社区版的基础上增加了诸如容器管理、镜像管理、插件、安全等额外服务与功能
迭代版本
稳定版(Stable release)
每三个月更新一次
预览版(Edge release)
每个月都更新
在预览版中可以及时掌握到最新的功能特性
Docker的环境依赖
以目前 Docker 官方主要维护的版本为例,使用基于 Linux kernel 3.10 以上版本的 Linux 系统来安装 Docker
CentOS -> CentOS7
Debian -> Debian Wheezy7.7 (LTS)
Debian Jessie 8 (LTS)
Debian Stretch 9
Debian Buster 10
Debian Jessie 8 (LTS)
Debian Stretch 9
Debian Buster 10
Fedora -> Fedora 26
Fedora 27
Fedora 27
Ubuntu -> Ubuntu Trusty 14.04 (LTS)
Ubuntu Xenial 16.04 (LTS)
Ubuntu Artful 17.10
Ubuntu Xenial 16.04 (LTS)
Ubuntu Artful 17.10
注意:在较低版本的 Linux 系统中也能安装 Docker,不过只是版本较低的 Docker,功能上会存在一些缺失
在 Linux 系统中安装 Docker
CentOS
Debian
Fedora
Ubuntu
上手使用
启动 Docker 服务
$ sudo systemctl start docker
实现开机启动 Docker 服务
$ sudo systemctl enable docker
查看 Docker 版本
$ sudo docker versin
查看 Docker 更多相关信息
$ sudo docker info
配置国内镜像源
修改 /etc/docker/daemon.json ( 如果文件不存在,你可以直接创建它 ) 这个 Docker 服务的配置文件达到效果
Docker 提供的国内镜像源
https://registry.docker-cn.com
阿里提供的国内镜像源
https://4si5aj06.mirror.aliyuncs.com
5、在 Windows 和 Mac 中使用 Docker
Docker Desktop
Docker 官方为 Windows 和 macOS 系统单独开辟了一条产品线,名为 Docker Desktop
Docker Desktop 产品线包含两个软件,也就是针对 Windows 系统的 Docker for Windows 和针对 macOS 的 Docker for Mac
安装 Docker Desktop
Windows
必须使用 Windows 10 Pro ( 专业版 )
必须使用 64 bit 版本的 Windows
https://store.docker.com/editions/community/docker-ce-desktop-windows
MacOS
Mac 硬件必须为 2010 年以后的型号
必须使用 macOS El Capitan 10.11 及以后的版本
https://store.docker.com/editions/community/docker-ce-desktop-mac
虚拟机软件 VirtualBox 与 Docker Desktop 兼容性不佳,建议在安装 Docker for Windows 和 Docker for Mac 之前先卸载 VirtualBox
启动 Docker
Docker for Windows 或 Docker for Mac 并启动
打开 Windows 中的 PowerShell 或 macOS 中的 Terminal,亦或者 Git Bash、Cmder、iTerm 等控制台类软件,输入 docker 命令即可
Docker Desktop 的实现原理
由于虚拟化在云计算时代的广泛使用,Windows 和 MacOS 也将虚拟化引入到了系统本身的实现中
在 Windows 中,我们可以通过 Hyper-V 实现虚拟化,而在 macOS 中,我们可以通过 HyperKit 实现虚拟化
重点:在Window 和 MacOS 里搭建一个虚拟Linux系统,并在其智商安装和运行 docker daemon
Docker Desktop 系列最突出的一项功能就是我们能够直接通过 PowerShell、Terminal 这类的控制台软件在 Windows 和 macOS 中直接操作虚拟 Linux 系统中运行的 docker daemon
Docker Desktop 系列最突出的一项功能就是我们能够直接通过 PowerShell、Terminal 这类的控制台软件在 Windows 和 macOS 中直接操作虚拟 Linux 系统中运行的 docker daemon
得益于上面提及到的 docker daemon对外暴露的 RESTful Api
配置 Docker Desktop
双击鲸鱼🐳图标
文件系统挂载配置
资源控制配置
网络配置
docker daemon配置
默认情况下,在 Daemon 面板里只有 Insecure registries 和 Registry mirrors 两个配置,分别用来定义未认证镜像仓库地址和镜像源地址
我们可以点击切换按钮切换到 Advanced 模式,在这个模式下,我们可以直接编辑 docker daemon 的 daemon.json 配置文件,实现更具体、完整的配置 docker daemon 的目的
低系统版本解决方案(Docker Toolbox)
Docker Toolbox for Windows
https://download.docker.com/win/stable/DockerToolbox.exe
Dcoker Toolbox for Mac
https://download.docker.com/mac/stable/DockerToolbox.pkg
由于不能很好的与系统以及 VirtualBox 互通结合,我们启动、关闭、重启 Docker 服务不能完全实现自动化,所以这里 Docker 为我们提供了 Docker QuickStart Terminal 这个工具来处理这些过程
6、镜像与容器
Docker 镜像
深入镜像实现
所有的 Docker 镜像都是按照 Docker 所设定的逻辑打包的,也是受到 Docker Engine 所控制的
Docker 的镜像我们必须通过 Docker 来打包,也必须通过 Docker 下载或导入后使用,不能单独直接恢复成容器中的文件系统
镜像层
Docker 镜像其实是由基于 UnionFS 文件系统的一组镜像层依次挂载而得,而每个镜像层包含的其实是对上一镜像层的修改,这些修改其实是发生在容器运行的过程中的
每个镜像层都有一个全球唯一的Hash码,是一个64长度的字符串
镜像层的共享
举一个实际的例子,由 Docker 官方提供的两个镜像 elasticsearch 镜像和 jenkins 镜像都是在 openjdk 镜像之上修改而得,那么在我们实际使用的时候,这两个镜像是可以共用 openjdk 镜像内部的镜像层的
让镜像可以共用一些存储空间,达到 1 + 1 < 2 的效果
查看镜像
命令:docker images
镜像ID(IMAGE ID)
构建时间(CREATED)
占用空间(SIZE)
....
镜像命名
组成部分
username: 主要用于识别上传镜像的不同用户,与 GitHub 中的用户空间类似
repository:主要用于识别进行的内容,形成对镜像的表意描述
tag:主要用户表示镜像的版本,方便区分进行内容的不同细节
缺省值为 :latest 表示最新版本
容器的生命周期
几个核心状态
Created
Running
在这种状态中的容器,是真正在运行的容器
Paused
Stoped
Deleted
主进程
在 Docker 的设计中,容器的生命周期其实与容器中 PID 为 1 这个进程有着密切的关系。更确切的说,它们其实是共患难,同生死的兄弟
容器的启动和停止和主进程一一对应
当我们启动容器时,Docker 其实会按照镜像中的定义,启动对应的程序,并将这个程序的主进程作为容器的主进程 ( 也就是 PID 为 1 的进程 )
当我们控制容器停止时,Docker 会向主进程发送结束信号,通知程序退出
重点
我们最佳的 Docker 实践方法是遵循着它的逻辑,逐渐习惯这种容器即应用,应用即容器的虚拟化方式
虽然在 Docker 中我们也能够实现在同一个容器中运行多个不同类型的程序,但这么做的话,Docker 就无法跟踪不同应用的生命周期,有可能造成应用的非正常关闭,进而影响系统、数据的稳定性
写时复制机制
Docker 的写时复制与编程中的相类似,也就是在通过镜像运行容器时,并不是马上就把镜像里的所有内容拷贝到容器所运行的沙盒文件系统中,而是利用 UnionFS 将镜像以只读的方式挂载到沙盒文件系统中。只有在容器中发生对文件的修改时,修改才会体现到沙盒环境上
容器在创建和启动的过程中,不需要进行任何的文件系统复制操作,也不需要为容器单独开辟大量的硬盘空间,与其他虚拟化方式对这个过程的操作进行对比,Docker 启动的速度可见一斑
7、从镜像仓库获得镜像
镜像仓库
存放镜像的仓库
个镜像的中转站,我们可以将开发环境上所使用的镜像推送至镜像仓库,并在测试或生产环境上拉取到它们,而这个过程仅需要几个命令,甚至自动化完成
获取镜像
命令:docker pull
当我们拉取时不带标签,默认会使用 latest 标签
Docker Hub:Docker 官方建立的中央镜像仓库
搜索镜像
OFFICIAL 代表镜像为 Docker 官方提供和维护,相对来说稳定性和安全性较高
STARS 代表镜像的关注人数,这类似 GitHub 的 Stars,可以理解为热度
PULLS 代表镜像被拉取的次数,基本上能够表示镜像被使用的频度
在 docker CLI 可以使用命令 docker search 来搜索 Docker Hub 中的镜像
管理镜像
查看本地 docker 中的所有镜像:docker images
查看镜像的详细信息:docker inspect
参数可以为镜像名称或镜像ID
注意:镜像ID不用必须传入整个,只要能唯一识别到镜像,传入一1个字符也是可以的
删除镜像
删除镜像的命令是 docker rmi,参数是镜像的名称或 ID
删除镜像的过程其实是删除镜像内的镜像层,在删除镜像命令打印的结果里,我们可以看到被删除的镜像层以及它们的 ID
注意:当然,如果存在两个镜像共用一个镜像层的情况,你也不需要担心 Docker 会删除被共享的那部分镜像层,只有当镜像层只被当前被删除的镜像所引用时,Docker 才会将它们从硬盘空间中移除
docker rmi 命令也支持同时删除多个镜像,只需要通过空格传递多个镜像 ID 或镜像名即可
8、运行和管理容器
容器的创建和启动
容器的五种状态
Created:容器已经被创建,容器所需的相关资源已经准备就绪,但容器中的程序还未处于运行状态
Running:容器正在运行,也就是容器中的应用正在运行
Paused:容器已暂停,表示容器中的所有程序都处于暂停 ( 不是停止 ) 状态
Stopped:容器处于停止状态,占用的资源和沙盒环境都依然存在,只是容器中的应用程序均已停止
Deleted:容器已删除,相关占用的资源及存储在 Docker 中的管理信息也都已释放和移除
创建容器
命令:docker create
docker create nginx:1.2
nginx:1.2 为镜像名
docker create--name mynginx nginx:1.2
可以通过 -- name 选项来指定容器名
执行命令后,Docker 会根据镜像创建容器,并为容器分配容器ID,此时容器处于 Created 状态
我们更习惯使用容器名来操作容器
启动容器
命令:docker start
docker start mynginx
由于我们为容器指定了名称,这样的操作会更加自然,所以我们非常推荐为每个被创建的容器都进行命名
当容器启动后,其中的应用就会运行起来,容器的几个生命周期也会绑定到了这个应用上
只要应用程序还在运行,那么容器的状态就会是 Running,除非进行一些修改容器的操作
创建于启动结合
命令:docker run
docker run --name mynginx -d nginx:1.2
将 docker create 和 docker start 这两步操作合成为一步
通过 -d 或 --detach 这个选项告诉 Docker 在启动后将程序与控制台分离,使其进入“后台”运行
管理容器
罗列本地 Docker 中的容器
命令:docker ps
docker ps 列出的容器是处于运行中的容器
如果要列出所有状态的容器,需要增加 -a 或 --all 选项
容器信息
CONTAINER ID:容器名
IMAGE:镜像名
CREATED:创建时间
NAMES:容器名
STATUS:容器状态
Created:此时容器已创建,但还没有被启动过
Up [ Time ]:这时候容器处于正在运行状态,而这里的 Time 表示容器从开始运行到查看时的时间
Exited ([ Code ]) [ Time ]:容器已经结束运行,这里的 Code 表示容器结束运行时,主程序返回的程序退出码,而 Time 则表示容器结束到查看时的时间
PORTS:对外暴露的端口号
停止和删除容器
停止容器
命令:docker stop
容器停止后,其维持的文件系统沙盒环境还是存在的,内部被修改的内容也都会保留
删除容器
命令:docker rm
正在运行中的容器默认情况下是不能被删除的,我们可以通过增加 -f 或 --force 选项来让 docker rm 强制停止并删除容器
随手删除容器
Docker 的轻量级容器设计,讲究随用随开,随关随删
容器删除,容器中的内部文件系统的变动会消失
解决方法一:打包新镜像
解决方法二:是用数据卷来保存,因为数据卷是独立于容器存在的
进入容器
命令:docker exec
docker exec 命令能帮助我们在正在运行的容器中运行指定命令
启动bash
docker exec -it mynginx bash
-i和-t不可或缺
-i ( --interactive ) 表示保持我们的输入流,只有使用它才能保证控制台程序能够正确识别我们的命令
-t ( --tty ) 表示启用一个伪终端,形成我们与 bash 的交互,如果没有它,我们无法看到 bash 内部的执行结果
衔接到容器
命令:docker attach
这个命令最直观的效果可以理解为我们将容器中的主程序转为了“前台”运行 ( 与 docker run 中的 -d 选项有相反的意思
由于 docker attach 限制较多,功能也不够强大,所以并没有太多用武之地
9、为容器配置网络
容器网络
沙盒:提供了容器的虚拟网络栈,也就是之前所提到的端口套接字、IP 路由表、防火墙等的内容。其实现隔离了容器网络与宿主机网络,形成了完全独立的容器网络环境
网络:可以理解为 Docker 内部的虚拟子网,网络内的参与者相互可见并能够进行通讯。Docker 的这种虚拟网络也是于宿主机网络存在隔离关系的,其目的主要是形成容器间的安全通讯环境
端点:位于容器或网络隔离墙之上的洞,其主要目的是形成一个可以控制的突破封闭的网络环境的出入口。当容器的端点与网络的端点形成配对后,就如同在这两者之间搭建了桥梁,便能够进行数据传输了
上面三者形成了 Docker 网络的核心,也就是网络模型
浅析 Docker 的网络实现
五种网络驱动
Bridge Driver
Docker 容器的默认网络驱动
通过网桥来实现网络通讯 ( 网桥网络的实现可以基于硬件,也可以基于软件
Host Driver
Overlay Driver
借助 Docker 集群模块 Docker Swarm 来搭建的跨 Docker Daemon 网络,我们可以通过它搭建跨物理主机的虚拟网络,进而让不同物理机中运行的容器感知不到多个物理机的存在
MacLan Driver
None Driver
容器互联
在容器通过 docker create 或 docker run 创建时通过 --link 选项进行配置
$ sudo docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes mysql
$ sudo docker run -d --name webapp --link mysql webapp:latest
$ sudo docker run -d --name webapp --link mysql webapp:latest
在 Web 应用中使用的是 JDBC 进行数据库连接的,我们可以这么填写连接
String url = "jdbc:mysql://mysql:3306/webapp";
String url = "jdbc:mysql://mysql:3306/webapp";
连接地址中的 mysql 就好似我们常见的域名解析,Docker 会将其指向 MySQL 容器的 IP 地址
程序不管在开发环境、测试环境还是正式环境再也不需要繁杂的更改各种链接地址了,只要在程序中配置被连接容器的别名,映射 IP 的工作就交给 Docker 完成了
暴露端口
Docker 为容器网络增加了一套安全机制,只有容器自身允许的端口,才能被其他容器所访问
我们可以通过命令 docker inspect 来看容器暴露的端口号
端口的暴露可以通过 Docker 镜像进行定义,也可以在容器创建时进行定义。在容器创建时进行定义的方法是借助 --expose 这个选项
$ sudo docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes --expose 13306 --expose 23306 mysql:5.7
$ sudo docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes --expose 13306 --expose 23306 mysql:5.7
容器暴露了端口只是类似我们打开了容器的防火墙,具体能不能通过这个端口访问容器中的服务,还需要容器中的应用监听并处理来自这个端口的请求
通过别名连接
纯粹的通过容器名来打开容器间的网络通道缺乏一定的灵活性,在 Docker 里还支持连接时使用别名来使我们摆脱容器名的限制
$ sudo docker run -d --name webapp --link mysql:database webapp:latest
$ sudo docker run -d --name webapp --link mysql:database webapp:latest
使用 --link : 的形式,连接到 MySQL 容器,并设置它的别名为 database。当我们要在 Web 应用中使用 MySQL 连接时,我们就可以使用 database 来代替连接地址了
String url = "jdbc:mysql://database:3306/webapp";
管理网络
容器能够互相连接的前提是两者同处于一个网络中 ( 这里的网络是指容器网络模型中的网络 )
ip:是宿主操作系统的监听 ip,可以用来控制监听的网卡,默认为 0.0.0.0,也就是监听所有网卡
通过 docker inspect 命令查看容器,可以在 Network 部分看到容器网络相关的信息
docker CLI 里与网络相关的命令都以 docker network 开头
创建网络
命令:docker network create
$ sudo docker network create -d bridge individual
通过 -d 选项我们可以为新的网络指定驱动的类型,其值可以是刚才我们所提及的 bridge、host、overlay、maclan、none,也可以是其他网络驱动插件所定义的类型
当我们不指定网络驱动时,Docker 也会默认采用 Bridge Driver 作为网络驱动
通过 docker network ls 或是 docker network list 可以查看 Docker 中已经存在的网络
在我们创建容器时,可以通过 --network 来指定容器所加入的网络,一旦这个参数被指定,容器便不会默认加入到 bridge 这个网络中了 ( 但是仍然可以通过 --network bridge 让其加入
端口映射
需求:容器外通过网络访问容器中的应用
实现:通过 Docker 端口映射功能,我们可以把容器的端口映射到宿主操作系统的端口上,当我们从外部访问宿主操作系统的端口时,数据请求就会自动发送给与之关联的容器端口
用法
创建容器时使用 -p 或者是 --publish 选项
-p <ip>:<host-port>:<container-port>
host-port:宿主操作系统的端口
container-port:容器的端口
使用命令 docker inspect 可以看到端口号的映射关系
可以看到,和 -e 【端口暴露】的区别是 PORTS 多了 -> 表示映射关系
在 Windows 和 macOS 中使用映射
如果是 Docker for Windows 和 Docker for Mac,端口映射的操作程序会自动帮助我们完成
而使用 Docker Toolbox 时,需要我们在 VirtualBox 里的网络配置单独配置
10、管理和存储数据
Docker 容器中的文件系统于我们这些开发使用者来说,虽然有很多优势,但也有很多弊端,其中显著的两点就是
沙盒文件系统是跟随容器生命周期所创建和移除的,数据无法直接被持久化存储
由于容器隔离,我们很难从容器外部获得或操作容器内部文件中的数据
数据管理实现方式
挂载方式
Bind Mount:能够直接将宿主操作系统中的目录和文件挂载到容器内的文件系统中,通过指定容器外的路径和容器内的路径,就可以形成挂载映射关系,在容器内外对文件的读写,都是相互可见的
Volume:也是从宿主操作系统中挂载目录到容器内,只不过这个挂载的目录由 Docker 进行管理,我们只需要指定容器内的目录,不需要关心具体挂载到了宿主操作系统中的哪里
Tmpfs Mount:支持挂载系统内存中的一部分到容器的文件系统里,不过由于内存和容器的特征,它的存储并不是持久的,其中的内容会随着容器的停止而消失
挂载文件到容器
在容器创建的时候通过参数 -v 或 --volume 来指定内外挂载的对应目录或文件
-v : <host-path>或 --volume :<container-path>
docker run -d --name mymysql -v /usr/mysql/data:/var/lib/mysql mysql:5.6
挂载目录
docker run -d --name mymysql -v /usr/mysql/my.cnf:/etc/mysql/my.cnf:ro mysql:5.6
挂载文件,ro为只读
通过 docker inspect 也可以看到容器的挂载信息
Type:bind
挂载临时文件使用参数 --tmpfs
-v 在定义绑定挂载时必须使用绝对路径,其目的主要是为了避免与数据卷挂载中命名这种形式的冲突
使用数据卷
数据卷的本质其实依然是宿主操作系统上的一个目录,只不过这个目录存放在 Docker 内部,接受 Docker 的管理
在使用数据卷进行挂载时,我们不需要知道数据具体存储在了宿主操作系统的何处,只需要给定容器中的哪个目录会被挂载即可
依然可以使用 -v 或 --volume 选项来定义数据卷的挂载
docker run -d --name mymysql -v /var/lib/mysql mysql:5.6
通过 docker inspect 也可以看到容器的挂载信息
Type:volume
Name:数据卷的名称
-v <name>:<contanier-path> 这种形式来命名数据卷
docker run -d --name mymysql -v mysqldata:/var/lib/mysql mysql:5.6
Source:Docker 为我们分配的用于挂载的宿主机目录
场景
数据在多个容器间共享
当我们希望对容器中挂载的内容进行管理时,可以直接利用数据卷自身的管理方法实现
当使用远程服务器或云服务作为存储介质的时候,数据卷能够隐藏更多的细节,让整个过程变得更加简单
共享数据卷
我们使用 -v 选项挂载数据卷时,如果数据卷不存在,Docker 会为我们自动创建和分配宿主操作系统的目录,而如果同名数据卷已经存在,则会直接引用
操作数据卷
创建数据卷:docker volume create mysqldata
列出创建的数据卷:docker volume ls
删除数据卷:docker volume rm mysqldata
在 docker rm 删除容器的命令中,我们可以通过增加 -v 选项来删除容器关联的数据卷
docker rm -v mysqldata
删除没有被容器引用的数据卷:docker volume prune -f
数据卷容器
创建数据卷容器
创建数据卷容器的方式很简单,由于不需要容器本身运行,因而我们找个简单的系统镜像都可以完成创建
docker create --name appdata -v /webapp/storage ubuntu
引用数据卷容器
docker run -d --name webapp --volumes-from appdata webapp:latest
引用数据卷容器时,不需要再定义数据卷挂载到容器中的位置,Docker 会以数据卷容器中的挂载定义将数据卷挂载到引用的容器中
11、保存和共享镜像
提交容器更改
将容器内的修改记录下来,保存为一个新的镜像
命令:docker commit -m="update index.html" -a="Howinfun" nginx(容器ID或容器名) howinfun/nginx:1.0(创建的目标镜像名)
-m:提交的信息
-a:作者名
Docker 执行将容器内沙盒文件系统记录成镜像层的时候,会先暂停容器的运行,以保证容器内的文件系统处于一个相对稳定的状态,确保数据的一致性
在使用 docker commit 提交镜像更新后,我们可以得到 Docker 创建的新镜像的 ID,之后我们也能够从本地镜像列表中找到它
为镜像命名
对未命名的镜像进行命名
如果上面的 docker commit 命令没有早最后指定目标镜像名,那么新创建的镜像将没有 RESPOSITORY 和 TAG
使用命令命名:docker tag 0bc42f nginx:1.0
0bc42f:镜像ID
nginx:1.0:镜像名
对已有的镜像创建一个新的命名
命令:docker tag nginx:1.0 nginx:latest
nginx:1.0:旧镜像名
nginx:latest:新镜像名
旧的镜像依然会存在于镜像列表中
由于镜像是对镜像层的引用记录,所以我们对镜像进行命名后,虽然能够在镜像列表里同时看到新老两个镜像,实质是它们其实引用着相同的镜像层,这个我们能够从镜像 ID 中看得出来 ( 因为镜像 ID 就是最上层镜像层的 ID )。正是这个原因,我们虽然创建了新的镜像,但对物理存储的占用空间却不是镜像大小直接翻倍,并且创建也在霎那之间
镜像的迁移
保存镜像到 docker 外部
sudo docker save webapp:1.0 > webapp-1.0.jar
推荐:sudo docker save -o ./webapp:1.0 webapp-1.0.jar
-o:用来指定输出文件
导入镜像到 docker 内部
sudo docker load < webapp-1.0.jar
推荐:sudo docker load -i webapp-1.0.jar
-i:选项指定输入文件
批量迁移
通过 docker save 和 docker load 命令我们还能够批量迁移镜像,只要我们在 docker save 中传入多个镜像名作为参数,它就能够将这些镜像都打成一个包,便于我们一次性迁移多个镜像
sudo docker save -o ./images.tar webapp:1.0 nginx:1.12 mysql:5.7
多个镜像用空格分开
导出和导入容器
导出
提交镜像,再导出镜像效率是不高的,我们可以直接导出容器,可以理解为 docker commit 与 docker save 的结合体
sudo docker export -o ./webapp.tar webapp
webapp:容器名
导入
使用 docker import 导入。这里需要注意的是,使用 docker import 并非直接将容器导入,而是将容器运行时的内容以镜像的形式导入。所以导入的结果其实是一个镜像,而不是容器
在 docker import 的参数里,我们可以给这个镜像命名
sudo docker import ./webapp.jar web:1.0
12、通过Dockerfile创建镜像
关于 Dockerfile
Dockerfile 是 Docker 中用于定义镜像自动化构建流程的配置文件
在 Dockerfile 中,包含了构建镜像过程中需要执行的命令和其他操作
包含注释行和命令行
Dockerfile 在容器体系下能够完成自动构建,不需要测试和运维人员深入理解环境中各个软件的具体细节,也不需要人工执行每一个搭建流程
编写 Dockerfile
Dockerfile 的结构
五大类指令
基础指令:用于定义新镜像的基础和性质
控制指令:是指导镜像构建的核心部分,用于描述镜像在构建过程中需要执行的命令
引入指令:用于将外部文件直接引入到构建镜像内部
执行指令:能够为基于镜像所创建的容器,指定在启动时需要执行的脚本或命令
配置指令:对镜像以及基于镜像所创建的容器,可以通过配置指令对其网络、用户等内容进行配置
常见 Dockerfile 指令
FORM
指定一个基础镜像,接下来所有的指令都是基于这个镜像所展开的
支持三种形式,其核心逻辑都是指出能够被 Docker 识别的镜像,让 Docker 从这个镜像之上开始构建工作
在 Dockerfile 中可以多次出现 FROM 指令,当 FROM 第二次或者之后出现时,表示在此刻构建时,要将当前指出镜像的内容合并到此刻构建镜像的内容里
RUN
用于向控制台发送命令的指令
在 RUN 指令之后,我们直接拼接上需要执行的命令,在构建时,Docker 就会执行这些命令,并将它们对文件系统的修改记录下来,形成镜像的变化
RUN 指令是支持 \ 换行的,如果单行的长度过长,建议对内容进行切割,方便阅读
ENTRYPOINT 和 CMD
基于镜像启动的容器,在容器启动时会根据镜像所定义的一条命令来启动容器中进程号为 1 的进程。而这个命令的定义,就是通过 Dockerfile 中的 ENTRYPOINT 和 CMD 实现的
ENTRYPOINT 指令和 CMD 指令的用法近似,都是给出需要执行的命令,并且它们都可以为空
当 ENTRYPOINT 与 CMD 同时给出时,CMD 中的内容会作为 ENTRYPOINT 定义命令的参数,最终执行容器启动的还是 ENTRYPOINT 中给出的命令
EXPOSE
暴露容器的端口号,在被其他容器通过 --link 选项连接时,能够直接允许其他容器对这些端口的访问
VOLUME
通过数据卷持久化数据
COPY 和 ADD
将一些软件配置、程序代码、执行脚本等直接导入到镜像内的文件系统里,使用 COPY 或 ADD 指令能够帮助我们直接从宿主机的文件系统里拷贝内容到镜像里的文件系统中
对比 COPY 与 ADD,两者的区别主要在于 ADD 能够支持使用网络端的 URL 地址作为 src 源,并且在源文件被识别为压缩包时,自动进行解压,而 COPY 没有这两个能力
构建镜像
构建镜像的命令为 docker build
$ sudo docker build ./webapp
docker build 可以接收一个参数,需要特别注意的是,这个参数为一个目录路径 ( 本地路径或 URL 路径 ),而并非 Dockerfile 文件的路径。在 docker build 里,这个我们给出的目录会作为构建的环境目录,我们很多的操作都是基于这个目录进行的
在我们使用 COPY 或是 ADD 拷贝文件到构建的新镜像时,会以这个目录作为基础目录
ADD jar/app.jar /root/docker_test/app.jar
ADD 指令中的src是相对路径,相对于 build 后面的目录路径
在默认情况下,docker build 也会从这个目录下寻找名为 Dockerfile 的文件,将它作为 Dockerfile 内容的来源。如果我们的 Dockerfile 文件路径不在这个目录下,或者有另外的文件名,我们可以通过 -f 选项单独给出 Dockerfile 文件的路径
$ sudo docker build -t webapp:latest -f ./webapp/a.Dockerfile ./webapp
当然,在构建时我们最好总是携带上 -t 选项,用它来指定新生成镜像的名称
$ sudo docker build -t webapp:latest ./webapp
实战例子:https://blog.csdn.net/Howinfun/article/details/102514099
13、常见 Dockerfile 使用技巧
构建中使用变量
参数变量
编写 Dockerfile 文件时,搭建环境变量的指令占据大部分比例,而且会涉及一下可变量,例如依赖软件的版本,编译的参数等等
这时候,我们可以使用 ARG 指令来建立一个参数变量,然后在构建时通过构建指令传入这个参数,并且在 Dockerfile 里使用它,这就可以做到可变的。
这时候,我们可以使用 ARG 指令来建立一个参数变量,然后在构建时通过构建指令传入这个参数,并且在 Dockerfile 里使用它,这就可以做到可变的。
例子
FROM debian:stretch-slim
## ......
ARG TOMCAT_MAJOR
ARG TOMCAT_VERSION
## ......
RUN wget -O tomcat.tar.gz "https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz"
## ......
## ......
ARG TOMCAT_MAJOR
ARG TOMCAT_VERSION
## ......
RUN wget -O tomcat.tar.gz "https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz"
## ......
我们需要通过这个 Dockerfile 文件构建 Tomcat 镜像,我们可以在构建时通过 docker build 的 --build-arg 选项来设置参数变量。
sudo docker build --build-arg TOMCAT_MAJOR=8 --build-arg TOMCAT_VERSION=8.0.53 -t tomcat:8.0 ./tomcat
sudo docker build --build-arg TOMCAT_MAJOR=8 --build-arg TOMCAT_VERSION=8.0.53 -t tomcat:8.0 ./tomcat
环境变量
环境变量也是用来定义参数的东西,与 ARG 指令相类似,环境变量的定义是通过 ENV 这个指令来完成的
例子
FROM debian:stretch-slim
## ......
ENV TOMCAT_MAJOR 8
ENV TOMCAT_VERSION 8.0.53
## ......
RUN wget -O tomcat.tar.gz "https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz"
## ......
ENV TOMCAT_MAJOR 8
ENV TOMCAT_VERSION 8.0.53
## ......
RUN wget -O tomcat.tar.gz "https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz"
环境变量的使用方法与参数变量一样,也都是能够直接替换指令参数中的内容
与参数变量的不同之处
环境变量不仅能够影响构建,还能够影响基于此镜像创建的容器。环境变量设置的实质,其实就是定义操作系统环境变量,所以在运行的容器里,一样拥有这些变量,而容器中运行的程序也能够得到这些变量的值。
环境变量的值不是在构建指令中传入的,而是在 Dockerfile 中编写的,所以如果我们要修改环境变量的值,我们需要到 Dockerfile 修改
由于环境变量在容器运行时依然有效,所以运行容器时我们还可以对其进行覆盖,在创建容器时使用 -e 或是 --env 选项,可以对环境变量的值进行修改或定义新的环境变量。
$ sudo docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:5.7
$ sudo docker run -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:5.7
通过 ENV 指令和 ARG 指令所定义的参数,在使用时都是采用 $ + NAME 这种形式来占位的,所以它们之间的定义就存在冲突的可能性。对于这种场景,大家只需要记住,ENV 指令所定义的变量,永远会覆盖 ARG 所定义的变量,即使它们定时的顺序是相反的
合并命令
不但减少了镜像层的数量,也减少了镜像构建过程中反复创建容器的次数,提高了镜像构建的速度
搭配 ENTRYPOINT 和 CMD
用来指定基于此镜像所创建容器里主进程的启动命令
两个指令的区别在于,ENTRYPOINT 指令的优先级高于 CMD 指令。当 ENTRYPOINT 和 CMD 同时在镜像中被指定时,CMD 里的内容会作为 ENTRYPOINT 的参数,两者拼接之后,才是最终执行的命令
ENTRYPOINT 指令主要用于对容器进行一些初始化,而 CMD 指令则用于真正定义容器中主程序的启动命令
使用脚本文件来作为 ENTRYPOINT 的内容是常见的做法,因为对容器运行初始化的命令相对较多,全部直接放置在 ENTRYPOINT 后会特别复杂
14、使用 Docker Compose 管理容器
实战例子:https://blog.csdn.net/Howinfun/article/details/102537191
0 条评论
下一页