Статьи

Запуск приложения MEAN в Docker Containers на AWS

Скорость принятия Docker в качестве контейнерного решения стремительно растет . Многие компании сейчас используют контейнеры Docker для запуска приложений. Во многих случаях использование контейнеров Docker может быть лучшим подходом, чем раскрутка полноценной виртуальной машины.

В этой статье я расскажу обо всех шагах, которые я предпринял для успешной установки и запуска веб-приложения, созданного из стека MEAN (MongoDB, Express, AngularJS и Node.js). Я размещал приложение в контейнерах Docker на Amazon Web Services (AWS).

Кроме того, я запускал базу данных MongoDB и веб-приложение в отдельных контейнерах. Есть много преимуществ для этого подхода иметь изолированные среды:

  1. Поскольку каждый контейнер имеет свою собственную среду выполнения, легко изменить среду одного компонента приложения, не затрагивая другие части. Мы можем изменить установленное программное обеспечение или опробовать другие версии программного обеспечения, пока не выясним наилучшую возможную настройку для этого конкретного компонента.
  2. Поскольку компоненты нашего приложения изолированы, проблемы безопасности легко решаются. Если контейнер атакован или вредоносный скрипт непреднамеренно запускается как часть обновления, другие наши контейнеры по-прежнему безопасны.
  3. Поскольку подключенные контейнеры легко переключать и менять, тестирование становится намного проще. Например, если мы хотим протестировать наше веб-приложение с разными наборами данных, мы можем легко это сделать, подключив его к разным контейнерам, настроенным для разных сред баз данных.

MEAN Web Framework

Веб — приложение , которое мы собираемся бежать является основой код MEAN.JS . Это полный стек решение JavaScript строит быстрое, надежное и обслуживаемое производство веб — приложения с использованием MongoDB , Экспресса , AngularJS и Node.js .

Еще одним большим преимуществом MEAN.JS является то, что мы можем использовать Yeoman Generators для создания строительных лесов для нашего приложения за считанные минуты. Он также имеет генераторы CRUD, которые я активно использовал при добавлении новых функций в приложение. Самое приятное то, что он уже хорошо настроен для поддержки развертывания Docker. Он поставляется с Dockerfile, который может быть создан для создания образа контейнера, хотя мы будем использовать предварительно созданный образ, чтобы сделать это еще быстрее (подробнее об этом позже).

Запуск Docker в инстансе Amazon

