Memento memo.

Today I Learned.

AirflowでDAGが認識されなくてハマった

起こった事象

  • 以下のようにDAGの生成を別のメソッドに切り出していたらAirflowからDAGが認識されなくなった
dag_factory = DAGFactory.get_default()
dag = dag_factory.create(
    dag_id="example",
    ...,
)

原因

  • DAG file は airflowdag という文字列を両方含んでいる必要があるらしい
  • option的には dag_discovery_safe_mode

airflow.apache.org

回避策

  • やや強引だが以下のようにAirflowという文字列が含まれるようにすればよい
  • または dag_discovery_safe_mode を False にしてもよい
dag_factory = AirflowDAGFactory.get_default()
dag = dag_factory.create(
    dag_id="example",
    ...,
)

Telepresence で Kubernetes Client Library を使う

結論

Telepresenceの --mount オプションと docker volume オプションを使う。

telepresence \
  --swap-deployment <container name> \
  --mount /tmp/known \
  --docker-run --rm \
  -v=/tmp/known/var/run/secrets:/var/run/secrets \
  <local image>

背景

Kubebuilder (v2.3.1) で k8s の Operator を開発していたが、共用の GKE Cluster で動かしたくなった。

ローカルでの開発を避けたい理由は以下の通り。

  • 外部リソース・他CRDへの依存があり、ローカルでの環境再現が面倒。
  • GCP の Workload Identity 機能を利用しており、ローカルで構築したクラスタでは正常に動作しない。

課題

以下のようなエラーが出た。

ERROR    controller-runtime.client.config    unable to get kubeconfig    {"error": "could not locate a kubeconfig"}

kubeconfigが読めないらしい。

解決策

https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client/config によると以下の順に評価して、configを取得しているらしい。

  • --kubeconfig flag pointing at a file
  • KUBECONFIG environment variable pointing at a file
  • In-cluster config if running in cluster
  • $HOME/.kube/config if exists

今回はCluster内で動かそうとしているので In-cluster config を見に行かせたい。

https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration によると/var/run/secrets/kubernetes.io/serviceaccount を見に行っている。 これは実際に kubectl describe pod ... 等実行するとvolume mountされていることが確認できる。

Telepresenceを使う場合は root dir が $TELEPRESENCE_ROOT 以下に設定されるためおかしなことになる。 冒頭のコマンドで $TELEPRESENCE_ROOT/var/run/secrets:/var/run/secrets を読みに行かせることで解決。

なお、kubebuilder で webhookを有効化した場合は以下のようなエラーが出る。

ERROR setup   problem running manager {"error": "open /tmp/k8s-webhook-server/serving-certs/tls.crt: no such file or directory"}

これも同様に tls.crtディレクトリのvolume設定を変えてあげればok。以下のオプションを追加すれば良い。

-v=/tmp/known/tmp/k8s-webhook-server/serving-certs:/tmp/k8s-webhook-server/serving-certs \

終わりに

最近は専らKubebuilder職人になっている。 Kubebuilder を用いた Custom Controller 実装には以下の書籍を参考にさせて頂いている。

参考

GHRC: GitHub Repositoryの設定を宣言的に管理するツールを作った

f:id:shotat_jp:20190917215922p:plain

ghrc っていうCLIツールをGoで書いた。

github.com

機能としてはRepositoryのLabel, Protected branch, Merge options, etc. の設定をYAMLで宣言的に管理できる。

既存の設定を ghrc import して .ghrc.yaml というファイルに書き込み、YAMLをシュッと書き換えて ghrc apply する、みたいな使い方ができる。 Terraformっぽくplanコマンドも実装したので ghrc plan でdry-runも可能。

似たツールとしてはTerraformのGitHub Providerや github-labeler が近い。 しかし、Terraformだとややセットアップが面倒なのと、github-labeler は想定しているユースケースが違った。

ghrcではmerge時のオプション( mergeボタンの Squash and merge とか選ぶやつ)や、Protected branchを管理できるようにした。

チーム開発時にcommitが汚い開発者(自分はこのタイプ)が適当にmaster mergeすると、masterブランチが大変なことになってしまう(許さない)。 そういった事態を避けるため、あるいは特殊なCI/CD上の都合で Squash and merge を強制したい場合があり、これらをコード(といってもYAML)として管理したいと思った。

Repositoryの設定についてチームのルールとして合意を取るだけではなく、各リポジトリで管理し、Pull Request & CI/CDベースで変更できるようにすると開発ルールが良い感じに統一できて嬉しい。

また、Protected Branchについては設定を忘れていてmasterに誤爆pushするケースが極稀にあるので一緒に設定できるようにした。 こちらもコードレビューのルール等をコードで管理できるようになるので良いんじゃないかと思った。

