Screaming Loud

日々是精進

EC2の起動時にEBSを紐付ける設定

EC2の起動時にEBSを紐付ける設定をするのに割と手間取ったのでメモ

利用するのは、起動テンプレートのユーザーデータです。

ユーザーデータに関してはこちら。

docs.aws.amazon.com

設定

今回はECSのインスタンスに対してdockerコンテナが立ち上がる前にebsをマウントさせたいため、cloud-boothookにその設定を書きます。

以下はcloud-boothookに関する引用です。

cloud-boothook ユーザーデータ形式は、起動プロセスでユーザーデータシェルスクリプトよりも前に実行されます cloud-boothook ユーザーデータは、インスタンス起動のたびに実行されるため、ブートフックが複数回実行されることを防ぐためのメカニズムを作成する必要があります。

docs.aws.amazon.com

実際に書くユーザーデータは以下

Content-Type: multipart/mixed; boundary="==BOUNDARY=="
MIME-Version: 1.0

--==BOUNDARY==
Content-Type: text/cloud-boothook; charset="us-ascii"

# Create /mnt/ebs folder
cloud-init-per once mkdir_ebs mkdir -p /mnt/ebs

#cloud-boothook
#!/bin/sh
# Install awscli
curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" \
  && python get-pip.py
pip install awscli

aws configure set region ap-northeast-1

# Install JQ JSON parser
yum install -y jq

# Find Volumes
INSTANCE_AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
VOLUME_ID=`aws ec2 describe-volumes --filters Name=status,Values=available  Name=availability-zone,Values=$INSTANCE_AZ | jq -r '.Volumes[] |.VolumeId' | head -n 1` 2>&1 >> /tmp/cloud-boot.log
echo "volumeID: ${VOLUME_ID}" 2>&1 >> /tmp/cloud-boot.log

## Attach Volumes
if [ "${VOLUME_ID}" != "" ]; then
  aws ec2 attach-volume  --volume-id $VOLUME_ID --instance-id $INSTANCE_ID --device  /dev/xvde 2>&1 >> /tmp/cloud-boot.log
  COUNT=0
  while [ ! -e /dev/xvde ] && [ $COUNT -le 4 ]; do
    echo "Waiting for disk to appear.. /dev/xvde" 2>&1 >> /tmp/cloud-boot.log
    sleep 1
    COUNT=$(($COUNT + 1))
  done
  mkfs -t ext4 /dev/xvde 2>&1 >> /tmp/cloud-boot.log
  echo -e '/dev/xvde /mnt/ebs ext4 defaults,user 0 2' >> /etc/fstab
fi

mount -a

chmod -R 775  /mnt/ebs

--==BOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

# Set any ECS agent configuration options
echo ECS_ENABLE_CONTAINER_METADATA=true >> /etc/ecs/ecs.config
echo ECS_CONTAINER_STOP_TIMEOUT=2m >> /etc/ecs/ecs.config
echo "ECS_CLUSTER=my-ecs-cluster" >> /etc/ecs/ecs.config

--==BOUNDARY==--

軽く解説

BOUNDARYでcloud-boothookとshellscriptを分けています。

#!/bin/sh と書くとそれ以降shellscriptが使えます。

ebsをマウントするにはawsコマンドを入れる必要があるので、pipで入れます。 入れたawsコマンドで必要なregion設定を行い、実際の余っているebsを使います。

ebsの検索時にAZを指定しないとマウントできないのが割とハマりどころでした。 EBSのAZはあまり意識しないので忘れがちです。

余ってるVolumeがありVolumeIDが取得できたらマウントします。

マウント出来たかどうかを見るために、sleepで確認します。

あとはECSで使う設定です。

注意

cloud-boothook内で yum update をしてはいけません。 ecs-agentがyumで管理されているため、boothook内でyumを上げると、ecs-agentも勝手に上がるようになってしまいます。

まとめ

