Merge pull request #14 from oscartbeaumont-ext/main

@sd/server in Docker and deploy to Kubernetes
This commit is contained in:
Oscar Beaumont
2022-04-24 16:06:51 +08:00
committed by GitHub
12 changed files with 282 additions and 9 deletions

View File

@@ -0,0 +1,55 @@
name: Build and Publish Server
description: Builds and publishes the docker image for the Spacedrive server
inputs:
gh_token:
description: 'A Github PAT'
required: true
runs:
using: 'composite'
steps:
- name: Log in to the Container registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ inputs.gh_token }}
- name: Build Server
shell: bash
run: |
cargo build --release -p server
cp ./target/release/server ./apps/server/server
- name: Determine image name & tag
shell: bash
run: |
if [ "$GITHUB_EVENT_NAME" == "release" ]; then
export IMAGE_TAG=${GITHUB_REF##*/}
else
export IMAGE_TAG=$(git rev-parse --short "$GITHUB_SHA")
fi
export GITHUB_REPOSITORY_LOWER=$(echo $GITHUB_REPOSITORY | awk '{print tolower($0)}')
export IMAGE_NAME="ghcr.io/$GITHUB_REPOSITORY_LOWER/server"
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
echo "Building $IMAGE_NAME:$IMAGE_TAG"
- name: Build & push Docker image
shell: bash
run: |
docker build ./apps/server --tag $IMAGE_NAME:$IMAGE_TAG
docker push $IMAGE_NAME:$IMAGE_TAG
- name: Tag & push image as latest staging image
if: github.event_name != 'release'
shell: bash
run: |
docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:staging
docker push $IMAGE_NAME:staging
- name: Tag & push image as latest production image
if: github.event_name == 'release'
shell: bash
run: |
docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:production
docker push $IMAGE_NAME:production

View File

@@ -114,4 +114,19 @@ jobs:
with:
projectPath: apps/desktop
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish server
if: matrix.platform == 'ubuntu-latest'
uses: ./.github/actions/build-and-publish-server
with:
gh_token: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy Spacedrive Server to Kubernetes
if: matrix.platform == 'ubuntu-latest'
env:
K8S_KUBECONFIG: ${{ secrets.K8S_KUBECONFIG }}
run: |
mkdir -p ~/.kube
echo "$K8S_KUBECONFIG" > ~/.kube/config 2>&1
kubectl rollout restart deployment/sdserver-deployment

View File

@@ -4,6 +4,8 @@ on:
push:
branches:
- main
paths:
- README.md
workflow_dispatch:
jobs:

View File

@@ -1,7 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- <script src="http://localhost:8097"></script> -->
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

View File

@@ -93,7 +93,7 @@ function App() {
onError={(e) => {
setShowApp(false);
}}
src="http://localhost:8002?library_id=9068c6ec-cf90-451b-bb30-4174781e7bc6"
src={`${import.meta.env.VITE_SDWEB_BASE_URL || "http://localhost:8002"}?library_id=9068c6ec-cf90-451b-bb30-4174781e7bc6`}
/>
)}
<Section

9
apps/landing/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_SDWEB_BASE_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@@ -19,10 +19,11 @@ RUN mkdir /data
ENV DATA_DIR /data
# Drop privledges to non-root user
RUN adduser --system --no-create-home --shell /usr/sbin/nologin $USER && \
RUN groupadd -g 1001 $USER && \
adduser --system --no-create-home --shell /usr/sbin/nologin --uid 1001 --gid 1001 $USER && \
chown -R $USER /data && \
chmod -R 770 /data
USER $USER
# Run the CLI when the container is started
ENTRYPOINT [ "/sdserver" ]
ENTRYPOINT [ "/sdserver" ]

View File

@@ -0,0 +1,42 @@
# Infrastructure setups up the Kubernetes cluster for Spacedrive!
#
# To get the service account token use the following:
# ```bash
# TOKENNAME=`kubectl -n spacedrive get sa/spacedrive-ci -o jsonpath='{.secrets[0].name}'`
# kubectl -n spacedrive get secret $TOKENNAME -o jsonpath='{.data.token}' | base64 -d
# ```
apiVersion: v1
kind: Namespace
metadata:
name: spacedrive
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: spacedrive-ci
namespace: spacedrive
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: spacedrive-ns-full
namespace: spacedrive
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: spacedrive-ci-rb
namespace: spacedrive
subjects:
- kind: ServiceAccount
name: spacedrive-ci
namespace: spacedrive
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: spacedrive-ns-full

