前言
大家好,我是洋子。上一篇文章《docker 快速入门指南》介绍了Docker的概念,安装Docker方法以及Docker的常用命令,并且在上篇内容当中,我们都是使用docker pull
命令直接拉取镜像仓库里面提供的官方镜像
比如拉取mysql镜像就使用docker pull mysql
,拉取centos镜像就使用docker pull centos
,拉取tomcat镜像就使用docker pull tomcat
然而官方提供的tomcat镜像,因为webapps
目录为空而不能直接使用,还需要在其生成的tomcat容器做相应文件修改后才能正常使用
所以实际使用当中,免不了对官方镜像进行修改后,制作成我们自己的镜像,用自己更改过的镜像创建的容器才能做到即开即用
那如何修改并制作docker镜像呢,下面教大家两个方法,一个是使用docker commit
命令,另一个是使用Dockerfile
文件
使用docker commit命令
使用docker commit
命令可以从正在运行的容器当中创建一个新的镜像,这个运行的容器已经安装好我们必要的环境或者修改好相应的配置,这样生成的镜像就可以直接使用
以修改tomcat
镜像为例,说明docker commit
命令的用法
- 拉取
tomcat
镜像
docker pull tomcat
- 查看已经拉取的镜像,使用
docker images
命令,可以看到列表当中已经出现刚下载的tomcat
镜像
[root@yangzi ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest fb5657adc892 7 months ago 680MB
- 创建容器,使用
docker run
命令启动tomcat,并将本机的8080端口映射到容器里面的8080端口
# 从Docker Hub 下载tomcat镜像到本地并运行 -it 交互终端 -p 端口映射
docker run -it -p 8080:8080 tomcat
#运行结果(看到如下信息说明Tomcat启动成功)
13-Aug-2022 09:30:24.586 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [1519] milliseconds
13-Aug-2022 09:30:24.728 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
13-Aug-2022 09:30:24.728 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.0.14]
13-Aug-2022 09:30:24.752 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
13-Aug-2022 09:30:24.780 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [193] milliseconds
- 现在我们用浏览器访问一下,并没有出现熟悉的tomcat欢迎页面,而是报了404,说明直接运行官方提供的tomcat镜像生成的容器存在问题
- 用
ctrl c
终端命令行,加上-d
参数,重新以后台方式运行tomcat容器,这样我们就能进入到容器,对容器进行修改
# 后台方式启动tomcat容器
docker run -d -p 8080:8080 --name tomcat01 tomcat
# 进入tomcat容器
docker exec -it tomcat01 /bin/bash
- 经过排查是发现
webpapps
目录为空,但webapps.dist
里有对应文件,处理方式是拷贝webapps.dist
里对应文件到webpapps
目录即可
- 在tomcat根目录执行拷贝命令
cp -r webapps.dist/* webapps
- 在浏览器再次输入ip和8080端口访问,发现tomcat欢迎页面有了
- 最后使用
docker commit
命令生成新的镜像
docker commit -a="yangzi" -m="new tomcat" d6cd9ac49dc2 tomcat02:1.1
#参数含义
-a : 提交的镜像作者
-m : 提交时的说明文字
d6cd9ac49dc2 :容器id,可以使用docker ps命令获取
tomcat02:1.1 :镜像名以及版本tag
- 使用
docker images
查看镜像列表,可以看到名字叫tomcat02
即为我们新创建的镜像
[root@yangzi ~]# docker commit -a="yangzi" -m="new tomcat" d6cd9ac49dc2 tomcat02:1.1
sha256:147e2244bc3912d60b33cc16c07b81eead8099bdfd35365b8b1ef37846896a94
[root@yangzi ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat02 1.1 147e2244bc39 31 seconds ago 684MB
tomcat latest fb5657adc892 7 months ago 680MB
hello-world latest feb5d9fea6a5 10 months ago 13.3kB
centos latest 5d0da3dc9764 11 months ago 231MB
- 验证效果,将之前运行的
tomcat
容器停止(不然会占用8080端口),停止容器使用命令docker stop 容器id
,容器id 用docker ps
命令查看
然后运行新创建tomcat02镜像所生成的容器,注意一点就是docker run
命令tomcat02名字后面得带上版本Tag 1.1(因为我们在docker commit
的时候设置了版本tag为1.1),不然默认拉取的是名为tomcat02镜像的latest
版本会提示无法找到该镜像
docker run -it -p 8080:8080 tomcat02:1.1
这一次无需做任何修改,在浏览器访问,就能看到Tomcat的欢迎页面
这就是使用docker commit
命令修改镜像的方法,不过使用该命令还是有些繁琐和局限性,因为docker commit
命令的前提是必须有运行当中的容器,而使用Dockerfile
文件就不需要正在运行的容器,也能自定义创建镜像,在企业实际工作当中,我们也一般使用Dockerfile
来构建镜像
使用Dockerfile
如果说容器是现成的商品房,镜像就是样板房,那么Dockerfile
文件就是构建房子的设计图纸,它来规定如何建造地基、铺设水电、开窗搭门等动作
比起容器、镜像来说,Dockerfile
非常普通,它就是一个纯文本,里面记录了一系列的构建指令,比如选择基础镜像、拷贝文件、运行脚本等等,而 Docker 顺序执行这个文件里的所有步骤,最后就会创建出一个新的镜像出来
写Dockerfile
文件就像是我们操作Linux系统来部署服务一样,Dockerfile
的每个命令基本都有类似功能的Shell
命令,大家可以把写Dockerfile
当做是手动构建环境和部署应用,把我们在Linux上需要做的事情使用Dockerfile的语法翻译一遍,Dockerfile就基本完成了,这样就很容易理解Dockerfile真正要做的是什么事情了
我们来看一个简单的 Dockerfile 实例:
代码语言:javascript复制# 文件名为 Dockerfile
# 选择基础镜像
FROM centos
# 启动容器时默认运行的命令
CMD echo "hello world"
写好Dockerfile
文件后,使用 docker build
命令来创建出镜像
docker build -f Dockerfile -t "yangzi_centos" .
# 参数含义
-t 镜像名:tag
. 表示当前路径的Dockerfile文件
看到Successfully built
信息就说明构建镜像成功了
[root@yangzi work]# docker build -f Dockerfile -t "yangzi_centos" .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM centos
---> 5d0da3dc9764
Step 2/2 : CMD echo "hello world"
---> Using cache
---> 5a9ec5ee43d3
Successfully built 5a9ec5ee43d3
Successfully tagged yangzi_centos:latest
使用 docker images
可以查看到刚刚使用Dockerfile
构建的镜像
[root@yangzi work]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
yangzi_centos latest 5a9ec5ee43d3 6 minutes ago 231MB
运行一下这个镜像生成的容器,CMD
命令的参数是指定容器启动时要执行的命令,所以这里打印了Hello World
注意在Dockerfile
里面允许有多个CMD
命令,但只有最后一个会执行(比如有两个CMD命令,只会执行最后一个),另外CMD
里命令可以被 docker run 之后的参数替换,如下面在docker run命令追加了ls
命令,就只会执行ls
命令,不再执行CMD
里的echo
命令
[root@yangzi work]# docker run -it 5a9ec5ee43d3
hello world
[root@yangzi work]# docker run -it 5a9ec5ee43d3 ls
bin etc lib lost found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
避免这种情况可以在Dockerfile
里面使用ENTRYPOINT
命令,它就不会像CMD
里面的命令一样被覆盖,而是可以docker run
时追加命令,具体用法可以百度,这里不再展开
Dockerfile命令总结
在Dockerfile
当中,除非上述例子里提到的FROM
、CMD
、ENTRYPOINT
命令,还包含了许多其他命令,下面是一些常用命令总结
FROM:
#指定基础镜像,所有构建的镜像都必须有一个基础镜像,且 FROM 命令必须是 Dockerfile 的第一个命令
FROM <image> [AS <name>] 指定从一个镜像构建起一个新的镜像名字
FROM <image>[:<tag>] [AS <name>] 指定镜像的版本 Tag
示例:FROM mysql:5.0 AS database
MAINTAINER:
#镜像维护人的信息
MAINTAINER <name>
示例:MAINTAINER Yangzi Yangzi@qq.com
RUN:
#构建镜像时要执行的命令,RUN 通常会是 Dockerfile 里最复杂的指令,会包含很多的 Shell 命令,但 Dockerfile 里一条指令只能是一行,所以有的 RUN 指令会在每行的末尾使用续行符 ,命令之间也会用 && 来连接
RUN <command>
示例:RUN [executable, param1, param2]
RUN例子
RUN apt-get update
&& apt-get install -y
build-essential
curl
make
unzip
&& cd /tmp
&& curl -fSL xxx.tar.gz -o xxx.tar.gz
&& tar xzf xxx.tar.gz
&& cd xxx
&& ./config
&& make
&& make clean
=
ADD:
#将本地服务器的文件添加复制到容器中去,压缩包会解压,可以访问网络上的文件,会自动下载
ADD <src> <dest>
示例:ADD *.js /app 添加 js 文件到容器中的 app 目录下
COPY:
#功能和 ADD 一样(类似linux当中的cp命令),只是复制,不会解压或者下载文件
CMD:
#启动容器后执行的命令,和 RUN 不一样,RUN 是在构建镜像是要运行的命令
当使用 docker run 运行容器的时候,这个可以在命令行被覆盖
示例:CMD [executable, param1, param2]
ENTRYPOINT:
#也是执行命令,和 CMD 一样,只是这个命令不会被命令行覆盖
ENTRYPOINT [executable, param1, param2]
示例:ENTRYPOINT [donnet, myapp.dll]
LABEL:
#为镜像添加元数据,key-value 形式
LABEL <key>=<value> <key>=<value> ...
示例:LABEL version=1.0 description=这是一个web应用
EXPOSE:
#暴露对外的端口(容器内部程序的端口,虽然会和宿主机的一样,但是其实是两个端口)
EXPOSE <port>
示例:EXPOSE 80
容器运行时,需要用 -p 映射外部端口才能访问到容器内的端口
VOLUME:
#指定数据持久化的目录,官方语言叫做挂载
VOLUME /var/log
#指定容器中需要被挂载的目录,会把这个目录映射到宿主机的一个随机目录上,实现数据的持久化和同步
VOLUME [/var/log,/var/test.....]
#指定容器中多个需要被挂载的目录,会把这些目录映射到宿主机的多个随机目录上,实现数据的持久化和同步
VOLUME /var/data var/log
#指定容器中的 var/log 目录挂载到宿主机上的 /var/data 目录,这种形式可以手动指定宿主机上的目录
WORKDIR:
#设置工作目录,设置之后 ,RUN、CMD、COPY、ADD 的工作目录都会同步变更
WORKDIR <path>
示例:WORKDIR /app/test
USER:
#指定运行命令时所使用的用户,为了安全和权限起见,根据要执行的命令选择不同用户
USER <user>:[<group>]
示例:USER test
ENV:
#设置环境变量,有些容器运行时会需要某些环境变量
ENV <key> <value> 一次设置一个环境变量
ENV <key>=<value> <key>=<value> <key>=<value> 设置多个环境变量
示例:ENV JAVA_HOME /usr/java1.8/
ARG:
#设置构建镜像是要传递的参数,ARG指令定义的参数,在docker build命令中以--build-arg a_name=a_value形式赋值
ARG <name>[=<value>]
ARG a_name=sss
ENV与ARG的区别:
ARG 创建的变量只在镜像构建过程中可见,容器运行时不可见,而 ENV 创建的变量不仅能够在构建镜像的过程中使用,在容器运行时也能够以环境变量的形式被应用程序使用
Dockerfile官方文档参考 https://docs.docker.com/engine/reference/builder/
Dockerfile 综合案例
再来看一个Dockerfile
文件的综合性例子,用来自定义镜像tomcat
,步骤如下
- linux终端下新建目录
mkdir -p yangzi/build/tomcat
- 在上述目录下新建文件
touch read.txt
(此步骤仅仅为了测试Dockerfile 里的COPY命令) - 将
JDK
和tomcat
安装的压缩包拷贝进上一步目录( JDK可从官网下载 https://www.oracle.com/java/technologies/javase-downloads.html,选择Linux x64 Compressed Archive版本)
Apache官网下载Tomcat路径:https://tomcat.apache.org
下载到本地计算机后,使用FileZilla
或者Linux rz命令
上传到远程服务器上
- 在
/yangzi/build/tomcat
目录下新建一个Dockerfile
文件,Dockerfile
文件内容如下:
# 需要打上镜像tag,拉取centos 7的镜像
FROM centos:7
MAINTAINER yangzi<12345678@qq.com> #把宿主机当前上下文的read.txt拷贝到容器/usr/local/路径下
COPY read.txt /usr/local/cincontainer.txt
#把java与tomcat添加到容器中
ADD jdk-18_linux-x64_bin.tar.gz /usr/local/
ADD apache-tomcat-10.0.23.tar.gz /usr/local/
#安装vim编辑器
RUN yum -y install vim
#设置工作访问时候的WORKDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk-18.0.2.1
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-10.0.23
ENV CATALINA_BASE /usr/local/apache-tomcat-10.0.23
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin #容器运行时监听的端口
EXPOSE 8080
#启动时运行tomcat,以下3种启动方式任意选择一种,startup.sh底层也是调用了catalina.sh
# ENTRYPOINT ["/usr/local/apache-tomcat-10.0.23/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-10.0.23/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-10.0.23/bin/startup.sh && tail -F /usr/local/apache-tomcat-10.0.23/logs/catalina.out
踩坑问题汇总:
(1)在Dockerfile
当中,不要直接使用FROM centos
命令,会拉取到centos 8
镜像,但镜像列表当中会找不到appstream
,会报以下错误,所以需要打上tag,使用FROM centos:7
命令,拉取 centos7
镜像
Step 6/15 : RUN yum -y install vim
---> Running in ea1aca98f262
CentOS Linux 8 - AppStream 62 B/s | 38 B 00:00
Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist
(2)有可能大家下载的tomcat
和jdk
版本跟我在Dockerfile
里面写的不一样,在Dockerfile
当中,涉及tomcat
和jdk
版本,均需要进行替换,如对应的压缩包名称,以及环境变量路径
- 编译
Dockerfile
文件,在本目录执行可以不写Dockerfile
文件名称,以下命令执行后,会生成一个名为diytomcat
的镜像,可以使用docker images
查看
docker build -t diytomcat .
看到下面的信息,说明镜像已经构建成功
代码语言:javascript复制[root@yangzi tomcat]# docker build -t diytomcat .
Sending build context to Docker daemon 193.1MB
Step 1/15 : FROM centos:7
7: Pulling from library/centos
2d473b07cdd5: Pull complete
Digest: sha256:9d4bcbbb213dfd745b58be38b13b996ebb5ac315fe75711bd618426a630e0987
Status: Downloaded newer image for centos:7
---> eeb6ee3f44bd
Step 2/15 : MAINTAINER yangzi<12345678@qq.com> #把宿主机当前上下文的read.txt拷贝到容器/usr/local/路径下
---> Running in dedcf313686f
Removing intermediate container dedcf313686f
---> 15188be5ddaa
...
Step 14/15 : EXPOSE 8080
---> Running in ec11bd1062bf
Removing intermediate container ec11bd1062bf
---> 5629d69f3d78
Step 15/15 : CMD /usr/local/apache-tomcat-10.0.23/bin/startup.sh && tail -F /usr/local/apache-tomcat-10.0.23/bin/logs/catalina.out
---> Running in 5e71fd77264f
Removing intermediate container 5e71fd77264f
---> cdbc8478b7b6
Successfully built cdbc8478b7b6
Successfully tagged diytomcat:latest
docker run
命令运行容器
docker run -d -p 9090:8080 --name mydiytomcat -v /root/yangzi/build/tomcat/test:/usr/local/apache-tomcat-10.0.23/webapps/test -v /root/yangzi/build/tomcat/tomcat9logs/:/usr/local/apache-tomcat-10.0.23/logs --privileged=true diytomcat
--name
表示容器名-d
参数表示后台运行-v
参数后面跟表示本地持久化保存容器数据的挂载目录,挂载目录共有两个,一个是webapps目录下的test服务,另一个是存放log文件,容器里产生的数据会自动同步到本地的指定目录下,即使容器被删除,本地目录还是可以找到相应的数据,防止数据丢失- 命令最后的
diytomcat
表示为镜像名 - 注意Docker挂载主机目录Docker访问出现cannot open directory .: Permission denied的解决办法:在挂载目录后多加一个
--privileged=true
参数即可(上面的命令已添加)
- 在linux服务器上测试访问
curl localhost:9090
,看到正常返回tomcat
的HTML网页即可
- 若无法正常返回,可以查看日志目录下文件
catalina.out
排查问题,路径为/root/yangzi/build/tomcat/tomcat9logs/catalina.out
- 我自己遇到过下载JDK版本错误(误下载为
Arm 64
版本,需要下载x64
版本),导致无法执行二进制文件/usr/local/apache-tomcat-10.0.23/bin/catalina.sh: line 491: /usr/local/jdk-18.0.2.1/bin/java: cannot execute binary file
- 最后可以进行Web服务test的发布,创建
WEB-INF
目录填写web.xml
,添加相应的jsp
文件
[root@yangzi test]# pwd
/root/yangzi/build/tomcat/test
[root@yangzi test]# mkdir WEB-INF
[root@yangzi test]# cd WEB-INF
[root@yangzi WEB-INF]# vim web.xml
[root@yangzi WEB-INF]# cd ..
[root@yangzi test]# vim a.jsp
web.xml
的内容如下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>test</display-name>
</web-app>
a.jsp
的内容如下
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>hello,yangzi</title> </head>
<body>
-----------welcome------------
<%=" my docker tomcat,yangzi "%>
<br>
<br>
<% System.out.println("-------my docker tomcat-------");%>
</body>
</html>
- 在服务器上测试验证test服务
curl localhost:9090/test/a.jsp
,正常返回jsp当中的内容即可,也可进入本地挂载日志目录/root/yangzi/build/tomcat/tomcat9logs/
下,查看tomcat
访问日志cat catalina.out
结束语
本文介绍了使用DockerFile
自定义Docker镜像方法,但它始终操作的是单个镜像,并生成单个容器,在实际工作当中,我们需要使用到多个容器(如Mysql容器,Nginx容器等),去部署完整的Web服务,这时候需要用到Docker-Compose
容器编排工具,下期继续为大家介绍
学习到这里Docker里面的大部分命令大家都基本使用过了,下篇文章也是Docker
系列的最后一篇,会完整的给大家总结Docker
命令