一、构建镜像介绍

  • 我们可以定制属于自己的Docker镜像,然后将Docker镜像存储到存储库中
  • 构建镜像的方法有2种:
    • 使用docker commit命令
    • 使用docker build命令和Dockerfile文件
  • 不推荐使用docker commit命令,建议使用docker build命令(编写完Dockerfile然后使用docker build命令)
  • 备注:此处我们不是真正“创建”新镜像,而是对已有镜像进行修改构建。如果想要从0构建一个全新的镜像,可以参阅:Redirecting…

登录Docker Hub账号

  • Docker Hub:Docker Hub
  • 我们可以注册一个Docker Hub账号,然后将构建好的镜像推送到Docker Hub或者用户自己的私有Registry中。但是需要现在本地登录Docker Hub账号
  • 可以输入下面的命令登录到Docker Hub:
sudo docker login

  • 个人认证信息配置文件:登录完成之后会将认证信息保存起来以供以后使用,信息会保存到$HOME/.dockercfg文件中。从Docker 1.7.0开始,该文件变为了$HOME/.docker/config.json

  • 退出登录的命令如下:
sudo docker logout

 

  • 遇到的问题: 有一次新机器登录(CentOS),一直登不上去

  • 编辑/etc/resolv.conf文件,添加如下的内容:
nameserver 8.8.8.8
nameserver 8.8.4.4

  • 重启docker,再次登录成功:
sudo systemctl restart docker

 

二、使用commit命令创建镜像

  • 可以使用docker commit命令来提交一个新镜像
  • docker commit提交的只是创建容器的镜像与容器的当前状态之间有差异的部分,这使得该更新非常轻量

演示案例

  • 第一步:运行一个带有ubuntu镜像的容器
sudo docker run -i -t ubuntu /bin/bash

  • 第二步:然后我们在该ubuntu镜像系统中安装Apache软件包
# 更新,-yqq忽略所有提示信息
apt-get -yqq update

# 安装apache2服务器
apt-get -y install apache2

  • 第三步:现在我们在这个镜像中安装了Apache2的服务器,我们想把这个镜像的当前状态保存下来,这样就不必每次都创建一个新容器并在此安装了。可以执行下面的命令来提交指定的容器:
# 查看刚才那个容器的ID
sudo docker ps -a

# 提交定制容器,需要指定容器ID、镜像名
sudo docker commit f3f694f1fc97 jamtur01/apache2

  • 第四步:查看一下新创建的镜像
sudo docker images jamtur01/apache2

  • 第五步:可以通过dcoker inspect命令查看新创建的镜像的详细信息
sudo docker inspect jamtur01/apache2

  • 可以尝试着以上面创建的新镜像运行一个容器
sudo docker run -i -t jamtur01/apache2

演示案例

  • 在提交新定制的镜像时还可以指定更定的信息选项,例如:
    • -m:用来指定提交时的备注信息
    • -a:列出该镜像的作者信息
    • 并且下面的镜像还给其指定了一个webserver标签
sudo docker commit -m"A new custom image" -a"dongyusheng" f3f694f1fc97 jamtur01/apache2:webserver

  • 查看一下新创建的镜像
sudo docker images jamtur01/apache2:webserver

  • 可以通过dcoker inspect命令查看新创建的镜像的信息(作者和备注)
sudo docker inspect --format='{{ .Author }} {{ .Comment }}' jamtur01/apache2:webserver

  • 可以尝试着以上面创建的新镜像运行一个容器
sudo docker run -i -t jamtur01/apache2:webserver /bin/bash

  • 现在我们本机一共有下面这些容器,图中圈出来的是新创建的镜像的名称
sudo docker ps -a

三、使用Dockerfile构建镜像

  • 上面介绍了使用docker commit的方法来构建镜像,不推荐使用这种方法
  • 下面介绍“Dockerfile + docker build”来构建镜像,比较推荐使用这种方式构建镜像,因此这种方法构建镜像更具备可重复性、透明性以及幂等性
  • Dockfile:
    • 就是一个文件,基于DSL(Domain Specific Language)语法的指令来构建一个Docker镜像。关于Dockfile的指令文章下面介绍
    • Dockfile由一系列指令和参数组成。每条指令(例如FROM)都必须为大写字母,且后面要跟随一个参数
    • Dockfile会按照文件中的指令从上到下进行执行,每执行一条指令都会创建一个新的镜像层并对镜像进行提交。Docker大题上按照如下流程执行Dockfile中的指令:
      • Docker从基础镜像运行一个容器
      • 执行一条指令,对容器做出修改
      • 执行类似docker commit的操作,提交一个新的镜像层
      • Docker再基于刚提交的镜像运行一个新容器
      • 执行Dockfile中的下一条指令,直到所有指令都执行完毕
    • Dockfile也支持注释,注意以#开头
  • Dokerfile指令语法请参阅:核心篇,你必须要会的Dockerfile指令详解_董哥的黑板报-CSDN博客
  • 下面介绍一个演示案例

第一步:创建一个Dockfile文件

  • 第一步:自己建立一个目录(取名随意),然后进入目录创建一个文件名为Dockfile的文件(必须名为Dockerfile)
mkdir ~/static_web

cd ~/static_web

touch Dockerfile

  • 此处我们创建了一个名为static_web的目录来保存Dockfile,这个目录就是我们的构建环境,Docker称此环境为上下文或者构建上下文。Docker会在构建镜像时将构建上下文和该上下文的文件和目录上传到Docker守护进程。这样Docker守护进程就能直接访问用户想在镜像中存储的任何代码、文件或者其他数据
  • 第二步:编写上面创建的Dockfile文件,内容如下:
# Version: 0.0.1
FROM ubuntu:16.04
MAINTAINER dongyusheng "1286550014@qq.com"
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' > /usr/share/nginx/html/index.html
EXPOSE 80

  • 下面介绍上面Dockerfile文件使用到的指令
  • FROM指令:
    • 每个Dockerfile的第一条指令必须是FROM
    • 该指令指定了一个已经存在的镜像,后续指令都将基于该镜像进行,这个镜像备被称为基础镜像。在此演示案例中,我们以ubuntu:16.04作为基础镜像
  • MAINTRINER指令:该指令告诉Docker该镜像的作者是谁,以及作者的电子邮箱地址
  • RUN指令:
    • RUN指令就是来指定在镜像中执行的命令。例如在这个演示案例中,我们执行了两条命令
    • 备注:默认情况下,RUN指令会在shell里使用命令包装器/bin/sh -c来执行。如果在一个不支持shell的平台上运行或者不希望在shell中运行(比如避免shell字符串篡改),也可以使用exec格式的RUN指令。演示案例如下:
# 用一个数组来指定要运行的命令和传递给该命令的每个参数
RUN { "apt-get", " install", "-y", "nginx" }
  • EXPOSE指令:
    • 该指令告诉Docker该容器内的应用程序将会使用容器的指定端口
    • 这并不意味着可以自动访问任意容器运行中的服务的端口(这里为80)
    • 处于安全的原因,Docker并不会自动打开该端口,而是需要用户在使用docker run运行容器时来指定需要打开哪些端口。下面我们会看到如何从这一镜像创建一个新容器
    • 可以指定多个EXPOSE指令来向外部公开多个端口
    • 备注:Docker也可以使用EXPOSE指令来帮助将多个容器链接,我们可以在“在测试中使用Docker”看到相关内容。用户可以在运行时以docker run命令通过--expose选项来指定对外部公开的端口

第二步:基于Dockerfile构建新镜像

  • 上面我们的Dockerfile已经书写好了,现在执行下面的命令来构建一个新镜像:
    • -t:新镜像的名称
    • .:表示Dockerfile文件的路径,此处我们在当前目录,因此就用了.表示
