次の方法で共有


【Management】 “Invoke-Command -AdJob” と “Invoke-Command {Start-Job}” の違い、説明できますか?

image

多くの方が Windows PowerShell をお使いのことと思います。

PowerShell にはさまざまな「奥義」が存在しますが、「バックグラウンドジョブ」も奥義の一つです。これは究極奥義である「ワークフロージョブ」へとつながる大切な概念です。

まずは以下をご覧ください。

Get-Service -ComputerName Server01

何をやっているかは一目瞭然ですよね。

リモートコンピューター Server01 上のサービス一覧を取得しています。

通常 Get-Service は直ぐに結果を得られるので問題ないのですが、結果取得までに10分とか20分を要する場合にはコンソールを占有されてしまうことを回避するため、「バックグラウンドジョブ」と呼ばれる方法を使用します。

つまり、コマンドを投げっぱなしにしておいて(非同期実行)、あとから結果を取りに行く...という方法です。

バックグラウンドジョブを作成するには2つの方法があります。

  • -AsJob パラメタを使用する
  • Start-Job コマンドレットを使用する

いずれを使用しても得られる結果は同じですが、コマンドレットによっては -AsJob をサポートしていない場合もあり、その場合には Start-Job の引数にコマンドレットを指定します。

例えば、Get-Service の場合には -AsJob パラメタをサポートしていないため、バックグラウンドジョブ化するには以下のように書きます。

Start-Job -ScriptBlock { Get-Service -ComputerName Server01 }

さて、ここまでは一般ピープルでも知っていることです。

エキスパートな方はここからが重要なのです。

さっそくですが、以下の2つの違いわかりますか?いずれも上の記述を書き換えたもので、同じ結果が得られます。

$S = New-PSSession -ComputerName Server01
Invoke-Command -Session $S -ScriptBlock { Get-Service } -AsJob

$S = New-PSSession -ComputerName Server01
Invoke-Command -Session $S -ScriptBlock {Start-Job -ScriptBlock { Get-Service } } 

1 行目の 「$S = New-PSSession -ComputerName Server01 」はおわかりですよね。リモートコンピューター Server01 と PS セッションを張ってます。図にすると以下のような感じです。

image

問題は2行目です。同じことをやってそうなのですが、微妙に書き方が異なっています。-AsJob と Start-Job に何か秘密が隠れているようではあります。

Invoke-Command -Session $S -ScriptBlock { Get-Service } -AsJob

Invoke-Command を使用すると、リモートコンピューター上でコマンドレットを実行することができます。今回の例では、事前に作成した Server01 とのセッションを使い、Server01 上で Get-Service を実行しています。

-AsJob を指定することで Invoke-Command 自体がバックグラウンドジョブ化され、リモートでのコマンド実行を待ち合せることなく、バックグラウンドで非同期に実行することができます。

結果は Invoke-Command を実行したローカルホストに保存されます(ここ重要!)。

図にすると以下のような感じです。

image

Invoke-Command -Session $S -ScriptBlock {Start-Job -ScriptBlock {Get-Service}} 

一方、-AsJob ではなく、ScriptBlock 内で Start-Job を実行した場合はどうなるでしょう。

Start-Job は Server01 上で実行されるため、バックグラウンドジョブ化されるのは Server01 上の Get-Service コマンドです。

では、結果はどこに保存されるかといえば、ジョブが実行された Server01 上です。

図にすると以下のようになります。

image

当然、結果を取りに行くときは、以下のように Server01 に対して Receive-Job する必要があります。

Invoke-Command -Session $S -ScriptBlock { Receive-Job <jobid> }

両者の違いを正しく理解しておくことはとても重要です。

例えば、「長く時間を要するバックグラウンドジョブを実行して、あとで結果を受け取ろう」と考えたとしましょう。

もし Invoke-Command -AsJob を使用してローカルコンピューター上に結果を保存するようにした場合、ローカルコンピューターはジョブが終わるまでネットワークから切り離すこともシャットダウンすることもできません。ネットワークから切り離されたり、シャットダウンした瞬間にジョブは消滅します。

しかし、後者( Invoke-Command {Start-Job })で実行すれば、ジョブはリモートで実行され、結果もリモートに保存されます。

ジョブを投げたあとはDisconnect-PSSession でセッションをいったん切断し、あとから Connect-PSSession でもとのセッションに再接続することができます。

(参考)

【Management】PowerShell V3.0 で向上したリモーティング機能 その1
https://blogs.technet.com/b/junichia/archive/2012/03/24/3488392.aspx

【Management】PowerShell V3.0 で向上したリモーティング機能 その2
https://blogs.technet.com/b/junichia/archive/2012/03/26/3488520.aspx

「ジョブの実行場所が、結果の保存場所である」

.....ということをくれぐれも忘れないでください。