目录

小服务器部署 K3s 并部署微服务实践

一、为什么是 K3s

我有一台 2C2G 的云服务器,跑一些个人项目。想用来学习一下 k8s,顺便把我跑的那些项目都迁到 k8s 上,但是看了一下性能要求,估计是撑不住,后来我发现了一个叫 k3s 的东西。

k3s 是 Rancher 推出的轻量级 Kubernetes 发行版,我理解就是 low 一些的 k8s,保留了核心功能,性能消耗会低很多。虽然感觉还是有点压力,因为它还要求要关闭虚拟内存,但跑通应该问题不大。

二、名词学习:Pod、Service、Namespace

虽然 K3s 兼容 Kubernetes API,但我之前对 k8s 本身也只停留在"知道是管容器的"这个层面。借着这个机会,认真学了一遍核心概念。

2.1 Namespace:逻辑隔离

Namespace 是 Kubernetes 里最基础的隔离单元。一个集群里可以划分多个 Namespace,资源互不干扰。

# 创建 namespace
kubectl create namespace simpfamily

挺好理解的,可以用 Namespace 按环境(dev / prod)或按项目拆分,执行命令例如kubectl get pods 的时候加上 -n 参数来表示指定某个 Namespace 下的东西进行操作。

apiVersion: v1
kind: Namespace
metadata:
  name: simpfamily

2.2 Pod:最小部署单元

Pod 是 Kubernetes 里能调度的最小单元。对于 docker-compose,每个容器直接裸跑;在 k8s 里,一个 Pod 可以包含一个或多个容器,共享网络和存储。

但实际很少直接创建 Pod,而是用 Deployment 来管理,因为 Deployment 提供了自愈、扩缩容、滚动更新等核心能力:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simpfamily-ledger
  namespace: simpfamily
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simpfamily-ledger
  template:
    metadata:
      labels:
        app: simpfamily-ledger
    spec:
      containers:
      - name: ledger
        image: xxxx
        ports:
        - containerPort: 8080
        env:
        - name: DB_URL
          value: "mysql://..."

例如通过调整 replicas,就可以控制服务扩缩容,非常方便。如果某个 pod 挂了,也会自动重启,直至有 replicas 数量的 pod。

2.3 Service:稳定的网络入口

Pod 是会飘的——滚动更新、节点故障、手动删除重建,Pod 的 IP 都会变。Service 就是给一组 Pod 提供一个稳定的虚拟 IP 和 DNS 名称。

apiVersion: v1
kind: Service
metadata:
  name: simpfamily-ledger-svc
  namespace: simpfamily
spec:
  selector:
    app: simpfamily-ledger
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

Service 通过 selector 匹配 pod 的 labels,流量自动转发到对应的 pod。其他服务只需要访问 simpfamily-ledger-svc 就行,不用关心 pod 在哪里。

三、编写配置文件

有了上面的基础,我开始写一个完整的微服务部署配置。

3.1 ConfigMap 和 Secret

配置和敏感信息需要分开管理。非敏感配置用 ConfigMap,密码、证书用 Secret:

apiVersion: v1
kind: ConfigMap
metadata:
  name: simpfamily-config
  namespace: simpfamily
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  JAEGER_ENDPOINT: "http://jaeger-collector.monitoring:14268/api/traces"
---
apiVersion: v1
kind: Secret
metadata:
  name: simpfamily-secret
  namespace: simpfamily
type: Opaque
stringData:
  DB_PASSWORD: "password"
  JWT_SECRET: "jwt-secret"

3.2 Ingress:对外暴露服务

要让外部访问服务,需要 Ingress。K3s 自带 Traefik 作为默认 Ingress Controller:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simpfamily-ingress
  namespace: simpfamily
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - api.simplife.tech
    secretName: simpfamily-tls
  rules:
  - host: api.simplife.tech
    http:
      paths:
      - path: /ledger
        pathType: Prefix
        backend:
          service:
            name: simpfamily-ledger-svc
            port:
              number: 80

四、部署 HTTPS 证书

证书我用了 cert-manager,它会在集群内自动申请和续期 Let’s Encrypt 证书。

配置 ClusterIssuer:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-account-key
    solvers:
    - http01:
        ingress:
          class: traefik

搞定之后,Ingress 的 tls 段会自动触发 cert-manager 去申请证书。证书生成后存在 Secret 里,Traefik 会自动使用。

# 查看证书状态
$ kubectl get certificate -n simpfamily
NAME       READY   SECRET     AGE
simpfamily-tls   True    simpfamily-tls   12h

整个过程全自动,到期前 cert-manager 会自动续期。比自己手动跑 certbot、挂 cron 任务省心太多了。

五、微服务上线

部署完成后,通过 Ingress 域名访问,服务正常运行。

期间遇到了内存打满的情况,重启服务器才解决,且多次出现,刚开始时候占用内存1.4G,过个几周就会撑爆,后面查到有人也出现过这个问题,是有个东西有 bug 要升级一下,忘了当时升级的啥了,总之是解决了。