DevOpsでAzure FunctionsにAPIをデプロイしてみました(Java編)
前回の記事ではDevOpsでAzure Functionsに対してC#で書いた簡単なAPIのコード(プロジェクト)をデプロイしてみました。
そもそものDevOpsに関する知識不足があったことはともかくとして、C#とAzureの組み合わせであればそれは動くよね、というか、DevOpsのPipelineのタスクにもそのままのものがありましたので(今思えば)それほど苦労はしなかったです。
が、今回は「Java」なのでちょっと苦労しそうな気が。。というか、実際にC#よりは苦労しましたので、そのあたりの内容も書き記していきたいと思います。
ではいつもの通り、VS Codeでサンプルプロジェクトの作成から進めます。
「WORKSPACE」→「Create Function」を選択します。
次に以下のダイアログで「Create new project」を選択。
そしてプロジェクトを配置するフォルダを選択。
次に言語はJavaを。
バージョンは11でよいです。
あとはGroupIDやアプリケーション名等、必要な内容を入力していきます。
(以下のスクショの後に更に3、4つ入力が必要になりますが、ここでは割愛します)
ビルドツールは「Maven」を選びます。
「Add to workspace」を選択。
最終的に以下のJavaによるAPIプロジェクトが作成されます。
既に以前の記事で試しているので割愛していますが、通常であればローカルで動作確認をした方が良いです。
そして、ローカルで作成したプロジェクトをAzure DevOpsのAzure ReposにPushします。
どうやったらよいかわからない方は以下のサイト等を参考にしてください。
以下のようにAzure Reposにプロジェクト内のファイルが配置されます。
さて、ここからが本番です。
前回のC#で味を占めた私は今回も上の画面の「Set up build」ボタンであっさりPipelineが作られることを期待して、まずはこれを試してみました。
以下の画面が表示されまして、恐らく最初の2つのうちどちらかだなと考えまして、まずは2番目の「Maven package Java project Web App to Linux on Azure」を選びます。
と、その前にAzure Functionsを作成しておきます。
ランタイムスタックとそのバージョンについては「Java」と「11.0」を選択するのを忘れないように。
OSは上の選択した内容に従って「Linux」にします。
そして、DevOpsに戻ってサブスクリプションを選択します。
そしてWeb Appを選ぶ画面になるのですが、ここまで読んできた賢明なあなたは既にお気づきの通り、Pipelineは「Web App」とあるにもかかわらず、私はAzure Fucntionsを作成しています。。
ということで、当然ながら以下のようにWeb Appが表示されず、自分の愚かさを嘆きつつ、最初のPipelineの選択に戻り、一番先頭の「Maven」を選択し直します。
「Maven」の説明はビルドのみのようだったのでデプロイは別に作らないといけないんだろうなと思いつつ、まずは以下のPipelineが作られます。
やっぱりビルドだけですね。
とりあえずビルドだけでもできればOKということでビルドを実施。
早速以下のようなエラーが発生します。
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project DotNetJavaAPI: Fatal error compiling: invalid target release: 11 -> [Help 1]
調べてみると「バージョンが違うんじゃね?」的な内容。
そこで以下のように「jdkVersionOption」を「1.8」から「1.11」に変更します。
ビルドは無事終了!
では、次にデプロイのためのタスク(YAML)を作成します。
さて、ここでも選択に迷います。
他の言語であればAzure Functionsとの組み合わせがあるのですが、Javaではそれがありません。。
嫌な予感を抱きつつ、一旦一番妥当そうな「Azure Functions」を選びます。
そしてデプロイしたいFunctionsを選択します。
ここでの注意点としては「App type」の部分で作成済みのFunctionsとOSを合わせることでしょうか。
そしてできたのが以下のYAMLです。
特に見た感じだと問題無さそうな気がするので早速Pipelineを動かしてみましょう。
Azureリソースに対してデプロイする権限をPipelineに与えてよいかの確認が表示されますので、承認して次に進みます。
すると以下のようなエラーが表示されて終了してしまいました。
「##[error]Error: No package found with specified pattern: /home/vsts/work/1/s/**/*.zip<br/>Check if the package mentioned in the task is published as an artifact in the build or a previous stage and downloaded in the current job.」
調べてみると以下のサイトを発見しました。私が今回やりたいこと、そのままのような内容です。
で、エラーの内容としては「パッケージが無い」と。
確かにYAMLを見るとパッケージを作っていませんでした。
ということでこのサイトを参考にZIPの作成のためのタスクを追記します。
そして、この後もエラー、修正がしばらく続きます。
アーカイブタスクの「archiveFile」と、デプロイタスクの「package」でパッケージのパスを設定するわけですが、そんなパスは無い(のでアーカイブできない)とか、デプロイ時にパッケージが存在しない等のエラーが続き、設定に苦しみます。
そして、いよいよパス設定もうまくいき(と思われ)、アーカイブからデプロイへのパッケージの受け渡しもでき、Pipelineがデプロイまで一通り正常に終了しました。
なのですが、Postmanで試してみると404エラーで返ってきます。
アーカイブの結果を見てみると以下のように「0%」とあって、「もしかすると何も圧縮されていないのでは」疑惑がふつふつと。。
そこで、Linuxでも良かったのですが、Kuduの方が簡単だったのでデプロイ対象のFunctionsのOSをWindowsに切り替え、デプロイ先を確認すると見事なまでに空っぽでした。
しかも「b.zip」というファイル名で圧縮しているようなのですが、「b」フォルダも作成(展開)していて、仮に中身がちゃんとデプロイできたとしてもこれは動かないだろうなーという雰囲気があります。
夜中から初めて少し寝て、朝の5時頃から再開し、ここで悶々と悩んでいた私が解決のために思いついたのは「正しく動く場合のデプロイ先のディレクトリ構成を調べてみよう」というものです。
以前の記事でも書きましたが、VS CodeからFunctionsへのデプロイができますので、それを試してみます。
すると、VS Codeからデプロイしたものは
「wwwroot」フォルダ配下に「直接」Jarファイル等一式が置かれています。
しかしながらDevOpsでデプロイしたものは「wwwroot」フォルダ直下に以下のように「DotNetJavaAPIApp」があり、そこにJarファイル一式が置かれています。
つまり、1つ余計なディレクトリが間に入っています。
かなり解決に近づいてきました。
そこで気になったのがYAMLのアーカイブタスクの部分、以下の「includeRootFolder」を見てもらうと「true」になっていました。
「includeRootFolder」=「DotNetJavaAPIApp」であると考え、これを含めない、つまりfalseに変更します。
そして、Pipelineを起動し、正常に終了。
Kuduで確認すると期待通り「wwwroot」直下にファイルが展開されています。やったー。
Postmanでも正常に動作することを確認。
無事DevOpsでJavaのプロジェクトをビルド・デプロイすることができました。
さて、今回の試みですが、以下のURLをご覧になっていただくとわかる通り、Microsoftのドキュメントでも何故かJavaだけではサンプルが無く、あちこち調べまくり、トライ&エラーを繰り返し、なんとか解決できました。
このPipeline、実際に試してもらうとビルドのたびにいちいちMavenをダウンロードしており、3分近くかかっています(Pipeline全体では5分程度)。
あとは前回同様ビルドとリリースのPipelineが一緒に書かれている等、色々と改善の余地はあるものの、この記事については一旦これで終わりとしたいと思います。
いやー、毎週毎週深夜までの調べものはなかなかに大変ではありますが、エンジニアの端くれとしてその達成感は何物にも変えがたいものがありますね。
でも、来週はもう少し楽させてもらおうかな。。