0%

MAC安装

1
2
3
pip3 install scrapy #安装scrapy
scrapy startproject testSpider #创建工程
scrapy crawl name #运行

kk多节点安装

准备

  1. 准备一个弹性伸缩,用于管理ECS虚拟机

  2. 伸缩组实例配置好ecs的配置(如果是学习可以采用抢占式虚拟机节约成本)

    Eg: ecs.hfc6.large(ecs.c7a.largeamd也可以了)抢占式2vCPU+4GiB+centos7.9 64为,可以挂载同一个共享数据盘,用于存储配置数据

    配置证书,采用证书cer登录

  3. 端口要求:安全组开放(因为用的一个安全组,所以组内连通策略:组内互通)因此不需要设置

    服务 端口
    ssh 22 TCP
    etcd 2379-2380 TCP
    apiserver 6443 TCP
    calico 9099-9100 TCP
    bgp 179 TCP
    nodeport 30000-32767 TCP
    master 10250-10258 TCP
    dns 53 TCP/UDP
    local-registry(离线环境需要) 5000 TCP
    local-apt(离线环境需要) 5080 TCP
    rpcbind( 使用 NFS 时需要) 111 TCP
    ipip(Calico 需要使用 IPIP 协议) IPENCAP / IPIP
    metrics-server 8443 TCP

只安装kubernetes

1
2
3
4
5
6
7
8
9
10
11
yum update -y
yum install -y socat conntrack ebtables ipset
export KKZONE=cn
#手动下载KK,上传阿里云(命令下载不下来)
./kk create config #生产配置文件config-sample.yaml
#修改config-sample.yaml,配置节点信息,密码采用证书登录模式,配置address
#上传证书,注意将证书改成400只读权限
chmod 400 k8s.cer
export KKZONE=cn
#创建kubernetes集群
./kk create cluster -f config-kubernetes-1.23.7.yaml | tee kk.log

安装kubernetes+kubesphere

1
2
3
yum update -y
./kk create config --with-kubesphere #生产配置文件config-sample.yaml
export KKZONE=cn && ./kk create cluster -f config-kubesphere3.3.0-kubernetes1.23.7.yaml | tee kk.log

添加/删除节点

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
#没有之前的部署文件,通过下面命令生产部署文件,文件名sample.yaml,我修改了名字为add-node.yaml
./kk create config --from-cluster
#修改,完善部署文件(主要是节点ip,负载,etcd),添加新节点的配置
#新节点安装依赖
yum install -y socat conntrack ebtables ipset
#执行添加节点
./kk add nodes -f add-node.yaml
#删除节点名是node4的节点
./kk delete node node4 -f add-node.yaml
#高可用
#1. 删除了node3虚拟机
#2. 添加了一个node4虚拟机
#3. kk的add-node.yaml节点信息修改为node1,node2,node4,(node3的配置删除)注意etcd和master要同步修改最新的节点名称
#4. 删除etcd的错误节点
export ETCDCTL_API=2;
export ETCDCTL_CERT_FILE='/etc/ssl/etcd/ssl/admin-node1.pem';
export ETCDCTL_KEY_FILE='/etc/ssl/etcd/ssl/admin-node1-key.pem';
export ETCDCTL_CA_FILE='/etc/ssl/etcd/ssl/ca.pem';
#查看成员
/usr/local/bin/etcdctl --endpoints=https://192.168.14.16:2379,https://192.168.14.17:2379 member list
#根据成员id,删除成员
/usr/local/bin/etcdctl --endpoints=https://192.168.14.16:2379,https://192.168.14.17:2379 member remove cab52e8fded2eefe
#5. 重新执行添加节点命令
./kk add nodes -f add-node.yaml
#6. 清理不可用的节点,因为连接不上,加上参数--delete-emptydir-data --ignore-daemonsets
kubectl cordon node3
kubectl drain node3 --force --ignore-daemonsets --delete-emptydir-data
kubectl delete node node3
#查看节点,错误节点已删除
kubectl get nodes

添加/删除污点

1
2
3
4
#给节点 node1 增加一个污点,它的键名是 key1,键值是 value1,效果是 NoSchedule。 这表示只有拥有和这个污点相匹配的容忍度的 Pod 才能够被分配到 node1 这个节点。
kubectl taint nodes node1 key1=value1:NoSchedule
#删除污点
kubectl taint nodes node1 key1=value1:NoSchedule-

注意事项

  • 能修改deployment优先修改deployment,如果不能修改再修改pod的配置文件,因为pod修改之后会重启消失。

常见问题

  1. kk安装多节点集群的时候,报如下错误:

    [wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s [kubelet-check] Initial timeout of 40s passed.

    解决:amd的主机有问题,换了一个inter芯片的主机就OK了,新解决方法安装前,执行yum update -y

  2. 一个节点宕机后,添加一个新的节点替代,执行kk添加节点命令时报如下错误:

    etcd health check failed: Failed to exec command: sudo -E /bin/bash -c "export ETCDCTL_API=2;export ETCDCTL_CERT_FILE='/etc/ssl/etcd/ssl/admin-node1.pem';export ETCDCTL_KEY_FILE='/etc/ssl/etcd/ssl/admin-node1-key.pem';export ETCDCTL_CA_FILE='/etc/ssl/etcd/ssl/ca.pem';/usr/local/bin/etcdctl --endpoints=https://192.168.14.16:2379,https://192.168.14.17:2379,https://192.168.14.20:2379 cluster-health | grep -q 'cluster is healthy'

    解决:先删除etcd的异常节点,在重新执行kk添加etcd及master节点

  3. 执行kubectl drain node3 --force --ignore-daemonsets --delete-emptydir-data删除节点时报如下错误:

    I0705 16:41:20.004286 18301 request.go:665] Waited for 1.14877279s due to client-side throttling, not priority and fairness, request: GET:https://lb.kubesphere.local:6443/api/v1/namespaces/kubesphere-monitoring-system/pods/alertmanager-main-1

    解决:强制取消,执行kubectl delete node node3即可

  4. 重建节点之后,新加的节点无法调度,报如下错误:

    0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 Insufficient cpu.

    解决:在集群节点查看污点,然后执行kubectl taint nodes node4 node-role.kubernetes.io/master=:NoSchedule-删除污点

  5. 重建节点之后组件监控prometheus-k8s容器事件提示如下错误:

    MountVolume.NewMounter initialization failed for volume "pvc-60891ee0-ba6c-4df4-b381-6e542b27d3a7" : path "/var/openebs/local/pvc-60891ee0-ba6c-4df4-b381-6e542b27d3a7" does not exist

    解决:在master节点执行,以下方法并不能解决,待验证存储卷是否是分布式的?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #在/etc/kubernetes/manifests/kube-apiserver.yaml
    #spec:
    # containers:
    # - command:
    # - kube-apiserver
    # - -–feature-gates=RemoveSelfLink=fals #添加该行
    vim /etc/kubernetes/manifests/kube-apiserver.yaml
    #应用配置
    kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
  6. 使用amd主机安装kubesphere,一直卡在Please wait for the installation to complete:,查看pod日志,发现calico-node-4hgbb的pod提示如下错误:

    1
    2
    3
    4
      Type     Reason     Age                    From     Message
    ---- ------ ---- ---- -------
    Warning Unhealthy 3m18s (x440 over 66m) kubelet (combined from similar events): Readiness probe failed: 2022-07-06 02:39:53.164 [INFO][4974] confd/health.go 180: Number of node(s) with BGP peering established = 2
    calico/node is not ready: felix is not ready: readiness probe reporting 503

    参考: kubernetes v1.24.0 install failed with calico node not ready #1282

    解决:resolved by change calico version, maybe calico verison should update from v3.20.0 to v3.23.0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #删除calico相关pod
    kubectl -n kube-system get all |grep calico | awk '{print $1}' | xargs kubectl -n kube-system delete
    #获取3.23新版本
    wget https://docs.projectcalico.org/archive/v3.23/manifests/calico.yaml
    #重新安装calico
    kubectl apply -f calico.yaml
    #calico虽然正常了,但是后续重新用kk安装又回回到不正常状态,注意不要修改pod的配置文件,要修改deployment。
    #最终解决在安装集群前执行
    yum update -y
  7. 系统组件->监控->prometheus-k8s->事件->错误日志:0/3 nodes are available: 3 Insufficient cpu.

    解决:修改工作负载->有状态副本集->prometheus-k8s

    总结:requests.cpu设置为0.5代表一个cpu的一半,0.5等价于500m,读做”500 millicpu”(五百毫核)

    官方说明:Kubernetes 中的资源单位

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #重启之后需要重新修改
    containers:
    - name: prometheus
    resources:
    limits:
    cpu: '4'
    memory: 16Gi
    requests:
    cpu: 200m #修改为20m
    memory: 400Mi #修改为40Mi
  8. 执行kubectl top node提示error: Metrics API not available错误

    解决:1.未安装修改kubesphere部署配置文件,已安装登录kubesphere点击定制资源定义->ClusterConfiguration->ks-installer修改。

    1
    2
    metrics_server:
    enabled: false #设置为true
  9. calico/node is not ready: felix is not ready: readiness probe reporting 503

    再次尝试之后calico/node is not ready: felix is not ready: Get "http://localhost:9099/readiness": dial tcp [::1]:9099: connect: connection refused

附录

config-kubernetes-1.23.7.yaml部署文件

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
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
name: sample
spec:
hosts:
- {name: node1, address: 192.168.14.1, internalAddress: 192.168.14.1, user: root, privateKeyPath: "/exxk/k8s.cer"}
- {name: node2, address: 192.168.14.2, internalAddress: 192.168.14.2, user: root, privateKeyPath: "/exxk/k8s.cer"}
- {name: node3, address: 192.168.14.3, internalAddress: 192.168.14.3, user: root, privateKeyPath: "/exxk/k8s.cer"}
roleGroups:
etcd:
- node[1:3]
master:
- node[1:3]
worker:
- node[1:3]
controlPlaneEndpoint:
## Internal loadbalancer for apiservers
# internalLoadbalancer: haproxy

domain: lb.kubesphere.local
address: "192.168.14.1"
port: 6443
kubernetes:
version: v1.23.7
clusterName: cluster.local
autoRenewCerts: true
containerManager: docker
etcd:
type: kubekey
network:
plugin: calico
kubePodsCIDR: 10.233.64.0/18
kubeServiceCIDR: 10.233.0.0/18
## multus support. https://github.com/k8snetworkplumbingwg/multus-cni
multusCNI:
enabled: false
registry:
privateRegistry: ""
namespaceOverride: ""
registryMirrors: []
insecureRegistries: []
addons: []

