Azure / azure-dev

A developer CLI that reduces the time it takes for you to get started on Azure. The Azure Developer CLI (azd) provides a set of developer-friendly commands that map to key stages in your workflow - code, build, deploy, monitor, repeat.
https://aka.ms/azd
MIT License
373 stars 168 forks source link

Leverage ACR build for docker container image builds #509

Open Gordonby opened 1 year ago

Gordonby commented 1 year ago

When using Azure Container Apps with azd, sometimes developers don't have the local tooling to build docker containers.

Reasons/Situations to avoid local docker builds

The Azure Container Registry build capability can be used to queue a local context and push it to the registry in one step. https://docs.microsoft.com/en-us/cli/azure/acr?view=azure-cli-latest#az-acr-build

ellismg commented 1 year ago

I wonder if we should do this by default. This would let us stop depending on Docker for cases where you were not concerned about running the application locally. As a windows user, I'd actually prefer this to Docker Desktop, I think,

Are there downsides to making this default, @Gordonby? I could imagine the remote build environment has some additional restrictions vs local, perhaps shipping the entire build context by default ends up being more traffic than just pushing up the built layers, and maybe that "no-op" builds take longer just because the roundtrip time to the remote build service.

BTW - Long term we would like to remove the dependency we have on the az CLI (in favor of talking to the control planes directly). Would be good for us to better understand long term how complex the implementation for the commands we are going to use is in az so we have an idea of what moving off depending on it would mean.

Gordonby commented 1 year ago

I think the only downside is for mature docker users who prefer the local experience, who have potentially large images already downloaded locally and in their ACR.

IMO Acr build would be a good default if Acr is already the default registry. I have Docker installed locally but the daemon does not start with machine startup, only on demand... so I always get caught out 😅

ellismg commented 1 year ago

When we design support for toggling between an ACR build and a local docker build, it might be good for us to think about the way that toggle is manifested in the system and if a similar strategy could be used for folks on App Service who want to switch between local builds and remote builds with oryx or something. At some level these problems look "the same".

pamelafox commented 2 months ago

This is still an open issue, right? as in, the only way to build an image with azd is if you have Docker running locally, correct? @andredewes is trying to decide how to add support for image building to https://github.com/Azure-Samples/openai-aca-lb and doesn't want to use the azd mechanism if it requires Docker (which is reasonable), so the current plan is to use a postprovision hook with az acr build.

ellismg commented 2 weeks ago

