normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

Azure Web サイトで Jenkins を無料で稼働させてストレージサービスにデータをアップロードする

本日の記事は Azure Advent Calendar の14日目となる。今回は以下の様に Webサイト 上に Jenkins を配置し、ジョブ実行時にデータを Azure ストレージサービスにアップロードするまでを紹介する。

構築手順の概要は?

Webサイトは JDKTomcat が標準で配置されているが、以下の様に Jenkins や Jenkins Plugin は配置されていない。
f:id:waritohutsu:20141214121627p:plain
したがって以下の流れとなる。

  • Jenkins 配置向けの Webサイト を新規作成する
    • フォルダ作成、モジュール配置
    • 環境変数の設定
  • Jenkins を設定し、Azure ストレージ上にアップロードできるようにする
    • Jenkins 向けのストレージサービスを新規作成
    • Azure Storage プラグインの設定
    • ジョブの作成
    • ジョブの実行

Jenkins 配置向けの Webサイト を新規作成する

まず、管理ポータルから新規に Webサイト を作成(今回は normalian-jenkins という名前にした)し、以下の様に Tomat を有効化する(JDKTomcat のバージョンがちょっと古いのはご容赦願いたいが、自前でモジュールを配置すればもちろん最新版が利用可能だ)。
f:id:waritohutsu:20141214121828p:plain
更に、以下を参考に HUDSON_HOME と JAVA_OPTS の環境変数を設定する。以下の設定を行わない場合、Jenkins がエラーを出力して起動しないので要注意だ( Jetty 側は -Djava.net.preferIPv4Stack=true が元々オプションに入っているのに、なぜか Tomcat 側は入っていない)。
f:id:waritohutsu:20141214122105p:plain

次に、Kudu にログインしてする。Kudu を知らないのは Azure 界の著名人が記載する Windows Azure Web Sitesの魅力を120%引き出す を参照すること。Kudu の Debug console タブから移動し、以下の様に HUDSON_HOME 環境変数向けのフォルダを作成する。なお、Ant や Maven は必要な場合はフォルダを作成し、モジュールを配置すること。
f:id:waritohutsu:20141214122148p:plain

更に、webapp\ROOT の直下に jenkins.war モジュールの中身を配置する。一番簡単な方法は jenkins.war を jenkins.zip にリネームし、モジュールの中身を引っこ抜いてコピーすればよい。
f:id:waritohutsu:20141214122254p:plain

ここまで設定した後、Azure 管理ポータルから Jenkins の設定をした Webサイト を再起動してアクセスする。以下のような画面がでるのでしばらく待ってほしい。
f:id:waritohutsu:20141214122406p:plain

しばらくすると、以下の画面が表示されるので設定完了となる。
f:id:waritohutsu:20141214122442p:plain

Web サイトはインターネット上に公開されているため、ユーザ作成を含む最低限のセキュリティ設定(Jenkins) を参照して登録されたユーザのみログインすることが可能となる(が、https にすらなっていないので、あくまで最低限)。

Jenkins を設定し、Azure ストレージ上にアップロードできるようにする

先ほどまでの手順で Webサイト 上に Jenkins の環境が構築されたので、次は Jenkins 自体の設定を行う。
まずは Jenkins Plugin 向けのストレージサービスを新規に作成する(名前は任意でよい)が、こちらは一般的な方法なので手順は割愛する。
次に、以下の流れで Azure Storage Plugin をインストールする。

  • Jenkins のダッシュボードで、[Jenkins の管理] を押下する。
  • [Jenkins の管理] ページで [プラグインの管理] を押下する。
  • [利用可能] タブをクリックします。
  • [Windows Azure Storage plugin] で検索し、Microsoft Azure ストレージ プラグインを確認する。
  • [再起動せずにインストール] か [ダウンロードして再起動後にインストール] のどちらか押下してインストールを行う。

次に、[Jenkinsの管理]-[Jenkinsの管理] の Microsoft Azure Storage Account Configuration セクションで以下を参考に設定を行う。
f:id:waritohutsu:20141214122519p:plain

さらに、Jenkins のダッシュボードから [新規ジョブ作成]を選択し、[フリースタイル・プロジェクトのビルド]を選択し、以下を参考に情報を設定する。
f:id:waritohutsu:20141214122555p:plain

こちらについてはの詳細な手順は Jenkins 継続的インテグレーション ソリューションでの Azure ストレージの使用にもあるので合わせて参考にすること。
以上の設定が終わった状態から作成したジョブから [ビルドの実行] を選択するとストレージサービスにジョブで設定したファイルがアップロードされる。まずは Jenkins のジョブのページを確認すると、以下のようにジョブの実行結果が確認される。
f:id:waritohutsu:20141214122648p:plain

更に、ストレージサービスを確認するとモジュールがアップロードされていることが確認できる。
f:id:waritohutsu:20141214122754p:plain

終わりに

今回は無料で Azure Webサイト上に Jenkins 環境が構築できることを紹介した。こちらに対し、さらに NEXUS を追加 したり、Github の Jenkins plugin を追加しても面白いだろう。

JavaEE7 で CDI の Interceptor を味わってみる

Java EE6 から導入された CDI のインターセプタだが( JavaEE5 までは EJB インターセプタしか存在しなかった)、JavaEE7 で改良が加えられているので紹介する。今回紹介する内容は以下の二点になる。

こちらをそれぞれ試してみる

ソースコード

今回作成したソースコードの動作イメージは以下となる。
f:id:waritohutsu:20141211154727p:plain

次に、動作に必要なソースコードは以下になる。beans.xml が不要な点に注意が必要だ。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>Facelet のインターセプタを学ぶ</title>
    </h:head>
    <h:body>
        <h:form>
            Hello Facelets: 今は <h:outputText value="#{listAction.count}" />回目<br />
            <h:commandButton value="加算" action="#{listAction.countUp()}" />
        </h:form>
    </h:body>
