はこだて未来大学さんと DevLOVEXさんでお話しました

先週、はこだて未来大学の大場みち子先生に呼んでいただきまして、学生さん向けにアジャイル開発のお話をしてきました。どうしてそういうものが生まれたのか、というところを私なりに整理してみたものです。「アジャイル開発の時代」という大仰なタイトルになっており、恐縮です。釣りバカ日誌の話をしたかっただけです。

speakerdeck.com

 

また、昨日は DevLOVE Xさんに呼んでいただきまして、モブプロのお話をしました。こちらは5月の連休で行ってきた Hunter Industries さんのモブプロの報告になってます。

speakerdeck.com

学んだことの詳しい内容は、以下の記事ですでに書いていますので、よかったらどうぞ。

kawaguti.hateblo.jp

kawaguti.hateblo.jp

並行セッションの裏番組の登壇者の皆さんが豪華すぎて、全部聞きに行きたい感じでした。そんな中で選んで聞いていただいた方に大感謝です。同窓会のようなカンファレンスに呼んでいただき、ありがとうございました。楽しかったです。今日は二日目、盛会をお祈りしております。

 

f:id:wayaguchi:20190623064646p:plain

 

Electron でカウントダウンタイマーをアプリにする

前回のカウントダウンタイマーに関して、早速ネットで繋がってないときにも使いたいという要望をいただいたので、Electronを試してみようと思いました。....の、作業ログです。

kawaguti.hateblo.jp

Chroniumブラウザを使って Webを単体アプリにパッケージするElectron

Electronというのは、ChromeやSaferiなどのブラウザのコアになっているChroniumを使って、Webフロントエンドの技術でスタンドアローンのアプリを作れるというものです。必要なソースを全部固めてexeとか実行ファイルにするツール群とデバッグ環境、という感じかなと思います。

人気のあるクロスプラットフォームエディタのAtomとかVSCodeがElectronで作られているそうなので、信頼性もありそう。

Electron Apps | Electron

2000年代前半くらいにあったWindowshtaという仕組みがあるのですが、それに近い感じもします。AngularやReactなどのWebフロントエンドのツールがそのまま使えるというのが面白いところかなと思います。普通にツール作るならなんとなく Swift とかのほうが簡単なんじゃないかと思うのですが、クロスプラットフォームで動くものを作れる(ChroniumとかJavaScriptのおかげ)ので、そういうときには便利なのではないかという感じがします。その分複雑度は上がってしまうので、ハマった時に抜け出すにはそれなりに熟練が必要そうな気がします。まあなんでもそうですね。

とりあえずマルチウインドウは避け、カウントダウンタイマーが動くところまで

今回はまず Electron で Angular のカウントダウンタイマーが動くところまでやりました。マルチウインドウでの同期は少しハードルが高いかもしれないので、まずはありがたいサンプルの組み合わせで、できそうなところまで。

前回のソースは agx-multi-window を組み込んでしまったので、その前まで戻します。別のフォルダを切って一からやることにしました。

ElectronでAngular初期画面まで

結構ここで時間を消費して調べることになってしまったのですが、AngularもElectronも変化が速くて、しかも別の組み合わせもあるので(React+Electronとか)、ハマった時に情報を調べるのがなかなかしんどかったです。Angular7 + Electronというキーワードで見つけたこちらがドンピシャでした。

qiita.com

ということで淡々と作っていきました。

npm i -g @angular/cli

ng new single-window

cd single-window

npm install --save-dev electron@latest

npm install --save-dev electron-packager@latest

npm install --save-dev ngx-build-plus@latest

https://github.com/kawaguti/tiimer/commit/fa170f51078e35a0ea80f51516cffc5e060ec902

プロジェクトを作ったら、上記のサイト通りに修正を入れました。

https://github.com/kawaguti/tiimer/commit/122398d0a3b38053134dd5c785fd682ec83ce5ba

実はこの時、ちょっとした失敗をしました。package.json の main 項の追記を忘れて、ビルドエラーはないのに起動しないという状況になりまして。手がかりが見つからずに数時間迷子になりました。簡単なサンプルが動かないと、疑うところが無限にあってしんどいですね。ミスなのでだいたい情報もなくて。

"main": "main.js",

f:id:wayaguchi:20190616194921p:plain

別の環境でもう一回作ったらけろりと出なくなって、気がついた次第。対照実験だいじです。

ちょっとハマった先で Angular の初期画面がElectron上で表示できたときには感動しました(自作自演)。同じプロジェクトで Web版も動きます。

f:id:wayaguchi:20190616195601p:plain

リアルタイム時刻表示は動くか?

次は時刻表示です。rxjs の Observable (pub/subイベント機構) が動くことの確認です。上記のサンプルがちゃんと動いたので、なんとなくシングルウィンドウものは動きそうな感じがしますが、一歩一歩確認します。

ありがたいこちらのブログを見ながら、コードに時刻表示を入れていきました。

【Angular】小ネタ:現在時刻の表示 - Qiita

コミットログはこちら

Add clock · kawaguti/tiimer@d69efb2 · GitHub

f:id:wayaguchi:20190616201328p:plain

Electronでも時刻が表示されました。同じプロジェクトで Angular CLI も動くので、ブラウザでも動きます。WindowsでもMacでも配布用のビルド(packager)できました。

