Works by

Ren's blog

プログラミング等IT技術関連でメモする

【Cloud Foundly】Cloud Foundlyアプリケーションのインスタンスを明示的に指定してHTTPリクエストを送る

f:id:rennnosukesann:20190205125549p:plain:w300

Cloud Foundlyインスタンスを複数立ち上げたときに、特定のインスタンスにアクセスするやり方のメモです。

X-CF-APP-INSTANCE ヘッダーでインスタンスを指定

Cloud Foundly上にデプロイしたアプリケーションにアクセスするとき、 X-CF-APP-INSTANCE ヘッダーをつけることでアクセスするインスタンスを指定することができます。

$ curl --request GET --url http://localhost:8080/api/v1/hoges/1 
  --header 'X-CF-APP-INSTANCE: 6aa4cf0f-1dd4-29b6-af62-cc21bc23df10:1'

X-CF-APP-INSTANCE の値は {アプリのGUID}:{インスタンスインデックス} のフォーマットで指定します。上記例では、インデックスの採番順序で2番目(インデックスは0開始)のアプリケーションインスタンスへアクセスしています。

ちなみに、アプリのGUIDは cf app {アプリ名} --guid で取得できます。

$ cf app hoge-app --guid
6aa4cf0f-1dd4-29b6-af62-cc21bc23df10

また、インスタンスのインデックスはインスタンス環境変数 $CF_INSTANCE_INDEX で取得可能です。

参考文献

docs.cloudfoundry.org

docs.cloudfoundry.org

【Spring】Spring Sessionでセッション情報をDB2上に保存する

f:id:rennnosukesann:20181220183020p:plain

Spring でのセッション

SpringではJava ServletのHttpSessionの仕組みを利用することができます。ServletのHttpSessionはSpringフレームワークの上に構築されたアプリケーション上でそのまま利用できるほか、Springの各種ライブラリによってラッピングされた形でも利用することができます。

アプリケーションサーバを複数台構成とした場合のセッション

特に設定を行わなかった場合、Servletのセッションはメモリ上で管理されます。もし複数台のサーバや複数のクラウドインスタンスにアプリケーションがデプロイされていると、各インスタンス間で個別のメモリ領域を持つことになり、互いにセッションの情報が共有されません。そのため、例えばあるインスタンス上アプリケーションでログインに成功したユーザが、別のインスタンス上アプリにアクセスすると未ログイン状態として扱われてしまう・・・といったことが起こります。

このような問題に対する解決策はいくつかあるのですが、今回はDB上にセッション情報を保存する方法を紹介します。

適用方法

今回はDBMSとしてDB2を使用します。 またSpringアプリケーションのビルドツールとしてGradleを利用し、 すでにDBMSへの接続は完了しているものとします。

Spring Sessionの導入

セッションの管理をDB上で行えるようにします。 build.gradle に下記パッケージを追加します。

dependencies {
    ....
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation("org.springframework.session:spring-session-core")
    implementation("org.springframework.session:spring-session-jdbc")
    ....
}

Configuration Beanクラスの定義

@EnableJdbcHttpSession アノテーションを付加した Config クラスを定義します。これにより、JDBC経由でDB上でセッションを管理できるようになります。

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.transaction.PlatformTransactionManager;

/**
 * 
 * <p>
 * セッション情報をJDBC経由でDB上で管理するためのConfiguration
 * </p>
 * 
 */
@EnableJdbcHttpSession
public class JdbcHttpSessionConfig {
  @Bean
  public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }
}

Spring設定ファイルの変更

application.properties に下記項目を追加します。

# Session Setting in DB
server.session.jdbc.initialize-schema=always
server.session.jdbc.table-name=SPRING_SESSION

application.yml の場合は以下。

server:
    sesion:
        jdbc:
            initialize-schema=always
            table-name=SPRING_SESSION

テーブルの追加

設定は上記で完了なのですが、このままではセッションを保存できません。 既存のDBにセッション情報を追加するテーブルを定義する必要があります。

Spring SessionのGitHubリポジトリDBMS別のテーブルCREATE用SQLがおいてあるので、こちらを活用しましょう。

ex. DB2の場合(少し編集してあります):

CREATE TABLE SPRING_SESSION (
    PRIMARY_ID CHAR(36) NOT NULL,
    SESSION_ID CHAR(36) NOT NULL,
    CREATION_TIME BIGINT NOT NULL,
    LAST_ACCESS_TIME BIGINT NOT NULL,
    MAX_INACTIVE_INTERVAL INT NOT NULL,
    EXPIRY_TIME BIGINT NOT NULL,
    PRINCIPAL_NAME VARCHAR(100),
    CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
)
ORGANIZE BY ROW;

CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);

CREATE TABLE SPRING_SESSION_ATTRIBUTES (
    SESSION_PRIMARY_ID CHAR(36) NOT NULL,
    ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
    ATTRIBUTE_BYTES BLOB NOT NULL,
    CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
    CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
)
ORGANIZE BY ROW;

DB2は列指向*1を定義する機能を持ち、DB2を利用するクラウドサービスではこの機能がデフォルトになっていたりします(IBM Cloud dashDBなど)。この方針のままだと、BLOB 型定義をサポートしていない仕様のため*2、上記SQLでは元のSQLに明示的に ORGANIZE BY ROW を指定し、メジャーなRDBMS同様の行指向操作がされるようにしています。

これで、DB上にセッションが保持されるはずです!

f:id:rennnosukesann:20190204174125p:plain

参考文献

qiita.com

qiita.com

*3 ja.wikipedia.org

*1:列指向の概念をよくわかっていない...
曰く、通常のRDBMSで行単位での操作をするように、列単位での操作を主とするらしい

*2:https://www-01.ibm.com/support/docview.wss?uid=swg22003176

*3:完全にDBMS=RDBMSの文脈前提で語っている...

【Grafana】Grafanaでアラート時通知を設定する

f:id:rennnosukesann:20190129142110p:plain

Grafanaで可視化対象になっている監視サーバが障害を検知したら、Grafana側でメールやWebhookなどの通知を行うための設定メモです。

Grafanaでメール通知を設定する

ダッシュボード左上のGrafanaアイコンをクリックし、プルダウンから「Alerting」->「Notification channels」をクリック。 f:id:rennnosukesann:20190129110135p:plain

「+New Channel」をクリック。

f:id:rennnosukesann:20190129110402p:plain

「Name」に設定のタイトルを任意に入力。
「Description」には設定内容の概要を任意に入力。
「Type」はEmailを選択。
「Email ID」に送信したいメールアドレスを入力してください。

f:id:rennnosukesann:20190129111342p:plain

「Send Test」を押すと試しに「Email ID」に指定したメールアドレス宛にメールを送ることができます。

メールが受け取れることが確認できたら「Save」をクリック。簡単。

監視対象メトリクスと通知設定を紐付ける

メールの設定が完了したら、今度はメール送信のトリガーとなるアラートを設定します。

アラートを設定したいメトリクスのグラフを選択肢、タイトルをクリックします。クリックして表示されるEditボタンをクリックします。

f:id:rennnosukesann:20190129164016p:plain

出現するGraph設定用ボードの「Alert」タブを開き、「Create Alert」をクリック。

f:id:rennnosukesann:20190129164057p:plain

「Alert Config」でアラートのタイトル・しきい値を設定します。
「Conditions」ではアラート条件を指定します。「WHEN」にはグラフ中のどのタイミングでの値を参照するか決定します。デフォルトは last() で、グラフの最右端の値を参照します。「or」句で他の条件を指定しています。

「IS BELOW」の部分ではメトリクス上で参照する値のしきい値を設定しています。「IS BELOW」であれば参照値がしきい値を下回ったときに、「IS ABOVE」であれば参照値がしきい値を上回った場合にアラートを行います。

f:id:rennnosukesann:20190129164132p:plain

下のプルダウンでは、値が未定・nullのとき/実行時エラー・タイムアウトのときにアラートを行うかどうかを設定できます。

これで設定完了です。

参考文献

Grafana Conditionの設定

docs.grafana.org

【Spring Security】セッションのデフォルト有効期限は30分しかない

f:id:rennnosukesann:20181220183020p:plain

というかServletの仕様ですね。

Spring Securityにおけるセッションの有効期限

Spring Securityでは、セッション情報をJava Servletで扱うようなHttpSessionとして扱えます。またセッションに紐づく情報は、クッキーであるHttpCookieオブジェクトとして扱えます。

    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    HttpServletRequest request = attr.getRequest();
    Session session = request.getSession();

    HttpServletResponse response = attr.getResponse();
    Cookie cookie = new Cookie();
    cookie.setPath("/");
    response.addCookie(cookie);

ただしこのセッション、デフォルトでは有効期限が30分となっているため、より長い期限を設けたい場合は明示的に有効期限を定義する必要があります。

簡単な有効期限の設定方法として、 application.properties(yml) に記述する方法があります。

server.session.timeout=3600 // 秒数で指定(負の数で無限)

また別の方法として、 HttpSession に直接指定する方法もあります。

    // セッションタイムアウト時間を秒数で指定
    //(負の数で無限)
    session.setMaxInactiveInterval(3600);

各クッキーの有効期限を設定する

