半農半エンジニアの記録

関西在住エンジニア。個人で勉強・開発したこと、その他趣味のことを書いてます。農業してます。

【読書録】プログラムはなぜ動くのか

未経験からソフトウェアエンジニアになり7年。
仕事ができる程度のプログラミングはできるようにはなったけど、CPUとかメモリとかプログラムの仕組みに関する知識をしっかり学んだことがないのでちゃんと勉強してみることにした。

第一弾はこちら

プログラムはなぜ動くのか 第2版 知っておきたいプログラムの基礎知識

プログラムはなぜ動くのか 第2版 知っておきたいプログラムの基礎知識

  • 作者:矢沢久雄
  • 出版社/メーカー: 日経ソフトウエア
  • 発売日: 2007/04
  • メディア: 単行本(ソフトカバー)

選んだ理由

とりあえず本屋でいくつか目を通してみて

  1. 随所に図が挿入されていて取っつきやすそう
  2. 昔の本だけど版数が多い

といったあたりで一冊目として読むにはいいのかな、と判断。

概要

タイトルの通りプログラムがなぜ動くのか、つまり人間が書くプログラムを PC がどうやって理解し実行しているのか、という内容。
高水準言語をコンパイルして機械語に翻訳されるとどう変わるのか、C言語をアセンブリ言語に変換して比較しながら解説している。
またCPUの構成(主にレジスタ)とともに、読み込んだコードがCPU、メモリを駆使して実行される流れの説明がされている。

感想

はじめに読む本としては間違ってなかったかな、という印象。
前述の通り図を使って説明してたり、内容もそこまで深入りしない内容だったので合間の細切れな時間でも読み勧めやすかった。
アセンブリ言語を使って比較しながら説明されるのでわかりやすい。
一方、やはり古い内容の本であるため内容があてにならない部分も散見される(PC のメモリは 2 GB くらい、最新の Windows は Vista だ、とか)。このあたりは流石に改定してほしい。
軽くつまむ程度には良いが、しっかり勉強するには物足りない。

キーワード

CPU
メモリ
レジスタ
高水準言語
アセンブリ言語
コンパイル
機械語

Flutter で作ったアプリに広告表示する(firebase_admob)

Flutter で作ったアプリで広告収入を得るために、admob ツールを使った。 firebase_admob という公式チームが作成しているライブラリがあるので、そちらを利用する。

pub.dev

気になったこと

アプリに広告を表示するまでの手順は、こちらのブログに書かれている手順に沿って設定できた。

[Flutter] AdMob を使ってバナー広告を表示する方法 │ Web備忘録

バナー広告でやってみて設定・表示するだけだと問題なくできたが、ちょっと工夫・考慮が必要な部分があった。
以下、画面下バナー広告を表示した場合の話

縦・横表示対応に向いていない

横向き(landscape)だと必然的に高さが小さく、広告でそれがさらに小さくなる。UI・UX 的に難ありなので縦(portrait)固定にした。

void main() {
  FirebaseAdMob.instance.initialize(appId: appId);
  // バナー広告を表示する
  myBanner
    ..load()
    ..show(
      // ボトムからのオフセットで表示位置を決定
      anchorOffset: 0.0,
      anchorType: AnchorType.bottom,
    );

  // 縦固定
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);

  runApp(MyApp());
}

位置調整が必要

ライブラリにより表示される広告は、Flutter の世界からは切り離されている。つまり Widget ではない。なので普通に画面いっぱい表示させると Widget の上に広告が表示される。

例えば Floating action button はデフォルトで右下(bottomEnd)に表示されるが、広告がその上に表示されるので、ボタンは完全に隠れてしまう。

なので Padding を追加して広告を避けさせる必要がある。 *1

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        ...
      ),
      body: Container(
        ...
      ),
      floatingActionButton: Padding(
        padding: const EdgeInsets.only(bottom: 90.0),
        child: FloatingActionButton(
          ...
        ),
      ),
    );
  }

キーボード出現時の位置調整

上記の位置調整をしたとき、更に問題が出てくるパターンがある。それは入力がある画面の場合だ。

キーボードが出てきたときに、Flutter のオブジェクトはキーボードが出てきた分を認識して下端がキーボードの上端になる。しかし広告は上記の通り Flutter とは切り離されており、かつキーボードを認識しない。そのため広告はキーボードによって隠されてしまう。

これで何が問題かというと、位置調整で Padding したぶんがそのまま適用されてしまっており、広告がないのに広告の分だけ Padding されたままになってしまうのである。
なのでこれを解決するには、キーボードが開いているかどうかを判定し、 Padding を動的に設定する必要がある。

ただし Flutter ではそのようなプロパティは用意されていないようなので、下記のpackage を使って対応することにした。*2

pub.dev

 @override
  void initState() {
    super.initState();
    KeyboardVisibilityNotification().addNewListener(
      onShow: () {
        _padding = EdgeInsets.all(0.0);
      },
      onHide: () {
        _padding = EdgeInsets.only(bottom: 90.0);
      },
    );
  }

という感じで違和感なく広告が表示できるようになったんじゃないかと思う。

*1:Scaffold の floatginActionButton は、FloatingActionButton を設定する必要はないので Padding でラップが可能

*2:MediaQuery.of(context).viewInsets.bottom の値で判定する方法を取っている人もいるようだが、Android でのみ動作し iOS では正常に動作しないらしい

Flutter の多言語対応(Intl)

Flutter で多言語対応した。

公式ドキュメントでもよく説明されていると思うし、 flutter.dev

こちらの mono さんの記事もすごく参考になる。
Dart/Flutter での多言語対応あれこれ - Flutter 🇯🇵 - Medium

今のところ DateFormat や NumberFormat は不要なので、文言をロケールに合わせて変更する部分だけ行った。

目次

パッケージのインストール

pubspec.yaml に追加。

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  flutter_cupertino_localizations: ^1.0.1

flutter_cupertino_localizations は iOS でちゃんと動作させたいなら入れなさい、とのこと。

追記したら flutter pub get を実行、もしくはAndroid Studio なら、Packages get をクリック。

Android Studio で Packages get をクリック

MaterialApp修正

import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_cupertino_localizations/flutter_cupertino_localizations.dart';

MaterialApp(
 localizationsDelegates: [
   // ... app-specific localization delegate[s] here
   GlobalMaterialLocalizations.delegate,
   GlobalWidgetsLocalizations.delegate,
   GlobalCupertinoLocalizations.delegate,
 ],
 supportedLocales: [
    const Locale('en'), // English
    const Locale('ja'), // Japanese
    // ... other locales the app supports
  ],
  // ...
)

localizationsDelegates

ローカライズした value を使うためのファクトリを指定する。

  • GlobalMaterialLocalizations.delegate
    文字などの文言や他の MaterialApp コンポーネントようライブラリ
  • GlobalWidgetsLocalizations.delegate
    文字列の方向(英語は左から右、アラビア語は右から左、みたいな)を定義するためのライブラリ

ということなのでおまじない的に両方入れておけばいいのかな。アラビア語とか対応しない限りは GlobalWidgetsLocalizations.delegate は無くてもいいのかはわからない。(試してない)

supportedLocales

アプリが対応する言語のロケールを指定する。
中国の繁体字/簡体字みたいに方言?がある場合はLocale.fromSubtags(languageCode: 'zh') という感じでできるらしいが、今回は日本語と英語のみなので省略。

ローカライズリソースの定義

class DemoLocalizations {
  DemoLocalizations(this.localeName);

  static Future<DemoLocalizations> load(Locale locale) {
    final String name = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
    final String localeName = Intl.canonicalizedLocale(name);
    return initializeMessages(localeName).then((_) {
      return DemoLocalizations(localeName);
    });
  }

  static DemoLocalizations of(BuildContext context) {
    return Localizations.of<DemoLocalizations>(context, DemoLocalizations);
  }

  final String localeName;

  String get hello {
    return Intl.message(
      'こんにちは',
      name: 'hello',
      locale: localeName,
    );
  }
}

initializeMessages() がローカライズされたメッセージカタログで読み込み、intl パッケージが インポートする。
Intl.message() でそれらを読み込んで返している。
という流れ。 メッセージカタログは、後述する intl のツールで作成できる。

多言語対応(メッセージカタログの作成)

intl のツールを利用する。

arb ファイル作成

 flutter pub run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dart

main.dart 内にある Intl.message() ごとに定義を作成できる。
lib/l10n の下に intl_messages.arb という JSON ファイルが出力される。
複数言語対応するときは、この arb ファイルをベースに intl_ja.arb や intl_en.arb を作成する。

dart ファイル作成

 flutter pub run intl_translation:generate_from_arb \
    --output-dir=lib/l10n --no-use-deferred-loading \
    lib/main.dart lib/l10n/intl_*.arb

上記で作成した arb ファイルごとに、 intl_XXX_all.dart が作成される。
この dart ファイルの中身がロケールに応じて使われる。

使ってみる

print(DemoLocalizations.of(context).hello);

みたいにするだけ。
デフォルトロケールが日本と想定して、

こんにちは

ロケールを変更したい場合は、 DemoLocalizations.load(Locale('en')); としてやる。

DemoLocalizations.load(Locale('en'));
print(DemoLocalizations.of(context).hello);
hello

その他

とりあえず公式ページにあるコードをサンプルとして掲載。
後日 git にサンプルあげる。

【車中泊】パート0 初心者のススメ

