Significant update

- Runs without root
- Internal port changed to 8000
- Healthcheck added
- Added emojis
- Switched to go modules
- Added hooks for labels
- Added env variable LISTENINGPORT with checks
- Updated readme
pull/4/head
Quentin McGaw 6 years ago
parent e3d61004cb
commit 5aae6ff56a

@ -1,29 +1,55 @@
FROM alpine:3.8 AS stackedit ARG ALPINE_VERSION=3.8
RUN echo https://alpine.global.ssl.fastly.net/alpine/v3.8/main > /etc/apk/repositories && \ ARG GO_VERSION=1.11.2
apk add -q --progress --update --no-cache git npm ARG STACKEDIT_VERSION=v5.13.2
RUN git clone --branch v5.13.2 --single-branch --depth 1 https://github.com/benweet/stackedit.git /stackedit &> /dev/null
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder
RUN apk --update add git build-base upx
RUN go get -u -v golang.org/x/vgo
WORKDIR /tmp/gobuild
FROM scratch AS final
ARG BUILD_DATE
ARG VCS_REF
ARG STACKEDIT_VERSION
LABEL org.label-schema.schema-version="1.0.0-rc1" \
maintainer="quentin.mcgaw@gmail.com" \
org.label-schema.build-date=$BUILD_DATE \
org.label-schema.vcs-ref=$VCS_REF \
org.label-schema.vcs-url="https://github.com/qdm12/stackedit-docker" \
org.label-schema.url="https://github.com/qdm12/stackedit-docker" \
org.label-schema.vcs-description="StackEdit server in a lightweight Docker container" \
org.label-schema.vcs-usage="https://github.com/qdm12/stackedit-docker/blob/master/README.md#setup" \
org.label-schema.docker.cmd="docker run -d -p 8000:8000/tcp qmcgaw/stackedit" \
org.label-schema.docker.cmd.devel="docker run -it --rm -p 8000:8000/tcp qmcgaw/stackedit" \
org.label-schema.docker.params="" \
org.label-schema.version=$STACKEDIT_VERSION \
image-size="29.3MB" \
ram-usage="7MB" \
cpu-usage="Very low"
EXPOSE 8000
HEALTHCHECK --start-period=1s --interval=100s --timeout=2s --retries=1 CMD ["/server","healthcheck"]
USER 1000
ENTRYPOINT ["/server"]
FROM alpine:${ALPINE_VERSION} AS stackedit
ARG STACKEDIT_VERSION
WORKDIR /stackedit
RUN apk add -q --progress --update --no-cache git npm
RUN wget -q https://github.com/benweet/stackedit/archive/${STACKEDIT_VERSION}.tar.gz -O stackedit.tar.gz && \
tar -xzf stackedit.tar.gz --strip-components=1 && \
rm stackedit.tar.gz
WORKDIR /stackedit WORKDIR /stackedit
RUN npm install RUN npm install
RUN npm run build RUN npm run build
FROM golang:alpine AS server FROM builder AS server
RUN apk --update add git build-base upx COPY go.mod go.sum ./
WORKDIR /go/src/app RUN go mod download
COPY main.go ./ COPY *.go ./
RUN go get -v ./... && \ RUN go test -v
CGO_ENABLED=0 GOOS=linux go build -a -ldflags="-s -w" -installsuffix cgo -o server . && \ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags="-s -w" -o app .
upx -v --best --ultra-brute --overlay=strip server && \ RUN upx -v --best --ultra-brute --overlay=strip app && upx -t app
upx -t server
FROM scratch FROM final
LABEL maintainer="quentin.mcgaw@gmail.com" \ COPY --from=stackedit --chown=1000 /stackedit/dist /html
description="StackEdit server in a lightweight Docker container" \ COPY --from=server --chown=1000 /tmp/gobuild/app /server
download="9.9MB" \
size="29.2MB" \
ram="7MB" \
cpu_usage="Very low" \
github="https://github.com/qdm12/stackedit-docker"
EXPOSE 80
COPY --from=stackedit /stackedit/dist /
COPY --from=server /go/src/app/server /server
ENTRYPOINT [ "/server" ]

@ -1,16 +1,12 @@
# StackEdit Docker server # StackEdit Docker server
Run a StackEdit v5.13.2 (October 2018) server with Nginx in a lightweight Docker container *StackEdit v5.13.2 (November 2018) with a Golang HTTP server on Scratch*
[![Docker StackEdit](https://github.com/qdm12/stackedit-docker/raw/master/readme/title.png)](https://hub.docker.com/r/qmcgaw/stackedit/) [![Docker StackEdit](https://github.com/qdm12/stackedit-docker/raw/master/readme/title.png)](https://hub.docker.com/r/qmcgaw/stackedit/)
Docker build:
[![Build Status](https://travis-ci.org/qdm12/stackedit-docker.svg?branch=master)](https://travis-ci.org/qdm12/stackedit-docker) [![Build Status](https://travis-ci.org/qdm12/stackedit-docker.svg?branch=master)](https://travis-ci.org/qdm12/stackedit-docker)
[![Docker Build Status](https://img.shields.io/docker/build/qmcgaw/stackedit.svg)](https://hub.docker.com/r/qmcgaw/stackedit) [![Docker Build Status](https://img.shields.io/docker/build/qmcgaw/stackedit.svg)](https://hub.docker.com/r/qmcgaw/stackedit)
Stackedit build:
[![Build Status](https://img.shields.io/travis/benweet/stackedit.svg?style=flat)](https://travis-ci.org/benweet/stackedit)
[![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/stackedit-docker.svg)](https://github.com/qdm12/stackedit-docker/commits) [![GitHub last commit](https://img.shields.io/github/last-commit/qdm12/stackedit-docker.svg)](https://github.com/qdm12/stackedit-docker/commits)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/stackedit-docker.svg)](https://github.com/qdm12/stackedit-docker/commits) [![GitHub commit activity](https://img.shields.io/github/commit-activity/y/qdm12/stackedit-docker.svg)](https://github.com/qdm12/stackedit-docker/commits)
[![GitHub issues](https://img.shields.io/github/issues/qdm12/stackedit-docker.svg)](https://github.com/qdm12/stackedit-docker/issues) [![GitHub issues](https://img.shields.io/github/issues/qdm12/stackedit-docker.svg)](https://github.com/qdm12/stackedit-docker/issues)
@ -19,31 +15,33 @@ Stackedit build:
[![Docker Stars](https://img.shields.io/docker/stars/qmcgaw/stackedit.svg)](https://hub.docker.com/r/qmcgaw/stackedit) [![Docker Stars](https://img.shields.io/docker/stars/qmcgaw/stackedit.svg)](https://hub.docker.com/r/qmcgaw/stackedit)
[![Docker Automated](https://img.shields.io/docker/automated/qmcgaw/stackedit.svg)](https://hub.docker.com/r/qmcgaw/stackedit) [![Docker Automated](https://img.shields.io/docker/automated/qmcgaw/stackedit.svg)](https://hub.docker.com/r/qmcgaw/stackedit)
[![](https://images.microbadger.com/badges/image/qmcgaw/stackedit.svg)](https://microbadger.com/images/qmcgaw/stackedit) [![Image size](https://images.microbadger.com/badges/image/qmcgaw/stackedit.svg)](https://microbadger.com/images/qmcgaw/stackedit)
[![](https://images.microbadger.com/badges/version/qmcgaw/stackedit.svg)](https://microbadger.com/images/qmcgaw/stackedit) [![Image version](https://images.microbadger.com/badges/version/qmcgaw/stackedit.svg)](https://microbadger.com/images/qmcgaw/stackedit)
| Download size | Image size | RAM usage | CPU usage | | Image size | RAM usage | CPU usage |
| --- | --- | --- | --- | | --- | --- | --- |
| 9.9MB | 29.2MB | 7MB | Very low | | 29.3MB | 7MB | Very low |
## Features ## Features
- [Stackedit features](https://github.com/benweet/stackedit/blob/master/README.md#stackedit-can) - [Stackedit features](https://github.com/benweet/stackedit/blob/master/README.md#stackedit-can)
- Lightweight image based on: - Lightweight image based on:
- [Stackedit 5.13.2](https://github.com/benweet/stackedit) - [Stackedit 5.13.2](https://github.com/benweet/stackedit)
- Alpine 3.8 - [Scratch](https://hub.docker.com/_/scratch)
- Nginx HTTP server - Golang simple HTTP static server
- Running without root
- Built-in Docker healthcheck
- Nice emojis in the logs...
## Setup ## Setup
Using plain Docker with: Run with:
```bash ```bash
docker run -d --name=stackedit --restart=always -p 8000:80/tcp qmcgaw/stackedit docker run -d -p 8000:8000/tcp qmcgaw/stackedit
``` ```
Compose with:
Or use Docker Compose:
```yml ```yml
version: '3' version: '3'
@ -52,22 +50,23 @@ services:
image: qmcgaw/stackedit image: qmcgaw/stackedit
container_name: stackedit container_name: stackedit
ports: ports:
- 8000:80/tcp - 8000:8000/tcp
network_mode: bridge network_mode: bridge
restart: always
``` ```
and `docker-compose up -d`
with the command Build with:
```bash ```bash
docker-compose up -d docker build -t qmcgaw/stackedit https://github.com/qdm12/stackedit-docker.git
``` ```
## Testing Access with [http://localhost:8000](http://localhost:8000)
## Environment variables
Go to [http://localhost:8000](http://localhost:8000) - `LISTENINGPORT` to change the internal HTTP server listening port if you need to
## Acknowledgements ## Acknowledgements
@ -76,4 +75,4 @@ of [StackEdit](https://stackedit.io/)
## TODOs ## TODOs
- [ ] Configuration of Stackedit - [ ] Configuration of Stackedit with env variables

@ -4,6 +4,6 @@ services:
image: qmcgaw/stackedit image: qmcgaw/stackedit
container_name: stackedit container_name: stackedit
ports: ports:
- 8000:80/tcp - 8000:8000/tcp
network_mode: bridge network_mode: bridge
restart: always restart: always

@ -0,0 +1,6 @@
module github.com/qdm12/stackedit-docker
require (
github.com/julienschmidt/httprouter v1.2.0
github.com/kyokomi/emoji v1.5.1
)

@ -0,0 +1,4 @@
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kyokomi/emoji v1.5.1 h1:qp9dub1mW7C4MlvoRENH6EAENb9skEFOvIEbp1Waj38=
github.com/kyokomi/emoji v1.5.1/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=

@ -0,0 +1,9 @@
#!/bin/bash
# see http://label-schema.org/rc1
# https://docs.docker.com/docker-cloud/builds/advanced/#override-build-test-or-push-commands
# https://docs.docker.com/docker-cloud/builds/advanced/#custom-build-phase-hooks
docker build --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
-t $IMAGE_NAME .

@ -4,21 +4,99 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os"
"strconv"
"time"
"github.com/kyokomi/emoji" "github.com/kyokomi/emoji"
) )
const listeningPort = "80" const defaultListeningPort = "8000"
func parseEnv() (listeningPort string) {
listeningPort = os.Getenv("LISTENINGPORT")
if len(listeningPort) == 0 {
listeningPort = defaultListeningPort
} else {
value, err := strconv.Atoi(listeningPort)
if err != nil {
log.Fatal(emoji.Sprint(":x:") + " LISTENINGPORT environment variable '" + listeningPort +
"' is not a valid integer")
}
if value < 1024 {
if os.Geteuid() == 0 {
log.Println(emoji.Sprint(":warning:") + "LISTENINGPORT environment variable '" + listeningPort +
"' allowed to be in the reserved system ports range as you are running as root.")
} else if os.Geteuid() == -1 {
log.Println(emoji.Sprint(":warning:") + "LISTENINGPORT environment variable '" + listeningPort +
"' allowed to be in the reserved system ports range as you are running in Windows.")
} else {
log.Fatal(emoji.Sprint(":x:") + " LISTENINGPORT environment variable '" + listeningPort +
"' can't be in the reserved system ports range (1 to 1023) when running without root.")
}
}
if value > 65535 {
log.Fatal(emoji.Sprint(":x:") + " LISTENINGPORT environment variable '" + listeningPort +
"' can't be higher than 65535")
}
if value > 49151 {
// dynamic and/or private ports.
log.Println(emoji.Sprint(":warning:") + "LISTENINGPORT environment variable '" + listeningPort +
"' is in the dynamic/private ports range (above 49151)")
}
}
return listeningPort
}
func healthcheckMode() bool {
args := os.Args
if len(args) > 1 {
if len(args) > 2 {
log.Fatal(emoji.Sprint(":x:") + " Too many arguments provided")
}
if args[1] == "healthcheck" {
return true
}
log.Fatal(emoji.Sprint(":x:") + " Argument 1 can only be 'healthcheck', not " + args[1])
}
return false
}
func healthcheck(listeningPort string) {
request, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:"+listeningPort+"/healthcheck", nil)
if err != nil {
fmt.Println("Can't build HTTP request")
os.Exit(1)
}
client := &http.Client{Timeout: time.Duration(1000) * time.Millisecond}
response, err := client.Do(request)
if err != nil {
fmt.Println("Can't execute HTTP request")
os.Exit(1)
}
if response.StatusCode != 200 {
fmt.Println("Status code is " + response.Status)
os.Exit(1)
}
os.Exit(0)
}
func main() { func main() {
listeningPort := parseEnv()
if healthcheckMode() {
healthcheck(listeningPort)
}
fmt.Println("#####################################") fmt.Println("#####################################")
fmt.Println("########## StackEdit Server #########") fmt.Println("########## StackEdit Server #########")
fmt.Println("########## by Quentin McGaw #########") fmt.Println("########## by Quentin McGaw #########")
fmt.Println("########## Give some " + emoji.Sprint(":heart:") + "at ##########") fmt.Println("########## Give some " + emoji.Sprint(":heart:") + "at ##########")
fmt.Println("# github.com/qdm12/stackedit-docker #") fmt.Println("# github.com/qdm12/stackedit-docker #")
fmt.Print("#####################################\n\n") fmt.Print("#####################################\n\n")
http.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path[1:]) http.ServeFile(w, r, "/html/"+r.URL.Path[1:])
}) })
log.Println("Web UI listening on 0.0.0.0:" + listeningPort + emoji.Sprint(" :ear:")) log.Println("Web UI listening on 0.0.0.0:" + listeningPort + emoji.Sprint(" :ear:"))
log.Fatal(http.ListenAndServe("0.0.0.0:"+listeningPort, nil)) log.Fatal(http.ListenAndServe("0.0.0.0:"+listeningPort, nil))

Loading…
Cancel
Save