Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って初めたブログ

個人的に気になった話題、ガジェット2021/09

はじめに

多忙な時期が続く...
個人的に気になった話題やガジェットなどを箇条書きで書く。

個人的に気になった話題

  • [VSCode] Syntax highlighting of codeblocks in the Settings editor

code.visualstudio.com

今まで拡張機能の「Bracket Pair Colorizer 2」を使用してたが、デフォルトの方が動作が軽い気がしたので、デフォルト機能を利用することにした。

devblogs.microsoft.com

  • [TypeScript] unknown on catch Clause Bindings

devblogs.microsoft.com

TypeScript 4.0のときにcatchした時の型がanyからunknownへ変更するとのアナウンスがあったのだが、これが4.4からデフォルトの挙動に変わっていた。

今後は、catchした時に以下のような判定が必要になりそう。 まぁ、元々anyを撲滅していれば問題なさそうだが・・・

} catch (error: unknown) {
      if (error instanceof Error) {
        Log.error('Error:', error)
      } else {
        throw error
      }
    }

aws.amazon.com

いよいよAmazon Elasticsearch ServiceからAmazon OpenSearch Serviceへと変わった。略称はOSSになるのだろうか。。。

  • 無駄にコメントを書くな、コードを書け!

とても参考になった。「あー、そんな書き方してるわ〜」ってところが幾つか見つかったので注意していきたいorz

  • GitHub ActionsでAWSの永続的なクレデンシャルを渡すことなくIAM Roleが利用できるようになったようです

dev.classmethod.jp

これヤバいな。実際に試せてないけど、IAM User / Access Keyを作らなくてすむなら幸せだ。

  • [GCP] Cloud Runのコンテナに常にCPUを割り当てられるようになりました。(プレビュー)

cloud.google.com

これは大きなアップデート。 AWSのApp Runnerがそんな感じだっただから、GCPは選択できるようになって、更に使い勝手が良くなりましたね。 AWS側のApp Runnerも選べるといいなぁ・・・

個人的に気になったガジェット

www.apple.com

新型iPad発表されましたね。
子供がちょくちょくYouTubeやらこどもちゃれんじアプリやら見初めたので、iPad miniも良かったのですが、とりあえず(金銭的な理由)無印iPadの購入を決めました。

振り返ると、タブレットを買うのはNexus 7 以来なので、少しうれしいです。

昔はタブレット買っても使い道がスマホとほとんど変わらなかったのですが、今だと文字は書けるし、電子書籍も見やすいし。。。と、用途がだいぶ変わった気がします。

というわけで、再びタブレットを使って何ができるのかを探求していきたいと思います。。。。

個人的に気になった話題、ガジェット2021/08

はじめに

最近、仕事の方が忙しくて定期的なアウトプットができていなかったので、個人的に気になった話題やガジェットなどを箇条書きで書いていこうと思います。

個人的に気になった話題

  • Elastic社がAWSのElasticsearchのクライアントライブラリ使用を拒否

aws.amazon.com

8月に入って、大きな話題の1つでした。
ちょうど、ElasticsearchのクライアントライブラリをAWSで使用していたので、自動的にバージョンを上げないよう設定しました。

www.infoq.com

これも話題になりました。
気になるな・・・と思ってみたら、順番待ちだそうなので、しばらく待ってみようと思います。

  • GitHub Discussionsのリリース

github.blog

あまり使いこなせていませんが、GitHub Discussionsがベータ版ではなくなったみたいですね。
ちゃんと使いこなせると、結構便利そう。。。

aws.amazon.com

最近、PythonよりもTypeScriptを使うことが多いので、認識だけはしておく。

  • TypeScript バージョン4.4のリリース

devblogs.microsoft.com

最近になってTypeScriptを追い始めたので、何が新しくできるようになったか理解できていませんが、今後はTypeScriptも追っていこうと思います。

個人的に気になったガジェット

store.google.com

「完全ワイヤレスイヤホン」をまだ試した事がなく、Googleから良さげなイヤホンが出たので、ちょっと気になっている。(Air Podsは高いしなぁ。。。)

『レベルアップNode.js』を読んだ話

最近、Node.jsを使うことが増えきて、だいぶコードを書くことに慣れてきたので、そろそろレベルアップしたいと考えていたところ、ドンピシャな本がアマゾンプライムデーで、セール販売されていたので、購入して読んでみた。

はじめに

先に全体的な感想を言うと、良本です。

著者自身がSocket.IOを使ってNode.jsでウェブシステムを構築した経験と知識を本に纏めてくださっており、Node.jsについて理解を深めて、レベルアップしていくには、どのような知識が必要なのかが書いてあった。

なので、この本を読むだけではレベルアップはできませんが、この本から「こういった知識を知っておくとレベルアップできるよ」といったことが学べます。

