about_Remote_Jobs — 원격 컴퓨터에서 배경 작업 실행하기

about_Remote_Jobs — 원격 컴퓨터에서 배경 작업 실행하기

PowerShell로 원격 컴퓨터에서 백그라운드 작업(background job)을 실행하는 방법을 다루는 문서예요. 로컬에서 백그라운드 작업을 돌려 본 분이라면, 원리 자체는 거의 같다는 걸 먼저 말씀드릴게요. 차이가 있다면 작업이 "어디서" 도는지와, 그 결과를 "어디로" 가져오는지 정도예요. 이 문서를 따라가다 보면 원격 작업의 세 가지 실행 방식과, 그중에서 특히 결과를 어디에 보관하느냐에 따른 차이를 자연스럽게 정리할 수 있을 거예요.

출처: about_Remote_Jobs (Microsoft Learn)

본문

간단한 설명

원격 컴퓨터에서 백그라운드 작업을 실행하는 방법을 설명합니다.

자세한 설명

PowerShell은 작업(job)을 통해 명령과 스크립트를 동시에 실행해요. PowerShell이 동시성을 지원하려고 제공하는 작업 유형은 세 가지가 있어요.

  • RemoteJob — 명령과 스크립트가 원격 세션에서 실행돼요.
  • BackgroundJob — 명령과 스크립트가 로컬 컴퓨터의 별도 프로세스에서 실행돼요. 자세한 내용은 about_Jobs를 참고하세요.
  • PSTaskJob 또는 ThreadJob — 명령과 스크립트가 로컬 컴퓨터의 같은 프로세스 안에서 별도 스레드로 실행돼요. 자세한 내용은 about_Thread_Jobs를 참고하세요.

별도의 컴퓨터나 별도의 프로세스에서 스크립트를 실행하면 격리(isolation)가 잘 돼요. 원격 작업에서 에러가 나도, 다른 실행 중인 작업이나 작업을 시작한 부모(parent) 세션에는 영향을 주지 않거든요. 다만 원격 계층(remoting layer)은 객체 직렬화(object serialization) 같은 부담이 추가돼요. 모든 객체는 부모 세션과 원격(작업) 세션 사이를 오갈 때 직렬화되고 역직렬화되는데, 크고 복잡한 데이터 객체를 직렬화하면 컴퓨팅·메모리 자원을 많이 쓰고 네트워크로 많은 데이터를 옮기게 돼요.

중요

작업을 만든 부모 세션은 작업 상태를 계속 주시하고 파이프라인 데이터를 수집해요. 작업이 완료된 상태에 도달하면 부모 프로세스가 작업 자식 프로세스를 종료해요. 부모 세션이 종료되면, 실행 중인 모든 자식 작업도 그 자식 프로세스와 함께 종료돼요.

이 상황을 우회하는 방법은 두 가지가 있어요.

  1. Invoke-Command를 사용해 연결이 끊긴(disconnected) 세션에서 실행되는 작업을 만들기. 이 문서의 "분리 프로세스로 실행하기" 섹션을 참고하세요.
  2. 작업 대신 Start-Process로 새 프로세스를 만들기. 자세한 내용은 Start-Process를 참고하세요.

원격 작업

원격 컴퓨터에서 작업을 실행하는 방법은 세 가지가 있어요.

  • 원격 컴퓨터에서 대화형 세션을 시작한 뒤, 그 대화형 세션 안에서 작업 시작하기. 절차는 로컬 작업을 실행하는 것과 같지만, 모든 동작이 원격 컴퓨터에서 이뤄져요.
  • 결과를 로컬 컴퓨터로 되돌려 주는 원격 작업 실행하기. 여러 작업의 결과를 모아서 로컬 컴퓨터의 한 곳에서 관리하고 싶을 때 이 방법을 써요.
  • 결과를 원격 컴퓨터에 유지하는 원격 작업 실행하기. 작업 데이터를 원래 컴퓨터에 더 안전하게 보관하고 싶을 때 이 방법을 써요.

대화형 세션에서 작업 시작하기

원격 컴퓨터와 대화형 세션을 시작한 뒤, 그 세션 안에서 작업을 시작할 수 있어요. 대화형 세션에 대한 자세한 내용은 about_RemoteEnter-PSSession을 참고하세요.

