跳至内容
玩弄服务器的一些心得

玩弄服务器的一些心得

服务器选择

  • 云厂商:国外的基本得要信用卡,可以直接Pass。国内阿里云份额最大,但是不同大厂的云服务对于普通人来说区别不大,可以看价格下菜,但是选小厂的要慎重。
  • CPU:至少两个核心。一个核心运维都难,服务和你的ssh、vscode server抢饭吃。
  • 内存:至少2GB;4GB是舒适区。如果没有Java服务还行,一旦有Java就洗洗睡吧。内存少的服务器只能用go和rust,jvm和node的服务完全用不了。只要自己亲自部署过服务的就知道go有多香了,jvm根本不敢用。
  • 网络:确信流量不大或是不怕DDoS的可以选择流量计费,否则选择带宽。
  • 地域:目前来看香港还是最优解,不仅不用备案,而且访问Docker Hub、GitHub、Huggingface等不需要代理,甚至还能搞proxy server。

操作系统选择

服务器端不是Debian系就是RHEL系。

免费的里面Debian和AlmaLinux最为突出(RockyLinux的作者风评不好)。喜欢RHEL(保守派)的选AlmaLinux,但是dnf管理器更新软件包比apt慢得多,有时候软件包更新还要看红帽脸色。Debian在生态上优势极为明显,Docker都选择了debian作为Docker Hardened Image。

Note

实际上CentOS Stream不是不能用,只是给人们带来的心理落差太大了,就让人感觉很危险。如果对安全没有特别严重的洁癖,担心这个不如担心其他地方的安全措施有没有做好。实际上它甚至还能比RHEL更早拿到安全补丁,而且上游还有Fedora兜底

准备工作

ssh

不建议用密码,建议用密钥。sshd_config里启用公钥登录,还可以顺带禁用密码登录。

最佳的公私钥算法是ed25519(可以用ssh-keygen -t ed25519生成)。剩下的操作就略过了,教程极其多。

Docker

Docker的优点不只是reproducible。服务的配置文件、生命周期、网络(端口)、日志、更新、CVE等等全都可以由Docker包办。

安装方式官方文档写得很详细:https://docs.docker.com/engine/install/

如果默认不是root用户,可以考虑把普通用户加入docker组。

使用docker

不管是一个服务还是多个服务,建议全都使用Docker Compose配置。命令行的局限性太大,配置文件更好维护。

可以像我一样专门搞一个文件夹整理配置文件:

        • Caddyfile
      • docker-compose.yml
      • Dockerfile
      • .env
      • reload.sh
        • app.conf
      • docker-compose.yml
      • docker-compose.yml
      • .env
      • docker-compose.yml
      • s3.config.json

具体配置举例:

  • 能写清楚major版本号的写清楚,便于无缝更新;不能的先看看有没有stable等版本,最后选择latest。后续不要轻易更新服务,不然很可能导致服务崩溃(如果是升级底层服务还会产生连锁反应,把其他服务搞崩溃)。因此我没有写 pull_policy: always。严格遵循SemVer的可以大胆更新。
  • 建议使用 .env,不要直接把环境变量写在compose文件里
  • 配置文件统一用本地目录挂载;否则尽量使用docker的volume管理。可以通过 docker volume inspect <id> 查看宿主机实际的存储路径。
  • 尽可能使用桥接网络和docker自带的ip解析功能(容器名自动解析为具体ip)。虽然host模式很方便,但是容器端口很容易冲突,而且如果防火墙配置不当很危险。生产者创建一个独立的桥接网络,消费者加入生产者的网络后使用容器名访问。所以除了反向代理服务之外尽可能避免暴露端口到宿主机。
docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
services:
  caddy2:
    build: .
    image: caddy2-custom

    container_name: caddy2
    restart: unless-stopped
    env_file: .env

    cap_add:
      - NET_ADMIN
    networks:
      - postgres18
      - seaweedfs
      - casdoor
      - openlist
      - memos
    ports:
      - 80:80
      - 443:443
      - 443:443/udp
      - 5432:5432

    volumes:
      - ./conf:/etc/caddy
      - ./static:/static
      - data:/data
      - config:/config

networks:
  postgres18:
    external: true
  seaweedfs:
    external: true
  casdoor:
    external: true
  openlist:
    external: true
  memos:
    external: true

