【深入浅出 Node + React 的微服务项目】4.使用 Kubernetes 部署服务

【深入浅出 Node + React 的微服务项目】

使用 Kubernetes 部署服务

本文格式是针对 github 的 Markdown,所以目录链接 和 代码链接打不开

目录

安装 Kubernetes

  • 在 Linux / CentOS 下,需要使用 minikube ,具体安装请见 https://minikube.sigs.k8s.io/docs/start/
  • 启动 minikube
    • 在 root 账户下,要强制使用 vm 为 docker,因为默认不能在 root 环境下用 docker, minikube start --force --driver=docker
    • 在非 root 账户下
      创建一个普通用户minikube: adduser sirius passwd sirius
      将此用户添加到docke组:usermod -aG docker sirius
      切换到此用户:su - sirius minikube start
    • pull image 卡住的解决方式
      • 方式一:换成阿里版,请注意更换版本 # curl -Lo minikube http://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/releases/v1.4.0/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
      • 方式二:换源成阿里,❗️必须要先把配置文件删除了,不然还要一直卡住
        删除配置 minikube delete rm -f ~/.kube/config rm -rf ~/.minikube
        换源并启动 minikube start --force --driver=docker --registry-mirror=https://21ilzrh4.mirror.aliyuncs.com
        –registry-mirror:阿里云镜像加速器的上给的 url ,https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
        会很慢,☕️坐等即可
        另外sudo mkdir -p /etc/docker && sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://xxxxxx.mirror.aliyuncs.com"] } EOF && sudo systemctl daemon-reload && sudo systemctl restart docker,把 docker 的镜像也阿里云加速
      • 方式三:未尝试过,手动下镜像包
    • 参考文献:
      • 方式二:https://blog.csdn.net/haohaifeng002/article/details/102478143
      • 手动离线包解压方式:https://www.cnblogs.com/liuhqsir/p/15528507.html
      • 阿里官方方式:https://developer.aliyun.com/article/779813?spm=a2c6h.13813017.content3.1.74a91dd5L9o588
  • 配置 kubectl 的命令行快捷启动,前提是你安装了 on my zsh 命令行优化插件
    • 打开 zsh 插件配置文件vim ~/.zshrc
    • 插入 alias k="minikube kubectl -- ",为什么?因为 minikube 是这样子启动的,每次输命令行很麻烦,简化一下
    • source ~/.zshrc
    • 试一试:命令行输入:k version

Kubernetes 总览

流程如下

  • 首先创建 docker image
  • 然后编写 config file,表示要 run 两个 posts image,并开通服务间交流的 network,交给 kubernetes
  • kubernetes 要做的事情:
    • 创建 Cluster,并把 config file 送进 Master 里解析
    • VMNode(不是那个Node…)中创建 Pods,包含 n 个 post image 的 container,相当于就是我们之前开的 n 个 post 服务
    • Deployment 保证 pods 死了也能重启
    • Service 开通通信

在这里插入图片描述

重要的 Kubernetes 术语

在这里插入图片描述

疑问:Deployment 和 Service 是存在于 Node 中 Pod 外,还是 Node 外 Cluster 里?

创建一个 Pod

尝试创建一个 Post 服务的 Pod

  • 根据前面的 Kubernetes 总览,首先需要给 posts 的 image build 到 docker 的 images 里面
  1. 进入 post 的目录,
    创建镜像docker build -t heysirius/posts:0.0.1 ./
    查看镜像docker images
    【附】删除镜像docker rmi -f heysirius/posts:0.0.1
  2. 创建 ./infra/k8s/ 目录
  3. 编写 post.yaml,让 Kubernetes 知道怎么配置
  4. 开始让 Kubernetes 配置,kubectl apply -f ./
  5. 查看 pod 运行情况 kubectl get pod posts,特别关注STATUS
  6. 查看 pod 的 command kubectl describe pod posts,特别关注是否有报错信息

最基础的 Pod 的 yaml 配置

