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 好きにもオススメかなと思います

2018年3月24日土曜日

OpenShift をサクッと試せる minishift を試してみた

概要

OpenShift は簡単に言ってしまえば kubernetes のエンタープライズ版になります
実際はライセンスを購入する必要がありますが簡単に試せる minishift というツールがあったので試してみました

環境

  • macOS 10.13.2
  • minishift v1.14.0+1ec5877
  • VirtualBox 5.1.30r118389

インストール

  • brew cask install minishift

今回は Mac なのでこれでインストールできます

minishift サーバの起動

  • minishift start --vm-driver=virtualbox

VirtualBox 上に minishift サーバ兼コンテナホスト VM を 1 つ立ててくれます
OS は minishift-b2d-iso という専用の OS を使っているようです
ISO やバイナリ、イメージのダウンロードが実行されるのである程度時間がかかります

Mac 上に Openshift 上のコンテナホストおよびコンテナを制御する oc コマンドがインストールされているので以下のコマンドで PATH に通します

  • eval $(minishift oc-env)

これで oc コマンドが使えるようになります
一応確認で oc version と打ってみてください
後述している証明書のエラーが出なければ完了です

minishift サーバ確認

VirtualBox 上に構築された VM の中を確認してみましょう

  • minishift ssh

で boot2docker のコンテナホストにログインしましょう
docker ps -a で確認するとすでにいくつかのコンテナが起動しているかと思います
minishift を動作させるのに必要なコンテナになるのでそのままにしておきます

管理画面

https://192.168.99.100:8443 にアクセスしてみましょう
192.168.99.100 は構築した minishift サーバのアドレスです

デフォルトは developer ユーザもしくは system ユーザでログインできます
developer ユーザはパスワードなしになっています
system ユーザのパスワードは「admin」です

まだ何もないですが以下のような UI が表示されるはずです
openshift1.png

コンテナを起動してみる

先の程の system ユーザを使って作業します
Mac 上の oc コマンドを使って制御します

まずはログインします
ログインしないで作業すると error: Error from server (Forbidden) というエラーになり進みません

  • oc login -u system

パスワードは admin と入力してください
次に test という名前のプロジェクトを作成します
ちなみにコマンドからプロジェクトを作成すると管理画面でも確認することができます

  • oc new-project test
  • oc project test

ログイン、プロジェクトの選択できたらアプリを作成します
アプリを作成するとコンテナも作成されます

  • oc new-app https://github.com/openshift/nodejs-ex -l name=myapp

-l オプションでラベルを付与することであとで削除しやすくしておきます
イメージの push などがあるので多少時間がかかります
コンテナができているか確認してみましょう

  • oc get pods
NAME                READY     STATUS      RESTARTS   AGE
nodejs-ex-1-build   0/1       Completed   0          3m
nodejs-ex-1-t9z5s   1/1       Running     0          9s

nodejs-ex-1-t9z5s というアプリ陽のコンテナが作成されました
build コンテナは Complelted になっていれば OK です
build コンテナのログを確認してみましょう

  • oc logs -f bc/nodejs-ex

これで「Push successful」 になっていれば OK です
OpenShift 内にあるプライベートリポジトリにイメージを push してそこからコンテナを起動しています
ちなみに bc は buildconfigs の略で OpenShift ではこれをリソースと読んでいます
リソースには様々な種類あり例えば dc は deploymentconfigs の略になります
ちなみに先ほどの new-app で buildconfigs, builds, imagestreams, deploymentconfigs, po, rc, svc のリソースが作成されています

外部からアクセスできるようにする

あとは外部からアクセスできるようにするだけです

  • oc expose svc/nodejs-ex

これで OK です
内部に DNS がありドメインを振ってくれています

  • oc status

でアクセスする URL が確認できます

http://nodejs-ex-test.192.168.99.100.nip.io to pod port 8080-tcp (svc/nodejs-ex)
  dc/nodejs-ex deploys istag/nodejs-ex:latest <-
    bc/nodejs-ex source builds https://github.com/openshift/nodejs-ex on openshift/nodejs:6 
    deployment #1 deployed 4 minutes ago - 1 pod

動作確認

内部のドメインにアクセスしてみましょう
http://nodejs-ex-test.192.168.99.100.nip.io

するとアプリにアクセスできると思います
openshift2.png

  • minishift openshift service nodejs-ex --in-browser -n test

でも確認できます

後処理

  • oc delete all -l name=myapp

で一通りのリソースを削除後

  • minishift stop

で OK です
サーバも不要な場合は minishift delete してください

Tips

Unable to connect to the server: x509: certificate signed by unknown authority

oc コマンド実行時に Unable to connect to the server: x509: certificate signed by unknown authority というエラーが出た場合には ~/.kube ファイルを一旦削除してから minishift start するようにしてください
どうやら oc コマンドが .kube ディレクトリを見ているようで同じホスト上で minikube を先に実行していると .kube ディレクトリがすでに出来ているようで minishift 用の .kube ディレクトリができていないみたいです
またすでに minishift start してしまっている場合は一旦 delete して再度 start する必要があります

  • minishift stop
  • minishift delete
  • rm -rf ~/.minishift
  • rm -rf ~/.kube
  • minishift start

oc login 時の注意

またログインする際に oc login -u system:admin という感じでログインするとユーザ名が「system:admin」という名前になってしまい管理画面などでプロジェクトを確認することができません
パスワードを指定する場合は -p オプションで指定するので注意してください
もしログイン先が間違っていると minishift openshift service nodejs-ex --in-browser -n test したときもそんなプロジェクトないと言われてエラーになってしまいます

コマンド

すべてのリソースを確認

  • oc get all

リソースの詳細を確認

  • oc describe svc/nodejs-ex

指定したリソースの取得

  • oc get svc

現在のプロジェクトの確認

  • oc project

最後に

minishift を Mac + VirtualBox で試してみました
名前からピンと来ている人はいると思いますが minishift は minikube の fork プロジェクトです
コマンドなども少し似ています
なので minikube に慣れている人であれば minishift は簡単に理解できると思います

まだ軽くしか触っていないので詳しくは不明ですが kubernetes にユーザ管理やプロジェクトの概念が追加されている感じです
また事前に準備されているカテゴリからアプリを簡単に展開することもできます
イメージ的には Rancher に近い感じだと思います

参考サイト

2018年3月23日金曜日

コンテナオーケストレーションツールの Rancher を試してみた

概要

Rancher は kubernetes などのコンテナマネージメントツールを更にラップして簡単にコンテナマネージメントをできるオーケストレーションツールになります
要するに kubernetes の難しい概念などを吸収して使いやすくしてくれています
それに加えて kubernetes にない ACL などの機能も提供してくれます
とりあえず今回は Rancher サーバを構築してコンテナホストの追加と挙動の確認まで行ってみました

環境

Rancher サーバ

  • macOS 10.13.2
  • Rancher v1.6.14

コンテナホスト

  • vagrant 2.0.3
  • VirtualBox 5.1.30r118389
  • Ubuntu 16.04
  • docker 17.12.1-ce, build 7390fc6

構成

Rancher サーバの起動

Mac 上でコンテナを起動するだけです

  • docker run -d --restart=unless-stopped -p 8080:8080 rancher/server:stable

http://172.28.128.1:8080/ にアクセスできます
172.28.128.1 は vboxnet1 が刺さっているローカルの Mac です

コンテナホストの追加

今回は vagrant を使って Mac 上に Ubuntu 16.04 を構築してそこに dockerd をインストールしました
https://hawksnowlog.blogspot.jp/2018/03/install-vagrant-on-macos-high-sierra.html

Rancher には docker-machine の機能が実装されており、docker-machine で使えるドライバを使えば EC2 や digitalOcean に自動で構築することもできるようです

コンテナホストができたら Rancker 用のエージェントコンテナを起動します

  • docker run -e CATTLE_AGENT_IP="172.22.128.1" --rm --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v1.2.9 http://172.28.128.1:8080/v1/scripts/277730D04E89F34BABFE:1514678400000:7Ob2cE0CNfOeYCsrbQOYHg24go
INFO: Running Agent Registration Process, CATTLE_URL=http://172.28.128.1:8080/v1
INFO: Attempting to connect to: http://172.28.128.1:8080/v1
INFO: http://172.28.128.1:8080/v1 is accessible
INFO: Inspecting host capabilities
INFO: Boot2Docker: false
INFO: Host writable: true
INFO: Token: xxxxxxxx
INFO: Running registration
INFO: Printing Environment
INFO: ENV: CATTLE_ACCESS_KEY=7FCF2ED7B53F4910177A
INFO: ENV: CATTLE_AGENT_IP=172.22.128.1
INFO: ENV: CATTLE_HOME=/var/lib/cattle
INFO: ENV: CATTLE_REGISTRATION_ACCESS_KEY=registrationToken
INFO: ENV: CATTLE_REGISTRATION_SECRET_KEY=xxxxxxx
INFO: ENV: CATTLE_SECRET_KEY=xxxxxxx
INFO: ENV: CATTLE_URL=http://172.28.128.1:8080/v1
INFO: ENV: DETECTED_CATTLE_AGENT_IP=172.17.0.1
INFO: ENV: RANCHER_AGENT_IMAGE=rancher/agent:v1.2.9
INFO: Launched Rancher Agent: d928df0e9a8326fc857a0b3295fc5f81419adffd912c6a130bdb8fa80340decb

成功時のログは上記の通りです

エージェント側から Rancher サーバが見える必要があります
このコマンドは Rancher の管理画面から生成することができます
http://172.28.128.1:8080/env/1a5/infra/hosts/add?driver=custom

rancher1.png

エージェント用のコンテナが起動すると別のコンテナが 10 個起動するようです
管理画面 (Stacks -> All) で見ると Stack とそれぞれのコンテナがカテゴライズされているのがわかります
rancher2.png

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

docker コマンドでもできますが、せっかくなので UI から立ち上げてみます

INFRASTRUCTURE -> Containers -> Add Container

で新規でコンテナを立ち上げることができます
DockerHub にあるrancker3.pngイメージ名とポートしています

これで立ち上がると以下のようにメトリックを確認することができます
rancker4.png

Public に公開されている IP はコンテナホスト上の docker0 インタフェースに付与された IP のようです
172.28.128.3 の vboxnet1 のネットワークではないようです

最後に

Rancher を試してみました
今回は単一のコンテナを立ち上げただけなので Stack や Service の機能の仕組みは使っていません
Stack は複数のコンテナ定義を登録しておくことができる機能です
docker-compose.yml を登録することもできるます
更に LoadBalancer なども登録することができます
Service はコンテナ定義や LoadBalancer など Stack に登録することができるコンポーネントの単位になります

なので基本は Stack でアプリの定義をしたあとで Stack 単位でアプリの起動やスケールアウトを行っていきます

また今回は Ubuntu を構築してわざわざ docker をインストールしました
初めは docker-machine の virtualbox ドライバを使って構築したのですがどうやら boot2docker ではエージェントが動作しないようなので仕方なく Ubuntu を構築しました

参考サイト

2018年3月22日木曜日

macOS High Sierra に Vagrant をインストールして Ubuntu を構築する

概要

macOS High Sierra に Vagrant をインストールしてみました
ついでに Ubuntu を構築してみました

環境

  • macOS 10.13.2
  • vagrant 2.0.3
  • VirtualBox 5.1.30r118389

Vagrant のインストール

公式サイトから dmg をダウンロードしてインストールする必要があります
今回は vagrant_2.0.3_x86_64.dmg というインストーラがダウンロードできました

ダウンロードできたらダブルクリックして開きましょう
そして vagrant.pkg をダブルクリックして起動します
vagrant1.png

あとはインストーラの指示に従ってインストールすれば OK です
vagrant2.png

ターミナルを起動してコマンドが使えるか確認してみましょう

  • vagrant -v

Vagrant 2.0.3 と表示されれば OK です

Ubuntu を構築する

適当な作業用ディレクトリを作成しそこに Vagrantfiile を配置して構築します

  • mkdir -p /path/to/vagrant/ubuntu
  • cd /path/to/vagrant/ubuntu
  • touch Vagrantfilie
  • vim Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.network "private_network", type: "dhcp"
end
  • vagrant up

で起動します
今回は直接サーバにアクセスするので Host Only Adapter のネットワークを追加しています

ちなみに xenial64 だと Ubuntu 16.04 になります
他のバージョンの Ubuntu はこちらを参照してください

動作確認

  • vagrant ssh
  • ip a show

(一部抜粋)

3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:64:a1:33 brd ff:ff:ff:ff:ff:ff
    inet 172.28.128.3/24 brd 172.28.128.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe64:a133/64 scope link 
       valid_lft forever preferred_lft forever

すでに vboxnet1 がすでにある環境で private_network を指定すると新規で vboxnet2 が出来て 172.28 帯を割り当てるようです
ホストマシンは 172.28.128.1 が割り当たっていました
ホストマシンから 172.28 帯でアクセスすることもできるようになっています

その他

停止

  • vagrant halt

削除

  • vagrant detroy

VirtualBox から確認

  • VBoxManage list vms

最後に

macOS 上に Vagrant をインストールしてみました
High Sierra でも特に問題なく使えるようです

2018年3月21日水曜日

rbVmomi で vMotion する方法 (RelocateVM_Task 編)

