將你的前端應用打包成docker鏡像並部署到服務器?僅需一個腳本搞定

1.前言

前段時間,本身搞了個阿里雲的服務器。想本身在上面折騰,可是不想由於本身瞎折騰而污染了現有的環境。畢竟,如今的阿里雲已經沒有免費的快照服務了。要想還原的話,最簡單的辦法就是從新裝系統。而一旦重裝,以前的搭建的全部環境就都白搭了。html

再加上以前自己就想引入docker,因此就打算利用docker容器來部署此次的前端應用。前端

2.構建前端應用

在打包以前,首先須要一個可正常運行的前端應用。這個可使用umi或者create-react-app來構建。react

3.nginx的默認配置文件

而後須要在項目中加上默認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
fi
複製代碼

由於每次更改後dist中的內容確定與以前不一樣,其實這一步顯得不是那麼必要。運行npm的打包命令也會自動清楚該目錄。git

而清除Dockerfile則是爲了防止更新了Dockerfile,而此次卻不能獲得最新的配置。github

4.2. 打包前端應用

執行前端的打包命令,生成靜態文件目錄。docker

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端口。npm

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。bash

而後登陸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,由於本身的小項目,暫時沒有必要。

在項目中的完整構建腳本以下。

#!/bin/bash 
if [ -d "./dist" ]; then
    rm -rf ./dist
fi
if [ -f "./Dockerfile" ]; then
    rm -f ./Dockerfile
fi

yarn build

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

docker build -t detectivehlh/mine .
docker login -u detectivehlh -p ********
docker push detectivehlh/mine

docker images | grep none | awk '{print $3}' | xargs docker rmi

cmd="cd ~ && sh deploy.sh mine"
ssh -t USER_NAME@IP_ADDRESS "bash -c \"${cmd}\""
複製代碼

5. 編寫服務器部署腳本

從上面步驟來看,咱們還須要一個服務器端的部署腳本。你們可能會說,標題不是說一個腳本搞定嗎?em。。。服務器一個,本地一個...簡稱只需一個腳本。

5.1. 接收參數

在本地的構建腳本中,咱們傳入了docker運行的container的名字。在服務器構建腳本中須要來接收它。而後更新剛剛推送的docker image。

#!/bin/bash
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"
fi
docker images | grep none | awk '{print $3}' | xargs docker rmi
複製代碼

因此在這裏作一個判斷,第一個if判斷若是存在名字爲傳入參數的container正在運行,就中止當前容器再從新啓動。若是不存在則直接啓動容器。

run命令就不過多解釋了。-d表示後臺運行容器並返回容器ID,--name表示設置容器的名字,-p表示設置端口,將阿里雲服務器的3000端口映射到容器的80端口,最後一句表示要啓動哪一個image(好像仍是解釋了一遍)。

最後一句就是移除屢次更新後出現的tag爲none的無用鏡像。完整的腳本以下。

#!/bin/bash
name=$1
docker pull detectivehlh/$name
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"
fi
docker images | grep none | awk '{print $3}' | xargs docker rmi
複製代碼

6. 若是你只是想打個包

看到標題進來的兄dei,若是隻是想打包一個docker鏡像,那麼你只須要Dockerfile文件和docker build命令就OK了。

7. 總結

最初寫這個腳本,主要目的是爲了方便。因此腳本中爲了達到這個目的作了一些調整。最終我達成了知足我需求的一個方便的部署腳本。

它的方便體如今,當我完成了項目代碼的更新,只須要跑一下這個腳本,而後等待一下子,項目就會自動打包成docker image,而且自動的在個人服務器上運行該container。

可是這種方式會給實際的生產環境帶來一些不可控的問題。好比,腳本必須不能上傳,由於涉及一些服務器的敏感信息。可是若是你不當心上傳了,那你的服務器就至關於裸奔了;再好比,你對你的代碼必需要十分自信,沒有通過測試的代碼就直接部署,會帶來一些風險。

若是是本身用的,那徹底不用擔憂,想怎麼搞怎麼搞。可是若是是開放給全部人用的而且有必定的訪問量,好比博客,那麼對於其餘用戶來講,這種方式就不怎麼友好。

因此個人觀點是,分狀況來。目前來講個人項目只有少數幾我的在用,也還在處於迭代階段。而且代碼倉庫是私有的,因此我徹底不用擔憂隱私的問題。服務未經測試就直接上線對於我來講,其實問題也不大。首先我會在本地測試,確認無誤後纔會執行部署操做。因此在不一樣的階段,找到最適合本身的方案就OK。

相關文章
相關標籤/搜索