View File

@@ -0,0 +1,118 @@
# This will deploy the Spacedrive Server container to the `spacedrive`` namespace on Kubernetes.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sdserver-ingress
namespace: spacedrive
labels:
app.kubernetes.io/name: sdserver
app.kubernetes.io/component: webserver
annotations:
traefik.ingress.kubernetes.io/router.tls.certresolver: le
traefik.ingress.kubernetes.io/router.middlewares: kube-system-antiseo@kubernetescrd
spec:
rules:
- host: spacedrive.otbeaumont.me
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sdserver-service
port:
number: 8080
---
apiVersion: v1
kind: Service
metadata:
name: sdserver-service
namespace: spacedrive
labels:
app.kubernetes.io/name: sdserver
app.kubernetes.io/component: webserver
spec:
ports:
- port: 8080
targetPort: 8080
protocol: TCP
selector:
app.kubernetes.io/name: sdserver
app.kubernetes.io/component: webserver
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sdserver-pvc
namespace: spacedrive
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 512M
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sdserver-deployment
namespace: spacedrive
labels:
app.kubernetes.io/name: sdserver
app.kubernetes.io/component: webserver
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: sdserver
app.kubernetes.io/component: webserver
template:
metadata:
labels:
app.kubernetes.io/name: sdserver
app.kubernetes.io/component: webserver
spec:
restartPolicy: Always
# refer to Dockerfile to find securityContext values
securityContext:
runAsUser: 101
runAsGroup: 101
fsGroup: 101
containers:
- name: sdserver
image: ghcr.io/oscartbeaumont/spacedrive/server:staging
imagePullPolicy: Always
ports:
- containerPort: 8080
volumeMounts:
- name: data-volume
mountPath: /data
securityContext:
allowPrivilegeEscalation: false
resources:
limits:
memory: 100Mi
cpu: 100m
requests:
memory: 5Mi
cpu: 10m
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
failureThreshold: 4
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 20
failureThreshold: 3
periodSeconds: 10
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: sdserver-pvc

View File

@@ -5,7 +5,10 @@ use actix::{
Actor, AsyncContext, ContextFutureSpawner, Handler, Message, StreamHandler,
WrapFuture,
};
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
use actix_web::{
get, http::StatusCode, web, App, Error, HttpRequest, HttpResponse, HttpServer,
Responder,
};
use actix_web_actors::ws;
use serde::{Deserialize, Serialize};
@@ -123,7 +126,18 @@ impl Handler<SocketMessage> for Socket {
}
}
async fn index(
#[get("/")]
async fn index() -> impl Responder {
format!("Spacedrive Server!")
}
#[get("/health")]
async fn healthcheck() -> impl Responder {
format!("OK")
}
#[get("/ws")]
async fn ws_handler(
req: HttpRequest,
stream: web::Payload,
event_receiver: web::Data<mpsc::Receiver<CoreEvent>>,
@@ -141,6 +155,10 @@ async fn index(
resp
}
async fn not_found() -> impl Responder {
HttpResponse::build(StatusCode::OK).body("We're past the event horizon...")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let (event_receiver, controller) = setup().await;
@@ -150,7 +168,10 @@ async fn main() -> std::io::Result<()> {
App::new()
.app_data(event_receiver.clone())
.app_data(controller.clone())
.route("/ws", web::get().to(index))
.service(index)
.service(healthcheck)
.service(ws_handler)
.default_service(web::route().to(not_found))
})
.bind(("0.0.0.0", 8080))?
.run()

View File

@@ -4,7 +4,9 @@ import SpacedriveInterface from '@sd/interface';
import { ClientCommand, ClientQuery, CoreEvent } from '@sd/core';
import { BaseTransport } from '@sd/client';
const websocket = new WebSocket('ws://localhost:8080/ws');
const websocket = new WebSocket(
import.meta.env.VITE_SDSERVER_BASE_URL || 'wss://spacedrive.otbeaumont.me/ws'
);
const randomId = () => Math.random().toString(36).slice(2);

9
apps/web/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_SDSERVER_BASE_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}