Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
René Jochum | 36d9340385 | 5 years ago |
@ -1,28 +0,0 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- cron
|
||||
- custom
|
||||
- tag
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: registry.fk.jochum.dev
|
||||
username: robot$jochum+drone
|
||||
password:
|
||||
from_secret: registry.fk.jochum.dev-robot
|
||||
repo: registry.fk.jochum.dev/jochum/homepage
|
||||
auto_tag: true
|
||||
build_args:
|
||||
- HUGO_VERSION=${DRONE_TAG:1}
|
||||
- URL=https://jochum.dev
|
@ -0,0 +1,22 @@
|
||||
stages:
|
||||
- name: Build
|
||||
steps:
|
||||
- publishImageConfig:
|
||||
dockerfilePath: ./Dockerfile
|
||||
buildContext: .
|
||||
tag: registry.wmk8s.com/rene.jochums.at/homepage:v1.0.5
|
||||
pushRemote: true
|
||||
registry: registry.wmk8s.com
|
||||
- name: Deploy
|
||||
steps:
|
||||
- applyYamlConfig:
|
||||
path: ./deployment.yaml
|
||||
timeout: 60
|
||||
notification:
|
||||
recipients:
|
||||
- recipient: rene@webmeisterei.com
|
||||
notifier: local:n-mtzwd
|
||||
condition:
|
||||
- Success
|
||||
- Changed
|
||||
- Failed
|
@ -1,20 +1,18 @@
|
||||
# Build with selfmade hugo
|
||||
FROM registry.fk.jochum.dev/jochum/hugo:latest
|
||||
|
||||
ARG HUGO_VERSION=unknown
|
||||
ENV HUGO_VERSION ${HUGO_VERSION}
|
||||
|
||||
ARG URL=https://example.com
|
||||
# Build public with hugo
|
||||
FROM jguyomard/hugo-builder:latest
|
||||
|
||||
COPY . /build
|
||||
|
||||
WORKDIR /build
|
||||
RUN /go/bin/hugo -b $URL -v -t persona
|
||||
|
||||
# Copy to a caddy container
|
||||
FROM registry.fk.jochum.dev/docker_hub_cache/library/caddy:alpine
|
||||
RUN hugo -b https://rene.jochums.at -v -t persona
|
||||
|
||||
# Copy to a nginx container
|
||||
FROM nginx:1.17-alpine
|
||||
|
||||
LABEL maintainer="René Jochum <rene@jochum.dev>"
|
||||
LABEL maintainer="René Jochum <rene@jochums.at>"
|
||||
|
||||
COPY docker/caddy/Caddyfile /etc/caddy/
|
||||
COPY docker/nginx/nginx.conf /etc/nginx/
|
||||
COPY docker/nginx/default.conf /etc/nginx/conf.d/
|
||||
|
||||
COPY --from=0 /build/public/ /usr/share/caddy/
|
||||
COPY --from=0 /build/public /var/www/rene.jochums.at
|
||||
|
@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
podman build --build-arg HUGO_VERSION=1.2.3-development --build-arg URL=http://localhost:8080 -t homepage:latest .
|
||||
podman run --rm -p "8080:80" homepage:latest
|
||||
go get github.com/spf13/hugo
|
||||
$GOPATH/bin/hugo server --bind="::1" -b http://localhost:3031/ -w -D -v -t persona --disableFastRender
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
date: 2015-06-12T00:00:00+01:00
|
||||
title: Imprint
|
||||
author: jochum
|
||||
author: pcdummy
|
||||
|
||||
---
|
||||
As this blog is hosted in Germany, it is subject to German media regulations and German media law, the Telemediengesetz (TMG). Please note that your comments have no right to publication on Rene's blog and might be deleted or shortened by me. All content on this website is subject to German intellectual property law.
|
@ -1,351 +0,0 @@
|
||||
---
|
||||
date: 2020-09-08T23:00:00+01:00
|
||||
title: Flutter simple router
|
||||
author: jochum
|
||||
tags:
|
||||
- flutter
|
||||
- Let's Check
|
||||
---
|
||||
|
||||
For the [Let's Check](https://forum.checkmk.com/t/lets-check-an-android-ios-app-for-check-mk/20895/2) App I'm writing I needed a simple router. I haven't found anything that suited my needs so I decided to role my own.
|
||||
|
||||
<!--more-->
|
||||
|
||||
My implementation supports:
|
||||
|
||||
* Regex named Args
|
||||
* All routes named, this allows usage like: ( `GlobalRouter().buildUri(routeSettingsConnection, buildArgs: {"alias": "JOCHUM"});`)
|
||||
* Static/Dynamic routes (Static string or Regex)
|
||||
* Dynamicaly register/deregister routes as needed
|
||||
|
||||
#### The Router implementation
|
||||
|
||||
I have this saved as **GlobalRouter.dart**
|
||||
|
||||
```dart
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const routeHome = 'home';
|
||||
const routeSplash = 'splash';
|
||||
const routeSettings = 'settings';
|
||||
const routeSettingsConnection = 'settings_connection';
|
||||
const routeNotFound = 'not_found';
|
||||
const routeHosts = 'hosts';
|
||||
const routeServices = 'services';
|
||||
const routeHost = 'host';
|
||||
const routeService = 'service';
|
||||
|
||||
typedef RouteBuilder = Route<dynamic> Function(RouteSettings context);
|
||||
|
||||
class BuildError implements Exception {
|
||||
final String message;
|
||||
BuildError(this.message);
|
||||
|
||||
String toString() => message;
|
||||
}
|
||||
|
||||
abstract class GlobalRoute {
|
||||
String get key;
|
||||
RouteBuilder get route;
|
||||
bool matchesRoute(String route);
|
||||
Map<String, String> extractNamedArgs(BuildContext context);
|
||||
String buildUri({Map<String, String> buildArgs});
|
||||
}
|
||||
|
||||
class ExactRoute implements GlobalRoute {
|
||||
final String key;
|
||||
final String uri;
|
||||
final RouteBuilder route;
|
||||
|
||||
ExactRoute({@required this.key, @required this.uri, @required this.route});
|
||||
|
||||
bool matchesRoute(String route) => route == uri;
|
||||
|
||||
Map<String, String> extractNamedArgs(BuildContext context) => {};
|
||||
|
||||
String buildUri({Map<String, String> buildArgs}) {
|
||||
assert(buildArgs == null);
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
class NamedArgsRoute implements GlobalRoute {
|
||||
final String key;
|
||||
final String builderUri;
|
||||
final RegExp regex;
|
||||
final Map<String, int> args;
|
||||
final RouteBuilder route;
|
||||
final bool lastArgOptional;
|
||||
|
||||
NamedArgsRoute(
|
||||
{@required this.key,
|
||||
@required this.builderUri,
|
||||
@required this.regex,
|
||||
@required this.args,
|
||||
@required this.route,
|
||||
this.lastArgOptional = false});
|
||||
|
||||
bool matchesRoute(String route) {
|
||||
return regex.hasMatch(route);
|
||||
}
|
||||
|
||||
Map<String, String> extractNamedArgs(BuildContext context) {
|
||||
var uri = ModalRoute.of(context).settings.name;
|
||||
if (!matchesRoute(uri)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
final match = regex.firstMatch(uri);
|
||||
|
||||
Map<String, String> result = {};
|
||||
for (var name in args.keys) {
|
||||
if (match.groupCount >= args[name] && match.group(args[name]) != null) {
|
||||
result[name] = Uri.decodeComponent(match.group(args[name]));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String buildUri({Map<String, String> buildArgs}) {
|
||||
if (buildArgs == null && lastArgOptional && args.length == 1) {
|
||||
return builderUri.replaceFirst(r'/{' + args.keys.first + r'}', "");
|
||||
} else if (buildArgs == null) {
|
||||
throw new BuildError("BuildArgs are not optional for route '$key'");
|
||||
}
|
||||
|
||||
if (lastArgOptional && buildArgs.keys.length < args.keys.length - 1) {
|
||||
throw new BuildError("Not all args given for route '$key'");
|
||||
} else if (buildArgs.keys.length < args.keys.length) {
|
||||
throw new BuildError("Not all args given for route '$key'");
|
||||
}
|
||||
|
||||
var result = builderUri;
|
||||
for (var argName in buildArgs.keys) {
|
||||
result = result.replaceAll('{$argName}', Uri.encodeComponent(buildArgs[argName]));
|
||||
}
|
||||
|
||||
if (lastArgOptional && result.contains('{')) {
|
||||
result = result.replaceFirst(RegExp(r"(\/?\{\S+\})$"), "");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalRoute buildRoute(
|
||||
{@required String key,
|
||||
@required String uri,
|
||||
bool lastArgOptional = false,
|
||||
RouteBuilder route}) {
|
||||
var matches = RegExp(r"\{(\w+)\}").allMatches(uri);
|
||||
if (!matches.isNotEmpty) {
|
||||
return ExactRoute(key: key, uri: uri, route: route);
|
||||
}
|
||||
|
||||
Map<String, int> args = {};
|
||||
var regex = r'^' + uri.replaceAll("/", r"\/") + r'$';
|
||||
|
||||
var i = 1;
|
||||
for (var match in matches) {
|
||||
if (lastArgOptional && i == matches.length) {
|
||||
regex = regex.replaceFirst(r'\/{' + match.group(1) + r'}', r"((\/([^\/]+))?)\/?");
|
||||
args[match.group(1)] = i + 2;
|
||||
break;
|
||||
}
|
||||
|
||||
regex = regex.replaceFirst('{' + match.group(1) + '}', r"([^\/]+)");
|
||||
args[match.group(1)] = i;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return NamedArgsRoute(
|
||||
key: key,
|
||||
builderUri: uri,
|
||||
regex: new RegExp(regex),
|
||||
args: args,
|
||||
route: route,
|
||||
lastArgOptional: lastArgOptional);
|
||||
}
|
||||
|
||||
class GlobalRouter {
|
||||
Map<String, GlobalRoute> routes = {};
|
||||
List<GlobalRoute> dynamicRoutes = [];
|
||||
Map<String, ExactRoute> exactRoutes = {};
|
||||
|
||||
final List<String> requiredRoutes = [
|
||||
routeHome,
|
||||
routeSplash,
|
||||
routeSettings,
|
||||
routeSettingsConnection,
|
||||
routeNotFound
|
||||
];
|
||||
|
||||
static final GlobalRouter _singleton = GlobalRouter._internal();
|
||||
GlobalRouter._internal();
|
||||
|
||||
factory GlobalRouter() {
|
||||
return _singleton;
|
||||
}
|
||||
|
||||
bool validateRoutes() {
|
||||
requiredRoutes.forEach((name) {
|
||||
if (!routes.containsKey(name)) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
routes.clear();
|
||||
exactRoutes.clear();
|
||||
dynamicRoutes.clear();
|
||||
}
|
||||
|
||||
void add<T extends GlobalRoute>(T route) {
|
||||
routes[route.key] = route;
|
||||
if (route is ExactRoute) {
|
||||
exactRoutes[route.uri] = route;
|
||||
} else {
|
||||
dynamicRoutes.add(route);
|
||||
}
|
||||
}
|
||||
|
||||
String buildUri(String key, {Map<String, String> buildArgs}) {
|
||||
return routes[key].buildUri(buildArgs: buildArgs);
|
||||
}
|
||||
|
||||
Map<String, String> extractNamedArgs(BuildContext context, String key) {
|
||||
return routes[key].extractNamedArgs(context);
|
||||
}
|
||||
|
||||
bool isCurrentRoute(BuildContext context, String key) {
|
||||
return routes[key].matchesRoute(ModalRoute.of(context).settings.name);
|
||||
}
|
||||
|
||||
Route<dynamic> generateRoute(RouteSettings context) {
|
||||
if (kDebugMode) {
|
||||
print("Generating route for '${context.name}'");
|
||||
}
|
||||
|
||||
if (exactRoutes.containsKey(context.name)) {
|
||||
if (kDebugMode) {
|
||||
print("... found route: ${context.name}");
|
||||
}
|
||||
return exactRoutes[context.name].route(context);
|
||||
}
|
||||
|
||||
for (var route in dynamicRoutes) {
|
||||
if (route.matchesRoute(context.name)) {
|
||||
if (kDebugMode) {
|
||||
print("... found route: ${route.key}");
|
||||
}
|
||||
return route.route(context);
|
||||
}
|
||||
}
|
||||
|
||||
print("... going to 404");
|
||||
return routes[routeNotFound].route(context);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Usage of GlobalRouter
|
||||
|
||||
This is how a static route definition looks like:
|
||||
|
||||
```dart
|
||||
class HomeScreen extends BaseSlimScreen {
|
||||
static final route = buildRoute(
|
||||
key: routeHome,
|
||||
uri: "/",
|
||||
route: (context) => MaterialPageRoute(
|
||||
settings: context,
|
||||
builder: (context) => HomeScreen(),
|
||||
));
|
||||
```
|
||||
|
||||
And this is a Regex Route:
|
||||
|
||||
```dart
|
||||
class HostScreen extends BaseSlimScreen {
|
||||
static final route = buildRoute(
|
||||
key: routeHost,
|
||||
uri: "/conn/{alias}/host/{hostname}",
|
||||
lastArgOptional: false,
|
||||
route: (context) => MaterialPageRoute(
|
||||
settings: context,
|
||||
builder: (context) => HostScreen(),
|
||||
));
|
||||
```
|
||||
|
||||
Somewhere I have register Routes with GlobalRouter():
|
||||
|
||||
File is **lib/screen/slim/slim_router.dart**
|
||||
|
||||
```dart
|
||||
import '../../global_router.dart';
|
||||
import 'splash_screen.dart';
|
||||
import 'home_screen.dart';
|
||||
import 'settings_screen.dart';
|
||||
import 'settings_connection_screen.dart';
|
||||
import 'not_found_screen.dart';
|
||||
import 'hosts_screen.dart';
|
||||
import 'services_screen.dart';
|
||||
import 'host_screen.dart';
|
||||
import 'service_screen.dart';
|
||||
|
||||
export '../../global_router.dart';
|
||||
|
||||
void registerSlimRoutes() {
|
||||
GlobalRouter().add(HomeScreen.route);
|
||||
GlobalRouter().add(SplashScreen.route);
|
||||
GlobalRouter().add(SettingsScreen.route);
|
||||
GlobalRouter().add(SettingsConnectionScreen.route);
|
||||
GlobalRouter().add(NotFoundScreen.route);
|
||||
GlobalRouter().add(HostsScreen.route);
|
||||
GlobalRouter().add(ServicesScreen.route);
|
||||
GlobalRouter().add(HostScreen.route);
|
||||
GlobalRouter().add(ServiceScreen.route);
|
||||
|
||||
assert(GlobalRouter().validateRoutes());
|
||||
}
|
||||
```
|
||||
|
||||
In **main.dart** i configure the router:
|
||||
|
||||
```dart
|
||||
Future<void> main() async {
|
||||
var mediaWidth = MediaQueryData.fromWindow(window).size.width;
|
||||
mediaWidth >= ultraWideLayoutThreshold
|
||||
? registerSlimRoutes() // UltraWide
|
||||
: mediaWidth > wideLayoutThreshold
|
||||
? registerSlimRoutes() // Wide
|
||||
: registerSlimRoutes(); // Slim
|
||||
}
|
||||
```
|
||||
|
||||
And with that GlobalRouter() is in use:
|
||||
|
||||
```dart
|
||||
class App extends StatelessWidget {
|
||||
App({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
...
|
||||
onGenerateRoute: (routeContext) =>
|
||||
GlobalRouter().generateRoute(routeContext),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### License
|
||||
|
||||
This is MIT Licensed do whatever you want with it but don't blame me. I hope it helps you to make your own Router.
|
@ -1,150 +0,0 @@
|
||||
---
|
||||
date: 2022-09-04T05:04:00+01:00
|
||||
title: Kubernetes/k3s Rancher with Traefik for HTTP/3
|
||||
author: jochum
|
||||
tags:
|
||||
- kubernetes
|
||||
- rancher
|
||||
- traefik
|
||||
---
|
||||
|
||||
Yesterday a friend of mine [Rei Bauer](https://my.stargazer.at/) told me about HTTP/3 and how much faster it made her website.
|
||||
|
||||
She got new tools, I WANT THAT TOO.
|
||||
|
||||
For me that wasn't a 5 minutes job as I had to replace [ingress-nginx](https://github.com/kubernetes/ingress-nginx) with [traefik](https://traefik.io/).
|
||||
<!--more-->
|
||||
|
||||
### Remove the current L7 Loadbalancer
|
||||
|
||||
- Remove Traefik
|
||||
|
||||
I have choosen to not use the k3s/rancher version (v2.6.x) of Traefik but use the latest and greatest (v2.8.x), so i modified my k3s with the following command:
|
||||
|
||||
**Do NOT use that command as is**
|
||||
|
||||
```bash
|
||||
curl -sfL https://get.k3s.io | sh -s - server --datastore-endpoint="mysql://k3s:<mysqlpw>@tcp(maxscale-rw.example.com:3306)/k3s" --disable servicelb --disable traefik
|
||||
```
|
||||
|
||||
Let's see what it does:
|
||||
|
||||
- *--disable servicelb* - Do not install servicelb, I replaced it with metallb.
|
||||
- *--disable traefik* - Do not install traefik
|
||||
|
||||
When you use the given command (with your own "datastore-endpoint") you will see that you loose access to all L7 Ingresses, so please be aware of that and make sure you have access over ssh to your cluster.
|
||||
|
||||
- Remove ingress-nginx
|
||||
|
||||
```bash
|
||||
helm uninstall -n kube-system ingress-nginx
|
||||
```
|
||||
|
||||
If you haven't lost layer 7 access to your cluster in the last step you will loose it now :)
|
||||
|
||||
### Install traefik from the upstream sources
|
||||
|
||||
- Add the upstream helm catalog
|
||||
|
||||
```bash
|
||||
helm repo add traefik https://helm.traefik.io/traefik
|
||||
```
|
||||
|
||||
- Save my *traefik-values.yaml* somewhere:
|
||||
|
||||
```yaml
|
||||
rbac:
|
||||
enabled: true
|
||||
ports:
|
||||
web:
|
||||
hostPort: 80
|
||||
http:
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
|
||||
websecure:
|
||||
hostPort: 443
|
||||
http3:
|
||||
advertisedPort: 443
|
||||
tls:
|
||||
enabled: true
|
||||
websecure-udp:
|
||||
port: 8443
|
||||
hostPort: 443
|
||||
protocol: UDP
|
||||
podAnnotations:
|
||||
prometheus.io/port: "8082"
|
||||
prometheus.io/scrape: "true"
|
||||
providers:
|
||||
kubernetesIngress:
|
||||
publishedService:
|
||||
enabled: true
|
||||
allowExternalNameServices: true
|
||||
kubernetesCRD:
|
||||
allowExternalNameServices: true
|
||||
priorityClassName: "system-cluster-critical"
|
||||
tolerations:
|
||||
- key: "CriticalAddonsOnly"
|
||||
operator: "Exists"
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
- key: "node-role.kubernetes.io/master"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
|
||||
experimental:
|
||||
http3:
|
||||
enabled: true
|
||||
additionalArguments:
|
||||
- "--certificatesresolvers.letsencrypt-prod.acme.caserver=https://acme-v02.api.letsencrypt.org/directory"
|
||||
image:
|
||||
name: traefik
|
||||
tag: v2.8.4
|
||||
proxyProtocol:
|
||||
enabled: true
|
||||
trustedIPs:
|
||||
- 10.0.0.0/8
|
||||
forwardedHeaders:
|
||||
enabled: true
|
||||
trustedIPs:
|
||||
- 10.0.0.0/8
|
||||
ssl:
|
||||
enabled: true
|
||||
permanentRedirect: true
|
||||
|
||||
certResolvers:
|
||||
letsencrypt-prod:
|
||||
email: support@jochum.dev
|
||||
tlsChallenge: true
|
||||
storage: /data/acme.json
|
||||
|
||||
logs:
|
||||
# general:
|
||||
# format: json
|
||||
access:
|
||||
enabled: true
|
||||
# format: json
|
||||
fields:
|
||||
headers:
|
||||
defaultmode: drop
|
||||
names:
|
||||
User-Agent: keep
|
||||
Content-Type: keep
|
||||
RequestLine: keep
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
- Change the acme email address there
|
||||
|
||||
- Install traefik
|
||||
|
||||
```bash
|
||||
helm install -n kube-system traefik traefik/traefik -f traefik-values.yaml
|
||||
```
|
||||
|
||||
Have fun with Traefik, it's internal ACME resolver and HTTP/3.
|
@ -1,49 +0,0 @@
|
||||
---
|
||||
date: 2022-09-16T04:33:00+01:00
|
||||
title: Homeserver
|
||||
author: jochum
|
||||
tags:
|
||||
- homeserver
|
||||
---
|
||||
|
||||
My Homeserver runs a lot of Free Open Source Software, maybe you find something for yourself?
|
||||
|
||||
<!--more-->
|
||||
|
||||
#### Debian GNU/Linux
|
||||
|
||||
[Debian](https://debian.org) what to say about Debian, I have it on my Desktop and all of my Servers since around version 4.0. On my Desktop i use Debian Testing/sid.
|
||||
|
||||
#### Proxmox VE and Proxmox Backup Server
|
||||
|
||||
Im a big fan of [Proxmox](https://proxmox.com/en/), I use theier [Virtual Environment](https://proxmox.com/en/proxmox-ve) which runs both LXC and KVM Machines since a long time and the [Backup Server](https://proxmox.com/en/proxmox-backup-server) since it's out there. It's just ROCK Stable.
|
||||
|
||||
#### SUSE Rancher with it's K3S
|
||||
|
||||
[k3s](https://k3s.io/) the lightweight alternative to Kubernetes Mainline, you can select your K/V storage, so no etcd which is using all of my disk IO/s :)
|
||||
|
||||
#### MariaDB
|
||||
|
||||
[MariaDB](https://mariadb.org) power's my K3S and some other stuff
|
||||
|
||||
#### PowerDNS with phpIPAM for splitdns
|
||||
|
||||
I have all my Domains in a splitdns environment for that I use a [PowerDNS](https://www.powerdns.com/) and [phpIPAM](https://phpipam.net/) as Mangement UI.
|
||||
|
||||
#### Unbound DNS Cacher
|
||||
|
||||
Rock solid DNS Cacher from cz labs
|
||||
|
||||
#### Public Services
|
||||
|
||||
- [Drone](https://www.drone.io/) for CI/CD Pipelines
|
||||
- [Gitea](https://gitea.io/en-us/) my private Github
|
||||
- [GoHarbor](https://goharbor.io/) docker registry WITH caching of other registries!
|
||||
- [Traefik](https://traefik.io/) HTTP/3 ready proxy which integrates nicely into Kubernetes.
|
||||
- [NextCloud](https://nextcloud.com/) Syncing files with it, also using Calobora CODE to have in Browser/online office.
|
||||
- Mail: Postfix, Dovecot, postfixAdmin, roundcube in custom build containers
|
||||
- [PostgreSQL](https://www.postgresql.org/) All apps that do Postgres are running on Postgres with help of PostgresOperator from Zalando.
|
||||
- [pgAdmin](https://www.pgadmin.org/)
|
||||
- [phpMyAdmin](https://www.phpmyadmin.net/)
|
||||
- [Vaultwarden](https://github.com/dani-garcia/vaultwarden) - Bitwarden Server replacement in lightweight rust
|
||||
- [Zitadel](https://zitadel.com/) My IAM of choice
|
@ -1,246 +0,0 @@
|
||||
---
|
||||
date: 2023-07-09T01:53:00+02:00
|
||||
title: Kubernetes/k3s Rancher with Traefik for HTTP/3 - v20230709
|
||||
author: jochum
|
||||
tags:
|
||||
- kubernetes
|
||||
- rancher
|
||||
- traefik
|
||||
---
|
||||
|
||||
A little update to [rancher-traefik](https://jochum.dev/20220904-rancher-traefik/), this contains the values in it's newest format.
|
||||
<!--more-->
|
||||
|
||||
### Install with helm
|
||||
|
||||
Save this as **2023-traefik-values.yaml** and adjust it for your needs:
|
||||
|
||||
```yaml
|
||||
additionalArguments: []
|
||||
additionalVolumeMounts: []
|
||||
affinity: {}
|
||||
autoscaling:
|
||||
enabled: false
|
||||
certResolvers:
|
||||
letsencrypt-prod:
|
||||
email: support@jochum.dev
|
||||
tlsChallenge: true
|
||||
httpChallenge:
|
||||
entryPoint: "web"
|
||||
# It has to match the path with a persistent volume
|
||||
storage: /data/acme.json
|
||||
commonLabels: {}
|
||||
deployment:
|
||||
additionalContainers: []
|
||||
additionalVolumes: []
|
||||
annotations: {}
|
||||
dnsConfig: {}
|
||||
enabled: true
|
||||
imagePullSecrets: []
|
||||
initContainers: []
|
||||
kind: Deployment
|
||||
labels: {}
|
||||
lifecycle: {}
|
||||
minReadySeconds: 0
|
||||
podAnnotations: {}
|
||||
podLabels: {}
|
||||
replicas: 1
|
||||
shareProcessNamespace: false
|
||||
terminationGracePeriodSeconds: 60
|
||||
env: []
|
||||
envFrom: []
|
||||
experimental:
|
||||
kubernetesGateway:
|
||||
enabled: false
|
||||
gateway:
|
||||
enabled: true
|
||||
plugins:
|
||||
enabled: false
|
||||
v3:
|
||||
enabled: false
|
||||
http3:
|
||||
enabled: true
|
||||
extraObjects: []
|
||||
globalArguments: []
|
||||
hostNetwork: false
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
registry: docker.io
|
||||
repository: traefik
|
||||
tag: v2.10.3
|
||||
name: traefik
|
||||
ingressClass:
|
||||
enabled: true
|
||||
isDefaultClass: true
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
annotations: {}
|
||||
enabled: false
|
||||
entryPoints:
|
||||
- traefik
|
||||
labels: {}
|
||||
matchRule: PathPrefix(`/dashboard`) || PathPrefix(`/api`)
|
||||
middlewares: []
|
||||
tls: {}
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 2
|
||||
logs:
|
||||
access:
|
||||
enabled: true
|
||||
fields:
|
||||
general:
|
||||
defaultmode: keep
|
||||
names: {}
|
||||
headers:
|
||||
defaultmode: drop
|
||||
names:
|
||||
Content-Type: keep
|
||||
RequestLine: keep
|
||||
User-Agent: keep
|
||||
filters: {}
|
||||
general:
|
||||
level: ERROR
|
||||
metrics:
|
||||
prometheus:
|
||||
entryPoint: metrics
|
||||
nodeSelector: {}
|
||||
persistence:
|
||||
accessMode: ReadWriteOnce
|
||||
annotations: {}
|
||||
enabled: true
|
||||
name: data
|
||||
path: /data
|
||||
size: 128Mi
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
podSecurityContext:
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
runAsGroup: 65532
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
podSecurityPolicy:
|
||||
enabled: false
|
||||
ports:
|
||||
metrics:
|
||||
expose: false
|
||||
exposedPort: 9100
|
||||
port: 9100
|
||||
protocol: TCP
|
||||
traefik:
|
||||
expose: false
|
||||
exposedPort: 9000
|
||||
port: 9000
|
||||
protocol: TCP
|
||||
web:
|
||||
expose: true
|
||||
exposedPort: 80
|
||||
port: 80
|
||||
protocol: TCP
|
||||
nodePort: 80
|
||||
websecure:
|
||||
expose: true
|
||||
exposedPort: 443
|
||||
nodePort: 443
|
||||
http3:
|
||||
enabled: true
|
||||
advertisedPort: 443
|
||||
middlewares: []
|
||||
port: 443
|
||||
protocol: TCP
|
||||
tls:
|
||||
certResolver: 'letsencrypt-prod'
|
||||
domains: []
|
||||
enabled: true
|
||||
options: ''
|
||||
priorityClassName: system-cluster-critical
|
||||
providers:
|
||||
kubernetesCRD:
|
||||
allowCrossNamespace: false
|
||||
allowEmptyServices: false
|
||||
allowExternalNameServices: true
|
||||
enabled: true
|
||||
namespaces: []
|
||||
kubernetesIngress:
|
||||
allowEmptyServices: false
|
||||
allowExternalNameServices: true
|
||||
enabled: true
|
||||
namespaces: []
|
||||
publishedService:
|
||||
enabled: true
|
||||
rbac:
|
||||
enabled: true
|
||||
namespaced: false
|
||||
readinessProbe:
|
||||
failureThreshold: 1
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 2
|
||||
resources: {}
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
service:
|
||||
annotations: {}
|
||||
annotationsTCP: {}
|
||||
annotationsUDP: {}
|
||||
enabled: true
|
||||
externalIPs: []
|
||||
labels: {}
|
||||
loadBalancerSourceRanges: []
|
||||
single: true
|
||||
spec: {}
|
||||
type: NodePort
|
||||
serviceAccount:
|
||||
name: ''
|
||||
serviceAccountAnnotations: {}
|
||||
tlsOptions: {}
|
||||
tlsStore: {}
|
||||
tolerations:
|
||||
- key: CriticalAddonsOnly
|
||||
operator: Exists
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
operator: Exists
|
||||
topologySpreadConstraints: []
|
||||
tracing: {}
|
||||
updateStrategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
type: RollingUpdate
|
||||
volumes: []
|
||||
forwardedHeaders:
|
||||
enabled: true
|
||||
trustedIPs:
|
||||
- 10.0.0.0/8
|
||||
global:
|
||||
cattle:
|
||||
systemDefaultRegistry: ''
|
||||
systemProjectId: p-g2j9j
|
||||
systemDefaultRegistry: ''
|
||||
proxyProtocol:
|
||||
enabled: true
|
||||
trustedIPs:
|
||||
- 10.0.0.0/8
|
||||
ssl:
|
||||
enabled: true
|
||||
permanentRedirect: true
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
helm uninstall -n kube-system traefik
|
||||
helm repo update
|
||||
helm install traefik traefik/traefik -n kube-system -f 2023-traefik-values.yaml
|
||||
```
|
@ -1,562 +0,0 @@
|
||||
---
|
||||
date: 2020-05-08T02:21:00+02:00
|
||||
description: Installing Rancher k3s with MariaDB Galera
|
||||
tags:
|
||||
- HOWTO
|
||||
- Kubernetes
|
||||
- Galera
|
||||
title: Howto Install Rancher k3s with MariaDB Galera
|
||||
---
|
||||
|
||||
In this blog Post we gonna install a HA Rancher Kubernetes Cluster with a MariaDB Galera Cluster as Datastore.
|
||||
|
||||
<!--more-->
|
||||
|
||||
#### Outline
|
||||
|
||||
- [Prepare Ubuntu Bionic Server]()
|
||||
- [Install MariaDB Galera]()
|
||||
- [Deploy K3S]()
|
||||
- [Install kubectl and helm]()
|
||||
- [Install MetalLB]()
|
||||
- [Install cert-manager for Let's Encrypt]()
|
||||
- [Install Rancher]()
|
||||
- [Forward HTTP/HTTPS to the Rancher Load Balancer IP]()
|
||||
|
||||
#### Prepare Ubuntu Bionic Server
|
||||
|
||||
You need 3 Nodes, 4 CPU, >8GiB RAM, 100GiB Disk, I have 3 Nodes 4 CPU, 24 GiB RAM, 250GiB Disk.
|
||||
|
||||
Install Ubuntu on one Server, remove snapd, ufw, cloud-init.
|
||||
Then clone it and edit /etc/hosts /etc/hostname /etc/netplan/50-cloud-init.yaml and `rm -f /etc/ssh/ssh_host_*` - reboot.
|
||||
|
||||
#### Install MariaDB Galera
|
||||
|
||||
Install the [MariaDB repo](https://downloads.mariadb.org/mariadb/repositories/#distro=Ubuntu&distro_release=bionic--ubuntu_bionic&mirror=host-europe&version=10.4) on each server.
|
||||
|
||||
```bash
|
||||
sudo apt-get install software-properties-common
|
||||
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
|
||||
sudo add-apt-repository 'deb [arch=amd64] http://ftp.hosteurope.de/mirror/mariadb.org/repo/10.4/ubuntu bionic main'
|
||||
|
||||
sudo apt update
|
||||
sudo apt install mariadb-server mariadb-client mariadb-backup
|
||||
```
|
||||
|
||||
##### Secure MariaDB on each Node
|
||||
|
||||
Set Password with mysql_secure_installation:
|
||||
|
||||
```bash
|
||||
pcdummy@rancher01:~$ sudo mysql_secure_installation
|
||||
|
||||
NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
|
||||
SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!
|
||||
|
||||
In order to log into MariaDB to secure it, we'll need the current
|
||||
password for the root user. If you've just installed MariaDB, and
|
||||
haven't set the root password yet, you should just press enter here.
|
||||
|
||||
Enter current password for root (enter for none):
|
||||
OK, successfully used password, moving on...
|
||||
|
||||
Setting the root password or using the unix_socket ensures that nobody
|
||||
can log into the MariaDB root user without the proper authorisation.
|
||||
|
||||
You already have your root account protected, so you can safely answer 'n'.
|
||||
|
||||
Switch to unix_socket authentication [Y/n] n
|
||||
... skipping.
|
||||
|
||||
You already have your root account protected, so you can safely answer 'n'.
|
||||
|
||||
Change the root password? [Y/n]
|
||||
New password:
|
||||
Re-enter new password:
|
||||
Password updated successfully!
|
||||
Reloading privilege tables..
|
||||
... Success!
|
||||
|
||||
|
||||
By default, a MariaDB installation has an anonymous user, allowing anyone
|
||||
to log into MariaDB without having to have a user account created for
|
||||
them. This is intended only for testing, and to make the installation
|
||||
go a bit smoother. You should remove them before moving into a
|
||||
production environment.
|
||||
|
||||
Remove anonymous users? [Y/n] Y
|
||||
... Success!
|
||||
|
||||
Normally, root should only be allowed to connect from 'localhost'. This
|
||||
ensures that someone cannot guess at the root password from the network.
|
||||
|
||||
Disallow root login remotely? [Y/n] n
|
||||
... skipping.
|
||||
|
||||
By default, MariaDB comes with a database named 'test' that anyone can
|
||||
access. This is also intended only for testing, and should be removed
|
||||
before moving into a production environment.
|
||||
|
||||
Remove test database and access to it? [Y/n] Y
|
||||
- Dropping test database...
|
||||
... Success!
|
||||
- Removing privileges on test database...
|
||||
... Success!
|
||||
|
||||
Reloading the privilege tables will ensure that all changes made so far
|
||||
will take effect immediately.
|
||||
|
||||
Reload privilege tables now? [Y/n] Y
|
||||
... Success!
|
||||
|
||||
Cleaning up...
|
||||
|
||||
All done! If you've completed all of the above steps, your MariaDB
|
||||
installation should now be secure.
|
||||
|
||||
Thanks for using MariaDB!
|
||||
```
|
||||
|
||||
##### Configure Galera on each Node
|
||||
|
||||
Stop Mariadb
|
||||
|
||||
```bash
|
||||
sudo systemctl stop mariadb
|
||||
```
|
||||
|
||||
Liste on all Interfaces (if you want configure it to listen only on a specific address):
|
||||
|
||||
```bash
|
||||
sudo sed -i 's/max_connections\t\t= 100/max_connections\t\t= 1000/g' /etc/mysql/my.cnf
|
||||
sudo sed -i 's/bind-address\t\t= 127.0.0.1/#bind-address\t\t= 127.0.0.1/g' /etc/mysql/my.cnf
|
||||
```
|
||||
|
||||
Enable Galera, Paste the following into /etc/mysql/mariadb.conf.d/99-cluster.cnf
|
||||
|
||||
```INI
|
||||
[galera]
|
||||
|
||||
wsrep_on = on
|
||||
wsrep_provider = /usr/lib/galera/libgalera_smm.so
|
||||
wsrep_cluster_address = gcomm://10.128.1.17,10.128.1.18,10.128.1.19
|
||||
wsrep_cluster_name = k3s_cluster_0
|
||||
wsrep_provider_options="gcache.size=512M"
|
||||
wsrep_sst_method = mariabackup
|
||||
wsrep_sst_auth = root:SuperSecretRootPassword
|
||||
|
||||
default_storage_engine = InnoDB
|
||||
innodb_autoinc_lock_mode = 2
|
||||
innodb_doublewrite = 1
|
||||
|
||||
binlog_format = ROW
|
||||
```
|
||||
|
||||
And change the ip addresse for `wsrep_cluster_address`
|
||||
|
||||
Some tuning if you use this Galera cluster for other purposes
|
||||
|
||||
```bash
|
||||
sudo nano /etc/mysql/mariadb.conf.d/98-tuning.cnf
|
||||
```
|
||||
|
||||
```INI
|
||||
[mysqld]
|
||||
key_buffer_size=256M
|
||||
thread_stack=192K
|
||||
thread_cache_size=8
|
||||
max_connections=1000
|
||||
innodb_buffer_pool_size=2G
|
||||
query_cache_limit=2M
|
||||
query_cache_size=0
|
||||
query_cache_type=0
|
||||
table_open_cache=128
|
||||
join_buffer_size=512k
|
||||
table_definition_cache=-1
|
||||
performance_schema=ON
|
||||
innodb_log_file_size=256M
|
||||
innodb_buffer_pool_instances=2
|
||||
tmp_table_size=32M
|
||||
max_heap_table_size=32M
|
||||
```
|
||||
|
||||
##### Bootstrap the cluster
|
||||
|
||||
One **one** node run `sudo galera_new_cluster`
|
||||
|
||||
One the other 2 nodes run: `sudo systemctl start mariadb.service`
|
||||
|
||||
##### Check the MariaDB Galera Cluster
|
||||
|
||||
```bash
|
||||
mysql -u root -p -e "SELECT * FROM information_schema.global_status WHERE variable_name IN ('WSREP_CLUSTER_STATUS','WSREP_LOCAL_STATE_COMMENT','WSREP_CLUSTER_SIZE','WSREP_EVS_REPL_LATENCY','WSREP_EVS_DELAYED','WSREP_READY');"
|
||||
```
|
||||
|
||||
|
||||
##### Install and configure MaxScale
|
||||
|
||||
```bash
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 135659E928C12247
|
||||
sudo add-apt-repository 'deb [arch=amd64] http://downloads.mariadb.com/MaxScale/2.2/ubuntu bionic main'
|
||||
sudo apt install maxscale
|
||||
```
|
||||
|
||||
```sql
|
||||
mysql -u root -p
|
||||
CREATE USER 'maxscale'@'%' IDENTIFIED BY 'SuperSecretPassword';
|
||||
GRANT SELECT ON mysql.user TO 'maxscale'@'%';
|
||||
GRANT SELECT ON mysql.db TO 'maxscale'@'%';
|
||||
GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'%';
|
||||
GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'%';
|
||||
GRANT SHOW DATABASES ON *.* TO 'maxscale'@'%';
|
||||
GRANT REPLICATION CLIENT ON *.* TO 'maxscale'@'%';
|
||||
GRANT SUPER ON *.* TO maxscale@'%';
|
||||
FLUSH PRIVILEGES;
|
||||
exit
|
||||
```
|
||||
|
||||
Generate MaxScale Keys **note the encrypted password** and write replace it in /etc/maxscale.cnf
|
||||
|
||||
```bash
|
||||
sudo systemctl stop maxscale
|
||||
sudo maxkeys /var/lib/maxscale/
|
||||
sudo maxpasswd 'SuperSecretPassword'
|
||||
sudo chown maxscale: /var/lib/maxscale/.secrets
|
||||
```
|
||||
|
||||
/etc/maxscale.cnf
|
||||
|
||||
```INI
|
||||
# MaxScale documentation:
|
||||
# https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22/
|
||||
|
||||
# Global parameters
|
||||
#
|
||||
# Complete list of configuration options:
|
||||
# https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-mariadb-maxscale-configuration-usage-scenarios/
|
||||
|
||||
[maxscale]
|
||||
threads=auto
|
||||
|
||||
# Server definitions
|
||||
#
|
||||
# Set the address of the server to the network
|
||||
# address of a MariaDB server.
|
||||
#
|
||||
|
||||
[server1]
|
||||
type=server
|
||||
address=10.248.8.1
|
||||
port=3306
|
||||
protocol=MariaDBBackend
|
||||
|
||||
[server2]
|
||||
type=server
|
||||
address=10.248.8.2
|
||||
port=3306
|
||||
protocol=MariaDBBackend
|
||||
|
||||
[server3]
|
||||
type=server
|
||||
address=10.248.8.3
|
||||
port=3306
|
||||
protocol=MariaDBBackend
|
||||
|
||||
# Monitor for the servers
|
||||
#
|
||||
# This will keep MaxScale aware of the state of the servers.
|
||||
# MariaDB Monitor documentation:
|
||||
# https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-mariadb-monitor/
|
||||
|
||||
[MariaDB-Monitor]
|
||||
type=monitor
|
||||
module=galeramon
|
||||
servers=server1,server2,server3
|
||||
user=maxscale
|
||||
passwd=D83ED4E84351BD822950FDE5C2991889
|
||||
monitor_interval=2000
|
||||
|
||||
# Service definitions
|
||||
#
|
||||
# Service Definition for a read-only service and
|
||||
# a read/write splitting service.
|
||||
#
|
||||
|
||||
# ReadWriteSplit documentation:
|
||||
# https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-readwritesplit/
|
||||
|
||||
[Read-Write-Service]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3
|
||||
user=maxscale
|
||||
passwd=D83ED4E84351BD822950FDE5C2991889
|
||||
|
||||
# This service enables the use of the MaxAdmin interface
|
||||
# MaxScale administration guide:
|
||||
# https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-maxadmin-admin-interface/
|
||||
|
||||
[MaxAdmin-Service]
|
||||
type=service
|
||||
router=cli
|
||||
|
||||
# Listener definitions for the services
|
||||
#
|
||||
# These listeners represent the ports the
|
||||
# services will listen on.
|
||||
#
|
||||
|
||||
[Read-Write-Listener]
|
||||
type=listener
|
||||
service=Read-Write-Service
|
||||
protocol=MariaDBClient
|
||||
port=4006
|
||||
|
||||
[MaxAdmin-Listener]
|
||||
type=listener
|
||||
service=MaxAdmin-Service
|
||||
protocol=maxscaled
|
||||
socket=default
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo systemctl start maxscale
|
||||
sudo systemctl enable maxscale
|
||||
```
|
||||
|
||||
##### Create the k3s Database
|
||||
|
||||
One one node run:
|
||||
|
||||
```bash
|
||||
mysql -u root -p
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE DATABASE `k3s`;
|
||||
GRANT ALL PRIVILEGES ON `k3s`.* TO 'k3s'@'%' IDENTIFIED BY '<superSecret>';
|
||||
```
|
||||
|
||||
|
||||
#### Deploy k3s
|
||||
|
||||
Install k3s one each nodes, one after another:
|
||||
|
||||
```bash
|
||||
curl -sfL https://get.k3s.io | sh -s - server --datastore-endpoint="mysql://k3s:SuperSecretPassword@tcp(localhost:4006)/k3s" --no-deploy servicelb --no-deploy traefik
|
||||
```
|
||||
|
||||
Check the nodes after.
|
||||
|
||||
```bash
|
||||
sudo k3s kubectl get nodes
|
||||
```
|
||||
|
||||
One one node copy the config (I choose node1 for that):
|
||||
|
||||
```bash
|
||||
mkdir ~/.kube
|
||||
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
|
||||
sudo chown -R $(whoami): ~/.kube
|
||||
```
|
||||
|
||||
#### Install kubectl and helm
|
||||
|
||||
Install kubectl
|
||||
|
||||
```bash
|
||||
sudo apt-get update && sudo apt-get install -y apt-transport-https
|
||||
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
|
||||
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y kubectl
|
||||
```
|
||||
|
||||
Install helm to ~/bin
|
||||
|
||||
```bash
|
||||
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
|
||||
chmod 700 get_helm.sh
|
||||
./get_helm.sh
|
||||
```
|
||||
|
||||
#### Install MetalLB
|
||||
|
||||
Install MetalLB (change the address range!)
|
||||
|
||||
See: [metallb install](https://metallb.universe.tf/installation/)
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
|
||||
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
|
||||
# On first install only
|
||||
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
|
||||
```
|
||||
|
||||
metallb.yaml -> kubectl apply -f metallb.yaml
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
namespace: metallb-system
|
||||
name: config
|
||||
data:
|
||||
config: |
|
||||
address-pools:
|
||||
- name: default
|
||||
protocol: layer2
|
||||
addresses:
|
||||
- 10.248.11.1-10.248.11.253
|
||||
```
|
||||
|
||||
Check the deployment
|
||||
|
||||
```bash
|
||||
kubectl get pods -n metallb-system -l app=metallb -o wide
|
||||
```
|
||||
|
||||
#### Install cert-manager for Let's Encrypt
|
||||
|
||||
```bash
|
||||
kubectl create namespace cert-manager
|
||||
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.15.0/cert-manager.crds.yaml
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
helm repo update
|
||||
helm install \
|
||||
cert-manager jetstack/cert-manager \
|
||||
--namespace cert-manager \
|
||||
--version v0.15.0
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl get pods --namespace cert-manager
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
cert-manager-6bcdf8c5cc-5bcrg 1/1 Running 0 54s
|
||||
cert-manager-cainjector-6659d6844d-zrr5h 1/1 Running 0 54s
|
||||
cert-manager-webhook-547567b88f-ptrlg 1/1 Running 0 54s
|
||||
```
|
||||
|
||||
#### Install Nginx
|
||||
|
||||
```bash
|
||||
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
|
||||
helm repo update
|
||||
helm install ingress-nginx ingress-nginx/ingress-nginx \
|
||||
--namespace kube-system \
|
||||
--set controller.image.runAsUser=101 \
|
||||
--set defaultBackend.enabled=false
|
||||
```
|
||||
|
||||
#### Install Rancher
|
||||
|
||||
```bash
|
||||
helm repo add rancher-latest https://releases.rancher.com/server-charts/latest
|
||||
helm repo update
|
||||
kubectl create namespace cattle-system
|
||||
helm install rancher rancher-latest/rancher \
|
||||
--namespace cattle-system \
|
||||
--set hostname=rancher.example.org \
|
||||
--set ingress.tls.source=letsEncrypt \
|
||||
--set letsEncrypt.email=support@example.org
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl -n cattle-system rollout status deploy/rancher
|
||||
Waiting for deployment "rancher" rollout to finish: 0 of 3 updated replicas are available...
|
||||
|
||||
Waiting for deployment "rancher" rollout to finish: 1 of 3 updated replicas are available...
|
||||
Waiting for deployment "rancher" rollout to finish: 2 of 3 updated replicas are available...
|
||||
deployment "rancher" successfully rolled out
|
||||
```
|
||||
|
||||
#### Forward HTTP/HTTPS to the Rancher Load Balancer IP
|
||||
|
||||
```bash
|
||||
$ kubectl -n kube-system describe service/traefik
|
||||
Name: traefik
|
||||
Namespace: kube-system
|
||||
Labels: app=traefik
|
||||
chart=traefik-1.81.0
|
||||
heritage=Helm
|
||||
release=traefik
|
||||
Annotations: field.cattle.io/publicEndpoints:
|
||||
[{"addresses":["10.128.3.1"],"port":80,"protocol":"TCP","serviceName":"kube-system:traefik","allNodes":false},{"addresses":["10.128.3.1"],...
|
||||
Selector: app=traefik,release=traefik
|
||||
Type: LoadBalancer
|
||||
IP: 10.43.47.63
|
||||
LoadBalancer Ingress: 10.128.3.1
|
||||
Port: http 80/TCP
|
||||
TargetPort: http/TCP
|
||||
NodePort: http 32316/TCP
|
||||
Endpoints: 10.42.0.6:80
|
||||
Port: https 443/TCP
|
||||
TargetPort: https/TCP
|
||||
NodePort: https 30752/TCP
|
||||
Endpoints: 10.42.0.6:443
|
||||
Session Affinity: None
|
||||
External Traffic Policy: Cluster
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal IPAllocated 16m metallb-controller Assigned IP "10.128.3.1"
|
||||
Normal nodeAssigned 3m10s (x5 over 16m) metallb-speaker announcing from node "rancher01"
|
||||
```
|
||||
|
||||
Here the IP is 10.128.3.1 i forward HTTP (80) and HTTPS (443) to it.
|
||||
|
||||
|
||||
Wait for the Let's Encrypt Cert
|
||||
|
||||
```bash
|
||||
$ kubectl -n cattle-system describe certificate
|
||||
Name: tls-rancher-ingress
|
||||
Namespace: cattle-system
|
||||
Labels: app=rancher
|
||||
chart=rancher-2.4.3
|
||||
heritage=Tiller
|
||||
release=rancher
|
||||
Annotations: <none>
|
||||
API Version: cert-manager.io/v1alpha2
|
||||
Kind: Certificate
|
||||
Metadata:
|
||||
Creation Timestamp: 2020-05-07T23:07:41Z
|
||||
Generation: 1
|
||||
Owner References:
|
||||
API Version: extensions/v1beta1
|
||||
Block Owner Deletion: true
|
||||
Controller: true
|
||||
Kind: Ingress
|
||||
Name: rancher
|
||||
UID: 625bd78c-819a-4ba5-8ed0-4e8cf0497860
|
||||
Resource Version: 23050
|
||||
Self Link: /apis/cert-manager.io/v1alpha2/namespaces/cattle-system/certificates/tls-rancher-ingress
|
||||
UID: 6e0886ec-8b2d-4459-8c60-f994f269a146
|
||||
Spec:
|
||||
Dns Names:
|
||||
rancher.example.org
|
||||
Issuer Ref:
|
||||
Group: cert-manager.io
|
||||
Kind: Issuer
|
||||
Name: rancher
|
||||
Secret Name: tls-rancher-ingress
|
||||
Status:
|
||||
Conditions:
|
||||
Last Transition Time: 2020-05-07T23:07:41Z
|
||||
Message: Waiting for CertificateRequest "tls-rancher-ingress-2753661366" to complete
|
||||
Reason: InProgress
|
||||
Status: False
|
||||
Type: Ready
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal GeneratedKey 18m cert-manager Generated a new private key
|
||||
Normal Requested 18m cert-manager Created new CertificateRequest resource "tls-rancher-ingress-2753661366"
|
||||
```
|
||||
|
||||
I had some troubles with cert-manager where it wasn't able to access http://<rancher-FQDN> without that it is not able to generate the certificate.
|
||||
|
||||
#### Links
|
||||
|
||||
[kauri.io](https://kauri.io/38-install-and-configure-a-kubernetes-cluster-with/418b3bc1e0544fbc955a4bbba6fff8a9/a) - Some Informations from there
|
||||
[howtoforge Galera](https://www.howtoforge.com/how-to-setup-mariadb-galera-multi-master-synchronous-replication-using-debian-10/) - Install Galera
|
@ -0,0 +1,88 @@
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
deployment.kubernetes.io/revision: "1"
|
||||
generation: 3
|
||||
name: nginx
|
||||
namespace: rene-jochums-at
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
container: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
container: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: registry.wmk8s.com/rene.jochums.at/homepage:v1.0.5
|
||||
imagePullPolicy: Always
|
||||
name: nginx
|
||||
resources: {}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities: {}
|
||||
privileged: false
|
||||
procMount: Default
|
||||
readOnlyRootFilesystem: false
|
||||
runAsNonRoot: false
|
||||
stdin: true
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
tty: true
|
||||
dnsPolicy: ClusterFirst
|
||||
imagePullSecrets:
|
||||
- name: wmk8s
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
- kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
workloadID_nginx: "true"
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
|
||||
- kind: Ingress
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
annotations:
|
||||
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
|
||||
kubernetes.io/tls-acme: "true"
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
generation: 2
|
||||
name: website
|
||||
namespace: rene-jochums-at
|
||||
spec:
|
||||
rules:
|
||||
- host: rene.jochums.at
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
path: /
|
||||
tls:
|
||||
- hosts:
|
||||
- rene.jochums.at
|
||||
secretName: website-prod-tls
|
@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script will make a best-effort attempt at showing modifications
|
||||
# to package-provided config files on a Debian system.
|
||||
#
|
||||
# It's subject to some pretty significant limitations: most notably,
|
||||
# there's no way to identify all such config files. We approximate the
|
||||
# answer by looking first at dpkg-managed conffiles, and then hoping
|
||||
# that most of the time, if maintainer scripts are managing files
|
||||
# themselves, they're using ucf. So, DO NOT TRUST THIS SCRIPT to find
|
||||
# everything... but it should help to find most customisation.
|
||||
|
||||
|
||||
# Set this non-empty to see a diff against empty for apparently-deleted
|
||||
# files; leave it empty for a single 'file deleted' note.
|
||||
diff_empty=
|
||||
|
||||
# Space-separated list of directory *trees* to be searched for package
|
||||
# files. This is the only means of locating packages that can't be
|
||||
# installed by apt. Note that we do a recursive search in here *before*
|
||||
# we ask apt to download the package; don't point it at a stupidly-large
|
||||
# tree.
|
||||
local_packages="/var/cache/puppet"
|
||||
|
||||
|
||||
|
||||
package_version() {
|
||||
pkg="$1"
|
||||
dpkg-query -W -f='${Version}\n' "$pkg"
|
||||
}
|
||||
|
||||
# I've made no attempt to create a sensible overall ordering; we keep
|
||||
# files grouped by package within a particular section, then hope that
|
||||
# most packages won't mix config file types.
|
||||
|
||||
|
||||
#############
|
||||
# conffiles
|
||||
|
||||
package_file() {
|
||||
pkg="$1"
|
||||
|
||||
exec 3< <(dpkg-query -W -f='${Version} ${Architecture} ${Status}\n' "$pkg")
|
||||
read -u3 version arch status
|
||||
|
||||
if [ "$status" != "install ok installed" -o -z "$version" ]; then
|
||||
# Package isn't actually installed; ignore it.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
basename="${pkg}_${version//:/%3a}_${arch}.deb"
|
||||
filename="/var/cache/apt/archives/$basename"
|
||||
|
||||
if [ -f "$filename" ]; then
|
||||
echo "$filename"
|
||||
exit
|
||||
fi
|
||||
|
||||
found="$(find $local_packages -name "$basename" -print -quit)"
|
||||
if [ -n "$found" ]; then
|
||||
echo "$found"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$UID" -gt 0 ]; then
|
||||
echo "Package ${pkg} (${version}, ${arch}) is not available; need to install, but not root" >&2
|
||||
exit 1
|
||||
fi
|
||||
apt-get -qq --download-only --reinstall install "${pkg}=${version}"
|
||||
|
||||
if [ -f "$filename" ]; then
|
||||
echo "$filename"
|
||||
else
|
||||
echo "Failed to download ${pkg} (${version}, ${arch})" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
original_content() {
|
||||
pkg="$1"
|
||||
file="$2"
|
||||
|
||||
deb="$(package_file "$pkg")"
|
||||
if [ "$?" -ne 0 -o -z "$deb" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dpkg-deb --fsys-tarfile "$deb" | tar -x -O ".$file"
|
||||
}
|
||||
|
||||
|
||||
dpkg-query -W -f='${Conffiles}\n' '*' |
|
||||
awk 'OFS=" "{print $2,$1}' |
|
||||
md5sum -c 2>/dev/null |
|
||||
awk -F': ' '$2 !~ /OK/{print $1}' |
|
||||
xargs dpkg -S |
|
||||
sort -u |
|
||||
awk -F ': ' 'OFS=" "{print $1,$2}' |
|
||||
while read pkg file; do
|
||||
if [ ! -f "$file" -a -z "$diff_empty" ]; then
|
||||
echo "Deleted: $file (from $pkg)"
|
||||
else
|
||||
content="$(original_content "$pkg" "$file")"
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo "package $pkg"
|
||||
diff -u --new-file --report-identical-files --label "$pkg $(package_version "$pkg")" <(echo "$content") "$file"
|
||||
else
|
||||
echo "Failed to load original for $file from $pkg"
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
done
|
||||
|
||||
|
||||
#######
|
||||
# ucf
|
||||
|
||||
md5sum -c /var/lib/ucf/hashfile 2>/dev/null |
|
||||
awk -F': ' '$2 !~ /OK/{print $1}' |
|
||||
xargs ucfq -w |
|
||||
sort -t ':' -k 2,1 | uniq |
|
||||
awk -F: 'OFS=" " {print $1,$2}' |
|
||||
while read file pkg; do
|
||||
if [ ! -f "$file" -a -z "$diff_empty" ]; then
|
||||
echo "Deleted: $file (from ${pkg:-??})"
|
||||
else
|
||||
cache="/var/lib/ucf/cache/${file//\//:}"
|
||||
if [ -f "$cache" ]; then
|
||||
if [ -n "$pkg" ]; then
|
||||
echo "package $pkg"
|
||||
label="$pkg $(package_version "$pkg")"
|
||||
else
|
||||
label="original"
|
||||
fi
|
||||
diff -u --new-file --report-identical-files --label "$label" "$cache" "$file"
|
||||
else
|
||||
echo "Failed to load original for $file from ${pkg:-??}"
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
done
|
@ -1,19 +0,0 @@
|
||||
:80 {
|
||||
log
|
||||
|
||||
# Set this path to your site's directory.
|
||||
root * /usr/share/caddy
|
||||
|
||||
# Compress responses according to Accept-Encoding headers
|
||||
encode zstd gzip
|
||||
|
||||
# Client side caching
|
||||
@static {
|
||||
file
|
||||
path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.woff2
|
||||
}
|
||||
header @static Cache-Control max-age=31536000
|
||||
|
||||
# Enable the static file server.
|
||||
file_server
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
|
||||
error_log /dev/stderr;
|
||||
access_log /dev/stdout;
|
||||
|
||||
root /var/www/rene.jochums.at;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
user nginx;
|
||||
worker_processes 4;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 2048;
|
||||
multi_accept on;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
server_tokens off;
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 15;
|
||||
types_hash_max_size 2048;
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
access_log off;
|
||||
error_log off;
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
open_file_cache max=100;
|
||||
client_body_temp_path /tmp 1 2;
|
||||
client_body_buffer_size 256k;
|
||||
client_body_in_file_only off;
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
<div class="home_single_post comment_area">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8 col-xs-offset-0 col-sm-offset-2 col-md-offset-2 col-lg-offset-2">
|
||||
{{"<!-- begin comments //-->" | safeHTML}}
|
||||
<section id="isso-thread">
|
||||
|
||||
</section>
|
||||
{{"<!-- end comments //-->" | safeHTML}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,10 +1,3 @@
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-9ET9Z12C0F"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-9ET9Z12C0F');
|
||||
</script>
|
||||
<!-- EDN GTAG -->
|
||||
{{ "<!-- isso -->" | safeHTML }}
|
||||
<script data-isso="{{ .Site.BaseURL }}isso/" src="{{ .Site.BaseURL }}isso/js/embed.min.js"></script>
|
||||
{{ "<!-- end isso -->" | safeHTML }}
|
@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
podman build --rm --build-arg=URL=http://localhost -t homepage:latest .
|
||||
podman run --rm -p "80:80" homepage:latest
|
@ -0,0 +1 @@
|
||||
google-site-verification: googled7a55c2994884381.html
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/jo-micro/auth2 git https://git.jochum.dev/jo-micro/auth2.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/jo-micro/auth2'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/jo-micro/auth2">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/jo-micro/buncomponent git https://git.jochum.dev/jo-micro/buncomponent.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/jo-micro/buncomponent'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/jo-micro/buncomponent">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/jo-micro/components git https://git.jochum.dev/jo-micro/components.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/jo-micro/components'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/jo-micro/components">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/jo-micro/geoip git https://git.jochum.dev/jo-micro/geoip.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/jo-micro/geoip'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/jo-micro/geoip">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/jo-micro/logruscomponent git https://git.jochum.dev/jo-micro/logruscomponent.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/jo-micro/logruscomponent'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/jo-micro/logruscomponent">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/jo-micro/router git https://git.jochum.dev/jo-micro/router.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/jo-micro/router'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/jo-micro/router">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/jo-micro/settings git https://git.jochum.dev/jo-micro/settings.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/jo-micro/settings'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/jo-micro/settings">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/orb/config git https://git.jochum.dev/orb-org/config.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/orb-org/config'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/orb-org/config">project page</a>...
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="go-import" content="jochum.dev/orb/orb git https://git.jochum.dev/orb-org/orb.git">
|
||||
<meta http-equiv="refresh" content="0;URL='https://git.jochum.dev/orb-org/orb'">
|
||||
</head>
|
||||
<body>
|
||||
Redirecting you to the <a href="https://git.jochum.dev/orb-org/orb">project page</a>...
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
@ -1,51 +0,0 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
=WT8e
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -1 +0,0 @@
|
||||
node_modules/
|
@ -0,0 +1,9 @@
|
||||
[submodule "vendor/lightslider"]
|
||||
path = vendor/lightslider
|
||||
url = https://github.com/sachinchoolur/lightslider.git
|
||||
[submodule "vendor/lightGallery"]
|
||||
path = vendor/lightgallery
|
||||
url = https://github.com/sachinchoolur/lightGallery.git
|
||||
[submodule "vendor/fontawesome"]
|
||||
path = vendor/fontawesome
|
||||
url = https://github.com/FortAwesome/Font-Awesome.git
|
@ -1,41 +0,0 @@
|
||||
module.exports = function (grunt) {
|
||||
'use strict';
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
|
||||
grunt.initConfig({
|
||||
copy: {
|
||||
main: {
|
||||
files: [
|
||||
// jQuery
|
||||
{ expand: true, flatten: true, src: ['node_modules/jquery/dist/jquery.min.js'], dest: 'static/static/js/' },
|
||||
|
||||
// Bootstrap
|
||||
{ expand: true, flatten: true, src: ['node_modules/bootstrap/dist/css/bootstrap.min.css'], dest: 'static/static/css/' },
|
||||
{ expand: true, flatten: true, src: ['node_modules/bootstrap/dist/css/bootstrap-theme.min.css'], dest: 'static/static/css/' },
|
||||
{ expand: true, flatten: true, src: ['node_modules/bootstrap/dist/js/bootstrap.min.js'], dest: 'static/static/js/' },
|
||||
|
||||
// lightgallery
|
||||
{ expand: true, flatten: true, src: ['node_modules/lightgallery/dist/css/lightgallery.min.css'], dest: 'static/static/css/' },
|
||||
{ expand: true, flatten: true, src: ['node_modules/lightgallery/dist/js/lightgallery.min.js'], dest: 'static/static/js/' },
|
||||
{ expand: true, flatten: true, src: ['node_modules/lightgallery/dist/fonts/*'], dest: 'static/static/fonts/' },
|
||||
|
||||
// lightslider
|
||||
{ expand: true, flatten: true, src: ['node_modules/lightslider/dist/css/lightslider.min.css'], dest: 'static/static/css/' },
|
||||
{ expand: true, flatten: true, src: ['node_modules/lightslider/dist/js/lightslider.min.js'], dest: 'static/static/js/' },
|
||||
{ expand: true, flatten: true, src: ['node_modules/lightslider/dist/img/*'], dest: 'static/static/img/' },
|
||||
|
||||
// Fontawesome 4.x
|
||||
{ expand: true, flatten: true, src: ['node_modules/font-awesome/css/font-awesome.min.css'], dest: 'static/static/css/' },
|
||||
{ expand: true, flatten: true, src: ['node_modules/font-awesome/fonts/*'], dest: 'static/static/fonts/' },
|
||||
|
||||
// fortawesome
|
||||
// { expand: true, flatten: true, src: ['node_modules/@fortawesome/fontawesome-free/css/all.min.css'], dest: 'static/static/css/' },
|
||||
// { expand: true, flatten: true, src: ['node_modules/@fortawesome/fontawesome-free/webfonts/*'], dest: 'static/static/webfonts/' },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
grunt.registerTask('default', ['copy:main']);
|
||||
};
|
@ -1,16 +1,20 @@
|
||||
|
||||
<script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/thirdparties.js"></script>
|
||||
<script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/jquery.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/lightGallery.min.js"></script>
|
||||
<script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/lightslider.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/main.js"></script>
|
||||
|
||||
<!-- <script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/highlight.pack.min.js"></script> -->
|
||||
<!-- <script>
|
||||
<script type="text/javascript" src="{{ .Site.BaseURL }}/static/js/highlight.pack.js"></script>
|
||||
<script>
|
||||
hljs.configure({
|
||||
languages: ['bash', 'html']
|
||||
});
|
||||
hljs.initHighlightingOnLoad();
|
||||
</script> -->
|
||||
</script>
|
||||
{{ partial "analytics.html" }}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,14 +1,20 @@
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/bootstrap.min.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/bootstrap-theme.min.css" rel="stylesheet"/>
|
||||
|
||||
<link type="text/css" media="print" onload="this.onload=null;this.removeAttribute('media');" href="{{ .Site.BaseURL }}/static/css/persona-icons.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/persona-icons.css" rel="stylesheet"/>
|
||||
|
||||
<!-- Fonts ni style.css -->
|
||||
<link type="text/css" media="print" onload="this.onload=null;this.removeAttribute('media');" href="{{ .Site.BaseURL }}/static/css/font-awesome.min.css" rel="stylesheet" />
|
||||
<link type="text/css" media="print" onload="this.onload=null;this.removeAttribute('media');" href="{{ .Site.BaseURL }}/static/css/lato-font.css" rel="stylesheet"/>
|
||||
<link type="text/css" media="print" onload="this.onload=null;this.removeAttribute('media');" href="{{ .Site.BaseURL }}/static/css/notoserif-font.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/lato-font.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/notoserif-font.css" rel="stylesheet"/>
|
||||
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/style.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/responsive.css" rel="stylesheet"/>
|
||||
|
||||
<link type="text/css" media="print" onload="this.onload=null;this.removeAttribute('media');" href="{{ .Site.BaseURL }}/static/css/chromastyles-monokai.css" rel="stylesheet"/>
|
||||
<link type="text/css" href="{{ .Site.BaseURL }}/static/css/highlight/googlecode.css" rel="stylesheet"/>
|
||||
|
||||
<!-- start hugo lightslider -->
|
||||
<!-- you need this jquery include if you want lightslider without includejs="true". -->
|
||||
<script src="{{ $.Site.BaseURL }}/static/js/jquery.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{{ .Site.BaseURL }}/static/css/lightGallery.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{{ .Site.BaseURL }}/static/css/lightslider.css" />
|
||||
<!-- end hugo lightslider -->
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "@pcdummy/persona",
|
||||
"version": "1.0.0",
|
||||
"description": "Persona theme",
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.4.1",
|
||||
"font-awesome": "^4.7.0",
|
||||
"grunt": "^1.5.3",
|
||||
"grunt-contrib-copy": "^1.0.0",
|
||||
"jquery": "^3.6.1",
|
||||
"lightgallery": "^2.6.0",
|
||||
"lightslider": "^1.1.6"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
../../../vendor/bootstrap/css/bootstrap-theme.min.css
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
../../../vendor/bootstrap/css/bootstrap.min.css
|
@ -1,85 +0,0 @@
|
||||
/* Background */ .bg { color: #f8f8f2; background-color: #272822; }
|
||||
/* PreWrapper */ .chroma { color: #f8f8f2; background-color: #272822; }
|
||||
/* Other */ .chroma .x { }
|
||||
/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
|
||||
/* CodeLine */ .chroma .cl { }
|
||||
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
|
||||
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
|
||||
/* LineHighlight */ .chroma .hl { background-color: #ffffcc }
|
||||
/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||
/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||
/* Line */ .chroma .line { display: flex; }
|
||||
/* Keyword */ .chroma .k { color: #66d9ef }
|
||||
/* KeywordConstant */ .chroma .kc { color: #66d9ef }
|
||||
/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
|
||||
/* KeywordNamespace */ .chroma .kn { color: #f92672 }
|
||||
/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
|
||||
/* KeywordReserved */ .chroma .kr { color: #66d9ef }
|
||||
/* KeywordType */ .chroma .kt { color: #66d9ef }
|
||||
/* Name */ .chroma .n { }
|
||||
/* NameAttribute */ .chroma .na { color: #a6e22e }
|
||||
/* NameBuiltin */ .chroma .nb { }
|
||||
/* NameBuiltinPseudo */ .chroma .bp { }
|
||||
/* NameClass */ .chroma .nc { color: #a6e22e }
|
||||
/* NameConstant */ .chroma .no { color: #66d9ef }
|
||||
/* NameDecorator */ .chroma .nd { color: #a6e22e }
|
||||
/* NameEntity */ .chroma .ni { }
|
||||
/* NameException */ .chroma .ne { color: #a6e22e }
|
||||
/* NameFunction */ .chroma .nf { color: #a6e22e }
|
||||
/* NameFunctionMagic */ .chroma .fm { }
|
||||
/* NameLabel */ .chroma .nl { }
|
||||
/* NameNamespace */ .chroma .nn { }
|
||||
/* NameOther */ .chroma .nx { color: #a6e22e }
|
||||
/* NameProperty */ .chroma .py { }
|
||||
/* NameTag */ .chroma .nt { color: #f92672 }
|
||||
/* NameVariable */ .chroma .nv { }
|
||||
/* NameVariableClass */ .chroma .vc { }
|
||||
/* NameVariableGlobal */ .chroma .vg { }
|
||||
/* NameVariableInstance */ .chroma .vi { }
|
||||
/* NameVariableMagic */ .chroma .vm { }
|
||||
/* Literal */ .chroma .l { color: #ae81ff }
|
||||
/* LiteralDate */ .chroma .ld { color: #e6db74 }
|
||||
/* LiteralString */ .chroma .s { color: #e6db74 }
|
||||
/* LiteralStringAffix */ .chroma .sa { color: #e6db74 }
|
||||
/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 }
|
||||
/* LiteralStringChar */ .chroma .sc { color: #e6db74 }
|
||||
/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 }
|
||||
/* LiteralStringDoc */ .chroma .sd { color: #e6db74 }
|
||||
/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 }
|
||||
/* LiteralStringEscape */ .chroma .se { color: #ae81ff }
|
||||
/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 }
|
||||
/* LiteralStringInterpol */ .chroma .si { color: #e6db74 }
|
||||
/* LiteralStringOther */ .chroma .sx { color: #e6db74 }
|
||||
/* LiteralStringRegex */ .chroma .sr { color: #e6db74 }
|
||||
/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 }
|
||||
/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 }
|
||||
/* LiteralNumber */ .chroma .m { color: #ae81ff }
|
||||
/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
|
||||
/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
|
||||
/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
|
||||
/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
|
||||
/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
|
||||
/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
|
||||
/* Operator */ .chroma .o { color: #f92672 }
|
||||
/* OperatorWord */ .chroma .ow { color: #f92672 }
|
||||
/* Punctuation */ .chroma .p { }
|
||||
/* Comment */ .chroma .c { color: #75715e }
|
||||
/* CommentHashbang */ .chroma .ch { color: #75715e }
|
||||
/* CommentMultiline */ .chroma .cm { color: #75715e }
|
||||
/* CommentSingle */ .chroma .c1 { color: #75715e }
|
||||
/* CommentSpecial */ .chroma .cs { color: #75715e }
|
||||
/* CommentPreproc */ .chroma .cp { color: #75715e }
|
||||
/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
|
||||
/* Generic */ .chroma .g { }
|
||||
/* GenericDeleted */ .chroma .gd { color: #f92672 }
|
||||
/* GenericEmph */ .chroma .ge { font-style: italic }
|
||||
/* GenericError */ .chroma .gr { }
|
||||
/* GenericHeading */ .chroma .gh { }
|
||||
/* GenericInserted */ .chroma .gi { color: #a6e22e }
|
||||
/* GenericOutput */ .chroma .go { }
|
||||
/* GenericPrompt */ .chroma .gp { }
|
||||
/* GenericStrong */ .chroma .gs { font-weight: bold }
|
||||
/* GenericSubheading */ .chroma .gu { color: #75715e }
|
||||
/* GenericTraceback */ .chroma .gt { }
|
||||
/* GenericUnderline */ .chroma .gl { }
|
||||
/* TextWhitespace */ .chroma .w { }
|
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/arta.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/ascetic.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/atelier-dune.dark.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/atelier-dune.light.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/atelier-forest.dark.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/atelier-forest.light.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/atelier-heath.dark.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/atelier-heath.light.css
|
@ -0,0 +1 @@
|
||||
../../../../vendor/highlightjs/styles/atelier-lakeside.dark.css
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue