GAミント至上主義

Web Monomaniacal Developer@ビズオーシャン

このブログについて(先頭固定)

【お約束】 投稿内容は個人の見解であり、所属する組織の公式見解ではありません。

名前:uyamazak(昔いた会社で上司が開発用Linuxサーバーのユーザー名に「yuyamazaki」が長いので勝手に作ってくれた。読み方わからないけどウヤマザク)

株式会社ビズオーシャン エンジニアリング部リーダー。
コミケ3日目で10年以上売り子無欠勤
アズールレーン@竹敷でモバイルのUI、UX研究中(初嫁ジャベリン)

GitHub: https://github.com/uyamazak/

関わってる主なプロジェクト

続きを読む

WSL(Windows Subsystem for Linux)でDockerを動かす(使えない)

普段の開発はLinux上で行っているけど、WSLに関してはLinux信者として興味があり、以前Bash on Windowsを動かしてみたりはしてた。
でも日本語がダメだったり、普通にLinuxが使える環境であれば実用性はなく放置してたけど、最近Dockerが動くようになったと聞くようになったので試してみた。
Amazonプライムで2019年のアニメがいろいろ始まったけどまだ何見ていいかわからない状態。

WSL(Windows Subsystem for Linux)に関してはここらへん
qiita.com

参考はここらへん。
qiita.com
qiita.com

Microsoft StoreからUbuntu16.04を入れ、上記記事を参考にdockerをインストールしたが動かなかった。
その後、Windowsアップデートをして下記バージョンにしたところ、hello-worldやnginxは動くようになった。

f:id:uyamazak:20190116165552p:plain

じゃあさっそく自分が作ってるアプリを動かしてみようと下記のnode、puppeteerを使ったPDFサーバーをcloneしてビルドしてみたところ失敗。

github.com

Unpacking dh-python (2.20170125) ...
Setting up libpython3.5-minimal:amd64 (3.5.3-1+deb9u1) ...
Setting up python3.5-minimal (3.5.3-1+deb9u1) ...
Traceback (most recent call last):
  File "/usr/lib/python3.5/py_compile.py", line 186, in <module>
    sys.exit(main())
  File "/usr/lib/python3.5/py_compile.py", line 178, in main
    compile(filename, doraise=True)
  File "/usr/lib/python3.5/py_compile.py", line 136, in compile
    os.makedirs(dirname)
  File "/usr/lib/python3.5/os.py", line 241, in makedirs
    mkdir(name, mode)
OSError: [Errno 39] Directory not empty: '/usr/lib/python3.5/__pycache__'
dpkg: error processing package python3.5-minimal (--configure):
 subprocess installed post-installation script returned error exit status 1
dpkg: dependency problems prevent configuration of python3-minimal:
 python3-minimal depends on python3.5-minimal (>= 3.5.3-1~); however:
  Package python3.5-minimal is not configured yet.

dpkg: error processing package python3-minimal (--configure):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 python3.5-minimal
 python3-minimal
