Open kh3654po opened 1 year ago
bazel
Bazel은 Google에서 개발한 오픈 소스 빌드 도구로 소프트웨어 프로젝트의 빌드, 테스트 및 배포를 자동화하기 위해 사용됨. Bazel은 대규모 프로젝트 및 분산 빌드 환경에서 효과적이며, 여러 프로그래밍 언어로 작성된 다양한 소스 코드를 관리하는 데 도움이 됨.
BUILD 파일을 작성하고 bazel build 명령어로 빌드함
BUILD 파일안에서 사용하는 언어별 규칙이 있음
https://bazel.build/reference/be/overview?hl=ko#rules
deps는 해당 라이브러리 또는 바이너리를 빌드하기위해서 필요한 라이브러리를 뜻함. 즉 deps에 있는 라이브러리들이 먼저 빌드되어야함(종속성)
사용예
// BUILD file
cc_library(
name = "example",
srcs = [
"example.cc",
],
hdrs = ["example.h"],
)
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
deps = [
":hello-greet",
],
#!/bin/bash
bazel build //main:hello-world
위 코드는 example.h 헤더파일과 example.cc 소스파일로 example이라는 라이브러리를 생성하고 이 라이브러리와 hello-world.cc 소스파일로 hello-world라는 바이너리파일을 생성한다.
현재 tf model server를 실행하는 명령어는 tensorflow_model_server인데 https://github.com/tensorflow/serving/blob/master/tensorflow_serving/model_servers/BUILD 에서 457 라인을 보면 cc_library로 tensorflow_model_server 생성하는 것을 볼 수 있다.
해당 바이너리파일은 tensorflow_model_server_main_lib라는 라이브러리에 의존해 생성됨. 코드로는 427라인
해당 라이브러리는 main.cc 및 version.cc, deps에 존재하는 라이브러리들에 의존함
main.cc
main은 서버를 생성하고 종료될때까지 기다리는 코드만 있어서 tensorflow_model_server_main_lib에 있는 deps를 보니 "@org_tensorflow//tensorflow/c:c_api"가 존재.
tf model server에 rest로 요청을 보낼 때 http://nvidia-xavier2:8501/v1/models/inception_v3:predict 주소에 추론데이터와 함께 전송하는데 api와 관련이 있을거 같아 해당 부분을 분석해야함
해당 라이브러리는 https://github.com/tensorflow/tensorflow/blob/master/tensorflow/c/BUILD 219라인에 있음
api와 관련된 부분은 위뿐만 아니라 https://github.com/tensorflow/serving/tree/master/tensorflow_serving/apis에도 존재 해당 폴더에는 proto 확장자 파일이 있는데 이부분도 알아보아야함
요즘 소프트웨어들은 코드 구성 자체도 그렇게 간단하지가 않구나. 코드를 분석할때는 같이 실행을 해보면서 보면 코드의 실행 시퀀스를 따라가기가 쉬운 경우가 많아.
예를 들어 석현이가 전에 캡쳐한 이미지를 보면 model_servers/http_server.cc 라는 코드에서 요청을 받는듯 한데. 해당 코드 부터 따라가보면서 만약에 필요하다면 디버그 메시지를 추가하고 새롭게 빌드해서 실행해보는 방법도 있을것 같아. 시간이 걸리는 작업이지만 하나씩 해보자.
교수님 말씀처럼 http_server.cc로 부터 시작하여 파악을 해나가니 좀더 쉬운거 같습니다. 정리중에 있는데 어느정도 정리되면 공유드리겠습니다.
http_server.cc
https://github.com/tensorflow/serving/blob/master/tensorflow_serving/model_servers/http_server.cc
147라인의 ProcessRequest 함수내에 아래의 코드가 존재
“VLOG(1) << "Processing HTTP request: " << req->http_method() << " " << req->uri_path() << " body: " << body.size() << " bytes."; ”
해당 코드는 TF_CPP_VMODULE=http_server=1로 tf model server의 환경변수를 설정하면 http 요청에 대한 로그가 나오는데 거기에 해당하는 코드
같은 함수내에 아래의 코드가 존재. 해당코드가 추론요청하는 함수로 추정
handler_->ProcessRequest(req->http_method(), req->uri_path(), body,
&headers, &model_name, &method, &output);
해당 함수는 http_rest_api_handler_base.h에 virtual로 선언되고 http_rest_api_handler.cc에서 구현됨
http_rest_api_handler.cc
63라인에 ProcessRequest 함수가 있는데 아래는 그 일부분임
if (http_method == "POST" && parse_successful) {
if (*method == "classify") {
status =
ProcessClassifyRequest(*model_name, model_version,
model_version_label, request_body, output);
} else if (*method == "regress") {
status = ProcessRegressRequest(*model_name, model_version,
model_version_label, request_body, output);
} else if (*method == "predict") {
status = ProcessPredictRequest(*model_name, model_version,
model_version_label, request_body, output);
}
}
*method == "predict"가 있는 라인을 보면 ProcessPredictRequest 함수를 호출하는데 151 라인에 있음 해당 함수 174라인을 보면 아래와 같은 코드가 있음
predictor_->Predict(run_options_, core_, *request, response));
predictor_는 TensorflowPredictor 클래스이고 해당 클래스는 predict_impl.h에 선언되어있음
predict_impl.cc
predict_util.cc
106라인에 아래와 같은 코드가있다
session->Run(run_options, input_tensors,
output_tensor_names, {}, &outputs,
&run_metadata, thread_pool_options));
session은 Session 클래스이고 session.h에 선언되어있다. 런옵션과 입력텐서를 받아 outputs에 결과를 저장
session.h
석현아. 잘 하고 있다. 해당 코드를 석현이가 새롭게 빌드 할 수 있다면 디버그 메시지를 추가해서 실행해보면 디버깅이 더 쉬워질것 같아. 예를 들어, 처음에 예로 든 ProcessRequest 함수에서 ServerRequestInterface 를 입력인자로 받는데, 우리가 요청을 보내는 순서대로 실제로 요청이 도착하려는지 보려고 한다면, 요청을 보낼때 쿼리 옵션으로 인덱스 번호 (1 부터 순차적으로 증가) 를 보내주고, 그걸 ProcessRequest 함수에서 차례대로 출력해보면 해당 인덱스대로 ProcessRequest 가 실행되는지는 확인할수 있을것 같아. 이런식으로 해서 문제가 발생하는 곳을 추적해 나가는것이 중요할것 같아. 다른 방법도 많이 있을수 있으니 많이 시도해보자.
session.h 를 상속받아 Run 함수를 구현하는 코드들
direct_session.cc, tfrt_session.cc, client_session.cc, grpc_session.cc 등을 찾아서 분석해보았는데 교수님이 말씀하신것처럼 코드를 수정하여 인덱스를 출력하는 것은 http_rest_api_handler.cc의 ProcessPredictRequest 함수를 수정하면 가능할 거 같습니다.
rest로 요청을 보낼때 json형식의 데이터를 보내는데 위 함수의 164라인을 보면 아래와 같은 코드가 있습니다.
TF_RETURN_IF_ERROR(FillPredictRequestFromJson(
request_body,
[this, request](const string& sig,
::google::protobuf::Map<string, TensorInfo>* map) {
return this->GetInfoMap(request->model_spec(), sig, map);
},
request, &format));
json 데이터를 받아 추론을 위한 request를 생성하는 코드로 추정됩니다. 따라서 요청을 보낼때 json형식에 인덱스를 추가하고 이를 받아서 처리하는 위 코드 부분을 수정하면 인덱스에 따라 요청을 받은 시간, 추론요청을 완료한 시간을 출력할 수 있을거 같습니다.
코드를 보다가 추가적인 방법이 또 있을 거 같습니다. 위와 비슷한 방법인데 http_server.cc의 ProcessPredictRequest 함수에서 위의 ProcessPredictRequest를 호출하는데 여기서 수정해도 될거같습니다. (https://github.com/tensorflow/serving/blob/master/tensorflow_serving/model_servers/http_rest_api_handler.cc)
코드 분석에 따라 코드를 수정하고 테스트를 하기위해서는 몇단계를 더 알아봐야합니다.
따라서 지금 해야할 일은
이렇게 3가지를 생각해보았습니다. 바로 진행하면서 어려운점이나 진행사항있으면 계속해서 이슈 남기겠습니다.
코드의 시퀀스는 어느정도 파악이 되는듯 하다. 기존에 사용하던 컨테이너 이미지라면 수정이 쉽지 않을수도 있을텐데, 해당 이미지 "helmuthva/jetson-xavier-tensorflow-serving-base" 의 Dockerfile 을 받아와서 로컬에서 직접 빌드 후 사용하는게 나을것 같아. 결국 Dockerfile 에 모든 종속적인 것들이 정의되어 있을거야.
tf serving
helmuthva/jetson-xavier-tensorflow-serving-base
Dockerfiles
docker image로 build 및 pubilsh 하는 Makefile
https://github.com/helmut-hoffer-von-ankershoffen/jetson/blob/master/Makefile
Makefile에서 publish 할 때 사용하는 shell script
Makefile 코드 분석
docker hub에 publish 코드
tensorflow-serving-publish: ## Publish latest tensorflow-serving image on Jetson device to Docker Hub given credentials in .docker-hub.auth
workflow/deploy/tools/publish tensorflow-serving $(shell sed '1q;d' .docker-hub.auth) $(shell sed '2q;d' .docker-hub.auth) $(filter-out $@,$(MAKECMDGOALS))
tensorflow-serving-publish: ## Publish latest tensorflow-serving image on Jetson device to Docker Hub given credentials in .docker-hub.auth
workflow/deploy/tools/publish tensorflow-serving $(shell sed '1q;d' .docker-hub.auth) $(shell sed '2q;d' .docker-hub.auth) $(filter-out $@,$(MAKECMDGOALS))
publish-all: ## Publish all images to DockerHub
JETSON_MODEL=nano make tensorflow-serving-base-publish $(filter-out $@,$(MAKECMDGOALS))
JETSON_MODEL=nano make tensorflow-serving-publish $(filter-out $@,$(MAKECMDGOALS))
JETSON_MODEL=xavier make tensorflow-serving-base-publish $(filter-out $@,$(MAKECMDGOALS))
JETSON_MODEL=xavier make tensorflow-serving-publish $(filter-out $@,$(MAKECMDGOALS))
이미지 빌드 코드
tensorflow-serving-base-build-and-test: ## Build, push and test ml tensorflow-serving image for Docker on Jetson device extending ml-base with TensorFlow *Serving*
cd workflow/deploy/tensorflow-serving-base && skaffold build
workflow/deploy/tools/container-structure-test tensorflow-serving-base
tensorflow-serving-build-and-test: ## Build and test tensorflow-serving
cd workflow/deploy/tensorflow-serving && skaffold build
workflow/deploy/tools/container-structure-test tensorflow-serving
tensorflow-serving-deploy: ## Build and deploy tensorflow-serving
kubectl create namespace jetson-tensorflow-serving || true
kubectl create secret generic tensorflow-serving.polarize.ai --from-file workflow/deploy/tensorflow-serving/.basic-auth --namespace=jetson-tensorflow-serving || true
cd workflow/deploy/tensorflow-serving && skaffold run
정리 (앞으로 해야할 일)
tensorflow-serving-base
skaffold란
[Skaffold](https://skaffold.dev/docs)는 Kubernetes 네이티브 애플리케이션을 위한 지속적인 개발을 용이하게 하는 CLI 도구이다. Skaffold는 애플리케이션을 빌드, 푸시 및 배포하기 위한 워크플로를 처리하고 CI/CD 파이프라인을 만들기 위한 빌딩 블록을 제공한다. 이를 통해 Skaffold가 로컬 또는 원격 Kubernetes 클러스터에 지속적으로 배포하는 동안 로컬에서 애플리케이션을 반복하는 데 집중할 수 있다.
ml-base의 skaffold.yaml 분석
apiVersion: skaffold/v1beta11
kind: Config
build:
tagPolicy:
sha256: {}
artifacts:
- image: max-one.local:5001/jetson/nano/ml-base
custom:
buildCommand: ../tools/builderx ml-base
dependencies:
paths:
- src
ignore:
- .gitignore
profiles:
- name: xavier
activation:
- env: JETSON_MODEL=xavier
patches:
- op: replace
path: /build/artifacts/0/image
value: max-one.local:5001/jetson/xavier/ml-base
- op: replace
path: /build/artifacts/0/custom/buildCommand
value: ../tools/builderx ml-base xavier
tensorflow-serving-base의 skaffold.yaml 분석
# 일부 코드
...
buildCommand: ../tools/builder tensorflow-serving-base nano max-one.local:5001/jetson/nano/ml-base --build-arg JOBS=1 --build-arg JETSON_MODEL=nano --build-arg TF_CUDA_COMPUTE_CAPABILITIES=5.3
...
value: ../tools/builder tensorflow-serving-base xavier max-one.local:5001/jetson/xavier/ml-base --build-arg JOBS=9 --build-arg JETSON_MODEL=xavier --build-arg TF_CUDA_COMPUTE_CAPABILITIES=6.2,7.2
tensorflow-serving-base 도커파일 분석
해당 도커파일에서는 bazel을 설치하고 JETSON_MODEL 환경변수를 지정하고 tensorflow/serving 깃헙을 clone하여 bazel build를 실행한다.
# 코드의 일부
RUN mkdir -p /tensorflow-serving && \
cd /tensorflow-serving && \
git clone --branch=${TF_SERVING_VERSION_GIT_BRANCH} --recurse-submodules https://github.com/tensorflow/serving . && \
git remote add upstream https://github.com/tensorflow/serving.git && \
if [ "${TF_SERVING_VERSION_GIT_COMMIT}" != "head" ]; then git checkout ${TF_SERVING_VERSION_GIT_COMMIT} ; fi && \
\
mv -f /tmp/tfs.bazelrc .bazelrc && \
\
bazel build \
--color=yes \
--curses=yes \
--jobs="${JOBS}" \
--verbose_failures \
--output_filter=DONT_MATCH_ANYTHING \
--config=cuda \
--config=nativeopt \
--config=jetson \
--config=${JETSON_MODEL} \
--copt="-fPIC" \
tensorflow_serving/model_servers:tensorflow_model_server && \
cp /tensorflow-serving/bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server /usr/local/bin/tensorflow_model_server && \
\
bazel build \
--color=yes \
--curses=yes \
--jobs="${JOBS}" \
--verbose_failures \
--output_filter=DONT_MATCH_ANYTHING \
--config=cuda \
--config=nativeopt \
--config=jetson \
--config=${JETSON_MODEL} \
--copt="-fPIC" \
tensorflow_serving/tools/pip_package:build_pip_package && \
bazel-bin/tensorflow_serving/tools/pip_package/build_pip_package \
/tmp/pip && \
pip --no-cache-dir install \
/tmp/pip/tensorflow_serving_api_gpu-*.whl && \
\
cd / && \
rm -rf /tmp/pip && \
rm -rf /root/.cache && \
rm -rf /tensorflow-serving
bazel build 옵션들
.bazelrc
###
# .bazelrc adapted to ml-base image for Jetson devices
###
# Options used to build with CUDA.
build:cuda --crosstool_top=@local_config_cuda//crosstool:toolchain
build:cuda --define=using_cuda=true --define=using_cuda_nvcc=true
# Jetson does not support NCCL
build:jetson --define=no_nccl_support=true
build:nano --define=maxrregcount=80
build:xavier --define=maxrregcount=255
# Processor native optimizations (depends on build host capabilities).
build:nativeopt --copt=-march=native
build:nativeopt --host_copt=-march=native
build:nativeopt --copt=-O3
# Location of python given Anaconda installation in ml-base
build --action_env PYTHON_BIN_PATH="/opt/archiconda3/bin/python"
build --define PYTHON_BIN_PATH=/opt/archiconda3/bin/python
build --spawn_strategy=standalone
build --genrule_strategy=standalone
build --define=grpc_no_ares=true
build -c opt
# Adding --cxxopt=-D_GLIBCXX_USE_CXX11_ABI=0" creates parity with TF
# compilation options. It also addresses memory use due to
# copy-on-write semantics of std::strings of the older ABI.
build --cxxopt=-D_GLIBCXX_USE_CXX11_ABI=0
build --workspace_status_command=/proc/self/cwd/tools/gen_status_stamp.sh
분석 결과
해당 도커파일을 빌드한다. 빌드 할 때 옵션은 skaffold.yaml과 builder 쉘스크립트를 참조하여 아래와 같이 빌드한다.
docker build -t jetson-xavier-tensorflow-serving-base "dockerfile path" --build-arg JOBS=9 --build-arg JETSON_MODEL=xavier --build-arg TF_CUDA_COMPUTE_CAPABILITIES=6.2,7.2
분석을 어느정도 완료하여 이제 tfserving 코드를 수정하고 이미지로 빌드하여 테스트 해보겠습니다.
Dockerfile 빌드 과정
bazel build
bazel 예전 버전으로 인한 에러
/tensorflow-serving/tensorflow_serving/workspace.bzl:126:5: //external:org_boost: no such attribute 'recursive_init_submodules' in 'new_git_repository' rule
두번째 에러 (원인파악중)
ERROR: Error in repository_rule: in call to repository_rule(), parameter 'remotable' is experimental and thus unavailable with the current flags.
python_configure.bzl 파일의 repository_rule 함수에서 remotable이란 파라미터가 실험적이라 사용하지 못한다는 에러 로그
remote_python_configure = repository_rule(
implementation = _create_local_python_repository,
environ = _ENVIRONS,
remotable = True,
attrs = {
"environ": attr.string_dict(),
"platform_constraint": attr.string(),
},
)
기존에 있던 에러는 bazel 버전의 문제로 버전을 최신으로 수정 후 제거되었습니다. 비슷한 문제로 보이는 에러가 발생하여 원인파악중에 있습니다.
두번째 에러
ERROR: Error in repository_rule: in call to repository_rule(), parameter 'remotable' is experimental and thus unavailable with the current flags.
remote_python_configure = repository_rule(
implementation = _create_local_python_repository,
environ = _ENVIRONS,
remotable = True,
attrs = {
"environ": attr.string_dict(),
"platform_constraint": attr.string(),
},
)
세번째 에러
ERROR: An error occurred during the fetch of repository 'local_config_tensorrt':
Traceback (most recent call last):
File "/root/.cache/bazel/_bazel_root/e53bbb0b0da4e26d24b415310219b953/external/org_tensorflow/third_party/tensorrt/tensorrt_configure.bzl", line 287, column 38, in _tensorrt_configure_impl
_create_local_tensorrt_repository(repository_ctx)
File "/root/.cache/bazel/_bazel_root/e53bbb0b0da4e26d24b415310219b953/external/org_tensorflow/third_party/tensorrt/tensorrt_configure.bzl", line 146, column 30, in _create_local_tensorrt_repository
config = find_cuda_config(repository_ctx, find_cuda_config_path, ["cuda", "tensorrt"])
File "/root/.cache/bazel/_bazel_root/e53bbb0b0da4e26d24b415310219b953/external/org_tensorflow/third_party/gpus/cuda_configure.bzl", line 672, column 41, in find_cuda_config
exec_result = _exec_find_cuda_config(repository_ctx, script_path, cuda_libraries)
File "/root/.cache/bazel/_bazel_root/e53bbb0b0da4e26d24b415310219b953/external/org_tensorflow/third_party/gpus/cuda_configure.bzl", line 666, column 19, in _exec_find_cuda_config
return execute(repository_ctx, [python_bin, "-c", decompress_and_execute_cmd])
File "/root/.cache/bazel/_bazel_root/e53bbb0b0da4e26d24b415310219b953/external/org_tensorflow/third_party/remote_config/common.bzl", line 230, column 13, in execute
fail(
Error in fail: Repository command failed
Could not find any NvInferVersion.h matching version '8.2.1' in any subdirectory:
''
'include'
'include/cuda'
'include/*-linux-gnu'
'extras/CUPTI/include'
'include/cuda/CUPTI'
'local/cuda/extras/CUPTI/include'
of:
'/lib'
'/lib/aarch64-linux-gnu'
'/usr'
'/usr/lib/aarch64-linux-gnu'
'/usr/lib/aarch64-linux-gnu/libfakeroot'
'/usr/local/cuda'
'/usr/local/cuda-10.0/targets/aarch64-linux/lib'
19년도 tf serving 코드를 빌드하는 Dockerfile로는 최신 버전의 tf serving 코드를 빌드하는 것은 어려울 것 같습니다. 아까 미팅에서 말씀하신대로 최신 버전의 코드를 빌드하는 쪽으로 진행하겠습니다.
tf model server가 요청을 어떻게 처리하는지 파악하기위해 코드를 분석할 필요가 있다. 서버코드는 https://github.com/tensorflow/serving/tree/master/tensorflow_serving/model_servers 여기에 있고 관련된 추가 코드는 https://github.com/tensorflow/tensorflow 여기에 있다.