0%

说明

M1-平台使用不当

常见类型

糟糕的Web服务强化

  • 逻辑缺陷
  • 弱认证
  • 弱会话管理或没有会话管理
  • 会话固定
  • 使用GET方法传输的敏感数据

不安全的Web服务器配置

  • 默认内容
  • 管理界面
  • Web服务和支持移动的网站上的注入(SQL,XSS,Command)
  • 身份验证缺陷
  • 会话管理缺陷
  • 访问控制漏洞
  • 本地和远程文件包括

实践

  • 开发写代码的调试信息中,经常可能会有一些敏感的信息被打印出来,比如用jd-gui查看下如下内容:

image-20220401111712771

  • 既然是查看日志,完成可以采用直接如IDE+监控LogCat的方式

image-20220401112145099

  • 还有一种方式,其实就是adb logcat 过滤日志的方式,也是可行的

M2-不安全的数据存储

敏感数据未加密存储、本地文件未加密、WebView本地明文存储cookie等问题

常见类型

  • SQL数据库
  • 日志文件
  • XML数据存储或清单文件
  • 二进制数据存储
  • Cookie
  • 敏感数据存储到SD卡
  • SD卡上的数据可以被任意应用读取
  • 云同步

避免方式

  • URL缓存(请求和响应)
  • 键盘按键缓存
  • 复制/粘贴缓冲区缓存
  • 应用背景
  • 中间数据
  • 记录
  • HTML5数据存储
  • 浏览器cookie对象
  • 析数据发送给第三方

实践

  • 下载diva apk 解压后把apk文件安装到雷电模拟器上
  • 用模拟器打开apk

image-20220401160746121

image-20220401155946405

  • jd-gui 打开代码分析,发现使用了sharepreference 存信息到本地

image-20220401160501447

  • adb 进入到shell,找到shared_prefs,最终使用cat jakhar.aseem.diva_preferences.xml 查看到敏感的信息内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
adb shell
cd /data/data/jakhar.aseem.diva

osp:/data/data/jakhar.aseem.diva # ls
cache code_cache databases lib shared_prefs
osp:/data/data/jakhar.aseem.diva # cd shared_prefs
aosp:/data/data/jakhar.aseem.diva/shared_prefs # ls
jakhar.aseem.diva_preferences.xml
osp:/data/data/jakhar.aseem.diva # cat jakhar.aseem.diva_preferences.xml

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="user">hello</string>
<string name="password">word</string>
</map>
at jakhar.aseem.diva_preferences.xml <
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="user">hello</string>
<string name="password">word</string>
</map>

说明

  • 了解移动平台上的漏洞,因为越来越多的用户正在使用个人智能手机,而这类设备操作复杂,我们可能无法理解其背后的漏洞。今天的课程将以OWASP TOP 10(web也有对应的top 10)提供的十大移动漏洞为指导。

  • 阅读这个网站,里面对这个top 10 都有介绍

OWASP Mobile Top 10

  • M1 - 平台使用不当
  • M2 - 不安全的数据存储
  • M3 - 不安全的通信
  • M4 - 不安全的身份验证
  • M5 - 弱加密
  • M6 - 不安全的授权
  • M7- -客户端代码质量
  • M8- -代码篡改
  • M9– 逆向工程
  • M10- 无关功能

环境准备

  • 这里使用这个网站提供的apk安装包
    • 这个github上面提供的安全测试的资料文档可以仔细研读

M9– 逆向工程

工具介绍

  • ApkTool是主要查看res文件下xml文件、AndroidManifest.xmlclasses.dex。(注意:如果直接解压.apk文件,xml文件打开全部是乱码)
    • dex = Dalvik Executable format
    • dex文件:逻辑类似于javaclass文件
    • 安卓系统中,用Dalvik虚拟机(DVM = Dalvik Virtual Machine)去把java源代码编译为dex可执行文件(Dalvik Executable
  • dex2jar 作用:将apk反编译成Java源码(classes.dex转化成jar文件)
  • jd-gui 作用:查看APKclasses.dex转化成出的jar文件,即源码文件

工具安装

  • 安装apktool,本机安装好java 1.8+,打开安装网页我亲自测试过了,apktool解压后的文件没有发现classes.dex,所以后面还是采用改后缀名解压

image-20220331114419613

  • 前三个步骤分别是,下载一个apktool.bat文件,下载apktool_version.jar文件后,并重命令为apktool.jar,然后把apktool.batapktool.jar 放在同一个目录下

  • 打开dex2jar,下载最新的版本为:dex2jar-2.1

  • 打开jd-gui,下载最新的版本为:jd-gui-windows-1.6.6.zip

开始工作

  • 看下我提前准备好的工具

image-20220331145737635

  • 执行命令
1
D:\appsafetest>apktool d diva-beta.apk
  • 成功把apk解压,到res目录发现居然没有classes.dex

image-20220331155618163

  • 还是换成改后缀名为.zip,然后解压的方式得到了classes.dex

  • 使用dex2jar 进行反编译(下载jdk7,修改环境变量),提前把解压中的classes.dex放到dex-tools-2.1目录中

1
2
3
4
D:\appsafetest\dex-tools-2.1>java --version
java 17.0.2 2022-01-18 LTS
Java(TM) SE Runtime Environment (build 17.0.2+8-LTS-86)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.2+8-LTS-86, mixed mode, sharing)
  • 使用d2j-dex2jar 执行反编译命令,在当前文件夹会出现一个classes-dex2jar.jar
1
2
D:\appsafetest\dex-tools-2.1>d2j-dex2jar.bat classes.dex
dex2jar classes.dex -> .\classes-dex2jar.jar
  • 使用 jd-gui 打开classes-dex2jar.jar

image-20220331165030082

其他

  • 常见的破解app的流程,这里是最基础也是最关键的:

  • 得到classes.dex

    • 解压apk得到classes.dex,用传统的unzipapktool
    • 如果涉及到加固方面的东西,有普通加固商家收费的加固,如果是普通加固可以采用:夜神模拟器+FDex2+Xposed
  • dex文件反编译出jar包,比如用的这个工具dex2jar

  • jar包反编译出java源代码,比如反编辑器工具jd-gui

  • 使用jd-gui反编译出来查看的可能是smali文件,所以要学习一些smali基本的语法,这里修改代码

  • 修改完代码后,就要重新打包成APK文件了,可以采用apktool 来打包得到新的apk

  • 然后就是签名操作,打包得到的apk文件无法安装,需要签名,可以采用keytool -genkey -keystorejarsigner -verbose -keystore ,签名成功后,apk包就可以安装成功

  • 当然网络上也有一些工具把这些集成在一起了,比如改之理ApkToolkit

说明

  • 这篇文章说明了如何搭建分布式测试流程,这次主要介绍把代码放到服务器上,用Jenkins来运行
  • 本次服务器为某云服务器,centos7 64

Jenkins 环境搭建

  • 下载镜像仓库
1
wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo --no-check-certificate
  • 下载Jenkins.io.key
1
rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
  • yum下载安装依赖和jenkins
1
2
3
yum install epel-release
yum install java-11-openjdk-devel ##如果有装jdk8以上可跳过
yum install jenkins
  • 如果要修改端口,到这里修改不会生效的:vim /etc/sysconfig/jenkins

  • 正确的修改方式是在这个路径下

1
2
3
4
vim /usr/lib/systemd/system/jenkins.service

# 修改为想要的端口
Environment="JENKINS_PORT=5444"
  • 修改好路径后,重新加载服务
1
2
systemctl daemon-reload
systemctl restart jenkins
  • 如何知道自己的路径?用下面的命令就能查到
1
2
3
4
5
6
7
8
9
root@VM-24-13-centos jenkins]# systemctl status jenkins
● jenkins.service - Jenkins Continuous Integration Server
Loaded: loaded (/usr/lib/systemd/system/jenkins.service; disabled; vendor preset: disabled)
Active: active (running) since 二 2022-03-29 15:25:17 CST; 10min ago
Main PID: 23668 (java)
Tasks: 37
Memory: 269.3M
CGroup: /system.slice/jenkins.service
└─23668 /usr/bin/java -Djava.awt.headless=true -jar /usr/share/java/jenkins.war --webroot=%C/jenkins/war --httpPort=544..
  • 用浏览器打开访问地址

image-20220329153925727

  • 页面会出现类似于Please wait while Jenkins is getting ready to work ...,等待一会儿后,就自动会打开如上面的输入密码的界面,

    • 这里我没有实践。有的博客说一直停留在无法please wait界面,可以要修改配置文件需要进入/var/lib/jenkins/目录(通过rpm方式安装的jenkins是这个目录)下hudson.model.UpdateCenter.xml
    1
    2
    3
    将<url>https://updates.jenkins.io/update-center.json</url>
    换成
    <url>http://mirror.xmission.com/jenkins/updates/update-center.json</url>
  • 检查jenkins配置配置文件,将执行用户改成root,不然后面可能出现执行shell没有权限

    1
    2
    3
    4
    vim /etc/sysconfig/jenkins # 编辑文件
    JENKINS_USER="root" # 改成root

    service jenkins restart
  • 找到密码文件后,拷贝输入到浏览器中登录成功

1
vi /var/lib/jenkins/secrets/initialAdminPassword # 得到密码
  • 先默认不选择插件

image-20220329155214241

image-20220329155307321

  • 把测试代码上传到服务器,也可以采用使用Jenkins拉取git的方式,中途测试中jenkins出现无权限问题,可以修改目录权限:chmod -R 777 auto_web_ui/

  • HTML Publisher 插件可以展示html报告,jenkins本次不安装,用自带的Achive the artifacts

  • 然后新建一个job,执行shell里面配置生成pytest命令,运行完成后在当前目录下生成一个report.html报告,第一行的#!/bin/bash和最后一行的echo 0 是为了解决构建失败的问题

image-20220330100642967

  • 构建后操作,使用Achive the artifacts即可,测试报告的路径记得要对应

image-20220330100856448

构建运行

image-20220330101017680

查看html报告,样式丢失了

image-20220329180732531

  • 在jenkins->系统管理->脚本命令行,输入以下命令执行就可以了
1
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","")

image-20220329180858248

  • 打开html界面后,刷新

image-20220330101618078

  • 但是这样重启后会失效,永久解决方案是安装插件:Startup TriggerGroovy 插件

  • 在Job配置页面, 在构建触发器的时候勾选:Build when job nodes start

image-20220330102008961

  • 在Job配置页面,增加构建步骤Execute system Groovy script,脚本输入:System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","")

image-20220330102150623

说明

  • 前面文章介绍了如何利用docker搭建selenium grid

  • selenium grid由一个中心Hub节点和大于等于1个的Node节点组成,所有的Node节点都要注册到Hub节点。测试执行时所有的请求发送到Hub节点,Hub节点再将执行指令分发到多个注册的Node节点,Node节点是真正执行Web测试的节点,就相当于selenium本机执行一样。

  • 网上很多教程都是使用多进程/多线程启动多个node去执行用例,这样的意义并不大,如果一个Node中的用例太多,并不会节约多少时间,如果开启太多的进程用node去跑用例,无论是管理用例的复杂性和损耗资源都不是成正比

  • 正确的使用场景是一个node里面再去分布式执行用例,其实java中的testng提供这样的功能,而此次我介绍的是用python,因此需要集结合pytest

环境搭建

  • 本机window10安装好python3
  • pytest
  • pytest-html 生成测试报告插件
  • pytest-xdist 分布式用例
1
2
3
pip install pytest
pip install pytest-xdist
pip install pytest-html
  • 修改pytest源代码文件,解决报告乱码问题
1
2
3
4
5
6
D:\app\Python37\Lib\site-packages\pytest_html\plugin.py

class TestResult:
def __init__(self, outcome, report, logfile, config):
#self.test_id = report.nodeid.encode("utf-8").decode("unicode_escape")
self.test_id = re.sub(r'(\\u[a-zA-Z0-9]{4})',lambda x:x.group(1).encode("utf-8").decode("unicode-escape"),report.nodeid)
  • 看下我的代码结构

