2017年12月4日月曜日

指定された国のタイムゾーン情報をどうやって設定すれば良いか調べる方法

海外向けサービスを構築したときのお話です。
たとえば突然「インドネシア向けサービスを作りたいんだけどさ」みたいなことを言われたとします。
専用サーバを構築するとして、そのサーバの時刻をその国のタイムゾーンで合わせないといけないわけです。
どうやって合わせる…?ていうか時差は何時間?

まずその国の「アルファベット2文字の国コード」を確認します。
例えばこのWikipediaのページで確認できます。
日本なら、「JP」ですね。インドネシアは「ID」です。
次に、このWikipediaのページを開いて、その2文字の国コードの行を検索します。
「CC」の列が「ID」の行です。
4つありますね。
これにより、インドネシアにはタイムゾーンが4種類あり、GMT+07:00、GMT+08:00、GMT+09:00の3種類があるわけです。
なので、エンジニアとしては「調べたところ、インドネシアは時差が4種類ほどあるようなのですが、どれにすれば良いでしょうか?」みたいなことを言って確認する必要が出てくるわけです。

以上、簡単ですが、何気に重要なタイムゾーンの選定方法でした。


2017年12月1日金曜日

社内でガチの初心者向けDocker勉強会をしました

その時のスライドがこちらです。




勉強会当日には、主に「Dockerを一ミリも使ったことがないエンジニア」に集まっていただき、Dockerってどんな感じのソフトウェア・システムなのか、自社ではどんな感じで開発に利用してるのかというような観点でお話しました。

デザイナーも来てくれたりしたので、具体的な操作方法は抜きにしてとにかく概要を理解してもらうことに重点を置いたスライドになりました。

本当はハンズオンのパートを作る時間を捻出できなかっただけですが…。

社内では割と好評だったので(そもそも勉強会をオラッっとやる人が少ない…)、次回はECSについて説明するようなスライドを作成できればと考えています。


2017年8月2日水曜日

オライリーのDocker本を読んだり色々な記事を読んだりしたので自分なりにまとめる(Dockerfileについて)

Docker経験値0だった私も何冊か本を読んで実際にDockerfileを見たりいじったりしてみてECSに試行錯誤でデプロイしてみたりして、ようやく少しずつ慣れてきた感が出てきました。
しかしまだまだ「バッチリできます!」というには力不足間は否めなく…いやそういう話じゃない。
折角本を読んだりネット上の先輩たちの記事を読んだりして色々覚えたのだから、それをまとめておきたいと思います。
実はもうすでに日本語での公式なDockerfileベストプラクティスが存在するので、意味がないといえばたぶん意味はないのですが、アウトプットをして学習にいったんの区切りをつけるという意味で自己満足も兼ねて書きたいと思います。

Dockerfileについての基本的なこと

  • 1コンテナ1プロセスを起動するつもりでDockerfileを書く
    • 1プロセスとはつまりnginxとmod-phpによるPHPアプリケーションなら2つのコンテナを用意するということ
  • コマンドは大文字で書く( RUN, COPY, EXPOSE 等)
  • #によるコメントアウトが使える
  • FROM 文は先頭に1行だけ書く
  • CMD 文は末尾に1行だけ書く

