less コマンドの基本的な使い方と知っておくと便利な機能

こちらのブログは個人ブログと同じ内容です

www.ritolab.com

LinuxCLI から操作している時に、ログや CSV などのテキストファイルなどの内容を確認するためのコマンドがいくつかありますが、less コマンドがなかなか使い勝手が良いので紹介します。

less とは

less は Unix 系のコマンドで、テキストファイルの内容を閲覧するためのコマンドです。

www.greenwoodsoftware.com

  • less は more の機能を拡張したコマンド(見た感じは一緒)
  • tail より使い勝手が良い(開いてから検索できる など)
  • エディタではないので、うっかり中身を変更してしまうことがない
  • メモリほとんど消費しない(実行時にファイル内容をすべて読み込まないため)

基本的な使い方

less コマンドはテキストファイルの内容を閲覧するためのコマンドなので「開く」そして「読み進める」これだけです。 とてもシンプルなので簡単です。

ファイルを開く

less <FILE_PATH>

ファイルを開いた後は、以下のキーで操作できます。

  • j 一行進む
  • k 一行戻る
  • d 半ページ進む
  • u 半ページ戻る
  • f 1ページ進む
  • b 1ページ戻る
  • g 先頭行へ飛ぶ
  • G 最終行へ飛ぶ
  • <number>g 入力した行(<number>行目)へ飛ぶ
  • q 終了する

検索

ファイルの中身が大量にある時など、1行ずつ見ていくと埒が明かないので探したい部分を検索にかけることができます。

ファイルを開いた後で、検索をかけると、ヒットした最初の行を先頭としてページが表示されます。

検索ワードには正規表現も記述可能です。

現在のページ以降を検索
/<search_word>
現在のページ以前を検索
?<search_word>

検索後は以下のキーでも操作できるようになります。

  • n 現在地の次にヒットした行へ進む
  • N 現在地の前にヒットした行へ戻る

検索にヒットしたものだけを表示させる

検索にヒットしたものだけを表示させることもできます。 & キーを入力後、検索ワードを入力します。

&<search_word>

表示を元に戻すには再度 & キーを入力し、検索ワードは入力せずに [ENTER] 押下で戻ります。

※ ファイルの大きさによってその分 CPU に負荷がかかるので注意

行番号を表示する

ファイルを開いた後で -N を入力すると、行番号を表示できます。

行番号表示前

行番号表示前

行番号表示後

行番号表示後

行番号を非表示にする時も同じです。もう一度 -N を入力すると非表示になります。

※ ファイルの大きさによってその分 CPU に負荷がかかるので注意

現在地情報を表示する

例えば大量の行数のあるファイルを上からなめている時に、今どの辺にいるのかがわからなくなりますが、 -M を入力すると、以下の情報が表示されるようになり、現在地が気になる場合には役に立ちます。

  • 現在表示されているものが何行目から何行目までか
  • 総行数
  • 現在地が総行数に対してどの辺りにいるか(パーセンテージ)

現在地情報を表示する

行の折り返し操作

1行がコンソール画面の横幅より長い場合は折り返されますが、-S を入力すると「折り返す」「折り返さない」を切り替えられます。

折り返す(デフォルト)

折り返す(デフォルト)

折り返さない

折り返さない

折り返さないと表示がスッキリして良いですね。ただし横スクロールはできないので、1行すべてを見なくて良い場合などで使います。

ビープー音を鳴らさない

先頭行から更に上に戻ろうとした場合や、最終行まで来た時に更に下に進もうとする場合にビープー音がなりますが、微妙にストレスなので音を止めたい時があります。

その時は -q を入力すると ON/OFF ができます。

リアルタイムでログを監視する

tail -f コマンドなどでリアルタイムにログを閲覧できますが、less コマンドでも可能です。

less では F を入力すると監視状態に入ります。

監視状態

ctrl + c で監視状態が解除されるので、そこから検索したりできます。

監視状態を解除した後でも、再度 F を入力すれば監視状態になります。

閲覧自体を終了させたい場合は ctrl + c からの q で終了できます。

オプション

上記の操作は less コマンド実行時にオプションをつけて実行する事もできます。

行数を表示する
less -N <file_name>
指定した行から表示させる
# 書式
less +<line> <file_name>

# 10 行目から表示開始
less +10 access_log
検索する文字列を指定する
# 書式
less +/<search_word> <file_name>

# p オプションも同じ
less -p<search_word> <file_name>
リアルタイムで入力を監視する
less +F <file_name>
tail -f <file_name> | grep -e xxx -e xxx
みたいに grep で監視できないのがちょっと残念

他のコマンドの実行結果を閲覧する

パイプを使って他のコマンドの実行結果を読むこともできます。

<some command> | less

例えば、圧縮したログを読む時などパイプで less へ渡してあげるとスムーズに確認できます。

gzip -dc xxx.gz | less

(ちなみに gz ファイルは パイプで渡さなくても less xxx.gz で読めます)

まとめ

less コマンドは、操作が直感的でわかりやすいのが良い点だと思います。

メモリ消費の部分も、試しに 100万件 1.6GB のログファイルを読んでみたけどメモリ消費はほぼありませんでした。(1画面分だけ読み込むため)

% wc access_log
10000000 190901510 1684049674 access_log

% ls -lhS
1.6G 4 29 17:46 access_log

% ps aux | grep access_log
%CPU %MEM
0.0 0.0 less access_log

ただし less コマンドに限った事ではありませんが、大きすぎるファイルを検索したり採番したりすると CPU に負荷がかかるので注意は必要です。

結構使いやすいので、試してみてください。

Vue 3 + vue-router-nextを動かす

Vue 3のbeta版がリリースされて、あわせて周辺ツールがalphaからbetaへ作業中とのことだったのでvue-router動くかなと思ってやってみた。

github.com

github.com

以下素振りりぽじとり

github.com

プロジェクトのセットアップ

必要なものをyarn addする。

yarn add vue@next vue-router@next

あと開発用にいつもの。lint周りはお好みなので省略

yarn add -D webpack webpack-cli webpack-dev-server ts-loader vue-loader clean-webpack-plugin html-webpack-plugin typescript

webpackの設定書く

webpack.config.js

/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const webpack = require('webpack')

const outputPath = resolve(__dirname, 'dist')

/** @type {import('webpack').ConfigurationFactory} */
const config = (env = {}) => ({
  mode: env.prod ? 'production' : 'development',
  devtool: env.prod ? 'source-map' : 'inline-source-map',
  devServer: {
    contentBase: outputPath,
    historyApiFallback: true,
    hot: true,
    stats: 'minimal',
  },
  output: {
    path: outputPath,
    publicPath: '/',
    filename: 'bundle.js',
  },
  entry: [resolve(__dirname, 'src/main.ts')],
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
      },
      {
        test: /\.vue$/,
        use: 'vue-loader',
      },
    ],
  },
  resolve: {
    alias: {
      vue: '@vue/runtime-dom',
      '~': resolve('src'),
    },
    extensions: ['.ts', 'd.ts', '.tsx', '.js', '.vue'],
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      template: resolve(__dirname, 'src/index.html'),
    }),
    new CleanWebpackPlugin(),
  ],
})

module.exports = config

適当にエイリアスの設定とかもしておく。

package.jsonに開発鯖起動用のスクリプト書く。

"scripts": {
    "dev": "webpack-dev-server --mode=development",
}

これでsrc/main.tsをエントリポイントとしてサーバーが立ち上がるようになるはず。

composition api + vue-router

viewsにindex.htmlを適当に用意。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Poketto</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

エントリポイントを定義する。従来とは若干apiが変わっているため注意。

createAppに<router-view />が定義されているメインのコンポーネントを渡し、rootにマウントする。やっていることは今までのVueと同じ。

main.ts

import { createApp } from 'vue'
import App from '~/App.vue'
import { route } from '~/router'

const app = createApp(App)
app.use(route)
app.mount('#root')

現時点でApp.vueもrouterもないので定義していく。

App.vue

<script>
export default {
  name: 'App',
}
</script>

<template>
  <div>
    <router-view />
  </div>
</template>

ページコンポーネントを定義する。

今回はカレントパスとなるindex.vueとサブページsub.vueを作る。なんか表示したかったので適当にcomputedを利用したreadonlyなデータを吐く関数も用意した。(useAppConfig)

vue-routerは既存のthis.$routeからのアクセスではなくなり、useRouterというnamed exportされている関数を用いることでjavascript側からhistoryの操作をすることができる。 router-linkは今まで通りに使えるが、特に型が効いたりはしない。

views/useAppConfig

import { computed } from 'vue'

export const useAppConfig =
  computed(() => {
    return {
      name: 'poketto',
      version: '0.0.1',
      mode: process.env.NODE_ENV,
    }
  })

views/index.vue

<script>
import { useAppConfig } from '~/views/useAppConfig'
import { useRouter } from 'vue-router'

export default {
  name: 'Index',
  setup() {
    const router = useRouter()
    const toSub = () => router.push({ name: 'sub' })
    return {
      useAppConfig,
      toSub
    }
  },
}
</script>

<template>
  <div>
    <p>{{ useAppConfig.name }}</p>
    <p>{{ useAppConfig.version }}</p>
    <p>{{ useAppConfig.mode }}</p>
    <router-link :to="{ name: 'sub' }">
      to sub link
    </router-link>
    <div>
      <button @click="toSub">
        to sub button
      </button>
    </div>
  </div>
</template>

views/sub.vue

<script>
import { useRouter } from 'vue-router'

export default {
  name: 'Index',
  setup() {
    const router = useRouter()
    const toHome = () => router.push({ path: '/' })
    return {
      toHome,
    }
  },
}
</script>

<template>
  <div>
    <p>Sub Page</p>
    <router-link :to="{ path: '/' }">
      home
    </router-link>
    <div>
      <button @click="toHome">
        to sub button
      </button>
    </div>
  </div>
</template>

routerの定義。

useRouterと同様に、新しくrouter作成用の関数などがnamed exportされるようになっているため、これらを使う。

router.ts

import { createRouter, createWebHistory } from 'vue-router'
import Index from '~/views/index.vue'
import Sub from '~/views/sub.vue'

export const routerHistory = createWebHistory()

export const route = createRouter({
  history: routerHistory,
  routes: [
    {
      path: '/home',
      redirect: '/',
    },
    {
      path: '/',
      name: 'index',
      component: Index,
    },
    {
      path: '/sub',
      name: 'sub',
      component: Sub,
    },
  ],
})

ここまでできたらyarn devで動作確認。

f:id:apple19940820:20200428191849g:plain

うごく

おわり

vue-routerはまだalphaなので大きくapiが変わる可能性もあるが、現時点ではちゃんと動作する。

別でフルtsxで書いてみたけどvue-routerはなんか動かなかった。あまり追えてない。

github.com

ちゃんと改善を回すためのDX Criteriaの活用

f:id:kotamat:20200429160545p:plain

DX Criteriaの第一弾がリリースされ、だいぶ時間が立ってから導入を検討することになったので、今更感は結構ありますが、ROXXなりの導入方法をもとに知見を共有することによって、今後導入される企業様並びにCTOの方々の何かしらの参考になればと思い、ブログを書くことにしました。

