diff --git a/docker/Dockerfile.nginx b/docker/Dockerfile.nginx new file mode 100644 index 0000000..76d7048 --- /dev/null +++ b/docker/Dockerfile.nginx @@ -0,0 +1,46 @@ +FROM alpine:latest as build + +ARG UID=1000 +ARG GID=1000 + +ARG NGINX_VER=1.17.6 +ARG NGINX_CONF="--prefix=/app --with-cc-opt='-static' \ + --with-ld-opt="-static" --with-cpu-opt=generic --with-pcre \ + --sbin-path=/app/nginx \ + --http-log-path=/app/log/access.log \ + --error-log-path=/app/log/error.log \ + --pid-path=/app/nginx.pid \ + --lock-path=/app/nginx.lock \ + --without-http_gzip_module \ + --without-http_uwsgi_module \ + --without-http_scgi_module \ + --without-http_fastcgi_module \ + --without-http_memcached_module \ + --with-threads \ + --with-ld-opt='-static'" + +WORKDIR /tmp + +RUN apk --update upgrade && \ + apk add --no-cache --no-progress build-base pcre-dev wget && \ + wget http://nginx.org/download/nginx-${NGINX_VER}.tar.gz && \ + tar xzf nginx-${NGINX_VER}.tar.gz && \ + cd /tmp/nginx-${NGINX_VER} && \ + ./configure ${NGINX_CONF} && \ + make -j 1 && \ + make install && \ + mkdir /app/tmp && \ + chown -R ${UID}:${GID} /app && \ + chmod 7777 /app/tmp + + +FROM scratch + +COPY --from=build /app /app +COPY ./docker/nginx.conf /app/nginx.conf +COPY ./client /app/www + +EXPOSE 8080 + +ENTRYPOINT ["/app/nginx"] +CMD ["-c", "/app/nginx.conf"] diff --git a/docker/Dockerfile.oeffisearch b/docker/Dockerfile.oeffisearch new file mode 100644 index 0000000..079ca1d --- /dev/null +++ b/docker/Dockerfile.oeffisearch @@ -0,0 +1,29 @@ +FROM nimlang/nim:latest-alpine as build + +COPY ./src /app +COPY ./docker/config.nims /app/config.nims + +WORKDIR /app + +ENV LIBRESSLVER=3.0.2 + +RUN apk upgrade --update && \ + apk add --no-cache --no-progress openssl-dev pcre-dev file make && \ + nim installLibreSsl + +RUN nim musl -d:release -d:libressl oeffisearch.nim + + +FROM scratch + +COPY --from=build /app/oeffisearch /oeffisearch + +VOLUME ["/data"] + +WORKDIR / + +ENV CACHE_PATH=/data + +EXPOSE 8081 + +CMD ["/oeffisearch"] diff --git a/docker/config.nims b/docker/config.nims new file mode 100644 index 0000000..b442083 --- /dev/null +++ b/docker/config.nims @@ -0,0 +1,233 @@ +from macros import error + +when NimMajor < 1 and NimMinor <= 19 and NimPatch < 9: + from ospaths import `/`, splitFile +else: + from os import `/`, splitFile + +const + doOptimize = true + +let + # pcre + pcreVersion = getEnv("PCREVER", "8.42") + pcreSourceDir = "pcre-" & pcreVersion + pcreArchiveFile = pcreSourceDir & ".tar.bz2" + pcreDownloadLink = "https://downloads.sourceforge.net/pcre/" & pcreArchiveFile + pcreInstallDir = (thisDir() / "pcre/") & pcreVersion + # http://www.linuxfromscratch.org/blfs/view/8.1/general/pcre.html + pcreConfigureCmd = ["./configure", "--prefix=" & pcreInstallDir, "--enable-pcre16", "--enable-pcre32", "--disable-shared"] + pcreIncludeDir = pcreInstallDir / "include" + pcreLibDir = pcreInstallDir / "lib" + pcreLibFile = pcreLibDir / "libpcre.a" + # libressl + libreSslVersion = getEnv("LIBRESSLVER", "2.8.1") + libreSslSourceDir = "libressl-" & libreSslVersion + libreSslArchiveFile = libreSslSourceDir & ".tar.gz" + libreSslDownloadLink = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/" & libreSslArchiveFile + libreSslInstallDir = (thisDir() / "libressl/") & libreSslVersion + libreSslConfigureCmd = ["./configure", "--disable-shared", "--prefix=" & libreSslInstallDir] + libreSslLibDir = libreSslInstallDir / "lib" + libreSslLibFile = libreSslLibDir / "libssl.a" + libreCryptoLibFile = libreSslLibDir / "libcrypto.a" + libreSslIncludeDir = libreSslInstallDir / "include/openssl" + # openssl + openSslSeedConfigOsCompiler = "linux-x86_64" + openSslVersion = getEnv("OPENSSLVER", "1.1.1") + openSslSourceDir = "openssl-" & openSslVersion + openSslArchiveFile = openSslSourceDir & ".tar.gz" + openSslDownloadLink = "https://www.openssl.org/source/" & openSslArchiveFile + openSslInstallDir = (thisDir() / "openssl/") & openSslVersion + # "no-async" is needed for openssl to compile using musl + # - https://gitter.im/nim-lang/Nim?at=5bbf75c3ae7be940163cc198 + # - https://www.openwall.com/lists/musl/2016/02/04/5 + # -DOPENSSL_NO_SECURE_MEMORY is needed to make openssl compile using musl. + # - https://github.com/openssl/openssl/issues/7207#issuecomment-420814524 + openSslConfigureCmd = ["./Configure", openSslSeedConfigOsCompiler, "no-shared", "no-zlib", "no-async", "-fPIC", "-DOPENSSL_NO_SECURE_MEMORY", "--prefix=" & openSslInstallDir] + openSslLibDir = openSslInstallDir / "lib" + openSslLibFile = openSslLibDir / "libssl.a" + openCryptoLibFile = openSslLibDir / "libcrypto.a" + openSslIncludeDir = openSslInstallDir / "include/openssl" + +# https://github.com/kaushalmodi/nimy_lisp +proc dollar[T](s: T): string = + result = $s +proc mapconcat[T](s: openArray[T]; sep = " "; op: proc(x: T): string = dollar): string = + ## Concatenate elements of ``s`` after applying ``op`` to each element. + ## Separate each element using ``sep``. + for i, x in s: + result.add(op(x)) + if i < s.len-1: + result.add(sep) + +task installPcre, "Installs PCRE using musl-gcc": + if not existsFile(pcreLibFile): + if not existsDir(pcreSourceDir): + if not existsFile(pcreArchiveFile): + exec("curl -LO " & pcreDownloadLink) + exec("tar xf " & pcreArchiveFile) + else: + echo "PCRE lib source dir " & pcreSourceDir & " already exists" + withDir pcreSourceDir: + putEnv("CC", "/usr/bin/x86_64-alpine-linux-musl-gcc -static") + exec(pcreConfigureCmd.mapconcat()) + exec("make -j8") + exec("make install") + else: + echo pcreLibFile & " already exists" + setCommand("nop") + +task installLibreSsl, "Installs LIBRESSL using musl-gcc": + if (not existsFile(libreSslLibFile)) or (not existsFile(libreCryptoLibFile)): + if not existsDir(libreSslSourceDir): + if not existsFile(libreSslArchiveFile): + exec("curl -LO " & libreSslDownloadLink) + exec("tar xf " & libreSslArchiveFile) + else: + echo "LibreSSL lib source dir " & libreSslSourceDir & " already exists" + withDir libreSslSourceDir: + # -idirafter /usr/include/ # Needed for linux/sysctl.h + # -idirafter /usr/include/x86_64-linux-gnu/ # Needed for Travis/Ubuntu build to pass, for asm/types.h + putEnv("CC", "/usr/bin/x86_64-alpine-linux-musl-gcc -static -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/") + putEnv("C_INCLUDE_PATH", libreSslIncludeDir) + exec(libreSslConfigureCmd.mapconcat()) + exec("make -j8 -C crypto") # build just the "crypto" component + exec("make -j8 -C ssl") # build just the "ssl" component + exec("make -C crypto install") + exec("make -C ssl install") + else: + echo libreSslLibFile & " already exists" + setCommand("nop") + +task installOpenSsl, "Installs OPENSSL using musl-gcc": + if (not existsFile(openSslLibFile)) or (not existsFile(openCryptoLibFile)): + if not existsDir(openSslSourceDir): + if not existsFile(openSslArchiveFile): + exec("curl -LO " & openSslDownloadLink) + exec("tar xf " & openSslArchiveFile) + else: + echo "OpenSSL lib source dir " & openSslSourceDir & " already exists" + withDir openSslSourceDir: + # https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html + # -idirafter /usr/include/ # Needed for Travis/Ubuntu build to pass, for linux/version.h, etc. + # -idirafter /usr/include/x86_64-linux-gnu/ # Needed for Travis/Ubuntu build to pass, for asm/types.h + putEnv("CC", "/usr/bin/x86_64-alpine-linux-musl-gcc -static -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/") + putEnv("C_INCLUDE_PATH", openSslIncludeDir) + exec(openSslConfigureCmd.mapconcat()) + echo "The insecure switch -DOPENSSL_NO_SECURE_MEMORY is needed so that OpenSSL can be compiled using MUSL." + exec("make -j8 depend") + exec("make -j8") + exec("make install_sw") + else: + echo openSslLibFile & " already exists" + setCommand("nop") + +# -d:musl +when defined(musl): + var + muslGccPath: string + echo " [-d:musl] Building a static binary using musl .." + muslGccPath = findExe("x86_64-alpine-linux-musl-gcc") + # echo "debug: " & muslGccPath + if muslGccPath == "": + error("'musl-gcc' binary was not found in PATH.") + switch("passL", "-static") + switch("gcc.exe", muslGccPath) + switch("gcc.linkerexe", muslGccPath) + # -d:pcre + when defined(pcre): + if not existsFile(pcreLibFile): + selfExec "installPcre" # Install PCRE in current dir if pcreLibFile is not found + switch("passC", "-I" & pcreIncludeDir) # So that pcre.h is found when running the musl task + switch("define", "usePcreHeader") + switch("passL", pcreLibFile) + # -d:libressl or -d:openssl + when defined(libressl) or defined(openssl): + switch("define", "ssl") # Pass -d:ssl to nim + when defined(libressl): + let + sslLibFile = libreSslLibFile + cryptoLibFile = libreCryptoLibFile + sslIncludeDir = libreSslIncludeDir + sslLibDir = libreSslLibDir + when defined(openssl): + let + sslLibFile = openSslLibFile + cryptoLibFile = openCryptoLibFile + sslIncludeDir = openSslIncludeDir + sslLibDir = openSslLibDir + + if (not existsFile(sslLibFile)) or (not existsFile(cryptoLibFile)): + # Install SSL in current dir if sslLibFile or cryptoLibFile is not found + when defined(libressl): + selfExec "installLibreSsl" + when defined(openssl): + selfExec "installOpenSsl" + switch("passC", "-I" & sslIncludeDir) # So that ssl.h is found when running the musl task + switch("passL", "-L" & sslLibDir) + switch("passL", "-lssl") + switch("passL", "-lcrypto") # This *has* to come *after* -lssl + switch("dynlibOverride", "libssl") + switch("dynlibOverride", "libcrypto") + +proc binOptimize(binFile: string) = + ## Optimize size of the ``binFile`` binary. + echo "" + if findExe("strip") != "": + echo "Running 'strip -s' .." + exec "strip -s " & binFile + if findExe("upx") != "": + # https://github.com/upx/upx/releases/ + echo "Running 'upx --best' .." + exec "upx --best " & binFile + +# nim musl foo.nim +task musl, "Builds an optimized static binary using musl": + ## Usage: nim musl [-d:pcre] [-d:libressl|-d:openssl] .. + var + switches: seq[string] + nimFiles: seq[string] + let + numParams = paramCount() + + when defined(libressl) and defined(openssl): + error("Define only 'libressl' or 'openssl', not both.") + + # param 0 will always be "nim" + # param 1 will always be "musl" + for i in 2 .. numParams: + if paramStr(i)[0] == '-': # -d:foo or --define:foo + switches.add(paramStr(i)) + else: + # Non-switch parameters are assumed to be Nim file names. + nimFiles.add(paramStr(i)) + + if nimFiles.len == 0: + error(["The 'musl' sub-command accepts at least one Nim file name", + " Examples: nim musl FILE.nim", + " nim musl FILE1.nim FILE2.nim", + " nim musl -d:pcre FILE.nim", + " nim musl -d:libressl FILE.nim", + " nim musl -d:pcre -d:openssl FILE.nim"].mapconcat("\n")) + + for f in nimFiles: + let + extraSwitches = switches.mapconcat() + (dirName, baseName, _) = splitFile(f) + binFile = dirName / baseName # Save the binary in the same dir as the nim file + nimArgsArray = when doOptimize: + ["c", "-d:musl", "-d:release", "--opt:size", extraSwitches, f] + else: + ["c", "-d:musl", extraSwitches, f] + nimArgs = nimArgsArray.mapconcat() + # echo "[debug] f = " & f & ", binFile = " & binFile + + # Build binary + echo "\nRunning 'nim " & nimArgs & "' .." + selfExec nimArgs + + when doOptimize: + # Optimize binary + binOptimize(binFile) + + echo "\nCreated binary: " & binFile diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..eaeb62d --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,42 @@ +worker_processes auto; +pid /app/tmp/nginx.pid; +daemon off; + +events { + worker_connections 1024; + use epoll; +} + +http { + include /app/conf/mime.types; + default_type application/octet-stream; + + access_log /app/log/access.log combined; + error_log /app/log/error.log error; + + client_body_temp_path /app/tmp/client_body_temp; + proxy_temp_path /app/tmp/poxy_temp; + + sendfile on; + keepalive_timeout 15; + keepalive_disable msie6; + keepalive_requests 100; + tcp_nopush on; + tcp_nodelay off; + server_tokens off; + + server { + listen 8080; + server_name default; + + port_in_redirect off; + + root /app/www; + index index.html; + + location ~ ^/(suggestions|journeys|moreJourneys|refreshJourneys) { + proxy_pass http://oeffisearch:8081; + } + } +} +