Dockerfile についてやるべきこと・意識すべきこと

  • DockerHub のイメージは「公式」とわかるもの以外むやみやたらに使わずできるだけ Dockerfile を読んでから利用を判断する
    DockerHub は Docker イメージの共有サイトだが、有象無象のコンテナイメージが登録されており、(その意図はなくても)セキュリティ的に危険なコンテナが存在する可能性は十分にある。 DockerHubが公式に提供しているNginx など、ソフトウェアベンダ(団体)の公式イメージ以外は基本的に利用しないことで基本的なセキュリティある程度確保することができる。
  • 構文にはshell形式とexec形式が利用できるものがあるが基本的にexec形式を利用する
    例えばCMDコマンドはshell形式とexec形式を利用することができる。
    shell形式は指定された文字列を /bin/sh -c に渡すことでコマンドを実行する。
    その場合に、exec形式だと/bin/shに対する攻撃のような文字列の実行を回避することができる。
  • FROM 文で引用するイメージのタグに latest は利用しない
    Docker はタグと呼ばれるバージョン情報で Docker イメージのバージョンを管理、選別することができる。
    latest は文字のごとく最新のバージョンのイメージに使われるタグのように見せかけて、実は Docker の予約語であり、特別にタグを指定していない時のデフォルト値として勝手に付与される。つまりそれは latest =! 最新という可能性を否定できず、意図しない動作を引き起こす原因になるかもしれない。
  • 作成したイメージには一意なタグを設定し latest は利用しない
    上述の「FROM 文で引用するイメージのタグに latest は利用しない」と同じ理由。
    もしも「これは最新のイメージです」という意味の任意のタグが必要であれば、「newest」といったタグを自分で設定するのがよい。
  • Docker はイメージのミルフィーユであることを意識する
    Docker は自身の軽量化のため、イメージ生成の各段階でスナップショットとしての中間イメージを作成する。
    そしてそれらの中間イメージは他の Docker イメージを作る際などでも流用できるものはキャッシュとして流用される。
    そして、その中間イメージは Dockerfile の1行ごとに作成される。
    なので Dockerfile の行数を減らすことによりイメージの軽量化に貢献し、開発プロセスをより高速に回すことができる。
  • MAINTAINER 文はオライリーの Docker 本でも必須などとは書かれていなかったけれどもできる限り書いた方がよい
    個人のプロジェクトで DockerHub にイメージを登録する場合は改善要望などをもらえるかもしれない。
    仕事で Dockerfile を書く場合にたとえプライベートリポジトリを用意されていたとしても、プロジェクトの共通メールアドレスなどを記載しておくと出生が分かって他のプロジェクトからの利用しやすくなる可能性など考えられる。
  • CMD 文は上書きされる 最後の1つのみが適用される
    また、ENTRYPOINT 文を書いたときは、そちらが最優先となって適用される。
    ただし、その場合CMD文を ENTRYPOINT 文の引数部分として利用することができる。
    CMD文と同等のコマンドは、docker run 実行時のオプションとしても与えることができる。
    つまりそのオプションで ENTRYPOINT 文の引数を与えることができる。
  • 明示的に圧縮ファイルの展開が必要な場合以外は基本的に COPY 文を使う
    ADD と COPY はどちらもホスト(開発端末)上のファイルをコンテナに転送するが、 ADD は圧縮ファイルの展開なども行う。
    ただのファイル転送であれば COPY 文を利用することで不要な誤解や不慮の不具合を防ぐことができる。
  • yum や apt-get で複数のパッケージをインストールする際は1行のRUN文内でできるかぎりすべてのパッケージをインストールする
    RUN 文を記述するたびに Docker は中間イメージを生成する。
    そのため、パッケージマネージャで各種プログラムをインストールする際は、必要なものをできる限り1回でインストールすることが Dockerfile としては望ましい。
  • yum や apt-get で複数のパッケージをインストールする際はパッケージ名ごとに改行し、かつ、パッケージ名をアルファベット順に記載する
    上述の中間イメージ節約を考慮しつつ、可読性を向上させるためのTipsとして推奨されている。
  • RUN 文で cd は使わずに WORKDIR 文 を利用する
    WORKDIR 文はコンテナ内での作業ディレクトリを定義する。
    用意されている WORKDIR 文を利用することで可読性が向上し、メンテナンスがしやすくなる。 また、同様の理由でパスには絶対パスを利用する。
  • RUN 文 と USER 文を利用してコンテナのプロセス実行ユーザを定義した方がよい Dockerは何もな考えずにイメージを起動するとrootユーザで実行されることが多い。
    VMと違って、ホストマシン(開発端末・本番環境のDockerサーバ)のrootと同じユーザとして振舞う。
  • root権限奪取の危険性を減らすためにコンテナ内に非rootなユーザを作成してそいつにプロセスを起動させる
    日本語のエントリでこれについて言及している人は何故か少ないが、オライリーの Docker 本には明確に書かれてあるし、 How secure are Docker containers? (Ben Hall) - Full Stack Fest 2016 などでも言及されている。
    利用しているイメージのDockerfileが非rootユーザを作成していない場合はできる限り非rootユーザを作成した方がよいのでは。
    そういう意味でも利用するイメージのDockerfileには一度目を通しておいた方がよいのではと考えられる。

