個人的にも仕事でもOSSの開発コミュニティで活動させてもらっている中で最近触れる機会があったAzure Pipelineについて書いていく。
本記事でカバーする範囲
AzurePipeline(とGithub)を使ったCI/CD環境作成の一例を紹介しながら以下を紹介する
- Github上のリポジトリへのPullRequestをトリガにAzurePipelineでジョブを走らせるための基本的な流れ
- Github上のリポジトリでリリースした際にDockerコンテナイメージをDockerHubへPushするための基本的な流れ
- 【おまけ】 amd64だけじゃなくRaspberry Pi 3B用のコンテナイメージ(arm64v8用イメージ)も同時にビルド&リリースする
環境
- Github上に公開されたリポジトリをターゲット
- リポジトリには既にテスト自動化の仕組みが作り込まれている前提
- 既にGithubのアカウントを持っている
- 既にAzureのアカウントを持っている(無料枠でAzurePipelineは利用可能)
- Githubのアカウントと紐付けて利用することも可能
簡単な経緯
冒頭でも触れたとおりOSSコミュニティで活動しているのだが、そこはしっかりした組織(Linux Foundation参加のHyperledger)で管理された比較的しっかりしたプロジェクトの1つ(Hyperledger Explorer)で、OSSに不慣れなしっかりしてない私でもしっかり受け入れてくれる体制が整っていた。たとえばJiraやConfluenceでワークアイテムや情報の共有がされていたり、チャットで気軽にコミュニティメンバーとコミュニケーションが取れたり、CI/CDの環境が提供されていたり、といった具合である。ただソース管理には一部の人にしか馴染みがなさそうなGerritを使っていたり、CICDはJenkins職人の助けを借りないとなかなか手を加えることができないブラックボックス気味の環境だった(あと不安定だったりした)。このへんのとっつきにくさが新しいコントリビューターにとってバリアになっていると組織の人たちも感じていて、コミュニティ内で議論が続けられていた。GitlabやCircleCIなど色々吟味した結果、AzurePipeline/Githubへの移行が決まったわけである。Jenkins/GerritからAzurePipeline/Githubへの移行はコミュニティのメンバーと協力しながら進めたわけだが、その模様をあたかも私の手柄かのようにシェアしようというわけである。とても感動したので、もし「AzurePipelineってなにそれ?」という方がいれば、是非知ってもらいたい、というのがホントのところである。
Azure Pipelineとは
Azure Pipelineとは、Azure DevOpsが提供する機能の1つである。Azure DevOpsには開発フロー全体をサポートするIssue TrackerやKanban Board、ソースリポジトリなどの機能も含まれる。Azure Pipelineは、Azure上でLinux, macOS、Windows向けに様々な言語で開発されたソフトウェアのビルドやテスト、デプロイまで面倒見てくれる。今回はLinuxをターゲットにした場合の流れを紹介するが、他のOSをターゲットに加えるのは非常に簡単である。詳しくはコチラを参照してもらいたい。私が参加しているOSSプロジェクのユーザの大半はlinuxもしくはmacOSのため、linux環境しか持ってない身としては非常に有難い。
パイプラインの設定はWeb上のエディタで可能である(VSCodeライクなキーバインドで操作可能)。予め用意された各種タスクのテンプレートを利用してYAMLファイルの操作ではあるが比較的簡単に設定は行える。
気になる利用料金であるが、ターゲットがOSSであれば、10VMまで1か月あたり時間無制限で並列利用可能とのこと(1job実行あたり60分までの時間制限はアリ)。太っ腹。
簡単な仕組みは、ターゲットとなるGithubのリポジトリにAgentがGithub Appとしてインストールされる。パイプラインの設定はリポジトリ上にYAMLファイルとしてコミットされる。これに基づいてAgentからAzure上のインスタンスが起動されて処理が走るような感じ(最後は想像)。Agentのインストール先にはオプションがいくつか用意されており、自分で管理するインスタンス(クラウドでも実サーバでも構いませんが)のようなSelf-hostedなマシンを利用することも可能です。
CI環境の作成(PullRequestからテスト実行まで)
ターゲットとなるGithub上のリポジトリを決める
先に触れたOSSコミュニティでの活動先であるHyperledger/expolorerを自身のアカウントにクローンして使用することにする。何もGithub上からターゲットを選ばなければならない訳ではない。
Azure DevOpsへログイン
今回は自身のGithubアカウントを利用してAzure DevOps(Azure Pipelineを含むサービス)を利用する。
https://azure.microsoft.com/en-au/services/devops/
サービスにログイン後、新たなプロジェクトを作成する。
プロジェクト作成の際に公開/非公開の設定やバージョン管理ツール(PipelineのVCSではなくDevOpsのVCS)の指定等が可能です。
サイドバーからPipelines
を選択して、パイプライン作成ウィザードを開始する。
ターゲットとなるリポジトリの場所を指定する。ここでGithubを選択すると、Githubのアクセス認証にリダイレクトされる。
AzureにGithubへのアクセス許可を与えるとAzure Pipeline上で自身のアカウント上に持つリポジトリの一覧が見えるようになる。そこからターゲットとなるリポジトリを選択する。
リポジトリを選択すると前述したエージェントアプリのインストール許可ダイアログが現れるので、これをターゲットのリポジトリにのみ許可する。
ここからはパイプラインの設定です。各種テンプレートからCIに必要なタスクを選ぶ。
今回のターゲットはReactで開発されたBlockchain用の可視化ツールなのでNode.js環境を選択する。
YAMLファイルをアシスタントツールを使いながらWeb上で編集する。YAMLファイルのインストール先、ファイル名はデフォルトで設定されていますが、以下のスクリーンショット内#1のところで変更可能。各ステップは直接YAMLを修正するか、#2のSetting
リンクをクリックしてアシスタントツールの助けを借りて修正することも可能。
タスクを追加する場合はアシスタントツールから選択することで可能である。
最終的にazure-pipelines.yml
ファイルは以下のようになる。
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
name: $(SourceBranchName)-$(Date:yyyyMMdd)$(Rev:.rrr)
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '8.11'
displayName: 'Install Node.js'
- task: UsePythonVersion@0
inputs:
versionSpec: '2.7'
addToPath: true
architecture: 'x64'
- script: pip install virtualenv
displayName: Install Virtualenv
- script: |
npm install
npm run e2e-test-sanitycheck:ci
displayName: Run Sanity Checks
- script: |
npm install
npm run e2e-gui-test:ci
displayName: Run GUI Tests
本YAMLファイルのキーとなるポイントは以下の通り。
-
master
ブランチに対する変更に対して本パイプラインをトリガする。どういった条件、タイミングでパイプラインをトリガするか、はtrigger
の設定で柔軟にカスタマイズ可能である。詳しくはコチラを参照 - ビルドおよびテストを実行するプラットフォームを
linux/ubuntu
に指定
もしアシスタントツール内にしっくりくるテンプレートがない場合、マーケットプレイスから取得、追加することも可能。
利用可能なタスクは以下から参照可能。またマーケットプレイスから追加も可能である。
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks
編集が完了したらSave and run
を実行すると、azure-pipeline.yml
ファイルがリポジトリ上にコミットされる。
Azure PipelineのDashboardを確認すると新規パイプラインが作成されて、無事に成功していることが分かる。40分近くかかってるが。
各ステップの詳細ログも参照可能。
このパイプラインの結果はGithub上で自動的に参照されており、トリガをもたらしたコミットをGithub上から参照すると下記の通りグリーンのチェックマークが見える。労せずしてソース管理とCI環境間の相互参照ができている。
Pipelineの構成
# Pipeline
stages:
- stage: A
jobs:
- jab: 1
steps:
...
- jab: 2
steps:
...
- stage: B
jobs:
- jab: 1
steps:
...
- jab: 2
steps:
...
今回の例ではstepsをシーケンシャルに記載した構成だが、処理内容に基づいて、stageやjobといったより大きな括りでステップをまとめることもできる。今回の例では1つのVMインスタンスがシーケンシャルにstepを処理していくが、これを処理内容毎(例えば、ビルドやテスト、デプロイ等)にstageやjobで分割すると複数のVMで並列処理される(stage/job間に指定する依存関係にも依るが)。今回は1つのPipelineあたりの上限である60分に迫るくらいの処理内容だったが、並列処理可能な複数の処理(Run Sanity Checks
とRun GUI Tests
)を別々のjobで処理すれば、所要時間を減らせる。ただし、複数のjob間で同一のローカルビルドしたコンテナイメージを使用するようなケースでは、jobごとに都度コンテナイメージをビルドしなおすか、docker registryにpushする等の対応が必要である(前述したAgentをSelf-hostedマシンにインストールした場合には本制約は生じない)。
試しにPull Requestを作成してみる
以下の通りREADME.md
ファイルをGithub上で少し修正してPullRequestを出してみる。
それをトリガにしてCIのパイプラインがトリガされたことがPullRequest上からも分かる。リポジトリのポリシー設定によって、このCIパイプラインのPassをマージの必要条件にすれば(あとReviewerによる承認も)、継続的インテグレーション環境の出来上がりである。
CD環境の作成(Tag付けからDocker HubへのPUSHまで)
ここで事前準備としてDocker Hubにアクセスするための秘匿情報をLibraryに保存する。保存したこの秘匿情報をパイプラインから参照する。Webからの作成手順は以下のとおりである。
前回と同じ手順でPipelineのReview画面まで進む。Configuration画面では以下のStarter pipeline
を選択すること、hello-worldなパイプラインが用意される。
PipelineのReview画面で初期設定を下記のYAMLで全て上書きする。前回と異なる主なポイントは以下。
-
v*
のパタンにマッチするタグが付けられたら本パイプラインがトリガされるように設定- 前回のパイプラインでは、
trigger: master
としていたので、masterブランチに対するあらゆる変更に対して当該のパイプラインがトリガされる。くわしくはコチラを参照
- 前回のパイプラインでは、
- PullRequestではトリガされないように設定
- Docker Hubにアクセスするための認証情報にアクセスするためのタスクを追加
- YAMLファイル名を
azure-pipeline-release.yaml
にリネーム - 登録時には
Save and run
のドロップダウンからSave
を選択- コミット時にも以下の通りPipelineのスキップ指示である
[skip ci]
をコミットメッセージ内に挿入。くわしくはコチラを参照
- コミット時にも以下の通りPipelineのスキップ指示である
# Copyright the Hyperledger Fabric contributors. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0
name: RELEASE-$(Date:yyyyMMdd)$(Rev:.rrr)
trigger:
tags:
include:
- v*
pr: none
variables:
DOCKERHUBID: 'nekia'
jobs:
- job: Release
pool:
vmImage: ubuntu-16.04
steps:
- task: DownloadSecureFile@1
name: mySecureFile
inputs:
secureFile: 'passwd.txt'
- script: 'cat $(mySecureFile.secureFilePath) | docker login --username $(DOCKERHUBID) --password-stdin'
displayName: Login to Docker
- script: ./build_docker_image.sh
displayName: Build Docker Images
- script: docker tag hyperledger/explorer $(DOCKERHUBID)/explorer:$(git describe --abbrev=0 --tags | cut -d 'v' -f 2)
displayName: Tag Explorer Image
- script: docker tag hyperledger/explorer-db $(DOCKERHUBID)/explorer-db:$(git describe --abbrev=0 --tags | cut -d 'v' -f 2)
displayName: Tag Explorer DB Image
- script: docker push $(DOCKERHUBID)/explorer
displayName: Push Explorer Latest Image
- script: docker push $(DOCKERHUBID)/explorer-db
displayName: Push Explorer DB Latest Image
- script: docker push $(DOCKERHUBID)/explorer:$(git describe --abbrev=0 --tags | cut -d 'v' -f 2)
displayName: Push Explorer Versioned Image
- script: docker push $(DOCKERHUBID)/explorer-db:$(git describe --abbrev=0 --tags | cut -d 'v' -f 2)
displayName: Push Explorer DB Versioned Image
Azure Pipelineの一覧画面に新しいパイプラインが作成されているのを確認。
動作確認のため、Github上から以下の通りリリース(タグ付け)してみる。git CLIでtag付けすることで動作確認することも可能。リリース用のYAMLファイルで指定した通り、タグ名をv*
のパタンで指定することだけ注意が必要。ここではv0.5.0-demo
を使用。
その結果、以下の通りRelease用のパイプラインがトリガされ、10分弱で無事成功していることが確認できる。
Docker Hub上にも期待通りlinux/amd64
向けのコンテナが指定したTagでPUSHされていることが確認できる。
おまけ
正直、ここまでの手順は一度CI/CD等の環境をご自身で作成された経験をお持ちの方なら特段目新しいものではない気がする。そんな方でも本投稿から何かしら持ち帰って頂けるよう、最後にオマケとして、あまり大きく取り上げられてない機能?をご紹介する。使う機会は非常に限られるとは思うが。
今回例として取り上げた本リポジトリとは別に、個人的にOSSなBlockchainプラットフォームであるHyperledger FabricのノードコンテナイメージをRaspberry Pi上で動かしたりすることがある。そのためにlinux/arm64v8
用の自作コンテナイメージを作ったりするのだが、Raspberry Pi上でビルド環境構築してビルドしてPUSHして等々、色々面倒くさい。この手間をついでAzure Pipelineにお願いしてしまおうと思う。
ポイントはココに書かれている通りなのだが、要はQEMUを使って実現する。
まずはRaspberry Pi用のDockerfileとリリースパイプラインを定義したYAMLファイルを用意する。
Dockerfileの差分は以下の1行のみ。ベースイメージ(nodeやpostgresコンテナイメージ)をarm64v8ベースに切り替えるだけ。arm64v8アーキテクチャのコンテナがPipelineのJobインスタンス上で動くようになる。自身のDockerfileでベースとしているイメージ(nodeやpostgres,ubuntuなど)をココにあるようなarm64v8ベースに切り替えてやる、というわけだ。
-
追記 今回例として使用したプロジェクトのコンテナイメージはalpineベースのnodeコンテナイメージをベースに作成しているが、これだとarmベースにアーキテクチャを切り替えた場合、
docker build
中のnpmコマンドがたびたび固まる事象が発生。そこでベースにするnodeコンテナイメージを少しサイズ大きめになるが無印のもの(Debianベース)に変えることで問題を回避。
$ diff -u5 Dockerfile Dockerfile-rpi3
--- Dockerfile 2019-11-23 23:21:08.775743476 +1100
+++ Dockerfile-rpi3 2019-12-15 13:42:18.649681471 +1100
@@ -1,11 +1,11 @@
# Copyright Tecnalia Research & Innovation (https://www.tecnalia.com)
# Copyright Tecnalia Blockchain LAB
#
# SPDX-License-Identifier: Apache-2.0
-FROM node:8.15.0-alpine
+FROM arm64v8/node:8.15.0
# default values pf environment variables
# that are used inside container
ENV DEFAULT_WORKDIR /opt
$ diff -u5 postgres-Dockerfile postgres-Dockerfile-rpi3
--- postgres-Dockerfile 2019-11-23 23:21:08.851781477 +1100
+++ postgres-Dockerfile-rpi3 2019-12-15 13:43:40.394533490 +1100
@@ -1,11 +1,11 @@
# Copyright Tecnalia Research & Innovation (https://www.tecnalia.com)
# Copyright Tecnalia Blockchain LAB
#
# SPDX-License-Identifier: Apache-2.0
-FROM postgres:10.4-alpine
+FROM arm64v8/postgres:10.4-alpine
# default database name for HLF Explorer db connection
ENV DATABASE_DATABASE fabricexplorer
# default username for HLF Explorer db connection
パイプラインでは、docker build
前(ここではbuild_docker_image*.sh
実行前)にmultiarch/qemu-user-static
を使ってホスト環境とは異なるアーキテクチャ用コンテナイメージの実行可能状態にする。
$ diff -u5 azure-pipelines-release.yml azure-pipelines-release-rpi3.yml
--- azure-pipelines-release.yml 2019-12-15 13:37:36.320587461 +1100
+++ azure-pipelines-release-rpi3.yml 2019-12-15 14:41:30.946233554 +1100
@@ -21,12 +21,12 @@
name: mySecureFile
inputs:
secureFile: 'passwd.txt'
- script: 'cat $(mySecureFile.secureFilePath) | docker login --username $(DOCKERHUBID) --password-stdin'
displayName: Login to Docker
- - script: ./build_docker_image.sh
- displayName: Build Docker Images
+ - script: ./build_docker_image_rpi3.sh
+ displayName: Build Docker Images for arm64v8
- script: docker tag hyperledger/explorer $(DOCKERHUBID)/explorer:$(git describe --abbrev=0 --tags | cut -d 'v' -f 2)
displayName: Tag Explorer Image
- script: docker tag hyperledger/explorer-db $(DOCKERHUBID)/explorer-db:$(git describe --abbrev=0 --tags | cut -d 'v' -f 2)
displayName: Tag Explorer DB Image
- script: docker push $(DOCKERHUBID)/explorer
$ diff -u5 build_docker_image.sh build_docker_image_rpi3.sh
--- build_docker_image.sh 2019-12-10 12:46:30.913925688 +1100
+++ build_docker_image_rpi3.sh 2019-12-15 14:40:58.354233554 +1100
@@ -18,16 +18,18 @@
echo ""
}
function deploy_build_database(){
echo "Building Hyperledger Fabric Database image..."
- docker build -f postgres-Dockerfile --tag $FABRIC_EXPLORER_DB_TAG .
+ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
+ docker build -f postgres-Dockerfile-rpi3 --tag $FABRIC_EXPLORER_DB_TAG .
}
function deploy_build_explorer(){
echo "Building Hyperledger Fabric explorer image..."
- docker build --tag $FABRIC_EXPLORER_TAG .
+ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
+ docker build -f Dockerfile-rpi3 --tag $FABRIC_EXPLORER_TAG .
}
banner
deploy_build_explorer
で改めてGithub上でリリースしてやると無事にRaspberry Pi用リリースパイプラインもトリガされた。が、QEMUを噛ましているせいか、あまりにも処理が遅くて1Jobあたり60分の制約に引っ掛かりパイプラインがタイムアウトするという結果に終わった。今後何かしら手立てが打てたら続報お伝えしたいと思う。見切り発車でゴメンナサイ。 Docker Hub上にも、armアーキテクチャなイメージがPUSHされている。
最後に
非常にながくなりましたが、MicrosoftのOSS Loveな姿勢は非常に徹底していて、近頃のオープンな活動ぶりには個人的に好感が持てます(上からですみません)。Azure DevOpsの開発上についてもMicrosoft内部の3 week sprintの様子を公開していて可視性が高いです。
今回シェアしたこのような情報は、OSSコミュニティに身を置いたことで得られた恩恵の1つといえます。もちろんGive and takeではありますが。仕事ではなかなか触れることのできない様々な良い機会/経験をOSS活動は与えてくれます。是非皆さんも気軽にOSS活動に足を突っ込んでみることをおすすめしたいです。