From d1e15ca364864a6b74ddb3b6cf217a7dd046501c Mon Sep 17 00:00:00 2001 From: Gabe Joseph Date: Wed, 5 May 2021 16:36:03 -0600 Subject: [PATCH] Update examples to use `us-west-2` (#49) Now that Coiled supports `us-west-2`, we can use that! This also adds a script to build software environments in parallel in multiple regions. By creating envs in multiple regions up front, the first person won't have to wait for an image build when they spin up a cluster in one of the demos. Also added `jupyterlab-system-monitor` for fun. --- examples/cluster.ipynb | 10 ++-- examples/show.ipynb | 2 +- poetry.lock | 93 ++++++++++++++++++++++++++++++---- pyproject.toml | 3 ++ scripts/coiled-envs.py | 101 +++++++++++++++++++++++++++++++++++++ scripts/coiled-notebook.py | 33 ------------ scripts/make-coiled-env.sh | 7 --- 7 files changed, 191 insertions(+), 58 deletions(-) create mode 100644 scripts/coiled-envs.py delete mode 100644 scripts/coiled-notebook.py delete mode 100755 scripts/make-coiled-env.sh diff --git a/examples/cluster.ipynb b/examples/cluster.ipynb index cab1899..6e20c77 100644 --- a/examples/cluster.ipynb +++ b/examples/cluster.ipynb @@ -7,11 +7,9 @@ "source": [ "# Running on a cluster\n", "\n", - "We'll use a Dask cluster in the cloud—in this case, using [Coiled](https://coiled.io/)—to use many machines to process the data in parallel. We can also run in a data center near* where the data is stored for better performance.\n", + "We'll use a Dask cluster in the cloud—in this case, using [Coiled](https://coiled.io/)—to use many machines to process the data in parallel. We can also run in the data center where the data is stored for better performance.\n", "\n", - "If you use Coiled (which is both easy to use, and currently free!), you can set `software=\"gjoseph92/stackstac\"` to get a software environment where the latest version of `stackstac` is already installed.\n", - "\n", - "_*these Sentinel-2 COGs are in AWS's `us-west-2`; Coiled currently only offers `us-west-1`. But it's close enough._" + "If you use Coiled (which is both easy to use, and currently free!), you can set `software=\"gjoseph92/stackstac\"` to get a software environment where the latest version of `stackstac` is already installed." ] }, { @@ -66,7 +64,7 @@ "cluster = coiled.Cluster(\n", " name=\"stackstac\",\n", " software=\"gjoseph92/stackstac\",\n", - " backend_options={\"region\": \"us-west-1\"},\n", + " backend_options={\"region\": \"us-west-2\"},\n", ")\n", "client = distributed.Client(cluster)\n", "client" @@ -1740,4 +1738,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/examples/show.ipynb b/examples/show.ipynb index b9fb23a..d964505 100644 --- a/examples/show.ipynb +++ b/examples/show.ipynb @@ -143,7 +143,7 @@ " software=\"gjoseph92/stackstac\",\n", " n_workers=22,\n", " scheduler_cpu=2,\n", - " backend_options={\"region\": \"us-west-1\"},\n", + " backend_options={\"region\": \"us-west-2\"},\n", ")\n", "client = distributed.Client(cluster)\n", "client" diff --git a/poetry.lock b/poetry.lock index 205d3d7..bc2f823 100644 --- a/poetry.lock +++ b/poetry.lock @@ -57,6 +57,21 @@ python-versions = ">=3.6" [package.dependencies] typing_extensions = ">=3.7" +[[package]] +name = "aiotools" +version = "1.2.1" +description = "Idiomatic asyncio utilities" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +build = ["setuptools (>=45.0.0)", "wheel (>=0.34.2)", "twine (>=3.1.0)", "towncrier (>=19.2.0,<19.3.0)"] +docs = ["sphinx", "sphinx-autodoc-typehints", "guzzle-sphinx-theme"] +lint = ["flake8 (>=3.8.4)"] +test = ["pytest (>=6.1.1,<6.2.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "pytest-cov", "pytest-mock", "codecov"] +typecheck = ["mypy (>=0.790)"] + [[package]] name = "alabaster" version = "0.7.12" @@ -706,7 +721,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [[package]] name = "ipykernel" -version = "5.5.3" +version = "5.5.4" description = "IPython Kernel for Jupyter" category = "main" optional = false @@ -738,7 +753,7 @@ traittypes = ">=0.2.1,<3" [[package]] name = "ipython" -version = "7.23.0" +version = "7.23.1" description = "IPython: Productive Interactive Computing" category = "main" optional = false @@ -919,6 +934,22 @@ tomlkit = "*" [package.extras] test = ["build", "coverage", "pytest", "pytest-cov", "pytest-mock"] +[[package]] +name = "jupyter-resource-usage" +version = "0.6.0" +description = "Simple Jupyter extension to show how much resources (RAM) your notebook is using" +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +jupyter-server = ">=1.0.0" +prometheus-client = "*" +psutil = ">=5.6.0" + +[package.extras] +dev = ["autopep8", "black", "pytest", "flake8", "pytest-cov (>=2.6.1)", "mock"] + [[package]] name = "jupyter-server" version = "1.6.4" @@ -1042,6 +1073,30 @@ requests = "*" [package.extras] test = ["codecov", "ipykernel", "pytest (>=5.3.2)", "pytest-cov", "jupyter-server", "openapi-core", "pytest-console-scripts", "strict-rfc3339", "ruamel.yaml", "wheel"] +[[package]] +name = "jupyterlab-system-monitor" +version = "0.8.0" +description = "JupyterLab extension to display system information" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +jupyter-resource-usage = ">=0.5,<1.0" +jupyterlab = ">=3.0,<4.0" +jupyterlab-topbar = ">=0.6,<1.0" + +[[package]] +name = "jupyterlab-topbar" +version = "0.6.1" +description = "JupyterLab extension to expose the top bar space" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +jupyterlab = ">=3.0,<4.0" + [[package]] name = "jupyterlab-widgets" version = "1.0.0" @@ -1943,7 +1998,7 @@ python-versions = "*" [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -2387,14 +2442,14 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [extras] -binder = ["Bottleneck", "coiled", "dask-labextension", "distributed", "geogif", "ipyleaflet", "jupyterlab-geojson", "matplotlib", "planetary-computer", "pystac-client", "sat-search"] +binder = ["Bottleneck", "coiled", "dask-labextension", "distributed", "geogif", "ipyleaflet", "jupyterlab-geojson", "jupyterlab-system-monitor", "matplotlib", "planetary-computer", "pystac-client", "sat-search"] docs = ["furo", "ipyleaflet", "ipython", "jupyter-sphinx", "nbsphinx", "numpydoc", "pandoc", "sphinx-autodoc-typehints", "Sphinx"] viz = ["aiohttp", "cachetools", "distributed", "ipyleaflet", "matplotlib", "mercantile", "Pillow"] [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "787a23d9a69b1f581e0ffa6a37b2322a29307d4343152d251f640eb860a8f510" +content-hash = "e7d999160b11f1038c869dbe6a1a80d050f32d77d519c6049066b6827b2bdf7c" [metadata.files] affine = [ @@ -2447,6 +2502,10 @@ aioitertools = [ {file = "aioitertools-0.7.1-py3-none-any.whl", hash = "sha256:8972308474c41ed5e0636819f948ebff32f2318e70f7e7d23cd208c4357cc773"}, {file = "aioitertools-0.7.1.tar.gz", hash = "sha256:54a56c7cf3b5290d1cb5e8974353c9f52c677612b5d69a859369a020c53414a3"}, ] +aiotools = [ + {file = "aiotools-1.2.1-py3-none-any.whl", hash = "sha256:8a8731b789d1ba315f30e67b2c65313dca24d03a00c95c1c36d73ed346d4a222"}, + {file = "aiotools-1.2.1.tar.gz", hash = "sha256:efbb6e0ed1108f58893cce44ccaca283f95bd79246c91a8355a711e6d98aa78e"}, +] alabaster = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, @@ -2806,16 +2865,16 @@ importlib-metadata = [ {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, ] ipykernel = [ - {file = "ipykernel-5.5.3-py3-none-any.whl", hash = "sha256:21abd584543759e49010975a4621603b3cf871b1039cb3879a14094717692614"}, - {file = "ipykernel-5.5.3.tar.gz", hash = "sha256:a682e4f7affd86d9ce9b699d21bcab6d5ec9fbb2bfcb194f2706973b252bc509"}, + {file = "ipykernel-5.5.4-py3-none-any.whl", hash = "sha256:f57739bf26d7396549562c0c888b96be896385ce099fb34ca89af359b7436b25"}, + {file = "ipykernel-5.5.4.tar.gz", hash = "sha256:1ce0e83672cc3bfdc1ffb5603e1d77ab125f24b41abc4612e22bfb3e994c0db2"}, ] ipyleaflet = [ {file = "ipyleaflet-0.13.6-py2.py3-none-any.whl", hash = "sha256:16b2406ea292109c356222042ab8017f38bced46edd9acea18c1c368b5f0f5e7"}, {file = "ipyleaflet-0.13.6.tar.gz", hash = "sha256:de6d0e8a85a6ce4c5d9d6eba85d8d4d8a1698ecfffaf333606f5be37b08ad841"}, ] ipython = [ - {file = "ipython-7.23.0-py3-none-any.whl", hash = "sha256:3455b020a895710c4366e8d1b326e5ee6aa684607907fc96895e7b8359569f49"}, - {file = "ipython-7.23.0.tar.gz", hash = "sha256:69178f32bf9c6257430b6f592c3ae230c32861a1966d2facec454e09078e232d"}, + {file = "ipython-7.23.1-py3-none-any.whl", hash = "sha256:f78c6a3972dde1cc9e4041cbf4de583546314ba52d3c97208e5b6b2221a9cb7d"}, + {file = "ipython-7.23.1.tar.gz", hash = "sha256:714810a5c74f512b69d5f3b944c86e592cee0a5fb9c728e582f074610f6cf038"}, ] ipython-genutils = [ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, @@ -2861,6 +2920,10 @@ jupyter-packaging = [ {file = "jupyter_packaging-0.9.2-py2.py3-none-any.whl", hash = "sha256:7d2cff62d0b0cf5267f5cd9edb4bd04591f68aa919bf026e7787f0424c0e7c55"}, {file = "jupyter_packaging-0.9.2.tar.gz", hash = "sha256:780082b43506eccb3fb39ed9306300b637245e622a9644701c60d89992468822"}, ] +jupyter-resource-usage = [ + {file = "jupyter-resource-usage-0.6.0.tar.gz", hash = "sha256:9d10d53525027206fb5f190964d4535bdb269fd241773fc09c87af02a974a5c7"}, + {file = "jupyter_resource_usage-0.6.0-py2.py3-none-any.whl", hash = "sha256:298b308a7dea1b90b308f43aee3e3ed8a1f117a0786d0daccf6539ffbbfadbcb"}, +] jupyter-server = [ {file = "jupyter_server-1.6.4-py3-none-any.whl", hash = "sha256:942b9a092f79b3663f78c8003a411e6672f0ca8cfc64a59657060a0e5a02a0cb"}, {file = "jupyter_server-1.6.4.tar.gz", hash = "sha256:21c20986047563aba2997cb10ba9441a162ec1d65b5aece9d5fa625602bb2b40"}, @@ -2889,6 +2952,14 @@ jupyterlab-server = [ {file = "jupyterlab_server-2.5.0-py3-none-any.whl", hash = "sha256:c29784df3269d45f54a1e23e041ae9e8638773fa6b11095938b4d30bec1c75c6"}, {file = "jupyterlab_server-2.5.0.tar.gz", hash = "sha256:4c2c482cc91b7439ea5415f8f85ded95059fb9cdab03f5f0e6338246830c2a5c"}, ] +jupyterlab-system-monitor = [ + {file = "jupyterlab-system-monitor-0.8.0.tar.gz", hash = "sha256:f8d03d3ee26c84300c85e570f1a9d5f69bdcfa85e9a298f5716b67e36d0d94df"}, + {file = "jupyterlab_system_monitor-0.8.0-py3-none-any.whl", hash = "sha256:2e26c2df5272237eba326ecd1eedb8203df19087fd1d651212cfd4ffeac3b122"}, +] +jupyterlab-topbar = [ + {file = "jupyterlab-topbar-0.6.1.tar.gz", hash = "sha256:f1f9144fd09c29b8921f378662f26c65a460967d9c48eeea8018cea49626fb5f"}, + {file = "jupyterlab_topbar-0.6.1-py3-none-any.whl", hash = "sha256:6863a055d94b40d42b01e7e6771bc3622677c4621d95849a8a2c15ff33f9835a"}, +] jupyterlab-widgets = [ {file = "jupyterlab_widgets-1.0.0-py3-none-any.whl", hash = "sha256:caeaf3e6103180e654e7d8d2b81b7d645e59e432487c1d35a41d6d3ee56b3fef"}, {file = "jupyterlab_widgets-1.0.0.tar.gz", hash = "sha256:5c1a29a84d3069208cb506b10609175b249b6486d6b1cbae8fcde2a11584fb78"}, @@ -3641,8 +3712,8 @@ simpervisor = [ {file = "simpervisor-0.4.tar.gz", hash = "sha256:cec79e13cdbd6edb04a5c98c1ff8d4bd9713e706c069226909a1ef0e89d393c5"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] snakeviz = [ {file = "snakeviz-2.1.0-py2.py3-none-any.whl", hash = "sha256:8ce375b18ae4a749516d7e6c6fbbf8be6177c53974f53534d8eadb646cd279b1"}, diff --git a/pyproject.toml b/pyproject.toml index 723900f..76a54f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ ipython = {version = "^7.20.0", optional = true} ipywidgets = {version = "^7.6.3", optional = true} jupyter-sphinx = {version = "^0.3.2", optional = true} jupyterlab-geojson = {version = "^3.1.2", optional = true} +jupyterlab-system-monitor = {version = "^0.8.0", optional = true} matplotlib = {version = "^3.4.1", optional = true} mercantile = {version = "^1.1.6", optional = true} nbsphinx = {version = "^0.8.2", optional = true} @@ -43,6 +44,7 @@ xarray = "^0.17.0" [tool.poetry.dev-dependencies] Pympler = "^0.9" +aiotools = "^1.2.1" awkward = {version = "^1.1.2", allow-prereleases = true} black = "^21.4b2" debugpy = "^1.2.1" @@ -67,6 +69,7 @@ binder = [ "ipyleaflet", "ipywidets", "jupyterlab-geojson", + "jupyterlab-system-monitor", "matplotlib", "planetary-computer", "pystac-client", diff --git a/scripts/coiled-envs.py b/scripts/coiled-envs.py new file mode 100644 index 0000000..a60b10a --- /dev/null +++ b/scripts/coiled-envs.py @@ -0,0 +1,101 @@ +""" +Make Coiled software environments for stackstac in multiple regions, +and upload all notebook files into a Coiled notebook (aka Job). +""" + +import asyncio +import io +import subprocess +from pathlib import Path +import sys +from typing import List + +import aiotools +import coiled + + +async def create_software_environment_quiet( + cloud: coiled.Cloud[coiled.core.Async], **kwargs +) -> None: + log = io.StringIO() + try: + await cloud.create_software_environment(log_output=log, **kwargs) + except Exception: + print( + f"Error creating software environment with {kwargs}:\n{log.getvalue()}", + file=sys.stderr, + ) + raise + + print(f"Built environment for {kwargs.get('backend_options')}") + + +async def make_coiled_stuff( + deps: List[str], + regions: List[str], + name: str, +) -> None: + examples = Path(__file__).parent.parent / "examples" + docs = Path(__file__).parent.parent / "docs" + notebook_paths = list(examples.glob("*.ipynb")) + list(docs.glob("*.ipynb")) + notebooks = list(map(str, notebook_paths)) + print(f"Notebooks: {notebooks}") + + async with coiled.Cloud(asynchronous=True) as cloud: + name_software = name + "-notebook" + async with aiotools.TaskGroup(name="envs") as tg: + # Build all the software environments in parallel in multiple regions + tg.create_task( + create_software_environment_quiet( + cloud, + name=name_software, + container="coiled/notebook:latest", + pip=deps, + ) + ) + + for region in regions: + tg.create_task( + create_software_environment_quiet( + cloud, + name=name, + pip=deps, + backend_options={"region": region}, + ) + ) + + # Create job configuration for notebook + await cloud.create_job_configuration( + name=name, + software=name_software, + cpu=2, + gpu=0, + memory="8 GiB", + command=["/bin/bash", "start.sh", "jupyter", "lab"], + files=notebooks, + ports=[8888], + description="Example notebooks from the stackstac documentation", + ) + print(f"Created notebook {name}") + + +if __name__ == "__main__": + proc = subprocess.run( + "poetry export --without-hashes -E binder -E viz", + shell=True, + check=True, + capture_output=True, + text=True, + ) + deps = proc.stdout.splitlines() + + # TODO single-source the version! this is annoying + version = subprocess.run( + "poetry version -s", shell=True, check=True, capture_output=True, text=True + ).stdout.strip() + print(f"Version: {version}") + deps += [f"stackstac[binder,viz]=={version}"] + + asyncio.run( + make_coiled_stuff(deps, regions=["us-west-2", "eu-central-1"], name="stackstac") + ) diff --git a/scripts/coiled-notebook.py b/scripts/coiled-notebook.py deleted file mode 100644 index 894c4fb..0000000 --- a/scripts/coiled-notebook.py +++ /dev/null @@ -1,33 +0,0 @@ -import subprocess - -from pathlib import Path -import coiled - -if __name__ == "__main__": - proc = subprocess.run( - "poetry export --without-hashes -E binder -E viz", - shell=True, - check=True, - capture_output=True, - text=True, - ) - deps = proc.stdout.splitlines() - - # TODO single-source the version! this is annoying - version = subprocess.run( - "poetry version -s", shell=True, check=True, capture_output=True, text=True - ).stdout.strip() - deps += [f"stackstac[binder,viz]=={version}"] - - examples = Path(__file__).parent.parent / "examples" - docs = Path(__file__).parent.parent / "docs" - notebooks = list(examples.glob("*.ipynb")) + list(docs.glob("*.ipynb")) - print(notebooks) - coiled.create_notebook( - name="stackstac", - pip=deps, - cpu=2, - memory="8 GiB", - files=notebooks, - description="Example notebooks from the stackstac documentation (https://stackstac.readthedocs.io/en/stable/)", - ) diff --git a/scripts/make-coiled-env.sh b/scripts/make-coiled-env.sh deleted file mode 100755 index 8ca9cd8..0000000 --- a/scripts/make-coiled-env.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -poetry export --without-hashes -E binder -E viz > requirements.txt -echo "stackstac==$(poetry version -s)" >> requirements.txt -poetry run coiled env create -n stackstac --pip requirements.txt - -rm requirements.txt \ No newline at end of file