config-kubesphere3.3.0-kubernetes1.23.7.yaml部署文件

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
name: sample
spec:
hosts:
- {name: node1, address: 192.168.14.16, internalAddress: 192.168.14.16, user: root, privateKeyPath: "/exxk/k8s.cer"}
- {name: node2, address: 192.168.14.17, internalAddress: 192.168.14.17, user: root, privateKeyPath: "/exxk/k8s.cer"}
- {name: node3, address: 192.168.14.18, internalAddress: 192.168.14.18, user: root, privateKeyPath: "/exxk/k8s.cer"}
roleGroups:
etcd:
- node[1:3]
control-plane:
- node[1:3]
worker:
- node[1:3]
controlPlaneEndpoint:
# Internal loadbalancer for apiservers
# internalLoadbalancer: haproxy
domain: lb.kubesphere.local
address: "192.168.14.16"
port: 6443
kubernetes:
version: v1.23.7
clusterName: cluster.local
autoRenewCerts: true
containerManager: docker
etcd:
type: kubekey
network:
plugin: calico
kubePodsCIDR: 10.233.64.0/18
kubeServiceCIDR: 10.233.0.0/18
## multus support. https://github.com/k8snetworkplumbingwg/multus-cni
multusCNI:
enabled: false
registry:
privateRegistry: ""
namespaceOverride: ""
registryMirrors: []
insecureRegistries: []
addons: []

---
apiVersion: installer.kubesphere.io/v1alpha1
kind: ClusterConfiguration
metadata:
name: ks-installer
namespace: kubesphere-system
labels:
version: v3.3.0
spec:
persistence:
storageClass: ""
authentication:
jwtSecret: ""
zone: ""
local_registry: ""
namespace_override: ""
# dev_tag: ""
etcd:
monitoring: false
endpointIps: localhost
port: 2379
tlsEnable: true
common:
core:
console:
enableMultiLogin: true
port: 30880
type: NodePort
# apiserver:
# resources: {}
# controllerManager:
# resources: {}
redis:
enabled: false
volumeSize: 2Gi
openldap:
enabled: false
volumeSize: 2Gi
minio:
volumeSize: 20Gi
monitoring:
# type: external
endpoint: http://prometheus-operated.kubesphere-monitoring-system.svc:9090
GPUMonitoring:
enabled: false
gpu:
kinds:
- resourceName: "nvidia.com/gpu"
resourceType: "GPU"
default: true
es:
# master:
# volumeSize: 4Gi
# replicas: 1
# resources: {}
# data:
# volumeSize: 20Gi
# replicas: 1
# resources: {}
logMaxAge: 7
elkPrefix: logstash
basicAuth:
enabled: false
username: ""
password: ""
externalElasticsearchHost: ""
externalElasticsearchPort: ""
alerting:
enabled: false
# thanosruler:
# replicas: 1
# resources: {}
auditing:
enabled: false
# operator:
# resources: {}
# webhook:
# resources: {}
devops:
enabled: false
# resources: {}
jenkinsMemoryLim: 2Gi
jenkinsMemoryReq: 1500Mi
jenkinsVolumeSize: 8Gi
jenkinsJavaOpts_Xms: 1200m
jenkinsJavaOpts_Xmx: 1600m
jenkinsJavaOpts_MaxRAM: 2g
events:
enabled: false
# operator:
# resources: {}
# exporter:
# resources: {}
# ruler:
# enabled: true
# replicas: 2
# resources: {}
logging:
enabled: false
logsidecar:
enabled: true
replicas: 2
# resources: {}
metrics_server:
enabled: false
monitoring:
storageClass: ""
node_exporter:
port: 9100
# resources: {}
# kube_rbac_proxy:
# resources: {}
# kube_state_metrics:
# resources: {}
# prometheus:
# replicas: 1
# volumeSize: 20Gi
# resources: {}
# operator:
# resources: {}
# alertmanager:
# replicas: 1
# resources: {}
# notification_manager:
# resources: {}
# operator:
# resources: {}
# proxy:
# resources: {}
gpu:
nvidia_dcgm_exporter:
enabled: false
# resources: {}
multicluster:
clusterRole: none
network:
networkpolicy:
enabled: false
ippool:
type: none
topology:
type: none
openpitrix:
store:
enabled: false
servicemesh:
enabled: false
istio:
components:
ingressGateways:
- name: istio-ingressgateway
enabled: false
cni:
enabled: false
edgeruntime:
enabled: false
kubeedge:
enabled: false
cloudCore:
cloudHub:
advertiseAddress:
- ""
service:
cloudhubNodePort: "30000"
cloudhubQuicNodePort: "30001"
cloudhubHttpsNodePort: "30002"
cloudstreamNodePort: "30003"
tunnelNodePort: "30004"
# resources: {}
# hostNetWork: false
iptables-manager:
enabled: true
mode: "external"
# resources: {}
# edgeService:
# resources: {}
terminal:
timeout: 600

add-node.yaml

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
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
name: sample
spec:
hosts:
##You should complete the ssh information of the hosts
- {name: node1, address: 192.168.14.16, internalAddress: 192.168.14.16, user: root, privateKeyPath: "/exxk/k8s.cer"}
- {name: node2, address: 192.168.14.17, internalAddress: 192.168.14.17, user: root, privateKeyPath: "/exxk/k8s.cer"}
- {name: node3, address: 192.168.14.18, internalAddress: 192.168.14.18, user: root, privateKeyPath: "/exxk/k8s.cer"}
- {name: node4, address: 192.168.14.19, internalAddress: 192.168.14.19, user: root, privateKeyPath: "/exxk/k8s.cer"}
roleGroups:
etcd:
- node[1:3]
master:
- node1
- node2
- node3
worker:
- node1
- node2
- node3
- node4
controlPlaneEndpoint:
##Internal loadbalancer for apiservers
#internalLoadbalancer: haproxy

