Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

deis pull as alternative to git push workflow #1190

Merged
merged 37 commits into from Jul 21, 2014
Merged

deis pull as alternative to git push workflow #1190

merged 37 commits into from Jul 21, 2014

Conversation

gabrtv
Copy link
Member

@gabrtv gabrtv commented Jun 18, 2014

The new deis pull command is an alternative to the git push workflow that allows promotion of existing Docker images, bit-for-bit, into a Deis application. It pulls the image from your repository and merges it with existing config to create a new release.

Overview of Changes

  • Refactored logic for creating new releases
  • Client now uses the git remote for backwards-compat, falling back to the name of the current directory as the app name (this is necessary for git-less deis pull process)
  • Controller uses a registry-to-registry transfer hack to speed up deis pull by 33%, which can be seen and reviewed at https://github.com/deis/docker-registry/compare/repository-import

How to Test

Test backwards-compat for existing applications

Install a Fresh Cluster from Master

  1. Create a Deis cluster on the master branch
  2. Register a user with deis register
  3. Create a cluster with deis clusters:create
  4. Add keys with deis keys:add

Create an Application

  1. Create an application from a git repository (e.g. example-ruby-sinatra)
  2. Deploy the application using git push
  3. Commit a minor change (git commit --allow-empty) and git push a new v3
  4. Use curl or deis open to test that the app is up

Perform an in-place Upgrade

  1. Uninstall Deis with make uninstall (should retain database data and application units)
  2. Checkout deis-build
  3. Install upgraded Deis with make build run
  4. Use deis login to re-establish a session with the controller

Check Backwards Compatibility

  1. Change directory back into your application
  2. Use deis info and deis open to check the health of the application
  3. Make a minor change (git commit --allow-empty) and use git push deis master to deploy it
  4. Use deis open to confirm it works
  5. Use deis rollback v2 to roll back to an image created on the old code
  6. Use deis open to confirm it works
  7. Create a new application and use git push deis master to ensure the initial build works

Test New Functionality

  1. Create a new directory to hold an application definition: mkdir /tmp/example-go && cd /tmp/example-go
  2. Use deis create to create "example-go"
  3. Use deis pull gabrtv/example-go to deploy from the public Docker Index
  4. Use deis open to confirm the app is working
  5. Use deis config:set POWERED_BY=blah to confirm config layers are set correctly
  6. Use deis rollback v2 to confirm rollback works
  7. Use deis pull registry.bacongobbler.com/helloworld to deploy from an in-house Docker index
  8. Use deis open to confirm the app is working
  9. Use deis run env to verify that the changes to the run command works

def _build_dockerfile(image, config):
dockerfile = ["FROM "+image]
for k, v in config.items():
dockerfile.append("ENV {} {}".format(k.upper(), v))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for The Docker Way:tm:

@bacongobbler
Copy link
Member

From an architectural standpoint, there seems to be a lot of confusion on what each component is supposed to accomplish. Why do we even need a docker engine in the controller at all? Maybe we can do the last-mile layer stuff in the builder instead. This would mean a lot of refactoring... though it makes sense that the builder should do the last-mile layer stuff from an architectural point of view. From my point of view (in a perfect world):

  • Controller -> API server (receive requests and shell out jobs elsewhere, including celery tasks)
  • Builder -> docker engine and deployment layer (receives images and/or repositories and spits out an app image to the registry, nothing else)
  • Registry -> docker image repository (stores images and not storing last-mile layer config in the JSON, which this PR fixes up with docker.py)

Right now we have the builder doing controller stuff (builder is POST'ing a request to the controller to launch an app, relies on CURL'ing the controller to inject envvars at compile-time) and the controller doing builder stuff (last-mile layer configuration should be handled by the builder).

Maybe this isn't the right PR to discuss this, but rather we should really take a good look at these two components and try to separate the two from each other if we can. @gabrtv let me know if this should belong in a separate issue, but I believe there is valid discussion to talk about in this PR to prevent code sprawl (post-1.0?)

