手把手docker部署java應用(初級篇)

煩囂的人發表於2019-07-04

本篇原創釋出於 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 這樣的資料儲存工具,需要滿足如下兩個要求:

  1. 保留資料,不論容器如何建立、銷燬,資料不能丟。
  2. 需要使用個性化的配置檔案,每次啟動根據該配置檔案來啟動。

部署 mysql

  使用容器部署 mysql 過程如下:

  1. 首先從 docker.hub 中根據各自的需求 pull 對應的 mysql 映象
docker pull mysql:5.7.24
  1. 啟動映象

  由於 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/timezonejava 應用中的時區還是錯的,雖然使用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 的個人部落格:點選跳轉

相關文章