Docker镜像&容器的基本操作

515 阅读8分钟

获取镜像

上一篇文章提到过 Docker 官方提供了一个公共的镜像仓库:Docker Hub,我们就可以从这上面获取镜像,获取镜像的命令:docker pull,格式为:

➜ docker pull [选项] [Docker Registry 地址[:端口]/]仓库名[:标签]

镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号],默认地址是 Docker Hub。

仓库名:这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。比如:

➜ docker pull routeman/user-api:v1
v1: Pulling from routeman/user-api
59bf1c3509f3: Pull complete
858b8406d0e4: Pull complete
3779a8cd7b07: Pull complete
8451c22ff927: Pull complete
0354926bb12f: Pull complete
Digest: sha256:925a1560e41009c60f8110398b068ebef45c0c61ef6728f87533521e8bc49529
Status: Downloaded newer image for routeman/user-api:v1
docker.io/routeman/user-api:v1

上面的命令中没有给出 Docker 镜像仓库地址,因此会默认从 Docker Hub 官方仓库获取镜像。而镜像名称是 routeman/user-api:v1,因此将会获取官方镜像 routeman/user-api 仓库中标签为 v1 的镜像。

从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。

运行容器

有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 routeman/user-api:v1 为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行下面的命令。

➜ docker run --rm -it -p 8888:8888 --name=user-api routeman/user-api:v1
Starting server at 0.0.0.0:8888...

可参考我提交的镜像运行说明:user-api:v1 镜像运行说明

  • docker run 就是运行容器的命令,具体格式我们会在后面的课程中进行详细讲解,我们这里简要的说明一下上面用到的参数。
  • it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
  • rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
  • p:容器对外暴露端口,这里映射的端口为 8888。
  • name:定义运行后容器的名称。
  • routeman/user-api:v1 :指用 routeman/user-api:v1 镜像为基础来启动容器。

需要注意的是:

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

查看有哪些镜像

➜ docker images

列表包含了仓库名、标签、镜像 ID、创建时间以及所占用的空间。镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签。

后台运行容器

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加-d 参数来实现。下面举两个例子来说明一下。

➜ docker run --rm -it -d -p 8888:8888 --name=user-api routeman/user-api:v1
2e2bf5e8b16e724f5315045247024db7215971d5d77cc1c12aaed64995eaa866
➜
➜ docker ps |grep user-api
2e2bf5e8b16e   routeman/user-api:v1  "./user -f etc/user-…"  16 seconds ago  Up 15 seconds    0.0.0.0:8888->8888/tcp   user-api

使用-d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息。

进入容器

在使用-d 参数时,容器启动后会进入后台。某些时候需要进入容器进行操作:exec 命令 -i -t 参数。

只用-i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。 当-i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

➜ docker exec -it 2e2bf5e8b16e /bin/sh
/app #
/app # ls
etc   user

如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 docker exec 的原因。

更多参数说明请使用 docker exec --help 查看。

删除容器

可以使用 docker container rm 来删除一个处于终止状态的容器。例如:

➜ docker container rm user-api

也可用使用 docker rm 容器名来删除,如果要删除一个运行中的容器,可以添加-f 参数。Docker 会发送 SIGKILL 信号给容器。

用 docker container ls -a (或者 docker ps -a)命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。

➜ docker container prune

删除本地镜像

如果要删除本地的镜像,可以使用`docker image rm·命令,其格式为:

➜ docker image rm [选项] <镜像1> [<镜像2> ...]

或者

➜ docker rmi

或者用 ID、镜像名、摘要删除镜像 其中,<镜像> 可以是 镜像短 ID、镜像长 ID、镜像名 或者 镜像摘要。 比如我们有这么一些镜像:

➜ docker images
REPOSITORY                               TAG                 IMAGE ID            CREATED             SIZE
routeman/user-api                        v1                  8d8edc6be0e2        2 days ago          19.7MB
postgres                                 12-alpine           a58cf5527d36        8 months ago        158MB
mysql                                    5.7                 2c9028880e58        8 months ago        447MB
ubuntu                                   16.04               b9409899fe86        2 years ago         122MB

我们可以用镜像的完整 ID,也称为 长 ID,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 短 ID 来删除镜像。docker image ls 默认列出的就已经是短 ID 了,一般取前 3 个字符以上,只要足够区分于别的镜像就可以了。

比如这里,如果我们要删除 mysql:5.7 镜像,可以执行:

➜ docker rmi 2c9

我们也可以用镜像名,也就是 <仓库名>:<标签>,来删除镜像。

➜ docker rmi mysql:5.7

......

docker 的更多基本操作,可以参考官方文档。

下面再来讲讲如何通过编写 Dockerfile 文件来定制镜像。

Dockerfile 定制镜像

镜像的定制实际上就是定制每一层所添加的配置、文件等信息,但是命令毕竟只是命令,每次定制都得去重复执行这个命令,而且还不够直观,如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么这些问题不就都可以解决了吗?对的,这个脚本就是我们说的 Dockerfile。

Dockerfile 简单介绍

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

以上面我自己的一个镜像 user-api 为例,这次我们使用 Dockerfile 来定制。目录结构如下:

routeman
├── go.mod
├── go.sum
└── service
    └── user-api
        ├── Dockerfile
        ├── etc
        │   └── user-api.yaml
        ├── user-api.go
        └── cmd
            ├── config
            │   └── config.go
            ├── handler
            │   ├── user-api-handler.go
            │   └── routes.go
            ├── logic
            │   └── user-api-logic.go
            ├── svc
            │   └── servicecontext.go
            └── types
                └── types.go

Dockerfile 内容为:

FROM golang:alpine AS builder
LABEL stage=gobuilder
ENV CGO_ENABLED 0
ENV GOOS linux
ENV GOPROXY https://goproxy.cn,direct
WORKDIR /build/routeman
ADD go.mod .
ADD go.sum .
RUN go mod download
COPY . .
COPY service/user-api/etc /app/etc
RUN go build -ldflags="-s -w" -o /app/user-api service/user-api/user-api.go

WORKDIR /app
COPY --from=builder /app/user-api /app/user-api
COPY --from=builder /app/etc /app/etc
CMD ["./user-api", "-f", "etc/user-api.yaml"]

Dockerfile 命令详细说明参考官方文档:Dockerfile reference

通过编写好的 Dockerfile 构建镜像

首先进到 routeman 目录下,即 go.mod 和 go.sum 目录下执行:

➜ docker built -t routeman/user-api:v1 -f service/user-api/Dockerfile .

这里我们使用了 docker build 命令进行镜像构建。其格式为:

➜ docker built [选项] <上下文路径/URL/>

可以重新定义标签:

# 格式
➜ docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
# 修改标签命令
➜ docker tag routeman/user-api:v1 routeman/user-api:v2

迁移镜像

Docker 还提供了 docker load 和 docker save 命令,用以将镜像保存为一个 tar 文件,然后传输到另一个位置上,再加载进来。这是在没有 Docker Registry 时的做法,现在已经不推荐,镜像迁移应该直接使用 Docker Registry,无论是直接使用 Docker Hub 还是使用内网私有 Registry 都可以。

# 打包镜像,拷贝到其它机器
docker save -o user-api.tar routeman/user-api:v1
scp user-api.tar username@ip:/dir/...

# 加载镜像包
docker load -i user-api.tar

小结

以上是关于 Docker 镜像和容器的一些基本操作说明,以及如何通过编写 Dockerfile 文件来构建镜像。主要以自己写的一个 user-api 镜像为例来进行讲解。更多高级的命令及操作可查看官方文档。

推荐阅读:

容器化技术之 Docker 了解下