</html>
package com.mydomain.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Logged {
}
package com.mydomain.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;

@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface SecondLogged {
}
  • LoggedInterceptor.java
package com.mydomain.interceptors;

import com.mydomain.annotations.Logged;
import java.io.Serializable;
import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;


@Priority(Interceptor.Priority.LIBRARY_BEFORE)
@Interceptor
@Logged
public class LoggedInterceptor implements Serializable {

    public LoggedInterceptor() {

    }
    @AroundInvoke
    public Object logMethodEntry(InvocationContext ic) throws Exception {
        System.out.println("$$Logged Interceptor, before: "
                + ic.getMethod().getName() + " in class "
                + ic.getMethod().getDeclaringClass().getName());
        Object obj = ic.proceed();
        System.out.println("$$Logged Interceptor, after: "
                + ic.getMethod().getName() + " in class "
                + ic.getMethod().getDeclaringClass().getName());
        return obj;
    }
}
  • SecondLoggedInterceptor.java
package com.mydomain.interceptors;

import com.mydomain.annotations.SecondLogged;
import java.io.Serializable;
import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Priority(Interceptor.Priority.APPLICATION)
@Interceptor
@SecondLogged
public class SecondLoggedInterceptor implements Serializable {

    @AroundInvoke
    public Object logMethodEntry(InvocationContext ic) throws Exception {
        System.out.println("@@SecondLogged Interceptor, before: "
                + ic.getMethod().getName() + " in class "
                + ic.getMethod().getDeclaringClass().getName());
        Object obj = ic.proceed();
        System.out.println("@@SecondLogged Interceptor, after: "
                + ic.getMethod().getName() + " in class "
                + ic.getMethod().getDeclaringClass().getName());
        return obj;
    }
}
package com.mydomain.action;

import com.mydomain.annotations.SecondLogged;
import com.mydomain.annotations.Logged;
import java.io.Serializable;
import javax.faces.view.ViewScoped;
import javax.inject.Named;

@Named
@ViewScoped
public class ListAction implements Serializable {

    private int count;

    @Logged
    @SecondLogged
    public String countUp() {
        System.out.println("★★★ListAction#countUp, count = " + count);
        setCount(getCount() + 1);
        return "";
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}


上記を動作させた場合の実行結果はいかになる。

情報:   $$Logged Interceptor, before: countUp in class com.mydomain.action.ListAction
情報:   @@SecondLogged Interceptor, before: countUp in class com.mydomain.action.ListAction
情報:   ★★★ListAction#countUp, count = 0
情報:   @@SecondLogged Interceptor, after: countUp in class com.mydomain.action.ListAction
情報:   $$Logged Interceptor, after: countUp in class com.mydomain.action.ListAction

インターセプタの順番を決める Priority は値が大きいほど優先度が高く、あらかじめ用意されている定数は以下になる。

定数名
PLATFORM_BEFORE 0
LIBRARY_BEFORE 1000
APPLICATION 2000
LIBRARY_AFTER 3000
PLATFORM_AFTER 4000

JavaEE7 な JSF 2.2 の ViewScoped を試してみる

今回はふと JavaEE を弄っていてちょっとはまってしまったポイントについて紹介する。JavaEE 有識者各位ならば J2EE 時代の XML 地獄を脱却するため、JavaEE5 からはアノテーションによる規約ベースの設定を重視する文化になったことはご存じだと思うが、特に JSF には以下の闇がある。

  • JSF 1.0(JavaEE5 時代):DI コンテナがなく(厳密には JBoss Seam が存在したが、Java EE の仕様に入っていなかった)、ManagedBean は faces-config.xmlXML ベースで設定
  • JSF 2.0(JavaEE6 時代):DI コンテナ(CDI)が導入されたが、JSF 2.0 のアノテーションCDIアノテーションが混在し、更に JSF の機能と CDI の機能に差があった
  • JSF 2.2(JavaEE7 時代):JavaEE6 時代の問題を大体解決(原則 CDI 側に寄せた)したが、若干名残あり

特に、JavaEE6 時代の闇については 達人プログラマーを目指して > 大混乱に陥っているJavaEE 6のアノテーションに関する使い分けについて達人プログラマーを目指して > Java EE6環境でJSF2を使う場合はCDIのBeanを管理Beanとして使う方がよい が詳しい。

今回は JSF 2.2 より CDI compatible @ViewScoped が存在するので、そちらを確認してみる。

サンプルコード

ManagedBean 相当の Java ファイルと画面相当 xhtml ファイルは以下となる。 MessageAction.java の ①~④のアノテーションを組み合わせて検証する。

  • MessageAction.java
package mysample.app.action;

import java.io.Serializable;
//① import javax.faces.view.ViewScoped;
//② import javax.faces.bean.ManagedBean;
//③ import javax.faces.bean.ViewScoped;
//④ import javax.inject.Named;

//@ManagedBean
@ViewScoped
@Named
public class MessageAction implements Serializable {

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String doAction() {
        System.out.println("message at MessageAction#doAction = " + message);
        return null;
    }
}
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
    <h:head>
        <title>My Sample</title>
        <style type="text/css">
            .strike{
                text-decoration: line-through;
            }
        </style>            
    </h:head>
    <h:body>
        <h2>Scope Confrim</h2>
        <h:messages layout="table" styleClass="alert" />
        <h:form>
            <h:panelGrid columns="2">
                <h:outputLabel value="Title: "/>
                <h:inputText value="#{messageAction.message}" p:placeholder="please input text" />
                <h:commandButton value="DoAction" action="#{messageAction.doAction()}" />
            </h:panelGrid>
        </h:form>
    </h:body>
</html>

上記のうち、p:placeholder と javax.faces.view.ViewScoped は JSF 2.2 からの新機能だ。こちらのコードを利用した実行結果は以下となる。

