寝ても覚めてもこんぴうた

プログラム書いたり、ネットワーク設計したり、サーバ構築したり、車いじったり、ゲームしたり。そんなひとにわたしはなりたい。 投げ銭は kyash_id : chidakiyo マデ

Appengine Node.js Standard Environmentをデプロイしてみる(quickstart)

まず初回はquickstartに倣ってデプロイしてみるだけ。
ほぼ こちら のクイックスタートのままやってみようと思う。

事前に必要なもの(というかもう設定済みなので)

  • GCPのアカウント作成(gmailアドレスあればすぐ)
  • GCPのプロジェクト作成(適当にGCPコンソールからポチポチっと)
  • Google Cloud SDK(gcloudコマンド)のインストール
  • gcloudコマンドのログイン処理
  • nodeのインストール(npmコマンド使うらしいので)

ドキュメントには、GCPコンソールからプロジェクトを作成して、アプリケーションリソースのリージョンを指定して、課金を有効してーって書いてありますね。ま、その辺やっておきましょう。

Githubにあるサンプルコードをダウンロードする

適当なディレクトリでgit cloneをします。

git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

ですね、当たり前のやつです。

nodejs-docs-samples というディレクトリが落ちてきますが、その中の appengine/hello-world/standard というのが今回使うディレクトリです。
他にも山のようにディレクトリありますが、参考になりそうなので時間のあるときにでも目を通しておきましょう。

対象のディレクトリに移動

cd nodejs-docs-samples/appengine/hello-world/standard

を実行

依存関係のインストール

依存モジュールのインストールを行います。
最新のnodeでやろうと思いましたが、nodebrewの設定がおかしくなっていたようなので、 node v10.9.0 で実行します。

npm install

ローカルhttpサーバで実行してみます。

npm start

ローカルホストの 8080 ポートで起動するようなので

http://locahost:8080

へブラウザでアクセスしてみます。

Hello, world! と表示されれば成功しています。

先程サーバを起動したターミナルを抜ける場合には ctrl + c でquitできます。(画面に出てますね。一応親切心で書きましたw)

デプロイしてみる

事前にGCPのプロジェクトの作成は済んでますか?
appengineのregionの設定は済んでますか?

では、okdであればデプロイしましょう。

gloud app deploy --project {PROJECT_ID}

quickstartのドキュメントには gcloud app deploy だけで良いとありますが、gcloudコマンドに事前にcurrentのprojectを設定していない場合などハマる場合があるので引数つけてます。

ちなみに time app deploy --project {PROJECT_ID} のような形で初回デプロイを計測しましたが、 2分20秒ほどかかりました。
2回目からはもう少し速いはずです。

デプロイしたアプリケーションにアクセスしてみる

デプロイしたログに