W: Download is performed unsandboxed as root as file '/var/cache/apt/archives/partial/fontconfig_2.11.0-6.7+b1_amd64.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
E: Sub-process /usr/bin/dpkg returned an error code (1)
The command '/bin/sh -c wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -   && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'   && apt-get update   && apt-get install -y google-chrome-stable --no-install-recommends   && rm -rf /var/lib/apt/lists/*   && rm -rf /src/*.deb' returned a non-zero code: 100
uyamazak@windows10pro:~/hcep-pdf-server$ sudo docker build -t hcep-pdf-server:latest .

chromeインストールでこけている。

もしかして開発に使ってるLinux(Ubuntu16.04)でだけ?と思ったけど、GCPのCloud Buildでもビルドは成功しているのでWSL側の問題である確率の方が高そう。
エラー出してる部分をなんとか直せば上手くいくのかもしれないけど、使わないWSLのためにそこまでしたくない。

開発作業以外なら、Windowsでも社内の事情、ハードウェアの自由もあるし全然嫌ではない。

でも開発環境はVM、ハイパーバイザーなどを使わない素に近いLinux、強いて言えば本番環境に近いLinux、強いて言えばDebian, Ubuntuがいいなぁと改めて思った。

Firebaseでユーザー作成/削除時にFunctionsでなんかやる

今更ゾンビランドサガを見始めて宮野節を堪能してます。
yagish履歴書でユーザー側でのアカウント削除を実装するにあたり、退会率などをみるため、せめてログには残したいと思った。
おかげさまで退会率は1%以下で使用するユーザーはかなり限られるため最低限の実装を予定。

自分でアカウントを削除させる処理

削除は下記公式どおり、ユーザーオブジェクトのdelete()を実行するだけ。
だけどログインしてから一定(詳細は不明)時間経つとエラーになるので、一度ログアウトし、再度ログインをしてもらう必要があった。
実行するまで切れてるか分からないので、エラーが出たら再ログインを求める案内文言でカバー。

var user = firebase.auth().currentUser;

user.delete().then(function() {
  // User deleted.
}).catch(function(error) {
  // An error happened.
});

https://firebase.google.com/docs/auth/web/manage-users?hl=ja#delete_a_user

Functions側

とりあえずログに出すだけ。
user(UserRecord)を受け取れるのでそれを使っていろいろできる。

exports.loggingUserCreate = functions
  .auth
  .user()
  .onCreate(user => {
    const data = JSON.stringify(user.toJSON())
    console.log(`account created ${data}`)
    return true
  })

userが持っている情報は下記ドキュメント
Interface: UserRecord  |  Firebase

最後のreturnは

Function returned undefined, expected Promise or value

というエラーが出るのでつけているが値に特に意味はなさそう。

ログであればemail, uidなど指定で出したほうがいいけど、とりあえずtoJSONで全部出して試してた段階。

登録、退会時に通知メールを送るにはSendGridなどを使う必要がありそう。
社内向けなら以前書いたようなHangOutsチャット連携もありかもしれない。

uyamazak.hatenablog.com

あとはuidを元にfirestoreに保存してあるデータを削除するのがよくやりそうだけど、yagishの場合はユーザー側に機能があるのでそちらで明示的にやってもらうことを考えている。

Docker + Google Cloud Buildを使ってCIしようとしてる

これまでLinuxの開発サーバー上のシェルコマンドでDockerのビルドやイメージのプッシュ、GKEへのデプロイをしていたけど、先日のGitHub Actionsの発表など世の中CI/CDが当たり前になっているので、DockerとGCPでできる範囲でやってみる。

Cloud Buildについて

Gitレポジトリへのプッシュなどに反応して、Dockerfileのビルド、もしくはcloudbuild.yamlに書いたコマンドを実行してくれる。

今回は元々Dockerでやってたので、Dockerfileでやる。

ビルドしたあと、Container Registryにイメージをプッシュしてくれるところまで。

GKEへのデプロイはまだどうやるかわからない。

cloud.google.com

ソースコードのレポジトリを用意

対象のソースコードは下記。今回はGitHubに公開していたので連携して使う。
github.com

node.jsで動いてHTMLをPDFにするだけのシンプルなアプリケーションなので自動テストもしやすくて。ちょうどいいため。

GCPをすでに使っていて、GitHubか公開する気がないのであればCloud Source Repositoriesでいいと思う。

Cloud Source Repositories - コードを保存、管理するためのプライベート Git リポジトリ  |  Source Repositories  |  Google Cloud

* テストを準備
今回はmocha, chaiユニットテスト、superagentを使ってリクエストのテストを書いた。
網羅性は気にせず、本番で使っている動かないとまずいリクエストや、手動でテストが難しく不安なところだけ書いた。

テストが書けたら稼働中のコンテナ内でmochaコマンドを実行して、大丈夫だったらDockerfileに下記を追加してdocker build時に実行する。

Dockerfile

// 省略
COPY test /hcep/test
RUN mocha
// 省略

expressが終了しない問題

テストが終わってもexpressのコネクションが残っているようで、express.close()しても終わらず、ちょっとハマった。

下記のような丁寧な解決策もあるようだけど、今回はテストと割り切りprocess.exit()することに。
node.js - How Do I Shut Down My Express Server Gracefully When Its Process Is Killed? - Stack Overflow

describeのafter内でprocess.exit()してしまうとChrome側でまだ生きているというエラーが出たので最後に5秒sleepするようにしたら大丈夫だった。

test/requests.js

// 省略
const sleep = (waitSeconds, someFunction) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(someFunction())
    }, waitSeconds)
  })
}

describe('requests routes', (done) => {
  let app
  before(beforeDone =>{
    (async() => {
      const browserPage = await hcPage()
      app =  await expressApp(browserPage)
      beforeDone()
    })()
  })
  after(afterDone => {
    (async ()=> {
      await app.close() 
      sleep(5000, () => {
        console.log('process.exit!')
        process.exit()
      })
      afterDone()
    })()
  })

  const req = request(SERVER_URL)
  it('Health Check GET /hc', async () => {
    await req.get('/hc')
      .expect(200, 'ok', done)
  })
//以下略

before、afterの中でasync無名関数を実行しているのはbefore、afterがPromise返しに対応していないのか、だめだったため内部で同期処理するようにした。
もっとスマートな方法がありそう。

テストがこけるとビルドも止まりエラーで終了する。
ビルド結果のslack通知もほしいけど、Cloud Pub/SubとCloud Functions連携が必要になるのだろうか。めんどい。

Cloud Buildを設定

トリガーの設定は先程のGihHubのレポジトリを指定して、アカウント連携する。
イメージ名のタグに$COMMIT_SHAを入れた以外、特に設定は変えずそのまま。

f:id:uyamazak:20181227150357p:plain

これでビルドが成功したらコミットハッシュ付きのイメージがContainer Registryにプッシュされていくようになった。

動作確認

Cloud Buildの履歴で標準出力されたログを確認することができる

f:id:uyamazak:20181227155324p:plain

無事テストも実行されて通っている。

実行時間もcorei5 4コア、メモリ32GBのローカルサーバーに比べると、コミットごとと思えば遅いけどまあ問題ないレベル。

前回のビルド中にまたpushしてもビルドは再実行されない模様。その場合は手動で実行すればよさげ。

これから

他のCIツールと比べるとできることは少なそうだけど、GitとDockerfileを使っていて、Container Registryにアップするまでならシンプルですぐ使えると思った。

もっといろいろしようとすると独自のcloudbuild.yamlで書く必要があるのでちょっと面倒。

あとはステージング、本番環境への自動デプロイができればCDまでいけそう。

個人的にはすべての開発をクラウド上でやりたくてローカルマシンは端末、エディタとしてだけ使うような開発環境が理想。


Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門