元営業WEBエンジニアのアプリ開発日記

営業出身のWEB系エンジニアが気になったものから作ってはメモを残してくブログ

GAEのGoでHelloWorld

概要

GCPにいろんなサービスが存在するのはわかった
とりあえずBookshelfプリケーション作ろうチュートリアル に準じて一通りのサービスを触ってみる。まずはGAE(Go1.12)でHelloWorldしてみるのだ。
ちなみに実験環境は「mac OS Mojave 10.14.6」

事前準備

ブラウザのコンソールよりターミナルからコマンドで実行できた方がかっこいいので
Cloud SDK のインストール に準じてgcloudが実行できる環境を準備

gcloudコマンドのインストール

まずインストール。何か聞かれたら基本「Y」か「何も書かず」Enter

curl https://sdk.cloud.google.com | bash

シェルを再起動

exec -l $SHELL

gcloud環境を初期化。希望するgoogleアカウントでログイン。

gcloud init

# [1] Re-initialize this configuration [default] with new settings
# [3] Log in with a new account
# [1] abstract-hydra-251604

構築

プロジェクトを作ってみよう

ざっとドキュメント見てみたところ
「組織->フォルダ->プロジェクト->GCEとかBigQueryとかいろんなサービス達」こんな構成にできるらしい。
でも組織、フォルダは「G Suite」か「Cloud Identity」契約してないとダメらしい。。
「ucwork->open or private->ucwork-ai-000001->サービス達」こんな感じにしてみたかったけど・・取り急ぎプロジェクトのみで行こう

プロジェクトの新規作成

gcloud projects create ucwork-ai-000002 --name="gae hello world" 
Create in progress for [https://cloudresourcemanager.googleapis.com/v1/projects/ucwork-ai-000002].
Waiting for [operations/cp.6028126867139483180] to finish...done.
Enabling service [cloudapis.googleapis.com] on project [ucwork-ai-000002]...
Operation "operations/acf.b0ff89af-10b4-4f38-8221-d4da7b79c559" finished successfully.

プロジェクト一覧確認

gcloud projects list
PROJECT_ID        NAME             PROJECT_NUMBER
ucwork-ai-000002  gae hello world  298968203397

プロジェクトの詳細

gcloud projects describe ucwork-ai-000002
createTime: '2019-09-23T08:05:31.373Z'
lifecycleState: ACTIVE
name: gae hello world
projectId: ucwork-ai-000002
projectNumber: '298968203397'

デフォルトプロジェクトの確認

デフォルトプロジェクトの設定を確認。
違うプロジェクトが設定されている。さっき作成したプロジェクトをセットしよー

gcloud config list
[core]
account = [your gmail account]
disable_usage_reporting = False
project = ucwork-ai-000001

Your active configuration is: [default]

デフォルトプロジェクト設定

さっき作成したプロジェクトをデフォルトプロジェクトに設定

gcloud config set project ucwork-ai-000002
Updated property [core/project].

[core]
account = [your gmail account]
disable_usage_reporting = False
project = ucwork-ai-000002

Your active configuration is: [default]

GAE用のアプリケーション作成

GoでHelloWorldなアプリケーション作成

モデュール初期化

go mod init github.com/shintaro123/ucwork-go

8080ポートの/でアクセスがあった場合のみHelloWorldなアプリケーション

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    http.HandleFunc("/", indexHandler)

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
        log.Printf("Defaulting to port %s", port)
    }

    log.Printf("Listening on port %s", port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        http.NotFound(w, r)
        return
    }
    fmt.Fprint(w, "Hello, World!")
}

ローカルで実行させてみる

以下の通り実行して ブラウザでhttp://localhost:8080/実行すると「Hello, World!」表示されました。

go build
./ucwork-go
2019/09/23 17:32:19 Defaulting to port 8080
2019/09/23 17:32:19 Listening on port 8080

GAEアプリケーションのデプロイ

GAEへのデプロイ条件指示ファイル作成

app.yamlファイル作成
ランタイムだけ指定しとく

runtime: go112

デプロイ実行

デプロイ!
[2] asia-northeast1を選択じゃ!

gcloud app deploy                                                                                                                         [add_hello_world]
You are creating an app for project [ucwork-ai-000002].
WARNING: Creating an App Engine application for a project is irreversible and the region
cannot be changed. More information about regions is at
<https://cloud.google.com/appengine/docs/locations>.

Please choose the region where you want your App Engine application
located:

 [1] asia-east2    (supports standard and flexible)
 [2] asia-northeast1 (supports standard and flexible)
 [3] asia-northeast2 (supports standard and flexible)
 [4] asia-south1   (supports standard and flexible)
 [5] australia-southeast1 (supports standard and flexible)
 [6] europe-west   (supports standard and flexible)
 [7] europe-west2  (supports standard and flexible)
 [8] europe-west3  (supports standard and flexible)
 [9] europe-west6  (supports standard and flexible)
 [10] northamerica-northeast1 (supports standard and flexible)
 [11] southamerica-east1 (supports standard and flexible)
 [12] us-central    (supports standard and flexible)
 [13] us-east1      (supports standard and flexible)
 [14] us-east4      (supports standard and flexible)
 [15] us-west2      (supports standard and flexible)
 [16] cancel
Please enter your numeric choice:  2

Creating App Engine application in project [ucwork-ai-000002] and region [asia-northeast1]....done.
Services to deploy:

