一、DockerFile是什么?
DockerFile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本,构建三步骤:
- 编写Dockerfile文件
- docker build
- docker run
那么DockerFile文件案例如下:
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20201113" \
org.opencontainers.image.title="CentOS Base Image" \
org.opencontainers.image.vendor="CentOS" \
org.opencontainers.image.licenses="GPL-2.0-only" \
org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
CMD ["/bin/bash"]
二、DockerFile构建过程解析
2.1.DockerFile内容基础
Dockerfile中的指令需要满足如下的规则:
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- #表示注释
- 每条指令都会创建一个新的镜像,并对镜像镜像提交
2.2.docker 执行Dockerfile的流程
docker执行一个Dockerfile脚本的流程大致如下:
- docker从基础镜像运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker在基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
2.3.说明
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,
- Dockerfile是软件的原材料
- Docker镜像是软件的交付品
- Docker容器则可以认为是软件的运行态。
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
- Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
- Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时,会真正开始提供服务;
- Docker容器,容器是直接提供服务的。
三、DockerFile体系结构(保留字指令)
指令 |
说明 |
FROM |
基础镜像,当前新镜像是基于哪个镜像的,有继承的意味 |
MAINTAINER |
镜像维护者的姓名和邮箱地址 |
RUN |
容器构建时需要运行的命令 |
EXPOSE |
当前容器对外暴露的端口 |
WORKDIR |
指定在创建容器后,终端默认登录的进来工作目录,一个落脚点 |
ENV |
用来在构建镜像过程中设置环境变量 |
ADD |
将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包 |
COPY |
类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中<源路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置 COPY ["src","dest"] |
VOLUME |
容器数据卷,用于数据保存和持久化工作 |
CMD |
指定一个容器启动时要运行的命令Dockerfile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换 |
ENTRYPOINT |
指定一个容器启动时要运行的命令ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及参数 |
ONBUILD |
当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发 |
DockerFile命令
BUILD |
BOTH |
RUN |
FROM |
WORKDIR |
CMD |
MAINTAINER |
USER |
ENV |
COPY |
|
EXPOSE |
ADD |
|
VOLUME |
RUN |
|
ENTRYPOINT |
ONBUILD |
|
|
.dockerignore |
|
|
四、案例演示
4.1.Base镜像(scratch)
Docker Hub 中99%的镜像都是通过在base镜像中安装和配置需要的软件构建处理的,
在dockerhub中搜索Tomcat,地址如下:https://hub.docker.com/,
点击查看镜像构成:
4.2.自定义镜像mycentos
我们从官方pull下来的centos
镜像是mini版的,所以不带有vim、ifconfig
这些基础命令,那我们就来自定义一个镜像,功能比官方下载的强大点,同时运用下各个指令。
4.2.1.编写dockerfile
Hub默认Centos镜像什么情况?
自定义mycentos目的使我们自己的镜像具备如下:
- 登陆后的默认路径
- vim编辑器
- 查看网络配置ifconfig支持
首先我们来编写对应的Dockerfile文件。内容如下:
# 指定基础镜像
FROM centos:7
# 镜像维护者的姓名和邮箱地址
MAINTAINER augus<11200297@qq.com>
# 在构建镜像过程中设置环境变量
ENV MYPATH /usr/local
# 指定在创建容器后,终端默认登录的进来工作目录
WORKDIR $MYPATH
# 镜像构建时需要运行的命令
RUN yum -y install vim
RUN yum -y install net-tools
# 当前容器对外露出的端口
EXPOSE 80
# 指定一个容器启动时要运行的命令
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
操作如下:
4.2.2.构建
语法如下:
docker build -f dockerfile名称 -t 新建的镜像名:TAG .
注意:
- docker build 命令最后有一个 .
- . 表示当前目录,
案例如下:
docker build -f dockerfile2 -t mycentos:100 .
执行后结果
4.2.3.运行
根据之前构建的镜像生成容器,语法如下:
docker run -it 新镜像名字:TAG
执行命令如下:
4.2.4.列出镜像的变更历史
语法如下:
docker history 镜像名
执行如下:
在本例中我们用到了 FROM
MAINTAINER
RUN
EXPOSE
ENV
WORKDIR
命令
4.3.CMD/ENTRYPOINT镜像案例
接下来我们通过案例来看看CMD
和ENTRYPOINT
两个命令的区别,这两个命令的作用都是指定一个容器启动时要运行的命令
4.3.1.CMD
Dockerfile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换掉,我们通过tomcat的案例来介绍。正常情况如下
docker run -it -p 8888:8080 tomcat
执行后,可以成功运行Tomcat
但是当我们在 执行命令后添加参数的话,如下
原因是我们先看Tomact对应的 Dockerfile文件,最后面是启动Tomcat的,而我们添加参数后,就将下面的命令给覆盖了,所以容器无法运行(就相当于有了多个CMD)。
4.3.2.ENTRYPOINT
有别于CMD
命令,ENTRYPOINT
命令是在 docker run
curl
指令来介绍这个案例。Dockerfile文件如下:
FROM centos:7
MAINTAINER Augus<Auguses@126.com>
ENV MY_PATH /usr/local/
WORKDIR $MY_PATH
RUN yum install -y curl
EXPOSE 80
ENTRYPOINT [ "curl", "-s", "http://www.baidu.com" ]
构建镜像,如下:
docker build -f dockerfile3 -t myapp .
正常执行run命令,创建容器,如下:
加-i
参数 查看响应报文头
通过这个例子 可以看到ENTRYPOINT
不会覆盖,而是组合成了一个新的命令。
4.4.自定义镜像Tomcat
最后我们通过自定义一个tomcat
镜像来介绍下ADD
和COPY
这两个命令的区别。
4.4.1.创建个tomcat目录
在dockerfile下创建tomcat目录
4.4.2 添加一个文件
在当前目录下创建一个 hello.txt
文件,作用是COPY
到容器中
4.4.3.拷贝相关软件
准备对应的jdk
和tomcat
的压缩文件。上传到tomcat目录:
4.4.4.创建Dockerfile文件
创建文件tomcatfile,内容如下:
FROM centos
MAINTAINER Augus<Auguses@126.com>
# 把主机的hello.txt 拷贝到容器的/usr/local/路径下
COPY hello.txt /usr/local/helloincontainer.txt
# 把jdk和tomcat添加到容器中/usr/local
ADD jdk-8u172-linux-x64.tar.gz /usr/local
ADD apache-tomcat-8.5.43.tar.gz /usr/local
# 设置工作访问时候的WORKDIR路径,进入容器的落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 配置tomcat和java的环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_172
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.43
ENV CATALINA_BASE /usr/local/apache-tomcat-8.5.43
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
# 容器运行暴露的端口
EXPOSE 8080
# 启动tomcat
CMD /usr/local/apache-tomcat-8.5.43/bin/startup.sh && tail -F /usr/local/apache-tomcat-8.5.43/logs/catalina.out
4.4.5.构建镜像
执行命令:
docker build -f tomcatfile -t java8/tomcat:8 .
4.4.6.生成容器
构建成功后,我们就可以运行了,命令如下:
docker run -d -it -p 8082:8080 --name mytomcat8.5 -v /mydockerfile/tomcat/test:/usr/local/apache-tomcat-8.5.43/webapps/ -v /mydockerfile/tomcat/tomcatlogs/:/usr/local/apache-tomcat-8.5.43/logs --privileged=true 镜像ID
执行后如下:
4.4.7.生成容器测试访问:
在浏览器输入:http://192.168.42.131:8082,访问如下:这里访问没有内容是因为webappes目录下是空的,但是不影响使用
我们在test目录放入war包,如下:
然后在浏览器访问,如下:
五、虚悬镜像
虚悬镜像:仓库名、标签名都是 <none>
的镜像,称为 dangling images(虚悬镜像)。
在构建或者删除镜像时可能由于一些错误导致出现虚悬镜像。例如:
1)在myfile目录下创建Dockerfile内容如下:
FROM centos
CMD echo "action is success"
2)构建镜像
# 构建时候没有镜像名、tag
docker build .
3)列出docker中的虚悬镜像:
docker image ls -f dangling=true
4)虚悬镜像一般是因为一些错误而出现的,没有存在价值,可以删除:
# 删除所有的虚悬镜像
docker image prune
六、Dockerfile基于centos7定制java8容器
需要在容器中安装java8,同时安装vim,在myfile中上传文件如下:
编写dockerfile如下:
FROM centos:7
MAINTAINER Augus<Auguses@126.com>
# 设置工作访问时候的WORKDIR路径,进入容器的落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 安装vim编辑器
RUN yum -y install vim
# 安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# 把jdk和tomcat添加到容器中/usr/local/java,注意Dockerfile和安装包必须在同一个位置
ADD jdk-8u172-linux-x64.tar.gz /usr/local/java
# 配置tomcat和java的环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_172
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 echo $MYPATH
CMD echo "success------------ok"
CMD /bin/bash
构建镜像如下:
docker build -f Dockerfile1 -t openjdk:8-oracle .
七、Docker发布微服务
7.1.创建springboot项目
搭建一个简单的SpringBoot项目,在pom.xml中导入依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.augus</groupId>
<artifactId>springboot_docker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot_docker</name>
<description>springboot_docker</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.augus.SpringbootDockerApplication</mainClass>
<fork>true</fork>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
在application.yaml中指定端口
server:
port: 6001
在controller包中创建OrderController,如下:
@RestController
public class OrderController {
@Value("${server.port}")
private String port;
@RequestMapping("/order/docker")
public String helloDocker(){
return "hello world \t" + port + "\t" + UUID.randomUUID().toString();
}
@RequestMapping("/order/index")
public String index(){
return "服务端口号:" + "\t" + port + "\t" + UUID.randomUUID().toString();
}
}
启动项目,然后在浏览器访问
在Idea中运行没有问题时,将其使用maven的package
打成jar包。操作如下:
7.2.发布微服务项目到Docker容器
7.2.1.将jdk和jar上传到Linux服务器的myfile目录
上传后结果如下:
7.2.2.编写dockerfile
利用基础镜像java:8构建,在Dockerfile内容如下:
# 基础镜像利用java:8完成
FROM java:8
# 作者
MAINTAINER Augus<Auguses@126.com>
#VoLUME指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
#将jar包添加到容器中并更名为augus_docker.jar
ADD springboot_docker-0.0.1-SNAPSHOT.jar augus_docker.jar
#运行jar包
RUN bash -c 'touch /augus_docker.jar'
ENTRYPOINT ["java","-jar", "/augus_docker.jar"]
#暴露6001端口作为微服务
EXPOSE 6001
7.2.3.构建镜像
执行如下命令
docker build -f Dockerfile -t springboot_docker:1.0 .
执行如下:
7.2.4.利用镜像创建容器
docker run -d -p 6001:6001 --name springboot springboot_docker:1.0
执行后如下:
7.2.5.测试
在浏览器访问:使用主机ip和端口6001访问: