Kubernetes - Init Cluster on CentOS

🎖 准备

🔅 配置防火墙(firewalld)

1
2
3
4
5
# 临时关闭防火墙
$ systemctl stop firewalld
# 永久防火墙开机自启动
$ systemctl disable firewalld

🔅 关闭 Swap

Kubernetes 1.8 开始要求关闭系统的 Swap,如果不关闭,默认配置下kubelet将无法启动。可以通过 kubelet 的启动参数 --fail-swap-on=false 更改这个限制。 我们这里关闭系统的 Swap:

1
$ swapoff -a

关闭之前:

1
2
3
4
$ free -m
total used free shared buff/cache available
Mem: 992 113 636 6 242 716
Swap: 2047 0 2047

关闭之后:

1
2
3
4
$ free -m
total used free shared buff/cache available
Mem: 992 111 638 6 242 718
Swap: 0 0 0

🔅 禁用 SELINUX:

1
$ setenforce 0

确认:

1
2
3
$ vi /etc/selinux/config
SELINUX=disabled

🔅 安装 Docker

Docker 的安装比较复杂,在另一篇文章中有单独讲安装和配置。

这里安装的是 docker-ce-17.10

这里,因为 kubernetes 用的 cgroup driver 为 systemd,所以要把 docker 也配置为 systemd。通过 info 命令确认。

1
2
3
4
5
6
7
8
9
# docker info
Server Version: 17.09.0-ce
Storage Driver: overlay
Backing Filesystem: xfs
Supports d_type: true
Logging Driver: json-file
Cgroup Driver: systemd
...


🎖 安装

🔅 更新源

1
2
3
4
5
6
7
8
9
10
11
12
13
# 如果需要 sudo 用户,请
$ su
$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

🔅 更新

1
$ sudo yum makecache fast

🔅 安装 kubernets 主要软件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ sudo yum install -y kubelet kubeadm kubectl
...
Dependencies Resolved
======================================================================================================================================================
Package Arch Version Repository Size
======================================================================================================================================================
Installing:
kubeadm x86_64 1.8.1-0 kubernetes 15 M
kubectl x86_64 1.8.1-0 kubernetes 7.3 M
kubelet x86_64 1.8.1-0 kubernetes 16 M
Installing for dependencies:
kubernetes-cni x86_64 0.5.1-1 kubernetes 7.4 M
socat x86_64 1.7.3.2-2.el7 base 290 k
Transaction Summary
======================================================================================================================================================

🔅 版本确认

1
2
3
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"8", GitVersion:"v1.8.1", GitCommit:"f38e43b221d08850172a9a4ea785a86a3ffa3b3a", GitTreeState:"clean", BuildDate:"2017-10-11T23:16:41Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64”}


🎖 配置 Master与问题集

1
$ sudo kubeadm init --kubernetes-version=v1.8.1 --pod-network-cidr=10.244.0.0/16

正常来说,这条命令就会启动一个 Master 了,但实际情况是新的 Kubernetes 是使用容器来启动三方软件的,而这些容器放置在 gcr.io,需要翻墙,这里我们预加载一下。

系统启动一共需要以下镜像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gcr.io/google_containers/kube-apiserver-amd64:v1.8.1
gcr.io/google_containers/kube-controller-manager-amd64:v1.8.1
gcr.io/google_containers/kube-scheduler-amd64:v1.8.1
gcr.io/google_containers/pause-amd64:3.0
gcr.io/google_containers/etcd-amd64:3.0.17
gcr.io/google_containers/kube-proxy-amd64:v1.8.1
gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.5
gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.5
gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.5
quay.io/coreos/flannel-amd64:v0.9.0

🔅 重新开始

1
2
3
$ kubeadm reset
$ sudo kubeadm init --kubernetes-version=v1.8.1 --pod-network-cidr=10.244.0.0/16

🔅 完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
$ kubeadm init --kubernetes-version=v1.8.1 --pod-network-cidr=10.244.0.0/16
# 以下为输出
[kubeadm] WARNING: kubeadm is in beta, please do not use it for production clusters.
[init] Using Kubernetes version: v1.8.1
[init] Using Authorization modes: [Node RBAC]
[preflight] Running pre-flight checks
[preflight] WARNING: docker version is greater than the most recently validated version. Docker version: 17.09.0-ce. Max validated version: 17.03
[preflight] Starting the kubelet service
[kubeadm] WARNING: starting in 1.8, tokens expire after 24 hours by default (if you require a non-expiring token use --token-ttl 0)
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [localhost.localdomain kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.8.78]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "scheduler.conf"
[controlplane] Wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] Wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] Wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] Waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests"
[init] This often takes around a minute; or longer if the control plane images have to be pulled.
[apiclient] All control plane components are healthy after 29.502147 seconds
[uploadconfig] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[markmaster] Will mark node localhost.localdomain as master by adding a label and a taint
[markmaster] Master localhost.localdomain tainted and labelled with key/value: node-role.kubernetes.io/master=""
[bootstraptoken] Using token: 405cea.cb350fd16023e08d
[bootstraptoken] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: kube-dns
[addons] Applied essential addon: kube-proxy
Your Kubernetes master has initialized successfully!
To start using your cluster, you need to run (as a regular user):
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
http://kubernetes.io/docs/admin/addons/
You can now join any number of machines by running the following on each node
as root:
kubeadm join --token 405cea.cb350fd16023e08d 192.168.8.78:6443 --discovery-token-ca-cert-hash sha256:87a0229f7837cf1502789e430c57ca381642902f5665005640b917b6fcbf5a3b