sudo docker build -t="jamtur01/static_web" .

  • 结果分析:
    • 从上图可以看到命令输入之后,会将构建上下文传到Docker守护进程中(第一行)。如果在构建上下文的根目录存在以.dockerignore命令的文件的话,那么该文件内容会按行进行分割,每一行都是一条文件过滤匹配模式。这非常像.gitignore文件,该文件用来设置哪些文件不会被当做构建上下文的一部分,因此可以防止它们被上传到Docker守护进程中去。该文件中的匹配模式的匹配规则采用了Go语言中的filepath(http://golang.org/pkg/path/filepath/#Match
    • Dockerfile中的每条指令都会被顺序执行,每一条指令对应一条“Step”,然后返回该指令的新镜像的ID,最后所有指令都执行完成之后,最终镜像的ID为b22dcf7f0505
  • 附加:如果不设置标签,那么Docker会自动为镜像设置一个latest标签。我们也可以为镜像设置标签,例如下面将其标签设置为v1
sudo docker build -t="jamtur01/static_web":v1 .
  •  附加:docker build最后一个参数为Dockerfile文件的路径,除了在本地目录中查找,还可以指定一个Git仓库的源地址来告诉Dockerfile文件的位置。例如:
sudo docker build -t="jamtur01/static_web":v1 git@github.com:jamtur01/docker-static_web
  • -f选项:自Docker 1.5.0开始,可以通过-f选项指定一个区别于标准Dockerfile的构建源的位置。例如,下面的命令将当前路径下的file文件作为Dockerfile文件
sudo docker build -t="jamtur01/static_web":v1 ./file

第三步:查看构建历史(docker history)

  • 现在可以看一下新构建的新镜像,可以使用以前介绍过的docker images命令来查看,如下所示:可以看到镜像ID与上面最后生成的镜像ID相同
sudo docker images jamtur01/static_web

  • 如果想要深入镜像是如何构建出来的,可以使用docker history命令,如下所示:
    • 从下面的结果可以看到新构建的jamtur01/static_web镜像的每一层,以及创建这些曾的Dockerfile指令
sudo docker history jamtur01/static_web

第四步:从新镜像启动容器

  • 我们在镜像中安装了Nginx,现在我们可以用该镜像来运行一个容器,命令如下:
    • -d:这个参数在前面文章已经介绍过了,让该容器以守护进程的方式在宿主机中运行
    • -p:运行容器时,将容器的80端口映射到宿主机的一个随机端口上
    • 最后我们运行了nginx程序,并且将其配置文件的daemon参数改为了off,让Nginx在容器中已非守护进程的方式运行程序
sudo docker run -d -p 80 --name static_web jamtur01/static_web nginx -g "daemon off;"

  • 运行一个容器时,Docker可以通过两种方法来在宿主机上分配端口:
    • Docker可以在宿主机上随机选择一个位于32768~61000的一个比较大的端口号来映射到容器中的80端口上(上面的演示案例就是的,宿主机随意选择一个端口映射到Docker容器的80端口上)
    • 可以在Docker宿主机中指定一个具体的端口号来映射到容器的80端口上(下面有演示案例)
  • 现在我们可以来查看容器的端口分配情况,如下所示,宿主机的32768端口映射到了Docker容器的80端口上
sudo docker ps -l

  • 现在我们可以该地址和端口来访问容器内部的nginx服务器。例如:
curl localhost:32768

  • 我们也可以docker port命令来查看容器的端口的映射情况,例如下面查看容器80端口的映射情况:
# 通过容器ID
sudo docker port 5c194f6d65b8 80

# 或者通过容器名称查看
sudo docker port static_web 80

附加:映射指定地址和端口

  • 在第四步中,我们将宿主机的随机端口映射到了容器的80端口上,我们还可以将容器的端口映射到主机的指定端口上
  • 例如,下面是将容器的80端口映射到本地宿主机的8080端口上:
sudo docker run -d -p 8080:80 --name static_web jamtur01/static_web nginx -g "daemon off;"
  • 也可以将容器的端口映射到宿主机指定的地址和端口上,例如下面将容器的80端口映射到本地宿主机127.0.0.1接口的80端口上
sudo docker run -d -p 127.0.0.1:80:80 --name static_web jamtur01/static_web nginx -g "daemon off;"
  • 还可以将容器的端口映射到宿主机指定接口的随机端口上,例如下面将容器的80端口映射到本地宿主机127.0.0.1接口的随机端口上
sudo docker run -d -p 127.0.0.1:80 --name static_web jamtur01/static_web nginx -g "daemon off;"
  • 也可以在端口绑定时使用/udp后缀来指定UDP端口
  • -P选项:
    • 该选项可以用来对外公开在Dockerfile中通过EXPOSE指令公开的所有端口
    • 例如,下面容器运行时,会将"jamtur01/static_web"镜像的Dockerfile中EXPOSE公开的端口映射到映射到宿主机的一个随机端口上
sudo docker run -d -P --name static_web jamtur01/static_web nginx -g "daemon off;"

四、镜像的推送(push)

  • 镜像构建完成之后,可以把镜像上传到Docker Hub上去,这样其他人就能使用这个镜像了。比如,我们可以在阻止内共享这个镜像,或者完全公开这个镜像
  • 备注:Docker Hub也提供了对私有仓库的支持,这是一个需要付费的功能,用户可以将镜像存储到私有仓库中,这样只有用户或者任何与用户共享这个私有仓库的人才能访问该镜像。这样用户就可以将机密信息或者代码放到私有镜像中,不必担心被公开访问了

演示案例

  • 例如上面我们将构建好的镜像推送到Docker Hub上,命令如下:
sudo docker push jamtur01/static_web

  • 可以看到发生了错误,推送失败了,原因是:该镜像会推送到jamtue01/static_web仓库中,但是我们没有这个权限

  • 解决方法:更改仓库名,命令如下:我自己的仓库名为dongyusheng/static_web
# 更改仓库名
docker tag jamtur01/static_web dongyusheng/static_web

  • 接着重新进行推送,成功:如果dongyusheng/static_web仓库不存在,那么会自动在dongyusheng这个用户下建立dongyusheng/static_web仓库
sudo docker push dongyusheng/static_web

  • 推送完成之后,在自己的Docker Hub中可以看到该仓库和镜像,并且默认的情况下,镜像的TAG为latest

五、自动构建

  • 除了从命令行构建和推送镜像,Docker Hub还允许我们定义自动构建(Automated Builds)
  • 实现方式:
    • 为了使用自动构建,我们只需要将Github或BitBucket中含有Dockerfile文件的仓库连接到Docker Hub即可
    • 向Github或BitBucket仓库推送代码时,将会触发一次镜像构建活动并创建一个新镜像
  • 在之前该工作机制被称为可信构建(Trusted Build)
  • 备注:自动构建同样支持私有Github和BitBucket仓库

演示案例

  • 待续,具体参阅《第一本Docker书》P90

六、删除镜像(rmi)

  • 如果不再需要一个镜像了,可以用docker rmi命令将其删除
  • 例如,下面删除上面我们构建的那个dongyusheng/static_web镜像。命令如下:
sudo docker rmi dongyusheng/static_web

  • 上面只是将本地的镜像删除了,但是上面我们已经将其上传到了Docker Hub仓库中,下次运行时仍然可以从仓库中进行下载
  • 如果想要将Docker Hub中的镜像也删除,可以进入Docker Hub进行删除。如下图所示,进入仓库,点击“Manage Repository”,然后在“Settings”中点击“Delete repository”删除整个仓库

附加:删除多个/全部镜像

  • 删除多个镜像,如下所示:
sudo docker rmi dongyusheng/static_web dongyusheng/apache2
  • 删除全部镜像,与docker rm一样,Docker不支持全部删除的命令,因此可以按照下面的形式进行删除
sudo docker rmi 'docker images -a -q'

  • 我是小董,V公众点击"笔记白嫖"解锁更多【Docker】资料内容。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