概要

rbVmomi を使って VM の vMotion をしてみました
今回は RelocateVM_Task を使って vMotion しています

環境

  • Ubuntu 16.04.3
  • Ruby 2.3.1p112
  • gem 2.5.1
  • rbvmomi 1.11.3
  • vCenter 6.5

コード

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

host = nil
priority = :defaultPriority
dc.hostFolder.children.each { |cluster|
  if cluster.is_a? RbVmomi::VIM::ClusterComputeResource
    host = cluster.host.select { |host|
      host.name == 'target_hostname'
    }.first
  end
}

migrate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(host: host)
vm.RelocateVM_Task(spec: migrate_spec, priority: priority).wait_for_completion

RelocateVM_Task の場合 VirtualMachineRelocateSpec を引数に取ります
また今回は host のみを指定しているため単純なホスト間の vMotion になりますがストレージ情報も指定できます
ストレージ情報を指定すると Storage vMotion にすることができます

host の情報は MigrateVM_Task 同様 HostSystem の情報を指定する必要があります

最後に

RelocateVM_Task を使って rbVmomi から vMotion してみました
vSphere 6.5 からはこちらを使うことが推奨されています

2018年3月20日火曜日

rbVmomi で vMotion する方法 (MigrateVM_Task 編)

概要

rbVmomi を使って VM の vMotion をしてみました
今回は MigrateVM_Task を使って vMotion しています

環境

  • Ubuntu 16.04.3
  • Ruby 2.3.1p112
  • gem 2.5.1
  • rbvmomi 1.11.3
  • vCenter 6.5

コード

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: '192.168.100.1',
  user: 'vcenter-user',
  password: 'vcenter-pass',
  insecure: 'true'
)

dc = vim.serviceInstance.find_datacenter('datacenter') || fail('datacenter not found')
vm = dc.find_vm('directory/path/to/vm') || fail('VM not found')

host = nil
priority = :defaultPriority
dc.hostFolder.children.each { |cluster|
  if cluster.is_a? RbVmomi::VIM::ClusterComputeResource
    host = cluster.host.select { |host|
      host.name == 'target_hostname'
    }.first
  end
}
vm.MigrateVM_Task(host: host, priority: priority).wait_for_completion

host と priority を指定しています
host は文字列をただ指定するだけではだめで HostSystem クラスのオブジェクトを設定する必要があります

今回はクラスタ直下にあるホストを取得していますが環境によってはフォルダを挟んでいる環境もあると思うので検索ロジックの部分は必要に併せて変更してください

最後に

MigrateVM_Task を使って rbVmomi で vMotion してみました
ちなみに MigrateVM_Task は vCenter 6.5 では deprecated になっているので RelocateVM_Task を使うようにしましょう

2018年3月19日月曜日

kompose を使って kubernetes 上で docker-compose してみる

概要

kompose は kubernetes 上で docker-compose.yml を実行するためのツールです
既存の docker-compose.yml を使って動作を確認してみました

環境

  • macOS 10.13.2
  • minikube 0.25.0
  • kubectl 1.9.2
  • VertualBox 5.1.30r118389
  • kompose 1.10.0

インストール

  • brew install kompose

その他のプラットフォームのインストール方法はこちら

ちょっとだけ変更する

  • git clone -b ver_golang_for_vch https://github.com/kakakikikeke/memo.git
  • cd memo
  • vim docker-compose.yml
version: '2'
services:
  web:
    image: kakakikikeke/memo:ver_golang_for_vch
    ports:
      - 80:8080
    links:
      - redis
    environment:
      - REDIS_URL=redis://redis:6379/0
    labels:
      kompose.service.type: LoadBalancer
  redis:
    image: redis:latest

kompose.service.type: LoadBalancer の部分だけ追記してあげます
これをやらないとアプリが外部からアクセスすることができません

コンバートする

  • kompose convert -f docker-compose.yml
INFO Kubernetes file "redis-service.yaml" created 
INFO Kubernetes file "web-service.yaml" created   
INFO Kubernetes file "redis-deployment.yaml" created 
INFO Kubernetes file "web-deployment.yaml" created

こんな感じでファイルが分割されます
中身を見ると kubernetes 形式の YAML フォーマットに変換されています

起動する

  • kompose up -f docker-compose.yml

で OK です
ログは以下の通り

INFO We are going to create Kubernetes Deployments, Services and PersistentVolumeClaims for your Dockerized application. If you need different kind of resources, use the 'kompose convert' and 'kubectl create -f' commands instead. 

INFO Deploying application in "default" namespace 
INFO Successfully created Service: redis          
INFO Successfully created Service: web            
INFO Successfully created Deployment: redis       
INFO Successfully created Deployment: web         

Your application has been deployed to Kubernetes. You can run 'kubectl get deployment,svc,pods,pvc' for details.

指示通り get コマンドで確認してみましょう
コンテナが Running 状態になれば OK です

  • kubectl get deployment,svc,pods,pvc
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/redis   1         1         1            1           2m
deploy/web     1         1         1            1           2m

NAME             TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
svc/kubernetes   ClusterIP      10.96.0.1        <none>        443/TCP        7h
svc/redis        ClusterIP      None             <none>        55555/TCP      2m
svc/web          LoadBalancer   10.105.101.184   <pending>     80:30146/TCP   2m

NAME                        READY     STATUS    RESTARTS   AGE
po/redis-69867bff9f-ztdcj   1/1       Running   0          2m
po/web-bdd7555b4-cfplp      1/1       Running   0          2m

動作確認

アプリにアクセスしてみましょう

  • minikube service web

でブラウザが開いてアプリにアクセスできます
もしは今回の場合であれば

  • curl $(minikube ip):30146

でアクセスすることができます

ダウンさせる

  • kompose down

で pods, deployments, services すべて削除してくれます

最後に

kubernetes 上で使える docker-compose 「kompose」を使ってみました
既存の docker-compose.yml をそのまま使えて便利です
コマンドもほぼ docker-compose と同様なので簡単に使えます

stop コマンドはないので kompose コマンド経由で停止する場合は down させるしかなさそうです
基本は up だけやらせてあとは kubectl で deployments と services を操作する感じかなと思います

あと LoadBlancer を追記した部分ですが公式を見ると up 後に services に対してロードバランサを追加してあげても良さそうです

  • kubectl expose deployment web --type="LoadBalancer"

参考サイト

2018年3月18日日曜日

kubectl run 時に deployments を一緒に作成しない方法

概要

kubectl run は deployments と pods を同時に作成してくれます
とりあえずコンテナだけ立てたい時には deployments は不要なのでその方法を紹介します

環境

  • macOS 10.13.2
  • minikube 0.25.0
  • kubectl 1.9.2
  • VertualBox 5.1.30r118389

