docker入门

docker关于容器、镜像、仓库的基本操作。

参考资料:

docker官网文档

docker tutorial

docker从入门到实践

docker的三个概念

镜像(Image)

类似于虚拟机中的镜像,是一个包含有文件系统的面向Docker引擎的只读模板。任何应用程序运行都需要环境,而镜像就是用来提供这种运行环境的。例如一个Ubuntu镜像就是一个包含Ubuntu操作系统环境的模板,同理在该镜像上装上Apache软件,就可以称为Apache镜像。

操作系统分为内核和用户空间。对于Linux而言,内核启动后,会挂载文件系统为其提供用户空间支持。而Docker镜像(Image),就相当于是一个文件系统。

Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

镜像的分层存储

镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。

分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

容器(Container)

容器类似于一个轻量级的沙盒,可以将其看作一个极简的Linux系统环境(包括root权限、进程空间、用户空间和网络空间等),以及运行在其中的应用程序。Docker引擎利用容器来运行、隔离各个应用。容器是镜像创建的应用实例,可以创建、启动、停止、删除容器,各个容器之间是是相互隔离的,互不影响。注意:镜像本身是只读的,容器从镜像启动时,Docker在镜像的上层创建一个可写层,镜像本身不变。

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间,甚至自己的用户ID空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。

前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。

容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

按照Docker最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器可以随意删除、重新run,数据却不会丢失。

仓库(Repository)与注册服务器(Docker Registry)

注册服务器(Docker Registry)是存放仓库(Repository)的地方,一般会有多个仓库;而仓库是存放镜像的地方,一般每个仓库存放一类镜像,每个镜像利用tag进行区分,比如Ubuntu仓库存放有多个版本(12.04、14.04等)的Ubuntu镜像。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以latest作为默认标签。

以Ubuntu镜像为例,ubuntu是仓库的名字,其内包含有不同的版本标签,如14.0416.04。我们可以通过ubuntu:14.04或者ubuntu:16.04来具体指定所需哪个版本的镜像。如果忽略了标签,比如ubuntu,那将视为ubuntu:latest

仓库名经常以两段式路径形式出现,比如jwilder/nginx-proxy,前者往往意味着Docker Registry多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体Docker Registry的软件或服务。

docker的安装

在macOS下安装docker:https://docs.docker.com/docker-for-mac/install

查看docker版本信息

1
➜  ~ docker version

查看docker帮助文档

1
➜  ~ docker --help

配置镜像加速器

注册阿里云账号,开通阿里云docker镜像服务https://cr.console.aliyun.com/cn-hangzhou/mirrors,并获取加速器地址

在docker for mac的Preferences->Daemon->Registry mirrors中添加阿里云镜像加速器地址。

docker关于镜像(image)的基本操作

从官方注册服务器(https://hub.docker.com)的仓库中pull下CentOS的镜像,每个仓库会有多个镜像,用tag标示,如果不加tag,默认使用latest镜像:

查看centos镜像是否存在

1
➜  ~ docker search centos  # 查看centos镜像是否存在

利用pull命令获取镜像

1
2
3
4
5
6
➜  ~ docker pull centos  # 利用pull命令获取镜像
Using default tag: latest
latest: Pulling from library/centos
aeb7866da422: Pull complete
Digest: sha256:67dad89757a55bfdfabec8abd0e22f8c7c12a1856514726470228063ed86593b
Status: Downloaded newer image for centos:latest

从 Docker Registry 获取镜像的命令是 docker pull 。其命令格式为:

1
docker pull [选项] [Docker Registry地址]<仓库名>:<标签>

具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。

  • Docker Registry地址:地址的格式一般是 <域名/IP>[:端口号] 。默认地址是 Docker Hub。
  • 仓库名:如之前所说,这里的仓库名是两段式名称,即<用户名>/<软件名>。 对于 Docker Hub,如果不给出用户名,则默认为library,也就是官方镜像。

比如:

1
2
3
4
5
6
7
8
9
10
$ docker pull ubuntu:14.04
14.04: Pulling from library/ubuntu
bf5d46315322: Pull complete
9f13e0ac480c: Pull complete
e8988b5b3097: Pull complete
40af181810e7: Pull complete
e6f7c7e5c03e: Pull complete
Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b49298
3ae97c3d643fbbe
Status: Downloaded newer image for ubuntu:14.04

上面的命令中没有给出 Docker Registry 地址,因此将会从 Docker Hub 获取镜像。而镜像名是ubuntu:14.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为14.04的镜像。

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

在实验上面命令的时候,你可能会发现,你所看到的层ID以及sha256的摘要和这里的不一样。这是因为官方镜像是一直在维护的,有任何新的bug,或者版本更新,都会进行修复再以原来的标签发布,这样可以确保任何使用这个标签的用户可以获得更安全、更稳定的镜像。

查看当前系统中的images信息

要想列出已经下载下来的镜像,可以使用 docker images 命令。

1
2
3
➜  ~ docker images  # 查看当前系统中的images信息
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 75835a67d134 6 weeks ago 200MB

列表包含了仓库名、标签、镜像 ID、创建时间以及所占用的空间。

其中仓库名、标签在之前的基础概念章节已经介绍过了。镜像 ID 则是镜像的唯一 标识,一个镜像可以对应多个标签。例如,ubuntu:16.04ubuntu:latest 拥有相同的 ID,因为它们对应的是同一个镜像。

Explaining Docker Image IDs

不加任何参数的情况下,docker images会列出所有顶级镜像,但是有时候我们只希望列出部分镜像。有好几个参数可以帮助做到这个事情。

根据仓库名列出镜像

1
$ docker images ubuntu

列出特定的某个镜像,也就是说指定仓库名和标签

1
$ docker images ubuntu:16.04

除此以外, docker images 还支持强大的过滤器参数 --filter ,或者简写 -f 。之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。 比如,我们希望看到在 mongo:3.2 之后建立的镜像,可以用下面的命令:

1
$ docker images -f since=mongo:3.2

想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可。

此外,如果镜像构建时,定义了 LABEL ,还可以通过 LABEL 来过滤。

1
$ docker images -f label=com.example.version=0.1

默认情况下, docker images 会输出一个完整的表格,但是我们并非所有时候都
会需要这些内容。比如,刚才删除虚悬镜像的时候,我们需要利用docker images把所有的虚悬镜像的 ID 列出来,然后才可以交给docker rmi 命令作为参数来删除指定的这些镜像,这个时候就用到了 -q 参数。

1
2
3
4
5
6
7
8
$ docker images -q
5f515359c7f8
05a60462f8ba
fe9198c04d62
00285df0df87
f753707788c5
f753707788c5
1e0c3dd64ccd

常常使用–filter 配合 -q 产生出指定范围的 ID 列表,然后送给另一个 docker 命令作为参数。

也可用Go的模板语法直接列出镜像结果,并且只包含镜像ID和仓库名

1
$ docker images --format "{{.ID}}: {{.Repository}}"

或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列:

1
$ docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"

新建镜像的两种方式

镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层。

利用镜像启动一个容器后进行修改,再commit更新后的副本

修改容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜  ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b05eb7072750 centos:latest "/bin/bash" 2 hours ago Exited (127) 2 hours ago optimistic_golick
➜ ~ docker start optimistic_golick
optimistic_golick
➜ ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b05eb7072750 centos:latest "/bin/bash" 2 hours ago Up 27 seconds optimistic_golick
➜ ~ docker attach optimistic_golick
[root@b05eb7072750 /]# git --version
bash: git: command not found
[root@b05eb7072750 /]# yum install git
[root@b05eb7072750 /]# git --version
git version 1.8.3.1

将容器转化为一个镜像

1
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]

例如:

1
2
3
4
docker commit \
--author "Tao Wang <twang2218@gmail.com>" \ --message "修改了默认网页" \
webserver \
nginx:v2
1
2
➜  ~ docker commit -m "centos with git" -a "sunnie" b05eb7072750 sunnie/centos:git
sha256:bc5c77aa38058e09c9b2a4b5ae8bad80bcaf6abd549c73c91184272bd0ef6595