按最后的要求,运行这几条命令:

1
2
3
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

🔅 结果

1
2
3
4
5
6
7
8
9
10
11
12
13
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
656dbe0d38be gcr.io/google_containers/pause-amd64:3.0 "/pause" 2 minutes ago Up 2 minutes k8s_POD_kube-dns-545bc4bfd4-2wrrg_kube-system_de9494c2-b6f7-11e7-af7d-0800278919d4_0
e0dfea9f00a5 gcr.io/google_containers/pause-amd64:3.0 "/pause" 2 minutes ago Up 2 minutes k8s_POD_kube-proxy-fnj6t_kube-system_de803e34-b6f7-11e7-af7d-0800278919d4_0
da7a7ec9d578 mirrorgooglecontainers/kube-scheduler-amd64 "kube-scheduler --..." 2 minutes ago Up 2 minutes k8s_kube-scheduler_kube-scheduler-localhost.localdomain_kube-system_5fbf3e68ff1f2f57797628887e9c1bec_0
2126fd5a0d57 mirrorgooglecontainers/kube-controller-manager-amd64 "kube-controller-m..." 2 minutes ago Up 2 minutes k8s_kube-controller-manager_kube-controller-manager-localhost.localdomain_kube-system_2f2bd9f6dddf513ac6c21a43335777dd_0
21edd6e71eeb mirrorgooglecontainers/kube-apiserver-amd64 "kube-apiserver --..." 2 minutes ago Up 2 minutes k8s_kube-apiserver_kube-apiserver-localhost.localdomain_kube-system_dc78e47f78457da950a93b36ff6ac4ba_0
8712a0e9833f mirrorgooglecontainers/etcd-amd64 "etcd --listen-cli..." 2 minutes ago Up 2 minutes k8s_etcd_etcd-localhost.localdomain_kube-system_d76e26fba3bf2bfd215eb29011d55250_0
47bd69d72e16 gcr.io/google_containers/pause-amd64:3.0 "/pause" 2 minutes ago Up 2 minutes k8s_POD_kube-controller-manager-localhost.localdomain_kube-system_2f2bd9f6dddf513ac6c21a43335777dd_0
292d54bd936f gcr.io/google_containers/pause-amd64:3.0 "/pause" 2 minutes ago Up 2 minutes k8s_POD_kube-apiserver-localhost.localdomain_kube-system_dc78e47f78457da950a93b36ff6ac4ba_0
dae95c5d2a8f gcr.io/google_containers/pause-amd64:3.0 "/pause" 2 minutes ago Up 2 minutes k8s_POD_kube-scheduler-localhost.localdomain_kube-system_5fbf3e68ff1f2f57797628887e9c1bec_0
5d41a042874c gcr.io/google_containers/pause-amd64:3.0 "/pause" 2 minutes ago Up 2 minutes k8s_POD_etcd-localhost.localdomain_kube-system_d76e26fba3bf2bfd215eb29011d55250_0

🔅 调试

如果在初始化过程中碰到问题,可以通过以下命令来显示日志。

🎖 配置 pod 网络 Flannel

1
2
3
4
5
6
7
8
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.0/Documentation/kube-flannel.yml
clusterrole "flannel" created
clusterrolebinding "flannel" created
serviceaccount "flannel" created
configmap "kube-flannel-cfg" created
daemonset "kube-flannel-ds” created

这条命令运行完之后,是会生成

1
2
3
4
5
6
7
8
9
/etc/cni/net.d/10-flannel.conf
{
"name": "cbr0",
"type": "flannel",
"delegate": {
"isDefaultGateway": true
}
}

这时的 docker 多出现了

1
2
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aaeef81d5b18 gcr.io/google_containers/pause-amd64:3.0 "/pause" 14 minutes ago Up 14 minutes k8s_POD_kube-flannel-ds-kwhl9_kube-system_5137e57d-b6fd-11e7-af7d-0800278919d4_0


