2019年3月27日水曜日
踏み台サーバー(EC2)経由でSQLServer(RDS)にSSH接続する方法(ポートフォワーディング)・Windows編
オフィス狛 技術部のHammarです。
最近はAWSで環境を作って開発する機会が多いですが、ここでセキュリティ的な部分や運用的な観点としてよく踏み台サーバー経由で目的のサーバーに接続するということがあると思います。
今回はその「踏み台サーバー(EC2)経由でSQLServer(RDS)に接続する」という目的で、接続したSQLServerをSQL Server Management Studio (SSMS) で操作したいと思います。
■利用環境
Windows(ローカルPC)AWS ES2(踏み台)
AWS RDS(SQL Server)
■利用ツール
SQL Server Management Studioteraterm
今回の目的を達成するには、「SSHポートフォワーディング」というテクニックを利用します。
ポートフォワーディングとは、SSHによって確立した通信経路を利用して、クライアントのポートを、クライアントが直接アクセスできないサーバのポートに転送してくれる仕組みを指します。
これを利用して今回の手順は簡単に書くと下記のようになります。
②踏み台サーバーからSQL Serverにポート転送(ポートフォワーディング)
③ポートフォワーディングした状態でSQLServerにログインしDB操作
では上記手順を踏まえて、具体的な手順を書いていきます。
1.teratermでポートフォワード設定する
よくSSH接続につかわれるツールで有名なteratermには、デフォルトでポートフォワード設定機能がついていて、これを利用します。①teratermを起動し、メニューバーの「接続」→「SSH転送」を選択
②ポート転送画面で「追加」ボタンを押下
③ポート転送を行う向きの選択で「ローカルのポート」にチェックを入れ下記のように設定します
リモート側ホスト:接続するRDSのエンドポイント
ポート:1143
④OKを選択する
2.teratermで踏み台にSSH接続する
次に踏み台にSSH接続するのですが、このとき1で設定したポートフォワード機能が有効になり、踏み台に接続したと同時に目的のサーバーにも接続されることになります。(トンネルを掘った状態にするともいいます)①teratermメニューバーの「ファイル」→「新しい接続」で下記のように設定します
TCPポート:22
サービス:SSHにチェック
②SSH認証画面でEC2へ接続するための認証情報を設定する
※基本ユーザー名と鍵設定で行けると思います
③OKを選択してSSH接続する
3.SSMSでログインする
1、2まででポートフォワーディングは完了しているので、この接続をつなげたままであとはツール(今回はSSMS)で直接SQLServerにつなげるだけです。接続設定は下記のように設定します。
認証:SQL Server認証
ログイン:ルート権限ユーザー名
パスワード:設定したパスワード
サーバー名は[127.0.0.1]のローカルホストを指定し、カンマでポート指定できますので、先ほど1で設定したローカルのポート(11433)をここに設定することで、 ローカルから踏み台サーバー経由、ポートフォワーディングでSQL Server接続が可能になります。
あとはSSMSで操作が可能なので、ツール内でデータベース作ったりテーブル作ったりが簡単にできます。
今回のポートフォワーディングを利用することで、もちろんSQL Server以外でも接続可能ですし、いろいろ応用できると思いますので、ぜひ参考にしてみてください。
2019年2月28日木曜日
SpringBoot 1.3 から 1.5 へのバージョンアップでハマったこと その2
オフィス狛 技術部のJoeです。
今回が初の投稿になります。どうぞよろしくお願いします。
以前、SpringBoot1.31から1.51にバージョンアップを記事にしましたが、その影響がさらに出てきました。
今回は、バージョンアップが原因で発生した事象を追加でご紹介します。
今回の事象が発生したのはファイルのアップロードです。 数MB程度のファイルをmultipart/form-data形式でアップロードしたところ、下記のエラーが出ました。
org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field [xxxxx] exceeds its maximum permitted size of 1048576 bytes.
「最大サイズの1MBを超えています」だそうです。
エラーに出ているファイルサイズ(1MB)はSpringbootのデフォルトのサイズです。
最大サイズはapplication.ymlに定義しているはずなので確認しました。
multipart:
maxFileSize: 100MB
maxRequestSize: 100MB
確かに最大100MBと定義しています。
では、何故1MBになってしまうのか・・・・・・
調べてみたところ、バージョンアップによりプロパティーのキー名が変更されていました。
【SpringBoot 1.5】での指定方法
spring:
http:
multipart:
max-file-size: 100MB
max-request-size: 100MB
デフォルトでは、上記の設定はapplication.ymlには存在しないので、デフォルトのサイズ(1MB)になっていたのですね。
とりあえず、上記のキー名に修正し、無事にアップロードすることができました。
ちなみに、今回のアップロードは1ファイルなので同じ値を設定していますが、
複数ファイルの場合、「max-request-size」はファイル数に応じて変更です。
・max-file-size :1ファイルの最大バイト数
・max-request-size:1リクエストで複数ファイルの最大バイト数
さらに調べてみると、SpringBoot 2.0では、下記の設定方法に変更されていました。
【SpringBoot 2.0】での指定方法
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
今回ご紹介した以外にも、バージョンアップでキー名が変更されているものがあるようなので、対応の際は調査が必要ですね。
2019年2月27日水曜日
Angular angular.json の scripts は記載する順番が大事、と言う話(bootstrap、popper)
オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。
Angular の angular.json には、外部のJavaScriptの読み込む為に、「scripts」と言う項目が存在しますが、そこに記載する際は、順番が大事だよ、と言う話です。
まあ、Angularに限った事じゃ無いのですが、毎回ハマるので、備忘録で記載します。
とある新規プロジェクトで、デザイナーが作ったhtmlをせっせとAngularプロジェクトに移植していたのですが、ポップアップ系の表示がエラーになってしまいました。
zone.js:192 Uncaught TypeError: Bootstrap dropdown require Popper.js (https://popper.js.org)
at c.t.toggle (bootstrap.min.js:6)
at HTMLAnchorElement. (bootstrap.min.js:6)
at Function.each (jquery.min.js:2)
at w.fn.init.each (jquery.min.js:2)
at w.fn.init.c._jQueryInterface [as dropdown] (bootstrap.min.js:6)
at HTMLAnchorElement. (bootstrap.min.js:6)
at HTMLDocument.dispatch (jquery.min.js:2)
at HTMLDocument.y.handle (jquery.min.js:2)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)
「Bootstrap dropdown require Popper.js」なので、
ああ、「bootstrap.min.js」が「Popper.js」を必要としているのね、と言う事で、まずはインストールしました。
npm install popper.js --save
その後、angular.jsonに「Popper.js」追加しました。
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js",
"node_modules/popper.js/dist/umd/popper.min.js"
]
既に、「bootstrap.min.js」は追加していあったので、その直後に追記しました。(これが良くなかった・・・・)
その後も一向にエラーが消えることがありません。
少し考えた後、「これ、もしかして順番かな?」と思い、
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/popper.js/dist/umd/popper.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
]
順番を入れ替えたらエラーも消えました。
いやー、どハマりする前に気付けて良かったです。
何事も読み込む順番には気を付けようね、と言う話でした。
あまり、Angular関係無かったですね。
次回はもっとAngularっぽい機能を書いていこうと思います。
それでは、良いAngularライフを!
2019年2月26日火曜日
node.jsでインストールしたモジュール(knex、mssql)のバージョンによる問題
オフィス狛 技術部のHammarです。
今回はnode.jsのpackage.jsonに記述したモジュールのインストールバージョンによってハマった事象を書こうと思います。
環境構築時にDBのテーブルを作成したり、テストデータを登録したりすると思いますが、今回そのテーブル作成(migrate)とテストデータ登録(seed)作業をnode.jsとknex.jsというSQL Query Builderのモジュールで実行させます。
また開発の都合上DBはSQL Serverを利用するので、SQLServerに接続するmssqlというモジュールも利用します。
■実装環境
・node.js(Express)・knex、mssqlモジュールを利用
■npmインストール
まずそれぞれのモジュールの最新バージョン(knex:0.16.3 、mssql:4.3.0)をインストールして使おうとしましたが、そもそもknexとmssqlの最新バージョン互換対応がしておらず、怒られます。Error: This knex version does not support any other mssql version except 4.1.0 (knex patches bug in its implementation)
このバージョン(knex:0.15.2 、mssql:4.1.0)が最終的に対応しているとのことで、バージョンを指定しインストールします。
■テーブル作成(migrate)、テストデータ登録(seed)
次にテーブル作成(migrate)とテストデータ登録(seed)の処理を作成します。テーブル作成(migrate)ではknexをつかったテーブルcreateを、テーブル毎のファイル別に作成し、その各ファイルを読み込んで実行するというような基本的な流れでテーブルを作成します。
データ登録(seed)も基本的には同じで、insert文が書かれた各ファイルを読み込む感じです。
このmigrate処理を下記のような感じで記述して実行したのですが
// テーブルcreateファイルの読み込み
const createTableA= require('./createTableA');
const createTableB= require('./createTableB');
const createTableC= require('./createTableC');
const migrationOrder = [
createTableA,
createTableB,
createTableC,
];
// 実行
exports.up = async knex => {
const results = migrationOrder.map(migration => migration.up(knex));
return Promise.all(results);
};
なぜか「Can't acquire connection for the request. There is another request in progress.」というエラーがSQLServerから返ってきてしまいテーブルが作成できませんでした。
記述的には特におかしなところはなさそうなので、なぜうまくいかないのかかなりハマりました。
いろいろ調べていくとmssqlのバージョン4系では単一のトランザクション内で複数のクエリ実行はエラーとなるバグ?のようらしくこのときpromise.allは機能しないようです。
https://github.com/tediousjs/node-mssql/issues/491
https://github.com/Vincit/objection.js/issues/671
■回避方法
1.mssql、knexのバージョンを下げる
mssqlのバージョンを単純に下げると今度はknexのバージョン互換の関係でつかえなくなるので、knex: 0.13.0
mssql: 3.3.0
が現在の記述でうまくいくバージョンの組み合わせとなるようです。
ただ、やはり最新ではないですが新しいバージョンで開発したほうが今後のためにも良いとはおもうので、できるだけバージョンは下げたくないなーという思いで、個人的にはこの回避方法はやめました。
2.for文を使う
GitHubにも対応策として書かれていた下記のように記述しました。exports.up = async knex => {
const results = [];
for (const migration of migrationOrder) {
results.push(await migration.up(knex));
}
return results;
};
promise.allをつかわずに上記のようにfor文でテーブル作成を行うとうまくいきました。
ただ自分の開発環境ではこれにも1つ問題点がありました。
ESlintをインストールしていて、for文の記述がこのESlintのチェックにひっかかってしまいました。
error iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations no-restricted-syntax
現在の開発環境ではgitのcommit時にESlintチェックしていて、もしチェックに引っかかる場合はcommitできないようにしているのでこのままではcommitできません。
うーんこれは困った。。。
このチェックだけESlintから外したりすることも可能ですが、それだと今後開発のときのチェックとしてはすべて外れてしまうしー、
ということで、一旦ここの記述だけをチェックしないようにする、というちょっと強引な感じでひとまず切り抜けることにしました。
記述はこんな感じです。
exports.up = async knex => {
/* eslint-disable */
const results = [];
for (const migration of migrationOrder) {
results.push(await migration.up(knex));
}
return results;
/* eslint-enable */
};
れでこの記述だけはチェック除外となるので、ひとまずは切り抜けられました。
データ登録(seed)も同じようなながれで記述することで回避できます。
結局やってみるとちょっと強引な形なので、回避方法1にしてもいい気もしますが、ご参考までに。
ということで、まだ他にもこのようなモジュールのバージョンでなにか事象があるかと思いますが、ハマり次第記載していけたらと思います。
2019年2月22日金曜日
StackViewの優先度 Content Hugging PriorityとContent Compression Resistance Priority
こんにちは、オフィス狛 モバイル開発担当 Aika-yuy です。
今回の投稿は、 StackViewにつけるAutoLayoutの優先度について書いてみたいと思います。
サイズが曖昧だと怒られる
動的なサイズ変更や、デバイスサイズにも柔軟に対応してくれるStackViewですが、サイズが曖昧だと怒られることがあります。固定サイズを指定するという方法もありますが、iPad対応で文字のサイズを動的に変更したい場合は、あらかじめ優先度を設定しましょう。
まず、StackViewに2つのLabelを配置し、StackViewのサイド20、Y軸の中心になるように制約をつけます。そうすると、こんなエラーが出ます。
Add missing constraints for "Stack View"? This will add enough constraints to resolve the ambiguity
こまちゃんのLabelの優先度を低くするようにアドバイスしてくれています。
優先度を指定する
優先度を変更するときはAutoLayoutで設定できます。
優先度は、高くしたい方のpriorityを高くするのではなく、低くしたい方のpriorityを低くするようです。
いろんな解釈の仕方があると思いますが、私が覚えやすい方法使い分けをで書いてみました。
・Content Hugging Priority・・・・・・・・・・ 子Viewが親Viewより小さい時
・Content Compression Resistance Priority・・・ 子Viewが親Viewより大きい時
子Viewが小さい時
2つのLabelどちらともの文章が短い時、どちらのサイズを大きくしてViewいっぱいに表示するかを指定しなければいけません。
その場合は、優先順位が低い方の(固有のサイズを保持しなくてもいい方、大きくなってもいい方)
Content Hugging Priorityを1小さく設定します。
こまちゃんのLabelのContent Hugging Priorityを下げることにより、長くなってしまった場合省略表示され、nameのラベルを親Viewから押し出すこともなくなります。
子Viewのどちらかが大きい時
2つのLabel合わせたサイズが親Viewよりも大きくなってしまう場合、どちらを優先して表示するか指定します。
省略して表示してもいい方の優先度を1小さくします。
子Viewどちらとも親Viewより大きい時(どちらかを優先すると、どちらかが消えてしまう場合)
この場合は、設定次第ですがどちらも表示したい場合は親のStackViewの比率から固定サイズで指定した方がいいかもしれません。
AutoLayoutマスターになるべく、少しづつブログも更新していけるよう頑張ります。
2019年1月31日木曜日
gitマージツールにWinmergeを使う方法。
オフィス狛 技術部のHammarです。開発するとき、特に複数人で開発をされる場合は、gitを使われている方が多いと思います。その際に皆さんはマージツールやdiffツールに何を使っていますでしょうか?
私は開発はwindowsを使うことがほとんどなので、WinMergeというマージツールをよく使っています。なので、これをgitのマージツールとして設定していますが、今回はこのgitのマージツールの設定方法について書いてみたいと思います。
ちなみに自分のgitクライアントツールですが、自分はGitExtensionsを使っています。GitExtensionsはデフォルトでkdiffというマージツールが使えるようになっていますが、個人的には使いづらくて、マージツールの設定方法を変えられてうれしかった記憶があります。
設定はプロジェクトにある「.git」というディレクトリ内の「config」ファイルに下記の記述をそのまま追加して保存するだけでOKです。※.gitは隠しファイルとなっています
■config
[diff] tool = WinMerge [difftool "WinMerge"] path = C:/Program Files/WinMerge/WinMergeU.exe cmd = \"C:/Program Files/WinMerge/WinMergeU.exe\" -f \"*.*\" -e -u -r \"$LOCAL\" \"$REMOTE\" [merge] tool = WinMerge [mergetool "WinMerge"] path = C:/Program Files/WinMerge/WinMergeU.exe cmd = \"C:/Program Files/WinMerge/WinMergeU.exe\" \"$MERGED\"
ひとまず上記で最低限の設定となりますが、この記述はいろいろとカスタマイズ可能なので、自分用に見やすくしたり、3画面構成にしたりとかcmdの記述部分でいろいろとオプション設定も可能なので、もっと細かく設定してとことん使いやすくするのもありだと思います。
もちろんクライアントツールで設定を変えることも可能ですが、ツール毎に設定画面も違いますし、この方法はconfigファイルに記述するだけなのでGitExtensions以外のツールでも同じやり方で使えるようになります。
とりあえず細かな設定は置いといてサクッとgitでWinMergeを使いたいときにはぜひご活用ください。
Angular Templateでリストのフィルタを行う。
オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。
今年最初のブログ、何とか1月中に出来ました。(ギリギリ)
今年最初はAngularネタで行きたいと思います。
AngularのTemplate(html)でリストのデータを表示する際、
<ng-container *ngFor="let item of testList>
<div class="test_class">{{ item.name }}</div>
</ng-container>
こんな感じの書き方をすると思います。この時、「リスト内のデータをフィルタしてから表示したいんだよなー」と思う時がありませんか?
ありません?ないかな・・・・まあ、私はあります。
というわけで、こんな時は「Pipe」を使います。
以前、『AngularのPipeを使ってhtml(View)の表示を共通化する。』をブログに書きましたが、あの「Pipe」です。
では、いつものようにAngular CLIで作成します。コマンドは、
ng g pipe [path/name]です。今回は、「list-filter」という名前で作ります。
$ng g pipe shared/list-filter CREATE src/app/shared/list-filter.pipe.spec.ts (204 bytes) CREATE src/app/shared/list-filter.pipe.ts (209 bytes) UPDATE src/app/shared/shared.module.ts (711 bytes)
すると、以下のようなファイルが生成されます。
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'listFilter'
})
export class ListFilterPipe implements PipeTransform {
transform(value: string, args?: any): any {
return null;
}
}
これを以下のように変更します。
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filter'
})
export class ListFilterPipe implements PipeTransform {
transform(items: any[], col: string, value: string): any {
if (col.length === 0 || value.length === 0) {
return items;
}
return items.filter(x => x[col] === value);
}
}
Pipeの名前は分かりやすく「filter」にしています。そして、transformの引数を以下のように変更しました。
第1引数:items: any[] → 対象のリスト
第2引数:col: string → フィルタを行う対象の項目名
第3引数:value: string → フィルタする値
実際は色々作り込むのですが、とりあえず、今回は、
フィルタする項目・値が無かったら、リストをそのまま返すようにして、
if (col.length === 0 || value.length === 0) {
return items;
}
最後にリストのフィルタした結果を返しています。return items.filter(x => x[col] === value);では、実際にこのPipeを使ってみましょう。
リストの中身はこんな感じだとします。(分かりやすくする為に、json形式で書いています。)
{"hoge_list":[
{"id":"1","name":"テスト1"},
{"id":"2","name":"テスト2"},
{"id":"3","name":"テスト3"},
{"id":"4","name":"テスト4"},
{"id":"7","name":"テスト7"}
]}
実際にTemplateで使用する場合は以下のように記載します。
<ng-container *ngFor="let item of testList | filter:'id':'7'">
<div class="test_class">{{ item.name }}</div>
</ng-container>
@Pipe の name をvertical lineでリストの後ろにつけて、Pipeの後ろは、「:(colon)」を繋げてPipeへの引数を記載します。つまり、「testList | filter:'id':'7'」の部分で、リスト内の「id」という項目が「7」になっている値を絞り込んでいます。
出力結果は、
テスト7となります。
如何でしょうか。Pipe、色々な使い方がありますね。
それでは、良いAngularライフを!







