赞
踩
Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。
获取镜像的命令是 docker pull。其命令格式为:
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
当我们下载镜像时,会发现会有多个文件被下载,如
- 18.04: Pulling from library/ubuntu
- 92dc2a97ff99: Pull complete
- be13a9d27eb8: Pull complete
- c8299583700a: Pull complete
这是因为我们之前说过的镜像的分层储存,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。
下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。
如果从 Docker Hub 下载镜像非常缓慢,可以添加镜像加速。
在/etc/docker目录中创建daemon.json文件,输入
- {
- "registry-mirrors": [
- "https://ung2thfc.mirror.aliyuncs.com",
- "https://registry.docker-cn.com",
- "http://hub-mirror.c.163.com",
- "https://docker.mirrors.ustc.edu.cn"
- ]
- }
然后重启docker:
- systemctl daemon-reload
- systemctl restart docker
可使用docker image ls命令查看已拉取的镜像。
docker image ls
默认的 docker image ls 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 -a 参数。(当然可能压根就没有中间层所以根本看不到)
docker image ls -a
可使用docker system df 命令来查看镜像、容器、数据卷所占用的空间。
docker system df
删除本地的镜像,可以使用 docker image rm 命令:
docker image rm [选项] <镜像1> [<镜像2> ...]
<镜像>可以是镜像短 ID、镜像长 ID、镜像名(:)或者镜像摘要(digest)。
由于一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已;当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。
镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变得非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。
(建议:请先完成容器方面的学习后再尝试制作镜像。)
注意:docker commit是一种生成镜像的方式,但是这种方式不被推荐。(在容器中进行文件操作时,可能会有大量的无关内容被添加进来,将会导致镜像极为臃肿;另外容器内进行的操作是不会被记录的,生成的镜像被称为黑箱镜像——没人知道执行过什么命令、怎么生成的镜像,进而难以维护。推荐的构建方式是后面要将的dockerfile。)
我将以定制数据库的方式来进行举例。
这个过程,从抽象理解,就是在原先的镜像层上,新建了一个容器存储层,接下来的所有文件修改都会在这个容器存储层中进行。比如我们在数据库中添加了一些数据,这些数据就会被添加到容器存储层中。
如果我们接下来直接关闭容器并删除,这些数据就会消失,因为整个容器存储层都会被删除;但是如果我们使用了commit命令,这个容器存储层就会成为以原先镜像层为基础的另一个镜像层,并且持久化保存下来。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像;以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
sudo docker commit ...... mysql:myMysql
先停止删除原来的容器,然后给新创建的镜像创建个容器。
(运行了一下发现没有,原因是mysql的Dockerfile中有这样一行——VOLUME /var/lib/mysql,导致在容器中的目录/var/lib/mysql的所有修改会对应到宿主机的某个位置...docker commit不会将容器中的/var/lib/mysql目录的更改提交到镜像中;当重新启动commit后的镜像时,container会重新在宿主机中创建一个目录来保存其数据更新,因此并不是原先的宿主机目录,所以新开启的容器看不到之前的数据更改,需要将宿主机对应的目录挂载到新容器的/var/lib/mysql目录下并开启读写权限,然后就可以看到原来的数据了......例子不是很恰当,可以看利用 commit 理解镜像构成 | Docker 从入门到实践 (docker-practice.com),其中举例了一个对nginx文件更改的案例)
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层。
(效果为访问80端口时显示Hello, Docker!)
- FROM nginx
- RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
先创建该文件:(输入上述文本)
然后试着启动一下nginx:sudo docker run --name webserver -d -p 80:80 nginx
访问地址后:
然后使用dockerfile脚本构建一层镜像:
docker build -t nginx:myNginx1 .
构建完成,启动容器看看访问后结果:
dockerfile讲解
构建镜像的格式为
docker build [选项] <上下文路径/URL/->
- RUN set -x; buildDeps='gcc libc6-dev make wget' \
- && apt-get update \
- && apt-get install -y $buildDeps \
- && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
- && mkdir -p /usr/src/redis \
- && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
- && make -C /usr/src/redis \
- && make -C /usr/src/redis install \
- && rm -rf /var/lib/apt/lists/* \
- && rm redis.tar.gz \
- && rm -r /usr/src/redis \
- && apt-get purge -y --auto-remove $buildDeps
这两个命令本质是用来保存镜像的,docker save用于将镜像保存为文件;docker load用于将文件展开为镜像。
sudo docker save 447125f01362 -o myNginx1_File
-o指定输出文件的文件名。
sudo docker load -i myNginx1_File
-i指定文件来源。
Dockerfile 功能很强大,它提供了十多个指令。FROM,RUN已经在前文讲解了,这里就不做赘述。
- FROM nginx
- RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
- HEALTHCHECK --interval=5s --timeout=3s \
- CMD curl -fs http://localhost/ || exit 1
Docker v17.05 开始支持多阶段构建,我们只需要编写一个 Dockerfile即可分离构建阶段和运行阶段。无论构建过程涵盖多少个阶段,多阶段构建最终只会生成一个Docker镜像。这是多阶段构建的主要优点之一,它允许我们在一个Dockerfile中定义多个中间镜像,但最终只保留一个镜像,大大节省了存储空间。
Docker多阶段构建能够有效地减小镜像体积出于以下原因:
分阶段构建:在多阶段构建中,每个阶段可以被看作是一个独立的构建,并且都有自己的基础镜像和中间层。这意味着你可以在第一阶段使用包含大量工具和库的重量级镜像,然后在第二阶段使用较轻量的镜像,且只拷贝第一阶段构建的产物。(理解:不用打包编译的工具,只需要编译的产物)
减少了层的数量:每个RUN, COPY, ADD命令都会创建一个新的层。多阶段构建可以通过减少这样的命令从而减少总的层数,再加上一些去除不需要的文件,可以使得最终的镜像体积大大减少。
格式举例:
- FROM golang:alpine as builder
-
- RUN apk --no-cache add git
-
- WORKDIR /go/src/github.com/go/helloworld/
-
- RUN go get -d -v github.com/go-sql-driver/mysql
-
- COPY app.go .
-
- RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
-
- FROM alpine:latest as prod
-
- RUN apk --no-cache add ca-certificates
-
- WORKDIR /root/
-
- COPY --from=0 /go/src/github.com/go/helloworld/app .
-
- CMD ["./app"]
我们可以使用 as 来为某一阶段命名,例如当我们只想构建 builder 阶段的镜像时,增加 --target=builder 参数即可。
docker build --target builder -t username/imagename:tag .
我们可以从任何镜像中复制文件,如:COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。