k3s 踩坑

Kubernetes 之前很火, 但是因为对每个计算节点的配置要求非常之高, 我一直是在使用 Rancher 的

Cattle
而并非使用 k8s. 最近听说 Rancher Labs 推出了 k3s 并且据说资源占用特别小, 那就来踩下坑(跑

开始安装

已知问题

因为默认的 Ingress

traefik
有问题, 在 Rancher 2 上添加 Load Balancing 会导致这个 Ingress 一直
Initializing
, 但是实际上是能用的.

Issue in Rancher #19135

同时因为

traefik
很多奇怪的问题, 我将会将其换为
nginx-ingress

因为我是要跨平台组集群 而

flannel
在各个节点之间的通信是不加密的, 此处不使用
flannel
作为 cni 驱动, 我将其换为了
calico
zerotier
的组合.

此处主控和节点已经处在同一个 zerotier 网络中

安装主控端

curl -sfL https://get.k3s.io -o k3s.sh
chmod +x k3s.sh

bash k3s.sh --no-deploy traefik --no-flannel

rm -f k3s.sh

修改主控端 node ip

如果你和我一样是使用的 zerotier 或者 wireguard 等软件搭建的内网, 需要修改 server 和 agent 的启动参数

在运行安装脚本的时候加入

--node-ip 内网IP --flannel-iface 网卡

安装 calico

curl https://docs.projectcalico.org/v3.6/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml -O
sed -i -e "s?192.168.0.0/16?10.42.0.0/16?g" calico.yaml
kubectl apply -f calico.yaml

安装被控端

curl -sfL https://get.k3s.io -o k3s.sh
chmod +x k3s.sh

K3S_URL=https://SERVER_IP:6443 \
    K3S_TOKEN=TOKEN \
    bash k3s.sh --no-flannel

rm -f k3s.sh

Token 可以从主控端的

/var/lib/rancher/k3s/server/node-token
中获得

使用

安装 Nginx-Ingress

apply 这个 yml

apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: nginx-ingress
  namespace: kube-system
spec:
  chart: stable/nginx-ingress
  valuesContent: |-
    controller:
      kind: DaemonSet
      daemonset:
        useHostPort: true
      nodeSelector:
        nodeRole: nginx-edge
      service:
        type: ClusterIP

注意要给能 nginx 访问的 node 打上

nodeRole=nginx-edge
的 label

安装 cert-manager

此处例子为配置 DNS 解析, 使用 DigitalOcean 更多操作请查看 官方文档

因为 k3s 的问题, 我们需要部署不带 webhook 的 cert-manager

Issue in cert-manager #1519 Issue in k3s #117 Issue in k3s #120

kubectl create namespace cert-manager
kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true

kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7/deploy/manifests/cert-manager-no-webhook.yaml

然后这时候 cert-manager 就安装完了 新建一个 Screct 存一下 DO 的 API Key

echo "TOKEN" > token
kubectl create secret generic digitalocean-dns-api --namespace=cert-manager --from-file=token

然后创建一个 Issuer

apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: digitalocean-issuer
  namespace: kube-system
spec:
  acme:
    email: [email protected]
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: acme-issuer-account-key
    dns01:
      providers:
      - name: digitalocean-acme
        digitalocean:
          tokenSecretRef:
            name: digitalocean-dns-api
            key: token

然后我们就能签下来我们的证书了

apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: dashboard-cert
  namespace: kube-system
spec:
  secretName: dashboard-cert-tls
  issuerRef:
    name: digitalocean-issuer
    kind: ClusterIssuer
  commonName: 'kubernetes.bogon.dev'
  dnsNames:
  - kubernetes.bogon.dev
  acme:
    config:
    - dns01:
        provider: digitalocean-acme
      domains:
      - kubernetes.bogon.dev

安装 kubernetes dashboard

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml

然后我们需要建立一个能够 Cluster Role view 的账户

apiVersion: v1
kind: ServiceAccount
metadata:
  name: kubedash
  namespace: kube-system
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: dashboard
subjects:
  - kind: ServiceAccount
    name: kubedash
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

然后使用

kubectl apply -f
进行导入并使用

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep kubedash | awk '{print $1}')

获取到账户的 Token

为了能进行外网访问, 我们还需要建立一个 Ingress 来将其开放到公网

创建一个 TLS 证书类型的 secret

如果你使用 cert-manager 创建证书的话, 可以跳过此步

kubectl create secret tls dashboard-cert-tls --key k8s.key --cert k8s.crt --namespace=kube-system
# 证书哪里拿我应该不用讲了

创建 Ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-ingress
  namespace: kube-system
  annotations:
    ingress.kubernetes.io/protocol: https
    nginx.ingress.kubernetes.io/backend-protocol: https

spec:
  rules:
  - host: kubernetes.bogon.dev
    http:
      paths:
        - backend:
            serviceName: kubernetes-dashboard
            servicePort: 443
  tls:
  - hosts:
    - kubernetes.bogon.dev
    secretName: dashboard-cert-tls

然后我们就完成了最简单的一部分 k8s dashboard

踩坑

Dind 中创建出来的容器 MTU 不正常

这个其实不是 k3s 的锅, 事实上你就算是 k8s 也有这个问题

这个问题困扰了我很久, 最后我在 Drone 的论坛中发现了一个解决方法

在创建 dind 的容器时修改 iptables 开启

clamp-mss-to-pmtu
就可以解决因为网卡 MTU 大小导致 Docker 的 Bridge 发不出去包的问题

就算你给 docker daemon 或者修改了 config 改掉了 docker 的网卡 MTU, 创建出来容器中的 bridge 网卡 MTU 还是 1500

- name: drone-dind
  image: "docker:17.12.0-ce-dind"
  imagePullPolicy: IfNotPresent
  command: ["/bin/sh"]
  args:
    - "-c"
    - "iptables -N DOCKER-USER; iptables -I DOCKER-USER -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu; dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375"

修改 Calico 网卡的 MTU

有时候因为用了虚拟网卡, 需要修改 calico 网卡的 MTU

kubectl edit cm -n kube-system calico-config# 修改 veth_mtu 的值kubectl delete pod $(kubectl get pod -n kube-system | grep calico-node | awk '{print $1}') -n kube-system

ChangeLog

  • 2019/8/14: 添加了 修改主控端 node ip 部分, 修改了 HelmCharts 的 apiVersion, 对应 Release v0.6.1