将微服务运行在Docker上
使用Dockerfile构建Docker镜像
Dockerfile构建Docker镜像。Dockerfile是一个文本文件,其中包含了若干条指令,指令描述了构建镜像的细节。
1)准备一个文件,名为Dockerfile:
FORM nginx
RUN echo '<h1>test</h1>' > /usr/share/nginx/html/index.html
FORM、RUN都是Dockerfile的指令。FROM指令用于指定基础镜像,RUN指令用于执行命令。
2)在Dockerfile所在路径执行以下命令构建镜像
docker build -t nginx:my .
其中,命令最后的点(.)用于路径参数传递,表示当前路径。
3)执行以下命令,即可使用该镜像启动一个Docker容器
docker run -d -p 92:80 nginx:my
4)访问
Dockerfile常用指令
ADD复制文件
ADD指令用于复制文件
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]
从src目录复制文件到容器dest。其中src可用是Dockerfile所在目录的相对路径,也可以是一个URL,还可以是一个压缩包。
注:src必须在构建的上下文内,不能使用例如ADD …/somehine /something这样的命令,因为docker build命令首先会将上下文路径和其子目录发送到docker daemon。
如果src是一个URL,同时dest不以斜杠结尾,dest将被视为文件,src对应内容文件将被下载到dest。
如果src是一个URL,同时dest以斜杠结尾,dest将被视为目录,src对应内容将被下载到dest目录。
如果src是一个目录,那么整个目录下的内容将被复制,包括文件系统元数据。
如果文件是可识别的压缩包格式,则docker会自动解压。
ADD microservice-discovery-eureka.jar app.jar
ARG设置构建参数
ARG指令用于设置构建参数,类似于ENV。和ENV不同的是,ARG设置的是构建时的环境变量,在容器运行时是不会存在这些变量的。
ARG user1=someuser
CMD容器启动命令
CMD指令用于为执行容器提供默认值。每个Dockerfile只有一个CMD命令,如果指定了多个CMD命令,那么只有最后一条会被执行,如果启动容器时指定了运行的命令,则会覆盖CMD指定的命令。
CMD ["executable","param1","param2"](推荐使用)
CMD ["param1","param2"] (为ENTRYPOINT指令提供预设参数)
CMD command param1 param2 (在shell中执行)
CMD echo "This is a test." | wc -
COPY复制文件
COPY <src>... <dest>
COPY ["<src>",... "<dest>"]
复制本地端的src到容器的dest。COPY指令和ADD指令类似,COPY不支持URL和压缩包
ENTRYPOINT入口点
ENTRYPOINT ["executable","param1","param2"]
ENTRYPOINT command param1 param2
ENTRYPOINT和CMD指令的目的一样,都是指定Docker容器启动时执行的命令,可多次设置,但只有最后一个有效。
ENV设置环境变量
ENV指令用于设置环境变量
ENV <key> <value>
ENV <key>=<value> ...
ENV JAVA_JOME /path/to/java
EXPOSE声明暴露的端口
EXPOSE指令用于声明在运行时容器提供服务的端口,格式为EXPOSE <port> [<port>....]
需要注意的是,这只是一个声明,运行时并不会因为该声明就打开相应端口,该指令的作用主要是帮助镜像使用者理解该镜像服务的守护端口;其次是当运行时使用随机映射时,会自动映射EXPOSE的端口。
#声明暴露一个端口示例
EXPOSE port1
#相应的运行容器使用的命令
docker run -p port1 image
#声明暴露多个端口示例
EXPOSE port1 port2 port3
#相应的运行容器使用的命令
docker run -p port1 -p port2 -p port3 image
#也可指定需要映射到宿主机器上的端口号
docker run -p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image
FROM指定基础镜像
使用FROM指令基础镜像,FROM指令有点像Java里面的extends关键字。需要注意的是,FROM指令必须指定且需要写在其他指令之前。FROM指令后的所有指令都依赖于该指定的镜像。
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
LABEL为镜像添加元数据
LABEL指令用于为镜像添加元数据。
格式为LABEL <key>=<value> <key>=<value> <key>=<value>....
使用"""和”\“转换命令行
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.lable-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
MAINTAINER指定维护者的信息
MAINTAINER指令用于指定维护者的信息,用于为Dockerfile署名。
格式为MAINTAINER <name>
RUN执行命令
RUN <command>
RUN ["executable","param1","param2"]
RUN <command>
在shell终端中运行,在Linux中默认是/bin/sh -c,在Windows中是cmd /s /c使用这种格式,使用这种格式,就像直接在命令行中输入命令一样。RUN ["executable","param1","param2"]
使用exec执行,这种方式类似于函数调用。指定其他终端可以通过该方式操作,例如RUN ["/bin/bash","-c","echo hello"]
,该方式必须使用双引号["]而不能使用单引号[‘],因为该方式会被转换成一个JSON数组。
USER设置用户
该指令用于设置启动镜像时的用户或者UID,写在该指令后的RUN,CMD以及ENTRYPOINT指令都将使用该用户执行命令。
USER daemon
VOLUME指定挂载点
该指令使容器中的一个目录具有持久化存储的功能,该目录可被容器本身使用,也可共享给其他容器。当容器中的应用有持久化数据的需求时可以在Dockerfile中使用该指令。
VOLUME /data
WORKDIR 指定工作目录
WORKDIR /path/to/workdir
切换目录指令,类似于cd命令,写在该指令后的RUN、CMD以及ENTRYPOINT指令都将该目录作为当前目录,并执行相应的命令。
其他
Dockerfile还有一些其他的指令,例如STOPSINGAL、HEEALTHCHECK、SHELL等。不常用略。
使用Dockerfile构建镜像
准备工作
以项目microservice-discovery-eureka为例,首先执行以下命令,将项目构建成jar包:microservice-discovery-eureka-0.0.1-SNAPSHOT.jar
mvn clean package #使用Maven打包项目
镜像构建
1)在jar包所在目录,创建名为Dockerfile的文件。
touch Dockerfile
在Dockerfile中添加以下内容。
#基于哪个镜像
FROM java:8
#将本地文件夹挂载到当前容器
VOLUME /tmp
#复制文件到容器
ADD microservice-discovery-eureka-1.0-SNAPSHOT.jar app.jar
RUN bash -c 'touch /app.jar'
#声明需要暴露的端口
EXPOSE 8761
#配置容器启动后执行的命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
3)使用docker build命令构建镜像
docker build -t example/microservice-discovery-eureka:0.0.1 .
#格式:docker build -t 仓库名称/镜像名称(:标签) Dockerfile的相对位置
测试
1)启动镜像
docker run -d -p 8761:8761 example/microservice-discovery-eureka:0.0.1
2)访问http://Docker宿主机IP:8761/,可正常显示Eureka Server首页。
使用Docker Registry管理Docker镜像
至此,已经构建了Docker镜像,并将微服务运行在Docker之上。但是,一个完整的应用系统可能包含上百个微服务,那就可能对应着上百个镜像,如果考虑各个微服务的版本,那么可能会构建更多的镜像。这些镜像该如何管理呢?
使用Docker Hub管理镜像
Docker Hub是Docker官方维护的Docker Registry,上面存放着很多优秀的镜像。不仅如此,Docker Hub还提供认证、工作组结构、工作流工具、构建触发器等工具来简化工作。
注册与登录
Docker Hub的使用非常简单,只需注册一个Docker Hub账号,就可正常使用了。登录后,可看到Docker Hub的主页。
也可使用docker login命令登录Docker Hub。输入该命令并按照提示输入账号和密码,即可完成登录。
创建仓库
单击Docker Hub主页上的Create Repository按钮,按照提示填入信息即可创建一个仓库。只需填入相关信息,并单击Create按钮,就可创建一个名为microservice-discovery-eureka的公共仓库。
推送镜像
下面来将前文构建的镜像推送到Docker Hub。使用以下命令
docker push example/microservice-discovery-eureka:0.0.1
经过一段时间的等待,就可推送成功。这样,就可在Docker Hub查看已推送的镜像。
使用私有仓库管理镜像
很多场景下,需使用私有仓库管理Docker镜像。相比Docker Hub,私有仓库有以下优势
- 节省带宽,对于私有仓库中已有的镜像,无须从Docker Hub下载,只需从私有仓库中下载即可。
- 更加安全。
- 便于内部镜像的统一管理
搭建私有仓库
Docker Registry 2.0的搭建非常简单,只需执行以下命令即可新建并启动一个Docker Registry 2.0
docker run -d -p 5000:5000 --restart=always --name registry2 registry:2
将镜像推送到私有仓库
前文使用了docker push命令将镜像推送到了Docker Hub,现在将前文构建的容器推送到私有仓库
docker push localhost:5000/example/microservice-discovery-eureka:0.0.1
执行以上命令,发现推送并没有成功,且提示以下内容:
Docker Hub默认的Docker Registry,所以example/microservice-discovery-eureka:0.0.1相当于docker.io/example/microservice-discovery-eureka:0.0.1。因此,想要将推送到私有仓库,需要修改镜像标签。
docker tag example/microservice-discovery-eureka:0.0.1 localhost:5000/example/microservice-discovery-eureka:0.0.1
修改标签后,再次执行
注:Docker Registry2.0需要Docker版本高于1.6
还可为私有仓库配置域名、SSL登录、认证等。
Docker Registry 2.0能够满足大部分场景下的需求,但它不包含界面、用户管理、权限控制等功能。如果想要使用这些功能,可使用Docker Trusted Registry。
使用Maven插件构建Docker镜像
Maven是一个强大的项目管理与构建工具。如果可以使用Maven构建Docker镜像,工作就能得到进一步的简化。
这里使用一款由Spotify公司开发的Maven插件
打开Docker远程API
# vim /usr/lib/systemd/system/docker.service
[Service]
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock
“unix:///var/run/docker.sock”:unix socket,本地客户端将通过这个来连接 Docker Daemon。
“tcp://0.0.0.0:2375”:tcp socket,表示允许任何远程客户端通过 2375 端口连接 Docker Daemon。
可以看到已经docker启动了一个新的守护进程。
设置白名单
vim /etc/docker/daemon.json
{
"insecure-registries":["192.168.238.10:5000"], #是registries不是registry
}
将java8上传到私有仓库里
启动仓库
docker start registry:2
给java:8镜像修改标签并上传
docker tag java:8 192.168.185.120:5000/java
docker push 192.168.185.120:5000/java
快速入门
以项目microservice-discovery-eureka为例
1)在pom.xml中添加Maven的Docker插件。
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.13</version>
<configuration>
<imageName>example/microservice-discovery-eureka:0.0.1</imageName>
<dockerHost>http://192.168.185.120:2375</dockerHost>
<baseImage>192.168.185.120:5000/java</baseImage>
<entryPoint>["java","-jar","/${project.build.finalName}.jar"]</entryPoint>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}</include>
</resource>
</resources>
</configuration>
</plugin>
- imageName:用于指定镜像名称,其中example是仓库名称,microservice-discovery-eureka是镜像名称,0.0.1是标签名称。
- baseImage:用于指定基础镜像,类似于Dockerfile中的FROM指令。
- entrypoint:类似于Dockerfile的ENTRYPOINT指令。
- resources.resource.directory:用于指定需要复制的根目录,${project.build.directory}表示target目录。
- resources.resource.include:用于指定需要复制的文件。${project.build.finalName}.jar指的是打包后的jar包文件。
2)执行以下命令,构建Docker镜像。
mvn clean package docker:build
插件读取Dockerfile进行构建
首先在/microservice-discovery-eureka/src/main/docker目录下,新建一个Dockerfile文件
#基于哪个镜像
FROM java:8
#将本地文件夹挂载到当前容器
VOLUME /tmp
#复制文件到容器
ADD microservice-discovery-eureka-1.0-SNAPSHOT.jar app.jar
RUN bash -c 'touch /app.jar'
#声明需要暴露的端口
EXPOSE 8761
#配置容器启动后执行的命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
修改pom.xml
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<imageName>example/microservice-discovery-eureka:0.0.1</imageName>
<dockerHost>http://192.168.2.120:2375</dockerHost>
<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
可以看到,不再指定baseImage和entrypoint,而是使用dockerDirectory指定Dockerfile所在的路径。这样,就可以使用Dockefiler构建Docker镜像了。
将插件绑定到某个phase执行
很多场景下,有这样的需求,执行例如mvn clean package时,插件就自动为构建Docker镜像。要想实现这点,执行将插件goal绑定在某个phase即可。
phase和goal可以这样理解:maven命令格式是mvn phase:goal,例如mvn package docker build。那么package docker都是phase,build则是goal。
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<imageName>example/microservice-discovery-eureka:0.0.1</imageName>
<dockerHost>http://192.168.2.120:2375</dockerHost>
<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
由配置可知,只需添加如下配置
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
就可将插件绑定在package这个phase上。
推送镜像
前文使用docker push命令实现了镜像的推送,也可使用Maven插件推送镜像。不妨使用Maven插件推送一个Docker镜像到Docker Hub。
1)修改Maven的全局配置文件settings.xml,在其中添加以下内容,配置Docker Hub的用户信息。
<server>
<id>docker-hub</id>
<username>repouser</username>
<password>repopwd</password>
<configuration>
<email>email</email>
</configuration>
</server>
2)修改pom.xml
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<imageName>18332104715/microservice-discovery-eureka:0.0.1</imageName>
<dockerHost>http://192.168.2.120:2375</dockerHost>
<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<serverId>docker-hub</serverId>
</configuration>
</plugin>
3)执行以下命令,添加pushImage的标识,表示推送镜像。
mvn clean package docker:build -DpushImage
同理,也可推送镜像到私有仓库,只需要将imageName指定成类似
<imageName>localhost:5000/microservice-discovery-eureka:0.0.1</imageName>
注:以上实例是通过imageName指定镜像名称和标签的
<imageName>localhost:5000/microservice-discovery-eureka:0.0.1</imageName>
也可借助imageTags元素更为灵活地指定镜像名称和标签
<configuration>
<imageName>example/microservice-discovery-eureka</imageName>
<imageTags>
<imageTag>0.0.5</imageTag>
<imageTag>latest</imageTag>
</imageTags>
</configuration>
这样就可为同一个镜像指定两个标签。
也可在构建命令时,使用dockerImageTags参数指定标签名称
mvn clean pachage docker:build -DpushImageTags -DdockerImageTags=latest -DdockerImageTags=another-tag
如需重复构建相同标签名称地镜像,可将forceTags设为true,这样就会覆盖构建相同标签的镜像。
<configuration>
<forceTags>true</forceTags>
</configuration>
Spotify是全球最大的正版流媒体音乐服务平台。