Ubuntu 22.04のVPSにStrapiをインストールする

Strapiは、オープンソースのヘッドレスCMS。ヘッドレスCMSとは、ヘッド(View)を持たないCMSのこと。CMSとはContent Management Systemの略で、Webサイトのコンテンツを管理できるシステムのこと。Next.jsでWebサイトをつくるにあたって、コンテンツの管理をヘッドレスCMSを使ってやってみたいなというのがモチベーションです。

ヘッドレスCMSにもContentfulとかmicroCMSとかいろいろあって、Strapiにも環境を用意する手間なく使えるStrapi Cloudというクラウド版もあるようだけど、じぶんでホストすれば無料で利用できるとのことなので、Ubuntu 22.04で動かしたConoha VPSにStrapiをインストールしたいと思います。

そもそもVPSをUbuntu 22.04でセットアップしたり、nginxを導入したここまでのあらすじは以下の記事を参照ください。

Node.jsをインストール

StrapiはJavaScriptで開発されているので、なにはともあれVPSにNode.jsをインストール。aptでインストールしてもいいのだけど、Node.jsのバージョンを管理したいので先にnodenvをインストールします。

GitHubのリポジトリからnodenvのソースコードをcloneしたあと、パスを指定してbashを再起動し、nodenvのプラグインのディレクトリへnode-buildのソースコードをcloneする。

$ git clone https://github.com/nodenv/nodenv.git ~/.nodenv
$ echo 'export PATH="$HOME/.nodenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(nodenv init -)"' >> ~/.bashrc
$ source ~/.bashrc
$ git clone https://github.com/nodenv/node-build.git $(nodenv root)/plugins/node-build

うまくインストールできたかどうかはnodenv-doctorで調べられるらしい。実行したらOKっていわれたのでだいじょうぶそう 🎉

$ curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-doctor | bash

nodenvでインストールできるバージョンを調べつつ、Node.jsのLTSをインストール(記事公開時点の情報です)。

$ nodenv install -l
$ nodenv install 18.16.0
$ nodenv global 18.16.0

$ node -v で 18.16.0 って表示されたらOK 🎉

リバースプロキシを設定する

VPSで起動したStrapiへWebからアクセスできるように、nginxでリバースプロキシを設定します。
まずは、既存のnginxの設定ファイルをコピーして、StrapiへアクセスできるURLの設定ファイルを用意し、リバースプロキシの設定を追記。

$ cd /etc/nginx/sites-available/
$ sudo cp shikakun.dev example.shikakun.dev
$ sudo vim example.shikakun.dev
upstream strapi {
  server 127.0.0.1:1337;
}

server {
  listen 80;
  listen [::]:80;

  server_name example.shikakun.dev;

  location / {
    return 301 https://$server_name$request_uri;
  }
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name example.shikakun.dev;

  ssl_certificate /etc/letsencrypt/live/shikakun.dev/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/shikakun.dev/privkey.pem;
  ssl_session_tickets on;
  ssl_protocols TLSv1.2;
  ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  location / {
    proxy_pass http://strapi;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_pass_request_headers on;
  }
}

作成した設定ファイルへsites-enabledからシンボリックリンクを貼り、nginxを再起動。

$ sudo ln -s /etc/nginx/sites-available/example.shikakun.dev /etc/nginx/sites-enabled/example.shikakun.dev
$ sudo systemctl restart nginx

Strapiをインストール

Strapiをインストールするフォルダを用意。

$ cd /var/www/
$ sudo mkdir example.shikakun.dev
$ sudo chown -R $USER:$USER /var/www/example.shikakun.dev

create-strapi-appを実行して、対話型インターフェースに回答しながら好みの仕様でStrapiをインストールします。データベースはとりあえずsqliteにしておきました。

$ npx create-strapi-app@latest example.shikakun.dev
? Choose your installation type Custom (manual settings)
? Choose your preferred language JavaScript
? Choose your default database client sqlite
? Filename: .tmp/data.db

Strapiのソースコードをビルドして、起動!

$ cd example.shikakun.dev
$ NODE_ENV=production npm run build
$ node /var/www/example.shikakun.dev/node_modules/.bin/strapi start

う、動いた〜! https://example.shikakun.dev/admin でアクセスできるようになりました 🎉

使うたびにStrapiを起動するのは大変なので、 pm2 を利用してデーモン化します。ということで、インストールしてシェルを再起動。

$ cd ~
$ npm install pm2@latest -g
$ source ~/.bashrc

設定ファイルを用意します。sqliteではなくデータベースのサーバーを用意する場合はenvのところに設定を指定するみたい。

$ vim ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'strapi',
      cwd: '/var/www/example.shikakun.dev',
      script: 'npm',
      args: 'start',
      env: {
        NODE_ENV: 'production',
      },
    },
  ],
};

保存したらpm2を実行。

$ pm2 start ecosystem.config.js
$ pm2 startup

[PM2] To setup the Startup Script, copy/paste the following command: のあとに、実行してねってコマンドが出力されるので、それをコピーペーストして実行!
$ pm2 list で登録されたかどうか確認できる。無事にデーモン化できました 🎉

Apacheをやめてnginxにする

Conoha VPSにUbuntu 22.04をインストールして、ApacheにPassengerというモジュールを組み込んでSinatraで書いたアプリケーションを動かしてました。PHPみたいにサーバーへファイルを置くだけで動くのは手軽で便利だったのだけど、勉強としていろいろ試してみようということでnginx + Pumaの構成へ変更してみます。

Apacheをアンインストール

$ sudo systemctl stop apache2
$ sudo apt-get remove apache2

$ apache2ctl -v で、コマンドがありませんと返されたらOK 🎉

設定ファイルはあとで見たくなるかも… と思って、ひとまずそのままにしておきます。
消したいときは $ sudo apt-get purge apache2 で消せるとのこと。

いったんポートも閉じておきます。

$ sudo ufw delete allow "Apache"
$ sudo ufw delete allow "Apache Secure"

$ sudo ufw status でルールが消えていることも確認。

nginxをインストール

$ sudo apt-get update
$ sudo apt install nginx

$ nginx -v で、バージョン情報が返されたらOK 🎉
OSを起動したらnginxも起動するように設定しておく。

$ sudo systemctl enable nginx

ポートを開きます。

$ sudo ufw allow "Nginx Full"

$ sudo ufw status でルールが増えていることを確認。

ここでブラウザで http://{サーバーのIPアドレス} へアクセスしてみると、nginxのデフォルトのHTMLファイルが表示されました 🎉

nginxの設定ファイルを作成

サーバーのIPアドレスではなくドメインで表示できるようにしたいし、バーチャルホストを設定したい。というわけで、nginxの設定ファイルを作成します。

設定ファイルには /etc/nginx/sites-available/etc/nginx/sites-enabled ってディレクトリがあって、 sites-available に書いた設定ファイルのうち有効にしたいやつを sites-enabled からシンボリックリンクを貼る… という運用だと理解しました(合ってますか?)。まずは sites-available にファイルを作成します。

$ sudo vim /etc/nginx/sites-available/shikakun.dev
server {
  listen 80;
  listen [::]:80;

  root /var/www/shikakun.dev;
  index index.html index.htm index.nginx-debian.html;

  server_name shikakun.dev;

  location / {
    try_files $uri $uri/ =404;
  }
}

作成したファイルへ sites-enabled から sites-available へシンボリックリンクを貼る。

$ sudo ln -s /etc/nginx/sites-available/shikakun.dev /etc/nginx/sites-enabled/shikakun.dev

サーバー名を追加することで「ハッシュバケットメモリの問題」なるものが発生する可能性があるとのことで、よく理解していないのですが /etc/nginx/nginx.conf に書かれている server_names_hash_bucket_size の設定をコメントアウトを外して保存。
参考: Ubuntu 20.04にNginxをインストールする方法 | DigitalOcean