volumes:
  data:
    name: caddy2-data
  config:
    name: caddy2-config

基础服务选择

最主要的基础服务就是反向代理(外界访问)、数据库(存数据)、对象存储(帮助数据库存文件)、身份认证(不必多言)。

反向代理

很多人第一反应肯定是Nginx,非常老牌的web服务器。但是在caddy面前Nginx的DX(Developer Experience)就太糟糕了。Traefik、HAProxy等反向代理过于高级,只适合极端性能的场景使用;Kong等网关类产品不适合个人服务器使用。

Caddy使用插件需要独立构建一个caddy执行文件:

Dockerfile
1
2
3
4
5
6
7
8
9
FROM caddy:2-builder AS builder

RUN xcaddy build \
    --with github.com/mholt/caddy-l4 \
    --with github.com/caddy-dns/alidns

FROM caddy:2-alpine

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

Caddyfile举例:

Caddyfile
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
	acme_dns alidns { # 自动用阿里云DNS配置https
		access_key_id {env.ALIYUN_ACCESS_KEY_ID}
		access_key_secret {env.ALIYUN_ACCESS_KEY_SECRET}
	}

	layer4 { # TCP代理数据库连接,统一用caddy更安全
		:5432 {
			route {
				proxy postgres18:5432
			}
		}
	}
}

mioyi.net { # 自动跳转到www
	redir https://www.mioyi.net
}

*.mioyi.net {
	encode # 自动zstd, gzip压缩

	@www host www.mioyi.net
	handle @www {
		reverse_proxy memos:8080 # 反向代理
	}

	@static host static.mioyi.net
	handle @static { # 静态文件托管
		root * /static
		file_server browse
	}

	@s3 host s3.mioyi.net
	handle @s3 {
		reverse_proxy seaweedfs-s3:8333
	}

	@auth host auth.mioyi.net
	handle @auth {
		reverse_proxy casdoor:8000
	}

	@oplist host oplist.mioyi.net
	handle @oplist {
		reverse_proxy openlist:5244
	}
}

*.s3.mioyi.net {
	encode

	rewrite * /{http.request.host.labels.3}{uri} # 自动重写Virtual Host风格的链接

	reverse_proxy seaweedfs-s3:8333 {
		flush_interval -1
	}
}

数据库

PostgreSQL是开源数据库里的佼佼者,无须多言。

创建用户+数据库教程:

1
2
create user casdoor with password '123456';
create database casdoor with owner casdoor;

对象存储

自从MinIO作恶之后,好用的对象存储似乎就没了。目前社区关注比较高的是:

  • GarageHQ:只支持CLI管理;当下版本还不支持anonymous的文件访问。
  • SeaweedFS:分布式存储,但是既支持anonymous访问也支持细粒度的鉴权。Docker Hardened Image中唯一的对象存储,足够可信。
  • RustFS:新星,使用体验不错,但是有很多负面评价,成熟度不够
  • Ceph:分布式存储。(我暂未尝试过)

