Ubuntu の更新で ThinkPad のポインティングデバイスが使えなくなった

ThinkPad X1 Extreme に Ubuntu 20.04 をインストールして使っているのですが、ある日カーネルアップデートをインストールしたら、ポインティングデバイスが一切効かなくなりました。これを復旧したときの記録です。

復旧手順

  1. /etc/default/grub を編集し、GRUB_CMDLINE_LINUX_DEFAULT に以下の記述を追加します。
    1
    GRUB_CMDLINE_LINUX_DEFAULT="i8042.nopnp=1 pci=nocrs"
  2. GRUB を更新します。
    1
    update-grub
  3. 再起動します。

参考

Camflix フィルムデジタイズアダプター FDA-135L を買いました。

Camflix のフィルムデジタイズアダプターの中望遠レンズ用の FDA-135L を買いました。それまでは Nikon のフィルムスキャナー CoolScan 5000ED を使っていたのですが、いい加減古くなってきたのと、フィルムをスキャンする機会もそんなにないので、断捨離するつもりで購入しました。

セットアップ

まず三脚を用意します。

三脚にレンズを付けます。使用するレンズは Canon EF100m F2.8L Macro IS です。

FDA-135L を取り付けます。FDA-135L のフィルター径は 62mm なのに対し、Canon EF100m F2.8L Macro IS のフィルター径は 67mm なのでステップダウンリングが必要です。

カメラを取り付けます。使用するカメラは、Canon EOS 5D Mark III です。

撮影

フィルムストリップのアダプターにフィルムを挟み込みます。アダプターは 5 コマなので1コマはみ出ます。

FDA-135L の本体にアダプターを差し込んで、

撮影します。FDA-135L は長さが調節できますので、ピントが合う位置まで伸び縮みさせます。光が必要なので窓際で撮影します。

Adobe Bridge で撮影結果をレビューします。EOS Utility を使えば撮影しながらレビューできます。

作例

どれもこれも暗いですが、トーンカーブをいじればそれなりに見られるようになります。ライトボックスでフィルムと見比べながらやるのがベターなのですが、面倒なので無調整で出しています。

まとめ

最近はカメラの解像度も上がっており、最近の微妙なフィルムスキャナーを買うよりかはこういうツールを使うほうがきれいにデジタル化できると思います。
ただし、準備が面倒くさいので相当気合を入れないと使わなさそうです。

Ubuntu の UEFI デュアルブートの復旧

Ubuntu で ThinkPad X1 Extreme の Firmware のアップデートをインストールしたら、デュアルブートの Ubuntu が起動しなくなりました。これを復旧したときの記録です。

USB メモリから Ubuntu を起動

  1. Windows で Ubuntu 18.04 の Live-DVD の ISO イメージをマウントします。
  2. 内容をフォーマットした USB メモリにコピーします。
  3. マシンを再起動します。
  4. Lenovo のロゴが出たら Enter キーを押します。
  5. F12 を押して USB から起動します。

GRUB を再インストール

  1. lsblk でデバイスの一覧を取得します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    NAME          MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT

    ...

    nvme1n1 259:0 0 953.9G 0 disk
    ├─nvme1n1p1 259:2 0 260M 0 part
    ├─nvme1n1p2 259:3 0 16M 0 part
    ├─nvme1n1p3 259:4 0 952.6G 0 part
    └─nvme1n1p4 259:5 0 1000M 0 part
    nvme0n1 259:1 0 953.9G 0 disk
    ├─nvme0n1p1 259:6 0 258M 0 part
    └─nvme0n1p2 259:7 0 953.6G 0 part
  2. Windows のディスクには 1000MB のリカバリ領域がありますので、それがない nvme0n1 の方をマウントします。また、/dev などもマウントします。そうしないと grub-install が失敗します。
    1
    2
    3
    4
    sudo su
    mount /dev/nvme0n1p2 /mnt
    mount /dev/nvme0n1p1 /mnt/boot/efi/
    for i in /dev /dev/pts /proc /sys /run; do mount -B $i /mnt$i; done
  3. mnt に chroot します
    1
    chroot /mnt
  4. GRUB をインストールします。
    1
    2
    3
    grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ubuntu
    Installing for x86_64-efi platform.
    Installation finished. No error reported.
  5. 再起動します。

参考

Amplify を触ってみた

Amplify でチャット風なアプリケーションを書いてみました。

初期設定

  1. 現時点では Amplify は Angular 9 をサポートしていません。Angular CLI の Version 8 をインストールします。
    1
    sudo npm install @angular/cli@8 --update -g
  2. Amplify の設定を行います
    1
    amplify configure
  3. Angular のプロジェクトを作成します。
    1
    ng new amplify-test --style=scss --routing
  4. Amplify の Angular サポートをインストールします。
    1
    2
    cd amplify-test
    npm install aws-amplify aws-amplify-angular --save-dev
  5. src/polyfills.ts に以下を追加します。
    1
    2
    3
    4
    (window as any).global = window;
    (window as any).process = {
    env: { DEBUG: undefined },
    };
  6. Amplify のセットアップをします。
    1
    amplify init
  7. CloudFormation のスタックが作成されます。
  8. Amplify の機能を追加します。認証を追加します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ amplify add auth
    Using service: Cognito, provided by: awscloudformation

    The current configured provider is Amazon Cognito.

    Do you want to use the default authentication and security configuration? Default configuration
    Warning: you will not be able to edit these selections.
    How do you want users to be able to sign in? Email
    Do you want to configure advanced settings? No, I am done.
    Successfully added resource amplifytestab53a8bb locally
    ...
  9. push します。
    1
    amplify push
  10. src/tsconfig.app.json に以下を追加します。
    1
    2
    3
    "compilerOptions": {
    "types" : ["node"]
    }
  11. main.ts に以下を追加します。
    1
    2
    3
    4
    import Amplify from 'aws-amplify';
    import awsconfig from './aws-exports';

    Amplify.configure(awsconfig);
  12. src/app/app.module.ts に以下を追加します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { AmplifyAngularModule, AmplifyService } from 'aws-amplify-angular';

    @NgModule({
    ...
    imports: [
    ...
    AmplifyAngularModule
    ],
    ...
    providers: [
    ...
    AmplifyService
    ]
    ...
    });
  13. npm start してみます。

認証画面を作る

  1. 認証画面のコンポーネントを作成します。
    1
    ng g component pages/auth
  2. src/app/pages/auth/auth.component.html を以下のようにします。
    1
    <amplify-authenticator></amplify-authenticator>
  3. src/app/app.component.html を以下のようにします。
    1
    <router-outlet></router-outlet>
  4. src/app/app-routing.module.ts にパスを追加します。
    1
    2
    3
    const routes: Routes = [
    { path: 'auth', component: AuthComponent },
    ];
  5. npm start します。
  6. ブラウザで http://localhost:4200/auth にアクセスします。
  7. アカウントを作ってみます。「Create account」をクリックします。
  8. 項目を埋めます。Username はメールアドレスである必要があります。
  9. 「Create account」を押します。
  10. メールで確認コードが飛んできますのでそれを入力します。
  11. 元の画面に戻ります。
  12. メールアドレスとパスワードを入力します
  13. ログインに成功します。

トップページの作成

  1. トップページのコンポーネントを作成します。
    1
    ng g component pages/top
  2. src/app/app-routing.module.ts にパスを追加します。
    1
    2
    3
    4
    const routes: Routes = [
    ...
    { path: '', component: TopComponent },
    ];

