작업(Job) 상세 — 백그라운드 작업이 실제로 어떻게 돌아가는지

작업(Job) 상세 — 백그라운드 작업이 실제로 어떻게 돌아가는지 (about_Job_Details)

PowerShell에서 오래 걸리는 명령을 실행하다 보면, 명령이 끝날 때까지 프롬프트를 못 누르고 기다려야 하는 답답함을 겪어본 적이 있을 거예요. 그럴 때 쓸 수 있는 게 **백그라운드 작업(background job)**이에요. 명령을 뒤에서 알아서 돌려 두고, 그동안 저는 다른 일을 계속할 수 있게 해주는 기능이죠. 이 문서는 그 백그라운드 작업이 개념적으로 무엇인지, 그리고 PowerShell 안에서 실제로 어떻게 동작하는지에 대한 기술적인 세부 내용을 다뤄요. about_Jobs, about_Thread_Jobs, about_Remote_Jobs 문서의 보충 설명이라고 생각하면 돼요.

출처: about_Job_Details

본문

짧은 설명

로컬 컴퓨터와 원격 컴퓨터에서 실행되는 백그라운드 작업에 대한 세부 내용을 제공해요.

자세한 설명

이 문서는 백그라운드 작업의 개념과 PowerShell에서 백그라운드 작업이 동작하는 방식에 대한 기술적 정보를 설명해요. about_Jobs, about_Thread_Jobs, about_Remote_Jobs 문서의 보충 자료예요.

백그라운드 작업이란

백그라운드 작업은 명령이나 식(expression)을 **비동기(asynchronously)**로 실행해요. cmdlet일 수도, 함수일 수도, 스크립트일 수도 있고 그 외에 명령 기반 작업이면 무엇이든 될 수 있어요. 원래는 오래 걸리는 명령을 실행하기 위한 용도로 설계됐지만, 어떤 명령이든 백그라운드에서 돌릴 수 있어요.

일반적인 동기(synchronous) 명령이 실행되는 동안에는 PowerShell 프롬프트가 명령이 끝날 때까지 눌리지 않아요. 그런데 백그라운드 작업은 PowerShell 프롬프트를 막지 않아요. 백그라운드 작업을 시작하는 명령은 작업 개체(job object)를 돌려주고, 프롬프트는 즉시 다시 돌아와요. 그래서 백그라운드 작업이 도는 동안 저는 다른 작업을 계속할 수 있죠.

다만 백그라운드 작업을 시작했다고 해서 결과를 바로 받을 수 있는 건 아니에요. 작업이 아주 빨리 끝나는 경우에도 마찬가지예요. 돌려받은 작업 개체에는 작업에 대한 유용한 정보가 들어 있지만, 결과 자체는 들어 있지 않아요. 결과를 얻으려면 별도의 명령을 따로 실행해야 해요. 작업을 중지하거나, 작업이 끝날 때까지 기다리거나, 작업을 삭제하는 명령도 각각 따로 있죠.

백그라운드 작업의 타이밍이 다른 명령과 독립적으로 움직이도록, 각 백그라운드 작업은 자기만의 PowerShell 세션에서 실행돼요. 이 세션은 작업을 실행하기 위해서만 만들었다가 사라지는 임시 연결일 수도 있고, 여러 관련 작업이나 명령을 돌리는 데 쓸 수 있는 영속적인 PSSession일 수도 있어요.

작업 cmdlet 사용하기

로컬 컴퓨터에서 백그라운드 작업을 시작하려면 Start-Job 명령을 써요. Start-Job은 작업 개체를 돌려주고, 로컬 컴퓨터에서 시작된 작업들을 나타내는 개체는 Get-Job cmdlet으로 가져올 수 있어요.

작업 결과를 얻으려면 Receive-Job 명령을 사용해요. 작업이 아직 끝나지 않았다면 Receive-Job부분 결과를 돌려줘요. 또한 Wait-Job cmdlet을 쓰면 세션에서 시작한 작업이 전부 또는 일부 끝날 때까지 명령 프롬프트를 누르지 못하게 막아둘 수도 있어요.

