A starter template to develop and deploy swift 3 applications with docker.
- Swift 3.0 (SNAPSHOT-2016-03-24-a)
- Uses two environments for development and production.
- Code autoreload during development
- Kitura swift framework preinstalled (but can be replaced)
- NGINX handles static files and acts as a reverse proxy for the swift application
- Pre configured MongoDB database, comes with an admin interface to create/backup databases
- Uses environment variables to store secrets
You'll need to have
- docker
- docker-compose > 1.6.0
- docker-machine (optional)
installed on your mac.
The easiest way to get them all is to download and install docker-toolbox. If you are completely new to docker, check out the getting started with docker guide.
Make sure you can run docker commands like docker run hello-world
on your terminal, or use the docker quickstart terminal.
Clone the template in a new directory (make sure to replace NewProject
with your own project name)
git clone https://github.com/jayfk/server-side-swift-starter.git NewProject
cd NewProject/
Remove all upstream git files, we don't need them.
rm -rf .git/
Change the defaults with sed (make sure to replace the values in brackets, e.g <user>
with jayfk
)
sed -i.tmp s/#ADMIN_USER#/<user>/g env/admin.env
sed -i.tmp s/#ADMIN_PASSWORD#/<password>/g env/admin.env
sed -i.tmp s/#PROJECT_NAME#/<NewProject>/g dockerfiles/swift/autoreload.sh dockerfiles/swift/Dockerfile Package.swift
find . -type f -name \*.tmp -exec rm -f {} \;
We are going to use docker to run our swift code during development and (later) in production. This has a couple of huge benefits and a few drawbacks, but the most important thing is that we have consistent environments.
First, build and start the stack with
docker-compose -f dev.yml up
This will take forever on the first start. Docker has to pull all base images and has
to build our own images in dev.yml
. This includes a full swift runtime linked to Foundation and the MongoDB
driver to name a few. Subsequent builds will be super fast, thanks to dockers caching.
While you wait, you can check out the docker-compose getting started
section in dockers docs. Our development environment is defined in dev.yml
and uses the
dockerfiles dockerfiles/swift/Dockerfile-dev
and dockerfiles/nginx/Dockerfiles
. This will give
you a quick overview of what's taking so long.
As soon as your terminal stops spitting out text, you should see an output similiar to this
swift_1 | Compiling Swift Module 'LoggerAPI' (1 sources)
swift_1 | Compiling Swift Module 'KituraTemplateEngine' (1 sources)
swift_1 | Compiling Swift Module 'Socket' (3 sources)
swift_1 | Compiling Swift Module 'SwiftyJSON' (2 sources)
swift_1 | Compiling Swift Module 'BinaryJSON' (9 sources)
swift_1 | Compiling Swift Module 'KituraSys' (4 sources)
swift_1 | Compiling Swift Module 'MongoDB' (7 sources)
swift_1 | Compiling Swift Module 'KituraNet' (12 sources)
swift_1 | Compiling Swift Module 'Kitura' (13 sources)
swift_1 | Compiling Swift Module 'NewProject' (1 sources)
swift_1 | Linking .build/debug/NewProject
swift_1 | Setting up watches. Beware: since -r was given, this may take a while!
swift_1 | Watches established.
Open up your browser with
open "http://$(docker-machine ip dev)"
The code powering the application is in Sources/main.swift
. Change the html string and hit save, you'll see
that the application is rebuilt automatically.
rebuild code here
Refresh your browser and you should see your changes.
Pro tip: Add the IP docker-machine ip dev
is putting out to /etc/hosts
under the
docker.local
domain. For example: add a new line with 192.168.99.100 docker.local
. You will now
be able to connect to your app at http://docker.local/
-
Take a look at the admin interface to create and backup databases at
http://your-ip/admin/
-
The code in
Sources/main.swift
uses the Kitura framework. For more examples, take a look at the Kitura example project. -
You can use whatever framework you like (or code your own!). Just make sure your code starts some kind of server and speaks HTTP on port 8090.
-
Dependencies are pulled in automatically using the swift package manager. If you want to add a new dependency, add it to
Package.swift
.
Note: In this example we are going to use docker-machine to create a remote server on DigitalOcean and install docker on it. The cool thing about docker-machine is that it works with a lot of cloud providers out of the box. I've found DigitalOcean to be the easiest to get started, but if you are a fan of AWS/Microsoft Azure/Google Compute Engine or whatnot, just use them.
You need an API access token to create a new server. Let's get one:
- Go to the Digitalocean Console and log in.
- Click on API in the header.
- Click on Generate new token
- Give the token a unique name, e.g docker-machine. Make sure that the write checkbox is checked. Otherwise docker-machine won't be able to create a new droplet.
- Click on Generate Token
- Copy the newly generated token and store it somewhere safe.
Now, let's create the machine:
docker-machine create swiftapp --driver=digitalocean --digitalocean-region=nyc3 --digitalocean-size=512mb --digitalocean-access-token=YOUR_TOKEN
This will create a new 512MB Droplet in New York City 3 with the name swiftapp. If you want to
create a Droplet of a different size or in a different datacenter, make sure to adjust
--digitalocean-size
and --digitalocean-region
to your needs.
The command takes a couple of minutes to finish, the output should be similiar to this:
Running pre-create checks...
Creating machine...
(swiftapp) Creating SSH key...
(swiftapp) Creating Digital Ocean droplet...
(swiftapp) Waiting for IP address to be assigned to the Droplet...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with ubuntu(systemd)...
Installing Docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env swiftapp
If you get any errors, remove the machine by running docker-machine rm -f swiftapp
. If this
also produces errors, you can always delete the Droplet using the Digitalocean web console as a
fallback.
Select your production server with
eval $(docker-machine env swiftapp)
This will set a couple of environment variables telling the docker client on our machine to talk directly to the docker deamon on our server.
Now, we can start our stack on the server with
docker-compose up -d
Don't worry, this will take a couple of minutes the first time. The docker deamon has to fetch all the images and install all the low level dependencies first. Subsequent builds will be a lot faster since the image build process will be cached on the server.
If you are bored, you can connect to the server and and launch htop
to see what's happening
under the hood.
docker-machine ssh swiftapp
apt-get install htop && htop
Once ready, check out your shiny swift powered web app by running
open "http://$(docker-machine ip swiftapp)"
If you want to push new code to the server, you'll need to rebuild the image, create a new container and restart the running container.
docker-compose build swift
docker-compose create swift
docker-compose stop swift
docker-compose start swift
To Create an Xcode project and open it (make sure to replace NewProject
with your own project name)
docker-compose -f dev.yml run swift swift build --generate-xcodeproj .
open NewProject.xcodeproj
The generated project is mediocre at best. You'll probably see a lot of compile errors. To fix
(most of) them, download and install the March 24, 2016
development snapshot and change the toolchain in Xcode at Preferences
> Components
> Toolchains
.
In order to get code highlighting and resolution to work, you need to select the modules manually.
In Xcode, locate your main.swift
file at Sources/main.swift
and select every imported module under
Target Membership. You'll still see No such module warnings, but the highlighting/resolution will
work.