前言
我相信很多人對Docker部署感到頭疼,我也是。
最近發現了一個有趣的現象:當一個人想學習某項技術的時候,他已經學會了,但是當出現問題,需要學習另一項技術的時候,無論他之前多么努力的學習,他都會以99%的概率在這一步放棄我想把這種現象稱為學習窗口
寫一個網站,學習Vue.js是很多人的學習窗口,只要你離開這個學習窗口他們不想學:我學了這么多,小草,為什么最后還要學部署。
因此,本文將與您分享一些關于Docker部署的事情。
需求
根據國際慣例,從一個非常簡單的要求開始,它只完成幾件事:
顯示待辦事項列表添加待辦事項列表
記錄網站的訪問量
上面是一個經典的Todo List應用程序。
分析需求:待辦事項需要數據庫完成,網站訪問量需要高速讀緩存記錄。
技術選擇
目前我的前端技術棧是React.js,所以前端使用react.js。
因為快遞有自己的腳手架,所以后端采用快遞。
數據庫方面,因為我自己用的是M1 Mac,所以mysql鏡像拉不出來,所以暫時用mariadb代替。
大家都很熟悉緩存,所以我們可以直接用redis來修復。
前端實現
前端的實現非常簡單,axios用于發送請求。
網站訪問量:counttodolist . map)setnewdotitle(e . target . value)type=' text '/gt,使服從
),后端實現
后端有點麻煩,需要解決的問題有:
跨域數據庫連接Redis連接
首先在main.ts中配置路由:
varcors=require(' CORS ')varindexRouter=require('/routes/index’),varusersRouter=require('/routes/count’),vartodosRouter=require('
流量路由需要redis來實現高速讀寫:
const express=require(' express '),constraedis=require(' ior edis '),constraint out=express。路由器(),//connection redis constraditis=new redis(port :6379,host:' 127.0.0.1 ',),router.get('/'),async(req,res,next)=gt,const count=Number(awaitredis . get(' my count ')
庫sequelize用于todo路由,實現數據庫連接和初始化:
constSequelize,DataTypes=require(' sequelize '),const express=require(' express '),constraint out=express路由器(),//連接數據庫const sequelize=newsequelize(host : ' localhost ',database3360' docker _ todo ',username3360' root ',password3360' 123456 ',dialect3360' mariadb ',),//define to omodelconstodo=sequelize . define(' todo ',id : type 3360 sequelize . integer,autoincrement :true,主鍵:true,title : type 3360 data type . string,status : type 3360 data type . string,//同步數據庫結構sequelize.sync (force: true)
//獲取todolistconsttodoList=awaitTodo.findAll(),res.json(todoList),)router.post('/',async(req,res,next)=gt,consttitle,status=req.body,//創建一個todoconstnewTodo=awaitTodo.create(title,status:status
本來使用以下命令就可以跑本地應用了:
#前端cdclientamp,amp,npmrunstart#后端cdserveramp,amp,npmrunstart
可是,我們本地并沒有 mariadb 和 redis,這就有點難受了。
啟動容器
如果是在以前,我一般會在 Mac 上用下面的命令安裝一個 mariadb 和 redis:
brewinstallmariadbbrewinstallredis
然后在 自己電腦 里一通配置(username, password...),最后才能在本地跑項目,非常麻煩而且一旦配置錯了,草,又要重裝
而 Docker 其中一個作用就是將上面 mariadb 和 redis 都打成不同 image(鏡像),使用 DockerHub 統一管理,使用 Docker 就可以快速配置一個服務。
以前只能一個電腦裝一個 MySQL,現在我能同時跑 8 個 MySQL 容器(不同端口),想刪誰刪誰,想裝誰裝誰遇事不決,先把容器重啟,重啟不行,再用鏡像構建一個容器,構建不行,再拉一個 latest 的鏡像,再構建一次,非常的帶勁
廢話不多說,先來把 redis 啟動:
dockerrun——namedocker—todo—redis—p6379:6379—dredis
然后再把 mariadb 啟動:
dockerrun—p127.0.0.1:3306:3306——namedocker—todo—mariadb—eMARIADB_ROOT_PASSWORD=123456MARIADB_DATABASE=docker_todo—dmariadb
解釋一下參數 —p 是端口映射:本機:容器,—e 指定環境變量,—d 表示后臺運行。
再次運行:
#前端cdclientamp,amp,npmrunstart#后端cdserveramp,amp,npmrunstart
貌似一切都很 OK 的樣子~
docker—compose
試想一下,如果現在給你一個機器,請問你要怎么部署你要先跑上面兩條 docker 命令,再跑下面兩條 npm 的命令,麻煩
能不能一鍵拉起 mariadb, redis 2 個容器呢這就是 docker—compose.yml 的由來
version:'3'services:mariadb:image:mariadbcontainer_name:'docker—todo—mariadb'environment:MARIADB_ROOT_PASSWORD:'123456'MARIADB_DATABASE:'docker_todo'ports:—'3306:3306'restart:alwaysredis:image:rediscontainer_name:'docker—todo—redis'ports:—'6379:6379'restart:always
這個 yml 文件描述的內容其實就等同于上面兩條 docker 命令。好處有兩個:
不用寫一串長長長長長長長長長長長長長長得讓人受不了的命令 把部署命令記到小本本 docker—compose.yml 文件里問:怎么部署
以后,一鍵跑本地服務的時候就可以一鍵啟動 mariadb 和 redis 了:
docker—compose—fdev—docker—compose.ymlup—d Dockerfile
不過,在生產環境時每次都要跑 npm 這兩條命令還是很煩,能不能把這兩行也整全到 docker—compose 里呢。
注意:生產環境應該要用 npm run build 構建應用,然后再跑構建出來的 JS 才是正常開發流程,這里為了簡化流程,就以 npm run start 來做例子說明。
既然 docker—compose 是通過 image 創建容器的,那么我們的 React App 和 Express App 也打成兩個 image,然后用 docker—compose 分別創建容器不就 OK 了么。
構建容器說白了就是我們常說的 ,CICD 或者構建流水線,,只不過這個 ,流水線, 關鍵的只有一條 npm run start描述 ,流水線, 的叫 Dockerfile (注意這里不是駝峰寫法)
注意:正常的鏡像構建和啟動應該是整個項目 CICD 其中的一環,這里只是打個比方項目的 CICD 除了跑命令,構建應用,還會有代碼檢查,脫敏檢查,發布消息推送等步驟,是更為繁雜的一套流程
先把 React 的 Dockerfile 整了:
#使用node鏡像FROMnode#準備工作目錄RUNmkdir—p/app/clientWORKDIR/app/client#復制package.jsonCOPYpackage*.json/app/client/#安裝目錄RUNnpminstall#復制文件COPY./app/client/#開啟DevCMD("npm","run","start")
非常的簡單,需要注意的是容器也可以看成一個電腦里的電腦,所以把自己電腦的文件復制到 ,容器電腦, 里是非常必要的一步。
Express App 的 Dockerfile 和上面的幾乎一毛一樣:
#使用node鏡像FROMnode#初始化工作目錄RUNmkdir—p/app/serverWORKDIR/app/server#復制package.jsonCOPYpackage*.json/app/server/#安裝依賴RUNnpminstall#復制文件COPY./app/server/#開啟DevCMD("npm","run","start")
那么現在再來改造一個 prod—docker—compose.yml 文件:
version:'3'services:client:build:context:./clientdockerfile:Dockerfilecontainer_name:'docker—todo—client'#暴露端口expose:—3000#暴露端口ports:—'3000:3000'depends_on:—serverrestart:alwaysserver:#構建目錄build:context:./serverdockerfile:Dockerfile#容器名container_name:'docker—todo—server'#暴露端口expose:—4200#端口映射ports:—'4200:4200'restart:alwaysdepends_on:—mariadb—redismariadb:image:mariadbcontainer_name:'docker—todo—mariadb'environment:MARIADB_ROOT_PASSWORD:'123456'MARIADB_DATABASE:'docker_todo'ports:—'3306:3306'restart:alwaysredis:image:rediscontainer_name:'docker—todo—redis'ports:—'6379:6379'restart:always
上面的配置應該都不難理解,不過,還是有一些細節需要注意:
端口都要暴露出來,也要做映射,不然本地也訪問不了 3000 和 4200 端口 depends_on 的作用是等 maraidb 和 redis 兩個容器起來了再啟動當前容器
然后運行下面命令,一鍵啟動:
docker—compose—fprod—docker—compose.ymlup—d——build
后面 ——build 是指每次跑時都構建一次鏡像。
可是,Boom:
ConnectionRefusedError:connectECONNREFUSED127.0.0.1:3306...
怎么連不上了。
解決連不上的問題
連不上的原因是我們這里用了 localhost 和 127.0.0.1。
雖然每個容器都在我們主機 127.0.0.1 網絡里,但是容器之間是需要通過對方的 IP 地址來交流和訪問的,按照官網的介紹 通過 Container Name 就可得知對方容器的 IP。
因此,Express App 里的 host 不能寫 127.0.0.1,而要填 docker—todo—redis 和 docker—todo—mariadb下面用環境變量 NODE_ENV 來區分是否以 Docker 啟動 App
修改 mariadb 的連接:
//連接數據庫constsequelize=newSequelize(host:process.env.NODE_ENV='docker'。'docker—todo—mariadb':"127.0.0.1",database:'docker_todo',username:'root',password:'123456',dialect:'mariadb',),
再修改 redis 的連接:
constredis=newRedis(port:6379,host:process.env.NODE_ENV='docker'。'docker—todo—redis':"127.0.0.1",),
然后在 /server/Dockerfile 里添加 NODE_ENV=docker:
#使用node鏡像FROMnode#初始化工作目錄RUNmkdir—p/app/serverWORKDIR/app/server#復制package.jsonCOPYpackage*.json/app/server/ENVNODE_ENV=docker#安裝依賴RUNnpminstall#復制文件COPY./app/server/#開啟DevCMD("npm","run","start")
現在繼續運行我們的 ,一鍵啟動, 命令,就能啟動我們的生產環境了:
docker—compose—fprod—docker—compose.ymlup—d——build 總結
一句話總結,Dockerfile 是用于構建 Docker 鏡像的,跟我們平常接觸的 CICD 或者流水線有點類似而 docker—compose 的作用則是 ,一鍵拉起, N 個容器
上面整個例子放在 Github 這里了,可以 Clone 下來自己搗鼓玩玩。
本文地址:http://www.dayishuiji.com/finance/8735.html - 轉載請保留原文鏈接。免責聲明:本文轉載上述內容出于傳遞更多信息之目的,不代表本網的觀點和立場,故本網對其真實性不負責,也不構成任何其他建議;本網站圖片,文字之類版權申明,因為網站可以由注冊用戶自行上傳圖片或文字,本網站無法鑒別所上傳圖片或文字的知識版權,如果侵犯,請及時通知我們,本網站將在第一時間及時刪除。 |