89293f87 by 周伟奇

first commit

0 parents
## 1.目录结构
```
.
├── bin
│   ├── conf.sh
│   ├── control.sh 运维操作脚本
│   ├── docker.sh
│   └── utils.sh
├── configs
│   └── settings.sh 配置文件
│   └── license 授权文件目录
├── logs 日志文件目录
└── repo
├── images docker镜像文件目录
├── models
└── python
```
---
## 2.服务部署
### 2.1 授权管理
- 获取服务器信息,发给授权人员生成授权文件(已有授权文件忽略此步骤)
```shell
bin/control.sh systeminfo
```
- 授权文件更新
```shell
bin/control.sh update_license /path_to/xxx.secret
```
### 2.2 配置文件
- 编辑`configs/settings.sh`
```
# 日志目录
LOG_BASE_DIR=${BASE_DIR}/logs
# 端口
BACKEND_PORT=80
# TODO 此端口随机,不需要配置
TRITON_HTTP_PORT=8000
TRITON_GRPC_PORT=8001
TRITON_METRICS_PORT=8002
# sanic-worker TODO 可配置
BACKEND_SANIC_WORKER_NUM=4
# GPU/CPU
# 使用CPU: TRITON_GPUS=
# 使用GPU0/GPU1: TRITON_GPUS=0,1
TRITON_GPUS=0
```
### 2.3 启动
```shell
bin/control.sh start all
```
### 2.4 停止
```shell
bin/control.sh stop all
```
### 2.5 重启
- 重启后端服务,适用于只更新了授权文件
```shell
bin/control.sh restart backend
```
- 重启所有,适用于更新了repo文件夹
```shell
bin/control.sh restart all
```
### 2.6 日志查看
```shell
bin/control.sh logs triton
bin/control.sh logs backend
```
## TODO
- 授权部分
- backend logs
- backend sanic worker num可配置
- 随机选择triton端口,该配置项无意义
- ASGI & supervisor ?
- async ?
\ No newline at end of file
#!/bin/bash
set -euo pipefail
# shellcheck is a shell lint
# shellcheck disable=SC1091
# shellcheck disable=SC1090
source "${_BIN_DIR}/utils.sh"
# 目录相关
# 配置文件目录
CONFIG_DIR=${BASE_DIR}/configs
# 授权文件目录
LICENSE_DIR=${CONFIG_DIR}/license
REPO_DIR=${BASE_DIR}/repo
# docker镜像目录
DOCKER_IMAGE_DIR=${REPO_DIR}/images
# 模型与代码相关目录
BACKEND_PYTHON_DIR=${REPO_DIR}/python
TRITON_MODEL_DIR=${REPO_DIR}/models
# docker相关
# triton相关
TRITON_CONTAINER_NAME=tritonserver
TRITON_IMAGE_NAME=tritonserver
TRITON_IMAGE_TAG=1.0
TRITON_IMAGE=${TRITON_IMAGE_NAME}:${TRITON_IMAGE_TAG}
TRITON_IMAGE_SAVE_NAME=tritonserver-tf2-pt.tar
TRITON_IMAGE_SAVE_PATH=${DOCKER_IMAGE_DIR}/${TRITON_IMAGE_SAVE_NAME}
# 工程相关
BACKEND_CONTAINER_NAME=situ-ocr
BACKEND_IMAGE_NAME=situ-ocr
BACKEND_IMAGE_TAG=1.0
BACKEND_IMAGE=${BACKEND_IMAGE_NAME}:${BACKEND_IMAGE_TAG}
BACKEND_IMAGE_SAVE_NAME=situ-ocr.tar
BACKEND_IMAGE_SAVE_PATH=${DOCKER_IMAGE_DIR}/${BACKEND_IMAGE_SAVE_NAME}
# 加载配置数据
_config_file_path="${CONFIG_DIR}/settings.sh"
if [ -f "${_config_file_path}" ]; then
# shellcheck disable=SC1091
# shellcheck disable=SC1090
source "${_config_file_path}"
else
_notify ERROR "config file <${_config_file_path}> does not exist"
exit 1
fi
# 这些配置依赖configs/settings.sh, 所以需要放在source之后
# TRITON容器启动参数相关
# -env
TRITON_CONTAINER_ENVS=()
TRITON_ENVS_OPTION=""
if (( ${#TRITON_CONTAINER_ENVS[@]} > 0 )); then
checkEnvsOption ${TRITON_CONTAINER_ENVS[@]}
TRITON_ENVS_OPTION="$(getOption "-env" ${TRITON_CONTAINER_ENVS[@]})"
fi
# -v
TRITON_CONTAINER_MODEL_DIR=/models
TRITON_CONTAINER_VOLUMES=(
"${TRITON_MODEL_DIR}:${TRITON_CONTAINER_MODEL_DIR}"
)
TRITON_VOLUMES_OPTION=""
if (( ${#TRITON_CONTAINER_VOLUMES[@]} > 0 )); then
checkVolumesOption ${TRITON_CONTAINER_VOLUMES[@]}
TRITON_VOLUMES_OPTION="$(getOption "-v" ${TRITON_CONTAINER_VOLUMES[@]})"
fi
# -p
TRITON_CONTAINER_PORTS=(
"${TRITON_HTTP_PORT}:8000"
"${TRITON_GRPC_PORT}:8001"
"${TRITON_METRICS_PORT}:8002"
)
TRITON_PORTS_OPTION=""
if (( ${#TRITON_CONTAINER_PORTS[@]} > 0 )); then
checkPortUsedStatus ${TRITON_CONTAINER_PORTS[@]}
TRITON_PORTS_OPTION="$(getOption "-p" ${TRITON_CONTAINER_PORTS[@]})"
fi
# --gpus
TRITON_GPUS_OPTION=""
if [ $TRITON_GPUS ]; then
TRITON_GPUS_OPTION="--gpus \"device=${TRITON_GPUS}\""
fi
# -env -v -p --gpus --model-repository
TRITON_OPTIONS="${TRITON_ENVS_OPTION} ${TRITON_VOLUMES_OPTION} ${TRITON_PORTS_OPTION} ${TRITON_GPUS_OPTION} --model-repository=${TRITON_CONTAINER_MODEL_DIR}"
# BACKEND容器启动参数相关
# -env
BACKEND_CONTAINER_ENVS=(
"WORKER_NUM=${BACKEND_WORKER_NUM}"
)
BACKEND_ENVS_OPTION=""
if (( ${#BACKEND_CONTAINER_ENVS[@]} > 0 )); then
checkEnvsOption ${BACKEND_CONTAINER_ENVS[@]}
BACKEND_ENVS_OPTION="$(getOption "-env" ${BACKEND_CONTAINER_ENVS[@]})"
fi
# -v
BACKEND_CONTAINER_VOLUMES=(
"${LOG_BASE_DIR}:/logs"
)
BACKEND_VOLUMES_OPTION=""
if (( ${#BACKEND_CONTAINER_VOLUMES[@]} > 0 )); then
checkVolumesOption ${BACKEND_CONTAINER_VOLUMES[@]}
BACKEND_VOLUMES_OPTION="$(getOption "-v" ${BACKEND_CONTAINER_VOLUMES[@]})"
fi
# -p
BACKEND_CONTAINER_PORTS=(
"${BACKEND_PORT}:9002"
)
BACKEND_PORTS_OPTION=""
if (( ${#BACKEND_CONTAINER_PORTS[@]} > 0 )); then
checkPortUsedStatus ${BACKEND_CONTAINER_PORTS[@]}
BACKEND_PORTS_OPTION="$(getOption "-p" ${BACKEND_CONTAINER_PORTS[@]})"
fi
# -env -v -p
BACKEND_OPTIONS="${BACKEND_ENVS_OPTION} ${BACKEND_VOLUMES_OPTION} ${BACKEND_PORTS_OPTION}"
\ No newline at end of file
#/bin/bash
set -euo pipefail
_BIN_DIR=$(dirname "$(readlink -f "$0")")
BASE_DIR=$(dirname "${_BIN_DIR}")
# shellcheck disable=SC1091
# shellcheck disable=SC1090
source "${_BIN_DIR}/conf.sh"
# shellcheck disable=SC1091
# shellcheck disable=SC1090
source "${_BIN_DIR}/docker.sh"
function _control_triton(){
cat <<END
triton settings:
TRITON_IMAGE: ${TRITON_IMAGE}
TRITON_CONTAINER_NAME: ${TRITON_CONTAINER_NAME}
TRITON_OPTIONS: ${TRITON_OPTIONS}
TRITON_IMAGE_SAVE_PATH: ${TRITON_IMAGE_SAVE_PATH}
END
case "${1:-help}" in
start) _docker_func start "${TRITON_IMAGE}" "${TRITON_CONTAINER_NAME}" "${TRITON_OPTIONS}" "${TRITON_IMAGE_SAVE_PATH}" ;;
stop) _docker_func stop "${TRITON_CONTAINER_NAME}" ;;
restart) _docker_func restart "${TRITON_CONTAINER_NAME}" ;;
rm) _docker_func rm "${TRITON_CONTAINER_NAME}" ;;
inside) _docker_func inside "${TRITON_CONTAINER_NAME}" ;;
top) _docker_func top "${TRITON_CONTAINER_NAME}" ;;
dlogs) _docker_func logs "${TRITON_CONTAINER_NAME}" ;;
logs) _docker_func logs "${TRITON_CONTAINER_NAME}" ;;
is_running) _docker_func is_running "${TRITON_CONTAINER_NAME}" ;;
debug) _docker_func debug "${TRITON_IMAGE}" "${TRITON_CONTAINER_NAME}" "${TRITON_OPTIONS}" ;;
# 其他
*) _echo_help_message ;;
esac
}
function _control_backend(){
cat <<END
backend settings:
BACKEND_IMAGE: ${BACKEND_IMAGE}
BACKEND_CONTAINER_NAME: ${BACKEND_CONTAINER_NAME}
BACKEND_OPTIONS: ${BACKEND_OPTIONS}
BACKEND_IMAGE_SAVE_PATH: ${BACKEND_IMAGE_SAVE_PATH}
END
case "${1:-help}" in
start) _docker_func start "${BACKEND_IMAGE}" "${BACKEND_CONTAINER_NAME}" "${BACKEND_OPTIONS}" "${BACKEND_IMAGE_SAVE_PATH}" ;;
stop) _docker_func stop "${BACKEND_CONTAINER_NAME}" ;;
restart) _docker_func restart "${BACKEND_CONTAINER_NAME}" ;;
rm) _docker_func rm "${BACKEND_CONTAINER_NAME}" ;;
inside) _docker_func inside "${BACKEND_CONTAINER_NAME}" ;;
top) _docker_func top "${BACKEND_CONTAINER_NAME}" ;;
dlogs) _docker_func logs "${BACKEND_CONTAINER_NAME}" ;;
is_running) _docker_func is_running "${BACKEND_CONTAINER_NAME}" ;;
debug) _docker_func debug "${BACKEND_IMAGE}" "${BACKEND_CONTAINER_NAME}" "${BACKEND_OPTIONS}" ;;
# 其他
*) _echo_help_message ;;
esac
}
function _control_all(){
case "${1:-help}" in
start) _control_triton start && _control_backend start ;;
stop) _control_triton stop && _control_backend stop ;;
restart) _control_triton restart && _control_backend restart ;;
rm) _control_triton rm && _control_backend rm ;;
# 其他
*) _echo_help_message ;;
esac
}
# 更新授权文件
function _updateLincese(){
local license_file="$1"
if ! [ -e "${license_file}" ];
then
_notify ERROR "license file <${license_file}> does not exist" ;
exit 1
fi
local target_file=${LICENSE_DIR}/$(basename ${license_file})
if [ -e "${target_file}" ];
then
now=$(date +%Y-%m-%d_%H:%M:%S)
backup_path="${target_file}__backup_${now}"
mv "${target_file}" "${backup_path}"
_notify SUCCESS "\"${target_file}\" already exists, mv to ${backup_path}"
fi
cp "${license_file}" "${LICENSE_DIR}"
_notify SUCCESS "cp \"${license_file}\" to ${LICENSE_DIR}"
}
function _echo_help_message(){
cat <<END
用法: $(basename "$0") [选项] [参数] ...
Container(容器):
- start triton/backend/all 启动部署用的容器
- stop triton/backend/all 停止当前部署的容器
- restart triton/backend/all 重启当前部署的容器
- rm triton/backend/all 删除当前部署的容器
- inside triton/backend 已交互式进入当前部署的容器
- is_running triton/backend 判断当前部署的容器是否在运行
- top triton/backend 查看当前部署的容器的进程
- dlogs triton/backend 查看当前部署的容器的日志
- debug triton/backend 创建并以交互式进入debug用的容器
Tools(工具):
- logs triton/backend 查看运行日志
- systeminfo 查看系统信息,用于授权
- update_license 更新授权文件
END
}
function _getSystemInfo(){
echo "尚未完成"
}
function _server_control() {
case "${2:-help}" in
triton) _control_triton $1 ;;
backend) _control_backend $1 ;;
all) _control_all $1 ;;
# 其他
*) _echo_help_message ;;
esac
}
case "${1:-help}" in
systeminfo) _getSystemInfo ;;
update_license) _updateLincese $2 ;;
* ) _server_control $@ ;;
esac
#!/bin/bash
function _docker_run(){
local run_type=$1
local image=$2
local container_name=$3
local input_options=$4
local all_options
if [ ${run_type} = "up" ]; then
# 启动部署用的container
all_options = "-d --net=bridge ${input_options} --name ${container_name} ${image}"
else
# 创建debug用的container
all_options = "-it --rm --net=bridge ${input_options} --entrypoint=/bin/bash ${image}"
fi
_notify INFO "run image ${image} with options: ${all_options}"
docker run ${all_options}
_notify SUCCESS "run image ${image} with options: ${all_options}"
}
function _load(){
local load_file=$1
if ! [ -e "${load_file}" ];
then
_notify ERROR "file <${load_file}> to be loaded does not exist" ;
exit 1
fi
_notify INFO "loading docker image from file <${load_file}>"
docker load -i ${load_file}
_notify SUCCESS "loaded docker image from file <${load_file}>"
}
# 判断镜像是否存在
function _is_image_exists(){
local image_name=$1
local image_id
image_id=$(docker image ls -a -q ${image_name})
if [ ! "${image_id}" ]; then
echo "false"
else
echo "true"
fi
}
function _start(){
echo $@
local image_name=$1
local container_name=$2
local input_options=$3
local load_file=$4
local container_id
container_id=$(docker ps -a -q -f name="^/${container_name}$")
if [ ! "${container_id}" ]; then
if [ "$(_is_image_exists "${image_name}")" != "true" ]; then
_notify WARNING "image <${image_name}> does not exist"
_load ${load_file}
fi
_docker_run "up" ${image_name} ${container_name} ${input_options}
else
docker start "${container_id=}"
fi
}
# 判断容器是否存在
function _is_container_exists(){
local container_name=$1
local container_id
container_id=$(docker ps -a -q -f name="^/${container_name}$")
if [ ! "${container_id}" ]; then
echo "false"
else
echo "true"
fi
}
# 检测容器是否运行
function _is_container_running(){
if [ "$(_is_container_exists "$1")" != "true" ]; then
_notify ERROR "container <$1> does not exist"
exit 1
fi
docker inspect -f '{{.State.Running}}' "$1"
}
# 调用
function _docker_func(){
case "$1" in
start) shift 1 && _start "$@" ;;
stop) shift 1 && docker stop "$@" ;;
restart) shift 1 && docker restart "$@" ;;
rm) shift 1 && docker rm "$@" ;;
inside) shift 1 && docker exec -it "$@" /bin/bash ;;
top) shift 1 && docker top "$@" ;;
logs) shift 1 && docker logs "$@";;
is_running) shift 1 && _is_container_running "$@" ;;
debug) shift 1 && _docker_run "debug" "$@";;
esac
}
\ No newline at end of file
#!/bin/bash
set -euo pipefail
####################################
# 常用工具
####################################
# 输出带有颜色的文本
function _notify(){
local _type=$1
local _msg=$2
local _GREEN
_GREEN=$(tput setaf 2)
local _YELLOW
_YELLOW=$(tput setaf 190)
local _RED
_RED=$(tput setaf 1)
local _NORMAL
_NORMAL=$(tput sgr0)
local _color_flag
case ${_type} in
INFO) _color_flag=${_NORMAL} ;;
SUCCESS) _color_flag=${_GREEN} ;;
WARNING) _color_flag=${_YELLOW} ;;
ERROR) _color_flag=${_RED} ;;
*)
_color_flag=${_RED}
echo "${_color_flag} invalid type: ${_type}${_NORMAL}"
return 1;;
esac
printf -- "${_color_flag}${_type}\t${_NORMAL}$(date +"%F %T")\t${_msg}\\n"
}
# 检查端口占用情况
function checkPortUsedStatus() {
if (( $# > 0 )); then
for option in $@ ;
do
local section_num
section_num=$(echo "${option}" | awk -F ':' '{print NF}')
if [ "${section_num}" -ne "2" ] ; then
_notify ERROR "invalid port config <${option}>"
exit 1
fi
# 检查端口是否为数字
local port
port=$(echo "${option}"| awk -F ':' '{print $1}')
if [ -n "$(echo ${port} | sed 's/[0-9]//g')" ] ; then
_notify ERROR "invalid port config <${option}>"
exit 1
fi
# 检查端口占用情况
if lsof -i tcp:${port} | grep -q LISTEN; then
_notify ERROR "${port}端口被占用,请手动关闭"
lsof -i tcp:${port} | grep LISTEN
exit 1
fi
done
fi
}
# 获取容器的env配置选项
# 判断envs配置是否合法, 方法为判断"="的个数
function checkEnvsOption() {
if (( $# > 0 )); then
for _env in $@ ;
do
if [ "$(echo "${_env}" | awk -F '=' '{print NF}' )" -ne "2" ]; then
_notify ERROR "invalid env config <${_env}>"
exit 1
fi
done
fi
}
# 获取容器的volume配置选项
# 判断volumes配置是否合法
# * local_abs_path:remote_abs_path:flag
# * local_abs_path:remote_abs_path
# 目前简单判断,仅判断是否包含":",以及local_abs_path是否存在
function checkVolumesOption() {
if (( $# > 0 )); then
for volume in $@ ;
do
local section_num
section_num=$(echo "${volume}" | awk -F ':' '{print NF}')
if [ "${section_num}" -ne "2" ] && [ "${section_num}" -ne "3" ]; then
_notify ERROR "invalid volume config <${volume}>"
exit 1
fi
# 判断本地文件夹是否存在
local local_dir
local_dir=$(echo "${volume}"| awk -F ':' '{print $1}')
if ! [ -e "${local_dir}" ];
then
_notify ERROR "dir <${local_dir}> to be mounted does not exist" ;
exit 1
fi
done
fi
}
function getOption() {
local _type=$1
local output_option
output_option=""
shift 1
if (( $# > 0 )); then
for _option in $@ ;
do
output_option="${output_option}${_type} ${_option} "
done
fi
echo ${output_option}
}
# 如果文件夹存在就将其备份
# 备份方法为将文件夹重命名为 "原始文件夹名称+当前时间+4未随机字符串"
# 使用随机字符串是为了避免同一时间多次跑脚本导致文件夹重复
# backup_dir_if_exists $some_dir
function backup_dir_if_exists(){
if [ -d "$1" ]
then
now=$(date +%Y-%m-%d_%H:%M:%S)
random_string=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c4)
backup_dirname="$1__backup_${now}_${random_string}"
mv "$1" "${backup_dirname}"
_notify SUCCESS "\"$1\" already exists, mv to ${backup_dirname}"
fi
}
#!/bin/bash
set -euo pipefail
# 日志目录
LOG_BASE_DIR=${BASE_DIR}/logs
# 端口
BACKEND_PORT=80
TRITON_HTTP_PORT=8000
TRITON_GRPC_PORT=8001
TRITON_METRICS_PORT=8002
# sanic-worker
BACKEND_WORKER_NUM=4
# GPU/CPU
# 使用CPU: TRITON_GPUS=
# 使用GPU0/GPU1: TRITON_GPUS=0,1
TRITON_GPUS=0
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!