本篇原创发布于 Flex 的个人博客:点击跳转

前言

  在没有 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,且了解基本操作。如不了解的先看这两篇:安装基本使用

部署 ENV 环境包

  环境包中诸如 elastricsearch,mysql 这样的数据存储工具,需要满足如下两个要求:

  1. 保留数据,不论容器如何创建、销毁,数据不能丢。
  2. 需要使用个性化的配置文件,每次启动根据该配置文件来启动。

部署 mysql

  使用容器部署 mysql 过程如下:

  1. 首先从 docker.hub 中根据各自的需求 pull 对应的 mysql 镜像
docker pull mysql:5.7.24
  1. 启动镜像

  由于 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

本篇原创发布于 Flex 的个人博客:点击跳转

手把手docker部署java应用(初级篇)的更多相关文章

  1. Docker学习笔记三:Docker部署Java web系统

    Docker部署Java Web系统 1.在root目录下创建一个路径test/app mkdir test && cd test&& mkdir app && ...

  2. 通过Docker部署Java项目的日志输出到宿主机指定目录

    之前写过2篇关于Docker部署的文章: 1.超!超!超简单,Linux安装Docker 2.Docker通过阿里云镜像仓库使用Gitlab_CI部署SpringBoot项目 用上篇博客部署Java程 ...

  3. docker 部署 java 项目

    Docker Docker官方网址: https://docs.docker.com/  英文地址 Docker中文网址: http://www.docker.org.cn/ 中文地址 Docker是 ...

  4. 使用docker部署java项目

    在接触了docker后,干什么都想用docker来弄.最近刚做完毕业设计,本来是说将项目简单在本地部署就行了,结果老师说如果部署在服务器的话有加加分,于是果断用docker来搞,多加点分. java项 ...

  5. jenkins+docker部署java项目

    jenkins + maven + jdk + docker + docker register + dockerfile jenkins插件 # 安装插件 SSH # 配置 系统设置-> SS ...

  6. 使用Docker部署java web项目

    在国内可能会有源下载失败问题,docker安装失败, 这里提供docker离线安装包如有需要可以进行下载 docker离线安装包下载 ##本文环境使用centos 7 进行部署. #1安装docker ...

  7. docker部署java应用程序

    https://docs.docker.com/get-started/ 安装docker   1.安装docker  apt install docker 2.配置docker加速器 安装完成后在 ...

  8. Java消息中间件--初级篇

    一. 为什么使用消息中间件? 假设用户登录系统   传统方式 用户登录  调用短息服务   积分服务  日志服务等各种服务  如果短息服务出现问题就无法发送短信而且用户登录成功必须所有调用全部完成返回 ...

  9. Java工程师学习指南(初级篇)

    Java工程师学习指南 初级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我之前写的文章都 ...

随机推荐

  1. 2 DDD理论学习2 领域

    一个领域本质上可以理解为就是一个问题域,只要是同一个领域,那问题域就相同. 所以,只要我们确定了系统所属的领域,那这个系统的核心业务,即要解决的关键问题.问题的范围边界就基本确定了. 领域首先要拆分成 ...

  2. WPF 导出资源文件

    在wpf开发中我们可以把各种文件.图片打包到项目中也就是应用程序资源文件,然后在项目中可以通过特定的uri格式去调用.那有些时候为了方便我们可能想要在程序中将资源文件导出来使用,那么怎么做呢? 第 1 ...

  3. OO五大原则

    1.单一职责原则 应该有且仅有一个原因引起类的改变 2.里氏替换原则 所有引用基类的地方必须能够透明的使用其子类的对象 3.依赖倒置原则 高层模块不应该依赖底层模块,两者都应该依赖抽象:抽象不应该依赖 ...

  4. latex 矩阵分块(block matrix)

    Lesson 12: Making Block Matrices in LATEX 按列分块: [AAb⋯An−1b] $$ \left[ \begin{array}{c|c|c|c} A & ...

  5. uboot初体验-----趣谈nand设备发起的浅显理解

    1 选择Uboot版本号 2 移植uboot至console正常work 3 制造uImage和使用uboot指南 4 写NFC驱动器 5 uboot从nand启动引导系统 1 选择Uboot版本号 ...

  6. 定长文本格式编辑神器 - Record Editor

    由于项目里有系统使用的是定长的文本格式,如果列数比较多的话编辑检查起来会特别麻烦.之前也找了各种编辑器的插件,最多只能显示列宽.辅助线而以还是不方便 .最后发现了一个开源项目 Record Edito ...

  7. Ionic2开发环境搭建-VS 2017

    原文:Ionic2开发环境搭建-VS 2017 目前在VS 2017中创建Ionic2版本项目 注:在VS中开发Ionic项目,使用的Ionic(v2.x),Cordova(v6.3.1),Angul ...

  8. vs2015 cordova环境安装【个人遇到的几个问题】

    原文:vs2015 cordova环境安装[个人遇到的几个问题] 问题1: vs2015,设置  Debug  Android 设备[真机调试] Exception in thread "m ...

  9. String的本质是一个char*,只是以类的形式提供,使用起来比较方便

    String的本质是一个char*,只是以类的形式提供,使用起来比较方便 Class String {private: char* m_data;}摘自<后台开发 核心技术与应用实践__徐晓鑫& ...

  10. Win8 Metro(C#)数字图像处理--2.37Wallis图象锐化

    原文:Win8 Metro(C#)数字图像处理--2.37Wallis图象锐化  [函数名称] Wallis图象锐化        WallisSharpen(WriteableBitmap sr ...