Возможно, вы уже знаете, что можете пользоваться базовыми услугами AWS бесплатно в течение всего года. Следующие шаги помогут вам настроить и запустить виртуальную машину в AWS вместе со службой Docker:

  1. Для начала создайте бесплатную учетную запись на aws.amazon.com . Обязательно выберите базовый (бесплатный) план поддержки. Вы будете перенаправлены на страницу приветствия AWS.
  2. На этой странице нажмите «Запустить консоль управления». Пожалуйста, потерпите меня, так как мы будем нажимать много кнопок Launch, прежде чем запустим экземпляр. Вы будете перенаправлены на страницу со списком сервисов AWS.Запустить консоль управления
  3. Выберите первый параметр в левом верхнем углу, который называется «EC2 (Виртуальные серверы в облаке)». EC2
  4. Нажмите «Запустить экземпляр». Вам будет предложено выбрать изображение. Я выбрал первую, «Amazon Linux AMI», но если вам удобнее использовать любую другую версию Linux, выберите ее.Выбрать
  5. Нажмите кнопку запуска.
  6. Далее вам будет предложено выбрать тип экземпляра. Вы можете выбрать первый вариант. В верхней навигационной панели отображаются все этапы выбора нашей конфигурации, но единственной настраиваемой конфигурацией, которую мне нужно было сделать, было увеличение дискового пространства для моего экземпляра. Моя первая попытка с 8 ГБ по умолчанию потерпела неудачу, поскольку моим образам Docker требовалось больше места, особенно выбранный образ MongoDB, для которого по умолчанию установлено значение более 3 ГБ для хранения журнала.
  7. Чтобы изменить хранилище, нажмите четвертую опцию «Добавить хранилище». Добавить хранилище
  8. Затем измените объем хранилища с 8 ГБ до 16 ГБ и нажмите «Просмотр и запуск». Вы попадете на заключительный экран просмотра.Обзор и запуск
  9. Снова нажмите «Запустить». Затем вам будет предложено создать пару ключей, которая будет использоваться для SSH в экземпляре после его создания. Мне нравится быть частью этого потока, поскольку подключение к экземпляру, очевидно, будет первым, что я хотел бы сделать после запуска экземпляра.запуск
  10. Выберите «Создать новую пару ключей» из выпадающего списка и введите имя файла.
  11. Наконец, нажмите «Загрузить пару ключей», чтобы сохранить этот файл на вашем компьютере в папке. Нам нужно будет ссылаться на этот файл, когда мы используем SSH в нашем экземпляре.Скачать пару ключей
  12. Итак, в последний раз нажмите кнопку «Launch Instance». Ваш экземпляр должен быть запущен через несколько минут. Вы увидите ссылку «Просмотр экземпляра», чтобы перейти на панель мониторинга EC2, где вы сможете увидеть подробную информацию о запущенном экземпляре.
  13. Если вы выберите свой экземпляр в списке, а затем нажмите «Подключиться» вверху, в окне есть несколько четких инструкций для SSH. На следующем скриншоте они рекомендуют использовать PuTTY, но я использовал свое приложение «Git Bash». Он уже был доступен на моем компьютере и поддерживает SSH-клиент.Подключиться к экземпляру

    Примечание. Для целей этого блога я буду проходить SSH через свой Mac-терминал, но раньше мне удавалось делать то же самое с помощью Windows. Это не сильно отличается.

  14. В Терминале перейдите в папку, в которой вы сохранили файл пары ключей. Запустите приведенные ниже команды, чтобы изменить разрешение файла в соответствии с инструкциями (см. Скриншот выше), а затем SSH в экземпляр AWS:
    MacBook-Pro:Documents vishalkumar$ chmod 400 meanjs_app_on_AWS.pem 
    MacBook-Pro:Documents vishalkumar$ SSH -i meanjs_app_on_AWS.pem 
    ec2-user@52.24.51.168 
    The authenticity of host '52.24.51.168 (52.24.51.168)' can't be established. RSA key fingerprint is ef:23:dc:8d:73:93:1e:d4:90:f7:4e:9b:50:67:f5:1b. 
    Are you sure you want to continue connecting (yes/no)? yes 
    Warning: Permanently added '52.24.51.168' (RSA) to the list of known hosts. 
    Run "sudo yum update" to apply all updates. 
    [ec2-user@ip-172-31-40-40 ~]$
  15. Следуя рекомендациям, которые мы получим после подключения, запустите команду yum update:
    [ec2-user@ip-172-31-40-40 ~]$ sudo yum update
  16. После установки обновления установите Docker:
    [ec2-user@ip-172-31-40-40~]$ sudo yum install -y docker
  17. Запустите сервис Docker:
    [ec2-user@ip-172-31-40-40 ~]$ sudo service docker start 
    Starting cgconfig service: [ OK ] 
    Starting docker: [ OK ] 
    [ec2-user@ip-172-31-40-40 ~]$
  18. Добавьте пользователя ec2 в группу Docker, чтобы вы могли выполнять команды Docker без использования sudo:
    [ec2-user@ip-172-31-40-40 ~]$ sudo usermod -a -G docker ec2-user
  19. Наконец, выйдите и снова войдите в SSH. Запустите «Docker Info», чтобы увидеть, была ли команда успешной без использования sudo. Вы должны увидеть некоторые статистические данные, возвращаемые Docker:
    [ec2-user@ip-172-31-40-40 ~]$ logout
    Connection to 52.24.51.168 closed. 
    MacBook-Pro:Documents vishalkumar$ SSH -i meanjs_app_on_AWS.pem 
    ec2-user@52.24.51.168 
    [ec2-user@ip-172-31-40-40 ~]$ docker info 
    Containers: 0 Images: 0 
    ..... 
    ..

Запуск базы данных MongoDB в качестве контейнера

Теперь, когда на нашем экземпляре Amazon запущен Docker, мы можем запустить наши контейнеры.

Как я упоминал ранее, мы собираемся запустить нашу базу данных MongoDB и наше веб-приложение в отдельных контейнерах. Я выбрал официальный репо для Mongo в хранилище докеров . Мы можем извлечь это изображение и запустить его как отдельный контейнер за один простой шаг:

[ec2-user@ip-172-31-40-40 ~]$ docker run --name mymongodb -d mongo

Последний аргумент mongo — это имя изображения, из которого он должен создать контейнер. Докер сначала будет искать это изображение локально. Когда он не находит его, он загружает его и все базовые изображения, от которых он зависит. Удобный!

Затем Docker запустит этот образ как контейнер. Флаг -d гарантирует, что он запускается в автономном режиме (в фоновом режиме), поэтому мы можем использовать эту же оболочку для запуска других наших команд. После этого мы можем выполнить быструю проверку, чтобы убедиться, что этот контейнер запущен и работает, с помощью команды docker ps:

[ec2-user@ip-172-31-40-40 ~]$ docker ps -a 
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 
2f93a31d4a3d mongo:latest "/entrypoint.sh mong About a minute ago Up About a minute 
27017/tcp mymongodb

The startup script script for this image already runs the mongo service listening on 27017 port by default. So there is literally nothing else we had to do here except for that one docker run command.

Running the MEAN Stack Container

The next phase of this project is to run our web application as a separate container.

The MEAN stack code base has a lot of dependencies like Node, Bower, Grunt, etc. But once again, we don’t need to worry about installing them if we have an image that already has all these dependencies. Turns out there is an image on the Docker Hub that already has everything we need.