image-20220325182337774

  • 核心目录是testcase是用例目录,里面分为了大回归、小回归、冒烟文件夹,用例放不同的用例,这样的放的好处非常明显了,大回归包含小回归和冒烟,小回归包含冒烟
  • testcase目录下由conftest.py 这里面对pytestpytest-html可以进行有些设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# conftest.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from py._xmlgen import html

_driver = None


# @pytest.fixture()
@pytest.fixture(scope='session', autouse=True)
def driver():
global _driver
print(11111)
ip = "远程ip"
server = "http://%s:7777/wd/hub" % ip
# ip = "localhost"
_driver = webdriver.Remote(
command_executor="http://%s:7777/wd/hub" % ip,
desired_capabilities=DesiredCapabilities.CHROME
)
# 返回数据
yield _driver
# 实现用例后置
_driver.close()
_driver.quit()


@pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
"""
当测试失败的时候,自动截图,展示到html报告中
:param item:
"""
if not _driver:
return
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
report.description = str(item.function.__doc__)
extra = getattr(report, 'extra', [])

if report.when == 'call' or report.when == "setup":
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
screen_img = _capture_screenshot()
if screen_img:
html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" ' \
'onclick="window.open(this.src)" align="right"/></div>' % screen_img
extra.append(pytest_html.extras.html(html))
report.extra = extra


def pytest_html_results_table_header(cells):
cells.insert(1, html.th('用例名称'))
cells.insert(2, html.th('Test_nodeid'))
cells.pop(2)


def pytest_html_results_table_row(report, cells):
cells.insert(1, html.td(report.description))
cells.insert(2, html.td(report.nodeid))
cells.pop(2)


def pytest_html_results_table_html(report, data):
if report.passed:
del data[:]
data.append(html.div('通过的用例未捕获日志输出.', class_='empty log'))


def pytest_html_report_title(report):
report.title = "pytest示例项目测试报告"

def _capture_screenshot():
"""
截图保存为base64
:return:
"""
return _driver.get_screenshot_as_base64()
  • 用例编写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# test_selenium.py

mport os
import time
import pytest

class TestCase(object):
@pytest.mark.finished
def test_001(self, driver):
time.sleep(3)
driver.get("https://www.baidu.com")
print(driver.title)
driver.find_element_by_id("kw").click()
driver.find_element_by_id("kw").send_keys("你好")
def test1_001(self, driver):
time.sleep(3)
driver.get("https://www.baidu.com")
print(driver.title)
driver.find_element_by_id("kw").click()
driver.find_element_by_id("kw").send_keys("你好")
  • 代码运行入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# runner.py

import os
from multiprocessing import Process

import pytest


def main(path):
# 这里的-n 3 意思就是同时并发3个用例执行
pytest.main(['%s' %path,'-n 3', '--html=report.html','--self-contained-html', '--capture=sys'])


if __name__ == '__main__':
# 大回归
test_case = Process(target=main, args=("d:\\project\\py_selenium_grid\\testcase\\大回归\\",))
test_case.start()
test_case.join()
# 小回归
...
# 冒烟
...
  • 看下代码运行结果

image-20220325182511617

  • 看下本地的测试报告,错误的自动截图

image-20220325182640854

image-20220325182619284

其他

  • pytest-xdist 经过测试,在云服务器(双核)上跑,可以正常跑,如果指定进程太大,会造成容器内存泄漏,服务器出现长期卡死,所以建议:每次执行任务时,都把容器删了重建,同时进程不要指定太大
    • 可以进入到docker 容器中排除内存情况:docker exec -it ec3d30bff042 top,其中ec3d30bff042 selenium/node-chrome 的镜像
  • 测试了pytest-parallel 这个无论是在服务器还是本地win上跑,都报错
  • 使用了pytest-multithreading 发现个问题
    • pytest-html上的记录日志,会被打乱,因此如果要使用的化,建议在conftest.py中,记录日志的代码去掉
    • 多线程访问百度网站,会被限制输入验证信息
    • 安装pip install pytest-multithreading -i https://pypi.douban.com/simple
    • 调用 pytest -s testcase/大回归/小回归/冒烟 --th 10 --html=report.html --self-contained-html --capture=sys

总结

  • 当然如果没有条件,你对本地搭建selenium grid有兴趣的化,可以参考我之前的这篇文章,源代码都提供好了
  • 后续工作就更加简单,比如jenkins启动,我会对接一个可视化平台实现:任务管理,报告分析等

  • 在服务器上安装docker-compose