deployments を作成しない方法

  • kubectl run nginx --image=nginx --restart=Never

--restart=Never を指定するだけです

もしくは pod 用の YAML ファイルを作成して kubectl create -f でも pod のみ作成することができます

参考サイト

2018年3月17日土曜日

minikube のデフォルトで使える heapster なる監視ツールを使う方法

概要

minikube にはデフォルトで監視ツールが付いています
デフォルトだと disabled になっているだけなので enabled にしてあげるだけで使えます

環境

  • macOS 10.13.2
  • minikube 0.25.0
  • kubectl 1.9.2
  • VertualBox 5.1.30r118389

有効化

  • minikube addons enable heapster

起動確認

  • kubectl get po,svc -n kube-system
NAME                                       READY     STATUS    RESTARTS   AGE
po/heapster-fjmp7                          1/1       Running   0          3m
po/influxdb-grafana-fzzpn                  2/2       Running   0          3m
po/kube-addon-manager-minikube             1/1       Running   1          5h
po/kube-dns-54cccfbdf8-5mwdc               3/3       Running   3          5h
po/kubernetes-dashboard-77d8b98585-58pbv   1/1       Running   1          5h
po/storage-provisioner                     1/1       Running   1          5h

NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
svc/heapster               ClusterIP   10.101.196.99    <none>        80/TCP              3m
svc/kube-dns               ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP       5h
svc/kubernetes-dashboard   NodePort    10.99.27.61      <none>        80:30000/TCP        5h
svc/monitoring-grafana     NodePort    10.99.68.139     <none>        80:30002/TCP        3m
svc/monitoring-influxdb    ClusterIP   10.100.191.113   <none>        8083/TCP,8086/TCP   3m

dashboard やら dns のアドオンも起動していますが、とりあえず全部 Running になれば OK です

UI 確認

  • minikube addons open heapster

で監視用の UI が起動します
heapster1.png

Grafana + influxdb でできているようです
Chronograf は使っていないようです

admin しかいないので必要であればユーザ登録しましょう
左上の Home から Cluster と Pods のメトリックがいきなり見れるので切り替えてみましょう
heapster2.png

ネームスペースごとに別れているようです
追加したコンテナも自動で監視が始まります

最後に

minikube で heapster という監視ツールを使ってみました
内部的には Grafana + influxdb のようです

とりあえずサクっとメトリックを見たいだけであればこれで十分かなと思います
あとは細かいアラートの設定や自分でみたいメトリックのカスタマイズをすれば良いのかなと思います

2018年3月16日金曜日

kubernetes 上でコンテナのスケールアウトを試してみた

概要

前回 kubernetes 上に DockerHub で公開しているイメージからコンテナをデプロイしてみました
今回はデプロイしたコンテナをスケールアウトしてみたいと思います

環境

  • macOS 10.13.2
  • minikube 0.25.0
  • kubectl 1.9.2
  • VertualBox 5.1.30r118389

スケールアウト

scale コマンドを使います
スケールアウトするコンテナ数は replicas で指定します

  • kubectl scale deployments/request-dumper --replicas=4

kubectl get deployments でスケールアウトしているか確認してみましょう

NAME             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
request-dumper   4         4         4            4           32m

こんな感じで数字の部分が 4 になっていれば OK です
kubectl get pods -o wide でコンテナの一覧を確認することができます

ロードバランシング

kubernetes はデフォルトでロードバランシングの機能があります

まず service を作成しましょう

  • kubectl expose deployment/request-dumper --type="NodePort" --port 4567

そしてホスト側でバインドしているポートを取得します

  • export NODE_PORT=$(kubectl get services/request-dumper -o go-template='{{(index .spec.ports 0).nodePort}}')

あとはこのポートにアクセスすれば勝手にバランシングしてくれます

  • curl $(minikube ip):$NODE_PORT

要するにいつものように service を定義すれば expose した deployment 配下にあるコンテナを自動でバランシングしてくれます

念のため kubectl logs -f [container_name] でログを確認しながらやるとちゃんとバランシングされていることがわかると思います

スケールダウン

最後にスケールダウンしてみましょう
と言ってもスケールアウトと同じです

  • kubectl scale deployments/request-dumper --replicas=2

これで kubectl get deployments/request-dumper で確認しましょう

NAME             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
request-dumper   2         2         2            2           47m

こんな感じでコンテナ数がスケールダウンしているのがわかります
deployments でイベントを確認してもスケールダウンしているのがわかります

  • kubectl describe deployments/request-dumper
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  51m   deployment-controller  Scaled up replica set request-dumper-5d75b447 to 1
  Normal  ScalingReplicaSet  22m   deployment-controller  Scaled up replica set request-dumper-5d75b447 to 4
  Normal  ScalingReplicaSet  5m    deployment-controller  Scaled down replica set request-dumper-5d75b447 to 2

最後に

kubernetes 上でコンテナのスケールアウト/ダウンを試してみました
ロードバランシングがデフォルトで付いているのは嬉しい点かなと思います

docker swarm の場合は haproxy などを使ってバランシングするのが定石ですが kubenetes ではそれらが不要になります
また今回紹介したロードバランシング以外にも External Load Balancer という機能もあるようです

2018年3月15日木曜日

kubernetes 上で Docker Hub にあるイメージからコンテナを立ち上げる

概要

前回 minikube を使ってローカル上に kubernetes 環境を構築してみました
今回はその上に Docker Hub で既に公開済みのイメージを使ってコンテナを立ち上げてみたいと思います

環境

  • macOS 10.13.2
  • minikube 0.25.0
  • kubectl 1.9.2
  • VertualBox 5.1.30r118389

コンテナの起動

  • kubectl run request-dumper --image=kakakikikeke/request-dumper

run コマンドを使います
ポイントは --image で DockerHub にあるイメージを指定するところ
リポジトリ名 (index.docker.io) を省略しているのは minikube ノードのデフォルトのレジストリが DockerHub になっているためです

$ minikube ssh
                         _             _            
            _         _ ( )           ( )           
  ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __  
/' _ ` _ `\| |/' _ `\| || , <  ( ) ( )| '_`\  /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )(  ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)

$ docker info | grep Registry
Registry: https://index.docker.io/v1/

あとはコンテナが起動するまで待ちます

  • kubectl get pods

外部からアクセスできるようにする

Running になったらコンテナにアクセスできるようにしましょう
expose コマンドを使います

  • kubectl expose deployment/request-dumper --type="NodePort" --port 4567

--port=4567 はコンテナで LISTEN しているポートを指定します
今回のアプリは Sinatra アプリで 4567 で LISTEN しているので、それを指定します
サービスの一覧を確認してノード側にバインドされているポートを確認します

  • kubectl get services
NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
kubernetes       ClusterIP   10.96.0.1     <none>        443/TCP          4h
request-dumper   NodePort    10.96.100.5   <none>        4567:30604/TCP   50s

今回は 30604 でバインドしていました
ちなみに以下のコマンドを使うことでポート部分だけを取得することができます

  • kubectl get services/request-dumper -o go-template='{{(index .spec.ports 0).nodePort}}'

この結果を変数に入れて使えば同じコマンドでアクセスし続けることも可能です

動作確認

curl でも叩いてみます

  • curl $(minikube ip):30604

これでアクセスできると思います
試しに service を削除してみるとアクセスできなくなると思います

  • kubectl delete service request-dumper

最後に

kubernetes 上に DockerHub で公開されているイメージを引っ張ってきてコンテナを起動してみました
特に考える点もなくそのまま素直に使うことができました

現在は minikube 1 台構成なので別のノードを追加して複数ホストでコンテナのデプロイも試してみたいなと思います
と思ったのですが minikube では現在マルチノード機能はないようです
https://github.com/kubernetes/minikube/issues/94

執筆次点では絶賛開発中だったのでそのうち対応されるかもしれません

2018年3月14日水曜日

minikube を使って kubernetes に入門してみた

概要

minikube はローカルで kubernetes クラスタを構築することができるツールです
今回は VirtualBox と minikube を使ってローカル環境に kubernetes 環境を構築してみました

環境

  • macOS 10.13.2
  • minikube 0.25.0
  • kubectl 1.9.2
  • VertualBox 5.1.30r118389

minikube インストール

brew を使って簡単にインストールできます

  • brew cask install minikube

インストールできたか確認してみましょう

  • brew cask info minikube
  • minikube version

minikube コマンドおよび kubectl コマンドが使えるようになっています

minikube ノードを起動する

minikube ノードは kubernetes 内で起動するマスターノードになります
今回ノードを作成する環境は VirtualBox にしています
minikube は VirtualBox 以外の仮想環境 (virtualbox xhyve vmwarefusion hyperkit) もサポートしているので VirtualBox 以外が良いという場合は別の環境を準備してそちらに構築しても OK です

  • minikube start
Starting local Kubernetes v1.9.0 cluster...
Starting VM...
Downloading Minikube ISO
 142.22 MB / 142.22 MB [============================================] 100.00% 0s
Getting VM IP address...
Moving files into cluster...
Downloading localkube binary
 162.38 MB / 162.41 MB [============================================]  99.98% 0s
 0 B / 65 B [----------------------------------------------------------]   0.00%
 65 B / 65 B [======================================================] 100.00% 0s
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Starting cluster components...
Kubectl is now configured to use the cluster.
Loading cached images from config file.

--vm-driver というオプションで VM を管理するインフラを指定します
デフォルトだと「virtualbox」が指定されるようです
ISO のダウンロードが始まり minikube 内で VM が起動します

これで VirtualBox 上に minikube というノードが構築されます

  • VBoxManage list runningvms

"minikube" {43f30a82-89d7-4341-bc66-adad08d6b21c}

kubectl get nodes しても確認することができます

NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     <none>    58m       v1.9.0

とりあえずコンテナを構築してみる

ノードが構築できたらとりあえずコンテナを作ってみましょう

deployment の作成 (コンテナも起動)

deployment はコンテナ定義になります
run コマンドを使うことで deployment の作成とコンテナの起動を同時に行えます

  • kubectl run hello-minikube --image=k8s.gcr.io/echoserver:1.4 --port=8080

今回は echoserver という Web アプリのイメージを使います
8080 ポートでアプリが起動するようにオプションを付与しています

  • kubectl get deployments

これで deployment ができているか確認しましょう

service の作成

service は deployment の上位的な概念でコンテナのスケールやバランシングを行います
今回は作成したコンテナを外部に公開するために使います

  • kubectl expose deployment hello-minikube --type=NodePort

NodePort はコンテナにアクセスのにノードのポートを使ってアクセスすることが出来るタイプです

  • kubectl get services

とするとサービスの一覧を確認することができます

動作確認

あとはコンテナにアクセスしてみましょう

  • kubectl get pod

でコンテナが起動していることを確認して

  • curl $(minikube service hello-minikube --url)

でアクセスしてみましょう
今回の Web アプリはリクエスト情報をダンプするようなアプリなのでヘッダやボディの情報が返ってくると思います

今度は動きを確認しながら別のコンテナを起動してみる

deployment と service の動きをもう少し確認してみたいと思います
今後は別のコンテナを起動してみましょう
まずは deployment だけ作成してみます

  • kubectl run kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1 --port=8080

google-samples/kubernetes-bootcamp は簡単な nodejs アプリです
8080 で LISTEN するようにします

この状態ではコンテナ外部からアプリにアクセスすることはできません
なので今度はコンテナに直接入ってみます

  • kubectl get pods

で起動したコンテナの名前を確認しましょう
そして exec コマンドを使って bash を起動しコンテナに入ってみます

  • kubectl exec -it kubernetes-bootcamp-5dbf48f7d4-bt9b5 bash

これでコンテナに入れると思います
コンテナ内で直接アプリにアクセスできることを確認しましょう

  • curl localhost:8080

これだとノード経由でコンテナにアクセスできないので、そのために serivce を定義してあげます

  • kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080

これで kubectl get services を確認すると 8080 ポートがノード側のポートに動的にバインドされているのが確認できます
ノード側は今回は minikube しかないので minikube のポートにアクセスしています

NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP   10.96.0.1       <none>        443/TCP          1h
kubernetes-bootcamp   NodePort    10.110.89.189   <none>        8080:32127/TCP   2m

あとはバインドされたノード側のポートにアクセスすればコンテナにアクセスできるようになります

  • curl $(minikube ip):32127

後処理

service -> deployment -> minikube 停止とすれば OK です

  • kubectl delete service hello-minikube
  • kubectl delete deployment hello-minikube
  • minikube stop

当然ですが service を delete した段階ではコンテナは削除されず deployment を削除したタイミングでコンテナも削除されます

Tips

minikube の管理用 UI を開く

  • minikube dashboard

ラベルを指定して pod を取得する (ラベルは describe service で取得できます)

  • kubectl get pods -l run=kubernetes-bootcamp

最後に

minikube を使ってローカルに kubernetes を試せる環境を構築してみました
かなり簡単に kubernetes を試せるようになったなと感じました

実際にプロダクジョンに適用する場合は minikube ではなく kubeadm という管理ツールがあるのでそれを使ってちゃんとサーバを構築してください

あとは minikube と kubectl コマンドが混同しないようにしましょう
kubectl は kubernetes クラスタ内に存在するコンテナやサービスの操作に使います
minikube は kubernetes クラスタ自体の操作に使うコマンドになります

