WESEEK Tech Blog

WESEEK のエンジニアブログです

Go 言語での開発始めてみる〜開発環境を作る編〜

こちらは 「Go言語での開発始めてみる〜開発環境を作る編〜 」からの転載です。



前回の続き Go言語での開発を試してみる 〜調べる編〜 で調べたものを組み合わせて実際に Go 言語を使って Web システムを作ってみようと思います。今回はフレームワークの起動までです。

開発環境構築

OS は Windows 10 にて行っていきます。

Go 自体のバージョン

この前入れた通り下記で行こうと思います。こちらもそこまで意味は無いです。

> go version
go version go1.10.3 windows/amd64

開発エディタ

特に選定したわけではなく、普段使い慣れている Visual Studio Code を使って開発をしようと思います。 とりあえず拡張機能をインストール

f:id:weseek:20191107165922p:plain

Ctrl + Shift + p でコマンドパレットを開き、 Go: Install/Update tools を実行

f:id:weseek:20191107165939p:plain

ツールをインストールしていきます。

    Installing github.com/nsf/gocode SUCCEEDED
    Installing github.com/uudashr/gopkgs/cmd/gopkgs SUCCEEDED
    Installing github.com/ramya-rao-a/go-outline SUCCEEDED
    Installing github.com/acroca/go-symbols SUCCEEDED
    Installing golang.org/x/tools/cmd/guru SUCCEEDED
    Installing golang.org/x/tools/cmd/gorename SUCCEEDED
    Installing github.com/fatih/gomodifytags SUCCEEDED
    Installing github.com/haya14busa/goplay/cmd/goplay SUCCEEDED
    Installing github.com/josharian/impl SUCCEEDED
    Installing github.com/davidrjenni/reftools/cmd/fillstruct SUCCEEDED
    Installing github.com/rogpeppe/godef SUCCEEDED
    Installing golang.org/x/tools/cmd/godoc SUCCEEDED
    Installing github.com/sqs/goreturns SUCCEEDED
    Installing github.com/golang/lint/golint SUCCEEDED
    Installing github.com/cweill/gotests/... SUCCEEDED
    Installing github.com/derekparker/delve/cmd/dlv SUCCEEDED

    All tools successfully installed. You're ready to Go :).

これらがインストールされました。
すると。。。

f:id:weseek:20191107170012p:plain

わーすごい!色もついたしブレイクポイントを設定して F5 を押すことでデバッグもできるようになりました!

  • 若干詰まった点
    • setting.json を作業ディレクトリ配下に置き下記のように gopath を設定しなければいけないようです

        {
          "go.gopath": "/path/to/go/src/beegotest"
        }
      

こんな感じにフルパスで指定してあげるとインストール直下以外でも動きました。

実際にやってみた

それではここから本格的な(とはいっても大層なものを作るわけではないですが)開発に入って行こうと思います。
せっかくなので前回調べた中のフレームワークを使ってみようと思います。

今回は GitHub上の Star 数が一番多いという理由だけで beego を使ってみようと思います。

パッケージ管理

特にこだわりがないのでパッケージ管理は dep を使おうと思います。vgo (Versioned Go) も公式ではありますがまだ人類には早いという噂を聞いたので無難な選択をしますw
とりあえず go get で取得し、init して ensure で /vendor 配下にパケージをダウンロードするように設定します。

> go get -u github.com/golang/dep/cmd/dep
> dep init
> dep ensure

どのコマンドもうんともすんとも言わず不安ですが、指定したフォルダの配下に下記ができていれば大丈夫のようです。

> dir


    ディレクトリ: go\src\beegotest


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2018/08/20     19:32                vendor
-a----       2018/08/20     19:32            222 Gopkg.lock
-a----       2018/08/20     19:32            655 Gopkg.toml
-a----       2018/08/20     19:20             55 settings.json

beego インストール

dep の準備が整ったら実際に beego をインストールしていきましょう。

> dep ensure -add github.com/astaxie/beego
Fetching sources...

"github.com/astaxie/beego" is not imported by your project, and has been temporarily added to Gopkg.lock and vendor/.
If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/.

bee ツールもご一緒に

> dep ensure -add github.com/beego/bee

と思ったんですが、どうやら依存関係のせいで dep でインストールができませんでした。。。 しかたがないので go get で取得します。ソースコードとしては直接関係ないのでまぁ問題ないと判断します。

> go get github.com/beego/bee

無事インストールできました

> bee version
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0

├── Beego     : Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego
├── GoVersion : go1.10.3
├── GOOS      : windows
├── GOARCH    : amd64
├── NumCPU    : 4
├── GOPATH    : C:\Path\to\go\
├── GOROOT    : C:\Go\
├── Compiler  : gc
└── Date      : Monday, 20 Aug 2018

プロジェクト作成

適当に bee ツールにてプロジェクトを作成してみます。

> bee new haruch
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2018/08/20 20:13:05 INFO     0001 Creating application...
        create   C:\Path\to\go\src\beegotest\haruch\
        create   C:\Path\to\go\src\beegotest\haruch\conf\
        create   C:\Path\to\go\src\beegotest\haruch\controllers\
        create   C:\Path\to\go\src\beegotest\haruch\models\
        create   C:\Path\to\go\src\beegotest\haruch\routers\
        create   C:\Path\to\go\src\beegotest\haruch\tests\
        create   C:\Path\to\go\src\beegotest\haruch\static\
        create   C:\Path\to\go\src\beegotest\haruch\static\js\
        create   C:\Path\to\go\src\beegotest\haruch\static\css\
        create   C:\Path\to\go\src\beegotest\haruch\static\img\
        create   C:\Path\to\go\src\beegotest\haruch\views\
        create   C:\Path\to\go\src\beegotest\haruch\conf\app.conf
        create   C:\Path\to\go\src\beegotest\haruch\controllers\default.go
        create   C:\Path\to\go\src\beegotest\haruch\views\index.tpl
        create   C:\Path\to\go\src\beegotest\haruch\routers\router.go
        create   C:\Path\to\go\src\beegotest\haruch\tests\default_test.go
        create   C:\Path\to\go\src\beegotest\haruch\main.go
2018/08/20 20:13:05 SUCCESS  ? 0002 New application successfully created!

いろいろと自動作成されました。

起動

それでは早速起動します!

> cd haruch
> bee run
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2018/08/20 20:16:35 INFO     ? 0001 Using 'haruch' as 'appname'
2018/08/20 20:16:35 INFO     ? 0002 Initializing watcher...
beegotest/vendor/github.com/astaxie/beego/config
beegotest/vendor/gopkg.in/yaml.v2
beegotest/vendor/github.com/astaxie/beego/utils
beegotest/vendor/github.com/astaxie/beego/logs
beegotest/vendor/github.com/astaxie/beego/grace
beegotest/vendor/github.com/astaxie/beego/session
beegotest/vendor/github.com/astaxie/beego/toolbox
beegotest/vendor/golang.org/x/crypto/acme
beegotest/vendor/github.com/astaxie/beego/context
beegotest/vendor/golang.org/x/crypto/acme/autocert
beegotest/vendor/github.com/astaxie/beego/context/param
beegotest/vendor/github.com/astaxie/beego
beegotest/haruch/controllers
beegotest/haruch/routers
beegotest/haruch
2018/08/20 20:16:40 SUCCESS  ? 0003 Built Successfully!
2018/08/20 20:16:40 INFO     ? 0004 Restarting 'haruch.exe'...
2018/08/20 20:16:40 SUCCESS  ? 0005 './haruch.exe' is running...
2018/08/20 20:16:40.951 [I] [asm_amd64.s:2361]  http server Running on http://:8080

起動した!!!

f:id:weseek:20191107170043p:plain

試しにいじってみる

普通の自動生成されたファイルを眺めてみると、普通の MVC フレームワークのようなので試しに View を編集してみましょうか。
/views/index.tpl というファイルを開いてみると想像通り html ファイルが入っていました。 なので下記のように編集してみます(抜粋)

