模型部署
Git
分布式的版本控制系统
版本控制
git工作区域分类:工作区 版本库 远程仓
工作区 →(add)→ 暂存区 →(commit)→ 版本库 →(push)→ 远程仓
工作区:平时我们写代码,存放代码的地方,本地文件夹
版本库:
暂存区:存放文件的索引,保存的是文件的信息
master:真实存储的地方
远程仓:存储云端
初始化及基础操作
图形化页面操作:
Git在这里创建版本库
Git提交到'master'
通过小乌龟(TortoiseGit)对版本继进行操控:日志和日志引用
git命令操作:
点击 Git Bash Here 打开命令窗口
显示配置信息 git config -l
配置个人信息
git config --global user.name 'aoiner' //配置用户名
git config --global user.email '1065346498@qq.com' //配置邮箱
git config --global core.quotepath false //中文文件名正常显示
git config --global credential.helper store //记住用户名和邮箱(远程联动时用 免去每次 push 输密码)
标准流程:改代码 → git status 看状态 → git add 暂存 → git commit -m 提交
在当前目录创建版本库 git init
查看工作区状态 git status
提交一个文件 git add 文件名
提交所有文件 git add *
提交到本地版本库(一次存档)git commit -m '描述信息'
修改最后一次提交的描述信息 git commit --amend -m '新描述'
显示版本记录(显示日志) git log
显示引用记录 git reflog
HEAD 指向当前版本(前7位即可) 该指令用于在各个版本里穿梭 git reset --hard commit_id
远程仓库
推送前最好先 git pull 拉取远程最新,避免冲突
命令操作
拷贝仓库 git clone 地址
推送文件 git push origin 分支名
git push (默认使用master推送)
分支管理:
查看分支:git branch
创建分支:git branch 分支名
合并分支(先切换到master):git merge 分支名
删除分支:git branch -d 分支名
Linux
基础操作
快捷键
放大终端:ctrl shift +
缩小终端:ctrl -
补齐:TAB
中英文切换:shift
清屏:ctrl + l or clear
终端
开启终端的快捷方式:
当前已经打开的终端窗口里,新建一个标签页 Ctrl + Shift + T
直接新开一个独立的终端窗口:Ctrl + Shift + N
关闭终端:ctrl d
| hq | @ | hq-jn | : | ~ | $ |
|---|---|---|---|---|---|
| 用户名 | 主机名 | 当前路径 | 命令提示符 |
| / | 根目录 |
|---|---|
| ~ | 家目录 |
| /home | 存放用户信息 |
| /bin | 二进制文件,存放普通用户命令 |
| /dev | 设备文件 |
| /etc | 配置文件 |
| /mnt | 挂载目录 |
| /root | 超级用户 |
查看当前绝对路径:pwd
查看用户名:whoami
查看主机名:hostname
ls 和 权限修改
显示隐藏文件:ls -a
查看文件详细信息:ls -l
| d | rwxrwxr-x | 2 | hq | hq | 4096 | 10月 30 2025 | work |
|---|---|---|---|---|---|---|---|
| 文件类型 | 权限 | 链接数 | 用户名 | 组名 | 大小 | 最后一次修改的时间 | 文件名 |
文件类型:bsp-lcd
| d | 目录文件 (文件夹) |
|---|---|
| - | 普通文件 (.c .h .py .txt .jpg) |
| b | 块设备文件 |
| s | 套接字文件 |
| p | 管道文件 |
| l | 软链接文件 |
| c | 字符设备文件 |
文件权限:
rwx r-x r-x
用户权限 组内成员权限 其他成员权限
读:r 写:w 执行:x 无权限:-
修改权限:chmod 权限值 文件名
权限值:111(对应读写执行位置) ---------> 4(读)+2(写)+1(执行) == 7
更改文件所有者:chown 所有者用户名:组名 文件名
cd 切换路径
cd 目标路径
绝对路径:从根目录开始一级级向下查找
相对路径:相对于当前位置开始索引查找
根目录:/
家目录:~
当前目录 . (可省略)
上一级 ..
上一次在的文件夹:-(cd -)
新建命令
新建文件夹:mkdir 目录文件名称(可连续创建多个文件夹)
创建有层级关系的文件夹 mkdir -p 目录文件/目录下文件
新建普通文件夹:touch 普通文件名称.后缀
更改时间戳:touch 已有文件名
删除命令
删除普通文件: rm 普通文件名
删除目录文件:rm -r 目录文件名
复制命令
复制普通文件: cp 目标文件 目标路径
复制目录文件:cp -r 目录文件 目标路径
另存为:cp 目标文件 目标路径/新名称
移动命令
将文件移动到新路径下:mv 文件名 目标路径
重命名:mv 源文件 目标文件
文件编辑和查看命令
vi编辑器
进入vi编辑器:vi 文件名
三种工作模式:
命令行模式 (一开始进入默认进入该模式) 复制(yy)、粘贴(p)、删除、剪切(dd)
插入模式 编辑文本
底行模式 保存(w)、退出(q) 、查找(/str)、替换(g /str hello)、取消高亮(set nohl)、行号(set nu)
通过 i o a 三个按键从命令行模式进入插入模式
通过 esc 回到 命令行模式
通过 shift : 进入底行模式
在底行模式下 w为保存 q为退出 !强制执行
解决 vi 方向键变成 ABCD、退格键失效:
输入sudo vi /etc/vim/vimrc.tiny
进入 vi 后,按 i 进入插入模式
修改set compatible为set nocompatible
并下面新增一行set backspace=2
显示文件内容
cat
cat 文件名
head(前) / tail(后)
默认显示hello.txt的前10行 head hello.txt
显示hello.txt的前n行 head -n hello.txt
默认显示hello.txt的后10行 tail hello.txt
显示hello.txt的后n行 tail -n hello.txt
网络配置
ubuntu网络配置
桥接模式:虚拟机和宿主机在同一个局域网 ,独立IP,局域网内其他设备能直接访问虚拟机 同一网段下
nat模式:虚拟机借用宿主机IP上网,内网隔离 ,外部设备默认不能直接访问虚拟机 不在同一网段
ping
测试能否联网 ping [www.baidu.com](https://www.baidu.com)
检测能否与主机通信 ping 主机ip地址
结束 ctrl c
ifconfig
显示网络配置接口,可以查看IP地址
ipconfig 查看windowsIP地址
netstat
查看所有网络连接状态 netstat -a
查看tcp的网络连接状态 netstat -at
查看udp的网络连接状态 netstat -au
curl传输数据的工具
curl发送一个基本的get请求,提供url即可
curl http://example.com :获取http://example.com的内容
通过-X POST 选项发送一个post请求,可以使用-d选项添加发送的数据
curl -X POST -d "param1=value1¶m2=value2" http://example.com/resource
wget
用于从网络下载文件,需要指定url
下载file.zip wget http://example.com/file.zip
Linux 安装工具
软件包安装
软件包的管理机制
| 常见的软件包后缀 | |
|---|---|
| windows | .exe |
| android | .apk |
| ubuntu | .deb |
| redhat | .rpm |
debian: deb软件包管理机制
redhat: rpm软件包管理机制(Red Hat Package Manager)
源 查看:sudo vi /etc/apt/sources.list
换源就是更新该文件中的网址:
换源:(推荐官方源或其他可用源)
步骤 1:备份当前源列表
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
步骤 2:编辑源列表文件
sudo vi /etc/apt/sources.list
步骤 3:替换为可用源(以 Ubuntu 18.04 LTS 为例)
将文件内容替换为 Ubuntu 官方源(或其他地区的镜像源,如阿里云、网易源),例如官方源:
deb http://archive.ubuntu.com/ubuntu/ bionic main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted universe multiverse
如果官方源速度慢,也可以尝试阿里云源:
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
步骤 4:保存并退出
esc 退回命令行模式,shift : 回到底行模式,wq保存并退出
软件包分类
二进制软件包:xx.deb
源码包: xx.dsc 源码、二进制文件、配置文件、说明文档
软件管理工具
在Debian的linux发行版本下,采用dpkg和apt两种包管理工具。
dpkg :离线安装,安装本地包,不需要网络;与仓库内的软件没有关系;不能检查包的依赖关系
apt :在线安装,通过仓库获取软件包资源;解决依赖关系,把依赖包一起安装;不能安装本地包
dpkg工具
离线安装软件包:dpkg -i 软件包全称
相关操作
sudo dpkg -s 软件包名:显示软件的安装状态
运行:直接写软件名
sudo dpkg -l 软件包名:查看版本名
sudo dpkg -L 软件包名:显示软件各种信息的安装路径
sudo dpkg -r 软件包名:卸载软件
sudo dpkg -P 软件包名:完全卸载软件,重新下载不会保留上一次配置。
查看软件使用:man sl
dpkg安装出现报错:dpkg: 错误: 另外一个进程已经为 dpkg frontend 加锁
解决方法:执行以下命令
sudo rm /var/lib/dpkg/lock-frontend 或者sudo rm /var/lib/dpkg/lock
apt工具
apt-get
在线安装软件包:sudo apt-get install 软件包名
默认下载的包存放路径:/var/cache/apt/archives/
相关操作
sudo apt-get remove 软件包名:卸载软件
sudo apt-get --purge remove 软件包: 完全卸载软件包(包括配置文件)
sudo apt-get update :下载更新软件包列表信息
sudo apt-get upgrade : 将系统中所有软件包升级到最新的版本
sudo apt-get upgrade 软件包名:将系统中指定的软件包升级到最新的版本
apt-cache
sudo apt-cache show 软件包名:获取二进制软件包的详细描述信息
sudo apt-cache policy 软件包名:获取软件包当前的安装状态
sudo apt-cache depends 软件包名:获取该软件包的依赖信息
sudo apt-cache rdepends 软件包名:获取所有依赖于该软件包的软件包
系统信息和控制
top
top动态显示系统进程和资源使用情况
退出:q
PR:优先级
VIRT:占用虚拟内存
RES:真正占用内存
SHR:共享内存
PR = NI + 20
NI值(nice值)的范围:-20 ~ 19,NI值越小优先级越高
sudo nice -n NI值 启动进程命令
sudo nice -n 5 bash
sudo renice [NI值] PID:改变正在运行的进程的优先级
sudo renice 6 2712
ps
显示所有用户的进程的详细信息:ps -aux
可以显示所有用户的进程号及父进程号:ps -ef
| 表头 | 含义 | 你图里标注 |
|---|---|---|
| USER | 进程所属用户 | 用户 |
| PID | Process ID,进程编号 | 进程号 |
| %CPU | CPU 占用百分比 | (图里漏写,CPU 使用率) |
| %MEM | 物理内存占用百分比 | (图里漏写,内存占比) |
| VSZ | Virtual Set Size,虚拟内存总大小 | 虚拟内存 |
| RSS | Resident Set Size,常驻物理内存(真正占用的物理内存) | 实际物理内存 |
| TTY | 关联终端 | 终端 |
| STAT | 进程状态(R/S/Sl/Z 等) | 进程状态 |
| START | 进程启动时间 | 开启时间 |
| TIME | 进程累计占用 CPU 总时间 | CPU 运行时长 |
| COMMAND | 启动这个进程的命令 | 命令 |
kill
本质是发送信号的命令
查看信号:kill -l
给PID进程发送num信号:kill -num PID
杀死PID进程,默认发送15号结束进程信号:kill PID
查看磁盘空间使用情况
df -Th
其他命令
grep 搜索文件中的文本
在file.txt中查找包含hello的字符串:grep "hello" file.txt
精确查找:grep "\<要查找的内容\>" 文件名
find 查找文件
find 路径 -name 文件名
示例:find ~/work -name "*.txt"
注意:?为只匹配任意一个字符,*匹配所有
zip
zip命令用于将一个文件或多个文件压缩成单一的压缩文件
unzip命令用于解压
1)具有归档功能,并进行压缩
2)可以压缩文件夹,后缀为.zip
3)压缩或者解压后源文件依然存在
tar 解压和压缩文件
1)具有归档功能,并通过选项可以进行压缩或解压
2)压缩或解压后源文件存在
3)需要写全压缩或解压的文件名格式
选项:
-x : 释放归档文件,解压
-c : 创建一个新的归档文件,压缩
-v : 显示归档和释放的过程信息
-f : 用户指定归档文件的文件名,否则使用默认名称
-j : 由tar生成归档,然后由bzip2压缩 .tar.bz2
-z : 由tar生成归档,然后由gzip压缩 .tar.gz
选项组合:
-cjf 以bz2格式压缩文件
-czf 以gz格式压缩文件
-xvf 解压压缩包
注意 : jz 在f 前; 压缩后源文件依然存在,解压后压缩包依然存在
示例:
tar -cjf xxx.tar.bz2 文件
tar -czf xxx.tar.gz 文件
tar -xvf xxx.tar.gz 或者 xxx.tar.bz2
history 显示命令历史记录
在家目录下的隐藏文件,通过vi打开:
.bash_history:默认存放2000条历史记录命令的文件
.bashrc :定义HISTSIZE(使用history时默认显示的记录个数)
HISTFILESIZE(.bash_history文件可以存放命令的个数)
man
man 章节 内容
man man:查看man手册
q 退出
1 Executable programs or shell commands 可执行程序或shell命令
2 System calls (functions provided by the kernel) 系统调用
3 Library calls (functions within program libraries) 库函数
sudo
sudo是 Linux 和其他类 Unix 操作系统中的一个非常重要的命令,用于以另一个用 户的身份(通常是 root 用户)执行命令。
sudo的全称是 "superuser do",它允许 普通用户在临时提升权限的情况下执行需要更高权限的操作。
eg: sudo command
切换到root用户
sudo su
MQTT
基础介绍
基于TCP/IP实现的一种应用层协议,同时兼具TCP和UDP的协议特点:有tcp的可靠性特点,也有udp分包、灵活的特点
适合小数据量传输的场景:比如物联网
三种身份:
1)broker 服务器 ,只负责消息转发,不处理,一般用现成的库搭建,不会做二次开发
2)发布者 往某个主题发消息
3)订阅者 订阅某个主题,接收来自这个主题的信息
客户端可以通过paho库来实现
消息
主题: topic 可以是任意的字符串,双方协定
消息体:字节流
服务质量QoS(Quality of Service)
1) 最多一次(QoS 0)这一级别会发生消息丢失,消息发布依赖于底层的TCP/IP 网络,适用于环境监测或快速数据采集
2) 至少一次(QoS 1)这一级别会确保消息到达,但是消息可能会重复,适用于智能家居控制或设备状态更新
3) 只有一次(QoS 2)确保消息只有一次到达,适用于财务交易、关键任务控制系统等
不同质量所付出的代价也不同 选择合适的QoS
搭建MQTT服务器(Broker)
windows版本:
安装mosquitto-1.6.9-install-windows-x64.exe,全部按默认安装即可,路径可以自己修改。
安装后在安装路径会有一个名为mosquitto.conf的配置文件,需要修改下配置文件才能让任意主机访问。
打开listener配置并允许任意IP访问,默认端口为1883
374行 listener 1883
修改allow_anonymous为true以允许主机匿名访问
651行 allow_anonymous true
如果想查看调试信息,那么就把log_type的配置项打开
600行 log_type debug
Linux版本:
获取离线压缩包mosquitto-1.6.3.tar.gz后
执行以下指令
tar -xf mosquitto-1.6.3.tar.gz
cd mosquitto-1.6.3/
make
sudo make install
代码实现客户端的接口
流程
创建MQTT实例 连接服务器(代理) 订阅主题 / 接收消息 / 发布消息
相关接口
Client
Client(client_id="", clean_session=True, userdata=None, protocol=MQTTv311, transport="tcp")
功能:创建MQTT客户端实例 参数: client_id :连接到代理时使用的唯一客户端ID字符串。 如果client_id长度为零或无,则会随机生成一个 clean_session :一个决定客户端类型的布尔值。 如果为True,那么代理将在其断开连接时删除有关此客户端的所有信息。 如果为False,则客户端是持久客户端,当客户端断开连接时,订阅信息和排队消息将被保留 userdata :用户定义的任何类型的数据作为userdata参数传递给回调函数。 它可能会在稍后使用user_data_set()函数进行更新。 protocol :用于此客户端的MQTT协议的版本。 可以是MQTTv31或MQTTv311 transport :设置为"websockets"通过WebSockets发送MQTT。 保留默认的"tcp"使用原始TCP 返回值:创建的实例
示例:
import paho.mqtt.client as mqtt
client = mqtt.Client()
connect
connect(host, port=1883, keepalive=60)
功能:将客户端连接到远程代码
参数 : host : 代理的IP地址或主机名 port :要连接的服务器主机的端口号,默认1883 keepalive :与代理通信之间允许的最长时间段(以秒为单位)。 如果没有其他消息正在交换,则它将控制客户端向代理发送ping消息的速率
on_connect:connect
功能:当代理响应连接请求时调用
on_connect(client,userdata,flags,rc)
rc: rc的值决定是否连接成功 0 为连接成功 1 协议版本错误 2 无效的客户端标识 3 服务器无法使用 4 错误的用户名和密码 5 未经授权
subscribe
subscrible(topic,qos=0)
功能:订阅主题
参数 :topic:一个字符串,指定要订阅的订阅主图
qos:期望的服务质量,默认0
on_message
on_message(client,userdata,massage)
功能:当收到关于客户端订阅的主题的消息是调用
message:是一个描述所有参数的MQTTMessage
message.topic 主题
message.payload 负载,消息的内容(字节类型,通常要**.decode()**转成字符串)
publish
publish(topic,payload)
功能:从客户端发送消息给代理
参数 :topic:该消息发布的主题
payload:要发送的实际消息。如果没有给出,或设置为无,则将使用零长度消息。
返回值 :rc:
| 内容 | 含义 |
|---|---|
| MQTT_ERR_SUCCESS | 成功 |
| MQTT_ERR_NO_CONN | 客户端当前未连接 |
| MQTT_ERR_QUEUE_SIZE | 当使用max_queued_messages_set来指示消息既不排队也不发送。 |
代码实现订阅和发布
订阅
# 模块导入
import paho.mqtt.client as mqtt
# 定义连接回调函数
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("连接成功")
client.subscribe("up")
client.subscribe("nb")
else:
print("连接失败")
# 定义消息到达后的回调函数
def on_message(client, userdata, message):
print(f"收到主题{message.topic},内容为{message.payload.decode()}")
# 创建实例
client = mqtt.Client()
# 绑定
client.on_connect = on_connect
client.on_message = on_message
# 连接
client.connect("192.168.50.98", 1883, 60)
# 阻塞,自动处理接收到的数据
client.loop_forever()
发布
# 模块导入
import paho.mqtt.client as mqtt
import time
# 定义连接回调函数
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("连接成功")
client.publish("up", "Hello")
else:
print("连接失败")
# 创建实例
client = mqtt.Client()
# 绑定回调函数
client.on_connect = on_connect
# 连接
client.connect("192.168.50.98", 1883, 60)
# 阻塞,自动处理接收数据
# client.loop_forever()
# 启动网络循环
client.loop_start()
# 等待一下,防止程序退出太快(测试用)
time.sleep(2)
# 停止循环
client.loop_stop()
# 断开连接
client.disconnect()
HTTP
简介
HTTP是一个基于TCP通信协议传输来传递数据 用于 B/S 架构
HTTP默认端口号为80,但是你也可以改为8080或者其他端口
是用于Web Browser(浏览器)到 Web Server(服务器)进行数据交互的传输协议
浏览器 发送请求 服务器 响应请求
特点
无连接:每次连接只处理一个请求
媒体独立 :任何数据都可以通过http发送 客户端以及服务器指定使用适合的MIME-type内容类型
无状态:对于事务处理没有记忆能力
协议格式
| 请求协议格式 | 响应协议格式 |
|---|---|
| 请求行 | 状态行 |
| 请求头部 | 响应头部 |
| 空行 | 空行 |
| 请求数据 | 响应正文 |
请求行
请求行 是由请求方法字段、url字段、http协议版本字段3个部分组成。请求行定义了本次请求的方式,格式如下:GET /example.html HTTP/1.1(CRLF)
请求方法有:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE、CONNECT
常用的也就是 get 和 post
GET和POST区别
| GET | POST |
|---|---|
| GET请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接(GET请求的数据会暴露在地址栏) | 把请求的数据放置在HTTP请求包的包体中 |
| 在使用GET请求时,传输数据会受到URL长度的限制 | 理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制 |
| 返回的内容可以被浏览器缓存起来 | 不会缓存POST请求返回的内容 |
| 只接受ASCII字符 | 没有限制 |
请求头
也被称作消息报头,请求头是由一些键值对组成,每行一对,关键字和值用英文冒号":"分隔。允许客户端向服务器发送一些附加信息或者客户端自身的信息,典型的请求头如下:
Accept:作用:描述客户端希望接收的 响应body 数据类型;示例:Accept:text/html
Accept-Charset:作用:浏览器可以接受的字符编码集;示例:Accept-Charset:utf-8
Accept-Language:作用:浏览器可接受的语言;示例:Accept-Language:en
Connection:作用:表示是否需要持久连接,注意HTTP1.1默认进行持久连接;示例:Connection:close
Content-Length:作用:请求的内容长度:示例:Content-Length:348
Content-Type:作用:描述客户端发送的 body 数据类型
请求数据
请求数据:请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是Content-Type和Content-Length。
状态行
由三部分组成,HTTP协议的版本号、状态码、以及对状态码的文本描述。例如:HTTP/1.1 200 OK (CRLF) 。(200表示请求已经成功)
常用状态码
● 200 OK:请求成功,服务器成功处理了请求并返回所请求的资源。
● 301 Moved Permanently:请求的资源已永久移动到新的URL,客户端应更新其链接。
● 302 Found:请求的资源暂时移动到新的URL,客户端应继续使用原始URL。
● 400 Bad Request:服务器无法理解请求的语法,通常是由于客户端发送的请求不正确导致的。
● 401 Unauthorized:请求要求身份验证,客户端需要提供有效的身份凭证。
● 403 Forbidden:服务器拒绝请求,客户端没有访问所请求资源的权限。
● 404 Not Found:请求的资源不存在,服务器无法找到所请求的资源。
● 500 Internal Server Error:服务器内部错误,无法完成请求的处理。
● 503 Service Unavailable:服务器当前无法处理请求,通常是由于服务器过载或维护导致的。
http和https的区别
| 对比项 | http | https |
|---|---|---|
| 默认端口 | 80 | 443 |
| 传输方式 | 明文 | 加密 |
| 安全性 | 可被窃听 | 加密+完整性校验 |
| 身份认证 | 无 | CA证书验证服务器身份 |
| 性能 | 快 | 稍慢 |
| URL前缀 | http:// | https:// |
| 协议栈 | HTTP->TCP | HTTP->TLS->TCP |
Flask
Flask 是一个非常小的 Python Web 框架 ,被称为微型框架:只提供了一个稳健的核心,其他功能全部通过扩展实现
Flask 的特点
| 特点 | 说明 |
|---|---|
| 轻量级和简洁 | 微框架,提供最基本功能,不强制使用任何特定工具或库 |
| 灵活性 | 没有强制性的项目布局或组件,开发者可自行定制 |
| 可扩展性 | 通过插件和扩展添加功能(表单、数据库、用户认证等) |
| 内置开发服务器 | 方便本地调试和测试 |
安装:pip install flask
第一个 Flask 应用
# 导入Flask类
from flask import Flask
# 创建Flask应用实例
app = Flask(__name__)
# 装饰器,用于告诉Flask哪个URL应该触发下面的函数
@app.route('/')
# 当用户访问根URL时,会调用以下函数
def hello_world():
return 'hello world!'
if __name__ == '__main__':
# 启动Flask内置开发服务器
app.run(debug=True)
运行后打开浏览器访问 127.0.0.1:5000/ 即可看到结果。
默认端口 5000 ;
debug=True开启调试模式,改代码后自动重启
路由
路由是 URL 到 Python 函数的映射:当用户访问特定 URL 时,Flask 调用对应的视图函数处理请求。
@app.route('/')
def hello_world():
return 'hello world!'
# 将 /about URL映射到 about 函数
@app.route('/about')
def about():
return 'This is about page'
第一个位置参数为路由分发的请求路径,分两类:
1)静态参数路由 ------ 路径写死
@app.route('/')
@app.route('/about')
@app.route('/index')
2)动态参数路由 ------ 路径里用 <变量名>,接收字符串和数字类型
@app.route('/index/<name>')
def index(name):
return f"hello {name}"
可以指定 int 型 (如 /index/<int:id>),视图函数中必须有同名的形参来接收:
@app.route('/index/<int:id>')
def index(id):
return f"hello {id}"
只写了
<name>的话,/index/123进来拿到的是字符串"123";指定<int:id>才会转成整数。
视图函数、请求对象、响应对象
视图函数:处理请求并返回响应的 Python 函数。通常接收请求对象作为参数,返回响应对象,或直接返回字符串、HTML 等内容。
请求对象(request):包含客户端发来的请求信息------请求方法、URL、请求头、表单数据等。
from flask import request
结合 curl 指令与服务器进行数据传输:
curl -X POST -d "username=John" 127.0.0.1:5000/submit
获取 GET 请求的数据用 request.args.get():
@app.route('/search')
def search():
keyword = request.args.get('key')
return f"搜索的关键词是 {keyword}"
访问
/search?key=python→ 返回搜索的关键词是 python。
响应对象:包含发送给客户端的响应信息------状态码、响应头、响应体。Flask 默认把字符串、HTML 直接作为响应体。
from flask import make_response
@app.route('/custom_response')
def custom_response():
response = make_response('This is a custom_response')
return response
模板(Templates)
Flask 使用 Jinja2 模板引擎渲染 HTML 模板,把 Python 代码嵌入 HTML,动态生成网页内容。
from flask import Flask, render_template
@app.route('/hello/<name>')
def hello(name):
return render_template('hello.html', name=name)
目录要求 :在当前 Python 文件同级目录 下新建 templates 文件夹,模板放里面。
⚠️ 文件夹名必须 是
templates(不能是template或别的名字),否则 Flask 找不到模板。
templates/hello.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> hello {{name}} </h1>
</body>
</html>
条件判断(Jinja2 语法):
{% if 条件 %}
...
{% else %}
...
{% endif %}
@app.route('/hello/<name>')
def hello(name):
# 假设 lisi、Alice 是 VIP 用户,其他人是普通用户
is_vip = False
if name in ['lisi', 'Alice']:
is_vip = True
return render_template('hello.html', name=name, is_vip=is_vip)
<body>
<h1> hello {{name}} </h1>
{% if is_vip %}
恭喜!您是尊贵的VIP用户!
{% else %}
欢迎体验!
{% endif %}
</body>
常用扩展
# request 全局请求对象,包含当前 HTTP 请求的所有信息
# send_file 允许从服务器向客户端发送文件
# jsonify 创建 JSON 响应的便捷函数
from flask import Flask, request, send_file, jsonify
# Flask-Compress:压缩响应数据(gzip/deflate/brotli/zstandard)
# 显著减少传输体积,加快客户端加载
from flask_compress import Compress
# CORS 是一种安全机制,用于控制不同域之间的资源访问
# 当前端访问与自己不同域(协议/域名/端口不同)的 API 时就需要 CORS
from flask_cors import CORS
# secure_filename:安全处理用户上传的文件名
# 移除有害字符、防目录遍历攻击、转为安全的 ASCII 文件名
from werkzeug.utils import secure_filename
HTML
简介
HTML (Hyper Text Markup Language,超文本标记语言):用来描述网页的一种语言。
-
所谓"超文本":可以加入图片、声音、动画、多媒体等内容,还可以从一个文件跳转到另一个文件。
-
HTML 不是编程语言 ,而是一种标记语言(markup language)。
-
Web 浏览器 的作用是读取 HTML 文档并以网页形式显示;浏览器不会显示 HTML 标签,而是用标签解释页面内容。
文档结构与核心标签
| 标签 | 作用 |
|---|---|
<html> |
所有标签的根节点,最大的标签 |
<head> |
文档头部,描述文档的属性信息(title、meta、base、style、script、link) |
<title> |
文档标题,让页面拥有自己的标题(head 里必须设置的就是它) |
<body> |
文档主体,页面内容基本都放这里(a、p、h、div、span、img...) |
标签格式
-
用尖括号包围的关键字,如
<html> -
通常成对存在,如
<body></body> -
前面是开始标签,后面是结束标签
标签分类
-
单标签 (空标签):
<标签名>,如<br> -
双标签 :
<标签名> 内容 </标签名>
常用标签
1)h1~h6 标题标签
<h1>这是标题标签</h1>
<h2>这是标题标签</h2>
<!-- ... 一直到 h6 -->
2)p 段落标签:段落会根据浏览器窗口大小自动换行
<p> 文本内容 </p>
3)br 换行标签(单标签)
<br>
4)div 标签:块级元素,可把文档分割为独立的、不同的部分,也能嵌套标签
<div class="news">
<h2>News headline 1</h2>
<p>some text. some text. some text...</p>
</div>
div 可以设置
class或id,通过选择器设置属性,内部成员就具有相同属性。
style 与 class
<style>
h1 {color:red;}
p {color:blue;}
</style>
class 属性规定元素的类名,主要用于指向样式表中的类:
<!-- head -->
<style>
.intro{ color:blue; }
.test { color:red; }
</style>
<!-- body -->
<div class="test">
<h2>News headline</h2>
<p>some text. some text. some text...</p>
</div>
表单标签
<form> 标签用于创建 HTML 表单来接收用户输入。
| 属性 | 取值 | 作用 |
|---|---|---|
method |
get / post | 规定发送表单数据时使用的 HTTP 方法 |
action |
URL | 规定提交表单时将数据发送到哪里 |
<form method="post" action="/submit"></form>
input :输入标签,单标签 ,属性很多,重点掌握 text 和 number。
<!-- 学生姓名输入框:单行文本,name=username,默认提示"学生姓名" -->
<input type="text" name="usrname" placeholder="学生姓名" required>
type="number" 用于输入数字,可用以下属性限制:
| 属性 | 说明 |
|---|---|
max |
允许的最大值 |
min |
允许的最小值 |
step |
合法的数字间隔 |
value |
默认值 |
<input type="number" name="score" placeholder="考试成绩(0-100)"
max="100" min="0" step="0.5" required>
button 的 type:
| 值 | 描述 |
|---|---|
button |
可点击的按钮 |
submit |
提交按钮(提交表单数据) |
reset |
重置按钮(重置为初始值) |
<button type="button">点击</button>
综合示例:Flask + HTML 表单
HTML(templates/hello.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
p {color:blue;}
.intro{ color:red; }
.test { color:blue; }
</style>
</head>
<body>
<h1>学生管理系统</h1>
<form method="post" action="/submit">
请输入学生姓名:
<input type="text" name="username" placeholder="学生姓名" required>
<br>
请输入成绩:
<input type="number" name="score" placeholder="考试成绩(0-100)"
max="100" min="0" step="0.5" required>
<button type="submit"> 提交成绩 </button>
</form>
</body>
</html>
Flask(app.py)
@app.route('/')
def hello_world():
return render_template('hello.html')
@app.route('/submit', methods=['post'])
def submit():
username = request.form.get('username')
score = request.form.get('score')
return f" {username}: {score}分 提交成功!"
流程 :输入 ip 出现 hello.html 页面 → 在页面填写表单 → 点击提交(向 /submit 发送 POST 请求)→ 服务器接收并返回一个网页。
request.form.get()取的是 POST 表单 数据,request.args.get()取的是 GET URL 参数,别混
Postman
Postman 是接口测试工具,不用写前端页面就能给后端发请求、看响应。老师课上用它测 Flask 写的接口。
基本用法(需先运行 Flask 代码)
-
新建请求 → 选择方法(
GET/POST) -
填 URL,如
http://127.0.0.1:5000/search?key=python -
GET:参数直接写在 URL 或 Params 里
-
POST :切到 Body → 选
form-data或x-www-form-urlencoded,填键值对 -
点 Send → 下方看响应体、状态码、响应头
| 场景 | 方法 | 参数位置 |
|---|---|---|
| 查询/搜索 | GET | URL 后 ?key=value(Params) |
| 提交表单 | POST | Body(form-data) |
Postman 里看到的 Content-Type 请求头,就是 HTTP 章节里说的"描述客户端发送的 body 数据类型"------用 form-data 时会自动带上 multipart/form-data。
Docker
是什么
Docker 是一种开源的容器化平台 ,允许开发者把应用程序及其所有依赖项(库、配置文件、环境变量等)打包到一个标准化的、轻量级、可移植的单元中,称为容器(Container)。
这些容器可以在任何支持 Docker 的环境中快速部署和运行,实现**"一次构建,处处运行"** 。容器完全使用沙箱机制 ,相互之间不会有任何接口,而且性能开销极低。
为什么需要 Docker
问题根源:应用部署的环境问题。 大型项目组件多、运行环境复杂:
-
依赖关系复杂,容易出现兼容性问题
-
开发、测试、生产环境有差异
例如一个项目部署时要依赖 node.js、Redis、RabbitMQ、MySQL 等,它们需要的函数库、依赖项各不相同,甚至冲突。
Docker 的两个手段
-
将应用的 Libs(函数库)、Deps(依赖)、配置 与应用一起打包
-
将每个应用放到隔离容器中运行,避免互相干扰
为什么不同系统上的库会不兼容? Ubuntu 和 CentOS 都基于 Linux 内核,无非系统应用不同、提供的函数库有差异。把 Ubuntu 版的 MySQL 装到 CentOS 上,调用 Ubuntu 函数库时就会找不到或不匹配而报错。
Docker 的三层解法
| 问题 | Docker 的解法 |
|---|---|
| 不同系统环境 | 把用户程序与所需调用的系统(如 Ubuntu)函数库一起打包 ;运行时借助宿主机的 Linux 内核直接跑 |
| 依赖关系复杂/冲突 | 应用+依赖+函数库+配置一起打包成可移植镜像 ;容器用沙箱机制相互隔离 |
| 开发/测试/生产环境差异 | 镜像中包含完整运行环境(含系统函数库),仅依赖 Linux 内核,可在任意 Linux 上运行 |
Docker 的优势
-
把程序及其依赖、运行环境一起打包为镜像,可迁移到任意 Linux 操作系统
-
运行时利用沙箱机制形成隔离容器,各应用互不干扰
-
启动、移除都可以通过一行命令完成,方便快捷
Docker 与虚拟机的区别
| 对比项 | Docker 容器 | 虚拟机 |
|---|---|---|
| 架构层级 | 操作系统级别 | 硬件级别 |
| 启动速度 | 秒级启动 | 分钟级启动 |
| 资源占用 | 很小,MB 级别 | 较大,GB 级别 |
| 隔离性 | 进程级别隔离(较轻) | 完全隔离(更安全) |
| 镜像大小 | 几十到几百 MB | 包含整个操作系统,很大 |
| 运行效率 | 高 | 低 |
结论:Docker 更轻量、更快速,更适合微服务和持续集成;虚拟机隔离性更强,适合运行不同内核或对安全性要求极高的场景。
🌱 墨补(老师课上留的思考题,给个参考答案)
-
Docker 和直接部署到 Linux 的区别 :直接部署要把依赖一个个装在宿主机上,环境互相污染、换机器就得重装;Docker 把环境和应用封进镜像,换机器只跑一条
docker run。 -
Docker 和 K8s 的区别 :Docker 是容器引擎/运行时 ,管单机上容器的构建与运行;K8s 是容器编排平台 ,管的是一整个集群里成百上千个容器的调度、扩缩容、自愈和负载均衡。两者是"零件"和"调度系统"的关系。
-
root 模式 vs rootless 模式 :默认 Docker 守护进程以 root 运行,容器逃逸风险较高;rootless 让守护进程和容器都以非 root 用户运行,安全性更高,代价是少量性能与功能限制。
-
Docker 镜像 vs 系统镜像 :系统镜像是整机备份 (含整个操作系统内核),用于装系统;Docker 镜像是应用及其运行环境的分层打包(不含内核,共享宿主内核),用于跑应用。
四个核心概念
| 概念 | 说明 |
|---|---|
| 镜像(Image) | 容器的静态模板 ,包含应用运行所需的所有依赖和文件,定义运行环境。镜像是不可变的 |
| 容器(Container) | 镜像的一个运行实例 ,有自己的文件系统、进程、网络,是动态的。从镜像启动,运行时保持可变 |
| Dockerfile | 文本文件,描述如何自动构建镜像(指定基础镜像、安装软件、复制文件等) |
| 仓库(Registry) | 存储和分发镜像的平台,如 Docker Hub(官方公共仓库)或私有仓库(如 Harbor) |
一句话记住关系:镜像是容器的模板,容器是镜像的实例;Dockerfile 里写着构建镜像的指令。
Dockerfile 常用指令
1)FROM ------ 指定基础镜像,必须为第一个命令
格式:
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
FROM python:3.9-slim
| 部分 | 含义 |
|---|---|
<image> |
基础镜像名称,如 ubuntu、python、nginx、alpine |
<tag> |
版本标签,如 3.9、slim、latest |
<digest> |
镜像唯一哈希值(更精准但不常用),如 sha256:xxxxxx,用于精确锁定内容 |
2)RUN ------ 构建镜像阶段的执行命令,每一条 RUN 都会生成一层镜像层
可做的事:安装软件包、下载并解压文件、编译安装软件、配置环境变量。
RUN mkdir /bigproject
3)ADD 和 COPY
| 指令 | 核心功能 |
|---|---|
| ADD | ① 复制本地文件/目录到镜像内 ② 自动解压归档文件(tar/gzip/bzip2) ③ 可下载远程 URL(不推荐用于生产) |
| COPY | 功能类似 ADD,但不会自动解压 ,也不能访问网络资源,推荐优先使用 |
COPY src dest
4)WORKDIR ------ 设置容器的工作目录,类似 Linux 的 cd,目录不存在会自动创建
WORKDIR /path/to/workdir
5)EXPOSE ------ 声明容器要暴露的端口
EXPOSE 80
⚠️ EXPOSE 只是声明/文档用途 ,真正对外映射必须靠运行时加
-p 宿主机端口:容器端口:
docker run -p 8080:80 # 把容器 80 端口映射到宿主机 8080 端口
6)ENTRYPOINT 和 CMD
CMD ------ 指定默认命令或参数 ,可以被 docker run 时附加的命令覆盖
CMD ["executable", "param1", "param2"]
FROM python:3.9-slim
CMD ["python", "app.py"]
ENTRYPOINT ------ 定义容器启动时始终运行的命令
ENTRYPOINT ["executable", "param1", "param2"]
特性:
-
支持附加参数 :
docker run的参数不会覆盖 ENTRYPOINT,而是作为它的附加参数 -
不可轻易覆盖 :除非用
--entrypoint明确指定新命令 -
唯一性 :每个 Dockerfile 中只能有一个 ENTRYPOINT,定义多个只有最后一个生效
-
主要用于定义不变的主命令
FROM python:3.9-slim
ENTRYPOINT ["nginx", "-g", "daemon off;"]
两者配合使用 :ENTRYPOINT 定义主命令 (固定不变),CMD 定义默认参数 (可变);docker run 传入的参数会覆盖 CMD,但不会覆盖 ENTRYPOINT。
FROM nginx
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
| 执行 | 等价于 |
|---|---|
docker run mynginx |
nginx -g daemon off; |
docker run mynginx -s reload |
nginx -s reload(CMD 被覆盖) |
实战写法(gunicorn 启动 Flask)
ENTRYPOINT ["gunicorn", "app:app", "-c", "gunicorn.conf.py"]
gunicorn.conf.py 常用配置:
bind = "0.0.0.0:80" # 绑定ip和端口号
workers = multiprocessing.cpu_count() * 2 + 1 # 工作进程数量
threads = 8 # 每个进程开启的线程数量
backlog = 2048 # 最大连接数量
worker_class = "gevent"
worker_connections = 1000 # 最大连接数量
max_requests = 1000 # 最大请求数量
daemon = False # 是否后台运行
pidfile = '/bigproject/gunicorn.pid' # pid文件存储路径
accesslog = '/bigproject/access.log' # 访问日志文件
errorlog = '/bigproject/gunicorn.log' # 错误日志文件
loglevel = 'info' # 日志级别
环境安装与配置(Ubuntu)
# 1. 更新软件源
sudo apt update
# 2. 安装基本软件
sudo apt-get install apt-transport-https ca-certificates curl software-properties-common lrzsz -y
# 3. 指定使用阿里云镜像(添加 GPG 公钥 + 软件源)
sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 4. 安装 docker
sudo snap install docker
# 5. 查看版本,验证安装
docker version
$(lsb_release -cs)会返回当前 Ubuntu 代号:20.04 → focal,22.04 → jammy。
配置国内镜像源(Docker Hub 在境外,直连慢或不稳)
cd /etc/docker
sudo touch daemon.json
sudo chmod 777 daemon.json
sudo vi daemon.json # 写入下面内容
{
"registry-mirrors": [
"https://docker-0.unsee.tech",
"https://docker-cf.registry.cyou",
"https://docker.1panel.live"
]
}
# 重启 docker 生效
sudo systemctl daemon-reload
sudo systemctl restart docker
# 测试:能正常下载镜像就说明配置成功
sudo docker pull alpine:latest
requirements.txt(放在项目文件夹中)
Flask==2.3.2
Werkzeug==2.3.7
Jinja2==3.1.2
itsdangerous==2.1.2
click==8.1.3
gunicorn==20.1.0
部署常用命令
构建镜像
sudo docker build -t 镜像名称:标签 .
sudo docker build -t ai_bigproject . # 不写标签默认 latest
-t给镜像打标签;末尾的.表示使用当前目录下的 Dockerfile 和构建上下文(别漏了)。
查看镜像 / 容器
sudo docker images # 查看构建的镜像
sudo docker ps # 查看运行中的容器
sudo docker ps -a # 查看所有容器(包括已关闭的)
启停 / 删除
sudo docker stop 容器ID # 停止容器
sudo docker start 容器ID # 启动已存在但已停止的容器
sudo docker rm -f 容器ID # 删除容器
sudo docker rmi -f 镜像ID # 删除镜像
🔥 面试题:Docker 查看容器的命令是什么? →
docker ps(运行中的)、docker ps -a(全部)。
从镜像创建并运行容器
sudo docker run -d --restart=always -p 18080:80 ai_bigproject:latest
| 部分 | 含义 |
|---|---|
docker run |
从镜像创建并启动一个新容器 |
-d |
--detach,后台(守护)模式运行 |
--restart=always |
重启策略:无论退出状态如何都自动重启。其他:no(默认不重启)、on-failure(非正常退出时重启)、unless-stopped(除非手动停止) |
-p 18080:80 |
端口映射,格式 主机端口:容器端口;外部通过 http://主机IP:18080 访问 |
ai_bigproject:latest |
要运行的镜像名称与标签 |
排查:容器无法正常启动怎么办?
# 1. 查看容器日志(容器ID 取前 3 位就行)
sudo docker logs 容器ID
# 2. 从容器把内容复制出来修改
sudo docker cp 7d0:/bigproject/ ./
# 3. 查看 ip 地址(先装工具)
sudo apt install net-tools
ifconfig
# 4. 进入容器内部
sudo docker exec -it 容器ID /bin/bash
exit # 退出
🔥 面试题:进入 docker 容器的指令? →
docker exec -it 容器ID /bin/bash。
检查镜像内环境
docker run -it --rm --entrypoint /bin/sh python:3.9
| 参数 | 含义 |
|---|---|
-it |
-i(--interactive)保持标准输入开启;-t(--tty)分配伪终端 |
--rm |
容器停止时自动删除,避免产生无用容器 |
--entrypoint /bin/sh |
覆盖镜像默认入口点,直接运行 shell |
python:3.9 |
使用的镜像 |
测试接口(上传文件)
sudo curl -X POST -F "file=@test.jpg" http://localhost:18080/upload --output file
| 参数 | 含义 |
|---|---|
-X POST |
指定 HTTP 方法为 POST |
-F "file=@test.jpg" |
发送 multipart/form-data 表单(常用于文件上传)。file 是表单字段名,@ 表示从本地文件读取 |
--output 文件名 |
指定保存返回内容的文件名 |
也可以用 Postman 测试同一接口。
容器导出为镜像 / 镜像导出导入
# 容器导出为镜像
sudo docker commit [容器ID或名称] [镜像名称]:[标签]
# 导出镜像到文件(tar 归档,便于跨机器传输)
sudo docker save -o ai.tar ai_bigproject:latest
# 导入镜像
sudo docker load -i ai.tar
示例一:Hello Docker(最小可用)
项目 hello_docker/ 下两个文件:app.py、Dockerfile
# app.py
print("Hello Docker!")
# Dockerfile
# 指定基础镜像
FROM python:3.9-slim
# 设置工作目录(容器内的目录)
WORKDIR /app
# 将当前目录的 app.py 复制到容器内的 /app 目录
COPY app.py .
# 运行 python 脚本
CMD ["python", "app.py"]
# 进入 hello_docker 目录后构建
sudo docker build -t hello-docker:v1.0 .
# 构建成功会输出:
# Successfully built xxxxxxxx
# Successfully tagged hello-docker:v1.0
docker images # 确认构建成功
# 运行
sudo docker run hello-docker:v1.0
# 终端显示:Hello Docker!
示例二:Flask + Docker(完整流程)
项目 flask_docker_demo/ 下两个文件:app.py、Dockerfile
# app.py
from flask import Flask
# 创建 Flask 应用实例
app = Flask(__name__)
# 定义路由 /
@app.route('/')
def hello():
return "Hello, Flask in Docker!"
# 启动应用(这行只在直接运行时生效,Docker 中不会直接用)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
注意:
app.run(...)在 Docker 中不会直接使用(生产用 Gunicorn)。这里为了简单先用 Flask 自带服务器,仅用于教学演示。
# 使用官方 Python 轻量镜像作为基础
FROM python:3.9-slim
# 设置工作目录(容器内的目录)
WORKDIR /app
# 将当前目录下的 app.py 复制到容器内的 /app 目录
COPY app.py .
# 安装 Flask(依赖多时建议用 requirements.txt)
RUN pip install --no-cache-dir flask
# 暴露容器运行时的端口(仅作文档用途,实际端口映射靠 docker run -p)
EXPOSE 5000
# 启动 Flask 应用(开发模式,仅演示,生产环境不推荐)
CMD ["python", "app.py"]
# 1. 构建镜像(确保 app.py 和 Dockerfile 在同一目录)
docker build -t flask-demo .
# 2. 确认构建成功
docker images
# 3. 后台运行,宿主机 8080 → 容器 5000
docker run -d -p 8080:5000 flask-demo
# 4. 访问
curl -D
# 或浏览器打开 http://localhost:8080
页面显示 Hello, Flask in Docker! 即成功。
⚠️ 在虚拟机内部 curl 可以直接用
127.0.0.1;在 Windows 浏览器 里要输入虚拟机的 IP:8080。