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<wangjingfeng2008@163.com>
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"/>
来源:CSDN
作者:wjf8882300
链接:https://blog.csdn.net/wjf8882300/article/details/103772835