记录为什么最终没有采用 helm 而是选择了 kustomize 作为 kubernetes 应用的部署工具。
前一阵子参加了在上海举行的 KubeCon,听了很多 session 收获还是挺多。(此处应再有一篇 blog 做做总结,不过介于最近半年来的 blog 效率实在不敢立任何 flag。)尤其是 habor 项目以及 helm 项目的介绍戳中的我的痛点。虽然这些项目我都听说过,也知道他们是做什么的,一个做 registry,一个做 kubernetes 包管理。但是因为没人在旁边不停的说,所以优先级一直被自己排的很低,这也是参加这种会议的意义之一吧:让你和你正在关注的技术的同行在一起聊聊,稍微的点拨和帮助可能就会解开你对某个东西的疑惑或顾虑。helm 的出镜率实在太高了,再加上我们本身有很多项目需要以一种更好的方式去部署到 kubernets,所以回来之后就决定去尝试一下。
首先说说之前的痛点。我们虽然不是个大公司,可是这代码也是越敲越多,服务也是越做越全。零零总总也有十几个项目要管理了。然后我们同样有多套部署环境:内网环境,预生产环境,生产环境。那么针对每一个环境几乎都要有一套 kubernetes 的 yaml 文件,但是各个仅仅是稍有不同。
然后我们自己的 ci 是将构建好的 docker 镜像放到 registry 里面。
那么,每次更新的镜像之后就是通过人手工去部署一下,绝大多数情况就是修改一下镜像的 tag,但是由于每个环境的 yaml 略有区别,那么如果我需要在不同环境切换的时候就需要来回修改这些 yaml 文件,一不小心写错了就只能怪自己手残。然而这种部署方式虽然在 kubernetes 之下就是改改 yaml 就好了,但是依然感觉很是原始。
仔细想想,自己的需求就是这么几个:
既然都说 helm 是 kubernetes 的包管理工具,那么我就先去尝试了一下 helm。
helm 诞生于一个名为 deis 的团队。早年在 mesos 大行其道之前有过自己的一个开源 paas 方案 deis,我当时在 tw 也做类似的事情,受其影响颇深。后来 kubernetes 崛起,deis 彻底放弃之前的项目,该做了一个基于 k8s 的 paas,并开源了 helm。然后 deis 团队最终被 ms 收购了并放弃了原来的 deis 项目。
简单的看了看,helm 给我一种大而无当的感觉:它真的是一个做包管理工具的,复杂的 go template 体系以及需要单独存放的 charts 让我感觉其更适合对标 ubuntu 的 apt 或者 macos 的 brew。它更像是对外提供一个复杂的可以依据各种配置信息生成适合于不同环境的软件发布包,而不是用于我们这种轻量级的部署配置管理的。所以我就放弃使用 helm 了。
在这个时候我想起来了在之前 github trending 看到的另外一个用户做 kubernetes 配置的工具 kustomize。简单的说,它就是一个简化 kubernetes yaml 编写的工具。它提供了两个重要的功能恰好满足了我的需求。
kustomize 可以设置如下的层次:
├── base
│ ├── deployment.yaml
│ ├── kustomization.yaml
│ └── service.yaml
└── overlays
└── stg
├── ingress.yaml
└── kustomization.yaml
其中 base 里保存各个环境所公有的配置:
base/kustomization.yaml:
resources:
- deployment.yaml
- service.yaml
然后在 overlays 中可以定义子环境:
overlays/stg/kustomization.yaml:
bases:
- ../../base
resources:
- ingress.yaml
可以看到 stg 下继承了 base 的配置,并且添加了 ingress.yaml 配置。同时,kustomize 不仅仅支持文件级别的 patch,还支持对一个文件某些字段的 patch 如下所示,replica_count.yaml
只包含了有关 replicas
的部分即可,在执行 kustomize build
之后就可以将这部分覆盖默认的配置。
kustomize 提供了一个命令行方法对镜像 tag 进行修改:
kustomize edit set imagetag xxx:94c269ec
如果图省事,可以这么做
export NEWTAG=94c269ec
kustomize edit set imagetag xxx:$NEWTAG
那么每次都去 ctrl-r 修改这个 export 然后再 ctrl-r 找到第二条命令执行一下就好了。虽然它还是修改了 kustomization.yaml 但是我觉得比打开编辑器改要舒服一些。
相对 helm,kustomize 依然保留了对 kubectl apply -f
命令的支持,仅仅作为一个命令行工具;不像 helm 还需要在 k8s 里面部署一个 tiller 可谓是非常的轻量级了。
分别举例说明:
bases:
- ../../base
configMapGenerator:
- literals:
- STORAGE.DATASETUPLOADURL=https://xxx/files/datasets
- STORAGE.CODEUPLOADURL=https://xxx/files/codes
- LIVELOG_PREFIX=https://xxx/jobs
name: storage-server
resources:
- ingress.yaml
imageTags:
- name: xxx
newTag: dc12c4d7
resources:
- deployment.yaml
secretGenerator:
- name: notification-service
commands:
SHORT_MESSAGE_API_KEY: "bash -c 'echo -n $SHORT_MESSAGE_API_KEY'"
MG_API_KEY: "bash -c 'echo -n $MG_API_KEY'"
type: Opaque
generatorOptions:
disableNameSuffixHash: true
secretGenerator
和 configMapGenerator
可以以更灵活的方式生成 configmap 和 secret,相对来说更方便吧。然后注意看我 configMapGenerator
的例子,echo -n $xxx
是会有问题的,一定要使用 "bash -c 'echo -n $SHORT_MESSAGE_API_KEY'"
的命令哦。
记录在采用 ingress-nginx 暴露内部服务的过程
ingress-nginx 是 ingress 的一个实现,目前它已经被放在 kubernetes
项目下面了,可见算是亲儿子了,可更新频率也非常高,再加上之前在别的环境用 nginx 的场景也很多,没想太多就觉得用它了。
在我安装 ingress-nginx 的时候,其最新的版本是 0.16.2
。首先遵循文档先安装 mandatory.yaml
:
kubectl apply -f \
https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.16.2/deploy/mandatory.yaml
这一步里面做了如下的事情:
ingress-nginx
namespace这也是一个神奇的操作,虽说 ingress 才是真正将服务暴露到外面的资源,但是实际上反而是一个 service
完成了最终将服务暴露出去的任务。这里我们可以有多种选择:
要么采用 NodePort
将 service 通过某一个特定的端口:
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app: ingress-nginx
要么采用 externalIPS
直接将 service
通过特定的 IP 暴露出去:
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
externalIPs:
- <external-ips>
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app: ingress-nginx
这里我采用的是第二种,这样暴露出来的服务更干净。
然后测试一下看看是否工作:
curl http://<external-ip>
如果返回 404 说明已经链接到了默认的 backend 了。
然后我们再创建一个 ingress
将我们的 java service 暴露到路径 /api
下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: openbayes-server-ing
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /api
backend:
serviceName: openbayes-server-svc
servicePort: 80
kubectl apply -f ingress.yaml
然后再尝试一下 curl http://<external-ip>/api
看看是不是可以正常的访问这个 api。
默认的 nginx 配置未必适合我们的服务,访问 Nginx Configuration 可以看到 ingress-nginx 所提供的三种 nginx 配置方式。其中 ConfigMaps 可以实现对 nginx 默认配置的修改;而 ingress annotation 则可以实现对特定 ingress 进行配置。
比如我们的 /api
有上传文件的需求,而默认的请求尺寸最大为 1m
会导致文件上传报错 413
,通过添加注解 nginx.ingress.kubernetes.io/proxy-body-size
可以指定请求大小限制:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: openbayes-server-ing
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "1024m"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /api
backend:
serviceName: openbayes-server-svc
servicePort: 80
每次修改 ingress 后,nginx-ingress-controller 会默认更新 nginx.conf,立即生效。
我们在自己的办公电脑上需要一些方式访问 google.com 等一些网站。在使用 ubuntu server 的时候同样也需要安装一些被 block 的依赖,因此对于很多 server 也需要做类似的配置。这里记录一下自己配置的过程。
首先,这里所有的配置是建立在我已经有了一个 ss 的 server 的前提之下。
sudo add-apt-repository ppa:max-c-lv/shadowsocks-libev -y
sudo apt-get update
sudo apt-get install shadowsocks-libev -y
修改 /etc/shadowsocks-libev/config.json
{
"server": "{{ ssserver }}",
"server_port": {{ ssserver_port }},
"local_address": "127.0.0.1",
"local_port": 1080,
"password": "{{ ssserver_password }}",
"timeout": 600,
"method":"{{ ssserver_method }}",
"fast_open": true
}
重启服务
systemctl daemon-reload
systemctl restart shadowsocks-libev-local@config
systemctl enable shadowsocks-libev-local@config
至此,本地已经有了 sock5 的代理:localhost:1080。然而系统使用的代理大多是 http_proxy 我们需要另外一个工具 polipo。
polipo 可以把 socks5 代理转化为 http 代理用。首先安装 polipo
sudo apt-get install polipo -y
然后修改配置文件 /etc/polipo/config
socksParentProxy = "localhost:1080"
socksProxyType = socks5
重启服务
systemctl enable polipo
systemctl restart polipo
然后可以在命令行下试试看了:
http_proxy=http://localhost:8123 curl www.google.com