From c83f98ed5ca1eb4d7971a78a1b44eac069a72764 Mon Sep 17 00:00:00 2001 From: Felix Pojtinger Date: Sat, 27 Apr 2019 15:49:50 +0200 Subject: [PATCH] build: Minimize build time with multi-stage build, add Ingress and production versions --- README.md | 82 +++++++++++++++--------------- server/Dockerfile.dev | 15 ++++-- server/Dockerfile.prod | 28 +++++++++++ server/stack.prod.yaml | 110 +++++++++++++++++++++++++++++++++++++++++ skaffold.yaml | 18 ++++++- ui/Dockerfile.dev | 4 +- ui/Dockerfile.prod | 22 +++++++++ ui/stack.prod.yaml | 54 ++++++++++++++++++++ 8 files changed, 285 insertions(+), 48 deletions(-) create mode 100644 server/Dockerfile.prod create mode 100644 server/stack.prod.yaml create mode 100644 ui/Dockerfile.prod create mode 100644 ui/stack.prod.yaml diff --git a/README.md b/README.md index 8e3db385..8b7999a5 100644 --- a/README.md +++ b/README.md @@ -11,19 +11,20 @@ A link aggregator / reddit clone for the fediverse. -[Lemmy Dev instance](https://dev.lemmy.ml) *for testing purposes only* +[Lemmy Dev instance](https://dev.lemmy.ml) _for testing purposes only_ This is a **very early beta version**, and a lot of features are currently broken or in active development, such as federation. -|Front Page|Post| -|-----------------------------------------------|----------------------------------------------- | -|![main screen](https://i.imgur.com/y64BtXC.png)|![chat screen](https://i.imgur.com/vsOr87q.png) | +| Front Page | Post | +| ----------------------------------------------- | ----------------------------------------------- | +| ![main screen](https://i.imgur.com/y64BtXC.png) | ![chat screen](https://i.imgur.com/vsOr87q.png) | ## Features - Open source, [AGPL License](/LICENSE). - Self hostable, easy to deploy. - - Comes with [docker](#docker). + - Comes with [Kubernetes](#kubernetes) + - Comes with [Docker](#docker). - Live-updating Comment threads. - Full vote scores `(+/-)` like old reddit. - Moderation abilities. @@ -56,9 +57,39 @@ Made with [Rust](https://www.rust-lang.org), [Actix](https://actix.rs/), [Infern ## Usage -### Production +### Kubernetes -#### Docker +#### Requirements + +- Local or remote Kubernetes cluster, i.e. [`minikube`](https://kubernetes.io/docs/tasks/tools/install-minikube/) +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +- [`skaffold`](https://skaffold.dev/) + +#### Production + +```bash +# Deploy the Traefik Ingress +kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v1.7/examples/k8s/traefik-rbac.yaml +kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v1.7/examples/k8s/traefik-ds.yaml +# Replace ${IP} with your Ingress' IP +echo "${IP} dev.lemmy.local" >> /etc/hosts +``` + +```bash +skaffold run -p lemmy--prod +``` + +Now go to http://dev.lemmy.local. + +#### Development + +```bash +skaffold dev -p lemmy--dev +``` + +Now go to http://localhost:4444. It automatically proxies to localhost, both if the cluster is local or remote; it also hot-reloads the UI and automatically recompiles and restarts the server. + +### Docker Make sure you have both docker and docker-compose installed. @@ -70,51 +101,22 @@ cd lemmy and goto http://localhost:8536 - - -### Development - -#### Kubernetes - -##### Requirements - -- Local or remote Kubernetes cluster, i.e. [`minikube`](https://kubernetes.io/docs/tasks/tools/install-minikube/) -- [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) -- [`skaffold`](https://skaffold.dev/) - -##### Running - -```bash -skaffold dev -p lemmy--dev -``` - -And goto http://localhost:4444 (automatically proxies to localhost, both if the cluster is local or remote). - -It hot-reloads the UI and automatically recompiles the server. - -#### Non-Kubernetes - -##### Requirements +#### Requirements - [Rust](https://www.rust-lang.org/) - [Yarn](https://yarnpkg.com/en/) - [Postgres](https://www.sqlite.org/index.html) -##### Set up Postgres DB +#### Set up Postgres DB ``` psql -c "create user rrr with password 'rrr' superuser;" -U postgres psql -c 'create database rrr with owner rrr;' -U postgres ``` -##### Running +#### Running ``` git clone https://github.com/dessalines/lemmy diff --git a/server/Dockerfile.dev b/server/Dockerfile.dev index c7951ce8..203dd742 100644 --- a/server/Dockerfile.dev +++ b/server/Dockerfile.dev @@ -1,9 +1,7 @@ # Setup env -FROM rust:1.33 +FROM rust:1.33 AS build RUN USER=root cargo new --bin /opt/lemmy/server--dev WORKDIR /opt/lemmy/server--dev -# Create empty directory where the frontend would normally be -RUN mkdir -p /opt/lemmy/ui--dev/dist # Enable deps caching RUN mkdir -p src/bin RUN echo 'fn main() { println!("Dummy") }' >src/bin/main.rs @@ -17,5 +15,14 @@ COPY src/ src/ COPY migrations/ migrations/ RUN rm target/release/deps/lemmy* RUN cargo build --release + +# Setup env (no Alpine because Rust requires glibc) +FROM ubuntu:18.04 +RUN apt update +RUN apt install postgresql-client -y +# Create empty directory where the frontend would normally be +RUN mkdir -p /opt/lemmy/ui--dev/dist +# Add app +COPY --from=build /opt/lemmy/server--dev/target/release/lemmy . # Run app -CMD ["/opt/lemmy/server--dev/target/release/lemmy"] +CMD ["./lemmy"] diff --git a/server/Dockerfile.prod b/server/Dockerfile.prod new file mode 100644 index 00000000..b375e478 --- /dev/null +++ b/server/Dockerfile.prod @@ -0,0 +1,28 @@ +# Setup env +FROM rust:1.33 AS build +RUN USER=root cargo new --bin /opt/lemmy/server--prod +WORKDIR /opt/lemmy/server--prod +# Enable deps caching +RUN mkdir -p src/bin +RUN echo 'fn main() { println!("Dummy") }' >src/bin/main.rs +# Install deps +COPY Cargo.toml . +COPY Cargo.lock . +RUN cargo build --release +RUN rm src/bin/main.rs +# Add app +COPY src/ src/ +COPY migrations/ migrations/ +RUN rm target/release/deps/lemmy* +RUN cargo build --release + +# Setup env (no Alpine because Rust requires glibc) +FROM ubuntu:18.04 +RUN apt update +RUN apt install postgresql-client -y +# Create empty directory where the frontend would normally be +RUN mkdir -p /opt/lemmy/ui--prod/dist +# Add app +COPY --from=build /opt/lemmy/server--prod/target/release/lemmy . +# Run app +CMD ["./lemmy"] diff --git a/server/stack.prod.yaml b/server/stack.prod.yaml new file mode 100644 index 00000000..d221de16 --- /dev/null +++ b/server/stack.prod.yaml @@ -0,0 +1,110 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgres +data: + POSTGRES_PASSWORD: rrr + POSTGRES_USER: rrr + POSTGRES_DB: rrr + PGDATA: /var/lib/postgresql/data/pgdata + DATABASE_URL: postgres://rrr:rrr@postgres:5432/rrr +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres +spec: + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:11.2-alpine + resources: + limits: + memory: 256Mi + cpu: 512m + ports: + - containerPort: 5432 + envFrom: + - configMapRef: + name: postgres + volumeMounts: + - name: postgres + mountPath: /var/lib/postgresql/data + volumes: + - name: postgres + persistentVolumeClaim: + claimName: postgres +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres +spec: + selector: + app: postgres + ports: + - port: 5432 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: lemmy-server--prod +data: + LEMMY_FRONT_END_DIR: /opt/lemmy/ui--prod/dist # not actually used here, polyfill for monolith +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lemmy-server--prod +spec: + selector: + matchLabels: + app: lemmy-server--prod + template: + metadata: + labels: + app: lemmy-server--prod + spec: + containers: + - name: lemmy-server--prod + image: registry.gitlab.com/pojntfx/lemmy/server.prod + envFrom: + - configMapRef: + name: postgres + - configMapRef: + name: lemmy-server--prod + resources: + limits: + memory: 512Mi + cpu: 512m + ports: + - containerPort: 8536 +--- +apiVersion: v1 +kind: Service +metadata: + name: lemmy-server--prod +spec: + selector: + app: lemmy-server--prod + ports: + - port: 8536 + targetPort: 8536 diff --git a/skaffold.yaml b/skaffold.yaml index 9d1d3cd7..9aeaa585 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -19,5 +19,19 @@ profiles: deploy: kubectl: manifests: - - server/stack.dev.yaml - - ui/stack.dev.yaml + - "**/*.dev.yaml" + - name: lemmy--prod + build: + artifacts: + - image: registry.gitlab.com/pojntfx/lemmy/server.prod + context: server + docker: + dockerfile: Dockerfile.prod + - image: registry.gitlab.com/pojntfx/lemmy/ui.prod + context: ui + docker: + dockerfile: Dockerfile.prod + deploy: + kubectl: + manifests: + - "**/*.prod.yaml" diff --git a/ui/Dockerfile.dev b/ui/Dockerfile.dev index 84ebbcad..37f9e34c 100644 --- a/ui/Dockerfile.dev +++ b/ui/Dockerfile.dev @@ -1,5 +1,5 @@ # Setup env -FROM node:10 +FROM node:10-alpine RUN mkdir -p /opt/lemmy/ui--dev WORKDIR /opt/lemmy/ui--dev # Install deps @@ -9,4 +9,4 @@ RUN npm install # Add app COPY . . # Run app -CMD npm start +CMD ["npm", "start"] diff --git a/ui/Dockerfile.prod b/ui/Dockerfile.prod new file mode 100644 index 00000000..9c478e67 --- /dev/null +++ b/ui/Dockerfile.prod @@ -0,0 +1,22 @@ +# Setup env +FROM node:10-alpine AS build +RUN mkdir -p /opt/lemmy/ui--prod +WORKDIR /opt/lemmy/ui--prod +# Install deps +COPY package.json . +COPY yarn.lock . +RUN npm install +# Add app +COPY . . +# Build app +RUN npm run build + +# Setup env +FROM node:10-alpine +RUN mkdir -p /opt/lemmy/ui--prod +WORKDIR /opt/lemmy/ui--prod +RUN npm install serve +# Add app +COPY --from=build /opt/lemmy/ui--prod/dist . +# Run app +CMD ["/opt/lemmy/ui--prod/node_modules/.bin/serve", "."] diff --git a/ui/stack.prod.yaml b/ui/stack.prod.yaml new file mode 100644 index 00000000..85ac6f6b --- /dev/null +++ b/ui/stack.prod.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lemmy-ui--prod +spec: + selector: + matchLabels: + app: lemmy-ui--prod + template: + metadata: + labels: + app: lemmy-ui--prod + spec: + containers: + - name: lemmy-ui--prod + image: registry.gitlab.com/pojntfx/lemmy/ui.prod + resources: + limits: + memory: 1024Mi + cpu: 512m + ports: + - containerPort: 4444 +--- +apiVersion: v1 +kind: Service +metadata: + name: lemmy-ui--prod +spec: + selector: + app: lemmy-ui--prod + ports: + - port: 5000 + targetPort: 5000 +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: lemmy-server--prod + annotations: + traefik.ingress.kubernetes.io/request-modifier: "ReplacePathRegex: ^/static/(.*) /$1" +spec: + rules: + - host: dev.lemmy.local + http: + paths: + - path: / + backend: + serviceName: lemmy-ui--prod + servicePort: 5000 + - path: /service/ws + backend: + serviceName: lemmy-server--prod + servicePort: 8536