Raspbian に Java 9 をインストール

Raspbian の Oracle Java 8 のバージョンを確認してみます。

$ sudo -s
# apt update
# apt search oracle-java8-jdk

oracle-java8-jdk/stable 8u65 armhf
  Java™ Platform, Standard Edition 8 Development Kit

合わせて、OpenJDK の方も確認してみます。

# apt search openjdk-8-jdk

default-jdk/stable 2:1.8-58+b2 armhf
  Standard Java or Java compatible Development Kit

default-jdk-headless/stable 2:1.8-58+b2 armhf
  Standard Java or Java compatible Development Kit (headless)

openjdk-8-jdk/stable 8u171-b11-1~deb9u1 armhf
  OpenJDK Development Kit (JDK)

openjdk-8-jdk-headless/stable 8u171-b11-1~deb9u1 armhf
  OpenJDK Development Kit (JDK) (headless)

OpenJDK の方は、8u171 なのに対して、もう一方は 8u65 ともう更新されることはないような雰囲気です。

今回、Java 8 にこだわるつもりもないので、最新をと思いたちましたが、まだ Java 10 はなかったので、Java 9 をインストールすることにしました。

# apt search jdk
# apt install openjdk-9-jdk -y

openjdk-9-jdk をインストール後、以前のバージョンの JDK は削除したかったので、find /usr -name 'java'dpkg -l | grep gcjdpkg -l | grep jdk などして、不要となったパッケージが残っていないか確認して、apt removeで削除しました。

Java 8 と Java 10 での MixerInfo の並び順の違い

macOSJDKのインストールにsdkmanを使っての話ですが、Java 8では声を出してくれていたコードが、Java 10で実行すると喋らないなと悩んでいたのですが、 単純にAudioSystem.getMixerInfo()が返す順序が変わっていただけということだった。

確認するためのコードは、こんな感じ

package jp.pigumer.javasound

import javax.sound.sampled.AudioSystem

object MixerInfoList extends App {

  val info = AudioSystem.getMixerInfo()
  info.zipWithIndex.foreach {
    case (mi, index) ⇒
      println(s"$index: ${mi.getName}")
  }
}

Java 8(Oracle JDK)

java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
0: Default Audio Device
1: Built-in Microphone
2: Built-in Output
3: Port Built-in Microphone
4: Port Built-in Output

Java 10(Oracle JDK)

java version "10.0.1" 2018-04-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
0: Port Built-in Microphone
1: Port Built-in Output
2: Default Audio Device
3: Built-in Microphone
4: Built-in Output

Java 10(OpenJDK)

openjdk version "10" 2018-03-20
OpenJDK Runtime Environment 18.3 (build 10+46)
OpenJDK 64-Bit Server VM 18.3 (build 10+46, mixed mode)
0: Port Built-in Microphone
1: Port Built-in Output
2: Default Audio Device
3: Built-in Microphone
4: Built-in Output

ECS タスクを Fargate でスケジュール起動

Fargate で ECS タスクを起動するためには、launchType で "FARGATE"、ネットワーク設定で awsvpc として、サブネット、セキュリティグループ等を設定する必要があります。 これらのことが、CloudWatch Event の Rule でまだ設定することができないようです。

そのため、以下のような Lambda Function を作成して、Lambda で タスクを起動するようにしてみました。

import boto3

def lambda_handler(event, context):
    client = boto3.client('ecs')

    response = client.run_task(
    cluster = '${ClusterArn}',
    taskDefinition = '${TaskArn}',
    count = 1,
    launchType = 'FARGATE',
    networkConfiguration = {
        'awsvpcConfiguration': {
        'subnets': [
            '${Subnet1}',
            '${Subnet2}'
        ],
        'securityGroups': [
            '${SecurityGroup}'
        ],
        'assignPublicIp': 'ENABLED'
        }
    }
    )

    print(response)

    return 'Success'

run_task の cluster には、ECS Cluster の Arn を、taskDefinition には TaskDefinition の Arn が必要になります。 また、サブネット名、セキュリティグループ名も必要です。これらを、CloudFormation のテンプレートで、Export された値を ImportValue してデプロイするように、プロジェクトを作成してみました。

Fargate がついに東京に

Service

首を長くして待ってましたが、ついに Fargate が東京にやってきました。 ということで、以前米国東部 (バージニア北部) に作っていたものを、 東京(ap-northeast-1)に変えて実行してみました。

