前言
在没有 docker 前, 项目转测试是比较麻烦的一件事. 首先会化较长的时间搭建测试环境, 然后在测试过程中又经常出现测试说是 bug, 开发说无法复现的情况, 导致撕逼.
本篇记录了如何将一个 java 应用部署到 docker 中. 主要讲述了以下几个部分:
docker 部署 MySQL
docker 部署 activemq
docker 部署 elastricsearch 集群
docker 部署 java 应用
docker 部署 nginx 作为静态服务器, 及代理服务器
项目架构如下:
本系统中有三个主要模块 OMS,DAS 和一个 Eureka 注册中心. 其中 OMS 和 DAS 使用有 activemq 消息队列, 来进行大量数据的交互然后各自使用一个 MySQL 数据库存储主要的业务数据. 使用 elastricsearch 存储超大量的数据.
传统软件部署和 docker 部署
本项目在 Windows 部署时是将其作为三个部分来进行安装的 --ENV 环境包 (保护 MySQL,es 等),OMS 产品包, DAS 产品包. 所以最初我的设想是一个容器中装 ENV 环境包所需的所有软件, 一个容器装 DAS, 一个容器装 OMS. 然后再实践的过程中越来越感觉不对劲, 环境配置比较复杂, 而且也有种把容器当虚拟机用的感觉, 一点没有简化的感觉.
遂停下了操作, 开始学习一波 docker 到底是怎么用的. 用租房子来做比喻:
传统软件部署方式相当于租到一个零家具, 零装修的房子. 我们想要住进去首先必须买齐必要的家具, 然后想要住的舒心呢, 还得花功夫装饰装饰, 让房子好看点. 这样就会对这个具体的房子产生较强的依赖, 很难迁移到另一个房子中 (想想那么多的家具, 家电, 杂物..). 如果全部放弃重新换一个房子代价又太大了.
使用 docker 相当于租到一个全家电, 精装修的房子. 我们只需带上自己的个人物品即可开始入住. 想要换一个房子也是轻而易举, 带上自己的东西麻溜的就换了.
使用 docker 推荐操作是一个进程放到一个容器中, 做到更好的隔离性, 同时也更容易进行管理. 下面来使用容器技术部署我们应用. 还是分为三部分, 但是每个进程使用一个容器, 做到 0 配置启动容器.
实战
在此默认已经会安装 docker, 且了解基本操作. 如不了解的先看这两篇: 安装 http://tapme.top/blog/detail/2018-11-20-10-38-05 , 基本使用 http://tapme.top/blog/detail/2018-12-26-13-18
部署 ENV 环境包
环境包中诸如 elastricsearch,MySQL 这样的数据存储工具, 需要满足如下两个要求:
保留数据, 不论容器如何创建, 销毁, 数据不能丢.
需要使用个性化的配置文件, 每次启动根据该配置文件来启动.
部署 MySQL
使用容器部署 MySQL 过程如下:
首先从 docker.hub 中根据各自的需求 pull 对应的 MySQL 镜像
docker pull MySQL:5.7.24
启动镜像
由于 MySQL 是用来存数据的, 数据无论什么情况都不能丢失, 所以数据存在容器外部, 通过映射操作, 映射到容器内部, 参数如下:
- # 将宿主机的路径, 映射到容器内部. 这个路径既可以是文件夹, 也可以是文件
- -v hostPath:containerPath
显然我们通过这个桉树将外部的数据文件夹, 配置文件映射到容器中. 最后启动这个容器的命令如下:
- # 假设在宿主机中数据存放路径为 / 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 设置环境变量到容器中
可能你们会问为什么要映射 / etc/timezone 和 / etc/timezone, 这是为了让容器的时间和时区与宿主机保持一致. 默认情况下容器为 UTC 标准时间./etc/timezone 让容器时间, 时区和宿主机一致. 但是如果不映射 / etc/timezonejava 应用中的时区还是错的, 虽然使用 date -R 命令查看时间和时区都正常.
部署 elastricsearch,activeMQ 容器
es 和 activeMQ 都依赖 java 的运行环境, 所以有两种部署方式:
直接拉取 es,activeMQ 对应镜像, 通过路径映射部署容器启动
在一个 java 镜像中运行 es 和 activeMQ
这里以第二种方式为例进行说明.
创建 java 镜像
这里不从 docker hub 中拉取镜像, 通过 dockerfile 来制作一个自定义的镜像. 由于只需要一个 java 运行环境, 所以只要将一个 jre 运行环境加入到一个基础 Linux 镜像中即可 (这里选择 Ubuntu). 制作过程如下:
首先创建一个文件夹 dockerFileTest 存放依赖和 dockerfile 文件.
然后将下载加压后的 jre 运行环境放到 dockerFileTest/jre 目录下.
接着在 dockerFileTest 目录中创建 Dockerfile 文件, 内容如下:
- # 说明基础镜像, 默认: latest
- FROM Ubuntu
- # 将当前路径下的 jre 文件夹复制到新镜像下的 / opt/jre 路径
- COPY jre /opt/jre
- # 设置环境变量
- ENV JAVA_HOME=/opt/jre CLASSPATH=/opt/jre/lib PATH=$PATH:/opt/jre/bin
最后通过 `` 命令生成新镜像 jre:v1
创建 elastricsearch 容器
下载好 es, 假设存放在 / root/es1 中, 通过以下命令创建一个 es 容器:
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
上面的命令以 es1 为容器名, 映射 9200,9300 到宿主机端口, 以./bin/'elasticearch -Des.insecure.allow.root=true 创建 es 容器. 最后加的参数为了让 es 能够以 root 在容器中启动.
创建 es 集群
将之前的 es1 复制一份命名为 es2 作为节点 2. 要让两个 es 节点构成 es 集群, 需要让节点间能够进行通信, 这里使用 --link 参数来让 es2 能够连上 es1 构成集群.--link 用法如下:
--link containerName[:alias]
es2 启动命令如下:
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).
但是使用 --link 有一些局限, 通过该参数联通的容器必须存在. 因此该参数只能用在 B 依赖 A 的情况, 如果同时 A 也依赖 B(也就是 A,B 要能够相互访问到), 这种情况下就不能通过 link 来实现了, 原因大家应该能够想到...
部署 activeMQ
在容器中启动 activeMQ 与启动 es 稍有不同. activeMQ 默认是后台启动的, 启动完成后启动程序就会退出, 因此如果直接以./bin/activemq start(假设当前目录在 activemq 中), 启动容器会发现在 activemq 启动成功后容器就停止运行了. 会出现这种情况是因为容器中启动的第一个进程结束后, 容器就会被 docker 关闭掉. 所以呢我们只需让第一个进程不结束就行了, 因此需要我们编写一个启动脚本来启动 activemq 并监测运行情况, 一旦 activemq 进程挂掉, 就结束启动脚本, 否则一直运行. 启动脚本代码如下:
- #!/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
然后以该脚本作为启动脚本来启动容器即可. 启动命令如下:(假设 activemq 目录为 / opt/activemq, 启动脚本路径为 / opt/activemq/start.sh)
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 环境包
还是使用之前制作的 jar 镜像来启动 java 应用, 这里以部署 jar 包为例, 如果部署 war 包则需要在 tomcat 镜像上部署. 特别注意下容器的时间和时区设置, 否则 java 程序中无法获取到正确的时间和时区. 这里通过映射宿主机的 localtime 和 timezone 文件来让容器时间和时区与宿主机相同. 启动命令如下:
- # 启动 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
本篇只是记录了如何使用一容器一进程的方式来部署 java 应用.
PS: 不推荐这么直接手撸命令, 建议使用 docker-compose
来源: https://www.cnblogs.com/wuyoucao/p/11130420.html