tl; dr

  • とりあえずやるよりは目的を持ってやったほうがいい
  • アセスメントは事業部ごとに行った
  • アセスメントめっちゃ多いので、2段階の絞り込みで対象範囲を絞った
  • 運用のためにシートをいじってみた
  • 一人でやるよりは、現場メンバーの声を反映したほうが解像度上がる

DX Criteriaとは

日本CTO協会が作成した、DXの基準となるものです。日本CTO協会はDXを2つの意味(Digital Transformation, Developer eXperience)で捉えており、DX Criteriaでは5つのテーマ、8つのカテゴリ、8つの項目で全320個のチェックにより数値化し、自己診断することができます。 詳しくは公式ページにて確認いただけます。 今回はこれをどのように現場で生かしていくのかを考え、改善サイクルに組み込んでいったROXX社の事例の紹介をさせていただきます。

DX Criteriaの導入背景と目的

今まではトップラインの向上を会社全体でも各事業部でも追ってきました。当然それは事業としての成長には不可欠ではありつつ、評価軸が事業成長に結びつくかどうかによりがちでもありました。 プロダクト開発においては、当然施策のリリース量も大事ではありつつ、それを追いすぎることによって将来の改善スピードの低下につながるような因子(いわゆる技術的負債など)がフォーカスに当たらなくなるというのが発生します。

「事業成長」と「事業部サイドからは見えないプロダクトに開発に必要な投資」のバランスを取るのは非常に大変であり、双方別々の責任者が立っていることが多い(求められるスキルが全く異なる)ため、優先順位をつけるためのコミュニケーションも非常に取りづらい状況になりがちです。

これを客観的評価軸に基づいて実施することによって、それが「ただのエゴ」ではなく「中長期を見据えた正しい意思決定」として認知されるようにする必要があり、それを行うために第三者機関が設定した評価軸を欲しておりました。

まさしくDXCriteriaはそれに該当するため、今回導入することにしたという背景があります。

当然導入するということは、何かしらの改善に活かすまでがゴールであります。すべての改善項目を同時に改善することは不可能であるため、改善効果の高いものからしっかりと改善に至るまでを評価軸ごとに目標設定し、実施していくことを導入時に決定しました。

Tips

正しく導入背景や目的を言語化し、「なんでこれやったんだっけ」みたいな状況にならないようにしたほうがいいと思います。

アセスメントやってみて

弊社には、2つのプロダクトがあり(agent bank, back check)、それぞれ技術スタックとしては同じなのですが、事業フェーズも組織構造も何もかもが違うため、それぞれでちゃんとアセスメントしないと薄まるなと初見で気づきました。

Tips

新規でやる方は、アセスメントの内容を確認の上、どの範囲でアセスメントをするのがいいのかを事前に確認したほうがいいかなと思います。

2段階の絞り込み

当然アセスメントはすべての項目に対してやったほうがいいわけですが、320項目もあり、一つ一つがかなり濃度の高い質問項目であるため、全部やるのは難しいケースもあると思います。

利用上の注意にもある通り、「すべての項目を満たせばいいというものではない」と認識しており、改善プロセスに回しアセスメントの内容を体感できて初めて活用の効果があるものかと思います。

ROXXではまず、開発組織全体として何が問題なのかというのを、5つのテーマから絞ることにしました。

事業と組織の状況を鑑み、改善されるイメージを最も持てた「システム」と「チーム」にフォーカスすることとしました。

次に、その項目に対してアセスメントをそれぞれの事業部の開発責任者主導でチェックをしていきました。

アセスメントをしていく中で、それぞれの評価項目に置いても重要視すべき項目と、現時点では重要視する必要がないものが浮き彫りになってきました。そのためシートを改修し、重要視すべき項目に対して、理想とされる評価を記入したものを別途用意しました。

f:id:kotamat:20200429155105p:plain

既存のアセスメントを「現実」、重要視すべき項目が記入されたものを「理想」とし、「現実」に記入されている項目のうち、「理想」に記入されているものと乖離があるものは黄色になるようにしました。

その結果が下記画像となります。 f:id:kotamat:20200429155121p:plain

f:id:kotamat:20200429155135p:plain

画像の通り、黄色になっているところをフォーカスし、改善していけば効果的に課題解決に向かうことがひと目で理解できるようになりました。

理想と現実を分割したシートはテンプレートとして下記URLで公開しております。もしお使いになる場合は、本家同様シートをコピーした上でお使いいただければと思います。

https://docs.google.com/spreadsheets/d/1NyEmOQ0wqnq_pNHtQlaDLbInGc6w6cCAViLF4sfwqx0/edit?usp=sharing

Tips

2段階の絞り込みにより、記入の負担を最小限にした上で今何を重要視すべきかの洗い出しをしやすくなりました。
DXCriteriaの導入目的を達成するために必要であればシートをカスタマイズするとよさそうです。

メンバーを巻き込んだ優先度付け

絞り込みを行いましたが、それでも対象項目は多いままです。このままでは何から改善を行えばいいかはわからないままです。

そこで、「改善項目」というシートに、対象項目を洗い出し、優先度付を行っていくことにしました。

まずは、当該評価を行った事業部ごとの開発責任者に「なぜその評価を行ったかの理由」と「本人が思う優先順位」をつけてもらうようにしました。

ただ、それだけではDXの当事者である「各開発者」の視点が抜けてしまいます。そのため、各メンバーと1on1を行い、上記評価理由をどう思うかと、その本人が思う優先順位を聞き、メンバーと開発責任者間にどのような現状認識の乖離があるかを把握することとしました。

その結果をもとに、総合的にチームのボトルネックを改善するためにどの項目を優先すべきかを並び替え、優先度の高いものに対してはどのような改善施策をいつまでに行っていくのかを決定しました。

f:id:kotamat:20200429155156p:plain f:id:kotamat:20200429155212p:plain

Tips

評価者一人で優先順位をつけると、現場との認識の乖離によって納得度の低い改善項目となりうるため、メンバーを巻き込んで優先度をつけました。

まとめ

まだこちらの改善プロセスは回し始めたばかりなので、これがどれだけ効果を発揮するのかは今後の改善次第ではありますが、改善プロセスを回し始めてすぐメンバーからは改善が体感できたという声が上がってきたので、改善項目の選定プロセスは一定の効果をもたらしたと感じられております。

「SSRと規約だけ」の勘違いを解く、 Nuxt.js をプロジェクトで採用すべき理由

back check 事業部に業務委託でコミットしているフロントエンドエンジニアの potato4d です。

ROXX では、 v1.0 が出る前から、プロダクト、コーポレートサイト、ランディングページ、メディアなど、多くのシーンにて、 Nuxt.js が採用されています。

Nuxt.js は多くの人に愛される一方で、最近は Vue 3.0 の話もあり、多少評価が落ち着いてきた印象があります。

この記事では、十分に普及した今だからこそ、 Nuxt.js の本当の優位性と、どういったときに使うべきか。あるいは使うべきでないかを再度言語化できればと思います。

SSRとルールだけ」という勘違い

まずは Nuxt.js の採用についてです。多くの場合、「Vue か Nuxt か」という問いには、いかが判断基準として使われます。

  • SSR 今必要かどうか
  • Nuxt.js の組み込みのルールをチームに適用したいかどうか

これは確かに Nuxt.js を採用する一つの理由ではありますが、実際にはこれだけではありません。

Nuxt.js は、 Vue.js の延長ではなく、 Vue.js の機能をよりよいアプローチで提供しているフレームワークとして、機能面でいくつかの大きな価値があります。

これから順を追って紹介していきますが、それらの判断軸について考えた上で、全てが不要な場合に限り Nuxt.js を利用しない選択肢が出てくるのではないかと思います。

Vue.js プロジェクトにおける Nuxt.js 選定の基準

SSR が今必要なのかどうか、Nuxt.js のいわゆる「規約」について採用したいかどうかは既に知られていると思うので、それ以外について言及します。

プロジェクトが将来的に SSR エンジンを利用する可能性があるか

まずは言及される「SSRするかどうか」。です。

SSRをするときにのみ Nuxt.js をすべき」という言説について、一部は正しいですが、一部は誤りがあります。

例えば「個人個人のデータを扱うアプリみたいな Web サービスだから必要ない」といった場合などです。ここで伝えたいのは、 SSR の是非を考えるさい、多くの場合は「開発開始時点の要件」で考えられがちということです。

しかし、実際は後から必要になることも多くあります。

例えば

  • 「サービスのトップにおいて、初回の応答速度を可能な限り高めたい」
  • 「求人の URL を貼ると API のデータを基に OGP とページタイトルがプレビューされてほしい」
  • 「個人のワークスペースを持つメモアプリだけど URL での限定共有がほしい」

などが上げられます。こういったシチュエーションにおいて、開発開始時点での要件だけをみると、 SSR は不要には見えるのではないでしょうか。しかし、実際にはこういったケースは頻発します。

このようなシチュエーションにおいて、あとから SSR をするのは非常にコストの高い作業となります。

簡単なものでいうと、暗黙的に window オブジェクトに依存したコードがある。ライフサイクルフックの選定をサボって全てが mounted になっている。というのは既存プロジェクトの SSR 対応を担当したことがあるかたは経験したことがあるはずです。

現代では、SSRを要求されるシチュエーションは非常に多くなっているため、Universal なコードであることを前提とした上で、本番環境を SSR するかどうかという判断軸を持つことが重要です。

一方で、「そのユーザーのログインコンテキストに依存して外部に絶対に何もでないアプリケーション」みたいなものがあれば、それは SPA 前提で問題ありません。

例えば社内向けの管理画面は、ログイン画面などのごく一部を除き、ログインが不要なエンドポイントは現実的に存在し得ないため、SSR は不要といえます。

なお、nuxt generate についても、こちらは内部的に SSR を行っておりますので、「最終的には静的サイトなので SPA モードで問題ない」というわけではないことをご留意ください。

Node.js サーバーの運用が可能かどうか

また余談として、Node.js サーバーが立ち上がるということもしっかりと認識しておくべきです。

これは SSR する場合に付随してくるほか、Nuxt.js の serverMiddleware や Node.js からの Nuxt.js 利用が発生する場合に考慮すべき事項です。

SSR というとサーバーが必要でランニングコストがかかるという部分はフロントエンドエンジニアの多くの共通認識になっています。しかし、運用という観点は忘れられがちではないでしょうか?

Nuxt.js を利用していると、SSR は勿論、その他でも Node.js 上で動かすことがそれなりに発生します。ただコンピューティングリソースを利用するというだけでなく、例えば以下のような問題にも対処が必要であるという認識を持った上で、自分たちが Node.js サーバーを運用できるかを考える必要があります。

  • Node.js ランタイムの追従 (常に LTS に追従できるか?1,2年サイクルでメジャーバージョンをあげられるか)
  • セキュリティ問題への対処 (脆弱性のあるパッケージがより無視できない問題となる)
  • メモリリーク・サーバースケーリングへの対処

これらを意識するのが難しい場合、Nuxt.js を採用する場合であっても SSR については忘れることを推奨します。

Context / Inject によってコードをクリーンに保ちたいか

あまり話に挙がりづらいですが、 Nuxt.js が独自のプラグイン構造を持っていること、そしてそれが強力であることも認識する必要があります。

