/// <summary> /// 保存配置 /// </summary> /// <returns></returns> public static string SaveConfiguration() { // Validate if (!ValidateUtil.ValidateIp(ConfigurationDto.DataSource)) { return("连接数据库的数据源地址格式有误!"); } if (!ValidateUtil.ValidateIp(ConfigurationDto.ServerIp)) { return("连接数据中心的地址格式有误!"); } if (!ValidateUtil.ValidatePort(ConfigurationDto.ServerPort)) { return("连接数据中心的端口设置有误!"); } bool configParameterBlured = false; List <string> tagNames = new List <string>(); List <string> newValues = new List <string>(); /// CloseMainwindowToExit if (CloseMainwindowToExit != ConfigurationDto.CloseMainwindowToExit) { tagNames.Add("CloseMainwindowToExit"); newValues.Add(ConfigurationDto.CloseMainwindowToExit.ToString()); CloseMainwindowToExit = ConfigurationDto.CloseMainwindowToExit; } /// DataBase Parameters if (!DataSource.Equals(ConfigurationDto.DataSource)) { configParameterBlured = true; tagNames.Add("DataSource"); newValues.Add(ConfigurationDto.DataSource); DataSource = ConfigurationDto.DataSource; } if (!InitialCatalog.Equals(ConfigurationDto.InitialCatalog)) { configParameterBlured = true; tagNames.Add("InitialCatalog"); newValues.Add(ConfigurationDto.InitialCatalog); InitialCatalog = ConfigurationDto.InitialCatalog; } if (!UserId.Equals(ConfigurationDto.UserId)) { configParameterBlured = true; tagNames.Add("UserId"); newValues.Add(ConfigurationDto.UserId); UserId = ConfigurationDto.UserId; } if (!Password.Equals(ConfigurationDto.Password)) { configParameterBlured = true; tagNames.Add("Password"); newValues.Add(EncryptionDecryption.Encrypt(ConfigurationDto.Password)); Password = ConfigurationDto.Password; } if (configParameterBlured) { ThreadPool.QueueUserWorkItem(new WaitCallback((obj) => { AppEventsManager.UpdateConnectionString(); }), null); } configParameterBlured = false; /// Data Server Parameter if (!ServerIp.Equals(ConfigurationDto.ServerIp)) { configParameterBlured = true; tagNames.Add("DataServerIP"); newValues.Add(ConfigurationDto.ServerIp); ServerIp = ConfigurationDto.ServerIp; } if (!ServerPort.Equals(ConfigurationDto.ServerPort)) { configParameterBlured = true; tagNames.Add("DataServerPort"); newValues.Add(ConfigurationDto.ServerPort); ServerPort = ConfigurationDto.ServerPort; } if (configParameterBlured) { ThreadPool.QueueUserWorkItem(new WaitCallback((obj) => { AppEventsManager.UpdateDataServer(); }), null); } configParameterBlured = false; if (OuterDiameterUpper != ConfigurationDto.OuterDiameterUpper) { tagNames.Add("Upper"); configParameterBlured = true; newValues.Add("" + ConfigurationDto.OuterDiameterUpper); OuterDiameterUpper = ConfigurationDto.OuterDiameterUpper; } if (OuterDiameterLower != ConfigurationDto.OuterDiameterLower) { tagNames.Add("Lower"); configParameterBlured = true; newValues.Add("" + ConfigurationDto.OuterDiameterLower); OuterDiameterLower = ConfigurationDto.OuterDiameterLower; } if (configParameterBlured) { ThreadPool.QueueUserWorkItem(new WaitCallback((obj) => { AppEventsManager.UpdateOuterDiameterUpperAndLower(); }), null); } if (tagNames.Count > 0) { XmlUtil.ChangeXmlByTagNames(AppConfig, tagNames, newValues); } return(null); }
/// <summary> /// /// </summary> /// <param name="publishFolder">deploy文件目录</param> /// <param name="isrollBack"></param> /// <param name="isDefaultDockfile">上传的时候没有DockerFile要创建</param> public void DoDockerCommand(string publishFolder, bool isrollBack = false, bool isDefaultDockfile = false, string publishName = "publish") { string port = string.Empty; string server_port = string.Empty; if (!publishFolder.EndsWith("/")) { publishFolder = publishFolder + "/"; } //先查看本地是否有dockerFile var dockFilePath = publishFolder + "Dockerfile"; var dockFilePath2 = $"{(string.IsNullOrEmpty(publishName)?"": publishName+"/")}Dockerfile"; // ReSharper disable once SimplifyConditionalTernaryExpression var isExistDockFile = isDefaultDockfile? false: _sftpClient.Exists(dockFilePath2); //如果本地存在dockerfile 那么就根据此创建image //如果不存在的话 就根据当前的netcore sdk的版本 进行创建相对应的 dockfile if (!isExistDockFile) { if (isrollBack) { _logger($"dockerFile is not exist: {dockFilePath}", NLog.LogLevel.Error); return; } if (!isDefaultDockfile) { var createDockerFileResult = CreateDockerFile(dockFilePath); if (!createDockerFileResult) { return; } } } else { //如果项目中存在dockerFile 那么check 该DockerFile的Expose是否配置了 没有配置就报错 try { var dockerFileText = _sftpClient.ReadAllText(dockFilePath); if (string.IsNullOrEmpty(dockerFileText)) { _logger($"dockerFile is empty: {dockFilePath}", NLog.LogLevel.Error); return; } var newPortA = dockerFileText.Split(new string[] { "EXPOSE " }, StringSplitOptions.None); if (newPortA.Length != 2) { _logger($"EXPOSE param in dockerFile is empty: {dockFilePath}", NLog.LogLevel.Error); return; } var newPort = string.Empty; foreach (var item in newPortA[1].Trim()) { if (Char.IsDigit(item)) { newPort += item; } else { break; } } if (string.IsNullOrEmpty(newPort)) { _logger($"EXPOSE in dockerFile is invalid: {dockFilePath}", NLog.LogLevel.Error); return; } else { if (!string.IsNullOrEmpty(NetCorePort) && !this.ContainerPort.Equals(newPort)) { _logger($"EXPOSE in dockerFile is defined,will use【{newPort}】replace【{ContainerPort}】", NLog.LogLevel.Warn); } else { _logger($"EXPOSE in dockerFile is : 【{newPort}】", NLog.LogLevel.Info); } } port = newPort; var volumeInDockerFile = string.Empty; var volumeExist = dockerFileText.Split(new string[] { volumeProfix }, StringSplitOptions.None); if (volumeExist.Length == 2) { var temp2 = volumeExist[1].Split('@'); if (temp2.Length == 2) { volumeInDockerFile = temp2[0]; } } if (!string.IsNullOrEmpty(volumeInDockerFile)) { //dockerFIle里面有配置 volume if (!string.IsNullOrEmpty(Volume) && !Volume.Equals(volumeInDockerFile)) { _logger($"Volume in dockerFile is defined,will use【{volumeInDockerFile}】replace【{Volume}】", NLog.LogLevel.Warn); } else { _logger($"Volume in dockerFile is : 【{volumeInDockerFile}】", NLog.LogLevel.Info); } Volume = volumeInDockerFile; } var serverPostDockerFile = string.Empty; var serverPostDockerFileExist = dockerFileText.Split(new string[] { serverPortProfix }, StringSplitOptions.None); if (serverPostDockerFileExist.Length == 2) { var temp2 = serverPostDockerFileExist[1].Split('@'); if (temp2.Length > 0) { serverPostDockerFile = temp2[0]; } } if (!string.IsNullOrEmpty(serverPostDockerFile)) { //dockerFIle里面有配置 ServerPort if (!string.IsNullOrEmpty(NetCorePort) && !ServerPort.Equals(serverPostDockerFile)) { _logger($"ServerPort in dockerFile is defined,will use【{serverPostDockerFile}】replace【{ServerPort}】", NLog.LogLevel.Warn); } else { _logger($"ServerPort in dockerFile is : 【{serverPostDockerFile}】", NLog.LogLevel.Info); } server_port = serverPostDockerFile; } else { server_port = port; } } catch (Exception) { _logger($"Get EXPOSE param in dockerFile fail: {dockFilePath}", NLog.LogLevel.Error); return; } } //执行docker build 生成一个镜像 var dockerBuildResult = RunSheell($"docker build --no-cache --rm -t {PorjectName}:{ClientDateTimeFolderName} -f {dockFilePath} {publishFolder} "); if (!dockerBuildResult) { _logger($"build image fail", NLog.LogLevel.Error); return; } var continarName = "d_" + PorjectName; //先发送退出命令 //https://stackoverflow.com/questions/40742192/how-to-do-gracefully-shutdown-on-dotnet-with-docker SshCommand r1 = null; try { r1 = _sshClient.RunCommand($"docker stop -t 10 {continarName}"); if (r1.ExitStatus == 0) { _logger($"docker stop -t 10 {continarName}", LogLevel.Info); } Thread.Sleep(5000); } catch (Exception) { //ignore } try { //查看容器有没有在runing 如果有就干掉它 r1 = _sshClient.RunCommand($"docker rm -f {continarName}"); if (r1.ExitStatus == 0) { _logger($"docker rm -f {continarName}", LogLevel.Info); } } catch (Exception) { //ignore } if (string.IsNullOrEmpty(port)) { port = ContainerPort; } if (string.IsNullOrEmpty(server_port)) { server_port = ServerPort; } string volume = GetVolume(); // 根据image启动一个容器 var dockerRunRt = RunSheell($"docker run --name {continarName}{volume} -d --restart=always -p {server_port}:{port} {PorjectName}:{ClientDateTimeFolderName}"); if (!dockerRunRt) { _logger($"docker run fail", NLog.LogLevel.Error); return; } //把旧的image给删除 r1 = _sshClient.RunCommand("docker images --format '{{.Repository}}:{{.Tag}}:{{.ID}}' | grep '^" + PorjectName + ":'"); if (r1.ExitStatus == 0 && !string.IsNullOrEmpty(r1.Result)) { var deleteImageArr = r1.Result.Split('\n'); var clearOldImages = false; foreach (var imageName in deleteImageArr) { if (imageName.StartsWith($"{PorjectName}:{ClientDateTimeFolderName}:")) { //当前版本 continue; } var imageArr = imageName.Split(':'); if (imageArr.Length == 3) { var r2 = _sshClient.RunCommand($"docker rmi {imageArr[2]}"); if (r2.ExitStatus == 0) { if (!clearOldImages) { _logger($"start to clear old images of name:{PorjectName}", LogLevel.Info); clearOldImages = true; } _logger($"docker rmi {imageArr[2]} [{imageName}]", LogLevel.Info); } } } } try { //查看是否有<none>的image 把它删掉 因为我们创建image的时候每次都会覆盖所以会产生一些没有的image _sshClient.RunCommand($"if docker images -f \"dangling=true\" | grep ago --quiet; then docker rmi -f $(docker images -f \"dangling=true\" -q); fi"); } catch (Exception) { //igore } ClearOldHistroy(); }
/// <summary> /// /// </summary> /// <param name="publishFolder">deploy文件目录</param> /// <param name="isrollBack"></param> /// <param name="isDefaultDockfile">上传的时候没有DockerFile要创建</param> public bool DoDockerCommand(string publishFolder, bool isrollBack = false, bool isDefaultDockfile = false, string publishName = "publish", string fromFolder = null) { string port = string.Empty; string server_port = string.Empty; if (!publishFolder.EndsWith("/")) { publishFolder = publishFolder + "/"; } //先查看本地是否有dockerFile var dockFilePath = publishFolder + "Dockerfile"; var dockFilePath2 = $"{(string.IsNullOrEmpty(publishName) ? "" : publishName + "/")}Dockerfile"; // ReSharper disable once SimplifyConditionalTernaryExpression var isExistDockFile = isDefaultDockfile ? false : _sftpClient.Exists(dockFilePath2); //如果本地存在dockerfile 那么就根据此创建image //如果不存在的话 就根据当前的netcore sdk的版本 进行创建相对应的 dockfile if (!isExistDockFile) { if (isrollBack) { _logger($"dockerFile is not exist: {dockFilePath}", LogLevel.Error); return(false); } if (!isDefaultDockfile) { var createDockerFileResult = CreateDockerFile(dockFilePath); if (!createDockerFileResult) { return(false); } } } else { //如果项目中存在dockerFile 那么check 该DockerFile的Expose是否配置了 没有配置就报错 try { var dockerFileText = _sftpClient.ReadAllText(dockFilePath); if (string.IsNullOrEmpty(dockerFileText)) { _logger($"dockerFile is empty: {dockFilePath}", LogLevel.Error); return(false); } var needAddPort = false; var newPort = string.Empty; var newPortA = dockerFileText.Split(new string[] { "EXPOSE " }, StringSplitOptions.None); if (newPortA.Length != 2) { needAddPort = true; } else { if (!newPortA[0].EndsWith("#")) { foreach (var item in newPortA[1].Trim()) { if (Char.IsDigit(item)) { newPort += item; } else { break; } } } } if (string.IsNullOrEmpty(newPort)) { needAddPort = true; } else { if (!string.IsNullOrEmpty(NetCorePort) && !this.ContainerPort.Equals(newPort)) { _logger($"EXPOSE in dockerFile is defined,will use【{newPort}】replace【{ContainerPort}】", LogLevel.Warning); } else { _logger($"EXPOSE in dockerFile is : 【{newPort}】", LogLevel.Info); } } //如果dockfile里面没有配置EXPOST 就用界面上提供的 port = needAddPort ? ContainerPort : newPort; var volumeInDockerFile = string.Empty; var volumeExist = dockerFileText.Split(new string[] { volumeProfix }, StringSplitOptions.None); if (volumeExist.Length == 2) { var temp2 = volumeExist[1].Split('@'); if (temp2.Length > 0) { volumeInDockerFile = temp2[0]; } } if (!string.IsNullOrEmpty(volumeInDockerFile)) { //dockerFIle里面有配置 volume if (!string.IsNullOrEmpty(Volume) && !Volume.Equals(volumeInDockerFile)) { _logger($"Volume in dockerFile is defined,will use【{volumeInDockerFile}】replace【{Volume}】", LogLevel.Warning); } else { _logger($"Volume in dockerFile is : 【{volumeInDockerFile}】", LogLevel.Info); } Volume = volumeInDockerFile; } var serverPostDockerFile = string.Empty; var serverPostDockerFileExist = dockerFileText.Split(new string[] { serverPortProfix }, StringSplitOptions.None); if (serverPostDockerFileExist.Length == 2) { var temp2 = serverPostDockerFileExist[1].Split('@'); if (temp2.Length > 0) { serverPostDockerFile = temp2[0]; } } if (!string.IsNullOrEmpty(serverPostDockerFile)) { //dockerFIle里面有配置 ServerPort if (!string.IsNullOrEmpty(NetCorePort) && !ServerPort.Equals(serverPostDockerFile)) { _logger($"ServerPort in dockerFile is defined,will use【{serverPostDockerFile}】replace【{ServerPort}】", LogLevel.Warning); } else { _logger($"ServerPort in dockerFile is : 【{serverPostDockerFile}】", LogLevel.Info); } server_port = serverPostDockerFile; } else { server_port = needAddPort ? ServerPort : port; } if (!string.IsNullOrEmpty(NetCoreEnvironment) || needAddPort) { var allLines = _sftpClient.ReadAllLines(dockFilePath).ToList(); var entryPointIndex = 0; var haveEnv = false; for (int i = 0; i < allLines.Count; i++) { var line = allLines[i]; if (line.Trim().StartsWith("ENTRYPOINT")) { entryPointIndex = i; } if (line.Trim().StartsWith("ENV ASPNETCORE_ENVIRONMENT")) { haveEnv = true; } } if (entryPointIndex > 0) { var add = false; if (needAddPort) { add = true; allLines.Insert(entryPointIndex, "EXPOSE " + port); _logger($"Add EXPOSE " + port + $" to dockerFile : 【{dockFilePath}】", LogLevel.Info); } if (!haveEnv && !string.IsNullOrEmpty(NetCoreEnvironment)) { add = true; allLines.Insert(entryPointIndex, "ENV ASPNETCORE_ENVIRONMENT " + NetCoreEnvironment); _logger($"Add ENV ASPNETCORE_ENVIRONMENT " + NetCoreEnvironment + $" to dockerFile : 【{dockFilePath}】", LogLevel.Info); } if (add) { //没有发现包含环境变量 就添加进去 _sshClient.RunCommand($"set -e;cd ~;\\rm -rf \"{dockFilePath}\";"); using (var writer = _sftpClient.CreateText(dockFilePath)) { foreach (var line in allLines) { writer.WriteLine(line); } //发布的时候界面上有填volume 也存在dockerfile 要记录到dockerfile中 不然回滚的时候就没了 if (string.IsNullOrEmpty(volumeInDockerFile) && !string.IsNullOrEmpty(this.Volume)) { writer.WriteLine(volumeProfix + this.Volume + "@"); _logger(volumeProfix + this.Volume + "@", LogLevel.Info); } writer.Flush(); } } } } else if (string.IsNullOrEmpty(volumeInDockerFile) && !string.IsNullOrEmpty(this.Volume)) { //发布的时候界面上有填volume 也存在dockerfile 要记录到dockerfile中 不然回滚的时候就没了 var allLines = _sftpClient.ReadAllLines(dockFilePath).ToList(); _sshClient.RunCommand($"set -e;cd ~;\\rm -rf \"{dockFilePath}\";"); using (var writer = _sftpClient.CreateText(dockFilePath)) { foreach (var line in allLines) { writer.WriteLine(line); } writer.WriteLine(volumeProfix + this.Volume + "@"); _logger(volumeProfix + this.Volume + "@", LogLevel.Info); writer.Flush(); } } if (!string.IsNullOrEmpty(fromFolder)) { //需要将修改过的DockerFile 移动到 发布文件夹的publish 目录下 不然会导致回滚的时候失败 var command = $"\\cp -rf {dockFilePath} {fromFolder}"; _logger($"Update DockerFile 【{command}】", LogLevel.Warning); _sshClient.RunCommand(command); } } catch (Exception ex) { _logger($"parse param in dockerFile fail: {dockFilePath},err:{ex.Message}", LogLevel.Error); return(false); } } //执行docker build 生成一个镜像 var dockerBuildResult = RunSheell($"docker build --no-cache --rm -t {PorjectName}:{ClientDateTimeFolderName} -f {dockFilePath} {publishFolder} "); if (!dockerBuildResult) { _logger($"build image fail", LogLevel.Error); return(false); } var continarName = "d_" + PorjectName; //先发送退出命令 //https://stackoverflow.com/questions/40742192/how-to-do-gracefully-shutdown-on-dotnet-with-docker SshCommand r1 = null; try { r1 = _sshClient.RunCommand($"docker stop -t 10 {continarName}"); if (r1.ExitStatus == 0) { _logger($"docker stop -t 10 {continarName}", LogLevel.Info); } Thread.Sleep(5000); } catch (Exception) { //ignore } try { //查看容器有没有在runing 如果有就干掉它 r1 = _sshClient.RunCommand($"docker rm -f {continarName}"); if (r1.ExitStatus == 0) { _logger($"docker rm -f {continarName}", LogLevel.Info); } } catch (Exception) { //ignore } if (string.IsNullOrEmpty(port)) { port = ContainerPort; } if (string.IsNullOrEmpty(server_port)) { server_port = ServerPort; } string volume = GetVolume(); // 根据image启动一个容器 var dockerRunRt = RunSheell($"docker run --name {continarName}{volume} -d --restart=always -p {server_port}:{port} {PorjectName}:{ClientDateTimeFolderName}"); if (!dockerRunRt) { _logger($"docker run fail", LogLevel.Error); return(false); } //把旧的image给删除 r1 = _sshClient.RunCommand("docker images --format '{{.Repository}}:{{.Tag}}:{{.ID}}' | grep '^" + PorjectName + ":'"); if (r1.ExitStatus == 0 && !string.IsNullOrEmpty(r1.Result)) { var deleteImageArr = r1.Result.Split('\n'); var clearOldImages = false; foreach (var imageName in deleteImageArr) { if (imageName.StartsWith($"{PorjectName}:{ClientDateTimeFolderName}:")) { //当前版本 continue; } var imageArr = imageName.Split(':'); if (imageArr.Length == 3) { var r2 = _sshClient.RunCommand($"docker rmi {imageArr[2]}"); if (r2.ExitStatus == 0) { if (!clearOldImages) { _logger($"start to clear old images of name:{PorjectName}", LogLevel.Info); clearOldImages = true; } _logger($"docker rmi {imageArr[2]} [{imageName}]", LogLevel.Info); } } } } try { //查看是否有<none>的image 把它删掉 因为我们创建image的时候每次都会覆盖所以会产生一些没有的image _sshClient.RunCommand($"if docker images -f \"dangling=true\" | grep ago --quiet; then docker rmi -f $(docker images -f \"dangling=true\" -q); fi"); } catch (Exception) { //igore } ClearOldHistroy(); return(true); }