##If the external loadbalancer was used, 'address' should be set to loadbalancer's ip.
domain: lb.kubesphere.local
address: "192.168.14.16"
port: 6443
kubernetes:
version: v1.23.7
clusterName: cluster.local
proxyMode: ipvs
masqueradeAll: false
maxPods: 110
nodeCidrMaskSize: 24
network:
plugin: calico
kubePodsCIDR: 10.233.64.0/18
kubeServiceCIDR: 10.233.0.0/18
registry:
privateRegistry: ""

常用命令

1
2
3
4
5
6
7
8
9
10
#查询所有pvc
kubectl get pvc -n kubesphere-devops-system
#查看pvc详情信息
kubectl describe pvc ks-jenkins -n kubesphere-devops-system
#查询所有pv
kubectl get pv
#查看pv详情信息
kubectl describe pv pvc-fe2e8742-a736-4134-b89a-85eef0b6edce
kubectl get sc
kubectl describe sc local

基础概念

jQhr6g.png](https://imgtu.com/i/jQhr6g)](https://imgtu.com/i/jQfkqA)

  • PV(PersistentVolume) :描述的是持久化存储卷,是集群中的资源。
  • PVC(persistentVolumeClaim):申领是对这些资源的请求,也被用来执行对资源的申领检查。
  • SC(StorageClass):为管理员提供了描述存储 “类” 的方法。

pv和sc都属于集群所有,没有namespace之分,只有pvc才各属于每个空间的资源

简单nginx挂载持久卷实例

  1. 创建pvc:nginx-pvc.yaml文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: nginx-pvc
    spec:
    accessModes: #访问模式的支持由storageClassName进行限定的
    - ReadWriteOnce
    #ReadWriteOnce(RWO)卷可以被一个节点以读写方式挂载。
    #ReadOnlyMany(ROX)卷可以被多个节点以只读方式挂载。
    #ReadWriteMany(RWX)卷可以被多个节点以读写方式挂载。
    #ReadWriteOncePod(RWOP)卷可以被单个 Pod 以读写方式挂载。
    resources:
    requests:
    storage: 1Gi #申请的资源大小
    storageClassName: local #指定存储类
  2. 执行kubectl apply -f nginx-pvc.yaml,可以通过kubectl get pvc进行查看

  3. 创建pod:nginx-pod-volumes.yaml文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    apiVersion: v1
    kind: Pod
    metadata:
    name: nginx
    labels: #标签
    app: nginx #标签为了后面service的selector
    spec:
    volumes:
    - name: nginx-data
    persistentVolumeClaim:
    claimName: nginx-pvc
    containers:
    - name: nginx
    image: nginx:alpine
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: "/usr/share/nginx/html"
    name: nginx-data
  4. 执行kubectl apply -f nginx-pod-volumes.yaml,可以通过kubectl get po进行查看

  5. 创建对应的服务,暴露端口,见k8s 部署基础

  6. 访问nginx服务

  7. 通过kubectl get pvkubectl describe pv pvc-...查看source:path的路径/var/openebs/local/pvc-***

  8. 该路径在其中一个节点,找到有该路径的节点,在该目录新建一个index.html,里面输入hello pvc

  9. 重新访问nginx服务,就能在网页上看到hello pvc

pvc部署文件
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
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: ks-jenkins
namespace: kubesphere-devops-system
labels:
app: ks-jenkins
app.kubernetes.io/managed-by: Helm
chart: jenkins-0.19.0
heritage: Helm
release: ks-jenkins
annotations:
meta.helm.sh/release-name: ks-jenkins
meta.helm.sh/release-namespace: kubesphere-devops-system
pv.kubernetes.io/bind-completed: 'yes'
pv.kubernetes.io/bound-by-controller: 'yes'
volume.beta.kubernetes.io/storage-provisioner: openebs.io/local
volume.kubernetes.io/selected-node: k8s-worker-242-17 #在那个节点上
finalizers:
- kubernetes.io/pvc-protection
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
volumeName: pvc-fe2e8742-a736-4134-b89a-85eef0b6edce #pv的name
storageClassName: local
volumeMode: Filesystem
Pod部署文件:
1
2
3
4
5
6
7
8
9
10
11
12
kind: Pod
apiVersion: v1
spec:
volumes:
- name: jenkins-home #定义挂载卷
persistentVolumeClaim: #使用的pvc
claimName: ks-jenkins #使用的pvc名字
containers:
- name: ks-jenkins
volumeMounts:
- name: jenkins-home #使用的挂载卷
mountPath: /var/jenkins_home #容器内需要挂载的目录
pvc信息解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Name:          ks-jenkins #pvc名字
Namespace: kubesphere-devops-system
StorageClass: local
Status: Bound
Volume: pvc-fe2e8742-a736-4134-b89a-85eef0b6edce #挂载卷名字也就是pv名字
Labels: app=ks-jenkins
app.kubernetes.io/managed-by=Helm
chart=jenkins-0.19.0
heritage=Helm
release=ks-jenkins
Annotations: meta.helm.sh/release-name: ks-jenkins
meta.helm.sh/release-namespace: kubesphere-devops-system
pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: openebs.io/local
volume.kubernetes.io/selected-node: k8s-worker-242-17 #那个节点
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 20Gi #大小
Access Modes: RWO
VolumeMode: Filesystem
Mounted By: ks-jenkins-76d6b84fdf-qvj98
Events: <none>
pv信息解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Name:              pvc-fe2e8742-a736-4134-b89a-85eef0b6edce #pv名字
Labels: openebs.io/cas-type=local-hostpath
Annotations: pv.kubernetes.io/provisioned-by: openebs.io/local
Finalizers: [kubernetes.io/pv-protection]
StorageClass: local
Status: Bound
Claim: kubesphere-devops-system/ks-jenkins
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 20Gi
Node Affinity:
Required Terms:
Term 0: kubernetes.io/hostname in [k8s-worker-242-17] #在那个节点
Message:
Source:
Type: LocalVolume (a persistent volume backed by local storage on a node) #类型
Path: /var/openebs/local/pvc-fe2e8742-a736-4134-b89a-85eef0b6edce #在节点的路径
Events: <none>
SC信息解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Name:            local
IsDefaultClass: Yes
Annotations: cas.openebs.io/config=- name: StorageType
value: "hostpath"
- name: BasePath
value: "/var/openebs/local/"
,kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"cas.openebs.io/config":"- name: StorageType\n value: \"hostpath\"\n- name: BasePath\n value: \"/var/openebs/local/\"\n","openebs.io/cas-type":"local","storageclass.beta.kubernetes.io/is-default-class":"true","storageclass.kubesphere.io/supported-access-modes":"[\"ReadWriteOnce\"]"},"name":"local"},"provisioner":"openebs.io/local","reclaimPolicy":"Delete","volumeBindingMode":"WaitForFirstConsumer"}
,openebs.io/cas-type=local,storageclass.beta.kubernetes.io/is-default-class=true,storageclass.kubesphere.io/support-snapshot=false,storageclass.kubesphere.io/supported-access-modes=["ReadWriteOnce"] #限定pvc的访问模式
Provisioner: openebs.io/local #制备器(包含NFS、CephFS、AzureFile、Local等等)
Parameters: <none>
AllowVolumeExpansion: <unset>
MountOptions: <none>
ReclaimPolicy: Delete
VolumeBindingMode: WaitForFirstConsumer #卷绑定模式
Events: <none>

