在K8S集群中创建clash代理服务

学习下ConfigMap、Deployment、Service的用法。

准备文件

首先准备创建docker镜像的文件,有如下这些。可以注意到,没有在docker镜像中放置config.yaml。这将通过config.map挂载到pod中。

$ tree
.
├── clash-linux-amd64-2022.11.25 # clash可执行文件
├── Country.mmdb # GEOIP规则用到的mmdb文件
└── Dockerfile

Dockerfile内容如下:

FROM alpine:3.18.2
COPY ./ /

制作镜像并推送到docker hub

podman build  -t clash -f Dockerfile . --tag docker.io/arloor/clash:1.0
podman login docker.io # 输入账号密码登陆docker hub
podman push docker.io/arloor/clash:1.0

创建manifest

cat > cm-clash-conf.yaml <<EOF
# config.yaml
apiVersion: v1
data:
  config.yaml: |
    port: 3128 
    # 绑定0.0.0.0
    allow-lan: true 
    
    proxies:
    - name: "bwg"
      type: http
      server: xxxx
      port: 443
      username: xxxx
      password: xxxxxx
      tls: true 
      skip-cert-verify: true
    
    rules:
    - IP-CIDR,192.168.0.0/16,DIRECT
    - IP-CIDR,10.0.0.0/8,DIRECT
    - IP-CIDR,172.16.0.0/12,DIRECT
    - GEOIP,CN,DIRECT
    - MATCH,bwg
kind: ConfigMap
metadata:
  name: clash-conf
  namespace: default

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: clash
  namespace: default
  labels:
    k8s-app: clash
spec:
  selector:
    matchLabels:
      k8s-app: clash
  template:
    metadata:
      labels:
        k8s-app: clash
    spec:
      tolerations:
      # 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
      # 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: clash
        image: docker.io/arloor/clash:1.0
        command: [ "/clash-linux-amd64-2022.11.25","-d","/","-f","/etc/clash/config.yaml" ]
        ports:
          - containerPort: 3128 #指定容器ip
            protocol: TCP
        resources:
          limits:
            memory: 100Mi
          requests:
            cpu: 50m
            memory: 50Mi
        volumeMounts:
        - mountPath: "/etc/clash"   #容器挂载的目录(空的)
          name: config   
      terminationGracePeriodSeconds: 30
      volumes:
      - configMap:
          name: clash-conf          #指定使用ConfigMap的名称
        name: config 

---

kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: clash
  name: clash
  namespace: default
spec:
  ports:
    - port: 3128
      targetPort: 3128
      protocol: TCP
  selector:
    k8s-app: clash
  type: ClusterIP

EOF
kubectl apply -f cm-clash-conf.yaml

其中ConfigMap对应的就是config.yaml的内容,其中采用了yaml的多行文本表示形式。(可以在vim中按control + v 进入块模式,然后shift + i在头部批量增加空格)。也可以使用如下命令从文件直接创建的:

kubectl create cm clash-conf --from-file=config.yaml 
kubectl get cm clash-conf  -o yaml

config.yaml内容:

port: 3128 
# 绑定0.0.0.0
allow-lan: true 

proxies:
- name: "xxx"
  type: http
  server: xxx
  port: xx
  username: xx
  password: xxx
  tls: true 
  skip-cert-verify: true

rules:
- IP-CIDR,192.168.0.0/16,DIRECT
- IP-CIDR,10.0.0.0/8,DIRECT
- IP-CIDR,172.16.0.0/12,DIRECT
- GEOIP,CN,DIRECT
- MATCH,xxx

另外在过程中遇到一点问题是Service写的不对,没有和deployment成功关联,主要是.spec.selector那里没写对,后面直接用expose来创建service了。而且刚好只需要ClusterIp类型的Service即可,expose刚刚好。

后面又学习了下,Deployment的 spec.selector.matchLabels 下的labels要和Service的.spec.selector下的labels一致。 这也可以形成个最佳实践:如无必要,勿增实体,Service和Deployment的Name保持一致,然后给Deployment增加一个label:固定为 k8s-app: ${name},然后Service的selector就填这个k8s-app: ${name}

kubectl expose deployment/clash

测试

我们开一个curl的pod来测试下

kubectl run curl --image=redhat/ubi9-minimal:9.2-717 --command --attach --rm -- \
curl https://google.com --proxy http://clash.default:3128
# 也可以 kubectl run curl --image=radial/busyboxplus:curl -it --rm来创建pod,然后在pod的bash中执行curl命令