在 Kubernetes 上部署 Web 应用

开 kubernetes 集群

如何开一个 kubernetes 集群较为复杂,我使用了一个支持 digital ocean 的第三方服务,进行 API 授权后即可替你在 DO 上还可以集群:https://stackpoint.io/ 。 具体创建集群的教程请参考 DO 的文章:https://www.digitalocean.com/community/tutorials/webinar-series-getting-started-with-kubernetes

开好以后 stackpoint 会提供一个 kubeconfig 文件,用这个带证书的配置文件可以使用 kubectl 工具远程连接集群进行管理,不需要 ssh 上去。

查看集群信息

使用如下命令查看集群信息:

1
kubectl --kubeconfig=./kubeconfig get cluster-info

会有类似如下的输出:

1
2
3
Kubernetes master is running at https://YOURIP:6443
Heapster is running at https://YOURIP:6443/api/v1/namespaces/kube-system/services/heapster/proxy
KubeDNS is running at https://YOURIP:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

查看所有节点信息:

1
kubectl --kubeconfig=./kubeconfig get nodes

得到如下输出:

1
2
3
4
NAME                  STATUS    ROLES     AGE       VERSION
spc1d17xmk-master-1 Ready master 1d v1.10.2
spc1d17xmk-worker-1 Ready <none> 1d v1.10.2
spc1d17xmk-worker-2 Ready <none> 1d v1.10.2

创建一个 webapp 的部署

为了方便管理部署的配置,我们使用 yaml 文件描述所要部署的应用,本文以 submodule-checker 一个 Node 开发的 web app 为例。

首先我们创建一个最简单的部署:

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: submodule-checker-deployment
spec:
replicas: 2
selector:
matchLabels:
app: submodule-checker
template:
metadata:
labels:
app: submodule-checker
spec:
containers:
- name: submodule-checker
image: richard1122/submodule-checker:fe51cb0
ports:
- containerPort: 7777
livenessProbe:
httpGet:
path: /healthz
port: 7777
initialDelaySeconds: 3
periodSeconds: 3
env:
- name: APPID
valueFrom:
secretKeyRef:
name: submodule-checker-key
key: appid
- name: APPSECRET
valueFrom:
secretKeyRef:
name: submodule-checker-key
key: appsecret
volumeMounts:
- name: key
mountPath: /app/keys
readOnly: true
volumes:
- name: key
secret:
secretName: submodule-checker-key
items:
- key: key.pem
path: key.pem

使用 kubectl 工具将这份配置文件发给服务器,其它类型的配置文件都可以用这个命令:

1
kubectl --kubeconfig=./kubeconfig apply -f ./submodule-checker/submodule-checker.yaml

这个配置非常容易理解,它非常类似 docker-compose 配置,部署了两个副本,容器内部端口是 7777,目前还不需要分配外部端口。

k8s 自带了健康检查功能,我们使用 httpGet 的方式访问容器内部的 /healthz 地址,返回成功的 http 状态吗就会被认为服务仍然健康,否则 k8s 会认为服务已经坏掉了,把这个容器重启了。

env 是两个从 secret 中取来的环境变量,k8s 可以将配置的 secret 独立部署上去,再需要的地方读取,传递给应用的环境变量、文件等。

volumes 则也是 secret 的一个内容,将对应可以为 key.pem 的 secret 写在文件中,映射到 /app/keys/key.pem 文件。

部署 secret

secret 可以与应用分开配置,一个样例的文件如下:

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: submodule-checker-key
data:
appid: MTE5MzQ=
appsecret: xxx
key.pem: xxx

这里每个字段对应的都是一个 base64 后的内容

  1. 字符串可以使用 echo -n '123' | base64 生成
  2. 文件则需要输出出来,并不带换行的转换为 base64:cat ./key.pem | base64 -w0

查看部署

以上两个配置应用完成后,即可查看 submodule-checker 的部署情况:

1
kubectl  --kubeconfig=./kubeconfig describe -f ./submodule-checker/submodule-checker.yaml
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
Name:                   submodule-checker-deployment
Namespace: default
CreationTimestamp: Thu, 10 May 2018 01:13:43 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision=4
kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"submodule-checker-deployment","namespace":"default"},"spec":{"replicas...
Selector: app=submodule-checker
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=submodule-checker
Containers:
submodule-checker:
Image: richard1122/submodule-checker:fe51cb0
Port: 7777/TCP
Liveness: http-get http://:7777/healthz delay=3s timeout=1s period=3s #success=1 #failure=3
Environment:
APPID: <set to the key 'appid' in secret 'submodule-checker-key'> Optional: false
APPSECRET: <set to the key 'appsecret' in secret 'submodule-checker-key'> Optional: false
Mounts:
/app/keys from key (ro)
Volumes:
key:
Type: Secret (a volume populated by a Secret)
SecretName: submodule-checker-key
Optional: false
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: submodule-checker-deployment-6bbcf79db8 (2/2 replicas created)
Events: <none>

可以看到这份部署(Deployment)的详细状态,更具体的每个 pod 也可以查看:

1
kubectl  --kubeconfig=./kubeconfig get pods
1
2
3
NAME                                            READY     STATUS    RESTARTS   AGE
submodule-checker-deployment-6bbcf79db8-7w9f7 1/1 Running 0 7h
submodule-checker-deployment-6bbcf79db8-ft2hs 1/1 Running 0 7h

对于每个 pod 可能处于各种不同状态,如崩溃重启等,此时可以使用 logs 命令查看某个 pod 的日志:

1
kubectl  --kubeconfig=./kubeconfig logs submodule-checker-deployment-6bbcf79db8-7w9f7

从外部访问

截至目前部署的容器都只是内部的,需要定义一个 service 暴露端口从外部访问

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Service
metadata:
name: submodule-checker-service
labels:
app: submodule-checker
spec:
ports:
- port: 7777
targetPort: 7777
type: NodePort
selector:
app: submodule-checker

部署后可以使用 describe 命令查看 service 对外具体暴露的端口,同时 service 也有不同 type,可以仅仅对内使用

1
kubectl  --kubeconfig=./kubeconfig describe -f ./submodule-checker/submodule-checker-service.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Name:                     submodule-checker-service
Namespace: default
Labels: app=submodule-checker
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"submodule-checker"},"name":"submodule-checker-service","namespace":"d...
Selector: app=submodule-checker
Type: NodePort
IP: 10.3.0.200
Port: <unset> 7777/TCP
TargetPort: 7777/TCP
NodePort: <unset> 30989/TCP
Endpoints: 10.2.1.15:7777,10.2.2.16:7777
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>

其中 NodePort 就是对外的端口,直接从外网 IP 访问到。

总结

这篇文章只是简单介绍了如何部署一个多副本的,无状态的 webapp,并把它暴露在外网上。

但想要更好的提供服务,还需要配置 SSL,Nginx 等。

本文涉及到的内容请参考以下文档:

  1. https://kubernetes.io/docs/reference/kubectl/overview/
  2. https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
  3. https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service
  4. https://kubernetes.io/docs/concepts/configuration/secret/