code-4-01-posts.yaml
# k8s 是可扩展的,我们可以添加进自己的项目进去,通过指定是哪个对象让 k8s 看到
apiVersion: v1
# 想创建的对象的 k8s 的 type
# 在这里我们想创建一个 Pod
kind: Pod
# 将要创建的对象的一些配置
metadata:
  name: posts
# 将要创建对象的确切属性
spec:
  # 可以在一个 pod 里创建 n 个 container
  containers:
    # 创建一个名为 posts 的 container
    - name: posts
      # 这个 container 需要使用到的具体的 image
      image: sirius/posts:0.0.1

Kubernetes 常见命令

在这里插入图片描述

Deployment 介绍

  • 更新版本,先创建出 n 个新版本 pod,再替换
    在这里插入图片描述

创建 Deployment

code-4-02-posts-depl.yaml

路径: ./infra/k8s/code-4-02-posts-depl.yaml

# 将生成 deployment.apps/posts-depl 的一个 depl
apiVersion: apps/v1
kind: Deployment
metadata:
  name: posts-depl
spec:
  # 生成 pod 的数量
  replicas: 1
  # 选择器 找到 label 为 app: posts 这样键值对的 
  selector:
    matchLabels: 
      app: posts
  # pod 的具体配置
  template:
    metadata:
      labels: 
        app: posts
    spec:
      containers:
        - name: posts
          image: heysirius/posts:0.0.1

Deployment 常见命令

在这里插入图片描述

更新 Deployment

方法一
  1. 项目代码更改了
  2. 重新构建 image 并改 版本号
  3. 在 Deployment 的配置文件里面,升级 image 的版本
  4. 重新 run kubectl apply -f [depl file name]
    在这里插入图片描述
方法二

甚至不用换目录(不用从 posts 换到 k8s)

  1. 在 pod 里配置 container 的 image 的版本为 :latest 或者不写 默认就是 latest
  2. 项目代码改一下(比如在 post 服务的开启阶段 打印一个 V20,如下图)
  3. 创建一个 image
  4. push 到 docker hub 里
    查看 image docker images
    pull 到 docker hub 里docker pull heysirius/posts
  5. 查看 depl kubectl get deployments
  6. 重开 depl kubectl rollout restart deployment posts-depl
  7. 查看 depl 管理的 pod kubectl get pods
  8. 查看该 pod 的 log 情况 kubectl logs [pod_name]
    在这里插入图片描述
    在这里插入图片描述

Service 介绍

在这里插入图片描述

  • Cluster IP Service 设置一个易于记忆的 URL 以便于 pod 和气气访问各个 pod,只在 cluster 中的 pod 们中暴露,cluster 外部不会知道
    在这里插入图片描述
  • Load Balancer Service负载均衡器,使 pod 可以从集群外部访问, 这是将 pod 暴露给外界的正确方法
  • Node Port 使 pod 可以从集群外部访问,通常仅用于开发

在这里插入图片描述

  • External Name 重定向

创建 NodePort

code-4-03-posts-srv.yaml
apiVersion: v1
kind: Service
metadata:
  name: posts-srv
spec:
  type: NodePort
  selector:
    app: posts
  ports:
    - name: posts
      protocol: TCP
      # 在 cluster 中的 port
      port: 4000
      # Pod 的 port
      targetPort: 4000
  • 配置文件写法和之前的 code-4-02-posts-depl.yaml 类似
  • 注意点:
    • kind 和 type 区别:前者是 Kubernetes 中的类型,后者是前者的 type
    • port 和 targetPort 、 NodePort 区别:如下图
      • port 是每个 Node 在 Kubernetes 的 cluster 中的 port
      • targetPort 是 Pod 的 port
      • NodePort 这里指的是对 cluster 外部的 port
  • 强调一下:
    • 这里我们只是对于 name:ports的 Pod 进行了对 cluster 外的 port 开放,就是说一个NodePort 管 一个 Pod 的对外接口
    • 后面我们如果要进行 comments 和 query 服务的 postman 测试的话,必须把 comments 和 query 也开个 NodePort,如果不想开的话也需要走 ingress-nginx,我们后面会讲到。