Nuxt.js には、 Plugin という概念が存在します。これは、これまで Vue.js プロジェクトにおいて、自分たちで作っていた Vue.use を吸収するレイヤーによく似ています。例えば Bootstrap Vue や Element UI を import して Vue.use していたような層がここにあたります。

しかし、 Nuxt.js の Plugin の価値はそれだけにとどまりません。Nuxt.js の Plugin は、 Vue.js の Provider / Inject をうまく隠蔽し、 Context として活用できるという大きな優位性があります。

聞き慣れないワードも多いかもしれませんが、身近な例としては、 Axios Module が this.$axiosapp.$axios で呼び出せるあの機能です。

Axios Module の抽象化粒度が良いかどうかは諸説ありますが、「コンポーネントやストアから特定の外部依存を切り出せる」ということには、大きな価値があります。

具体的には、コンポーネントのテストを書く場合のシチュエーションなどにおいて、以下のように記述できるという利点があります。

import TheUserEditForm from '~/src/components/TheUserEditForm.vu'
import { shallowMount } from 'vue-test-utils'

const $axios = {
  async get() {
    return {
      data: {
        foo: 'bar'
      }
    }
  }
}

describe('~/src/components/TheUserEditForm.vue', () => {

  test('Submit form', () => {
    const wrapper = shallowMount(TheUserEditForm, {
      mocks: {
        $axios
      }
    })
    // Code
  })
})

多くの場合こういった記述が全く不要というわけではないので、Nuxt.js を利用したほうがシンプルに取り回せます。

反対に、こういった優位性が不要なシンプルな Web サイトの場合、同様に静的サイト出力が可能な Gridsome などが選択肢に上がってくる可能性もあります。

ここも判断軸として加えると良いでしょう。

選定にあたってあまり重要ではないこと

Routes の自動生成と layout

Nuxt.js の大きな特徴として、ファイルシステムと密接に紐付いた Routes が挙げられます。

これは、非常に便利な機能ではありますが、 Nuxt.js のみが唯一もつというわけではありません。Nuxt.js スタイルの Routes について、 Vue CLI v3.x or higher が利用可能なvue-auto-routing というパッケージにて代替が可能となっています。

これは日本人の Vue.js コアチームメンバーの ktsn によって開発されており、今もアップデートが続いています。

この機能自体は便利ですが、意思決定に関わるようなものではありません。

Vuex 連携

これも非常に便利な機能ですが、 Single Source of Truth への評価が落ち着いてきた現代においては、大きなポイントとはならないのではないでしょうか。

実際に ROXX では、新規でのコードにおいては Vuex を利用しないケースも多くなっていきています。

このあたりは現場にあわせて効果が変わる部分であるため、あまり大きな要因とは言えません。

おわりに

Vue 3.0 が既にインストール可能となり、 TypeScript との親和性や Composition API による新記法などに期待と注目が集まっています。

その大きな変化をうけて、 Nuxt.js がどうなっていくか、単純な Vue.js プロジェクトで十分となるかは非常に気になるトピックではあります。

しかし、Nuxt.js には、目先の生産性やSSRだけではなく、高品質なプロジェクトの基盤を持っていること、そして、より高品質な基盤を自分たちで作り上げていくための土壌が存在しているという大きな価値があります。

この記事が現場における Nuxt.js の妥当性について再度考えるきっかけとなれば幸いです。

agent bankの開発で使われているKubernetes, EKS

agent bank開発部の森です。

新機能の開発や不具合修正中、他の人にちょっとみてもらいたい時に プレビュー環境 とよばれる環境を構築してURLを共有してみてもらうことが多々あります。

最近 agent bank のプレビュー環境を Amazon EKS に置き換えましたのでどのように構築したのか、構築してみての感想を記します。

back check でもプレビュー環境を構築しています。あえて異なるアプローチをとったのでback checkでのプレビュー環境の構築についてはこちらの記事もご参照ください。 techblog.roxx.co.jp

概要

概要を列記すると以下のとおりです。

構成

  • KubernetesAPIコンテナ、フロントエンドコンテナを起動、管理している
  • Kubernetesの環境構築には Skaffold , Helm を利用している
    • ローカル環境のAPIコンテナ、データベースコンテナもKubernetesで構築している
      • ローカル環境ではAPIのみコンテナ起動、フロントエンドは yarn dev で起動
    • プレビュー環境はEKS (Amazon Elastic Kubernetes Service) 上で利用している

プレビュー環境へのデプロイ

  • GitHub ActionsでPull Request作成時や任意のタイミングで開発者がアクセスする環境にデプロイしてコンテナを作成する
    • Pull Request をクローズしたら自動で環境は削除される

インフラ構成図

インフラ構成図です。大きいサイズの画像はこちら

f:id:jiska_roxx:20200319192954p:plain
インフラ構成図

クラスター内部詳細はこちらです。大きいサイズの画像はこちら

f:id:jiska_roxx:20200319192943p:plain:w800
クラスタ

クラスター内部でnginx controllerが起動しており、其々ネームスペースごとに起動するコンテナへ紐づいています。 また、メールの表示確認などに使用している MailHog やシステムの統計のために Grafana が起動しています。

クラスター内部のログはCloudwatch Logsへ出力するようにしています。

MySQLやElasticacheなどはクラスターには含めていません。

Before Kubernetes

今回構築したKubernetesで動作している環境の前はどうだったのか、その課題はなんだったのか、それぞれ記載しておきます。

旧開発環境はEC2インスタンスで起動しており、nginxで複数サブドメインを定義して動かすようにしていました。 環境を追加したい場合は EC2インスタンスsshしてサブディレクトリ作成して.envファイルを手動で編集する という面倒なもので、社内esaに手順はまとめてあるものの準備に時間がかかっていました。