docker swarm に慣れてしまっている人でも基本的な概念などは似ているのでそこまで取っ付きにくくないかなと思います
機能的には kubernetes のほうが多いので複雑なコンテナマネージメントをやりたい場合には kubernetes を使うことになると思います

参考サイト

2018年3月13日火曜日

Hulu を 2 年 3ヶ月間使ってみた感想

概要

実は Hulu を 2 年間以上使っていたのでその感想をまとめてみました
Good, Bad に分けて書いています
一応上に書いている項目ほどより Good より Bad という感じにしています

環境

  • Hulu 2015/12 から使用
  • 主な視聴デバイス
    • Google Chrome Cast
    • スマホ (iPhone, iPad, Android)
    • Mac Book Air

Good

  • 視聴できるデバイスが豊富に用意されている
    • iPhone, Android アプリが ChromeCast にも対応していたのは嬉しい
  • 無料期間が終了した次の日から月額課金の開始になるのがいい
    • 31 日に申し込んだからと言って次月の 1 日から課金開始ということにはならない
  • 暇な時間を映画やドラマ鑑賞で潰せる
  • 見たいドラマや映画があったときにいつでも見直せる
  • プロ野球のライブ配信をしてくれている
    • もしかしたらもう終了しているかも
    • ただなぜかライブ配信は ChromeCast に対応していない
  • 海外のドラマや映画はかなり充実している
  • オリジナルコンテンツが結構おもしろい (シリコンバレーなど)

Bad

  • 1 アカウントで複数デバイスの同時再生ができない
    • Netflix などは一番下のプランでも同時再生 2 デバイスまではいける
    • 家族で 1 アカウントを共有するスタイルだったので視聴履歴などが共有されてしまう (複数申し込めという話ですが、、、)
  • happyon.jp 事件
    • 日本法人への完全システム移行による有名なドメイン変更
    • 自分のデバイスでは大きく影響を受けたデバイスはなかったが、アプリの更新と再ログインが結構面倒で大変だった
    • というのも使っているデバイスが複数台あったのでアプリの更新とログインをその分行う必要があったので大変だった
    • あと移行当初はかなり重い状態が続き動画の画質が悪くなったなどの問題があった (最近はほとんどなくなったと思う)
  • 回線が重いとほぼ再生できない
    • これはどちらかというと自分の環境の問題かもしれないがインターネットの回線が重くなると動画が再生できない状況になる
    • というのも回線が専有回線ではなくマンションタイプのような共有回線だったので夕方から夜にかけてのゴールデンタイム中は回線が混むためほぼ動画の再生は不可能だった
    • 後述にある Bad で見なくなったという項目があるが、それの原因はこれかもしれない
  • 洋画の英語字幕がほしかった
  • 生活リズムがおかしくなった
    • 一時期海外ドラマにハマってしまい 1 日中ぶっ通しで見てしまう状況が続き生活リズムが崩れた (単純に自分のせいですが、、、)
    • 時間を決めて何時以降は強制的に見れないようになる、みたいな機能があると実は嬉しいかも
    • これと併せて外に出る機会が少なくなった気がする
    • 月額課金をしているせいか、見なければいけないという衝動にかられ無駄に見てしまう
  • 実はあまり見ていないということに気づく
    • ハマると永遠と見続けてしまうのですが、そうじゃない場合は意外と Hulu を見ていないということに気がついた
    • 少ないときだと 1 ヶ月の間で全く見ないという月もあった
    • 見たいコンテンツがなくなったというのが一番の原因かなと思うが、自分の場合は環境の変化 (家族が増えた) も原因の 1 つだと思う
    • 止めよう止めようと思った時期が結構あったが月額 1,000 円程度なのでダラダラと続けてしまっている

総括

一言でいうと良いサービスだと思います
他の人に薦めても恥ずかしくない有料コンテンツだと思います
ただ、料金や機能を考えると Netflix や AmazonPrime の方が良い点は多いかなと思います
配信されているコンテンツの総数は Hulu の方が多い印象ですが、アニメに限っては Netflix の方が強いイメージがあったり AmazonPrime に関しては EC サービスの Amazon.com 自体の恩地を受けれるのも大きいかなと思います

それぞれメリット・デメリットはあると思うので自分にあった動画配信サービスを選択してくださいというのが全てですが、料金もそこまで高額ではないので、いろんなサービスを転々として試してみるのもいんじゃないないかと思います

自分のこの機会にいい加減別サービスに移行してみようと思います

2018年3月10日土曜日

Swift4 でアラートを表示する方法

概要

UIAlertController を使います
いろいろとサンプルは転がっているので備忘録として残して残しておきます

環境

  • macOS 10.13.2
  • Xcode 9.2 (9C40b)

サンプルコード

func showAlert() {
    let alert = UIAlertController(title: "Let's play the game.", message: "You have not got history yet.", preferredStyle: UIAlertControllerStyle.alert)
    let ok = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)
    alert.addAction(ok)
    self.present(alert, animated: true, completion: nil)
}

これを画面が遷移した直後に表示したい場合は viewDidLoad ではなく viewDidAppear でコールしましょう
例えば以下のような感じです

override func viewDidAppear(_ animated: Bool) {
    if results.isEmpty {
        showAlert()
    }
}

viewDidLoad でコールすると Warning: Attempt to present * on * whose view is not in the window hierarchy という警告が表示されアラートが表示されないので注意してください

2018年3月9日金曜日

Xcode9 で UITableView に出てくるなぞのスペースを削除する方法

概要

UITableView を Storyboard で配置するとセルの周りになぞのスペースが入っています
左端に寄せたい場合などには邪魔になるので削除方法を紹介します

環境

  • macOS 10.13.2
  • Xcode 9.2 (9C40b)

使用するコード

以下の簡単な UITableView を使ったサンプルコードを準備しました

ViewController.swift

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!
    let data = ["ピカチュウ", "カイリュウ", "ヤドラン", "ピジョン", "コダック"]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let label = cell.viewWithTag(1) as! UILabel
        label.text = data[indexPath.row]
        return cell
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
}

storyboard から UITableView を IBOutlet を紐付けています
また、UITableViewDelegate, UITableViewDataSource を継承して最低限の cellForRowAtIndexPathnumberOfRowsInSection を実装しています

Main.storyboard

storyboard は以下のような感じです

uitable_remove_space1.png

やったこととしては

  • UITableView を配置
  • UITableView 内に UITableViewCell を配置
  • 「cell」という identifier を設定
  • Cell 内に UILabel を配置し背景をわかりやすいように色付け
  • UILabel に Tag = 1 を設定
  • UILabel に constraints を追加、上下左右に余白 0 の位置を指定
  • Outlets で ViewController に delegate と dataSource を紐付け

