public async Task CreateAndStartContainerWaitForExit(SessionHostsStartInfo startParameters) { IList <PortMapping> portMappings = GetPortMappings(startParameters, 0); if (startParameters.SessionHostType == SessionHostType.Container) { foreach (PortMapping mapping in portMappings) { _logger.LogInformation( $"{mapping.GamePort.Name} ({mapping.GamePort.Protocol}): Local port {mapping.NodePort} mapped to container port {mapping.GamePort.Number} "); } } DownloadAndExtractAllAssets(startParameters); DownloadGameCertificates(startParameters); ISessionHostRunner sessionHostRunner = _sessionHostRunnerFactory.CreateSessionHostRunner(startParameters.SessionHostType, _vmConfiguration, _logger); // RetrieveResources does a docker pull // If we're running Linux containers on Windows, we might want to pull image from ACR first to save the image on local. // In this case, you need to simply change the value to true for forcePullFromAcrOnLinuxContainersOnWindows in MultiplayerSettings.json // so if this image is not locally built, docker create will do a docker pull first // In another case, we might have built the image locally but tagged with a fake registry name (e.g. myacr.io/mygame:0.1), // Then make sure to change the value to false if you want to use the image from local. if (Globals.GameServerEnvironment == GameServerEnvironment.Windows || Globals.Settings.ForcePullFromAcrOnLinuxContainersOnWindows) { await sessionHostRunner.RetrieveResources(startParameters); } NoOpSessionHostManager sessionHostManager = new NoOpSessionHostManager(); SessionHostInfo sessionHostInfo = await sessionHostRunner.CreateAndStart(0, new GameResourceDetails { SessionHostsStartInfo = startParameters }, sessionHostManager); if (sessionHostInfo == null) { return; } string typeSpecificId = sessionHostInfo.TypeSpecificId; _logger.LogInformation("Waiting for heartbeats from the game server....."); await sessionHostRunner.WaitOnServerExit(typeSpecificId).ConfigureAwait(false); string logFolder = Path.Combine(Globals.VmConfiguration.VmDirectories.GameLogsRootFolderVm, sessionHostInfo.LogFolderId); await sessionHostRunner.CollectLogs(typeSpecificId, logFolder, sessionHostManager); await sessionHostRunner.TryDelete(typeSpecificId); }
public SessionHostInfo AddNewSessionHost(string sessionHostId, string assignmentId, int instanceNumber, string logFolderId, SessionHostType type = SessionHostType.Container) { var sessionHostInfo = new SessionHostInfo(sessionHostId, assignmentId, instanceNumber, logFolderId, type) { SessionHostHeartbeatRequest = new SessionHostHeartbeatInfo() { AssignmentId = assignmentId, CurrentGameState = SessionHostStatus.PendingHeartbeat, LastStateTransitionTimeUtc = DateTime.UtcNow } }; SessionHostsMap.TryAdd(sessionHostId, sessionHostInfo); return(sessionHostInfo); }
public override Task <SessionHostInfo> CreateAndStart(int instanceNumber, GameResourceDetails gameResourceDetails, ISessionHostManager sessionHostManager) { SessionHostsStartInfo sessionHostStartInfo = gameResourceDetails.SessionHostsStartInfo; string sessionHostUniqueId = Guid.NewGuid().ToString("D"); string logFolderPathOnVm = Path.Combine(_vmConfiguration.VmDirectories.GameLogsRootFolderVm, sessionHostUniqueId); _systemOperations.CreateDirectory(logFolderPathOnVm); // Create the dumps folder as a subfolder of the logs folder string dumpFolderPathOnVm = Path.Combine(logFolderPathOnVm, VmDirectories.GameDumpsFolderName); _systemOperations.CreateDirectory(dumpFolderPathOnVm); ISessionHostConfiguration sessionHostConfiguration = new SessionHostProcessConfiguration(_vmConfiguration, _logger, _systemOperations, sessionHostStartInfo); string configFolderPathOnVm = _vmConfiguration.GetConfigRootFolderForSessionHost(instanceNumber); _systemOperations.CreateDirectory(configFolderPathOnVm); ProcessStartInfo processStartInfo = new ProcessStartInfo(); (string executableFileName, string arguments) = GetExecutableAndArguments(sessionHostStartInfo, instanceNumber); processStartInfo.FileName = executableFileName; processStartInfo.Arguments = arguments; processStartInfo.WorkingDirectory = sessionHostStartInfo.GameWorkingDirectory ?? Path.GetDirectoryName(executableFileName); processStartInfo.Environment.AddRange(sessionHostConfiguration.GetEnvironmentVariablesForSessionHost(instanceNumber, sessionHostUniqueId, sessionHostManager.VmAgentSettings)); _logger.LogInformation($"Starting process for session host with instance number {instanceNumber} and process info: FileName - {executableFileName}, Args - {arguments}."); SessionHostInfo sessionHost = sessionHostManager.AddNewSessionHost(sessionHostUniqueId, sessionHostStartInfo.AssignmentId, instanceNumber, sessionHostUniqueId, SessionHostType.Process); sessionHostConfiguration.Create(instanceNumber, sessionHostUniqueId, GetVmAgentIpAddress(), _vmConfiguration, sessionHostUniqueId); try { string processId = _processWrapper.Start(processStartInfo).ToString(); sessionHostManager.UpdateSessionHostTypeSpecificId(sessionHostUniqueId, processId); _logger.LogInformation($"Started process for session host. Instance Number: {instanceNumber}, UniqueId: {sessionHostUniqueId}, ProcessId: {processId}"); } catch (Exception exception) { _logger.LogException($"Failed to start process based host with instance number {instanceNumber}", exception); sessionHostManager.RemoveSessionHost(sessionHostUniqueId); sessionHost = null; } return(Task.FromResult(sessionHost)); }
public async Task CreateAndStartContainerWaitForExit(SessionHostsStartInfo startParameters) { IList <PortMapping> portMappings = GetPortMappings(startParameters, 0); if (startParameters.SessionHostType == SessionHostType.Container) { foreach (PortMapping mapping in portMappings) { _logger.LogInformation( $"{mapping.GamePort.Name} ({mapping.GamePort.Protocol}): Local port {mapping.NodePort} mapped to container port {mapping.GamePort.Number} "); } } DownloadAndExtractAllAssets(startParameters); DownloadGameCertificates(startParameters); ISessionHostRunner sessionHostRunner = _sessionHostRunnerFactory.CreateSessionHostRunner(startParameters.SessionHostType, _vmConfiguration, _logger); // RetrieveResources does a docker pull // if we're running Linux containers on Windows we do not want that, since we might // have built the image locally but tagged with a fake registry name (e.g. myacr.io/mygame:0.1) // docker pull will try to connect to myacr.io and fail // if this image is not locally built, docker create (executed next) will do a docker pull first if (Globals.GameServerEnvironment == GameServerEnvironment.Windows) { await sessionHostRunner.RetrieveResources(startParameters); } NoOpSessionHostManager sessionHostManager = new NoOpSessionHostManager(); SessionHostInfo sessionHostInfo = await sessionHostRunner.CreateAndStart(0, new GameResourceDetails { SessionHostsStartInfo = startParameters }, sessionHostManager); string containerId = sessionHostInfo.TypeSpecificId; _logger.LogInformation("Waiting for heartbeats from the game server....."); await sessionHostRunner.WaitOnServerExit(containerId).ConfigureAwait(false); string logFolder = Path.Combine(Globals.VmConfiguration.VmDirectories.GameLogsRootFolderVm, sessionHostInfo.LogFolderId); await sessionHostRunner.CollectLogs(containerId, logFolder, sessionHostManager); await sessionHostRunner.TryDelete(containerId); }
/// <summary> /// Creates and starts a container, assigning <paramref name="instanceNumber"/> to it. /// </summary> /// <param name="instanceNumber"> /// An instance number associated with a container. It is used to map assets folder to the container /// and then re-use for container recycling. /// </param> /// <returns>A <see cref="Task"/>.</returns> public async Task <SessionHostInfo> CreateAndStart(int instanceNumber, GameResourceDetails gameResourceDetails, ISessionHostManager sessionHostManager) { // The current Docker client doesn't yet allow specifying a local name for the image. // It is stored with as the remote path name. Thus, the parameter to CreateAndStartContainers // is the same as the remote image path. SessionHostsStartInfo sessionHostStartInfo = gameResourceDetails.SessionHostsStartInfo; ContainerImageDetails imageDetails = sessionHostStartInfo.ImageDetails; string imageName = $"{imageDetails.Registry}/{imageDetails.ImageName}:{imageDetails.ImageTag ?? "latest"}"; // The game containers need a unique folder to write their logs. Ideally, // we would specify the containerId itself as the subfolder. However, we have to // specify volume bindings before docker gives us the container id, so using // a random guid here instead string logFolderId = _systemOperations.NewGuid().ToString("D"); ISessionHostConfiguration sessionHostConfiguration = new SessionHostContainerConfiguration(_vmConfiguration, _logger, _systemOperations, _dockerClient, sessionHostStartInfo); IList <PortMapping> portMappings = sessionHostConfiguration.GetPortMappings(instanceNumber); List <string> environmentValues = sessionHostConfiguration.GetEnvironmentVariablesForSessionHost(instanceNumber, logFolderId) .Select(x => $"{x.Key}={x.Value}").ToList(); string dockerId = await CreateContainer( imageName, environmentValues, GetVolumeBindings(sessionHostStartInfo, instanceNumber, logFolderId), portMappings, GetStartGameCmd(sessionHostStartInfo), sessionHostStartInfo.HostConfigOverrides, GetGameWorkingDir(sessionHostStartInfo)); SessionHostInfo sessionHost = sessionHostManager.AddNewSessionHost(dockerId, sessionHostStartInfo.AssignmentId, instanceNumber, logFolderId); // https://docs.docker.com/docker-for-windows/networking/ string agentIPaddress = sessionHostManager.LinuxContainersOnWindows ? "host.docker.internal" : GetVmAgentIpAddress(); sessionHostConfiguration.Create(instanceNumber, dockerId, agentIPaddress, _vmConfiguration, logFolderId); // on LinuxContainersForWindows, VMAgent will run in a Windows environment // but we want the Linux directory separator char if (sessionHostManager.LinuxContainersOnWindows) { string configFilePath = Path.Combine(_vmConfiguration.GetConfigRootFolderForSessionHost(instanceNumber), VmDirectories.GsdkConfigFilename); File.WriteAllText(configFilePath, File.ReadAllText(configFilePath). Replace($"{_vmConfiguration.VmDirectories.GameLogsRootFolderContainer}\\\\", $"{_vmConfiguration.VmDirectories.GameLogsRootFolderContainer}/")); } try { await StartContainer(dockerId); _logger.LogInformation($"Started container {dockerId}, with assignmentId {sessionHostStartInfo.AssignmentId}, instance number {instanceNumber}, and logFolderId {logFolderId}"); } catch (Exception exception) { _logger.LogException($"Failed to start container based host with instance number {instanceNumber}", exception); sessionHostManager.RemoveSessionHost(dockerId); sessionHost = null; } return(sessionHost); }