k8s部署项目

198 阅读5分钟

clone好项目 一. 拉取镜像(CPU版本)

  1. 拉取tensorflow-serving镜像
    docker pull tensorflow/serving

  2. 启动镜像 2.1 启动模型

格式如下

docker run -d -t --rm -p 8501:8501 -p 8500:8500 -v "模型绝对地址:/models/任务自定义名称" -e MODEL_NAME="任务自定义名称" tensorflow/serving &

按照上面的步骤,我们的模型路径是: /root/tom/newspage-type-identify/model/

屏幕截图333.jpg 启动

docker run -d -t --rm -p 8501:8501 \
    -v "/root/longjiang/newspage-type-identify/model:/models/newspage_type_identify" \
    -e MODEL_NAME=newspage_type_identify \
    tensorflow/serving &

2.2 接口测试

测试脚本

# -*- coding: utf-8 -*-

import os
import jieba
import numpy as np
import requests
from lxml.html import fromstring
from lxml.html import tostring


def file2element(file_path):
    """
    convert file to element
    :param file_path:
    :return:
    """
    if not os.path.exists(file_path):
        return
    with open(file_path, 'rb') as f:
        return html2element(f.read())


def html2element(html: str):
    """
    convert html to HtmlElement
    :param html:
    :return:
    """
    if not html:
        return None
    try:
        return fromstring(html)
    except:
        print(html)


def rest_client():
    html_list = [file2element(r"C:\Users\longjiang\Desktop\list1.html")]
    # html_list = [file2element(r"C:\Users\longjiang\Desktop\detail1.html")]

    SPLIT_SEP = " "
    sample_text_list = []
    for html in html_list:
        sample_text = SPLIT_SEP.join(jieba.lcut(tostring(html, encoding="utf-8"), HMM=False))
        sample_text_list.append([sample_text])

    a = np.array(sample_text_list)
    data = {"instances": a.tolist()}
    # 进行预测
    r = requests.post("http://xx.xx.xx.xx:8501/v1/models/html_type_identify:predict",
                      json=data)

    def _sigmoid(x):
        return 1 / (1 + np.exp(-x))

    res = r.json()['predictions'][0][0]

    print(_sigmoid(res))


if __name__=='__main__':
    rest_client()

二. 在k8s中部署

  1. 创建docker镜像
  • 后台运行serving容器 docker run -d --rm --name serving_base tensorflow/serving

1.1 拷贝模型数据到容器中的model目录

docker cp /root/longjiang/newspage-type-identify/model serving_base:/models/newspage_type_identify

1.2 commit容器,将新的镜像保存为serving newspage_type_identify:

docker commit --change "ENV MODEL_NAME newspage_type_identify" serving_base newspage_type_identify_serving

newspage_type_identify_serving将是我们的新的镜像

1.3 停止并删除serving base镜像

docker kill serving_base

docker rm serving_base

2. 测试自定义的serving

2.1 启动服务

如果之前的服务还启动着,要停掉,否则8501端口被占用会无法启动新服务

docker run -d -p 8501:8501 -t newspage_type_identify_serving &

使用上面的客户端调用测试一下,注意,这一步我把接口名字换成了 /v1/models/newspage_type_identify:predict

3. 部署到k8s

将镜像推送到私有仓库,私有仓库的搭建见:juejin.cn/post/710149…, 当然也可以上传到docker hub的私有库里去,或者使用阿里云的免费私有仓库 3.1 将自定义的镜像推送到镜像仓库

docker tag newspage_type_identify_serving:latest 10.0.0.12:5000/newspage_type_identify_serving:v1

docker push 10.0.0.12:5000/newspage_type_identify_serving:v1
# login success 
docker tag newspage_type_identify_serving:latest registry.cn-hangzhou.aliyuncs.com/nln/newspage_type_identify_serving:v1 
docker push registry.cn-hangzhou.aliyuncs.com/nln/newspage_type_identify_serving:v1

3.2 通过kubectl 命令创建secret

kubectl create secret docker-registry [secret名称] --docker-server=[私有仓库ip:端口] --docker-username=[用户名] --docker-password=[密码] --namespace=[k8s命名空间]

例如

kubectl create secret docker-registry aliyun-registry-secret --docker-server=registry.cn-hangzhou.aliyuncs.com/nln --docker-username=xxxxxxxx --docker-password=xxxxxxx

创建完成后,可以查看下

kubectl get secret aliyun-registry-secret --output=yaml

3.2 创建deployment.yaml(master机器)

在master机器上创建 vim newspage_type_identify_k8s.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: newspage-type-identify-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: newspage-type-identify-server
  template:
    metadata:
      labels:
        app: newspage-type-identify-server
    spec:
      containers:
        - name: newspage-type-identify-container
          image: registry.cn-hangzhou.aliyuncs.com/nln/newspage_type_identify_serving:v1
          ports:
            - containerPort: 8501
      imagePullSecrets:
        - name: aliyun-registry-secret

---
apiVersion: v1
kind: Service
metadata:
  labels:
    run: newspage-type-identify-service
  name: newspage-type-identify-service