f:id:wayaguchi:20190616223156j:plain

カウントダウンタイマーにする

時刻表示までいければ、あとはカウントダウンタイマーをちょっと作り込むだけです。

Add countdown timer · kawaguti/tiimer@e5c8253 · GitHub

remove example · kawaguti/tiimer@20167d7 · GitHub

Add timer set button · kawaguti/tiimer@40da1ff · GitHub

Angularのロゴを消すなどして、カウントダウンタイマーになりました。

f:id:wayaguchi:20190616202140p:plain

 

Beep音の実装

あ、beepもありました。こちらもありがたく拝借

JavaScript で Beep 音を鳴らす方法 - Qiita

音もちゃんと出るようです。

Add beep · kawaguti/tiimer@9f42b66 · GitHub

Beep音がオフィスで鳴り響いてドキッとしたので、BeepOffボタンも追加しました。押さなくても10回で勝手に止まるようにしてます。

Add BeepOff button · kawaguti/tiimer@86598e3 · GitHub

f:id:wayaguchi:20190616202626p:plain

以上で、 Electron でカウントダウンタイマーが動きました。

 



まだデバッグ版なので、本番用のpackage作成は次回。あとマルチウインドウもやりたいですね。

 

 

Angular 7 でカウントダウンタイマー

Angularチュートリアルを一通りこなしました。わーい。HTTPの章がボリューミーでしんどかったですが勉強になりました。

angular.jp

インメモリで仮設のAPIを作って試せるところがなかなかいいですね。

で、ちょっとなにか作ってみようと思って、デュアルモニターに対応するカウントダウンタイマーを作ってみました。よかったら使ってみてください。

http://tiimer.net/


f:id:wayaguchi:20190610164229p:plain

使い方はシンプルです。

サイトにアクセスして、”Open a new Window” ボタンで別ウインドウ(タブ)を作って、それをデュアルディスプレイ側に持っていきます。そのあとでどちらかのウインドウで分秒を入力して”Set”をクリックするとタイマーが両側のウインドウでスタートします。00:00になるとアラームが鳴ります。音を止めるには”BeepOff”をクリックしてください。

起動時やウインドウオープン時のソースの読み込み以外はサーバとの通信はありません。

 

ソースコードはこちらです。

tiimer/angular at master · kawaguti/tiimer · GitHub

作った過程を以下に記録しておきます。

 

要件: デュアルモニター対応のカウントダウンタイマー

研修でよくカウントダウンタイマーを使うのですが、よく使っていたアプリケーションがOSアップデートの波についていけず、次々と使えなくなるという問題がありました。いろんな人が軽い気持ちでつくてみることができるような性質のアプリですので、マーケットにも常に似たようなアプリが存在していて、次のものを見つければいいのですが、ちょっとめんどくさいんですよね。今回は一つ加えたい要件もあったので、自作してみることにしました。

その要件とは

デュアルモニターでプレゼンしているときに、発表者ディスプレイ側で操作して、受講者向けディスプレイに時間を出したい!

です。

 これを実現する方法は数限りなくあると思いますが、今回はAngularの練習ということもあるので、こういうソリューションを目指しました。

JavaScriptでタイマーを実現し、かつ、HTML の New Window で新しいウインドウを起動して、子ウィンドウ側にタイマーを表示する 

結果的にはもう少し踏み込んだ、複数ウインドウを同期するカウントダウンタイマーになりました。(実装の都合により)

 

要素技術1: AngularのObservableを使って時刻を更新する 

まずはカウントダウンタイマー部分の実装ですが、Angular Tutorial に出てきた、 pub/sub型のイベント処理を扱う Observable という仕組みをつかうと簡単にできそうです。

....と思って探したら素晴らしいサンプルがありました。

qiita.com

このサンプルのいいところは、Observable の使い方を端的に示すだけではなく、html側のパイプの使い方も教えてくれているところです。「もっとシンプルな方法や、間違いの指摘等あればコメントよろしくお願いします。」ということですが、全く思いつきませんでした。ありがとうございます。

要素技術1の派生: カウントダウンタイマー

現在時刻が更新できたら、次はカウントダウンタイマーです。毎秒このイベントが起こることを信じて、残り秒を remainedSec という変数に持ち、これを秒ごとに減算して、ゼロになったら完了、ということにしてみます。

targetSec が最初にセットした残り秒数、remainedSec が現在の残り秒数、elapsedSecが現在の経過秒数です。

https://github.com/kawaguti/tiimer/blob/master/angular/src/app/app.component.ts

