2018年3月31日土曜日

Ubuntu で Let's Encrypt で証明書を取得

概要

過去に CentOS で取得しました
今回は Ubuntu で取得してみました
少しやり方が変わっていので備忘録として残りしておきます

環境

  • Ubuntu 16.04
  • letsencrypt 0.4.1-1

letsencrypt コマンドのインストール

  • apt -y install nginx
  • apt -y install letsencrypt python-letsencrypt-apache

nginx はドメインが自分のものか証明するために必要になります
python-letsencrypt-apache をインストールすると apache2 もインストールされてしまうので不要であればアンインストールしてください

ファイアウォールの設定

  • ufw disable

80, 443 ポートをオープンにすれば disable にしないでも OK です
AWS などで他にファイアウォールを使って場合はそちらも 80, 443 ポートで通信できるようにしておいてください

証明書の取得

  • systemctl stop nginx

事前に nginx は停止しておきましょう
letsencrypt コマンドでサーバを立ち上げるので停止しておく必要があります

  • letsencrypt certonly --standalone -d admiral.cm-system.ga

メールアドレスを登録します
ubuntu_letsencrypt1.png

あとは規約に同意するだけです
ubuntu_letsencrypt2.png

成功すると以下のログが表示されます

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.test100-domain.ga/fullchain.pem. Your cert
   will expire on 2018-06-24. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
  • ls /etc/letsencrypt/live/www.test100-domain.ga/

で以下のファイルが作成されています

  • cert.pem -> 証明書
  • chain.pem -> 中間証明書
  • fullchain.pem -> 証明書 + 中間証明書
  • privkey.pem -> 秘密鍵

最後に

Ubuntu で Let’s Encrypt からサーバ証明書を取得してみました
かなり簡単になっていた印象です
はまりポイントは DNS の A レコードがちゃんと設定されているかどうかとサーバの 80, 443 ポートがちゃんとオープンになっているかかなと思います

あまりセキュリティ的にはよろしくないですが一瞬だけフルオープンにするのはありかなと思います

2018年3月30日金曜日

VMware Admiral で SSL を有効にする方法

概要

VMware Admiral で SSL 通信を有効にしてみました
ドメイン+証明書もちゃんと設定しています
ドメインと証明書の取得方法については過去の記事を参考にしてみてください

環境

  • Ubuntu 16.04
  • docker 18.03.0-ce, build 0520e24
  • admiral

SSL を有効にして admiral を起動

  • docker run -d -p 443:8383 --name admiral -e ADMIRAL_PORT=-1 -e XENON_OPTS="--securePort=8383 --certificateFile=/tmp/fullchain.key --keyFile=/tmp/private.key" -v /etc/letsencrypt/live/admiral.cm-system.ga/fullchain.pem:/tmp/fullchain.key -v /etc/letsencrypt/live/admiral.cm-system.ga/privkey.pem:/tmp/private.key vmware/admiral

コマンドがやや長いですが指定するのは SSL のポートと証明書、秘密鍵だけです
admiral 側は /tmp を指定しておきます
そしてローカル側に取得した SSL 証明書を配置しておいて、それをコンテナにマウントすることでコンテナに証明書を参照させます

上記の例は Let’s Encrypt で取得した証明書と秘密鍵を指定しています

ADMIRAL_PORT=-1 にすると SSL ではないポートでは LISTEN しなくなります

動作確認

あとはブラウザ https に接続するだけです

最後に

VMware Admiral で SSL を有効にする方法を紹介しました
docker コマンドでもできるようです

2018年3月29日木曜日

コンテナをサクッと GUI でみたい場合には Portainer が便利

概要

Portainer は docker コンテナを GUI 上で操作することができるマネージメントツールです
Kubernetes や Mesos, Openshift などとカテゴリ的には同じですが複雑な構成は不要でコンテナを一つ立ち上げるだけで GUI を確認することができます
ホストもシングル構成でとりあえず GUI がほしい場合には便利なので紹介します

環境

  • Ubuntu 16.04
  • docker 17.05
  • Portainer

Portainer の起動

  • docker run -d -p 9000:9000 --restart always -v /var/run/docker.sock:/var/run/docker.sock -v /opt/portainer:/data portainer/portainer

これでブラウザで 9000 番にアクセスできます

初期設定

とりあえずユーザを作成するように言われるのでパスワードを入力して作成します
portainer1.jpg

可視化する docker ホストを指定します
今回は localhost なので Local 側を選択しましょう
portainer2.png

ダッシュボードにアクセス

こんな感じでダッシュボードが表示されます
portainer3.png

コンテナの一覧はこんな感じです
起動/停止はもちろん削除や追加もできます
portainer4.png

イメージの一覧はこんな感じです
pull もできます
portainer5.png

あとは基本的なストレージやネットワークが見れる他、イベントが見れたり別のコンテナホストを追加したりもできます
ユーザも追加できるので Read Only ユーザなどを作成して簡単な ACL 的なこともできます

最後に

コンテナを簡単に GUI で確認できるようになる Portainer を試してみました
個人的にはかなり便利なツールだなと思います
大規模なプロダクションだと心細いですが個人利用でとりあえずってことであればこれで十分だと思います

API もあるので独自コンパネを作ったり CLI ツールなども作れます

クラスタ構成でロードバランサ使いとか HA したいみたいな話が出たら素直に kubernetes などを導入しましょう

参考サイト

2018年3月28日水曜日

firebase-admin を使って nodejs から Firebase を操作するサンプル

概要

firebase-ruby だとリアルタイムデータベースを扱えないので nodejs の firebase-admin を使ってみました
今回紹介するサンプルはリアルタイムデータベースからデータを取得して一定条件だった場合に特定のペイロードにプッシュ通知を送信するサンプルです

環境

  • macOS 10.13.2
  • firebase-admin 5.11.0
  • nodejs 8.7.0

サンプルコード

事前に Firebase Admin SDK の管理画面から新しい秘密鍵を生成して JSON ファイルをダウンロードしておいてください

var admin = require('firebase-admin');

var serviceAccount = require('./project-firebase-adminsdk-xxxxx-xxxxxxxxxx.json');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://project-name.firebaseio.com/'
});

var db = admin.database();
var ref = db.ref("path/to/ref");
ref.on("child_added", function(snapshot) {
  var val = snapshot.val();
  console.log(val.id);
  // ある条件に合う場合は
  if (val.id.startsWith("push_")) {
    // プッシュ通知を送信する
    console.log("send push");
    var topic = "topic";
    var payload = {
      notification: {
        title: "Hello",
        body: "This message sent from nodejs"
      }
    };
    admin.messaging().sendToTopic(topic, payload)
      .then(function(response) {
        console.log("Successfully sent message:", response);
      })
      .catch(function(error) {
        console.log("Error sending message:", error);
      });
  }
},function (errorObject) {
  console.log("The read failed: " + errorObject.code);
});

ref.on を使うことでデータが登録される度に function(snapshot) がコールされます
イベントにはいくつが種類があり今回は child_added というイベントを指定しています
このイベントは子要素が追加されたときだけ関数内の処理が実行されるイベントです

プッシュ通知は admin.messaging().sendToTopic で行います
今回は特定のトピックに対して送信しています
あとのその結果を表示しているだけです

参考サイト

2018年3月27日火曜日

Firebase でトピックを Subscribe してプッシュ通知を送信してみる

概要

過去に Firebase で iOS にプッシュ通知を送信してみました
そのときは特にトピックを指定しなかったので全端末にプッシュ通知が送信されてしまいます
トピックは特定の端末群に対してプッシュ通知を送信するための機能です
今回はトピックを使ってプッシュ通知を送信してみました

あと事前に過去の記事を元に最低限のプッシュ通知の設定と実装は済ませておいてください (プッシュ用の証明書の登録, p12 ファイルの登録, プッシュ通知用の設定)

環境

  • macOS 10.13.2
  • Xcode 9.2 (9C40b)
  • FirebaseMessaging 2.1.1

トピックを Subscribe する

以下のメソッドをコールすることでトピックを Subscribe することができます

import FirebaseMessaging
Messaging.messaging().subscribe(toTopic: "topic-random_match")

こんな感じです、簡単ですね

Unsubscribe する

トピックを購読したくなくなった場合は Unsubscribe メソッドをコールする必要があります

Messaging.messaging().unsubscribe(fromTopic: "topic-random_match")

これも同じようにコールするだけなので簡単ですね

テスト送信

ではテスト送信してみましょう
Firebase の管理画面で Notifications から送信してみましょう
ポイントとしては当然ですが送信時にトピックを指定してください
firebase_topic_push1.png

これでトピックを subscribe している端末にだけ送信できると思います
端末がフォアグランドで動作しているとプッシュ通知が来ないのでバックグラウンドに移動しましょう

ON/OFF テクニック

subscribe/unsubscribe メソッドは当然あるのですが isSubscribe メソッド的なものはまだないようです
調べてみると Github で issue は上がっているようです
https://github.com/firebase/firebase-ios-sdk/issues/225