2017年6月28日水曜日

日本語ドメインについてお勉強したのでそのまとめ

日本語.jp みたいなドメインってたまに見かけますが実際に自分が運用することになるとは思わなかったので学んだことをまとめたいと思います。
このような形式のドメイン名は「国際化ドメイン名」と呼ばれるものらしいです。

従来のアルファベット、数字、ハイフン以外にマルチバイト文字を利用できるようにする仕組みのことで、日本語の他にもアラビア文字なども利用できるようです。
ただ、毎回国際化ドメインと呼ぶのもアレだし非エンジニアのスタッフに説明する時に相手に難しそうなイメージを持たれたくないので、
日本語が含まれたドメイン名のことを日本語ドメインと会社では呼んでいます。
中国語だったら中国語ドメインですね。

この日本語ドメインをDNSサーバが理解できるようにするためには、Punycode(ピュニコード)と呼ばれる形式で、アルファベット数字ハイフンだけの、既存のドメイン名に使用できる文字だけの対応ドメインに変換してあげる必要があります。
言いたかったのはそれだけです。

たとえば、JPRSが運用している 日本語.jp はPunycodeだと xn--wgv71a119e.jp になります。

これはHTTPS接続についてほんのちょっとだけめんどくさいトラブルが発生します。
curlなどでHTTPアクセスのテストをすることはよくあると思いますが、開発している日本語ドメインのサービスがHTTPSを基本としていて、curlでもHTTPSで行う場合、通常はSSLに関するエラーが出力されます。
SSL証明書を取得するときはPunycode変換済みのドメイン名で取得することが前提となっており、変換済みのドメインは変換前のドメインとリテラル的に同等とみなされないからです。
SSL証明書エラーを回避するには curlの -k オプションを利用します。

今回私が運用することになった日本語のドメイン名は、AWSのACMで無料の証明書を取得しました。
そのドメインは お名前.com で購入したのですが、デフォルトの状態では取得不可能でした。
なぜか?それは、こちらの仕様によるもの

日本語ドメインは、A / AAAA / CNAMEレコードのみご利用いただくことが可能です。
設定方法はこちらをご参照ください。

これはMXレコードを設定できないことを意味します。
MXレコードが設定できないということは、該当ドメインでのメール受信ができないことを示します。
つまり、ACMで必須となる認証メールによるACM発行承認が得られないわけです。

なので、何らかの方法でお名前.com以外のDNSサーバ、できればRoute53でDNSレコードが管理できる状況にしなければなりません。
私は「お名前.comのドメインをAWSで使用する4つの方法 - Qiita」によるところの、2番目の方法で実質的な管理者をRoute53に変更して対応しました。

これを行うことにより、Route53上で好きなようにDNSレコードを設定することができるようになりました。
これでACMの証明書が取得できましたので、ELBやCloudFrontに登録して、快適HTTPSライフが遅れるという算段です。

以上がTIL(Today I Learned)になります。
これから同様の仕事を行う方の参考になれば幸いです。




2017年5月4日木曜日

基本的な使い方を学ぶために速習Docker的なことをしました

MacでDockerを使うために基本的な使い方を学習しました。
下記を読みながら、適宜読み替えたりググったりしてコマンドを実行してみました。


Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

Docker実行環境の構築

brew install docker
brew install docker-machine
brew install docker-compose

Dockerマシンを作成しようとして失敗

TearTheSky-MacBook:~ TearTheSky$ docker-machine create --driver virtualbox test-docker
Running pre-create checks...
(test-docker) You are using version 4.3.26r98988 of VirtualBox. If you encounter issues, you might want to upgrade to version 5 at https://www.virtualbox.org
Error with pre-create check: "VirtualBox is configured with multiple host-only adapters with the same IP \"192.168.33.1\". Please remove one."
TearTheSky-MacBook:~ TearTheSky$

VirtualBox上のDockerマシンの確認

TearTheSky-MacBook:~ TearTheSky$ docker-machine ls
NAME          ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
test-docker   -        virtualbox   Running   tcp://192.168.99.100:2376           v17.04.0-ce
TearTheSky-MacBook:~ TearTheSky$

Dockerコンテナを確認しようとして失敗