<body>
  <header>
    <h1 class="logo">haruhikonyan 参上</h1>
    <div class="description">
      go 及び beego を試してみてるマン
    </div>
  </header>
  <footer>
    <div class="author">
      Official website:
      <a href="http://{{.Website}}">{{.Website}}</a> /
      Contact me:
      <a class="email" href="mailto:{{.Email}}">{{.Email}}</a>
    </div>
  </footer>
  <div class="backdrop"></div>

  <script src="/static/js/reload.min.js"></script>
</body>

f:id:weseek:20191107170109p:plain

無事編集できたのと、ライブリロードこそ対応してないものの、ソースを保存してブラウザを更新すればサーバの再起動無しにコードは反映されました。本日はこれくらいで。

まとめ

まだまだ Go言語たるものの真髄には全然触れられてはいないとは思いますが、 VSCode でのエディタの整備も特に苦も無く整備でき、デバッグ環境もすぐ整えられました。また、ちょこっと試してみた Go の Web フレームワークである beego も Rails のようなフルスタックフレームワークも体感的には Ruby で開発環境を整えるよりも楽に行えた気がしたのでこれはアリだなと思いました。

次回はこの beego を使ってもっと Web サービスっぽくするのと、他の機能も使ってみようと思います。

関連記事

tech.weseek.co.jp

アプリケーション開発におけるロックの重要性と ORM におけるロックの実現例 〜楽観的ロックの紹介〜

こちらは 「アプリケーション開発におけるロックの重要性と ORM におけるロックの実現例〜楽観的ロックの紹介〜」からの転載です。



前回、「アプリケーション開発におけるロックの重要性と ORM におけるロックの実現例」ではロックについて掘り下げ、トランザクションについてとその特徴を紹介し、その中で楽観的ロックの存在を挙げました。

そこで、今回は楽観的ロック及び悲観的ロックについて紹介したいと思います。

まずは、データの不整合が発生する状態とトランザクションの分離レベルについて詳細を紹介し、続いて楽観的ロックと悲観的ロックについて紹介していきます。

データの不整合

ロックによりデータの不整合が発生することを防ぐことができますが、不整合が発生するケースは様々です。

データの不整合を全て防ごうとすると可用性や性能が著しく低下するため、どの程度まで不整合を許容するかを考え、それに見合ったトランザクションの分離レベルを選択する必要があります。

データの不整合が発生した状態

整合性に問題が発生した状態として、ANSI/ISO 標準 SQL と論文「A critique of ansi sql isolation levels」から取り上げると次の項目が挙げられます。

「※」が付いた不整合状態は、コミット前の値は参照できない前提で考えると問題の本質が分かりやすいと思われます。(それでも防げない状態であるため)

リードスキューとライトスキューを除く各状態について、以下に例を用いて問題が発生するまでのシーケンスを図示します。

ダーティライト

ダーティライトとは「複数のトランザクションが同じエンティティを更新した後、あるトランザクションロールバックした場合に戻すべき値が不明となった状態」です。

f:id:weseek:20191105181121p:plain

図のように、Transaction A が Michael のニックネームを Mick から Mike へ変更したことにより、Transaction B がロールバックする際に Mick に戻すべきか、Mike へ戻すべきか判断できない状態となります。

ロストアップデート(Lost Update)

ロストアップデートとは「とあるトランザクションが書き込んだ値が、他のトランザクションにより上書きされた状態」です。

f:id:weseek:20191105181144p:plain

図のように、Transaction A が Michael のニックネームを Mick から Mike へ変更し、その後に Transaction B が Michael のニックネームを Mick から Mickey へ変更する操作を行うと、Transaction A の更新が失われてしまいます。

ダーティリード

ダーティリードとは「とあるトランザクションが更新した値 A' を他のトランザクションが参照した後に、更新された値がロールバックされると、読み取った値 A' がロールバックされずにトランザクションに利用されることになってしまった状態」です。

f:id:weseek:20191105181210p:plain

図のように、Transaction A がコミットする前に Michael のニックネームを Mick から Mike へ変更した内容をTransaction B が読み取ってしまうと、Transaction A がロールバックしても Transaction B はニックネーム Mike を保持し続けてしまいます。

ファジーリード / 非再現リード / ノンリピータブルリード

ファジーリードとは「とあるトランザクションが読み込んだ値 A が、他のトランザクションにより A' に更新されてコミットされると、値 A が二度と呼び出せなくなってしまった状態」です。

ダーティリードを防ぐために、他のトランザクションがコミットする前のエンティティは参照できないようにしても発生します。

f:id:weseek:20191105181233p:plain

図のように、Transaction A が処理中に読み込んだエンティティは Michael の Age が 12 であった値が、Transaction B によって Age が 13 に変更された上でコミットされると、再度 Transaction A が読み取った値は Age 13 になります。

ファントムリード

ファントムリードとは「とあるトランザクションがテーブルを読み込んだ後に、他のトランザクションによりエンティティが挿入された場合、再度テーブルを読み込むと挿入されたエンティティが参照できてしまう状態」です。

ファジーリードを防ぐために、トランザクション処理中に読み取ったエンティティの値は常に同じとなるようにしても発生します。

f:id:weseek:20191105181257p:plain

図のように、Transaction B は Prize winners テーブルから読み取ったレコード数は 2 つであるため、David を当選者へ追加しようとしますが、Transaction A により Charlie が当選者に追加された上でコミットされると、Transaction B では新しい当選者 Charlie が追加されたテーブルが読み込まれるようになります。

トランザクションの分離レベル

データの不整合が発生するケースを紹介しましたが、極端な話ではトランザクションを並列で実行せずに直列(シリアル) に実行することで全てのケースを防ぐことが出来ます。

しかしそれでは非効率なので、次に示すトランザクションの分離レベルが ANSI/ISO 標準 SQL によって定義されています。(前回記事で紹介した通り、これらの分離レベルは MySQL 等多くの DBMS で実装されています)

  • READ UNCOMMITTED
    • コミットされていない値も読み取ることが出来るよう分離する
  • READ COMMITTED
    • 読み取った値は必ずコミットされた値となるよう分離する
  • REPEATABLE READ
    • とあるトランザクション処理の間、同じエンティティであればいつ読み取っても同じ値となるよう分離する
  • SERIALIZABLE
    • 複数のトランザクション処理結果が、シリアルに実行された場合と同じ結果になるよう分離する

それぞれの分離レベルにおいてデータ不整合発生可否をまとめると次のようになります。

分離レベル Dirty Write Dirty Read Non-Repeatable Read Phantom Read
READ UNCOMMITTED × × ×
READ COMMITTED × ×
REPEATABLE READ ×
SERIALIZABLE

<凡例>×…発生する、〇…発生しない

一方で、論文「A critique of ansi sql isolation levels」では上記の ANSI/ISO SQL 標準における分離レベルを再定義・拡張させた分離レベルが定義されています。
(参考: A critique of ansi sql isolation levels 解説公開用)

この定義の中では ANSI/ISO 標準 SQLトランザクション分離レベルを明確に定義し、それらの分離レベルでは防ぐことのできないデータ不整合が発生する状態を追加し、それらに含むトランザクション分離レベル毎の発生可否を整理しています。

新たに追加されたトランザクション分離レベルは次の 2 つであり、Snapshot Isolation が Serialize に近い分離レベルを保つことができ、並列で実行することのできるレベルであると述べています。(Snapshot Isolation は InterBase, Firebird, Oracle, PostgreSQL, SQL Anywhere, MongoDB, Microsoft SQL Server (2005 and later) で実装されています。但し、Oracle では Snapshot Isolation を Serializable と呼ぶなど、DBMS によって分離レベル名が異なっていたりするようです。※参考)

  • Cursor Stability
    • SQL カーソルにおけるロック動作を踏まえた拡張により、READ COMMITTED では解決できない問題を防げるよう分離する
  • Snapshot Isolation
    • とある時点において取得したスナップショットに対してトランザクション操作を行うことで、ファントムリードを防げるよう分離する

分離レベルの再定義は割愛して、トランザクションの分離レベルとデータの不整合発生可否をまとめると次のようになります。

分離レベル Dirty Write Dirty Read Lost Update Non-Repeatable Read Phantom Read Read Skew Write Skew
READ UNCOMMITTED × × × × × ×
READ COMMITTED × × × × ×
Cursor Stability × ×
REPEATABLE READ ×
Snapshot Isolation ×
SERIALIZABLE

<凡例>×…発生する、〇…発生しない、△…一部発生する

分離レベルの選択

トランザクションの分離レベルの種類と、それに応じて防ぐことのできるデータ整合性について紹介しましたが、結論としてはどのような目的・環境においても最適となる分離レベルは存在せず、可用性と性能のトレードオフで選択することになります。

参考までに、いくつかの DBMS におけるデフォルトの分離レベルを紹介します。

DBMS デフォルトの分離レベル 参考情報
MySQL 8.0(5.6も同じ)
(InnoDB)
REPEATABLE READ 8.0, 5.6
PostgreSQL 10(9.6も同じ) READ COMMITTED 9.6, 10
Oracle Database 18c(12cも同じ) READ COMMITTED 18c, 12c
Microsoft SQL Server 2017(2016も同じ) READ COMMITTED 2017, 2016
MongoDB 4.0 READ UNCOMMITTED 4.0
※v4.0からマルチドキュメントのトランザクションが対応された

ロックの有効期間と楽観的アプローチ

トランザクションの分離レベル以外の観点として、ロックの有効期間の違いによってもデータの不整合状態を防げる可能性の違いと性能の違いが生まれます。

例えばトランザクションが開始されてから終了するまでの間ずっとロックを行うことで不整合を防げる可能性は高まりますが、ロックが解放されるまでの待ち時間が増えることになります。

一方で、読込・更新操作の間だけロックをかけることで待ち時間は少なくなりますが、今度は不整合が発生する可能性が高まります。

そこで、不整合が発生するような更新が同時に行われる頻度によるアプローチの違いを紹介します。

悲観的ロック(Pessimistic Locking)

概要

悲観的ロックは更新が同時に行われる頻度が高いことを想定しており、読込・更新処理が開始された時点で他の処理を排除するロック方式です。

アプリケーションレベルでも悲観的ロックを行うことは可能ですが、一般的に DB レベルで行なわれます。

書き込み操作が主に行われる用途に対して適したロック方式です。

デメリット

悲観的ロックはロックが解除されるまでトランザクションの待ち時間が長く発生する可能性があること、明示的な開放が必要となることから、読み取り操作が主であり、ステートレスな通信である HTTP 等との相性が悪い(ロックかけっぱなしが発生しうる)とされています。

楽観的ロック(Optimistic Locking)

概要

楽観的ロックはレコードに対する書き込みを禁止するためのロック方式の 1 つです。

更新が同時に行われる頻度は低いだろうという楽観的な考えに基づくロック方式です。 ロックと言いつつも、データに対してのロックは行わずに競合の検証のみを行います。

悲観的ロックの対となる方式です。

ロックの仕組み

楽観的ロックは ActiveRecord や GORM 等、O/R マッパーによりアプリケーションレベルで実装されています。

ロックをかける実装の単位としては、エンティティ単位(RDB におけるテーブルの 1 レコード単位) であることが一般的のようです。 (単位は O/R マッパーの実装に依存するとは思いますが、そもそも同時更新が行われる頻度が低い前提なので、カラム単位で設定するメリットが少ないのだと思います)

ロックの仕組みは次のとおり単純なものです。

  1. エンティティを更新する前にエンティティ毎に設定したバージョンを読み取る
  2. エンティティの更新処理が完了したらバージョンが読み取った時から変わっていないか検証する
  3. 【バージョン変化なしの場合】
    競合がなかったと判断して、バージョンをカウントアップしてエンティティの更新処理を行う
  4. 【バージョン変化ありの場合】
    競合が発生したと判断して、トランザクション処理を失敗させる

ここで、上記説明の中で事前の定義なく「バージョン(を示すカラム)」と記載しましたが、楽観ロックを使うためにはテーブルのレコードにバージョンを示すカラムを用意する必要があります。

このカラムは DB のテーブル作成/マイグレーション時に必要であり、O/R マッパーの使い方によって具体的な方法は変わりますが、例えば Ruby on RailsActiveRecord ではマイグレーションファイルでモデルに lock_version カラムを追加するマイグレーションファイルを作成してマイグレーションを実行することになります。

ロック動作のシーケンス

下記にロック未使用時と楽観的ロック使用時のシーケンス図を示します。

ロック未使用時

f:id:weseek:20191105181328p:plain
ロストアップデート状態となる

楽観的ロック使用時

f:id:weseek:20191105181350p:plain
更新処理が競合したことを検出してトランザクション B が失敗する

図のように、楽観的ロック使用時には更新処理が競合したことが検出され、トランザクション B は失敗して Optimistic Lock Exception(OLE) が発生します。

このように、楽観的ロックはエンティティに対するロックは行わず、更新処理の開始時点とコミット時点のバージョンを比較することで更新処理が競合したことを検証します。

防ぐことのできるデータ不整合

楽観的ロックではダーティリードの発生を防ぐことが出来ますが、ファジーリード、ファントムリードは防ぐことができません。

メリット

悲観的ロックに比べてロック待ちが発生しない分、トランザクション完了までの時間は短くなります。 また、ロックによるブロックと解放(Two phase locking) を行う必要がありません。

従って、高速な応答が求められ、ステートレスである HTTP と相性が良いとされています。

デメリット

潜在的に、ロック待ちが発生しない分、不整合が発生する可能性が高まるデメリットが存在します。

但し、前提として更新が同時に行われる頻度が少ない環境を想定していることから、このデメリットは無視できます。

また、楽観的ロックは DB レベルで提供される機能ではないため、アプリケーション側でバージョンフィールドを用意して、バージョン比較による検証や、バージョン書き込み、OLE の発生などを実装する必要があります。

但し、先に述べたように O/R マッパーにより楽観的ロック機能が提供されるため、O/R マッパーを使う限りはこのデメリットも無視できます。

まとめ

データの不整合が発生した状態は多く存在し、トランザクションの分離レベルを可用性と効率とのトレードオフで選択する必要があることを紹介しました。

また、楽観的な考えに基づくアプローチである楽観的ロックについて紹介し、高速な読み取り処理が必要とされるステートレスな処理である HTTP において有効であると言えることを紹介しました。

次回は楽観的ロックを実装したフレームワークや O/R マッパーについて具体例を紹介していきたいと思います。

関連記事

tech.weseek.co.jp

Kubernetes 時代の CI/CD「Jenkins X」とは? 〜中編 〜

Kubernetes 時代の CI/CD「Jenkins X」とは? 〜前編〜 で予告した通り、本記事では実際に Jenkins X をインストールし、どのような構成でクラスタが構築されるのかを見ていきたいと思います。

前提

本記事では、GKE (Google Kubernetes Engine) へ Jenkins X をデプロイする手順を紹介していきますので、まず Jenkins X をインストールする前に以下の事項を確認してください。

  1. GKEクラスタを作成できるアカウント・権限を持っていること
  2. MacOSLinux などのターミナル環境上で gcloud, kubectl コマンドがインストールされていること
  3. gcloud コマンドで 1. のアカウントでコマンドが実行できる状態になっている
    • gcloud auth login でログインている状態であれば問題ありません
    • 2., 3. については、Google Cloud Console 上で提供されている Google Cloud Shell を使うことで代替することもできます

この辺については、以下の記事が参考になります。

また、本記事では Git Provider として Jenkins X インストール時のデフォルト設定である GitHub を利用します。jx コマンドは、Jenkins X のインストール途中で、構築した Kubernetes クラスタ上に staging/production 環境としてアプリケーションを起動できるようにするための情報を GitHub 上のリポジトリとして登録するため、GitHub アカウントが必要となります。

インストールしてみよう