🎖 加入 Worker Node

🔅 更改 docker 为 systemd

🔅 安装 docker

🔅 准备镜像

和 Master 一样,

1
2
3
gcr.io/google_containers/kube-proxy-amd64:v1.8.1
gcr.io/google_containers/pause-amd64:3.0
quay.io/coreos/flannel-amd64:v0.9.0

🔅 安装 kubeadm kubectl 和 cni

1
$ sudo yum install -y kubelet kubeadm

🔅 配置 cni 的插件

1
2
3
4
5
6
7
8
9
/etc/cni/net.d/10-flannel.conf
{
"name": "cbr0",
"type": "flannel",
"delegate": {
"isDefaultGateway": true
}
}

🔅 最后,加入 cluster

1
kubeadm join --token cbc509.fcc110dc2d9c47d8 10.10.81.150:6443 --discovery-token-ca-cert-hash sha256:b7b0d7d737376b59417f2760e4c61bbb0948abee283677f83afc6d80fe67fff9

实就是运行最后一句话。

🎖 验收

在 master 上运行

1
$ kubectl get nodes

如何学习一门新技术

最近,因工作需要,专研云的相关技术,从技术的金字塔模型来看,云算是技术链顶端。

这也就造就了,学习和使用的人不多,会的人都在大公司,可交流的人不多。也就是有难度才有挑战。

那我是如何开始学习的呢?


🔅 一万小时定律

著名的一万小时定律指的是在一个特定的领域刻意训练达到一定时间,就会变成这个领域的顶尖人材。

但我想说的是这个理论的另外一面,你永远不是从零开始,宇宙万物是彼此关联,在一个领域的经验往往在进入另一个领域时也同样生效。

这一点很重要,消除恐惧感,对学习而言是很有效的。


🔅 学习的方法

☼ 提前准备的资源

  • Google / Github / 其它各种论坛

就开始搜索相关的资料,然后一并保存起来。形式零星的关键词记忆。

  • 一本相关好书

等有了一些基础知识时,再选一本这个领域的好书。我个人喜欢纸质书,因为这本书不是用来看的,而是一个归纳整理工具,这一点一会再讲到。


☼ 拼图理论

现代知识体系越来越趋于复杂化,像之前两三个做出一个小产品,到现在的动辄百人的开发团队协作,在云的相关技术更是这样,技术不再是单体,而是生态了。

那学习的方法也要跟得上,不然学到老也未必跟的上技术的迭代。

解决复杂性的方法有哪些呢,我个人用的一个叫拼图模型(也可以说是知识图谱)就是我把要学习的这个生态里的知识归类,做成拼图里的一个个小块,刚开始小块的边界很难划分,先不用太在意。在未来的时间里,这些块会趋向于完美。

当我们脑海中有这么一张拼图时(当然更建议用软件、笔把它画出来,这样更清晰),再看待一些东西,就会很清晰的知道它的位置、边界,重要性的定义,以及几样事物之前的关联。

而且更为重要的是,可以有区别的对待每一块的技术,在不周的时间上专研不同的块,还可以在某一个块不得要领时,换一个块去解决,为什么叫拼图理论,因为和拼图一样,有时你解决不了的一个地方,换去解决另一个地方,反而就把之前未能解决的问题给解决了。

当然,从管理学角度来看,这会引起另一个变革,清晰的块划分有利于分工合作。而合作带来整体效能的提升。

再回到刚提起的那本书,书的作用不再是细读,而是更快速的用前人总结好的知识体系,更快速的构建自己的知识架构拼图。

除了好书,构建拼图的好办法就是去听一些演讲,演讲从来都不能表达行为,都是用于概念的传播。听一个演讲就可以从别人的经验中完善自己的领域知识拼图。


☼ 学会排优先级

有了拼图模型,看着其中密密麻麻的块,这里最应该注意的是什么,是找到其中重要的点。

有些知识很重要,但可能在起步时反而用不到,有些是边边角角的技能反而是初始化的必要条件。

可以用管理学的重要和紧急二维模型,来给知识块进行优先级分类。

这样也可以大规模的降低启动的复杂度,合理的安排推进时间。


☼ 合作带来效能

有了拼图,划分清楚了边界,就有了合作的可能性。

然后,就是找到某些块的合作伙伴,或者在某个块上找到一些有经验的人、专家,迅速把这一个块给做掉。

比如说这次的学习中,有一个很大的块就是网络模型,这牵涉到很低层的网络知识体系,这一块就可以找到一个擅长网络的同事一起研究,于是两个人相互提升,扩充了自己的边界,也让事情的进展更快。


🔅 行动的力量