백그라운드 작업을 중지하려면 Stop-Job cmdlet을, 작업을 삭제하려면 Remove-Job cmdlet을 사용해요.

각 cmdlet이 어떻게 동작하는지 더 자세히 알고 싶다면 cmdlet별 Help 항목과 about_Jobs를 참고해요.

원격 컴퓨터에서 백그라운드 작업 시작하기

백그라운드 작업은 로컬 컴퓨터뿐 아니라 원격 컴퓨터에서도 만들고 관리할 수 있어요. 원격으로 백그라운드 작업을 실행하려면 Invoke-Command 같은 cmdlet의 AsJob 매개변수를 쓰거나, Invoke-Command cmdlet으로 Start-Job 명령을 원격에서 실행하면 돼요. 대화형(interactive) 세션에서 백그라운드 작업을 시작할 수도 있어요.

원격 백그라운드 작업에 대한 자세한 내용은 about_Remote_Jobs를 참고해요.

자식 작업(Child jobs)

각 백그라운드 작업은 부모 작업(parent job) 하나와 자식 작업(child job) 하나 이상으로 이루어져 있어요. Start-Job이나 Invoke-CommandAsJob 매개변수로 시작한 작업에서 부모 작업은 일종의 관리자(executive) 역할을 해요. 자체적으로 명령을 실행하지도, 결과를 돌려주지도 않죠. 명령은 실제로는 자식 작업들이 실행해요. 다른 cmdlet으로 시작한 작업은 방식이 다를 수 있어요.

자식 작업은 부모 작업 개체의 ChildJobs 속성에 저장돼요. ChildJobs 속성에는 자식 작업 개체가 하나 또는 여러 개 들어갈 수 있어요. 자식 작업 개체는 부모 작업과 다른 Name, Id, InstanceId를 갖고 있어서, 부모 작업과 자식 작업을 각각 따로 또는 하나의 단위로 관리할 수 있어요.

작업의 부모·자식 작업을 모두 가져오려면 Get-Job cmdlet의 IncludeChildJobs 매개변수를 사용해요. IncludeChildJob 매개변수는 Windows PowerShell 3.0에서 도입됐어요.

Get-Job -IncludeChildJob
Id Name   PSJobTypeName State      HasMoreData   Location    Command
-- ----   ------------- -----      -----------   --------    -------
1  Job1   RemoteJob     Failed     True          localhost   Get-Process
2  Job2                 Completed  True          Server01    Get-Process
3  Job3                 Failed     False         localhost   Get-Process

부모 작업과, 특정 State 값을 가진 자식 작업만 가져오려면 Get-Job cmdlet의 ChildJobState 매개변수를 써요. ChildJobState 매개변수는 Windows PowerShell 3.0에서 도입됐어요.

Get-Job -ChildJobState Failed
Id Name   PSJobTypeName State      HasMoreData   Location    Command
-- ----   ------------- -----      -----------   --------    -------
1  Job1   RemoteJob     Failed     True          localhost   Get-Process
3  Job3                 Failed     False         localhost   Get-Process

모든 버전의 PowerShell에서 작업의 자식 작업을 가져오려면 부모 작업의 ChildJob 속성을 사용해요.

(Get-Job Job1).ChildJobs
Id Name   PSJobTypeName State      HasMoreData   Location    Command
-- ----   ------------- -----      -----------   --------    -------
2  Job2                 Completed  True          Server01    Get-Process
3  Job3                 Failed     False         localhost   Get-Process

자식 작업에도 Get-Job 명령을 쓸 수 있어요. 다음 명령처럼요.

Get-Job Job3
Id Name   PSJobTypeName State      HasMoreData   Location    Command
-- ----   ------------- -----      -----------   --------    -------
3  Job3                 Failed     False         localhost   Get-Process