デプロイはSlackの専用チャンネルでhubot経由でデプロイしていましたが、 朝になるとhubotプロセスが死んでる ので毎朝再起動かけていました。

解決したかった課題

この状況から解決したかったことは以下のとおりです。

  • 環境を簡単に増やせること
    • PRごととか、欲をいえばコミット単位で環境作りたい
  • かんたんにデプロイできること
    • Slackの専用チャンネルでhubot経由でデプロイできるのは便利なのでこの体験は失いたくない
  • メンテナンスコスト下げたい
    • 俗人化のもと
  • デプロイは高速化したい
    • デプロイに数分かかっていた

Why Kubernetes

この状況でなぜKubernetesを選択したかですが、正直なところback checkと同じくECSで環境構築した方が楽でしたが、今後EKSやKubernetesまわりの運用を開発チームが正しく行えるのか評価、またback checkとあえて別のアプローチを検証した方が社内知見も増えるだろうという目的でした。

次にkubernetesクラスターの構築手順を記します。

Slaffold, Helmを利用した環境構築

kubernetesクラスターの構築には Skaffold , Helm を組み合わせて利用しています。

skaffold.dev

helm.sh

それぞれ公式ホームページから引用すると、SkaffoldはKubernetesのアプリケーション構築、プッシュ、デプロイのワークフローを処理し、CI / CDパイプラインを作成するためのビルディングブロックを提供するツール、HelmはKubernetes用のパッケージマネージャーです。

Skaffold & Helm導入して良かったこと、大変だったこと

Skaffold用とHelm用にYamlを記述し、skaffold run を実行するとDockerイメージのビルド、ECRへのプッシュ、EKSへpodsを作成…といった一連の作業をコマンド1つで行えるようになりました。

また、ローカル環境も skaffold dev で起動させ、APIコンテナを動作させつつソースコードの編集時に自動反映させるような仕組みを実現できています。

Helmについてはパッケージ管理に用いるChartsが拡張性が高く、agent bank用のAPIコンテナ、フロントエンドコンテナの作成もHelm Chartsで定義しています。 またMySQLコンテナなどよく使いそうなものが公開されており便利でした。

大変だったこととしてHelm Chartsに go templates を多用することになり、{{ include "xxxx" . | indent 4 }} などindent合ってたっけ?みたいな数えることが多々あり以下のようなぱっと見読むのが大変なYamlが出来上がりました…。

# 抜粋
metadata:
  name: {{ template "agent-bank-base.api.fullname" . }}
  labels:
    app: agent-bank-api
{{ include "agent-bank-base.labels" . | indent 4 }}
spec:
  selector:
    matchLabels:
      app: agent-bank-api
      release: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app: agent-bank-api
{{ include "agent-bank-base.labels" . | indent 8 }}
    spec:
      terminationGracePeriodSeconds: {{ .Values.api.terminationGracePeriodSeconds }}
      containers:
        - name: api
          image: {{ .Values.api.imageName }}
          imagePullPolicy: {{ .Values.api.imagePullPolicy }}
          args:
            - php-fpm
          command:
            - sh
            - -c
          env:
{{- range $key, $value := .Values.api.env }}
            - name: "{{ $key }}"
              value: "{{ $value }}"
{{- end }}

プレビュー環境へのデプロイ

プレビュー環境へのデプロイはGitHub Actionsで行っています。上述の skaffold run が起動するようにしています。

  1. Pull Request作成
  2. コメントに /preview 入力
  3. でCIフローが動き出す
  4. skaffold runが起動してコンテナのビルド、デプロイ開始
  5. デプロイ完了、Pull Requestに起動したコンテナのURLがコメントされる

解決したかった課題は解決したのか

課題と結果を振り返ります。

課題 結果 振り返り
環境を簡単に増やせること よい
かんたんにデプロイできること とてもよい
メンテナンスコスト下げたい Hubotの運用をやめられたけどEKSの運用は大変だったのでトータルではあまりコスト下がってない
デプロイは高速化したい × デプロイから環境立ち上げまでの時間は数分かかっている

今後改善していきたいこと

  • コンテナの自動起動数に制限がある
    • インスタンスのスペック次第だが同時に割り当てられるIP数に制限がある
  • コンテナの起動に数分かかる
    • イメージのビルドに時間がかかっているので高速化が必要
  • 各コンテナで共通のデータベースを参照している
    • agent bankのseederが貧弱なのでいい感じのデータが作成できていないため
      • seederをいい感じにしていく

EKS使ってみての感想

たとえば EKSのコンソールではコンテナ内部の情報を確認することができず、クラスター内部にどんなpodsが起動しているのかとかが確認できません。 確認するためにはたとえばOctantなど別ツールを使う必要があり、せっかくAWS使っているのにAWSだけで全て完結するでもないのがつらいです。

github.com

また、クラスターはデプロイごとに破棄、再作成したいけどロードバランサーは再利用したい…みたいな運用が難しいです。

なぜつらいのか調べていたところ、Amazon EKS Advent Calendar 2019 最終日のtoriさんの記事みてふに落ちました。

toris.io

長いですが引用します。

Kubernetes というプラットフォームの哲学に沿ってシステムを構築すれば、素晴らしく一貫性のある自動化とその体験が手に入ります. しかし、Kubernetes というプラットフォーム自体の運用容易性を高める目的で Kubernetes クラスタ自体を短命なものにしようとすると、そのクラスタから作成された長寿命なクラスタ外リソース(ALB や RDS)のライフサイクルとの整合性が取れず、奇妙な運用をやることになってしまう.

