はじめに
コンテナにロジックを閉じ込め任意のインスタンスで動かすことができるAWS Batchが便利で使っていたのですが、今の案件はAzureでしたのでAzure Batchを使ってみました。これもコンセプトはAWSと同じです。
Azureは相変わらずドキュメントやQiitaなどにもあまり記事がないので少しでもお役に立てれば幸いです。
ちなみに今回JavaSDKで実装していますが、画面上からバッチの定義を行ったり他の言語のSDKを使った場合も同じ設定値に設定することで動かすことができますので参考になるかと思います。
Azure Batch
AzureBatchでは任意のプログラム(Shellも)を、指定された大きさのインスタンスで動かすことが可能なサービスです。
AWS Batchのようにコンテナ内のプログラムを動かすことが可能です。ちなみにAWS(※)ではCloudWatchEventsを使ってスケジュールさせていましたが、AzureBatchではスケジュール機能が組み込まれています。
※ AWSの場合は、CloudWatchEvents -> Lambda -> AWS Batchって感じで動かす必要があります
前準備
コンテナで動かすプログラムはこんな感じです。
ちゃんと指定されたインスタンスで動いているかどうか確認するためにOSなどの情報を出してみましょう。
ちなみにJavaでOSの情報をだすにはhttps://github.com/oshi/oshiが便利ですよ。
public class BatchMain {
public static void main(String[] args) {
System.out.println("execute main method.");
System.out.println("Environment Variables");
System.getenv().forEach((k,v) -> System.out.println(k + ":" + v));
var si = new SystemInfo();
System.out.println("OS: " + si.getOperatingSystem());
var hardware = si.getHardware();
System.out.println("Processor: " + hardware.getProcessor());
var memory = hardware.getMemory();
System.out.println("Memory: " + memory.getAvailable() + " / " + memory.getTotal());
System.exit(0);
}
}
さてこれをAzure Container Registryにpushしていきます。いつも通りjibを使ってpushします。
gradle.propertiesにUSERNAMEとPASSWORDを忘れずに書いておいてください。
jib {
to {
image = 'xxxxx.azurecr.io/batch:0.0.1-SNAPSHOT'
auth {
username = "${USERNAME}"
password = "${PASSWORD}"
}
}
}
gradlew jib
Azure Batchのアカウントを作る
「batch accounts」を選択します。
batch アカウントを作りましょう。
batch account, url, primary access keyをコピーしておきます。これはプログラムで指定するときに利用します。
プールを作る
Azure Batchでは実行環境の定義をPoolで行います。プールの設定はジョブごとに作る必要はなく環境が変わらないのであれば一つ大丈夫です。AWSでいうとComputing Environmentかな。
サポートされる仮想マシンイメージ(Linux)はこちらに記載がありますがcentos or ubuntuのみになります。
自分で用意することももちろん可能です。
指定する値はAzureの管理コンソールから探したほうが簡単でしょう
public class Pool {
public static final String POOL_ID = "pool_1";
public static void main(String[] args) throws IOException {
BatchClient client = BatchClient.open(new BatchSharedKeyCredentials(
"https://xxxxx.japaneast.batch.azure.com",
"xxxx",
"xxxxxx"));
ContainerRegistry containerRegistry = new ContainerRegistry()
.withRegistryServer("xxxxxx.azurecr.io")
.withUserName("xxxxxx")
.withPassword("xxxxxxxxx");
ContainerConfiguration containerConfiguration = new ContainerConfiguration();
containerConfiguration.withContainerRegistries(Arrays.asList(containerRegistry));
containerConfiguration.withContainerImageNames(Arrays.asList("xxxx.azurecr.io/batch:0.0.1-SNAPSHOT"));
ImageReference imageReference = new ImageReference();
imageReference.withPublisher("microsoft-azure-batch");
imageReference.withOffer("ubuntu-server-container");
imageReference.withSku("16-04-lts");
imageReference.withVersion("latest");
// VM Configuration
VirtualMachineConfiguration virtualMachineConfiguration = new VirtualMachineConfiguration();
virtualMachineConfiguration.withImageReference(imageReference);
virtualMachineConfiguration.withContainerConfiguration(containerConfiguration);
virtualMachineConfiguration.withNodeAgentSKUId("batch.node.ubuntu 16.04");
// Create Pool
client.poolOperations().createPool(POOL_ID, "standard_d1_v2", virtualMachineConfiguration, 1);
}
}
シンプルに動かす
Poolで定義した情報をもとにJOB、Taskを動かしてみます。
まずはJOBを作ります。
public class SimpleJob {
public static final String POOL_ID = "pool_1";
public static final String JOB_ID = "job_1";
public static void main(String[] args) throws IOException {
BatchClient client = BatchClient.open(new BatchSharedKeyCredentials(
"https://xxxxx.japaneast.batch.azure.com",
"xxxx",
"xxxxxx"));
PoolInformation poolInformation = new PoolInformation();
poolInformation.withPoolId(POOL_ID);
// CreateJob
client.jobOperations().createJob(JOB_ID, poolInformation);
}
}
JOB内でタスクを動かします。
public class SimpleTask {
public static void main(String[] args) throws IOException, InterruptedException {
BatchClient client = BatchClient.open(new BatchSharedKeyCredentials(
"https://xxxxx.japaneast.batch.azure.com",
"xxxx",
"xxxxxx"));
TaskAddParameter parameter = new TaskAddParameter();
parameter.withId("task1")
.withUserIdentity(new UserIdentity().withAutoUser(new AutoUserSpecification().withElevationLevel(ElevationLevel.ADMIN).withScope(AutoUserScope.TASK)))
.withContainerSettings(new TaskContainerSettings().withImageName("xxxx.azurecr.io/batch:0.0.1-SNAPSHOT")
.withContainerRunOptions("--rm"))
.withConstraints(new TaskConstraints().withMaxTaskRetryCount(-1)).withCommandLine("");
client.taskOperations().createTask(ScheduleJob.JOB_ID, parameter);
long timeout = 300 * 1000;
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime <= timeout) {
CloudTask task = client.taskOperations().getTask(ScheduleJob.JOB_ID, parameter.id());
if (task.state() != TaskState.COMPLETED) {
Thread.sleep(1000);
}
}
}
}
これを実行するとAzureの管理画面上で確認できます。
動いたJOBをクリックすると、Taskの画面が表示され出力された内容は画面上から見ることができます。
指定された時間にJOBを実行する
JOBの時間をwithDoNotRunUntilで指定します。JOBが実行されると動くタスクも合わせて定義します。
public class ScheduleJob {
public static final String POOL_ID = "pool_1";
public static final String JOB_ID = "job_5";
public static void main(String[] args) throws IOException {
BatchClient client = BatchClient.open(new BatchSharedKeyCredentials(
"https://xxxxx.japaneast.batch.azure.com",
"xxxx",
"xxxxxx"));
// Create Schedule SimpleJob
DateTime scheduleDateTime = new DateTime(2019,8,22,4,30, DateTimeZone.UTC);
Schedule schedule = new Schedule().withDoNotRunUntil(scheduleDateTime);
// pool
PoolInformation poolInformation = new PoolInformation();
poolInformation.withPoolId(POOL_ID);
// container
JobManagerTask task = new JobManagerTask();
task.withId("scheduletask")
.withUserIdentity(new UserIdentity().withAutoUser(new AutoUserSpecification().withElevationLevel(ElevationLevel.ADMIN).withScope(AutoUserScope.TASK)))
.withContainerSettings(new TaskContainerSettings().withImageName("xxxxx.azurecr.io/batch:0.0.1-SNAPSHOT")
.withContainerRunOptions("--rm"))
.withCommandLine("");
JobSpecification jobSpecification = new JobSpecification().withPoolInfo(poolInformation).withJobManagerTask(task);
client.jobScheduleOperations().createJobSchedule(JOB_ID,schedule, jobSpecification);
}
}
指定された時間から繰り返し動かす(Cron)
初回に動く時刻をDoNotRunUntilで指定し、RecurrenceIntervalで繰り返し動かす時間を指定します。
DoNotRunUntilを指定しない場合は、JOBを定義された時間から繰り返し動かすことになります。
public class CronScheduleJob {
public static final String POOL_ID = "pool_1";
public static final String JOB_ID = "job_7";
public static void main(String[] args) throws IOException {
BatchClient client = BatchClient.open(new BatchSharedKeyCredentials(
"https://xxxxx.japaneast.batch.azure.com",
"xxxx",
"xxxxxx"));
// Create Schedule SimpleJob
DateTime startDateTime = new DateTime(2019,8,22,4,45, DateTimeZone.UTC);
Period period = new Period(0,1,0,0);
Schedule schedule = new Schedule().withRecurrenceInterval(period).withDoNotRunUntil(startDateTime);
// pool
PoolInformation poolInformation = new PoolInformation();
poolInformation.withPoolId(POOL_ID);
// container
JobManagerTask task = new JobManagerTask();
task.withId("cronscheduletask")
.withUserIdentity(new UserIdentity().withAutoUser(new AutoUserSpecification().withElevationLevel(ElevationLevel.ADMIN).withScope(AutoUserScope.TASK)))
.withContainerSettings(new TaskContainerSettings().withImageName("xxxxx.azurecr.io/batch:0.0.1-SNAPSHOT")
.withContainerRunOptions("--rm"))
.withCommandLine("");
JobSpecification jobSpecification = new JobSpecification().withPoolInfo(poolInformation).withJobManagerTask(task);
client.jobScheduleOperations().createJobSchedule(JOB_ID,schedule, jobSpecification);
}
}