katsuyukikun’s diary

とある天パーエンジニアのblog

【rails5.2 + mysql5.7】docker上にrailsアプリケーションを立ててリモートDBと繋ぐ

先週くらいからようやくdockerに入門しました!(遅い・・)

本とか自分のPC上では軽く触ったことあったのですが、業務でがっつりdockerで環境構築をしたことはなかったです。

そして今回業務で環境構築する際に、

「どうせならdockerでやろう」

と思って作業してみたもののちょびっと詰まったところがあったのでこれから入門する人の参考になればと思いログとして残しておきます!

 

構成

rails5.2、mysql5.7で作ろうと思っていました。

dockerの公式のチュートリアルでもありますが、結構docker上にアプリケーションとDBの両方置いちゃうケースがあると思います。

 f:id:katsuyukikun:20180718205812p:plain  

  なので僕も最初これで環境作ろうと思っていました。

ただ今回の環境構築では新規DBを作成ではなく既存のDBを使いたい!!

既存のDBをダンプしてそれを新規DBでリストアするか〜とも思いましたが、データ量かなり大きそうで断念。。。。

 

ということで結局この構成にしました。

f:id:katsuyukikun:20180718210333p:plain  

初めての構成だったので本当にできるのか不安だったのでまずはローカルからリモートのDBに繋げられるかを確認。

f:id:katsuyukikun:20180718221547p:plain

リモートDBサーバーのホスト名(xxx:xxx:xxx:xxx)

ユーザー名(hoge)

データベース名(database_name)

$ mysql -h xxx:xxx:xxx:xxx  database_name --port=3306 -u hoge -p

結果は

bash: mysql: command not found

それ以前の問題でしたwww

brewを入れているのでmysqlの5.7をインストールした後にもう一度実行しました

$ brew install mysql@5.7
$ mysql -h xxx:xxx:xxx:xxx  database_name --port=3306 -u hoge -p
bash: mysql: command not found

結果は変わらず・・

$ which mysql

と打っても何も出ないので、パスを通すことにしました。 (なぜこうなるのかは後で調べるつもりです) 僕は.bash_profileにいつも書くのでそこに追記します。

$ echo export PATH=/usr/local/opt/mysql@5.7/bin:$PATH > .bash_profile 
$ source .bash_profile
$ which mysql
/usr/bin/mysql

準備できた。

$ mysql -h xxx:xxx:xxx:xxx  database_name --port=3306 -u hoge -p
Access denied for user

おっふ、、、弾かれましたな。

インフラチームに聞いたところローカルからは直接繋げられないよ、とのことでした。

なので踏み台を利用して繋ぐ方針に変更。

f:id:katsuyukikun:20180718213546p:plain

トンネル掘って繋げれるようにしました。
(「sshポートフォワーディング」とかでググると出てくると思います)

僕の場合、ローカルから踏み台サーバーへは

ssh 踏み台サーバーのホスト名

でログインできていたので以下になりました。

$ ssh -NL 3307:db_host:3306 ssh_host
$


db_hostはデータベースのホスト名
ssh_hostは踏み台のホスト名

接続確認

$ mysql -h 127.0.0.1 -P 3307 -u db_user -p

> これでmysqlにログインできるはずです


db_userはデータベースログインのユーザ名

やめたかったら

$ ps aux | grep ssh

とやると、フォワーディングしているプロセスが出てくるのでkillすればokです!

ここまできてようやく今回の構成が決まりました汗。

dockerからローカル経由してフォワーディング先のリモートDBに繋げそうですね。

f:id:katsuyukikun:20180718215028p:plain

コード

f:id:katsuyukikun:20180719002005p:plain

あとはdockerからローカルのところを実装すればうまくいきそうです!

なのでコードを書いていきましょう。チュートリアルと同じなので迷うこともないかなと思います。

まず作業するディレクトリを作り、その上でDockerfileとdocker-compose.ymlとGemfile、Gemfile.lockを作ります。

$ mkdir work
$ cd work
$ touch Dockerfile
$ touch docker-compose.yml
$ touch Gemfile
$ touch Gemfile.lock

そして各々コードを記述していきます。

Dockerfileの中身

FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp
Gemfileの中身

source 'https://rubygems.org'
gem 'rails', '5.2.0'

Gemfile.lockは空。

docker-compose.ymlの中身

