2021-08-17 17:00:34 +08:00
|
|
|
from flask_appbuilder import Model
|
|
|
|
from sqlalchemy.orm import relationship
|
2022-10-11 14:25:25 +08:00
|
|
|
import json
|
|
|
|
from sqlalchemy import Text
|
2021-09-07 18:09:47 +08:00
|
|
|
from myapp.utils import core
|
2022-10-10 11:44:53 +08:00
|
|
|
|
2022-10-11 14:25:25 +08:00
|
|
|
from myapp.models.helpers import AuditMixinNullable
|
|
|
|
from flask import request
|
2021-08-17 17:00:34 +08:00
|
|
|
from .model_team import Project
|
2022-10-10 11:44:53 +08:00
|
|
|
|
2022-10-11 14:25:25 +08:00
|
|
|
from myapp import app
|
2021-08-17 17:00:34 +08:00
|
|
|
from myapp.models.base import MyappModelBase
|
2022-10-11 14:25:25 +08:00
|
|
|
from sqlalchemy import Column, Integer, String, ForeignKey
|
2022-10-10 11:44:53 +08:00
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
from flask import Markup
|
|
|
|
import datetime
|
|
|
|
metadata = Model.metadata
|
|
|
|
conf = app.config
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-02-26 22:36:57 +08:00
|
|
|
class service_common():
|
|
|
|
@property
|
|
|
|
def monitoring_url(self):
|
|
|
|
# return Markup(f'<a href="/service_modelview/clear/{self.id}">清理</a>')
|
|
|
|
url=self.project.cluster.get('GRAFANA_HOST','').strip('/')+conf.get('GRAFANA_SERVICE_PATH')+self.name
|
|
|
|
return Markup(f'<a href="{url}">监控</a>')
|
|
|
|
# https://www.angularjswiki.com/fontawesome/fa-flask/ <i class="fa-solid fa-monitor-waveform"></i>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_node_selector(self):
|
|
|
|
return self.get_default_node_selector(self.project.node_selector,self.resource_gpu,'service')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Service(Model,AuditMixinNullable,MyappModelBase,service_common):
|
2021-08-17 17:00:34 +08:00
|
|
|
__tablename__ = 'service'
|
|
|
|
id = Column(Integer, primary_key=True)
|
2022-08-08 20:11:53 +08:00
|
|
|
project_id = Column(Integer, ForeignKey('project.id'))
|
2021-08-17 17:00:34 +08:00
|
|
|
project = relationship(
|
|
|
|
Project, foreign_keys=[project_id]
|
|
|
|
)
|
2022-02-26 22:36:57 +08:00
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
name = Column(String(100), nullable=False,unique=True) # Used to generate pod service and vs
|
|
|
|
label = Column(String(100), nullable=False)
|
|
|
|
images = Column(String(200), nullable=False)
|
2021-08-17 17:00:34 +08:00
|
|
|
working_dir = Column(String(100),default='')
|
|
|
|
command = Column(String(1000),default='')
|
|
|
|
args = Column(Text,default='')
|
2022-08-08 20:11:53 +08:00
|
|
|
env = Column(Text,default='')
|
|
|
|
volume_mount = Column(String(200),default='')
|
|
|
|
node_selector = Column(String(100),default='cpu=true,serving=true')
|
2022-02-26 22:36:57 +08:00
|
|
|
replicas = Column(Integer,default=1)
|
2022-08-08 20:11:53 +08:00
|
|
|
ports = Column(String(100),default='80')
|
2021-08-17 17:00:34 +08:00
|
|
|
resource_memory = Column(String(100),default='2G')
|
|
|
|
resource_cpu = Column(String(100), default='2')
|
|
|
|
resource_gpu= Column(String(100), default='0')
|
|
|
|
deploy_time = Column(String(100), nullable=False,default=datetime.datetime.now)
|
2022-08-08 20:11:53 +08:00
|
|
|
host = Column(String(200), default='')
|
2022-08-22 13:28:23 +08:00
|
|
|
expand = Column(Text(65536), default='{}')
|
2022-02-26 22:36:57 +08:00
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
@property
|
|
|
|
def deploy(self):
|
2022-08-22 13:28:23 +08:00
|
|
|
monitoring_url = self.project.cluster.get('GRAFANA_HOST', '').strip('/') + conf.get('GRAFANA_SERVICE_PATH') + self.name
|
|
|
|
help_url=''
|
|
|
|
try:
|
|
|
|
help_url = json.loads(self.expand).get('help_url','') if self.expand else ''
|
|
|
|
except Exception as e:
|
|
|
|
print(e)
|
|
|
|
|
|
|
|
if help_url:
|
|
|
|
return Markup(f'<a target=_blank href="{help_url}">帮助</a> | <a href="/service_modelview/deploy/{self.id}">部署</a> | <a href="{monitoring_url}">监控</a> | <a href="/service_modelview/clear/{self.id}">清理</a>')
|
|
|
|
else:
|
|
|
|
return Markup(f'帮助 | <a href="/service_modelview/deploy/{self.id}">部署</a> | <a href="{monitoring_url}">监控</a> | <a href="/service_modelview/clear/{self.id}">清理</a>')
|
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
|
|
@property
|
|
|
|
def clear(self):
|
|
|
|
return Markup(f'<a href="/service_modelview/clear/{self.id}">清理</a>')
|
|
|
|
|
2021-11-25 18:05:19 +08:00
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
@property
|
|
|
|
def name_url(self):
|
|
|
|
# user_roles = [role.name.lower() for role in list(g.user.roles)]
|
|
|
|
# if "admin" in user_roles:
|
|
|
|
url = self.project.cluster['K8S_DASHBOARD_CLUSTER'] + '#/search?namespace=%s&q=%s' % (conf.get('SERVICE_NAMESPACE'), self.name.replace('_', '-'))
|
|
|
|
# else:
|
|
|
|
# url = conf.get('K8S_DASHBOARD_PIPELINE', '') + '#/search?namespace=%s&q=%s' % (conf.get('SERVICE_NAMESPACE'), self.name.replace('_', '-'))
|
|
|
|
|
|
|
|
return Markup(f'<a target=_blank href="{url}">{self.label}</a>')
|
|
|
|
|
2022-02-26 22:36:57 +08:00
|
|
|
def __repr__(self):
|
|
|
|
return self.name
|
|
|
|
|
2022-05-22 22:05:10 +08:00
|
|
|
@property
|
|
|
|
def ip(self):
|
2022-07-23 20:38:20 +08:00
|
|
|
port = 30000+10*self.id
|
2022-08-08 20:11:53 +08:00
|
|
|
# first, Use the proxy ip configured by the project group
|
2022-05-25 11:28:30 +08:00
|
|
|
SERVICE_EXTERNAL_IP = json.loads(self.project.expand).get('SERVICE_EXTERNAL_IP',None) if self.project.expand else None
|
2022-07-23 20:38:20 +08:00
|
|
|
if not SERVICE_EXTERNAL_IP:
|
2022-08-08 20:11:53 +08:00
|
|
|
# second, Use the global configuration proxy ip
|
2022-07-23 20:38:20 +08:00
|
|
|
SERVICE_EXTERNAL_IP = conf.get('SERVICE_EXTERNAL_IP',[])
|
|
|
|
if SERVICE_EXTERNAL_IP:
|
|
|
|
SERVICE_EXTERNAL_IP=SERVICE_EXTERNAL_IP[0]
|
2022-05-25 11:28:30 +08:00
|
|
|
|
2022-07-23 20:38:20 +08:00
|
|
|
if not SERVICE_EXTERNAL_IP:
|
2022-08-08 20:11:53 +08:00
|
|
|
ip = request.host[:request.host.rindex(':')] if ':' in request.host else request.host # remove port in host
|
2022-07-23 20:38:20 +08:00
|
|
|
if core.checkip(ip):
|
|
|
|
SERVICE_EXTERNAL_IP = ip
|
2022-05-25 11:28:30 +08:00
|
|
|
|
2022-05-22 22:05:10 +08:00
|
|
|
if SERVICE_EXTERNAL_IP:
|
2022-07-23 20:38:20 +08:00
|
|
|
host = SERVICE_EXTERNAL_IP + ":" + str(port)
|
2022-05-25 11:28:30 +08:00
|
|
|
return Markup(f'<a target=_blank href="http://{host}/">{host}</a>')
|
2022-05-22 22:05:10 +08:00
|
|
|
else:
|
|
|
|
return "未开通"
|
2022-02-26 22:36:57 +08:00
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
@property
|
|
|
|
def host_url(self):
|
|
|
|
url = "http://" + self.name + "." + conf.get('SERVICE_DOMAIN')
|
|
|
|
if self.host:
|
|
|
|
if 'http://' in self.host or 'https://' in self.host:
|
|
|
|
url = self.host
|
|
|
|
else:
|
|
|
|
url = "http://"+self.host
|
|
|
|
|
|
|
|
return Markup(f'<a target=_blank href="{url}">{url}</a>')
|
|
|
|
|
2022-02-26 22:36:57 +08:00
|
|
|
|
|
|
|
class InferenceService(Model,AuditMixinNullable,MyappModelBase,service_common):
|
|
|
|
__tablename__ = 'inferenceservice'
|
|
|
|
id = Column(Integer, primary_key=True)
|
2022-08-08 20:11:53 +08:00
|
|
|
project_id = Column(Integer, ForeignKey('project.id'))
|
2022-02-26 22:36:57 +08:00
|
|
|
project = relationship(
|
|
|
|
Project, foreign_keys=[project_id]
|
|
|
|
)
|
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
name = Column(String(100), nullable=True,unique=True)
|
|
|
|
label = Column(String(100), nullable=False)
|
2022-02-26 22:36:57 +08:00
|
|
|
|
|
|
|
service_type= Column(String(100),nullable=True,default='serving')
|
|
|
|
model_name = Column(String(200),default='')
|
|
|
|
model_version = Column(String(200),default='')
|
|
|
|
model_path = Column(String(200),default='')
|
|
|
|
model_type = Column(String(200),default='')
|
|
|
|
model_input = Column(Text(65536), default='')
|
|
|
|
model_output = Column(Text(65536), default='')
|
2022-08-08 20:11:53 +08:00
|
|
|
inference_config = Column(Text(65536), default='') # make configmap
|
2022-02-26 22:36:57 +08:00
|
|
|
model_status = Column(String(200),default='offline')
|
|
|
|
# model_status = Column(Enum('offline','test','online','delete'),nullable=True,default='offline')
|
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
transformer=Column(String(200),default='') # pre process and post process
|
2022-02-26 22:36:57 +08:00
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
images = Column(String(200), nullable=False)
|
2022-02-26 22:36:57 +08:00
|
|
|
working_dir = Column(String(100),default='')
|
|
|
|
command = Column(String(1000),default='')
|
|
|
|
args = Column(Text,default='')
|
2022-08-08 20:11:53 +08:00
|
|
|
env = Column(Text,default='')
|
|
|
|
volume_mount = Column(String(2000),default='')
|
|
|
|
node_selector = Column(String(100),default='cpu=true,serving=true')
|
2022-02-26 22:36:57 +08:00
|
|
|
min_replicas = Column(Integer,default=1)
|
|
|
|
max_replicas = Column(Integer, default=1)
|
2022-08-08 20:11:53 +08:00
|
|
|
hpa = Column(String(400), default='')
|
|
|
|
metrics = Column(Text(65536), default='')
|
|
|
|
health = Column(String(400), default='')
|
|
|
|
sidecar = Column(String(400), default='')
|
|
|
|
ports = Column(String(100),default='80')
|
2022-02-26 22:36:57 +08:00
|
|
|
resource_memory = Column(String(100),default='2G')
|
|
|
|
resource_cpu = Column(String(100), default='2')
|
|
|
|
resource_gpu= Column(String(100), default='0')
|
|
|
|
deploy_time = Column(String(100), nullable=True,default=datetime.datetime.now)
|
2022-08-08 20:11:53 +08:00
|
|
|
host = Column(String(200), default='')
|
2022-07-17 18:38:49 +08:00
|
|
|
expand = Column(Text(65536), default='{}')
|
2022-08-08 20:11:53 +08:00
|
|
|
canary = Column(String(400), default='')
|
|
|
|
shadow = Column(String(400), default='')
|
2022-02-26 22:36:57 +08:00
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
run_id = Column(String(100),nullable=True)
|
2022-02-26 22:36:57 +08:00
|
|
|
run_time = Column(String(100))
|
2022-08-08 20:11:53 +08:00
|
|
|
deploy_history = Column(Text(65536), default='')
|
2021-09-07 18:09:47 +08:00
|
|
|
|
2022-08-08 20:11:53 +08:00
|
|
|
priority = Column(Integer,default=1) # giving priority to meeting high-priority resource needs
|
2021-08-17 17:00:34 +08:00
|
|
|
|
|
|
|
@property
|
2022-02-26 22:36:57 +08:00
|
|
|
def model_name_url(self):
|
2022-07-26 20:47:49 +08:00
|
|
|
url = self.project.cluster['K8S_DASHBOARD_CLUSTER'] + '#/search?namespace=%s&q=%s' % (conf.get('SERVICE_NAMESPACE'), self.name.replace('_', '-'))
|
2022-02-26 22:36:57 +08:00
|
|
|
return Markup(f'<a target=_blank href="{url}">{self.model_name}</a>')
|
2021-08-17 17:00:34 +08:00
|
|
|
|
2022-08-28 20:24:32 +08:00
|
|
|
@property
|
|
|
|
def replicas_html(self):
|
|
|
|
return "%s~%s"%(self.min_replicas,self.max_replicas)
|
|
|
|
|
|
|
|
|
2022-08-20 13:45:54 +08:00
|
|
|
@property
|
|
|
|
def resource(self):
|
|
|
|
return 'cpu:%s,memory:%s,gpu:%s'%(self.resource_cpu,self.resource_memory,self.resource_gpu)
|
|
|
|
|
2022-02-26 22:36:57 +08:00
|
|
|
@property
|
|
|
|
def operate_html(self):
|
2022-08-22 13:28:23 +08:00
|
|
|
help_url=''
|
|
|
|
try:
|
|
|
|
help_url = json.loads(self.expand).get('help_url','') if self.expand else ''
|
|
|
|
except Exception as e:
|
|
|
|
print(e)
|
|
|
|
|
|
|
|
monitoring_url=self.project.cluster.get('GRAFANA_HOST','').strip('/')+conf.get('GRAFANA_SERVICE_PATH')+self.name
|
2022-07-26 20:47:49 +08:00
|
|
|
# if self.created_by.username==g.user.username or g.user.is_admin():
|
|
|
|
dom = f'''
|
|
|
|
<a target=_blank href="/inferenceservice_modelview/deploy/debug/{self.id}">调试</a> |
|
|
|
|
<a href="/inferenceservice_modelview/deploy/test/{self.id}">部署测试</a> |
|
|
|
|
<a href="/inferenceservice_modelview/deploy/prod/{self.id}">部署生产</a> |
|
2022-08-22 13:28:23 +08:00
|
|
|
<a target=_blank href="{monitoring_url}">监控</a> |
|
2022-07-26 20:47:49 +08:00
|
|
|
<a href="/inferenceservice_modelview/clear/{self.id}">清理</a>
|
|
|
|
'''
|
2022-08-22 13:28:23 +08:00
|
|
|
if help_url:
|
|
|
|
dom=f'<a target=_blank href="{help_url}">帮助</a> | '+dom
|
|
|
|
else:
|
2022-10-11 14:25:25 +08:00
|
|
|
dom = '帮助 | ' + dom
|
2022-02-26 22:36:57 +08:00
|
|
|
return Markup(dom)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def debug(self):
|
|
|
|
return Markup(f'<a target=_blank href="/inferenceservice_modelview/debug/{self.id}">调试</a>')
|
|
|
|
|
|
|
|
@property
|
|
|
|
def test_deploy(self):
|
|
|
|
return Markup(f'<a href="/inferenceservice_modelview/deploy/test/{self.id}">部署测试</a>')
|
|
|
|
|
|
|
|
@property
|
|
|
|
def deploy(self):
|
|
|
|
return Markup(f'<a href="/inferenceservice_modelview/deploy/prod/{self.id}">部署生产</a>')
|
|
|
|
|
|
|
|
@property
|
|
|
|
def clear(self):
|
|
|
|
return Markup(f'<a href="/inferenceservice_modelview/clear/{self.id}">清理</a>')
|
2021-08-17 17:00:34 +08:00
|
|
|
|
2022-05-31 14:16:55 +08:00
|
|
|
@property
|
|
|
|
def ip(self):
|
2022-07-23 20:38:20 +08:00
|
|
|
port = 20000+10*self.id
|
2022-08-08 20:11:53 +08:00
|
|
|
# first, Use the proxy ip configured by the project group
|
2022-05-31 14:16:55 +08:00
|
|
|
SERVICE_EXTERNAL_IP = json.loads(self.project.expand).get('SERVICE_EXTERNAL_IP',None) if self.project.expand else None
|
2022-07-23 20:38:20 +08:00
|
|
|
if not SERVICE_EXTERNAL_IP:
|
2022-08-08 20:11:53 +08:00
|
|
|
# second, Use the global configuration proxy ip
|
2022-07-23 20:38:20 +08:00
|
|
|
SERVICE_EXTERNAL_IP = conf.get('SERVICE_EXTERNAL_IP', [])
|
|
|
|
if SERVICE_EXTERNAL_IP:
|
|
|
|
SERVICE_EXTERNAL_IP = SERVICE_EXTERNAL_IP[0]
|
2022-05-31 14:16:55 +08:00
|
|
|
|
2022-07-23 20:38:20 +08:00
|
|
|
if not SERVICE_EXTERNAL_IP:
|
2022-08-08 20:11:53 +08:00
|
|
|
ip = request.host[:request.host.rindex(':')] if ':' in request.host else request.host # remove port in host
|
2022-07-23 20:38:20 +08:00
|
|
|
if core.checkip(ip):
|
|
|
|
SERVICE_EXTERNAL_IP = ip
|
2022-05-31 14:16:55 +08:00
|
|
|
|
|
|
|
if SERVICE_EXTERNAL_IP:
|
2022-07-23 20:38:20 +08:00
|
|
|
host = SERVICE_EXTERNAL_IP + ":" + str(port)
|
2022-05-31 14:16:55 +08:00
|
|
|
return Markup(f'<a target=_blank href="http://{host}/">{host}</a>')
|
|
|
|
else:
|
|
|
|
return "未开通"
|
|
|
|
|
2022-07-23 20:38:20 +08:00
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
def __repr__(self):
|
|
|
|
return self.name
|
|
|
|
|
2022-02-26 22:36:57 +08:00
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
def inference_host_url(self):
|
2022-07-23 20:38:20 +08:00
|
|
|
url = "http://" + self.name + "." + self.project.cluster.get('SERVICE_DOMAIN',conf.get('SERVICE_DOMAIN'))
|
2022-02-26 22:36:57 +08:00
|
|
|
if self.host:
|
|
|
|
if 'http://' in self.host or 'https://' in self.host:
|
|
|
|
url = self.host
|
|
|
|
else:
|
|
|
|
url = "http://"+self.host
|
|
|
|
|
|
|
|
link = url
|
|
|
|
if self.service_type=='tfserving':
|
|
|
|
link+="/v1/models/"+self.model_name
|
|
|
|
if self.service_type=='torch-server':
|
|
|
|
link+=":8080/models"
|
|
|
|
hosts=f'''
|
|
|
|
<a target=_blank href="{link}">{url}</a>
|
|
|
|
<br><a target=_blank href="{link.replace('http://','http://debug.').replace('https://','https://debug.')}">{url.replace('http://','http://debug.').replace('https://','https://debug.')}</a>
|
|
|
|
<br><a target=_blank href="{link.replace('http://','http://test.').replace('https://','https://test.')}">{url.replace('http://','http://test.').replace('https://','https://test.')}</a>
|
|
|
|
'''
|
|
|
|
|
|
|
|
hosts=f'<a target=_blank href="{link}">{url}</a>'
|
|
|
|
return Markup(hosts)
|
|
|
|
|
|
|
|
|
2021-08-17 17:00:34 +08:00
|
|
|
|
2022-05-31 14:16:55 +08:00
|
|
|
def clone(self):
|
|
|
|
return InferenceService(
|
|
|
|
project_id=self.project_id,
|
|
|
|
name = self.name+"-copy",
|
|
|
|
label = self.label,
|
|
|
|
service_type = self.service_type,
|
|
|
|
model_name = self.model_name,
|
|
|
|
model_version = self.model_version,
|
|
|
|
model_path = self.model_path,
|
|
|
|
model_type = self.model_type,
|
|
|
|
model_input = self.model_input,
|
|
|
|
model_output = self.model_output,
|
|
|
|
model_status = 'offline',
|
|
|
|
|
|
|
|
transformer = self.transformer,
|
|
|
|
|
|
|
|
images = self.images,
|
|
|
|
working_dir = self.working_dir,
|
|
|
|
command = self.command,
|
|
|
|
args = self.args,
|
|
|
|
env = self.env,
|
|
|
|
volume_mount =self.volume_mount,
|
|
|
|
node_selector = self.node_selector,
|
|
|
|
min_replicas = self.min_replicas,
|
|
|
|
max_replicas = self.max_replicas,
|
|
|
|
hpa = self.hpa,
|
|
|
|
metrics = self.metrics,
|
|
|
|
health = self.health,
|
|
|
|
sidecar = self.sidecar,
|
|
|
|
ports = self.ports,
|
|
|
|
resource_memory = self.resource_memory,
|
|
|
|
resource_cpu = self.resource_cpu,
|
|
|
|
resource_gpu = self.resource_gpu,
|
|
|
|
deploy_time = '',
|
|
|
|
host = self.host,
|
|
|
|
expand = '{}',
|
|
|
|
canary = '',
|
|
|
|
shadow = '',
|
|
|
|
|
|
|
|
run_id = '',
|
|
|
|
run_time = '',
|
|
|
|
deploy_history = ''
|
|
|
|
|
|
|
|
)
|