-m指定说明信息,-a指定用户信息,b05eb7072750代表容器的id;sunnie/centos:git指定目标镜像的用户名、仓库名和 tag 信息。注意这里的用户名sunnie,需要与后面远程登录dockerhub的用户名一致才能push。

我们还可以用docker history具体查看镜像内的历史记录。

查看现有镜像

1
2
3
4
➜  ~ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sunnie/centos git bc5c77aa3805 2 minutes ago 353MB
centos latest 75835a67d134 6 weeks ago 200MB

此时Docker引擎中就有了我们新建的镜像sunnie/centos:git,此镜像和原有的CentOS镜像区别在于多了个Git工具。此时我们利用新镜像创建的容器,本身就自带git了。

用新镜像创建容器

1
2
3
4
5
6
7
8
9
➜  ~ docker run -it sunnie/centos:git /bin/bash
[root@6b23b207fa53 /]# git --version
git version 1.8.3.1
[root@6b23b207fa53 /]# exit
exit
➜ ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6b23b207fa53 sunnie/centos:git "/bin/bash" 22 seconds ago Exited (0) 4 seconds ago clever_hopper
b05eb7072750 centos:latest "/bin/bash" 2 hours ago Exited (0) 7 minutes ago optimistic_golick

docker commit的缺点

除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然docker diff或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。

而且,回顾之前提及的镜像所使用的分层存储的概念,除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用docker commit制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。

不要使用 docker commit 定制镜像,定制行为应该使用 Dockerfile 来完成。

利用Dockerfile创建

Dockerfile可以理解为一种配置文件,用来告诉docker build命令应该执行哪些操作。

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

Dockerfile的内容

1
2
3
4
5
6
7
8
9
10
11
# 说明该镜像以哪个镜像为基础,FROM是必备的指令,而且必须是第一条指令
FROM centos:latest

# 构建者的基本信息
MAINTAINER mengting

# 在build这个镜像时执行的操作
RUN yum install -y git