車中泊に興味があるけど何が必要かわからない、やり方がわからないなど、初心者にありがちな悩み。
ググっても意外に初心者向けの情報がすくなかったりしたので、初心者向けの情報を、初心者からの視点でまとめられたらと思う。

目次

自己紹介

ブログ主

関西在住、妻と二人暮らし。
身長190センチと、車中泊に相性の悪い高身長。
2019年になってから、車中泊をやるようになった。 まだまだ初心者で、毎回いろんなことを試しながら、次はこうやってみよう、というのがすごく楽しい。 初心者感覚で気づいたことを書いておいて、誰かのためになればと思う。

車中泊を始めた経緯

普段お世話になってる理容師が車中泊をやってると聞いたのがきっかけ。
以前から車に自転車を載せて長野まで行ったりしていたので、それの延長くらいのノリ。
そして意外にも妻がすごく乗り気だった。

車紹介

車種

  • モビリオスパイク
    今は生産されていないが、後継車種としてフリードがある。
    車中泊に限らず、アクティブな夫婦二人で使うには最強の車だと思っている。
    車を買うなら自転車を載せれる、というのを最低条件にして探していたところ、モビリオスパイクを勧められた。ハンドルを切った状態にしておく必要はあるが、タイヤを外さなくても2台載せれる。
    また運良く安く購入できるツテもあったため、購入に踏み切った。

装備

カーテン(目隠し)

寝るときに外部からの光や視線を遮るために必要。自作した(妻が)。
ドアガラス部分は市販のカーテンでいいとも思うが、微妙に隙間から光は入ってきたりするし、フロント・リアガラス部分は結局何か必要。

ポータブル電源

携帯充電用のモバイルバッテリー持ってる場合は、はじめのうちは要らないと思う。持ってるとコンセントでもUSBでも使えるので、便利。

suaoki ポータブル電源 S270 40540mAh/150Wh 家庭用蓄電池 PSE認証済み 三つの充電方法 AC(150W) DC(180W) USB出力 急速充電QC3.0 車中泊 キャンプ 釣り アウトドア 防災グッズ 地震 停電時に 二年間保証

収納

モビリオはデフォルトで収納がすごく充実している。なので特別追加しなくてもいいが、運転席後ろにポケット無いのはやや不便だったので、そこだけ追加。

ロープ、バー

使ったタオルとか照明を吊るす用に、車内にロープを張った。 後部座席上の持ち手に、キャンプ用のループ付きのロープを張っただけ。ループ付きなのが意外と便利。

テーブル

折りたたみ式のミニテーブル。車内でご飯食べたりするときあると良い。運転席と助手席で食べる場合は要らないかも。

車中泊をやってみて思うこと

メリット

時間の制約がない
ホテルのチェックインとか退室の時間を気にしなくていい。出発時間も前日の夜とかにできる自由。
荷物の出し入れが少ない
安あがり

デメリット

お店で食べるとお酒飲めない
お風呂 狭い

記録

やりたいこと

  • キャンプファイヤー
  • 釣り

Flutter の pop による戻り値

Flutter のルーティングで push / pop する時の扱い

スタックでのルーティング

概略

  • push 時に return で受け取りたい型を指定
  • pop 時に指定された型を return
  • pop の引数を省略すると null が返る
  • 戻るボタンでも null が返るので、必ず考慮が必要

ページ遷移(進む)

push で積む。
Navigator.of(context).push
を使う。

return で受け取りたい型を指定してやればいいので、 int を指定してみる。

int ret = await Navigator.of(context)
    .push(MaterialPageRoute<int>(builder: (BuildContext context) {
  return SecondPage();
}));

ページ遷移(戻る)

pop でスタックから取り除く。
Navigator.of(context).pop
を使う。

この時、 push 側で受け取りたい戻り値を、 pop() の引数で渡す。

int _counter;
~~ 中略 ~~
Navigator.of(context).pop(_counter)

引数を省略すると null が返される。

デフォルトの戻る機能

OS の戻る機能を使うと、引数を省略した時と同じで null が返される。

back button in Android
back_button

なのでそこは考慮しておく必要がある。逆に言えば、戻る機能と同じ挙動をさせたければ、引数を省略して null を返せばいい。

コードサンプル

全体のコード

simple push and pop demo with return

【車中泊】パート4 岡山編

最近、車中泊にハマってるのでその記録を残すことにした。あれば良かった、使って良かった便利グッズや、プランの練り方など気づいたことをメモしていく。初心者なので、同じような方の参考になればと思う。

(パート4と題しているが、過去3回分はそのうち投稿したい)

10月の3連休に2泊3日の岡山旅行してきた。

目次

行程

前日の夜出発も検討していたが、仕事で遅くなったので普通に朝出発にした。

  • 一日目
    大阪→岡山県立博物館(岡山市)→後楽園(岡山市)→道の駅一本松展望園(瀬戸内市)
  • 二日目
    →カブトガニ博物館(笠岡市)→サンロード吉備路(総社市)→高松城址公園跡(岡山市)→吉備スマートSA(岡山市)
  • 三日目
    →備前焼ミュージアム(備前市)→五味の市(備前市)

アップデート

今回のニューアイテムたち

ドライブパス

高知に行ったとき、使っていれば高速代がお得になっていたことを知った。今回は事前申込み。 定額で一帯の高速が乗り放題。

吊り紐

ランタンとか、風呂で使ったタオルとか掛ける場所。 とても便利なのでオススメ。 キャンプ用のロープをカラビナで後部座席ドア上に引っ掛けるだけでOK。

シートポケット

運転席の後ろにポケットが無かったので。 収納はいくらあっても便利。

車中泊拠点

今回は初のSA利用。2泊したのも初めて。

道の駅一本松展望園

国道2号線、ここら周辺は岡山ブルーラインと名付けられており、信号も無く快適に走行できる。そのブルーラインの道中にある道の駅。
瀬戸内海を一望でき、朝はコンビニ飯でも気持ちよく味わえる。9時オープンのレストランにはテラス席もあり、そちらも良いかもしれない。夜は星もよく見える。運良く流れ星も見れた。
トイレは複数あり、今回は片方が工事中だったが、もう片方も十分きれいで虫も全然いない。なぜか野良猫がすごく多い。
ただし運悪く地元のヤンキーがバイクで集まり騒いでいた。頻繁に来るのかもしれない。

吉備スマートSA

ラグビーW杯スコットランド戦をビール飲みながら見たい。でも車やしスポーツバー行けない…。SAならいけるのでは?となり訪問。なんとなく予想してたけど、SAではノンアルのみ。でもテレビはあったのでノンアル飲みながら観戦。
想像していたより快適。人も多く、売店も24時間やってたので、車中泊初心者にはうれしい。

ごはん

今回は夕飯もお店に入って食べた。

大坂屋(お好み焼き)

maps.app.goo.gl

豚モダンとイカモダン(うどん)を注文。 感動するような味ではないが、お手頃価格でシンプルな味を求めている時は是非。

レストランまつもと(洋食)

goo.gl

ミニッツステーキもミンチカツを注文。ステーキはポン酢系のソース、ミンチカツはデミグラスソース。どちらも美味。少し値は張るが、満足。

山ちゃん(笠岡ラーメン)

goo.gl

細麺の醤油ラーメン。チャーシューが鶏肉。あっさりしながらも深みがあり、大盛りでもぺろり。メニューはラーメンのみ。(大盛りあり)

えびめしや(えびめし)

goo.gl

今回のヒット飯。岡山で数店展開するチェーン。広々としたファミレスで、店名の通り「えびめし」というソース味のえびチャーハンが看板商品。岡山のB級グルメとして知られているらしい。
今回はハンバーグ&大盛りオムえびめしとエビフライ&えびめしを注文。個人的にはオムえびめしのほうが好みだが、妻はノーマルえびめしのほうがよかったらしい。いずれも独特なソース味が良い。プレートに乗ってるコールスローが、ソース味によく合う。十分な量があったが、冒頭の通りヒットしたので、追加でえびめし&ワンタンスープを追加注文した。
とまあ褒めまくったが、セットで付いてきたハンバーグ、エビフライは正直普通というか、冷凍食品レベル。スープは美味しいが、ワンタンは普通。ワンタンスープのセットが一番という結論になった。

浜屋みっちゃん(かきおこ)

goo.gl

牡蠣の産地である日生(ひなせ)にある、夫婦経営のお店。5年ほど前に一度訪問。当時の感動もあり、再訪。まだ牡蠣のシーズンには早いが、一帯のかきおこ屋はどこも営業している模様。牡蠣は地産ではないのかもしれない。 かきおこ2つ注文。
やはり美味。夫婦のキャラクターも魅力的。また旬の時期に再訪したい。

その他

ローカルなお菓子を探したが、今回は見つけられず。

振り返り

ドライブパスで損した

前回高知に訪問したときには、ドライブパスで往復分を下回っていたこともありお得だった。今回は岡山と比較的大阪から近距離だったこともあり、ドライブパスで逆に赤字になってしまった。
今回訪問した岡山は無料道路が充実しており、あまり高速を使う機会がなかったことも要因。

快眠のために

今回初めて騒がしいヤンキーに遭遇。こういう時は耳栓とがほしい。
またより良い睡眠のためには枕が欲しい。枕はタオルで代用してみようと思う。

はじめてのSA車中泊

道の駅と比べると、

  • トイレが広くてきれい
  • 24時間コンビニで買い物できる(場所による)

あたりがメリットに感じ、初心者もしやすい気がする。デメリットは、

  • 深夜でも大型車の出入りが多い
  • 明るすぎる

とはいえこれは駐車位置や遮光をすれば調整できるので、スマートICや乗り放題パス持ってるならSAのほうを選びたい。
また他車の音については、前日のヤンキーの件があったので多少のエンジン音は気にならなかったというのはあるかもしれないので、注意しておきたい。
また車中泊でテレビ見れるのは、こういったイベントのときに使えそう。

python で リモートサーバーにある postgresql の copy コマンドを実行する

python で リモートサーバー(python 実行とは異なるサーバー)にある postgresql の copy コマンドを実行したときの挙動についてメモ

ライブラリ

psycopg2 というやつを使いました。

調べるとpython copy postgresql なんかで検索するといろいろ情報出てきた。

やり方

いろいろやり方はある

copy_to / copy_from

conn = psycopg2.connect("dbname=test user=postgres")
cur = conn.cursor()
f = open("./output.csv")
cur.copy_to(f, table_name)

copy_expert

conn = psycopg2.connect("dbname=test user=postgres")
cur = conn.cursor()
cur.copy_expert("COPY db_name.table_name FROM file_name)
i

execute

conn = psycopg2.connect("dbname=test user=postgres")
cur = conn.cursor()
cur.execute("COPY db_name.table_name FROM file_name")

あまりちゃんと調べていないので、copy_expert と execute の違いが理解していませんが。。

リモート/ローカルの違い

あえてタイトルに「リモートサーバーにある」 postgresql と書いているのには理由があって、プログラム実行コマンドとDBのサーバーが異なると、動きが違った。

command ローカル リモート
copy_to
copy_expert ×
execute ×

上記の○はリモートDBのデータをローカルにファイルで保存できるかどうか。 copy_from (ローカルのファイルを読み込んでリモートDBに登録)も同じ結果だった。 copy_expert や execute では、おそらくリモートDBのサーバー内のファイルを指定されたと認識するような挙動になった。

copy_to はテーブル指定のみ

copy_to / from が用意されているので普通に使えばいいのだが、少し困ったのがcopy_toでクエリ指定したいとき。 copy_to はテーブル名の指定しかできないため、任意のクエリによる結果を抽出できない。

なのでtemporary table を作成して、そこから copy_to することにした。 またpostgresql 10 からは、view からも copy できるようです。

XamarinでQRコード表示 - 落とし穴編

前回の記事にしたXamarinで使えるQRコード関連ライブラリのZXing。その後ハマったのでメモしとく。

www.m24te28.com

ちなみにUWPアプリの話で、Android、iOSは検証していない。

何が起こったか

ライブラリ使えばQRコードなんて楽やな、なんて思ってたのも束の間、事件が起こったのはそう、リリース時。 いや正確にはReleaseビルドした時。

察しの良い方はわかったかもしれない。そう、Debugビルドで動いていたのにReleaseビルドでは動かない。

正確にはReleaseビルドではQRコードが表示されない。

まじで焦った。もう夜の8時や。今日中に完成させなあかんのに。

解決方法

最終的にはググったら同士が見つかり、その通り設定すれば動いた。

ZXingScannerPage cannot work on UWP release build · Issue #775 · Redth/ZXing.Net.Mobile · GitHub

ビルド構成って何や?と思って調べたところやったのもあって、アタリをつけやすかった。

何が変わった?

要するに中間コードまでの生成で留めて、.Netランタイム上で動く状態にしておくらしい? そのためパフォーマンス下がりそう。(聞いたことそのまま)

ビルド時間めっちゃ短くなったので、なんとなく納得した。

詳しい人おりましたら是非とも教えていただきたい。

あるある?

この「Debugビルドで動くけどReleaseビルドすると動かなくなる」というのはあるある、というのをちょうど先日見かけたので心の準備ができていたのだろうか。焦ったけど、「これが噂のやつか…」という感覚になった。

常日頃Releaseビルドすることの大切さを実感。

別のライブラリ?

自分でも調べたつもりで良い候補が無いなーと思ってたけど、下記のライブラリを使ってみたら?と言われたので、また試してみる。

www.nuget.org

追記

別のライブラリ使っても同じ現象になった。両ライブラリでベースが同じなのか?とか思ったけどワカラン。

真の問題

しゃーなし今回は.NET Native tool chainビルド無しでやるか、と決心して数分後、別の問題発生。

Windowsストア申請する時に、「このプロジェクトは.NET Native tool chainビルドじゃないとアカンで」とのこと……orz

どうすればいいのかわからないまま、QRコードの実装はひとまず延期……

おまけ?今週のお題「雨の日の楽しみ方」

競プロ

XamarinでQRコード表示

Xamarinで作成中のアプリで、QRコード表示をする。 「Xamarine QR」でググってみると、読み取りの情報はよくヒットしたけど、生成の方は少なかった。

ZXing

Zxing(Zebra Crossing)というライブラリを利用した。

良いネーミングだ。。

実装

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:qrSample"
             x:Class="qrSample.MainPage"
             xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
             xmlns:zxcm="clr-namespace:ZXing.Common;assembly=zxing.portable">
    <StackLayout>
        <!-- show QRcode -->
        <zxing:ZXingBarcodeImageView WidthRequest="300" HeightRequest="300"
                                     BarcodeValue="https://google.com"
                                     BarcodeFormat="QR_CODE">
            <zxing:ZXingBarcodeImageView.BarcodeOptions>
                <zxcm:EncodingOptions Width="300" Height="300" />
            </zxing:ZXingBarcodeImageView.BarcodeOptions>
        </zxing:ZXingBarcodeImageView>
    </StackLayout>
</ContentPage>

という感じ。

f:id:m24te28:20190606070940p:plain
qrcode_sample

BarcodeOptionsは無くても表示できるけど、なぜか少しぼやけたイメージが表示される。

  • BarcodeValue="{Binding foobar}"としてバインディング可能
  • WidthRequest、HeightRequestは必要

【Rancher】Rancher CLIでkubernetesのコンテナを更新する with gitlab-ci

概要と環境

CI/CDのCDの部分をrancher CLIで設定した。

rancherはバージョン2なので、オーケストレーションはk8s。

CI/CDはgitlab-ciから。

gitlabでのCI

gitlabでは.gitlab-ci.ymlというジョブ定義ファイルを作成する。

他のCIツールを使ったことが無いので比較はできないが、どのブランチの時はどのジョブを実行するか、特定のブランチのみ環境変数を参照させる、みたいなことはできる。

やりたいこと

もともとrancher1.6で同じことはしていたので、dockerイメージのbuild部分(つまりCI)はあって、今回そこはいじる必要ナシ。

rancher1.6→2にしたことで、中のオーケストレーションがcattleからkubernetesに変わったこともありCLIに大きな変更があった。それによりbuild、pushしたイメージにアップデートする部分(CD)をリファクタリング。

やりかた

単純にdeploymentのマニフェストを更新してやる。

開発環境はタグをlatestで固定しているので、annotationを書き換えていく。gitlab-ciでは$CI_PIPELINE_IDに毎回異なる値が入るので、そいつをannotationsにつけて更新。

stackoverflow参考にして、imageは変更。 リンク先のimageではCLIのバージョンが古くて、ログイン時にk8sのnamespace(--context)を指定できなかったから。--contextパラメータを使うには2.0.4以降のバージョンが必要。

kubernetes - Upgrade Rancher 2 workload from CLI - Stack Overflow

$RANCHER_URLと$RANCHER_BEARER_TOKENはrancherで発行してgitlabに定義しておく。

image: heunghingwan/rancher-cli-k8s
stage: upgrade
script:
  - rancher login $RANCHER_URL --token $RANCHER_BEARER_TOKEN --context your_namespace
    - rancher kubectl --namespace=default patch deployment nginx -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"id\":\"$CI_PIPELINE_ID\"}}}}}"

あとがき

本番環境のアップデートは任意のタイミングで行う必要があり、CDまで行うのは開発環境だけ、という理由もあってこんなやり方してるけど、本来はイメージタグを書き換えるべき。

gitlab-ciだとタグ名とか取れるみたいなので、これも簡単にできそうではある。

gitlab.com

【kubernetes】Pod間に依存関係を持たせるときの一工夫

Kubernetesにアプリをデプロイするにあたり、Podが1つということはあまりないと思う。

かつ複数のPod(AとB)を、Aが起動できてからBを起動させる、みたいに依存関係を持たせるのは一般的に考えられるユースケースだと思う。WEB + DBなんかは特に。

本記事はそんな話。

docker-compose

dockerをインストールすればdocker-composeもKubernetesもインストールできる。

まだKubernetesよりdocker-composeで複数コンテナ作ってみる人のほうが多いのではと思う。のでdocker-composeでのやり方と比較(というほどでもないが)しながら進めたい。

docker-composeはコンテナ間、KubernetesはPod間という違いはある。

docker-comopseではyamlファイルにdepends_onやlinksといったハッシュキーが用意されている。

version2からはこれらの違いは無くて、どちらもコンテナの依存関係を定義し、[エイリアス名]を使用してコンテナにアクセスすることができる。

依存関係を定義しておけば、依存元は依存先が立ち上げるのを待ってから、起動する。

Kubernetes

Kubernetesでは、docker-composeのように依存関係を明記する機能は備わっていない。

とはいえ必要な機能だからか、公式ページにやり方が記載されている。

PostStart

Container Lifecycle Hooksの1つに、PostStartというのがある。

公式ドキュメント

kubernetes.io

ざっくり訳す。

ここで定義した内容は、コンテナが作成された後に実行される。

ただし、コンテナのENTRYPOINTの前後どちらになるかは保証されない。

ふむ、今回の用途には不向きのようだ。

initContainers

containersとは別にinitContainersというのがある

公式ドキュメント

kubernetes.io

containersとの違い

こちらもざっくり訳す。

Init Containersでは、普通のContainerと同じfieldやfeatureをサポートしていて、resource limits, volumes, security settingなんかもある。でも、resource requestsやlimitsの扱いはちょっと違っていて、それはResourceのページ見てくれ。あとはreadiness probesはサポートしていない、だってPodがreadyになる前に完了するものだから。

複数のInit Containersがポッドにある場合、順番に走る。前のやつが成功するまで次のやつは走らない。すべてのInit Containersが完了したら、Podが初期化されて通常通りコンテナが実行される。

うん、これならいけそう。サンプルコードもそんな感じ。

nslookupは微妙?

公式のサンプルコードを抜粋する。

  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;

nslookupで名前解決できるまで走り続ける。つまり名前解決できた時点でInitContainerは終了して、containersで定義されているものが実行される。

これを使って、DBのポッドが起動した後に、そこに接続するアプリを含んだポッドを定義して試してみた。が、上手くいかない。

おそらく、DB起動コマンドが実行された瞬間にnslookupが成功していて、DBが正常に起動する前にDB接続をトライして失敗してるっぽい。

netcatでやってみる

少し修正し、netcatでポート指定。MySQLなので3306。

  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', 'until nc -z myservice 3306; do echo waiting for myservice; sleep 2; done;

これだと想定通り、DBがしっかり起動してからアプリのPodが起動し、DB接続も成功した。

まとめ

  • KubernetesでPod間に依存関係は作れる
  • 公式ページで紹介されている方法だと上手くいかないユースケースもある

ハンズオン参加:Reactを用いたFirebaseで認証するGraphQL APIサーバの構築

昨日はこのハンズオンに参加しました。

Reactを用いたFirebaseで認証するGraphQL APIサーバの構築 - Osaka Web Developers Meetup #1 - Osaka Web Developers Meetup | Doorkeeper

tl;dr

  • ついて行くので精一杯
  • タイピング速くならないと
  • Firebaseで認証すごい簡単
  • hasuraって何?面白い

題材はこちらです。

github.com

概要

フロントはReactでクエリ言語にGraphQL、認証にFirebase。またGraphQLエンジンにHasuraを採用。

(初めて触るものバカリ・・。)

Firebase

まずはログインしてプロジェクト作成、コンソールに移動。

認証プロバイダの設定

Authentication→ログイン方法で、認証プロバイダ(他のアプリケーションのアカウント)を選択できます。今回はGoogleアカウントを使った。

firebase認証のログインプロバイダにGoogleアカウントを設定
ログインプロバイダの有効化

functionのデプロイ

認証だけならこれで大丈夫ですが、認証情報をアプリ側で利用するので、そのためのfunctionをデプロイしておく。

Hasura

docker(docker-compose)でHasuraを立てる。

この時点でHasuraが適当な文字列と思っていたのはここだけの話。

Hasauraとは

postgreSQLをバックエンドにしたGraphQLエンジン、と言えばいいのかな?

Prismaとよく比較されている。

今日の話だと、PrismaはGraphQLが後ろにいて、Hasuraでは前にいる、とのこと。規模によって使い分けるのがいいのかな。

Hasura使ってみた所感としては、色々簡単にできてすごい。簡単なアプリならこれで十分?管理はどうすればいいのか?

GraphQLとは

クエリ言語らしい。調べるとRESTとよく比較されている。

個人的にはRESTより直感的に理解もしやすいと思う。

React

Reactはゴリゴリ書いてく。

リアルタイム更新

GraphQLのsubscriptionを使っていて、DBの変更を監視する感じになっている。

感想

主催者の方も言ってたけど、今日はやること多かった。前回参加したハンズオンは正直余裕やったので、同じ気持ちでいたらギリギリでした。

特にFirebaseはもっと触っていきたい。

【GCP】バケットを削除するときは中身を先に削除しないといけない

GCPのバケットをAPIから削除する場合、中身をまず消さないとダメ、という話。

中身が残ってる場合

PHPですが、単純にバケットを消そうとするとこんな感じ。

$storage = new StorageClient();
$bucket = $storage->bucket("name");
$bucket->delete();

バケットが空っぽだと大丈夫ですが、ファイルやフォルダがあると…

======================================================================
   The application has thrown an exception!
======================================================================
 Google\Cloud\Core\Exception\ConflictException
 {
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "conflict",
    "message": "The bucket you tried to delete was not empty."
   }
  ],
  "code": 409,
  "message": "The bucket you tried to delete was not empty."
 }
}

