tag:blogger.com,1999:blog-17979291850447975532024-02-08T13:15:09.924+09:00sitateru tech blogシタテルの技術やエンジニアの取り組みを紹介するテックブログです。Unknownnoreply@blogger.comBlogger85125tag:blogger.com,1999:blog-1797929185044797553.post-61913988573767398952021-12-22T18:04:00.003+09:002021-12-22T18:04:37.361+09:00GitHub Container Registryに公開リポジトリを作ってみた<p>みなさんはコンテナイメージはどこに置いているでしょうか。<br />
シタテルでは「基本はECR、GCPで利用するイメージはGCR」というまあ普通な構成になっています。<br />
ですがこの度、「せっかく作ったし公開しておいてもいいかな」というコンテナイメージの置き場としてGHCRを使い始めました。</p>
<h3>GHCRとは</h3>
<p><a href="https://docs.github.com/ja/packages/working-with-a-github-packages-registry/working-with-the-container-registry">コンテナレジストリの利用 - GitHub Docs</a><br />
GHCRはGitHub Container Registryの頭文字で、GitHub上でいろいろなパッケージを公開できるGitHub Packagesの一機能といったところでしょうか。<br />
DockerfileをGitHubで管理するならGitHub上でイメージ配布まで完結するわけですね。</p>
<p>料金ですが、パブリックなパッケージについては無料となってます。今回のニーズにはちょうどいいですね。<br />
<a href="https://docs.github.com/ja/billing/managing-billing-for-github-packages/about-billing-for-github-packages">About billing for GitHub Packages - GitHub Docs</a></p>
<p>イメージのpull/push等は <code>ghcr.io/<OWNER>/<IMAGE_NAME>:<tag></code> という表記になります。(OWNERはGitHubのユーザー名やorganization名になります)</p>
<p>push前などに認証する場合は以下のようになります。</p>
<ol>
<li>
<code>write:packages</code> スコープを持つアクセストークンを作っておく</li>
<li><code>$ echo <token> | docker login ghcr.io -u USERNAME --password-stdin</code></li>
</ol>
<h3>リポジトリ作成・push</h3>
<p>というわけで、organizationのPackagesにコンテナイメージをアップロードして公開してみましょう。<br />
上記のコマンドで認証したら、好きなイメージ名とタグ名をつけてpushするだけです。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ echo $GH_TOKEN | docker login ghcr.io -u sitateru --password-stdin
$ docker pull hello-world:latest
$ docker tag hello-world:latest ghcr.io/sitateru/hello-world:latest
$ docker push ghcr.io/sitateru/hello-world:latest
</code></pre></div>
<p>アップロードしたパッケージは、<code>https://github.com/orgs/&lt;OWNER>/packages</code>のURLで一覧できます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6_BwbIbjqvG3yRDQPjQMLi0l4o8lbDYDD7u7vnnZbntRSFVhNe218-aMp9RzEoXQouZ-X4oiRo3GSawXad7ew03UqgvZV7rKa9m2hflDYeq1Ky9M1HhBXyA3THz-eV9AFvMiJoxFlNZw/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="148" data-original-width="713" height="66" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6_BwbIbjqvG3yRDQPjQMLi0l4o8lbDYDD7u7vnnZbntRSFVhNe218-aMp9RzEoXQouZ-X4oiRo3GSawXad7ew03UqgvZV7rKa9m2hflDYeq1Ky9M1HhBXyA3THz-eV9AFvMiJoxFlNZw/" width="320" /></a></div><br />organizationにアップロードすると、最初はInternalというアクセス設定になるようです。<p></p><p>
公開するためには、設定画面から <code>Change package visiblity</code> をクリックしてPublicを選択。<br />
確認のためパッケージ名を入力してボタンをクリックすれば完了です。</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9Dos_d8-PqkZ9SGBnA_GtJ3FYo9UtdyKLKp7TtgefLiIDHkYlAybffrdZLy1xcDcGcJxADxy9bNigcA9vI7wCAsdNTZE3DrG8fkrEudPHwuPB3pYJ1qjfeQ_7A_ogkqWEizLP1fH7H_8/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="223" data-original-width="800" height="89" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9Dos_d8-PqkZ9SGBnA_GtJ3FYo9UtdyKLKp7TtgefLiIDHkYlAybffrdZLy1xcDcGcJxADxy9bNigcA9vI7wCAsdNTZE3DrG8fkrEudPHwuPB3pYJ1qjfeQ_7A_ogkqWEizLP1fH7H_8/" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNXYA9UpyqNkml_HX7qVOWWi-3vaqjahNaFtDgSBVDQ4PCGw6LM1sZBjTurrnwKczqClp9vOWreP3zQYuEr7QRkxJh4JtKHPu35NhVn4ufdoPo9XCgMvaxr7obRev0KScfZZhNoiq1mQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="415" data-original-width="455" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNXYA9UpyqNkml_HX7qVOWWi-3vaqjahNaFtDgSBVDQ4PCGw6LM1sZBjTurrnwKczqClp9vOWreP3zQYuEr7QRkxJh4JtKHPu35NhVn4ufdoPo9XCgMvaxr7obRev0KScfZZhNoiq1mQ/" width="263" /></a></div><br /><h3>リポジトリと紐づけ</h3>
<p>作成したパッケージにはリポジトリを紐づけできるようになっています。<br />
パッケージのページで <code>Connect Repository</code> をクリックしてリポジトリを選択すればOKです。</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjadFLlSJ19X-D1J-0PNOK-l4vVi7uzK6aXWORpXvXFhP53unOnYr0B5foYtusKD_iOC4DoVVro1GEvqvnB_ONuhvLjY6ncQHmjW3oU7WtlghSPX4JLaKkYA8bxxjLANqOEM131Y8rIelo/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="330" data-original-width="594" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjadFLlSJ19X-D1J-0PNOK-l4vVi7uzK6aXWORpXvXFhP53unOnYr0B5foYtusKD_iOC4DoVVro1GEvqvnB_ONuhvLjY6ncQHmjW3oU7WtlghSPX4JLaKkYA8bxxjLANqOEM131Y8rIelo/" width="320" /></a></div><br />紐づけするとリポジトリのREADMEがパッケージのREADMEとして表示されるほか、パッケージのアクセス権限を「紐づけたリポジトリと等しくする」ことができます。管理の手間が楽になってありがたいですね。<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibGDBhpvosdv4y4FqdZRJrmWPNUiEWhwaoQiifOEbaX7nAMkYZe2E4wfeHyTt6ZirHTpOTRbM-CM7I96wdKEKovRdCWoYwA9xXapViKce63M3FZGYOOOcVdusf8RQSq0vNCrc2uVnLD7g/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="124" data-original-width="747" height="53" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibGDBhpvosdv4y4FqdZRJrmWPNUiEWhwaoQiifOEbaX7nAMkYZe2E4wfeHyTt6ZirHTpOTRbM-CM7I96wdKEKovRdCWoYwA9xXapViKce63M3FZGYOOOcVdusf8RQSq0vNCrc2uVnLD7g/" width="320" /></a></div><br /><p></p>
<h3>どんなイメージを公開するか</h3>
<p>現在のところ、ローカル開発環境やCIで使うために公式イメージに一手間加えたようなものばかりです。詳しくはリポジトリをご参照ください。</p>
<p><a href="https://github.com/sitateru/docker-images">GitHub - sitateru/docker-images</a></p>
<p>ご利用はご自由にどうぞなのですが、保証やサポート等はできませんのでご了承ください🙇♂️<br />
タグ追加などの要望があれば、issueなりPRなりいただければできる限り対応しますのでよろしくお願いします。</p>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-10325770367689873052021-10-14T17:06:00.001+09:002021-10-14T17:06:29.034+09:00Amazon EFSを使ってEKSに永続ボリュームを導入した<p>AWS EKSで動かしているアプリケーション用にEFSを使って永続ボリュームを用意してみたので、その方法をまとめてみようと思います。</p>
<p>とあるテスト用Kubernetes環境でMySQLのイメージを使ってデータベースを動かしているのですが、何かの拍子にpodが一旦削除などされてしまうとデータベースの中身のデータが全て無くなってしまいます。<br />
そこで永続ボリュームを確保しておいて、MySQLデータ用のディレクトリをマウントしたボリュームにすればOKというわけです。</p>
<p>手順はほぼこのドキュメントどおりですが、一部変えないと動かなかった箇所があるのでその点も触れつつ書いてみようと思います。<br />
<a href="https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/efs-csi.html">Amazon EFS CSI ドライバー</a></p>
<p>ドキュメントに従っても動かないよ!という件のissueはこちら。<br />
<a href="https://github.com/kubernetes-sigs/aws-efs-csi-driver/issues/489">Missing permission in the example IAM policy file · Issue #489 · kubernetes-sigs/aws-efs-csi-driver · GitHub</a></p>
<p>また、今回やってみた環境は以下のようになっています。<br />
Kubernetes v1.21<br />
EKSプラットフォーム eks.2<br />
EFS CSI ドライバー v1.3.4</p>
<hr />
<ol>
<li>Amazon EFS CSI ドライバー</li>
</ol>
<p>まずはIAMポリシーを作成します。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">{
"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"
}
}
}
]
}
</code></pre></div>
<p><code>AmazonEKS_EFS_CSI_Driver_Policy</code> というポリシーを作成してこのjsonでアクセス権限を設定します。</p>
<p>AWSのドキュメントからリンクされているポリシーと比べると"elasticfilesystem:DescribeMountTargets", "ec2:DescribeAvailabilityZones"が増えているのですが、これを追加しないと権限不足のエラーが出てドライバーが動きませんでした。🤔</p>
<p>このポリシーでサービスアカウントを作成します。<code><cluster-name></code>と<code><Account ID></code>, <code><region></code>はお使いの環境に合わせて置き換えてください。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ 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>
</code></pre></div>
<p>Helmを使ってドライバーをクラスターにインストールします。<br />
クラスターのリージョンによって<code>image.repository</code>は変えないといけないようなので↓を参照してください。<br />
<a href="https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/add-ons-images.html">Amazon EKS アドオンコンテナイメージのアドレス - Amazon EKS</a></p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ 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
</code></pre></div>
<p>AWSのドキュメントでは <code>efs-csi-node-sa</code> は出てこないのですが、こちらも試した環境ではこのサービスアカウントを作成してhelmで指定しないと動きませんでした。🤔</p>
<br />
<ol start="2">
<li>EFS ファイルシステム作成</li>
</ol>
<p>ここはインフラ管理の都合上terraformで行いました。<br />
セキュリティグループ、ファイルシステム、マウントターゲット(AWSコンソールでは「ネットワーク」という表示名になっています)を作成します。<br />
VPCに複数のプライベートサブネットがある場合は <code>aws_efs_mount_target</code> も同様に複数作っておきましょう。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">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]
}
</code></pre></div>
<br />
<ol start="3">
<li>クラスターでStorageClassを追加</li>
</ol>
<p>まずはストレージクラスの宣言です。StorageClassはnamespaceに属していないので、一度applyしておけばOKです。<br />
parametersについてはこちらを参考にしてください👇</p>
<p><a href="https://github.com/kubernetes-sigs/aws-efs-csi-driver/tree/master/examples/kubernetes/dynamic_provisioning">aws-efs-csi-driver/examples/kubernetes/dynamic_provisioning at master · kubernetes-sigs/aws-efs-csi-driver · GitHub</a></p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">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"
</code></pre></div>
<br />
<ol start="4">
<li>アプリケーションで永続ボリュームを確保、マウント</li>
</ol>
<p>これで準備ができたので、PersistentVolumeを使うアプリケーションでのyamlを書いていきます。</p>
<p>まずはPVC(PersistentVolumeClaim)です。<br />
<code>storageClassName</code> は3.で宣言したStorageClassのnameですね。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 2Gi
</code></pre></div>
<p>このPVCで確保したボリュームをコンテナにマウントします。<br />
以下のyamlはdeploymentでやる場合のvolumeに関するところを書き出したものです。 <code>claimName</code> がPVCリソースのnameですね。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">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
</code></pre></div>
<hr />
<p>と、このような作業で永続ボリュームを利用できました。</p><p>冒頭で触れたドキュメント不完全問題もあって少し手間取りましたが、振り返ってまとめてみるとそう難しいことはなさそうですね。</p><p>EKSで永続ボリュームが欲しくなったときはお試しください👐</p>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-1582981509989056812021-08-27T18:19:00.000+09:002021-08-27T18:19:37.984+09:00AWS App Mesh をEKSで試してみた(仮想ゲートウェイ)<p><a href="https://tech-blog.sitateru.com/2021/06/aws-app-mesh-on-eks-2.html">前回</a>に続き、EKS上でのAWS App Meshをやっていきます。</p>
<p>今回は、仮想ゲートウェイです。<br />
ゲートウェイがあれば、メッシュ上のリソースにメッシュ外のpodやWebなどからアクセスできるようになります💁♂️</p><p>今回の内容もドキュメント等いまいち見つからなかったので正確さなどは怪しいところがありますがご了承ください🙇♂️</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/app-mesh/latest/userguide/virtual_gateways.html">Virtual gateways - AWS App Mesh</a></p>
<hr />
<ol>
<li>仮想ゲートウェイ作成</li>
</ol>
<p>まずは仮想ゲートウェイとそのルートを作成します。<br />
リスナーのポートは後で作るゲートウェイの実体と番号を合わせれば何番でもよさそうです。ルートは、全てのリクエストを前回作った仮想サービス(svc-v)へ流すよう指定してみました。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">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: /
</code></pre></div>
<ol start="2">
<li>ゲートウェイ作成</li>
</ol>
<p>ゲートウェイの実体となる、envoyイメージのコンテナと <code>type: LoadBalancer</code> な サービスを作成します。<br />
envoyのcontainerPortとサービスのtargetPortは仮想ゲートウェイのリスナにしているポートと等しくする必要があります。<br />
また、envoyの環境変数としてリージョンと仮想ゲートウェイARNを与える必要があるようです。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">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
</code></pre></div>
<hr />
<p>以上をapplyすると、ロードバランサーが作られて仮想ゲートウェイとして動くようになります。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ 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
</code></pre></div>
<p>AWSコンソールでロードバランサーを見てみると、NLBができていました。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM7ySRlaS30hanMfiZQwATmIO8hjzQNBHwJTf16iCipW9dn4qWkgaw3cZfPPAHlofK_V9J7kGfOpwgOtRGl5GNfa8lRE9XhB_FDUj2tAhlb_apq0am3wJohMsiVuhzbL13o-p4HsGRTvI/s840/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2021-08-24+165325.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="443" data-original-width="840" height="169" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM7ySRlaS30hanMfiZQwATmIO8hjzQNBHwJTf16iCipW9dn4qWkgaw3cZfPPAHlofK_V9J7kGfOpwgOtRGl5GNfa8lRE9XhB_FDUj2tAhlb_apq0am3wJohMsiVuhzbL13o-p4HsGRTvI/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2021-08-24+165325.png" width="320" /></a></div><br />
<p>コンソールのApp Meshでもゲートウェイが表示されています。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4GRc-gMpeJbTb9CBYn20tWJplrw7FhpAZw3srLx9Ewd71vkChNjA7kaCKc7ySFv_k4YvgHKW0QzlflW_5WvosUyukOc1OERqQKu24uvX3yzCRkSPsvmkoJrOCuLjzhkEw98L8CsGjkhg/s637/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2021-08-24+173647.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="452" data-original-width="637" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4GRc-gMpeJbTb9CBYn20tWJplrw7FhpAZw3srLx9Ewd71vkChNjA7kaCKc7ySFv_k4YvgHKW0QzlflW_5WvosUyukOc1OERqQKu24uvX3yzCRkSPsvmkoJrOCuLjzhkEw98L8CsGjkhg/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2021-08-24+173647.png" width="320" /></a></div><br />
<p>ゲートウェイへのアクセスですが、クラスター内からは<br />
<code>$ curl gateway-v.mesh-test.svc.cluster.local</code><br />
外からは<br />
<code>$ curl <NLBのドメイン名></code><br />
でリクエストを送って、仮想サービスsvc-vに送られることが確認できました。</p>
<hr />
<p><br /></p><p>ということで、App Mesh作ってみるシリーズでした。<br />
今回はごく基本的な部分しか試していませんが、各リソースで設定できる機能はいろいろあるので公式ドキュメントやCustomResourceDefinitionを参照してみてください🙏</p>
<p><a href="https://github.com/aws/eks-charts/blob/master/stable/appmesh-controller/crds/crds.yaml">eks-charts/crds.yaml at master · aws/eks-charts</a></p>
<p>ちなみに、App Meshには(他のサービスと同様に)作れるリソース数の制限があります。<br />
ほとんどはサポートセンターに頼めば引き上げ可能なようですが、メッシュ設計の際はちょっと気にしておいたほうがいいかもしれないですね。<br />
<a href="https://docs.aws.amazon.com/ja_jp/general/latest/gr/appmesh.html#limits_appmesh">AWS App Mesh エンドポイントとクォータ - AWS 全般のリファレンス</a></p>Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-66786044814454036682021-06-17T17:55:00.005+09:002021-06-17T17:57:08.298+09:00AWS App Mesh をEKSで試してみた(仮想ルーター、仮想サービス)<p><a href="https://tech-blog.sitateru.com/2021/06/aws-app-mesh-on-eks-1.html" target="_blank">前回</a>に続き、EKS上でのAWS App Meshをやっていきます。<br />
今回は、仮想ルーターと仮想サービスです。<br />
前回作った仮想ノードにアクセスするには仮想ルーターか仮想ゲートウェイが必要になるので、いよいよメッシュができていきます💪</p><p>あまりしっかり説明されたドキュメントが見つからず経験ベースな部分が多いので、正しい説明になっているかどうか怪しい部分もありますがご了承ください🙇</p>
<hr />
<ol>
<li>仮想ルーター作成</li>
</ol>
<p>仮想ノードへトラフィックを振り分ける仮想ルーターを作成します。<br />
振り分け先は <code>weightedTargets</code> の <code>virtualNodeRef.name</code> で仮想ノード名を指定します。<br />
プロトコルはHTTPにしましたが、他にもGRPC, HTTP2, TCPが設定できるようです。</p>
<div class="code-frame"><pre class="highlight"><code>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
</code></pre></div>
<br />
<ol start="2">
<li>仮想サービス用サービス作成</li>
</ol>
<p>仮想サービスの実体にするserviceを作成します。<br />
<code>selecter</code> は、仮想サービスを介してアクセスする先のpodが全て含まれるように指定する必要があるみたいです。<br />
今回の例だと、仮想サービス → 仮想ルーター(vrouter-1) → 仮想ノード(vnode-1, vnode-2) というトラフィックの流れになるので、vnode-1とvnode-2の実体になるpodに <code>app: sample1</code> のラベルをつけてあります。</p>
<div class="code-frame"><pre class="highlight"><code>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
</code></pre></div>
<br />
<ol start="3">
<li>仮想サービス作成</li>
</ol>
<p>先の <code>svc-v</code> を実体とする仮想サービスを作成します。<br />
<code>name</code> は上で仮想サービス用に作ったサービス名と等しくする必要があるみたいです。<br />
<code>provider</code> には仮想サービスを通してアクセスする先を指定を指定します。単一のvirtualNodeかvirtualRouterを選択できるので、トラフィックを複数ノードに分けたいならvirtualRouter、分けなくていいならvirtualNodeにするとよさそうです。</p>
<div class="code-frame"><pre class="highlight"><code>apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualService
metadata:
namespace: mesh-test
name: svc-v
spec:
provider:
virtualRouter:
virtualRouterRef:
name: vrouter-1
</code></pre></div>
<br />
<ol start="4">
<li>仮想サービスをバックエンドに設定</li>
</ol>
<p>作った仮想サービスにアクセスするには、「仮想ノードのバックエンド」にその仮想サービスを指定する必要があります。<br />
仮想ノードのyamlで、<code>backends</code>に仮想サービスを追加しておきます。</p>
<div class="code-frame"><pre class="highlight"><code>apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
name: vnode-x
namespace: mesh-test
spec:
# 主要部分は省略
backends:
- virtualService:
virtualServiceRef:
name: svc-v
</code></pre></div>
<hr />
<p>仮想ノード化されたPodから仮想サービスへリクエストを送ると、<br />
ルーティングに従ったレスポンスが返ってくるはずです。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ kubectl exec -it somepod-524b1761b4-vg8kw -- bash
root@somepod-524b1761b4-vg8kw:/# curl svc-v.mesh-test.svc.cluster.local
</code></pre></div>
<p>いろいろ試してみたところ、「バックエンドにその仮想サービスを指定したpod」で「<code>仮想サービス名.名前空間.svc.cluster.local</code>へトラフィックを送る」場合に仮想サービスで設定したトラフィック振り分けが行われる仕様のようです。<br />
それ以外の条件、例えばバックエンドを指定していないpodからのリクエストや仮想サービス名だけのホスト名にトラフィックを送る(<code>$ curl svc-v</code>)場合は(仮想ではない)Serviceの <code>svc-v</code> に行くようです。<br />なかなかややこしいですね。</p>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-69681302879278960952021-06-03T18:06:00.000+09:002021-06-03T18:06:47.065+09:00AWS App Mesh をEKSで試してみた(準備~仮想ノードまで)<p>突然ですが、AWS App Meshは皆さんお使いでしょうか?<br />
AWS製のサービスメッシュ構築サービスですね。</p>
<p>サービスメッシュといえばIstioが有名ですが、AWS App MeshはAWS製だけあってEKSはもちろんEC2やECSのインスタンス・コンテナもメッシュに組み込むことができるのが強みという印象です。</p>
<p>そんなApp MeshをEKS上でいろいろと実験してみたので、自分の理解の反芻のためにもまとめてみたいと思います。</p>
<p>試した環境は kubernetes 1.20, App Meshコントローラ 1.3.0 でした。</p>
<hr />
<ol>
<li>準備</li>
</ol>
<p>ドキュメントに従ってコントローラをインストールします。</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/app-mesh/latest/userguide/getting-started-kubernetes.html#install-controller">Getting started with AWS App Mesh and Kubernetes - AWS App Mesh</a></p>
<p>手順はドキュメントの通りなのでここでは書きませんが、完了すると<code>appmesh-system</code>ネームスペースでコントローラのポッドができていました。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ kubectl get pod -n appmesh-system
NAME READY STATUS RESTARTS AGE
appmesh-controller-6c55b46558-8s363 1/1 Running 0 1d
</code></pre></div>
<br />
<ol start="2">
<li>mesh作成</li>
</ol>
<p>メッシュを作成します。<br />
<code>egressFilter</code> はメッシュ内部から外部へのアクセスを許可するかどうかですね。許可しない場合はDROP_ALLにします。<br />
<code>namespaceSelecter</code> も何でもいいので書いておきましょう。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">apiVersion: appmesh.k8s.aws/v1beta2
kind: Mesh
metadata:
name: mesh-1
spec:
egressFilter:
type: ALLOW_ALL
namespaceSelector:
matchLabels:
mesh: sitateru-mesh
</code></pre></div>
<br />
<ol start="3">
<li>namespace作成</li>
</ol>
<p>メッシュのリソースを配置するネームスペースを宣言します。<br />
上のMeshで指定した <code>namespaceSelector</code> とラベルを合わせておきましょう。<br />
App Meshの機能を使うには<code>appmesh.k8s.aws/sidecarInjectorWebhook</code> をenabledにしてEnvoyサイドカーが入れられるようにしておく必要があります。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">apiVersion: v1
kind: Namespace
metadata:
name: mesh-test
labels:
mesh: mesh-1
appmesh.k8s.aws/sidecarInjectorWebhook: enabled
</code></pre></div>
<br />
<ol start="4">
<li>サービスアカウント作成</li>
</ol>
<p>IAMで以下のポリシーを作成します。<br />
今回は動作検証なので <code>mesh-1</code> メッシュの全ての仮想ノードと仮想ゲートウェイに対して <code>StreamAggregatedResources</code> を許可していますが、実使用の際には適宜リソース名を指定するほうが望ましいですね。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">{
"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/*"
]
}
]
}
</code></pre></div>
<p>続いてこのポリシーをアタッチしたサービスアカウントをEKSクラスターに作成します。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ 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
</code></pre></div>
<br />
<ol start="5">
<li>仮想ノード用サービス作成</li>
</ol>
<p>仮想ノードの実体にするdeploymentとserviceを作成します。コンテナイメージは何でもいいのですが、今回はただのnginxにしています。<br />
<code>serviceAccountName</code> には👆で作成したサービスアカウント名を指定します。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">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
</code></pre></div>
<br />
<ol start="6">
<li>仮想ノード作成</li>
</ol>
<p>
今作った<code>svc-1</code> を実体とする仮想ノードを作成します。<br />
<code>serviceDiscovery</code> で仮想ノードとする実体を指定するのですが、ここでは目的のサービスのDNSホスト名( <code>サービス名.名前空間.svc.cluster.local</code> )を指定します。<br />
</p>
<p><a href="https://kubernetes.io/ja/docs/concepts/services-networking/dns-pod-service/#a-aaaa%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89">ServiceとPodに対するDNS | Kubernetes</a></p>
<p>ちなみにDNSではなくCloud Mapを使って指定することもできるみたいです。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">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
</code></pre></div>
<br />
<p>うまくできていればAWSコンソールでもメッシュや仮想ノードが表示されているはずです。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqtveD5Z4dZwNbS02DC2rx9tYZi0fZq_QT1N3AB_YwaK1WEgt75PkhL_3pVYqoSiV-Tt8hGUawP4N2SvUBPM5WJ8zSwIdY53afv28K7Ofv-uM8uQB5Z15ChzjG3FPrngJ8BJ8ntk455Fc/s906/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2021-06-03+002410.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="265" data-original-width="906" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqtveD5Z4dZwNbS02DC2rx9tYZi0fZq_QT1N3AB_YwaK1WEgt75PkhL_3pVYqoSiV-Tt8hGUawP4N2SvUBPM5WJ8zSwIdY53afv28K7Ofv-uM8uQB5Z15ChzjG3FPrngJ8BJ8ntk455Fc/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2021-06-03+002410.png" width="450" /></a></div>
<p>できていない場合は <code>appmesh-controller</code> ポッドのログを見れば何かわかるかもしれないのでチェックしてみてください。</p>
<hr />
<p>そこそこ長くなってきたのでここまでにしようと思います。<br />
ここまで作ったものだけではぜんぜんサービスメッシュになっていないのですが、続きは近いうちにまた書きます!</p>
<p>ちなみに、メッシュのリソース作成はkubernetes的にはカスタムリソースの作成になっています。<br />
そのカスタムリソースの定義は👇なので、yamlをどう書けばいいのかこちらを見つつやるのがいいのではないかと思います!</p>
<p><a href="https://github.com/aws/eks-charts/blob/master/stable/appmesh-controller/crds/crds.yaml">eks-charts/crds.yaml at master · aws/eks-charts</a></p>
<br />
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-13349562227599233852021-04-23T17:35:00.001+09:002021-04-23T17:35:36.646+09:00シタテル開発陣に聞いてみた:便利なデスクトップアプリ<p> 今回はちょっと新企画として、シタテルの開発部にアンケートをとって記事を書いてみようかと思います。</p>
<p>第1回は、「日々の開発業務で便利なデスクトップアプリを聞いてみた」です😁</p>
<p>初めての試みということもあり、まずはジャンル問わずで募集してみました。<br />早速コメントとともにまとめてみたいと思います。</p><span></span><span><a name='more'></a></span><p><br /></p>
<p><a href="https://www.meetsidekick.com/" rel="noopener" target="_blank">Sidekick</a></p>
<ul>
<li>説明<ul><li>Webブラウザ+アプリまとめアプリ</li></ul></li>
<li>特にいいと思うところ<br />Chromiumベースのブラウザとアプリ集約が一体化したようなアプリです。<br />縦にアプリ、横にブラウザタブが並んで管理できるので1ウィンドウで一覧性よく収まります。<br />1passwordなどのブラウザ拡張機能が使えるのがうれしいところで、StationやBiscuitなどとの一番の差別化ポイントだと思っています。</li>
</ul>
<p><a href="https://sequel-ace.com/" rel="noopener" target="_blank">Sequel Ace</a></p>
<ul>
<li>説明<ul><li>mysqlのGUIツール</li><li>Workbenchより軽快な気がする</li></ul></li>
</ul>
<p><a href="https://github.com/alacritty/alacritty" rel="noopener" target="_blank">Alacritty</a></p>
<ul>
<li>説明<ul><li>クロスプラットフォームのターミナルエミュレータ</li></ul></li>
<li>特にいいと思うところ<ul><li>GPUによる高速なレンダリング</li><li>YAMLファイルで設定できて、別のOSでも同じ設定が使えて便利</li></ul></li>
</ul>
<p><a href="https://www.jetbrains.com/ja-jp/ruby/" rel="noopener" target="_blank">RubyMine</a></p>
<ul>
<li>説明<ul><li>JetBrains製のRubyに特化したIDE</li></ul></li>
<li>特にいいと思うところ<ul><li>補完、コードジャンプ、検索など開発に必要な機能が特別な設定無しですぐ使える<br /></li></ul></li>
</ul>
<p><a href="https://gitup.co/" rel="noopener" target="_blank">GitUp</a></p>
<ul>
<li>説明<ul><li>とてもシンプルなGUIのGitクライアント</li></ul></li>
<li>特にいいと思うところ<ul><li>ツリーが見やすい</li><li>基本的なgitの操作はショートカットキーで素早くできる</li><li>(あまり複雑な機能はない代わりに)見た目がシンプル</li></ul></li>
</ul>
<span><br /><!--more--></span><p>いかがでしょうか。気になるものがあれば、ぜひリンクをチェックしてみてください🤲<br />実際聞いてみたところ、「これといったデスクトップアプリはあまり使っていない」「VSCodeでたいていのことができてしまう」といった感想が多く、思ったほど集まりませんでした🤔</p>
<p>コマンドラインツールやWebアプリ・Webサービスのほうがいろいろあるという声があったので、気を取り直して次回はそのあたりを集めてみようと思います!</p>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-25446938937063484702021-02-26T13:35:00.000+09:002021-02-26T13:35:31.748+09:00Node.jsのバージョン管理をVoltaに統一したわけ<p>Node.jsを使っていれば永遠のテーマである(?)バージョン管理ですが、皆さんはどのようにしているでしょうか。</p>
<p>今更ながら、ローカル環境で使用するバージョン管理を原則としてVoltaに統一する運びになったので、その背景などを書いてみたいと思います。</p><p><br /></p>
<hr />
<p>今までは特にバージョン管理ツールを標準で決めておらず、個人やチームでそれぞれ好きなものを使っているような状態でした。<br />
それはそれで大きく困るようなことはなかったのですが、先日多くのリポジトリにざっといくつかの設定・アップデートをして回るようなタスクがあって話が変わりました。</p>
<p>それぞれのリポジトリで使っているNodeのバージョンをどこからか見つけてきて切り替えする作業を1日に何度もやるのはやはり効率が悪いしストレスですね。<br />
バージョン管理ツールを決めて全社標準で設定してもらわないことには、私が 😇 してしまう・・・</p>
<p>ということで何のツールをツールを使うか検討を始めました。<br />
今回の悩みを解消するためには、要件はこんなところです。</p>
<ul>
<li>設定しておけばカレントディレクトリに応じて自動でNodeのバージョンが切り替わる</li>
<li>設定したバージョンをVCSに記録できる</li>
<li>npmのバージョンも記録して自動切換えできる</li>
</ul>
<p>ちなみに最後の条件はnpmのバージョン7がもう一般リリースされているので出てきたものです。<br />
<a href="https://github.blog/2021-02-02-npm-7-is-now-generally-available/">npm 7 is now generally available! - The GitHub Blog</a><br />
それまでのバージョン6とは機能面の破壊的変更とlockファイルの変更などがあるので、これも併せてバージョン管理しておきたくなったわけですね。</p><p><br /></p>
<hr />
<p>さて結論ですが、Voltaを使うことにしました。<br />
<a href="https://volta.sh/">Volta - The Hassle-Free JavaScript Tool Manager</a><br />
ある程度メジャーなツールで上の要件を満たすものとなるとVoltaしかなかったためです。</p>
<p>使うのも簡単でいい感じですね。<br />
Voltaの使い方については詳しい記事が各地にあるのでここではあまり書きませんが、例えばNode 14, npm 6を使うなら</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">$ volta install node@14 npm@6
$ volta pin node@14 npm@6
</code></pre></div>
<p>とすると package.json にバージョンが書き込まれます。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">{
...
"volta": {
"node": "14.15.5",
"npm": "6.14.11"
}
}
</code></pre></div>
<p>これをgitにコミットしておけば、自動でこのバージョンのnodeとnpmが実行されるようになります。<br />
全リポジトリで設定されればもうバージョンを気にしなくてよくなるわけですね🤗</p>
<p>Nodeはもちろん、npmやその他nodeツール類のバージョン管理をしっかりやりたいという方はVoltaを一度試してみてはいかがでしょうか💁♂️</p>Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-40688922744612338062021-01-15T17:07:00.001+09:002021-01-15T17:10:34.606+09:00GitHub dependabot でDockerタグをバージョン指定する<p>寒い日が続きますね。</p>
<p>ところで皆さんはGitHubのdependabotは使っていますでしょうか?<br />
リポジトリをスキャンして、使っているライブラリやパッケージの脆弱性をお知らせしてくれるサービスですね。</p>
<p><a href="https://docs.github.com/ja/free-pro-team@latest/github/administering-a-repository/keeping-your-dependencies-updated-automatically">依存関係を自動的に更新する - GitHub Docs</a></p>
<p>そのdependabotでちょっとした問題を調べて解決したので記録しておこうと思います。<br />
題して「特定のDockerタグを無視する設定の書き方」です。<br />
dependabotの概要や初期設定などはこの記事ではちょっと飛ばしますのでご了承ください🙇</p>
<hr />
<p>dependabotはDockerタグにも対応していて、脆弱性対応された新しいバージョンタグがある場合はそれを出すようになっています。<br />
例えばリポジトリの <code>docker/Dockerfile</code> をスキャン対象にする場合、コミットしておく設定ファイル <code>.github/dependabot.yml</code> はこのようになります。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">version: 2
updates:
- package-ecosystem: "docker"
directory: "/docker"
</code></pre></div>
<p>さて、基本的にはdependabotはメジャーバージョンアップも含めて最新バージョンにアップデートさせるように動作するようです。<br />
そのため、Dokerfileで <code>node:14.x.x</code> イメージを使っている場合</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC9pj59QFq9YO781LqN_09JIykH3BgU0-73XZhpwH48vDVoAJ9HrkaUOdaT1EuKw3co5fpWfNbS62n1dl6qGbz6Kx8ygvT8dV-3wH5WKo2IjOjSTUlpwkn0iNU4Oc15W5dlhDkCBBfBX4/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="80" data-original-width="748" height="40" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC9pj59QFq9YO781LqN_09JIykH3BgU0-73XZhpwH48vDVoAJ9HrkaUOdaT1EuKw3co5fpWfNbS62n1dl6qGbz6Kx8ygvT8dV-3wH5WKo2IjOjSTUlpwkn0iNU4Oc15W5dlhDkCBBfBX4/" width="320" /></a></div>
<p>このように <code>node:15</code> 系にアップデートしなよ!というPRが作成されるのです。</p>
<p>でもNode.jsはバージョン14はLTSで15はLTSじゃない・・・更新するなら14系の最新にしてほしい!<br />
というわけで、「Dockerタグの特定バージョンを無視する設定」を調べて入れてみました。</p>
<hr />
<p><a href="https://docs.github.com/ja/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#ignore">依存関係の更新の設定オプション - GitHub Docs</a></p>
<p>こちらのドキュメントによると、あるバージョンへの更新を無視するためにはdependabot.ymlにそのバージョンを明記すればいいのですが、</p>
<blockquote>
<p>範囲を定義する場合は、パッケージマネージャーの標準パターンを使用します</p>
</blockquote>
<p>Dockerタグの標準パターンって何だ・・・?🤔</p>
<p>結局わからなかったのでソースを見てみたところ、判定しているのはおそらくこのあたり。</p>
<p><a href="https://github.com/dependabot/dependabot-core/blob/main/docker/lib/dependabot/docker/update_checker.rb#L83">dependabot-core/update_checker.rb at main · dependabot/dependabot-core · GitHub</a></p>
<p>バージョンの取り扱いに使っているのは Gem::Versionクラス のようです。</p>
<p><a href="https://docs.ruby-lang.org/ja/latest/class/Gem=3a=3aVersion.html">class Gem::Version (Ruby 3.0.0 リファレンスマニュアル)</a></p>
<p>ということはGemfileでバージョンを指定するのと同じ記法で良さそうですね。<br />
dependabot.ymlはこうなりました。</p>
<div class="code-frame"><pre class="highlight"><code data-lang="plaintext">version: 2
updates:
- package-ecosystem: "docker"
directory: "/docker"
ignore:
- dependency-name: "node"
versions: ["~>15.0"]
# nodeイメージの 15.x 系は無視
</code></pre></div>
<p>これでnodeイメージは <code>15.x</code> を対象外にして更新するようになりました。<br />
新しく作られるPRは14系の範囲でバージョンアップするものになっていますね。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkZLL8ghDHCeY8LCKSTLGOhJmTSvHItCAewZmu4lVc2okRAugN3FUGpQOBKs5VRHF-1f6g7GI2uV2gHmBfeIkgcJvlsxwHE8snzMPSBbLZz7Cz6ZBFyZDzx1NtcQB8lVGOZ3lr6XzUNyI/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="74" data-original-width="693" height="40" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkZLL8ghDHCeY8LCKSTLGOhJmTSvHItCAewZmu4lVc2okRAugN3FUGpQOBKs5VRHF-1f6g7GI2uV2gHmBfeIkgcJvlsxwHE8snzMPSBbLZz7Cz6ZBFyZDzx1NtcQB8lVGOZ3lr6XzUNyI/" width="320" /></a></div>
<div><br /></div>dependabotでDocker更新、なかなか便利なのでお試しください🙏Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-83585282962207056912020-11-12T18:15:00.001+09:002020-11-12T18:18:20.263+09:00AWS ECRのライフサイクルポリシーを設定して自動クリーンアップ<p>今回は軽く、AWS ECR (Elastic Container Registry) の小ネタです。</p>
<p>AWSでコンテナを使って何かする場合ECRにコンテナイメージを保存しておくことが多いと思いますが、日ごろからイメージをよくビルド&プッシュしているとどんどんイメージが増えていきますね。</p>
<hr />
<p>イメージサイズがものすごく大きいとかでなければそれほど料金を食うわけでもないのですが、まず使わない古いイメージをずっと保存しておくこともないよなーと思ったのでそのあたりを設定してみました。</p>
<p>ECRではライフサイクルポリシーという機能があり、いろいろな条件を定めてイメージを自動クリーンアップすることができるんですね。<br />
<a href="https://aws.amazon.com/jp/blogs/news/clean-up-your-container-images-with-amazon-ecr-lifecycle-policies/" rel="nofollow" target="_blank">Amazon ECRのライフサイクルポリシーでコンテナイメージのクリーンアップ | Amazon Web Services ブログ</a></p>
<p>設定できる条件は</p>
<ul>
<li>タグ</li>
<li>イメージ数</li>
<li>プッシュされてからの経過日数</li></ul>を組み合わせることができます。<br /><ul>
</ul>
<p>今回は、「タグが <code>latest</code> ではない」かつ「プッシュされて90日以上」の条件に合うイメージを削除するポリシーを設定してみました。</p>
<p>AWSコンソールで設定する場合は、ECRのコンソールでリポジトリを選択して左のメニューの <code>Lifecycle Policy</code> をクリックすると設定画面があります。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhm4u3PP9dWx4c_ha5i-NTB-NOrE3I5tF1ZtKdbV1P9lzMBOIXTQLJqQ7Spee7puWGLCafC-sLcPplO-oywCqzmEpcCI6pA4JIM_xHDLhSnplnqyQ5LfbrxIRU4S4flWVchMHJWoAKrIrM/s851/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-11-11+164226.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="557" data-original-width="851" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhm4u3PP9dWx4c_ha5i-NTB-NOrE3I5tF1ZtKdbV1P9lzMBOIXTQLJqQ7Spee7puWGLCafC-sLcPplO-oywCqzmEpcCI6pA4JIM_xHDLhSnplnqyQ5LfbrxIRU4S4flWVchMHJWoAKrIrM/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-11-11+164226.png" width="320" /></a></div><br /><p>「テストルールの編集」ボタンを押すとポリシーを適用した結果が確認できる(実際に保存されているイメージには何もしない)テスト用ページに移動するので、まずはそこで試してみるのがいいですね。</p>
<p>さて、ライフサイクルポリシーは「ルール」を優先順位つきで好きな数設定することで構成します。ルール作成画面はこうなっていて、一致条件は「イメージをプッシュしてから」「次の数値を超えるイメージ数」が選べます。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRSiMLUbLARHzyuJKa_noCa4EXickYFpgcC8331NnkpIHSG_u8REgEhyphenhyphenxl0BqjRCQHru0xDlBhAf0bPqGtQOBNRDtdJzdmd3VGHqA1VZXkWluQFcSI7bops2x0TBR5_bUE7bknrGFoI9U/s758/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-11-11+164621.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="758" data-original-width="651" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRSiMLUbLARHzyuJKa_noCa4EXickYFpgcC8331NnkpIHSG_u8REgEhyphenhyphenxl0BqjRCQHru0xDlBhAf0bPqGtQOBNRDtdJzdmd3VGHqA1VZXkWluQFcSI7bops2x0TBR5_bUE7bknrGFoI9U/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-11-11+164621.png" /></a></div><br /><p>今回作りたい条件は「タグが <code>latest</code> ではない」かつ「プッシュされて90日以上」ということで、</p>
<ul>
<li>タグ付け済("latest")、次の数値を超えるイメージ数(1)</li>
<li>すべて、イメージをプッシュしてから(90日)</li>
</ul>
<p>という2つのルールを作ってこのようになります。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhajw9Qgon7x6qVkaC_b3L9WMebAl1auA3KtkRZlsLqH-eLdzV7KtDPFGem41m6UGIB8SK_LuPb0KKIuh8ywfBf9b80iBbghyphenhyphenr1WkYIbvEQGDAMK1LZ1jjN5PtD1A42dj_in4WNyov8NI8/s620/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-11-11+164748.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="216" data-original-width="620" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhajw9Qgon7x6qVkaC_b3L9WMebAl1auA3KtkRZlsLqH-eLdzV7KtDPFGem41m6UGIB8SK_LuPb0KKIuh8ywfBf9b80iBbghyphenhyphenr1WkYIbvEQGDAMK1LZ1jjN5PtD1A42dj_in4WNyov8NI8/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-11-11+164748.png" width="320" /></a></div><br /><p>ルールを作ればあとは自動でそれに従ってイメージが毎日掃除されるので、とても楽ですね😊</p>
<hr />
<p>ここまではコンソールで操作してきましたが、リポジトリが多い場合やコンソール面倒だと言う場合はもちろんCLIが使えます。</p>
<p><a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr/put-lifecycle-policy.html" rel="nofollow" target="_blank">put-lifecycle-policy — AWS CLI 2.1.0 Command Reference</a></p>
<p>まずは先ほどのルールをJSON化して <code>policy.json</code> とでもファイルを作ります。</p>
<div class="code-frame"><pre><code>{
"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"
}
}
]
}
</code></pre></div>
<p>今回は全リポジトリに適用したかったのでシェルスクリプトを作りました。</p>
<div><pre><code data-lang="shell">#!/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</code></pre></div>
<p>これで一括適用ができますね💪</p><p>比較的簡単に設定ができるので、「うわっ…ECRのイメージ、多すぎ…?」と気づいた際には設定してみてはいかがでしょうか。</p><p>ちなみにterraformだと<code>aws_ecr_lifecycle_policy</code>リソースで設定できるようです。</p>
<p><a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_lifecycle_policy" rel="nofollow" target="_blank">aws_ecr_lifecycle_policy | Resources | hashicorp/aws | Terraform Registry</a></p>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-79289125563167797352020-09-04T15:49:00.000+09:002020-09-04T15:49:40.869+09:00GCPの料金アラートをSlackに出してみる<p> AWSやGCPの料金、たまに気になりますよね。</p><p>そういえば今月どれくらい料金食ってるんだ?と思ったとき、もちろん各サービスのコンソールで課金管理のページを開けば確認できますね。<br />が、日常的にはお知らせが届くほうが簡単ですむよね、ということでslackに通知をさせてみました。</p><p>AWSは簡単だったのですがGCPはちょっと手間だったのでまとめておきたいと思います。</p><p>まあエンジニアとしては課金の具合を気にせず開発に集中できれば一番いいのですが、現実には会社の予算とかいろいろありますし私は課金周りの把握をする役回りもあるので、これはやっておきたいところです。<span></span></p><p><br /></p><a name='more'></a><p></p><h2 style="text-align: left;">1. Pub/Subトピック作成</h2><p>まずは適当なプロジェクトを用意し、<a href="https://console.cloud.google.com/cloudpubsub/topic/list" rel="nofollow" target="_blank">Pub/Sub</a> でトピックを作成します。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLs_uBkT5PD7YJSMjy0EVEE6FElt2sKmoqJTVfRq4TSEX4dUJi1cvsAiIPdCOFSsK-4oyLQ48EJvMOa7Q5iG6pfNTSoppbM8aWsiqTH0x0ehBTrXCQ7KP8wXKH5QnBdCZrmR5WzBHgyG8/s533/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+122729.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="434" data-original-width="533" height="278" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLs_uBkT5PD7YJSMjy0EVEE6FElt2sKmoqJTVfRq4TSEX4dUJi1cvsAiIPdCOFSsK-4oyLQ48EJvMOa7Q5iG6pfNTSoppbM8aWsiqTH0x0ehBTrXCQ7KP8wXKH5QnBdCZrmR5WzBHgyG8/w341-h278/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+122729.png" width="341" /></a></div><h2 style="text-align: left;">2.GCPで予算の作成</h2><p>次は予算の作成です。GCPのメニューから「お支払い」→「予算とアラート」と進み、予算の作成をクリック。</p><p><a href="https://cloud.google.com/billing/docs/how-to/budgets?hl=ja#create-budget" rel="nofollow" target="_blank">予算と予算アラートの設定 | Cloud Billing | Google Cloud</a></p><p></p><ul style="text-align: left;"><li>まずは料金を集計する対象を選択します。<br />プロジェクトやサービスを絞り込んだり、特定のラベルを付けたリソースだけを対象にすることもできます。<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOibdqG0En4JT8ZHxvIt05mh_GfTseY13U2e-msYpwsFczVfNj2Lv6Fp0xHeGo0u6hIqH7ihVo8WpdkytDhv_f8SX54w30tRWFO0MU7cNHUbG1F_XnEq5yu6SoPb2knlT1Usxwvpfkyxc/s553/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+123347.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="553" data-original-width="523" height="442" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOibdqG0En4JT8ZHxvIt05mh_GfTseY13U2e-msYpwsFczVfNj2Lv6Fp0xHeGo0u6hIqH7ihVo8WpdkytDhv_f8SX54w30tRWFO0MU7cNHUbG1F_XnEq5yu6SoPb2knlT1Usxwvpfkyxc/w418-h442/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+123347.png" width="418" /></a></li></ul><div><br /></div><ul style="text-align: left;"><li>予算は設定された額に対して実際の課金額が達したら通知されるようになっています。設定額は、「自由に設定」と「先月の額」から選べるんですね。<br />ちなみに5000兆円は設定できませんでした🥺<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOCaPP2ZgDuBEtA3aqxN8cRq012C6RrwckED20Wbv_QxMgF0RSsRB_YU6FSpTIc0nXOEMKjH6zhiPA2tQ6COx7Bhe70-hZH32FvJT-4wAT-TbGUcq7Pn3eYOs_zvw4eGbzphfX5xHEnNA/s530/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+123747.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="365" data-original-width="530" height="292" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOCaPP2ZgDuBEtA3aqxN8cRq012C6RrwckED20Wbv_QxMgF0RSsRB_YU6FSpTIc0nXOEMKjH6zhiPA2tQ6COx7Bhe70-hZH32FvJT-4wAT-TbGUcq7Pn3eYOs_zvw4eGbzphfX5xHEnNA/w424-h292/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+123747.png" width="424" /></a></div><br /></li></ul><p></p><p></p><ul style="text-align: left;"><li>最後に設定額の何%になった時点でアラート出すかのしきい値を設定します。実値or予測値で、パーセンテージは自由に設定できます。<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKtgAVecSbACHqRbzDggFnxqn8vGEFZ7VBVWFyUMmLavxnlrkfbWKCehZxcbVrqP9elUl70WI4Yyuz3F2XYm4aZ8ryZgTVRXmwUr8mw9VCBX_CviwXdS25Uo7GFSsa3B3KXQzz8zoAEIk/s699/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+124347.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="699" data-original-width="537" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKtgAVecSbACHqRbzDggFnxqn8vGEFZ7VBVWFyUMmLavxnlrkfbWKCehZxcbVrqP9elUl70WI4Yyuz3F2XYm4aZ8ryZgTVRXmwUr8mw9VCBX_CviwXdS25Uo7GFSsa3B3KXQzz8zoAEIk/s640/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-08-26+124347.png" /></a></div><br /></li></ul><p></p><p>あと、先ほど作成したPub/Subトピックを忘れずに接続しておきましょう。</p><p><br /></p><h2 style="text-align: left;">3. Slack App作成</h2><p>Slackに通知するために、Appを作成してトークンを発行します。</p><p><a href="https://cloud.google.com/billing/docs/how-to/notify?hl=ja#send_notifications_to_slack" rel="nofollow" target="_blank">コスト管理の自動レスポンスの例 | Cloud Billing | Google Cloud</a></p><p><a href="https://api.slack.com/apps" rel="nofollow" target="_blank">https://api.slack.com/apps</a> で <code>Create New App</code> をクリックし、適当なAPP名と導入したいワークスペース名を入力してアプリを作成します。</p><p style="text-align: center;"><img border="0" data-original-height="465" data-original-width="576" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8w415ntBOF7UJbe3R2pdJUdoh11wQY_lg_oKdPwHsgZPpMctqSnH_tA9pd06HjL7eaZy6t-lN1WuYvPdvvdvCkEbgn7S1po5lOi-rvTx_fbOROI2Kq9zhx813QFo4Q6GiHCRiH8UBGFE/w369-h298/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-02+184034.png" width="369" /></p><p>メニューの <code>OAuth & Permissions </code>を開き、 <code>Bot Token Scopes </code>に <code>chat:write</code> スコープを追加します。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhWd4CSja0BeZ3wPqxmxT9MzFyIZVqa8RG82pewaDnTJayVBBtJc6cT3a0lkt9kfZsPQFNqvenu6x8feHR1UaHKXGy0hACeZV5Wo8t3MaRQIvI7DzoPbKrS9W6Des2QMIT7mVMI_9eC60/s699/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-02+184134.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="699" height="264" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhWd4CSja0BeZ3wPqxmxT9MzFyIZVqa8RG82pewaDnTJayVBBtJc6cT3a0lkt9kfZsPQFNqvenu6x8feHR1UaHKXGy0hACeZV5Wo8t3MaRQIvI7DzoPbKrS9W6Des2QMIT7mVMI_9eC60/w512-h264/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-02+184134.png" width="512" /></a></div><br /><p style="clear: both; text-align: left;">ページ上部の <code>Install App to Workspace</code> をクリックし、ワークスペースにAppをインストールします。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBug7ryxvOAw8Rh1135TZT3SJAganj9wcCXrSVkoluvtpW597lV197UYcg1a8cfArXB3MK8Eyp3VJjdURmZtQucwcB7xIA_ta9NdBb7SZOF-ZrBb3F0UGxOW4SN_rhBD4x0C73gyws5M4/s699/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-02+184334.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="226" data-original-width="699" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBug7ryxvOAw8Rh1135TZT3SJAganj9wcCXrSVkoluvtpW597lV197UYcg1a8cfArXB3MK8Eyp3VJjdURmZtQucwcB7xIA_ta9NdBb7SZOF-ZrBb3F0UGxOW4SN_rhBD4x0C73gyws5M4/w512-h166/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-02+184334.png" width="512" /></a></div><p style="clear: both; text-align: left;">インストールして戻ってくれば、アクセストークンが得られます。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu3ME1iGH2ui748WhwPPcPdY-xOlmUUovccmqwlbOMqaRYi5uoGUkrV17afVCyuh8lw604QQ53aVYOq-uQcwIw4bASOBfwRh4lTVoehUAqozesv-SDMOM3sujtfQQykqKnUUtcfng8eSg/s693/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-02+184534.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="361" data-original-width="693" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu3ME1iGH2ui748WhwPPcPdY-xOlmUUovccmqwlbOMqaRYi5uoGUkrV17afVCyuh8lw604QQ53aVYOq-uQcwIw4bASOBfwRh4lTVoehUAqozesv-SDMOM3sujtfQQykqKnUUtcfng8eSg/w512-h266/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-02+184534.png" width="512" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><p></p><h2 style="text-align: left;">4. Function作成</h2><p>さてCloud Functionsを作ります。ここがメインディッシュです。</p><p>GCPの料金アラートは、常に1時間に3回程度送信される仕様なようです。なのでPub/Subで受け取った通知をすべてslackに出しているといらない通知ばかり頻繁に来てしまいます😇</p><p>そこでFirestoreをちょこっと使って、しきい値突破時の通知だけをslackに投げるようにFunctionを作ってみました。</p><p>まず、Pub/Subに送られてくるデータは一例として以下のようになってます。</p><p>(詳しくはドキュメントを参照してください)</p><p><a href="https://cloud.google.com/billing/docs/how-to/budgets-programmatic-notifications#notification_format" rel="nofollow" target="_blank">プログラムによる予算アラート通知を管理する | Cloud Billing | Google Cloud</a></p><div class="code-frame"><pre class="highlight"><code data-lang="plaintext">{
"budgetDisplayName": "sitateru",
"alertThresholdExceeded": 0.8,
"costAmount": 43210,
"costIntervalStart": "2020-08-01T07:00:00Z",
"budgetAmount": 50000,
"budgetAmountType": "SPECIFIED_AMOUNT",
"currencyCode": "JPY"
}
</code></pre></div><p></p><p>そこで、アルゴリズムとしては、</p><ul><li>Pub/Subから来たデータを確認</li><li>しきい値に達したときの通知であれば<br /><code>alertThresholdExceeded</code> か <code>forecastThresholdExceeded</code> というキーが含まれるので、キーの存在チェック</li><li><code>budgetDisplayName</code>, <code>alertThresholdExceeded</code>, <code>costIntervalStart</code>, <code>budgetAmountType</code> の値の組み合わせを見て、<ul><li>Firestore上にその値のセットのデータがなければslackで通知+Firestoreに値セットを保存</li><li>Firestore上にその値のセットのデータがあれば何もしない</li></ul></li></ul><p>というような設計にすればだいじょうぶだあ、と思いつつそこから少し調整してこのようなコードになりました。</p><div class="code-frame"><pre class="highlight"><code data-lang="plaintext">const slack = require('slack')
const Firestore = require('@google-cloud/firestore')
// slackbotのアクセストークン
const BOT_ACCESS_TOKEN = process.env.BOT_ACCESS_TOKEN
// 投稿先slackチャンネル名
const CHANNEL = process.env.SLACK_CHANNEL
// GCPの予算ページに飛べるようにURLを入れる
const BUDGET_URL = process.env.BUDGET_URL
const collection = new Firestore({
projectId: process.env.PROJECT_ID,
keyFilename: process.env.KEYFILE
}).collection('billing_alert_slack')
exports.billingAlertSlack = async (pubsubEvent, context) => {
const pubsubData = JSON.parse(Buffer.from(pubsubEvent.data, 'base64').toString())
const postSlack = async (fields) => {
const messageFields = Object.entries(fields).map(datum => {
return {
"title": datum[0],
"value": datum[1],
"short": true
}
})
await slack.chat.postMessage({
token: BOT_ACCESS_TOKEN,
channel: CHANNEL,
text: '',
attachments: [{
"color": "#1a73e8",
"title": "GCP Billing Alert",
"title_link": BUDGET_URL,
"fields": messageFields
}]
})
return 'Slack notification sent successfully'
}
if(pubsubData.hasOwnProperty("alertThresholdExceeded") || pubsubData.hasOwnProperty("forecastThresholdExceeded")) {
const ThresholdExceeded = pubsubData.hasOwnProperty("alertThresholdExceeded") ? pubsubData.alertThresholdExceeded : pubsubData.forecastThresholdExceeded
const querySnapshot = await collection.where('budgetDisplayName', '==', pubsubData.budgetDisplayName)
.where('ThresholdExceeded', '==', ThresholdExceeded)
.where('budgetAmountType', '==', pubsubData.budgetAmountType)
.get()
if (querySnapshot.docs.length) {
const data = querySnapshot.docs[0].data()
if (pubsubData.costIntervalStart != data.costIntervalStart) {
// update document
querySnapshot.docs[0].ref.update({ costIntervalStart: pubsubData.costIntervalStart })
// post slack
return await postSlack(pubsubData)
}
} else {
// add document
collection.add({
budgetDisplayName: pubsubData.budgetDisplayName,
ThresholdExceeded: ThresholdExceeded,
budgetAmountType: pubsubData.budgetAmountType,
costIntervalStart: pubsubData.costIntervalStart
})
// post slack
return await postSlack(pubsubData)
}
} else {
return 'Slack notification was not sent'
}
}
</code></pre></div><p></p><p>上のコードで出てきた環境変数ですが、</p><p></p><ul style="text-align: left;"><li> <code>BOT_ACCESS_TOKEN</code> 手順3で作ったSlack App用アクセストークン</li><li><code>SLACK_CHANNEL</code> 通知先のSlackチャンネル名</li><li><code>BUDGET_URL</code> Slackの通知からすぐ飛べるように、GCPの予算ページのURL</li></ul>を入れています。<div><br /><span><!--more--></span><div><p>ということでGCPの予算通知がSlackに送られてくるようになりました。<br />このような見た目になります。</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxvsgVln7lk10RcAZIQvh5hKs7BwYCnrHP22Aoaa_eBtbCdcAQEaeyxGPqVYidExAUDdgI0Yaly5TW27zK_wDueq60LuGfkWvTNaKi_ExOJ5TSEikB9NBd63KMMWNf1VG9tPm31jXKRDE/s565/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-04+011757.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="245" data-original-width="565" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxvsgVln7lk10RcAZIQvh5hKs7BwYCnrHP22Aoaa_eBtbCdcAQEaeyxGPqVYidExAUDdgI0Yaly5TW27zK_wDueq60LuGfkWvTNaKi_ExOJ5TSEikB9NBd63KMMWNf1VG9tPm31jXKRDE/w452-h196/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-09-04+011757.png" width="452" /></a></div><p></p><div>もう少し各項目の意味が分かりやすくなるように改良してもいいんじゃないかとは思いつつ、だいぶ課金の額を気にする作業が身近になったなあと感じています。</div></div></div>Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-17557003413269459282020-07-10T17:52:00.000+09:002020-07-10T17:52:27.944+09:00GCPでGitHub Actionsのセルフホストランナーを作った朝野です。少し前の話ですが、GitHubの料金が全体的に値下げされましたね。<br />
<br />
<a href="https://github.co.jp/pricing.html">https://github.co.jp/pricing.html</a><br />
<br />
Teamプランは1ユーザーあたり4ドルになったのですが、さりげなく同時にGitHub Actionsの利用時間(organizationのプライベートリポジトリ全体で)が月3000分まで、と減ってしまいました 😥 (前は10000分)<br />
以前の記事(<a href="https://tech-blog.sitateru.com/2020/02/github-actions-cicdcircleciactions-e2e.html">GitHub ActionsとCypressでサイト監視してみる|sitateru tech blog</a>)で紹介したようなCypressを使った監視をActionsで動かしていたので、月3000分は少ないなーと思って料金を見てみたところ・・・<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJhEBIsDLvN7qKfDAuw1bQkfxwLE0vTPN179c1YUnadcomuG_zL4yNjaXNlA9sa1hAEUrohvlJBrhiksrRNgvUA-kAdYNetdCsJepbjZErOagtNTy-C-oFUKeDGfo0dBTcqwRCw-0i5kk/s1600/image.png" imageanchor="1"><img border="0" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJhEBIsDLvN7qKfDAuw1bQkfxwLE0vTPN179c1YUnadcomuG_zL4yNjaXNlA9sa1hAEUrohvlJBrhiksrRNgvUA-kAdYNetdCsJepbjZErOagtNTy-C-oFUKeDGfo0dBTcqwRCw-0i5kk/s320/image.png" width="320" /></a><br />
<a href="https://github.co.jp/features/actions#pricing">https://github.co.jp/features/actions#pricing</a><br />
<br />
セルフホストはタダなのか!しかしセルフホストって何だ?🤔<br />
ということでやってみました。<br />
<br />
<a href="https://docs.github.com/ja/actions/hosting-your-own-runners/about-self-hosted-runners">セルフホストランナーについて - GitHub Docs</a><br />
<br />
セルフホストランナーというのは、GitHub Actionsを実行するマシンのことです。自分の管理下にあるサーバーやPCでActionsが実行できちゃいます。前述のように、セルフホストランナーで実行する分にはGitHubに対して課金は発生しません。<br />
<br />
・・・おっ、ということはGCPの永久無料枠のCompute Engineインスタンスを使えばタダで回し放題やんけ!( ^ω^)<br />
<a href="https://cloud.google.com/free/docs/gcp-free-tier?hl=ja#always-free-usage-limits">Google Cloud の無料枠 | Google Cloud Platform の無料枠</a><br />
<br />
と思ったのですが、先に結論を書きます、コンテナ上でCypressテストを実行するには無料枠の <code>f1-micro</code> では処理能力的に無理でした。もっと軽い処理なら足りるのかもしれませんが・・・<br />
<code>e2-micro</code> ならなんとか動いたのでこれで良しとしましょう。<br />
<br />
さて、セルフホストランナーをクラウド上のLinuxインスタンスで構築する手順に行きたいと思います。<br />
<a href="https://docs.github.com/ja/actions/hosting-your-own-runners/adding-self-hosted-runners">セルフホストランナーの追加 - GitHub Docs</a><br />
<br />
organizationで使えるランナーを追加するには、 まず<code>https://github.com/organizations/{organization名}/settings/actions</code> で <code>Add Runner</code> をクリック。するとインストールするコマンドが出てくるので、それを実行すればOKです。簡単ですね。<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWbHcIuS6YO5JMHcLKEqtl1t9j0v1wNrOsT-8ZKXrsDr641u5QQaCCJdH_I6aOMCQomkpoJS7Oi6GBYTRCCT8j2db8v7f-WvyesZE7wV1Jxe8r-TLVGhtvosqZrGII77RipXRy3X4nwks/s1600/2020-06-30+154047.png" imageanchor="1"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWbHcIuS6YO5JMHcLKEqtl1t9j0v1wNrOsT-8ZKXrsDr641u5QQaCCJdH_I6aOMCQomkpoJS7Oi6GBYTRCCT8j2db8v7f-WvyesZE7wV1Jxe8r-TLVGhtvosqZrGII77RipXRy3X4nwks/s320/2020-06-30+154047.png" width="314" /></a><br />
<br />
インストールコマンドを実行するとこんな感じです。AAがいいですね。<br />
途中でこのランナーにつける名前やラベルを聞かれますが、とりあえずは初期値でも大丈夫そうです。ランナーを複数登録する想定ならわかりやすいものをつけるとよさそうですね。<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOICZzEV2s76gdztRE69jk8ozNOlAbT9HLQIOoT5hjKqJ_puEeq0KGTGP2zHt1_y8nzLAFePtZ59kxoCgWLyZuecc8Dy74cl1aFJOz2cmdR2xRsg7v35XhSXSGJlvMKvK5jBdG0OLMzCg/s1600/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-06-30+154455.png" imageanchor="1"><img border="0" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOICZzEV2s76gdztRE69jk8ozNOlAbT9HLQIOoT5hjKqJ_puEeq0KGTGP2zHt1_y8nzLAFePtZ59kxoCgWLyZuecc8Dy74cl1aFJOz2cmdR2xRsg7v35XhSXSGJlvMKvK5jBdG0OLMzCg/s320/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-06-30+154455.png" width="320" /></a><br />
ちなみに、DockerイメージをAction上で使う場合はランナーマシンにDockerをインストールしておく必要があります。<br />
<br />
インストール後、ランナーを起動するにはコマンドで起動する方法とサービスとして動かす方法があります。<br />
コマンドの場合はランナーのパッケージ内の <code>run.sh</code> を実行するだけ。<br />
サービスの場合は下ドキュメントにありますが<br />
<br />
<ul>
<li><code>svc.sh</code> でサービスを実行する</li>
<li><code>actions-runner/bin/actions.runner.service.template</code> ファイルを編集して <code>/etc/systemd/system/</code> あたりにうまいこと設置して systemctl でコントロールする</li>
</ul>
といった方法があります。<br />
今回は後者の方法で、<code>/etc/systemd/system/actions.service</code> を設置しました。<br />
<div class="code-frame">
<pre class="highlight"><code data-lang="plaintext">[Unit]
Description=GitHub Actions Runner
After=network.target
[Service]
ExecStart=/usr/local/actions-runner/runsvc.sh
User=dev
WorkingDirectory=/usr/local/actions-runner
KillMode=process
KillSignal=SIGTERM
TimeoutStopSec=5min
[Install]
WantedBy=multi-user.target</code></pre>
</div>
<a href="https://docs.github.com/ja/actions/hosting-your-own-runners/configuring-the-self-hosted-runner-application-as-a-service">セルフホストランナーアプリケーションをサービスとして設定する - GitHub Docs</a><br />
<br />
インストールと起動がうまくいけば、このようにGitHubのページ上にも出てきます。<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTD3oXCiNToFIbA9Hdq6SmiggcteR9-6kOxsSNvB30FJxgk-RXkjW8Ngs_aOuBu9-ab6SFZMBBI15UKZEs7lcRzvVvDx_iX2VtjAjSau4YN-G89g8-C8yU3Ip0utxYqJB6czX7TX_YIwk/s1600/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-07-07+154956.png" imageanchor="1"><img border="0" height="108" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTD3oXCiNToFIbA9Hdq6SmiggcteR9-6kOxsSNvB30FJxgk-RXkjW8Ngs_aOuBu9-ab6SFZMBBI15UKZEs7lcRzvVvDx_iX2VtjAjSau4YN-G89g8-C8yU3Ip0utxYqJB6czX7TX_YIwk/s320/%25E3%2582%25B3%25E3%2583%25A1%25E3%2583%25B3%25E3%2583%2588+2020-07-07+154956.png" width="320" /></a><br />
<br />
Actionsをセルフホストランナーで実行するには、ワークフローのyamlで以下のように書きます。<br />
<div class="code-frame">
<pre class="highlight"><code data-lang="yaml"><span class="na">runs-on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">self-hosted</span><span class="pi">,</span> <span class="nv">linux</span><span class="pi">,</span> <span class="nv">x64</span><span class="pi">]</span></code></pre>
</div>
同じOS&アーキテクチャのランナーが複数ある時はラベルをつければよさそうです。<br />
<a href="https://docs.github.com/ja/actions/hosting-your-own-runners/using-self-hosted-runners-in-a-workflow">ワークフローでのセルフホストランナーの利用 - GitHub Docs</a>
<br />
<br />
このセルフホストランナー、今回やってみたようにサーバ上で常時稼働してもいいですが、ちょっとactionsを動かしていろいろデバッグとかしたいけど使用量の枠を食うのがやだなあ・・・というときにも便利そうですね。開発用マシンでその時だけランナーを動かすということもできるので。<br />
<br />
<hr />
<br />
なおセルフホストランナーですが、セキュリティ上の理由でパブリックリポジトリでは利用しないようにと公式ドキュメントに書かれています。<br />
パブリックリポジトリではActionsが無料で使えるようなので、そちらでやればよさそうですね。Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-24389438101122104022020-04-30T17:38:00.000+09:002020-04-30T17:38:01.257+09:00Flood Element でブラウザベースの負荷テストどうも朝野です。<br />
少し前に負荷テストツールの<a href="https://element.flood.io/">Flood Element</a>というものを使ってみたので、紹介しようと思います。<br />
<br />
まずはCLIをインストールします。npmかHomeBrewでインストールできます。<br />
<code>$ npm install -g @flood/element-cli</code><br />
<code>$ brew install flood-io/taps/element</code><br />
<br />
CLIを使ってプロジェクトを作成、テストファイルを追加します。<br />
<code>$ element init</code><br />
<code>$ element generate some-test.ts</code><br />
<br />
テストファイルにはブラウザ上での操作を書いていきます。elementは<a href="https://github.com/puppeteer/puppeteer">puppeteer</a>を利用しているので、書き方はかなりpuppeteerに近いですね。<br />
googleのトップページにアクセスして"flood element"と検索するならこのようなコードになります。<br />
<div class="code-frame">
<pre class="highlight"><code data-lang="typescript">import { step, TestSettings, By, Until } from '@flood/element'
export const settings: TestSettings = {
// userAgent: 'flood-chrome-test',
loopCount: 10,
screenshotOnFailure: true,
clearCache: true,
clearCookies: true,
actionDelay: 1,
stepDelay: 1,
waitTimeout: 180
}
export default () => {
step('Sample', async browser => {
await browser.visit('https://google.com')
await browser.click(By.css('form input[type="text"]'))
await browser.sendKeys('flood element', Key.ENTER)
await browser.wait(Until.elementIsVisible(By.css('div#search')))
await browser.takeScreenshot()
})
}
</code></pre>
</div>
ローカルで実行するときは<br />
<code>$ element run some-test.ts</code><br />
でOKです。<br />
スクリーンショット等の実行結果は、 <code>tmp/element-results/<テストファイル名>/<実行タイムスタンプ></code> 以下に記録されます。<br />
<br />
ファイル前半で定義しているのは動作時の設定なのですが、そのあたり他詳しいことは<a href="https://element.flood.io/docs/1.0/get-started">公式ドキュメント</a>を参照してください。<br />
<br />
<hr />
さて、テストファイルができたら負荷テストを実行してみます。負荷テストSaaSのFloodの出番です。<br />
<a href="https://flood.io/">Scalable software starts here - Flood</a><br />
<br />
elementはFloodがテストを書くために作ったライブラリなんですね。<br />
ログインしたら"Stream"タブの"CREATE STREAM"をクリック、先ほどのテストファイルをアップロードします。<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk0vrijwf4S275HGnFCSA2mBAFDw8-kS92XD-zcf5oA_DalKmNg6KtFAd6TnnDQxfdOok1x7cWNPe1H7kcAbl-LS0Da7-1k69puaccOJEejwxCAEeT_etJwIt6bCXMbio9lH22TColGGs/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-04-23+17.03.09.png" imageanchor="1"><img border="0" height="259" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk0vrijwf4S275HGnFCSA2mBAFDw8-kS92XD-zcf5oA_DalKmNg6KtFAd6TnnDQxfdOok1x7cWNPe1H7kcAbl-LS0Da7-1k69puaccOJEejwxCAEeT_etJwIt6bCXMbio9lH22TColGGs/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-04-23+17.03.09.png" width="320" /></a><br />
<br />
"CONFIGURE LAUNCH"をクリックすると実行設定に移ります。<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid9Vlqk3MDAo_wT0SJ0V5oLsKDCn_ALbsB2lUfExRFsE5NH-uN57Ase50sk7zLkmg2BU0m4WdBxQUIO4c1qFfOdoTyDabVttILegNREMrKIh73s6j5YJa-kLmaiKnQtupCl3JBIkZRhj0/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-04-23+17.07.43.png" imageanchor="1"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid9Vlqk3MDAo_wT0SJ0V5oLsKDCn_ALbsB2lUfExRFsE5NH-uN57Ase50sk7zLkmg2BU0m4WdBxQUIO4c1qFfOdoTyDabVttILegNREMrKIh73s6j5YJa-kLmaiKnQtupCl3JBIkZRhj0/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-04-23+17.07.43.png" width="320" /></a><br />
テストファイルを実行するリージョンをまず選びましょう。<br />
"Users per Region"がリージョンごとの並列実行数で、"Duration"は実行時間の上限のようです。(試した限りでは、設定したDurationより早くテストファイルの実行が終わったらその時点でテスト完了になりました)<br />
<br />
あとは"LAUNCH TES"を押せば負荷テストが始まります。<br />
結果はこのようにグラフで見られます。<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKU_dxMDOHbeRd7xVq26p6RSzmGWRcZzUrAxj7bQsTT7w8BmF8blGpC9SOls_Gu78ps9-mQ5cnbg9_PyUHMP25y9CLRMtosmJ8tyxt_YXNu0ZJ6nMYeVgFb16C35gq8RiQsL6FLq6xuTI/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-04-23+17.14.30.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKU_dxMDOHbeRd7xVq26p6RSzmGWRcZzUrAxj7bQsTT7w8BmF8blGpC9SOls_Gu78ps9-mQ5cnbg9_PyUHMP25y9CLRMtosmJ8tyxt_YXNu0ZJ6nMYeVgFb16C35gq8RiQsL6FLq6xuTI/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2020-04-23+17.14.30.png" data-original-width="1600" data-original-height="1011" /></a>
<br />
<hr />
ちなみにFloodの料金ですが、VUHという単位の使用量によって決まります。<br />
<a href="https://flood.io/pricing">Flood Load Testing Pricing</a><br />
VUH = Virtual User Hourであり、 (実行時に設定したUser数) x (テストを実行した時間(15分単位)) というものです。例えば200ユーザーで30分間(=0.5時間)動かしたら 200x0.5=100VUH 、という要領ですね。<br />
<br />
ひと月あたり500VUHまでは無料で、それ以降は500VUHごとに$22程度かかる従量課金制になっています。うっかり使いすぎないよう気をつけないといけないですね。<br />
<br />
<hr />
実際のブラウザアクセスを想定した負荷テストって難しそうだなと思っていたのですが、これで無料でもちょっとした実験はすることができました。<br />
<br />
好きなサイトに大量のアクセスをかけることができてしまうので悪用厳禁なツールですが、簡単な負荷テストやってみたいという際には試してみてはいかがでしょうか。<br />
<br />Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-46278093137010488752020-03-19T12:46:00.000+09:002020-03-19T12:46:03.940+09:00Cypress on GitHub Actions のいろいろなテクニック朝野です。<br />
先日<a href="https://tech-blog.sitateru.com/2020/02/github-actions-cicdcircleciactions-e2e.html" target="_blank">CypressとGitHubを使う記事</a>を書いたのですが、今回はもう少し突っ込んだ話として、ActionsでCypressを使う上で引っかかったところなどをまとめてみたいと思います。<br />
<h2>
<a class="anchor" href="https://www.blogger.com/blogger.g?blogID=1797929185044797553#cypress%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E3%81%8C%E6%AD%A2%E3%81%BE%E3%81%A3%E3%81%9F%E3%81%BE%E3%81%BE%E3%81%AB%E3%81%AA%E3%82%8B" id="cypress%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E3%81%8C%E6%AD%A2%E3%81%BE%E3%81%A3%E3%81%9F%E3%81%BE%E3%81%BE%E3%81%AB%E3%81%AA%E3%82%8B"><i class="fa fa-link"></i></a>Cypressのテストが止まったままになる??</h2>
Dockerコンテナ上でCypress(ブラウザはChrome)を実行していると途中で止まってしまうことがあります。<br />
調べてみたところChromeの共有メモリが足りなくなっているようです。<br />
そんなときは cypress/plugin/index.js にこの記述で直りました。<br />
<div class="code-frame">
<pre class="highlight"><code data-lang="javascript"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">(</span><span class="nx">on</span><span class="p">,</span> <span class="nx">config</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">before:browser:launch</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">browser</span><span class="p">,</span> <span class="nx">launchOptions</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">browser</span><span class="p">.</span><span class="nx">name</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">chrome</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">launchOptions</span><span class="p">.</span><span class="nx">args</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="dl">'</span><span class="s1">--disable-dev-shm-usage</span><span class="dl">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">launchOptions</span>
<span class="p">})</span>
<span class="p">}</span>
</code></pre>
</div>
Chromeの起動時に <code>--disable-dev-shm-usage</code> オプションをつけているのですが、これによって共有メモリファイルを <code>/tmp</code> 以下に配置するので充分な容量が確保できるということだそうです。<br />
<h2>
<a class="anchor" href="https://www.blogger.com/blogger.g?blogID=1797929185044797553#hover" id="hover"><i class="fa fa-link"></i></a>:hover</h2>
CypressにはCSSの:hover状態を発生させる機能が無いようです🙄<br />
<a href="https://docs.cypress.io/api/commands/hover.html">hover | Cypress Documentation</a><br />
<blockquote>
If cy.hover() is used, an error will display and redirect you to this page.</blockquote>
Cypressでは非表示状態の要素はクリックなどの操作ができないのですが、じゃあ:hover時に表示される要素をクリックしたいときはどうするの?という話になりますね。<br />
上記のドキュメントでは<br />
<div class="code-frame">
<pre class="highlight"><code data-lang="javascript"><span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">.hidden</span><span class="dl">'</span><span class="p">).</span><span class="nx">invoke</span><span class="p">(</span><span class="dl">'</span><span class="s1">show</span><span class="dl">'</span><span class="p">).</span><span class="nx">click</span><span class="p">()</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">.hidden</span><span class="dl">'</span><span class="p">).</span><span class="nx">click</span><span class="p">({</span> <span class="na">force</span><span class="p">:</span> <span class="kc">true</span> <span class="p">})</span>
</code></pre>
</div>
という方法が紹介されています。<br />
ちょっと試したところ <code>invoke('show')</code> はうまくいかなかったのですが <code>click({ force: true })</code> はできたので、これでいいかなということにしました。<br />
<h2>
<a class="anchor" href="https://www.blogger.com/blogger.g?blogID=1797929185044797553#xpath" id="xpath"><i class="fa fa-link"></i></a>XPath</h2>
ページに表示されているテキストを検索して要素を選択したいときなどにはXPathを使いたくなりますが、その場合はプラグインを追加するという方法になります。<br />
package.jsonに <a href="https://www.npmjs.com/package/cypress-xpath">cypress-xpath</a> を追加し、cypress/support/index.js に<br />
<div class="code-frame">
<pre class="highlight"><code data-lang="javascript"><span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">cypress-xpath</span><span class="dl">'</span><span class="p">)</span>
</code></pre>
</div>
と1行加えればOKです。<br />
<code>cy.xpath('//div[contains(text(), "ここをクリック")]')</code> のように要素を取得できます。<br />
<h2>
<a class="anchor" href="https://www.blogger.com/blogger.g?blogID=1797929185044797553#eslint" id="eslint"><i class="fa fa-link"></i></a>ESLint</h2>
ESLintを使っている場合、Cypressのコードはそのままだと引っかかってエラーが出てしまいます。<br />
そんなときはlintのcypressプラグインを使いましょう。<br />
<a href="https://www.npmjs.com/package/eslint-plugin-cypress">eslint-plugin-cypress</a> を追加し、cypress/.eslintrc.jsonを作成します。<br />
最低限の設定をするならこんなもんです。<br />
<div class="code-frame">
<pre class="highlight"><code data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nl">"plugins"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"cypress"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"env"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"cypress/globals"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"extends"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"plugin:cypress/recommended"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
</div>
<h2>
<a class="anchor" href="https://www.blogger.com/blogger.g?blogID=1797929185044797553#cypress%E3%81%A0%E3%81%91%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B" id="cypress%E3%81%A0%E3%81%91%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B"><i class="fa fa-link"></i></a>Cypressだけインストールする</h2>
これはCypressではなくnpmのテクニックなのですが、Actions内でCypressを実行するだけなら、Cypress以外のnpmパッケージをインストールする必要はないですよね。<br />
package.jsonやpackage-lock.jsonの中身を無視してCypressだけインストールするならこうする手もあります。<br />
<br />
<code>$ npm install cypress@4.1.0 --no-save --no-package-lock</code><br />
<a href="https://docs.npmjs.com/cli-commands/install.html">npm-install | npm Documentation</a><br />
<br />
本当はnode_modulesをキャッシュしたいんですが、<br />
<a href="https://help.github.com/ja/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows">依存関係をキャッシュしてワークフローのスピードを上げる - GitHub ヘルプ</a><br />
によると<br />
<blockquote>
pull_requestのclosedイベントの場合を除く、push及びpull_requestイベントで起動されたワークフロー内のキャッシュにのみアクセスできます。</blockquote>
ということなので、スケジュール実行だとできないのです・・・<br />
<br />
<hr />
<br />
というわけで、CypressとActions関係のさまざまなTipsを書いてみました。<br />
何かの参考になれば幸いです。Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-17998912281531468182020-02-05T18:18:00.000+09:002020-02-05T18:46:51.191+09:00GitHub ActionsとCypressでサイト監視してみるこんにちは、朝野です。<br />
<br />
皆さんは<a href="https://github.co.jp/features/actions">Github Actions</a>使ってますか?<br />
シタテルではCI/CDはCircleCIを使っているのですが、せっかくなのでActionsも何かに使いたいなあと思って、簡単なサイト監視のようなものを作ってみたのでそのことを書いてみます。<br />
<br />
監視に使うのは、E2Eテストフレームワークの<a href="https://www.cypress.io/">Cypress</a>です。<br />
ブラウザの自動化やテスト用に使っている方も多いのではないかと思います。<br />
サクッと動かすなら<br />
<ul>
<li>テストコードを書いて <code>cypress/integration/</code> ディレクトリに入れておく</li>
<li>npmでcypressをインストール</li>
<li>
<code>$ npx cypress run</code> で実行</li>
</ul>
と、なかなかお手軽です。<br />
<br />
たとえば <a href="https://sitateru.com/">https://sitateru.com/</a> のページが表示されることをチェックするコードをCypressで書くとこのようになります。<br />
<pre><code>describe('Check page', function() {
it('top', function() {
// URLを開く
cy.visit('https://sitateru.com/')
// ページタイトルをチェック
cy.title().should('eq', 'sitateru - シタテル - | その服は、つくれる。')
// ページ内に特定の文字列があることを確認する
cy.contains('かんたん無料登録')
// CSSセレクタを使って、要素が存在することを確認する
cy.get('.top-main-visual').should('exist')
})
})
</code></pre>
<br />
また、ログイン操作であればこんなふうになります。<br />
<pre><code>describe('Login', function() {
it('login', function() {
cy.visit('https://atelier.sitateru.com/login')
// フォームに入力
cy.get('#email').type(Cypress.env('LOGIN_EMAIL'))
cy.get('#password').type(Cypress.env('LOGIN_PASSWORD'))
// ログインボタンをクリック
cy.get('input[type="submit"]').click()
// 遷移先のURLパスをチェック
cy.location('pathname').should('eq', '/topics')
})
})
</code></pre>
パスワードなどの情報は環境変数に入れておくのがいいですね。<br />
<code>CYPRESS_SOMEENV</code> のように CYPRESS_ プレフィックスをつけた環境変数を定義しておくと、コード内で <code>Cypress.env('SOMEENV')</code> と書いて呼び出すことができます。<br />
<br />
<hr />
GitHub Actionsは <code>.github/workflows/</code> にやりたいことを書いたyamlファイルを置けば実行されるので簡単ですね。<br />
ざっと作ってみたのがこちら。<br />
<pre><code>name: e2e test
on:
# 毎時0分に実行
schedule:
- cron: '0 * * * *'
jobs:
build:
name: monitoring
runs-on: ubuntu-latest
# cypress公式のDockerイメージを使う
container:
image: cypress/browsers:node12.14.0-chrome79-ff71
steps:
- name: checkout
uses: actions/checkout@master
with:
ref: 'master'
- name: install
run: npm install
- name: run test
env:
LANG: "ja_JP.UTF-8"
CYPRESS_LOGIN_EMAIL: ${{ secrets.CYPRESS_LOGIN_EMAIL }}
CYPRESS_LOGIN_PASSWORD: ${{ secrets.CYPRESS_LOGIN_PASSWORD }}
run: npx cypress run --browser chrome
# 失敗したときはスクリーンショットをダウンロードできるように
- name: gather artifact on failure
uses: actions/upload-artifact@v1
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
# 結果をslackに通知 (失敗したときはメンション)
- name: post to slack
uses: homoluctus/slatify@master
if: always()
with:
type: ${{ job.status }}
job_name: 'e2e test'
mention: 'here'
mention_if: 'failure'
channel: '#notification-actions'
icon_emoji: ':github:'
url: ${{ secrets.SLACK_WEBHOOK_URL }}
</code></pre>
<br />
実行時の環境変数は <code>env: </code> で定義できます。<br />
yaml内に直接書きたくないパスワードなどは、<a href="https://github.com/USERNAME/REPOSITORY/settings/secrets">https://github.com/USERNAME/REPOSITORY/settings/secrets</a> で登録しておけば、<br />
<code>${{ secrets.SECRET_NAME }}</code> で参照することができます。<br />
<br />
このyamlでは、ソースコードをチェックアウトしてcypressをインストール&実行した後、失敗した場合のスクリーンショットを見られるように<a href="https://github.com/actions/upload-artifact">upload-artifact</a>、結果をslackに送る<a href="https://github.com/homoluctus/slatify">slatify</a>を使ってみました。<br />
<br />
また、Actionsでテストを実行すると、何もしなければブラウザのデフォルト言語が英語になるようです。今回は日本語表示を監視したかったので、環境変数LANGで日本語を設定しています。<br />
<br />
<hr />
というわけで、1時間に一度サイトをチェックすることができるようになりました。<br />
slackに監視OKのメッセージが来るのがちょっとした安心感がありますね。<br />
<br />
ここで紹介したコードはだいぶ基本的な部分で現在はもう少しいろいろ入れて運用していますが、さらに高度なレベルに持っていきたいですね💪🏻!<br />
<br />Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-25858679811862167132019-12-19T16:38:00.000+09:002020-02-05T15:33:51.930+09:00安全にkubectl applyするコマンドを作ったどうも朝野です。<br />
最近 <a href="https://qiita.com/advent-calendar/2019/yarakashi-production">本番環境でやらかしちゃった人 Advent Calendar</a> が話題ですね。
私も楽しく読ませてもらっています。<br />
<br />
かくいう私も先日、kubernetes環境で作成中のアプリケーションを間違って別アプリケーション用のnamespaceにデプロイしてしまうという事故をやってしまいました😇<br />
<br />
何をどうミスしたかというと、<br />
<ul>
<li>新しく作るnamespace以下にkubectl applyでアプリケーションをデプロイしようとした</li>
<li>対象のyamlファイルは別プロジェクトのソースファイルをコピーして作ったものだった</li>
<li>yaml内に書かれているapply先namespaceが別プロジェクトのnamespaceのままだった</li>
</ul>
ということだったのです。<br />
普段kubernetes環境へのapplyは原則CircleCI内で行っているのですが、今回は環境構築中なアプリケーションだったので手動で実行しようとしていたのでした。<br />
<br />
<div>
上書きしてしまったほうもデモ用環境のようなもので利用者もいなかったため、大事には至らなかったのは不幸中の幸いでした。<br />
実行前に確認しなかった私が悪いのですが、これは何とかして防げるようにしたいなあということで、applyする前にyaml内のnamespaceをチェックしておかしかったら止める、というシェルスクリプトを作りました。<br />
<br />
作るにあたっては以下のような前提があります。<br />
<ul>
<li>環境の切り替え管理にkustomizeを使っている</li>
<li>各環境のyamlは k8s/overlays/{環境名} ディレクトリに置いて、<code>kubectl apply -k k8s/overlays/{環境名}</code>でデプロイ</li>
<li>デプロイ先namespaceは {リポジトリ名}-{環境名} に統一する</li>
</ul>
というわけで作ってみたのがこちら。<br />
<br />
<pre><code>#!/bin/sh
# check whether namespace is {repository}-{env} correctly or not
kubectl kustomize $1 > /dev/null 2> /dev/null
if [ $? != 0 ]; then
/bin/echo 'usage: kubeapply [kustomize_dir]'
exit 1
fi
PROCEED=0
REPOSITORY_NAME=$(git rev-parse --show-toplevel | rev | cut -f1 -d'/' | rev)
NAMESPACES=$(kubectl kustomize $1 | grep namespace | sed -E 's/namespace:(.*)/\1/' | tr -d ' ' | sort | uniq)
OVERLAY_DIR=$(echo $1/ | tr -s '/' | rev | cut -f2 -d'/' | rev)
FOUND=()
for NAMESPACE in $NAMESPACES
do
if [ ${NAMESPACE} != ${REPOSITORY_NAME}-${OVERLAY_DIR} ]; then
FOUND+=("${NAMESPACE}")
PROCEED=0
else
PROCEED=1
fi
done
if [ ${PROCEED} = 0 ]; then
/bin/echo "expected namespace is '${REPOSITORY_NAME}-${OVERLAY_DIR}'"
/bin/echo "but, namaspace '${FOUND[@]}' found in kustomize"
/bin/echo -n "proceed apply? (yes/No): "
read ANSWER
if [ ${ANSWER} = 'yes' ]; then
PROCEED=1
fi
fi
if [ ${PROCEED} = 1 ]; then
kubectl apply -k $@
else
echo "stop applying"
fi
</code></pre>
<br />
これを私はkubeapplyという名前にしてPATHが通っているところに置いてます。<br />
<code>$ kubeapply k8s/overlays/dev</code> のように実行すればOKです。<br />
<br />
やっていることは、<br />
<ol>
<li>引数がkustomizeディレクトリとして正しいかチェック</li>
<li>kustomize結果内のnamespaceを抽出</li>
<li>抽出されたnamespaceが{リポジトリ名}-{環境名(=引数のディレクトリ名)}と一致しているか確認</li>
<li>すべて一致していれば <code>kubectl apply -k {引数}</code> を実行</li>
<li>一致していないものがあればプロンプトを表示、yesと回答したらapplyを実行</li>
</ol>
<div>
というフローとなります。</div>
<div>
<br /></div>
<div>
力技っぽさもある気がしますが、これで同じようなパターンの事故はそうそう再発しなくなるので、一歩前進です🤜🏻</div>
<div>
<br /></div>
<div>
kubernetesはyamlを食わせるだけでアプリケーションに必要なものが一式出来上がるのが便利だなあとずっと思っているのですが、その分気軽に環境を吹き飛ばすこともできてしまうので注意して作業しないといけないですね。<br />
<br /></div>
</div>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-5378774746899166272019-10-11T17:50:00.001+09:002019-10-11T17:50:35.586+09:00GASでG Suiteグループ追加をやってみたどうも朝野です。<br />
<br />
以前の記事で、作業を楽にするためにRPAを触ってみたという話を書いたのですが<br />
その関係の取り組みの一つとして現在Google Apps Script(GAS)でアカウント管理操作を集約・省力化するという作業を進めています。<br />
<br />
Google Apps Scriptはご存知の方も多いと思いますが、Googleの様々なサービスと連携させて動かすことができるスクリプト言語です。<br />
<br />
シタテルではG Suiteをバリバリ使っているので、アカウント管理システムもGASを中心として作っていこうと考えています。<br />
そこで、まずはGASで何か作ってみる第1号として、ユーザーをグループに追加するフォームを作ってみました。<br />
グループ追加はG Suiteの管理画面からできるのですが若干作業として面倒だったりするんですね。<br />
<br />
というわけでまずはGoogleフォームを作成。<br />
「追加するユーザー」「追加先グループ」の入力欄を作ります。<br />
<span style="background-color: white; font-family: "roboto" , "robotodraft" , "helvetica" , "arial" , sans-serif;">それからオプション設定の「メールアドレスを収集する」をチェック。</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb0pqIft6_F5CzEGvi6PjQpron3EZKr4sVQEkHftMnCSrrhPQZB31kn37OvM2VVqr4QO3FS5__mh9t1IJmwWa2Zie_ADg4cvd-NuKxuyDMd2sJ65P8ut2Vtbubs4qyfJ2OHCW1ZQYgHu4/s1600/form.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="780" data-original-width="877" height="355" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb0pqIft6_F5CzEGvi6PjQpron3EZKr4sVQEkHftMnCSrrhPQZB31kn37OvM2VVqr4QO3FS5__mh9t1IJmwWa2Zie_ADg4cvd-NuKxuyDMd2sJ65P8ut2Vtbubs4qyfJ2OHCW1ZQYgHu4/s400/form.png" width="400" /></a></div>
<br />
つづいて右上メニューから「スクリプトエディタ」を選択するとGASのオンラインエディタが開きます。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfiiv1QPVFBoMZqdZkyEYhZoUPeU7mWkUFd9UXVKCzvXqV7sqipFBFe8nh_1ao9ur1R-DRT8nDbBJYV4ZQBfou2WuFF_fLfGqBK1zAgQ-GD7uY7Zb0ZOl1jrZkUGZ_MaHrQn7h0etid2U/s1600/form2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="460" data-original-width="260" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfiiv1QPVFBoMZqdZkyEYhZoUPeU7mWkUFd9UXVKCzvXqV7sqipFBFe8nh_1ao9ur1R-DRT8nDbBJYV4ZQBfou2WuFF_fLfGqBK1zAgQ-GD7uY7Zb0ZOl1jrZkUGZ_MaHrQn7h0etid2U/s320/form2.png" width="180" /></a></div>
<br />
ここでソースコードを編集します。<br />
今回のコードはこのようなものです。<br />
<pre><code>var privilegedRespondent = ['taro@sitateru.sample', 'jiro@sitateru.sample'];
// グループ追加,フォーム送信時に実行
function addUserToGroup(event) {
console.log('addUserToGroup()');
var message = "グループ追加フォームからの通知です\n\n";
const respondent = event.response.getRespondentEmail();
if(privilegedRespondent.indexOf(respondent) === -1) {
console.log('Respondent ' + respondent + ' not allowed');
message += 'あなた( ' + respondent + " )にはアカウントを作成する権限がありません\n";
sendResult(respondent, message);
return;
}
const response = parseResponse(event.response.getItemResponses());
const userName = response['追加するユーザー'];
const groups = response['追加先グループ'].split(',');
for (var i = 0; i < groups.length; i++) {
var groupName = groups[i].trim();
try {
var member = addGroupMember(userName, groupName);
console.log(' added member', member);
if(member == undefined) {
message += "ユーザーをグループに追加できませんでした。入力内容を確認してください\n";
message += ' ユーザー: ' + userName + "@sitateru.com\n";
message += ' グループ: ' + groupName + "@sitateru.com\n\n";
} else {
message += "ユーザーをグループに追加しました\n";
message += ' ユーザー: ' + userName + "@sitateru.com\n";
message += ' グループ: ' + groupName + "@sitateru.com\n\n";
}
} catch(error) {
console.log(' error', error);
message += "ユーザーをグループに追加できませんでした。入力内容を確認してください\n";
message += ' ユーザー: ' + userName + "@sitateru.com\n";
message += ' グループ: ' + groupName + "@sitateru.com\n";
message += ' エラー: ' + error.toString() + "\n\n"
}
}
sendResult(respondent, message);
}
// Googleユーザーをグループに追加
function addGroupMember(userName, groupName) {
if(userName && groupName) {
const userEmail = userName + '@sitateru.com';
const groupEmail = groupName + '@sitateru.com';
console.log(' add ' + userEmail + ' to ' + groupEmail);
const member = {
email: userEmail,
role: 'MEMBER'
};
return AdminDirectory.Members.insert(member, groupEmail);
}
}
// メール送信
function sendResult(recipient, message) {
MailApp.sendEmail({
name: 'アカウント管理システム',
to: recipient,
subject: 'グループ追加フォーム実行結果',
body: message
});
console.log('send result');
}
// フォームの入力内容をオブジェクトにして返す
function parseResponse(itemResponses) {
var obj = {};
for (var i = 0; i < itemResponses.length; i++) {
var itemResponse = itemResponses[i];
var question = itemResponse.getItem().getTitle();
// var type = itemResponse.getItem().getType();
var answer = itemResponse.getResponse();
obj[question] = answer;
}
return obj;
}</code></pre>
<br />
あとはスクリプトにグループを操作する権限を与えるために、メニューの「Googleの拡張サービス」を選択して「Admin Directory API」をONにします。<br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYrn7Eve-Yvt_Qx5IFcbzVmSOCf20-zqlR1tw9f2MlnLl7fHcUg-OYLG4WtKbKveyy0pxTqu4PS-nahaCMDR9Vtah-n4_Z_yWBYJfGNq9EYDdB9DxEZPOl3Up56ks9fRcxOK4OtxRQWMA/s1600/form3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="214" data-original-width="769" height="89" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYrn7Eve-Yvt_Qx5IFcbzVmSOCf20-zqlR1tw9f2MlnLl7fHcUg-OYLG4WtKbKveyy0pxTqu4PS-nahaCMDR9Vtah-n4_Z_yWBYJfGNq9EYDdB9DxEZPOl3Up56ks9fRcxOK4OtxRQWMA/s320/form3.png" width="320" /></a></div>
<br />
コードは長いですが、GASでユーザーをグループに追加するには <code>AdminDirectory.Members.insert()</code> を実行すればOKです。公式のサンプルは<a href="https://developers.google.com/apps-script/advanced/admin-sdk-directory#add_group_member" target="_blank">こちら</a>。</div>
<br />
今回のコードではグループ追加フォームとして実用上あったほうがいい機能もいろいろつけてみました。<br />
・ <code>event.response.getRespondentEmail()</code> でフォーム送信者のメールアドレスを取得し、あらかじめ定義しておいた <code>privilegedRespondent</code> にいる人でなければ実行しない<br />
・「追加先グループ」はカンマ区切りで複数指定できるように<br />
・最後に実行結果をメール送信 <code>MailApp.sendEmail()</code><br />
<br />
と、こんなもので実際に利用を始めています。<br />
<br />
こちらにリファレンスがありますが、GASはかなりの種類のGoogleのサービスを操れるのが強みです。<span style="font-family: inherit;">↓のページで左メニューの「G Suite Services」や「Advanced Google Services」を開いてみよう!</span><br />
<a href="https://developers.google.com/apps-script/reference">https://developers.google.com/apps-script/reference</a><br />
<br />
<span style="font-family: inherit;">これからもいろいろなものを作って楽をしていこうと思います!</span><br />
<div>
<br /></div>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-79158843666078840042019-09-09T12:00:00.003+09:002019-09-09T12:00:05.883+09:00SQLをつかってJSONで格納されたデータを検索してみた<p><img src="https://s3-ap-northeast-1.amazonaws.com/sitateru-tech-blog/images/0e4d274d_334c_465d_83a4_a9ba9de0f7ff.png" alt=""></p>
<p>こんにちは!<br><br>シタテルでエンジニアをしている建山です。</p>
<p>今回は、データベース(MySQL)にJSON形式で格納されているデータをSQLで検索する方法を紹介します。<br>シタテルでは、AWS上のデータベースに蓄積されたデータをredashというツールを使い、SQLでデータ抽出したあと、データ分析できるようになっています。</p>
<h1 id="-">やりかた</h1>
<p>今回はMySQLの</p>
<pre><code class="hljs language-JSON_EXTRACT```という関数を紹介します。">
たとえば、格納データが以下の<span class="hljs-number">2</span>レコードあったとします。</code></pre>
<p>{"maxCount":1000,"targets":{"ladies":false,"mens":true,"kids":false,"baby":false}}</p>
<pre><code></code></pre><p>{"maxCount":50,"targets":{"ladies":true,"mens":false,"kids":false,"baby":false}}</p>
<pre><code>
ここで、maxCountが<span class="hljs-number">50</span>のデータを検索したいときは、
JSON_EXTRACTを使用し、以下のように抽出できます。(テーブル名:table_a、JSON格納カラム名:detail)</code></pre><p>select * from table_a where JSON_EXTRACT(detail, '$.maxCount') = 50</p>
<pre><code>
<span class="hljs-built_in">targets</span>のladiesが<span class="hljs-literal">true</span>の検索をしたいときは、以下のようになります。</code></pre><p>select * from tablename where JSON_EXTRACT(detail, '$.targets.ladies') = true</p>
<pre><code>
# 感想
このように、```$.</code></pre><p>のあとに、キーの名前を指定してあげれば、かんたんに、jsonが格納してあるカラムの中身を検索することができます。(表示の記述方法も同じ)</p>
<p>公式のドキュメントはこの辺あたりのようです。<br><a href="https://dev.mysql.com/doc/refman/5.7/en/json.html">https://dev.mysql.com/doc/refman/5.7/en/json.html</a></p>
<p>他にもいくつか関数があるようなので、いろいろ試してみたら、いいSQL+JSONライフが送れるかも知れません!</p>
<p>簡単ではありましたが、ご紹介でした!</p>
Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-23525485919541146452019-09-09T12:00:00.002+09:002019-09-09T12:00:10.316+09:00RPA触ってみた<p>どうもお久しぶりです。シタテルの朝野です。</p>
<p>突然ですがRPAってご存知でしょうか?私は数週間前にまともに知りました🙄<br>"Robotic Process Automation" の略で、PC上での作業をソフトウェアロボットが行うよう自動化することです。</p>
<p>現在私のいるDevOpsチームでは単純な作業を自動化して時間と手間を節約できないかという取り組みを少しずつ進めています。<br>そんな中でRPAというものの存在を知り、簡単に無償で導入できるものを試してみたので、サンプルと共に紹介します。</p>
<p>今回やってみたサンプルは、シタテルの<a href="https://atelier.sitateru.com/login">「マイアトリエ」</a>をブラウザで開いてログインするというものです。<br>(※サンプル内のメールアドレスとパスワードはダミーの値です)</p>
<h1 id="javascript-for-automation">Javascript for Automation</h1>
<p>MacOS Yosemite以降で使用可能なスクリプト言語です。<br>サンプルコードはこのようになります。</p>
<pre><code class="hljs language-javascript"><span class="hljs-keyword">const</span> DELAY = 3;
<span class="hljs-keyword">const</span> Chrome = Application(<span class="hljs-string">"Google Chrome"</span>);
<span class="hljs-keyword">const</span> <span class="hljs-keyword">window</span> = Chrome.windows[0];
<span class="hljs-keyword">const</span> <span class="hljs-keyword">tab</span> = Chrome.<span class="hljs-keyword">Tab</span>({
url: 'https:<span class="hljs-comment">//atelier.sitateru.com/login'</span>
});
<span class="hljs-keyword">window</span>.tabs.push(<span class="hljs-keyword">tab</span>);
delay(DELAY);
inputText(<span class="hljs-keyword">tab</span>, 'email', '<span class="hljs-keyword">sample</span>@sitateru.<span class="hljs-keyword">sample</span>');
inputText(<span class="hljs-keyword">tab</span>, 'password', 'sample_password');
submit(<span class="hljs-keyword">tab</span>);
function inputText(<span class="hljs-keyword">tab</span>, elementId, value) {
<span class="hljs-keyword">tab</span>.execute({ javascript: <span class="hljs-string">"document.getElementById('"</span> + elementId + <span class="hljs-string">"').value =`"</span> + value + <span class="hljs-string">"`;"</span> });
}
function submit(<span class="hljs-keyword">tab</span>) {
<span class="hljs-keyword">tab</span>.execute({ javascript: <span class="hljs-string">"document.forms[0].submit();"</span> });
}</code></pre>
<p>ブラウザ上での入力やクリックはjavascriptをブラウザ内で走らせることで行っています。</p>
<p>実行はコマンドで <code>$ osascript -l JavaScript sample.js</code> と打つだけです。<br>JavaScriptを書いてコマンド実行なので、RPAわからんけどJavaScriptチョットワカル!という私のような者にはありがたいです。</p>
<p>エンジニアで使う分にはハードルが低いと思うのですが、ノンプログラマーやMac使わない人がいると導入しにくそうなのが難点でしょうか。</p>
<h1 id="sikulix">SikuliX</h1>
<p><a href="http://sikulix.com/">http://sikulix.com/</a></p>
<p>JavaがあればWindowsでもMacでもLinuxでも使えるOSSのRPAアプリケーションです。<br>IDEを立ち上げて自動化プログラムを作成・実行します。</p>
<p>サンプルコード(コードと言っていいのかわかりませんが)はこのようになります。<br><img src="https://s3-ap-northeast-1.amazonaws.com/sitateru-tech-blog/images/1419c6be_1ff7_4293_8350_914fc355c6ab.png" alt=""></p>
<p>自動化したい操作をスクリプトににしてIDE上で書いていきますが、マウスポチポチでもある程度作れるのでノンプログラマーにもいくぶんとっつきやすそうです。</p>
<p>画像処理で有名なOpenCVを使って作られているらしく、操作対象の認識を「デスクトップ画面の中で対象の画像を抽出する」ことで実現しています。<br>(サンプルコードの <code>click()</code> のカッコ内にあるのは操作したい要素のスクリーンショットなのです)<br>画面上の文字を認識するOCR機能も一応あるのですが試してみたところ精度が怪しく実用には難しいなという印象でした。</p>
<p>機能面はやや物足りない感触でしたが、OSをまたいで使えることと導入の難易度が低そうなのはメリットですね。</p>
<h1 id="uipath">UiPath</h1>
<p><a href="https://www.uipath.com/ja/">https://www.uipath.com/ja/</a></p>
<p>こちらもIDEで作成・実行するタイプで、GUIでフローチャートを組み立てたりブラウザ操作を自動で読み取って操作を記録したりと、基本シェアウェアだけあって操作感や機能は充実しています。(そのぶん見た目は複雑なので最初は少し戸惑うかも)</p>
<p>個人や中小企業はCommunity Editionという無料版が使えますが、ある程度以上の規模の企業で使う場合は有料になるようです。<br><a href="https://www.uipath.com/ja/freetrial-or-community">https://www.uipath.com/ja/freetrial-or-community</a></p>
<p>サンプルコード(これもコードと言っていいのかわかりませんが)はこのようになります。<br><img src="https://s3-ap-northeast-1.amazonaws.com/sitateru-tech-blog/images/e162f122_9ec9_4249_b3b8_f3b6788a9eef.png" alt=""></p>
<p>作業の流れがグラフィカルに表示されるので大掛かりなようにも見えますが、Webページ上の要素を選択して操作しているというのは同じですね。</p>
<p>公式コミュニティがあるほかハンズオンセミナーなども開かれているようで、費用面とWindowsのみという点が問題なければ有力な候補になりそうだと思います。</p>
<hr>
<p>というわけで、今回は無料で使えるRPAを簡単にお試ししてみました。</p>
<p>こういったRPAは使いようで色々な作業の手間を省けそうな予感がしているので、できることを把握してうまく活用していきたいですね。</p>
<p>この記事がRPAに手を出してみる取っ掛かりになれば嬉しいです🤗</p>
Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-36908743700351837602019-06-03T12:00:00.000+09:002019-06-03T12:00:11.820+09:00書評『進化的アーキテクチャ ―絶え間ない変化を支える』<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
<a href="https://www.oreilly.co.jp/books/9784873118567/" target="_blank"><img height="320" src="https://www.oreilly.co.jp/books/images/picture_large978-4-87311-856-7.jpeg" width="225" /></a></div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
こんにちは、シタテルの茨木です。<br />
<br />
社内の輪読会でオライリー<a href="https://www.oreilly.co.jp/books/9784873118567/" target="_blank">『進化的アーキテクチャ ―絶え間ない変化を支える』</a>を読みました。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
琴線に触れた部分・キーワードを中心に簡単にご紹介したいと思います。</div>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
どういう本か・総評</h3>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
システムの「変更しやすさ」(進化可能性)をいかにして追求し、また経年劣化させないか、というところに主眼を置いています。経年劣化から守る仕組みとして「適応度関数」という考え方を導入します。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
具体的な手法や考え方は、継続的デリバリーやドメイン駆動設計などの知見を参照しており、これらの知見を俯瞰的にまとめた内容とも言えます。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
個人的には、適応度関数を始めとした本書で導入される概念より、参照されている既存の概念(CI/CD、DDDやその他の設計プラクティス)の方が参考になりました。最近の設計や開発プラクティスを俯瞰する本として読むのもいいかもしれません。</div>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
適応度関数</h3>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
アーキテクチャが満たしているべき要件(本書内では次元)を保護するもの。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
循環的複雑度・ユニットテスト・監視・メトリクスといったシステム的・自動的に測定可能なものだけでなく、外部組織によるシステム監査などの人手による測定も包含する概念。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
守るべき次元を定め、適応度関数として定義、測定し続けることで、外形的にアーキテクチャを保護する。保護することで、壊さずに変更し続けられることを担保する。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
ちょっと理想論すぎるかなという印象もありますが、そういう捉え方もあるな、という感想です。</div>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
アーキテクチャの分類・マイクロサービス</h3>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
本書では下記のような各アーキテクチャを対等に並べて、それぞれの歴史的背景や特性を、主に進化可能性の観点から整理しています。</div>
<ul style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; list-style-image: initial; list-style-position: initial; margin: 0px 0px 16px; padding: 0px 0px 0px 2em;">
<li style="box-sizing: inherit;">モノリス(3層レイヤ化モノリス等を含む)</li>
<li style="box-sizing: inherit; margin-top: 0.25em;">イベント駆動アーキテクチャ</li>
<li style="box-sizing: inherit; margin-top: 0.25em;">SOA</li>
<li style="box-sizing: inherit; margin-top: 0.25em;">マイクロサービス</li>
<li style="box-sizing: inherit; margin-top: 0.25em;">サーバレス</li>
</ul>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
いわゆるレガシー・エンタープライズ感の強いEDAやSOAから、マイクロサービスまで、しっかり比較の土台に載せている点は非常に良いと思いました。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
まあ、最終的にマイクロサービス推しであり、そこに多くの紙幅が割かれてはいますが…</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
マイクロサービスのどういった特性が優れていて、他のアーキテクチャではその特性は何とトレードオフにされているのか、整理して理解することができただけでも本書を読んだ価値があったと思います。</div>
<blockquote style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-left: 0.25em solid rgb(237, 236, 225); border-right-color: rgb(237, 236, 225); border-top-color: rgb(237, 236, 225); box-sizing: inherit; color: #888888; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin: 16px 10px; padding: 10px 0px 10px 16px;">
<div style="box-sizing: inherit; font-size: 1.6rem;">
モノリスを構築できないとき、なぜマイクロサービスがその答えだと思うのか。</div>
</blockquote>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
マイクロサービスのメリットの多くは、ビジネスドメインを中心としてサービス単位を構成することに由来するものです。(この辺は、後述の逆コンウェイ戦略にも通じます)</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
従い、仮にマイクロサービスであっても、サービス粒度がビジネス的な境界線(境界づけられたコンテキスト)と乖離していてはメリットの多くは享受できません。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
<span style="font-family: , , "helvetica neue" , "helvetica" , "arial" , "hiragino sans" , "hiragino kaku gothic pron" , "meiryo" , "segoe ui symbol" , sans-serif;">また逆に、モノリスアーキテクチャであっても、ビジネスドメインに基づいてしっかりとモノリス内でモジュール化(疎結合化)が成されていれば、マイクロサービスに近いメリットが得られるということでもあります。</span></div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
そこのところをしっかり意識して、安易に流行りのアーキテクチャというだけで飛び付かないようにしたいですね。</div>
<blockquote style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-left: 0.25em solid rgb(237, 236, 225); border-right-color: rgb(237, 236, 225); border-top-color: rgb(237, 236, 225); box-sizing: inherit; color: #888888; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin: 16px 10px; padding: 10px 0px 10px 16px;">
<div style="box-sizing: inherit; font-size: 1.6rem;">
マイクロサービスという名前にひっかからないようにしてほしい。各サービスは決して小さい必要はない。むしろ有効な境界づけられたコンテキストを捉えることが必要なのだ。</div>
</blockquote>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
サービス境界分割の手法</h3>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
何を軸にサービス境界を分割するべきかについては下記3つが挙げられています。</div>
<ul style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; list-style-image: initial; list-style-position: initial; margin: 0px 0px 16px; padding: 0px 0px 0px 2em;">
<li style="box-sizing: inherit;">ビジネス機能グループ<br style="box-sizing: inherit;" />既存のビジネスコミュニケーション階層を反映する。コンウェイの法則に従う。</li>
<li style="box-sizing: inherit; margin-top: 0.25em;">トランザクション境界<br style="box-sizing: inherit;" />トランザクションは最も分離しにくい要素である。</li>
<li style="box-sizing: inherit; margin-top: 0.25em;">デプロイメント目標<br style="box-sizing: inherit;" />デプロイ頻度が異なる箇所を境界とする。</li>
</ul>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
マイクロサービスであればサービス単位として、モノリスであればモノリス内のモジュール構成として、反映していけると良いですね。</div>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
コンウェイの法則/逆コンウェイ戦略</h3>
<blockquote style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-left: 0.25em solid rgb(237, 236, 225); border-right-color: rgb(237, 236, 225); border-top-color: rgb(237, 236, 225); box-sizing: inherit; color: #888888; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin: 16px 10px; padding: 10px 0px 10px 16px;">
<div style="box-sizing: inherit; font-size: 1.6rem;">
システムを設計するあらゆる組織は、必ずその組織のコミュニケーション構造に倣った構造を持つ設計を生み出す。</div>
</blockquote>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
一般に人はコミュニケーションコストを避ける。また、チームを跨ぐコミュニケーションはコスト高になる。<br />
ゆえに、チーム外の人間が所管する業務フローやコードにはできるだけ影響を及ぼさないように設計・実装しようとする(調整範囲をチーム内に閉じようとする)力学が働く。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
そこから転じて、解決してほしい課題に合わせて、極力チーム内で調整が閉じるように、チーム組成をする、というのが「逆コンウェイ戦略」になります。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
ただ一方で、チーム内の結束が維持できるのは精々10人程度が上限(一般的なスクラムでもそう言われますね)、とも述べられていますので、そこにはトレードオフがあります。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
本書ではマイクロサービスアーキテクチャを進化可能性の面で優れていると評価していますが、マイクロサービスは「ドメインエンティティ」、「デプロイ単位」、「チーム単位」を一致させながら、かつチームを小さく保ちやすく、結果として逆コンウェイ戦略を満たしやすい、という点も強調されています。</div>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
結合よりも重複</h3>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
結合よりも重複が望ましいという考え方は、本書の中で繰り返されています。</div>
<blockquote style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-left: 0.25em solid rgb(237, 236, 225); border-right-color: rgb(237, 236, 225); border-top-color: rgb(237, 236, 225); box-sizing: inherit; color: #888888; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin: 16px 10px; padding: 10px 0px 10px 16px;">
<div style="box-sizing: inherit; font-size: 1.6rem;">
マイクロサービスは無共有アーキテクチャを形成する。その目標は、できるだけ結合を減らすことだ。<span style="box-sizing: inherit; font-weight: bolder;">一般的に、結合よりも重複の方が望ましい。</span></div>
</blockquote>
<blockquote style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-left: 0.25em solid rgb(237, 236, 225); border-right-color: rgb(237, 236, 225); border-top-color: rgb(237, 236, 225); box-sizing: inherit; color: #888888; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin: 16px 10px; padding: 10px 0px 10px 16px;">
<div style="box-sizing: inherit; font-size: 1.6rem;">
ツールがあまりにもコードの再利用を容易にしてしまったせいで、現代の開発者は、<span style="box-sizing: inherit; font-weight: bolder;">たやすく結合できてしまう環境の中で適切な結合を行うことに悪戦苦闘している。</span></div>
</blockquote>
<blockquote style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-left: 0.25em solid rgb(237, 236, 225); border-right-color: rgb(237, 236, 225); border-top-color: rgb(237, 236, 225); box-sizing: inherit; color: #888888; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin: 16px 10px; padding: 10px 0px 10px 16px;">
<div style="box-sizing: inherit; font-size: 1.6rem;">
例えば、 CatalogCheckout サービスと ShipToCustomer サービスの両方が Item という概念を持つ。両方のチームが同じ名前と同じプロパティを持つので、開発者はサービスをまたいでそれを再利用しようと試みる。それが時間や労力の節約につながると考えるからだ。<br />
けれど、それは労力を増やす結果となる。なぜなら、コンポーネントを共有する全てのチームが変更を伝搬しなければならなくなるからだ。一方、コンポーネントを結合せずに各サービスにItem があり、必要な情報だけをCatalogCheckout から ShipToCustomer に渡す場合は、それらは独立して変更することが可能だ。</div>
</blockquote>
<blockquote style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-left: 0.25em solid rgb(237, 236, 225); border-right-color: rgb(237, 236, 225); border-top-color: rgb(237, 236, 225); box-sizing: inherit; color: #888888; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin: 16px 10px; padding: 10px 0px 10px 16px;">
<div style="box-sizing: inherit; font-size: 1.6rem;">
開発者が再利用可能なコードを作成する場合、開発者は最終的にコードを使用する無数の方法に対応するための機能を追加する必要がある。その将来の保証全ては、開発者がコードを単一の目的のために使用することをより難しくする</div>
</blockquote>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
共通化をしたくなる(DRYにしたくなる)のはエンジニアの性ですが、共通化は結合でもあり、変更時の影響範囲を広げるものでもあります。不用意に共通化を行うと変更容易性を損ないます。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
<span style="box-sizing: inherit; font-family: , , "helvetica neue" , "helvetica" , "arial" , "hiragino sans" , "hiragino kaku gothic pron" , "meiryo" , "segoe ui symbol" , sans-serif; font-weight: bolder;">あえて重複させる</span><span style="font-family: , , "helvetica neue" , "helvetica" , "arial" , "hiragino sans" , "hiragino kaku gothic pron" , "meiryo" , "segoe ui symbol" , sans-serif;">という選択肢を忘れないようにしたいですね。</span></div>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
腐敗防止層を設ける</h3>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
レイヤを間に挟むことで、将来的な変更可能性を残しておく、という手法です。例えばO/Rマッパであれば、DBMSの変更可能性を残しておく、という意味での腐敗防止層と捉えることができます。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
最近、RailsアプリからViewを排除してAPIサーバ化し、フロントを別でSPA化する機会がありましたが、この構成におけるAPIコントローラも腐敗防止層だなー、と思いながら書いていました。</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
RailsのViewではどうしてもViewからModelへの直参照を止められず、結果として密結合になっていきますが、SPA<->API Controller<->Modelの分離が明確になったことで、かなりモデル層の変更がやりやすくなったと感じます。</div>
<h3 style="background-color: white; border-bottom-color: rgb(237, 236, 225); border-bottom-style: solid; border-image: initial; border-left-color: initial; border-left-style: initial; border-right-color: initial; border-right-style: initial; border-top-color: initial; border-top-style: initial; border-width: 0px 0px 2px; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 1.8rem; line-height: 1.25; margin: 40px 0px 16px; padding-bottom: 5px; position: relative;">
デプロイに関するプラクティス</h3>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
<span style="box-sizing: inherit; font-family: , , "helvetica neue" , "helvetica" , "arial" , "hiragino sans" , "hiragino kaku gothic pron" , "meiryo" , "segoe ui symbol" , sans-serif; font-weight: bolder;">「継続的デプロイは開発側の都合であって、ユーザは頻繁な小規模改修を望んでいない」</span><span style="font-family: , , "helvetica neue" , "helvetica" , "arial" , "hiragino sans" , "hiragino kaku gothic pron" , "meiryo" , "segoe ui symbol" , sans-serif;"> という観点はもっともだと思いました。</span></div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
折衷案として機能トグル(コードベースにはマージするがフラグ制御でユーザオープンしない)を用いる方法などが挙げられています。</div>
<hr style="background-color: #e1e4e8; border: 0px rgb(237, 236, 225); box-sizing: content-box; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; height: 2px; margin: 32px 0px; overflow: visible; padding: 0px;" />
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px; margin-bottom: 16px;">
<div style="box-sizing: inherit; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; margin-bottom: 16px;">
個人的には、適応度関数はそこまで刺さらなかったですが、個別のトピックは面白い・参考になるものが多かったです。</div>
<div style="box-sizing: inherit; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; margin-bottom: 16px;">
ただ個別トピックはおそらく殆どに元文献があるので、本格的な設計本をたくさん読んでいる方には物足りないかもしれません。<br />
取っ掛かりをつかみたい人にはおすすめできるかと思います。</div>
<div style="box-sizing: inherit; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif;">
よければ読んでみてください。</div>
</div>
<div style="background-color: white; box-sizing: inherit; color: #4d4d4d; font-family: -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, "Segoe UI Symbol", sans-serif; font-size: 16px;">
<br /></div>
Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-84564474693242163632019-05-08T12:00:00.000+09:002019-05-08T12:00:14.189+09:00プログラミング・ゲームこんにちは。<br />
シタテルでバックエンドエンジニアをしている熊谷です。<br />
<br />
今日は、プログラミングを学べるゲームやアプリの紹介をしたいと思います。<br />
<br />
まず1つ目は、「Swift Playgrounds」です。<br />
<br />
これはAppleが出しているiPad用のアプリで、キャラクターを動かすなどのプログラミングを、ゲーム感覚で学ぶことができます。<br />
<br />
アプリ自体は前からあったのですが、最近やっと少し触ることができました。<br />
<br />
画面イメージとしては、こんな感じです。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhphwQES8X0TP_1zAtziFiN1gmd_dX9-Fc7ex-rnWP_eN6qIjzStCVNLaWNf1vY9ZcCvgKtuSQNC05jpkHJKpSQkERMVG1qswQEm8uJd7rw1udXVlCqFipXsjqLxteN4cdG64kgJROo0C5W/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2019-05-07+14.33.29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1100" data-original-width="1600" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhphwQES8X0TP_1zAtziFiN1gmd_dX9-Fc7ex-rnWP_eN6qIjzStCVNLaWNf1vY9ZcCvgKtuSQNC05jpkHJKpSQkERMVG1qswQEm8uJd7rw1udXVlCqFipXsjqLxteN4cdG64kgJROo0C5W/s320/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2019-05-07+14.33.29.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
自分はまだ少ししか触れていませんが、初級からだんだん難しくなっていくレッスン形式になっていて、プログラミングに慣れていない人でも入りやすいのではないかと思います。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
公式のレッスンの他にもサードパーティのレッスンもあり、センサーやロボット、ドローンを扱うようなものもあるようです。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
普段のプログラミングや設計からの気分転換にもなって、良いと思います。</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
もう1〜2個紹介しようと思いましたが、長くなりそうなので、続きは次回にしたいと思います。</div>
<br />Kumahttp://www.blogger.com/profile/02845485119766970669noreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-84184100757135333442019-04-23T12:00:00.000+09:002019-04-23T12:00:07.410+09:00同時に複数のマシーンで同じ操作をする with Tmux<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-m0FVMubvG74/XKF6G_usoGI/AAAAAAAAANc/P_Ufg8Dx4H4T9Gc1VQvohCXvXX_roBJlgCLcBGAs/s1600/Apr-01-2019%2B11-39-30.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="316" data-original-width="640" src="https://3.bp.blogspot.com/-m0FVMubvG74/XKF6G_usoGI/AAAAAAAAANc/P_Ufg8Dx4H4T9Gc1VQvohCXvXX_roBJlgCLcBGAs/s1600/Apr-01-2019%2B11-39-30.gif" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span id="goog_1559516269"></span><span id="goog_1559516270"></span><br /></div>
<br />
<br />
sitateruのエンジニア北爪です。<br />
<br />
複数のシェルで同じ操作をしたい時ってないですか?<br />
<ul>
<li>ログを一箇所に集めてないけど、アプリケーションサーバーが複数台ある</li>
<li>複数台同時にtopコマンドで負荷をまたはtailコマンドでログを調査したい</li>
</ul>
<br />
など調査段階でAnsible, Fabやcapistranoで操作するほどではなく、複数のサーバーを同じようにインタラクティブに操作したいという要求はあるように思います。<br />
<br />
今回は複数のシェルに対して、分割された画面をみなが同時に操作、そのコマンドラインの結果を参照することが可能です。<br />
<br />
tmuxを風にいうと、<b>一つのpaneに行うキーボード操作をそのpaneが存在しているWindow内すべてのpaneキーボード操作を同期することができます</b>。<br />
<br />
この機能は、tmuxの <b>synchronize-panes 機能</b>です。tmuxを使ってない人には導入する大きなメリットの一つになるのではないかとお思います。デフォルトです。<br />
<br />
<a href="http://man.openbsd.org/OpenBSD-current/man1/tmux.1#synchronize-panes">http://man.openbsd.org/OpenBSD-current/man1/tmux.1#synchronize-panes</a><br />
<h3>
使い方</h3>
1. tmuxを起動し、複数のpaneを作成がある状態で<br />
2. tmuxにbindしてあるtmuxのprefixキーを入力した後で、 `:` を入力します<br />
(余談私は ctrl-t にしています)<br />
<br />
<div style="background-color: black; color: #d1d1d1; font-family: "Andale Mono"; font-size: 17px; font-stretch: normal; line-height: normal;">
<span style="color: #7200cd; font-variant-ligatures: no-common-ligatures;"># act like GNU screen</span><span style="font-variant-ligatures: no-common-ligatures;"> </span></div>
<div style="background-color: black; color: #d1d1d1; font-family: "Andale Mono"; font-size: 17px; font-stretch: normal; line-height: normal;">
<span style="color: #fc7208; font-variant-ligatures: no-common-ligatures;">unbind</span><span style="font-variant-ligatures: no-common-ligatures;"> </span><span style="color: #1cd404; font-variant-ligatures: no-common-ligatures;">C-b</span><span style="font-variant-ligatures: no-common-ligatures;"> </span></div>
<br />
<div style="background-color: black; color: #fed10a; font-family: "Andale Mono"; font-size: 17px; font-stretch: normal; line-height: normal;">
<span style="color: #fc7208; font-variant-ligatures: no-common-ligatures;">set</span><span style="font-variant-ligatures: no-common-ligatures;"> -g</span><span style="color: #d1d1d1; font-variant-ligatures: no-common-ligatures;"> </span><span style="font-variant-ligatures: no-common-ligatures;">prefix</span><span style="color: #d1d1d1; font-variant-ligatures: no-common-ligatures;"> </span><span style="color: #1cd404; font-variant-ligatures: no-common-ligatures;">C-t</span><span style="color: #d1d1d1; font-variant-ligatures: no-common-ligatures;"> </span></div>
<br />
3. tmux用のコマンドが立ち上がるので、そこに、 <code>set synchronize-panes on</code>を入力します。<br />
4. そうするとなんと!表示されているpane(分割した画面) に同じキーボード操作が入力されます<br />
5. もう同期したくなくなったら、 <code>set synchronize-panes off </code>にて解除することが可能です。<br />
<br />
以上です。<br />
<br />
<br />
<br />Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-63347997363679278162019-04-16T12:00:00.000+09:002019-04-16T12:00:08.213+09:00Vue.jsとScoped CSSとz-indexの話<img alt="ã¦ã§ããµã¤ãã®ã¤ã©ã¹ã" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsugVTHbo937pCT9XS82yNJds7iQCbx09mVtuMHcjdgIZoyuUQhfxY-gfsIfykHpCEX0fElJfmDZnOAQeO5Md1Uwvb1M1wCqBkT4sEKjguu78orJuNKZb3h8XsCtoD1lQZqtD05GIcWfgI/s400/website_normal.png" /><br />
<br />
こんにちは、シタテルの茨木です。<br />
<h3>
突然ですが、scoped cssいいですよね</h3>
scoped cssは、特にVue.jsにおいては、css設計のスタンダードと言っていいのではないかと思います。<br />
<br />
BEM等の学習コストの高い手法を覚える必要がなく、「コンポーネント内でのみcssクラス名の一意性を確保すればいい <b><span style="font-size: x-small;">*1</span></b>」という、シンプルで管理しやすい設計指針を打ち立てることができ、実に理にかなった手法ですね。<br />
プロジェクト全体でのcss命名規約の統制問題は依然としてありますが、コンポーネントベースでの設計手法自体がコンポーネントを書き捨てる運用と親和性があるため、そこまで大きな問題にはならないのかなと思います。<br />
<h3>
依然として残る考慮点</h3>
一方、scoped cssだけで、コンポーネント内にcss的な関心事を完全に閉じ込められるかというとそうではなく、当然ながら例外もあります。<br />
<br />
最も代表的な例としては、cssの仕様上継承されてしまう属性ですね。<br />
font-sizeなど、属性自体が子孫要素に継承されるものとして定義されていますので、セレクタレベルでscopedにしたところで、子孫要素への影響を避けることはできません。<br />
<br />
まあこの辺は書いていても直感的に理解しやすいのでそこまで問題になりにくいのですが、個人的に危ないなと思うのは「z-index」「重ね合わせコンテキスト」です。<br />
<h3>
z-indexの管理</h3>
cssをざっくり理解したつもりになっているエンジニアが犯しがちな間違いの一つが、z-indexがグローバルだと思いこんでしまうことだと思います。<br />
<br />
そう考えているエンジニアは、Vueプロジェクト全体で単純にコンポーネントごとのz-indexを管理することをを思いつきます <b><span style="font-size: x-small;">*2</span></b>。例えば、コンテキストメニューのコンポーネントはz-index1000番台、モーダルダイアログのコンポーネントはz-index500番台といった管理です。(モーダル上でコンテキストメニューを開くかもしれない。その場合はコンテキストメニューがモーダルの下に回り込んでほしくない、なんてことを考えているわけですね)<br />
<br />
この方式は重ね合わせコンテキストがページ内で1つであるうちは上手く機能しますし、ルール自体はおそらく間違いでもないです。<br />
ただ、重ね合わせコンテキストに関する考慮を漏らしていると、「z-indexで勝っているはずなのに、なぜか後ろに回り込んでしまう要素」が出てくる可能性があり、「なぜか動かない」という状況に見舞われます。実際には仕様理解が足りていないだけなのですが。<br />
<br />
理解すべき仕様「重ね合わせコンテキスト」の詳細についてはMDNを読んでください。<br />
<br />
https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context<br />
<br />
ざっくり言えば、「z-indexは同じ重ね合わせコンテキストの中での比較にしか使われない」「重なる系の属性(potision: absoluteなど)が指定された要素は自身が重ね合わせコンテキストを形成する」ということです。<br />
<br />
これもfont-sizeなどと同じで、親要素で指定された属性が(たとえscopedであろうとも)、子孫要素に影響を与えうる、ということですね。<br />
<h3>
デザインは文脈依存?</h3>
scoped cssとコンポーネントベースでの部品化は、確かに今までのcssの不便さや複雑さの大部分を解決しているように見えますが、z-indexの件を一つの例として考えると、「デザイン・見た目・UIは、どこまで部品化しても文脈依存から逃れられない」ということを示している気がします。まさに、重ね合わせ「コンテキスト」ですしね。<br />
<br />
z-indexをpropsで渡せば…みたいな発想はもちろんあるのですが、結局その先にあるのはinput要素の再発明だったりするわけです。input要素をwrapしてたはずがinput要素を作っていた…みたいな。<br />
<br />
どんなコンテキストでもデザイン破綻せず使うことのできるUI部品の実装は、相当な労力を要します。(まあ、実際にUI部品系のnpmパッケージの中を覗いてみると、普通にz-indexがハードコードされていて変更できなかったりすることが多く、みんなほどほどに手を抜いているんだと思いますが)<br />
<br />
プロジェクト内でのデザイン統一・再利用を想定してコンポーネントを分割設計する際に、デザイン的な汎用性を求めるのはほどほどにしておくべきなのかな、と思った次第でした。<br />
<br />
他コンポと疎結合に作るとか、ステートレスに作るとか、そちらに労力を割いたほうが実りが大きいんじゃないかな。UIの寿命は短いので、捨てやすいように疎結合に書くのが一番大事かな、と個人的には思います。<br />
<br />
<hr />
<br />
*1: slot内のスコープが親子共通だったり、コンポーネントroot要素に親からclassが注入できるといった例外はありますが<br />
<br />
*2: 私です<br />
<div>
<br /></div>
<div>
<br /></div>
Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-16979122595315875632019-04-08T16:17:00.000+09:002019-04-08T16:38:27.472+09:00地方活性化のイベントに参加してきました<span style="background-color: white; color: #24292e; font-family: , , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol"; font-size: 16px;">こんにちは、シタテルの藤本です。</span><br />
<span style="color: #24292e; font-family: , , "segoe ui" , "helvetica" , "arial" , sans-serif , "apple color emoji" , "segoe ui emoji" , "segoe ui symbol";"><span style="background-color: white;">イベントの参加のために久しぶりに東京に行きましたが、</span></span>渋谷駅から出られなくなりそうになりました。<br />
あらためてお上りさんには厳しい街であることを実感しました。<br />
<div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-cikfpFkmizc/XJ3EmW62rMI/AAAAAAAAABY/Cnx1Uk7CZEELGqpNXkJWv2l_Rza3g0jBwCLcBGAs/s1600/audience-1677028_1280.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="960" data-original-width="1280" height="300" src="https://2.bp.blogspot.com/-cikfpFkmizc/XJ3EmW62rMI/AAAAAAAAABY/Cnx1Uk7CZEELGqpNXkJWv2l_Rza3g0jBwCLcBGAs/s400/audience-1677028_1280.jpg" width="400" /></a></div>
<br />
<br /></div>
先日<b>「スマホで見つける地方のしごと」</b>というシンポジウムにパネラーとして<br />
参加させていただきました。<br />
<br />
内容を簡潔にすると「首都圏での離職と地方へUIJターンを希望する方と地方企業の<br />
マッチングを進めて地方を活性化していこう」というイベントでした。<br />
<br />
200名以上の方が参加され、各者の熱いプレゼンもありここから少しづつ<br />
地方へ人材が広がっていってくれるのではないかと感じることができたイベントでした。<br />
<br />
そんなイベントについて簡単にレポートしてみたいと思います<br />
<br />
<h2>
プレゼンター、パネラー</h2>
プレゼンター、パネラーとして以下の方々が参加されていました。<br />
<br />
<ul>
<li>求人サイト運営企業</li>
<li>地方自治体</li>
<li>商工会議所</li>
<li>地方への移住者(Uターン)</li>
</ul>
<br />
<h2>
イベントの大まかな流れ</h2>
<div>
<div>
以下の内容で2時間ほどのイベントでした。</div>
<div>
<br /></div>
<div>
<ol>
<li>厚生労働省からの本取組みにおける施策のついての説明</li>
<li>求人サイト運営企業からのプレゼン</li>
<li>地方自治体、商工会議所、移住者からのプレゼン</li>
<li>パネルディスカッション</li>
</ol>
</div>
</div>
<div>
<h2>
各者の主張について</h2>
各者の主張について個人的に印象的だったことについて書いていきます。<br />
<br />
<h3>
求人サイト運営企業</h3>
求人サイト運営企業の方からのプレゼンで印象に残ったのは以下の2点でした<br />
<br />
<br />
<ul>
<li>転職は孤独であり寄り添うことに注力したい</li>
<li>転職は一般的になっている</li>
</ul>
<h4>
転職は孤独であり寄り添うことに注力したい</h4>
転職にする時は非常に多くの<b>「不安」</b>と戦うことになると思いますが共有・共感者が少ないといった問題があり、そこに<b>「賛成」</b>と後押しすることで生き方を一緒に考えていこうといったものでした。<br />
<br />
個人的には人材の流動性が高まることはいいことだと思っているので求人サイト側がこういったことを考えていただけるとありがたいなと思いました。<br />
<br />
ちなみに弊社のBaseValueの一つに<b>「よりそう」</b>と言葉が定義されており、大きく以下の3つで構成されています。<br />
<br />
<ul>
<li>リスペクトする</li>
<li>声を聴く</li>
<li>感動を与える</li>
</ul>
詳細が気になった方は以下を参照いただけると嬉しいです。<br />
<br />
<a href="https://sitateru.co.jp/about/" target="_blank">シタテルの目指す未来|シタテル株式会社</a><br />
<br />
<h4>
転職は一般的になっている</h4>
転職をポジティブに捉えているのは全世代で5割以上となっているということでした。<br />
プレゼンターの方の言葉ですと「<b>ポジティブ、ネガティブというより転職は一般化</b>してきている」といわれていることがとても印象的でした。<br />
<h3>
地方自治体・商工会議所</h3>
地方自治体・商工会議所でも地方企業の魅力や人材育成・確保に向けた種々の取り組みを<br />
されているのがすごく伝わってくるプレゼンでした。<br />
印象的だったのは以下の3つです。<br />
<br />
<ul>
<li>人材育成・確保に向けた種々の取り組みをされている</li>
<li>コメリは新潟の上場企業だった</li>
<li>地域資源を生かしたビジネスを展開</li>
</ul>
<h4>
人材育成に向けた種々の取り組みをされている</h4>
小型ロケットの製造から打ち上げを通じた技術者の育成を行なっており、実際に<b>ロケットの打ち上げを成功</b>されている地方がありました。<br />
<br />
企業の枠を超えた<b>技術者の連携や情報交換、人脈の育成</b>などを推進されているとのことで地方を活性化していくために技術を磨いていくことは大切だなと感じました。<br />
<br />
またUターンを促進するために東京からの無料のバスを出していたり、子供の時からものづくりフェアを実施して小学生に体験し興味を持ってもらい、ものづくりの楽しさを感じてさらに地元企業への魅力に気づいてもらうなどの取り組みをされていることも印象的でした。<br />
<h4>
コメリは新潟の上場企業だった</h4>
家の近くにコメリというホームセンターがあり、品揃えもよく安いのでよく利用させてもらっています。<br />
<br />
その企業が新潟の三条市発祥の企業でしかも上場しているのは知りませんでした。<br />
さらに三条市には上場しており本社をおいている企業が多くあり、人口1人あたりの上場企業数は<b>東京、大阪についで3位</b>とのことです。<br />
<br />
<h4>
地域資源を生かしたビジネスを展開</h4>
「もくロック」というブロックの木製玩具があるのですがこれの原材料となっているのが<br />
<b>地元の金属加工目メーカー</b>が生み出しものということでした。<br />
地元の資源を使い、地元の企業が<b>新たなビジネスを生み出す</b>ということは素晴らしいなと思い印象的でした。<br />
<br />
<h3>
地方への移住者</h3>
広島への移住者の方でまちづくりのコンサルタントを行われている方でした。<br />
非常に広島への熱い気持ちを持っている方で印象的だったのは以下の2点です。<br />
<br />
<ul>
<li>経営者との距離が近くなった</li>
<li>「自分がいなくても回る」から「自分がいるから回る」</li>
</ul>
<br />
<h4>
経営者との距離が近くなった</h4>
頻繁に経営者と面と向かって話す機会があり東京にいる時にはなかったことであるということでした。<br />
<h4>
「自分がいなくても回る」から「自分がいるから回る」</h4>
東京よりも人口が少なくなるので自分の役割や影響の割合が多くなるため、<b>自分を必要とされる、頼られる</b>ことが強く感じられるようになったということでした。<br />
<br />
<h2>
ぜひシタテルへ!</h2>
弊社シタテルですが本社は熊本にあります。<br />
私も東京で13年程働いておりましたが2018年より熊本へUターンしてシタテルで働いております。<br />
<br />
転職についての不安は色々とありましたが1つだけ挙げるとすると<br />
前職ではほとんど管理業務を行なっており、実務でのプログラミングは5年以上していない状態でした。<br />
<br />
そんな状態でどこまでやくに立てるか不安では、転職してまずは1年やってこれることができました。<br />
まだまだ未熟で周りに迷惑をかけてはいますがシタテルのエンジニアの方はみんな教えたがりで優しく、わからないことがあり聞いたら、進んで色々と教えてくれるのでいつも助かっています。<br />
<br />
私のように少しプログラムから離れてしまったがまた現場に戻りたいと考えている方もいるのではないかと思いますので、もし首都圏にて「また現場でプログラミングをしたい」、「いろんな技術に触れたい」という方、かつ「地方へ移住してみたい」という方がいらしたぜひシタテルへお話を聞きにいらしてください!<br />
<br />
<a href="https://www.wantedly.com/projects/233821" target="_blank">エンジニア│熊本or東京勤務 - シタテル株式会社のWeb エンジニア中途・契約・委託の求人 - Wantedly</a><br />
<h2>
少しだけ熊本の風景を</h2>
参加したイベント時に使用した風景写真です、熊本には自然が多く、いろんな風景を楽しむことができます。<br />
<br />
<h3>
近くの公園で桜満開になった時</h3>
<a href="http://2.bp.blogspot.com/-EK64Y-86ObQ/XJ2-dkCITlI/AAAAAAAAABE/8aAmxx2xHS0mHNd1ptgUgugRMWr-DaHLQCK4BGAYYCw/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2019-03-29%2B15.42.30.png" imageanchor="1"><img border="0" height="356" src="https://2.bp.blogspot.com/-EK64Y-86ObQ/XJ2-dkCITlI/AAAAAAAAABE/8aAmxx2xHS0mHNd1ptgUgugRMWr-DaHLQCK4BGAYYCw/s640/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2019-03-29%2B15.42.30.png" width="640" /></a><br />
<h3>
家族でよく出かける天草の海</h3>
<a href="http://2.bp.blogspot.com/-IEPLwwLZNJM/XJ2-fXmlnqI/AAAAAAAAABM/z1TtcrgOOfg4tVidYuyVAVZytx6CIrFLACK4BGAYYCw/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2019-03-29%2B15.42.38.png" imageanchor="1"><img border="0" height="418" src="https://2.bp.blogspot.com/-IEPLwwLZNJM/XJ2-fXmlnqI/AAAAAAAAABM/z1TtcrgOOfg4tVidYuyVAVZytx6CIrFLACK4BGAYYCw/s640/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2019-03-29%2B15.42.38.png" width="640" /></a></div>
Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-62571638060940885062019-03-12T12:00:00.000+09:002019-03-12T12:11:15.302+09:00初めて見るとぎょっとするかもしれない JavaScript の構文 4 選<div class="part" data-endline="1" data-startline="1" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
こんにちは! エンジニアの諏訪です。シタテルでは主にフロントエンドを担当しています。</div>
<div class="part" data-endline="3" data-startline="3" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
今日はフロントエンド制御のエンジンともいえる JavaScript に関する話題です。</div>
<div class="part" data-endline="7" data-startline="7" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
<a href="http://1.bp.blogspot.com/--DGcwd8m1hM/XIYto9EVwmI/AAAAAAAAADM/NJBBiWv2c44XrrACBFqUrbbNZ79K8Is-QCK4BGAYYCw/s1600/javascript-illustration.png" imageanchor="1"><img border="0" height="240" src="https://1.bp.blogspot.com/--DGcwd8m1hM/XIYto9EVwmI/AAAAAAAAADM/NJBBiWv2c44XrrACBFqUrbbNZ79K8Is-QCK4BGAYYCw/s640/javascript-illustration.png" width="640" /></a></div>
<div class="part" data-endline="7" data-startline="7" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
1990 年代半ば Web 時代の幕開け (※) とともに誕生した JavaScript ── ほんとうの名前は ECMAScript というのだそうですが ── 今も昔も変わらずクライアント側での処理機構としては唯一無二の立ち位置を保ち続けてきました。</div>
<div class="part" data-endline="9" data-startline="9" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
よく考えてみるとこれってとてもすごいことですよね!</div>
<div class="part" data-endline="11" data-startline="11" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
※ 余談ですが World Wide Web の誕生から今日 (2019.03.12) でちょうど 30 年だそうです。</div>
<div class="part" data-endline="11" data-startline="11" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
さて、上で「今も昔も変わらず」と書きましたが JavaScript 自身はもちろん何度も進化を重ねてきています。</div>
<div class="part" data-endline="13" data-startline="13" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
その中でも ES2015 (ES6) では多くの新しい構文が追加され、処理内容によっては従来よりも大幅にシンプルな書き方ができるようになりました。</div>
<div class="part" data-endline="15" data-startline="15" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
新しい構文は便利なのですが、勉強を始めたての新人やレガシーなプログラマが初めて見るとびっくりするような記法も。</div>
<div class="part" data-endline="17" data-startline="17" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
今日はそんなユニークな (ちょっとぎょっとする) 記法を4つピックアップしてみました。</div>
<h1 class="part" data-endline="21" data-startline="21" id="ドット3つ-" style="background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin: 24px 0px 16px; padding-bottom: 0.3em; position: relative;">
<a class="anchor hidden-xs" href="https://hackmd.io/ki2mAJmmRJ-punb1Kg6MZw#%E3%83%89%E3%83%83%E3%83%88%EF%BC%93%E3%81%A4-" smoothhashscroll="" style="background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="ドット3つ-"><span class="octicon octicon-link" style="box-sizing: border-box; color: black; display: inline-block; font-family: "octicons"; font-size: 16px; font-stretch: normal; font-weight: normal; line-height: 1; vertical-align: middle; visibility: hidden;"></span></a>ドット3つ <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: inherit; margin: 0px; padding: 0.2em 0px;">...</code></h1>
<div class="part" data-endline="23" data-startline="23" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
JavaScript のコードで、3つ並んだドットを見たことがありませんか。</div>
<pre class="part" data-endline="29" data-startline="25" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">let array = [ 1, 2, 3 ]
let result = [ 0, ...array ]
</code></pre>
<div class="part" data-endline="31" data-startline="31" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
こんなコードを初めて見た時は「<span style="box-sizing: border-box; font-weight: 700;">・・・あれ〜?</span>」と脳が一瞬フリーズしてしまうこと必至です。ちなみにだじゃれです。</div>
<div class="part" data-endline="34" data-startline="33" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
こちらは <span style="box-sizing: border-box; font-weight: 700;">スプレッド構文 (spread syntax)</span> と呼ばれるもので、 ES2015 で導入された新しい記法です。<br />
配列やオブジェクトの展開操作を楽にしてくれます。</div>
<div class="part" data-endline="36" data-startline="36" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
展開操作って?</div>
<div class="part" data-endline="38" data-startline="38" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
大雑把に言うと <span style="box-sizing: border-box; font-weight: 700;">Array や Object の外側の括弧を取り払って丸裸にします</span>。</div>
<div class="part" data-endline="40" data-startline="40" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
例えばこういうことです。さっきの Array の例だと:</div>
<pre class="part" data-endline="46" data-startline="42" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">let array = [ 1, 2, 3 ]
let result = [ 0, ...array ]
</code></pre>
<pre class="part" data-endline="51" data-startline="48" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">console.log(result)
// => [0, 1, 2, 3]
</code></pre>
<div class="part" data-endline="53" data-startline="53" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
Object の場合だと:</div>
<pre class="part" data-endline="61" data-startline="55" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">let obj1 = { potato: 2, carrot: 3, onion: 5, niku: 8 }
let obj2 = { water: 65535, spice: Infinity }
let result = { ...obj1, ...obj2 }
</code></pre>
<pre class="part" data-endline="66" data-startline="63" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">console.log(result)
// => { "potato": 2, "carrot": 3, "onion": 5, "niku": 8, "water": 65535, "spice": Infinity }
</code></pre>
<div class="part" data-endline="68" data-startline="68" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
といった具合です。</div>
<div class="part" data-endline="70" data-startline="70" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
応用編として、メソッドの引数に使うことだって可能です:</div>
<pre class="part" data-endline="80" data-startline="72" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">function createUser(firstName, lastName, bornYear, bornMonth, bornDay) {
// doSomeProcess
}
let name = ['Colonel', 'Sanders']
let birthday = [1890, 9, 9]
let user = createUser(...name, ...birthday) // => createUser('Colonel', 'Sanders', 1890, 9, 9) を実行するのと同じ
</code></pre>
<h1 class="part" data-endline="84" data-startline="84" id="ビックリマークふたつ-" style="background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin: 24px 0px 16px; padding-bottom: 0.3em; position: relative;">
<a class="anchor hidden-xs" href="https://hackmd.io/ki2mAJmmRJ-punb1Kg6MZw#%E3%83%93%E3%83%83%E3%82%AF%E3%83%AA%E3%83%9E%E3%83%BC%E3%82%AF%E3%81%B5%E3%81%9F%E3%81%A4-" smoothhashscroll="" style="background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="ビックリマークふたつ-"><span class="octicon octicon-link" style="box-sizing: border-box; color: black; display: inline-block; font-family: "octicons"; font-size: 16px; font-stretch: normal; font-weight: normal; line-height: 1; vertical-align: middle; visibility: hidden;"></span></a>ビックリマーク2つ <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: inherit; margin: 0px; padding: 0.2em 0px;">!!</code></h1>
<div class="part" data-endline="87" data-startline="86" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
ビックリマークひとつならおなじみの否定演算子ですが<br />
ビックリマークふたつの記法もあるのか!!と<span style="box-sizing: border-box; font-weight: 700;">びっくりびっくり</span>したことがあります。</div>
<div class="part" data-endline="90" data-startline="89" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
じつはこれ別に ES2015 の構文ではなく、単に古典的な否定演算子を2つ重ねただけのものです。(本稿のほかの項目と違って JavaScript の黎明期からあるものです。)</div>
<div class="part" data-endline="92" data-startline="92" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
<span style="box-sizing: border-box; font-weight: 700;">Boolean (真偽値) 以外の値を Boolean にキャストしたいとき</span>に使うと便利です。</div>
<div class="part" data-endline="94" data-startline="94" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
たとえば次のようなコード</div>
<pre class="part" data-endline="100" data-startline="96" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">function hasName (user) {
return ( user.name !== undefined && user.name !== null && user.name !== '' )
}
</code></pre>
<div class="part" data-endline="102" data-startline="102" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
は次のように書けます:</div>
<pre class="part" data-endline="108" data-startline="104" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">function hasName (user) {
return !!user.name
}
</code></pre>
<div class="part" data-endline="110" data-startline="110" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
条件判断をシンプルに書きたいという方、ぜひこの書き方を取り入れてみてください。</div>
<h3 class="part" data-endline="112" data-startline="112" id="おまけ-いろんな値の-truthy--falsy-について" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 1.25em; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;">
<a class="anchor hidden-xs" href="https://hackmd.io/ki2mAJmmRJ-punb1Kg6MZw#%E3%81%8A%E3%81%BE%E3%81%91-%E3%81%84%E3%82%8D%E3%82%93%E3%81%AA%E5%80%A4%E3%81%AE-truthy--falsy-%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6" smoothhashscroll="" style="background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="おまけ-いろんな値の-truthy--falsy-について"><span class="octicon octicon-link" style="box-sizing: border-box; color: black; display: inline-block; font-family: "octicons"; font-size: 16px; font-stretch: normal; font-weight: normal; line-height: 1; vertical-align: middle; visibility: hidden;"></span></a>(おまけ) いろんな値の truthy / falsy について</h3>
<div class="part" data-endline="114" data-startline="114" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
ちょっと復習しておきたかったのでまとめてみました。</div>
<ul class="part" data-endline="133" data-startline="116" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em; position: relative;">
<li class="" data-endline="124" data-startline="116" style="box-sizing: border-box;"><div style="box-sizing: border-box; margin-bottom: 16px; margin-top: 16px;">
truthy (Boolean にキャストすると true となる) の例</div>
<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li class="" data-endline="117" data-startline="117" style="box-sizing: border-box;">true</li>
<li class="" data-endline="118" data-startline="118" style="box-sizing: border-box; margin-top: 0.25em;">0 以外の Number (ex: 1, -1, 3.14, -273.15, 6.02e+23, etc)</li>
<li class="" data-endline="119" data-startline="119" style="box-sizing: border-box; margin-top: 0.25em;">Infinity と -Infinity</li>
<li class="" data-endline="120" data-startline="120" style="box-sizing: border-box; margin-top: 0.25em;">空でない文字列 (例: "a", "false")</li>
<li class="" data-endline="121" data-startline="121" style="box-sizing: border-box; margin-top: 0.25em;">[] (空の Array)</li>
<li class="" data-endline="122" data-startline="122" style="box-sizing: border-box; margin-top: 0.25em;">{} (空の Object)</li>
<li class="" data-endline="124" data-startline="123" style="box-sizing: border-box; margin-top: 0.25em;">1 == true (緩やかな比較)</li>
</ul>
</li>
<li class="" data-endline="133" data-startline="125" style="box-sizing: border-box; margin-top: 0.25em;"><div style="box-sizing: border-box; margin-bottom: 16px; margin-top: 16px;">
falsy (Boolean にキャストすると false となる) の例</div>
<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li class="" data-endline="126" data-startline="126" style="box-sizing: border-box;">false</li>
<li class="" data-endline="127" data-startline="127" style="box-sizing: border-box; margin-top: 0.25em;">0 (Number)</li>
<li class="" data-endline="128" data-startline="128" style="box-sizing: border-box; margin-top: 0.25em;">"" (空の String)</li>
<li class="" data-endline="129" data-startline="129" style="box-sizing: border-box; margin-top: 0.25em;">null</li>
<li class="" data-endline="130" data-startline="130" style="box-sizing: border-box; margin-top: 0.25em;">undefined</li>
<li class="" data-endline="131" data-startline="131" style="box-sizing: border-box; margin-top: 0.25em;">NaN</li>
<li class="" data-endline="133" data-startline="132" style="box-sizing: border-box; margin-top: 0.25em;">1 === true (厳密な比較)</li>
</ul>
</li>
</ul>
<div class="part" data-endline="134" data-startline="134" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
注目すべきポイント (落とし穴) は</div>
<ul class="part" data-endline="137" data-startline="135" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em; position: relative;">
<li class="" data-endline="135" data-startline="135" style="box-sizing: border-box;">空文字列は falsy だが、<span style="box-sizing: border-box; font-weight: 700;">空の配列や空のオブジェクトは truthy</span></li>
<li class="" data-endline="137" data-startline="136" style="box-sizing: border-box; margin-top: 0.25em;">“false” や 配列 [false] などは truthy</li>
</ul>
<div class="part" data-endline="138" data-startline="138" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
あたりでしょうか。すぐ忘れそう…</div>
<h1 class="part" data-endline="142" data-startline="142" id="関数のアロー記法--gt-" style="background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin: 24px 0px 16px; padding-bottom: 0.3em; position: relative;">
<a class="anchor hidden-xs" href="https://hackmd.io/ki2mAJmmRJ-punb1Kg6MZw#%E9%96%A2%E6%95%B0%E3%81%AE%E3%82%A2%E3%83%AD%E3%83%BC%E8%A8%98%E6%B3%95--gt-" smoothhashscroll="" style="background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="関数のアロー記法--gt-"><span class="octicon octicon-link" style="box-sizing: border-box; color: black; display: inline-block; font-family: "octicons"; font-size: 16px; font-stretch: normal; font-weight: normal; line-height: 1; vertical-align: middle; visibility: hidden;"></span></a>関数のアロー記法 <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: inherit; margin: 0px; padding: 0.2em 0px;">() => {}</code></h1>
<div class="part" data-endline="144" data-startline="144" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
先ほど array の話をしたので今度は arrow の話をしなければという義務感を抱いています (嘘です)。</div>
<div class="part" data-endline="146" data-startline="146" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
JavaScript といえば無名関数 (ラムダ式) ですが、ES2016 以前は無名関数を記述するには <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0px;">function</code> という予約語を使うのが常でした。</div>
<pre class="part" data-endline="152" data-startline="148" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">setTimeout(function() {
console.log('Hello!')
})
</code></pre>
<div class="part" data-endline="154" data-startline="154" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
ES2015 からはアロー構文 <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0px;">() => {}</code> を使って書くこともできるようになりました。</div>
<pre class="part" data-endline="160" data-startline="156" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">setTimeout(() => {
console.log('Hello!')
})
</code></pre>
<div class="part" data-endline="162" data-startline="162" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
<code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0px;">function</code> と <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0px;">() => {}</code> どちらを使ってもよいのですが、1つだけ挙動の異なる点があるので注意が必要です。<br />
<span style="letter-spacing: 0.35px;">何が異なるのかというと、メソッド内での </span><code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: 13.6px; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">this</code><span style="letter-spacing: 0.35px;"> の扱いです; </span><code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: 13.6px; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">function () {}</code><span style="letter-spacing: 0.35px;"> の場合はメソッドの呼び出し元のオブジェクトを指しますが、 </span><code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: 13.6px; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">() => {}</code><span style="letter-spacing: 0.35px;"> の場合はメソッドが記述された文脈上のオブジェクトを指します。</span><span style="letter-spacing: 0.35px;">つまり、 </span><code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: 13.6px; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">function () {}</code><span style="letter-spacing: 0.35px;"> の場合は </span><code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: 13.6px; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">this</code><span style="letter-spacing: 0.35px;"> が動的に変わるのに対して </span><code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: 13.6px; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">() => {}</code><span style="letter-spacing: 0.35px;"> の場合は </span><code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: 13.6px; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">this</code><span style="letter-spacing: 0.35px;"> が固定されます。</span></div>
<div class="part" data-endline="166" data-startline="166" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
ローカル変数レベルで後者のような挙動をするラムダ関数を closure というのでしたっけ...λ</div>
<h1 class="part" data-endline="170" data-startline="170" id="テンプレートリテラル" style="background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin: 24px 0px 16px; padding-bottom: 0.3em; position: relative;">
<a class="anchor hidden-xs" href="https://hackmd.io/ki2mAJmmRJ-punb1Kg6MZw#%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%83%AA%E3%83%86%E3%83%A9%E3%83%AB" smoothhashscroll="" style="background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="テンプレートリテラル"><span class="octicon octicon-link" style="box-sizing: border-box; color: black; display: inline-block; font-family: "octicons"; font-size: 16px; font-stretch: normal; font-weight: normal; line-height: 1; vertical-align: middle; visibility: hidden;"></span></a>テンプレートリテラル <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit; font-family: menlo, monaco, consolas, "courier new", monospace; font-size: inherit; letter-spacing: 0.35px; margin: 0px; padding: 0.2em 0px;">`${}`</code></h1>
<div class="part" data-endline="172" data-startline="172" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
プログラムを書いていて、動的に文字列を生成しないといけない場面でよくお世話になるのが <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0px;">printf</code> のようなメソッド。メソッド名は言語によって様々ですが、世の中の大半の言語で提供されている構文です。</div>
<div class="part" data-endline="174" data-startline="174" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
ところが JavaScript にはこれまで対応するものがなかったのです。そんな中 ES2015 になってようやく登場したのが「テンプレートリテラル」。</div>
<div class="part" data-endline="176" data-startline="176" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
なんと文字列全体をバックチック <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0px;">`</code> で囲います。 (バックチックを約物に用いる言語ってあまりなかった気が…)</div>
<div class="part" data-endline="178" data-startline="178" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
そして<span style="box-sizing: border-box; font-weight: 700;">プレースホルダ</span>は <code style="background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; box-sizing: border-box; color: inherit !important; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0px;">${}</code> と書きます。</div>
<pre class="part" data-endline="185" data-startline="180" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">let username = `山田太郎`
let message = `ようこそ、${username}さん!`
console.log(message) // => "ようこそ、山田太郎さん!"
</code></pre>
<div class="part" data-endline="187" data-startline="187" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
そしてすごいのが、Ruby のヒアドキュメントよろしく<span style="box-sizing: border-box; font-weight: 700;">改行をそのまま書くことができます</span>。</div>
<pre class="part" data-endline="196" data-startline="189" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">let body = `${client.name}様
いつも大変お世話になっております。
${company.name}の${staff.name}です。
${message.content}`
</code></pre>
<pre class="part" data-endline="199" data-startline="197" style="background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; overflow-wrap: normal; overflow: auto; padding: 16px; position: relative; word-break: break-all;"><code style="background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">console.log(body) // => "山田太郎様\n\nいつも大変お世話になっております。\n〇〇株式会社の...
</code></pre>
<div class="part" data-endline="201" data-startline="201" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
一点だけ残念なのが、ソース全体をインデントしてしまうと、そのインデント分の空白文字も無視されずに含められてしまうこと。そのためテンプレートリテラルで記載した部分はインデントすることができず、ソースの可読性が若干下がります。</div>
<div class="part" data-endline="203" data-startline="203" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
Ruby のヒアドキュメントが上手にインデントを除去してくれるのに比べると少し物足りなく感じます。</div>
<hr style="background-color: #e7e7e7; border: 0px; box-sizing: content-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; height: 0.25em; letter-spacing: 0.35px; margin: 24px 0px; padding: 0px;" />
<div class="part" data-endline="207" data-startline="207" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
以上 ES2015 のユニークな構文を一部ご紹介しました。</div>
<div class="part" data-endline="209" data-startline="209" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; position: relative;">
新しい記法は少し古いブラウザではほとんどサポートされていないものもあって、動かなかったらどうしよう…という不安から新しい書き方で書くことを躊躇してしまいがちですが、その不安を取り払ってくれる polyfill もちゃんとあります。</div>
<div class="part" data-endline="211" data-startline="211" style="background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, system-ui, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; position: relative;">
正しく使えば、より書きやすく読みやすく無理のない安全なコードに近づける新しい構文。しっかりと学び、追いつき、使いこなせるようになりたいものです。</div>
Unknownnoreply@blogger.comtag:blogger.com,1999:blog-1797929185044797553.post-46948166005740550982019-03-04T12:00:00.000+09:002019-03-04T12:00:05.332+09:00Good Project Award 2019で最優秀賞をいただきました!<p>こんにちは!シタテル株式会社UI/UXデザイナーの田仲です。<br>私が担当しているマイオペレーター(以下、マイオペ)という縫製工場の生産管理者向けのシステムが、「JBUG(ジェイバグ:Japan Backlog User Group)」が開催する『Backlog World 2019』内の『Good Project Award 2019』にて最優秀賞を獲得いたしました。</p>
<p><img src="https://s3-ap-northeast-1.amazonaws.com/sitateru-tech-blog/images/0a347c1a_82d7_4578_a211_0d38bdac5708.jpeg" alt=""></p>
<h1 id="-backlog-world-2019-">『Backlog World 2019』とは</h1>
<p>株式会社ヌーラボの日本最大級のプロジェクト管理ツール”Backlog(バックログ)”のユーザーコミュニティである”JBUG(ジェイバグ)”が主催した、プロジェクト管理に関わる全ての方のための祭典です。<br>Backlog Worldとして2回目の今年は、「プロジェクトマネジメント×働き方改革」というテーマで、 数々のセッションやワークショップ、情報共有の場、Good Project Award(表彰イベント)などでプロジェクト管理に関する知見を相互に高め合うことを目的としています。<br>2019年1月26日(土)、秋葉原UDXにて開催されました。<br><a href="https://backlogworld2019.jbug.info/">https://backlogworld2019.jbug.info/</a></p>
<h1 id="-good-project-award-2019-">『Good Project Award 2019』とは</h1>
<p>2018年〜2019年に活動したプロジェクトの課題やそれに対するアクション、その結果得られたことのストーリーを通し、プロジェクトマネジメントのヒントが共有されることを目指したアワードです。<br>Backlog Worldイベント内コンテンツとして「Good Project Award 2019」というピッチコンテストが開催され、来場者投票と審査員の審査により、最も素晴らしいものを表彰します。</p>
<h1 id="-good-project-award-2019-">『Good Project Award 2019』への応募経緯</h1>
<p>アワードの存在を知ったCTOの和泉さんから「だしてみたら?」ともちかけてもらったのがきっかけです。<br>マイオペは2018年の頭から立ち上げ開始し、7月ごろにリリースをしました。Backlogは使用していなかったのですが、応募条件に利用有無は問われていなかった、応募することにしました。</p>
<h1 id="-good-project-award-2019-">『Good Project Award 2019』に登壇するまで</h1>
<h2 id="-">エントリーフォームより応募</h2>
<p>2018年末頃に、プロジェクトの目的や結果、熱い想いを書きました。</p>
<ul>
<li>エントリーについての情報<br><a href="https://backlog.com/ja/blog/good-project-award2019/">https://backlog.com/ja/blog/good-project-award2019/</a></li>
</ul>
<h2 id="-">ピッチコンテスト出場決定</h2>
<p>2019年1月上旬に、アワード実行委員会より一次選考通過の連絡が届きました。</p>
<ul>
<li>一次選考通過したプロジェクトがお知らせされています!<br><a href="https://backlog.com/ja/blog/good-project-award-2019-nominated/">https://backlog.com/ja/blog/good-project-award-2019-nominated/</a></li>
</ul>
<h2 id="-">ピッチコンテスト当日</h2>
<p>一次選考通過した他の4プロジェクトの方々と共に登壇しました。<br>マイオペからは、生産と開発の間をとりもつディレクションを担当している宇田川さんが発表しました。<br><img src="https://s3-ap-northeast-1.amazonaws.com/sitateru-tech-blog/images/4f5f2d0f_6f8c_4d33_965c_58c85d061584.jpeg" alt=""></p>
<ul>
<li>見事最優秀賞をいただくことができました!<br><a href="https://backlog.com/ja/blog/good-project-award-2019-sitateru/">https://backlog.com/ja/blog/good-project-award-2019-sitateru/</a></li>
</ul>
<h1 id="-">審査員からのコメント</h1>
<p>ソリューションについてはプロではないが工場の現場をよく知っている人が、関係者をどんどん巻き込んで共感をベースにプロジェクトを推進していたという点がユニーク。<br>さらに、もっとも重要だと感じたのは言語の共通化。プロジェクトはいろんな部署の人が入っているが言語の共通化は意外とスルーされているところも多いのではないか。工場もエンジニアもわかる言語の共通化やアイコンをつかうことで、年齢・カルチャー・価値観など異なる人を巻き込んでいる点が素晴らしい。最終的に、スムーズな導入に繋がったのは、細かい点まで配慮されたプロジェクトづくりがなされた結果ではないだろうか。</p>
<h2 id="-">最後に</h2>
<p>先日、プロダクト名が入った盾が届き、みんなで喜びました!<br><img src="https://s3-ap-northeast-1.amazonaws.com/sitateru-tech-blog/images/a8f49b45_ef69_4815_a892_fdc451c122e0.jpg" alt=""></p>
Anonymousnoreply@blogger.com