  • ①+④:「情報: message at MessageAction#doAction = <入力値>」が出力
  • ①+②:「情報: message at MessageAction#doAction = null」が出力
  • ②+③:「情報: message at MessageAction#doAction = <入力値>」が出力
  • ③+④:「情報: message at MessageAction#doAction = null」が出力

なんでこうなるの?

冒頭で語った JSF の闇に記載した通り、JSF のライフサイクルと CDI のライフサイクルのどちらを利用するかで必要なアノテーションの組み合わせが異なるためだ。

javax.faces.view.ViewScoped & javax.inject.Named は CDI を利用したライフサイクルになる。以下に javax.faces.view.ViewScoped に対するコメントを抜粋する。

import javax.faces.view.ViewScoped;

When this annotation, along with javax.inject.Named is found on a class, the runtime must place the bean in a CDI scope such that it remains active as long as NavigationHandler.handleNavigation(javax.faces.context.FacesContext, java.lang.String, java.lang.String) does not cause a navigation to a view with a viewId that is different than the viewId of the current view. Any injections and notifications required by CDI and the Java EE platform must occur as usual at the expected time.


また、javax.faces.bean.ViewScoped & javax.faces.bean.ManagedBean の場合は JSF を利用したライフサイクルになる。以下に javax.faces.bean.ViewScoped に対するコメントを抜粋する。

import javax.faces.beans.ViewScoped;

When this annotation, along with ManagedBean is found on a class, the runtime must act as if a <managed-bean-scope>view<managed-bean-scope> element was declared for the corresponding managed bean.

更に、上記で検証した通り JSF 向けのアノテーションCDIアノテーションを組み合わせた場合はデータバインディングがうまく動かない点に注意が必要だ。
JSFCDI を利用する際の問題は、他のスコープについてのアノテーション(リクエストスコープ、セッションスコープ、アプリケーションスコープ)にも存在するため、これらのアノテーションの使い分けに注意してほしい。

Microsoft Azure PowerShell で Windows Server の最新 ImangeName を取得する方法

今回は簡単に備忘録だけ記載するが、もっといい方法があれば突っ込みを期待する。Microsoft Azure の IaaS を利用する際、作成する仮想マシンインスタンスのイメージ名を選択するため、Get-AzureVMImage のコマンドを叩く人がいるだろう。

PS C:\temp> Get-AzureVMImage
ImageName            : 03f55de797f546a1b29d1b8d66be687a__Team-Foundation-Server-2013-Update4-WS2012R2
OS                   : Windows
MediaLink            : 
LogicalSizeInGB      : 128
AffinityGroup        : 
Category             : Public
Location             : East Asia;Southeast Asia;Australia East;Australia Southeast;Brazil South;North Europe;West Europe;Japan East;Japan West;Central US;East US;East US 2;North Central US;South Central US;West US
Label                : Team Foundation Server 2013 Update 4 on Windows Server 2012 R2
Description          : Microsoft Team Foundation Server 2013 Trial on Windows Server 2012 R2 Update. Virtual Machines created with this trial image will require a product key for Team Foundation Server (such as from an 
                       MSDN Subscription). This image includes a complete installation of Team Foundation Server 2013 Update 4. Some components require additional setup and configuration. You can configure SQL Server us
                       ing SQL Server Express included in this image, by downloading and installing SQL Server Standard edition (from an MSDN Subscription), or by connect to a pre-existing SQL Server. Minimum virtual ma
                       chine size for this image is Medium. For more details on TFS server setup please see the [Team Foundation Server install guide|http://msdn.microsoft.com/en-us/library/dd631902.aspx].
Eula                 : http://www.microsoft.com/en-us/download/details.aspx?id=13350
ImageFamily          : Team Foundation Server 2013 Update 4 on Windows Server 2012 R2
PublishedDate        : 2014/11/12 17:00:00
IsPremium            : False
IconUri              : VisualStudio2013_45.png
SmallIconUri         : VisualStudio2013_45.png
PrivacyUri           : http://go.microsoft.com/fwlink/?LinkID=286720
RecommendedVMSize    : Medium
PublisherName        : Microsoft Visual Studio Group
IOType               : Standard_LRS
OperationDescription : Get-AzureVMImage
OperationId          : e2084b70-8858-3951-89f3-dfe2b1334a11
OperationStatus      : Succeeded

ImageName            : 03f55de797f546a1b29d1b8d66be687a__Visual-Studio-2013-Community-12.0.31101.0-AzureSDK-2.5-WS2012R2
OS                   : Windows
MediaLink            : 
LogicalSizeInGB      : 128
AffinityGroup        : 
Category             : Public
Location             : East Asia;Southeast Asia;Australia East;Australia Southeast;Brazil South;North Europe;West Europe;Japan East;Japan West;Central US;East US;East US 2;North Central US;South Central US;West US
Label                : Visual Studio Community 2013 on Windows Server 2012 R2
Description          : The Visual Studio Community 2013 image enables you to unleash the full power of Visual Studio to develop cross-platform solutions. Create apps in one unified IDE, and incorporate new languages, fe
                       atures, and development tools into them with Visual Studio Extensions (available in the Visual Studio Gallery).
Eula                 : http://go.microsoft.com/fwlink/?LinkId=430755
ImageFamily          : Visual Studio Community 2013 on Windows Server 2012 R2
PublishedDate        : 2014/11/12 17:00:00
IsPremium            : False
IconUri              : VisualStudio2013_45.png
SmallIconUri         : VisualStudio2013_45.png



<中略>

だが、Get-AzureVMImageの実行結果は上記の様に膨大な出力結果になる。知っている人なら何の問題もないと思うが、今回は最新の Windows Server 2012 R2(他のイメージでも良いが)のイメージ名を取得するための方法を記載する。

結論から言うと大体こんな感じ。イメージ一覧を取ってきて、イメージ名でフィルタして、更新日付でソートして最初のイメージを持ってくる。

PS C:\temp> Get-AzureVMImage | Where-Object {$_.label -like “Windows Server 2012 R2*”} | Sort-Object -Descending PublishedDate | Select-Object -Index 0

ImageName            : a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-201411.01-en.us-127GB.vhd
OS                   : Windows
MediaLink            : 
LogicalSizeInGB      : 128
AffinityGroup        : 
Category             : Public
Location             : East Asia;Southeast Asia;Australia East;Australia Southeast;Brazil South;North Europe;West Europe;Japan East;Japan West;Central US;East US;East US 2;North Central US;Sou
                       th Central US;West US
Label                : Windows Server 2012 R2 Datacenter, November 2014
Description          : At the heart of the Microsoft Cloud OS vision, Windows Server 2012 R2 brings Microsoft's experience delivering global-scale cloud services into your infrastructure. It o
                       ffers enterprise-class performance, flexibility for your applications and excellent economics for your datacenter and hybrid cloud environment. This image includes Windo
                       ws Server 2012 R2 Update.
Eula                 : 
ImageFamily          : Windows Server 2012 R2 Datacenter
PublishedDate        : 2014/11/14 17:00:00
IsPremium            : False
IconUri              : WindowsServer2012R2_45.png
SmallIconUri         : WindowsServer2012R2_45.png
PrivacyUri           : 
RecommendedVMSize    : 
PublisherName        : Microsoft Windows Server Group
IOType               : Standard_LRS
OperationDescription : Get-AzureVMImage
OperationId          : 23e313f9-87f5-3457-bc5a-ca5946801382
OperationStatus      : Succeeded

仮想マシンの作成までやってみる

上記のスクリプトを利用して仮想マシンインスタンスを作成するまでのスクリプト

# 変数全般
$cloudServiceName = 'eastasia-dmz01'
$vnetName = 'vnet01'
$imageFilterName = 'Windows Server 2012 R2*'
$instanceName = 'WebVM'
$instanceSize = 'Small'
$location = 'East Asia'
$staticIP = '10.0.0.10'
$subnetName = 'Subnet-DMZ'

# 最新版の Windows Server 2012 R2 のイメージ名を取得する
$image = Get-AzureVMImage -Verbose:$false | Where-Object {$_.label -like $imageFilterName } | Sort-Object -Descending PublishedDate | Select-Object -Index 0

# 必要な場合、利用するストレージサービスを設定
# Set-AzureSubscription -CurrentStorageAccountName "<ストレージサービス名>" -SubscriptionName "<サブスクリプション名>"

# 仮想マシン・インスタンスのプロビジョニング
$vm = New-AzureVMConfig -Name $instanceName -InstanceSize $instanceSize -ImageName $image.ImageName
Add-AzureProvisioningConfig -VM $vm -Windows -AdminUsername "<ユーザ名>" -Password "<パスワード>"

# サブネットの設定
Set-AzureSubnet -SubnetNames $subnetName -VM $vm

# 静的IPの割り振り
Set-AzureStaticVNetIP -IPAddress $staticIP -VM $vm

# 仮想マシン・インスタンスを入れるクラウドサービスを作成する
New-AzureService -ServiceName $cloudServiceName -Location $location

# 仮想マシン・インスタンスの作成
New-AzureVM -ServiceName $cloudServiceName -VNetName $vnetName -VM $vm

久しぶりに GlassFish v8.0.1 を弄ったらはまったこと

JavaEE7 が出てからしばらく、情報キャッチアップのために NetBeans を弄ったときにエラーをもらった。特に NetBeansJUnit を利用した場合のエラーがちょっとわかりにくかったので、備忘録代わりに記載しておく。

NetBeansJUnit 利用時に EJB が見つからない

JUnit を実行時、以下のエラーが発生した。

 If the jar file contains valid EJBs which are annotated with EJB component level annotations (@Stateless, @Stateful, @MessageDriven, @Singleton), please check server.log to see whether the annotations were processed properly.

もろもろ調べてみたところ、どうも NetBeans のバグらしい。Issue 11296 ejb_container New - ejb container problem の記事に記載があったのが、元となる情報が削除されていた。NetBeansJUnit 実行時にデバッグモードで実行すれば急場はしのげる。

NetBeansJUnit 利用時にデータベースに接続できない

NetBeans 側で作成したデータソースに JUnit のテストコードで接続しようとしたところ、以下のエラーが発生した。

重大: Exception while invoking class org.glassfish.persistence.jpa.JPADeployer prepare method
java.lang.RuntimeException: Invalid resource : mysample__pm
	at com.sun.enterprise.connectors.ConnectorRuntime.lookupDataSourceInDAS(ConnectorRuntime.java:593)
	at com.sun.enterprise.connectors.ConnectorRuntime.lookupPMResource(ConnectorRuntime.java:517)
	at org.glassfish.persistence.common.PersistenceHelper.lookupPMResource(PersistenceHelper.java:63)
	at org.glassfish.persistence.jpa.ProviderContainerContractInfoBase.lookupDataSource(ProviderContainerContractInfoBase.java:71)
	at org.glassfish.persistence.jpa.PersistenceUnitInfoImpl.<init>(PersistenceUnitInfoImpl.java:108)
	at org.glassfish.persistence.jpa.PersistenceUnitLoader.loadPU(PersistenceUnitLoader.java:142)
	at org.glassfish.persistence.jpa.PersistenceUnitLoader.<init>(PersistenceUnitLoader.java:107)
	at org.glassfish.persistence.jpa.JPADeployer$1.visitPUD(JPADeployer.java:223)
	at org.glassfish.persistence.jpa.JPADeployer$PersistenceUnitDescriptorIterator.iteratePUDs(JPADeployer.java:510)
	at org.glassfish.persistence.jpa.JPADeployer.createEMFs(JPADeployer.java:230)
	at org.glassfish.persistence.jpa.JPADeployer.prepare(JPADeployer.java:168)
	at com.sun.enterprise.v3.server.ApplicationLifecycle.prepareModule(ApplicationLifecycle.java:925)
	at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:434)
	at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
	at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:539)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:535)
	at java.security.AccessController.doPrivileged(Native Method)
	at javax.security.auth.Subject.doAs(Subject.java:360)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:534)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:565)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:557)
	at java.security.AccessController.doPrivileged(Native Method)
	at javax.security.auth.Subject.doAs(Subject.java:360)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:556)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1464)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1300(CommandRunnerImpl.java:109)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1846)
	at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1722)
	at com.sun.enterprise.admin.cli.embeddable.DeployerImpl.deploy(DeployerImpl.java:133)
	at com.sun.enterprise.admin.cli.embeddable.DeployerImpl.deploy(DeployerImpl.java:109)
	at org.glassfish.ejb.embedded.EJBContainerImpl.deploy(EJBContainerImpl.java:138)
	at org.glassfish.ejb.embedded.EJBContainerProviderImpl.createEJBContainer(EJBContainerProviderImpl.java:134)
	at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:127)
	at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:102)
	at todo.domain.service.todo.TodoServiceTest.setUp(TodoServiceTest.java:42)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:39)
	at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:532)
	at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1179)
	at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:1030)
Caused by: com.sun.appserv.connectors.internal.api.ConnectorRuntimeException: Invalid resource : mysample__pm
	at org.glassfish.jdbcruntime.service.JdbcDataSource.validateResource(JdbcDataSource.java:81)
	at org.glassfish.jdbcruntime.service.JdbcDataSource.setResourceInfo(JdbcDataSource.java:62)
	at org.glassfish.jdbcruntime.JdbcRuntimeExtension.lookupDataSourceInDAS(JdbcRuntimeExtension.java:136)
	at com.sun.enterprise.connectors.ConnectorRuntime.lookupDataSourceInDAS(ConnectorRuntime.java:589)
	... 59 more

調べた結果、如何問題らしい。

netbeans - Problem to deploy war in glassfish - Stack Overflow

NetBeans プロジェクトの glassfish-resources.xml に記載された内容を GlassFish 側の設定ファイルにも追記することで問題を回避できる。

Microsoft Azure の仮想ネットワーク上に複数NICの仮想マシンを配置してみる

先日は Network Security Group を紹介させて頂いたが、今回は複数NICを持つ仮想マシンを作成して操作してみたいと思う。すでに以下の様な立派な記事が記載されているが、動かしてみないとわからないところもあるので、本記事も合わせて一読してほしい。

今回利用する Multi NIC という社畜御用達な機能は、L サイズ以上の大きさのインスタンスを利用する必要がある。参考までに、インスタンスサイズによる NIC 登録数は以下になる。

