Go 使用 Docker 部署的意义(优势)在哪?
由于 go 最终是编译为一个二进制可执行文件,没有运行时依赖,也不需要管理库,丢到服务器上就可以直接运行。所以,如果你有一个二进制文件,那么在容器中打包二进制文件的要点是什么?如果使用 docker 的话,还得在服务器上装 docker,那么把最终程序打包成 docker 有什么好处呢?
我想有这么几个好处:
依赖打包
如果你的应用程序(二进制文件)依赖配置文件或一些静态文件,那使用 docker 就很方便的把这些文件一起打包进容器里。
版本控制
启动 Docker 就和运行一个进程一样快,我们可以在几秒钟的时间内运行整个服务器集群。除此之外,Docker 镜像的注册中心使 Docker 容器还可以像 git 仓库一样,可以让你提交变更到 Docker 镜像中,并通过不同的版本来管理它们。设想如果你因为完成了一个组件的升级而导致你整个环境都损坏了,Docker 可以让你轻松地回滚到这个镜像的前一个版本。这整个过程可以在几分钟内完成。
隔离性
容器包含了应用程序的代码、运行环境、依赖库、配置文件等必需的资源。容器之间达到进程级别的隔离,在容器中的操作,不会影响道宿主机和其他容器,这样就不会出现应用之间相互影响的情形!
可移植性
可以实现开发、测试和生产环境的统一化和标准化。镜像作为标准的交付件,可在开发、测试和生产环境上以容器来运行,最终实现三套环境上的应用以及运行所依赖内容的完全一致。在现在微服务的架构中,一个应用拆成几十个微服务,每个微服务都对应有开发、测试、生产三套环境需要搭建。自己算算,如果采用传统的部署方式,有多少环境需要部署。
轻量和高效
和虚拟机相比,容器仅需要封装应用和应用需要的依赖文件,实现轻量的应用运行环境,且拥有比虚拟机更高的硬件资源利用率。在微服务架构中,有些服务负载压力大,需要以集群部署,可能要部署几十台机器上,对于某些中小型公司来说,使用虚拟机,代价太大。如果用容器,同样的物理机则能支持上千个容器,对中小型公司来说,省钱!
安全性
Docker 容器不能窥视运行在其他容器中的进程。从体系结构角度来看,每个容器只使用着自己的资源(从进程到网络堆栈)。作为紧固安全的一种手段,Docker 将宿主机操作系统上的敏感挂载点(例如 /proc 和 /sys)作为只读挂载点,并且使用一种写时复制系统来确保容器不能读取其他容器的数据。Docker 也限制了宿主机操作系统上的一些系统调用,并且和 SELinux 与 AppArmor 一起运行的很好。此外,在 Docker Hub 上可以使用的 Docker 镜像都通过数字签名来确保其可靠性。由于 Docker 容器是隔离的,并且资源是受限制的,所以即使你其中一个应用程序被黑,也不会影响运行在其它 Docker 容器上的应用程序。
Part 1: Dockerize
对于许多编程语言(包括 Go),有几个很好的官方和社区支持的容器。我们在容器化 Go apps 的时候,可以选择基于 Golang 官方镜像构建,如:golang:onbuild,golang:latest。但是这有一个很大的缺点:这些容器可能很大,所以基于它们的镜像创建的镜像文件将会非常大。
这是因为我们的应用程序是在容器内编译的。这意味着该容器需要安装 Go,以及 Go 的依赖关系,同时这也意味着我们需要一个程序包管理器和整个操作系统。实际上,如果您查看 Golang 的 Dockerfile,它将以 Debian Jessie 开头,安装 GCC 编译器和一些构建工具,压缩 Go 并安装它。因此,我们几乎有一个完整的 Debian 服务器和 Go 工具包来运行我们的小型应用程序。
所以我们应该使用一种静态构建 Go 容器化应用的方法,这种方法生成的镜像文件非常小。
Part 2: Our“app”
我以 Passport 应用为例,下面是应用程序结构:
$ tree -L 2
.
├── Makefile
├── control
├── app
│ ├── boot
│ ├── kernel
│ ├── lib
│ ├── main.go
│ ├── server
├── output
│ └── bin
└── vendor
├── appengine
├── cloud.google.com
├── github.com
├── go.etcd.io
├── go.uber.org
├── golang.org
├── golang_org
├── google.golang.org
├── gopkg.in
└── vendor.json
我们要做的是在工作目录中编译 Go,然后将二进制文件添加到容器中。这种方式比直接使用官方镜像麻烦一些,不过体积要小很多,所以建议这么做。
go build -o output/bin/go_service_passport ./app
Part 3: Builder
.dockerignore file
首先,在项目的根目录下,新建一个文本文件.dockerignore,写入下面的内容。
# comment
.git
.gitignore
output
*.out
*.log
*/temp*
*/*/temp*
temp?
*.md
!README.md
在 Docker CLI 将上下文发送到 Docker 守护程序之前,它将在上下文的根目录中查找名为.dockerignore 的文件。如果此文件存在,则 CLI 会修改上下文以排除与其中的模式匹配的文件和目录。这有助于避免不必要地将大型文件或敏感文件和目录发送到守护程序,并避免使用 ADD 或 COPY 将它们添加到映像中。
如果.dockerignore 文件中的行以第 1 列中的#开头,则该行将被视为注释,并且在 CLI 解释之前将被忽略。
官方说明见:.dockerignore file
Dockerfile
接下来就是编写 Dockerfile 文件了。Dockerfile 是一个文本文档,Docker 可以通过阅读 Dockerfile 中的指令来自动构建映像。
该文档内的指令不区分大小写。但是,习惯是大写,以便更轻松地将它们与参数区分开。
Docker 按顺序在 Dockerfile 中运行指令。Dockerfile 必须以“FROM”指令开头。当然,FROM 前面可以有一个或多个 ARG 指令或注释,ARG 指令声明 Dockerfile 中 FROM 行中使用的参数。
在开始之前我们应该想清楚到底使用哪个基础镜像?一般情况下,都会从以下三个基础镜像开始。
- 镜像 scratch(空镜像), 大小 0B
- 镜像 busybox(空镜像 + busybox), 大小 1.4MB
- 镜像 alpine (空镜像 + busybox + apk), 大小 3.98MB
So what’s scratch? Scratch is a special docker image that’s empty. It’s truly 0B:
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
scratch latest 511136ea3c5a 22 months ago 0 B
目前 Docker 官方已开始推荐使用 Alpine 替代之前的 Ubuntu 做为基础镜像环境。这样会带来多个好处。包括镜像下载速度加快,镜像安全性提高,主机之间的切换更方便,占用更少磁盘空间等。
下面,不如以上三个镜像我们都尝试一下吧。在项目的根目录下,新建一个文本文件 Dockerfile,写入下面的内容。
FROM scratch
FROM scratch
LABEL maintainer="tobeabme@gmail.com" version="1.0"
ENV RUNMODE dev
ENV CONSUL_ADDR 127.0.0.1:8500
ENV ETCD_ADDR 127.0.0.1:2379
ADD output/bin/go_service_passport /
EXPOSE 8080 9080
CMD ["/go_service_passport"]
下面这种写法是错误的,为什么呢?
FROM scratch
MAINTAINER weizi
ENV APP_RUN_DIR /data/app/go/work
ENV APP_LOG_DIR /data/app/go/log
RUN mkdir -p ${APP_RUN_DIR} \
&& mkdir -p ${APP_LOG_DIR}
COPY output/bin/go_service_passport ${APP_RUN_DIR}
WORKDIR ${APP_RUN_DIR}
EXPOSE 8080 9080
CMD ["${APP_RUN_DIR}/go_service_passport"]
这是由于 scratch 镜像几乎不包含任何东西,不支持环境变量,也没有 shell 命令。因此,基于 scratch 的镜像通过 ADD 指令进行添加,以此绕过目录创建。更完整的原因说明见如下:
FROM scratch is a completely empty filesystem. You have no installed libraries, and no shell (like /bin/sh) included in there. To use this as your base, you’d need a statically linked binary, or you’ll need to install all of the normal tools that are included with a linux distribution.
The latter is what is prepackaged in the various busybox, debian, ubuntu, centos, etc images on the docker hub. The fast way to make your image work with a minimal base image is to change the from to FROM busybox and change your /bin/bash to /bin/sh.
FROM busybox
FROM busybox
MAINTAINER weizi
ENV APP_RUN_DIR /data/app/go/work
ENV RUNMODE dev
ENV CONSUL_ADDR 127.0.0.1:8500
ENV ETCD_ADDR 127.0.0.1:2379
#RUN mkdir -p /data/app/go/work
WORKDIR $APP_RUN_DIR
ADD output/bin/go_service_passport .
EXPOSE 8080 9080
CMD ["./go_service_passport","-g","daemon off;"]
多阶构建
ARG GO_VERSION=1.10.3
FROM golang:${GO_VERSION} AS builder
MAINTAINER weizi
LABEL author="name@gmail.com"
ENV GO111MODULE=off
ENV GO15VENDOREXPERIMENT=1
WORKDIR $GOPATH/src/code.qschou.com/peduli/go_service_passport
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o output/bin/go_service_passport ./app
#------------------------------------------
FROM alpine
MAINTAINER weizi
LABEL author="name@gmail.com"
ENV APP_RUN_DIR /data/app/go/work
ENV RUNMODE dev
ENV CONSUL_ADDR 127.0.0.1:8500
ENV ETCD_ADDR 127.0.0.1:2379
RUN apk update \
&& apk --no-cache add wget ca-certificates \
&& apk add -f --no-cache git \
&& apk add -U tzdata \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR $APP_RUN_DIR
COPY --from=builder /go/src/code.qschou.com/peduli/go_service_passport/output/bin/go_service_passport .
EXPOSE 8080 9080
CMD ["./go_service_passport","-g","daemon off;"]
最后,让我们看下上面三种镜像生成后的大小:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
passport-busybox 1.0.1 1028fbd88847 32 seconds ago 36.3MB
passport-scratch 1.0.1 aa407fee8d95 33 minutes ago 35.1MB
passport-multi-stage 1.0.9 dd8a070d96e9 2 days ago 59.4MB
Go 应用本身的二进制文件为 33MB
$ ls -lh output/bin/go_service_passport
-rwxr-xr-x 1 will staff 33M Nov 18 11:09 output/bin/go_service_passport
scratch size = 35.1-33 = 2.1M
busybox size = 36.3-33 = 3.3M
alpine size = 59.4-33 = 26.1M
指令说明:
FROM
FROM 指令初始化一个新的构建阶段,并为后续指令设置基本映像。因此,有效的 Dockerfile 必须以 FROM 指令开头。
格式:
FROM <image> [AS <name>]
Or
FROM <image>[:<tag>] [AS <name>]
Or
FROM <image>[@<digest>] [AS <name>]
- ARG 是 Dockerfile 中唯一可以出现在 FROM 指令之前的指令。
- FROM 可以在单个 Dockerfile 中多次出现,以创建多个映像或将一个构建阶段用作对另一个构建阶段的依赖。只需在每个新的 FROM 指令之前记录一次提交输出的最后一个图像 ID。每个 FROM 指令清除由先前指令创建的任何状态。
- 通过将 AS 名称添加到 FROM 指令中,可以选择为新的构建阶段指定名称。该名称可以在后续的 FROM 和 COPY –from = < 名称 | 索引 > 指令中使用,以引用在此阶段构建的映像。
- tag or digest 值是可选的。如果您忽略其中任何一个,那么缺省情况下构建器都会采用 latest 标签。如果构建器找不到标签值,则返回错误。
MAINTAINER
MAINTAINER 指令设置生成图像的“作者”字段。LABEL 指令是此指令的更为灵活的版本,您应该使用它,因为它可以设置所需的任何元数据,并且可以轻松查看,例如使用 docker inspect。要设置与 MAINTAINER 字段相对应的标签,可以使用:
LABEL maintainer="SvenDowideit@home.org.au"
LABEL
LABEL 指令将元数据添加到图像。标签是键值对。要在 LABEL 值中包含空格,请使用引号和反斜杠。一些用法示例:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL description="This text illustrates \
that label-values can span multiple lines."
一个镜像可以有多个标签。以下两种方式之一在一条指令中指定多个标签:
基本或父图像(FROM 行中的图像)中包含的标签由您的图像继承。如果标签已经存在但具有不同的值,则最近应用的值将覆盖任何先前设置的值。
LABEL multi.label1="value1" multi.label2="value2" other="value3"
OR
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
被包含在基础镜像或父镜像(images in the FROM line)的 Labels 是被你的镜像继承的。如果一个标签已经存在,但具有不同的值,则最近应用的值将覆盖任何先前设置的值。
去查看一个镜像的 Labels,请使用 docker inspect 命令。
ENV
设置环境变量。将环境变量 <key> 设置为值 <value>。此值将在构建阶段中所有后续指令(RUN、ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD)的环境中使用。
格式:
ENV <key> <value>
ENV <key>=<value> ...
第一种形式,ENV < 键 > < 值 >,将单个变量设置为一个值。第一个空格之后的整个字符串将被视为 <value>- 包括空格字符。该值可以被其他环境变量解释,因此如果不对引号字符进行转义,则将其删除。
第二种格式,ENV <key> = <value> …,允许一次设置多个变量。请注意,第二种形式在语法中使用等号(=),而第一种形式则不使用等号(=)。与命令行解析一样,引号和反斜杠可用于在值中包含空格。
For example:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
and
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
当一个容器是从产生的镜像运行时,使用 ENV 设置的环境变量将持续存在。您可以使用 docker inspect 查看值,并使用 docker run –env <key> = <value> 更改它们。
Environment replacement
环境变量(用 ENV 语句声明)也可以在某些指令中用作 Dockerfile 解释的变量。通过在字面上将类似变量的语法包含到语句中来处理。
在 Dockerfile 文档中,可以使用 $variable_name 或 ${variable_name} 引用环境变量,它们是等同的。其中大括号的变量是用在没有空格的变量名中的,如 ${foo}_bar。
${variable_name}变量也支持一些标准的 bash 修饰符,如:
- ${variable:-word} 表示如果 variable 设置了,那么结果就是设置的值。否则设置值为 word
- ${variable:+word} 表示如果 variable 设置了,那么结果是 word 值,否则为空值。
word 可以是任意的字符,包括额外的环境变量。
转义符(Escaping)可以添加在变量前面:$foo or ${foo},例如,会分别转换为 $foor 和 ${foo}。示例:
FROM busybox
ENV foo /bar
WORKDIR ${foo} # WORKDIR /bar
ADD . $foo # ADD . /bar
COPY \$foo /quux # COPY $foo /quux
在此实例中:
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
将导致 def 的值为 hello,而不是 bye。但是,ghi 的值是 bye。
Dockerfile 中的以下指令列表支持环境变量:
- ADD
- COPY
- ENV
- EXPOSE
- FROM
- LABEL
- STOPSIGNAL
- USER
- VOLUME
- WORKDIR
- ONBUILD(在 1.4 之前,ONBUILD 指令不支持环境变量)
官方说明见: Environment replacement
ARG
ARG 指令定义了一个变量,用户可以在构建时使用 –build-arg <varname> = <value> 标志使用 docker build 命令将其传递给构建器。如果用户指定了未在 Dockerfile 中定义的构建参数,则构建会输出警告。
[Warning] One or more build-args [foo] were not consumed.
格式:
ARG <name>[=<default value>]
Dockerfile 可能包含一个或多个 ARG 指令。例如,
FROM busybox
ARG user1
ARG buildno
...
警告:不建议使用构建时变量来传递诸如 github 密钥,用户凭据等机密。构建时变量值对于使用 docker history 命令的映像的任何用户都是可见的。
默认值
ARG 指令可以选择包含默认值:
FROM busybox
ARG user1=someuser
ARG buildno=1
...
如果 ARG 指令具有默认值,并且在构建时未传递任何值,则构建器将使用默认值。
ARG 指令在定义它的构建阶段结束时超出范围。要在多个阶段中使用 arg,每个阶段都必须包含 ARG 指令。
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS
FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS
使用 ARG 变量
您可以使用 ARG 或 ENV 指令来指定 RUN 指令可用的变量。使用 ENV 指令定义的环境变量始终会覆盖同名的 ARG 指令。
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER v1.0.0
4 RUN echo $CONT_IMG_VER
然后,假定此映像是使用以下命令构建的:
$ docker build --build-arg CONT_IMG_VER=v2.0.1 .
在这种情况下,RUN 指令使用 v1.0.0 而不是用户传递的 ARG 设置:v2.0.1 此行为类似于 shell 脚本,其中局部作用域的变量会覆盖从参数传递过来的作为参数。
使用上面的示例,但使用不同的 ENV 规范,您可以在 ARG 和 ENV 指令之间创建更有用的交互:
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
4 RUN echo $CONT_IMG_VER
与 ARG 指令不同,ENV 值始终保留在生成的映像中。考虑不带 –build-arg 标志的 Docker 构建:
$ docker build .
使用此 Dockerfile 示例,CONT_IMG_VER 仍保留在映像中,但其值为 v1.0.0,因为它是 ENV 指令在第 3 行中设置的默认值。
在此示例中,变量扩展技术使您可以从命令行传递参数,并利用 ENV 指令将其保留在最终映像中。
预定义的 ARG
Docker 具有一组预定义的 ARG 变量,您可以在 Dockerfile 中使用它们而无需相应的 ARG 指令。
- HTTP_PROXY
- http_proxy
- HTTPS_PROXY
- https_proxy
- FTP_PROXY
- ftp_proxy
- NO_PROXY
- no_proxy
默认情况下,这些预定义变量从 docker history 记录的输出中排除。排除它们可以降低意外泄漏 HTTP_PROXY 变量中的敏感身份验证信息的风险。
--build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com
COPY
COPY 指令从 <src> 复制新文件或目录,并将它们添加到容器的文件系统中,路径为 <dest>。由于我们这里是拷贝 Go 构建好的二进制文件,所以不用将当前目录下的所有文件(除了.dockerignore 排除的路径),都拷贝进入 image 文件的 $WORKPATH 目录。如果需要拷贝后自动解压,用 ADD 指令。
COPY 有两种形式:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] (this form is required for paths containing whitespace)
每个 <src> 都可以包含通配符,并且将使用 Go 的 filepath.Match 规则进行匹配。例如:
COPY hom* /mydir/ # adds all files starting with "hom"
COPY hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt"
<dest> 是绝对路径,或相对于 WORKDIR 的路径,源将在目标容器内复制到该路径。
COPY test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/ # adds "test" to /absoluteDir/
复制包含特殊字符 (such as [ and]), 的文件或目录时,需要遵循 Golang 规则转义那些路径,以防止将它们视为匹配模式。例如,要复制名为 arr[0].txt 的文件,请使用以下命令:
COPY arr[[]0].txt /mydir/ # copy a file named "arr[0].txt" to /mydir/
可选地,COPY 接受 –from=<name|index> 标志,该标志可用于将源位置设置为先前的构建阶段 (created with FROM .. AS <name>),该阶段将用于代替由发送的构建上下文用户。如果找不到具有指定名称的构建阶段,则尝试改用具有相同名称的图像。
COPY 遵守以下规则:
- <src> 路径必须在构建的上下文中。您无法 COPY ../something /something,因为 Docker 构建的第一步是将上下文目录(和子目录)发送到 docker 守护程序。
- 如果 <src> 是目录,则将复制目录的整个内容,包括文件系统元数据。注意:目录本身不会被复制,只是其内容被复制。
- 如果 <src> 是任何其他类型的文件,则将其与其元数据一起单独复制。在这种情况下,如果 <dest> 以尾斜杠 / 结束,则它将被视为目录,并且 <src> 的内容将写入 <dest>/base(<src>)
- 如果直接或由于使用通配符而指定了多个 <src> 资源,则 <dest> 必须是目录,并且必须以斜杠 / 结尾。
- 如果 <dest> 不以斜杠结尾,它将被视为常规文件,并且 <src> 的内容将写入 <dest>。
- 如果 <dest> 不存在,它将与路径中所有缺少的目录一起创建。
官方说明见:COPY
ADD
ADD 指令从 <src> 复制新文件,目录或远程文件 URL,并将它们添加到镜像的文件系统中的路径 <dest>。
ADD 有两种形式:
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] (this form is required for paths containing whitespace)
ADD 遵守以下规则:
- 如果 <src> 是 URL,并且 <dest> 不以斜杠结尾,则从 URL 下载文件并将其复制到 <dest>。
- 如果 <src> 是 URL,并且 <dest> 确实以斜杠结尾,则从 URL 推断文件名,并将文件下载到 <dest>/<filename>。例如,ADD http://example.com/foobar / 将创建文件 /foobar。该 URL 必须具有不平凡的路径,以便在这种情况下可以找到适当的文件名(http://example.com 将不起作用)。
- 如果 <src> 是采用公认压缩格式(身份,gzip,bzip2 或 xz)的本地 tar 归档文件,则将其解压缩为目录。来自远程 URL 的资源不会被解压缩。注意:是否将文件识别为可识别的压缩格式仅根据文件的内容而不是文件的名称来完成。例如,如果一个空文件碰巧以.tar.gz 结尾,则该文件将不会被识别为压缩文件,并且不会生成任何类型的解压缩错误消息,而是会将文件简单地复制到目标位置。
- 如果 <dest> 不存在,它将与路径中所有缺少的目录一起创建。
其它规则与 COPY 相同,不重复描述。
WORKDIR
WORKDIR 指令为 Dockerfile 中跟在其后的所有 RUN,CMD,ENTRYPOINT,COPY 和 ADD 指令设置工作目录。相当于 cd。如该目录不存在,WORKDIR 会帮你建立目录,即使随后的 Dockerfile 指令未使用它。
WORKDIR 指令可在 Dockerfile 中多次使用。如果提供了相对路径,则它将相对于上一个 WORKDIR 指令的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
该 Dockerfile 中最后一个 pwd 命令的输出为 /a/b/c。
WORKDIR 指令可以解析以前使用 ENV 设置的环境变量。您只能使用在 Dockerfile 中显式设置的环境变量。例如:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
该 Dockerfile 中最后一个 pwd 命令的输出为 /path/$DIRNAME
RUN
RUN 指令将在当前映像顶部的新层中执行任何命令,并提交结果。生成的提交映像将用于 Dockerfile 中的下一步。
RUN <command> (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows)
RUN ["executable", "param1", "param2"] (exec form)
分层运行 RUN 指令并生成提交符合 Docker 的核心概念,在 Docker 上,提交很便宜,并且可以从映像历史记录的任何位置创建容器,就像源代码控制一样。
在 shell 形式中,可以使用(反斜杠)将一条 RUN 指令继续到下一行。
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'
Together they are equivalent to this single line:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
注意:要使用‘/bin/sh’以外的其他 shell,请使用 exec 形式传入所需的 shell。例如,RUN [“/bin/bash”, “-c”, “echo hello”]
注意:不同与 shell 形式,exec 形式不会调用命令 shell。这意味着正常的外壳处理不会发生。例如,RUN [“echo”, “$HOME”] 不会在 $HOME 上进行变量替换。如果要进行 shell 处理,则可以使用 shell 形式或直接执行 shell,例如:RUN [“sh”, “-c”, “echo $HOME”]。
注意:在 JSON 格式中,必须转义反斜杠。在 Windows 中,反斜杠是路径分隔符,这一点尤其重要。由于无效的 JSON,以下行将被视为 shell 形式,并以意外的方式失败:RUN [“c:windowssystem32tasklist.exe”] 此示例的正确语法是:RUN [“c:\windows\system32\tasklist.exe”]
在下一个构建时,RUN 指令的缓存不会自动失效。诸如 RUN apt-get dist-upgrade - y 之类的指令的缓存将在下一次构建中重用。可以使用 –no-cache 标志使 RUN 指令的缓存无效,例如 docker build –no-cache。
EXPOSE
EXPOSE 指令通知 Docker 运行时容器在指定的网络端口上进行侦听。您可以指定端口是侦听 TCP 还是 UDP,如果未指定协议,则默认值为 TCP。
EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
要将 EXPOSE 和在运行时使用 -p < 宿主端口 >:< 容器端口 > 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。格式为 EXPOSE < 端口 1 > [< 端口 2 >…]。
默认情况下,EXPOSE 假定使用 TCP。您还可以指定 UDP:
EXPOSE 80/udp
要同时在 TCP 和 UDP 上公开,请包括以下两行:
EXPOSE 80/tcp
EXPOSE 80/udp
无论 EXPOSE 设置如何,都可以在运行时使用 - p 标志覆盖它们。例如
docker run -p 80:80/tcp -p 80:80/udp ...
docker network 命令支持创建用于容器之间通信的网络,而无需暴露或发布特定端口,因为连接到网络的容器可以通过任何端口相互通信。有关详细信息,请参阅此功能的概述。
CMD
用于指定默认的容器主进程的启动命令。Dockerfile 中只能有一条 CMD 指令。如果您列出多个 CMD,则只有最后一个 CMD 才会生效。
CMD 的主要目的是为执行中的容器提供默认值。这些默认值可以包含一个可执行文件,也可以忽略该可执行文件,在这种情况下,您还必须指定 ENTRYPOINT 指令。
注意:如果使用 CMD 为 ENTRYPOINT 指令提供默认参数,则 CMD 和 ENTRYPOINT 指令均应使用 JSON 数组格式指定。
CMD 指令具有三种形式:
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。)
CMD command param1 param2 (shell form)
If you use the shell form of the CMD, then the <command> will execute in /bin/sh -c:
FROM ubuntu
CMD echo "This is a test." | wc -
如果要在没有 shell 的情况下运行 <command>,则必须将命令表示为 JSON 数组,并提供可执行文件的完整路径。此数组形式是 CMD 的首选格式。任何其他参数必须在数组中分别表示为字符串:
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
注意,指定了 CMD 命令以后,docker container run 命令就不能附加命令了(比如 /bin/bash),否则它会覆盖 CMD 命令。
RUN 命令与 CMD 命令的区别在哪里?
简单说,RUN 命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;CMD 命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个 RUN 命令,但是只能有一个 CMD 命令。
VOLUME
Volume,通常翻译为数据卷,用于保存持久化数据。当我们将数据库例如 MySQL 运行在 Docker 容器中时,一般将数据通过 Docker Volume 保存在主机上,这样即使删除 MySQL 容器,数据依然保存在主机上,有效保证了数据的安全性。
VOLUME 指令创建具有指定名称的挂载点,并将其标记为保存来自本地主机或其他容器的外部安装的卷。该值可以是 JSON 数组,VOLUME [“/var/log/”], 或具有多个参数的纯字符串,例如 VOLUME /var/log or VOLUME /var/log /var/db。
我们知道,镜像的每一层都是 ReadOnly 只读的。只有在我们运行容器的时候才会创建读写层。文件系统的隔离使得:
- 容器不再运行时,数据将不会持续存在,数据很难从容器中取出。
- 无法在不同主机之间很好的进行数据迁移。
- 数据写入容器的读写层需要内核提供联合文件系统,这会额外的降低性能。
docker 为我们提供了三种不同的方式将数据挂载到容器中:volume、bind mount、tmpfs。
volume 方式是 docker 中数据持久化的最佳方式。
- docker 默认在主机上会有一个特定的区域(/var/lib/docker/volumes/ Linux),该区域用来存放 volume。
- 非 docker 进程不应该去修改该区域。
- volume 可以通过 docker volume 进行管理,如创建、删除等操作。
- volume 在生成的时候如果不指定名称,便会随机生成。
volume 在容器停止或删除的时候会继续存在,如需删除需要显示声明。
$ docker rm -v <container_id>
$ docker volume rm <volume_name>
格式:
VOLUME ["< 路径 1 >", "< 路径 2 >"...]
VOLUME < 路径 >
可以通过以下两种方式创建 VOLUME:
- 在 Dockerfile 中指定 VOLUME
- 使用 docker run –volume 命令来指定
这两种方式有区别吗?
根据官方文档,Dockerfile 生成目标镜像的过程就是不断 docker run + docker commit 的过程,当 Dockerfile 执行到 VOLUME /some/dir(这里为 /var/lib/mysql)这一行时,输出:
Step 6 : VOLUME /var/lib/mysql
---> Running in 0c842ec90849
---> 214e3dccd0f2
在这一步,docker 生成了临时容器 0c842ec90849,然后 commit 容器得到镜像 214e3dccd0f2。因此 VOLUME /var/lib/mysql 是通过 docker run -v /var/lib/mysql,即第二种方式来实现的,随后由于容器的提交,该配置被保存到了镜像 214e3dccd0f2 中,通过 inspcet 可以查看到:
"Volumes": {"/var/lib/mysql": {},
}
使用 docker inspect 命令,可以查看 Docker 容器的详细信息:
docker inspect --format='{{json .Mounts}}' test | python -m json.tool
"Mounts": [
{
"Name": "8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a",
"Source": "/mnt/sda1/var/lib/docker/volumes/8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a/data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "","RW": true,"Propagation":""
}
]
Source 表示主机上的目录,Destination 为容器中的目录。
由于没有指定挂载到的宿主机目录,因此会默认挂载到宿主机的 /var/lib/docker/volumes 下的一个随机名称的目录下,在这为 /mnt/sda1/var/lib/docker/volumes/8827c361d103c1272907da0b82268310415f8b075b67854f27dbca0b59a31a1a/data。因此 Dockerfile 中使用 VOLUME 指令挂载目录和 docker run 时通过 - v 参数指定挂载目录的区别在于,run 的 - v 可以指定挂载到宿主机的哪个目录,而 Dockerfile 的 VOLUME 不能,其挂载目录由 docker 随机生成。
若指定了宿主机目录,比如:
docker run --name mysql -v ~/volume/mysql/data:/var/lib/mysql -d mysql:5.7
那么 inspect 如下:
"Mounts": [
{
"Source": "/Users/weizi/volume/mysql/data",
"Destination": "/var/lib/mysql",
"Mode": "","RW": true,"Propagation":"rprivate"
}
]
这里将 /var/lib/mysql 挂载到宿主机的 /Users/weizi/volume/mysql/data 目录下,而不再是默认的 /var/lib/docker/volumes 目录。这样做有什么好处呢?我们知道,将该目录挂载到宿主机,可以使数据在容器被移除时得以保留,而不会随着容器 go die。下次新建 mysql 容器,只需同样挂载到 /Users/weizi/volume/mysql/data,即可复用原有数据。
在宿主机 /Users/weizi/volume/mysql/data 目录中新建 hello.txt 文件,在容器 /var/lib/mysql 目录中可见。
在容器 /var/lib/mysql 目录中新建 world.txt 文件,在宿主机 /Users/weizi/volume/mysql/data 目录中可见。
通过 VOLUME,我们得以绕过 docker 的 Union File System,从而直接对宿主机的目录进行直接读写,实现了容器内数据的持久化和共享化。
关于 Dockerfile 中的卷,请记住以下几点。
- 从 Dockerfile 内更改卷:如果在声明了卷后,有任何构建步骤更改了卷内的数据,则这些更改将被丢弃。
- JSON 格式:列表被解析为 JSON 数组。您必须用双引号(”)而不是单引号(’)括起单词。
- 主机目录(host directory)在容器运行时(container run-time)声明:主机目录(挂载点)从本质上说是依赖于主机的。这是为了保留镜像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,您无法从 Dockerfile 中挂载主机目录。VOLUME 指令不支持指定 host-dir 参数。创建或运行容器时,必须指定安装点。
-
基于 Windows 的容器上的卷:使用基于 Windows 的容器时,容器内的卷的目的地必须是以下之一:
- 不存在 或 空目录
- C: 以外的驱动器
Part 4: image
完成 Dockerfile 文档之后,接下来我们可以基于 Dockerfile 文档生成镜像文件了。
docker build -t passport-scratch:1.0.1 -f Dockerfile .
以上,如果运行成功,就可以看到新生成的 image 文件了。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
passport-busybox 1.0.1 1028fbd88847 10 minutes ago 36.3MB
passport-scratch 1.0.1 aa407fee8d95 42 minutes ago 35.1MB
passport-multi-stage 1.0.9 dd8a070d96e9 2 days ago 59.4MB
passport-busybox 1.0.0 d56a21693694 3 days ago 36.3MB
<none> <none> 492f4b83ea2d 16 minutes ago 34.9MB
nginx v1 75b671fe9af3 9 days ago 126MB
busybox latest 020584afccce 2 weeks ago 1.22MB
nginx latest 540a289bab6c 3 weeks ago 126MB
alpine latest 965ea09ff2eb 3 weeks ago 5.55MB
ubuntu 19.10 09604a62a001 4 weeks ago 72.9MB
kong latest 03f9bc1cd4f7 2 months ago 130MB
postgres 9.6 f5548544c480 3 months ago 230MB
pantsel/konga latest dc0af5db6ce9 7 months ago 389MB
pgbi/kong-dashboard latest f9e2977207e3 8 months ago 96.4MB
golang 1.10 6fd1f7edb6ab 9 months ago 760MB
golang 1.10-alpine 7b53e4a31d21 9 months ago 259MB
golang 1.10.3-alpine cace225819dc 15 months ago 259MB
golang 1.10.3 d0e7a411e3da 16 months ago 794MB
soyking/e3w latest a123f3eeaad2 23 months ago 24.2MB
soyking/etcd-goreman 3.2.7 4c0139e55ed5 2 years ago 121MB
上面代码中,- t 参数用来指定 image 文件的名字(名字中不能报考下划线_),后面还可以用冒号指定标签。如果不指定,默认的标签就是 latest。最后的那个点表示 Dockerfile 文件所在的路径,上例是当前路径,所以是一个点。
docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端 / 服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径(也就是上面命令中最后的那个圆点 “.”),docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
Part 5: container
Ok, 接下来我们来生成容器。docker container run 命令会基于 image 文件生成容器。
$ docker run -p 80:8080 -it passport-scratch:1.0.1
no such file or directory
从上面可以看出,运行容器时报错了。这是为什么呢?
Go 二进制文件正在其运行的操作系统上寻找一些库。我们编译了应用,但它仍动态链接到需要运行的库(即,它绑定到的所有 C 库)。不幸的是,scratch 是空的,因此没有库。我们要做的是修改构建脚本,以使用所有内置库静态编译我们的应用程序。
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o output/bin/go_service_passport ./app
OR
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o output/bin/go_service_passport ./app
上面的命令,我们禁用了 cgo,它为我们提供了静态二进制文件。我们还将操作系统设置为 Linux(以防有人在 Mac 或 Windows 上构建),- a 标志意味着可以重建我们正在使用的所有软件包,这意味着所有导入将在禁用 cgo 的情况下进行重建。
重新生成二进制文件后,让我们再次试一下:
# 后台运行
$ docker container run -it -p 80:8080 -d passport-scratch:1.0.1
OR
# 前台运行并删除
$ docker container run --rm -p 8080:8080 -it passport-scratch:1.0.1
2019-11-15T17:25:33.747+0800 DEBUG setting/etcd.go:35 setting.etcd: Backend config {"backend": "etcdv3", "machines": ["127.0.0.1:2379"], "keyspace": "","traceId":""}
这时发现仍然有问题,容器内的主程序是可以运行了,不过很快就退出了。什么原因呢?
这是因为 passport 程序内部有连接 etcd,consul,mysql 等服务,而这些基础服务在宿主机器上默认监听地址是 127.0.0.1。而容器内也确实存在 127.0.0.1/localhost 地址。不过这和宿主机上的 127.0.0.1 是不一样的,所以容器内的程序就无法访问宿主机上的 127.0.0.1/localhost。解决方法是把宿主机器上的 etcd, consul 等服务的监听地址修改为 0.0.0.0。如:
#consul
$ nohup /usr/local/opt/consul/bin/consul agent -dev -client 0.0.0.0
$ lsof -nP -iTCP -sTCP:LISTEN | grep consul
consul 79872 will 5u IPv4 0x9c21088b2c0aa1b 0t0 TCP 127.0.0.1:8300 (LISTEN)
consul 79872 will 6u IPv4 0x9c210888fd5863b 0t0 TCP 127.0.0.1:8302 (LISTEN)
consul 79872 will 8u IPv4 0x9c21088b2c1137b 0t0 TCP 127.0.0.1:8301 (LISTEN)
consul 79872 will 11u IPv6 0x9c21088832ff0d3 0t0 TCP *:8600 (LISTEN)
consul 79872 will 12u IPv6 0x9c21088832fd413 0t0 TCP *:8500 (LISTEN)
#etcd
$ nohup ./etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2379
$ lsof -nP -iTCP -sTCP:LISTEN | grep etcd
etcd 80114 will 4u IPv4 0x9c21088b0d6463b 0t0 TCP 127.0.0.1:2380 (LISTEN)
etcd 80114 will 6u IPv6 0x9c21088832ff693 0t0 TCP *:2379 (LISTEN)
好,让我们再重启下容器并加上环境变量
# 后台运行
$ docker container run -it -p 80:8080 -d -e ETCD_ADDR=172.16.60.88:2379 -e CONSUL_ADDR=172.16.60.88:8500 passport-scratch:1.0.1
OR
# 前台运行并删除
$ docker container run --rm -p 8080:8080 -it -e ETCD_ADDR=172.16.60.88:2379 -e CONSUL_ADDR=172.16.60.88:8500 passport-scratch:1.0.1
2019-11-15T17:25:33.747+0800 DEBUG setting/etcd.go:35 setting.etcd: Backend config {"backend": "etcdv3", "machines": ["127.0.0.1:2379"], "keyspace": "","traceId":""}
### 以下是启动信息
2019-11-15T22:46:12.278+0800 DEBUG setting/etcd.go:35 setting.etcd: Backend config {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "","traceId":""}
2019-11-15T22:46:12.290+0800 DEBUG setting/etcd.go:65 setting.etcd: Retrieved mysql-key-val from etcd store {"key": "root/config/common/database/mysql/passport", "config": {"master":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10},"slave":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10}}, "traceId": ""}
DEBU[0001] new passport mysql store MasterDB="&{<nil> <nil> 0 0xc42022ac80 false 2 {0xc42034a280} <nil> map[] 0xc4200e05a0 0x16c7aa0 0xc4201e39a0 false}" SlaveDB="&{<nil> <nil> 0 0xc42022ae60 false 2 {0xc42034a280} <nil> map[] 0xc4200e06c0 0x16c7aa0 0xc4201e3b60 false}"
2019-11-15T22:46:13.888+0800 DEBUG setting/etcd.go:35 setting.etcd: Backend config {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "","traceId":""}
2019-11-15T22:46:13.894+0800 DEBUG setting/etcd.go:102 setting.etcd: Retrieved redis-key-val from etcd store {"key": "root/config/common/database/redis", "config": {"master":{"addr":"127.0.0.1:6379","password":"","db":1},"slave":{"addr":"127.0.0.1:6379","password":"","db":1}}, "traceId": ""}
2019-11-15T22:46:13.895+0800 DEBUG setting/etcd.go:35 setting.etcd: Backend config {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "","traceId":""}
2019-11-15T22:46:13.902+0800 DEBUG setting/etcd.go:102 setting.etcd: Retrieved redis-key-val from etcd store {"key": "root/config/common/database/redis", "config": {"master":{"addr":"127.0.0.1:6379","password":"","db":1},"slave":{"addr":"127.0.0.1:6379","password":"","db":1}}, "traceId": ""}
2019-11-15 22:46:13.905270 I | Initializing logging reporter
INFO[0001] new command Command="&{<nil> <nil>}"
2019-11-15T22:46:13.907+0800 DEBUG setting/etcd.go:35 setting.etcd: Backend config {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "","traceId":""}
2019-11-15T22:46:13.916+0800 DEBUG setting/etcd.go:65 setting.etcd: Retrieved mysql-key-val from etcd store {"key": "root/config/common/database/mysql/passport", "config": {"master":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10},"slave":{"dsn":"","user":"root","pass":"GL@c*Nm#dkaLH!FNe","host":"rm-j6cli54dhwo5ski2quo.mysql.rds.aliyuncs.com","port":3306,"dbname":"peduli","max_open":100,"max_idle":10}}, "traceId": ""}
DEBU[0003] new passport mysql store MasterDB="&{<nil> <nil> 0 0xc4200ba6e0 false 2 {0xc42034a280} <nil> map[] 0xc4200e0a20 0x16c7aa0 0xc42019bee0 false}" SlaveDB="&{<nil> <nil> 0 0xc4201c3ea0 false 2 {0xc42034a280} <nil> map[] 0xc420376480 0x16c7aa0 0xc4201e2780 false}"
DEBU[0003] new store MySQL="&{0xc4200e0a20 0xc420376480 0xc4200831e0}" config="&{dev [10.10.1.29:2379] 0 0 }"
2019-11-15T22:46:15.736+0800 DEBUG setting/etcd.go:35 setting.etcd: Backend config {"backend": "etcdv3", "machines": ["10.10.1.29:2379"], "keyspace": "","traceId":""}
2019-11-15T22:46:15.742+0800 DEBUG setting/etcd.go:83 setting.etcd: Retrieved service-key-val from etcd store {"key": "root/config/custom/go_service_passport", "config": {"Runmode":"","EtcdEndpoints":null,"AppConfigPath":"","ServiceName":"","ServiceIP":"","ServiceHttpPort":0,"ServiceRpcPort":0,"log_level":"debug","log_path":"","domain_www":"https://www.pedulisehat.id","domain_api":"","domain_passport":"https://passport-qa.pedulisehat.id","domain_project":"https://project-qa.pedulisehat.id","domain_trade":"https://trade-qa.pedulisehat.id","domain_static_avatar":"https://static-qa.pedulisehat.id/img/avatar","url_share_project":"","url_ico":"","host_passport":"","host_project":"","host_trade":"","url_share":"","domain_gtry":""},"traceId":""}
DEBU[0003] setting.NewConfig Config="&{dev [10.10.1.29:2379] go_service_passport 0.0.0.0 8080 9080 debug https://www.pedulisehat.id https://passport-qa.pedulisehat.id https://project-qa.pedulisehat.id https://trade-qa.pedulisehat.id https://static-qa.pedulisehat.id/img/avatar }"
INFO[0003] command run ... Command="&{<nil> 0xc4201e21c0}"
2019-11-15 22:46:15.744629 I | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
2019-11-15 22:46:15.745561 I | RPC Server has been started up. 172.17.0.2:9080
可以发现我们的 passport 服务可以正常启动了,查看下容器的运行状态:
$ docker container ls --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b58ee39088dd passport-multi-stage:1.0.9 "./go_service_passpo…" 5 seconds ago Up 4 seconds 9080/tcp, 0.0.0.0:80->8080/tcp recursing_poitras
参数说明:
-p 参数: 容器的 8080 端口映射到本机的 80 端口。
-it 参数: 容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。其中,-t 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,-i 则让容器的标准输入保持打开。
passport-scratch:1.0.1: image 文件的名字(如果有标签,还需要提供标签,默认是 latest 标签)。
当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
到此,完整的 docker 容器制作流程讲完了!
发布 image 文件
容器运行成功后,就确认了 image 文件的有效性。这时,我们就可以考虑把 image 文件分享到网上,让其他人使用。image 文件制作完成后,可以上传到网上的仓库。
首先,去 Docker 的官方仓库 Docker Hub 注册一个账户,这是最重要、最常用的 image 仓库。
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: isgiker
Password:
Login Succeeded
接着,为本地的 image 标注用户名和版本。
$ docker image tag [imageName] [username]/[repository]:[tag]
# 实例
$ docker image tag passport-multi-stage:1.0.9 isgiker/passport-multi-stage:1.0.9
最后,发布 image 文件。
$ docker image push isgiker/passport-multi-stage:1.0.9
The push refers to repository [docker.io/isgiker/passport-multi-stage]
e22072d3470d: Pushed
9136612a4372: Pushed
dac53910d311: Pushed
77cae8ab23bf: Mounted from library/alpine
1.0.9: digest: sha256:b5e9f0db2bd3e9ba684c8c359b087aa097adbb6a7426732b6d9246ca1b3dd6dc size: 1158
可以通过 docker search 命令来查找官方仓库中的镜像,
$ docker search keywords[username/image name]
命令
image 命令
docker image COMMAND
Child commands
Command | Description |
---|---|
docker image build | Build an image from a Dockerfile |
docker image history | Show the history of an image |
docker image import | Import the contents from a tarball to create a filesystem image |
docker image inspect | Display detailed information on one or more images |
docker image load | Load an image from a tar archive or STDIN |
docker image ls | List images |
docker image prune | Remove unused images |
docker image pull | Pull an image or a repository from a registry |
docker image push | Push an image or a repository to a registry |
docker image rm | Remove one or more images |
docker image save | Save one or more images to a tar archive (streamed to STDOUT by default) |
docker image tag | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE |
docker image 命令
container 命令
docker container COMMAND
Child commands
Command | Description |
---|---|
docker container attach | Attach local standard input, output, and error streams to a running container |
docker container commit | Create a new image from a container’s changes |
docker container cp | Copy files/folders between a container and the local filesystem |
docker container create | Create a new container |
docker container diff | Inspect changes to files or directories on a container’s filesystem |
docker container exec | Run a command in a running container |
docker container export | Export a container’s filesystem as a tar archive |
docker container inspect | Display detailed information on one or more containers |
docker container kill | Kill one or more running containers |
docker container logs | Fetch the logs of a container |
docker container ls | List containers |
docker container pause | Pause all processes within one or more containers |
docker container port | List port mappings or a specific mapping for the container |
docker container prune | Remove all stopped containers |
docker container rename | Rename a container |
docker container restart | Restart one or more containers |
docker container rm | Remove one or more containers |
docker container run | Run a command in a new container |
docker container start | Start one or more stopped containers |
docker container stats | Display a live stream of container(s) resource usage statistics |
docker container stop | Stop one or more running containers |
docker container top | Display the running processes of a container |
docker container unpause | Unpause all processes within one or more containers |
docker container update | Update configuration of one or more containers |
docker container wait | Block until one or more containers stop, then print their exit codes |
docker container 命令
docker 命令
Child commands
Command | Description |
---|---|
docker attach | Attach local standard input, output, and error streams to a running container |
docker build | Build an image from a Dockerfile |
docker builder | Manage builds |
docker checkpoint | Manage checkpoints |
docker commit | Create a new image from a container’s changes |
docker config | Manage Docker configs |
docker container | Manage containers |
docker context | Manage contexts |
docker cp | Copy files/folders between a container and the local filesystem |
docker create | Create a new container |
docker deploy | Deploy a new stack or update an existing stack |
docker diff | Inspect changes to files or directories on a container’s filesystem |
docker engine | Manage the docker engine |
docker events | Get real time events from the server |
docker exec | Run a command in a running container |
docker export | Export a container’s filesystem as a tar archive |
docker history | Show the history of an image |
docker image | Manage images |
docker images | List images |
docker import | Import the contents from a tarball to create a filesystem image |
docker info | Display system-wide information |
docker inspect | Return low-level information on Docker objects |
docker kill | Kill one or more running containers |
docker load | Load an image from a tar archive or STDIN |
docker login | Log in to a Docker registry |
docker logout | Log out from a Docker registry |
docker logs | Fetch the logs of a container |
docker manifest | Manage Docker image manifests and manifest lists |
docker network | Manage networks |
docker node | Manage Swarm nodes |
docker pause | Pause all processes within one or more containers |
docker plugin | Manage plugins |
docker port | List port mappings or a specific mapping for the container |
docker ps | List containers |
docker pull | Pull an image or a repository from a registry |
docker push | Push an image or a repository to a registry |
docker rename | Rename a container |
docker restart | Restart one or more containers |
docker rm | Remove one or more containers |
docker rmi | Remove one or more images |
docker run | Run a command in a new container |
docker save | Save one or more images to a tar archive (streamed to STDOUT by default) |
docker search | Search the Docker Hub for images |
docker secret | Manage Docker secrets |
docker service | Manage services |
docker stack | Manage Docker stacks |
docker start | Start one or more stopped containers |
docker stats | Display a live stream of container(s) resource usage statistics |
docker stop | Stop one or more running containers |
docker swarm | Manage Swarm |
docker system | Manage Docker |
docker tag | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE |
docker top | Display the running processes of a container |
docker trust | Manage trust on Docker images |
docker unpause | Unpause all processes within one or more containers |
docker update | Update configuration of one or more containers |
docker version | Show the Docker version information |
docker volume | Manage volumes |
docker wait | Block until one or more containers stop, then print their exit codes |
docker container 命令
参考
Go 语言应用 Docker 化部署
Building Docker Containers for Go Applications
Deploying a containerized Go app on Kubernetes
Building Minimal Docker Containers for Go Applications
构建安全可靠、最小化的 Docker 镜像
Docker ARG, ENV and .env – a Complete Guide
How To Pass Environment Info During Docker Builds
跟我一起学 Docker——Volume