From f5921185ed5e4c860df8aca80026416dd9cc7049 Mon Sep 17 00:00:00 2001 From: Orfeas Kourkakis Date: Thu, 11 Jan 2024 18:53:13 +0200 Subject: [PATCH] chore: bump mlserver-xgboost for Seldon version 1.15.x -> 1.17.1 (#73) Update ROCK according to upstream changes plus: - introduce parts that we missed in the ROCK - use ubuntu 20.04 as base due to #39 - refactor tox.ini according to canonical/oidc-authservice-rock#14 and canonical/bundle-kubeflow#763 - update `test_rock.py` according to latest changes in chisme canonical/charmed-kubeflow-chisme#81 - pins starlette due to #80 Ref #37 Closes #52 --- mlserver-xgboost/rockcraft.yaml | 74 ++++++++++++++--------------- mlserver-xgboost/tests/test_rock.py | 14 +++--- mlserver-xgboost/tox.ini | 53 +++++++++------------ 3 files changed, 65 insertions(+), 76 deletions(-) diff --git a/mlserver-xgboost/rockcraft.yaml b/mlserver-xgboost/rockcraft.yaml index d0470ba..0a7004b 100644 --- a/mlserver-xgboost/rockcraft.yaml +++ b/mlserver-xgboost/rockcraft.yaml @@ -1,26 +1,27 @@ -# Based on https://github.com/SeldonIO/MLServer/blob/1.2.0/Dockerfile +# Based on https://github.com/SeldonIO/MLServer/blob/1.3.5/Dockerfile name: mlserver-xgboost summary: An image for Seldon MLServer XGBoost description: | This image is used as part of the Charmed Kubeflow product. -version: 1.2.0_22.04_1 # __ +version: 1.3.5 license: Apache-2.0 -base: ubuntu:22.04 +base: ubuntu@20.04 run-user: _daemon_ services: mlserver-xgboost: override: replace summary: "mlserver-xgboost service" startup: enabled - # We need to build and activate the "hot-loaded" environment before MLServer starts - command: bash -c 'export PATH=/opt/conda/bin/:/opt/mlserver/.local/bin:${PATH} && \ - export PYTHONPATH=/opt/mlserver/.local/lib/python3.8/site-packages/:${PYTHONPATH} && \ - source /opt/conda/etc/profile.d/conda.sh && \ - source /hack/activate-env.sh $MLSERVER_ENV_TARBALL && \ - mlserver start $MLSERVER_MODELS_DIR' + # Do not split command in many lines using `\` because this results in + # export: command not found error during container startup + command: bash -c 'export PATH=/opt/conda/bin/:/opt/mlserver/.local/bin:${PATH}:/usr/bin && export PYTHONPATH=/opt/mlserver/.local/lib/python3.8/site-packages/:${PYTHONPATH} && eval $(/opt/conda/bin/conda shell.bash hook 2> /dev/null) && mlserver start ${MLSERVER_MODELS_DIR}' + working-dir: "/opt/mlserver" environment: - MLSERVER_ENV_TARBALL: "/mnt/models" - MLSERVER_MODELS_DIR: "/mnt/models/environment.tar.gz" + MLSERVER_ENV_TARBALL: "/mnt/models/environment.tar.gz" + MLSERVER_MODELS_DIR: "/mnt/models" + LD_LIBRARY_PATH: "/usr/local/nvidia/lib64:/opt/conda/lib/python3.8/site-packages/nvidia/cuda_runtime/lib:$LD_LIBRARY_PATH" + TRANSFORMERS_CACHE: "/opt/mlserver/.cache" + NUMBA_CACHE_DIR: "/opt/mlserver/.cache" platforms: amd64: @@ -29,32 +30,24 @@ parts: plugin: nil source: https://github.com/SeldonIO/MLServer source-type: git - source-tag: 1.2.0 + source-tag: 1.3.5 build-packages: - - bash - - tar - - gzip - # TO-DO: Verify need for the packages below - # - libgomp - # - mesa-libGL - # - glib2-devel - # - shadow-utils - stage-packages: - - bash + - python3-dev + - python3-setuptools + - python3-pip + - python-is-python3 override-build: | mkdir -p ${CRAFT_PART_INSTALL}/opt/mlserver/dist - cp ${CRAFT_PART_SRC}/setup.py . - cp ${CRAFT_PART_SRC}/README.md . - + mkdir -p ${CRAFT_PART_INSTALL}/opt/openapi ./hack/build-wheels.sh ${CRAFT_PART_INSTALL}/opt/mlserver/dist override-stage: | export ROCK_RUNTIME="xgboost" - export PYTHON_VERSION="3.8.13" - export CONDA_VERSION="4.13.0" + export PYTHON_VERSION="3.8.16" + export CONDA_VERSION="22.11.1" export RUNTIMES="mlserver_${ROCK_RUNTIME}" - export MINIFORGE_VERSION="${CONDA_VERSION}-1" + export MINIFORGE_VERSION="${CONDA_VERSION}-4" export MLSERVER_PATH=opt/mlserver export CONDA_PATH=opt/conda export PATH=/opt/mlserver/.local/bin:/opt/conda/bin:$PATH @@ -69,23 +62,30 @@ parts: ln -sf ${CONDA_PATH}/etc/profile.d/conda.sh etc/profile.d/conda.sh echo ". ${CONDA_PATH}/etc/profile.d/conda.sh" >> ~/.bashrc echo "PATH=${PATH}" >> ~/.bashrc - bash -c "${CONDA_PATH}/bin/conda init bash" + ${CONDA_PATH}/bin/conda init bash echo "conda activate base" >> ~/.bashrc chgrp -R root opt/conda && chmod -R g+rw opt/conda - # conda writes shebangs with its path everywhere, and in crafting, that will be, for example: - # #!/root/stage/opt/conda/... - # - # Snip off the /root/stage part - bash -c "grep -R -E '/root/stage' opt/ 2>/dev/null | grep -v Bin | awk '{split(\$0,out,\":\"); print out[1]}' | uniq | xargs -I{} sed -i -e 's/\/root\/stage//' {}" - # install required wheels - mkdir -p $MLSERVER_PATH + mkdir -p ${MLSERVER_PATH} mkdir -p ./wheels cp -p ${CRAFT_PART_INSTALL}/opt/mlserver/dist/mlserver-*.whl ./wheels cp -p ${CRAFT_PART_INSTALL}/opt/mlserver/dist/mlserver_${ROCK_RUNTIME}-*.whl ./wheels + # setup pip to be from conda + . ${CONDA_PATH}/etc/profile.d/conda.sh + pip install --prefix ${MLSERVER_PATH}/.local --upgrade pip wheel setuptools pip install --prefix ${MLSERVER_PATH}/.local $(ls "./wheels/mlserver-"*.whl) pip install --prefix ${MLSERVER_PATH}/.local $(ls "./wheels/mlserver_${ROCK_RUNTIME}-"*.whl) + # Pin starlette due to https://github.com/canonical/seldonio-rocks/issues/80 + echo starlette==0.22.0 >> ${CRAFT_PART_SRC}/requirements/docker.txt + pip install --prefix ${MLSERVER_PATH}/.local -r ${CRAFT_PART_SRC}/requirements/docker.txt + chown -R root:root ${MLSERVER_PATH} && chmod -R 777 ${MLSERVER_PATH} + + # conda writes shebangs with its path everywhere, and in crafting, that will be, for example: + # #!/root/stage/opt/conda/... + # + # Snip off the /root/stage part + bash -c "grep -R -E '/root/stage' opt/ 2>/dev/null | grep -v Bin | awk '{split(\$0,out,\":\"); print out[1]}' | uniq | xargs -I{} sed -i -e 's/\/root\/stage//' {}" # replace first line of mlserver script with reference to installed Conda python export CONDA_PYTHON="#\!\/opt\/conda\/bin\/python" @@ -111,5 +111,5 @@ parts: override-build: | mkdir -p ${CRAFT_PART_INSTALL}/usr/share/rocks (echo "# os-release" && cat /etc/os-release && echo "# dpkg-query" && \ - dpkg-query --root=${CRAFT_PROJECT_DIR}/../bundles/ubuntu-22.04/rootfs/ -f '${db:Status-Abbrev},${binary:Package},${Version},${source:Package},${Source:Version}\n' -W) \ + dpkg-query -f '${db:Status-Abbrev},${binary:Package},${Version},${source:Package},${Source:Version}\n' -W) \ > ${CRAFT_PART_INSTALL}/usr/share/rocks/dpkg.query diff --git a/mlserver-xgboost/tests/test_rock.py b/mlserver-xgboost/tests/test_rock.py index c331e95..8917676 100644 --- a/mlserver-xgboost/tests/test_rock.py +++ b/mlserver-xgboost/tests/test_rock.py @@ -13,7 +13,6 @@ import subprocess import yaml -from pytest_operator.plugin import OpsTest from charmed_kubeflow_chisme.rock import CheckRock @pytest.fixture() @@ -29,18 +28,17 @@ def rock_test_env(tmpdir): # tmpdir fixture we use here should clean up the other files for us @pytest.mark.abort_on_fail -def test_rock(ops_test: OpsTest, rock_test_env): +def test_rock(rock_test_env): """Test rock.""" temp_dir, container_name = rock_test_env check_rock = CheckRock("rockcraft.yaml") - rock_image = check_rock.get_image_name() + rock_image = check_rock.get_name() rock_version = check_rock.get_version() - LOCAL_ROCK_IMAGE = f"{check_rock.get_image_name()}:{check_rock.get_version()}" + LOCAL_ROCK_IMAGE = f"{rock_image}:{rock_version}" - # TO-DO uncomment when updated chisme is published - #rock_services = check_rock.get_services() - #assert rock_services["mlserver-xgboost"] - #assert rock_services["mlserver-xgboost"]["startup"] == "enabled" + rock_services = check_rock.get_services() + assert rock_services["mlserver-xgboost"] + assert rock_services["mlserver-xgboost"]["startup"] == "enabled" # create ROCK filesystem subprocess.run(["docker", "run", LOCAL_ROCK_IMAGE, "exec", "ls", "-la", "/opt/mlserver/.local/lib/python3.8/site-packages/mlserver"], check=True) diff --git a/mlserver-xgboost/tox.ini b/mlserver-xgboost/tox.ini index 8057d42..576cd6f 100644 --- a/mlserver-xgboost/tox.ini +++ b/mlserver-xgboost/tox.ini @@ -13,37 +13,33 @@ setenv = CHARM_BRANCH=main LOCAL_CHARM_DIR=charm_repo -[testenv:unit] +[testenv:pack] passenv = * allowlist_externals = - bash - tox rockcraft -deps = - juju~=2.9.0 - pytest - pytest-operator - ops - charmed_kubeflow_chisme commands = - # build and pack rock rockcraft pack + +[testenv:export-to-docker] +passenv = * +allowlist_externals = + bash + skopeo + yq +commands = + # pack rock and export to docker bash -c 'NAME=$(yq eval .name rockcraft.yaml) && \ VERSION=$(yq eval .version rockcraft.yaml) && \ - ARCH=$(yq eval ".platforms | keys" rockcraft.yaml | awk -F " " '\''{ print $2 }'\'') && \ - ROCK="$\{NAME\}_$\{VERSION\}_$\{ARCH\}" && \ - sudo skopeo --insecure-policy copy oci-archive:$ROCK.rock docker-daemon:$ROCK:$VERSION' - - # run rock tests - pytest -vvv --tb native --show-capture=all --log-cli-level=INFO {posargs} {toxinidir}/tests + ARCH=$(yq eval ".platforms | keys | .[0]" rockcraft.yaml) && \ + ROCK="$\{NAME\}_$\{VERSION\}_$\{ARCH\}.rock" && \ + DOCKER_IMAGE=$NAME:$VERSION && \ + echo "Exporting $ROCK to docker as $DOCKER_IMAGE" && \\ + skopeo --insecure-policy copy oci-archive:$ROCK docker-daemon:$DOCKER_IMAGE' [testenv:sanity] passenv = * deps = - juju~=2.9.0 pytest - pytest-operator - ops charmed_kubeflow_chisme commands = # run rock tests @@ -56,16 +52,13 @@ allowlist_externals = git rm tox - rockcraft sed deps = - juju~=2.9.0 + juju<4.0 pytest pytest-operator ops commands = - # build and pack rock - rockcraft pack # clone related charm rm -rf {env:LOCAL_CHARM_DIR} git clone --branch {env:CHARM_BRANCH} {env:CHARM_REPO} {env:LOCAL_CHARM_DIR} @@ -74,16 +67,14 @@ commands = # upload rock to docker and microk8s cache, replace charm's container with local rock reference bash -c 'NAME=$(yq eval .name rockcraft.yaml) && \ VERSION=$(yq eval .version rockcraft.yaml) && \ - ARCH=$(yq eval ".platforms | keys" rockcraft.yaml | awk -F " " '\''{ print $2 }'\'') && \ - ROCK="$\{NAME\}_$\{VERSION\}_$\{ARCH\}" && \ - sudo skopeo --insecure-policy copy oci-archive:$ROCK.rock docker-daemon:$ROCK:$VERSION && \ - docker save $ROCK > $ROCK.tar && \ - microk8s ctr image import $ROCK.tar --digests=true && \ - predictor_servers=$(yq e ".data.predictor_servers" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2) && \ - predictor_servers=$(jq --arg jq_rock $ROCK -r '\''.XGBOOST_SERVER.protocols.v2.image=$jq_rock'\'' <<< $predictor_servers) && \ + DOCKER_IMAGE=$NAME:$VERSION && \ + docker save $DOCKER_IMAGE > $DOCKER_IMAGE.tar && \ + sudo microk8s ctr image import $DOCKER_IMAGE.tar --digests=true && \ + predictor_servers=$(yq e ".data.predictor_servers" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2) && \ + predictor_servers=$(jq --arg jq_name $NAME -r '\''.XGBOOST_SERVER.protocols.v2.image=$jq_name'\'' <<< $predictor_servers) && \ predictor_servers=$(jq --arg jq_version $VERSION -r '\''.XGBOOST_SERVER.protocols.v2.defaultImageVersion=$jq_version'\'' <<< $predictor_servers) yq e -i ".data.predictor_servers=strenv(predictor_servers)" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2' # replace yq safe placeholder with original value sed -i "s/namespace: YQ_SAFE/namespace: {{ namespace }}/" {env:LOCAL_CHARM_DIR}/src/templates/configmap.yaml.j2 # run charm integration test with rock - tox -c {env:LOCAL_CHARM_DIR} -e integration + tox -c {env:LOCAL_CHARM_DIR} -e seldon-servers-integration -- -k xgboost-v2