如何高效学习Docker镜像管理技巧?

2026-05-23 08:021阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2061个文字,预计阅读时间需要9分钟。

简介+在docker学习笔记(1)- 架构概述+一节中可以看到,镜像是docker三大组件之一,可将其类比为虚拟机的版本。镜像由多层组成,每层叠加后,从外部看就像一个独立的对象。

简介

在docker学习笔记(1)- 架构概述一节中可以看到镜像是docker三大组件之一,可以将Docker镜像类比为虚拟机的模版。

  1. 镜像由多个层组成,每层叠加之后从外部看就像一个独立的对象,镜像的内部包括操作系统、应用程序、应用运行时所必须的依赖包等。
  2. 使用镜像时从仓库中拉取镜像到Docker主机,然后使用该镜像可以启动一个或多个容器,也可以将容器构建为镜像。

具体的概念与实现在后续实现Docker的基础技术中记录,先整理镜像的用法

相关命令 镜像加速

国内访问Docker hub有速度缓慢甚至会无法的情况,换用国内云厂商提供的加速服务,可以添加多个源,在/etc/docker/daemon.json文件中添加如下json内容,没有daemon.json可以自己新建

{ "registry-mirror": [ "hub-mirror.c.163.com/", "reg-mirror.qiniu.com" ] } # 重启docker systemctl daemon-reload systemctl restart docker # 查看是否生效,在使用docker pull时会快很多 docker info > Registry: index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Registry Mirrors: hub-mirror.c.163.com/ reg-mirror.qiniu.com/ 搜索镜像

docker search centos --filter=stars=20 > NAME DESCRIPTION STARS OFFICIAL AUTOMATED centos The official build of CentOS. 7066 [OK] centos/systemd systemd enabled base container. 105 [OK] centos/mysql-57-centos7 MySQL 5.7 SQL database server 92 centos/postgresql-96-centos7 PostgreSQL is an advanced Object-Relational … 45 centos/myip.ipip.net" ]

也可以做启动容器前的准备工作,以下为redis创建用户,然后为ENTRYPOINT指定脚本,该脚本判断CMD参数是否是启动redis-server,如果是使用redis用户启动,如果是其他操作则继续使用root,这样既保证了服务运行的安全性,又不妨碍使用root用户做一些调试和信息获取等操作

FROM alpine:3.4 ... RUN addgroup -S redis && adduser -S -G redis redis ... ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 6379 CMD [ "redis-server" ] # docker-entrypoint.sh #!/bin/sh ... # allow the container to be started with `--user` if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then find . \! -user redis -exec chown redis '{}' + exec gosu redis "$0" "$@" fi exec "$@" ENV

设置环境变量,在后面的指令中引用

ENV <key>=<value> ... VOLUME

在镜像中创建挂载点,但是无法指定创建在主机的对应目录,可以通过docker inspect <CONTAINER NAME>查看Source挂载目录是哪个

VOLUME ["<路径1>", "<路径2>"...] or VOLUME <路径> EXPOSE

声明容器运行时打算用什么端口,并不会自动在宿主机和容器进行端口映射。可以使用docker run -p <主机端口:容器端口>进行端口映射,也可以使用docker run -P随机映射EXPOSE的端口

EXPOSE <port> [<port>/<protocol>...] EXPOSE 80/tcp WORKDIR

指定当前工作目录,后面各层(RUN,CMD,ENTRYPOINT,COPY,ADD)的当前目录就被改为WORKDIR目录,如果该目录不存在则会自动创建

WORKDIR /path/to/workdir # 示例,pwd的路径为/a/b/c WORKDIR /a WORKDIR b WORKDIR c RUN pwd USER

指定用户身份,影响后面各层操作的用户,用户必须事先创建好

USER <用户名>[:<用户组>]

