Постоянная интеграция / развертывание / доставка в Google App Engine, слишком рискованно?
Недавно мы настроили непрерывную интеграцию / развертывание / доставку веб-приложения nodejs в Google App Engine. Сервер CI (GitLabCI) выполняет установку зависимостей, сборку, тестирование и развертывание в интеграцию / продакшн в зависимости от отрасли (разработка / мастер).
На сегодняшний день единственные ошибки, с которыми мы столкнулись, были на этапе зависимостей, и поэтому нас это не особо волновало. Но вчера (21/10/16) произошел широкомасштабный сбой DNS, и в середине этапа развертывания произошел сбой конвейера, что привело к поломке продукта. Просто повторный запуск конвейера сделал работу, но проблему можно воспроизвести в любое время.
Мои вопросы:
- Как мы можем решить такие проблемы с сетью в процессе непрерывного развертывания?
- Является ли непрерывное развертывание в Google App Engine действительно хорошей идеей?
- Если да, то каков метод развертывания App Engine? Я не нахожу никаких соответствующих документов об этом...
На данный момент у нас есть только две версии "dev" и "prod", которые обновляются после фиксации, но в случайное время я мог наблюдать странное поведение.
Любые отзывы / предложения / отзывы очень приветствуются!
Пример трассировки стека, касающийся проблем с сетью, о которых я говорю:
DEBUG: Error sending result: 'MetadataServerException(HTTPError(),)'. Reason: 'PicklingError("Can't pickle <type 'cStringIO.StringO'>: attribute lookup cStringIO.StringO failed",)'
Traceback (most recent call last):
File "/google-cloud-sdk/lib/googlecloudsdk/calliope/cli.py", line 733, in Execute
resources = args.calliope_command.Run(cli=self, args=args)
File "/google-cloud-sdk/lib/googlecloudsdk/calliope/backend.py", line 1630, in Run
resources = command_instance.Run(args)
File "/google-cloud-sdk/lib/surface/app/deploy.py", line 53, in Run
return deploy_util.RunDeploy(self, args)
File "/google-cloud-sdk/lib/googlecloudsdk/command_lib/app/deploy_util.py", line 387, in RunDeploy
all_services)
File "/google-cloud-sdk/lib/googlecloudsdk/command_lib/app/deploy_util.py", line 247, in Deploy
manifest = _UploadFiles(service, code_bucket_ref)
File "/google-cloud-sdk/lib/googlecloudsdk/command_lib/app/deploy_util.py", line 115, in _UploadFiles
service, code_bucket_ref)
File "/google-cloud-sdk/lib/googlecloudsdk/api_lib/app/deploy_app_command_util.py", line 277, in CopyFilesToCodeBucketNoGsUtil
_UploadFiles(files_to_upload, bucket_ref)
File "/google-cloud-sdk/lib/googlecloudsdk/api_lib/app/deploy_app_command_util.py", line 219, in _UploadFiles
results = pool.map(_UploadFile, tasks)
File "/usr/lib/python2.7/multiprocessing/pool.py", line 251, in map
return self.map_async(func, iterable, chunksize).get()
File "/usr/lib/python2.7/multiprocessing/pool.py", line 558, in get
raise self._value
MaybeEncodingError: Error sending result: 'MetadataServerException(HTTPError(),)'. Reason: 'PicklingError("Can't pickle <type 'cStringIO.StringO'>: attribute lookup cStringIO.StringO failed",)'
DEBUG: Exception captured in Error
Traceback (most recent call last):
File "/google-cloud-sdk/lib/googlecloudsdk/core/metrics.py", line 411, in Wrapper
return func(*args, **kwds)
TypeError: Error() takes exactly 3 arguments (1 given)
ERROR: gcloud crashed (MaybeEncodingError): Error sending result: 'MetadataServerException(HTTPError(),)'. Reason: 'PicklingError("Can't pickle <type 'cStringIO.StringO'>: attribute lookup cStringIO.StringO failed",)'
Traceback (most recent call last):
File "/google-cloud-sdk/lib/gcloud.py", line 65, in <module>
main()
File "/google-cloud-sdk/lib/gcloud.py", line 61, in main
sys.exit(googlecloudsdk.gcloud_main.main())
File "/google-cloud-sdk/lib/googlecloudsdk/gcloud_main.py", line 145, in main
crash_handling.HandleGcloudCrash(err)
File "/google-cloud-sdk/lib/googlecloudsdk/command_lib/crash_handling.py", line 107, in HandleGcloudCrash
_ReportError(err)
File "/google-cloud-sdk/lib/googlecloudsdk/command_lib/crash_handling.py", line 86, in _ReportError
util.ErrorReporting().ReportEvent(error_message=stacktrace,
File "/google-cloud-sdk/lib/googlecloudsdk/api_lib/error_reporting/util.py", line 28, in __init__
self._API_NAME, self._API_VERSION)
File "/google-cloud-sdk/lib/googlecloudsdk/core/apis.py", line 254, in GetClientInstance
http_client = http.Http()
File "/google-cloud-sdk/lib/googlecloudsdk/core/credentials/http.py", line 60, in Http
creds = store.Load()
File "/google-cloud-sdk/lib/googlecloudsdk/core/credentials/store.py", line 282, in Load
if account in c_gce.Metadata().Accounts():
File "/google-cloud-sdk/lib/googlecloudsdk/core/credentials/gce.py", line 122, in Accounts
gce_read.GOOGLE_GCE_METADATA_ACCOUNTS_URI + '/')
File "/google-cloud-sdk/lib/googlecloudsdk/core/util/retry.py", line 160, in TryFunc
return func(*args, **kwargs), None
File "/google-cloud-sdk/lib/googlecloudsdk/core/credentials/gce.py", line 45, in _ReadNoProxyWithCleanFailures
raise MetadataServerException(e)
googlecloudsdk.core.credentials.gce.MetadataServerException: HTTP Error 503: Service Unavailable
DEBUG: Uploading [/builds/apps/webapp/lib/jinja2/defaults.pyc] to [151c77b4e5bdd2c38b6a2bf914fffa3a6ffa71a6]
INFO: Uploading [/builds/apps/webapp/lib/jinja2/defaults.pyc] to [151c77b4e5bdd2c38b6a2bf914fffa3a6ffa71a6]
INFO: Refreshing access_token
1 ответ
Хорошо плохо? Субъективно - значит, не по теме для SO. Предположим, вопрос заключается в том, как сделать надежное непрерывное развертывание:)
Проблема в том, что вы используете версии приложений в качестве среды CI, что означает, что вы не можете избежать поломок из-за плохой конкретной версии. Вы можете только надеяться, что восстановитесь как можно быстрее, повторно развернув версию (когда сбой закончится) - это можно автоматизировать.
Ваш производственный сайт не должен работать прямо с версии, перезаписанной CI production
В противном случае вы рискуете отключить сайт при неудачном развертывании. Вместо этого вы можете использовать новую / уникальную версию для каждого выполнения CI production
конвейер и только после этого успешно завершается, вы, наконец, переключаете трафик сайта на его версию, используя поток, описанный ниже (который также может использоваться внутри конвейеров CI, если используются другие приложения вместо версий приложений в качестве сред CI)
От развертывания вашей программы:
По умолчанию команда deploy автоматически генерирует новый идентификатор версии каждый раз, когда вы его используете, и перенаправляет весь трафик на новую версию.
Чтобы переопределить это поведение, вы можете указать идентификатор версии с флагом версии:
gcloud app deploy --version myID
Вы также можете указать не отправлять весь трафик в новую версию немедленно с флагом --no-promoter:
gcloud app deploy --no-promote
Поэтому убедитесь, что вы никогда не развернете версию и сделаете ее версией назначения трафика по умолчанию на одном и том же шаге (возможно, не атомарной, если она ведется со стороны клиента). Специально для производственного приложения. Вместо:
- развернуть новую версию (
gcloud app deploy --no-promote --version ...
) - начать новую версию (
gcloud app versions ...
) и проверьте, что это работает - если он работает нормально переключи реальный трафик на него (
gcloud app services set-traffic ...
)
Таким образом, единственной критической операцией является переключение трафика, которое (надеюсь) является атомарной операцией, которая либо успешна, либо полностью отменена на стороне GAE (если нет, это ошибка GAE). Если этот шаг не пройден, приложение должно продолжать работать со старой версией.
Конечно, это предполагает, что проблемы с сетью возникают только между вами и GAE, если они также влияют на внутренние операции GAE, все ставки сняты (но те, на которые я надеюсь, должны быть исправлены довольно своевременно).