About Me

My photo
Software Engineer at Starburst. Maintainer at Trino. Previously at LINE, Teradata, HPE.

2018-12-25

Try HDP 3.0.1

Started using HDP 3.0.1. Past version I was used is 2.6.5, therefore I found many changes, new Ambari design, hive command starts beeline and so one.


One major change to me, hive view has been deleted since HDP 3.0.
https://community.hortonworks.com/questions/202444/where-is-hive-view-on-hdp-3.html

Tried Data Analytics Studio instead of that.

You can move the page by
Ambari → Services → Data Analytics Studio  → Data Analytics Studio UI
http://sandbox-hdp.hortonworks.com:30800

I guess it's BI tool from the name, but it seems the DBA tool. It has 4 components.

1. Queries
You can see query history. And Recommendations tab gives us suggested changes.
Queries
Recommendations

Query Details

Visual Explain

Configs

Timeline

DAG Info


2. Compose
This is almost the same as class hive view. We can use auto suggest.
Compose

3. Database
Partitions tab shows column name, column type and a comment if the table is partitioned. I want to see partition's numbers, size and so on.
Columns

Partitions

Storage Information

Detailed Information

Statistics

Data Preview

4. Reports
This page visualizes lineage from the query history. It looks really useful. 
Reports

Read and Write Report
Join Report

2018-12-23

Trino Storage Connector

When I played Apache Drill, I felt it's useful to analyze local csv and parquet. Then, I developed a Trino connector to support accessing for local files. This is GitHub repository.

As you may already know, Trino specify a table name like `catalog.schema.table`. The connector identify the file type from schema. Current supported types are  csv, tsv, txt, raw and excel. All types except for raw returns multi rows if the file contains EOL. A raw type returns 1 column and 1 record. I assume the raw type can be used for converting to JSON file.

`table` name will be like below. You can access local and remote file.
"file:///tmp/numbers.csv"
"https://raw.githubusercontent.com/ebyhr/trino-storage/master/src/test/resources/example-data/numbers.tsv"

Here is the entire query example. The `csv` on schema and the extension of `file:///tmp/numbers.csv` are not irrelevant. Therefore, if you change the schema to `tsv`, it returns the result split by tab.

select
  * 
from 
 storage.csv."file:///tmp/numbers.csv"
;

Current Trino doesn't have importer like PostgreSQL's COPY. This connector may useful in such case🍭

2018-12-08

AES CBC ISO7816 and SHA256 on Java

This is Java snippet using AES/CBC/ISO7816-4Padding and SHA256. javafx.cypto doesn't support ISO7816 padding, therefore I use org.bouncycastle.

The group id and artifact is is below and my sample code uses 1.52.
groupId: org.bouncycastle
artifactId: bcprov-jdk15on
version: 1.52

import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public final class Encryptor {

private static final byte[] KEY = "0123456789abcdef".getBytes();
private static final byte[] INIT_VECTOR = "abcdef0123456789".getBytes();

static {
Security.insertProviderAt(new BouncyCastleProvider(), 1);
}

private static String encrypt(String value)
throws Exception
{
SecretKeySpec keySpec = new SecretKeySpec(KEY, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/ISO7816-4Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(INIT_VECTOR));
byte[] encrypted = cipher.doFinal(value.getBytes());
return DigestUtils.sha256Hex(encrypted);
}
}

2018-12-07

Try HBase Java Client