대화형 세션에서 작업을 시작하는 절차는 로컬 컴퓨터에서 백그라운드 작업을 시작하는 절차와 거의 같아요. 다만 모든 동작이 로컬 컴퓨터가 아니라 원격 컴퓨터에서 이뤄진다는 점만 다르죠.

  1. Enter-PSSession cmdlet으로 원격 컴퓨터와 대화형 세션을 시작해요. Enter-PSSession의 ComputerName 매개 변수로 대화형 세션용 임시 연결을 만들 수도 있고, Session 매개 변수로 PowerShell 세션(PSSession) 안에서 대화형 세션을 실행할 수도 있어요.

    다음 명령은 Server01 컴퓨터에서 대화형 세션을 시작해요.

    C:\PS> Enter-PSSession -ComputerName Server01
    

    명령 프롬프트가 바뀌면서 지금 Server01 컴퓨터에 연결되어 있다는 걸 보여줘요.

    Server01\C:>
    
  2. 세션 안에서 원격 작업을 시작하려면 Start-Job cmdlet을 써요. 다음 명령은 Server01 컴퓨터의 Windows PowerShell 이벤트 로그에서 이벤트를 가져오는 원격 작업을 실행해요. Start-Job cmdlet은 작업을 나타내는 객체를 반환해요.

    이 명령은 작업 객체를 $job 변수에 저장해요.

    Server01\C:> $job = Start-Job -ScriptBlock { Get-EventLog "Windows PowerShell" }
    

    작업이 실행되는 동안 대화형 세션에서 다른 명령을, 다른 작업까지도 실행할 수 있어요. 단, 작업이 완료될 때까지 대화형 세션을 계속 열어 두어야 해요. 세션을 끝내면 작업이 중단되고 결과가 사라져요.

  3. 작업이 완료됐는지 확인하려면 $job 변수의 값을 표시하거나 Get-Job cmdlet으로 작업을 가져와요. 다음 명령은 Get-Job cmdlet으로 작업을 표시해요.

    Server01\C:> Get-Job $job
    
    SessionId  Name  State      HasMoreData  Location   Command
    ---------  ----  -----      -----------  --------   -------
    1          Job1  Complete   True         localhost  Get-EventLog "Windows...
    

    Get-Job 출력을 보면 작업이 "localhost" 컴퓨터에서 실행 중이라고 나와요. 작업이 시작된 컴퓨터(여기서는 Server01)에서 그대로 실행되고 있으니까요.

  4. 작업 결과를 가져오려면 Receive-Job cmdlet을 써요. 결과를 대화형 세션에 표시하거나 원격 컴퓨터의 파일로 저장할 수 있어요. 다음 명령은 $job 변수의 작업 결과를 가져와요. 리디렉션 연산자(>)를 사용해 작업 결과를 Server01 컴퓨터의 PsLog.txt 파일로 저장하죠.

    Server01\C:> Receive-Job $job > C:\logs\PsLog.txt
    
  5. 대화형 세션을 끝내려면 Exit-PSSession cmdlet을 써요. 명령 프롬프트가 바뀌면서 로컬 컴퓨터의 원래 세션으로 돌아왔다는 걸 보여줘요.

    Server01\C:> Exit-PSSession
    C:\PS>
    
  6. Server01 컴퓨터의 PsLog.txt 파일 내용을 아무 때나 보려면 대화형 세션을 다시 시작하거나 원격 명령을 실행하면 돼요. 이런 종류의 명령은 PsLog.txt 파일의 데이터를 여러 명령으로 조사하고 관리하고 싶을 때 쓸 수 있으니, PSSession(영구 연결)에서 실행하는 게 좋아요. PSSession에 대한 자세한 내용은 about_PSSessions를 참고하세요.

    다음 명령들은 New-PSSession cmdlet으로 Server01 컴퓨터에 연결된 PSSession을 만들고, Invoke-Command cmdlet으로 그 PSSession 안에서 Get-Content 명령을 실행해 파일 내용을 보여줘요.

    $s = New-PSSession -ComputerName Server01
    Invoke-Command -Session $s -ScriptBlock {
      Get-Content C:\logs\pslog.txt}
    

결과를 로컬 컴퓨터로 되돌려 주는 원격 작업 시작하기 (AsJob)

원격 컴퓨터에서 작업을 시작하되 그 명령 결과를 로컬 컴퓨터로 되돌려 주려면, Invoke-Command 같은 cmdlet의 AsJob 매개 변수를 써요.

AsJob 매개 변수를 쓰면 작업이 원격 컴퓨터에서 실행되긴 하지만, 작업 객체는 실제로 로컬 컴퓨터에 만들어져요. 작업이 완료되면 결과가 로컬 컴퓨터로 반환되죠.

