利用Docker-Compose实现:Nginx+Tomcat负载均衡、javaweb项目的部署、Hadoop大数据完全分布式环境搭建

一.利用Dokcer-Compose实现Nginx+Tomcat的负载均衡

NginxApache 一样都是一种 Web 服务器。基于 REST 架构风格,以统一资源描述符(Uniform Resources Identifier)URI 或者统一资源定位符(Uniform Resources Locator)URL 作为沟通依据,通过 HTTP 协议提供各种网络服务。Nginx 是一款自由的、开源的、高性能的 HTTP 服务器和反向代理服务器;同时也是一个 IMAP、POP3、SMTP 代理服务器。Nginx 可以作为一个 HTTP 服务器进行网站的发布处理,另外,Nginx 可以为反向代理进行负载均衡的实现。

关于Nginx的代理功能,可参考 Nginx的代理功能

Tomcat服务器是一个开源的轻量级Web应用服务器,在中小型系统和并发量小的场合下被普遍使用,是开发和调试Servlet、JSP 程序的首选。其结构图如下:
Tomcat结构

Tomcat主要组件:服务器Server,服务Service,连接器Connector、容器Container。连接器Connector和容器Container是Tomcat的核心。

一个Container容器和一个或多个Connector组合在一起,加上其他一些支持的组件共同组成一个Service服务,有了Service服务便可以对外提供能力了,但是Service服务的生存需要一个环境,这个环境便是Server,Server组件为Service服务的正常使用提供了生存环境,Server组件可以同时管理一个或多个Service服务。具体介绍可参阅Tomcat简介

本模块主要介绍如何利用Docker-Compose实现Nginx+Tomcat的负载均衡

🔹 负载均衡
负载均衡主要是为了解决服务器负载过大,在有的时候,系统的并发量过大,一台服务器无法负担的起用户发送的请求,因此我们需要搭建服务器集群,而nginx负载均衡就是接收用户的请求将其转发给后台服务器集群上的每一台机器。

🔹 nginx的负载均衡策略

策略 类型
轮询 默认方式
weight 权重方式
ip_hash 依据ip分配方式
ip_hash 依据ip分配方式
fair(第三方) 响应时间方式
url_hash(第三方) 依据URL分配方式

1️⃣ docker-compose.yml

version: "3.8"

services:
  nginx:
    image: "nginx"
    container_name: nginx
    ports:
      - "8000:80" #端口映射
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf #将容器中的配置文件替换成自己的配置文件
   
    depends_on: #依赖关系
      - tomcat-1 
      - tomcat-2
      - tomcat-3
  #基于tomcat镜像构造三个tomcat容器,分别映射到不同的主机端口以示区别
  tomcat-1:
    image: "tomcat"
    container_name: tomcat-1
    volumes:
      - ./tomcat:/usr/local/tomcat #将容器中的tomcat替换为本机目录下的tomcat
      - ./myapp1:/usr/local/tomcat/webapps #挂载自己的web代码到容器的webapps下
    ports:
      - "8080:8080"

  tomcat-2:
    image: "tomcat"
    volumes:
      - ./tomcat:/usr/local/tomcat
      - ./myapp2:/usr/local/tomcat/webapps
    container_name: tomcat-2
    ports:
      - "8081:8080"

  tomcat-3:
    image: "tomcat"
    container_name: tomcat-3
    volumes:
      - ./tomcat:/usr/local/tomcat
      - ./myapp3:/usr/local/tomcat/webapps
    ports:
      - "8082:8080"

docker-compose中包含一个nginx服务器镜像和三个tomcat服务器镜像的构建,nginx充当反向代理,当客户端发送请求时,nginx对请求进行解析,将根据负载均衡策略选择一个tomcat服务器,并将请求发送给该tomcattomcat处理完成后返回结果给nginxnginx再把响应结果返回给客户端,在这个过程中,nginx充当方向代理的作用。

注意:

①. myapp1、myapp2、myapp3文件夹下分别存放三个简单的jsp页面,分别挂载到三个不同的tomcat服务器上,以便测试时辨别是哪台tomcat服务器响应的请求:

②. tomcat默认发布路径为/usr/local/webapps/ROOT,也就是说需要将自己的web代码存放到容器的/usr/local/webapps/ROOT这个目录下,否则通过浏览器访问web页面的时候会出现404的错误。
解决方法:一种方法是使用默认发布路径,将web代码部署在/usr/local/webapps/ROOT下;另外一种方法也可以通过修改发布路径,重定向到自己的项目目录,可参考Tomcat 修改/webapps/ROOT发布路径(Linux和windows环境),这里利用了其中的方法二,通过修改/usr/local/tomcat/conf/server.xml将web发布目录改为webapps,然后把web代码放到webapps下面,访问浏览器可以得到响应页面。即在server.xml中添加<Context path="" docBase="" />:

2️⃣ nginx.conf

①. 使用轮询策略:负载均衡默认策略,每个请求会按时间顺序逐一分配到不同的后端服务器。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    
    #配置动态服务器组,使用轮询的负载均衡策略
    upstream blance{
            server tomcat-1:8080;
            server tomcat-2:8080;
            server tomcat-3:8080;
    }
    server {
                listen 80;
                server_name localhost;
                
		#其他页面反向代理到tomcat服务器
                location / {
	    		index index.jsp index.html;
	   		proxy_pass http://blance;
    	        }
    }
 
    

   

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

②. 使用权重策略:权重方式,在轮询策略的基础上指定轮询的几率。weight参数用于指定轮询几率,其默认值为1。指定tomcat-1的权重为2,则该服务器被访问几率为其他服务器的2倍。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    
    #配置动态服务器组,使用轮询的负载均衡策略
    upstream blance{
            server tomcat-1:8080 weight=2;
            server tomcat-2:8080;
            server tomcat-3:8080 max_fails=3 fail_timeout=20s;
    }
    server {
                listen 80;
                server_name localhost;
                
		#其他页面反向代理到tomcat服务器
                location / {
	    		index index.jsp index.html;
	   		proxy_pass http://blance;
    	        }
    }
 
    

   

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

二.利用Docker-Compose完成javaweb项目的部署

参考使用docker-compose部署Javaweb项目

1️⃣ Nginx+Tomcat+javaweb文件目录结构

├── docker-compose.yml
├── index.jsp
├── mysql
│   ├── ini.sh
│   ├── ini.sql
│   ├── my.ini
│   └── mysql_dockerfile
├── nginx.conf
└── webapps
    └── myApp.war

2️⃣ docker-compose.yml,这部分比上个模块只多了mysql服务的构建

version: "3.8"

services:
  nginx:
    image: "nginx"
    container_name: nginx #容器名
    ports:
      - "8000:80" #端口映射
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf #nginx配置文件,设置权重方式的负载均衡策略
   
    depends_on: 
      - tomcat-1
      - tomcat-2
      - tomcat-3

  tomcat-1:
    image: "tomcat"
    container_name: tomcat-1
    volumes:
      - ./webapps:/usr/local/tomcat/webapps #挂载war包到tomcat容器的webapps下

    ports:
      - "8080:8080"

  tomcat-2:
    image: "tomcat"
    volumes:
      - ./webapps:/usr/local/tomcat/webapps
    container_name: tomcat-2
    ports:
      - "8081:8080"

  tomcat-3:
    image: "tomcat"
    container_name: tomcat-3
    volumes:
      - ./webapps:/usr/local/tomcat/webapps
    ports:
      - "8082:8080"

  db:
    image: mysql
    build:
      context: ./mysql
      dockerfile: ./mysql_dockerfile
    container_name: mysql
    command: [
            '--character-set-server=utf8mb4',
            '--collation-server=utf8mb4_unicode_ci'
    ]
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=6666
      - MYSQL_ALLOW_EMPTY_PASSWORD=no

3️⃣ nginx.conf,使用权重方式的负载均衡策略

4️⃣ mysql下的构建文件

①. mysql_dockerfile

