docker:快速学习基础使用
发布时间:2020年3月25日
发布者:progor
docker介绍
- 官网
- docker 是一个开源的应用容器引擎,docker 可以让开发者打包他们的应用以及应用的环境依赖到一个轻量级、可移植的容器中,然后发布到任何流行的 平台(linux,windows)上。
- docker基于Go语言。
- docker的相关技术有docker swarm ,kubernates,Compose,这些都是比较热门的容器技术,特别是k8s特别火。
docker是一种容器技术,为什么现在容器技术那么火?
容器能够打包应用和隔离运行环境,这是出发点。
容器打包应用快速而高效。
容器的资源使用效率比VM高。在理论上,他肯定要比原本无容器下更消耗资源,但他换来的是与环境的隔离,避免了不同环境的问题。而他与另一种环境隔离技术--虚拟机技术相比又更加节省资源,因为虚拟机是虚拟出一台完整的机器。容器使用宿主机硬件,不需要像虚拟机一样虚拟硬件。docker使用宿主机的操作系统内核,不需要像虚拟机那样重新创建一个内核(创建内核消耗时间较多)。相应的,容器的启动速度也要快于虚拟机。
容器可以部署在各种平台,只要这个平台能够安装docker,然后就可以在docker上部署容器。
容器的部署很快。如果你使用了一台物理机来部署你的应用,如果某天你的物理机挂了,那么你需要在另外一台机上安装各种环境,然后再部署应用。但容器的话就像一套环境的模板,部署很快。
【最重要的一点】容器的打包是把环境一起打包的,就保证了每个应用的运行环境都会是一模一样的。一次打包,多地运行。很多时候,BUG都是由于环境不同导致的,相同的环境可以避免一些BUG的产生。
现实使用意义:
docker由于其快速部署和一次构建多次部署的特性,使得根据服务器的压力动态快速部署服务端成为可能,比人工部署服务端快速得多。
安装与镜像源配置
CentOS7
安装
1.卸载可能存在的旧版的docker:
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2.安装所需的软件包:
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
3.设置下载docker的yum库
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
4.安装最新版docker-ce
yum install -y docker-ce
5.启动docker
systemctl daemon-reload && systemctl start docker
6.查看docker信息,如果打印成功,那么就是安装成功了:
docker info
设置镜像源
docker创建一个容器需要对应的镜像,而镜像通常都是从docker hub上下载的,docker hub的功能有点像github,都是一种仓库,docker hub是存储镜像的仓库。
但是docker hub是一个国外的网站,他的访问速度不太行,github的不稳定大家应该也深有感受了。所以我们要修改docker 拉取镜像的镜像源,让他从国内的一些镜像网站来拉取镜像,这些国内的镜像都会依靠自己的稳定网络不断同步docker hub。
编辑/etc/docker/daemon.json
文件,把里面registry-mirrors
的值改成下面的:【还可以自行增加阿里云的源】
vi /etc/docker/daemon.json
{
"registry-mirrors": [
"http://hub-mirror.c.163.com",
"https://registry.docker-cn.com"
]
}
阿里的镜像源还是比较稳定的,可以进入阿里云搜索容器镜像服务,然后按照下图来找到加速地址:
补充:
- 其他系统的安装方式,可以参考官方文档:Docker安装
简单使用例子
这是一个使用docker来创建一个包含了mysql的容器,我们借助这个例子来体验docker创建应用容器的快速。
1.安装之后,运行systemctl start docker
启动docker,然后执行以下命令:
docker pull mysql:5.7
当这样显示的时候就是代表成功了。
2.查看镜像:
docker images
3.使用这个镜像创建一个容器(也可以称为镜像实例):
语法:
docker run --name 这个镜像实例的名字 -e MYSQL_ROOT_PASSWORD=mysql根用户的密码 -d -p 主机端口:容器内部的端口 mysql:标签
(MYSQL_ROOT_PASSWORD=123456 =号不要有空格间隔)
(-d代表后台运行,-p是为了把容器内部的mysql绑定到外部端口进行使用)
具体示例:
docker run --name testmysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:5.7
4.查看容器运行状态:
docker container ls
5.测试连接,由于我们使用了-p 3306:3306
,这是将容器内部的3306端口与主机的端口建立的连接,,所以可以直接通过连接主机Linux的端口来操作容器内部的mysql:
假设当前我的机器的IP是192.168.48.129
,那么我需要连接192.168.48.129:3306
来操作数据库,请使用你的mysql连接工具来测试吧😀。
你做完这个实验后,你应该能感受到你大大的节省了安装mysql和部署mysql的功夫,而如果我们创建了一个包含了我们的应用的镜像,那么部署我们的应用的人也能大大减少部署的难度和节省大量的时间。
另外,我们还引出了镜像和容器的概念,这将在下面讲。
基础概念
四个概念
上面引出了镜像和容器的概念,下面讲一下。
- 镜像之于容器,就好像类之于类对象,镜像是生成多个容器的模板,有了镜像,能很快地生成多个容器;
- Images: Images是镜像的意思,镜像里面是通常包含了应用及其应用运行所需要的环境,但他只是一些资源,并没有运行起来。就好像安装操作系统的时候需要IOS文件,但IOS并不是操作系统,IOS只是一些文件。只有通过一些对这些资源的软件处理和硬件处理才可以运行成一个操作系统(在docker中,也需要对镜像做类似的处理)。
- Container:Container是容器的意思,镜像启动后的实例称为容器。容器里面包含独立运行的一个或多个应用。(从虚拟化角度来可,容器就是一个包含了服务端的运行起来的虚拟机,这就好比我在我的虚拟机上装了一个我的应用,应用的环境什么的都装好了,然后我把它的vmx文件发给你,你再通过vmware把这些vmx启动起来。)
- 只有将镜像创建成容器后,里面得应用才是真正得运行起来得(这就好像在面向对象中,平时都是使用具体的类对象);
- Repository:Repository是仓库的意思,仓库保存了各种打包好的镜像,我们需要从仓库中拉取镜像来创建容器。有些人或许会把仓库和仓库注册中心弄乱了,这里以一个例子说明:假如我是tomcat镜像的开发者,我创建了一个tomcat的仓库,就像github的仓库那样,然后我第一次提交了一个tomcat 5.0的镜像,第二次提交了一个tomcat 7.0的镜像,这些镜像都是属于tomcat仓库的,但这些镜像都是属于同一系列镜像(当然了,你就是铁头娃的存储了不同系列的镜像那也没什么好说的,一个仓库里面最好还是存储属于同一个更新系列的镜像比较好),如果你要获取redis的镜像,那么你要从redis仓库获取。而管理着多个仓库的就是仓库注册服务器,
docker hub
就是一个知名的仓库注册服务器,就好像github是个代码托管中心一样。
- Registry:仓库注册服务器,是仓库注册的地方。类比github来解释,比如说github上面有私人的仓库和公开的仓库,私人的仓库并不是谁都能拉取和推送的,所以github在实际上担当了仓库注册服务器,它的下面再有私人仓库和公开仓库。我们指定获取镜像的仓库时,其实首先要指定仓库注册服务器,这样docker才知道我们的仓库是在哪里的。平时我们指定的镜像源地址,其实就是仓库注册服务器,因为可能会不同地址的仓库注册服务器,就好比git界的github与gitlab。镜像仓库网站
通过一个例子把上面的概念串联起来:
假设我们需要拉取一个tomcat镜像。
首先,我们指定这个镜像的仓库在docker hub,然后搜索一下是否有这个仓库,然后指定从这个拉取镜像的哪个版本(tag),然后把它从仓库上拉取下来;
镜像拉取下来之后,使用这个镜像来生成一个容器,这个容器是怎么可以在docker上面运行的呢?这些镜像都会带有可以支撑它运行的操作系统,一个较小的操作系统也就10+M,这些操作系统运行在docker上,而里面的应用运行在各自的操作系统上。(你可以这样类比,docker = vmware,镜像 = vmx文件,容器= vmware对vmx文件运行起来之后的包含了我们的应用的操作系统)
上面的步骤运行起来了一个容器,你可以对这个容器进行操作。(就好像我在简单使用例子一节中讲述的那样)假设你对了这个容器进行了修改,然后你想提交给别人,该怎么做呢?(这里只是文字概念,详情参考下面的把镜像推送到阿里云)
首先,你需要在一个仓库注册服务器中创建自己的仓库,然后在docker中进行登录操作,并基于修改的容器生成一个镜像,把这个镜像推送到仓库上。然后别人就可以从你的仓库中拉取下镜像了,如果你的仓库是个开放的仓库的话。
镜像概念补充:
- 镜像用于生成容器,它就是生成容器的模板。
- 可以说镜像就是停止的容器,在后面我们甚至可以学到通过容器来生成自定义的镜像。
- 镜像是应用与依赖环境的打包,镜像里面的应用是需要运行在操作系统之上的,镜像本身都会携带一个比较小型的操作系统,这些操作系统都去除了大多数无用的内容,比如一个简单的linux镜像 Alpine Linux 大约只有 10+MB, Ubuntu镜像大概100+M。
- 其实上面说了,镜像本质上还是一些文件而已,不过这些文件可以被docker以容器的方式运行起来,因为镜像的文件包含了操作系统,docker实质上就像是运行一个小型操作系统那样运行容器。
- 镜像是实质是一个文件系统,由于现在还只是开头,先不引入那么多概念了,而且这东西的概念性内容比较多,可能会后面再整理一章节来讲。
容器概念补充:
- 容器用于运行应用或者服务,所以镜像中必须包含应用或服务所必要的操作系统和应用文件。
- 容器的运行要基于操作系统,为了在保证应用可以运行的同时节省资源,会对不必要的操作系统文件和应用文件进行了缩减。就好像以前的Linux操作系统其实很小,现在的Linux操作系统随随便便就几百上千M,如果削减了一些不必要的功能,
常用命令:
查看docker信息
- 查看本地docker版本:
docker version
或者docker -v
,会返回安装的docker的版本。 - 查看本地docker信息:
docker info
镜像操作
- 搜索docker仓库相关镜像(或者应当说在仓库注册服务器搜索仓库吧,不过我们下载的是镜像而不是仓库罢了):
docker search 镜像关键字
,例如docker search mysql
。你除了在命令行中搜索,也可以在docker hub
上搜索镜像。docker search 镜像关键字 -s 数字A
:列出start数不小于指定数字A的镜像。【如果提示--star过期,那么使用docker search 镜像关键字 -filter=stars=数字A
】【star类似于github中的star,也表示这个镜像的受欢迎程度】docker search 镜像关键字 --no-trunc
:完整显示镜像的描述Descriptiondocker search 镜像关键字 --automated
:只列出automated build类型的镜像。
如果你在docker hub上搜索镜像, 那么首先进入docker hub(https://hub.docker.com/),然后里面有个搜索框。搜索mysql之后你可以看到mysql相关的镜像,随便点进去一个,你可以看到这个镜像的Description(描述),Tags(可以看到有什么版本),Reviews(评价),而在镜像的旁边会有一个
docker pull xxxx
(xxxx是镜像的名字),这个就是教你怎么拉取镜像。(拉取下面讲)
- 从仓库拉取镜像:
docker pull 镜像名:tag
。:tag
是可选的,tag表示标签,多为软件的版本,默认是latest。比如docker pull mysql:5.7
就代表拉取5.7版本的mysql镜像。
*tag表示标签,多为软件的版本,对于一个镜像有什么tag,建议到docker hub上搜索这个镜像,里面会显示这个镜像的tag。
- 查看所有本地已有镜像:
docker images
docker images -a
:显示所有镜像(包含中间映像层)。中间映像层的意思是,一个镜像可能是由多层镜像层叠而来的,这中间的部分就是中间映像层。(这个知识与镜像的实质有关,什么时候再写一章单独分析。)docker images -q
: 只显示ID,不显示镜像名。docker images --no-trunc
: 显示完整的镜像信息,主要是完整显示了Image ID,就跟git的版本ID差不多,短的ID也可以用来标识镜像。docker images --digests
: 显示镜像的摘要信息。每一个机器上拉取下来的同一个镜像生成的镜像ID是不一样的,只有摘要才能判断他们是否是同一个镜像。
- 删除指定的本地镜像:
docker rmi 镜像的ID
或者docker rmi 镜像名:tag
- 可以同时删除多个镜像:
docker rmi 镜像的ID1 镜像的ID2
或者docker rmi 镜像名1:tag 镜像名2:tag
- 删除全部:
docker rmi ${docker images -qa}
,这是把docker images -qa的结果作为删除镜像的参数。 -f
:强行删除镜像
- 可以同时删除多个镜像:
- 使用镜像创建一个容器实例:
docker run [镜像ID]或[镜像名:tag]
--name 容器的名字
:定义容器的名字--detach
或-d
:让容器后台运行-p 主机的端口:容器的端口
:将容器的端口绑定到主机的端口- 除了
主机的端口:容器的端口
,还可以是IP:主机的端口:容器的端口
、IP::容器的端口
、容器的端口
- 除了
-P
:随机对容器开放的端口进行绑定。容器开放的端口是需要声明的,比如tomcat镜像中会声明开放端口8080。随机绑定就会把声明的端口随机与主机端口绑定。--env
或-e
:设置容器的环境变量,有些容器需要环境变量来设置一些数据,比如mysql的初始密码。- 示例:
docker run --name testmysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:5.7
- 如果本地不存在这个镜像,那么会从仓库拉取,如果仓库中也没有,那么就会报错。
- 同时执行命令:
docker run [镜像ID]或[镜像名:tag] 命令
,这个会在启动容器之后,帮我们执行我们指定的命令。比如docker run -t centos:7 top
就会在启动centos容器后,帮我们执行top
,此处我没用使用-i所以会当场看到top命令的执行。【这个小心使用吧,因为有时候可能会覆盖容器内部的一些命令,比如如果你在运行mysql镜像时额外执行命令,那么将会导致这个容器无法运行,这将在Dockerfile处讲解】
容器操作
- 查看正在运行的容器:
docker ps
。docker ps -a
查看所有容器,包括没有在运行的。docker ps -l
查看最近创建的容器(一个)docker ps -n 数字
查看最近创建的n个容器docker ps -q
:只显示容器IDdocker ps --no-trunc
:只显示容器ID
- 查看容器日志:
docker logs 容器名称或者容器的ID
,有时候容器的启动问题就要依靠日志来解决,容器的故障信息都会显示在日志中。有些应用会把应用的运行日志写在容器日志中,但有些会写在容器内的文件中。docker logs -t 容器名称或者容器的ID
:加入时间戳docker logs -f 容器名称或者容器的ID
:持续打印docker logs --tail 数字 容器名称或者容器的ID
:显示最后多少行日志docker logs --since 时间 容器名称或者容器的ID
:查看什么时间的日志,时间可以是30m
,1h
,或者2020-02-02
,或者2013-01-02T13:23:37
(UTC统一时间),是从什么时候开始的意思,会从指定时间取到现在。
- 停止容器:
docker stop 容器名称或者容器的ID
- 启动容器:
docker start 容器名称或者容器的ID
- 删除指定容器:
docker contatiner rm 容器名称或者容器的ID
,容器必须先停止才能被删除- 删除所有容器:
docker rm -f ${docker ps -a -q}
docker ps -a -q | xargs docker rm
- 删除所有容器:
- 向容器发命令:
docker exec 容器名称或者ID 命令
,- 发生命令并实时交互
docker exec -it 容器名称或者ID linux命令
:-it
可以说是进入了交互界面,而没有-it
的时候只是发了一个请求,显示的只是请求的结果。-t
:在新容器内指定一个伪终端或终端。-i
:允许你对容器内的标准输入 (STDIN) 进行交互。it可以分开使用。 - 有些命令是必须以-i模式交互的,比如现在的mysql连接的时候要求你在交互的时候输入密码。
- 示例:
docker exec testmysql echo hello
- 发生命令并实时交互
- 进入容器:
docker attach
,与docker exec -it
的效果有点类似。但它进入的其实是容器启动时最后执行的命令。- 比如说
docker run -td centos:7 top
,创建容器的时候执行了一个top命令,那么attach进入的就是这个top命令,如果你执行的是其他命令,attcach也是进去那个命令的情况下。所以可能有些容器你attach进去是一片空白。【当你在attach中关闭了这个容器最终运行的进程的时候(注意,是关闭,退出并不代表一定关闭),这个容器可能会自动关闭,因为你关闭了他的前台进程。如果你是在终端命令下,可以使用ctrl+p+q
来退出容器但不关闭终端,退出直接用exit。】 - 还有一个attach与exec的不同之处,exec是每次都执行新的命令的(也就是说命令是用新的进程来执行),并不影响原有的容器的前台进程,而attach是进入了容器的前台进程。
- 比如说
- 查看容器的端口绑定情况:
docker port 容器名称或者ID
- 查看容器内运行的进程:
docker top 容器名称或者ID
- 查看容器的底层信息:
docker inspect 容器名称或者ID
,会显示容器名称、容器数据存储位置、网络设置、环境变量、容器卷等信息。
- 从本地复制文件到容器中:
docker cp 本地文件路径 容器ID:容器路径
- 从容器复制文件到本地:
docker cp 容器ID:容器路径 本地文件路径
- 根据Dockerfile生成镜像:
docker build
【Dockerfile的内容,后面讲】
- 根据容器来创建一个镜像:
docker commit
【生成新镜像的内容,后面讲】
补充:
- 命令帮助:
docker --help
- 更多命令可以参考命令文档,虽然他们的文档讲解很一般。
当你学完上述的命令的时候,你应该能理解之前的简单使用例子中的那些命令的意义了。
上述命令讲解中,简单例子中没用到的命令,也讲述了基本用法。
下面将是一些稍微深入一点的内容。
- 比如容器卷
- 比如有如何制作docker镜像。
- 比如有如何推送镜像到阿里云。
- 以及会补充之前一些概念性的内容。
如果你还不是很了解镜像的拉取、容器的创建和运行命令的话,建议先从各方面尝试使用各种命令,做一些帮助自己了解的测试。
关于环境变量:
- 镜像可能会支持自己的一些参数,就比如说创建mysql镜像实例的时候需要提供root账号的初始密码,这些参数通常通过环境变量来指定。
这一些都通常可以在docker hub中搜索镜像的时候在镜像的How to use this image
模块看到:
容器内容补充:
资源节省方面:
- 为了在保证应用可以运行的同时节省资源,会对不必要的操作系统文件和应用文件进行了缩减。比如下图的mysql容器中也是有一个Linux系统,但从mysql的大小来看,我们可以猜到这个操作系统的大小还是远小于正常的系统镜像的:
- 查看mysql容器中操作系统核心:
- mysql容器的大小:
自动狗带
当一个容器发现自己无事可做的时候就会自动狗带(自杀)。
比如说你运行centos容器:docker run centos:7
那么你会发现这个容器一启动就自动停止了。这是因为centos内部并没有前台进程,如果docker检测到这个容器没有前台进程,那么就会自动关闭这个容器。
docker run -td centos
这句命令可以让centos不自动停止,因为使用-t就为这个容器创建一个前台进程/bin/bash
,平时使用-it的时候,会直接进行交互,我们去掉-i和加上d,使得这个交互终端挂起,不马上交互。
后面我们再想交互的时候,可以使用docker attach 容器ID
或者docker exec -it 容器ID 命令
来进行交互。
上面说了attach的使用注意事项,由于这里又涉及了attach,再说一遍:
- 进入容器:
docker attach
,与docker exec -it
的效果有点类似。但它进入的其实是容器启动时最后执行的命令。- 比如说
docker run -td centos:7 top
,创建容器的时候执行了一个top命令,那么attach进入的就是这个top命令,如果你执行的是其他命令,attcach也是进去那个命令的情况下。所以可能有些容器你attach进去是一片空白。【当你在attach中关闭了这个容器最终运行的进程的时候(注意,是关闭,退出并不代表一定关闭),这个容器可能会自动关闭,因为你关闭了他的前台进程。如果你是在终端命令下,可以使用ctrl+p+q
来退出容器但不关闭终端,退出直接用exit。】
解决自动狗带这个问题的方法就是把你的应用以前台的方式来运行,又或者创建一个额外的前台进程,所以对于那些是后台运行的应用要注意
其他概念补充:
Daemon守护进程
在执行docker命令的时候,有时候docker还没启动,那么可能会报一个这样的错误。
你应该看到了一个docker daemon,daemon在计算机领域通常是守护进程的意思,下面我们来介绍一下这个的意义。
docker是一种Client-Server结构的系统,其实我们对于docker的所有操作都是守护进程来帮我们发送给docker的,我们平时运行命令其实是在Docker Client,Docker Client通过命令行与Docker Damon通信,完成Docker相关操作。
我们表面上操作docker,但实际上,我们只是操作守护进程,守护进程来帮我们发送命令。
容器卷Volume
- Volume主要用来共享主机和容器的数据。
- 在不使用容器卷的时候,容器内的硬盘数据是独立的,主机无法直接读取容器内的硬盘文件(复制可以),而且这时候如果删除了容器,容器内的硬盘数据也会丢失。
- 而且因为这样,数据与容器关联性也变强了,我们不能根据这个容器来生成镜像了,可能有时候也无法直接很方便的快速部署,所以外部持久化,把数据从容器中抽离出来就可以降低耦合性。
- 如果我们想要对容器内的数据进行外部持久化,那么就需要使用上容器卷了。
- Volume的效果有点像mount,把主机的某个文件夹与容器内的文件夹进行了映射,容器的关闭并不会清空主机上的文件夹,从而把容器的数据持久化到了主机上。
命令:
- 创建容器的时候指定容器卷:
docker run -it -v /宿主机路径:/容器内路径 镜像名
- 当目录并不存在的时候,会自动创建。
- 你可以试验一下,在内部的指定容器卷目录中创建一个有数据的文件,然后再去主机看是否存在,并修改,看修改的内容容器中能否看到。
- 只读模式:
docker run -it -v /宿主机路径:/容器内路径:ro 镜像名
,默认创建的容器卷是两边都可读可写的,加上ro之后,是容器只读,主机可读可写。
- 创建一个Volume:
docker volume create 容器卷名称
- 此时将自动在主机的docker目录下指定一个目录作为容器卷在主机的目录,比如
/var/lib/docker/volumes/hello/_data
- 创建的容器卷的信息可以使用
docker volume inspect 容器卷
名称来查看 - 容器卷名称为空的时候将使用一个64位的随机的名称。
- 此时将自动在主机的docker目录下指定一个目录作为容器卷在主机的目录,比如
- 查看Volume的详细信息:
docker volume inspect 容器卷名称
- 显示docker的Volume列表:
docker volume ls
-q
只显示容器卷名称
- 删除未使用的Volume:
docker volume prune
,会移除没有被容器使用的容器卷。
- 删除一个或多个Volume:
docker volume rm 容器卷名称
容器卷的数据传递
可以使用--volumes-from
来继承另一个容器的容器卷。
比如:
先执行:docker run --name centos1 -itd -v /outpath:/containerpath centos:7
再执行:docker run --name centos2 -itd --volumes-from centos1 centos:7
然后再执行:docker exec -it centos2 cat /containerpath/a
你就可以看到centos2中也配置了容器卷/outpath:/containerpath
,也就是说,centos2继承了centos1的容器卷。
- 由于他们对应的都是主机的同一个目录,centos1修改的centos2也能看到,反之也是,主机中修改的centos1和centos2都可以看到。
- 而且它的继承只是配置信息的传递罢了,虽然上面说的是centos2继承了centos1的容器卷,但centos1关闭了也不会影响centos2读写容器卷的内容
- centos1是挂载了容器卷的容器,可以称为数据卷容器,其他的容器通过继承它的容器卷,就可以实现多个容器之间的数据共享。
补充:
- 容器卷除了这样加,还可以使用Dockerfile来添加,这在后面讲。
怎么制作docker镜像
- 一种是通过拉取已有的镜像创建容器,然后对容器内部进行修改,然后使用命令
docker commit
通过容器来生成一个新的镜像。 - 一种是通过制作Dockerfile,然后
docker build
来创建镜像。
修改已有镜像:
1.比如说我们可以拉取一个tomcat镜像,然后生成一个容器;
docker pull tomcat:8.0
2.启动这个容器:
docker run --name mytomcat -d -p 8888:8080 tomcat:8.0
3.对容器进行一些自定义的修改,我们这里把我们的一个非常简单的应用部署到tomcat上。【这个war是我从一个structs的jar包下弄的,是一个很干净的war,适合用来做实验】war下载地址
- 通过
docker cp 本地文件路径 容器ID:容器路径
命令把我们的war包复制到tomcat中。
docker cp /struts2-blank.war mytomcat:/
- 进入容器:
docker exec -it mytomcat /bin/bash
把容器内的war包复制到容器的tomcat中:
root@9ce9d01569e6:/usr/local/tomcat# cp /struts2-blank.war webapps/
然后重启容器:
docker restart mytomcat
【你这里可以玩一下,在cd /usr/local/tomcat/bin/
下执行shutdown.sh,你就会发现没过一会,容器就自动停止了。】
然后你在外部访问http://主机地址:8888/struts2-blank
就可以访问到
那么现在,这个容器可以说已经有了我们的应用了,那么怎么基于这个容器来创建新的镜像,以方便通过镜像来方便部署到一台台机器上呢?
4.然后提交镜像。
docker commit 可以提交容器副本,使之成为一个新的镜像。
命令:docker commit -a "作者名" -m "提交说明" 容器名称或id 打包的镜像名称:标签
选项说明:
-a :提交的镜像作者;
-c :使用Dockerfile指令来创建镜像;
-m :提交时的说明文字;
-p :在commit时,将容器暂停。
[root@localhost /]# docker commit -a "progor" -m "简单的新镜像测试" mytomcat mytomcat2:1.0
sha256:a3393dbf9326d0f065d6ee8d757c6d7c1050df8a513e6a52286b4af91154806b
5.查询一下本地镜像列表,可以看到我们刚刚创建的镜像:
6.我们基于这个镜像来创建一个新的容器看看,看是不是有我们的应用了:
docker run --name mytomcat2 -d -p 8889:8080 mytomcat2:1.0
7.然后再在外部访问一下http://主机地址:8889/struts2-blank
,可以访问,证明了新的镜像包含了我们之前部署的应用。
使用Dockerfile
使用Dockerfile其实也是通过修改已有镜像来创建新镜像,但它把修改的命令都存储到Dockerfile中了。Dockerfile比手动修改更方便管理。
下面列一个mysql 5.7的示例Dockerfile,由于篇幅问题,这里就不直接列了,请参考下列链接,然后介绍一下之后,我们就学习相关的命令。
mysql/5.7/Dockerfile
实例的介绍:
上面的示例的内容也比较长,我不会所有都讲,只会讲基本流程。
从上面的流程来看,其实就是引入了一个基础的镜像之后,配置环境变量等各种参数,其实也就是讲我们之前的手动的操作换成了换成命令写在了Dockerfile了。
常用命令:
- FROM:用于指定一个基础的镜像。
- 通常FROM命令都是第一个命令。用来导入基础的镜像,比如可能tomcat这个镜像要基于jdk,如果要创建一个有tomcat的镜像,那么这个镜像的基础必须得有jdk镜像。所以如果我们要创建一个运行于tomcat上的Web应用的镜像的话,我们首先需要一个tomcat作为基础镜像。
- 基础镜像涉及到了一些镜像实质的知识,其实一个镜像可以说是层层继承下来的,比如说centos继承一个根源镜像,jdk继承这个centos镜像,tomcat继承这个jdk镜像。
- FROM 可以出现多次,用来继承多个镜像。这多个镜像中,共同的部分只会继承一次。
- 指定基础镜像的时候也需要指定tag,如果不指定,那么tag默认是latest。
- 示例:
FROM debian:buster-slim
【来自 mysql/5.7/Dockerfile 】
- RUN:用来执行命令。
- 格式:
RUN 命令
或者RUN ["可执行文件"," 参数1","参数2"]
,例如:RUN ["/bin/bash", "-c", "echo hello"]
RUN 命令
是在shell中执行的,会做Shell处理,多行命令,可以使用\
分行。RUN ["可执行文件","参数1","参数2"]
是非SHELL的,所以它不会进行上下文变量解析,例如RUN [ "echo", "$HOME" ]
- RUN命令还有一些与中间映像层相关的,这将在后面单独讲。
- 示例:
RUN groupadd -r mysql && useradd -r -g mysql mysql
【来自 mysql/5.7/Dockerfile 】
- 格式:
- ENV:用于为容器设置环境变量。
- ENV设置的环境变量,可以使用
docker inspect
命令来查看。 docker run --env 环境变量名=变量值
可以在运行时指定环境变量。会覆盖默认的环境变量。- 可以使用
$变量名
或者${变量名}
来获取指定环境变量的值。 - 示例:
ENV TINI_VERSION 0.14.0
【来自jenkins/Dockerfile 】 - 在获取环境变量的时候,
${variable:-word}
表示如果variable设置,则结果将是该值。如果variable未设置,则为word结果;如果variable设置了,则将是word结果,否则结果为空字符串。word都可以是任何字符串,包括其他环境变量。可以通过\
来进行转义:例如,\$foo
或\${foo}
将分别转换为$foo
和${foo}
文字。 - 环境变量可以使用在以下命令中:ADD,COPY,ENV,EXPOSE,FROM,LABEL,STOPSIGNAL,USER,VOLUME,WORKDIR,ONBUILD(1.4以前不支持环境变量)。
- 如果你在ENV中既修改了变量又获取了这个变量,获取的变量的值会是旧的值,而不是设置的新的值。
ENV abc=bye def=$abc
- ENV设置的环境变量,可以使用
- USER:用来指定运行容器的时候使用的用户以及可选的用户组,默认使用ROOT用户
- 格式:
USER <user>[:<group>]
或者USER <UID>[:<GID>]
- 示例:
USER ${user}
【来自jenkins/Dockerfile 】
- 格式:
- WORKDIR:用来修改默认的工作目录,也就是你刚进去容器的工作目录。当容器启动时,默认的工作目录是
/
- 对于工作目录的修改,对后面的使用了相对路径的Dockerfile命令也是有影响的,。
- 示例:
WORKDIR /data
【来自 redis/6.0-rc/Dockerfile 】
- COPY:
- 格式:
COPY [--chown=<user>:<group>] <src> <dest>
将文件从路径<src>
复制到容器路径<dest>
中。 - src是一个相对路径,或者一个URL;dest是一个绝对路径
- 示例:
COPY docker-entrypoint.sh /usr/local/bin/
【来自 redis/6.0-rc/Dockerfile 】
- 格式:
- ADD:
ADD [--chown=<user>:<group>] <src> <dest>
:将文件从路径<src>
复制到容器路径<dest>
中,并解压。[--chown=<user>:<group>]
是可选的,用于修改文件的所属用户或所属组,但仅使用与Linux容器
*src是一个相对路径,或者一个URL;dest是一个绝对路径
- VOLUME:创建容器卷,这样会随机创建一个与容器内容器卷相对于的主机目录。
- 示例:
VOLUME /data
【来自 redis/6.0-rc/Dockerfile 】
- 示例:
- EXPOSE:
- EXPOSE指令通知Docker容器在运行时侦听的端口,在上面的
docker run
中有说到可以随机端口绑定,容器怎么知道为我们绑定哪些容器内部的端口呢,就是通过expose知道的。 EXPOSE <port> [<port>/<protocol>...]
- 暴露端口的时候还可以指定端口的连接方式:比如
EXPOSE 80/tcp
或EXPOSE 80/udp
,默认使用TCP - 无论EXPOSE设置如何,都可以在运行时使用-p标志覆盖它们。
- 示例:
EXPOSE 6379
【 redis/6.0-rc/Dockerfile 】
- EXPOSE指令通知Docker容器在运行时侦听的端口,在上面的
- CMD:CMD也是用来执行命令的,但他只能生效一次,如果你有多个,那么最后指定的生效。
- 格式:
CMD ["可执行命令或文件","param1","param2"]
,CMD ["param1","param2"]
,CMD 命令 param1 param2
- 示例:
CMD ["redis-server"]
,CMD [ "sh", "-c", "echo $HOME" ]
,CMD echo hello
,CMD ls -a
,CMD ["ls","-a"]
- 格式:
- ONBUILD:当将镜像作为构建另一个镜像的基础时,也就是FROM的时候,ONBUILD才会触发,他不会在自己构建镜像时触发,只会在别人FROM它的时候才会触发。
- ONBUILD让指令延迟到下一个使用FROM的Dcokerfile建立镜像时才执行(只能传一代,不会传到孙子,也就是说只能延迟一次)。
- ONBUILD 主要用于定制基础镜像,通过一系列命令来定制基础镜像。首先,你要判断这些命令是应该在子代时执行,而基础镜像中不应该执行的,而且这些命令应该是基于子代的上下文环境的,比如有一个代码解析器,在父代的时候,他应该还不知道他要解析的代码在那里,而在子代的时候才知道在哪里,你或许可以说可以在子代的Dockerfile中把解析器复制过去解析器的目录下,但ONBUILD提供了一个更好的方案,如果你在基础镜像的ONBUILD中定义“把解析器复制过去解析器的目录下”操作,由于ONBUILD的基础时才运行,所以他使用了子代的上下文环境,它就知道了代码文件在哪里,然后他再“把解析器复制过去解析器的目录下。
- 示例:
ONBUILD RUN echo hello
ONBUILD的使用例子,没有结合场景的
- LABEL:用来添加一些镜像元数据。用来指定镜像的所有者/创建者、版本等各种元信息,这些元信息对于镜像没有影响,仅用于标识,
- 格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
- 指定创建人,以前使用
MAINTAINER 创建者
现在推荐使用LABEL maintainer="创建者"
- 指定镜像所有者,
LABEL OWNER="所属者"
- 要查看LABEL信息,可以使用
docker inspect
命令。 - 示例:
LABEL maintainer="SvenDowideit@home.org.au"
【官方文档示例】
- 格式:
- ENTRYPOINT:用于指定通过镜像创建或运行容器时,要执行的命令或者文件。
- 示例:
ENTRYPOINT ["docker-entrypoint.sh"]
【来自 redis/6.0-rc/Dockerfile 】 - 那么它与CMD的区别呢?有时候我们能在Dockerfile中看到ENTRYPOINT命令之后还有CMD命令。
- 当你在RUN中附加命令的时候,会附加在ENTRYPOINT后面,并覆盖CMD.
- CMD应该用作ENTRYPOINT在容器中定义命令或执行临时命令的默认参数的方式。
- 示例:
- ARG:
- ARG是docker1.9 版本才新加入的指令。
- ARG 定义的变量只在建立 image 时有效,建立完成后变量就失效消失。
- ARG定义的变量可以在Dockerfile中使用
${group}
来获取。 - 同名的ENV环境变量始终会覆盖ARG同名变量
ARG user=jenkins
【来自jenkins/Dockerfile 】
补充:
- 可以在Dockerfile中使用#来做单行注释,例如
# add gosu for easy step-down from root
- 你应该可以看到上面的RUN命令的时候通常会执行很多命令,这些命令使用
\
来分行。 - 另外,在使用环境变量或者ARG变量的时候,如果变量值有空格,要要使用""包裹,例如
cd "$OPENSSL_PATH"
- 说一下RUN,CMD,ENTRYPOINT的区别:
- RUN它用于执行命令,做的主要是一些准备工作,比如创建用户、创建目录、安装程序,并不直接与应用的启动相关。
- ENTRYPOINT用于指定创建和运行容器时要执行的命令,所以你可以在ENTRYPOINT中指定创建容器的时候要运行的一些命令,或者可以说,它是RUN的组合,但ENTRYPOINT更具有
应用
意义。RUN可以随便创建一个目录,或许做的只是一些运行的前置工作;但ENTRYPOINT里面应该与这个容器前台进程(应用)的运行更加相关,比如定义如何启动应用。当然你也可以使用RUN创建前台进程😓。。。 - CMD也是用来指定容器的前台进程的,但有了ENTRYPOINT的时候,它将作为ENTRYPOINT的参数,比如上面提到的MYSQL的例子中 mysql/5.7/Dockerfile ,最后的是
CMD ["mysqld"]
,而前面有ENTRYPOINT ["docker-entrypoint.sh"]
,这将会在执行docker-entrypoint.sh的最后执行mysqld
,内部有没有再处理mysqld,就要看具体的docker-entrypoint.sh了。这是一个流行用法,所以你可以在很多Dockerfile中看到"docker-entrypoint.sh"
- 基础镜像继承相关:
- 环境变量会继承下来,如果你知道基础镜像的环境变量的话,可以直接复用。
- 基础镜像的CMD会继承下来。
- 容器卷会继承下来。
- 官网Dockerfile文档,只能说好过没有
尝试使用Dockerfile创建镜像
先来解释一下docker build
的用法:
- 语法格式:
docker build Dockerfile文件路径
,不要漏了文件路径,这个路径同时也是Dockerfile的上下文-f Dockerfile文件名
,默认情况下,文件名默认是Dockerfile,如果你使用的不是默认的,需要使用-f指定-t 镜像名:tag
,如果你不指定镜像名的话,那你就要留意后面构建完成的时候返回给你的镜像ID。比如Successfully built 0a9ee2fae0d0
- 示例:
docker build -t mytomcat6:1.0 -f TomcatDockerfile .
最后的.代表从当前目录查找TomcatDockerfile;docker build -t mytomcat5:1.0 war
会从当前目录下的war目录下查找Dockerfile - docker build还有很多配置参数,比如cpu,内存什么的,自查了解吧。
1.我们根据之前的“修改已有镜像”的例子来写一个Dockerfile:
# 继承基础镜像
FROM tomcat:8.0
# 把war包拷贝进去
COPY struts2-blank.war /
# 把容器内的war包复制到容器的tomcat中
WORKDIR $CATALINA_HOME
RUN cp /struts2-blank.war webapps/
# 指定端口,端口也会继承下来,由于我们也使用一样的端口,其实可以省略EXPOSE
# EXPOSE 8080
# 启动tomcat,基础镜像的CMD会继承下来 所以其实下面的CMD可以省略,除非你要执行另外的命令
# CMD ["catalina.sh", "run"]
2.执行docker build 创建一个镜像:
docker build -t mytomcat3:1.0 .
3.通过这个镜像创建一个容器:
docker run --name mytomcat3container -d -p 8888:8080 mytomcat3:1.0
4.测试访问:
http://主机地址:8888/struts2-blank
5.结果是可以访问的,说明我们使用Dockerfile实现了之前基于容器修改生成镜像的效果。
阿里云推送镜像和拉取镜像
下面的基本来自阿里云的官网教程,只不过加了配图。
1. 登录阿里云Docker Registry
语法:
$ sudo docker login --username=用户名 仓库注册服务器
示例:
$ sudo docker login --username=10086 registry.cn-shenzhen.aliyuncs.com
- 用于登录的用户名为阿里云账号全名,
- 密码为开通服务时设置的密码,不是你阿里云的密码。
- 您可以在访问凭证页面修改凭证密码。
2. 将镜像推送到Registry
语法:
1.登录
$ sudo docker login --username=用户名 仓库注册服务器
2.创建tag:
$ sudo docker tag 镜像ID 仓库注册服务器/用户名/镜像名:[镜像版本号]
3.推送镜像:
$ sudo docker push 仓库注册服务器/用户名/镜像名:[镜像版本号]
docker tag 用于重命名镜像,下面的就把本地的一个镜像重命名了。
然后你就可以在仓库管理页看到这个镜像了。
3. 从Registry中拉取镜像
语法:
$ sudo docker pull 镜像地址
示例:
$ sudo docker pull registry.cn-shenzhen.aliyuncs.com/用户名/镜像名:[镜像版本号]
这一篇文章,其实讲的东西还是很浅的。还有很多东西都没用讲。
但对于基本使用场景应该还是可以应付的。(当然,😄容器管理、swarm、k8s并没有讲)
还有很多技术没讲,后面有空再写吧。。。咕咕咕。
- .dockerignore
- 命令的深层次应用啊
- 镜像的实质:联合文件系统啊、容器的环境隔离技术啊
- 。。。。。
有兴趣的可以自行去找书籍来了解更多底层的东西。