当网站遭遇DDoS：如何优雅地应对云服务器被封堵
网站被DDoS后制定解决方案很平常的一天，我在网站主题交流群里，发现有群友网站被打掉线了。刚刚给他发“贺电”，没过多久，我也收到了华为云发来的“贺电”：尊敬的(Rin):感谢您使用华为云产品，非常抱歉打扰您。在北京时间2025/05/22 20:33，华为云监测到您的弹性公网IP(EIP)实例 发生EIP封堵事件。 详情：节点：亚太-新加坡实例名称：119.8.?.?实例ID：88????e7-1??7-4??a-8??d-0??????????5告警描述：{"reason_zh": 尊敬的Rin：华为云DDoS防护服务在2025-05-22 20:33:05检测到您的EIP 119.8.?.? 异常流量已超过DDoS防护峰值触发封堵，封堵时带宽为 67 Gbps, 您服务器的访问将受到限制，24小时后系统将自动为您解封。您可登陆官网购买DDoS高防服务提升防护能力，保障服务器稳定运行。, "reason_en": Dear Rin: HUAWEI CLOUD Anti-DDoS Service detects that your EIP 119.8.?.? abnormal traffic has exceeded the DDoS protection peak at 2025-05-22 20:33:05 and triggers blocking. Bandwidth when blocking is 67 Gbps, Access to your server will be restricted, and the system will automatically unblock it for you after 24 hours. You can log in to the official website of HUAWEI CLOUD to purchase Advanced Anti-DDoS service to enhance protection capabilities and ensure stable server operation.}原因分析：尊敬的Rin：华为云DDoS防护服务在2025-05-22 20:33:05检测到您的EIP 119.8.?.? 异常流量已超过DDoS防护峰值触发封堵，封堵时带宽为 67 Gbps您服务器的访问将受到限制，24小时后系统将自动为您解封。您可登陆官网购买DDoS高防服务提升防护能力，保障服务器稳定运行。如有疑问，您可以登录华为云官网提交工单咨询，会有相应工程师为您核实查看。而且连着几个群友的网站都被攻击，甚至有人还被短信轰炸了，初步断定就是群里面有人搞鬼，但是没有证据抓现行，只能自己做好预案和防护了。由于DDoS攻击基本上都会被云服务器提供商黑洞IP，如果只有单台云服务器，这个真的无解。但是，我有不止一台机器，这可就有解决办法了。我的大体解决思路是这样的：配置服务器访问频率限制，防止高频恶意请求（CC攻击）耗尽服务器资源。设置主服务器定期备份内容到备用服务器。备用服务器部署检测，一分钟一次，判定主服务器状态，如果超过三次无响应，判定为被打挂了。判定打挂后，通过Cloudflare的API，设置域名的A记录IP地址为备用服务器IP地址，并开启Cloudflare的代理防护。当主服务器恢复后，自动将DNS记录切换回主服务器，然后将备份的内容还原到主服务器。接下来，让我们开始任务吧。这里说一下环境条件：目标操作系统为 Alpine Linux 3.20 ，LNMP环境都为系统默认设置安装的，备份的目标为Typecho博客。配置服务器访问频率限制虽然我一开始网站就已经做了缓存配置，是能抗高并发的，不至于像某些人的网站itdog网站测速一测就死。但是，如果是高频的请求，还是会耗尽带宽的资源，导致网络阻塞，访问变慢。所以，是时候配合iptables和内核设置来阻挡恶意请求了。首先是拉黑IP，平时注意留意服务器的网络负载，如果遇到有过高负载历史的时候，善于使用Nginx的日志进行IP追溯，拉黑恶意IP。拉黑命令如下：iptables -A INPUT -s 45.140.143.77 -j DROP iptables -A INPUT -s 106.15.224.186 -j DROP其次，就是设置单个IP的最大连接数，对超出的部分数据包进行丢弃处理（如果是拒绝处理，还是会回传数据包导致上行被占）。这里设置为20：iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 20 --connlimit-mask 32 -j DROP iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 20 --connlimit-mask 32 -j DROP一并设置内核参数保证连接处理数量足够：sysctl -w net.netfilter.nf_conntrack_max=10240由于直接丢弃数据包会造成半连接状态，仍然会消耗资源，所以要进行一些规则防止资源耗尽。#防御 SYN Flood # 启用 SYN Cookies（防御 SYN Flood） sysctl -w net.ipv4.tcp_syncookies=1 # 限制 SYN 请求速率（每秒最多 20 个） iptables -A INPUT -p tcp --syn --dport 443 -m connlimit --connlimit-above 20 -j DROP iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 20 -j DROP # 缩短 SYN 重试次数（加速无效连接释放） sysctl -w net.ipv4.tcp_syn_retries=2 sysctl -w net.ipv4.tcp_synack_retries=2至此，这个设置已经可以防御常见的CC攻击手段。不过需要注意的是，以上命令只是临时生效，重启后就会失效，如果需要永久生效，则需要写入配置文件。不过考虑到云服务器很少重启，问题也不大。设置定期备份我打算使用mysqldump进行数据库导入和导出操作，然后使用rsync进行内容同步，最后使用chmod和chown设置网站目录权限。配置ssh密钥登陆首先，如果要自动化，则需要配置ssh密钥登陆。在客户端生成SSH密钥对：ssh-keygen -t rsa -b 4096 -C "your_email@nekopara.uk"执行后一路回车即可。然后，将公钥复制到服务器：ssh-copy-id root@server.nekopara.uk提示输入密码，输入目标服务器密码即可完成操作。这里your_email@nekopara.uk请替换为你实际邮箱，server.nekopara.uk替换为实际服务器的IP或者域名，推荐是IP。设置自动复制规则在主服务器上，将以下内容保存为backup_blog.sh放到root文件夹：/usr/bin/mysqldump -u typecho_user -p'p@sswd' typecho > "/data/database/typecho_backup.sql" /usr/bin/rsync -avz --no-perms --delete -e ssh /data/ root@backupserver.nekopara.uk:/data/ /usr/bin/ssh root@backupserver.nekopara.uk /root/set_website.sh然后赋予执行权限：chmod +x /root/backup_blog.sh相应的，在备份服务器上，也要在root文件夹创建一个set_website.sh文件进行文件权限的设置和数据库导入：/bin/chown -R nginx:nginx /data/typecho/ /bin/chmod -R 755 /data/typecho/ /usr/bin/mysql -u typecho_user -p'p@sswd' typecho < /data/database/typecho_backup.sql设置完成后，自己执行一次backup_blog.sh脚本看看有没有备份成功。要注意，这里的backupserver.nekopara.uk请替换为实际备份服务器的IP或者域名，推荐是IP。typecho_user替换为实际博客数据库的用户名，p@sswd替换为实际数据库的密码，typecho替换为实际数据库。测试成功后，配置crontab自动任务，一个小时执行一次：0 * * * * "/root"/backup_blog.sh >> /var/log/backup.log 2>&1设置状态检测和切换策略首先，这个前提条件是你要去Cloudflare获取区域API令牌：打开目标站点>点击“获取您的 API 令牌”>点击“创建令牌”>使用“编辑区域 DNS”模板>然后选择特定区域，创建令牌记录下这个令牌，后面会用到，而且以后不再可以查看了，请妥善保存。然后，在root文件夹创建一个dns_auto_update.sh，内容如下：#!/bin/bash # 🐱 Cloudflare API Token CF_API_TOKEN="neko????????????????????????????????para" # 🐱 域名，例如 nekopara.uk CF_ZONE_NAME="nekopara.uk" # 🐱 记录名称列表，例如 "nekopara.uk www.nekopara.uk" CF_RECORD_NAMES=(nekopara.uk www.nekopara.uk) # 🐱 目标 IP TARGET_IP="119.8.?.?" # 🐱 目标端口 TARGET_PORT="80" # 🐱 备用 IP BACKUP_IP="172.16.1.233" # 🐱 获取 Zone ID get_zone_id() { curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${CF_ZONE_NAME}" \ -H "Authorization: Bearer ${CF_API_TOKEN}" \ -H "Content-Type: application/json" | jq -r '.result[0].id' } # 🐱 获取 DNS 记录 ID get_record_id() { local zone_id="$1" local record_name="$2" curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records?type=A&name=${record_name}" \ -H "Authorization: Bearer ${CF_API_TOKEN}" \ -H "Content-Type: application/json" | jq -r '.result[0].id' } # 🐱 更新 DNS 记录 update_dns_record() { local zone_id="$1" local record_id="$2" local record_name="$3" local ip="$4" local proxied="$5" curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${record_id}" \ -H "Authorization: Bearer ${CF_API_TOKEN}" \ -H "Content-Type: application/json" \ --data "{\"type\":\"A\",\"name\":\"${record_name}\",\"content\":\"${ip}\",\"ttl\":1,\"proxied\":${proxied}}" | jq } # 🐱 检测目标 IP 的 HTTP 状态码 check_target() { curl -s --connect-timeout 10 "http://${TARGET_IP}:${TARGET_PORT}" -o /dev/null return $? } # 🐱 初始化 zone_id=$(get_zone_id) declare -A record_ids for record_name in "${CF_RECORD_NAMES[@]}"; do record_ids["$record_name"]=$(get_record_id "$zone_id" "$record_name") done current_ip="$TARGET_IP" proxied=false failure_count=0 success_count=0 # 🐱 监控循环 while true; do if check_target; then echo "🐾 目标 IP ${TARGET_IP} 响应正常" failure_count=0 success_count=$((success_count + 1)) if [ "$current_ip" != "$TARGET_IP" ] && [ "$success_count" -ge 3 ]; then echo "🐾 切换回原始 IP ${TARGET_IP}，关闭代理" for record_name in "${CF_RECORD_NAMES[@]}"; do update_dns_record "$zone_id" "${record_ids[$record_name]}" "$record_name" "$TARGET_IP" false done current_ip="$TARGET_IP" proxied=false success_count=0 fi else echo "🐾 目标 IP ${TARGET_IP} 无响应" failure_count=$((failure_count + 1)) success_count=0 if [ "$current_ip" != "$BACKUP_IP" ] && [ "$failure_count" -ge 3 ]; then echo "🐾 切换到备用 IP ${BACKUP_IP}，开启代理" for record_name in "${CF_RECORD_NAMES[@]}"; do update_dns_record "$zone_id" "${record_ids[$record_name]}" "$record_name" "$BACKUP_IP" true done current_ip="$BACKUP_IP" proxied=true failure_count=0 fi fi sleep 60 done运行这个脚本需要确保jq和curl软件包已经安装。这个shell脚本前面是配置部分，请根据注释的提示替换为实际内容。脚本策略是，每60秒（一分钟）检测一次目标站点的http服务，如果目标站点连续三次异常，则切换到备用服务器并开启Cloudflare的代理防护。当目标服务器恢复正常后，则切换回去。确认脚本无误后，在备用服务器上面创建一个tmux终端挂着就行了：tmux new -t dns_auto ./dns_auto_update.sh总结一下其实漏了一步——将备份服务器的内容还原回目标服务器。这部分就跟第一部分是同理的，不加crontab计划任务就行了。也可以结合切换脚本自行实现。由于我网站流量比较小，对内容的及时性没有这么高的要求，在被打期间我也不会写文章。最多就是在被打期间如果有人评论，那这个评论会在主服务器恢复后的数据同步中被清除，无非就是丢一两条评论，而且大部分时间也没人评论，所以无所谓了。现在想想如果真的搞了，万一恢复的时间赶在同步时间的点子上，冲突搞炸数据库就不好玩了。其实就算是主服务器备份到备胎服务器，也不一定需要计划任务，每次更新文章后连上去执行一下脚本就行，顺手的事情。切换期间如果真有评论想要保留的，手动迁移过去也不费什么事，毕竟被打也算是小概率事件，玩网站一年多了才被打这一次。或许有人会好奇，既然被打就是切Cloudflare，为什么不一直套Cloudflare，这样不就不怕打了吗？其实我不怎么做是有原因的，因为我还是希望网站速度能快一些，而切了Cloudflare，那速度自然就快不起来了。而且，一直套Cloudflare降低网站国内访问速度和质量，和在关键时刻因为DDoS临时切换Cloudflare降低速度和质量，哪个更好还是看得出来的。当然，怎么玩是建立在你有两个服务器到的基础上，如果只有一个，打寄了就寄了，一点办法都没有。其实之前就已经做好被打的预案了，但是实际发生了被DDoS的时候，还是有点手忙脚乱，这下能让我的网站更加完善了。最主要还是想保持网站不掉线罢了。
今天也是第一次体验了大学的开卷考试，给你带书进考场翻。太难了，太难了，书都来不及翻。考试还直接考上课没讲过的，书上课只讲了三分之一，然后考试选择题部分考整本书，太过分了。(●･̆⍛･̆●)
华为云活动白嫖代金券记录前段时间华为云搞了个 云学堂「集证」有礼活动 最高可以得到两个1000块钱代金券。可惜我拉人能力不行，拉人的代金券只拿到了100的额度。但是，积分代金券直接干满了，拿了1000！虽然但是我对华为终端产品并不认可，但是华为云送钱这件事还是得好好夸夸。以后博客运营就靠华为云了！干满，干满，全部干满！！！一直白嫖一直爽，服务器2033年7月到期。虽然不是完全白嫖，因为活动需要1块钱购买认证，而且有一些代金券为了彻底用完自己还是垫付了一些，但是累计总花费不超过50，白嫖2024年9月-2033年7月将近9年的服务器，太香了！
回首来时路：十年不过一场清醒的梦
2015年竟然是十年前，真是可怕2015年，感觉好像还很熟悉，在我看来好像不是很久，因为2015年那会，刚上初中，生活有了新的乐趣。但是，现在点开电脑时间栏一看，赫然写着2025而且已经条快过半，真是吓人。再想想自己现在已经大四，同届的同学已经准备要毕业了，大学也即将完结撒花。不知不觉居然已经过去十年了，十年时间弹指之间就飞过，感觉真的太快了，刚上初中，小学毕业，那些场景仍然历历在目，似乎不曾走远。到现在，生活给我的感受就是:人生就像一趟高速火车，只有始发站和终点站，没有停靠站，从发车开始就在不停的加速，不会慢下来，只会越来越快。终点站不会停车，只会狠狠的撞击过去，化作尘土，离开人间。最近确实是有点emo了，虽然不是很想在这么可爱的博客里面写下这些内容，但是这似乎也只是唯一合适的地方让我长篇大论的写下这些emo文字了。这篇文章应该不会有什么逻辑，都是想到什么写什么，就当作一篇倾诉了。那么，让我们开始吧。对时间流逝的感受我对于时间的感受，在小时候是没什么感觉的。在我上初中之前，我都感觉每一年无比的漫长，或许是没有什么可以玩的，可以消磨时间的东西，生活总觉得很慢。从现在回望小学时候的暑假，一个暑假仿佛是过了一两年的时间，因为太漫长了。而且，我小时候家里面管的也比较多，不允许我随意出门，只能自己呆家里。每次都只能隔着铁栅栏门看街道的小孩在外面玩，而铁栅栏门上赫然拴着一把包浆的挂锁。由于很多玩具我都已经玩腻了，一般都是积木啥的，所以，我小时候无聊就会倒腾别的东西——爷爷的工具箱。也正是如此，造就了我现在较强的动手能力和解决问题的能力。到了初中，家里面开始允许我自己骑单车去学校了。生活明显没有了小学时候的惬意——早上六点钟就要出门骑单车去学校了，这时候天还只是蒙蒙亮，街上没什么人，所以可以骑得很快。然后初中就需要晚自习了，每天晚上的时间也被学习占据。但是，我刚上初中那会，还是有完整双休的，现在想想还真是幸福啊。不过，也正因为有了自由度，生活也变得快乐了许多。可以跟同学在学校周边逛逛，晚上晚自习结束后和同学一边聊天一边骑单车回家，现在想想也是不错的回忆。“快乐的时间总是短暂的”，或许也是因为这个，也有可能是初中课业繁忙事情变多了，上初中后我明显感觉时间变快了。转眼就上高中了，高中就更快了，因为高中内宿了，宿舍，饭堂，教室，三点一线的学习生活，周末被剥削得只剩下一个下午，多少有点折磨。在受折磨的过程中，感觉时间确实变慢了。高一高二那会还真觉得时间相比于初中放慢了一点，特别是高二的寒假，遇到2020年的疫情，本来应该不到一个月的寒假硬生生放了五个月。第一次觉得假期如此漫长。但是，上了高三，快节奏等生活又让我感觉时间快了起来，而且比以往都要快。高考倒计时100天转瞬即逝，然后我高考还考砸了，去了末流211。其实如果按照模拟考的水平，我应该是能上中末流985而，但是，被物理试卷50条减速带留在了广西大山（然后上了大学，有了手机，电脑，游戏的助攻，外加希望获得好的成绩保研，在课程上也比较用功，时间简直飞快。刷的一下，大一结束，又刷的一下，大二结束，不知不觉现在大四也即将结束。虽然大学期间也拿了不少可喜的，含金量比较高的成果，但是现在回望想想，时间流逝的速度真的太可怕了。所以我现在真的感觉，时间流逝飞快，不好好抓住时间做自己想做的事情，过了机会就过了，再也没有了，人生可没有'Ctrl+Z'。虽然有一些我想做的事情事情，因为种种原因，一直没能做，也是一种遗憾了，但是为了更好的目标，有一些遗憾也是必要的牺牲。回望小学时代，真是个有趣的小孩时间：2009-2015小学的时候，我很喜欢观察。在我读小学期间，见证了这个学校基础设施的一点一点升级，并且熟记于心。虽然我也不知道我记得这些东西有什么用，现在回想可能是单纯为了好玩。例如，我能精确记住小学校园里面所有摄像头的位置，并且因为作为老师子女，可以随意进出保安室的监控室，所以也知道这些监控摄像头的拍摄范围。那时候摄像头还没有全覆盖的时候，我可以在不被摄像头拍到的情况下溜达一圈校园。当然，后面读五年级的时候学校全面升级了监控系统，基本上很多地方无死角，就做不到了。又如，因为自己在课余时间亲眼目睹了整个学校的基础设施部署，我能知道哪个配电箱是控制哪里的，监控摄像头的走线是什么样的，教室电脑还原卡的密码是多少，等等。对，没错，就是那么善于观察和记录，在同学里是神人一般的存在，特别是因为我会破开电脑还原卡后为所欲为安装游戏。总的来说小学还是挺惬意的，只是可惜因为家里面管的比较严，基本上回家就是被关监狱，在家自由度还没有学校多。要么就是带出去买东西，逛超市，或者丢在书店逼我看书，虽然我小学并不喜欢看书，现在也是。总之就是在学校以外的时间没办法和同龄人一起玩耍，不能和同学一起玩耍，这算是我小学时光最大的遗憾吧。但是也得益于被这样关着在家没东西可玩，玩具也比较少，硬生生让我对工具产生了浓厚兴趣。也是受小时候看到爷爷拆东西，修东西的影响，小学三年级就会熟练使用各种五金工具，接电线，拆风扇再装回去都不在话下。对那时候我我而言，最好玩的不是玩具，而是各种五金配件，我眼中的玩具店就是五金店。甚至当时小学还喜欢收集各式各样的灯泡：红色的，绿色的，蓝色的；梨形的，圆形的，尖的。还有各种类型的节能灯：螺旋型的，2U的，3U的，4U的。以及他们不同瓦数的，不但收集，而且型号，瓦数，品牌都烂熟于心。那时候我收集等这些灯泡都是放在我房间抽屉里面，一旦家里面灯泡烧了，我老爸就会去我房间取灯泡，弄得我好不高兴。其实不至于灯泡，其他能收集的五金配件，我都有收集，例如空气开关，电缆，灯座，插座，排水管，等等。那时候为了买这些东西，经常就是缠着爷爷奶奶要买“玩具”，因为那时候父母是不同意我买这些当玩具玩的。因为收集这些东西，有一些需要接电来玩的，例如接个灯头点亮灯泡，我每次玩家里面都担惊受怕的，说我一定要接好才能玩，每次之前每次自己接线都要大人审核一遍才允许插电，后面发现我接线没问题，有安全意识后也不管了。说起来，为了收集各种常见款式的灯泡，之前我还干过去翻邻居家垃圾桶去找扔掉的坏灯泡这种事情。不过拿回来后家里面一眼不对劲，因为没有零花钱也没办法自己买灯泡，老实交代后，灯泡被砸碎扔掉，还免不了一顿打。平时的话，家里面有什么电器坏了，只要不是太大件，都允许我拆解来玩。我拆的最多的就是电话机，一般是拆喇叭，然后接一节干电池在电极划来划去滋啦滋啦响。其他的电器，我一般也就是拆马达来玩，因为电路板太复杂了，搞不懂。然后，小时候也总对和电气相关的问这问那，以至于上初中的物理课，我不听课都能闭眼考100分。小学时候拿老爸手机在景区拍的灯泡：总的来说，我小学的时候，在周边的人看来都是一个相当有趣的人，懂得东西多，还会拆解电器，玩的玩具也是另类。回望初中，近乎完美，但小有遗憾时间：2015-2018总体来说，初中的生活还算是轻松愉快的。不过现在想想也是十年前了，真的太快了。“不要动～身体向前倾～”——这就是初中的开场——小升初暑假的初中军训，教官叫站军姿的口头禅。刚上初中，主科一下子从三门变成了七门，压力确实不小，每天的课程也被排的满满当当。而且，因为历史和政治是我的弱项，我对这种死记硬背类型的科目都是没办法接受的，所以初一的成绩并没有特别好。那会我记得全校1200多人，我排名300多这样子吧，算是勉勉强强有好的高中读的水平。那时候我也不是别人眼中的学霸，就是普普通通的一个常规中等偏上水平的学生。不过同样是文科的地理，我却学的挺有兴趣，经常考试满分，生物也是接近满分。而我最弱势的科目是语文，这一直延续到了高中，直到高考我语文都不好。而数学因为初中开始逐渐上强度，成绩也没有之前这么好了。怎么说呢，初一的生活还是挺好玩的，我感觉我更加贪玩，没有很多心思放在学习上，晚自习快速做完作业，接下来的时间就是玩了。初中时期的晚自习，是两节课，很多情况下我都是前面一节课就能把当天的作业全部做完，或者作业多的话第二节课一半时间也应该做完了。因为作业确实比较简单，也不多，不但是我，班里面大部分同学也是差不多这样子，所以第二节课基本上都是在玩。我们初中那会的晚自习挺离谱的，学生和家长被迫“自愿”在晚自习申请书上面签字，现在看来是真的搞笑又奇葩。那个所谓的晚自习，你猜怎么着，它要每个同学的班长轮流去看晚自习监督，每个晚上去两个同学家长，就这样轮着来，老师一般不在，然后由班委负责记名违反纪律的同学。而这，就给了某些人公报私仇的机会，看你不爽，就逮着你违纪来记你，然后下个星期的班会，就会公开处刑。处刑的方式有以下几种：深蹲，俯卧撑，跑步，打电话给家长认错。而我因为初中时也比较皮，也没少被记，特别是有一些女的班干会针对男生，就是专门去记一些她们看不爽的男生。但是总体来说，相比于名单上的常客，我上榜的概率还是小很多的。那时候班里面有一个女生，因为得罪的人太多，几乎很多班干都是无中生有，故意鸡蛋里挑骨头去记她，让她每周都能被点名枪毙。以至于每周的50个俯卧撑/深蹲让她变得四肢发达，到后面跟男生打架甚至可以打赢，中考体育甚至轻松满分，那时候她去扔男生的实心球标准都能超线一大截。不过，要说当年被处罚最严重的一次，那还是初一时候的一次整活。那时候喜欢搞点奇奇怪怪的东西，搞了个矿泉水瓶，然后灌了半瓶自来水，往里面折断笔芯倒入墨水，然后又倒入涂改液，又找同学要了两条鸡柳往里面塞，发酵了两个星期，打开一闻简直是生化武器，闻到几乎秒想吐。然后，我拿去整蛊让别人试着闻一下，让他们体会一下我自制“生化武器”的威力。这还不至于被处罚，被处罚是因为后面发生的事情。那时候我不知道想什么，想要这个“生化武器”换一个保存容器，于是旧的那个容器——冰露矿泉水瓶（对，至今我还记得）就被替换下来了。但是，因为存放过“生化武器”，所以那个瓶子捏一下喷出来的气，是可以马上让人作呕的。那天晚自习课间，我换完瓶子后，换下来的瓶子被班里面一个好事者拿去喷人（因为冰露矿泉水瓶很软，捏一下就能把气喷出来了，喷出来的气有多“生化武器”，我只能说闻到不过三秒必吐），我很快就意识到他这么干的后果，势必会让我下次处刑大会被登榜。但是，我想去阻止的时候，直接被他喷了一脸，当场吐了。于是，就是这样，大半个班的人课间都被他迫害了一遍，被毒气喷到吐的人也不少。并且因为捏的次数太多，喷出的毒气太多，再加上当时的教室通风透气并不好，以至于到上课时间教室都是一股很浓的味道，不少同学受不了走去走廊通风透气了。那一刻，我就知道下个星期肯定要被公开处刑了，但是我没想到的是，因为事件后果太过于严重，第二天上班主任的课的时候就被公开处刑了。说起来，初一那会我还参加了学校的机器人班，基本上就是拿下午放学到晚自习那段时间去参加相关的培训，以及根据赛题去制作相关的机器人。我记得那时候的赛题是搬运小球和方块到对面，然后有两种方式：从特定洞口推入，或者从上面扔过去。我当时直接简单粗暴做了一个铲子，一路推过去，全部铲上，然后自己举起来倒到对面。然后没想到就是这么一个简单除暴的设计，直接在比赛中拿奖了。后面，因为玩的有点飘，我初一升初二的期末考试成绩一落千丈，直接从中等偏上水平跌倒了中等水平，退步三百多名，变成了六百多七百名。因此，跟家里面也闹得很不愉快，然后老师也找谈过话。也正是这一次，我彻底怒了，因为初中那时候有点叛逆，然后那时候放出狠话，你不觉得我能学好是吧，我偏要学好给你看。正巧那时候初中搬校区，离家更远了，只能进行内宿，而内宿给了更多的空余时间，因为不需要每天通勤了。为了提高成绩，我必须要补齐短板——历史和政治，我每天吃完晚饭后上晚自习之前的一个多小时，我洗完澡后就来教室背历史和政治，虽然我确实很难背得下去。然后再加上初二多了物理这一门我擅长的科目，几乎满分的成绩是很好的助攻。因为语文我确实是不行，也就只能求个平均水平，其他科目我都是尽可能发力争取好成绩。也正是这样子，我初二期中考试，离学校考试的三等奖只有一步之遥——前十是特等，11-110是一等，111-210是二等，211-310是三等，而我那次排名是311名，我至今都记得。因为那次期中考试，第一次物理考试直接考了满分，本来我只是班里一个成绩平平的人，而这次考试直接一战成名，班里面那些学霸都对我刮目相看。但是，我感觉也就是那次之后，有一些“朋友”开始不怎么和我那么好了，感觉那时候班里面成绩好的和成绩差的，完全就是两派人，你成绩好了，就不能和那边平均生坐一桌。因为当时发誓过下次考试绝对不拿鼓励奖（311名到600名吧好像，不太记得了，反正全年级排名超过一半就有了），要至少拿到三等奖，所以这次期中考试也搞得我很没面子，对这个成绩还是不服的。于是，我继续努力，想想那时候初中真是精力旺盛，再加上就寝时间个给的休息时间相对比较长，甚至还做出了晚上就寝和中午就寝时段在厕所背历史和政治这种事情。然后，不出所料，考完了期末考试后，确实不再是鼓励奖了，但是成绩连我自己都惊呆了——排名83名，直接拿下一等奖。那是我第一次觉得学习有成就感，而不是挫败与厌恶。也正是这一次，班里面班主任在班会点名表扬，说要控制向我学习，班里面那些老牌学霸也是对我开始刮目相看。此后，我就跻身了学霸行列，后面的排名基本上都是前一百拿一等奖。初二时期的教室（同学偷偷带手机拍摄的）：那时候初二发生了许多有意思的事情，我就挑一些讲一下吧。初二那时候学校刚搬到“新”校区，为什么要加双引号呢，因为这个校区并不新，而是本地一个大专用剩的校区，用来给学校临时过度用，新的校区已经划地开始建设了，说起来，直到我写这篇博客，新校区还没弄好，远远迟与当年校长在年级大会说的时间，也真是幽默。然后搬过去的那个校区，不出所料还是破破烂烂，但是因为毕竟是大专校园，还是比原来的校区要好的——至少宿舍有独立厕所了。然后还有很多基础的设施还是临时搭起来的，因为把很多房间改成了教室，导致厕所不够用，还在户外搭建了毛坯厕所。然后宿舍刚搬进去，那可真无语了，花洒是没有的，只有悬挂的水管，一开水直接就往头上撞。洗手台的排水管不翼而飞，外面洗个衣服，如果这时候厕所有人蹲坑，那他的屁股大概是要湿透了，因为水直接飞下来撞击地板弹出的水花足够湿透屁股了。为了解决这个问题，我自己找家里面要了钱买了花洒，以及排水管，把问题解决了。但是由于那个排水管我是先接到半截怡宝矿泉水瓶上面，然后穿过一根一次性筷子，在入水口也放一根，用绳子绑起来，绑不够紧。于是，一旦外面倒水太狠，还是会飞水出来给屁股沐浴。然后我们宿舍那时候就特别喜欢在别人蹲坑的时候狠狠倒水折磨他人。初二的时候，我们转内宿的就被称为“外宿班的内宿生”，然后把我们混合编制在一个宿舍里面，我记得那时候我那个宿舍有三个班的人，还有两个算是烂仔打架那种类型，他们那两个一天天就互相掐架，总能给宿舍带来点乐趣。还记得那时候有一天晚上，下铺那个人给我上铺那个人下床的地方倒扣放了个脸盆，然后当天晚上晚些时候，上铺那个人在不知情的情况下直接跳下来啪一声把自己的脸盆踩爆了，然后宿舍就是一顿笑声。要说那个时候宿舍发生的趣事，印象最深刻的就是一次全宿舍被抓就寝时间讲话。那时候我们有一个年级主任特别鸡贼，我就直接点名道姓了——刘超鹏。这个人为了抓违纪，无所不用其极，各种阴间手段层出不穷，不讲武德，偷袭，窃听，问话扣罪，等等，导致绝大部分学生对他的怨气都很大。（插个题外话，这个人，我读初三的时候因为有人打架给那个人休学了。然后，那个人找了一群小混混，在学校外面蹲点，在刘超鹏去拿车的时候一个蛇皮袋套上然后暴打一顿。这是被其他人证实过的，因为刘超鹏他班里的同学说那天之后他几天没有来上课，后面来上课也是鼻青脸肿的。我看他被打是活该，做的太过分了，被打只是迟早问题）就是这个狗逼，为了抓其我们宿舍讲话，在门口蹲点了半个小时，取证完全之后，直接敲门进来记名扣分。然后到了初三，换了宿舍，和内宿班的同学一起，宿舍的环境好了很多。而且宿舍里面还有一个二次元，偷偷带手机，天天玩崩坏三。那时候我也是二次元了，我变成二次元是初二的时候，第一个喜欢的角色是镜音铃，当然，到现在都是喜欢的，头像从那时候开始没换过角色，一直是镜音铃。说到那个带手机玩崩坏三的舍友，那可是和老师，宿管抓违纪斗智斗勇的高手。他敢明目张胆在被窝里面玩手机，宿管还看不出来，而且他的床说最靠近窗户的位置，也是最“危险”等位置，但是我看他玩了一年，稳如老狗，从未翻车。即使刘超鹏站在窗户外观察我们宿舍，他还是继续打崩坏三，当做刘超鹏不存在。初三上学期的时候，因为想要实体的二次元插画，正好班里面有一个女生会画插画，而她因为过去的言行已经被班里面大部分人孤立。而因为种种机缘巧合，我和她的关系还算正常，于是就厚着脸皮问她能不能帮忙画一下插画，我可以借我的物理书给她看笔记，然后她同意了，帮忙花了好几张镜音双子的插画，这些插画现在还放在我的房间抽屉里面。现在回想起来，当年也是真敢问啊，那个女生之前我和她坐过同座，是会割手的，第一次坐在一起的时候，我看她手上都是伤痕，问他怎么回事，她指了指桌面上的美工刀。然后和她在一起的晚自习时间，亲眼目睹她在割手，我问她这样子不痛吗，她却表示这是一种“快乐”。当年让那个女生帮忙画的插画，我甚至拿一张套在物理书上做封面：然后到了下个学期，学校为了提高精英学生的水平，设立了两个尖子班，把最近两次考试进前一百的学生纳入尖子班，不出所料我被分去了尖子班。那时候在初中那个班也树敌不少，分去尖子班也算是对我的一种精神解脱。然后在尖子班，认识了新的朋友，有了更有趣更有水平的老师，而且有了更优的待遇——教室首次安装空调，这在之前是没有的。这算是我初中最快乐的一个学期了，只可惜快乐的时间是短暂的，转眼就奔赴中考，各奔东西了。初中的唯一一次班级集体烧烤活动：那时候读尖子班的时候，教室电脑是可以上网的，而且外面还有一个什么学习展示机，Windows7系统的，也是联网的，也变相相当于给我们玩的电脑了。我还记得那时候吃完午饭用那个学习展示机拿来看B站视频，那时候就关注三个科技区UP，显卡姬阿狸，FUN科技，LTT。那会觉得B站是真的好啊，内容有意思，网站里面互动体验也很好，也没这么多广告，不像现在一样一坨狗屎。那时候初中老想买显卡了，当时的梦中情卡是GTX1080Ti，但是奈何没有零花钱，后面只能和家里面说好，中考完后奖励显卡——RX580，那会考完中考那天都是在宿舍收拾完东西迫不及待回家去玩显卡，在空间炫耀了一次又一次。而这张显卡一直干到了我读大学才退休。中考奖励显卡——RX580：陪我度过中考结束后快乐假期的电脑：总的来说，初中过的还是挺快乐的，不过那时候初二电脑坏了（其实是自己故意搞坏的，因为想换电脑），奶奶给了5K让我换电脑，我自己配置都找好了，当时的配置至今我都记得：CPU是AMD FX8370 显卡是RX480 内存买两条8G DDR3 1600的金士顿骇客神条，然后主板买微星的970主板。但是后面被安排去了超市自己挑，后面只能选了均衡配置最好的电脑——联想的家悦H5050。配置是 Intel Core i3 4170，4G内存，GT720 2G显卡，500G机械硬盘，系统是Windows 8.1。基本上玩不了什么游戏了。像拆开升级内存，显卡这些家里面还不给，因为保修两年，拆了就没保修了。到后面拿来带RX580的都不是这台电脑，实际上初三的时候我朋友就送了我一套板U：技嘉的A88X主板和AMD Athlon X4 880K。正巧我堂哥把他的GT730 2G换下来了。然后我拼凑拼凑吧老电脑复活了，初三打游戏就是靠的老电脑。后面为了带RX580，去本地带电脑城，买了机箱和电源，回来自己装，用880K带RX580，8G内存，虽然有点吃力，但是能玩。然后这台电脑陪我度过了考完中考的暑假，玩了刺客信条黑旗和消逝的光芒。然后随着没有作业的最爽暑假结束，我就步入高中了。初中毕业典礼：高中时期，苦逼压抑又略带快乐的时光时间：2018-2021高中刚开始，就遇到了我直到现在都关系很好的朋友，兴趣爱好和话题都很契合，在一起总有聊不完的天。不过除了他之外，整个高中期间，真正深交的朋友其实也不多。不过，相比于初中，我高中还是参加了社团的。我当时参加的是航模社，和初中一样，社团活动时间也安排在下午放学后到晚自习开始前。但是，实际上去到社团的教室，没几个人真的在做航模，而是利用社团活动教室隐蔽的环境偷偷玩手机，还有玩社团教室里面的那台电脑。印象最深刻的是，有一个高三的学长，每天必定出现在社团里面，用那台电脑玩红警。不过，航模社终归是航模社，要做航模玩，还是有会带的。于是，在老社员的指导下，我在淘宝买齐了配件，做了第一架自己的航模——纸飞机航模模型。后面还去操场试飞了，社长先试飞了一圈，确认航模没有问题后才让我操作。我操作航模转了一圈操场就降落了，得益于我经常玩GTA5开天煞撕逼，虽然是云飞行员，但是飞行的道理还是懂的，毫发无损降落了，降落后社长还夸我飞的好，第一次飞居然没有炸机。但是，有且只有这一次试飞了，因为搞完这个航模已经快期末了，要开始复习了，后面也没怎么去航模社了。当年做的航模：因为高一的时候没有月考，只有期中考和期末考，还是相对比较轻松的。不过让我最不爽的是，周六补课就算了，周日没有课还要留在学校自习一个上午，完全就是可以周六上完课直接放周末的。而且，对于那时候的我来说，多留的一个上午并没有意义，只是增加了疲劳罢了，于是我就想请假周日上午不来了，回归我初三的生活节奏：周六补课，周日休息，晚上回来晚自习。我那时候的班主任人挺好的，前面几次都给我请假了，但是后面就找我谈心，然后说要家长同意才能请假。后面因为种种原因，只持续了开学几周，后面也被迫周日留在学校自习了。因为实在没事做，基本上周日的早上大家都没什么心思学习，下课就在玩。甚至还做出自习课时间段用教室电脑看电影，然后安排同学在门口放风这种事情。不过高中那时候的尖子班分班制度有点抽象——通过入学那一个学期的期中期末成绩进行分班。刚入学那时候先通过中考成绩分成尖子班平行班，然后在分出普通班平行班，然后通过期中考试和期末考试的成绩，在高一下学期划分出尖子班，次尖班，普通班。我记得那时候尖子班是10班和12班，然后我被分到了10班。进入尖子班，明显感觉学习氛围更加浓重一些，然后感觉就是在尖子班大家都是各顾各的学习，真正能够深交的朋友没有。这种情况在分班之前的尖子班平行班里面是没有的，那时候好像是根据中考前600进行划分，我那时候中考进入前600是没有成绩的，只能知道总分A+。进入尖子班后，因为观念不合，以及平时交往比较少，我和班里面那些小仙女闹得挺不愉快的。虽然初中也有类似的情况，但是很明显高中更加严重。然后我就开始无视那几个小仙女的感受，特立独行。不过有一说一，其他大部分同学，虽然都算不上深交的朋友，但是还是算搭得上话，只是我在班里不是被完全孤立的状态。好巧不巧，当年和我有过交往的朋友，大部分都没能进入特尖班，大部分都是进入次尖班。即使有进入特尖班的，也在隔壁12班。那时候我电脑水平是班里最厉害的，各种疑难杂症都可以轻松解决，甚至高中的那时候还受邀去帮老师处理电脑问题。无非就是电脑进来一大堆流氓软件，导致电脑广告弹窗多，卡顿。我的处理手段基本上都是直接重装系统，因为这样来的更快。每个学年换教室，一打开教室的一体机总是一大堆流氓软件直接贴脸开大，我一般都是在开学第一周找个中午，直接给教室电脑重装系统，然后装成Windows8.1，因为那时候用下来Windows8.1的触控体验是最棒的。然后我一般装完系统都喜欢夹带私货——换成二次元壁纸，那时候我还喜欢洛天依（现在不喜欢了，已经成黑粉了），所以基本上都是直接换成洛天依的壁纸。开始敢重装系统也是高二的事情了，高一的时候是破开还原保护卡，然后把背景换成初音未来和镜音双子，不过一般换的壁纸都是挑那种正常的，好看，耐看的壁纸的。也正是这样，我跟那帮小仙女没少起冲突，主要也就是那几个小仙女看不惯，其他同学其实无所谓，有一些还觉得挺好看的。老师也没什么意见，毕竟我把电脑维护得这么好，也有我一份功劳，反观隔壁班电脑那可真是一天天疯狂弹广告，上课都弹广告打断上课。然后正是因为那帮小仙女看我不爽，经常就换掉我的壁纸来跟我中门对狙。因为那时候我已经会玩易语言编程了，我也不甘示弱，直接就写了一个程序锁死壁纸，换都换不掉。看到那帮小仙女试图换掉我的壁纸，刚换上，过一会又变回二次元壁纸，气得直跺脚，我就在心里暗暗高兴，毕竟那时候就她们的电脑水平和我相比，技术差了十万八千里。其实换教室电脑壁纸这种事情我初中初二就开始干了，不过那时候电脑没有网，而且学校打压偷玩电脑的行为比较严，也就没有玩的很飘。给教室电脑换壁纸（考完高考后回学校才拍的）：要说在高中事情做偷换电脑壁纸这件事，最登峰造极的，就是高一电脑课黑进老师电脑给老师电脑换壁纸了。那时候我们一周有两节电脑课，我们班很多男生都是喜欢在电脑课上面组队局域网打CS1.6。而我则喜欢在上面折腾各种奇奇怪怪的东西，包括在电脑室的电脑上运行我写的恶搞病毒。而很巧的一次机会，我认识的一个朋友，给我介绍了KaliLinux，那也是我第一次接触Linux。然后他在电脑课上当着我的面用虚拟机运行KaliLinux用永恒之蓝漏洞黑进老师的电脑，直接让我惊掉下巴。他没有告诉我具体的操作细节，那时候我也看不懂他操作命令行是干什么的，但是我得到了三个关键词——“KaliLinux”，“漏洞”，“永恒之蓝”。于是，在接下来的几个周日下午的休息时间，我都在疯狂的检索相关的资料，然后在家里面的电脑部署了环境进行相关的测试。那时候国内互联网技术方面的检索结果没有现在这么垃圾，全是CSDN这类复制粘贴，错漏百出的内容，经过不懈的努力，我终于在虚拟机里面复现了当时我朋友的操作——黑进目标虚拟机，获取命令行最高权限。然后我就开始寻思着这么给目标电脑更换壁纸，为了实现这个目标，我甚至用易语言专门写了一个程序，开机自启动，更换壁纸，然后退出。很快啊，下周的电脑课就来了，我在电脑课上行云流水完成了我“蓄谋已久”的计划：脱离电脑室控制软件控制——安装VMware Workstation虚拟机——从移动硬盘中复制事先准备好的 Kali Linux 虚拟机文件——添加虚拟机——设置网络为桥接模式——开启虚拟机——配置KaliLinux的虚拟机网络——打开命令行，启动Metasploit——选择永恒之蓝漏洞模块——设置Payload——执行exploit进行入侵——上传准备好的更换壁纸程序——进入shell——执行！由于程序没有能够立即生效，必须要重新重启过电脑才能生效，那时候比较怂，没敢直接在上课时间直接给老师电脑来个reboot，只能等下车电脑课见证效果了。第二天，我从其他班上电脑课的同学口中得知一个消息：电脑室老师的电脑壁纸被换成了初音未来的壁纸。在电脑室电脑运行洛天依病毒2.0：要说高二的时候还有哪些有趣的事情，那就是通用技术课的作业了。那时候高中的通用技术课就是教我们做些小制作，教会我们一些制作的基本技能，上这个课的老师也很有意思。那时候我做的一个作品，名称就叫做“八音盒”，为什么叫这个名字呢，因为这个作品一打开就会大声播放老八吃屎的录音，以及老八炸鸡巴的录音，当时这三个老八吃屎的视频和一个老八炸鸡巴的视频被成为“四大名著”，大概在2019年-2020年那段时间应该是很出名的。然后就是这么一个作品，在准备睡觉之前的那段时间，我在宿舍阳台以最大音量把“四大名著”都播放了一遍，不但引来了大量的笑声，有人跟着起哄，还引来了宿管（X“八音盒”实物（看着就应该知道这玩意有多大声了）：到了高三，因为学习压力瞬间上来了，也没再整什么活了，都是苦中作乐的日子，没什么好说的。那时候倒是整活买了烧杯和锥形瓶用来喝水，然后锥形瓶上课的时候还被化学老师借去当教具了，其他就没什么了。很快转眼高三就过去了，大家各奔东西，虽然现在看是转眼就过去了，但是当时这一年可是真的难熬，整体感觉非常压抑。而压抑的心情，没有随着高考的结束而结束——因为高考考砸了。本来应该是快乐的三个月暑假，成了整体以泪洗面，后悔，懊恼，失望的一个假期。后面知道成绩填志愿的时候，虽然明知道已经上不了想要的大学了，但是还是最后的倔强把之前模拟考成绩能上的大学填在了第一个位置。而经过苦苦的等待，得到的却只是广西某211的录取通知书，哎，还真是被理综试卷50条减速带留在了广西大山。假期的时候，也有老师关心来询问过情况，老师的建议是复读，因为这是很明显考砸了，和正常表现差别太大，家里面也是同样建议我去复读。但是，我那时候已经心灰意冷，内心已经受不了再经历一年的折磨了，甚至我当时放出狠话：复读可以，如果复读后还是考不上985，我就从学校门楼直接跳下自杀。当时我也是做好了思想准备，如果复读真上不了，就付诸行动。后面家里面妥协，让我去读大学了，虽然是破211，但是好歹也是个211，还是比普通的一本要好的，如果只有普通一本那我真要复读了。就这样，我带着不甘与遗憾，踏入了大学的校门。高三教室一角：学校内景：高考结束后空荡荡的校园：高三宿舍的《打了300年史莱姆，不知不觉就满级了》挂画（那时候刚出第一季，现在已经出第二季了，时隔四年，又是毕业季，真巧啊）：高三宿舍门口贴的纱世里：高考结束后即将各奔东西的宿舍：高中宿舍楼内景：大学时期，血洗高考耻辱，努力学习时间：2021-至今（博客发布时间）因为高考留下心理创伤的原因，我一直认为对比我以前身边的人，他们个个考上985，就我我低人一等，留在了广西，读着广西某211，我便不愿意和他们再交流，只想自己努力争取好成绩，然后可以顺利保研。有人或许想问，不是还有考研吗？但是，这种一考定终生的考试我可不想再来体验一次了，已经很PTSD了。我当时做的打算是，争取保研，保得上就去读，保不上就拉倒。因此，为了这个目标，我几乎就是班里面最卷的那个人——设计类课程我会主动请教老师以完善设计，考试类课程则会提前几周开始认真复习，课程作业花了大量时间认真完成。各种竞赛，只要能抽出时间，必定参加。因为忙的比较多，以至于现在甚至患上了电子阳痿——游戏都提不起兴趣，不想玩——这可是我初中和高中想都不敢想的事情，那时候真的是有时间就疯玩游戏。而现在，我的休闲时间有了新的“兴趣爱好”来消遣——服务器，网站，博客，Blender... 正是由于这样的投入，我在大学期间取得了不少较为丰硕的成果。在大学，大家的活动更加自由——不需要天天在一起。我可以只和聊得来的人交往，而不想见的人可以做到避之不见。也正是这样的社交环境，让我的社交压力小了不少，虽然我混的大部分都是小众圈子，但是好在能遇到真正的挚友。也是上了大学第一次体验到拥有真正的挚友可以经常交流是什么感觉，简直比中学时代好太多！朋友不求多，但是要求真。这是这么多年社交体验下来，我觉得最真实的感受。上了大学，有了自己的财政自由，我便可以省钱来买各式各样的东西，外加上可以获得奖学金，比赛奖金，我可以买的就更多了。我为了自己兴趣爱好买到东西，还真挺多的：各种角色的手办，fufu，各种捡垃圾电脑硬件，机架式服务器（这可是我高中时最想要的东西，现在这篇博客就是在这台机架式服务器上面写的，我买来当电脑用的），RTX2080Ti（这可是我高中时宛如虚空，不可得到，只能看别人测评馋得流口水的显卡），触摸屏的笔记本电脑，功放，游戏等等，太多了。配了一台捡垃圾层面真正意义的高配电脑（在我别的博客文章可以知道这个电脑的配置）。而且，虽然一直感觉广西某211档次低，宿舍烂，饭堂比外面贵还难吃，但是实打实还是有值得夸赞的地方的。例如，宿舍不限电不断电，这点已经爆杀很多大学了。虽然说还是过晚上12点断网，第二天6点才恢复网络，但是，但是，但是，宿舍有公网IP！！！如果你是一个搞计算机网络相关技术的人，肯定知道这意味着什么，公网IP很多地方宽带都是非常难求，而这个学校宿舍网络，拨号即送！我自己实测下来，搭建NAS什么的都是没问题的，而且打游戏延迟什么的也非常低。不过要达成这样的网络环境是有前提的，等我毕业了我会专门写一篇文章分享出来。在上大学期间，也是第一次被朋友带去逛了漫展，虽然去了几次就不是很想去了。因为我感觉国内的漫展更像是一群人搞个场地，各自cosplay自己想cosplay的角色，搞个party而已，去那里也就是看看coser，门票还很贵，基本上50起步，漫展上买的东西也不便宜。最主要的是，国内二游角色占据了主导地位，特别是原神，导致真的cosplay番剧角色的coser相对就比较少，对于正统二次元的吸引力就没这么大了。在大学期间，也跟朋友去了广州玩，去逛漫展，去逛景点，去面基网友，这也是我人生第一次自己和别人出远门。还有就是去参加比赛然后得以去重庆进行公费旅游，可惜在重庆的那个网友离得太远了，要不就杀过去面基了。后面在重庆公费旅游的两天去随便逛了一些景点，逛了一下动物园。但是，总体来说，大学更加平平淡淡，但是更加轻松愉快，以至于我现在想不出来有什么特别想写的经历。又或许，这种感想和感受是需要时间积淀的，而大学的生活离现在还是太近了，触手可及，没有距离感。总结碎碎念一下呀，不知不觉到这里居然写了一万多字，可真是比课程论文都要长了。顺着思路写完了各个时段的回忆录，竟然不记得一开始想要写什么了。现在想想，好像生活就是这样，有一些时期没办法追求完美，或者既要又要，我全都要。有一些事情必须要做出取舍，有一些遗憾是必须要留下的。而有一些遗憾，或者是大部分遗憾，是没办法弥补的。同一个东西，几年前拿到和现在拿到，意义是完全不一样的。就像我的RTX2080Ti一样，如果是高中2018年那会拿到，意义肯定是要大于上了大学，读了三年后，2024年才拿到，还是以原价的1.5折拿到的，如果考虑到通货膨胀，实际价格会更低。尽管从物质角度看，结果是一样的——手里多了一块 RTX 2080 Ti 显卡，但是精神层面的感受则是截然不同的。所以，我觉得在以后的生活中，如果条件允许，适当去追求一下自己想要的东西又何尝不可，尽可能不给自己留下遗憾。要是说当年想要的东西，可以等到后天有能力后再搞到手，稍微弥补一下当年的遗憾。而有一些非物质层面的东西，如果当年不去做，过了那个时间节点，那就真的没有任何办法补偿了。所以，时间不等人，Just DO IT !眼看距离高考失利已经快要过去四年了，现在还有不到一个月又要高考了，当时也被安慰说时间会冲淡一切，我还是想对当年的自己说：“没关系，你已经尽力了。人生应该是享受的，不应该是被折磨的，不喜欢就不做了，多做自己喜欢的事情。”和挫败和解，和过去和解。尽管过去的生活，有快乐，有痛苦，有扎心，但是都过去了，即使你想再重新体验一次，也是不可能了。前段时间回家，坐着公交车路过初中时期搬迁后的校区，校长还没有兑现他的诺言，让大家搬进新建校区。熟悉的街道，熟悉的校服，熟悉的校门口，熟悉的广播铃声，仿佛初中生活还是昨天，然而透过熄屏的手机屏幕的反光，赫然是一个年轻的小伙子了，已经不是当年青涩的初中生了。看着放学后嘻嘻哈哈打闹的初中生，校门口繁忙的交通，一种伤感莫名涌上心头——青春完结撒花。但是，回忆过去并没有什么用，只会让自己体会到时间飞快的流逝，带来无比的伤感。是时候该检查检查人生的行囊，合上一路以来的记忆回忆录，为了新的目标继续奋斗了。毕竟，未来的机遇与挑战，远比过去的成就与经历更加重要。我也只是最近几天记忆的回忆录被莫名其妙的翻开，不知不觉中回想起过去的事情，回忆有感，写下了这篇有感而发的文章罢了。是时候，让这些记忆继续沉睡在心底了。如果你能完整的看完我写的文章，那我还是要向你说声谢谢，感谢你愿意花宝贵的时间倾听我的故事。如果你也有感而发，也欢迎在评论区分享你的经历哦。（其实但凡是初中或者高中和我同班的，应该立马知道我是谁了，毕竟那时候我在班里刷存在感还是很上分的。）
二次开发 Typecho 后台服务器状态查看插件
发现Typecho后台服务器状态插件在翻Typecho插件的时候，我偶然发现了两双筷子写的Typecho后台服务器状态查看插件，于是就尝试了一下下。这个插件在桌面端网页没问题，感觉还挺不错的。但是一旦变成移动端，后台的布局就会出现问题，逼死强迫症。而且，CPU利用率有时候还会变成0.9999999999%这种离谱的数据，我寻思着就不能取两位小数好看一点吗？于是，我决定，自己修改代码，把服务器信息移动到页面底部的位置，并且修改显示数据的逻辑，让信息看着更加舒适一点。（其实也不完全是我写，是面向LLM编程 X）修改代码的过程首先，需要把注册方法的地方改为页面底部Typecho_Plugin::factory('admin/footer.php')->begin = array('LinuxInfo_Plugin', 'render');然后就是修改一下插入的样式：echo <<<STR $info_str STR;因为我觉的系统信息用鼠标悬浮预览不明显，于是修改为直接独立一行把信息全部展示：return <<<SYS_INFO 服务器信息: 持续运行：{$uptime}，Web服务器：{$_SERVER['SERVER_SOFTWARE']}，PHP 版本：{$php['version']}，SAPI 接口：{$php['sapi']}，PHP内存限制：{$php['mem_max']}，上传文件限制：{$php['up_max']} SYS_INFO;以及让LLM帮忙完善了CPU信息获取的逻辑：private static function cpu_info(){ function GetCoreInformation() { $data = file('/proc/stat'); $cores = []; foreach( $data as $line ) { if( preg_match('/^cpu[0-9]/', $line) ) { $info = explode(' ', $line); $cores[] = [ 'user' => $info[1], 'nice' => $info[2], 'sys' => $info[3], 'idle' => $info[4], 'iowait' => $info[5], 'irq' => $info[6], 'softirq' => $info[7] ]; } } return $cores; } function GetCpuPercentages($stat1, $stat2) { if(count($stat1) !== count($stat2)){ return; } $cpus = []; for( $i = 0, $l = count($stat1); $i < $l; $i++) { $dif = []; $dif['user'] = $stat2[$i]['user'] - $stat1[$i]['user']; $dif['nice'] = $stat2[$i]['nice'] - $stat1[$i]['nice']; $dif['sys'] = $stat2[$i]['sys'] - $stat1[$i]['sys']; $dif['idle'] = $stat2[$i]['idle'] - $stat1[$i]['idle']; $dif['iowait'] = $stat2[$i]['iowait'] - $stat1[$i]['iowait']; $dif['irq'] = $stat2[$i]['irq'] - $stat1[$i]['irq']; $dif['softirq'] = $stat2[$i]['softirq'] - $stat1[$i]['softirq']; $total = array_sum($dif); $cpu = []; foreach($dif as $x=>$y) $cpu[$x] = round($y / $total * 100, 2); $cpus['CPU' . $i] = $cpu; } return $cpus; } $stat1 = GetCoreInformation(); sleep(1); $stat2 = GetCoreInformation(); $data = GetCpuPercentages($stat1, $stat2); $overall_cpu_usage = 0; $core_details = []; foreach ($data as $core => $usage) { $core_usage = 100 - $usage['idle']; $overall_cpu_usage += $core_usage; $core_details[] = "{$core}: " . number_format($core_usage, 2) . "%"; } $overall_cpu_usage = round($overall_cpu_usage / count($data), 2); $core_details_str = implode(", ", $core_details); $sys_load = sys_getloadavg(); $sys_load_1min = number_format($sys_load[0], 3); $sys_load_5min = number_format($sys_load[1], 3); $sys_load_15min = number_format($sys_load[2], 3); return <<<CPU_INFO CPU: {$overall_cpu_usage} % CPU_INFO; }原来的逻辑只是判断单个CPU的占有率，当CPU数量为多个的时候就会不准确，这里我优化成了整体利用率，并且悬停时会显示每个CPU的占用率信息。改造效果虽然不是彻底重构，但是我对改造后的插件还是比较满意的。手机的网页端也舒服了。修改后的插件分享修改后的插件已经放到了GitHub：Typecho-LinuxInfo反正源码也不多，我这里也可以直接放出来：Plugin.php:<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; /** * 查看系统信息，仅限 Linux 系统，基于两双筷子的插件进行二次开发，使用帮助 * * @package LinuxInfo * @author GTX690战术核显卡导弹 * @version 1.1.0 * @link https://www.nekopara.uk */ class LinuxInfo_Plugin implements Typecho_Plugin_Interface { // 插件名称 protected static $plugin_name = 'LinuxInfo'; /** * 激活插件方法,如果激活失败,直接抛出异常 * * @access public * @return void * @throws Typecho_Plugin_Exception */ public static function activate() { // 如果当前系统不是 linux 则抛出错误 if(PHP_OS != 'Linux') { throw new Typecho_Plugin_Exception(_t("此插件仅支持 Linux 系统")); } // 在后端页面底部 注册方法 Typecho_Plugin::factory('admin/footer.php')->begin = array('LinuxInfo_Plugin', 'render'); } /** * 禁用插件方法,如果禁用失败,直接抛出异常 * * @static * @access public * @return void * @throws Typecho_Plugin_Exception */ public static function deactivate(){} /** * 获取插件配置面板 * * @access public * @param Typecho_Widget_Helper_Form $form 配置面板 * @return void */ public static function config(Typecho_Widget_Helper_Form $form) { /** 分类名称 */ $show_item = array( 'cpu_info' => _t('CPU使用情况') . '（ CPU 使用率、系统负载）', 'mem_info' => _t('内存使用情况') . '（内存使用率、SWAP交换分区）', 'disk_info' => _t('磁盘使用情况') . '（磁盘使用率、磁盘已用空间、磁盘总空间）', 'sys_info' => _t('服务器信息') . ' （系统运行时间、服务器软件、PHP 版本、SAPI 接口、PHP 内存限制、上传文件限制）', ); // 设置默认选中 $show_option_value = ['cpu_info','mem_info','disk_info']; $ShowInfo = new Typecho_Widget_Helper_Form_Element_Checkbox('show_items',$show_item, $show_option_value, _t('选择显示系统信息')); $form->addInput($ShowInfo->multiMode()); $separator = new Typecho_Widget_Helper_Form_Element_Text('separator', NULL, ' ★ ', _t('分隔符'), '设置数据项分割字符'); $form->addInput($separator); } /** * 个人用户的配置面板 * * @access public * @param Typecho_Widget_Helper_Form $form * @return void */ public static function personalConfig(Typecho_Widget_Helper_Form $form){} /** * 插件实现方法 * * @access public * @return void */ public static function render() { $show_items = Typecho_Widget::widget('Widget_Options')->plugin(self::$plugin_name)->show_items; $separator = Typecho_Widget::widget('Widget_Options')->plugin(self::$plugin_name)->separator; // 获取 linux 系统消息 $info_str = ''; $show_items_max = count($show_items) - 1; foreach ($show_items as $show_item_k => $show_item) { $method = $show_item; if (method_exists(__CLASS__, $method)) { $info_str .= self::$method(); } if ($show_items_max != $show_item_k) { $info_str .= $separator; } } echo <<<STR $info_str STR; } // 获取CPU信息 private static function cpu_info(){ function GetCoreInformation() { $data = file('/proc/stat'); $cores = []; foreach( $data as $line ) { if( preg_match('/^cpu[0-9]/', $line) ) { $info = explode(' ', $line); $cores[] = [ 'user' => $info[1], 'nice' => $info[2], 'sys' => $info[3], 'idle' => $info[4], 'iowait' => $info[5], 'irq' => $info[6], 'softirq' => $info[7] ]; } } return $cores; } function GetCpuPercentages($stat1, $stat2) { if(count($stat1) !== count($stat2)){ return; } $cpus = []; for( $i = 0, $l = count($stat1); $i < $l; $i++) { $dif = []; $dif['user'] = $stat2[$i]['user'] - $stat1[$i]['user']; $dif['nice'] = $stat2[$i]['nice'] - $stat1[$i]['nice']; $dif['sys'] = $stat2[$i]['sys'] - $stat1[$i]['sys']; $dif['idle'] = $stat2[$i]['idle'] - $stat1[$i]['idle']; $dif['iowait'] = $stat2[$i]['iowait'] - $stat1[$i]['iowait']; $dif['irq'] = $stat2[$i]['irq'] - $stat1[$i]['irq']; $dif['softirq'] = $stat2[$i]['softirq'] - $stat1[$i]['softirq']; $total = array_sum($dif); $cpu = []; foreach($dif as $x=>$y) $cpu[$x] = round($y / $total * 100, 2); $cpus['CPU' . $i] = $cpu; } return $cpus; } $stat1 = GetCoreInformation(); sleep(1); $stat2 = GetCoreInformation(); $data = GetCpuPercentages($stat1, $stat2); $overall_cpu_usage = 0; $core_details = []; foreach ($data as $core => $usage) { $core_usage = 100 - $usage['idle']; $overall_cpu_usage += $core_usage; $core_details[] = "{$core}: " . number_format($core_usage, 2) . "%"; } $overall_cpu_usage = round($overall_cpu_usage / count($data), 2); $core_details_str = implode(", ", $core_details); $sys_load = sys_getloadavg(); $sys_load_1min = number_format($sys_load[0], 3); $sys_load_5min = number_format($sys_load[1], 3); $sys_load_15min = number_format($sys_load[2], 3); return <<<CPU_INFO CPU: {$overall_cpu_usage} % CPU_INFO; } // 获取内存信息 private static function mem_info(){ if (false === ($str = @file("/proc/meminfo"))) return false; $str = implode("", $str); preg_match_all("/MemTotal\s{0,}\:+\s{0,}([\d\.]+).+?MemFree\s{0,}\:+\s{0,}([\d\.]+).+?Cached\s{0,}\:+\s{0,}([\d\.]+).+?SwapTotal\s{0,}\:+\s{0,}([\d\.]+).+?SwapFree\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $buf); preg_match_all("/Buffers\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $buffers); $mem_info['memTotal'] = round($buf[1][0]/1024, 2); $mem_info['memFree'] = round($buf[2][0]/1024, 2); $mem_info['memBuffers'] = round($buffers[1][0]/1024, 2); $mem_info['memCached'] = round($buf[3][0]/1024, 2); $mem_info['memUsed'] = $mem_info['memTotal']-$mem_info['memFree']; $mem_info['memPercent'] = (floatval($mem_info['memTotal'])!=0)?round($mem_info['memUsed']/$mem_info['memTotal']*100,2):0; $mem_info['memRealUsed'] = $mem_info['memTotal'] - $mem_info['memFree'] - $mem_info['memCached'] - $mem_info['memBuffers']; //真实内存使用 $mem_info['memRealFree'] = $mem_info['memTotal'] - $mem_info['memRealUsed']; //真实空闲 $mem_info['memRealPercent'] = (floatval($mem_info['memTotal'])!=0)?round($mem_info['memRealUsed']/$mem_info['memTotal']*100,2):0; //真实内存使用率 $mem_info['memCachedPercent'] = (floatval($mem_info['memCached'])!=0)?round($mem_info['memCached']/$mem_info['memTotal']*100,2):0; //Cached内存使用率 $mem_info['swapTotal'] = round($buf[4][0]/1024, 2); $mem_info['swapFree'] = round($buf[5][0]/1024, 2); $mem_info['swapUsed'] = round($mem_info['swapTotal']-$mem_info['swapFree'], 2); $mem_info['swapPercent'] = (floatval($mem_info['swapTotal'])!=0)?round($mem_info['swapUsed']/$mem_info['swapTotal']*100,2):0; $mem_per = round($mem_info['memRealUsed'] / $mem_info['memTotal'] * 100, 2); return <<<MEM_INFO 内存: {$mem_per} % MEM_INFO; } // 获取磁盘信息 private static function disk_info(){ $dt = round(@disk_total_space(".")/(1024*1024*1024), 2); //总 $df = round(@disk_free_space(".")/(1024*1024*1024), 2); //可用 $du = $dt-$df; //已用 $disk_use = round($du / $dt * 100, 2); return <<<DISK_INFO 硬盘: {$disk_use} % DISK_INFO; } // 获取 系统信息 private static function sys_info() { if (false === ($str = @file("/proc/uptime"))) return false; $str = explode(" ", implode("", $str)); $str = trim($str[0]); $min = $str / 60; $hours = $min / 60; $days = floor($hours / 24); $hours = floor($hours - ($days * 24)); $min = floor($min - ($days * 60 * 24) - ($hours * 60)); if ($days !== 0) $uptime = $days."天"; if ($hours !== 0) $uptime .= $hours."小时"; $uptime .= $min."分钟"; $php['version'] = PHP_VERSION; $php['sapi'] = php_sapi_name(); $php['mem_max'] = get_cfg_var('memory_limit'); $php['up_max'] = get_cfg_var('upload_max_filesize'); return <<<SYS_INFO 服务器信息: 持续运行：{$uptime}，Web服务器：{$_SERVER['SERVER_SOFTWARE']}，PHP 版本：{$php['version']}，SAPI 接口：{$php['sapi']}，PHP内存限制：{$php['mem_max']}，上传文件限制：{$php['up_max']} SYS_INFO; } }优先使用Github上面的吧，万一那天我修复一些问题，可能只会修改Github上面的仓库的内容而忘记修改这篇文章。当然，我希望以后不会出现问题。使用方法将GitHub上面的Plugin.php或者这个页面的Plugin.php内容保存下载，放到Typecho的插件目录usr/plugins/下的LinuxInfo文件夹（需要自己创建），例如：typecho/usr/plugins/LinuxInfo/Plugin.php，进入Typecho后台启用插件即可。默认不显示系统详细信息，需要自己勾选显示才会展示。不过，需要注意的是，这个插件仅可用于Linux系统，Windows系统不适用。谁用Windows做服务器啊？！！不会吧不会吧？最后还是要感谢一下两双筷子写出来这个有意思的插件。
AlamLinux 8.10 部署 LNMP 环境并安装 NextCloud如果说希望自己的手机可以有一个app自动同步照片到NAS，那NextCloud就是一个不错的选择。虽然服务端确实是臃肿了一些，但是如果只是拿来同步个照片问题不大。我前前后后折腾了一天，后面又在虚拟机上部署失败了四次，终于算摸索出来了。写一篇博客记录一下折腾过程，供日后参考。部署目标：目标操作系统：AlmaLinux8.10。使用Nginx作为Web服务器，MariaDB作为数据库，PHP作为后端部署Nextcloud。NextCloud本体放在/opt/nextcloud/目录下，数据文件存放在RAID1的硬盘目录/mnt/3T-RAID1/nextcloud_data/下。前置任务：安装LNMP环境NextCloud的运行依赖于Nginx(或者Apache)，MariaDB(或者MySQL)，PHP，这些软件一般是部署在Linux上，所以缩写为LNMP环境。如果是经常搞网站的，对这个环境应该挺熟悉的吧，不熟悉就是用宝塔用的（配置Nginx安装Nginx：yum install -y nginx创建Nginx配置文件/etc/nginx/conf.d/nextcloud.conf：upstream php-handler { server unix:/run/php-fpm/www.sock; } # Set the `immutable` cache control options only for assets with a cache busting `v` argument map $arg_v $asset_immutable { "" ""; default ", immutable"; } server { listen 80; listen [::]:80; server_name nextcloud.nekopara.uk; # Prevent nginx HTTP Server Detection server_tokens off; # Enforce HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; # With NGinx >= 1.25.1 you should use this instead: # listen 443 ssl; # listen [::]:443 ssl; # http2 on; server_name nextcloud.nekopara.uk; # Path to the root of your installation root /opt/nextcloud; # Use Mozilla's guidelines for SSL/TLS settings # https://mozilla.github.io/server-side-tls/ssl-config-generator/ ssl_certificate /opt/certs/nekopara.uk.pem; ssl_certificate_key /opt/certs/nekopara.uk.key; # Prevent nginx HTTP Server Detection server_tokens off; # HSTS settings # WARNING: Only add the preload option once you read about # the consequences in https://hstspreload.org/. This option # will add the domain to a hardcoded list that is shipped # in all major browsers and getting removed from this list # could take several months. #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # set max upload size and increase upload timeout: client_max_body_size 512M; client_body_timeout 300s; fastcgi_buffers 64 4K; # Enable gzip but do not remove ETag headers gzip on; gzip_vary on; gzip_comp_level 4; gzip_min_length 256; gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; # Pagespeed is not supported by Nextcloud, so if your server is built # with the `ngx_pagespeed` module, uncomment this line to disable it. #pagespeed off; # The settings allows you to optimize the HTTP2 bandwidth. # See https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/ # for tuning hints client_body_buffer_size 512k; # HTTP response headers borrowed from Nextcloud `.htaccess` add_header Referrer-Policy "no-referrer" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Permitted-Cross-Domain-Policies "none" always; add_header X-Robots-Tag "noindex, nofollow" always; add_header X-XSS-Protection "1; mode=block" always; # Remove X-Powered-By, which is an information leak fastcgi_hide_header X-Powered-By; # Set .mjs and .wasm MIME types # Either include it in the default mime.types list # and include that list explicitly or add the file extension # only for Nextcloud like below: include mime.types; types { text/javascript mjs; application/wasm wasm; } # Specify how to handle directories -- specifying `/index.php$request_uri` # here as the fallback means that Nginx always exhibits the desired behaviour # when a client requests a path that corresponds to a directory that exists # on the server. In particular, if that directory contains an index.php file, # that file is correctly served; if it doesn't, then the request is passed to # the front-end controller. This consistent behaviour means that we don't need # to specify custom rules for certain paths (e.g. images and other assets, # `/updater`, `/ocs-provider`), and thus # `try_files $uri $uri/ /index.php$request_uri` # always provides the desired behaviour. index index.php index.html /index.php$request_uri; # Rule borrowed from `.htaccess` to handle Microsoft DAV clients location = / { if ( $http_user_agent ~ ^DavClnt ) { return 302 /remote.php/webdav/$is_args$args; } } location = /robots.txt { allow all; log_not_found off; access_log off; } # Make a regex exception for `/.well-known` so that clients can still # access it despite the existence of the regex rule # `location ~ /(\.|autotest|...)` which would otherwise handle requests # for `/.well-known`. location ^~ /.well-known { # The rules in this block are an adaptation of the rules # in `.htaccess` that concern `/.well-known`. location = /.well-known/carddav { return 301 /remote.php/dav/; } location = /.well-known/caldav { return 301 /remote.php/dav/; } location /.well-known/acme-challenge { try_files $uri $uri/ =404; } location /.well-known/pki-validation { try_files $uri $uri/ =404; } # Let Nextcloud's API for `/.well-known` URIs handle all other # requests by passing them to the front-end controller. return 301 /index.php$request_uri; } # Rules borrowed from `.htaccess` to hide certain paths from clients location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } # Ensure this block, which passes PHP files to the PHP process, is above the blocks # which handle static assets (as seen below). If this block is not declared first, # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` # to the URI, resulting in a HTTP 500 error response. location ~ \.php(?:$|/) { # Required for legacy support rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode(_arm64)?\/proxy) /index.php$request_uri; fastcgi_split_path_info ^(.+?\.php)(/.*)$; set $path_info $fastcgi_path_info; try_files $fastcgi_script_name =404; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $path_info; fastcgi_param HTTPS on; fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice fastcgi_param front_controller_active true; # Enable pretty urls fastcgi_pass php-handler; fastcgi_intercept_errors on; fastcgi_request_buffering off; fastcgi_max_temp_file_size 0; } # Serve static files location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ { try_files $uri /index.php$request_uri; # HTTP response headers borrowed from Nextcloud `.htaccess` add_header Cache-Control "public, max-age=15778463$asset_immutable"; add_header Referrer-Policy "no-referrer" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Permitted-Cross-Domain-Policies "none" always; add_header X-Robots-Tag "noindex, nofollow" always; add_header X-XSS-Protection "1; mode=block" always; access_log off; # Optional: Don't log access to assets } location ~ \.(otf|woff2?)$ { try_files $uri /index.php$request_uri; expires 7d; # Cache-Control policy borrowed from `.htaccess` access_log off; # Optional: Don't log access to assets } # Rule borrowed from `.htaccess` location /remote { return 301 /remote.php$request_uri; } location / { try_files $uri $uri/ /index.php$request_uri; } }配置文件中的主机名要改为你实际的域名，证书位置改为你实际证书放置的目录。测试没问题后，就启动Nginx：nginx -t systemctl enable --now nginx配置PHP安装PHP以及NextCloud需要的模块，由于需要使用PHP8.2，所以需要添加Remi仓库后再安装：yum install https://rpms.remirepo.net/enterprise/remi-release-8.rpm -y dnf module reset php -y dnf module enable php:remi-8.2 -y dnf install -y php php-cli php-gd php-mbstring php-intl php-pecl-apcu php-mysqlnd php-opcache php-json php-zip配置/etc/php.ini，查找配置并修改为以下内容：memory_limit = 512M upload_max_filesize = 500M post_max_size = 600M max_execution_time = 300启动PHP：systemctl enable --now php-fpm配置MariaDB安装MariaDB：yum install mariadb -y安装完成后，启动数据库，设置NextCloud的数据库：systemctl enable --now mariadb mysql -u root -p默认没有密码，直接回车就进数据库了，然后开始配置：CREATE DATABASE netxcloud_db; CREATE USER nextcloud_user@localhost IDENTIFIED BY 'nextcloud_passwd'; GRANT ALL PRIVILEGES ON netxcloud_db.* TO nextcloud_user@localhost; FLUSH PRIVILEGES;数据库名称，用户，密码，自己看着改。至此，前置任务已完成，让我们来开始安装NextCloud。安装NextCloud下载NextCloud并解压，我用地是28.0.14版本：cd /opt wget https://download.nextcloud.com/server/releases/nextcloud-28.0.14.zip unzip nextcloud-28.0.14.zip配置SELinux允许NextCloud进行文件读写操作：semanage fcontext -a -t httpd_sys_rw_content_t "/opt/nextcloud(/.*)?" semanage fcontext -a -t httpd_sys_rw_content_t '/opt/nextcloud/data(/.*)?' semanage fcontext -a -t httpd_sys_rw_content_t '/opt/nextcloud/config(/.*)?' semanage fcontext -a -t httpd_sys_rw_content_t '/opt/nextcloud/apps(/.*)?' semanage fcontext -a -t httpd_sys_rw_content_t '/opt/nextcloud/.htaccess' semanage fcontext -a -t httpd_sys_rw_content_t '/opt/nextcloud/.user.ini' semanage fcontext -a -t httpd_sys_rw_content_t '/opt/nextcloud/3rdparty/aws/aws-sdk-php/src/data/logs(/.*)?' restorecon -Rv '/opt/nextcloud/'设置必要的SELinux规则：setsebool -P httpd_can_connect_ldap on setsebool httpd_unified on setsebool -P httpd_can_network_connect_db on setsebool -P httpd_can_network_connect on setsebool -P httpd_can_network_memcache on setsebool -P mysql_connect_http on设置文件权限：chown -R apache:apache /opt/nextcloud chmod -R 775 /opt/nextcloud同理，设置数据目录权限：semanage fcontext -a -t httpd_sys_rw_content_t "/mnt/3T-RAID1/nextcloud_data(/.*)?" restorecon -Rv /mnt/3T-RAID1/nextcloud_data chown -R apache:apache /mnt/3T-RAID1/nextcloud_data chmod -R 775 /mnt/3T-RAID1/nextcloud_data配置NextCloud在浏览器输入你的域名，按理来说应该可以正常打开NextCloud的安装界面，按照提示设置用户名，密码，文件目录，数据库，安装完成后就应该是可以正常使用的了。