OpenTracingチュートリアルをやってみた(その1)
OpenTracingチュートリアルをやったメモ
OpenTracingとは
OpenTracingは、API仕様とそれを実装したフレームワークとライブラリ、およびプロジェクトのドキュメントで構成されています。 OpenTracingを使用すると、開発者は特定の製品やベンダーにロックされていないAPIを使用してアプリケーションコードにトレーシング機能を追加できます。
OpenTracingがサポートしている言語
- Go, JavaScript, Java, Python, Ruby, PHP, Objective-C, C++, C#
OpenTracingチュートリアル
このチュートリアルではTracerとしてJaegerを使用しています。
Jaegerとは
DapperとOpenZipkinにインスパイヤーされUber TechnologiesによってリリースされたオープンソースのOpenTracing互換の分散トレースシステムです。
アーキテクチャはこのような感じです。
Architecture — Jaeger documentation
Client以外の起動に関してはAll-in-oneのDockerイメージが公開されているのでそれを使用すれば起動できます。 Getting started — Jaeger documentation
今回は必要最低限のポートのみを指定して起動します。
docker run --rm -p 6831:6831/udp -p 6832:6832/udp -p 16686:16686 jaegertracing/all-in-one:1.8 --log-level=debug
簡単なトレースを作成してみる
まずはLesson 1 - Hello Worldから。
- TracerはJaegerを初期化しJaegerTracerを取得します。
buildSpan()
を使用してオペレーション名を設定しSpanBuilder
を生成します。(今回は"say-hello"
と設定)span
はstart()
を使用して生成しfinish()
を使用し終了する必要があります。span
の開始と終了のタイムスタンプは、Tracerの実装(Jaeger)によって自動的に取得されます。
import io.jaegertracing.Configuration; import io.jaegertracing.Configuration.ReporterConfiguration; import io.jaegertracing.Configuration.SamplerConfiguration; import io.jaegertracing.internal.JaegerTracer; import io.opentracing.Span; import io.opentracing.Tracer; public class Hello { private final Tracer tracer; public Hello(Tracer tracer) { this.tracer = tracer; } public static void main(String[] args) { if (args.length != 1) throw new IllegalArgumentException(); Tracer tracer = initTracer("hello-world"); new Hello(tracer).sayHello(args[0]); } private void sayHello(String helloTo) { Span span = tracer.buildSpan("say-hello").start(); String helloStr = String.format("Hello, %s", helloTo); System.out.println(helloStr); span.finish(); } private static JaegerTracer initTracer(String service) { SamplerConfiguration samplerConfiguration = SamplerConfiguration.fromEnv().withType("const").withParam(1); ReporterConfiguration reporterConfiguration = ReporterConfiguration.fromEnv().withLogSpans(true); Configuration configuration = new Configuration(service).withSampler(samplerConfiguration).withReporter(reporterConfiguration); return configuration.getTracer(); } }
上記で分かるように、Tracer、Spanに関してはOpenTracingによって仕様が切られている為OpenTracingのinterfaceを使用し実装に関してはJaegerを使用していることが分かります。
トレースにTagとLogを付ける
- Tag
- Log
- Spanに関連づけられるログステートメント
OpenTracing仕様には、推奨されるTagとLogフィールドの セマンティック規約と呼ばれるガイドラインがあります。
Hello, Hoge!
の場合に引数の"Hoge"はSpan全体に適用される為、Tagとして記録します。- このプログラムでは引数をフォーマットしてからprintlnしています。この両方の操作には一定の時間がかかるので、その完了をLogとして記録します。
public class Hello { private final Tracer tracer; public Hello(Tracer tracer) { this.tracer = tracer; } public static void main(String[] args) { if (args.length != 1) throw new IllegalArgumentException(); Tracer tracer = initTracer("hello-world"); new Hello(tracer).sayHello(args[0]); } private void sayHello(String helloTo) { Span span = tracer.buildSpan("say-hello").start(); // Tagの記録 span.setTag("hello-to", helloTo); String helloStr = String.format("Hello, %s", helloTo); // Logの記録 span.log(ImmutableMap.of("event", "string-format", "value", helloStr)); System.out.println(helloStr); // Logの記録 span.log(ImmutableMap.of("event", "println")); span.finish(); } private static JaegerTracer initTracer(String service) { SamplerConfiguration samplerConfiguration = SamplerConfiguration.fromEnv().withType("const").withParam(1); ReporterConfiguration reporterConfiguration = ReporterConfiguration.fromEnv().withLogSpans(true); Configuration configuration = new Configuration(service).withSampler(samplerConfiguration).withReporter(reporterConfiguration); return configuration.getTracer(); } }
個別機能をトレースする
次はLesson 2 - Context and Tracing Functionsを。
- 文字列のフォーマットと出力を個別のメソッドに分けます。
buildSpan()
の追加オプションasChildOf()
を使用し、個別に分けたメソッドのSpanをmain()
メソッドのSpanの子Spanにします。
public class Hello { private final Tracer tracer; public Hello(Tracer tracer) { this.tracer = tracer; } public static void main(String[] args) { if (args.length != 1) throw new IllegalArgumentException(); Tracer tracer = initTracer("hello-world"); new Hello(tracer).sayHello(args[0]); } private void sayHello(String helloTo) { Span span = tracer.buildSpan("say-hello").start(); span.setTag("hello-to", helloTo); String helloStr = formatString(span, helloTo); printHello(span, helloStr); span.finish(); } private String formatString(Span rootSpan, String helloTo) { Span span = tracer.buildSpan("formatString").asChildOf(rootSpan).start(); try { String helloStr = String.format("Hello, %s", helloTo); span.log(ImmutableMap.of("event", "string-format", "value", helloStr)); return helloStr; } finally { span.finish(); } } private void printHello(Span rootSpan, String helloStr) { Span span = tracer.buildSpan("printHello").asChildOf(rootSpan).start(); try { System.out.println(helloStr); span.log(ImmutableMap.of("event", "println")); } finally { span.finish(); } } private static JaegerTracer initTracer(String service) { Configuration.SamplerConfiguration samplerConfiguration = Configuration.SamplerConfiguration.fromEnv().withType("const").withParam(1); Configuration.ReporterConfiguration reporterConfiguration = Configuration.ReporterConfiguration.fromEnv().withLogSpans(true); Configuration configuration = new Configuration(service).withSampler(samplerConfiguration).withReporter(reporterConfiguration); return configuration.getTracer(); } }
"active span"
を使用
上記のコードで幾つか面倒なところが有りました。
- 各関数の最初の引数としてSpanオブジェクトを渡さなければなりませんでした
- スパンを完成させるためにやや冗長なtry / finallyコードを書く必要もありました
OpenTracing API for Javaではスレッドローカルと"active span"
という概念を使用することで、Spanをコードに渡すことなくTracer経由でアクセスすることができます。
start()
ではなくbuildSpan()
のstartActive()
メソッドを使用して、スレッドローカルに格納してSpanをアクティブにします。startActive()
はSpan
ではなくScope
オブジェクトを返します。Scope
は現在アクティブなSpanのコンテナです。scope.span()
を介してアクティブなSpanにアクセスします。Scopeを閉じると以前のScopeが現在のScopeになり、現在アクティブなスレッドのSpanを再びアクティブにします。Scope
は自動クローズ可能で、try-with-resource構文を使用できます。startActive(true)
はスコープを閉じるとスパンを終了します。startActive()
はアクティブSpanへのChildOf
参照を自動的に作成するため、buildSpan
のasChildOf()
を使用する必要はありません。
public class Hello { private final Tracer tracer; public Hello(Tracer tracer) { this.tracer = tracer; } public static void main(String[] args) { if (args.length != 1) throw new IllegalArgumentException(); Tracer tracer = initTracer("hello-world"); new Hello(tracer).sayHello(args[0]); } private void sayHello(String helloTo) { try (Scope scope = tracer.buildSpan("say-hello").startActive(true)) { scope.span().setTag("hello-to", helloTo); String helloStr = formatString(helloTo); printHello(helloStr); } } private String formatString(String helloTo) { try (Scope scope = tracer.buildSpan("formatString").startActive(true)) { String helloStr = String.format("Hello, %s", helloTo); scope.span().log(ImmutableMap.of("event", "string-format", "value", helloStr)); return helloStr; } } private void printHello(String helloStr) { try (Scope scope = tracer.buildSpan("printHello").startActive(true)) { System.out.println(helloStr); scope.span().log(ImmutableMap.of("event", "println")); } } private static JaegerTracer initTracer(String service) { Configuration.SamplerConfiguration samplerConfiguration = Configuration.SamplerConfiguration.fromEnv().withType("const").withParam(1); Configuration.ReporterConfiguration reporterConfiguration = Configuration.ReporterConfiguration.fromEnv().withLogSpans(true); Configuration configuration = new Configuration(service).withSampler(samplerConfiguration).withReporter(reporterConfiguration); return configuration.getTracer(); } }
次回は残りのLesson 03 、Lesson 04をしたいと思います。
Sigfox Callbackで温度、湿度、気圧をSlackへ通知してみた
10月12日にSigfoxハンズオン広島(お土産付き)へ参加しArduino用Sigfoxシールド(Sigfox Shield for Arduino : UnaShield)を頂いたので、温度、湿度、気圧をSlackへ通知してみました。
ArduinoからSlackへの通知する構成図
本当ならAWS IoT を使用するのだろうけど今回は手っ取り早く下記の方法で。
UnaShieldで取得した温湿度・気圧センサーデータをSigfoxで送信
AWS Lambdaを使用してSlackへPOST送信し通知
- Sigfoxとは
- Arduino用Sigfoxシールドの準備
- Arduinoのプログラミング
- Sigfox Cloud側のCALLBACK設定
- Slackの設定
- AWS Lambda、Amazon API Gatewayの設定
- 最後に
- 参考URL
Sigfoxとは
フランスの通信事業会社Sigfox社が2009年から提供しているIoT向けに特化したLPWA通信規格(Low Power、Wide Area)です。国内では京セラコミュニケーションシステム株式会社(以下、KCCSと表記)が事業者としてサービスを提供しています。
Arduino用Sigfoxシールドの準備
ハンズオンでお土産で頂いたUnaShieldには温湿度・気圧センサ(BME280)が搭載されているのでこのセンサーを利用して 温度、湿度、気圧を取得します。
UnaShieldを使用する前にSigfox Backend Cloudへの登録する必要があります。登録方法は取扱説明書に記載されているのでそれに従って登録します。
Sigfox Shield for Arduino(UnaShield V2 / V2S* )取扱説明書
Arduinoのプログラミング
今回はUnaShieldに標準で搭載されている温湿度・気圧センサ(BME280)から値を取得しSigfox Cloudへ送信する為のプログラムを作成し実行します。 温湿度・気圧センサ(BME280)を利用すりプログラムに関しては下記のサンプルを元に作成します。
30分毎に温度、湿度、気圧をSigfox Cloudへ送信します。(Arduinoのソース)
#include <Wire.h> #include <SPI.h> #include "Adafruit_Sensor.h" #include "Adafruit_BME280.h" #define BME_SCK 13 #define BME_MISO 12 #define BME_MOSI 11 #define BME_CS 10 #define SEALEVELPRESSURE_HPA (1013.25) Adafruit_BME280 bme; // I2C #include "SIGFOX.h" static const String device = "NOTUSED"; // Set this to your device name if you're using SIGFOX Emulator. static const bool useEmulator = false; // Set to true if using SIGFOX Emulator. static const bool echo = true; // Set to true if the SIGFOX library should display the executed commands. static const Country country = COUNTRY_JP; // Set this to your country to configure the SIGFOX transmission frequencies. static UnaShieldV2S transceiver(country, useEmulator, device, echo); // Assumes you are using UnaBiz UnaShield V2S Dev Kit void setup() { // Initialize console so we can see debug messages (9600 bits per second). Serial.begin(9600); Serial.println(F("Running setup...")); // Initialize the onboard LED at D13 for output. pinMode(LED_BUILTIN, OUTPUT); if (!bme.begin(0x76)) stop("Bosch BME280 sensor missing"); // Will never return. // Check whether the SIGFOX module is functioning. if (!transceiver.begin()) stop("Unable to init SIGFOX module, may be missing"); // Will never return. } void loop() { // Will be called repeatedly. // Read the ambient temperature, humidity, air pressure. float filteredTemp = bme.readTemperature(); float filteredPressure = bme.readPressure() / 100.0F; float filteredAltitude = bme.readAltitude(SEALEVELPRESSURE_HPA); float humidity = bme.readHumidity(); Serial.println("---"); Serial.print("filteredTemp = "); Serial.print(filteredTemp); Serial.println(" degrees C"); Serial.print("filteredPressure = "); Serial.print(filteredPressure); Serial.println(" hPa"); Serial.print("filteredAltitude = "); Serial.print(filteredAltitude); Serial.println(" metres above sea level"); Serial.print("humidity = "); Serial.print(humidity); Serial.println(" %"); Serial.println("---"); // Send temperature, pressure, altitude, module temperature as a SIGFOX message. // Count messages sent and failed. static int counter = 0, successCount = 0, failCount = 0; Serial.print(F("\nRunning loop #")); Serial.println(counter); String msg = transceiver.toHex(filteredTemp) // 4 bytes + transceiver.toHex(filteredPressure) // 4 bytes + transceiver.toHex(humidity); // 4 bytes // Send the message. digitalWrite(LED_BUILTIN, HIGH); // Turn the LED on (HIGH is the voltage level). if (transceiver.sendMessage(msg)) { Serial.println(F("successCount!")); successCount++; } else { Serial.println(F("failCount!")); failCount++; } digitalWrite(LED_BUILTIN, LOW); // Turn the LED off (LOW is the voltage level). counter++; // Wait a while before looping. // ex:10000 milliseconds = 10 seconds. // 60000 milliseconds = 1 minute. // 900000 milliseconds = 15 minute. // 1800000 milliseconds = 30 minute. Serial.println(F("Waiting...")); delay(1800000); }
Sigfox Cloud側のCALLBACK設定
Arduino用Sigfoxシールドからの送信されたデータを受信しそれをトリガーに起動されるCALLBACKを設定します。
まずは、送信してくるSigfoxシールドのDEVICEの「Device Type」を選択します。
新規Collbackの作成します。
今回は単純にPOST送信するだけなので「Custom collback」を選択します。
「Custom collback」の設定
- Custom payload config:
temp::float:32:little-endian pressure::float:32:little-endian humidity::float:32:little-endian
Custom payload configの記述方法に関しては下記のサイトを参考にしました。 qiita.com
{ "device" : "{device}", "time" : {time}, "seqNumber" : {seqNumber}, "duplicate" : {duplicate}, "data" : "{data}", "temp" : {customData#temp}, "humidity" : {customData#humidity}, "pressure" : {customData#pressure} }
Slackの設定
Slackへメッセージを送信するためにIncoming Webhooksを使用します。 Slackのアプリケーション追加ページより incoming-webhooks を検索・選択しインストールします。
インストールを行ったら通知を行うチャンネルを選択します。Webhook URLはAWS Lambdaから通知を行う為に使用するURLになります。
AWS Lambda、Amazon API Gatewayの設定
Amazon API Gatewayを使用しSigfox CollbackからAWS Lambdaを呼び出します。
今回はAWS LambdaのランタイムはGo 1.xを使用し作成しました。
package main import ( "bytes" "encoding/json" "fmt" "github.com/aws/aws-lambda-go/lambda" "net/http" "strconv" "time" ) const mySlackURL = "Slackで設定したIncoming WebhooksのWebhook URL" const channel = "通知するSlackのチャンネル" type SensorInfo struct { Device string `json:"device"` Time int64 `json:"time"` SeqNumber int64 `json:"seqNumber"` Duplicate bool `json:"duplicate"` Data string `json:"data"` Temp float64 `json:"temp"` Humid float64 `json:"humidity"` Press float64 `json:"pressure"` } type SendSlack struct { Channel string `json:"channel"` Username string `json:"username"` Text string `json:"text"` } func slack(si SensorInfo) (SensorInfo, error) { jst := time.Unix(si.Time, 0).In(time.FixedZone("Asia/Tokyo", 9*60*60)).Format(time.RFC1123) message := jst + "\n温度:" + strconv.FormatFloat(si.Temp, 'f', 2, 64) + `℃, 湿度:` + strconv.FormatFloat(si.Humid, 'f', 2, 64) + `%` + "\n気圧:" + strconv.FormatFloat(si.Press, 'f', 2, 64) + `hPa` sendms := SendSlack{ Channel: channel, Username: "百葉箱", Text: message} jsonBytes, err := json.Marshal(sendms) if err != nil { fmt.Println("JSON Marshal error:", err) return si, nil } fmt.Println(string(jsonBytes)) req, err := http.NewRequest( "POST", mySlackURL, bytes.NewBuffer(jsonBytes), ) if err != nil { fmt.Println(err) return si, nil } req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println(err) return si, nil } fmt.Println(resp) defer resp.Body.Close() return si, nil } func main() { lambda.Start(slack) }
作成したAWS Lambdaを呼び出せるようにAmazon API Gatewayの作成・設定を行います。 API GatewayにPOSTメソッドを追加し先ほど作成したLambda 関数を指定します。
最後に
Arduinoを電源に繋ぐと30分毎にSlackに気温、湿度、気圧の通知が来るようになります。 (画像は15分毎ですが。。。)
参考URL
Doker for WindowsでFn Projectを動かしてみた
Doker for WindowsでFn Projectを動かした時のメモ
Fn Project
特徴
- 「JavaOne 2017」の基調講演で、Oracleが発表したJava対応のオープンソースサーバレスプラットフォーム
- 特徴
- 簡単
- 複雑なfunctionを実装するためのCLIを提供
- 構成
- Function Server(Docker)上にFunction(Docker)が乗っかる(Docker on Docker)
- 起動するとFunctionのコンテナが立ち上がり一定時間後に消える
Function Serverの同期処理シーケンス
(非同期処理の場合はMQが使用される)
FnProject CLI toolのDownloadと設定
windowsの場合は下記からfn.exeを適当な場所へDownloadし、PATHを通す。
PATHが通っているかの確認
> fn --version fn version 0.5.0
Fnction Serverの起動
ここではAPIのMetadataを格納するDBのファイルを"C:\mnt\docker\fnserver\data"へ置くように設定する。 (何も指定しないとSQLighのようだ)
docker run --rm --name fnserver -it -d -v /var/run/docker.sock:/var/run/docker.sock -v /c/mnt/docker/fnserver/data:/app/data -p 8080:8080 fnproject/fnserver
Fnction Serverの起動確認
> curl http://localhost:8080 {"goto":"https://github.com/fnproject/fn","hello":"world!"}
Functionの作成
JavaでのFunctionの作成方法
> fn init --runtime java --trigger http javafn Creating function at: /javafn Function boilerplate generated. func.yaml created. > tree /f フォルダー パスの一覧: ボリューム Windows ボリューム シリアル番号は 00000215 2E6B:1EF1 です C:. └─javafn │ func.yaml │ pom.xml │ └─src ├─main │ └─java │ └─com │ └─example │ └─fn │ HelloFunction.java │ └─test └─java └─com └─example └─fn HelloFunctionTest.java
FunctionはデフォルトではCold StandbyだがJavaでFunctionを作成するとHot Standbyで作成される。 Functionの定義ファイル"func.yaml"のformatがhttpだとHot Standbyのようだ。ちなみにアイドルタイムはデフォルトは30秒。 docs/hot-functions.md at master · fnproject/docs · GitHub
schema_version: 20180708 name: javafn version: 0.0.1 runtime: java build_image: fnproject/fn-java-fdk-build:jdk9-1.0.70 run_image: fnproject/fn-java-fdk:jdk9-1.0.70 cmd: com.example.fn.HelloFunction::handleRequest format: http triggers: - name: javafn-trigger type: http source: /javafn-trigger
作成したFunctionのデプロイ
作成されたFunctionのディレクトリでおこなう
> fn --verbose deploy --app javaapp --local
Functionの呼び出し
> fn invoke javaapp javafn Hello, world! > curl -d Java http://localhost:8080/t/javaapp/javafn-trigger Hello, Java!
ダッシュボード
fn project用のUIが用意されている。 github.com
UIの起動
docker run --rm --name ui -it -d --link fnserver -p 4000:4000 -e "FN_API_URL=http://fnserver:8080" fnproject/ui
おまけ
Fnction ServerとUIを同時に動かすdocker-composeはこんな感じです。
docker-compose.yaml
version: '2' services: fnserver: image: fnproject/fnserver container_name: fnserver ports: - '8080:8080' volumes: - "/var/run/docker.sock:/var/run/docker.sock" - "/c/mnt/docker/fnserver/data:/app/data" fnui: image: fnproject/ui container_name: fnui ports: - '4000:4000' environment: FN_API_URL: "http://fnserver:8080" depends_on: - fnserver
docker-composeの起動
> set COMPOSE_CONVERT_WINDOWS_PATHS=1 > docker-compose up -d Creating network "fnproject_default" with the default driver Creating fnserver ... done Creating ui ... done
参考
Cloud Native Hiroshima #01で行ったLTスライド
雑に QnA Maker を触って Slack と連動させてみた
QnA Maker を使用してFAQ BotをSlack に連動させたメモ
(2018.04時点ではプレビュー版です)
QnA Maker とは
会話的な方法でユーザーの質問に回答するためのREST APIとWebベースのサービスを簡単に作成する事が出来るサービス
こんな感じの仕組み
QnA servicesの作成
まずはQnA Makerにアクセスして
"Create new service"からQnA servicesを作成する
FAQの元となる情報の指定方法には二通りある
一つはFAQサイトのURLを指定する方法で、もう一つはFAQが書かれているファイルをアップロードする方法
今回はMazda Zoom-Zoom スタジアム広島|よくあるご質問(Q&A)のURLを指定する方法で作成してみる
後は"Create"ボタンを押すだけ
正常に作成されるとFAQの元となる情報が表形式で表示される
微妙に読み取れていない場合は表を修正し"Save and retrain"を押す
また、指定したFAQサイトによっては全く読み取れない場合もある
QnA servicesのテスト
作成したservicesはTestタブからテストも可能
ここで質問に対する回答が間違っている場合は、正解となる回答を選択し学習することも可能のようだ
QnA servicesの公開
"Publish"よりserviceを公開する
公開されるとSample HTTP Requestが表示され、その中にQnAKnowledgebaseIdとQnASubscriptionKeyが埋め込まれている
これはAzule Functions Botを作成する際に必要なのでメモっておく
メモるのを忘れた場合はMy QnA servicesからView Codeでも確認できる
ここまででQnA servicesの作成は完了
Azule Functions Bot の作成
次にQnA serviceを使用したBot アプリをAzule Functions Botで作成する
作成する際にボット テンプレートは"Question and Answer"を指定する
アプリケーション設定
作成されたAzule Functions Botへアプリケーション設定を行う
アプリケーション設定タブのアプリ設定へQnAKnowledgebaseIdとQnASubscriptionKeyを設定して保存する
値は先ほどQnA services作成時にSample HTTP Requestに埋められていた値を使用する
"Web チャットでテスト"タブでテストを行い回答が返ってくれば完了
Slack との連動
今回作成したBotをSlackと連動させる
Slack Appの作成
アプリ名と作成したアプリが所属するワークスペースを指定してSlack Appを作成する
Slack Appの設定
Redirect URLの追加を行う
Bot Userの作成
Event Subscriptionsの設定
Request URLに https://slack.botframework.com/api/Events/{ボット ハンドル} を入力する
ボット ハンドルはAzure Functions Botの設定にある
Subscribe to Bot Eventsへダイレクトメッセージに反応するイベントを追加する
Azure Functions Botのチャンネル設定
Azure Functions BotのチャンネルからSlackを選択
Slack AppのBasic InformationにあるApp Credentialsの情報をSlackの資格情報へ設定する
まとめ
QnA Makerをつかうと簡単にSlackで上で質問に回答してくれるBotが作成出来た
参考
kubeadmでKubernetes1.9.3のClusterを構築したメモ
CloudGarage Deep Meetup in Hiroshimaで行ったLTのスライド作成時に行った事をメモしておく
- 1. まずは使用するインスタンスの作成
- 2. Dockerのインストール
- 3. kubeadm, kubelet, kubectlのインストール
- 4. Cgroup Driverの設定
- 5. kubeletの自動起動設定
- 6. OSの設定
- 7. Maste Node の初期化(Maste Node)
- 8. podネットワークのインストール(Maste Node)
- 9. Nodeの追加(Worker Node)
- おまけ
- 参考
- CloudGarage Deep Meetup in Hiroshimaで行ったLTスライド
1. まずは使用するインスタンスの作成
今回はLT用にCloudGarageさんに6Gタイプのインスタンスプランを頂いたので下記のスペックで3つ作成
2. Dockerのインストール
$ apt-get update
$ apt-get install -y docker.io
3. kubeadm, kubelet, kubectlのインストール
- kubeadm:Kubernetesが公式に提供しているKubernetes Clusterを簡単に構築できるツールキット
- kubelet:全てのNodeで動いているnode agent
- kubectl:Kubernetes ClusterをコントールするためのCLI
$ apt-get update && apt-get install -y apt-transport-https $ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - $ cat <<EOF >/etc/apt/sources.list.d/kubernetes.list > deb http://apt.kubernetes.io/ kubernetes-xenial main > EOF $ apt-get update $ apt-get install -y kubelet kubeadm kubectl
4. Cgroup Driverの設定
Cgroup DriverをDockerとkubeletとの間で一致させておく必要がある 以下で確認
$ docker info | grep -i cgroup WARNING: No swap limit support Cgroup Driver: cgroupfs $ cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf | grep KUBELET_CGROUP_ARGS
異なる場合は以下を10-kubeadm.confへ追記
Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs" $KUBELET_CGROUP_ARGS
最終的に以下のようになる
$ cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf | grep KUBELET_CGROUP_ARGS Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs" ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_SYSTEM_PODS_ARGS $KUBELET_NETWORK_ARGS $KUBELET_DNS_ARGS $KUBELET_AUTHZ_ARGS $KUBELET_CADVISOR_ARGS $KUBELET_CERTIFICATE_ARGS $KUBELET_EXTRA_ARGS $KUBELET_CGROUP_ARGS
5. kubeletの自動起動設定
kubeletが自動起動になっているかの確認
$ systemctl is-enabled kubelet enabled
自動起動になっていない場合は変更する
$ systemctl enable kubelet && systemctl start kubelet
6. OSの設定
kubeletがちゃんと動くためにはswapを無効にする必要があるとのことなので、/etc/fstabを編集してswapをコメントアウトした
/dev/mapper/centos-swap swap swap defaults 0 0
で、OSリブート
※ 2 から 6の内容を各インスタンスに行う
7. Maste Node の初期化(Maste Node)
podネットワークプロバイダにflannelを使用するため、 --pod-network-cidr=10.244.0.0/16を指定する
$ kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=<kube-apiserverがリクエストを待ち受けるIPアドレス>
初期化終了後に発行されるトークン kubeadm join xxx.xxx.xxx.xxx:6443 --token *** は後で使用するので記録しておく
Your Kubernetes master has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ You can now join any number of machines by running the following on each node as root: kubeadm join xxx.xxx.xxx.xxx:6443 --token uxhdok.0z4s4lyjyb7qot36 --discovery-token-ca-cert-hash sha256:8dd4dd2fd3442174c99c67d683ac5ad03c52f0dbfec42f60c215a1336f5b5fb3
8. podネットワークのインストール(Maste Node)
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml clusterrole.rbac.authorization.k8s.io "flannel" created clusterrolebinding.rbac.authorization.k8s.io "flannel" created serviceaccount "flannel" created configmap "kube-flannel-cfg" created daemonset.extensions "kube-flannel-ds" created
kubectlコマンドで下記のメッセージが表示された場合
The connection to the server localhost:8080 was refused - did you specify the right host or port?
ユーザの場合は.bashrcへ下記を追記する
export KUBECONFIG=/etc/kubernetes/admin.conf
9. Nodeの追加(Worker Node)
- で出力されたトークンをWorker Nodeで実行しNodeの追加を行う
$ kubeadm join xxx.xxx.xxx.xxx:6443 --token uxhdok.0z4s4lyjyb7qot36 --discovery-token-ca-cert-hash sha256:8dd4dd2fd3442174c99c67d683ac5ad03c52f0dbfec42f60c215a1336f5b5fb3 [preflight] Running pre-flight checks. [WARNING Hostname]: hostname "worker02" could not be reached [WARNING Hostname]: hostname "worker02" lookup worker02 on 203.174.65.3:53: no such host [WARNING FileExisting-crictl]: crictl not found in system path Suggestion: go get github.com/kubernetes-incubator/cri-tools/cmd/crictl [discovery] Trying to connect to API Server "125.6.66.132:6443" [discovery] Created cluster-info discovery client, requesting info from "https://125.6.66.132:6443" [discovery] Requesting info from "https://125.6.66.132:6443" again to validate TLS against the pinned public key [discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "125.6.66.132:6443" [discovery] Successfully established connection with API Server "125.6.66.132:6443" This node has joined the cluster: * Certificate signing request was sent to master and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the master to see this node join the cluster.
Maste NodeでNodeの確認
$ kubectl get nodes NAME STATUS ROLES AGE VERSION master Ready master 4h v1.9.3 worker01 Ready <none> 27s v1.9.3 worker02 Ready <none> 2m v1.9.3
おまけ
k8sダッシュボードのデプロイ
kubernetes-dashboardのデプロイ
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml secret "kubernetes-dashboard-certs" created serviceaccount "kubernetes-dashboard" created role.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created rolebinding.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created deployment.apps "kubernetes-dashboard" created service "kubernetes-dashboard" created
作成されたpodの確認
$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system etcd-master 1/1 Running 0 37m kube-system kube-apiserver-master 1/1 Running 0 37m kube-system kube-controller-manager-master 1/1 Running 0 37m kube-system kube-dns-86f4d74b45-lt994 3/3 Running 0 4h kube-system kube-flannel-ds-2qfg6 1/1 Running 1 26m kube-system kube-flannel-ds-6b8x9 1/1 Running 1 24m kube-system kube-flannel-ds-dzsd7 1/1 Running 0 38m kube-system kube-proxy-jvg28 1/1 Running 0 4h kube-system kube-proxy-lfm59 1/1 Running 0 24m kube-system kube-proxy-tslwt 1/1 Running 0 26m kube-system kube-scheduler-master 1/1 Running 0 37m kube-system kubernetes-dashboard-7d5dcdb6d9-hlh2r 1/1 Running 0 7m
API ServerへローカルPCからアクセスするためにadmin.confを取得しkubectl proxyを実行する
$ scp root@xxx.xxx.xxx.xxx:/etc/kubernetes/admin.conf . kubectl --kubeconfig ./admin.conf proxy
ダッシュボードURL
http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/overview?namespace=default
このままではログイン出来ない!
ダッシュボードのPodのService Accountであるkubernetes-dashboardにAdmin権限を付けるとサインイン画面でSKIPを押すとなんでも見れるようになる cluster-adminというClusterRoleにkubernetes-dashboardにバインドするClusterRoleBindingのYAMLファイル作成する (本番ではNG)
apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: kubernetes-dashboard labels: k8s-app: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kube-system
作成したYAMLファイルををkubectlでロールを作成する
$ kubectl --kubeconfig ./admin.conf create -f dashboard-admin.yml clusterrolebinding "kubernetes-dashboard" created
参考
- Installing kubeadm - Kubernetes
- Kubernetes1.8のクラスタを構築する。kubeadmで。 | To Be Decided
- Kubernetes 1.8のアクセス制御について。あとDashboard。 | To Be Decided
CloudGarage Deep Meetup in Hiroshimaで行ったLTスライド
そんな広島のスキー場
この記事は「そんな広島 Advent Calendar 2017」の18日目の記事です。
今年は寒くなるのが早いですね。
広島県は瀬戸内の温かい気候にも関わらずウインタースポーツも楽しめるお得な県なのです。
突然ですが広島県のスキー場どのくらい在るのだろうか。
調べてみた。
広島県公式観光サイトでは、日本の南限に位置するスキー場の集積地で現在は下記の13のスキー場が県内に在るようです。
広島県のスキー場
- りんご今日話国スキー場
- ひろしま県民の森スキー場
- スノーリゾート猫山
- 道後山高原スキー場
- スノーフィールドもみのき森林公園
- 女鹿平温泉めがひらスキー場
- 恐羅漢スノーパーク
- やわたハイランド191リゾート
- 芸北高原 大佐スキー場
- 雄鹿原高原スキー場
- 芸北国際スキー場
- ユートピアサイオト
- スキーパーク寒曳
ちなみに私がよく行くスキー場は芸北国際スキー場です。
アクセス方法
近いスキー場であれば広島市内から車で一時間半もあれば着きます。
道具に関して
各スキー場ではレンタルも行っているので大丈夫です。
ツアーによってはレンタル込みのものも有るので確認してみましょう。
よく行かれる方でお子様のレンタルにお困りの方は、スキープロショップ・ピステのシーズンレンタルを利用すると良いかと思います。
子供は成長するのが早いので私もピステのシーズンレンタルを利用しています。
そう言えば、最近はようやくヘルメットを着用する方が多くなった気がしますがヘルメットのメリットは何と言っても温かい!!
特にお子様と行かれる方はヘルメットを着用して楽しく安全に楽しみましょう。
まとめ
広島県のスキー場数は西日本でもトップクラスです。
瀬戸内は温暖で山に行けばウインタースポーツも楽しめるお得な地域ですので、しっかり楽しみましょう!