This is HBase Java test example. My repository is here (https://github.com/ebyhr/hbase-embed). I've changed log level to INFO because default log level is little noisy.

import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.TableNotDisabledException;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class HBaseTest {
    private static final HBaseTestingUtility HBASE = new HBaseTestingUtility();
    private static final byte[] TABLE_NAME = Bytes.toBytes("EMPLOYEE");
    private static final byte[] CF = Bytes.toBytes("cf1");
    private static final List PEOPLES = Arrays.asList(
            new People("山田", 20),
            new People("영수", 33),
            new People("John", 18)
    );

    @BeforeAll
    static void setUp()
            throws Exception
    {
        HBASE.startMiniZKCluster();
        HBASE.startMiniCluster();

        HTable table = HBASE.createTable(TABLE_NAME, CF);
        for (People people : PEOPLES) {
            int i = PEOPLES.indexOf(people);
            String rowKey = "rowkey" + i;
            Put put = new Put(Bytes.toBytes(rowKey));
            put.add(CF, Bytes.toBytes("name"), Bytes.toBytes(people.name));
            put.add(CF, Bytes.toBytes("age"), Bytes.toBytes(people.age));
            table.put(put);
        }
    }

    @AfterAll
    static void tearDown()
            throws Exception
    {
        HBASE.cleanupTestDir();
    }

    @Test
    void testAccessTable()
            throws IOException
    {
        HTable table = new HTable(HBASE.getConfiguration(), TABLE_NAME);
        String rowKey = "rowkey1";
        Result getResult = table.get(new Get(Bytes.toBytes(rowKey)));
        assertEquals("영수", Bytes.toString(getResult.getValue(CF, Bytes.toBytes("name"))));
        assertEquals(33, Bytes.toInt(getResult.getValue(CF, Bytes.toBytes("age"))));
    }

    @Test
    void testDrop()
            throws Exception
    {
        HTable table = HBASE.createTable(Bytes.toBytes("TMP-TABLE1"), CF);
        HBaseAdmin admin = HBASE.getHBaseAdmin();
        assertThrows(TableNotDisabledException.class, () -> admin.deleteTable(table.getTableName()));
        admin.disableTable(table.getTableName());
        admin.deleteTable(table.getTableName());
    }

    private static class People {
        private String name;
        private int age;

        private People(String name, int age)
        {
            this.name = name;
            this.age = age;
        }
    }
}

2018-11-29

Kanazawa Trip

Went to Kanazawa at Oct 27 and 28. I would recommend to eat 'Fu' in addition to the seafoods. It was really delicious.
Day 1st  金沢駅→ひがし茶屋街→香林坊→兼六園→近江町市場→銭湯→ホテル
Day 2nd 近江町市場→金沢21世紀博物館→鈴木大拙館→金沢駅
I wanted to go Kanazawa Umimirai Library, but it was closed. I should have investigated it before booking the hotel.



Kanazawa st.

Misodare Dengaku Fu


Noren

Kenrokuen








GNOME

Sento

Omicho-Ichiba

21st Century Museum of Contemporary Art, Kanazawa








D.T. Suzuki Museum


2018-11-26

LINE Developer Day 2018

Went to LINE Developer Day 2018 at Happo-en. I mainly attended the session about Data Engineering. Whereas all talks was helpful, infrastructure by Saegusa-san and Kafka by Kawamura-san were especially fascinating.
  • Opening Session - "Next LINE" LINEが創る新たな世界 -
  • LINEが目指す理想の広告プラットフォーム
  • LINEのインフラプラットフォームはどのように大規模サービスをスケールさせ運用コストの小さなインフラを提供しているのか
  • Multi-Tenancy Kafka cluster for LINE services with 250 billion daily messages
  • Efficient And Invincible Big Data Platform In LINE
  • OASIS - Data Analysis Platform for Multi-tenant Hadoop Cluster
  • Closing Session LINEが創る理想のDeveloper Relations


2018-10-05

Aster debug the SQLMR

Aster AnalyticsのSQL-MR関数をデバッグする方法です。ログだけで原因が特定できず、更にはサポートチームで再現できない時には非常に役に立つTipsかと思います。
流れとしては対象の関数(jar, zip)をサーバーからダウンロードし、それを読み込んだSQL-MR UDFを作成後にインストール、最後に実行です。

今回Random Forestの実行中に元データが約50MBしか無いにも関わらずJVMのMaxHeapSizeを120GBまで設定してもOOMになるという事象が発生したので、その時のデバッグの流れを記載してみます。
Asterのインタフェースから実行したのはforest_drive関数ですが、AMCでログを確認したところエラーとなっていたのは内部で実行されているforest_builderだったので、その関数を調査のついでに修正してみました。

1. 関数のダウンロード
どこに格納されているかは環境に依存してしまいますが、関数名.zipもしくは関数名.jarでファイルを探せば見つかるはずです。VM環境では以下のパスに存在していました。
/data/ncluster/tmp/cleanonreboot/WorkerDaemon/tasks/218.0/tmp/filedump/public

2. ライブラリをインポートしたプロジェクトを作成
Javaのプロジェクトを作成します。間違えて元の関数を上書きしないよう別名にしておきましょう。今回はforest_builder_debugにしました。
ライブラリの追加はIntelliJであれば、各jarファイルを右クリック→Add as a libraryしていきます。
次に追加した関数のpackageと同じ階層でデバッグ用のコードも合わせ、jarファイルからコピー&ペーストします。

packageはforest_builderであれば以下のようになっていました。
package com.asterdata.sqlmr.analytics.predictive_modeling.decision_tree.forest

3. エラーメッセージから原因の場所を探すとscanRowsメソッド内の以下の部分にたどり着きます。forループの後ろにobjectOutputStream.reset();を追加したところ、大幅にメモリ使用量が抑えられ、50MBのデータも問題なく実行できるようになりました。

try {
    int e = poissonGenr.nextInt();
    ObjectOutputStream objectOutputStream = (ObjectOutputStream)ex.get(var13);

    for(int j = 0; j < e; ++j) {
        objectOutputStream.writeObject(var12);
    }
    objectOutputStream.reset(); // Added
} catch (Error var10) {
    throw new IllegalUsageException("Please increase JVM size to avoid Java heap out of space");
}

4. インストール
actでログインし、インストールコマンドを実行します。ファイル名しか指定できないのでファイルがあるディレクトリまで移動しておく必要があります。
\install forest_builder_debug.jar
(アンインストールの場合は\remove forest_builder_debug.jarです)

5. 実行
実行時はエラーとなった際に使用しているクエリの関数名部分を入れ替えるだけです。

2018-10-04

Bulk Insert to Teradata using Python ​


This snipet is bulk-loading csv to Teradata via Python. Recently teradatasql was released, but this code uses PyTd. If you haven't setup PyTd, please install the library by `pip install teradata`.

import teradata
import csv

udaExec = teradata.UdaExec()
session = udaExec.connect("tdpid")
data = list(csv.reader(open("testExecuteManyBach.csv")))
batchsize = 10000
for num in range(0, len(data), batchsize):
    session.executemany("insert into testExecuteManyBatch values (?, ?, ?, ?)"),
    data[num:num+batchsize], batch=True)


The points are batch=True and specifying batchsize. If you don't set the batchsize or the size is too large, it will be failed (forgot the actual message though). The performance in my environment (1 node) was 10,000 rows/sec. The table has 4 columns. I assume tens of thousands looks fine, but more rows should be imported with FastLoad or MLOAD.

2018-08-25

XMLAGG in Teradata

SQLServerやPostgreSQLにあるstring_aggやMySQLのgroup_concatですが残念なことにTeradataでは存在しません。代わりにという訳ではないですが、xmlaggという集約関数があり、これを使うと同じようなことが実現できます。

まずはデータを準備します。
drop table test_xml_agg
;
create table test_xml_agg (
 c1 int
,c2 int
,c3 varchar(10)
)
;
insert into test_xml_agg values (1,1,'hello');
insert into test_xml_agg values (1,2,'world');
insert into test_xml_agg values (2,1,'this');
insert into test_xml_agg values (2,2,'is');
insert into test_xml_agg values (2,3,'xmlagg');

上記のデータを1列目で集約し、2列目の順番で一度3列目を横に展開しカンマで結合するというクエリを書いてみます。
select
 c1  
 ,trim(trailing ',' from xmlagg(c3 || ',' order by c2) (varchar(100))) as string_agg
from test_xml_agg
group by 1
;

Result Set
c1 string_agg
1  hello, world
2  this, is, xmlagg


string_aggに比べるとごちゃちゃしていますが、まずxmlagg(c3 || ',' order by c2)で2列目の昇順で3列目を結合していくことを表しています。続いて(varchar(100))で型をsysudtlib.xmlからvarcharにキャストし、最後に末尾のカンマを除いています。

2018/10/19追記
tdstats.udfconcatという関数がひそかに存在すると同僚が教えてくれました。AutoStatsで統計対象カラム名を連結するための関数とのことです。
UDFCONCAT

2018-08-18

GCPUG BD & ML 2018

Google Cloud Next 2018 Extended BigData & ML Dayに行ってきました。前半はメモレベルですが今回のミートアップについて、後半はBQMLとAsterの簡単な比較について書いてみます。BigQueryでHardware Accelerationを使ってるのは初めて知ったんですが、結構驚きでした。

# BigQuery New Feature by satoluxx
BQML
- 今のところ線形回帰とロジスティック回帰のみが使用できる
本筋とは関係ないですが、モデルの説明を画面に保存できるの地味に凄い助かるやつですね...! Create Model文も自動で保存してくれていたりしたら言うことなしな気がします。

GIS
- PostGISで実装されている
- WKTとGeoJSON (ShapeFileは変換する必要あり)
- 現在はベーダだが可視化もできる