上面提到了很多学习的方法论,但技术的学习从来都不只是提升知识,而是多于实践,重于应用。

在本次中,一开始,就尝试着把 google / github 的代码去运行,计算机的好处就是永远可以低成本实践,对错永远分明。

为什么行动如此重要,因为别人的东西都不一定是对的,哪怕是最新的文章,都不一定能跑出正确的结果。要自己去多实践。

行动过程中的错误,更是一笔财富,无论是一本书,还是一篇文章,里面教的都是正确的路径,而如何解决错误,调试问题的能力则是行动过程中的最重要的。

毕竟理论和实践中总有那么一条鸿沟。


🔅 完成

在大的知识体系拼图的指引下,通过行动的力量,慢慢的,终于一嘬山一样的难题,就这样被解决了。

REF::

iPhone 屏幕一些事

今天看了下 iPhone X 的屏幕参数,发现它的分辨率达 2436 x 1125,和用的 DELL 2K 显示器几乎相同,但尺寸上的对比差别却是巨大。不得不感慨。

于是就总结了 iPhone 这些年的屏幕演变,一件很好玩的事情。


iPhone 2G, 3G, 3GS

从最初代的 iPhone 开始,Jobs 定下了一个尺寸 3.5 寸,认为这是人手最适合的大小,并一直的坚持着。

这时的工艺水平,还只是普通的高分屏水准。分辨率维持在 320 x 480,PPI 为 163

Spec

Size PPI Points Render @ Rendered Pixels Physical Pixels
3.5 163 320 x 480 1 320 x 480 -


iPhone 4, 4s

3GS 那圆坨坨的造型深入人心,于是在 4 的方正预告之时,办公时还出现过一些争论,不过人总是向前看的,4 的设计很快征服了所有人,特别是前后玻璃工业水平所带来的震撼,无与伦比!

4 还带来了视觉认知上的第一次升级,Retina 屏,也视网膜屏,这个原理很简单,就是以前固定大小放一个显示单位,现在放4个。视觉的改变就是颗粒感一下子就没有了,那清晰度,在两个手机间稍有对比,就会感动的落泪。

同样是 3.5 寸,分辨率变成了 640 x 960,起因是 PPI x 2,由 163 变成了 326。

Spec

Size PPI Points Render @ Rendered Pixels Physical Pixels
3.5 326 320 x 480 2x 640 × 960 -

苹果最牛的地方不是发明一个新东西,而是把这个已有的东西,从一些应用变成全民应用。像不像 Martin Flower


iPhone 5, 5s, 5c, SE

有人说 iPhone 5 是 Jobs 走前定下的最后一个手机的样式,所以是他最终的杰作。

5 出来时的变化,其实是让不少人也纠结了下的,多出一块下巴,是显示信息更多了,还是变得不好拿了。

不过当时的手机世界是 Apple 坚持不造大屏,所以其它手机在大屏的领域不断开拓,也有了不少的市场。

带给程序上的变化就是,以前简单的按点布局的方式不奏效了。要有一套新的布局系统了。就是 AutoLayout。不过更多人用的方式是,判断机型,如果是 5,就把高度算到 568,否则还是 480,又安稳的过了两年。

Spec

Size PPI Points Render @ Rendered Pixels Physical Pixels
4 326 320 × 568 2x 640 × 1136 -


iPhone 6, 6s, 7, 8

iPhone 6 的出现是讨人喜欢的,同时有 6 和 6 Plus,Apple 终于打破了 Samsung 在大屏的垄断,有人说这是没有了 Jobs 的后时代 Apple 在向市场妥协。

当时中国不在首发区,好多人从 HK,JP 进行代购,海关当时查的很紧,我的 日本代购的 6 Plus 还交了 550 的海关税,并且附带了3年的拍照咔嚓声。

6 时代的屏幕完全的变化了,Point 变为 375 x 667,PPI 依然为 326,也就是 2x 渲染,分辨率为 750 x1134。

从此 Autolayout 完全被用上了。

Spec

Size PPI Points Render @ Rendered Pixels Physical Pixels
4.7 326 375 × 667 2x 750 × 1334 -


iPhone 6+, 6s+, 7+, 8 +

但 6 Plus 就很有意思了。

它的 Point 为 414 x 736 ,然后按照 @3x 渲染,理论上的分辨率应该是 1242 × 2208,但实际 PPI 仅为 401,物理分辨率 1920x1080,在向屏幕物理渲染时,要进行一次缩放 ÷1.15。

于是这对 6 Plus 带来了不小的性能问题,3倍渲染,再 ÷1.15 缩放渲染,导致在动画多的应用中出现很多的卡顿。6s Plus 因为内顿升级为 2G,CPU 也进行了升级,卡顿要好太多。

