将你的前端應用打包成docker鏡像并部署到伺服器?僅需一個腳本搞定
1.前言
前段時間,自己搞了個阿裡雲的伺服器。想自己在上面折騰,但是不想因為自己瞎折騰而污染了現有的環境。畢竟,現在的阿裡雲已經沒有免費的快照服務了。要想還原的話,最簡單的辦法就是重新裝系統。而一旦重裝,之前的搭建的所有環境就都白搭了。
再加上之前本身就想引入docker,是以就打算利用docker容器來部署這次的前端應用。
2.建構前端應用
在打包之前,首先需要一個可正常運作的前端應用。這個可以使用umi或者create-react-app來建構。
3.nginx的預設配置檔案
然後需要在項目中加上預設nginx配置檔案。
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
4.編寫本地建構腳本
4.1. 移除上次的目錄和Dockerfile
!/bin/bash
if [ -d "./dist" ]; then
rm -rf ./dist
fi
if [ -f "./Dockerfile" ]; then
rm -f ./Dockerfile
因為每次更改後dist中的内容肯定與之前不同,其實這一步顯得不是那麼必要。運作npm的打包指令也會自動清楚該目錄。
而清除Dockerfile則是為了防止更新了Dockerfile,而這次卻不能得到最新的配置。
4.2. 打包前端應用
執行前端的打包指令,生成靜态檔案目錄。
yarn build
4.3. 生成Dockerfile
echo "FROM nginx:latest" >> ./Dockerfile
echo "COPY ./dist /usr/share/nginx/html/" >> ./Dockerfile
echo "COPY ./default.conf /etc/nginx/conf.d/" >> ./Dockerfile
echo "EXPOSE 80" >> ./Dockerfile
FROM制定了該定制容器的基礎鏡像為nginx:latest;COPY命裡将打包好的靜态檔案目錄複制到容器内的/usr/share/nginx/html/目錄下,然後将nginx的配置寫入容器中對應的位置; EXPOSE則是設定對外暴露容器的80端口。
4.4. 生成并推送定制image
docker build -t detectivehlh/mine .
docker login -u detectivehlh -p
docker push detectivehlh/mine
這裡是在開發本地,使用docker指令來打包,是以該腳本對docker有強依賴。build指令表示打包docker應用的,-t選項則制定了docker鏡像的名字和tag,tag會預設為latest。
然後登入dockerHub,将定制好的鏡像推送到dockerHub中。detectivehlh就是dockerHub的使用者名,mine是image的名字。
4.5. 删除tag為none的無用image
第一次建構不會生成tag為none的image,但是後面每次再次執行該指令就會出現這樣的情況。是以每次建構了一個新的image後,需要清除調不需要的image。
docker images | grep none | awk '{print $3}' | xargs docker rmi
使用grep指令比對到tag為none的image,awk是一個強大的文本分析工具,{print $3}表示列印出比對到的每一行的第三個字段,也就是docker的image id。如果是$0的話表示目前整行的資料。
xargs是一個給其他指令(也就是後面的docker rmi)傳遞參數的一個過濾器,将标準輸入轉換成指令行參數。
總結來說,上述指令就是找到tag為none的image的ID,然後使用docker rmi指令移除該image。
4.6. 執行部署
cmd="cd ~ && sh deploy.sh mine"
ssh -t USER_NAME@IP_ADDRESS "bash -c "${cmd}""
通過ssh指令,登入遠端伺服器,并且執行參數中的腳本。
deploy.sh是放在服務端的建構腳本。放在預設的登入使用者下。我們發現,後面還跟了個mine,這是在伺服器上運作的docker鏡像的名字。這裡暫時沒有對container的名字加上hash,因為自己的小項目,暫時沒有必要。
在項目中的完整建構腳本如下。
rm -rf ./dist
rm -f ./Dockerfile
-
編寫伺服器部署腳本
從上面步驟來看,我們還需要一個伺服器端的部署腳本。大家可能會說,标題不是說一個腳本搞定嗎?em。。。伺服器一個,本地一個...簡稱隻需一個腳本。
5.1 接收參數
在本地的建構腳本中,我們傳入了docker運作的container的名字。在伺服器建構腳本中需要來接收它。然後更新剛剛推送的docker image。
name=$1
docker pull detectivehlh/$name
5.2. 啟動container
在啟動container時我們會面對兩種情況,名字為傳入參數的container已經在運作了。而在此時如果再次運作docker run指令就會報錯而導緻我們無法使用最新的container,也無法達到更新應用的目的。
if docker ps | grep $name | awk {'print $(NF)'} | grep -Fx $name; then
echo "Container mine is already start"
docker stop $name
docker rm $name
docker run -d --name $name -p 3000:80 detectivehlh/$name
else
echo "Container mine is not start!, starting"
docker run -d --name $name -p 3000:80 detectivehlh/$name
echo "Finish starting"
是以在這裡做一個判斷,第一個if判斷如果存在名字為傳入參數的container正在運作,就停止目前容器再重新啟動。如果不存在則直接啟動容器。
run指令就不過多解釋了。-d表示背景運作容器并傳回容器ID,--name表示設定容器的名字,-p表示設定端口,将阿裡雲伺服器的3000端口映射到容器的80端口,最後一句表示要啟動哪個image(好像還是解釋了一遍)。
最後一句就是移除多次更新後出現的tag為none的無用鏡像。完整的腳本如下。
echo "Container mine is already start"
docker stop $name
docker rm $name
docker run -d --name $name -p 3000:80 detectivehlh/$name
echo "Container mine is not start!, starting"
docker run -d --name $name -p 3000:80 detectivehlh/$name
echo "Finish starting"
-
如果你隻是想打個包
看到标題進來的兄dei,如果隻是想打包一個docker鏡像,那麼你隻需要Dockerfile檔案和docker build指令就OK了。
-
總結
最初寫這個腳本,主要目的是為了友善。是以腳本中為了達到這個目的做了一些調整。最終我達成了滿足我需求的一個友善的部署腳本。
它的友善展現在,當我完成了項目代碼的更新,隻需要跑一下這個腳本,然後等待一會兒,項目就會自動打包成docker image,并且自動的在我的伺服器上運作該container。
但是這種方式會給實際的生産環境帶來一些不可控的問題。比如,腳本必須不能上傳,因為涉及一些伺服器的敏感資訊。但是如果你不小心上傳了,那你的伺服器就相當于裸奔了;再比如,你對你的代碼必須要十分自信,沒有經過測試的代碼就直接部署,會帶來一些風險。
如果是自己用的,那完全不用擔心,想怎麼搞怎麼搞。但是如果是開放給所有人用的并且有一定的通路量,比如部落格,那麼對于其他使用者來說,這種方式就不怎麼友好。
是以我的觀點是,分情況來。目前來說我的項目隻有少數幾個人在用,也還在處于疊代階段。并且代碼倉庫是私有的,是以我完全不用擔心隐私的問題。服務未經測試就直接上線對于我來說,其實問題也不大。首先我會在本地測試,确認無誤後才會執行部署操作。是以在不同的階段,找到最适合自己的方案就OK。
原文位址
https://www.cnblogs.com/detectiveHLH/p/10756702.html