Create a custom AWS Sagemaker Estimator with a configurable entrypoint - amazon-sagemaker

I'm writing a custom Estimator in AWS Sagemaker , for a framework that isn't supported out-of-the-box. I have my own docker image for training, with the training code bundled into the image, which forces me to rebuild the image every time the code changes.
What I would like to do, is create an Estimator that uses this image, and accepts a file as an entry-point, like the built-in framework estimators do (e.g Tensorflow).
From reading the source code for the Sagemaker python SDK, I found the sagemaker.estimator.Framework class, which accepts the entry_point argument, and from which the built-in frameworks estimators inherit. However, the documentation doesn't really show how to inherit from that class in my own code.
Is it possible to write a custom Estimator class that inherits from Framework, or is there another way to create a custom estimator that receives an entry-point argument?

Instead of using an Estimator, use a TensorFlow estimator and then pass your custom image as an input to image_uri. This way you have the best of the both worlds. And if you have a requirements.txt file in your source_dir, that gets installed before training.
Here is a custom image docker file
FROM python:3.9-buster
RUN apt-get -y update && apt-get install -y --no-install-recommends \
wget \
python3-pip \
python3-setuptools \
nginx \
ca-certificates \
vim \
&& rm -rf /var/lib/apt/lists/*
#RUN ln -s /usr/bin/python3 /usr/bin/python
RUN ln -s /usr/bin/pip3 /usr/bin/pip
ENV PYTHONUNBUFFERED=TRUE
ENV PYTHONDONTWRITEBYTECODE=TRUE
ENV PATH="/opt/program:${PATH}"
RUN pip install sagemaker-training
and here is how you call your custom image
tf_estimator = TensorFlow(entry_point='mnist_keras_tf.py',
source_dir='.',
role=role,
instance_count=1,
instance_type='ml.p3.2xlarge',
image_uri='<arn of custom image>',
script_mode=True,
hyperparameters={
'epochs': 2,
'batch-size': 256,
'learning-rate': 0.01}

The existing framework estimators in the SageMaker Python SDK can be a good place to start, e.g. https://github.com/aws/sagemaker-python-sdk/blob/master/src/sagemaker/pytorch/estimator.py
However, it would probably be easier, since you've already bundled your training code into the image, to set an entrypoint in your Dockerfile that runs the training code. For more about how Amazon SageMaker runs your training image, see https://docs.aws.amazon.com/sagemaker/latest/dg/your-algorithms-training-algo-dockerfile.html

Related

Which ChromeDriver & Headless Chrome versions exist that are compatible with ruby 2.7?

The issue
I have a web scraper running in AWS lambda but in a few weeks AWS lambda will stop supporting Ruby 2.7. I built my scraper last year using this tutorial.
I need to find a version of chrome driver & headless chrome that is compatible with Ruby 2.7, But I don't know exactly where to start.
I have looked at the ChromeDriver's downloads portal But I don't see any indication there that Chrome driver will work for ruby 2.7 or any other specific version of ruby for that matter.
The code I have works by accessing the ChromeDriver binary and starting it inside a specific folder
I downloaded the specific binaries I am using by running these commands:
# serverless chrome
wget https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-37/stable-headless-chromium-amazonlinux-2017-03.zip
unzip stable-headless-chromium-amazonlinux-2017-03.zip -d bin/
rm stable-headless-chromium-amazonlinux-2017-03.zip
# chromedriver
wget https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip
unzip chromedriver_linux64.zip -d bin/
rm chromedriver_linux64.zip
Solution
I found the solution to this problem. Ruby 2.7 that Lambda offers by default runs on top of Amazon Linux 2 (which lacks many important libraries & dependencies), unfortunately, there's nothing you can do to change that.
However, Amazon offers you the ability to run your code in a custom docker image that can be up to 10GB in size.
I fixed this problem by creating my own image using the following Dockerfile
FROM public.ecr.aws/lambda/ruby:2.7
# Install dependencies needed to run MySQL & Chrome
RUN yum -y install libX11
RUN yum -y install dejavu-sans-fonts
RUN yum -y install procps
RUN yum -y install mysql-devel
RUN yum -y install tree
RUN mkdir /var/task/lib
RUN cp /usr/lib64/mysql/libmysqlclient.so.18 /var/task/lib
RUN gem install bundler
RUN yum -y install wget
RUN yum -y groupinstall 'Development Tools'
# Ruby Gems
ADD Gemfile ${LAMBDA_TASK_ROOT}/
ADD Gemfile.lock ${LAMBDA_TASK_ROOT}/
RUN bundle config set path 'vendor/bundle' && \
bundle install
# Install chromedriver & chromium
RUN mkdir ${LAMBDA_TASK_ROOT}/bin
# Chromium
RUN wget https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-37/stable-headless-chromium-amazonlinux-2017-03.zip
RUN unzip stable-headless-chromium-amazonlinux-2017-03.zip -d ${LAMBDA_TASK_ROOT}/bin/
RUN rm stable-headless-chromium-amazonlinux-2017-03.zip
# Chromedriver
RUN wget https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip
RUN unzip chromedriver_linux64.zip -d ${LAMBDA_TASK_ROOT}/bin/
RUN rm chromedriver_linux64.zip
# Copy function code
COPY app.rb ${LAMBDA_TASK_ROOT}
WORKDIR ${LAMBDA_TASK_ROOT}
RUN tree
RUN ls ${LAMBDA_TASK_ROOT}/bin
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "app.handle" ]
Notes
If your code was previously deployed using a zip file you will have to either destroy the previous function or create a second function with the code update, it all comes down to how you want to handle deployment.
It is possible to automate the deployment process using the serverless framework

Lambda Layer Function for Snowflake

I have followed the standard method for creating a Lambda Layer in Snowflake.
[in a AWS EC2 instance]
rm -rf snowflake
mkdir -p snowflake/python/lib/python3.7/site-packages
pip3 install --no-cache-dir --ignore-installed --upgrade snowflake-connector-python -t snowflake/python/lib/python3.7/site-packages
cd snowflake; rm -f snowflake.zip; zip -r snowflake.zip .
I can create the Lambda Layer and then add it to my Lambda function. As well as validate that the library is attached, but when I call the Lambda function, it is failing on
import snowflake.connector
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': No module named 'snowflake'
Are there any snowflake Lambda libraries or detailed guides on what may be going wrong here. I build lambda layers all the time in this manner and usually the above process works.
The issue turned out to be a requirement on 3.7. I added the following to my library creation and it worked fine:
I changed the default python version in my Lambda function to be 3.7 to match the python instance I used to compile the library, then I added the following into my code:
ssh -i ${PEM_FILE} ${EC2_HOST} "cp -r snowflake/python/lib/python3.7/site-packages/* dblayer/python/lib/python3.7/site-packages"

How to create react-app using directly only docker instead of host?

I am creating new Reactjs application using Docker and I want to create new instance without installing Node.js to host system. I have seen many of tutorials but everytime first step was to install Node.js to the host, init app and then setup Docker. Problem I ran into was the official Node.je Docker images are designed for run application only instead of to run like detached container, so I cannot use container command line to initial install. I was about to create image based on any linux distro and install Node.js on my own, but with these approache I cannot use advantages of prepared official images of Node.js.
Does exist any option how to init React app using Docker without installing Node.js to the host system?
Thank You
EDIT: Based od #David Maze answer I decide to use docker-compose, just mount project directory to container and put command: ["sleep", "infinity"] to docker-compose file. So I didn't need to install Node.js to host and I can manage everthing from container command line as usual in project folder. I wasn't solving any shared global cache, but I am not really sure that it is needed if I will have more versions of node containered because of conflict of npms of different versions. Maybe I try to mount it like volume to containers from some global place in the host one day, but disk space is not so big problem ...
You should be able to run something like:
sudo docker run \
--rm \
-it \
-u$(id -u):$(id -g) \
-w/ \
-v"$PWD":/app \
node:10 \
npx create-react-app app
You will have to repeat this litany of Docker options every time you want to do anything to use a Docker-packaged version of Node.
Ultimately this sequence of things starts in the container root directory (-w/) and uses create-react-app to create an app directory; the -v option has that backed by the current directory on the host, and the -u option is needed to make filesystem permissions line up. The -it options make it possible to answer interactive questions, and --rm causes the container to clean up after itself.
I suspect you will find it much easier to just install Node.

Installing additional fonts for Imagemagick on GAE Flexible environment

I'm using custom runtime on GAE Flexible environment.
Imagemagick is installed and it's working as expected.
RUN apt-get update && \
apt-get install imagemagick -y
I now need additional fonts installed (as some pdfs, when being converted to images, aren't rendering correctly).
Are the steps to install a new font:
apt-get install the font(s)
Somehow get imagick_type_gen ( https://martin-thoma.com/add-a-new-font-to-imagemagick/)
Call perl imagick_type_gen > types.xml
Copy over imagemagick types with types generated in step 3.
Are the steps outlined correct?
How does one download a script that isn't available via apt-get (step 2)?
Does GAE flexible environment allow you to overwrite files (steps 3 and 4 above).
Thanks

How do I customise a Google App Engine Managed VM with a Standard Runtime?

I would like to customise a (Python) Standard Runtime Managed VM.
In theory, this should be possible by adding some extra commands to the VM Dockerfile.
Google's documentation states that a VM Dockerfile is automatically generated when the App is first deployed;
If you are using a standard runtime, the SDK will create a Dockerfile for you the first time you run the gcloud preview app deploy commands. The file will exist in a predetermined location:
If you are developing in Java, the Dockerfile appears in the root of the compiled Web Application Archive directory (WAR)
If you are developing in Python or Go, the Dockerfile appears in the root of your application directory.
And that extra commands can indeed be added;
You can add more docker commands to this file, while continuing to run and deploy your app with the standard runtime declaration.
However in practice the Dockerfile is automatically deleted immediately after deployment competes, preventing any customisation.
Has anyone managed to add Dockerfile commands to a Managed VM with a Standard Runtime? Any help would be gratefully appreciated.
I tried the same thing and did not succeed. There is however an equivalent way of doing this that I fell back to.
You can create a custom runtime that mimics the standard runtime.
You can do this because Google provides the Docker base images for all the standard runtimes. Mimicking a standard runtime is therefore simply a matter of selecting the right base image in the Dockerfile of the custom runtime. For the standard Python App Engine VM the Dockerfile is:
FROM gcr.io/google_appengine/python-compat
ADD . /app
Now that you have recreated the standard runtime as a custom runtime, you can modify the Dockerfile to make any customizations you need.
Important Note
The development server does not support custom Dockerfiles (you will get an error about --custom-entrypoint), so you have to move your test environment to App Engine servers if you are doing this. I think this is true regardless of whether you are using a standard runtime and customizing the Dockerfile or using a custom runtime. See this answer.
A note about the development server not working with custom runtimes - dev_appserver.py doesn't deal with Docker or Dockerfiles, which is why it complains about needing you to specify --custom_entrypoint. However as a workaround you can manually set up the dependencies locally. Here's an example using 'appengine-vm-fortunespeak' which uses a custom runtime based on python-compat:
$ git clone https://github.com/GoogleCloudPlatform/appengine-vm-fortunespeak-python.git
$ cd appengine-vm-fortunespeak-python
# Local dependencies from Dockerfile must be installed manually
$ sudo pip install -r requirements.txt
$ sudo apt-get update && install -y fortunes libespeak-dev
# We also need gunicorn since its used by python-compat to serve the app
$ sudo apt-get install gunicorn
# This is straight from dev_appserver.py --help
$ dev_appserver.py app.yaml --custom_entrypoint="gunicorn -b localhost:{port} main:app"
Note that if you are using any of the non -compat images, you can run your app directly using Docker since they don't need to emulate the legacy App Engine API, for example using 'getting-started-python' which uses the python runtime:
$ git clone https://github.com/GoogleCloudPlatform/getting-started-python.git
$ cd 6-pubsub
# (Configure the app according to the tutorial ...)
$ docker build .
$ docker images # (note the IMAGE_ID)
$ docker run -p 127.0.0.1:8080:8080 -t IMAGE_ID
Try the above with any -compat images and you will have problems - for example on python-compat you'll see initialization errors in runtime/google/appengine/tools/vmboot.py. It needs to be run on a real Managed VM instance.

Resources