常见问题

  1. 错误信息,申明错误的访问模式,访问模式由sc存储类定义,local存储类不支持该访问模式

    1
    Warning  ProvisioningFailed    48s (x4 over 2m33s)    openebs.io/local_openebs-localpv-provisioner-7bbb56d7dc-zsq5k_8a90d62c-5e9f-4544-a7bb-50ca898c55a0  failed to provision volume with StorageClass "local": Only support ReadWriteOnce access mode
  2. 使用local存储卷时,节点不会从一个节点游走到另一个节点,因此节点挂机,该服务直接不可用了,损失了高可用,这是因为sc限制了访问模式为ReadWriteOnce,影响了节点亲和性

官方文档

仓库ArtifactHUB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#官方仓库
helm repo add stable https://charts.helm.sh/stable
#常用仓库
helm repo add elastic https://helm.elastic.co
helm repo add gitlab https://charts.gitlab.io
helm repo add harbor https://helm.goharbor.io
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com
helm repo add stable https://kubernetes-charts.storage.googleapis.com
#国内
helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo update
#仓库列表
helm repo list

常用命令

1
2
3
4
5
6
7
8
# mac 安装helm
brew install helm
#获取服务的配置
helm get values -n namespaseName server-name
# 查看服务的部署历史
helm history -n namespaseName server-name
# 回滚版本
helm rollback -n namespaseName server-name revision

charts模版

模版目录结构

1
2
3
4
5
6
7
8
9
10
mychart/
Chart.yaml # 文件包含了该chart的描述。你可以从模板中访问它。
values.yaml #父亲values.yml文件,会被-f命令覆盖,-f命令会被--set覆盖
charts/ #可以 包含其他的chart(称之为 子chart)。
templates/ #目录包括了模板文件,Helm会通过模板渲染引擎将所有文件发送到templates/目录中,然后收集模板的结果并发送给k8s。
NOTES.txt # chart的"帮助文本"。这会在你的用户执行helm install时展示给他们
deployment.yaml #创建Kubernetes 工作负载的基本清单
service.yaml #为你的工作负载创建一个 service终端基本清单。
_helpers.tpl #放置可以通过chart复用的模板辅助对象
...

helm upgrade

helm upgrade [RELEASE] [CHART] [flags]

常见参数:

  • -i, --install :如果版本不存在就运行安装
  • --history-max <int>:限制每个版本保存的最大修订数。使用 0 表示无限制(默认 10)
  • -n, --namespace <string>:指定命名空间范围
  • -f, --values <strings>:在 YAML 文件或 URL 中指定值(可以指定多个)
  • --set stringArray: 在命令行设置值(可以用逗号指定多个或单独的值:key1=val1,key2=val2)