jx のインストール

jx コマンドとは、Jenkins X のインストールやプロジェクト管理などを行える、CLI ツールです。Jenkins X を使い始めるためには、まずこのツールを用意する必要があります。

jx コマンドをインストールするには、gcloud/kubectl が揃っている環境で以下を実行します。

$ curl -L https://github.com/jenkins-x/jx/releases/download/v1.3.167/jx-linux-amd64.tar.gz | tar xzv 
$ sudo mv jx /usr/local/bin
  • 1.3.167 の部分については、jx コマンドのリリースページから最新のものを選択してください

上記を実行すると、システム上で jx コマンドを実行することができるようになります。

ついでに、bashzsh の Tab キーを用いた補完機能にも対応させてみましょう。 Ubuntubash 環境下では以下を実行してログインしなおすと、jx コマンドと打った後に Tab キーを押すとコマンド候補が表示され、便利です。

$ jx completion bash | sudo tee /etc/bash_completion.d/jx
$ exit
(再度ログインする)
$ jx [Tab]
cloudbees    compliance   context      create       edit         gc           import       install      namespace    preview      prompt       rsh          start        step         sync         uninstall    upgrade
completion   console      controller   delete       environment  get          init         logs         open         promote      repository   shell        status       stop         team         update       version

Kubernetes クラスタの構築

それでは、クラスタを構築していきましょう。

まず、以下のコマンドを打ちます。

$ jx create cluster gke --skip-login

すると、以下のように GCP 上のプロジェクト選択肢が表示されますので、クラスタを構築するプロジェクトを矢印キーで選択して Enter を押します。

? Google Cloud Project:  [Use arrows to move, type to filter, ? for more help]
  project-a
  project-b
❯ project-c

いくつか jx 側で処理が実行されたのちに、以下のようなゾーン選択画面が表示されます。 東京リージョンであれば、asia-northeast1 のいずれかのリージョンを選択しましょう。

? Google Cloud Zone:  [Use arrows to move, type to filter, ? for more help]
  asia-east1-a
  asia-east1-b
  asia-east1-c
❯ asia-northeast1-a
  asia-northeast1-b
  asia-northeast1-c
  asia-south1-a
  asia-south1-b
  asia-south1-c
  asia-southeast1-a

次に、起動するマシンタイプを選択画面が表示されます。Jenkins X では、最小でも n1-standard-2 を推奨しているようですので、n1-standard-2 を選択します。 (「?」キーを押すと、選択肢に関する情報が出てきます)

ⓘ We recommend a minimum of n1-standard-2 for Jenkins X,  a table of machine descriptions can be found here https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture
? Google Cloud Machine Type:  [Use arrows to move, type to filter]
  g1-small
  n1-standard-1
❯ n1-standard-2
  n1-standard-4
  n1-standard-8
  n1-standard-16
  n1-standard-32
  n1-standard-64
  n1-standard-96
  n1-highmem-2

次に、Kubernetes Node 最小数・最大数を入力します。Jenkins X では 3 Node を推奨していますが、今回はお試しのため最小 1・最大 2 に設定します。

ⓘ We recommend a minimum of 3 for Jenkins X,  the minimum number of nodes to be created in each of the cluster's zones
? Minimum number of Nodes (3)
? Maximum number of Nodes [? for help] (5)

ここまで入力すると、 Creating cluster... と表示され、実際にクラスタの構築が開始されます。(数分で終わると思います)

GKE 管理画面 を確認すると、クラスタが構築されている様子を確認できると思います。

Kubernetes クラスタの設定

クラスタ作成が完了すると、jx はすかさずクラスタの準備に取り掛かってくれます。 この準備作業では、Kubernetes クラスタ上で Jenkins X が稼働するために必要となる Role/ServiceAccount の用意や、helm のインストールが行われます。

Initialising cluster ...
Git configured for user: Hoge Hogeo and email *******
Trying to create ClusterRoleBinding **********-cluster-admin-binding for role: cluster-admin for user *******
: clusterrolebindings.rbac.authorization.k8s.io "**********-cluster-admin-binding" not foundCreated ClusterRoleBinding **********-cluster-admin-binding
Created ServiceAccount tiller in namespace kube-system
Trying to create ClusterRoleBinding tiller for role: cluster-admin and ServiceAccount: kube-system/tiller
Created ClusterRoleBinding tiller
Initialising helm using ServiceAccount tiller in namespace kube-system
helm installed and configured

それが終わると、次は以下のような質問を聞かれます。ingress controller とは、Kubernetes クラスタ内で稼働するアプリケーションを、外部に公開するために必要なアプリケーションのことです。今回は、Jenkins X をインターネットからアクセスできるようにするため、 y と入力しましょう。

(GKE の場合は本来 ingress-gce が稼働しているはずなので、nginx-ingress-controller は不要なはずですが、ここで n を選択するとクラスタ上にインストールされたサービスへのアクセス経路がなぜか設定されないため、ここでは y とします)

? No existing ingress controller found in the kube-system namespace, shall we install one? [? for help] (Y/n)

入力すると、ingress controller のインストールが行われた後、構築した Kubernetes クラスタに対してアクセスするためのドメイン名について聞かれますが、今回はそのまま [Enter] を押します。

Waiting for external loadbalancer to be created and update the nginx-ingress-controller service in kube-system namespace
Note: this loadbalancer will fail to be provisioned if you have insufficient quotas, this can happen easily on a GKE free account. To view quotas run: gcloud compute project-info describe
External loadbalancer created
Waiting to find the external host name of the ingress controller Service in namespace kube-system with name jxing-nginx-ingress-controller
You can now configure a wildcard DNS pointing to the new loadbalancer address XXX.XXX.XXX.XXX

If you do not have a custom domain setup yet, Ingress rules will be set for magic dns nip.io.
Once you have a customer domain ready, you can update with the command jx upgrade ingress --cluster
If you don't have a wildcard DNS setup then setup a new CNAME and point it at: XXX.XXX.XXX.XXX.nip.io then use the DNS domain in the next input...
? Domain [? for help] (XXX.XXX.XXX.XXX.nip.io)

Jenkins X のインストール

ここまで完了すると、次はいよいよ Jenkins X のインストールです。

GitHub アカウント名について聞かれるので、前提で用意した GitHub アカウント名を入力します。

Lets set up a git username and API token to be able to perform CI/CD

? GitHub username for CI/CD pipelines:

次に、API token を発行するよう表示されるので、出力された URL へブラウザでアクセスして token を作成し、作成した token を入力しましょう。

To be able to create a repository on GitHub we need an API Token
Please click this URL https://github.com/settings/tokens/new?scopes=repo,read:user,read:org,user:email,write:repo_hook,delete_repo

Then COPY the token and enter in into the form below:

? API Token:

入力すると以下のように、Jenkins X が予め用意したクラウド環境向けの設定が入ったリポジトリが手元にクローンされ、Jenkins X のインストールが開始されます。

Cloning the Jenkins X cloud environments repo to /home/hoge/.jx/cloud-environments
Counting objects: 805, done.
Compressing objects: 100% (6/6), done.
Total 805 (delta 2), reused 6 (delta 2), pack-reused 797
Generated helm values /home/hoge/.jx/extraValues.yaml
Installing Jenkins X platform helm chart from: /home/hoge/.jx/cloud-environments/env-gke
waiting for install to be ready, if this is the first time then it will take a while to download images

ここまでメッセージが出ると少し時間がかかります。(10分程度)

やがて、インストールが終わると、以下のようなメッセージが続いて出力されます。指示の通り URL へアクセスし、admin/表示されているパスワードで Jenkins へログインしてください。ログイン後、ページ内に「APIトークンを表示」というボタンがありますので、それを押して出てきた API トークンを入力してください。

Jenkins X deployments ready in namespace jx


        ********************************************************

             NOTE: Your admin password is: XXXXXXXX

        ********************************************************

Getting Jenkins API Token
using url http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/me/configure
unable to automatically find API token with chromedp using URL http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/me/configure
Please go to http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/me/configure and click Show API Token to get your API Token
Then COPY the token and enter in into the form below:

? API Token:

API token を入力し終わると、staging/production 環境用の設定ファイルが置かれるリポジトリGitHub 上に作成されます。

Created user admin API Token for Jenkins server jenkins.jx.XXX.XXX.XXX.XXX.nip.io at http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io
Updating Jenkins with new external URL details http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io
Creating default staging and production environments
Using git provider GitHub at https://github.com


About to create repository environment-speakerglow-staging on server https://github.com with user XXXXXX


Creating repository XXXXXX/environment-speakerglow-staging
Creating git repository XXXXXX/environment-speakerglow-staging
Pushed git repository to https://github.com/XXXXXX/environment-speakerglow-staging

Created environment staging
Created Jenkins Project: http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/job/XXXXXX/job/environment-speakerglow-staging/

Note that your first pipeline may take a few minutes to start while the necessary images get downloaded!

Creating github webhook for XXXXXX/environment-speakerglow-staging for url http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/github-webhook/
Using git provider GitHub at https://github.com


About to create repository environment-speakerglow-production on server https://github.com with user XXXXXX


Creating repository XXXXXX/environment-speakerglow-production
Creating git repository XXXXXX/environment-speakerglow-production
Pushed git repository to https://github.com/XXXXXX/environment-speakerglow-production

Created environment production
Created Jenkins Project: http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/job/XXXXXX/job/environment-speakerglow-production/

Note that your first pipeline may take a few minutes to start while the necessary images get downloaded!

Creating github webhook for XXXXXX/environment-speakerglow-production for url http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/github-webhook/

ここで作成される GitHub リポジトリには、今回インストールした Jenkins X の webhook 用エンドポイントにアクセスするような webhook 設定が予め追加されます。 つまり、このリポジトリに対して push すると、自動的に Jenkins X 上のジョブが実行されるように設定されます。

Jenkins X installation completed successfully


        ********************************************************

             NOTE: Your admin password is: XXXXXXXX

        ********************************************************


Your kubernetes context is now set to the namespace: jx
To switch back to your original namespace use: jx ns default
For help on switching contexts see: https://jenkins-x.io/developing/kube-context/

To import existing projects into Jenkins:       jx import
To create a new Spring Boot microservice:       jx create spring -d web -d actuator
To create a new microservice from a quickstart: jx create quickstart

そして、ここまでで、インストールは完了です!お疲れさまでした!!

Jenkins X へのアクセス

インストール完了時に出るメッセージ中に記載された http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io/ にアクセスすると、Jenkins X へアクセスすることができます。

アクセスすると、ユーザ名・パスワードを求められますが、ユーザ名は admin、パスワードはインストール完了後に出てくるパスワードを入力すればログインできます。

もし万が一パスワードを忘れたとしても、jx コマンドで構築した際に以下のディレクトリに設定ファイルが残っていますので、そのファイルを見ることでパスワードを確認できます。

$ cat ~/.jx/jenkinsAuth.yaml
servers:
- url: http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io
  users:
  - username: admin
    apitoken: XXXXXXXXXX
    bearertoken: ""
    password: XXXXXXXXXX
    ^^^^^^^^^^^^^^^^^^^^ これが admin のパスワードです
  name: jenkins.jx.XXX.XXX.XXX.XXX.nip.io
  kind: ""
  currentuser: ""
defaultusername: ""
currentserver: http://jenkins.jx.XXX.XXX.XXX.XXX.nip.io

インストール後の Jenkins X・その他コンポーネントの構成

jx コマンドでクラスタ構築が完了した後、クラスタ上では以下の様な Pod が稼働し、Ingress 経由でサービス公開されます。

$ kubectl get pod --all-namespaces
NAMESPACE     NAME                                                   READY     STATUS      RESTARTS   AGE
jx            jenkins-6dc7ff9cfd-f28gf                               1/1       Running     0          2d
jx            jenkins-x-chartmuseum-7b95b777b-r7qhs                  1/1       Running     0          2d
jx            jenkins-x-docker-registry-dcb6d6d44-w9c6q              1/1       Running     0          2d
jx            jenkins-x-gc-activities-1534714200-rwm2p               0/1       Completed   0          4h
jx            jenkins-x-gc-activities-1534723200-bcdwj               0/1       Completed   0          2h
jx            jenkins-x-gc-activities-1534725000-5pk5c               0/1       Completed   0          1h
jx            jenkins-x-gc-previews-1534701600-bqcbn                 0/1       Completed   0          8h
jx            jenkins-x-gc-previews-1534712400-5kd6j                 0/1       Completed   0          5h
jx            jenkins-x-gc-previews-1534723200-wprdp                 0/1       Completed   0          2h
jx            jenkins-x-heapster-96bd95dcf-bgxps                     2/2       Running     0          2d
jx            jenkins-x-mongodb-6c84f866dc-hp2bc                     1/1       Running     0          2d
jx            jenkins-x-monocular-api-8499d45598-s5gxn               1/1       Running     3          2d
jx            jenkins-x-monocular-prerender-6d8897856-x87lh          1/1       Running     0          2d
jx            jenkins-x-monocular-ui-967d8d8d9-h7h6p                 1/1       Running     1          2d
jx            jenkins-x-nexus-7c6c4579fd-9jws7                       1/1       Running     0          2d
jx            pipelinecontroller-5dbc96bbd4-hmrsr                    1/1       Running     0          2d
kube-system   event-exporter-v0.1.9-5c8fb98cdb-zrbs8                 2/2       Running     0          2d
kube-system   fluentd-gcp-v2.0.17-gsw5d                              2/2       Running     0          2d
kube-system   fluentd-gcp-v2.0.17-pbdd6                              2/2       Running     0          24m
kube-system   heapster-v1.5.2-5fc46bc685-mz9bj                       3/3       Running     0          2d
kube-system   jxing-nginx-ingress-controller-86c769695f-ndkw4        1/1       Running     0          2d
kube-system   jxing-nginx-ingress-default-backend-5dbcb4b48b-csjzn   1/1       Running     0          2d
kube-system   kube-dns-5dcfcbf5fb-9z8bm                              4/4       Running     0          2d
kube-system   kube-dns-5dcfcbf5fb-9zbkr                              4/4       Running     0          24m
kube-system   kube-dns-autoscaler-69c5cbdcdd-kgtm8                   1/1       Running     0          2d
kube-system   kube-proxy-gke-speakerglow-pool-1-6c92678d-cjxx        1/1       Running     0          2d
kube-system   kube-proxy-gke-speakerglow-pool-1-6c92678d-j1c2        1/1       Running     0          24m
kube-system   kubernetes-dashboard-bf9c699db-64df7                   1/1       Running     0          2d
kube-system   l7-default-backend-57856c5f55-8r9dx                    1/1       Running     0          2d
kube-system   metrics-server-v0.2.1-7f8dd98c8f-9jlrf                 2/2       Running     0          2d
kube-system   tiller-deploy-5cd7c76b66-pph9h                         1/1       Running     0          22m

$ kubectl get ing --all-namespaces
NAMESPACE   NAME              HOSTS                                       ADDRESS          PORTS     AGE
jx          chartmuseum       chartmuseum.jx.XXX.XXX.XXX.XXX.nip.io       XXX.XXX.XXX.XXX   80        2d
jx          docker-registry   docker-registry.jx.XXX.XXX.XXX.XXX.nip.io   XXX.XXX.XXX.XXX   80        2d
jx          jenkins           jenkins.jx.XXX.XXX.XXX.XXX.nip.io           XXX.XXX.XXX.XXX   80        2d
jx          monocular         monocular.jx.XXX.XXX.XXX.XXX.nip.io         XXX.XXX.XXX.XXX   80        2d
jx          nexus             nexus.jx.XXX.XXX.XXX.XXX.nip.io             XXX.XXX.XXX.XXX   80        2d

簡単に見ていくと、稼働しているソフトウェアの概要は以下です。

まとめ

