diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b352c75 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +#terraform +.terraform +*.tfstate +*.tfstate.backup +*.tfvars \ No newline at end of file diff --git a/IaC/main.tf b/IaC/main.tf new file mode 100644 index 0000000..df350e7 --- /dev/null +++ b/IaC/main.tf @@ -0,0 +1,33 @@ +terraform { + backend "gcs" { + } + required_version = "=0.12.29" + required_providers { + google = "~> 3.13" + } +} + +provider "google" { + project = var.project_id +} + +provider "google-beta" { + project = var.project_id +} + + +module "network" { + source = "./modules/network" + vpc_name = var.network_name +} + +module "mlflow" { + source = "./modules/mlflow" + artifacts_bucket_name = var.artifacts_bucket + db_password_value = var.db_password_value + private_vpc_connection = module.network.private_vpc_connection + network_link = module.network.network_link + server_docker_image = var.mlflow_docker_image + project_id = var.project_id + vpc_connector = module.network.vpc_connector +} diff --git a/IaC/modules/mlflow/artifacts/main.tf b/IaC/modules/mlflow/artifacts/main.tf new file mode 100644 index 0000000..e7c0fbd --- /dev/null +++ b/IaC/modules/mlflow/artifacts/main.tf @@ -0,0 +1,16 @@ +resource "google_storage_bucket" "this" { + name = var.bucket_name + location = var.bucket_location + storage_class = var.storage_class + versioning { + enabled = var.versioning_enabled + } + lifecycle_rule { + condition { + num_newer_versions = var.number_of_version + } + action { + type = "Delete" + } + } +} diff --git a/IaC/modules/mlflow/artifacts/outputs.tf b/IaC/modules/mlflow/artifacts/outputs.tf new file mode 100644 index 0000000..c7af138 --- /dev/null +++ b/IaC/modules/mlflow/artifacts/outputs.tf @@ -0,0 +1,4 @@ +output "url" { + description = "gcs uri" + value = google_storage_bucket.this.url +} diff --git a/IaC/modules/mlflow/artifacts/variables.tf b/IaC/modules/mlflow/artifacts/variables.tf new file mode 100644 index 0000000..ec992d4 --- /dev/null +++ b/IaC/modules/mlflow/artifacts/variables.tf @@ -0,0 +1,28 @@ +variable "bucket_name" { + description = "Name of the bucket." + type = string +} +variable "bucket_location" { + description = "Location of the bucket." + type = string + default = "EUROPE-WEST1" +} +variable "versioning_enabled" { + description = "True if you want to version your bucket." + type = bool + default = true +} +variable "number_of_version" { + description = "Number of version you want to keep with the versionning." + type = number + default = 1 +} +variable "storage_class" { + description = "Storage class of your bucket" + type = string + default ="STANDARD" +} +variable "module_depends_on" { + type = any + default = null +} diff --git a/IaC/modules/mlflow/database/main.tf b/IaC/modules/mlflow/database/main.tf new file mode 100644 index 0000000..79b4046 --- /dev/null +++ b/IaC/modules/mlflow/database/main.tf @@ -0,0 +1,37 @@ +resource "random_id" "db_name_suffix" { + byte_length = 5 +} + +resource "google_sql_database_instance" "this_instance" { + name = "${var.instance_prefix}-${random_id.db_name_suffix.hex}" + database_version = var.database_version + region = var.region + + depends_on = [var.private_vpc_connection] + + settings { + tier = var.size + ip_configuration { + ipv4_enabled = false + private_network = var.network_link + } + backup_configuration { + enabled = true + } + availability_type = var.availability_type + + } +} + +resource "google_sql_database" "this_database" { + name = var.database_name + instance = google_sql_database_instance.this_instance.name + depends_on = [google_sql_database_instance.this_instance] +} + +resource "google_sql_user" "this_user" { + name = var.username + instance = google_sql_database_instance.this_instance.name + password = var.password + depends_on = [google_sql_database_instance.this_instance] +} diff --git a/IaC/modules/mlflow/database/outputs.tf b/IaC/modules/mlflow/database/outputs.tf new file mode 100644 index 0000000..093dafd --- /dev/null +++ b/IaC/modules/mlflow/database/outputs.tf @@ -0,0 +1,12 @@ +output "instance_connection_name" { + description = "Connection string used to connect to the instance" + value = google_sql_database_instance.this_instance.connection_name +} +output "private_ip" { + description = "Private ip connect to the instance" + value = google_sql_database_instance.this_instance.private_ip_address +} +output "database_name" { + description = "The name of the database" + value = google_sql_database.this_database.name +} diff --git a/IaC/modules/mlflow/database/variables.tf b/IaC/modules/mlflow/database/variables.tf new file mode 100644 index 0000000..e77d728 --- /dev/null +++ b/IaC/modules/mlflow/database/variables.tf @@ -0,0 +1,50 @@ +variable "instance_prefix" { + type = string + description = "Name of the database instance you want to deploy" + default = "mlflow" +} +variable "database_version" { + type = string + description = "Version of the database instance you use" + default = "MYSQL_5_7" +} +variable "region" { + type = string + description = "Region of the database instance" + default = "europe-west1" +} +variable "private_vpc_connection" { + type = any + description = "Private connection used to connect your instance with" +} +variable "size" { + type = string + description = "Size of the database instance" + default = "db-f1-micro" +} +variable "network_link" { + type = string + description = "Network link you want to connect your database with" +} +variable "availability_type" { + type = string + description = "Availability of your instance" + default = "ZONAL" +} +variable "database_name" { + type = string + description = "Name of the database created" + default = "mlflow" +} +variable "username" { + type = string + description = "Username to connect to database instance" +} +variable "password" { + type = string + description = "Password to connect to database instance" +} +variable "module_depends_on" { + type = any + default = null +} diff --git a/IaC/modules/mlflow/main.tf b/IaC/modules/mlflow/main.tf new file mode 100644 index 0000000..88344ab --- /dev/null +++ b/IaC/modules/mlflow/main.tf @@ -0,0 +1,44 @@ +module "artifacts" { + source = "./artifacts" + bucket_name = var.artifacts_bucket_name + bucket_location = var.artifacts_bucket_location + number_of_version = var.artifacts_number_of_version + storage_class = var.artifacts_storage_class +} + +module "db_secret" { + source = "./secret_manager" + secret_id = var.db_password_name + secret_value = var.db_password_value +} + +module "database" { + source = "./database" + instance_prefix = var.db_instance_prefix + database_version = var.db_version + region = var.db_region + private_vpc_connection = var.private_vpc_connection + size = var.db_size + network_link = var.network_link + availability_type = var.db_availability_type + database_name = var.db_name + username = var.db_username + password = module.db_secret.secret_value +} + +module "server" { + source = "./server" + server_name = var.mlflow_server + location = var.server_location + docker_image_name = var.server_docker_image + env_variables = var.server_env_variables + sql_instance_name = module.database.instance_connection_name + db_private_ip = module.database.private_ip + project_id = var.project_id + db_password_name = var.db_password_name + db_username = var.db_username + db_name = var.db_name + gcs_backend = module.artifacts.url + vpc_connector = var.vpc_connector + module_depends_on = var.module_depends_on +} diff --git a/IaC/modules/mlflow/secret_manager/main.tf b/IaC/modules/mlflow/secret_manager/main.tf new file mode 100644 index 0000000..8b82ef4 --- /dev/null +++ b/IaC/modules/mlflow/secret_manager/main.tf @@ -0,0 +1,19 @@ +resource "google_secret_manager_secret" "secret" { + provider = google-beta + + secret_id = var.secret_id + + replication { + automatic = true + } +} + + +resource "google_secret_manager_secret_version" "secret-version" { + provider = google-beta + + secret = google_secret_manager_secret.secret.id + + secret_data = var.secret_value + depends_on = [google_secret_manager_secret.secret] +} diff --git a/IaC/modules/mlflow/secret_manager/outputs.tf b/IaC/modules/mlflow/secret_manager/outputs.tf new file mode 100644 index 0000000..2ffa9b4 --- /dev/null +++ b/IaC/modules/mlflow/secret_manager/outputs.tf @@ -0,0 +1,4 @@ +output "secret_value" { + description = "Value of the created secret" + value = google_secret_manager_secret_version.secret-version.secret_data +} diff --git a/IaC/modules/mlflow/secret_manager/variables.tf b/IaC/modules/mlflow/secret_manager/variables.tf new file mode 100644 index 0000000..a262a7a --- /dev/null +++ b/IaC/modules/mlflow/secret_manager/variables.tf @@ -0,0 +1,12 @@ +variable "secret_id" { + type = string + description = "Name of the secret you want to create" +} +variable "secret_value" { + type = string + description = "value of the secret you want to create" +} +variable "module_depends_on" { + type = any + default = null +} diff --git a/IaC/modules/mlflow/server/main.tf b/IaC/modules/mlflow/server/main.tf new file mode 100644 index 0000000..b8b96c3 --- /dev/null +++ b/IaC/modules/mlflow/server/main.tf @@ -0,0 +1,94 @@ +locals { + env_variables = merge( + { + "GCP_PROJECT"=var.project_id, + "DB_PASSWORD_NAME"=var.db_password_name, + "DB_USERNAME"=var.db_username, + "DB_NAME"=var.db_name, + "DB_PRIVATE_IP"=var.db_private_ip, + "GCS_BACKEND"=var.gcs_backend + }, var.env_variables) +} + + +resource "google_service_account" "service_account_cloud_run" { + account_id = format("cloud-run-%s", var.server_name) + display_name = "Cloud run service account used" +} + +resource "google_project_iam_member" "cloudsql" { + project = google_service_account.service_account_cloud_run.project + role = "roles/cloudsql.client" + member = format("serviceAccount:%s", google_service_account.service_account_cloud_run.email) +} + +resource "google_project_iam_member" "secret" { + project = google_service_account.service_account_cloud_run.project + role = "roles/secretmanager.secretAccessor" + member = format("serviceAccount:%s", google_service_account.service_account_cloud_run.email) +} + +resource "google_project_iam_member" "gcs" { + project = google_service_account.service_account_cloud_run.project + role = "roles/storage.objectAdmin" + member = format("serviceAccount:%s", google_service_account.service_account_cloud_run.email) +} + + +resource "google_cloud_run_service" "default" { + name = var.server_name + location = var.location + + template { + spec { + service_account_name = google_service_account.service_account_cloud_run.email + containers { + image = var.docker_image_name + dynamic "env" { + for_each = local.env_variables + content { + name = env.key + value = env.value + } + } + resources { + limits = { + cpu = var.cpu_limit + memory = var.memory_limit + } + } + } + } + metadata { + annotations = { + "run.googleapis.com/cloudsql-instances" = var.sql_instance_name + "run.googleapis.com/vpc-access-connector" = var.vpc_connector + } + } + } + + traffic { + percent = 100 + latest_revision = true + } + autogenerate_revision_name = true + depends_on = [google_project_iam_member.cloudsql, google_project_iam_member.secret, google_project_iam_member.gcs, var.module_depends_on] +} + + +data "google_iam_policy" "noauth" { + binding { + role = "roles/run.invoker" + members = [ + "allUsers", + ] + } +} + +resource "google_cloud_run_service_iam_policy" "noauth" { + location = google_cloud_run_service.default.location + project = google_cloud_run_service.default.project + service = google_cloud_run_service.default.name + + policy_data = data.google_iam_policy.noauth.policy_data +} diff --git a/IaC/modules/mlflow/server/variables.tf b/IaC/modules/mlflow/server/variables.tf new file mode 100644 index 0000000..4a81b20 --- /dev/null +++ b/IaC/modules/mlflow/server/variables.tf @@ -0,0 +1,68 @@ +variable "server_name" { + type = string + description = "Name of your server" +} +variable "location" { + type = string + description = "Location to deploy your server" + default = "europe-west1" +} +variable "docker_image_name" { + type = string + description = "Name of the docker image" +} +variable "env_variables" { + type = map + description = "Env variable to be used in your container" +} +variable "sql_instance_name" { + type = string + description = "Sql instance name your server needs access to" +} +variable "project_id" { + description = "GCP project" + type = string +} +variable "db_password_name" { + description = "Name of the db password stored in secret manager" + type = string +} +variable "db_username" { + description = "Username used to connect to your db" + type = string +} +variable "db_name" { + description = "Name of the database" + type = string +} +variable "gcs_backend" { + description = "Gcs bucket used for artifacts" + type = string +} +variable "cpu_limit" { + type = string + description = "Maximum cpu" + default = "1000m" +} +variable "memory_limit" { + type = string + description = "Memory limit of your container" + default = "1024Mi" +} +variable "vpc_connector" { + type = string + description = "Vpc connector of your private network" +} +variable "db_private_ip" { + type = string + description = "Private ip of the db" +} +variable "module_depends_on" { + type = any + default = null +} +variable "service_account_mlflow_users" { + type = string + default = "mlflow-users" + description = "Service account created to connect to mlflow" +} \ No newline at end of file diff --git a/IaC/modules/mlflow/variables.tf b/IaC/modules/mlflow/variables.tf new file mode 100644 index 0000000..122dda8 --- /dev/null +++ b/IaC/modules/mlflow/variables.tf @@ -0,0 +1,102 @@ +variable "artifacts_bucket_name" { + description = "Name of the mlflow bucket created to store artifacts" + type = string +} +variable "artifacts_bucket_location" { + description = "Location of the mlflow artifact bucket deployed" + type = string + default = "EUROPE-WEST1" +} +variable "artifacts_number_of_version" { + description = "Number of file version kept in your artifacts bucket" + type = number + default = 1 +} +variable "artifacts_storage_class" { + description = "Storage class of your artifact bucket" + type = string + default = "STANDARD" +} +variable "db_password_name" { + description = "Name of the database password stored in secret manager" + type = string + default = "mlflow-db-pwd" +} +variable "db_password_value" { + description = "Value of the database password stored in secret manager" + type = string +} +variable "db_username" { + description = "Value of the database username" + type = string + default = "mlflowuser" +} +variable "db_instance_prefix" { + description = "prefix used as database instance name" + type = string + default = "mlflow" +} +variable "db_version" { + description = "Database instance version in GCP" + type = string + default = "MYSQL_5_7" +} +variable "db_region" { + description = "Database region" + type = string + default = "europe-west1" +} +variable "private_vpc_connection" { + description = "Vpc connection with the database" + type = any +} +variable "db_size" { + description = "Database instance size" + type = string + default = "db-f1-micro" +} +variable "network_link" { + description = "Link to your network" + type = string +} +variable "db_availability_type" { + description = "Availability of your database" + type = string + default = "ZONAL" +} +variable "db_name" { + description = "Name of the database created inside the instance" + type = string + default = "mlflow" +} +variable "mlflow_server" { + description = "Name of the mlflow server deployed to cloud run" + type = string + default = "mlflow" +} +variable "server_location" { + description = "Location to deploy cloud run server" + type = string + default = "europe-west1" +} +variable "server_docker_image" { + description = "Docker image name of your mlflow server" + type = string +} +variable "server_env_variables" { + description = "Env variables used inside your container" + type = map + default = {} +} +variable "project_id" { + description = "GCP project" + type = string +} +variable "vpc_connector" { + type = string + description = "Vpc connector of your private network" +} +variable "module_depends_on" { + type = any + default = null +} diff --git a/IaC/modules/network/main.tf b/IaC/modules/network/main.tf new file mode 100644 index 0000000..4ed1a9c --- /dev/null +++ b/IaC/modules/network/main.tf @@ -0,0 +1,28 @@ +resource "google_compute_network" "vpc" { + name = var.vpc_name + routing_mode = "GLOBAL" + auto_create_subnetworks = true +} + +resource "google_compute_global_address" "private_ip_address" { + provider = google-beta + + name = "private-ip-address" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.vpc.self_link +} + +resource "google_service_networking_connection" "private_vpc_connection" { + network = google_compute_network.vpc.self_link + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.private_ip_address.name] +} + +resource "google_vpc_access_connector" "vpc_con" { + name = "vpc-con" + region = var.region + ip_cidr_range = lookup(var.vpc_connector_regions, var.region) + network = google_compute_network.vpc.name +} diff --git a/IaC/modules/network/outputs.tf b/IaC/modules/network/outputs.tf new file mode 100644 index 0000000..bf9dedc --- /dev/null +++ b/IaC/modules/network/outputs.tf @@ -0,0 +1,12 @@ +output "network_link" { + description = "Link of the created network" + value = google_compute_network.vpc.self_link +} +output "private_vpc_connection" { + description = "Private vpc connection to servicenetworking" + value = google_service_networking_connection.private_vpc_connection +} +output "vpc_connector" { + description = "Connector to your private network" + value = google_vpc_access_connector.vpc_con.self_link +} diff --git a/IaC/modules/network/variables.tf b/IaC/modules/network/variables.tf new file mode 100644 index 0000000..fce4fee --- /dev/null +++ b/IaC/modules/network/variables.tf @@ -0,0 +1,16 @@ +variable "vpc_name" { + type = string + description = "Name of the network you want to create" +} +variable "region" { + type = string + description = "Region to deploy your vpc connector" + default = "europe-west1" +} +variable "vpc_connector_regions" { + type = map + description = "Regions where the VPC Access connector resides and the matching ip cidr range" + default = { + "europe-west1" = "10.8.0.0/28" + } +} \ No newline at end of file diff --git a/IaC/modules/services/main.tf b/IaC/modules/services/main.tf new file mode 100644 index 0000000..d817bbe --- /dev/null +++ b/IaC/modules/services/main.tf @@ -0,0 +1,6 @@ +resource "google_project_service" "project" { + count = length(var.services) + project = var.project_id + service = var.services[count.index] + disable_dependent_services = true +} diff --git a/IaC/modules/services/variables.tf b/IaC/modules/services/variables.tf new file mode 100644 index 0000000..d6d99bd --- /dev/null +++ b/IaC/modules/services/variables.tf @@ -0,0 +1,8 @@ +variable "project_id" { + description = "Id of your project" + type = string +} +variable "services" { + description = "List of url of the service you want to activate" + type = list(string) +} diff --git a/IaC/prerequesites/main.tf b/IaC/prerequesites/main.tf new file mode 100644 index 0000000..60102b4 --- /dev/null +++ b/IaC/prerequesites/main.tf @@ -0,0 +1,26 @@ +terraform { + required_version = "=0.12.29" + required_providers { + google = "~> 3.13" + } +} + +provider "google" { + project = var.project_id +} + +module "services" { + source = "./../modules/services" + project_id = var.project_id + services = ["container.googleapis.com", "servicenetworking.googleapis.com", + "stackdriver.googleapis.com", "vpcaccess.googleapis.com", "run.googleapis.com", + "sqladmin.googleapis.com", "secretmanager.googleapis.com"] +} + +module "bucket_backend" { + source = "./../modules/mlflow/artifacts" + bucket_name = var.backend_bucket + bucket_location = var.backend_bucket_location + number_of_version = var.backend_bucket_number_of_version + storage_class = var.backend_bucket_storage_class +} \ No newline at end of file diff --git a/IaC/prerequesites/variables.tf b/IaC/prerequesites/variables.tf new file mode 100644 index 0000000..86b2805 --- /dev/null +++ b/IaC/prerequesites/variables.tf @@ -0,0 +1,28 @@ +variable "project_id" { + description = "GCP project" + type = string +} +variable "backend_bucket" { + description = "Name of the bucket." + type = string +} +variable "backend_bucket_location" { + description = "Location of the bucket." + type = string + default = "EUROPE-WEST1" +} +variable "versioning_enabled" { + description = "True if you want to version your bucket." + type = bool + default = true +} +variable "backend_bucket_number_of_version" { + description = "Number of version you want to keep with the versionning." + type = number + default = 3 +} +variable "backend_bucket_storage_class" { + description = "Storage class of your bucket" + type = string + default ="STANDARD" +} \ No newline at end of file diff --git a/IaC/variables.tf b/IaC/variables.tf new file mode 100644 index 0000000..7acd7f1 --- /dev/null +++ b/IaC/variables.tf @@ -0,0 +1,22 @@ +variable "project_id" { + description = "GCP project" + type = string +} +variable "artifacts_bucket" { + description = "GCS bucket used to store your artifacts" + type = string + default = "oneclick-mlflow-store" +} +variable "db_password_value" { + description = "Database password to connect to your instance" + type = string +} +variable "mlflow_docker_image" { + description = "Docker image used in container registry" + type = string +} +variable "network_name" { + description = "Network used" + type = string + default = "default-private" +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0b8db72 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +DOCKER_REPO := eu.gcr.io +DOCKER_NAME := mlflow +DOCKER_TAG := 0.1 + + +pre-requesites: + @cd Iac/prerequesites && terraform init && terraform apply -var="project_id=$(PROJECT_ID)" + +build-docker: + @cd tracking_server && docker build -t $(DOCKER_REPO)/$(PROJECT_ID)/$(DOCKER_NAME):$(DOCKER_TAG) -f tracking.Dockerfile . + +push-docker: + @docker push $(DOCKER_REPO)/$(PROJECT_ID)/$(DOCKER_NAME):$(DOCKER_TAG) + +init-terraform: + @cd Iac && terraform init -backend-config="bucket=$(BACKEND_TERRAFORM)" + +apply-terraform: + @cd Iac && terraform apply -var="project_id=$(PROJECT_ID)" -var="mlflow_docker_image=$(DOCKER_REPO)/$(PROJECT_ID)/$(DOCKER_NAME):$(DOCKER_TAG)" + +plan-terraform: + @cd Iac && terraform plan -var="project_id=$(PROJECT_ID)" -var="mlflow_docker_image=$(DOCKER_REPO)/$(PROJECT_ID)/$(DOCKER_NAME):$(DOCKER_TAG)" + +destroy-terraform: + @cd Iac && terraform destroy -var="project_id=$(PROJECT_ID)" -var="mlflow_docker_image=$(DOCKER_REPO)/$(PROJECT_ID)/$(DOCKER_NAME):$(DOCKER_TAG)" + +apply: init-terraform apply-terraform + +plan: init-terraform plan-terraform + +destroy: init-terraform destroy-terraform + +docker: build-docker push-docker diff --git a/README.md b/README.md index b0c51a2..70623dc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,22 @@ # one-click-mlflow A tool to deploy a mostly serverless MLflow on a GCP project with one command +## How to use + +### Pre-requesites +- Create a GCP project +- Init gcloud +- Have docker installed locally +- Export the name of your GCP project by running `EXPORT PROJECT_ID=YOUR_PROJECT` +- Run `make pre-requesites`. You'll be asked to enter the bucket name used to store terraform state, and your project id +- Deploy mlflow container by running `make docker`. This will build mlflow docker image locally and push it to container registry + +### Deploy mlflow +- Export the name of your GCP project by running `EXPORT PROJECT_ID=YOUR_PROJECT` +- Export the name of your bucket used to store terraform state by running `EXPORT BACKEND_TERRAFORM=BUCKET_NAME`. It must be the same as the one you selected when installing the pre-requesites. +- Deploy mlflow to GCP by running `make apply`. You'll be asked to enter the database password you want to use + + ## Goals The project's deliverables are diff --git a/bin/one-click-mlflow.py b/bin/one-click-mlflow.py deleted file mode 100644 index ed6bfc2..0000000 --- a/bin/one-click-mlflow.py +++ /dev/null @@ -1,2 +0,0 @@ -from lib.entrypoint import run - diff --git a/tracking_server/requirements.txt b/tracking_server/requirements.txt new file mode 100644 index 0000000..780f8c9 --- /dev/null +++ b/tracking_server/requirements.txt @@ -0,0 +1,7 @@ +mlflow==1.11.0 +sqlalchemy==1.3.13 +boto3==1.14.62 +google-cloud-storage==1.31.0 +psycopg2==2.8.6 +mysql==0.0.2 +pymysql==0.10.1 diff --git a/tracking_server/run_tracking.sh b/tracking_server/run_tracking.sh new file mode 100644 index 0000000..5d432f0 --- /dev/null +++ b/tracking_server/run_tracking.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +DB_PASSWORD=$(gcloud beta secrets versions access --project=${GCP_PROJECT} --secret=${DB_PASSWORD_NAME} latest) +BACKEND_URI=mysql+pymysql://${DB_USERNAME}:${DB_PASSWORD}@${DB_PRIVATE_IP}/${DB_NAME} + +mlflow db upgrade ${BACKEND_URI} + +mlflow server \ + --backend-store-uri ${BACKEND_URI} \ + --default-artifact-root ${GCS_BACKEND} \ + --host 0.0.0.0 \ + --port $PORT diff --git a/tracking_server/tracking.Dockerfile b/tracking_server/tracking.Dockerfile new file mode 100644 index 0000000..e6a3631 --- /dev/null +++ b/tracking_server/tracking.Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.7 + +WORKDIR /mlflow/ + +RUN mkdir -p /mlflow/ \ + && apt-get update \ + && apt-get -y install --no-install-recommends apt-transport-https \ + ca-certificates gnupg default-libmysqlclient-dev libpq-dev build-essential curl + +RUN curl -sSL https://sdk.cloud.google.com | bash +ENV PATH $PATH:/root/google-cloud-sdk/bin +RUN gcloud components install beta -q + +COPY requirements.txt . +RUN pip install -r requirements.txt + +COPY run_tracking.sh . +RUN chmod +x run_tracking.sh + +CMD /mlflow/run_tracking.sh