Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Docker Compose-based development workflow #4

Merged
merged 9 commits into from
Nov 4, 2017

Conversation

forabi
Copy link
Contributor

@forabi forabi commented Nov 2, 2017

This PR introduces support for launching the API and database servers using Docker Compose.

This gives us all the benefits of Docker without compromising on development ergonomics:

  • We can lock all the system-wide dependencies (e.g. Node.js) to avoid version mismatch issues.

  • We also avoid conflicts with the host operating system. If we use Node.js 8 for our API, contributors do not need to use a Node.js version manager to change their system-wide Node.js installation to a version we use.

  • It's also more convenient to use Docker Compose to launch the API services instead of having to set up a MySQL server for development, which could also conflict with other development projects on the contributor's machine. One command is all that is needed to start the API and database servers:

    docker-compose up --build -d
    

    This build and starts the servers in the background. We can simply go to localhost:8080/graphiql and query the API.
    The running API server container will use the local project directory directly, i.e. it does not copy the files to the build image. This means that:

    • the API server still restarts on code changes just as it does when not using Docker Compose,
    • and building the images is lightning fast

    I've also set up the API image to use a Docker volume to cache the node_modules folder separately from the local node_modules directory so that native Node.js dependencies do not conflict with the host's equivalents. This volume is persisted even when the containers are stopped so it subsequent runs take a fraction of the time.

    This PR also changes the yarn dev command to use the standard Node.js executable instead of ts-node. This allows us to use the --inspect flag to debug TypeScript code using Visual Studio Code, including setting breakpoints and watch expressions. Unfortunately, this only works when the API server is not running in a container, even though the debugging port is exposed to the host operating system. I haven't figured out why yet.

  • We could theoretically configure Docker Compose environment to be as close to the production environment as possible. For example, we can proxy the DNS requests of the development environment to route api.hollowverse.com to the containerized API development server. This PR does not do that, but using Docker Compose opens up the door for a lot of cool things!

  • If, for any reason, we do not want to use Docker Compose, we can keep the current development workflow. This PR does not affect that in any way.

Tasks

  • Update the Wiki entry to use Docker Compose
  • Figure out why debugging does not work when the server is running inside the container

services:
database:
image: mysql
restart: on-failure
Copy link
Contributor

Choose a reason for hiding this comment

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

What's on-failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This tells Docker Compose to restart MySQL server when the server command exits with a non-zero exit code.

- MYSQL_ROOT_HOST=%

api:
restart: always
Copy link
Contributor

Choose a reason for hiding this comment

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

What's always?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

always means that the container will restart whenever the entry point command (yarn dev) exits, regardless of the exit code. yarn dev is not supposed to exit at all so I'm not sure about using always. on-failure could also work.

source: .
target: /code

- type: volume
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the difference between type bind and type volume?

Copy link
Contributor Author

@forabi forabi Nov 3, 2017

Choose a reason for hiding this comment

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

bind tells Docker to use an existing directory on the host OS as a volume inside the container. volume, on the other hand, creates an empty volume, managed by Docker, so that the data can be persisted even if the container is stopped. We need bind to mount the project code and allow automatic reloading on code changes. volume is used to cache node_modules separately from the host node_modules so that dependencies do not conflict (suppose that the host is Windows, if the container overwrites node_modules, some native dependencies will not work when running the server outside the container)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The volume created with type volume can be inspected like this:

❯ docker volume inspect api_node_modules
[
    {
        "CreatedAt": "2017-10-30T07:23:24+02:00",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "api",
            "com.docker.compose.volume": "node_modules"
        },
        "Mountpoint": "/var/lib/docker/volumes/api_node_modules/_data",
        "Name": "api_node_modules",
        "Options": {},
        "Scope": "local"
    }
]

package.json Outdated
@@ -13,7 +13,7 @@
"lint-ts": "tslint './*.ts' 'src/**/*.ts{,x}' -e 'src/typings/schema.ts' --project tsconfig.json",
"generate-schema-types": "graphql-to-typescript schema.graphql src/typings/schema.ts",
"generate-schema-types/dev": "nodemon --watch src/schema.graphql --exec 'run-p generate-schema-types'",
"server/dev": "PORT=8080 nodemon --watch ./src --ext ts,graphql,tsx,json --exec 'ts-node src/server.ts --project ./src'",
"server/dev": "PORT=8080 nodemon --watch ./src --ext ts,graphql,tsx,json --exec 'node --inspect -r 'ts-node/register' src/server.ts'",
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

@wholesomedev
Copy link
Contributor

Cool stuff!

So this containerizes our API dev workflow. Does it make sense to try to containerize the client-side dev workflow as well?

@wholesomedev
Copy link
Contributor

With regard to the debugging issue that you're running into, I've encountered Docker networking issues in the past, and the thing I've had to do to resolve them is change the hostnames inside Docker from localhost to 0.0.0.0 because apparently 0.0.0.0 is more dynamic. Not sure if this is related to the issue you're encountering now, but I just thought to throw that out there.

@forabi forabi mentioned this pull request Nov 3, 2017
@forabi
Copy link
Contributor Author

forabi commented Nov 3, 2017

So this containerizes our API dev workflow. Does it make sense to try to containerize the client-side dev workflow as well?

Yeah, I'm thinking we should containerize everything! It's awesome!

With regard to the debugging issue that you're running into, I've encountered Docker networking issues in the past, and the thing I've had to do to resolve them is change the hostnames inside Docker from localhost to 0.0.0.0 because apparently 0.0.0.0 is more dynamic. Not sure if this is related to the issue you're encountering now, but I just thought to throw that out there.

Interesting, I will give that a try.

@forabi
Copy link
Contributor Author

forabi commented Nov 4, 2017

I finally figured out why debugging didn't work. It turns out that the port inside the container is only exposed on localhost (it is not exposed to the network Docker Compose creates). This comment (nodejs/node#11591 (comment)) pointed me in the right direction.

@forabi forabi merged commit 9d9acbb into hollowverse-archive:master Nov 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants