AWS, 優総研

ポートフォリオ制作なうなんだが…その1

 

 

 

 

 

最短で完成を目指す。

 

※雑記。現状は参考?にするのはおすすめしません。最後に整理します。

 

内容

本の感想投稿掲示板 + Google Books APIとのマッシュアップ

他書評サービスのコミュニティ機能があまり使われておらず成功していないから、Yomuyoは本好きの間で小さくてもコミュニケーションが生まれるものにしたいな。それがこのサービスの意義。

 

  • 収益という目的性があるよって存在意義
    →Amazon APIの売り上げ条件の為採用を断念
  • おすすめの本というコミュニティ
  • Google Books APIがアプリの外見に華やかさを与えてくれる

※Amazon のAP API利用は売上実績がないとAPI利用でエラーになるので断念。Google Book APIでスタートさせて、Amazonアフィリエイトで実績を十分に作れたら、PA APIに移行するのが良いかな?

 

機能

  • Google Books API連携 + Amazonアソシエイト
  • 検索
  • 書評投稿
  • 会員機能 + 投稿
  • 書評ランキング
  • レビュアランキング
  • レコメンド(余裕があれば実装したい)

シンプルに最短で実装する為に機能を一時的にそぎ落とす。

キャッシュ戦略

  • Google Books APIのキャッシュ
    外部APIに負荷を与え過ぎて利用停止されないように、Google Books APIでの問い合わせ結果はElasticache(Redis)に1週間分をキャッシュする。
    →検索結果が高速化してUXも向上
    // まだGoogle Books APIは良いけれど、Amazon PA APIはシビアなので必須。
  • よくある参照結果もキャッシュ
    トップページなど30秒~1分程度キャッシュします。
    →DBへの負荷軽減と高速化。

 

 

技術要素

AWSのフルマネージドとSaaSでなるべくサーバレス構成にしています。

  • 開発ツール:VS Code + Git 済
  • 開発環境[ 構築|更新|テスト]自動化: Bash Shellスクリプト + docker-compose 済
  • 機密情報隠蔽化:KMS + AWS Systems Manager パラメータグループ 済
  • 自動デプロイ:Docker => GitHub => CodePipeline => CodeBuild => ECR +ECS 済
  • CI + ChatOps:GitHub => CodePipeline => CodeBuild + PHPUnit + [ CloudWatch Events => Lambda+KMS => Slack ] 済
  • フレームワーク:Laravel 済
  • データベースGUI管理ツール:phpMyAdmin on Docker 済
  • 言語:PHP7 済
  • バッチ処理:Lambda, CloudWatch(定期スナップショット) 済
  • RDBMS:RDS(MySQL) 済
  • NoSQL:Elasticache for Redis(セッションサーバ) 済
  • オブジェクトキャッシュ:Elasticache for Redis + Laravel 済
  • フロント:Vue.js + Laravel
  • CDN:Laravel + CloudFront + S3 済
  • ログ分析基盤:Cloudwatch + S3 + Athena, Elasticsearch+kibana 済
  • 日時ロギング:CloudWatch Logs(LogGroup)  => Lambda => S3
  • DNS:Route53 済
  • 証明書自動更新:Route53 + ACM + [ ALB|CloudFront ] 済
  • メール送信管理:SendGrid 済
  • メッセージキュー:Laravel + AWS SQS
  • 監視:Mackerel => [ Slack|Twilio ], CloudWatch Alert => SNS => Lambda => Slack
  • 負荷試験:Jmeter, Apache Bench, Locust
  • セキュリティ:ELB+AWS WAF(Trend Micro Managed Rules for AWS WAF) 済
  • ソーシャルログイン(Laravel Socialite+[Facebook, Twitter]) 済
  • いいねボタン:Ajax + jQuery + Laravel 済
  • マークアップ, グリッド:HTML/CSS, Bootstrap4, Laravel Blade 済
  • ER図 自動作成・更新:SchemaSpy on Docker + Nginx 済

 

 

ローカル or EC2

  • local(開発)
    EC2 + docker-compose
    ローコストで開発&確認

ECS

GitHubのブランチ名でデプロイ先を分ける

  • staging(検証)
    ECS(staging)+EC2
    本番と同じ構成でテストして表示や動作を確認します。
  • production(本番)
    ECS(production)+Fargate

Fargateにするとホスト管理の開放、スケールを気にしなくて良い。

 

Laravel

バージョン:5.8

  • CRUD + Eloquent ORM  済
  • リレーション
    tinker, hasMany, belogsTo, many to many,
  • Eagerローディング
    with()によるN+1対策
  • トランザクション 済
    ロック処理,ロールバック, リトライ
  • ログイン 済
  • Ajax + jQuery いいねボタン 済
  • ランキング機能 済
  • ソーシャルログイン(Facebook, Twitter) 済
  • セッション 済
    relfresh(), old()
  • バリデーション 済
    Request, 
  • アクセス管理 済
  • ストレージ + S3連携 済
  • try catch + ログ出力 済
  • 単体テスト(PHPUnit)
  • ページネーション 済
  • サービスプロバイダ(DI) 済
  • メール送信 済
  • システム管理者用ページ
    Vue.js + RestAPI(Laravel)で作る?
  • ジョブキュー・イベント + AWS SQS
  • Artisan
    auth, request, response, rule, middleware, scope, migrate, seeder, tinker,

 

おすすめ書籍

 

[amazon_link asins=’4798052582,4798059072,4798110663,B07T5995HK,4863542178′ template=’ProductCarousel’ store=’izayoi55-22′ marketplace=’JP’ link_id=’5611bcdd-41d2-4000-a62d-d6648a2af9d5′]

 

 

VS Code

マルチプラットフォームで環境に左右されないIDE

 

拡張プラグイン

 

 

SSHでの接続

ターミナルからコマンドラインで接続しておくこと

ssh -i "秘密鍵のパス" ec2-user@<IPアドレス>

 

ssh_config

C:\Users\\root\Desktop\secret\ssh_config


# Laravel APIお勉強
Host 52.xxx.xxx.91
    HostName 52.xxx.xxx.91
    User ec2-user  
    Port 22
    IdentityFile C:\Users\\root\Desktop\secret\Yuu-AWS-20190614-key.pem


# yomuyo.net開発
Host dev.yomuyo.net
    HostName 52.xxx.xxx.243
    User ec2-user  
    Port 22
    IdentityFile C:\Users\\root\Desktop\secret\Yuu-AWS-20190614-key.pem

 

 

 

ショートカット

  • 矩形選択
    [Ctrl]+[Shift]+[Alt]+矢印キー

 

 

Route53と各フルマネージドサービスのエンドポイント

  • エンドポイントはそのままではわけが分からなくなるので、AliasやCNAMEでドメイン名に置き換えると管理しやすい。
  • Alias
    ALB, CloudFront, S3
  • CNAME
    RDS, ElastiCache(Redis)

AliasはDNS問い合わせが1回なので通信パフォーマンスが高いがAlias指定できないものがある。RDSなどはCNAMEしか指定できない(2019年07月時点)

 

環境構築シェルを作成

 

完成図