descriptor:      [/Users/shintaro.a.uchiyama/project/github.com/shintaro123/ucwork-go/app.yaml]
source:          [/Users/shintaro.a.uchiyama/project/github.com/shintaro123/ucwork-go]
target project:  [ucwork-ai-000002]
target service:  [default]
target version:  [20190923t175204]
target url:      [https://ucwork-ai-000002.appspot.com]


Do you want to continue (Y/n)?  Y

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 11 files to Google Cloud Storage               ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...failed.
ERROR: (gcloud.app.deploy) Error Response: [7] Access Not Configured. Cloud Build has not been used in project ucwork-ai-000002 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudbuild.googleapis.com/overview?project=ucwork-ai-000002 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.

なんか怒られた...
何やらサービスがdisabledになってるらしいのでenableにしてみる

# サービスから名前絞ってみる
gcloud services list --available | grep cloudbuild
cloudbuild.googleapis.com                             Cloud Build API

# 対象サービスをenabledにしてみる
gcloud services enable cloudbuild.googleapis.com
ERROR: (gcloud.services.enable) FAILED_PRECONDITION: Billing must be enabled for activation of service '[cloudbuild.googleapis.com, containerregistry.googleapis.com]' in project '298968203397' to proceed.
- '@type': type.googleapis.com/google.rpc.PreconditionFailure
  violations:
  - description: "billing-enabled: Project's billing account is not found. https://console.developers.google.com/project/298968203397/settings"
    subject: '298968203397'
    type: serviceusage/billing-enabled

また怒られた...先は長いぜ
課金有効にしろ的な感じに見えるな

課金を有効化

課金アカウント一覧確認

gcloud alpha billing accounts list
ACCOUNT_ID            NAME                OPEN  MASTER_ACCOUNT_ID
0184D4-9DEE56-138D09  My Billing Account  True

今回のプロジェクトに↑のアカウント紐づけてみよう

gcloud alpha billing projects link ucwork-ai-000002 --billing-account 0184D4-9DEE56-138D09                                                [add_hello_world]
billingAccountName: billingAccounts/0184D4-9DEE56-138D09
billingEnabled: true
name: projects/ucwork-ai-000002/billingInfo
projectId: ucwork-ai-000002

再度サービスを有効化じゃ!

gcloud services enable cloudbuild.googleapis.com                                                                                          [add_hello_world]
Operation "operations/acf.5ad913ed-016f-455f-b2c9-c906e0d09d36" finished successfully.

うまくいった!!!この勢いでデプロイじゃ!

2度目の正直。デプロイ

うまくデプロイできた!!!

gcloud app deploy                                                                                                                         [add_hello_world]
Services to deploy:

descriptor:      [/Users/shintaro.a.uchiyama/project/github.com/shintaro123/ucwork-go/app.yaml]
source:          [/Users/shintaro.a.uchiyama/project/github.com/shintaro123/ucwork-go]
target project:  [ucwork-ai-000002]
target service:  [default]
target version:  [20190923t195917]
target url:      [https://ucwork-ai-000002.appspot.com]


Do you want to continue (Y/n)?  y

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 0 files to Google Cloud Storage                ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://ucwork-ai-000002.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

以下叩いてブラウザからアクセス!
無事Hello,Worldみれましたわ。

gcloud app browse                                                                                                                         [add_hello_world]
Opening [https://ucwork-ai-000002.appspot.com] in a new tab in your default browser.

まとめ

独自ドメインの場合どうするの」とか 「app.yamlの設定あんまり理解してない」とか色々あるけど欲張らずちょっとずつ進めてこう。
次はアプリケーションをNoSQLやSQLにつないでみたい

機械脳の時代を読んでみた

概要

AI系企業に勤めることになるので機械脳の時代 を読んでみた。
記憶したことすぐ忘れちゃうので読書感想文書いとく。

読書感想文

「AIの時代ではなく、機械脳の時代」
これからは判断するためのツールではなく、判断自体を機械が行う時代。大きな時代の変革期にある。

これだけ大きなことが起こっている事実を最初に打ち出しながら
機械脳に出来ることを可視化、分類、予測というシンプルな3つに分類しそれぞれ事例を交えて説明してくれている。

著者は実際にデータサイエンスビジネスの前線で活躍している人物なので
モデルがどうとかデータがどうとかいう観点ではなく
ビジネス観点で物事が記載されており非常に勉強になりました。

また、ABCDE、SMART、AICS、RVGCなど
機械脳を設計するためのフレームワークは、データサイエンス未経験者にとっては
拠り所の一つになるためこちらも参考になる。

実際にAIを用いた事業に取り組んだら改めて読んでみよー

GCPってどんなサービスがあるんや

概要

AWS勉強した矢先に、今後はGCP使うことになりそうなのでまずは
GCPドキュメントに記載されてるサービス全部チラ見して自分なりの言葉でメモを残しとこう
AWSGCPもたくさんサービスあるのね・・・)

サービス一覧

コンピューティング

IaaS

PaaS

  • App Engine
    • Go,Javaとかアプリケーション実行できる環境

サーバーレス

  • Cloud Functions
    • サーバーレスアプリケーション
  • Cloud Run(Beta)
    • サーバーレスコンテナ

コンテナオーケストラレーション

  • Kubernetes Engine
    • Dockerコンテナのオーケストラレーション

データベース

NoSQL

  • Cloud Datastore
    • NoSQLやってみっかって時にとりあえず使ってみるやつ
  • Cloud Bigtable
    • 高パフォーマンスのNoSQLデータベース
  • Cloud Firestore
    • NoSQL。Firebaseと絡ませるときに使えるのかなぁ・・

MySQL

  • CloudSQL
    • リレーショナルデータベースサービス
  • Cloud Spanner
    • すげぇ高機能・パフォーマンスリレーショナルデータベースサービス

キャッシュ

  • Cloud Memorystore
    • キャッシュサービス

ストレージ

ブロックストレージ

オンラインクラウドストレージ

  • Cloud Storage
    • いろんなデータ入れ込める場所
  • Cloud Storage Nearline
    • アクセス頻度少ないデータ入れる場所
  • Cloud Storage Coldline
    • アクセス頻度がめちゃ少ないデータ入れる場所

ファイルサーバー

  • Cloud Filestore
    • NFS サーバー

データ分析

ビッグデータ解析

  • Google データポータル
    • 様々なデータソースをダッシュボードで表示できる素敵なやつ
  • BigQuery
    • データウェアハウス。SQL使えるらしい。
  • BigQuery BI Engine(Beta)
    • BI(Google データポータル等)とBigQueryの接続キャッシュしてくれたりする素敵なやつ
  • Cloud Datalab
    • Jupyterノートブックをwebで実行
  • Google Genomics
    • 大規模な遺伝子データ分析ができるらしい。おらはきっと使わぬだろう

pub/sub

  • Cloud Pub/Sub
    • メッセージングミドルウェア、キューをためて順次さばいてくれるやつ

データ処理

  • Cloud Composer
    • ワークフローを作成、スケジュール設定、モニタリング
  • Cloud Data Fusion(Beta)
    • ETL(Extract/Transform/Load)ツール
  • Cloud Dataflow
    • ストリーム・バッチでのデータ変換処理
  • Cloud Dataproc
    • バッチでのデータ変換処理
  • Cloud Dataprep
    • TRIFACTAなるツールを使ってデータを事前準備(変換)するサービス

AI と ML

AI Platform

  • Cloud TPU

    • TensorFlowに最適化されたマシン(ハード)のこと
  • AI Platform Deep Learning VM Image

    • ディープ ラーニング アプリ用に事前構成されたCE
  • AI Platform のトレーニングと予測

    • TensorFlow、scikit-learn、XGBoostなどをGCPで実行できるらしい
  • BigQuery ML

    • BigQuery でSQLを使用して機械学習モデルを作成、実行ができる
  • AI Platform Data Labeling Service(beta)

  • AI Hub

    • Google Cloud 上で AI の探索、共有、デプロイを行う

プリパッケージ ソリューション

  • Cloud Talent Solution
    • 募集中の職位の条件に一致する候補者を探す

AI ビルディング ブロック

  • Cloud AutoML

    • より簡単なカスタム ML モデル
  • Cloud Vision

    • 画像投げるとai使って画像分析した情報が返ってくるらしい
  • Cloud Video Intelligence

    • 動画投げるとai使って動画分析した情報が返ってくるらしい
  • Cloud Natural Language

    • 自然言語(普段の会話)を解析・分析して情報返してくれるらしい
  • Cloud Translation

    • テキスト入れると翻訳してくれるらしい
  • Cloud Speech-To-Text

    • 音声をテキストに変換してくれるらしい
  • Cloud Text-To-Speech

    • テキストを音声に変換してくれるらしい
  • Dialogflow

    • 足りない情報を聞き返してくれたり、表現を統一したりしてくれるらしい
  • Recommendations AI(Beta)

    • 個人に特化したおすすめの商品情報を幅広く提供
  • Cloud Inference API

    • なんだかわからんが大規模な相関関係がわかるAPIらしい
  • ML for Developers

ネットワーキング

GCP内のネットワーク構成

外部ネットワークとの接続

  • ダイレクトピアリング
    • GCPのプライベートコンピューティングに接続する必要なくて、Googleピアリング要件を満たす場合
  • キャリアピアリング
    • GCPのプライベートコンピューティングに接続する必要なくて、Googleピアリング要件を満たさない場合
  • Dedicated Interconnect
    • GCPのプライベートコンピューティングに接続する必要ありで、gcpネットワークに直接接続。すげえ速い
  • Partner Interconnect
    • GCPのプライベートコンピューティングに接続する必要ありで、gcpネットワークにサービスプロバイダーを介して接続。そこそこ速い
  • IPsec VPN

アディショナルなネットワーク関連サービス

  • Cloud Armor
    • DDos対策
  • Traffic Director(Beta)
    • マイクロサービス間の通信を管理するサービスメッシュ
  • Network Service Tiers
    • ネットワークのパフォーマンス落としてコスト最適化するスタンダード階層や現在のプレミアム階層を選択できる

セキュリティと ID

アクセスコントロール

  • Access Context Manager

  • vpc service controls

    • BigQueryやCloudStorageへのアクセス制御などセキュリティ的に素敵な制御ができる

セキュリティチェック

  • アクセスの透明性

    • Googleがアクセスしてきたことをログに残す
  • Cloud Data Loss Prevention(DLP

    • 機密データを検出して分類。秘匿化もできる
  • Cloud Identity-Aware Proxy

    • GoogleAcountで認証(アクセス制御)ができる
  • ID プラットフォーム

    • メアド・パスワード、電話番号IDなど様々な認証方式で認証(アクセス制御)ができる
  • Cloud Security Command Center

    • 各リソースのセキュリティ脅威をスキャンしてくれるやつ
  • Binary Authorization

    • GKEへのイメージデプロイにおいて署名されたもののみ許可する
  • Cloud Security Scanner

    • コンピューター系リソースのセキュリティ脅威。スキャン

ID管理

  • Cloud IAM

    • 誰(ID)がどのリソースに対するどのようなアクセス権限(役割)を持つかを定義
  • Cloud Key Management Service

    • 暗号化キーの作成・管理ができる
  • Resource Manager

    • 組織・フォルダ・プロジェクトなどのリソース階層をAPIで作成して管理できる
  • セキュリティキーの適用

    • 物理的なセキュリティーキーをUSBとかにぶっさして2段階認証できる

モノのインターネット

  • Cloud IoT Core
    • IoTのT、デバイスからのデータを受けてくれるサービス。pub/sub->Cloud Functionなどに連携される

GCPへの移行

  • Google Transfer Appliance

    • ラックマウント型のストレージサーバーにデータぶち込んでgoogleに返すとGCPに入れといてくれるらしい
  • BigQuery Data Transfer Service

    • Google広告とかのsaasデータをBigQueryに入れ込める(コード要らない)
  • Storage Transfer Service

    • AWS S3, http/httpsロケーション, Cloud Storageを特定のCloud Storageにデータ転送できるサービス
  • Velostrata

    • Googleに買収されたVelostrata社の技術を使ってリフト&シフトでGCPに移行できるらしい

メディアソリューション

オープンソース

観測系

  • istio

    • 複雑なサービスメッシュの監視、認証、ロードバランシングを行ってくれる
  • OpenCensus

    • クラウド上でのメトリクス(平均100msで処理終了などのマクロ情報)とトレーシング(どの処理がどの順番、秒数で終わっているかなど)を観測できる

コンテナ系

  • gVisor

    • セキュアなコンテナランタイム。デフォルトのruncではなくrunscを用いてアプリケーションからハードウェアを制御ある
  • Kubernetes

    • コンテナオーケストラレーションエンジン
  • knative

    • kubernetes上でサーバーレスアプリケーションをビルドしてデプロイしてくれるらしい
  • Kubeflow

GCPサービスとの関連系

デベロッパー ツール

開発運用ツール

  • Cloud SDK
    • GCPのリソースやアプリケーション管理するためのツール群。gcloudコマンドとか打てるようになる
  • Cloud Build
    • CI/CDツール

ソース・イメージ管理

  • Cloud Source Repositories
    • Git Repository
  • Container Registry
    • プライベート コンテナの登録と保存

ローカルツール拡充サービス

管理ツール

GCPリソース管理系

  • Cloud Console
    • ウェブベースの管理コンソール
  • Cloud Shell
  • Google Cloud API
    • GCPサービスに何かした操作をおこなうためのAPI
  • Cloud Billing
    • 請求 / コスト管理ツール

運用・開発ツール

  • Cloud Deployment Manager
    • アプリケーションのリソースをコード出かける。いわゆるIaaS
  • Anthos
    • GCP, AWS, オンプレなどハイブリット環境でコンテナ化したアプリを動かせるやつ

監視系

  • Stackdriver Debugger

    • 本番環境でのライブデバッグができるらしい。すげえ
  • Stackdriver Error Reporting

    • アプリエラーの報告
  • Stackdriver Logging

    • ログの一元管理
  • Stackdriver Monitoring

    • インフラストラクチャとアプリケーションのモニタリング
  • Stackdriver Profiler

    • CPU とヒープのプロファイリング
  • Stackdriver Trace

    • アプリケーション パフォーマンスの分析情報

API プラットフォームとエコシステム

Apigeeの製品

医療

  • Cloud Healthcare API
    • 医療関係のデータを素敵に扱えるサービスっぽい

SAP

省略

まとめ

だめだ。多すぎて全然入ってこない。
とりあえず以下ステップで学習を進めよう

  • アプリケーションが動く環境の構築
  • GCE,GKEなど同系のいろんなサービスを適当してみる
  • AI学習しアプリケーションに取り込む

2ヶ月でAWS 認定ソリューションアーキテクト – アソシエイトに合格してきた(2019/08/14)

概要

仕事で完全AWS環境!!とかいう保守運用案件を回されたが
EC2!RDS!S3!とかものすごいポイント・ポイントしか理解していない状態だったので
せっかくなら体系的に学びたいと思いお勉強。
結果的には1発で受かったがかなりギリギリだったのでもっと勉強法見直せばよかったなという反省日記。

awsアソシエイト 試験結果
awsアソシエイト 試験結果

勉強方法

結果的には以下「やったこと」の勉強を経て1発で合格できたが
今振り返ると「やっとけばよかった」に記載された内容をやっておけば
あんなに冷や汗かかずに本番挑めたのになーという反省

やったこと

やればよかったこと

かかったコスト

20,930円(受験料:16,200円+模擬試験:2,160円+参考書:2,570円)

勉強期間

ざっと2ヶ月

6月ごろに本買って勉強開始
通勤時間や休みの空いた時間に本読んだりして、1回受けようとしてたこと忘れるくらい仕事忙しくなって
また気力を取り戻して、最後に1週くらいに頭に詰め込み詰め込みでざっと2ヶ月!!

所感

「やればよかった」にも書いたけどもっといろんな問題解いとけばよかった。
UMTPとかOracleJavaの時は正直黒い本だけで行けた感あったけどAWSは黒い本だけだと少し不安が残る気がした(勝手な感想)

あの手この手で最適な解を聞いてくる問題なので、表面的に言葉を覚えるより 「どういう時にこのサービスは使われるのか」みたいな思考をもっと高めるべきだった。

仕事がドタバタして通勤時間と休日少し時間あるときぐらいしか勉強できなかったが
なんとか試験に受かってよかった。すこし余裕も出てきたので勉強再開しよう!!

awesome-typescript-loader,tslintからts-loader,eslintに乗り換える

概要

GWなのでちょいちょい噂になってた以下2点対応しようと思う

  • awesome-typescript-loader -> ts-loaderに変更
    • もうawesomeなスピードじゃないし、ソースの更新も止まってるらしい
  • tslint->eslintに変更
    • typescriptチームが正式にeslintにするって言ったとかどうとか

ts-loader対応

ts-loaderインストール

$ npm install --save-dev ts-loader

ts-loaderを使用する設定

webpack.config.jsで 「use: 'awesome-typescript-loader',」を「use: 'ts-loader',」に変更するだけで行けた。

webpack.config.js

  module: {
    // ファイルタイプに対するloaderの設定
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
      },

eslint対応

eslintとeslint-loaderのインストール

linterであるeslint自体と、webpackから対象ファイルを読み込むためのeslint-loaderをインストール

npm install --save-dev eslint eslint-loader

eslint-loaderの設定

webpack.config.jsで以下の通りruleを指定

  • enforce:preでts-loaderが走り出す前に実行
  • test: /\.(js|ts|jsx|tsx)$/,で対象になりそうな拡張子全部指定
  • exclude: /node_modules/,でnode_modulesは対象外にする

webpack.config.js

  module: {
    // ファイルタイプに対するloaderの設定
    rules: [
      {
        enforce: "pre",
        test: /\.(js|ts|jsx|tsx)$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
      }

eslint自体の設定

まずは最小の設定

こんな感じで最小設定してみる

  • "root": trueで親階層の設定を見に行かない(同階層の設定ファイルを優先)
  • "env": {}でglobalな変数をeslintに理解させる
    • "es6": true,でes6形式に記述してもeslintに怒られなくなる
    • "browser": trueでdocument,consoleなど記載しても怒られない
  • "extends": ["eslint:recommended",でおすすめlinter設定
  • "parserOptions": {"sourceType": "module"}でES6を有効化

.eslintrc

{
  "root": true,
  "env": {
    "es6": true,
    "browser": true
  },
  "extends": [
    "eslint:recommended"
  ],
  "parserOptions": {
    "sourceType": "module"
  }
}

webpack実行すると10:5 error Parsing error: Unexpected token <が出てくる。
ReactのFragment Shortcut<>のとこで怒られた

$ #  package.jsonの以下script実行
$ # "scripts": { "build": "webpack",
$ npm run build
  10:5  error  Parsing error: Unexpected token <

Reactエラーが出ないように対応させる

@typescript-eslint/eslint-pluginでeslintをReactにも対応させられそう。
でもこれを動かすには@typescript-eslint/parserもインストールする必要があるらしい。
@typescript-eslint/typescript-estreeを入れるとパースするときにAST(Abstract Syntax Tree)として取り扱えるらしい。なんのこっちゃ。とりあえず入れとく。

$ npm install --save-dev @typescript-eslint/parser @typescript-eslint/typescript-estree @typescript-eslint/eslint-plugin

extendsに"plugin:react/recommended"を追加する。
pluginsに指定する方式もあるらしいがこれでいこう。

{
  "root": true,
  "env": {
    "es6": true,
    "browser": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended"
  ],
  "parserOptions": {
    "sourceType": "module"
  }
}

webpack実行すると11:14 error Parsing error: Unexpected token =が出てくる。
jsxで<div id=""ってしてるだけなのになんか怒られる。

$ npm run build
  11:14  error  Parsing error: Unexpected token =

eslintでtypescriptもlintしてくれるように設定

そもそも、eslintがTypeScriptを理解させるための設定入れてないよな。
typescriptのparserを入れ込む

{
  "root": true,
  "env": {
    "es6": true,
    "browser": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "sourceType": "module"
  }
}

webpack実行するとついにエラー消えたしちゃんとjsファイル出力されてる!

まとめ

今回仕事で初めてフロントにも取り組んだけど。
ほんと変化激しい。ちょっと放置してるとduplicateになってるし。
置いてかれないように見つけたらすぐ対応するようにしよう。

golangのチュートリアル解いてみた

概要

Go言語勉強したい!とりあえずチュートリアル2周読んだ!!
理解度を試すために練習問題(Excesise)解いてメモしとく。
わからないとこはこの方の素晴らしい解答をみてやってみる!!!

練習問題やってみた

Basic

Exercise: Loops and Functions

ニュートン法を用いて平方根求めろって言ってる多分。
ニュートン法自体はこの記事がすごくわかりやすかった!!!
ざっくりいうと「ある関数f(x)が0になるためのxを求める式」はx(n+1) = x(n) - f(x)/f'(x) を繰り返していくことでわかるらしい。

今回の平方根に照らし合わせるとx=√2 <=> 2-x^2=0なのでf(x)=2-x^2が0になるためにはx(n+1) = x(n) - (2-x^2/2x)ってことなんだな。

10回ループして平方根求めてみる

package main

import (
    "fmt"
)

func Sqrt(x float64) float64 {
    z := 1.0;
    for i:=0; i<10; i++ {
        z -= (z*z - x) / (2*z)
    }
    return z
}

func main() {
    fmt.Println(Sqrt(2))
}

何回ループすれば答えに近づくか調べてみる

初期値のzを色々変えてみて、x(n+1)とx(n)の差分が極めて小さくなるまで繰り返す。
初期値1では5回で終わった。100とかにしたら11回だった。
(まぁ無心でmath.Sqrt使っちゃうだろうけど・・・)

package main

import (
    "fmt"
    "math"
)

func Sqrt(x float64) float64 {
    z, count := 1.0, 0
    for {
        prev := z
        z -= (z*z - x) / (2*z)  
        count++
        if math.Abs(z - prev) < 1e-10 {
            break
        }
    }
    fmt.Println("iterate count: ", count)
    return z
}

func main() {
    fmt.Println(Sqrt(2))
}

Exercise: Slices

いい感じのグラデーションが出るように2次元のSlice作って
2次元のインデックを計算するんじゃ!

package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
    // 8bit(0-255)数値型の配列をdy個保持する2次元配列生成
    /* イメージ
       [
           []
           [] 
           ...
           [] <- dy個
       ]
   */
    pic := make([][]uint8, dy)
    // []の数(dy)分ループ
    for y := range pic {
        // 8bit(0-255)数値型をdx個保持する配列生成
        /* イメージ
           [0 0 ... 0]<-dx個
       */
        pic[y] = make([]uint8, dx)
        // 0の数(dx)分ループ
        for x := range pic[y] {
            // x,yのインデックスを操作してグラデーションを作成
            /* イメージ
               [(0,0) (0,1) ...   (0,dx-1)]
               [(1,0) (1,1) ...   (1,dx-1)]
               [(2,0) (2,1) ...(dy-1,dx-1)]
           */
            pic[y][x] = uint8((x+y)/2)
            // (x+y)/2の代わりにx*yとかx^yすると色合いがぐっと変わる
        }
    }
    return pic
}

func main() {
    pic.Show(Pic)
}

Exercise: Maps

文章に出現する文字と数の対応Mapを作るのじゃ!

package main

import (
    "golang.org/x/tour/wc"
    "strings"
)

func WordCount(s string) map[string]int {
    // 文字と出現回数格納用のmap生成
    wordCountMap := make(map[string]int)
    // strings.Fieldsで文章を文字で分割
    // 文字数分だけループして出現回数カウント
    for _, word := range strings.Fields(s) {
        wordCountMap[word]++
    }
    return wordCountMap
}

func main() {
    wc.Test(WordCount)
}

Exercise: Fibonacci closure

フィボナッチな数字が帰ってくるようにするのじゃ!

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
    // フィボナッチの2項目(保持される値)
    n, afterN := 0, 1
    return func() int {
        // 計算のために現状の2項目を一時保存
        snapN, snapAfterN := n, afterN
        // 現状の2項目を次の1項目に格納
        // 現状の1項目+2項目を次の2項目に格納
        n, afterN = snapAfterN, snapN + snapAfterN
        // 0から出力されるように一時保存しておいた現状の1項目返却
        return snapN
    }
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Println(f())
    }
}

Methods and interfaces

Exercise: Stringers

Stringメソッドを実装することで fmt.Printfした時にいい感じ(1.1.1.1)に表示されるようにするのじゃ

package main

import "fmt"

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.
func (ip IPAddr) String() string {
    return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func main() {
    hosts := map[string]IPAddr{
        "loopback":  {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8},
    }
    for name, ip := range hosts {
        fmt.Printf("%v: %v\n", name, ip)
    }
}

Exercise: Errors

平方根求めるやつでマイナスの数値来た時にError投げるようにする!!
(float64でキャストしないとループしちゃうのなんでや・・・)

package main

import (
    "fmt"
    "math"
)

// エラー用のtype宣言
type ErrorNegativeSqrt float64

// エラー投げられた時に出力されるやつ
func (e ErrorNegativeSqrt) Error() string {
  // float64でキャストしなきゃいけないらしい。Why
    return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

func Sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0, ErrorNegativeSqrt(x)
    }
    z, count := 1.0, 0
    for {
        prev := z
        z -= (z*z - x) / (2*z)  
        count++
        if math.Abs(z - prev) < 1e-10 {
            break
        }
    }
    return z, nil
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

Exercise: Readers

Aを無限に出力するものを作りなさい!!

package main

import "golang.org/x/tour/reader"

type MyReader struct{}

// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (r MyReader) Read(b []byte) (int, error) {
    for i := range b {
        b[i] = 'A'
    }
    return len(b), nil
}

func main() {
    reader.Validate(MyReader{})
}

Exercise: rot13Reader

ROT13で文字列を復号するのじゃ!!
ROT13は文字列の順序操作で暗号化する方式(丸パクリ)

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

// io.Copyで読み込む際にこの変換かましてくれる
func (rot13 rot13Reader) Read(b []byte) (int, error) {
    n, err := rot13.r.Read(b)
    for i, v := range b {
        switch {
        case v >= 'A' && v < 'N', v >= 'a' && v < 'n':
            b[i] += 13
        case v >= 'N' && v <= 'Z', v >= 'n' && v <= 'z':
            b[i] -= 13
        }
    }
    return n, err
}

func main() {
  // io.Reader型で文字列取得
    s := strings.NewReader("Lbh penpxrq gur pbqr!")
  // rot13Reader structに設定
    r := rot13Reader{s}
  // io.Copyに参照渡し
    io.Copy(os.Stdout, &r)
}

Exercise: Images

Sliceで生成した画像をimage.Imageインターフェースで実現するんだ!
これも素晴らしい解答の丸パクリだけど、image.Imageに定義されてるインターフェースに必要なメソッドを実装するだけっぽい。。。

package main

import (
    "golang.org/x/tour/pic"
    "image"
    "image/color"
)

type Image struct{}

func (i Image) ColorModel() color.Model {
    return color.RGBAModel
}

func (i Image) Bounds() image.Rectangle {
    return image.Rect(0, 0, 256, 256)
}

func (i Image) At(x, y int) color.Color {
    return color.RGBA{uint8(x), uint8(y), 255, 255}
}

func main() {
    m := Image{}
    pic.ShowImage(m)
}

Concurrency

並行処理ができるみたい。
ちょっと完全に理解しきれてないけどとりあえず解いてみる。
今後いろいろ作りながら適切に理解を深めていこう

Exercise: Equivalent Binary Trees

二分木を用いて並行処理をしてみようみたいなとこ

binary treeを探検して数字を順番に出力

binary treeって左下が自身より小さくて、右下が自身より大きい値で構成されたツリー構造。 ↓こんな感じ。これを並行処理で探検させて1-7を順番に出力する的なこと

      4
  2       6
1   3   5   7

tree.New(k)使うと1*k, 2*k, ..., 10*kのbinary treeが生成されるっぽい

channelとclose使ってみる

チュートリアルにあったchannelとclose使うパターンだとこんな感じ。
rangeでくるくる回して、やりたいこと終わったらclose(ch)って感じ

package main


import (
    "golang.org/x/tour/tree"
    "fmt"
)


// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    WalkRecrusive(t, ch)
    close(ch)
}


func WalkRecrusive(t *tree.Tree, ch chan int){
    if t == nil {
        return
    }
    WalkRecrusive(t.Left, ch)
    ch <- t.Value
    WalkRecrusive(t.Right, ch)
}


// Same determines whether the trees
// t1 and t2 contain the same values.
// func Same(t1, t2 *tree.Tree) bool


func main() {
    ch := make(chan int)
    go Walk(tree.New(1), ch)
    for v := range ch {
        fmt.Println(v)
    }
}

参考までにだが探索するロジックとしては、こんな感じ
これのイメージが最初全く湧かなかった。再帰的処理に慣れてないのかな・・

1. 頂点(4)を呼び出し walk(tree.New(1), ch)

    1.1. 左下(2)を呼び出し walk(tree.Left, ch)

        1.1.1. 左下(1)を呼び出し walk(tree.Left, ch)

            1.1.1.1. 左下(nil)を呼び出し walk(nil, ch)

                1.1.1.1.1 nilだからreturn if tree == nil return

            1.1.1.2. 自分の値(1)をchに詰め込み  ch <- tree.Value

            1.1.1.3. 右下(nil)を呼び出し  walk(nil, ch)

                1.1.1.3.1. nilだからreturn if tree == nil return

        1.1.2. 自分の値(2)をchに詰め込み  ch <- tree.Value

        1.1.3. 右下(3)を呼び出し walk(tree.Right, ch)
        ...
channelとselectを使ったパターン

せっかくチュートリアルにもあったしselectでも書いてみる
closeじゃなくて、終了用チャネルに値が入ったら終了してる

package main


import (
    "golang.org/x/tour/tree"
    "fmt"
)


// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int, chend chan bool) {
    WalkRecrusive(t, ch)
    chend <- true
}


func WalkRecrusive(t *tree.Tree, ch chan int){
    if t == nil {
        return
    }
    WalkRecrusive(t.Left, ch)
    ch <- t.Value
    WalkRecrusive(t.Right, ch)
}


// Same determines whether the trees
// t1 and t2 contain the same values.
// func Same(t1, t2 *tree.Tree) bool


func main() {
    ch := make(chan int)
    chend := make(chan bool)
    go Walk(tree.New(1), ch, chend)
    for {
        select {
            case val := <- ch:
                fmt.Println(val)
            case <- chend:
                return
        }
    }
}

同じ値を格納したBinary Treeか判定

以下条件を満たしたら異なると判定するように実装 - 一方の探索対象だけなくなる - 1から出力していき、値が異なる場合

package main

import (
    "golang.org/x/tour/tree"
    "fmt"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    WalkRecrusive(t, ch)
    close(ch)
}

func WalkRecrusive(t *tree.Tree, ch chan int){
    if t == nil {
        return
    }
    WalkRecrusive(t.Left, ch)
    ch <- t.Value
    WalkRecrusive(t.Right, ch)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go Walk(t1, ch1)
    go Walk(t2, ch2)


    for {
        val1, ok1 := <- ch1
        val2, ok2 := <- ch2
        // どちらかチャネルの値が出切ったら終わり
        if !ok1 || !ok2 {
            // 両者falseで数が同じならfalse
            return ok1 == ok2
        }
        
        // 値が異なった時点でfalse
        if val1 != val2 {
            return false
        }
    }
}


func main() {
    fmt.Println(Same(tree.New(1), tree.New(1)))
    fmt.Println(Same(tree.New(1), tree.New(2)))
}

Exercise: Web Crawler

fekeFetcherで用意したURLたちをクローリングする。
指定されたurlが存在するか検索する。 存在すれば、その配下ににひもづくurlを取得してまた検索という繰り返し

デフォルトから追記したのは以下処理

  • 並行処理
    • Crawlメソッドをgo func内で実行
    • <- doneChでブロッキング
    • 処理が終わればdoneCh <- trueで値突っ込みブロック解除
  • 同じURLを二回検索しない対策
    • fetched.Lock()でロックしてすでに検索済みではないか確認
package main

import (
    "fmt"
    "sync"
)

type Fetcher interface {
    // Fetch returns the body of URL and
    // a slice of URLs found on that page.
    Fetch(url string) (body string, urls []string, err error)
}

type Fetched struct {
    m map[string]error
    sync.Mutex
}

var fetched Fetched = Fetched{
    m: make(map[string]error),
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
    // 指定した深さ全部終了したら終了
    if depth <= 0 {
        return
    }
 
    // 確認ずみのurlか確認
    fetched.Lock()
    if _, ok := fetched.m[url]; ok {
        fmt.Printf("fetched already: %s\n", url)
        fetched.Unlock()
        return
    }
    fetched.Unlock()
 
    // 対象URLをフェッチ
    body, urls, err := fetcher.Fetch(url)
    fetched.m[url] = err
 
    // 対象URLが存在しなければ終わり
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("found: %s %q\n", url, body)
 
    // 並列処理で配下のurlたちを実行
    doneCh := make(chan bool)
    for _, u := range urls {
        go func(url string){
            Crawl(url, depth-1, fetcher)
            doneCh <- true
        }(u)
        <- doneCh
    }
    return
}

func main() {
    Crawl("https://golang.org/", 4, fetcher)
 
    fmt.Println("----------")
 
    for url, err := range fetched.m {
        if err != nil {
            fmt.Printf("%v failed: %v\n", url, err)
        } else {
            fmt.Printf("%v was fetched\n", url)
        }
    }
}

// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult

type fakeResult struct {
    body string
    urls []string
}

func (f fakeFetcher) Fetch(url string) (string, []string, error) {
    if res, ok := f[url]; ok {
        return res.body, res.urls, nil
    }
    return "", nil, fmt.Errorf("not found: %s", url)
}

// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
    "https://golang.org/": &fakeResult{
        "The Go Programming Language",
        []string{
            "https://golang.org/pkg/",
            "https://golang.org/cmd/",
        },
    },
    "https://golang.org/pkg/": &fakeResult{
        "Packages",
        []string{
            "https://golang.org/",
            "https://golang.org/cmd/",
            "https://golang.org/pkg/fmt/",
            "https://golang.org/pkg/os/",
        },
    },
    "https://golang.org/pkg/fmt/": &fakeResult{
        "Package fmt",
        []string{
            "https://golang.org/",
            "https://golang.org/pkg/",
        },
    },
    "https://golang.org/pkg/os/": &fakeResult{
        "Package os",
        []string{
            "https://golang.org/",
            "https://golang.org/pkg/",
        },
    },
}