DataStore の作成

  1. 必要なパッケージをインストールします。
    1
    2
    npx amplify-app@latest
    npm i @aws-amplify/core @aws-amplify/datastore --save-dev
  2. Amplify API を追加します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ amplify add api

    ? Please select from one of the below mentioned services: GraphQL
    ? Provide API name: amplifyTest
    ? Choose the default authorization type for the API Amazon Cognito User Pool
    Use a Cognito user pool configured as a part of this project.
    ? Do you want to configure advanced settings for the GraphQL API No, I am done.
    ? Do you have an annotated GraphQL schema? No
    ? Do you want a guided schema creation? Yes
    ? What best describes your project: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)
    ? Do you want to edit the schema now? Yes
    Please edit the file in your editor: /home/vagrant/amplify-test/amplify/backend/api/amplifyTest/schema.graphql
    ? Press enter to continue
  3. amplify/backend/api/amplifyTest/schema.graphql ファイルを編集します。
    1
    2
    3
    4
    5
    6
    7
    8
    type Status
    @model
    @auth(rules: [{allow: owner}]) {
    id: ID!
    posted: AWSDateTime!
    content: String!
    poster: String
    }
  4. npm run amplify-modelgen を実行します。
  5. npm run amplify-push を実行します。
  6. AppSync に API が作成されます。
  7. 投稿ボタン、削除ボタンを実装します。
    • top.component.ts
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      import { Component, OnInit } from '@angular/core';
      import { FormControl, FormGroup } from '@angular/forms';
      import { DataStore, Predicates } from '@aws-amplify/datastore';
      import { AmplifyService } from 'aws-amplify-angular';
      import { from } from 'rxjs';
      import { Status } from '../../../models';

      @Component({
      selector: 'app-top',
      templateUrl: './top.component.html',
      styleUrls: ['./top.component.scss']
      })
      export class TopComponent implements OnInit {

      // データ
      data = from(this.query());

      // 投稿
      fcStatus = new FormControl('');
      formGroup = new FormGroup({
      status: this.fcStatus,
      });

      constructor(
      private amplifyService: AmplifyService,
      ) { }

      ngOnInit() {
      this.subscription();
      }

      isLoggedIn(): boolean {
      return this.amplifyService.auth().user !== null;
      }

      onSubmit() {
      DataStore.save(new Status({
      content: this.fcStatus.value,
      posted: new Date().toISOString(),
      poster: this.amplifyService.auth().user.attributes.email,
      }));
      this.fcStatus.setValue('');
      }

      doDelete(id: string) {
      DataStore.query(Status as any, id).then((status) => {
      return DataStore.delete(status);
      // this.list();
      }).catch((error) => {
      console.error(error);
      });
      }

      private async query() {
      const retVal = DataStore.query(Status, Predicates.ALL).then((statuses: Status[]) => {
      statuses.sort((l, r) => {
      if (l.posted > r.posted) {
      return -1;
      } else if (l.posted < r.posted) {
      return 1;
      } else {
      return 0;
      }
      });
      return statuses;
      });
      return retVal;
      }

      private subscription() {
      DataStore.observe(Status as any).subscribe((status) => {
      this.list();
      console.log(status);
      });
      }

      private list() {
      this.data = from(this.query());
      }
      }
    • top.component.html
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      <div class="wrapper">
      <div class="left">
      <div class="post_box" *ngIf="isLoggedIn()">
      <form [formGroup]="formGroup" (ngSubmit)="onSubmit()">
      <textarea placeholder="何しとんじゃわれぇ" formControlName="status"></textarea>
      <div class="post_box_action">
      <button type="submit">投稿</button>
      </div>
      </form>
      </div>
      <div *ngIf="!isLoggedIn()">
      <p>今なら無料! 今すぐ契約!!</p>
      </div>
      </div>
      <div class="right">
      <div class="contents">
      <div class="status" *ngFor="let status of data | async">
      <div class="status_poster"><span class="poster">{{status.poster}}</span></div>
      <div class="status_inner">
      <div class="status_content">{{status.content}}</div>
      <div class="status_posted">
      <a class="delete_link" href="javascript:void(0);" (click)="doDelete(status.id)">Delete</a>&nbsp;
      <span class="posted_time">{{status.posted}}</span>
      </div>
      </div>
      </div>
      </div>
      </div>
      </div>
  8. npm start で実行します。

