По-долу е изчерпателна инструкция за това как да настроите и разположите Django приложение в Amazon Web Services (AWS), като същевременно останете здрави.
Използвани инструменти/технологии:
- Python v2.7.8
- Django v1.7
- Amazon Elastic Beanstalk, EC2, S3 и RDS
- EB CLI 3.x
- PostgreSQL
Сега с Python 3! Вижте актуализираната версия на тази статия тук.
Тази статия беше актуализирана, за да покрие внедряването с Python 3, защото AWS вече има много любов към Python 3.
Elastic Beanstalk срещу EC2
Elastic Beanstalk е платформа като услуга (PaaS), която рационализира настройката, внедряването и поддръжката на вашето приложение в Amazon AWS. Това е управлявана услуга, свързваща сървъра (EC2), базата данни (RDS) и вашите статични файлове (S3). Можете бързо да внедрите и управлявате приложението си, което автоматично се мащабира с разрастването на вашия сайт. Вижте официалната документация за повече информация.
Първи стъпки
Ще използваме просто приложение „Изображение на деня“, което можете да вземете от това хранилище:
$ git clone https://github.com/realpython/image-of-the-day.git
$ cd image-of-the-day/
$ git checkout tags/start_here
След като изтеглите кода, създайте virtualenv и инсталирайте изискванията чрез pip:
$ pip install -r requirements.txt
След това, когато PostgreSQL работи локално, настройте нова база данни с име iotd
. Също така, в зависимост от вашата локална конфигурация на Postgres, може да се наложи да актуализирате DATABASES
конфигурация в settings.py .
Например, актуализирах конфигурацията на:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'iotd',
'USER': '',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': '5432',
}
}
Сега можете да настроите схемата на базата данни, да създадете суперпотребител и да стартирате приложението:
$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver
Отидете до администраторската страница във вашия браузър на адрес http://localhost:8000/admin и добавете ново изображение, което след това ще се покаже на главната страница.
Приложението не е предназначено да бъде много вълнуващо; ние просто го използваме за демонстрационни цели. Всичко, което прави, е да ви позволи да качите изображение през администраторския интерфейс и да го покажете на цял екран на главната страница. Въпреки това, въпреки че това е сравнително основно приложение, то все пак ще ни позволи да проучим редица „проблеми“, които съществуват при внедряването в Amazon Beanstalk и RDS.
Сега, когато сайтът е готов и работи на нашата локална машина, нека започнем процеса на внедряване на Amazon.
CLI за AWS Elastic Beanstalk
За да работим с Amazon Elastic Beanstalk, можем да използваме пакет, наречен awsebcli. Към момента на писане най-новата версия на е 3.0.10 и препоръчителният начин за инсталирането й е с pip:
$ pip install awsebcli
Не използвайте brew за инсталиране на този пакет. Към момента на писане той инсталира v2.6.3, която е разбита по фини начини, което ще доведе до сериозно разочарование.
Сега тествайте инсталацията, за да се уверите, че работи:
$ eb --version
Това трябва да ви даде хубав номер на версията 3.x:
EB CLI 3.0.10 (Python 2.7.8)
За да започнете да използвате Elastic Beanstalk, ще ви трябва акаунт в AWS (изненада!). Регистрирайте се (или влезте).
Конфигуриране на EB – Инициализирайте приложението си
С работата на AWS Elastic Beanstalk CLI, първото нещо, което искаме да направим, е да създадем среда Beanstalk, в която да хостваме приложението. Изпълнете това от директорията на проекта („изображение на деня“):
$ eb init
Това ще ви подскаже с редица въпроси, които да ви помогнат да конфигурирате вашата среда.
Регион по подразбиране
Изборът на региона, който е най-близо до крайните ви потребители, обикновено ще осигури най-добра производителност. Разгледайте тази карта, ако не сте сигурни коя да изберете.
Акредитивни данни
След това ще поиска вашите идентификационни данни за AWS.
Тук най-вероятно ще искате да настроите IAM потребител. Вижте това ръководство за това как да го настроите. Ако все пак настроите нов потребител, ще трябва да се уверите, че потребителят има съответните разрешения. Най-лесният начин да направите това е просто да добавите „Достъп на администратор“ към потребителя. (Това вероятно не е чудесен избор от съображения за сигурност, обаче.) За специфичните политики/роли, от които потребителят се нуждае, за да създаде/управлява приложение Elastic Beanstalk, вижте връзката тук.
Име на приложението
Това ще бъде по подразбиране името на директорията. Просто продължете с това.
Версия на Python
След това CLI трябва автоматично да открие, че използвате Python, и просто да поиска потвърждение. Кажи да. След това трябва да изберете версия на платформата. Изберете Python 2.7
.
SSH
Кажете „да“ на настройката на SSH за вашите екземпляри.
RSA Keypair
След това трябва да генерирате RSA двойка ключове, която ще бъде добавена към вашия ~/.ssh папка. Тази двойка ключове също ще бъде качена в публичния ключ EC2 за региона, който сте посочили в стъпка първа. Това ще ви позволи да свържете SSH към вашия EC2 екземпляр по-късно в този урок.
Какво постигнахме?
След като eb init
е завършен, ще видите нова скрита папка, наречена .elasticbeanstalk в директорията на вашия проект:
├── .elasticbeanstalk
│ └── config.yml
├── .gitignore
├── README.md
├── iotd
│ ├── images
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── migrations
│ │ │ ├── 0001_initial.py
│ │ │ └── __init__.py
│ │ ├── models.py
│ │ ├── tests.py
│ │ └── views.py
│ ├── iotd
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── manage.py
│ ├── static
│ │ ├── css
│ │ │ └── bootstrap.min.css
│ │ └── js
│ │ ├── bootstrap.min.js
│ │ └── jquery-1.11.0.min.js
│ └── templates
│ ├── base.html
│ └── images
│ └── home.html
├── requirements.txt
└── www
└── media
└── sitelogo.png
Вътре в тази директория има config.yml
файл, който е конфигурационен файл, който се използва за дефиниране на определени параметри за вашето новоизготвено приложение Beanstalk.
В този момент, ако въведете eb console
той ще отвори вашия браузър по подразбиране и ще отиде до конзолата Elastic Beanstalk. На страницата трябва да видите едно приложение (наречено image-of-the-day
). ако следвате точно), но без среди.
Едно приложение представлява вашето кодово приложение и е това, което eb init
създаден за нас. С Elastic Beanstalk едно приложение може да има множество среди (т.е. разработка, тестване, етапиране, производство). От вас зависи изцяло как искате да конфигурирате/управлявате тези среди. За прости Django приложения обичам да имам средата за разработка на лаптопа си, след което да създам тестова и производствена среда на Beanstalk.
Нека настроим тестова среда...
Конфигуриране на EB – Създаване на среда
Връщайки се към терминала, в директорията на вашия проект напишете:
$ eb create
Точно като eb init
, тази команда ще ви подкани с поредица от въпроси.
Име на средата
Трябва да използвате конвенция за именуване, подобна на това, което Amazon предлага – напр. application_name-env_name – особено когато/ако започнете да хоствате множество приложения с AWS. Използвах - iod-test
.
Префикс DNS CNAME
Когато разположите приложение в Elastic Beanstalk, вие автоматично ще получите име на домейн като xxx.elasticbeanstalk.com. DNS CNAME prefix
е това, което искате да се използва вместо xxx
. Просто отидете с по подразбиране.
Какво се случва сега?
В този момент eb
всъщност ще създаде вашата среда за вас. Бъдете търпеливи, тъй като това може да отнеме известно време.
Ако получите грешка при създаването на средата, като -
aws.auth.client.error.ARCInstanceIdentityProfileNotFoundException
- проверете дали идентификационните данни, които използвате, имат подходящи разрешения за създаване на средата Beanstalk, както беше обсъдено по-рано в тази публикация.
Веднага след създаването на средата, eb
ще се опита да разгърне вашето приложение, като копира целия код в директорията на вашия проект в новия EC2 екземпляр, изпълнявайки pip install -r requirements.txt
в процеса.
Трябва да видите куп информация за средата, която се настройва, показвана на екрана ви, както и информация за eb
опитвайки се да се разположи. Ще видите и някои грешки. По-специално трябва да видите тези редове, заровени някъде в изхода:
ERROR: Your requirements.txt is invalid. Snapshot your logs for details.
Не се притеснявайте - всъщност не е невалидно. Проверете регистрационните файлове за подробности:
$ eb logs
Това ще вземе всички скорошни регистрационни файлове от инстанцията EC2 и ще ги изведе на вашия терминал. Има много информация, така че може да искате да пренасочите изхода към файл (eb logs -z
). Преглеждайки регистрационните файлове, ще видите един регистрационен файл с име eb-activity.log :
Error: pg_config executable not found.
Проблемът е, че се опитахме да инсталираме psycopy2
(обвързванията на Postgres Python), но трябва да бъдат инсталирани и клиентските драйвери на Postgres. Тъй като те не са инсталирани по подразбиране, първо трябва да ги инсталираме. Нека поправим това...
Персонализиране на процеса на внедряване
eb
ще прочете персонализиран .config
файлове от папка, наречена „.ebextensions“ на основното ниво на вашия проект (директория „image-of-the-day“). Тези .config
файловете ви позволяват да инсталирате пакети, да изпълнявате произволни команди и/или да задавате променливи на средата. Файловете в директорията “.ebextensions” трябва да отговарят на JSON
или YAML
синтаксис и се изпълняват по азбучен ред.
Инсталиране на пакети
Първото нещо, което трябва да направим, е да инсталираме някои пакети, така че нашият pip install
командата ще завърши успешно. За да направите това, нека първо създадем файл, наречен .ebextensions/01_packages.config :
packages:
yum:
git: []
postgresql93-devel: []
Екземплярите на EC2 изпълняват Amazon Linux, който е аромат на Redhat, така че можем да използваме yum, за да инсталираме пакетите, от които се нуждаем. Засега просто ще инсталираме два пакета - git и клиента Postgres.
След като създадем този файл, за да преразположим приложението, трябва да направим следното:
$ git add .ebextensions/
$ git commit -m "added eb package configuration"
Трябва да запишем промените, защото командата за разгръщане eb deploy
работи от най-новия комит и по този начин ще бъде наясно с промените във файловете ни само след като ги ангажираме с git. (Имайте предвид обаче, че не е нужно да настояваме; ние работим от нашето локално копие...)
Както вероятно се досещате, следващата команда е:
$ eb deploy
Сега трябва да видите само една грешка:
INFO: Environment update is starting.
INFO: Deploying new version to instance(s).
ERROR: Your WSGIPath refers to a file that does not exist.
INFO: New application version was deployed to running EC2 instances.
INFO: Environment update completed successfully.
Нека разберем какво се случва...
Конфигуриране на нашата Python среда
EC2 екземпляри в Beanstalk изпълняват Apache и Apache ще намери нашето Python приложение според WSGIPATH, който сме задали. По подразбиране eb
предполага, че нашият wsgi файл се нарича application.py . Има два начина да коригирате това-
Опция 1:Използване на специфични за средата настройки за конфигурация
$ eb config
Тази команда ще отвори вашия редактор по подразбиране, като редактира конфигурационен файл, наречен .elasticbeanstalk/iod-test.env.yml . Този файл всъщност не съществува локално; eb
го извади от сървърите на AWS и ви го представи, за да можете да промените настройките в него. Ако направите някакви промени в този псевдо-файл и след това запишете и излезете, eb
ще актуализира съответните настройки във вашата среда Beanstalk.
Ако търсите термините „WSGI“ във файла и трябва да намерите секция за конфигурация, която изглежда така:
aws:elasticbeanstalk:container:python:
NumProcesses: '1'
NumThreads: '15'
StaticFiles: /static/=static/
WSGIPath: application.py
Актуализирайте WSGIPath:
aws:elasticbeanstalk:container:python:
NumProcesses: '1'
NumThreads: '15'
StaticFiles: /static/=static/
WSGIPath: iotd/iotd/wsgi.py
И тогава ще настроите WSGIPath правилно. Ако след това запишете файла и излезете, eb
автоматично ще актуализира конфигурацията на средата:
Printing Status:
INFO: Environment update is starting.
INFO: Updating environment iod-test's configuration settings.
INFO: Successfully deployed new configuration to environment.
INFO: Environment update completed successfully.
Предимството на използването на eb config
методът за промяна на настройките е, че можете да зададете различни настройки за всяка среда. Но можете също да актуализирате настройките, като използвате същия .config
файлове, които използвахме преди. Това ще използва същите настройки за всяка среда като .config
файловете ще бъдат приложени при внедряването (след настройките от eb config
са приложени).
Опция 2:Използване на глобални настройки за конфигурация
За да използвате .config
опция файл, нека създадем нов файл, наречен /.ebextensions/02_python.config :
option_settings:
"aws:elasticbeanstalk:application:environment":
DJANGO_SETTINGS_MODULE: "iotd.settings"
"PYTHONPATH": "/opt/python/current/app/iotd:$PYTHONPATH"
"aws:elasticbeanstalk:container:python":
WSGIPath: iotd/iotd/wsgi.py
NumProcesses: 3
NumThreads: 20
"aws:elasticbeanstalk:container:python:staticfiles":
"/static/": "www/static/"
Какво се случва?
DJANGO_SETTINGS_MODULE: "iotd.settings"
- добавя пътя към модула за настройки."PYTHONPATH": "/opt/python/current/app/iotd:$PYTHONPATH"
- актуализира нашияPYTHONPATH
така Python може да намери модулите в нашето приложение. (Обърнете внимание, че използването на пълния път е необходимо.)WSGIPath: iotd/iotd/wsgi.py
задава нашия WSGI път.NumProcesses: 3
иNumThreads: 20
- актуализира броя на процесите и нишките, използвани за изпълнение на нашето WSGI приложение."/static/": "www/static/"
задава пътя на нашите статични файлове.
Отново можем да направим git commit
след това eb deploy
за да актуализирате тези настройки.
След това нека добавим база данни.
Конфигуриране на база данни
Опитайте да видите внедрения уебсайт:
$ eb open
Тази команда ще покаже внедреното приложение във вашия браузър по подразбиране. Трябва да видите грешка при отказ на връзка:
OperationalError at /
could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP/IP connections on port 5432?
Това е така, защото все още не сме създали база данни. В този момент eb
ще настрои вашата среда Beanstalk, но не настройва RDS (нивото на базата данни). Трябва да го настроим ръчно.
Настройка на базата данни
Отново използвайте eb console
за да отворите страницата за конфигурация на Beanstalk.
Оттам направете следното:
- Щракнете върху връзката „Конфигурация“.
- Превъртете до края на страницата и след това под секцията „Ниво на данни“ щракнете върху връзката „създайте нова RDS база данни“.
- На страницата за настройка на RDS променете „DB Engine“ на „postgres“.
- Добавете „Основно потребителско име“ и „Главна парола“.
- Запазете промените.
Beanstalk ще създаде RDS за вас. Сега трябва да накараме нашето приложение Django да се свърже с RDS. Beanstalk ще ни помогне тук, като изложи редица променливи на средата на EC2 инстанциите за нас, които подробно описват как да се свържем със сървъра на Postgres. Така че всичко, което трябва да направим, е да актуализираме нашия settings.py файл, за да се възползвате от тези променливи на средата. Потвърдете, че DATABASES
конфигурационният параметър отразява следното в settings.py :
if 'RDS_DB_NAME' in os.environ:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': os.environ['RDS_DB_NAME'],
'USER': os.environ['RDS_USERNAME'],
'PASSWORD': os.environ['RDS_PASSWORD'],
'HOST': os.environ['RDS_HOSTNAME'],
'PORT': os.environ['RDS_PORT'],
}
}
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'iotd',
'USER': 'iotd',
'PASSWORD': 'iotd',
'HOST': 'localhost',
'PORT': '5432',
}
}
Това просто казва:„Използвайте настройките на променливата на средата, ако има такава, в противен случай използвайте нашите настройки за разработка по подразбиране“. Просто.
Обработване на миграции на база данни
С нашата настройка на базата данни все още трябва да се уверим, че миграциите се изпълняват, така че структурата на таблицата на базата данни да е правилна. Можем да направим това, като модифицираме .ebextensions/02_python.config и добавяне на следните редове в горната част на файла:
container_commands:
01_migrate:
command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py migrate --noinput"
leader_only: true
container_commands
ви позволяват да изпълнявате произволни команди, след като приложението е разгърнато на EC2 инстанция. Тъй като екземплярът EC2 е настроен с помощта на виртуална среда, първо трябва да активираме тази виртуална среда, преди да изпълним нашата команда за мигриране. Също така leader_only: true
настройката означава:„Изпълнете тази команда само на първия екземпляр, когато се внедрява в множество екземпляри“.
Не забравяйте, че нашето приложение използва администратора на Django, така че ще ни трябва суперпотребител...
Създайте администраторския потребител
За съжаление createsuperuser
не ви позволява да посочите парола, когато използвате --noinput
опция, така че ще трябва да напишем собствена команда. За щастие, Django прави много лесно създаването на персонализирани команди.
Създайте файла iotd/images/management/commands/createsu.py :
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
class Command(BaseCommand):
def handle(self, *args, **options):
if not User.objects.filter(username="admin").exists():
User.objects.create_superuser("admin", "[email protected]", "admin")
Уверете се, че сте добавили подходящия __init__.py
файлове също:
└─ management
├── __init__.py
└── commands
├── __init__.py
└── createsu.py
Този файл ще ви позволи да стартирате python manage.py createsu
, и ще създаде суперпотребител, без да иска парола. Чувствайте се свободни да разширите командата, за да използвате променливи на средата или друго средство, което да ви позволи да промените паролата.
След като създадете командата, можем просто да добавим друга команда към нашите container_commands
раздел в .ebextensions/02_python.config :
02_createsu:
command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py createsu"
leader_only: true
Преди да тествате това, нека се уверим, че нашите статични файлове са поставени на правилното място...
Статични файлове
Добавете още една команда под container_commands
:
03_collectstatic:
command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py collectstatic --noinput"
Така че целият файл изглежда така:
container_commands:
01_migrate:
command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py migrate --noinput"
leader_only: true
02_createsu:
command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py createsu"
leader_only: true
03_collectstatic:
command: "source /opt/python/run/venv/bin/activate && python iotd/manage.py collectstatic --noinput"
option_settings:
"aws:elasticbeanstalk:application:environment":
DJANGO_SETTINGS_MODULE: "iotd.settings"
"PYTHONPATH": "/opt/python/current/app/iotd:$PYTHONPATH"
"ALLOWED_HOSTS": ".elasticbeanstalk.com"
"aws:elasticbeanstalk:container:python":
WSGIPath: iotd/iotd/wsgi.py
NumProcesses: 3
NumThreads: 20
"aws:elasticbeanstalk:container:python:staticfiles":
"/static/": "www/static/"
Сега трябва да се уверим, че STATIC_ROOT
е зададен правилно в settings.py файл:
STATIC_ROOT = os.path.join(BASE_DIR, "..", "www", "static")
STATIC_URL = '/static/'
Уверете се, че сте записали www
директория в git, за да може да се създаде статичната директория. След това стартирайте eb deploy
отново и вече трябва да сте в бизнеса:
INFO: Environment update is starting.
INFO: Deploying new version to instance(s).
INFO: New application version was deployed to running EC2 instances.
INFO: Environment update completed successfully.
В този момент трябва да можете да отидете на http://your_app_url/admin, да влезете, да добавите изображение и след това да видите това изображение, показано на главната страница на вашето приложение.
Успех!
Използване на S3 за медийно съхранение
С тази настройка всеки път, когато внедряваме отново, ще загубим всички наши качени изображения. Защо? Е, когато стартирате eb deploy
, за вас е създаден нов екземпляр. Това не е това, което искаме, тъй като тогава ще имаме записи в базата данни за изображенията, но няма свързани изображения. Решението е да съхранявате медийните файлове в Amazon Simple Storage Service (Amazon S3) вместо в самия EC2 екземпляр.
Ще трябва да:
- Създайте кофа
- Вземете ARN на вашия потребител (име на ресурс на Amazon)
- Добавяне на разрешения за сегмент
- Конфигурирайте приложението си Django да използва S3 за обслужване на вашите статични файлове
Тъй като вече има добри публикации за това, просто ще ви насоча към моя любим:Използване на Amazon S3 за съхраняване на статични и медийни файлове на Django
Конфигурация на Apache
Тъй като използваме Apache с Beanstalk, вероятно искаме да настроим Apache да (наред с други неща) активира gzip компресията, така че файловете да се изтеглят по-бързо от клиентите. Това може да стане с container_commands
. Създайте нов файл .ebextensions/03_apache.config и добавете следното:
container_commands:
01_setup_apache:
command: "cp .ebextensions/enable_mod_deflate.conf /etc/httpd/conf.d/enable_mod_deflate.conf"
След това трябва да създадете файла .ebextensions/enable_mod_deflate.conf
:
# mod_deflate configuration
<IfModule mod_deflate.c>
# Restrict compression to these MIME types
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xml+rss
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/css
# Level of compression (Highest 9 - Lowest 1)
DeflateCompressionLevel 9
# Netscape 4.x has some problems.
BrowserMatch ^Mozilla/4 gzip-only-text/html
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip
# MSIE masquerades as Netscape, but it is fine
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
<IfModule mod_headers.c>
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
</IfModule>
</IfModule>
Правейки това ще активирате gzip компресия, което би трябвало да помогне за размера на файловете, които изтегляте. Можете също да използвате същата стратегия, за да минимизирате и комбинирате автоматично вашия CSS/JS и да извършите всяка друга предварителна обработка, която трябва да направите.
Отстраняване на неизправности
Не забравяйте много полезния eb ssh
команда, която ще ви отведе в екземпляра EC2, за да можете да се ровите и да видите какво се случва. Когато отстранявате неизправности, има няколко директории, за които трябва да сте наясно:
/opt/python
- Корен на мястото, където ще се окаже вашето приложение./opt/python/current/app
- Текущото приложение, което се хоства в средата./opt/python/on-deck/app
- Първоначално приложението се поставя на палубата и след това, след като цялото внедряване приключи, то ще бъде преместено вcurrent
. Ако получавате грешки във вашитеcontainer_commands
, вижтеon-deck
папка, а неcurrent
папка./opt/python/current/env
- Всички env променливи, коитоeb
ще настрои за вас. Ако се опитвате да възпроизведете грешка, може първо да се наложи даsource /opt/python/current/env
за да настроите нещата така, както биха били, когато се изпълнява eb deploy.opt/python/run/venv
- Виртуалната среда, използвана от вашето приложение; ще трябва също да стартиратеsource /opt/python/run/venv/bin/activate
ако се опитвате да възпроизведете грешка
Заключение
Внедряването в Elastic Beanstalk може да бъде малко обезсърчително в началото, но след като разберете къде са всички части и как работят нещата, всъщност е доста лесно и изключително гъвкаво. Освен това ви предоставя среда, която ще се мащабира автоматично с нарастването на употребата ви. Надяваме се, че вече имате достатъчно, за да бъдете опасни! Успех при следващото ви внедряване на Beanstalk.
Изпуснахме ли нещо? Имате ли други съвети или трикове? Моля, коментирайте по-долу.