Docker 镜像构建之 Dockerfile
在 Docker 中构建镜像最常用的方式,就是使用 Dockerfile
。Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。官方文档: https://docs.docker.com/engine/reference/builder/
引入部分
什么是 Dockerfile?
Dockerfile 主体内容分为四部分:基础镜像信息、 维护者信息、 镜像操作指令和容器启动时执行指令。
构建Dockerfile步骤
编写 Dockerfile 文件
docker built 构建镜像
docker run 创建容器
Dockfile 文件的注意事项
dockerfile的保留字指令
主要保留指令:
FROM
RUN
ADD
COPY
WORKDIR
CMD
一般用以上保留字指令就可以完成容器想要的功能,所有字段如下。
指令 含义 FROM 指定基础镜像,必须为第一个命令 MAINTAINER 维护者信息 RUN 构建镜像docker build时执行的命令 ADD 将本地文件添加到容器中,tar 类型文件会自动解压(网络压缩资源不会被解压) COPY 功能类似ADD,但是是不会自动解压文件,也不能访问网络资源 CMD 在docker run时会执行的命令,如果存在多个则仅最后一个生效。 LABEL 用于为镜像添加元数据 ENV 设置环境变量 EXPOSE 指定于外界交互的端口 VOLUME 用于指定持久化目录 WORKDIR 工作目录,类似于cd命令 ARG 用于指定传递给构建运行时的变量 ONBUILD 用于设置镜像触发器
二、Dockerfile 的保留字指令详解
1. FROM
FROM < image >
FROM < image > :< tag >
FROM < image >@< digest >
示例:
FROM mysql :5.6
如果不以任何镜像为基础,那么写法为:FROM scratch。官方说明:scratch 镜像是一个空镜像,可以用于构建 busybox 等超小镜像,可以说是真正的从零开始构建属于自己的镜像。
2. MAINTAINER
格式:
MAINTAINER < name >
示例:
MAINTAINER Jasper Xu MAINTAINER sorex @ 163. com
MAINTAINER Jasper Xu < sorex @ 163. com >
3.LABEL
?示例:元数据
LABEL version ="1.0" description ="这是一个 Web 服务器" by ="IT 笔录"
?注:使用 "LABEL" 指定元数据时,一条 "LABEL" 指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条 "LABEL" 指令指定,以免生成过多的中间镜像LABEL
示例二
maintainer ="http://blog.taoxiaoxin.club/"
4. RUN
shell 执行
格式:RUN < command >
exec 执行
格式:RUN ["executable" , "param1" , "param2" ]
示例:
RUN ["executable" , "param1" , "param2" ]
RUN apk update RUN ["/etc/execfile" , "arg1" , "arg1" ]
注: RUN 指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建 时指定 -- no - cache 参数,如:docker build -- no - cache
5. EXPOSE
?格式:
EXPOSE < port > [< port > ...]
?示例:
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211 / tcp 11211 / udp
?注: "EXPOSE" 并不会让容器的端口访问到主机。如果想使得容器与宿主机的端口有映射关系,要使其可访问,需要在 " docker run" 运行容器时通过 "-p" 来发布这些端口,或通过 "-P" 参数来发布 " EXPOSE" 导出的所有端口 ,
6. WORKDIR
?格式:
WORKDIR / path / to / workdir
?示例:
WORKDIR / a (这时工作目录为 / a )
WORKDIR b (这时工作目录为 / a / b )
WORKDIR c (这时工作目录为 / a / b / c )
?注:通过 "WORKDIR" 设置工作目录后, "Dockerfile" 中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用 "docker run" 运行容器时,可以通过 "-w" 参数覆盖构建时所设置的工作目录
7. ENV
?格式:
ENV < key > < value >
ENV < key > =< value > ...
?示例:
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat =fluffy
8. ADD
?格式 :
ADD < src > ... < dest >
ADD ["<src>" ,... "<dest>" ]
?示例:
ADD hom * / mydir /
ADD hom ? .txt / mydir /
ADD test relativeDir /
ADD test / absoluteDir /
9. COPY
指令:COPY
功能描述:复制文件到镜像中。
语法:COPY < src > … < dest >| [“ < src > ” ,… “ < dest > ” ]
提示:指令逻辑和 ADD 十分相似,同样 Docker Daemon 会从编译目录寻找文件或目录,dest 为镜像中的绝对路径或者相对于 WORKDIR 的路径。
10. VOLUME
?格式:
VOLUME ["/path/to/dir"]
?示例:
VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
?注:一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
1. 卷可以容器间共享和重用
2. 容器并不一定要和其它容器共享卷
3. 修改卷后会立即生效
4. 对卷的修改不会对镜像产生影响
5. 卷会一直存在,直到没有任何容器在使用它
11. CMD (这个指令需放在最后)
?格式:
CMD ["executable" ,"param1" ,"param2" ] (执行可执行文件,优先 )
CMD ["param1" ,"param2" ] (设置了 ENTRYPOINT,则直接调用 ENTRYPOINT 添加参数 )
CMD command param1 param2 (执行 shell 内部命令 )
?示例:
CMD echo "This is a test." | wc - w
CMD ["/usr/bin/wc" ,"--help" ]
?注: "CMD" 不同于 "RUN" , "CMD" 用于指定在容器启动时所要执行的命令,而 "RUN" 用于指定镜像构建时所要执行的命令
12.ONBUILD
?格式:
ONBUILD [INSTRUCTION ]
?示例:
ONBUILD ADD . / app / src
ONBUILD RUN / usr / local / bin / python - build -- dir / app / src
?注:当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被触发
三、构建镜像
Dockerfile 文件编写好以后,真正构建镜像时需要通过 docker build
命令。
docker build
命令用于使用 Dockerfile
创建镜像。
docker build - t mycentos :7 .
docker build - f / usr / local / dockerfile / Dockerfile - t mycentos :7 .
关于 . 理解
我们在使用 docker build
命令去构建镜像时,往往会看到命令最后会有一个 .
号。它究竟是什么意思呢?
很多人以为是用来指定 Dockerfile
文件所在的位置的,但其实 -f
参数才是用来指定 Dockerfile
的路径的,那么 .
号究竟是用来做什么的呢?
Docker
在运行时分为 Docker 引擎(服务端守护进程)
和 客户端工具
,我们日常使用各种 docker 命令
,其实就是在使用 客户端工具
与 Docker 引擎
进行交互。
当我们使用 docker build
命令来构建镜像时,这个构建过程其实是在 Docker 引擎
中完成的,而不是在本机环境。如果在 Dockerfile
中使用了一些 ADD
等指令来操作文件,如何让 Docker 引擎
获取到这些文件呢?
这里就有了一个 镜像构建上下文
的概念,当构建的时候,由用户指定构建镜像时的上下文路径,而 docker build
会将这个路径下所有的文件都打包上传给 Docker 引擎
,引擎内将这些内容展开后,就能获取到上下文中的文件了。
举个栗子:我的宿主机 jdk 文件在 /root 目录下,Dockerfile 文件在 /usr/local/dockerfile 目录下,文件内容如下:
ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
那么构建镜像时的命令就该这样写:
docker build -f /usr/local/dockerfile/Dockerfile -t mycentos:7 /root
再举个栗子:我的宿主机 jdk 文件和 Dockerfile 文件都在 /usr/local/dockerfile 目录下,文件内容如下:
ADD jdk-11.0.6_linux-x64_bin.tar.gz /usr/local/java
那么构建镜像时的命令则这样写:
docker build - f / usr / local / dockerfile / Dockerfile - t mycentos :7 .
四、Dockerfile实践
接下来我们通过基础镜像 centos:7
,在该镜像中安装 jdk 和 tomcat 以后将其制作为一个新的镜像 mycentos:7
。
创建目录。
mkdir - p / usr / local / dockerfile
编写 Dockerfile 文件。
vi Dockerfile
Dockerfile 文件内容如下:
FROM centos :7
LABEL maintainer ="mrhelloworld.com"
WORKDIR / usr / local
RUN mkdir - p / usr / local / java && mkdir - p / usr / local / tomcat
ADD jdk - 11.0.6 _linux - x64_bin .tar .gz / usr / local / java
ADD apache - tomcat - 9.0.37 .tar .gz / usr / local / tomcat
EXPOSE 8080
ENV JAVA_HOME / usr / local / java / jdk - 11.0.6 /
ENV PATH $ PATH :$ JAVA_HOME / bin
CMD ["/usr/local/tomcat/apache-tomcat-9.0.37/bin/catalina.sh" , "run" ]
构建镜像。
[root @ localhost ~ ]
Sending build context to Docker daemon 191.4 MB
Step 1 / 10 : FROM centos :7
- --> 7e6257 c9f8d8
Step 2 / 10 : LABEL maintainer ="mrhelloworld.com"
- --> Running in 3 f18aa4f3fb2
Removing intermediate container 3 f18aa4f3fb2
---> 7364 f68ca4ab
Step 3 / 10 : WORKDIR / usr / local
- --> Running in d9889152cfc4
Removing intermediate container d9889152cfc4
---> d05bd2e09fa4
Step 4 / 10 : RUN mkdir - p / usr / local / java && mkdir - p / usr / local / tomcat
- --> Running in 3 bcd6ef78350
Removing intermediate container 3 bcd6ef78350
---> 4832 abf9d769
Step 5 / 10 : ADD jdk - 11.0.6 _linux - x64_bin .tar .gz / usr / local / java
- --> e61474bf7a76
Step 6 / 10 : ADD apache - tomcat - 9.0.37 .tar .gz / usr / local / tomcat
- --> 7110 cdff7438
Step 7 / 10 : EXPOSE 8080
- --> Running in a4731c1cf77d
Removing intermediate container a4731c1cf77d
---> f893cefee00c
Step 8 / 10 : ENV JAVA_HOME / usr / local / java / jdk - 11.0.6 /
- --> Running in f0cb08f390db
Removing intermediate container f0cb08f390db
---> ff9f6acf6844
Step 9 / 10 : ENV PATH $ PATH :$ JAVA_HOME / bin
- --> Running in eae88cf841d0
Removing intermediate container eae88cf841d0
---> 4 b9226a23b10
Step 10 / 10 : CMD ["/usr/local/tomcat/apache-tomcat-9.0.37/bin/catalina.sh" , "run" ]
- --> Running in ccf481045906
Removing intermediate container ccf481045906
---> 9 ef76a16441b
Successfully built 9 ef76a16441b
Successfully tagged mycentos :7
五、镜像构建历史
docker history 镜像名称 :标签 | ID
docker history mycentos :7
六、使用构建的镜像创建容器
# 创建容器
docker run -di --name mycentos7 -p 8080:8080 mycentos:7
# 进入容器
docker exec -it mycentos7 /bin/bash
# 测试 java 环境变量
[root@dcae87df010b /]# java -version
java version "11.0.6" 2020-01-14 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.6+8-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.6+8-LTS, mixed mode)
# 访问 http://192.168.10.10:8080/ 看到页面说明环境 OK!
七、Dockerfile 训练
1.使用 centos7 作为基础镜像部署 nginx 服务
# vim nginx.repo
'''文件内容
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
'''
[root@shawn ~]# vim Dockerfile
# 指定基础镜像(依赖镜像)
FROM centos:7
# 执行一个命令
RUN yum install -y yum-utils
# 将本地文件添加到容器中
ADD nginx.repo /etc/yum.repos.d/nginx.repo
# 更新YUM缓存
RUN yum makecache
# 安装nginx
RUN yum install -y nginx
# 制定容器启动默认执行的命令
CMD nginx -g 'daemon off;'
[root@shawn ~]# docker build -t install/nginx:v1 .
[root@shawn ~]# docker images
[root@shawn ~]# docker run -dit install/nginx:v1 sh
[root@shawn ~]# docker exec -it 94f8e35f3357 bash
[root@shawn ~]# crul 127.0.0.1 # 出现 html 代码说明部署成功
2.在容器中编译安装 nginx 服务
[root@shawn ~]# vim Dockerfile
'''文件内容
# 指定基础镜像(依赖镜像)
FROM centos:7
# 执行命令
RUN yum install yum-utils wget zlib zlib-devel pcre pcre-devel make gcc gcc-c++
RUN cd /opt && wget http://nginx.org/download/nginx-1.18.0.tar.gz && tar -xvf nginx.1.18.0/ && cd nginx-1.18.0/ && ./configure && make && make install
# 指定进入容器的默认工作目录
WORKDIR /usr/local/nginx/sbin
# 指定容器启动默认执行的命令
CMD ./nginx -g 'daemon off;'
'''
[root@shawn ~]# docker build -t yuan/install/nginx:v2 .
[root@shawn ~]# docker images
[root@shawn ~]# docker run -dit --name yuan_nginx yuan/install/nginx:v2 sh
[root@shawn ~]# docker exec yuan_nginx crul 127.0.0.1 # 出现 html 代码说明部署成功
3.构建以 Centos 为依赖镜像并安装 Django 的服务
[root@shawn ~]#vim Dockerfile
# 指定基础镜像
FROM centos:7
# 运行命令
RUN yum makecache && yum update -y && yum install -y python3 && pip3 install django
# 拷贝本地文件到容器
COPY shawn /root/
# 指定进入到容器的工作目录
WORKDIR /root/
# 指定向外暴露的端口
EXPOSE 8080
# 运行命令
CMD cd ./shawn && python3 manage.py runserver 0.0.0.0:8080
在宿主机上安装 Django
django-admin startproject shawn #创建一个 "Shawn" 项目
cd ./shawn #进入目录
django-admin startapp application #开始项目
cd ./shawn
vim setting.cong #修改配置文件"*"代理
cd .. #退出
[root@shawn ~]#docker build -t test333:v1 .
[root@shawn ~]#docker images
[root@shawn ~]#docker run -dit --name test001 -p 9999:8080 test333:v1 sh
[root@shawn ~]#docker exec -it test001 bash
[root@80f1315c030c ~]# python3 manage.py runserver 0.0.0.0:8080
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
December 04, 2020 - 06:50:19
Django version 3.1.4, using settings 'lingxiu.settings'
Starting development server at http://0.0.0.0:8080/
Quit the server with CONTROL-C.
3.构建以 python 为依赖镜像并安装 Django 服务
[root @ shawn ~ ]
'''文件内容
# 指定依赖镜像
FROM python:3.6
# 设置作者
MAINTAINER Shawn
# 执行命令
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip3 install django==2.2.2
# 拷贝文件
COPY app /root/
# 设置工作目录
WORKDIR /root/
# 执行命令
CMD cd ./app && python3 manage.py runserver 0.0.0.0:7777
在宿主机上安装 Django
django - admin startproject app
cd ./ app
django - admin startapp application
cd ./ app
vim setting .cong
cd ..
[root@shawn ~]#docker build -t jjjj .
[root@shawn ~]#docker images
[root@shawn ~]#docker run -dit --name jjjjtest -p 4444:7777 jjjj:latest sh
[root@shawn ~]#docker exec -it jjjtest bash
root@b85f93fcc114:~# python3 manage.py runserver 0.0.0.0:7777
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
December 04, 2020 - 10:17:51
Django version 2.2.2, using settings 'app.settings'
Starting development server at http://0.0.0.0:7777/
Quit the server with CONTROL-C.
4.使用 NGINX 代理 Django
?编写 "Dockerfile" 文件
[root@shawn DjangoDocker]#vim Dockerfile
'''文件内容
# 指定依赖镜像
FROM pyhton:3.6
# 安装 Django
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip3 install django==2.2.2
# COPY 文件
COPY app /root/
# 指定工作目录
WORKDIR /root/
# 运行命令
CMD cd ./app && python3 manage.py runserver 0.0.0.0:8080
'''
[root@shawn DjangoDocker]#ls
app Dockerfile # 这两个文件, "app" 在上一个例子中有构建
?构建镜像,并查看
[root@shawn DjangoDocker]#docker build -t python_django:v6 .
[root@shawn DjangoDocker]#docker images
?实例出容器,并查看
[root@shawn DjangoDocker]#docker run -dit --name p_d_test1 -p 8888:8080 python_django:v6 sh
6906ff9e3ec0f9d583eb27890d82c79deff4358a43e5f1ec768a702547d020bf
[root@shawn DjangoDocker]#docker ps
?进到容器里面,开启服务,再测试
[root@shawn DjangoDocker]#docker exec -it p_d_test1 bash
root@6906ff9e3ec0:~# python3 manage.py runserver 0.0.0.0:8080
[root@shawn DjangoDocker]#curl 127.0.0.1:8888
?编写 "nginx.repo" 文件
[root@shawn NginxDocker]#vim nginx.repo
'''文件内容(官网可复制)
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
'''
?编写 "default.conf" 文件(代理"Django"配置)
[root@shawn NginxDocker]#vim default.conf
'''文件内容
server {
listen 80;
server_name www.py16zxl.com;
location / {
# 这里填的是 Django 服务的访问地址与端口(映射端口)
proxy_pass http://192.168.13.234:8888/;
index index.html index.htm index.jsp;
}
}
'''
?编写 "Dockerfile" 文件
[root@shawn NginxDocker]#vim Dockerfile
'''文件内容
# 指定依赖进行
FROM centos:7
# 指定作者
MAINTAINER shawn
# 安装依赖
RUN yum install -y yum-utils gcc gcc-c++ pcre pcre-devel zlib zlib-devel make wget
## 源码安装 nginx1.18.0
# RUN wget http://nginx.org/download/nginx-1.18.0.tar.gz && tar -xvf nginx-1.18.0.tar.gz && cd nginx.1.18.0 && ./configure --prefix="/usr/local/nginx-1.18.0" && make && make install
# 拷贝 NGINX 配置文件
COPY nginx.repo /etc/yum.repos.d/
# 更新 yum 软件包索引
RUN yum makecache fast
# yum 安装 nginx
RUN yum install -y nginx
# 指定向外暴露的端口
EXPOSE 8000
# 拷贝 nginx 默认配置文件
COPY default.conf /etc/nginx/conf.d/
# 容器起来运行的命令
CMD /usr/local/nginx-1.18.0/sbin/nginx -g 'daemon off;'
'''
?当前需要的文件
[root@shawn NginxDocker]#ls
default.conf Dockerfile nginx.repo
?开始构建镜像,并查看
[root@shawn NginxDocker]#docker build -t nginx_d:v7 .
[root@shawn NginxDocker]#docker images
?实例化出容器,并查看
[root@shawn NginxDocker]#docker run -dit --name nginx_d -p 80:80 nginx_d:v7 sh
[root@shawn NginxDocker]#docker ps
?进入容器,开启 "nginx" 服务,并验证
[root@shawn NginxDocker]#docker exec -it nginx_d bash
[root@51f54c1d5abb /]#nginx
[root@shawn NginxDocker]#curl 127.0.0.1:80
# 发现通过访问 nginx 也可以进入 Django 页面