エラーになります。

空っぽじゃないよ、というエラー。

バケットの中身を消してやる

なのでバケットの中身を消してやります。

$storage = new StorageClient();
$bucket = $storage->bucket("name");
// バケットの中身を削除してからバケットを削除する
$objects = $bucket->objects();
foreach ($objects as $object)
{
  $object->delete();
}
$bucket->delete();

いちいち中身を1つずつ消すというダサいコードです。 とりあえずこれでバケットは削除できます。

一括削除したい場合

APIから

ライブラリのコード確認しましたが、とりあえずPHPではバケットの中身全削除とか、バケットの中身入ってても削除、みたいな方法はありませんでした。。

コンソール

GCPコンソールからは、バケット選択して削除すると、中身ごと消えます。

うーん、やはりAPIはあって、自分の調査不足なのか?

なんかありそうな気もするので、見つけたら追記します。

【Alexa】非エンジニアでもblueprintsでスキル作成

AlexaDay2019にて、blueprintsの即席ハンズオンが開催されてたので、隙間時間に参加しました。

blueprintsって?

Amazonアカウントに紐づけて、ノンコーディングでAlexaスキルが作成できます。

blueprints.amazon.co.jp

ハンズオンで聞いた特徴は以下のとおり

初心者・非エンジニアでも簡単

講師の方ははじめにこれ言うてた。基本エンジニアのイベントやし、ということもあってでしょう。

とはいえ、やってみるとまじで簡単。スマホでもできる。

これなら子どもも楽しめそう。

スキル名の呼び出しが不要

これはかなり便利。

通常作成・追加したカスタムスキルは、

「アレクサ、『スキルの呼び出し名』で〜〜して」

という実行方法になりますが、カスタムQAでは

「アレクサ、結婚記念日は、いつ?」

でOK。

カスタムQAは1スキルのみ、だけど・・

カスタムQAは1人1スキルしか作成できない。が、スキル内にいくつでも作成可能らしい。なので実質無制限。

とは言え、UI見ると1画面にスキル内のQAがすべて並んでいるように見えるので、あまりに多く作るとどうなるのか?そんなに作る人はいない想定?

カスタムQAで返せるのは固定メッセージ

今日の時間割は? → NG

水曜の時間割は? → OK

みたいな感じ。シンプルで良いと思う。

標準機能を上書きしてしまう

今日の天気は?みたいに、標準で備わってる機能をカスタムQAで作成すると、上書きされてしまうらしい。上書きした場合は、そのカスタムQAを削除すると復活する模様。

という話だったが、講師の方曰く、やろうとしたら、保存するときに固まって動かなくなったらしい。

