A dummy application
This is a simple app for working out the flows for automated Docker image
creation and deployment from within CI.
THIS IS CURRENTLY DISABLED: I shut down my Docker Hub bot-integration rather
than pay a minimum of $300/yr for demoing a few open source projects, just to
get team management. The .circleci/config.yml
has been deleted.
I vaguely intend to recreate this using an alternative image registry, which
is why this git repository has not been deleted. — 2020-11-24
No warranty. You get to keep all the pieces and shards if it breaks.
There’s a 2-clause BSD license as a formality.
There should be little enough here in terms of “traditional code”, but the
infrastructure of how pieces fit together may be useful to you, after reading
and analysis. If you use any of it, then a word of attribution might be nice
(and will absolve you of the need to honor the formal copyright notice
propagation for build framework).
This Git repo is setup so that pushes automatically trigger builds within
Circle CI, which creates a from-scratch Docker image (using a multi-stage
Dockerfile) and deploys it to both Docker Hub and, for main branch,
to Heroku.
Well, it did, but I’ve shut down the Heroku app, so that bit of logic is
commented out in the .circleci/config.yml
file; I’ve left it intact as a
reference.
.circleci/config.yml
; this is not invoked by docker build
, but is thedocker build
will be run.circleci/
directory.Dockerfile
creates two images; the firstDockerfile
, which copies files made in the earlier stages. This is theWe create a Heroku app, enable Go language metrics manually (because using
Docker deploy, not a buildpack), disable git push to the remote (but leave
the remote in place so that the Heroku CLI can auto-determine the deployed
app name), and do a build and deploy with the Heroku tag set.
The build-tag affects both the Docker image tag-name and the content which
is built; for Heroku, it ensures that we compile their metrics push code.
heroku apps:create pt-dummy-app
heroku labs:enable go-language-metrics
heroku labs:enable runtime-heroku-metrics
heroku labs:enable runtime-empty-entrypoint
git config --local --unset remote.heroku.fetch
git config --local remote.heroku.pushurl no_push_because_we_deploy_docker_images
git config --local --bool remote.heroku.skipFetchAll true
# At the time this was done:
# make BUILD_TAGS=heroku heroku-deploy
# If done today:
env BUILD_TAGS=heroku ./build/build.with-docker.sh
# or equivalently:
./build/build.with-docker.sh env-BUILD_TAGS=heroku
Created repo on Docker Hub through web UI: pennocktech/dummyapp
For a second project, philpennock/poetry
(as an example of depending upon
external data) I set up an automated Docker Hub build. That project creates
a data-only Docker image, which we now depend upon at build time. There’s
one COPY --from
line in our Dockerfile
to edit to remove that.
Created Circle CI project; pushed on branch circle, aborted first build onmaster
(as the main
branch was then called).
NB: the runtime-empty-entrypoint
lab came into existence after I first
created this project, but is what lets us skip setting the ENTRYPOINT
in theDockerfile
and just have array-form RUN
work correctly.
Created a Circle CI org-level Context, heroku-and-dockerhub
, added
credentials there for HEROKU_TOKEN
, DOCKERHUB_USER
, andDOCKERHUB_PASS
.
As to the values:
heroku auth:token
while signed in, that’s the token to use.heroku authorizations:create -d 'Circle CI token, created by Fred'
~/.docker/config.json
; you probably want to installdocker-credential-helper
if you haven’t already done so.offline_token=true
Organizations
in theTeams
and create a newdummyapppushers
and add the new account to it.Collaborators
, and add thedummyapppushers
team with Write access.DOCKERHUB_USER
and DOCKERHUB_PASS
.Now update the .circleci/config.yml
to reference the context; yes, any build
within the org can request any context, you can’t have admins defining
restricted contexts with some credentials. If you want that, then you’ll need
multiple Circle CI orgs (each with their own billing?).
Note: Google have replaced Container Registry with Artifact Registry
Here we can take advantage of a decent permissions model and get a token which
can update just the one image repository.
Ideally it couldn’t delete or remove objects, such that a compromise of the CI
environment does not propagate out to data loss elsewhere; alas, this does not
appear to be possible, as a Docker limitation.
Google:
dummyapp-214121
), tied to billing account.IAM & admin
, in Service accounts
, created a service account:circleci-dummyapp-image-builder
Storage
/ Storage Admin
(this is temporary)Furnish a new private key
(JSON)Circle CI:
google-dummyapp
google-dummyapp
GCLOUD_AUTH_ENCODED
base64 < ~/Downloads/dummyapp-214121-31db07d29579.json | pbcopy
After doing a run and having the Bucket be created, we can strip away
permissions in Google:
circleci-dummyapp-image-builder
us.artifacts.dummyapp-214121.appspot.com
edit permissions for theStorage Admin
andThen re-run the Circle CI push job and confirm can still push.
I’ve tried using a PT: GCR Image Pusher
role which dropsstorage.buckets.delete
and storage.objects.delete
(and originally alsostorage.objects.update
) but it doesn’t work, Docker just fails. This seems
likely to be an infelicity in the operations which Docker tries against the
image repository backend, such that it’s not designed to work with
non-destructive access to blob stores.
pennocktech/ci
image for building in Circle; it’s got Go and aphilpennock/poetry
which is just a couple of Rudyard Kipling poems.golang
image, for the Builder Image.All are automated Docker Hub builds as public images from public GitHub repos.
The golang
image is from the docker-library
GitHub organization, while the
others are from GitHub repos which have names matching the Docker Hub repo
names.
Create:
DOCKER_http_proxy=http://192.0.2.1:3128/ DOCKER_RUNTIME_BASE_IMAGE=alpine \
./build/build.with-docker.sh`
Run:
docker run -it --rm ${imageid} /bin/sh
Before v0.1.0 we defaulted to Heroku bug-compatibility, so had to useENTRYPOINT
to get around an attempt to invoke /bin/sh
for our command,
even when given in array form. From v0.1.0 onwards, we require that Heroku
be told heroku labs:enable runtime-empty-entrypoint
which isn’t quite right,
but does at least let us use an array to invoke a command where there is no/bin/sh
inside the container.
This needs too many knobs, we should look at what’s needed to simplify it,
but:
DOCKER_BUILDER_IMAGE=pennocktech/ci:purple \
EXTRACT_GO_VERSION_FROM_LABEL=com.pennock-tech.versions.go \
./build/build.with-docker.sh