AWS DevOps & Developer Productivity Blog
Running Docker on AWS OpsWorks
AWS OpsWorks lets you deploy and manage application of all shapes and sizes. OpsWorks layers let you create blueprints for EC2 instances to install and configure any software that you want. This blog will show you how to create a custom layer for Docker. For an overview of Docker, see
https://www.docker.com/tryit. Docker lets you precisely define the runtime environment for your application and deploy your application code and its runtime as a Docker container. You can use Docker containers to support new languages like Go or to incorporate your dev and test workflows seamlessly with AWS OpsWorks.
The Docker layer uses Chef recipes to install Docker and deploy containers to the EC2 instances running in that layer. Simply provide a Dockerfile and OpsWorks will automatically run the recipes to build and run the container. A stack can have multiple Docker layers and you can deploy multiple Docker containers to each layer. You can extend or change the Chef example recipes to use Docker in the way that works best for you. If you aren’t familiar with Chef recipes, see
Cookbooks 101 for an introduction.
Step 1: Create Recipes
First, create a repository to store your Chef recipes. OpsWorks supports Git and Subversion, or you can store an archive bundle on Amazon S3. The structure of a cookbook repository is described in the
OpsWorks documentation.
The
docker::install recipe installs the necessary Docker software on your instances:
case node[:platform] when "ubuntu","debian" package "docker.io" do action :install end when 'centos','redhat','fedora','amazon' package "docker" do action :install end end service "docker" do action :start end |
The docker::docker-deploy recipe deploys your docker containers (specified by a Dockerfile):
include_recipe 'deploy' node[:deploy].each do |application, deploy| if node[:opsworks][:instance][:layers].first != deploy[:environment_variables][:layer] Chef::Log.debug("Skipping deploy::docker application #{application} as it is not deployed to this layer") next end opsworks_deploy_dir do user deploy[:user] group deploy[:group] path deploy[:deploy_to] end opsworks_deploy do deploy_data deploy app application end bash "docker-cleanup" do user "root" code <<-EOH if docker ps | grep #{deploy[:application]}; then docker stop #{deploy[:application]} sleep 3 docker rm #{deploy[:application]} sleep 3 fi if docker images | grep #{deploy[:application]}; then docker rmi #{deploy[:application]} fi EOH end bash "docker-build" do user "root" cwd "#{deploy[:deploy_to]}/current" code <<-EOH docker build -t=#{deploy[:application]} . > #{deploy[:application]}-docker.out EOH end dockerenvs = " " deploy[:environment_variables].each do |key, value| dockerenvs=dockerenvs+" -e "+key+"="+value end bash "docker-run" do user "root" cwd "#{deploy[:deploy_to]}/current" code <<-EOH docker run #{dockerenvs} -p #{node[:opsworks][:instance][:private_ip]}:#{deploy[:environment_variables][:service_port]}:#{deploy[:environment_variables][:container_port]} --name #{deploy[:application]} -d #{deploy[:application]} EOH end end |
Then create a repository to store your Dockerfile. Here’s a sample Dockerfile to get you going:
FROM ubuntu:12.04 RUN apt-get update RUN apt-get install -y nginx zip curl RUN echo "daemon off;" >> /etc/nginx/nginx.conf RUN curl -o /usr/share/nginx/www/master.zip -L https://codeload.github.com/gabrielecirulli/2048/zip/master RUN cd /usr/share/nginx/www/ && unzip master.zip && mv 2048-master/* . && rm -rf 2048-master master.zip EXPOSE 80 CMD ["/usr/sbin/nginx", "-c", "/etc/nginx/nginx.conf"] |
Step 2: Create an OpsWorks Stack
Now you’re ready to use these recipes with OpsWorks. Open the
OpsWorks console:
- Select Add a Stack to create an OpsWorks stack.
- Give it a name and select Advanced.
- Set Use custom Chef Cookbooks to Yes.
- Set Repository type to Git.
- Set the Repository URL to the repository where you stored the recipes created in the previous step.
- Click the Add Stack button at the bottom of the page to create the stack.
Step 3: Add a Layer
- Select Add Layer.
- Choose Custom Layer, set the name to “Docker”, shortname to “docker”, and click Add Layer.
- Click the layer’s edit Recipes action and scroll to the Custom Chef recipes section. You will notice there are several headings—Setup, Configure, Deploy, Undeploy, and Shutdown—which correspond to OpsWorks lifecycle events. OpsWorks triggers these events at these key points in instance’s lifecycle, which runs the associated recipes.
- Enter docker::install in the Setup box and click + to add it to the list
- Enter docker::docker-deploy in the Deploy box and click + to add it to the list
- Click the Save button at the bottom to save the updated configuration.
Step 4: Add an Instance
The Layer page should now show the Docker layer. However, the layer just controls how to configure instances. You now need to add some instances to the layer. Click
Instances in the navigation pane and under the Docker layer, click
+ Instance. For this walkthrough, just accept the default settings and click
Add Instance to add the instance to the layer. Click
start in the row’s Actions column to start the instance. OpsWorks will then launch a new EC2 instance and run the Setup recipes to configure Docker. The instance’s status will change to
online when it’s ready.
Step 5: Add an App & Deploy
Once you’ve started your instances:
- In the navigation pane, click Apps and on the Apps page, click Add an app.
- On the App page, give it a Name.
- Set app’s type to other.
- Specify the app’s repository type.
- Specify the app’s repository URL. This is where your Dockerfile lives and is usually a separate repository from the cookbook repository specified in step 2.
- Set the following environment variables:
- container_port – Set this variable to the port specified by the EXPOSE parameter in your Dockerfile.
- service_port – Set this variable to the port your container will expose on the instance to the outside world. Note: Be sure that your security groups allow inbound traffic for the port specified in service_port.
- layer – Set this variable to the shortname of the layer that you want this container deployed to (from Step 3). This lets you have multiple docker layers with different apps deployed on each, such as a front-end web app and a back-end worker.
- For our example, set container_port=80, service_port=80, and layer=docker. You can also define additional environment variables that are automatically passed onto your Docker container, for example a database endpoint that your app connects with.
- Keep the default values for the remaining settings and click Add App.
- To install the code on the server, you must deploy the app. It will take some time for your instances to boot up completely. Once they show up as “online” in the Instances view, navigate to Apps and click deploy in the Actions column. If you create multiple docker layers, note that although the deployment defaults to all instances in the stack, the containers will only be deployed to the layer specified in the layer environment variable.
- Once the deployment is complete, you can see your app by clicking the public IP address of the server. You can update your Dockerfile and redeploy at any time.
Step 6: Making attributes dynamic
The recipes written for this blog pass environment variables into the Docker container when it is started. If you need to update the configuration while the app is running, such as a database password, solutions like
etcd can make attributes dynamic. An etcd server can run on each instance and be populated by the instance’s
OpsWorks attributes. You can update OpsWorks attributes, and thereby the values stored in etcd, at any time, and those values are immediately available to apps running in Docker containers. A future blog post will cover how to create recipes to install etcd and pass OpsWorks attributes, including app environment variables, to the Docker containers.
Summary
These instructions have demonstrated how use AWS OpsWorks and Docker to deploy applications, represented by Dockerfiles. You can also use Docker layers with other AWS OpsWorks features, including automatic instance scaling and integration with Elastic Load Balancing and Amazon RDS.