为什么出现这样的渲染机制,猜想是更高 PPI 的屏幕成本较高,2@ 渲染又会降低清晰度。所以这就变成了这种方案。

这个模型被用了 4 年,直到 iPhone 10 周年时,Apple 依然用这个模型生产了 8和 8 Plus,不过可被看作为 iPhone X 这朵红花的绿叶吧。

Spec

Size PPI Points Render @ Rendered Pixels Physical Pixels
5.5 401 414 × 736 3x 1242 × 2208 1080 × 1920


iPhone X

iPhone X 的变化有种当年 4 出现时的感觉,砰然心动。

不过无边框屏在很多设备上已经有了,Apple 从来不会过于创新,只会在最合适的时机推出产品。

屏幕的变化是多了一个刘海,达 5.8 寸的 OLED 屏幕,不过由于是无边框屏,整机的尺寸与 8 差不多持平。

不过对渲染无变化,只是在设计时,要考虑一些东西。

屏幕的 Points 为 375 x 812,依然是 3x 渲染,分辨率为 1125 x 2436,不过这次 Apple 真的是做到了 3x PPI 的屏幕,高达 458 PPI,再无需向下缩放 1.15 倍了。这也是 iPhone X 售价高达 $999 的原因之一吧。

Spec

Size PPI Points Render @ Rendered Pixels Physical Pixels
5.8 458 375 × 812 3x 1125 × 2436 -


Spec 总览

Device Size PPI Points Render @ Rendered Pixels Physical Pixels
iPhone 2G,3G,3GS 3.5 163 320 x 480 1 320 x 480 -
iPhone 4,4s 3.5 163 320 x 480 1 320 x 480 -
iPhone 5,5s,5c,SE 4 326 320 × 568 2x 640×1136 -
iPhone 6,6s,7,8 4.7 326 375 × 667 2x 750 × 1334 -
iPhone 6+,6s+,7+,8+ 5.5 401 414 × 736 3x 1242 × 2208 1080 × 1920
iPhone X 5.8 458 375 × 812 3x 1125 × 2436 -


REF::

也议全栈

当代社会的总趋势是越来越复杂,应对这种趋势的办法就是封装,把一个领域里的知识包装进一个黑盒,然后提供几个接口。

就像电视机,那里面的东西复杂的去了,但对用户来说,就一个遥控器。电视机就是一个封装,提供了遥控器就是这个接口。

封装就会带来行业的差异越来越细。还拿上个列子,遥控器里面也有一些组件,像微型电池,电池和遥控器电路板也是两个很专业的领域,他们两个通过一种接口在另一个维度进行合作,这个接口可被理解为微型电子的正负极和它的样式。

这种封装在人类的进化史上就叫分工。人的大脑有限,所能理解的事物也有限。所以人们只能复杂的世界中找到一个领域,然后进去专研。

分工和封装是同步的,人们也越来在越在自己的领域深挖,变成一个专业的人才。

但这也带来了一些问题,跨领域的沟通成本越来越高,有模式就有反模式。有些人会越来越深入单一领域,但有人就会跨领域发展,叫做通材。

专才和通才的没有优劣之分,也没有高下区别。

但时势不停发生变化,每个行业,每家公司总要经过几个不同的阶段,在这些阶段中,会随着需求的不同,而对不同的人的渴望度不同,所以就会表现出不行的价格,注意不是价值。

在计算机编程行业里,也遵循着这个理论。

在设备上,有 iOS,Android 和 Web,在层级上,有前端,中端,后端和数据库,在专业领域上,又有音视频,网络,压缩等。以上这些分类法只是用来举例。

在外界看来都是同一类的工程师,但里面的划分就有这么多种。所谓的隔行如隔山。因为太复杂了,只能继续通过接口来相互通信。

那个行业的通才呢,就是跨越多种语言,多种层级间的人,近几年有个专有名词,叫作全栈工程师。

从社会总理论上来讲,这个是一个发展的方向,但因为领域比较新,所以在这方面的认知还没达到统一,关于全栈的争论也就比较多。

UPS - 数据安全的必选项

最近上海的天是这样子的。

后果是偶尔停电,偶尔大雷下来之前,会跳电,空气开关会跳掉,对于一个有着家庭数据中心的人来说,真是心疼呀。要行动起来,用买买买来解决问题。

那这次要买的是什么呢,当然就是解决电的 UPS 啦。

目标

  • 体型较小并美观
  • 电的要求不高
  • 防雷,防浪涌
  • 断电后能通知 NAS / Mac 进行关机

看完这些,网上对比下来,其实很快就锁定了,只是细节都没有描述,也就是评测不多。于是只能自己买回来后进行评测了。

这里选中的是 APC-BR550G,APC 没听过,不过网上评价不错,而且群晖原生支持。