になります

これでとりあえず実行すると以下のようになぞのスペースができています
uitable_remove_space2.png

なぞというわけではないですがデフォルトでは Cell 内にマージンを作ってくれるようです
自分は不要なケースが多いので削除するのですが Xcode9 でやり方を知らなかったのでその方法を紹介します

削除方法

UILabel に設定した constraints を選択します
そして「Trailing Space to」を選択します
uitable_remove_space3.png

そして「First Item」を「Superview.Trailing」にします
すると Constant が 15 になっているのでこれを 0 にします
uitable_remove_space4.png

するとマージンがなくなります
今のは右側のマージンを削除したので、これと同じことを左、上下に対して行います


uitable_remove_space5.png


uitable_remove_space6.png


uitable_remove_space7.png

これで再度起動すると以下のようになります
uitable_remove_space8.png

ただこのままだとセルを区切るための線が一番左に行っていません
なので UITableViewCell の「Separator Inset」を「Custom」にし Left の値を 0 にすることで線も一番左まで行きます

最後に

UITableView で出来てしまうマージンを削除する方法を紹介しました
これを UIView でやればその中にコンポーネントを入れることができます

やってみて気づいたのですが、たぶん削除する必要はないかなと思っています
1 からセルをカスタマイズしたい場合には一度 UIView でマージンを潰したあとでデザインするのはありだと思います

2018年3月8日木曜日

Firebase Swift で observe が複数回コールされてしまう場合に確認すること

概要

Firebase からデータを取得する場合 observe or observeSingleEvent を使うと思います
observeSingleEvent の場合は特に問題ないのですがリアルタイムでデータを取得したい場合には observe を使います
その場合に何故か 1 度しかデータを更新していないのに observe が複数回コールされる現象に遭遇したので対策を紹介します

環境

  • macOS 10.13.2
  • Xcode 9.2 (9C40b)
  • Firebase 4.8.2

確認する点

いくつか紹介します

単純にデータを複数回更新していないか

Swift からデータを更新する際には setValue or updateChildValues を使うと思います
単純にこれらが複数回コールされていないか確認しましょう
例えば touchDown などのイベントが発生したら上記メソッドを呼ぶようなケースだとすると連打などの対策をしていないと同じデータが複数回更新される可能性があります

Firebase の場合データが同じであっても更新イベントがあれば observe してしまいます

observe を removeObserver していない

自分はこっちでハマリました
observe の場合 removeObserver をしないといくら View が変わってもデータの監視が続いてしまうようです
これは SpriteKit でも同様で SKScene が変わった時に removeObserver していないと複数回コールされてしまうことがあります

removeObserver は例えば以下のように使います

var bmref = DatabaseReference()
var handler: UInt = 0

bmref = rootRef.child("path/to/data")
handler = bmref.observe(.value, with: { (snapshot) in
    bmref.removeObserver(withHandle: handler)
    move(to: scene)
})

move(to :scene) は適当にこちらで定義したシーンを移動するためのメソッドです
要するにシーンの移動直前に removeObserver を呼ぶようにしましょう
removeObserver の引数には observe を識別するためのハンドラが必要です
これは observe を登録した際の戻り値として取得することができます
ハンドラはインスタンス変数などにして別のメソッドからも参照できるようにしておきましょう

これで observe を削除することができます

observe が複数コールされる場合の影響

自分の場合は SpriteKit 上で使っていたのですが observe が複数回コールされることで fps が低下するという現象が発生しました

具体的に言うと SKAction.move などで動いているノードがある状態で observe が複数回コールされるとノードが一瞬 (0.5 - 1 秒ほど) 停止する状態になります
これが必要最低限のみコールされるようになることで fps が落ちることなくノードが動き続けてくれます

最後に

Swift + Firebase で observe が複数回コールされてしまう場合の対策を紹介しました
observe が削除されていない場合には削除するように修正してみてください

それでも重い、fps が落ちるという場合には単純に Firebase に対してアクセスしすぎていないか確認してみてください (1 秒間に 100 回データを更新するような処理をしていないかなど)

そういう場合にはずっと observe しないで定期的に observeSingleEvent するような仕組みに変更するようにしてみてください

2018年3月7日水曜日

Mac でスクロールの方向をコマンドで変更する方法

概要

普通は「システム環境設定」でマウスから変更します
いちいち開くのが面倒なのでターミナルからコマンドで一発で変更してみます

環境

  • macOS 10.13.2

コマンド

ナチュラル方向にするコマンド

  • defaults write -g com.apple.swipescrolldirection -bool true

逆の方向にするコマンド (Windows マウスなどを使う場合)

  • defaults write -g com.apple.swipescrolldirection -bool false

defaults read -g com.apple.swipescrolldirection で確認すると 0 or 1 なのですがセットするときは bool でセットします

2018年3月6日火曜日

macOS アプリのアイコンは「Icon Set Creator」を使おう

概要

iOS アプリのアイコン作成は MakeAppIcon が最強だと過去に紹介しました
MakeAppIcon だと macOS 用のアイコンが作成できなかったので別のものを探したら「Icon Set Creator」という便利ツールがあったので紹介します

環境

  • macOS 10.13.2
  • Icon Set Creator 1.1.6

インストール

AppStore からインストールします
https://itunes.apple.com/jp/app/icon-set-creator/id939343785?mt=12

インストールできたら開きましょう

アイコンを作成する

使い方は簡単です
アイコンを作成したい元画像をドラッグアンドドロップしましょう
icon_set_creator1.png

ファイルを開いたら左下の Platform で「macOS」を選択しましょう
icon_set_creator2.png

あとは「Go!」を選択するだけです
アイコンを出力するフォルダを選択しましょう
icon_set_creator3.png

すると「macOS」というフォルダが作成されます
中を見ると以下のような感じです
icon_set_creator4.png

これを xcode で作成したプロジェクトにコピーするだけです

AppIcon.appiconset をコピーする