# 拷贝本地文件到镜像中
COPY ./* /Users/sunnie/Desktop/1.png
FROM指定基础镜像

除了选择现有镜像为基础镜像外,Docker还存在一个特殊的镜像,名为scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

1
2
FROM scratch
...

如果你以scratch为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如swarm、coreos/etcd。对于Linux下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接FROM scratch会让镜像体积更加小巧。使用Go语言开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为Go是特别适合容器微服务架构的语言的原因之一。

RUN执行命令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:

  • shell 格式:RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的RUN指令就是这种格式。
1
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
  • exec 格式:RUN ["可执行文件", "参数1", "参数2"] ,这更像是函数调用中的格式。

Dockerfile 中每一个指令都会建立一层,不要把每一条命令都写成一条RUN,只需要使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。Dockerfile 支持 Shell 类的行尾添加 \的命令换行方式,以及行首 # 进行注释的格式。

一组命令的最后需要添加清理工作的命令,删除了为了编译构建 所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层 被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要 添加的东西,任何无关的东西都应该清理掉。 很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。

COPY复制文件

格式:

  • COPY <源路径>...<目标路径>
  • COPY ["<源路径1>",..."<目标路径>"]

RUN指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的<目标路径>位置。比如:

1
COPY package.json /usr/src/app

<源路径>可以是多个,甚至可以是通配符,其通配符规则要满足Go的filepath.Match规则,如:

1
2
COPY hom* /mydir/
COPY hom?.txt /mydir/

<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存 在会在复制文件前先行创建缺失目录。

此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如 读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建 相关文件都在使用 Git 进行管理的时候。

ADD指令

仅在需要自动解压缩的场合使用 ADD 。

如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为gzip/bzip2/xz的情况下, ADD 指令将会自动解压缩这个压缩文件到<目标路径>去。

在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像 ubuntu 中:

1
2
3
FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
...
构建镜像

docker build 命令进行镜像构建。其格式为:

1
docker build [选项] <上下文路径/URL/->

上下文路径的含义:

首先我们要理解docker build的工作原理。Docker在运行时分为Docker引擎(也就是服务端守护进程)和客户端工具。Docker的引擎提供了一组REST API,被称为Docker Remote API,而如docker命令这样的客户端工具,则是通过这组API与Docker引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种docker功能,但实际上,一切都是使用的远程调用形式在服务端(Docker引擎)完成。也因为这种C/S设计,让我们操作远程服务器的Docker引擎变得轻而易举。

当我们进行镜像构建的时候,并非所有定制都会通过RUN指令完成,经常会需要将一些本地文件复制进镜像,比如通过COPY指令、AND指令等。而docker build命令构建镜像,其实并非在本地构建,而是在服务端,也就是Docker引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build命令得知这个路径后,会将路径下的所有内容打包,然后上传给Docker引擎。这样Docker引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

如果在Dockerfile中这么写:

1
COPY ./package.json /app/

这并不是要复制执行docker build命令所在的目录下的package.json,也不是复制Dockerfile所在目录下的package.json,而是复制上下文(context)目录下的package.json

有了Dockerfile之后,就可以利用build命令构建镜像了:

1
2
3
docker build -t mengting/centos:gitdir . 
或者
docker build -t="mengting/centos:gitdir" .

其中-t用来指定新镜像的用户信息、tag等。最后的点表示在当前目录寻找Dockerfile

这个.实际上是在指定上下文的目录,docker build命令会将该目录下的内容打包交给Docker引擎以帮助构建镜像。

一般来说,应该会将Dockerfile置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给Docker引擎,那么可以用.gitignore一样的语法写一个.dockerignore,该文件是用于剔除不需要作为上下文传递给Docker引擎的。

那么为什么会有人误以为.是指定Dockerfile所在目录呢?这是因为在默认情况下,如果不额外指定Dockerfile的话,会将上下文目录下的名为Dockerfile的文件作为Dockerfile。

这只是默认行为,实际上Dockerfile的文件名并不要求必须为Dockerfile,而且并不要求必须位于上下文目录中,比如可以用-f ../Dockerfile.php参数指定某个文件作为Dockerfile。

参数指定某个文件作为Dockerfile。当然,一般大家习惯性的会使用默认的文件名Dockerfile,以及会将其置于镜

像构建上下文目录中。

构建完成之后,同样可以使用docker images命令查看现有的镜像:

1
2
3
4
5
➜  ~ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mengting/centos gitdir b201ea6b50a6 46 seconds ago 353MB
sunnie/centos git bc5c77aa3805 41 minutes ago 353MB
centos latest 75835a67d134 6 weeks ago 200MB

用新镜像创建容器

1
➜  ~ docker run -it mengting/centos:gitdir /bin/bash

删除镜像

删除镜像前必须先删除以此镜像为基础的容器。

1
2
➜  ~ docker rm container_name/container_id
➜ ~ docker rmi image_name/image_id

docker关于容器(container)的基本操作

1
2
3
4
5
6
7
8
➜  ~ docker run -it centos:latest /bin/bash   # 启动一个容器
[root@b05eb7072750 /]# git --version # 这里命令行形式变了,表示已经进入了一个新环境
bash: git: command not found # 此时的容器中没有git
[root@b05eb7072750 /]# exit # 利用exit退出该容器
exit
➜ ~ docker ps -a # 查看docker中运行的程序(容器)
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b05eb7072750 centos:latest "/bin/bash" 2 minutes ago Exited (127) 7 seconds ago optimistic_golick

启动一个容器

1
➜  ~ docker run -it centos:latest /bin/bash

docker run --help查看参数: -i表示--interactive(Keep STDIN open even if not attached),-t表示--tty(Allocate a pseudo-TTY)。此时如果使用exit退出,则容器的状态处于Exit,而不是后台运行。如果想让容器一直运行,而不是停止,可以使用docker exec,退出后容器的状态为Up。

除了这两个参数之外,run命令还有很多其他参数。其中比较有用的是-d后台运行:

1
2
3
4
5
6
➜  ~ docker run centos:latest /bin/bash -c "while true; do echo hello; sleep 1; done"
hello
hello
...
➜ ~ docker run -d centos:latest /bin/bash -c "while true; do echo hello; sleep 1; done"
3cd99dbdfa1441f9aa4383e0668efd82a6b00a3e1d3e4044631f905558c0b3f8

第一条命令一直输出hello,第二条命令使用了-d参数,使这个容器处于后台运行的状态,不会对当前终端产生任何输出,所有的stdout都输出到log,可以使用docker logs container_name或者docker logs container_id查看。

1
$ docker run -it --rm ubuntu:14.04 bash
  • -it :这是两个参数,一个是 -i 交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
  • --rm :这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm 。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
  • ubuntu:14.04:这是指用 ubuntu:14.04 镜像为基础来启动容器。
  • bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是bash

查看docker中运行的容器

1
➜  ~ docker ps -a

启动、停止、重启容器

1
2
3
➜  ~ docker start container_name/container_id
➜ ~ docker stop container_name/container_id
➜ ~ docker restart container_name/container_id

后台启动一个容器后进入到这个容器

1
➜  ~ docker exec -it container_name/container_id bash # exit后状态仍为Up

删除容器

1
➜  ~ docker rm container_name/container_id

docker中关于仓库的基本操作

Docker官方维护了一个DockerHub的公共仓库,里边包含有很多平时用的较多的镜像。除了从上边下载镜像之外,我们也可以将自己自定义的镜像发布(push)到DockerHub上。

在镜像操作章节中,我们新建了一个sunnie/centos:git镜像。

注册

访问https://hub.docker.com/,如果没有账号,需要先注册一个(一般install的时候已经注册了)。

登录

利用命令docker login登录DockerHub,输入用户名、密码即可登录成功:

1
2
3
4
5
➜  ~ 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: sunnie1005
Password:
Login Succeeded

如果dockerhub用户名与镜像所属用户不一致

利用命令docker tag imageid name:tag进行修改

1
2
3
4
5
6
7
8
9
10
➜  ~ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sunnie/centos git bc5c77aa3805 About an hour ago 353MB
centos latest 75835a67d134 6 weeks ago 200MB
➜ ~ docker tag bc5c77aa3805 sunnie1005/centos:git
➜ ~ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sunnie/centos git bc5c77aa3805 About an hour ago 353MB
sunnie1005/centos git bc5c77aa3805 About an hour ago 353MB
centos latest 75835a67d134 6 weeks ago 200MB

push到自己的dockerhub

1
2
3
4
5
➜  ~ docker push sunnie1005/centos:git
The push refers to repository [docker.io/sunnie1005/centos]
4c49c38c758e: Pushed
f972d139738d: Mounted from library/centos
git: digest: sha256:87304ae2c52cfcfb560ea29db149cb93c8d0614d923aeca8d57637ecfd9a6e41 size: 741

登录到自己的dockerhub就可以查看到已经上传的镜像了。

别人从你的仓库中下载合适的镜像

1
➜  ~ docker pull sunnie1005/centos:git

镜像的更新

对应于镜像的两种创建方法,镜像的更新也有两种:

  • 创建容器之后做更改,之后commit生成镜像,然后push到仓库中。
  • 更新Dockerfile。在工作时一般建议这种方式,更简洁明了。

docker中仓库/镜像/容器的关系

从仓库(一般为DockerHub)下载(pull)一个镜像,Docker执行run方法得到一个容器,用户在容器里执行各种操作。Docker执行commit方法将一个容器转化为镜像。Docker利用login、push等命令将本地镜像推送(push)到仓库。其他机器或服务器上就可以使用该镜像去生成容器,进而运行相应的应用程序了。

练习

利用docker创建一个用于Flask开发的Python环境:

上边已经解释和练习了Docker的基本操作命令,下边以实例的形式完整走一遍流程。

我们创建一个用于Flask开发的Python环境,包含Git、Python3、Flask以及其他依赖包等。

完整命令如下:

1
2
3
4
5
➜  ~ docker pull centos
➜ ~ docker run -it centos:latest /bin/bash
# 此时进入容器,安装Python3、Git、Flask及其依赖包等,安装完成后exit退出
➜ ~ docker commit -m "Flask" -a "sunnie" container_id sunnie1005/flask:v1
➜ ~ docker push sunnie1005/flask:v1
谢谢小天使请我吃糖果
0%