1
pip3 install docker-compose
  • 把本地windows10中的中文字体(C:\Windows\Fonts)随便选择几种拷贝到服务器的路径(/usr/local/selenium_grid/fonts

image-20220324193321079

  • 查看字体
1
2
3
4
5
6
7
8
/usr/local/selenium_grid/fonts
[root@VM-24-13-centos fonts]# ll
总用量 65268
-rw-r--r-- 1 root root 16829116 3月 24 18:15 msyhbd.ttc
-rw-r--r-- 1 root root 12139380 3月 24 18:15 msyhl.ttc
-rw-r--r-- 1 root root 19647736 3月 24 18:14 msyh.ttc
-rw-r--r-- 1 root root 18214472 3月 24 18:15 simsun.ttc

  • 编写compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
chrome: 
image: selenium/node-chrome:3.8.1
links:
- hub:hub # 这里是把挂载到hub下面的意思
ports:
- "5902:5900" # 给vnc调试用
environment:
- NODE_MAX_INSTANCES=5 # 实例化和下面参数一般保持一致,可以多机并行
- NODE_MAX_SESSION=5
- SCREEN_WIDTH=1920
- SCREEN_HEIGHT=1080
volumes:
- /dev/shm:/dev/shm # 挂载这个持久化数据,据说是为了防止不同的闪退
- ./fonts:/usr/share/fonts # 把中文字体挂载进来,解决截图乱码问题

hub:
image: selenium/hub:3.8.1
ports:
- "7777:4444" # 7777为外部web访问端口
  • 执行compose,-d表示后台执行
1
docker-compose up -d
  • 查看下镜像和容器

image-20220323192225606

image-20220323192256161

  • 远程查看下,输入:http://ip:7777/grid/console/?config=true&configDebug=true

image-20220323192506464

  • 编写测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 pip3 install selenium

import time

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from multiprocessing import Process

print(11111)
# ip = "远程ip"
ip = "localhost"
driver = webdriver.Remote(
command_executor="http://%s:7777/wd/hub" %ip,
desired_capabilities=DesiredCapabilities.CHROME
)
print(22222)

def test(i):
time.sleep(3)
driver.get("https://www.baidu.com")
print(driver.title, i)


if __name__ == '__main__':
test(1)
driver.close()
driver.quit()
  • 无论是本地还是服务器运行测试代码已经通过

image-20220323192620673

Dockerfile

  • 本文搭建步骤来自这里

  • Dockerfile 可以自定义编写镜像,简单来说就类似写shell脚本

  • Dockerfile命令科普

关键字 说明
FROM 基础镜像,当前新镜像是基于哪个镜像的
MAINTAINER 镜像维护者的姓名和邮箱地址
RUN 容器构建时需要运行的命令
EXPOSE 当前容器对外暴露出的端口
WORKDIR 指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
ENV 用来在构建镜像过程中设置环境变量
ADD 将宿主机目录下的文件拷贝进镜像且 ADD 命令会自动处理 URL 和解压 tar 压缩包
COPY 类似 ADD,拷贝文件和目录到镜像中。(COPY src dest 或 COPY [“src”,”dest”])
VOLUME 容器数据卷,用于数据保存和持久化工作
CMD 指定一个容器启动时要运行的命令,Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
ENTRYPOINT 指定一个容器启动时要运行的命令,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数
ONBUILD 当构建一个被继承的 Dockerfile 时运行命令,父镜像在被子继承后父镜像的 onbuild 被触发
  • 网上的图片介绍的还不错,仅供参考

image-20220322162743943

本地代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
import pytest


@pytest.mark.finished
def test_add():
print("测试函数:test_add")


@pytest.mark.finished
def test_subtract():
print("测试函数:test_subtract")


@pytest.mark.finished
def test_request():
res = requests.get("http://www.baidu.com")
assert res.status_code == 201


@pytest.mark.unfinished
def test_no_finish():
pass


if __name__ == "__main__":
pytest.main(["-s", "test.py"])
  • 导出依赖文件
1
2
pip install pipreqs
pipreqs --use-local --encoding=utf8 --force .
  • 得到依赖文件(requirements.txt)如下
1
2
pytest==6.2.4
requests==2.24.0

服务配置

1
2
3
4
5
6
7
8
9
[root@VM-24-13-centos data]# pwd
/usr/local/docker-demo/data

[root@VM-24-13-centos data]# ll
total 16
-rw-r--r-- 1 root root 264 Mar 22 17:47 Dockerfile
drwxr-xr-x 2 root root 4096 Mar 22 17:57 __pycache__
-rw-r--r-- 1 root root 33 Mar 22 17:36 requirements.txt # 依赖文件
-rw-r--r-- 1 root root 467 Mar 22 17:36 test.py # 源代码
  • 编写Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vi Dockerfile
# 基础镜像
FROM rackspacedot/python37
# 作者信息
MAINTAINER gsxl <xxxx@qq.com>
# 升级pip
RUN pip install --upgrade pip
# 容器的工作目录
WORKDIR /python
# 把依赖文件拷贝到容器的工作目录
COPY requirements.txt /python
# 安装容器工作目录下的依赖文件
RUN pip install -r /python/requirements.txt -i https://pypi.douban.com/simple
# 指定容器启动程序及参数
ENTRYPOINT ["pytest"]
CMD ["--help"]
  • 编译dockerfile build 运行(注意后面有个 . ):dcoker build -t 镜像:标签 .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@VM-24-13-centos data]# docker build -t test_py:v1 .
Sending build context to Docker daemon 4.096kB
Step 1/8 : FROM rackspacedot/python37
---> 4513b6d75e1c
Step 2/8 : MAINTAINER gsxl <xxxx@qq.com>
---> Using cache
---> 97ee10a89cc0
Step 3/8 : RUN pip install --upgrade pip
---> Using cache
---> 7e71f8ad49e6
Step 4/8 : WORKDIR /python
---> Using cache
---> 961389b2c6d2
Step 5/8 : COPY requirements.txt /python
---> 26d36133bc07
Step 6/8 : RUN pip install -r /python/requirements.txt -i https://pypi.douban.com/simple
---> Running in 8d36f9506780
Looking in indexes: https://pypi.douban.com/simple
Collecting pytest==6.2.4
Downloading https://pypi.doubanio.com/packa
Successfully built 1473b91e578e
Successfully tagged test_py:v1
  • 编译dockerfile成功后,会生成镜像文件,下面的test_py 就是刚刚生成的
1
2
3
4
5
6
[root@VM-24-13-centos data]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test_py v1 1473b91e578e 25 seconds ago 1.05GB
rackspacedot/python37 latest 4513b6d75e1c 4 weeks ago 1.03GB
mysql 5.7 c20987f18b13 3 months ago 448MB
hello-world latest feb5d9fea6a5 5 months ago 13.3kB
  • 开启测试:docker run -it --rm -v /usr/local/docker-demo/data:/python test_py:v1 test.py -s
  • -it :以交互模式运行容器
  • -v:挂载目录/usr/local/docker-demo/data到 容器内python文件夹
  • --rm:容器退出时,自动清除容器
  • test_py:v1:镜像:标签
  • test.py 就是源代码文件,因为-v挂载到了本地,使用pytest就能执行到本地源代码
  • -s:pytest 详细信息
1
2
3
4
5
[root@VM-24-13-centos data]# docker run -it --rm -v /usr/local/docker-demo/data:/python test_py:v1 test.py -s

====================================================== short test summary info ======================================================
FAILED test.py::test_request - assert 200 == 201
============================================== 1 failed, 3 passed, 4 warnings in 0.12s ==========

说明

本次代码主要是针对vue下载excel文件

服务器代码

  • django代码逻辑,每次客户端点击下载文件,服务器进行判断

  • 如果服务器路径上已经存在了生成的excel文件,就直接下载;否则就生成excel文件,然后下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def reports_top_slow_export(request):
data = []
# 每次都删除excel,然后重建
name = "top10慢的接口"
excel_name = os.path.join(Element.REPORT_FILE, name) + ".xlsx"
if os.path.exists(excel_name):
os.remove(excel_name)
print("文件已经删除" + excel_name)

