Hi there 👋

I’m Alan Wang, a full-stack developer.

Hugo Post

Introduction 把简书上的博客搬到 GitHub 了,简书上广告越来越多了。

April 23, 2023

Mac解决too-many-open-files问题

查看 maxfiles 设置 launchctl limit 设置一个较大的 maxfiles sudo launchctl limit maxfiles 102400 102400 查看应用资源情况 lsof -n | awk '{print $1}' | uniq -c | sort -rn | head

August 10, 2022

Kubernetes-watch-golang实现

第一种方法 使用k8s.io/client-go/tools/cache func StartWatchingServices(c *Client) { watchlist := cache.NewListWatchFromClient( c.K8sClientset.CoreV1().RESTClient(), string(v1.ResourceServices), v1.NamespaceAll, fields.Everything(), ) _, controller := cache.NewInformer( watchlist, &v1.Service{}, 0, //Duration is int64 cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { fmt.Printf("added: %s \n", obj) }, DeleteFunc: func(obj interface{}) { fmt.Printf(deleted: %s \n", obj) }, UpdateFunc: func(oldObj, newObj interface{}) { fmt.Printf("changed \n") }, }, ) stop := make(chan struct{}) defer close(stop) go controller.Run(stop) for { time.Sleep(time.Second) } } 第二种方法 使用k8s.io/client-go/informers,可以 watch CRD 资源但需要使用 code-generator 生成 clientset 和 informer(指 CRD 资源)。...

June 23, 2020

利用Keycloak实现Kubernetes单点登录与权限验证(SSO,OIDC,RBAC)

本文将详细介绍如何利用 Keycloak 配置 Kubernetes 登录验证,以及 RBAC 管理。 本文全部为测试环境工具。 流程示意 配置 Keycloak 安装配置 生产环境不建议使用 docker 直接部署 Keycloak。如果使用 nginx 需要将PROXY_ADDRESS_FORWARDING配置成true. sudo docker run -e KEYCLOAK_USER=wanglei -e KEYCLOAK_PASSWORD=your_password -e PROXY_ADDRESS_FORWARDING=true -p 8081:8080 -d jboss/keycloak 配置 nginx 将 server_name 配置成你的域名,代理 Keycloak 端口,注意上面PROXY_ADDRESS_FORWARDING=true server { listen 80; server_name key.wanglei.me; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_pass http://localhost:8081; } } 在 Clients 中创建 kubernetes 设置Client ID为kubernetes,Client Protocol选择openid-connect即 OIDC 协议。 设置 Client 将Access Type设置为confidential, Valid Redirect URIs根据需要设置,本文后面用到 kubelogin,需要设置为http://localhost:8000 记录 Client Secret 在 Credentials 中找到 Secret,后面需要用到。 新建 Mapper 选择Mapper Type为User Attribute,选择Claim JSON Type为String,下面开关一律选on(可根据需求更改)。此处Token Claim Name字段设置为groups,后面配置 kube-apiserver 会用到。 为 User 添加 Attribute 添加 key groups, value admin,然后点击 add,再 save。 配置 kubernetes 此处使用 kind(kubernetes in docker) https://github....

February 28, 2020

使用scratch构建最小化Go程序的docker-image

由于 Golang 编译之后的文件是二进制,而 scratch 是 docker 最基础的空 image,所以可以使用 scratch 来构建 Go 程序的 docker image,使得最终构建的 image 最小化. 构建 image 过程分为两步: 在 Go 基础 image 中 build. 将 build 好的二进制文件拷贝到 scratch image 中。 无需 cgo 的程序 对于无需 cgo 交叉编译的程序,使用 scratch 来作为最终运行的基础 image 非常合适。 首先,选择合适版本的 golang 基础 image 来 build,这里没有必要选择更小的 golang alpine,build 过程中 pull 一般会有缓存所以 pull 速度差别不大,此外 alpine 中没有 git 和 ssl,我们在构建 image 过程中都有可能用到,况且 alpine 也不会影响最终 image 大小。 FROM golang:1.13 AS builder 禁掉 cgo 交叉编译,我们服务器一般为 linux amd64,build 二进制文件。...

November 22, 2019

不同域名之间共享localStorage-sessionStorage

问题 两个不同的域名的 localStorage 不能直接互相访问。那么如何在aaa.com中如何调用bbb.com的 localStorage? 实现原理 1.在aaa.com的页面中,在页面中嵌入一个 src 为bbb.com的iframe,此时这个iframe里可以调用bbb.com的 localStorage。 2.用postMessage方法实现页面与iframe之间的通信。 综合 1、2 便可以实现aaa.com中调用bbb.com的 localStorage。 优化 iframe 我们可以在bbb.com中写一个专门负责共享 localStorage 的页面,例如叫做page1.html,这样可以防止无用的资源加载到iframe中。 示例 以在aaa.com中读取bbb.com中的 localStorage 的item1为例,写同理: bbb.com中page1.html,监听aaa.com通过postMessage传来的信息,读取 localStorage,然后再使用postMessage方法传给aaa.com的接收者。 <!DOCTYPE html> <html lang="en-US"> <head> <script type="text/javascript"> window.addEventListener('message', function(event) { if (event.origin === 'https://aaa.com') { const { key } = event.data; const value = localStorage.getItem(key); event.source.postMessage({wallets: wallets}, event.origin); } }, false); </script> </head> <body> This page is for sharing localstorage. </body> </html> 在aaa.com的页面中加入一个 src 为bbb....

October 17, 2019

三门问题(蒙提霍尔悖论)分析与Golang模拟

问题描述 三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目 Let’s Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门会否增加参赛者赢得汽车的机率? 答案 答案是会。不换门的话,赢得汽车的几率是 1/3。换门的话,赢得汽车的几率是 2/3。 争议 有人认为,在主持人排除了一个门之后,汽车只可能在另外两个门中,所以在两扇门的概率各是 1/2。 分析 首先参赛者选定了一扇门,主持人未开启门时,汽车在这扇门的概率为 1/3,在另外两扇门中的概率为 2/3,此时争议不大。而另外两扇门中必定至少有一扇是山羊,所以即使主持人指出这两扇门中一扇是山羊,并不会影响这两扇门的概率,两扇概率和仍为 2/3,此时一扇已知是山羊,所以两扇中的另外一扇是汽车的概率是 2/3。所以换门会提高概率。 思考 如果主持人开启揭露一扇门是山羊后,另外一个人 B 此时在剩下的两扇门中做抉择,并且他不知道其他信息,只知道一扇是汽车,一扇是羊,那么此时 B 选择到汽车的概率是 1/2。 这是因为没有之前的信息,B 不知道那扇门概率大,B 此时是在两扇门中做随机选择,B 可能有 1/2 的概率选择 A(参赛者)开始选择的门,也有 1/2 的概率选择 A 将要换的门。所以 B 选择到汽车的概率为 1/2 _ 1/3 + 1/2 _ 2/3 = 1/2。 程序模拟 package main import ( "fmt" "math/rand" "time" ) func main() { totalTimes := 1000000 aRightTimes := 0 bRightTimes := 0 rand....

October 10, 2019

Median-of-medians无序数组寻找中位数最差O(n)复杂度JS实现

在无序数组中寻找中位数,最差复杂度为 O(n). 实现算法为 Median of medians,又叫 BFPRT 算法。 实现原理与复杂度研究:https://en.wikipedia.org/wiki/Median_of_medians 贴一版 JS 实现: export const selectMedian = (arr, compare) => { return selectK(arr, Math.floor(arr.length / 2), compare); }; export const selectK = (arr, k, compare) => { if (!Array.isArray(arr) || arr.length === 0 || arr.length - 1 < k) { return; } if (arr.length === 1) { return arr[0]; } let idx = selectIdx(arr, 0, arr.length - 1, k, compare || defaultCompare); return arr[idx]; }; const partition = (arr, left, right, pivot, compare) => { let temp = arr[pivot]; arr[pivot] = arr[right]; arr[right] = temp; let track = left; for (let i = left; i < right; i++) { // if (arr[i] < arr[right]) { if (compare(arr[i], arr[right]) === -1) { let t = arr[i]; arr[i] = arr[track]; arr[track] = t; track++; } } temp = arr[track]; arr[track] = arr[right]; arr[right] = temp; return track; }; const selectIdx = (arr, left, right, k, compare) => { if (left === right) { return left; } let dest = left + k; while (true) { let pivotIndex = right - left + 1 <= 5 ?...

June 3, 2019

为React单页应用提供Runtime运行时环境变量

使用 create-react-app 创建的单页应用(SPA)是在 build 时注入环境变量的。一旦 build 成静态文件便不能动态提供环境变量了。 比如 build 一个单页应用的 docker image,可以在 build 时提供环境变量。但是已经 build 完成,使用 docker run 运行的时候不能再传递环境变量。 本文主要解决在运行时提供环境变量的问题。 原理:通过一段 shell 脚本将指定将 env 转换为 config.js 文件,该文件和 build 好的 static 文件 serve 在同一目录下,在应用中引用 config.js 文件,通过 window._env 获取环境变量。shell 脚本通过 docker image 的 entrypoint 执行。服务使用 go 构建的一个简单服务器,比 nginx 轻量很多。 将如下env.sh文件拷问到项目目录下,在 entrypoint(docker run)时执行,作用是将环境变量转出 config.js #!/bin/sh if [ $CONFIG_VARS ]; then # clear echo -n > ${CONFIG_FILE_PATH}/config.js SPLIT=$(echo $CONFIG_VARS | tr "," "\n") echo "window....

March 27, 2019

Kubernetes等待部署完成-kubectl-wait-rollout

使用 kubectl apply 或者 create 命令创建/更新部署后,其 pod 需要时间完成创建/更新。 如果在 CI 中不等待所有 pod 更新完成,下一步流程很有可能使用更新前的环境。 正确方法 kubectl rollout status 根据实际情况kubectl rollout status命令,能够正确的获取 rollout status。 ATTEMPTS=0 ROLLOUT_STATUS_CMD="kubectl rollout status deployment/myapp -n namespace" until $ROLLOUT_STATUS_CMD || [ $ATTEMPTS -eq 60 ]; do $ROLLOUT_STATUS_CMD ATTEMPTS=$((attempts + 1)) sleep 10 done 以上 shell 脚本经过轮询kubectl rollout status可以很好的解决实际问题。 错误方法 kubectl wait 注意kubectl wait命令不能适用于更新部署。 kubectl wait --for=condition=available --timeout=600s deployment/myapp -n namespace 该命令只能判断 deployment 是否 available,不能用来判断 rollout,即 available 状态的 deployment,很可能老的 pod 还在 terminating,新的 pod 还没创建好。...

March 18, 2019