tech.guitarrapc.cóm

Technical updates

AzureDevOpsPipeline/Azure DevOps Pipeline で docker build と Azure Container Repository への push を行う

Azure Pipeline を使っているとタスクによせたくなるのですが、あんまりそういうのもアレなのでほどほどにというのはもうちょっと言われてもいい気がします。 Docker はその最たる例で コマンドで3行で済むようなものがDockerタスクを使うといたずらに時間を取られる傾向にあります。

今回は Azure DevOps Pipeline でDocker ビルドを行って、Azure Container Repository に push する流れを見てみましょう。 Azure DevOps Pipeline を使っていないなら、これでやるメリットはアカウント統合程度で特にない感じです。(アカウント統合も統合できていない感がつよい)

目次

[:contents:]

TL;DR

ビルドマシンは hosted が楽でよい。 docker build は、dockerタスクではなく docker コマンドでやるほうがいい docker push は、docker タスクを使って認証を透過的に行おう。

何をDockerで動かすの

ASP.NET Core 2.2 を対象にdockerで動かしてみましょう。 例えばAzure Container Instance で動かすのもいいのです。 しかし、container じゃなくてもAzure Web Apps for Linux にデプロイすれば たいがいはいいのでWebアプリのdocker展開はポータビリティをどこまで担保したいか、どう動かしたいかの相談なのでほどほどに。

Azure DevOps Pipeline の Docker タスク

Docker Module は、0 と 1と2があります。

0 を利用するのがおすすめです。

f:id:guitarrapc_tech:20190416104133p:plain
Docker タスク0.*

1 は妙な挙動をして不安定なためお勧めしません。

f:id:guitarrapc_tech:20190416104152p:plain
Dockerタスク 1.*

2 は、コンセプトが変わっててACRで使うならあんまりいらなさそう。というか、余計な手間が増えてて微妙。

f:id:guitarrapc_tech:20190416104215p:plain
Dockerタスク2.*

ローカルでの docker build

なにはともあれlocal で通しておきましょう。 これがCIで通るようにします。

適当にこんな構成とします。 ASPNETCOREにcsprojの名前を、YOUR_PROJECT_DIR にプロジェクトのフォルダ名を指定しておきます。

.
└ src
  └ YOUR_PROJECT_DIR 
      ├ Dockerfile
      └ ASPNETCORE.csproj

Dockerfileはこのような感じになるかと思います。

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
WORKDIR /src/YOUR_PROJECT_DIR
COPY ["YOUR_PROJECT_DIR/ASPNETCORE.csproj", "ASPNETCORE.csproj"]
RUN dotnet restore "ASPNETCORE.csproj"
WORKDIR /src
COPY . .
WORKDIR /src/YOUR_PROJECT_DIR
ARG conf="Debug"
RUN dotnet build "ASPNETCORE.csproj" -c $conf -o /app
RUN dotnet publish "ASPNETCORE.csproj" -c $conf -o /app

FROM base AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "ASPNETCORE.dll"]

あとは実行時に ASPNETCORE_ENVIRONMENT を Development | Staging | Production で指定すれば挙動が切り替わるのでokですね。

docker build

特にWindows系のhosted|private agent がそうですが、build をする場合、Dockerタスクではなく docker コマンドをたたくほうが安定します。 dockerタスクで行おうとすると、docker build中にイメージが見つかったり見つからなかったり、COPYが実行できたりできなかったり構成を変えていないのに実行ごとに挙動が変わりました。(これでDockerfile や設定がおかしいかと思って試行錯誤で数時間消費した)

variables:
  BuildConfiguration: Debug
  DockerImageName: ASPNETCORE
  DockerFile: YOUR_PROJECT_DIR/Dockerfile

steps:
- bash: 'docker build -t youraspnetcoreregistry.azurecr.io/$(DockerImageName):$(Build.SourceVersion)_$(Build.BuildId) -t $(Registry).azurecr.io/$(DockerImageName):latest -f YOUR_PROJECT_DIR/Dockerfile .'
  displayName: 'docker build'

これでビルドが実行できます。 ACRに挙げるにあたって、ビルドごとにバージョンが一意に特定できる必要があるので、git hash + build id で毎ビルドで一意にしています。

docker push

push は簡単です。 local でいうところの、docker login しておいて docker push です。 ただし、CIでやるに伴ってレジストリのログインを認証情報を出さずに透過的に行いたいでしょう。

docker@0 タスク を使うと ACRへの認証情報を事前に解決しておけるので便利です。

先ほど docker build で指定した tag を指定してpush してあげましょう。 この時、ACRを対象にしている場合、ACRのアドレス "youraspnetcoreregistry.azurecr.io" 部分は自動的に解決されるため、tagで指定する必要がありません。docker build でyouraspnetcoreregistry.azurecr.io/$(DockerImageName):$(Build.SourceVersion)_$(Build.BuildId) と指定した場合、$(DockerImageName):$(Build.SourceVersion)_$(Build.BuildId) でokです。(.... なんと余計なことを)

- task: Docker@0
  displayName: 'Push an image'
  inputs:
    azureSubscription: YOUR_SUBSCRIPTION
    azureContainerRegistry: '{"loginServer":"youraspnetcoreregistry.azurecr.io", "id" : "/subscriptions/abcdefg-1234-abcd-56789ghijklmn/resourceGroups/AWESOME-RESOURCE/providers/Microsoft.ContainerRegistry/registries/youraspnetcoreregistry"}'
    action: 'Push an image'
    imageName: '$(DockerImageName):$(Build.SourceVersion)_$(Build.BuildId)'
    includeLatestTag: true

これで無事にdocker build -> push ができるでしょう。