如果是执行SHELL时候要改变身份,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 ``gosu`

# 建立 redis 用户,并使用 gosu 换另一个用户执行命令 RUN groupadd -r redis && useradd -r -g redis redis # 下载 gosu RUN wget -O /usr/local/bin/gosu "github.com/tianon/gosu/releases/download/1.12/gosu-amd64" \ && chmod +x /usr/local/bin/gosu \ && gosu nobody true # 设置 CMD,并以另外的用户执行 CMD [ "exec", "gosu", "redis", "redis-server" ] LABEL

为镜像添加元数据

LABEL <key>=<value> <key>=<value> <key>=<value> ... LABEL "com.example.vendor"="ACME Incorporated" LABEL com.example.label-with-value="foo" LABEL version="1.0" LABEL description="This text illustrates \ that label-values can span multiple lines." SHELL

用来指定RUN ENTRYPOINT CMD 指令的 shell,Linux 中默认为 ["/bin/sh", "-c"]

SHELL ["executable", "parameters"] ONBUILD

一般作为基础镜像时使用,该指令在构建当前镜像时不会执行,当其他镜像以此为基础镜像时才会执行

ONBUILD <其它指令> 使用git仓库构建

docker build -t hello-world git://github.com/docker-library/hello-world.git\#master:amd64/hello-world > Sending build context to Docker daemon 22.02kB Step 1/3 : FROM scratch ---> Step 2/3 : COPY hello / ---> e0499e772bd9 Step 3/3 : CMD ["/hello"] ---> Running in 1eeb706f26e2 Removing intermediate container 1eeb706f26e2 ---> 6abb50a2e5cc Successfully built 6abb50a2e5cc Successfully tagged hello-world:latest # 查看镜像 docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest 6abb50a2e5cc 47 seconds ago 13.3kB

指定要构建的git仓库地址,切换到master分支,进入amd64/hello-world目录开始构建

使用tar压缩包构建

docker engine下载该tar包并自动解压,以解压后的文件夹作为上下文开始构建

docker build server/context.tar.gz Dockerfile多阶段构建

Docker v17.05开始支持多阶段构建 (multistage builds),解决了以下问题:

  • 如果使用一个Dockerfile,镜像体积过大使得部署时间过长(比如编译依赖组件繁多,但实际运行中并不需要),而且容易泄露源码
  • 如果使用多个Dockerfile(比如编译和运行分开进行),中间需要脚本整合不同构建阶段内容是个比较复杂的工作,容易出现问题

下面对比单个Dockerfile构建和多阶段构建一个go helloworld程序的区别。

# app.go package main import "fmt" func main(){ fmt.Printf("Hello World!"); } 使用单个文件

Dockerfile

FROM golang:alpine RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories RUN apk --no-cache add git ca-certificates WORKDIR /go/src/github.com/go/helloworld/ COPY app.go . RUN go mod init RUN GOPROXY="goproxy.io" GO111MODULE=on go get -d -v github.com/go-sql-driver/mysql \ && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \ && cp /go/src/github.com/go/helloworld/app /root WORKDIR /root/ CMD ["./app"]

build

docker build -t go/helloworld:1 . # 运行容器 docker container run go/helloworld:1 > Hello World!% 多阶段构建

Dockerfile

# 将此阶段命名为builder FROM golang:alpine as builder # 解决下载go慢的问题 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories RUN apk --no-cache add git WORKDIR /go/src/github.com/go/helloworld/ RUN GOPROXY="goproxy.io" GO111MODULE=on go get -d -v github.com/go-sql-driver/mysql COPY app.go . # 处理go.mod缺失问题 RUN go mod init # 编译app.go RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . # 制作应用镜像,此阶段命名为prod 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"]

build

docker build -t go/helloworld:2 . # 运行容器 docker container run go/helloworld:2 > Hello World!%

对比两个镜像的大小,可以看到通过多阶段构建的方法,摒弃编译所需环境依赖,最后的应用镜像要精简很多很多

docker image ls |grep go/hello > go/helloworld 2 38f137a75add 6 minutes ago 7.86MB go/helloworld 1 e7606d3c0921 17 minutes ago 353MB 构建到某一阶段

依据上面的Dockerfile,如果我们只想构建到Build阶段的镜像时,可以用--targe参数指定此阶段别名来实现

docker build --target builder -t username/imagename:tag . 结束

本篇主要汇总go镜像相关操作指令等,镜像原理等架构技术会在后面深入学习docker底层实现时分析

学习自:
《Docker技术入门与实战(第3版)》Nigel,Poulton(奈吉尔·波尔顿) 著,李瑞丰,刘康 译
《深入浅出Docker》杨保华,戴王剑,曹亚仑 著
docs.docker.com/

望各位大佬指出错误! 不断学习更新,转载请注明出处

本文共计2061个文字,预计阅读时间需要9分钟。

简介+在docker学习笔记(1)- 架构概述+一节中可以看到,镜像是docker三大组件之一,可将其类比为虚拟机的版本。镜像由多层组成,每层叠加后,从外部看就像一个独立的对象。

简介

在docker学习笔记(1)- 架构概述一节中可以看到镜像是docker三大组件之一,可以将Docker镜像类比为虚拟机的模版。

  1. 镜像由多个层组成,每层叠加之后从外部看就像一个独立的对象,镜像的内部包括操作系统、应用程序、应用运行时所必须的依赖包等。
  2. 使用镜像时从仓库中拉取镜像到Docker主机,然后使用该镜像可以启动一个或多个容器,也可以将容器构建为镜像。

具体的概念与实现在后续实现Docker的基础技术中记录,先整理镜像的用法

相关命令 镜像加速

国内访问Docker hub有速度缓慢甚至会无法的情况,换用国内云厂商提供的加速服务,可以添加多个源,在/etc/docker/daemon.json文件中添加如下json内容,没有daemon.json可以自己新建

{ "registry-mirror": [ "hub-mirror.c.163.com/", "reg-mirror.qiniu.com" ] } # 重启docker systemctl daemon-reload systemctl restart docker # 查看是否生效,在使用docker pull时会快很多 docker info > Registry: index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Registry Mirrors: hub-mirror.c.163.com/ reg-mirror.qiniu.com/ 搜索镜像

docker search centos --filter=stars=20 > NAME DESCRIPTION STARS OFFICIAL AUTOMATED centos The official build of CentOS. 7066 [OK] centos/systemd systemd enabled base container. 105 [OK] centos/mysql-57-centos7 MySQL 5.7 SQL database server 92 centos/postgresql-96-centos7 PostgreSQL is an advanced Object-Relational … 45 centos/myip.ipip.net" ]

也可以做启动容器前的准备工作,以下为redis创建用户,然后为ENTRYPOINT指定脚本,该脚本判断CMD参数是否是启动redis-server,如果是使用redis用户启动,如果是其他操作则继续使用root,这样既保证了服务运行的安全性,又不妨碍使用root用户做一些调试和信息获取等操作

FROM alpine:3.4 ... RUN addgroup -S redis && adduser -S -G redis redis ... ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 6379 CMD [ "redis-server" ] # docker-entrypoint.sh #!/bin/sh ... # allow the container to be started with `--user` if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then find . \! -user redis -exec chown redis '{}' + exec gosu redis "$0" "$@" fi exec "$@" ENV

设置环境变量,在后面的指令中引用

ENV <key>=<value> ... VOLUME

在镜像中创建挂载点,但是无法指定创建在主机的对应目录,可以通过docker inspect <CONTAINER NAME>查看Source挂载目录是哪个

VOLUME ["<路径1>", "<路径2>"...] or VOLUME <路径> EXPOSE

声明容器运行时打算用什么端口,并不会自动在宿主机和容器进行端口映射。可以使用docker run -p <主机端口:容器端口>进行端口映射,也可以使用docker run -P随机映射EXPOSE的端口

EXPOSE <port> [<port>/<protocol>...] EXPOSE 80/tcp WORKDIR

指定当前工作目录,后面各层(RUN,CMD,ENTRYPOINT,COPY,ADD)的当前目录就被改为WORKDIR目录,如果该目录不存在则会自动创建

WORKDIR /path/to/workdir # 示例,pwd的路径为/a/b/c WORKDIR /a WORKDIR b WORKDIR c RUN pwd USER

指定用户身份,影响后面各层操作的用户,用户必须事先创建好

USER <用户名>[:<用户组>]

如果是执行SHELL时候要改变身份,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 ``gosu`