セッションそのもの(セッションID)の有効期限ではなく、セッションに紐づく個々のクッキーの有効期限にも気をつけるべきです。

こちらも同様に application.properties(yml) に記述できます。

server.session.cookie.max-age=3600

一回ハマったはずなのに、完全に忘れてた。。。

ブログタイトルとデザインを更新しました

突然ですが、本ブログのタイトルと、ブログ全体のデザインを更新させていただきました。

f:id:rennnosukesann:20190113185422p:plain

今までははてなブログで提供されていたデフォルトのブログテーマを使わせていただきましたが、そろそろデザインを変えたいと思い、またどうせ変えるなら自分でカスタムしたいという思いもあり、ブログテーマを一から作ってみることにしました。

ブログデザインを作成するにあたり、いくつか知見があったので共有します。

HTMLをすべて自由記述できるわけではない

はてなブログでは、既存のHTMLの編集はほぼできないようになっています。例えば個人で一からHTMLのテンプレートを作成し、それをアップロードする・・・といった機能はついていません。

これは、既存のHTMLテンプレートを個々にいじられてしまうと、はてなブログとして運用していくのに必要なタグや要素が削除されてしまい、思わぬレイアウトの崩れ等の問題が発生してしまうためかと思われます。

そのため新しくブログデザインを作成する場合には、すでにあるブログテンプレートに対してスタイリングを行うCSSをメインに作っていくことになります。

作成したCSSは、ブログ管理画面のサイドメニュー「デザイン」->「カスタマイズ(レンチのアイコン)」タブ->「デザインCSS」のテキストボックスに貼り付ければ適用できます。

f:id:rennnosukesann:20190113184214p:plain

f:id:rennnosukesann:20190113184233p:plain

とはいえHTMLを全く編集することができないわけではなく、一部ヘッダーなどのコンポーネントの直下などに追加のHTMLを記述することはできます。

HTMLを追加記述可能な箇所は、

  • タイトル下(ヘッダ)
  • 記事上(記事)
  • 記事下(記事)
  • フッタ

の4箇所になります。

編集箇所は先ほどと同じブログ管理画面の「デザイン」メニューになります。例えばヘッダ直下にHTMLを新しく追加したい場合には、下記のように「HTMLを記述できます」と書かれたテキストボックスにHTMLを記述します。

f:id:rennnosukesann:20190113185625p:plain

はてな公式がボイラープレートを用意してくれている

はてなブログのデザインテーマを作成するにはCSSを貼り付ければ良い、ということはわかりました。が、CSSをちまちまと書いていきながら、編集したCSSを管理画面に貼り付けて、逐一チェックする・・・というのはちょっと骨が折れます。

そこで、はてなブログ公式が用意してくれているボイラープレートを拝借します。

github.com

このボイラープレートではSCSSによるスタイルシート作成環境を提供してくれています。ボイラープレートをクローン後、 npm で必要なパッケージをインストールしてサーバを立ち上げるだけで、SCSS編集時に自動でCSSを生成してくれます。立ち上げたローカルサーバへのパスをデザインCSSとしてブログデザインに適用すれば、SCSSをビルドするたびにプレビュー画面に最新の編集状況を表示できます。

詳細ははてなブログ公式ヘルプブログで紹介されています。ボイラープレートの導入手順を紹介している他、編集の手引きまでしてくれています。

help.hatenablog.com

レスポンシブデザインも対応可能(今はしてないけど。。。)

はてなブログのデザインテーマにはレスポンシブデザインの有無が設定できます。

ブログ管理画面のサイドメニュー「デザイン」->「スマートフォンスマホのアイコン)」タブ->「詳細設定」の項目で「レスポンシブデザイン」のチェックを入れます。

f:id:rennnosukesann:20190113191931p:plain

次に、ブログ管理画面のサイドメニュー「デザイン」->「カスタマイズ(レンチのアイコン)」タブ->「デザインCSS」の先頭に下記コメントを追加します。

/* Responsive: yes */

これで、現在のブログがスマホ上のブラウザであってもPCブラウザと同じスタイルで表示されるようになります。

レスポンシブデザインを考慮していないサイトの場合、PCブラウザと同じ表示の仕方でスマホブラウザ上にブラウジングされてしまうため、レスポンシブ対応していないデザインの場合はOFFにすることをおすすめします(僕も現在はそうしています)。


僕がブログデザイン作成で得た治験はざっとこんな感じです。

ブログデザイン作るのは楽しいですね。

まだレスポンシブデザイン未対応なので、今度はレスポンシブ対応も頑張ってみようかと思います。

【Docker】ローカル環境上のイメージ/コンテナを全て削除する

f:id:rennnosukesann:20190108224132p:plain

