けけずんセルフハッキング

エンジニアっぽい雰囲気を醸しだしているかのようなブログです!

/bin/launchctl で色々と自動で起動する設定する

はじめに

よく分からんコマンド調べてみようシリーズ。

kkznch.hatenablog.com

今回は /bin/launchctl について。

/bin/launchctl

名前から推測するに CentOS でいう systemctl みたいな、 OS 起動時に自動でサービスの起動を設定する奴かね。 ということで man launchctl したときの DESCRIPTION の内容はこちら。

launchctl interfaces with launchd to manage and inspect daemons, agents and XPC services.

launchctl はデーモンやエージェント、XPC サービスの管理や検査をする launchd のインターフェース。よく分からない。

念の為 launchd についても man launchd してみた。

launchd manages processes, both for the system as a whole and for individual users.

launchd はシステム全体と個別のユーザのプロセスを管理するらしい。 なるほど、launchctl は launchd をどうこうするためのコマンドということでしょうか、そういうことにしておきましょう。

/bin/launchctl 実行してみる

説明だけだとよく分からんので動かそう。

plist ファイルを書く

何を実行するのか、どの間隔で実行するのか、などといった様々なオプションを plist 形式のファイルに書く。 今回は 10 秒間隔で echo するだけの test.plist というファイルを作成してみた。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
 <dict>
  <key>Label</key>
  <string>test</string>
  <key>ProgramArguments</key>
  <array>
     <string>echo</string>
     <string>Hello,World!</string>
  </array>
  <key>StandardOutPath</key>
  <string>/path/to/output.log</string>
  <key>StartInterval</key>
  <integer>10</integer>
 </dict>
</plist>

ロードと起動

作成後は以下のコマンドで、フルパスで test.plist を指定してロードする。

$ launchctl load /path/to/test.plist

この時点ではまだ起動はしていない。 ロードした時点で起動するオプションもあるが、今回はないので手動で起動する。 起動時には test.plist の Label キーで指定した値(今回は test )を引数に渡す。

$ launchctl start test

これで完了。 このあと少し時間をおくと以下のように /path/to/output.logHello, World! が出力されているはず。

$ cat /path/to/output.log
Hello,World!
Hello,World!
Hello,World!

停止とアンロード

止める際は以下のコマンドで止めて、 unload してあげる。

$ launchctl stop test
$ launchctl unload /path/to/test.plist

以上。

おわりに

これは今後自分で plist を書いて何かを動かすということはあるのだろうか。 Linux とかだとなんとなく使い道は分かるが、mac だと何をするねん。

plist の書き方だったり細かいオプションは参考リンク先を見てね。

参考リンク

qiita.com www.minimalab.com

/bin/pax でファイルのアーカイブする

はじめに

久しぶりのよく分からんコマンド調べてみようシリーズ。

kkznch.hatenablog.com

今回は /bin/pax について。

/bin/pax

名前から推測するに、なんとなくパッキングしてくれそうな雰囲気を感じる。 ただ全く使ったことないし使ってる人見たことない、一体こいつは何者だ。

ということで man pax したときの DESCRIPTION の内容はこちら。

pax will read, write, and list the members of an archive file and will copy directory hierarchies.

pax は圧縮ファイルの読み書きや一覧、ディレクトリ構造のコピーをするらしい。

tar とか zip みたいに圧縮解凍ができるコマンドという認識でいいのかねこれは?と思って調べてみると微妙に違うっぽい。 tar とか zip は圧縮ファイルに対する操作で、 pax はアーカイブファイルに対する操作ができるのかも。 圧縮ファイルとアーカイブファイルについての違いは以下を参照してくれ。

linuxfan.info

/bin/pax 実行してみる

以下のサイトで色々試されている。

qiita.com

基本的な構文は以下の通り。

# ファイルをアーカイブする
$ pax -w 入力元

# アーカイブされたファイルを展開する
$ pax -r 入力元

なるほどそんなに難しくなさそう。 以下のファイルがある状態でアーカイブと展開をそれぞれやってみる。

$ ls -1
fuga.txt
hoge.txt
piyo.txt

ファイルをアーカイブする

まずはアーカイブから。

$ pax -w *
fuga.txt0000644 0000765 0000024 00000000017 13644250623 0013557 0ustar00kkznchstaff0000000 0000000 fuga
fuga
fuga
hoge.txt0000644 0000765 0000024 00000000017 13644250612 0013555 0ustar00kkznchstaff0000000 0000000 hoge
hoge
hoge
piyo.txt0000644 0000765 0000024 00000000017 13644250631 0013614 0ustar00kkznchstaff0000000 0000000 piyo
piyo
piyo

なんか出力された。 ファイルの内容とかメタ情報っぽいのが出力されているっぽい。

ちなみに標準出力に出力されているだけなので、上記のコマンドだとアーカイブファイルは作成されない。 アーカイブファイルを作成したい場合はリダイレクトさせる。

$ pax -w * > archive
$  ls -1
archive
fuga.txt
hoge.txt
piyo.txt

標準出力には何も出力されず、archive というファイルが新たに作成される。

アーカイブされたファイルを展開する