version: '3'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"

コンテナ上でrails newします。

$ docker-compose run web rails new . --force --database=mysql

新しいファイルができたらconfig/database.ymlを修正します。 ここ最初どう書けば良いのか迷いましたが最終的にこれでいけました!

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: リモートDBのユーザー名
  password: リモートDBのパスワード
  host: docker.for.mac.localhost
  port: 3307

development:
  <<: *default
  database:リモートDBのデータベース名

dockerからローカルのMacを見るときはdocker.for.mac.localhostで見てくれるそうです! あと今回、フォワーディングのポートを3307にしているのでport: 3307にしています。 ここも適宜変更してください!

ここまでできたらあとは起動するだけです!

$ docker-compose up --build

localhost:3000にアクセスするとお馴染みの画面が表示されるはずです。

f:id:katsuyukikun:20180718220141p:plain

全然関係ない話。

今回初めて知ったのですが、Active Recordって使えるmysqlのバージョンがあったんですね笑。 ORマッパーもあるし、よくよく考えると当たり前なんですが意識したことが一度もなかったです!

というのも今回既存のDBのmysqlが5.0.9という状態で・・・アクセスしたら

Your version of MySQL (5.0.9) is too old. Active Record supports MySQL >= 5.1.10.

という見たことないエラー出てちょっと感動しましたw こういうところもしっかりみておきたいですね!

github.com

終わり。。

awsサービス cloudformationが想像以上に良い!!

以前サーバー移行の話をちらっとだけ書きました。

 

katsuyukikun.hatenablog.com

 

デプロイ方式はBlueGreenでやっています。

この時、環境構築するのにcloudformationを使用していました。

 

aws.amazon.com

 

テンプレートに記述するだけで新規・既存問わずに自由に設定できます。

僕の時は「既存のVPCの中の既存のセキュリティグループにインスタンス2台(web機とapplication機)を構築する」という感じで作っていました。

ロードバランサーにALBを使いたかったので、2台立てることになりました。

ALBはpublicにしかアタッチできないとか聞いてなかったです笑

 

   本当はこうしたかった                                         でもこうなった 

f:id:katsuyukikun:20170517233807p:plain              f:id:katsuyukikun:20170517234004p:plain

 

publicにweb機(nginx)、privateにapplication機(unicorn)をおくことにしました。で、この時cloudformationでここの関係性も定義しました。こういうのを用意しておいてくれるのは本当に助かりました。

 

良かったところ①

 

インスタンス構築時にこういうプロパティ宣言ができるようになっています。

 

"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
  "#! /bin/bash -v\n",
  "yum update -y\n",
  "yum install -y nginx\n"
]]}}

 

なのでnginxインストール済みのインスタンス作成できちゃいます。

docs.aws.amazon.com

 

 

しかもこれ結構よくてnginxの/etc/nginx/test.confファイルにこう書いておく

 

location / {
 proxy_pass http://private_ip_address:8080;
}

 

cloudformatationのnginxのインスタンスUserDataにこう書く

 "sudo sed -i \"s/private_ip_address/",
{
    "Fn::GetAtt": [
        "Ec2Instance",
        "PrivateIp"
    ]
},
"/g\" /etc/nginx/test.conf \n",
]]}}

 

 

そうするとnginxインストール済みインスタンスunicornを設定済みインスタンスの繋ぎこみ完了している状態で作成されます笑。これできた時は少し感動でした。

 

良かったところ②

lambdaが使えるところww

インスタンス立ち上げ終わったあとに、lambdaキックさせてターゲットグループの入れ替えというのをやりたかったんですけど、この辺もカバーしてくれて助かりました笑。

 

それを実現するためにカスタムリソースってのが用意されてます。

docs.aws.amazon.com

 

 

 
"CustomResource" : {
  "Type" : "AWS::CloudFormation::CustomResource",
    "Properties" : {
        "ServiceToken": "lambdaのARN",
        "Test1": {"Ref": "InstanceName"},
        "Test2": {"Ref": "TargetGroupName"}
    }
}

ServiceTokenってのにlambdaのARN入れるだけで構築し終わった後そのlambdaを叩きにいってくれます。しかもTest1といった任意の文字列もeventの一部として渡せます!便利なんですけど、ここで詰まったポイントがあります。

詰まりポイント