ローカルにあるDockerイメージ/コンテナを全て削除するコマンド。メモ。

# Dockerイメージの全削除
$ docker rmi $(docker images -aq)

# Dockerコンテナの全削除
$ docker rm $(docker ps -aq)

DockerHubにあるイメージをいろいろ試していると、どうしてもローカルのイメージやコンテナがいっぱいいっぱいになってしまいがち。 特にコンテナは一旦きれいにしたい場合が多いので、重宝します。

【Kotlin/Spring】Kotlin版SpringBoot開発環境を構築する

f:id:rennnosukesann:20190106130012p:plain:w300    f:id:rennnosukesann:20181220183020p:plain:w300

Kotlin版Spring Boot開発環境構築のメモです。

検証環境はMac OS X Mojave 10.14.2 になります。

IntelliJ IDEのインストール

今回はIDEとしてIntelliJを使用します。 IntelliJはKotlinの開発元であるJetbrain社が開発していることもあって、Kotlinのサポートはバッチリです。

下記リンクより遷移後、DownloadボタンからIntelliJをダウンロードできます。

www.jetbrains.com

プロジェクトの作成

次に、Spring InitializerからSpringプロジェクトの雛形を取得します。

取得するプロジェクトに関して、いくつかオプションを選択していきます。

f:id:rennnosukesann:20190109194245p:plain

まずビルドツールとして Maven Gradle のうち何れかを選択します。今回は Gradle のプロジェクトを作成していきます。

f:id:rennnosukesann:20190109194422p:plain

次に言語選定です。主要なJVM言語 Java Gloovy Kotlin から選択できます。 今回はKotlinで書くので「Kotlin」 を選択。

f:id:rennnosukesann:20190109194503p:plain

次にSpringのバージョンを選択します。今回は安定版の 2.1.1 を選択します。

f:id:rennnosukesann:20190109194630p:plain

プロジェクトのメタ情報について設定します。

GroupはGradleのGroupIDとなり、プロジェクトを一意に識別するためのものです。プロジェクトのソースファイルを含めるパッケージと同一にすることが慣習となっています。

ArtifactIDはいわゆるプロジェクト名です。

メタ情報に関する記述は下記参照。

maven.apache.org

f:id:rennnosukesann:20190109194810p:plain

最後にSpringに追加したいライブラリを入力・選択します。

今回はWeb(Webページ生成/APIインタフェース等のWeb関連機能)、JPA(永続化機能)、Security(認証機能)を選択しました。

f:id:rennnosukesann:20190109195844p:plain

最後に「Cenerate Project」ボタンを押して、zipファイルをダウンロードしてください。

f:id:rennnosukesann:20190109195925p:plain

プロジェクトを開く

次は、ダウンロードしたプロジェクトをIntelliJで開きます。

ダウンロードした圧縮済みプロジェクトを解凍し、解凍後のプロジェクトを任意のディレクトリに配置してください。 その後、ダウンロード(+インストール)したIntelliJを起動し、起動後ダイアログ中の「Open」をクリックしてください。

f:id:rennnosukesann:20190109201202p:plain

するとディレクトリ選択のためのファイルダイアログが開かれます。 ここでIntelliJで開きたいプロジェクトを選択。

f:id:rennnosukesann:20190109201327p:plain

「OK」をクリック。

f:id:rennnosukesann:20190109201523p:plain

これでプロジェクトがIntelliJ上に読み込まれました。

f:id:rennnosukesann:20190109202133p:plain

あとは開発するのみ!

ローカルでSpringアプリケーションを起動

実際にアプリケーションをローカルで起動してみます。

起動の前に、 build.gradle を少し編集します。 Spring Data JPAライブラリを読み込むとDBの設定が必要になるため、 今回は一旦外しておきます。

f:id:rennnosukesann:20190109234614p:plain

「View」->「Tool Windows」->「Gradle」 を選択。

f:id:rennnosukesann:20190109234745p:plain

「(プロジェクト名)」-> 「Tasks」->「application」->「bootRun」を右クリックし、「Run '(プロジェクト名)[bootRun]'」 を選択。

f:id:rennnosukesann:20190109234812p:plain

するとビルド・アプリケーションの起動が始まります。

23:39:41: Executing task 'bootRun'...

:compileKotlin
w: ... ated
:compileJava NO-SOURCE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.1.RELEASE)

...

2019-01-09 23:40:22.533  INFO 31168 --- [           main] c.r.n.rpg.rpgkit.RpgKitApplicationKt     : Started RpgKitApplicationKt in 18.3 seconds (JVM running for 19.039)

アプリケーションの起動はこれで成功です!

参考文献

https://start.spring.io/

maven.apache.org