JenkinsでwarファイルをTomcatにデプロイする

前提

ビルドが成功して、warファイルが作成されること。

プラグインのインストール

JenkinsのPlugin ManagerでDeploy to container Pluginをインストールする。

ビルド後の処理の追加

Deploy war/ear to a containerを追加する。

Warファイルのパス、コンテキスト名を設定する。

Built-in Tomcat manager rolesで、manager-scriptに設定した、ユーザー名、パスワードをCredentialsに設定する。

JenkinsでMavenの実行結果のレポートを作成する

Mavenのインストール

sudo apt install maven
mvn --version

レポート作成でよく使うプラグインをインストールする

JaCoCo plugin: JaCoCoコードカバレッジレポートをJenkinsに統合する。

Warnings Next Generation Plugin: 静的分析ツールによって報告されたコンパイラの警告または問題を収集し、結果を視覚化する。(自分はspotbugs、checkstyleで使用)

Jenkinsの設定

ビルドにMavenの呼び出しを追加する。

package site

ビルド後の処理に以下の項目を追加する。

1.JUnitテスト結果の集計
target/surefire-reports/*.xml


2.JaCoCoカバレッジレポートを記録


3.Javadocの保存


4.CheckStyleとSpotBugsの解析結果を保存

GithubにコミットしたらJenkinsで自動的にビルドされるようにする

Jenkins側の設定

グローバルセキュリティの設定

ログインしていない場合でも、jenkinsにリードオンリーでアクセスできるように設定する。

ジョブを作成する

GitHub projectにチェックし、GitHub上のプロジェクトのURLを設定する。同時にソースコード管理にもURLを設定する。公開レポジトリであれば認証情報は入力不要。

GitHub hook trigger for GITScm pollingにチェックを入れる。

上記でジョブを作成する。これでジョブを自動的に起動する準備が出来たので、後はプロジェクトの要件に合わせてビルドやテストが起動するように、カスタマイズすれば良い。

GitHub側の設定

連携するプロジェクトのSettingsを開きWebhookを作成する。Payload URLにはJenkinsのURL+「/github-webhook/」を指定する。Content typeにはJSONを指定する。

動作確認

GitHubに変更がpushされると、Jenkinsのジョブが起動することが確認できる。

Mavenでのビルド、そのビルド後の処理について

Mavenのビルド、Mavenのビルド処理で作成したJavadoc等をJenkinsプロジェクトのメニューに表示されるようにする方法はこちら

Postfixのrelayhostにsmtp.gmail.comを使用する

Googleアカウント側の設定

smtp.gmail.comを使用するにはGoogleアカウントが必要になる。Googleアカウントの設定を開き、セキュリティにあるアプリパスワードをクリックする。

アプリはメールを選択する。

デバイスはその他を選択し、わかりやすい名前を入力する。


生成ボタンをクリックすると下記の黄色の枠内にアプリパスワードが表示されるので、Postfix側の設定に使用する。

Postfix側の設定

Postfixをインストールして、SMTP認証を有効にする。

sudo apt install postfix

sudo postconf -e "relayhost = [smtp.gmail.com]:587"
sudo postconf -e "smtp_sasl_auth_enable = yes" / 
"smtp_sasl_security_options = noanonymous" / 
"smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd"

sudo postconf -e "smtp_use_tls = yes" /
"smtp_tls_loglevel = 1" /
"smtp_tls_security_level = encrypt" /
"smtp_tls_note_starttls_offer = yes" /
"smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt"

sudo postconf -e "mynetworks = 127.0.0.0/8"

Googleアカウント側の設定を参照し、smtp.gmail.comのログインID、パスワードを設定する。

sudo vi /etc/postfix/sasl_passwd
[smtp.gmail.com]:587 Gmailアドレス:アプリパスワード

sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap hash:/etc/postfix/sasl_passwd
sudo service postfix restart

テストメールを送信する。

echo 'Hellow World!' | mail -s test toメールアドレス

無料のSSL証明書をインストールする(Ubuntu 22.04版)

No-IPドメインでLet’s Encryptの証明書を使うことが出来る。

事前確認

・ドメイン名を取得していること(今回はNo-IPを使用する)
・80、443ポートが外部に開放されていること

Certbotをインストールする

sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

SSL証明書の取得とApacheへの設定の追加

sudo certbot --apache

取得と設定に成功すれば、下記の通り表示される。

Successfully deployed certificate forドメイン名 to /etc/apache2/sites-available/000-default-le-ssl.conf
Congratulations! You have successfully enabled HTTPS on https://ドメイン名

自動更新のタスクもこの処理で設定されるので、特にこれ以上することはないが、念のため下記のコマンドで自動更新のテストが可能となっている。

sudo certbot renew --dry-run

Ubuntu Server 22.04 LTSをインストールする

目的

開発用のサーバーをUbuntu Server 22.04で再構築する。

Ubuntu Serverのインストール

下記からISOファイルをダウンロードして、USBフラッシュドライブに書き込む。
https://jp.ubuntu.com/download

SSHのインストールのチェックを入れるのを忘れずに、他はインストーラーの指示通りに入力する。

MariaDBのインストール

ここではインストールのみ行い、設定は後程行う。

sudo apt install mariadb-server
sudo mysql_secure_installation

Apache、phpのインストール

phpを使用することを前提としたインストールと、設定を行う。

sudo apt install apache2
sudo a2enmod rewrite
sudo a2enmod ssl
sudo a2ensite default-ssl
sudo apt install php libapache2-mod-php php-fpm php-common php-mbstring php-xmlrpc php-gd php-xml php-mysql php-cli php-zip php-curl php-imagick php-intl

.htaccesによる、設定の上書きを有効にする。

sudo vi /etc/apache2/apache2.conf

<Directory /var/www/>
	Options Indexes FollowSymLinks
	AllowOverride all
	Require all granted
</Directory>

sudo service apache2 restart

Webminのインストール

sudo apt install wget apt-transport-https software-properties-common

wget https://download.webmin.com/jcameron-key.asc
cat jcameron-key.asc | gpg --dearmor >jcameron-key.gpg
sudo cp jcameron-key.gpg /etc/apt/trusted.gpg.d/jcameron-key.gpg

sudo apt update
sudo apt install webmin
sudo /usr/share/webmin/changepass.pl /etc/webmin ユーザー名 パスワード

日本語設定

sudo apt install language-pack-ja
localectl list-locales | grep ja
sudo localectl set-locale LANG=ja_JP.UTF-8

Tomcat 9のインストール

sudo apt install tomcat9 tomcat9-admin

Jenkins運用のため、/etc/tomcat9/context.xmlに下記を追記する。

    <Resources cacheMaxSize="102400" />

/etc/tomcat9/server.xmlのAJP/1.3の設定を下記の通り変更する。

    <Connector protocol="AJP/1.3"
               address="localhost"
               port="8009"
               redirectPort="8443"
               secretRequired="false" />

/etc/tomcat9/tomcat-users.xmlに下記を追記する。

  <role rolename="manager-script"/>
  <user username="ユーザー名" password="パスワード" roles="manager-script"/>

/usr/lib/systemd/system/tomcat9.serviceに下記を追記する。

Environment="CATALINA_OPTS=-DJENKINS_HOME=/opt/jenkins/"
ReadWritePaths=/opt/jenkins/

/etc/tomcat9/policy.d/50local.policyに下記を追記する。

grant codeBase "file:${catalina.base}/webapps/jenkins/-" {
    permission java.security.AllPermission;
};
grant codeBase "file:/opt/jenkins/-" {
    permission java.security.AllPermission;
};
sudo systemctl daemon-reload
sudo service tomcat9 restart

Jenkinsのインストール

/opt/jenkinsフォルダを作成し、フォルダにTomcatから書き込み可能な権限を付与する。

jenkins.warファイルをダウンロードし、/var/lib/tomcat9/webapps/に配置する。

ApacheとTomcatの連携の設定

sudo a2enmod proxy
sudo a2enmod proxy_ajp

/etc/apache2/conf-enabled/tomcat.confを作成し、下記の通り記載する。

ProxyPass /jenkins/ ajp://localhost:8009/jenkins/
ProxyPassReverse /jenkins/ ajp://localhost:8009/jenkins/
sudo service apache2 restart

Postfixのインストール

こちらのページにインストールと設定方法を記載した。

Vue Composition APIを試す

Composition APIを使用すれば、同じ論理的な関心事に関連するコードを並べることが出来る?可読性が良くなる?良くわからないけど、前にVue 2で書いたコードを書き換えてみる。

このドキュメントでは実際にどのように変更したかをメインに記載する。

ログイン画面(Login.vue)の例

・Vue 2
一見シンプルに見えるけれども、thisで参照するものが多すぎて、何が使えて、何が使えないのか、後から見るとわかりづらい。

<script>
import {AUTH_REQUEST} from '@/store/actions/auth'

export default {
  data () {
    return {
      username: '',
      password: '',
    }
  },
  methods: {
    login: function () {
      const { username, password } = this
      this.$store.dispatch(AUTH_REQUEST, { username, password }).then(() => {
        this.$router.push(this.$route.query.redirect || '/')
      }).catch(error => {
        this.$message({
          showClose: true,
          message: error,
          type: 'error'
        })
      })
    }
  }
}
</script>

・Vue 3.2
Vue 2で必要だった独特な書き方がなくなって、一般的なJavaScriptの書き方になっている。インポート、宣言部、関数とわかれていて、わかりやすくなっているように感じる。

<script setup>
import { reactive } from 'vue'
import { useStore } from 'vuex'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { AUTH_REQUEST } from '@/store/actions/auth'

const store = useStore()
const router = useRouter()
const route = useRoute()

const state = reactive({
  username: '',
  password: ''
})

function login() {
  const { username, password } = state
  store.dispatch(AUTH_REQUEST, { username, password }).then(() => {
    router.push(route.query.redirect || '/')
  }).catch(error => {
    ElMessage({
      message: error,
      grouping: true,
      type: 'error'
    })
  })
}
</script>

Raspberry Pi 4 Model Bを設定する

やりたいこと

Raspberry Pi OS動かして、デスクトップ画面を表示する。

Raspberry Pi 4を購入する

今は品薄で、なかなか手が入らない。仕方がないので必要なものが揃っている下記を購入した。

Raspberry Pi OSをインストールする

下記からRaspberry Pi Imagerをダウンロードしてインストールする。
https://www.raspberrypi.com/software/

SDカードを挿入し、Raspberry Pi Imagerを起動して、OS選択し下記の通り実行する。

OSをインストールしたSDカードをRaspberry Piに挿入し電源を入れる。

※4/21追記
コンパクトなキーボードとマウスが欲しかったので下記を購入した。USBレシーバーも付属しているので、互換性を気にしなくて良く、すぐ使用できる点が気に入った。

Unreal Engine 5をインストールしてCity Sampleを見てみたい

Unreal Engine 5のCity Sampleとは何か

The Matrix Awakens: An Unreal Engine 5 Experienceの技術デモはPlayStation 5 と Xbox Series X/Sでしか公開されていなかったが、Unreal Engine 5が先日リリースされ、この都市シーンをPC上で動かしてみることが出来るようになった。(ただし、Matrix関係のコンテンツは含まれない)

City Sample をインストールする

Epic Gamesから、Unreal Engine 5をダウンロードし、インストールする。(Epic Games Launcherが必要になる)

Unreal EngineのマーケットプレイスからCityサンプルを探して入手する。

ライブラリのマイダウンロードにあるCityサンプルから、プロジェクトを作成する。

Cityサンプルの容量は約93GB程あるので、ダウンロードに時間がかかる。

Unreal Engineは全く触ったことがないので、サンプル起動後Small Cityをロードしてみた。

プレビューで動かすことは出来たけど、この後パッケージするにはどうしたら良いのだろう。

※追記:パッケージ化出来るようになった。
1.勝手に日本語化されていたタイトルの2バイト文字を1バイト文字に変更(Cityサンプル→CitySample)。
2.下記から.NET Core 3.1 ランタイムをダウンロードしてインストール。
https://aka.ms/dotnet-core-applaunch?framework=Microsoft.WindowsDesktop.App&framework_version=3.1.0&arch=x64&rid=win10-x64

Spring Boot 2.6でHibernate Search 6を動かす

依存関係

implementation 'org.hibernate.search:hibernate-search-mapper-orm:6.1.4.Final'
implementation 'org.hibernate.search:hibernate-search-backend-lucene:6.1.4.Final'

実装

Entityクラスに@Indexed、メソッドに@FullTextFieldを追加する。

@Entity
@Table(name = "novel")
@Indexed
public class Novel implements Serializable {

...

    @FullTextField
    private String title;

Serviceクラスに検索処理のロジックを追加する。

@Service
public class NovelService {

...

    @Transactional
    public Stream<Novel> searchIndex(final String searchParameters) {
        SearchSession searchSession = Search.session(entityManager);

        SearchResult<Novel> result = searchSession.search(Novel.class)
                .where(f -> f.bool()
                        .must(f.match()
                                .field("title")
                                .matching("異世界")))
                .fetchAll();
        return result.hits().stream();
    }

実際に動くコードはGitHubに公開予定。公開完了したら、下記に追記する。

※4/22追記
NovelService.java – github

public Stream<Novel> searchIndex(final String searchParameters) {
    SearchSession searchSession = Search.session(entityManager);
    String operationSetExper = String.join("|", SearchOperation.SIMPLE_OPERATION_SET);
    Pattern pattern = Pattern.compile(
            "(\\p{Punct}?)(\\w+?)(" + operationSetExper + ")(\\p{Punct}?)(\\w+?)(\\p{Punct}?),",
            Pattern.UNICODE_CHARACTER_CLASS);
    Matcher matcher = pattern.matcher(searchParameters + ",");

    SearchResult<Novel> result = searchSession.search(Novel.class)
            .where(f -> f.bool(b -> {
                b.must(f.matchAll());
                while (matcher.find()) {
                    b.must(f.match().field(matcher.group(KEY))
                            .matching(matcher.group(VALUE)));
                }
            }))
            .fetchAll();

    return result.hits().stream();
}