ところが…

DynamoDB にいつまでたっても反映されません。ブラウザのコンソールを見ると以下のようなエラーが発生していました。

1
2
3
4
5
6
7
8
[WARN] 27:23.221 DataStore - Sync error subscription failed Connection
failed: {"errors":[{"message":"Validation error of type
FieldUndefined: Field '_version' in type 'Status' is undefined @
'onCreateStatus/_version'"},{"message":"Validation error of type
FieldUndefined: Field '_lastChangedAt' in type 'Status' is undefined @
'onCreateStatus/_lastChangedAt'"},{"message":"Validation error of type
FieldUndefined: Field '_deleted' in type 'Status' is undefined @
'onCreateStatus/_deleted'"}]}

作り方を変えてみる

今まで作ったものを捨てて作り方を変えてみます。

  1. まず amplify delete で CloudFormation スタックを削除します。
  2. プロジェクトを作成します。
    1
    2
    3
    4
    ng new sakai-amplify-test --style=scss --routing
    cd sakai-amplify-test
    npx amplify-app@latest
    npm i @aws-amplify/core @aws-amplify/datastore --save-dev
  3. Amplify Auth を追加します。
    1
    2
    amplify init
    amplify add auth
  4. amplify/backend/api/amplifyDatasource/schema.graphql を編集します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    type Status
    @model
    @auth(
    rules: [
    {
    allow: owner,
    operations: [create, update, delete, read]
    }
    ])
    {
    id: ID!
    posted: AWSDateTime!
    content: String!
    poster: String!
    }
  5. モデルを生成します。
    1
    2
    amplify update api
    npm run amplify-modelgen
  6. npm install aws-amplify aws-amplify-angular @aws-amplify/ui --save-dev
  7. amplify push を実行します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    $ amplify push
    ✔ Successfully pulled backend environment dev from the cloud.

    Current Environment: dev

    | Category | Resource name | Operation | Provider plugin |
    | -------- | ------------------------ | --------- | ----------------- |
    | Api | amplifyDatasource | Create | awscloudformation |
    | Auth | sakaiamplifytestcce6b8e1 | Create | awscloudformation |
    ? Are you sure you want to continue? Yes

    GraphQL schema compiled successfully.

    Edit your schema at /home/vagrant/sakai-amplify-test/amplify/backend/api/amplifyDatasource/schema.graphql or place .graphql files in a directory at /home/vagrant/sakai-amplify-test/amplify/backend/api/amplifyDatasource/schema
    ? Do you want to generate code for your newly created GraphQL API Yes
    ? Choose the code generation language target angular
    ? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.graphql
    ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
    ? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
    ? Enter the file name for the generated code src/app/service/api.service.ts
    ディレクトリを掘らなかったので src/app/service/api.service.ts の作成に失敗しました。
  8. 古いのから移植します。
  9. 起動します。
    1
    npm start
  10. ユーザーを作成します。
  11. 今度はうまくいきました。

まとめ

  • 単純なシステムであれば、簡単に実装することができる。
  • スキーマ設計は GraphQL だけで行うので、DynamoDB 側のことは意識する必要がなくなる。
  • 自動的に CloudFormation スタックが構築されるので、システム開発に集中できる。ただし、インフラの細かい制御ができるかは不明。
  • DataStore はオフラインでも動作するのでスマホサイトには最適
  • プロジェクトの作り方に気を付けないとハマる。
  • バックエンドがシンプルになる分、フロントエンドの負荷が高くなる。
  • フロントエンドとバックエンドの両方から DynamoDB を触りたい場合、どう設計するべきか ?
  • ログインが遅い。
  • データのオーナーが自動的に設定されるせいか、チャットアプリにならなかった…

参考

Squid でプロキシ認証を自動化する

プロキシ認証を自動化する

Basic 認証を要求してくるプロキシサーバーがありますが、これによっていろいろな問題が発生します。

  • sbt でアーティファクトがダウンロードできない。
  • MySQL Server のインストールで MySQL Installer が VC++ のランタイムをダウンロードできない。
  • Visual Studio Code の機能拡張の更新ができない。
  • MSYS Git が SSH で動かない。

回避策はあったりなかったりしますが、いちいち調べるのも時間の無駄ですので、これを回避するために自分のマシンにプロキシサーバーを立てて、認証を代行させます。

  1. Squid をインストールします。

  2. C:\Squid\etc\squid\squid.conf を編集します。パスワードは URL エンコードします。Squid はファイルの上から順番に設定を読み込んでいくので、設定を記述する場所に注意します。

    1
    2
    3
    4
    # Example rule allowing access from your local networks.
    # Adapt to list your (internal) IP networks from where browsing
    # should be allowed
    cache_peer proxy.zak.sobal.co.jp parent 3128 0 no-query no-netdb-exchange login=ユーザー名:パスワード
    1
    2
    acl SSL_ports port 22
    acl Safe_ports port 22
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #
    # Recommended minimum Access Permission configuration:
    #
    acl TRACE method TRACE
    acl GET method GET
    acl POST method POST
    acl PUT method PUT
    acl OPTIONS method OPTIONS
    acl DELETE method DELETE

    acl SSL method CONNECT
    never_direct allow all
    1
    dns_nameservers [DNS サーバー1] [DNS サーバー2] ...
  3. Squid を再起動します。コントロールパネル→管理ツール→サービス→Squid for Windows を選択して「再起動」ボタンを押します。

  4. プロキシの設定を変更します。コントロールパネル→インターネットオプション→接続→LAN 設定→プロキシサーバ

    • アドレス: 127.0.0.1
    • ポート: 3128
  5. ブラウザで接続を確認します。ブラウザは再起動したほうが確実です。

仮想マシンのプロキシ設定

仮想マシンから、ホストの Squid を経由してインターネットにアクセスするには、以下のように設定します。

  • VirtualBox
    • アドレス: 10.0.2.2
    • ポート: 3128
  • Android Virtual Device
    • アドレス: 10.0.2.2
    • ポート: 3128

hexo を S3 でホスティングして Cloud Front で公開する。

このブログサイトを公開したときのメモです。

hexo を S3 でホスティングする

hexo で作成したコンテンツを S3 に置くには、 hexo-deployer-aws-s3 を使います。npm install hexo-deployer-aws-s3 --save-dev して、_config.yml ファイルに以下の記述を追加します。

1
2
3
4
5
deploy:
type: 'aws-s3'
region: S3 のリージョン
bucket: S3 のバケット名
prefix: S3 のフォルダ名

S3 バケットの「アクセス権限」設定を行います。「ブロックパブリックアクセス」を「オフ」にします。「オン」の状態だとデプロイに失敗します。

あとは、npm run build && npm run deploy でコンテンツが S3 に転送されます。

次に S3 の Static website hosting を有効にします。インデックスドキュメントは index.html、エラードキュメントは空でもいいですが、とりあえず index.html を指定しました。
また、ここに表示される「エンドポイント」は Cloud Front の設定で使用います。

サーバー証明書を取得

独自ドメインで HTTPS サイトを公開するために、サーバー証明書を取得します。AWS を使っているので Certificate Manager で証明書を取得します。
「証明書のリクエスト」ボタンを押すと、証明書のタイプを選択する画面になります。「パブリック証明書のリクエスト」を選択します。

ドメイン名を設定します。

「DNS の検証」を選択します。

タグは特に設定せず次のステップに進みます。

ドメイン名を確認して「確定とリクエスト」を押します。

DNS に設定するパラメータが表示されますので、これを DNS に登録します。

Cloud Front で公開する

Cloud Formation を使って、Cloud Front を立ち上げます。設定のポイントとしては

  • オリジンに S3 の Static website hosting のエンドポイントを指定する

これだけです。S3 バケットを指定すると、サブフォルダの index.html を解決できなくて 403 エラーになるページが発生します。

Cloud Formation テンプレートの例を示します。このテンプレートでは独自ドメインで hexo のコンテンツを公開することができます。HTTPS で公開するので Certificate Manager でリクエストした証明書の ARN を指定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
AWSTemplateFormatVersion: 2010-09-09
Parameters:
BlogFQDN:
Description: FQDN for Blog
Type: 'CommaDelimitedList'
S3BlogBucketName:
Description: S3 Bucket Name for blog
Type: String
AcmCertificateArn:
Description: ACM Certificate ARN
Type: String

Resources:
BlogOriginAccessId:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: 'Blog hosting'

BlogDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases:
!Ref BlogFQDN
Comment: 'Blog site.'
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
DefaultTTL: 86400
ForwardedValues:
QueryString: true
MaxTTL: 86400
MinTTL: 0
TargetOriginId: !Ref S3BlogBucketName
ViewerProtocolPolicy: 'redirect-to-https'
DefaultRootObject: 'index.html'
Enabled: true
HttpVersion: http2
IPV6Enabled: true
Origins:
- DomainName: !Sub "${S3BlogBucketName}.s3-website-us-east-1.amazonaws.com"
CustomOriginConfig:
OriginProtocolPolicy: http-only
Id: !Ref S3BlogBucketName
OriginPath: ''
PriceClass: 'PriceClass_All'
ViewerCertificate:
AcmCertificateArn: !Ref AcmCertificateArn
MinimumProtocolVersion: TLSv1.1_2016
SslSupportMethod: sni-only

このテンプレートを使って、以下のコマンドでスタックを構築します。

1
2
3
4
5
6
7
8
9
aws cloudformation deploy \
--region us-east-1 \
--stack-name [blog-site] \
--template-file cloud-front.yaml \
--capabilities CAPABILITY_IAM \
--parameter-overrides \
BlogFQDN=[公開するサイトの FQDN] \
S3BlogBucketName=[バケット名] \
AcmCertificateArn=[サーバー証明書の ARN]

これで、1時間待てばコンテンツを公開できます。

20200201

メモ置き場がてらブログを始めます。
個人的に役に立ちそうなこと(他人に役に立つかわからないようなこと)を書いていきたいと思います。

自己紹介

  • 1973年生まれ、魚座
  • 都内の IT 関係の企業にプログラマーとして勤務中
  • 開発経験
    • Windows アプリケーション
    • Linux アプリケーション
    • WEB 系
    • 組み込み Linux
  • 使ったことがある言語
    • C
    • C++
    • C#
    • Java
    • Python
    • TypeScript
    • JavaScript
    • Scala
    • SQL
  • 使ったことがあるフレームワーク
    • Spring
    • Akka
    • Angular (not -JS)
    • Angular Material
    • ASP .Net
  • 使ったことがあるデータベース
    • Oracle
    • MySQL
    • Postgresql
    • MS SQL Server
    • mongoDB
  • 開発に使ったことがあるOS
    • Windows
    • MacOS
    • Android
    • Linux
  • その他使ったことがあるもの
    • ErasticSearch
    • AWS
      • EC2
      • Lambda
      • DynamoDB
      • CloudFormation
      • Cognito
      • ElasticBeanstalk
      • APIGateway
      • Certificate Manager
      • SES
      • Route 53
    • Google Cloud Platform
      • BigQuery
    • Mastodon
      • 個人サーバー運営中
    • Nginx
    • Apache HTTP Server