Golang 容器部署

Go 使用 Docker部署的意义(优点)在哪?

因为 go 最终是编译为一个二进制可执行文件,没有运行时依赖,也不须要管理库,丢到服务器上就能够直接运行。因此,若是你有一个二进制文件,那么在容器中打包二进制文件的要点是什么?若是使用 docker 的话,还得在服务器上装 docker,那么把最终程序打包成 docker 有什么好处呢?html

我想有这么几个好处:node

依赖打包
若是你的应用程序(二进制文件)依赖配置文件或一些静态文件,那使用docker就很方便的把这些文件一块儿打包进容器里。python

版本控制
启动Docker就和运行一个进程同样快,咱们能够在几秒钟的时间内运行整个服务器集群。除此以外,Docker 镜像的注册中心使Docker容器还能够像git仓库同样,可让你提交变动到Docker镜像中,并经过不一样的版原本管理它们。设想若是你由于完成了一个组件的升级而致使你整个环境都损坏了,Docker可让你轻松地回滚到这个镜像的前一个版本。这整个过程能够在几分钟内完成。mysql

隔离性
容器包含了应用程序的代码、运行环境、依赖库、配置文件等必需的资源。容器之间达到进程级别的隔离,在容器中的操做,不会影响道宿主机和其余容器,这样就不会出现应用之间相互影响的情形!linux

可移植性
能够实现开发、测试和生产环境的统一化和标准化。镜像做为标准的交付件,可在开发、测试和生产环境上以容器来运行,最终实现三套环境上的应用以及运行所依赖内容的彻底一致。在如今微服务的架构中,一个应用拆成几十个微服务,每一个微服务都对应有开发、测试、生产三套环境须要搭建。本身算算,若是采用传统的部署方式,有多少环境须要部署。nginx

轻量和高效
和虚拟机相比,容器仅须要封装应用和应用须要的依赖文件,实现轻量的应用运行环境,且拥有比虚拟机更高的硬件资源利用率。在微服务架构中,有些服务负载压力大,须要以集群部署,可能要部署几十台机器上,对于某些中小型公司来讲,使用虚拟机,代价太大。若是用容器,一样的物理机则能支持上千个容器,对中小型公司来讲,省钱!git

安全性
Docker容器不能窥视运行在其余容器中的进程。从体系结构角度来看,每一个容器只使用着本身的资源(从进程到网络堆栈)。做为紧固安全的一种手段,Docker将宿主机操做系统上的敏感挂载点(例如/proc和/sys)做为只读挂载点,而且使用一种写时复制系统来确保容器不能读取其余容器的数据。Docker也限制了宿主机操做系统上的一些系统调用,而且和SELinux与AppArmor一块儿运行的很好。此外,在Docker Hub上可使用的Docker镜像都经过数字签名来确保其可靠性。因为Docker容器是隔离的,而且资源是受限制的,因此即便你其中一个应用程序被黑,也不会影响运行在其它Docker容器上的应用程序。github

Part 1: Dockerize

对于许多编程语言(包括 Go ),有几个很好的官方和社区支持的容器。咱们在容器化Go apps的时候,能够选择基于 Golang 官方镜像构建,如:golang:onbuild,golang:latest。可是这有一个很大的缺点:这些容器可能很大,因此基于它们的镜像建立的镜像文件将会很是大。golang

这是由于咱们的应用程序是在容器内编译的。这意味着该容器须要安装 Go ,以及 Go 的依赖关系,同时这也意味着咱们须要一个程序包管理器和整个操做系统。实际上,若是您查看 Golang 的 Dockerfile,它将以 Debian Jessie 开头,安装 GCC 编译器和一些构建工具,压缩 Go 并安装它。所以,咱们几乎有一个完整的 Debian 服务器和 Go 工具包来运行咱们的小型应用程序。redis

因此咱们应该使用一种静态构建 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)能够添加在变量前面:&dollar;foo or &dollar;{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

相关文章
相关标签/搜索