technology-note/linux/docker/3.docker部署java应用.md
2019-02-13 10:28:55 +08:00

210 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id="2019-02-01-14-05"
title="3.从零开始在docker中部署java应用"
headWord="&emsp;&emsp;在没有 docker 前,项目转测试是比较麻烦的一件事。首先会化较长的时间搭建测试环境,然后在测试过程中又经常出现测试说是 bug开发说无法复现的情况导致撕逼。<br/>&emsp;&emsp;本篇记录了如何将一个 java 应用部署到 docker 中。"
tags=["docker","springboot","jar","war","elastricsearch","activemq"]
category="linux"
serie="docker教程"
---
**本篇原创发布于 Flex 的个人博客**:[点击跳转](http://tapme.top/blog/detail/2019-02-01-14-05)
# 前言
&emsp;&emsp;在没有 docker 前,项目转测试是比较麻烦的一件事。首先会化较长的时间搭建测试环境,然后在测试过程中又经常出现测试说是 bug开发说无法复现的情况导致撕逼。
&emsp;&emsp;本篇记录了如何将一个 java 应用部署到 docker 中。主要讲述了以下几个部分:
- docker 部署 mysql
- docker 部署 activemq
- docker 部署 elastricsearch 集群
- docker 部署 java 应用
- docker 部署 nginx 作为静态服务器,及代理服务器
项目架构如下:
![项目架构](https://raw.githubusercontent.com/FleyX/files/master/blogImg/linux/docker/20190201151250.png)
本系统中有三个主要模块 OMS,DAS 和一个 Eureka 注册中心。其中 OMS 和 DAS 使用有 activemq 消息队列,来进行大量数据的交互然后各自使用一个 mysql 数据库存储主要的业务数据。使用 elastricsearch 存储超大量的数据。
## 传统软件部署和 docker 部署
&emsp;&emsp;本项目在 windows 部署时是将其作为三个部分来进行安装的--ENV 环境包(保护 mysqles 等OMS 产品包DAS 产品包。所以最初我的设想是一个容器中装 ENV 环境包所需的所有软件,一个容器装 DAS一个容器装 OMS。然后再实践的过程中越来越感觉不对劲环境配置比较复杂而且也有种把容器当虚拟机用的感觉一点没有简化的感觉。
&emsp;&emsp;遂停下了操作,开始学习一波 docker 到底是怎么用的。用租房子来做比喻:
- 传统软件部署方式相当于租到一个零家具,零装修的房子。我们想要住进去首先必须买齐必要的家具,然后想要住的舒心呢,还得花功夫装饰装饰,让房子好看点。这样就会对这个具体的房子产生较强的依赖,很难迁移到另一个房子中(想想那么多的家具,家电,杂物。。)。如果全部放弃重新换一个房子代价又太大了。
![搬家](https://raw.githubusercontent.com/FleyX/files/master/blogImg/linux/docker/20190201161044.png)
- 使用 docker 相当于租到一个全家电,精装修的房子。我们只需带上自己的个人物品即可开始入住。想要换一个房子也是轻而易举,带上自己的东西麻溜的就换了。
![轻松搬家](https://raw.githubusercontent.com/FleyX/files/master/blogImg/linux/docker/20190201161301.png)
&emsp;&emsp;使用 docker 推荐操作是一个进程放到一个容器中,做到更好的隔离性,同时也更容易进行管理。下面来使用容器技术部署我们应用。还是分为三部分,但是每个进程使用一个容器,做到 0 配置启动容器。
# 实战
&emsp;&emsp;在此默认已经会安装 docker且了解基本操作。如不了解的先看这两篇[安装](http://tapme.top/blog/detail/2018-11-20-10-38-05)[基本使用](http://tapme.top/blog/detail/2018-12-26-13-18)
## 部署 ENV 环境包
&emsp;&emsp;环境包中诸如 elastricsearchmysql 这样的数据存储工具,需要满足如下两个要求:
1. 保留数据,不论容器如何创建、销毁,数据不能丢。
2. 需要使用个性化的配置文件,每次启动根据该配置文件来启动。
### 部署 mysql
&emsp;&emsp;使用容器部署 mysql 过程如下:
1. 首先从 docker.hub 中根据各自的需求 pull 对应的 mysql 镜像
```bash
docker pull mysql:5.7.24
```
2. 启动镜像
&emsp;&emsp;由于 mysql 是用来存数据的,数据无论什么情况都不能丢失,所以数据存在容器外部,通过映射操作,映射到容器内部,参数如下:
```bash
# 将宿主机的路径,映射到容器内部。这个路径既可以是文件夹,也可以是文件
-v hostPath:containerPath
```
显然我们通过这个桉树将外部的数据文件夹,配置文件映射到容器中。最后启动这个容器的命令如下:
```bash
# 假设在宿主机中数据存放路径为/opt/mysql/data,配置文件路径为:/opt/mysql/my.cnf
docker run --name=mysql -itd -p 3308:3306 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /opt/mysql/data:/var/lib/mysql -v /opt/mysql/my.cnf:/etc/mysql/my.cnf -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.25
```
**注意:如果 mysql 版本为 8.x还需要映射容器中的/var/lib/mysql-files 目录,否则启动会报错**
下面介绍具体参数含义:
- -it 标准输入输出有关
- -d 后台启动
- -v 文件映射
- -e 设置环境变量到容器中
&emsp;&emsp;可能你们会问为什么要映射`/etc/timezone``/etc/timezone`,这是为了让容器的时间和时区与宿主机保持一致。默认情况下容器为 UTC 标准时间。`/etc/timezone`让容器时间,时区和宿主机一致。但是如果不映射`/etc/timezone`java 应用中的时区还是错的,虽然使用`date -R`命令查看时间和时区都正常。
### 部署 elastricsearch,activeMQ 容器
&emsp;&emsp;es 和 activeMQ 都依赖 java 的运行环境,所以有两种部署方式:
- 直接拉取 esactiveMQ 对应镜像,通过路径映射部署容器启动
- 在一个 java 镜像中运行 es 和 activeMQ
&emsp;&emsp;这里以第二种方式为例进行说明。
#### 创建 java 镜像
&emsp;&emsp;这里不从 docker hub 中拉取镜像,通过 dockerfile 来制作一个自定义的镜像。由于只需要一个 java 运行环境,所以只要将一个 jre 运行环境加入到一个基础 linux 镜像中即可(这里选择 ubuntu。制作过程如下
&emsp;&emsp;首先创建一个文件夹`dockerFileTest`存放依赖和 dockerfile 文件。
&emsp;&emsp;然后将下载加压后的 jre 运行环境放到`dockerFileTest/jre`目录下。
&emsp;&emsp;接着在 dockerFileTest 目录中创建 Dockerfile 文件,内容如下:
```bash
#说明基础镜像,默认:latest
FROM ubuntu
#将当前路径下的jre文件夹复制到新镜像下的/opt/jre路径
COPY jre /opt/jre
#设置环境变量
ENV JAVA_HOME=/opt/jre CLASSPATH=/opt/jre/lib PATH=$PATH:/opt/jre/bin
```
&emsp;&emsp;最后通过``命令生成新镜像 jre:v1
#### 创建 elastricsearch 容器
&emsp;&emsp;下载好 es假设存放在/root/es1 中,通过以下命令创建一个 es 容器:
```bash
docker run --name=es1 -itd -p 9200:9200 -p 9300:9300 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/es1:/opt/es -w /opt/es jre:v1 ./bin/elasticsearch -Des.insecure.allow.root=true
```
参数含义如下:
- -w containerPath 设置容器工作目录为 containerPath
&emsp;&emsp;上面的命令以 es1 为容器名,映射 92009300 到宿主机端口,以`./bin/'elasticearch -Des.insecure.allow.root=true`创建 es 容器。最后加的参数为了让 es 能够以 root 在容器中启动。
#### 创建 es 集群
&emsp;&emsp;将之前的 es1 复制一份命名为 es2 作为节点 2。要让两个 es 节点构成 es 集群,需要让节点间能够进行通信,这里使用`--link`参数来让 es2 能够连上 es1 构成集群。`--link`用法如下:
```bash
--link containerName[:alias]
```
es2 启动命令如下:
```bash
docker run --name=es2 -itd -p 9201:9200 -p 9301:9300 --link es1 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/es2:/opt/es -w /opt/es jre:v1 ./bin/elasticsearch -Des.insecure.allow.root=true
```
然后就能在 es2 中通过 es1 的容器名访问到 es1实际是在 es2 的 host 中增加了一条记录,将 es1 指向 es1 的 IP该 IP 是 docker 的虚拟网卡分配的 IP
&emsp;&emsp;但是使用--link 有一些局限,通过该参数联通的容器必须存在。因此该参数只能用在 B 依赖 A 的情况,如果同时 A 也依赖 B(也就是 AB 要能够相互访问到),这种情况下就不能通过 link 来实现了,原因大家应该能够想到。。。
#### 部署 activeMQ
&emsp;&emsp;在容器中启动 activeMQ 与启动 es 稍有不同。activeMQ 默认是后台启动的,启动完成后启动程序就会退出,因此如果直接以`./bin/activemq start`(假设当前目录在 activemq 中),启动容器会发现在 activemq 启动成功后容器就停止运行了。会出现这种情况是因为`容器中启动的第一个进程结束后容器就会被docker关闭掉`。所以呢我们只需让第一个进程不结束就行了,因此需要我们编写一个启动脚本来启动 activemq 并监测运行情况,一旦 activemq 进程挂掉,就结束启动脚本,否则一直运行。启动脚本代码如下:
```bash
#!/bin/bash
#使用sh脚本启动activemq然后定时判断服务是否被关闭关闭后退出脚本否则一致循环。
#为避免docker容器在active自带的启动脚本运行结束后就关闭容器了。
#获取启动pid
out=`./bin/activemq start`
echo "$out"
pid=`echo $out | grep -Eo "pid '[0-9]+'" | grep -Eo "[0-9]+"`
echo "当前mq进程pid为${pid}"
if [ ${#pid} = 0 ]; then
echo "启动失败"
exit 0
fi
while true; do
num=`ps -e | grep -cwE "$pid"`
if [ $num = 0 ]; then
echo "进程异常关闭"
exit 0
fi
sleep 1
done
```
&emsp;&emsp;然后以该脚本作为启动脚本来启动容器即可。启动命令如下:(假设 activemq 目录为/opt/activemq,启动脚本路径为/opt/activemq/start.sh)
```bash
docker run --name=activemq -itd -p 8161:8161 -p 61616:61616 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/activemq:/opt/activemq -w /opt/activemq jre:v1 /bin/bash ./start.sh
```
## 部署 java 环境包
&emsp;&emsp;还是使用之前制作的 jar 镜像来启动 java 应用,这里以部署 jar 包为例,如果部署 war 包则需要在 tomcat 镜像上部署。特别注意下容器的时间和时区设置,否则 java 程序中无法获取到正确的时间和时区。这里通过映射宿主机的 localtime 和 timezone 文件来让容器时间和时区与宿主机相同。启动命令如下:
```bash
# 启动oms
docker run -itd --name=oms -p 8082:9090 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/oms.jar:/opt/oms.jar -w /opt --link es1 --link activemq --link oms-mysql --link eureka-server jre:v1 java -jar oms.jar
# 启动das
docker run -itd --name=das -p 8083:9099 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/das.jar:/opt/oms -w /opt --link es1 --link activemq --link oms-mysql --link eureka-server jre:v1 java -jar das.jar
```
&emsp;&emsp;本篇只是记录了如何使用`一容器一进程`的方式来部署 java 应用,后续可能会继续学习容器编排和容器管理技术。
**本篇原创发布于 Flex 的个人博客**:[点击跳转](http://tapme.top/blog/detail/2019-02-01-14-05)