I looked into a how az acr build works behind the scenes and at a high level here's what happens:

  1. The docker context is packed into a .tar.gz file and the Dockerfile is added to it.
  2. A request is made to ACR to get an upload URL for the docker context. In practice this is the URL for a azure storage blob (with an included SAS token). The .tar.gz from (1) is uploaded with this.
  3. A request is made to ACR to queue a run to do the docker build. Part of this request includes the path to the context from (2) as well as the typical docker configuration (any build args, the type of image to build, the path to the Dockerfile (this is a relative path into the .tar.gz from (1). This returns an LRO (but it seems like the LRO is not tied to the completion of the build, possibly due to a bug in the generated client? The response from the server does not have any of the usual Azure LRO headers, so the internal machinery of runtime.NewPoller thinks the operation completed synchronously)
  4. Using the Run ID from (3) you can get the URL for the logs of the build (again, this is a SAS URL for a blob storage blob). The az CLI fetches this blob over and over and streams the output as it is being written to the stdout.

https://gist.github.com/ellismg/08e4475cf144629e391ff0705aee589a is a small translation of the happy path of this logic into go. To produce the context, I checked out the todo-nodejs-mongo-aca sample and did a tar zcvf in the src/api folder.

Running the linked gist produced output like the following:

matell@Matts-Work-MacBook acr-remote-build % go run .                                                                                               
Upload URL: https://wus2managed227.blob.core.windows.net/e9237aaae12f4ba19bd01539de94fa63-9nt1eaohka/source/202405160000/811021f6-51d3-4976-b3e8-cb2139728a60.tar.gz?sv=2023-01-03&se=2024-05-17T00%3A49%3A21Z&sr=b&sp=cw&sig=weIKsnO9C1qGs621EsMO1Ec2zQ7G0k0mntUzlDdGjLU%3D
Relative Path: source/202405160000/811021f6-51d3-4976-b3e8-cb2139728a60.tar.gz
Run ID: ccb
Run Status: Queued
Run Status: Running
Run Status: Running
Run Status: Running
Run Status: Running
Run Status: Running
Run Status: Running
Run Status: Running
Run Status: Running
Run Status: Running
Run Status: Succeeded
Log URL: https://wus2managed227.blob.core.windows.net/e9237aaae12f4ba19bd01539de94fa63-9nt1eaohka/logs/ccb/rawtext.log?sv=2023-01-03&se=2024-05-17T00%3A05%3A09Z&sr=b&sp=r&sig=MLKkc%2FC56EutOFaiyzVjIdoaUaymG920BkDl6z7IHCI%3D
Log Metadata: Complete=successful
Log Content:
2024/05/16 23:49:23 Downloading source code...
2024/05/16 23:49:23 Finished downloading source code
2024/05/16 23:49:23 Using acb_vol_5905ad39-018e-4224-addb-f908cf3a160a as the home volume
2024/05/16 23:49:23 Setting up Docker configuration...
2024/05/16 23:49:24 Successfully set up Docker configuration
2024/05/16 23:49:24 Logging in to registry: acrmatell.azurecr.io
2024/05/16 23:49:24 Successfully logged into acrmatell.azurecr.io
2024/05/16 23:49:24 Executing step ID: build. Timeout(sec): 28800, Working directory: '', Network: ''
2024/05/16 23:49:24 Scanning for dependencies...
2024/05/16 23:49:25 Successfully scanned dependencies
2024/05/16 23:49:25 Launching container with name: build
Sending build context to Docker daemon  532.5kB
Step 1/6 : FROM node:20-alpine AS build
20-alpine: Pulling from library/node
4abcf2066143: Already exists
7231dd947546: Pulling fs layer
58ec58ffa356: Pulling fs layer
853d72f1314b: Pulling fs layer
853d72f1314b: Verifying Checksum
853d72f1314b: Download complete
58ec58ffa356: Verifying Checksum
58ec58ffa356: Download complete
7231dd947546: Verifying Checksum
7231dd947546: Download complete
7231dd947546: Pull complete
58ec58ffa356: Pull complete
853d72f1314b: Pull complete
Digest: sha256:291e84d956f1aff38454bbd3da38941461ad569a185c20aa289f71f37ea08e23
Status: Downloaded newer image for node:20-alpine
 ---> 4b21504f2506
Step 2/6 : WORKDIR /app
 ---> Running in 86a18ed9fff0
Removing intermediate container 86a18ed9fff0
 ---> ef71c24566a0
Step 3/6 : COPY . .
 ---> c2de4a26912d
Step 4/6 : RUN npm ci
 ---> Running in 0bff91293129

added 963 packages, and audited 964 packages in 10s

81 packages are looking for funding
  run `npm fund` for details

1 moderate severity vulnerability

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
npm notice 
npm notice New minor version of npm available! 10.5.2 -> 10.8.0
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v10.8.0>
npm notice Run `npm install -g npm@10.8.0` to update!
npm notice 
Removing intermediate container 0bff91293129
 ---> 70077a1d6b95
Step 5/6 : RUN npm run build
 ---> Running in 1d7b4ebf76e0

> todo-node-ts-express-mongo@0.1.0 prebuild
> npm run lint

> todo-node-ts-express-mongo@0.1.0 lint
> eslint src/**/*.ts

> todo-node-ts-express-mongo@0.1.0 build
> tsc -b .

Removing intermediate container 1d7b4ebf76e0
 ---> 0417844f7840
Step 6/6 : CMD ["npm","run","start"]
 ---> Running in f041f4debf6f
Removing intermediate container f041f4debf6f
 ---> a0a1833f7c31
Successfully built a0a1833f7c31
Successfully tagged acrmatell.azurecr.io/matell/test:azd-1715903362
2024/05/16 23:49:55 Successfully executed container: build
2024/05/16 23:49:55 Executing step ID: push. Timeout(sec): 3600, Working directory: '', Network: ''
2024/05/16 23:49:55 Pushing image: acrmatell.azurecr.io/matell/test:azd-1715903362, attempt 1
The push refers to repository [acrmatell.azurecr.io/matell/test]
000d445e0503: Preparing
102404cf1f8d: Preparing
fa58936bd236: Preparing
7706050dcfa3: Preparing
a258a309f52e: Preparing
0563f6148117: Preparing
206e1855e6d1: Preparing
d4fc045c9e3a: Preparing
0563f6148117: Waiting
206e1855e6d1: Waiting
d4fc045c9e3a: Waiting
a258a309f52e: Layer already exists
0563f6148117: Layer already exists
fa58936bd236: Pushed
000d445e0503: Pushed
d4fc045c9e3a: Layer already exists
7706050dcfa3: Pushed
206e1855e6d1: Layer already exists
102404cf1f8d: Pushed
azd-1715903362: digest: sha256:fbcb835b60e785a547bd690ed04cdf9bbfedda3704b1026d2b781478acee1a03 size: 1995
2024/05/16 23:50:05 Successfully pushed image: acrmatell.azurecr.io/matell/test:azd-1715903362
2024/05/16 23:50:05 Step ID: build marked as successful (elapsed time in seconds: 30.933225)
2024/05/16 23:50:05 Populating digests for step ID: build...
2024/05/16 23:50:06 Successfully populated digests for step ID: build
2024/05/16 23:50:06 Step ID: push marked as successful (elapsed time in seconds: 9.869777)
2024/05/16 23:50:06 The following dependencies were found:
2024/05/16 23:50:06 
- image:
    registry: acrmatell.azurecr.io
    repository: matell/test
    tag: azd-1715903362
    digest: sha256:fbcb835b60e785a547bd690ed04cdf9bbfedda3704b1026d2b781478acee1a03
  runtime-dependency:
    registry: registry.hub.docker.com
    repository: library/node
    tag: 20-alpine
    digest: sha256:291e84d956f1aff38454bbd3da38941461ad569a185c20aa289f71f37ea08e23
  git: {}

Run ID: ccb was successful after 44s

Going to start working on turning this from an exploratory hack into something that we could push into azd.