Docker 基础
什么是Docker
👇网址能告诉你关于Docker的一切
基本概念
Docker 镜像
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变, 因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
Docker 容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
注意,是容器可以被创建,启动,停止,删除,暂停
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
⚠️ 容器删除或者退出,存储层也会丢失
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器可以随意删除、重新 run,数据却不会丢失。
Docker 仓库
镜像构建完成后,可以很容易的在当前宿主上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务, Docker 仓库就是这个用途, 就像Git仓库
一个 Docker 仓库 中可以包含多个小仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。就像gitlab是一个大仓库, 下面又有好多项目Project, 每个Project又有好多branch(分支)
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。标签>仓库名>
加速器
镜像仓库
Docker 安装
这里提供一个PDF, 里面安装写的很全
这里提供文档的git地址,里面会更新
Docker 基本使用
docker run -d -p 80:80 --name webserver nginx
本地没有下载镜像,为什么这个命令能跑的起来呢? 因为 docker在本地找不到,就回去仓库中下载
Docker 获取镜像
docker pull [选项] [Docker Registry地址]<仓库名>:<标签>
- Docker Registry地址:
<域名/IP>[:端口号]
- 仓库名:
<用户名>/<软件名>
如果省略用户名,则默认library, 官方镜像 - 标签: 多为版本号
docker pull --help
Usage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]
Pull an image or a repository from a registry
Options:
-a, --all-tags Download all tagged images in the repository
--disable-content-trust Skip image verification (default true)
--help Print usage
Docker 列出镜像
docker images
列出仓库名、标签、镜像 ID、创建时间以及所占用的空间
- 列出的体积会比仓库中的体积大, 因为仓库中是供下载用的,压缩过
- 列出的体积总和比实际占用磁盘空间大,Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层, 相同的层只需要保存一份即可
这个镜像既没有仓库名,也没有标签,均为
这个镜像原本是有镜像名和标签的,原来为 mongo:3.2,随着官方镜像维护,发布了新版本后,重新 docker pull mongo:3.2 时,mongo:3.2 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了
docker build 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为
docker images -f dangling=true
显示虚悬镜像
docker images -a
列出中间层镜像, 默认显示顶级镜像
docker images name[:tag]
根据name[:tag] 筛选镜像
docker images --help
Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]
List images
Options:
-a, --all Show all images (default hides intermediate images)
--digests Show digests
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print images using a Go template
--help Print usage
--no-trunc Don't truncate output
-q, --quiet Only show numeric IDs
Docker 定制镜像
镜像是容器的基础,每次执行 docker run 的时候都会指定哪个镜像作为容器运行的基础。
docker diff
显示某个容器做了什么变动
Usage: docker diff CONTAINER
Inspect changes to files or directories on a container's filesystem
Options:
--help Print usage
docker commit
提交对某个容器的修改, 打包成镜像
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Create a new image from a container's changes
Options:
-a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
-c, --change list Apply Dockerfile instruction to the created image
--help Print usage
-m, --message string Commit message
-p, --pause Pause container during commit (default true)
docker history
具体查看镜像内的历史记录
Usage: docker history [OPTIONS] IMAGE
Show the history of an image
Options:
--format string Pretty-print images using a Go template
--help Print usage
-H, --human Print sizes and dates in human readable format (default true)
--no-trunc Don't truncate output
-q, --quiet Only show numeric IDs
使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。因为他会提交很多我们没有做的操作但是已经改变的文件,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。而且除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。
定制行为应该使用 Dockerfile 来完成
Dockerfile
我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,里面一行指令会构建一层Docker镜像
FROM 命令
FROM <镜像名:镜像版本>
指定基础镜像, 即后面所有的操作都是在基础镜像上进行的。在一个Dockerfile中有且只能有一个基础镜像,并且必须是第一条指令。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch
。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像, 如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写 的指令将作为镜像第一层开始存在。
RUN 命令
RUN <命令>
RUN ["可执行文件", "参数1", "参数2"]
RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制 镜像时是最常用的指令之一。
但要注意的是, Dockerfile 中每一个指令都会建立一层, RUN 也不例外。每一个RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像。 如果每执行一个命令都要构建一层, 那么将会非常臃肿。
错误的Demo
COPY 命令
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
ADD 命令
ADD命令与COPY命令格式和性质基本一致
ADD 指令将会自动解压缩gzip, bzip2 以及 xz后缀的压缩文件到 <目标路径>
去。
因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。
CMD 命令
- CMD <命令>命令>
- CMD ["可执行文件", "参数1", "参数2"…]
- 参数列表格式:CMD ["参数1", "参数2"…]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
CMD 指令就是用于指定默认的容器主进程的启动命令的, 比如,ubuntu 镜像默认的 CMD 是 /bin/bash, docker run -it ubuntu
== docker run -it ubuntu /bin/bash
, 即可以用 docker run -it ubuntu cat /etc/os-release
一般推荐使用 exec 格式, 如果使用 shell, 实际的命令会被包装为 sh -c 的参数的形式进行执行, 比如: CMD echo $HOME
, 在实际执行中,会将其变更为:CMD [ "sh", "-c", "echo $HOME" ]
⚠️docker运行容器中的应用,都应该以前台的形式执行,容器中的程序对docker来说没有后台的概念,也不能后台执行,而容器的运行可以相对操作者进行前后台执行,如-d使容器后台运行
ENTRYPOINT 命令
ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。
当指定了 ENTRYPOINT 后,CMD 的内容作为参数传给 ENTRYPOINT 指令: <ENTRYPOINT> "<CMD>"
例子:
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
docker run myip -i
ENV 命令
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
设置操作系统的环境变量
ARG 命令
ARG <参数名>[=<默认值>
ARG和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
EXPOSE 命令
EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明
一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
WORKDIR 命令
WORKDIR <工作目录路径>
WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为/目录),该指令在Dockerfile文件中可以出现多次,如果使用相对路径则为相对于WORKDIR上一次的值,例如WORKDIR /a,WORKDIR b,RUN pwd最终输出的当前目录是/a/b。(RUN cd /a/b,RUN pwd是得不到/a/b的)