③CoreOSでDockerしてみる(Dockerを使って、Jenkinsをインストール)

Dockerを使って、Jenkinsをインストールしてみます。


■Dockerイメージの取得
公式のDockerイメージが配布されていますので、そちらを利用します。
下記コマンドでイメージを取得します。

docker pull jenkins


■Jenkinsコンテナの起動
下記コマンドでコンテナを起動します。

mkdir /home/core/jenkins_home
sudo chown 1000 /home/core/jenkins_home
sudo docker run --restart=always --name jenkins -d -p 8080:8080 -v /home/core/jenkins_home:/var/jenkins_home -v /etc/localtime:/etc/localtime:ro
 -e JAVA_OPTS='-Duser.timezone=Asia/Tokyo -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8' jenkins


・[--restart=always]オプション指定で、OS起動時に自動起動します。
・[--name jenkins]でコンテナ名を指定します。
・[-v]オプション指定で、Dockerコンテナが停止しても、/home/core/jenkins_homeにデータが残ります。(データの永続化)
・[-p 8080:8080]でポート番号を指定する。とりあえずそのまま。
・[-v /etc/localtime:/etc/localtime:ro]でホストと時刻同期する。(同期しないとコンテナ内はUTCになる)
・[-e JAVA_OPTS='-Duser.timezone=Asia/Tokyo 〜]でJenkinsのタイムゾーンとファイルエンコードを指定する。


http://:8080/ にブラウザからアクセスします。


また、Dockerの仮想コンテナは、パラメータで、コンテナ名、ポート番号、データ永続化フォルダが指定できるので、1サーバ内にJenkinsを複数立ち上げることもできます。
JENKINS_HOME等が競合しないので、便利ですね。

sudo docker run --restart=always --name jenkins1 -d -p 8081:8080 -v /home/core/jenkins_home1:/var/jenkins_home jenkins
sudo docker run --restart=always --name jenkins2 -d -p 8082:8080 -v /home/core/jenkins_home2:/var/jenkins_home jenkins
sudo docker run --restart=always --name jenkins3 -d -p 8083:8080 -v /home/core/jenkins_home3:/var/jenkins_home jenkins

自宅開発環境のアップデート

自宅開発環境アップデートです。
前回の教訓を生かして、こまめにアップデートします。自分への覚書を残しておきます。


Ubuntuアップデート
VirtualBox上のEclipse等を詰め込んだ、Ubuntu開発環境をアップデート。
デスクトップ左側のランチャーの1番上の「unity-dash」アイコンをクリック→「update」と入力→「ソフトウェアの更新」で手動アップデートします。
Ubuntu15.04→15.10にアップグレード。特に問題なく終了。


Hyper-V Serverアップデート
Hyper-V ServerのWindows Updateを実行。特に問題なく終了。
Hyper-V上のCentOS5.11は、yum update でアップデート。特に問題なく終了。
Hyper-V上のJenkinsとJenkinsプラグインもアップデート。特に問題なく終了。

②CoreOSでDockerしてみる(Panamaxをインストール)

Panamaxをインストールしてみます。


■Panamaxとは?
オープンソースのWEBアプリケーションです。
Dockerコンテナの管理やインストールをGUI上で実行できます。
http://panamax.io/


■Panamaxをインストール
下記コマンドで、CoreOSにインストールします。

sudo su
curl -O http://download.panamax.io/installer/panamax-latest.tar.gz && mkdir -p /var/panamax && tar -C /var/panamax -zxvf panamax-latest.tar.gz
cd /var/panamax
./coreos install --stable


http://:3000/ にブラウザからアクセスします。


Dockerレポジトリを検索して、公開されているDockerコンテナをインストールすることができます。
例えば、Wordpressをインストールする場合、通常なら、WordpressMySQLをインストールして設定する必要があります。
Dockerなら、Dockerコンテナをインストールするだけで、Wordpressが使用できます。
(Dockerコンテナに、WordpressMySQL等の必要なソフトウェアがインストール&設定済み)
また、Panamaxを使えば、WEBブラウザ上から、Dockerコンテナをインストールすることができます。


インストール済みのDockerコンテナを一覧表示できます。
設定変更や削除等ができるみたいですね。

①CoreOSでDockerしてみる(CoreOSのセットアップ)

CoreOSでDockerしてみます。


■CoreOSとは?
Dockerコンテナ実行に特化した、Linuxベースの軽量OSです。
https://coreos.com/


■Dockerとは?
コンテナ型の仮想化オープンソースソフトウェアです。
従来はOS全体を仮想化していましたが、コンテナ型の仮想化は、ソフトウェアアプリケーション単位で仮想化します。
その為、インスタンス生成やインスタンス起動が早く、性能劣化が少ない利点があります。
また、仮想化なので、プラットフォームやハードウェアに依存しません。
例えば、WEBサーバ、WEBアプリケーション、データベース等を1つのコンテナで仮想化しておけば、
開発PC上で作成したコンテナを、そのまま本番環境に配置する事ができます。
https://www.docker.com/




まずは、Hyper-V環境に、CoreOSをセットアップします。


■CoreOSのセットアップ
下記から、「coreos_production_iso_image.iso」をダウンロードします。


・Booting CoreOS from an ISO
https://coreos.com/os/docs/latest/booting-with-iso.html


新規にHyper-V仮想マシンを作成し、ダウンロードしたISOから起動します。


CoreOSが立ち上がるので、ユーザ名Coreのパスワードを設定します。

$ sudo passwd core


インストール後のssh接続用パスワードのハッシュ値を取得します。

$ openssl passwd -1
$【ハッシュ値が表示される】


初期セットアップファイルを作成します。

$ vi cloud-config.yml
#cloud-config

coreos:
  update:
    reboot-strategy: best-effort
  units:
    - name: etcd.service
      command: start
    - name: fleet.service
      command: start
    - name: docker.service
      command: start
    - name: timezone.service
      command: start
      content: |
        [Unit]
        Description=timezone
        [Service]
        Type=oneshot
        RemainAfterExit=yes
        ExecStart=/usr/bin/ln -sf ../usr/share/zoneinfo/Japan /etc/localtime

users:
  - name: core
    passwd: `ハッシュ値` ※「openssl passwd -1」で取得したハッシュ値を設定する
    groups:
      - sudo
      - docker


下記のコマンドでインストールします。

#インストール
sudo coreos-install -d /dev/sda -C stable -c cloud-config.yml
sudo shutdown -h now

#インストールCDを外して、起動する

#cloud-config.ymlを保存しておく
sudo coreos-cloudinit -from-file=./cloud-config.yml
sudo cp cloud-config.yml /var/lib/coreos-install/user_data
sudo reboot

⑤Meteorに触ってみる(アカウント管理を追加してみる)

公式サイトのTutorialsを参考に、
前回作成したアプリケーションに、アカウント管理を追加してみます。


meteorには、アカウント管理の部品が標準で用意されています。
部品を利用するだけで、アカウント管理が実装できます。


メール認証、TwitterFacebook、GoogleAccount認証とかもできるみたいですが、
今回は、シンプルなパスワード認証を追加してみます。


まずは、下記コマンドを実行して、パスワード認証を追加します。

meteor add accounts-ui accounts-password


■simple-todos.html を変更します。

<body>
  <div class="container">
    {{> loginButtons}}
    {{#if currentUser}}
   〜
    {{/if}}
  </div>
</body>

<template name="task">
  <li class="{{#if checked}}checked{{/if}}">
   〜
    <span class="text"><strong>{{username}}</strong> - {{text}}</span>
  </li>
</template>

{{> loginButtons}} を追加します。
ログインしている時だけ、表示したい部分を{{#if currentUser}} 〜 {{/if}}で囲みます。


{{username}}は、ログインユーザです。
誰が追加したタスクであるかを表示します。


■simple-todos.js を変更します。

// クライアント側のコード
if( Meteor.isClient ){

  //--------------------------------------
  // アカウント管理
  Accounts.ui.config({
    passwordSignupFields: "USERNAME_ONLY"
  });


  //--------------------------------------
  // <body>内のイベント
  Template.body.events({


     // Form送信(新規タスクの入力)
    "submit .new-task": function( event ) {

   〜
 
      // タスクの追加
      Tasks.insert({
        text: text,
        createdAt: new Date(), // 現在時刻
        username: Meteor.user().username  // ログインユーザ名
      });
 
   〜
    }

  });

}

アカウント管理の宣言を追加します。
また、タスク追加時に、ログインユーザ名も保存するように変更します。


初回アクセス時は、こんな感じです。


Sign in▼」をクリックすると、アカウント画面が出てきます。
右下にある「Create account」をクリックします。


ユーザ名、パスワードを入力し、「Create account」ボタンを押して、ユーザを作成します。


作成したユーザでログインしてみます。
Sign in」ボタンを押して、ログインします。


ログインできました。
画面の上部に、ユーザ名が表示されています。


タスクを追加してみます。
タスク名の前に、ユーザ名が表示されています。


■meteorのアップデートについて
今回、meteorをアップデートしたら、作成したアプリが動かなくなってしまいました。
そんな時は、一度、resetすると動くようになります。

meteor update
meteor reset
meteor

④Meteorに触ってみる(簡単なDB検索/更新のアプリを作成してみる)

公式サイトのTutorialsを参考に、簡単なDB検索/更新のアプリを作成してみます。


まずは、下記コマンドで、アプリを作成します。※「simple-todos」の部分は、任意のアプリ名を指定します

meteor create simple-todos


simple-todosフォルダ配下に下記ファイルが生成されます。
Tutorialsを参考にして、ファイルの中身を変更します。

  • simple-todos.html
  • simple-todos.js
  • simple-todos.css


■simple-todos.html

<head>
  <title>Todo List</title>
</head>
<body>
  <div class="container">
    <header>
      <h1>Todo List</h1>
      <input type="text" name="taskSearch" placeholder="検索するタスクを入力してください" value="{{taskSearchQuery}}" size="50"/>
      <form class="new-task">
        <input type="text" name="text" placeholder="追加するタスクを入力してください" />
      </form>
    </header>
    <ul>
      {{#each tasks}}
        {{> task}}
      {{/each}}
    </ul>
  </div>
</body>

<template name="task">
  <li class="{{#if checked}}checked{{/if}}">
    <button class="delete">&times;</button>
    <input type="checkbox" checked="{{checked}}" class="toggle-checked" />
    <span class="text">{{text}}</span>
  </li>
</template>

基本は普通のHTMLですが、
動的に表示を変更する箇所のみ、Meteorの記載になっています。


下記は、ループ制御文です。
サーバ側で設定した、tasksコレクションを回して、taskテンプレート形式で出力します。

 {{#each tasks}}
   {{> task}}
 {{/each}}


下記が、taskテンプレートの定義です。
{}で囲まれている部分で、条件式判定や、サーバ側で設定した値を出力しています。

<template name="task">
  <li class="{{#if checked}}checked{{/if}}">
    <button class="delete">&times;</button>
    <input type="checkbox" checked="{{checked}}" class="toggle-checked" />
    <span class="text">{{text}}</span>
  </li>
</template>


■simple-todos.js

Tasks = new Mongo.Collection( "tasks" );

// サーバ側のコード
if( Meteor.isServer ) {

}

// クライアント側のコード
if( Meteor.isClient ){

  //--------------------------------------
  // <body>内の自動設定
  Template.body.helpers({

    tasks: function() {

       // 検索条件をセッションから取得
      var query = Session.get( "taskSearchQuery" );

       // ログ出力(ブラウザのコンソールに出力)
      console.log( "query = " + query );
    
       // 検索条件が指定されている場合
      if( query ) {

         // タスク一覧の取得(部分一致検索)
        var search = new RegExp( query, "i" );
	 return Tasks.find( { text: search }, { sort: { createdAt: -1 } } );
       }
       // 検索条件が指定されていない場合
      else {

         // タスク一覧の取得(登録順)
        return Tasks.find( {}, { sort: { createdAt: -1 } } );
       }
    }

  });

  //--------------------------------------
  // <body>内のイベント
  Template.body.events({

     // 検索条件テキストボックスのキーイベント
    "keyup [name=taskSearch]": function( event, template ) {

       // 検索条件をセッションに格納
      Session.set( "taskSearchQuery" , event.target.value );
     },

     // Form送信(新規タスクの入力)
    "submit .new-task": function( event ) {

      // Form処理時の画面リフレッシュ防止
      event.preventDefault();
 
       // 入力値の取得
      var text = event.target.text.value;
 
       // タスクの追加
      Tasks.insert({
        text: text,
        createdAt: new Date() // 現在時刻
       });
 
       // 入力エリアをクリア
      event.target.text.value = "";
    }

  });

  //--------------------------------------
  // <template name="task">内のイベント
  Template.task.events({

    // 完了チェックボックスのクリックイベント
   "click .toggle-checked": function() {

       // タスクの更新
      Tasks.update( this._id, { $set: { checked: ! this.checked } } );
    },

    // 削除ボタンのクリックイベント
   "click .delete": function() {

       // タスクの削除
      Tasks.remove( this._id );
    }

  });
}


サーバ側のコードとクライアント側のコードを分けて書くことができます。
今回は、クライアント側のコードのみで実装できました。

// サーバ側のコード
if( Meteor.isServer ) {}

// クライアント側のコード
if( Meteor.isClient ){}


各イベントを定義して、その中で、ロジックを記述していきます。
DB操作も含めて、全てJavaScriptでロジックを記述する事ができます。

また、データの自動同期等は、Meteor内部で行われるので、コード上で意識する必要はありません。
数行で実装できてしまうので、とても簡潔なコードになります。


また、DBは、MongoDBなので、CreateTable文を発行してスキーマを作成する必要はありません。


Meteorの作法を覚える必要がありますが、
JavaJavaScriptが書ける人なら、敷居は高くないと思いました。


■simple-todos.css

/* CSS declarations go here */
body {
  font-family: sans-serif;
  background-color: #315481;
  background-image: linear-gradient(to bottom, #315481, #918e82 100%);
  background-attachment: fixed;

  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;

  padding: 0;
  margin: 0;

  font-size: 14px;
}

.container {
  max-width: 600px;
  margin: 0 auto;
  min-height: 100%;
  background: white;
}

header {
  background: #d2edf4;
  background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);
  padding: 20px 15px 15px 15px;
  position: relative;
}

#login-buttons {
  display: block;
}

h1 {
  font-size: 1.5em;
  margin: 0;
  margin-bottom: 10px;
  display: inline-block;
  margin-right: 1em;
}

form {
  margin-top: 10px;
  margin-bottom: -10px;
  position: relative;
}

.new-task input {
  box-sizing: border-box;
  padding: 10px 0;
  background: transparent;
  border: none;
  width: 100%;
  padding-right: 80px;
  font-size: 1em;
}

.new-task input:focus{
  outline: 0;
}

ul {
  margin: 0;
  padding: 0;
  background: white;
}

.delete {
  float: right;
  font-weight: bold;
  background: none;
  font-size: 1em;
  border: none;
  position: relative;
}

li {
  position: relative;
  list-style: none;
  padding: 15px;
  border-bottom: #eee solid 1px;
}

li .text {
  margin-left: 10px;
}

li.checked {
  color: #888;
}

li.checked .text {
  text-decoration: line-through;
}

li.private {
  background: #eee;
  border-color: #ddd;
}

header .hide-completed {
  float: right;
}

.toggle-private {
  margin-left: 5px;
}

@media (max-width: 600px) {
  li {
    padding: 12px 15px;
  }

  .search {
    width: 150px;
    clear: both;
  }

  .new-task input {
    padding-bottom: 5px;
  }
}

公式サイトのTutorialsのままです。
普通のCSSです。



では、アプリを実行してみます。

cd simple-todos
meteor


http://localhost:3000に、ブラウザでアクセスしてみます。


Todoの登録、更新、削除ができます。
また、リアルタイムに絞込み検索ができます。


ブラウザを2つ並べて、片方でTodoを新規登録すると、
もう片方のブラウザにも、追加したTodoが表示されます。
ブラウザのリロード無しで、リアルタイムに同期する処理が組み込まれています。


下記コマンドで、クラウド環境へデプロイします。※「hogehogehoge-simple-todos」の部分は、任意のサイト名を指定します

meteor deploy hogehogehoge-simple-todos.meteor.com


http://hogehogehoge-simple-todos.meteor.comに、ブラウザでアクセスして、動かすことができます。

③Meteorに触ってみる(モバイルアプリへの変換)

作成したMeteorアプリをモバイルアプリに変換してみます。


下記コマンドで、iOSAndroid用のモバイルアプリに変換できます。

・iOS用
meteor install-sdk ios
meteor add-platform ios
meteor run ios

・Android用
meteor install-sdk android
meteor add-platform android
meteor run android


iOSへの変換は、Mac上でのみ実行可能です。
今回は、Ubuntu環境なので、Android用に変換してみます。


Android用のコマンドを実行すると、Androidエミュレータが起動します。


てっきり、Android用アプリに変換されるのかと思ったら違うみたい・・・
やり方間違ってるかもですが。


とりあえず、Androidエミュレータ上でブラウザを起動して、hogehogehoge.meteor.comにアクセスしてみます。
問題なく表示されますね。