相信大家或多或少体验过大模型的魅力,有一定门槛的chatGPT(包含各种套壳的chat_bot),还有文心、通义千问等等。我总结有以下小缺陷

  1. 知识库有截止时间,比如GPT当前在21年9月
  2. 生成代码场景需要在本地手动执行、验证,反复贴报错最终得到一份可用的代码
  3. 无法理解私域任务,比如你们公司每天要做服务器安全巡检
  4. 准确度,在一些计算场景会乱答

打个比方大模型是大脑,AI agent给了模型“双手”。常见的autoGPT、babyGPT、本文介绍来自微软的autogen (https://microsoft.github.io/autogen/)
autogen官网.jpg

代码执行
先看效果,“请求https://www.baidu.com 50次,2秒间隔,记录每次的状态码、网络延迟,结果记录下来。并且生成图片,保存到当前目录下”

- 阅读剩余部分 -

近期在尝试大数据在企业内的应用,两个需求:

  • 用户输入自然语言后返回代码模版,最好能够进一步推理
  • 用户输入自然语言返回给定答案,不要扩展

两种方向:向量库+大模型、模型微调。
方向选择.jpg

以下给出openai模型微调的详细过程,目前官方推荐gpt-3.5-turbo,gpt4的微调将在年底推出

  • 数据预处理:准备至少10条数据,质量越高且数量越多,效果越好。如果没有就人工老老实实的标记几十条高质量数据,比大量低质数据更好。格式如下
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}

我的数据源是csv,第一列问题,第二列答案,用以下脚本处理

import pandas as pd
import json


def convert_csv_to_jsonl(input_csv, output_jsonl):
    # Read the CSV file
    df = pd.read_csv(input_csv)

    with open(output_jsonl, 'w', encoding='utf-8') as f:
        for _, row in df.iterrows():
            jsonl_data = {
                "messages": [
                    {"role": "system", "content": "SunSun is an internal knowledge base communication robot."},
                    {"role": "user", "content": row['Generated Questions']},
                    {"role": "assistant", "content": row['source']}
                ]
            }
            f.write(json.dumps(jsonl_data, ensure_ascii=False) + '\n')


# Usage
# convert_csv_to_jsonl('path_to_your_csv_file.csv', 'desired_output_file.jsonl')
if __name__ == "__main__":
    convert_csv_to_jsonl('/Users/jixing/Downloads/export_result0925.csv',
                         '/Users/jixing/Downloads/export_result0925.jsonl')
  • 上传文件至openai
import openai

# 替换你的key
openai.api_key = "sk-40LIdYxxxxxxx"
training_file = openai.File.create(
    file=open("export_result0925.jsonl", "rb"),
    purpose='fine-tune'
)
# 记录文件id,下一步需要使用
print(training_file.id)
  • 开始微调
import openai

# 你的key
openai.api_key = "sk-40LIdYIwxxxxx"

# 刚才的文件id
openai.FineTuningJob.create(training_file="file-0ACDKAM7xxxxxx", model="gpt-3.5-turbo")

    最近在看大模型和运维行业的关联,初步想法是标记监控数据,配合混沌工程,给出故障数据进行多元线性回归,根据最佳曲线来预测故障。实际进行过程中发现困难重重,还在尝试标记数据。
    最近有个很火的词儿叫“数字孪生”,又叫数字骨灰盒:),大意是通过大量的文字痕迹训练已有模型,让模型从“扮演”到“重塑”你。受启发于 https://greatdk.com/1908.html,并做了些许优化,效果还是挺好玩的,或许这才是数字世界的你?!😄

看疗效
微调前1.jpg微调前2.jpg!微调.jpg
思路步骤:

  • 使用wechatExporter导出微信聊天记录,纯文本格式
  • 手动挑选适合训练的数据,对聊天记录众多的群聊进行排除
  • 自动数据清洗,合并聊天记录,记录历史
  • 使用ChatGLM2进行微调、推演
  • 启动web_demo就可以体验了😄

优化项目:

  • 兼容一问一答外,大多数人的聊天习惯是连续发出多条信息,当然我们回消息也可能是多条。比如张三问,1明天有空没? 2我想找你喝点 3别带媳妇,我回复:1有呀 2必须喝白的 3当然不带 4哈哈哈。我做了合并最终效果,
    {"prompt": "明天有空没?,我想找你喝点,别带媳妇", "response": "有呀,必须喝白的,当然不带,哈哈哈", "history": []}
  • 保留历史会话,沟通都是有上下文的,我这里简单粗暴的认为当天的会话都有关联,记录在history中
    {"prompt": "我去找你?", "response": "你开车了没", "history": [["?", "?"], ["忙完了", "怎么说"], ["吃饭打台球?", "行"]]}

python清洗脚本

import os
import re
import json

# 定义源文件夹和目标文件
source_folder = '/Users/jixing/Downloads/wechat_history'
output_file_path = '/Users/jixing/Downloads/0811output.txt'


# Regular expression patterns for extracting dates, usernames, and messages
date_pattern = re.compile(r"\((\d{4}-\d{2}-\d{2}) \d{2}:\d{2}:\d{2}\)")
user_msg_pattern = re.compile(r"^(.+?) \(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\):(.+)$")

# 遍历文件夹中的所有.txt文件
for filename in os.listdir(source_folder):
    if filename.endswith('.txt'):
        with open(os.path.join(source_folder, filename), 'r', encoding='utf-8') as source_file:
            content = source_file.readlines()

            # Parsing the chat data
            conversations = []
            current_date = None
            current_convo = []
            for line in content:
                # Check for date
                date_match = date_pattern.search(line)
                if date_match:
                    date = date_match.group(1)
                    if current_date != date and current_convo:
                        conversations.append(current_convo)
                        current_convo = []
                    current_date = date

                # Extracting user and message
                user_msg_match = user_msg_pattern.match(line)
                if user_msg_match:
                    user, msg = user_msg_match.groups()
                    if current_convo and current_convo[-1][0] == user:
                        current_convo[-1][5] += f",{msg.strip()}"
                    else:
                        current_convo.append([user, msg.strip()])

            # Adding the last conversation if any
            if current_convo:
                conversations.append(current_convo)

            # Formatting conversations
            adjusted_conversations = []
            for convo in conversations:
                history = []
                for i in range(0, len(convo) - 1, 2):  # Increment by 2 to ensure one question and one answer
                    prompt = convo[i][6]
                    response = convo[i + 1][7] if i + 1 < len(convo) else None
                    if response:  # Only add to the list if there's a response
                        adjusted_conversations.append({
                            "prompt": prompt,
                            "response": response,
                            "history": history.copy()
                        })
                        history.append([prompt, response])

            # Appending the results to output.txt, one object per line
            with open(output_file_path, 'a', encoding='utf-8') as output_file:
                for convo in adjusted_conversations:
                    json.dump(convo, output_file, ensure_ascii=False)
                    output_file.write('\n')

清洗后数据,可以看到已经有非常完整的逻辑关系了
cleaned_data.jpg

我在训练时使用了70%的训练集,30%作为测试集。3000条数据在我的2080显卡需要10小时!都去试试看效果吧:)

OPA是一种开源通用策略引擎,可在整个堆栈中实现统一的、上下文感知的策略实施。该项目于2018年4月被CNCF沙箱接受,2021年2月4日正式毕业于CNCF。来自大约 30 个组织的 90 多人为 OPA 做出了贡献,维护者来自包括 Google、Microsoft、VMware 和 Styra。

简单来说,是在服务上抽象一层,统一控制、审计,本文讨论仅限在Kubernetes中的gatekeeper,对容器创建进行安全约束,确保符合运维规范。

opa-gatekeeper.png

  1. 安装过程略 https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/
  2. 文件结构,规则、范围一一对应。例:default命名空间必须设置探针,规则名 k8srequiredprobes.yaml ,应用范围名 default_ns_must_have_probes.yaml

- 阅读剩余部分 -