$ sudo vim /etc/nginx/nginx.conf
- #server_names_hash_bucket_size 64;
+ server_names_hash_bucket_size 64;

ここまででnginxの設定は完了。念のため $ sudo nginx -t を実行すると、設定ファイルのシンタックスに問題がないか確認できて便利。
nginxを再起動!

$ sudo systemctl restart nginx

/var/www/shikakun.dev/index.html に適当な内容のHTMLファイルを置いておくと、ブラウザから http://shikakun.dev/ でアクセスできるようになりました 🎉
ちなみにChromeは .dev のトップレベルドメインのサイトは自動でhttpsプロトコルでアクセスする仕様になっているそうで、Firefoxで確認しました。

常時SSL化

Let's Encryptで取得したワイルドカードのSSL証明書をnginxで使います。SSL証明書を取得した手順は以下の記事に書いたので省略します。

さきほど作成したnginxの設定ファイルを開いて、ポート番号の80へアクセスしたら443でアクセスするようにリダイレクトを設定し、443でアクセスされたらSSL証明書を読み込むように設定。

$ sudo vim /etc/nginx/sites-available/shikakun.dev
server {
  listen 80;
  listen [::]:80;

  server_name shikakun.dev;

  location / {
    return 301 https://$server_name$request_uri;
  }
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name shikakun.dev;

  root /var/www/shikakun.dev;
  index index.html index.htm index.nginx-debian.html;

  ssl_certificate /etc/letsencrypt/live/shikakun.dev/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/shikakun.dev/privkey.pem;
  ssl_session_tickets on;
  ssl_protocols TLSv1.2;
  ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  location / {
    try_files $uri $uri/ =404;
  }
}

$ sudo systemctl restart nginx でnginxを再起動すると、ブラウザから https://shikakun.dev/ でアクセスできるようになりました 🎉

SinatraアプリをPumaで動かす

Pumaで起動できるようにSinatraアプリのコードに手を加えます。
GemfileにPumaを追記。

gem "sinatra"
gem "puma"

app.rb にpumaで起動するように設定。

set :server, :puma

Sinatraアプリのルートディレクトリに、以下のような内容で puma.rb ってファイルを追加。
なにもわからないので Gistで @ctalkington さんが共有されていたコード をそのまま使用させていただきました。ありがとうございます。自分のアプリや環境に応じてチューニングしていきたいです。

root = "#{Dir.getwd}"

bind "unix://#{root}/tmp/puma/socket"
pidfile "#{root}/tmp/puma/pid"
state_path "#{root}/tmp/puma/state"
rackup "#{root}/config.ru"

threads 4, 8

activate_control_app

手を加えたSinatraアプリのコードをVPSへアップロードしたあと、アプリのルートディレクトリへ移動してGemをインストール。

$ cd /var/www/example-sinatra-app.shikakun.dev
$ bundle config set path "vendor/bundle"
$ bundle install

pumaを実行した際に作成されるファイルを置くディレクトリを用意しておく。

$ mkdir -p tmp/puma

ここからnginxの設定をします!
まずは、ここまでに作成した設定ファイルをコピーして、Sinatraアプリの設定ファイルを用意。

$ cd /etc/nginx/sites-available
$ sudo cp shikakun.dev example-sinatra-app.shikakun.dev
$ sudo vim example-sinatra-app.shikakun.dev
upstream app {
  server unix:///var/www/example-sinatra-app.shikakun.dev/tmp/puma/socket;
}

server {
  listen 80;
  listen [::]:80;

  server_name example-sinatra-app.shikakun.dev;

  location / {
    return 301 https://$server_name$request_uri;
  }
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name example-sinatra-app.shikakun.dev;

  root /var/www/example-sinatra-app.shikakun.dev/public;

  access_log /var/www/example-sinatra-app.shikakun.dev/log/nginx.access.log;
  error_log /var/www/example-sinatra-app.shikakun.dev/log/nginx.error.log info;

  ssl_certificate /etc/letsencrypt/live/shikakun.dev/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/shikakun.dev/privkey.pem;
  ssl_session_tickets on;
  ssl_protocols TLSv1.2;
  ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  location / {
    try_files $uri @puma;
  }

  location @puma {
    include proxy_params;

    proxy_pass http://app;
  }
}

sites-enabledへ設定ファイルへのシンボリックリンクを貼って、nginxを再起動。

$ sudo ln -s /etc/nginx/sites-available/example-sinatra-app.shikakun.dev /etc/nginx/sites-enabled/example-sinatra-app.shikakun.dev
$ sudo systemctl restart nginx

Sinatraアプリのルートディレクトリへ移動してpumaを起動。

$ cd /var/www/example-sinatra-app.shikakun.dev
$ bundle exec puma --config puma.rb

https://example-sinatra-app.shikakun.dev でSinatraアプリが動きました 🎉

Pumaの起動をデーモン化

わ〜い!と無事に動いて喜んだものの、このままだとSSH接続を切るとSinatraアプリが終了してしまうので、デーモン化しておきます。以前は puma.rbdaemonize って書けばデーモン化できたようなのだけど、最近のバージョンではデーモン化はOSに任せるべきという方針から廃止されたらしい。なるほど。ということで、systemdでデーモン化することに。

PumaのGitHubのリポジトリにある設定例 を参考に、以下のように書いてみました。

$ sudo vim /etc/systemd/system/example-sinatra-app.shikakun.dev.service
[Unit]
Description=Puma HTTP Server for example-sinatra-app.shikakun.dev
After=network.target

[Service]
Type=simple
WorkingDirectory=/var/www/example-sinatra-app.shikakun.dev
ExecStart=/home/shikakun/.rbenv/shims/bundle exec puma --config /var/www/example-sinatra-app.shikakun.dev/puma.rb
Restart=always

[Install]
WantedBy=multi-user.target

保存したら、以下のコマンドを実行して有効化。

sudo systemctl daemon-reload
sudo systemctl enable example-sinatra-app.shikakun.dev.service
sudo systemctl start example-sinatra-app.shikakun.dev.service
sudo systemctl status example-sinatra-app.shikakun.dev.service

SSH接続を切っても、サーバーを再起動しても、 https://example-sinatra-app.shikakun.dev でSinatraアプリが動きつづけるようになりました 🎉

MacBookを買ってウキウキ開発環境構築

突然ですが、M2 MacBook Air(CPU 8コア, GPU 8コア, メモリ 16GB, SSD 512GB)を買いました!ふつうに買うと220,800円という感じなのだけど、Apple Storeでおなじスペックの整備済製品があったので2万円ほど安く買えたのと、楽天リーベイツを経由して1,800ポイントもらえた。やったね。

使ってみてよかったことを簡潔に3つ紹介します。重量が1.24kgでメチャ軽いので、出張やちょっとした外でのミーティングなど、気軽にmacOSを外へ連れていけること。はじめてAppleのMなんとかチップのパソコンを触ったけど、メモリ16GBなのにじゅうぶんサクサクだったこと。あとは、体感できるくらい電池が長持ちなこと。30Wで出力するモバイルバッテリーでも充電できるのはうれしいね。

さて、そんな新しいパソコンを買ってウキウキな気分でやるのが開発環境の構築ということで、今回やったことをまとめておこうと思います。

シェルの設定

シェルはZshを使っています。理由はmacOSのデフォルトだから…。Zshの設定のフレームワークにはずっとPreztoを使っていたのだけど、ちょっとばかし起動が遅いのが気になっていて、この機会にZimを試してみることに。起動がちょ〜はやい。テーマの設定は .zimrc ファイルに zmodule eriner と書くだけでいい感じの見た目に。フォントはRicty for PowerlineをインストールしてiTerm 2のプロファイルで設定(でも、Rictyは「サポート終了」したそうなので、代替のフォントを選んだほうがいいのかもしれません)。

