02 . DockerFile构建镜像和Docker仓库
为什么用DockerFile
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
说dockerfile之前我们先说一下dockercommit
利用commit理解镜像构成
注意: docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现 场等。但是,不要使用 docker commit 定制镜像,定制镜像应该使用 Dockerfile 来完成
镜像是容器的基础,每次执行 docker run 的时候都会指定哪个镜像作为容器运行的基础。
我们之前所使用的镜像都是docker hub等网站上的,直接使用这些镜像可以满足一定的需求,而当这些镜像无法直接满足需求时候,我们需要定制这些镜像.之前有说过,镜像是多层存储,每一层是在前一层的基础上进行的修改,而容器也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层.
定制一个Web服务器
docker pull daocloud.io/library/nginx
docker run --name webserver -d -p 80:80 daocloud.io/library/nginx
# 我们访问这个web服务,会看到nginx的默认欢迎界面,假如我不喜欢这个界面,想看到欢迎docker的文字,可以exec
curl 119.3.255.91
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
docker exec -it webserver bash
root@6e63ab0d5109:/# echo '<h1>Hello,Docker!</h1>'> /usr/share/nginx/html/index.html
root@6e63ab0d5109:/# exit
exit
# 我们在访问时候就发现内容改变了
curl 119.3.255.91
<h1>Hello,Docker!</h1>
# 我们修改了容器的文件,就是改动了容器的存储层,我们可以通过docker diff命令看到具体变动
docker diff webserver
C /root
A /root/.bash_history
C /run
A /run/nginx.pid
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
A /var/cache/nginx/client_temp
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
现在我们定制好了变化,我们希望将其保存下来形成镜像.
要知道,当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被 记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保 存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新 的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化(类似于虚拟机的快照)。
docker commit 的语法格式为:
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
# 我们可以用下面的命令将容器保存为镜像
docker commit -a="youmen <18621048481@163.com>" -m="edited nginx Default page" webserver nginx:v2
* -m 提交的描述信息
* -a 指定镜像作者
* webserver为你要给那个容器做成镜像的那个容器名字或者ID
* nginx:v2是形成的新镜像名
# 接下来我们基于这个创建好的新镜像再启动一个容器.
docker run --name web2 -d -p 81:80 nginx:v2
curl `cat ip.txt`:81
<h1>Hello,Docker!</h1>
# 就这样,我们完成了一次基于容器构建定制镜像的操作.
慎用docker commit
使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境 中并不会这样使用。
首先,如果仔细观察之前的 docker diff webserver 的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加 了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添 加进来,如果不小心清理,将会导致镜像极为臃肿。
此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑 箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根 本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽 然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。 这种黑箱镜像的维护工作是非常痛苦的。
而且,回顾之前提及的镜像所使用的分层存储的概念,除当前层外,之前的每一层都是不会 发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改 动上一层。如果使用 docker commit 制作镜像,以及后期修改的话,每一次修改都会让镜像 更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使 根本无法访问到。这会让镜像更加臃肿.
使用Dockerfile定制镜像
从刚才的 docker commit 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所 添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚 本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问 题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层, 因此每一条指令的内容,就是描述该层应当如何构建。
还以之前定制 nginx 镜像为例,这次我们使用 Dockerfile 来定制。
在一个空白目录中,建立一个文本文件,并命名为 Dockerfile :
cat DockerFile
mkdir mynginx
cd mynginx
vim Dockerfile
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
FROM指定基础镜像
FROM为指定基础镜像,我们定制镜像,肯定要以一个镜像为基础,在其上做定制,而FROM就是指定基础镜像,因此一个Dockerfile中FROM是必备的命令,并且必须是第一条指令.
在Docker Store上有非常多高质量的官方镜像,有可以直接拿来使用的服务类镜像,如nginx,redis,mysql,mongo,tomcat等,也有方便开发、构建、运行各种语言的镜像,如node,openjdk,python,ruby,golang等,可以在其中寻找一个最符合我们的镜像为基础镜像进行定制.
如果还没有找到对应服务的镜像,官方镜像中提供了一些更为基础的操作系统镜像,如ubuntu,debian,centos,fedora,alpine等,这些操作系统的软件库为我们提供了更广阔的扩展空间.
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像 是虚拟的概念,并不实际存在,它表示一个空白的镜像。
如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作 为镜像第一层开始存在。 不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm 、 coreos/etcd 。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时 支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小 巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
RUN 执行命令
RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最 常用的指令之一。其格式有两种:
shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中 的 RUN 指令就是这种格式。
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
exec 格式: RUN ["可执行文件", "参数1", "参数2"] ,这更像是函数调用中的格式。 既然 RUN 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个 命令对应一个 RUN 呢?比如这样:
cat Dockerfile
FROM debian:jessie
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install
之前说过,Dockerfile 中每一个指令都会建立一层, RUN 也不例外。每一个 RUN 的行为, 就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束 后, commit 这一层的修改,构成新的镜像。
而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东 西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常 多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 这是很多初学 Docker 的人常 犯的一个错误。
Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。因此上面Dockerfile可以这样写:
cat DockerFile
FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \ && apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
&& mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立 很多层,这只是一层的事情。因此,这里没有使用很多个 RUN 对一一对应不同的命令,而是 仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每 一层该如何构建。
并且,这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方 式,以及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障 更为容易,这是一个比较好的习惯。
此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的 软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我 们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。 因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清 理掉。
很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要 清理掉无关文件。
构建镜像
在之前Dockerfile文件所在目录执行
cat DockerFile
mkdir mynginx
cd mynginx
# 我们继续编辑一下DockerFile
vim DockerFile
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
docker build -t nginx:v3 .
# 最后的.表示本文执行的上下文路径
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM nginx
---> 231d40e811cd
Step 2/2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 3a439666c4da
Removing intermediate container 3a439666c4da
---> 78990dc4a6a6
Successfully built 78990dc4a6a6
Successfully tagged nginx:v3
从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 Step 2 中,如同我们之前 所说的那样, RUN 指令启动了一个容器231d40e811cd,执行了所要求的命令,并最后提交 了这一层 3a439666c4da ,随后删除了所用到的这个容器78990dc4a6a6。
docker build [选项] <上下文路径/URL/->
构建成功后我们可以跟nginx:v2那样运行这个镜像,结果一样.
这只是默认行为,实际上 Dockerfile 的文件名并不要求必须为 Dockerfile ,而且并不要求 必须位于上下文目录中,比如可以用 -f ../Dockerfile.php 参数指定某个文件作为 Dockerfile
上下文路径
是指在docker构建镜像,有时候想要使用本机的文件(比如复制),docker build命令知道这个路径后,会将这个路径下所有内容打包.
由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。
如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。
注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
Docker build的用法
直接用git repo 进行构建
yum -y install git
docker build https://github.com/twang2218/gitlab-ce-zh.git:8.14 docker
build https://github.com/twang2218/gitlab-ce-zh.git\#:8.14
Sending build context to Docker daemon 2.048 kB Step 1 :
FROM gitlab/gitlab-ce:8.14.0-ce.0 8.14.0-ce.0:
Pulling from gitlab/gitlab-ce
这行命令指定了构建所需的Git repo,并且指定默认的master分支,构建目录为/8.14/,然后Docker就会自己去git clone这个项目,切换到指定分支,并进入到指定目录后开始构建.
用给定的tar压缩包构建
docker build http://server/context.tar.gz
# 如果所给出的 URL 不是个 Git repo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,
# 并自动解压缩,以其作为上下文,开始构建。
从标准输入中读取Dockerfile进行构建
docker build - < Dockerfile
or
cat Dockerfile | docker build -
如果标准输入传入的是文本文件,则将其视为 Dockerfile ,并开始构建。这种形式由于直接 从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本 地文件 COPY 进镜像之类的事情。
从标准输入中读取上下文压缩包进行构建
docker build - < context.tar.gz
# 如果发现标准输入的文件格式是gzip、bzip2、以及xz的话,将会使其为上下文压缩包,直接将其展开,
# 将里面视为上下文,并开始构建.
Dockerfile指令详解
FROM 和 RUN 指令的作用
FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。
RUN:用于执行后面跟着的命令行命令。有以下俩种格式
shell格式
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。
exec格式
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
注意: Dockerfile的指令是每执行一次都会在docker上新建一层,所以过多无意义的层,会造成镜像膨胀过大,上面提到过,可以用&&符号链接命令,这样执行后,只会创建一层镜像
COPY复制文件
# 格式:
# COPY <源路径>...<目标路径>
COPY ["<源路径1>",..."<目标路径>"]
# 和RUN指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用.
COPY指令将从构建上下文目录中<源路径>的文件/目录复制到新的一层的镜像内的<目标路径> 位置,比如.
COPY package.json /usr/src/app/
# <源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足Go的filepath.Match规则,如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
# <目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径.工作目录可以 用 WORKDIR 指令来指定).
# 目标路径不需要事先创建,如果目录不存在会在复制文件前先行 创建缺失目录
# 此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执 行权限、文件变更时间等.
# 这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
ADD
ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:
ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
CMD
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
- CMD 在docker run 时运行。
- RUN 是在 docker build。
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
格式:
CMD <shell 命令>
CMD ["<可执行文件或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
ENTRYPOINT
类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
但是, 如果运行 docker run 时使用了 --entrypoint 选项,此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
格式:
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。
示例:
假设已通过 Dockerfile 构建了 nginx:test 镜像:
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
不传参运行
docker run nginx:test
# 容器内会默认运行以下命令,启动主进程
nginx -c etc/nginx/nginx.conf
传参运行
docker run nginx:test -c /etc/nginx/new.conf
# 容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
ENV
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
ARG
构建参数,与 ENV 作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。
格式:
ARG <参数名>[=<默认值>]
VOLUME
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
作用:
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
- 避免容器不断变大。
格式
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
# 在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。
EXPOSE
仅仅只是声明端口。
作用:
- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
- 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
格式:
EXPOSE <端口1> [<端口2>...]
WORKDIR
指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。
docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。
格式:
WORKDIR <工作目录路径>
USER
用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。
格式:
USER <用户名>[:<用户组>]
HEALTHCHECK
用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
格式:
# HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
# HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
# HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
ONBUILD
用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这是执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
格式
ONBUILD <其它指令>
Docker仓库
访问仓库
仓库(Repository)是集中存放镜像的地方
一个容易混淆的概念是注册服务器( Registry )。实际上注册服务器是管理仓库的具体服务 器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以 被认为是一个具体的项目或目录。例如对于仓库地址 dl.dockerpool.com/ubuntu 来 说, dl.dockerpool.com 是注册服务器地址, Ubuntu 是仓库名。
大部分时候,并不需要这两者的概念.
Docker Hub
目前Docker官方维护了一个公共仓库Docker Hub,其中包括了数量超过15000的镜像,大部分需求都可以通过在Docker Hub中直接下载镜像来实现.
注册,可以直接在https://cloud.docker.com免费注册一个Docker Hub账号.
登陆
可以通过执行docker login 命令交互式的输入用户名及密码来完成命令行界面登陆Docker Hub。docker logout退出登陆.
拉取镜像
可以通过docker search命令来查找官方仓库中的镜像,并利用docker pull命令将他下载到本地.
docker search centos
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
centos The official build of CentOS. 5717 [OK]
ansible/centos7-ansible Ansible on Centos7 126 [OK]
jdeathe/centos-ssh OpenSSH / Supervisor / EPEL/IUS/SCL Repos - … 114 [OK]
consol/centos-xfce-vnc Centos container with "headless" VNC session… 101 [OK]
可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、收藏数(表示该镜像的受关注程度),是否官方创建、是否自动创建.
根据是否是官方提供,可将镜像资源分为两类.
一种是类似centos这样的镜像,被称为基础镜像或根镜像,这些基础镜像由Docker公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字.
还有一种类型,比如tianon/centos镜像,他是由Docker的用户创建并维护的,往往带有用户名称前缀,可以通过前缀username/来指定某个用户提供的镜像,比如tianon用户.
另外,在查找的时候可以通过--filter=stars=N参数可以显示收藏数量为N以上的镜像.
推送镜像
用户可以在登陆后通过docker push命令将自己的镜像推送到Docker Hub。
以下命令的flyingdreams请替换为你的Docker账号用户名.
docker login # 登陆自己的账号
docker tag nginx:v2 flyingdreams/nginx:v2
docker push flyingdreams/nginx:v2
自动创建
自动创建(Automated Builds)功能对于需要经常升级镜像内程序来说,十分方便.
有时候,用户创建了镜像,安装了某个软件,如果程序发布新版本则需要手动更新镜像.
而自动创建允许用户Docker Hub指定跟踪一个目标网站(目前支持GitHub或BitBucket)上的项目,一旦项目发生新的提交或者创建新的标签(tag),Docker Hub会自动构建镜像并推送到Docker Hub中.
要配置自动创建,包括如下的步骤:
# 1. 创建并登陆Docker Hub,以及目标网站:
# 2. 在目标网站中连接账户到Docker Hub;
# 3. 在Docker Hub中配置一个自动连接:
# 4. 选取一个目标网站中的项目(需要含Dockerfile)和分支
# 5. 指定Dockerfile的位置,并提交创建.
# 之后,可以在Docker Hub的自动创建页面中跟踪每次创建的状态.
私有仓库docker-registry
有时候使用Docker Hub这样的公共仓库可能不安全,用户可以创建一个本地仓库供私人使用.
通过官方提供的私有仓库镜像
registry
来搭建私有仓库。通过 humpback 快速搭建轻量级的Docker容器云管理平台此外还有像Harbor,rancher等私有仓库。
安装运行docker-registry
# 自定义存储位置$HOME,一般是root
docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v $HOME/_docker/registry:/var/lib/registry \
registry:2.6
# 从官方仓库拉去一个镜像
docker pull nginx
docker tag nginx:latest 47.92.24.137:5000/test_nginx:latest
# 在推送到的时候报错误,默认是使用`https`提交,这个搭建的默认使用的是 `http`,解决方法两个:
# 创建一个https映射
# 将仓库地址加入到不安全的仓库列表中
# 我们使用第二种方法,加入到不安全的仓库列表中,修改docker配置文件
# `vi /etc/docker/daemon.json` 添加 `insecure-registries`配置信息
docker push 47.92.24.137:5000/test_nginx:latest
The push refers to repository [47.92.24.137:5000/test_nginx]
Get https://47.92.24.137:5000/v2/: http: server gave HTTP response to HTTPS client
cat /etc/docker/daemon.json
{
"insecure-registries":[
"47.92.24.137:5000"
]
}
systemctl stop docker
systemctl daemon-reload
systemctl start docker
# 推送到私有仓库中
docker push 47.92.24.137:5000/test_nginx:latest
ls /root/_docker/registry/docker/registry/v2/repositories/test_nginx/
_layers _manifests _uploads
安装运行harbor(http方式)
Harbor(港口,港湾)是一个用于存储和分发Docker镜像的企业级Registry服务器
Harbor 可帮助用户迅速搭建企业级的 Registry 服务, 它提供了管理图形界面, 基于角色的访问控制 ( Role Based Access Control), 镜像远程复制 (同步), AD/LDAP 集成, 以及审计日志等企业用户需求的功能, 同时还原生支持中文, 深受中国用户的喜爱;
Harbor相比于Registry有以下优势
- 提供分层传输机制,优化网络传输,Docker镜像是分层的,而如果每次传输都使用全量文件,显然效率不是很高,必须提供识别分层传输的机制,以层的UUID为标识,确认传输的对象.
- 提供WEB界面,优化用户体验,只用镜像的名字来进行上传下载显然很不方便,需要有一个用户界面可以支持登录,搜索功能,包括区分公有,私有镜像
- 支持水平扩展集群,当有用户对镜像的上传下载操作集中在某服务器,需要对相应的访问压力工作分解.
- 良好的安全机制,企业中的开发团队有很多不同的职位,对于不通的职位人员,分配不通的权限,具有更好的安全性.
安装harbor
是
VMware
公司开源了企业级Registry
项目, 其的目标是帮助用户迅速搭建一个企业级的Docker registry
服务。由于 Harbor 是基于 Docker Registry V2 版本,所以 docker 版本必须
>=1.10.0
docker-compose>=1.6.0
下载最新版 Docker Compose
curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
下载最新版Docker Harbor
wget https://github.com/goharbor/harbor/releases/download/v1.10.0-rc1/harbor-offline-installer-v1.10.0-rc1.tgz
# 对二进制文件应用可执行权限:
sudo chmod +x /usr/local/bin/docker-compose
# 测试是否安装成功
docker-compose --version
# 按照上面给的docker harbor地址,下载离线安装包
tar xvf harbor-offline-installer-v1.8.1.tgz -C /usr/local/
vim /usr/local/harbor/harbor.yml
hostname: 47.92.24.137
# 运行安装脚本
./install.sh
[Step 0]: checking installation environment ...
Note: docker version: 19.03.4
Note: docker-compose version: 1.22.0
[Step 1]: loading Harbor images ...
Loaded image: goharbor/harbor-core:v1.8.1
Loaded image: goharbor/harbor-registryctl:v1.8.1
Loaded image: goharbor/redis-photon:v1.8.1
Loaded image: goharbor/notary-server-photon:v0.6.1-v1.8.1
Loaded image: goharbor/chartmuseum-photon:v0.8.1-v1.8.1
Loaded image: goharbor/harbor-db:v1.8.1
Loaded image: goharbor/harbor-jobservice:v1.8.1
Loaded image: goharbor/nginx-photon:v1.8.1
Loaded image: goharbor/registry-photon:v2.7.1-patch-2819-v1.8.1
Loaded image: goharbor/harbor-migrator:v1.8.1
Loaded image: goharbor/prepare:v1.8.1
Loaded image: goharbor/harbor-portal:v1.8.1
Loaded image: goharbor/harbor-log:v1.8.1
Loaded image: goharbor/notary-signer-photon:v0.6.1-v1.8.1
Loaded image: goharbor/clair-photon:v2.0.8-v1.8.1
[Step 2]: preparing environment ...
prepare base dir is set to /usr/local/harbor
Generated configuration file: /config/log/logrotate.conf
Generated configuration file: /config/nginx/nginx.conf
Generated configuration file: /config/core/env
Generated configuration file: /config/core/app.conf
Generated configuration file: /config/registry/config.yml
Generated configuration file: /config/registryctl/env
Generated configuration file: /config/db/env
Generated configuration file: /config/jobservice/env
Generated configuration file: /config/jobservice/config.yml
Generated and saved secret to file: /secret/keys/secretkey
Generated certificate, key file:/secret/core/private_key.pem, cert file:/secret/registry/root.crt
Generated configuration file: /compose_location/docker-compose.yml
Clean up the input dir
[Step 3]: starting Harbor ...
✔ ----Harbor has been installed and started successfully.----
Now you should be able to visit the admin portal at http://47.92.24.137.
For more details, please visit https://github.com/goharbor/harbor
接下来我们可以直接浏览器访问配置文件定义的IP或者域名加端口
修改harbor端口
# 因为harbor默认端口是80,而大多数时候是不希望使用80端口,修改方法如下
# vim harbor.yml
# 找到port选项修改端口,然后执行./install 就会使用配置文件端口
# 还有一种情况就是更改已有harbor的配置
vim docker-compose.yml
dns_search: .
ports:
- 99:80
auth:
token:
issuer: harbor-token-issuer
realm: http://47.92.24.137:99/service/token
rootcertbundle: /etc/registry/root.crt
service: harbor-registry
docker-compose down -v
docker-compose up -d
使用harbor
为了体现出效果,建议使用非harbor的另一台机器
# 镜像推送
docker login 47.92.24.137:99 -u admin -p youmen
vim /etc/docker/daemon.jsonyml
{
"insecure-registries":["47.92.24.137"]
}
systemctl daemon-reload
systemctl restart docker
# 因为docker默认使用的是https协议,而搭建harbor是http提供服务的,
# 所以要配置可信任,或者强制docker login和docker push 走http的80端口,而不是443端口.
docker tag nginx:latest 47.92.24.137:99/library/nginx:latest
docker push 47.92.24.137:99/library/nginx:latest
安装harbor(https方式)
DNS服务器安装dnsmasq
yum -y install dnsmasq
mkdir -p /data/ssl && cd /data/ssl
vim harbor.cfg
hostname = harbor.youmen.com
ui_url_protocol = https
db_password = root123
harbor_admin_password = baiyongjie
ssl_cert = /usr/local/harbor/cert/harbor.youmen.com.crt
ssl_cert_key = /usr/local/harbor/cert/harbor.youmen.com.key
# grep -Ev '#|^$' harbor.yml
hostname: harbor.youmen.com # 本机外网IP或域名,该地址供用户通过UI进行访问,不要使用127.0.0.1
https: # 用户访问私仓时使用的协议,默认时http,配置成https
port: 443 # https使用的端口
certificate: /usr/local/harbor/cert/harbor.youmen.com.crt # 设置证书文件路径
private_key: /usr/local/harbor/cert/harbor.youmen.com.key # 设置证书密钥文件路径
harbor_admin_password: youmen # harbor的管理员账户密码
database:
password: root123 # 指定mysql数据库管理员密码
data_volume: /data # image存储目录
clair:
updaters_interval: 12
http_proxy:
https_proxy:
no_proxy: 127.0.0.1,localhost,core,registry
jobservice:
max_job_workers: 10
chart:
absolute_url: disabled
log:
level: info
rotate_count: 50
rotate_size: 200M
location: /var/log/harbor
_version: 1.8.0
生成harbor证书
mkdir /usr/local/harbor/cert/
cd /usr/local/harbor/cert/
#生成根证书
openssl req -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 3650 -out ca.crt -subj "/C=CN/L=Shanghai/O=harbor/CN=harbor-registry"
#生成一个证书签名, 设置访问域名为 harbor.baiyongjie.com
openssl req -newkey rsa:4096 -nodes -sha256 -keyout harbor.youmen.com.key -out server.csr -subj "/C=CN/L=Shanghai/O=harbor/CN=harbor.youmen.com"
#生成主机证书
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out harbor.youmen.com.crt
# 接下来执行自动安装脚本即可.
添加本地解析到hosts
#echo "192.168.1.155 harbor.baiyongjie.com" >> /etc/hosts
#cd /usr/local/harbor
# ./install.sh
官方建议不要在Harbor上启用https,而是将Harbor放置到一个SLB的后边,配置SLB的端口转发进行访问,或者再装一个Nginx,进行Nginx的端口转发.
如果想做一个HA方案的话,可以按照如下方式构建一个(主从模式个人感觉很不靠谱
负载均衡同时还要承担检查的任务,而Redis用于数据的缓存和消息队列的实现,Mysql存储用户信息和仓库信息,云存储用来存储Docker镜像.