TearTheSky-MacBook:~ TearTheSky$ docker ps
Cannot connect to the Docker daemon. Is the docker daemon running on this host?
TearTheSky-MacBook:~ TearTheSky$

対処法

yoshida-no-MacBook-Pro:~ TearTheSky$ eval "$(docker-machine env test-docker)"
TearTheSky-MacBook:~ TearTheSky$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
TearTheSky-MacBook:~ TearTheSky$

Dockerコンテナの作成

TearTheSky-MacBook:~ TearTheSky$ docker run -d -p 80:80 --name webserver nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
6d827a3ef358: Pull complete
f8f2e0556751: Pull complete
5c9972dca3fd: Pull complete
451b9524cb06: Pull complete
Digest: sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582
Status: Downloaded newer image for nginx:latest
61d1da7ae17cf573ceb51a99b213cb5e315150ce92ee8c23686898e28d603946
TearTheSky-MacBook:~ TearTheSky$

Dockerコンテナの確認

TearTheSky-MacBook:~ TearTheSky$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                         NAMES
61d1da7ae17c        nginx               "nginx -g 'daemon off"   15 seconds ago      Up 14 seconds       0.0.0.0:80->80/tcp, 443/tcp   webserver
TearTheSky-MacBook:~ TearTheSky$

Nginxの起動確認

ブラウザで「192.168.99.100」にアクセスするとNginxデフォルトページが表示される。
「192.168.99.100」はVirtualBox上のDockerコンテナホスト。

Dockerコンテナの削除

TearTheSky-MacBook:~ TearTheSky$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                         NAMES
61d1da7ae17c        nginx               "nginx -g 'daemon off"   25 minutes ago      Up 25 minutes       0.0.0.0:80->80/tcp, 443/tcp   webserver
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker rm webserver
Error response from daemon: You cannot remove a running container 61d1da7ae17cf573ceb51a99b213cb5e315150ce92ee8c23686898e28d603946. Stop the container before attempting removal or force remove
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker stop webserver
webserver
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker rm webserver
webserver
TearTheSky-MacBook:~ TearTheSky$

VirtualBox上のDockerマシンの停止と削除

TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker-machine ls
NAME          ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
test-docker   *        virtualbox   Running   tcp://192.168.99.100:2376           v17.04.0-ce
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker-machine kill test-docker
Killing "test-docker"...
Machine "test-docker" was killed.
TearTheSky-MacBook:~ TearTheSky$ docker-machine ls
NAME          ACTIVE   DRIVER       STATE     URL   SWARM   DOCKER    ERRORS
test-docker   -        virtualbox   Stopped                 Unknown
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker-machine rm test-docker
About to remove test-docker
Are you sure? (y/n): y
Successfully removed test-docker
TearTheSky-MacBook:~ TearTheSky$
TearTheSky-MacBook:~ TearTheSky$ docker-machine ls
NAME   ACTIVE   DRIVER   STATE   URL   SWARM   DOCKER   ERRORS
TearTheSky-MacBook:~ TearTheSky$

Docker for Macのインストールと利用

インストール

Docker Machine でコンテナを立てたような感じでコンテナを立てる

TearTheSky-MacBook:~ TearTheSky$ docker run -d -p 80:80 --name webserver nginx
TearTheSky-MacBook:~ TearTheSky$ docker ps

Nginxの起動確認

ブラウザで「localhost」にアクセスするとNginxデフォルトページが表示される。
VirtualBoxには仮想マシンが存在しない。

Docker Machine と Docker for Mac の違い

Docker Machine は 書籍上の Boot2Docker と同じ立ち位置で、Mac上にVirtualBoxを使って仮想マシンのLinuxを起動し、それをコンテナサーバとして、その中にコンテナを立てる。
Docker for Mac は Macbook 自身がコンテナサーバになる。
なのでVirtualBox上に仮想マシンは不要だし、起動されない。
なるほどなるほど。 基本の使い方は理解できました。

2017年3月7日火曜日

AWSを使ってソーリーサーバを構築しました。割と便利だと思う…。

とてもお久しぶりです。
恵比寿のITベンチャーでサービス運営やっています。
仲間募集中です。ご興味ある方はTwitterなどでご連絡ください。

今回はAWSを使って、わりと便利なソーリーサーバを構築しましたというお話です。
構成は下の図のようになっています。



