RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

プログラミング未経験者向けの学習方法

はじめに

皆さん、こんにちは、今年の4月に入社したプログラミング未経験者のr_yxkxrx13です。
プログラミング超初心者!!という方は、プログラミングの学習を始める前に何を準備して取り組めば分からない…ということが多いと思います。 プログラミング未経験である私が今まで取り組んだプログラミングの学習方法を紹介したいと思います。

プログラミング学習方法

私が今まで取り組んできたプログラミング学習方法は、「コンピュータ基礎/ネットワークの基礎学習サイト」、 「プログラミング学習サイト」、「書籍学習」の3つです。
学習サイトとは、ブラウザ上に無料で気軽に利用できるサイトのことで、インターネットにたくさん出ています。
まずはそのようなサイトを利用して学習を始めるのが良いと思います。 また書籍学習で基礎知識の理解を深めながら、プログラミングの練習をすると良いと思います。 私が利用した学習サイトと書籍について説明します。

コンピュータとネットワークの基礎学習サイト

プログラミング学習の初めに「コンピュータ基礎/ネットワーク」を学習しました。
プログラミングの基礎なので、基礎知識と概念を理解しておくと良いと思います。

  1. 【5分で覚えるIT基礎の基礎】コンピュータの仕組み---目次 | 日経クロステック(xTECH)
    「コンピュータの仕組み」をテーマに基礎知識の全体像と概要を分かりやすく解説されています。

  2. 初心者のためのネットワーク技術 - ネットワークエンジニアを目指して
    「ネットワーク技術」をテーマに基礎知識を分かりやすく解説されています。

プログラミング学習サイト

プログラミングの参考本では、決まった書き方やコードの意味がたくさんあり、全部覚えられない…。
とつまづいてしまうことはあると思います。

私の経験の1つですが、正直に言って、全てを一気に覚えることは無理があります。
逆に理解度が低くなってしまうので意味がありません。

まずプログラミングを書く練習をたくさんすると、自然に覚えられるようになってきます。
また1行ずつコードの意味をコメントアウトしてみると、理解が深まってきます。