Once again, we will pull it in and run it with just one command:

[ec2-user@ip-172-31-40-40 ~]$ docker run -i -t --name mymeanjs --link mymongodb:db_1 -p 80:3000 maccam912/meanjs:latest bash 
... 
.. 
Status: Downloaded newer image for maccam912/meanjs:latest 
root@7f4e72af1cf0:/#

Now there is a lot going on with this single command. To be honest, it took me some time to get it exactly right.

  1. The most important piece here is the –link mymongodb:db_1 argument. It adds a link between this container and our mymongodb container. This way, our web application is able to connect to the database running on the mymongodb container. db_1 is the alias name that we’re choosing to reference this connected container. Our MEAN application is set to use db_1, so it’s important to keep that name.
  2. Another important argument is -p 80:3000, where we’re mapping the 3000 port on the container to port 80 on the host machine. We know that web applications are accessed through the default port of 80 using the HTTP protocol. Our MEAN application is set to run on port 3000. This mapping enables us to access the same application from outside the container over the host port 80.
  3. We of course have to specify the image from which the container should be built. As we discussed before, maccam912/meanjs:latest is the image we’ll use for this container.
  4. The -i flag is for interactive mode, and -t is to allocate a pseudo terminal. This will essentially allow us to connect our terminal with the stdin and stdout streams of the container. This stackoverflow question explains it in a little more detail.
  5. The argument bash hooks us into the container where we will run the required commands to get our MEAN application running. We can bash into a previously running Docker container, but here we are doing all that with just one command.

Новый призыв к действию

Building and Running our MEAN Application

Now that we’re inside our container, running the ls command shows us many folders including one called Development. We will use this folder for our source code.

cd into this folder and run git clone to get the source code for our MEAN.JS application from GitHub:

root@7f4e72af1cf0:/# cd Development/ 
root@7f4e72af1cf0:/Development# git clone https://github.com/meanjs/mean.git meanjs 
Cloning into 'meanjs'... remote: 
.... 
.. 
Checking connectivity... done.

cd into our MEAN.JS folder. We can run npm install to download all the package dependencies:

root@7f4e72af1cf0:/Development# cd meanjs 
root@7f4e72af1cf0:/Development/meanjs# ls 
Dockerfile LICENSE.md Procfile README.md app bower.json config fig.yml gruntfile.js karma.conf.js package.json public scripts server.js 
root@7f4e72af1cf0:/Development/meanjs# npm install

A couple of hiccups to watch out for: For some reason, my npm install hung during a download. So I used Ctrl + C to terminate it, deleted all packages to start from scratch, and ran npm install again. Thankfully, this time it worked:

^C 
root@7f4e72af1cf0:/Development/meanjs# rm -rf node_modules/ 
root@7f4e72af1cf0:/Development/meanjs# npm install

Install the front-end dependencies running by running bower. Since I’m logged in as the super user, bower doesn’t like it. But it does give me an option to still run it by using the –allow-root option:

root@7f4e72af1cf0:/Development/meanjs# bower install 
bower ESUDO Cannot be run with sudo 
.... 
You can however run a command with sudo using --allow-root option
root@7f4e72af1cf0:/Development/meanjs# bower install --allow-root

Run our grunt task to run the linter and minimize the js and css files:

root@7f4e72af1cf0:/Development/meanjs# grunt build 
... 
Done, without errors.

Now, we are ready to run our application. Our MEAN stacks looks for a configuration flag called NODE_ENV, which we will set to production and use the default grunt task to run our application. If you did all the steps right, you should see this final output:

root@7f4e72af1cf0:/Development/meanjs# NODE_ENV=production grunt 
... 
.. 
MEAN.JS application started 
Environment: production 
Port: 3000 
Database: mongodb://172.17.0.1/mean

Validating Our Application from the Browser

Our application would have given errors if there was some problem running it or if the database connection failed. Since everything looks good, it’s time to finally access our web application through the browser.

But first, we’ll need to expose our virtual machine’s port 80 over HTTP.

  1. Go back to the EC2 dashboard.
  2. Click on the security group link for the given instance. You should
    see the settings page for the security group. launch_wizard_3
  3. Click the “Inbound” tab at the bottom, and then click the “Edit”
    link. You should see that SSH is already added. Now we need to add
    HTTP to the list of inbound rules. редактировать
  4. Click “Add Rule.” Добавить правило
  5. Select HTTP from the dropdown menu and leave the default setting of
    port 80 for the Port Range field. Click “Save.”
  6. Pick up the URL to our instance from the Public DNS column and hit
    that URL from the browser. Инстансы You should see the homepage of our
    fabulous application. You can validate it by creating some user
    accounts and signing in to the app. Поздравляю

So that’s it. We’ve managed to run our application on AWS inside isolated Docker containers. There were a lot of steps involved, but at the crux of it all, we really needed to run only two smart Docker run commands to containerize our application.