Deployed service [default] to [https://{PROJECT_ID}.appspot.com]

と表示されているのでそのURLに直接ブラウザからアクセスしてもOKです。

コマンドでカッコよくアクセスしたい場合には

gcloud app browse --project {PROJECT_ID}

のようにコマンドを実行すれば自動的にブラウザが起動するはずです。

GAE/SE nodeのデプロイ簡単ですね!

Appengineで初回1回だけ実行したい(設定の初期化)ときはwarmupを利用する

Appengine (GAE) はリクエストのイベントでインスタンスが起動されるので、通常のアプリケーションを実装した際に初回1回だけ実行する処理というのが普通に実装するとなかなか難しい。
特にDB(Datastore)アクセスなどが必要な場合にはrequestが無いとappengineのcontextを作成することができないのでそもそもDB(Datastore)へのアクセスができない。

そんなときに利用するのがwarmupリクエスト。

warmupリクエストとは

最初に書いたとおりでappengineはリクエスト駆動で動くため、warmupのリクエストがappengineの環境から実行される。
appengineはよしなにスケールするため、スピンアップのタイミングでそのリクエストがそのインスタンスに対して、1度だけやってくるというイメージ。

設定方法

app.yamlへwarmupの設定を追加する

Github にサンプルファイルをおいているが、以下のようにwarmupを実行するという設定を入力する必要がある。

ポイントは2箇所

まずは、warmupリクエストを使用しますよ。という設定が以下。

inbound_services:
- warmup

もう一つは、warmupリクエストは /_ah/warmup のパスへリクエストが送られるため、

- url: /_ah/warmup
  script: _go_app
  login: admin

という設定が必要となる。
_ah ってどういう意味?って思うかもしれないが、 app handler とかって意味とどこかで見たけど忘れたw
また、 login:admin と書かれている場合には外部からのアクセスが行えないため、外からいたずらされないためには忘れずに付けましょう。

Application側の設定

こちらも Githug にサンプルを置いていますが、
特に難しいことはなく、handlerの設定として

http.HandleFunc("/_ah/warmup", warmup)

のような実装を行い、warmup functionの中で初期化を行いたい処理を書くと良いです。
先程も書いたように通常のハンドラと同様に実装できますので、requestを使ってappengine contextの生成も問題なく行えます。

参考

公式ドキュメント

追記 : 落とし穴

warmupはどうやら絶対に実行してもらえるという感じのものでもないらしい。

Note that warmup requests are not guaranteed to be called. In some situations loading requests are sent instead: for example, if the instance is the first one being started up, or if there is a steep ramp-up in traffic. However, there will be a "best effort" attempt to send requests to already warmed-up instances if warmup requests are enabled

意訳 : warmupの実行は保証してないよー、ベストエフォートだよー

つまり、warmupが実行される前にリクエストを受け取った際には、初期化がされているか判別して初期化する、のような制御が必要

こちらの資料の p.12 - p.13 あたりに特に詳しく説明が載っています。(ありがたい)

www.slideshare.net

enjoy GAE/go!

Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what? (Kubernetes NodePort と LoadBalancer と Ingress のどれを使うべきか) を訳した

Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what? がよくまとまった記事だったので社内で共有するために適当に訳してみた


Kubernetes NodePort と LoadBalancer と Ingress のどれを使うべきか

最近、NodePorts、LoadBalancers、Ingress の違いを尋ねられます。 それらは外部のトラフィッククラスタ内に取り込む方法で、全て異なる方法で実現しています。 それぞれがどのように動作するか、それとどのように使うべきか見てみましょう。

注意 : すべてがGKEで適用したものであり、もしあなたがその他のクラウド、オンプレ、minikube、その他で稼働している場合には少々異なる場合があります。深い技術的な説明はしません。もっと学ぶことに興味があれば、オフィシャルドキュメントはとてもいい資料です。

ClusterIP

ClusterIPは標準のk8sサービスです。 ClusterIPはクラスタ内のアプリケーションがアクセスできるクラスタ内のサービスを提供し、外部からのアクセスはできません。

ClusterIPサービスのYAMLは以下のようになります :

apiVersion: v1
kind: Service
metadata:  
  name: my-internal-service
spec:
  selector:    
    app: my-app
  type: ClusterIP
  ports:  
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP

ClusterIPに外部からアクセスできない場合、なぜでしょう。 あなたはKubernetes Proxyを利用してアクセスできることがわかります。

f:id:chidakiyo:20180830195747p:plain

Kubernetes Proxyを起動します :

$ kubectl proxy --port=8080

今度はこちらのスキームを利用して、Kubernetes APIにたどり着くことができます

http://localhost:8080/api/v1/proxy/namespaces//services/<SERVICE-NAME>:<PORT-NAME>/

よって、上で定義したサービスにアクセスするには、以下のアドレスを利用することができます

http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/

どんな時に使いますか?

Kubernetes Proxyを利用してあなたのサービスにアクセスするいくつかのシナリオがあります

  1. サービスをデバッグする、もしくは、直接何らかの理由でラップトップから直接接続を行う。

  2. 内部トラフィックを許可する、ダッシュボードを閲覧するなど。

しかしこの方法を用いて、kubectlを認証されたユーザで実行する必要があり、サービスをインターネットに公開したり、プロダクションサービスとして利用すべきではありません。

NodePort

NodePortサービスは外部トラフィックをあなたのサービスに届ける最も基本的な方法です。 NodePortの名前が示すように、すべてのノードの特定のポートを開き、このポートに送られたすべてのトラフィックはサービスにフォワードされます。

f:id:chidakiyo:20180830195806p:plain

NodePortサービスのYAMLは以下のようになります :

apiVersion: v1
kind: Service
metadata:  
  name: my-nodeport-service
spec:
  selector:    
    app: my-app
  type: NodePort
  ports:  
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30036
    protocol: TCP

基本的に、NodePortサービスには通常の "ClusterIP" サービスとの2点違いがあります。 まず、Typeが "NodePort" であり、ノード上で開くポートを指定する nodePort という追加のポートもあります。 もしポートを指定しない場合にはランダムなポートが選択されます。 殆どの場合、Kubernetesにポートを選択させる必要があります。thockinによれば、利用できるポートについては多くの注意点があります。

どんな時に使いますか?

これらの方法には多くの弱点があります :

  1. 1サービスあたり1つのポートしか利用できない

  2. 30000–32767の間のポートだけが利用できます

  3. ノード/VMのIPを変更した場合、対処する必要がある

それらの理由から、その方法を利用してプロダクションのアプリケーションを公開することを私はおすすめしません。 いつも利用可能である必要がないサービスを実行している場合や、コストを気にする場合、この方法は役にたちます。 そのようなアプリケーションの良い例はデモアプリケーションや一時的なものの場合です。

LoadBalancer

LoadBalancerサービスは、サービスをインターネットに公開する一番標準な方法です。 GKEでは、Network Load Balancerが立ち上がり、すべてのトラフィックがサービスにフォワードされる1つのIPアドレスを取得できます。

f:id:chidakiyo:20180830195817p:plain

どんな時に使いますか?

あなたが直接サービスを公開しようとしたばあい、これが標準の方法です。 指定したすべてのポートに対するトラフィックがサービスにフォワードされます。 フィルタリングされず、ルーティングなどもされません。 HTTP、TCPUDP、Websocket,gRPCなど様々なあらゆる種類のトラフィックを送ることができます。

大きな欠点として、LoadBalancerを利用して公開するそれぞれのサービスがIPアドレスを取得し、LoadBalancerを公開するサービスごとに支払いが発生するため、とても高価になります!

Ingress

上記の例と違い、Ingressは実際にはサービスの一種ではありません。 代わりに、複数のサービスの前に配置され、"smart router" または、クラスタのエントリポイントとして振る舞います。

あなたはIngressを利用することで様々な事ができる、異なる機能を持った様々なタイプのIngressコントローラーがあります。

標準のGKEのIngressコントローラーはHTTP(S) LoadBalancerを起動します。 そのため、バックエンドサービスへのパスベースルーティングとサブドメインベースルーティングの両方ができます。 たとえば、foo.youdomain.com宛はfooサービスへ、youdomain.com/bar/以下の全てはbarサービスへ送ることができます。

f:id:chidakiyo:20180830195828p:plain

L7 HTTP Load Balancerを使用するGKEのIngressオブジェクトのYAMLは次のような感じです :

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  backend:
    serviceName: other
    servicePort: 8080
  rules:
  - host: foo.mydomain.com
    http:
      paths:
      - backend:
          serviceName: foo
          servicePort: 8080
  - host: mydomain.com
    http:
      paths:
      - path: /bar/*
        backend:
          serviceName: bar
          servicePort: 8080

どんな時に使いますか?

Ingressはあなたのサービスを外部に公開する場合に最もパワフルな方法ですが、最も難解な方法にすることもできます。 Ingressコントローラーの種類は、GCLB、Nginx、Contour、istioなどあがります。 また、cert-managerのようなIngressコントローラー用のプラグインもあり、サービスのSSL証明書を自動的にプロビジョニングできます。

Ingressは同じIPアドレスで複数のサービスを公開吸う場合に非常に便利であり、これらのサービスはすべて同じL7プロトコルを利用します。(通常はHTTP) ネイティブGCP統合を利用している場合にはLoadBalancer一台分を払うだけ、Ingressは "smart" なので、多くの機能をそのまま利用できます。(SSLやAuth,Routingなど)

Raspberry Pi に関して書いた記事一覧(ブクマおすすめw)

最近Raspberry Piに関して何本か記事を書いたので INDEX.

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

chidakiyo.hatenablog.com

これを読めばRaspberry Piマスター!w

Raspberry Pi : OSのswapを無効にする

どういうこと?

Raspberry PiはストレージがSDカードなので、読み書きを大量にしているとちょっと寿命的に気になる。
そのため無駄な読み書きをなくすためにswapを無効化したほうが良さそう!
(実際にRaspberry PiKubernetesクラスタを作る手順とかでもswapは無効化してる)

swap状況を確認してみる

以下のコマンドでswap状況を見てみる

free -h

表示はこんな感じ

              total        used        free      shared  buff/cache   available
Mem:           927M         95M        210M         37M        621M        730M
Swap:           99M        3.5M         96M

100MBぐらいのswap領域を確保しているようだ。

swapを無効化する

以下のコマンドで不要なパッケージ及び領域を削除する

sudo swapoff --all
sudo apt-get purge -y --auto-remove dphys-swapfile
sudo rm -fr /var/swap

swap無効化後の状況を確認してみる

free -h

表示はこんな感じ

total        used        free      shared  buff/cache   available
Mem:           927M         95M         91M         40M        740M        727M
Swap:            0B          0B          0B

バッチリ!
swap領域がなくなったのでこれでok.

Raspberry Pi : tmuxをインストールする

Motivation

Raspberry Pi上でなにか処理をするとまーまー時間がかかります。
特にDockerのBuildとか待ってるのしんどいですよね。もうすぐ帰ろうというタイミングでうっかり実行すると悲しくなります。
そんな時にtmuxがインストールされていればデタッチすればSSH接続が切れてもOK。そんなわけでインストールしましょう。

tmuxのインストール

sudo apt-get -y install tmux

tmuxを実行してみる

tmux

なんかかっこいいバーが下段に出てきたら成功。

セッションをデタッチしてみる

デフォルトのコントロールキーが Control + b なので、

Control + b (を押したあとに) d

これでtmuxセッションが生きたままデタッチができる

デタッチしたセッションに再度アタッチし直す

先程のセッションにアタッチするので

tmux attach

これでまたさっきのカッコイバーが出ている画面に戻る。

長い時間が必要なバッチを実行する場合などは、 tmux を起動した状態でスクリプトを実行し、
detachすることで端末とのSSHが切れてもバッチが止まることがないので便利。

tmuxのconfigのカスタマイズ

先程のコントロールキーのバインドなどを任意に変更ができる。
この記事では詳しくは書きません

Raspberry Pi : Dockerのexperimentalな機能(--squash)を有効化する

Motivation

MacRaspberry Piバージョンは 18.06.1-ce で同じものを使っていたのですが、Mac側では--squashを利用できたものの、Raspberry Pi側では利用できなかったので有効化したい。
(おそらくMacはDocker on MacのEdgeを利用していたので有効化されていたのでは?)

設定する

experimentalな機能を有効化するには /lib/systemd/system/docker.service 内の以下の場所を修正する必要がある。

sudo vi /lib/systemd/system/docker.service

ExecStart の行を以下のように変更する

ExecStart=/usr/bin/dockerd -H fd:// -s overlay --experimental

(overlayもついでに追加した)

Dockerの再起動

設定を有効化するにはDockerを再起動する必要がある

sudo systemctl daemon-reload
sudo systemctl restart docker

これで --squash コマンドも利用できるようになります。