nabeo がピーしているブログ (仮)

どーも、nabeop です

最近は Vagrant では ansible_local でプロビジョニングしている

vagrant で手元の VirtualBox に検証環境を作る場合は Ansible Local Provisioner を使っています。

昔は Ansible Provisioner を使っていたけど、手元環境を汚さずに仮想ホスト上で ansible を直接実行できることにメリットを感じています。

Vagrantfile はこんな感じで準備しておきます

Vagrant.configure(2) do |config|
  config.vm.synced_folder './ansible', '/home/vagrant/ansible',
                          create: true, owner: 'vagrant', group: 'vagrant'

  config.vm.define 'vm01' do |node|
    node.vm.provider 'virtualbox' do |vb|
      vb.cpus = 1
      vb.memory = 512
      vb.gui = false
    end
    node.vm.hostname = 'vm01'
    node.vm.box = 'ubuntu/cosmic64'
    node.vm.provision :ansible_local do |ansible|
      ansible.compatibility_mode = '2.0'
      ansible.playbook = '/home/vagrant/ansible/all.yml'
      ansible.inventory_path = '/home/vagrant/ansible/inventories/hosts'
      ansible.limit = 'clients'
    end
  end

Vagrantfile があるディクトリに ansible ディレクトリを作って、その下に ansible playbook を配置しています。で、asible ディレクトリは config.vm.synced_folder './ansible', '/home/vagrant/ansible'VM 上の /home/vagrant/ansible として配置しています。

Ansible Local Provisioner を使うときの注意点としては ansible は vm 上で動くため、inventory file は自分自身をプロビジョニング対象にする必要があります。vagrant-hosts プラグインなどを使って hosts ファイルを管理してもいいのですが、僕は inventory file を ansible の role ごとに分けて管理しています。したがって、 ansible ディレクトリの中身はこんな感じになっています

ansible
├── all.yml <--- ansible.playbook で指定している
├── clients.yml
|
├── inventories
│   └── clients <--- ansible.inventory_path で指定している
|
└── roles
    ├── clients
    └── role_template

また、inventory file は ansible をローカル環境を対象に実行する必要があるので、以下のように書いています。

[clients]
127.0.0.1 ansible_connection=local

未来大×企業エンジニア 春の LT 大会で LT してきました #springlt_hakodate

未来大×企業エンジニア 春のLT大会に登壇させてもらえる機会があったので、函館の元気な若者に会いに行ってきました。

お題は「これだけは伝えておきたい技術」ということで、若者に刺さって10分の発表枠に収まりそうな内容ということで、かなり悩みました。そんな悩みを抱えていたある日、社内の若者と雑談していると Docker イメージを小さくするテクニックだと手軽に話せて、かつ、若者に刺さりそう!!ということに気づいたので、こんな資料を作って LT してきました。

大きな講義室にびっしりと聴講者が入ってとても大盛況なかで、「なるほど、最近の学生さんてこういうことに興味あるのか」と発見させられたりととても有意義なイベントでした。また、LT 大会後の懇親会では、自分の学生時代にこんなイベントがあったらもう少し違った学生生活してたかもなーと思いながら話を聞いていました。イベント運営スタッフの皆さん、お疲れ様でした、とても楽しいイベントでした。

また、Tiwtter のタイムラインで指摘されている方もいましたが、DockerCon19 で Dockerfile Best Practices というセッションでイメージサイズの圧縮とは異なる切り口でも触れられているのでより深く知りたい方はオススメです。

あと、スライドの最後にも告知していますが、はてなサマーインターン 2019 の募集が始まっています。興味のある方は応募してください!待ってます!!!

pass で複数のレポジトリを管理する

この記事は仕事用のマシンで pass のレポジトリを統合した時に実施した作業のメモです。

仕事とプライベートの両方でパスワードなど秘密情報の管理に pass を使っています。ただし、仕事の pass は会社のレポジトリ、プライベートの pass はプライベートなレポジトリと管理しているレポジトリが分断していて、仕事で使っているマシンでプライベートなアカウントにログインするときに若干不便を感じていました。しかし、pass は .gpg-id というファイルに複数の GPG 鍵の gpg-id を列挙することで、複数人での秘密情報の共有をサポートしており、レポジトリは git の機能が全て使えます。そこで git の subtree を使って複数のレポジトリを統合して、かつ、仕事用の GPG 鍵と私用の GPG 鍵の両方を使い分けて秘密情報を管理することにしました。

続きを読む

株式会社はてなに入社して1年と1ヶ月が経ちました

