Deploy Swift WebApps on Amazon EC2 Container Services (ECS)

TL;DR

Developing Web applications with Swift is pretty fun 🤘

We can run them on Amazon EC2 Container Services and I tried to build light-weight Docker image to deploy more faster.

Here is an example project I made, check this out :point_down:

Swift Web Frameworks

Since Swift language became open source, some web application frameworks come out.

Swifton

I’m not yet sure about which is the best. But I started trying Swifton the Ruby on Rails port of Swift Language.

The interface is pretty easy to understand:

import Swifton
import Curassow

class MyController: ApplicationController {
    override init() {
        super.init()
        action("index") { request in
            return self.render("Index")
            // renders Index.html.stencil in Views directory
        }
    }
}

let router = Router()
router.get("/", MyController()["index"])

serve { router.respond($0) }

Swifton TodoApp

Swifton has an example Todo app project: necolt/Swifton-TodoApp

This already has a Dockerfile and Heroku configurations (app.json and Procfile).

This works, so we can get started with this. But I don’t want to use Heroku for production and tried to use Amazon EC2 Container Service (ECS) instead.

Fat Docker Image

As I mentioned above, this project has a Dockerfile and we can build an image to work with ECS.

But the image contains entire depending libraries to build Swift source code in itself. That becomes 326 MB image size and 893.2 MB virtual size.

REPOSITORY  TAG     IMAGE ID      CREATED         VIRTUAL SIZE
<none>      <none>  sha256:c35f9  30 seconds ago  893.2 MB

ref: https://hub.docker.com/r/atsnngs/docker-swifton-example/tags/

So I tried to build binaries outside of Docker build process and put it on the image with minimum required assets on CircleCI.

CircleCI Ubuntu 14.04 Trusty Container

I use the Development Snapshot Swift tarball built on Trusty Ubuntu (14.04).

https://swift.org/download/#latest-development-snapshots

So I choose CircleCI’s Trusty Container that is provided as public beta.

http://blog.circleci.com/trusty-image-public-beta/

Required softwares for continuously building

First of all, we need to install the following softwares to run swift build

sudo apt-get install libicu-dev clang-3.6 jq
# We also need jq to handle awscli response.

sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.6 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 100
# See: https://goo.gl/hSfhjE

The required assets to run Swifton application

So, I ignored unneeded assets with .dockerignore file. This can reduce Docker Image build time.

*
!Views
!swift/usr/lib/swift/linux/*.so
!.build/release/Swifton-TodoApp

The Dockerfile

Here is the Dockerfile. Much simpler than the original

FROM ubuntu:14.04
MAINTAINER a@ngs.io

RUN apt-get update && apt-get install -y libicu52 libxml2 curl && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ENV APP_DIR /var/www/app
RUN mkdir -p ${APP_DIR}
WORKDIR ${APP_DIR}
ADD . ${APP_DIR}
RUN ln -s ${APP_DIR}/swift/usr/lib/swift/linux/*.so /usr/lib

EXPOSE 8000
CMD .build/release/Swifton-TodoApp

With this Dockerfile, I could reduce the image size to 88 MB, virtual size to 245.8 MB (27.5%).

REPOSITORY  TAG     IMAGE ID      CREATED         VIRTUAL SIZE
<none>      <none>  sha256:0d31d  30 seconds ago  245.8 MB

Test the Docker Image

I use Serverspec to built Docker Images to keep it reliable.

There’s some tips (and monkey patches) to run Serverspec for Docker Containers on CircleCI in Japanese, and will be translate to English later.

https://ja.ngs.io/2015/09/26/circleci-docker-serverspec/

I wrote spec for the Todo app like this:

require 'spec_helper'

describe port(8000) do
  it { should be_listening }
end

describe command('curl -i -s -H \'Accept: text/html\' http://0.0.0.0:8000/') do
  its(:exit_status) { is_expected.to eq 0 }
  its(:stdout) { is_expected.to contain 'HTTP/1.1 200 OK' }
  its(:stdout) { is_expected.to contain '<h1>Listing Todos</h1>' }
end

1.upto(2) do|n|
  describe command("curl -i -s -H \'Accept: text/html\' http://0.0.0.0:8000/todos -d \'title=Test#{n}\'") do
    its(:exit_status) { is_expected.to eq 0 }
    its(:stdout) { is_expected.to contain 'HTTP/1.1 302 FOUND' }
    its(:stdout) { is_expected.to contain 'Location: /todos' }
  end
end

describe command('curl -i -s -H \'Accept: text/html\' http://0.0.0.0:8000/todos') do
  its(:exit_status) { is_expected.to eq 0 }
  its(:stdout) { is_expected.to contain 'HTTP/1.1 200 OK' }
  its(:stdout) { is_expected.to contain '<h1>Listing Todos</h1>' }
  its(:stdout) { is_expected.to contain '<td>Test1</td>' }
  its(:stdout) { is_expected.to contain '<td>Test2</td>' }
  its(:stdout) { is_expected.to contain '<td><a href="/todos/0">Show</a></td>' }
  its(:stdout) { is_expected.to contain '<td><a href="/todos/1">Show</a></td>' }
end

Deploy on ECS

Before deploying applications on ECS, we need to push the Docker Image on Docker Registry.

$ docker tag $DOCKER_REPO "${DOCKER_REPO}:b${CIRCLE_BUILD_NUM}"
$ docker push "${DOCKER_REPO}:b${CIRCLE_BUILD_NUM}"

I won’t describe about how to set up ECS environment this article. Please refer AWS ECS Documentation.

The deploy script is executed in deploy phase of build process of the example project.

This script runs the following operations with AWS Command Line Interface.

After deploying it, you can browse the Todo Example App.

Have fun! Give me feedbacks if you found any.

https://github.com/ngs/Swifton-TodoApp