そのような悩ましい現実を直視した結果、ALB や RDS は AWSAPI で、コンテナで動かすものは KubernetesAPI で、というような、一貫性に欠ける構築・運用方法を選ぶことが往々にしてあります. あるいは Kubernetes クラスタを短命かつ継続的に再作成することは諦め、長寿命なクラスタとしての運用を選択することで、AWS リソース作成と管理まで含めて Kubernetes の一貫性ある体験の中で完結させることにプライオリティを置く方もいます. しかしその選択の結果、数多くのアプリケーションが同居しながら動く大規模クラスタに育ってしまい、ちょっとしたクラスタ障害がそこで動く多くのアプリケーションに影響を与えるようなことになってしまうのは想像に難くありません.

このような状況と前提条件の中で、どういう決断をするのか. 何が自分たちと自分たちのシステムにとってベストなのかをどうやって判断するのか. これが EKS(Kubernetes) が ECS と比較して『難しい』と言われる正体、その理由の1つだと僕は理解しています.

AWSの恩恵を受けつつもKubernetesの運用を楽したい、という欲張りすると難しいなあという感想でした。

まとめ

  • Kubernetesは便利だった
  • Skaffold, Helmの組み合わせはかなり便利だった
  • 引き続き開発環境よくするのやっていきたい
  • EKSの運用には勘所つかむのが大変

ともあれ今後もagent bankの開発がんばっていくための足場固めができつつあるので新機能の開発や既存機能のブラッシュアップもより加速することができると思います。

back checkのプレビュー環境

これは3/18に開催された、ROXX社内LT大会の資料です。

概要

今回は下記の内容を紹介します

  • back checkのリポジトリの状況
  • プレビュー環境に求められる要件
  • 参考にしたOSS
  • 実際の昨日のアウトプット
  • 具体的な実装

back checkのリポジトリの状況

backcheckには下記の4つのリポジトリが存在しています

  • LP
    • LPのフロントエンドが実装されている
    • Nuxt.js製
  • admin
    • 管理画面のフロントエンドが実装されている
    • Nuxt.js製
  • front
    • ユーザが使う画面が実装されている
    • Nuxt.js製
  • API
    • back checkが使うすべてのAPIが実装されてる
    • Laravel製

プレビュー環境に求められる要件

上記の様に、それぞれが別のリポジトリで実装されているため、下記のケースを想定したレビュー環境が必要となりました

  • LP, admin, front, APIそれぞれどれかだけの変更だけが反映されているもの
  • 複数のリポジトリにまたがった修正が反映されているもの

また、それ以外にプレビュー環境として、下記の要件を満たす必要がありました

  • プレビュー環境同士は完全に独立していること
    • 新規機能も多いためDBスキーマの変更にも耐えられるよう、DBも独立していること
  • githubのChatOpsでできること
  • Auth0と連携ができること
    • 環境作成時に、callbackURLなどが自動的に反映されること
    • https化されていること
  • PRがクローズされたら、自動で環境が削除されること
  • k8sとかの知識は不要で、AWS上で動くこと
  • リソースの上限とかはあんまり気にしたくない

参考にしたOSS

色々と探していたら、下記のOSS(というかプレゼン用に作ったと思われるなにか)が見つかった

https://github.com/clareliguori/clare-bot

clare-botというボットに対して、 preview this とメンションするだけで、プレビュー環境を作ってくれるというもの。使用している技術スタックも

Built with GitHub APIs, AWS Fargate, AWS CodeBuild, Amazon ECR, and AWS CloudFormation

とのことで、実装も簡単そうだったのでこれを使うことにした。

一方これだけでは最初の要件(マルチリポでよしなに使える)が適応できないため、ソースコードをいじる必要があった

実際の機能の使い方

  1. GitHub上でプルリクエストを作成します。
  2. プルリクエスト上で「@roxx-bot preview this」とコメントします。
  3. roxx-botさんがちょこちょことコメントを残していきます。(最長で30秒ほどかかります)
  4. しばらく経つとそのプルリクエスト(=base branch)のコードが反映された環境が立ち上がり、URLが表示されます。
  5. backcheckの環境は1リポジトリでは完結しないので、もう一方の環境を立ち上げます。

    つまり、apiのプルリクエストであればfrontを、frontのプルリクエストであればapiのプレビュー環境を立ち上げます。

    1. @roxx-bot preview front」または「@roxx-bot preview front」とコメントします。
  6. しばらく経つとそのプルリクエスト(=base branch)のコードが反映された環境が立ち上がり、URLが表示されます。

  7. URLのリンクを踏むとプレビュー環境を見ることができます。

preview XXX のあとに、githubのPRのURLを貼り付けることによって、そのPRで立ち上がっているURLを、環境変数に適応できるため、複数PRにまたがる施策でも問題なくアプリケーションが動くようになります。

具体的な実装

立ち上げ時

bot

  1. 10秒ごとに自分宛てのメンションが来ているかの監視
  2. メンションが来ていたら、コメントの中身を解析し、環境立ち上げの関数を実行
    1. preview xxx のxxxの部分から、どのリポジトリの環境を立ち上げるか判断
    2. xxx以降に指定されたPRのURLをよしなに解析し、そのリポジトリで必要となる環境変数を書き換え
  3. 環境用のユニークIDを発行(これが環境立ち上げ時のURLのサブドメインにもなる)
  4. Auth0にcallback URLなどを追加
  5. 対象リポジトリにあるbuildspec.ymlをもとにCodeBuildを開始
  6. CodeBuildからCloudFormation用のymlが出力されるので、それをもとに対象環境を立ち上げ
  7. 立ち上げ後、成功したらGitHubのコメントにURL付きで通知

リポジトリ

  1. bot側から渡ってくる各種環境変数を変換し、必要に応じてdocker buildするときに使う
  2. gitのコミットハッシュをdocker tagとしコミット→ECRにpush
  3. 生成したイメージのタグ等を用い、CDKを実行(以下CDKの実装内容)
  4. 環境変数の中で、ECSの実行時に使うものだけを抽出し、環境変数
  5. APIの場合はapimysqlを立ち上げ、フロント系の場合はnuxt generateしたものをnginxで静的ファイルとして参照する

cleanup時(bot側のみ)

  1. Githubのステータスがcloseになったことを検知
  2. Auth0のcallback URLなどを削除
  3. CloudFormationのスタックを削除
  4. 削除が完了し次第GitHubのコメントに通知

課題は解決したのか

課題 結果 コメント
他の環境に依存しない環境を立ち上げられたか AWSのフルマネージドな環境なので、無意識に無限に増やせる
Auth0との連携はできたか botをTypeScriptで書いてるので柔軟性が高くそれほど困らずに作れた
複数リポジトリのPRにまたがる環境を作れたか 一応作れたが、こういうのを作るためにモノレポ化はしたほうがいいなと改めて認識
デプロイの速度 docker cacheとか色々挟んだが、どうしても時間はかかってしまう。何故かECSのタスクをアップデートするときに、過去のタスクが削除されるまでの時間が長く、トータルの時間が長くなってしまっている
DBの独立性 backcheckはシーダーがいい感じに整っているので、特に問題なく環境構築はできた

今後の課題

  • ECSのタスクアップデート処理を見直して高速化
  • SSMを用いてAPIのコンテナ内に入れるようにする
    • プレビュー環境上でDBをいじりたかったりするので
  • 不要なコンテナは非稼働時には停止しておきたい
    • せっかくfargate使ってるのに、常に立ち上がっててコストがかかってる
  • botのコードのリファクタリング
    • 継ぎ足しになってるので不要なところとかは削除したい
    • 適度にモジュール化したい。現状は手続き型の典型的な処理の流れみたいになってる

PHPerKaigi 2020 に参加して、LTしてきました

こんにちは、今年の2月から、backcheck開発チームにジョインしました 秋葉です。
2020/02/09〜2020/02/11 に3日間に渡り開催された、PHPerkaigi 2020 に参加してきました。
今回自分は、スタッフとして色々やりつつ、ルーキーズLTの登壇者としてLTもしてきました。

phperkaigi.jp

これは参加者に全員配られたPHPerトレカ
f:id:akki_megane:20200215192447j:plain

ルーキーズLTしたよ

2/10 Day1 に
「明日からフロントもよろしく!」と言われたときに備える Atomic Design でのフロントエンド設計
というタイトルでルーキーズLTに登壇してきました
(プロポーザルを出すときに、Atom Design で出しているの前日に気づいてとても焦りました、Atomic Design が正しいです)
資料はこれ
speakerdeck.com

2019年に学んだことの一つとして、Atomic Design を使ったフロントエンドの設計があり、そのアウトプットという意味で登壇しました。 5分のLTということもあり、Atomic Design については触りの部分しか話せませんでしたが、興味を持ってくれる方がいればありがたいです。

反省

登壇時に緊張して、画面ばかりを見てしまいました。。。
理由としては登壇直前に、自己紹介のページを PHPerトレカに差し替え、
自己紹介を「ぼくの効果は 対戦相手のカードを1枚捨てることです! なのでみなさん手札を捨ててください」という自己紹介が ややうけ という結果に終わり(自分は大爆笑になると思っていました (泣))
登壇前は緊張していなかったのに、100人規模をややうけを見て緊張が一気に上がり、それ以降まともにお客さんの顔が見れませんでした。。。
終わった後に何人かに「いつもより緊張してたね」的なことを言われてしまい、若干凹みました。
緊張したとはいえ、カンファレンスでの登壇はいい経験になりました。
聞いてくださった皆様ありがとうございました。

アンカンファレンスでもLTしたよ

友人がトラックCで、ゲリラ的にLTしていたので自分も飛びこんで、LTを2回してきました。

  • 入門無限LT
  • AWS re:Invent 2019 俺的 注目技術

最初にやった、「入門無限LT」で、「会場にいるみんなにぜひこの場でLTやってほしいと」
けしかけたら、何人かの人が実際にLTに参加してくれてとても嬉しかです。

スタッフとして

スタッフのことについては、同僚の前田さんがいい感じにまとめてくれいますのでそちらもどうぞ。
https://techblog.roxx.co.jp/entry/2020/02/13/113611

事前準備

去年のPHPerkaigi2019年に引き続きスタッフとして参加しました!
今年はコアスタッフとして事前準備から参加していましたが、事前にやることや各種フローが整備されていて、
初めてのコアスタッフでしたがスムーズに割当られた仕事をすることができ、さすがカンファレンスジャンキーの集まりだと感動しました。

当日

自分は当日ルームBの担当をしながら、トークを聞いたり、参加者の方と交流したり、例年通りとても楽しく過ごせました。
大きな問題もなく無事に3日間やりきって、疲れましたがとても楽しかったです。
来年もスタッフやりたいなーと自然と思えました。

まとめ

カンファレンスで初登壇さらに初めてのコアスタッフで、資料作りや、事前準備等、個人的に慣れない作業で少し大変でしたが、
当日は最高に楽しく、登壇も楽しかったし、スタッフ業も楽しかったです。 
去年に引き続き、今年のPHPerkaigiは最高のカンファレンスでした!

ルーキーズLT × 1
アンカンファレンスLT × 2
懇親会LT × 1
計 4 回LTをしたので今回のPHPerkaigi一番LTをしたのは僕です!