通信フローとしては、まずエンドユーザがELBにアクセスします。 このELBは既存のWebサービスやWebサイトをバランシングしていたものです。 諸事情によりメンテナンスしたり、ドメインを畳むことになったという話を想定しています。
そのELBに接続されていたWebサーバやアプリケーションサーバの代わりにソーリーサーバを接続します。 するとエンドユーザはソーリーサーバにアクセスしてきますよね。
ソーリーサーバはリクエストを受けたら、各ドメインに対応させたいCloudFrontへリクエストをパスします。 CloudFrontはS3のコンテンツを返すのですが、このS3に静的なソーリーページを格納しておきます。 それにより、ドメインにアクセスしてきたエンドユーザにソーリーページを見せることができます。
ポイントはソーリーサーバとCloudFrontですがやってることは簡単です。 まず、ソーリーサーバはただのNginxです。 proxy_pass を使って、特定ドメインに来たリクエストを対応するCloudFrontのドメインへパスします。 その設定は、/etc/nginx/conf.d/virtual.conf に書きました。
server {
        listen 80;
        server_name example.com;
        if ($http_x_forwarded_proto != https) {
                return 301 https://$host$request_uri;
        }
        location / {
                proxy_pass http://domain-a.sorry.example.com/;
        }
}
上記の server ディレクティブのかたまりを、ソーリーページの数だけ用意します。 1ドメイン1種類のページでよければ、3ドメインなら3つのディレクティブになります。
if文のおかげで、自動でHTTPSになります。
しかしここで一つ問題があります。 ELBごとにヘルスチェックエンドポイントが違うのです。 なのでそのままではそもそもNginxがヘルスチェックに合格せず、ELBがNginxにリクエストを渡してくれません。
それはNginxの素敵機能 empty_gif を使って解決します。 今回は /etc/nginx/nginx.conf に以下のように書きました。
http {
    (略)
    server { 
        (略)
        location / {
                 empty_gif;
                break;
        }
    (略)
    } 
    (略)
}
これにより、どんなパスへのアクセスに対しても 1ピクセルのgifイメージを生成して返します。 そのおかげで、ELBのどんなヘルスチェックパスも合格し、めでたく InService になることができます。
残りは CloudFront ですね。
CloudFrontはソーリーページごとにインスタンスを用意する必要があります。
理由はカスタムエラーページを利用したいからです。
CloudFrontのカスタムエラーページでは、特定のHTTPステータースコードの場合、任意のステータスコードに変更することができ、さらに任意のコンテンツを表示させることができます。
これを使って、全ての403アクセスを404へ変更して、ついでにソーリーページのhtmlを表示させちゃうことができます。
なんで403にするかというと、CloudFront + S3 でのコンテンツ配信方式で、ページが存在しない場合、CloudFront側が 403 Access Denied を返すからです。
これを404+ソーリーページにすることで、どんな存在しないURLにアクセスが来ても、自分が用意した404用のhtmlが表示されることになります。
ただし、「正しいソーリーページのパス」にアクセスされると、ステータスコードは200になってしまいます。
よほどの物好きなエンドユーザ意外はそんなことしないので、気にしないことにしました。
Nginxのコンフィグの書き方でもうちょっとCloudFrontのインスタンスを減らせる気もしますが、料金体系的にデータ転送量とリクエスト数のみで、インスタンス数による固定料金はないので、管理は面倒くさいですがこれでいいかなという感じです。

あ、あと、ACMを使って無料証明書を取得してCloudFrontに登録しています。
そのために、 sorry.example.com のような、ソーリーページ専用サブドメインを用意して、 それのワイルドカード証明書をACMから発行させてもらっています。

ところで何が便利なの?
まず、ステータスコードを任意に指定できます。404とか、500とか。それから、ソーリーページにhttpsが使えます。労力の割には些細なリターンのような気もしますが…。
NginxのコンフィグはGitで管理してAnsibleなどで配布できます。
1台に全部のソーリーページについて書かなきゃいけないですが、書いてしまえば、1台をマスターサーバにして楽にスケールアウトできます。
マスターサーバのコンフィグに全てのソーリーページの設定が書かれてあるので、ドメインに対して透過的なソーリーサーバが出来上がります。
どのELBにどのサーバを突っ込んでもOKということです。