nginx负载均衡+docker部署应用

Docker已经出来好长时间了,一直没有时间研究,正好最近有个项目部署在一台内存和CPU都超夸张的机器上,而项目因并发量增加,后面肯定也需要扩展了。因为这台服务器内存和CPU都足够大,部署一个项目实在太浪费了,于是想到用docker部署方式做横向扩展。

首先想到的方案就是nginx做负载均衡,再加多台docker的方式部署项目。思路很简单,但在真正操作的时候,遇到各种各样的问题,所以说实践是最好的老师一点没错。

准备docker

跟同学借了一台亚马逊的云作为测试环境

Linux ip-10-200-8-1044.9.20-11.31.amzn1.x86_64 #1 SMP Thu Apr 13 01:53:57 UTC 2017 x86_64 x86_64x86_64 GNU/Linux

安装docker

通过yum方式安装

yum install docker –y

配置docker的镜像源

因为被墙了,貌似很多docker镜像都下载不下来,可以配置docker镜像的地址为国内的地址。其实很简单,改下一个docker的配置文件就好了。在/etc/docker目录下面有个daemon.json的文件,修改下就行了。

cd /etc/docker
vim daemon.json

修改为如下内容:

{
    "registry-mirrors": [
       "http://8fcab180.m.daocloud.io"
    ],
    "insecure-registries": []
}

配置DockerFile

项目是基于struts2+mybatis的web项目,用到oracle数据库和redis缓存。因为要做这种横向扩展所以要解决session共享问题,用的spring session,缓存自然也是redis,这块后面再讲。

DockerFile文件配置如下:

#docker容器使用linux系统
FROM ubuntu:14.04
MAINTAINER tonggu<[email protected]>
ENV REFRESHED_AT 2017-8-20

#安装tomcat7
RUN apt-get -yqq update
RUN apt-get -yqq install tomcat7default-jdk

#设置时区和时间(这个很重要,不然oracle数据库连不上,在这个上面花了好长时间)
RUN cp /usr/share/zoneinfo/Asia/Shanghai/etc/localtime && \
   echo "Asia/Shanghai" > /etc/timezone && \
   date

#设置tomcat的环境变量
ENV CATALINA_HOME /usr/share/tomcat7
ENV CATALINA_BASE /var/lib/tomcat7
ENV CATALINA_PID  /var/run/tomcat7.pid
ENV CATALINA_SH   /usr/share/tomcat7/bin/catalina.sh
ENV CATALINA_TMPDIR/tmp/tomcat7-tomcat7-tmp

RUN mkdir -p $CATALINA_TMPDIR

#映射发布程序目录
VOLUME["/var/lib/tomcat7/webapps/"]

#把8080端口映射出去
EXPOSE 8080

#docker容器启动的时候启动tomcat
ENTRYPOINT["/usr/share/tomcat7/bin/catalina.sh", "run" ]

启动/停止docker

启动docker

sudo service docker start

停止

sudo service docker stop

构建docker镜像文件

切换到DockerFile所在目录,然后执行下面的命令

sudo docker build -t ifa/erp-web .

这段命令的意思是通过DockerFile文件生成一个名字叫ifa/erp-web的镜像,需要执行的命令都已经在DockerFile里面写好了。

若执行完了,可以查看下镜像

sudo docker images

显示结果:

REPOSITORY  TAG          IMAGE ID     CREATED       SIZE

ifa/erp-web   latest        503a44ae7b40  21 hours ago   454 MB

运行docker容器

我一共部署两个docker容器,所以启动了两个。一个是erp-web1,一个是erp-web2,然后用服务器的8001和8002端口分别映射容器的8080端口。

注意:/var/lib/docker/data和/var/lib/docker/logs这两个目录要预先创建,其中/data目录放要发布的项目war包,因为映射到了容器中的/var/lib/tomcat7/webapps目录,所以容器启动的时候,相当于放在tomcat的webapps目录下面,等tomcat启动的时候就会自动发布了。/logs目录为日志目录,方便查看日志用。

sudo docker run -it -d --name erp-web1 -p 8001:8080 -v /var/lib/docker/data:/var/lib/tomcat7/webapps -v /var/lib/docker/logs:/logs ifa/erp-web ubuntu /bin/bash

sudo docker run -it -d --name erp-web2 -p 8002:8080 -v /var/lib/docker/data2:/var/lib/tomcat7/webapps -v /var/lib/docker/logs2:/logs ifa/erp-web ubuntu /bin/bash

上面两个命令会启动两个docker容器,一个是erp-web1,一个是erp-web2,可以通过下面的命令查看。

sudo docker ps

显示结果:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                        NAMES

b02d455b3393        ifa/erp-web         "/usr/share/tomcat..."   19 hours ago        Up 42 minutes       1521/tcp, 9000/tcp,0.0.0.0:8002->8080/tcp   erp-web2

473cb6b2837c        ifa/erp-web         "/usr/share/tomcat..."   19 hours ago        Up 42 minutes       1521/tcp, 9000/tcp, 0.0.0.0:8001->8080/tcp   erp-web1

到目前为止其实已经完成docker的启动了,项目也可以正常访问了。

访问地址:
http://ec2-54-223-252-143.cn-north-1.compute.amazonaws.com.cn:8001/erp-web/welcome.html
http://ec2-54-223-252-143.cn-north-1.compute.amazonaws.com.cn:8002/erp-web/welcome.html