AZ以外に個人的にハマったポイントは cloud-init per once でずっとawsコマンドのインストールをしており、インストール出来ていなかったという点です。 全部 cloud-init per always に全部書いてもいいですが、cloud-boothook上のshellで普通に書いたほうが結果的には楽でした。

自分のドメイン取得してWebサイトを作った

お久しぶりです。 自分のドメインを取得してページ作りました。

tl;dr

yutosuzuki.info

やったこと

  1. お名前.comでドメイン取得
  2. AWSのS3の静的ホスティングで公開
  3. CloudFrontを噛まして、S3直では見させないように。
  4. Reactで実際の見せるサイトを構築
  5. OGP対応

なんで?

どこかのWebサービスドメインではなく、自分のドメインを持っておきたかったから。

Webサービスの自己紹介ページとかは、自分が出したい情報出したくない情報がテンプレート化されており、自由に選択できない。

自分のフォーマットで作りたかった

なんでこの技術選択

ブログは引き続きこのはてなブログを使うし、ブログを立てるわけではなかったので、WordPressはオーバースペック。

AWSは普段自分が使っていたため、使いやすかった。

HTMLべた書きじゃないのは、HTML書くのが大変だから。 Reactとかコンポーネントベースのもので書けば、再利用できるので必然。

styled-componetを使ってみたら、CSSが分離せずVue.jsぽく書けたし、constructorがstyleであふれることなく出来てよかった。

まとめ

自分のサイト持つと、いろいろ遊べるので、オススメです。

思考し言語化することが次のフェーズへの道

平成最後のブログですね。

最後なのでちょっとエモい話を投下します。

成長の定義

今回の記事では「成長」の定義とは、何かをできるようになることとします。

何かできるようになるとは、自分の中でパターンを持つことと思っています。 そして、パターンを持つことで思考するフェーズを飛ばし、反射神経で実行することが可能になります。 これが直感だと思っています。

https://4.bp.blogspot.com/-WjzLI8e02OE/UZSs4ybZHlI/AAAAAAAAS_0/m_fh1wEkkGA/s400/graph_up.png

成長の過程

なにかを初めて実行するときはいろいろなやり方があると思います。 それは、自分の中でパターンができていないからでしょう。

とりあえずやってみるから入る

いわゆるとりあえず出力から入るパターンです。 兎にも角にも、何かやってみないことにはわからないので、とりあえずやってみるというのは、手探りの状態では一番手っ取り早く、成果も出やすいでしょう。

むしろ、先人がいないところでは、これをしないとダメでしょう。

量を質に転換するというパターンはこれでしょう。

入力から入る

情報収集・できる人の動きを見るなどの入力から入る人もいると思います。

すでに似たようなことを誰かがやっている場合は、同じ轍を踏まないように、まず情報収集は大事だと思います。

とにかく情報を大量にインプットして、パターンを組み上げるというパターンもあります。 多読など大量に情報を浴びることでそれを実現できますね。

できるようになる

慣れてくると、自分の中でパターンが生成され、反射的に実行できるようになる。

いわゆる成長したという状態です。

タスクをこなす上でこのパターン化が完成すると思考を通さないので、実行速度があがります。

確かに、直感での実行は早いですが、成長という面では止まってしまっています。 なぜなら、成長はパターンを持つことと考えているからです。

パターン化による弊害

出力パターンが同じときにはうまくいきますが、状況が変わった時点でそのパターンは使えなくなることがあります。

自分はスポーツをやっているので、その感覚を実感するときがあります。

まず全然できない状態から、なんとなく練習をやっていると、段々上達していき上手くなっていきます。 しばらく、なんとなくできている時期が続きますが、突然スランプに陥ることがあります。

例えば、テニスでストロークの練習をしていて、なんとなく打てるようになるとします。 どうやって打つのかなど本を読んで入力を行っているかもしれません。

しかし、身体の調子・精神的な調子・外的状況によって自分が今までどうやって打っていたかよくわからなくなり、突然打てなくなります。