上次的数据库故障余波未平。老服务整改周期内仍有可能增高,有没什么方法限制单个pod只能建立一定数量的数据库连接,把事故控制在一定范围内

  • 首先是数据库层面,可以在配置文件中限制连接数,但基于容器的环境IP会有变化 pass
  • 其次想到的是服务网格,因为是业务标配+出色的流量控制,应该可以从这里入手。看了圈文档,Istio更多关注的是进方向
  • 再次想到kubernetes本身的网络插件也有限流的功能,calico具备对进出方向端口的限制,但没找到连接数的

陷入僵局,最笨用iptables限制,但还能实时发现pod的重启更换IP,难道要复杂化,监控结合脚本的方式吗?忽然灵光一闪,initContainers阶段不是可以做很多事情嘛

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  initContainers:
  - name: init-iptables
    image: my-iptables-image
    command: ['sh', '-c', 'iptables -A OUTPUT -p tcp --dport 3306 -m connlimit --connlimit-above 20 -j REJECT']
  containers:
  - name: my-container
    image: my-image

😅未验证,原理可行- -

某日读写分离中间件报警,有大量非业务IP连接涌入,新连接无法建立。查询数据库连接有大量的”unauthenticated user 1.2.3.4:37414 NULL Connect NULL Reading from net NULL”。一时间大量用户报障,“登录失效”、“设备断连”、“影响产线生产”等等
解决过程倒不复杂,跳过中间件恢复。售后工程师承认是中间件设计问题,释放连接逻辑bug,也提到了有大量连接时可能触发!

反观自身,架构设计阶段把过多的压力放在主库,19年上线的读写分离中间件就是“业务迭代优先,没时间基础设施改造”、“历史包袱”背景下的缓兵之计。阿里云的问题有几点
1.中间件控制台无法显示真实IP,故障后对方研发回复“日志因升级规格消失?”
2.假死后控制台无法重启
3.控制台监控不准确,使用者无法准确选择
4.1.2版本释放连接逻辑bug
主因是内部某服务突然建立了大量连接,进而引发的故障。

  • 开发阶段的考虑对运维阶段的影响:

开发阶段把更多的重点放在功能实现和业务迭代上,而忽略了基础设施的可扩展性。这可能会造成短期内的业务顺利进行,但长期看来,如果基础设施不能跟上业务的发展,最终可能会形成技术债务,导致在运维阶段遇到无法解决或者处理复杂度高的问题。

  • 对云服务商SLA的信任问题:

云服务提供商的SLA(服务等级协议)是我们选择使用其服务的一个重要依据,但是是否100%信任SLA,我们需要结合自身的业务情况和对服务提供商的了解来决定。在应急情况下,我们可能需要更具备自主的故障应对能力,而不是完全依赖服务提供商的SLA。

在对内部OS部门优化的过程中发现,服务器整体利用率很好,编译时可以将服务器所有线程打满,唯一掉链子的时候是解压缩unzip环节,只有单线程升高。简单了解了下,原来已经有了多线程的pigz工具,格式做一些微调即可。详细评测https://zhuanlan.zhihu.com/p/389817246
在翻看docker源码时,发现也会将pigz等压缩工具优先docker_source_code.jpg

chatGPT火爆IT圈已经几个星期了,仿佛没用过就被时代所抛弃。了解后发现,使用门槛还是挺高,需要使用海外的手机号注册openai,常见的“机场”都会被屏蔽。偶然发现接口在国内是可以访问的
testchatgpt.jpg

接下来的事就很简单了,使用django起了个页面,调用接口就可以了,供内网体验wangyechat.jpg

有效代码12行

import openai

openai.api_key = "sk-od9TZTgXar70JLTxf4K1T3BlbkFJlcQjxxxxx"

response = openai.Completion.create(
    engine="text-davinci-003",  # select model
    prompt="人生的意义何在?",
    max_tokens=512,  # response tokens
    temperature=1,  # diversity related
    top_p=0.75,  # diversity related
    n=1,  # num of response
)

completed_text = response["choices"][0]["text"]
print(completed_text)

需求描述:对某一地址,公司网络解析至172.16.1.1,外部解析到1.1.1.1
现状:公司内无单独的DNS服务器,DHCP分配上海公共DNS 202.96.209.5/133
过程:

  1. 内部搭建DNSmasq,DHCP更改配置。稍繁琐,所有DNS流量都走DNSmasq,单点且没必要
  2. 智能DNS解析中的自定义线路解析,实现原理