Skaffoldでminikubeのローカル環境開発

概要

TLSでの通信も行うことができたのでローカルで開発していきたいが
docker-composeのvolumesみたいにMacとコンテナをマウントさせるすべがなさそう。

ざっと調べるとSkaffoldなるものが良さそうなのでそれを使って開発してみる
また、以下記事にて構築した環境をベースに検証しているのでご承知おき

Skaffold導入

設定ファイルを記載してSkaffold実行するとマニュフェストや、Dockerfile、Dockerfile内でcopyされてるバイナリファイルやソースファイルなどの変更を感知してデプロイしてくれる。

Skaffoldのインストール

$ brew install skaffold
$ skaffold version
v0.26.0

Skaffoldの設定

色々設定できるみたいだが、Dockerfileの場所と、対象のManifestを指定するだけにしておく

apiVersion: skaffold/v1beta7
kind: Config
build:
  artifacts:
  - image: shintaro0123/nginx
    context: ./docker/nginx/
  - image: shintaro0123/golang
    context: ./docker/go/
deploy:
  kubectl:
    manifests:
      - nginx-deployment.yaml
      - go-deployment.yaml

ちなみに、Deploymentにはこんな感じでtag:latestを指定しておく。
Skaffoldが再ビルドするとタグが付いてくるのでlatestにしときゃとりあえず見てくれる

    spec:
      containers:
      - image: shintaro0123/golang:latest