# 建立 redis 用户,并使用 gosu 换另一个用户执行命令 RUN groupadd -r redis && useradd -r -g redis redis # 下载 gosu RUN wget -O /usr/local/bin/gosu "github.com/tianon/gosu/releases/download/1.12/gosu-amd64" \ && chmod +x /usr/local/bin/gosu \ && gosu nobody true # 设置 CMD,并以另外的用户执行 CMD [ "exec", "gosu", "redis", "redis-server" ] LABEL

为镜像添加元数据

LABEL <key>=<value> <key>=<value> <key>=<value> ... LABEL "com.example.vendor"="ACME Incorporated" LABEL com.example.label-with-value="foo" LABEL version="1.0" LABEL description="This text illustrates \ that label-values can span multiple lines." SHELL

用来指定RUN ENTRYPOINT CMD 指令的 shell,Linux 中默认为 ["/bin/sh", "-c"]

SHELL ["executable", "parameters"] ONBUILD

一般作为基础镜像时使用,该指令在构建当前镜像时不会执行,当其他镜像以此为基础镜像时才会执行

ONBUILD <其它指令> 使用git仓库构建

docker build -t hello-world git://github.com/docker-library/hello-world.git\#master:amd64/hello-world > Sending build context to Docker daemon 22.02kB Step 1/3 : FROM scratch ---> Step 2/3 : COPY hello / ---> e0499e772bd9 Step 3/3 : CMD ["/hello"] ---> Running in 1eeb706f26e2 Removing intermediate container 1eeb706f26e2 ---> 6abb50a2e5cc Successfully built 6abb50a2e5cc Successfully tagged hello-world:latest # 查看镜像 docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest 6abb50a2e5cc 47 seconds ago 13.3kB