Clusterd Tables
- 今まではテーブルの分割は時間軸だったのに対し、任意のカラムでサブテーブルに分ける(パーティションのイメージ)

# Cloud TPU by kazunori_279
TPU
- John Hennessy:ソフトウェアだけで解決できる時代は終わりつつある → Domain Specific Hardwareが必要
- 実はBigQueryはHardware Accelerationを使っているので速い
- GPUは電力を食う、DCの費用の1/3~1/2は電力代とも言われている。これが理由で東京のDCはGPUを置かせてくれなかったりする
- GPUも並列とはいえCPUと同様の処理が動いている
- TPUはメモリへのアクセス回数を減らすことで性能および電力消費量を大幅に改善している
- Cloud TPU v2 の料金体系はNormal とPreemptible(優先度低)で値段差がある
- DAWNBenchでベンチマークおよびコストパフォーマンスが公開されている

TPU Pod
- MPIライクなものを使ってTPU同士で通信している(all reduce)
今まではParameter ServerとNodeはgRPCで通信している
- eBayの事例ではaccuracy +10%, 100x faster。試行錯誤の回数も増えるのでaccuracyも向上

# Auto ML by shuhei_fujiwara
Auto ML Vision
- Cloud Storageに画像をアップロードし、csv(path, label)を用意し、実行するとWebAPIが生えてくる

AutoML Natural Language
- CSVをアップロードする(文書, カテゴリ)

AutoML Translation
TMXファイルをアップロードする
English→Japneseは既にあるが、逆はまだ

学習後に学習過程の遷移やConfusionMatrix、間違った部分なども表示してくれる
- AutoMLのモデルダウンロードは時期が未定だがいずれ可能になる予定

モデルの作成の価格
- Auto ML Vision (20$/per)
- AutoML Natural Language (3$/per)
- AutoML Translation (76$/per)
VisionはNASNetがベースとなっている

~~~

次に、BQMLとAsterの簡単な違いについて見ていきたいと思います。Teradataの製品であるAsterもBQMLと同様にSQLで機械学習を行えます。現在BQMLは線形回帰とロジスティック回帰の2つのようですが、Asterはなななんと100種類以上もの関数が用意されていて、NeuralNetXGBoostなんかもサポートしています。各関数はJavaで書かれているのですが、足りない関数があれば自分で書いてUDFとしてインストールできるのが個人的には気に入っています。両者をシンタックスレベルで並べると以下のようになります。Aster側は見慣れていないとやや奇妙な印象を受けますね。BQMLはシンタックスから想像する限り、説明変数Numeric/Categoryは自動で判別してくれているのでしょうか...?もしそうだとしたら良いですね〜。

-- BQML (Train)
CREATE  `bqml_model` 
OPTIONS(model_type='logistic_reg') AS
SELECT
  label,
  os,
  is_mobile,
  country,
  pageviews
FROM
  `bigquery-public-data.google_analytics_sample.ga_sessions_*`;

