Merge branch 'docker'

This commit is contained in:
ErickSkrauch 2016-05-10 01:55:37 +03:00
commit 455d7b9469
28 changed files with 710 additions and 48 deletions

16
.dockerignore Normal file
View File

@ -0,0 +1,16 @@
.git/*
.env
# vendor будет заполнен уже внутри контейнера
vendor
# frontend и его node_modules внутри контейнера не нужны
frontend
# Все -local файлы
*/config/*-local.php
# Все временные файлы
runtime
# Их по идее и не должно образовываться, но мало ли
api/web/assets

3
.gitignore vendored
View File

@ -32,3 +32,6 @@ phpunit.phar
# npm debug
npm-debug*
# Docker override file
docker-compose.override.yml

61
Dockerfile Normal file
View File

@ -0,0 +1,61 @@
FROM php:7.0-fpm
ENV PATH $PATH:/root/.composer/vendor/bin
# PHP extensions come first, as they are less likely to change between Yii releases
RUN apt-get update \
&& apt-get -y install \
git \
g++ \
libicu-dev \
libmcrypt-dev \
zlib1g-dev \
--no-install-recommends \
# Install PHP extensions
&& docker-php-ext-install intl \
&& docker-php-ext-install pdo_mysql \
&& docker-php-ext-install mbstring \
&& docker-php-ext-install mcrypt \
&& docker-php-ext-install zip \
&& docker-php-ext-install bcmath \
&& apt-get purge -y g++ \
&& apt-get autoremove -y \
&& rm -r /var/lib/apt/lists/* \
# Don't clear our valuable environment vars in PHP
&& echo "\nclear_env = no" >> /usr/local/etc/php-fpm.conf \
# Fix write permissions with shared folders
&& usermod -u 1000 www-data
# Поставим xdebug отдельно, т.к. потом его потенциально придётся отсюда убирать
RUN yes | pecl install xdebug \
&& echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" > /usr/local/etc/php/conf.d/xdebug.ini \
&& echo "xdebug.remote_enable=on" >> /usr/local/etc/php/conf.d/xdebug.ini \
&& echo "xdebug.remote_autostart=off" >> /usr/local/etc/php/conf.d/xdebug.ini
# Next composer and global composer package, as their versions may change from time to time
RUN curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer.phar \
&& composer.phar global require --no-progress "fxp/composer-asset-plugin:~1.1.4" \
&& composer.phar global require --no-progress "hirak/prestissimo:~0.2.2"
COPY ./docker/php/composer.sh /usr/local/bin/composer
RUN chmod a+x /usr/local/bin/composer
WORKDIR /var/www/html
# Copy the working dir to the image's web root
COPY . /var/www/html
# The following directories are .dockerignored to not pollute the docker images
# with local logs and published assets from development. So we need to create
# empty dirs and set right permissions inside the container.
RUN mkdir api/runtime api/web/assets console/runtime \
&& chown www-data:www-data api/runtime api/web/assets console/runtime
# Expose everything under /var/www (vendor + html)
# This is only required for the nginx setup
VOLUME ["/var/www"]

107
README.md
View File

@ -1,54 +1,71 @@
Yii 2 Advanced Project Template
===============================
# Account Ely.by
Yii 2 Advanced Project Template is a skeleton [Yii 2](http://www.yiiframework.com/) application best for
developing complex Web applications with multiple tiers.
## Развёртывание dev
The template includes three tiers: front end, back end, and console, each of which
is a separate Yii application.
Предварительно нужно установить [git](https://git-scm.com/downloads),
[docker](https://docs.docker.com/engine/installation/) и его
[docker-compose](https://docs.docker.com/compose/install/).
The template is designed to work in a team development environment. It supports
deploying the application in different environments.
Сливаем репозиторий:
Documentation is at [docs/guide/README.md](docs/guide/README.md).
```sh
git clone git@bitbucket.org:ErickSkrauch/ely.by-account.git account.ely.by.local
cd account.ely.by.local
```
[![Latest Stable Version](https://poser.pugx.org/yiisoft/yii2-app-advanced/v/stable.png)](https://packagist.org/packages/yiisoft/yii2-app-advanced)
[![Total Downloads](https://poser.pugx.org/yiisoft/yii2-app-advanced/downloads.png)](https://packagist.org/packages/yiisoft/yii2-app-advanced)
[![Build Status](https://travis-ci.org/yiisoft/yii2-app-advanced.svg?branch=master)](https://travis-ci.org/yiisoft/yii2-app-advanced)
Выполняем первый запуск контейнеров:
DIRECTORY STRUCTURE
-------------------
```sh
docker-compose up -d
```
Далее нужно влезть в работающие контейнеры и сделать что-нибудь, что их настроит.
### Как влезть в работающий контейнер
Сперва, с помощью команды `docker ps` мы увидим все запущенные контейнеры. Нас интересуют значения из первой колонки
CONTAINER ID. Узнать, чему они соответствуют можно прочитав название IMAGE из 2 колонки. Чтобы выполнить команду
внутри работабщего контейнера, нужно выполнить:
```
common
config/ contains shared configurations
mail/ contains view files for e-mails
models/ contains model classes used in both backend and frontend
console
config/ contains console configurations
controllers/ contains console controllers (commands)
migrations/ contains database migrations
models/ contains console-specific model classes
runtime/ contains files generated during runtime
backend
assets/ contains application assets such as JavaScript and CSS
config/ contains backend configurations
controllers/ contains Web controller classes
models/ contains backend-specific model classes
runtime/ contains files generated during runtime
views/ contains view files for the Web application
web/ contains the entry script and Web resources
frontend
assets/ contains application assets such as JavaScript and CSS
config/ contains frontend configurations
controllers/ contains Web controller classes
models/ contains frontend-specific model classes
runtime/ contains files generated during runtime
views/ contains view files for the Web application
web/ contains the entry script and Web resources
widgets/ contains frontend widgets
vendor/ contains dependent 3rd-party packages
environments/ contains environment-based overrides
tests contains various tests for the advanced application
codeception/ contains tests developed with Codeception PHP Testing Framework
docker exec -it a7c267b27f49 /bin/bash
```
Где `a7c267b27f49` - одно из значений из первой колонки. Для выхода из контейнера используем `exit`.
-------------------------
Так вот, нам нужно выполнить ряд команд. Здесь и далее я буду писать имена контейнеров в их соответствии с compose
файлом, но в реалиях их нужно будет заменить на реальные значения:
```sh
# Настройка php контейнера
docker exec -it app php init --env=Docker
docker exec -it app php composer install
docker exec -it app php ./yii migrate --interactive=0
# Настройка node контейнера
docker exec -it node-dev-server npm i
docker exec -it node-dev-server npm --prefix ./webpack i ./webpack
docker exec -it node-dev-server npm --prefix ./scripts i ./scripts
# Настройка rabbitmq контейнера
docker exec -it rabbitmq /init.sh
```
После этого перезапускаем все контейнеры командой:
```sh
docker-compose restart
```
## Тестирование php бэкэнда
```sh
# Прежде чем тестировать, необходимо накатить миграции
docker exec -it db6366f120ee php tests/codeception/bin/yii migrate --interactive=0
# Собрать все тестовые окружения
docker exec -it db6366f120ee /bin/sh -c 'cd tests; ./../vendor/bin/codecept build'
# И запустить собственно процесс тестирования
docker exec -it db6366f120ee /bin/sh -c 'cd tests; ./../vendor/bin/codecept run'
```

62
docker-compose.yml Normal file
View File

@ -0,0 +1,62 @@
version: '2'
services:
app:
links:
- db
- redis
- rabbitmq
depends_on:
- app-console-account-queue
environment:
ENABLE_ENV_FILE: 1
ENABLE_LOCALCONF: 1
web:
build: ./docker/nginx
ports:
- "80:80"
links:
- app
volumes_from:
- app
app:
build: .
expose:
- "9000"
volumes:
- ./:/var/www/html/
environment:
ENABLE_ENV_FILE: 1
ENABLE_LOCALCONF: 1
node-dev-server:
build: ./frontend
ports:
- "8080:8080"
volumes:
- ./frontend/:/usr/src/app/
environment:
DOCKERIZED: "true"
app-console-account-queue:
build: .
volumes:
- ./:/var/www/html/
command: ./docker/wait-for-it.sh rabbitmq:5672 -- ./yii account-queue
links:
- db
- redis
- rabbitmq
db:
build: ./docker/mariadb
redis:
image: redis:3.0
rabbitmq:
build: ./docker/rabbitmq
ports:
- "15672:15672"

13
docker/mariadb/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM mariadb:10.0
COPY mariadb.cnf /etc/mysql/conf.d
# Add script to create default users / vhosts
ADD init.sh /init.sh
ADD run.sh /run.sh
# Run rabbitmq, execute init configuration and then shutdown
RUN chmod +x /init.sh /run.sh \
&& /init.sh
ENTRYPOINT "/run.sh"

34
docker/mariadb/init.sh Normal file
View File

@ -0,0 +1,34 @@
#!/bin/bash
# Копипаста. Я не знаю, что тут происходит
set -e
set -x
mysql_install_db
# Start the MySQL daemon in the background.
/usr/sbin/mysqld &
mysql_pid=$!
until mysqladmin ping &>/dev/null; do
echo -n "."; sleep 0.2
done
# Конец рандомной копипасты
# Устаналиваем беспарольный доступ для рута
mysql -e "GRANT ALL ON *.* TO root@'%' IDENTIFIED BY '' WITH GRANT OPTION"
# Создаём базу данных для приложения и для тестов
mysql -e "CREATE DATABASE IF NOT EXISTS ely_accounts CHARACTER SET utf8 COLLATE utf8_general_ci"
mysql -e "CREATE DATABASE IF NOT EXISTS ely_accounts_test CHARACTER SET utf8 COLLATE utf8_general_ci"
# Tell the MySQL daemon to shutdown.
mysqladmin shutdown
# Wait for the MySQL daemon to exit.
wait $mysql_pid
# Сохраняем состояние базы данных
tar czvf default_mysql.tar.gz /var/lib/mysql

View File

9
docker/mariadb/run.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
set -e
set -x
# first, if the /var/lib/mysql directory is empty, unpack it from our predefined db
[ "$(ls -A /var/lib/mysql)" ] && echo "Running with existing database in /var/lib/mysql" || ( echo 'Populate initial db'; tar xpzvf default_mysql.tar.gz )
/usr/sbin/mysqld

3
docker/nginx/Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM nginx:1.9
COPY nginx.conf /etc/nginx/nginx.conf

67
docker/nginx/nginx.conf Normal file
View File

@ -0,0 +1,67 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
set $root_path '/var/www/html';
set $api_path '${root_path}/api/web';
set $frontend_path '${root_path}/frontend/dist';
root $root_path;
charset utf-8;
client_max_body_size 100M;
location / {
alias $frontend_path;
index index.html;
try_files $uri /index.html =404;
}
location /api {
try_files $uri /api/web/index.php?$args;
}
location ~* \.php$ {
fastcgi_pass app:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Раздача статики для frontend
location ~* ^.+\.(html|jpg|jpeg|gif|png|svg|js|json|css|zip|rar|eot|ttf|woff|ico) {
root $frontend_path;
expires max;
access_log off;
}
location ~* \.(htaccess|htpasswd|svn|git) {
deny all;
}
}
}

8
docker/php/composer.sh Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
if [ -n "$API_TOKEN" ]
then
php /usr/local/bin/composer.phar config -g github-oauth.github.com $API_TOKEN
fi
exec php /usr/local/bin/composer.phar "$@"

View File

@ -0,0 +1,10 @@
FROM rabbitmq:3.6
RUN rabbitmq-plugins enable rabbitmq_management \
&& rabbitmq-plugins enable rabbitmq_web_stomp \
&& rabbitmq-plugins enable rabbitmq_mqtt
# Add script to create default users / vhosts
ADD init.sh /init.sh
RUN chmod +x /init.sh

44
docker/rabbitmq/init.sh Normal file
View File

@ -0,0 +1,44 @@
#!/bin/sh
#( sleep 10 ; \
#
## Create users
#rabbitmqctl add_user ely-accounts-app app-password ; \
#rabbitmqctl add_user ely-accounts-tester tester-password ; \
#
## Set user rights
#rabbitmqctl set_user_tags ely-accounts-app administrator ; \
#rabbitmqctl set_user_tags ely-accounts-tester administrator ; \
#
## Create vhosts
#rabbitmqctl add_vhost /account.ely.by ; \
#rabbitmqctl add_vhost /account.ely.by/tests ; \
#
## Set vhost permissions
#rabbitmqctl set_permissions -p /account.ely.by ely-accounts-app ".*" ".*" ".*" ; \
#rabbitmqctl set_permissions -p /account.ely.by/tests ely-accounts-tester ".*" ".*" ".*" ; \
#) &
#rabbitmq-server $@
#service rabbitmq-server start
# Create users
rabbitmqctl add_user ely-accounts-app app-password
rabbitmqctl add_user ely-accounts-tester tester-password
# Set user rights
rabbitmqctl set_user_tags ely-accounts-app administrator
rabbitmqctl set_user_tags ely-accounts-tester administrator
# Create vhosts
rabbitmqctl add_vhost /account.ely.by
rabbitmqctl add_vhost /account.ely.by/tests
# Set vhost permissions
rabbitmqctl set_permissions -p /account.ely.by ely-accounts-app ".*" ".*" ".*"
rabbitmqctl set_permissions -p /account.ely.by/tests ely-accounts-tester ".*" ".*" ".*"
#service rabbitmq-server stop
# Сохраняем состояние рэбита
#tar czvf default_rabbitmq.tar.gz /var/lib/rabbitmq/mnesia

8
docker/rabbitmq/run.sh Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
set -x
[ "$(ls -A /var/lib/rabbitmq/mnesia)" ] && echo "Running with existing rabbitmq in /var/lib/rabbitmq" || ( echo 'Populate initial rabbitmq'; tar xpzvf default_rabbitmq.tar.gz )
rabbitmq-server

161
docker/wait-for-it.sh Executable file
View File

@ -0,0 +1,161 @@
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
cmdname=$(basename $0)
echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $TIMEOUT -gt 0 ]]; then
echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT"
else
echoerr "$cmdname: waiting for $HOST:$PORT without a timeout"
fi
start_ts=$(date +%s)
while :
do
(echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1
result=$?
if [[ $result -eq 0 ]]; then
end_ts=$(date +%s)
echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds"
break
fi
sleep 1
done
return $result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $QUIET -eq 1 ]]; then
timeout $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
else
timeout $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
fi
PID=$!
trap "kill -INT -$PID" INT
wait $PID
RESULT=$?
if [[ $RESULT -ne 0 ]]; then
echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT"
fi
return $RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
hostport=(${1//:/ })
HOST=${hostport[0]}
PORT=${hostport[1]}
shift 1
;;
--child)
CHILD=1
shift 1
;;
-q | --quiet)
QUIET=1
shift 1
;;
-s | --strict)
STRICT=1
shift 1
;;
-h)
HOST="$2"
if [[ $HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
HOST="${1#*=}"
shift 1
;;
-p)
PORT="$2"
if [[ $PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
PORT="${1#*=}"
shift 1
;;
-t)
TIMEOUT="$2"
if [[ $TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
CLI="$@"
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$HOST" == "" || "$PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
TIMEOUT=${TIMEOUT:-15}
STRICT=${STRICT:-0}
CHILD=${CHILD:-0}
QUIET=${QUIET:-0}
if [[ $CHILD -gt 0 ]]; then
wait_for
RESULT=$?
exit $RESULT
else
if [[ $TIMEOUT -gt 0 ]]; then
wait_for_wrapper
RESULT=$?
else
wait_for
RESULT=$?
fi
fi
if [[ $CLI != "" ]]; then
if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then
echoerr "$cmdname: strict mode, refusing to execute subprocess"
exit $RESULT
fi
exec $CLI
else
exit $RESULT
fi

View File

@ -0,0 +1,24 @@
<?php
$config = [
'components' => [
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => '',
],
'reCaptcha' => [
'secret' => '',
],
],
];
if (!YII_ENV_TEST) {
// configuration adjustments for 'dev' environment
$config['bootstrap'][] = 'debug';
$config['modules']['debug'] = \yii\debug\Module::class;
$config['bootstrap'][] = 'gii';
$config['modules']['gii'] = \yii\gii\Module::class;
}
return $config;

View File

@ -0,0 +1,4 @@
<?php
return [
'jwtSecret' => 'some-long-secret-key',
];

View File

@ -0,0 +1,18 @@
<?php
// NOTE: Make sure this file is not accessible when deployed to production
if (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
die('You are not allowed to access this file.');
}
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'test');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');
$config = require(__DIR__ . '/../../tests/codeception/config/api/acceptance.php');
(new yii\web\Application($config))->run();

View File

@ -0,0 +1,18 @@
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../../common/config/bootstrap.php');
require(__DIR__ . '/../config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../../common/config/main.php'),
require(__DIR__ . '/../../common/config/main-local.php'),
require(__DIR__ . '/../config/main.php'),
require(__DIR__ . '/../config/main-local.php')
);
$application = new yii\web\Application($config);
$application->run();

View File

@ -0,0 +1,26 @@
<?php
return [
'components' => [
'db' => [
'dsn' => 'mysql:host=db;dbname=ely_accounts',
'username' => 'root',
'password' => '',
],
'mailer' => [
'useFileTransport' => true,
],
'redis' => [
'hostname' => 'redis',
'password' => null,
'port' => 6379,
'database' => 0,
],
'amqp' => [
'host' => 'rabbitmq',
'port' => 5672,
'user' => 'ely-accounts-app',
'password' => 'app-password',
'vhost' => '/account.ely.by',
],
],
];

View File

@ -0,0 +1,4 @@
<?php
return [
'fromEmail' => 'account@ely.by',
];

View File

@ -0,0 +1,7 @@
<?php
return [
'bootstrap' => ['gii'],
'modules' => [
'gii' => \yii\gii\Module::class,
],
];

View File

@ -0,0 +1,3 @@
<?php
return [
];

28
environments/docker/yii Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env php
<?php
/**
* Yii console bootstrap file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/common/config/bootstrap.php');
require(__DIR__ . '/console/config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/common/config/main.php'),
require(__DIR__ . '/common/config/main-local.php'),
require(__DIR__ . '/console/config/main.php'),
require(__DIR__ . '/console/config/main-local.php')
);
$application = new yii\console\Application($config);
$exitCode = $application->run();
exit($exitCode);

View File

@ -43,6 +43,20 @@ return [
'api/config/main-local.php',
],
],
'Docker' => [
'path' => 'docker',
'setWritable' => [
'api/runtime',
'api/web/assets',
],
'setExecutable' => [
'yii',
'tests/codeception/bin/yii',
],
'setCookieValidationKey' => [
'api/config/main-local.php',
],
],
'Production' => [
'path' => 'prod',
'setWritable' => [

View File

@ -11,11 +11,11 @@ modules:
Yii2:
configFile: '../config/api/functional.php'
Redis:
host: localhost
host: redis
port: 6379
database: 1
AMQP:
host: localhost
host: rabbitmq
port: 5672
username: 'ely-accounts-tester'
password: 'tester-password'

View File

@ -14,7 +14,7 @@ return [
],
'components' => [
'db' => [
'dsn' => 'mysql:host=localhost;dbname=ely_accounts_test',
'dsn' => 'mysql:host=db;dbname=ely_accounts_test',
],
'mailer' => [
'useFileTransport' => true,