以下、私が特に刺さった部分をピックアップして、感想を書いていく。

第1章 Node.jsの全体像

まず、最初の第1章が面白かった。Node.jsがどのように構築されているかをさらっと説明している。Node.jsがV8エンジンを採用しているのは周知されていると思うが、他にもlibuvやnode-coreがあり、それらがどのような特徴を持ち、どう機能しているのかが書かれていて面白かった。

残念ながら、詳しい話は「Node.jsデザインパターン」を読むようにとのことだが、こういったNode.jsについて深い知識を得るにはどういった本や記事を読むべきかを纏めてくれているだけでも、有り難いし良本と言える。

第3章 サーバーとしてのNode.js

次に面白いと感じたのは第3章。Node.jsはシングルプロセス・シングルスレッドで動く故、その利点や問題点が書かれている。私自身も気になっていた部分ではあったものの、スルーしていたため、非常に勉強になった。ここでレベルアップに向けてクラスタリングやプロセスマネージャーが重要になってくるということが知れたのは大きい。

第4章 イベントループ

第4章は、はじめて知ることばかりだった。そもそも私はイベントループを知らなかった。この本では「イベントループについて知りたいなら、本を閉じてこの記事を読みなさい!」とあり、他の記事へ誘導するだけだったが、私もそれで十分だと思えた。

それくらい丁寧にまとめられている記事が既に存在し、むしろその記事の情報を貰えただけで有り難いと思えた。

第7章 Stream