早いもので以下のエントリから1年経ちました。

入社した 3/1 に入社して1年経ちましたエントリを書こうと思ったけど、エイプリールフールに入社エントリを書くのは面白かったので、4/1まで待ちました。

ということで、この1年(と1ヶ月)の間、何をしていたかの振り返りです。

続きを読む

手元で Dockerfile から docker イメージを作る時に使っている Makefile の書き方

手元環境で Dockerfile から docker イメージを作る時はいちいち docker build コマンドを直接実行するのはダルいのでラッパースクリプトなどで実行するようにしています。

管理している Dockerfile や docker イメージの種類が多くなってくると素朴なラッパースクリプトでは作成したい docker イメージを狙い撃ちで生成するにはラッパースクリプトで一工夫が必要になります。これだとラッパースクリプトが必要以上に多機能になってしまい、作業の本質以外に気を取られてしまいます。

古くから使われている GNU make を使えば、Dockefile から docker イメージを作成するルールをいい感じに作れたので、作り方のメモを書いておきます。

まず、Makefile のルールの書き方は

生成物(ターゲット名): 生成元のリスト

といった感じに書いていきます。ターゲット名で指定しているファイルと生成元のリストで列挙されているファイルのタイムスタンプを比較して、生成物よりも新しい生成元のファイルがあればルールが実行されます。

ただし、docker build で生成されるのは docker イメージなので、ファイルのタイムスタンプを取ることが GNU make の世界ではできません。そこで docker build の後に docker inspect コンテナイメージ | jq -r .[].RepoTags[] > .build といったように生成したコンテナイメージの情報を書いたファイルを GNU make の世界での生成物とするうにします。以下のような感じです。

.build: Dockerfile
  docker build -t hoge:latest -f Dockerfile .
  docker inspect hoge:latest | jq -r .[].RepoTags[] > .build

生成物である .build ファイルに記述する内容は生成した docker イメージが特定できればなんでも良いと思いますが、 make clean などで消す時の利便性を考えて docker イメージのレポジトリとタグ名を入れています。clean ターゲットは以下のように書いています

clean:
  cat .build | xargs -I {} docker rmi {}

また、docker で multi stage build を使用している場合はターゲットの生成元にベースになる Docker イメージを生成した時の .build ファイルを指定しておけば依存関係に組み込まれて便利です。

.build-base: Dockerfile.base
  docker build -t base:latest -f Dockerfile.base .
  docker inspect base:latest | jq -r .[].RepoTags[] > .build-base

.build: Dockerfile .build-base
  docker build -t hoge:latest -f Dockerfile .
  docker inspect hoge:latest | jq -r .[].RepoTags[] > .build

さらに Dockerfile 内で ADD 命令や COPY 命令などでコンテナイメージにファイルを配置しておきたい場合は Dockefile を置いているディレクトリに roo-hogehoge ディレクトリをコンテナ内の / として見立てて配置しておくと Makefile のターゲットでは $(shell find root-hogehoge -type f) として依存関係に組み込んでいます。

具体的には nginx を nginx-build で作るときに -c で指定するスクリプトをコンテナ内の /tmp/nginx-build/nginx_configure.s として使用するときは以下のように配置しています。

.
├── Dockerfile
├── Makefile
└── root
    └── tmp
        └── nginx-build
            └── nginx_configure.sh

このときの Makefile は以下のようにターゲットを指定しています。

.build: Dockerfile $(shell find root -type f)
  docker build -t nginx:latest -f Dockerfile .
  docker inspect nginx:latest | jq -r .[].RepoTags[] > .build

実際に使っている Makefile は以下のようになっています

MAKEFLAGS = -j 4
DOCKER_BUILD_ARGS := --no-cache --squash --rm
IMAGE_NAME := varnish
USER_NAME := $(shell whoami)
BUILD_FILES = .build-4.1 .build-5.2 .build-6.2 .build-6.0lts

.PHONY: build
build: $(BUILD_FILES)

.build-4.1: Dockerfile.jessie
  docker build $(DOCKER_BUILD_ARGS) --build-arg VARNISH_VERSION=41 -t '$(USER_NAME)/$(IMAGE_NAME):jessie-4.1' -f Dockerfile.jessie .
  docker inspect '$(USER_NAME)/$(IMAGE_NAME):jessie-4.1' | jq -r '.[].RepoTags[]' > .build-4.1

.build-5.2: Dockerfile.jessie
  docker build $(DOCKER_BUILD_ARGS) --build-arg VARNISH_VERSION=52 -t '$(USER_NAME)/$(IMAGE_NAME):jessie-5.2' -f Dockerfile.jessie .
  docker inspect '$(USER_NAME)/$(IMAGE_NAME):jessie-5.2' | jq -r '.[].RepoTags[]' > .build-5.2

.build-6.2: Dockerfile.stretch
  docker build $(DOCKER_BUILD_ARGS) --build-arg VARNISH_VERSION=62 -t '$(USER_NAME)/$(IMAGE_NAME):stretch-6.2' -f Dockerfile.stretch .
  docker inspect '$(USER_NAME)/$(IMAGE_NAME):stretch-6.2' | jq -r '.[].RepoTags[]' > .build-6.2

.build-6.0lts: Dockerfile.stretch
  docker build $(DOCKER_BUILD_ARGS) --build-arg VARNISH_VERSION=60lts -t '$(USER_NAME)/$(IMAGE_NAME):stretch-6.0lts' -f Dockerfile.stretch .
  docker inspect '$(USER_NAME)/$(IMAGE_NAME):stretch-6.0lts' | jq -r '.[].RepoTags[]' > .build-6.0lts

.PHONY: clean
clean: $(BUILD_FILES)
  cat $^ | xargs -I {} docker rmi {}
  rm -f $^

BUILD_FILES に対象となる .build 相当のファイルを列挙して、build ターゲットや clean ターゲットで指定しておくことで -j オプションで並列化できるので便利です。

なんらかの CI/CD パイプラインを構築している場合はこのままでは使えないけど、手元でテストする分にはこんな感じで割と便利に使えています。

コードの書き納めと書き初めで mackerel plugin を作った

2018年から2019年の年末年始は 1/4(金) を有給にして、12/29 から 1/6 までガッツリと休みを確保したので、チマチマとコードを書いていた。

Linuxproc/net 以下に出ている情報をなんとかして、mackerel で表示できたら便利だよなーと思っていたんだけど、ちょうど時間がガッツリとれたので、年末年始の書き納めと書き初めという体で以下の3つのプラグインを作ってみた。

github.com github.com github.com

mackerel-plugin-proc-net-ip_vs_stats_percpu

2018 年のコードの書き納めとして作りました。

/proc/net/ip_vs_stats_percpu を情報源に CPU コアごとに処理したパケットの統計情報が取れる。LVS-HOWTO の 33. LVS: Monitoring では /proc/net/ip_vs_stats でいい感じな情報が取れる、ということだったけど、僕が確認した kernel 3.6 系だと real server ごとの統計情報が表示されていなかった。そのかわり /proc/net/ip_vs_stats_percpu では CPU コアごとに処理したパケット数とバイト数が取れるようだったので、こっちを使うことにした。 /proc/net/ip_vs_stats_percpulinux/ip_vs_ctl.c at master · torvalds/linux · GitHub を見る限り、意図した情報が取れそうな気配です。

mackerel-plugin-proc-net-ip_vs_stats_percpu では引数に -cpus で CPU コア数を明示できるようにしていますが、内部で runtime.NumCPU() で CPU コア数を取得するようにもしています。なんでこんなことをしているか、というと仮想化されたホストで LVS を使っていると /proc/net/ip_vs_stats_percpu で表示される CPU コアが実際のコア数よりも多くなっているケースがあるからです。runtime.NumCPU() に任せれば良えやんという話もあるかもしれませんが、なんとなくオプションで渡してあげると便利かも、と思ってつけました。

mackerel-plugin-proc-net-arp

2019 年のコードの書き初めとして作りました。

/proc/net/arp を情報源にホストの ARP テーブルのサイズが取れます。本当は Flags ごとの統計も取れたら便利かなーと思ったんですけど、とりあえずテーブルサイズだけでも取っておけば、ARP テーブル溢れとかに気付けるかと思って、最小限の機能だけにしました。

mackerel-plugin-proc-net-ip_vs

mackerel-plugin-proc-net-ip_vs_stats_percpu を作っているときに LVS-HOWTO や linux/net/netfilter/ipvs at master · torvalds/linux · GitHub を眺めていて、 ipvsadm -nL 相当の情報を /proc/net/ip_vs から取得できそうだったので作りました。

ipvsadm -nL の出力を情報源にする、というアプローチもあると思いますが、個人的には ipvsadm という cli ツールに頼らずに procfs から情報が取れるなら、procfs を利用したほうが依存関係がスッキリしていいんじゃないかと思っています。

というわけで

今年も健やかにコードが書けることを祈念して、書き初めをしてました。今年も良い年でありますように。