読者です 読者をやめる 読者になる 読者になる

ECS+ALB+hakoでホットデプロイ

ホットデプロイが流行っているらしいので。

github.com

hakoはECS用のデプロイツールで社内ではいろいろと使われています。 ALBと組み合わせて使うと、頑張っている感じのホットデプロイまわりがシンプルになってよいです。

まだ正式にはALBに対応していないのですが、使う方法を後ろの席の人が教えてくれたので、メモしておきます。

2016/09/07 ALBに対応したようです。 https://github.com/eagletmt/hako/pull/11

hakoのインストール

gemでインストール。

gem install hako

EC2インスタンスの準備

とりあえず適当なインスタンスがあればいいのでecs-cliインスタンス群を起動。

ecs-cli configure \ 
  -r ap-northeast-1 \
  -c hello-hako

ecs-cli up \
  --keypair winebarrel \
  --capability-iam \
  --size 2 \
  --vpc vpc-... \
  --subnets subnet-... \
  --security-group sg-... \
  --instance-type m4.large

f:id:winebarrel:20160902232008p:plain

f:id:winebarrel:20160902232034p:plain

おけおけ。

タスク定義

以下のようなタスクを定義したyamlを用意。 だいたいはjsonにそのまま対応しているのんですが、微妙に差異があったりなかったり(リファレンス欲しいなぁ)

  • hello-hako.yml(ファイル名がサービス名・タスク名・クラスタ名になる)
scheduler:
  type: ecs
  region: ap-northeast-1
  cluster: hello-hako
  desired_count: 2
app:
  image: nginx
  memory: 300
  cpu: 256
  essential: true
  mount_points:
    - container_path: /usr/share/nginx/html
      source_volume: vol
    - container_path: /var/log/httpd
      source_volume: log
  port_mappings:
    - host_port: 0
      container_port: 80
additional_containers:
  busybox:
    image_tag: busybox
    cpu: 64
    memory: 256
    env:
      MSG: hello
    volumes_from:
      - source_container: app
    # entry_pointはまだ使えないっぽい
    command:
      - /bin/sh
      - -c
      - |
        while true; do
          date >> /usr/share/nginx/html/index.html
          echo $MSG >> /usr/share/nginx/html/index.html
          echo '<br>' >> /usr/share/nginx/html/index.html
          sleep 3
        done
volumes:
  vol: {}
  log:
    source_path: /var/log/httpd

デプロイ

以下のコマンドをたたいて、タスクをデプロイ。