コマンド実行

$ # このコマンドでwatchしてくれる
$ skaffold dev

開発、デプロイの流れを確認

Nginxの場合

アクセスしてみる

$ curl --resolve nginx.ucwork.local:443:`minikube ip` -k https://nginx.ucwork.local
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <title>sample page</title>
</head>

<body>
  <p>sample page1</p>
</body>

</html>%

DockerでADDされてるindex.htmlを修正

$ git diff
diff --git a/docker/nginx/index.html b/docker/nginx/index.html
index 5dc8292..afa1aa0 100644
--- a/docker/nginx/index.html
+++ b/docker/nginx/index.html
@@ -7,7 +7,7 @@
 </head>

 <body>
-  <p>sample page1</p>
+  <p>sample page2</p>
 </body>

 </html>
\ No newline at end of file
$
$ skaffold dev

# watchしてた奴が反応始めた!!
Generating tags...
 - shintaro0123/nginx -> shintaro0123/nginx:n-1.0.5-5-g37e6f39-dirty
Tags generated in 68.854557ms
Starting build...
Found [minikube] context, using local docker daemon.
Building [shintaro0123/nginx]...
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM nginx:1.15.8

もう一回アクセス

sample page2になってる!!!数秒もかからんレベル

$ curl --resolve nginx.ucwork.local:443:`minikube ip` -k https://nginx.ucwork.local
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <title>sample page</title>
</head>