Sixeightさんの記事を参考に、設定ファイルもホームディレクトリ直下ではなく ~/.config ディレクトリにまとめてみました。XDG Base Directory Specificationなんてディレクトリ構造の仕様があるって知らなかったなあ。

.config
├── git
│   ├── config
│   └── ignore
└── zsh
    ├── .zim
    ├── .zimrc
    ├── .zprofile
    ├── .zsh_history
    ├── .zshenv
    └── .zshrc

/etc/zshenvZDOTDIR=$HOME/.config/zsh と書いたうえで ~/.config/zsh/.zshenv で環境変数を設定しておく。

export XDG_CONFIG_HOME="$HOME"/.config
export XDG_CACHE_HOME="$HOME"/.cache
export XDG_DATA_HOME="$HOME"/.local/share
export XDG_STATE_HOME="$HOME"/.local/state

~/.config/zsh/.zshrc には、おなじみのaliasの設定など。

alias g='git'
alias ga='git add'
alias gc='git commit -m'
alias gca='git commit --amend'
alias gcaa='git commit --amend --no-edit'
alias gce='git commit --allow-empty -m'
alias gd='git diff'
alias gdd='git diff --cached'
alias gs='git status -sb'
alias mm='git-checkout-default-branch'
alias o='open'
alias pwdd='open -a iTerm .'
alias z='exec $SHELL -l'

git-checkout-default-branch() {
  local BRANCH=`git remote show origin | grep 'HEAD branch' | awk '{print $NF}'`
  git checkout $BRANCH && git pull && git pull origin $BRANCH
}

こだわりポイントとしては、 mm って打つと、Gitのどのブランチにいてもmasterやmainブランチへ移動して最新版をpullしてくれるコマンドをよく使ってます。あと pwd ならぬ pwdd って打つと、いま自分がいるディレクトリを新しいiTermのタブで開いてくれるコマンドも便利。もっといい書き方やツールがあるのかもしれませんが…。

プログラミング言語のインストール

複数の言語やプロジェクトごとに異なるバージョンを管理しやすくするために、anyenvを利用してます。ということで、まずanyenvとanyenv-updateをインストール。

$ brew install anyenv
$ echo 'eval "$(anyenv init -)"' >> ~/.zshrc
$ anyenv init
$ exec $SHELL -l
$ anyenv install --init
$ mkdir -p $(anyenv root)/plugins
$ git clone <https://github.com/znz/anyenv-update.git> $(anyenv root)/plugins/anyenv-update

そのうえでNode.jsをインストール。

$ anyenv install nodenv
$ exec $SHELL -l
$ anyenv update
$ nodenv install -l
$ nodenv install 18.16.0 #記事公開時のLTSの最新バージョンです
$ nodenv global 18.16.0

あとRubyもインストール。

$ anyenv install rbenv
$ exec $SHELL -l
$ anyenv update
$ rbenv install -l
$ rbenv install 3.2.2 #記事公開時の安定版の最新バージョンです
$ rbenv global 3.2.2

グラフィックツールの設定

Adobe Creative Cloudにも毎月お金を払ってるので料金の高額さに怒りながらインストールします(1ライセンスで2台までOKとのこと)。よく忘れるので設定をメモしておきます。なお、僕は印刷物をまったく作りません。

  • Photoshop
    • 環境設定 > ツール
      • ツールヒントを表示 をOFF
    • 環境設定 > 単位・定規 > 単位
      • 定規文字pixel
    • 環境設定 > ガイド・グリッド・スライス > グリッド
      • グリッド線10 pixel
      • 分割数10
  • Illustrator
    • 環境設定 > 一般
      • キー入力1px
      • ツールヒントを表示 をOFF
    • 環境設定 > 単位
      • すべて ピクセル

あとは思い出したら追記しま〜す!