- 印刷する
- PDF
コアデバイスの設定
- 印刷する
- PDF
Classic/VPC環境で利用できます。
コアデバイスの設定では、コアソフトウェアを実行するエッジデバイスとしてメッセージとデータを処理します。
コアデバイス画面
コアデバイスの画面構成は次の通りです。
項目 | 説明 |
---|---|
① コアデバイスの設定 | コアデバイス設定のご案内 |
② コアデバイスの検索 | コアデバイス名で検索 |
③ 削除ボタン | コアデバイスの削除 |
④ リスト | コアデバイスリスト |
⑤ 詳細情報の配布タブ | コアデバイスの最新配布 |
⑥ 詳細情報のクライアントデバイスタブ | コアデバイスで設定したクライアントデバイスリスト |
⑦ 詳細情報のエンドポイントタブ | クライアントがアクセスするコアデバイスのエンドポイント |
事前システム要件
コアデバイス設定時に必要な事項を説明します。
Cloud IoT Coreリソース
コアデバイスの設定で必要な Cloud IoT Coreリソースは次の通りです。
- 仮想デバイス: 既に作成された仮想デバイスと同一名でコアデバイスを作成します。
- 認定書: Edgeコアソフトウェアは、コアデバイスで設定した仮想デバイスに連結された認定書を使用して Cloud IoT Coreと通信します。
Edgeコアソフトウェアは、迅速な設置のために Cloud IoT Coreリソースを自動で作成するプロビジョン機能を提供します。
IAM認証キー
認証キーはコアデバイスが必要なリソースをプロビジョンし、クラウドサービスと相互作用する際に使用します。必要な IAM認証キーおよび、ポリシー権限の説明は次の通りです。
ポリシー名 | ポリシー説明 |
---|---|
NCP_CLOUD_IOT_CORE_MANAGER | Cloud IoT Coreサービス内のすべての機能を利用できる権限 |
IAM認証キーを環境変数で提供するために以下のコマンドをデバイスターミナルにコピーし、「=」記号の次にあるテキストを指定してから情報を変更します。
export NCLOUD_ACCESS_KEY_ID=<NCLOUD_ACCESS_KEY_ID>
export NCLOUD_SECRET_KEY=<NCLOUD_SECRET_KEY>
Java
Edgeコアソフトウェアは、8バージョン以上の Javaランタイムが必要です。
- Linuxで Open JDKを設置するコマンド例は次の通りです。
sudo apt-get install openjdk-8-jdk
- 設置された Javaのバージョンを確認するコマンドは次の通りです。
java -version
コアデバイスの設定
コアデバイスを設定する方法は次の通りです。
NAVERクラウドプラットフォームのコンソールで、Services > Internet of Things > Cloud IoT Coreメニューを順にクリックします。
IoT Edge > Core Devicesメニューを順にクリックします。
[コアデバイスの設定] ボタンをクリックします。
コアデバイスとして設定する仮想デバイスを選択します。
- 新規作成を選択すると入力した名前で作成された仮想デバイスが、コアデバイスとして設定されます。
Edgeコアソフトウェアをダウンロードして設定します。
- ダウンロード後、圧縮解除の位置(-dオプション)はユーザーが指定でき、「入力した経路/edgecore/」下位にソフトウェアファイルが圧縮解除されます。
curl -sL 'https://github.com/NaverCloudPlatform/iot-edge/releases/latest/download/edgecore.zip' -o edgecore.zip && unzip edgecore.zip -d /
- ダウンロード後、圧縮解除の位置(-dオプション)はユーザーが指定でき、「入力した経路/edgecore/」下位にソフトウェアファイルが圧縮解除されます。
次のようなコマンドを使用して Edgeコアソフトウェアを実行します。
sudo -E java -Droot="/edgecore" -jar /edgecore/lib/edgecore.jar --virtual-device-name <CoreDeviceName> --setup-system-service true --provision true
Edgeコアソフトウェアで提供する実行オプションは次の通りです。
オプション 説明 デフォルト値 --h Edgeコアソフトウェア実行オプションのヘルプを表示 - --virdual-device-name, --vdn コアデバイスで使用する仮想デバイスの名前。仮想デバイスが存在しない場合、Edgeコアソフトウェアで作成 CoreDevice-{任意の文字列} --start true入力時に Edgeコアソフトウェアプロセスを即開始 true -- provision, -p コアデバイスの設定で必要なリソースプロビジョン。 - trueの場合: Edgeコアソフトウェアがクラウドに仮想デバイスと認定書を作成して連結
- falseの場合、仮想デバイスまたは、認定書が存在しなかったり連結が正しくない場合: Edgeコアソフトウェアが終了
false -- setup-system-service trueの場合: Edgeコアソフトウェアをデバイスが起動する時に実行するシステムサービスとして設定 false
[リストを見る] ボタンをクリックします。
- IoTデバイスに設定が完了すると、数分以内にリストでコアデバイスを確認できます。
コアデバイスの削除
これ以上使用しなくなった仮想デバイスは削除できます。コアデバイスを削除する方法は次の通りです。
Edgeコアソフトウェアを削除します。
- Edgeコアソフトウェアをシステムサービス(systemctl)で実行した場合、サービスを停止/日活性化した後に削除します。以下のコマンドを実行して Edgeコアソフトウェアを削除します。
//サービス停止、日活性化、削除 sudo systemctl stop edgecore.service sudo systemctl disable edgecore.service sudo rm /etc/systemd/system/edgecore.service
NAVERクラウドプラットフォームのコンソールで、Services > Internet of Things > Cloud IoT Coreメニューを順にクリックします。
IoT Edge > Core Devicesメニューを順にクリックします。
コアデバイスリストで削除するコアデバイスをクリックして選択します。
[削除] ボタンをクリックします。
ポップアップウィンドウが表示されたら、[削除] ボタンをクリックします。
クライアントデバイスの管理
クライアントデバイスは、コアデバイスのローカルメッセージブローカーにアクセスできる仮想デバイスです。事前に設定されたクライアントデバイスのみ該当コアデバイスにアクセスできます。Core Devicesメニューで該当コアデバイスに連結するクライアントデバイスを一目で確認できます。
1つの仮想デバイスをクライアントデバイスとして複数のコアデバイスに設定できます。
クライアントデバイスの設定
クライアントデバイスを設定する方法は次の通りです。
NAVERクラウドプラットフォームのコンソールで、Services > Internet of Things > Cloud IoT Coreメニューを順にクリックします。
IoT Edge > Core Devicesメニューを順にクリックします
コアデバイスリストでコアデバイスをクリックして選択します。
[クライアントデバイスの設定] ボタンをクリックします。
該当のコアデバイスに連結させるクライアントデバイスをクリックして選択します。
クライアントデバイスを名前で検索します
- [保存] ボタンをクリックします。
クライアントデバイスの分離
コアデバイスに設定されたクライアントデバイスを分離する方法は次の通りです。
- NAVERクラウドプラットフォームのコンソールで、Services > Internet of Things > Cloud IoT Coreメニューを順にクリックします。
- IoT Edge > Core Devicesメニューを順にクリックします
- コアデバイスリストでコアデバイスをクリックして選択します。
- [クライアントデバイスの設定] ボタンをクリックします。
- 分離するクライアントデバイスをクリックしてチェックボックスを解除します。
- [保存] ボタンをクリックします。
エンドポイントの管理
クライアントデバイスがコアデバイスのローカルメッセージブローカーにメッセージを送信するためには、エンドポイントを知る必要があります。それぞれのコアデバイスのエンドポイント情報は、クラウド上で管理します。
エンドポイントの設定
Edgeコアソフトウェアは、コアデバイスのエンドポイント変更を自動で感知してクラウドにアップデートします。
エンドポイントが変更されるとクラウドにアップデートした後、Edgeコアソフトウェアが再始動します。
エンドポイントの照会
仮想デバイスは Discovery APIを使用して該当の仮想デバイスがクライアントデバイスとして設定されたコアデバイスのエンドポイントを照会できます。Discovery APIに関する詳細な情報は Discovery API文書をご参照ください。
コンソールでもそれぞれのコアデバイスのエンドポイントを確認できます。
クライアントデバイスをコアデバイスに接続
クライアントデバイスはコアデバイス内のローカルメッセージを通じて他のクライアントデバイスとメッセージをやり取りできます。クライアントデバイスが接続できるコアデバイスを検索して必要な情報を照会するには Discovery APIが使用されます。
- クライアントデバイスの設定を参照して、接続したいコアデバイスにクライアントデバイスを設定します。
- エンドポイントの照会を参照してコアデバイスのエンドポイントが存在するかを確認します。
- クライアントデバイスは、コアデバイスのエンドポイントにアクセスできなければなりません。
- 認定書の接続を参照して Cloud IoT Coreの認定書をクライアントデバイスと接続してダウンロードし、クライアントデバイスにコピーします。
- クライアントデバイスで Discoveryユースケースコードを実行します。
- このサンプルでは、1つのクライアントデバイスが同じトピックをコアデバイスに発行して購読します。
Java
Discoveryユースケースコードを実行させるためのライブラリーは、次の通りです。
- maven
<dependencies>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${version}</version>
</dependency>
</dependencies>
- Java Discoveryのユースケースコードは次の通りです。
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
public class DiscoveryExample {
/*
*** Modify device name, file path and authentication key ***
*/
static String deviceName = "<DEVICE_NAME>}";
static String clientCaChainFilePath = "/<Your>/<file>/<path>/caChain.pem";
static String clientCertFilePath = "/<Your>/<file>/<path>/thingCert.crt";
static String clientKeyFilePath = "/<Your>/<file>/<path>/privKey.key";
static String accessKey = "<NCLOUD_ACCESS_KEY_ID>";
static String secretKey = "<NCLOUD_SECRET_KEY>}";
static String topic = "/java/action/topic";
static String message = String.format("{ \"name\": \"device\", \"tempC\": \"15\", \"eventTime\": \"%d\"}", Instant.now().toEpochMilli());
public static final String X_NCP_IAM_ACCESS_KEY = "x-ncp-iam-access-key";
public static final String X_NCP_APIGW_TIMESTAMP = "x-ncp-apigw-timestamp";
public static final String X_NCP_APIGW_SIGNATURE_V2 = "x-ncp-apigw-signature-v2";
public static final String X_NCP_IOT_HOST = "cloudiotcore.apigw.ntruss.com";
public static void main(String[] args) throws UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException, MqttException, URISyntaxException {
DiscoveryExample app = new DiscoveryExample();
MqttClient mqttClient = app.getClientFromDiscovery(deviceName);
System.out.println("==== Successfully Connected");
mqttClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
System.out.println("==== Connection Lost");
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("<<< Subscribed from Local MQTT Broker Server. topic: " + topic + ", message: " + message.toString());
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println(">>> Published to Local MQTT Broker Server");
}
});
boolean isConnected = mqttClient.isConnected();
if (isConnected) {
mqttClient.subscribe(topic);
}
if (isConnected) {
for (int i = 5; i < 10; i++) {
Random r = new Random();
MqttMessage mqttMessage = new MqttMessage(message.getBytes());
mqttMessage.setQos(0);
mqttMessage.setRetained(false);
try {
MqttTopic mqttTopic = mqttClient.getTopic(topic);
MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
token.waitForCompletion();
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
mqttClient.disconnect();
mqttClient.close();
}
private DiscoveryExample() {
init();
}
private MqttClient getClientFromDiscovery(String deviceName) throws MqttException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException, URISyntaxException {
String clientId = deviceName;
//discovery request
URI uri = new URIBuilder()
.setScheme("https")
.setHost(X_NCP_IOT_HOST)
.setPath(String.format("/iotdeviceedge/v1/discover/device/%s", deviceName))
.build();
Header[] headers = makeHeaders("GET", uri.getRawPath(), Instant.now().toEpochMilli(), accessKey, secretKey);
OpenApiResponse<EdgeGroupsDto> response = sendGetObject(uri, headers, new TypeReference<OpenApiResponse<EdgeGroupsDto>>() {
});
List<EdgeGroupDto> edgeGroupList = response.getBody().edgeGroupList;
if (!edgeGroupList.isEmpty()) {
for (EdgeGroupDto edgeGroupDto : edgeGroupList) {
for (ConnectivityInfoDto connectivityInfo : edgeGroupDto.connectivityList) {
//endpoint
String hostAddress = connectivityInfo.hostAddress;
int port = connectivityInfo.portNumber;
String endpoint = String.format("ssl://%s:%d", hostAddress, port);
// set mqtt client
MqttClient mqttClient = new MqttClient(endpoint, clientId, new MemoryPersistence());
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
connOpts.setConnectionTimeout(5);
connOpts.setKeepAliveInterval(60);
connOpts.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1);
// create ssl socket factory
Optional<String> ca = edgeGroupDto.caList.stream().findFirst();
if (ca.isPresent()) {
TrustManagerFactory tmf = getTrustManagerFactory(ca.get());
KeyManagerFactory kmf = getKeyManagerFactory(clientCaChainFilePath, clientCertFilePath, clientKeyFilePath, "");
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
connOpts.setSocketFactory(context.getSocketFactory());
}
//connect
try {
mqttClient.connect(connOpts);
return mqttClient;
} catch (Exception e) {
System.out.println(String.format("client device(%s) failed to connect core device(ssl://%s:%d) with exception %s", deviceName, connectivityInfo.hostAddress, connectivityInfo.getPortNumber(), e.toString()));
}
}
}
} else {
throw new RuntimeException(String.format("client device(%s) could not connect to core device using any of the edge group endpoint", deviceName));
}
throw new RuntimeException("There is no edge group to connect client device " + deviceName);
}
private KeyManagerFactory getKeyManagerFactory(String clientCaChainFilePath, String clientCertFilePath, String clientKeyFilePath, String password) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
KeyPair key = getClientKey(clientKeyFilePath);
Certificate clientCert = getClientCert(clientCertFilePath);
List<Certificate> caChain = getClientCaChain(clientCaChainFilePath, clientCert);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
int caChainSize = caChain.size();
Certificate[] caChainArray = caChain.toArray(new Certificate[caChainSize]);
keyStore.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), caChainArray);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password.toCharArray());
return keyManagerFactory;
}
private TrustManagerFactory getTrustManagerFactory(String coreCaPem) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore rootCaKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
rootCaKeyStore.load(null, null);
X509Certificate coreCaCert = convertStringToX509Cert(coreCaPem);
rootCaKeyStore.setCertificateEntry("core", coreCaCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(rootCaKeyStore);
return tmf;
}
private void init() {
Security.addProvider(new BouncyCastleProvider());
}
private KeyPair getClientKey(String clientKeyFilePath) throws IOException {
PEMParser pemParser = new PEMParser(new FileReader(clientKeyFilePath));
Object object = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair key = converter.getKeyPair((PEMKeyPair) object);
pemParser.close();
return key;
}
private List<Certificate> getClientCaChain(String clientCaChainFilePath, Certificate clientCert) throws CertificateException, IOException {
X509Certificate cert = null;
List<Certificate> caChain = new ArrayList<Certificate>();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(clientCaChainFilePath));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
cert = (X509Certificate) cf.generateCertificate(bis);
caChain.add(cert);
}
caChain.add(0, clientCert);
return caChain;
}
private X509Certificate getClientCert(String clientCertFilePath) throws CertificateException, IOException {
return getX509Certificate(clientCertFilePath);
}
private X509Certificate getX509Certificate(String filePath) throws CertificateException, IOException {
X509Certificate caCert = null;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
caCert = (X509Certificate) cf.generateCertificate(bis);
}
return caCert;
}
private X509Certificate convertStringToX509Cert(String certificate) throws CertificateException {
InputStream targetStream = new ByteArrayInputStream(certificate.getBytes());
return (X509Certificate) CertificateFactory
.getInstance("X509")
.generateCertificate(targetStream);
}
private Header[] makeHeaders(String method, String url, Long epoch, String accessKey, String secretKey) {
Header[] headers = {
new BasicHeader("Content-Type", "application/json"),
new BasicHeader(X_NCP_IAM_ACCESS_KEY, accessKey),
new BasicHeader(X_NCP_APIGW_TIMESTAMP, String.valueOf(epoch)),
new BasicHeader(X_NCP_APIGW_SIGNATURE_V2, makeSignature(method, url, String.valueOf(epoch), accessKey, secretKey))
};
return headers;
}
private String makeSignature(String method, String url, String epoch, String accessKey, String secretKey) {
String signature = null;
String message = method + " " + url + "\n" + epoch + "\n" + accessKey;
try {
SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
signature = Base64.encodeBase64String(rawHmac);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
System.out.println(String.format("Failed to make signature with message : $s", message));
}
return signature;
}
private <T> T sendGetObject(URI uri, Header[] headers, TypeReference<T> responseType) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(uri);
if (headers != null) {
httpGet.setHeaders(headers);
}
return getResponseObject(httpClient.execute(httpGet), responseType);
}
private <T> T getResponseObject(CloseableHttpResponse httpResponse, TypeReference<T> responseType) throws IOException {
try {
HttpEntity responseEntity = httpResponse.getEntity();
String resultString = EntityUtils.toString(responseEntity);
EntityUtils.consume(responseEntity);
if (httpResponse.getStatusLine().getStatusCode() >= 200 && httpResponse.getStatusLine().getStatusCode() <= 299) {
return new ObjectMapper().readValue(resultString, responseType);
}
throw new HttpResponseException(httpResponse.getStatusLine().getStatusCode(), resultString);
} finally {
httpResponse.close();
}
}
static class OpenApiResponse<T> {
private String status;
private T body;
public String getStatus() {
return status;
}
public T getBody() {
return body;
}
}
static class EdgeGroupsDto {
@JsonProperty("EdgeGroups")
private List<EdgeGroupDto> edgeGroupList;
public EdgeGroupsDto(List<EdgeGroupDto> edgeGroupDtos) {
this.edgeGroupList = edgeGroupDtos;
}
public EdgeGroupsDto() {
this.edgeGroupList = new ArrayList<>();
}
public List<EdgeGroupDto> getEdgeGroupList() {
return edgeGroupList;
}
}
static class EdgeGroupDto {
@JsonProperty("EdgeGroupId")
private String edgeGroupId;
@JsonProperty("Connectivity")
private List<ConnectivityInfoDto> connectivityList;
@JsonProperty("CAs")
private List<String> caList;
public String getEdgeGroupId() {
return edgeGroupId;
}
public List<ConnectivityInfoDto> getConnectivityList() {
return connectivityList;
}
public List<String> getCaList() {
return caList;
}
}
static class ConnectivityInfoDto {
private String hostAddress;
private int portNumber;
private String metadata;
public String getHostAddress() {
return hostAddress;
}
public int getPortNumber() {
return portNumber;
}
public String getMetadata() {
return metadata;
}
}
}