インスタンスサイズ 作成可能NIC
L サイズ (A3) および A6 2
XL サイズ (A4) および A7 4
A9 2
D3 2
D4 4
D13 4

Multi NIC、仮想ネットワーク、Network Security Group を利用して構築するネットワークは以下になる。
f:id:waritohutsu:20141119151907p:plain

Multi NIC 仮想マシンの作成

早速 PowerShell を利用して環境を構築する。仮想ネットワーク、Network Security Group の作成は Microsoft AzureのNetwork Security Groupを使ってなんちゃってネットワーク設計(インスタンスのインターネットアクセス禁止とか)をする の記事を参考にして作成したうえで、以下の PowerShell コマンドを実行する。

# 仮想マシンイメージの選択(参考記事が使っていた Windows Serve 2012 R2 を選択
$image = Get-AzureVMImage -ImageName "a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-201408.01-en.us-127GB.vhd"

# インスタンスの大きさは Large を選択(Large 以上のサイズでないと、Multi NIC インスタンスを作成できない
$vm = New-AzureVMConfig -Name "MultiNicVM" -InstanceSize "Large" -Image $image.ImageName

# インスタンスのプロビジョニング情報を設定する
Add-AzureProvisioningConfig -VM $vm -Windows -AdminUsername <ユーザ名> -Password <パスワード>

# NIC0?は Subnet-DMZ に所属させる
Set-AzureSubnet -SubnetNames "Subnet-DMZ" -VM $vm
Set-AzureStaticVNetIP -IPAddress 10.0.0.11 -VM $vm

# NIC1 は Subnet-AP_DB に所属させる
Add-AzureNetworkInterfaceConfig -Name "NIC1" -SubnetName "Subnet-AP_DB" -VM $vm -StaticVNetIPAddress "10.0.1.11"

# 仮想ネットワーク vnet01 を指定し、仮想マシンを作成する
New-AzureVM -ServiceName <service name> -VNetName "vnet01" -VMs $vm

上記のコマンドを実行した仮想マシンリモートデスクトップで入り、ipconfig, route print を実行した結果は以下となる。

C:\Users\myuser>ipconfig

Windows IP Configuration


