问题描述
在任务处理系统中,发现同一个任务会被多个worker并发处理,导致以下问题:
- 同一个任务在不同worker中重复处理
- 资源浪费和冲突,尤其是在Docker镜像构建过程中
- 并发写入和操作文件系统导致的错误
解决方案
实现完整的任务锁定机制,确保同一时间只有一个worker能处理特定的任务:
任务状态扩展:添加了新的”processing”状态,表示任务已被锁定但尚未正式开始处理
// internal/task/types.goconst ( TaskStatusPending TaskStatus = "pending" TaskStatusProcessing TaskStatus = "processing" // 正在被锁定处理中,但还未正式运行 TaskStatusRunning TaskStatus = "running" TaskStatusComplete TaskStatus = "complete" TaskStatusFailed TaskStatus = "failed" TaskStatusCancelled TaskStatus = "cancelled" TaskStatusCompleted TaskStatus = "completed")
任务锁定接口:扩展TaskQueue接口,添加TryLock和Unlock方法
// internal/task/queue.gotype TaskQueue interface { // ... TryLock(ctx context.Context, id string) (bool, error) Unlock(ctx context.Context, id string) error // ...}
// internal/task/queue.gofunc (q *MemoryTaskQueue) TryLock(ctx context.Context, id string) (bool, error) { q.mu.Lock() defer q.mu.Unlock() task, exists := q.tasks[id] if !exists { return false, ErrTaskNotFound } if task.GetStatus() != string(TaskStatusPending) { return false, nil } task.SetStatus(string(TaskStatusProcessing)) q.tasks[id] = task return true, q.saveTask(task)}func (q *MemoryTaskQueue) Unlock(ctx context.Context, id string) error { q.mu.Lock() defer q.mu.Unlock() task, exists := q.tasks[id] if !exists { return ErrTaskNotFound } if task.GetStatus() != string(TaskStatusProcessing) { return nil } task.SetStatus(string(TaskStatusPending)) q.tasks[id] = task return q.saveTask(task)}
任务查重机制:在创建任务前检查是否有相同任务正在处理
// internal/task/queue.gofunc (q *MemoryTaskQueue) FindPending(ctx context.Context, name, tag string) ([]Task, error) { q.mu.RLock() defer q.mu.RUnlock() tasks := make([]Task, 0) for _, task := range q.tasks { status := task.GetStatus() if status == string(TaskStatusPending) || status == string(TaskStatusProcessing) || status == string(TaskStatusRunning) { if task.GetType() == string(TaskTypeBuildImage) { if strings.Contains(task.GetName(), name) { taskTag := task.GetTag() if taskTag == tag || taskTag == "" { tasks = append(tasks, task) } } } } } return tasks, nil}
// internal/task/processor.go// 创建任务前查重existingTasks, err := p.queue.FindPending(ctx, req.Name, req.Tag)if err == nil && len(existingTasks) > 0 { // 返回已存在任务 for _, t := range existingTasks { if buildTask, ok := t.(*BuildImageTaskV3); ok { return buildTask, nil } }}
Worker流程优化:修改worker处理逻辑,添加锁定和解锁步骤
// internal/task/processor.gofunc (p *TaskProcessor) worker(ctx context.Context, id int) { for { // ... task, err := p.queue.Dequeue(ctx) if task == nil { continue } locked, err := p.queue.TryLock(ctx, task.GetID()) if !locked { continue } taskID := task.GetID() defer func() { unlockErr := p.queue.Unlock(ctx, taskID) // 错误处理 }() p.processTask(ctx, task) }}
具体实现
Task接口扩展:
- 添加SetStatus方法用于更新任务状态
- 添加GetTag方法用于获取任务特定标签,便于任务查重
// internal/task/types.gotype Task interface { // ... SetStatus(status string) GetTag() string}
TaskQueue接口扩展:
- TryLock:尝试锁定任务,成功返回true
- Unlock:解锁任务
- FindPending:查找具有相同名称和标签的未完成任务
见上方接口定义和实现。
Worker处理流程:
- 出队任务后先尝试锁定
- 锁定成功才处理,失败则跳过
- 使用defer确保总是解锁任务
- 处理任务状态流转:pending -> processing -> running -> completed/failed
// internal/task/processor.gofunc (p *TaskProcessor) processTask(ctx context.Context, task Task) error { // ... task.SetStatus(string(TaskStatusRunning)) if err := p.queue.Update(ctx, task); err != nil { ... } // 任务处理逻辑 // 失败时SetError,成功时SetResult}
任务创建机制优化:
- 创建任务前先查找是否有同名同标签的任务在处理
- 有则返回已存在任务,避免创建重复任务
- 提升任务使用效率
见上方查重代码。
测试验证
该方案解决了任务被多次并发处理的问题,显著提升了系统稳定性和资源使用效率。测试表明:
- 不再出现同一任务被多个worker处理的情况
- 任务队列处理效率提高
- Docker镜像构建过程更加稳定可靠
环境准备
主机名 | IP地址 | 主机 |
---|---|---|
node | 192.168.100.50 | CentOS7.9.2009 |
环境部署
关闭防火墙与Selinux
systemctl disable --now firewalldsetenforce 0sed -i 's/SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
检查是否支持虚拟化
egrep -o 'vmx|svm' /proc/cpuinfo
安装KVM、QEMU、libvirt
yum install -y qemu-kvm libvirt libvirt-python libguestfs-tools virt-install virt-manager
启动libvirt
systemctl start libvirtdsystemctl enable libvirtd
加载KVM内核
modprobe kvmegrep -o 'vmx|svm' /proc/cpuinfomodprobe kvm_intel # 如果是 vmx执行modprobe kvm_amd # 如果是 svm执行
验证KVM内核加载
lsmod | grep kvm
验证虚拟化环境
virsh list --all
KVM的存储管理
虚拟磁盘管理
使用qemu-img创建一个虚拟磁盘
qemu-img create -f qcow2 mydisk.qcow2 10G
验证
ls | grep mydisk.qcow2qemu-img info mydisk.qcow2
增量磁盘管理
增量磁盘是基于目标磁盘进行复制
的一块磁盘,所有基础信息基于基础磁盘,新增数据写入增量磁盘中,可以将基础磁盘理解为模版机
,通常用于单个镜像批量启动虚拟机。
qemu-img create -f qcow2 -b mydisk.qcow2 incrementaldisk.qcow2
创建增量磁盘
qemu-img create -f qcow2 -b mydisk.qcow2 incrementaldisk.qcow2
验证
qemu-img info incrementaldisk.qcow2
磁盘格式转换
qemu-img convert -f qcow2 -O raw /var/lib/libvirt/images/mydisk.qcow2 /var/lib/libvirt/images/mydisk.raw
磁盘大小压缩
qemu-img convert -O qcow2 -c /var/lib/libvirt/images/mydisk.qcow2 /var/lib/libvirt/images/mydisk_compressed.qcow2
磁盘差异对比
qemu-img compare /var/lib/libvirt/images/mydisk.qcow2 /var/lib/libvirt/images/mydisk_compressed.qcow2
存储池管理
创建存储池
mkdir /var/lib/libvirt/images/mypoolvirsh pool-define-as mypool dir - - - - "/var/lib/libvirt/images/mypool"virsh pool-start mypoolvirsh pool-autostart mypool
待更新,这里有点抽象,等有空再接着研究了
]]>KubeEdge分为云端(cloud)和边缘端(edge)
云端:docker,k8s集群,
边缘端(不要安装K8S):docker,
cloud端安装
(cloud端负责编译KubeEdge的相关组件与运行cloudcore)
准备需要的安装包
因为github上下载慢(只有几KB的速度)提前下载好以下文件
https://github.com/kubeedge/kubeedge/releases/download/v1.12.1/kubeedge-v1.12.1-linux-amd64.tar.gz
https://github.com/kubeedge/kubeedge/releases/download/v1.12.1/keadm-v1.12.1-linux-amd64.tar.gz
下载好的安装包上传到/etc/kubeedge
在/etc/kubeedge下操作
mkdir /etc/kubeedge/cd /etc/kubeedge/tar -zxvf keadm-v1.12.1-linux-amd64.tar.gzcp keadm-v1.12.1-linux-amd64/keadm/keadm /usr/local/bin/keadm
设置云端(KubeEdge 主节点)keadm init --advertise-address="IP" --profile version=v1.12.1 --kube-config=/root/.kube/config
输出:
Kubernetes version verification passed, KubeEdge installation will start...CLOUDCORE started=========CHART DETAILS=======NAME: cloudcoreLAST DEPLOYED: Wed Oct 26 11:10:04 2022NAMESPACE: kubeedgeSTATUS: deployedREVISION: 1kubectl get all -n kubeedge
设置边缘端(KubeEdge 工作节点)
在云端运行将返回令牌,该令牌将在加入边缘节点时使用。
keadm gettoken
加入边缘节点
在/etc/kubeedge下操作
mkdir /etc/kubeedge/cd /etc/kubeedge/tar -zxvf keadm-v1.12.1-linux-amd64.tar.gzcp keadm-v1.12.1-linux-amd64/keadm/keadm /usr/local/bin/keadmkeadm join --cloudcore-ipport="cloud端IP":10000 --token=27a37ef16159f7d3be8fae95d588b79b3adaaf92727b72659eb89758c66ffda2.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTAyMTYwNzd9.JBj8LLYWXwbbvHKffJBpPd5CyxqapRQYDIXtFZErgYE --kubeedge-version=v1.10.1
边缘端在启动edgecore后,会与云端的cloudcore进行通信,K8s进而会将边缘端作为一个node纳入K8s的管控。
kubectl get node
[root@k8s-master-node1 kubeedge]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master-node1 Ready control-plane,master,worker 40h v1.22.1
k8s-worker-node1 Ready worker 40h v1.22.1
node-1.novalocal Ready agent,edge 8s v1.22.6-kubeedge-v1.12.1
节点规划
IP地址 | 主机名 | 服务 |
---|---|---|
192.168.100.10 | ansible | ansible |
192.168.100.20 | elk-1 | ela+kibana |
192.168.100.21 | elk-2 | ela+logstash |
192.168.100.22 | elk-3 | ela |
设置解析域名(所有节点)
cat >> /etc/hosts <<EOF192.168.100.10 ansible192.168.100.20 elk-1 192.168.100.21 elk-2 192.168.100.22 elk-3 EOF
设置主机名(所有节点)
#ansible节点--192.168.100.10:hostnamectl set-hostname#elk1节点--192.168.100.21:hostnamectl set-hostname elk-1#elk2节点--192.168.100.22:hostnamectl set-hostname elk-2#elk3节点--192.168.100.23:hostnamectl set-hostname elk-3
创建目录结构(Ansible)
mkdir /root/install_elktouch /root/install_elk/install_elk.yamlmkdir -p /root/install_elk/roles/{ela,kib,log}/{files,handlers,tasks,templates,vars}##下载所需要的软件包curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.0-linux-x86_64.tar.gzcurl -O https://artifacts.elastic.co/downloads/kibana/kibana-7.17.0-linux-x86_64.tar.gzcurl -O https://artifacts.elastic.co/downloads/logstash/logstash-7.17.0-linux-x86_64.tar.gz##复制所需要的软件包cp -rvf elasticsearcg-7.17.0-linux-x86_64.tar.gz /root/install_elk/ela/files/cp -rvf jdk-8u144-linux-x64.tar.gz /root/install_elk/ela/files/cp -rvf kibana-7.17.0-linux-x86.64.tar.gz /root/install_elk/kib/files/cp -rvf logstash-7.17.0-linux-x86_64.tar.gz /root/install_elk/kib/files/
ssh-keygen##执行ssh-keygen不用输入内容,按几下回车ssh-copy-id elk-1ssh-copy-id elk-2ssh-copy-id elk-3cat >> /etc/ansible/hosts <<EOF[ela]elk-1 node_name=elk-1elk-2 node_name=elk-2elk-3 node_name=elk-3[kib]elk-1[log]elk-2EOF
编写ELK剧本(Ansible)
配置ELA模板
cat > /root/install_elk/install_elk.yaml <<EOF---- hosts: ela gather_facts: no remote_user: root roles: - ela- hosts: kib gather_facts: no remote_user: root roles: - kib - hosts: log gather_facts: no remote_user: root roles: - logEOF
cat > /root/install_elk/roles/ela/tasks/main.yaml <<EOF- name: 安装Java环境 unarchive: src: jdk-8u144-linux-x64.tar.gz dest: /opt list_files: yes- name: 添加Java环境变量 shell: echo 'export JAVA_HOME=/opt/jdk1.8.0_144' >> /etc/profile && echo 'export PATH=$PATH:/opt/jdk1.8.0_144/bin' >> /etc/profile- name: 生效环境变量 shell: source /etc/profile- name: 创建用户 user: name: ela- name: 传输本地软件包到远程主机并且解压到指定目录 unarchive: src: elasticsearch-7.17.0-linux-x86_64.tar.gz dest: /opt owner: ela group: ela list_files: yes register: ret- name: 创建软链接 file: src: /opt/{{ ret.files.0 | regex_replace('/.*') }} dest: /opt/elasticsearch state: link- name: 传输配置文件 template: src: elasticsearch.j2 dest: /opt/elasticsearch/config/elasticsearch.yml owner: ela group: ela - name: 传输系统配置文件 copy: src: limits.conf dest: /etc/security/limits.conf- name: 传输系统配置文件 copy: src: sysctl.conf dest: /etc/sysctl.conf- name: 加载 /etc/sysctl.conf文件,使内核参数生效 shell: sysctl -p- name: 启动服务 # 使用ela用户执行此命令 become: yes become_user: ela command: # argv 是一个列表,存放了需要执行的命令及其参数 # 一行一个 argv: - nohup - /opt/elasticsearch/bin/elasticsearch - -dEOF
设置j2主配置文件模板
cat > /root/install_elk/roles/ela/templates/elasticsearch.j2 <<EOFcluster.name: elknode.name: {{ node_name }}node.data: truenetwork.host: 0.0.0.0http.port: 9200discovery.seed_hosts: - 192.168.100.20 - 192.168.100.21 - 192.168.100.22cluster.initial_master_nodes: ["elk-1","elk-2","elk-3"]EOF
设置句柄
cat > /root/install_elk/roles/ela/files/limits.conf <<EOF* soft nofile 65535* hard nofile 65535* soft nproc 4096* hard nproc 4096EOF
配置KIB模板
cat > /root/install_elk/roles/kib/tasks/main.yaml <<EOF- name: 传输本地软件包到远程主机并且解压到指定目录 unarchive: src: kibana-7.17.0-linux-x86_64.tar.gz dest: /opt owner: ela group: ela list_files: yes register: ret- name: 创建软链接 file: src: /opt/{{ ret.files.0 | regex_replace('/.*') }} dest: /opt/kibana state: link- name: 创建日志与PID存放目录 shell: mkdir -p /var/log/kibana /run/kibana- name: 给如上目录设置权限 shell: chown -R ela:ela /var/log/kibana /run/kibana- name: 传输配置文件 copy: src: kibana.yml dest: /opt/kibana/config/kibana.yml - name: 传输服务管理文件 template: src: kibana.service.j2 dest: /etc/systemd/system/kibana.service- name: 启动服务 systemd: name: kibana state: started daemon_reload: yesEOF
设置程序配置文件
cat > /root/install_elk/roles/kib/files/kibana.yml <<EOFserver.port: 5601server.host: "0.0.0.0"elasticsearch.hosts: ["http://192.168.100.20:9200"]i18n.locale: "zh-CN"pid.file: /run/kibana/kibana.pidlogging.dest: /var/log/kibana/kibana.logEOF
设置程序管理模板
cat > /root/install_elk/roles/kib/templates/kibana.service.j2 <<EOF[Unit]Description=KibanaDocumentation=https://www.elastic.coAfter=network-online.targetWants=network-online.target[Service]Type=simpleUser=elaGroup=elaExecStart=/opt/kibana/bin/kibanaExecStop=/bin/pkill -F "/run/kibana/kibana.pid"Restart=on-failureRestartSec=3StartLimitBurst=3StartLimitInterval=60WorkingDirectory=/opt/kibanaStandardOutput=journalStandardError=inherit[Install]WantedBy=multi-user.targetEOF
配置LOG模板
cat > /root/install_elk/roles/log/tasks/main.yaml <<EOF- name: 创建日志目录 file: path: /var/log/logstash state: directory- name: 服务日志目录权限 shell: chown -R ela:ela /var/log/logstash- name: 传输本地软件包到远程主机并且解压到指定目录 unarchive: src: logstash-7.17.0-linux-x86_64.tar.gz dest: /opt list_files: yes register: ret- name: 创建软链接 file: src: /opt/{{ ret.files.0 | regex_replace('/.*') }} dest: /opt/logstash state: link- name: 传输配置文件 template: src: logstash.yml dest: /opt/logstash/config/logstash.yml- name: 传输管道配置文件 copy: src: logstash.conf dest: /opt/logstash/config/logstash-sample.conf - name: 传输系统服务文件 template: src: logstash.service.j2 dest: /etc/systemd/system/logstash.service- name: 启动 logstash systemd: name: logstash state: started daemon_reload: yes- name: restart logstash systemd: name: logstash state: restarted daemon_reload: yes#- name: 启动服务# become: yes# become_user: ela# shell: sh /opt/logstash/bin/logstash -f /opt/logstash/config/logstash-sample.conf EOF
设置程序配置文件
cat > /root/install_elk/roles/log/templates/logstash.yml <<EOFhttp.host: "0.0.0.0"path.logs: /var/log/logstash/EOF
设置管道配置文件
cat > /root/install_elk/roles/log/files/logstash.conf <<EOF#将本地的/var/log/yum.log内日志标准输入input { file { path => "/var/log/yum.log" type => "yum_log" start_position => "beginning" }}#标准输出到elasticsearch中output { elasticsearch { hosts => ["192.168.100.20:9200","192.168.100.21:9200","192.168.100.22:9200"] index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}" #user => "elastic" #password => "changeme" }}EOF
设置进程管理模板
cat > /root/install_elk/roles/log/templates/logstash.service.j2 <<EOFUnit]Description=logstash[Service]Type=simpleExecStart=/opt/logstash/bin/logstash "-f" "/opt/logstash/config/*.conf"Restart=alwaysWorkingDirectory=/LimitNOFILE=65535TimeoutStopSec=infinity[Install]WantedBy=multi-user.targetEOF
]]>主机名 | IP地址 | 配置 |
---|---|---|
master | 10.18.4.10 | 2H4G |
node | 10.18.4.20 | 2H4G |
用到的镜像:CentOS-7-x86_64-DVD-2009.iso、chinaskills_cloud_paas_v2.0.3.iso
安装K8S集群
挂载镜像并安装Kubeeasy
mount /dev/sr0 /mediacp -rvf /media/* /optcp /opt/kubeeasy-v2.0 /usr/binmv /usr/bin/kubeeasy-v2.0 /usr/bin/kubeeasychmod +x /usr/bin/kubeeasy
使用kubeeasy安装依赖包
kubeeasy install dependencies \--host 10.18.4.10,10.18.4.20 \--user root \--password 000000 \--offline-file /opt/dependencies/base-rpms.tar.gz
装完依赖再开始装k8s
kubeeasy install kubernetes \--master 10.18.4.10 \--worker 10.18.4.20 \--user root \--password 000000 \--version 1.22.1 \--offline-file /opt/kubernetes.tar.gz
测试搭建
1.访问masterIP:30080,用户名admin密码000000000000
打开网页面板不报错的话就是搭建完成了
2.输入kubectl get nodes
看得出已经部署正常了
输出结果:
[root@k8s-master-node1 ~]# kubectl get nodesNAME STATUS ROLES AGE VERSIONk8s-master-node1 Ready control-plane,master,worker 24h v1.22.1k8s-worker-node1 Ready worker 24h v1.22.1
安装kubevirt(可选)
kubeeasy add --virt kubevirt
安装harbor(可选)
kubeeasy add --registry harbor
安装istio(可选)
安装istio会同时安装prometheus+grafana+kiali等其他相关的Pod
kubeeasy add --istio istio
]]>在Linux操作系统中,进程管理是核心功能之一。进程是正在执行的程序实例,系统需要管理这些进程的创建、调度、终止以及资源分配。理解和掌握进程管理的理论知识对于系统管理和性能优化至关重要。
1. 进程的概念
1.1 进程的定义
- 进程:一个正在执行的程序实例,包括程序代码和当前活动的状态(寄存器、变量等)。
- 线程:进程中的一个执行单元,共享进程的资源,如内存空间、文件描述符等。
1.2 进程的状态
- 运行状态(Running):进程正在CPU上执行。
- 就绪状态(Ready):进程准备执行,但等待CPU调度。
- 阻塞状态(Blocked):进程等待某个事件(如I/O操作)完成。
- 终止状态(Terminated):进程已完成执行或被终止。
2. 进程的生命周期
2.1 进程的创建
进程可以通过以下方式创建:
- 系统初始化:系统引导时创建的进程,如
init
进程(PID 1)。 - 用户请求:用户通过命令行或GUI启动程序。
- 系统调用:通过
fork()
系统调用创建子进程,子进程可以通过exec()
系统调用执行新程序。
2.2 进程的终止
进程可以通过以下方式终止:
- 正常退出:进程完成任务后调用
exit()
退出。 - 异常终止:进程因错误或未处理的信号崩溃。
- 被其他进程终止:一个进程可以通过
kill
系统调用终止另一个进程。
3. 进程控制块(PCB)
每个进程在操作系统内核中都有一个数据结构称为进程控制块(PCB),包含了进程的所有信息。
- 进程ID(PID):进程的唯一标识符。
- 程序计数器:记录进程下一条要执行的指令。
- CPU寄存器:保存进程执行时的寄存器状态。
- 内存管理信息:进程的内存分配情况。
- 文件描述符表:进程打开的文件列表。
- 进程状态:进程当前的状态。
4. 进程调度
4.1 调度策略
Linux使用多种调度策略来管理进程的执行:
- SCHED_OTHER:默认的时间共享调度策略,为普通进程设计。
- SCHED_FIFO:实时调度策略,先入先出。
- SCHED_RR:实时调度策略,时间片轮转。
4.2 调度算法
- 时间片轮转(Round Robin):每个进程分配一个固定的时间片,时间片结束后切换到下一个进程。
- 优先级调度:进程根据优先级调度,高优先级的进程优先执行。
- 多级反馈队列:结合时间片轮转和优先级调度,动态调整进程的优先级。
5. 进程通信
进程间通信(IPC)是进程之间交换数据的手段,主要方式包括:
- 管道(Pipe):单向通信通道,常用于父子进程之间。
- 命名管道(FIFO):类似管道,但具有名称,可用于无亲缘关系的进程之间的通信。
- 信号(Signal):用于通知进程某个事件的发生。
- 共享内存:多个进程共享同一块内存区域,速度快。
- 消息队列:进程通过消息队列发送和接收消息,适用于复杂通信需求。
- 套接字(Socket):用于网络通信的IPC机制。
6. 常用的进程管理命令
6.1 查看进程
ps
:显示当前进程状态。top
:实时显示系统中进程的运行状态。htop
:增强版的top
,提供更友好的界面。
6.2 管理进程
kill
:向进程发送信号,如终止进程 (kill -9 PID
)。nice
:启动进程时设置优先级。renice
:改变正在运行进程的优先级。killall
:按名称终止进程。
6.3 查看进程树
pstree
:以树状图显示进程关系。
7. 实践操作
示例:创建并管理进程
- 创建一个进程
sleep 1000 &
- 查看进程
ps aux | grep sleep
- 调整进程优先级
renice +10 <sleep进程的PID>
- 终止进程
kill <sleep进程的PID>
]]>在 Linux 系统中,用户和组是管理文件和目录权限、进程和其他系统资源的关键概念
用户 (User)
定义:用户是 Linux 系统中的一个实体,每个用户都有一个唯一的用户名和用户 ID (UID)。用户可以登录系统、执行命令和访问文件。
类型
:
- 超级用户 (root):拥有系统中所有文件和资源的最高权限,可以执行任何操作。
- 普通用户:权限受限,只能访问自己的文件和系统中允许的资源。
用户管理命令
:
- 创建用户:
useradd [选项] 用户名
- 修改用户:
usermod [选项] 用户名
- 删除用户:
userdel [选项] 用户名
- 设置密码:
passwd 用户名
- 创建用户:
组 (Group)
定义:组是一组用户的集合,用于简化权限管理。每个用户可以属于一个或多个组,每个组都有一个唯一的组名和组 ID (GID)。
类型
:
- 主要组:用户的主要组,用于文件和目录的默认权限。
- 次要组:用户可以同时属于多个次要组,用于特定的权限管理。
组管理命令
:
- 创建组:
groupadd [选项] 组名
- 修改组:
groupmod [选项] 组名
- 删除组:
groupdel [选项] 组名
- 将用户添加到组:
usermod -a -G 组名 用户名
- 创建组:
Linux 用户与组与其他系统的不同
Linux 的用户和组管理与其他操作系统(如 Windows 和 macOS)有一些显著的不同:
1. 权限模型
- Linux:Linux 使用基于用户和组的权限模型,每个文件和目录都有所有者、组和其他用户的权限设置。权限分为读 (r)、写 (w) 和执行 (x)。
- Windows:Windows 使用基于访问控制列表 (ACL) 的权限模型,可以为每个用户和组设置详细的权限。
- macOS:macOS 基于 Unix,也使用类似 Linux 的用户和组权限模型,但同时支持 ACL。
2. 用户和组管理
- Linux:Linux 的用户和组管理主要通过命令行工具(如
useradd
、usermod
、groupadd
等)进行,也可以通过图形界面工具(如Users and Groups
)进行管理。 - Windows:Windows 的用户和组管理主要通过图形界面工具(如
Computer Management
)进行,也可以通过命令行工具(如net user
、net localgroup
)进行管理。 - macOS:macOS 的用户和组管理可以通过图形界面工具(如
System Preferences
)进行,也可以通过命令行工具(如dscl
)进行管理。
3. 超级用户权限
- Linux:Linux 的超级用户是
root
,拥有系统中所有文件和资源的最高权限。普通用户可以通过sudo
命令临时提升权限。 - Windows:Windows 的超级用户是
Administrator
,拥有系统中所有文件和资源的最高权限。普通用户可以通过Run as administrator
提升权限。 - macOS:macOS 的超级用户是
root
,但默认情况下是禁用的。普通用户可以通过sudo
命令临时提升权限。
4. 用户和组文件
- Linux:Linux 的用户和组信息存储在
/etc/passwd
和/etc/group
文件中。 - Windows:Windows 的用户和组信息存储在 Active Directory 或本地用户和组数据库中。
- macOS:macOS 的用户和组信息存储在
/etc/passwd
和/etc/group
文件中,同时也支持目录服务(如 Open Directory)。
总结
Linux 的用户和组管理基于 Unix 的传统,使用基于用户和组的权限模型,通过命令行工具进行管理。与其他操作系统相比,Linux 的用户和组管理更加灵活和强大,适用于需要精细权限控制的环境。通过理解这些概念和差异,用户可以更好地管理和保护 Linux 系统中的资源。
]]>权限类型
Linux 权限分为三种类型:
- 读取权限 (r):允许用户查看文件内容或目录列表。
- 写入权限 (w):允许用户修改文件内容或目录中的文件。
- 执行权限 (x):允许用户执行文件(如果文件是可执行程序)或进入目录。
权限对象
权限可以分配给三类对象:
- 所有者 (u):文件或目录的创建者。
- 组 (g):一组用户,可以共享文件或目录的权限。
- 其他用户 (o):系统中的所有其他用户。
权限表示法
权限通常以两种方式表示:
符号表示法:使用字母和符号来表示权限。
r
:读取权限w
:写入权限x
:执行权限-
:无权限
例如,
rwxr-xr--
表示所有者有读、写、执行权限,组用户有读、执行权限,其他用户只有读权限。八进制表示法:使用数字来表示权限。
4
:读取权限2
:写入权限1
:执行权限0
:无权限
例如,
755
表示所有者有读、写、执行权限,组用户和其他用户有读、执行权限。
修改权限
使用 chmod
命令可以修改文件或目录的权限。
- 符号模式:
chmod u+rwx,g+rx,o+r filename
这表示给所有者添加读、写、执行权限,给组用户添加读、执行权限,给其他用户添加读权限。
- 八进制模式:
chmod 755 filename
这表示设置所有者为读、写、执行权限,组用户和其他用户为读、执行权限。
修改所有者和组
使用 chown
和 chgrp
命令可以修改文件或目录的所有者和组。
- 修改所有者:
chown new_owner filename
- 修改组:
chgrp new_group filename
- 同时修改所有者和组:
chown new_owner:new_group filename
特殊权限
除了基本的读、写、执行权限外,Linux 还有三种特殊权限:
- SetUID (4):当应用于可执行文件时,允许用户以文件所有者的权限执行该文件。
chmod 4755 filename
- SetGID (2):当应用于可执行文件时,允许用户以文件所属组的权限执行该文件;当应用于目录时,新创建的文件将继承该目录的组。
chmod 2755 filename
- Sticky Bit (1):当应用于目录时,只有文件的所有者、目录的所有者或超级用户可以删除或重命名该目录中的文件。
chmod 1755 directory_name
查看权限
使用 ls -l
命令可以查看文件和目录的详细信息,包括权限。
ls -l
输出示例:
-rwxr-xr-- 1 user group 1234 Jan 1 12:34 filename
在这个示例中:
-rwxr-xr--
表示权限。1
表示链接数。user
表示所有者。group
表示组。1234
表示文件大小。Jan 1 12:34
表示最后修改时间。filename
表示文件名。
节点规划
主机名 | 第一张网卡(ens33)(NAT) | 第二张网卡(ens34)(仅主机) | 配置 |
---|---|---|---|
comtroller | 192.168.100.10 | 仅主机无需配置 | 4C8G_50G |
compute | 192.168.100.20 | 仅主机无需配置 | 4C8G_50+50+50+50G |
用到的镜像:CentOS-7-x86_64-DVD-1804.iso、chinaskills_cloud_iaas_v2.0.3.iso
一道云不同于旧版先电与国基北盛的镜像,多出来了几个服务,不过部署过程也大致相同,其中的要点已经标注出来
selinux与防火墙关闭
#关防火墙systemctl stop firewalld && systemctl disable firewalld#关selinuxsetenforce 0sed -i 's/=enforcing/=disabled/g' /etc/selinux/config
配置网络
vi /etc/sysconfig/network-scripts/ifcfg-ens33#这里主控IPADDR是192.168.100.10,计算节点改成192.168.100.20,两边都要执行BOOTPROTO="static"ONBOOT="yes"IPADDR="192.168.100.10" NETMASK="255.255.255.0"GATEWAY="192.168.100.2"DNS1="8.8.8.8"#重启网络systemctl restart network
绑定hosts
vi /etc/hosts192.168.100.10 controller192.168.100.20 compute
克隆虚拟机
使用VMWare克隆一下controller虚拟机然后虚拟机改名为compute,执行配网与设置主机名还有hosts
克隆完成后执行,注意那个发Hosts的命令是在controller里执行的:
#设置compute网络vi /etc/sysconfig/network-scripts/ifcfg-ens33按i进入编辑模式,把里面的192.168.100.10改成192.168.100.20,还有UUID那一行删除掉编辑完之后重启网络:systemctl restart network#设置compute主机名hostnamectl set-hostname compute#设置hosts#在controller执行以下命令发送hosts文件到compute节点上(注意是在controller节点执行):scp /etc/hosts root@compute:/etc/hosts
配置本地yum源与vsftpd共享源
controller执行
mkdir /opt/{centos,iaas}mount -o loop CentOS7-x86_64-DVD-1804.iso /mnt/cp -rvf /mnt/* /opt/centos/#确认上面复制完成之后执行以下解挂并拷贝一道云软件包umount /mnt/mount -o loop chinaskills_cloud_iaas.iso /mnt/cp -rvf /mnt/* /opt/iaas/#复制完成后执行以下解挂umount /mnt/
controller设置本地源:
cd /etc/yum.repos.d/mkdir backupmv CentOS-* backup/mv backup /home/vi /etc/yum.repos.d/local.repocat /etc/yum.repos.d/local.repo[centos]name=centosbaseurl=file:///opt/centosgpgcheck=0enabled=1[iaas]name=iaasbaseurl=file:///opt/iaas/iaas-repogpgcheck=0enabled=1#修改完文件保存之后更新源yum clean allyum makecache
controller配置vsftpd:
yum -y install vsftpdecho "anon_root=/opt" >> /etc/vsftpd/vsftpd.confsystemctl start vsftpd && systemctl enable vsftpdsystemctl status vsftpd
切换到compute执行
cd /etc/yum.repos.d/ && rm -rf *cat > ftp.repo << EOF[centos]name=centosbaseurl=ftp://controller/centosgpgcheck=0enabled=1[iaas]name=iaasbaseurl=ftp://controller/iaas/iaas-repogpgcheck=0enabled=1EOFyum clean allyum repolistyum makecache
controller和compute两个都要执行
yum install openstack-iaas -y
二、开始安装
磁盘分区
compute除了系统盘之外添加三块50G的盘,选SCSI模式
配置三块盘,cinder、swift、manila使用
磁盘名 | 卷名称 | 大小 |
---|---|---|
sdb1 | cinder | 50G |
sdb2 | swift | 30G |
sdb3 | manila | 29G |
#以上规划仅供参考,根据实际情况分盘,不是熟手的话建议操作前先打快照,千万不要把系统盘给格了#查看所有磁盘和分区fdisk -lecho "- - -" > /sys/class/scsi_host/host0/scanlsblkfdisk /dev/sdbn p enter enter +20G *3pwpartprobe /dev/sdb2选1parted -s /dev/sdb mklabel msdosparted /dev/sdb mkpart primary 0M 50Gparted /dev/sdb mkpart primary 51G 80Gparted /dev/sdb mkpart primary 81G 110Gparted /dev/sdb print
配置环境变量
在配置之前有几个需要注意的点:
这是配置要点不要直接扔进去了不然会报错,去掉所有参数开通的#号注释然后所有密码填入000000
#--------------------system Config--------------------##HOST_IP=192.168.100.10#上面这是controller的IPHOST_PASS=000000#上面这是controller的root密码HOST_NAME=controller#上面这是controller的主机名HOST_IP_NODE=192.168.100.20#上面这是计算节点的IPHOST_PASS_NODE=000000#上面这是计算节点的root密码HOST_NAME_NODE=compute#上面这是计算节点的主机名#--------------------Chrony Config-------------------##network_segment_IP=192.168.100.0/24#以上是两台机处于的网段#--------------------Rabbit Config ------------------##RABBIT_USER=openstack#以上默认openstack用户#--------------------Keystone Config------------------##DOMAIN_NAME=demo#以上是默认的域,默认demo#--------------------Neutron Config-------------------##INTERFACE_NAME=ens34#以上是仅主机的网卡名称Physical_NAME=providerminvlan=101maxvlan=200#以上是默认参数#--------------------Cinder Config--------------------##BLOCK_DISK=sdb1#以上是cinder占用的分区#--------------------Swift Config---------------------##OBJECT_DISK=sdb2#以上是swift占用的分区STORAGE_LOCAL_NET_IP=192.168.100.20#以上是计算节点compute的IP地址#--------------------Manila Config----------------##SHARE_DISK=sdb3#以上是manila占用的分区
controller执行
vi /etc/openstack/openrc.sh#至于怎么填吗,上面都有解析,看完就会了#填完发到compute计算节点上:scp /etc/openstack/openrc.sh root@compute:/etc/openstack/openrc.sh#然后主节点初始化(脚本不可逆,建议新手执行前先打快照):source /etc/openstack/openrc.shiaas-pre-host.sh#执行完毕之后退出登录:logout#然后重新登陆回来ssh
compute执行
#计算节点初始化(脚本不可逆,建议新手执行前先打快照):source /etc/openstack/openrc.shiaas-pre-host.sh#执行完毕之后退出登录:logout#然后重新登陆回来ssh
执行安装
controller执行
#从上到下按顺序执行,千万不能乱也不能漏iaas-install-mysql.sh iaas-install-keystone.shiaas-install-glance.shiaas-install-placement.shiaas-install-nova-controller.shiaas-install-neutron-controller.shiaas-install-dashboard.shiaas-install-cinder-controller.shiaas-install-swift-controller.shiaas-install-heat.shiaas-install-manila-controller.shiaas-install-cloudkitty.shiaas-install-barbican.sh
compute执行
#从上到下按顺序执行,千万不能乱也不能漏iaas-install-nova-compute.shiaas-install-neutron-compute.shiaas-install-cinder-compute.shiaas-install-swift-compute.shiaas-install-manila-compute.sh
执行完如果没问题的话,dashboard应该就可以打开了
地址:192.168.100.10/dashboard登录信息:域:demo用户名:admin密码:000000
]]>- node1 10.24.227.111
- node2 10.24.227.112
- node3 10.24.227.113
二.安装jdk环境
方法一:rpm安装:预先下载好 jdk-21_linux-x64_bin.rpm
将包拉进根目录,执行:rpm -ivh jdk-21_linux-x64_bin.rpm
方法二:yum install -y java-1.8.0-openjdk*
三.关闭防火墙
systemctl stop firewalld && systemctl disable firewalld### 关闭selinuxvi /etc/selinux/config模式改成permissivesetenforce 0
四.安装zookeeper
tar -zxvf apache-zookeeper-3.9.2-bin.tar.gz -C /usr/local/cd /usr/local/mv apache-zookeeper-3.9.2-bin zookeeper-3.9.2cd /usr/local/zookeeper-3.9.2/mkdir datacd conf/mv zoo_sample.cfg zoo.cfgvi zoo.cfg `# zookeeper 数据存储地址``dataDir=/usr/local/zookeeper-3.9.2/data``# zookeeper 集群地址``server.1=10.24.227.111:2888:3888``server.2=10.24.227.112:2888:3888``server.3=10.24.227.113:2888:3888``# admin.server端口``admin.serverPort=8888` scp -r zookeeper-3.9.2 node2:/usr/local/scp -r zookeeper-3.9.2 node3:/usr/local/
node1:
echo 1 > data/myid
node2:
echo 2 > data/myid
node3:
echo 3 > data/myid
启动zookeeper:
sh bin/zkServer.sh startsh bin/zkServer.sh status //注意,要启动三台才能看见它的运行状态,我们可以通过bin目录下面的zookeeper.out来查看问题原因,这上面说的是myid文件缺失,可能涉及到Leader(Master)选举的问题,需要先启动master,然后再启动follower。
安装Kafka
tar -zxvf kafka_2.12-3.6.0.tgz -C /usr/local/cd /usr/local/kafka_2.12-3.6.0/vi config/server.propertiesbroker.id=1 //不同节点id不同host.name=10.24.227.111listeners=PLAINTEXT://10.24.227.111:9092advertised.listeners=PLAINTEXT://10.24.227.111:9092zookeeper.connect=10.24.227.111:2181,10.24.227.112:2181,10.24.227.113:2181/kafkascp -r kafka_2.12-3.6.0 node2:/usr/local/scp -r kafka_2.12-3.6.0 node3:/usr/local/
启动Kafka
sh bin/kafka-server-start.sh -daemon config/server.properties
一共三个node节点,1leader,2follower集群成功,三个卡夫卡成功运行
]]>错误信息:
detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
解决方法:修改启动方式为systemd
1.修改docker的配置文件
vim打开/etc/docker/daemon.json文件,改成以下内容。
{ "registry-mirrors": ["https://x3nqjrcg.mirror.aliyuncs.com"], "exec-opts": ["native.cgroupdriver=systemd"]}
重启docker服务
systemctl daemon-reloadsystemctl restart docker
2.修改kubelet配置
新建/etc/systemd/system/kubelet.service.d/10-kubeadm.conf文件,并添加以下内容。
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --cgroup-driver=cgroupfs"
3.重启kubelet
systemctl daemon-reloadsystemctl restart kubelet
二、Node节点加入集群时卡住 “[preflight] Running pre-flight checks”
3个解决方法:
1.时间异常:同步NTP时间,node和master节点要时间正常
master和node执行以下
yum install ntpdatentpdate ntp1.aliyun.com;hwclock --systohcdate;hwclock
2.token过期失效:重新获取token和加入命令
node执行以下
kubeadm token create --ttl 0 --print-join-command
3.node未关闭防火墙
node和master执行以下关闭
systemctl disable firewalldsystemctl stop firewalldsystemctl status firewalld
三、node加入k8s集群报错this Docker version is not on the list of validated versions: 20.10.17. Latest validated…
详细报错:[preflight] Running pre-flight checks
[WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.17. Latest validated version: 18.09
查看源支持安装的版本列表:yum list docker-ce --showduplicates | sort -r
降级到1806版本(1809会报错):yum downgrade --setopt=obsoletes=0 -y docker-ce-18.09.9-3.el7 docker-ce-cli-18.09.9-3.el7 containerd.io
重启docker随后查看版本
systemctl start dockerdocker version
四、kubernetes-dashboard无证书,浏览器无法打开访问
生成个证书就好了
#Step 1: 新建目录:mkdir key && cd key#Step 2: 生成 SSL 证书openssl genrsa -out dashboard.key 2048#Step 3: 我这里写的自己的 node1 节点,因为我是通过 nodeport 访问的;如果通过 apiserver 访问,可以写成自己的 master 节点 ipopenssl req -new -out dashboard.csr -key dashboard.key -subj '/CN=192.168.9.143'openssl x509 -req -in dashboard.csr -signkey dashboard.key -out dashboard.crt#Step 4: 删除原有的证书 secretkubectl delete secret kubernetes-dashboard-certs -n kubernetes-dashboard#Step 5: 创建新的证书 secretkubectl create secret generic kubernetes-dashboard-certs --from-file=dashboard.key --from-file=dashboard.crt -n kubernetes-dashboard#Step 6: 查看 podkubectl get pod -n kubernetes-dashboard#Step 7: 重启 podkubectl delete pod kubernetes-dashboard-7b5bf5d559-gn4ls -n kubernetes-dashboard
获取token:kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep dashboard-admin | awk ‘{print $1}’)
]]>前几天在PDD刷到个透明探索版随身wifi充电宝,外观设计不错而且还是好价直接冲了。收货以后才发现这是MTK方案的机子,很冷门没有成熟的破解方法只能用卖家的内置卡充值上网切换不了外置卡槽插自己的卡,于是提取固件尝试从固件里入手
开始
拆机发现型号MF901Q,再往下深扒得知这和某一个4GCPE路由器是同一个代工厂生产的,按照CPE的方式尝试开启ADB调试
USB调试:http://192.168.0.1/adbenableForm.do?adb=1
无线调试:http://192.168.0.1/adbWifiDebugForm.do?adb=1
开启两个调试模式发现USB调试压根开不了,电脑USB连接设备不跳端口,无线调试开启倒是正常
连接无线调试查看设备APP列表找不到httpserver主进程,在内置存储目录里找到了这个ROOT.war,猜测可能是固件所在
拉取固件
adb pull /storage/emulated/0/lrserver/webapps/ROOT.war ROOT.war
通过常规zip方式解包,文件结构如下
分析
定位切卡所在页面的前端资源,找不到有关密码的选项
在仔细浏览固件内容后发现了lib文件夹里面的类库是httpserver核心所在,找到负责切卡表单处理的com/lr/web/SimSwitchEnableFrom.class
发现密码指向另一个类:com/lr/util/NetworkConfigUtil.class
smali拖入Jadx转Java代码,找到前者调用的方法定位,于是得出以下密码算法
1.获取设备的 IMEI 号码和两个系统属性值:ro.sim.passwd 和 ro.sim.ccsn。
2.如果 IMEI 不为空且长度大于等于 INDEX_LTE_GSM_WCDMA(已定义的一个常量),则对 IMEI 进行处理,提取后8位并与 ro.sim.ccsn 进行拼接。
3.将拼接后的字符串进行 MD5 加密为16位小写。
4.如果加密后的结果不为空且长度大于等于 INDEX_LTE_GSM_WCDMA,则返回加密结果的后8位;否则返回默认密码 ro.sim.passwd。
5.如果 IMEI 为空或长度不符合要求,则直接返回默认密码 ro.sim.passwd。
尝试
我的设备IMEI是890983065396943,截取后八位是65396943
CCSN的值不在代码里,而是作为一个系统属性存在,于是直接用ADB Shell读取:
adb shell getprop ro.sim.ccsn
执行得到CCSN明文:A0018,拼接字符串得出65396943A0018再转16位小写md5:33a7109f3ce9dc5b
取后八位,切卡密码最终结果:3ce9dc5b
把密码扔进SIM卡切换页面,成功切卡
用这个算法写了个密码计算器页面,更方便了
完结撒花
2024.08.02更新:
厂家找到了我,在新版产品修复了这个开ADB调试、切卡的漏洞并往卡槽里滴502胶水还更新了密码算法,而我手上也没有了新版的机子测试破解,此帖终结
博客接入CDN后设置了封顶策略但设置阈值还是太多了,当天晚上被人恶意刷流量达到封顶阈值断开接入后我收到了服务商的短信
随后登录控制台发现流量已经跑了55G,之前在群里经常看到被刷欠费的站长,没想到这回还是轮到我了
溯源
在停止服务后尝试一次重新接入但依旧被刷,于是断开并不再接入,从日志入手来获取详细的情况
调取日志,发现站点底下的某一图片被两个不同的IP连续访问,且User-agant为空值,实锤了。根据IP反查归属地与运营商得知此IP来自山西太原联通的ADSL拨号池,推测攻击来源于家用宽带或是黑机房提供的拨号VPS。
处置
先是在CDN控制面板把该山西太原的拨号池IP段拉黑,因为这是拨号动态IP,单独拉黑IP不会起一点作用。
然后加强CDN层的QPS限制,缩小流量封顶阈值,根据文件类型和权重适当限速,拉黑异常的User-agant请求头,屏蔽所有海外来源IP。
但在这还是不能从根源上解决问题,于是我把站内所有图片资源大小压缩一遍再把图片资源迁出服务器,使套了CDN的站点主机与图片资源节点分离。前端资源则引用字节跳动的公共资源库,原图片资源移至海外多节点轮询切换,而国内CDN只负责加速除了图片与CSSJS等的站点资源。
总结
截至本文发稿前,本站的加速服务已全部恢复。话说回来,CDN被刷的事早已屡见不鲜,少的扣费几十几百多则上千上万的都有。建议新手站长们在接入CDN时一定要仔细检查面板的访问控制策略,避免造成更大的损失。
当谈论互联网速度和性能时,CDN是一个不可或缺的元素。在当今数字化时代,人们对网站和应用程序的访问速度要求越来越高,而CDN作为一种优化网络性能的解决方案,扮演着至关重要的角色。通过将内容分发到全球各地的服务器,CDN可以显著减少用户访问网站或应用程序时的加载时间,提高用户体验,降低网络拥堵,减少延迟,从而实现快速、高效的内容交付。
CDN的起源可以追溯到20多年前,当时随着骨干网压力的增加和长传需求的增长,骨干网面临着日益加剧的压力和长传效果的恶化。1995年,麻省理工学院的应用数学教授Tom Leighton与研究生Danny Lewin以及其他顶尖研究人员合作,尝试利用数学问题解决网络拥堵难题。通过数学算法处理内容的动态路由安排,他们成功解决了困扰互联网用户的问题。随后,史隆管理学院的MBA学生Jonathan Seelig加入了Leighton团队,开始实施他们的商业计划。最终,于1998年8月20日,他们正式创立了公司,取名为Akamai。在接下来的20年里,CDN行业经历了巨大的变革和持续发展,涌现出许多云CDN厂商。阿里云CDN起源于淘宝CDN,从2008年开始发展,直至2014年正式成为阿里云CDN。它不仅为阿里巴巴集团旗下所有子公司提供服务,还通过云计算的方式输出自身的资源和技术。这种发展模式使得阿里云CDN在行业中扮演着重要角色,为用户提供高效的内容分发服务,并推动了云计算技术在CDN领域的应用和发展。
本文将深入浅出探讨CDN的概念、原理以及其在现代互联网中的重要性。了解CDN如何工作,以及它如何帮助网站和应用程序提供商加速其内容传输,提高可靠性和安全性。
一、源站与CDN
1.源站直连的交互
在了解CDN前,先了解一下应用CDN之前客户端与源站服务器的交互过程
客户端通过DNS拿到源站IP,访问服务器上的资源时一般分为四个步骤:
步骤一: 客户端和服务器端建立连接
步骤二:客户端发送请求数据到服务器端(HTTP 协议)
步骤三:服务器端接收到请求后,进行处理,然后将 处理结果响应客户端(HTTP 协议)
步骤四:关闭客户端和服务器端的连接(HTTP1.1 后不会立即关闭)
源站服务器就是部署网站所在的服务器,在用户访问指定资源时提供初始内容并握手交互数据,用户距离服务器的距离越远则转发次数越多,浏览器加载会越耗时。
当多个用户访问源站时就会占用大量的资源处理发送与响应数据,导致源站服务器产生过多负载压力。
如同12306在应用CDN技术前逢年过节抢票高峰期大量用户访问源站,导致有时会出现部分服务器宕机,影响用户的使用体验。如今应用了CDN技术的12306通过多个缓存服务器分担压力,大大提升了用户体验。借助公有云的混合CDN使得资源灵活调配,减少了服务器带宽等资源上的开支,效果立竿见影。
2.什么是CDN?
那么有没有办法解决这些缺陷呢?有,它就是CDN技术。
以下为套了CDN后用户浏览器与源站、CDN缓存节点交互数据的情形
CDN通过将部分资源内容缓存在多个缓存服务器上,挑选离用户最近的缓存服务器进行数据传输,减少了数据传输距离和网络拥堵,从而加快了内容加载速度,提高用户体验。而这些需要缓存的资源一般都是静态资源(html、css、js等)、多媒体(图片、音乐、视频等)资源。除去缓存资源,其他数据交由源站服务器来处理。
通过将这些内容复制到多个服务器上,CDN提高了内容的可用性和稳定性。即使某个服务器发生故障,用户仍然可以从其他服务器上获取内容。
不仅如此,CDN可以减少原始服务器的负载,降低服务器带宽使用成本。由于内容被缓存在CDN服务器上,减少了对源站服务器的直接请求,节省了带宽费用、减轻了部分骨干网的压力。
二、CDN的技术实现
1.调度策略与方式
1.1调度策略
在用户请求到达时,CDN如何选择最合适的服务器来响应请求。这些策略将会优化内容交付的速度、性能和效率
1.1.1 最近节点调度
这种策略会将用户请求路由到距离用户最近的CDN节点。通过测量用户的地理位置和网络延迟,CDN可以选择最近的服务器来响应请求,从而减少数据传输时间和延迟,提高用户体验。
1.1.2 负载均衡调度
负载均衡调度策略旨在平衡CDN服务器之间的负载,避免单一服务器过载。通过监控服务器的负载情况,CDN可以将请求分发到负载较低的服务器上,确保整体性能和可靠性。
1.1.3. 源站响应时间调度
该策略基于各个CDN节点与原始服务器的响应时间来进行调度。CDN会选择响应时间最短的服务器来处理请求,以减少数据传输时间和提高内容交付速度。
1.1.4 带宽利用率调度
这种策略会根据各个CDN节点的带宽利用率来进行调度。CDN会将请求路由到带宽利用率较低的服务器上,以避免网络拥堵和提高整体带宽利用效率。
1.1.5 动态调度策略
动态调度策略根据实时的网络状况和服务器负载情况来进行调度决策。CDN可以根据不同情况动态调整调度策略,以确保最佳的内容交付性能。
1.1.6 内容类型调度策略
针对不同类型的内容(如图片、视频、文本等),CDN可以采用不同的调度策略。例如,对于大型视频文件,CDN可能会选择带宽较大的节点来提供更快的传输速度。
通过合理选择和组合这些调度策略,CDN可以实现更高效的内容交付,提高用户体验,降低延迟,减少带宽成本,并确保网络的稳定性和可靠性。
1.2调度方式
CDN使用多种方式来进行调度,以确保最佳的内容交付性能和用户体验。以下是一些常见的方式:
1.2.1 DNS负载均衡
DNS负载均衡是一种常见的CDN调度方式,通过DNS解析将用户请求路由到最合适的CDN节点。CDN提供商会在DNS解析阶段根据用户的地理位置、网络条件和其他因素,将用户请求定向到最佳的服务器上。每请求一次可能都会让服务器查找合适的IP,属于是压力给到了DNS上,我觉得这种方式还是比较考验DNS集群的性能。
1.2.2 HTTP请求重定向(302)
CDN可以通过HTTP请求重定向也就是302来实现调度主要基于客户端 IP 和 302 调度集群。当用户请求到达时,CDN节点可以根据用户的地理位置和其他因素,向用户返回重定向响应,指示用户访问最适合的服务器。
1.2.3 负载均衡算法
CDN使用各种负载均衡算法来决定将用户请求路由到哪个服务器。常见的负载均衡算法包括轮询、加权轮询、最小连接数、最小响应时间等,以确保服务器负载均衡和性能优化。
1.2.4 Anycast
Anycast是一种网络寻址和路由技术,允许多个服务器拥有相同的IP地址。当用户请求到达时,网络会将请求路由到距离最近的服务器,从而提高内容交付速度和性能。像我们熟知的AWS CloudFront、CloudFlare等运营商都在使用这样的路由层面调度。
1.2.5 实时监控与动态调整
CDN通过实时监控服务器负载、网络状况和用户请求情况,动态调整调度策略。这种实时调整可以确保始终选择最佳的服务器来响应用户请求,提高内容交付效率。
1.2.6 内容感知调度
CDN可以根据不同类型的内容(如静态内容、动态内容、视频等)采用不同的调度策略。例如,对于大型视频文件,CDN可能会选择带宽较大的节点来提供更快的传输速度。
综合利用这些调度方式,CDN可以实现高效的内容交付,提高用户体验,降低延迟,减少带宽成本,并确保网络的稳定性和可靠性。
2.缓存与缓存调度技术
2.1缓存技术
2.1.1边缘缓存
边缘缓存是CDN最常用的缓存方式之一。CDN在全球各地部署服务器节点,这些节点被称为边缘服务器。当用户请求内容时,CDN会将内容缓存到最接近用户的边缘服务器上,以减少数据传输距离和提高内容交付速度。
2.1.2内容预抓取
CDN可以通过内容预取的方式提前将一些内容缓存到边缘服务器上。这样,当用户请求这些内容时,CDN可以直接从缓存中响应,而不必再向原始服务器请求,从而加快内容交付速度。
2.2缓存的调度技术
CDN通过缓存控制策略来管理缓存内容的过期时间、缓存规则等。通过合理设置缓存控制策略可以提高缓存命中率,减少对源站服务器的请求。
除了静态内容,也可以缓存动态内容,如动态网页、API响应等。CDN可以根据内容的更新频率和用户请求情况,动态地缓存和更新这些内容。
在优化方面则主要是对内容进行压缩,减小文件大小,从而减少传输时间和带宽消耗。压缩后的内容被缓存到边缘服务器上,用户请求时可以更快地传输和加载。
通过上述的这些缓存方式,更是确保内容的稳定性和可靠性。
三、CDN的多种形式
1.传统CDN
传统的CDN基本包含上述的所有特性,区别在于公有云CDN和私有云CDN。像腾讯云、阿里云、CloudFlare这种都属于公有云CDN。一般来说部署在组织自己的服务器和网络中,用于加速内部应用程序或网站的内容交付的,就是私有云CDN。
2.SCDN
SCDN与一般的CDN架构都差不多,关键点在于CDN的节点上都部署有防火墙,兼顾加速的同时提供安全防护。网站服务器的痛点主要就来自于流量攻击和入侵攻击,SCDN的出现带来了一个很好的解决方案。比如需要抵挡恶意流量(DDOS、CC等)使用硬件防火墙过滤或者清洗恶意流量,提高源站与缓存节点的安全性。
除了在缓存节点部署硬防外,往往还会部署Web防火墙集群,有效防护SQL注入、提交木马等等的网站层面的攻击。常见的像知道创宇云防、奇安信安域之类的都属于SCDN的范畴。
3.PCDN
主要利用P2P对等网络技术,让用户共享带宽和资源来加速内容交付。在PCDN中,用户的设备(如电脑手机,在往下甚至是一台机顶盒一台路由器等等)都可以缓存服务器用来相互共享数据,比如说A用户可以从B、C等用户设备获取数据片段然后拼接成完整的文件,不用像传统CDN技术那样完全依赖中心服务器。比如啊我看一集电视剧一部电影,我的浏览器或是播放器首先拿到CDN调度服务器给的m3u8表,然后用这个m3u8表把不同片段的ts文件下载地址给下载了再拼接转码就得到mp4文件,处理完后的视频文件通过播放器让我观看。这样不仅减轻了CDN缓存服务器的负担,也提高内容传输效率,更是减少了云厂商的成本投入。
4.融合CDN
融合CDN主要混合公有云或是私有云CDN厂商的优质节点,通过智能调度策略为用户选择最合适的CDN缓存节点。比如融合CDN国内用户访问的时候就会优先通过国内的像阿里云腾讯云这样的而且是距离最近的CDN缓存节点来加速。国外用户访问融合CDN就会自动切换成CloudFlare、AWS这样的公有云厂商离海外用户最近的缓存节点,可以说是打破了单个CDN厂商的节点资源以及调度能力有限的困境,突破了地域时间以及不同运营商的限制。像京东云与Cloudflare合作的融合CDN,不论是给国内还是海外用户都提供了很好的加速体验。
5.全站加速DCDN / ECDN
全站加速服务可能在不同云厂商都有不同的叫法,它是一项基于CDN加速技术的云技术升级,智能地区分静态和动态内容以提升浏览体验。静态内容直接利用CDN进行加速;而对动态内容,则通过高效的回源拉取,包括路由决策优化和协议优化等方法。全站加速不仅提供基本的CDN静态资源加速功能,还进一步提供动态加速、TCP和UDP四层加速、以及Websocket七层加速等功能。这使得安全性和边缘计算等能力能够快速整合到全球节点的全站加速中,可靠性要比传统CDN更高。其运作原理如下图所示:
四、CDN的场景应用
1.流媒体传输
常用的比较主流的视频平台一般都用上了CDN视频直播和视频点播服务,通过将视频内容缓存在分布式的边缘节点上,使用户可以从距离更近的服务器获取视频数据,加速视频的加载速度,减少缓冲时间,提高观看体验。大型直播的环境下CDN也可以支持高并发的视频流传输,确保观众可以流畅观看直播内容。
2.云存储加速
像现在的网盘很多都是分布式的下载节点了,要不然源站服务器得吃不少性能,这个除了用在网盘上也可用于企业分发软件更新和补丁,加快软件下载速度,减少服务器负载,提高软件发布的效率。
3.安全加固
CDN通常提供DDoS防护服务,可以帮助抵御大规模的分布式拒绝服务攻击,也可以检测和阻止恶意流量,保护网站和应用不受攻击。
内容安全方面上CDN也可以提供内容过滤功能,以防止恶意内容的传播,保护内容的完整性和安全性。
结语
在互联网高速发展的今天,CDN技术的进步和应用将继续推动网络性能的提升,满足用户对高速、高效内容交付的需求。随着技术的不断创新和发展,我们相信CDN将继续发挥重要作用,为构建更加智能、高效的互联网世界贡献力量。期待CDN技术在未来的发展中继续发挥重要作用,为用户和企业带来更优质的网络体验和服务。
感谢阅读,喜欢的话可以的话能帮忙点个赞吗
参考文献
12306是不是能抗住上亿级的高并发,背后它如何承受的? - 知乎 (zhihu.com)
CDN图解(秒懂 + 史上最全) - 疯狂创客圈 - 博客园 (cnblogs.com)
全站加速网络 产品概述-产品简介-文档中心-腾讯云 (tencent.com)
什么是cdn_CDN的工作原理_使用CDN服务器的好处 | Cloudflare (cloudflare-cn.com)
尊重原创,允许规范转载。如需转载请标明来自作者:@JiuXia2025 以及本文的原文链接
]]>下载Nginx
yum update #更新软件包列表yum install wget #安装wget下载工具wget http://nginx.org/download/nginx-1.18.0.tar.gz #下载指定版本的Nginxtar -zxvf nginx-1.18.0.tar.gz #解压文件
编译
cd ./cd nginx-1.18.0#配置安装项./configure --prefix=/usr/local/nginx \--with-stream \--with-stream_ssl_module \--with-http_ssl_module \--with-http_v2_module \--with-threads#安装make && make install
加入链接命令
ln -s /usr/local/nginx/sbin/nginx /usr/local/bin/nginx
一些常用命令
启动Nginx:nginx
通过配置文件启动:nginx -c /usr/local/nginx/conf/nginx.conf
强制关闭:nginx -s stop
关闭:nginx -s quit
重载配置:nginx -s reload
使用命令启动Nginx,Nginx默认监听80端口,浏览器访问服务器的IP地址即可显示运行成功的网页界面,如下图
设置开机自启动
官方文档中给到了一个示例脚本,把它加入到/etc/init.d/nginx
里
示例脚本:https://www.nginx.com/resources/wiki/start/topics/examples/redhatnginxinit/
vi /etc/init.d/nginx
脚本里把Nginx指向到配置文件和可执行程序,wq保存退出
nginx="/usr/local/nginx/sbin/nginx"NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"
使用chkconfig设置开机自启
授予权限:chmod +x /etc/init.d/nginx
将Nginx添加到管理列表:chkconfig --add /etc/init.d/nginx
设置开机启动:chkconfig nginx on
看一下列表里面加入了就搞定了:chkconfig --list
二、编译安装php
首先安装依赖
yum -y install libpng libpng-develyum -y install openssl-develyum -y install bzip2 bzip2-develyum -y install gccyum -y install libxml2-develyum -y install libzip
下载指定版本并解压
wget https://www.php.net/distributions/php-7.3.19.tar.gztar -zxvf php-7.3.19.tar.gzcd ./php-7.3.19/
编译前设置
#设置目录./configure --prefix=/usr/local/php73 --enable-fpm#加入常用扩展./configure --prefix=/usr/local/php73 --with-curl --with-mysqli --with-openssl --with-pdo-mysql --enable-fpm#开始编译make#编译完成后安装make install
安装后配置
回到编译目录中执行以下:
加入配置文件cp ./php.ini-production /usr/local/php73/php.inicp ./sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm加入可执行权限chmod +x /etc/init.d/php-fpm 加入php-fpm配置文件cp /usr/local/php73/etc/php-fpm.conf.default /usr/local/php73/etc/php-fpm.confcp /usr/local/php73/etc/php-fpm.d/www.conf.default /usr/local/php73/etc/php-fpm.d/www.conf
检验是否安装完成:ps -ef|grep php-fpm
启动php-fpm:/etc/init.d/php-fpm start
加入开机自启动列表:chkconfig --add php-fpm
三、Nginx引入php模块
打开web配置文件:vi /usr/local/nginx/conf/nginx.conf
添加以下:
location ~ \.php$ { root /usr/local/nginx/html; fastcgi_pass 127.0.0.1:8800; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }
这里我绑定的是8800端口,浏览器访问IP:8800,可以看到php已经成功加载
kubectl get node
查看状态可以看到coredns是Pending状态,master是NotReady
master安装cailo网络插件
curl https://docs.projectcalico.org/v3.20/manifests/calico.yaml -Okubectl apply -f calico.yaml
安装成功重启服务,master已经回归了正常的Ready状态
VMWare WorkStation Pro下载:链接
CentOS7镜像(阿里云源):镜像链接
VM网络设置
- master节点IP: 172.16.0.3
- node01节点IP:172.16.0.4
- node02节点IP:172.16.0.5
- 子网掩码:255.255.0.0
- 网关:172.16.0.2
- DNS:114.114.114.114
设置虚拟网卡
首先打开顶栏编辑-虚拟网络编辑器改下VMnet8的NAT网卡
子网172.16.0.0,子网掩码255.255.0.0
创建master虚拟机
安装CentOS7
创建完安装系统,这里我选CentOS7镜像
挂载安装盘
保存,开机
过下开机引导
配置静态IP
配网好了就可以开始安装,设置root密码
安装完后关闭虚拟机
进入虚拟机设置里关掉光驱的安装镜像,开机
输入ip addr
看看IP对不对
安装docker
移除原来的docker
yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
设置yum为阿里云源
sudo yum install -y yum-utilssudo yum-config-manager \--add-repo \http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安装docker
yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7 containerd.io-1.4.6systemctl enable docker --now
配置加速
sudo mkdir -p /etc/docker # 创建文件夹sudo tee /etc/docker/daemon.json <<-'EOF'{ "registry-mirrors": ["https://82m9ar63.mirror.aliyuncs.com"], "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2"}EOFsudo systemctl daemon-reloadsudo systemctl restart docker
docker安装完成!
安装kubeadm
部署基础环境
# 将 SELinux 设置为 permissive 模式sudo setenforce 0sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config#swap关闭swapoff -a sed -ri 's/.*swap.*/#&/' /etc/fstab#允许 iptables 检查桥接流量cat <<EOF | sudo tee /etc/modules-load.d/k8s.confbr_netfilterEOFcat <<EOF | sudo tee /etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-ip6tables = 1net.bridge.bridge-nf-call-iptables = 1EOFsudo sysctl --system
安装kubelet、kubeadm、kubectl 三大件
# 配置k8s下载地址cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo[kubernetes]name=Kubernetesbaseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64enabled=1gpgcheck=0repo_gpgcheck=0gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpgexclude=kubelet kubeadm kubectlEOFsudo yum install -y kubelet-1.20.9 kubeadm-1.20.9 kubectl-1.20.9 --disableexcludes=kubernetes# 启动kubeletsudo systemctl enable --now kubelet
拉取镜像
# 下载镜像sudo tee ./images.sh <<-'EOF'#!/bin/bashimages=(kube-apiserver:v1.20.9kube-proxy:v1.20.9kube-controller-manager:v1.20.9kube-scheduler:v1.20.9coredns:1.7.0etcd:3.4.13-0pause:3.2)for imageName in ${images[@]} ; dodocker pull registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/$imageNamedoneEOF chmod +x ./images.sh && ./images.sh
初始化matser主节点
#所有机器添加master域名映射,以下IP地址需要修改为自己的echo "172.16.0.3 cluster-endpoint" >> /etc/hosts # master节点 每个节点都需要执行,让每个节点知道master节点#初始化主节点(只需在master节点运行)kubeadm init \--apiserver-advertise-address=172.16.0.3 \--control-plane-endpoint=cluster-endpoint \--image-repository registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images \--kubernetes-version v1.20.9 \--service-cidr=10.96.0.0/16 \--pod-network-cidr=192.168.0.0/16# 以下是各个命令的备注不需要执行kubeadm init \--apiserver-advertise-address=172.16.0.4 \ # master 节点ip--control-plane-endpoint=cluster-endpoint \ # 域名值--image-repository registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images \ # 镜像仓库--kubernetes-version v1.20.9 \ # k8s 版本 --service-cidr=10.96.0.0/16 \ # 网络范围 一般不用改 网络范围不重叠--pod-network-cidr=192.168.0.0/16 # k8s 给pod分配网络ip的范围 一般不用改#所有网络范围不重叠
我的运行结果,出现这个就说明master初始化成功了
根据上述运行结果的提示可以进行下一步加入node子节点的操作
一、刷前的准备
exTHmUI刷机包
JX-Bootloader.7z(用于启动twrp)
以上文件我都打包在网盘链接里了,ROM详细介绍见:查看链接
二、对安卓系统进行分区
启动Switch进入hetake引导界面
如果你之前刷了LineageOS那么你就不需要分区了,直接进SD UMS拷包点刷写Android,就可以进TWRP开刷
点击上方工具-SD卡分区管理
分好合适的空间后点击下一步
分好区了点击SD UMS,用数据线将Switch连接电脑
删除原有的switchroot文件夹
把刷机包和TWRP整合包先解压到电脑里
再把这三个文件夹拖进去
有重复就覆盖
电脑上弹出UMS再拔数据线
然后点击刷写Android,就可以进入TWRP了
点击确认
滑动滑块确认
点击Mount,挂载所有分区
然后回到主页点击Wipe
点Advance Wipe
勾选这几个,滑动滑块确认
回到主页点Install
找到根目录下的/external_sd/exTHmUI_A11_nx-tab_JiuXia2025文件夹
点击右下角的Install Image切换成刷镜像模式,然后显示镜像出来就可以开始刷机了
点击boot.img刷Boot分区,滑动等刷完返回列表继续
点击system.img刷System分区,滑动等刷完返回列表继续
点击vendor.img刷Vendor分区,滑动等刷完返回列表继续
刷完先别急着进系统,回到主页点Wipe
点Format Data
输入yes然后点击右下角的勾
清完Data就可以点击Reboot System重启系统了
回到主页点更多设置再点击exTHmUI,就可以进入系统了
等待开机
小提示:如果是启动安卓系统时在这个画面短按音量减可以返回hetake,长按音量加就可以进入安卓的Recovery
熟悉的开机引导
时区选择北京,时间就会自动校准了
搞定
进到exTHmUI看到这个提示千万不要点进去格式化,忽略即可,不影响里面的数据
刷机包和TWRP包放网盘链接了:查看链接 提取码:CLVK
前两期图文
Switch刷LineageOS:查看链接
Switch刷Magisk:查看链接
一、刷前的准备
在刷之前首先准备好
L4T引导配置文件
Recovery镜像包
lineageos刷机包
如果你用的是国产芯片而非树莓派,那么你需要先更新芯片固件才能开始刷入,固件我也放网盘链接里了,按照其他视频教程升级即可(树莓派芯片不用刷)
以上所有文件都已经打包好在网盘里了,需要可以自取,网盘链接在图文结尾
二、对安卓系统进行分区
插卡开机启动来到hekate引导界面
点击上方的工具选择SD卡分区管理
调整合适的分区容量,点击下一步
备份好数据之后点击开始分区
三、刷入Recovery以及dtb
点击SD UMS,然后使用USB数据线连接电脑,不要点击刷写Android
这里最好用传输稳定不松动的数据线,以防数据丢失或传输文件中断
进入电脑打开新弹出的设备,把下载下来的整合包里面所有文件都拖进去
如果有重复直接覆盖即可
注意:这里一定要在电脑先弹出UMS再拔数据线
这样就已经准备好镜像与刷机包了,接下来就是刷入Recovery并进入
点击刷写Android
一路点击继续
这里也点击继续
四、刷入ROM
来到LineageOS的Recovery里,点击Apply update
选择第二个Choose from SWITCH SD
找到刚刚解压出来的zip格式刷机包
这是签名验证的提示,点Yes
刷完点左上角返回选择Reboot system now,重启进入系统
又回到了hetake的引导界面,点击更多设置里的安卓图标就可以进入系统
开机引导过完进桌面
刷入好之后接着下面的
五、刷机后解决联网与时间不准的问题
打开设置,划到底下点击关于平板电脑
划到底下点十下版本号
返回滑下去点击系统
点击高级
划到底部选择开发者选项
滑下去USB调试以及Root身份的调试两个开关都要打开
数据线连接电脑,打开秋之盒工具箱点击右下角的ADB命令行
这个修复的命令我整理好放在网盘链接里了
输入完之后拔掉数据线,开启飞行模式再关闭飞行模式,联网跟时间就正常了
至此,已成功刷入安卓类原生,enjoy!
本文用到的文件:查看链接 提取码:PBwF
秋之盒工具箱:查看链接
如果需要刷入Magisk的话,看我上篇图文:查看链接
图文首发于酷安,如需转载请注明出处来源@JiuXia2025
准备:
大气层Bootloader整合包
Magisk卡刷包+安装包(酷安里有下)
Switch硬破
一、更新大气层bootloader
Switch上的安卓系统不同于手机,是由大气层这一bootloader来引导的,最好先更新bootloader引导再开始刷机,准备好大气层整合包
文件下载: http://appcenter.9xia.top/ns/atmosphere_pack.php
视频教程: https://b23.tv/ZimaeWq
二、进入Recovery
重启进到hetake引导界面,更多设置-Android
进到如图页面长按按音量+:
Switch的LineageOS默认是自带的Rec,按选择Apply update
选择第三个
选择Magisk
重启完重新进安卓系统进软件列表,用MT管理器找到SD卡目录
选择SWITCH SD
安装完面具就正常激活了