Grailsのデプロイスクリプトを作成する

デプロイスクリプトが必要な理由



Grailsアプリケーションは、WARで非常に簡単に構築できます。 これは次のように行われます。



grails war
      
      





WARが行われているという事実に加えて、このWARをサーバーにインストールしたいのです。 私たちの場合、これはTomcatです。 手動インストールには大騒ぎが必要です。

  1. サーバーを停止します。 プロセスが停止しない場合、プロセスを強制終了します。
  2. 古いアプリケーションファイルを削除する(念のため)
  3. 新しいWARをサーバーにコピーします。 名前を変更する必要がある場合があります(ROOT.warなど)
Mavenでは、これは、たとえば、貨物プラグインによって実行できます。 しかし、彼には多くの冒険とカスタマイズがあり、サーバーの機能を特に考慮していません。



シェルスクリプトも使用できます。 しかし、素晴らしいクロスプラットフォームのGroovy言語があるのに、なぜ不快なシェル言語で書くのでしょうか?





Groovyでスクリプトを書く



Grailsを使用すると、アプリケーションのスクリプトフォルダーに追加するコマンドラインスクリプトをGroovyで簡単に作成できます。 これらのスクリプトはすべてgrails console コマンドで実行できます。 Deploy.groovyというスクリプトを作成します。



スクリプト内で、Grailsアプリケーションの構成データを使用できるのは素晴らしいことです。 たとえば、サーバー名とユーザー名は環境固有です。 Grails 1.3.7の場合、次のような構成にアクセスできます。



 depends(compile) depends(createConfig) /*   Gant,   Config.groovy */ def host = ConfigurationHolder.config?.deploy.host def username = ConfigurationHolder.config?.deploy.username
      
      





grails-app / conf / Config.groovyの内部には次のようなものがあると想定されています。



 ... environments { production { ... deploy { host = 'www1.shards.intra' username = 'deployer' } } }
      
      





ちょっとしたトリックは、構成をダウンロードするには、最初にConfig.groovyファイルをコンパイルする必要があるということです。 これを行うために、 depends(compile)を宣言しました。compileは、プロジェクトをコンパイルするための既知のGantコマンド(タスク)です。



SSHを使用します



SSHアクセスを備えたサーバー上であらゆる種類の操作を行う必要があります。 簡単にするために、私は無料のJSchライブラリを使用し、パスワードアクセスオプションに制限しました。 したがって、スクリプトは次のように始まります。



 @GrabResolver(name='jcraft', root='http://jsch.sourceforge.net/maven2') @Grab(group='com.jcraft', module='jsch', version='0.1.44') import com.jcraft.jsch.*
      
      





次に、JSchを使用して魔法の操作を行います。 次の2つが必要です。

Groovy言語は自由に使えるので、必要な機能を備えたミニDSLを作成しようとします。 SSHセッションであるSessionオブジェクト(JSchから)にいくつかの新しいメソッドを追加します。 まず、サーバーでコマンドを実行するexecメソッド:



 Session.metaClass.exec = { String cmd -> Channel channel = this.openChannel("exec") channel.command = cmd channel.inputStream = null channel.errStream = System.err InputStream inp = channel.inputStream channel.connect() int exitStatus = -1 StringBuilder output = new StringBuilder() try { while (true) { output << inp if (channel.closed) { exitStatus = channel.exitStatus break } try { sleep(1000) } catch (Exception ee) { } } } finally { channel.disconnect() } if (exitStatus != 0) { println output throw new RuntimeException("Command [${cmd}] returned exit-status ${exitStatus}") } output.toString() }
      
      





簡潔にするため、実行が成功してもメソッドは何も出力せず、エラーが発生すると実行されたコマンドの出力ストリーム全体を出力するようにしました。



ここで、ファイルをサーバーに書き込むこともできます。



 Session.metaClass.scp = { sourceFile, dst -> ChannelSftp channel = (ChannelSftp) openChannel("sftp") channel.connect() println "${sourceFile.path} => ${dst}" try { channel.put(new FileInputStream(sourceFile), dst, new SftpProgressMonitor() { private int max = 1 private int points = 0 private int current = 0 void init(int op, String src, String dest, long max) { this.max = max this.current = 0 } boolean count(long count) { current += count int newPoints = (current * 20 / max) as int if (newPoints > points) { print '.' } points = newPoints true } void end() { println '' } }) } finally { channel.disconnect() } }
      
      





実際、このメソッドの主な内容はすべて進行状況のインジケータです。



そして最後に、本格的なDSLを作成するには、構造をアタッチするクロージャーが必要です。 これは、たとえば次のように実行されます( doRemoteメソッドにフックされます)。



 Session.metaClass.doRemote = { Closure closure -> connect() try { closure.delegate = delegate closure.call() } finally { disconnect() } }
      
      





doRemoteメソッドは「ブラケット」を形成し、その中でexecおよびscpメソッドを使用できます。



最後に、デプロイ手順自体を記述します



実際、スクリプトの本文は次のようになります。



 //   .    grails help. target(main: " WAR-  .") { ..  JSch JSch jsch = new JSch() Properties config = new Properties() config.put("StrictHostKeyChecking", "no") config.put("HashKnownHosts", "yes") jsch.config = config //    ,  host  username  . ... String password = new String(System.console() .readPassword("Enter password for ${username}@${host}: ")) Session session = jsch.getSession(username, host, 22) session.setPassword(password) session.doRemote { exec "- " ... scp warFile, '/opt/tomcat/latest/webapps/ROOT.war' ... } }
      
      





実際には、WARファイルの場所と、展開手順の前に収集するのが良いことをシステムに伝える方法を理解する必要があります。



これは、 dependsと呼ばれる既知のGantチームによって行われます



 depends(clean) depends(war)
      
      





最初にプロジェクトをクリーンアップしてから、WARを収集します。 WARファイルへのアクセスに関して、不可能なことは何もありません。 grailsS​​ettings変数はすべてのスクリプトで使用でき、そこから変数の場所を確認することもできます。



 File warFile = grailsSettings.projectWarFile
      
      





GrailsのドキュメントでgrailsS​​ettingsについて詳しく読んでください。



実際には、すべてが準備ができて、最後のタッチが残ります。 スクリプト(main)で宣言されたタスクは1つだけで、デフォルトで実行するように割り当てます。



 setDefaultTarget(main)
      
      





さらに、Grails組み込みスクリプト( compilewarなど)を使用します。 それらをスクリプトにインポートするには( dependsコマンドで参照できるように)、スクリプトの先頭に次を追加します。



 includeTargets << grailsScript("Clean") includeTargets << grailsScript("Init") includeTargets << grailsScript("War")
      
      





スペースを節約するために、最終的なスクリプト全体を公開しません。 Tomcatの完成したスクリプトはこちらからご覧ください



おわりに



SSH経由でサーバーにアクセスする機能を備えたデプロイスクリプトを記述するための小さなミニフレームワークをまとめました。 次のように実行できます。



 grails deploy
      
      



ランニング

 grails help deploy
      
      



スクリプトを使用するための指示さえ取得できます:)



シェルスクリプトと比較して、これには次の利点があります。




All Articles