なので ON/OFF する場合は自分で実装する必要があります
自分の場合ですが単純にローカルに ON/OFF のデータを持つようにしました
RealmSwift を使って Bool の値を保存しているだけです

あとはその値を元に subscribe したり unsubscribe するだけです
subscribe 中に subscribe をコールしても特に問題はないようです (重複して受け取ることは自分の端末では起こりませんでした)

Tips

Cannot parse topic name topic/random_match. Will not subscribe. subscribe したときに出る場合があります
どうやらスラッシュを入れることができないのでハイフンなどに変えてみましょう

最後に

Firebase のプッシュ通知でトピック機能を使ってみました
簡単に使うのは使えますが少し機能が欠けているので、足りない機能は自分で補完する必要があります

今回は管理画面から送信しましたが Ruby などからも送信できるので、その場合もトピックを指定すれば便利な使い方ができるかなと思います

参考サイト

2018年3月26日月曜日

RealmSwift でマイグレートする方法

概要

RealmSwift ですでに定義したクラスにフィールドを追加したい場合はあると思います
リリースしているアプリの場合、ただ単純にフィールドを追加しただけだとエラーになってしまいます
その場合には正しい手順でマイグレートをしなければいけません

環境

  • macOS 10.13.2
  • Xcode 9.2 (9C40b)
  • RealmSwift 3.1.1

マイグレートする方法

AppDelete.swift の didFinishLaunchingWithOptions に以下を追加します

let config = Realm.Configuration(
  schemaVersion: 1,
  migrationBlock: { migration, oldSchemaVersion in
    if (oldSchemaVersion < 1) {
      // auto migrate
    }
  })

Realm.Configuration.defaultConfiguration = config

let realm = try! Realm()

これだけです、簡単
ただ、新規で追加したフィールドを何かしらの値で初期化するのであれば if 文内にそれを実装しなければなりません
例えば sum というフィールドを追加した場合、初期値が 0 でいいのであれば特に何もする必要はありません
Realm のクラス側でフィールドを 0 で定義すれば OK です
(※ RealmSwift では各フィールドはこちらのルールで初期化されます)
そうではなく例えば score と oldScore フィールドの足した値を sum フィールドに設定して初期化したい場合はその足し算のロジックを実装する必要があります

試してはいないのですがおそらく以下のような感じでできるはずです

if (oldSchemaVersion < 1) {
  migration.enumerateObjects(ofType: UserData.className()) { oldObject, newObject in
    let score = oldObject!["score"] as! Int
    let oldScore = oldObject!["oldScore"] as! Int
    newObject!["sum"] = score + oldScore
  }
}

また schemaVersion を今回「1」にしましたが次もマイグレートする場合は必ずここのバージョンを 2 なり 1 以上の値にしてくださいそうしないとマイグレートが行われません

最後に

RealmSwift でマイグレート処理を試してみました
処理を 1 つ Delegate に追加するだけなので非常に簡単にできます
クラスのスキーマのバージョン管理だけしっかりやる必要があるので定数などにしてちゃんと数字を追えるようにしておくと良いと思います

参考サイト

2018年3月25日日曜日

Apache Mesos を試してみた

概要

Apache Mesos はコンテナマネージメントツールです
minimesos という簡単に試せるツールがあったのでこれで Mesos を試してみました

環境

  • macOS 10.13.2
  • minimesos 0.13.0
  • docker 18.03.0-ce-rc4

インストール

  • brew install minimesos

初期化

  • minimesos init

~/.minimesos ディレクトリの作成と containersol/minimesos-cli というイメージが pull されます

起動

  • minimesos up --mapPortsToHost

結構時間がかかります
minimesos はすべてコンテナで起動するので pull などに時間がかかっているんだと思います

完了すると minimesosFile というコンフィグが記載されたファイルが作成されます

Minimesos cluster is running: 3215234594
Mesos version: 1.0.0
export MINIMESOS_NETWORK_GATEWAY=172.17.0.1
export MINIMESOS_AGENT=http://172.17.0.6:5051; export MINIMESOS_AGENT_IP=172.17.0.6
export MINIMESOS_ZOOKEEPER=zk://172.17.0.3:2181/mesos; export MINIMESOS_ZOOKEEPER_IP=172.17.0.3
export MINIMESOS_MARATHON=http://172.17.0.5:8080; export MINIMESOS_MARATHON_IP=172.17.0.5
export MINIMESOS_CONSUL=http://172.17.0.7:8500; export MINIMESOS_CONSUL_IP=172.17.0.7
export MINIMESOS_MASTER=http://172.17.0.4:5050; export MINIMESOS_MASTER_IP=172.17.0.4
You are running Docker on Mac so use localhost instead of container IPs for Master, Marathon, Zookeepr and Consul

こんな感じになれば OK です
localhost:8080 にアクセスすると管理画面 (Marathon) にアクセスできます
minimesos1.png

docker ps コマンドで確認すると consul や zookeeper, 内部向けの dns サーバなどのコンテナが立ち上がっているのが確認できると思います
また UI だと weave-scope のコンテナが 1 つだけ立ち上がっているように見えます

コンテナを立ち上げてみる

サンプルに http-server を立ち上げるものがあったのでそれをやってみました

  • vim http-server.json
{
  "id": "http-no-ports-exposed",
  "mem": 16,
  "cpus": 0.1,
  "instances": 1,
  "container": {
    "type": "DOCKER",
    "docker": {
      "network": "BRIDGE",
      "image": "scrapbook/docker-http-server"
    }
  }
}

こんな感じで Mesos では Json ファイルでコンテナ定義を定義するようです
コンテナを作成するには

  • minimesos install --marathonFile http-server.json

という感じで実行します
UI でも確認できます
minimesos2.png

上記の Json は外部からアクセスできない http-server を立ち上げるサンプルです
もうひとつ外部からアクセス可能な http-server を立ち上げるサンプルがありました

  • vim http-server-expose.json
{
  "id": "http-ports-static-assigned-to-31002",
  "mem": 16,
  "cpus": 0.1,
  "instances": 1,
  "container": {
    "type": "DOCKER",
    "docker": {
      "network": "BRIDGE",
      "image": "scrapbook/docker-http-server",
      "portMappings": [
        { "containerPort": 80, "hostPort": 31002, "servicePort": 10002, "protocol": "tcp" }
      ]
    }
  }
}
  • minimesos install --marathonFile http-server-expose.json

これでコンテンを起動すると localhost:31002 で Web サーバのコンテナにアクセスすることができます

Java のプロセスをデプロイする

Mesos はコンテナ以外にも Java のプロセスを Json で定義してデプロイできるようです
mesos-agent コンテナ内で java コマンドが使えるようになっており、そこで動作するようです

サンプルでは elasticsearch のプロセスを動作させていました

  • vim elasticsearch.json
{
  "id": "elasticsearch-mesos-scheduler",
  "uris": [
    "file:///path/to/jar/elasticsearch-mesos-scheduler-1.0.0-withDependencies.jar"
  ],
  "cmd": "java -Djava.security.egd=file:/dev/urandom -jar elasticsearch-mesos-scheduler-1.0.0-withDependencies.jar --frameworkUseDocker false --zookeeperMesosUrl zk://172.17.0.3:2181/mesos --frameworkName elasticsearch --elasticsearchClusterName mesos-elasticsearch --elasticsearchCpu 0.5 --elasticsearchRam 1024 --elasticsearchDisk 1024 --elasticsearchNodes 1",
  "cpus": 0.2,
  "mem": 512,
  "env": {
    "JAVA_OPTS": "-Djava.security.egd=file:/dev/urandom -Xms256m -Xmx512m"
  },
  "ports": [31100],
  "requirePorts": true,
  "instances": 1
}

ポイントは 2 つでまず動作させる Java ファイル (ここでは elasticsearch-mesos-scheduler-1.0.0-withDependencies.jar という jar ファイル) をローカルにダウンロードしてそのパスを指定します
今回の jar は以下のコマンドでダウンロードできました

  • curl -L https://github.com/mesos/elasticsearch/releases/download/1.0.0/elasticsearch-mesos-scheduler-1.0.0-withDependencies.jar -o elasticsearch-mesos-scheduler-1.0.0-withDependencies.jar

これをそのままコンテナ上に送って動作させるようです

cmd では jar ファイルを動作させるコマンドをつらつら並べていくのですが、その際に zookeeper コンテナの URL が必須になります
今回であれば --zookeeperMesosUrl zk://172.17.0.3:2181/mesos の部分になります

あとはこれを同じようにデプロイすれば OK です

  • minimesos install --marathonFile elasticsearch.json

UI 上ではコンテナと同じように確認することができました
minimesos3.png

後処理

  • minimesos destroy

で必要なコンテナを全部削除してくれます
イメージは残っているので再度 up するときはすぐに立ち上がります

最後に

minimesos で Apache Mesos を試してみました
Json ファイルで定義する部分は kubernetes や Openshift とは違った点かなと思います

内部で zookeeper と consul を使っているのでその辺のノウハウがある人にはトラシュしやすいかなと思います
あとは全体的に Java プロダクションが多いので Java 好きにもオススメかなと思います