云解析是通过识别LOCALDNS的出口IP,来判断访问者来源。
如客户端LOCALDNS支持EDNS
因为云解析DNS支持 edns-client-subnet,所以在获取访问者来源IP时,优先获取 edns-client-subnet 扩展里携带的IP ,如果edns-client-subnet 扩展里存在IP,云解析DNS会以该IP来判断访问者的地理位置 ;如果不存在,则以LocalDNS出口ip来判断访问者的地理位置。

dig +short TXT whoami.ds.akahelp.net 

不错的办法,但我的DNS出口IP带ipv6,测试下来不生效
自定义线路解析.jpg

  1. 偶然发现华为防火墙有DNS透明代理功能,可以把特定解析指定DNS服务器,配合DNSmasq,测试下来效果逆天。不管设置何DNS,都受影响

一、一直自诩是柔性的管理者,讲情怀、谈感情、不涉及原则问题都是友善提醒。谈谈近期遇到的一位伙伴小王,他是一个月前加入,原本负责网络的同学匆忙离开。积压的问题越来越多,小王在上手之后不太能搞定,我经常提醒不要成为“沟通黑洞”,发包过去一声不吭。无奈,离开

二、会议效率降低怪象

  1. 靠会议推动,会议过多
  2. 不参会被定责,各类事故复盘会中,未参会部门会被定责。质量部门亦或是质量人员不够专业

对待故障要敬畏,要追根因。惩罚机制要恰到好处,避免大家不敢动,更应该把故障看成一份宝贵的经验包;对待历史问题不逃避。我反对把责任甩的一干二净。
正因为我这种“大包大揽”的责任感,质量部门经常莫名其妙定责给我。前天一次故障,其部门自行维护的服务单点宕机,事故前多次反复提醒仍不整改。坑惨一波又一波接任者

三、越来越像项目经理,技术上已得不到成长,离我的“专家”目标渐远
qunshan.jpg

遇到一种场景,某前端服务部署在kubernetes中,有偶发的服务故障。想着健康探针重启就行,忽然想到,如果是重要的线上服务宕机,不查出来心里憋得慌,怎么让服务恢复的同时又能保留现场呢
改当前pod的标签,这样deployment会认为副本消失,自动创建。完美实现老容器保留,业务也及时恢复
截图.jpg

kong中默认有安全插件,黑白名单限流等,限制UA暂时没找到。可以自己开发一个

-- handler.lua
local BasePlugin = require "kong.plugins.base_plugin"
local MyPluginHandler = BasePlugin:extend()

MyPluginHandler.VERSION = "1.0.0"
MyPluginHandler.PRIORITY = 10

function MyPluginHandler:new()
  MyPluginHandler.super.new(self, "block-user-agent")
end

function MyPluginHandler:access(conf)
  MyPluginHandler.super.access(self)
  
  -- 检查 User-Agent 请求头
  local user_agent = kong.request.get_header("User-Agent")
  for i, ua in ipairs(conf.blocked_user_agents) do
    if user_agent == ua then
      -- 如果 User-Agent 被阻止,使用 kong.response.exit 返回响应并停止处理
      return kong.response.exit(conf.response_code, { message = conf.response_message })
    end
  end
end
-- schema.lua
local typedefs = require "kong.db.schema.typedefs"

return {
  name = "block-user-agent",
  fields = {
    { consumer = typedefs.no_consumer },
    { config = {
        type = "record",
        fields = {
          { blocked_user_agents = { type = "array", default = {}, elements = { type = "string", }, }, },
          { response_code = { type = "number", default = 403 }, },
          { response_message = { type = "string", default = "Forbidden" }, },
        },
      },
    },
  },
}

docker启动时注意修改kong/constants.lua,在插件底部加入UA_block