在这里插入图片描述

  • 应用配置 kubectl apply -f .
  • 查看 Service kubectl get service kubectl describe service ports-srv
    在这里插入图片描述
  • 访问 NodePort 比如 http://localhost:30625/posts,会返回 posts 服务的 get 的 response

给需要开 NodePort 的 feature 创建 Srv

  • 要注意的是,之前写的代码中开的 express 服务的 port,要跟 Pod 的 port 一样
  • 原因是: Pod 其实就是一个 VM,我们 pull 了 image,然后运行的时候相当于是在当前 VM 开了个 port

commit 链接

code-4-04-posts-srv.yaml
code-4-05-comments-srv.yaml
code-4-06-query-srv.yaml

创建剩余项目的 Deployment 和 ClusteIPService

  • 要注意的是:
    • 我们在配置文件里面,顺便把 Service 也写进去了,我们知道 Service 和 Deployment 是独立的,但是因为都在一个 Node 里,我们配置文件的时候方便起见,也可以把两者写进一个文件里面,用---来划分
    • 注意 Pod 的版本号一定要改成 :latest

commit 链接

code-4-07-comment-depl.yaml
code-4-08-event-bus-depl.yaml
code-4-09-moderation-depl.yaml
code-4-10-query-depl.yaml
code-4-11-posts-depl.yaml
# https://github.com/SiriusZHT/Microservices-with-Node-JS-and-React/commit/c4b0c04a2b0a55b1b6ec3207b93836e8ea289170#diff-0fc4a8b4c4fbbcb7a76add4e004853dcf101522979e15895232886751da6c0f9
apiVersion: apps/v1
kind: Deployment
metadata:
  name: posts-depl
spec:
  # 生成 pod 的数量
  replicas: 1
  # 选择器 找到 label 为 app: posts 这样键值对的 
  selector:
    matchLabels: 
      app: posts
  # pod 的具体配置
  template:
    metadata:
      labels: 
        app: posts
    spec:
      containers:
        - name: posts
          image: heysirius/posts:latest
---
apiVersion: v1
kind: Service
metadata:
  name: posts-clusterip-srv
spec:
  # 默认为 type: ClusterIP 所以可以不用写
  # type: ClusterIP
  selector:
    app: posts
  ports:
    - name: posts
      protocol: TCP
      # 在 cluster 中的 port
      port: 4000
      # Pod 的 port
      targetPort: 4000

修改项目服务的 URL

  • kubectl get service可以看到我们开的所有服务
    • 比如 type 为 NodePort 的对外服务
    • 比如 type 为 ClusterIP 的 Pod 交流服务
  • Pod 间的服务 除了用虚拟出来的 IP 以外,还可以直接用 Service 的 name 作为域名
    • 比如 http://posts-clusterip-srv:4000/events,记得一定要带端口号

在这里插入图片描述
commit 链接

code-4-12-event-bus.js
code-4-13-posts.js
code-4-14-query.js
code-4-15-comments.js
code-4-16-moderation.js

优化和部署项目业务代码 以便于 测试

  1. 我们之前已经修改了所有服务的 URL,
    比如把:localhost:4000/events修改成了posts-clusterip-srv:4000/events
code-4-17-Dockerfile
  1. 给每个 feature 创建docker file
  2. 在每个 feature 下进行创建 image,并 push 到 docker hub 上 docker build -t heysirius/[your feature name] . && docker push
  3. 在 k8s 目录下kubectl rollout restart deployment批量更新全部 Deployment 或者 kubectl rollout restart deployment posts-depl更新单个
  4. k get pods 查看状态 k logs posts-[上面获取的 pod 的 sha number]
  • 值得优化和继续的地方
    • 我们每个服务中,比如 post comment query eventbus 这几个服务中,都没有初始值,就是说我们刚开启服务,并没有进行服务间数据初始化,我认为这不是很方便调试,因为我们测试的时候,是会发送一个 request 上去,然后用 k logs xxx进行查看,但是如果报错了,就不好定位,特别是这几个服务都有相关性,如果一个服务出错,发了个错的request 给 eventbus,肯定会影响另一个服务,让另一个服务也报错,这个问题浪费了我一下午的时间
    • 所以我们可以在,post comment,这两个项目代码里,
      • 首先缓存中创建一个初始值
      • app.listen(xxxx, async ()=>{})加入把这个初始值 axios 请求发送给 event-bus 的操作,即可
      • 我们开服务的时候,就先开 moderation,再依次:event-bus posts comments query
      • 另外,comment 和 post 的 id 要对应上