lambdaにある記述を書かないとcloudformationのタスクがプログレスのまま終わらない・・・そして一時間後くらいにfailedとなってしまう。

なんか cfn-response モジュールなるものが必要らしいです。

 

docs.aws.amazon.com

 

 

cfn-response モジュールは、ZipFile プロパティを使用してソースコードを作成した場合にのみ使用できます。S3 バケットに保存されたソースコードには使用できません。S3 バケットのコードでは、独自の関数を作成してレスポンスを送信する必要があります。

 

 

とあります。これURL見てもらえらば良いのですが、 

 

"ZipFile": { "Fn::Join": ["", [
  "var response = require('cfn-response');",
  "exports.handler = function(event, context) {",
  "  var input = parseInt(event.ResourceProperties.Input);",
  "  var responseData = {Value: input * 5};",
  "  response.send(event, context, response.SUCCESS, responseData);",
  "};"

 上記のようにcloudformationの中に直接コードを書きましょということです。 

・・・・・いや、さすがにそれはいやだよ?w

ってことでマネジメントコンソールの方に記述したいからこの方法とったのに・・・と思ったんですがgithubみるとこんな感じ。

 

github.com

 

結局、cloudformation側にレスポンス返せばいいだけだったので自分で実装してしおうと思いました。(まあコピペですな)lambda側に以下を書いてあげれば良い。

 


var SUCCESS = "SUCCESS";
var FAILED = "FAILED";
 
var cfnResponse = function(event, context, responseStatus, responseData, physicalResourceId) {
 
    var responseBody = JSON.stringify({
        Status: responseStatus,
        Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
        PhysicalResourceId: physicalResourceId || context.logStreamName,
        StackId: event.StackId,
        RequestId: event.RequestId,
        LogicalResourceId: event.LogicalResourceId,
        Data: responseData
    });
 
    console.log("Response body:\n", responseBody);
 
    var https = require("https");
    var url = require("url");
 
    var parsedUrl = url.parse(event.ResponseURL);
    var options = {
        hostname: parsedUrl.hostname,
        port: 443,
        path: parsedUrl.path,
        method: "PUT",
        headers: {
            "content-type": "",
            "content-length": responseBody.length
        }
    };
 
    var request = https.request(options, function(response) {
        console.log("Status code: " + response.statusCode);
        console.log("Status message: " + response.statusMessage);
        context.done();
    });
 
    request.on("error", function(error) {
        console.log("send(..) failed executing https.request(..): " + error);
        context.done();
    });
 
    request.write(responseBody);
    request.end();
}

exports.handler = (event, context, callback) => {
    // TODO implement
    if(event['RequestType'] == 'Delete'){
        cfnResponse(event, context, SUCCESS, {});
    }
    console.log(event);
    cfnResponse(event, context, SUCCESS, {});
};

 

 これで無事lambdaにキックさせ、かつタスクもちゃんと終わってくれます。

 

 

まとめ

さすがAWSって感じですね!

ちゃんと手を届かせてくれるあたりがユーザーとしては満足度高いです。

もっと知ればいろんなことができそうなので他のサービス含め触り倒していきたいなって思います。

 

 

 

あっGoogle I/O見ないと・・・・

 

 

終わり。

データベース入門②

もうすぐGWに突入にします。。。

 

結婚式の余興動画も作り終えたのでまた書いていきたいと思います。

 

まああとせっかくGWでまとまった時間取れるのでサイトの速度改善やら新しい技術入れたり何やらをやりたいのでね。。

 

とりあえずデータベース入門はとっとと読み終えて他のことしたいなと思ったので急ピッチで進めてます笑。

 

さて、久しぶりのデータベース入門です。

前回から読み進めましたが、8章くらいまでは論理値の説明と正規化の必要性とどうやったら正しくDB設計できるのかという説明でした。ここに関して個人的には「知っている人多いし、軽く流し読みする程度でいいかな」という感想です。それくらいで理解できる内容だと思います。

 

 

ってことで8章〜9章くらいからの内容に触れていきます。その内容とは以下です。

 

select文の使い方

まずはSQLの基本ですね、select文です。こいつは何をするやつなのか・・データ取得できる唯一の存在です!

書き方はこんな感じ、説明するまでもないですね。

SELECT カラムのリスト 
FROM テーブルのリスト 
WHERE 検索条件

 

これにいろいろ条件を付け加えることができます。集計結果を出したいなら

SELECT count(カラムのリスト) 
FROM テーブルのリスト 
WHERE 検索条件

 

重複省いた集計結果なら

SELECT count(distinct カラムのリスト) 
FROM テーブルのリスト 
WHERE 検索条件

 

集計結果に制限をつけたい時は

SELECT department, COUNT(*) 
FROM students
GROUP BY department 
HAVING COUNT(*) <= 30

とcountで取得したものに<=30といったような制限をかけることで取得することができます。今回だと30以下という制限をかけました

 

ここで人数少ない順にしたいときはサブクエリ使っても良いのですが、せっかくなのでorder byを使ってみます

SELECT department, COUNT(*) 
FROM students
GROUP BY department 
HAVING COUNT(*) <= 30 
ORDER BY COUNT(*) ASC

 

ちなみにこれ順番大事です笑。group byしてからhavingしてorder byしないとエラリますので気をつけましょ。上から順に読んでいくので今回だと「グループに分けて、それぞれを集計した結果を制限して最後、集計結果順に並べてみる」になります。イメージはつきやすいかなと思います。

 

 

サブクエリの説明もあったのですが、結局それはこれらの組み合わせでしかないので割愛しました!

 

今回はここまでとします。

 

軽〜い内容でした笑。 

 

 

データベース入門もあとは

・インデックスの説明

・リファクタの仕方

・実際にアプリケーションを扱うとき

トランザクションについて

この4つで本は終わりにしようかなと思います。

そのあとにrailsのときのjoinとinner_joinの違いの説明を軽くやってDB周りは一旦終えようかなと思います。(アンチパターンもやるかもですが)

 

 

GW前までには終わりそうですね、、よかった。

 

終わり。 

reactをまだマスターしていなかった件

「え?こんな書き方あるのか、知らなかったわ〜」と思ったのでメモる笑

 

まあまずはこれですな・・・

 


 

ループ処理

配列のループ処理!意識していなかったわけではないんですけどね、、反省。。

 

forEach

連想配列(object)だと使えないんですけどね、ただ指定のarrayを回すときに使う


[1, 2, 3].forEach( (element, index, array) => {
  console.log(index + ":" + element);
});

 

indexとarrayは省略可能ですー!

 

map

新しい配列を作成したいときに使う感じですな


const elementSquareArray = [1, 2, 3].map( (element, index, array) => {
  return element * element;
});

 こちらもindexとarrayは省略可能でっす!returnで返してあげましょ!

 

filter

条件にあうものだけを抽出したいときに使います


const evenNumberArray = [1, 2, 3].filter( (element, index, array) => {
  return (element % 2 === 0);
});

 

reduce

処理をして1つの値を取得。
previousValueは前の値というより、それまでの結果と考えるとわかりやすいかもです


const total = [1,2,3].reduce( (previousValue, currentValue, index, array) => {
  return previousValue+currentValue; 
}); 
### total=6になる

 

every、some 

これ使ったことないですけど・・・

everyは条件を全ての値が満たすか、someは条件を満たすものがあるかの判定。
結果はbooleanで返ってくるらしいです 


const everyEvenFlag = [2, 4, 6].every( (element, index, array) => {
  return (element % 2 === 0);
});

 


const someEvenFlag = [1, 2, 3].some( (element, index, array) => {
  return (element % 2 === 0);
});

 


 

render

なにを返すのか問題ですね、返す型はそろえましょ!

 

はい、すみません。僕は適当にやってました・・・

 

 

何も描画したくないときにnullまたはfalseを返すことでそれを実現することができます。実際には裏で<noscript>タグを描画します。 なんでそういうときは

return <noscript/>

とするとよいかもですね

 


 

JSXのif文

jsx内のrender部分でif文書くとき即時間数を使って書くことが多いと思いますが、いかんせん括弧が多くて見にくくなってしまいます。


{(() => {
  if (this.props.count >= 3) {
  return  (componentとかviewを返す);
  }
})()}

 

 

こんなときに使える書き方があります、それがrenderのときの理論です。falseやnullのときはなにも表示されないのでそれを使うと以下になります。


{(this.props.count >= 3) && (
  return (
    componentとかviewを返す
  )
)}

 

 

見やすい!!

 

わかると思いますが、最初のif文でtrueのときの処理しか使えません。なので最初からif~elseで分けるものに対しては即時間数で書くしかありません。

 

 

 

 

 

 

 

 

 

react使って3ヶ月くらいですが、まだまだ知らないことがありそうなので詰めていきたいものです。

 

 

終わり。

 

railsとrubyの相性

rubyrailsのバージョンを上げることになったが、一つ疑問に思いました。

 

適当にどっちも手間なくあげれるとこまで上げれば良いのかな・・・??

 

ビルド時にどのくらい速度に差があるのか気になったので調べてみました。

 

 

準備

ruby 2.2.0、2.3.0、2.4.0をrbenvでインストールしておく

 

 

コード

RUBY=("2.2.0" "2.3.0" "2.4.0") 
for ruby in ${RUBY[@]}; do
  export PATH=$RBENV_ROOT/shims:$PATH
  rbenv global $ruby >/dev/null 2>&1

  RAILS_VER=("4.0.13" "4.2.8") 
  for rails_ver in ${RAILS_VER[@]}; do
    gem install rails -v $rails_ver >/dev/null 2>&1
    if test $rails_ver = '4.0.13' ; then
      gem uninstall rails -v "4.2.8" >/dev/null 2>&1
      gem uninstall railties -v "4.2.8" >/dev/null 2>&1
    else 
      gem uninstall rails -v "4.0.13" >/dev/null 2>&1
      gem uninstall railties -v "4.0.13" >/dev/null 2>&1 
    fi
    rbenv rehash
    ruby -v
    rails -v
    rails new rails_${rails_ver}_speedtest -d mysql >/dev/null 2>&1
    echo "gem 'spring'" >> ./rails_${rails_ver}_speedtest/Gemfile
    cd ./rails_${rails_ver}_speedtest
    bundle install --path vendor/bundle >/dev/null 2>&1
    bundle exec spring rails g scaffold blog title:string description:string >/dev/null 2>&1

    echo spring
    for i in {1..10};do
      bundle exec time spring rake routes | grep real
    done 2> routes.txt
    awk -f ../ave.awk routes.txt
    cd ..
    rm -rf ./rails_${rails_ver}_speedtest
  done
done

結果

f:id:katsuyukikun:20170328193006p:plain


何度もやってもこのくらいに落ち着くのでやはりビルドに差があると言ってもいいのではないでしょうか。。。ちょっと今回はしょりすぎてrake routeでやったのがセンスなかったですね笑

あと追加でrails5もやったっていうのがruby2.2.0を使っているところからバレバレですねw

 

まとめ

rubyrailsのバージョンの相性はありそう。

ただ今回rake routeでやってしまったので差があまり明確にならず。

またバージョンのどこに差があるのかというところまで調べていない。

DBの本を読み終わった後、ここについてもう一度追加調査していたいと思います。

 

 

終わり。

データーベース入門①

さて、宣言通り技術についてアウトプットしていこうかなと思います。

 

題材は「DBについて」です!

Webアプリケーションをずっと作ってきたのでアプリケーション周りは結構強いのですが、DB周りについてはまだまだ穴があるので今回はそこについて「理論から学ぶデーターベース実践入門」を読んで抑えていきたい思います。

 

 

まずは第一回ということでそもそものところから書いていこうと思います。

 

  • データベースとは

ここからやらなくてもいいんじゃない?と思いますが、一応やっておきます笑。

wikipediaさんに聞いてみると

データベース(英: database, DB)とは、検索や蓄積が容易にできるよう整理された情報の集まり。 通常はコンピュータによって実現されたものを指すが、紙の住所録などをデータベースと呼ぶ場合もある。 狭義には、データベース管理システム (Database Management System, DBMS) またはそれが扱う対象のことをいう。

 

つまり特定のテーマに沿ったデータを集めて効率的に管理し、使いやすくしたものと考えてもらって良いと思います。ちなみにもう少し調べてみたら実は・・・・

データベースという名称は米軍からきているらしいです。第二次大戦後の米軍が、そこにアクセスすればすべての情報が得られるように、点在していた膨大な量の資料をひとつの基地に集約して効率化を図ったらしいです。この際にデータベースという言葉が誕生したと言われているらしい・・

 

そしてそんなデータベースですが種類があります!大きく分けると以下4つに分けることができます。

 

「階層型」「ネットワーク型」「関係型」「NoSQL」

 

一つずつ説明していってもよいのですが、ちょっとそれをしちゃうと肝心の話まで届かないで終わってしまう気がするので割愛します笑

参考リンクを貼っておきますので興味のある方はこちらをどうぞ!!

 

ざっくり説明が書いてあります↓

programming-study.com

 

図がわかりやすいかもです↓

www.techscore.com

 

NoSQLについての説明あります↓

www.sejuku.net

 

 

 

業務ではSQLを使用しているのでRDBについて書いていきますー 

( ´ ▽ ` )ノ あっ「関係型」のことです。

 

 

 

SQLとは、リレーショナルデータベース(以下RDB)に対して問い合わせを行うための言語のことです。そしてSQL はリレーショナルモデルをベース設計されておりリレーショナルモデルに沿った演算が得意です。なのでSQLを理解するにはRDBについて理解しなければならないということになります。逆にいうとRDBを理解しないとSQLについて理解できたとは言えずそれは単になんとなく書けるというレベルでしかありません。

 

  • リレーショナルモデル

現実世界のデータを「リレーション」と呼ばれる概念を用いて表現するデータモデルのこと。ここで注意したいのがあくまでも、リレーショナルモデルが表すデータモデルは 設計という意味ではなく、データをどのように表現するか、という概念の話だということ。「○○という概念を使ってデータを表現してください」という決まりごとがデータモデルであり、リレーショナルモデルはその中の一つにすぎないということになります!

 

  • リレーションとは

最もよくある間違いとしてあげられていたのが、「テ ーブル同士の関係」というものです。概念にすぎないという説明をしたと思うので理解されていると思いますが、テーブル同士の関係を(ER 図などを使って)デザインするのはリレーショナルモデルではありません。SQLにおいてリレーションに相当するものは、テーブルです!

言葉だとだとわけわからないと思うのでずを載せておきます。

                        f:id:katsuyukikun:20170325022254p:plain

説明書きとしては以下でした。

リレーショナルモデルにおけるリレーションの定義のようなもので すリレーションは見出(Heading)本体(Body)のペアで構成されます。 見出しは、0でないn  属性(Attribute)集合ですこの属性、名前とデー タのペアになっています。本体、属性値集合である組、あるいは英 語うとタプル(tuple)集合です

 

タプルに含まれる属性値は、名称とデータ型が見出しで指定されたものと、それぞれ一致していなければなりません。

見出しで定義されていない属性が存在したり、逆に見出しに含まれる属性がタプルに存在していない場合は、ルール違反です。

つまりリレーションとはタプ ルの集合であり、タプルはすべて同じ n 個の属性値の集合という同じデー タ構造を持っています。図 1.1 は、リレーションをイメージ化したもの。

 

 

 

 

今回はここまでにしておこうかな・・・ここまで理解するとわかりますが、結局集合体でしかありません。だからもっと理解を深めるにはその集合体の話を知っておく必要があります。なので次はそこから始めれればなと思います。

 

終わり。

 

 

サーバー移行時のざっくりとした手順と考えるべきこと

ちょっとした記憶の整理。

2016年の10月くらいから2ヶ月間かけて事業の全サーバーをGMOクラウドからAWSへサーバーを移行したのでそのときの手順。本当は終わった直後に書けば記憶が鮮明なので詳細に書けたのに・・・それを言っても仕方ないのでとりあえず思い出せるだけ思い出します。

 

f:id:katsuyukikun:20170317012524p:plain

 

と話に入るまえにこういった大きなことをするときのポイントを述べておきます。それは

既存の環境(GMOクラウドの環境)をなるべくそのまま新規の環境へ(AWS)へ移す

 

これ結構大事です!移行しようとして色々調べたりすると「あれ?これいらなくね?」とか「え?これこのバージョンなの?あげたほうがよくね?」といったものが結構多く出てきます。しかしここはぐっとこらえて一旦、目を閉じましょう。もしリファクタリングみたいなことを同時にしてしまうとエラーになったときに、問題の原因特定が極めて困難になります。

ちなみに僕はアプリケーション側のサーバー設定のときに設定ファイル含めリファクタリングしながらやってしまい、結構困りました。エラーになったとき「新規のサーバーの設定ミスなのか?それともリファクタリングしたときのミスなのか?」と原因推測の範囲が分散してしまいわけわからなくなりました。もしこれをリファクタリングしないで進めていたとすると、既存の環境では動いていたのは事実なのでサーバーの設定ミスっぽいなと、ある程度明確に当たりをつけられます。もし古いものを新しくするときはこのことを覚えておくと結構早く物事を進められるかもしれません。

 

 

話を戻して以下が手順です。

  • 既存のネットワーク、サーバー構成の確認

新規で0→1のサービスを作るならこんなこと考えなくて調べて洗い出してみたいなことをやらなくていいし、やるとしてもおそらく今だとgithubとかでどういう設計にしたのかを残しておくと思うのですが。。うちの事業は10年くらい前からやっていてそのときの記録が一切ないので現状を把握することから始めました。

 

  • 既存で使われているライブラリやツールの確認

うちはnginx→unicornでサーバーが構成されていました。各々のサーバーでどんなミドルウェアが使われいるのかを確認します。

 **ここまでが現状の理解になります**

  • 新規のネットワーク・サーバー構成の考案

ここは全体設計の話なのでかなり楽しかった記憶があります笑。サーバーの移行なんてそうそう行うものではないので「自分の考えた構成が残っていくのかぁぁぁ」と深く感動したものです。ただこの頃インフラなんて触ったことなかったので設計と言われてもなにがなんだか・・という感じでした。

なのでキャッチアップしながら進めました。以下が流れです。

1:とりあえず「サーバー移行」と適当にググったりインフラの本読んだりする。

Amazon Web Services 基礎からのネットワーク&サーバー構築

Amazon Web Services 基礎からのネットワーク&サーバー構築

 

 

2:それらを見て・読んでなんとなくの全体像を把握。名前すら知らなかったのもあったのでここでは本当に「こんな感じなのかー」という感覚をつかむ程度。 

3:(自分の環境を作ってまずは遊んでみるのもあり)

4:設計の本を読んだり、他社さんのを参考にしたりする。以下を読みましたね。あとはAWSのブログとか見てたような気がします。

 

Amazon Web Services パターン別構築・運用ガイド

Amazon Web Services パターン別構築・運用ガイド

 

 

Amazon Web Servicesクラウドデザインパターン設計ガイド 改訂版(日経BP Next ICT選書)

Amazon Web Servicesクラウドデザインパターン設計ガイド 改訂版(日経BP Next ICT選書)

 

 

aws.typepad.com

 

5:4まででなんとなく設計に必要そうな技術がわかるのでとりあえずそれらを知っておく、また似たようなことができるのもあるのでそれらの違いを理解する。僕の中では「elastic beanstalk / opsworks / cloudformation」の違いがよくわからなかったのでそれを結構調べましたww

6:以上のことを踏まえて設計を考える。真似でも良いと思います。その候補が上がったら、各々の候補同士でメリットデメリットを考える

7:デメリットを考えるときは「一番最悪のことが起こったときどう対処できるのか」という基準を持つとすっきりします。

 

全体の話に戻します。この先は言わずもがなですね。

  • 実際に構築

実際に作る前に部分部分で小さく作って問題ないのかの検証して、できなさそうならもう一度考えて作って検証するというのの繰り返しをやって「できる!」となった時点で実際の全体構築に入りました。

  • 全体のテスト

アクセステストやらDBがちゃんと動いているのかといったテストをしました。

  • 本番環境にする

 

というのが僕がやった時の流れです。実際に作るときには、「これできるのか?」みたいな検証に時間がかかってたような気がします。そんな時はAWSのサポートに問い合わせてみると答えが返ってくるかもしれません。最終こんな感じになりました。

 

f:id:katsuyukikun:20170317212708p:plain

 

 

こだわったポイントは以下ですかね笑

・この図だとわかりませんがcapistrano deployからblue green deploy方式に変えた

・この図だとわからないかもしれませんが、セキュリティ面強化のためにweb機をpublicにapplication機をprivateにおいた

・AMIを主体にcloudformaiton一発でこの環境ができるようにした

 

 

 

 

最後に・・・・

最近は、そろそろGCPを触る時が来たかなと思っているのでAWSからGCPに移行してみたというのもやってみようと思っています。

 

www.publickey1.jp

 

 

終わり。