では次にアーカイブしたファイルを展開してみよう。 展開時にフォルダを指定して展開することは可能らしいが、ちょっと面倒なので別のフォルダを作成して archive を移動し、そこで展開する。

$ mkdir test
$ mv archive test

この状態で展開用のコマンドを実行する。

$ pax -r < archive

ここで ls を実行すると、アーカイブされていたファイルが展開でできていることが確認できる。

$ ls -1
archive
fuga.txt
hoge.txt
piyo.txt

展開できているっぽい。

ちなみに pax で作ったアーカイブファイルのファイルサイズは 10KB で、zip で圧縮した場合のファイルサイズは 481B だった。 こんなに違うのか、アーカイブファイルの使いみちが最早よく分からん。

Laravel でプロジェクトを作成する際の雛形作った

概要

開発案件が始まる度に Docker Compose の設定や OpenAPI の設定を行うのが面倒だったので、雛形を作って GitHub にあげておいた。

github.com

内容

以下の内容を含んでいる。

  • ドキュメント類
    • OpenAPI
      • API 仕様書をいい感じに表示してくれる
    • k1LoW/tbls
      • データベースからいい感じにテーブル設計書を出力してくれる
  • 開発環境用 Docker

使い方

ほれ README じゃ、これ読め。

終わりに

この程度の雛形でも、残しておくと後で便利になったりする。 他にも良い感じの構成とかあれば教えてほしい。

SPA な Web アプリを継続的デリバリーする環境を作る (4) 〜 継続的デリバリー( GitLab CI/CD )編

概要

SPA な Web アプリを継続的デリバリーする環境を作るシリーズ最終回、継続的デリバリー編。 タイトルに継続的デリバリーというワードが二つ入ってるのが少しアホっぽく見える。 ていうか継続的デリバリーって自分で書いてて思ったけど、こんな言葉普段使わんからパッと見て何言ってんだコイツって思われてそう。

序章はこちら。

kkznch.hatenablog.com

全4部構成でお届けする。

長い道のりだったが、これが最終回じゃよ。

ハンズオンするぞ

バックエンド用リポジトリ、フロントエンド用リポジトリに GitLab CI/CD の設定ファイルを作成し、それぞれに合わせた設定を記述していく。

今回はテストの作成はしていないけど、実際の開発では CI/CD 実行中にテストもしていたりする。 なので、もしテストをしたいのであれば CI/CD 設定ファイル中にあるテスト用のステージを編集してね。

バックエンドの自動デプロイ設定

流れ的にはこんな感じのことをする。

f:id:kkznch:20200224141650p:plain

CI/CD 設定ファイルの作成

バックエンド用リポジトリ 直下に以下の内容で GitLab CI/CD 設定ファイルを作成する。 ファイル名の頭は . から始まっているので付け忘れないようにね。

.gitlab-ci.yml

stages:
  - test
  - deploy

# テスト
job-test:
  stage: test
  image: php:7.3-alpine
  before_script:
    - apk --no-cache update && apk --no-cache upgrade
    - apk --no-cache add zip unzip
    - docker-php-ext-install bcmath pdo_mysql
    - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
    - php composer-setup.php
    - php -r "unlink('composer-setup.php');"
    - php composer.phar install
  script:
    - echo "ここでテストの実行するよ"
  artifacts:
    paths:
      - composer.phar

# デプロイ
job-deploy:
  stage: deploy
  image: php:7.3-alpine
  before_script:
    - apk --no-cache add openssh-client
    - eval $(ssh-agent -s)
    - cat ${SSH_PRIVATE_KEY} | ssh-add -
    - mkdir -p ~/.ssh
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >~/.ssh/config
    - php composer.phar global require laravel/envoy
  script:
    - ~/.composer/vendor/bin/envoy run deploy
  when: manual
  only:
    - master

SSH 接続のために GitLab に秘密鍵の保存

次に GitLab から EC2 へ SSH 接続する際に使用される秘密鍵を GitLab CI/CD の Variables に登録する。 秘密鍵は EC2 を作成した際にダウンロードしたキーペア app-keypair-ec2.pem なので、こいつの中身を出力してクリップボードにコピーしておく。

$ cat ~/app-keypair-ec2.pem
# 出力される値をコピーしよう

GitLab で バックエンド用リポジトリ のページを開きサイドバーから 「Settings」 → 「CI / CD」をクリックする。 遷移先に Variables という項目があるので、その中で環境変数を登録する。 内容は以下の通り。

  • Type:File
  • Key:SSH_PRIVATE_KEY
  • Valueapp-keypair-ec2.pem の中身

スクランナーの作成

バックエンド用リポジトリ直下に SSH 接続先の設定や接続後の処理の内容が記述された Envoy.blade.php を作成する。 ELASTIC_IPAWS の Elastic IP で取得した IP アドレスな。

Envoy.blade.php

@servers(['ec2' => 'ec2-user@ELASTIC_IP'])

@setup
  $appDir = '/app'
@endsetup

@story('deploy', ['on' => 'ec2'])
  task-setup
  task-deploy
@endstory

@task('task-setup')
  cd {{ $appDir }}
  git pull origin master
  composer install --no-dev
@endtask