spec:
  ports:
    - port: 8501
      targetPort: 8501
  selector:
    app: newspage-type-identify-server
  type: LoadBalancer

然后
kubectl apply -f newspage_type_identify_k8s.yaml

  • 查看deployments kubectl get deployments

  • 查看部署的所有副本 kubectl get pod

  • 查看部署的services kubectl get services

  • 查看部署服务的详情 kubectl describe service newspage-type-identify-service,其中的IP即外网ip

  • 删除 newspage_type_identify_k8s.yaml kubectl delete -f newspage_type_identify_k8s.yaml

  • 删除所有pod kubectl delete pod --all

  • 删除某一个pod kubectl delete pod POD_NAME

  • 删除某一个service kubectl delete svc SERVICE_NAME

3.3 安装Kuboard

docker run -d \
  --restart=unless-stopped \
  --name=kuboard \
  -p 80:80/tcp \
  -p 10081:10081/tcp \
  -e KUBOARD_ENDPOINT="http://内网IP:80" \
  -e KUBOARD_AGENT_SERVER_TCP_PORT="10081" \
  -v /root/kuboard-data:/data \
  eipwork/kuboard:v3

注意:

  • KUBOARD_ENDPOINT 参数的作用是,让部署到 Kubernetes 中的 kuboard-agent 知道如何访问 Kuboard Server;
  • KUBOARD_ENDPOINT 中也可以使用外网 IP;
  • Kuboard 不需要和 K8S 在同一个网段,Kuboard Agent 甚至可以通过代理访问 Kuboard Server;
  • 建议在 KUBOARD_ENDPOINT 中使用域名;
  • 如果使用域名,必须能够通过 DNS 正确解析到该域名,如果直接在宿主机配置 /etc/hosts 文件,将不能正常运行;

参数解释:

  • 建议将此命令保存为一个 shell 脚本,例如 start-kuboard.sh,后续升级 Kuboard 或恢复 Kuboard 时,需要通过此命令了解到最初安装 Kuboard 时所使用的参数;
  • 第 4 行,将 Kuboard Web 端口 80 映射到宿主机的 80 端口(您可以根据自己的情况选择宿主机的其他端口);
  • 第 5 行,将 Kuboard Agent Server 的端口 10081/tcp 映射到宿主机的 10081 端口(您可以根据自己的情况选择宿主机的其他端口);
  • 第 6 行,指定 KUBOARD_ENDPOINT 为 http://内网IP,如果后续修改此参数,需要将已导入的 Kubernetes 集群从 Kuboard 中删除,再重新导入;
  • 第 7 行,指定 KUBOARD_AGENT_SERVER 的端口为 10081,此参数与第 5 行中的宿主机端口应保持一致,修改此参数不会改变容器内监听的端口 10081,例如,如果第 5 行为 -p 30081:10081/tcp 则第 7 行应该修改为 -e KUBOARD_AGENT_SERVER_TCP_PORT="30081"
  • 第 8 行,将持久化数据 /data 目录映射到宿主机的 /root/kuboard-data 路径,请根据您自己的情况调整宿主机路径;

例如

docker run -d \
  --restart=unless-stopped \
  --name=kuboard \
  -p 8089:80/tcp \
  -p 10081:10081/tcp \
  -e KUBOARD_ENDPOINT="http://10.0.0.12:8089" \
  -e KUBOARD_AGENT_SERVER_TCP_PORT="10081" \
  -v /root/container/k8s_project/kuboard-data:/data \
  eipwork/kuboard:v3

访问

http://ip:port:8089

初始用户名密码:
admin
Kuboard123

【报错】

  1. No versions of servable W tensorflow_serving/sources/storage_path/file_system_storage_path_source.cc:268] No versions of servable html_type_identify found under base path /models/html_type_identify. Did you forget to name your leaf directory as a number (eg. '/1/')?

解决:模型目录不正确,检查是否是模型的绝对路径

  1. exceeds 10% of free system memory
W external/org_tensorflow/tensorflow/core/framework/cpu_allocator_impl.cc:80] Allocation of 19826176 exceeds 10% of free system memory

解决:网上的解决办法是减小batch size,没有试过

  1. External-IP 一直显示 pending Kubernetes 不为裸机集群提供网络负载均衡器的实现,如果使用minikube、kubeadm自检的Kubernetes集群,是不支持 LoadBalancer的(与 AWS、Google Cloud、阿里云等云厂商不同),此时,只能使用 NodePort 或 Ingress Controller, 相反,如果配置中部署了 LoadBalancer 则会出现 External-IP 一直处于 pending 的问题
    解决:
    使用Ingress为外部访问集群提供了一个统一入口,类似与nginx。
    要使用 Ingress,需要一个负载均衡器 + Ingress Controller
    如果是裸机(bare metal) 搭建的集群,你需要自己安装一个负载均衡插件,可以安装 MetalLB
    如果是云服务商,会自动给你配置,否则你的外部 IP 会是 “pending” 状态,无法使用。