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"/>