そこで、ブラウザ上で実際にプログラミングを作る環境と似た環境で練習できるサイトを紹介します。

  1. Progate

    f:id:r_yxkxrx13:20171024084457p:plain

    (画像出典:Progate

    Progateは、ブラウザ(インターネット)上でプログラミングが学べるサービスです。

    イラストを中心に分かりやすく解説されたスライドで、自分のペースに学習や復習ができます。 また以下のようにブラウザ上でコードを書いて結果を確認することができます。 複雑な環境設定などの準備は不要なので、気軽にプログラミング学習を始めれます!

    初心者レベルから実践レベルまで対応されていて、
    さらにレッスンの種類もたくさんあり、分かりやすく楽しみながら学べるサイトです。

    f:id:r_yxkxrx13:20171023094623p:plain

  2. ドットインストール

    f:id:r_yxkxrx13:20171024085444p:plain

    (画像出典:ドットインストール

    ドットインストールは、約3分間の短いレッスン動画でプログラミングを学べるサイトです。
    動画の音声が聞き取りづらくても動画画面の下に字幕があるので、見返すことができます。 また右側に学習メモの機能があり、新しい知識や疑問をメモすることができます。 動画を見ながら、実際に作ってみることを重点において学習すると良いでしょう。

    f:id:r_yxkxrx13:20171024090834p:plain

  3. CodeResume

    f:id:r_yxkxrx13:20171024095044p:plain
    (画像出典:CodeResume

    CodeResumeは、ブラウザ上でプログラミングが学習できるプラットフォームです。
    練習問題の画面では、以下の図のように解答欄にコードを書き込んで、コンパイルと実行で結果を確認できるようになっています。 またタイマー機能も付いているので、時間を意識して考えて解く力が身についてくると思います。

    f:id:r_yxkxrx13:20171024092332p:plain

書籍学習

学習サイトだけでは学習範囲が限られていますが、書籍での学習も有効な学習方法だと思います。
私が利用した書籍の中からオススメの書籍を紹介します。

  1. スッキリわかるJava入門 第2版 (スッキリシリーズ)

    スッキリわかるJava入門 第2版 (スッキリシリーズ)

    スッキリわかるJava入門 第2版 (スッキリシリーズ)

    本書は、Javaの基本を丁寧にイラストと同時に説明されていますので、非常にわかりやすい内容になっています。 Javaの複雑な開発準備や必要なツールって分からない…。という方、安心して下さい。
    Webブラウザを持つ携帯電話やパソコンで、準備されたクラウド開発実行環境「dokojava」を使って、手軽にプログラミングの練習ができるようになっています。 また「dokojava」の他に、実際に開発現場で使われている「Ecplise」のインストール方法や使い方まで書かれています。 Javaの学習を始める時は、まずこの本を読んでおくことをオススメします!

  2. スッキリわかる Java入門 実践編 第2版 (スッキリシリーズ)

    スッキリわかる Java入門 実践編 第2版 (スッキリシリーズ)

    スッキリわかる Java入門 実践編 第2版 (スッキリシリーズ)

    • 作者:中山 清喬
    • 発売日: 2014/09/22
    • メディア: 単行本(ソフトカバー)

    本書は、Java入門の実践編で開発や設計手法、ツールの知識について、分かりやすく解説されています。
    Javaの応用技術まで紹介されているので、Javaの基本をマスターした方は、さらにステップアップで学びたい時に読むことをオススメします!

  3. Javaルールブック 〜読みやすく効率的なコードの原則

    コードの書き方について大変参考になる本です。
    正しい書き方と間違った書き方の例を挙げて分かりやすく解説されています。
    正しいコードの書き方を身につけると、シンプルで見やすいコードが書けるようになります。

  4. リーダブルコード

    Javaルールブックと同様にコードの書き方について、さらに具体的に丁寧に解説されています。

プログラミング学習の取り組み方

ここまでは学習サイトや書籍を紹介してきましたが、
以下については私が有効だと感じた具体的な学習方法と心構えについて説明します。

  1. 学習内容を読むだけにしない
    学習内容を読んで満足するだけでは理解が深まりません。 学習内容を読んで実際に操作して動作を確認してみる、疑問になったことを納得するまで深く掘り出すことが大切です。

  2. コードを1行1行にコメントをつける
    プログラミングの経験が長い方が書いたコードは、必要な情報だけコメントしています。
    プログラミング未経験の方は、まだコードの意味や書き方に慣れていないので、 まずコードの意味を理解することから始めると良いでしょう。
    慣れてきたら、少しずつコメントを減らして、コードを読める・書けるようにすると良いと思います。

  3. コードが完成したらデバッグをする
    プログラムを実行して結果が正しくても完成とは限らないです。 本当に問題がないかどうか、デバッグをして修正箇所がないか確認をするように心掛けましょう。 処理の流れを把握できるようになってきたら、デバッグ機能に頼らず、自分の目で確認してみましょう。

  4. エラーの原因と対策方法を見つける
    プログラムのバグ(故障・エラー)を見つける力と対策方法の知識を身につけることが大切です。
    例えば、プログラムが完成してバグに気付かないまま、共同者のプログラムを合わせると、 トラブルを起こしてしまう可能性があります。 このようなトラブルが起きた時、どこにバグがあるか、どのように対策をすれば解決できるか、 実際にプログラミングの練習でテストしてみると良いでしょう。
    またプログラムを作成する時は、バグがないプログラムを完成するように心掛けて書くようにしましょう。

  5. サンプルプログラムを鵜呑みにしない
    コードが書けない…という方は、インターネットや書籍にサンプルプログラムを参考にしていますが、 丸ごとコピーして修正するやり方は良くないことです。 サンプルプログラムを書き写して目的に合わせて修正し、動作の理解を深めることが良いでしょう。

  6. プログラミングの練習をできるだけ毎日やる
    スポーツと同じように、プログラミングを書く練習を毎日取り組むと、自然にコードの意味や書き方が分かるようになってきます。 最初は慣れなくて大変ですが、書籍や学習サイトを参考にしながら、書く練習をすると良いでしょう。

  7. プログラミングのツールを試してみる
    プログラミングのツールは、代表的なものだけではなく、あまり知られていないものや最近新しくできたツールを 試しに使ってみて比較することも大切です。 プログラミングの進化が続いているので、新たな知識をたくさん吸収すると良いでしょう。

  8. 簡単なものを開発してみる
    私の場合は、HTML/CSSの学習をしながら、自分のホームページを制作しました。 学習で学んだことを活かして、ホームページやアプリを開発してみると、楽しくやりがいがあると実感できます。

  9. プログラマーと情報交換をする
    プログラミング経験者は、技術情報やプログラミングの学習などたくさんの情報を持っています。 疑問や悩みまで相談できるので、プログラマーの仲間を作ることも大切です。

終わりに

プログラミング未経験者向けの学習方法と取り組み方を紹介しました。

紹介した学習方法の他にもたくさんありますので、
自分に合った方法を見つけて、是非プログラミングの学習を始めてみてください!

今だからこそ!マルチブラウザJavaScript再考

はじめに

皆さん初めまして、新卒一年目のFM_Harmonyと申します。

今回はマルチブラウザという観点から、最近のJavaScriptについて調べました。jQueryの登場や、主要なブラウザが標準仕様への準拠を進めたことで、エンジニアがJavaScriptの動作環境を気にすることが、以前に比べ減りました。しかし調べていくと、今後はトランスパイルAltJSによる対応が、JavaScriptにおいて必要になっていくのだということが分かりました。

このまとめが、最近Webアプリケーション開発に関わりはじめた方や、今後のJavaScriptについて興味のある方にとって、少しでも参考になれば幸いです。

目次

  • はじめに
  • 目次
  • 本題の前に
  • これまでのマルチブラウザ対策
    • JavaScriptとブラウザ
    • マルチブラウザ対策としてのjQuery
    • 各ブラウザの対応
  • これからのマルチブラウザ対策
    • ES6とIE11
    • トランスパイル
    • AltJS
  • まとめ
  • 注釈
続きを読む

セッション管理としてRedisを使用する

はじめに

みなさん こんにちは、Thuatと申します。今年ラクスに入社しました1年目です。 この記事ではセッション管理としてRedisを使用するケースを紹介します。

Redisとは?

Redis は簡単に言うと、メモリ上のKey-Valueストアです。 メモリ上にデータを格納しますので高速に動作します。
以下はインストールから簡単なデータの登録・取得までの手順になります。

Redisをインストールする

$ wget http://download.redis.io/releases/redis-4.0.1.tar.gz
$ tar xzf redis-4.0.1.tar.gz
$ cd redis-4.0.1
$ make

Redisサーバーを起動する

$ src/redis-server
33507:C 25 Sep 23:21:32.201 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
33507:C 25 Sep 23:21:32.202 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=33507, just started
33507:C 25 Sep 23:21:32.202 # Warning: no config file specified, using the default config. In order to specify a config file use src/redis-server /path/to/redis.conf
33507:M 25 Sep 23:21:32.203 * Increased maximum number of open files to 10032 (it was originally set to 256).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 4.0.1 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 33507
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

33507:M 25 Sep 23:21:32.206 # Server initialized
33507:M 25 Sep 23:21:32.206 * DB loaded from disk: 0.000 seconds
33507:M 25 Sep 23:21:32.206 * Ready to accept connections

Redisのポートはデフォルトで6379になります。

クライアントからGET/SETする

Redisにあらかじめ組込まれているクライアントツールからデータをGET/SETしてみましょう。
シンタックスは以下になります。
set key value
get key

$ src/redis-cli
127.0.0.1:6379> set key1 10000
OK
127.0.0.1:6379> get key1
"10000"
127.0.0.1:6379> 

ValueJSONでも保存できます。

127.0.0.1:6379> set user_info '{"username":"taro", "age":20}'
OK
127.0.0.1:6379> get user_info
"{\"username\":\"taro\", \"age\":20}"
127.0.0.1:6379>

Redisは メモリ上にデータを格納しますので、デフォルトはサーバーを停止、再起動した場合は、データが失われます。 Redisはどのような時に利用すればよいでしょうか。 最適なユースケースはキャッシュやセッション管理です。

セッション管理としてRedisを使用する

セッションとは?

セッションはクライアントとサーバの通信の状態をWebサーバー上に保持されます。クライアントはセッションIDをURLやクッキー経由でサーバに渡します。 Key-Value構造としてメモリ、ファイル又はDBに格納します。KeyはセッションID、Valueはセッションに保存したいデータです。
デフォルトではセッションは Webサーバーのメモリ上に保持されます。 Webサーバー単独でセッション管理を行う場合はWebサーバーのロードバランサを構成できません。 セッションをDBに格納する設定にすると、ロードバランサを構成することができますが、パフォーマンスに影響します。
セッションについては、
https://blog.takanabe.tokyo/2014/12/%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86%E3%81%AE%E5%91%A8%E8%BE%BA%E7%9F%A5%E8%AD%98%E3%81%BE%E3%81%A8%E3%82%81/
を参照ください。

Redisを使ってセッションを管理する

データをセッションではなくて Redisで管理する方法を紹介します。
Redisにはセッションのようにタイムアウト時間を設定できます。タイムアウト時間を超えた場合はRedisに保存したデータが自動的に削除されます。

以下の機能をJavaで実現してみます。

JavaでのRedisクライアントは多くありますが一番人気はJedisです。
https://github.com/xetorthio/jedis/wiki/Getting-started

プロジェクトのLibrariesにjedis-2.9.0.jarを追加してください。
http://search.maven.org/remotecontent?filepath=redis/clients/jedis/2.9.0/jedis-2.9.0.jar

jedis-2.9.0.jar を追加する


フォルダの構成は以下のようになります

Userクラスを作成します。

package jp.co.jedis;

public class User {
    private String username;
    private String address;

    public User(String username, String address) {
        this.username = username;
        this.address  = address;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "{'username':'"+this.username + "' , 'address':'"+ this.address + "'}";
    }

}


UserActionクラスを作成します。 ユーザーのデータをRedisに保存するためのクラスです。
3分後、自動にExpireされるように設定します。
KeyはユニークなUUID、ValueJSONでのユーザーのデータです。

package jp.co.jedis;

import java.util.UUID;

import redis.clients.jedis.Jedis;

public class UserAction {
    private static final int TIMEOUT = 3*60; //3分
    public static void main(String[] args) {
        String userToken = UUID.randomUUID().toString();
        System.out.println("user token: " + userToken);
        Jedis jedis = new Jedis("localhost");
        User user = new  User("tanaka", "Tokyo-shibuya");
        //3分後、無効になる
        jedis.setex(userToken, TIMEOUT, user.toString());
        jedis.close();

    }

}


Eclipseで実行してみましょう。


Redisのクライアントで確認しましょう。

127.0.0.1:6379> get a62a05d0-1efe-4d09-99f1-4289ff1511a6   -> 3分以内の場合
"{'username':'tanaka' , 'address':'Tokyo-shibuya'}"

127.0.0.1:6379> get a62a05d0-1efe-4d09-99f1-4289ff1511a6  -> 3分以上の場合
(nil)
127.0.0.1:6379> 

a62a05d0-1efe-4d09-99f1-4289ff1511a6 は生成されたKey、ユニークなUUIDです。

セッション管理としてRedisを使用するメリットは以下になります。

  • メモリ上に格納するのでパフォーマンスがよい

  • Webサーバーの負荷が減る

  • 別サーバーでセッション管理するので、Webサーバーのロードバランサを問題なく構成することができる
    ロードバランサを構成するには以下のような構成となります。

最後に

セッション管理としてRedisを使用する方法やJavaでRedisにアクセスする方法を紹介しました。 Redisはまだたくさんオプションがあります。
例えば接続数ハンドル、トランザクションクラスターなどをもっと知りたい方は https://github.com/xetorthio/jedis/wiki を参照ください。

参考資料

https://blog.takanabe.tokyo/2014/12/%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86%E3%81%AE%E5%91%A8%E8%BE%BA%E7%9F%A5%E8%AD%98%E3%81%BE%E3%81%A8%E3%82%81/

Redis

Home · redis/jedis Wiki · GitHub


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

今更聞けない『クラウド』の仕組み

はじめに

皆さんこんにちは、rs_tukkiです。今年新卒でラクスに入社しましたピッカピカの一年生です。

普段仕事や授業に取り組んでいると、「あれ、この単語どういう意味だろう?」と思っても、さも知ってて当然かのように話が進むせいでなかなか聞けなかったりすることありますよね。大丈夫です。私も割と多いです。
そんな「今更聞けない」言葉ですが、皆さん、こんな単語をご存知でしょうか。

クラウド

…はい、おそらく、知らない方はいないはずです。ですが、
クラウドサービスとか、最近よく聞くけどどんなサービスなの?」
「なんかデータを預けるとか聞いたことがあるけど、いざ質問されるとわからないな」

という方も多いでしょう。

そこで今回は、ラクスのシステムの根幹を担う「クラウド」が、どんなサービスなのかということについて、一から解説していこうと思います。

目次


クラウドとは?

概要

さて、クラウドについてネットで調べてみると、たまにこういう記述が見つかります。
クラウドの定義はとても曖昧です」
「定義や意味は曖昧である」

…さすがにこれでは元も子もないですね。
ですが、なんとか一言で「クラウド」を説明するのであれば、おそらく
「ユーザ一人ひとりがソフトやサーバを用意しなくても、ネットワークを通じてそれらを使うことが出来る、という考え方」
とするのが正しいと思います。

私たちが今までサービスを利用するには、専用のソフトウェアをインストールしたり、サーバを一から作る必要がありました。それが、クラウドによって「どこにあるか分からないけど、ネットワークの中から、必要な時に必要な量を取り出して利用する」ようになったのです。

クラウドという名称ですが、どこにあるかは分からない、けどどこかにある、というイメージが雲(=cloud)の中を手探りで操作しているように見えた、とか、ソフトやサーバを一極集中させていることから、ユーザがそこに群がっている(=crowd)ように見えた、など単語の意味自体には諸説あるようです。

クラウドという名前が生まれた背景

クラウド」という言葉を最初に使用し始めたのは、当時の米GoogleSEOエリック・シュミット氏だと言われています。これは2006年8月のことですが、実は有名なクラウドサービスの一つであるGmailは、2004年には既に稼働が始まっていました。 つまりクラウドという言葉は、技術の登場と同時に誕生したのではなく「元々存在していた技術に名前を付けたもの」ということになります。

なぜ、わざわざ名前を付けるようなことをしたのでしょうか。

それは、当時多くのサービスが「クラウドではなかった」からなのです。例えば、ワードやエクセル、パワーポイントなどのアプリケーション。これらは、当然自分のパソコンの中にインストールしなければ使えませんし、作成したデータも(USBなどの外部領域を使わないのであれば)一般的には、そのパソコンの中に保存しています。

このような非クラウドなサービスでも問題はないのですが、Gmailなどの誕生を機に「この形式でサービスを作れば、より便利になるんじゃないか」という流れが生まれました。そのため、「ユーザがそれぞれの領域にソフトやデータを確保する」という今までの使い方に対して、これからは「ネットワークにソフトやデータを配置して、場所は分からないけど簡単に使えるようにしよう」、ということを簡単に表す言葉として、「クラウド」が使われ始めたのです。


クラウドの使用例

さて、実際のクラウドの使用例についてですが、先ほど挙げたGmailなどのメールサービスをもとに説明します。
私たちがメールを送る際は、パソコン上でメールを作成や送受信を行うための「メールソフト」と、実際に送受信を制御するための「メールサーバ」が必要です。
メールソフトはOutlookBecky!Thunderbirdなどのソフトをインストールすればいいのですが、メールサーバの構築をクラウドなしで実現しようとすると結構大変です。企業であれば社内のメールサーバを持っているところもあるものの、個人で構築するのは手間ですし、あまり意味はありません。

一方、GmailHotmailなどの、クラウドサービスとして提供されているメールサービスでは、私たちユーザが用意するものは「インターネットに接続できるブラウザ」だけで十分です。ソフトのインストールも、サーバの構築も必要なく、
「どこにあるかはわからないけど、どこかにあるソフトとサーバを使わせてもらう」
ことで、メールの送受信を実現しているのです。


クラウド使用によるメリット・デメリット

ただ、クラウドサービスは便利なだけではなく、当然デメリットも存在します。個人向け、企業向けのクラウドサービスごとに、主な例を挙げて説明します。

個人向けクラウドサービスのメリット
  1. どこからでも、どんな端末からでもアクセスできる
    企業、個人問わず、クラウドサービスにおいて最もメリットを体感できるのはこれだと思います。わざわざSDカードやUSBメモリを使ってデータを移し替えなくても、データはインターネット上のサーバに保存されていますから、他のデバイスでもログインするだけでデータの共有が可能になります。買い物に行くとき、自宅のパソコンで欲しい商品を調べてメモしておいて、それをスマートフォンで確認する、といったことが可能になります。

  2. 更新作業が不要
    パソコンでもスマートフォンでも、インストールして使用するタイプのソフトは、定期的なアップデートが必要でした。ですが、クラウドサービスはソフトの更新をサーバ側で行ってくれるため、煩雑な更新作業や、それに伴うシステムの不具合に悩まされる必要もなくなるのです。

個人向けクラウドサービスのデメリット
  1. データ漏洩・紛失の可能性
    クラウドはインターネット上にデータを保管するわけなので当然と言えば当然なのですが、ログイン情報の流出や、その他サーバ側の不手際によって、簡単に個人情報が流出してしまう可能性があるわけです。前者はともかく、後者は使用者にとってはどうしようもないので、信頼のおけるサービスを使うようにしましょう。
    また、サーバ障害、サービス自体の廃止など、クラウド上に保存しておいたデータがいつのまにかなくなっていることも考えられます。大切なデータは、自分の手元にバックアップを取るようにした方が無難です。
企業向けクラウドサービスのメリット
  1. どこからでも、どんな端末からでもアクセスできる
    先ほど個人向けでも説明した内容と同じです。ネット環境さえあれば、たとえ出張先でも使えます。

  2. コスト削減
    ここでいう「コスト」とは、「初期コスト」と「保守・管理コスト」の2種類に分けられます。クラウドを用いず、自社でサーバやシステムを用意しようとすると、どうしても最初の環境整備や、稼働開始後の保守・管理に費用や人員を割かなければいけません。しかし、クラウドサービスにおいては、特に小規模のシステムで初期費用が抜群に軽減されるほか、煩雑なアップデート作業も管理会社が請け負ってくれるため、そのためのコストも削減が見込めるのです。

企業向けクラウドサービスのデメリット
  1. データ漏洩の可能性
    個人向けでも説明した内容ですが、企業向けのクラウドサービスでは扱うデータの性質上、よりハッキングの攻撃対象になりやすいというリスクがあります。当然企業向けの有料サービスであれば管理会社が何らかの対策はしてあるでしょうが、その面もきちんと確認しておくのが堅実かと思います。


クラウドサービスの分類

ここまで説明してきたクラウドサービスの提供形態ですが、実は更に細かく分類することができます。

SaaS(Software as a Service)

SaaSは、クラウドによってソフトウェアを提供するサービスのことです。先ほど例として挙げたGmailや、Google Driveなどのサービスは代表的なSaaSと言えるでしょう。
その他、個人向けではDropBox、企業向けではSalesforceなど、普段から皆さんがよく使っている「クラウドサービス」は、ほとんどがこのSaaSだと思います。

PaaS(Platform as a Service)

PaaSとは、クラウドによって開発環境を提供するサービスのことです。有名なところだとGoogle App EngineMicrosoft Azureなどがあります。
SaaSとは違い、PaaSとIaaSはエンジニア以外が使うことはあまりないと思います。利用者は提供された開発環境の上でウェブサービスを開発し、更に提供しているのです。

IaaS(Infrastructure as a Service)

IaaSとは、クラウドによってサーバを提供するサービスのことです。各種レンタルサーバなどはIaaSに含まれます。
PaaSより更に範囲が狭まって、サーバのみの提供となっていますが、開発環境から自分で用意できるため、自分の好きなプラットフォームで開発したい、という人はこちらを使うことになります。


おわりに

最近よく聞くけど、改めて考えてみるとイメージのつかめない「クラウド」という言葉について解説しました。
私自身大学でクラウドシステムを用いたゲームを開発していたことがあるのですが、意外と知らないことが多く苦戦しました改めて勉強し直すいい機会になったと思います。
今回の記事で、少しでも皆さんの「?」が解消出来たら幸いです!


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Git初心者のためのローカル作業時の備忘録

はじめに

皆さんはじめまして。新人エンジニアのsts-250rrです。
本年度新卒として入社し、1年目の私ですがよろしくお願いいたします。

Git初心者である方の中には、Gitの大枠がどんなものかは知っているし、使ったこともある。
でも機械的git status,git add -Aとしていたり、git commitをしていた。
というような方もおられるのではないでしょうか?(私はその口でした・・・)

各コマンドの正しい知識を持たずに機械的にコマンドを実行するのでは、 業務を行う上で正しい操作ができないことも考えられます。チームの開発ではなおさら御法度といえるでしょう。
私自身含め、そのような方が今後Gitで失敗をしてしまわないように手助けができればと思います。

本記事では、ローカル環境で作業をする際のGitの構成であるリポジトリやワークツリー・インデックスに関してまとめ、利用するコマンドがどのような操作であるのかを再確認していきます。 それらを把握したうえで、よく利用するGitコマンドをシーン別に分けて紹介し、コマンドによる操作のイメージを持っていただきたいと思います。
加えて、コミットの流れやコミットで失敗してしまったときの対処法も載せておきましたので、困ったときの参考になると幸いです。

リポジトリ・ワークツリー・インデックスって?

Gitではローカルで作業する際に以下のような構成でプロジェクトを管理しています。 この部分の構成を理解していると、addやcommitによってどのような操作がなされるのかがわかってくるかと思います。

  • リモートリポジトリ[Remote repository]
    サーバに配置されており、複数人でプロジェクトを共有する場所。
    *本記事での話題の対象ではありません。

  • ローカルリポジトリ[local repository]
    ローカル環境内でのプロジェクトのファイルやディレクトリの変更履歴を記録する場所。

    • ヘッド[HEAD]:最新のコミット状態を示す。
  • インデックス[index]
    ローカルリポジトリにコミットを行う準備をする場所。ワークツリーから登録された内容をgit commitでコミットする。
    インデックスが存在することで、ワークツリー内の必要のないファイルはコミットしないといったことができる。

  • ワークツリー[work tree]
    実際に作業を行っているディレクトリであり、編集を行った最新のファイルが存在する。
    ここでの作業内容をgit addすることでインデックスに変更を登録する。

これらローカル内の3つの構成により、Gitではプロジェクトの管理が行われています。 この部分が理解できればaddcommit、それらの操作を取り消すresetといった操作が理解しやすくなったのではないでしょうか。

シーン別:よく使うGitコマンド

前節まででローカル環境でのGitの構成の大枠は掴んでいただけたでしょうか?
本節では、多様なgitコマンドの中からよく使うコマンドを3つのシーンに分けてまとめています。
逆引きのように利用していただけると幸いです。

編集を確認したいとき
  • log
    • git log :最新までのコミットログを確認。
    • git log --oneline :コミットログを簡潔に確認。
  • status
    • git status:ローカルで編集されたファイルの一覧を表示。
  • diff
    • git diff:インデックスとワークツリーの全ての差分を表示。
    • git diff ファイル名:インデックスとワークツリーのしてファイルの差分を表示。
編集したものをリポジトリにコミットしたいとき
  • add
    • git add -A:ワークツリーの内容をすべてインデックスに登録。
    • git add <ファイル名>:編集した1ファイルのみをインデックスに登録。
    • git add <ファイル名1> <ファイル名2>:複数の編集をインデックスに登録。
  • commit
    • git commit:インデックスの内容を全てコミットする。
    • git commit -m 'message':コミットと同時に1行メッセージを書く。
addやコミットを取り消したいとき
  • reset
    • git reset:git addの内容を取り消す。
    • git reset --soft HEAD^:直前のコミットのみを取り消す。
    • git reset --hard HEAD^:現時点のインデックスやワークツリーごと直前のコミットを取り消す。

コミットするときの流れ

本節では実際コミットをするまでの流れを以下のTest.javaファイルと共に確認していきます。

public class Test {
    public static void main(String[] args) {

    }
}

Test.javaにある編集を加えました。編集は正しく反映されているでしょうか?
自身の編集が完了したらgit statusで現在の状態を確認します。

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   src/test/Test.java

no changes added to commit (use "git add" and/or "git commit -a")

Test.javaに変更があったようです。ソース内ではどのような変更があったのかgit diffで確認します。

$ git diff
diff --git a/src/test/Test.java b/src/test/Test.java
index 8e11c08..b625295 100644
--- a/src/test/Test.java
+++ b/src/test/Test.java
@@ -2,5 +2,6 @@ package test;

 public class Test {
        public static void main(String[] args) {
+               System.out.println("Test");^M
        }
 }

出力部分が追加されたようですね。ではこの編集をインデックスに登録しましょう。
確実にインデックスに追加したい自身の編集ファイルのみ git add src/test/Test.java で登録しましょう。

再度、現在の状態を確認します。

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   src/test/Test.java

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

インデックスに登録されている様子が確認できました。 それではコミットしていきましょう。

$ git commit -m 'Test出力の追加'
[master 2eb4313] Test出力の追加
 1 file changed, 1 insertion(+)

問題なくコミットできたようです。
最後にコミットの履歴のログを確認しておきましょう。

$ git log --oneline
2eb4313 (HEAD -> master) Test出力の追加
8823cc3 Test.javaの作成
97fde21 first commit

ここまでの作業が完了すればローカルでの編集内容が履歴として記録されることになります。
以降は同様に編集→コミットしていけばよいわけですね。
個人的にコミットまでの注意点は

  • git diffで変更差分を確認すること。
  • git addで必要のないファイルまでインデックスに登録しないこと

この2点だと思っています。
この点を意識できているかどうかだけでも、Gitに対する理解が深まるのではないでしょうか。

コミットで失敗したとき

次に、今までやってきた「addやcommitをやり直したい」そんなときの流れを簡単に確認します。
例として、前節で行ったコミットが間違えていたのでなかったことにしましょう。
git resetのオプションを指定して直前のコミットをなかったことにします。

$ git reset --soft HEAD^

ログやステータスを確認してコミットが取り消されていることと現在の状態を確認します。

$ git log --oneline
8823cc3 (HEAD -> master) Test.javaの作成
97fde21 first commit

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   src/test/Test.java

直前のコミットTest出力の追加が取り消されています。 ここまでで、直前のコミットを取り消し、編集内容がインデックスに登録されているという状態が確認できました。 次にこのadd自体も取り消してみましょう。
この場合はオプション無しのgit resetです。

$ git reset
Unstaged changes after reset:
M       src/test/Test.java

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   src/test/Test.java

no changes added to commit (use "git add" and/or "git commit -a")

addされた内容がインデックスの登録から外れた様子も確認できました。
ここまでで直前のコミットを取り消し、編集内容も取り消しました。つまり、2つ前のコミットの状態まで戻ったことになります。ここからは引き続き作業を行っていきましょう。

おわりに

最初にも述べたように機械的に行うcommitaddはGitを利用していく上で良いことではありません。
自身が利用するツールに関してはどのような利用方法があるのか、どのような操作であるのかを確認して利用していくと良いかと思います。

自身の学習を踏まえた記事ではありますが、読んで頂いた方々の参考になれば幸いです。


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Ansibleでバージョンアップ作業を自動化する

デベロッパーのkyosimotoです。

Ansibleをバージョンアップ作業の自動化ツールとして導入するための手順、おすすめ構成などについて紹介させていただきます。

目次

なぜAnsible

私は担当サービスのバージョンアップ作業を自動化するため、シェルベースのスクリプトを開発・運用してきました。 スクリプト導入により、ほとんどの作業は自動化され、運用コストは大幅に削減されました。

半面、バージョンアップスクリプトの属人化が課題となっており、リリース時のトラブルが起きた場合に、作業担当者による 原因調査と復旧作業が難しい状態となっています。

シェルスクリプトで作成したバージョンアップスクリプトには、フレームワークのような拘束力が無く、長い運用の中でイレギュラーケースに対応したコードやフラグが追加され、複雑化の道をすすみ続ける可能性があります。

さらには、今のところこの複雑化したスクリプトをポジティブに学習したいという人はいません。問題が起きたとしても、開発担当者に聞けばすぐに解決できるからです。

私は属人化を解消するためには、シェルスクリプトによる実装をやめ、学習コストが低く複雑なコードを生まない自動化ツールの導入が必要と考えており、この要件にマッチしたAnsibleを導入提案することになりました。

どんな感じ?

今のところメリットと感じているのは下記3点です。

  • 学習コストが低い
    設定ファイルはYAMLという形式で記述します。設定ファイルがシンプルで、初めての人でも内容をすぐに理解できると思います。
    運用作業用のモジュールが充実していますので、プログラミング書くことも読むこともほとんどありません。

  • 導入コストが低い
    管理対象サーバには余計なツールやデーモンをインストールする必要がありません。
    SSHPythonさえ使えれば、Ansibleからの操作が可能ですので、運用チームへも提案しやすいと思います。

  • 運用コストが低い
    少しの工夫で設定ファイルをそのまま手順書として扱うことができます。

Ansibleの基本

実行方法

Ansibleの実行コマンドは以下の通りです。

$ ansible-playbook -i {Inventory} {Playbook}   

Inventoryには、サーバ名やIPアドレスなどの管理対象ノードの情報、Playbookには管理対象ノードで実行するタスクを記述します。

実行イメージ

Ansibleは、上記コマンドを実行するとPlaybookの内容をPythonのプログラムに変換します。
変換されたプログラムファイルは、Inventory(インベントリ)に記述された管理対象ノードに転送後に実行されます。

f:id:kyoshimoto:20170926152405p:plain

マシン要件

対象 要件
コントロールマシン Python 2 (version 2.6 or 2.7)、またはPython3(version 3.5以上)がインストールされている。
Windowsはサポート対象外。  (詳細)
管理対象ノード SSH接続できる。
Python 2.6以上がインストールされている。  (詳細)

ファイル構成

私のお勧めするファイル構成サンプルを紹介します。

ディレクトリ構成(サンプル)
myapp_verup  
    product.ini            # inventory (本番環境のホスト名/IPを記述)
    staging.ini            # inventory (ステージングのホスト名/IPを記述)
    development.ini        # inventory (社内検証環境のホスト名/IPを記述)
    versionup.yml          # playbook (バージョンアップ手順を記述)
    roles/                 # 具体的バージョンアップ手順を実装するディレクトリ
        apacheを停止する/
        apacheを起動する/
        apacheをバージョンアップする/
        cronを停止する/
        cronを起動する/  
        postgresqlを停止する/
        postgresqlを起動する/
        postgresqlをバージョンアップする/
        アプリケーションをバージョンアップする/
playbook(サンプル)

以下、playbookファイルの内容です。
- myapp_verup/versionup.yml

---
- hosts: all
  roles:
    - cronを停止する

- hosts: webservers
  roles:
    - apacheを停止する
    - apacheをバージョンアップする
    - phpをバージョンアップする
    - アプリケーションをバージョンアップする

- hosts: dbservers
  roles:
    - postgresqlを停止する
    - postgresqlをバージョンアップする
    - postgresqlを起動する。

- hosts: webservers
  roles:
    - apacheを起動する

- hosts: all
  roles:
    - cronを起動する
ファイル構成のポイント

ファイル構成を考える上で、お勧めするポイントは下記2点です。

  • playbookを日本語で記述する playbookのタスクを日本語化することで、設定ファイルの可読性が上がる、playbookがそのまま手順書/ドキュメントになるという点でメリットが大きいと考えています。

  • バージョンアップ作業に集中する。
    バージョンアップ作業以外のタスクを含めないようにします。
    例えば、サーバ構成管理や冪等性のための実装を行うと、Ansibleの設定は複雑化します。 複雑化は属人化を進行させますし、(Serverspecなど)ツールを使ったテストなども検討する必要がでてくるでしょう。

検証環境の準備

ここからは、Ansibleの検証用環境の構築手順について記載します。

検証環境の説明

仮想マシンの構築に Vagrant + Virtualbox を利用します。 検証用に構築するサーバは以下の通りです。

ホスト名 IPアドレス OS MW/Tool
control 192.168.33.100 CentOS 6.9 Ansible
web 192.168.33.101 CentOS 6.9 Apache2.2 + PHP7.1
db 192.168.33.102 CentOS 6.9 PostgreSQL9.6

別のディストリビューションで検証したい場合は、Vagrant Cloudよりboxイメージを検索し、後述する「vagrant init」コマンドの引数に指定してください。

検証用仮想マシンの構築手順

# Vagrantの作業用ディレクトリを作成します。  
$ cd
$ mkdir -p vagrant_work/ansible_test
$ cd vagrant_work/ansible_test

# Vagrantの作業用ディレクトリを初期化します。    
$ vagrant init bento/centos-6.9


# 出力されたVagrantfileをエディタで修正します。
$ vi Vagrantfile
-----
# config.vm.box = "bento/centos-6.9"    # この行をコメントアウトし、以下の設定をコピペする。

config.vm.define "control" do |node|
  node.vm.box = "bento/centos-6.9"
  node.vm.hostname = "control"
  node.vm.network :private_network, ip: "192.168.33.100"
end

config.vm.define "web" do |node|
  node.vm.box = "bento/centos-6.9"
  node.vm.hostname = "web"
  node.vm.network :private_network, ip: "192.168.33.101"
end

config.vm.define "db" do |node|
  node.vm.box = "bento/centos-6.9"
  node.vm.hostname = "db"
  node.vm.network :private_network, ip: "192.168.33.102"
end
-----

# 仮想サーバを起動します
$ vagrant up

# 仮装サーバが起動するまでしばらく待ちます。

仮想サーバにSSH接続する

$ vagrant ssh control
# パスワードは「vagrant」

Windowsの場合は、ターミナルソフトで接続します。

ホスト 192.168.33.100
user vagrant
password vagrant

Ansible実行環境の構築

AnsibleのインストールとSSH設定の手順について記載します。

Ansibleのインストール

# Vagrantの作業ディレクトリより仮想サーバにSSH接続します。
$ vagrant ssh control
# パスワードは「vagrant」

# EPELリポジトリを追加
$ sudo yum install -y epel-release

# Ansibleのインストール 
$ sudo yum install -y ansible

# Ansibleのバージョン確認  
$ ansible --version
ansible 2.3.2.0 
  config file = /etc/ansible/ansible.cfg  
  configured module search path = Default w/o overrides   
  python version = 2.6.6 (r266:84292, Aug 18 2016, 15:13:37) [GCC 4.4.7 20120313 (Red Hat 4.4.7-17)]

SSH接続設定

接続先サーバへのSSH接続を簡単にするためSSH設定を記述します。

$ vi ~/.ssh/config
------------
Host *
  StrictHostKeyChecking no
  UserKnownHostsFile=/dev/null

Host web
  HostName 192.168.33.101
  User vagrant

Host db
  HostName 192.168.33.102
  User vagrant
------------

# 設定ファイルのパーミッションを変更します。
$ vi ~/.ssh/config
------------
Host *  
  StrictHostKeyChecking no  
  UserKnownHostsFile=/dev/null  
    
Host web    
  HostName 192.168.33.101   
  User vagrant  
    
Host db 
  HostName 192.168.33.102   
  User vagrant  
------------
    
# 設定ファイルのパーミッションを変更します。   
$ chmod 600 ~/.ssh/config
    
# SSHの公開鍵を登録します。  
$ ssh-keygen -t rsa
Generating public/private rsa key pair. 
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):  # Enterキー入力
Enter passphrase (empty for no passphrase):  # Enterキー入力
Enter same passphrase again:   # Enterキー入力
Your identification has been saved in /home/vagrant/.ssh/id_rsa.    
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.    
The key fingerprint is: 
# 秘密鍵と公開鍵が作成されます。
~/.ssh/id_rsa
~/.ssh/id_rsa.pujb

# 作成したSSH公開鍵を接続先サーバにコピーします。 
$ ssh-copy-id web
# パスワードは「vagrant」   
$ ssh-copy-id db
# パスワードは「vagrant」   

# 接続先サーバにパスワードなしでアクセスできることを確認します。 
$ ssh web
$ hostname
web 
$ exit
    
$ ssh db
$ hostname
db  
$ exit

検証用仮想マシンミドルウェアセットアップ

バージョンアップ手順の検証用に、予めミドルウェアをセットアップします。

WEBサーバの構築 (Apache2.2 + PHP7.1)

# WEBサーバにSSH接続    
$ ssh web
    
# Apacheをインストール  
$ sudo yum install -y httpd
    
# PHPをインストール 
$ sudo yum install -y epel-release
$ sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
$ sudo yum -y --enablerepo=remi-php71,epel install php php-cli php-common php-mbstring php-mcrypt php-pdo php-xml php-json php-devel php-pecl-zip php-pgsql
$ sudo service httpd restart
$ sudo chkconfig httpd on
    
# テストページを作成してApache+PHPの連携確認   
$ sudo vi /var/www/html/phpinfo.php
--------------
<?php
phpinfo();
--------------
# ブラウザから http://192.168.33.101/phpinfo.php にアクセスできることを確認 

# SSH接続を終了   
$ exit

DBサーバの構築(PostgreSQL9.6)

# DBサーバにSSH接続 
$ ssh db

# PostgreSQLのインストール  
$ sudo yum install -y https://yum.postgresql.org/9.6/redhat/rhel-6.9-x86_64/pgdg-redhat96-9.6-3.noarch.rpm
$ sudo yum -y install postgresql96-server
    
# PostgreSQLの初期設置    
$ sudo service postgresql-9.6 initdb
$ sudo vi /var/lib/pgsql/9.6/data/pg_hba.conf
--------------
# 末尾に追加  
host    all            all              192.168.33.101/32        trust 
--------------

$ sudo vi /var/lib/pgsql/9.6/data/postgresql.conf
# 接続設定追加(59行目あたり)  
--------------
#listen_addresses = 'localhost'    
listen_addresses = '*'   
--------------

# PostgreSQLの再起動   
$ sudo service postgresql-9.6 start
$ sudo chkconfig postgresql-9.6 on
    
# データベース&テーブル作成  
$ sudo su - postgres -c "psql"

# ココからはSQLモード  
> \c test
> create database test;
> create table t_staff (id int, name text);
> insert into t_staff values (1, 'あああああ'), (2, 'いいいいい');
> \q
# SSH接続を終了   
$ exit

WEB/DBサーバの連携チェック

# WEBサーバにSSH接続    
$ ssh web

# WEBサーバとDBサーバ間の疎通確認 
$ sudo vi /var/www/html/test.php
---------
<?php  
$connectString = "host=192.168.33.102 port=5432 dbname=test user=postgres";    
$conn = pg_connect($connectString);   
$result = pg_query($conn, "select * from t_staff");    

var_dump(pg_fetch_all($result));    
---------   

# ブラウザから下記URLにアクセスして、DBレコードが出力されることを確認   
http://192.168.33.101/test.php    

プロジェクトディレクトリ作成

ファイル構成の項目で紹介したAnsibleプロジェクトを作成していきましょう。

バージョンアッププロジェクト用のディレクトリ作成

# HOMEディレクトリにプロジェクトディレクトリを作成します。  
$ mkdir ~/myapp_verup

# フォルダを作成します   
$ cd ~/myapp_verup
$ mkdir -p roles/apacheを停止する/tasks
$ mkdir -p roles/apacheを起動する/tasks
$ mkdir -p roles/apacheをバージョンアップする/tasks
$ mkdir -p roles/cronを停止する/tasks
$ mkdir -p roles/cronを起動する/tasks
$ mkdir -p roles/postgresqlを停止する/tasks
$ mkdir -p roles/postgresqlを起動する/tasks
$ mkdir -p roles/postgresqlをバージョンアップする/tasks
$ mkdir -p roles/アプリケーションをバージョンアップする/tasks

Inventoryの作成

Inventoryでは、管理対象ノードのホスト名、またはIPアドレスとグループの定義を行います。 ファイルはINIファイル形式で記述します。

  • myapp_verup/development.ini
[webservers]
192.168.33.101

[dbservers]
192.168.33.102
Inventoryの作成(補足)

Inventoryを本番環境用、ステージング環境用、開発環境用に分けて管理することで バージョンアップ対象の切り替えできるようにします。

以下サンプルです。

  • myapp_verup/product.ini (本番環境用)
# 本番環境用のInventory
[webservers]
192.168.34.101
192.168.34.102
192.168.34.103

[dbservers]
192.168.34.104
192.168.34.105
  • myapp_verup/staging.ini (ステージング環境用)
# ステージング環境用のInventory
# セクション名(グループ名)は同じでIPアドレスのみ異なる。
[webservers]
192.168.35.101
192.168.35.102
192.168.35.103

[dbservers]
192.168.35.104
192.168.35.105

Playbookの作成

Playbookには、バージョンアップ手順を記述します。
(どのサーバでどんなタスクをどのような順番で実行するかを記述します)

  • myapp_verup/versionup.yml
---
- hosts: all
  roles:
    - cronを停止する

- hosts: webservers
  roles:
    - apacheを停止する
    - apacheをバージョンアップする
    - phpをバージョンアップする
    - アプリケーションをバージョンアップする

- hosts: dbservers
  roles:
    - postgresqlを停止する
    - postgresqlをバージョンアップする
    - postgresqlを起動する

- hosts: webservers
  roles:
    - apacheを起動する

- hosts: all
  roles:
    - cronを起動する

ファイルはYAML形式となりますので、拡張子は「yml」、1行目は「---」としてください。

2行目以降にバージョンアップ手順を記述します。書き方のルールは以下の通りです。

セクション 解説
hosts inventoryに定義したグループ名を記述します。
"all"の場合、inventoryに記述した全サーバを対象に処理を実行します。
roles hostsセクションに指定したサーバで実行するタスクを記載します。
タスクは「◯◯を停止する」「◯◯を起動する」「◯◯をバージョンアップする」くらいの粒度で記述しておき、具体的な処理内容をroles配下のタスク名と同名のディレクトリ配下に実装します。

ansible.cfgの作成

Ansibleの動作設定を記述するファイルで、INI形式で記述します。
- myapp_verup/ansible.cfg

[defaults]
# 実行時のログを出力するファイルを指定します。
log_path=/tmp/ansible.log

[privilege_escalation]
# タスクの実行ユーザをrootに設定します
become = true
become_user = root

Roleの作成

Playbookのrolesディレクティブに記述したタスクの実態をrolesディレクトリの配下作成します。 例えば、「Cronを起動する」というタスクは、roles/Cronを起動する/tasks/main.yml に、処理内容を記述します。

ミドルウェアの起動

service コマンドが準備されているミドルウェア(デーモン)であれば、「service」モジュールを使って以下のように記述します。

  • myapp_verup/roles/cronを起動する/tasks/main.yml
---
- name: crondを起動する
  service:
    name: crond 
    state: started
  • myapp_verup/roles/apacheを起動する/tasks/main.yml
---
- name: Apacheを起動する
  service:
    name: httpd
    state: started
  • myapp_verup/roles/postgresqlを起動する/tasks/main.yml
---
- name: PostgreSQLを起動する
  service:
    name: postgresql-9.6
    state: started

※「service」モジュールの使い方はこちら

(補足)ミドルウェアの起動

service コマンドが提供されていないミドルウェアの場合は、「shell」モジュールと「wait_for」モジュールで実装することもできます。

---            
- name: 起動コマンドを実行する          
  shell: /usr/local/myapp/apache/bin/apachectl start

- name: 80番ポートの疎通確認が終わるまで待機する
  wait_for:  
    host: localhost
    port: 80    
    state: started
    delay: 1
    timeout: 60

shell モジュールは終了ステータスコードが0以外は、すべてエラーとして処理を中断しますので注意が必要です。 ステータスコードが0以外でも処理を継続する場合には、下記サンプルを参考にしてください。

---
- name: スクリプトを実行する
  shell: /usr/local/myapp/bin/hoge.sh
  register: exitStatus
  failed_when: exitStatus.rc not in [0, 100]   # 終了ステータスが0 or 100の場合はエラーにしない        

※「shell」モジュールの使い方はこちら
※「wait_for」モジュールの使い方はこちら

ミドルウェアの停止

service コマンドが準備されているミドルウェア(デーモン)であれば、「service」モジュールを使って以下のように記述します。

  • myapp_verup/roles/cronを停止する/tasks/main.yml
---
- name: crondを停止する
  service:
    name: crond 
    state: stopped
  • myapp_verup/roles/apacheを停止する/tasks/main.yml
---
- name: Apacheを停止する
  service:
    name: httpd
    state: stopped
  • myapp_verup/roles/postgresqlを停止する/tasks/main.yml
---
- name: PostgreSQLを停止する
  service:
    name: postgresql-9.6
    state: stopped

ミドルウェアのバージョンアップ

RPMyumコマンドでバージョンアップできる場合は、「yum」モジュールを利用して「latest」の状態に更新します。

  • myapp_verup/roles/apacheをバージョンアップする/tasks/main.yml
---
- name: RPMを更新する
  yum:
    name: httpd
    state: latest
  • myapp_verup/roles/phpをバージョンアップする/tasks/main.yml
---
- name: RPMを更新する
  yum: 
    name={{ item }}
    state=latest
    enablerepo=remi,epel
  with_items:
    - php
    - php-cli
    - php-common
    - php-mbstring
    - php-mcrypt
    - php-pdo
    - php-xml
    - php-json
    - php-devel
    - php-pecl-zip
    - php-pgsql
  • myapp_verup/roles/postgresqlをバージョンアップする/tasks/main.yml
---
- name: RPMを更新する
  yum: 
    name=postgresql96-server
    state=latest

リポジトリ管理されていない(カスタムRPMをつかっている)場合は、以下設定を参考にしてください。

myapp_verup
  └ roles
       └ Apacheをバージョンアップする
             ├ tasks
             │  └ main.yml
             ├ files
             │  └ myapp_apache2.2.99.rpm # カスタムPRMを格納
             ├ templates
             │  └ httpd.conf.j2          # テンプレートファイルを格納。拡張子は「.j2」にする。(テンプレートエンジン「Jinja2」を利用)
             └ vars
               └ main.yml               # RPMのファイル名やチェックサム値などを記述する

ディレクトリ構成については公式ページのBest Practices の「Directory Layout」を参考にしています。

  • myapp_verup/roles/Apacheをバージョンアップする/tasks/main.yml
---
- name: RPMファイルを転送する
  copy: src=files/{{ rpm_file_name }} dest=/tmp 

- name: RPMファイルの状態を取得する
  stat:
    path: /tmp/{{ rpm_file_name }}
  register: file_status

- name: チェックサム値を確認する
  fail: msg='MD5 value did not match'
  when: file_status.stat.md5 != rpm_file_md5

- name: "RPMを更新する ({{ rpm_file_name }})"
  shell: rpm -Uvh --force --nodeps /tmp/{{ rpm_file_name }}
  args:
    chdir: "/tmp"
  register: verup_result

- name: ステータスコードを確認する
  fail: msg="Failed upgrade rpm."
  when: verup_result.rc != 0

- name: httpd.confを差し替える
  template:
    src=httpd.conf.j2
    dest=/usr/local/vanguard/apache/conf/httpd.conf
    owner=root
    group=root
    mode=644

※「copy」モジュールの使い方はこちら
※「stat」モジュールの使い方はこちら
※「fail」モジュールの使い方はこちら
※「template」モジュールの使い方はこちら

  • myapp_verup/roles/Apacheをバージョンアップする/templates/httpd.conf.j2
...中略...
# サーバ毎に異なる設定は 2重波括弧と変数名を記述しておきます。
ServerName {{ apache_server_name }}:80
...中略...

※ 上記変数部分は、例えばInventoryファイルに記述した変数の値に自動で 置き換えることができます。

...中略...
[web:vars]
apache_server_name = myapp.example.com
...中略...
  • myapp_verup/roles/Apacheをバージョンアップする/vars/main.yml
---
# RPMファイル名を記述します。
# この値はtask/main.ymlで参照されます。
rpm_file_name: "vg_httpd-2.4.25-centos6.x86_64.rpm"

# RPMファイルのチェックサム値を記述します。
# この値はtask/main.ymlで参照されます。
rpm_file_md5: "4f8009b1cbcf5dbc7f082773d2f0d661"

playbookの実行

$ cd ~/myapp_verup
$ ansible-playbook -i development.ini versionup.yml

実行結果は以下の通りです。
日本語で書いたタスクがそのままターミナル上のログに出力されていることが確認できます。
(/tmp/ansible.log にも出力されます。)

f:id:kyoshimoto:20170926174322p:plain

最後に

本記事は、運用チーム向けにAnsibleを使ったバージョンアップ自動化提案後に書いた記事です。
本番運用が開始されれば、いろいろと課題はでてくると思いますので、知見がたまりましたら
改めて情報を共有させていただこうと思います。

以上、ありがとうございました。


  • エンジニア中途採用サイト
    ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
    ご興味ありましたら是非ご確認をお願いします。
    20210916153018
    https://career-recruit.rakus.co.jp/career_engineer/

  • カジュアル面談お申込みフォーム
    どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
    以下フォームよりお申込みください。
    forms.gle

  • イベント情報
    会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com

意図しない処理が実行されるCSRFとは?概要と対策

はじめに

 こんにちは、mickey-STRANGEです。昨年に新卒でラクスに入社しました2年目です。

 新卒に毛が生えた程度の新米エンジニアですが、今回はその数少ない毛の中から学生時代に意識したことのなかったものという観点で脆弱性のお話を選び、記事にしました。新しくWeb開発企業に入社した新卒の方の学習の手助けになればと思います。

目次

脆弱性とは?

 脆弱性とは、セキュリティの面からみたシステムの欠陥のことです。 脆弱性をついて攻撃されてしまうと、ユーザには本来許されていない操作ができてしまったり、サーバにウィルスを仕込まれてしまったりという被害が出ます。

 この記事では脆弱性の中でも、私が実際に理解に時間のかかった

という脆弱性の概要と対策をご紹介したいと思います。

CSRF(クロスサイトリクエストフォージェリ)

概要

 サービスの利用者に意図しないHTTPリクエストを送信させ、利用者の意図しない処理をサービスに実行させる攻撃です。と、1文で書いてはみるものの、これだけで理解は出来ないと思います(私が勉強したときは理解出来ませんでした)ので、shop.example.comという架空の通販サイトで簡単な例を挙げてみましょう。

 shop.example.comではユーザはIDとパスワードでログインします。利用ユーザがパスワードを変更しようとしたとき(ユーザが新しいパスワードを入力して確定ボタンを押したとき)に送信されるリクエストが下のようなものだったとします。

URL
 http://shop.example.com/password/change
パラメータ
 new_pass:【新しいパスワード】
 new_pass_conf:【新しいパスワード(確認入力用)】

 このリクエストと同じものを作成して送信させる悪意のあるWebページを攻撃者が用意します。 偽装リクエストを送信さえできればよいのでWebページの用意は難しいことではありません。具体的なコードは示しませんが、javascriptを使って数行で実現出来ます。GETでよければ他の手段でもっと単純に、コードとしては1行でも十分に実現できてしまいます。(確定処理でGETを使用すること自体が論外ですが。)

 さて、shop.example.comのユーザAさんが、攻撃者の用意した悪意のあるWebページを開いてしまったとしましょう。

 するとAさんのブラウザから上記リクエストと同じものが勝手に送信されてしまいます。正規にパスワード変更画面で確定ボタンを押したときのリクエストと違うところがあるとすれば、パラメータのパスワード部分は攻撃者しか知らないものである、ということだけです。 このときAさんのブラウザに「shop.example.comにログインしている状態のセッション情報」が残っていた場合、その情報もブラウザが同時に送信してしまいます。 そのリクエストを受け取ったサーバ側のプログラムはAさんが操作を再開して、パスワードの変更確定ボタンを押したと誤認し、パスワード変更の処理を行ってしまいます。パスワード変更の処理が完了するとAさんはもうログイン出来なくなってしまいます。

 CSRFログイン済みのユーザに、意図しない操作を強制的に実行させてしまう攻撃であるといえます。

 今回の例ではパスワード変更ですが、これが購入や決済の確定処理で起きてしまうと取り返しがつかないということは簡単に想像出来ると思います。ではCSRFがどのような脆弱性か分かったところで、CSRF攻撃を防ぐためにどのような仕組みを入れればよいかを考えてみようと思います。

対策

 ではCSRFの対策を考えてみましょう。 上記の通り、CSRFにおいてサーバはリクエスト通りに1つの機能を正しく完了しているという特徴があります。つまり対策として考えられるのは受け取ったリクエストに対して処理を実行するかしないかを確認することになります。

確定処理の前に認証を行う

 一番分かりやすい方法はユーザに確認してもらうことです。重要な処理の前にはユーザにもう一度認証を行う、つまりログインIDとパスワードの入力をしてもらうことになります。今回のパスワード変更の例ですと、以下のようになります。(数字がユーザ操作、→がサーバ処理です。)

1.新しいパスワードを入力して確定ボタン
2.認証画面でログインIDとパスワードを入力
 →変更処理を行う

 この順番で操作してもらうようになっていれば、今回の例のように1番のリクエストを偽装されても処理を行ってしまうことはありません。

 しかし、この方法で対策するとユーザの操作が増えてしまいます。大事な処理の前だけとしても、ユーザの操作量が増えてしまったり、直感的に進めない画面が表示されると「このサイトは使いにくい、面倒だ」と感じてしまうかもしれません。ユーザに負担をお願いしたくない、ということで次にプログラム側だけで出来る対策を考えてみます。

リファラを確認する

 ユーザ操作を増やさずに出来るCSRF対策は「送られてきたリクエストが正しいものか確認する」ことです。ここで正しいリクエストとは、サイト内にある確定ボタンを押すことで送信されたリクエストということになります。それを確認するために、リファラというHTTPヘッダの1つを利用します。

 リファラとは、リクエスト元のページのURLを示すHTTPヘッダです。処理の前にリファラの値がhttp://shop.example.comから始まっているかを確認すれば、偽装リクエストかどうか判定することが可能です。

1.新しいパスワードを入力して確定ボタン
 →リファラのチェックを行う
 →変更処理を行う

 しかし、この方法ではまだ完璧な対策ではありません。リファラはその特性上、プライバシー面に問題を抱えており、ブラウザのでリファラを送信しない設定が可能です。また、前述のとおり、リファラはHTTPヘッダの1つです。改竄されてしまっては元も子もありません。

ワンタイムトークンを利用する

 ワンタイムトークンとは、リクエストが正しいものか判断するための文字列をリクエストの中に仕込む手法です。

 今回のパスワード変更の例ですと、以下のような流れになります。

0.パスワード変更画面を開く
 →乱数文字列(トークン)を生成
1.新しいパスワードを入力して確定ボタン(トークンを付与したリクエストを送信)
 →サーバ側で保持しておいたトークンとリクエストで送信されたトークンの比較を行う
 →変更処理を行う

 パスワード変更画面を開く際にトークン(乱数文字列)を作成し、セッションなどのサーバ側の領域で一時保存しておきます。また、確定のリクエストの時にそのトークンをパラメータに追加して送信させ、サーバ側でトークンの照合を行います。サーバ側で保存していたトークンと一致すれば処理を行う、一致しなければ不正なリクエストとして処理を行わない、といった判断が可能となります。

 偽装リクエストがもしトークンを勝手に付与したリクエストを送信してもサーバ側で同じトークンを保持していないので判別できますし、リファラとは違いヘッダではなくパラメータなのでユーザ依存で失敗したりすることがありません。今回紹介した中ではこの手法が最も適切だといえるでしょう。

おわりに

 CSRFの概要と対策についてご紹介いたしました。いかがでしたでしょうか。 XSS(クロスサイトスクリプティング)と名前が似ていることで勘違いしやすい脆弱性で、自分が理解しづらかったCSRFについて記事にしてみました。

 Web開発に関する勉強を始めたばかりの方の手助けになれば幸いです。


  • エンジニア中途採用サイト
    ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
    ご興味ありましたら是非ご確認をお願いします。
    20210916153018
    https://career-recruit.rakus.co.jp/career_engineer/

  • カジュアル面談お申込みフォーム
    どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
    以下フォームよりお申込みください。
    forms.gle

  • イベント情報
    会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.