GitHub - PigumerGroup/aws-ecs-fargate

Task

polly で mp3 を生成してそれを S3 に保存する Task を Fargate で実行できるようにしてみました。

GitHub - PigumerGroup/sbt-aws-cloudformation-example

両方とも、ちゃんと実行できましたぁ。うれしい。

これからは、どんどん ECS を使っていきたい気持ちです。

スレッド数を CloudWatch のカスタムメトリクスとして Publish する

micrometer を使ってスレッド数を CloudWatch のカスタムメトリクスとして Publish してみました

// https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-cloudwatch
libraryDependencies += "io.micrometer" % "micrometer-registry-cloudwatch" % "1.0.5"
package com.pigumer.example;

import java.util.Properties;

public class MicrometerProperties {

    public Properties DEFAULT;

    public MicrometerProperties() {
        DEFAULT = new Properties();
        DEFAULT.setProperty("cloudwatch.enabled", "true");
        DEFAULT.setProperty("cloudwatch.namespace", "MICROMETER");
        DEFAULT.setProperty("cloudwatch.step", "PT1M");
        DEFAULT.setProperty("cloudwatch.batchSize", "1");
    }
}
package com.pigumer.example;

import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsync;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsyncClientBuilder;
import io.micrometer.cloudwatch.CloudWatchConfig;
import io.micrometer.cloudwatch.CloudWatchMeterRegistry;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;

import java.time.Duration;
import java.util.Properties;
import java.util.stream.IntStream;

public class Main {
    private static AmazonCloudWatchAsync cloudWatch =
         AmazonCloudWatchAsyncClientBuilder.standard().withRegion("ap-northeast-1").build();
    private static Clock clock = Clock.SYSTEM;
    private static CloudWatchConfig config =
            new CloudWatchConfig() {
                private Properties properties =
                        new MicrometerProperties().DEFAULT;
                @Override
                public String get(String key) {
                    return properties.getProperty(key);
                }
            };

    private static Runnable sleep = () -> {
        try {
            Thread.sleep(60000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    public static void main(String[] args) throws Exception {
        CloudWatchMeterRegistry registry =
                new CloudWatchMeterRegistry(config, clock, cloudWatch);
        new JvmThreadMetrics().bindTo(registry);

        IntStream.range(1, 20).forEach(n -> new Thread(sleep).run());

        Thread.sleep(60000 * 3);
    }
}

スレッド数を CloudWatch のカスタムメトリクスとして Publish する

package jp.pigumer.monitor;

import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsync;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsyncClientBuilder;
import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.MetricDatum;
import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest;
import com.amazonaws.services.cloudwatch.model.StandardUnit;

import java.lang.management.ManagementFactory;
import java.util.Date;

public class CloudWatchReporter implements Runnable {

    private AmazonCloudWatchAsync cloundwatch = AmazonCloudWatchAsyncClientBuilder.standard().build();
    private String id;

    public CloudWatchReporter(String id) {
        this.id = id;
    }

    @Override
    public void run() {
        Double count = (double) ManagementFactory.getThreadMXBean().getThreadCount();
        MetricDatum metricData = new MetricDatum()
                .withDimensions(new Dimension().withName("Id").withValue(id))
                .withMetricName("thread_count")
                .withUnit(StandardUnit.Count)
                .withValue(count)
                .withTimestamp(new Date());
        PutMetricDataRequest request = new PutMetricDataRequest()
                .withNamespace("CUSTOM/TEST")
                .withMetricData(metricData);
        cloundwatch.putMetricDataAsync(request);
    }
}

この CloudWatchReporter を 1 分間隔で実行して CloudWatch に Publish します。

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    ScheduledFuture handler = scheduler.scheduleAtFixedRate(
            new CloudWatchReporter("test"),
            0,
            1,
            TimeUnit.MINUTES);

f:id:section27:20180612234607p:plain

GitHub - takesection/jmxmonitor

TOTP - Time-Based One-Time Password

TOTP について調べるため、AWS の MFA で使うことのできる 仮想 MFA アプリケーションと同様の機能を持つアプリケーションを作成してみました。

TOTP の仕様

使い方

ユーザのホームディレクトリに .onetime というディレクトリを作成し、そのディレクトリに onetime.properties というファイルを作成します。

$ mkdir ~/.onetime
$ cat > ~/.onetime/onetime.properties

onetime.properties には、AWS の MFA を有効にしたときに表示されるシークレットキーを設定します。

secret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX