Nvidia Triton 使用入门教程

1 相关预备知识

  • 模型:包含了大量参数的一个网络(参数+结构),体积10MB-10GB不等。
  • 模型格式:相同的模型可以有不同的存储格式(可类比音视频文件),目前主流有torch、tf、onnx和trt,其中tf又包含了三种格式。
  • 模型推理:输入和网络中的参数进行各种运算从而得到一个输出,计算密集型任务且需要GPU加速。
  • 模型推理引擎:模型推理工具,可以让模型推理速度变快,使用该工具往往需要特定的模型格式,现在主流推理引擎有trt和ort。
  • 模型推理框架:对模型推理的过程进行了封装,使之新增、删除、替换模型更方便;更高级的框架还会有负载均衡、模型监控、自动生成grpc、http接口等功能,就是为部署而生的。 

接下来要介绍的triton就是目前比较优秀的一个模型推理框架

2 入门triton

接下来手把手教你跑通triton,让你明白triton到底是干啥的。

2.1 注册NGC平台

NGC可以理解是NV的一个官方软件仓库,里面有好多编译好的软件、docker镜像等。我们要注册NGC并生成相应的api key,这个api key用于在docker上登录ngc并下载里面的镜像。

NGC 官方网站的地址是: https://ngc.nvidia.com

注册和登录后,你可以在用户界面(profile UI)中生成 API 关键字(key)。这个 key 允许你在 Docker 或其他适当的地方登录 NGC,并从该仓库下载 Docker 镜像等资源。

2.2 登录

命令行界面输入  docker login nvcr.io

然后输入用户名和你上一步生成的key,用户名就是$oauthtoken,不要忘记$符号,不要使用自己的用户名。
最后会出现Login Succeeded字样,就代表登录成功了。

2.3 拉取镜像

docker pull nvcr.io/nvidia/tritonserver:22.04-py3

你也可以选择拉取其他版本的triton。镜像大概有几个G,需耐心等待,这个镜像不区分gpu和cpu,是通用的。

2.4 构建模型目录

执行命令mkdir -p /home/triton/model_repository/fc_model_pt/1
其中/home/triton/model_repository就是你的模型仓库,所有的模型都在这个模型目录中。启动容器时会将其映射到容器中的/model文件夹上,fc_model_pt可以理解为是某一个模型的存放目录,比如一个用于情感分类的模型,名字则没有要求,最好见名知义,1代表版本是1
模型仓库的目录结构如下:

  <model-repository-path>/# 模型仓库目录<model-name>/ # 模型名字[config.pbtxt] # 模型配置文件[<output-labels-file> ...] # 标签文件,可以没有<version>/ # 该版本下的模型<model-definition-file><version>/<model-definition-file>...<model-name>/[config.pbtxt][<output-labels-file> ...]<version>/<model-definition-file><version>/<model-definition-file>......

2.5 生成一个用于推理的torch模型

创建一个torch模型,并使用torchscript保存:

import torch
import torch.nn as nnclass SimpleModel(nn.Module):def __init__(self):super(SimpleModel, self).__init__()self.embedding = nn.Embedding(100, 8)self.fc = nn.Linear(8, 4)self.fc_list = nn.Sequential(*[nn.Linear(8, 8) for _ in range(4)])def forward(self, input_ids):word_emb = self.embedding(input_ids)output1 = self.fc(word_emb)output2 = self.fc_list(word_emb)return output1, output2if __name__ == "__main__":model = SimpleModel()ipt = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.long)script_model = torch.jit.trace(model, ipt, strict=True)torch.jit.save(script_model, "model.pt")// 这段代码定义了一个简单的 PyTorch 模型,然后使用一个输入 Tensor 来在严格模式下 Trace 该模型,并将 Trace 后的模型保存为 "model.pt" 文件。

生成模型后拷贝到刚才建立的目录中,注意要放到版本号对应的目录,且模型文件名必须是model.pt。

triton支持很多模型格式,这里只是拿了torch举例。

2.6 编写配置文件

为了让框架识别到刚放入的模型,我们需要编写一个配置文件config.pbtxt,具体内容如下:

name: "fc_model_pt" # 模型名,也是目录名
platform: "pytorch_libtorch" # 模型对应的平台,本次使用的是torch,不同格式的对应的平台可以在官方文档找到
max_batch_size : 64 # 一次送入模型的最大bsz,防止oom
input [{name: "input__0" # 输入名字,对于torch来说名字于代码的名字不需要对应,但必须是<name>__<index>的形式,注意是2个下划线,写错就报错data_type: TYPE_INT64 # 类型,torch.long对应的就是int64,不同语言的tensor类型与triton类型的对应关系可以在官方文档找到dims: [ -1 ]  # -1 代表是可变维度,虽然输入是二维的,但是默认第一个是bsz,所以只需要写后面的维度就行(无法理解的操作,如果是[-1,-1]调用模型就报错)}
]
output [{name: "output__0" # 命名规范同输入data_type: TYPE_FP32dims: [ -1, -1, 4 ]},{name: "output__1"data_type: TYPE_FP32dims: [ -1, -1, 8 ]}
]

这个模型配置文件估计是整个triton最复杂的地方,上线模型的大部分工作估计都在写配置文件,我这边三言两语难以解释清楚,只能给大家简单介绍下,具体内容还请参考官方文档。注意配置文件不要放到版本号的目录里,放到模型目录里,也就是说config.pbtxt和版本号目录是平级的。

关于输入shape默认第一个是bsz的官方说明:

As discussed above, Triton assumes that batching occurs along the first dimension which is not listed in in the input or output tensor dims. However, for shape tensors, batching occurs at the first shape value. For the above example, an inference request must provide inputs with the following shapes.

2.7 创建容器并启动

执行命令:

docker run --rm -p8000:8000 -p8001:8001 -p8002:8002 \-v /home/triton/model_repository/:/models \nvcr.io/nvidia/tritonserver:22.04-py3 \tritonserver \--model-repository=/models 

如果你的系统有一个可用的gpu,那你可以加上如下参数 --gpus=1来让推理框架使用GPU加速,这个参数要放到run的后面。

2.8 测试接口

如果你按照我的教程一步一步走下去,那么肯定可以成功启动容器,下面我们可以写段代码测试下接口是否是通的,调用地址是:
http:\\localhost:8000/v2/models/{model_name}/versions/{version}/infer
测试代码如下:

import requestsif __name__ == "__main__":request_data = {"inputs": [{"name": "input__0","shape": [2, 3],"datatype": "INT64","data": [[1, 2, 3], [4,5,6]]}],"outputs": [{"name": "output__0"}, {"name": "output__1"}]}res = requests.post(url="http://localhost:8000/v2/models/fc_model_pt/versions/1/infer",json=request_data).json()print(res)// 这段代码使用 Python 的 requests 库向本地运行的服务器发送 POST 请求。服务器托管了一个模型,即 fc_model_pt 。POST 请求包含输入数据和所期望的输出数据格式,并打印服务器的响应结果。

执行代码后会得到相应的输出:

{'model_name': 'fc_model_pt','model_version': '1','outputs': [{'name': 'output__0','datatype': 'FP32','shape': [2, 3, 4],'data': [1.152763843536377, 1.1349767446517944, -0.6294105648994446, 0.8846281170845032, 0.059508904814720154, -0.06066855788230896, -1.497096061706543, -1.192716121673584, 0.7339693307876587, 0.28189709782600403, 0.3425392210483551, 0.08894850313663483, 0.48277992010116577, 0.9581012725830078, 0.49371692538261414, -1.0144696235656738, -0.03292369842529297, 0.3465275764465332, -0.5444514751434326, -0.6578375697135925, 1.1234807968139648, 1.1258794069290161, -0.24797165393829346, 0.4530307352542877]}, {'name': 'output__1','datatype': 'FP32','shape': [2, 3, 8],'data': [-0.28994596004486084, 0.0626179575920105, -0.018645435571670532, -0.3376324474811554, -0.35003775358200073, 0.2884367108345032, -0.2418503761291504, -0.5449661016464233, -0.48939061164855957, -0.482677698135376, -0.27752232551574707, -0.26671940088272095, -0.2171783447265625, 0.025355860590934753, -0.3266356587409973, -0.06301657110452652, -0.1746724545955658, -0.23552510142326355, 0.10773542523384094, -0.4255935847759247, -0.47757795453071594, 0.4816707670688629, -0.16988427937030792, -0.35756853222846985, -0.06549499928951263, -0.04733048379421234, -0.035484105348587036, -0.4210450053215027, -0.07763291895389557, 0.2223128080368042, -0.23027443885803223, -0.4195460081100464, -0.21789231896400452, -0.19235755503177643, -0.16810789704322815, -0.34017443656921387, -0.05121977627277374, 0.08258339017629623, -0.2775516211986542, -0.27720844745635986, -0.25765007734298706, -0.014576494693756104, 0.0661710798740387, -0.38623639941215515, -0.45036202669143677, 0.3960753381252289, -0.20757021009922028, -0.511818528175354]}]
}

不知道是不是我的用法有问题,就使用体验来看,这个推理接口有些让我不适应:

  1. 明明在config.pbtxt里指定了datatype,但是输入的时候还需要指定,不指定就会报错
  2. 输入的shape也需要指定,那不然也会报错
  3. datatype的值和config.pbtxt里不统一,如果datatype设为TYPE_INT64,则会报错,必须为INT64
  4. 输出里的data是个1维数组,需要根据返回的shape自动reshape成对应的array

除了像我这样直接写代码调用,还可以使用他们提供的官方库pip install tritonclient[http],地址如下:https://github.com/triton-inference-server/client。

3 使用triton的高级特性

上一小节的教程只是用到了triton的基本功能,所以段位只能说是个黄金,下面介绍下一些triton的高级特性。

3.1 模型并行

模型并行可以指同时启动多个模型或单个模型的多个实例。实现起来并不复杂,通过修改配置参数就可以。在默认情况下,triton会在每个可用的gpu上都部署一个该模型的实例从而实现并行。
接下来我会对多种情况进行测试,以让你们清楚模型并行所带来的效果,本人的配置是2块3060(租的)用于测试多模型的情况。
压测命令使用ab -k -c 5 -n 500 -p ipt.json http://localhost:8000/v2/models/fc_model_pt/versions/1/infer
这条命令的意思是5个进程反复调用接口共500次。
测试配置及对应的QPS如下:

  • 共1个卡;每个卡运行1个实例:QPS为603
  • 共2个卡;每个卡运行1个实例:QPS为1115
  • 共2个卡;每个卡运行2个实例:QPS为1453
  • 共2个卡;每个卡运行2个实例;同时在CPU上放2个实例:QPS为972

结论如下:多卡性能有提升;多个实例能进一步提升并发能力;加入CPU会拖累速度,主要是因为CPU速度太慢。

下面是上述测试对应的配置项,直接复制了放到config.pbtxt中就行

#共2个卡;每个卡运行2个实例
instance_group [
{count: 2kind: KIND_GPUgpus: [ 0 ]
},
{count: 2kind: KIND_GPUgpus: [ 1 ]
}
]
# 共2个卡;每个卡运行2个实例;同时在CPU上放2个实例
instance_group [
{count: 2kind: KIND_GPUgpus: [ 0 ]
},
{count: 2kind: KIND_GPUgpus: [ 1 ]
},
{count: 2kind: KIND_CPU
}
]

至于选择使用几张卡,则通过创建容器时的--gpus来指定

3.2 动态batch

动态batch的意思是指, 对于一个请求,先不进行推理,等个几毫秒,把这几毫秒的所有请求拼接成一个batch进行推理,这样可以充分利用硬件,提升并行能力,当然缺点就是个别用户等待时间变长,不适合低频次请求的场景。使用动态batch很简单,只需要在config.pbtxt加上 dynamic_batching { },具体参数细节大家可以去看文档,我的这种简单写法,组成的bsz上限就是max_batch_size,本人压测的结果是约有50%QPS提升,反正就是有效果就对了。

PS:这种优化方式对于压测来说简直就是作弊。。。

3.3 自定义backend

所谓自定义backend就是自己写推理过程,正常情况下整个推理过程是通过模型直接解决的,但是有一些推理过程还会包含一些业务逻辑,比如:整个推理过程需要2个模型,其中要对第一个模型的输出结果做一些逻辑判断然后修改输出才能作为第二个模型的输入,最简单的做法就是我们调用两次triton服务,先调用第一个模型获取输出然后进行业务逻辑判断和修改,然后再调用第二个模型。不过在triton中我们可以自定义一个backend把整个调用过程写在里面,这样就简化调用过程,同时也避免了一部分http传输时延。
我举的例子其实是一个包含了业务逻辑的pipline,这种做法NV叫做BLS(Business Logic Scripting)。

要实现自定义的backend也很简单,与上文讲的放torch模型流程基本一样,首先建立模型文件夹,然后在文件夹里新建config.pbtxt,然后新建版本文件夹,然后放入model.py,这个py文件里就写了推理过程。为了说明目录结构,我把构建好的模型仓库目录树打印出来展示一下:

model_repository/  # 模型仓库|-- custom_model # 我们的自定义backend模型目录|   |-- 1 # 版本|   |   |-- model.py # 模型Py文件,里面主要是推理的逻辑|   `-- config.pbtxt # 配置文件`-- fc_model_pt # 上一小节介绍的模型|-- 1|   `-- model.pt`-- config.pbtxt

如果上一小节你看明白了,那么你就会发现自定义backend的模型目录设置和正常目录设置是一样的,唯一不同的就是模型文件由网络权重变成了自己写的代码而已。下面说下config.pbtxt和model.py的文件内容,大家可以直接复制粘贴:

import json
import numpy as np
import triton_python_backend_utils as pb_utilsclass TritonPythonModel:"""Your Python model must use the same class name. Every Python modelthat is created must have "TritonPythonModel" as the class name."""def initialize(self, args):"""`initialize` is called only once when the model is being loaded.Implementing `initialize` function is optional. This function allowsthe model to intialize any state associated with this model.Parameters----------args : dictBoth keys and values are strings. The dictionary keys and values are:* model_config: A JSON string containing the model configuration* model_instance_kind: A string containing model instance kind* model_instance_device_id: A string containing model instance device ID* model_repository: Model repository path* model_version: Model version* model_name: Model name"""# You must parse model_config. JSON string is not parsed hereself.model_config = model_config = json.loads(args['model_config'])# Get output__0 configurationoutput0_config = pb_utils.get_output_config_by_name(model_config, "output__0")# Get output__1 configurationoutput1_config = pb_utils.get_output_config_by_name(model_config, "output__1")# Convert Triton types to numpy typesself.output0_dtype = pb_utils.triton_string_to_numpy(output0_config['data_type'])self.output1_dtype = pb_utils.triton_string_to_numpy(output1_config['data_type'])def execute(self, requests):"""requests : listA list of pb_utils.InferenceRequestReturns-------listA list of pb_utils.InferenceResponse. The length of this list mustbe the same as `requests`"""output0_dtype = self.output0_dtypeoutput1_dtype = self.output1_dtyperesponses = []# Every Python backend must iterate over everyone of the requests# and create a pb_utils.InferenceResponse for each of them.for request in requests:# 获取请求数据in_0 = pb_utils.get_input_tensor_by_name(request, "input__0")# 第一个输出结果自己随便造一个假的,就假装是有逻辑了out_0 = np.array([1, 2, 3, 4, 5, 6, 7, 8])  # 为演示方便先写死out_tensor_0 = pb_utils.Tensor("output__0", out_0.astype(output0_dtype))# 第二个输出结果调用fc_model_pt获取结果inference_request = pb_utils.InferenceRequest(model_name='fc_model_pt',requested_output_names=['output__0', 'output__1'],inputs=[in_0])inference_response = inference_request.exec()out_tensor_1 = pb_utils.get_output_tensor_by_name(inference_response, 'output__1')inference_response = pb_utils.InferenceResponse(output_tensors=[out_tensor_0, out_tensor_1])responses.append(inference_response)return responsesdef finalize(self):"""`finalize` is called only once when the model is being unloaded.Implementing `finalize` function is OPTIONAL. This function allowsthe model to perform any necessary clean ups before exit."""print('Cleaning up...')// 这段代码是一个 Python 版本的 Triton 后端模型的基本框架。模型的 initialize 方法在加载模型时被调用一次,execute 方法在收到推理请求时被调用,并返回服务器的响应,finalize 在模型被卸载时调用,可选的实现清理工作。

config.pbtxt的文件内容:

name: "custom_model"
backend: "python"
input [{name: "input__0"data_type: TYPE_INT64dims: [ -1, -1 ]}
]
output [{name: "output__0" data_type: TYPE_FP32dims: [ -1, -1, 4 ]},{name: "output__1"data_type: TYPE_FP32dims: [ -1, -1, 8 ]}
]

待上述工作都完成后,就可以启动程序查看运行结果了,大家可以直接复制我的代码进行测试:

import requestsif __name__ == "__main__":request_data = {"inputs": [{"name": "input__0","shape": [1, 2],"datatype": "INT64","data": [[1, 2]]}],"outputs": [{"name": "output__0"}, {"name": "output__1"}]}res = requests.post(url="http://localhost:8000/v2/models/fc_model_pt/versions/1/infer",json=request_data).json()print(res)res = requests.post(url="http://localhost:8000/v2/models/custom_model/versions/1/infer",json=request_data).json()print(res)// 这段代码使用 Python 的requests库向本地运行的服务器发送 POST 请求。服务器托管了两个模型,一个是fc_model_pt,另一个是custom_model。POST 请求包含输入数据和期望的输出数据格式。然后,将服务器的响应结果打印出来。

运行结果可以发现2次的输出中,ouput__0是不一样,而output__1是一样的,这和咱们写的model.py的逻辑有关系,我这里就不多解释了。

PS:自定义backend避免了需要反复调用NLG模型进行生成而产生的传输时延

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/20436.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vscode 无法导入自己写的模块文件(.py)问题

问题主要是在 vscode中 python 的读入模块路径存在问题&#xff0c;下面先介绍下python的模块读入路径&#xff1a; 什么是PYTHONPATH? PYTHONPATH是一个环境变量&#xff0c;用于指定Python解释器在导入模块时搜索模块的路径。当我们导入一个模块时&#xff0c;Python解释器…

Leetcode 268. Missing Number

Problem Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array. Algorithm Sum all the numbers as x x x and use n ( n 1 ) 2 − x \frac{n(n1)}{2} - x 2n(n1)​−x. Code …

k8s概念-secret

回到目录 k8s secrets用于存储和管理一些敏感数据&#xff0c;比如密码&#xff0c;token&#xff0c;密钥等敏感信息。它把 Pod 想要访问的加密数据存放到 Etcd 中。然后用户就可以通过在 Pod 的容器里挂载 Volume 的方式或者环境变量的方式访问到这些 Secret 里保存的信息了…

npm,yarn,pnpm

原理 npm、yarn和pnpm都是用于管理Node.js项目依赖的包管理工具&#xff0c;下面对它们进行详细讲解&#xff1a; npm&#xff08;Node Package Manager&#xff09;&#xff1a; npm是Node.js的默认包管理工具&#xff0c;也是最早被广泛使用的。npm使用package.json文件来管…

【多模态】21、BARON | 通过引入大量 regions 来提升模型开放词汇目标检测能力(CVPR2021)

文章目录 一、背景二、方法2.1 主要过程2.2 Forming Bag of Regions2.3 Representing Bag of Regions2.4 Aligning bag of regions 三、效果 论文&#xff1a;Aligning Bag of Regions for Open-Vocabulary Object Detection 代码&#xff1a;https://github.com/wusize/ovdet…

pytorch(续周报(1))

文章目录 2.1 张量2.1.1 简介2.1.2 创建tensor2.1.3 张量的操作2.1.4 广播机制 2.2 自动求导Autograd简介2.2.1 梯度 2.3 并行计算简介2.3.1 为什么要做并行计算2.3.2 为什么需要CUDA2.3.3 常见的并行的方法&#xff1a;网络结构分布到不同的设备中(Network partitioning)同一层…

微服务系列<3>---微服务的调用组件 rpc 远程调用

什么是rpc调用,让我们调用远程方法就像调用本地方法一样 这就属于rpc调用 rpc是针对于本地来说的 调用远程方法根调用本地方法一样 如果能达到这种效果 就是rpc调用如果达到一种效果 调用远程和调用本地一样 他就是一种rpc框架2个微服务 之间发的调用 我们之前通过ribbon的方式…

springboot访问请求404的原因

是记录&#xff0c;可能出现错误 可能出现的原因 1.你请求的URL路径不对,比如说你请求的路径是/usr/list,GET方法,但是你UserController上面的RequestMapping是这个样子:RequestMapping(“user”)&#xff0c;有可能哈 2.前端的请求时GET方法&#xff0c;后端对应的处理函数的方…

【Linux命令200例】whereis用于搜索以及定位二进制文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜…

IDA+Frida分析CTF样本和Frid源码和objection模块

文章目录 一些资料IDA调试命令IDA调试安卓的10个技巧objection基本使用 Wallbreaker1frida源码阅读之frida-java 第一个实例EasyJNI第二个实例objection资料 art_trace2.pyart_trace2.js IDAFrida分析CTF样本和Frid源码和objection模块 一些资料 IDA调试命令 adb devices adb…

C#设计模式之---抽象工厂模式

抽象工厂模式&#xff08;Abstract Factory&#xff09; 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。工厂方…

Python 批量处理JSON文件,替换某个值

Python 批量处理JSON文件&#xff0c;替换某个值 直接上代码&#xff0c;替换key TranCode的值 New 为 Update。输出 cancel忽略 import json import os import iopath D:\\Asics\\850\\202307 # old path2 D:\\test2 # new dirs os.listdir(path) num_flag 0 for file…

Python3 高级教程 | Python3 正则表达式(一)

目录 一、Python3 正则表达式 &#xff08;一&#xff09;re.match函数 &#xff08;二&#xff09;re.search方法 &#xff08;三&#xff09;re.match与re.search的区别 二、检索和替换 &#xff08;一&#xff09;repl 参数是一个函数 &#xff08;二&#xff09;comp…

C++ 矩形切割

【问题描述】 小明有一些矩形的材料&#xff0c;他要从这些矩形材料中切割出一些正方形。 当他面对一块矩形材料时&#xff0c;他总是从中间切割一刀&#xff0c;切出一块最大的正方 形&#xff0c;剩下一块矩形&#xff0c;然后再切割剩下的矩形材料&#xff0c;直到全部切为正…

Curve深陷安全事件,OKLink如何破局

出品&#xff5c;欧科云链研究院 作者&#xff5c;Matthew Lee 7月31号&#xff0c;Curve 在平台表示 Vyper 0.2.15 的稳定币池由于编译器的漏洞所以遭到攻击。具体因为重入锁功能的失效&#xff0c;所以黑客可以轻易发动重入攻击&#xff0c;即允许攻击者在单次交易中执行某…

二、搜索与图论6:Dijkstra 模板题+算法模板(Dijkstra求最短路 I, Dijkstra求最短路 II,1003 Emergency)

文章目录 算法模板Dijkstra题目代码模板朴素dijkstra算法堆优化版dijkstra 树与图的存储(1) 邻接矩阵&#xff1a;(2) 邻接表&#xff1a;关于e[],ne[],h[]的理解 关于堆的原理与操作 模板题Dijkstra求最短路 I原题链接题目思路题解 Dijkstra求最短路 II原题链接题目思路题解 1…

05|Oracle学习(UNIQUE约束)

1. UNIQUE约束介绍 也叫&#xff1a;唯一键约束&#xff0c;用于限定数据表中字段值的唯一性。 1.1 UNIQUE和primary key区别&#xff1a; 主键/联合主键每张表中只有一个。UNIQUE约束可以在一张表中&#xff0c;多个字段中存在。例如&#xff1a;学生的电话、身份证号都是…

AWS——01篇(AWS入门 以及 AWS之EC2实例及简单实用)

AWS——01篇&#xff08;AWS入门 以及 AWS之EC2实例及简单实用&#xff09; 1. 前言2. 创建AWS账户3. EC23.1 启动 EC2 新实例3.1.1 入口3.1.2 设置名称 选择服务3.1.3 创建密钥对3.1.4 网络设置——安全组3.1.4.1 初始设置3.1.4.2 添加安全组规则&#xff08;开放新端口&…

ELKELFK 日志分析系统

目录 一、ELK 概述 二、为什么要使用 ELK 三、完整日志系统基本特征 四、ELK 的工作原理&#xff1a; 五、ELK Elasticsearch 集群部署&#xff08;在Node1、Node2节点上操作&#xff09; 1&#xff0e;环境准备 2&#xff0e;部署 Elasticsearch 软件 3&#xff0e;安装 …

尝试多数据表 sqlite

C 唯一值得骄傲的地方就是 通过指针来回寻址 &#x1f602; 提高使用的灵活性 小脚本buff 加成