本記事では、Jenkins X をインストールするために必要な jx ツールをインストールし、実際に GKE 上に Jenkins X が稼働するクラスタを構築するステップをご紹介しました。

次回は、構築した Jenkins X を利用し、サンプルとして用意されている quickstart プロジェクトを立ち上げて、Jenkins X がどのように CI/CD を実施するのかを見ていく予定です

関連記事

tech.weseek.co.jp

GROWI のユーザーズガイドをリニューアルしたお話

WESEEK の kouki です。GitHub 個人アカウントは kaishuu0123 で活動しています。

今回は SEROKU からの引用ではなく、オリジナル記事として「GROWI のユーザーズガイドをリニューアルした話」を書かせていただこうと思います。

ドキュメントサイトはこちらです: https://docs.growi.org

リニューアルしようとしたモチベーション

WESEEK では最近 GROWI.cloud をリリースいたしました。

growi.cloud

GROWI.cloud リリース以前では、Geek な方たちが GROWI を触っているという状況で、Issue も上げてもらいつつ、ポジティブなご意見をいただいて、特定のクラスタで盛り上がりを見せていました。

しかし、今後 GROWI.cloud と共に GROWI のコミュニティを広めていくには Geek な方も含め、 GROWI でページを書く側に寄り添ったドキュメントが必要になるだろうと考えました。

そこで、ユーザーズガイドのリニューアル提案に至ったわけです。

高尚なことを掲げていますが、正直なところ 「GROWI の機能一覧を紹介する場所を自分が作りたかった」というのが本音だったりします。 「こんな機能があったんだ~」と後になって気付くより、俯瞰して機能を把握する方が使っていて楽しいので。

提案からリニューアルまで

提案フェーズ

まずはメインコミッターである弊社武井に対して、提案ページを見せるところから始めました。

dev.growi.org

ついでに一応 GROWI はオープンなものなので、自身のアカウントでも呟いてみたりもしました。

そこから意外にもサラッと OK をもらいました。(口頭でですが)

リニューアルフェーズ

ここからは単純作業ですが、スクショを撮って、ガリガリとドキュメントを書いていきます。 リニューアル以前は「管理者ガイド」相当は無かったので、まずは以前の「ユーザーズガイド」を「管理者ガイド」に移動し、 中のコンテンツを充実させていく作業を進めました。

変更が大きくなるため、作業途中の様子を見てもらうために、タイトルに [WIP] (Work In Progress) という文字列を付与して作業していました。 実際の PR は下記の URL です。

github.com

ファイル変更が 160 と多いですが、ほぼほぼリネームです。 主要な変更点は /guide/ 以下にドキュメントを追加する作業でした。

無事マージ

PR を送ってから、slack の GROWI workspace 上で改善点を指摘してもらって、すぐにマージされました。

f:id:weseek:20191028182549p:plain

今後考えていること