というわけで気が向いたら使って下さい。

jemallocを利用したRubyのDocker Imageを作る

以下の記事で説明されているようにメモリアロケータをjemallocにすることで、Rubyのメモリ効率をよしなにできる場合があります。是非使いましょう!!!!!!

techracho.bpsinc.jp

さて、時は2019年であり、世界はコンテナに包まれました。

RubyのDocker Imageもjemallocオプションを有効にしてビルドしたものが提供されてあろうこと期待していたのですが、公式イメージとしては提供されていません。

代わりに以下のIssueが見つかりました。要するにサポートされていません。

github.com

そのため、jemallocを有効化したImageは自分で作る必要があります。

幸いにも上記Issueのコメントに作り方が書いてありました。

正解は以下です。

FROM ruby:2.6.2

# Enable jemalloc
RUN apt-get update && apt-get install libjemalloc1 && rm -rf /var/lib/apt/lists/*
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.1

ビルド時に指定する必要はないみたいですね。

FIN

ソフトウェアエンジニア4年目の作業環境(物理)

職業ソフトウェアエンジニアを始めて4年目の自分の現在の作業環境について語ります。 タイトルに「物理」と付けているのはソフトウェアとしての開発環境ではなく、椅子やキーボード等の実物質を伴った環境であることを表しています。

背景

ソフトウェアエンジニア、あるいはプログラマは性質上、長時間椅子に座りながらディスプレイを凝視しながら仕事をします。 生物学的には不自然な行いであるが故、身体に相応の負荷がかかることは想像に難くありません。

長期間の負荷の蓄積か、あるいは加齢による身体機能の低下か原因は不明ですが、同じスタイルでコードを書いていてもある日突然身体に異変が起こる事があります。 腰・肩・首を破壊するケースに大別されるかと思いますが、自分の場合は2年目くらいから肩の痛みを発症し、最近になって突発的に首の痛みも併発してしまいました。 次は腰かな?

現代医療(整形外科)の力で致命的な痛みは一時的に緩和できるものの、根本的な問題に向き合う必要があります。

そこで、本記事では身体の負荷を抑えるべく見直した自分の作業環境について簡単に紹介していきます。

PC

Mac Book Proを使っています。前提条件としての位置づけなので特に語ることはないです。

キーボード

外付けのキーボード(Magic Keyboard)を2台利用します。

Apple Magic Keyboard - USApple Magic Keyboard - US

人間工学界では有名なデュアルキーボードスタイルを採用し、右手と左手で別々のキーボードを操作します。 デュアルキーボードスタイルにすることによって人間工学的に肩が開いて良く、肩凝りが起きにくくなります。 自分は実際にデュアルキーボードを導入してから深刻な肩凝り問題からほぼ解放されました。

重要なポイントは2点あります。

一点目は外付けのキーボードを利用する点です。外付けのキーボードを利用しPC自体を遠くに配置することで自然と目線が上がります。 こうすることで首への負荷を小さくすることができます。 PC自体は適当なPCスタンド上に配置し、自分に合った高さに調節すると良い感じです。

自分の場合はPCスタンドを購入したのですが、冷静に考えると本棚の積読を引っ張り出してオライリータワーを建築してあげると土台としてそこそこ安定しますし、何よりオシャレなのでおすすめです。 本は読みましょう。

二点目はセパレートキーボードではなくデュアルキーボードを利用している点です。 セパレートキーボードはキーボードが真っ二つに別れ、キーがシャーディングされた構成のものです。 対してデュアルキーボードは通常のキーボードを水平にスケーリングしたスタイルを指します。

セパレートキーボードは左右のパーツ単体だけでは機能しない分断されたモノリスであり、柔軟性に欠けます。また、利用可能な種類も限られており、値段も高いものが多いです。 デュアルキーボード方式では左右それぞれが単体のキーボードとして動作するため冗長性が高く、キーボードの種類も自由に選ぶことができます。 万が一デュアルキーボードに飽きたら片方のキーボードはメルカリに出品可能な点も魅力的です。

椅子

自宅ではオカムラコンテッサを利用しています。

オカムラ オフィスチェア コンテッサ 可動肘 ヘッドレストタイプ 座:メッシュ ブラック CM91AB-FBH1

また、会社ではオカムラのバロンを使っています。 他の選択肢としてはアーロンチェア等が有名ですが、10万円↑くらいの高機能オフィスチェアかつ自分の体格に合ったものであれば何でも良いと思います。

個人的に腰は無傷なので今の所ランバーサポートの恩恵はあまり感じていないのですが、アームレスト・ヘッドレストは必須度が高いです。 アームレストをデスクの高さと揃え、デュアルキーボードを組み合わせ、肩・肘・手首をほぼ直角の位置関係にすると肩への負荷が大きく減らせます。 ヘッドレストも同様に首へのダメージを大きく減らすことができます。 ヘッドレストは位置固定のものが多く難易度が高いため、ネックピローのようなクッションと併用して良いポジションを見つけると良いです。

会社の椅子のデフォルト設定だとヘッドレスト・アームレストがなかったのですが、首痛発症を機にヘッドレスト・アームレスト付きのものに交換してもらい、無事に人権を得ることができました。

あまり課金するポイントではないと思っているので適当にニトリで買って組み立てました。 個人的に気をつけるべき点は以下だと考えています。

  • 幅: 利用する椅子が余裕で入ること。ディスプレイを複数台おくなら画面幅x枚数分。
  • 奥行き: ディスプレイを置いても近すぎない程度に。
  • 高さ: 適当(椅子やフットレストでチューニング可能)。
  • 足元: 後方に板がないタイプ。板があると足が伸ばせないのでつらい。

フットレスト

上述の通り、机の高さのチューニングとして利用します。何でも良いと思います。

サンワサプライ エルゴノミクスフットレスト MR-FR1

フロアマット

ないとフローリングが大変なことになります。

サンコー ズレない チェアマット おくだけ吸着 デスク 床保護マット 90×120cm ダークグレー

ディスプレイ

目は意外と丈夫なので適当なものを使っています。念の為作業中はブルーライトカットのメガネを装備しています。

終わりに

健康面には気をつけましょう。 作業環境の改善以外にも食生活・睡眠を改善したり、適度な運動を取り入れて体調を管理することが大切だと思います。

2018年に読んで良かった技術書・ビジネス書TOP3

今年は読んだ中で特に良かった本を3冊紹介します。

Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems

今年読んだ中ではこれがダントツでした。通称DDIAと呼ばれている本です。 ACID・BASE・CAP・RDBMS・KVS・バッチ処理・ストリーム処理・分散システム...等、データ(フロー)設計周りについて体系的かつ詳細に学ぶことができます。 また、パフォーマンス・スケーラビリティ・整合性などのトレードオフについても多く語られているので、実際にデータを扱うシステム設計の意思決定に非常に役立ちます。

かなり物量があって読み通すのは非常に骨が折れますが、各章がある程度独立しているので辞書っぽく使うこともできます。

ビジネス書

エンジニアリング組織論への招待 ~不確実性に向き合う思考と組織のリファクタリング

この本を読んでから「不確実性」というものを強く意識できるようになりました。 アジャイルで迷子になった時に読むと良いです。

起業の科学

起業の科学

起業の科学

社内外でいくつか新規プロジェクトに関わることが増えたので読みました。 リーンスタートアップ系の本と合わせて読んでおくと新規事業で大きく道を踏み外すことはないと思います。

GatsbyJSをGAEにデプロイする

GatsbyJSGoogle App Engine にデプロイする方法の備忘です。

app.yamlcloudbuild.yaml を設定してCloud Buildのコマンドを叩くことでデプロイします。

Google App Engine の設定

まずは app.yaml の設定です。

基本は Hosting a static website on Google App Engine  |  App Engine standard environment for PHP  |  Google Cloud を参考にします。

ルーティングの都合上、拡張子を指定しない場合のリクエストは index.htmlマッピングさせます。

runtime: php55
api_version: 1

handlers:
# file with extensions (e.g. .html)
- url: /(.*\..*)
  static_files: public/\1
  upload: public/(.*)

- url: /(.*)/
  static_files: public/\1/index.html
  upload: public/(.*)/index.html

- url: /
  static_files: public/index.html
  upload: public/index.html

- url: /(.*)
  static_files: public/\1/index.html
  upload: public/(.*)/index.html

skip_files:
  - node_modules/
  - src/
  - \.cache/
  - package\.json
  - yarn\.lock

Google Cloud Build の設定

cloudbuild.yaml の設定です。

steps:
- name: 'gcr.io/cloud-builders/yarn'
  args: ['install']
- name: 'gcr.io/cloud-builders/yarn'
  args: ['run', 'build']
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['app', 'deploy']

権限の設定

Google Cloud Build はデフォルトではAppEngineにデプロイする権限を持たないのでいい感じに設定してあげる必要があります。

appengine.serviceAdminappengine.deployer のroleを <project_number>@cloudbuild.gserviceaccount.com に設定してあげます。

Terraformを使った例では以下のようになります。

resource "google_project_iam_binding" "appengine-service-admin" {
  project = "${var.gcp_project}"
  role    = "roles/appengine.serviceAdmin"

  members = [
    "serviceAccount:${var.gcp_project_number}@cloudbuild.gserviceaccount.com",
  ]
}

resource "google_project_iam_binding" "appengine-deployer" {
  project = "${var.gcp_project}"
  role    = "roles/appengine.deployer"

  members = [
    "serviceAccount:${var.gcp_project_number}@cloudbuild.gserviceaccount.com",
  ]
}

Deploy

gcloudコマンドを叩くことでデプロイが完了します。

$ gcloud builds submit . --config=cloudbuild.yaml

WTFormsでPythonの予約語をFieldとして扱いたい場合の対処法

こういうことをしたかった。

class MyForm(FlaskForm):
    from = DateField(format='%Y%m%d')
    to = DateField(format='%Y%m%d')

が、これは syntax error になる。理由は fromPython予約語であるため。 from を辞書のkeyのstringとして扱えれば回避できるのでここは type を使ってclassをメタ的に生成する。

MyForm = type('MyForm', (FlaskForm,), {
    'from': DateField(format='%Y%m%d'),
    'to': DateField(format='%Y%m%d'),
})

値を取り出す際も dot アクセスするとsyntax errorになるので辞書として取得する。

f['from']

面倒なのでそもそもの form に予約語になりそうなフィールドを含めないようにしましょう。

Docker の multi-stage build で golang の 軽量 image を作る

f:id:shotat_jp:20170805214939p:plain

Multi-Stage Build

docs.docker.com

Docker の 17.05 (ce) から multi-stage build 機能が追加されました。 multi-stage build とは、ベースイメージを複数利用し、多段階で build を行う機能です。

利用シーンはビルド環境とランタイム環境の分離が挙げられます。 ビルド環境とランタイム環境を分けるとランタイムがネイティブである Go のような言語では Docker image を劇的に小さくすることができます。

golang の ベースイメージは 数百 MB ありますが、alpine のような 軽量 Linux イメージ単体では 数 MB のため、効果としては 90 % 以上のダイエットに繋がります。

Docker image サイズを小さくするメリットや、一般的なプラクティスについてはページ下部のリンクが非常に参考になります。

Dockerfile の作成

multi-stage build を使って Go のコンテナアプリケーションを作成するテンプレートです。

github.com

Dockerfile の中身はこんな感じになります。

FROM golang:latest as builder

ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
WORKDIR /go/src/github.com/shotat/light-golang-container-template
COPY . .
RUN make

# runtime image
FROM alpine
RUN apk add --no-cache ca-certificates
COPY --from=builder /go/src/github.com/shotat/light-golang-container-template/app /app
EXPOSE 8080
ENTRYPOINT ["/app"]

Multi-stage build なので FROM が二回登場しています。 一行目の FROM では as xxx としてビルド環境に名前をつけています。 as で指定した名前は COPY --from=builder /go/src/github.com/shotat/light-golang-container-template/app /app で参照しています。

builder で GOOS GOARCH CGO_ENABLED環境変数を指定し、 alpine 用にビルドを行います。 amd64の場合 CGO_ENABLED=0 を指定しないとビルド環境依存が発生してしまうため指定は必須です。

RUN apk add --no-cache ca-certificatesSSL/TLS 通信を行うために必要で、ほとんどアプリケーションで利用するはずです。 通信の必要がなければ alpine ではなく busyboxscratch を使ってより小さなイメージが作れます。

Build する

ビルド自体は make だけで簡潔するようにしています。 Makefile の中身はこんな感じです。パッケージ管理には dep を使います。

# 適当に設定を書く
# NAME     := hello
# VERSION  := v1
# ...

all: setup build

setup:
    go get -u github.com/golang/dep/cmd/dep
    dep ensure

build:
    go build -o app
$ docker build . -t app:dev

アプリケーションの中身です。 適当に外部ライブラリを入れたかったので logger として logrus を使っています。

package main

import (
    log "github.com/sirupsen/logrus"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    log.Info("Hello")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

実際にビルドしてみます。

$ docker build . -t app:dev

Run !

動かします。

$ docker run --rm -p 8080:8080 app:dev

$ curl localhost:8080
# container output
time="2017-08-05T12:43:25Z" level=info msg=Hello

無事いい感じに動作しています。 デプロイサイクル短縮の鼓動を感じました。

参考書籍

Docker 実践ガイド (impress top gear)

Docker 実践ガイド (impress top gear)

WEB+DB PRESS Vol.99

WEB+DB PRESS Vol.99

  • 作者: ?橋健一,谷口禎英,井本大登,山崎勝平,大和田純,内村元樹,坂東昌哉,平田敏之,牧大輔,板敷康洋,大?浩崇,穴井宏幸,原口宗悟,久田真寛,ふしはらかん,のざきひろふみ,うらがみ,ひげぽん,池田拓司,はまちや2,竹原,片田雄樹,渋江一晃,WEB+DB PRESS編集部編
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/06/24
  • メディア: 大型本
  • この商品を含むブログを見る

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

参考資料