├── .deployment ├── .gitignore ├── Azure WebApp Slides ├── 001.jpeg ├── 002.jpeg ├── 003.jpeg ├── 004.jpeg ├── 005.jpeg ├── 006.jpeg ├── 007.jpeg ├── 008.jpeg ├── 009.jpeg ├── 010.jpeg ├── 011.jpeg ├── 012.jpeg ├── 013.jpeg ├── 014.jpeg ├── 015.jpeg ├── 016.jpeg ├── 017.jpeg ├── 018.jpeg ├── 019.jpeg ├── 020.jpeg ├── 021.jpeg ├── 022.jpeg ├── 023.jpeg ├── 024.jpeg ├── 025.jpeg ├── 026.jpeg ├── 027.jpeg └── 028.jpeg ├── README.md ├── azure_webapp_setup.py ├── deploy.py ├── deploy_settings.py ├── download.py ├── images ├── 1 - deployment_source.png ├── 2 - deployment_source - choose source.png ├── 3 - deployment_source - choose source - github.png ├── 4 - deployment_source - choose source - github - choose project.png ├── 5 - deployment_source - choose source - github - choose project after.png └── 6 - deployment_source - completed.png ├── ptvs_virtualenv_proxy.py ├── web.2.7.config └── web.3.4.config /.deployment: -------------------------------------------------------------------------------- 1 | [config] 2 | command = python deploy.py 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | db.sqlite3 3 | -------------------------------------------------------------------------------- /Azure WebApp Slides/001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/001.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/002.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/003.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/003.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/004.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/004.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/005.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/006.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/006.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/007.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/007.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/008.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/008.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/009.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/009.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/010.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/010.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/011.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/011.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/012.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/012.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/013.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/013.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/014.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/014.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/015.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/015.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/016.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/016.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/017.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/017.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/018.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/018.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/019.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/019.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/020.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/020.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/021.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/021.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/022.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/022.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/023.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/023.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/024.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/024.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/025.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/025.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/026.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/026.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/027.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/027.jpeg -------------------------------------------------------------------------------- /Azure WebApp Slides/028.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/Azure WebApp Slides/028.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # azure-webapp-django-setup 3 | 4 | [Azure 공식문서 - Azure에서 Django를 활용하여 웹앱 만들기](https://azure.microsoft.com/documentation/articles/web-sites-python-create-deploy-django-app/) 문서에도 배포방법을 설명하고 있습니다. 하지만 불필요한 설명이 많고 배포스크립트가 ***bat 스크립트***로 되어있어서 (암호같아요 ;;; ), 불필요한 파일들을 정리하고 코드 가독성이 좋도록 ***Azure WebApp Django Setup*** 프로젝트를 만들었습니다. 5 | 6 | 7 | ## 튜토리얼 실습 영상 8 | 9 | 본 스크립트를 통해, Azure WebApp 배포를 실습하는 영상을 찍어봤습니다. 10 | 11 | * [장고걸스 튜토리얼, Azure WebApp 배포 동영상 (한글버전)](https://www.facebook.com/askdjango/videos/634463410050050/) 12 | 13 | 14 | ## Azure WebApp 15 | 16 | [Azure](https://azure.microsoft.com/)는 Microsoft에서 서비스하고 있는 클라우드 서비스입니다. 클라우드 서비스는 크게 IaaS, PaaS, SaaS로 구분됩니다. Azure PaaS서비스로서 [Azure WebApp](https://azure.microsoft.com/services/app-service/web/)가 서비스되고 있습니다. 17 | 18 | Azure WebApp의 주요 특징 19 | 20 | * 인프라 운영부담이 없습니다. (PaaS의 일반적인 특징) 21 | * Github 등을 활용한 지속적인 배포지원 : Github 에 올리는 것만으로 배포를 수행할 수 있습니다. 22 | * 지원언어 : .NET, Java, PHP, Node.js 및 ***Python*** 23 | * 배포된 프로그램은 윈도우, IIS 웹서버 상에서 구동됩니다. 24 | 25 | 26 | ## Azure WebApp Django Setup 27 | 28 | Azure WebApp 상에서 Django 애플리케이션을 돌리기 위해서는, 다음 파일들이 필요합니다. 29 | 30 | * .deployment : 배포 스크립트 지정 31 | * deploy.py : 실질적인 배포작업을 수행하는 파이썬 스크립트 32 | * deploy\_settings.py : 배포 환경설정 33 | * web.3.4.config : 파이썬 3.4용 웹서비스 설정 34 | * web.2.7.config : 파이썬 2.7용 웹서비스 설정 35 | * ptvs\_virtualenv\_proxy.py : Python Tools for Visual Studio 용 가상환경 프록시 36 | 37 | 명령 한 줄 만으로 Azure Webapp Django 배포에 필요한 위 파일들이 모두 생성이 됩니다. 38 | 39 | 파이썬 `3.4` 와 `2.7` 을 지원합니다. 디폴트로 `3.4` 로 설정되어있습니다. `2.7` 로 설정하실려면, `deploy_settings.py` 에서 `PYTHON_VERSION` 을 `2.7`로 수정하시면 됩니다. 40 | 41 | 42 | ## 소스코드 선행작업 43 | 44 | * 스크립트 실행을 위해 `requests` 라이브러리가 필요합니다. 아래 명령으로 설치해주세요. 45 | ``` 46 | 쉘> pip install requests 47 | ``` 48 | * 디렉토리 ROOT는 직접적으로 Django 프로젝트로 시작해야 합니다. 49 | * 지원되는 구조 50 | * manage.py 51 | * myproject 디렉토리 52 | * 하위 디렉토리에 django 프로젝트를 두고자할 경우, `web.3.4.config` 수정이 필요합니다. (파이썬 2.7을 쓰시고자 하실 경우에는 `web.2.7.config` 파일을 수정해주세요.) 53 | 54 | * 프로젝트 ROOT 에 `requirements.txt` 파일이 꼭 필요하며, 현 Django 프로젝트 구동에 필요한 파이썬 팩키지들을 모두 명시해주세요. Azure WebApp 배포 시에 본 `requirements.txt` 에 명시한 파이썬 팩키지가 자동설치됩니다. 아래는 예시입니다. 55 | ``` 56 | django 57 | pillow 58 | ``` 59 | * `프로젝트/settings.py` 에 STATIC/MEDIA 설정을 꼭 넣어주세요. `web.3.4.config` (혹은 `web.2.7.config`) 에서 아래 설정값으로 STATIC/MEDIA 파일 서빙을 하도록 설정되어있습니다. 60 | ```python 61 | STATIC_URL = '/static/' 62 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 63 | MEDIA_URL = '/media/' 64 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 65 | ``` 66 | 67 | 68 | ### 주의사항 69 | 70 | * `env` 이름으로 virtualenv를 만들어서 업로드하지 마세요. virtualenv는 Azure WebApp상에서 배포과정 중에 `env` 이름으로 자동으로 생성이 됩니다. 71 | 72 | 73 | ## Azure WebApp 설정파일 다운로드 74 | 75 | 다음 명령을 `manage.py` 가 있는 경로에서 명령프롬프트 혹은 터미널을 통해 실행시켜주세요. 76 | 77 | ``` 78 | 쉘> python -c "import requests; exec(requests.get('https://festi.kr/azure/d.py').content)" 79 | ``` 80 | 81 | 82 | `web.3.4.config` (혹은 `web.2.7.config`) 파일 내 `DJANGO_SETTINGS_MODULE` 값을 Azure WebApp 에서 쓸 settings 로 설정해주세요. 다음은 설정 예입니다. 83 | 84 | ```xml 85 | 86 | ``` 87 | 88 | 89 | ## Azure WebApp 에 배포하기 90 | 91 | ![](Azure WebApp Slides/004.jpeg) 92 | 93 | ![](Azure WebApp Slides/005.jpeg) 94 | 95 | ![](Azure WebApp Slides/006.jpeg) 96 | 97 | ![](Azure WebApp Slides/007.jpeg) 98 | 99 | ![](Azure WebApp Slides/008.jpeg) 100 | 101 | ![](Azure WebApp Slides/009.jpeg) 102 | 103 | ![](Azure WebApp Slides/010.jpeg) 104 | 105 | ![](Azure WebApp Slides/011.jpeg) 106 | 107 | ![](Azure WebApp Slides/012.jpeg) 108 | 109 | ![](Azure WebApp Slides/013.jpeg) 110 | 111 | ![](Azure WebApp Slides/014.jpeg) 112 | 113 | ![](Azure WebApp Slides/015.jpeg) 114 | 115 | ![](Azure WebApp Slides/016.jpeg) 116 | 117 | ![](Azure WebApp Slides/017.jpeg) 118 | 119 | ![](Azure WebApp Slides/018.jpeg) 120 | 121 | ![](Azure WebApp Slides/019.jpeg) 122 | 123 | ![](Azure WebApp Slides/020.jpeg) 124 | 125 | ![](Azure WebApp Slides/021.jpeg) 126 | 127 | ![](Azure WebApp Slides/022.jpeg) 128 | 129 | ![](Azure WebApp Slides/023.jpeg) 130 | 131 | ![](Azure WebApp Slides/024.jpeg) 132 | 133 | ![](Azure WebApp Slides/025.jpeg) 134 | 135 | ![](Azure WebApp Slides/026.jpeg) 136 | 137 | ![](Azure WebApp Slides/027.jpeg) 138 | 139 | ![](Azure WebApp Slides/028.jpeg) 140 | 141 | 142 | ## 참고 143 | 144 | Azure WebApp 에 배포한 Django 프로젝트 샘플은 [이곳](http://msdjangoisbest.azurewebsites.net/)에서 확인하실 수 있으며, 소스코드는 [github 저장소](https://github.com/askdjango/djangoisbest)에서 확인하실 수 있습니다. 145 | 146 | 147 | ## 관련 문의 148 | 149 | * [ask@festi.kr](mailto:ask@festi.kr) 150 | * [AskDjango 페이스북 페이지](http://facebook.com/askdjango) 151 | * [AskDjango 페이스북 그룹](http://facebook.com/groups/askdjango) 152 | * [AskDjango 공식 사이트](http://festi.kr) 153 | 154 | -------------------------------------------------------------------------------- /azure_webapp_setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | from __future__ import unicode_literals 4 | import os 5 | import sys 6 | try: 7 | import requests 8 | except ImportError: 9 | print('Error) Missing python package : requests', file=sys.stderr) 10 | sys.exit(1) 11 | 12 | try: 13 | from django.conf import settings 14 | except ImportError: 15 | print('Error) Missing python pakcage : django', file=sys.stderr) 16 | sys.exit(1) 17 | 18 | settings.configure( 19 | DEBUG=True, 20 | TEMPLATES=[{ 21 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 22 | }]) 23 | 24 | import django 25 | django.setup() 26 | 27 | from django.template import engines 28 | 29 | 30 | def main(settings_module): 31 | if not os.path.exists('requirements.txt'): 32 | print('Error) Missing file : requirements.txt', file=sys.stderr) 33 | sys.exit(1) 34 | 35 | base_url = 'https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/master/' 36 | filenames = ['ptvs_virtualenv_proxy.py', '.deployment', 'deploy.py', 'deploy_settings.py', 'web.3.4.config'] 37 | 38 | for filename in filenames: 39 | print(filename + ' ...', end=' ') 40 | url = os.path.join(base_url, filename) 41 | content = requests.get(url).text 42 | if os.path.exists(filename): 43 | print('overwrite.') 44 | else: 45 | print('created.') 46 | 47 | template = engines['django'].from_string(content) 48 | content = template.render({'settings_module': settings_module}) 49 | open(filename, 'wb').write(content.encode('utf8')) 50 | 51 | 52 | if __name__ == '__main__': 53 | try: 54 | settings_module = sys.argv[1] 55 | main(settings_module) 56 | except IndexError: 57 | print('Error) Missing settings_module', file=sys.stderr) 58 | sys.exit(1) 59 | 60 | print() 61 | print('Powered by AskDjango : http://festi.kr') 62 | 63 | -------------------------------------------------------------------------------- /deploy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | from __future__ import unicode_literals 4 | import datetime 5 | import os 6 | import shutil 7 | import sys 8 | import xml.etree.ElementTree as ET 9 | import deploy_settings 10 | 11 | 12 | def log(s, is_error=False): 13 | file = sys.stderr if is_error else sys.stdout 14 | prefix = datetime.datetime.now().strftime('[%Y-%m-%d %H:%M:%S] ') 15 | print(prefix + s, file=file) 16 | 17 | 18 | class AskDjango(object): 19 | def run(self): 20 | self.check_nodejs() 21 | self.check_variables() 22 | self.do_deployment() 23 | self.copy_web_config() 24 | self.do_extra_for_django() 25 | self.post_python_deployment() 26 | 27 | def check_nodejs(self): 28 | if int(os.system('where node')) != 0: 29 | self.goto_error('Missing node.js executable, plaease install node.js, if already installed make sure it can be reached from current environment.') 30 | 31 | def check_variables(self): 32 | log('check variables ...') 33 | 34 | self.deploy_src_path = self.get_deploy_src_path() 35 | self.deploy_path = self.get_deploy_path() 36 | 37 | self.python_version = deploy_settings.PYTHON_VERSION 38 | self.python_runtime = 'python-%s' % self.python_version 39 | 40 | if self.python_version.startswith('2.'): 41 | self.python_env_module = 'virtualenv' 42 | self.python_path = os.path.join(os.environ['SYSTEMDRIVE'] + os.path.sep, 'python27', 'python.exe') 43 | else: 44 | self.python_env_module = 'venv' 45 | self.python_path = os.path.join(os.environ['SYSTEMDRIVE'] + os.path.sep, 'python34', 'python.exe') 46 | 47 | self.is_skip_python_deployment = deploy_settings.IS_SKIP_PYTHON_DEPLOYMENT 48 | self.is_skip_django_extra = deploy_settings.IS_SKIP_DJANGO_EXTRA 49 | 50 | self.virtualenv_path = self.get_deploy_path('env') 51 | self.python_runtime_mark_path = os.path.join(self.virtualenv_path, self._('azure.env.{python_runtime}.txt')) 52 | 53 | self.requirements_path = self.get_deploy_path('requirements.txt') 54 | self.requirements_name = os.path.basename(self.requirements_path) 55 | 56 | self.config_src_path = self.get_deploy_src_path(self._('web.{python_version}.config')) 57 | self.config_dst_path = self.get_deploy_path('web.config') 58 | 59 | self.next_manifest_path = os.environ.get('NEXT_MANIFEST_PATH', None) 60 | self.prev_manifest_path = os.environ.get('PREVIOUS_MANIFEST_PATH', None) 61 | if self.next_manifest_path is None: 62 | self.next_manifest_path = self.current('..', 'artifacts', 'manifest') 63 | if self.prev_manifest_path is None: 64 | self.prev_manifest_path = self.current('..', 'artifacts', 'manifest') 65 | 66 | # https://github.com/projectkudu/kudu/wiki/Post-Deployment-Action-Hooks 67 | self.post_deployment_action = os.environ.get('POST_DEPLOYMENT_ACTION', None) 68 | 69 | self.is_inplace_deployment = (os.environ.get('IN_PLACE_DEPLOYMENT', None) == '1') 70 | 71 | def do_deployment(self): 72 | log('Handling python deployment.') 73 | if not self.is_inplace_deployment: 74 | kudu_sync_cmd = os.environ.get('KUDU_SYNC_CMD', None) 75 | if kudu_sync_cmd is None: 76 | log('Installing Kudu Sync') 77 | self.run_cmd('npm install kudusync -g --silent') 78 | # Locally just running "kuduSync" would also work 79 | kudu_sync_cmd = os.path.join(os.environ['appdata'], 'npm', 'kuduSync.cmd') 80 | 81 | self.run_cmd( 82 | '{kudu_sync_cmd} --verbose=300 ' 83 | '--from="{deploy_src_path}" --to="{deploy_path}" ' 84 | '--nextManifest="{next_manifest_path}" --previousManifest="{prev_manifest_path}" ' 85 | '--ignore=".git;.hg;.deployment;deploy.cmd;deploy.py"', kudu_sync_cmd=kudu_sync_cmd) 86 | 87 | if not os.path.exists(self.requirements_path): 88 | log('Missing %s' % os.path.basename(self.requirements_path)) 89 | self.post_python_deployment() 90 | 91 | elif self.is_skip_python_deployment: 92 | log('skip python deployment.') 93 | self.post_python_deployment() 94 | 95 | else: 96 | log('create virtual environment ...') 97 | 98 | if os.path.exists(self.python_runtime_mark_path): 99 | log('Found compatible virtual environment.') 100 | else: 101 | if os.path.exists(self.virtualenv_path): 102 | log('Deleting incompatible virtual environment.') 103 | shutil.rmtree(self.virtualenv_path) 104 | 105 | log(self._('Creating {python_runtime} virtual environment.')) 106 | 107 | self.run_cmd('{python_path} -m {python_env_module} env') 108 | 109 | open(self.python_runtime_mark_path, 'wb').truncate() 110 | 111 | log('Pip install requirements.') 112 | self.run_cmd('env\scripts\pip install --verbose -r "{requirements_path}"') 113 | 114 | def copy_web_config(self): 115 | log('copy web.config ...') 116 | if os.path.exists(self.config_src_path): 117 | log(self._('Overwriting web.config with web.{python_version}.config')) 118 | shutil.copyfile(self.config_src_path, self.config_dst_path) 119 | 120 | def do_extra_for_django(self): 121 | log('do_extra_for_django ...') 122 | os.chdir(self.deploy_path) 123 | if os.path.exists(self.get_deploy_path('manage.py')): 124 | if os.path.exists(self.get_deploy_path('env', 'lib', 'site-packages', 'django')): 125 | root = ET.parse(self.config_dst_path).getroot() 126 | django_settings_module = root.find(".//*[@key='DJANGO_SETTINGS_MODULE']").get('value') 127 | 128 | if not self.is_skip_django_extra: 129 | log('Collecting Django static files.') 130 | if not os.path.exists(self.get_deploy_path('static')): 131 | os.makedirs(self.get_deploy_path('static')) 132 | 133 | self.run_cmd('env\scripts\python manage.py collectstatic --settings={django_settings_module} --noinput --clear', 134 | django_settings_module=django_settings_module) 135 | 136 | def post_python_deployment(self): 137 | if self.post_deployment_action: 138 | self.run_cmd(self.post_deployment_action) 139 | sys.exit(0) 140 | 141 | def run_cmd(self, cmd, fail_message=None, **kwargs): 142 | os.chdir(self.deploy_path) 143 | cmd = self._(cmd, **kwargs) 144 | log(cmd) 145 | if int(os.system(cmd)) != 0: 146 | if fail_message: 147 | log(fail_message, is_error=True) 148 | self.goto_error() 149 | 150 | def goto_error(self, message=None): 151 | if message: 152 | log(message, is_error=True) 153 | sys.exit(1) 154 | 155 | def _(self, s, **kwargs): 156 | kwargs.update(self.__dict__) 157 | return s.format(**kwargs) 158 | 159 | def current(self, *args): 160 | current_dir_path = os.path.abspath(os.path.dirname(__file__)) 161 | return os.path.join(current_dir_path, *args) 162 | 163 | def get_deploy_src_path(self, *args): 164 | deployment_source = os.environ.get('DEPLOYMENT_SOURCE', self.current()) 165 | return os.path.join(deployment_source, *args) 166 | 167 | def get_deploy_path(self, *args): 168 | deployment_target = os.environ.get('DEPLOYMENT_TARGET', self.current('..', 'artifacts', 'wwwroot')) 169 | return os.path.join(deployment_target, *args) 170 | 171 | 172 | if __name__ == '__main__': 173 | AskDjango().run() 174 | 175 | -------------------------------------------------------------------------------- /deploy_settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | PYTHON_VERSION = '3.4' 5 | 6 | assert(PYTHON_VERSION in ('3.4', '2.7')) 7 | 8 | 9 | # 가상환경을 env 디렉토리에 이미 생성하셨다면, True로 설정해주세요. 10 | IS_SKIP_PYTHON_DEPLOYMENT = False 11 | 12 | # Azure WebApp 상에서 collectstatic 명령을 수행하지 않으실려면, True로 설정해주세요. 13 | IS_SKIP_DJANGO_EXTRA = False 14 | 15 | -------------------------------------------------------------------------------- /download.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | import requests 4 | 5 | 6 | def main(): 7 | base = 'https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/master/' 8 | names = ['.deployment', 'deploy.py', 'deploy_settings.py', 'web.2.7.config', 'web.3.4.config', 'ptvs_virtualenv_proxy.py'] 9 | 10 | for name in names: 11 | url = base + name 12 | print('downloading {} ...'.format(name)) 13 | open(name, 'wb').write(requests.get(url).content) 14 | 15 | 16 | if __name__ == '__main__': 17 | main() 18 | 19 | -------------------------------------------------------------------------------- /images/1 - deployment_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/images/1 - deployment_source.png -------------------------------------------------------------------------------- /images/2 - deployment_source - choose source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/images/2 - deployment_source - choose source.png -------------------------------------------------------------------------------- /images/3 - deployment_source - choose source - github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/images/3 - deployment_source - choose source - github.png -------------------------------------------------------------------------------- /images/4 - deployment_source - choose source - github - choose project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/images/4 - deployment_source - choose source - github - choose project.png -------------------------------------------------------------------------------- /images/5 - deployment_source - choose source - github - choose project after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/images/5 - deployment_source - choose source - github - choose project after.png -------------------------------------------------------------------------------- /images/6 - deployment_source - completed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/askdjango/azure-webapp-django-setup/ccfedb591f7277cdb199172e3a35f14b4f69ae7b/images/6 - deployment_source - completed.png -------------------------------------------------------------------------------- /ptvs_virtualenv_proxy.py: -------------------------------------------------------------------------------- 1 |  # ############################################################################ 2 | # 3 | # Copyright (c) Microsoft Corporation. 4 | # 5 | # This source code is subject to terms and conditions of the Apache License, Version 2.0. A 6 | # copy of the license can be found in the License.html file at the root of this distribution. If 7 | # you cannot locate the Apache License, Version 2.0, please send an email to 8 | # vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 9 | # by the terms of the Apache License, Version 2.0. 10 | # 11 | # You must not remove this notice, or any other, from this software. 12 | # 13 | # ########################################################################### 14 | 15 | import datetime 16 | import os 17 | import sys 18 | 19 | if sys.version_info[0] == 3: 20 | def to_str(value): 21 | return value.decode(sys.getfilesystemencoding()) 22 | 23 | def execfile(path, global_dict): 24 | """Execute a file""" 25 | with open(path, 'r') as f: 26 | code = f.read() 27 | code = code.replace('\r\n', '\n') + '\n' 28 | exec(code, global_dict) 29 | else: 30 | def to_str(value): 31 | return value.encode(sys.getfilesystemencoding()) 32 | 33 | def log(txt): 34 | """Logs fatal errors to a log file if WSGI_LOG env var is defined""" 35 | log_file = os.environ.get('WSGI_LOG') 36 | if log_file: 37 | f = open(log_file, 'a+') 38 | try: 39 | f.write('%s: %s' % (datetime.datetime.now(), txt)) 40 | finally: 41 | f.close() 42 | 43 | ptvsd_secret = os.getenv('WSGI_PTVSD_SECRET') 44 | if ptvsd_secret: 45 | log('Enabling ptvsd ...\n') 46 | try: 47 | import ptvsd 48 | try: 49 | ptvsd.enable_attach(ptvsd_secret) 50 | log('ptvsd enabled.\n') 51 | except: 52 | log('ptvsd.enable_attach failed\n') 53 | except ImportError: 54 | log('error importing ptvsd.\n'); 55 | 56 | def get_wsgi_handler(handler_name): 57 | if not handler_name: 58 | raise Exception('WSGI_HANDLER env var must be set') 59 | 60 | if not isinstance(handler_name, str): 61 | handler_name = to_str(handler_name) 62 | 63 | module_name, _, callable_name = handler_name.rpartition('.') 64 | should_call = callable_name.endswith('()') 65 | callable_name = callable_name[:-2] if should_call else callable_name 66 | name_list = [(callable_name, should_call)] 67 | handler = None 68 | 69 | while module_name: 70 | try: 71 | handler = __import__(module_name, fromlist=[name_list[0][0]]) 72 | for name, should_call in name_list: 73 | handler = getattr(handler, name) 74 | if should_call: 75 | handler = handler() 76 | break 77 | except ImportError: 78 | module_name, _, callable_name = module_name.rpartition('.') 79 | should_call = callable_name.endswith('()') 80 | callable_name = callable_name[:-2] if should_call else callable_name 81 | name_list.insert(0, (callable_name, should_call)) 82 | handler = None 83 | 84 | if handler is None: 85 | raise ValueError('"%s" could not be imported' % handler_name) 86 | 87 | return handler 88 | 89 | activate_this = os.getenv('WSGI_ALT_VIRTUALENV_ACTIVATE_THIS') 90 | if not activate_this: 91 | raise Exception('WSGI_ALT_VIRTUALENV_ACTIVATE_THIS is not set') 92 | 93 | def get_virtualenv_handler(): 94 | log('Activating virtualenv with %s\n' % activate_this) 95 | execfile(activate_this, dict(__file__=activate_this)) 96 | 97 | log('Getting handler %s\n' % os.getenv('WSGI_ALT_VIRTUALENV_HANDLER')) 98 | handler = get_wsgi_handler(os.getenv('WSGI_ALT_VIRTUALENV_HANDLER')) 99 | log('Got handler: %r\n' % handler) 100 | return handler 101 | 102 | def get_venv_handler(): 103 | log('Activating venv with executable at %s\n' % activate_this) 104 | import site 105 | sys.executable = activate_this 106 | old_sys_path, sys.path = sys.path, [] 107 | 108 | site.main() 109 | 110 | sys.path.insert(0, '') 111 | for item in old_sys_path: 112 | if item not in sys.path: 113 | sys.path.append(item) 114 | 115 | log('Getting handler %s\n' % os.getenv('WSGI_ALT_VIRTUALENV_HANDLER')) 116 | handler = get_wsgi_handler(os.getenv('WSGI_ALT_VIRTUALENV_HANDLER')) 117 | log('Got handler: %r\n' % handler) 118 | return handler 119 | -------------------------------------------------------------------------------- /web.2.7.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /web.3.4.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | --------------------------------------------------------------------------------