まだ構想段階ですが、下記のような流れで情報をまとめていけたら、と考えています。(お手伝いいただける方募集中です)

  1. GROWI 本体から GROWI Docs (https://docs.growi.org) へのリンクを追加
  2. github にある weseek/growi の README.md に掲載されている情報、wiki を docs.growi.org にまとめていく
  3. growi.org も併せた情報の整備 (i18n 対応)
  4. 海外の方に知っていただきたいので、もっと発信は増やしていきたい

最後に

正直、独断と偏見でユーザーズガイドを用意したので、「読みにくい」とか「大して役に立たないのではないか」など不安はありますが、今まで機能を紹介する場所が無かったので、その場所を作ることができたことは個人的に満足しています。

多少なりとも GROWI を使ってくださっている方、これから GROWI を使おうとしている方に GROWI docs を一目でも見ていただけたら嬉しいです。

Slack の GROWI workspaceWESEEK の Twitter アカウント などでご意見、ご感想をお待ちしています。

蛇足

しれっと awesome-vuepress にも PR しておきました。

github.com

自作で Video Chat 環境を作って導入した話〜選定編〜

こちらは 「自作でVideo Chat環境を作って導入した話〜選定編〜 」からの転載です。



「SEROKU フリーランス(以下、SEROKU)」の中の人をやっている ryosuke です。 前回の経緯編に続き、今回は Video Chat サービスや機器の選定についてお伝えします。

Video Chatサービス比較

さて、使用する Video Chat 用のプロダクトを用意する必要があります。

以前はビデオ会議システムといえば Polycom 社製品などを代表する、高価な専用のハードウェアを購入したり、ビデオ会議システム用の SIP, H.323, RTP といった特殊なプロトコルの通信ができるようにファイアウォールの設定が必要だったりと、コストと手間がかかる手段が大半を占めていました。しかし最近では普通の PC とカメラ、マイク、スピーカさえそろえば、あとは適当なブラウザベースの Video chat サービスを利用すれば、だれでもすぐに簡単に高品質な Video Chat サービスができるようになっています。ラップトップ PC ならばカメラ、マイク、スピーカも内蔵されているものが多くありますから、より手軽に利用できますね。

今回はできるだけ安価に環境を整えたいのもあり、PC で使える VideoChat サービスを利用する方針としました。

では、利用する Video Chat サービスを選定しましょう。選定の条件として下記の 4 つの観点を挙げました。

  1. 長時間接続ができる
    • 業務中のコミュニケーション手段として利用したいので、会議の時に限らず業務時間中は常時接続した状態で運用しようと考えていました。話しかけたいときに毎回接続操作をすることなく、すぐに会話ができる状態にするのが理想だからです。業務時間中に接続し続けることを考えると、 8 時間程度は連続利用ができることが要求されます。
  2. 多人数が参加できる
    • 現在は 2 拠点間で使用できれば良いですが、将来拠点が増える可能性も考慮し、複数人での Group Video Chat ができることが望ましいです。また、Group Video Chat ができるサービスを選択すれば、例えばある日に在宅ワークをする社員がいたとしても、その社員も Video Chat に参加できれば現状よりコミュニケーションロスを減らすことが期待できます。
  3. 画面共有ができる
    • 会議などでは共通の画面を見ながら話を進める場面が多々あります。また、それ以外の業務においても遠隔地と画面を共有して、実際に行っている操作を見せながら話を進める様な使い方もできそうです。せっかく PC で使える Video Chat サービスを選定しているので、この機能も欲しいところです。
  4. 実質無料で利用できる
    • Video Chat サービスには無料で利用できるものが数多くありますが、最も使える機能が少ないプランが無料で、そのほかの付加価値の高い機能を使おうとするとサービスの使用料が必要な(いわゆるフリーミアム)ビジネスモデルを採用しているものが多いです。もちろん実際にかかるコスト次第ではありますが、可能であれば上記に挙げた条件をクリアしつつ、無料で利用できると嬉しいです。

この条件を基にいくつかの Video Chat できるサービスを比較し、以下にまとめました。

サービス 長時間接続 多人数 画面共有 料金(左記要件を利用する場合)
Skype △※1 25人まで 10人まで 無料
Hangouts 25人まで 無料
appear.in 4人まで 無料
Slack 15人まで ¥850/月/人
ChatWork × 無料

※1 グループ通話の場合、最長時間に制限がある (グループビデオ通話公正使用ポリシー) ※2 無料版だと画面共有時に自動的にフルスクリーン表示をしない制限がある

比較したところ、Hangouts と appear.in であれば無料で要件を満たすことができます。Hangouts の方がより大人数で同時に接続できるようですので、今回は Hangouts を採用することにしました。

なぜ chromebox for meeting を買わないのか?

Hangouts を採用することにしましたが、実は Google は Hangouts を会社のテレビ会議システムとして利用するためのソリューションを提供しています。 Chrome devices for meetings という名前で複数の代理店を経由して PC、カメラ、マイク、スピーカなどハードウェアを含めて購入することができます。これを使えば簡単に Hangouts を使う環境が整いますが、今回は採用しませんでした。

その理由ですが、安価ではないという点です。手ごろなプランでも1台当たり初期設備費が約 11.5 万円となるだけでなく、年間保守費が約 3.5 万で、初年度を含め 1 年ごとに保守費用が発生します。これでも大企業が導入するような専用システムと比べればかなり安いほうなのですが、「自前で PC を揃えれば使える」と考えている我々にとっては割高になってしまいます。

そのため、今回はコストを抑えつつ Hangouts を使うのに十分なスペックを揃えた PC と周辺機器を別途調達することにしました。

機器選定・構築

PC の調達ですが、 WESEEK では普段使用している PC を自作(組み立てだけ外注)しているため、今回も自作することにしました。 PC のスペックについては下記の点を考慮して選定しました。

  • Hangouts が快適にできるスペック
  • OS は Windows である必要はない
    • PCを自作する場合、Windows だと OS を購入するコストも考慮しなければならなくなるので、買わずに済むならそちらの方がよいです。 Hangouts はブラウザである Google Chrome 上で動作するサービスのため、Chrome さえ動作すれば OS には制約がありません。今回は無料で利用できる Linux の主要なディストリビューションの一つである Ubuntu Desktop を採用することにしました。
  • 常時使用している状態でも冷却を維持しつつ比較的静かである
    • 一日中使用することになるため、PC の騒音はできるだけ小さく抑えたいところです。かといって闇雲にファンレス構成にしてしまうのも問題があります。実はこの PC 構築を検討する前に、一時的に既存の安価なラップトップ PC で Hangouts を試用していました。しかし、しばらく使用し続けていると、映像がコマ落ちしたり音声がぶつぶつと切れてしまう問題が発生していました。原因を調査してみると Hangouts を使用し続けたためにPCの冷却が追い付かず、Thermal throttling という CPU の性能を低下させて冷却を間に合わせている状態になっていたためということが分かりました。同じ問題を起こさないためにも十分に冷却しつつできるだけ静音であることを目指します。
  • まあまあ安い
    • Hangouts 専用機にするため、その他のアプリケーションを同時に利用することはありません。過剰なスペックにする必要はないのでその分安価に仕上げることを考えます。
  • エコーキャンセラーのついたスピーカーマイクを使う
    • Hangouts 自身にもエコーキャンセル機能がついていますが、専用のハードウェアで処理できたほうが品質向上が期待できるため、エコーキャンセラーのついたスピーカーマイクを使用することを検討します。
  • できるだけ大勢が画面に映るようにする
    • 常時接続していつでも話しかけられるような運用を想定しているため、オフィスにいる多くの人が何をしているか見渡せるぐらい広角に撮ることができるカメラが望ましいです。

これを基準に選定した結果が下記のとおりです。

部品 品名
CPU Intel Pentium Dual-Core G4560
RAM 4GB
Storage SSD 32GB SATA3.0
MotherBoard Intel H370搭載 Mini ITXマザーボード
電源 SFX 300W 80PLUS:Bronze
ケース 筒形状の Mac Proと似たケース
ディスプレイ 27inch Full HD ディスプレイ
カメラ 視野角120度 Full HDカメラ (Buffalo BSW200MBK)
スピーカマイク エコーキャンセラー搭載WEB会議小型スピーカーフォン (サンワサプライ MM-MC28 )

この構成で 1 セット当たり合計 7 万円強で調達ができました。

まとめ

いかがでしたか?今回は Video Chat サービスと使用機器の選定についてお話ししました。Video Chat サービスの導入を検討している方にご参考になれば幸いです。

次回は hangouts をベースにもう少し WESEEK 社での用途向きに使いやすくした話と、失敗談、今後の展望についてお伝えする予定です。

関連記事

tech.weseek.co.jp

社内 Kubernetes トラブルシュート 前編

こちらは 「SEROKUを支える技術〜社内 Kubernetes トラブルシュート前編〜」からの転載です。



「SEROKU フリーランス(以下、SEROKU)」の中の人をやっている kouki です。

今回は 社内 Kubernetes 実験環境をRancher 1.6から 2.0にアップデートして復活させた話 の中でお話しした「2.0で行ったトラブルシューティング」の「グローバル IP とプライベート IP 2つの足(NIC)を持つサーバを Kubernetes クラスタのネットワークに所属させることができない (Calico ネットワークが確立されない)」という件についてお話させていただきます。

経緯

経緯としては、 Rancher を利用した Kubernetes クラスタに対してインターネットからリーチャビリティを持たせるためにグローバル IP アドレスと社内通信用のプライベート IP 2つの足(NIC)を持つサーバをクラスタに参加させようとしました。

その際に、Kubernetes クラスタ間の通信を行う Canal ネットワークの確立がうまくいかない状況に遭遇しました。

接続イメージとしては以下の通りです。

f:id:weseek:20191017143107p:plain

Host A にグローバル IP アドレスとプライベート IP アドレスを持たせます。 また、正常なケースでは Host A、Host B、Host C、Host D と Kubernetes クラスタを組むための Canal (Calico + flannel) (Canal 以外のネットワークも選択可能です) ネットワークを確立します。

ですが、Host A はインターネットにも接続を持たせたいため、デフォルトゲートウェイを Internet 側(グローバル IP 側) に向ける必要があります。 そうすると、Host B、Host C、Host D との Canal (Calico + flannel) のネットワーク確立に失敗する、という事象に遭遇しました。

直後は「HostA は Host B/C/D にはプライベート IP で到達可能なのに、ネットワーク確立に失敗する理由がまったく分からん」という状態でした。

トラブルシューティング 初動

まずは Host A の中に SSH でログインし、Canal ネットワークを確立しようとするコンテナを調べてみることにしました。 そこで以下のようなログが記録されていました。

$ docker logs k8s_kube-flannel_canal-wtqnx_kube-system_0f6345ce-7b54-11e8-bf7c-525400638f33_18
I0629 14:44:25.191200       1 main.go:487] Using interface with name eth0 and address 192.168.XX.XXX
I0629 14:44:25.191314       1 main.go:504] Defaulting external address to interface address (192.168.XX.XXX)
E0629 14:44:55.192923       1 main.go:231] Failed to create SubnetManager: error retrieving pod spec for 'kube-system/canal-wtqnx': Get https://10.43.0.1:443/api/v1/namespaces/kube-system/pods/canal-wtqnx: dial tcp 10.43.0.1:443: i/o timeout

IP アドレスは伏せてあります。 Host A を追加する際に Kubernetes クラスタ通信用の I/F を指定することができるのですが、無事 192.168.XX.XXX が利用されています。 この点は問題無さそうで、 10.43.0.1:443 への通信がうまくいっていないようでした。

この失敗している状態でルーティングテーブルを参照しても 10.43.0.1 宛のルーティングは登録されている気配がありません。

「どこで通信を曲げているのだろう?」ということがまったくハッキリせず、「一度社内ネットワークにデフォルトゲートウェイを曲げて、その後、グローバルにデフォルトゲートウェイを向けなおす」という workaround を実施し、後日調査することにしました。

トラブルシューティング iptables

ふと思いつき、 iptables で通信を曲げているのではないか、ということが思いつきました。そこでまた Host A にログインし、iptable の nat テーブルから調べることにしました。