$ hako deploy hello-hako.yml
I, [2016-09-02T22:42:59.125887 #10722]  INFO -- : Registered task definition: arn:aws:ecs:ap-northeast-1:...:task-definition/hello-hako:36
I, [2016-09-02T22:42:59.263683 #10722]  INFO -- : Updated service: arn:aws:ecs:ap-northeast-1:...:service/hello-hako
I, [2016-09-02T22:43:07.825926 #10722]  INFO -- : 2016-09-02 22:43:07 +0900: (service hello-hako) has started 2 tasks: (task adf37618-c758-456b-a35c-cbac2a4a34a4) (task 5b1412d1-ca3d-46d0-8ed8-f3b0fedd3904).
I, [2016-09-02T22:43:13.976431 #10722]  INFO -- : Deployment completed

そうするとサービスが作られます。

f:id:winebarrel:20160902232531p:plain

が、これは削除。(事前にNumber of tasks0にする)

f:id:winebarrel:20160902232624p:plain

ALBの作成

次にコマンドラインからALBを作成。

aws elbv2 create-load-balancer --name hello-hako --subnets subnet-xxx subnet-yyy --security-group sg-...

サービスの再作成

同じ名前でサービスを再作成。 このときにALBの設定もします。

f:id:winebarrel:20160902233046p:plain f:id:winebarrel:20160902233147p:plain f:id:winebarrel:20160902233131p:plain

あと、ターゲットグループのDeregistration delayは短めで。 f:id:winebarrel:20160902233230p:plain

で、サービス作成完了。

f:id:winebarrel:20160902233347p:plain

一応、ALBにアクセス。

f:id:winebarrel:20160902233533p:plain

おけおけ。

ホットデプロイ

yamlを以下のように変更してデプロイ。

    command:
      - /bin/sh
      - -c
      - |
        while true; do
          echo modified >> /usr/share/nginx/html/index.html 
          date >> /usr/share/nginx/html/index.html
          echo $MSG >> /usr/share/nginx/html/index.html
          echo '<br>' >> /usr/share/nginx/html/index.html
          sleep 3
        done
$ hako deploy hello-hako.yml
I, [2016-09-02T23:06:55.847629 #16101]  INFO -- : Registered task definition: arn:aws:ecs:ap-northeast-1:...:task-definition/hello-hako:37
...

デプロイと同時にサービスを監視。

$ while true; do echo `date +%X`" | "`curl -s http://hello-hako-1111294014.ap-northeast-1.elb.amazonaws.com/ | head -n 1`; sleep 1; done
230710秒 | Fri Sep 2 14:05:25 UTC 2016
...

しばらく待つとデプロイ完了。

$ hako deploy hello-hako.yml
I, [2016-09-02T23:06:55.847629 #16101]  INFO -- : Registered task definition: arn:aws:ecs:ap-northeast-1:...:task-definition/hello-hako:37
I, [2016-09-02T23:06:56.190146 #16101]  INFO -- : Updated service: arn:aws:ecs:ap-northeast-1:...:service/hello-hako
I, [2016-09-02T23:07:29.817474 #16101]  INFO -- : 2016-09-02 23:07:28 +0900: (service hello-hako) has started 2 tasks: (task c8467e26-c46b-41f7-87b1-f58690fdc1de) (task 058bd7d7-4cfe-4f1b-baf9-46b4d623581a).
I, [2016-09-02T23:07:29.817558 #16101]  INFO -- : 2016-09-02 23:07:28 +0900: (service hello-hako) has begun draining connections on 1 tasks.
I, [2016-09-02T23:07:29.817599 #16101]  INFO -- : 2016-09-02 23:07:28 +0900: (service hello-hako) deregistered 1 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:...:targetgroup/ecs-hello-hello-hako/f1593d29b6a39517)
I, [2016-09-02T23:07:43.310115 #16101]  INFO -- : 2016-09-02 23:07:38 +0900: (service hello-hako) registered 2 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:...:targetgroup/ecs-hello-hello-hako/f1593d29b6a39517)
I, [2016-09-02T23:07:43.310198 #16101]  INFO -- : 2016-09-02 23:07:38 +0900: (service hello-hako) has stopped 1 running tasks: (task a1bb3d3a-ebfa-45dd-8022-907f9c212d49).
I, [2016-09-02T23:07:56.160512 #16101]  INFO -- : 2016-09-02 23:07:51 +0900: (service hello-hako) has begun draining connections on 2 tasks.
I, [2016-09-02T23:07:56.160670 #16101]  INFO -- : 2016-09-02 23:07:51 +0900: (service hello-hako) deregistered 1 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:...:targetgroup/ecs-hello-hello-hako/f1593d29b6a39517)
I, [2016-09-02T23:08:08.035586 #16101]  INFO -- : 2016-09-02 23:08:05 +0900: (service hello-hako) registered 1 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:...:targetgroup/ecs-hello-hello-hako/f1593d29b6a39517)
I, [2016-09-02T23:08:08.035677 #16101]  INFO -- : 2016-09-02 23:08:04 +0900: (service hello-hako) has stopped 1 running tasks: (task 31877197-9498-45be-b345-8f3457e4dd09).
I, [2016-09-02T23:08:18.055483 #16101]  INFO -- : Deployment completed

監視の方は…

23時07分37秒 | Fri Sep 2 14:05:24 UTC 2016
23時07分38秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分39秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分40秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分41秒 | modified
23時07分43秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分44秒 | modified
23時07分45秒 | modified
23時07分46秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分47秒 | modified
23時07分48秒 | modified
23時07分49秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分50秒 | modified
23時07分51秒 | modified
23時07分52秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分54秒 | modified
23時07分55秒 | modified
23時07分56秒 | Fri Sep 2 14:05:25 UTC 2016
23時07分57秒 | modified
23時07分58秒 | modified
23時07分59秒 | Fri Sep 2 14:05:25 UTC 2016
23時08分00秒 | modified
23時08分01秒 | modified
23時08分02秒 | Fri Sep 2 14:05:25 UTC 2016
23時08分03秒 | modified
23時08分04秒 | modified
23時08分06秒 | modified
23時08分07秒 | modified

おけおけ。 ダウンタイムないですね。

DynamoDBとGoogleスプレッドシートを相互同期するやつ

confdのDynamoDBをGoogleスプレッドシートで管理したかったので作りました。

docs.google.com

使い方

まず、以下のようにDynamoDBのテーブルを作ります。(confdのと同じ

aws dynamodb create-table \
    --region ap-northeast-1 --table-name hello \
    --attribute-definitions AttributeName=key,AttributeType=S \
    --key-schema AttributeName=key,KeyType=HASH \
    --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

ついでにddbcliを使ってテーブルにデータを入れておきます。

$ ddbcli
ap-northeast-1> insert into hello (key, value) values ('foo', 'bar');
// 1 row changed (0.29 sec)

ap-northeast-1> select all * from hello;
[
  {"key":"foo","value":"bar"}
]
// 1 row in set (0.07 sec)

したらば上記のスプレッドシートをコピーします。

コピーしたらスクリプトエディタを開きます。

f:id:winebarrel:20160901213655p:plain

config.gsというスクリプトがあるので、それを適当に編集します。

f:id:winebarrel:20160901213835p:plain

メニューからDynamoDBLoad from DynamoDBを選択すると、DynamoDBからスプレッドシートにデータが読み込まれます。

f:id:winebarrel:20160901214114p:plain

スプレッドシートを適当に編集して、DynamoDBSave to DynamoDBを選択すると、データがDynamoDBに保存されます。

f:id:winebarrel:20160901214244p:plain

$ ddbcli
ap-northeast-1> select all * from hello;
[
  {"key":"zoo","value":"baz"},
  {"key":"foo","value":"BAR"}
]
// 2 rows in set (0.05 sec)

参考にしたりフォークしたりしたやつ

適当なECSデプロイスクリプト

JSONYAMLプリプロセス出来ると、なおうれし。

{
  "containerDefinitions": [
    {
      "volumesFrom": [],
      "memory": 300,
      "portMappings": [
        {
          "hostPort": 0,
          "containerPort": 80,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "mountPoints": [
        {
          "containerPath": "/usr/local/apache2/htdocs",
          "sourceVolume": "vol"
        },
        {
          "containerPath": "/var/log/httpd",
          "sourceVolume": "log"
        }
      ],
      "name": "httpd",
      "environment": [],
      "image": "httpd:2.4",
      "cpu": 10
    },
    {
      "volumesFrom": [
        {
          "sourceContainer": "httpd"
        }
      ],
      "memory": 200,
      "portMappings": [],
      "essential": true,
      "entryPoint": [
        "sh",
        "-c"
      ],
      "mountPoints": [],
      "name": "testapp",
      "environment": [],
      "image": "busybox",
      "command": [
        "/bin/sh -c 'while true; do echo xxx >> /usr/local/apache2/htdocs/index.html; sleep 3; done'"
      ],
      "cpu": 10
    }
  ],
  "volumes": [
    {
      "name": "vol"
    },
    {
      "host": {
        "sourcePath": "/var/log/httpd"
      },
      "name": "log"
    }
  ],
  "family": "test"
}
  • ec-deploy.sh
#!/bin/bash
set -e

if [ -z "$1" -o -z "$2" ]; then
  echo "usage: $0 SERVICE TASK_JSON"
  exit
fi

SERVICE=$1
TASK_JSON=$2
TASK_NAME=$(jq -r .family $TASK_JSON)
TASK_ROLE=arn:aws:iam::...:role/...

function get_last_event() {
  EVENT=$(aws ecs describe-services --services $1 | jq -r '.services[0].events[0] | [.id, .message] | @tsv')
  EVENT_ID=$(awk -F '\t' '{print $1}' <<< "$EVENT")
  EVENT_MSG=$(awk -F '\t' '{print $2}' <<< "$EVENT")
}

get_last_event $SERVICE
LAST_EVENT_ID="$EVENT_ID"
LAST_EVENT_MSG="$EVENT_MSG"

aws ecs register-task-definition \
  --task-role-arn=$TASK_ROLE \
  --cli-input-json file://$TASK_JSON > /dev/null

aws ecs update-service --service $SERVICE --task-definition $TASK_NAME > /dev/null

while true; do
  get_last_event $SERVICE

  if [ "$EVENT_ID" != "$LAST_EVENT_ID" ]; then
    echo $EVENT_MSG

    if [[ "$EVENT_MSG" =~ has\ reached\ a\ steady\ state ]]; then
      break
    fi

    LAST_EVENT_ID="$EVENT_ID"
    LAST_EVENT_MSG="$EVENT_MSG"
  fi
done

デプロイ例

$  ecs-deploy.sh hello task.json
(service hello) has started 1 tasks: (task 2b3ab535-05f7-40be-a2a7-b661e56e9527).
(service hello) registered 1 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:...:targetgroup/default/afb88bc90fe30b1a)
(service hello) has begun draining connections on 1 tasks.
(service hello) has stopped 1 running tasks: (task 9cc4b054-0fa5-4083-a23d-e3a497400bd5).
(service hello) has reached a steady state.

graceful restart可能なHAProxyのDockerイメージ

ふつうに作れそうだったので作ってみた。 以下、関連ファイル。

  • Dockerfile
FROM ubuntu:latest
MAINTAINER Genki Sugawara <sgwr_dts@yahoo.co.jp>

RUN apt-get update
RUN apt-get install -y haproxy

ADD haproxy.cfg /

ADD supervisor.sh /
RUN chmod +x /supervisor.sh

CMD ["/supervisor.sh"]
  • supervisor.sh
#!/bin/bash
RELOAD=0

function set_reload() {
  RELOAD=1
}

trap set_reload USR1

haproxy -f /haproxy.cfg &

while true; do
  if [ $RELOAD -eq 1 ]; then
    haproxy -f /haproxy.cfg -sf `ps -o pid,command | awk '$2 == "haproxy" {print $1}'` &
    RELOAD=0
  fi

  sleep 1
done
  • haproxy.cfg
defaults
  timeout connect 5000ms
  timeout client 50000ms
  timeout server 50000ms

listen example
  mode http
  bind :80
  server example.com example.com:80

これをdocker build

$ docker build -t haproxy .
Sending build context to Docker daemon 4.096 kB
Step 1 : FROM ubuntu:latest
 ---> 3876b81b5a81
Step 2 : MAINTAINER Genki Sugawara <sgwr_dts@yahoo.co.jp>
 ---> Using cache
 ---> 249acbf0fe6a
Step 3 : RUN apt-get update
 ---> Using cache
 ---> f2e5cbbb164f
Step 4 : RUN apt-get install -y haproxy
 ---> Using cache
 ---> 3f518b918618
Step 5 : ADD haproxy.cfg /
 ---> Using cache
 ---> bf5df2e90b51
Step 6 : ADD supervisor.sh /
 ---> Using cache
 ---> 98ae226f35b8
Step 7 : RUN chmod +x /supervisor.sh
 ---> Using cache
 ---> 1ae6d30dca44
Step 8 : CMD /supervisor.sh
 ---> Using cache
 ---> eaae080bd487
Successfully built eaae080bd487

で、docker run

$ docker run -p 10080:80 --name haproxy haproxy
$ curl localhost:10080
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head>
            <title>404 - Not Found</title>
        </head>
        <body>
            <h1>404 - Not Found</h1>
        </body>
</html>

docker execでUSR1を投げる。

$  docker exec haproxy kill -USR1 1

するとgraceful restartされる

[WARNING] 241/130328 (6) : Stopping proxy example in 0 ms.
[WARNING] 241/130328 (6) : Proxy example stopped (FE: 1 conns, BE: 1 conns).
$ curl localhost:10080
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        <head>
            <title>404 - Not Found</title>
        </head>
        <body>
            <h1>404 - Not Found</h1>
        </body>
</html>

おけおけ。

他のシグナルのこととか、そのままではプロダクションには使えないけど、同じ戦略でいけそう。

Docker+confdはなかなかよいと思う…

Dockerでいろいろやりたいことがあったので、いろいろ調べてました。

具体的には

  • 設定ファイルのテンプレート化
  • 設定の動的な管理と、変更に伴う継続的な設定の反映
    • 要するに設定が変更されたら設定ファイルが更新されてミドルウェアがリロードされると

でまあEntrykitconfdを教えてもらったんですが、継続的な設定の更新を考えるとconfdがよかろうと。codepの機能は必要ですが、それだけならdumb-initで十分そうでした。

で、以下のようなファイルを用意。

  • Dockerfile
FROM nginx:latest
MAINTAINER Genki Sugawara <sgwr_dts@yahoo.co.jp>

ENV DUMB_INIT_VERSION 1.1.3
ENV CONFD_VERSION 0.11.0

RUN apt-get update
RUN apt-get install -y --force-yes curl

RUN curl -sOL https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_amd64.deb
RUN dpkg -i dumb-init_*.deb
RUN rm dumb-init_*.deb

RUN curl -sOL https://github.com/kelseyhightower/confd/releases/download/v${CONFD_VERSION}/confd-${CONFD_VERSION}-linux-amd64
RUN chmod +x confd-*
RUN mv confd-* /bin/confd
RUN mkdir -p /etc/confd/{conf.d,templates}
ADD nginx_default.conf.toml /etc/confd/conf.d/
ADD nginx_default.conf.tmpl /etc/confd/templates/

ADD init.sh /
RUN chmod +x /init.sh

ENV CONFD_ARGS -backend dynamodb -table confd

ENV AWS_REGION ap-northeast-1
ENV AWS_DEFAULT_REGION ap-northeast-1
ENV AWS_ACCESS_KEY_ID ...
ENV AWS_SECRET_ACCESS_KEY ...

CMD /init.sh
  • init.sh
#!/usr/bin/dumb-init /bin/sh
confd $CONFD_ARGS &
nginx -g 'daemon off;'
  • nginx_default.conf.toml
[template]
src = "nginx_default.conf.tmpl"
dest = "/etc/nginx/conf.d/default.conf"
keys = [
    "/nginx/statuscode"
]
check_cmd = "service nginx configtest"
reload_cmd = "service nginx reload"
  • nginx_default.conf.tmpl
server {
  listen       80 default_server;
  server_name  _;

  location / {
    return {{getv "/nginx/statuscode"}};
  }
}

バックエンドのdynamodbは以下の通り。

aws dynamodb create-table \
  --table-name confd \
  --attribute-definitions AttributeName=key,AttributeType=S \
  --key-schema AttributeName=key,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
$ ddbcli
ap-northeast-1> show create table confd;
CREATE TABLE `confd` (
  `key` STRING HASH
) read=1 write=1

ap-northeast-1> select all * from confd;
[
  {"key":"/nginx/statuscode","value":"404"}
]
// 1 row in set (0.05 sec)

バックエンドが選べるのはなかなかよいですね。 このコンテナをローカルで動かそうと思ったら、-backend envで適当な値を突っ込めるし。

で、これをdocker buildしてdocker runするとcurlで404が返る。

$ docker build -t awesome-confd .
...
$ docker run --rm -p 10080:80 --name awesome-confd -it awesome-confd
2016-08-28T08:34:26Z 1f4823caba59 confd[8]: INFO Backend set to dynamodb
2016-08-28T08:34:26Z 1f4823caba59 confd[8]: INFO Starting confd
2016-08-28T08:34:26Z 1f4823caba59 confd[8]: INFO Backend nodes set to
2016-08-28T08:34:26Z 1f4823caba59 confd[8]: INFO DynamoDB table set to confd
2016-08-28T08:34:26Z 1f4823caba59 confd[8]: INFO /etc/nginx/conf.d/default.conf has md5sum 4dce452bf8dbb01f278ec0ea9ba6cf40 should be 1e1181119f8355e81d3b0a7de2859d5a
2016-08-28T08:34:26Z 1f4823caba59 confd[8]: INFO Target config /etc/nginx/conf.d/default.conf out of sync
2016-08-28T08:34:26Z 1f4823caba59 confd[8]: INFO Target config /etc/nginx/conf.d/default.conf has been updated
$ curl localhost:10080
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.11.3</center>
</body>
</html>

おけおけ。これで-watchオブションつければ設定の監視できるぞ、、と思ったら

2016-08-28T08:36:32Z 63120fea70d1 confd[7]: INFO Watch is not supported for backend dynamodb. Exiting...

残念(あたりまえか…)。

まあ、手動でreloadすればいいかと、CONFD_ARGSとinit.shを以下のように書き換え。

ENV CONFD_ARGS -backend dynamodb -table confd -onetime
#!/usr/bin/dumb-init /bin/sh
confd $CONFD_ARGS
nginx -g 'daemon off;'

再度、docker runしたあと、dynamodbの値を書き換え。

ap-northeast-1> update confd set value = '401' where key = '/nginx/statuscode';
// 1 row changed (0.08 sec)

condを実行。

$ docker exec -it awesome-confd confd -backend dynamodb -table confd -onetime
2016-08-28T08:40:44Z 4b3509b1fd93 confd[49]: INFO Backend set to dynamodb
2016-08-28T08:40:44Z 4b3509b1fd93 confd[49]: INFO Starting confd
2016-08-28T08:40:44Z 4b3509b1fd93 confd[49]: INFO Backend nodes set to
2016-08-28T08:40:44Z 4b3509b1fd93 confd[49]: INFO DynamoDB table set to confd
2016-08-28T08:40:44Z 4b3509b1fd93 confd[49]: INFO /etc/nginx/conf.d/default.conf has md5sum 1e1181119f8355e81d3b0a7de2859d5a should be 306f95f0ae59a29344ed21eb0c7886c9
2016-08-28T08:40:44Z 4b3509b1fd93 confd[49]: INFO Target config /etc/nginx/conf.d/default.conf out of sync
2016-08-28T08:40:44Z 4b3509b1fd93 confd[49]: INFO Target config /etc/nginx/conf.d/default.conf has been updated
$ curl localhost:10080
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.11.3</center>
</body>
</html>

おけおけ。

機密情報について

機密情報については上記DynamoDBとは別のとこに置くと思うんですよね。 VaultとかCredStashとか。

現状のconfdだと、そこから引っ張るのは難しいので、init.shとかで機密情報を引っ張ってきて環境変数に保存して利用とかですかねぇ。環境変数に機密情報を入れるのはもにょるんですが、rootしか見れないだろうし、まあいいかなぁ…

PR眺めてたら機密情報を出し入れするやつがありました。

github.com

ですが、この機能をconfd側持ってしまうのは微妙かな、と考えて任意のシステムコマンドを実行できるテンプレート関数を追加するPRを投げてみました。

github.com

なんとなくリジェクトされる気がする…。

本来的にはプラガブルになるといいんでしょうけど、そこまで工数さくほどかなー

MySQLのPASSWORD関数のRubyバインディング

一身上の都合によりMySQLのPASSWORD関数のRubyバインディングを作った。

github.com

実装はPASSWORD関数の実態であるmake_scrambled_password()を拡張ライブラリから呼び出しているだけです。

使い方は以下の通り。

require "mysql_make_scrambled_password"
include MysqlMakeScrambledPassword

# PASSWORD()
make_scrambled_password("FOOBARZOO")
#=> "*70B45D8E18771E93E011F30DB91E80D6306EA4C3"

# OLD_PASSWORD()
make_scrambled_password_323("FOOBARZOO")
#=> "6e35068701b1cc8b"

追記

go版も作りました

github.com

Cfdef: CloudFrontをコード化するやつ

github.com

こんなん。

export AWS_ACCESS_KEY_ID='...'
export AWS_SECRET_ACCESS_KEY='...'
cfdef -e -o CFfile  # export CloudFront
vi CFfile
cfdef -a --dry-run
cfdef -a            # apply `CFfile`
require 'other/cffile'

distribution "EXAMPLEID" do
  aliases do
    quantity 0
  end
  origins do
    quantity 2
    items do |*|
      id "Custom-ehxample.cpm"
      domain_name "example.cpm"
      origin_path ""
      custom_headers do
        quantity 0
      end
      custom_origin_config do
        http_port 80
        https_port 443
        origin_protocol_policy "http-only"
        origin_ssl_protocols do
          quantity 3
          items ["TLSv1", "TLSv1.1", "TLSv1.2"]
        end
      end
    end
    items do |*|
      id "S3-example"
      domain_name "example.s3.amazonaws.com"
      origin_path ""
      custom_headers do
        quantity 0
      end
      s3_origin_config do
        origin_access_identity ""
      end
    end
  end
  default_cache_behavior do
    target_origin_id "S3-example"
    forwarded_values do
      query_string false
      cookies do
        forward "none"
      end
      headers do
        quantity 0
      end
    end
    trusted_signers do
      enabled false
      quantity 0
    end
    viewer_protocol_policy "allow-all"
    min_ttl 0
    allowed_methods do
      quantity 2
      items ["GET", "HEAD"]
      cached_methods do
        quantity 2
        items ["GET", "HEAD"]
      end
    end
    smooth_streaming false
    default_ttl 86400
    max_ttl 31536000
    compress false
  end
  cache_behaviors do
    quantity 0
  end
  custom_error_responses do
    quantity 0
  end
  comment ""
  price_class "PriceClass_All"
  enabled true
  viewer_certificate do
    cloud_front_default_certificate true
    minimum_protocol_version "SSLv3"
    certificate_source "cloudfront"
  end
  restrictions do
    geo_restriction do
      restriction_type "none"
      quantity 0
    end
  end
  web_acl_id ""
end