目前我的选择是SeaweedFS。(参阅另一篇博客

Note

WebDAV是可选项,可以套一个OpenList(AList后继)作为前端管理文件

docker-compose.yml
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
# https://github.com/seaweedfs/seaweedfs/blob/master/docker/seaweedfs-compose.yml
# https://github.com/seaweedfs/seaweedfs/wiki/Production-Setup
services:
  master:
    image: chrislusf/seaweedfs:latest

    container_name: seaweedfs-master
    restart: unless-stopped
    # -defaultReplication=000: https://github.com/seaweedfs/seaweedfs/wiki/Replication
    # -volumeSizeLimitMB=64: https://github.com/seaweedfs/seaweedfs/wiki/Production-Setup
    command: master -ip=seaweedfs-master -ip.bind=0.0.0.0 -defaultReplication=000 -volumeSizeLimitMB=64

    networks:
      - seaweedfs

    volumes:
      - master:/data

  volume:
    image: chrislusf/seaweedfs:latest

    container_name: seaweedfs-volume
    restart: unless-stopped
    # -index=leveldb: https://github.com/seaweedfs/seaweedfs/wiki/Optimization
    # -max=0: https://github.com/seaweedfs/seaweedfs/wiki/Production-Setup
    # https://github.com/seaweedfs/seaweedfs/wiki/S3-API-FAQ#can-not-upload-due-to-no-free-volumes-left
    command: volume -ip=seaweedfs-volume -ip.bind=0.0.0.0 -master="seaweedfs-master:9333" -index=leveldb -max=0
    depends_on:
      - master

    networks:
      - seaweedfs

    volumes:
      - volume:/data

  filer:
    image: chrislusf/seaweedfs:latest

    container_name: seaweedfs-filer
    restart: unless-stopped
    command: filer -ip=seaweedfs-filer -ip.bind=0.0.0.0 -master="seaweedfs-master:9333"
    tty: true
    stdin_open: true
    depends_on:
      - master
      - volume

    networks:
      - seaweedfs

    volumes:
      - filer:/data

  s3:
    image: chrislusf/seaweedfs:latest

    container_name: seaweedfs-s3
    restart: unless-stopped
    # https://github.com/seaweedfs/seaweedfs/wiki/Amazon-S3-API
    # https://github.com/seaweedfs/seaweedfs/wiki/S3-API-FAQ
    command: s3 -ip.bind=0.0.0.0 -filer="seaweedfs-filer:8888" -config=/s3.config.json -domainName="s3.mioyi.net"
    depends_on:
      - master
      - volume
      - filer

    networks:
      - seaweedfs

    # https://github.com/seaweedfs/seaweedfs/blob/master/docker/compose/s3.json
    volumes:
      - ./s3.config.json:/s3.config.json
      - s3:/data

  webdav:
    image: chrislusf/seaweedfs:latest

    container_name: seaweedfs-webdav
    restart: unless-stopped
    command: webdav -filer="seaweedfs-filer:8888"
    depends_on:
      - master
      - volume
      - filer

    networks:
      - seaweedfs

    volumes:
      - webdav:/data

networks:
  seaweedfs:
    name: seaweedfs

volumes:
  master:
    name: seaweedfs-master
  volume:
    name: seaweedfs-volume
  filer:
    name: seaweedfs-filer
  s3:
    name: seaweedfs-s3
  webdav:
    name: seaweedfs-webdav

S3配置文件:

Note

既可以配置anonymous权限,也支持细分到具体操作和Bucket的权限。

s3.config.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
  "identities": [
    {
      "name": "anonymous",
      "actions": ["Read"]
    },
    {
      "name": "admin",
      "credentials": [
        {
          "accessKey": "███",
          "secretKey": "██████"
        }
      ],
      "actions": ["Admin", "Read", "Write", "List", "Tagging"]
    },
    {
      "name": "casdoor",
      "credentials": [
        {
          "accessKey": "███",
          "secretKey": "██████"
        }
      ],
      "actions": ["Read:casdoor", "Write:casdoor", "List:casdoor", "Tagging:casdoor"]
    },
    {
      "name": "memos",
      "credentials": [
        {
          "accessKey": "███",
          "secretKey": "██████"
        }
      ],
      "actions": ["Read:memos", "Write:memos", "List:memos", "Tagging:memos"]
    }
  ]
}

Caddy代理S3 API(支持Virtual Host Style)

Note

DNS需要配置*.s3.mioyi.net的泛域名解析,光*.mioyi.net不够

Caddyfile
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# https://github.com/seaweedfs/seaweedfs/wiki/S3-Nginx-Proxy
# https://github.com/seaweedfs/seaweedfs/wiki/S3-API-FAQ#s3-authentication-fails-when-using-reverse-proxy
*.s3.mioyi.net {
	reverse_proxy seaweedfs-s3:8333 {
		flush_interval -1

		# https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#defaults
		# https://caddyserver.com/docs/json/apps/http/#docs
		header_up X-Forwarded-Port {port}
		header_up -Connection
	}
}

Note

SeaweedFS主要是为了分布式+大量小文件存储设计的,因此在volume的创建上非常激进,会创建大量并发写和容灾副本,还会预分配,对于硬盘小的个人服务器来说非常紧张。因此master必须配置 -defaultReplication=000 -volumeSizeLimitMB=64,同时volume容器也要配置-max=0,让它能够自动无限制扩容。

参阅 https://github.com/seaweedfs/seaweedfs/wiki/Replication https://github.com/seaweedfs/seaweedfs/wiki/Optimization

身份认证

casdoor的UX(User Experience)非常好(至少比Keycloak和Authentik好),也有中文支持。配置单点登录后可以自动登录你部署的所有App。具体配置篇幅较大,略。

最后更新于