export class AppComponent implements OnInit, OnDestroy {
now: Observable<Date>;
intervalList = [];
targetSec: number;
elapsedSec: number;
remainedSec: number;
remainedString: string;
public ngOnInit() {
this.now = new Observable((observer) => {
this.intervalList.push(setInterval(() => {
if ( this.targetSec > 0 && this.remainedSec > 0) {
this.elapsedSec++;
this.remainedSec = this.targetSec - this.elapsedSec;
this.remainedString =
Math.floor(this.remainedSec / 60).toString().padStart(2, '0') + ':' +
Math.floor(this.remainedSec % 60).toString().padStart(2, '0');
}
if ( this.remainedString === '00:00' && this.beepFlag ) { AppComponent.beep(); }

observer.next(new Date());
}, 1000));
});

残り秒数表示が秒だと分単位がわかりにくいので、表示用にremainedStringというのを作っています。これは mm:ss 形式で残り秒数を表示します。

表示が 00:00 になったらビープ音を鳴らす条件式が最後に入っています。ビープ音については次項で説明します。。

要素技術2: ビープ音を鳴らす

カウントが 0 になったら (表示が 00:00 なら) ビープ音を鳴らしたいです。いくつも思いつくのですが、できる限り簡単で、ネットワークに依存しない実装が欲しかったので、こちらを参考にさせていただきました。

qiita.com

音声データもそのまま頂いてしまいました。すみません。

https://github.com/kawaguti/tiimer/blob/master/angular/src/app/app.component.ts

public static beep() {
// tslint:disable-next-line:max-line-length
const base64 = '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';
const sound = new Audio('data:audio/wav;base64,' + base64);
sound.play();
}

beepFlag という変数を持っておいて、カウントダウン開始時にこれをTrueにします。カウントがゼロになったときに、このフラグがTrueだったら、このbeepメソッドを呼んでビープを鳴らします。ユーザーが止めない限り毎秒鳴らしておきます。

ユーザーが止める操作をしたら、beepFlagを False にして次のイベントからはbeepが呼ばれないようになります。

if ( this.remainedString === '00:00' && this.beepFlag ) { AppComponent.beep(); }

要素技術3: マルチウインドウでの同期

普通のHTMLでは、window.openというメソッドで新しいウインドウを開くことができます。このときの戻り値がウインドウオブジェクトというもので、これを経由して小ウインドウのオブジェクトにアクセスすることができます...のですが、ちょっとめんどくさいので、ライブラリを探しました。いいのがありました。

nolanus.github.io

メッセージのキューを実装していて、ウインドウ間でメッセージをやりとりできます。Angular の Observable でメッセージを受け取ることができるところが素敵でした。サンプルがよくてきていて、初心者の私でもわかりやすく、とてもありがたかったです。

f:id:wayaguchi:20190610154950p:plain

サンプルをちょっと変更して、受け取ったメッセージが 0 < n < 3600 の数字だったら、 それを目標の秒数としてセットしてカウントダウンを開始せよ、という司令ということにします。

あと、ビープ音が鳴っているときに消す命令として「beefOff」を設定しました。この文字列が来たらビープ音をOffにします。

https://github.com/kawaguti/tiimer/blob/master/angular/src/app/app.component.ts

this.multiWindowService.onMessage().subscribe((value: Message) => {
// tslint:disable-next-line:radix
const newsec = parseInt(value.data);
if ( newsec > 0 && newsec < 60 * 60 ) {
this.targetSec = newsec;
this.elapsedSec = 0;
this.remainedSec = newsec;
}
if ( value.data === 'beepOff' ) { this.beepFlag = false; }
this.logs.unshift('Received a message from ' + value.senderId + ': ' + value.data);
});

ウインドウIDごとに出し先を変えられるのですが、それは不要なので、基本ブロードキャストします。ブロードキャストといっても、ウインドウのリストにforEachで全部メッセージを投げるだけの実装にしました。

public broadcastMessage( message: string ) {
this.windows.forEach((window) => {
this.sendMessage(window.id, message);
});
}

困ったところ: ビープ音がウインドウごとに鳴ってしまう

要素技術を合わせるときに困ったところは、ビープ音がウインドウごとに鳴ってしまうことでした。

これは、カウントダウンの指示を出したウインドウでだけビープ音が鳴るようにする( beepFlag を Trueにする) ということで解決しました。

https://github.com/kawaguti/tiimer/blob/master/angular/src/app/app.component.html

<button type="submit" class="btn btn-primary"
(click)="broadcastTime(minInput.value, secInput.value);minInput.value='';secInput.value='';beepFlag=true;">Set
</button>

ただ、カウントダウン開始の指示を出したウインドウ以外で止めたいこともあるので、beepFlag の Off はブロードキャストするようにしています。

https://github.com/kawaguti/tiimer/blob/master/angular/src/app/app.component.html

<button class="btn btn-secondary" (click)="beepFlag = false; broadcastMessage('beepOff');">BeepOff</button>

https://github.com/kawaguti/tiimer/blob/master/angular/src/app/app.component.ts

this.multiWindowService.onMessage().subscribe((value: Message) => {
      (中略)
if ( value.data === 'beepOff' ) { this.beepFlag = false; }
      (中略)
});

 

ウインドウサイズに合わせて文字を大きくしたい

細かい話ですが、ウインドウサイズに合わせて文字を大きくしたかったので、HTML5で加わった vh/vw というCSS要素を使いました。

dev.classmethod.jp

とりあえず style 要素で貼っています。そのうちCSSファイルに動かしたい。

https://github.com/kawaguti/tiimer/blob/master/angular/src/app/app.component.html

<p style="Font-Size: 20vw; Margin: 15vh 2vw 0">
{{ remainedString }}
</p>

 

ビルドとデプロイ

手元で動くようになったので、Angular で本番用にビルドしてデプロイしました。

$ ng build --prod

Date: 2019-06-10T06:17:12.545Z
Hash: acbc4eea2ebde4796da1
Time: 29359ms
chunk {0} runtime.26209474bfa8dc87a77c.js (runtime) 1.41 kB [entry] [rendered]
chunk {1} es2015-polyfills.bda95d5896422d031328.js (es2015-polyfills) 56.6 kB [initial] [rendered]
chunk {2} main.780e3db59e24bd6cf759.js (main) 242 kB [initial] [rendered]
chunk {3} polyfills.8bbb231b43165d65d357.js (polyfills) 41 kB [initial] [rendered]
chunk {4} styles.3ff695c00d717f2d2a11.css (styles) 0 bytes [initial] [rendered]

Azureへの静的ファイルのデプロイは前回の記事の手順で行っています。

kawaguti.hateblo.jp

kawaguti.hateblo.jp

 

今後の課題

  • ネットに繋がってない状態でも使いたい!
  • スマホだとスリープに落ちたところでカウントがずれる!
  • スマホだとビープ音がならない!

といった課題が見つかったので、次は Electron か Swift で作ってみたいと思います。あ、二番目は内部ロジックの変更(命令を残り秒数ではなく、目標時間にする)で対応できそうですね。

蛇足ですが、こういう要望が見つかるところが、「動くソフトウェア」の大事なところだな、と思いました。

 

Azure DevOps アップロードの続き : Subscription ID を変える

前回のエントリの作業中に、サイトを Free Trial から Pay-as-you-go にサブスクリプションを変えたのでした。実質的には変わらないのかもしれないですが。

kawaguti.hateblo.jp

 

そうするとですよ、その前に作っていたデプロイパイプラインが動かなくなることに気がつきまして。

kawaguti.hateblo.jp

 

YAMLサブスクリプション名を変えないと!

じゃあということで、リポジトリにある azure-pipelines.yml のサブスクリプション部分を書き換えればいいのかな、と思って編集しました。平文で書いてあるサブスクリプションIDを編集して、pushするとパイプラインが自動で動きます。.......でコケました。

f:id:wayaguchi:20190601210003p:plain

考えてみれば、ここ書き換えるだけでどんな配置先にもいれられてしまうとなると、セキュリティなんてあったもんじゃないわけで、どこかで連携処理が必要、ということに気がつきました。

ああそっか、やはり。Project Settings にいました。

f:id:wayaguchi:20190601210407p:plain

ここを変えて再登録してあげないと!まず、[+ New Service Connection]で追加します。

f:id:wayaguchi:20190601210610p:plain

ここで Azure側のログインを求められて、認証が行われます。...ので、他人のリポジトリに突っ込んでしまうことはないわけですね。納得。

 

よしこれで大丈夫なはず!こんどは手動でパイプラインを実行してみる

ではもう一回ビルドパイプラインでコピーを実行します。

f:id:wayaguchi:20190601210959p:plain

なぜかまたコケてしまいました。サブスクリプションIDちゃんと直したのに。

そういえば、さっき YAML ファイルをいじった時に IDが平文記述されていることにちょっと気持ち悪さを感じたんですよね、そういうものなのかな?と思いつつ。そう言えばさっきサービス連携を追加する時に、Connection Name なるものを記入してました。

f:id:wayaguchi:20190601211454p:plain

 

YAMLの azureSubscription を書き換える

これをYAMLで指定すればいいのか!ということでYAMLを修正。平文にIDがでていたのは、何かの既定値だったんですねきっと。ついでにソースからマジックナンバーが除去できてよかったです。(ボーイスカウトルール)

trigger:
- master

pool:
vmImage: 'windows-2019'

steps:
- task: AzureFileCopy@3
inputs:
SourcePath:
azureSubscription: 'innovationsprint'
Destination: 'AzureBlob'
storage: 'innovationsprint'
ContainerName: '$web'

Pushするとまた自動的にビルドが走りました。

f:id:wayaguchi:20190601211915p:plain

実は最後のビルドは成功を確信していたので、ブログ書きながらメールで通知を受けたのですが、これははかどりますね。

f:id:wayaguchi:20190601212808j:plain

しかし、こうなると、単純な死活監視くらいは走らせてから終わらせたくなります。

どんどん色々やりたくなるから不思議ですね。

 

 

Azure Storage / CDN / DNS で Web公開

前回の投稿の結果、Azure Storage に Webサイトが投入できるようになったので、これをWebサイトとしてちゃんと公開するところまでやりました。確実に忘れそうなので備忘録です。

kawaguti.hateblo.jp

 

Azure Storage で Web公開の設定

まずは基本の Azure Storage での Web公開です。

 [対象のストレージアカウント] - [静的な Webサイト] で 有効にします。

f:id:wayaguchi:20190530201734p:plain

プライマリエンドポイントのURLにアクセスできればOKです。httpsも勝手に対応してくれます。(DNS独自ドメインからの山椒がなければこれで終わりでいいんですけど....)

f:id:wayaguchi:20190530202107p:plain

 

Azure Storageだけでよいのか?CDNは必要か?httpsは?

次は独自のドメインで上記のサイトが出てくれればいいだけなのですが...。ちょっと手間が必要になりました。以下のサイトを参考にすると「httpsをちゃんと必要とするならCDN必要」ということです。

qiita.com

今回はhttpsを使うことを目指して、Azure CDNをつかってみることにしました。

Azure CDN で キャッシュする

CDNはお金高いかな?と思ってサイトを確認したら5桁の日本円が並んでいて、「うわっ」と思ったのですが、小数点以下が5桁でした。つまりGBあたり数円。

azure.microsoft.com

 

ということでストレージアカウントのメニューから[Azure CDN]を選んで設定を入れます。ホスト名は2つ前の項で確認した静的Webサイトのプライマリエンドポイントを入力します。

f:id:wayaguchi:20190530202847p:plain

リソースグループからエンドポイントを表示して内容を確認します。

f:id:wayaguchi:20190530203724p:plain

CDNエンドポイントからもちゃんと見えました。

f:id:wayaguchi:20190530203703p:plain

 

独自ドメインで表示したい - とりあえず http

Webサイトとしてはちゃんと表示できそうなので、次はDNSを振り替えて、独自ドメインで表示できれば完璧です。もともとドメイン名を購入したDNSには登録しているので、そのレコードをいじるだけでよさそうなのですが、契約しているプロバイダのDNSは、設定をいじる時にテキストを直接いじる感じで今ひとつです。そこで、今回はプロバイダではなく、Azure DNS を使ってみました。

独自ドメインhttpsは証明書の手配が必要なので後回しにして、httpで確認をとっていくことにします。そのために、静的Webサイトの設定を緩めます。 [ストレージアカウント]-[構成] で "安全な転送が必須" を無効 にします。

f:id:wayaguchi:20190530205400p:plain

Azure DNS を起動する

[全てのリソース]-[追加] で DNSを作成します。

f:id:wayaguchi:20190530210047p:plain

f:id:wayaguchi:20190530210044p:plain

ネームサーバー情報を確認して...

f:id:wayaguchi:20190530210617p:plain

ドメインのプロバイダ側にネームサーバを記入します。

f:id:wayaguchi:20190530210718p:plain

この反映には数時間かかります。

Azure DNS側に転送設定を入れておきます。

f:id:wayaguchi:20190530211322p:plain

テキストファイルじゃないし、適宜入力値チェックもしてくれるので便利。

 

数時間して、DNSの切り替えが有効になったら、CDNエンドポイントにカスタムドメインを設定します。切り替わるまではエラーになります(DNSの設定が間違っていてもなりますので注意)。DNSの設定変更は反映のタイムラグが大きいので、時間あるときに気長に試すとよさそうです。

f:id:wayaguchi:20190530211949p:plain 

だいぶ経ってから、アクセスできるように。

f:id:wayaguchi:20190530212417p:plain

(この時点でトラブって色々試行錯誤してしまったので、手順が抜けてたらごめんなさい...。)

 

補足: PC側のDNSキャッシュを消す

DNSの設定を変えた時に作業用の Mac だけどうにも切り替わらないなーと思ったらOSやChromeでキャッシュされていたみたいです。

qiita.com

波及に時間がかかるDNS設定変更の時は、特にハマりやすいなと思いました。スマホとか他の端末でも並行して確認する方がいいかもしれません。

 

Windowsはこちらのようです(試してません)。

qiita.com

 

www. なしでもうまく表示したい

www,なしのURLでも同じものを表示するか、wwwに転送したいところですが、DNSで単純に解決しようと思うと、「CNAMEは www.抜きのアドレス(@)には使うことができない」というのがRFCに書いてあるようで、ググるといろんなところでやっちゃダメって書いてあります。え、みんなどうしてるの?

Azure DNSだと、「エイリアスレコード」というので実現できるようです。AWSでも同じようにエイリアスでやるようです。

f:id:wayaguchi:20190530213627p:plain

ドキュメントも見つけたので貼っときます。

docs.microsoft.com

Traffic Manager プロファイルの場合と同様に、エイリアス レコードを使用して DNS ゾーンの頂点から Azure CDN エンドポイントをポイントすることもできます。 これは、Azure Storage と Azure CDN を使って静的な Web サイトを作成する場合に便利です。 DNS 名の前に "www" を付けなくても、Web サイトにアクセスできるようになります。

たとえば、静的な Web サイトの名前が www.contoso.com の場合、DNS 名の前に www を付ける必要はなく、ユーザーは contoso.com を使ってサイトにアクセスできます。

前述のように、CNAME レコードはゾーンの頂点ではサポートされていません。 そのため、CNAME レコードを使用して contoso.com から CDN エンドポイントをポイントすることはできません。 代わりに、エイリアス レコードを使用して、ゾーンの頂点から直接 CDN エンドポイントをポイントすることができます。

CDN側のカスタムドメインに追加しておきます。これをしないとエラーになることがあるみたい。(うまくアクセスできる時もあったりしたのでよくわかってませんが、DNSCDNに振ったら、CDN側にも設定を入れるものだと覚えておきたい)

f:id:wayaguchi:20190531000423p:plain

httpsも対応したい

最後に独自ドメインhttps対応 ですが、これはAzure KeyVaultから証明書を買うことでかなり自動的にできるみたいです(便利や)。

f:id:wayaguchi:20190530214143p:plain

ドメインの検証に時間がかかるようです。

f:id:wayaguchi:20190530214236p:plain

しばらくほっといたら終わってました。

f:id:wayaguchi:20190531000913p:plain

ということでサイトを公開できました。

www.innovationsprint.com

 

あ、www.無しのURLも証明書付けないといけないのか....。こっちも証明書を追加しました。

f:id:wayaguchi:20190531001624p:plain

 

 ※ 6/10 追記 : wwwなしのURLはSSL追加でエラーが出ます。

   f:id:wayaguchi:20190610145431p:plain

 

以上、Azure で静的Webサイトをホストした時の備忘録でした。

もっとこうしたほうがいいよとかあれば、ぜひお教えいただければありがたいです。 

The Phoenix Project : 自社の情報システムの開発・運営を体験するワークショップ

ITプレナーズさんで主催しているDevOpsの体験型研修がありまして、見学させていただきました。

f:id:wayaguchi:20190528223635j:plain

この研修はオランダに本拠をおく GamingWorks 社が開発したワークショップをITプレナーズさんが日本語化(のお手伝い)をしたものです。GamingWorks 社はAgileプロマネ向けのゲーム形式研修を手がけているようなので、今後の拡充が期待されます。

https://www.gamingworks.nl/business-simulations/the-phoenix-project/

 

継続的デリバリーの先の世界

ここでいう DevOpsについては、Phoenix Project を参考にしていただくとよいかと思います。当初の DevOps の定義はインフラやデプロイの自動化を中心に考えられたものですが、サイロ化が進行した会社が本当に変化のスピードを上げるためには、組織間の連携や会社全体のプロセス見直しが必要、ということになり、こちらを取り扱う本が多くなっています。

The DevOps 逆転だ!

The DevOps 逆転だ!

 
The DevOps ハンドブック 理論・原則・実践のすべて

The DevOps ハンドブック 理論・原則・実践のすべて

  • 作者: ジーン・キム,ジェズ・ハンブル,パトリック・ボア,ジョン・ウィリス,榊原彰,長尾高弘
  • 出版社/メーカー: 日経BP
  • 発売日: 2017/06/22
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る
 

 

改善すると次の壁が見えてくる

アジャイル開発の現場では、チーム開発をはじめてしばらくやっていると、だんだんデプロイが問題なくなり、次は開発や品質がボトルネックになり、そこも改善していくと、次はプロダクトオーナーが問題になる(バックログがジリ貧)、というのが黄金パターンのように感じます。得意なところ、手の届くところから初めていくと、だんだんその先にボトルネックが移動していくんですね。

 

以下の例は私が一時期やっていた流れなのですが、このパターンは多いかなと思います。

  1. バージョン管理や開発環境を整備して、常時結合をする。
  2. インフラを整備し、デプロイを自動化し、開発環境と本番環境の差異を縮める。
  3. 開発の人員安定化とスキル習熟が進み、品質が安定化する。
  4. プロダクトバックログの洗練が進む。不要なものを積み込む習慣がなくなる。新たな要望に即応できるようになる。
  5. 新しいビジネス施策に人員追加なしで対応できるようになる。イノベーションに継続的に対応できるようになる。

f:id:wayaguchi:20190821130602p:plain

 

自分のチームがうまくいったあとにぶつかる壁

後半で特に大事になってくるのが組織内の連携です。連携というと「みんなでやる」感じに聞こえるかもしれませんが、実際のところは全員にお願いして「気持ちよく動いてもらうように計らう」動きが必要になります。相手を尊敬・理解して、こちらを信じてもらって、自律的に動いてもらう。この部分は、なかなか外部のコーチには難しくて、社内の人の信頼貯金を試されるところかなと思います。

もし自分自身や仲間が十分な信頼貯金を持っていれば、バリューストリームマップのワークショップをしてデプロイや運用のムダをみんなで省くように改善を進めていく、とか選択肢が見えてくるのですが、信頼貯金がないとなかなかうまく始めにくいです。

f:id:wayaguchi:20190528115813j:plain

 

業務ITシステムを題材に

人事システム、給与システムやPOSシステムなど、実際によくある業務ITシステムを題材に、全社のプロジェクトを進めるのがワークショップの本線です。

f:id:wayaguchi:20190528122655j:plain

 

さまざまな役割の人と一緒に体験する

ワークショップのいいところは、自分だけでなく、他の人も一緒に参加できるところです。一人だけ研修を受けて他の人に全部伝えるのはかなり無理ゲーですので、関係者と一緒に参加して、やりながら実際の打ち手を考えていくことができれば、高速にトライアンドエラーを繰り返すことができるようになります。組織の課題は組織ごとに違いますので、それぞれで試行するしかありません。

f:id:wayaguchi:20190528223734j:plain

 

共犯関係を作る

実践的なワークショップを一緒に体験し、議論したり苦労したりすると、共犯関係ができます。同じ場所で同じことを学んで、語り合った仲というのは、その人が会社にいる限り、宝になるでしょう。私たち vs あの人たち、ではなく、問題 vs 私たちの関係になりやすくなります。

f:id:wayaguchi:20190528223926j:plain

 

お金のことを考える

あと、これは日本の開発コミュニティの特徴なのかなと思いますが、お金の流れを把握していない時があります。マネジメントですら企業会計がわかってない人も結構いるので、なかなか難しいところなのですが、売上とコストは連動しているものなので、感覚を掴んで、施策を人任せ(またはブラックボックス)にせず、一緒に考えていける組織文化を育めれば、より健全に経営できるようになるのではないかと思います。

f:id:wayaguchi:20190528121050j:plain

 

役割に分かれて実際にITプロジェクトを進行していく

CFOの視点としてどうしていくか?必要な情報システムの予算配分や、当面のプロジェクト進行をどうしていくのか。みんなの意見がどうか。役割分担を変えるとどのように次のイテレーションでのアウトプットが変化するか、優先順位を変えるべきか、各役割に分かれて議論しながら進めていきます。

f:id:wayaguchi:20190528115617j:plain

 

タイムボックス: 計画とふりかえりの力

ワークショップでは定期的に計画とふりかえりを入れていきます。次にどのような優先順位で進めるのか、前のイテレーションの結果を受けてもっとよくできるところはどこか。

まや、最後に全体のふりかえりを通じて、今日何を学んだのか、持って帰れるものはなにかを話し合います。

f:id:wayaguchi:20190528123240j:plain

 

緊急の問題に対処し、計画に反映する

プロジェクトが無風で進むことはほとんどありません。問題が起きた時こそ、組織の力が試されるともいえます。緊急の作業を、計画していた作業とどうバランスするのか。どのように意思決定していくのか、練習していきます。しかも障害というのは重なるもので...。時間は刻々と過ぎていき、議論する時間も、対応する時間を削っていきます。なにを諦め、なにを守るのか....。

「ooとxxはやめることになりました」「えっ?なになに?じゃあ、aa は?」「それは残るみたいです」「わっ、残り5分」「はーい、まとめまーす。aaとbbは残します」「えっと、それでも溢れてます」「おっと、どうしようか」

f:id:wayaguchi:20190528121801j:plain

 

とにかく歩き回る、とにかく声をかける

一つの部屋でワークショップで、部署間のコミュニケーションをシミュレートします。

情報は足で集める...というのは昔から言われることですが、リアルタイムで起きている課題に複数の担当間で調整しながら対応するには、歩き回って情報を見回し、声をかけて情報を確認するのが一番早く、必然的に歩き回って話し合いながら進みます。

f:id:wayaguchi:20190528123827j:plain

 

全体ふりかえりで学びを確認する

最後に全体ふりかえりで今日の学びを確認をします。見学した回では、以下のような意見が出ていました。

  • 「ふりかえりはやっていたが、ファシリテートや質問で深掘りすることができていなかったことに気づいた」
  • 「他の担当に対して、上から目線でなく、相手の立場に立ってコミュニケーションすることの重要性を再認識した。」
  • 「監査対応や標準化の意味がわかった」
  • 「システム投資にビジネス側の視点を入れることが重要だと思った」
  • 「3回目のイテレーションで、進め方を見直して改善を考えることができた」
  • 「IT部門に勤務しているが、ユーザー部門に参加してほしい」
  • 「ビジネス部門、IT部門、管理部門でどのように同期を取るのかがわかった」
  • 「仕事を進めるのが楽しいって思えた。これが働き方改革なのでは」

いつも一緒に仕事していない他部門の人と話し合うきっかけとしてもいいでしょう。意外と議論やファシリテートが上手い同僚を発見することになるかもしれません。

f:id:wayaguchi:20190528223931j:plain

 

技術的負債の根源にある相互無理解をつぶす

技術的負債が多くなるのは、開発者以上に、マネジメントが無理解であることが原因だということが多いのではないかと思います。解消するためのコストを取りにくい、評価されにくい、理解されない。その結果、次の手が打ちにくくなってしまうのですが、その領域に詳しくない人にはぱっと見、わからないものです(基礎的理解がない人に、わかるように説明することも難しいものです)。その辺りをちゃんと見抜く眼力や、他の専門家たちとの人間関係を築いていくことが重要になってきます。

f:id:wayaguchi:20190528223937j:plain

 

さまざまな視点で組織を見直そう

一歩引いたところから、いくつかの視点を取り入れて、自分たちの現状を見つめ直すために、こうした研修やワークショップに参加する機会をぜひ利用していただければと思います。

f:id:wayaguchi:20190528125310j:plainf:id:wayaguchi:20190528122218j:plain

おまけ : 組織で新しいことを進めたい方へ

組織内でさまざまな人を巻き込んでいくなら、この本がバイブルだと思っています。モブプロの聖地 Hunter Industries で学んだこと - kawaguti’s diary の Chris Lucian さんもオススメしてました!

Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン

Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン

 

電子版が出てました!

 

なお、研修の問い合わせ先はこちらだそうです。

講師育成含めた研修受講にご興味がある方

株式会社ITプレナーズジャパン・アジアパシフィック

https://www.itpreneurs.co.jp/contact-us/

 

純粋な研修受講にご興味がある方

クリエーションライン株式会社

https://www.creationline.com/contact

 

Azure DevOps の パイプラインで静的Webサイトを GitHub からデプロイ

Azure DevOps を使って、GitHub に置いてある静的なWebサイトをクラウドにデプロイしてみたので、そのやり方をメモしておきます。

やりたいこと : Webサイトにファイルをごそっとおきたい

長らく放置していた静的Webサイトの引越しをやるにあたって、ついでに自動デプロイの練習をすることにしました。

結果として、masterにpushすると、自動的にサイトに配置されるようになりました。

f:id:wayaguchi:20190528160503p:plain

初心者がやるタスクとしては最高にシンプルなやつだと思ったのですが、あまりに簡単すぎるのか、ちょうどいい記事が見当たらなくて、ちょっと調べながら進めることになりました。

Azure DevOps とは

Azure DevOps はSam Guckenheimer さんたちが作っているクラウドサービスで、もともとはVisual Studio Team Services とか Team Foundation Server とか言っていたものです。もともと DVDに入れてインストール型で売っていたアジャイル支援ツールを、クラウドに移行しました。プロダクトバックログ管理、デプロイパイプライン、アラート管理などが統合されています(バラバラでも使えますので今回はパイプラインだけ)。

azure.microsoft.com

 

ビルドを作る

今回やりたいのはGitHubからコピーするだけの作業ですが、それを行う「ビルド」を作ります。

[Pipelines]-[Builds] -> [+ New]-[New build pipeline]

f:id:wayaguchi:20190528142243p:plain

https://dev.azure.com/

 

更新元のファイルはGithub.comにあるのでGitHubを選択。

f:id:wayaguchi:20190528142416p:plain

プロジェクトにGithubを連携してあると、自分のリポジトリを検索できます。

f:id:wayaguchi:20190528142638p:plain
プロジェクトのGitHub連携は左下の project settings から GitHub Connections で [ + New Connection]でアカウントに紐づけて、連携対象のリポジトリを指定します。

f:id:wayaguchi:20190528143040p:plain

GitHubリポジトリ直下に azure-pipelines.yml があると自動的に読み込むみたいです。

f:id:wayaguchi:20190528143422p:plain

設定ファイル azure-pipeline.yml の内容

今回の azure-pipeline.yml の内容はこれです。

masterブランチの更新をトリガーにして、WindowsVM上で AzureFileCopyコマンドを実行し、SourcePath (規定値では作業ディレクトリ直下 = 対象リポジトリをクローンしたルートディレクトリ) から、ディレクトリの内容をごっそり対象のBlobストレージにコピーします。

trigger:
- master

pool:
vmImage: 'windows-2019'

steps:
- task: AzureFileCopy@3
inputs:
SourcePath:
azureSubscription: 'Free Trial (3372c....)'
Destination: 'AzureBlob'
storage: 'innov......'
ContainerName: '$web'

 Azure DevOps上で設定を変更すると、このファイルに保存され、GithubリポジトリにPushされます。ここで master ブランチが更新されるため、すぐにこのビルドジョブがキックされて実行されます。

pool: vmImageは、Linuxや、低いバージョンのWindowsでは動かないようです。AzureFileCopyの制約かと思います。

The Microsoft-hosted agent pool provides 7 virtual machine images to choose from:

Microsoft-hosted agents for Azure Pipelines - Azure Pipelines | Microsoft Docs

task: AzureFileCopy が今回の処理のキモです。いつくかパラメータがあります。

azureSubscriptionと storage は、あらかじめ Azure上で作成したBlobストレージの情報を書き込みます。

sourceはちょっとハマったのですが、全部消すとビルドのローカルパスになると気づいて、消したらうまくいったのでそのままにしています。

ドキュメントによると $(Build.Repository.LocalPath)¥hogehoge とかやると Github 上のサブディレクトリが指定できそうです。

Source Required. The source of the files to copy. Pre-defined system variables such as $(Build.Repository.LocalPath) can be used. Names containing wildcards such as *.zip are not supported.

Azure File Copy task - Azure Pipelines | Microsoft Docs


さて実行

右上の [Run] を押すと実行されます。GitHub で masterになにか push しても実行されます。私の場合は、azure-pipeline.yml でエラーになったので何度か書き換えたらそのつど実行されて解析が楽でした。

まずAzureのクラウド内にある待機中のエージェント(VM)がアサインされます。

f:id:wayaguchi:20190528151311p:plain

リポジトリからチェックアウト(ダウンロード)して...

f:id:wayaguchi:20190528151636p:plain

AzureFileCopyコマンドが実行されます。

f:id:wayaguchi:20190528151657p:plain

黄色い文字で警告(Warning)が出ていますがこれは後で調べるとして...

下の白い文字「Uploading files from ...」 の行でコピーができたことがわかります。

エラーの場合はここが赤文字になってエラー内容が表示されます。

f:id:wayaguchi:20190528151852p:plain

ジョブ終了。前述の2個の警告は気になりますが、ジョブは全て成功です。

f:id:wayaguchi:20190528152013p:plain

Azure Storage の Blob にファイルがコピーされていることを確認!

f:id:wayaguchi:20190528152751p:plain

https://portal.azure.com/

 

今回の作業にどれくらいかかったか

ちなみにいろいろ調べながら、10回くらいで成功したみたいです。エラーの原因はVMLinux だったこと、パス指定が違っていたことでした(消したらすんなりいった)。更新予定のないサイトですが、いつでも直せるのは気持ちいいし、引っ越しも簡単(と思いたい)。

f:id:wayaguchi:20190528154111p:plain

(備忘録) 試しに作ったビルドを消すとき

ビルドを消すときは、ビルドの名前を全部入力します。(1) の入力どうするんだろ、ってドギマギしたのですが、kawagutiの部分を入力していないことに気づきました。

f:id:wayaguchi:20190528151952p:plain

 

余談:  Sam Guckenheimer さんの Agile2014の基調講演

余談ですが、Sam Guckenheimer さんの「マイクロソフトがいかにしてクラウド時代にシフトしたか」というAgile2014の基調講演は、私の中でもピカイチに衝撃を受けたスピーチでした。

devblogs.microsoft.com