あとは丸ごとコピーするだけです

  • cd /path/to/xcode-workspace/test-proj/test-proj/Assets.xcassets
  • cp ~/Downloads/icons/macOS/AppIcon.appiconset/* .

これで Xcode から Assets.xcassets を見ると以下のようになっています
icon_set_creator5.png

最後に

Icon Set Creator を使って macOS 用のアイコンを作成してみました
できたファイルたちをコピーするだけなので iOS のときとほぼ同じ感じでできます
Web ではなくアプリとして動くのでそこだけ違う感じでしょうか

2018年3月5日月曜日

time コマンドの format を使ってみた

概要

time コマンドと言っても format が使えるのは /usr/bin/time のほうになります
サンプルを紹介します

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

サンプルコマンド

(/usr/bin/time --format="{\"real\":%e,\"sys\":%S,\"user\":%U,\"time\":`date +"%Y%m%d%H%M%S"`}" curl -sL 192.168.0.2 > /dev/null) &>> log

こんな感じ
上記は curl を実行した時間を計測しています
JSON で出力して結果を log ファイルにリダイレクトしています
format ないでは eval することもできるので普通のコマンドの結果も含めることができます

2018年3月3日土曜日

ESXi から紐付いている vCenter を見つける方法

概要

vCenter に登録されているホストは簡単に見つけることができます
逆に ESXi から登録されている vCenter を知りたいケースもあると思います
そんな場合に役に立つ方法を紹介します

環境

  • Ubuntu 16.04
  • Ruby 2.3.1p112
  • rbvmomi 1.11.3

サンプルコード

rbvmomi を使って取得しています
moref を使って取得しているだけなので直接 SOAP をコールしても問題ありません

require 'rbvmomi'

vim = RbVmomi::VIM.connect(
  host: 'your-esxi-ip-or-hostname',
  user: 'esxi-username',
  password: 'esxi-password',
  insecure: true
)

vim.serviceContent.rootFolder.childEntity.each { |dc|
  dc.hostFolder.childEntity.each { |compute|
    compute.host.each { |host|
      puts host.summary.managementServerIp
    }
  }
}

こんな感じです
moref で見るとこの階層です
esxi_belong_vcenter1.jpg

ID/PW が同じであれば、ESXi の情報をリストにして順番に検索すれば vCenter にぶら下がっているかどうかわかります

2018年3月2日金曜日

Swift でネットワークに接続されているか確認できる ReachabilitySwift をさらっと試してみた

概要

Swift でネットワークに接続しているかどうか確認するのに Reachability.swift というライブラリがあったので使ってみました
サンプルが動作するか確認したレベルです

環境

  • macOS 10.13.3
  • Xcode 9.2 (9C40b)
  • ReachabilitySwift 4.1.0

ライブラリインストール

  • pod init
  • vim Podfile
pod 'ReachabilitySwift'
  • pod install

サンプルコード

  • vim ViewController.swift
import UIKit
import Reachability

class ViewController: UIViewController {
    let reachability = Reachability()!
    override func viewDidLoad() {
        super.viewDidLoad()
        reachability.whenReachable = { reachability in
            if reachability.connection == .wifi {
                print("Reachable via WiFi")
            } else {
                print("Reachable via Cellular")
            }
        }
        reachability.whenUnreachable = { _ in
            print("Not reachable")
        }
        do {
            try reachability.startNotifier()
        } catch {
            print("Unable to start notifier")
        }
    }
}

let reachability = Reachability()! はインスタンス変数として定義するようにしてください
ローカル変数だと初めの 1 回しか監視できません

closure でリスナーを登録してネットワークの状態が変化したら呼ばれるようにします
リスナーを登録したら reachability.startNotifier() でネットワークの監視を初めます
基本的には必要なときに start して終了したら stop したほうが良いと思います
このリスナーの中でフラグを ON/OFF するような仕組みにすればアプリがネットワークにつながっているかチャックできると思います

ちなみに実機でテストして Wifi などを切ったりすると実際にコールされるリスナーの動きを確認することができると思います

参考サイト

2018年3月1日木曜日

SwiftRealm で update 処理

概要

前回、基本的な操作方法を紹介しました
今回は更新系の処理を試してみました

環境

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

クラス定義

まずはクラスを定義しましょう
シンプルに id 属性と name 属性を持つクラスを定義します

import RealmSwift

class User: Object {
    @objc dynamic var id = 0
    @objc dynamic var name: String? = nil

    override static func primaryKey() -> String? {
        return "id"
    }

    static func insertTestData() {
        let names = ["bob", "tom", "jessie"]
        var users: [User] = []
        for (id, name) in names.enumerated() {
            let user: User = User()
            user.name = name
            user.id = id
            users.append(user)
        }
        let realm = try! Realm()
        try! realm.write {
            realm.add(users)
        }
    }

    static func debug() {
        let realm = try! Realm()
        let user = realm.objects(User.self)
        for u in user {
            print(u.id)
            print(u.name!)
        }
    }
}

そしていきなりポイントですが id 属性を Primary Key として登録しています
更新系の処理を行う場合はこの定義がないとエラーとなります

あとテストデータを挿入するためのメソッドと挿入したデータを表示するだけのメソッドを追加しています

一旦実行する

今回はシングルビューアプリケーションにしたので ViewController 側で定義したクラスを呼び出します

override func viewDidLoad() {
    super.viewDidLoad()
    User.insertTestData()
    User.debug()
}

実行結果は以下の通りです

0
bob
1
tom
2
jessie

更新するためのメソッドを追加する

先ほどの User クラスに以下のメソッドを追加しましょう

static func update(id: Int, name: String) {
    let realm = try! Realm()
    let user = User()
    user.id = id
    user.name = name
    try! realm.write {
        realm.add(user, update: true)
    }
}

id と name を指定して該当する id のレコードを更新します
ポイントは add を引数付きでコールする点で update: true を付与すると Primary Key で該当するレコードがあるとそのレコードを更新してくれます
実行してみましょう

override func viewDidLoad() {
    super.viewDidLoad()
    User.update(id: 0, name: "holly")
    User.debug()
}

新規でレコードを追加せず指定した id のレコードが更新されているのがわかります

0
holly
1
tom
2
jessie

該当する id がない場合の挙動

例えば今回の場合 User.update(id: 3, name: "holly") という感じでコールすると以下のようになりました

0
holly
1
tom
2
jessie
3
holly

所謂 upsert の挙動になります
該当するレコードがある場合は更新し、ない場合は新規のレコードとして追加します

ちなみに update: false にして同じように id = 3 のユーザを登録しようとするとプライマリキーが重複しているよーというエラーになります

Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to create an object of type 'User' with an existing primary key value '3'.'

該当するレコードがない場合に追加しないようにするには

それ専用のメソッドがないようなので条件に合うレコードがある場合は更新してない場合は何もしないというメソッドを定義します

static func updateOnly(id: Int, name: String) {
    let realm = try! Realm()
    let user = realm.objects(User.self).filter("id == \(id)")
    if !user.isEmpty {
        try! realm.write {
            user[0].name = name
        }
    }
 }

あとは以下のような感じでコールすれば id = 10 の場合にエラーとならずかつ新規で追加もされません

override func viewDidLoad() {
    super.viewDidLoad()
    User.updateOnly(id: 3, name: "jobs")
    User.updateOnly(id: 10, name: "none")
    User.debug()
}

最後に

RealmSwift で update 処理を試してみました
ポイントは Primary Key を定義する点かなと思います