자식 작업의 구성은 작업을 시작할 때 쓴 명령에 따라 달라져요.

  • 로컬 컴퓨터에서 Start-Job으로 작업을 시작하면, 작업은 관리자 역할의 부모 작업과 명령을 실행하는 자식 작업 하나로 이루어져요.

  • Invoke-CommandAsJob 매개변수로 한 대 이상의 컴퓨터에서 작업을 시작하면, 작업은 관리자 역할의 부모 작업과, 각 컴퓨터에서 실행되는 작업별 자식 작업 하나로 이루어져요.

  • Invoke-Command로 한 대 이상의 원격 컴퓨터에서 Start-Job 명령을 실행하면, 결과는 각 원격 컴퓨터에서 로컬 명령을 실행한 것과 같아요. 이 명령은 컴퓨터마다 작업 개체를 하나씩 돌려주죠. 각 작업 개체는 관리자 역할의 부모 작업과 명령을 실행하는 자식 작업 하나로 이루어져 있어요.

부모 작업은 모든 자식 작업을 대표해요. 부모 작업을 관리하면 연결된 자식 작업도 함께 관리돼요. 예를 들어 부모 작업을 중지하면 모든 자식 작업이 중지되고, 부모 작업의 결과를 가져오면 모든 자식 작업의 결과를 가져오게 돼요.

물론 자식 작업을 개별적으로 관리할 수도 있어요. 특히 Invoke-CommandAsJob 매개변수로 시작한 여러 자식 작업 중에서 문제가 있는 작업을 조사하거나, 결과 하나만 가져오고 싶을 때 유용해요.

다음 명령은 Invoke-CommandAsJob 매개변수를 사용해 로컬 컴퓨터와 원격 컴퓨터 두 대에서 백그라운드 작업을 시작해요. 작업은 $j 변수에 저장돼요.

$invokeCommandSplat = @{
    ComputerName = 'localhost', 'Server01', 'Server02'
    ScriptBlock = {Get-Date}
    AsJob = $true
}
$j = Invoke-Command @invokeCommandSplat

$j에 들어 있는 작업의 NameChildJob 속성을 표시해 보면, 명령이 컴퓨터마다 하나씩, 총 세 개의 자식 작업을 가진 작업 개체를 돌려줬다는 걸 확인할 수 있어요.

$j | Format-List Name, ChildJobs
Name      : Job3
ChildJobs : {Job4, Job5, Job6}

부모 작업을 표시해 보면 작업이 실패(Failed) 상태라는 게 보여요.

$j
Id Name   PSJobTypeName State      HasMoreData   Location
-- ----   ------------- -----      -----------   --------
3  Job3   RemotingJob   Failed     False         localhost,Server...

그런데 자식 작업을 가져오는 Get-Job 명령을 실행하면, 실패한 자식 작업은 하나뿐이라는 게 드러나요.

Get-Job -IncludeChildJobs
Id  Name   PSJobTypeName State      HasMoreData   Location    Command
--  ----   ------------- -----      -----------   --------    -------
3   Job3   RemotingJob   Failed     False         localhost,Server...
4   Job4                 Completed  True          localhost   Get-Date
5   Job5                 Failed     False         Server01    Get-Date
6   Job6                 Completed  True          Server02    Get-Date

모든 자식 작업의 결과를 가져오려면 Receive-Job cmdlet으로 부모 작업의 결과를 가져오면 돼요. 특정 자식 작업의 결과만 가져올 수도 있어요. 다음 명령처럼요.

Receive-Job -Name Job6 -Keep |
    Format-Table ComputerName, DateTime -AutoSize
ComputerName DateTime
------------ --------
Server02     Thursday, March 13, 2008 4:16:03 PM

PowerShell 백그라운드 작업의 자식 작업 기능 덕분에 실행 중인 작업을 훨씬 세밀하게 제어할 수 있어요.

작업 형식(Job types)

