20190710のAWSに関する記事は15件です。

TerraformerのためのTerraformer

Terraformerというツールを最近見つけて、
「えぇ!!めちゃ便利やん!!」と少々感動したのでご紹介します。

Terraformer

現存しているリソースの.tfファイルとtfstateを自動作成してくれるCLIツールです1
レポジトリはこちら

インストール

Homebrewでインストールできます。
brew install terraformer
その他のインストール方法はこちら

利用する前にディレクトリ(~/.terraform.d/plugins/{darwin,linux}_amd64/)を作成し、ここにプロバイダをインストールしておく必要があります。

$ pwd
/Users/<user-name>/.terraform.d/plugins/darwin_amd64
$ wget https://releases.hashicorp.com/terraform-provider-google/2.10.0/terraform-provider-google_2.10.0_darwin_amd64.zip
$ unzip terraform-provider-google_2.10.0_darwin_amd64.zip

使い方

以下プロバイダとしてGCPを利用した例です。
AWSなど他のプロバイダについても似たような感じだと思います(未検証)。

指定したリソースのファイルを作る

コマンド一つで楽ちん。--resourcesオプションで、.tfファイルとtfstateを作成したいリソースの指定します。

terraformer import google --resources=cloudFunctions --zone=asia-northeast1-a --projects=<your-project-id>

複数選択することで、一気に作れます。プロジェクトも複数選択できます。

terraformer import google --resources=cloudFunctions,gke,gcs,instances --zone=asia-northeast1-a --projects=<project-A>,<project-B>

作成された.tfファイルとtfstate./generated/<provider>/<project-name>/<resources>にあります。

ローカルにファイルを作成する前に確認したい

importの代わりにplanを使います。

terraformer plan google --resources=cloudFunctions --zone=asia-northeast1-a --projects=<your-project-id>

すると./generated/<provider>/<project-name>/terraformerplan.jsonというJSONファイルが作成されるので、ここから確認ができます。

必要であればJSONファイルを変更して、.tfファイルを作成します。

terraformer import plan ./generated/<provider>/<project-name>/terraformer/plan.json

メリット/デメリット

個人の感想です。ツッコミがあればコメントをいただきたいです。

メリット

コードを書かなくていい

自動作成されるので、無論書く必要がないです。

フォーマットが一定なので見やすい

人が書くコードでは、デフォルトの設定として書かれないような設定も書いてくれるので、
リソース単位でコードを読む分には読みやすいと感じました。

デメリット

生成されるコードが0.12に対応していない

terraform 0.12upgradeでほとんど対応できると思いますし、これから置き換えられるような気がします(issue)。

リソースごとなので、依存を意識してリソースを立てる必要がある

.tfファイルが作成された後の話です。ここでようやく頭を使うので、許容範囲かとも思います。

終わりに

issueを見てみると、IAMやAWSのリソースについても開発が進んでいるようで、これからもっと便利になりそうです。

参考


  1. reverse Terraformというらしい。この言葉、使っていきたい。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ほとんどアクセスのない個人開発サービスがAWSに月額いくら払っているか全て公開します

はじめに

「AWSって結局いくらかかるの?」
これが結構見積もりが難しい問題で、ざっくりAWSのようにざっくり見積もれるサービスが登場したのはその見積もりの難しさから来ていると思う。

個人的に開発しているwebサービスをAWSで公開してからしばらくたって、月の料金が安定しているので、ここで公開してみる。

しょっぼいwebサービスを公開したいけど、AWSだといくら掛かるのかなー?という方に向けて書いているので、「herokuなら無料だぜ!もしくは7ドルだぜ!」とかいう話ではないです。

サービス概要

公開したサービスはこちら。
https://uwaoe.net

html5のcanvas機能を利用してブラウザ上でお絵かきすることができて、他の人が描いた絵にうわがき(というか落書き)することができる。
サーバはRuby on Rails、フロントはVue.jsで作った。

利用ユーザは2人。
たった2人!!
これはストレージ的にもアクセス数的にも安上がり!!!

料金内訳

AWSコンソールのコストエクスプローラから、半年間の支払い金額を取得した。

EC2 ELB EC2 インスタンス EC2 その他 税金 Route 53 S3 CloudFront 合計
2019-01-01 18.08 11.31 3.60 2.68 0.50 0.02 0.01 36.20
2019-02-01 16.33 10.21 3.60 2.46 0.50 0.02 0.02 33.14
2019-03-01 18.08 11.31 3.60 2.68 0.50 0.02 0.01 36.20
2019-04-01 17.50 10.92 3.60 2.60 0.50 0.02 0.01 35.15
2019-05-01 18.08 11.31 3.60 2.68 0.50 0.02 0.01 36.20
2019-06-01 17.50 10.94 3.60 2.61 0.50 0.02 0.01 35.18

※ 単位は$
※ 見易さのために少数第3位を四捨五入しています

EC2 ELB

平均 $17.595(ドル円108円として約1900円

EC2インスタンスは1台しか使ってないのだけど、ELBで簡単にhttpsを使いたかったので導入。
結構する。httpsにしたいだけなので、Let's Encryptとか使ってEC2に直接証明書をインストールしたほうが安上がりだったかな。
初回12ヶ月間は1台分無料だけど、無料期間は切れているため課金。

EC2 インスタンス

平均 $11.000(ドル円108円として約1188円

インスタンスはt2.microが1台。t3ではない。
microインスタンスには、初回12ヶ月は1台分が無料というキャンペーンがあるんだけど、12ヶ月間の無料期間は過ぎているためお金がかかっている。

EC2 その他

平均 $3.600(ドル円108円として約389円

EBSの料金ぽい。
SSD30Gを使っているので計算は$0.12 * 30
空きがめっちゃあるのでもっと少なくてもいけそう。

Route 53

平均 $0.500(ドル円108円として約54円

uwaoe.netのドメインを登録している。
リクエスト数で料金が上がるらしいが、現状やっすいので、金額的には気にならないレベル。

S3

平均 $0.020(ドル円108円として約2円

おえかきされた絵やアップロードされたユーザのアバターを保存している。
put系の書き込みリクエストが1000件、get系の取得リクエストが45000件、データ送料が 50MB程度なので大したことがない。
rails側で画像のサムネイルを何種類か作成してS3に上げたり、画像アップロード用のgemのshrineがキャッシュをS3に作りまくったりしているけど、軽い。
現状やっすいので、誤差レベル。

関係ないけど、gem shrineはBase64エンコードされた画像を簡単に扱えるので、canvasの画像を扱いたい場合におすすめ。

Coundfront

平均 $0.012(ドル円108円として約1円

t2.microサーバから画像配信するのは不安があったので、画像の配信にはCloudfrontを使用するようにした。
現状やっすいね。

RDS

使ってない。
節約のためにEC2インスタンスにmysql-serverを入れちゃったのだけど、メモリが足りなくて辛い。
t2.microってメモリ0.5Gじゃん? mysqldがメモリを160MB使っていて、railsを動かしているpumaサーバが180MB使っている。その他サービスで使われているメモリもありで、デプロイを実行するメモリが足りない。デプロイする時、assets:precompileのたびにメモリが足りなくてアプリケーションサーバが死ぬというカッコいい状態になっている。

CloudWatch

使ってない。
別にサービスが何時間止まろうが困らないし・・。

SES

使ってるけど0円。
月62,000件までは0円らしい。

合計

平均 $35.345(ドル円108円として約3817円

おわりに

月3800円。上で書いたようにALBが無駄っぽいので月2000円ぐらいまで節約できると思う。
今の所、広告や課金要素は全く無いので完全赤字になっているが、趣味として見たら安い。

AWSには初回12ヶ月無料なことがいっぱいあって、1年間であれば次の構成が無料にできそう。
* EC2 microインスタンス1つ
* ALB 1つ
* RDS microインスタンス1つ

新規に個人開発のサービス公開を目論んでいる方は、AWSもインフラの候補にしてみてはどうだろうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Developer Associate 合格しました(2019/6)

AWS 3冠になりました:v:

この度晴れて、アソシエイトの資格全てに合格することができました!

受験期間としては3月末〜6月末の約3ヶ月間です。
間で業務のためRailsにはまっていた時期もあったので実質3ヶ月かからないくらいだと思っています。

AWS Developerアソシエイト試験に関して

その中でも今回はSysopsの反省を生かして一発で合格できたDeveloperアソシエイトに関してまとめようと思います。

一言でまとめると、、、

めっちゃ難しかった!!!

につきます。

Sysopsもかなり理解を問われる問題もありますが、問題改定を終えて肌感覚的な意見ですが3つで一番難しかったです。。

最後の最後まで不安なまま試験を終える形になってしまいました:joy:

アソシエイトの新試験の難易度は

Developer > Sysops > Solution Architect

の順になっているといっても過言ではありません。

得意分野は人それぞれなので一概には言えませんが、参考にしていただけたらと思います。

その中でDeveloperの勉強で行ったこと

今回も前回を踏襲して問題集を解きながら、Black Beltを三回づつくらい読みました。

問題集は自分に合うものでいいかと思いますが、私は英語の同じものを5回分を最低三回ずつは解いて95%以上取れるようになるまでやりました。

BlackBeltで特に読んだ方が良い資料

やはり開発者向けなので、基本こちらになります。他二つの知識はある前提です。

・ Codebuild ←特に重要
・ CodePipeline
・ CodeDeploy ←特に重要
・ CodeCommit
・ ElasticBeanstalk
・ Lambda ←特に重要
・ API Gateway ←特に重要
・ ElastiCache
・ DynamoDB ←特に重要
・ SQS
・ KMS
・ ECS
・ X-Ray ←特に重要
・ Step Functions

上記は必須の知識になるかと思いますが、
これに加えて基本的なサービスの理解は必ず必要になります。

基本的にほとんどの問題が複合された問題なので、丸覚えだと当たり前ですがほとんど模試の問題も出ないので理解しておくことが大切です。

:point_up:わからない用語は調べて理解する
:v: なぜそうなるのがAWSのベストプラクティスに則っているのか、常に自分に問いかけながら学習する

これを忘れなければきっとみなさまも合格できると思います!!

次はプロフェッショナルの取得(8月,9月)に向けて頑張ります:raised_hands:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

s3オブジェクト一覧取得(Go)

はじめに

S3バケットのオブジェクト一覧を取得する場合は通常awscliを使用して

aws --profile {profile} s3 ls --recursive s3://{bucket}/{prefix}

のような感じで実行すると思います。
オブジェクトが少なければこれで問題ないですが、大量にあると時間がかかります。
実際に特定prefix配下の(1億ほど)オブジェクト一覧を取得する必要があり、awscliだと
数日かかることが見えていたので、Goで実装してどれだけ速くなるか試してみました。

コード

main.go
package main

import (
    "flag"
    "fmt"
    "os"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/credentials"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
)

var (
    argRegion  = flag.String("region", "ap-northeast-1", "specify region name")
    argProfile = flag.String("profile", "", "specify credential profile name")
    argBucket  = flag.String("bucket", "", "specify bucket name")
    argPrefix  = flag.String("prefix", "", "specify object key prefix")
)

func main() {
    flag.Parse()
    defer func() {
        if r := recover(); r != nil {
            flag.Usage()
            fmt.Println(r)
            os.Exit(1)
        }
    }()

    config := aws.Config{Region: argRegion, MaxRetries: aws.Int(10)}
    if *argProfile != "" {
        creds := credentials.NewSharedCredentials("", *argProfile)
        config.Credentials = creds
    }
    sess := session.New(&config)
    svc := s3.New(sess)

    params := &s3.ListObjectsV2Input{Bucket: argBucket, Prefix: argPrefix}
    jst, _ := time.LoadLocation("Asia/Tokyo")
    svc.ListObjectsV2Pages(params,
        func(page *s3.ListObjectsV2Output, lastPage bool) bool {
            for _, obj := range page.Contents {
                fmt.Printf("%s %10d %s\n", obj.LastModified.In(jst).Format("2006-01-02 15:04:05"), *obj.Size, *obj.Key)
            }
            return *page.IsTruncated
        })

}

実行方法

# コンパイル
go build -o s3lsall main.go
# 実行
./s3lsall -profile {profile} -bucket {bucket} -prefix {prefix}

結果表示は、awscli(aws s3 ls)と同じフォーマットで出力されます

2019-01-22 11:46:39       8023 hoge/197352643.json
2019-01-23 11:16:28       5000 hoge/197512582.json
2019-01-23 19:46:02       4995 hoge/197512839.json

性能比較(参考)

t3.smallインスタンスで実行し、10万オブジェクトの一覧取得で比較

使用ツール 所要時間 CPU使用率
awscli 65秒 30%
s3lsall(go実装) 15秒 10%

まとめ

awscliで時間がかかりそうな大量リソースに対する処理などは、必要な処理に特化した
プログラムをGoでサクッと実装するとよさそうですね(速度/負荷的にも)

Go以外の言語でsdkを使ってプログラムを書いてもいいですが、そのランタイム環境を用意
しないといけないので、ワンバイナリ配布で実行できる、というのもGoのいいところですね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.envのCredentialsでboto3を使う

動機

.envにAWSのCredentialsを記述してs3からファイルをダウンロードしたい。

前提

pipenvをつかってpython環境を構築する。

手順

準備

.envを作成してアクセストークンなどを入力する。

AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=yyy

読み込み

.envを作成したら仮想環境を作成、立ち上げる。

$ pipenv install boto3
$ pipenv shell

仮想環境を立ち上げると.envを自動で読み込み環境変数に設定できる。
スクリプト中ではosモジュールから環境変数にアクセスする。

# REPLを立ち上げ
>>> import os
>>> os.environ['AWS_ACCESS_KEY_ID']
'xxx'

ファイルダウンロード

client = boto3.client(
    's3',
    aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY']
)

client.download_file(AWS_S3_BUCKET_NAME, s3_key, file_path)

管理

.envはgitで管理せずに.gitignoreに追加する。
かわりに値を空白にした.env.sampleをgitで管理しておく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Session ManagerでSCPとかSSH Portforwardもできる!

何の話?

以前、うちのエンジニアの人たちに、EC2に接続するときはSSHやめて、セッションマネージャーすすめてみたんですが、SSHでのターミナルだけでなく、SCPでのファイルコピーとか、インスタンス内でサービス立ち上げてアクセスするためにPortforwardとか頻繁に使うってことであまりセッションマネージャーは使っていなかったんですが・・・

こんなアナウンスが。
Session Manager launches tunneling support for SSH and SCP

これはもしや!

セットアップ

https://aws.amazon.com/jp/about-aws/whats-new/2019/07/session-manager-launches-tunneling-support-for-ssh-and-scp/

クライアント側

セッションマネージャープラグイン

1.1.22.0 が必要なのでアップデートしましょう。
https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html

$ session-manager-plugin --version
1.1.23.0

SSH Config

~/.ssh/config に下記を追加します。

~/.ssh/config
# SSH over Session Manager
host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

i-* はインスタンスIDを指定したとき、 mi-* はオンプレのサーバをSSMで管理している場合に使うIDです。
つまり、インスタンスIDに対してSSHアクセスしたときには、ssmでトンネル張って、その中をSSH接続するということですね。

EC2インスタンス側

SSMエージェント

2.3.672.0 以上が必要ですのでRunCommandでアップデートしてください。

EIP

SSM使うので不要です。

セキュリティグループ

もちろん外部からのSSHへのアクセス許可は不要です。

SSH Key

前述のとおり、セッションマネージャーのトンネル内でSSH接続するので、keyの付与は必要になります。
ない場合、SSHの鍵が違う場合はこんな感じ。

Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

なので、EC2にsshdは必要ってことですね。

接続

普通にSSH

普通にSSHコマンドでインスタンスID指定して接続できました。

$ ssh ec2-user@i-xxxxxxxxxxxxxxx -i ~/.ssh/id_rsa
ast login: Wed Jul 10 03:23:25 2019 from localhost

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
No packages needed for security; 6 packages available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-0-1-104 ~]$ 

おお!
普通にOSのユーザーで使えますね。

トンネル張ってるので、ログ的にはlocalhostから接続されていることになってます。

/var/log/secure
Jul 10 03:08:19 ip-10-0-1-104 sshd[4173]: Accepted publickey for ec2-user from 127.0.0.1 port 49148 ssh2: RSA SHA256:aHEcVcQEixW0amxNSFVjabsst2Jglm7KDbYr+Tyb3Zjko
Jul 10 03:08:19 ip-10-0-1-104 sshd[4173]: pam_unix(sshd:session): session opened for user ec2-user by (uid=0)
Jul 10 03:08:38 ip-10-0-1-104 sshd[4207]: Received disconnect from 127.0.0.1 port 49148:11: disconnected by user
Jul 10 03:08:38 ip-10-0-1-104 sshd[4207]: Disconnected from 127.0.0.1 port 49148
Jul 10 03:08:38 ip-10-0-1-104 sshd[4173]: pam_unix(sshd:session): session closed for user ec2-user
Jul 10 03:14:29 ip-10-0-1-104 sshd[4253]: Accepted publickey for ec2-user from 127.0.0.1 port 49216 ssh2: RSA SHA256:aHEcVcQEixW0amxNSFVjJyt2Jglm7KDbYr+Tyb3Zjko
Jul 10 03:14:29 ip-10-0-1-104 sshd[4253]: pam_unix(sshd:session): session opened for user ec2-user by (uid=0)
Jul 10 03:14:35 ip-10-0-1-104 sudo: ec2-user : TTY=pts/0 ; PWD=/home/ec2-user ; USER=root ; COMMAND=/bin/tail /var/log/secure

SCPしてみる

$ scp test.txt ec2-user@i-xxxxxxxxxxxxxxx:test.txt
test.txt                                                                                      100%  191    11.2KB/s   00:00
$ ssh ec2-user@i-xxxxxxxxxxxxxxx ls 
test.txt

おおお!

SSH PortForwardしてみる

$ ssh ec2-user@i-xxxxxxxxxxxxxxx sudo yum -y install httpd                                            
$ ssh ec2-user@i-xxxxxxxxxxxxxxx sudo systemctl start httpd       

$ ssh -fNL 8080:localhost:80 ec2-user@i-xxxxxxxxxxxxxxx
$ curl -I localhost:8080
HTTP/1.1 403 Forbidden
Date: Wed, 10 Jul 2019 03:24:08 GMT
Server: Apache/2.4.39 ()
Upgrade: h2,h2c
Connection: Upgrade
Last-Modified: Thu, 04 Apr 2019 18:08:00 GMT
Accept-Ranges: bytes
Content-Length: 3630
Content-Type: text/html; charset=UTF-8

おおおお!!!

残念ながら・・・

セッションマネージャーには出力をS3や、CloudWatch Logsに送信する機能がありますが、この接続の場合は出力されないようです。
出力はSSHになるので当然かもしれないですが・・・

まとめ

今度こそSSHするためのアクセス許可は考えなくていいことになりそう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Personalizeを導入してわかった12のこと

はじめに

私の所属しているエイチームグループでは「サイマ」という自転車のECサイトを運営しているのですが、先日レコメンデーションエンジンとしてAmazon Personalizeを導入しました。サービスに導入しようとすると少し触っただけではわからなかったことが見えてきたので、Amazon Personalizeを導入する過程でわかったことをつらつらと書きだしてみました。
この記事を読む前提知識として、Amazon Personalizeの全体像を把握していたほうが良いです。Amazon Web Servicesブログの以下の記事が図解もあって個人的にはわかりやすいと思います。

Amazon Personalizeを導入してわかった12のこと

ソリューションとキャンペーンに関して

1. 良いデータの持ち方は試行錯誤して探る必要がある

  • トレーニングに使うデータの持ち方(スキーマ)を任意に決められるのですが、どういう持ち方をすれば良い結果になるかわかりません。
  • Amazon Personalizeがどういう学習をしているかわからないこともあり、良い結果を出すためには下記のような手順を繰り返す必要があります。
    1. データの持ち方を変更する
    2. キャンペーンを作成する
    3. メトリクスを確認する
  • データ量が多ければ良いというものではなくて、1年分の履歴を使った時より3か月分の履歴を使ったときのほうが良いメトリクスになるなんてことも発生しました。

2. 商品情報のスキーマのcategorizeは設定したほうが良い

  • 商品情報のスキーマには「categorical」というオプションがあって、カテゴリ分けに使う項目に設定しておくと同一カテゴリの商品がレコメンドに出やすくなります。

    {
      "name": "GENRE",
      "type": "string",
      "categorical": true
    }
    

3. 個人別レコメンドを使ったほうが良い結果になる

  • 商品別レコメンド(レシピはSIMS)と個人別レコメンド(レシピはHRNN-METADATA)で下記のようにレコメンドのメトリクスに差がつきました。なお、下記のメトリクスの指標は2種類とも1に近いほど良好なものです。

    レシピ Normalized discounted cumulative at 25 Mean reciprocal rank at 25
    SIMS 0.3665 0.2306
    HRNN-METADATA 0.5559 0.3725
  • サイトへの導入成果ではクリック率・コンバージョン率ともにHRNN-METADATAがSIMSの1.8倍近くになりました。

4. Auto MLを使える場合は使ったほうが良い

  • 同じレシピでAuto MLを使ったソリューションとそうでないソリューションを比較すると下記のようにAuto MLのほうが良いメトリクスになりました。

    レシピ Normalized discounted cumulative at 25 Mean reciprocal rank at 25
    HRNN-METADATA(Auto MLあり) 0.5559 0.3725
    HRNN-METADATA(Auto MLなし) 0.5149 0.3432
  • Auto MLを使わない場合は学習に使うパラメータを手動で設定して良い結果になるように調整する必要があります。

  • ただ、現状ではAuto MLはHRNNとHRNN-METADATAの2つのレシピしか選択できません。

5. キャンペーンのMinimum provisioned TPSは重要

  • キャンペーンにMinimum provisioned TPSという設定項目がありますが、これは料金とスピードにかかわる重要な設定です。
  • TPSはtransaction per secondの略で、Minimum provisioned TPSは1秒間に何回レコメンドを取得できるようにするのかという設定です。
  • ここに設定した値が1時間あたりの最低料金になります。
    • 1 TPSあたり0.2USDが基本料金で、Minimum provisioned TPSを5にすると1時間1USD、10にすると1時間2USDが最低料金としてかかるといった具合です。
    • 1時間あたりの金額なので、「1時間あたりの金額×24時間×1か月の日数」が1ヶ月分の料金としてかかります。
  • 1秒間にこの数値を超えるアクセスがあった場合、レコメンド情報の取得に遅延が発生します。
    • 同じような状況が継続すると1時間あたりの料金もその分増えます。
  • つまり、この数値が高ければ余分な料金、低ければ表示の遅延が発生します。

サイトへの組み込みに関して

6. 最低限必要なAPIは3つだけ

  • APIリファレンスを見るとたくさんのAPIが並んでいますが、通常の利用で必要なものは以下の3つだけです。
    • GetRecommendations
      • レシピタイプがRELATED_ITEMSもしくはUSER_PERSONALIZATIONのキャンペーンでレコメンド情報を取得する
    • GetPersonalizedRanking
      • レシピタイプがSEARCH_PERSONALIZATIONのキャンペーンでレコメンド情報を取得する
    • PutEvents
      • ユーザーの行動をレコメンドに反映する(イベントトラッカー)
  • 他のAPIはAWSコンソールから行うレコメンドの設定をプログラムから実行したい場合に使います。

7. 個人別レコメンドの引数に商品IDを渡しても結果が変わらない

  • 個人別レコメンドはユーザーIDだけに基づいてレコメンドを行っているようで、レコメンド情報を取得するAPIに商品IDを渡しても結果が変わりません。
  • そのため、商品ページに個人別レコメンドを表示する場合はユーザーの行動履歴に閲覧した商品を反映した後にレコメンド情報を取得するようにしないと、ひとつ前の商品を見たときのレコメンド結果が商品ページに出てしまいます。
  • 上記の問題を回避するために、レコメンドの表示は遅延ロードするようにしました。
    1. 商品ページを表示した段階でイベントトラッカーを呼び出し、行動履歴に商品ページの閲覧を反映する
    2. レコメンドの表示する場所までスクロールしたときにAjaxでレコメンド情報を取得して表示する

8. レコメンドの効果を確認できる仕組みを作ったほうが良い

  • Amazon PersonalizeにはASPのレコメンデーションエンジンのようなレコメンドの効果測定機能がありません。
  • そのため、レコメンドのクリック率やコンバージョン率が把握できるように効果測定の仕組みは作っておいたほうが良いです。

9. 型の違いに注意が必要

  • スキーマの設定でデータ型を指定できるのですが、Amazon PersonalizeのAPIは型を区別します。
  • APIを呼び出すときはintで定義したスキーマには数値型の値、stringで定義したスキーマには文字列型の値を渡すことが必要です。

10. レコメンド情報をまとめて取得すると料金が高くなる

  • 料金体系上、短時間にレコメンド情報の取得が集中するとその時間の料金が高くなります。
  • そういった状況としてはメールマガジンの送信時に多数のユーザーのレコメンド情報をまとめて取得するケースなどが考えられます。
  • レコメンド情報を取得するAPIの所要時間は私の環境だと1回あたり50〜100ミリ秒くらいでしたので、秒間10回以上APIを呼べそうでした。
  • Amazon PersonalizeでAPIの実行回数の上限は設定できないため、料金が気になるようであればAPIの実行回数が多くなりすぎないようにする仕組みを入れたほうが良いです。

導入後の運用に関して

11. 最新の情報に基づいてレコメンドをするためには再トレーニングが必要

  • Amazon Personalizeには商品情報や会員情報をリアルタイムでソリューションに反映する仕組みがありません。
  • そのため、最新の情報に基づいてレコメンドするためにはトレーニングに使うCSVを改めてアップロードして、ソリューションの新しいバージョンを作成する必要があります。バージョンの作成はソリューションの画面から実行できます。 version.PNG
  • トレーニング時間に料金がかかるるため、料金の見積もりではこの点も計算に反映したほうが良いです。

12. アクセス数に合わせてキャンペーンのMinimum provisioned TPSを調整したほうが良い

  • 4で挙げたようにMinimum provisioned TPSは高くても低くても良くないので、アクセス数に合わせて調整したほうが良いです。
  • Minimum provisioned TPSはキャンペーン情報を更新するAPIから変更できるため、アクセスログに基づいて自動で調整するようなことも可能です。

最後に

Amazon Personalizeは世の中に導入事例やノウハウがまだ少ないのですが、ポイントを押さえれば導入は難しくありません。手探りだった私達のケースでも30人日程度の工数で導入できました。
また、Amazonのレコメンドと同じアルゴリズムを使っているだけのことはあって導入効果もしっかりと出ています。
この記事が導入を考えている人の参考になればうれしいです。

参考資料

サイト URL
Amazon Personalize https://aws.amazon.com/jp/personalize/
Amazon Personalize 開発者ガイド https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/what-is-personalize.html
Amazon Personalize を使用してレコメンドエンジンを作成する https://aws.amazon.com/jp/blogs/news/creating-a-recommendation-engine-using-amazon-personalize/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Session Manager で SSH/SCPをトンネリングしてEC2に接続する

はじめに

2019/7/9 に AWS Systems Manager Session Manager が SSHおよびSCP接続の
トンネリングをサポートしました。
これにより、踏み台サーバー等を使用せずに、Session Manager を介して対象のEC2や
マネージドインスタンスに接続することが可能になります。

Session Manager launches tunneling support for SSH and SCP
https://aws.amazon.com/about-aws/whats-new/2019/07/session-manager-launches-tunneling-support-for-ssh-and-scp/

やってみる

AWS Systems Manager 自体の説明は割愛させていただきます。

サーバー側の要件

SSM Agentのバージョンが 2.3.672.0 以上である必要があります。
バージョンが古い場合は、Systems Managerの Run Command を使用して
最新版にアップデートします。

image.png

コマンドの履歴でアップデートが正常終了していることを確認します。

image.png

クライアント側の要件

AWS CLI のバージョンが 1.16.12 以上である必要があります
その他に Session Manager Plugin のバージョンが 1.1.22.0 以上である必要があります

$ aws --version
aws-cli/1.16.195 Python/3.6.0 Windows/10 botocore/1.12.185

$ session-manager-plugin --version
1.1.23.0

バージョンが古い場合は以下のドキュメントに沿って最新版をインストールします。

(Optional) Install the Session Manager Plugin for the AWS CLI
https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html

また使用するSSHクライアントが ProxyCommand をサポートしている必要があります。
私の環境はWindowsでしたので、OpenSSH + Git Bash を使用しました。
SSH の設定ファイル(~/.ssh/config)に以下の内容を追記します。
SSHコマンドで接続先ホスト名が i-(EC2インスタンス) または mi-(マネージドインスタンス)で
はじまる場合は aws ssm start-session コマンドを実行するという内容になっています。

# SSH over Session Manager
host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

IAM ユーザーの要件

最低限、以下のドキュメントで紹介されているポリシーに記載された権限が必要となります。

Quickstart Default IAM Policies for Session Manager
https://docs.aws.amazon.com/systems-manager/latest/userguide/getting-started-restrict-access-quickstart.html

接続する

SSHコマンドを実行する際にホスト名にインスタンスIDを指定すると以下のようにSSHで接続できます。

$ ssh -i ./my-key.pem ec2-user@i-0123456789abcdefg
Last login: Wed Jul 10 02:56:56 2019 from localhost

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/

[ec2-user@ip-172-31-20-75 ~]$

Session Manger と SSM Agent を介して接続しているためだと思われますが、
接続元はlocalhost(127.0.0.1)になっています。

[ec2-user@ip-172-31-20-75 ~]$ sudo tail -3 /var/log/secure
Jul 10 04:26:20 ip-172-31-20-75 sshd[9269]: Accepted publickey for ec2-user from 127.0.0.1 port 41360 ssh2: RSA SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxx
Jul 10 04:26:20 ip-172-31-20-75 sshd[9269]: pam_unix(sshd:session): session opened for user ec2-user by (uid=0)
Jul 10 04:31:50 ip-172-31-20-75 sudo: ec2-user : TTY=pts/0 ; PWD=/home/ec2-user ; USER=root ; COMMAND=/bin/tail -3 /var/log/secure

メリット

セキュリティグループでSSHを許可する必要がない

SSM Agent を介して通信するため、EC2としてはアウトバウンド通信でSSMのエンドポイントと
通信できればよいです。そのためセキュリティグループのインバウンドルールで
SSHのポートを開けておく必要がありません。
最近利用可能になった EC2 Instance Connect では EC2 Instance Connect が利用する
IPアドレス範囲 をセキュリティグループに明示的に追加する必要があります。

SCPも利用できる

Systems Manager のシェルセッションでは仕組み上、SCPの転送には対応できていませんでした。
この機能を使うとSCPによるファイル転送を行うことができます。

$ scp -i ./my-key.pem ./test.txt ec2-user@i-0123456789abcdefg:/tmp/test.txt
test.txt                                                                  100%  769    38.0KB/s   00:00

対応OSの幅が広い

EC2 Instance Connect は 2019/7/9 時点 で対応OSが限られています。

  • Amazon Linux 2(任意のバージョン)
  • Ubuntu 16.04以降

Session Managerを使用したSSH接続の場合、SSM Agent をインストール可能な
以下のLinux環境であれば利用することができます。

  • Amazon Linux and Amazon Linux 2
  • Ubuntu Server
  • Red Hat Enterprise Linux (RHEL)
  • CentOS
  • SUSE Linux Enterprise Server (SLES) 12
  • Raspbian

Manually Install SSM Agent on Amazon EC2 Linux Instances
https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-manual-agent-install.html

またSystems Managerを利用して管理されているオンプレミスの
マネージドインスタンスに対しても利用可能です。
ただしAdvanced-Instances Tierで登録されている必要があります。

Step 7: (Optional) Enable the Advanced-Instances Tier
https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-managedinstances.html

参考

Step 7: (Optional) Enable SSH Session Manager Sessions
https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-enable-ssh-connections.html

Starting a Session (SSH)
https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#sessions-start-ssh

EC2 Instance Connect の特徴や注意点についてまとめてみる
https://qiita.com/hayao_k/items/7b44ed1cd11651272985

以上です。
参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サーバレスで静的Webサイトをホスティング&自動デプロイする(CloudFront+ACM+Route53+S3+CodePipeline+Github)

やりたいこと

  • AWSのCloudFront+ACM+Route53+S3を利用し、サーバレスで静的Webサイトをホスティングする。
    ※WebサイトのソースはS3に配置する。
    ※AWS以外のサービスで取得した独自ドメインをRoute53に設定する。
    ※S3単体だとHTTPSは利用できないため、CloudFront&ACMを利用する。
    ※以下のURLでのアクセスを想定。HTTPはHTTPSにリダイレクトさせる。
    https://xxxx.com
    https://www.xxxx.com

  • AWSのS3+CodePipelineとGithubを利用し、自動デプロイ環境を構築する。

構成イメージ

image.png

手順(サマリー)

  1. S3のバケットを作成する
  2. CodePipelineを利用し、S3とGithubを連携する(自動デプロイ)
  3. S3で静的Webサイトをホスティングする(外部公開する)
  4. Route53に独自ドメインを登録する
  5. ACMで証明書を作成する
  6. CloudFrontのDistributionを作成する
  7. CloudFront向けに独自ドメインを設定する
  8. 動作確認(全体)

手順(詳細)

1. S3のバケットを作成する

静的WebサイトをホスティングするためのS3のバケットを作成する。

スクリーンショット 2019-06-15 14.19.37.png

名前とリージョン

バケット名:www.xxxx.com ※バケット名=FQDNとして設定すること。
リージョン:お好みで。
スクリーンショット 2019-06-01 3.06.48.png

オプションの設定

お好みで。基本的にデフォルトのままでOK。
スクリーンショット 2019-06-01 3.07.22.png

アクセス許可の設定

デフォルトのまま。後ほど設定する。
スクリーンショット 2019-06-01 3.07.43.png

確認画面

スクリーンショット 2019-06-15 14.29.41.png

このようにS3のバケットが作成できた。
スクリーンショット 2019-06-15 14.33.11.png

2. CodePipelineを利用し、S3とGithubを連携する(自動デプロイ)

CodePipelineを利用し、GitHub上のソースをS3に自動的にデプロイする。

パイプラインを作成する

スクリーンショット 2019-06-01 2.57.33.png

パイプラインの基本設定

基本的にデフォルトでOK。パイプライン名はわかりやすいものなら何でもOK。
スクリーンショット 2019-06-01 2.58.17.png

ソースステージを追加する

「GitHub」を選択する。
スクリーンショット 2019-06-01 2.58.30.png

GitHubを選択すると設定項目が現れるので、「GitHubに連携する」ボタンを押下する。
スクリーンショット 2019-06-01 2.58.39.png

GitHubの認証ポップアップが表示されるので、指示に従い認証する。
スクリーンショット 2019-06-01 2.59.37.png

認証に成功したら、連携対象のリポジトリとブランチを選択し、次へ。
※この例の場合、対象リポジトリのmasterブランチにPushすると、最新ソースがS3に自動的にデプロイされる。
スクリーンショット 2019-06-01 2.59.58.png

ビルドステージを追加する

特に何も設定せず、「ビルドステージをスキップ」を押下する。
スクリーンショット 2019-06-01 3.01.12.png

確認ポップアップが表示されるが、気にせずスキップする。
スクリーンショット 2019-06-01 3.01.28.png

デプロイステージを追加する

「Amazon S3」を選択し、次へ。
スクリーンショット 2019-06-01 3.01.49.png

前工程で作成したS3のバケットを参照するよう各項目を入力する。
「デプロイする前にファイルを抽出する」にチェックを付ける。
スクリーンショット 2019-06-16 2.57.09.png

確認画面

設定内容を確認し、問題なければパイプラインを作成する。
スクリーンショット 2019-06-01 3.11.04.png

動作確認

パイプラインの作成が完了すると自動的にデプロイが開始する。
以後、対象リポジトリの対象ブランチにPushされるたびにCodePipelineが自動的にソースをS3にデプロイしてくれる。
スクリーンショット 2019-06-06 21.25.15.png

デプロイが完了したら、作成したS3にGitHub上のソースが反映されていることを確認する。
スクリーンショット 2019-06-15 14.58.20.png

パイプライン作成後はGitHubのリポジトリにWebhookが登録されているか念のため確認しておく。
スクリーンショット 2019-06-16 2.25.14.png

3. S3で静的Webサイトをホスティングする(外部公開する)

Static website hostingを有効にする

S3のバケットを開き、「プロパティ」タブの「Static website hosting」を選択する。
スクリーンショット 2019-06-15 22.39.36.png

「このパケットを使用してウェブサイトをホストする」を選択し、
インデックスドキュメントに「index.html」を入力し、保存する。
スクリーンショット 2019-06-06 20.21.37.png

紫チェックボックスと「パケットホスティング」が表示されていればOKです。
image.png

公開設定

「アクセス権限」タブの「ブロックパブリックアクセス」を選択し、編集を選択する。
スクリーンショット 2019-06-15 22.58.35.png

赤枠のチェックを外して保存する。
スクリーンショット 2019-06-15 22.58.40.png

「アクセス権限」タブの「パケットポリシー」を選択し、パケットポリシーエディタにポリシー設定のテキストを入力し、保存。
※以下テキストの【S3バケット名を入力】を作成済みのS3バケット名に置き換え、パケットポリシーエディタに貼り付ければOK。
スクリーンショット 2019-06-15 15.18.34.png

{
   "Version":"2012-10-17",
   "Statement":[{
    "Sid":"PublicReadForGetBucketObjects",
         "Effect":"Allow",
      "Principal": "*",
       "Action":["s3:GetObject"],
       "Resource":["arn:aws:s3:::【S3バケット名を入力】/*"
       ]
     }
   ]
 }

保存すると以下のようにパブリックの表示が出る。
※Webサイトとしてどこからでも誰からでも見られるようにするため、警告は無視してOK。
スクリーンショット 2019-06-06 20.24.09.png

動作確認

先ほど操作した「Static website hosting」の以下の赤枠にサイトのURLが記載されているので、そのURLにアクセスし、問題なくWebサイトが表示されることを確認する。
※この時点ではまだHTTPでアクセスする。
スクリーンショット 2019-07-07 19.48.15.png

4. Route53に独自ドメインを登録する

ホストゾーンを作成

Route53のメニューから「ホストゾーン」をクリックする。
スクリーンショット 2019-06-15 16.38.26.png

「ホストゾーンの作成」ボタンを押下すると右ペインに入力欄が表示されるので、利用するドメインを入力し、タイプは「パブリックホストゾーン」を選択し、「作成」ボタンを押下する。
スクリーンショット 2019-06-15 16.41.45.png

NSとSOAが自動的に作成される。
スクリーンショット 2019-06-15 16.44.59.png

5. ACMで証明書を作成する

HTTPSを利用する際に参照する証明書をACMで作成する。

パブリック証明書のリクエスト

リージョン「バージニア北部」を選択した上で、「証明書のリクエスト」ボタンを押下する。
※CloudFrontでACMの証明書を利用する場合、証明書はリージョン「バージニア北部(us-east-1)」で取得する必要がある。
※ハマりポイント。

スクリーンショット 2019-06-15 16.16.52.png

「パブリック証明書のリクエスト」を選択し、「証明書のリクエスト」ボタンを押下する。
スクリーンショット 2019-06-15 16.19.16.png

以下の通り、ドメイン名を入力する。
1つ目:*.xxxx.com
※ワイルドカードで指定。

2つ目:xxxx.com
※「この証明書に別の名前を追加」ボタン押下で入力欄が表示される。

スクリーンショット 2019-06-13 19.52.51.png

「DNSの検証」を選択し、「確認」ボタンを押下する。
※これはお好みで。
スクリーンショット 2019-06-15 16.24.22.png

確認画面で、入力内容が正しいか確認する。問題なければ、「確認とリクエスト」ボタンを押下する。
スクリーンショット 2019-06-15 16.24.30.png

証明書の検証

検証画面で検証用レコード(CNAME)が表示されるので、それをRoute53に登録する。
※独自ドメインをRoute53で管理する場合、「Route53でのレコードを作成」ボタンを押下することで、検証用レコード(CNAME)を自動的にRoute53に登録することができる。らくらく。
スクリーンショット 2019-06-13 19.53.39.png

検証状況は証明書一覧から確認することができる。状況が「発行済み」に変わったら証明書の作成は完了。
スクリーンショット 2019-06-15 16.25.12.png

6. CloudFrontのDistributionを作成する

Distributionを作成する

CloudFrontの設定画面を開き、「Create Distribution」ボタンを押下する。
スクリーンショット 2019-06-07 11.14.43.png

Web側の「Get Started」ボタンを押下する。
スクリーンショット 2019-06-07 11.14.55.png

「Origin Domain Name」の入力欄をクリックし、前工程で作成したS3バケット名を選択する。
スクリーンショット 2019-06-07 11.16.13.png

「OriginID」が自動的に割り当てられる。それ以外は基本的にデフォルトでOK。
スクリーンショット 2019-06-07 11.16.34.png

「Viewer Protocol Policy」では「Redirect HTTP to HTTPS」を選択する。
TTLはお好みで設定。
それ以外は基本的にデフォルトでOK。
スクリーンショット 2019-06-07 11.16.47.png

「Alternate Domain Names(CNAMEs)」に以下の通りドメインを入力する。
xxxx.com,www.xxxx.com ※カンマ区切り
「SSL Certificate」は「Custom SSL Certificate」を選択し、前工程で作成したACMの証明書を選択する。
「Default Root Object」には「index.html」を入力する。
それ以外は基本的にデフォルトでOK。
スクリーンショット 2019-06-16 0.01.38.png
スクリーンショット 2019-06-16 0.01.46.png

動作確認

Distributionが作成されるまで少々時間がかかる。
「Status」が「Deployed」、「State」が「Enabled」になるまで待つ。
↑の状態になったら、「Domain Name」の「xxxx.cloudfront.net」のドメインにアクセスし、Webサイトが正常に表示されることを確認する。
スクリーンショット 2019-07-07 21.13.17.png

7. CloudFront向けに独自ドメインを設定する

レコードセットを作成

前工程で操作したRoute53の設定画面を開き、「レコードセットの作成」を押下し、以下の通りAレコードのエイリアスを設定する。
名前:独自ドメイン(xxxx.com)
タイプ:A - IPv4 Address
エイリアス:はい
エイリアス先:前工程で作成したCloudFrontのDistribution(xxxx.cloudfront.net)
※それ以外は基本的にデフォルトでOK。
※「www.xxxx.com」などサブドメインやMXレコードなどもこのタイミングで作成してOK。
スクリーンショット 2019-06-16 2.31.17.png

8. 動作確認(全体)

  • 以下のURLにアクセスし、SSLが正常に動作すること、Webサイトが正常に表示されることを確認する。
    https://xxxx.com
    https://www.xxxx.com
  • HTTPでアクセスした場合、HTTPSにリダイレクトされることを確認する。
    http://xxxx.com
    http://www.xxxx.com
  • GitHubの対象リポジトリ、対象ブランチにPushしたら自動デプロイが動作し、変更内容がCloudFront側に正しく反映されることを確認する。

以上、全行程おわり。

その他

  • 実運用する場合、要求仕様にあわせて設定などを諸々最適化すること。
  • CloudFrontのキャッシュにより、自動デプロイを実行しても最新の変更内容を即座に確認できないため注意。 早めに確認したい場合は以下の記事を参考にキャッシュを削除するとよい。
    [AWS] Amazon CloudFrontのキャッシュ削除(Invalidation)

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS ECS向けCloudWatch Container Insightsのパブリックプレビューが開始したので、使用開始方法をまとめてみた

はじめに

こんにちわ。Wano株式会社エンジニアのnarikawaと申します。


ということで、以下の手順書を参考に試しに設定してみました。
Amazon ECS CloudWatch Container Insights - Amazon Elastic Container Service
ところどころわかりづらいところがあったり、日本語訳が提供されていないこともあったので、整理して発信しようと思い、この記事を書いています。
全てを訳せているわけではないので、詳細は大元の手順書を参考にしていただけると幸いです。

対象読者

  • CloudWatch Container Insightsの設定が、プレビュー時に使っていなくてよくわからない方
  • 手順書が英語しか対応していないため嫌厭してまだ試せていない方
  • CloudWatch Container Insightsでどんなことができるか知りたい方

導入手順

1.まずは設定いじる

GUI(console)で設定する

  • ecsのAccount Settingのタブに移動する   スクリーンショット 2019-07-10 11.06.32.png

※この際、ルートユーザーまたはコンテナーインスタンスのIAMロールを使用していることが前提

  • すると以下のような見慣れない設定チェックボックスがあるので、チェックして有効にする スクリーンショット 2019-07-10 11.06.17.png

※この際、実行しようとしているIAM usersとIAM role はecs:PutAccountSetting permissionがこのアクションに必要

CUIで設定する

  • 自分のアカウント全てのIAM usersとIAM roleのdefaultの設定をcontainer insightsをenabledにしたい

put-account-setting-default (AWS CLI)の場合

aws ecs put-account-setting-default --name containerInsights --value enabled --region us-east-1

Write-ECSAccountSettingDefault (AWS Tools for Windows PowerShell)の場合

Write-ECSAccountSettingDefault -Name containerInsights -Value enabled -Region us-east-1 -Force
  • 自分のアカウントのあるIAM usersとIAM roleのdefaultの設定をcontainer insightsをenabledにしたい(root userのみ)

put-account-setting (AWS CLI)の場合(特定のuser設定変更)

aws ecs put-account-setting --name containerInsights --value enabled --principal-arn arn:aws:iam::aws_account_id:user/userName --region us-east-1

Write-ECSAccountSetting (AWS Tools for Windows PowerShell)の場合(特定のuser設定変更)

Write-ECSAccountSetting -Name containerInsights -Value enabled -PrincipalArn arn:aws:iam::aws_account_id:user/userName -Region us-east-1 -Force

2.クラスターを作る

スクリーンショット 2019-07-10 11.05.33.png

  • その際、以下のような新しいチェックボックスがあるので、enableにチェックする スクリーンショット 2019-07-10 12.24.39.png

3.いつも通りサービスかタスクを立ち上げる

4.誘導に従って、cloudwatchのcontainer insightsを見る

  • クラスタのメトリクスのタブに移動すると、見慣れない青枠の誘導があるので、View Container Insightsのボタンをクリックする

スクリーンショット 2019-07-10 11.05.18.png

  • そうすると以下のような、docker stats相当の情報が観れる

スクリーンショット 2019-07-10 11.34.24.png

  • 以下のように時間でも絞れるし
    スクリーンショット 2019-07-10 11.34.41.png

  • 以下のように特定のクラスタ、サービス、タスクでもfilterをかけれるようになっている

スクリーンショット 2019-07-10 11.34.35.png

container insightsのいいところ

  • コンテナインスタンスにsshして、docker statsしなくてもコンテナの状態を監視できる

疑問点

  • まだterraformでは対応していない?
    • public previewだから多分対応していない
    • clusterのパラメタで早く切り替えれると嬉しい。。
  • 既存のクラスタをいじって設定を足せないのか
    • terraformで建ててから手動で切り替えようと思ったらできなかった(そもそもクラスターの設定修正画面がない)
    • CLIならいけるのか??
    • ecs — AWS CLI 1.16.195 Command Reference 見当たらない、、
    • わかる方ぜひ教えて欲しいです、、わりと切実
    • [追記 2019/7/10]以下の回答いただけました!、正式なリリースでの対応が待ち遠しい

終わりに

  • 週1投稿に関して、これは先週分ってことで許してください(遅れてすみません。。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

goofys で sftp ユーザの chroot を s3 バケット配下のディレクトリにできなかった備忘

goofys でマウントした s3bucket

#df -h
Filesystem        Size  Used Avail Use% Mounted on
[bucket name]  1.0P     0  1.0P   0% /data/s3bucket

goofys でマウントした s3bucket と
sftp ユーザが接続直後にアクセスするディレクトリ

# pwd
/data

# tree
.
|-- chroot ← sftp ログインユーザ用のオーナーにしたシンボリックリンクを用意
|   `-- SFTP_USER -> /data/s3bucket/SFTP_USER
`-- s3bucket ← goofys で s3Bucket をマウントする先
    `-- SFTP_USER

# ls -la chroot/
lrwxrwxrwx 1 SFTP_USER SFTP_USER 24 Jul 10 10:44 SFTP_USER -> /data/s3bucket/SFTP_USER

SFTP のユーザと chroot 先の設定

# cat /etc/ssh/sshd_config
〜省略〜
AllowUsers SFTP_USER
〜省略〜
Match Group supplier
  ChrootDirectory /data/chroot/%u
〜省略〜

sftp 鍵認証用に Linux ユーザ home 配下 .ssh/authorized_keys へ公開鍵を追加

ローカルから SFTP サーバへ接続

# sftp -oIdentityFile=~/.ssh/[id_rsa秘密鍵] [SFTP_USER]@[SFTPサーバ]
packet_write_wait: Connection to [SFTPサーバ] port 22: Broken pipe
Connection closed

このときの sftp サーバの message と secure ログ

# tail -f /var/log/messages
systemd: Created slice User Slice of [SFTP_USER].
systemd: Starting User Slice of [SFTP_USER].
systemd-logind: New session 18 of user [SFTP_USER].
systemd: Started Session 18 of user [SFTP_USER].
systemd: Starting Session 18 of user [SFTP_USER].
systemd-logind: Removed session 18.
systemd: Removed slice User Slice of [SFTP_USER].
systemd: Stopping User Slice of [SFTP_USER].

# tail -f /var/log/secure
Accepted publickey for [SFTP_USER] from [SFTP SERVER] port <省略> ssh2: RSA <省略>
pam_unix(sshd:session): session opened for user [SFTP_USER] by (uid=0)
fatal: bad ownership or modes for chroot directory "/data/chroot/[SFTP_USER]" [postauth]
pam_unix(sshd:session): session closed for user [SFTP_USER]

bad ownership or modes for chroot directory

シンボリックリンクの権限では sftp を騙せないんですね。。。

その後わかったこと

Cent os 6 では uid gid ともに 500 番台から 7 では 1000 番台から。
そのため s3fs でも 7 では usermod grpmod で id を 6 に合わせる必要がある。

sftp の chroot 先は root:root の 755 の必要あり。

これが制限となり共通グループがあっても 775 ではないため、書き込みと削除が出来ない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSで利用するSSL証明書管理をIAMからACMに変更するべき理由

はじめに

自分が担当しているシステムでELBに登録しているSSL証明書をIAM管理からACM管理に変更しました。
AWS機能を利用してSSL証明書を管理する方法は2つあります。
IAMを利用する方法とACMを利用する方法です。
なぜ今までIAMで管理していたのか、ACMにすると何が良いのかを纏めておきます。

基本知識まとめ

まず基本情報についてざっと纏めます。

SSL証明書の種類

DV証明書:ドメイン証明書。ドメイン名が正しいかどうかを認証。
OV証明書:ドメイン名に加え、会社名も証明。
EV証明書:DV、OVよりも厳格な審査がある。

セキュリティレベルの高さはEV→OV→DVの順です。DVがセキュリティレベルが一番低く簡単に認証を受けられます。

参考 今さら聞けないSSL証明書とは、DV、OV、EVとは、常時SSLについて
https://shared-blog.kddi-web.com/network/244/

IAMの基本情報

正式名称:AWS Identity and Access Management
ローンチ日:2010/9/2
機能概要:AWS リソースへのアクセスを安全にコンソールするためのウェブサービス

参考 AWS Identity and Access Management ユーザーガイド
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/introduction.html

ACMの基本情報

正式名称:AWS Certificate Manager
ローンチ日:2016/1/21(東京リージョンローンチ日は2016/5/16)
機能概要:AWS ベースのウェブサイトとアプリケーション用のパブリック SSL/TLS 証明書の複雑な作成と管理を処理
証明書のインポート機能ローンチ日:2016年10月あたり

参考 AWS Certificate Manager ユーザーガイド
https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/import-certificate.html

今までIAMで管理していた理由

ローンチ日を見るとACMは2016年と比較的最近リリースされています。
システム構築時はIAM一択であったことがわかりました。
さらに2016年10月にACMで外部の証明書インポート機能がリリースされたことにより、
IAMを選択する理由はほぼなくなりました。

ELB作成時のSSL証明書選択画面からもACM推奨であることが明記されています。
ALBのSSL証明書選択画面.png

ACMのすごいところ

自分が感じたACMのメリットを並べてみます。(もっとあった気が。。)
・無料でDV証明書を作成できる(これは驚きました)
・ACMで作成したDV証明書は自動更新される

まとめ

外部に公開しているシステムだとOV証明書必須の場合が多いかと思いますが、
社内利用のみのシステムであればDV証明書でも問題ないのではないでしょうか。
IAM管理されているDV証明書があったらぜひACMへの移行を検討してみてください。
※2015年以前に構築され、SSL証明書を利用しているシステムは同様の状況になっていることがあるのでは

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSLambda + Python3 でLINEに通知してくれるToDoリストを作った話

作り始めた経緯

一か月前ほどからJavaを使ってandroidアプリ開発の勉強を始めたのですが、ToDoリストを作っている途中で
「よく考えたら普段使いはPCiPhoneだった:innocent:
ということに気づき、このまま使いもしないandroidアプリを作るくらいだったらとりあえずPCからタスク管理ができるものを作ろうということで作り始めました。

AWSを使う

AWS(Amazon Web Service)はクラウドで様々なことを行えるサービスです
登録後12か月は無料枠がありますし、個人で使う分には利用料金もそこまでしないので安心です。

AWS登録はこちら:point_right: https://aws.amazon.com/jp/

今回は様々なサービスがある中でLambdaS3CloudWatchEventsを使っていきます。

Lambda:関数を作って何かのトリガーで実行する機能
S3:ファイルを保存するストレージ
CloudWatchEvents:指定した時間に関数を叩いてくれる

くらいの認識で大丈夫だと思います。

システム概要

AWSflow1.png
分かりにくいですがこんな感じに作っていきます。

Lambda関数を作る

まずはLambdaの関数を作らなければ何も始まりませんので作っていきます。

まず関数の作成を押します。
AWS1.png
一から作成を選択します。

AWS2.png

すぐ下にこのような画面が出るので関数名と使う言語を入力し関数の作成を押します。
AWS3.png

なんかこんな画面が出てきたらOKです。
AWS4.png

ロールの設定

ロールにポリシーをアタッチしておかないと許可がなく実行できなかったりするのでロールの設定をしていきます。

先ほどのページを下がっていくと実行ロールの欄に既存のロールというというところがあります。そこのhogehogeロールを表示しますというところをクリックしてください。
AWS5.png
すると、このようなページが出てくると思うのでポリシーをアタッチしますをクリックしてください。
AWS6.png
その先のページで下記3つのポリシーをアタッチしてください。
AWS7.png
これでポリシーの設定は完了です。

PythonでAWSを扱う

PythonでAWSを扱うためにはboto3というライブラリを使用します。
初期設定などは少し難しいので偉大なる先人様の記事を参考にしました。
:point_down:ココミテ:eye:
boto3を使ってS3をごにょごにょする

S3にタスクをアップロードする

まずはクライアント側のプログラムを作ります。
細かいプログラムは人に見せられるような代物ではないので、ここでは僕が作っていて詰まった点とその解決法を書いていきます。

受け渡しデータ

CSVファイルに渡すデータはタスク名、日付、時間の三つのデータです。
年のデータを渡していないので一年以内のタスクしか扱えません。

ClientErrorが出る

これはtry-exceptで例外処理できるのですが、素の状態だとエラー名が見つからないので

from botocore.exceptions import ClientError

してから

try:
    hogehoge
except(ClientError):
    hugahuga

してください。

データの受取型

with open した後に

reader = csv.reader(f)

するのですが、readerにはreaderオブジェクトとして帰るのでそのままは使えません。
僕はfor文を使って二重ループで内容を回収しました。

通知時間に到達しているかチェックする関数

ここからはLambda側で作っていきます。
なおLambdaにライブラリとコードをまとめたZIP形式でアップロードするためコーディングは自分側で行います。

プログラム概要

task_check_flow.png

データを保存するディレクトリ

Lambdaにデータを受け渡すためにS3にいったんCSV形式でタスクのデータを送っているため、それを受け取らなければいけません。
その際ダウンロードをする場所を/tmp/hogehoge.csvとしなければなりません。

s3 = boto3.resource('s3')
bucket = s3.Bucket('hoge')
bucket.download_file('hogehoge.csv','/tmp/hogehoge.csv')

のようにします。

handleについて

Lambdaは呼び出されたときhandle設定した関数にeventとcontextを渡して実行します。
今回はS3からデータを持ってきて利用するのでこの受け取った内容は使用しませんが、受け取らないとエラーを吐くのでしっかりと

def handle(event, context):

と記述する必要があります。
また、handle名は自由につけることができ
AWS8.png
上記のようにハンドラの欄から プログラム名.ハンドル名 の形式で設定します。

Lambda上でのタイムゾーン

Lambda上では(多分)リージョンにかかわらずtimeモジュールで持ってくる時間がグリニッジ標準時(GMT)になっています。当然僕は日本でこのプログラムを利用するため日本時間(JST)になおす必要があります。
AWS9.png
そのために環境変数の欄にキーをTZ、値をAsia/Tokyoと設定します。
するとtimeモジュールで持ってくる値が日本時間に変更されます。

LambdaからLambdaを叩く

Lambda関数から別のLambda関数を叩くには

import boto3
import json

clientLambda = boto3.client("lambda")
clientLambda.invoke(
    FunctionName="send_line", #送る先の関数名
    InvocationType="Event", #EventかRequestResponseどちらを受け取るか選ぶ
    Payload=json.dumps(event) 
)

ここも少し難しかったので下記の先人様の記事を参考にさせていただきました。
AWS LambdaからLambda呼んでハマった話。
AWSのLambdaからLambdaを呼んで、Slackにメッセージを送信する

LambdaにZIP形式でアップロードする

ライブラリをインポートして使用している場合Lambdaで扱うためにはライブラリとプログラム本体をまとめたZIP形式のファイルでアップロードする必要があります。

C:\Users\user\programfile> pip install hogehoge -t ./

とかでプログラムと同階層に利用したライブラリを入れてください。

その後ZIP圧縮するのですが、この際ファイルの置いてあるディレクトリを圧縮すると階層が一つ深くなってhandleを掴めなくなってしまうようなので、ファイル自体を全選択して圧縮します。
AWS10.png
あとはここに投げて保存して終了です。
一応、ハンドルがつかめているか確認するためにテストイベントを実行します。
AWS11.png
テストイベントの設定を押して
AWS12.png
空のテストイベントを作成します。
その後テストから実行します。handleを掴めていてほかのエラーが出ていればとりあえずOKです。

CloudWatchEventsのcron式

cron式の記法は[分 時間 日 月 曜日 年]の形式で書きます。
cron式の日フィールドと曜日フィールドは同時に指定することができないとCloudWatchEventsのガイドに書いてあるので、どちらか一方でワイルドカード?(疑問符)を使用する必要があります。

ルールのスケジュール式 - Amazon CloudWatch Events

このプログラムは一時間に一回、00分に叩かれる必要があるので

[0 * * * ? *]
のように記述しました。

LINE Notifyの設定をする

設定はこちらから:point_right:LINE Notify
ラインに通知を行うためにLINEのAPIサービスのLINE Notifyの登録を行います。
ページを見ればわかるとは思いますが詰まった場合は
[超簡単]LINE notify を使ってみる
のページを参考にすることをお勧めします。

LINE送信部

アクセストークンが発行出来たら、先ほどのLambda関数でメッセージが生成されたときに叩かれるsend_line関数を作成していきます。

権限

ロールにアタッチする権限はS3のreadonly権限のみでOKです。

初期設定

url = "https://notify-api.line.me/api/notify"
token ="accesstoken" #各個取得したアクセストークン
headers = {"Authorization" : "Bearer "+ token}

これをスクリプト内に記述します。

プログラム概要

メッセージのCSVファイルを受け取って読み取って各メッセージごとにラインに送るだけ。

メッセージ複数送信

for mess in mess_lis:
    message =  mess
    payload = {"message" :  message}
    r = requests.post(url ,headers = headers ,params=payload)

こうしておけば複数送信も可能です。

最後に

今回はAWSのさわりとしてこのようなToDoリストを作ってみました。
初めての投稿で読みずらい箇所多々あったとは思いますが、また何か作った際にはもっと改善して読みやすい記事を書けるようになってきますのでよろしくお願いします。:bow_tone1:

余談なのですが、最近「ほんとに使える「ユーザービリティ」(エリック・L.ライス著)」という本を読みました。振り返ってみるとこの記事は写真が多すぎた気がします。:pensive:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CloudFormationのLambda-Backedカスタムリソースでリソース作成を待ち受けできるようにする

AWS CloudFormation(CFn)のLambda-Backedカスタムリソースを利用するとCFnが対応していないリソースでもAWS Lambdaを利用して管理できます。

AWS Lambda-backed カスタムリソース - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html

その際に、リソース作成するとレスポンスはすぐに返ってくるけど、リソースが利用できるまでに時間がかかるものを取り扱えるようにしてみました。

テンプレート

CFnでリソース作成を待ってから関連するリソース作成などができるようにするテンプレートです。

Resources:
  CreateResource:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt CreateResourceFunction.Arn

  CustomResource:
    Type: Custom::CustomResource
    Properties:
      ServiceToken: !GetAtt CustomResourceFunction.Arn
      ResourceId: !GetAtt CreateResource.Id
    DependsOn: CreateResource

  CreateResourceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          def handler(event, context):
            response = {}
            if event['RequestType'] == 'Create':
              try:
                # ほんとはここでリソース作成
                response = {
                  "Id": "hoge"
                }
              except Exception as e:
                response = {'error': str(e)}
                cfnresponse.send(event, context, cfnresponse.FAILED, response)
                return

            cfnresponse.send(event, context, cfnresponse.SUCCESS, response)
      Runtime: python3.7

  CustomResourceFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt FunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import cfnresponse
          import time
          def handler(event, context):
            Id = event['ResourceProperties']['ResourceId']

            # リソース作成完了を待ち受ける
            response = {}
            while True:
              # ホントはここでリソース取得
              response = {
                "Id": Id,
                "Status": "AVAILABLE"
              }

              # 特定のステータスになったら抜ける
              if response['Status'] == 'AVAILABLE':
                break
              print('create wait...')
              time.sleep(60)

            cfnresponse.send(event, context, cfnresponse.SUCCESS, response)
      Runtime: python3.7
      Timeout: 900

  FunctionExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      Policies:
      - PolicyName: root
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: "arn:aws:logs:*:*:*"

ポイント

カスタムリソースを2つ用意する

リソースを作成するCreateResource とリソース情報を取得するCustomResource のカスタムリソースを定義することで、リソース作成を待ち受けできるようにしています。

Lambda関数のタイムタウト x 3回まで待ち受けできる

AWS Lambdaの関数は最大15分のタイムタウト設定ができます。またLambda-Backedカスタムリソースで関数実行に失敗すると3回までリトライしてくれるのでそれを利用して最大45分間待ち受けできるようになります。

AWS Lambda の制限 - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html

※Lambda-Backedカスタムリソースでのリトライ回数について必ず3回実行されるのかはドキュメントが見当たらず不明確となります。

AWS Lambda 再試行動作 - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/retries-on-errors.html

DependsOn 属性を利用してリソース作成順を制御する

CFnのDependsOn 属性を利用することで、リソース作成CreateResource 後、リソース情報取得CustomResource が実行されるようにします。
後続するリソースはCustomResourceDependsOn 属性に指定することでリソース作成完了してから実行されるようにできます。

DependsOn 属性 - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html

カスタムリソースの更新・削除と絡めて利用する

下記記事の内容と組み合わせることで、カスタムリソースの更新・削除が実現できます。

AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 - Qiita
https://qiita.com/kai_kou/items/7be2eb9a36611bb5da12

参考

AWS Lambda-backed カスタムリソース - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html

AWS Lambda の制限 - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html

AWS Lambda 再試行動作 - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/retries-on-errors.html

DependsOn 属性 - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html

AWS CloudFormationのLambda-backedカスタムリソースでリソースの更新・削除をする方法 - Qiita
https://qiita.com/kai_kou/items/7be2eb9a36611bb5da12

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

shields.ioを使って技術系アイコンを量産した

概要

shields.ioを用いて技術系アイコンを量産しました。

とりあえず完成したのがこちらです。
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
skills.png

これでスキルマップを作ってみたらいい感じになりました。

shields.ioについて

https://shields.io/

GitHubのREADMEでよく見かけるアレです。
shields.ioはSVG形式のバッジサービスです。

カスタムバッジを作る

特徴的な機能の1つとして
URLのパターンでカスタムバッジを作ることができます。

https://img.shields.io/badge/${subject}-${status}-${color}.svg

subject : バッジの左側に入る文言
status : バッジの右側に入る文言
color : 色

Color

以下のようなものが用意されています。

color.png

16進数形式で指定することも可能です。

カスタムスタイル

いくつかのスタイルが用意されています。

?style=plastic&logo=nginx

?style=flat&logo=java

?style=flat-square&logo=adobe

?style=for-the-badge&logo=gitlab

?style=popout&logo=kotlin

?style=popout-square&logo=google

?style=social&logo=qiita

simpleicons

バッジではいくつかのアイコンが使えます。
これについてはsimpleiconsを参考にします。

https://simpleicons.org/

そして、使えるアイコンの一例がこちらです。

 2019-07-08 1.41.11.png

全部で数えたら648ありました。
Qiitaのアイコンもありました。

カラーコードとロゴがセットになっているのでコレを使ってアイコンを作っていきます。

おすすめアイコンを作った

おすすめしたいアイコンを作りました。

言語系

 2019-07-08 23.38.15.png

ライブラリ・フレームワーク

 2019-07-08 23.38.46.png

OS

 2019-07-08 23.39.28.png

ミドルウェア

 2019-07-08 23.39.50.png

エディタ・IDE

 2019-07-08 23.40.09.png

クラウド・他

 2019-07-08 23.40.25.png

参考

shields.ioで技術系のアイコンをたくさん作ってみる

付録 : アイコンのURL

各アイコンのURLです。1

### 言語系

<img src="https://img.shields.io/badge/PHP-ccc.svg?logo=php&style=flat">
<img src="https://img.shields.io/badge/Javascript-276DC3.svg?logo=javascript&style=flat">
<img src="https://img.shields.io/badge/-TypeScript-007ACC.svg?logo=typescript&style=flat">
<img src="https://img.shields.io/badge/-Python-F9DC3E.svg?logo=python&style=flat">
<img src="https://img.shields.io/badge/-CSS3-1572B6.svg?logo=css3&style=flat">
<img src="https://img.shields.io/badge/-HTML5-333.svg?logo=html5&style=flat">

### ライブラリ・フレームワーク

<img src="https://img.shields.io/badge/-CakePHP-D3DC43.svg?logo=cakephp&style=flat">
<img src="https://img.shields.io/badge/-Rails-CC0000.svg?logo=rails&style=flat">
<img src="https://img.shields.io/badge/-Django-092E20.svg?logo=django&style=flat">
<img src="https://img.shields.io/badge/-Flask-000000.svg?logo=flask&style=flat">
<img src="https://img.shields.io/badge/-Bootstrap-563D7C.svg?logo=bootstrap&style=flat">
<img src="https://img.shields.io/badge/-React-555.svg?logo=react&style=flat">
<img src="https://img.shields.io/badge/-jQuery-0769AD.svg?logo=jquery&style=flat">

### OS

<img src="https://img.shields.io/badge/-Linux-6C6694.svg?logo=linux&style=flat">
<img src="https://img.shields.io/badge/-Ubuntu-6F52B5.svg?logo=ubuntu&style=flat">
<img src="https://img.shields.io/badge/-Windows-0078D6.svg?logo=windows&style=flat">
<img src="https://img.shields.io/badge/-RedHat-EE0000.svg?logo=red-hat&style=flat">
<img src="https://img.shields.io/badge/-Debian-A81D33.svg?logo=debian&style=flat">
<img src="https://img.shields.io/badge/-Raspberry%20Pi-C51A4A.svg?logo=raspberry-pi&style=flat">
<img src="https://img.shields.io/badge/-Arch%20Linux-EEE.svg?logo=arch-linux&style=flat">

### ミドルウェア

<img src="https://img.shields.io/badge/-Apache-D22128.svg?logo=apache&style=flat">
<img src="https://img.shields.io/badge/-Nginx-bfcfcf.svg?logo=nginx&style=flat">
<img src="https://img.shields.io/badge/-Oracle-f80000.svg?logo=oracle&style=flat">
<img src="https://img.shields.io/badge/-Redis-D82C20.svg?logo=redis&style=flat">
<img src="https://img.shields.io/badge/-Elasticsearch-005571.svg?logo=elasticsearch&style=flat">
<img src="https://img.shields.io/badge/-PostgreSQL-336791.svg?logo=postgresql&style=flat">


### エディタ・IDE

<img src="https://img.shields.io/badge/-Visual%20Studio%20Code-007ACC.svg?logo=visual-studio-code&style=flat">
<img src="https://img.shields.io/badge/-Vim-019733.svg?logo=vim&style=flat">
<img src="https://img.shields.io/badge/-Emacs-EEE.svg?logo=spacemacs&style=flat">
<img src="https://img.shields.io/badge/-Atom-66595C.svg?logo=atom&style=flat">
<img src="https://img.shields.io/badge/-Xcode-EEE.svg?logo=xcode&style=flat">
<img src="https://img.shields.io/badge/-intellij%20IDEA-000.svg?logo=intellij-idea&style=flat">

### クラウド・他

<img src="https://img.shields.io/badge/-Amazon%20AWS-232F3E.svg?logo=amazon-aws&style=flat">
<img src="https://img.shields.io/badge/-Google%20Cloud-EEE.svg?logo=google-cloud&style=flat">
<img src="https://img.shields.io/badge/-Ansible-EE0000.svg?logo=ansible&style=flat">
<img src="https://img.shields.io/badge/-GitHub-181717.svg?logo=github&style=flat">
<img src="https://img.shields.io/badge/-Docker-EEE.svg?logo=docker&style=flat">
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む