@bacongobbler
Copy link
Member

To add on from internal discussions, I don't like the "controller pulls, controller pushes to registry, fleet job pulls from registry" workflow for this (lotta pushing for just one image to launch), though I don't see any other way since we have to do last-mile configuration on the image, then push it to the registry so the cluster can retrieve the image.

Maybe there's a way we can inject those environment variables into the fleet job itself, and just make the fleet job pull from their source registry (whether that'd be DockerHub or an in-house registry)? The app image is already stored somewhere else and I'd assume that users would want to put their in-house registry near their Deis cluster anyways, so maybe we don't even have to use those components for this workflow.

@gabrtv
Copy link
Member Author

gabrtv commented Jun 20, 2014

After a ton of discussion with @bacongobbler and others, we've decided to try a different approach to adding last-mile layers -- one that hopefully results in a slight performance gain on deploys, versus the ~2x performance hit from the current approach.

Rather than bundling a Docker-in-Docker on the controller, we will try to forking the Docker registry and implementing registry-to-registry image transfers via the Registry API. This feature, combined with last-mile layers implemented on the registry (as we do today) should make for a much more powerful solution.

@gabrtv gabrtv added this to the 0.10.0 milestone Jun 23, 2014
@gabrtv
Copy link
Member Author

gabrtv commented Jun 23, 2014

Note this is being assigned to @bacongobbler to explore registry transfers as an alternative to Docker-in-Docker inside the controller. Thankfully, most of the PR will remain usable.

@gabrtv
Copy link
Member Author

gabrtv commented Jun 24, 2014

I don't think this PR is mergeable as is, given the performance implications of the extra layer transfer. If we have to cut 0.10 without 'deis build', so be it.

On Jun 23, 2014, at 23:13, Matthew Fisher notifications@github.com wrote:

@gabrtv we could potentially review this issue now and come back to optimizing this PR at a later date... Would you prefer to get this in ASAP and get some feedback from users or just start optimizing now and delay the PR review? Up to you. I expect my work to take up to Wednesday at the very latest.


Reply to this email directly or view it on GitHub.

@aledbf
Copy link
Contributor

aledbf commented Jul 3, 2014

