※ この記事は公開されてから1年以上経過しているため、情報が古い可能性があります。
久々の更新です(*・ω・)ノ
さて、今回はタイトルの通りです(`・ω・´)
家のラズパイは各種監視やエアコン等の制御用にSpringbootで作られたAPIが走っています。
なのでスマホから色々操作できるようになっています♪
この話はまたいつか…
で、こいつの接続先をローカルDBではなく、別のサーバー(このブログの動いてるサーバー)のDBに接続するようにします!
経緯としては結構前になってしまうのですが、実はラズパイのSDカードがアボ〜ンしました(T_T)
サーバー自体の再構築はAnsibleさんがいらっしゃったのでそこまで大変でもなかったのですが、DBのデータはもう帰ってこない…
せめてDBはレン鯖にしとくべきだったと反省しております_| ̄|○
ということでその作業の一角、SpringbootのDB接続はSSHポートフォワーディングを使ってリモートのMySQLに接続するようにする手順の備忘録的なものです。
作業の手順としては
- JSchを導入
- コネクション用のクラスを作成
- リスナーに登録処理追加
- ポートフォワーディングに合わせてSQLの接続先を修正
- テストの修正
となります。
では行ってみましょう!!
JSchを導入
まぁこれは大した内容ではないですね。
gradleに追加します。
1
2
3
4
5
6
7
8
diff --git a/build.gradle b/build.gradle
index 163dc68..e8e1c89 100644
--- a/build.gradle
+++ b/build.gradle
@@ -49,6 +49,7 @@ dependencies {
compile('org.projectlombok:lombok')
+ compile('com.jcraft:jsch:0.1.54')
}
コネクション用のクラスを作成
JSchを使ってポートフォワーディングを行います。
rsaKeyPathとknownHostsPathを引数にしているのはテストや本番用です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
public class SSHConnection {
private static final String S_PASS_PHRASE = "公開鍵のパスフレーズ";
private static final int LOCAl_PORT = 3307;
private static final int REMOTE_PORT = 3306;
private static final int SSH_REMOTE_PORT = 10022;
private static final String SSH_USER = "SSH接続ユーザー";
private static final String SSH_REMOTE_SERVER = "SSH接続先サーバ";
private static final String MYSQL_REMOTE_SERVER = "MySQLのサーバ";
private Session session;
public void closeSSH() {
session.disconnect();
}
public SSHConnection(String rsaKeyPath, String knownHostsPath) throws JSchException {
JSch jsch = new JSch();
jsch.setKnownHosts(knownHostsPath);
jsch.addIdentity(rsaKeyPath, S_PASS_PHRASE.getBytes());
session = jsch.getSession(SSH_USER, SSH_REMOTE_SERVER, SSH_REMOTE_PORT);
session.connect();
session.setPortForwardingL(LOCAl_PORT, MYSQL_REMOTE_SERVER, REMOTE_PORT);
}
}
リスナーに登録処理追加
ServletContextListenerを継承したクラスを作ります。
これで登録することで、起動と同時にポートフォワーディング処理が走ります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import com.jcraft.jsch.JSchException;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.util.logging.Level;
@Log
@Component
@WebListener
@RequiredArgsConstructor
public class ContextListener implements ServletContextListener {
@NonNull
private final AppParams appParams;
private SSHConnection sshConnection;
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("Context Initialized");
try {
sshConnection = new SSHConnection(appParams.getRsaKeyPath(), appParams.getKnownHostsPath());
} catch (JSchException e) {
log.log(Level.WARNING, "SSHConnection Error", e);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
if (sshConnection != null) {
sshConnection.closeSSH();
}
log.info("Context Destroyed");
}
}
ポートフォワーディングに合わせてSQLの接続先を修正
コネクション用のクラスで定義したポートに変更してやります。
1
2
3
4
5
6
7
8
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3307/scheme_name?useSSL=false
username: MySQLのユーザー名
password: MySQLのパスワード
driverClassName: com.mysql.jdbc.Driver
testonborrow: true
validationQuery: SELECT 1 FROM DUAL
テストの修正
実はこれが地味にハマりました。。。
テスト時もDB接続テストが発生するため、リスナーに登録したのと同様に実行前に接続し、実行後に破棄してやる必要があったのです。
方法は色々あるかと思いますが、今回はClassRuleアノテーションを使い、TestRuleを実装したクラスを呼び出す方法をとりました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class ConnectionTestRule implements TestRule {
private static final String ID_RSA_DIR = "公開鍵のファイルパス";
private static final String KNOWN_HOSTS_DIR = "known_hostsのファイルパス";
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
SSHConnection sshConnection = new SSHConnection(ID_RSA_DIR, KNOWN_HOSTS_DIR);
base.evaluate();
sshConnection.closeSSH();
}
};
}
}
上記のクラスを以下のように設定してあげます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class BvlionBatchApplicationTests {
@ClassRule
public static ConnectionTestRule rule = new ConnectionTestRule();
@Test
public void contextLoads() {
}
}
これで無事にgradle buildも通り、DBにも繋がるようになりました!
今まで何度か自宅鯖をアボ〜ンさせた経験はあったのですが、ここ最近はずっとPaaS系を使っていたためすっかりローカルDBが信頼ならないことを失念していました(>_<)
いつかFirebaseにしたい…