最短で完成を目指す。
※雑記。現状は参考?にするのはおすすめしません。最後に整理します。
もくじ
内容
本の感想投稿掲示板 + 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
拡張プラグイン
- Remote-SSH
SSH接続 - PHP Intelephense
PHP用サジェスト - Bracket Pair Colorizer
{}の対応を色でペアにしてくれる - vscode-icons
アイコンがついて見やすくなる - GitLens
- Git History
- GitHub Pull Requests
- Trailing Spaces
行末のスペースのマークアップ - Rainbow CSV
CSVがマークアップされる - REST Client
デバッグしやすくなる - Output Colorizer
ログなどの出力のマークアップ
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(本番)環境変数のデプロイ
- AWS SSMのパラメータストアにAWS KMSで暗号化させた値を保存
- 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へアップするファイルを指定
時刻、タイムゾーン設定
$ 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', ], ], ・・・
スタックトレースを見せない
- .envでAPP_DEBUG=falseを設定
- 「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へのデプロイまで完了したか確認出来たら完了。
自動デプロイ環境が出来たので、アプリ開発に集中しましょう~!