Kenny Shen

Deploying a Haskell application

A simple deployment workflow with CircleCI and AWS Beanstalk

For a while now, I was accustomed to deploying Haskell applications by building them in a CI like CircleCI or TravisCI, then copying the resulting binaries compressed (via UPX) to S3, which were then copied over to the target servers via Ansible - a very nice and simple strategy we employed back when I worked at Zalora.

At Anapi, I have some applications that we were deploying as microservices, and I didn't really want to burn up time with deployments, so we decided to run these apps on AWS Beanstalk, which allowed for some nice features like autoscaling and relieving me of tasks like managing the load balancing etc. The workflow I configured in CircleCI is pretty simple:

  1. Run the usual build & test on push to Github
  2. If the build succeeds, then check if we are on a targeted branch, such as staging
  3. If we are, then execute an additional workflow that builds the app via Docker and gets pushed to AWS ECR
  4. Reload the Beanstalk environment
Here's an snippet from my .circleci/config.yml:

version: 2
    # .. build and test instructions here
    # ...
      - image: kenny/foo:latest
      - checkout
      - run: stack setup
      - run: stack install --local-bin-path .
      - setup_remote_docker
      - run: eval $(aws ecr get-login --region ap-southeast-1 --no-include-email)
      - run: docker build --rm=false -t$CIRCLE_SHA1 -f Dockerfile .
      - run: docker push$CIRCLE_SHA1
      - run: aws/ $CIRCLE_SHA1
  version: 2
      - build
      - deploy_staging:
            - build
                - staging

For the Docker images I tend to start with a very minimal distro and maintain seperate images for CI and Beanstalk. The only caveat is that reloading the application environments take a bit longer compared to the "S3 copy and deploy via Ansible" route, but it's a tradeoff I can live with.

<< back