windows10 + docker for windows + intellij idea

windows環境のintelliJ IDEAでdocker integrationを使おうとしたときに、接続できなかった。
docker for windows 側の設定を直すことで解決できたのでメモしておく。

前提条件

解決方法

docker for windows のsettingsを開く

f:id:naught00:20170523120813p:plain

一番下の Expose daemon on tcp://localhost:2375 without TLS にチェックを入れる

docker for windows で docker pull に失敗する場合の対処方法

docker for windows にて docker pull に失敗する場合の対象方法について

dockerイメージを取得しようとすると下記のエラーが出た

$ docker pull debian
Using default tag: latest
Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

docker for windows バージョン情報

f:id:naught00:20170519160606p:plain

dockerのバージョン

$ docker -v
Docker version 17.03.1-ce, build c6d412e

対処方法

Settings > Network の項目にて DNS Server の項目で Fixed にチェックを入れ、値を 8.8.8.8 にする

参考

github.com

.NET Core コンソールアプリで日本語出力する方法

Windowsでコンソールアプリを作成し実行すると日本語が文字化けする。
Consoleの出力文字コードがUTF8固定になっていることが問題の様です。

対応方法

  1. NuGetから System.Text.Encoding.CodePages をインストールする

  2. Main関数の先頭に下記を追加

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

こんな感じ

static void Main(string[] args)
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}

参考

.NET Core での コンソールアプリの文字化けを直す | Aqua Ware つぶやきブログ

AWSのEC2に自動デプロイする(github+circleci+CodeDeploy)

githubにpushしたとき、自動でデプロイし、失敗した場合はslackに通知する設定方法のメモです。

期待する動作

  1. githubにpush
  2. circleciでビルド
  3. CodeDeployでデプロイ
  4. もしデプロイに失敗したらslack通知

設定するもの

  • s3
  • EC2
  • CodeDeploy
  • circleci
  • SNS
  • lambda

デプロイ用s3バケット

デプロイ時のリビジョン保存用のバケットを用意する

EC2

  • CodeDeploy Agentのインストール
  • EC2用ロール

インスタンス

CodeDeployができるOSは以下

EC2でインスタンスの作成を選択したとき、クイックスタートで選択できるOSの様な気がします。

IAM

下記の権限を持つIAMをアタッチする

  • AWSCodeDeployFullAccess
  • AmazonS3ReadOnlyAccess

CodeDeploy Agentのインストール

EC2内で実行

sudo yum update
sudo yum install aws-cli -y
cd /home/ec2-user
aws s3 cp s3://aws-codedeploy-ap-northeast-1/latest/install . --region ap-northeast-1
chmod +x ./install
sudo ./install auto

CodeDeployの設定

  • appspec.yml
  • CodeDeploy用ロール

appspec.yml

デプロイ時の設定として appspec.yml が必要です。
appspec.yml は、gitで管理してるアプリのルートディレクトリに置きます。

appspec.yml のサンプル

version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/campaign-app
hooks:
  BeforeInstall:
    - location: scripts/BeforeInstall.sh
  AfterInstall:
    - location: scripts/AfterInstall.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: scripts/ApplicationStart.sh
      timeout: 300
      runas: root
  ValidateService:
    - location: scripts/ValidateService.sh
      timeout: 300
      runas: root

BeforeInstallAfterInstall など、各処理が実行される順番については↓に詳しく書いてありました。 dev.classmethod.jp

CodeDeploy用ロール

ポリシー AWSCodeDeployRole を持つロール

githubとcircleciの連携

  • circle.yml
  • circleci用IAM

circle.yml のデプロイメント部分

deployment:
  deploy:
    branch: master                  # デプロイ対象にするブランチ名
    codedeploy:                     # circleciでCodeDeployを使う
      codedeploy-aplication-name:   # CodeDeployのアプリケーション名にする
        application_root: /
        region: ap-northeast-1
        revision_location:
          revision_type: S3         # デプロイ履歴のソースコードをs3に保存する
          s3_location:
            bucket: bucket-name     # デプロイ履歴のソースコードを保存するbucket名
            key_pattern: {BRANCH}-{SHORT_COMMIT}.zip    # 保存時のファイル名
        deployment_group: all

CodeDeployではデプロイしたアプリを圧縮してs3に保存することができるので、s3へ保存するファイル名を決める必要があります。
ファイル名は

key_pattern: {BRANCH}-{SHORT_COMMIT}.zip

の用に定義することができます。

パラメータ 意味
BRANCH デプロイするbranch名に変わる
masterブランチならmasterとなる
SHORT_COMMIT デプロイ時のcommitIDになる

circleciに設定するIAM

  • AWSCodeDeployFullAccess の権限をもつIAM

デプロイ失敗時の通知設定

  • SNS
  • lambda function

設定方法

SNS

  1. トピックを作成する

CodeDeploy

  1. CodeDeployの設定でトリガーに 1.で作成したSNSを追加する
    失敗時の通知をしたいので、イベントは Deployment fails
    f:id:naught00:20170409142214p:plain

lambdaの設定

項目 設定
設計図の選択 sns-message
トリガーの設定 作成したSNSトピック
'use strict';

// Ref: https://gist.github.com/vgeshel/1dba698aed9e8b39a464
console.log('Loading function');

const https = require('https');
const url = require('url');
// to get the slack hook url, go into slack admin and create a new "Incoming Webhook" integration
const slack_url = "{slackのwebhookurl}";
const region = 'ap-northeast-1'
const codedeploy_url = 'https://' + region + '.console.aws.amazon.com/codedeploy/home?region=' + region + '#/deployments/'
const slack_req_opts = url.parse(slack_url);
slack_req_opts.method = 'POST';
slack_req_opts.headers = {'Content-Type': 'application/json'};

exports.handler = function(event, context) {
  (event.Records || []).forEach(function (rec) {
    if (rec.Sns) {
      var req = https.request(slack_req_opts, function (res) {
        if (res.statusCode === 200) {
          context.succeed('posted to slack');
        } else {
          context.fail('status code: ' + res.statusCode);
        }
      });
      
      req.on('error', function(e) {
        console.log('problem with request: ' + e.message);
        context.fail(e.message);
      });
      
      var message = JSON.parse(rec.Sns.Message);
      var str = '*' + 'Application: ' + message.applicationName + ' deploymentGroupName: ' + message.deploymentGroupName + ' deploymentId: ' + message.deploymentId + '*' + ' ' + codedeploy_url + message.deploymentId;
      req.write(JSON.stringify({text: str})); // for testing: , channel: '@vadim'
      
      req.end();
    }
  });
};

その他

Railsアプリをデプロイする時にハマったことがありました。
詳しくは調べてませんが、下のような順番でデプロイしてるんじゃないかなと思ってます。

  1. circleciでビルドした結果を圧縮し、s3にデプロイ
  2. s3から各EC2へソースコードをデプロイ

ここで問題になるのが、 circleci内で bundle install した結果をそのままEC2にデプロイするということです。
ビルド環境(circleci)で bundle install したときのパスがそのまま設定されているため、EC2インスタンスとcircleciのビルド時の環境が違うと、パスが通らないことが有りアプリの起動に失敗します。
解決策としては、CodeDeployでアプリ起動前に bundle install をすることです。

bundle install --path ../vendor/bundle

の様な形で、デプロイするソースコード外にインストールする方法で回避しました。

参考

[公式チュートリアルではじめる]CircleCI+CodeDeployを使ったCD(継続的デプロイ) | Developers.IO
CircleCI、CodeDeploy、S3を使って、GitHubにpushしたらEC2にデプロイされる仕組みを作る - Qiita
CodeDeployのApplicationStopイベントフックはどう実行される? | Developers.IO
Amazon Web Services ブログ: AWS CodeDeployがアジア・パシフィック(東京)で利用可能になりました
AWS CodeDeployのデプロイ結果をSlackに流す | mediba Creator × Engineer Blog

TypeScriptでReactを書くときはStateのパラメータに?を付ける

TypeScriptでReactを書いているとき、Stateに複数パラメータを用意した時に ? を付けてオプションパラメータにします。
そうしないと、setState の更新時に全てのパラメータを更新しないといけなくなります。

なので、必ず付けろというよりは、パラメータを一つだけ更新したいときにはこうしたほうが良いという話でした。

もうちょっと詳しく

こんな感じで年齢と身長を表示するコンポーネントがあったとします。

import * as React from 'react';

interface IPersonProps {
}
interface IPersonState {
  age: number;
  height: number;
}

/**
 * Person
 */
export class Person extends React.Component<IPersonProps, IPersonState> {
  constructor(props: IPersonProps) {
    super(props);

    this.state = {
      age: 0,
      height: 0
    }
  }

  render() {
    return (
      <div className="person">
        <div>年齢 : { this.state.age }</div>
        <div>身長 : { this.state.height }</div>
      </div>
    );
  }
}

ここで、身長のみを更新するイベントを追加しようとしたとき下記のように書きたくなります。

  handleOnChangeHeight = (height: number) => {
    this.setState({
      height: height
    });
  }

ですが、この場合はコンパイル時エラーになります。
理由は↓です。

interface IPersonState {
  age: number;
  height: number;
}

