Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その35 )( Docker で起動しているサーバの TimeZone を Asia/Tokyo に変更する )
概要
記事一覧はこちらです。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その34 )( Docker で複数の Tomcat を起動して動作確認する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- 前回の記事で jar ファイル起動用コンテナを生成する Dockerfile に
ENV TZ="Asia/Tokyo"
を定義したのですが、これは Web アプリケーションを起動後にログを見たら日時が日本時間になっていないことに気づいたからです。 - PostgreSQL のコンテナとかはおそらく日本時間になっていなんだろうな。。。と思ったので、確認して必要があれば変更します。
- 前回の記事で jar ファイル起動用コンテナを生成する Dockerfile に
参照したサイト・書籍
目次
- 日本時間になっていないサーバを洗い出す
- 環境変数 TZ=Asia/Tokyo を設定してみる
- docker image を作成して rabbitmq1~3 に環境変数 TZ=Asia/Tokyo の設定が反映されるようにする
手順
日本時間になっていないサーバを洗い出す
docker-compose up -d
コマンドを実行してから各サーバのログ等を見たところ、以下のサーバが日本時間になっていませんでした。結局全てのサーバでしたが。。。
docker logs
コマンドで出力されるログを見て分かったもの- prometheus
- grafana
- redis-cluster-1~6
- redis_exporter
- rabbitmq1~3
- rabbitmq_exporter
- postgresql
- pgadmin4
- postgres_exporter
- mail-server
- ログが出力されていなかったので
docker exec <コンテナ名> date
コマンドを実行して分かったもの- haproxy
- haproxy-rsyslog
- rainloop
- 実行完了していて分からなかったもの
- redis-cluster-make
- flyway
この状態で pgadmin4 から select now()
を実行するとログと同じく日本時間ではありませんでした。
環境変数 TZ=Asia/Tokyo を設定してみる
docker-compose.yml の全てのコンテナに以下の設定を追加してみます。
environment: - TZ=Asia/Tokyo
コンテナを起動し直して確認してみると全てのコンテナが日本時間にはなりませんでした。以下の結果です。
- ログあるいは date コマンドの日時が変わらなかったもの
- prometheus
- redis_exporter
- rabbitmq1~3
- rabbitmq_exporter
- pgadmin4
- postgres_exporter
- rainloop
- ログあるいは date コマンドの日時が日本時間に変わったもの
- grafana
- redis-cluster-1~6
- postgresql
- mail-server
- haproxy
- haproxy-rsyslog
今は何がなんでも設定する必要はないので、日本時間に変わらなかったものについては rabbitmq1~3 以外は TZ=Asia/Tokyo
の設定をコメントアウトすることにします。rabbitmq1~3 は Dockerfile を作成して TZ=Asia/Tokyo
が反映されるイメージを作成してみます。
docker image を作成して rabbitmq1~3 に環境変数 TZ=Asia/Tokyo の設定が反映されるようにする
docker/rabbitmq/Dockerfile を新規作成し、以下の内容を記述します。
ARG RABBITMQ_VERSION FROM rabbitmq:${RABBITMQ_VERSION}-alpine RUN apk add --no-cache tzdata
docker-compose.yml を以下のように変更します。
rabbitmq1: build: context: ./docker/rabbitmq args: - RABBITMQ_VERSION=${RABBITMQ_VERSION} image: rabbitmq:${RABBITMQ_VERSION}-alpine-custom container_name: rabbitmq1 hostname: rabbitmq1 environment: - TZ=Asia/Tokyo - RABBITMQ_ERLANG_COOKIE - RABBITMQ_DEFAULT_USER - RABBITMQ_DEFAULT_PASS - RABBITMQ_DEFAULT_VHOST rabbitmq2: image: rabbitmq:${RABBITMQ_VERSION}-alpine-custom container_name: rabbitmq2 hostname: rabbitmq2 environment: - TZ=Asia/Tokyo - RABBITMQ_ERLANG_COOKIE volumes: - ./docker/rabbitmq/cluster-entrypoint.sh:/usr/local/bin/cluster-entrypoint.sh entrypoint: /bin/sh -c /usr/local/bin/cluster-entrypoint.sh depends_on: - rabbitmq1 rabbitmq3: image: rabbitmq:${RABBITMQ_VERSION}-alpine-custom container_name: rabbitmq3 hostname: rabbitmq3 environment: - TZ=Asia/Tokyo - RABBITMQ_ERLANG_COOKIE volumes: - ./docker/rabbitmq/cluster-entrypoint.sh:/usr/local/bin/cluster-entrypoint.sh entrypoint: /usr/local/bin/cluster-entrypoint.sh depends_on: - rabbitmq1
- rabbitmq1 に build の記述を追加し、image に記述するイメージ名の末尾に
-custom
を追加します。 - rabbitmq2, 3 は image に記述するイメージ名の末尾に
-custom
を追加します。これで rabbitmq1 で生成したイメージが使用されます。
docker-compose up -d
コマンドで起動します。
ログに出力される日時が日本時間になっていることが確認できます。(1/22 00:16に起動しています)。
rabbitmq:3.7.8-management-alpine と rabbitmq:3.7.8-management-alpine-custom のサイズを docker image ls rabbitmq
コマンドで確認してみると 1.7MB の差異でした。
履歴
2019/01/22
初版発行。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その34 )( Docker で複数の Tomcat を起動して動作確認する )
概要
記事一覧はこちらです。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その33 )( PC の IP アドレスが変更された時に修正するファイルを最小限にする ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
参照したサイト・書籍
Spring Boot with Docker
https://spring.io/guides/gs/spring-boot-docker/Networking in Compose
https://docs.docker.com/compose/networking/docker-compose creating multiple instances for the same image
https://stackoverflow.com/questions/39663096/docker-compose-creating-multiple-instances-for-the-same-imageCompose file versions and upgrading
https://docs.docker.com/compose/compose-file/compose-versioning/Get Started, Part 3: Services
https://docs.docker.com/get-started/part3/COPY failed: stat /var/lib/docker/tmp/docker-builder918577595/...
https://github.com/moby/moby/issues/34986Web Server Load-Balancing with HAProxy on Ubuntu 14.04
https://www.howtoforge.com/tutorial/ubuntu-load-balancer-haproxy/How to Enable HAProxy Stats
https://tecadmin.net/how-to-configure-haproxy-statics/多機能なロードバランサとして使える多機能プロクシサーバー「HAProxy」入門
https://knowledge.sakura.ad.jp/8078/Using SSL Certificates with HAProxy
https://serversforhackers.com/c/using-ssl-certificates-with-haproxy- 今回の記事では設定していませんが、HAProxy で http, https の両方を受け付けられるようにする記事を見つけたのでメモ書きとして残しておきます。
How can I set the timezone please?
https://github.com/gliderlabs/docker-alpine/issues/136How to set locale in Docker Alpine?
https://stackoverflow.com/questions/49042223/how-to-set-locale-in-docker-alpinegitattributes
https://git-scm.com/docs/gitattributes
目次
- 方針
- docker-compose.app.yml を作成する
- HAProxy の設定ファイル haproxy.cfg を作成する
- logback-spring.xml を変更し、spring.profiles.active = product の時にログをコンソールに出力できるようにする
- jar ファイル起動用コンテナを生成するための Dockerfile を作成する
- 動作確認
- *.sh をチェックアウトした時に必ず改行コードが LF になるよう .gitattributes を作成する
手順
方針
- docker-compose.yml とは別に docker-compose.app.yml というファイル作成し、
docker-compose up -d
コマンドを実行した後にdocker-compose -f docker-compose.app.yml up -d
コマンドで起動します。 - docker-compose.app.yml で jar ファイルからの Tomcat 3インスタンスと HAProxy を起動します。HAProxy の 8080番ポートにアクセスすると Tomcat の3インスタンスのどれかにリクエストが振り分けられます。
- jar ファイルから起動する時の Profile は既存の product を使用します。また、Docker で起動する時に product の設定から変更するものは docker-compose.app.yml に環境変数(environment)として設定します。
- product で起動する時はログファイルに出力するように設定していますが、Docker で起動する時にはコンソールに出力されるようにします。
docker-compose.app.yml を作成する
docker-compose.app.yml を新規作成し、以下の内容を記述します。
# docker-compose -f docker-compose.app.yml --compatibility up -d # docker-compose -f docker-compose.app.yml --compatibility down version: '3' services: app: build: context: . dockerfile: docker/app/Dockerfile image: ksbysample-webapp-lending volumes: - ./build/libs/ksbysample-webapp-lending-2.0.8-RELEASE.jar:/app.jar - ./docker/app/docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh environment: - SPRING_DATASOURCE_HIKARI_JDBC_URL=jdbc:postgresql://postgresql/ksbylending - SPRING_MAIL_HOST=mail-server - SPRING_RABBITMQ_HOST=haproxy deploy: mode: replicated replicas: 3 # entrypoint: /bin/sh # stdin_open: true # tty: true haproxy-app: image: haproxy:1.8.14-alpine container_name: haproxy-app volumes: - ./docker/app/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro ports: - "8080:8080" depends_on: - app haproxy-app-rsyslog: image: rafpe/docker-haproxy-rsyslog container_name: haproxy-app-rsyslog volumes: - ./docker/app/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro depends_on: - haproxy-app networks: default: external: name: ksbysample-webapp-lending_default
- app、haproxy-app、haproxy-app-rsyslog の3つのコンテナの設定を記述します。
- コンテナから接続する docker network は
docker-compose up -d
コマンド実行時に自動生成されるksbysample-webapp-lending_default
にします。docker-compose.app.yml の最後にnetworks: ksbysample-webapp-lending_default: external: name: ksbysample-webapp-lending_default
の設定を記述します。 - app コンテナの設定のポイントは以下の通りです。
- jar ファイルを Docker コンテナにコピーすることも考慮して(今回はコピーしませんが)、build の下の context の設定を
.
にし、Dockerfile のパスを dockerfile に記述します。 - jar ファイルはコンテナ内にコピーせず、
/app.jar
にマウントされるよう volumes に記述します。build し直した時にコンテナを生成し直さなくてもよいようにします。 - 以下の設定はサーバをコンテナ名に変更したものを environment に記述します。
- SPRING_DATASOURCE_HIKARI_JDBC_URL
- SPRING_MAIL_HOST
- SPRING_RABBITMQ_HOST
- deploy の設定を記述し、
docker-compose up -d
コマンドでコンテナを生成した時に app コンテナのインスタンスを自動で複数起動されるようにします。また deploy の設定は本来 docker swarm 用の設定ですが、--compatibility
オプションを追加すればdocker-compose up -d
コマンドでも使用できるようになります。
- jar ファイルを Docker コンテナにコピーすることも考慮して(今回はコピーしませんが)、build の下の context の設定を
HAProxy の設定ファイル haproxy.cfg を作成する
docker の下に app ディレクトリを新規作成した後、docker/app/haproxy.cfg を新規作成して以下の内容を記述します。
global log 127.0.0.1 local1 maxconn 4096 defaults log global mode http option httplog option dontlognull retries 3 option redispatch maxconn 2000 timeout connect 5000 timeout client 50000 timeout server 50000 listen app bind *:8080 stats enable stats hide-version stats realm Haproxy\ Statistics stats refresh 5s stats uri /haproxy?stats balance roundrobin option httpclose option forwardfor server ksbysample-webapp-lending_app_1 ksbysample-webapp-lending_app_1:8080 check inter 5s rise 2 fall 3 server ksbysample-webapp-lending_app_2 ksbysample-webapp-lending_app_2:8080 check inter 5s rise 2 fall 3 server ksbysample-webapp-lending_app_3 ksbysample-webapp-lending_app_3:8080 check inter 5s rise 2 fall 3
docker-compose -f docker-compose.app.yml --compatibility up -d
で複数起動させた app コンテナのコンテナ名は<プロジェクト名(ksbysample-webapp-lending)>_<コンテナ名(app)>_<連番(1~)>
になるので、ksbysample-webapp-lending_app_1、ksbysample-webapp-lending_app_2、ksbysample-webapp-lending_app_3 の3サーバにリクエストを振り分けるように設定します。
logback-spring.xml を変更し、spring.profiles.active = product の時にログをコンソールに出力できるようにする
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> <property name="LOGGING_APPENDER" value="${logging.appender:-FILE}"/> <if condition='"${spring.profiles.active}" == "product" && "${LOGGING_APPENDER}" == "FILE"'> <then> <property name="LOG_FILE" value="${LOG_FILE}"/> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> </appender> </then> </if> <if condition='"${spring.profiles.active}" == "develop"'> <then> <root> <appender-ref ref="CONSOLE"/> </root> </then> </if> <if condition='"${spring.profiles.active}" == "product"'> <then> <root> <appender-ref ref="${LOGGING_APPENDER}"/> </root> </then> </if> </configuration>
<property name="LOGGING_APPENDER" value="${logging.appender:-FILE}"/>
を追加します。<appender name="FILE" ...
を定義している部分の<if condition='"${spring.profiles.active}" == "product"'>
→<if condition='"${spring.profiles.active}" == "product" && "${LOGGING_APPENDER}" == "FILE"'>
に変更します。<if condition='"${spring.profiles.active}" == "product"'> ... </if>
の中に記述している<appender-ref ref="FILE"/>
→<appender-ref ref="${LOGGING_APPENDER}"/>
に変更します。
これで jar ファイル起動時に java の起動時オプションに -Dlogging.appender=CONSOLE
を指定すれば Profile が product でも CONSOLE に出力されるようになります。
jar ファイル起動用コンテナを生成するための Dockerfile を作成する
docker/app/docker-entrypoint.sh を新規作成し、以下の内容を記述します。
#!/bin/sh # 改行コードを LF にすること。LF でないと実行されない。 export JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxMetaspaceSize=384m" exec java $JAVA_OPTS \ -Djava.security.egd=file:/dev/./urandom \ -Dspring.profiles.active=product \ -Dlogging.appender=CONSOLE \ -jar /app.jar
docker/app/Dockerfile を新規作成し、以下の内容を記述します。
FROM openjdk:8-jdk-alpine RUN apk add --no-cache tzdata ENV TZ="Asia/Tokyo" ENV LANG="ja_JP.UTF-8" VOLUME /tmp EXPOSE 8080 ENTRYPOINT ["docker-entrypoint.sh"]
- Spring Boot with Docker の「Containerize It」に記載されている Dockerfile から以下の点を変更しました。
- 以下の2行を削除します。
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
- jar ファイルから出力されるログの日時を日本時間にしたいので、以下の2行を追加します。
RUN apk add --no-cache tzdata
ENV TZ="Asia/Tokyo"
- openjdk:8-jdk-alpine イメージのデフォルトの環境変数 LANG の値は C.UTF-8 なので、
ENV LANG="ja_JP.UTF-8"
を追加します。 - 8080番ポートを公開するので
EXPOSE 8080
を追加します。 - ENTRYPOINT には
"docker-entrypoint.sh"
だけ記述します。
- 以下の2行を削除します。
動作確認
docker-compose up -d
コマンドを実行した後、build タスクを実行して jar ファイルを作成します。
docker-compose -f docker-compose.app.yml --compatibility up -d
コマンドを実行します。
http://localhost:8080/haproxy?stats にアクセスして全てのインスタンスが緑色になるまで待ちます。
IntelliJ IDEA の Docker Plugin を見ると ksbysample-webapp-lending_app_1、ksbysample-webapp-lending_app_2、ksbysample-webapp-lending_app_3 の3つのコンテナが起動しており、ログがファイルではなくコンソールに出力されていることが確認できます。TimeZone を設定しているので、ログの日時が日本時間になっていることも確認できます。
以下の手順で動作確認します ( 画面キャプチャは省略します )。
- ブラウザを起動して http://localhost:8080/ にアクセスしてログイン画面を表示します。tanaka.taro@sample.com / taro でログインします。
- 検索対象図書館登録画面が表示されます。"東京都" で検索した後、一覧表示されている図書館から「国立国会図書館東京本館」を選択します。
- ログアウトします。
- ログイン画面に戻るので suzuki.hanako@test.co.jp / hanako でログインします。
- 貸出希望書籍 CSV ファイルアップロード画面が表示されます。以下の内容が記述された CSV ファイルをアップロードします。
"ISBN","書名"
"978-4-7741-6366-6","GitHub実践入門"
"978-4-7741-5377-3","JUnit実践入門"
"978-4-7973-8014-9","Java最強リファレンス"
"978-4-7973-4778-4","アジャイルソフトウェア開発の奥義"
"978-4-87311-704-1","Javaによる関数型プログラミング" - 「貸出状況を確認しました」のメールが送信されるので、メールに記述されている URL にアクセスします。
- 貸出申請画面が表示されます。3冊程「申請する」を選択して申請します。
- ログアウトします。
- 「貸出申請がありました」のメールが送信されるので、メールに記述されている URL にアクセスします。ログイン画面が表示されるので、tanaka.taro@sample.com / taro でログインします。
- 貸出承認画面が表示されます。「承認」あるいは「却下」を選択して確定させます。
- ログアウトします。
- 「貸出申請が承認・却下されました」のメールが送信されるので、メールに記述されている URL にアクセスします。ログイン画面が表示されるので、suzuki.hanako@test.co.jp / hanako でログインします。
- 貸出申請結果確認画面が表示されるので内容を確認します。
動作確認は特に問題ありませんでした。
HAProxy の stats page を見るとセッション数も In と Out のバイト数もほぼ均等になっており、リクエストが各 Tomcat に転送されていることが分かります。
またセッション情報が Spring Session により Redis に格納されているのでログイン後にどの Tomcat にアクセスしてもログイン状態が維持されていますが、ログインした後、
Redis の master に保存されているデータを全てクリアしてから、
メニューから「貸出希望書籍登録」を選択すると、セッション情報が削除されてログイン状態ではないのでログイン画面が表示されます。
最後に docker-compose -f docker-compose.app.yml --compatibility down
コマンドでコンテナを終了します。
これで簡単に Tomcat のマルチインスタンスでの動作確認が出来るようになりました。
*.sh をチェックアウトした時に必ず改行コードが LF になるよう .gitattributes を作成する
今回作成した docker/app/docker-entrypoint.sh は改行コードが LF でないと動作しないのですが、現在の git の設定ではチェックアウト時に改行コードを自動で CRLF になるように設定しているので(.gitconfig に autocrlf = true が設定されています)チェックアウトし直した時に改行コードが CRLF に変換されてしまいます。
*.sh だけ LF のまま維持する方法がないか調べたところ、https://git-scm.com/docs/gitattributes のページを見つけました。.gitattributes を作成して設定すれば維持できるようです。
.gitattributes を新規作成し、以下の内容を記述します。
* text=auto *.sh text eol=lf
実際に別のディレクトリに git clone してみたところ、docker/app/docker-entrypoint.sh の改行コードが LF になっていました。
履歴
2019/01/20
初版発行。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その33 )( PC の IP アドレスが変更された時に修正するファイルを最小限にする )
概要
記事一覧はこちらです。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その32 )( Spring Boot を 2.0.6 → 2.0.8 へバージョンアップする ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- 別の記事を書いている途中に WindowsUpdate で PC を再起動したら IP アドレスが変更されたのですが、変更を反映するファイルが以下の5ファイルと多かったので最低限のファイルだけ修正すればよいようにします。
- .env
- docker/prometheus/prometheus.yml
- src/main/resources/application-develop.properties
- src/main/resources/application-product.properties
- src/main/resources/application-unittest.properties
- また HAProxy に stats page というものがあることに気づいたので、表示できるようにします。
- 別の記事を書いている途中に WindowsUpdate で PC を再起動したら IP アドレスが変更されたのですが、変更を反映するファイルが以下の5ファイルと多かったので最低限のファイルだけ修正すればよいようにします。
参照したサイト・書籍
Accessing host machine from within docker container
https://forums.docker.com/t/accessing-host-machine-from-within-docker-container/14248- docker container から PC で起動している Spring Boot の Web アプリケーションにアクセスする時に
docker run --add-host="localhost:192.168.65.1"
のように--add-host
オプションを使うとホスト名でアクセスできると書かれていました。 - https://docs.docker.com/compose/compose-file/ を見ると docker-compose の場合には、extra_hosts で定義できるようです。
- docker container から PC で起動している Spring Boot の Web アプリケーションにアクセスする時に
目次
- docker-compose.yml、prometheus.yml を変更する
- application.properties を変更する
- RabbitMQ 用 HAProxy の stats page を表示する
手順
docker-compose.yml、prometheus.yml を変更する
まずは Prometheus と Grafana から。docker-compose.yml に extra_hosts を設定すると IP アドレスではなく定義したホスト名でアクセスできるようにできるので、docker-compose.yml を以下のように変更します。
services: prometheus: image: prom/prometheus:latest container_name: prometheus ports: - "9090:9090" volumes: - ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - ./docker/prometheus/storage:/prometheus extra_hosts: - "app:${HOST_IP_ADDRESS}"
- prometheus コンテナに extra_hosts の定義を追加します。PC のホスト名は app にします。
docker/prometheus/prometheus.yml を以下のように変更します。
- job_name: 'spring-actuator' metrics_path: '/actuator/prometheus' scrape_interval: 15s basic_auth: username: actuator password: xxxxxxxx static_configs: # Docker で起動した Prometheus からローカルPCで起動している Spring Boot のアプリケーション # にアクセスする。docker-compose.yml の extra_hosts に定義した PC のホスト名 app を設定する。 - targets: ['app:8080'] labels: application: 'lending'
targets: ['172.31.16.1:8080']
→targets: ['app:8080']
に変更します。
Grafana の Data Source で URL に IP アドレスを記述していましたが、コンテナ名でアクセスできるので prometheus
に変更します。
これで Grafana から app:8080
の Instance 名で Spring Boot の Web アプリケーションのメトリックスが見られるようになります。
application.properties を変更する
src/main/resources/application.properties を以下のように変更します。
host.ip.address=172.31.16.1 doma.dialect=org.seasar.doma.jdbc.dialect.PostgresDialect management.endpoints.web.exposure.include=health,info,loggers,prometheus #spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration spring.datasource.hikari.jdbc-url=jdbc:postgresql://localhost/ksbylending spring.datasource.hikari.username=ksbylending_user spring.datasource.hikari.password=xxxxxxxx spring.datasource.hikari.driver-class-name=org.postgresql.Driver spring.datasource.hikari.leak-detection-threshold=60000 spring.datasource.hikari.register-mbeans=true spring.mail.host=localhost spring.mail.port=25 spring.redis.cluster.nodes[0]=${host.ip.address}:6379 spring.redis.cluster.nodes[1]=${host.ip.address}:6380 spring.redis.cluster.nodes[2]=${host.ip.address}:6381 spring.redis.cluster.nodes[3]=${host.ip.address}:6382 spring.redis.cluster.nodes[4]=${host.ip.address}:6383 spring.redis.cluster.nodes[5]=${host.ip.address}:6384 spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=rabbitmq spring.rabbitmq.password=12345678 spring.session.store-type=redis spring.freemarker.cache=true spring.freemarker.settings.number_format=computer spring.freemarker.charset=UTF-8 spring.freemarker.enabled=false spring.freemarker.prefer-file-system-access=false spring.thymeleaf.mode=HTML valueshelper.classpath.prefix= logging.level.root=INFO logging.level.org.seasar.doma=ERROR
host.ip.address=172.31.16.1
を追加します。- 以下の設定を追加し、application-develop.properties, application-unittest.properties, application-product.properties から削除します。
spring.mail.
から始まる設定(2項目)spring.redis.
から始まる設定 (6項目)spring.rabbitmq.
から始まる設定 (4項目)
spring.redis.cluster.nodes
の値の IP アドレスの部分を${host.ip.address}
に変更します。
これで IP アドレスが変更された時には .env の HOST_IP_ADDRESS
と src/main/resources/application.properties の host.ip.address
を変更するだけでよくなりました。
RabbitMQ 用 HAProxy の stats page を表示する
docker/rabbitmq/haproxy.cfg に listen stats
の設定を記述していましたが、コピーして書いていただけで何の設定か理解していなかったので stats page を表示できるように設定していませんでした。表示すると RabbitMQ の起動状況が見えるようになるので、必要な設定をします。
まず docker/rabbitmq/haproxy.cfg の設定が少し足りなかったのと変えたい部分があったので以下のように変更します。
listen stats bind *:1936 mode http stats enable stats hide-version stats realm Haproxy\ Statistics stats refresh 5s stats uri /haproxy?stats
- stats page を自動更新させたいので、
stats refresh 5s
の記述を追加し 5秒おきに自動更新されるようにします。 - stats page であることが分かるよう URI を変更します。
stats uri /
→stats uri /haproxy?stats
にします。
次に docker-compose.yml を以下のように変更します。
haproxy: image: haproxy:1.8.14-alpine container_name: haproxy volumes: - ./docker/rabbitmq/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro depends_on: - rabbitmq1 - rabbitmq2 - rabbitmq3 ports: - "1936:1936" - "5672:5672" - "15672:15672"
- haproxy コンテナの設定の ports に
- "1936:1936"
を追加します。
これで docker-compose up -d
コマンドでコンテナを起動してから http://localhost:1936/haproxy?stats にアクセスすると HAProxy の stats page が表示されます。
起動直後は全ての rabbitmq が赤色ですが、
起動が認識されると黄色に変わって、
最後に緑色になります。全ての rabbitmq が緑色で表示されれば Spring Boot の Web アプリからアクセスしても問題ありません。
履歴
2019/01/18
初版発行。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その32 )( Spring Boot を 2.0.6 → 2.0.8 へバージョンアップする )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- Spring Boot を 2.0.x 系の最新バージョンである 2.0.8 へバージョンアップします。
- ライブラリも出来るだけ最新バージョンにします。
参照したサイト・書籍
目次
手順
build.gradle を変更する
build.gradle の以下の点を変更します。
buildscript { ext { group "ksbysample" version "2.0.8-RELEASE" } repositories { mavenCentral() maven { url "https://repo.spring.io/release/" } maven { url "https://plugins.gradle.org/m2/" } } } plugins { id "java" id "eclipse" id "idea" id "org.springframework.boot" version "2.0.8.RELEASE" id "io.spring.dependency-management" version "1.0.6.RELEASE" id "groovy" id "checkstyle" id "com.github.spotbugs" version "1.6.8" id "pmd" id "net.ltgt.errorprone" version "0.0.16" id "de.undercouch.download" version "3.4.3" id "com.gorylenko.gradle-git-properties" version "2.0.0-beta1" } .......... dependencies { def jdbcDriver = "org.postgresql:postgresql:42.2.5" def spockVersion = "1.2-groovy-2.5" def domaVersion = "2.21.0" def lombokVersion = "1.18.4" def errorproneVersion = "2.3.1" def powermockVersion = "2.0.0" def spotbugsVersion = "3.1.10"
- buildscript block 内で
version "2.0.6-RELEASE"
→version "2.0.8-RELEASE"
に変更します。 - plugins block 内で
id "org.springframework.boot" version "2.0.6.RELEASE"
→id "org.springframework.boot" version "2.0.8.RELEASE"
に変更します。 - dependencies block の以下の点を変更します。
def domaVersion = "2.20.0"
→def domaVersion = "2.21.0"
def powermockVersion = "2.0.0-RC.4"
→def powermockVersion = "2.0.0"
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
履歴
2019/01/12
初版発行。
IntelliJ IDEA を 2018.3.2 → 2018.3.3 へバージョンアップ
IntelliJ IDEA を 2018.3.2 → 2018.3.3 へバージョンアップする
IntelliJ IDEA の 2018.3.3 がリリースされているのでバージョンアップします。
- IntelliJ IDEA 2018.3.3 is Released!
https://blog.jetbrains.com/idea/2019/01/intellij-idea-2018-3-3-is-released/
※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。
IntelliJ IDEA のメインメニューから「Help」-「Check for Updates...」を選択します。
「IDE and Plugin Updates」ダイアログが表示されます。左下に「Update and Restart」ボタンが表示されていますので、「Update and Restart」ボタンをクリックします。
Plugin の update も表示されました。このまま「Update and Restart」ボタンをクリックします。
Patch がダウンロードされて IntelliJ IDEA が再起動します。
IntelliJ IDEA が起動すると画面下部に「Indexing…」のメッセージが表示されますので、終了するまで待機します。
IntelliJ IDEA のメインメニューから「Help」-「About」を選択し、2018.3.3 へバージョンアップされていることを確認します。
Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run 'All Tests' with Coverage」を選択し、テストが全て成功することを確認します。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その31 )( Spring Actuator の Basic 認証用ユーザの認証成功時には AuthenticationSuccessEvent イベントが発生しないようにする+いろいろ調整する )
概要
記事一覧はこちらです。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その30 )( Redis のクライアントライブラリを Jedis → Lettuce に変更する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Prometheus が Spring Actuator にアクセスした時に、コンソールにレスポンスの内容が出力されて他のログが見にくかったり、Basic 認証の成功時に AuthenticationSuccessEvent が発生して update 文が実行されたりしていたので、修正します。
- 他に以下の点を変更します。
- PostgreSQL のコンテナ起動時に毎回検索対象図書館を登録し直さなくてもよいようにします。
- build タスク実行時に PMD の
This analysis could be faster, ...
のメッセージが出力されないようにします。 - HikariCP を最新バージョンに変更して leak-detection-threshold の設定を変更します。
- spring-boot-properties-migrator が何もメッセージを出力していないので削除します。
参照したサイト・書籍
- @EventListener for AuthenticationSuccessEvent or InteractiveAuthenticationSuccessEvent not fired
https://stackoverflow.com/questions/41076500/eventlistener-for-authenticationsuccessevent-or-interactiveauthenticationsucces
目次
- Prometheus が Spring Actuator にアクセスした時に、コンソールにレスポンスの内容が出力されないようにする
- Prometheus が Spring Actuator にアクセスした時に AuthenticationSuccessEventListener#onApplicationEvent の update 文が実行されないようにする
- library_forsearch テーブルに初期データを登録して、最初に検索対象図書館を登録不要にする
- pmdMain タスク実行時の
This analysis could be faster, ...
のメッセージが出力されないようにする - HikariCP は最新の 3.3.0 を dependenceis に指定し、spring.datasource.hikari.leak-detection-threshold の値を 5000 → 60000 に変更する
runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")
を削除する- 次回は。。。
手順
Prometheus が Spring Actuator にアクセスした時に、コンソールにレスポンスの内容が出力されないようにする
src/main/resources/application-develop.properties で logging.level.org.springframework.web=DEBUG を設定しているため、Prometheus が Spring Actuator にアクセスした時のレスポンスの内容もコンソールに出力されます。
このままではコンソールに大量に出力されるため、ログが全然確認できなくて調査等の時に困ったので出ないようにします。org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor の DEBUG ログなので、このクラスだけ log.level を INFO に変更して出力されないようにします。
src/main/resources/application-develop.properties に logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor=INFO
を追加します。
# Spring MVC logging.level.org.springframework.web=DEBUG logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor=INFO
Prometheus が Spring Actuator にアクセスした時に AuthenticationSuccessEventListener#onApplicationEvent の update 文が実行されないようにする
Prometheus が Spring Actuator にアクセスした時のレスポンスの内容が出力されないようにしたら、今度は Prometheus が Spring Actuator にアクセスした時に Basic 認証で成功したら ksbysample.webapp.lending.security.AuthenticationSuccessEventListener#onApplicationEvent が呼び出されて update user_info set cnt_badcredentials = 0 where mail_address = 'actuator'
文が実行されていることに気づきました。Spring Actuator の Basic 認証用ユーザの場合には AuthenticationSuccessEvent イベントが発生しないようにします。
src/main/java/ksbysample/webapp/lending/config/WebSecurityConfig.java の以下の点を変更します。
@Configuration public class WebSecurityConfig { public static final String DEFAULT_SUCCESS_URL = "/booklist"; public static final String REMEMBERME_KEY = "ksbysample-webapp-lending"; public static final String ACTUATOR_USERNAME = "actuator"; .......... /** * Spring Actuator の Basic認証用ユーザの場合には AuthenticationSuccessEvent を発生させないための * AuthenticationEventPublisher */ static class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher { /** * コンストラクタ * * @param applicationEventPublisher {@link ApplicationEventPublisher} オブジェクト */ public CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { super(applicationEventPublisher); } /** * 認証成功時のメソッド * Spring Actuator の Basic認証用ユーザの場合には AuthenticationSuccessEvent を発生させない * * @param authentication {@link Authentication} オブジェクト */ @Override public void publishAuthenticationSuccess(Authentication authentication) { if (StringUtils.equals(authentication.getName(), ACTUATOR_USERNAME)) { return; } super.publishAuthenticationSuccess(authentication); } } /** * @param auth ??? * @throws Exception */ @SuppressWarnings("PMD.SignatureDeclareThrowsException") @Autowired public void configAuthentication(AuthenticationManagerBuilder auth , ApplicationEventPublisher applicationEventPublisher) throws Exception { // AuthenticationManagerBuilder#userDetailsService の後に auth.inMemoryAuthentication() を呼び出すと // AuthenticationManagerBuilder の defaultUserDetailsService に // org.springframework.security.provisioning.InMemoryUserDetailsManager がセットされて // Remember Me 認証で InMemoryUserDetailsManager が使用されて DB のユーザが参照されなくなるので、 // Remember Me 認証で使用する UserDetailsService を一番最後に呼び出す // ※今回の場合には auth.userDetailsService(userDetailsService) が一番最後に呼び出されるようにする auth.inMemoryAuthentication() .withUser(ACTUATOR_USERNAME) .password("{noop}xxxxxxxx") .roles("ENDPOINT_ADMIN"); auth.authenticationEventPublisher(new CustomAuthenticationEventPublisher(applicationEventPublisher)) .userDetailsService(userDetailsService); } }
public static final String ACTUATOR_USERNAME = "actuator";
を追加し、configAuthentication メソッド内の.withUser("actuator")
→.withUser(ACTUATOR_USERNAME)
に変更します。- DefaultAuthenticationEventPublisher を継承した CustomAuthenticationEventPublisher クラスを定義し、publishAuthenticationSuccess メソッドで Spring Actuator の Basic認証用ユーザの場合には何もしないようにします。
- configAuthentication メソッドの以下の点を変更します。
- 引数に
ApplicationEventPublisher applicationEventPublisher
を追加します。 .authenticationEventPublisher(new CustomAuthenticationEventPublisher(applicationEventPublisher))
を追加します。
- 引数に
これで update 文は実行されないようになります(DispatcherServlet の DEBUG ログだけが出ています)。
library_forsearch テーブルに初期データを登録して、最初に検索対象図書館を登録不要にする
PostgreSQL を Docker で起動して Flyway で初期データを登録するように変更しましたが、library_forsearch テーブルには初期データを登録していなかったため起動直後は検索対象図書館登録画面から図書館を選択する必要がありました。面倒なので初期データを登録するようにします。
src/main/resources/db/migration/V1.1__insert_data.sql を以下のように変更します。
.......... INSERT INTO public.user_role (role_id, user_id, role) VALUES (9, 8, 'ROLE_USER'); INSERT INTO public.library_forsearch (systemid, formal) VALUES ('Tokyo_NDL', '国立国会図書館東京本館');
INSERT INTO public.library_forsearch (systemid, formal) VALUES ('Tokyo_NDL', '国立国会図書館東京本館');
を追加します。
これで最初から「国立国会図書館東京本館」が選択された状態になります。
pmdMain タスク実行時の This analysis could be faster, ...
のメッセージが出力されないようにする
build タスクを実行した時に pmdMain タスクで This analysis could be faster, please consider using Incremental Analysis: https://pmd.github.io/pmd-6.10.0/pmd_userdocs_incremental_analysis.html
のメッセージが出力されますが、出力しないようにする方法が分かったので反映します。
build.gradle を以下のように変更します。
pmd { toolVersion = "6.10.0" sourceSets = [project.sourceSets.main] ignoreFailures = true consoleOutput = true ruleSetFiles = rootProject.files("/config/pmd/pmd-project-rulesets.xml") ruleSets = [] } pmdMain { def backupLoggerLevel doFirst { backupLoggerLevel = logger.context.level logger.context.level = LogLevel.QUIET } doLast { logger.context.level = backupLoggerLevel } }
pmdMain { ... }
の部分を追加します。
build タスクを実行すると以下のように This analysis could be faster, ...
のメッセージが出力されません。
また PMD で警告が出る場合は以下のように出力されます(WebSecurityConfig クラスで使用されない import 文が残るように変更して build しています)。
HikariCP は最新の 3.3.0 を dependenceis に指定し、spring.datasource.hikari.leak-detection-threshold の値を 5000 → 60000 に変更する
ここまでの状態で Tomcat を起動していろいろ画面を操作してみたのですが、そこそこの頻度で Connection leak detection triggered for org.postgresql.jdbc.PgConnection@4d36771 on thread http-nio-8080-exec-4, stack trace follows
のようなメッセージがコンソールに出力されることに気づきました。現在 5 秒で設定していますが、もう少し長めにするとこのエラーは出なくなるようなので 60秒に変更します。
また依存関係にある HikariCP のバージョンが Spring Boot の 2.0.6 では 2.7.9、2.1.1 だと 3.2.0 なのですが、HikariCP/CHANGES を見ると Changes in 3.0.0
に update to Micrometer 1.0.0.
という記述を見つけたので、最新の 3.3.0 に変更するとにします。
まずは build.gradle に implementation("com.zaxxer:HikariCP:3.3.0")
を追加した後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
dependencies { .......... implementation("org.flywaydb:flyway-core:5.2.4") implementation("com.zaxxer:HikariCP:3.3.0") testImplementation("org.dbunit:dbunit:2.6.0") ..........
src/main/resources/application.properties を以下のように変更します。
#spring.autoconfigure.exclude=com.integralblue.log4jdbc.spring.Log4jdbcAutoConfiguration spring.datasource.hikari.jdbc-url=jdbc:postgresql://localhost/ksbylending spring.datasource.hikari.username=ksbylending_user spring.datasource.hikari.password=xxxxxxxx spring.datasource.hikari.driver-class-name=org.postgresql.Driver spring.datasource.hikari.leak-detection-threshold=60000 spring.datasource.hikari.register-mbeans=true
spring.datasource.hikari.leak-detection-threshold=5000
→spring.datasource.hikari.leak-detection-threshold=60000
に変更します。
runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")
を削除する
Tomcat 起動時に spring-boot-properties-migrator がメッセージを出力していなかったので、build.gradle の dependencies block から runtimeOnly("org.springframework.boot:spring-boot-properties-migrator")
を削除します。削除後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
次回は。。。
Spring Framework の 5.1.4 がリリースされたので、まもなく Spring Boot の 2.0.8 が出るはずです。2.0.8 にバージョンアップしてから jar ファイルを作成→動作確認してから、最後に感想を書いて終わりにします。
履歴
2019/01/11
初版発行。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その30 )( Redis のクライアントライブラリを Jedis → Lettuce に変更する )
概要
記事一覧はこちらです。
Spring Boot 1.5.x の Web アプリを 2.0.x へバージョンアップする ( その29 )( build.gradle の dependencies から不要な記述を削除する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Spring Boot 2.0 Migration Guide - Redis に
Lettuce is now used instead of Jedis as the Redis driver when you use spring-boot-starter-data-redis.
という記述があり、spring-boot-starter-data-redis の依存関係に入っている Redis のクライアントライブラリが Jedis から Lettuce になっていることに気づきました。 - 以前 Redis Cluster への接続処理を Jedis で実装したのですが、Lettuce に変更します。
- Spring Boot 2.0 Migration Guide - Redis に
参照したサイト・書籍
Spring Data Redis
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/Redis Spring data with Lettuce: com.lambdaworks.redis.RedisCommandExecutionException: MOVED error
https://stackoverflow.com/questions/53402326/redis-spring-data-with-lettuce-com-lambdaworks-redis-rediscommandexecutionexcepRedisCommandExecutionException: MOVED error when used cluster configuration endpoint
https://jira.spring.io/browse/DATAREDIS-898LINE LIVE のチャットが?30,000+/min のコメント投稿を捌くようになるまで
https://www.slideshare.net/linecorp/line-live-30000min-98811987Lettuce Reference Guide
https://lettuce.io/core/release/reference/index.html- 7.2.1. Cluster-specific options を参考にしました。
目次
- spring-boot-starter-data-redis の依存関係に Lettuce が入っていることを確認する
- build.gradle を変更する
- 単純に JedisConnectionFactory → LettuceConnectionFactory に変更してみる
- Lettuce で failover 発生時にサーバの切替が認識されるように実装する
- build.gradle に spring-boot-configuration-processor を追加する
手順
spring-boot-starter-data-redis の依存関係に Lettuce が入っていることを確認する
gradlew dependencies
コマンドを実行すると spring-boot-starter-data-redis の依存関係に io.lettuce:lettuce-core が入っていることが確認できます。
+--- org.springframework.boot:spring-boot-starter-data-redis -> 2.0.6.RELEASE | +--- org.springframework.boot:spring-boot-starter:2.0.6.RELEASE (*) | +--- org.springframework.data:spring-data-redis:2.0.11.RELEASE | | +--- org.springframework.data:spring-data-keyvalue:2.0.11.RELEASE | | | +--- org.springframework.data:spring-data-commons:2.0.11.RELEASE (*) | | | +--- org.springframework:spring-context:5.0.10.RELEASE (*) | | | +--- org.springframework:spring-tx:5.0.10.RELEASE (*) | | | \--- org.slf4j:slf4j-api:1.7.25 | | +--- org.springframework:spring-tx:5.0.10.RELEASE (*) | | +--- org.springframework:spring-oxm:5.0.10.RELEASE | | | +--- org.springframework:spring-beans:5.0.10.RELEASE (*) | | | \--- org.springframework:spring-core:5.0.10.RELEASE (*) | | +--- org.springframework:spring-aop:5.0.10.RELEASE (*) | | +--- org.springframework:spring-context-support:5.0.10.RELEASE (*) | | \--- org.slf4j:slf4j-api:1.7.25 | \--- io.lettuce:lettuce-core:5.0.5.RELEASE | +--- io.projectreactor:reactor-core:3.1.6.RELEASE -> 3.1.10.RELEASE | | \--- org.reactivestreams:reactive-streams:1.0.2 | +--- io.netty:netty-common:4.1.24.Final -> 4.1.29.Final | +--- io.netty:netty-transport:4.1.24.Final -> 4.1.29.Final | | +--- io.netty:netty-buffer:4.1.29.Final | | | \--- io.netty:netty-common:4.1.29.Final | | \--- io.netty:netty-resolver:4.1.29.Final | | \--- io.netty:netty-common:4.1.29.Final | \--- io.netty:netty-handler:4.1.24.Final -> 4.1.29.Final | +--- io.netty:netty-buffer:4.1.29.Final (*) | +--- io.netty:netty-transport:4.1.29.Final (*) | \--- io.netty:netty-codec:4.1.29.Final | \--- io.netty:netty-transport:4.1.29.Final (*)
build.gradle を変更する
build.gradle の dependencies block から implementation("redis.clients:jedis")
を削除します。変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
単純に JedisConnectionFactory → LettuceConnectionFactory に変更してみる
src/main/java/ksbysample/webapp/lending/config/RedisClusterConfig.java を以下のように変更します。
package ksbysample.webapp.lending.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import java.util.List; /** * Redis Cluster 用 Configuration クラス */ @Data @Configuration @ConfigurationProperties(prefix = "spring.redis.cluster") public class RedisClusterConfig { List<String> nodes; /** * Redis Cluster に接続するための {@link LettuceConnectionFactory} オブジェクトを生成する * * @return {@link LettuceConnectionFactory} オブジェクト */ @Bean public RedisConnectionFactory connectionFactory() { return new LettuceConnectionFactory(new RedisClusterConfiguration(nodes)); } }
- connectionFactory メソッド内で、
JedisConnectionFactory
→LettuceConnectionFactory
に変更します。
動作確認してみます。docker-compose up -d
コマンドを実行した後、Tomcat を起動します。
Spring Actuator の health check で Redis に接続できていることを確認します。
docker exec -it redis-cluster-6382 redis-cli
コマンドで Redis に接続した後、cluster nodes
コマンドを実行してクラスタ構成を確認します。
今は以下の構成になっています。
- redis-cluster-6379(master) - redis-cluster-6382(slave)
- redis-cluster-6380(master) - redis-cluster-6383(slave)
- redis-cluster-6381(master) - redis-cluster-6384(slave)
まず http://localhost:8080/ にアクセスしてログイン画面を表示した後、tanaka.taro@sample.com / taro でログインします。
IntelliJ IDEA の Docker Plugin から Redis の master 3台(redis-cluster-6379, redis-cluster-6380, redis-cluster-6381)を停止します。
redis-cli から cluster nodes
コマンドを実行して以前の master のステータスが fail になっていること、slave → master に昇格していることを確認します。
ブラウザの右上の「ログアウト」リンクをクリックしてログアウトしてみます。JedisConnectionFactory の時はログアウトできたのですが、JedisConnectionFactory
→ LettuceConnectionFactory
に変更しただけの状態だとエラーになりログアウトできません(Ctrl+F5 を押して何度かリロードしてみましたがずっとエラーのままでした)。
コンソールには
org.springframework.data.redis.RedisSystemException: Redis exception; nested exception is io.lettuce.core.RedisException: io.lettuce.core.RedisConnectionException: Unable to connect to 172.23.136.33:6381
というエラーが出ていて、どうもライブラリの方で master が変わったことが認識できていないようです。
Tomcat を停止し、docker-compose down
コマンドを実行してコンテナも停止します。
Lettuce で failover 発生時にサーバの切替が認識されるように実装する
Spring Data Redis の記述だと分からなかったのですが、以下のドキュメントを見るとオプションの設定が必要だったようです。
- LINE LIVE のチャットが?30,000+/min のコメント投稿を捌くようになるまで
- Lettuce Reference Guide - 7.2.1. Cluster-specific options
src/main/java/ksbysample/webapp/lending/config/RedisClusterConfig.java を以下のように変更します。
package ksbysample.webapp.lending.config; import io.lettuce.core.cluster.ClusterClientOptions; import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import java.time.Duration; import java.util.List; /** * Redis Cluster 用 Configuration クラス */ @Data @Configuration @ConfigurationProperties(prefix = "spring.redis.cluster") public class RedisClusterConfig { List<String> nodes; /** * Redis Cluster に接続するための {@link LettuceConnectionFactory} オブジェクトを生成する * * @return {@link LettuceConnectionFactory} オブジェクト */ @Bean public RedisConnectionFactory connectionFactory() { ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enablePeriodicRefresh(Duration.ofSeconds(30)) .enableAllAdaptiveRefreshTriggers() .build(); ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder() .validateClusterNodeMembership(false) .topologyRefreshOptions(clusterTopologyRefreshOptions) .build(); LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() .commandTimeout(Duration.ofSeconds(15)) .shutdownTimeout(Duration.ZERO) .clientOptions(clusterClientOptions) .build(); RedisClusterConfiguration serverConfig = new RedisClusterConfiguration(nodes); return new LettuceConnectionFactory(serverConfig, clientConfig); } }
動作確認してみます。先程と同じ手順で、docker-compose up -d
コマンド実行 → Tomcat 起動 → ログイン画面から tanaka.taro@sample.com / taro でログインする、まで実行します。
cluster nodes
コマンドの実行結果は以下のようになっており、構成は先程と同じです。
IntelliJ IDEA の Docker Plugin から Redis の master 3台(redis-cluster-6379, redis-cluster-6380, redis-cluster-6381)を停止します。
redis-cli から cluster nodes
コマンドを実行して以前の master のステータスが fail になっていること、slave → master に昇格していることを確認します。
ブラウザの右上の「ログアウト」リンクをクリックすると、今度はログアウトすることができました。
この後ログイン/ログアウトを繰り返したり、Redis の failover を何度も行ってみましたが、エラーは出ませんでした。
build.gradle に spring-boot-configuration-processor を追加する
@ConfigurationProperties を付与すると IntelliJ IDEA では以下のような画面(エディタの上部に Spring Boot Configuration Annotation Processor not found in classpath
のメッセージが表示され、@ConfigurationProperties に赤波下線が付く )になります。
build.gradle の dependencies block に compileOnly("org.springframework.boot:spring-boot-configuration-processor")
を追記して出ないようにします。
dependencies { .......... runtimeOnly("org.springframework.boot:spring-boot-devtools") compileOnly("org.springframework.boot:spring-boot-configuration-processor") implementation("org.springframework.session:spring-session-data-redis") ..........
Recommend the use of the compileOnly or annotationProcessor configurations for spring-boot-configuration-processor の Issue を見ると compileOnly ではなく annotationProcessor でも良さそうな気がしますが、annotationProcessor だとエディタ上のメッセージが消えず、build 実行時に compileTestJava タスクで 警告:次のオプションはどのプロセッサでも認識されませんでした: '[org.springframework.boot.configurationprocessor.additionalMetadataLocations]'
という警告が出るので compileOnly にしました。
履歴
2019/01/06
初版発行。