测试 Deployment 和 Service

  • 和之前 Step2 的测试方式相同
  • 不同的是,NodePort 的 port 才是我们要传入的 port
  • 而因为一个 NodePort 对应一个 Pod,所以我们如果要 post comment query 测试的话,这几个的 port 都是不一样的,详见我们刚才那张kubectl get service的图
  • 最终如下
    在这里插入图片描述

部署 React App

  • 按照我们之前的方式
  • 每个 feature 开个 NodePort
  • React App 也开一个 Pod 和 NodePort
    在这里插入图片描述

Load Balancer 和 Ingress Controller 介绍

  • Load Balance 负载均衡器 负责接收外部的请求,保证负载均衡的情况下分配给某一个 Pod
  • Ingress Controller 入口管理器 他是一个Pod,里面有很多 routing rules 分发请求给 rules 里的 Service
    在这里插入图片描述

使用 Load Balancer 和 Ingress Controller

  • 方法二:
    • 每个 Pod 还是保持有 ClusterIPService
    • 通过 一个 Ingress Controller 控制路由的分发,而不是 n 个 NodePort

在这里插入图片描述

安装 Ingress-Nginx

  • Ingress-Nginx 给我们提供 Load Balancer 和 Ingress Controller 的集成
  • CentOS Linux 下,用 minikube addons enable ingress
    • 但是正常情况下,因为镜像问题,pull 的时候会一直卡住
    • 解决方式:参考https://www.jianshu.com/p/ea88a0ceac19
      因为 ingress-nginx 其实也是通过 yaml 来构造的 kubernetes 结构,所以可以通过 kubectl apply -f ingress-nginx.yaml 来实现手动构造,直接从 github 上的 yarm 文件里面复制粘贴到 ingress-nginx.yaml
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/baremetal/deploy.yaml
sed -i 's@k8s.gcr.io/ingress-nginx/controller:v1.1.0\(.*\)@willdockerhub/ingress-nginx-controller:v1.1.0@' deploy.yaml
sed -i 's@k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1\(.*\)$@hzde0128/kube-webhook-certgen:v1.1@' deploy.yaml
kubectl apply -f ingress-nginx.yaml
code-4-18-ingress-nginx.yaml
  • macOS 下,可以像 centos Linux 一样的操作

查看 Ingress-Nginx 开启状态

在这里插入图片描述
在这里插入图片描述

配置 Ingress Controller 的路由分配

在这里插入图片描述

编写 Ingress 配置文件

  • 通过编写包含 路由规则的配置文件 给 ingress controller
  • ingress controller 会更改自己的路由规则
apiVersion: networking.k8s.io/v1beta1
# yaml 类型
kind: Ingress
# 将要创建的对象的一些配置
metadata:
  name: ingress-srv 
  # annotations 用来设置 ingress-nginx 实例中 nginx 虚拟主机的相关配置
  # 对应配置的是 nginx 当前虚拟主机的 server 指定域内容 
  # by https://www.weixueyuan.net/a/884.html
  #  annotations:
    # nginx.ingress.kubernetes.io/ingress.class: nginx
    # # 使用HTTPS协议代理后端服务器
    # nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    # # 启用SSL透传
    # nginx.ingress.kubernetes.io/ssl-passthrough: "true"
  annotations: 
    kubernetes.io/ingress.class: nginx 
    nginx.ingress.kubernetes.io/use-regex: "true"
# 将要创建对象的确切属性
spec:
  rules:
    - host: posts.com
      http:
        paths:
          - path: /posts/create
            backend:
              serviceName: posts-clusterip-srv
              servicePort: 4000
          - path: /posts
            backend:
              serviceName: query-srv
              servicePort: 4002
          - path: /posts/?(.*)/comments
            backend:
              serviceName: comments-srv
              servicePort: 4001
            # (.*)贪婪匹配 ?是约束
            # 不加?的话后面所有全都要匹配到
          - path: /?(.*)
            backend:
              serviceName: client-srv
              servicePort: 3000
  • 有关 ingress-srv 的其他配置 特别是 annotation,可以参考