パラメータがオプションになっていないため両方更新しないといけなくなります。
なので、 ? を付けてオプションパラメータにしてあげます。

interface IPersonState {
  age?: number;
  height?: number;
}

そうすることで、 setState 時に更新したいパラメータだけを更新することができます。

sendgrid-phpで添付画像を付けてメール送信する方法

phpでメール送信する方法の一つとしてSendGridがあります。

github.com

添付ファイルを付けてメール送信する方法についてのメモです。
※ 前提として、composerを使用して、sendgrid-phpをインストールしているものとします

テキストのみのメール

<?php
require_once "vendor/autoload.php";

$from = new SendGrid\Email("name", "email@example.com");
$subject = "件名";
$personalization = new SendGrid\Personalization();
$personalization->addTo(new SendGrid\Email(null, "send@example.com"));
$personalization->setSubject($subject);
$content = new SendGrid\Content("text/plain", "本文");

$mail = new SendGrid\Mail();
$mail->setFrom($from);
$mail->addPersonalization($personalization);
$mail->setSubject($subject);
$mail->addContent($content);

$sg = new \SendGrid("sendgridApiKey");
$response = $sg->client
    ->mail()
    ->send()
    ->post($mail);

テキスト+添付画像のメール

<?php
require_once "vendor/autoload.php";

$from = new SendGrid\Email("name", "email@example.com");
$subject = "件名";
$personalization = new SendGrid\Personalization();
$personalization->addTo(new SendGrid\Email(null, "send@example.com"));
$personalization->setSubject($subject);
$content = new SendGrid\Content("text/plain", "本文");

$mail = new SendGrid\Mail();
$mail->setFrom($from);
$mail->addPersonalization($personalization);
$mail->setSubject($subject);
$mail->addContent($content);

// 添付ファイル
$filePath = "path/to/file";
$attachment = new SendGrid\Attachment();
$handle = fopen($filePath, "rb");
$contents = fread($handle, filesize($filePath));
$attachment->setContent(base64_encode($contents));
$attachment->setFilename("filename.png");
$fileInfo = new FInfo(FILEINFO_MIME_TYPE);
$attachment->setType($fileInfo->file($filePath));
$mail->addAttachment($attachment);

$sg = new \SendGrid("sendgridApiKey");
$response = $sg->client
    ->mail()
    ->send()
    ->post($mail);

テキストメール送信の処理に対して追加したのは下記コードです。
処理としては、ファイルをバイナリで開いてBase64変換することだけです。
調べると、ファイルパスとファイル名を渡すと送信できるような記述が多くてハマりました。

// 添付ファイル
$filePath = "path/to/file";
$attachment = new SendGrid\Attachment();
$handle = fopen($filePath, "rb");
$contents = fread($handle, filesize($filePath));
$attachment->setContent(base64_encode($contents));
$attachment->setFilename("filename.png");
$fileInfo = new FInfo(FILEINFO_MIME_TYPE);
$attachment->setType($fileInfo->file($filePath));
$mail->addAttachment($attachment);

その他

テキストメールの改行が無視される

テキストメールを送信したときに、改行が無視されてしまうことがありました。
SendGridのダッシュボードから、テキストメールはそのまま送信するように設定すれば解決しました。

メール送信時のステータスコード確認

上記のソースコード送信後に下記を実行することでステータスコードを取得できます。

$response->statusCode()

200や202などがメール送信成功時に返ってくるステータスコードの様です。

参照

qiita.com

sendgrid.com

AWS CloudWatch Logs で選択可能なOSについて

以前は debian にインストールできなかった、 AWS cloudlogs のインストール可能なOSが増えていました。
regionの選択肢も増えて、アジアパシフィック (東京) ap-northeast-1 も選択できるようになっていました。

もともとは下記方法でawslogsを使おうとしてました。
いつもは、debianを使うのでubuntuにするしかないかなと思ってました。

dev.classmethod.jp

setupスクリプト

https://s3.amazonaws.com/aws-cloudwatch/downloads/awslogs-agent-setup-v1.0.py

インストール可能OS

https://hub.docker.com/r/takipone/docker-awslogs/~/dockerfile/

dockerfileでは

ADD awslogs.conf.dummy ./awslogs.conf

と書いてあって、dummyって何だろうと思ったけど、githubのRepositoryを見たら、ファイルがありました。

そして、こちらに記載されているものがインストール可能OSと出力先のリージョンが増えたものです。

docs.aws.amazon.com

setupスクリプト

https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py

インストール可能OS

せっかくなので、githubにDockerfileをpushして、dockerhubでビルドしてみました。

github.com

https://hub.docker.com/r/naughtldy/debian-awslogs/