with open(excel_name, 'a+', encoding="utf-8") as f:
print("创建文件成功" + excel_name)

item_entry = ReportItem.objects.all().order_by("-sum_time")[:10]

for i in item_entry:
url = i.protocol + "//" + i.url
if i.result == 1:
result = "通过"
else:
result = "失败"

data.append({"url": url, "params": i.params, "name": i.name, "method": i.method, "hope": i.hope,
"result": result, "sum_time": i.sum_time + "ms", "fact": i.fact})

workbook = xlsxwriter.Workbook(excel_name, {"string_to_urls": False})
worksheet = workbook.add_worksheet("响应时间最慢的top10")
worksheet.write("A1", "用例名")
worksheet.set_column("B:B", 60)
worksheet.write("B1", "url")
worksheet.write("C1", "请求方法")
worksheet.write("D1", "入参")
worksheet.write("E1", "期望结果")
worksheet.write("F1", "实际结果")
worksheet.set_column("F:F", 60)
worksheet.write("G1", "是否通过")
worksheet.write("H1", "耗时")
temp = 2
for j in data:
worksheet.write("A" + str(temp), j["name"])
worksheet.write("B" + str(temp), j["url"])
worksheet.write("C" + str(temp), j["method"])
worksheet.write("D" + str(temp), j["params"])
worksheet.write("E" + str(temp), j["hope"])
worksheet.write("F" + str(temp), j["fact"])
worksheet.write("G" + str(temp), j["result"])
worksheet.write("H" + str(temp), j["sum_time"])
temp += 1
workbook.close()

file_name1 = os.path.join(settings.BASE_DIR, "myapi/Report",name + ".xlsx")
file = open(file_name1, 'rb')
response = StreamingHttpResponse(file)
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="{}"'.format(name)
# file.close() 这里关闭会导致报错

return response

vue代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async reportExport() {
this.$http.get("reports_top_slow_export", {responseType:"blob"}).then(response => {
if(response.statusText=="OK"){
let downloadURL = this.dataURLtoBlob(response.data)
let anchor = document.createElement("a")
//下载时显示的文件名(自定义)
anchor.download = 'top10最慢接口.xlsx'
anchor.href = downloadURL
anchor.click()
this.$message({type: 'success', message: '下载成功!'})

} else {
this.$message({type: 'success', error: '下载失败!'})
}
});

},
dataURLtoBlob(encoded){
const dataBlob = new Blob([encoded]);
return window.URL.createObjectURL(dataBlob);
},

mysql实践

  • 本地环境(云服务器的本地路径)新建文件
1
2
mkdir -p ~/mysql/conf ~/mysql/data ~/mysql/logs
# 用来挂载容器中mysql的conf、data、logs目录)
  • docker pull镜像 安装 mysql5.7
1
2
3
4
5
6
7
docker pull mysql:5.7

