本文目錄
顯示
1.
docker化改造
1.1.
改造后目錄結構
2.
docker化過程的困難和解決方法
2.1.
spring-boot雙配置切換
2.2.
動態(tài)配置axios的baseurl地址
2.3.
nuxt在docker中無法訪問到宿主機ip問題
2.3.1.
這樣就產生了問題
2.3.2.
如何解決呢
2.4.
dockerfile的環(huán)境參數統一設置
3.
總結
4.
推薦閱讀
在今年年初的時候,完成了自己的一個fame博客系統的實現,當時也做了一篇博文spring-boot vue = fame 寫blog的一次小結,作為記錄和介紹。從完成實現到現在,也斷斷續(xù)續(xù)的根據實際的使用情況進行更新。
只不過每次上線部署的時候都覺得有些麻煩,因為我的服務器內存太小,每次即使只更新了前臺部分(fame-front)的代碼,在執(zhí)行npm build的時候都還必須把我的后端服務(fame-server)的進程關掉,不然會造成服務器卡死(慘啊)。
而且這個項目是前后端分離的,博客前臺頁面還為了seo用了nuxt框架,假如是第一次部署或者要服務器遷移的話,麻煩的要死啊,部署一次的話要以下步驟:
安裝mysql,修改相關配置文件,設置編碼時區(qū)等,然后重啟下載安裝java,配置java環(huán)境下載安裝maven,配置maven環(huán)境下載安裝nginx,修改配置文件,設計反向代理等啟動spring-boot項目打包vue項目npm install,npm run build等啟動nuxt項目,npm install,npm run start等如果能夠順利的完成這七個步驟算是幸運兒了,假如中間哪個步驟報錯出了問題,可能還要回頭查找哪個步驟出了問題,然后又重新部署。
在這些需求面前,docker就是解決這些問題的大殺器。無論是其虛擬化技術隔離各個容器使其資源互不影響,還是一致的運行環(huán)境,以及docker-compose的一鍵部署,都完美的解決了上述問題。
項目地址:https://github.com/zzzzbw/fame
docker和docker-compose安裝
docker和docker-compose的功能和使用可以看線上的一個中文文檔docker — 從入門到實踐
下面是centos7安裝和配置docker以及docker-compose的shell腳本,其他操作系統可以參考修改來安裝。其中docker版本為docker-ce,docker-compose版本為1.22.0
#!/bin/sh###更新###yum-yupdate###安裝docker####安裝一些必要的系統工具sudoyuminstall-yyum-utilsdevice-mapper-persistent-datalvm2#添加軟件源信息sudoyum-config-manager--add-repohttp://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo#更新yum緩存sudoyummakecachefast#安裝docker-cesudoyum-yinstalldocker-ce#啟動docker并設置為開機啟動(centos7)systemctlstartdocker.servicesystemctlenabledocker.service#替換docker為國內源echo\'{\registry-mirrors\:[\https://registry.docker-cn.com\],\live-restore\:true}\'>/etc/docker/daemon.jsonsystemctlrestartdocker#安裝dokcer-composesudocurl-lhttps://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname-s`-`uname-m`-o/usr/local/bin/docker-composechmod x/usr/local/bin/docker-compose#安裝命令補全工具yum-yinstallbash-completioncurl-lhttps://raw.githubusercontent.com/docker/compose/$(docker-composeversion--short)/contrib/completion/bash/docker-compose>/etc/bash_completion.d/docker-compose###安裝docker結束###docker化改造
改造后目錄結構
先看一下改造后的項目的結構
├─fame││.env//docker-compose環(huán)境參數配置文件││docker-compose.yml//docker-compose文件│├─fame-docker│││fame-front-dockerfile//fame-front的dockerfile文件│││fame-server-dockerfile//fame-server的dockerfile文件│││││├─fame-admin│││fame-admin-dockerfile//fame-admin的dockerfile文件│││nginx.conf//fame-admin的nginx服務器配置文件│││││├─fame-mysql│││fame-mysql-dockerfile//mysql的dockerfile文件│││mysqld.cnf//mysql的配置文件mysqld.cnf│││││└─fame-nginx││nginx-dockerfile//整個項目的nginx服務器的dockerfile文件││nginx.conf//整個項目的nginx的配置文件│││├─fame-admin//博客管理后臺,基于vue elementui│├─fame-front//博客前端,基于nuxt│└─fame-server//博客服務端,基于spring-boot為了不破壞原有項目的結構,無論前端還是后端的docker的相關配置文件全部提取出來,單獨放在了fame-docker文件夾中。
docker-compose.yml放在項目根目錄下,直接在根目錄運行命令:docker-compose up -d
[root@localhostfame]#docker-composeup-dstartingfame-front...startingfame-admin...startingfame-front...donestartingfame-admin...donestartingfame-nginx...done就啟動項目了,再也不用重復繁瑣的步驟!
改造后的docker-compose.yaml文件
version:\'3\'services:fame-nginx:container_name:fame-nginxbuild:context:./dockerfile:./fame-docker/fame-nginx/nginx-dockerfileports:-\80:80\volumes:-./logs/nginx:/var/log/nginxdepends_on:-fame-server-fame-admin-fame-frontfame-mysql:container_name:fame-mysqlbuild:context:./dockerfile:./fame-docker/fame-mysql/fame-mysql-dockerfileenvironment:mysql_database:famemysql_root_rootmysql_root_host:\'%\'tz:asia/shanghaiexpose:-\3306\volumes:-./mysql/mysql_data:/var/lib/mysqlrestart:alwaysfame-server:container_name:fame-serverrestart:alwaysbuild:context:./dockerfile:./fame-docker/fame-server-dockerfileworking_dir:/appvolumes:-./fame-server:/app-~/.m2:/root/.m2-./logs/fame:/app/logexpose:-\9090\command:mvncleanspring-boot:run-dspring-boot.run.profiles=docker-dmaven.test.skip=truedepends_on:-fame-mysqlfame-admin:container_name:fame-adminbuild:context:./dockerfile:./fame-docker/fame-admin/fame-admin-dockerfileargs:base_url:${base_url}expose:-\3001\fame-front:container_name:fame-frontbuild:context:./dockerfile:./fame-docker/fame-front-dockerfileenvironment:base_url:${base_url}proxy_host:${proxy_host}proxy_port:${proxy_port}expose:-\3000\docker-compose.yml的結構和剛才目錄結構大體類似,也是分以下幾個部分
fame-nginxfame-mysqlfame-serverfame-adminfame-front這個docker-compose.yml中有幾個要點
fame-mysql和fame-server的restart要設置為always,因為目前docker-compose是沒有一個方案可以解決容器啟動的先后的問題的。即使設置了depends_on,那也只是控制容器開始啟動的時間,不能控制容器啟動完成的時間,所以讓fame-mysql和fame-server這兩個容器設置restart,防止spring-boot在mysql啟動完成之前啟動而報錯啟動失敗fame-server,fame-mysql,fame-nginx這三個容器都設置了volumes,把容器里的logs日志文件掛載到宿主機的項目目錄里,方便隨時看日志文件fame-mysql容器的mysql存儲文件也設置了volumes掛載在項目目錄里(./mysql/mysql_data:/var/lib/mysql),這個建議大家可以根據實際的情況設置到宿主機的其他目錄里,不然不小心刪除項目的話那么容器里的數據庫數據也都沒了幾個鏡像的dockerfile大部分都比較簡單,這部分就不全部詳細介紹了,可以直接去我項目中了解。
docker化過程的困難和解決方法
spring-boot雙配置切換
為了能夠讓spring-boot能夠在開發(fā)環(huán)境和docker環(huán)境下快速切換,需要將spring-boot的配置文件進行修改
└─fame-server...│└─resources││application-dev.properties││application-docker.properties││application.properties在原有的application.properties基礎上增加application-dev.properties和application-docker.properties配置文件,把application.properties里的數據庫日志等信息分別放到application-dev.properties和application-docker.properties這兩個文件中,實現開發(fā)環(huán)境和docker環(huán)境的快速切換。
#application.properties文件#端口號server.port=9090#mybatismybatis.type-aliases-package=com.zbw.fame.model#mappermapper.mappers=com.zbw.fame.util.mymappermapper.not-empty=falsemapper.identity=mysql#mailspring.mail.properties.mail.smtp.auth=truespring.mail.properties.mail.smtp.starttls.enable=truespring.mail.properties.mail.smtp.starttls.required=true#默認propertiesspring.profiles.active=dev~
#application-docker.properties文件#datasourcespring.datasource.driverclassname=com.mysql.jdbc.driverspring.datasource.url=jdbc:mysql://fame-mysql:3306/fame?useunicode=true&characterencoding=utf-8&usessl=falsespring.datasource.username=rootspring.datasource.password=root#loglogging.level.root=infologging.level.org.springframework.web=infologging.file=log/fame.logapplication-dev.properties的內容和application-docker.properties文件類似,只是根據自己開發(fā)環(huán)境的情況修改mysql和log配置。
動態(tài)配置axios的baseurl地址
在fame-admin和fame-front中用了axios插件,用于發(fā)起和獲取fame-server服務器的請求。在axios要配置服務器url地址baseurl,那么通常開發(fā)環(huán)境和docker環(huán)境以及生產環(huán)境的url可能都不一樣,每次都去修改有點麻煩。(雖然只需要配置兩處,但是代碼潔癖不允許我硬編碼這個配置)。
1.先修改fame-admin(vue)使其兼容手動部署模式和docker模式
fame-admin是基于vue cli 3搭建的,相對于cli 2.0官方把webpack的一些配置文件都封裝起來了,所以沒有config和build文件夾。不過對應的官網也給了一些設置更加方便的配置參數。
在官方文檔中提到:
只有以 vue_app_ 開頭的變量會被 webpack.defineplugin 靜態(tài)嵌入到客戶端側的包中。你可以在應用的代碼中這樣訪問它們:
console.log(process.env.vue_app_secret)在構建過程中,process.env.vue_app_secret 將會被相應的值所取代。在 vue_app_secret=secret 的情況下,它會被替換為 \”sercet\”。
利用這個特性來設置環(huán)境變量來動態(tài)的設置docker模式和手動部署模式的baseurl的值
在fame-admin目錄下創(chuàng)建文件server-config.js,編寫以下內容
constisprod=process.env.node_env===\'production\'constlocalhost=\'http://127.0.0.1:9090/\'constbaseurl=process.env.vue_app_api_url||localhostconstapi=isprod?baseurl:localhostexportdefault{isprod,api}那么只要在環(huán)境變量中有vue_app_api_url的值,且node_env === \’production\’,baseurl就等于vue_app_api_url的值,否則就是localhost的值。
接著在axios配置文件中引用該文件設置
//fame-admin/src/plugins/http.js...importserverconfigfrom\'../../server-config\'constaxios=axios.create({baseurl:serverconfig.api \'api/\',...})...現在只要將docker的環(huán)境變量設置一個vue_app_api_url的值就行了,只要在對應的dockerfile中增加一個步驟就可以了。
envvue_app_api_urlhttp://xx.xxx.xxx.xxx2.再修改fame-front(nuxt)使其兼容手動部署模式和docker模式,同樣的,對于用nuxt搭建fame-front博客前臺修改也是類似的思路。在nuxt的官方文檔中寫到:
nuxt.js 讓你可以配置在客戶端和服務端共享的環(huán)境變量。
例如 (nuxt.config.js):
module.exports={env:{baseurl:process.env.base_url||\'http://localhost:3000\'}}以上配置我們創(chuàng)建了一個 baseurl 環(huán)境變量,如果應用設定了 base_url 環(huán)境變量,那么 baseurl 的值等于 base_url 的值,否則其值為http://localhost:3000。
所以我們只要和官方文檔說的一樣,在nuxt.config.js文件中增加代碼就可以了
module.exports={env:{baseurl:process.env.base_url||\'http://localhost:3000\'}}接著在server-config.js文件和axios的配置文件fame-front/plugins/http.js以及對應的dockerfile文件中編寫和上面fame-admin部分一樣的代碼就可以了
現在已經把baseurl的設置從代碼的硬編碼中解放出來了,但事實上我們只是把這個參數的編碼從代碼從轉移到dockerfile文件里了,要是想要修改的話也要去這兩個文件里查找然后修改,這樣也不方便。后面會解決這個問題把所有環(huán)境配置統一起來。
nuxt在docker中無法訪問到宿主機ip問題
先要說明一點,為什么博客前端要單獨去使用的nuxt而不是和博客后臺一樣用vue呢,因為博客前端有seo的需求的,像vue這樣的對搜索引擎很不友好。
所以nuxt的頁面是服務器端渲染(ssr)的
這樣就產生了問題
fame-front的頁面在渲染之前必須獲取到fame-server服務器中的數據,但是每個docker容器都是互相獨立的,其內部想要互相訪問只能通過容器名訪問。例如容器fame-front想要訪問容器fame-server,就設置baseurl = fame-server(fame-server是服務器的容器的container_name)。
這樣設置之后打開瀏覽器輸入網址:http://xx.xxx.xxx.xx可以成功…,但是隨便點擊一個鏈接,就會看到瀏覽器提示錯誤無法訪問到地址http://fame-server/…
vendor.e2feb665ef91f298be86.js:2gethttp://fame-server/api/article/1net::err_connection_refused這是必然的結果,在容器里http://fame-server/就是服務器…,但是你本地的瀏覽器當然是不知道http://fame-server/是個什么鬼…,所以就瀏覽器就報出無法訪問的錯誤。
什么?可是剛才不是說nuxt是服務器渲染的頁面嗎,怎么又讓本地瀏覽器報這個錯誤了。
原來是因為當通過瀏覽器鏈接直接訪問的時候,nuxt的確是從后端渲染了頁面再傳過來,但是在頁面中點擊鏈接的時候是通過vue-router跳轉的,這時候不在nuxt的控制范圍,而是和vue一樣在瀏覽器渲染的,這時候就要從瀏覽器里向服務端獲取數據來渲染,瀏覽器就會報錯。
如何解決呢
這個問題開始的時候一直想要嘗試配置docker容器的網絡模式來解決,可是都沒有解決。直到后面我看axios文檔的時候才注意到axios的代理功能,其本質是解決跨域的問題的,因為只要在axios設置了代理,在服務端渲染的時候就會使用代理的地址,同時在瀏覽器訪問的時候會用baseurl 的地址,這個特點完美解決我的問題啊。
在server-config.js文件里增加以下代碼(在nuxt.config.js里獲取環(huán)境變量里的proxyhost和proxyport)
...constlocalproxy={host:\'127.0.0.1\',port:9090}constbaseproxy={host:process.env.proxyhost||localproxy.host,port:process.env.proxyport||localproxy.port}exports.baseproxy=isprod?baseproxy:localproxy...然后在axios配置文件里增加代碼
//fame-front/plugins/http.jsconstaxios=axios.create({proxy:serverconfig.baseproxy...})...就可以完美的解決問題了。
dockerfile的環(huán)境參數統一設置
在上文解決動態(tài)配置axios地址的部分把baseurl的設置放在了dockerfile中,現在就再把dockerfile中的硬編碼提取出來,放到統一的配置文件中。
首先在docker-compose.yml文件目錄下(即項目跟目錄)創(chuàng)建環(huán)境文件.env并編寫一下內容
base_url=http://xx.xxx.xxx.xxxproxy_host=fame-nginxproxy_port=80這個是docker-compose的env_file參數,從文件中獲取環(huán)境變量,可以為單獨的文件路徑或列表,如果同目錄下有.env文件則會默認讀取,也可以自己在docker-compose里設置路徑。
已經在.env設置了環(huán)境變量base_url的值,就能在docker-compose.yml里直接使用了。修改docker-compose.yml的fame-front部分:
fame-front:...environment:base_url:${base_url}proxy_host:${proxy_host}proxy_port:${proxy_port}...這樣在fame-front的容器里就有對應的base_url,proxy_host,proxy_port環(huán)境變量,nuxt也能夠成功獲取并設置。
不過對于fame-admin容器來說就要稍微復雜一點點了。先來看一下fame-admin容器的dockerfile文件fame-admin-dockerfile
#buildstagefromnode:10.10.0-alpineasbuild-stage#中間一些操作省略...runnpmrunbuild#productionstagefromnginx:1.15.3-alpineasproduction-stagecopy./fame-docker/fame-admin/nginx.conf/etc/nginx/conf.d/default.confcopy--from=build-stage/app/dist/usr/share/nginx/htmlexpose80cmd[\nginx\,\-g\,\daemonoff;\]這里用了多階段構建容器,如果直接通過docker-compose設置環(huán)境變量只會在后面一個階段生效,但是npm run build是在第一個階段執(zhí)行的,所以環(huán)境變量不能應用到vue當中。為了讓環(huán)境變量在第一階段就應用,必須要在構建的時候就把變量從docker-compose傳到fame-admin-dockerfile中,然后在dockerfile中的第一階段把這個環(huán)境變量應用到容器里。下面修改docker-compose.yml的fame-admin部分:
fame-admin:...build:context:./dockerfile:./fame-docker/fame-admin/fame-admin-dockerfileargs:base_url:${base_url}#這里把環(huán)境變量當做arg傳給dockerfile...然后在fame-admin-dockerfile的第一階段增加步驟
#buildstagefromnode:10.10.0-alpineasbuild-stageargbase_url#必須申明這個arg才能從docker-compose里獲取envvue_app_api_url$base_url#以下省略...這樣就可以在構建階段一鏡像的時候就把環(huán)境變量傳入到階段一的鏡像里,讓vue里的變量生效了。
總結
現在網上很多復雜一點的項目即使用了docker-compose部署,也多少依賴shell腳本來操作,比如復制文件設置環(huán)境等,我覺得這樣會降低docker-compose的意義。如果都使用了shell腳本,那不如直接不用docker-compose而全用shell來構建和啟動鏡像。
所以在docker化的過程中雖然遇到一些坎坷,但堅持實現了只用docker-compose部署,以后上線和下線就及其方便了。也希望我的docker化思路可以給其他項目做一些參考。
對比以前恐怖的步驟,現在fame博客的上線和下線只需要兩行命令,真的十分的便捷。
docker-composeupdocker-composedown源碼地址 https://github.com/zzzzbw/fame