• ..

NodeJS

    デプロイ

    準備

    コマンドのインストール

    now へデプロイするには専門のnowコマンドを使う必要があります。これはyarnnpmでグローバルインストールで入れることができます。

    yarn global add now
    npm install -g now

    now -vでバージョンが出れば完了です。

    設定ファイルを作る

    now の設定ファイルはnow.jsonという名前で作ります。
    now はバーション2になってから様々なデプロイタイプが増えました。例えば静的サイトをただホスティングするだけなら@now/static、 NextJS をデプロイしたいなら@now/next、 Go 言語で書いたものをデプロイしたいなら@now/goなど、自分に環境に合わせて設定ファイルを作ります。

    そしてこれらは混合させることができます。例えば、static/ディレクトリ以下は@now/staticgo/以下は@now/goでのようにです。それらはxxx.now.sh/static/index.htmlxxx.now.sh/go/index.goのようなURLで見れる形でデプロイされます。

    そのような設定ファイルを作ってみました。ちなみに**/*は再帰的にstatic/以下のすべてのファイルが対象ということになります。(srcへ指定するパスはgulpなどを使っている人などは馴染んでるかもしれません)つまりこれは、static/以下のすべてのファイルがデプロイ対象ということになります。

    {
      "builds": [
        {"src": "static/**/*", "use": "@now/static"},
        {"src": "go/**/*", "use": "@now/go"}
      ]
    }

    デプロイしてみる

    これは単にnowと実行するだけです。

    now

    デプロイが完了するとランダムに生成された URL 上で見れるようになります。ちなみにnow lsで URL などの情報はいつでも見ることができます。

    now ls

    デプロイ時に--nameオプションを使うことでその名前のグループにデプロイすることができます。例えば、

    now --name foo

    というような、foo-xxxxxxx.now.shというようなプレフィックスを自分で決めた URL が発行されるようになります。(nameを指定しないと今いるディレクトリ名が使われる)そしてもう一つの利点はnow rm fooとしたときにfooという名前でデプロイしてホスティング中のインスタンスをすべて削除することができます。
    now はデプロイしたものは自分で消さないと消えないのでできるだけ名前を付けてデプロイしたほうが楽だと思います。

    --name の省略

    ちなみに上記はnow.json"name": "foo"と追加すれば省略できるようになります。何度もその名前でデプロイする必要があるなら設定しちゃうのが1番楽だと思います、

    これまでのデプロイ情報の確認

    now lsまたはnow ls [app]を使います。

    すべてのプロジェクトの最新デプロイ履歴を一覧する

    単にnow lsを実行します。

    now ls
    app    url                     inst #  type  state  age
    nju33  nju33-c00bgh0qj.now.sh       -  -     READY  6h

    nowまたはnow deploy時にメタ情報を付けていた場合は同じように-mを使いメタ情報でフィルタリングできます。

    あるプロジェクトに関するデプロイ履歴を一覧する

    こちらは[app]も指定します。例えばfoo-xxxxxxxx.now.shというような URL の場合fooappです。

    now ls foo
      app  url                   inst #   type  state  age
      foo  foo-c00bgh0qj.now.sh       -   -     READY  6h
      foo  foo-5234afnq7.now.sh       -   -     READY  6h

    3分で NextJS をデプロイして「 Hello from NextJS 」

    必要なファイルは4つです。

    1. NextJS をインストールする為のpackage.json
    2. NextJS の設定ファイルのnext.config.js
    3. now の設定ファイルのnow.json
    4. ページになるpages/index.jsx

    package.json

    まず NextJS がデプロイ先でインストールされるように、package.jsonを作る必要があります。中身はこんな感じです。

    {
      "dependencies": {
        "next": "*"
      }
    }

    next.config.js

    next.config.jsは NextJS の設定ファイルで、 Webpack の設定を弄ったり、静的サイトビルド時のパスといった設定をするファイルなのですが、今回は何もする必要がないので空オブジェクトを指定します。

    module.exports = {
      target: 'serverless',
    };

    now.json

    NextJS のデプロイには@now/nextを使います。またその場合srcにはnext.config.jsを指定します。

    {
      "builds": [{"src": "next.config.js", "use": "@now/next"}]
    }

    pages/index.jsx

    ページとなるファイルです。これはnext.config.jsが置かれているディレクトリにpages/というディレクトリを作りその中に置く必要があります。中には JSX でHello from NextJSと表示するように書きます。
    この辺は now というより NextJS のルールです。

    export default () => <div>Hello from NextJS</div>

    デプロイ

    そして、nowをただ実行するとデプロイが始まりDIR_NAME-xxxxxx.now.shという URL で見れるようになるはずです。そのページを開きこのように表示されれば完了です。

    2分で Go をデプロイして「 Hello from Go 」

    設定ファイルを作る

    こんな感じのnow.jsonファイルを作ります。これは作業ディレクトリ内にあるすべての*.goファイルを@now/goを使ってデプロイするという意味になります。

    {
      "builds": [{"src": "**/*.go", "use": "@now/go"}]
    }

    Go ファイルを作る

    以下のようなファイルを作ります。現段階では、各*.goファイルではHandlerという関数を必ず定義する必要があります。(now 側がこの関数を呼び出します)

    package main
    
    import (
        "fmt"
        "net/http"
    )
    
    func Handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>Hello from Go</h1>")
    }

    デプロイ

    あとは以下でデプロイします。

    now

    DIR_NAME-xxxxxxx.now.shのような URL で見れたら完了です。

    now.json の為の JSON Schema

    作りました。

    スキーマはunpkg.com/@nju33/json-schema/schemas/now/now.jsonに置いてあります。(設定時は先に先の URL を使う必要があるので注意です)

    VSCode の設定

    コマンドパレットへ>Preferences: Open Settings (JSON)と入力し設定ファイルを開きます。そして中でjson.schemasへ以下のような設定をします。

    {
      "json.schemas": [
        {
          "fileMatch": ["now.json"],
          "url": "https://unpkg.com/@nju33/json-schema@0.0.19/schemas/now/now.json"
        },
      ]
    }

    now.json を編集

    適当にプロジェクトルートにnow.jsonファイルを作り、内容が{}の状態で「Missing property version」のように警告されれば完了です。

    ワンライナーでデプロイ・エイリアス作成・古くなった環境の削除

    なるべく短くするためにnow.jsonにはnamealiasを設定します。以下この設定での話です。

    {
      "version": 2,
      "name": "foo",
      "alias": "foo",
      "builds": [...]
    }

    コマンド

    そして以下を実行します。

    now && now alias && now rm -sy <name>

    (2019-04 追記) 上記は代わりました。2019年4月以降aliasは使わなくて良くなり、now--target productionオプションを付けることでnow.jsonaliasセクションがある場合、それを自動で設定するようになりました。

    よって、上記コマンドは以下のように変更になりました。

    now --target production && now rm -sy <name>

    now

    これはデプロイコマンドですね。これが完了するとfoo-xxxxxxx.now.shという URL が発行されます。

    now alias

    これによりfoo-xxxxxx.now.shという URL が(空いていれば)foo.now.shに紐づけ設定されます。

    now rm -sy foo

    now rm foofooという名前でデプロイしたものすべての環境を削除しようとします。この時yes/noで「本当に削除しますか?」と聞いてくるので-y(--yes) を付けることでこれに先に答えています。また、-s(--safe)を付けると削除対象からエイリアスと紐付いているものを外すことができます。つまり、

    foo-xxxxxx.now.sh -> foo.now.sh
    foo-yyyyyy.now.sh
    foo-zzzzzz.now.sh

    のときに、now rm -sy fooした場合foo-xxxxxx.now.shだけが残りそのエイリアスも残ってくれるはずです。 ```

    環境変数を設定する

    @now/nextではbuild.env、それ以外ではenvマシンの環境変数を渡すことができます。

    {
      "env": {
        "FOO": "BAR"
      },
      "build": {
        "env": {
          "FOO": "BAR"
        }
      }
    }

    隠したい値にはシークレット変数を設定する

    「何とかkey」や「何とかtoken」のようにあまり見られたくない値があると思います。その場合はまずシークレット変数として、 now に登録してそれを使うようにします。これの登録にはnow secretコマンドを使います。

    以下はtokenxxxxxxという値を登録する例です。

    now secret add token xxxxxx

    登録が終わったら先程のenv設定で頭に@を付けて指定してあげるだけです。

    {
      "env": {
        "TOKEN": "@token"
      },
      "build": {
        "env": {
          "TOKEN": "@token"
        }
      }
    }

    環境変数が登録されたか確認する方法

    そういうログを吐いて、それをnow logsコマンドから見てチェックします。now logs <url> -n 500(500行)とすれば確実に見れると思います。

    @now/node

    これは中でconsole.log(process.env)とすればログを吐いてくれるので、適当にアクセスしたあとにnow logsでその部分をチェックします。

    @now/next

    @now/nextを使ったデプロイでは、デプロイフローの中でyarn now-buildが呼ばれています。なので、これにフックする形で吐かせるといいと思います。(頭にpreを付ける)

    printenvはマシンに設定されている環境変数をすべて表示するコマンドです。

    {
      "scripts": {
        "prenow-build": "printenv"
      }
    }

    NextJS の中で環境変数が見れるようにする

    まだマシンに環境変数を設定しただけで NextJS の中では見ることができません。これにはnext.config.jsの Webpack の設定でwebpack.EnvironmentPluginなどを使って定義します。

    先に Webpack をインストールだけしておきます。

    yarn add -D webpack

    そして、こんな感じでnext.config.jsの Webpack 設定に手を入れます。

    module.exports = {
      webpack: config => {
        /* ...色々 */
    
        const webpack = require('webpack');
        config.plugins.push(
          new webpack.EnvironmentPlugin({
            TOKEN: 'default value'
          })
        );
      }
    }

    これでコードの中のprocess.env.TOKENで値が取得できるようになるはずです。

    now でドメインを買ったらやること

    そのドメインを自分のアカウント・チームに追加

    ドメインは自分のアカウントに買ったという情報だけある状態なので、まず使いたいアカウント・チームに追加してあげる必要があります。

    now switch <追加したいアカウント・チーム>
    now domains add <domain>

    ちなみに間違えた場合はnow domains rm <domain>です。

    TXT レコードを追加

    now domains inspect <domain>を実行すると、

      Verification Record
    
        name        type        value
        _now        TXT         xxxxxxxxxxxxxxxx

    のような部分が表示されると思います。上記の場合以下のようなコマンドを実行します。

    now dns add <domain> _now TXT xxxxxxxxxxxxxxxx

    ネームサーバーと TXT 認証が終わるまで暫く待つ

    これには僕は1時間ぐらい掛かったと思うので、焦らず待ちます。再度、now domains inspect <domain>を実行した時に、

        nsVerifiedAt        -
        txtVerifiedAt       -

    ここが-じゃなく日付に変わっていればnow aliasなどで使えるようになっているはずです。

    買ったドメインでサブドメイン設定

    now.jsonaliasに設定したいサブドメインを指定します。

    {
      "alias": "foo.<my-domain>"
    }

    あとは以下を実行すると勝手に設定してくれます。

    now alias

    手作業で設定する

    `nowでビルドしたあとに、foo-xxxxxx.now.sh`のような URL が発行されますが、これを使って以下のように紐付けることもできます。

    now alias set foo-xxxxxx.now.sh foo.<my-domain>

    こちらは毎回ビルドのあと設定する必要があります。

    買ったドメインで AWS SES の設定をする

    now でドメインは購入済みだとします。

    AWS SES でドメインを追加する

    SESのホームへ行き「Domains」 → 「Verify New Domain」から now で購入したドメインを入力します。

    すると以下のようなname type valueを取得できます。

    now コマンドで DNS 設定を更新する

    このようなコマンドを実行するだけです。

    now dns add <自身のドメイン> __amazonses TXT <上記のvalue>

    上記のコマンドがちゃんと設定できたか確認をします。

    now dns ls <自身のドメイン>

    ちゃんと設定できたようなら僕の場合は30分ぐらいで以下のメールが来て認証されました。

    __amazonses.<自身のドメイン> じゃないのか?

    SES で「設定してください」と言われる名前は__amazonses.<自身のドメイン>なので僕も最初はこれを設定して試しました。ですが、数日経っても認証されなかったので now にフィードバックを送ってみた所「__amazonsesでやってみて」と返信があったのでした。

    ログを確認する

    コマンド

    now logsを使います。例えばこのサイトは now で運用していますが、このサイトのログを見たいなら以下のコマンドを実行します。

    now logs nju33.com

    オプション

    -nを付けると何行分表示するかを設定できます。デフォルトでは100です。例えば1000行表示したいなら、

    now logs nju33.com -n 1000

    とします。

    -fを付けると更新を待機して、更新があった時に自動で表示します。less -fみたいな感じです。

    now logs nju33.com -f # または --follow

    ブラウザ

    now にログインしていればブラウザ上でログを見ることもできます。now ではnow.jsonroutesを何も設定していなくても、/_logsという特殊なルーティングを設定します。この URL にアクセスすると今ターミナルで見ていたログと同じものを見ることができます。例えばこのサイトであれば nju33.com/_logsという URL になります。

    ちゃんとファイルがデプロイされているか確認する

    now でデプロイすると自動でユニークな URL が発行されます。この時/_srcというルーティングも自動で設定されこの URL へアクセスすることでどういったファイルがアップロードされたのか、ファインダーのような形で確認することができます。

    このサイトも now でホスティングしていますが、このサイトの場合nju33.com/_srcという URL になります。

    CircleCI 上でデプロイする

    デプロイする前にnow loginが必要ですが、これはメール認証なので CI 上で自動化できません。自動化したい場所では認証トークンを使います。

    トークンの発行

    zeit.co/account/tokensから発行できます。個人アカウントから発行します。チームアカウント上で使いたい場合も、その個人アカウントが使いたいチームに所属していれば使うことができるので大丈夫です。

    チームとトークンを指定してデプロイ

    コマンド毎に--team--tokenで渡して実行します。(チーム(--team,-T)を省略するの個人アカウントがデフォルトで設定されます)--tokenは環境によって環境変数に置いたりして渡しましょう。以下はjaavscriptチーム上でデプロイする例です。自分がこのチームに所属していれば正常にデプロイできるはずです。

    now --team javascript --token xxxxx && \
      now alias --team javascript --token xxxxx && \
      now rm foo.now.sh --team aaaaa --token xxxxx

    npm のプライベートパッケージを使いたい時

    そのままではプライベートパッケージを見ることができないので、nowのデプロイフローがエラーになってしまします。

    この問題を解決するにはプロジェクトのルートディレクトリに.npmrcを追加しデプロイファイルに含まれるようにしてデプロイします。.npmrcはこんな感じの内容で作ります。

    //registry.npmjs.org/:_authToken=...

    authTokenauthTokenを取得の方法で取得してください。

    `now alias` がおかしいので、正しく動作するシェルワンライナー

    now alias がおかしい

    保存していたキャプチャがどっかいってしまったので曖昧ですが、now2からnow aliasargument ...みたいなエラーになりnow alias <url> <alias>でやらないと効かなくなってしまいました。

    その後、https://github.com/zeit/now-cli/issues/1701こういうものができたのでyarn global add nowで更新し、now aliasは治りましたが、対象のurlが何故か古いものになってしまいます。

    解決シェルワンライナー

    これです。

    now -n appname \
    && sh -c 'now ls appname | while read line; do if echo \"$line\" | grep \"appname-\" > /dev/null; then set $line; echo \"$2\"; break; fi; done;' | xargs -I@ now ln @ appname \
    && now rm appname --safe --yes

    1行目

    これはただデプロイしてるだけです。ちなみに-nはアプリ名(appname)を指定してます。

    2行目

    now ls appnameでこのアプリの全部のurlを含む詳細一覧を出します。その一覧を1行ずつ見ていってappname-が含まれている部分が来たらそれが最新のurlを含む行なので、そこからurlを取り出しnow lnへ渡します。

    3行目

    最後にデプロイしたもの以外(過去にデプロイした古いアプリ)を全部削除します。

    領収書を晒す

    now2になってから料金体制がよく分からないので少しでも参考になれば。

    2019年05月20日から2019年06月20日

    運用サイトは10程度(ただしアクセスはあまりない(/゜Д゜)/┻┻)です。

    GitHub連携によって自動でステージング反映とかしているので、気になるならそれを切ればもっと安くなりそうですね。

    ログは少し節約してたんですが、もっとガンガン吐いても良さそう。

    起こったエラーメモ

    デプロイ時にずっと「Initializing」のまま

    同プロジェクトでデプロイ中のデプロイメントがあると、それが終わるまで最新のデプロイは「Initializing」になるようです。

    now ls <project-name>

    を実行し、いらないデプロイメントを削除してみると治るかもしれません。

    build 時

    UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '//../../.svgo.yml'

    (node:1) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '//../../.svgo.yml'
        at Object.fs.openSync (fs.js:646:18)
        at Object.fs.readFileSync (fs.js:551:33)
        at e.exports (/var/task/.next/server/static/iqdZhcAsT3LfSmq39Ilkz/pages/_document.js:206:307121)
        at new u (/var/task/.next/server/static/iqdZhcAsT3LfSmq39Ilkz/pages/_document.js:18:123)
        at TPFN.t.default.r.default.plugin (/var/task/.next/server/static/iqdZhcAsT3LfSmq39Ilkz/pages/_document.js:20:223985)
        at n (/var/task/.next/server/static/iqdZhcAsT3LfSmq39Ilkz/pages/_document.js:20:50105)
        at /var/task/.next/server/static/iqdZhcAsT3LfSmq39Ilkz/pages/_document.js:206:21288
        at <anonymous>

    now(2)へのデプロイ時に、svgo 付近のパス周りで__dirnameundefinedなのが原因かも。

    postcsscssnano_document.tsxで使っていたのでこれをやめて、さらにpostcsscssnanoをアンインストールして依存から外した。これでsvgo依存も無くなり表示できるようになった。

    TypeError: r.isMemo is not a function

    now(2)へ@now/nextでのデプロイ時に。

    https://github.com/zeit/next.js/issues/5750#issuecomment-451607604のとおりに修正で治りました。

    Error: pages/index.js from Terser

    next.config.jsの Webpack の設定で EnvironmentPluginNODE_ENV=production と上書きしていたので、これをやめた。

    TypeError: Cannot read property 'minify' of undefined

    2019-02-04発生。

    terser@3.14.1を指定する

    yarn add terser@3.14.1

    package.jsonresolutions

    {
      "terser": "3.14.1"
    }

    export 時

    Cannot find module 'pages/_document'

    now 用の設定でtarget: 'serverless'していたのでこれを削除で直った。