真相はいかに・・。

思うところ

まだ作ってないので予想ですが

メンテしやすそう

固定メッセージしか返せないけど、スマホからも簡単に編集できるので、ほぼ変わらない内容じゃないとダメという感じはしない。

質問じゃなくてもいい

「アレクサ、いってきます」

「財布、携帯電話、定期は持ちましたか?」

みたいなチェック機能ができそう。

【kubernetes】nginx-ingress-controllerをロードバランサに採用する時に気を付けたいこと

kubernetesでL4ロードバランサを作成するときの選択肢として、nginx-ingress-controllerがある。 使う機会があったので簡単に設定方法を残しておく。

インストール

Installation Guide - NGINX Ingress Controller

基本的には公式サイトの手順に従って、kubectlコマンドを数回叩くだけ。

GKEの場合

今回はGKE上に構築したので、まずは

kubectl create clusterrolebinding cluster-admin-binding --clusterrole cluster-admin --user $(gcloud config get-value account)

そのあと

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml

で最後に

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/cloud-generic.yaml

で完了。超簡単!

GKE以外の場合

パッと見、Azureとはコマンド共通(clusterrole除く)ぽいけど、いつ変わるかわからんし、公式サイト見ながらするのが安全ではある。

Helm

Helmを使う方法も紹介されている。

が、今回試していないので割愛。

Helmで入れるとカスタマイズできるんかな?

設定の変更

中身としては、名前の通りnginxをロードバランサとして使ってる。

なのでwebサーバの設定をいじっている場合、ここのnginxの設定値も変更しないといけなかったりすることもある。

設定項目の名前が違う罠

例えばファイルアップロードサイズの制限。 nginxでは、こう。

client-max-body-size 5m;

でもnginx-ingress-controllerに設定するときは、こう。

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: 5m

client-max-body-sizeではなくproxy-body-size。

名前が違うので、少し面倒。

他にもいろいろ設定できる。

Annotations - NGINX Ingress Controller

カスタマイズ

上記のコマンドを見ると、githubにあるファイルをそのままapplyしてるだけのようなので、cloneするなりしてyamlを編集することで簡単にカスタマイズできる。

設定値の変更が必要な場合は、管理することも考えれば、yamlを直接編集してapplyするのが良いのではないか。