[root@VM-24-13-centos ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 c20987f18b13 2 months ago 448MB
hello-world latest feb5d9fea6a5 5 months ago 13.3kB

  • 启动容器运行镜像
1
2
3
4
5
6
7
8
9
10
docker run -p 7777:3306 --name mysqltest --restart=always -v ~/mysql/conf:/etc/mysql/conf.d -v ~/mysql/data:/var/lib/mysql -v ~/mysql/logs:/logs -e MYSQL_ROOT_PASSWORD=root1234567 -d mysql:5.7

# -p 7777:3306 :容器内部端口3306映射到端口3306,既是稍后我们连接mysql用7777
# --name :运行的容器名称,每次为mysqltest
# --restart always:开机启动
# -v:如 ~/mysql/conf 对应容器的目录文件/etc/mysql/conf.d
# -e MYSQL_ROOT_PASSWORD=root1234567:设置root的密码root1234567
# -d 后台运行
# mysql:5.7:既是运行这个镜像mysql:5.7

  • 说明,还可以指定如下参数:
1
2
3
4
5
--privileged=true:提升容器内权限
-e MYSQL_USER=”marvin”:添加用户marvin
-e MYSQL_PASSWORD=”pwd123”:设置marvin的密码伟pwd123
--character-set-server=utf8:设置字符集为utf8
--collation-server=utf8_general_ci:设置字符比较规则为utf8_general_ci

注意:如果运行时添加了marvin用户,用root登录后执行用户授权ALTER USER 'marvin'@'%' IDENTIFIED WITH mysql_native_password BY 'password123';,才能用远程客户端访问数据库。

  • 使用客户端(HeidiSQL)连接,用户名为root,密码为root1234567

image-20220318145451272

  • 连接成功后,创建数据库和表

image-20220318151159402

  • 在服务器也可采用如下命令进入容器内部
1
2
3
4
5
6
7
8
9
10
 docker exec -it mysqltest mysql -uroot -proot1234567


mysql> select * from demo.student;
+----+------+------+---------+
| id | name | sex | address |
+----+------+------+---------+
| 1 | ?? | ? | 22333 |
+----+------+------+---------+
1 row in set (0.00 sec)

python实践

  • 搜索python镜像:docker search python3

image-20220318160151482

  • 拉取python3.7的镜像
1
docker pull rackspacedot/python37 
  • 拉取成功后可以看到镜像
1
2
3
4
5
6
[root@VM-24-13-centos ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
rackspacedot/python37 latest 4513b6d75e1c 3 weeks ago 1.03GB
mysql 5.7 c20987f18b13 2 months ago 448MB
hello-world latest feb5d9fea6a5 5 months ago 13.3kB

  • 创建自己的脚本目录。自己的应用或文件一定要放在宿主机上,使用-v 选项将宿主机的某个目录映射到容器的某个目录下,不然容器删除时,自己的应用及文件有可能会丢失。
1
mkdir /usr/local/python_proj
  • 启动容器
1
2
3
4
5
6
docker run -itd -v /usr/local/python_proj:/usr/src/myapp -w /usr/src/myapp --name mypython-37 rackspacedot/python37
# -itd 开启交互模式,为容器分配伪终端,在后台执行
# -w 选项 为-w, --workdir="", 指定容器的工作目录为/usr/src/myapp
# -v 选项将宿主机的/usr/local/python_pro录映射到容器的/usr/src/myapp目录
# --name 容器名词
# rackspacedot/python37 镜像名词
  • 查看到已经启动成功:docker ps

image-20220318163618143

  • 进入到容器查看python版本
1
2
3
[root@VM-24-13-centos ~]# docker exec -it mypython-37 python3 --version
Python 3.7.11

  • 在本机(宿主)脚本目录准备测试脚本
1
2
3
4
5
cd /usr/local/python_proj
vi hello.py

#!/usr/bin/python
print("hello world!")
  • 运行结果
1
2
[root@VM-24-13-centos python_proj]#  docker exec -it mypython-37 python3 hello.py
hello world!
  • pip 安装模块,可以进入到容器中,然后直接安装模块
1
2
3
# 进入到容器中
[root@VM-24-13-centos python_proj]# docker exec -it mypython-37 /bin/bash
root@f471a7e4b8c8:/usr/src/myapp# pip install requests
  • 也可以不进容器,直接在宿主机上安装
1
2
3
[root@VM-24-13-centos python_proj]#  docker exec -it mypython-37 pip install requests
Requirement already satisfied: requests in /usr/local/lib/python3.7/site-packages (2.27.1)

什么是docker

  • 这篇文章对docker进行了比较详细的介绍,我抄录一些笔记

  • Docker是容器技术的一种实现,也是操作系统层面的一种虚拟化,与虚拟机通过一套硬件再安装操作系统完全不同。

image-20220317105407328

  • Docker 和虚拟机并不一样,Docker是在操作系统进程层面的隔离,而虚拟机是在物理资源层面的隔离,两者完全不同,另外,我们也可以通过下面的一个比较,了解两者的根本性差异。

image-20220317105131805

Docker的版本

  • Docker分为社区版(CE)和企业版(EE)两个版本,社区版本可以免费使用,而企业版则需要付费使用,对于我们个人开发者或小企业来说,一般是使用社区版的。

  • Docker CE有三个更新频道,分别为stabletestnightlystable是稳定版本,test是测试后的预发布版本,而nightly则是开发中准备在下一个版本正式发布的版本,我们可以根据自己的需求下载安装。

开始安装

  • 我自己购买的是某云的服务器,系统是centos7 64如下,docker官方说至少3.8以上,建议3.10以上
1
2
3
[root@VM-24-13-centos ~]# uname -a
Linux VM-24-13-centos 3.10.0-1160.11.1.el7.x86_64 #1 SMP Fri Dec 18 16:34:56 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

  • 更新yum包,需要时间可能会比较久,慢慢等就行了,生产环境慎重操作
1
yum update
  • 修改yun-config的python版本为2
1
2
3
vi /usr/bin/yum-config-manager

#!/usr/bin/python2 -tt
  • 设置yum的源
1
2
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

  • 安装docker
1
sudo yum install docker-ce
  • 启动docker服务,后台运行
1
sudo systemctl start docker
  • 更换国内源
1
2
3
4
5
6
7
8
9
vi /etc/docker/daemon.json

{
"registry-mirrors": [
"https://registry.docker-cn.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn"
]
}
  • 重启
1
2
sudo systemctl daemon-reload
sudo systemctl restart docker
  • 查看是否设置成功
1
2
3
4
5
6
7
8
9
10
11
12
docker info

Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Registry Mirrors:
https://registry.docker-cn.com/
http://hub-mirror.c.163.com/
https://docker.mirrors.ustc.edu.cn/
Live Restore Enabled: false

  • 查看版本信息
1
2
3
4
5
6
7
8
9
10
11
[root@VM-24-13-centos ~]# docker version
Client: Docker Engine - Community
Version: 20.10.13
API version: 1.41
Go version: go1.16.15
Git commit: a224086
Built: Thu Mar 10 14:09:51 2022
OS/Arch: linux/amd64
Context: default
Experimental: true

  • 卸载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sudo yum remove docker \

docker-client \

docker-client-latest \

docker-common \

docker-latest \

docker-latest-logrotate \

docker-logrotate \

docker-selinux \

docker-engine-selinux \

docker-engine


sudo rm -rf /var/lib/docke

三大组件

镜像(Image)容器(Container)仓库(Repository)是我们常说的Docker的三大组件,来自对此文章的抄录

镜像

简单地理解,Docker镜像就是一个Linux的文件系统(Root FileSystem),这个文件系统里面包含可以运行在Linux内核的程序以及相应的数据。

谈到这里,我们可能需要先补充一点与Linux操作系统相关的知识:

一般而言, Linux分为两个部分:Linux内核(Linux Kernel)用户空间,而真正的Linux操作系统,是指Linux内核,我们常用的Ubuntu,Centos等操作系统其实是不同厂商在Linux内核基础上添加自己的软件与工具集(tools)形成的发布版本(Linux Distribution)。

因此,我们也可以把镜像看成是上面所说的用户空间,当Docker通过镜像创建一个容器时,就是将镜像定义好的用户空间作为独立隔离的进程运行在宿主机的Linux内核之上。

搜索镜像

1
2
3
4
[root@VM-24-13-centos ~]# docker search hello-word
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
chenlicn163/hello-word hello-word 0
...........

拉取镜像

  • 拉取镜像可以使用docker image pull,其格式如下:
1
docker image pull [OPTIONS] NAME[:TAG|@DIGEST]
  • 要拉取镜像,需要指定Docker Registry的URL和端口号,默认是Docker Hub,另外还需要指定仓库名和标签,仓库名和标签唯一确定一个镜像,而标签是可能省略,如果省略,则默认使用latest作为标签名,而仓库名则由作者名和软件名组成。

  • 比如下面就算拉取hello-word的镜像,整个命令的意思就是:在Docker Hub中拉取最新(last)版本的镜像文件

1
2
3
4
5
6
7
[root@VM-24-13-centos ~]# docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:4c5f3db4f8a54eb1e017c385f683a2de6e06f75be442dc32698c9bbe6c861edd
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest
  • 查看镜像 docker imagesdocker image ls
1
2
3
[root@VM-24-13-centos ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 5 months ago 13.3kB

导出和导入镜像

  • 如果想与别人共享某个镜像,除了从镜像服务仓库中pull镜像和把镜像push到仓库上去之外,其实我们还可以将本地构建好的镜像直接导出并保存为文件发送给别人,如下:
1
docker image save -o /tmp/test_image.tar.gz hello-world:latest
  • 查看导出的镜像
1
2
[root@VM-24-13-centos ~]# ll /tmp/ | grep image
-rw------- 1 root root 24064 Mar 17 15:11 test_image.tar.gz
  • 而当你拿到别人导出的镜像文件,你可以使用docker load命令把镜像加载到本地的Docker镜像列表中,如下:
1
docker load < /tmp/test_image.tar.gz

删除本地镜像

  • 常用格式如下:
1
docker rmi  [option]  IMAGE1,IMAGE2,...IMAGEn
  • 可以使用镜像的长id、镜像短id、镜像摘要以及镜像名称来删除镜像,如下删除刚刚拉取的hello-word镜像
1
2
3
4
5
6
[root@VM-24-13-centos ~]# docker rmi feb5d9fea6a5
Untagged: hello-world:latest
Untagged: hello-world@sha256:4c5f3db4f8a54eb1e017c385f683a2de6e06f75be442dc32698c9bbe6c861edd
Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
Deleted: sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359

  • 当然我们想要清除本地全部镜像时,可以使用下面的命令,不过一般不建议使用
1
$ docker rmi $(docker images -qa)
  • 另外,一般如果镜像已经被使用来创建容器,使用上面的命令删除会报下面的错误,告诉我们该镜像已经被使用,不允许删除。对于已经被用于创建容器的镜像,删除方法有两种,一种是先把容器删除,再删除镜像,另一种则只需要在删除镜像的命令中跟一个-f参数便可,如:
1
$ docker rim -f f7302

构建镜像

面的例子都是直接使用官方提供的镜像,其实,除了从官方仓库或其他镜像仓库拉取别人构建好的镜像外,我们也可以构建自己的镜像,本次跳过

容器(Container)

  • 容器与镜像的关系,就如同面向编程中对象与类之间的关系。因为容器是通过镜像来创建的,所以必须先有镜像才能创建容器,而生成的容器是一个独立于宿主机的隔离进程,并且有属于容器自己的网络和命名空间。

  • 镜像是只读的,但容器却是可读可写的

启动容器

有两种,分别为docker rundocker start

docker run
  • 使用docker run命令通过镜像创建一个全新的容器
    • 如果镜像(hello-world)本地不存在,会提前去下载,然后再次运行
    • 运行后会退出,通过docker ps 查看不到,要加个-adocker ps -a
1
docker run hello-world
  • 如容器是一种提供服务的守护进程,那么通常需要开放端口供外部访问,则容器会一直处于运行状态,如
1
2
docker run -p 81:80 nginx
# -p 81:80 将容器内部端口80映射到端口81
  • 可以为容器指定一个名称,如:
1
2
$ docker run -p 80:80 --name mynginx nginx
# --name 后面的myngin就是自定义的容器名字
  • 也可以进入到容器中,直接与容器进行交互
1
2
3
4
$ docker run -it centos /bin/bash
# -i 表示允许你对容器内的标准输入 (STDIN) 进行交互。
# -t 在新容器内指定一个伪终端或终端
# /bin/bash表示运行容器后要执行的命令
docker start

另外一种则是使用docker start命令重新启动已经停止运行的容器

1
2
# container_id表示容器的id
$ docker start container_id

进入容器

  • Docker容器运行起来以后,要想进入容器内部可以先通过docker ps命令查看,当前运行的容器信息

  • 再通过 docker exec -it ec3d30bff042 命令,其中ec3d30bff042为容器ID docker exec -it ec3d30bff042 /bin/bash

重启

而对于正在运行的容器,也可以通过docker restart命令重新启动,如

1
$ docker restart container_id

查看容器

  • 查看在运行的容器
1
docker ps
  • 查看所有容器包括在运行中的和停止的容器
1
docker ps -a
  • 比如运行hellword,为什么输入 docker ps 不显示出来呢?输入docker ps -a 就显示出来?这里说明一下,这个 hellwordrun一下子它就结束程序了你还想它后台运行啥?如果你还是让它运行,那倒是也可以,写一个死循环就可以咯!

  • 有时候,我们只想查到容器的id,可以用下面的命令:

1
docker ps -aq

停止容器

  • 对于已经不需要的容器,可以使用docker stop命令停止其运行,如:
1
$ docker stop container_id1,container_id2...
  • 批量停止容器,如:
1
$ docker stop $(docker ps -qa)

删除容器

  • 指定容器id进行删除
1
2
# container_id表示容器id,通过docker ps可以看到容器id
$ docker rm container_id
  • 当我们需要批量删除所有容器,可以用下面的命令:
1
2
3
# 删除所有容器
docker rm $(docker ps -a -q)

  • 删除所有退出的容器,这样执行后,刚刚执行的helloword容器用docker ps -a就查不到了
1
docker container prune

导出容器为镜像

1
$ docker export -o ./image.tar.gz f4f184f5ffb9
  • 将容器导出后,我们可以另外一台有安装Docker的电脑中将文件包导入成为镜像,如:
1
$ docker import image.tar.gz

仓库

仓库(Repository)是集中存储镜像的地方,这里有个概念要区分一下,那就是仓库与仓库服务器(Registry)是两回事,像我们上面说的Docker Hub,就是Docker官方提供的一个仓库服务器,不过其实有时候我们不太需要太过区分这两个概念。

公共仓库

  • 公共仓库一般是指Docker Hub,前面我们已经多次介绍如何从Docker Hub获取镜像,除了获取镜像外,我们也可以将自己构建的镜像存放到Docker Hub,这样,别人也可以使用我们构建的镜像。

  • 不过要将镜像上传到Docker Hub,必须先在Docker的官方网站上注册一个账号,注册好了之后,可以在本地使用命令登录到Dokcer Hub了,过程如下:

    1
    $ docker login

在输入账号密码登录到Docker Hub之后,便可以使用docker push命令把镜像推送到Docker Hub

1
$ docker push test:1.0

私有仓库

  • 有时候自己部门内部有一些镜像要共享时,如果直接导出镜像拿给别人又比较麻烦,使用像Docker Hub这样的公共仓库又不是很方便,这时候我们可以自己搭建属于自己的私有仓库服务,用于存储和分布我们的镜像。

  • Docker官方提供了registry这个镜像,可以用于搭建私有仓库服务,我们把镜像拉到本地之后,用下面命令创建该镜像的容器便可以搭建一个仓库服务,如下:

1
$ docker run -d -p 5000:5000 --restart=always --name registry registry
  • 假设我们把一台IP为192.168.0.100的服务器作为仓库服务,并运行上面的语句,那么我们可以下面的语句重新构建上面的镜像,如:
1
$ docker build -t "192.168.0.100/hello-go:1.0" .
  • 然后使用下面的语句推送到自己的私有仓库服务器:
1
$ docker push 192.168.0.100/hello-word:1.0

Docker的组成与架构

  • 在安装好并启动了Docker之后,我们可以使用在命令行中使用docker命令操作docker,比如我们使用如下命令打印docker的版本信息。

image-20220317165337772

  • 从上面的图中,我们看到打出了两个部分的信息:ClientServer

  • 这是因为Docker跟大部分服务端软件一样(如MySQL),都是使用C/S的架构模型,也就是通过客户端调用服务器,只是我们现在刚好服务端和客户端都在同一台机器上而已。

  • 因此,我们可以使用下面的图来表示Docker的架构,DOCKER_HOSTDocker server,而Clinet便是我们在命令中使用docker命令。

    image-20220317165825026

说明

本次主要针对在腾讯云服务器上对vue+django进行部署

vue本地配置

  • vue的请求接口修改为服务器上的ip或者域名
1
2
3
4
// 设置接口访问的根目录
axios.defaults.baseURL = "http://host:8081/myapi/"
// 设置原型属性后,其他地方如果要调用,只要用this.$http.get就可以了
Vue.prototype.$http = axios
  • 打包后
1
npm run build
  • 压缩打包后的文件传到服务器路径:/home/dist,压缩软件推荐使用:7-zip
  • 先压缩为tar

image-20220314102924031

  • 再次压缩为格式为gzip

image-20220314103056936

image-20220314103209261

  • 把dist.tar.gz上传的服务器,路径为:home

django本地

  • 主要是导出依赖文件,执行下面的命令后,项目本地会生成一个requirements.txt依赖文件
1
2
pip install pipreqs
pipreqs --use-local --encoding=utf8 --force .

服务器配置

  • 腾讯云的centos7系统内置了python3环境
1
2
3
[root@VM-24-13-centos mysite]# python  --version
Python 3.6.8

端口设置

  • 端口规则,因为腾讯云的端口除了防护墙打开外,还需要单独新建端口规则

image-20220314102334057

  • 服务器开放端口的一些配置:
1
2
3
4
5
6
7
8
firewall-cmd --zone=public --add-port=8100/tcp --permanent
firewall-cmd --zone=public --add-port=8081/tcp --permanent

firewall-cmd --reload # 配置立即生效
firewall-cmd --zone=public --list-port # 查看防火墙所有开放的端口
firewall-cmd --state # 查看防火墙状态
netstat -lnpt # 查看监听的端口
netstat -lnpt |grep 8081# 查看监听的具体端口

nginx配置

  • 查看 Nginx 的默认配置的安装位置
1
2
3
4
root@VM-24-13-centos git]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

  • 配置路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vi /etc/nginx/nginx.conf

server {
listen 8001;
listen [::]:8001;
server_name www.XXX.123;
root /home/dist; # vue的路径
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}


  • 解压打包的dist压缩包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@VM-24-13-centos home]# tar -zxvf dist.tar.gz
dist/
dist/css/
dist/css/app.54a7ebec.css
dist/css/chunk-vendors.84bb20f7.css
dist/favicon.ico
dist/fonts/
dist/fonts/element-icons.535877f5.woff
dist/fonts/element-icons.732389de.ttf
dist/img/
dist/img/logo.82b9c7a5.png
dist/index.html
dist/js/
dist/js/app.942844bf.js
dist/js/app.942844bf.js.map
dist/js/chunk-vendors.0b404296.js
dist/js/chunk-vendors.0b404296.js.map

  • 重启nginx
1
systemctl restart nginx

django

  • 把django的源代码上传到服务器
  • 安装依赖文件
1
2
3
cd /usr/local/mysite
pip install -r requirements.txt

  • 测试django是否能运行
1
python3 manage.py runserver 0.0.0.0:8081
  • 出现报错文件,类似于
1
corsheaders 文件不存在
  • 这个是django装的一个跨域访问的中间件,需要手动安装
1
pip3 install django-cors-headers
  • 再次测试django是否正常,发现报错如下,看字面意思就知道是使用的sqlite版本python必须大于3.8.3
1
2
3
4
ile "/usr/local/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 67, in check_sqlite_version
raise ImproperlyConfigured('SQLite 3.8.3 or later is required (found %s).' % Database.sqlite_version)
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).

  • 修改如下,把版本修改下就即可
1
2
3
4
5
6
vi /usr/local/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py

def check_sqlite_version():
if Database.sqlite_version_info < (3, 7, 17):
raise ImproperlyConfigured('SQLite 3.7.17 or later is required (found %s).' % Database.sqlite_version)

  • 再次执行就不会报错:python3 manage.py runserver 0.0.0.0:8081

  • 直接新建一个sh文件,内容主要是日志记录文件和后台启动,注意日志这里文件要手动提前新建好:

1
2
3
4
5
6
7
8
9
root@VM-24-13-centos mysite]# pwd
/usr/local/mysite
[root@VM-24-13-centos mysite]# vi start_api.sh

MYDATE=`date +%Y%m%d`
ALL_LOGFILE=/home/api_log/log/log_$MYDATE

nohup python3 manage.py runserver 0.0.0.0:8081 > ${ALL_LOGFILE} 2>&1 &

  • 提前查看端口是否没有被占用,如果被占用就直接kill
1
netstat -lnpt |grep 8081
  • 执行start.sh文件
1
sh start.sh
  • 最好重启下nginx
1
systemctl restart nginx
  • 访问下域名正常

image-20220314105945414