è´­ä¹°

京东下单,两天后才想起买的东西,原来并不是京东配送,而是其它一个小快递公司从广州配行的配送。

东西到了后,发现还是挺重的,看了下说明书,超级复杂,都好几类电源口,不过我的需求比较简单,只是接出一个插座,把 NAS,光猫,路由器,移动硬盘接在上面。

效果

APC 提供了一根数据线,一头是 RJ45 网线口,另一端是 USB,USB 接入 NAS 后,在后台设置中,立即就发现了 UPS,进行开启就可以了。

这就是最终效果图:

HiDPI 解析

最近研究显示器时,朋友一直在讲一个词,叫 HiDPI,于是做了些笔记。

几个参数

  • pixel/px 就是显示器上一个独立可以显示的点,显示器由很多这样的点排列组成。
  • point/pt point 在一个指标下就是一个固定的长度,比如说 72,就是指 1/72 英寸,在普通显示情况下,pt值是等于px 值的。但一些情况不行了,像retina 下,4个 px 才等于一个 pt。
  • PPI(Pixels per inch) 是每英寸上的点数,跟尺寸无关,越大显示的点越多,就越清晰。
  • Display resolution 显示器上一共有的点数,和尺寸有关,也和 PPI 有关,是 PPI * 尺寸

iPhone 6 Plus 诡异的分辨率

iPhone 6 和 6+ 虽然是大小差不多,但在意义上却有点不尽相同,感觉 6+ 更接近一个 iPad Mini。

6 在显示参数上和 5S 是保持一致的,像 PPI 都是 326,也就是经典的 @2x 素材,只是物理屏幕变化, Point 也相应的增大为 375 × 667。设计师要做的就是注意尺寸变大后的留白,开发所要做的就是用相对布局。

6+ 就不同了,在渲染时用的是 @3x,理论上,PPI 应该是 163*3=489, Points 为 736 x 414,对应 3x 分辨率应为 2001x1125。但实际上,PPI 为 401;分辨率为 1920x1080,是标准的 PPI x 2.46,

在实际渲染时,6+ 也是先按照 @3x 2001x1125 来渲染,然后整体缩放 1.15 倍,到 1920*1080。

显示器分辨率

从 iPhone 为入口再来理解显示器 HiDPI。

所以最好的情况只有一种,就是显示器上,用显卡渲染出四个点,做成一个 Point,对应到显示器的4个点上。这也是 Retain 的显示原理。这样是最清晰的,显示所渲染的每一个点都能在显示器上还原。

然后就是让显卡渲染出显示器的分辨率,一个点做为一个 point,这样的利用率是最高的,但字就会比较小。

举个例子,我们常见的 2.5K, 4K, 5K 分辨率。

设备 设备分辨率 HiDPI
2.5K 2560x1440 1280x720
4K 3840x2160 1920x1080
5K 5120x2880 2560x1440

以我最爱的分辨率 2.5K 来看,那开启 HiDPI 后的最佳分辨率就是 5K。

但 4K 分辨率依然可以按 2.5K 来进行 HiDPI 来渲染,那是怎么回事呢?

答案很简单,和 iPhone 7+ 的原理是一样的,显卡仍按 @2x 5120x2880 来渲染,然后再进行缩放到 3840x2160 进行显示。当然这样会造成显示的不清晰,同时也会产生资源的浪费。

Macbook Pro - Retina display

先来看看 rMBP 15 的显示参数:

13.3-inch (diagonal) LED-backlit display with IPS technology; 2560-by-1600 native resolution at 227 pixels per inch with support for millions of colors

Supported scaled resolutions:

  • 1680 by 1050
  • 1440 by 900
  • 1280 by 800
  • 1024 by 640

从以上参数来看,它的显示器分辨率是 2560x1600,比 DELL U2515 还高,但因为屏幕小,所以官方认为这么高的分辨率没有什么意义,就在选项中只支持了四种分辨率。

所以,它理论上都是开启 HiDPI 的,1280x800 是它最佳分辨率。其它分辨率都要进行一些显示上的缩放。

REF::

the division in software engineering

理解分工

分工带来效能,当带社会由经济学的分工效应,所带来前所未有的发展。

如果你去看每一个行业。

我曾对装修进行过观察,一个再小的工程,如果想自己做,就会发现陷入了一场灾难。而那个衣着不整的民工,却可以不费力的做到一种完美。虽然你可能感觉智力领先。

这就是分工的结果。每个人在自己的行业不断的专研,积累经验,合起来创建更大的财富。

那软件行业呢。

也同样遵循了这个定律,从设计到代码,从产品到交互,从开发到测试,从管理到架构。

这是我们能看到的方向上的不同。

