文章目录
通过docker之初见 和 docker之基石—镜像中体验了通过 docker 来使用tomcat
和mysql
,并了解了镜像的构成,那么有个问题,我们以前都是通过官网去下载安装包==>解压到指定目录==>修改配置文件==>启动使用,现在使用 docker 只要pull
一下镜像,run
一下就可以启动,如此简单,那么这个镜像包的Dockerfile
是怎么定义?
官方Dockerfile
首先来看下 docker 的官方镜像的代码仓库https://github.com/docker-library/official-images,在这个仓库里有的 library 目录里找到一个 mysql 的文件,里面的内容是这样的。
# this file is generated via https://github.com/docker-library/mysql/blob/ff627942605ce08a32fa527993337df0e9856760/generate-stackbrew-library.sh
Maintainers: Tianon Gravi <admwiggin@gmail.com> (@tianon),
Joseph Ferguson <yosifkit@gmail.com> (@yosifkit)
GitRepo: https://github.com/docker-library/mysql.git
Tags: 8.0.20, 8.0, 8, latest
GitCommit: bc6e37a2bed792b1c4fc6ab1ec3ce316e6a5f061
Directory: 8.0
Tags: 5.7.30, 5.7, 5
GitCommit: bc6e37a2bed792b1c4fc6ab1ec3ce316e6a5f061
Directory: 5.7
Tags: 5.6.48, 5.6
GitCommit: bc6e37a2bed792b1c4fc6ab1ec3ce316e6a5f061
Directory: 5.6
第5
行的代码告诉我们, mysql 镜像仓库的代码在 https://github.com/docker-library/mysql.git ,打开看下发现有好几个版本,这里打开5.7
的版本来看下。
发现有一个叫Dockerfile
的文件,应该就是生成 mysql
镜像的配置了,如果拿到这一份配置,是不是自己也可以生成一个镜像呢?
按照这个想法,来尝试一下。
# 创建一个空白目录,并进入
mkdir create-mysql-image && cd create-mysql-image
# 通过vi将 mysql 5.7 的 Dockerfile 内容保存到本地,并将文件名保存为 Dockerfile
vi Dockerfile
# 使用 docker build 命令进行镜像构建
# -t 指定镜像的名称为mysql,并自定tag为custom
# 需要注意的是最后一个点,是在指定上下文目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
docker build -t mysql:custom .
等待了几分钟发现报错了,报错的内容为:
Step 14/18 : COPY docker-entrypoint.sh /usr/local/bin/
COPY failed: stat /var/lib/docker/tmp/docker-builder874622285/docker-entrypoint.sh: no such file or directory
脚本执行的过程需要复制一个名为docker-entrypoint.sh
的文件,所以将 github 上的[docker-entrypoint.sh](https://github.com/docker-library/mysql/blob/master/5.7/docker-entrypoint.sh)
也保存下来,然后执行docker build -t mysql:custom .
,等待打包结束。
# 查看是否存在 mysql:custom
docker image ls
#REPOSITORY TAG IMAGE ID CREATED SIZE
#mysql custom 129011b1e809 14 seconds ago 448MB
#debian buster-slim 108d75da320f 2 weeks ago 69.2MB
果然有了。
使用容器创建镜像
# 创建一个nginx 容器
[vagrant@nexus3 ~]$ docker run -d --name my-nginx -p 3000:80 nginx
5535e66541d0facb4475717f1788c3839ebae2a26f9f394eb8cf7033ea0944fd
# 访问容器
[vagrant@nexus3 ~]$ curl localhost:3000
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
# body {
# width: 35em;
# margin: 0 auto;
# font-family: Tahoma, Verdana, Arial, sans-serif;
# }
# </style>
# </head>
# <body>
# <h1>Welcome to nginx!</h1>
# <p>If you see this page, the nginx web server is successfully installed and
# working. Further configuration is required.</p>
#
# <p>For online documentation and support please refer to
# <a href="http://nginx.org/">nginx.org</a>.<br/>
# Commercial support is available at
# <a href="http://nginx.com/">nginx.com</a>.</p>
#
# <p><em>Thank you for using nginx.</em></p>
# </body>
# </html>
# 进入容器
[vagrant@nexus3 ~]$ docker exec -it my-nginx /bin/bash
# 修改 nginx 首页展示内容
root@5535e66541d0:/# cd /usr/share/nginx/html/
root@5535e66541d0:/usr/share/nginx/html# ls
# 50x.html index.html
root@5535e66541d0:/usr/share/nginx/html# echo "hello docker!" > index.html
root@5535e66541d0:/usr/share/nginx/html# exit
# exit
# 重新访问首页,查看是否修改成功
[vagrant@nexus3 ~]$ curl localhost:3000
# hello docker!
# 使用 docker commit 根据容器 my-nginx 创建镜像 nginx:v2
[vagrant@nexus3 ~]$ docker commit --author "xingmu" --message "修改nginx默认首页" my-nginx nginx:v2
#sha256:deafd57279f0b4d1bd89e352469833e81fc40fbe3391f5a8cfdbeb065192251f
#查看镜像列表,是否创建成功
[vagrant@nexus3 ~]$ docker image ls
#REPOSITORY TAG IMAGE ID CREATED SIZE
#nginx v2 deafd57279f0 5 seconds ago 127MB
#custom-image 1.0 eae6faff977a 27 minutes ago 528MB
#mysql custom 129011b1e809 18 hours ago 448MB
#mariadb latest ac9c11a18222 2 days ago 357MB
#tomcat latest 1b6b1fe7261e 2 weeks ago 647MB
#openjdk 8 1077d23b5882 2 weeks ago 510MB
#nginx latest 9beeba249f3e 2 weeks ago 127MB
# 查看 nginx:v2 的镜像层历史
[vagrant@nexus3 ~]$ docker history nginx:v2
#IMAGE CREATED CREATED BY SIZE COMMENT
#deafd57279f0 39 seconds ago nginx -g daemon off; 119B 修改nginx默认首页
#9beeba249f3e 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
#<missing> 2 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGTERM 0B
#<missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
#<missing> 2 weeks ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… # ...
# 使用 nginx:v2 创建一个容器 my-nginx02
[vagrant@nexus3 ~]$ docker run -d --name my-nginx02 -p 3001:80 nginx:v2
# 3e9e7598b72cd8367dc18316967d0dec0ad57b00b15a82f08fdf7b65b1e3dd65
# 访问容器 my-nginx02 服务
[vagrant@nexus3 ~]$ curl localhost:3001
# hello docker!
使用 docker commit
命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。
如果仔细观察之前的 docker diff webserver
的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html
文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。
Dockerfile
使用 docker commit
虽然可以通过对容器的修改来创建镜像,但是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体的操作。所以使用这种方式生成的镜像维护起来是非常痛苦的。
如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。也就是说,如果我们熟悉Dockerfile
的一个编写语法,也可以定制自己的镜像。
dockerfile 语法
FROM 指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。 FROM
就是指定 基础镜像的,因此一个 Dockerfile
中 FROM
是必备的指令,并且必须是第一条指令。
在 Docker Hub 上有非常多的高质量的官方镜像,其中还提供了一些更为基础的操作系统镜像,如 ubuntu
、debian
、centos
、fedora
、alpine
等,这些操作系统的软件库为我们提供了更广阔的扩展空间。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch
。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
FROM scratch
...
如果你以 scratch
为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm
、etcd
。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch
会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
RUN 执行命令
RUN
指令是用来执行命令行命令的。由于命令行的强大能力,RUN
指令在定制镜像时是最常用的指令之一。其格式有两种:
- shell 格式:
RUN <命令>
,就像直接在命令行中输入的命令一样。
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
- exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
,这更像是函数调用中的格式。
之前说过,Dockerfile 中每一个指令都会建立一层,RUN
也不例外。指令执行结束后,commit
这一层的修改,构成新的镜像。需要注意的是 Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。
更多命令,请参看这里https://yeasy.gitbook.io/docker_practice/image/dockerfile
定义自己的镜像
-
创建一个Spring Boot项目,并写一个 controller
@RestController public class DockerController { @GetMapping("/dockerfile") @ResponseBody String dockerfile() { return "hello docker" ; } }
-
mvn clean package打成一个jar包
在target下找到
dockerfile-demo-0.0.1-SNAPSHOT.jar
-
在 docker 环境中创建一个空目录
dockerfile-demo
-
将第二步中的
jar
复制到上一步创建的目录 -
创建
Dockerfile
文件,加入如下内容# 基础镜像 FROM openjdk:8 # 设置镜像元数据 LABEL name="dockerfile-demo" vaersion="1.0" author="xingmu" # 复制 jar 包到镜像,并重命名为 dockerfile-image.jar COPY dockerfile-demo-0.0.1-SNAPSHOT.jar dockerfile-image.jar # 启动命令 CMD java -jar dockerfile-image.jar
-
基于 Dockerfile 构建镜像
docker build -t custom-image:1.0 . docker image ls #REPOSITORY TAG IMAGE ID CREATED SIZE #custom-image 1.0 eae6faff977a 9 minutes ago 528MB
-
基于上一步构建的镜像,创建container
docker run -d --name custom-image-c1 -p 8081:8080 custom-image:1.0 docker ps #CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES #0bfd8ad50b46 custom-image:1.0 "/bin/sh -c 'java -j…" 10 seconds ago Up 9 seconds 0.0.0.0:8081->8080/tcp custom-image-c1
-
查看启动日志
docker logs custom-image-c1 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.0.RELEASE) 2020-05-31 09:54:34.989 INFO 6 --- [ main] c.g.d.DockerfileDemoApplication : Starting DockerfileDemoApplication v0.0.1-SNAPSHOT on 0bfd8ad50b46 with PID 6 (/dockerfile-image.jar started by root in /) 2020-05-31 09:54:34.996 INFO 6 --- [ main] c.g.d.DockerfileDemoApplication : No active profile set, falling back to default profiles: default
-
访问镜像服务
curl localhost:8081/dockerfile # hello docker
发布镜像
将镜像发布到 docker hub
-
首先要拥有一个 docker hub 的账号,如果没有就到 https://hub.docker.com/ 去注册一个
-
在本地登录 docker hub
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. # 输入在 hub.docker.com 上注册的用户名和密码 Username: xingmu2020 Password: #WARNING! Your password will be stored unencrypted in /home/vagrant/.docker/config.json. #Configure a credential helper to remove this warning. See #https://docs.docker.com/engine/reference/commandline/login/#credentials-store #Login Succeeded
-
将镜像上传到 docker hub
docker hub 对上传镜像的格式是有要求的,
<docker hub 账号>/仓库名称:tag
,需要对本地镜像打 tag。# 给镜像打tag docker tag custom-image:1.0 xingmu2020/springboot-docker-demo:1.0 # 上传镜像 docker push xingmu2020/springboot-docker-demo:1.0
docker hub 在国外,速度感人,需要多等几分钟。
push
完成之后,在 https://hub.docker.com/repositories 应该就可以看到自己制作的镜像了。
这时候,其他人也可以通过 docker pull xingmu2020/springboot-docker-demo:1.0
来下载这个镜像并使用了。
将镜像发布到阿里云 docker 仓库
aliyun 容器镜像服务地址 https://cr.console.aliyun.com/cn-hangzhou/instances/repositories
-
登录到阿里云 docker 仓库,这里具体登录说明https://cr.console.aliyun.com/cn-hangzhou/instances/credentials
docker login --username=username@mail.com registry.cn-hangzhou.aliyuncs.com # 输入在上面的地址中设置的固定密码 Password: #WARNING! Your password will be stored unencrypted in #/home/vagrant/.docker/config.json. #Configure a credential helper to remove this warning. See #https://docs.docker.com/engine/reference/commandline/login/#credentials-store #Login Succeeded
-
创建命名空间
到这里https://cr.console.aliyun.com/cn-hangzhou/instances/namespaces 去创建一个自己的命名空间,我这里创建的是
xingmu2020
-
给镜像打 tag
docker tag custom-image:1.0 registry.cn-hangzhou.aliyuncs.com/xingmu2020/springboot-docker-demo:aliyun1.0
-
推送镜像到 阿里云 镜像服务器
docker push registry.cn-hangzhou.aliyuncs.com/xingmu2020/springboot-docker-demo:aliyun1.0
推送完成,就可以在 https://cr.console.aliyun.com/cn-hangzhou/instances/repositories 看到刚才推送的景象了。这时候,其他人也可以通过 docker pull registry.cn-hangzhou.aliyuncs.com/xingmu2020/springboot-docker-demo:aliyun1.0
来下载这个镜像并使用了。