| Deploying Go servers with Docker | 
 | 26 Sep 2014 | 
 |  | 
 | Andrew Gerrand | 
 |  | 
 | * Introduction | 
 |  | 
 | This week Docker [[https://blog.docker.com/2014/09/docker-hub-official-repos-announcing-language-stacks/][announced]] | 
 | official base images for Go and other major languages, | 
 | giving programmers a trusted and easy way to build containers for their Go programs. | 
 |  | 
 | In this article we'll walk through a recipe for creating a Docker container for | 
 | a simple Go web application and deploying that container to Google Compute Engine. | 
 | If you're not familiar with Docker, you should read | 
 | [[https://docs.docker.com/engine/understanding-docker/][Understanding Docker]] | 
 | before reading on. | 
 |  | 
 | * The demo app | 
 |  | 
 | For our demonstration we will use the | 
 | [[https://godoc.org/github.com/golang/example/outyet][outyet]] program from the | 
 | [[https://github.com/golang/example][Go examples repository]], | 
 | a simple web server that reports whether the next version of Go has been released | 
 | (designed to power sites like [[http://isgo1point4.outyet.org/][isgo1point4.outyet.org]]). | 
 | It has no dependencies outside the standard library and requires no additional | 
 | data files at run time; for a web server, it's about as simple as it gets. | 
 |  | 
 | Use "go get" to fetch and install outyet in your | 
 | [[https://golang.org/doc/code.html#Workspaces][workspace]]: | 
 |  | 
 | 	$ go get github.com/golang/example/outyet | 
 |  | 
 | * Write a Dockerfile | 
 |  | 
 | Replace a file named `Dockerfile` in the `outyet` directory with the following contents: | 
 |  | 
 | 	# Start from a Debian image with the latest version of Go installed | 
 | 	# and a workspace (GOPATH) configured at /go. | 
 | 	FROM golang | 
 |  | 
 | 	# Copy the local package files to the container's workspace. | 
 | 	ADD . /go/src/github.com/golang/example/outyet | 
 |  | 
 | 	# Build the outyet command inside the container. | 
 | 	# (You may fetch or manage dependencies here, | 
 | 	# either manually or with a tool like "godep".) | 
 | 	RUN go install github.com/golang/example/outyet | 
 |  | 
 | 	# Run the outyet command by default when the container starts. | 
 | 	ENTRYPOINT /go/bin/outyet | 
 |  | 
 | 	# Document that the service listens on port 8080. | 
 | 	EXPOSE 8080 | 
 |  | 
 | This `Dockerfile` specifies how to construct a container that runs `outyet`, | 
 | starting with the basic dependencies (a Debian system with Go installed; | 
 | the [[https://registry.hub.docker.com/_/golang/][official `golang` docker image]]), | 
 | adding the `outyet` package source, building it, and then finally running it. | 
 |  | 
 | The `ADD`, `RUN`, and `ENTRYPOINT` steps are common tasks for any Go project. | 
 | To simplify this, there is an | 
 | [[https://github.com/docker-library/golang/blob/9ff2ccca569f9525b023080540f1bb55f6b59d7f/1.3.1/onbuild/Dockerfile][`onbuild` variant]] | 
 | of the `golang` image that automatically copies the package source, fetches the | 
 | application dependencies, builds the program, and configures it to run on | 
 | startup. | 
 |  | 
 | With the `onbuild` variant, the `Dockerfile` is much simpler: | 
 |  | 
 | 	FROM golang:onbuild | 
 | 	EXPOSE 8080 | 
 |  | 
 | * Build and run the image | 
 |  | 
 | Invoke Docker from the `outyet` package directory to build an image using the `Dockerfile`: | 
 |  | 
 | 	$ docker build -t outyet . | 
 |  | 
 | This will fetch the `golang` base image from Docker Hub, copy the package source | 
 | to it, build the package inside it, and tag the resulting image as `outyet`. | 
 |  | 
 | To run a container from the resulting image: | 
 |  | 
 | 	$ docker run --publish 6060:8080 --name test --rm outyet | 
 |  | 
 | The `--publish` flag tells docker to publish the container's port `8080` on the | 
 | external port `6060`. | 
 |  | 
 | The `--name` flag gives our container a predictable name to make it easier to work with. | 
 |  | 
 | The `--rm` flag tells docker to remove the container image when the outyet server exits. | 
 |  | 
 | With the container running, open `http://localhost:6060/` in a web browser and | 
 | you should see something like this: | 
 |  | 
 | .image docker-outyet.png | 
 |  | 
 | (If your docker daemon is running on another machine (or in a virtual machine), | 
 | you should replace `localhost` with the address of that machine. If you're | 
 | using [[http://boot2docker.io/][boot2docker]] on OS X or Windows you can find | 
 | that address with `boot2docker`ip`.) | 
 |  | 
 | Now that we've verified that the image works, shut down the running container | 
 | from another terminal window: | 
 |  | 
 | 	$ docker stop test | 
 |  | 
 | * Create a repository on Docker Hub | 
 |  | 
 | [[https://hub.docker.com/][Docker Hub]], the container registry from which we | 
 | pulled the `golang` image earlier, offers a feature called | 
 | [[http://docs.docker.com/docker-hub/builds/][Automated Builds]] that builds | 
 | images from a GitHub or BitBucket repository. | 
 |  | 
 | By committing [[https://github.com/golang/example/blob/master/outyet/Dockerfile][the Dockerfile]] | 
 | to the repository and creating an | 
 | [[https://registry.hub.docker.com/u/adg1/outyet/][automated build]] | 
 | for it, anyone with Docker installed can download and run our image with a | 
 | single command. (We will see the utility of this in the next section.) | 
 |  | 
 | To set up an Automated Build, commit the Dockerfile to your repo on | 
 | [[https://github.com/][GitHub]] or [[https://bitbucket.org/][BitBucket]], | 
 | create an account on Docker Hub, and follow the instructions for | 
 | [[http://docs.docker.com/docker-hub/builds/][creating an Automated Build]]. | 
 |  | 
 | When you're done, you can run your container using the name of the automated build: | 
 |  | 
 | 	$ docker run goexample/outyet | 
 |  | 
 | (Replace `goexample/outyet` with the name of the automated build you created.) | 
 |  | 
 | * Deploy the container to Google Compute Engine | 
 |  | 
 | Google provides | 
 | [[https://developers.google.com/compute/docs/containers/container_vms][container-optimized Google Compute Engine images]] | 
 | that make it easy to spin up a virtual machine running an arbitrary Docker container. | 
 | On startup, a program running on the instance reads a configuration file that | 
 | specifies which container to run, fetches the container image, and runs it. | 
 |  | 
 | Create a [[https://cloud.google.com/compute/docs/containers/container_vms#container_manifest][containers.yaml]] | 
 | file that specifies the docker image to run and the ports to expose: | 
 |  | 
 | 	version: v1beta2 | 
 | 	containers: | 
 | 	- name: outyet | 
 | 	  image: goexample/outyet | 
 | 	  ports: | 
 | 	  - name: http | 
 | 	    hostPort: 80 | 
 | 	    containerPort: 8080 | 
 |  | 
 | (Note that we're publishing the container's port `8080` as external port `80`, | 
 | the default port for serving HTTP traffic. And, again, you should replace | 
 | `goexample/outyet` with the name of your Automated Build.) | 
 |  | 
 | Use the [[https://cloud.google.com/sdk/#Quick_Start][gcloud tool]] | 
 | to create a VM instance running the container: | 
 |  | 
 | 	$ gcloud compute instances create outyet \ | 
 | 		--image container-vm-v20140925 \ | 
 | 		--image-project google-containers \ | 
 | 		--metadata-from-file google-container-manifest=containers.yaml \ | 
 | 		--tags http-server \ | 
 | 		--zone us-central1-a \ | 
 | 		--machine-type f1-micro | 
 |  | 
 | The first argument (`outyet`) specifies the instance name, a convenient label | 
 | for administrative purposes. | 
 |  | 
 | The `--image` and `--image-project` flags specify the special | 
 | container-optimized system image to use (copy these flags verbatim). | 
 |  | 
 | The `--metadata-from-file` flag supplies your `containers.yaml` file to the VM. | 
 |  | 
 | The `--tags` flag tags your VM instance as an HTTP server, adjusting the | 
 | firewall to expose port 80 on the public network interface. | 
 |  | 
 | The `--zone` and `--machine-type` flags specify the zone in which to run the VM | 
 | and the type of machine to run. (To see a list of machine types and the zones, | 
 | run `gcloud`compute`machine-types`list`.) | 
 |  | 
 | Once this has completed, the gcloud command should print some information about | 
 | the instance. In the output, locate the `networkInterfaces` section to find the | 
 | instance's external IP address. Within a couple of minutes you should be able | 
 | to access that IP with your web browser and see the "Has Go 1.4 been released | 
 | yet?" page. | 
 |  | 
 | (To see what's happening on the new VM instance you can ssh into it with | 
 | `gcloud`compute`ssh`outyet`. From there, try `sudo`docker`ps` to see which | 
 | Docker containers are running.) | 
 |  | 
 | * Learn more | 
 |  | 
 | This is just the tip of the iceberg—there's a lot more you can do with Go, Docker, and Google Compute Engine. | 
 |  | 
 | To learn more about Docker, see their [[https://docs.docker.com/][extensive documentation]]. | 
 |  | 
 | To learn more about Docker and Go, see the [[https://registry.hub.docker.com/_/golang/][official `golang` Docker Hub repository]] and Kelsey Hightower's [[https://medium.com/@kelseyhightower/optimizing-docker-images-for-static-binaries-b5696e26eb07][Optimizing Docker Images for Static Go Binaries]]. | 
 |  | 
 | To learn more about Docker and [[http://cloud.google.com/compute][Google Compute Engine]], | 
 | see the [[https://cloud.google.com/compute/docs/containers/container_vms][Container-optimized VMs page]] | 
 | and the [[https://registry.hub.docker.com/u/google/docker-registry/][google/docker-registry Docker Hub repository]]. |