def setup(self): """ Sets up MLStack by pulling Docker images, downloading dependencies, and building Docker images """ logger.info( """ _ _ _ _ __ | | __| |_ __ _ __| |__ | ' \| | (_-< _/ _ / _| / / |_|_|_|_| /__/\__\__,_\__|_\_\ Local Kubernetes cluster for machine learning engineering """ ) logger.info("Setting up MLStack. This will take a little while. Grab a coffee!") dockerclient = DockerClient() repositories = [ "tensorflow/tensorflow:latest-py3-jupyter", "openjdk:8-jdk-slim-stretch", "localstack/localstack:0.8.6", ] images = ["tensorflow", "spark"] dockerclient.pull_images(repositories=repositories) download_spark() dockerclient.build_images(images=images)
def pull_images(self, repositories: list): """ Pulls images from Docker repositories Args repositories: Docker repositories to pull from """ for repository in repositories: logger.info("Pulling from %s\n", repository) for line in self.pull(repository, stream=True, decode=True): if (bool(line.get("status", False))) & ("." not in line.get( "id", "")): sys.stdout.write("\r{id}: {status} {progress}".format( id=line.get("id", ""), status=line.get("status", ""), progress=line.get("progress", ""), )) print("\n")
def build_images(self, images: list): """ Builds Docker images from a Dockerfile. Args images: Images stored in mlstack/build/{image}, where the path is a Docker build context containing a Dockerfile """ for image in images: # Returns mlstack/build/{image} (where mlstack is the install directory) package_dir = str( str(Path(__file__).absolute()).replace( Path(__file__).name, "")) context_path = package_dir.replace( "mlstack/clients", "build/{image}".format(image=image)) logger.info("Building %s from %s", image, context_path) for line in self.build( path=context_path + "/.", tag="mlstack-{image}:latest".format(image=image), decode=True, ): if line.get("stream", None): logger.info(line.get("stream")) elif line.get("error", None): logger.error(line.get("error")) else: logger.warning(line) logger.info("Successfully built %s", image)
def __init__(self): logger.info("Initializing MLStack")
def close(): """ Close the MLStack cluster """ logger.info("Closing the MLStack cluster in Kubernetes") KubernetesClient().delete_manifests(["spark", "tensorflow", "localstack"])
def create(): """ Creates an MLStack local Kubernetes cluster """ logger.info("Creating an MLStack cluster in Kubernetes") KubernetesClient().create_manifests(["spark", "tensorflow", "localstack"])
def create_manifest(self, component: str): """ Creates a manifest from a list of components located in mlstack/manifests. Uses a clever getattr() trick to avoid hardcoding everything. Will create kubernetes apps in the following order: - PersistentVolumeClaim - PersistentVolume - ConfigMap - Deployment - Secret - Service Args components: A list of mlstack component manifests to create. """ warning_message = "KubeApiException on {kind} `{name}`. \n Exception:\n" for file in glob.glob( str(Path(self.manifests_dir, component)) + "/*.yaml"): generator = read_yaml(file) for body in generator: if body: kind = body.get("kind") name = body.get("metadata").get("name") method_ext = "_".join( val.lower() for val in re.findall("[A-Z][^A-Z]*", kind)) warning_message = "KubeApiException on {kind} `{name}`".format( kind=kind, name=name) if kind in [ "PersistentVolumeClaim", "ConfigMap", "Service", "Secret", ]: try: method = "create_namespaced_{ext}".format( ext=method_ext) getattr(self, method)(namespace="mlstack", body=body) logger.info("%s `%s` created", kind, name) except KubeApiException as exception: logger.warning(warning_message, exception) if kind in ["PersistentVolume"]: try: getattr(self, "create_persistent_volume")(body=body) logger.info("%s `%s` created", kind, name) except KubeApiException as exception: logger.warning(warning_message, exception) if kind in ["Deployment"]: try: AppsV1Api().create_namespaced_deployment( namespace="mlstack", body=body) logger.info("%s `%s` created", kind, name) except KubeApiException as exception: logger.warning(warning_message, exception)