Docker 容器 - Dockerfile
Docker 容器 - Dockerfile
- 一、Dockerfile 基本结构
- 二、Dockerfile 指令详解
- 2.1 FROM
- 2.2 MAINTAINER
- 2.3 COPY
- 2.4 ADD
- 2.5 WORKDIR
- 2.6 VOLUME
- 2.7 EXPOSE
- 2.8 ENV
- 2.9 RUN
- 2.10 CMD
- 2.11 ENTRYPOINT
- 三、Dockerfile 创建镜像与模板
- 3.1 Dockerfile 镜像
- 3.2 镜像管理
- 3.3 Dockerfile 模板
- 3.3.1 Redhat 镜像模板
- 3.3.2 JDK 镜像模板
- 3.3.3 Tomcat 镜像模板
- 四、Docker 数据持久化
- 五、案例实战
- 5.1 构建nginx服务镜像
DockerFile 是一个文本格式的配置文件,用户可以使用 DockerFile 来快速创建自定义的镜像
镜像的定制实际上就是 定制每一层所添加的配置、文件 。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile
一、Dockerfile 基本结构
Dockerfile 分为四部分:基础镜像信息
、维护者信息
、 镜像操作指令
和 容器启动时执行指令
- 每条指令创建一层:
- FROM : 指明所基于的镜像名称
- MAINTAINER:维护者信息,docker_user 维护(可以不写)。
- COPY : 从 Docker 客户端的当前目录添加文件,镜像操作 指令。
- RUN : 构建镜像时 执行 make 命令,每运行一条 RUN 指令,镜像就添加新的一层,并提交(添加可写层)。
- CMD : 指定在容器中运行什么命令,用来指定运行容器时的操作命令
Docker 镜像由只读层组成,每个只读层代表一个 Dockerfile 指令。这些层是堆叠
的,每个层都是上一层的变化的增量
Docker 可以通过 读取 Dockerfile 指令 来自动构建镜像
# 在一个空目录下,新建一个名为 Dockerfile 文件
mkdir /www/ #/www这个目录是自己创建的,通过创建空目录,在此目录下创建文件
vim /www/Dockerfile# 编辑 Dockerfile
FROM nginx:1.27.4# 维护者,可以省略
MAINTAINER kendra kendra@docker.com# 启动容器
RUN mkdir /usr/share/nginx/html -p
RUN echo "hello docker" > /usr/share/nginx/html/index.html# 构建镜像 . : 根据当前上下文环境构建
docker build -t mynginx:v1.0 .# 运行
docker run --rm -it mynginx:v1.0 /bin/bash
[root@Docker-kd ]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat 11.0.5 e8c9a002d37b 7 weeks ago 467MB
nginx 1.27.4 4cad75abc83d 2 months ago 192MB
mysql 8.4.4 4a8a163431d3 3 months ago 769MB
busybox latest ff7a7936e930 7 months ago 4.28MB
[root@Docker-kd ]# ls /
afs boot etc lib media opt root sbin sys usr
bin dev home lib64 mnt proc run srv tmp var
[root@Docker-kd ]# mkdir /www
[root@Docker-kd ]# cd /www/
[root@Docker-kd www]# ls
[root@Docker-kd www]# vim Dockerfile
[root@Docker-kd www]# cat Dockerfile
FROM nginx:1.27.4
MAINTAINER kendra kendra@docker.comRUN mkdir /usr/share/nginx/html -p
RUN echo "hello docker" > /usr/share/nginx/html/index.html
编辑界面:w保存,另一个会话即可构建镜像、运行,测试
- 另起一个会话
- 构建:
docker build -t mynginx:v1.0 .
- 运行:
docker run --rm -it mynginx:v1.0 /bin/bash
- 构建:
[root@Docker-kd ~]# cd /www/
[root@Docker-kd www]# ls
Dockerfile
# 构建
[root@Docker-kd www]# docker build -t mynginx:v1.0 .
[+] Building 0.9s (7/7) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 245B 0.0s=> WARN: MaintainerDeprecated: Maintainer instruction is deprecated in favor of us 0.0s=> [internal] load metadata for docker.io/library/nginx:1.27.4 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/3] FROM docker.io/library/nginx:1.27.4 0.0s=> [2/3] RUN mkdir /usr/share/nginx/html -p 0.3s=> [3/3] RUN echo "hello docker" > /usr/share/nginx/html/index.html 0.5s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:7c6af446d495a180d79138a4abfc5c2b99d9b0f639a9e7e3c6bc8b8 0.0s=> => naming to docker.io/library/mynginx:v1.0 0.0s1 warning found (use docker --debug to expand):- MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 2)[root@Docker-kd www]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mynginx v1.0 7c6af446d495 3 minutes ago 192MB
tomcat 11.0.5 e8c9a002d37b 7 weeks ago 467MB
nginx 1.27.4 4cad75abc83d 2 months ago 192MB
mysql 8.4.4 4a8a163431d3 3 months ago 769MB
busybox latest ff7a7936e930 7 months ago 4.28MB
# 运行
[root@Docker-kd www]# docker run --rm -it mynginx:v1.0 /bin/bash
root@741204f40a3d:/# pwd
/
root@741204f40a3d:/# ls
bin docker-entrypoint.d home media proc sbin tmp
boot docker-entrypoint.sh lib mnt root srv usr
dev etc lib64 opt run sys var
root@741204f40a3d:/# cd /usr/share/nginx/html/
root@741204f40a3d:/usr/share/nginx/html# ls
50x.html index.html
root@741204f40a3d:/usr/share/nginx/html# exit
exit
二、Dockerfile 指令详解
2.1 FROM
FROM 指令必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从FROM 语句开始,如果FROM语句没有指定镜像标签,则默认使用latest标签
FROM 指令用于为镜像文件构建过程指定基础镜像
FROM可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。
如果FROM语句没有指定镜像标签,则默认使用latest标签
- 命令格式如下:
FROM [--platform=<platform>] <image> [AS <name>]
或者
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
或者
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]FROM <image>[:<tag>]
FROM busybox:latest
2.2 MAINTAINER
指定维护者信,可以不写。
- 命令格式如下:
MAINTAINER <authtor's detail>例如:
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"
2.3 COPY
用于从 docker 宿主机复制文件或者目录至创建的新镜像中的路径下
,目标路径不存在时,会自动创建
- 命令格式如下:
COPY <src>... <dest>
或
COPY ["<src>",... "<dest>"]
# COPY文件:
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"
COPY index.html /data/web/html/#构建:
# 在dockerfile同级目录下准备好index.html文件
[root@localhost ~]# vim index.html
<h1>Busybox httpd server</h1>
[root@localhost ~]# docker build -t busyboxhttpd:v0.1 ./#运行:
[root@localhost ~]# docker run --name web1 --rm busyboxhttpd:v0.1 cat /data/web/html/index.html
<h1>Busybox httpd server</h1>
# COPY 目录
vim index.html
<h1>Busybox httpd server</h1>
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"
COPY index.html /data/web/html/
COPY yum.repos.d /etc/yum.repos.d/
注:如果是复制目录,则其
内部文件或子目录会被递归复制
,但目录自身不会被复制;需要把复制目录名字也写在容器中要复制的路径下!
#构建:
# 在dockerfile同级目录下准备好index.html文件
[root@localhost ~]# vim index.html
<h1>Busybox httpd server</h1>
[root@localhost ~]# docker build -t busyboxhttpd:v0.1 ./#运行:
[root@localhost ~]# docker run --name web1 --rm busyboxhttpd:v0.2 ls /etc/yum.repos.d/
along.repo
docker-ce.repo
epel-release-latest-7.noarch.rpm
epel-testing.repo
epel.repo
当使用本地目录为源目录时,推荐使用 COPY
2.4 ADD
ADD 指令类似于COPY指令,ADD支持使用TAR文件和URL路径,该命令将复制指定的路径下的内容到容器中的路径下
- 命令格式如下:
ADD <src> .. <dest>
或
ADD ["<src>".. "<dest>"]
与COPY的区别
1、Dockerfile 中的 COPY 指令和 ADD 指令都可以将主机上的资源复制或加入到容器镜像中,都是在构建镜像
的过程中完成的。
2、COPY 指令和 ADD 指令的区别在于是否支持从远程 URL 获取资源。
COPY 指令只能从执行 docker build 所在的主机上读取资源并复制到镜像中。
而 ADD 指令还支持通过 URL 从远程服务器读取资源并复制到镜像中。
3、满足同等功能的情况下,推荐使用 COPY 指令。ADD 指令更擅长读取本地 tar 文件并解压缩。
4、当要读取 URL 远程资源的时候,并不推荐使用 ADD 指令,而是建议使用 RUN 指令,在 RUN 指令中执行 wget 或 curl 命令。
- COPY 网上路径(URL)的tar包
# COPY 网上路径(URL)的tar包
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"
COPY index.html /data/web/html/
COPY yum.repos.d /etc/yum.repos.d/
ADD http://nginx.org/download/nginx-1.15.8.tar.gz /usr/local/src/
#注:ADD 的 是网上的nginx下载路径#构建:
[root@localhost img1]# docker build -t busyboxhttpd:v0.3 ./#运行:
[root@localhost ~]# docker run --name web1 --rm busyboxhttpd:v0.3 ls
/usr/local/src
nginx-1.15.8.tar.gz
- COPY 本地的路径的tar包
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"
COPY index.html /data/web/html/
COPY yum.repos.d /etc/yum.repos.d/
ADD nginx-1.15.8.tar.gz /usr/local/src/#构建:
# 在dockerfile同级目录下准备好yum.repos.d 目录
[root@localhost img1]# wget http://nginx.org/download/nginx-
1.15.8.tar.gz
[root@localhost img1]# docker build -t busyboxhttpd:v0.4 ./#运行:
[root@localhost ~]# docker run --name web1 --rm busyboxhttpd:v0.4 ls
/usr/local/src /usr/local/src/nginx-1.15.8
/usr/local/src:
nginx-1.15.8/usr/local/src/nginx-1.15.8:
CHANGES
CHANGES.ru
LICENSE
README
auto
conf
configure
contrib
html
man
src
2.5 WORKDIR
用于为 Dockerfile 中所有的 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指定设定工作目录
- 命令格式如下:
WORKDIR <dirpath>
在 Dockerfile 文件中,WORKDIR 指令可出现多次,其路径也可以为相对路径,不过,其是相对此前一个 WORKDIR 指令指定的路径
。另外,WORKDIR 也可调用由ENV指定定义的变量
eg.
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"COPY index.html /data/web/html/
COPY yum.repos.d /etc/yum.repos.d/WORKDIR /usr/local/ADD nginx-1.15.8.tar.gz ./src/
2.6 VOLUME
用于在镜像中创建一个 挂载点 目录,以挂载 Docker host 上的卷或其它容器上的卷
- 命令格式如下:
VOLUME <mountpoint>
或
VOLUME ["<mountpoint>"]
注:如果挂载点目录路径下此前在文件存在,docker run命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中
# eg.FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"COPY index.html /data/web/html/
COPY yum.repos.d /etc/yum.repos.d/WORKDIR /usr/local/
ADD nginx-1.15.8.tar.gz ./src/VOLUME /data/mysql
# 构建:
[root@localhost ~]# docker build -t busyboxhttpd:v0.5 ./# 运行:[root@localhost ~]# docker run --name web1 --rm -it busyboxhttpd:v0.5
/bin/sh
/usr/local #
--- 另打开一个终端,查询存储卷
[root@localhost ~]# docker inspect -f {{.Mounts}} web1
[{volume
b788b8a50d69953e2b086b3b54ba683154647319a481246cb7ab2ff927b21372
/var/lib/docker/volumes/b788b8a50d69953e2b086b3b54ba683154647319a48124
6cb7ab2ff927b21372/_data /data/mysql local true }]
2.7 EXPOSE
用于为容器打开指定要监听的端口以实现与外部通信
- 命令格式如下:
EXPOSE <port>[/ <protocol>] [<port>[/ <protocol>] ....
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"COPY index.html /data/web/html/
COPY yum.repos.d /etc/yum.repos.d/WORKDIR /usr/local/
ADD nginx-1.15.8.tar.gz ./src/VOLUME /data/mysqlEXPOSE 80/tcp
注:就算dockerfile 中有EXPOSE 指令暴露端口,但是不是真正的暴露;需要在启动容器时,使用 -p 选项真正的暴露端口
# 构建
[root@localhost img1]# docker build -t busyboxhttpd:v0.6 ./# 运行
[root@localhost ~]# docker run --name web1 -P --rm -it
busyboxhttpd:v0.6 /bin/httpd -f -h /data/web/html
--- 另打开一个终端,验证httpd 服务的80端口
[root@localhost ~]# docker inspect -f {{.NetworkSettings.IPAddress}}
web1 #查询容器的IP
172.17.0.2
[root@localhost ~]# curl 172.17.0.2:80
<h1>Busybox httpd server</h1>
--- 在宿主机通过暴露的端口访问httpd 服务
[root@localhost ~]# docker port web1
80/tcp -> 0.0.0.0:32768
[root@localhost ~]# curl 127.0.0.1:32768
<h1>Busybox httpd server</h1>
2.8 ENV
用于为镜像定义所需的环境变量,并可被 Dockerfile 文件中位于其后的其它指令(如 ENV、ADD、COPY 等)所调用,调用格式为$variable_ name 或 ${variable_ name}
- 命令格式如下:
ENV <key> <value>
或
ENV <key>=<value> ...
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"ENV DOC_ROOT=/data/web/html/ \WEB_SERVER_PACKAGE="nginx-1.15.8"COPY index.html ${DOC_ROOT}
COPY yum.repos.d /etc/yum.repos.d/WORKDIR /usr/local/
ADD ${WEB_SERVER_PACKAGE}.tar.gz ./src/VOLUME /data/mysqlEXPOSE 8080:80/tcp
# 构建
[root@localhost ~]# docker build -t busyboxhttpd:v0.7 ./# 运行
[root@localhost ~]# docker run --name web1 -P --rm -it
busyboxhttpd:v0.7 ls /usr/local/src /data/web/html
/data/web/html:
index.html/usr/local/src:
nginx-1.15.8
--- 也可以使用printenv 查看变量验证
[root@localhost ~]# docker run --name web1 --rm -it busyboxhttpd:v0.7
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
DOC_ROOT=/data/web/html/
WEB_SERVER_PACKAGE=nginx-1.15.8
--- 在启动容器时,使用docker run -e 设置修改变量
[root@localhost ~]# docker run --name web1 -e
WEB_SERVER_PACKAGE=nginx-1.15.7 --rm -it busyboxhttpd:v0.7 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
WEB_SERVER_PACKAGE=nginx-1.15.7
DOC_ROOT=/data/web/html/
2.9 RUN
用于指定 docker build 过程中运行的程序
,其可以是任何命令
- 命令格式如下:
RUN <command>
或
RUN ["<executable>", "<param1>", "<param2>"]
FROM busybox:latest
MAINTAINER "jock <jock@docker.com>"ENV DOC_ROOT=/data/web/html/ \WEB_SERVER_PACKAGE="nginx-1.15.8.tar.gz"COPY index.html ${DOC_ROOT}
COPY yum.repos.d /etc/yum.repos.d/WORKDIR /usr/local/
ADD http://nginx.org/download/${WEB_SERVER_PACKAGE} ./src/VOLUME /data/mysqlEXPOSE 8080:80/tcpRUN cd ./src && \tar -xf ${WEB_SERVER_PACKAGE}
# 构建
[root@localhost ~]# docker build -t busyboxhttpd:v0.8 ./# 运行
[root@localhost ~]# docker run --name web1 -P --rm -it
busyboxhttpd:v0.7 ls /usr/local/src
nginx-1.15.8
2.10 CMD
类似于 RUN 指令,CMD 指令也可用于运行任何命令或应用程序
-
不同点:
- RUN 指令运行于
映像文件构建过程
中,而 CMD 指令运行于基于 Dockerfile 构建出的新映像文件启动一个容器
时。 - CMD 指令首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止。不过,CMD 指定的命令其可以被 docker run 的命令行选项所覆盖。
- 在 Dockerfile 中可存在多个 CMD 指令,但仅最后一个会生效
- RUN 指令运行于
-
命令格式如下:
CMD <command>
或
CMD ["<executable>","<param1>","<param2>"]
或
CMD ["<param1>","<param2>"]
注:
前两种语法格式的意义同 RUN。
第三种则用于为 ENTRYPOINT 指令提供默认参数。
JSON 数组中,要使用双引号,单引号会出错。
FROM busybox
MAINTAINER "jock <jock@docker.com>"ENV WEB_DOC_ROOT="/data/web/html"RUN mkdir -p ${WEB_DOC_ROOT} && \echo "<h1>Busybox httpd server</h1>" > ${WEB_DOC_ROOT}/index.htmlCMD /bin/httpd -f -h ${WEB_DOC_ROOT}
# 构建
docker build -t busyboxhttpd:v1.1 ./# 运行
[root@localhost ~]# docker run --name web2 --rm -d busyboxhttpd:v1.1
20aa07198943887db51173e245392f75e3654525cb32242f2b04f0b3e007e47d
[root@localhost ~]# docker inspect -f {{.NetworkSettings.IPAddress}}
web2
172.17.0.2
[root@localhost ~]# curl 172.17.0.2
<h1>Busybox httpd server</h1>使用CMD定义的命令,在启动容器时,会被后面追加的指令覆盖;与下面ENTRYPOINT 指令对比
[root@localhost ~]# docker kill web2
web2
[root@localhost ~]# docker run --name web2 --rm busyboxhttpd:v1.1 ls /
bin
data
dev
etc
[root@localhost ~]# curl 172.17.0.2 被ls /覆盖,所以没有执行httpd服务
2.11 ENTRYPOINT
类似 CMD 指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序。
与 CMD 不同的是,由 ENTRYPOINT 启动的程序不会被 docker run 命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT 指定指定的程序。
不过,docker run 命令的 --entrypoint 选项的参数可覆盖 ENTRYPOINT 指令指定的程序
- 命令格式如下:
ENTRYPOINT <command>
ENTRYPOINT ["<executable>", "<param1>", "<param2>"]
注:
docker run 命令传入的命令参数会覆盖 CMD 指令的内容并且附加到 ENTRYPOINT 命令最后做为其参数使用。
Dockerfile 文件中也可以存在多个 ENTRYPOINT 指令,但仅有最后一个会生效
FROM busybox
MAINTAINER "jock <jock@docker.com>"ENV WEB_DOC_ROOT="/data/web/html"RUN mkdir -p ${WEB_DOC_ROOT} && \echo "<h1>Busybox httpd server</h1>" > ${WEB_DOC_ROOT}/index.htmlENTRYPOINT /bin/httpd -f -h ${WEB_DOC_ROOT}
# 构建
[root@localhost ~]# docker build -t busyboxhttpd:v1.2 ./# 运行
[root@localhost ~]# docker run --name web2 --rm busyboxhttpd:v1.2 ls /
发现是不会执行ls / 这个命令;仍然执行的是ENTRYPOINT中设置的命令;与上面CMD 指令对比;
[root@localhost ~]# curl 172.17.0.2 #httpd服务仍然执行,没有被ls / 指令覆盖
<h1>Busybox httpd server</h1>
三、Dockerfile 创建镜像与模板
3.1 Dockerfile 镜像
编写完成 Dockerfile 之后,可以通过 docker build
命令来创建镜像
基本的格式为:
docker build [选项] 内容路径
该命令将读取指定路径下(包括子目录)的 Dockerfile,并将该路径下的所有内容发送给Docker服务端,由服务端来创建镜像
# 如果使用非内容路径下的Dockerfile,可以通过-f选项来指定其路径
docker build -t lagou/ubuntu:v1 .
#docker build 最后的 . 号,其实是在指定镜像构建过程中的上下文环境的目录
3.2 镜像管理
Docker 镜像由一系列层组成。每层代表镜像的 Dockerfile 中的一条指令。除最后一层外的每一层都是只读的
# 该 Dockerfile 包含四个命令,每个命令创建一个层
FROM ubuntu:18.04 #从 ubuntu:18.04 镜像创建只读层
COPY . /app #从 Docker 客户端的当前目录添加一些文件
RUN make /app #使用命令构建应用程序 make
CMD python /app/app.py #最后一层指定在容器中运行什么命令
运行镜像并生成容器时,可以在基础层之上添加一个新的可写层(“容器层”)。对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此可写容器层
容器和镜像之间的主要区别是可写顶层
在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中。删除容器后,可写层也会被删除。基础镜像保持不变。
因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础镜像的访问,但具有自己的数据状态
- 查看镜像的分层信息
docker history 镜像ID
- 要查看正在运行的容器的大致大小,可以使用以下
docker ps -s
命令- size :用于每个容器的可写层的数据量(在磁盘上)
- virtual size :容器使用的只读图像数据加上容器的可写层使用的数据量size
多个容器可以共享部分或全部只读图像数据。从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层
# 可以通过Docker仓库来传输我们的镜像,也可以通过文件模式
docker save 镜像ID -o xxxx.tar 或(docker save xxxx > xxxx.tar)docker load -i xxxx.tar 或docker (docker load < xxxx.tar)docker diff 容器IDdocker commit 容器ID openlab/testimage:version4 # 直接保存容器docker commit --change='CMD ["apachectl", "-DFOREGROUND"]' -c "EXPOSE 80" 容器ID openlab/testimage:version4 # 将正在运行的容器添加几个层之后再保存】
3.3 Dockerfile 模板
通过最小化Dockerfile中RUN 单独命令的数量来减少镜像中的层数。可以通过将多个命令合并为RUN 一行并使用Shell的机制将它们组合在一起来实现此目的
#在镜像中创建两层
RUN apt-get -y update
RUN apt-get install -y python#在镜像中创建一层
RUN apt-get -y update && apt-get install -y python
3.3.1 Redhat 镜像模板
dockerfile-redhat:
# 依据哪个镜像创建
From centos:7.6.1810# 指定容器内部使用语言
ENV LANG="en_US.UTF-8"
ENV LC_ALL="en_US.UTF-8"
# 使用亚洲/上海时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 创建工作目录
RUN mkdir -p /data/apps /data/data /data/logs ; ln -s /data/apps /apps
# 安装字体
RUN yum install -y epel-release
RUN yum groupinstall -y "fonts"
RUN yum install -y kde-l10n-Chinese
# 安装openssl等依赖包
RUN yum install -y openssl openssl-devel
RUN yum install -y crontabs cronolog ntp
# 安装数据库依赖
RUN yum install -y mariadb-libs
RUN ln -s /usr/lib64/mysql/libmysqlclient.so.18
/usr/lib64/libmysqlclient_r.so.16
RUN yum install -y gcc cmake
RUN yum install -y lrzsz telnet net-tools file bind-utils less
RUN yum install -y jq xml2; yum clean all
RUN yum install -y expat-devel apr-devel ghostscript ghostscript-devel
# 运行容器时的默认命令
CMD ["/bin/bash"]
- 构建命令:
docker build -f dockerfile-redhat -t lagou/centos/7.6/centos .
docker run --rm -it lagou/centos/7.6/centos# 使用 Red Hat 9 作为基础镜像
FROM registry.access.redhat.com/ubi9/ubi:latest# 设置时区为亚洲/上海
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ >
/etc/timezone# 安装中文语言包
RUN dnf install -y kde-l10n-Chinese# 安装 openssl 和其他依赖包
RUN dnf install -y openssl openssl-devel# 安装 MySQL 8.4.4 的依赖
RUN dnf install -y \
libaio \
numactl-libs \
libtirpc \
libedit \
ncurses-compat-libs \
wget \
tar# 下载并安装 MySQL 8.4.4
RUN wget https://dev.mysql.com/get/Downloads/MySQL-8.4/mysql-8.4.4-
linux-glibc2.28-x86_64.tar.gz -O /tmp/mysql.tar.gz && \
tar -xzf /tmp/mysql.tar.gz -C /opt && \
mv /opt/mysql-8.4.4-linux-glibc2.28-x86_64 /opt/mysql && \
rm -f /tmp/mysql.tar.gz# 设置 MySQL 环境变量
ENV PATH=/opt/mysql/bin:$PATH# 创建 MySQL 数据目录
RUN mkdir -p /var/lib/mysql && \
chown -R mysql:mysql /var/lib/mysql# 初始化 MySQL
RUN mysqld --initialize-insecure --user=mysql --basedir=/opt/mysql --
datadir=/var/lib/mysql# 暴露 MySQL 默认端口
EXPOSE 3306# 设置容器启动时运行的命令
CMD ["mysqld", "--user=mysql"]
- 构建和运行命令:
docker build -t redhat9-mysql8.4.4 .
docker run -d --name mysql-container -p 3306:3306 redhat9-mysql8.4.4
3.3.2 JDK 镜像模板
dockerfile-jdk:
# 使用 Red Hat 9 作为基础镜像
FROM registry.access.redhat.com/ubi9/ubi:latest# 设置时区为亚洲/上海
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ >
/etc/timezone# 安装必要的工具
RUN dnf install -y \
wget \
tar \
gzip \
&& dnf clean all# 下载并安装 JDK 21
# 注意:请根据实际需求替换 JDK 21 的下载链接
RUN wget
https://download.java.net/java/GA/jdk21/fd2272bbf8e04c3dbaee1377009041
6c/35/GPL/openjdk-21_linux-x64_bin.tar.gz -O /tmp/jdk21.tar.gz && \
tar -xzf /tmp/jdk21.tar.gz -C /opt && \
mv /opt/jdk-21 /opt/jdk && \
rm -f /tmp/jdk21.tar.gz# 设置 JDK 环境变量
ENV JAVA_HOME=/opt/jdk
ENV PATH=$JAVA_HOME/bin:$PATH# 验证安装
RUN java -version# 设置工作目录
WORKDIR /app# 默认启动命令(可以根据需要修改)
CMD ["java", "-version"]
- 构建命令:
docker build -t redhat9-jdk21 .
docker run -it --rm redhat9-jdk21
3.3.3 Tomcat 镜像模板
dockerfile-jdk:
# 使用 Red Hat 9 作为基础镜像
FROM registry.access.redhat.com/ubi9/ubi:latest# 设置时区为亚洲/上海
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ >
/etc/timezone# 安装必要的工具
RUN dnf install -y \
wget \
tar \
gzip \
&& dnf clean all# 下载并安装 JDK 21
RUN wget
https://download.java.net/java/GA/jdk21/fd2272bbf8e04c3dbaee1377009041
6c/35/GPL/openjdk-21_linux-x64_bin.tar.gz -O /tmp/jdk21.tar.gz && \tar -xzf /tmp/jdk21.tar.gz -C /opt && \mv /opt/jdk-21 /opt/jdk && \rm -f /tmp/jdk21.tar.gz# 设置 JDK 环境变量
ENV JAVA_HOME=/opt/jdk
ENV PATH=$JAVA_HOME/bin:$PATH# 验证 JDK 安装
RUN java -version# 下载并安装 Tomcat 11.0.5
RUN wget https://archive.apache.org/dist/tomcat/tomcat-
11/v11.0.5/bin/apache-tomcat-11.0.5.tar.gz -O /tmp/tomcat.tar.gz && \tar -xzf /tmp/tomcat.tar.gz -C /opt && \mv /opt/apache-tomcat-11.0.5 /opt/tomcat && \rm -f /tmp/tomcat.tar.gz# 设置 Tomcat 环境变量
ENV CATALINA_HOME=/opt/tomcat
ENV PATH=$CATALINA_HOME/bin:$PATH# 创建 Tomcat 用户和组
RUN groupadd -r tomcat && \useradd -r -g tomcat -d $CATALINA_HOME -s /bin/false tomcat && \chown -R tomcat:tomcat $CATALINA_HOME# 暴露 Tomcat 默认端口
EXPOSE 8080# 设置工作目录
WORKDIR $CATALINA_HOME# 切换为 Tomcat 用户
USER tomcat# 启动 Tomcat
CMD ["catalina.sh", "run"]
- 构建:
# 构建
docker build -t redhat9-tomcat11.0.5 .# 运行
docker run -d --name tomcat-container -p 8080:8080 redhat9-
tomcat11.0.5
访问:
打开浏览器,访问 http://localhost:8080
,即可看到 Tomcat 的默认页面
- 注意事项:
- 如果需要部署自定义的 WAR 文件,可以将 WAR 文件复制到 /opt/tomcat/webapps 目录。
- 如果需要持久化 Tomcat 的日志或配置文件,可以使用 Docker 卷( volumes )。
- 如果需要调整 Tomcat 的配置,可以修改 /opt/tomcat/conf 目录下的配置文件。
四、Docker 数据持久化
- 创建一个卷,待后边使用
docker volume create test_volume
- 分别启动2个容器挂在上卷
# 在2个终端窗口启动2个容器
docker run -it --rm -v test_volume:/test nginx:1.27.4 /bin/bash
docker run -it --rm -v test_volume:/test nginx:1.27.4 /bin/bash
cd /test;
touch a.txt
ls /test# 在两个容器中我们均可以看到我们创建的文件,这样我们就可以做到了在多个容器之间实现数据共享
挂载在容器/test 目录内创建
Docker 不支持容器内安装点的相对路径。 多个容器可以在同一时间段内使用相同的卷。如果两个容器需要访问共享数据,这将很有用
注意:如果宿主机上的目录可以不存在,会在启动容器的时候创建
五、案例实战
5.1 构建nginx服务镜像
[root@docker ~]# ls
anaconda-ks.cfg elasticsearch.yml index3.html redis:7.4.2.tar.gz
centos_7.tar.gz index2.html nginx.1.27.4.tag.gz
[root@docker ~]# docker load -i centos_7.tar.gz
174f56854903: Loading layer 211.7MB/211.7MB
Loaded image: centos:7
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 65750d044ac8 3 months ago 117MB
busybox latest ff7a7936e930 7 months ago 4.28MB
centos 7 eeb6ee3f44bd 3 years ago 204MB[root@docker ~]# mkdir demo
[root@docker ~]# cd demo/
[root@docker demo]# ls
[root@docker demo]# vim Dockerfile
[root@docker demo]# ls
Dockerfile
[root@docker demo]# cat Dockerfile
FROM centos:7MAINTAINER jock 1062080730@qq.comRUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backupRUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repoRUN yum install -y wget make gcc gcc-c++ pcre-devel zlib-devel openssl-devel && \yum clean allRUN wget https://nginx.org/download/nginx-1.24.0.tar.gz && \tar -zxf nginx-1.24.0.tar.gz && \rm nginx-1.24.0.tar.gzWORKDIR /nginx-1.24.0
RUN ./configure --prefix=/usr/local/nginx --with-http_ssl_module && \make && \make installRUN mkdir -p /usr/local/nginx/html && \echo "hello jock | welcome to nginx! | version 1.0" > /usr/local/nginx/html/index.htmlRUN echo '#!/bin/bash' > /start.sh && \echo 'hostname > /usr/local/nginx/html/hostname.html' >> /start.sh && \echo '/usr/local/nginx/sbin/nginx -g "daemon off;"' >> /start.sh && \chmod +x /start.shEXPOSE 80CMD ["/start.sh"]
[root@docker demo]# docker build -t myapp1.0 .
[+] Building 147.5s (13/13) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 1.14kB 0.0s=> WARN: MaintainerDeprecated: Maintainer instruction is deprecated in favor of us 0.0s=> [internal] load metadata for docker.io/library/centos:7 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/9] FROM docker.io/library/centos:7 0.0s=> [2/9] RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.rep 0.2s=> [3/9] RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/r 0.4s=> [4/9] RUN yum install -y wget make gcc gcc-c++ pcre-devel zlib-devel openssl- 123.0s=> [5/9] RUN wget https://nginx.org/download/nginx-1.24.0.tar.gz && tar -zxf n 2.4s => [6/9] WORKDIR /nginx-1.24.0 0.0s => [7/9] RUN ./configure --prefix=/usr/local/nginx --with-http_ssl_module && 20.7s => [8/9] RUN mkdir -p /usr/local/nginx/html && echo "hello jock | welcome to n 0.1s => [9/9] RUN echo '#!/bin/bash' > /start.sh && echo 'hostname > /usr/local/ngi 0.1s => exporting to image 0.5s => => exporting layers 0.5s => => writing image sha256:9fc60c39bb353c478d9ddffd9599c5d5be1b3667a199a7b849aa0bf 0.0s => => naming to docker.io/library/myapp1.0 0.0s 1 warning found (use docker --debug to expand):- MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 3)[root@docker demo]# docker run --name myapp -d -p 80:80 myapp1.0:latest
ded4100304c454ca8871f9db9384eb2736e6382cfad289e270262e43d85616e9
[root@docker demo]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ded4100304c4 myapp1.0:latest "/start.sh" 11 seconds ago Up 10 seconds 0.0.0.0:80->80/tcp, [::]:80->80/tcp myapp
[root@docker demo]# curl localhost
hello jock | welcome to nginx! | version 1.0
[root@docker demo]# curl localhost/hostname.html
ded4100304c4
- nginx
[root@docker demo]# rm -rf *
[root@docker demo]# vim Dockerfile
[root@docker demo]# cat Dockerfile
FROM centos:7MAINTAINER jock 1062080730@qq.comRUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backupRUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repoRUN yum -y install epel-release && \yum -y install nginx && \yum clean allCOPY error.html /usr/share/nginx/html/error.htmlRUN echo 'server { error_page 404 /error.html; location = /error.html { root /usr/share/nginx/html; } }' > /etc/nginx/conf.d/default.confEXPOSE 80CMD ["nginx", "-g", "daemon off;"]
[root@docker demo]# vim error.html
[root@docker demo]# cat error.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>自定义错误页面</title><style>body {font-family: Arial, sans-serif;text-align: center;padding: 50px;}h1 {color: #333;}p {color: #666;}</style>
</head>
<body><h1>哎呀!页面未找到</h1><p>你所请求的页面可能已经被移除或者地址错误。</p>
</body>
</html>
[root@docker demo]# ls
Dockerfile error.html
[root@docker demo]# docker build -t error:1.0 .
[+] Building 34.6s (11/11) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 732B 0.0s=> WARN: MaintainerDeprecated: Maintainer instruction is deprecated in favor of us 0.0s=> [internal] load metadata for docker.io/library/centos:7 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/6] FROM docker.io/library/centos:7 0.0s=> [internal] load build context 0.0s=> => transferring context: 674B 0.0s=> CACHED [2/6] RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-B 0.0s=> CACHED [3/6] RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyu 0.0s=> [4/6] RUN yum -y install epel-release && yum -y install nginx && yum c 34.3s=> [5/6] COPY error.html /usr/share/nginx/html/error.html 0.0s=> [6/6] RUN echo 'server { error_page 404 /error.html; location = /error.html { r 0.2s=> exporting to image 0.2s => => exporting layers 0.2s => => writing image sha256:f658b54fa24cce7c0322ff844402e5917495b98650df3e43e78ff33 0.0s => => naming to docker.io/library/error:1.0 0.0s 1 warning found (use docker --debug to expand):- MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 3)[root@docker demo]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
error 1.0 f658b54fa24c 28 seconds ago 262MB
myapp 1.0 c48517613d32 7 minutes ago 210MB
myapp1.0 latest 9fc60c39bb35 15 minutes ago 368MB
kibana 8.17.3 7dcda0c152aa 7 weeks ago 1.18GB
mysql 8.4.4 4a8a163431d3 3 months ago 769MB
centos 7 eeb6ee3f44bd 3 years ago 204MB
[root@docker demo]# docker run --name error -d -p 80:80 error:1.0
aeb56da0b8e066c9b135937f8e10ac934ba5e3fe3f87cd126b75e8a2e4409c04
[root@docker demo]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aeb56da0b8e0 error:1.0 "nginx -g 'daemon of…" 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, [::]:80->80/tcp error
[root@docker demo]# curl localhost/error.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>自定义错误页面</title><style>body {font-family: Arial, sans-serif;text-align: center;padding: 50px;}h1 {color: #333;}p {color: #666;}</style>
</head>
<body><h1>哎呀!页面未找到</h1><p>你所请求的页面可能已经被移除或者地址错误。</p>
</body>
</html>
- go
[root@docker demo]# vim main.go
[root@docker demo]# cat main.go
package mainimport ("log""net/http"
)func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello, HTTPS!"))})log.Println("Server running on https://localhost:443")err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)if err != nil {log.Fatal("ListenAndServeTLS: ", err)}
}[root@docker demo]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 \-keyout server.key -out server.crt \-subj "/C=CN/ST=Demo/L=Demo/O=Demo/CN=localhost"
.............+++++++++++++++++++++++++++++++++++++++*..+...+..+............+.+........+....+..+++++++++++++++++++++++++++++++++++++++*.+.......+.....+......+....+..............+.+.........+...........+.+..+............+.+..+......+.......+...+.........+..+.+.....+....+...+........+...+.......+........+......+.+...............+.....+....+......+............+..+.........+....+.....................+............+..+.+..+....+........+............+.+.................+..........+..+.+.....+...+......+.............+......+......+...+...+..+......+....+..+.+.........+.....+.............+..+.+............+..+....+..+...+.........+.+..+....+.....+.............+......+......++++++
....+....+...+...........+....+...+..+++++++++++++++++++++++++++++++++++++++*.........+....+......+.................+...+.+...+......+..+++++++++++++++++++++++++++++++++++++++*.+.+.........+......+......+.........+.....+...+...+...+.+........+..........+..+.........+.........+.......+.....+..........+...+......+..+...+.+...+...+..+.+...........+.+........+......+................+............+..+.............+.....+...+...+.....................+..........+..................+...+..................+..+.......+..+....+.....................+...+...+..............+.+...............+..+......+.........+......+.+.....+......+...+....+..+...+...+............+.+......+......+...+...........+.+...+............+..+...+...+......+.+..+............+.+..............+....+..+....+..............+.+...+..........................++++++
-----[root@docker demo]# vim Dockerfile
[root@docker demo]# cat Dockerfile
FROM golang:alpineMAINTAINER jock 1062080730@qq.comWORKDIR /appCOPY main.go .RUN go build -o main main.goCOPY server.crt .
COPY server.key .EXPOSE 443CMD ["./main"]