学习了一段时间的django和vue,对于前后端开发有了一个初步的了解,这里记录一下编写的流程和思路,主要是为了后面如果遗忘从哪里开始操作做一个起步引导作用
一、Django后端
参考下前面django的文档https://moziang.blog.csdn.net/article/details/130720709
1、安装django环境
//配置清华镜像源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip config set global.trusted-host pypi.tuna.tsinghua.edu.cn
//安装django
pip install django==4.2.1
//创建项目paas
django-admin startproject PAAS
//登录PAAS目录
cd PAAS
//创建应用
python manage.py startapp app_demo1
python manage.py startapp app_demo2
目录结构
2、项目添加应用模块
vi test1\PAAS\PAAS\settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app_demo1', #添加
#'app_demo2'
]
ALLOWED_HOSTS = ['*'] #允许所有请求访问
在理想中我们需要维护一个大的平台项目,也就是paas平台,而app_demo1 和app_demo2 就是我们这个平台下负责某个功能的模块,这种模块可能有多个,为了区分他们不同模块对外提供的功能,我们首先要做的就是设置路由(urls),app_demo2我这里演示不用,先注释了
3、添加主路由转发到模块路由
PAAS/PAAS/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
#添加请求路由,当访问app1或者app2时会将请求交给两个模块下的urls路由
path("app1/",include("app_demo1.urls")),
#path("app2/",include("app_demo2.urls"))
]
4、添加模块路由配置
上面是将主路由指定到了模块下的子路由,我们模块下默认是没有路由文件的需要手动创建,这里我们只演示一个模块下的路由案例,另一个可以先忽略了,操作都一样
PAAS/app_demo1/urls.py
from django.urls import path
urlpatterns = [
#添加请求路由,当访问app1或者app2时会将请求交给两个模块下的urls路由
path("info/",函数方法,name="info"),
]
# 假设后端传递了路由名称为 "info" 到前端
#var url = "{% url 'info' %}";
#在模板中使用Django模板标签获取路由URL
#然后在Vue.js中使用这个url
#例如:
#this.$router.push(url); // 使用Vue Router进行页面跳转
在上面中的info 是请求接口的路径,当我们请求该接口的时候会去调用该函数,而name=info则是定义了info/这个路径的别名,当需要进行访问跳转时可以根据这个别名去快速访问,不需要考虑路径的问题,比如<a href="{% url 'info' %}">点击这里查看信息</a>
5、添加接口下函数功能
正常来说,我们django是只做后端的逻辑处理,将处理好的数据通过json格式的方法返回给请求方(前端) 然后根据前端的代码做逻辑判断后显示在页面上,现在我们想要对django的/app1/info/这个路径的请求,返回一个json数据。 而这些函数我们定义一个api的目录单独存放
#创建api目录
mkdir api/
PAAS/app_demo1/api/info.py
from django.http import JsonResponse
#这里的函数名称开头必须小写
def paasInfo(request):
# 定义需要返回的数据
data = {
"status": "success",
"message": "Data retrieved successfully",
"data": {
"user": {
"id": 123,
"username": "张三",
"email": "123@qq.com"
}
}
}
#返回json类型数据
return JsonResponse(ata)
6、补全模块路由配置
from django.urls import path
#通过.做相当路径导入,适用于包内导入
from .api import info
urlpatterns = [
#添加请求路由,当访问app1或者app2时会将请求交给两个模块下的urls路由
path("info/",info.paasInfo,name="info"),
]
7、测试访问
现在我们的后端基础的格式就完成了,下面我们测试下访问是否能返回json数据
http://127.0.0.1:8000/app1/info
这里可以看到我们已经可以通过访问后端的api接口获取一些数据了,具体的其他逻辑放到下面说,我们下面开始整前端程序
二、 Vue 前端
0、切换目录
#这里的目录是我们上面django的项目根目录
#我们将vue的项目目录和django的项目目录平级,方便迁移
cd C:\Users\Administrator\IdeaProjects\test1
1、安装环境
这块都是页面操作不重复编写,参考我下面的文档装下环境(完成步骤一即可)
https://moziang.blog.csdn.net/article/details/134414702
2、目录结构
3、测试访问
#ie浏览器会显示不出来,可以换edge
http://localhost:8080/
4、添加vue工作目录
我现在想要创建多个页面,每个页面都有独立的功能,首先我们需要先区分开文件存放的位置,没有的目录手动创建下
src/components #存放 Vue 组件文件的目录。
src/assets #存放静态资源文件的目录,如图片、字体等。
src/views #存放路由组件文件的目录,通常用于组织不同路由对应的页面组件。
src/api #存放与后端 API 交互的文件
src/store #存放 Vuex 相关的文件,用于状态管理。
src/router #存放 Vue Router 相关的文件,用于配置路由。
5、安装路由插件
npm install -g vue-router
6、定义视图函数(view)
my-vue-app/src/views/index.vue
<template>
<div>
我是主页
</div>
</template>
<script>
export default {
name: "IndexView",
}
</script>
<style>
/* 这里可以添加一些页面样式 */
</style>
7、添加路由配置
my-vue-app/src/router/router.js
import { createRouter,createWebHistory } from 'vue-router' //引入路由插件函数
import IndexView from '@/views/index.vue' //引入被路由到的页面相关文件
//定义需要路由的组,可以定义多个 (基本都在改这个
const routes = [
{
path: '/',
name: 'IndexView', //这里是字符串哈
component: IndexView
},
]
// 创建路由
const router = createRouter({
history: createWebHistory(),
routes
});
export default router
8、注册路由
my-vue-app/src/main.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
import router from './router/router'
app.use(router); //注册路由
app.mount('#app')
9、清理app.vue 视图
// app.vue
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App",
}
</script>
<style>
/* 这里可以添加一些全局样式 */
</style>
10、切换目录启动项目
#切换到package.json所在的目录
cd C:\Users\Administrator\IdeaProjects\test1\my-vue-app>
#启动服务
npm run serve
11、访问页面 (IE浏览器打不开)
http://localhost:8081/
说明:
上面从6-11步都是为了后续开发梳理一个大致的流程
步骤6中定义了单个页面的显示信息,
步骤7定义了我们具体通过访问那个路由来到达视图的位置
举个例子比如说我们现在想要加俩页面,访问/paas 和/log 两个路由到各自的页面
案例
和步骤6一样,先添加视图文件,直接复制改下页面
my-vue-app/src/views/paas.vue
<template>
<div>
paas
</div>
</template>
<script>
export default {
name: "PaasView",
}
</script>
<style>
/* 这里可以添加一些页面样式 */
</style>
my-vue-app/src/views/log.vue
<template>
<div>
log
</div>
</template>
<script>
export default {
name: "LogView",
}
</script>
<style>
/* 这里可以添加一些页面样式 */
</style>
添加路由,和步骤7类似
my-vue-app/src/router/router.js
import { createRouter,createWebHistory } from 'vue-router' //引入路由插件函数
import IndexView from '@/views/index.vue' //引入被路由到的页面相关文件
import PaasView from '@/views/paas.vue'
import LogView from "@/views/log.vue";
const routes = [
{
path: '/',
name: 'IndexView',
component: IndexView
},
// 新增路由
{
path: '/paas',
name: 'PaasView',
component: PaasView
},
{
path: '/log',
name: 'LogView',
component: LogView
},
]
// 创建路由
const router = createRouter({
history: createWebHistory(),
routes
});
export default router
访问测试
http://localhost:8081/paas
http://localhost:8081/log
后续编写依此类推
12、定义前台主页文件
没怎么接触过前端,之前我想自己整个前端页面方便使用一些小工具来提升效率,但碰到这块的时候我确不清楚应该怎么去调用各个层级的路由(vue还没学到路由),所以我的方法是利用app.vue文件做一个主页面的显示,通过点击按钮跳转访问vue自身的不同路由
// app.vue
<template>
<div>
<router-view></router-view>
<!-- 添加html跳转路由-->
<router-link to="/">主页</router-link>
<router-link to="/paas">paas</router-link>
<router-link to="/log">log</router-link>
</div>
</template>
<script>
export default {
name: "App",
}
</script>
<style>
/* 这里可以添加一些全局样式 */
</style>
通过上面的方法,我们可以从主页去手动访问各个视图的路由页面实现跳转,关于样式不着急用先放着,下面我们试着通过调用后端的api来请求和接收数据
13、vue调用后端接口
定义接口函数,在vue中通过按钮或者其他方法触发
my-vue-app/src/api/paasinfo.js
export function paasClusterInfo(method, params) {
if (method === 'GET') {
// 如果是GET请求,将参数拼接到URL上
//url += '?' + new URLSearchParams(params).toString();
return fetch('/app1/info/', {
method: 'GET', //可以是post请求 根据你后端逻辑判断即可
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.json()
});
} else if (method === 'POST') {
// 如果是POST请求,将参数放在请求体中
return fetch('/app1/info/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.json()
});
} else {
throw new Error('Unsupported method')
}
}
重新编辑paas视图,调用api函数将请求的数据打印到F12 开发工具页面
my-vue-app/src/views/paas.vue
<template>
<div>
paas
<Button @click="getChange">按钮</Button>
</div>
</template>
<script>
// 导入api
import { paasClusterInfo } from '@/api/paasinfo'
export default {
name: "PaasView",
methods: {
async getChange() {
console.log("输出个信息,证明函数执行了,再有报错就是下面请求的问题")
try {
const data = await paasClusterInfo('GET','action=list') // 调用paasClusterInfo需要加上括号()
console.log(data)
} catch (error) {
console.error("An error occurred:", error)
}
}
}
}
</script>
<style>
/* 这里可以添加一些页面样式 */
</style>
vue在请求后端的时候会存在一些问题,比如跨域请求问题,需要单独进行配置
当做如下配置后,vue中发起的url请求,比如/app1 开头的请求会直接转发到下面的url中
my-vue-app/vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
devServer: {
proxy: {
//环境内请求/app1的请求都转给下面的地址,需要重启
'/app1': {
target: 'http://localhost:8000',
changeOrigin: true
}
}
}
})
修改完上面的文件,必须要重启 ,不重启无法生效
14、关于获取的数据显示
我们接口返回的数据格式如下,我们需要在前端页面上显示出来我们需要的值
{
"status": "success",
"message": "Data retrieved successfully",
"data": {
"user": {
"id": 123,
"username": "张三",
"email": "123@qq.com"
}
}
}
console.log(data.status)
console.log(data.message)
console.log(data.data)这个请求方法就是正常的json值的获取了
关于数据显示就看前面vue的一些使用方法了,比如v-mode 映射数据上去 ,字典加循环之类的
三、vue 构建docker镜像
我们在打包的时候会分为两个部分
1、 nginx + vue
2、python + Django(Gunicorn)
这两个镜像打包的时候也存在一些区别,nginx+vue 为了性能考虑是以nginx作为主进程运行而不是vue作为主进程,而python + django则是以django为主进程运行
1、vue项目上传linux服务器
我们在实际部署时,需要通过命令npm run build 将vue项目中的public 和src目录下的静态文件、vue文件、图片、字体、css、js等信息经过转换压缩后存放到dist目录下,然后将dist目录直接仍到nginx主页下进行访问,打包文件如下
但是如果我们每次都需要手动操作进行打包,并不利于我们后续做自动化CI/CD的使用,这里我们直接考虑使用一个分段构建的形式,登录linux创建构建目录
我们先将项目拷贝一份出来,将项目中的dist和mode_modules目录删除,这俩一个是打包目录,一个是项目依赖目录,依赖模块我们打包镜像的时候重新下载即可
#创建构建目录
mkdir /apps/web -p
cd /apps/web #直接将vue项目目录扔进去
2、编写dockerfile
#使用官方node镜像作为基础镜像
FROM node:14 as build-stage
#拷贝项目目录到镜像
COPY ./my-vue-app1 /home/my-vue-app1
#设置工作目录
WORKDIR /home/my-vue-app1
#添加加速源并安装依赖
RUN npm config set registry http://registry.npm.taobao.org/
#这块的3个RUN可以试着合并为一个RUN执行,我这边是跑的时候死活不能用
#\ + && 一直报错就这样用了,另一台主机可以。 就很烦
RUN npm install
RUN npm run build
# 使用 Nginx 镜像作为最终镜像
FROM nginx:1.21
# 将构建阶段生成的 dist 目录复制到 Nginx 的默认静态文件目录
COPY --from=build-stage /home/my-vue-app1/dist /usr/share/nginx/html
# 暴露 80 端口
EXPOSE 80
# 容器启动时运行 Nginx
CMD ["nginx", "-g", "daemon off;"]
3、构建镜像
docker build . -f Dockerfile -t web-vue:v1
4、部署镜像
docker run -itd --name test1 -p 30030:80 docker.io/library/web-vue:v1
5、访问测试
http://linux服务器ip:30030/
看起来访问是正常的,但是当我们点击按钮访问后端django时还是有问题的
我们之前配置跨域访问的时候都是在同一台主机上所以直接用的localhost, 之前我们为什么要用则个代理,就是因为有个什么跨域访问的原因,但是我们现在已经将vue部署到了nginx上面,我们就可以把这块的配置去掉,让vue发起请求的时候访问自身,因为他是部署在nginx上的,他实际会访问到nginx,我们只需要在nginx上做反向代理,将vue访问自身的api相关的请求交给对应的服务器上即可,而后续我们修改请求地址就和vue无关,只需要根据请求接口变更代理地址即可
6、去除vue代理配置
vi /apps/web/my-vue-app1/vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
});
7、添加nginx反向代理配置
vi /apps/web/default.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 反向代理设置
location /app1/ {
proxy_pass $VUE_APP_API_BASE_URL;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
8、重新构建镜像
vi Dockerfile
#使用官方node镜像作为基础镜像
FROM node:14 as build-stage
#拷贝项目目录到镜像
COPY ./my-vue-app1 /home/my-vue-app1
#设置工作目录
WORKDIR /home/my-vue-app1
#添加加速源并安装依赖
RUN npm config set registry http://registry.npm.taobao.org/
#这块的3个RUN可以试着合并为一个RUN执行,我这边是跑的时候死活不能用
#\ + && 一直报错就这样用了,另一台主机可以。 就很烦
RUN npm install
RUN npm run build
# 使用 Nginx 镜像作为最终镜像
FROM nginx:1.21
# 将构建阶段生成的 dist 目录复制到 Nginx 的默认静态文件目录
COPY --from=build-stage /home/my-vue-app1/dist /usr/share/nginx/html
# 暴露 80 端口
EXPOSE 80
# 拷贝配置文件
COPY ./default.conf /etc/nginx/conf.d/
# 容器启动时运行 Nginx
CMD ["nginx", "-g", "daemon off;"]
重新构建
docker build . -f Dockerfile -t web-vue:v1
部署
docker rm -f test1
docker run -itd --name test1 -p 30030:80 docker.io/library/web-vue:v1
9、访问nginx 调用后端接口
图里的报错说明我们请求已经能正常发送了,没有出现跨域报错,没有404,报错信息是返回的数据不是一个json格式,说明我们目前位置请求是正确的,关于那个nginx的配置文件的变量可以通过挂载的形式来调整,就不在动镜像了 ,开始做django后端镜像
(上面那个地址案例不太好,后面找个好看的案例贴上去 溜了溜了)
四、django镜像构建
django 部分参考之前文章 https://moziang.blog.csdn.net/article/details/134195331
架构子系统协同分析
1、nginx
#Nginx 运行起来是多个进程, 接收从客户端(通常是浏览器或者手机APP)发过来的请求
#它会根据请求的URL进行判断, 如果请求的是静态资源, 比如HTML文档、图片等
#它直接从配置的路径进行读取, 返回内容给客户端
#如果请求的是动态数据, 则转发给Gunicorn+Django进行处理
2、Gunicorn/Django
#Gunicorn和Django是运行在同一个 Python进程里面的, 它们都是用Python代码写的程序。
#启动Gunicorn的时候,它会根据配置加载Django的入口模块,这个入口模块里面提供了WSGI接口。
#当Gunicorn接收到Nginx转发的HTTP请求后, 就会调用Django的WSGI入口函数,将请求给Django进行处理
#Django框架再根据请求的URL和我们项目配置的URL路由表,找到我们编写的对应的消息处理函数进行处理。
#我们编写的消息处理函数,就是前面章节大家学习到的,处理前端请求。如果需要读写数据库,就从MySQL数据库读写数据。
1、添加项目权限及导入Gunicorn 功能
PAAS/PAAS/settings.py
#添加导入应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app_demo1',
'gunicorn', #新增
#'app_demo2'
]
DEBUG = False #改成False
2、拷贝项目目录
3、删除拷贝项目中数据库配置
PAAS1/app_demo1/migrations 将目录下除init以外的都删掉,这个是django生成的数据库相关文件,如果不删除带到其他环境可能会因为同步数据库结构发生意料之外的问题
4、修改数据库变量
我们在不同环境中使用django的都需要配置对应环境的数据库,默认配置如下
PAAS/PAAS/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
我们用django去链接mysql数据库,正常情况配置如下
PAAS/PAAS/settings.py
DATABASES = {
'default':
{
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'paas', # 数据库名称
'HOST': '101.43.156.78', # 数据库地址
'PORT': 30013, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123456', # 数据库密码
}
}
为了方便我们构建镜像后在不同环境获取数据库的值,做一下改造 通过获取变量来配置数据库,同时给一个默认值
PAAS/PAAS/settings.py
import os
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ.get('DB_NAME', 'paas'),
'HOST': os.environ.get('DB_HOST', '101.43.156.78'),
'PORT': int(os.environ.get('DB_PORT', 30013)),
'USER': os.environ.get('DB_USER', 'root'),
'PASSWORD': os.environ.get('DB_PASSWORD', '123456'),
}
}
为了使用数据库,我们还需要另外装一些的东西
pip3 install pymysql
pip3 install cryptography
PAAS1/PAAS/__init__.py
import pymysql
pymysql.install_as_MySQLdb()
5、添加Gunicorn启动配置文件
PAAS1/gunicorn_conf.py
# gunicorn/django 服务监听地址、端口
bind = '0.0.0.0:8000'
# gunicorn worker 进程个数,建议为: CPU核心个数 * 2 + 1
workers = 3
# gunicorn worker 类型, 使用异步的event类型IO效率比较高
worker_class = "gevent"
# 日志文件路径
#errorlog = "/home/gunicorn.log"
errorlog = "-"
loglevel = "info"
import sys,os
cwd = os.getcwd()
sys.path.append(cwd)
6、添加镜像启动脚本
vi PAAS1/main.sh
#!/bin/bash
DIR="$( cd "$( dirname "$0" )" && pwd )"
echo $DIR
cd $DIR
# ulimit -n 50000
#这里就是指定一下我们定义的配置文件
#PAAS.wsgi 就是我们这个项目的项目名称+ .wsgi就行
gunicorn --config=$DIR/gunicorn_conf.py PAAS.wsgi
#后台运行用下面这个
#nohup gunicorn --config=$DIR/gunicorn_conf.py Django_demo.wsgi &> /dev/null &
7、导出项目所需依赖
#切换paas项目目录
cd C:\Users\Administrator\IdeaProjects\test1\PAAS1
#导出依赖
pip freeze > requirements.txt
8、添加额外库
vi requirements.txt
#添加
Gunicorn
gevent
greenlet
os #给那个data获取变量用的
说明
Gunicorn 是一个 Python WSGI HTTP 服务器,用于运行 Python Web 应用程序。它可以处理并发请求,提供了稳定、快速的性能,适合用于部署生产环境的 Web 应用。
gevent 是一个基于 libev 的并发库,它提供了协程和事件循环的机制,可以用于高性能的网络应用程序。gevent 可以在 Python 中实现异步 I/O,通过协程实现并发处理,从而提高网络应用程序的性能和吞吐量。
greenlet 是 gevent 的底层实现,它提供了轻量级的协程机制,允许在 Python 程序中实现协作式多任务处理。gevent 是基于 greenlet 实现的,它提供了更高级的接口和功能,使得在 Python 中实现高性能的并发处理变得更加容易
9、创建app项目目录
mkdir /apps/app
#将PAAS1 项目仍进去
10、编写dockerfile
FROM python:3
#这里导入的时候换下目录名称,因为项目本身还是PAAS
COPY ./PAAS1 /home/PAAS/
WORKDIR /home/PAAS/
RUN python3 -m pip config set global.index-url https://pypi.douban.com/simple/ \
&& python3 -m pip config set global.trusted-host pypi.douban.com \
&& pip3 install -r requirements.txt \
&& chmod +x main.sh
CMD ["./main.sh"]
构建
docker build . -f Dockerfile -t test-app:v1
部署
#不带数据库变量(不带就用默认变量)
docker run -itd --name test2 -p 30031:8000 docker.io/library/test-app:v1
#带数据库信息启动
docker run -itd --name test2 -e DB_NAME=mydb -e DB_HOST=192.168.1.100 -e DB_PASSWORD=strongpassword docker.io/library/test-app:v1
11、查看日志是否报错
docker logs test2
返回
/home/PAAS
[2024-02-02 03:05:44 +0000] [9] [INFO] Starting gunicorn 21.2.0
[2024-02-02 03:05:44 +0000] [9] [INFO] Listening at: http://0.0.0.0:8000 (9)
[2024-02-02 03:05:44 +0000] [9] [INFO] Using worker: gevent
[2024-02-02 03:05:44 +0000] [10] [INFO] Booting worker with pid: 10
[2024-02-02 03:05:44 +0000] [11] [INFO] Booting worker with pid: 11
[2024-02-02 03:05:44 +0000] [12] [INFO] Booting worker with pid: 12
写这块的时候比较忙,本来想演示的时候把数据库配置带上但忘记没做数据库相关的配置,上面有部分是后改的没有验,不确定是否有漏写的配置,有大兄弟做到这块了日志有报错私信发我下,会看
12、访问测试
http://服务器ip:30031/app1/info/
13、指定web容器nginx代理到django
cd /apps/web
vi default.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 反向代理设置
location /app1/ {
proxy_pass 127.0.0.1:30031;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
重构镜像
docker build . -f Dockerfile -t web-vue:v1
docker rm -f test1
docker run -itd --name test1 -p 30030:80 docker.io/library/web-vue:v1
14、验证从vue访问后通过nginx代理到django
#访问vue
http://101.43.156.78:30030
可以看到我们基本已经可以实现前后端容器的访问了,但是还是存在一些问题
nginx代理地址需要我们手动配置,我们更希望通过一种域名的方法去访问,但在实际环境中我申请个域名访问太费劲了,如果我自己个人使用的,那么直接使用docker network 创建一个网络,然后nginx通过容器name就可以实现访问,但这并不适用于较大的环境,处于考虑我们还是使用docker容器的编排部署工具K8S
五、服务上云(需先部署K8S环境)
环境搭建参考下前面的搭建文档 https://moziang.blog.csdn.net/article/details/132618086
1、查看节点状态
kubectl get node
返回
NAME STATUS ROLES AGE VERSION
k8s-master01 Ready control-plane,master 6m1s v1.22.2
我用的云服务器是单节点的,所以这里就一台同时作为master和node节点
2、yaml文件模板
在编写yaml前我们需要了解在K8s中的,无状态服务和有状态服务,无状态应用通常将状态信息存储在外部的数据存储服务(如数据库、缓存、文件存储等)中,而不是在应用本身内部,像是常见的无状态应用包括Web服务器、负载均衡器、API服务,所以这里我们基于K8S中的Deployment控制器进行编写
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
nodeSelector:
kubernetes.io/hostname: k8s-master01
containers:
- name: web-app
image: nginx
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "2Gi"
cpu: "500m"
env:
- name: APP_ENV
value: "production"
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
ports:
- containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 60
periodSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "touch /tmp/1.txt"]
preStop:
exec:
command: ["/bin/sh", "-c", "rm -f /tmp/1.txt"]
volumeMounts:
- name: localtime
mountPath: /etc/localtime
readOnly: true
volumes:
- name: localtime
hostPath:
path: /etc/localtime
配置说明
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app #控制器名称
spec:
replicas: 3 #pod的副本梳理
selector:
matchLabels:
app: web #匹配模板定义的标签
template:
metadata:
labels:
app: web #模板定义的标签
spec:
nodeSelector:
kubernetes.io/hostname: k8s-master01 #将模板调度到特定主机上
containers: #容器详情
- name: web-app #容器名称
image: nginx #镜像名称
imagePullPolicy: IfNotPresent #镜像策略
resources: #资源限制
limits:
memory: "2Gi"
cpu: "1000m" #硬限制为1C
requests:
memory: "2Gi"
cpu: "500m" #最低调度要求为0.5C
env: #设置环境变量
- name: APP_ENV #常规变量定义
value: "production"
- name: NODE_NAME #模板信息变量获取定义
valueFrom:
fieldRef:
fieldPath: spec.nodeName #获取模板带出的变量配置,这里是宿主机的主机名
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
ports: #查看端口配置
- containerPort: 80 #容器端口声明
protocol: TCP
livenessProbe: #设置健康检查
httpGet:
path: / #检查服务的/路径
port: 80
initialDelaySeconds: 60 #初次检查时间为容器创建后60s
periodSeconds: 3 #检查间隔时间
failureThreshold: 3 #检查失败次数
readinessProbe: #就绪检查探针,和上面基本一样,就是0/1 到1/1的转变
httpGet: #就绪检查不会导致容器重启
path: /
port: 80
initialDelaySeconds: 5
lifecycle: #声明周期勾子
postStart: #容器正常启动后执行啥
exec:
command: ["/bin/sh", "-c", "touch /tmp/1.txt"]
preStop: #容器退出之前挂掉执行啥
exec:
command: ["/bin/sh", "-c", "rm -f /tmp/1.txt"]
volumeMounts: #挂载外部存储到容器内
- name: localtime #这里是为了同步宿主机和容器的时间
mountPath: /etc/localtime
readOnly: true
volumes: #提供挂载配置,没有这个上面的mount不了
- name: localtime
hostPath:
path: /etc/localtime
3、编写Deployment模板
因为我们这里并不需要考虑后续对外注册什么的只是试验,这里的存储、俩检查、资源限制、狗子、节点匹配都可以先去掉,上面我定义了俩镜像web-vue:v1 和test-app:v1,把多余的配置去除,改下名称先跑跑看
vi web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: web-vue:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
vi app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: test-app:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
protocol: TCP
部署
kubectl apply -f web.yaml
kubectl apply -f app.yaml
查看
[root@k8s-master01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
api-84d68db78b-4667r 1/1 Running 0 24s
web-7d5cf4f9c9-j9lcc 1/1 Running 0 19s
4、创建后端svc
svc 是K8S 内部提供的一种负载均衡访问机制,这里是为了让web节点能够找到后端服务使用
vi app-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
selector:
app: api #这里对应的名称是上面deployment中templates下面定义的
#template: metadata: labels: app: api 对应这个
ports:
- protocol: TCP
port: 80
targetPort: 80
部署
kubectl apply -f app-svc.yaml
查看
[root@k8s-master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
app-service ClusterIP 10.99.245.102 <none> 8000/TCP 17m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 17h
[root@k8s-master01 ~]# curl 10.99.245.102:8000
5、创建前端svc
前端是直接面向用户进行访问的,所以还需要创建一个对外保留端口的svc,这里使用的模式是nodeport
vi web-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
type: NodePort
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30033
部署
[root@k8s-master01 ~]# kubectl apply -f web-svc.yaml
service/web-service unchanged
[root@k8s-master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
app-service ClusterIP 10.99.245.102 <none> 8000/TCP 5h27m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 22h
web-service NodePort 10.107.201.127 <none> 80:30033/TCP 63s
这里将我们的web服务以30033端口暴露在整个集群所有主机上,我们直接通过集群内任意主机+30033端口访问到web页面
//换成自己宿主机ip即可
http://101.43.156.78:30033/
6、修改web nginx后端地址
我们在上面使用docker部署的时候是通过在nginx配置文件中定义了后端的地址,这里明显不能在使用了
我们先去看看nginx代理之前配置的啥,
[root@k8s-master01 ~]# kubectl exec -it web-7d5cf4f9c9-j9lcc bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@web-7d5cf4f9c9-j9lcc:/# cat /etc/nginx/conf.d/default.conf
是直接指定固定地址(分好几天写的,应该是测试的时候改过,不影响下面操作)
cd /apps/web/
vi default.conf
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 反向代理设置
location /app1/ {
proxy_pass http://${DJANGO_HOST}:${DJANGO_PORT};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
我们这里需要将他改成 后端svc的名称+端口,我们也不希望每次改地址都打镜像,这里将经常变更的变量声明${}的格式,方便后续我们修改
vi main.sh
rm -f /etc/nginx/conf.d/default.conf
#定义要修改的变量名称
cat >> /tmp/env <<EOF
DJANGO_HOST
DJANGO_PORT
EOF
#拷贝模板文件
cd /etc/nginx/conf.d/
cp default.conf.template default.conf
#读取环境变量中的值并修改模板文件
while IFS= read -r var
do
# 从环境变量中获取对应变量的值
value=$(eval echo \$$var)
# 替换模板文件中的变量
sed -i "s|\${${var}}|${value}|g" default.conf
done < /tmp/env
#启动服务
nginx -g 'daemon off;'
我们上面在容器主进程启动之前,将我们要修改的值读取环境变量并替换掉,关于那个定义要修改的变量,我们可以用其他的方法,不一定非要在这里定个文件还不好修改,我们可以做个configmap 挂到某个目录 声明ENV_开头的变量都是要修改的变量,方便读取
vi Dockerfile
#使用官方node镜像作为基础镜像
FROM node:14 as build-stage
#拷贝项目目录到镜像
COPY ./my-vue-app1 /home/my-vue-app1
#设置工作目录
WORKDIR /home/my-vue-app1
#添加加速源并安装依赖
RUN npm config set registry http://registry.npm.taobao.org/
#这块的3个RUN可以试着合并为一个RUN执行,我这边是跑的时候死活不能用
#\ + && 一直报错就这样用了,另一台主机可以。 就很烦
RUN npm install
RUN npm run build
# 使用 Nginx 镜像作为最终镜像
FROM nginx:1.21
# 将构建阶段生成的 dist 目录复制到 Nginx 的默认静态文件目录
COPY --from=build-stage /home/my-vue-app1/dist /usr/share/nginx/html
# 暴露 80 端口
EXPOSE 80
# 拷贝配置文件
COPY ./default.conf /etc/nginx/conf.d/default.conf.template
COPY ./main.sh /usr/local/bin/main.sh
RUN chmod +x /usr/local/bin/main.sh
# 容器启动时运行 Nginx
CMD ["/usr/local/bin/main.sh"]
这里将我们修改的nginx配置文件和启动脚本都放进去,开始打镜像
重新构建镜像
docker build . -f Dockerfile -t web-vue:v2
7、修改web 控制器
vi web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: web-vue:v2 #修改新镜像
imagePullPolicy: IfNotPresent
env: #新增app的svc作为反向代理地址
- name: DJANGO_HOST
value: "app-service"
- name: DJANGO_PORT
value: "8000"
ports:
- containerPort: 80
protocol: TCP
部署
kubectl apply -f web.yaml
可以看到,前端程序已经通过nginx反向代理到后端的svc进行访问了
六、服务注册ETCD + HA 对外提供访问
我们前面部署的前端程序中,使用了一个nodeport的svc负载均衡,他会在集群上暴露一个指定的端口,你访问集群内任意一台主机都会转交给提供服务的pod,但是有一个弊端,每个这样的svc都相当于是占用了所有主机的一个端口,默认情况下端口范围是30000-32767,虽然可以调整,但是在多租户初次使用时,总是无法合理规划端口使用范围,经常出现争抢问题,为了降低端口占用,我们让容器部署成功后注册到etcd上,通过confd动态识别并修改HA上的代理地址,就不需要考虑宿主机端口的问题,后续要扩容也是走HA了
#参考文档
https://blog.csdn.net/liukuan73/article/details/53427070
实测用K8 暴露随机端口方案 不是很友好,先搁着研究研究