FROM mysql
#将脚本文件ini.sh以、数据库初始化文件ini.sql以及配置文件my.ini复制到容器中
COPY ./ini.sh /docker-entrypoint-initdb.d/ini.sh
COPY ./ini.sql /mysql/ini/ini.sql
COPY ./my.ini //etc/mysql/conf.d/mysql.cnf

②. 脚本文件ini.sh``以及数据库初始化文件ini.sql: :small_blue_diamond: ini.sh```

#!/bin/bash
mysql -uroot -p6666 << EOF
source /mysql/ini/ini.sql; 

🔹 ini.sql:

drop database if exists test;
create database test;
use test;
drop table if exists user;
CREATE TABLE user (
                      `id` bigint(20) NOT NULL PRIMARY KEY ,
                      `name` varchar(20) DEFAULT NULL,
                      `age` int(3) DEFAULT NULL,
                      `sex` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO user(`id`, `name`, `age`, `sex`) VALUES (1, '小明', 18, 'male');
INSERT INTO user(`id`, `name`, `age`, `sex`) VALUES (2, '小红', 19, 'female');
INSERT INTO user(`id`, `name`, `age`, `sex`) VALUES (3, '小李', 20 , 'male');

③. mysql配置文件my.ini

[mysqld]
# 设置3306端口
port=3306
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为utf8mb4
character-set-server=utf8mb4
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8mb4
[client]
# 设置mysql客户端连接服务端时默认使用的端口
port=3306
default-character-set=utf8mb4

😩😩配置文件需要指明mysql相关的字符集为utf8mb4,否则在运行的时候会出现中文乱码现象:

搁这找了尬了快俩小时,试了5、6种方法愣是没用,最后不得不靠一手百度才找到解决方案(不愧是百度嵌入式学科,i了i了😬😬):docker中mysql中文乱码问题把容器默认的配置文件替换成上述配置文件就🉑了。

5️⃣ webapps/myApp.war

基于springboot框架,利用jdbc连接mysql数据库,实现简单的增删改查功能,项目已打成war包,上传至百度网盘,有需要可自取(网盘链接 提取码:gn35 )

5️⃣ 开启服务

①. docker-compose up:

②. 当看到Spring图形及Server startup说明springboot成功运行:

6️⃣ 服务验证

①. 查

②. 增

③. 改

④. 删

三.利用Docker-compose搭建Hadoop完全分布式环境

参考使用Docker搭建Hadoop分布式集群

1️⃣ 使用-v参数 挂载本地软件源到容器中实现Ubuntu换源,加快资源获取速度

2️⃣ 设置sshd自启动,并配置sshd免密连接本地服务

设置sshd自启动

使用RSA算法生成密钥,并将密钥加入到本机接收的公钥列表中

验证免密连接localhost

3️⃣ 配置Hadoop完全分布式环境

①. 修改hadoop_env.sh

#假设现在/usr/local/hadoop目录下
vim etc/hadoop/hadoop-env.sh
# 将export JAVA_HOME=${JAVA_HOME}替换成
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/

②. 配置文件的修改

🔺 core-site.xml

<configuration>
      <property>
          <name>hadoop.tmp.dir</name>
          <value>file:/usr/local/hadoop-3.1.3/tmp</value>
          <description>Abase for other temporary directories.</description>
      </property>
      <property>
          <name>fs.defaultFS</name>
          <value>hdfs://master1:9000</value>
      </property>
</configuration>

🔺 hdfs-site.xml

<configuration>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:/usr/local/hadoop-3.1.3/namenode_dir</value>
    </property>
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:/usr/local/hadoop-3.1.3/datanode_dir</value>
    </property>
    <property>
        <name>dfs.replication</name>
        <value>3</value>
    </property>
</configuration>

🔺 mapred-site.xml

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <property>
        <!配置mapreduce依赖,否则运行会报错-->
        <name>mapreduce.application.classpath</name>
        <value>/usr/local/hadoop-3.1.3/etc/hadoop:/usr/local/hadoop-3.1.3/share/hadoop/common/lib/*:/usr/local/hadoop-3.1.3/share/hadoop/common/*:/usr/local/hadoop-3.1.3/share/hadoop/hdfs:/usr/local/hadoop-3.1.3/share/hadoop/hdfs/lib/*:/usr/local/hadoop-3.1.3/share/hadoop/hdfs/*:/usr/local/hadoop-3.1.3/share/hadoop/mapreduce/lib/*:/usr/local/hadoop-3.1.3/share/hadoop/mapreduce/*:/usr/local/hadoop-3.1.3/share/hadoop/yarn:/usr/local/hadoop-3.1.3/share/hadoop/yarn/lib/*:/usr/local/hadoop-3.1.3/share/hadoop/yarn/*</value>
    </property>

</configuration>

🔺 yarn-site.xml

<configuration>
      <property>
          <name>yarn.nodemanager.aux-services</name>
          <value>mapreduce_shuffle</value>
      </property>
      <property>
          <name>yarn.resourcemanager.hostname</name>
          <value>master1</value>
      </property>
     <property>
          <name>yarn.nodemanager.vmem-pmem-ratio</name>
          <value>2.5</value>
     </property>
     <property>
          <!--防止虚拟内存不足导致程序崩溃-->
          <name>yarn.nodemanager.vmem-check-enabled</name>
          <value>false</value>
     </property>
</configuration>

③. 将已安装jdk和hadoop,并且按上述文件修改进行配置后的容器commit,生成带有jdk和hadoop的镜像ubuntu/hadoopinstalled

④. 分别在三个终端上运行该镜像,其中一台作为master节点,另外两台作为slave节点

⑤. 分别修改三个节点的hosts文件,使不同容器的主机名映射对应容器IP

⑥. master节点

🔻 免密连接slave0、slave1

🔻 初次启动hadoop需要将namenode格式化,之后启动无需重新格式化

当打印的日志信息中出现successfully formatted说明namenode成功格式化,否则,需要删除/tmp重新格式化

🔻 由于Hadoop3.x的slaves文件改名为worksrs,教程中slaves的修改改为master的workers修改

🔻 启动hadoop,启动命令:start-all.sh,对应的停止命令为:stop-all.sh

🔻 jps查看hadoop是否成功启动,出现以下四个进程信息,说明Hadoop正常启动,否则需要检查配置文件是否编写正确,修改后再重新启动slaves:

ps:如果hadoop在启动过程中出现如下失败的情况,说明是使用root配置的Hadoop启动报错,则需要修改hdfs和yarn运行脚本start-dfs.sh、stop-dfs.sh和start-yarn.sh、stop-yarn.sh

启动错误:

🔻 在start-dfs.sh和stop-dfs.sh添加:

🔻 在start-yarn.sh和stop-yarn.sh添加:

⑦. 运行测试

🔺 向hdfs上传输入文件

🔺 运行grep实例,寻找匹配的文本及其个数
运行结果:

🔺 运行wordmean实例,计算输入文件中单词平均长度,并输出单词总长度和总个数
运行结果:

大功告成!!!

四.小结

这次实践花了快一周的时间才完成。首先是因为电脑出了问题,送去维修,而且没有备份,导致之前的配置全部丢失,情急之下找同学借了电脑,重新搭建实验环境,浪费了不少时间;其次是大量时间花在了学习(和debug)javaweb应用的编写,由于之前缺少web学习的经验,对javaweb的编写可以说是一窍不通。经过查阅资料和同学推荐,选择了springboot框架进行javaweb的编写。通过视频的学习,参照网上的代码,对其进行了魔改,用了快三天的时间才搭了一个简单的javaweb;(对于一个java小白,三天速学springboot也太难了,头又秃了😭😭)还有一个点就是之前的习惯是先做完实验保存截图再写博客,这样做有利于在写博客的时候回顾一下实验操作,加深印象,但是这次实践过程中因为自己的一时疏忽截错了图、少截了图,在写博客的时候不得不返工重做,造成了不必要的时间浪费,降低了效率。所以在接下来的实践中,一定要多留心,及时记录和检验实验步骤的完整性和正确性,以免出现类似此次回炉重造的麻烦!

posted @ 2020-05-18 00:38  $¥$¥$¥  阅读(1783)  评论(0编辑  收藏  举报