@task('task-deploy')
  cd {{ $appDir }}
  php artisan down
  echo "DB使ってたらここで migrate とかする"
  php artisan up
@endtask

コミットとプッシュ

ここまででバックエンド用リポジトリの自動デプロイの設定が完了した。 以下のコマンドでコミットしてプッシュしよう。

$ git add .
$ git commit -m "third commit"
$ git push -u origin master

プッシュ後に GitLab CI/CD の画面を見ると CI/CD が動いていることが確認できるはず。 今後はプッシュするたびに CI/CD が動いて自動でデプロイされるようになる。

フロントエンドの自動デプロイ設定

流れ的にはこんな感じのことをする。

f:id:kkznch:20200224141821p:plain

フロントエンド用リポジトリ 直下に以下の内容で GitLab CI/CD 設定ファイルを作成する。 ファイル名の頭は . から始まっているので気をつけよう。

stages:
  - test
  - build
  - deploy

# テスト
job-test:
  stage: test
  image: node:12-alpine
  script:
    - npm install
    - echo "ここでテストの実行するよ"

# ビルド
job-build:
  stage: build
  image: node:12-alpine
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - dist/
  only:
    - master

# デプロイ
job-deploy:
  stage: deploy
  image: python:alpine
  dependencies:
    - job-build
  variables:
    AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
    AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
    AWS_DEFAULT_REGION: ap-northeast-1
    S3_BUCKET: app-bucket
  script:
    - pip install awscli --upgrade --user
    - export PATH=~/.local/bin:$PATH
    - aws s3 sync --delete dist/ s3://${S3_BUCKET}/
  only:
    - master

S3_BUCKET: app-bucket には自分で作成した S3 バケットの名前を入れること。

S3 へアクセスするために GitLab に認証情報の保存

CI/CD 設定ファイル中の AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY の具体的な値をコード中に含めるのはよろしくない。 なので GitLab CI/CD の Variables という機能を使い、環境変数を GitLab に持たせる。

GitLab で フロントエンド用リポジトリ のページを開きサイドバーから 「Settings」 → 「CI / CD」をクリックする。 遷移先に Variables という項目があるので、その中で環境変数を登録する。 内容は以下の通り。

  • AWSのアクセスキーID
    • Key:AWS_ACCESS_KEY_ID
    • Valueaccess_key_id(S3 アクセス用ユーザのアクセスキー ID )
  • AWSのシークレットキー
    • Key:AWS_SECRET_ACCESS_KEY
    • Valuesecret_access_key(S3 アクセス用ユーザのシークレットアクセスキー)

access_key_idecret_access_keyAWS の IAM で作成した S3 アクセス用ユーザの認証情報が入るので、各自の環境に合わせて入力してくれ。

コミットとプッシュ

ここまででフロントエンド用リポジトリの自動デプロイの設定が完了した。 以下のコマンドでコミットしてプッシュしよう。

$ git add .
$ git commit -m "third commit"
$ git push -u origin master

プッシュ後に GitLab CI/CD の画面を見ると CI/CD が動いていることが確認できるはず。 今後はプッシュするたびに CI/CD が動いて自動でデプロイされるようになる。

おわりに

序章から始まり、バックエンド編、フロントエンド編、インフラ編、継続的デリバリー編の全四部構成でハンズオンを行った。 今回のハンズオンを通すことで、プロジェクトを立ち上げ、開発を行い、自動デプロイをする一連の流れ(環境)を一人で作れるようになっているはず。

説明を端折った箇所がいくつかあるので、分からないことがあれば Twitter などで気軽に聞いてくれ。

次回はインフラ部分をコード化したハンズオンだとか、 EC2 ではなく ECS にデプロイするハンズオンだとかをやっていきたい。

SPA な Web アプリを継続的デリバリーする環境を作る (3) 〜 インフラ( AWS )編

概要

SPA な Web アプリを継続的デリバリーする環境を作るシリーズ第三回、インフラ編。 序章はこちら。

kkznch.hatenablog.com

全4部構成でお届けする。

今回は長いぞ。

ハンズオンするぞ

AWS の画面からポチポチ操作してインフラを構築しよう。 そもそも AWS に登録してないという人は登録作業からやってくれ。 AWS 使いたくないという人は画面をそっと閉じよう。

今回は図中の AWS Cloud の枠内の構築をしていく。

f:id:kkznch:20200222125448p:plain

AWS でポチポチする際に何かしら項目への入力が求められたりするが、本記事で記述がないものに関してはデフォルト値を使用してくれ。 なお設定ミスって多額の料金を請求されても責任はとらんので、気をつけてね。

本当は環境の構築に CloudFormation または Terraform を使おうと思っていたんだけど、そこまでキャッチアップしきれていないのですまんな。 誰か代わりに書いて説明も含めて公開してくれ。

バックエンドのインフラ作るよ

バックエンドで動作するWeb サーバ、 APP サーバの構築をしていく。 一見サーバが二つあるかのように書いているが、その実は一つのサーバ内で Web サーバと APP サーバが動いている。 分けてもいいけど今は分ける必要ないので一つのサーバ内で動作させるよ。

VPC でネットワークの構築

心を無にして AWS のコンソールから VPC の画面を開き、各項目を以下の内容でポチポチ作っていこう。

  • VPC の作成
    • 名前タグ: app-vpc
    • IPv4 CIDR ブロック:10.0.0.0/16
  • サブネット の作成
    • 名前タグ:app-subnet
    • VPC:app-vpc
    • IPv4 CIDR ブロック:10.0.128.0/24
  • インターネットゲートウェイ の作成
    • 名前タグ:app-igw
    • 作成後は app-igw を選択し、「アクション」ボタンから app-vpc にアタッチする
  • ルートテーブル の作成
    • 名前タグ:app-route-table
    • VPC:app-vpc
    • 「ルート」タブから編集を開き、以下の項目を入力してルートを設定する
      • 送信先:0.0.0.0/0
      • ターゲット:app-igw
    • 「サブネットの関連付け」タブから編集を開き、app-subnet を関連付ける
  • Elastic IP の取得
    • IPv4アドレスプール:Amazonプール
    • ここで取得した IP アドレスを覚えておく(以下、この IP アドレスを ELASTIC_IP として記述する)

EC2 でサーバの構築

心を無にして AWS のコンソールから EC2 の画面を開き、各項目を以下の内容でポチポチ作っていこう。

  • EC2 の作成
    • AMI:Amazon Linux 2 AMI (HVM), SSD Volume Type
    • インスタンスタイプ:t2.micro
    • インスタンスの詳細の設定
      • ネットワーク:app-vpc
      • サブネット:app-subnet
    • タグの追加
      • キー:NAME
      • 値:app-ec2
    • セキュリティグループ
      • セキュリティグループ名:app-sg-ec2
      • 説明:app-sg-ec2
      • ルール
        • タイプに SSH, HTTP を設定し、ソースに 0.0.0.0/0 を設定する
    • キーペア
      • 「新しいキーペアの作成」を選択する
      • キーペア名:app-keypair-ec2
      • キーペアを 絶対にダウンロードしてね
  • Elastic IP で EC2 に IP を紐付け

ここまでの手順で外部から EC2 にアクセスすることができるようになる。

EC2 に SSH で接続する

ダウンロードしたキーペア app-keypair-ec2.pem をそのまま使って SSH 接続しようとすると鍵のパーミッションおかしいよって怒られるので、app-keypair-ec2.pem の権限を変更する必要がある。 便宜上、app-keypair-ec2.pem はホームディレクトリに置いてあるとする。

$ chmod 600 ~/app-keypair-ec2.pem

権限を変更したら ssh コマンドにオプションでキーペアを指定して実行する。 ELASTIC_IP は先程 AWS の Elastic IP で取得、EC2 に割り当てした IP アドレスだよ。

$ ssh ec2-user@ELASTIC_IP -i ~/app-keypair-ec2.pem

以降はローカルと EC2 どちらでコマンドを実行したかを区別するために、プロンプトの頭が $ ならローカルでの実行、 ec2$ なら EC2 での実行ということにする。

EC2 で Nginx と Laravel の実行環境の構築

SSH 接続ができたら EC2 内部でポチポチ作業していく。

yum の初期設定とアップデート

始めに yum が文字化けするので以下のように各ファイルを直して ~/.bashrc を読み込み直す。 /etc/locale.conf は編集する際に sudo コマンドで管理者権限を付与しないと保存できないはずなので、sudo vi /etc/locale.conf みたいな感じで vi を起動して編集しよう。

/etc/locale.conf

- LANG=en_US.UTF-8
+ LANG=ja_JP.UTF-8
+ LC_CTYPE=ja_JP.UTF-8

~/.bashrc は管理者権限がなくても大丈夫なのでそのまま編集しよう。

~/.bashrc

+ # 末尾に追加する
+ source /etc/locale.conf

上記二つのファイルを編集した後、以下のコマンドを実行して設定を反映させよう。

ec2$ source ~/.bashrc

次に yum をアップデートする。 yum でインストールやアップデートをする際は管理者権限がないと実行できないので、sudo を付けて実行しよう。

ec2$ sudo yum update -y

nginx のインストールと設定

yum のアップデート完了後は nginx をインストールして設定をする。 Amazon Linux 2 は amazon-linux-extras というコマンドを提供しているため、今回はそのコマンドを叩いて nginx のインストールを行う。

ec2$ sudo amazon-linux-extras install nginx1.12

インストール後は nginx の設定ファイルを以下のように編集しよう。

/etc/nginx/nginx.conf