Ethernet adapter Ethernet 2:

   Connection-specific DNS Suffix  . : multiniceastjpvm.l5.internal.cloudapp.net

   Link-local IPv6 Address . . . . . : fe80::f479:f0e8:eac3:67ea%26
   IPv4 Address. . . . . . . . . . . : 10.0.0.11
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 10.0.0.1

Ethernet adapter Ethernet :

   Connection-specific DNS Suffix  . : multiniceastjpvm.l5.internal.cloudapp.net

   Link-local IPv6 Address . . . . . : fe80::a5f1:fd46:f60c:e6b7%22
   IPv4 Address. . . . . . . . . . . : 10.0.1.11
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 10.0.1.1

Tunnel adapter isatap.multiniceastjpvm.l5.internal.cloudapp.net:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . : multiniceastjpvm.l5.internal.cloudapp.net


Tunnel adapter Teredo Tunneling Pseudo-Interface:

   Connection-specific DNS Suffix  . :
   IPv6 Address. . . . . . . . . . . : 2001:0:9d38:90d7:2cc6:18f8:f5ff:fff4
   Link-local IPv6 Address . . . . . : fe80::2cc6:18f8:f5ff:fff4%15
   Default Gateway . . . . . . . . . : ::


C:\Users\myuser>route print
===========================================================================
Interface List
 31...00 0d 3a 50 05 96 ......Microsoft Hyper-V Network Adapter #8
 27...00 0d 3a 50 04 a9 ......Microsoft Hyper-V Network Adapter #7
  1...........................Software Loopback Interface 1
 15...00 00 00 00 00 00 00 e0 Teredo Tunneling Pseudo-Interface
 18...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter #2
===========================================================================

IPv4 Route Table
===========================================================================
Active Routes:
Network Destination        Netmask          Gateway       Interface  Metric
          0.0.0.0          0.0.0.0         10.0.0.1        10.0.0.11      5
          0.0.0.0          0.0.0.0         10.0.1.1        10.0.1.11      5
         10.0.0.0    255.255.255.0         On-link         10.0.0.11    261
        10.0.0.11  255.255.255.255         On-link         10.0.0.11    261
       10.0.0.255  255.255.255.255         On-link         10.0.0.11    261
         10.0.1.0    255.255.255.0         On-link         10.0.1.11    261
        10.0.1.11  255.255.255.255         On-link         10.0.1.11    261
       10.0.1.255  255.255.255.255         On-link         10.0.1.11    261
        127.0.0.0        255.0.0.0         On-link         127.0.0.1    306
        127.0.0.1  255.255.255.255         On-link         127.0.0.1    306
  127.255.255.255  255.255.255.255         On-link         127.0.0.1    306
        224.0.0.0        240.0.0.0         On-link         127.0.0.1    306
        224.0.0.0        240.0.0.0         On-link         10.0.0.11    261
        224.0.0.0        240.0.0.0         On-link         10.0.1.11    261
  255.255.255.255  255.255.255.255         On-link         127.0.0.1    306
  255.255.255.255  255.255.255.255         On-link         10.0.0.11    261
  255.255.255.255  255.255.255.255         On-link         10.0.1.11    261
===========================================================================
Persistent Routes:
  None

IPv6 Route Table
===========================================================================
Active Routes:
 If Metric Network Destination      Gateway
 15    306 ::/0                     On-link
  1    306 ::1/128                  On-link
 15    306 2001::/32                On-link
 15    306 2001:0:9d38:90d7:444:c23:f5ff:fff4/128
                                    On-link
 27    261 fe80::/64                On-link
 31    261 fe80::/64                On-link
 15    306 fe80::/64                On-link
 15    306 fe80::444:c23:f5ff:fff4/128
                                    On-link
 27    261 fe80::6c59:b544:5e61:1eaa/128
                                    On-link
 31    261 fe80::c480:65b1:30bd:2f52/128
                                    On-link
  1    306 ff00::/8                 On-link
 27    261 ff00::/8                 On-link
 31    261 ff00::/8                 On-link
 15    306 ff00::/8                 On-link
===========================================================================
Persistent Routes:
  None

上記を確認すると、Add-AzureNetworkInterfaceConfig で追加した NIC 側(10.0.1.11)と Set-AzureStaticVNetIP で登録された NIC ではメトリックの差がないことが分かる。
Network Security Group の NSG01, NSG02 ではそれぞれ仮想マシン⇒インターネットへのアクセスを禁止しているが、仮想マシンからインターネットに接続するにつなげたり繋げなかったりするという現象が起きた。これから、「追加した NIC には Network Security Group が効かない」ということが推察(確認レベルにはならない…)できる。
まだドキュメントが多い機能ではないので推察の域をできないところもあるが参考になれば幸いだ。

その他の留意点