接下来就是要配置nginx,nginx采用8080端口,然后通过负载均衡和反向代理映射到上面两个地址上。

准备nginx

安装nginx

依然采用yum安装方式

yum install nginx –y

配置nginx

修改配置文件/etc/nginx/nginx.conf

#For more information on configuration,see:
#*Official English Documentation: http://nginx.org/en/docs/
#*Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

events {
   worker_connections 1024;
}

http {
   include /etc/nginx/mime.types;
   default_type application/octet-stream;

   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;
   tcp_nodelay        on;

   #开启gzip压缩
   gzip  on;
   gzip_disable "MSIE [1-6]\.(?!.*SV1)";

   #设定请求缓冲
   #client_header_buffer_size    1k;
   #large_client_header_buffers  44k;
   client_body_buffer_size 1024k;
   client_max_body_size 100m;

   include /etc/nginx/conf.d/*.conf;
   include /etc/nginx/sites-enabled/*;

   upstream www.tonggu.com {
       server localhost:8001;
       server localhost:8002;
    }

   server {
       #侦听8080端口
       listen       8080;
       server_name  localhost;
       index        welcome.html;

       #默认请求
       location / {
            root /var/lib/docker/data;
            expires 30d;
       }

       #静态文件,nginx自己处理
       location ~ ^/(images|javascript|js|css|flash|media|static)/{
           root /var/lib/docker/data;
           #过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
           expires 30d;
       }

       #静态文件,nginx自己处理
       location ~ ^/(upload)/ {
           root /var/lib/docker/data;
           #过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
           expires 30d;
       }

       #设定访问静态文件直接读取不经过tomcat
       location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|html|mp3|apk|doc|xls)$
       {
           root /var/lib/docker/data;
           expires      30d;
       }       

	    #处理tomcat请求
	    location ~ \.whtml$ {
	         proxy_redirect off;
	         proxy_set_header Host $host;
	         proxy_set_headerX-Real-IP $remote_addr;
	         proxy_set_headerX-Forwarded-For $proxy_add_x_forwarded_for;
	         proxy_passhttp://www.tonggu.com; 
	    }

        # 定义错误提示页面
        error_page  500 502 503 504 /50x.html;
            location = /50x.html {
            root  html;
        }
    }
}

启动nginx

启动

sudo service start nginx

停止

sudo service stop nginx

重启加载配置文件

sudo nignx –s reload

现在通过以下地址就能访问了:
http://ec2-54-223-252-143.cn-north-1.compute.amazonaws.com.cn:8080/erp-web/welcome.html

至此已经完成了nginx+docker的配置,这个配置不算复杂,当然在操作过程中主要把时间花在解决镜像源上和数据库无法连接上,前面已经标记出了这些坑及解决方法。

Session共享文件及解决方案

下面讲下如何解决session共享问题,首先想到的就是用spring session解决共享问题。其实现在很多项目都做前后端分离,一般都是采用在http头中添加token的方式来解决不同用户不同凭证的问题。当然也可以采用cookie的方式来解决,而spring session都能解决。

关于spring session的配置及其原来在网上有很多,这里主要讲下我碰到的问题。

第一次配置的时候直接返回docker里面的服务是生效的,当通过nginx却不生效,开始我以为是nginx的问题,导致session丢失,但通过nginx连接单台docker却是生效的。(我这里的生效主要是项目比较特殊,不生效的时候可以登录,但进不了主页。另外生效的时候在redis里面可以看到spring放进去的key,未生效的时候看不到。)

以下是我的配置:

web.xml的配置:

<filter>
   <filter-name>springSessionRepositoryFilter</filter-name>
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
   <filter-name>springSessionRepositoryFilter</filter-name>
   <url-pattern>/*</url-pattern>
   <dispatcher>REQUEST</dispatcher>
   <dispatcher>ERROR</dispatcher>
</filter-mapping>

HttpSessionConfig.java

@EnableRedisHttpSession
public class HttpSessionConfig {

}

application-redis.xml

<context:annotation-config/>
<context:component-scanbase-package="com.saier.erp"/>
<!-- 创建redis工厂 -->
    <beanid="jedisFactory"       class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <propertyname="hostName"value="XXX.XXX.XXX.XXX"/>
        <propertyname="port"value="6379"/>
        <propertyname="usePool"value="true"/>
</bean>

后来看了下spring session及其servelet的原理,spring session本质上是加了一层filter,在filter里面处理session,这样的话这个filter必须配置在第一个,如果有多个filter的话。

于是调整了下filter的位置放到了第一个。再看调用顺序是listener——>filter——>interceptor,发现我在listener和interceptor里面有使用到session的东西。而因为filter在listener之后,所以在listener里面是取不到spring session设置的session内容的,而interceptor可以。于是把listener里面获取session的内容挪到interceptor里面再处理。

其他还有就是启动的时候报如下错误的话加上下面这句就可以了。

错误内容:

org.springframework.beans.factory.NoSuchBeanDefinitionException:No bean named ‘springSessionRepositoryFilter’ is defined

新增配置:

<context:component-scanbase-package="org.springframework.web.filter.DelegatingFilterProxy"/>
发布了19 篇原创文章 · 获赞 14 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章