-- BQML (Predict)
SELECT  *
FROM
  ml.EVALUATE(MODEL `bqml_codelab.sample_model`, (
SELECT
  label,
  os,
  is_mobile,
  country,
  pageviews
FROM
  `bigquery-public-data.google_analytics_sample.ga_sessions_*`;

-- Aster (Train)
SELECT * FROM GLM (
  ON (SELECT 1)
  PARTITION BY 1
  InputTable ('aster_train')
  OutputTable ('aster_model')
  InputColumns ('pageviews')
  CategoricalColumns ('os', 'is_mobile', 'county')
);

-- Aster (Predict)
 SELECT * FROM GLMPredict (
  ON aster_test
  ModelTable ('aster_model'')
  Family ('LOGISTIC')
  Link ('LOGIT')
);

ミートアップで紹介されていたリンクはこれから見ていくつもりです。
GCPUG http://gcpug.jp/
Google Cloud Platformのノウハウ https://github.com/gcpug/nouhau
Google Cloud Next 2018 まとめ https://github.com/gcpug/nouhau/issues/60

2018-08-13

Vulture, Syndrome


真山仁さんの「シンドローム」を読み終わりました。タイトルはチャイナ・シンドロームにかけているのかなと想像しています。久しぶりのスピンオフではない本編とあって思わず一気読みしました。内容は東日本大震災を描いたもので、首都電力という東京電力を模した会社が出てきます。事実と虚飾が入り混じっているんだとは思いますが、政府の鈍さや首都電会長の無責任さは見ていて複雑な気持ちになります。そのぐらいリアルだということでしょうが....。人名については当時の方々と照らし合わせると、以下のようになるでしょうか。

濱尾重臣→勝俣恒久
萩本あかね→丸山桂里奈
串村勝之→吉田昌郎
古谷光太郎→菅直人
水野義信→枝野幸男
佐伯昇→鉢呂吉雄

同じく真山さんの原発事故に関する小説のベイジンも読んだことがありますが、福島原発事故のあとに出た本だと思っていたので、出版された年が2008年だったということに驚きました。おおまかなあらすじは北京五輪の開会式に向けて突貫工事で進めた原発が大地震とメルトダウンを起こすといったもので、東日本大地震以前であればそんなこと起きるはずない!と一蹴できていたかもしれない内容です。
どちらの本も事実のみでなく、少し引いた目線から原発事故の流れやそこで働いていたもしくは働いている方々についてもう1度考えてみたい人にお勧めしたい本です。

2018-08-06

Functional Programming in Java



4年近く前に出版された本ですが、Javaによる関数型プログラミングを読んでみました。タイトルをそのまま受け取ると関数型に興味のある人しか読まなさそうですが、遅延評価、末尾呼び出し最適化、メモ化などの話も載っていて、全体的に性能向上に繋がる要素がちりばめられています。末尾の付録にDRY原則が載っているのですが、そこに"DRYの精神の則って、ここではその内容を説明しません。URLの記事を参照してください"と書いてあるのがちょっと面白かったです。

2018-07-17

Money Never Sleeps


言の葉の庭と同じぐらい何度か観ているWall Street: Money Never Sleepsを観ました。一番最初に観たキッカケは父が家で観ていたことですが、その後 真山仁さんのハゲタカシリーズを読んで金融に興味を持ってから2回目以降を観るようになりました。レビューでは前作の方が良かったという人が多いみたいですが、前作は未だに手を出していません。先に新しい方を観ると画質が下がるのが嫌だなぁと適当な理由をつけて避けていたんですが、調べてみたところ前作と2年しか経ってないようなので今度観てみようと思います。
ラストに流れるTalking HeadsのThis Must Be The Placeは何度観てもやっぱり好きで、段々カメラが上を向くに連れておじさんもまた仕事頑張らなあかんなという気持ちになります。この曲はMarvelのAgents of S.H.I.E.L.Dにも使われているみたいですね。全く違うジャンルなので違和感があって面白いです。CD版の音源ももちろん良いですが、YoutubeにあがっているNaive Melody版は雰囲気もあいまって最高です。(この元の動画はどこで買えるのだろう!)

2018-07-05

Hi, YARN-6640

YARN上で動いている、とあるアプリがYARNのバグを踏み抜きました。
AM heartbeat stuck when responseId overflows MAX_INT

これは典型的なオーバーフローのバグで、Application ManagerとResource Manager間のやりとりをint型の変数で応答のIDを管理し毎回インクリメントしているのですが、その値が2147483647に達すると、次回のIDが-2147483648になり、前回のレスポンスとの整合性が取れていないとうことで例外を投げます。YARNの2.8.2で修正されていますが、それ以前のバージョンを使っており、ハートビートの間隔が短いアプリをロングランしているような場合には一度確認してみると良いかもしれません。
なお、AM-RM間だけでなくName Manager-Resource Manger間でも同様のロジックは含まれていました。こちらは2.8.4で修正されています。
NM heartbeat stuck when responseId overflows MAX_INT

2018-07-04

Visualize heatmap on Aster AppCenter

Aster AppCenterでheatmapのCustom Visualizationを作ってみました。
https://github.com/ebyhr/Teradata/tree/master/aac_heatmap



使い方はdb_table_nameにテーブル名を指定して、vizTypeにheatmapを指定します。
heatmapのライブラリは座標をx, y指定するようなデータの持ち方を前提にしているものが多いですが、あえてテーブルをそのまま表示して色付けするようにしています。
insert into app_center_visualizations (json) values (
'{
    "db_table_name": "public.confusionmatrix",
    "vizType": "heatmap",
    "title": "Report by confusionmatrix"
}')

Teradata AppCenterはたまに触っていますが、UXが微妙すぎてあまり使う気が起きません。バックエンドにKubernetesを据えたのは将来的にはいい判断になると思うんですが、現時点ではオーバーエンジニアリングかなと感じます。Javaでちょっとコードを書きたいだけなのにDockerfile書くのは面倒です。

話が変わりますが、最近SQLを二重実行するようなUDFを作ろうかなと考えています。分析クエリで欲しい場面は皆無ですが、バッチ処理で使いたい場面は多々あります。
AsterのSQL-MRであればコードを書くのは3分もあれば終わるのですが、良い名前が思いつかないのでペンディングにしています。
例えば全部のテーブルを削除したい時はこんなクエリになります。Javaであれば中のSELECT文の結果をResultSetで受け取り、ループを回してexecuteQueryを実行するだけです。
select * from xxx ( on (select 1)
 query ('select ''drop table '' || a.schemaname || b.tablename from nc_system.nc_all_schemas a inner join nc_system.nc_all_tables b on a.schemaid = b.schemaid')
)

2018-07-02

Learning Coursera Deep Learning

Courseraでdeeplearning.aiのNeural Networkのコースを受講し始めました。ちょうど同僚がやっていたこともあり、分からないところが出てきた時に聞けるのがだいぶ助かっています。Neural Networks and Deep Learningは終わり、今はImproving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimizationを受講しています。
同僚も同じ現象に遭遇していましたが、Jupyter NotebookでKernelがエラーになり、保存も出来なくなった時は、固まった画面はそのままの状態で、 Courseraの画面を別タブで開き、Open Notebookを押して2つ開いた状態にすると、エラーになっていたNotebookが復活すると思います。



週末に「天空の矢はどこへ?」を読みました。Wシリーズは10月に発売予定の「人間のように泣いたのか?」が最終巻なのですね。待ち遠しいなぁと書こうとしましたが、最終巻が待ち遠しいというのは早く終わって欲しいというようにもとらえることができます。SFが得意なNetflixあたりでアニメかドラマ化してくれると嬉しいのですが、可能性は低いだろうなぁ...。

2018-06-18

Read NEW ELITE

ニューエリート グーグル流・新しい価値を生み出し世界を変える人たちという本を読んでみました。結論から言うと、ちょっと微妙かな...という感想です。色んな会社や起業家の例を出し過ぎて、話が発散しているように思います。読み終わって結局この本が1番伝えたかったのかは何だったんだろうと、若干もやもやっとしてしまいました。要所要所でこれは参考になりそうという部分はもちろんあります。
 個人的にはターゲットとなる読者の層はマネージャー以上やHRの方々な印象を受けたので、そういった方々が読んでみると全く違う感想を持たれるんだろうなぁと。

2018-06-14

Read Learned Indexes Paper

今更ながらA Machine Learning Approach to Databases Indexesを読んでみました。論文の中身はデータベースのインデックス(索引)に機械学習を適用してみようというものです。
インデックスの種類は大きく3つに分けて説明されています。

#1 Range Index
多くのDBで使われているB木との比較がされています。B木がf(key) ➝ posとピンポイントでキーが存在するページの開始位置を表すのに対して、データがあらかじめソートされている前提としてLearned Ranged Indexでは[ˆy − ∆, yˆ + ∆] (∆はmaximum error)の範囲を探しに行くようです。

#2 Point Index
Hash Mapのような1対1のデータ取得についてです。O(1)は改善の余地がないように見えますが、ハッシュが衝突する場合Linked Listを走査するのに時間がかかるので、それを改善するというのが主な狙いのようです。#1で紹介したfをCDF (Cumulative Distribution Function)として、h(x) = mCDF(x)でハッシュを計算します。こうすることで衝突のないハッシュ値を算出しているとのことです。

#3 Existence Index
特定のキーがデータに存在しているかを判断するIndexについてです。Binary Classificationを想定し、1/0のラベルを持ったデータに対してRNN/CNN & Sigmoidで分類し、結果がYesならそのまま、NoならBloom Filterを通してYesへという流れのようです。Bloom FilterがFalse Negativeを起こさないことを活用してるようです。

より詳しい論文がThe Case for Learned Index Structuresで、グラフとコードが少し増えているので、こちらも読むと理解しやすいかもしれません。

2018-05-03

Robot in Shinagawa Prince Hotel

NHK Worldを見てたら品川のプリンスホテルにいるデリバリーロボットが出演してました。従業員からお水を受け取ったら自分でエレベータにのり、宿泊者の部屋まで運んでくれるようです。
Ever-Evolving Shinagawa


Yiingyaさんがロボットの評価に星5つを選択すると「やったぁ〜!」と笑顔を表示しながら左右に揺れているのがなんとも可愛い!名前はHARRYというみたいです。生で見てみたいなぁ〜🤖
http://www.princehotels.co.jp/shinagawa/ntower/robot.html

2018-05-02

SQL as a Visualization


GW中にヒマだったので作っているプチBIツールです。まだまだ作り途中なんんですが、SQLを書いてそのまま可視化できるので何気に便利です。コメントの部分の--visualize=Xでどんなグラフを表示するかを指定し、その後ろで必要なパラメータを入力します。今のところは素のテーブル、棒グラフ、折れ線グラフ、パイチャート、バブルチャート、ゲージチャートは可視化できるようにしてあります。

--visualize=line,x=custkey,y=sumprice
select 
 custkey
 ,sum(totalprice) as sumprice
from tpch.tiny.orders
group by 1
order by 1
;

中身としてはフレームワークはRailsでグラフの部分はHighchartにしました。最初はRubyで簡単にグラフを出せるChartkickを使っていたんですが、描画できる種類が限られているので、Highchartに変更しています。

実運用や利便性を考えると、以下のようなポイントを実装していく必要があるかなと考えています。
  • アクセス権の制御
  • クエリの実行ユーザーの制御
  • クエリ編集時のサジェスチョン
  • パラメータを使用した実行
  • スケジューリング実行
  • 結果のメール配信

Did you cry in doutor?

毎年冬になると鼻づまりが辛すぎるので、日帰りで鼻中隔湾曲症の手術を受けてきました。これで誘拐されて口にガムテープを貼られるような事態になっても窒息しないですみます。
ガーゼを抜く瞬間が相当痛いというのは噂で聞いていたんですが、麻酔の瞬間が1番痛かったです。鼻の奥に注射するんですが、顔面の骨に直接注射しているような感覚で顔がメキメキいってました。完全にノーマークだったのであまりに痛さに放心状態に。サッカーに例えると、格下だと思ってたチームから試合開始前に突然ボディブローを食らうみたいな状況です。あと麻酔薬に心拍数をあげる作用もあるようで、注射をされただけなのに全力疾走したあとみたいに胸がどきどきしたのには焦りました。手術が始まったあとは当然痛みはなく、代わりにノミでゴンゴンやられたり、骨をジョキジョキするような不快な感触との戦いです。途中ちょっと気持ち悪くなってしまい、1, 2分休憩を挟んでもらいました。終わったあとに削り取ったあとの骨を見せてもらったんですが、いやいや取り過ぎやろ...というぐらいの量でした。術後は1時間ほどベッドで休憩した後に退院です。
術後の痛み自体はそんなに無いんですが、口呼吸しかできないというのがかなりストレスではあります。最終的には慣れましたが、手術当日はほとんど眠れず。痛みだけで言えば親知らず抜いた後の方が痛かったです。
ガーゼ抜きの瞬間は気持ちの悪い感触こそあれど、全く痛みはありませんでした。一瞬だけ鼻の通りを実感できますが、スポンゼルと綿球をまたもや詰め込まれます。この日からだんだん食欲も湧いてきたんですが、いかんせん味が感じられないので、これは今ゴマの味がしているはず…と自分を思い込ませながら食べていました。
最後のスポンゼルを抜いた後は感動するほど鼻が通ることを期待してたんですが、元々点鼻薬を使ってたこともあり、手術前を10だとしたら手術後が12ぐらいの感覚です。朝早く病院に行ったのでドトールで軽食を取ろうと寄ったんですが、頼んだホットドッグからパンの香りが感じられたのには、泣きそうになりました🌭

初診から最後のスポンゼル抜きまでのスケジュールはこんな感じでした。
3/31 初診 CT&血液検査
4/9  手術前の確認
4/25 手術
4/27 ガーゼ抜き&スポンゼル詰め込み
5/1 スポンゼル抜き

2018-04-27

New EOM Style

仕事で使っているとある製品が結構な量の不具合がある状態で、EOMになってしまい悲しい。不具合のある部分はJavaで実装されていたので、jadで逆コンパイラを行い修正する箇所のあたりをつけてから、dirtyJOEでclassファイルを直接修正してみたんですが、結果的には直らず...。関連する全classファイルのpackageパスを変更すれば直りそうなことはわかったんですが、それはさすがに大変すぎるので諦めました。EOMにするならコア部分は公開しないにせよ、バグに関わる部分だけでもそのユーザーに公開することができれば、みんなハッピーになれるのではと思ったりします。

森博嗣さんの百年シリーズ 1作目の「女王の百年密室」を読み終わりました。Wシリーズ、Xシリーズ、百年シリーズと読んでいて、共通して面白いと思うのは、主人公とその主人公を支える人間もしくはウォーカロンの会話でしょうか。Wシリーズならハギリとウグイ、Xシリーズなら小川と真鍋、百年シリーズならサエバ・ミチルとロイディ。ハギリとデボラの会話も好きですが、巻を重ねるにつれウグイの良さがより際立っていると僕は感じます。Wシリーズはそろそろ終わりの雰囲気が出ていて若干寂しいですが、最後まで楽しみです。

2018-04-17

Kubernetes Up & Running


入門 Kubernetesを読みました。本の中で紹介されているyamlはkubernetes-up-and-running/examplesにあるみたいです。

# Docker
docker run -d --name kuard --publish 8081:8080 --memory 200m --memory-swap 1G --cpu-shares 1024 gcr.io/kuar-demo/kuard-amd64:1
docker stop kuard; docker rm kuard

# Kubernetes
kubectl version
kubectl get componentstatuses

controller-manager' クラスタ上の振る舞いを制御する
scheduler 各Podをクラスタ内のそれぞれのノードに配置する
etcd クラスタのすべてのAPIオブジェクトが保存されるストレージ

ノードの表示する
kubectl get nodes
masterノード APIサーバーやスケジューラのコンテナが動く
workerノード ユーザが作成したコンテナが動く

特定ノードの表示する
kubectl describe nodes xxx

プロキシの一覧を表示する
kubectl get daemonSets --namespace=kube-system kube-proxy

DNSの一覧を表示する
kubectl get deployments --namespace=kube-system kube-dns

DNSサーバをロードバランシングするためのKubernetes Serviceを表示する
kubectl get services --namespace=kube-system kube-dns

UIサーバの確認
kubectl get deployments --namespace=kube-system kube-dashboard

ロードバランスのためのServiceを表示
kubectl get services --namespace=kube-system kube-dashboard

UIにアクセスする
kubectl proxy

# よく使うkubectlコマンド
Namespaceはオブジェクトの集まりを入れるフォルダのようなイメージ

実行中のコンテナのログを確認する
kubectl logs podname
kubectl logs -f podname

ファイルのやりとりを行う
kubectl cp podname:/path/to/remote/file /path/to/local/file

実行中のコンテナにログインする
kubectl exec -it podname -- bash

Kubernetesではコンテナではなく、Podが最小のデプロイ単位であり、1つのPodないのコンテナはすべて同じマシン上に配置される
スケールさせる時もPod単位なので、例えばWordPressとMySQLを同じPodにすると片方だけスケールさせることができない
コンテナが違うマシンに配置されても正常に動作できるのであれば、Podを分けるのが正解な可能性が高い

Podの作成、確認、削除
kubectl run kuard --image=gcr.io/kuar-demo/kuard-amd64:1
kubectl get pods
kubectl delete deployments/kuard

Podをyamlで作成する
apiVersion: v1
kind: Pod
metadata:
  name: kuard
spec:
  containers:
    - image: gcr.io/kuar-demo/kuard-amd64:1
      name: kuard
      ports:
        - containerPort: 8081
          name: http
          protocol: TCP

kubectl apply -f kuard-pod.yaml

Podの詳しい情報の確認
kubectl describe pods kuard

詳細を確認するオプション
kubectl get pods -o wide
kubectl get pods -o json
kubectl get pods -o yaml

Podの削除
kubectl delete pods/kuard
kubectl delete -f kuard-pod.yaml

Podの削除コマンドを実行した時に、まずTerminatingという状態になりデフォルト30秒の猶予期間(grace period)後に削除される。Terminating状態になるとそのPodはリクエストを受け付けない。この猶予期間によって処理中の可能性のあるリクエストをPodを削除する前に終わられることができる。

ポートフォワード(コマンド実行中のみ有効)
kubectl port-forward kuard 8081:8080

ログの確認
kubectl logs kuard
kubectl logs -f kuard
kubectl logs --previous kuard

ヘルスチェック
プロセスヘルスチェック機能によってアプリケーションのメインプロセスが動いているか常に監視し、動いていない場合はKubernetesがプロセスを再起動する。プロセスが生きているものの、リクエストには応答できていない場合にはこれだけでは対応できない。

Liveness probe
apiVersion: v1
kind: Pod
metadata:
  name: kuard
spec:
  containers:
    - image: gcr.io/kuar-demo/kuard-amd64:1
      name: kuard
      livenessProbe:
        httpGet:
          path: /healthy
          port: 8080
        initialDelaySeconds: 5
        timeoutSeconds: 1
        periodSeconds: 10
        failureThreshold: 3
      ports:
        - containerPort: 8081
          name: http
          protocol: TCP

initialDelaySecondsはPod内の全コンテナが作成されてから5秒経過するまでリクエストを送らない
timeoutSecondsの1秒以内に応答する必要がある
HTTPチェックだけでなくtcpSocketもサポートしている
exec監視も可能で、コンテナ内でスクリプトの返り値が0なら成功、0以外なら失敗のようなこともできる
https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/

リソース要求 最低限必要なリソース
リソース制限 最大リソース量
apiVersion: v1
kind: Pod
metadata:
  name: kuard
spec:
  containers:
    - image: gcr.io/kuar-demo/kuard-amd64:1
      name: kuard
      resources:
        requests:
          cpu: "500m"
          memory: "128Mi"
      ports:
        - containerPort: 8081
          name: http
          protocol: TCP
1CPUの半分および128MBをリソース要求に設定する例
CPUのリソース要求はLinuxカーネルのcpu-shares機能を使って実装されている

apiVersion: v1
kind: Pod
metadata:
  name: kuard
spec:
  containers:
    - image: gcr.io/kuar-demo/kuard-amd64:1
      name: kuard
      resources:
        requests:
          cpu: "500m"
          memory: "128Mi"
        limits:
          cpu: "1000m"
          memory: "256Mi"         
      ports:
        - containerPort: 8081
          name: http
          protocol: TCP
リソース制限に1CPU、メモリ256MBを設定する例

データの永続化
spec.volumnsセクションもしくはvolumnMountsを使用する
apiVersion: v1
kind: Pod
metadata:
  name: kuard
spec:
  volumes:
    - name: "kuard-data"
      hostPath:
        path: "/var/lib/kuard"
  containers:
    - image: gcr.io/kuar-demo/kuard-amd64:1
      name: kuard
      volumeMounts:
        - mountPath: "/data"
          name: "kuard-data"
      ports:
        - containerPort: 8081
          name: http
          protocol: TCP
kuard-dataというボリュームを定義し、kuardコンテナの/dataにマウントする例
empDirはキャッシュに便利
NFSサーバーを利用することも可能
  volumes:
    - name: "kuard-data"
      nfs:
        server: my.nfs.server.local
        path: "/exports"


# LabelとAnnotation
LabelとAnnotationの使い分けは、好みにもよる。セレクタとして使いたくなったらLabelに昇格させるのが良い

kubectl run alpaca-prod --image=gcr.io/kuar-demo/kuard-amd64:1 --replicas=2 --labels="ver=1,app=alpaca,env=prod"
kubectl run alpaca-test --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=1 --labels="ver=2,app=alpaca,env=test"
kubectl run bandicoot-prod --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=2 --labels="ver=2,app=bandicoot,env=prod"
kubectl run bandicoot-staging --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=1 --labels="ver=2,app=bandicoot,env=staging"

Labelの確認
kubectl get deployments --show-labels
kubectl get deployments -L env
kubectl get pods --selector="ver=2"
kubectl get pods --selector="ver!=2"
kubectl get pods --selector="app=bandicoot,ver=2"
kubectl get pods --selector="app in (alpaca,bandicoot)"
kubectl get pods --selector="app notin (alpaca,bandicoot)"
kubectl get deployments --selector="canary"
kubectl get deployments --selector="!canary"

Labelの追加(ReplicaSetやPodは変更されないので注意)
kubectl label deployments alpaca-test "canary=true"

Deploymentの削除
kubectl delete deployments --selector="canary"
kubectl delete deployments --all

# サービスディスカバリ
kubectl run alpaca-prod --image=gcr.io/kuar-demo/kuard-amd64:1 --replicas=3 --port=8080 --labels="ver=1,app=alpaca,env=prod"
kubectl expose deployment alpaca-prod
kubectl run bandicoot-prod --image=gcr.io/kuar-demo/kuard-amd64:2 --replicas=2 --port=8080 --labels="ver=2,app=bandicoot,env=prod"


kubectl expose deployment bandicoot-prod
kubectl get services -o wide

ALPACA_POD=$(kubectl get pods -l app=alpaca -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward $ALPACA_POD 48858:8080

Readiness probe
kubectl edit deployment/alpaca-prod

    spec:
      containers:
      - image: gcr.io/kuar-demo/kuard-amd64:1
        imagePullPolicy: IfNotPresent
        name: alpaca-prod
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          periodSeconds: 2
          initialDelaySeconds: 0
          failureThreshold: 3
          successThreshold: 1
        ports:
        - containerPort: 8080
          protocol: TCP

ServiceのEndpointsに対するトラフィックの確認
kubectl get endpoints alpaca-prod --watch

Endpointsオブジェクトの確認
kubectl describe endpoints alpaca-prod

新しいServiceが作られるとkube-proxyは、iptablesのルールをホストのカーネルに設定する

# ReplicaSet
冗長性、スケール、シャーディングのために用意されているのがReplicaSet
ReplicaSetはPodのLabelを使ってクラスタの状態を監視する
不具合のあるPodのLabelだけを変更し、 ReplicaSetからPodを切り離すと、Podは起動したままになるのでデバッグがしやすくなる

ReplicaSetの最小限の定義例(kuard-rs.yaml)
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: kuard-rs
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: kuard
        version: "2"
    spec:
      containers:
        - name: kuard
          image: "gcr.io/kuar-demo/kuard-amd64:2"
kubectl apply -f kuard-rs.yaml

PodからReplicaSetの特定
kubectl get pods kuard-rs-4m2nj -o yaml | grep owner -A 

ReplicaSetに対応するPodの集合の特定
kubectl get pods -l app=kuard,version=2

kubectl scaleを使った命令的スケール(もし使用する場合はyamlも書き換えること)
kubectl scale replicasets kuard-rs --replicas=4

宣言的スケール
yamlのreplicasの値を書き換え後にkubectl apply -f kuard-rs.yaml

水平Podオートスケーリング(HPA Horizontal Pod Autoscaling)
heapsterというPodによりメトリクスを追跡し、HPAがスケーリングの判断を行う時に使用するメトリクスを取得するAPIを提供する
クラスタ内にheapsterが存在しない場合はHPAは正常に動作しない
Podのレプリカを追加することを水平スケール、Podに必要なリソースを増やすことを垂直スケールと区別している

CPU使用率を元にしたオートスケール
kubectl autoscale rs kuard-rs --min=2 --max=5 --cpu-percent=80
CPU使用率80%を閾値にして、レプリカ数2〜5の間でスケールするオートスケーラ

確認
kubectl get hpa

ReplicaSetを削除すると管理しているPodも一緒に削除される
kubectl delete rs kuard-rs

ReplicaSetのみ削除したい場合
kubectl delete rs kuard-rs --cascade=false

# Daemon Set
ReplicaSetと違い、ノードセレクタを使わない限り、全ノードにPodを作る

fluentd.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  labels:
    app: fluentd
spec:
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      containers:
      - name: fluentd
        image: fluent/fluentd:v0.14.10
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
kubectl apply -f fluentd.yaml

NodeにLabelを追加し、確認
kubectl label nodes minikube ssd=true
kubectl get nodes --show-lables
kubectl get nodes --selector ssd

nginx-fast-storage.yaml(ssd=trueというLabelがノードでのみ動くDaemonSet)
apiVersion: extensions/v1beta1
kind: "DaemonSet"
metadata:
  labels:
    app: nginx
    ssd: "true"
  name: nginx-fast-storage
spec:
  template:
    metadata:
      labels:
        app: nginx
        ssd: "true"
    spec:
      nodeSelector:
        ssd: "true"
      containers:
        - name: nginx
          image: nginx:1.10.0
他のノードにssd=trueというLabelを追加すれば、そのノードにもデプロイされる。逆にLabelを削除するとそのPodはDaemonSetコントローラから削除される

forループを用いたPodの削除
PODS=$(kubectl get pods -o jsonpath -template='{.ites[*].metadata.name}')
for x in $PODS; do
  kubectl delete pods ${x}
  sleep 60
done

ローリングアップデートを使うにはspec.updateStrategy.typeフィールドにRollingUpdateを設定する。ローリングアップデート中のステータスの確認はkubectl rolloutコマンドを使用する
kubectl rollout status daemonSets nginx-fast-storage

DaemonSetの削除
kubectl delete -f fluentd.yaml

# Job
1回限りの処理を行うのに便利なのがJobオブジェクト
kubectl run -i oneshot --image=gcr.io/kuar-demo/kuard-amd64:1 --restart=OnFailure -- --keygen-enable --keygen-exit-on-complete --keygen-num-to-gen 10
-iオプションはコマンドが対話モードであることを表す
--restart=OnFailureはkubectlにJobオブジェクトを作成するよう指示する

完了後のPodも含めて表示する
kubectl get pods -a

yamlでJobを作成する例
kubectl apply -f job-oneshot.yaml

失敗するJobの例
kubectl apply -f job-oneshot-failure1.yaml
kubectl get pods -a -l job-name=oneshot
KubernetesはこのPodがCrashLoopBackOffだと認識している。クラッシュの繰り返しによってノードのリソースが使われ過ぎないように、Podの再起動まで自動で少し待つ。
restartPolicy: Neverに設定するとkubeletはJob失敗時にPodを再起動せず、代わりにPodを失敗と宣言し、代わりのPodを作成する。restartPolicy: OnFailureにしておくことを推奨。

一定数成功するまで並列実行
kubectl apply -f job-parallel.yaml
kubectl get pods -w
--watchフラグによって時間とともにPodの一覧が更新されることを確認できる

並列実行キューを実装する例
1. 1回限りのキューデーモンを管理するために、ReplicaSetを作成する
kubectl apply -f rs-queue.yaml

2. キューに接続するためにポートフォワードを設定する
QUEUE_POD=$(kubectl get pods -l app=work-queue,component=queue -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward $QUEUE_POD 8080:8080
MemQ Serverを開いてキューが動作していれば、キューのプロデューサとコンシューマがDNS経由でキューを使える状態。

3. キューのServiceを作成する
kubectl apply -f service-queue.yaml

4. サブタスクをキューに入れ、状態を確認
for in in work-item-{0..99}; do
  curl -X POST localhost:8080/memq/server/queues/keygen/enqueue -d "$i"
done
curl 127.0.0.1:8080/memq/server/stats

5. コンシューマJobを作成する
kubectl apply -f job-consumers.yaml
kubectl get podsで5つのPodが並列に動作していることがわかる

# ConfigMapとSecret
ConfigMapにはワークロードに応じた設定情報を保存する
SecretにはパスワードやTLS証明書などの機密情報を保存する

ConfigMapの作成
my-config.txt
parameter1 = value1
parameter2 = value2

kubectl create configmap my-config --from-file=my-config.txt --from-literal=extra-param=extra-value --from-literal=another-param=anather-value

kubectl get configmaps my-config -o yaml

ConfigMapの使用
kuard-config.yaml
apiVersion: v1
kind: Pod
metadata:
  name: kuard-config
spec:
  containers:
    - name: test-container
      image: gcr.io/kuar-demo/kuard-amd64:1
      imagePullPolicy: Always
      command:
        - "/kuard"
        - "$(EXTRA_PARAM)"
      env:
        - name: ANOTHER_PARAM
          valueFrom:
            configMapKeyRef:
              name: my-config
              key: another-param
        - name: EXTRA_PARAM
          valueFrom:
            configMapKeyRef:
              name: my-config
              key: extra-param
      volumeMounts:
        - name: config-volume
          mountPath: /config
  volumes:
    - name: config-volume
      configMap:
        name: my-config
  restartPolicy: Never
kubectl apply -f kuard-config.yaml
kubectl port-forward kuard-config 8080

Secretの作成
サンプルのTLSキーと証明書をダウンロードする(この例以外では使用しないこと)
curl -o kuard.crt https://storage.googleapis.com/kuar-demo/kuard.crt
curl -o kuard.key https://storage.googleapis.com/kuar-demo/kuard.key
kubectl create secret generic kuard-tls --from-file=kuard.crt --from-file=kuard.key
kubectl describe secrets kuard-tls

Secretの使用
今回の例ではSecret volumeを/tlsにマウントすると/tls/kuard.crtおよびtls/kuard.keyにアクセス可能になる
kuard-secret.yaml
apiVersion: v1
kind: Pod
metadata:
  name: kuard-tls
spec:
  containers:
    - name: kuard-tls
      image: gcr.io/kuar-demo/kuard-amd64:1
      imagePullPolicy: Always
      volumeMounts:
      - name: tls-certs
        mountPath: "/tls"
        readOnly: true
  volumes:
    - name: tls-certs
      secret:
        secretName: kuard-tls
kubectl apply -f kuard-secret.yaml
kubectl port-forward kuard-tls 8443:8443

プライベートDockerレジストリ
kubectl create secret docker-registry my-image-pull-secret \
 --docker-username=xxx \
 --docker-password=xxx \
 --docker-email=xxx
yamlのサンプル kuard-secret-ips.yaml

ConfigMapとSecretの管理
kubectl get secrets
kubectl get configmaps
kubectl describe configmap my-config

# Deployment
Deploymentを使うとダウンタイムやエラーを発生させずに新しいバージョンのソフトウェアをシンプルにロールアウトできる。
kubectl scale deployments nginx --replicas=2
Deploymentをスケールすると、その管理下にあるReplicaSetもスケールされる

kubectl get deployments nginx --export -o yaml > nginx-deployment.yaml
kubectl replace -f nginx-deployment.yaml --save-config
kubectl describe deployments nginx
ロールアウト中はOldReplicaSetsフィールドに値が入る

コマンドではなくyamlでスケールさせるには以下の部分を更新する
spec:
  replicas: 3

ロールアウトを中断・再開する
kubectl rollout pause deployments nginx
kubectl rollout resume deployments nginx

ロールアウトの履歴を確認する
kubectl rollout history deployments nginx
kubectl rollout history deployments nginx --revision=2

ロールアウトのロールバック
kubectl rollout undo deployments nginx

履歴の数を制限する方法
spec:
  revisionHistoryLimit: 14

Deployment戦略
Recreate: Deploymentに基づいたPodを全て停止することでReplicaSetに新しいイメージを作成させる。シンプルで高速だが障害を起こす可能性があり、ダウンタイムも避けられない。
Rolling Update: ある一定の期間、新旧バージョンの両方がリクエストを受けトラフィックを処理する。クライアント側が新旧どちらとも連携できることが重要になってくる。

maxUnavailableパラメータはローリングアップデート中に使用不可能になってもいいPodの最大数を指定する。絶対値もしくはパーセンテージで指定する。この値はローリングアップデートを進める速度にも関わってくる。

maxSurgeパラメータはロールアウト時にどのぐらいの追加リソースを作れるかを制御する。maxUnavailableを0、maxSurgeを20%に設定すると、レプリカ数を1.2倍に増やしてからレプリカの割合を旧0.8 新0.2へ更新する。maxSurgeを100%に設定するとブルーグリーンデプロイメントを行える。

minReadySecondsを使用するとPodが起動してから指定した時間後に次のアップデートに移る
spec:
  minReadySeconds: 60

タイムアウトを設定するにはprogressDeadlineSecondsを使用する
spec:
  progressDeadlineSeconds: 600

# ストレージソリューションとKubernetesの統合
セレクタのないService
dns-service.yaml
kind: Service
apiVersion: v1
metadata:
  name: external-database
spec:
  type: ExternalName
  externalName: "database.company.com
ExternalNameというタイプのServiceを作成すると、外部のDNS名を示すAレコードの代わりにCNAMEレコードを作成する。クラスタ内のアプリケーションがexternal-database.default.svc.cluster.localというホスト名をDNS名前解決すると、DNSプロトコルがdatabase.company.comに名前をエイリアスする。

IPアドレスのみ提供されている場合はサービスとエンドポイントを作成する必要がある
external-ip-service.yaml
kind: Service
apiVersion: v1
metadata:
  name: external-ip-database

external-ip-endpoints.yaml
kind: Endpoints
apiVersion: v1
metadata:
  name: external-ip-database
subsets:
  - addresses:
    - ip: 192.168.0.1
    ports:
    - port: 3306

Persistent Volumnの例
nfs-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: database
  labels:
    volume: my-volume
spec:
  capacity:
    storage: 1Gi
  nfs:
    server: 192.168.0.1
    path: "/exports"

PersistentVolumeClaimオブジェクトを使ってPersistentVolumnをPodから取得する
nfs-volume-claim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: database
spec:
  resources:
    requests:
      storage: 1Gi
  selector:
    matchLabels:
      volume: my-volume

StatefulSetはReplicaSetに似た複製されたPodのグループ。削除時やスケールダウン時に各レプリカはインデックスの数字が大きい順から削除される。
kubectl apply -f mongo-simple.yaml

StatefulSet作成後はDNSエントリを管理するヘッドレスなServiceも作る必要がある。
kubectl apply -f mongo-service.yaml
kubectl exec mongo-0 bash ping mongo-1.mongo

# 実用的なアプリケーションのデプロイ
Parse, Ghost, Redisのデプロイ方法のサンプル