詳細實現方式以及文件下載請前往 https://www.passerma.com/article/76
本教程將從鏡像構建,容器編排,容器通信來部署實現一個基於nodejs+eggjs+mysql+nginx+react的前后端分離項目
從一個小demo來從無到有搭建一個完整的項目
以下所有代碼均可直接訪問,地址 https://gitee.com/passerma/docker-node-nginx-mysql-redis/tree/master
一、前端界面搭建
1.前端代碼預覽
前端使用react框架搭建,具體源碼請自行瀏覽,地址 https://gitee.com/passerma/docker-node-nginx-mysql-redis/tree/master/web
2.前端接口調用
前端接口調用統一使用/api作為前綴,后面好通過nginx代理來訪問后端服務
開發時代理到 http://localhost:7001
package.json
"proxy":"http://localhost:7001"
內置登錄和獲取用戶接口
src/App.tsx
import React, { useRef, useState }from"react";import"./App.css";functionApp(){const token = useRef('')const [data, setdata] = useState<{id:number,userName:string }[]>([])const login =async () => {const userName ='root'const passwd ='admin_123'const response =await fetch('/api/login', {method:'POST',headers: {'Content-Type':'application/json' },body:JSON.stringify({ userName, passwd }) }); response.json().then(res => { token.current = res.token }); }const getData =async () => {if (!token.current) { alert('請先登錄!') }else {const response =await fetch('/api/list', {method:'POST',headers: {'Content-Type':'application/json','token': token.current }, }); response.json().then(res => {if (res.errCode ===0) { setdata(res.data) } }); } }return (<divclassName="App"><buttononClick={login}>登錄</button><buttononClick={getData}>獲取數據</button> { data.length > 0 &&<ul> { data.map(res =><likey={res.id}> {res.userName}</li>) }</ul> }</div> ); }exportdefault App;
其余關於數據處理即展示請查看源碼
二、后端代碼編寫
1.后端代碼預覽
后端使用eggjs框架,具體源碼請自行瀏覽,地址 https://gitee.com/passerma/docker-node-nginx-mysql-redis/tree/master/server/server
2.后端接口編寫
后端使用eggjs框架,實現了兩個接口,即登錄和獲取用戶接口
其中登錄接口會將生成的token存入redis
然后獲取用戶接口需要攜帶token來經redis校驗獲取用戶數據
接口如下,包含/api/login和/api/list
app\router.ts
import { Application }from'egg';exportdefault (app: Application) => {const { controller, router } = app; router.post('/api/login', controller.home.login); router.post('/api/list', controller.home.list); };
其余關於接口的編寫、連接mysql數據庫、讀取redis和mysql的數據請查看源碼
3.配置文件編寫
配置文件主要是配置開發環境和生產環境的redis和mysql鏈接的
1)開發環境配置
使用本地的redis和mysql
config\config.local.ts
import { EggAppConfig, PowerPartial }from'egg'; exportdefault () => { const config: PowerPartial<EggAppConfig> = {}; config.redis = { client: { port:6379, host:'127.0.0.1',password:'', db:0, }, }; config.mysql = { client: { host:'127.0.0.1', port:'3306',user:'root',password:'admin_123',database:'test', }, app:true, agent:false, };return config; };
2)生產環境配置
使用生產環境下,使用redis鏡像和mysql
config\config.prod.ts
import { EggAppConfig, PowerPartial }from'egg'; exportdefault () => { const config: PowerPartial<EggAppConfig> = {}; config.redis = { client: { port:6379, host:'redis',password:'', db:0, }, }; config.mysql = { client: { host:'mysql', port:'3306',user:'root',password:'admin_123',database:'test', }, app:true, agent:false, };return config; };
其中redis的host: "redis" 代表redis鏡像構建的容器名
其中mysql的host: "mysql" 代表mysql鏡像構建的容器名
之后可以通過構建相同網橋的形式來使用容器名進行容器間的通信
三、Dockerfile文件編寫
1.nginx鏡像構建
Dockerfile文件如下
FROM nginxCOPYdefault.conf/etc/nginx/conf.d/default.conf ADD build/ /usr/share/nginx/build
其中default.conf即為nginx的配置文件,/etc/nginx/conf.d/為配置文件目錄
/usr/share/nginx/static為靜態文件目錄
default.conf
server {listen80;listen [::]:80;server_name localhost;#access_log /var/log/nginx/host.access.log main;location /api {proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-Proto https;proxy_set_header X-Forwarded-For $remote_addr;proxy_set_header X-Forwarded-Host $remote_addr;proxy_pass http://server:7001; }location / {root /usr/share/nginx/build;index index.html index.htm; }#error_page 404 /404.html;# redirect server error pages to the static page /50x.html#error_page500502503504 /50x.html;location = /50x.html {root /usr/share/nginx/html; }# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {# proxy_pass http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000##location ~ \.php$ {# root html;# fastcgi_pass 127.0.0.1:9000;# fastcgi_index index.php;# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;# include fastcgi_params;#}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {# deny all;#} }
修改靜態文件目錄為/usr/share/nginx/build,
增加代理/api到node容器的7001端口
同時將打包編譯的build文件拷貝到靜態目錄下
2.nodejs和eggjs鏡像構建
Dockerfile文件如下
FROM alpineADD server/ /usr/local/node-server WORKDIR /usr/local/node-serverRUN apkadd nodejs npm && npm install -g cnpm--registry=https://registry.npm.taobao.org && cnpm i && npmrun tsc CMD ["npm","start"]
由於官方的node鏡像十分之大,因此這里我們通過alpine來構建一個nodejs環境
同時我們將后台相關文件放在server文件,並拷貝至鏡像/usr/local/node-server目錄下
然后設置 npm start 為容器啟動時的命令
3.mysql鏡像構建
Dockerfile文件如下
FROM mysql:5.7COPY create_table.sql /docker-entrypoint-initdb.d
我們在啟動MySQL容器時自動創建我們需要的數據庫和表
對應的sql文件為create_table.sql,並移動到容器/docker-entrypoint-initdb.d下
create_table.sql
CREATEDATABASEIFNOTEXISTS`test`; USE `test`;CREATE TABLEIFNOTEXISTS`users` (`id` INT(11) UNSIGNED AUTO_INCREMENT,`userName` VARCHAR(255)NOTNULL,PRIMARYKEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4COLLATE = utf8mb4_unicode_ci;
mysql官方鏡像中提供了容器啟動時自動docker-entrypoint-initdb.d下的腳本的功能
4.redis鏡像構建
redis使用默認的官方鏡像即可
四、docker-compose實現容器編排
1.整理目錄結構
在使用容器編排前,需要先整理下我們的目錄結構
web ----- 前端項目文件,與docker-compose無關,僅用於生成build里的靜態文件
mysql------ Dockefile mysql鏡像的Dockefile文件
nginx ------ Dockefile nginx鏡像的Dockefile文件
------ build 前端打包后的靜態文件
server----- Dockefile node環境及后台服務鏡像的Dockefile文件
----- server eggjs文件目錄,提供后台服務
docker-compose.yml docker-compose的配置文件
2.安裝docker-compose
下載docker-compose
sudo curl -L"https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o/usr/local/bin/docker-compose
設置docker-compose執行權限
sudo chmod +x/usr/local/bin/docker-compose
創建軟鏈
sudo ln -s/usr/local/bin/docker-compose/usr/bin/docker-compose
測試是否安裝成功:
$ docker-compose--version cker-composeversion1.24.1, build4667896b
3.編寫docker-compose.yml文件
完整的docker-compose.yml文件如下
其中配置文件詳解如下
docker-compose.yml
version:"3.0" services: redis: container_name: redis image: redis ports: -"6379:6379" restart: on-failure networks: - my-server mysql: container_name: mysql build: context: mysql dockerfile: Dockerfile ports: -"3306:3306" restart: on-failure environment: - MYSQL_ROOT_PASSWORD=admin_123 networks: - my-server server: container_name: server build: context: server dockerfile: Dockerfile ports: -"7001:7001" restart: on-failure networks: - my-server depends_on: - redis - mysql nginx: container_name: nginx build: context: nginx dockerfile: Dockerfile ports: -"80:80" restart: on-failure networks: - my-server depends_on: - redis - mysql - server networks: my-server:
server中的redis為官方鏡像,直接啟動即可,同時設置自動重啟
使用專屬網橋my-server
----------
server中的mysql為自己編譯的mysql鏡像
其中MYSQL_ROOT_PASSWORD為root的用戶密碼,需設置與mysql配置文件里的一致
build里指定了Dockerfile目錄
使用專屬網橋my-server
----------
server中的server為自己編譯的nodejs服務鏡像
build里指定了Dockerfile目錄
使用專屬網橋my-server
node服務依賴於mysql和redis
同時設置自動重啟,這一步必須設置,主要是存在mysql還沒有啟動完成就啟動了node服務
會導致后台服務無法啟動從而退出,這時再重啟直到連接數據庫成功為止
----------
server中的nginx為自己編譯的前端服務鏡像
build里指定了Dockerfile目錄
使用專屬網橋my-server
同時設置自動重啟,這一步必須設置,主要是存在node還沒有啟動完成就啟動了nginx服務
會導致連接不上node的host而退出,這時再重啟直到代理到node地址成功為止
--------
最后聲明一下網橋 my-server
將所有服務都掛載在同一網橋即可通過容器名來互相通信了
至此docker-compose配置文件編寫完成,直接后台啟動即可
docker-compose up -d
啟動完成,查看容器是否都正常運行
輸入宿主機ip查看界面
點擊登錄,然后點擊獲取數據,流程暢通
這時我們可以通過宿主機ip:3306連接數據庫,在users表添加幾條數據再次點擊獲取數據,可以查詢到剛添加的記錄
五、總結
至此,整個項目的環境就搭建完成了,不過還是有許多需要優化的地方,比如通過shell腳本來控制數據庫完全啟動再啟動node服務,而不是一直重啟node直到連接上為止
之后只需要一步步完善整個流程即可實現更為復雜的項目