即使在同一个方向上,也会有着更细致的划分。

iOS, android, Web,

性能测试,功能测试,集成测试,

分工是什么,是因为时代的知识发生了爆炸,不再像以前一样,只有经典,而我们的个人时间,能力有限,只能注重于一个领域,去深入。

当然,除了这一点之外,还有一个解释,就是工作太复杂,也不再是一个人能完成的,需要合作。合作带来分工。

分工和组织

分工的不同,也是组织的聚合。

相同的分工会聚在一起,形成自己的知识体系的交流。

但不同分工通过产品、项目形式聚合在一起,进行跨专业的合作。

这就是一个公司最基本的团队模型。

软件工程中的合作

工种的不同,其实也反应在工作中的依赖。

像开发依赖设计交互,设计依赖产品,而测试又依赖开发,

所以,这个合作本身就有着一个链式的关系,如果在设计流程时,不考虑这层关系。那一定是会出问题的。

反模式

社会的永恒定律就是不会存在唯一真理,有模式就会有反模式。

分工带来的坏处是什么呢?

从软件工程上来讲,分工带来年问题就是大家相互不了解,在一起做事的沟通成本就要高很多。

从专业的术语来讲,就是要做到边界冗余。所以我们常提到高内聚,低耦合。差不多一个道理。

有时在一起交流的复杂度被拉高了太多,于是就有了跨专业的人。就是全栈。

全栈更多的不应该是写代码飞快,而是它能主导一场沟通的顺畅。站在更高点上,提出合理的意见。

当然,全栈是不容易的。

niu ebike

我加入了一个 BMW群,里面的车主都是 BMW, 当然除了我,不过这次是这些车主都升级了另一样,成为了小牛电动车的车主,当然还是除了我。

6000块的电动车,值吗

对这个品牌没有感觉,所以一个 5000 多的电动车感觉还是贵了不少。毕竟普通的一辆只要 2000 块。款式也不错。

6000 块是我一年的油钱了,而且是每天跨越上海来回。

安全

电动车是现在路上最恐怖的一个东西,有时会逆行在汽车道上,有时会忽然出现在你的车前。

而且出现问题都很麻烦,所以我也不是推荐。

共享电车

上海现在很多共享电动车,就绿色的那个,2块钱一小时,还是挺方便的,有时去个远一点的地方就会去找一下。

但和初期共享单车一样,有时会找不到,现在用的也少了。

短距离出行方案

买了的人还是挺满意是,说这是短距离出行的最佳方案。不过他们可能忘了这个夏天太多的雷雨了。

回想自己,周未出行,除了开车,都是地铁公交或步行,还真对单车需求比较少。

还是觉得有一辆车多开开会更省钱省心省力。

一场由 DBA 犯错,外键放大的事故分析

本来注定平淡的一天,就像骄阳下的树叶,没有一点精神。

日常修正

批了一个数据修正的单子,产品上有一个评价的功能,从设计时就坚持公平公正,但还是没顶住运营的压力,经常会删除一些不好的评论。与是就会经常的修正线上的数据。典型的理想和现实的差别。

紧急事件

十一点钟,忽然运营反馈老师的课程不见了,刚开始以为这个老师重新注册了 id,或者其它。慢慢的反馈越来越多,感觉有点头大。

然而越来越多的消息指向问题,来自外界和内部。

初步猜想

刚好,因为公司泄露 appkey 的事情闹得满城风雨,在想是不是因为哪些密码被泄露然后导致有人黑我们。

这种阴谋论细想就不大可能了,有谁能这么精确的知道我们的数据模型,所有丢失的课程都是一些好的课程。而且就算拿到 DB 密码,也需要在内网的某个环境,又懂业务,又懂技术,这几个人就在我们面前呀。

又花了不少时间,排除另一个论证。

排除一切不可能的,剩下的即使再不可能,那也是真相。

问题在 DBA?

这时,把想法又放回到数据修正时的操作。

发现 DB 中的数据有几个点也指向这里,比如说数据在上午9点多时,id 从 8k 跳到 98w。

于是开始讨论方案,最后选择从昨晚的备份中恢复。

下午3点多钟,困的想抽烟,最后用一瓶可乐解决。

第一张表恢复完,发现搜索可以,但不能用分类筛选,再恢复第2张 2W 量级的 tag 关联表。

本以为完事了,结果产品反馈,用户的订阅数据也丢失了一部分。

此时,感觉就像个无底洞,到底干了什么。

订阅记录的量级在 10M,这种可不像上两张表,几分钟进行恢复。而是达数小时的重新写回数据库。

恢复过程中,也想了下原因。

假设

DBA 拿到用于修正数据的 CSV 的数据后,用 MySQL 的 replace into 方法,对课程表进行更新,但不小心将一些课程进行了删除。

