- Project Online Preview
- Github source repository
Many of the articles are configuration, you can directly copy the past directly, or directly download the source code to run, first run through the project.
1. Major package version and advance preparation
- package version
package | version |
---|---|
koa | ^ 2.13.4 |
sequelize | ^ 6.14.1 |
vite | ^ 2.7.13 |
vue | ^ 3.2.25 |
pinia | ^ 2.0.6 |
- Prepare in advance
- 1 online server, Baidu Cloud, Ali cloud and so on, and install a good Docker (tutorial self-search, very simple). If you don’t have one, use a local VIRTUAL machine instead and do your own research
- Register a Dockerhub account, ali cloud server will own container image service can be used, you can not register dockerhub
- Sign up for a Github account and create a repository. This article takes Github as an example, gitee’s own research
2. Deconstruction of the project catalog
The structure of the project directory is simple, the front-end application is placed in the Web directory, the database is placed in the mysql directory, and the other is the background KOA application
├ ─ ─ docker - compose. Yml # docker - compose configuration file ├ ─ ─ Dockerfile # background of koa container mirror configuration file ├ ─ ─ the log # log storage | ├ ─ ─ Access. The log - 2022-01-28. The log | ├ ─ ─ application. Log - 2022-01-28. The log | └ ─ ─ mysql. The log - 2022-01-28. The log ├ ─ ─ middleware # koa middleware | ├ ─ ─ handleGlobalError. Js | ├ ─ ─ index. The js | └ ─ ─ logClientDevices. Js ├ ─ ─ # mysql mysql configuration file | ├ ─ ─ create_db. # to create SQL database SQL | ├ ─ ─ Dockerfile # mysql container mirror configuration file | ├ ─ ─ initial_data. SQL # initialization data table SQL | ├ ─ ─ privileges. SQL # modify permissions on the mysql account password | └ ─ ─ setup. Sh # container startup execution of shell ├ ─ ─ package - lock. Json ├ ─ ─ package. The json ├ ─ ─ public # koa - static static file configuration directory | ├ ─ ─ the favicon. Ico | └ ─ ─ Index2. HTML ├ ─ ─ the README. Md ├ ─ ─ routes # @ koa/router interface routing | ├ ─ ─ todoList. Js | └ ─ ─ uuid. Js ├ ─ ─ for server js # koa boot file ├ ─ ─ Util | ├ ─ ─ the js # mysql connection configuration | └ ─ ─ logger. The logging configuration of js # koa - log4 └ ─ ─ # vue web front-end application ├ ─ ─ Dockerfile ├ ─ ─ index. The HTML ├ ─ ─ Nginx. Conf ├ ─ ─ package. Json ├ ─ ─ public ├ ─ ─ the README. Md ├ ─ ─ the SRC | ├ ─ ─ App. Vue | ├ ─ ─ assets | ├ ─ ─ components | ├ ─ ─ main. Js | | ├ ─ ─ services # interface └ ─ ─ store # Pinia state management ├ ─ ─ vite. Config. Js └ ─ ─ yarn. The lockCopy the code
3. Front end
Of course, you should write todolist application with Pinia state Management + vue3
3.1 Pinia status management for quick start, masturbating to a document
// src/store/index.js
import { defineStore } from "pinia";
import * as types from "./types";
export const useTodosStore = defineStore("todos", {
state: () = > ({
/ * *@type {{ msg: string, id: string, is_finished: boolean, create_time: date }[]} * /
todos: [].filter: types.ALL,
nextId: 0,}).getters: {
finishedTodos(state) {
return state.todos.filter((todo) = > todo.is_finished);
},
unfinishedTodos(state) {
return state.todos.filter((todo) = >! todo.is_finished); },filterTodos(state) {
if (this.filter === types.FINISHED) {
return this.finishedTodos;
} else if (this.filter === types.UNFINISHED) {
return this.unfinishedTodos;
}
return this.todos; }},actions: {
addTodos({ id, msg, create_time }) {
this.todos.unshift({ id, msg, create_time, is_finished: false });
},
finishedOneTodo(obj) {
const index = this.todos.findIndex((item) = > item.id === obj.id);
this.todos.splice(index, 1, {
...obj,
is_finished: true}); },deleteOne(id) {
this.todos = this.todos.filter((item) = >item.id ! == id); },setInitialData(arr) {
this.todos = [...arr]; ,}}});Copy the code
Error while updating Dependencies vite+element-plus
Error part:
Morning 11:02:22 [vite] new dependencies found: element - plus/es, element - plus/es/components/option/style/CSS, updating... 11:02:22 am [vite] Failed to load source map for /node_modules/. Vite/chunk-tpoprdhf.js? v=e12284c2. > error: Failed to write to output file: open D:\my\resource\Vue.js\vue3+Pinia\node_modules\.vite\element-plus.js: Access is denied. 11:02:37 [vite] error while updating dependencies: error: Build failed with 1 error: error: Failed to write to output file: open D:\my\resource\Vue.js\vue3+Pinia\node_modules\.vite\element-plus.js: Access is denied. at failureErrorWithLog (D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:1493:15) at D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:1151:28 at runOnEndCallbacks (D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:941:63) at buildResponseToResult (D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:1149:7) at D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:1258:14 at D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:629:9 at handleIncomingPacket (D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:726:9) at Socket.readFromStdout (D:\my\resource\Vue.js\vue3+Pinia\node_modules\esbuild\lib\main.js:596:7) at Socket.emit (events.js:400:28) at addChunk (internal/streams/readable.js:290:12)Copy the code
(deleting node_modules and reinstalling, upgrading to the latest version, and checking whether the folder path is Chinese…) Later, I found a solution:
Upgrade Node.js to the latest version (the latest version is 16.13.2), delete the node_modules folder, install dependencies again, and start normally
If you are using NVM, nodeJS can be upgraded quickly like this
NVM install 16.13.2 NVM use 16.13.2Copy the code
3.3 nginx. Conf
server { listen 80; Charset UTF-8; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; Docker-compose = docker-compose = docker-compose = docker-compose = docker-compose = docker-compose = docker-compose = docker-compose = docker-compose # rewrite ^/api/(.*)$ /$1 break; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 100m; } location / { root /usr/share/nginx/dist; Index.html index.htm; Try_files $uri /index.html; # prevent browser refresh after page 404 client_max_body_size 100m; } location =/admin { deny all; } location ~\. Java ${deny all; } error_page 500 502 503 504/50x.html; Error_page 404/404.html; #404 page location = /50x. HTML {root /usr/share/nginx/html; } gzip on; # enable gzip gzip_buffers 32 4k; Gzip_comp_level 6 set the size of the buffer required for compression, in 4k, if the file size is 32K, apply 32* 4K buffer. # gzip_min_length 4000; # gzip_min_length 4000; # gzip_min_length 4000; #gizp compression starting point, files larger than 4K to compress; Vary: accept-encoding (gzip_static on); #nginx will search for files that end with.gz and return them directly. Gzip_types text/ XML text/javascript application/javascript text/ CSS text/plain Application /json application/x-javascript; # Type of file to compress}Copy the code
3.4 Dockerfile
Use multi-stage builds to reduce container size
FROM alpine:3.15 AS base
ENV NODE_ENV=production \
APP_PATH=/app
WORKDIR $APP_PATH
Install nodejs and YARN using the apk command
RUNApk add --no-cache --update nodejs=16.13.2-r0 YARN
FROM base AS install
COPY package.json yarn.lock $APP_PATH/
RUN yarn install
FROM base AS build
Copy the node_modules folder to the final working directory
COPY --from=install $APP_PATH/node_modules $APP_PATH/node_modules
# copy files from current directory to working directory (except ignored in.dockerignore)
COPY . $APP_PATH/
RUN yarn run build
FROM nginx:alpine
WORKDIR /usr/share/nginx/dist
# Add your own configuration default.conf below
ADD nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist .
EXPOSE 80
Copy the code
4. Backstage
4.1 Import File
// server.js
const path = require("path");
const Koa = require("koa");
const Router = require("@koa/router");
const ROOT = path.resolve(process.cwd(), ". /");
const { connectMySQL } = require(path.resolve(ROOT, "./util/db"));
const todoList = require(path.resolve(ROOT, "./routes/todoList"));
const app = new Koa();
const router = new Router();
// Load all child routes
router.use("/api", todoList.routes(), todoList.allowedMethods());
// Load the routing middleware
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000.async() = > {await connectMySQL();
});
Copy the code
4.2 Sequelize Connects to the mysql database
// util/db.js
const path = require("path");
const { Sequelize } = require("sequelize");
const db = new Sequelize("todolist"."root"."123456", {
dialect: "mysql".dialectOptions: {
charset: "utf8mb4".collate: "utf8mb4_unicode_ci".supportBigNumbers: true.bigNumberStrings: true,},Docker-compose = docker-compose = docker-compose = docker-compose = docker-compose = docker-compose
host:
process.env.NODE_ENV === "development"
? "localhost"
: "todolist_mysql_server".timezone: "+ 08:00." "./ / east eight area
port: "3306"});const connectMySQL = async() = > {try {
await db.authenticate();
console.log(Mysql connection successful);
} catch (e) {
console.log(e);
console.log("Connection failed. Try again in 3 seconds.");
setTimeout(connectMySQL, 3000); }};exports.connectMySQL = connectMySQL;
exports.db = db;
Copy the code
4.3 Interface Examples
// routes/todolist.js
const Router = require("@koa/router");
const dayjs = require("dayjs");
const utc = require("dayjs/plugin/utc"); // dependent on utc plugin
const timezone = require("dayjs/plugin/timezone");
const { QueryTypes } = require("sequelize");
const ROOT = path.resolve(process.cwd(), ". /");
const { db } = require(path.resolve(ROOT, "./util/db"));
const todoList = new Router();
// https://dayjs.gitee.io/docs/zh-CN/plugin/timezone
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz.setDefault("Asia/Shanghai");
// List query
todoList.get("/todoList/list".async (ctx, next) => {
const reqParams = ctx.query;
// https://github.com/demopark/sequelize-docs-Zh-CN/blob/master/core-concepts/getting-started.md#promises-%E5%92%8C-asyncaw ait
const selects = {
0: "SELECT * FROM todolist WHERE is_finished='0' ORDER BY create_time DESC;".1: "SELECT * FROM todolist WHERE is_finished='1' ORDER BY create_time DESC;".2: "SELECT * FROM todolist ORDER BY create_time DESC;"};const filterType = reqParams.filterType || "2";
try {
let list = await db.query(selects[filterType], {
type: QueryTypes.SELECT,
});
list = list.map((item) = > ({
...item,
create_time: dayjs(item.create_time)
.tz("Asia/Shanghai")
.format("YYYY-MM-DD HH:mm:ss"),
is_finished: item.is_finished === "0" ? false : true,})); ctx.body = {code: 200.data: list || [],
msg: "ok"}; }catch (e) {
console.log(e);
}
await next();
});
module.exports = todoList;
Copy the code
4.4 Dockerfile
FROM alpine:3.15 AS base
ENV NODE_ENV=production \
APP_PATH=/www/node-server
WORKDIR $APP_PATH
Install nodejs using the apk command
RUNApk add --no-cache --update nodejs=16.13.2-r0 NPM
Install project dependencies based on the underlying image
FROM base AS install
# copy package.json from the current directory to the working directory
COPY package.json package-lock.json $APP_PATH/
RUN npm install
# Final build based on base image
FROM base
Copy the node_modules folder to the final working directory
# COPY When you COPY a folder, you do not COPY the folder directly, but COPY the contents of the folder to the destination path
COPY --from=install $APP_PATH/node_modules $APP_PATH/node_modules
# copy files from current directory to working directory (except ignored in.dockerignore)
COPY . $APP_PATH/
EXPOSE 4000
CMD ["npm"."run"."server"]
Copy the code
4.5 Precautions
- Configure the time zone for database connection
timezone
, set it to east 8 - Set the database character set to
utf8mb4
For storing emoticons dayjs
Library adjustment shows the time for east 8 zonesequelize
Duplicate entries (non-duplicate data in the database) are found and need to be configuredtype
forQueryTypes.SELECT
5. MySQL part
The selected version is 5.7.30. Online version 8.0 is not deployed successfully
5.1 the mysql/Dockerfile
FROM mysql:5.7.30
LABEL version="1.0.0" description=Todolist MySQL Server
WORKDIR /mysql
ENV MYSQL_ROOT_PASSWORD=123456
# MYSQL_DATABASE=todolist
# MYSQL_ALLOW_EMPTY_PASSWORD=yes
# startup script
COPY setup.sh /mysql/setup.sh
Create database
COPY create_db.sql /mysql/create_db.sql
Initialize data
COPY initial_data.sql /mysql/initial_data.sql
Set password and permission
COPY privileges.sql /mysql/privileges.sql
EXPOSE 3306
CMD ["sh"."/mysql/setup.sh"]
Copy the code
5.2 mysql/setup.sh
Container startup script
#! /bin/bash
set -e
# https://xie.infoq.cn/article/a3c8ffbd34d818de010f2b0f6
Print the status of the mysql service
echo $(service mysql status)
echo '1. Start the mysql... '
# start mysql
# service mysql stop
# service mysql restart
service mysql start
# sleep 3
echo '2. Create database... '
mysql </mysql/create_db.sql
sleep 3
echo '3. Start importing data... '
mysql </mysql/initial_data.sql
sleep 3
echo $(service mysql status)
echo Mysql > alter database; '
mysql </mysql/privileges.sql
sleep 3
echo '4. Permission modification is complete... '
Prevent the Container from exiting after it is started
# http://www.mayanpeng.cn/archives/121.html
tail -f /dev/null
Copy the code
5.3 mysql/create_db.sql
Creating a database
CREATE DATABASE IF NOT EXISTS todolist;
Copy the code
5.4 mysql/initial_data.sql
Initialize the table
-- Use the Todolist library
USE todolist;
Create todolist table
CREATE TABLE IF NOT EXISTS todolist(id VARCHAR(50) PRIMARY KEY, create_time DATETIME UNIQUE, is_finished ENUM('0'.'1') DEFAULT '0', msg VARCHAR(100) DEFAULT The '-') COMMENT='todolist table' ENGINE=InnoDB DEFAULT CHARSET=utf8;
Print database
SHOW TABLES;
Print the table structure
DESC todolist;
Insert 1 default data
INSERT INTO todolist(id, create_time, is_finished, msg) VALUES(1, NOW(), '0'.'hello world');
Copy the code
5.5 mysql/privileges.sql
Setting permission Password
use mysql;
SELECT host, user FROM user;
Grant database permission to user root with password 123456
GRANT ALL PRIVILEGES ON *.* TO 'root'@The '%' IDENTIFIED BY '123456';
-- Refresh permission must have:
flush privileges;
Copy the code
6 Preparing for Deployment
6.1 Prepare in advance
- in
dockerhub
Account orAli Cloud image container service
Create 3 repositories for mysql, KOA and Vue applicationstodolist_mysql
,koa
andvite_vue3_pinia
, in the docker-comemage. yml configuration file below
- In your
github
Configure the dockerhub account and password above in the warehousesecrets
To facilitate the use of github repository actions push later. So what I’m creating here isDOCKERHUB_TOKEN
Put a password,DOCKERHUB_USERNAME
Put the account
6.2 docker-compose.yml
version: "3"
services:
mysql: # mysql
build: ./mysql
The # image name is the name created in the DockerHub
image: Your Docker account name /todolist_mysql:latest
container_name: todolist_mysql_server
restart: always
# MYSQL_ROOT_PASSWORD: "123456"
node: # nodejs service
depends_on:
- "mysql"
build: . # build the directory where Dockerfile resides
image: Your Docker account name/KOA: Latest
container_name: koa_server
# ports:
# - "8001-4000"
restart: always Automatic restart
environment:
- NODE_ENV=production
command: npm run server Overrides the commands executed by default when the container is started
vue:
depends_on: # vue is guaranteed to start after node
- "node"
build: ./web
image: Your Docker account name /vite_vue3_pinia:latest
container_name: vite_vue3_pinia
restart: always
environment:
- NODE_ENV=production
ports:
- "8001:80"
networks:
default:
external:
name: vue3-koa
Copy the code
6.3 Github CI Configuration File
# .github/workflows/ci.yml
name: MySQL + Koa2 Server + Vue3 todolist
on:
push:
branches: [main] # listen on the main branch
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
Make a Docker image and push it to dockerHub
- name: build and push docker image
run: | # used here created above the secrets of variable # login you docker account docker login - u ${{secrets. DOCKERHUB_USERNAME}} - p ${{secrets. DOCKERHUB_TOKEN Docker-compose compose: docker-compose compose: docker-compose compose: docker-compose compose: docker-compose compose: docker-compose compose: docker-compose compose: docker-compose composeCopy the code
6.4 Submitting Code
Can’t wait, it’s time to commit code
git add .
git commit -m "feat: init"
git push -u origin main
Copy the code
Not surprisingly, the build was successful in the Actions of the repository.
7. Portainer
appearance
After all this trouble, Todolist hasn’t seen it yet. It’s my fault. Last step. Don’t give up.
7.1 Benefits of Portainer
This is just like git, using git GUI tools and git bash command lines to facilitate container-managed operations. If you prefer the command line, skip this section and deploy containers directly.
Docker compose up is composed for your git repository.
7.2 Logging In to your server installationportainerI have installed version 1.24.2 here
If you have Docker installed on your server
- download
docker pull portainer/portainer:latest
Copy the code
- run
Note: here your server’s security group is open to port 9000
docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock --restart=always --name prtainer portainer/portainer
Copy the code
7.3 the portainer speaking
In the browser, open http://public IP address of your server :9000, set your account and password, and configure the local node
7.3.1 Pull the image you just pushed to the DockerHub
I’m going to pull all three mirror images
If it is pushed to ali Cloud image container, click Registry on the left menu, and then click Add Registry to Add your image container URL. At the same time, you can configure Authentication, your account and password, and then you can pull the image as above
7.3.2 Configuring container NetWork for container communication
I can’t take it. How many more steps? You lied to me. It really will be over soon
The essence is to use the Docker network, and then add their own containers to the network
Docker network Create User-defined bridge name Docker Network Connect User-defined bridge name Container nameCopy the code
So here we’re using a graphical interface
7.3.3 Deploying containers
I can’t take it anymore. It’s not over. It’s over this time
Click Container to add a container. You must deploy MySQL first, Koa then vUE application in the same order as docker-compose
Deploy the three containers in sequence, and the container names and ports must be the same as those in the configuration file
7.3.4 Amida Buddha
XDM, it’s really over. I tortured you. If all goes well, all three containers are deployed successfully
Open your browser and visit your todolist, http://your IP :8001
7.3.5 Failed to Save Emoji
The MSG column did not use the UTF8MB4 character set when the table was created. Then why don’t you set it up when you build the meter? I’m sorry, XDM. Punch me
If you feel complicated and uncomfortable, skip it
Docker goes into container combat
If you log in to your server
- View running containers
docker container ls
Copy the code
- Into the container
docker exec-it Indicates the CONTAINER ID of the mysql CONTAINER shCopy the code
- Connect the mysql
mysql -h localhost -u root -p
#Then enter your password
Copy the code
Unsurprisingly, we managed to enter the container and connect to mysql
[root@iZbp19ftqv2b85av0b2d4qZ /]# docker exec -it a206f021c205 sh # mysql -h localhost -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 53 Server version: 5.7.30 MySQL Community Server (GPL) Copyright (C) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help; ' or '\h' for help. Type '\c' to clear the current input statement. mysql>Copy the code
- Modify the MSG column attribute of the todolist table
- Use todolist library
USE todolist;
Copy the code
- modified
mysql> ALTER TABLE todolist MODIFY msg VARCHAR(100) character set utf8mb4 collate utf8mb4_unicode_ci default The '-';
Query OK, 6 rows affected (0.04 sec)
Records: 6 Duplicates: 0 Warnings: 0
Copy the code
- Verify that the modification is successful
As you can see, the MSG column has been modified successfully
mysql> SHOW CREATE TABLE todolist\G
*************************** 1. row ***************************
Table: todolist
Create Table: CREATE TABLE `todolist` (
`id` varchar(50) NOT NULL,
`create_time` datetime DEFAULT NULL,
`is_finished` enum('0'.'1') DEFAULT '0',
`msg` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT The '-'.PRIMARY KEY (`id`),
UNIQUE KEY `create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='todolist table'
1 row in set (0.00 sec)
Copy the code
- Enter emojis in the browser to add a test, no surprise, it is ok. It’s over!!
8. Reference materials
-
Docker container deployment practice using Portainer
-
MySql Dockerfile preparation
-
Pinia document
-
Sequelize Chinese document
-
dayjs
-
【 field 】 How to write logs in Node service?