kubeadm init --apiserver-advertise-address 192.168.56.105 --pod-network-cidr=10.244.0.0/16
–apiserver-advertise-address 指明了使用master与其他节点的通信ip
–pod-network-cidr 指定pod网络的范围。
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl 需要通过这个config文件访问K8s的apiserver,来获取集群的信息。
KUBECONFIG=xxx.conf kubectl get pods
所以使用不同的配置文件,就可以访问不同的集群
没有pod网络,集群中的pod就不能相互通信。K8s有多种网络
flannel
创建flannel网络
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Cannal
在node节点上执行
kubeadm join --token xxxx 192.168.56.105:6443
token是master节点 kubeadm init时创建。可以通过kubeadm token list获取。
k8s的组件都是由kubeadm控制,从官方库下载的镜像,然后运行的容器。他们都是k8s中的pod,属于集群中的一部分。
kubelet是唯一没有以容器形式运行的k8s组件。以systemd服务运行。
k8s支持两种创建资源的方式
kubectl run nginx-deployment --image=nginx:1.9.1 --replicas2
apiVersion: apps/v1 #apiserver版本,每个版本内功能可能不相同
kind: Deployment #资源类型
metadata: #deployment元数据,name是必要元数据项
name: bxy-local-nginx
namespace: kube-system #命名空间
spec: #deployment资源清单
replicas: 1 #副本数量
selector: #(必须项)保证和pod标签一致即可
matchLabels:
k8s-app: bxy-local-nginx-label
template: #pod模板
metadata: #pod元数据,至少有一个label
labels: #pod标签(必需项)
k8s-app: bxy-local-nginx-label
spec: #pod规格
imagePullSecrets:
- name: regcred
containers: #容器描述,name和image是必须
- name: bxy-local-nginx
image: mirrors.tencent.com/tcnp_tkestack/busybox:latest
imagePullPolicy: IfNotPresent #如果本地没有则拉取镜像库
volumeMounts: #挂载卷配置
- mountPath: /echo_dir #挂载容器目录
name: bxy-local-pv-volume #绑定的pv名称,和volumes.name一致
args:
- /bin/sh
- -c
- echo "hello world" > /echo_dir/hello; sleep 3000
volumes: #pv描述
- name: bxy-local-pv-volume #pv名称
persistentVolumeClaim:
claimName: bxy-local-pvc-volume #绑定的pvc名称
kubectl apply -f nginx.yml 应用nginx.yml配置文件
kubectl delete -f nginx.yml 或者 kubectl delete deployment_name 删除deployment
kubectl describe pod -n namespace 查看pod的详细情况
kubectl tain node k8s-master node-role.kubernetes.io/master- 将master配置成node
kubectl tain node k8s-master node-role.kubernetes.io/master="":NoSchedule 恢复master only
kubectl label node k8s-node1 disktype=ssd 添加标签
kubectl label node k8s-node1 disktype- 删除标签
spec:
containers:
- name:nginx
image: nginx:1.0.9
nodeSelector:
disktype: ssd
spec:
nodename: k8s-node1
nodename就是主机名,修改主机名
kubectl --hostname-override=node1
DaemonSet的不同之处,每个node只能运行一个副本,一般用于监控daemon,日志收集daemon,存储daemon.
容器分为服务型和工作型,服务型需要一直运行,工作型一次性任务,完成后容器退出。
pod内的容器共享同一个ip和端口空间,他们之间可以使用localhost通信。类似于同一个机器上的不同进程。
每一个pod都会分配一个IP,这个ip只能被集群内的容器和节点访问。
Pod可能会频繁地销毁和创建,也就是说Pod的IP不是固定的。为了解决这个问题,Service提供了访问Pod的抽象层。无论后端的Pod如何变化,Service都作为稳定的前端对外提供服务。同时,Service还提供了高可用和负载均衡功能,Service负责将请求转发给正确的Pod。
apiVersion: apps/v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx-test #pod标签
ports:
- protocol: TCP
port: 8080 #将service端口映射到pod的80端口
targetPort: 80
nodePort: 30326 #将service的8080端口绑定到宿主机的30326端口,这样外网机器就能通过宿主机的30326访问pod
转发pod ip: 集群中的pod通过ip相互访问。现假设pod1通过pod2的ip1来访问服务,现在Pod2重启,分配了一个新的ip2。于是为了访问pod2,就需要修改pod1的访问配置,这显然是不合理的。service可以映射一组pod,k8s会维护service和pod的映射关系。无论pod如何变化,service的ip都不会变,所以客户端只需要通过service就能访问到pod。
集群中的pod除了通过service的ip访问之外,还可以使用dns访问。
kubeadm部署kube-dns组件,每当有新的service创建,就会在dns组件中添加service - dns记录。集群中的pod通过
k8s提供了多种访问service的方法
k8s提供了健康检测主要有三种
只要容器启动指令返回值不为0就重启
apiVersion: apps/v1
kind: pod
metadata:
labels:
test: healthcheck
name: healthcheck
spec:
restartPlicy: OnFailure
containers:
- name: healthcheck
image: busybox
args:
- /bin/bash
- -c
- sleep 1; exit 1
探测指令执行失败,返回非零,重启容器
spec:
restartPlicy: OnFailure
containers:
- name: healthcheck
image: busybox
args:
- /bin/bash
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 10 #10s后开始探测
periodSeconds: 5 #探测间隔5s
探测指令执行失败,返回非零,把pod从service负载均衡中剔除。
spec:
restartPlicy: OnFailure
containers:
- name: healthcheck
image: busybox
args:
- /bin/bash
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
redinessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 10 #10s后开始探测
periodSeconds: 5 #探测间隔5s
spec:
containers:
- name: web
image: myhttpd
ports:
- containerPort: 8080
readlinessProbe:
httpGet:
scheme: HTTP
path: /healthy
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
httpGet探测判断成功的条件是http请求返回的代码在200~400之间。
在扩容和更新的过程中,新的pod启动需要一段时间准备,通过探测可以知道当前pod服务是否准备好了,如果服务准备好了,就可以加入到负载均衡中。
emptyDir volume 生命周期和pod一致
pod的所有容器都共享volume,他们可以指定各自的mount路径。
spec:
containers:
- name: producer
image: busybox
volumeMouts: #挂载volume
- mountPath: /producer_dir
name: shared-volume
args:
- /bin/bash
- -c
- echo "hello world" > /producer_dir/hello; sleep 3000
- name: consumer
image: busybox
- mountPath: /consumer_dir
name: shared-volume
args:
- /bin/bash
- -c
- cat /consumer_dir/hello; sleep 3000
volumes: #创建volume
- name: shared-volume
emptyDir: {}
把node宿主机上的目录挂载给容器。一般不会用这种,增加了pod和节点的耦合。
volumeMouts: #挂载volume
- mountPath: /producer_dir
name: k8s
volumes:
- name: k8s
hostPath:
path: /etc/k8s #宿主机目录
如果k8s部署在公有云上,可以直接使用云硬盘作为volume
在实际生产中,12两种方案都不适用。使用3方案,在编写yml的时候,必须知道volume的信息。而pod通常由开发人员维护,而volume通常由存储系统的管理人员负责。也就是说开发人员要总是去问管理人员新的volume信息。这显然是不合理的,于是k8s给出了pv和pvc的解决方案。
管理人员提前创建pv,然后开发人员创建pvc指明存储资源的容量大小和访问模式等信息。K8s就会查找并提供满足条件的pv。
PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv1
labels:
name: nfs
spec:
capacity:
storage: 1Gi #PV容量
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs-name
nfs:
path: /nfsdata/pv1
server: 192.168.56.105
**** 本地文件作为存储资源
volumeMode: Filesystem
storageClassName: local-name
local:
path: /data/home/waynebfhu/k8s/pv1
PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc1
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
storageClassName: nfs
selector:
matchLabels:
name: nfs
pvc创建成功后,pv就和pvc绑定在一起了。之后就是在pod中使用存储了
Pod.yml
spec:
containers:
volumeMounts:
- mountPath: "/mydata"
name: mydata
volumes:
- name: mydata
persistentVolumeClaim:
claimName: mypvc1
删除pvc,pv就会被回收。
persistentVolumeReclaimPolicy: Recycle pv回收后,数据被删除。 Retain 则数据保留。
动态供给是指StorageClass动态创建Pv
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/no-provisioner
parameters:
type: gp2
reclaimPolicy: Retain
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/no-provisioner
parameters:
type: io1
zones: us-east-1d
iopsPerGB: "10"
reclaimPolicy: Retain
provisioner 指定了生成aws ebs
pvc和pv中的storageClassName 就是指的上述metada中的name,也就是standard和slow
mysql数据库,可以把数据库数据存放到pv中。当pod1故障后,切换新的pod,重新读取pv中的数据。
在实际生产中,我们应该实现配置文件和容器分离。
例如密码,token等敏感信息,可以使用Secret。其他的不敏感信息,可以使用Configmap保存。
(1)通过–from-env-file
cat << EOF > secret.txt
username=admin
password=123456
EOF
kubectl create secret generic mysecret --from-env-file=secret.txt
文件中的每行key-value对应一个信息条目
(2)通过YAML配置文件
apiVersion: v1
kind: Secret
metadata:
name: mysecret
data:
username: YWRtaW4=
password: MTIzNDU2
文件中的敏感数据必须通过base64编码后的结果。
echo -n admin | base64
echo -n YWRtaW4=| base64 --decode 反编码
kubectl edit secret mysecret 查看mysecret
(1)volume形式
spec:
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
在pod的/etc/foo目录下会根据key分别生成名为username和password的两个文件,文件内容就是之前设置的value。
也可以自定义文件名
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-username
-key: password
path: my-password
(2)环境变量形式
env:
- name: SECRET_USERNAME # 环境变量名
valueFrom:
secretKeyRef:
name: mysecret
key: username # 变量值
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
(1)通过–from-env-file
cat << EOF > secret.txt
username=admin
password=123456
EOF
kubectl create secret generic myconfig --from-env-file=secret.txt
文件中的每行key-value对应一个信息条目
(2)通过YAML配置文件
apiVersion: v1
kind: Secret
metadata:
name: myconfig
data:
username: admin
password: 123456
明文形式
(1) Volume形式使用
spec:
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
configMap:
name: myconfig
items:
- key: logging.conf
path: myapp/logging.conf
文件会被创建在pod的/etc/foo/myapp/logging.conf
(2)环境变量形式
env:
- name: CONF_USERNAME # 环境变量名
valueFrom:
configMapKeyRef:
name: myconfig
key: username # 变量值
- name: CONF_PASSWORD
valueFrom:
configMapKeyRef:
name: myconfig
key: password
k8s是用来管理容器的,通过K8S的文件控制容器的部署。
helm是用来管理k8s的容器部署文件的。就是说将所有的k8s容器部署文件打包给helm管理。
也就是说k8s用来管理容器
helm用来管理k8s的
Helm3和Helm2的区别比较大,且不兼容。所以后续我们都使用Helm3
# 下载helm3,解压并拷贝到/usr/local/bin目录下
wget https://get.helm.sh/helm-v3.5.3-linux-amd64.tar.gz
tar -zxvf helm-v3.5..3-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
或者
sudo curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
已经不再使用helm stable仓库了。而是迁移到artifact hub。artifact hub不是一个仓库,而是所有charts仓库的入口。
命令补全
# 在~/.bashrc 中加入
source <(helm completion bash)
helm repo add bitnami https://charts.bitnami.com/bitnami #添加repo库
helm search repo stable #查找charts
helm install stable/mysql --generate-name #通过helm安装release
helm uninstall stable/mysql #卸载release
helm ls #查看用helm安装的release
helm show all stable/mysql #查看详情
helm pull stable/mysql #下载charts
helm search repo [reponame] #查看库里面的镜像
helm create mychart #创建chart
helm package mychart #打包chart包,生成tgz文件
helm push mychart/mychart.0.1.tgz localrepo #上传chart包到repo仓库
创建一个Chart,分为两个步骤
helm create chart.name 创建chart模板,修改该模板就能创建我们自己的chart
应用更新就是修改该文件
apiVersion: v2
name: hello-helm
description: A Helm chart for Kubernetes
# 分为application 和 library
type: application
# chart的版本号,每次修改了chart都要修改当前版本
version: 0.1.0
# 容器tag
appVersion: "1.0.4"
用来描述chart配置的文件,会根据该配置生成对应的 k8s管理文件
replicaCount: 1
image:
repository: dev.registry.works:30984/hello-hw #镜像
pullPolicy: IfNotPresent #拉取镜像的规则
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
ingress:
enabled: false
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
backend:
serviceName: chart-example.local
servicePort: 80
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
Helm chart对仓库的要求并不高,需要你对外提供yaml文件和tar文件的web服务即可.
chartmuseum镜像给我们提供了repo仓库的功能。
mkdir /opt/charts #创建repo库的文件存放目录
docker run -d \
-p 4002:8080 \
-e DEBUG=1 \
-e STORAGE=local \
-e STORAGE_LOCAL_ROOTDIR=/charts \
-v /opt/charts:/charts \
chartmuseum/chartmuseum:latest
curl localhost:4002/api/charts
{} 测试repo是否搭建完成,如果能正确访问,搭建成功
helm repo add myrepo http://9.134.35.171:4002 #添加库
Kubernetes采用的是基于扁平地址空间的网络模型,集群中的每个Pod都有自己的IP地址,Pod之间不需要配置NAT就能直接通信。另外,同一个Pod中的容器共享Pod的IP,能够通过localhost通信。应用可以非常方便地从传统网络迁移到Kubernetes。每个Pod可被看作是一个个独立的机器,而Pod中的容器则可被看作同一机器中的不同进程。
K8S的网络模型分为4个部分
pod内的容器共享同一个本地文件系统,IPC和网络空间。他们之间直接使用localhost:port通信
pod之间通过集群给的ip通信
在Kubernetes集群中,Pod可能会频繁地销毁和创建,也就是说Pod的IP不是固定的。为了解决这个问题,Service提供了访问Pod的抽象层。无论后端的Pod如何变化,Service都作为稳定的前端对外提供服务。同时,Service还提供了高可用和负载均衡功能,Service负责将请求转发给正确的Pod。
k8s提供了两种外界访问pod的方式:
为了保证网络方案的标准化、扩展性和灵活性,Kubernetes采用了ContainerNetworking Interface(CNI)规范。
目前已有多种支持Kubernetes的网络方案,比如Flannel、Calico、Canal、Weave Net等。因为它们都实现了CNI规范,用户无论选择哪种方案,得到的网络模型都一样。都必须实现网络模型的4种通信情况。