また、Multi NIC 利用時の留意点としては以下となるので、利用前に一読してほしい。

  • Multi NIC は GA リリース(一般提供)なので、SIerも安心して使えるよ!(サポート上はね!!
  • NIC 追加による仮想マシンへの追加料金は存在しません
  • Linux でも Multi NIC は使えます
  • Multi-NIC仮想マシンはコマンド経由でしか作成できない(管理ポータルからは作成できない
  • 仮想マシンの作成後、NICの追加や削除はできない
  • 追加 NIC は負荷分散セット内で使用できない( ロードバランサ側のVIPとマッピング取れるのがデフォルトNICだけらしい
  • デフォルトNIC以外では Network Security Group や Force Tunneling が使えないので要注意
  • Multi NIC 利用時、オーダーはランダムになるらしい。以下は抜粋となるが、デフォルト NIC がどれになるかもどうもランダムっぽいような…。
The order of the NICs inside the VM will be random, but the IP addresses and the corresponding MACs will remain the same.

Microsoft AzureのNetwork Security Groupを使ってなんちゃってネットワーク設計(インスタンスのインターネットアクセス禁止とか)をする

御大ブログにて以下の新機能リリースがあった。

上記の機能のうち、Network Security Groupと呼ばれる機能の提供により、仮想ネットワーク上で簡易なネットワーク設計が可能となった。以下の様な社蓄心をくすぐる要件にこたえることが可能なナイス機能となっているので、今回はNetwork Security Groupの機能について紹介する。

Network Security Group の概要

MSDN - About Network Security Groups に概要がある。Network Security Group は仮想ネットワーク上に設定された サブネット or 仮想マシン、またはサブネットと仮想マシン両方に通信制御を可能とする機能だ。
IP アドレス、ポート、TCP/UDP プロトコルを指定して通信制御を行うため、冒頭で記載したインターネットへのアクセス禁止やインスタンス間(またはサブネット間)における通信制御が可能となる。
Network Security Group で利用可能なコマンドは以下となるため、詳細は MSDN を確認してほしい。

PS C:\Temp> Get-Command *NetworkSecurity*

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Get-AzureNetworkSecurityGroup                      Azure
Cmdlet          Get-AzureNetworkSecurityGroupConfig                Azure
Cmdlet          Get-AzureNetworkSecurityGroupForSubnet             Azure
Cmdlet          New-AzureNetworkSecurityGroup                      Azure
Cmdlet          Remove-AzureNetworkSecurityGroup                   Azure
Cmdlet          Remove-AzureNetworkSecurityGroupConfig             Azure
Cmdlet          Remove-AzureNetworkSecurityGroupFromSubnet         Azure
Cmdlet          Remove-AzureNetworkSecurityRule                    Azure
Cmdlet          Set-AzureNetworkSecurityGroupConfig                Azure
Cmdlet          Set-AzureNetworkSecurityGroupToSubnet              Azure
Cmdlet          Set-AzureNetworkSecurityRule                       Azure

また、Network Security Groupの主な制限は以下になるので、実際に利用する際には参考にして欲しい。

  • 一つのサブスクリプションに100個までNetwork Security Groupを作成できる
  • 一つのNetwork Security Groupにつき、ルールは 200個まで設定可能
  • ACL とは併用できない(Network Security Group利用時に、あらかじめ削除しておく必要がある)
  • Multi NIC を利用している場合、デフォルト NIC にしか適用されない
  • VM 単位、Subnet 単位、または両方に適用可能

試しにネットワークを設定してみる

以下のネットワークを設定してみる。
f:id:waritohutsu:20141115163958p:plain

また、あらかじめ以下の仮想ネットワーク、インスタンスが作成済みとする。

DMZ 向けの Network Security Group を適用する

以下の PowerShell スクリプトを実行する。

# DMZ用のNetwork Security Group作成
New-AzureNetworkSecurityGroup -Name "NSG01" -Label "for vnet01 DMZ NSG" -Location "Japan East"

# インターネットからの HTTP アクセスを許可
Get-AzureNetworkSecurityGroup -Name NSG01 | Set-AzureNetworkSecurityRule -Name "AllowWEBFromInternet" -Type Inbound -Priority 100 -Action Allow -SourceAddressPrefix INTERNET -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 80 -Protocol *

# インターネットからの RDP を許可
Get-AzureNetworkSecurityGroup -Name NSG01 | Set-AzureNetworkSecurityRule -Name "AllowRDPFromInternet" -Type Inbound -Priority 500 -Action Allow -SourceAddressPrefix INTERNET -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 3389 -Protocol *

# インターネットへのアクセスを禁止
Get-AzureNetworkSecurityGroup -Name NSG01 | Set-AzureNetworkSecurityRule -Name "DenyToInternet" -Type Outbound -Priority 1100 -Action Deny -SourceAddressPrefix VIRTUAL_NETWORK -SourcePortRange * -DestinationAddressPrefix INTERNET -DestinationPortRange * -Protocol *

# NSG01 の情報を表示(なくても勝手に表示されますが、一応
Get-AzureNetworkSecurityGroup -Name NSG01 -Detailed

# Subnet-DMZ へのNetwork Security Groupの適用
Get-AzureNetworkSecurityGroup NSG01 | Set-AzureNetworkSecurityGroupToSubnet -SubnetName Subnet-DMZ -VirtualNetworkName vnet01

# Subnet-DMZ へのNetwork Security Groupへの適用の確認
Get-AzureNetworkSecurityGroupForSubnet -SubnetName Subnet-DMZ -VirtualNetworkName vnet01 -Detailed

以下の実行結果が出力されるはずだ。

Name  : NSG01
Rules :

           Type: Inbound

        Name                 Priority  Action   Source Address  Source Port R Destination Addr Destination Po Protocol
                                                Prefix          ange          ess Prefix       rt Range
        ----                 --------  ------   --------------- ------------- ---------------- -------------- --------
        AllowWEBFromInternet 100       Allow    INTERNET        *             *                80             *
        AllowRDPFromInternet 500       Allow    INTERNET        *             *                3389           *
        ALLOW VNET INBOUND   65000     Allow    VIRTUAL_NETWORK *             VIRTUAL_NETWORK  *              *
        ALLOW AZURE LOAD BAL 65001     Allow    AZURE_LOADBALAN *             *                *              *
        ANCER INBOUND                           CER
        DENY ALL INBOUND     65500     Deny     *               *             *                *              *


           Type: Outbound

        Name                 Priority  Action   Source Address  Source Port R Destination Addr Destination Po Protocol
                                                Prefix          ange          ess Prefix       rt Range
        ----                 --------  ------   --------------- ------------- ---------------- -------------- --------
        DenyToInternet       1100      Deny     VIRTUAL_NETWORK *             INTERNET         *              *
        ALLOW VNET OUTBOUND  65000     Allow    VIRTUAL_NETWORK *             VIRTUAL_NETWORK  *              *
        ALLOW INTERNET OUTBO 65001     Allow    *               *             INTERNET         *              *
        UND
        DENY ALL OUTBOUND    65500     Deny     *               *             *                *              *
AP/DB 向けの Network Security Group を適用する

以下の PowerShell スクリプトを実行する。

# APサーバ、DBサーバ用のセキュリティグループ作成
New-AzureNetworkSecurityGroup -Name "NSG02" -Label "for vnet01 AP_DB NSG" -Location "Japan East"

# インターネットからの HTTP アクセスを許可
Get-AzureNetworkSecurityGroup -Name NSG02 | Set-AzureNetworkSecurityRule -Name "AllowWEBFromInternet" -Type Inbound -Priority 100 -Action Allow -SourceAddressPrefix INTERNET -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 80 -Protocol *

# インターネットからの RDP を許可
Get-AzureNetworkSecurityGroup -Name NSG02 | Set-AzureNetworkSecurityRule -Name "AllowRDPFromInternet" -Type Inbound -Priority 500 -Action Allow -SourceAddressPrefix INTERNET -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 3389 -Protocol *

# インターネットへのアクセスを禁止
Get-AzureNetworkSecurityGroup -Name NSG02 | Set-AzureNetworkSecurityRule -Name "DenyToInternet" -Type Outbound -Priority 1100 -Action Deny -SourceAddressPrefix VIRTUAL_NETWORK -SourcePortRange * -DestinationAddressPrefix INTERNET -DestinationPortRange * -Protocol *

# DMZ Subnet(NSG01) へのアクセスを禁止
Get-AzureNetworkSecurityGroup -Name NSG02 | Set-AzureNetworkSecurityRule -Name "DenyToNSG01" -Type Outbound -Priority 2000 -Action Deny -SourceAddressPrefix 10.0.1.0/24 -SourcePortRange * -DestinationAddressPrefix 10.0.0.0/24 -DestinationPortRange * -Protocol *

# DMZ Subnet(NSG01) からのアクセスを許可
Get-AzureNetworkSecurityGroup -Name NSG02 | Set-AzureNetworkSecurityRule -Name "AllowFromNSG01" -Type Inbound -Priority 2100 -Action Allow -SourceAddressPrefix 10.0.0.0/24 -SourcePortRange * -DestinationAddressPrefix 10.0.1.0/24 -DestinationPortRange * -Protocol *

# Subnet-AP_DB へのNetwork Security Groupの適用
Get-AzureNetworkSecurityGroup NSG02 | Set-AzureNetworkSecurityGroupToSubnet -SubnetName Subnet-AP_DB -VirtualNetworkName vnet01

# Subnet-AP_DB へのNetwork Security Groupへの適用の確認
Get-AzureNetworkSecurityGroupForSubnet -SubnetName Subnet-AP_DB -VirtualNetworkName vnet01 -Detailed

以下の実行結果が出力されるはずだ。

Name  : NSG02
Rules :

           Type: Inbound

        Name                 Priority  Action   Source Address  Source Port R Destination Addr Destination Po Protocol
                                                Prefix          ange          ess Prefix       rt Range
        ----                 --------  ------   --------------- ------------- ---------------- -------------- --------
        AllowWEBFromInternet 100       Allow    INTERNET        *             *                80             *
        AllowRDPFromInternet 500       Allow    INTERNET        *             *                3389           *
        AllowFromNSG01       2100      Allow    10.0.0.0/24     *             10.0.1.0/24      *              *
        ALLOW VNET INBOUND   65000     Allow    VIRTUAL_NETWORK *             VIRTUAL_NETWORK  *              *
        ALLOW AZURE LOAD BAL 65001     Allow    AZURE_LOADBALAN *             *                *              *
        ANCER INBOUND                           CER
        DENY ALL INBOUND     65500     Deny     *               *             *                *              *


           Type: Outbound

        Name                 Priority  Action   Source Address  Source Port R Destination Addr Destination Po Protocol
                                                Prefix          ange          ess Prefix       rt Range
        ----                 --------  ------   --------------- ------------- ---------------- -------------- --------
        DenyToInternet       1100      Deny     VIRTUAL_NETWORK *             INTERNET         *              *
        DenyToNSG01          2000      Deny     10.0.1.0/24     *             10.0.0.0/24      *              *
        ALLOW VNET OUTBOUND  65000     Allow    VIRTUAL_NETWORK *             VIRTUAL_NETWORK  *              *
        ALLOW INTERNET OUTBO 65001     Allow    *               *             INTERNET         *              *
        UND
        DENY ALL OUTBOUND    65500     Deny     *               *             *                *              *

しかして

上記で設定は完了だが、設定がうまく反映されたり反映が遅れたりする場合があるようだ。MSDN にも以下の記載があり、反映に数分はかかることが記載されているが、体感で30分以上の時間が過ぎたにも関わらず設定が反映されていない場合もあった(逆に、ルールを変更したら即座に反映された場合もあった)ので、利用時には注意してほしい。

Associating an NSG to a VM - When a NSG is directly associated to a VM, the Network access rules in the NSG are directly applied to all traffic that is destined to the VM. Whenever the NSG is updated for rule changes, the changes are reflected in the traffic handling within minutes. When the NSG is dis-associated from the VM, the state goes back to whatever it was before the NSG, i.e. the system defaults before the introduction if NSG will be used.
Associating an NSG to a Subnet - When a NSG is associated to a subnet, the Network access rules in the NSG are applied to all the VMs in the subnet. Whenever the access rules in the NSG are updated the changes are applied to all Virtual machines in the subnet within minutes.