Continuous deployment of SAFE apps on linux server
For some time I was looking for solution how to do continuous deployment of my SAFE stack projects to server. My needs are:
- automagically deploy new version after push to master branch
- support multiple apps on one server with different URLs (
server.com/app1
,server.com/app2
)
My final solution consists of:
- build as Docker image
- GitLab CI / CD for build image and push it to registry
- Watchtower - docker app for automatically updating to new version of docker images
- Nginx - reverse proxy for serving multiple apps
Repository side
Full build with Docker
Following 2-phase Dockerfile do full build of SAFE app and run it.
FROM vbfox/fable-build:stretch AS builder
WORKDIR /build
RUN dotnet tool install fake-cli -g
ENV PATH="${PATH}:/root/.dotnet/tools"
# Package lock files are copied independently and their respective package
# manager are executed after.
#
# This is voluntary as docker will cache images and only re-create them if
# the already-copied files have changed, by doing that as long as no package
# is installed or updated we keep the cached container and don't need to
# re-download.
# Initialize node_modules
COPY package.json yarn.lock ./
RUN yarn install
# Initialize paket packages
COPY paket.dependencies paket.lock ./
COPY .paket .paket
RUN mono .paket/paket.exe restore
# Copy everything else and run the build
COPY . ./
RUN rm -rf deploy
RUN fake run build.fsx --target Bundle
FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine
WORKDIR /app
COPY --from=builder /build/deploy ./
WORKDIR /app/Server
EXPOSE 8085
ENTRYPOINT ["dotnet", "Server.dll"]
Gitlab CI / CD and docker registry
This .gitlab-ci.yml
config builds the docker image using above Dockerfile, and pushes it to built-in Docker container registry. Docker image adress will be registry.gitlab.com/<user-name>/<repo-name>:<branch-name>
.
image: docker:stable
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
before_script:
- docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
build:
stage: build
script:
- docker build -f build.Dockerfile -t $IMAGE_TAG .
- docker push $IMAGE_TAG
Server side
Docker run options
On my server, I am using the following command to run docker image:
docker run -p <port>:8085 --restart unless-stopped --name <name> -d <docker-image>
For each app I am using different port.
Watchtower
Watchtower is a great docker app that watches over your other running docker images, and when a new version is uploaded, that docker image is updated and restarted.
Using watchtower is simple, just run once:
docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock --restart unless-stopped v2tec/watchtower
Multiple application on one server with Nginx
Each app running on different port can be mapped to url using reverse proxy with Nginx. My configuration may look like this:
/etc/nginx/sites-enabled/reverse-proxy
server {
listen 80;
location /app1/ {
proxy_pass http://localhost:9001/;
}
location /api/app1/ {
proxy_pass http://localhost:9001/api/app1/;
}
location /app2/ {
proxy_pass http://localhost:9002/;
}
location /api/app2/ {
proxy_pass http://localhost:9002/api/app2/;
}
}
I used two entries for each app, one for main app site, the second for api that SAFE uses for communication between its client and server part. This way app works in local and production environment.
End note
A sample of working repository with my setup can be found here: safe-template.
At the moment, I am using this pipeline for 2 projects now: