前回、IISでDjangoをホストしてみたので、今回はそれをデプロイする方法を試してみます。
事前調査
実現できそうなデプロイツールの調査
今後のことも考えて、Windowsに限定しないデプロイツールを調べてみたところ、
などがありました。
そのため、Windowsで使えそうなFabricを、今回は試してみることにしました。
Windows上でのSSHDについて
上記のFabricの記事ではfreeSSHd
を使っていました。
freeSSHd and freeFTPd - open source SSH and SFTP servers for Windows
ただ、最近Microsoft公式のOpenSSHがプレリリースされたという記事を見かけました。
- PowerShell/Win32-OpenSSH
- OpenSSH for Windows Update - Windows PowerShell Blog - Site Home - MSDN Blogs
せっかくなので、今回使うOpenSSHはプレリリース版のWin32-OpenSSH
としました。
なお、現時点ではWin32-OpenSSHのバージョンは、
- 10_13_2015
- 11_09_2015
の2つがあります。
Releases · PowerShell/Win32-OpenSSH
後者のほうがバージョンが新しく、また、Widnowsサービスに登録もできるようになっています。
ただ、実際に使ってみたところ、同じ内容のfabfile・環境にも関わらず、11_09_2015
版では文字化けやエラーが出て動作しませんでした。
10_13_2015
[<host_name>] run: PsExec.exe -u Administrator -p root %windir%\system32\inetsrv\appcmd list site [<host_name>] out: [<host_name>] out: PsExec v2.11 - Execute processes remotely [<host_name>] out: Copyright (C) 2001-2014 Mark Russinovich [<host_name>] out: Sysinternals - www.sysinternals.com [<host_name>] out: [<host_name>] out: C:\WINDOWS\system32\inetsrv\appcmd exited with error code 0. [<host_name>] out:
11_09_2015
2行目の[20h[?7h
が文字化けをしている部分です。
[<host_name>] run: PsExec.exe -u Administrator -p root %windir%\system32\inetsrv\appcmd list site [<host_name>] out: [20h[?7h [<host_name>] out: PsExec v2.11 - Execute processes remotely [<host_name>] out: Copyright (C) 2001-2014 Mark Russinovich [<host_name>] out: Sysinternals - www.sysinternals.com [<host_name>] out: [<host_name>] out: C:\WINDOWS\system32\inetsrv\appcmd exited with error code -1073741502. [<host_name>] out:
今後のリリースで改善されるかもしれないので、今回は正常に動作する10_13_2015
版を使うことにしました。
環境
ローカルマシン
- Windows7
- Python 2.7
- Fabric 1.10.2
- Python 3.4
- Django 1.8.6
- wfastcgi 2.2
- Git for Windows 2.6.2
- インストール時の
Adjusting your PATH environment
はUse Git and optional Unix tools...
- インストール時の
- Microsoft Visual C++ Compiler for Python 2.7
- TeraTerm
- Win32-OpenSSHへの接続確認
リモートマシン
事前に構築しておくもの
- Windows7
- .NET Framework 4.5
- Python 2.7
- IIS
- 使用ポートは
80
Default Web Site
にバインド
- 使用ポートは
- PsExecは以下に展開済
C:\PSTools\PsExec.exe
- ダウンロード元:PsExec - TechNet
後述の手順で構築するもの
- ファイアウォールのポート
22
をオープン Win32-OpenSSH
の設定と、SSHDの起動- Bitbucketの設定
- PsExecで管理者コマンドプロンプトを使うため、ローカルのAdministratorアカウントを有効化
Fabricで構築するもの
- Djangoアプリは、Bitbucketのプライベートリポジトリから
git clone
- virtualenv環境の構築
- git cloneした
requirements.txt
より、virtualenv環境へpip install
- Djangoの
manage.py migrate
の実行 Default Web Site
のPhysicalPath
を設定- IISのサービス停止と起動
今回扱わないもの
- Cuisine
- 冪等性のためのものだが、今回は単純化するために使用せず
- 使い方の参考
リモートマシンの環境設定
以下を参考に、リモートマシンのOpen-SSHまわりの環境設定を行います。
- Windows 版 OpenSSH が登場したので使い方を解説してみる | Webセキュリティの小部屋
- Deploy Win32 OpenSSH · PowerShell/Win32-OpenSSH Wiki
- 最新版とは内容が異なるため、過去のコミット内容を参照しています
- Releases · PowerShell/Win32-OpenSSH
OpenSSHまわりの設定
管理者のコマンドプロンプトで実行します。
# ダウンロード C:\>bitsadmin.exe /TRANSFER OpenSSH https://github.com/PowerShell/Win32-OpenSSH/releases/download/10_13_2015/OpenSSH-Win32.zip c:\OpenSSH-Win32.zip ... DISPLAY: 'OpenSSH' TYPE: DOWNLOAD STATE: TRANSFERRED PRIORITY: NORMAL FILES: 1 / 1 BYTES: 10295557 / 10295557 (100%) Transfer complete. # zipファイルを解凍 # コマンドプロンプトからPowerShellを呼んでいるけど、標準で解凍する方法がこれしかないので仕方ない... C:\>powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('C:\OpenSSH-Win32.zip', 'C:\'); }" # key-gen C:\>cd OpenSSH-Win32 C:\OpenSSH-Win32>ssh-keygen.exe -t rsa -f ssh_host_rsa_key C:\OpenSSH-Win32>ssh-keygen.exe -t dsa -f ssh_host_dsa_key C:\OpenSSH-Win32>ssh-keygen.exe -t ecdsa -f ssh_host_ecdsa_key C:\OpenSSH-Win32>ssh-keygen.exe -t ed25519 -f ssh_host_ed25519_key # C:\OpenSSH-Win32の中に鍵ができているのを確認 C:\OpenSSH-Win32>dir /b setup-ssh-lsa.cmd sftp-server.exe sftp.exe ssh-keygen.exe ssh.exe sshd.exe sshd_config ssh_host_dsa_key ssh_host_dsa_key.pub ssh_host_ecdsa_key ssh_host_ecdsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub ssh_host_rsa_key ssh_host_rsa_key.pub x64 x86 # ファイアウォールのポートをオープン C:\OpenSSH-Win32>netsh advfirewall firewall add rule protocol=tcp localport=22 dir=in action=allow name="SSH" OK
参考
- Windowsのコマンドプロンプトでファイルを取得するには? - Qiita
- How can you zip or unzip from the command prompt using ONLY Windows' built-in capabilities? - Stack Overflow
Bitbucketまわりの設定
Bitbucketからcloneなどができるよう、以下を参考に作業を行います。
Bitbucketアカウント作成〜ローカルのGit既存プロジェクトをインポート(push) | EasyRamble
作業は、以下のとおりです。
- Git for Windowsのインストール
- Bitbucket接続用の鍵生成とBitbucketへの鍵登録
C:\OpenSSH-Win32>ssh-keygen -t rsa -f %USERPROFILE%\.ssh\bitbucket_rsa
- Bitbucket接続設定ファイル(
%USERPROFILE%\.ssh\config
)の作成
Host bitbucket.org User git port 22 Hostname bitbucket.org IdentityFile ~/.ssh/bitbucket_rsa TCPKeepAlive yes IdentitiesOnly yes
SSHD起動
C:\>cd PSTools C:\PSTools>psexec -s cmd.exe /c "cd "c:\openssh-win32" && sshd.exe" PsExec v2.11 - Execute processes remotely Copyright (C) 2001-2014 Mark Russinovich Sysinternals - www.sysinternals.com [Build Oct 13 2015 10:49:11]
SSHD起動後、ローカルマシンにて、
を行い、問題なく接続できることを確認します。
SSHDを停止する場合は、Ctrl + C
にて行います。
リモートマシンのAdministratorユーザーを有効化
Fabricでデプロイする場合、run()
では一般ユーザーでの操作となるようです。そのため、そのままではIISの設定など管理者権限が必要なものが操作できません。
この場合、PsExecを使って管理者ユーザーを指定することで、管理者権限で操作ができるようになります。
そこで、リモートマシン上のローカルAdministrator
ユーザーを有効化します。
なお、ドメインユーザーでも試してみましたが、ローカルのAdministratorsグループに所属している場合でも、以下の影響か、「ユーザー名またはパスワードが正しくありません。」というエラーが表示されて実行できませんでした。
User Account Control (UAC) and PsExec - RioSec
ローカルマシンにて、Djangoアプリの作成
以下の内容で、Djangoアプリを作成します。
ひな型の取得
今回は、Djangoアプリのひな型としてthinkAmi-sandbox/Django_hello_worldを使います。
Download Zip
ボタンでローカルにダウンロード- ダウンロードしたzipファイルを
C:\Django_wfastcgi_sample
として展開C:\Django_wfastcgi_sample\env
、C:\Django_wfastcgi_sample\my_project
な形で配置
virtualenv環境の用意
# Python2.7と3.4が入っているため、Python3.4を指定しておく C:\Django_wfastcgi_sample>virtualenv env --python=C:\Python34\python.exe C:\Django_wfastcgi_sample>env\Scripts\activate (env) C:\Django_wfastcgi_sample>pip install django ... Successfully installed django-1.8.6 (env) C:\Django_wfastcgi_sample>pip install wfastcgi ... Successfully installed wfastcgi-2.2
web.configファイルの作成
今回は、C:\Django_wfastcgi_sample\my_project\web.config
に、以下の内容で作成します。
<configuration> <appSettings> <add key="WSGI_HANDLER" value="django.core.wsgi.get_wsgi_application()" /> <add key="PYTHONPATH" value="C:\django_apps\Django_wfastcgi_sample" /> <add key="DJANGO_SETTINGS_MODULE" value="my_project.settings" /> </appSettings> <system.webServer> <handlers> <add name="Python FastCGI" path="*" verb="*" modules="FastCgiModule" scriptProcessor="c:\django_apps\Django_wfastcgi_sample\env\scripts\python.exe|c:\django_apps\Django_wfastcgi_sample\env\lib\site-packages\wfastcgi.py" resourceType="Unspecified" /> </handlers> </system.webServer> </configuration>
requirement.txtの作成
C:\Django_wfastcgi_sample\my_project\requirements.txt
として作成します。
(env) C:\Django_wfastcgi_sample>cd my_project (env) C:\Django_wfastcgi_sample\my_project>pip freeze -l > requirements.txt
GitHubへpush
GUIでBitbucketにリポジトリ作成後に、以下の作業を行います。
# Gitリポジトリの作成 (env) C:\Django_wfastcgi_sample>git init (env) C:\Django_wfastcgi_sample>git add . (env) C:\Django_wfastcgi_sample>git commit -m "first commit" # remoteにBitbucketを追加 (env) C:\Django_wfastcgi_sample>git remote add origin git@bitbucket.org:<USER_NAME>/django_wfastcgi_sample.git # git push (env) C:\Django_wfastcgi_sample>git push -u origin master ... Branch master set up to track remote branch master from origin.
fabfile.py作成時の調査
fabfile.pyを作成するにあたり、いくつか悩んだことがあったので、メモとして残しておきます。
Win32-OpenSSHでのrunについて
Fabricのタスクの中で、
run("pip list") run("pip install virtualenv")
のように連続してrun
すると、
socket.error: [Errno 10054] 既存の接続はリモート ホストに強制的に切断されました。
と2回目のrun
実行時にエラーが発生し、動作が中断されてしまいました。これは10_13_2015
と11_09_2015
のどちらのバージョンでも発生します。
一方、freeSSHdを使った場合には発生しませんでいた。
Win32-OpenSSHのコネクションまわりに何かあると考え、run()
後にdisconnect_all()
したところ、エラーが発生しませんでした。
ただ、run()
ごとにdisconnect_all()
するのは見た目がイマイチなので、disconnect_all()
する以下のデコレータを作りました。
def disconnect(func): import functools @functools.wraps(func) def wrapper(*args, **kwargs): result = func(**kwargs) disconnect_all() return result return wrapper
これにより
@disconnect def pip_list(): return run("pip list")
のようにすれば、タスク実行後にdisconnect_all()
が自動実行されるようになりました。
警告表示の回避
実行ログの中に
No handlers could be found for logger "paramiko.transport"
という表示があり、気になりました。
そのため、stackoverflowを参考にして、以下の方法で回避することができました。
python - No handlers could be found for logger paramiko - Stack Overflow
import paramiko paramiko.util.log_to_file(r"filename.log")
コマンドラインからのIISのサービス開始/停止
関係するサービスは
- Windows Process Activation Service (WAS)
- World Wide Web Publishing Service (W3SVC)
のため、以下を参考にして停止します。
Web サーバーを開始または停止する (IIS 7) - TechNet
コマンドラインからのIISのDefault Web SiteのPhysicalPathの変更
以下を参考に、appcmdを使って変更しました。
アプリケーション コンテンツの物理パスを変更する (IIS 7) - TechNet
なお、appcmdについては、以下の記事も参考になりました。
- Appcmd.exe (IIS 7) - TechNet
- 連載:ASP.NETによる軽量業務アプリ開発:第2回 ASP.NETによるCRUD処理(パート1:管理用ページの作成) (2/4) - @IT
フォルダの存在チェック
はじめはPythonコードでやろうと考えましたが、いろいろと面倒だったため、dir
を使うことにしました。
Bitbucketのプライベートリポジトリからのclone
ローカルマシンからFabricを使った場合、SSH形式のgit clone
がエラーとなりました。また、run("ssh -T git@bitbucket.org")
もエラーとなりました。
一方、Fabricを使わずにローカルマシンでgit clone
した場合は、SSH形式でも正常に動作しました。
SSHまわりの環境やコードをいろいろと調査すれば良いのかもしれませんが、時間の都合上、今回はhttpsのパスワード形式でのcloneをすることにしました。
参考:Githubにhttpsでgit cloneできない時やったこと - Qiita
virtualenv環境下での実行
Fabricのprefix
を使い、
with prefix(path\to\Scripts\activate.bat): run("pip list")
のようにすれば、virtualenv環境下でpip list
が実行されました。
参考:python - Activate a virtualenv via fabric as deploy user - Stack Overflow
なお、Windowsの場合、上記のブログにもある通りenv.shell
がPowerShellだとprefix()
が使えません。
そのため、Windowsでvirtualenvを使う場合には、env.shell = "Cmd.exe /C"
とコマンドプロンプトで実行するしかないようです。
FabricでPsExecの引数として"
を含む内容がうまく動作しない
IISのDefault Web Site
に対する変更を行うため、
run(PsExec.exe -u Administrator -p {password} %windir%\system32\inetsrv\appcmd set vdir "Default Web Site/" /physicalPath:C:\django_apps\Django_wfastcgi_sample\my_project)
としましたが、Fabric経由だとうまく動作しませんでした。ダブルクオーテーションがうまく認識されないのか、パラメータが足りない旨のエラーが出ました。
そこで、今回はバッチファイルを作成して、Fabricからそれを叩くようにしました。
C:\Django_wfastcgi_sample\iis_default_web_site_settings.bat
として、以下の内容で作成します。
%windir%\system32\inetsrv\appcmd set vdir "Default Web Site/" /physicalPath:C:\django_apps\Django_wfastcgi_sample\my_project
fabfile.pyでは、
run(PsExec.exe -u Administrator -p {password} C:\django_apps\django_wfastcgi_sample\iis_default_web_site_settings.bat)
のようにしてバッチファイルを実行すれば、設定が反映されました。
fabfile.pyの内容
長くなるため、GitHubへのリンクとしておきます。
Django_wfastcgi_sample/fabfile.py - thinkAmi-sandbox/Django_wfastcgi_sample
あとは、ローカルマシンにて
- Python2.7のvirtualenv環境の用意
- virtualenv環境にFabricのインストール
- 上記のfabfile.pyを使って、
fab deploy
を行うことで、Djangoアプリをデプロイすることができました。
参考
公式ドキュメント
英語版の他、日本語版も用意されていました。
ソースコード
全体のソースコードをGitHubに上げておきました。
thinkAmi-sandbox/Django_wfastcgi_sample
fabfile.pyはfabric
の下にあります。