$ tree  --charset=o  -L  4
.
`-- yomuyo-docker-template
    |-- data
    |-- db
    |   |-- auto.cnf
    |   |-- ibdata1
    |   |-- ib_logfile0
    |   |-- ib_logfile1
    |   |-- mysql [error opening dir]
    |   |-- performance_schema [error opening dir]
    |   `-- yomuyodb [error opening dir]
    |-- env
    |-- shell
    |   |-- 1_init_Setup_local.sh
    |   |-- 2_Create_APP.sh
    |   `-- 3_Docker-Run.sh
    `-- src
        |-- docker
        |   |-- nginx
        |   `-- php-fpm
        |-- docker-compose.yml
        |-- update.sh
        `-- yomuyo
            |-- app
            |-- artisan
            |-- bootstrap
            |-- composer.json
            |-- composer.lock
            |-- config
            |-- database
            |-- package.json
            |-- phpunit.xml
            |-- public
            |-- readme.md
            |-- resources
            |-- routes
            |-- server.php
            |-- storage
            |-- tests
            |-- vendor
            `-- webpack.mix.js

 

 

ディレクトリ作成

$ mkdir -p $HOME/yomuyo-docker-template/{data,src,shell,db,env}
$ mkdir -p $HOME/yomuyo-docker-template/src/docker/{nginx,php-fpm,schemaspy,nginx_for_schemaspy}
$ mkdir -p $HOME/yomuyo-docker-template/src/docker/schemaspy/config

 

 

ローカルのみ有効な環境変数の設定

作業を楽にするために設定

$ vi ~/.bash_profile


# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

# アプリのソースパス
APP_PATH=$HOME/yomuyo-docker-template/src/yomuyo
# アプリコントローラパス
CONT_PATH=$HOME/yomuyo-docker-template/src/yomuyo/app/Http/Controllers
# Modelパス
MODEL_PATH=$HOME/yomuyo-docker-template/src/yomuyo/app/Models
# Viewパス
VIEW_PATH=$HOME/yomuyo-docker-template/src/yomuyo/resources/views
# CSSパス
CSS_PATH=$HOME/yomuyo-docker-template/src/yomuyo/public/css
# Dockerパス
DOCKER_PATH=$HOME/yomuyo-docker-template/src/yomuyo/docker


export PATH
export APP_PATH
export CONT_PATH
export MODEL_PATH
export VIEW_PATH
export CSS_PATH

 

適用

$ source ~/.bash_profile

確認

$ echo $APP_PATH
/home/ec2-user/yomuyo-docker-template/src/yomuyo

これでよく利用するディレクトリの指定が楽になる。

 

 

.env.localの作成

開発用の環境変数です。

$ vi $HOME/yomuyo-docker-template/env/.env.local


APP_NAME=yomuyo
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost

## ログチャネル 開発:develop 本番:production
LOG_CHANNEL=develop


## MySQL
DB_CONNECTION=mysql
DB_HOST=mysql57
DB_PORT=3306
DB_DATABASE=yomuyodb
DB_USERNAME=root
DB_PASSWORD=naishodayo

# Master
DB_MASTER_HOST=mysql57
DB_MASTER_PORT=3306

# Slave(Read)
DB_SLAVE_HOST=mysql57
DB_SLAVE_PORT=3306


## SendGrid
SENDGRID_API_KEY=<●SendGridAPIキー>

MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=587
MAIL_FROM_ADDRESS=no-reply@yomuyo.net
MAIL_FROM_NAME=自動送信メール
MAIL_ENCRYPTION=tls


## Redis
CACHE_DRIVER=redis
SESSION_DRIVER=redis
REDIS_HOST=elasticache-redis-primary.yomuyo.net
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_READ_WRITE_TIMEOUT=60


## Social Login
# Facebook
FACEBOOK_APP_ID=XXXXXXX
FACEBOOK_APP_SECRET=XXXXXXXXXXXXXXXXXX
FACEBOOK_CALLBACK_URL=https://<ドメイン>/auth/callback/facebook

# Twitter
TWITTER_APP_ID=XXXXXXX
TWITTER_APP_SECRET=XXXXXXXXXXXXXXXXXX
TWITTER_APP_ACCESS_TOKEN=XXXXXXXXXXXXXXXXXX
TWITTER_APP_ACCESS_SECRET=XXXXXXXXXXXXXXXXXX
TWITTER_CALLBACK_URL=https://<ドメイン>/auth/callback/twitter


## AWS S3
AWS_ACCESS_KEY_ID=xxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxx
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=s3.yomuyo.net

 

staging(検証)やproduction(本番)環境変数のデプロイ

  1. AWS SSMのパラメータストアにAWS KMSで暗号化させた値を保存
  2. ECSで値を復号してコンテナに環境変数を上書きすることで反映させます

 

 

1_init_Setup_local.sh

環境構築します。実行後はリブートがかかる。

$ cat $HOME/yomuyo-docker-template/shell/1_init_Setup_local.sh



#!/bin/bash
set -e

# スクリプトディレクトリの取得
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR

## docker-composeインストール
echo "docker-composeインストール...Start" &&
sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose &&
sudo chmod +x /usr/local/bin/docker-compose &&
echo "docker-composeインストール...End" &&


## Dockerインストール
echo "Dockerインストール...Start" &&
sudo yum install -y docker &&
sudo usermod -aG docker ${USER} &&
sudo gpasswd -a $USER docker &&

sudo systemctl restart docker &&
sudo systemctl enable docker &&
sudo systemctl status docker &&
echo "Dockerインストール...End" &&


## PHP7.2インストール
echo "PHP7.2インストール...Start" &&
sudo amazon-linux-extras install -y php7.2 &&
sudo amazon-linux-extras install -y epel &&
sudo yum install -y php-common php-gd php-mysqlnd php-mbstring php-pdo php-xml php-zip php-pecl-zip &&
sudo yum install -y git mailx tree &&
echo "PHP7.2インストール...End" &&


# npm, yarnインストール
echo "npm, yarnインストール...Start" &&
sudo yum install -y nodejs gcc-c++ make &&
curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
sudo yum install -y yarn
echo "npm, yarnインストール...End" &&


## MySQLクライアントインストール
echo "MySQLクライアントインストール...Start" &&
sudo yum install -y mysql &&
echo "MySQLクライアントインストール...End" &&

## Redisクライアント
echo "Redisクライアントインストール...Start" &&
sudo yum install -y redis &&
echo "Redisクライアントインストール...End" &&


## AWS CLIインストール
echo "AWS CLIインストール...Start" &&
curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" &&
sudo python get-pip.py &&
sudo pip install awscli &&
echo "AWS CLIインストール...End" &&


# サーバの更新と再起動
echo "サーバの更新と再起動...Start" &&
sudo yum update -y &&
sudo reboot now

 

 

2_Create_APP.sh

Laravelをインストールします。

プロジェクト名とバージョンを変更できます。

cat $HOME/yomuyo-docker-template/shell/2_Create_APP.sh



#!/bin/bash
set -e


# スクリプトディレクトリの取得
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR

######################################################
## 設定値入力 ========================================

## プロジェクト名
LARAVEL_PROJECT_NAME=yomuyo
LARAVEL_VERSION=5.8.*

## 設定値入力 END ====================================
######################################################

# Composerインストール
echo "Install_Composer...Start" &&
curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer &&
echo "Install_Composer...End" &&

# Laravelインストール
echo "Install_Laravel...Start" &&
cd ../src/ &&
composer global require "laravel/installer" &&
composer create-project --prefer-dist laravel/laravel ${LARAVEL_PROJECT_NAME} "${LARAVEL_VERSION}" &&
sudo chown $USER:$USER -R ${LARAVEL_PROJECT_NAME} &&
echo "Install_Laravel...End" &&

# npm install in Laravel
echo "npm install in Laravel...Start" &&
cd $APP_PATH &&
npm install
echo "npm install in Laravel...End"

 

 

3_Docker-Run.sh

DockerでLaravel開発環境を立ち上げます

$ cat $HOME/yomuyo-docker-template/shell/3_Docker-Run.sh



#!/bin/bash
set -e


# スクリプトディレクトリの取得
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR

# Dockerfileの更新
rsync -r ../../src/docker/ ../src/docker/ &&

# コンテナ群の起動
echo "docker-compose up -d...Start" &&
docker-compose up -d &&
echo "docker-compose up -d...End" &&

# .env作成(開発用)
echo ".env作成(開発用)...Start" &&
cp ../../env/.env.local ./.env &&
echo ".env作成(開発用)...End" &&


# keyの作成
echo "Laravel php artisan key:generate...Start" &&
docker-compose exec php-fpm php artisan key:generate &&
echo "Laravel php artisan key:generate...End"

 

実行権限の付与

chmod +x $HOME/yomuyo-docker-template/shell/{1_init_Setup_local.sh,2_Create_APP.sh,3_Docker-Run.sh}

 

①環境構築スクリプトを実行

sh $HOME/yomuyo-docker-template/shell/1_init_Setup_local.sh

 

②Laravelプロジェクトの作成

sh $HOME/yomuyo-docker-template/shell/2_Create_APP.sh

 

 

docker-compose.yml

# vi $APP_PATH/docker-compose.yml


version: '3.2'
services:
  mysql8:
    container_name: "mysql8"
    image: mysql:8.0
    environment:
        MYSQL_DATABASE: yomuyodb
        MYSQL_ROOT_PASSWORD: naishodayo
    ports:
      - "3306"
    restart: always
    volumes:
      - "../../db:/var/lib/mysql"
  nginx:
    container_name: "nginx"
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    volumes:
      - "./public:/app/public/"
    ports:
      - "80:80"
    depends_on:
      - php-fpm
    restart: always
  php-fpm:
    container_name: "php-fpm"
    build:
      context: .
      dockerfile: ./docker/php-fpm/Dockerfile
    volumes:
      - "./:/app/"
    restart: always
    links:
     - mysql8
  pma:
    container_name: "pma"
    image: phpmyadmin/phpmyadmin:latest
    environment:
      PMA_HOST: mysql8
    ports:
    - "8080:80"
    restart: always
    links:
     - mysql8


  nginx_for_schemaspy:
    image: nginx:1.13.9-alpine
    container_name: nginx_for_schemaspy
    volumes:
      - ./schemaspy:/var/www/html:ro
      - ./docker/nginx_for_schemaspy/default.conf:/etc/nginx/conf.d/default.conf:ro
    ports:
      - "8081:80"
    environment:
      - LANG=ja_JP.UTF-8
      - TZ=Asia/Tokyo
    command: "nginx -g 'daemon off;'"
  schemaspy:
    build: ./docker/schemaspy
    image: treetips/schemaspy-mysql
    container_name: schemaspy
    volumes:
      - ./schemaspy:/app/html:rw
      - ./docker/schemaspy/config/schemaspy.properties:/app/schemaspy.properties:ro
    environment:
      - LANG=ja_JP.UTF-8
      - TZ=Asia/Tokyo
    working_dir: "/app"
    command: "java -jar schemaspy.jar"

 

 

NginxコンテナDockerfile

$ vi $HOME/yomuyo-docker-template/src/docker/nginx/Dockerfile


FROM nginx:latest

## Timezon 
ENV TZ Asia/Tokyo
RUN echo "${TZ}" > /etc/timezone \
   && dpkg-reconfigure -f noninteractive tzdata

## Nginx Setting
ADD ./docker/nginx/site.conf /etc/nginx/conf.d/default.conf
ADD ./docker/nginx/nginx.conf /etc/nginx/nginx.conf

RUN usermod -u 1000 www-data
RUN groupmod -g 1000 www-data

RUN mkdir -p /app/public
ADD ./public/ /app/public/

 

Nginxメイン設定ファイル

$ vi $HOME/yomuyo-docker-template/src/docker/nginx/nginx.conf


user www-data;
worker_processes auto;

error_log   /dev/stderr;
pid /var/run/nginx.pid;

events {
  worker_connections  1024;
}

http {
  # ALB Timeout対策
  fastcgi_connect_timeout 120;
  fastcgi_read_timeout    120;
  fastcgi_send_timeout    120;
  keepalive_timeout       120;
  keepalive_requests      100;
  client_header_timeout   60s;
  client_body_timeout     60s;

  include /etc/nginx/mime.types;
  default_type  application/octet-stream;

  include /etc/nginx/conf.d/*.conf;
}

 

 

Nginx バーチャルホスト設定ファイル

$ vi $HOME/yomuyo-docker-template/src/docker/nginx/site.conf


server {
    index index.php index.html;
    server_name localhost;
    #error_log  /var/log/nginx/error.log;
    #access_log /var/log/nginx/access.log;
    access_log  /dev/stdout;
    error_log   /dev/stderr;
    root /app/public;

    location ~ ^.+/\.(png|jpg|jpeg|gif|ico|css|js|html?)$ {
        try_files $uri $uri/ =404;
    }

    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;
    }
}

 

 

PHP-FPMコンテナDockerfile

$ vi $HOME/yomuyo-docker-template/src/docker/php-fpm/Dockerfile



FROM php:7-fpm
 
ENV DEBIAN_FRONTEND noninteractive

## Timezon
ENV TZ Asia/Tokyo
RUN echo "${TZ}" > /etc/timezone \
   && dpkg-reconfigure -f noninteractive tzdata

## Basic Install
RUN apt-get update && apt-get install -y git zlib1g-dev zip unzip libzip-dev
RUN docker-php-ext-install zip mysqli pdo_mysql

## Permission
RUN mkdir -p /app
ADD ./ /app
WORKDIR /app
 
RUN usermod -u 1000 www-data
RUN groupmod -g 1000 www-data
RUN chown -R www-data:www-data /app
 
## Deploy Laravel Libs by Composer
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

RUN composer global require hirak/prestissimo
RUN cp .env.example .env
RUN composer install
RUN php artisan key:generate
RUN php artisan cache:clear
RUN php artisan config:clear
RUN php artisan route:clear
RUN php artisan view:clear
RUN composer dump-autoload
RUN php artisan clear-compiled

## Laravel Permission 
RUN chmod -R a+w storage/ bootstrap/cache
RUN chown -R www-data:www-data /app/storage
RUN chmod -R 775 /app/storage

 

 

 

php.ini

$ vi $HOME/yomuyo-docker-template/src/docker/php-fpm/php.ini


[global]
; ファイルアップロード
file_uploads = On
memory_limit = 64M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120

; ログの出力
log_errors = On
error_reporting = E_ALL
error_log = "/proc/self/fd/2"


;バージョン非表示
expose_php = Off


;タイムゾーン
date.timezone = "Asia/Tokyo"


;デフォルト言語
mbstring.language = Japanese
;文字コード
default_charset = "UTF-8"
;HTTP入力文字エンコーディングのデフォルト
mbstring.http_input = auto
;文字エンコーディング検出順序のデフォルト
mbstring.detect_order = auto


[www]
access.log = /proc/1/fd/1

 

PHP-FPM設定ファイル

$ vi $HOME/yomuyo-docker-template/src/docker/php-fpm/www.conf


[www]
listen = 9000
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
user  = www-data
group = www-data


;チューニング
pm = dynamic
pm.process_idle_timeout = 10s
pm.max_children = 120
pm.start_servers = 30
pm.min_spare_servers = 30
pm.max_spare_servers = 120
pm.max_requests = 500;

pm.process_idle_timeout = 10s
;request_terminate_timeout = 30

;ALB対策
;request_terminate_timeout = 40
request_terminate_timeout = 120

 

 

Schemaspyコンテナ

$ vi $APP_PATH/docker/schemaspy/Dockerfile


FROM openjdk:8u121-jdk-alpine

ENV DRIVER_URL http://central.maven.org/maven2/mysql/mysql-connector-java/5.1.45/mysql-connector-java-5.1.45.jar
ENV APP_URL https://github.com/schemaspy/schemaspy/releases/download/v6.0.0-rc2/schemaspy-6.0.0-rc2.jar

WORKDIR /app

RUN apk --update add graphviz ttf-dejavu && \
    apk --update add --virtual .builddep tzdata wget libressl && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    wget -O mysql-connector-java.jar ${DRIVER_URL} && \
    wget -O schemaspy.jar ${APP_URL} && \
    apk del .builddep && \
    rm -rf /var/cache/apk/*

 

$ vi $APP_PATH//docker/schemaspy/config/schemaspy.properties



# type of database. Run with -dbhelp for details
schemaspy.t=mysql
# optional path to alternative jdbc drivers.
schemaspy.dp=/app/mysql-connector-java.jar
# database properties: host, port number, name user, password
schemaspy.host=mysql8
schemaspy.port=3306
schemaspy.db=yomuyodb
schemaspy.u=root
schemaspy.p=naishodayo
# output dir to save generated files
schemaspy.o=/app/html
# db scheme for which generate diagrams
schemaspy.s=yomuyodb

 

nginx_for_schemaspyコンテナ作成

$ cat $APP_PATH/docker/nginx_for_schemaspy/default.conf



server {
    listen                80 default;
    server_name           localhost;

    root                  /var/www/html;
    charset               utf-8;
    disable_symlinks      off;
    index                 index.html;

    access_log            off;
    error_log             off;

    sendfile              on;
    tcp_nodelay           on;
    tcp_nopush            on;
    keepalive_timeout     120s;

    gzip                  on;
    gzip_http_version     1.0;
    gzip_disable          “MSIE [1-6].(?!.*SV1)”;
    gzip_comp_level       1;
    gzip_proxied          any;
    gzip_vary             on;
    gzip_buffers          4 8k;
    gzip_min_length       1100;
    gzip_types            text/plain
                          text/xml
                          text/css
                          application/xml
                          application/xhtml+xml
                          application/rss+xml
                          application/atom_xml
                          application/json
                          application/javascript
                          application/x-javascript
                          application/x-httpd-php;
}

 

.gitignore設定

$ cat $APP_PATH/.gitignore


/node_modules
/public/hot
/public/storage
/public/schema
/schema
/schemaspy
/storage/*.key
/vendor
.env
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log

 

 

 

③ Docker起動スクリプト実行

sh $HOME/yomuyo-docker-template/shell/3_Docker-Run.sh

 

④ Docker関連ディレクトリをLaravelプロジェクトに複製する

$ rsync -r $HOME/yomuyo-docker-template/src/docker/ $APP_PATH/docker/
$ rm -rf $HOME/yomuyo-docker-template/src/docker/

$HOME/yomuyo-docker-template/src/docker/を削除する。

 

Laravel

http://IPアドレス/

 

phpMyAdmin

http://IPアドレス:8080

  • ユーザ:root
  • パスワード:naishodayo
    ※docker-compose.ymlでmysql56コンテナで設定しているもの

 

 

SchemaSpy

http://IPアドレス:8081

 

 

外部キー制約を行うとリレーションが確認出来ます。

 

 

 

ローカル用 更新スクリプト作成

Laravelの設定ファイル変更などした時はこのスクリプトを叩いて反映させる。

 

update.sh(通常更新用)

$ vi $APP_PATH/update.sh


#!/bin/bash
set -e

# スクリプトディレクトリの取得
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR


echo "Docker+Laravel Update ...Start"
# .envをローカル開発用に更新
rm -f ./.env &&
cp ../../env/.env.local ./.env &&

# docker-composeによるDockerコンテナの更新
docker-compose up -d &&

# composer installによるライブラリの追加
docker-compose exec php-fpm php artisan key:generate &&
docker-compose exec php-fpm composer install &&
docker-compose exec php-fpm composer dump-autoload &&

# DBマイグレーション, シーディング
docker-compose exec php-fpm php artisan migrate:refresh --seed

# キャッシュの削除
docker-compose exec php-fpm php artisan cache:clear &&
docker-compose exec php-fpm php artisan config:clear &&
docker-compose exec php-fpm php artisan route:clear &&
docker-compose exec php-fpm php artisan view:clear &&
echo "Docker+Laravel Update ...End"

 

 

 

hard_update.sh(コンテナイメージを変更した時など)

$ vi $APP_PATH/hard_update.sh


#!/bin/bash
set -e

# スクリプトディレクトリの取得
SCRIPT_DIR=`dirname $0`
cd $SCRIPT_DIR


echo "Docker+Laravel Update ...Start"
# .envをローカル開発用に更新
rm -f ./.env &&
cp ../../env/.env.local ./.env &&


# dockerの既存イメージ, コンテナの削除
#docker stop $(docker ps -q) &&
#docker rm $(docker ps -q -a) &&
#docker rmi $(docker images -q) &&

# docker-composeによるDockerコンテナの更新
docker-compose build --no-cache &&
docker-compose up -d &&

# composer installによるライブラリの追加
docker-compose exec php-fpm php artisan key:generate &&
docker-compose exec php-fpm composer install &&
docker-compose exec php-fpm composer dump-autoload &&

# DBマイグレーション, シーディング
docker-compose exec php-fpm php artisan migrate:refresh --seed

# キャッシュの削除
docker-compose exec php-fpm php artisan cache:clear &&
docker-compose exec php-fpm php artisan config:clear &&
docker-compose exec php-fpm php artisan route:clear &&
docker-compose exec php-fpm php artisan view:clear &&

 

実行権限付与

$ chmod +x $HOME/yomuyo-docker-template/src/(update.sh,hard_update.sh}

 

実行

sh $HOME/yomuyo-docker-template/src/update.sh

更新されたらOK

 

 

ECSへのデプロイ用ファイル【CodeBuild用builspec.yml】作成

$ cat $APP_PATH/buildspec.yml


version: 0.2

phases:
  install: # パッケージインストール
    commands:
      #- echo Tool install...
      #- apt-get update
      #- apt-get install -y php
      #- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
      #- php composer-setup.php ;
      #- php -r "unlink('composer-setup.php');" ;
      #- mv composer.phar /usr/local/bin/composer
      #- chmod -R a+w storage/ bootstrap/cache
      #- composer install --no-dev
    runtime-versions:
      docker: 18
  pre_build: # ビルド前処理
    commands:
      - echo Clean file...
      - rm -rf .git .gitignore README.md
      - echo Logging in to Amazon ECR...
      - pip install awscli --upgrade
      - aws --version
      - $(aws ecr get-login --no-include-email --region ap-northeast-1)
      - IMAGE_NAME_NGINX=nginx
      - IMAGE_NAME_PHP_FPM=php-fpm
      - REPOSITORY_URI_PHP_FPM=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${PROJECT_NAMESPACE}/${IMAGE_NAME_PHP_FPM}
      - REPOSITORY_URI_NGINX=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${PROJECT_NAMESPACE}/${IMAGE_NAME_NGINX}
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build: #ビルドテスト
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t ${REPOSITORY_URI_PHP_FPM}:latest -f docker/php-fpm/Dockerfile .
      - docker build -t ${REPOSITORY_URI_NGINX}:latest -f docker/nginx/Dockerfile .
      - docker tag ${REPOSITORY_URI_PHP_FPM}:latest ${REPOSITORY_URI_PHP_FPM}:$IMAGE_TAG
      - docker tag ${REPOSITORY_URI_NGINX}:latest ${REPOSITORY_URI_NGINX}:$IMAGE_TAG
  post_build: # ECRへのプッシュ
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push ${REPOSITORY_URI_PHP_FPM}:latest
      - docker push ${REPOSITORY_URI_PHP_FPM}:$IMAGE_TAG
      - docker push ${REPOSITORY_URI_NGINX}:latest
      - docker push ${REPOSITORY_URI_NGINX}:$IMAGE_TAG
      - echo Writing image definitions file...
      - IMAGE_DIFINITION_PHP_FPM="{\"name\":\"${IMAGE_NAME_PHP_FPM}\",\"imageUri\":\"${REPOSITORY_URI_PHP_FPM}:${IMAGE_TAG}\"}"
      - IMAGE_DIFINITION_NGINX="{\"name\":\"${IMAGE_NAME_NGINX}\",\"imageUri\":\"${REPOSITORY_URI_NGINX}:${IMAGE_TAG}\"}"
      - echo "[${IMAGE_DIFINITION_PHP_FPM},${IMAGE_DIFINITION_NGINX}]" > imagedefinitions.json
artifacts:
    files: imagedefinitions.json # S3へアップするファイルを指定

 

 

@see

 

 

 

時刻、タイムゾーン設定

$ vi $HOME/yomuyo-docker-template/src/yomuyo/config/app.php


    /*
    |--------------------------------------------------------------------------
    | Application Timezone
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default timezone for your application, which
    | will be used by the PHP date and date-time functions. We have gone
    | ahead and set this to a sensible default for you out of the box.
    |
    */

-    'timezone' => 'UTC',
+    'timezone' => 'Asia/Tokyo',

    /*
    |--------------------------------------------------------------------------
    | Application Locale Configuration
    |--------------------------------------------------------------------------
    |
    | The application locale determines the default locale that will be used
    | by the translation service provider. You are free to set this value
    | to any of the locales which will be supported by the application.
    |
    */

-    'locale' => 'en',
+    'locale' => 'ja',

 

 

データベース接続設定変更

$ vi $HOME/yomuyo-docker-template/src/yomuyo/config/database.php


・・・
        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],
        

↓変更


        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            //'host' => env('DB_HOST', '127.0.0.1'),
            //'port' => env('DB_PORT', '3306'),
            
            // リード(読み込み専用エンドポイント)
            'read' => [
                'host' => env('DB_SLAVE_HOST', ''),
                'port' => env('DB_SLAVE_PORT', ''),
            ],
            // マスター(書き込み専用エンドポイント)
            'write' => [
                'host' => env('DB_MASTER_HOST', ''),
                'port' => env('DB_MASTER_PORT', ''),
            ],
            
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

・・・

Auroraだと読み込み、書き込みで負荷分散でスケールできるように分かれるので指定が必要。

今回はRDSなのでこういう形にもしなくて良いが、後々を考えてこうする。

 

ProductionをHTTPS化する事前準備

{{ asset(‘パス’) }}を利用している場合、本番環境にデプロイするとパスが読み込めなくなる。

$ vi $APP_PATH/routes/web.php

<?php

・・・

// ProductionならHTTPSに変更
if (app()->environment('production')) {
  URL::forceScheme('https');
}

これをすることで回避できる。

AWS SSMとECSコンテナの環境変数を利用して、

  • キー:APP_ENV
  • 値:production

となるようにデプロイすることでこの設定が有効になります。

 

duskを無効化する

$ vi $HOME/yomuyo/composer.json

    "extra": {
        "laravel": {
            "dont-discover": [
                "laravel/dusk" // ←●追加
            ]
        }
    },

 

 

Laravelのログを標準エラーに出力する

 

$ docker exec php-fpm composer require monolog/monolog
$ docker exec php-fpm composer dump-autoload

 

$ vi $APP_PATH/config/logging.php



・・・
        'develop' => [
            'driver' => 'monolog',
            'level' => 'debug',
            'handler' => StreamHandler::class,
            'formatter' => env('LOG_STDERR_FORMATTER'),
            'with' => [
                'stream' => 'php://stderr',
            ],
        ],
        'production' => [
            'driver' => 'monolog',
            'level' => 'notice',
            'handler' => StreamHandler::class,
            'formatter' => env('LOG_STDERR_FORMATTER'),
            'with' => [
                'stream' => 'php://stderr',
            ],
        ],



    ],

];

 

$ vi $HOME/yomuyo-docker-template/env/.env.local

・・・

## ログチャネル 開発:develop 本番:production
LOG_CHANNEL=develop

・・・

 

 

Dockerのログ確認

 

まずコンテナのIDを確認します。

$ docker ps

CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS              PORTS                  NAMES
41586672d05b        2467e09df54f                   "nginx -g 'daemon of…"   10 hours ago        Up 10 hours         0.0.0.0:80->80/tcp     yomuyo_nginx_1
f5f1bd32201a        phpmyadmin/phpmyadmin:latest   "/docker-entrypoint.…"   10 hours ago        Up 10 hours         0.0.0.0:8080->80/tcp   yomuyo_pma_1
6cff00670693        dbff472c899a                   "docker-php-entrypoi…"   10 hours ago        Up 10 hours         9000/tcp               yomuyo_php-fpm_1

 

リアルタイムでログを確認する

$ docker logs -f <コンテナID>

例)
$ docker logs -f 6cff00670693

[16-Jun-2019 10:55:36] NOTICE: fpm is running, pid 1
[16-Jun-2019 10:55:36] NOTICE: ready to handle connections
172.18.0.4 -  16/Jun/2019:11:33:20 +0000 "GET /index.php" 200
172.18.0.4 -  16/Jun/2019:12:23:55 +0000 "GET /index.php" 200
172.18.0.4 -  16/Jun/2019:14:04:45 +0000 "GET /index.php" 200

サイトにアクセスするたびに標準出力がログとして出ているか確認できる。

 

ファイル更新した場合のローカルでの確認

$ docker-compose up -d

これで更新される。

 

 

Laravel Whoops!での環境変数を隠す

 

これをやらないと「ひえっ」となる。

$ vi $APP_PATH/config/app.php

・・・
    'debug_blacklist' => [
        '_ENV' => [
            'APP_KEY',
        'URL' => Illuminate\Support\Facades\URL::class,
            'AMAZON_API_SECRET_KEY',
        'Config' => Illuminate\Support\Facades\Config::class,
            'SENDGRID_API_KEY',
            'MAIL_FROM_ADDRESS',
            'FACEBOOK_APP_ID',
            'FACEBOOK_APP_SECRET',
            'FACEBOOK_CALLBACK_URL',
            'TWITTER_APP_ID',
            'TWITTER_APP_SECRET',
            'TWITTER_APP_ACCESS_TOKEN',
            'TWITTER_APP_ACCESS_SECRET',
            'TWITTER_CALLBACK_URL',
        ],
        '_SERVER' => [
            'APP_KEY',
            'AWS_ACCESS_KEY_ID',
            'AWS_SECRET_ACCESS_KEY',
            'DB_DATABASE',
            'DB_USERNAME',
            'DB_PASSWORD',
            'REDIS_PASSWORD',
            'MAIL_PASSWORD',
            'PUSHER_APP_KEY',
            'PUSHER_APP_SECRET',
            'AMAZON_ASSOCIATE_TAG',
            'AMAZON_API_KEY',
            'AMAZON_API_SECRET_KEY',
            'SENDGRID_API_KEY',
            'MAIL_FROM_ADDRESS',
            'FACEBOOK_APP_ID',
            'FACEBOOK_APP_SECRET',
            'FACEBOOK_CALLBACK_URL',
            'TWITTER_APP_ID',
            'TWITTER_APP_SECRET',
            'TWITTER_APP_ACCESS_TOKEN',
            'TWITTER_APP_ACCESS_SECRET',
            'TWITTER_CALLBACK_URL',
        ],
        '_POST' => [
            'password',
        ],
    ],
・・・

 

スタックトレースを見せない

  1. .envでAPP_DEBUG=falseを設定
  2. 「php artisan config:clear」を実行

本番でのデプロイでは必ず自動実行されるようにすること。

 

 

AWS Systems Manager  パラーメタストアによる機密情報隠蔽化

 

.envに記載する内容をパラメータストアに格納します。パラメータはECSで参照されてコンテナの環境変数に渡します。

 

例えばこんな感じ

  • 名前
    /yomuyo/prod/ecs/database_master_host
  • 利用枠
    標準
  • KMS の主要なソース
    現在のアカウント
  • タイプ
    安全な文字列
  • KMS キー ID
    alias/aws/ssm

  • db-master.yomuyo.net

 

設定出来ました、パラメータグループの名前は階層化できるので階層化したほうがわかりやすいです。

ここからはCUIでどんどんパラメータを追加していきます。

 

AWS CLIでパラメータストアに登録

 

AWS CLIのインストール

$ curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
$ sudo python get-pip.py
$ sudo pip install awscli

 

IAMのプログラムアクセスユーザでIDとキーを取得して設定

# aws configure

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

 

パラメータストアに適用

echo "●APP_ENV"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/app_env \
  --value production \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●APP_KEY"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/app_key \
  --value <★Laravelで生成されるキー> \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●DB_CONNECTION"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/database_connection \
  --value mysql \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●DB_MASTER_HOST"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/database_master_host \
  --value db-master.yomuyo.net \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●DB_SLAVE_HOST"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/database_slave_host \
  --value db-master.yomuyo.net \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●DB_DATABASE"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/database_name \
  --value yomuyodb \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●DB_USERNAME"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/database_user_name \
  --value yomuyodbmaster \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●DB_DB_PASSWORD"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/database_password \
  --value <★RDSパスワード> \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●SESSION_DRIVER"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/session_driver \
  --value redis \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●SESSION_DOMAIN"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/session_domain \
  --value '.yomuyo.net' \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●REDIS_HOST"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/redis_host \
  --value elasticache-redis-primary.yomuyo.net \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●REDIS_PASSWORD"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/redis_password \
  --value null \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●REDIS_PORT"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/redis_port \
  --value 6379 \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●FACEBOOK_APP_ID"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/facebook_app_id \
  --value <★APP_ID> \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●FACEBOOK_APP_SECRET"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/facebook_app_secret \
  --value <★APP_SECRET> \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●FACEBOOK_CALLBACK_URL"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/facebook_callback_url \
  --value https://www.yomuyo.net/auth/callback/facebook \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●TWITTER_APP_ID"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/twitter_app_id \
  --value <★APP_ID> \
  --type SecureString \
  --key-id alias/aws/ssm
 
echo "●TWITTER_APP_SECRET"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/twitter_app_secret \
  --value <★APP_SECRET> \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●TWITTER_APP_ACCESS_TOKEN"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/twitter_app_access_token \
  --value <★アクセストークン> \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●TWITTER_APP_ACCESS_TOKEN_SECRET"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/twitter_app_access_token_secret \
  --value <★アクセストークンシークレット> \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●TWITTER_CALLBACK_URL"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/twitter_callback_url \
  --value https://www.yomuyo.net/auth/callback/twitter \
  --type SecureString \
  --key-id alias/aws/ssm


echo "●AWS_ACCESS_KEY_ID"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/aws_access_key_id \
  --value <★AWSキーID> \
  --type SecureString \
  --key-id alias/aws/ssm

echo "●AWS_SECRET_ACCESS_KEY"
aws ssm put-parameter \
  --region ap-northeast-1 \
  --name /yomuyo/prod/ecs/aws_secret_access_key_id \
  --value <★AWSシークレットキー> \
  --type SecureString \
  --key-id alias/aws/ssm

 

 

AWS S3とLaravelの連携

 

変数の設定

BUCKET_NAME=s3.yomuyo.net
S3_USER=s3-yomuyo-user
S3_POLICY=yomuyo-S3-FullAccess-Policy

S3 バケットはWEB公開する予定なので、バケット名はサブドメイン『s3.yomuyo.net』にします。

 

1. AWS S3 バケットの作成

バケットの作成

$ aws s3 mb s3://${BUCKET_NAME}

make_bucket: s3.yomuyo.net

 

WEB公開設定を許可します。

aws s3 website s3://${BUCKET_NAME} --index-document index.html

 

/yomuyo-img/*以下にWEBでの公開許可を与えます。

cat << EOF > ${BUCKET_NAME}-Public-Policy.json
{
  "Version":"2012-10-17",
  "Statement":[{
        "Sid":"PublicReadForGetBucketObjects",
        "Effect":"Allow",
          "Principal": {
            "AWS": "*"
         },
      "Action":["s3:GetObject"],
      "Resource":["arn:aws:s3:::${BUCKET_NAME}/*"
      ]
    }
  ]
}
EOF

 

設定の適用を実施します。

$ aws s3api put-bucket-policy --bucket ${BUCKET_NAME} --policy file://${BUCKET_NAME}-Public-Policy.json

 

※設定の確認

$ aws s3api get-bucket-policy --bucket ${BUCKET_NAME}

 

GUIで確認

 

s3.yomuyo.netバケットを確認出来ました。

公開設定もされていますね。

 

フォルダの作成

  • books
    本のサムネイル
  • profile
    ユーザプロフィールの画像

 

ACM バージニア

後で設定するCloudFront用の証明書に、ACM バージニアリージョンで証明書を作成する必要があります。

 

 

もちろんDNS検証。ウィザードに沿ってRoute53での認証リクエストを進めます。

 

 

発行されました。

 

CloudFront

 

 

 

 

 

 

 

 

 

『s3.yomuyo.net』にCloudFrontを紐づける。

 

ちゃんとCloudFrontから配信されていることがわかります。

 

 

2. AWS S3のyomuyo-imgバケットを操作可能なIAMユーザの作成

 

ポリシー設定用jsonファイルの作成

cat << EOF > ${S3_POLICY}.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
          "arn:aws:s3:::${BUCKET_NAME}",
          "arn:aws:s3:::${BUCKET_NAME}/*"
      ]
    }
  ]
}
EOF

 

ポリシーの作成

 

$ aws iam create-policy --policy-name ${S3_POLICY} --policy-document file://${S3_POLICY}.json

{
    "Policy": {
        "PolicyName": "yomuyo-S3-FullAccess-Policy",
        "PermissionsBoundaryUsageCount": 0,
        "CreateDate": "2019-07-21T10:58:46Z",
        "AttachmentCount": 0,
        "IsAttachable": true,
        "PolicyId": "xxxxxx",
        "DefaultVersionId": "v1",
        "Path": "/",
        "Arn": "arn:aws:iam::xxxxxx:policy/S3-Read-Write-Policy", ←●メモしておく
        "UpdateDate": "2019-07-21T10:58:46Z"
    }
}

“Arn”: “arn:aws:iam::xxxxxx:policy/S3-Read-Write-Policy”をメモしておきます

 

 

IAMユーザの作成

$ aws iam create-user --user-name ${S3_USER}

{
    "User": {
        "UserName": "s3-yomuyo-user",
        "Path": "/",
        "CreateDate": "2019-07-21T10:51:31Z",
        "UserId": "xxxxxxxxxxxxxxxxx",
        "Arn": "arn:aws:iam::xxxxxxxxxxxxxx:user/s3-yomuyo-user"
    }
}

 

ユーザにポリシーを紐づけます

aws iam attach-user-policy --user-name ${S3_USER} --policy-arn "arn:aws:iam::xxxxxx:policy/S3-Read-Write-Policy"

 

アクセス用の認証情報の作成

$ aws iam create-access-key --user-name ${S3_USER}

{
    "AccessKey": {
        "UserName": "s3-yomuyo-user",
        "Status": "Active",
        "CreateDate": "2019-07-21T11:00:57Z",
        "SecretAccessKey": "xxxxxx",
        "AccessKeyId": "xxxxxx"
    }
}

ユーザのシークレットキーとアクセスIDを控える

 

3. Laravel 設定

 

flysystem-aws-s3-v3のインストール

$ docker-compose exec php-fpm composer require league/flysystem-aws-s3-v3
$ docker-compose exec php-fpm composer dump-autoload

 

.envの設定

$ vi $HOME/yomuyo-docker-template/env/.env.local


・・・

## AWS S3
AWS_ACCESS_KEY_ID=xxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxx
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=s3.yomuyo.net

先ほど得た認証情報を記入します。

 

4. Laravel から S3への投稿

 

/app/Http/Controllers/HomeController.php

$ vi $CONT_PATH/HomeController.php


<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Review;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        // authミドルウェア生成.認証の有無ををチェックする。
        // このコントローラで利用する関数すべてに影響を与える
        $this->middleware('auth');
    }

・・・
・・・

    /**
    * 感想の投稿
    *
    */
    public function post(Request $request)
    {
        $user = Auth::user();
        $review = new Review();
        $val = $review->create($request);

        if($val == true){
            return view('mypage.index', ['user' => $user, 'item' => $request]);
        }
        else {
            echo "投稿に失敗しました";
            exit();
        }

    }

}

 

 

/app/Models/Review.php

$ vi $APP_PATH/app/Models/Review.php


<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;      // ←追加 ●DBを操作するのにこれは必須
use Illuminate\Http\Request;            // ←追加 ●きっと後で使うよ
use Storage;                            // AWS S3アクセス league/flysystem-aws-s3-v3
use Illuminate\Support\Facades\Log;     // ログ
use Illuminate\Support\Facades\Cache;   // キャッシュファサード

class Review extends Model
{
    protected $table      = 'reviews';      // テーブル名
    protected $primaryKey = 'id';           // PK
    protected $guarded    = array('id');    // PK


・・・
・・・

   /**  ============================
    *    レビューを投稿
    *   ============================
    *   @param Request $request
    *   @return boolean
    */
    public function create(Request $request)
    {
        $request = $request->all();
        unset($request['_token']); //トークン削除

        $user = \Auth::user(); // ログインユーザID取得
        $user_id = $user->id;

        if( isset($request['netabare_flag']) ){
            $request['netabare_flag'] = 1;
        }else{
            $request['netabare_flag'] = 0;
        }


        $jpg = $request['google_book_id'] . '.jpg';


        $notdone = (bool) true; // 初期値
        $retry   = 0;           // リトライ初期値
        $limit   = 3;           // リトライ最大回数閾値
        while( $notdone && $retry < $limit)
        {
            try{
                    // トランザクションスタート!
                    DB::beginTransaction();

                    // books テーブにデータを保存
                    $books_param = [
                        "google_book_id" => $request['google_book_id'], // Googl Books ID
                        "name"           => $request['title'],          // 本のタイトル
                        "thumbnail"      => $jpg,                       // 本のサムネイル
                        'created_at'     => now(),
                        'updated_at'     => now(),
                    ];

                    // 本のレコードがなければ挿入
                    $result = (bool) DB::table('books')->where('google_book_id', $request['google_book_id'])->exists();
                    if($result == false)
                    {
                        DB::insert('INSERT INTO books (google_book_id, name, thumbnail, created_at, updated_at)
                                            VALUES(:google_book_id, :name, :thumbnail, :created_at, :updated_at)', $books_param);
                        // booksテーブルに挿入したレコードのid(主キー)を取得
                        $id = DB::getPdo()->lastInsertId();

                    // 本のレコードが既にある場合は該当の本のidを取得
                    }else{
                        $rec = DB::table('books')->where('google_book_id', $request['google_book_id'])->get();
                        foreach($rec as $key){
                            $id  = (int) $key->id;
                        }
                    }


                    // reviewsテーブルに保存
                    $reviews_param = [
                        "book_id"        => $id,                         // booksテーブルid
                        "user_id"        => $user_id,                    // ユーザID
                        "netabare_flag"  => $request['netabare_flag'],   // ネタばれフラグ
                        "user_ip"        => \Request::ip(),              // アクセスIP
                        "comment"        => $request['comment'],         // 感想
                        'created_at'     => now(),
                        'updated_at'     => now(),
                    ];

                    DB::insert('INSERT INTO reviews (book_id, user_id, netabare_flag, user_ip, comment, created_at, updated_at)
                                        VALUES(:book_id, :user_id, :netabare_flag, :user_ip, :comment, :created_at, :updated_at)', $reviews_param);


                    // 本のサムネイルをAWS S3 バケット(s3.yomuyo.net/books/)に保存
                    $thumbnail_url = $request['thumbnail'] . '&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api';
                    $img = file_get_contents($thumbnail_url);
                    $id  = $request['google_book_id'];
                    $disk = Storage::disk('s3')->put("books/{$id}.jpg", $img, 'public');

                    // 成功処理
                    DB::commit();
                    return true;
            }catch(\PDOException $e){
                // 失敗処理 : ロールバック。$limit回数まで試行できる
                DB::rollBack();
                $retry++;
            }

        }//while

        // トランザクション処理がリトライ回数の閾値を超えたらエラーを通知して処理を止める
        if($retry == $limit)
        {
            echo get_class() . ':register() PDOException Error. Rollback was executed.' . $e;
            Log::error(get_class() . ':register() PDOException Error. Rollback was executed.' . $e);
            return false;
        }

    }// create()

}
  • users←reviews→books

reviewsテーブルは中間テーブルになっている。

usersとbooksは多対多の関係なので、reviewsテーブルが必要。

 

 

画像アップロードの確認

 

 

 

 

 

ECS実行用ロールの作成 「ecsTaskExecutionRole」

 

ポリシーの付与を行う

  • AWS管理ポリシー
    AmazonECSTaskExecutionRolePolicy
  • インラインポリシー
    SSM-Value-Get-Policy

 

SSM-Value-Get-Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters",
        "secretsmanager:GetSecretValue",
        "kms:Decrypt"
      ],
      "Resource": "*"
    }
  ]
}

 

 

 

ECR

 

 

 

 

 

 

ECRにログイン

$ $(aws ecr get-login --no-include-email --region ap-northeast-1)

WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

 

イメージの確認

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
yomuyo_nginx        latest              7fc8153010fc        12 minutes ago      110MB
yomuyo_php-fpm      latest              d8a03926676b        13 minutes ago      499MB
php                 7-fpm               86cc2acebfae        2 days ago          367MB
nginx               latest              719cd2e3ed04        4 days ago          109MB

 

ECRにイメージにタグ付け

$ docker tag yomuyo_nginx:latest xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yomuyo/nginx:latest
$ docker tag yomuyo_php-fpm:latest xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yomuyo/php-fpm:latest

URI:タグという形で設定できます。

 

ECRにPushします。

$ docker push xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yomuyo/nginx:latest
$ docker push xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yomuyo/php-fpm:latest

 

こんな風にイメージがECRに格納されます。

 

 

ECS

 

タスク定義

  • タスク定義名:yomuyo

 

 

 

EC2を選択して【次のステップ】

 

 

タスクロールをきちんと指定します。AWS System managerのパラメータグループから値を受け取るのに必要です。

 

 

コンテナの追加

 

 

PHP-FPMコンテナ

  • コンテナ名:php-fpm
  • イメージ:xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yomuyo/php-fpm:latest
  • メモリ制限:ハード制限 300(MB)
  • 作業ディレクトリ:/app

※イメージを指定すること

 

 

ログの設定を行います。

 

 

ここでパラメータストアで設定した値が生きてきます。

 

Nginxコンテナ

  • コンテナ名:nginx
  • イメージ:xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yomuyo/nginx:latest
  • メモリ制限:ハード制限 300(MB)
  • ポートマッピング(TCP):0 , 80
  • リンク:php-fpm

 

ホストポートは「0」、コンテナは80にします。

80, 80にするとデプロイで失敗します。

 

ログの設定を行います。

 

 

 

 

 

 

 

【作成】をクリックします。

 

 

 

 

 

 

クラスタの作成

 

 

 

  • メモリなどのスペック不足でECSでうまく動かなかったりする、EC2インスタンスタイプは【t2.small】以上のインスタンスを選ぶようにする。
  • キーペアの指定
    ステージングと本番はFargateでログインをしないが,
    開発はEC2でSSHによる状態確認をしやすいようにしたほうが開発しやすい。

 

 

 

 

ALB

 

 

 

ECSのインスタンスが利用しているVPCを選択する。

 

 

 

ターゲットに登録して、【次の手順】へ。

 

【作成】で完了

 

 

 

ALBからデプロイ時間の調整(重要)

【ターゲットグループ】から【属性の編集】をクリックします。

 

「300」秒から「30」秒へ変更しました。

これでECSへのデプロイ完了が速くなります。

 

サービスの登録

 

 

 

 

 

新規作成のところを選択して、

 

こうする。

 

 

 

 

 

 

 

 

 

ALBとECSとの連携での重要な設定

ECSのEC2インスタンスのセキュリティグループのインバウンドにALBのSGを全許可受け入れを行う。こうすることでECSが行うコンテナへの動的ポートマッピングに対応しできる。公式にも書いてある。

ユーザの80番アクセスがALBに渡り、ALBからクラスタ化されたコンテナに伝わる。

 

ALB経由でアクセスを確認

http://yomuyo-alb-xxxxxxxx.ap-northeast-1.elb.amazonaws.com/

 

GitHub

GitHub上で「yomuyo.git」リポジトリを作る

 

GitHubにアップロードできるようにする

# vi $HOME/.ssh/id_rsa_GitHub

-----BEGIN RSA PRIVATE KEY-----

・・・

-----END RSA PRIVATE KEY-----

 

$ vi $HOME/.ssh/config


Host github.com
    User git
    Port 22
    HostName github.com
    IdentityFile ~/.ssh/id_rsa_GitHub
    IdentitiesOnly yes
    Compression yes

 

$ chmod 400 $HOME/.ssh/config
$ chmod 600 $HOME/.ssh/id_rsa_GitHub

 

リポジトリにpushする

cd $HOME/yomuyo-docker-template/src/yomuyo/
echo "# yomuyo" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:yuukanehiro/yomuyo.git
git push -u origin master

pushできたらおーけ。

 

個人設定

$ git config --global user.name "yuukanehiro"
$ git config --global user.email <メールアドレス>
$ git config --global core.autoCRLF false

 

$ git add .
$ git commit -m "Laravel APP Default"
$ git push -u origin master

Counting objects: 9201, done.
Compressing objects: 100% (6086/6086), done.
Writing objects: 100% (9201/9201), 10.25 MiB | 3.12 MiB/s, done.
Total 9201 (delta 2711), reused 9201 (delta 2711)
remote: Resolving deltas: 100% (2711/2711), done.
To github.com:yuukanehiro/yomuyo.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

 

developブランチ

developブランチの作成

$ git branch develop

 

developブランチに移動する

$ git checkout develop

M       app/Http/Controllers/BookController.php
M       public/css/style.css
M       resources/lang/ja.json
M       resources/views/auth/login.blade.php
M       resources/views/book/result.blade.php
M       resources/views/layouts/layout.blade.php
M       resources/views/layouts/partials/header.blade.php
M       routes/web.php
Switched to branch 'develop'

 

現在のブランチの確認

$ git branch

* develop
  master

 

developブランチにpushする

$ git push origin develop

 

 

 

 

CodePipeline

【パイプラインを作成する】をクリックします。

 

※なんでCircleCIじゃないの?GitHub + CodePipeline + CodeBuild + ECR + ECSの理由

  • CircleCIは複数Dockerfileでの設定は辛い。
    ただし1つだけのDockerfileであればCircleCI Orbsを利用すると簡単。
  • AWS CodePipelineを利用すればセキュリティが向上する
    認証キーなどの環境変数をCircleCI上に保存するよりセキュア。

そういうわけで今回はCodePipelineで構築、お客様の要望によってCircleCIは触らざるを得ないという辛みがある。

 

 

 

 

 

 

CodeBuild

 

  • AmazonEC2ContainerRegistryFullAccess
  • AmazonEC2ContainerServiceFullAccess

 

  • AWS_ACCOUNT_ID
    9xxxxxx7
  • AWS_DEFAULT_REGION
    ap-northeast-1
  • PROJECT_NAMESPACE
    yomuyo

 

 

これでCodeBuildはおしまい。

 

CodePipelineに戻る

 

 

 

Deployまで成功すればOK

 

※エラー対応

[Container] 2019/06/14 16:22:45 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: $(aws ecr get-login –no-include-email –region ap-northeast-1. Reason: exit status 2

ロールにポリシーが付与されていないことが原因

ロールを確認して、IAMからポリシーを付与する。

ここのロールに後でポリシーをつけよう。

 

  • AmazonEC2ContainerRegistryFullAccess
  • AmazonEC2ContainerServiceFullAccess

ポリシーを付与する

 

CodePipelineから確認して、ECSへのデプロイまで完了したか確認出来たら完了。

 

自動デプロイ環境が出来たので、アプリ開発に集中しましょう~!

 

ポートフォリオ制作なうなんだが…その2

 

 

 

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)