<body>
  <p>sample page2</p>
</body>

</html>%

Golangの場合

アクセスしてみる

$ curl --resolve go.ucwork.local:443:`minikube ip` -k https://go.ucwork.local
url path is
id: 1
name: taro
id: 2
name: jiro
id: 3
name: hanako

DockerでCOPYされてるmainを修正

$ git diff docker/go/main.go
diff --git a/docker/go/main.go b/docker/go/main.go
index 7aafdd1..2e16314 100644
--- a/docker/go/main.go
+++ b/docker/go/main.go
@@ -27,7 +27,7 @@ func main() {

 func handler(w http.ResponseWriter, r *http.Request) {
        /** URLパス表示 */
-       w.Write([]byte("url path is " + r.URL.Path[1:] + "\n"))
+       w.Write([]byte("access url path is " + r.URL.Path[1:] + "\n"))

        /** DB接続 */
        var dbConnectQuery string
$
$ env GOOS=linux GOARCH=amd64 go build main.go
$
$ skaffold dev

# watchしてた奴が反応始めた!!
Generating tags...
 - shintaro0123/golang -> shintaro0123/golang:n-1.0.5-5-g37e6f39-dirty
Tags generated in 51.69ms
Starting build...
Found [minikube] context, using local docker daemon.
Building [shintaro0123/golang]...
Sending build context to Docker daemon  7.639MB
Step 1/6 : FROM golang:1.11.5
 ---> 1454e2b3d01f

もう一回アクセス

access urlになってる!!!

$ curl --resolve go.ucwork.local:443:`minikube ip` -k https://go.ucwork.local
access url path is
id: 1
name: taro
id: 2
name: jiro
id: 3
name: hanako

まとめ

docker-composeでホストとコンテナマウントできるんだからk8sもなんか手はあるでしょ。
&コンテナのビルドデプロイを自動化って時間かかっちゃうよなぁって疑心暗鬼だったけど、さすがgoogle先生全然爆速。
詳細までは理解できてないけどとりあえず編集とともにデプロイ自動化されたのでこれでいこう