@gabrtv it possible to add a third option uploading a tgz file?
(the build option is awesome but imply that I can't use buildpacks for instance)

@bacongobbler
Copy link
Member

@aledbf do you mean like a ROOTFS tarball of your image or a tarball of your application such as https://github.com/deis/example-go/archive/master.tar.gz?

@aledbf
Copy link
Contributor

aledbf commented Jul 3, 2014

@bacongobbler a tarball of an application (the result of npm pack for instance or https://github.com/deis/example-go/archive/master.tar.gz)

@bacongobbler
Copy link
Member

Jenkins, test this please.

@bacongobbler
Copy link
Member

PR's ready for review (barring documentation). I've manually tested pulling both private and public images using deis build. Some feedback on the client state behaviour that @gabrtv implemented would be much appreciated, as I still think it's not the best solution. I'll write the docs after we decide on what's the best approach for the app state in the OP:

Client now uses a ~/.deis/apps.yaml mapping to track applications, falling back to the git remote for backwards-compat (this is necessary for git-less deis build process)

><> pwd
/tmp/example-go
><> source ~/venv/bin/activate
(venv)><> rm ~/.deis/apps.yml
(venv)><> deis create
Creating application... done, created indoor-caffeine
(venv)><> deis build gabrtv/example-go
Creating build... done, v2
(venv)><> curl http://indoor-caffeine.local.deisapp.com
Powered by Deis
(venv)><> deis config:set POWERED_BY=deis-build
Creating config... done, v3

=== indoor-caffeine
POWERED_BY: deis-build
(venv)><> curl http://indoor-caffeine.local.deisapp.com
Powered by deis-build
(venv)><> deis build <REDACTED>/deis/helloworld
Creating build... done, v4
(venv)><> curl http://indoor-caffeine.local.deisapp.com
Welcome to Deis!
See the documentation at http://docs.deis.io/ for more information.

The hacks done to implement registry-to-registry transfers can be seen here: https://github.com/deis/docker-registry/compare/repository-import

Rollback still works:

(venv)><> deis rollback v2
Rolling back to v2... done, v5
(venv)><> curl http://indoor-caffeine.local.deisapp.com
Powered by Deis
(venv)><> cd ~/git/src/github.com/deis/example-ruby-sinatra

git push deis master does not. I have a feeling that this is coming from the changes in the registry module.

(venv)><> git push deis master
Warning: Permanently added '[deis.local.deisapp.com]:2222,[172.17.8.100]:2222' (RSA) to the list of known hosts.
Counting objects: 101, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (53/53), done.
Writing objects: 100% (101/101), 21.93 KiB | 0 bytes/s, done.
Total 101 (delta 43), reused 97 (delta 42)
-----> Ruby app detected
[...]
remote: Successfully built d6e7e5af38d0
remote: -----> Pushing image to private registry
remote:
remote:        Launching... Build hook error: 500 <h1>Server Error (500)</h1>

registry logs (looks like the tag was accidentally sent in the API call):

2014-07-11 19:55:03,278 DEBUG: [put_tag] namespace=library; repository=ablest-jerrycan; tag=git-1ee2418f
10.1.42.1 - - [11/Jul/2014:19:55:03] "PUT /v1/repositories/ablest-jerrycan/tags/git-1ee2418f HTTP/1.1" 200 4 "-" "docker/1.0.0 go/go1.2.1 git-commit/63fe64c kernel/3.14.6+ os/linux arch/amd64"
2014-07-11 19:55:03,279 INFO: 10.1.42.1 - - [11/Jul/2014:19:55:03] "PUT /v1/repositories/ablest-jerrycan/tags/git-1ee2418f HTTP/1.1" 200 4 "-" "docker/1.0.0 go/go1.2.1 git-commit/63fe64c kernel/3.14.6+ os/linux arch/amd64"
2014-07-11 19:55:03,282 DEBUG: args = {'images': True}
10.1.42.1 - - [11/Jul/2014:19:55:03] "PUT /v1/repositories/ablest-jerrycan/images HTTP/1.1" 204 - "-" "docker/1.0.0 go/go1.2.1 git-commit/63fe64c kernel/3.14.6+ os/linux arch/amd64"
2014-07-11 19:55:03,290 INFO: 10.1.42.1 - - [11/Jul/2014:19:55:03] "PUT /v1/repositories/ablest-jerrycan/images HTTP/1.1" 204 - "-" "docker/1.0.0 go/go1.2.1 git-commit/63fe64c kernel/3.14.6+ os/linux arch/amd64"
2014-07-11 19:55:03,681 DEBUG: args = {'tag': u'1ee2418f22b1afa4dd224e51eb7a98c91963fe8f'}
2014-07-11 19:55:03,681 DEBUG: [get_tag] namespace=172.17.8.100:5000; repository=ablest-jerrycan%3Agit-1ee2418f; tag=1ee2418f22b1afa4dd224e51eb7a98c91963fe8f
2014-07-11 19:55:03,681 DEBUG: api_error: Tag not found
10.1.42.1 - - [11/Jul/2014:19:55:03] "GET /v1/repositories/172.17.8.100:5000/ablest-jerrycan:git-1ee2418f/tags/1ee2418f22b1afa4dd224e51eb7a98c91963fe8f HTTP/1.1" 404 26 "-" "docker/0.9.0"
2014-07-11 19:55:03,682 INFO: 10.1.42.1 - - [11/Jul/2014:19:55:03] "GET /v1/repositories/172.17.8.100:5000/ablest-jerrycan:git-1ee2418f/tags/1ee2418f22b1afa4dd224e51eb7a98c91963fe8f HTTP/1.1" 404 26 "-" "docker/0.9.0"

@bacongobbler
Copy link
Member

fixed:

><> deis create
Creating application... done, created lively-anaconda
Warning: Permanently added '[deis.local.deisapp.com]:2222,[172.17.8.100]:2222' (RSA) to the list of known hosts.
Git remote deis added
><> git push deis master
Warning: Permanently added '[deis.local.deisapp.com]:2222,[172.17.8.100]:2222' (RSA) to the list of known hosts.
Counting objects: 101, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (53/53), done.
Writing objects: 100% (101/101), 21.93 KiB | 0 bytes/s, done.
Total 101 (delta 43), reused 97 (delta 42)
-----> Ruby app detected
[...]
http://lively-anaconda.local.deisapp.com
><> curl http://lively-anaconda.local.deisapp.com
Powered by Deis!

@bacongobbler
Copy link
Member

PR ready for review once more. Let's chat about it on monday when @gabrtv is back and alive :)

@gabrtv
Copy link
Member Author

gabrtv commented Jul 14, 2014

I'm back and partially alive. Let's start readying this for a merge! @bacongobbler let's add docs and get to testing.

@bacongobbler
Copy link
Member

I've added documentation which is syntactically correct, but some of the links are broken at the moment. I'll resurrect #1314 and break it down such that we can get some of these nice fixes in so the links work properly.

Matthew Fisher added 18 commits July 21, 2014 14:27
The logic has been moved over to the registry, so it's not needed any more.
When target_image was POST'd to the controller through /api/hooks/build,
the tag was not originally sent. Reverting the behaviour so `git push deis master` works
again.
'sha' is just metadata. it is not used as a tag on the registry.

The release ledger is append-only, which means that the latest image should be
used by default, with an optional source tag for rollbacks.
the registry module will take care of that
some packages in dev_requirements.txt are required
for the test suite (such as mock).
Changing the name to something that seems more accurate, as some people
associate "build" with `docker build`. It also complements `docker pull`
quite nicely, since we are effectively "pulling" images into Deis.
Previously, `deis create` would store session data in a ~/.deis/apps.yml
file for applications with no git root to facilitate `deis pull`. This
file looked similar to this:

    {'/tmp/deis-test': 'united-mainsail'}

The issue with this means if you move your application's root directory
somewhere else, the reference to the instance is lost. With this change,
if no git root is present in the application directory, the base name
of the current working directory (in the example above, that would be
'deis-test') is synonymous with the app name. With this in mind,
`deis create` in a git repo retains the old behaviour (via the
auto-generator from the server), but in a non-git directory, it takes
the name of the current working directory. As always, --app works just
like it used to, or you can still manually specify a name with
`deis create <id>`.
If you're in a git repo and you did not supply an app name to
`deis create`, the client will fail because app_name was never
initialized.
This way, we can bust the cache when we rebase the branch.
this reference exists in #1327 which as of this writing is not merged
yet.
@bacongobbler
Copy link
Member

Jenkins, test this please.

@gabrtv
Copy link
Member Author

gabrtv commented Jul 21, 2014

Jenkins is having problems with this due to some of the infrastructural changes. However, we've done a great deal of manual QA and are ready to merge. Hold on to your hats.

gabrtv pushed a commit that referenced this pull request Jul 21, 2014
`deis pull` as alternative to `git push` workflow
@gabrtv gabrtv merged commit 9beb70e into master Jul 21, 2014
@bacongobbler bacongobbler deleted the deis-build branch July 21, 2014 21:57
bacongobbler pushed a commit that referenced this pull request Jul 22, 2014
A regression from #1190 was introduced where the config file used
was the sample that came from dotcloud/docker-registry instead of
using the confd templated config we use today. Updating the file
to upstream's changes with the common layer as well as changing
the reference to the file in the Dockerfile fixes this regression.
bacongobbler pushed a commit that referenced this pull request Jul 22, 2014
A regression from #1190 was introduced where the config file used
was the sample that came from dotcloud/docker-registry instead of
using the confd templated config we use today. Updating the file
to upstream's changes with the common layer as well as changing
the reference to the file in the Dockerfile fixes this regression.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants