2021年10月14日木曜日
Amazon EFSを使ってEKSに永続ボリュームを導入した
AWS EKSで動かしているアプリケーション用にEFSを使って永続ボリュームを用意してみたので、その方法をまとめてみようと思います。
とあるテスト用Kubernetes環境でMySQLのイメージを使ってデータベースを動かしているのですが、何かの拍子にpodが一旦削除などされてしまうとデータベースの中身のデータが全て無くなってしまいます。
そこで永続ボリュームを確保しておいて、MySQLデータ用のディレクトリをマウントしたボリュームにすればOKというわけです。
手順はほぼこのドキュメントどおりですが、一部変えないと動かなかった箇所があるのでその点も触れつつ書いてみようと思います。
Amazon EFS CSI ドライバー
ドキュメントに従っても動かないよ!という件のissueはこちら。
Missing permission in the example IAM policy file · Issue #489 · kubernetes-sigs/aws-efs-csi-driver · GitHub
また、今回やってみた環境は以下のようになっています。
Kubernetes v1.21
EKSプラットフォーム eks.2
EFS CSI ドライバー v1.3.4
- Amazon EFS CSI ドライバー
まずはIAMポリシーを作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"elasticfilesystem:DescribeAccessPoints",
"elasticfilesystem:DescribeFileSystems",
"elasticfilesystem:DescribeMountTargets",
"ec2:DescribeAvailabilityZones"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"elasticfilesystem:CreateAccessPoint"
],
"Resource": "*",
"Condition": {
"StringLike": {
"aws:RequestTag/efs.csi.aws.com/cluster": "true"
}
}
},
{
"Effect": "Allow",
"Action": "elasticfilesystem:DeleteAccessPoint",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/efs.csi.aws.com/cluster": "true"
}
}
}
]
}
AmazonEKS_EFS_CSI_Driver_Policy
というポリシーを作成してこのjsonでアクセス権限を設定します。
AWSのドキュメントからリンクされているポリシーと比べると"elasticfilesystem:DescribeMountTargets", "ec2:DescribeAvailabilityZones"が増えているのですが、これを追加しないと権限不足のエラーが出てドライバーが動きませんでした。🤔
このポリシーでサービスアカウントを作成します。<cluster-name>
と<Account ID>
, <region>
はお使いの環境に合わせて置き換えてください。
$ eksctl create iamserviceaccount \
--name efs-csi-controller-sa \
--namespace kube-system \
--cluster <cluster-name> \
--attach-policy-arn arn:aws:iam::<Account ID>:policy/AmazonEKS_EFS_CSI_Driver_Policy \
--approve \
--override-existing-serviceaccounts \
--region <region>
$ eksctl create iamserviceaccount \
--name efs-csi-node-sa \
--namespace kube-system \
--cluster <cluster-name> \
--attach-policy-arn arn:aws:iam::<Account ID>:policy/AmazonEKS_EFS_CSI_Driver_Policy \
--approve \
--override-existing-serviceaccounts \
--region <region>
Helmを使ってドライバーをクラスターにインストールします。
クラスターのリージョンによってimage.repository
は変えないといけないようなので↓を参照してください。
Amazon EKS アドオンコンテナイメージのアドレス - Amazon EKS
$ helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
$ helm repo update
$ helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
--namespace kube-system \
--set image.repository=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/aws-efs-csi-driver \
--set controller.serviceAccount.create=false \
--set controller.serviceAccount.name=efs-csi-controller-sa \
--set node.serviceAccount.create=false \
--set node.serviceAccount.name=efs-csi-node-sa
AWSのドキュメントでは efs-csi-node-sa
は出てこないのですが、こちらも試した環境ではこのサービスアカウントを作成してhelmで指定しないと動きませんでした。🤔
- EFS ファイルシステム作成
ここはインフラ管理の都合上terraformで行いました。
セキュリティグループ、ファイルシステム、マウントターゲット(AWSコンソールでは「ネットワーク」という表示名になっています)を作成します。
VPCに複数のプライベートサブネットがある場合は aws_efs_mount_target
も同様に複数作っておきましょう。
resource "aws_security_group" "efs" {
name = "eks-${var.cluster_name}-efs"
# ↓クラスターがあるVPCのid
vpc_id = var.vpc_id
ingress {
from_port = 2049
to_port = 2049
protocol = "tcp"
# ↓VPCのCIDR
cidr_blocks = [var.vpc_cidr]
}
}
resource "aws_efs_file_system" "fs" {
creation_token = "eks-${var.cluster_name}-fs"
tags = {
"Name" = "eks-${var.cluster_name}-fs"
}
}
resource "aws_efs_mount_target" "az" {
file_system_id = aws_efs_file_system.fs.id
# ↓プライベートサブネットのID
subnet_id = var.subnet_private.id
security_groups = [aws_security_group.efs.id]
}
- クラスターでStorageClassを追加
まずはストレージクラスの宣言です。StorageClassはnamespaceに属していないので、一度applyしておけばOKです。
parametersについてはこちらを参考にしてください👇
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
# EFSで作成したファイルシステムのID
fileSystemId: fs-00abcdef123456789
directoryPerms: "700"
- アプリケーションで永続ボリュームを確保、マウント
これで準備ができたので、PersistentVolumeを使うアプリケーションでのyamlを書いていきます。
まずはPVC(PersistentVolumeClaim)です。
storageClassName
は3.で宣言したStorageClassのnameですね。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 2Gi
このPVCで確保したボリュームをコンテナにマウントします。
以下のyamlはdeploymentでやる場合のvolumeに関するところを書き出したものです。 claimName
がPVCリソースのnameですね。
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: mysql
image: mysql:8.0.25
volumeMounts:
- name: persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
と、このような作業で永続ボリュームを利用できました。
冒頭で触れたドキュメント不完全問題もあって少し手間取りましたが、振り返ってまとめてみるとそう難しいことはなさそうですね。
EKSで永続ボリュームが欲しくなったときはお試しください👐
2021年8月27日金曜日
AWS App Mesh をEKSで試してみた(仮想ゲートウェイ)
前回に続き、EKS上でのAWS App Meshをやっていきます。
今回は、仮想ゲートウェイです。
ゲートウェイがあれば、メッシュ上のリソースにメッシュ外のpodやWebなどからアクセスできるようになります💁♂️
今回の内容もドキュメント等いまいち見つからなかったので正確さなどは怪しいところがありますがご了承ください🙇♂️
Virtual gateways - AWS App Mesh
- 仮想ゲートウェイ作成
まずは仮想ゲートウェイとそのルートを作成します。
リスナーのポートは後で作るゲートウェイの実体と番号を合わせれば何番でもよさそうです。ルートは、全てのリクエストを前回作った仮想サービス(svc-v)へ流すよう指定してみました。
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualGateway
metadata:
namespace: mesh-test
name: gateway-v
spec:
namespaceSelector:
matchLabels:
mesh: mesh-1
podSelector:
matchLabels:
for: gateway
listeners:
- portMapping:
port: 8088
protocol: http
connectionPool:
http:
maxConnections: 1024
logging:
accessLog:
file:
path: /dev/stdout
---
apiVersion: appmesh.k8s.aws/v1beta2
kind: GatewayRoute
metadata:
namespace: mesh-test
name: gateway-v-route-v
spec:
httpRoute:
action:
target:
virtualService:
virtualServiceRef:
name: svc-v
match:
prefix: /
- ゲートウェイ作成
ゲートウェイの実体となる、envoyイメージのコンテナと type: LoadBalancer
な サービスを作成します。
envoyのcontainerPortとサービスのtargetPortは仮想ゲートウェイのリスナにしているポートと等しくする必要があります。
また、envoyの環境変数としてリージョンと仮想ゲートウェイARNを与える必要があるようです。
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway-v
namespace: mesh-test
spec:
replicas: 1
selector:
matchLabels:
for: gateway
template:
metadata:
labels:
for: gateway
spec:
serviceAccountName: appmesh-proxyauth
containers:
- name: gateway
image: public.ecr.aws/appmesh/aws-apmesh-envoy:v1.19.0.0-prod
env:
- name: AWS_REGION
value: ap-northeast-1
- name: APPMESH_RESOURCE_ARN
value: arn:aws:appmesh:ap-northeast-1:123456780000:mesh/mesh-1/virtualGateway/gateway-v_mesh-test
ports:
- containerPort: 8088
resources:
limits:
cpu: 10m
memory: 32Mi
---
apiVersion: v1
kind: Service
metadata:
name: gateway-v
namespace: mesh-test
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 8088
selector:
for: gateway
以上をapplyすると、ロードバランサーが作られて仮想ゲートウェイとして動くようになります。
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
gateway-v LoadBalancer 10.100.184.34 aae2598dca4e348b2a934f4cd88b7982-607b093417cc2f4d.elb.ap-northeast-1.amazonaws.com 80:31248/TCP
AWSコンソールでロードバランサーを見てみると、NLBができていました。
コンソールのApp Meshでもゲートウェイが表示されています。
ゲートウェイへのアクセスですが、クラスター内からは
$ curl gateway-v.mesh-test.svc.cluster.local
外からは
$ curl <NLBのドメイン名>
でリクエストを送って、仮想サービスsvc-vに送られることが確認できました。
ということで、App Mesh作ってみるシリーズでした。
今回はごく基本的な部分しか試していませんが、各リソースで設定できる機能はいろいろあるので公式ドキュメントやCustomResourceDefinitionを参照してみてください🙏
eks-charts/crds.yaml at master · aws/eks-charts
ちなみに、App Meshには(他のサービスと同様に)作れるリソース数の制限があります。
ほとんどはサポートセンターに頼めば引き上げ可能なようですが、メッシュ設計の際はちょっと気にしておいたほうがいいかもしれないですね。
AWS App Mesh エンドポイントとクォータ - AWS 全般のリファレンス
2021年6月17日木曜日
AWS App Mesh をEKSで試してみた(仮想ルーター、仮想サービス)
前回に続き、EKS上でのAWS App Meshをやっていきます。
今回は、仮想ルーターと仮想サービスです。
前回作った仮想ノードにアクセスするには仮想ルーターか仮想ゲートウェイが必要になるので、いよいよメッシュができていきます💪
あまりしっかり説明されたドキュメントが見つからず経験ベースな部分が多いので、正しい説明になっているかどうか怪しい部分もありますがご了承ください🙇
- 仮想ルーター作成
仮想ノードへトラフィックを振り分ける仮想ルーターを作成します。
振り分け先は weightedTargets
の virtualNodeRef.name
で仮想ノード名を指定します。
プロトコルはHTTPにしましたが、他にもGRPC, HTTP2, TCPが設定できるようです。
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualRouter
metadata:
namespace: mesh-test
name: vrouter-1
spec:
listeners:
- portMapping:
port: 80
protocol: http
routes:
- name: route-1
httpRoute:
match:
prefix: /
action:
weightedTargets:
- virtualNodeRef:
name: vnode-1
weight: 1
- virtualNodeRef:
name: vnode-2
weight: 1
- 仮想サービス用サービス作成
仮想サービスの実体にするserviceを作成します。
selecter
は、仮想サービスを介してアクセスする先のpodが全て含まれるように指定する必要があるみたいです。
今回の例だと、仮想サービス → 仮想ルーター(vrouter-1) → 仮想ノード(vnode-1, vnode-2) というトラフィックの流れになるので、vnode-1とvnode-2の実体になるpodに app: sample1
のラベルをつけてあります。
apiVersion: v1
kind: Service
metadata:
name: svc-v
namespace: mesh-test
labels:
app: sample1
spec:
type: ClusterIP
selector:
app: sample1
ports:
- protocol: TCP
port: 80
targetPort: 80
- 仮想サービス作成
先の svc-v
を実体とする仮想サービスを作成します。
name
は上で仮想サービス用に作ったサービス名と等しくする必要があるみたいです。
provider
には仮想サービスを通してアクセスする先を指定を指定します。単一のvirtualNodeかvirtualRouterを選択できるので、トラフィックを複数ノードに分けたいならvirtualRouter、分けなくていいならvirtualNodeにするとよさそうです。
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualService
metadata:
namespace: mesh-test
name: svc-v
spec:
provider:
virtualRouter:
virtualRouterRef:
name: vrouter-1
- 仮想サービスをバックエンドに設定
作った仮想サービスにアクセスするには、「仮想ノードのバックエンド」にその仮想サービスを指定する必要があります。
仮想ノードのyamlで、backends
に仮想サービスを追加しておきます。
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
name: vnode-x
namespace: mesh-test
spec:
# 主要部分は省略
backends:
- virtualService:
virtualServiceRef:
name: svc-v
仮想ノード化されたPodから仮想サービスへリクエストを送ると、
ルーティングに従ったレスポンスが返ってくるはずです。
$ kubectl exec -it somepod-524b1761b4-vg8kw -- bash
root@somepod-524b1761b4-vg8kw:/# curl svc-v.mesh-test.svc.cluster.local
いろいろ試してみたところ、「バックエンドにその仮想サービスを指定したpod」で「仮想サービス名.名前空間.svc.cluster.local
へトラフィックを送る」場合に仮想サービスで設定したトラフィック振り分けが行われる仕様のようです。
それ以外の条件、例えばバックエンドを指定していないpodからのリクエストや仮想サービス名だけのホスト名にトラフィックを送る($ curl svc-v
)場合は(仮想ではない)Serviceの svc-v
に行くようです。
なかなかややこしいですね。
2021年6月3日木曜日
AWS App Mesh をEKSで試してみた(準備~仮想ノードまで)
突然ですが、AWS App Meshは皆さんお使いでしょうか?
AWS製のサービスメッシュ構築サービスですね。
サービスメッシュといえばIstioが有名ですが、AWS App MeshはAWS製だけあってEKSはもちろんEC2やECSのインスタンス・コンテナもメッシュに組み込むことができるのが強みという印象です。
そんなApp MeshをEKS上でいろいろと実験してみたので、自分の理解の反芻のためにもまとめてみたいと思います。
試した環境は kubernetes 1.20, App Meshコントローラ 1.3.0 でした。
- 準備
ドキュメントに従ってコントローラをインストールします。
Getting started with AWS App Mesh and Kubernetes - AWS App Mesh
手順はドキュメントの通りなのでここでは書きませんが、完了するとappmesh-system
ネームスペースでコントローラのポッドができていました。
$ kubectl get pod -n appmesh-system
NAME READY STATUS RESTARTS AGE
appmesh-controller-6c55b46558-8s363 1/1 Running 0 1d
- mesh作成
メッシュを作成します。
egressFilter
はメッシュ内部から外部へのアクセスを許可するかどうかですね。許可しない場合はDROP_ALLにします。
namespaceSelecter
も何でもいいので書いておきましょう。
apiVersion: appmesh.k8s.aws/v1beta2
kind: Mesh
metadata:
name: mesh-1
spec:
egressFilter:
type: ALLOW_ALL
namespaceSelector:
matchLabels:
mesh: sitateru-mesh
- namespace作成
メッシュのリソースを配置するネームスペースを宣言します。
上のMeshで指定した namespaceSelector
とラベルを合わせておきましょう。
App Meshの機能を使うにはappmesh.k8s.aws/sidecarInjectorWebhook
をenabledにしてEnvoyサイドカーが入れられるようにしておく必要があります。
apiVersion: v1
kind: Namespace
metadata:
name: mesh-test
labels:
mesh: mesh-1
appmesh.k8s.aws/sidecarInjectorWebhook: enabled
- サービスアカウント作成
IAMで以下のポリシーを作成します。
今回は動作検証なので mesh-1
メッシュの全ての仮想ノードと仮想ゲートウェイに対して StreamAggregatedResources
を許可していますが、実使用の際には適宜リソース名を指定するほうが望ましいですね。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "appmesh:StreamAggregatedResources",
"Resource": [
"arn:aws:appmesh:ap-northeast-1:111122223333:mesh/mesh-1/virtualNode/*",
"arn:aws:appmesh:ap-northeast-1:111122223333:mesh/mesh-1/virtualGateway/*"
]
}
]
}
続いてこのポリシーをアタッチしたサービスアカウントをEKSクラスターに作成します。
$ eksctl create iamserviceaccount \
--cluster test-cluster \
--namespace mesh-test \
--name appmesh-proxyauth \
--attach-policy-arn arn:aws:iam::111122223333:policy/appmesh-proxyauth \
--override-existing-serviceaccounts \
--approve
- 仮想ノード用サービス作成
仮想ノードの実体にするdeploymentとserviceを作成します。コンテナイメージは何でもいいのですが、今回はただのnginxにしています。
serviceAccountName
には👆で作成したサービスアカウント名を指定します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-1
namespace: mesh-test
labels:
app: sample0
spec:
selector:
matchLabels:
app: sample0
template:
metadata:
labels:
app: sample0
server: nginx
spec:
serviceAccountName: appmesh-proxyauth
containers:
- name: nginx
image: nginx:1.19.0
ports:
- containerPort: 80
resources:
limits:
cpu: 10m
memory: 10Mi
---
apiVersion: v1
kind: Service
metadata:
name: svc-1
namespace: mesh-test
labels:
app: sample0
spec:
selector:
app: sample0
server: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
- 仮想ノード作成
今作ったsvc-1
を実体とする仮想ノードを作成します。
serviceDiscovery
で仮想ノードとする実体を指定するのですが、ここでは目的のサービスのDNSホスト名( サービス名.名前空間.svc.cluster.local
)を指定します。
ServiceとPodに対するDNS | Kubernetes
ちなみにDNSではなくCloud Mapを使って指定することもできるみたいです。
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
name: vnode-1
namespace: mesh-test
spec:
podSelector:
matchLabels:
app: sample0
listeners:
- portMapping:
port: 80
protocol: http
serviceDiscovery:
dns:
hostname: svc-1.mesh-test.svc.cluster.local
うまくできていればAWSコンソールでもメッシュや仮想ノードが表示されているはずです。
できていない場合は appmesh-controller
ポッドのログを見れば何かわかるかもしれないのでチェックしてみてください。
そこそこ長くなってきたのでここまでにしようと思います。
ここまで作ったものだけではぜんぜんサービスメッシュになっていないのですが、続きは近いうちにまた書きます!
ちなみに、メッシュのリソース作成はkubernetes的にはカスタムリソースの作成になっています。
そのカスタムリソースの定義は👇なので、yamlをどう書けばいいのかこちらを見つつやるのがいいのではないかと思います!
eks-charts/crds.yaml at master · aws/eks-charts
2020年11月12日木曜日
AWS ECRのライフサイクルポリシーを設定して自動クリーンアップ
今回は軽く、AWS ECR (Elastic Container Registry) の小ネタです。
AWSでコンテナを使って何かする場合ECRにコンテナイメージを保存しておくことが多いと思いますが、日ごろからイメージをよくビルド&プッシュしているとどんどんイメージが増えていきますね。
イメージサイズがものすごく大きいとかでなければそれほど料金を食うわけでもないのですが、まず使わない古いイメージをずっと保存しておくこともないよなーと思ったのでそのあたりを設定してみました。
ECRではライフサイクルポリシーという機能があり、いろいろな条件を定めてイメージを自動クリーンアップすることができるんですね。
Amazon ECRのライフサイクルポリシーでコンテナイメージのクリーンアップ | Amazon Web Services ブログ
設定できる条件は
- タグ
- イメージ数
- プッシュされてからの経過日数
今回は、「タグが latest
ではない」かつ「プッシュされて90日以上」の条件に合うイメージを削除するポリシーを設定してみました。
AWSコンソールで設定する場合は、ECRのコンソールでリポジトリを選択して左のメニューの Lifecycle Policy
をクリックすると設定画面があります。
「テストルールの編集」ボタンを押すとポリシーを適用した結果が確認できる(実際に保存されているイメージには何もしない)テスト用ページに移動するので、まずはそこで試してみるのがいいですね。
さて、ライフサイクルポリシーは「ルール」を優先順位つきで好きな数設定することで構成します。ルール作成画面はこうなっていて、一致条件は「イメージをプッシュしてから」「次の数値を超えるイメージ数」が選べます。
今回作りたい条件は「タグが latest
ではない」かつ「プッシュされて90日以上」ということで、
- タグ付け済("latest")、次の数値を超えるイメージ数(1)
- すべて、イメージをプッシュしてから(90日)
という2つのルールを作ってこのようになります。
ルールを作ればあとは自動でそれに従ってイメージが毎日掃除されるので、とても楽ですね😊
ここまではコンソールで操作してきましたが、リポジトリが多い場合やコンソール面倒だと言う場合はもちろんCLIが使えます。
put-lifecycle-policy — AWS CLI 2.1.0 Command Reference
まずは先ほどのルールをJSON化して policy.json
とでもファイルを作ります。
{
"rules": [
{
"rulePriority": 1,
"description": "keep latest image",
"selection": {
"tagStatus": "tagged",
"tagPrefixList": ["latest"],
"countType": "imageCountMoreThan",
"countNumber": 1
},
"action": {
"type": "expire"
}
},
{
"rulePriority": 2,
"description": "expire images older than 90 days",
"selection": {
"tagStatus": "any",
"countType": "sinceImagePushed",
"countUnit": "days",
"countNumber": 90
},
"action": {
"type": "expire"
}
}
]
}
今回は全リポジトリに適用したかったのでシェルスクリプトを作りました。
#!/bin/sh
# ECRリポジトリにライフサイクルポリシーを設定する
for REPO in $(aws ecr describe-repositories --query "repositories[*].{name:repositoryName}" --output text)
do
echo "repository: $REPO"
aws ecr put-lifecycle-policy --lifecycle-policy-text "file://policy.json" --repository-name $REPO
done
これで一括適用ができますね💪
比較的簡単に設定ができるので、「うわっ…ECRのイメージ、多すぎ…?」と気づいた際には設定してみてはいかがでしょうか。
ちなみにterraformだとaws_ecr_lifecycle_policy
リソースで設定できるようです。
aws_ecr_lifecycle_policy | Resources | hashicorp/aws | Terraform Registry
2019年2月21日木曜日
Alexaスキルを作ろう - 第2回
SPECというサービスのバックエンド開発などを行っています。
今回は前回の続きで、Alexaスキルの作成について書いていきます。
今回は、前回作成したスキルから呼び出されるLambdaを作成します。
まずAWSコンソールにログインして、Lambda - 関数の作成に行きます。
そこから「一から作成」、「設計図」などが選べます。
「設計図」でalexaを検索すると、
それぞれのスキルタイプに合わせたsdkが用意されています。
使用したいタイプのsdkがある場合は、それを使用しましょう。
今回はとても単純なスキルを作成したので、「一から作成」を選択します。
いろいろな言語が選べますが、今回はNode.jsで作成しました。
関数を作成すると、シンプルなレスポンスを返す関数が書かれているので、その中身を変更します。
今回作成したスキルは、「言葉を逆さまにする」です。
シンプル...!
コードを記述します。
exports.handler = async (event) => { const word = event.request.intent.slots.word.value; const response = { "version": "1.0", "response": { "outputSpeech": { "type": "PlainText", "text": word.split('').reverse().join('') }, "shouldEndSession": false }, "sessionAttributes": {} }; return response; };
こんな感じになりました。
responseの形式について、詳しくは応答の形式を参考にしてください。
「intent.slots.word」の部分の「word」は前回設定した「スロット名」に対応します。
次にこのLambdaを呼び出すトリガーを設定します。
「トリガーの追加」から、「Alexa Skills Kit」を選択して、スキルID(alexa developer consoleを参照)を設定します。
alexa developer consoleでテストしてみましょう。
前回、スキルの呼び出し名とサンプル発話を設定しました。
テスト画面で、
「<スキルの呼び出し名>を開いて、<サンプル発話>おはよう」と入力すると‥
ちゃんと逆さまにして返してくれましたね!
シンプルなスキルですが動くと面白いと思います。
今回はここまでにします。
次回は実機でのテストとスキルの公開について書ければと思います。
ありがとうございました!
2018年12月25日火曜日
[第一回]SansanとHubSpotの連携
こんにちは、シタテルエンジニアの工です!
今回は、SansanとHubSpotの連携の仕方について書いていきます 🚀
はじめに
シタテルでは、衣服生産をSCS、マーケティング・営業の部分をHubSpotを使って管理しています。
HubSpotを導入する際に、Sansanの情報をHubSpotに同期したいという要望があったので、SansanのAPIとHubSpotのAPIを組み合わせて同期処理を実装しました。
AWSLambdaを使って、定期的に同期処理を実行しています。
HubSpot公式にもSansanとハブスポットの連携の仕方という記事も公開されていますので参考にしてみてください。
Sansanの名刺情報をHubSpotにインポートする
使うもの
余談ですが実装したときAWSLambdaにはRubyの選択肢がなかったのですが、今は使用できるみたいですね。
普段はRubyを使うことが多いので、次にLambdaを使うときはRubyで実装してみたいです。
Sansanの情報を取得
handler.js
const axios = require('axios')
const moment = require('moment')
axios.defaults.headers.common['X-Sansan-Api-Key'] = process.env.SANSAN_API_KEY
axios.defaults.headers.get['Content-Type'] = 'application/json'
module.exports.importSansanData= async (event, context, callback) => {
var updatedFrom = moment().subtract(2, 'hours').format("YYYY-MM-DDTHH:mm:ss")+"Z"
var updatedTo = moment().format("YYYY-MM-DDTHH:mm:ss")+"Z"
await axios.get(`https://api.sansan.com/v2.0/bizCards?updatedFrom=${updatedFrom}&updatedTo=${updatedTo}&range=all`)
.then(function (response) {
importHubspot(response)
callback(null, response.data)
})
.catch(function (error) {
callback(error)
});
};
詳しくはこのあたりを参考にしてください。
AWSLambdaとServerlessを使ってみる[第1回]
AWSLambdaとServerlessを使ってみる[第2回]
日時取得のところがとてもブサイクです... 😢
なぜかタイムゾーンZにしないとうまくいかなっかった 🤔
本当は+09:00
に設定したい。
var updatedFrom = moment().subtract(2, 'hours').format("YYYY-MM-DDTHH:mm:ss")+"Z"
var updatedTo = moment().format("YYYY-MM-DDTHH:mm:ss")+"Z"
HubSpotにインポート
取得したSansanデータをインポート。
propertyは数が多いのでほとんど割愛して記載します。
handler.js
function importHubspot (response) {
response.data.data.forEach(createContact)
}
function createContact (value, index) {
var properties = [
{
property: 'email',
value: value.email
},
{
property: 'firstname',
value: value.firstName
},
{
property: 'lastname',
value: value.lastName
}
]
axios.post(`https://api.hubapi.com/contacts/v1/contact?hapikey=${process.env.HUBSPOT_API_KEY}`, {
properties: properties,
})
.then(function (response) {
console.log(response.data)
})
.catch(function (error) {
console.error(error.response.data)
});
}
上の例はひとつひとつPOSTしていますが、こちらのAPI使えば1回のPOSTでまとめてCreateできます。
Create or update a group of contacts | Contacts API
CloudWatchでスケジュールを設定
serverless.ymlにスケジュールを設定します。
2時間おきにインポート 🚀
serverless.yml
functions:
importSansanData:
handler: handler.importSansanData
events:
- schedule: cron(0 */2 * * ? *)
これで2時間おきに、Sansanの名刺情報がHubSpotのコンタクトに同期されます!👏
最後に、deploy 🚀
serverless deploy -v
まとめ
今回はSansanとHubSpotのAPIを使って、Sansanの名刺情報をHubSpotのコンタクトにインポートする方法について書きました。
SCSと外部サービスとの連携は今後もどんどん加速していくと思います!マーケティング・営業 -> 衣服生産 -> 請求
この流れがシステムでシームレスに実現できるように開発中です 💻
2018年12月21日金曜日
金沢で開催された IVS CTO Night & Day に参加してきました!
こんにちは!
シタテル株式会社CTOの和泉です。
12月17〜19の3日間、金沢で開催された IVS CTO Night and Day 2018 Winter powered by AWS に参加してきました。
どんな会なのか? 詳しくはこちら!
CTO Night and Day 2017
(リンク先は昨年のレポートです)
新旧大小さまざまな企業のCTOがずらり100人以上。
前夜祭に始まり、AWSの最新技術や文化紹介、選ばれた15社の技術やマネジメント紹介(登壇しました)、先輩CTOとの公開メンタリング、ダンスパーティーまで盛りだくさん。
「CTOがいなくなって8ヶ月がたった話」「CTOの休み方」など、CTOにまつわる、CTOならではの話題や悩み相談が随所で繰り広げられていました。
事業を進めていくのは本当にエネルギーが必要で、でも皆さん熱量高く真摯に取り組んでおられて本当に刺激になりました。
40名を超える方と名刺を交換して事業について意見交換させていただいて素晴らしい出会いがたくさんありました。
そして、本当にAWSの運営の方々のホスピタリティが半端ない!
私も事前に伝えていた事業内容に対してこの人が最適だろうという方を紹介いただいて、有意義なディスカッションが出来ました。
来年もできたら参加したい!頑張ろう!とめちゃくちゃテンションが上りました。
反省
話すのに忙しすぎて写真を全然撮っていませんでした、、、
2018年12月20日木曜日
プロジェクトごとに環境変数/クレデンシャルを管理する
sitateruでサービス・プロダクトを担当している北爪です。
インフラを見ることもあり、今日はShellでの環境変数の扱い方について書きます。
プロジェクトごとに環境変数/クレデンシャルを管理する
複数のプロジェクトに関わっているときに、プロジェクトごとの環境変数やAWSのkey/secretsなど環境変数に設定し管理する必要があることがあります。
Shellを使って管理する一つの方法を記述します。
準備
プロジェクトごとにクレデンシャルフェイルを作る
クレデンシャルファイル置き場を作ります
$ mkdir ~/.crds/
プロジェクトごとにプロジェクトファイルを作ったディレクトリ以下に置きます。
ディレクトリはinvisibleファイルにしておきます。
$ touch ~/.crds/pj-1
ファイルに必要なクレデンシャルを記述します
export AWS_ACCESS_KEY_ID==XXXXX
export AWS_SECRET_ACCESS_KEY==XXXXX
export AWS_DEFAULT_REGION=ap-northeast-1
export TF_VAR_secret_key=XXXXX
export TF_VAR_rds_password=XXXXX
読み込みと切り替え
設定した環境変数群を以下の方法でプロジェクトごとに読み込みます。
リセットはされないので、別プロジェクトに変える際は、あらたにshellを立ち上げるようにします。
プロジェクトのクレデンシャルを読み込む
source コマンドをつかってシェルに読み込めば(dot operatorでもOK)
$ source ~/.crds/pj-1
or
$ . .~/.crds/pj-1
別のプロジェクトのクレデンシャルを読み込む
複数のプロジェクトファイルを作ることで、クレデンシャルを別で読み込むことができます。
$ source ~/.crds/pj-2
Tips
初期読み込みが必要な場合は、 bashrcやzshrcなどに、読み込みコマンドを記述しておけます
まとめ
クレデンシャルファイルを特定のディレクトリに作り、基本コマンドで呼び出すだけです。一覧性も高く、管理しやすくなります。
2018年12月13日木曜日
ec2のインスタンスログインを簡単に管理する ~EC2 with Peco ~
sitateruでサービス・プロダクトを担当している北爪です。
インフラを見ることもあり、ec2サーバーのアドレス管理についてCLIで行う方法を紹介します。
目的
ec2のインスタンスはアドレスが構築するたびに変わり、負荷や新しいプロジェクトが立ち上がると増えたり減ったりします。
そのため、動的にサーバーアドレスを取得する必要があります。
AWS consoleに入ることなく、awsコマンドからサーバーリストを取得し、CLI filterのpecoを使って、SSHまでおこないます。
使い方
Command+\
でpecoを起動
やじるしの ↑↓
か、キーワードで絞る
SSHするサーバーを選んでEnterを押す
SSHコマンドがはかれる
準備
zshを使っているのを前提に説明します。
必要ソフトのインストール
Shellの設定
ec2 コマンドを作る
AWS CLIから、ec2をリスト化するコマンドを作成し、shellに設定します。(zshrc)
alias ec2="aws ec2 describe-instances --query 'Reservations[].Instances[].{PublicDnsName:PublicDnsName,InstanceId:InstanceId,Tags:Tags[?Key==\`Name\`].Value|[0],InstanceType:InstanceType,State:State.Name}' --output table"
peco-ec2コマンドを作る
zshrc
にpeco-ec2
をつくります- ログインユーザーをdeployにしています
function peco-ec2 () {
local selected=$(ec2 | peco --query "$LBUFFER")
if [ -n "$selected" ]; then
BUFFER="ssh deploy@$(echo $selected | awk 'match($0, /ec2.*\.amazonaws\.com/) {print substr($0, RSTART, RLENGTH)}')"
zle accept-line
fi
zle clear-screen
}
- さらに、好きなキーにバインドします。
- ここでは、
Command-\
につけています
zle -N peco-ec2
bindkey '^\' peco-ec2
まとめ
ec2をリスト化し、すぐにそこからsshを選べるようにしました。
2018年12月12日水曜日
Alexaスキルを作ろう
こんにちは。シタテルでエンジニアをしている熊谷です。
主にSPECというサービスのバックエンド開発(Rails)を行っています。
少し前にAlexaスキルの作成を試したので、その流れを簡単に説明していきます。
手順
1.Amazonアカウントを用意する
すでにamazon.co.jpの開発者アカウントがある場合は、それを使います。
お買い物用のAmazonアカウントを使用して、開発を開始できます。
Amazon Developerから、サインインして、情報を入力すると、開発用アカウントが登録されます。
※ 画像のページで、ログインをして進みます。[Amazon Developerアカウントを作成]から進むと、amazon.com用のアカウントになってしまい、US向けのスキルになってしまうようです。
2.Alexa Developer Consoleにアクセス
3.[スキルの作成]をクリックし、スキル名などを入力する
4.スキルのページで、[呼び出し名]を設定する
5.エンドポイントを設定する
- AWS Lambdaが推奨されているので、Lambdaでエンドポイントを作成するのが良いと思います。
- リクエスト、レスポンスはJSON形式で送信します。
- 作成したLambdaのARNを指定します。
6.インテントを作成する
- [インテントを追加]をクリックし、カスタムインテントを作成します。
7.作成したインテントのページで、スキルの発話時に使用する、スロット(変数のようなもの)を設定する
- スロットには、日付、数値、検索クエリー(文字列)などの型を指定できます
8.インテントを設定する
- 作成したインテントの設定で、サンプル発話を登録します。
- サンプル発話には上で作成したスロットを
{スロット名}
のような形で使用できます。
これで簡単なスキルを作成できました!
今回はざっくりした流れの紹介なので、ここまでにしたいと思います。
次回はLambdaの内容や、作成したスキルの公開、テストなどを説明していこうと思います。
2018年12月7日金曜日
CircleCIでAWS ECSに自動デプロイする(おまけで環境面の切り替えも)
こんにちは、シタテルの茨木です。
今回はCircleCIでDockerをビルドし、そのままAWS ECR/ECSにデプロイする為の設定例を紹介します。
Nuxt.jsアプリ用に作ったものですが、言語やAPサーバが違っても共通の部分がほとんどです。
PRマージからの自動デプロイ、いいですよね。
https://circleci.com/docs/2.0/ecs-ecr/
https://github.com/CircleCI-Public/circleci-demo-aws-ecs-ecr
基本的にはCircleCI公式ガイドの上記リンクを参考にしていますが、元ネタはterraform前提だったりでなかなか大仰です。(下記に説明漏れがあったら原典を見てください…)
今回紹介するのはリンク先のスクリプト類を簡素化し、少し機能を追加(リポジトリ名からデプロイ先の環境面を切り替えできるように)したものです。
以下で省略しているけれど必要なモノ
- ECS/ECRの設定
これだけで別の記事が書けてしまうので割愛します
ecsTaskExecutionRoleの割り当てとか忘れがちですかね
- githubリポジトリ
言わずもがな。CircleCIとの接続設定はされているものとします
- Dockerfile
リポジトリのルートに。Dockerfileの書き方は解説しません
- requirements.txt
リポジトリのルートに。awscliを使えるようにします
https://github.com/CircleCI-Public/circleci-demo-aws-ecs-ecr/blob/master/requirements.txt
- CircleCIの環境変数設定
github上にaccess_keyを晒さなくていいように、入れておきます
https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-project
この手順で設定した環境変数はCircleCIのジョブから参照できます
.circleci/config.yml:build
version: 2
jobs:
~省略~
build:
docker:
- image: circleci/node:10-stretch
steps:
- checkout
- setup_remote_docker
- run:
name: Setup common environment variables
command: |
echo 'export ECR_REPOSITORY_NAME="YOUR-ECR-REPO-NAME"' >> $BASH_ENV
echo 'export FULL_IMAGE_NAME="${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${ECR=REPOSITORY_NAME}:${CIRCLE_SHA1}"' >> $BASH_ENV
- run:
name: Build image
command: |
docker build -t $FULL_IMAGE_NAME .
- run:
name: Save image to an archive
command: |
mkdir docker-image
docker save -o docker-image/image.tar $FULL_IMAGE_NAME
- persist_to_workspace:
root: .
paths:
- docker-image
~省略~
さっそくですがビルドジョブです。
- YOUR-HOGEHOGE箇所はご自身のECS/ECRの設定に合わせて置き換えてください
- image: circleci/node:10-stretch
node:10を使っていますが、これはビルドする対象がNuxt.jsアプリだからですね。Dockerビルド時に必要なモノに合わせて修正してください
.circleci/config.yml:deploy
~省略~
deploy:
docker:
- image: circleci/python:3.6.1
environment:
AWS_DEFAULT_OUTPUT: json
steps:
- checkout
- setup_remote_docker
- attach_workspace:
at: workspace
- restore_cache:
key: v1-{{ checksum "requirements.txt" }}
- run:
name: Install awscli
command: |
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
- save_cache:
key: v1-{{ checksum "requirements.txt" }}
paths:
- "venv"
- run:
name: Load image
command: |
docker load --input workspace/docker-image/image.tar
- run:
name: Setup target environment variables
command: |
echo 'export TARGET=`echo $CIRCLE_BRANCH | sed -e s/develop.*/dev/ -e s/release.*/stg/ -e s/master/prd/`' >> $BASH_ENV
source $BASH_ENV
- run:
name: Setup common environment variables
command: |
echo 'export ECR_REPOSITORY_NAME="YOUR-ECR-REPO-NAME"' >> $BASH_ENV
echo 'export ECS_CLUSTER_NAME="YOUR-ECS-CULSTER-NAME-PREFIX${TARGET}"' >> $BASH_ENV
echo 'export ECS_SERVICE_NAME="YOUR-ECS-SERVICE-NAME-PREFIX${TARGET}"' >> $BASH_ENV
- run:
name: Push image
command: |
. venv/bin/activate
eval $(aws ecr get-login --region ap-northeast-1 --no-include-email)
docker push $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/$ECR_REPOSITORY_NAME:$CIRCLE_SHA1
- run:
name: Deploy
command: |
. venv/bin/activate
export AWS_DEFAULT_REGION="ap-northeast-1"
export ECS_TASK_FAMILY_NAME="YOUR-ECS-TASK-NAME-PREFIX${TARGET}"
export ECS_CONTAINER_DEFINITION_NAME="YOUR-ECS-CONTAINER-DEF-NAME-PREFIX${TARGET}"
export EXECUTION_ROLE_ARN="arn:aws:iam::$AWS_ACCOUNT_ID:role/ecsTaskExecutionRole"
bash ./deploy.sh
~省略~
デプロイジョブです。
- ブランチ名から環境名へ
echo 'export TARGET=`echo $CIRCLE_BRANCH | sed -e s/develop.*/dev/ -e s/release.*/stg/ -e s/master/prd/`' >> $BASH_ENV
ブランチ名に応じた値(developブランチなら"dev")を環境変数に入れています。これをsuffixとして使い、デプロイ先のクラスタ名等に反映しています
- YOUR-HOGEHOGE箇所はご自身のECS/ECRの設定に合わせて置き換えてください
余談ですが、ECSはクラスタ・サービス・タスク・タスク定義といろいろ設定しないといけないのですが、なかなか直感的にわかりにくくてイマイチな印象です
.circleci/config.yml:workflows
~省略~
workflows:
version: 2
build-deploy:
jobs:
- build:
filters:
branches:
only:
- develop
- /release\/.*/
- master
- deploy:
requires:
- build
filters:
branches:
only:
- develop
- /release\/.*/
- master
この辺はよしなに
deploy.sh
#!/usr/bin/env bash
set -eo pipefail
# more bash-friendly output for jq
JQ="jq --raw-output --exit-status"
deploy_cluster() {
make_task_def
register_definition
if [[ $(aws ecs update-service --cluster $ECS_CLUSTER_NAME --service $ECS_SERVICE_NAME --task-definition $revision | \
$JQ '.service.taskDefinition') != $revision ]]; then
echo "Error updating service."
return 1
fi
# wait for older revisions to disappear
# not really necessary, but nice for demos
for attempt in {1..30}; do
if stale=$(aws ecs describe-services --cluster $ECS_CLUSTER_NAME --services $ECS_SERVICE_NAME | \
$JQ ".services[0].deployments | .[] | select(.taskDefinition != \"$revision\") | .taskDefinition"); then
echo "Waiting for stale deployment(s):"
echo "$stale"
sleep 30
else
echo "Deployed!"
return 0
fi
done
echo "Service update took too long - please check the status of the deployment on the AWS ECS console"
return 1
}
make_task_def() {
task_template='[
{
"name": "%s",
"image": "%s.dkr.ecr.%s.amazonaws.com/%s:%s",
"essential": true,
"portMappings": [
{
"containerPort": 80
}
],
"environment": [
{
"name": "TARGET",
"value": "%s"
}
]
}
]'
task_def=$(printf "$task_template" $ECS_CONTAINER_DEFINITION_NAME $AWS_ACCOUNT_ID $AWS_DEFAULT_REGION $ECR_REPOSITORY_NAME $CIRCLE_SHA1 $TARGET)
}
register_definition() {
if revision=$(aws ecs register-task-definition --requires-compatibilities FARGATE --cpu 256 --memory 1024 --network-mode awsvpc --execution-role-arn $EXECUTION_ROLE_ARN --container-definitions "$task_def" --family $ECS_TASK_FAMILY_NAME | $JQ '.taskDefinition.taskDefinitionArn'); then
echo "New deployment: $revision"
else
echo "Failed to register task definition"
return 1
fi
}
deploy_cluster
デプロイジョブから呼び出すスクリプトです。
task_templateの内容を色々弄るとカスタマイズできます
設定可能な項目はこちら
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/create-task-definition.html
ここでは、environmentで環境変数TARGETを設定しています
ECSがdocker runする際に引数として付与され、docker内のアプリから環境変数として参照可能になります
実際には、nuxt.config.js内でTARGETを参照し、APIサーバの向け先を切り替えたり等しています
以上、ご参考になれば幸いです。
2018年11月30日金曜日
AWSLambdaとServerlessを使ってみる[第2回]
こんにちは、シタテルエンジニアの工です!
AWSLambdaとServerless第2回に入っていきます!
前回は、AWSLambdaとserverlessについてさらっと確認して
serverlessでServiceをcreateするところまでをやってきました。
AWSLambdaとServerlessを使ってみる[第1回]|sitateru tech blog
今回は実際にSansan OpenAPIを利用して名刺情報を取得してみます 🚀
SansanAPI
公式ドキュメント Sansan Open API
名刺API 名刺Set取得(期間指定)
を使ってみます。
SansanAPIを利用するにはAPI Keyが必要です。
取得方法はこちらを参考にしてください。
作る
前回createしたServiceを利用して作っていきます。
現在この2ファイルが作成されている状態です。
- serverless.yml
- 各種設定を書いていきます
- handler.js
- 処理を書いていきます
axiosインストール
今回は、httpリクエストにaxiosを使用します。
$ npm install axios
インストールされました
.
├── handler.js
├── node_modules
│ ├── axios
├── package-lock.json
└── serverless.yml
serverless.yml
環境変数の設定
lambdaには環境変数を設定することができます。
SansanAPIKeyを環境変数として設定してみます。
serverless.yml
# you can define service wide environment variables here
environment:
SANSAN_API_KEY: xxx
環境変数を利用する場合は、このように取得できます。
process.env.SANSAN_API_KEY
handler.js
それでは処理を書いていきます。
lambdaの非同期処理とcallbackについてはこちらを参考にしてください。
名刺情報はお見せできないので、取得した件数を出力してみます。
'use strict';
const axios = require('axios')
axios.defaults.headers.common['X-Sansan-Api-Key'] = process.env.SANSAN_API_KEY
axios.defaults.headers.get['Content-Type'] = 'application/json'
module.exports.hello = async (event, context, callback) => {
var updatedFrom = "2018-11-26T01:00:00Z"
var updatedTo = "2018-11-27T21:00:00Z"
await axios.get(`https://api.sansan.com/v2.0/bizCards?updatedTo=${updatedTo}&range=all&entryStatus=completed`)
.then(function (response) {
callback(null, response.data.data.length)
})
.catch(function (error) {
callback(error)
});
};
deploy
cliでdeployします。
serverlessを使わない場合、serviceを手動でzipにしてawsにdeployする必要があるのでとても面倒です...
$ serverless deploy -v
deployが完了したら情報が出力されます。
...
Serverless: Stack update finished...
Service Information
service: hello-sansan
stage: dev
region: us-east-1
stack: hello-sansan-dev
api keys:
None
endpoints:
None
functions:
hello: hello-sansan-dev-hello
lambdaの関数のところにも表示されています。
環境変数も設定されています。
実行
こちらもcliで実行します。
$ sls invoke -f hello
16
件数が取得できました。
おわりに
今回はlambdaからSansanOpenAPIを利用して名刺情報をゲットしてみました!
lambdaはサーバーのこと気にせずに処理だけ書けば良く、serverlessはcliでごにょごにょできるし、とても便利です!
sitateruではlambdaを使って、SansanとHubSpotの同期を自動化しています。
どこかのタイミング第3回としてserverless-offlineを使用したlambdaの開発についても書こうと思います。
2018年11月22日木曜日
AWSLambdaとServerlessを使ってみる[第1回]
こんにちは、シタテルエンジニアの工です!
AWS Lambdaを触る機会がありましたので、ServerlessFrameworkと一緒にいじいじしてみました!
シリーズで、
Serverlessをインストール
AWSLambdaでSansanの名刺データを取得してみる
serverless-offlineでAWSLambdaをローカルで実行
なんかを書いていこうと思います!
さらっとAWSLamdaとServerlessFrameworkについて
AWS Lambdaとは
AWS Lambda はサーバーをプロビジョニングしたり管理しなくてもコードを実行できるコンピューティングサービスです。
つまり、流行りのサーバーレス
てやつで、サーバーを準備しなくてよいので、ちょっとした処理の実行など大変助かります。
しかも、LambdaはNode.js (JavaScript)、Python、Java (Java 8 互換)、C# (.NET Core)、 Goに対応しています!
様々な言語に対応しているので学習コストもかからないのがいいですね。
シタテルでは、HubSpotとSansanのデータ同期や、SlackCommandでリリースPRを作るなどで使っています。
Serverlessとは
The Serverless Framework is an open-source CLI for building and deploying serverless applications.
つまり、サーバーレスアプリケーションの開発を手助けをしてくれるやつです!
AWS Lambda単体で開発してみるとわかるのですが、ローカルでの開発やビルド、デバッグやらが面倒です。
このServerless Frameworkを使えばそのあたりのもやもやを解消してくれます!
作ってみる
Serverless Framework - AWS Lambda Guide - Introduction こちらがAWS用のServerlessFramework公式ドキュメントです。 基本的にこちらの内容に沿って進めていきます、
今回は、Sansanの名刺データを取得してみます。
まずはServerlessFrameworkをインストール
$ npm install -g serverless
$ serverless -v
1.33.2
正しくバージョンが表示されていたら、インストール完了です!
Serviceを作る
Serviceはプロジェクトのようなもの。
今回は、nodejsで作っていきますので、aws-nodejs
を指定。
その他の言語を使いたい方はこちらを参考に。
$ serverless create --template aws-nodejs --path hello-sansan
実行すると
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/mogura/work/sitateru/lambda/hello-sansan"
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v1.33.2
-------'
Serverless: Successfully generated boilerplate for template: "aws-nodejs"
このような構成で、Serviceができました!
hello-sansan
├── handler.js
└── serverless.yml
次は、実際に処理を書いていきます。
が、長くなりそうなので今回はここまでに。
おわりに
今回は、ServerlessFrameworkでserviceを作るところまでをやってみました。
次回は、実際にAWSLamdaでSansanから名刺データを取得するとことまでをやっていきます! 🚀
2018年11月14日水曜日
リリース作業を少し楽にした開発(Slack+Lambda+Golang)
こんにちは、シタテルの鶴巻です。
devops&インフラ担当です。
シタテルのプロダクトのリリース作業を少し楽にするために、
Slack+Lambda+Golangを用いて開発した話をします。
リリースの流れ
シタテルでは、GitフローやGitHubフローを採用しています。
また、本番環境へのデプロイは、以下の流れで実施しています。
- developブランチからreleaseブランチを作成
- ステージング環境でreleaseブランチをデプロイ&動作確認
- 本番環境にデプロイ
ちなみに、1.でreleaseブランチを作成すると、CircleCIによって2.のステージング環境へのデプロイは自動で行われます。
地味に面倒なリリース作業
ステージング環境へのデプロイは自動化されているとはいえ、小さな手作業が合間に発生し、地味に面倒でした。
具体的には以下のような作業です。
- developブランチからreleaseブランチの作成
- ローカルPCでdevelopブランチを最新の状態にpullして、releaseブランチ作成してGitHubにpush
- releaseブランチのプルリクエスト作成
- GitHubのUIをポチポチ
- リリースタグの作成
- 前回のリリース以降でマージされたプルリクエストを見て、リリース内容を記述
手作業はSlackからコマンドを叩くだけにしました
Slackから以下のコマンドを叩くだけで、地味に面倒くさかった作業を自動で行うようにしました。
/release <repository> <release branch>
実際には、以下の内容を実施するGolangプログラムをLambdaで実行しています。
- 対象のリポジトリをクローンし、developブランチからreleaseブランチを作成し、GitHubにpush
- releaseブランチのプルリクエストを作成
- 前回のリリース以降にマージされたプルリクエストのタイトルのリストを記述したリリースタグのドラフトを作成
構成
Slack -> API Gateway -> Lambda01(同期) -> Lambda02(非同期) -> GitHub
- Slackからコマンドを叩くと、Lambda01にリクエストを送信します。
- Lambda01は、Lambda02に対象レポジトリとブランチ名を付与してリクエストを送信します。
- Lambda02で作業を実行します。
Lambdaを2つ使用している理由
Slackはリクエスト送信後3秒でタイムアウトするためです。
実行したいプログラムは3秒以上かかるため、実際の作業は非同期で実施する必要がありました。
そのため、Lambda01はLambda02に処理を投げて、Slackに200を返し、実際の処理はLambda02で実施します。
Lambda01はLambda02を非同期実行で呼び出すので、リクエストをキューに入れた後は、Lambda01は関数を終了することができます。
Lambdaの呼び出しタイプ(同期・非同期)
Lambdaは、呼び出しタイプ(InvocationType)を指定することで、同期実行や非同期実行を選択することができます。
例:InvocationTypeで"Event"を指定することで、非同期実行でLambdaを呼び出せます。
input := &lambda.InvokeInput{
FunctionName: aws.String("xxxx"),
Payload: jsonBytes,
InvocationType: aws.String("Event"),
}
※同期実行は"RequestResponse"を指定します
今回使用したGitHub操作のためのGoライブラリ
- go-git https://github.com/src-d/go-git
- インメモリでgitクローンするために使用
- go-github https://github.com/google/go-github
- google製
- プルリク作成やリリースタグ作成に使用
その他ハマったポイント
- APIGatewayの統合リクエストのマッピングテンプレートを使いこなせず。デバッグのやり方もわからず、使うのを諦めました。
まとめ
- 少しの手間が自動化されるだけで、すごく楽になります。これからももっとやっていきたいです。
- GitHub操作のライブラリはとても便利でした。
- シタテルのサービスはRails + vue.jsが主ですが、自分の好きなGo言語を使えたので楽しかったです。
参考
2018年10月31日水曜日
s3の操作はaws-cliよりs3cmd使うほうが便利
こんにちは!
シタテルで エンジニアをしている建山です。
主に工場向けのマイオペというシステムの開発を行っています。
シタテルではファイルをawsのs3でホスティングしています。
直接s3のファイルを操作することは少ないのですが、やはり時々は発生します。
そんなときにはaws-cli
を使うこともできるのですが、s3cmd
を使うとより簡単なコマンドでS3を操作できます。
いくつかよく使うコマンドをまとめました。
ディレクトリのファイル権限を操作
s3cmd setacl -r --acl-private s3://バケット名/ディレクトリ名/
-r
は再帰的に配下すべてやってくれる--acl-private
はprivate権限にする--acl-public
はpublic権限にする
※バケットも指定できる
ディレクトリの中身を参照
s3cmd ls s3://バケット名/ディレクトリ名/
バケットの情報をみる
s3cmd info s3://バケット名
これ以外にもファイル/ディレクトリアップロードや削除などいろいろなことができます。
コマンドのインストール
インストールはmacの場合、
brew install s3cmd
でできます。
コマンドを使う前に、AWSのアクセスキー等を設定 する必要があります。
s3cmd --configure
ぜひ、お試ししてみてください。
2018年10月30日火曜日
AWS Loft Tokyoに行ってきました
こんにちは!
シタテルで エンジニアをしている熊谷です。
主にSPECというサービスのバックエンド開発(Rails)を行ってます。
今回は、話題のAWS Loft Tokyoに行って来たのでレポートします。