一、Docker简介
Docker是一个操作系统级的虚拟化技术,是基于LXC技术构建的轻量级容器引擎。
LXC技术:LXC是Linux Container的缩写,Linux Container容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。
二、Docker入门
1.在Linux中下载并安装Docker
1.1.前提条件
Docker要求CentOS系统内核高于3.10
uname -r 查看当前系统内核版本
[root@VM-8-9-centos ~]# uname -r
3.10.0-1127.19.1.el7.x86_64
如果版本达不到要求需要更新
yum -y update:升级所有包同时也升级软件和系统内核
yum -y upgrade:只升级所有包,不升级软件和系统内核。
从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE。
Docker CE 即社区免费版,Docker EE 即企业版,强调安全,但需付费使用。
1.2.卸载旧版本的Docker
yum -y remove docker docker-engine docker.io containerd runc
1.3.通过yum安装 Docker
安装一些必要的系统工具
yum install -y yum-utils device-mapper-persistent-data lvm2
添加软件源信息
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
更新yum缓存
yum makecache fast
安装Docker-ce
yum -y install docker-ce
启动Docker
systemctl start docker
1.4.使用脚本安装Docker
curl -fsSL https://get.docker.com -o get-docker.sh
bash get-docker.sh
安装Docker并将镜像源修改成阿里的镜像源
curl -fsSL https://get.docker.com -o get-docker.sh
bash get-docker.sh --mirror Aliyun
1.5.启动Docker进程
systemctl start docker
1.6.验证docker是否成功安装
docker ps
1.7.镜像加速
docker在海外,后续拉取镜像十分缓慢,可以把镜像源改成国内的
在 /etc/docker/daemon.json文件里添加配置,没有就创建一个
{
"registry-mirrors": ["http://hub-mirror.c.163.com"] }
{
1.8.镜像源
Docker官方中国区
https://registry.docker-cn.com
网易
http://hub-mirror.c.163.com
阿里云
https://pee6w651.mirror.aliyuncs.com
中国科学技术大学
https://docker.mirrors.ustc.edu.cn
删除Docker
yum remove docker-ce
rm -rf /var/lib/docker
2.Hello,Docker!
2.1.镜像和容器的概念
镜像
镜像可以理解为一个打包了运行环境的特殊文件系统,它包含了容器启动运行所需的所有信息,包括运行程序和配置数据等。镜像不包含任何动态数据,其内容在构建之后也不会改变。
例如,一个官方的Ubuntu14.04镜像,就包含了一套完整的Ubuntu14.04最小系统的root文件系统。
容器
镜像和容器的关系,类似于面向对象程序设计中的类和实例一样,镜像是静态的定义,而容器是镜像运行时的实体,可以看成是一个具备某个运行环境的非常轻量的虚拟机。容器可以被创建、启动、停止和删除等。在创建容器时,需要显示地为容器指定镜像。指定镜像之后,容器就具备了镜像中保存的运行环境了。
例如,可以为容器指定Ubuntu14.04的镜像,然后该容器就具备Ubuntu14.04的运行环境了。
2.2.Docker使用基本过程
1.获取镜像
在安装完Docker服务之后,本地是没有任何镜像的,所以首先需要获取所需要的镜像。
2.基于该镜像创建并启动一个容器
在获取到所需的镜像之后,就可以基于该镜像创建并启动一个容器,该容器就具备了镜像包含的运行环境了。同时,在创建容器时也可以设置容器的启动命令,该命令会在容器启动时执行。
3.进入该容器,执行“程序”
在容器成功创建并启动之后,该容器就具备了ubuntu的运行环境。我们可以进入该容器内部,并在其内部执行任何在ubuntu系统上的程序了。这里的“程序”可以是“Linux命令”、“shell脚本”、“C++程序”等。
2.3.启动一个容器并输出Hello Docker
docker pull busybox:latest
docker run --name first_docker_container busybox:latest echo "Hello Docker"
- 第一条命令:获取一个名为busybox:latest的镜像。这条命令会从Docker Hub官方镜像仓库获取一个名为busybox:latest的镜像(busybox的最新版),并把它下载到宿主机。其中busybox是最小的Linux系统。
- 第二条命令: 创建并启动一个容器,并执行相应命令。首先,--name设置容器的名字为first_docker_container,然后为容器指定了busybox:latest作为启动镜像,最后设置了该容器的启动命令为echo "Hello Docker"。容器启动并输出 “Hello Docker”后,将其停止。
3.拉取镜像
3.1.获取镜像
docker pull [选项] <仓库名> <标签>
- docker pull:Docker拉取镜像的命令关键词;
- [选项]:命令选项;
- 仓库名:仓库名的格式一般为<用户名>/<软件名>。对于Docker Hub,如果不指定用户名,则默认为library,即官方镜像;
- 标签:标签是区分镜像不同版本的一个重要参数,<仓库名>:<标签>会唯一确定一个镜像。默认为latest。
例如拉取一个Ubuntu 14.04的官方镜像
docker pull ubuntu:14.04
如果标签为空的话就会使用默认的标签,也就是latest,执行上面的命令后默认会去Docker Hub中寻找名为repoName的仓库,不存在则返回错误信息,如果存在,就从仓库拉取对应的标签的镜像。
不加标签默认拉取最新的版本
docker pull ubuntu latest
拉取一个不存在的镜像会报错
[root@localhost Desktop]# docker pull aaa
Using default tag: latest
Error response from daemon: repository aaa not found: does not exist or no pull access
4.启动一个容器
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态的容器重新启动。
4.1.第一种方式:新建并启动
docker run命令会基于指定的镜像创建一个容器并且启动它。docker run的基本语法如下:
docker run [选项] 镜像名 [命令] [参数]
docker run: Docker创建并启动容器的命令关键词;
- 选项: 命令选项,最常用的包括
- -d后台运行容器并返回容器ID,
- -i以交互模式运行容器,
- -t为容器分配一个伪输入终端,
- --name 指定启动容器的名称。
- 镜像名: 以<仓库名>:<标签>的方式来指定;
- 命令: 设置启动命令,该命令在容器启动后执行;
- 参数: 其他一些参数。
docker run背后的工作
Docker在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载启动;
- 利用镜像创建并启动一个容器;
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层;
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去;
- 从地址池配置一个
ip
地址给容器; - 执行用户指定的启动命令;
- 执行完毕后容器被终止。
实例一
创建并启动一个容器,容器中具有ubunt的运行环境,输出hello docker
docker run ubunt:14.04 echo 'hello docker'
实例二
创建并启动一个容器,容器中具有Ubuntu的运行环境,容器名为firstContainer,为容器分配一个终端,与用户进行交互
docker run -it --name firstContainer ubunt /bin/bash
- -i选项告诉Docker保持标准输入输出流对容器开放,-t选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上;--name为容器设置容器名。
- docker run是创建一个新容器并启动,所以这条命令创建的容器与上个实例的创建的容器不是同一个容器。而且由于本地已经存在ubuntu:latest镜像了,所以并不需要再次从Docker Hub中下载,而是直接使用本地的ubuntu:latest镜像构建容器。
- 启动容器之后,我们进入容器内部并在终端进行与容器交互。我们可以根据左侧的命令提示符判断自己是否在容器内部。例如上面的例子,当左侧的命令提示符为root@localhost时,表示我们在容器外部,而命令提示符为:root@fe263c9359dd/时,表示我们在容器内部,且容器的ID是fe263c9359dd。我们可以通过exit退出当前的容器。
4.2.第二种方式:启动一个已经终止的容器
docker run 每次都会创建一个新的容器,可以通过docker start命令,使用容器名或容器id启动一个已经终止的容器
docker start [选项] 容器 [容器2...]
- docker start: Docker启动容器的命令关键词;
- 选项: 命令选项;
- 容器: 需要启动的容器,该容器用“容器ID”或“容器名”表示,如果指定了多个容器,那么就将这些容器都启动。
现有名为firstContainer的容器处于终止状态,可以通过docker start firstContainer启动它。
4.3.查看容器信息
如果既不知道容器名,又不知道容器id,可以使用docker ps 来查看容器的信息。
docker ps -a 可以查看Docker内的所有容器,
docker ps -a命令执行结果
[root@VM-8-9-centos ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
faeacc3b5aa7 busybox:latest "echo 'Hello Docker'" 2 minutes ago Exited (0) 2 minutes ago first
5799b6f4eeee busybox:latest "echo 'Hello Docker'" 8 minutes ago Exited (0) 8 minutes ago first_docker_container
实例
创建并启动一个容器,容器名为firstContainer,具备busybox的运行环境。并输出hello world
docker busybox
docker run --name firstContainer busybox:latest echo "hello worldr"
5.停止一个容器
5.1.使用docker stop停止一个容器
docker stop可以用来终止一个正在运行的容器。它的命令格式如下:
docker stop [OPTIONS] Container [Container ...]
其中:
docker stop: Docker停止容器的命令关键词;
OPTIONS:命令选项,其中-t指定等待多少秒后如果容器还没终止,就强行停止,默认等待10秒;
Container:需要启动的容器,该容器用“容器ID”或“容器名”表示,如果指定了多个容器,那么就将这些容器都启动。
停止一个名为firstContainer的容器,可以执行
docker stop firstContainer
执行完成后该容器会处于终止状态,可以通过docker ps -a查看。
5.2.什么情况下容器启动后会立即终止?
除了使用docker stop命令来强制地终止一个容器以外,当容器的启动命令终结时,容器也自动会终止。以
docker run --name testcontainer ubuntu echo 'hello docker
为例,echo 'hello docker'就是该容器的启动命令。实际上执行完这条命令后,执行docker ps -a,可以发现testcontainer容器是处于终止状态的。
5.3.如何才能使容器启动后不立即终止
如果容器的主进程不停止,容器就不会停止。
5.3.1.将启动命令设置为死循环
docker run ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
这条命令在创建并启动容器之后,会执行/bin/sh -c "while true; do echo hello world; sleep 1; done",由于该命令永远都不会执行完毕,除非强行终止,所以容器的主进程sh不会停止,因此容器也将不会停止。但是这样的做的话,无法正常的操作容器,而且它会占据资源,所以这种做法在实际的工作中意义并不大,/bin/sh -c 将字符串当命令来执行。
5.3.2.将命令设置为“启动一直运行的子进程”
docker run --name first_container -it ubuntu /bin/bash
执行完这条命令后,创建并启动容器之后,执行/bin/bash,会启动一个子进程,此时父进程(也就是容器的主进程sh)会进入sleep状态,由于sleep状态不是终止状态,所以容器会继续运行。
为什么在容器中输入exit或者执行ctrl D后,容器将会终止呢,这是因为exit会退出(结束)当前进程,也就是/bin/bash,由于子进程结束,sh主进程恢复到运行态,然而由于没有命令需要继续执行,所以sh主进程结,因此容器终止。
6.进入一个容器
6.1.进入一个docker容器的几种方法
1.使用ssh登录容器;
2.使用nsenter、nsinit等等第三方工具;
3.使用docker本身提供的工具
6.2.使用docker attach进入一个容器内部
命令
docker attach 容器ID或容器名字
首先使用docker run创建了一个容器,为其分配了伪终端,打开了它的标准输入流,并且让它在后台执行。然后使用docker attach进入了该容器内部,实际上就是进入容器“启动命令”的终端。
容器ID可以不用输全,只要能代表容器即可。例如下面的0539就是代表容器ID以0539开头的容器,一般情况下,前4位就能唯一标识一个容器了。
[root@localhost]# docker run -itd ubuntu /bin/bash
0539852938cdb9538f67750d07ed8c7fa072de742d5c0c02128576f2d227ec46
[root@localhost]# docker attach 0539
root@0539852938cd:/#
root@0539852938cd:/# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
root@0539852938cd:/# exit
exit
[root@localhost]#
6.3.使用docker exec进入一个容器内部
docker exec [选项] 容器名|容器ID 命令参数
实例:进入test2容器,创建一个名为HelloWorld的文件夹
docker pull ubuntu
docker run -itd --name test2 ubuntu /bin/bash
docker exec test2 mkdir HelloWorld
6.4.attach与exec的主要区别
- attach直接进入容器“启动命令”的终端,不会启动新的进程;
- exec则是在容器中打开新的终端,并且可以启动新的进程;
- 如果想直接在终端中查看容器“启动命令”的输出,用attach;其他情况使用exec。
7.删除容器
7.1.删除一个终止状态的容器
使用 docker rm 容器名|容器ID 来删除一个处于终止状态的容器,不加参数的情况下只能删除处于终止状态的容器。
步骤
1.docker ps -a查看所有的容器
2.查看容器的ID
3.执行docker rm 容器名或容器ID 命令
7.2.删除一个正在运行的容器
删除一个处于运行状态的容器有两种方式
1.执行docker stop停止该容器,然后使用docker rm删除
2.执行docker rm -f 强制删除
实例:
1.删除所有处于终止状态的容器
docker rm $(docker ps -aq)
2.删除所有容器
docker rm -f $(docker ps -aq)
三、镜像管理
1.基于Commit定制镜像
之前一直使用的镜像都是来自于官方Docker Hub中的镜像,有时候需要在这些镜像的基础上进行修改,来定制符合需求的镜像。
将“对容器的修改”保存为镜像
在Docker中提供了一个命令docker commit,该命令会把对容器的修改提交成一个镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层(该存储层仅仅保存了容器所做的修改),将这些内容构成一个新的镜像。docker commit的基本语法如下:
docker commit [选项] 容器名 [镜像名]
其中:
docker commit:Docker拉取镜像的命令关键词;
[选项]:命令选项,其中--author指定作者,--message制定commit的信息;
容名:容器的名字;
镜像名:新镜像的名字,以<仓库名>:<标签>的方式来指定。如果不显示设置,将默认为None:None。(这个代表没有指定镜像名)
实例
定制一个busybox:v1镜像,该镜像在busybox:latest的基础上,新增了一个hello.txt文件。
docker pull busybox
docker run --name container1 busybox touch hello.txt
docker commit container1 busybox:v1
使用commit定制镜像的缺陷,实际工作中很少用commit制作镜像
1.会保存很多不必要的文件
2.commit定制的镜像是黑箱镜像,除了制作人谁也不知道制作出来的,过一段时间可能作者都不知道怎么制作出来的,因此维护非常困难
2.基于save保存镜像与基于load加载镜像
为了防止镜像丢失或备份镜像;并且在需要的时候恢复镜像,就需要保存和加载镜像
2.1.将镜像保存到tar包
docker save [选项] 镜像名.tar [镜像名.tar]
选项:
-o:指定写到一个文件中,而不是标准输出流中;
例如,将alpine:latest镜像保存到tar包,对应的语句如下:
docker save alpine:latest > alpine.tar
或者
docker save -o alpine:lateste alpine.tar
保存多个镜像
docker save alpine:latest ubuntu:latest > image.tar
2.2.从tar包加载镜像
docker load使用docker save保存的tar文件加载镜像,它的具体语法如下:
docker load [选项]
选项,
-i:指定从一个tar文件中读取,而不是标准输入流中。
例如,从alpine.tar中加载镜像,对应的语句如下:
docker load < alpine.tar
或者
docker load -i alpine.tar
如果一个tar包中包含多个镜像,那么会将这些镜像全部到加载进来。
结合这两个命令以及ssh甚至pv的话,利用 Linux强大的管道,我们可以写一个命令完成从一个机器将镜像迁移到另一个机器,并且带进度条的功能:
docker save <镜像名> | bzip2 | pv | ssh <用户名>@<主机名> 'cat | docker load'
实例:
1.busybox:latest镜像保存为一个tar包;
2.删除busybox:latest镜像后,从tar包加载busybox:latest镜像。
拉取镜像
docker pull busybox:latest
保存到tar包
docker save busybox:latest > busybox:latest.tar
删除镜像
docker rmi busybox:latest
从tar包加载镜像
docker load < busybox:latest.tar
3.导入导出容器
3.1.导出
docker export [选项] 容器名
选项:
-o指定写到一个文件中,而不是标准输出流中。
例如将容器Container1保存到tar包
docker export container1 > container1.tar
或者
docker export container1 -o container.tar
3.2.导入
docker import 文件或URL| - [镜像名]
文件|URL|: 指定docker import的对象,可以是文件或者某个URL;
[镜像名]: 以<仓库名>:<标签>的方式来指定。
例如从container1.tar中加载镜像,镜像名为test:v1.0
cat container1.tar | docker import - test:v1.0
docker export和docker save的区别
首先,两者的操作对象不同。docker save是将一个镜像保存为一个tar包,而docker export是将一个容器快照保存为一个tar包。
然后,docker export导出的容器快照文件将丢弃所有的历史记录和元数据信息,即仅保存容器当时的快照状态;而docker save保存的镜像存储文件将保存完整记录,体积也要大。下图就能够很好的说明,ubuntu:test仅仅占97.8MB而ubuntu:latest却占了120MB。
实例
将busyboxContainer容器的文件系统保存为一个tar包;
通过该tar包导入一个busybox:v1.0镜像。
docker pull busybox
docker run --name busyboxContainer busybox echo "Hello,World"
docker export busyboxContainer > busybox.tar
cat busybox.tar| docker import - busybox:v1.0
4.删除镜像
删除本地镜像可以使用docker rmi;rm是删除,i是image,镜像的意思。
删除镜像前需要删除容器,也可以使用 docker rmi -f强制删除镜像。
docker rmi [选项] image [image...]
选项:
-f:强制删除
image:需要删除的镜像。这里的镜像可以用“镜像短ID”、“镜像长ID”、“镜像名”、“镜像的digest”来标识。
使用下面的命令可以查看镜像的具体信息包括digest
docker images -- digest
删除ubuntu:latest镜像,有以下几种方法:
1.镜像短ID:docker rmi 14f6;(这个代表镜像id以14f6开头的镜像,一般而言,前四位可以唯一标志,如果不可以,docker会提示的)
2.镜像长ID:docker rmi 14f60031763d;
3.镜像名: docker rmi ubuntu:latest;
4.镜像的digest:docker rmi digest值
以上的方法都能删除掉ubuntu:v1镜像。但日常生活中,我们比较常用的是短ID以及镜像名,因为用起来最方便。
删除多个镜像
删除所有仓库名为redis的镜像,可以这么写:
docker rmi $(docker images -q redis)
删除所有镜像
docker rmi $(docker images -qa)
删除镜像前需要删除容器,也可以使用 docker rmi -f强制删除镜像。
5.构建私有Registry
5.1创建一个私人仓库
在Docker Hub中提供了创建私人仓库的镜像Resposity(镜像仓库):Registry
docker run -d -p 5000:5000 --restart=always --name registry registry:2
从这条命令可以看出,这个私人仓库以容器的形式运行着。其中--restart=always是指在Docker服务重启或者registry容器退出时会重新启动.
而-p是指将宿主机的5000端口映射到容器的5000端口,这样就可以通过宿主机ip:5000访问到容器的5000端口了。(registry容器默认会监听5000端口);-d参数是指在后台运行。
-v指定私人仓库的存储位置,添加-v /mnt/registry:/var/lib/registry可以将私人仓库的存储位置设置为宿主机的/mnt/registry。
5.2.将镜像推送到私人仓库
1.使用docker tag给镜像添加一个标签
如果想要将镜像推送到私人仓库而不是Docker Hub,首先必须使用docker tag命令,使用主机名和端口来标记一个镜像,如下所示,为ubuntu:latest镜像加上一个localhost:5000/my-ubuntu:latest的标签。
docker tag ubuntu:latest localhost:5000/my-ubuntu
使用docker push将镜像推送到私人仓库
使用docker push命令可以将镜像推送到仓库,默认情况下会将镜像推送到官方仓库Docker Hub中去,但是如果推送一个“用主机名和端口来标记”的镜像,那么就会推送到私人仓库。
docker push localhost:5000/my-ubuntu
5.3.从私人仓库拉取镜像
docker pull可以从仓库拉取某个镜像,默认情况下,也是从官方仓库拉取。当我想从私人仓库拉取my-ubuntu:latest镜像。执行以下命令就行了。
docker pull localhost:5000/my-ubuntu
5.4.查看或者删除私人仓库中的镜像
Docker提供的Registry镜像没有提供查看镜像和删除镜像的指令,但是有第三方的软件可以提供这些功能,例如:harbor。
5.5.删除私人仓库
私人仓库实质上就是一个容器,所以删除私人仓库就是删除私人仓库对应的容器。我们可以使用docker rm -f 强制删除删除它,但是这样删除之后,私人仓库中存储的镜像并不会被删除掉。如果你想在删除私人仓库的同时,也将镜像删除,需要添加-v参数,也就是docker rm -f -v。例如删除本地的私人仓库,可以执行以下语句:
docker rm -vf myregistry
实例
1.构建一个私人仓库
docker pull registry:2
docker run -d -p 5000:5000 --restart=always --name myregistry registry:2
2.拉取ubuntu镜像
docker pull ubuntu
3.使用docker tag给ubuntu加上一个标签localhost:5000/my-ubuntu:latest
docker tag ubuntu:latest localhost:5000/my-ubuntu:latest
4.将localhost:5000/my-ubuntu:latest镜像推送到私人仓库
docker push localhost:5000/my-ubuntu:latest
5.删除本地镜像
docker rmi localhost:5000/my-ubuntu:latest
6.从私人仓库拉取localhost:5000/my-ubuntu:latest镜像
docker pull localhost:5000/my-ubuntu
7.删除私人仓库并将私人仓库中的镜像也删除掉
docker rm -vf myregistry
四、Dockerfile
1.初识Dockerfile
1.1.Dockerfile简介
Dockerfile描述了组装镜像的步骤,其中每一条命令都是单独执行的,除了FROM指令外,其他每一条指令都在上一条指定所生成的镜像基础上执行,执行完会生成一个新的镜像层,新的镜像层覆盖在原来的镜像层之上,从而形成了新的镜像。解决了之前提及的无法重复、镜像构建透明性和体积的问题。
1.2.Dockerfile基本的两条指令:FROM和RUN
FROM指定基础镜像; 格式:FROM<镜像名>或 FROM <镜像名>:<标签>。
FROM指令的功能是为后面的指令提供基础镜像,因此一个有效的Dockerfile必须以FROM指令作为第一条非注解指令。若FROM指令中tag参数为空,则tag默认为latest;若参数image或tag指定镜像不存在,则返回错误。
RUN执行命令; 格式:RUN <命令>
RUN 指令是用来执行命令行命令的。RUN指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令。在命令结束运行后提交新容器为新镜像,新镜像被Dockerfile的下一条指令使用。
1.3.使用Dockerfile构建一个镜像
步骤
1.创建一个空的文件,并进入
2.创建一个名为Dockerfile的文件,并根据实际需要补全内容
3.使用dockerfile构建一个名为testimage的镜像docker build -t testimage .
-t 指定新镜像的镜像名
命令的最后有一个小数点不能丢
实例
使用Dockerfile构建一个名为testimage的镜像,该镜像具有ubuntu:latest的运行环境,并且在根目录下创建了一个Linux的文件夹
mkdir Ubuntuimage
cd Ubuntuimage
touch Dokerfile
vim Dockerfile
#添加下面的内容
FROM ubuntu:latest
RUN mkdir Linux
docker build -t testimage .
在Dockerfile的编写过程中一定要牢记一点:镜像的每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。
2.docker build、COPY和ADD
2.1.docker build命令详解
Dockerfile创建完成后,可以使用docker build命令根据Dockerfile构建一个镜像。
docker build [选项] 上下文路径|URL
选项:
-t:指定镜像名字;
-f:指定Dockerfile,不指定则默认使用当前文件夹下的Dockerfile。
除了从本地构建以外,docker build还支持从URL构建,比如可以直接从Git repo中构建。
2.2.COPY命令和ADD指令
COPY指令语法:
COPY <源路径> <目标路径>
ADD指令语法:
ADD <源路径> <目标路径>
COPY与ADD指令在功能十分相似,但在COPY的路径上添加了一些功能。比如ADD的源文件路径可以是URL,这种情况下Docker会下载URL指向的文件。
当源文件路径是tar压缩文件时,使用COPY会拷贝源文件到指定目录,ADD命令自动解压这个压缩文件到指定目录。
实例
使用Dockerfile构建一个名为busybox:v3的镜像,具体要求如下:
以Ubuntu为基础镜像; 将目录下的dir1.tar“解压提取后”,拷贝到新镜像的/中; 使用docker build基于该Dockerfile构建一个名为Ubuntu:v3的镜像。
mkdir ubuntuImage
cd ubuntuImage
mkdir dir1 && -cvf dir1.tar dir1 && rm -rf dir1
touch Docfile
vim Dockerfile
#添加到Dockerfile文件中的内容
FROM ubuntu:latest
ADD ./dir1.tar /
#退出vim,以该Dockerfile构建一个名为busybox:v3的镜像
docker build -t ubuntu:v3 .
3.CMD指令和ENTRYPOINT指令
CMD和ENTRYPOINT都是为镜像指定容器启动命令的常用Dockerfile指令。
CMD和Entrypoint的区别
共同点:
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD/ENTRYPOINT 命令。如果指定了多条命令,只有最后一条会被执行。 用法都支持exec和shell两种使用方式且用法相近
区别:
CMD: 启动容器时候指定了运行的命令,则会覆盖掉镜像中 CMD 指定的命令
ENTRYPOINT: 启动容器时候指定了运行的命令,并不会覆盖掉镜像中Entrypoint的命令
实例
要求:
1.以busybox:latest为基础镜像; 2.将启动命令设置为df -Th,要求df命令不能被覆盖,但-Th能够被覆盖。 3.使用docker build基于该Dockerfile构建一个名为mydisk:v1的镜像。
mkdir busyboxImage
cd busyboxImage
touch Dockerfile
echo "FROM busybox:latest" > Dockerfile
echo 'ENTRYPOINT ["df"]' >>Dockerfile
echo 'CMD ["-Th"]' >>Dockerfile
docker build -t mydisk:v1 .
4.ENV、EXPOSE、WORKDIR、ARG指令
WORKDIR指令
WORKDIR为其他指令设置工作目录; 格式:WORKDIR <工作目录路径>。
WORKDIR /tmp
WORKDIR指令为Dockerfile中的任何RUN,CMD,ENTRYPOINT,COPY和ADD指令设置工作目录(或称当前目录);如果WORKDIR对应的目录不存在,将会自动被创建。
EXPOSE指令
ENV设置环境变量; 格式:ENV 或ENV =;
这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,还是运行时的应用,都可以直接使用这里定义的环境变量。
ENV str="Hello,World"
ENV APACHE_HOME /var/tmp/apache-tomcat-8.0.45
ARG指令
ARG构建参数; 格式:ARG <参数名>[=<默认值>];
ARG与ENV有些类似,它们都可以被后面的其它指令直接使用,但是它并不是环境变量,这意味着将来容器运行时是不会存在ARG变量的。
什么时候用ARG,什么时候用ENV? 如果想保存为环境变量,就用ENV;如果只想在Dockerfile中临时使用,就用ARG。
EXPOSE指令
EXPOSE暴露端口; 格式:EXPOSE <端口1> [<端口2>...]
EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。如果想要公开容器的端口,必须在docker run是指定-p参数去公开端口或者指定-P参数公开所有被EXPOSE的端口。
在Dockerfile中写入这样的声明有两个好处
1.帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;
2.在运行时使用随机端口映射时,也就是 docker run -P时,会自动随机映射 EXPOSE 的端口。
实例
要求:
以busybox:latest作为基础镜像; 声明暴露3000端口; 将变量var1="test"设置为环境变量; 设置工作目录为/tmp,在工作目录下创建一个1.txt文件; 基于该Dockerfile文件,构建一个名为testimage:v1的镜像。
mkdir busyboxImage
cd busyboxImage
touch Dockerfile
echo "FORM busybox:latest" >Dockerfile
echo "EXPOSE 3000" >> Dockerfile
echo "ENV var1=test" >> Dockerfile
echo "WORKDIR /tmp" >>Dockerfile
echo "RUN touch 1.txt" >> Dockerfile
5.ONBUILD和VOLUME指令
ONBUILD指令
ONBUILD添加一个将来执行的触发器(trigger)
格式: ONBUILD <其它指令>
ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如RUN, COPY等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行,在镜像构建完成后,触发器指令会被清除,不会被后面的镜像继承。
VOLUME指令 VOLUME定义匿名卷; 格式:VOLUME ["<路径1>", "<路径2>"...]或VOLUME <路径>;
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于数据卷(volume)中。
6.镜像构建时的缓存机制
镜像构建时的缓存机制
在构建映像的过程中,Docker将按照指定的顺序逐步执行您的Dockerfile中的指令。随着每条指令的检查,Docker将在其缓存中查找可重用的现有映像,而不是创建一个新的(重复)映像。如果您不想使用缓存,可以在docker build命令中使用--no-cache = true选项。
五、数据卷操作
1.创建一个数据卷
Docker1.9版本以后,Docker提供了一条新的命令:docker volume,用来创建、查看、删除数据卷。而传统的docker run -v创建一个数据卷的方式也仍然保留了下来。
创建数据卷的方式
1.使用docker volume create创建一个数据卷,并指定数据卷的名字。
docker volume create --name vo1
2.创建新容器的时候,通过添加-v
标签创建一个数据卷
docker run -itd -v/data ubuntu /bin/bash
3.在创建新容器的时候,指定数据卷的名字,并挂载到容器目录
docker run -itd -v vo2:/data ubuntu /bin/bash
日常工作一般都用第三种,创建容器指定数据卷的名字,并挂载到指定的目录
实例
创建一个名为vo1的数据卷,并将该数据卷挂载到container1容器的/dir1目录。
docker pull ubuntu
docker run -v vo1:/dir --name container1 ubuntu
2.挂载和共享数据卷
2.1.挂载数据卷
默认情况下,在创建数据卷时,会在宿主机中的/var/lib/docker/volume/下创建一个以“数据卷名”为名的目录,并将数据卷的内容保存在该目录下的/_data目录下;也就是将数据卷的内容保在/var/lib/docker/volume/数据卷名/data中,数据卷的内容会和容器的挂载点始终保持一致。“数据卷名”可以用户指定,如果不指定,就会随机生成一个“数据卷名”。
挂载宿主机目录
将宿主机的/host/dir挂载到了容器的/container/dir目录。
docker run --name vocotainer1 -v /host/dir:/container/dir ubuntu
需要注意的是,宿主机的目录和容器的目录必须使用绝对路径。如果宿主机不存在/host/dir目录,则会创建一个空文件夹。在/host/dir下的所有文件和文件夹都可以在容器中在/container/dir下被访问。如果镜像中本来就存在/container/dir文件夹,那么该文件夹下所有内容都会被删除,保证与宿主机中文件夹一致。
同时创建多个数据卷
在挂载的时候也可以同时创建多个数据卷,例如下面的命令就创建了两个数据卷co3和co4
docker run --name vocotainer2 -v co3:/data -v co4:/dir1 ubuntu
与其他容器共享数据卷
在使用docker run创建并启动一个新容器时,也可以使用--volumes-from标签使容器与已有的容器共享数据卷;下面的命令创建了一个名为vocotainer3的容器,并与vocontainer1共享数据卷。因为vocontainer1的挂载点在/container/dir上,所以如果vocotainer3的挂载点也将会是/container/dir。
docker run --name vocotainer3 --volumes-from vocontainer1 ubuntu
通常如果有一些文件如果需要被多个容器共享,一种常见的做法就是创建一个数据容器(该容器仅仅用来共享数据而不做其他用途),其他容器与之共享数据卷。
实例
要求:
创建一个名为container1的容器,并将本地主机的/dir1目录挂载到容器中的/codir1中; 创建一个名为container2的容器,与container1共享数据卷。
docker pull ubuntu
docker run -v /dir:codir --name container1 ubuntu
docker run --volumes-from container1 --name container2 ubuntu
3.查看数据卷的名字
3.1.查看数据卷的具体信息
查看数据卷先创建数据卷,没有指定数据卷的名字
docker run -v /data --name vocontainer1 ubuntu
查看数据卷
docker inspect --type container vocontainer1
会输出ID,创建时间,名字和其他很多参数。
3.2.仅查看数据卷的名字
通过--format来解析docker inspect
docker inspect --type container --format='{{range .Mounts}}{{.Name}}{{end}}' 容器名|容器ID
查看vocontainer1的数据卷名字
docker inspect --type container --format='{{range .Mounts}}{{.Name}}{{end}}' vocontainer1
4.删除数据卷
第一种方式
如果该数据卷还被其他容器使用就会删除失败,没有被其他容器使用就会删除
docker volume rm 数据卷名
第二种方式
删除容器的同时删除数据卷,分两种情况:
1.在创建数据卷的时候没有指定数据卷的名字
如果该数据卷还被其他容器使用就会删除失败,没有被任何容器使用就会删除
2.在创建数据卷的时候指定了数据卷的名字
删除容器只会解除数据卷与容器之间的联系,要删除数据卷需要使用第一种方式
docker rm -v 容器名或容器ID
第三方式
在创建容器时指定了--rm标签,那么在容器处于“终止状态”时就会删除容器以及尝试删除容器所对应的数据卷。
如果没有指定了数据卷名,那么将删除对应的数据卷。如果指定了数据卷名,也只是解除了数据卷和容器的联系,真正要删除,还是要使用第一种方式。
删除无用的数据卷
如果执行下面这条命令,那么会将所有没有被容器使用的数据卷删除掉。
docker volume prune
实例
docker pull ubuntu
docker run -v vo4:/data --name container1 ubuntu
docker rm -v container1
docker volume rm vo4
5.备份和恢复数据卷
1.备份数据卷
首先创建一个容器vocontainer1,并创建了一个名为db1的数据卷,将数据卷挂在到容器的/dbdate目录。
docker run -v db1:/dbdate --name vocontainer1 ubuntu
首先进入一个空白目录,使用--volumes-from创建一个新容器,这样新容器与container1容器共享dbdata挂载目录,同时把主机上的当前目录挂载到容器的 /backup 目录
docker run --volumes-from container1 -v $(pwd):/backup ubuntu tar -cvf /backup/backup.tar /dbdata
容器启动后,使用了tar 命令来将 dbdata目录压缩,并保存在 /backup/backup.tar文件中,由于主机的当前目录挂载在容器的/backup目录下,而绑定挂载的两个目录的内容完全保持一致,所以相当于将dbcontainer1数据卷的内容压缩后备份到了宿主机的当前目录了。
2.恢复一个数据卷
首先创建一个带有空数据卷的容器container2,挂载目录为/dbdata,数据卷名为db1。
docker run -v db1:/dbdata --name container2 ubuntu /bin/bash
然后进入之前保存backup.tar的宿主机目录,在该目录下执行下面命令,该命令创建一个新容器,新容器与container2容器共享dbdata挂载目录,同时将主机的当前目录挂载的容器的/backup中。
docker run --volumes-from container2 -v $(pwd):/backup busybox tar -xvf /backup/backup.tar -C /dbdata
启动容器时,使用tar命令将数据卷的备份文件backup.tar解压到/dbdata目录,由于该容器与container2容器共享一个数据卷,也就相当于将backup.tar解压到了container2的/dbdata目录。又因为container2将名为db1的数据卷挂载到了/dbdata上,所以实质上就将db1的数据卷内容完全恢复了!
实例:备份和恢复vo1数据卷
创建vo1数据卷并在数据卷中添加1.txt
docker pull ubuntu
docker run --name vocontainer -v vo1:/dir1 ubuntu touch /dir1/1.txt
将vo1数据卷的数据备份到宿主机的/newback中,将容器的/backup路径挂载上去,并将容器内/dir1文件夹打包至/backup/backup.tar
docker run --volumes-from vocontainer1 -v /newback:/backup ubuntu tar -cvf /backup/backup.tar /dir
删除所有的容器以及它使用的数据卷
docker rm -vf $(docker ps -aq)
docker volume rm vo1
在次创建一个vo1的数据卷
docker run -itd --name vocontainer2 -v vo1:/dir1 ubuntu /bin/bash
将保存在宿主机中备份文件的数据恢复到vocontainer2的/中
docker run --volumes-from vocontainer2 -v /newback:/backup ubuntu tar -xvf /backup/backup.tar -C /