终于能够写一篇k8s相关的文章了。在2018年的时候,一个很佩服的同事说过,以后肯定是k8s的未来。彼时,我才刚知道Docker,处于刚开始上手的阶段。他的这句话,就让我对这个东西产生了很大的兴趣。后来有时间就了解这方面的知识。因为它安装麻烦,配置要求高,就不是很用力的去实践。于是,从swarm入手,开始实践,折腾。

随着发展,k8s也确实越来越火了。相关的技术也层出不穷,终于迎来了k3s这个项目。k3s是一个非常轻量级的k8s发行版,阉割了很多不必要的特性,能够运行在低配置的环境。正好,Mu项目也在swarm上跑了一年,经过不断的迭代,已经做好了迁移的准备。

最近,经过调研,终于决定动手了。没想到,迁移比我想象的顺利多了,晚上在家熬了两个小时就搞定了。

这篇文章主要是介绍下自己对k8s的理解,以及自己实践相关的内容。

k3s

关于k3s的介绍,官方的文档介绍的非常详细。包括如何安装,阉割了那些东西,包含哪些组件。

和本次实践相关的k8s概念有:PodDeploymentServiceIngressNamespaceConfigMap

Pod是一组容器的集合,就像豆荚。它是k8s中调度的最小单位,在swarm中,最小单位是容器。例如,一个FPM进程不能直接对外提供服务,一般需要一个Nginx作为代理,两者结合才能实现一个完整的服务。容器也往往不是孤立的。所以,就有了Pod的概念。

Deployment是一种k8s资源对象。它主要是描述一个应用的状态,例如,用的哪些个镜像,包含几个Pod副本。只要提供了应用的状态描述文件,就可以很方便的部署一个应用。这也是容器技术火热的原因之一。

Service即是服务。在k8s中,如果只是部署了Pod,它只能在集群内访问。并且,访问单个Pod也没有意义。通常这几个Pod合并在一起才算一个完整的服务。Service提供了一种方式,可以让外界可以轻松访问这些Pod。它有ClusterIpNodePortLoadBalancer这几种方式。第一种是只在集群内部可访问;第二种则是在每个k8s节点都暴露一个端口,然后访问任何一个节点的这个端口,都能路由到Pod。和之前介绍过的swarm中的overlay一样;第三种,则是需要云厂商支持的负载均衡模式。想想,我们部署多个Nginx服务,往往会在前面架一个负载均衡器。就是如此。

Ingress也是比较重要的一个概念。什么是Ingress?它是k8s中的一个资源对象,用于管理Service的。为什么需要Ingress呢?我们来看传统的服务是怎么管理的。假设有5个Go服务跑在一个服务器上,它们每个都占用一个端口。怎么方便访问呢?通常会在前面加一个Nginx作为反向代理,用于域名解析和SSL等。同样的,Service固然能够暴露服务给外界。但是,每个Service占用一个端口,且不能通过域名和路径等来区分服务,只能通过端口。这就不方便了,于是就有了IngressIngress很好用,但是我在部署的时候,并没有用上,甚至还卸载了自带的traefik。后面会介绍为什么。

Namespace,命名空间。一想就知道用于资源隔离,权限控制等。

ConfigMap即是配置。既然是服务,服务就少不了配置,所以k8s提供了ConfigMap对象来方便管理配置等。

k8s中还有很多其他的资源对象,每一个都有其对应的应用场景。这也是k8s越来越火的原因,它提供了一整套架构方案来支持应用容器化。

最后,看一下,在k8s中,请求是如何到达应用的

img

我的部署实践

首先,Mu有3个组件,接口api组件,调度器commander组件,执行器agent组件。需要3个部署文件。

1、首先,创建一个命名空间

apiVersion: v1
kind: Namespace
metadata:
  name: k3s-apps

2、其次,创建一个配置文件

apiVersion: v1
kind: ConfigMap
metadata:
  name: mu-config
  namespace: k3s-apps
data:
  app.json: |
    12345

3、部署各个组件

# mu-api.yml
apiVersion: v1
kind: Service
metadata:
  name: mu-api-svc
  namespace: k3s-apps
spec:
  selector:
    app: mu-api
  type: NodePort
  ports:
  - port: 80
    nodePort: 30080
    targetPort: 7980
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mu-api
  namespace: k3s-apps
spec:
  selector:
    matchLabels:
      app: mu-api
  replicas: 2
  revisionHistoryLimit: 1
  template:
    metadata:
      labels:
        app: mu-api
    spec:
      volumes:
      - name: mu-conf
        configMap:
          name: mu-config
      containers:
      - name: mu-api
        image: aaronzjc/mu-api:latest
        volumeMounts:
        - name: mu-conf
          mountPath: /app/conf
        ports:
        - containerPort: 7980
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
# mu-commander.yml
apiVersion: v1
kind: Service
metadata:
  name: mu-commander-svc
  namespace: k3s-apps
spec:
  selector:
    app: mu-commander
  type: NodePort
  ports:
  - port: 80
    nodePort: 30070
    targetPort: 7970
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mu-commander
  namespace: k3s-apps
spec:
  selector:
    matchLabels:
      app: mu-commander
  replicas: 2
  revisionHistoryLimit: 1
  template:
    metadata:
      labels:
        app: mu-commander
    spec:
      volumes:
      - name: mu-conf
        configMap:
          name: mu-config
      containers:
      - name: mu-commander
        image: aaronzjc/mu-commander:latest
        volumeMounts:
        - name: mu-conf
          mountPath: /app/conf
        ports:
        - containerPort: 7970
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
# mu-agent.yml
apiVersion: v1
kind: Service
metadata:
  name: mu-agent-zyra-svc 
  namespace: k3s-apps
spec:
  selector:
    app: mu-agent-zyra
  type: NodePort
  ports:
  - port: 80
    nodePort: 30091
    targetPort: 7990
---
apiVersion: v1
kind: Pod
metadata:
  name: mu-agent-zyra
  namespace: k3s-apps
  labels:
    app: mu-agent-zyra
spec:
  containers:
  - name: mu-agent-zyra
    image: aaronzjc/mu-agent:latest
    ports:
      - containerPort: 7990
    resources:
      limits:
        cpu: 100m
        memory: 50Mi

---
apiVersion: v1
kind: Service
metadata:
  name: mu-agent-nami-svc 
  namespace: k3s-apps
spec:
  selector:
    app: mu-agent-nami
  type: NodePort
  ports:
  - port: 80
    nodePort: 30092
    targetPort: 7990
---
apiVersion: v1
kind: Pod
metadata:
  name: mu-agent-nami
  namespace: k3s-apps
  labels:
    app: mu-agent-nami
spec:
  containers:
  - name: mu-agent-nami
    image: aaronzjc/mu-agent:latest
    ports:
      - containerPort: 7990
    resources:
      limits:
        cpu: 100m
        memory: 50Mi

然后就没了。搭好环境,几个配置文件,就是这么简单。

前面提到为什么没有用到Ingress。因为我只有一台1C2G的小主机,没有分布式的环境,Pod不会调度到其他的机器。所以,对我而言,ServiceClusterIpNodePort方式都是可行的。因为当服务重启或者重新部署时,ClusterIp可能会变,但是NodePort定义的端口不会变,所以Service选择了NodePort

因为我通过服务器IP+端口方式可以访问到Service了,在这个场景,Ingress和传统的Nginx反向代理的方式,区别并不大。都是做域名解析和SSL等。考虑到我的服务器安装了一些其他非容器应用,例如,MySQLRedis*ray等。如果通过Service将它们映射到k8s,再通过Ingress代理,这样显得有点多此一举。没有Nginx反代这种传统方式来的方便。所以最终我的服务器部署如下

img

最后

这只是一个非常好的开端。现在只是进行了简单的部署,还有很多内容值得学习折腾。例如,将k8s部署和Github Action结合,实现自动化部署。再就是使用sidecar模式采集日志。还有helm。等等。