Job 명사를 포함한 cmdlet(Job cmdlet)을 사용하면 어떤 cmdlet이 만들었든 모든 작업을 관리할 수 있어요. AsJob 매개 변수가 있는 cmdlet 중에는 PowerShell 원격을 사용하지 않는 것도 많아서, 원격이 구성되지 않았거나 원격 요구 사항을 충족하지 못하는 컴퓨터에서도 쓸 수 있어요.

  1. 다음 명령은 Invoke-CommandAsJob 매개 변수로 Server01 컴퓨터에서 작업을 시작해요. 이 작업은 System 로그의 이벤트를 가져오는 Get-Eventlog 명령을 실행해요. JobName 매개 변수로 작업에 표시 이름을 지정할 수도 있어요.

    Invoke-Command -ComputerName Server01 -ScriptBlock { Get-EventLog System} -AsJob
    

    명령 결과는 다음과 비슷한 출력이에요.

    SessionId   Name   State    HasMoreData   Location   Command
    ---------   ----   -----    -----------   --------   -------
    1           Job1   Running  True          Server01   Get-EventLog System
    

    AsJob 매개 변수를 쓰면 Invoke-CommandStart-Job이 반환하는 것과 같은 유형의 작업 객체를 반환해요. 작업 객체를 변수에 저장하거나 Get-Job 명령으로 가져올 수 있어요.

    Location 속성 값을 보면 이 작업이 Server01 컴퓨터에서 실행됐다는 걸 알 수 있어요.

  2. Invoke-Command cmdlet의 AsJob 매개 변수로 시작한 작업을 관리하려면 Job cmdlet을 써요. 원격 작업을 나타내는 작업 객체가 로컬 컴퓨터에 있으니, 작업을 관리하려고 원격 명령을 실행할 필요가 없어요.

    작업이 완료됐는지 확인하려면 Get-Job 명령을 써요. 다음 명령은 현재 세션에서 시작된 모든 작업을 가져와요.

    Get-Job
    

    원격 작업이 현재 세션에서 시작됐으니, 로컬 Get-Job 명령으로도 작업을 가져올 수 있어요. 작업 객체의 State 속성은 명령이 성공적으로 완료됐음을 보여줘요.

    SessionId   Name   State      HasMoreData   Location   Command
    ---------   ----   -----      -----------   --------   -------
    1           Job1   Completed  True          Server01   Get-EventLog System
    
  3. 작업 결과를 가져오려면 Receive-Job cmdlet을 써요. 작업 결과는 작업 객체가 있는 컴퓨터로 자동 반환되므로, 로컬 Receive-Job 명령으로도 결과를 가져올 수 있어요.

    다음 명령은 Receive-Job cmdlet으로 작업 결과를 가져와요. 세션 ID로 작업을 식별하죠. 이 명령은 작업 결과를 $results 변수에 저장해요. 결과를 파일로 리디렉션할 수도 있어요.

    $results = Receive-Job -Id 1
    

결과를 원격 컴퓨터에 유지하는 원격 작업 시작하기

명령 결과를 원격 컴퓨터에 유지하는 작업을 시작하려면, Invoke-Command cmdlet으로 원격 컴퓨터에서 Start-Job 명령을 실행해요. 이 방법으로 여러 컴퓨터에서 작업을 실행할 수 있어요.