1
2
3
4
5
6
7
8
9
10
#env环境变量
CHARTS_REPO = 'https://harbor.exxk.com/chartrepo/baseimage/charts'
CHARTS_NAME = 'appchart-uat-latest.tgz'
CICD_REGISTRY = 'harbor.exxk.com.cn'
HARBOR_NAMESPACE = 'dev-1'
#---示例
#获取当前镜像
CURRENT_IMAGE=`helm3 get values -n exxk exxk-server-v1 | grep repository | awk -F ': ' '{print \\$2}' `
#更新或安装服务
helm3 upgrade --install --history-max=5 exxk-server-v1 $CHARTS_REPO/$CHARTS_NAME -n exxk -f z-k8s-helm/uat/val-exxk-server.yaml --set image.repository=$CICD_REGISTRY/$HARBOR_NAMESPACE/exxk-server:$CICD_SEQUENCE,currentImage=$CURRENT_IMAGE

创建helm chrat

常见语法

1
2
3
4
{{- with .Values.nodeSelector }} #with可以指定变量范围
nodeSelector:
{{- toYaml . | nindent 8 }} #nindent 4 是指换行后加4个空格,toYaml是一个函数,指定映射路径并全部取出。
{{- end }}
1
2
3
4
5
6
#安装helm
brew install helm
#创建项目
helm create nginx-file-brower
#检查依赖
helm lint mychart

常见问题

  1. helm Error: UPGRADE FAILED: another operation (install/upgrade/rollback) is in progress

    错误原因:是因为历史更新有未成功的,导致一直Preparing upgrade

    解决:执行下面命令回退版本,然后重新跑流水线即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [root@k8s-master-242-10 ~]# helm history -n namespaseName server-name
    REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
    12 Wed Jun 1 07:00:04 2022 superseded appchart-uat-latest 1.16.0 Upgrade complete
    13 Thu Jun 2 02:01:17 2022 superseded appchart-uat-latest 1.16.0 Upgrade complete
    14 Thu Jun 2 02:23:25 2022 pending-upgrade appchart-uat-latest 1.16.0 Preparing upgrade
    [root@k8s-master-242-10 ~]# helm rollback -n namespaseName server-name 13
    Rollback was a success! Happy Helming!
    [root@k8s-master-242-10 ~]# helm history -n namespaseName server-name
    REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
    12 Wed Jun 1 07:00:04 2022 superseded appchart-uat-latest 1.16.0 Upgrade complete
    13 Thu Jun 2 02:01:17 2022 superseded appchart-uat-latest 1.16.0 Upgrade complete
    14 Thu Jun 2 02:23:25 2022 pending-upgrade appchart-uat-latest 1.16.0 Preparing upgrade
    15 Tue Jun 28 16:35:18 2022 deployed appchart-uat-latest 1.16.0 Rollback to 13
    [root@mh-k8s-master-242-10 ~]#

常用语法表达式

1
2
3
4
5
6
7
8
9
10
11
12
//----??表达式----
String a;
print(a??'defaul value'); //a为null就打印右边的值,不为空就左边
//----函数缩写----
(){print()}//回掉中只含有一句可以缩写成如下
()=>print()
//-----dynamic 相当于object类型------
Map<String,dynamic> data={}; //初始化map
data['key']="value";//赋值
String a=data['key']; //取值
//----list 如何转 json
String jsonStr= jsonEncode(<List>);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DraftBean {
String title = "";
String summary = "";
SoftAllBean soft = SoftAllBean();
List<StepBean> steps = [];

DraftBean(); //无参构造

DraftBean.fromJson(Map<String, dynamic> json) //自定义初始化方法
: title = json['title'],
summary = json['summary'],
soft = SoftAllBean.fromJson(json['soft']), //对象初始化
steps = (json['steps'] as List).map((e) => StepBean.fromJson(e)).toList(); //list初始化

Map<String, dynamic> toJson() =>
{'title': title, 'summary': summary, 'soft': soft, 'steps': steps};
}

提示语法错误,检查是否有const修饰,他不能修饰可变的东西。

Flutter项目基本介绍