- server {
-     listen       80 default_server;
-     listen       [::]:80 default_server;
-     server_name  _;
-     root         /usr/share/nginx/html;
-
-     # Load configuration files for the default server block.
-     include /etc/nginx/default.d/*.conf;
- 
-     location / {
-     }
- 
-     error_page 404 /404.html;
-         location = /40x.html {
-     }
- 
-     error_page 500 502 503 504 /50x.html;
-         location = /50x.html {
-     }
- }
+ server {
+     server_name localhost;
+     listen 80;
+     root /app/public;
+     index index.php index.html;
+ 
+     error_log  /var/log/nginx/error.log;
+     access_log /var/log/nginx/access.log;
+ 
+     location / {
+         try_files $uri $uri/ /index.php$is_args$args;
+     }
+
+     location ~ \.php$ {
+         fastcgi_split_path_info ^(.+\.php)(/.+)$;
+         fastcgi_pass unix:/var/run/php-fpm/www.sock;
+         fastcgi_index index.php;
+         include fastcgi_params;
+         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+         fastcgi_param PATH_INFO $fastcgi_path_info;
+     }
+ }

nginx から php-fpm へリクエストを流す的なことを書いている。 そもそも nginx と php-fpm がなんなんだよって人はこちらを参考にしてくれ。

qiita.com

php, php-fpm のインストールと設定

続いて php7.3 、Laravel に必要な php の拡張を EC2 にそれぞれインストールしていく。

ec2$ sudo amazon-linux-extras install php7.3
ec2$ sudo yum install -y php-bcmath php-mbstring php-xml

インストール後は php-fpm の設定ファイルを以下のように編集する。

/etc/php-fpm.d/www.conf

- user = apache
- user = apache
+ user = nginx
+ group = nginx    
- ;listen.owner = nobody
- ;listen.group = nobody
+ listen.owner = nginx
+ listen.group = nginx

git と composer のインストール

GitLab からリポジトリを取ってくるために git をインストールする。 yum でインストールするだけ。

ec2$ sudo yum install -y git

それと PHP のパッケージを管理するために composer をインストールする。 こいつは yum ではインストールできないので、公式の手順に沿ってインストールする。

ec2$ curl -sS https://getcomposer.org/installer | php
ec2$ sudo mv composer.phar /usr/local/bin/composer

ここまででバックエンドのインフラは準備できた。 後はバックエンド用リポジトリを手動でデプロイし、 Web サーバと APP サーバを起動してあげればよい。

バックエンド用リポジトリの手動デプロイ

EC2 から GitLab のリポジトリに公開鍵認証方式でアクセスするために、 EC2 上で鍵を作成して GitLab のリポジトリに公開鍵を設置する。 公開鍵認証がわからない人はここを見てくれ。

knowledge.sakura.ad.jp

鍵生成コマンドはこれ。

ec2$ ssh-keygen -t rsa -b 4096
# 入力を求められても全部エンターでオッケー

作った鍵を cat で出力してコピーし、GitLab のバックエンド用リポジトリから「Settings」 → 「Repositories」を開いて「Deploy Keys」の項目に公開鍵を貼り付ける。

ec2$ cat ~/.ssh/id_rsa.pub

git からバックエンド用リポジトリを clone し、/app にフォルダを移動する。

ec2$ cd ~/
ec2$ git clone git@gitlab.com:自分のユーザ名/app-backend.git
ec2$ sudo mv app-backend /app

/app に移動して env ファイルをコピーし、composer コマンドを実行する。 これと合わせてストレージのリンクや Laravel の中で使われる鍵の生成なども行う。

ec2$ cd /app
ec2$ cp .env.example .env
ec2$ composer install
ec2$ chmod -R 777 storage
ec2$ php artisan storage:link
ec2$ php artisan key:generate

ここまでで手動デプロイが完了したことになる。

nginx と php-fpm の起動、自動起動の設定

以下のコマンドで nginx と php-fpm を起動する。

ec2$ sudo systemctl start nginx
ec2$ sudo systemctl start php-fpm

ついでに nginx と php-fpm の自動起動を設定する。 これで EC2 が再起動した際に自動で nginx, php-fpm が立ち上がるようになる。

ec2$ sudo systemctl enable nginx
ec2$ sudo systemctl enable php-fpm

API の実行確認

ここまで進めると上手くいっていれば API が実行できるようになっているはず。 以下のコマンドを実行してくれ。 ELASTIC_IP は先程 AWS の Elastic IP で取得、EC2 に割り当てした IP アドレスよ。

ec2$ curl http://ELASTIC_IP/api

{"body":"Hello, World!","datetime":"2020-02-20T09:00:00.000000Z"}

上記のように値が返ってきていれば大丈夫。 返ってこない人は最初から一個ずつ確認して頑張ってくれ。

フロントエンドのインフラ作るよ

インフラっていうほど大それたものでもないが、フロントエンドのデプロイ先となる S3 バケットの作成をする。 S3 バケットにビルドした成果物を置き、公開設定と Web サイトのホスティング設定をするだけでユーザーが外部からアクセスすると SPA な画面を返すようになる。

S3 でバケットの作成

AWS で S3 の画面から S3 バケットを作成する。 なおバケット名はユニークである必要があるため、他と衝突しないバケット名を考えよう。

本記事では上記の名前で説明を進めていくが、おそらく既に使用されているので実際に使用することはできないはず。 実際に作成する際はバケット名の末尾に現在の日付時刻を入れたり、自分のあだ名をローマ字にしたものを間に挟むなりして工夫してくれ。

S3 で Web サイトのホスティング設定

外部から S3 にアクセスした際に、指定した Web ページを返すように設定していく。 作成した S3 バケットを選択し、以下の手順で設定をしていく。

  • 「プロパティ」タブ → 「Static website hosting」を選択する
  • 「このバケットを使用してウェブサイトをホストする」を選択する
  • 「インデックスドキュメント」に index.html と入力して保存する

これで Web サイトのホスティングの設定は以上、簡単である。

ブロックパブリックアクセスの設定

この機能がいまいち理解できていない。 おそらく公開設定などをミスって意図していないものが公開されないようにするための機能だと思うが、どうなんだろうか。 気になる人は以下を参照してくれ。

dev.classmethod.jp

ではでは、作成した S3 バケットを選択して以下の手順で設定をしていこう。

  • 「アクセス権限」タブ → 「ブロックパブリックアクセス」を選択する
  • 全てのチェックボックスからチェックを外して保存する

お分かりだろうか。 細かい設定が面倒くさいので、全てを公開した。 大切なファイルとかが人に見られても責任は一切とりませんので、お気をつけて。

バケットポリシーの設定

バケット内にあるオブジェクトにアクセスできるかできないかなど細かい設定をバケットポリシーで設定する。 作成した S3 バケットを選択し、以下の手順で設定をしていく。

  • 「アクセス権限」タブ → 「バケットポリシー」を選択し、以下を入力して保存する
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::app-bucket/*"
        }
    ]
}

"Resource": "arn:aws:s3:::app-bucket/*" この部分は自分が作成したバケットの名前を入れてあげること。 ちなみにこちらも全てのオブジェクトを公開するよう設定している。

フロントエンド用リポジトリの手動デプロイ

S3 の準備ができたらフロントエンド用リポジトリを手動デプロイしてみる。 その際に S3 にデプロイするための権限と、デプロイするためのコマンドが必要になるのでその準備から行う。

S3 へアクセスするユーザの作成と設定

AWS の IAM というサービスを使って S3 へアクセスするユーザの作成と設定をする。 IAM のページを開き、以下の内容でユーザを作ってくれ。

  • ユーザの作成
    • ユーザ名:app-s3-user
    • 「プログラムによるアクセス」にチェックを入れる
  • ポリシーのアタッチ
    • 「既存のポリシーを直接アタッチ」
    • 「AmazonS3FullAccess」にチェックを入れる
  • アクセス情報を覚えておく(それぞれの環境で異なるよ)
    • アクセスキーID:access_key_id
    • シークレットアクセスキー:secret_access_key

AWS CLI のインストールと設定

バックエンドとは異なり、フロントエンドはこの手順を踏まなくても自動デプロイまで行う環境を作ればデプロイされるようになるのだが、現段階で作った環境が適切に動作するか確認するために手動でデプロイして動作確認しておく。 手動デプロイはローカルから行うので注意。

S3 へ手動デプロイする際には AWS CLI を用いてデプロイを行う。 mac だと brew を使って AWS CLI をインストールできる。 brew 知らんし入ってないという人はこれ見て頑張ってくれ。

brew.sh

では AWS CLI をインストールしよう。

$ brew install acscli

これだけで入る。 なお mac 以外でインストールする場合は手順が異なると思うので、調べて頑張ってくれ。

AWS CLI をインストールした後は初期設定を行う。 以下のコマンドを実行し、ぽちぽち入力していく。 AWS Access Key IDAWS Secret Access Key には先程取得したアクセスキー ID とシークレットアクセスキーを入力する。

$ aws configure

AWS Access Key ID [None]: access_key_id
AWS Secret Access Key [None]: secret_access_key
Default region name [None]: ap-northeast-1
Default output format [None]: json

これでコマンドの準備はできた。

S3 への手動デプロイするぞ

ローカルでフロントエンド用リポジトリの場所に移動し、ビルドを実行するんだけどその前に本番環境用の env ファイルを書き換える必要ありけり。 API を叩く向き先になる部分、こいつに AWS の Elastic IP で取得した IP アドレスを入れておく。 ELASTIC_IP の部分には取得した IP アドレスをそのまま書いてね。

.env.production

NODE_ENV=production
- VUE_APP_API_BASE_URL=http://あとでEC2のIPアドレス入れるよ
+ VUE_APP_API_BASE_URL=http://ELASTIC_IP

これでビルドする。

$ npm run build

ビルドが実行されるとリポジトリ内に dist というフォルダが作成されているはず。 以下のコマンドを実行して dist を S3 に設置する。

$ aws s3 sync --delete dist/ s3://app-bucket/

これでフロントエンドの手動でのデプロイが完了。

サイトにアクセスして確認

ここまで設定を済ませるとアプリ自体は一通り完成したことになる。 それでは実際にサイトにアクセスして SPA な画面を表示し、API を実行して取得した値が表示されているか確認しよう。

サイトの URL は S3 バケットの Web サイトのホスティング欄に記述されているので、以下の手順で URL を探してページを開こう。

  • 「プロパティ」タブを選択する → 「Static website hosting」を選択する
  • 「エンドポイント」に記述された URL をクリックして開く

これで以下のように表示されれば成功、おつかれさま。

f:id:kkznch:20200223171823p:plain

SPA な Web アプリを継続的デリバリーする環境を作る (2) 〜 フロントエンド( Vue.js )編

概要

SPA な Web アプリを継続的デリバリーする環境を作るシリーズ第二回、フロントエンド編。 序章はこちら。

kkznch.hatenablog.com

全4部構成でお届けする。

ハンズオンするぞ

ひたすら手を動かそう。

Vue.js プロジェクトの作成と git の初期設定

フロントエンドで動作する Vue.js プロジェクトを作成する。 作成時に対話形式で色々と聞かれるけど、全部デフォルト値でも大丈夫だと思う。

$ vue create app-frontend

Vue CLI v4.1.2
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Linter
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Airbnb
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

多分 vue create コマンドで作成したプロジェクトはデフォで git リポジトリとなってるので、今回は git init コマンドは不要なはず。 なのでそのまま git remote add コマンドでハンズオンの前に自分で作成したリポジトリを git のリモートとして設定してあげよう。

$ git remote add origin git@gitlab.com:自分のユーザ名/app-frontend.git

なんとなくコミットしてプッシュまでしとく。

$ git add .
$ git commit -m "first commit"
$ git push -u origin master

環境毎に読み込む env ファイルの作成とビルドコマンドへオプションを追加

開発環境、本番環境ごとに読み込む env ファイルを作成する。 .env.local はローカル開発環境でビルドした際に読み込まれ、 .env.production は本番環境でビルドした際に読み込まれる。

.env.local

NODE_ENV=local
VUE_APP_API_BASE_URL=http://localhost

なお本番環境で指定する VUE_APP_API_BASE_URL は、このあと AWS でバックエンドの環境を構築する際に作られる EC2 の IP アドレス( EIP )を指定してあげる。 なので今は空白にしておいてもいいよ。

.env.production

NODE_ENV=production
VUE_APP_API_BASE_URL=http://あとでEC2IPアドレス入れるよ

env ファイルを作成しただけではビルド時の環境毎の切り替えができない(多分)ため、ビルドコマンドを実行する際にオプションを渡すよう package.json を修正する。

package.json

-   "serve": "vue-cli-service serve",
-   "build": "vue-cli-service build",
+   "serve": "vue-cli-service serve --mode local",
+   "build": "vue-cli-service build --mode production",

JS ライブラリ Axios のインストール

フロントから API を叩くために Axios という JS のライブラリをインストールする。

$ npm install axios

画面の作成

API を叩いて取得した値を表示する画面を作成する。 created() の中で axios.defaults.baseURL にバックエンドの URL を設定しているけど、本当は axios の interceptors という機能を使ってその中で設定してあげる方がよいかもしれない。

src/views/Hello.vue

<template>
  <div>
    <h1>ハローワールドですね</h1>
    <p>Body: {{ body }}</p>
    <p>Datetime: {{ datetime }}</p>
    <button @click="getHello">更新するよ</button>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  name: 'hello',
  data() {
    return {
      body: '',
      datetime: null,
    };
  },
  methods: {
    async getHello() {
      const response = await axios.get('/api');
      this.body = response.data.body;
      this.datetime = response.data.datetime;
    },
  },
  created() {
    axios.defaults.baseURL = process.env.VUE_APP_API_BASE_URL;
  },
  mounted() {
    this.getHello();
  },
};
</script>

動作の確認

ここまででフロントエンドの準備は9割方できた。 この状態で以下のコマンドを実行し、Vue.js へアクセスするための node サーバを起動する。

$ npm run serve

...略

  App running at:
  - Local:   http://localhost:8080/ 
  - Network: http://192.168.1.89:8080/

色々と表示された後で上記のようなメッセージが出力される。 ここに書いてあるとおり http://localhost:8080 へアクセスすると Vue.js の画面が表示される。 node サーバ起動時に環境によっては 8080 ポートが既に使用されている場合があり、別のポート番号が割り振られている場合があるが、その場合は表示されたポート番号にアクセスすればよい。

f:id:kkznch:20200222140217p:plain
Vue.jsアプリ初期画面

上記の画面のように "Hello, World!" と表示されていれば Vue.js から Laravel の API を実行できたことになる。 表示されていなかったら Vue.js または Laravel また Docker 環境のいずれかが問題なので、問題を一個ずつ解決していってくれ。

ここまで実行できたら下図のようにローカル環境で SPA な Web アプリを動作させることができたことになる。 おつかれさん。

f:id:kkznch:20200222134439p:plain
ローカルでの実行環境_Vue.js経由でアクセスしたよ

SPA な Web アプリを継続的デリバリーする環境を作る (1) 〜 バックエンド( Laravel )編

概要

SPA な Web アプリを継続的デリバリーする環境を作るシリーズ第一回、バックエンド編。 ちなみに序章はこちら。

kkznch.hatenablog.com

全4部構成でお届けする。

ハンズオンするぞ

ひたすら手を動かそう。

Laravel プロジェクトの作成と git の初期設定

バックエンドで動作する Laravel プロジェクトを作成する。

$ composer create-project --prefer-dist laravel/laravel app-backend

ついでに git でリポジトリの設定もする。 ハンズオンの前に自分で作成したリポジトリを git のリモートとして設定してあげよう。

$ cd app-backend
$ git init
$ git remote add origin git@gitlab.com:自分のユーザ名/app-backend.git

なんとなくコミットしてプッシュまでしとく。

$ git add .
$ git commit -m "first commit"
$ git push origin master

開発環境で使用する Docker な環境の構築

ローカル環境で動作を確認しながら開発をしたいと思うことがしばしばあるだろう。 雰囲気こんな感じでやる。

f:id:kkznch:20200222134439p:plain
ローカルでの実行環境

本記事では図中の右側にある Docekr Engine の枠と、Laravel の部分を実装していく。 Docker 環境は一度作っておくと使いまわしが効くので便利、頑張って理解しよう。

docker-compose.yml の作成

ローカル環境で開発をする際に使用する Docker Compose の設定ファイル docker-compose.yml を作成する。 Dockerfile などはこの後で用意するから心配無用。

docker-compose.yml

version: '3.7'
services:
  php-fpm:
    build:
      context: .
      dockerfile: ./Dockerfile
    depends_on:
      - mysql
    volumes:
      - .:/app:cached
    working_dir: /app
  nginx:
    image: nginx:latest
    ports:
      - 80:80
    depends_on:
      - php-fpm
    volumes:
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_DATABASE=laravel
      - MYSQL_USER=root
      - MYSQL_PASSWORD=
      - MYSQL_ROOT_PASSWORD=root
    ports:
      - 3306:3306
    volumes:
      - db-volume:/var/lib/mysql
volumes:
  db-volume:

php-fpm 用の Dockerfile の作成

Laravel が動作する php-fpm な Dockerfile を作成する。

Dockerfile

FROM php:7.3-fpm-alpine

RUN apk --no-cache update && apk --no-cache upgrade
RUN docker-php-ext-install \
    bcmath \
    pdo_mysql

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN composer global require hirak/prestissimo

nginx の設定ファイルの作成

リクエストを nginx コンテナから php-fpm コンテナに流すよう設定ファイルを作成する。 こいつは nginx コンテナを起動する際にファイルがコンテナ内にマウントされるよう ./docker-compose.yml に記述されてる。

docker/nginx/default.conf

server {
    server_name localhost;
    listen 80;
    root /app/public;
    index index.php index.html;

    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

docker-compose でビルドとコンテナの起動

docker-compose コマンドを実行して Docker イメージのビルドをする。

$ docker-compose build

ついでにイメージからコンテナを作成して起動する。

$ docker-compose up -d

これでアプリケーションが動作する環境が動いている状態になる。 このあとで Laravel プロジェクトを作成するので、その後にアクセスすると動作が確認できるはず。

ちなみにコンテナを停止して削除したい場合は以下のコマンドを実行するとよい。 今は実行しなくてもいいよ。

$ docker-compose down

バックエンドアプリの実装

Laravel で API コントローラの作成

フロントからリクエストがあった際にシンプルな文字列を返すためのコントローラを作成する。 以下のコマンドを実行するのだ。

$ docker-compose exec php-fpm php artisan make:controller Api/HelloController

app/Http/Controllers/Api/HelloController.php というファイルが作成されるので、その内容を以下のようにする。

app/Http/Controllers/Api/HelloController.php

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Carbon\Carbon;

class HelloController extends Controller
{
    public function index()
    {
        return response()->json([
            'body' => 'Hello, World!',
            'datetime' => Carbon::now(),
        ]);
    }
}

コントローラの index にアクセスがあると "Hello, World!" と一緒に日時を返すだけのアクションになっている。

API コントローラ用のルーティングを設定する

ルーティングの設定を以下のように行う。 今回は / にアクセスされると HelloController の index メソッドに記述された処理が実行される。

routes/api.php

<?php

Route::get('/', 'Api\HelloController@index');

CORS 対策用パッケージのインストールと設定ファイルの編集

今回想定している構成だと別のドメインにある API を実行するため、 CORS エラーが起きてアクセスが遮断される。 それを回避するために、バックエンド側で CORS の対策を行う。

CORS 知らん人はここを見てね。

developer.mozilla.org

というわけで、まずは Laravel で CORS 対策用のパッケージをインストールする。

$ docker-compose exec php-fpm composer require fruitcake/laravel-cors

CORS 対策パッケージを有効化するために Kernel.php の middleware に以下のように記述する。

app/Http/Kernel.php

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    protected $middleware = [
        ...略
+        \Fruitcake\Cors\HandleCors::class, 
    ];
    
    ...略

以下のようにCORS 対策パッケージの設定ファイルを書く。

config/cors.php

<?php

return [
    'paths' => ['*'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => false,
    'max_age' => false,
    'supports_credentials' => false,
];

動作の確認

以下のコマンドを実行してレスポンスが返ることを確認する。 レスポンスが返ってこない人はドンマイ。

$ curl http://localhost/api

{"body":"Hello, World!","datetime":"2020-02-20T09:00:00.000000Z"}

curl コマンドを実行することで、Vue.js からではなく直接ローカルホストを経由して nginx 、 php-fpm にアクセスしたことになる。

f:id:kkznch:20200222135135p:plain
ローカルでの実行環境_curlでアクセスしたよ

確認できたらコミットしてプッシュまでしておく。 この後の工程で必要になるので、忘れずに。

$ git add .
$ git commit -m "second commit"
$ git push origin master