/// <summary> /// ユーザの任意追加環境変数をマージする /// </summary> private static void AddUserEnvToInputModel(Dictionary <string, string> optionDic, RunContainerInputModel inputModel) { if (optionDic.Count > 0) { foreach (var env in optionDic) { // ユーザー指定環境変数とappsettingsの環境変数を結合 string value = env.Value ?? ""; //nullは空文字と見なす if (inputModel.EnvList.ContainsKey(env.Key)) { inputModel.EnvList[env.Key] = value; //あればユーザ指定の値で上書き } else { inputModel.EnvList.Add(env.Key, value); //なければ追加 } } } }
/// <summary> /// 新規に画像認識の推論用コンテナを作成する。 /// </summary> /// <param name="inferenceHistory">対象の推論履歴</param> /// <returns>作成したコンテナのステータス</returns> public async Task <Result <ContainerInfo, string> > RunInferenceContainerAsync(InferenceHistory inferenceHistory) { string token = await GetUserAccessTokenAsync(); if (token == null) { //トークンがない場合、結果はnull return(Result <ContainerInfo, string> .CreateErrorResult("Access denied. Failed to get token to access the cluster management system.")); } long gitId = inferenceHistory.ModelGitId == -1 ? CurrentUserInfo.SelectedTenant.DefaultGitId.Value : inferenceHistory.ModelGitId.Value; var registryMap = registryLogic.GetCurrentRegistryMap(inferenceHistory.ContainerRegistryId.Value); var gitEndpoint = gitLogic.GetPullUrl(gitId, inferenceHistory.ModelRepository, inferenceHistory.ModelRepositoryOwner); if (gitEndpoint == null) { //Git情報は必須にしているので、無ければエラー return(Result <ContainerInfo, string> .CreateErrorResult("Git credential is not valid.")); } var nodes = GetAccessibleNode(); if (nodes == null || nodes.Count == 0) { //デプロイ可能なノードがゼロなら、エラー扱い return(Result <ContainerInfo, string> .CreateErrorResult("Access denied. There is no node this tenant can use.")); } //コンテナを起動するために必要な設定値をインスタンス化 var inputModel = new RunContainerInputModel() { ID = inferenceHistory.Id, TenantName = TenantName, LoginUser = CurrentUserInfo.Alias, //アカウントはエイリアスから指定 Name = inferenceHistory.Key, ContainerImage = registryMap.Registry.GetImagePath(inferenceHistory.ContainerImage, inferenceHistory.ContainerTag), ScriptType = "inference", Cpu = inferenceHistory.Cpu, Memory = inferenceHistory.Memory, Gpu = inferenceHistory.Gpu, KqiImage = "kamonohashi/cli:" + versionLogic.GetVersion(), KqiToken = loginLogic.GenerateToken().AccessToken, LogPath = "/kqi/attach/inference_stdout_stderr_${INFERENCE_ID}.log", NfsVolumeMounts = new List <NfsVolumeMountModel>() { // 結果保存するディレクトリ new NfsVolumeMountModel() { Name = "nfs-output", MountPath = "/kqi/output", SubPath = inferenceHistory.Id.ToString(), Server = CurrentUserInfo.SelectedTenant.Storage.NfsServer, ServerPath = CurrentUserInfo.SelectedTenant.InferenceContainerOutputNfsPath, ReadOnly = false }, // 添付ファイルを保存するディレクトリ // 学習結果ディレクトリを学習完了時にzip圧縮して添付するために使用 new NfsVolumeMountModel() { Name = "nfs-attach", MountPath = "/kqi/attach", SubPath = inferenceHistory.Id.ToString(), Server = CurrentUserInfo.SelectedTenant.Storage.NfsServer, ServerPath = CurrentUserInfo.SelectedTenant.InferenceContainerAttachedNfsPath, ReadOnly = false } }, ContainerSharedPath = new Dictionary <string, string>() { { "tmp", "/kqi/tmp/" }, { "input", "/kqi/input/" }, { "git", "/kqi/git/" } }, EnvList = new Dictionary <string, string>() { { "DATASET_ID", inferenceHistory.DataSetId.ToString() }, { "INFERENCE_ID", inferenceHistory.Id.ToString() }, { "PARENT_ID", inferenceHistory.ParentId?.ToString() }, { "MODEL_REPOSITORY", gitEndpoint.FullUrl }, { "MODEL_REPOSITORY_URL", gitEndpoint.Url }, { "MODEL_REPOSITORY_TOKEN", gitEndpoint.Token }, { "COMMIT_ID", inferenceHistory.ModelCommitId }, { "KQI_SERVER", containerOptions.WebServerUrl }, { "KQI_TOKEN", loginLogic.GenerateToken().AccessToken }, { "http_proxy", containerOptions.Proxy }, { "https_proxy", containerOptions.Proxy }, { "no_proxy", containerOptions.NoProxy }, { "HTTP_PROXY", containerOptions.Proxy }, { "HTTPS_PROXY", containerOptions.Proxy }, { "NO_PROXY", containerOptions.NoProxy }, { "COLUMNS", containerOptions.ShellColumns }, { "PYTHONUNBUFFERED", "true" }, // python実行時の標準出力・エラーのバッファリングをなくす { "LC_ALL", "C.UTF-8" }, // python実行時のエラー回避 { "LANG", "C.UTF-8" } // python実行時のエラー回避 }, EntryPoint = inferenceHistory.EntryPoint, PortMappings = new PortMappingModel[] { new PortMappingModel() { Protocol = "TCP", Port = 22, TargetPort = 22, Name = "ssh" }, }, ClusterManagerToken = token, RegistryTokenName = registryMap.RegistryTokenKey, IsNodePort = true }; // 親を指定した場合は親の出力結果を/kqi/parentにマウント // 推論ジョブにおける親ジョブは学習ジョブとなるので、SubPathとServerPathの指定に注意 if (inferenceHistory.ParentId != null) { inputModel.NfsVolumeMounts.Add(new NfsVolumeMountModel() { Name = "nfs-parent", MountPath = "/kqi/parent", SubPath = inferenceHistory.ParentId.ToString(), Server = CurrentUserInfo.SelectedTenant.Storage.NfsServer, ServerPath = CurrentUserInfo.SelectedTenant.TrainingContainerOutputNfsPath, ReadOnly = true }); } // ユーザの任意追加環境変数をマージする AddUserEnvToInputModel(inferenceHistory.OptionDic, inputModel); //使用できるノードを制約に追加 inputModel.ConstraintList = new Dictionary <string, List <string> >() { { containerOptions.ContainerLabelHostName, nodes } }; if (string.IsNullOrEmpty(inferenceHistory.Partition) == false) { // パーティション指定があれば追加 inputModel.ConstraintList.Add(containerOptions.ContainerLabelPartition, new List <string> { inferenceHistory.Partition }); } var outModel = await clusterManagementService.RunContainerAsync(inputModel); if (outModel.IsSuccess == false) { return(Result <ContainerInfo, string> .CreateErrorResult(outModel.Error)); } var port = outModel.Value.PortMappings.Find(p => p.Name == "ssh"); return(Result <ContainerInfo, string> .CreateResult(new ContainerInfo() { Name = outModel.Value.Name, Status = outModel.Value.Status, Host = outModel.Value.Host, Port = port.NodePort, Configuration = outModel.Value.Configuration })); }
/// <summary> /// 新規にTensorBoard表示用のコンテナを作成する。 /// 成功した場合は作成結果が、失敗した場合はnullが返る。 /// </summary> /// <param name="trainingHistory">対象の学習履歴</param> /// <returns>作成したコンテナのステータス</returns> public async Task <ContainerInfo> RunTensorBoardContainerAsync(TrainingHistory trainingHistory) { //コンテナ名は自動生成 //使用できる文字など、命名規約はコンテナ管理サービス側によるが、 //ユーザ入力値検証の都合でどうせ決め打ちしないといけないので、ロジック層で作ってしまう string tenantId = CurrentUserInfo.SelectedTenant.Id.ToString("0000"); string containerName = $"tensorboard-{tenantId}-{trainingHistory.Id}-{DateTime.Now.ToString("yyyyMMddHHmmssffffff")}"; var registryMap = registryLogic.GetCurrentRegistryMap(trainingHistory.ContainerRegistryId.Value); string token = await GetUserAccessTokenAsync(); if (token == null) { //トークンがない場合、結果はnull return(new ContainerInfo() { Status = ContainerStatus.Forbidden }); } var nodes = GetAccessibleNode(); if (nodes == null || nodes.Count == 0) { //デプロイ可能なノードがゼロなら、エラー扱い return(new ContainerInfo() { Status = ContainerStatus.Forbidden }); } //コンテナを起動するために必要な設定値をインスタンス化 var inputModel = new RunContainerInputModel() { ID = trainingHistory.Id, TenantName = TenantName, LoginUser = CurrentUserInfo.Alias, //アカウントはエイリアスから指定 Name = containerName, ContainerImage = "tensorflow/tensorflow", ScriptType = "tensorboard", Cpu = 1, Memory = 1, //メモリは1GBで仮決め Gpu = 0, KqiImage = "kamonohashi/cli:" + versionLogic.GetVersion(), NfsVolumeMounts = new List <NfsVolumeMountModel>() { // 結果が保存されているディレクトリ new NfsVolumeMountModel() { Name = "nfs-output", MountPath = "/kqi/output", SubPath = trainingHistory.Id.ToString(), Server = CurrentUserInfo.SelectedTenant.Storage.NfsServer, ServerPath = CurrentUserInfo.SelectedTenant.TrainingContainerOutputNfsPath } }, EnvList = new Dictionary <string, string>() { { "KQI_SERVER", containerOptions.WebServerUrl }, { "KQI_TOKEN", loginLogic.GenerateToken().AccessToken }, { "http_proxy", containerOptions.Proxy }, { "https_proxy", containerOptions.Proxy }, { "no_proxy", containerOptions.NoProxy }, { "HTTP_PROXY", containerOptions.Proxy }, { "HTTPS_PROXY", containerOptions.Proxy }, { "NO_PROXY", containerOptions.NoProxy }, { "PYTHONUNBUFFERED", "true" }, // python実行時の標準出力・エラーのバッファリングをなくす { "LC_ALL", "C.UTF-8" }, // python実行時のエラー回避 { "LANG", "C.UTF-8" } // python実行時のエラー回避 }, ConstraintList = new Dictionary <string, List <string> >() { { containerOptions.ContainerLabelHostName, nodes }, //使用できるノードを取得し、制約に追加 { containerOptions.ContainerLabelTensorBoardEnabled, new List <string> { "true" } } // tensorboardの実行が許可されているサーバでのみ実行, }, PortMappings = new PortMappingModel[] { new PortMappingModel() { Protocol = "TCP", Port = 6006, TargetPort = 6006, Name = "tensorboard" } }, ClusterManagerToken = token, RegistryTokenName = registryMap.RegistryTokenKey, IsNodePort = true //ランダムポート指定。アクセス先ポートが動的に決まるようになる。 }; var outModel = await clusterManagementService.RunContainerAsync(inputModel); if (outModel.IsSuccess == false) { return(new ContainerInfo() { Status = ContainerStatus.Failed }); } var port = outModel.Value.PortMappings.Find(p => p.Name == "tensorboard"); return(new ContainerInfo() { Name = containerName, Status = outModel.Value.Status, Host = outModel.Value.Host, Port = port.NodePort, Configuration = outModel.Value.Configuration }); }
/// <summary> /// 新規に前処理用コンテナを作成する。 /// </summary> /// <param name="preprocessHistory">対象の前処理履歴</param> /// <returns>作成したコンテナのステータス</returns> public async Task <Result <ContainerInfo, string> > RunPreprocessingContainerAsync(PreprocessHistory preprocessHistory) { string token = await GetUserAccessTokenAsync(); if (token == null) { //トークンがない場合、結果はnull return(Result <ContainerInfo, string> .CreateErrorResult("Access denied. Failed to get token to access the cluster management system.")); } var registryMap = registryLogic.GetCurrentRegistryMap(preprocessHistory.Preprocess.ContainerRegistryId.Value); string tags = "-t " + preprocessHistory.Preprocess.Name; //生成されるデータのタグを設定 foreach (var tag in preprocessHistory.InputData.Tags) { tags += " -t " + tag; } //コンテナを起動するために必要な設定値をインスタンス化 var inputModel = new RunContainerInputModel() { ID = preprocessHistory.Id, TenantName = TenantName, LoginUser = CurrentUserInfo.Alias, //アカウントはエイリアスから指定 Name = preprocessHistory.Name, ContainerImage = registryMap.Registry.GetImagePath(preprocessHistory.Preprocess.ContainerImage, preprocessHistory.Preprocess.ContainerTag), ScriptType = "preproc", // 実行スクリプトの指定 Cpu = preprocessHistory.Cpu.Value, Memory = preprocessHistory.Memory.Value, Gpu = preprocessHistory.Gpu.Value, KqiToken = loginLogic.GenerateToken().AccessToken, KqiImage = "kamonohashi/cli:" + versionLogic.GetVersion(), LogPath = "/kqi/attach/preproc_stdout_stderr_${PREPROCESSING_ID}_${DATA_ID}.log", // 前処理履歴IDは現状ユーザーに見えないので前処理+データIDをつける NfsVolumeMounts = new List <NfsVolumeMountModel>() { // 添付ファイルを保存するディレクトリ // 前処理結果ディレクトリを前処理完了時にzip圧縮して添付するために使用 new NfsVolumeMountModel() { Name = "nfs-preproc-attach", MountPath = "/kqi/attach", SubPath = preprocessHistory.Id.ToString(), Server = CurrentUserInfo.SelectedTenant.Storage.NfsServer, ServerPath = CurrentUserInfo.SelectedTenant.PreprocContainerAttachedNfsPath, ReadOnly = false } }, ContainerSharedPath = new Dictionary <string, string>() { { "tmp", "/kqi/tmp/" }, { "input", "/kqi/input/" }, { "git", "/kqi/git/" }, { "output", "/kqi/output/" } }, EnvList = new Dictionary <string, string>() { { "DATA_ID", preprocessHistory.InputDataId.ToString() }, { "DATA_NAME", preprocessHistory.InputData.Name }, { "PREPROCESSING_ID", preprocessHistory.PreprocessId.ToString() }, { "TAGS", tags }, { "COMMIT_ID", preprocessHistory.Preprocess.RepositoryCommitId }, { "KQI_SERVER", containerOptions.WebServerUrl }, { "KQI_TOKEN", loginLogic.GenerateToken().AccessToken }, { "http_proxy", containerOptions.Proxy }, { "https_proxy", containerOptions.Proxy }, { "no_proxy", containerOptions.NoProxy }, { "HTTP_PROXY", containerOptions.Proxy }, { "HTTPS_PROXY", containerOptions.Proxy }, { "NO_PROXY", containerOptions.NoProxy }, { "COLUMNS", containerOptions.ShellColumns }, { "PYTHONUNBUFFERED", "true" }, // python実行時の標準出力・エラーのバッファリングをなくす { "LC_ALL", "C.UTF-8" }, // python実行時のエラー回避 { "LANG", "C.UTF-8" } // python実行時のエラー回避 }, EntryPoint = preprocessHistory.Preprocess.EntryPoint, ClusterManagerToken = token, RegistryTokenName = registryMap.RegistryTokenKey, IsNodePort = true }; // 前処理はGitの未指定も許可するため、その判定 if (preprocessHistory.Preprocess.RepositoryGitId != null) { long gitId = preprocessHistory.Preprocess.RepositoryGitId == -1 ? CurrentUserInfo.SelectedTenant.DefaultGitId.Value : preprocessHistory.Preprocess.RepositoryGitId.Value; var gitEndpoint = gitLogic.GetPullUrl(preprocessHistory.Preprocess.RepositoryGitId.Value, preprocessHistory.Preprocess.RepositoryName, preprocessHistory.Preprocess.RepositoryOwner); if (gitEndpoint != null) { inputModel.EnvList.Add("MODEL_REPOSITORY", gitEndpoint.FullUrl); inputModel.EnvList.Add("MODEL_REPOSITORY_URL", gitEndpoint.Url); inputModel.EnvList.Add("MODEL_REPOSITORY_TOKEN", gitEndpoint.Token); } else { //Git情報は必須にしているので、無ければエラー return(Result <ContainerInfo, string> .CreateErrorResult("Git credential is not valid.")); } } // ユーザの任意追加環境変数をマージする AddUserEnvToInputModel(preprocessHistory.OptionDic, inputModel); //使用できるノードを取得し、制約に追加 inputModel.ConstraintList = new Dictionary <string, List <string> >() { { containerOptions.ContainerLabelHostName, GetAccessibleNode() } }; if (string.IsNullOrEmpty(preprocessHistory.Partition) == false) { // パーティション指定があれば追加 inputModel.ConstraintList.Add(containerOptions.ContainerLabelPartition, new List <string> { preprocessHistory.Partition }); } var outModel = await clusterManagementService.RunContainerAsync(inputModel); if (outModel.IsSuccess == false) { return(Result <ContainerInfo, string> .CreateErrorResult(outModel.Error)); } return(Result <ContainerInfo, string> .CreateResult(new ContainerInfo() { Name = outModel.Value.Name, Status = outModel.Value.Status, Host = outModel.Value.Host, Configuration = outModel.Value.Configuration })); }