$ sudo iptables -L -t nat
...
Chain KUBE-SEP-OT4MDINTM57KEHPZ (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 KUBE-MARK-MASQ  all  --  *      *       192.168.YY.YYY       0.0.0.0/0            /* default/kubernetes:https */
    2   120 DNAT            tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/kubernetes:https */ recent: SET name: KUBE-SEP-OT4MDINTM57KEHPZ side: source mask: 255.255.255.255 tcp to:192.168.YY.YYY:6443
...

非常に長い出力になるので省略していますが、Kubernetes master である Host B (192.168.YY.YYY) へ通信を曲げている箇所が見つかりました。

このエントリは POSTROUTING として登録されており、おそらく初回起動時にネットワーク設定などを Kubernetes master へ取得しに行っているのだろう、と予想を立てました。

こちらの設定も192.168.YY.YYY へ到達するためには正しいように見えます。

トラブルシューティング 解明編

あと疑う点としては、Kubernetes master へ通信する際に source I/F が加味されているのだろうか、という点でした。

flannel が一番最初に通信するタイミングなども気になったので、 coreos/flannelソースコードを読むことにしました。

まずソースコードを読むにあたって、当たりを付けたのは、前述した error retrieving pod spec for というメッセージでした。親切に文章として出されているので、おそらくソースコード中に記述されているだろうと、検索をかけてみました。

予想は的中し、メッセージを出しているコードが見つかりました。

       pod, err := c.Pods(podNamespace).Get(podName, metav1.GetOptions{})
        if err != nil {
            return nil, fmt.Errorf("error retrieving pod spec for '%s/%s': %v", podNamespace, podName, err)
        }
        nodeName = pod.Spec.NodeName
        if nodeName == "" {
            return nil, fmt.Errorf("node name not present in pod spec '%s/%s'", podNamespace, podName)
        }

このコードを見ると、おそらく c.Pods(podNamespace).Get(podName, metav1.GetOptions{}) で失敗しているようです。 c の出所を探るため、更に調べると以下のような記述が見つかりました。

c, err := clientset.NewForConfig(cfg)

clientset の NewForConfig を利用して、クライアントを作成しているようです。 この clientset は "k8s.io/client-go/kubernetes" を利用しているようでした。

そこからさらに kubernetes/client-go に潜っていきます。

(ここからコードを追う作業は煩雑になるので省略します)

kubernetes/client-go を読むと、 HTTP リクエストを出す際に Source Address の指定はされていないようでした。

また、 kubernetes/client-go を利用している coreos/flannel 側も HTTP リクエストを出すための source I/F を指定しているようには読み取れませんでした。

これらの調査結果から、「Kubernetes master に HTTP でリクエストを行う際に使われるルーティングはデフォルトゲートウェイが用いられる」という裏打ちが取れました。

具体的な workaround

原因が分かれば対処はシンプルで、Kubernetes master (社内のネットワーク) への通信を社内のゲートウェイに向けてあげるだけです。

$ sudo ip route add 10.43.0.1 via 192.168.XX.X

このコマンドを起動時に実行するように設定するだけでトラブルは発生することはなくなりました。

宛先を 10.43.0.1/32 にしている理由は単純に 10.43.0.1 への通信に失敗していた事実からです。

まとめ

Canal ネットワークのトラブルから、 flannel, client-go へのソースコードに潜ることになるとは思いませんでしたが、go のソースコードリーディングも含めて、良い勉強になりました。

バッドノウハウな気がしなくもないのですが、こういった未知の領域のトラブルをきっかけにノウハウを蓄積することは続けていきたいと思っております。

関連記事

tech.weseek.co.jp

Go 言語での開発を試してみる 〜調べる編〜

こちらは 「Go言語での開発を試してみる 〜調べる編〜」からの転載です。

2018 年 8 月時点での Go 言語関連の調査結果を紹介しています。


こんにちは。haruhikonyan です。 自分 Go 言語というものを実はこれまで触ったことが無かったのでちょいと触ってみることにしました。 とはいえ何がやりたいとかそういうことは無いのでとりあえず Hello World と開発のための道具としてはどういうものがあるかを調べてみようと思います。

Go 言語のインストール

まずはインストールから。 調べるにもとりあえず手元で go コマンド自体が動かないとつらいものがあると思うのでインストールします。

windows

  1. インストーラダウンロード
  2. インストール
  3. バージョン確認

     > go version
     > go version go1.10.3 windows/amd64
    

無事インストールされて PATH も自動的に通してくれています!

mac

  1. homebrew でインストール

     $ brew install go
    
    • 自分は普段から homebrew を使っているのでインストーラは使わずに homebrew でインストールします。
  2. バージョン確認

     $ go version
     go version go1.10.3 darwin/amd64
    

こちらも無事インストール完了です。

Hello World

書いてみる

とりあえず公式のトップにある下記コードを拝借し、保存。

package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}
$ go run hello.go
Hello, 世界

簡単ですね!

コンパイル

Go 言語で書いたコードはデフォルトでクロスコンパイルに対応しているということで早速試してみます。

Windows

windows環境で試しているのでそのままビルド

> go build hello.go

なんとexeが吐き出されます! それをcmd上で実行してみると?

> hello.exe
hello, 世界

ちゃんと出力されました!

Mac

次は mac 用に出力してみることにします。

$ SET GOOS=darwin
$ SET GOARCH=amd64

$ echo %GOOS%
darwin
$ echo %GOARCH%
amd64

上記のように環境変数をセットしてあげます。 すると、hello というバイナリファイルが出力されました。 そいつを手元の mac に送ってターミナルで実行してみると?

$ ./hello
hello, 世界

どんな環境でも同じコードで動く!楽しい!

本格的に開発するために必要なうんちく

この章は Go 言語あまり関係ないんで飛ばしてくれて構いません。 Go 言語開発を始める上で他言語で開発を始める上で気にしていることを述べています。

フレームワークの有無

開発を行う上で言語の選定と同じ、それ以上に重要なこととしてフレームワークの選定があると思います。 何を作るにしても大体は先人が同じようなことを行なっていることがほとんどで、そのライブラリであったり、フレームワークを選定し、優れたフレームワークのある言語で開発を行うという選択が開発を成功させることであったり、工期を短くする秘訣であったりします。

例えば Ruby には Ruby on Rails という優れた Web フレームワークがあったからこそ莫大な人気が出たと言っても過言ではありません。 他にも Java の Spring や PHP の Laravel、 PythonDjango といったフレームワークが人気が高く幅広く使われていると言えるでしょう。

また、フレームワークを採用することでチーム開発をする上でのコーディング規約などが自然と揃い、読みやすくなるという利点もあると言えるでしょう。 車輪の再開発を悠長にやっている暇は無いのです。使えるものを使いましょう。

開発エディタ候補

開発環境においても VSCodeAtom のようなテキストエディタ拡張機能を追加して機能を充実させていくようなタイプのエディタもあれば、Java でいう Ecripce や IntelliJ のようなすでにデバッガ機能などが充実した IDE (統合開発環境)という大きな二つの選択に分かれると言えます。

前者のテキストエディタがベースのものではまず軽いといったメリットがありますが、機能を増やすには拡張機能を探して自分で入れる必要があったり、全ての要件を満たそうとすると自分で色々調べたりしなければならないのが欠点とも言えると思います。また、全てを詰め込もうとするとエディタ自体も重くなってしまうとうことも十分考えられます。 後者の IDE では専用ということもあり、機能は一通り揃っているが、エディタ自体が重いということもよくあることです。

エディタはよく Vim vs Emacs というような宗教戦争などと揶揄されるようにエンジニア個人の好みも大きく分かれるところです。開発効率にも大きく影響する部分だと思うのでベストな選択をしたいところです。

パッケージ管理

開発を行う上でライブラリやパッケージの依存管理はほぼ必須と言ってもいいでしょう。Node.js には npm や yarn といったものがあったり、Ruby では RubyGems (gem) であったり、他にもさきほど Mac で Go をインストールするために使った Homebrew もパッケージ管理システムです。

これらにパッケージ管理を任せることで、だれがアプリをビルドしても必ず同じパッケージ構成にできるというのも大きな利点の一つです。 開発をする上で依存関係を人間が細かく気にすることなく進める上では欠かせない存在であると言えます。

Go で本格的に開発するための道具候補

フレームワーク

Web フレームワークばかりになってしまいましたが、フレームワークの紹介です。

Gin

beego

Echo

iris

Revel

Martini

Gorilla

開発エディタ候補

公式ページによると4つのエディタが紹介されていました。

vim

Visual Studio Code

GoLand

Atom

パッケージ管理

dep

glide

vgo (Versioned Go)

使わない

  • パッケージ管理しないといけないような粒度の成果物つくるのは Go じゃなくていい説という話も。。。

次回予告

次回は後編ということで実際にこれらの調べた要素を組み合わせて簡単な何かを作ってみたいと思います。