текст перекладу
Elastic Beanstalk, EB, — це своєрідна магічна коробка. Я думав, що буде корисно зазирнути під капот, подивитися, як все насправді працює, і навчитися з цього. Розуміння того, як саме працює EB, надзвичайно корисне для налагодження. У цьому прикладі я використовую "64bit Amazon Linux 2017.03 v2.7.0, що працює на Docker 17.03.1-ce" у складі рішення EB.
Що відбувається, коли ми деплоїмо код
Отже, що відбувається, коли ми деплоїмо код в середовище EB? Давайте деплоїмо простий Rails-додаток і відслідкуємо середовище, щоб дізнатися. Вихідний код простого Rails-додатка знаходиться на GitHub за посиланням tongueroo/hi, але для цього посту не має значення, який додаток ви використовуєте для деплою.
$ eb deploy
WARNING: Deploying a previously deployed commit
INFO: Environment update is starting.
INFO: Deploying new version to instance(s).
INFO: Successfully pulled tongueroo/hi:base
INFO: Successfully built aws_beanstalk/staging-app
INFO: Successfully built aws_beanstalk/staging-app
INFO: Docker container 3691fd5e5d46 is running aws_beanstalk/current-app.
INFO: Command execution completed on 1 of 2 instances in environment.
INFO: Docker container 14638e61c6ee is running aws_beanstalk/current-app.
INFO: Command execution completed on 2 of 2 instances in environment.
INFO: New application version was deployed to running EC2 instances.
INFO: Environment update completed successfully.
$
Це чудово, але що ж насправді сталося, коли ми запустили eb deploy
?
Менеджер хостів EB
EB згадує про процес менеджера хостів в розділі архітектури документації. Це демон-процес, який працює в фоновому режимі і керує сервером. Він слухає події, такі як деплой коду чи оновлення змінних середовища, і обробляє ці події, коли вони спрацьовують. Менеджер хостів насправді використовує команду /opt/aws/bin/cfn-hup.
_# which cfn-hup_
/opt/aws/bin/cfn-hup
_# ps auxww | grep cfn-hup_
root 1276 0.0 0.2 110456 2196 pts/0 S+ 22:48 0:00 grep --color=auto cfn-hup
root 2796 0.2 2.5 530956 26060 ? Ssl Jul15 3:46 /usr/bin/python2.7 /opt/aws/bin/cfn-hup
_#_
Процес cfn-hup
запускається через upstart.
текст перекладу
Якщо ви будете використовувати команду tail
для перегляду файлу /var/log/cfn-hup.log
, ви побачите, що процес cfn-hup
слухає події зі черги SQS.
_# tail -f /var/log/cfn-hup.log_
2017-07-16 23:09:15,432 [DEBUG] Receiving messages for queue [https://us-west-2.queue.amazonaws.com/blah/bleh](https://us-west-2.queue.amazonaws.com/blah/bleh)
2017-07-16 23:09:33,602 [DEBUG] Processing message: {'EventHandle': 'blah', 'DispatcherId': 'blah', 'CommandName': 'blah', 'ResultQueue': 'blah', 'Data': 'blah', 'InvocationId': 'blah'}
2017-07-16 23:09:33,602 [DEBUG] Command message: {'EventHandle': 'blah', 'DispatcherId': 'blah', 'CommandName': 'blah', 'Expiration': 1500246693428, 'ResultQueue': 'blah', 'Data': 'blah', 'InvocationId': 'blah'}
2017-07-16 23:09:33,602 [INFO] Received command ElasticBeanstalkCommand-AWSEBAutoScalingGroup (invocation id: blah)
2017-07-16 23:09:33,602 [INFO] Running action for aws-eb-command-handler
2017-07-16 23:09:33,602 [DEBUG] Action environment: {'LANG': 'en_US.UTF-8', 'TERM': 'linux', 'LISTENER_ID': u'blah', 'RESULT_SECRET_KEY': u'blah', 'SHLVL': '1', 'RESULT_ACCESS_KEY': u'blah', 'STACK_NAME': 'blah', 'RESULT_SESSION_TOKEN': u'blah', 'UPSTART_JOB': 'cfn-hup', 'CMD_DATA': 'blah', 'UPSTART_INSTANCE': '', 'PWD': '/', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin', 'CMD_NAME': 'ElasticBeanstalkCommand-AWSEBAutoScalingGroup', 'RESULT_QUEUE': 'blah', 'DISPATCHER_ID': 'blah', 'EVENT_HANDLE': 'blah', '_': '/opt/aws/bin/cfn-hup', 'INVOCATION_ID': 'blah'}
2017-07-16 23:09:47,841 [DEBUG] Action for aws-eb-command-handler output: {"status":"SUCCESS","api_version":"1.0","results":[{"status":"SUCCESS","msg":"","returncode":0,"events":[{"msg":"Successfully pulled ruby:2.3.3","severity":"TRACE","timestamp":1500246578},{"msg":"Successfully built aws_beanstalk/staging-app","severity":"TRACE","timestamp":1500246578},{"msg":"Docker container 1c4d4630b9e6 is running aws_beanstalk/current-app.","severity":"TRACE","timestamp":1500246587}]}]}
2017-07-16 23:09:47,842 [INFO] Action for aws-eb-command-handler succeeded, returning SUCCESS
2017-07-16 23:09:47,842 [DEBUG] Sending {"Status": "SUCCESS", "DispatcherId": "blah", "ListenerId": "blah", "CommandName": "ElasticBeanstalkCommand-AWSEBAutoScalingGroup", "InvocationId": "blah", "Data": "{\"status\":\"SUCCESS\",\"api_version\":\"1.0\",\"results\":[{\"status\":\"SUCCESS\",\"msg\":\"\",\"returncode\":0,\"events\":[{\"msg\":\"Successfully pulled ruby:2.3.3\",\"severity\":\"TRACE\",\"timestamp\":1500246578},{\"msg\":\"Successfully built aws_beanstalk/staging-app\",\"severity\":\"TRACE\",\"timestamp\":1500246578},{\"msg\":\"blah.\",\"severity\":\"TRACE\",\"timestamp\":1500246587}]}]}"} to queue [https://us-west-2.queue.amazonaws.com/blah/bleh](https://us-west-2.queue.amazonaws.com/blah/bleh) from queue [https://us-west-2.queue.amazonaws.com/blah/bleh](https://us-west-2.queue.amazonaws.com/blah/bleh)
2017-07-16 23:09:47,971 [DEBUG] Receiving messages for queue [https://us-west-2.queue.amazonaws.com/blah/bleh](https://us-west-2.queue.amazonaws.com/blah/bleh)
Отже, коли ми запускаємо команду eb deploy
або деплоїмо через API EB, що насправді відбувається? Сервіс AWS EB записує повідомлення в чергу SQS під нашим AWS-аккаунтом. Процес-демон менеджера хостів слухає цю чергу і виконує завдання. Зауважте: я приховав всю чутливу інформацію.
Що робить cfn-hup
?
Якщо ви відслідкуєте файли та бібліотеки, ви побачите, що сервіс AWS EB проходить через такі етапи:
* /usr/bin/python2.7 /opt/aws/bin/cfn-hup
* /bin/bash -e /opt/elasticbeanstalk/bin/command-processor
* /opt/elasticbeanstalk/lib/ruby/bin/ruby /opt/elasticbeanstalk/lib/ruby/bin/command-processor
Трасування переходить між Python, Bash і Ruby-скриптами. Однак простіше і швидше побачити, що відбувається, використовуючи команду ps
.
текст перекладу
Запустіть цю команду watch ‘ps auxwwf | grep -A 5 “python /opt/aws/bin/cfn-hup”’
під час деплою.
Важливо, що в кінцевому підсумку викликається Ruby-скрипт /opt/elasticbeanstalk/lib/ruby/bin/command-processor. Якщо ви прослідкуєте цей код, ви побачите, що він завантажує гем beanstalk-core. Нарешті, файл /opt/elasticbeanstalk/lib/ruby/lib/ruby/gems/2.2.0/gems/beanstalk-core-2.3/lib/elasticbeanstalk/command-processor.rb містить основну логіку того, як ElasticBeanstalk обробляє деплой. Ви також побачите тут, що команда command-processor
записує свої дії в файл /var/log/eb-commandprocessor.log. Ось приклад деяких з його виводів:
$ tail -f /var/log/eb-commandprocessor.log
...
[2017-07-19T06:24:05.584Z] DEBUG [4510] : Loaded 6 actions for stage 0.
[2017-07-19T06:24:05.584Z] INFO [4510] : Running 1 of 6 actions: InfraWriteConfig...
[2017-07-19T06:24:05.587Z] INFO [4510] : Running 2 of 6 actions: DownloadSourceBundle...
[2017-07-19T06:24:05.895Z] INFO [4510] : Running 3 of 6 actions: EbExtensionPreBuild...
[2017-07-19T06:24:06.177Z] INFO [4510] : Running 4 of 6 actions: AppDeployPreHook...
[2017-07-19T06:24:09.086Z] INFO [4510] : Running 5 of 6 actions: EbExtensionPostBuild...
[2017-07-19T06:24:09.368Z] INFO [4510] : Running 6 of 6 actions: InfraCleanEbextension...
[2017-07-19T06:24:09.370Z] INFO [4510] : Running stage 1 of command CMD-AppDeploy...
[2017-07-19T06:24:09.370Z] DEBUG [4510] : Loaded 2 actions for stage 1.
[2017-07-19T06:24:09.370Z] INFO [4510] : Running 1 of 2 actions: AppDeployEnactHook...
[2017-07-19T06:24:17.592Z] INFO [4510] : Running 2 of 2 actions: AppDeployPostHook...
[2017-07-19T06:24:18.186Z] INFO [4510] : Running AddonsAfter for command CMD-AppDeploy...
[2017-07-19T06:24:18.305Z] INFO [4510] : Command CMD-AppDeploy succeeded!
[2017-07-19T06:24:18.306Z] INFO [4510] : Command processor returning results:
{"status":"SUCCESS","api_version":"1.0","results":[{"status":"SUCCESS","msg":"","returncode":0,"events":[{"msg":"Successfully pulled ruby:2.3.3","severity":"TRACE","timestamp":1500445448},{"msg":"Successfully built aws_beanstalk/staging-app","severity":"TRACE","timestamp":1500445449},{"msg":"Docker container d8cdffc1b069 is running aws_beanstalk/current-app.","severity":"TRACE","timestamp":1500445457}]}]}
$
Ви можете побачити, як виконуються різні дії: завантажується код, виконуються хоки, відбувається побудова і очищення. Наприкінці відображається результат успіху.
Є кілька цікавих файлів у гему, які варто розглянути, щоб краще зрозуміти, як це працює:
- lib/elasticbeanstalk/command.rb
- lib/elasticbeanstalk/hook-directory-executor.rb
- lib/elasticbeanstalk/executable.rb
Подивившись на command.rb
, ми бачимо, що скрипти хока зберігаються в директорії /opt/elasticbeanstalk/hooks/. Дивлячись на executable.rb
, цікаво, що процесор команд ElasticBeanstalk використовує instance_eval
для виконання скриптів, якщо це Ruby-скрипт, і виконує їх як підпроцес, якщо це не Ruby-скрипт.
Зрештою, трасування коду призводить до директорії /opt/elasticbeanstalk/hooks/appdeploy/:
_# ls /opt/elasticbeanstalk/hooks/appdeploy/enact/_
00run.sh 01flip.sh
_# ls /opt/elasticbeanstalk/hooks/appdeploy/pre/_
00clean_dir.sh 01unzip.sh 02loopback-check.sh 03build.sh
_# ls /opt/elasticbeanstalk/hooks/appdeploy/enact/_
00run.sh 01flip.sh
_# ls /opt/elasticbeanstalk/hooks/appdeploy/post/_
00_clean_imgs.sh 01_monitor_pids.sh
_#_
Ці скрипти виконуються в порядку, і вони викликають 03build.sh для побудови Docker-образу і 00run.sh для запуску Docker-контейнера.
Найкорисніше, що треба знати, коли Docker-контейнер не запускається
Основний висновок з цього блогу — це знати, на чому зосередити ваші зусилля для налагодження, коли сервіс AWS EB не працює так, як ви очікуєте. Якщо ви не можете з’ясувати, чому середовище не працює, швидше за все, Docker-контейнер не запустився належним чином.
текст перекладу
Запустіть цю команду як root:
$ sudo su
_# bash -x /opt/elasticbeanstalk/hooks/appdeploy/pre/04run.sh_
Там ви побачите команду docker run
. Скопіюйте та вставте цю команду, замінивши Docker-образ з aws_beanstalk/staging-app
на aws_beanstalk/current-app
. Наприклад:
_docker_ _run_ -_d_ --_env_ _PARAM3=_ _XXXXX_ --_env_ _PARAM4=_ _XXXXX_ --_env_ _PARAM1=_ _XXXXX_ --_env_ _PARAM2=_ _XXXXX_ --_env_ _PARAM5=_ _XXXXX_ --_env_ _AWS_SECRET_KEY=XXXXX_ --_env_ _AWS_ACCESS_KEY_ID=_ _XXXXX_ --_env_ _RDS_PORT=3306_ --_env_ _RDS_PASSWORD=_ _XXXXX_ --_env_ _RDS_USERNAME=_ _XXXXX_ --_env_ _RDS_HOSTNAME=_ _XXXXX_ --_env_ _RDS_DB_NAME=_ _XXXXX_ -_v_ _/var/log/eb_-_docker/containers/eb_-_current_-_app:/var/log/nginx_ _aws_beanstalk/staging_-_app_
на:
_docker_ _run_ -_d_ --_env_ _PARAM3=_ --_env_ _PARAM4=_ --_env_ _PARAM1=_ --_env_ _PARAM2=_ --_env_ _PARAM5=_ --_env_
_AWS_SECRET_KEY=_ --_env_ _AWS_ACCESS_KEY_ID=_ --_env_ _RDS_PORT=3306_ --_env_ _RDS_PASSWORD=blah_ --_env_
_RDS_USERNAME=blah_ --_env_ _RDS_HOSTNAME=blah_ --_env_ _RDS_DB_NAME=blah_ -_v_ _/var/log/eb_-_docker/containers_
_eb_-_current_-_app:/var/log/nginx_ _aws_beanstalk/current_-_app_
Ось сесія налагодження:
_#_ _docker_ _ps_
_CONTAINER_ _ID_ _IMAGE_ _COMMAND_ _CREATED_ _STATUS_ _PORTS_ _NAMES_
_163ebbeedede_ _aws_beanstalk/current_-_app:latest_ _“/bin/sh_ -_c_ _docker/b_ _About_ _an_ _hour_ _ago_ _Up_ _About_ _an_
_hour_ _3000/tcp_ _hopeful_brattain_
_#_ _docker_ _images_
_REPOSITORY_ _TAG_ _IMAGE_ _ID_ _CREATED_ _VIRTUAL_ _SIZE_
_aws_beanstalk/current_-_app_ _latest_ _14f8d8a3276b_ _About_ _an_ _hour_ _ago_ _959_._4_ _MB_
_tongueroo/hi_ _base_ _1f2b3ca72e39_ _2_ _weeks_ _ago_ _959_._4_ _MB_
_#_ _docker_ _run_ -_d_ --_env_ _PARAM3=_ --_env_ _PARAM4=_ --_env_ _PARAM1=_ --_env_ _PARAM2=_ --_env_ _PARAM5=_ --_env_
_AWS_SECRET_KEY=_ --_env_ _AWS_ACCESS_KEY_ID=_ --_env_ _RDS_PORT=3306_ --_env_ _RDS_PASSWORD=blah_ --_env_
_RDS_USERNAME=blah_ --_env_ _RDS_HOSTNAME=blah_ --_env_ _RDS_DB_NAME=blah_ -_v_ _/var/log/eb_-_docker/containers_
_eb_-_current_-_app:/var/log/nginx_ _aws_beanstalk/current_-_app_
_b7ce5a7e4d90f1f34d77545c9005b33bc414171e58685c34f2d7b988e8a00c15_
_#_ _docker_ _ps_
_CONTAINER_ _ID_ _IMAGE_ _COMMAND_ _CREATED_ _STATUS_ _PORTS_ _NAMES_
_b7ce5a7e4d90_ _aws_beanstalk/current_-_app:latest_ _“/bin/sh_ -_c_ _docker/b_ _4_ _seconds_ _ago_ _Up_ _3_ _seconds_ _3000_
_tcp_ _insane_pasteur_
_163ebbeedede_ _aws_beanstalk/current_-_app:latest_ _“/bin/sh_ -_c_ _docker/b_ _About_ _an_ _hour_ _ago_ _Up_ _About_ _an_
_hour_ _3000/tcp_ _hopeful_brattain_
_#_ _docker_ _stop_ _b7ce5a7e4d90_
_b7ce5a7e4d90_
У цій сесії налагодження в кінці є два Docker-контейнери, оскільки перший контейнер запустився нормально. Якщо контейнери не працюють, то показана вище техніка налагодження буде дуже корисною. Найчастіше проблема полягає у неправильній конфігурації додатку через неправильну або відсутню змінну середовища. Якщо ви змогли запустити Docker-контейнер локально без проблем і впевнені, що використовуєте той самий Docker-образ, то проблема, швидше за все, в змінних середовища.
У наступному пості я розповім про налаштування nginx та Elastic Beanstalk: Під капотом AWS Elastic Beanstalk Частина 2.
Дякую, що дочитали до кінця. Якщо цей пост виявився корисним, буду дуже вдячний, якщо ви порекомендуєте його (натиснувши на кнопку "clap"), щоб інші теж могли його знайти! Також підключайтеся до мене на LinkedIn.
P.S. Обов'язково приєднуйтесь до розсилки BoltOps, щоб отримувати безкоштовні поради та оновлення по DevOps.
Перекладено з: Under the Hood of AWS Elastic Beanstalk Part 1