PowerShell은 용도에 따라 여러 가지 형식의 작업을 지원해요. Windows PowerShell 3.0부터 개발자는 **작업 소스 어댑터(job source adapter)**를 작성해서 PowerShell에 새 작업 형식을 추가하고, 그 어댑터를 모듈에 포함할 수 있었어요. 모듈을 가져오면 세션에서 새 작업 형식을 사용할 수 있죠. 예를 들어 PSScheduledJob 모듈은 예약 작업(scheduled job)을 추가하고, PSWorkflow 모듈은 워크플로 작업(workflow job)을 추가해요.

사용자 정의 작업 형식은 표준 PowerShell 백그라운드 작업과 꽤 다를 수 있어요. 예를 들어 예약 작업은 디스크에 저장되기 때문에 특정 세션에만 존재하지 않아요. 워크플로 작업은 일시 중지하고 다시 시작할 수도 있죠.

사용자 정의 작업을 관리하는 cmdlet은 작업 형식에 따라 달라요. 어떤 작업은 Get-Job, Start-Job 같은 표준 작업 cmdlet을 쓰고, 어떤 작업은 특정 형식만 관리하는 전용 cmdlet이 따로 와요. 사용자 정의 작업 형식에 대한 자세한 내용은 해당 작업 형식의 도움말 항목을 참고해요.

작업의 형식을 알아보려면 Get-Job cmdlet을 사용해요. Get-Job은 형식에 따라 다른 작업 개체를 돌려줘요. Get-Job이 돌려주는 작업 개체의 PSJobTypeName 속성 값이 작업 형식을 알려주죠.

다음은 PowerShell에 기본 내장된 작업 형식 목록이에요.

  • BackgroundJobStart-Job cmdlet으로 시작돼요.

  • RemoteJobInvoke-Command cmdlet의 AsJob 매개변수로 시작돼요.

  • CIMJob — CDXML 모듈의 cmdlet에서 AsJob 매개변수로 시작돼요.

  • WMIJob — WMI 모듈의 cmdlet에서 AsJob 매개변수로 시작돼요.

  • PSEventJobRegister-ObjectEvent를 사용하고 Action 매개변수로 동작을 지정해서 만들어져요.

참고

특정 형식의 작업을 Get-Job cmdlet으로 가져오기 전에, 그 작업 형식을 추가한 모듈이 현재 세션에 import되어 있는지 확인해요. import되어 있지 않으면 Get-Job이 그 형식의 작업을 가져올 수 없어요.

Windows PowerShell 5.1에는 다음 작업 형식이 추가로 포함돼요.

  • PSWorkflowJob — 워크플로의 AsJob 매개변수로 시작돼요.

  • PSScheduledJob — 작업 트리거(job trigger)로 시작된 예약 작업의 인스턴스예요.

이 작업 형식은 Windows PowerShell 5.1에서만 사용할 수 있어요. 이 형식을 추가하는 모듈은 PowerShell 6.0 이상과 호환되지 않아요.

자세한 내용은 다음 문서를 참고해요.

예시

다음 명령들은 로컬 백그라운드 작업, 원격 백그라운드 작업, 워크플로 작업, 예약 작업을 만들고 Get-Job cmdlet으로 그 작업들을 가져와요. Get-Job은 예약 작업 자체는 가져오지 못하지만, 예약 작업으로 시작된 인스턴스는 가져와요.

로컬 컴퓨터에서 백그라운드 작업을 시작해요.

PS> Start-Job -Name LocalData {Get-Process}

Id Name        PSJobTypeName   State   HasMoreData   Location   Command
-- ----        -------------   -----   -----------   --------   -------
2  LocalData   BackgroundJob   Running        True   localhost  Get-Process

원격 컴퓨터에서 실행되는 백그라운드 작업을 시작해요.

$invokeCommandSplat = @{
    ComputerName = 'Server01'
    AsJob = $true
    JobName = 'RemoteData'
    ScriptBlock = {Get-Process}
}
Invoke-Command @invokeCommandSplat
Id  Name        PSJobTypeName  State   HasMoreData   Location   Command
--  ----        -------------  -----   -----------   --------   -------
2   RemoteData  RemoteJob      Running        True   Server01   Get-Process

더 알아보기