スポーツでは常に同じ相手とやるわけではないので、状況が同じとは限りません。 大きく外的要因が変わりますが、そのたびにスランプのような状態に陥っていては大変です。

思考と言語化

では、さらにもう一歩進むにはどうするのか?

それは思考しながら言語化しながら、練習を行うことです。

テニスの話が続きますが、以下のように言語化しながら練習するのです。

「このくらいの高さで飛んできたボールはここらへんの打点で打つ」 「自分の腰くらいのときは、テイクバックをこれくらいからここへ」

入力と出力と言語化

この「思考しながら言語化」は入力と出力両方やっていて初めてできる次のフェーズでしょう。

そもそも出力だけでは、ただ無為に時間がすぎるでしょう。 逆に、入力しているだけでは自分のパターンに当てはまらないことが多く、落とし込むには相当の種類の入力を必要とするでしょう。

思考しながら実行に移すと、初めは以前よりも対応が遅くなったりわからなくなるので、よりできなくなるかもしれません。 しかし、思考して自分がやっていることを言語化すると、外的・心身の状況に対して自分の言葉で対処できるようになります。

これはスランプの脱出方法にほかなりません。

実務における思考と言語化

スポーツで例をあげましたが、これは一般的な仕事でも変わらないと思っています。

仕事、特に知識労働でのパフォーマンスは、大量のインプットで支えていることが多いでしょう。 また、何か新しいことをやる場合は、とにかくやってみるというスタンスで始めることが多いでしょう。

上記2つは一般的にエンジニアなどでやっていることが多いと思います。 やっていなければ、落ちぶれていくでしょう。

ただ、次のフェーズに行くには、言語化が必要だと思っています。

例えば、

上記だけであれば、ただ知っている、作ったことがあるという段階です。

「なんとなくできる」から「できる」へ昇華させるには、思考による言語化が必要です。

自分で思考し言語化した場合、

という次のフェーズにたどり着くでしょう。

まとめ

思考と言語化という話をしましたが、それには入力と出力という土台があってこそなので、そこはしっかりやっていくべきですね。

最近読んだ漫画の「アオアシ」は、その思考の過程が描かれてめっちゃ面白いので、ぜひ読んでみてください。

参考資料

learningpatterns.sfc.keio.ac.jp

books.rakuten.co.jp

goaでXMLを生成する

goaとはgoのWebフレームワークです。

goa.design

goでリクエストとレスポンスを定義してあげると、APIとswagger周りを生成してくれるので、便利なライブラリです。

詳しくはこちら

The goa API Design Language · goa :: Design-first API Generation

今回goaでjsonの例はあるのですが、XMLの生成のサンプルがないので作りました。 基本的にはgoの encodign/xml を参考にすればできました。

design

基本的には Attribute を追加すると、タグの間に挿入されます。

属性やCDATAなど内容以外のものはMetadataとしてAttributeに定義してあげると反映されます。

design

xmlとそれに対応したgoaの書き方をまとめます。

ベース

<User></User>
var User = MediaType("vnd.application/user+xml", func() {
    TypeName("User")
    ContentType("application/xml")
})

属性

<User id="1234"></User>
var User = Type("User", func() {
    Attribute("ID", String, "user id", func() {
        Metadata("struct:tag:xml", "id,attr")
    })
    Required("ID")
})

Metadata内の id が属性名で、attrは属性であることを明示している。

内容

<User>
  <Name>太郎</Name>
</User>
var User = Type("User", func() {
    Attribute("Name", String)
})

CDATA

<URL><![CDATA[http://www.google.com]]></URL>
var URL = Type("URL", func() {
    Attribute("CDATA", String, func() {
        Metadata("struct:tag:xml", ",cdata")
    })
})

属性と同じように cdata はcdataであることを示している。 キー名はないので空。

配列

<User>
   <Friend>次郎</Friend>
   <Friend>三郎</Friend>
</User>
var User = Type("User", func() {
    Attribute("Friend", ArrayOf(String))
})