pubspec.yaml:依赖包管理文件,添加依赖后,点击`Pub get可以进行下载添加的依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
name: draft #包名或项目名
description: write a draft #项目描述
publish_to: 'none' # 发布包配置,不发布
version: 1.0.0+1 #项目版本
environment: #环境变量
sdk: ">=2.17.1 <3.0.0" #Dart语言版本
dependencies: #应用或包依赖的其他包或插件,会编译到app应用里
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
dev_dependencies: #开发环境依赖的工具包,用于辅助开发和测试,不会编译到app应用里
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter: #flutter相关配置
uses-material-design: true

控件(material

  • TextField 输入框

    1
    2
    3
    4
    5
    6
    7
    child: TextField(
    obscureText: true,
    decoration: InputDecoration(
    border: OutlineInputBorder(), //边框样式
    labelText: '用户名', //提示标签
    ),
    ),
  • Text 文本框

    1
    child: Text("欢迎登录"),
  • TextButton 按钮

    1
    2
    3
    4
    5
    6
    child: TextButton(
    onPressed: (){
    print("点击了");
    },
    child: const Text("登录")
    )
  • Autocomplete 自动补齐输入框框(带下拉搜索输入框)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    child: Autocomplete<Soft>(
    displayStringForOption: (Soft option) => option.name, //输入框显示的值
    optionsBuilder: (TextEditingValue textEditingValue) {
    return HttpUtil().getSoftListString(
    context, "20", textEditingValue.text.toLowerCase());
    }, //选项构建,数据来源支持异步
    fieldViewBuilder: (context, textEditingController, focusNode, onFieldSubmitted) {
    return TextField(
    controller: textEditingController, //传递
    focusNode: focusNode, //传递
    decoration: const InputDecoration(
    border: OutlineInputBorder(), labelText: '软件'),
    ); //自定义样式
    }, //输入框自定义
    onSelected: (Soft selection) {
    versionController.text = selection.version;
    }, //选中回掉
    ),
  • Image图片显示

    1
    Image(image: NetworkImage('url'))
  • GestureDetector 给一个控件添加一个事件

    1
    2
    3
    4
    GestureDetector(
    onTap:() //触摸事件
    child: //给该child添加一个触摸事件
    )

布局(layout

  • Column:列布局

  • Row:行布局

    1
    2
    3
    4
    5
    6
    Column( //或者Row布局
    children: [
    const Text('行一'),
    const Text('行二'),
    ]
    )
  • CustomScrollView:滚动布局

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    1. 继承State
    2. 设置中心key
    3. 触发setState((){num++}) 动态更新布局
    4. 基础结构:
    CustomScrollView
    center:
    slivers:[
    SliverList(key: centerKey,delegate:SliverChildBuilderDelegate(childCount:num)),//num是指重复布局几个
    SliverList()),
    ]

启动类

1
2
3
4
5
6
import 'package:flutter/material.dart';
import 'login.dart';

void main() {
runApp(const Login());
}

一个页面(类)基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import 'package:flutter/material.dart';

class Login extends StatelessWidget {
const Login({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
title: '登录',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: Scaffold(
appBar: AppBar(
title: const Text('登录'),
),
body: Colum(),
),
);
}
}

简单页面跳转

1
2
//跳转到Home页面 
Navigator.push(context, MaterialPageRoute(builder:(context)=> const Home()));

多复杂页面跳转-路由(router)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//-----主页面
void main() {
runApp(MaterialApp(
routes: { //配置路由
'/': (context) => const Home(),
'/login': (context) => const Login(),
},
));
}
//----跳转到登录页面----
Navigator.pushNamed(context, '/login');
//----登录页面退回主页----
Navigator.pop(context);
//---移除当前页面,再加载/页面,可以用于页面刷新
Navigator.pushReplacementNamed(context, '/');

获取输入框的值

  • 方法一:定一个变量,然后通过输入框的onChanged设置值到变量,然后获取

  • 方法二:设置controller

    1
    2
    3
    4
    5
    TextEditingController userNameController =TextEditingController(); //定义controller
    TextField(
    controller: userNameController, //设置controller
    )
    print("输入了${userNameController.text}"); //获取controller的值

toast提示框-fluttertoast

  1. 添加依赖fluttertoast: ^8.0.9
  2. 导包import 'package:fluttertoast/fluttertoast.dart';
  3. 使用Fluttertoast.showToast(msg: "请登录");

网络请求-dio框架

  1. pubspec.yamldependencies下添加依赖dio: ^4.0.6,然后点击文件顶部Pub get更新依赖

  2. 导入包import 'package:dio/dio.dart';

  3. 简单使用

    1
    2
    3
    4
    5
    6
    /// 登录请求
    Future<Response> loginPost(String userName, String password) async {
    Response response = await Dio.post('/api/v1/user/login',
    data: {'username': userName, 'password': password});
    return response;
    }

文件选择-File Picker

1
2
3
///选择图片文件
FilePickerResult? result = await FilePicker.platform
.pickFiles(type: FileType.image);

文件压缩-flutter_native_image

1
2
File compressedFile = await FlutterNativeImage.compressImage(file.path,
quality: 50);

缓存-文件模式-shared_preferences

工具类用Sputil

问题

  1. 如下错误:

    1
    2
    Error: Not a constant expression.
    onPressed: (){},

    解决:Not a constant expression 关键字const,使用const修饰的变量及方法不能包含可变的,所以onPressed就不可以使用了,const关键字使用要点

    • 不可修改
    • 编译期初始化
    • const 构造函数
  2. 错误:Navigator operation requested with a context that does not include a Navigator.

    解决:启动类添加MaterialApp包括,修改如下

    1
    2
    3
    4
    5
    void main() {
    runApp(const MaterialApp(
    home: Login(),
    ));
    }
  3. flutter 如何动态更新界面或控件?

    解决:使用setState((){})

  4. flutter如何重新加载主页?

    解决:使用Navigator.pushReplacementNamed();Navigator.popAndPushNamed()

  5. flutter的File Picker如何选择相册?

    解决:设置typeFileType.image,完整示例FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.image);

  6. dio如何开启网络请求日志?

    解决:添加过滤器_dio.interceptors.add(LogInterceptor(responseBody: true, requestBody: true)); //debug开启权限日志

  7. flutter如何获取输入框的值,或者动态修改输入框的值?

    解决:添加公共变量TextEditingController versionController = TextEditingController(); 然后在TextField设置controller: versionController,最后在其他地方获取或设置输入框的值versionController.text

  8. Row布局如何按比例分配空间大小?

    解决:添加Expanded包装控件,然后设置flex比例,例如1:2

  9. 如何修改输入框提示文字的颜色?

    解决:添加labelStyle: TextStyle(color: Colors.amberAccent)属性

官方英文文档

Flutter中文网

flutter常用命令

1
2
3
flutter run --release
flutter devices #查看连接的设备
flutter doctor #检查环境

安装-环境搭建-debug-运行发布一个应用到手机上

  1. 下载flutter_macos_3.0.1-stable.zip
  2. 解压到~/flutter
  3. 配置环境变量,在~/.zshrc末尾添加export PATH="~/flutter/bin:$PATH"
  4. idea安装flutter插件并附带安装Dart插件
  5. 配置idea的flutter路径~/flutter
  6. 新建一个Flutter项目

IOS设置

  1. 安装Xcode
  2. 设置IOS模拟器,在idea或bash执行open -a Simulator
  3. 在idea项目上面右键,然后点击Flutter/Opne IOS module in Xcode
  4. 然后在Xcode里面点击运行即可
  5. (可选)启用其他平台执行flutter config --enable-linux-desktop
  6. 设置开发者账号,在Xcode-runner-setting-team添加苹果账号
  7. 点击即可debug测试
  8. iOS提示不受信任的开发者,点击设置-通用-VPN与设备管理-添加信任
  9. 断开数据线会提示In iOS 14+,debug mode Flutter apps can only 执行flutter run --release进行发布安装

常见问题

  1. 找不到ios设备,健康检查提示CocoaPods not installed

    解决:执行sudo gem install cocoapods安装cocoapods

    1
    2
    3
    4
    5
    6
    7
    #无sudo安装,报错
    gem install cocoapods --user-install
    #错误信息,解决,用sudo gem install cocoapods安装
    /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: fatal error: 'ruby/config.h' file not found
    #include "ruby/config.h"
    ^~~~~~~~~~~~~~~
    /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: note: did not find header 'config.h' in framework 'ruby' (loaded from '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks')
  2. 健康检查提示`HTTP host “https://maven.google.com/" is not reachable. Reason: An error

      occurred while checking the HTTP host: Operation timed out
    ✗ HTTP host "https://cloud.google.com/" is not reachable. Reason: An error
      occurred while checking the HTTP host: Operation timed out`
    

    解决:在~/.zshrc添加环境如下变量

    1
    2
    3
    # 国内用户需要设置
    export PUB_HOSTED_URL=https://pub.flutter-io.cn
    export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

Brotli压缩

  • 阿里云CDN的一个功能
  • 同时开启Brotli压缩和智能压缩,且客户端请求头Accept-Encoding同时携带brgzip时,仅Brotli压缩生效。

错误原因:请求头设置了headers.put("Accept-Encoding","gzip, deflate, br");

解决方法:

  1. 解压br
  2. 请求头Accept-Encoding中去掉br

Waline官方文档

LeanCloud数据库添加
  1. 注册登录创建国际版LeanCloud数据库
  2. 绑定db域名
  3. 阿里云解析CNAME域名
  4. 记录APP IDAPP KeyMaster Key
Vercel服务端部署
  1. 使用github授权登录vercel
  2. 点击deploy
  3. 输入仓库名点击创建
  4. 环境变量设置里面添加三个环境变量 LEAN_ID, LEAN_KEYLEAN_MASTER_KEY 值分别为 APP ID, APP KEY, Master Key
  5. 绑定服务端域名
  6. 登录waline.iexxk.com进行管理员注册
hexo博客next主题配置waline评论
  1. 部署脚本文件添加命令npm install @waline/hexo-next

  2. theme/next/_config.yml配置文件添加如下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # Waline
    # For more information: https://waline.js.org, https://github.com/walinejs/waline
    waline:
    enable: true #是否开启
    serverURL: waline.iexxk.com # Waline #服务端地址,我们这里就是上面部署的 Vercel 地址
    placeholder: 请文明评论呀 # #评论框的默认文字
    avatar: mm # 头像风格
    meta: [nick, mail, link] # 自定义评论框上面的三个输入框的内容
    pageSize: 10 # 评论数量多少时显示分页
    lang: zh-cn # 语言, 可选值: en, zh-cn
    # Warning: 不要同时启用 `waline.visitor` 以及 `leancloud_visitors`.
    visitor: false # 文章阅读统计
    comment_count: true # 如果为 false , 评论数量只会在当前评论页面显示, 主页则不显示
    requiredFields: [] # 设置用户评论时必填的信息,[nick,mail]: [nick] | [nick, mail]
    libUrl: # Set custom library cdn url

常见问题

  1. 版本太低问题,升级到next 8+

    1
    Error: Unable to call `next_data`, which is undefined or falsey

参考:

LeanCloud如何绑定域名

waline如何创建Vercel服务

Vercel如何绑定域名

Hexo博客进阶:为 Next 主题添加 Waline 评论系统

需求

因博客修改了域名,以前收录的旧域名需要自动重定向到新域名,而且百度和google需要添加新站点收录。因为博客采用netlify部署的。

操作步骤

  1. 在阿里云域名修改新域名解析

  2. 修改netlify里面的https认证及新域名添加

  3. 兼容旧域名,添加310重定向,修改hexo博客目录下的netlify.toml文件,增加如下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [[redirects]]
    from = "https://xuan.netlify.app/*"
    # :splat 是为了原路径保持,不然会跳转到主页
    to = "https://iexxk.com/:splat"
    status = 301
    force = true
    [[redirects]]
    from = "https://blog.iexxk.com/*"
    to = "https://iexxk.com/:splat"
    status = 301
    force = true
  4. 修改百度收录并在网站改版里面添加旧域名和新域名的映射

  5. 修改Google收录