試行錯誤のおと

日々の試行錯誤した結果です。失敗することが多い記録、それだけでっす!

Conky で Github Grass Graph を rsvg で描画する

Linux Desktop を使っていない人や興味のない人にはほとんど旨味のない記事です。

毎日コードを書こうと思っていても、モチベーションを維持するのは難しいことが多いなと最近思ってる。 Github の Grass Graph を必然的に見るようにしたかったので、 conky に表示させてみた。

簡単に書くとシェルスクリプトと imlib で画像表示するだけで終わる。

Github の Grass Graph を取得して png に変換するスクリプト

#!/bin/sh
set -eu

ID="kizkoh"
curl https://github.com/${ID} | awk '/<svg.+class="js-calendar-graph-svg"/,/svg>/' > grass.svg
convert grass.svg grass.png

.conkyrc のサンプル

own_window_type conky
${image ./grass.png -p 0,0 -s 470x120 -f 3600}

ただ、ハックに物足りなさを感じたので、ちょっと Lua で頑張ってみた。

使用しているディストリで Conky の Lua rsvg モジュールを有効にする。 Lua rsvg は /usr/lib/conky/librsvg.so に入るので有効かどうかはここを見るとわかるみたい。 Conky のモジュールとしてインストールされるけど、 librsvg に依存しているだけなので普通に Lua からも使える。 Gentoo だと app-admin/conkylua-rsvg USE フラグを有効にしてビルドすると、 Lua rsvg が入る。

Lua rsvg はドキュメントがないので、ソースコードを読んで関数の役割を把握する。 といっても toluapp でバインディングが作成されているだけなので、関数を参照して rsvg 本家のドキュメントと照らし合わせればよいので簡単。 (最初は Lua 知識ゼロ だったので readelf でシンボルを読んでいた><)

それで書いたコードは以下のリンク。Cairo で描画しているので動作には Cairo モジュールも必要。

手元の Conky はこんな感じで表示されている。

f:id:kizkoh:20170620215925p:plain

出来上がったコードは Github に置いてある。
dotfiles/github_grass.lua at master · kizkoh/dotfiles · GitHub

いつも目がいくところ (Google Calendar とかの予定を表示させたりしている) にあるので、コードを書かないとなと気づきを与えてくれたり、芝生を育てるのが目的になってコードを書くモチベーションを維持できるようになったのでよい。 (最近うまくいってなくて説得力がないのが玉に瑕)

一つ欠点があって .conkyrc を書き換えたり、 SIGUSR1 をプロセスに送って設定をリロードさせると以下のようなエラーが発生する。

conky: received SIGHUP or SIGUSR1. reloading the config file.
conky: Syntax error (/home/kizkoh/.conkyrc:2: '=' expected near 'conky') while reading config file. 
conky: Assuming it's in old syntax and attempting conversion.
(process:15389): GLib-GObject-WARNING **: cannot register existing type 'RsvgHandle'

(process:15389): GLib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed

こうなると SIGKILL を送るしかなくてすごく面倒。はじめは Lua の書き方が悪いのかと思っていて調べていたけれど、それほど困ってはいなかった。けれどもどうしてこのようなエラーが発生するのかは気になっていたので時間が空いたときに調べていくと Conky の問題だと分かるようになってきた。

話題がすっかり変わりそうなのと深入りして長くなりそうなので、この記事はひとまずこのへんにして問題の調査の過程は次回の記事に書くとします。

私的 CloudFormation ベストプラクティス

最近 CloudFormation を触っていて、よくある初期構築のベストプラクティスについて意見がほしいので自分の考える CloudFormation の設計や使い方についての考えを書いた。

CloudFormation のメリット

CloudFormation を利用するためメリットはリソースの参照を簡単に記述できることと、べき等性の保証だと思っている。

以前、 EC2 インスタンスのプロビジョニングを Ansible で書いたことがある。

べき等性を確保したく、 サブネットの作成、 EBS のマウント、アンマウント、 EIP の付け替えなどの変更操作を Ansible だけで操作したい + その後のプロビジョニングの操作も Ansible にお任せしたいということを考えたときに、 Ansible の YAML ではなく以下のような Python のコードを書いた。

# ansible_playbook は data に playbook のデータを取ってタスクを実行する関数
data = [
    {
        'connection': 'local',
        'tasks': [
            {'ec2_vpc_subnet': {
                'vpc_id': 'vpc-xxxxxxxx',
                'state': 'present',
                'az': 'ap-northeast-1a',
                'cidr': '10.0.0.0/24',
                'resource_tags': {
                    'Name': 'jessie'
                }
            }, 'name': 'create subnet'}],
        'hosts': 'localhost'
    }
]
ansible_playbook(data=data)

subnets = get_all_subnets(filters={"vpcId": vpc_id,
                                     "tag:Name": "jessie"})
subnet_id = subnets[0].id

data = [
    {
        'connection': 'local',
        'tasks': [
            {
                'ec2':
                {
                    'assign_public_ip': 'yes',
                    'count': 1,
                    'ebs_optimized': False,
                    'image': 'ami-dbc0bcbc',
                    'instance_type': "t2.micro",
                    'key_name': 'kizkoh',
                    'monitoring': 'no',
                    'placement_group': False,
                    'private_ip': '10.0.0.4',
                    'tenancy': 'default',
                    'vpc_subnet_id': subnet_id,
                    'wait': 'yes'
                },
                'name': 'run jessie'
            }
        ],
        'hosts': 'localhost'
    }
]

ansible_playbook(data=data)

こんなコードを書いたのは Ansible の Playbook では YAML で EC2 API の実行の応答結果を扱えないためだ。 たとえば、インスタンスを作成した時に得られるインスタンス ID を引数に他の API を実行することができない。

CloudFormation はテンプレートと呼ばれるファイルに記述することでそれぞれのマネージドサービスのプロパティを記述することができる。 マネージドサービスのプロパティでは他のマネージドサービスのプロパティを参照できる。 先の Python のコードを CloudFormation のテンプレートに書き換えると以下のようになる。

AWSTemplateFormatVersion: 2010-09-09
Description: jessie stack
Resources:
  Subnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.0.0/24
      Tags:
        - Key: Name
          Value: jessie
      VpcId:
        Ref: vpc-xxxxxxxx
  Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      EbsOptimized: False
      ImageId: ami-dbc0bcbc
      InstanceType: t2.micro
      KeyName: kizkoh
      Monitoring: False
      NetworkInterfaces: 
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: 
            Ref: !Ref Subnet
      Tenancy: default

依存関係ごとにスタックを分けて、記述するのが個人的なベストプラクティスだと思っている。 特に依存関係がないものはマネージドサービスごとにスタックを分けていて、以下のようなフォルダ構成を取っている。

$ tree
.
├── cloudtrail
│   ├── parameters
│   │   ├── production.json
│   │   └── staging.json
│   └── template.yaml
├── iam
│   └── template.yaml
├── README.md
...

ステージング環境と本番環境で一部のパラメタを変えて API の引数を変えているのでオペレーションを環境間でそのまま適用できるのも CloudFormation の魅力だと思っている。 スタックの動作について環境間の差分としてパラメータ以外は変えることはできず If 文がサポートされていないのもそういった用途のマッチしていて、環境間で整合性をとるための方法としてユースケースを限定してくれているのがよいなという印象を持った。

リソースの参照以外にも最新の API への対応がすぐにされるあたりは公式ならではだと思う。 Terraform などのサードパーティ製ツールを使うとどうしても API への対応が遅れることがあるので、公式がサポートしている恩恵も大きい。 CloudFormer を使えば YAML を書かなくてもテンプレート化できるので、深くドキュメントを読み込む必要がないところもよいと感じた。

テンプレートフォーマットの選択

テンプレートフォーマットに YAML がサポートされるまではテンプレートは JSON で記述するか、サードパーティ製モジュールの SparkleFormation, kumogata, CoffeeFormation 等を用いて独自の DSL で記述するといった選択肢があった。 これらはナンセンスだと思っていて、 JSON を使って記述するとフォーマットとしての可読性はそこそこ高いのだけどコメントが書けない致命的な問題、サードパーティ製モジュールの DSL を利用するとサードパーティモジュールへの依存や、公式ドキュメントのサンプルを参照しにくい問題につまずいた。 YAML がサポートされるようになってからはこれらの問題が解決されて、 YAML が選択肢として良いと思っている。

しかし、 YAML を採用するとなると新たに考慮すべき点がある。例えば、ポリシードキュメントの記法を考慮しないといけない。 IAM グループを作成するときのテンプレートを、すべて YAML で記述すると以下のようになる。

...
  Groupadmin:
    Type: "AWS::IAM::Group"
    Properties:
      GroupName: admin
      Policies:
        - PolicyName: AllAllow
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action: "*"
                Resource: "*"
...

しかし、ポリシードキュメント部分は以下のようにして JSON (文字列扱い) で書くことができる。

...
  Groupadmin:
    Type: "AWS::IAM::Group"
    Properties:
      GroupName: admin
      Policies:
        - PolicyName: AllAllow
          PolicyDocument: |
            {
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": "*",
                  "Resource": "*"
                }
              ]
            }
...

どちらが見やすいかという議論ではなくて、ポリシードキュメントは AWS のシステム上では JSON で記述される。 そのため、ポリシードキュメントのドキュメントを見ても、 JSON のサンプルはあるものの YAML のサンプルはない。 また、変更したい時にマネジメントコンソール上で validate して確認した後にテンプレートに貼り付けることで Stack 更新時のエラーにハマることなく更新できる。 なので、自分はポリシードキュメント部分に関しては JSON で記述するようにしている。

