标签:kubernetes

Docker跑NodeJS的同步问题

一直MacOSX本地做开发,没遇到过Watch同步文件不了的问题。最近在skaffold + minikube + React下跑开发环境,居然无法触发WebpackDevServer的Watch刷新,折腾了些时间。

用的是create-react-app建的基于TypeScript的环境,eject了webpack设定,本地跑能watch,在skaffold的manual sync下模式就不行了。skaffold有两个模式,一个是默认模式,一旦有文件修改就重建整个容器,能动但是慢;另一个是manual sync,可以把文件自动同步进容器内,触发容器内的开发服务器重编译或者HotReload,是做本地开发比较现实的方案。

开始以为是skaffold设置问题,但是进容器内能看到文件的确被同步成功;又以为是create-react-app的问题,检查了也是能跑的。奇怪的是明明同样的设置,我的机器却跑不了;明明跑python flask没问题,node却不能动。

后来调试时一不小心触发了提示:

Error: ENOSPC: System limit for number of file watchers reached

ENOSPC指的是没有空间,常见的一般有两种问题:一是真的没空间了,还有一种是inode不足。而这次这个则是file watcher不足。

Webpack官方对这个问题给出了解答:改内核的监视器数目限制。我用的node:10.15.3-alpine,默认数量才8192,对很多Node应用是不够的。其实同样的问题以前用golang在ubuntu做服务器编程,监视log文件时也遇到过,没想到会在写React时再被恶心一次。

因为是内核问题,在Docker容器内部是改不了的,必须进node里改。Production跑的是编译版可以不用动,只改开发用的minikube就可以了。minikube ssh进去

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

一下就好,不用重启,连进pod就能看到更新。

不止是docker,理论上来说用ubuntu这类内核默认设定比较保守的linux发行版做开发都应该很容易遇到同样的问题。据说VS Code在Ubuntu跑的时候就会显示:

Visual Studio Code is unable to watch for file changes in this large workspace

新的PC还没到,提早碰上这个问题也是个好事。打算去skaffold开个ticket警告各位。

穷鬼k8s

穷鬼k8s

k8s是好,自己用还是挺贵的。本文的目的是帮助各位像我一样的穷鬼去架一个自家用k8s,工作学习两相宜。

目标

虽然是穷鬼k8s,但是作为达到了马洛斯需求层次理论前四个层次的穷鬼,还是要达到以下的目标的:

  • 可以跑一些平均QPS大概在10以下的小型Web服务。流量不大。
  • 可以接受一定程度的延迟,基本只服务本地Region。
  • 基本上24个小时跑,但接受一定的downtime。
  • 有一定的扩展性,如果服务盈利了可以比较方便的Scale Up和Scale Out。
  • 不考虑中国国情。
  • 长期使用,不考虑运营商的返鑫。
  • 尽量简单。

在满足了上述的前提条件下,才是尽量便宜。

本文假设读者已经有一定的k8s知识,没学过的话建议读读官方教程。

Provisioner

首先要决定的是Provisioner,也就是服务提供商。主要分两类:

  • 使用云托管的k8s。像Google的GKE、Amazon的EKS、还有Azure那啥。
  • 自建k8s。这里面又有好几种,比如像rancher一类建在云主机上的Turnkey cloud,还有是在DataCenter上组的On-prem cloud,等等。

k8s官方有个表格,大家可以看看。

有些人推荐自建,但除非想用k8s吃饭,或者考CKA,自己架k8s是非常吃力不讨好的。我认为个人使用的话只有云托管一个选择。

  • 自建k8s坑太多,而且运维起来非常麻烦。本来用k8s就是为了把平台抽象化加速DevOps,自建k8s的话那些恶心人的东西都会回来。Turnkey Cloud听上去很好建,像Rancher2那种一键造Cluster听上去很美,但除非是企业的成本限制,否则不建议尝试。
  • 现在的托管k8s有很多便宜的选择,像DigtialOcean,KubeSail,都不贵,丰俭由人,当然好不好用是另一个问题。
  • 建k8s不但要考虑Node的成本,还要算上Master。k8s要跑好,Master必须要用好主机,4G内存起步,3台或以上最好,有这些钱还是买Node吧。
  • 周边设施也要花钱,跑k8s要有Container Registry,要有Disk,要有LoadBalancer。这些钱自己建都不便宜,而且你还不一定建得出来。

选择了托管后,就要选运营商。我自己选的是GKE,理由很简单:

  • 稳定压倒一切。
  • 没用过EKS和Azure,而他们的价格都不比GKE便宜。GKE的Master可以免费的,只收节点钱。
  • GKE性能强大,在公版k8s基础上,有支持AutoScaling的NodePool,有抢占式的Node,这些都能很好的省钱。
  • 上面提到的Container Registry什么的性价比非常高,而选择DigitalOcean虽然节点便宜,但周边就差很多。当然你也可以用多个运营商来弥补不足,但是网络费用也不便宜。
  • 有东京节点,这个可不容易,只能怪本地选手不给力。
  • 可扩展性强。

Architecture

我读了很多关于如何省钱的方法,主要是几点:

  • 自建或选择便宜的小运行商。前面也说了,这其实也不便宜。忽略。
  • 选用低配节点。省是省了,但是满足不了我们对有一定可用性的要求。k8s默认的Deploy方法是Rolling Update,你要有足够的空闲资源去建新pod。在节点配置上省钱不现实。
  • 减少节点数。这个其实比低配节点现实很多。GKE的节点价格跟性能大概是线性关系,而每个节点却都要装占一定资源的kube公共模块。反正单节点也一样能跑k8s,同样价格上少节点比降低配置实惠。但是单节点的话跑k8s就没有意思了,而且用单节点的话很多问题其实会注意不到,不建议。
  • 减少公共组件。GKE默认使用下会自动加载Flunted等组件来记录Metrics,穷鬼k8s心比较大,完全可以关掉它们。现在GKE的创建Cluster的Wizard已经有了“第一个集群”这种选项,选这个模板的话这些用不上的组件都会被关掉。
  • 不用LoadBalancer。这一点是非常容易忽略的。很多托管k8s提供商都用自身的L7的负载均衡组件来实现Ingress,而且是按Rule收费,大概是15~30美元不等。我用GKE的免费额度时就被坑过。LB虽然功能强大,但穷鬼k8s不需要,去年LB能省不少钱。
  • 使用抢占式节点。GKE有个特点,可以用抢占式节点。同样的n1-standard-1,东京区普通版是31刀,而抢占式只有不同10刀。但是抢占式节点会在24个小时内关机,也就是每天都会重启。在GKE下k8s会自动给我们重启节点,所以不会有大问题。然而重启会IP会变,不可能全部用抢占式。
  • 使用AutoScaling。毕竟是穷鬼k8s,负载不高,我们可以用AutoScaling关掉不用的节点。虽然GKE支持,我还没有用过就是。

综上考虑,最终的图是这样的:

穷鬼k8s
  • 我用DaemonSet跑Caddy代替了LoadBalancer,设置dnsPolicy: ClusterFirstWithHostNet后就能直接从节点连接到Cluster内网络。记得要设置Node的防火墙规则,让外部访问能直接进来。
  • PrimaryNodePool只有1个普通的f1-micro,5刀不到,还每月送一个。普通节点的公共IP是固定的,可以直接绑到域名上。
  • 这里用Caddy代替Nginx主要是因为Caddy可以很方便地实现HTTPS,省却很多麻烦。用Caddy的HTTPS功能有一点要注意的是要把TLS证书缓存到硬盘上,否则每次Deploy都会去更新证书而触发LetsEncrypt的RateLimit。
  • PrimaryNodePool上设定Label,CaddyService设定NodeSelector,CaddyService就能绑在PrimaryNodePool上。
  • 其他的Pod全部跑在抢占式的PreemptiveNodePool上,这里用了两个n1-standard-1。因为是抢占式,要注意做好Replica。

这个1拖2的设计,节点成本是5刀+2*10刀=25刀,加上Disk和Network一类的都不到30刀,完全够用了。想要在这基础上省钱,可以把PreemptiveNodePool换成AutoScaling或直接砍一个。再砍的话可以把n1-standard-1换成g1-small,比较恶心的是抢占式主机的价格地域差比较大,同样的g1-small在Iowa才5刀多点,在东京要7.3刀不便宜,所以我直接上n1-standard-1。

最后

有了穷鬼k8s,还是要做开发的。现在有Skaffoldk9s这些工具,在k8s上做开发比以前要简单很多。大家可以自己组一个试试玩。k8s代码我会在整理好后再放上来。