Start-Job 명령을 원격으로 실행하면 작업 객체가 원격 컴퓨터에 만들어지고, 작업 결과도 원격 컴퓨터에 유지돼요. 작업 입장에서 보면 모든 동작이 로컬인 셈이죠. 원격 컴퓨터의 로컬 작업을 관리하려고, 그저 명령을 원격으로 실행하고 있을 뿐이에요.

  1. Invoke-Command cmdlet으로 원격 컴퓨터에서 Start-Job 명령을 실행해요.

    이 명령은 PSSession(영구 연결)이 필요해요. Invoke-Command의 ComputerName 매개 변수로 임시 연결을 만들면, 작업 객체가 반환되는 순간 Invoke-Command 명령이 완료된 것으로 간주돼요. 그래서 임시 연결이 닫히고 작업이 취소돼 버려요.

    다음 명령은 New-PSSession cmdlet으로 Server01 컴퓨터에 연결된 PSSession을 만들어요. 이 PSSession을 $s 변수에 저장하죠.

    $s = New-PSSession -ComputerName Server01
    

    다음 명령은 Invoke-Command cmdlet으로 PSSession 안에서 Start-Job 명령을 실행해요. Start-Job 명령과 Get-Eventlog 명령이 중괄호 안에 들어 있어요.

    Invoke-Command -Session $s -ScriptBlock {
      Start-Job -ScriptBlock {Get-EventLog System}}
    

    결과는 다음과 비슷한 출력이에요.

    Id       Name    State      HasMoreData     Location   Command
    --       ----    -----      -----------     --------   -------
    2        Job2    Running    True            Localhost  Get-EventLog System
    

    Start-Job 명령을 원격으로 실행하면 Invoke-CommandStart-Job이 반환하는 것과 같은 유형의 작업 객체를 반환해요. 작업 객체를 변수에 저장하거나 Get-Job 명령으로 가져올 수 있어요.

    Location 속성 값을 보면 이 작업이 Server01 컴퓨터에서 실행됐는데도 로컬 컴퓨터, 그러니까 "LocalHost"에서 실행된 것으로 나와요. 작업 객체가 Server01 컴퓨터에 만들어지고 작업도 같은 컴퓨터에서 실행되니, 로컬 백그라운드 작업으로 간주되기 때문이에요.

  2. 원격 작업을 관리하려면 Job cmdlet을 써요. 작업 객체가 원격 컴퓨터에 있으니, 작업 결과를 가져오거나 중지·대기하려면 원격 명령을 실행해야 해요.

    작업이 완료됐는지 확인하려면 Invoke-Command 명령으로 Server01 컴퓨터에 연결된 PSSession 안에서 Get-Job 명령을 실행해요.

    Invoke-Command -Session $s -ScriptBlock {Get-Job}
    

    이 명령은 작업 객체를 반환해요. 작업 객체의 State 속성은 명령이 성공적으로 완료됐음을 보여줘요.

    SessionId   Name  State      HasMoreData   Location   Command
    ---------   ----  -----      -----------   --------   -------
    2           Job2  Completed  True          LocalHost   Get-EventLog System
    
  3. 작업 결과를 가져오려면 Invoke-Command cmdlet으로 Server01 컴퓨터에 연결된 PSSession 안에서 Receive-Job 명령을 실행해요.

    다음 명령은 Receive-Job cmdlet으로 작업 결과를 가져와요. 세션 ID로 작업을 식별하죠. 작업 결과를 $results 변수에 저장해요. Receive-Job의 Keep 매개 변수로 결과를 원격 컴퓨터의 작업 캐시에 유지해요.

    $results = Invoke-Command -Session $s -ScriptBlock {
      Receive-Job -SessionId 2 -Keep
    }
    

    결과를 로컬 컴퓨터나 원격 컴퓨터의 파일로 리디렉션할 수도 있어요. 다음 명령은 리디렉션 연산자로 결과를 Server01 컴퓨터의 파일에 저장해요.

    Invoke-Command -Session $s -Command {
      Receive-Job -SessionId 2 > C:\logs\pslog.txt
    }
    

분리 프로세스로 실행하기

앞에서 말했듯이 부모 세션이 종료되면, 실행 중인 모든 자식 작업이 그 자식 프로세스와 함께 종료돼요. 로컬 컴퓨터의 원격 기능을 사용하면 현재 PowerShell 세션에 붙어 있지 않은 작업을 실행할 수 있어요.

로컬 컴퓨터에서 새 PowerShell 세션을 만들고, 그 세션 안에서 Invoke-Command로 작업을 시작해요. Invoke-Command는 원격 세션의 연결을 끊고 부모 세션을 종료할 수 있게 해줘요. 나중에 새 PowerShell 세션을 시작해 아까 연결을 끊었던 세션에 다시 연결하면 작업 모니터링을 이어갈 수 있어요. 다만 원래 PowerShell 세션으로 반환됐던 데이터는 그 세션이 종료되면 사라져요. 다시 연결했을 때는 연결을 끊은 뒤에 새로 생성된 데이터 객체만 반환돼요.

# Create remote session on local machine
PS> $session = New-PSSession -Cn localhost

# Start remote job
PS> $job = Invoke-Command -Session $session -ScriptBlock { 1..60 | % { sleep 1; "Output $_" } } -AsJob
PS> $job

Id     Name     PSJobTypeName   State         HasMoreData     Location      Command
--     ----     -------------   -----         -----------     --------      -------
1      Job1     RemoteJob       Running       True            localhost     1..60 | % { sleep 1; ...

# Disconnect the job session
PS> Disconnect-PSSession $session

Id Name         Transport ComputerName    ComputerType    State         ConfigurationName     Availability
-- ----         --------- ------------    ------------    -----         -----------------     ------------
1 Runspace1     WSMan     localhost       RemoteMachine   Disconnected  Microsoft.PowerShell          None

PS> $job

Id     Name     PSJobTypeName   State         HasMoreData     Location      Command
--     ----     -------------   -----         -----------     --------      -------
1      Job1     RemoteJob       Disconnected  True            localhost     1..60 | % { sleep 1;

# Reconnect the session to a new job object
PS> $jobNew = Receive-PSSession -Session $session -OutTarget Job
PS> $job | Wait-Job | Receive-Job
Output 9
Output 10
Output 11
...

이 예시에서 작업들은 여전히 부모 PowerShell 세션에 붙어 있어요. 다만 그 부모 세션은 Invoke-Command가 실행됐던 원래 PowerShell 세션이 아니라는 점이 핵심이에요.

더 알아보기