どこまでを CloudFormation で扱うか

今回、 CloudFormation のテンプレートを書いていて困ったのは一部の API が扱えないこと。 例えば、 EC2 の KeyPair の登録や CloudFormation 以外で作成したリソースのプロパティの取得ができない。

EC2 の KeyPair の登録は AWS アカウントごとあるいはリージョンごとに繰り返す作業なので、 CloudFormation にまとめて定型化したい。 特に CloudFormation が API をサポートしてくれない理由はないと思うだけど、 CloudFormation が対応しない API に対しては無理に CloudFormation で対応するのではなく別のツールを使うのが良いと思う。 外部のリソースに関してはパラメータに埋め込むのがよいと思う。 テンプレートを動的に生成するような対応を取ると、リソースの依存解決が CloudFormation 外に依存することや環境ごとにテンプレートが異なる可能性 (この環境ではリソースを作成するが別の環境では作成しない等の If 文のような場合分け) が発生する。 テンプレートの生成次第でなにもかもできるようになってしまうと、結局のところ冒頭で書いた Ansible の Python コードと何ら変わりはないので、CloudFormation は CloudFormation がサポートする範囲内でオペレーションを収めたい。 AWS に CloudFormation の API のサポートを要望を出していって、それまでのつなぎとして使うには動的テンプレート生成はありかなと思うけれども、避けられるようなら避けたい課題かなと思う。

まとめ

CloudFormation の個人的に思っているベストプラクティス知見について書いた。もっとこうしたほうがいいとか、知見がほしい。

株式会社はてなにジョインしました!

1 月末付けで前職を退職し、 2 月から株式会社はてなで Web オペレーションエンジニアとして働いています。

転職の理由

前職も今と同じようにインフラレイヤの設計、構築、運用を担当していて、業務でやりたいことや技術的に取り組みたいことは自由にやらせてもらっていたり、上司や周りにいる方々も高い技術力を持っていて、かつ運用に対する改善等の考え方も技術的に前向きで働きやすく、特に大きな不満があったわけではありません。

一方で昨年の秋には新卒で入社して 3 年弱が経過しようとしていて、自身のキャリアについて振り返ると転職を考え始める時期かと考え始めていました。 前職は 1 社目ということもあり構築するサービスの内容やそれに応じて要求されているシステム構成の考え方、開発のスピード感、新しい技術に対するモチベーションが鈍ってしまうことが怖いという思いや、自社以外のそれらの様子をまだエンジニアとして若い間に見ておきたいという思いがありました。それと同時に、昨年から少しずつ自身のアウトプットを発信していく過程で、ブログや勉強会や OSS に成果物をアウトプットしている同年代の技術に明るいエンジニアと一緒に働きたいという希望もありました。

はてなを考えたきっかけ

はてなを転職先として考えるに至ったのは今までお会いして話を聞いた方々の影響があります。

学生時代の出来事で、今ではもうはてなにはおられませんが、大学に来られた際に id:ninjinkun さんや id:htomine さんとはてなの技術に対する考え方、プロダクトに対するモチベーションといったカルチャーについて話したり、勉強会では id:r_kurain さんとフロントエンドの技術についての苦労や新しい取り組みについて楽しく話したのを今でも覚えています。

話を聞く中でカルチャーや技術に対するモチベーションに憧れ、はてなで働きたいと思っていたのですが、当時はアプリケーションエンジニアを目指していて、言語は Perl より Python が好きでした。 そういったところもあって、前職の会社で働くに至ったということもあるのですが、社会に出てインフラレイヤのエンジニアとして働き始めると言語や既存の技術に縛られることなく働くことが求められ、言語に対する苦手意識は薄まっていきました。

転機があったのは昨年で、勉強会や技術系のイベントに参加する中で id:y_uuki さんや id:Songmu さんと今のはてなのカルチャーや技術に対する思いを聞いて、以前に思い描いていたはてなと変わらないなと確信できたこと、そのタイミングで自身のキャリアと成長について考え始めたことが今回の転職につながっています。

これから

はてなで働き始めて 1 ヶ月が経ち、システムのポリシーや構成、そしてカルチャーにもなじんできたところで自分の転職当時の気持ちを整理してみました。 今までお会いしたいろいろな方の影響を受けて今はてなで働いているんだなと考えるとなかなか感慨深いです。

今ではミドルウェアの構成やネットワークの運用ポリシーについて技術に尖ったメンバと真剣に議論ができ、一人のエンジニアとしてはてなで働くことができて嬉しく思っています。

つらつらと書きましたがこれからもよろしくお願いします!