Dockerfile
dockerfile是用来构建docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本
没有学dockerfile之前,是通过在系统上一步步安装的vim,ifocnfig,
是当前镜像的基础上,重新安装新功能,再使用docker commit指令生成一个新的镜像
docker commit 只适用于简单的场景,面对要求镜像随时变化的复杂环境,使用commit太麻烦了
通过使用dockerfile,可以一次性的构建
一、构建三步骤
- 编写dockerfile文件
- docker build命令构建镜像
- docker run 按照镜像运行容器实例
二、dockerfile构建过程
1. dockerfile基础知识
- 每条保留字指令必须为大写字母并且后面要跟随至少一个参数
- 每条指令按照从上到下,顺序执行
- #为注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交(镜像上面叠加镜像,一层层最终形成一个新镜像)
2. docker 执行dockerfile的流程
- docker从基础镜像运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令,直到所有的指令都执行完成
从应用软件的角度看,dockerfile、docker、镜像与docker容器分别代表软件的三个阶段
dockerfile => 软件的原材料
docker镜像 => 软件的交付品
docker容器 => 软件镜像的运行态,即依照镜像运行的容器实例
dockerfile面向开发,docker镜像成为交付标准,docker容器则涉及部署与运维,三者缺一不可,是docker体系的基石
build run
dockerfile ----------> docker镜像 ----------> docker容器
Dockerfile,需要定义一个dockerfile,dockerfile定义了进程需要的一切东西。dockerfile涉及的内容包括执行的代码或者文件、环境变量、依赖包、运行时的环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务以及内核进程打交道,则需要考虑如何设计namespace的权限控制)等
docker镜像,在用dockerfile定义一个文件之后,docker build时产生的一个docker镜像,当运行docker镜像时,会真正开始提供服务
docker容器,容器是直接提供服务的
三、 dockerfile常用保留字指令
1. FROM
基础镜像,当前新镜像是基于哪个镜像的,指定一个以及存在的镜像作为模板,第一条必须是FROM
2. MAINTAINER或LABEL
镜像维护者的姓名和邮箱地址
注意:
MAINTAINER指定镜像作者信息,即镜像的Author属性。
LABEL是一个更灵活的版本,可以替代MAINTAINER,LABEL可以设置任何需要设置的元数据
按照官方文档的描述,可以使用LABEL maintainer=“xxx” 代替maintainer xxx 需要注意的是两者设置的值,在镜像的描述文件中所处的位置是不一样的
3. RUN
- 容器构建时需要运行的命令
- 两个格式
shell格式:
RUN <命令行命令>
#<命令行命令>等同于,在终端操作的shell命令
exec格式:
RUN [“可执行文件”,“参数1”,“参数2”]
#例如:
#RUN [“./test.php”,“dev”,“offline”] 等价于 RUN .test.php dev offline
4. EXPOSE
当前容器对外暴露的端口
5. WORKDIR
指定在创建容器后,终端默认登录进来的工作目录
6. USER
指定该镜像以什么样的用户去执行,如果不指定,默认是root
7. ENV
用来在构建镜像过程中设置环境变量
例如:
ENV MY_PATH /usr/mytest
这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样。
也可以在其他指令中直接使用执行环境变量。
比如:WORKDIR $MY_PATH 这里就是通过$引用MY_PATH路径/usr/mytest,作为默认登录的工作目录
8. ADD
将宿主机目录下的文件拷贝进镜像且自动处理URL和解压tar包
9. COPY
类型ADD,拷贝文件和目录到镜像中
将从构建上下文目录中<源路径>的文件/目录复制一份新的一层镜像内的<目标路径>位置
格式:
COPY 宿主机源路径 容器内路径
COPY src dest
COPY [“src”,“dest”]
<src源路径>:源文件或者源目录
<dest目的路径>:容器内的指定路径,该路径如果不存在就会自动创建
10. VOLUME
容器数据卷:用于数据保存和持久化工作
11. CMD
指定容器启动时要做的事情
格式:
CMD指令的格式和RUN相似
两种格式:
shell格式:CMD <命令>
exec格式:CMD [“可执行文件”,“参数1”,"参数2’…]
参数列表格式:CMD [“参数1”,"参数2’…] 在指定了ENTRYPOINT指令后,用CMD指定具体参数
注意:
- Dockerfile可以有多个CMD指令,但只有最后一个生效
- CMD会被docker run 之后的参数替换
例如:
dockerfile:
...
EXPOSE 80
CMD ["test.sh","run"]
运行这个dockerfile构建的镜像,如果使用docker run -it -p 8080:8080 镜像ID,默认是执行test.sh
如果使用的命令是docker run -it -p 8080:8080 镜像ID /bin/bash,运行的就是run之后的/bin/bash,被替换了
12. ENTRYPOINT
用来指定一个容器启动时要运行的命令
类似CMD指令,但是ENTRYPOINT不会被docker run 后的命令替换
这些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序
格式:ENTRYPOINT [“executeable”,“param1”,“param2”,…]
ENTRYPOINT和CMD可以一起使用,一般变参数才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参
当指定了ENTRYPOINT后,CMD的含义就发生变化,不再直接运行命令而是将CMD的内容作为参数传递给ENTRYPOINT命令,二者组合变成<ENTRYPOINT> “<CMD>”
例如:
假设通过Dockerfile构建了nginx:test镜像
Dockerfile:
FROM nginx # 基础镜像
ENTRYPOINT ["nginx","-c"] # 定参 启动nginx的固定写法:nginx -c
CMD ["/etc/nginx/nginx.conf"] # 变参 nginx需要指定配置文件,这里写在CMD
使用上面dockerfile构建的镜像运行容器实例
docker run nginx:test
ENTRYPOINT 和 CMD为容器启动时要运行的命令
<ENTRYPOINT> "<CMD>"
实际执行:nginx -c /etc/nginx/nginx.conf
即启动nginx的命令,指定配置文件为/etc/nginx/nginx.conf
如果换个参数的命令执行
docker run nginx:test -c /etc/nginx/new.conf
根据上面的CMD只有最后一个生效,CMD会被docker run 之后的参数替换,
则实际执行的命令是:nginx -c /etc/nginx/new.conf ,这里的CMD参数被docker run之后的参数替换,即指定的配置文件/etc/nginx/new.conf
案例-构建java环境
[root@localhost myfile]# pwd
/root/myfile
[root@localhost myfile]# ls
Dockerfile jdk-8u191-linux-x64.tar.gz
[root@localhost myfile]# cat Dockerfile
# 声明基础镜像基于centos:7
FROM centos:7
# 作者及邮箱
MAINTAINER wq<123@qq.com>
# 声明一个变量
ENV MY_PATH /usr/local
# 工作目录,进入容器后 目录为/usr/local
WORKDIR $MY_PATH
# 安装vim编辑器
RUN yum install -y vim
# 安装ifconfig命令
RUN yum install -y net-tools
# 安装java8和lib库
RUN yum install -y glibc.i686
# 创建目录
RUN mkdir /usr/local/java
# ADD是相对路径tar,把jdk-8u191-linux-x64.tar.gz添加到容器中,安装包必须在Dockerfile文件在同一个位置,ADD是拷贝后自动解压
ADD jdk-8u191-linux-x64.tar.gz ${MY_PATH}/java
# 配置java环境变量,声明
ENV JAVA_HOME ${MY_PATH}/java/jdk1.8.0_191
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH ${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar:${JRE_HOME}/lib:${CLASSPATH}
ENV PATH ${JAVA_HOME}/bin:$PATH
# 暴露端口
EXPOSE 80
# CMD
CMD /bin/bash
构建镜像
[root@localhost myfile]# docker build -t mycentos:1.0 .
[root@localhost myfile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 1.0 6369d9b5501d 13 minutes ago 1.38GB
验证
[root@localhost myfile]# docker run -it mycentos:1.0
[root@542578d96658 local]# pwd
/usr/local
[root@542578d96658 local]# ls
bin etc games include java lib lib64 libexec sbin share src
[root@542578d96658 local]# ls java/
jdk1.8.0_191
[root@542578d96658 local]# java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
升级-添加python
[root@localhost myfile2]# ls
Dockerfile
[root@localhost myfile2]# cat Dockerfile
# 基于mycentos:1.0构建进行升级
FROM mycentos:1.0
# 安装python3
RUN yum install -y python3
# 运行默认进入python环境
CMD python3
[root@localhost myfile3]# docker build -t mycentos:2.0 .
[root@localhost myfile3]# docker run -it mycentos:2.0
Python 3.6.8 (default, Nov 14 2023, 16:29:52)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print ("hhh")
hhh
指定/bin/bash,替换Dockerfile中的CMD指令
[root@localhost myfile2]# docker run -it mycentos:2.0 /bin/bash
[root@78a64d606bfa local]# pwd
/usr/local
[root@78a64d606bfa local]# java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
[root@78a64d606bfa local]# python3
Python 3.6.8 (default, Nov 14 2023, 16:29:52)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>