指定要构建的git仓库地址,切换到master分支,进入amd64/hello-world目录开始构建

使用tar压缩包构建

docker engine下载该tar包并自动解压,以解压后的文件夹作为上下文开始构建

docker build server/context.tar.gz Dockerfile多阶段构建

Docker v17.05开始支持多阶段构建 (multistage builds),解决了以下问题:

  • 如果使用一个Dockerfile,镜像体积过大使得部署时间过长(比如编译依赖组件繁多,但实际运行中并不需要),而且容易泄露源码
  • 如果使用多个Dockerfile(比如编译和运行分开进行),中间需要脚本整合不同构建阶段内容是个比较复杂的工作,容易出现问题

下面对比单个Dockerfile构建和多阶段构建一个go helloworld程序的区别。

# app.go package main import "fmt" func main(){ fmt.Printf("Hello World!"); } 使用单个文件

Dockerfile

FROM golang:alpine RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories RUN apk --no-cache add git ca-certificates WORKDIR /go/src/github.com/go/helloworld/ COPY app.go . RUN go mod init RUN GOPROXY="goproxy.io" GO111MODULE=on go get -d -v github.com/go-sql-driver/mysql \ && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \ && cp /go/src/github.com/go/helloworld/app /root WORKDIR /root/ CMD ["./app"]

build

docker build -t go/helloworld:1 . # 运行容器 docker container run go/helloworld:1 > Hello World!% 多阶段构建

Dockerfile

# 将此阶段命名为builder FROM golang:alpine as builder # 解决下载go慢的问题 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories RUN apk --no-cache add git WORKDIR /go/src/github.com/go/helloworld/ RUN GOPROXY="goproxy.io" GO111MODULE=on go get -d -v github.com/go-sql-driver/mysql COPY app.go . # 处理go.mod缺失问题 RUN go mod init # 编译app.go RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . # 制作应用镜像,此阶段命名为prod 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"]

build

docker build -t go/helloworld:2 . # 运行容器 docker container run go/helloworld:2 > Hello World!%

对比两个镜像的大小,可以看到通过多阶段构建的方法,摒弃编译所需环境依赖,最后的应用镜像要精简很多很多

docker image ls |grep go/hello > go/helloworld 2 38f137a75add 6 minutes ago 7.86MB go/helloworld 1 e7606d3c0921 17 minutes ago 353MB 构建到某一阶段

依据上面的Dockerfile,如果我们只想构建到Build阶段的镜像时,可以用--targe参数指定此阶段别名来实现

docker build --target builder -t username/imagename:tag . 结束

本篇主要汇总go镜像相关操作指令等,镜像原理等架构技术会在后面深入学习docker底层实现时分析

学习自:
《Docker技术入门与实战(第3版)》Nigel,Poulton(奈吉尔·波尔顿) 著,李瑞丰,刘康 译
《深入浅出Docker》杨保华,戴王剑,曹亚仑 著
docs.docker.com/

望各位大佬指出错误! 不断学习更新,转载请注明出处