本篇原創釋出於 Flex 的個人部落格:點選跳轉
前言
在沒有 docker 前,專案轉測試是比較麻煩的一件事。首先會化較長的時間搭建測試環境,然後在測試過程中又經常出現測試說是 bug,開發說無法復現的情況,導致撕逼。
本篇記錄瞭如何將一個 java 應用部署到 docker 中。主要講述了以下幾個部分:
- docker 部署 mysql
- docker 部署 activemq
- docker 部署 elastricsearch 叢集
- docker 部署 java 應用
- docker 部署 nginx 作為靜態伺服器,及代理伺服器
專案架構如下:
本系統中有三個主要模組 OMS,DAS 和一個 Eureka 註冊中心。其中 OMS 和 DAS 使用有 activemq 訊息佇列,來進行大量資料的互動然後各自使用一個 mysql 資料庫儲存主要的業務資料。使用 elastricsearch 儲存超大量的資料。
傳統軟體部署和 docker 部署
本專案在 windows 部署時是將其作為三個部分來進行安裝的--ENV 環境包(保護 mysql,es 等),OMS 產品包,DAS 產品包。所以最初我的設想是一個容器中裝 ENV 環境包所需的所有軟體,一個容器裝 DAS,一個容器裝 OMS。然後再實踐的過程中越來越感覺不對勁,環境配置比較複雜,而且也有種把容器當虛擬機器用的感覺,一點沒有簡化的感覺。
遂停下了操作,開始學習一波 docker 到底是怎麼用的。用租房子來做比喻:
- 傳統軟體部署方式相當於租到一個零傢俱,零裝修的房子。我們想要住進去首先必須買齊必要的傢俱,然後想要住的舒心呢,還得花功夫裝飾裝飾,讓房子好看點。這樣就會對這個具體的房子產生較強的依賴,很難遷移到另一個房子中(想想那麼多的傢俱,家電,雜物。。)。如果全部放棄重新換一個房子代價又太大了。
- 使用 docker 相當於租到一個全家電,精裝修的房子。我們只需帶上自己的個人物品即可開始入住。想要換一個房子也是輕而易舉,帶上自己的東西麻溜的就換了。
使用 docker 推薦操作是一個程式放到一個容器中,做到更好的隔離性,同時也更容易進行管理。下面來使用容器技術部署我們應用。還是分為三部分,但是每個程式使用一個容器,做到 0 配置啟動容器。
實戰
在此預設已經會安裝 docker,且瞭解基本操作。如不瞭解的先看這兩篇:安裝,基本使用
部署 ENV 環境包
環境包中諸如 elastricsearch,mysql 這樣的資料儲存工具,需要滿足如下兩個要求:
- 保留資料,不論容器如何建立、銷燬,資料不能丟。
- 需要使用個性化的配置檔案,每次啟動根據該配置檔案來啟動。
部署 mysql
使用容器部署 mysql 過程如下:
- 首先從 docker.hub 中根據各自的需求 pull 對應的 mysql 映象
docker pull mysql:5.7.24
- 啟動映象
由於 mysql 是用來存資料的,資料無論什麼情況都不能丟失,所以資料存在容器外部,通過對映操作,對映到容器內部,引數如下:
# 將宿主機的路徑,對映到容器內部。這個路徑既可以是資料夾,也可以是檔案
-v hostPath:containerPath
顯然我們通過這個桉樹將外部的資料資料夾,配置檔案對映到容器中。最後啟動這個容器的命令如下:
# 假設在宿主機中資料存放路徑為/opt/mysql/data,配置檔案路徑為:/opt/mysql/my.cnf
docker run --name=mysql -itd -p 3308:3306 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /opt/mysql/data:/var/lib/mysql -v /opt/mysql/my.cnf:/etc/mysql/my.cnf -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.25
注意:如果 mysql 版本為 8.x,還需要對映容器中的/var/lib/mysql-files 目錄,否則啟動會報錯
下面介紹具體引數含義:
- -it 標準輸入輸出有關
- -d 後臺啟動
- -v 檔案對映
- -e 設定環境變數到容器中
可能你們會問為什麼要對映/etc/timezone
和/etc/timezone
,這是為了讓容器的時間和時區與宿主機保持一致。預設情況下容器為 UTC 標準時間。/etc/timezone
讓容器時間,時區和宿主機一致。但是如果不對映/etc/timezone
java 應用中的時區還是錯的,雖然使用date -R
命令檢視時間和時區都正常。
部署 elastricsearch,activeMQ 容器
es 和 activeMQ 都依賴 java 的執行環境,所以有兩種部署方式:
- 直接拉取 es,activeMQ 對應映象,通過路徑對映部署容器啟動
- 在一個 java 映象中執行 es 和 activeMQ
這裡以第二種方式為例進行說明。
建立 java 映象
這裡不從 docker hub 中拉取映象,通過 dockerfile 來製作一個自定義的映象。由於只需要一個 java 執行環境,所以只要將一個 jre 執行環境加入到一個基礎 linux 映象中即可(這裡選擇 ubuntu)。製作過程如下:
首先建立一個資料夾dockerFileTest
存放依賴和 dockerfile 檔案。
然後將下載加壓後的 jre 執行環境放到dockerFileTest/jre
目錄下。
接著在 dockerFileTest 目錄中建立 Dockerfile 檔案,內容如下:
#說明基礎映象,預設:latest
FROM ubuntu
#將當前路徑下的jre資料夾複製到新映象下的/opt/jre路徑
COPY jre /opt/jre
#設定環境變數
ENV JAVA_HOME=/opt/jre CLASSPATH=/opt/jre/lib PATH=$PATH:/opt/jre/bin
最後通過``命令生成新映象 jre:v1
建立 elastricsearch 容器
下載好 es,假設存放在/root/es1 中,通過以下命令建立一個 es 容器:
docker run --name=es1 -itd -p 9200:9200 -p 9300:9300 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/es1:/opt/es -w /opt/es jre:v1 ./bin/elasticsearch -Des.insecure.allow.root=true
引數含義如下:
- -w containerPath 設定容器工作目錄為 containerPath
上面的命令以 es1 為容器名,對映 9200,9300 到宿主機埠,以./bin/'elasticearch -Des.insecure.allow.root=true
建立 es 容器。最後加的引數為了讓 es 能夠以 root 在容器中啟動。
建立 es 叢集
將之前的 es1 複製一份命名為 es2 作為節點 2。要讓兩個 es 節點構成 es 叢集,需要讓節點間能夠進行通訊,這裡使用--link
引數來讓 es2 能夠連上 es1 構成叢集。--link
用法如下:
--link containerName[:alias]
es2 啟動命令如下:
docker run --name=es2 -itd -p 9201:9200 -p 9301:9300 --link es1 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/es2:/opt/es -w /opt/es jre:v1 ./bin/elasticsearch -Des.insecure.allow.root=true
然後就能在 es2 中通過 es1 的容器名訪問到 es1(實際是在 es2 的 host 中增加了一條記錄,將 es1 指向 es1 的 IP,該 IP 是 docker 的虛擬網路卡分配的 IP)。
但是使用--link 有一些侷限,通過該引數聯通的容器必須存在。因此該引數只能用在 B 依賴 A 的情況,如果同時 A 也依賴 B(也就是 A,B 要能夠相互訪問到),這種情況下就不能通過 link 來實現了,原因大家應該能夠想到。。。
部署 activeMQ
在容器中啟動 activeMQ 與啟動 es 稍有不同。activeMQ 預設是後臺啟動的,啟動完成後啟動程式就會退出,因此如果直接以./bin/activemq start
(假設當前目錄在 activemq 中),啟動容器會發現在 activemq 啟動成功後容器就停止執行了。會出現這種情況是因為容器中啟動的第一個程式結束後,容器就會被docker關閉掉
。所以呢我們只需讓第一個程式不結束就行了,因此需要我們編寫一個啟動指令碼來啟動 activemq 並監測執行情況,一旦 activemq 程式掛掉,就結束啟動指令碼,否則一直執行。啟動指令碼程式碼如下:
#!/bin/bash
#使用sh指令碼啟動activemq,然後定時判斷服務是否被關閉,關閉後退出指令碼,否則一致迴圈。
#為避免docker容器在active自帶的啟動指令碼執行結束後就關閉容器了。
#獲取啟動pid
out=`./bin/activemq start`
echo "$out"
pid=`echo $out | grep -Eo "pid '[0-9]+'" | grep -Eo "[0-9]+"`
echo "當前mq程式pid為:${pid}"
if [ ${#pid} = 0 ]; then
echo "啟動失敗"
exit 0
fi
while true; do
num=`ps -e | grep -cwE "$pid"`
if [ $num = 0 ]; then
echo "程式異常關閉"
exit 0
fi
sleep 1
done
然後以該指令碼作為啟動指令碼來啟動容器即可。啟動命令如下:(假設 activemq 目錄為/opt/activemq,啟動指令碼路徑為/opt/activemq/start.sh)
docker run --name=activemq -itd -p 8161:8161 -p 61616:61616 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/activemq:/opt/activemq -w /opt/activemq jre:v1 /bin/bash ./start.sh
部署 java 環境包
還是使用之前製作的 jar 映象來啟動 java 應用,這裡以部署 jar 包為例,如果部署 war 包則需要在 tomcat 映象上部署。特別注意下容器的時間和時區設定,否則 java 程式中無法獲取到正確的時間和時區。這裡通過對映宿主機的 localtime 和 timezone 檔案來讓容器時間和時區與宿主機相同。啟動命令如下:
# 啟動oms
docker run -itd --name=oms -p 8082:9090 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/oms.jar:/opt/oms.jar -w /opt --link es1 --link activemq --link oms-mysql --link eureka-server jre:v1 java -jar oms.jar
# 啟動das
docker run -itd --name=das -p 8083:9099 -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone -v /root/das.jar:/opt/oms -w /opt --link es1 --link activemq --link oms-mysql --link eureka-server jre:v1 java -jar das.jar
本篇只是記錄瞭如何使用一容器一程式
的方式來部署 java 應用.
PS:不推薦這麼直接手擼命令,建議使用 docker-compose
本篇原創釋出於 Flex 的個人部落格:點選跳轉