結論
Vector Asset Studioで追加する
方法
以下公式ドキュメント参考:
セッションベース認証について調べたことをまとめます。
セッションベース認証は、主にセッションIDを用いて行う認証方式です。 基本的なフローは以下です。 まずユーザが認証画面で認証情報を入力しwebサーバに情報を送ります。webサーバは認証情報をチェックし、正しければセッションIDというランダムな文字列を生成しユーザ情報と紐付けます。それをクライアント(ブラウザ)に返し、以降のやり取りにセッションIDを含めることでログイン状態を維持します。セッションIDのやり取りは主にCookieを使います。
Cookieとは、Webサーバがクライアント(ブラウザ等)に預けておく小さなファイルのことを指します。 ブラウザ・サーバ間でCookieをやり取りする際はHTTPのリクエストヘッダ(Cookieフィールド)・レスポンスヘッダ(Set-Cookie)に格納することでCookieのやりとりを行います。
具体的な動きを見てみます。
① ユーザはログイン画面で認証情報を入力しサーバに送ります。 ② 送られてきた認証情報が正しい場合、サーバはそのデータをレスポンスに含めます。 具体的には以下のようにレスポンスヘッダのSet-Cookieフィールドにデータを入れます。Set-Cookieは、ブラウザにデータをCookieとして保存してくれ、という命令です。
HTTP/2.0 200 OK Content-Type: text/html Set-Cookie: id=123 Set-Cookie: passwd=hogehoge
③ レスポンスを受け取ったブラウザはSet-Cookieフィールドからデータを取り出し、ブラウザのCookieに保存します。次回リクエストの時には以下のようにCookieをリクエストヘッダのCookieフィールドに含め送信します。
GET /index.html HTTP/2.0 Host: www.example.org Cookie: id=123; passwd=hogehoge
具体的にブラウザのCookieにデータを保存するにはJavaScriptのDocument.cookie
を使い以下のようにします。
document.cookie = "id=123"; document.cookie = "passwd=hogehoge";
参考:
Document.cookie - Web API | MDN
④ リクエストヘッダからCookieに含まれる認証情報をチェックし、正しい情報であればレスポンスする、などします。
Cookieの基本的な動きは以上です。
RFC2109によると、Cookieには以下の性質があります。
項目 | 説明 |
---|---|
保存形式 | テキスト |
cookieの全数量 | 少なくとも300個 |
ドメイン毎のcookieの数量 | 少なくとも20個 |
1つのcookieの容量 | 4096byte |
有効期限 | 設定可能(未設定の場合はブラウザ終了時に削除) |
異なるブラウザ間の共有 | 不可 |
有効範囲 | domainオプションを付けない限り、Cookieが有効なオリジンに限る |
有効範囲についてですが、設定しない限りCookieは異なるオリジン(scheme://hostname:port の組み合わせ、URL)には送信されないということです。
以上のことから、Cookieには以下のような問題点があります。
よって、重要データや容量の大きなデータをやり取りする場合はCookieは適していないことがわかります。
なので、ログイン状態の保持は認証情報を直接やり取りする方法ではなく、セッションIDを用いるセッションベース認証が使われることが多いです。
上記の特徴により認証情報そのものをCookieでやり取りするのは危険なので、そこで考え出されたのがセッションベース認証です。 セッションベース認証では、セッションIDと呼ばれる文字列を生成しそれをユーザ情報と紐付け、都度リクエスト時にセッションIDをCookieに含めることで認証状態を保持します。
このように、セッションIDをCookieでやり取りすることで、リクエストを受けたサーバはセッションIDに紐づくユーザ情報を探し存在していればそのユーザをログイン済みと見なします。
セッション情報はユーザからのリクエストのたびに参照されるため、高速に処理する必要があります。 ですので、セッション管理はRDBではなくRedisのようなインメモリデータベースで管理することが多いです。
このようなセッションを利用した認証をセッションベース認証と呼びます。
以下のような記述のDockerfileを用意しアプリケーションをコンテナ化しgcloud run deployでCloud Run上にデプロイしたところ、特定のファイル(今回はinit.incファイル)がアップされていないことに気が付きました。
FROM php:7.0-apache
# Configure PHP for Cloud Run.
# Precompile PHP code with opcache.
RUN docker-php-ext-install -j "$(nproc)" opcache pdo_mysql
RUN set -ex; \
{ \
echo "; Cloud Run enforces memory & timeouts"; \
echo "memory_limit = -1"; \
echo "max_execution_time = 0"; \
echo "; File upload at Cloud Run network limit"; \
echo "upload_max_filesize = 32M"; \
echo "post_max_size = 32M"; \
echo "; Configure Opcache for Containers"; \
echo "opcache.enable = On"; \
echo "opcache.validate_timestamps = Off"; \
echo "; Configure Opcache Memory (Application-specific)"; \
echo "opcache.memory_consumption = 32"; \
} > "$PHP_INI_DIR/conf.d/cloud-run.ini"
# Copy in custom code from the host machine.
WORKDIR /var/www/html
COPY . ./
# Use the PORT environment variable in Apache configuration files.
# https://cloud.google.com/run/docs/reference/container-contract#port
RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf
# Configure PHP for development.
# Switch to the production php.ini for production operations.
# RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# https://github.com/docker-library/docs/blob/master/php/README.md#configuration
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
COPY . ./ でコピーされてるはずだが、試しに以下のようにinit.incファイルを直接指定しCOPYすると、COPY failed: file not found in build context or excluded by .dockerignore: stat init.inc: file does not existというエラーが発生しました。
...
COPY . ./
COPY ./init.inc /
...
上記エラーが起きている原因は、.gitignoreでinit.incを除外しているためのようです。.gitignoreで除外しているファイルはGCP上にアップされないようで、そのためfile does not existとなっていたようです。
対策として、.gcloudignoreファイルをアプリケーションのルートディレクトリに作成し、.gitignoreを記載することで.gitignoreに記載したファイルもアップされるようになりました。また、GCP上にアップする必要のないファイル、ディレクトリも追記しました。
.gcloudignore
.gcloudignore
.git
.gitignore
参考
MySQLを用いたCRUDを行うSpringBootアプリケーションを、App Engineスタンダード環境にデプロイし、プライベートIPでCloud SQLへ接続を行う大枠の手順についてまとめます。
アプリケーション環境
GCP環境
今回やりたいことは、上記環境に基づいて作成したSpringBootアプリケーションをApp Engine上で動かし、Cloud SQLにプライベートIPでアクセスすることです。
App Engine上でデータベースを使う際は、App Engine内でミドルウェアとしてのデータベースを使うのは一般的ではなくGCPのストレージサービスを使用することになりますので、今回はデータベースとしてCloud SQLを使います。
この段階でデプロイしてもデータベースの設定がローカルのままなのでエラーになりますが、以降のステップでCloud SQLと連携すると動くようになります。
なお、#2でSpringBootの実行ファイル形式をjarからwarに変更します。
また、SpringBootアプリケーションのビルドやデプロイは以下のコマンドを使いました。
mvn clean install
mvn package appengine:deploy
ここまでの設定を行い、GAEにデプロイすると動くかと思います。今回はざっくりとした手順のみまとめましたが、今後詳しい設定手順をまとめようと思います。
Java8, SpringBootで作ったアプリをGoogle App EngineのJava8フレキシブル環境にデプロイしていたのですが、デプロイ先の環境をスタンダード環境に変更する必要がありました。これの手順についてメモします。
以下アプリケーションの環境です。
なお、SpringBootアプリケーションはこちら(https://start.spring.io/)で作った雛形をベースにしています。Packagingはjarを選択しました。
流れは以下の通りです。
GAE用の設定ファイルをフレキシブル環境用のproject_root/src/main/appengine/app.yamlを削除し、Java8スタンダード環境用にproject_root/src/main/webapp/WEB-INF/appengine-web.xmlを作成し必要事項(参考)を記述します。
雛形生成の際にビルド後の生成物としてjar形式を選択していたので、pom.xmlの<project>タグ内に<packaging>war</packaging>と記述しwar形式を指定します。(デフォルトでは<packaging>タグはありませんが、指定しないとjar形式になるようです)
SpringBootでは、パッケージングをjarからwarに変更する際にクラスの修正も必要になります。以下のようにアプリケーション起動処理が書かれているクラスを修正します。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(DemoApplication.class);
}
}
以上の記述をしないと404エラーが発生します。
1~3のステップを踏んだ後に、mvn clean installしてmvn package appengine:deployでデプロイ成功するはずです。
SpringBootで作ったアプリを起動すると、以下のエラーが起こり起動に失敗する現象に遭遇しました。
Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
この現象の原因と解決策についてメモします。
言語: Java8
FW: SpringBoot
DB: MySQL5.7
ビルドツール: Maven
application.propertiesにdb接続の設定を以下のように記述し、
spring.datasource.url=jdbc:mysql://localhost:3306/sample_app
spring.datasource.username=testuser
spring.datasource.password=testuser
spring.jpa.database=MYSQL
mvn spring-boot:run とターミナルで叩きアプリを起動します。
すると以下のようにエラーが発生しました。
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.5.RELEASE)
2021-07-05 18:57:34.756 INFO 10112 --- [ restartedMain] com.example.demo.DemoApplication : Starting DemoApplication on N0128.local with PID 10112 (/Users/akiyoshi.tokiya/develop/iTICKET/app_in_app/springbootsample/target/classes started by akiyoshi.tokiya in /Users/akiyoshi.tokiya/develop/iTICKET/app_in_app/springbootsample)
2021-07-05 18:57:34.758 INFO 10112 --- [ restartedMain] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-07-05 18:57:34.788 INFO 10112 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-07-05 18:57:34.789 INFO 10112 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2021-07-05 18:57:35.631 INFO 10112 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-07-05 18:57:35.651 INFO 10112 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-07-05 18:57:35.651 INFO 10112 --- [ restartedMain] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.19]
2021-07-05 18:57:35.718 INFO 10112 --- [ restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-07-05 18:57:35.718 INFO 10112 --- [ restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 929 ms
2021-07-05 18:57:35.806 INFO 10112 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-07-05 18:57:37.040 ERROR 10112 --- [ restartedMain] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Exception during pool initialization.
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174) ~[mysql-connector-java-8.0.16.jar:8.0.16]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64) ~[mysql-connector-java-8.0.16.jar:8.0.16]
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:835) ~[mysql-connector-java-8.0.16.jar:8.0.16]
...
at com.mysql.cj.NativeSession.connect(NativeSession.java:165) ~[mysql-connector-java-8.0.16.jar:8.0.16]
at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:955) ~[mysql-connector-java-8.0.16.jar:8.0.16]
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825) ~[mysql-connector-java-8.0.16.jar:8.0.16]
... 104 common frames omitted
Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
at java.base/sun.security.ssl.HandshakeContext.(HandshakeContext.java:172) ~[na:na]
at java.base/sun.security.ssl.ClientHandshakeContext.(ClientHandshakeContext.java:98) ~[na:na]
at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:238) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:434) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:412) ~[na:na]
at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:316) ~[mysql-connector-java-8.0.16.jar:8.0.16]
at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:188) ~[mysql-connector-java-8.0.16.jar:8.0.16]
at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:99) ~[mysql-connector-java-8.0.16.jar:8.0.16]
at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:352) ~[mysql-connector-java-8.0.16.jar:8.0.16]
... 111 common frames omitted
application.propertiesのspring.datasource.url=jdbc:mysql://localhost:3306/sample_appの末尾に?enabledTLSProtocols=TLSv1.2をつけることで解決しました。
spring.datasource.url=jdbc:mysql://localhost:3306/sample_app?enabledTLSProtocols=TLSv1.2
ログを詳しく見ると、Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate) というエラーに起因しています。これはRHEL 7 から RHEL 8 でサポートされる最新の TLS プロトコルバージョンが異なるために発生するようで、これにより、TLSハンドシェイクのネゴシエーション中にアプリケーションとT LSプロトコルの不一致が発生し接続が失敗してしまいます。
これの回避策として、データベース接続URLにTLSプロトコルバージョンを手動で指定すると接続できるようになるようです。
また、Communications link failureというエラーに関してですが、これはアプリケーションがdbにアクセスできていないために起こるエラーで、原因としては以下が考えられるようです。
上記の対策、調査方法としては以下があります。
今回の場合は5が当てはまりますね。
参考
・https://access.redhat.com/documentation/ja-jp/red_hat_build_of_thorntail/2.7/html/release_notes_for_thorntail_2.7/connection-between-a-rhel-8-based-application-and-a-rhel-7-based-mysql-5-7-database-fails-due-to-tls-protocol-version-mismatch
・https://stackoverflow.com/questions/2983248/com-mysql-jdbc-exceptions-jdbc4-communicationsexception-communications-link-fai
Linuxの負荷監視コマンドについてまとめます。
キャパシティプランニング(Capacity planning)とは、リソース不足によってシステムの運用に支障が出ないようにリソースを将来的に確保するための設計技法です。
具体的には、以下のようなリソースの状態を測定・監視・記録します。
・CPU使用率
・物理メモリ使用率
・スワップ領域の使用率
・ディスクI/O
・ネットワークI/O
これらリソースの測定をするのにtop, vmstat, iostat, free, sar, ...といったコマンドが用意されています。
以下、各リソースとコマンドの対応です。
測定対象 コマンド CPU システム全体としてのCPU使用率を表示: top, htop, vmstat, iostat, mpstat, sar
プロセス毎のCPU利用率を表示 : top, htop, ps u[ax]
負荷平均(load average)の表示 : top, htop, uptime, w物理メモリ システム全体としての物理メモリ使用状況を表示 : top, htop, free, vmstat, sar -r
プロセス毎の物理メモリ利用率を表示 : top, htop, ps u[aux]スワップ領域 スワップ領域の使用状況を表示 : top, htop, free, wmstat, sar -S, swapon -s
スワップイン・アウトの状況を表示 : vmstatディスクI/O iostat, iotop, vmstat, sar -b ネットワークI/O iptraf, netstat -i | -s, sar -n DEV|EDEV, ss プロセスのPID pstree -p, ps[aux], top, lsof