docker stop kong-gateway
docker rm kong-gateway  
docker run -d --name kong-gateway \
 --network=kong-net \
 -e "KONG_DATABASE=postgres" \
 -e "KONG_PG_HOST=kong-database" \
 -e "KONG_PG_USER=kong" \
 -e "KONG_PG_PASSWORD=kongpass" \
 -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
 -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
 -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
 -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
 -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
 -e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
 -v /data/UA-block:/usr/local/share/lua/5.1/kong/plugins/UA-block \
 -v /data/constants.lua:/usr/local/share/lua/5.1/kong/constants.lua \
 -p 8000:8000 \
 -p 8443:8443 \
 -p 8001:8001 \
 -p 8444:8444 \
 -p 8002:8002 \
 -p 8445:8445 \
 -p 8003:8003 \
 -p 8004:8004 \
 kong/kong-gateway:2.6.1.0-alpine

UA-ill.jpg

最近忙于处理安全事故,在政府的白帽行动中,发现了误暴露在公网的konga,通过发送post请求,能够成功注册管理员进而管理所有规则。我试过两个版本都成功

postman创建.jpg

屏蔽公网访问避免95%的安全问题!

近一周安全问题频发,明显是针对性的精准渗透行为,钓鱼邮件、ERP服务器被拿下、线上kubernetes集群被拿到部分权限成功部署反弹shell。从入侵轨迹来看,未做破坏但有明显的扫描内网行为,对方对安全、运维都有比较深入的了解。与政府组织的“磐石行动”时间点吻合,推测是对我们的白帽行为
云安全中心提醒还是很精准的,以容器中被运行反弹shell为例。从kubernetes审计日志,“黑客”使用被泄露账号通过暴露在公网的k8s api server进来,在进行了一系列尝试后发现有A命名空间的管理权限,具有onl的namespace 权限,查看了cm发现免密登陆,推送镜像,创建deployment ,镜像中传输数据。已关服务,wifi api server取消外网监听

过程中用到的命令
pstree -p -a #查看
docker inspect #查看pod信息
docker run -it --entrypoint /bin/sh xxxxx #启动疑似容器
查看kubernetes 审计日志
kubernetes get rolebinding -n xxx -o yaml

最新战报:
内部员工已中招,对方社工客服运行了可执行文件,导致在OA内向其他用户发送病毒文件
ERP服务器沦陷,有扫描内网的行为
CRM服务器中毒
🏳️

在一次常态的EMQ集群巡检时发现,有非周期的CPU超过80%毛刺,按照预案新增了如下防火墙规则限流(此规则验证过多次),当天为了方便使用&&将多行语句连接成一行。执行后发现连接数立刻下降,通过监控发现大量连接都变成了non-establish

# 清空INPUT链,标记RELATED,ESTABLISHED,对超出限速的REJECT
iptables -F INPUT
iptables -t filter -A INPUT -p tcp -m multiport -m state --dport 1883,8883 --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A INPUT -p tcp -m multiport -m state -m limit --dport 1883,8883 --limit 200/second --limit-burst 600 --state NEW -j ACCEPT
iptables -t filter -A INPUT -p tcp -m multiport --dport 1883,8883 -j REJECT --reject-with tcp-reset
service iptables save
# 当天为批量执行,简化为一条
iptables -F INPUT && iptables -t filter -A INPUT -p tcp -m multiport -m state --dport 1883,8883 --state RELATED,ESTABLISHED -j ACCEPT && iptables -t filter -A INPUT -p tcp -m multiport -m state -m limit --dport 1883,8883 --limit 200/second --limit-burst 600 --state NEW -j ACCEPT && iptables -t filter -A INPUT -p tcp -m multiport --dport 1883,8883 -j REJECT --reject-with tcp-reset && service iptables save

微信截图_20220719211616.png
黄色为non-establish

经排查,在执行标记流量 --state RELATED,ESTABLISHED 会将连接当前状态写入系统内核文件 /proc/net/nf_conntrack,当时每台机器均有100000左右连接,写入磁盘需要2-3秒。用&&将命令合并=没有间隔马上执行下条命令,未写完的连接未标记完成,命中了第三条REJECT tcpreset。陷入取消限速集群扛不住,增加限速丢弃连接死循环,通过SLB限速后,原厂删除集群信息后重建恢复。结论:iptables标记流量需要考虑写盘IO,执行时慢一些