code-4-19-ingress-https.yaml

修改 Client 的请求路由

  • 获取 client 页面 http://posts.com/
  • 创建:http://posts.com/posts/create
  • 获取:http://posts.com/posts
  • 创建 comment http://posts.com/posts/${postId}/comments
code-4-20-CommentCreate.js
code-4-21-PostCreate.js
code-4-22-PostList.js

部署 Client 和 Ingress 版本优化

code-4-23-client-depl
  • kubectl apply -f xxx

另外,现在 Kubernetes 已经升级了,所以之前我们写的 ingress config file 已经不能用 apiVersion: v1beta1
需要修改配置文件

具体问题请见这篇文章

code-4-24-ingress-srv.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-srv
  namespace: ingress-nginx
  annotations:
    # kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/rewrite-target: /
    # service.beta.kubernetes.io/do-loadbalancer-hostname: "siriuszht.com"
spec:
  ingressClassName: nginx
  rules:
    - host: siriuszht.com
      http:
        paths:
          - pathType: Prefix
            path: /posts/create
            backend:
              service:
                name: posts-clusterip-srv
                port:
                  number: 4000
          - pathType: Prefix
            path: /posts
            backend:
              service:
                name: query-clusterip-srv
                port:
                  number: 4002
          - pathType: Prefix
            path: /posts/?(.*)/comments
            backend:
              service:
                name: comments-clusterip-srv
                port:
                  number: 4001
          - pathType: Prefix
            path: /?(.*)
            backend:
              service:
                name: client-clusterip-srv
                port:
                  number: 3000

修改本地 host 文件测试是否能进入 ingress

  • 原因:我们在 Ingress-srv.yaml 里给出的所有域名heysirius.com,其实都是本地虚拟出来的,并不是我们直接浏览器输入URL后通过各种DNS来获取的
  • 所以需要修 /etc/hosts:127.0.0.1 heysirius.com
    在这里插入图片描述

🎉🎉🎉 此时,我们之前的项目,已经全部部署到 Kubernetes 里面,且能通过k describe ingress -n ingress-nginx查看 ingress 的映射状态
在这里插入图片描述
甚至最终可以通过 heysirius.com获取 client 页面了

  • !!!注意:如果 ingress 的 服务的 backends 的 name 对应的 ip 为<error: endpoints "xxxxx" not found>,有一种可能是我们的 namespace 没有对应到 ingress-nginx 上面
  • 需要添加 Deployment 和 Service 的 metadata 的 namespace: ingress-nginx

安装 skaffold

  • skaffold 是能够让我们部署的时候变得简单,比如每次更新的时候,之前我们每次都要手动 build push rollout restart
  • 现在我们通过 skaffold 关联相关的 image 和 yaml 配置文件,只要修改了他就能知道并帮我们重新部署一遍,而且非常简单方便
  • macOS 下 brew install skaffold

配置 skaffold

code-4-25-skaffold.yaml
apiVersion: skaffold/v2alpha3
kind: Config
deploy:
  kubectl:
    manifests:
      - ./infra/k8s/*
build:
  local:
    # push: false
  artifacts:
    - image: heysirius/client
      context: client
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "src/**/*.js"
            dest: .
    - image: heysirius/comments
      context: comments
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "*.js"
            dest: .
    - image: heysirius/event-bus
      context: event-bus
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "*.js"
            dest: .
    - image: heysirius/moderation
      context: moderation
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "*.js"
            dest: .
    - image: heysirius/posts
      context: posts
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "*.js"
            dest: .
    - image: heysirius/query
      context: query
      docker:
        dockerfile: Dockerfile
      sync:
        manual:
          - src: "*.js"
            dest: .
  • 最后 skaffold dev即可完成所有相关 image 和 Kubernetes 配置文件的应用

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值