やはりStreamは外せない。「Streamを制するものは、Node.jsを制す」という言葉があるくらいStreamへの理解は重要とのこと。とはいえ、Streamは難易度が高い部類に入るため、Streamをきちんと理解して利用しているエンジニアは少ないとのこと。ここでもStreamへの理解を深めるには「Node.jsデザインパターン」をオススメされており、これはもう「Node.jsデザインパターン」を読め。と言っているに等しいのでは・・・(= = ;;

付録A Node.jsの習得に役立つ情報を得るには

「Node.jsデザインパターン」をはじめ、他にもイベントや良記事を紹介している。

このあたりはNode.js界隈では有名な記事だそうだ。

PLAIDのgamiさんの記事もありました。。。知らんかった。。。

さいごに

上でも書いたとおり、この本は良本でした。

Node.jsへの理解を深めていくための方法や何を知っておくべきなのかが纏まっており、本のタイトル通り「レベルアップするための情報」がこの本に詰められていました。

まだ紹介された記事をすべて読んではいませんが、記事を読みつつ写経しつつで、理解を深めたいと思います。そして「Node.jsデザインパターン」もいつか読んでみよう。。。と思います。(= =;;

試しにCopilotを使ってAWS App Runnerを動かしてみた話

はじめに

最近発表されたAWSの新サービス「AWS App Runner」

コンテナ化したウェブアプリケーションを直接デプロイできるフルマネージドなサービス。

近いところで言うとGCPの「Cloud Run」のAWS版といった感じです。

こいつは便利そうだぜ。。。というわけで、さっそく試してみることにしました。

Copilot

コンソール画面からポチポチしても良かったのですが、これに合わせてかCopilotのバージョンも上がり、「AWS App Runner」にも対応しているとのことなので、こちらも試してみることにしました。(ちなみに、私は今回初めてCopilotを使います)

Copilotは、コンテナアプリケーションのビルド、 リリース、運用を更に加速させたいという思いから産まれたCLIツールだそうです。詳細は以下を参照ください。

aws.github.io

コンテナ

今回は、簡単にNginxのコンテナをアップします。

// Dockerfile
FROM alpine:latest

# nginxのインストール
RUN apk update && \
    apk add --no-cache nginx

# ドキュメントルート
ADD app /app
ADD default.conf /etc/nginx/conf.d/default.conf

# ポート設定
EXPOSE 80

RUN mkdir -p /run/nginx

# フォアグラウンドでnginx実行
CMD nginx -g "daemon off;"
// default.conf
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /app;

        location / {
        }
}
// app/index.html
<h1>Copilot - App Runner</h1>

デプロイ

copilot init を入力すると、名前(Application Name, Service Name)とDockerfileを指定すればデプロイが始まります。(はやぃ・・・)

$ copilot init
Note: It's best to run this command in the root of your Git repository.
Welcome to the Copilot CLI! We're going to walk you through some questions
to help you get set up with a containerized application on AWS. An application is a collection of
containerized services that operate together.

Application name: sample-app
Workload type: Request-Driven Web Service
Service name: nginx
Dockerfile: ./Dockerfile
Ok great, we'll set up a Request-Driven Web Service named nginx in application sample-app listening on port 80.

✔ Created the infrastructure to manage services and jobs under application sample-app.

✔ Wrote the manifest for service nginx at copilot/nginx/manifest.yml
Your manifest contains configurations like your container size and port (:80).

✔ Created ECR repositories for service nginx.

All right, you're all set for local development.
Deploy: Yes

✔ Linked account XXXXXXXXXXX and region ap-northeast-1 to application sample-app.

✔ Proposing infrastructure changes for the sample-app-test environment.
- Creating the infrastructure for the sample-app-test environment.       [create complete]  [83.3s]
  - An IAM Role for AWS CloudFormation to manage resources               [create complete]  [22.0s]
  - An ECS cluster to group your services                                [create complete]  [11.0s]
  - Enable long ARN formats for the authenticated AWS principal          [create complete]  [2.4s]
  - An IAM Role to describe resources in your environment                [create complete]  [22.2s]
  - A security group to allow your containers to talk to each other      [create complete]  [4.4s]
  - An Internet Gateway to connect to the public internet                [create complete]  [16.5s]
  - Private subnet 1 for resources with no internet access               [create complete]  [18.7s]
  - Private subnet 2 for resources with no internet access               [create complete]  [18.7s]
  - Public subnet 1 for resources that can access the internet           [create complete]  [18.7s]
  - Public subnet 2 for resources that can access the internet           [create complete]  [18.7s]
  - A Virtual Private Cloud to control networking of your AWS resources  [create complete]  [16.5s]
✔ Created environment test in region ap-northeast-1 under application sample-app.
Environment test is already on the latest version v1.4.0, skip upgrade.
[+] Building 2.8s (11/11) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                            0.0s
 => => transferring dockerfile: 356B                                                                                                                                            0.0s
 => [internal] load .dockerignore                                                                                                                                               0.0s
 => => transferring context: 2B                                                                                                                                                 0.0s
 => [internal] load metadata for docker.io/library/alpine:latest                                                                                                                2.0s
 => [auth] library/alpine:pull token for registry-1.docker.io                                                                                                                   0.0s
 => [1/5] FROM docker.io/library/alpine:latest@sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f                                                          0.0s
 => [internal] load build context                                                                                                                                               0.0s
 => => transferring context: 277B                                                                                                                                               0.0s
 => CACHED [2/5] RUN apk update &&     apk add --no-cache nginx                                                                                                                 0.0s
 => [3/5] ADD app /app                                                                                                                                                          0.0s
 => [4/5] ADD default.conf /etc/nginx/conf.d/default.conf                                                                                                                       0.0s
 => [5/5] RUN mkdir -p /run/nginx                                                                                                                                               0.5s
 => exporting to image                                                                                                                                                          0.1s
 => => exporting layers                                                                                                                                                         0.1s
 => => writing image sha256:caf72e9509ffe1e28b2070021afe000b6304b8a1fda457160f373aa2d38f4f2c                                                                                    0.0s
 => => naming to XXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/sample-app/nginx                                                                                             0.0s
Login Succeeded
Using default tag: latest
The push refers to repository [XXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/sample-app/nginx]
4056d7cdbba7: Pushed
27836a6a132d: Pushed
1d8fedb1407f: Pushed
4f56318aa220: Pushed
b2d5eeeaba3a: Pushed
latest: digest: sha256:81d1c8846ae62ca6c4e86bd5c277b65b9e5ef8ec088df9be4e4e5422c8099b89 size: 1360
✔ Proposing infrastructure changes for stack sample-app-test-nginx
- Creating the infrastructure for stack sample-app-test-nginx                     [create complete]  [290.1s]
  - An IAM Role for App Runner to use on your behalf to pull your image from ECR  [create complete]  [20.1s]
  - An IAM role to control permissions for the containers in your service         [create complete]  [20.1s]
  - An App Runner service to run and manage your containers                       [create complete]  [258.6s]
✔ Deployed nginx, you can access it at https://yrqh7trqcy.ap-northeast-1.awsapprunner.com.
$

作成されたURLに接続すると・・・見れました。

デプロイ成功です。

f:id:special-moucom:20210601094556p:plain

AWS App Runner」の設定もコンソール画面から見てみると、構成(CPU/メモリ)は最小のようですね。

f:id:special-moucom:20210601094616p:plain

Copilotは、コンテナに特化しているCLIツールだけあって、CDKを使って書くよりも全然早い。。。裏側ではCloudFormationのテンプレートを作成して、それを使ってデプロイを行っている模様。

デプロイしたリソースの削除もcopilot app deleteコマンドで一発削除。

$ copilot app ls
sample-app
$ copilot app delete --name sample-app
Are you sure you want to delete application sample-app? Yes
✔ Deleted service nginx from environment test.
✔ Deleted resources of service nginx from application sample-app.
✔ Deleted service nginx from application sample-app.
✔ Deleted environment test from application sample-app.
✔ Cleaned up deployment resources.
✔ Deleted application resources.
✔ Deleted application configuration.
✔ Deleted local .workspace file.
$

感想

AWS App Runner」は、まさにGCPの「Cloud Run」のAWS版。Copilotを使用することで、CLIによるデプロイも可能。

気になる料金ですが、「AWS App Runner」はコンテナインスタンスを0にすることはできない模様。なので、最低1つのコンテナは動き続ける分、課金が発生します。

0.009 USD/GB/時なので、0.009 * 2(最小構成が2GB) * 24時間 * 30日 = 約 13 USD/月といったところでしょう。うーん、これならGCPの「Cloud Run」の方が低コストですね。。。

でも、AWSには他の様々なサービスと連携できるメリットもあるので、「AWS App Runner」という新しい選択肢ができた。というだけでも、素晴らしい事だと思います。

今後は、ちょっと試したい!ちょっと動かして確認したい!ってな時に「AWS App Runner」を使う機会はやってくでしょう!!いやぁ、便利なサービスがまた1つできましたね(= =)ノ

Objectのデータを取得する時、ブラケット記法で書くと型がanyになって辛かった話

TypeScriptでObjectからデータを取得する時、ブラケット記法で書くと強制的に型がanyになって、大変辛い思いをしたので、以下に残しておく。

Objectのサンプルは以下の通り。ポイントは、exampleAB.a以降とexampleAB.b以降とで型が違うこと。

interface ExampleAB {
  'a': {
    a_example: {
      example: string
    }
  },
  'b': {
    b_example: {
      example: number
    }
  }
}

const exampleAB: ExampleAB = {
  'a': {
    a_example: {
      example: 'dummy'
    }
  },
  'b': {
    b_example: {
      example: 1
    }
  }
}

たとえば、以下のようにするとObjectからデータを取得することができる。

当たり前ですね。

console.log(exampleAB.a.a_example.example) // 'dummy'

しかし、これをブラケット記法で書くとTypeScript側で型を判断できずにエラーになる。

( T 0 T ) ォォォォ...

const k = 'a'
console.log(exampleAB[k as strng].a_example.example) // 型 'any' の式を使用して型 'ExampleAB' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。

keyofを使えばワンチャンあるかも・・・と思い、試すもダメだった。

const k = 'a'
console.log(exampleAB[k as keyof ExampleAB].a_example.example) // プロパティ 'a_example' は型 '{ a_example: { example: string; }; } | { b_example: { example: number; }; }' に存在しません。
  プロパティ 'a_example' は型 '{ b_example: { example: number; }; }' に存在しません。

どうやら、Objectの構造が深いとダメな模様。

Objectのデータ構造が、'exampleAB.a'と'exampleAB.b'までであれば、TypeScriptが判断してくれることを確認した。

interface ExampleAB2 {
  'a': string
  'b': number
}

const exampleAB2: ExampleAB2 = {
  'a': 'dummy1',
  'b': 1
}
const i = 'a'
console.log(exampleAB2[i as keyof ExampleAB2]) // 'dummy'

もうすこし、根気よくやってみよう。

「そうだ!interfaceを分けてみよう!」と思い、試してみた結果がこちら。。。

interface ExampleA {
  'a': {
    a_example: {
      example: string
    }
  }
}

interface ExampleB {
  'b': {
    b_example: {
      example: number
    }
  }
}

const exampleAB: (ExampleA | ExampleB) = {
  'a': {
    a_example: {
      example: 'dummy'
    }
  },
  'b': {
    b_example: {
      example: 1
    }
  }
}

const k = 'a'
const i = 'b'
console.log(exampleAB[k as keyof ExampleA].a_example.example) // 型 '"a"' の式を使用して型 'ExampleA | ExampleB' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。
  プロパティ 'a' は型 'ExampleA | ExampleB' に存在しません。
console.log(exampleAB[i as keyof ExampleB].b_example.example) // 型 '"b"' の式を使用して型 'ExampleA | ExampleB' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。
  プロパティ 'b' は型 'ExampleA | ExampleB' に存在しません。

orz... ダメだ...

ならば・・・と、exampleABに対して型アサーションを定義してあげればうまくいきました。

const k = 'a'
const i = 'b'
console.log((exampleAB as ExampleA)[k as keyof ExampleA].a_example.example) // 'dummy'
console.log((exampleAB as ExampleB)[i as keyof ExampleB].b_example.example) // 1

まさかブラケット記法で書くと型が強制的にanyになってしまうとは。。。。

TypeScriptで型を定義するのは、なかなか慣れないものですね。

最近は、QiitaでTypeScriptの型定義の演習問題を作ってくれている方がいて、こちらを使って修行しています。

qiita.com

とはいえ、なかなか問題が難しくて軽々解けない。

これが軽々解けるようになると、大幅にレベルアップできるかもしれない。。。そんな事を思いながら日々修行を続けていますm( = = )m