本以为只有 10K 条数据的,也没大放心上,照理做任务前先做了备份。

但这几张表是历史老表,表间是有外键关联的。

于是主表的 ID 被删除之后,导致 20K 的标签表和 10M 的用户记录表都被删除或者引用变为空。

外键

对于外键,各说纷纭,目前主流的观点是不用外键,毕竟孤岛数据已经不是问题。

但外键引发的问题远远大于几个孤岛数据。

完结

整个恢复工作一直进行到深夜,为了数据安全,是用单行恢复的方式。

这就是一个典型的操作时犯傻,外键引用放大的事件。

JavaScript 模块化之路

我们都知道 js 在诞生时,都只是为了操作一些 dom,所以也就是被随意的写在一个 js 文件里,放上几个 function,绑定上 document.ready,或者是某个 button 的 click 事件。

但恰恰历史选择了 js,这门语言被无限制的放大使用场景。以前的种种简单都变成了束缚。如何在大型应用中使用 js 成了一个重大话题。

模块化

现代软件的构成基石就是模块化,

- 模块定义

模块可以有很多种形式,一个完成可用功能的函数,类语言中的一个类文件,

- 引用机制

模块还需要一种引用机制。让模块可被其它模块所使用。除了定义。

物理模块,引用模块。

如果从这个角度来看,就是你要引用的模块,是不是在同一个文件中。如果是,那就是一个引用型模块。如果不是,还需要找到对应的模块。

JS 的模块化之路

Object

这种方式,把所有的变量,函数封装在一个 Object 里,来实现模块化,缺点是,里面的变量都能被直接读取,所有的函数都被暴露。

1
2
3
4
5
6
7
const study = {
let _count = 0;
method() {
//...
};
}

它是 new Object 形式的缩写:

1
2
3
4
5
6
7
var module1 = new Object({
_name : 'foo',
 
method : function (){
  //...
 }
});

funcation

使用立即执行函数,可以将内部的变量的访问被锁住。

1
2
3
4
5
6
7
8
9
10
11
var module = (function() {
var _name = 'foo';
var method = function() {
// ...
};
return {
method: method
};
})();

这样,只能访问到 return 函数中提供的方法 method,里面的变量是不可见的。即使有其它方法,如果不在 return 中绑定,也是不可见的。

Class

ES 2015 中提出了 Class,这是个好东西,因为它符合了当下主流主言的语法,贴近人类思想,而且对继承等进行了修正。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo {
construct() {
this.name = 'foo';
this.objectMethod = function { return 'objectMethod'}
}
prototypeMethod() {
return 'classMethod';
}
static classMethod() {
return 'classMethod';
}
}

Class 解决了变量作用域的问题。

这样,之前两种的模块方式,都可以用 Class 代替掉。
之前,object 的 prototype 可以用 static method 来代替。

class 是个语法糖

把上面的类定义,转成之前的 ES5 语法,是可以对应上的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Foo() {
this.name = 'foo';
this.objectMethod = function() {
return 'objectMethod';
}
}
Foo.classVariable = 'classVariable';
Foo.classMethod = function() { return 'classMethod'};
Foo.prototype.color = "red";
Foo.prototype.prototypeMethod = function() {
return "prototypeMethod" + this.name + this.color;
}

使用:

1
2
3
var foo = new Foo();
var temp = foo.name;
foo.method();

原型链方法函数是读写不对称,默认它是在prototype 的,但如果往这个上面写值的话,就会被复制一份到对象上去。

像一些变量,不可能在每个实例间共享的,所以它一定是实例级的,实例方法变量,会加在每一个实例上,而原型方法变量,则是共享的,但一量为原型方法变量进行赋值,其实是在实例上放上一个同名的方法,依据 JS 原型链查找顺序,只会用最近的方法变量。

除了模块的定义之外,模块的引用也有多条路在走。

  • CommonJS(require)
  • AMD
  • ES Module(import)

CommonJS

module.export()

1
module.exports = function (x){ console.log(x);};

require

1
var foo = require('foo');

ES Module

export & import

因为ES6模块是静态加载,而CommonJS模块只有运行时才知道输出哪些接口。

因为它是 static ,所以可以编译优化。

https://github.com/joshgav/CTC/blob/b50b790a89ea8c7271cee2356f6ee6abf8224906/meetings/2017-01-11.md#rewrite-002---esm-node-eps39

ES Module 的现状

  • Edge 第一个开启
  • WebKit 已开启
  • Firefox 已开启
  • Chrome 61 即将开启
  • Node.js …

可以看到在 2017 年,除了 Node,其它浏览器均已支持 ES Module。代码编译的时代即将属于历史。