知乎專欄 | 多維度架構 | 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者” |
apiVersion: v1 kind: ServiceAccount metadata: labels: app: elasticsearch name: elasticsearch namespace: elastic
創建 jenkins-namespace.yaml
apiVersion: v1 kind: Namespace metadata: name: jenkins-project
$ kubectl create -f jenkins-namespace.yaml namespace ”jenkins-project“ created
apiVersion: v1 kind: Pod metadata: name: counter spec: containers: - name: count image: busybox args: [/bin/sh, -c, 'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']
iMac:kubernetes neo$ kubectl create -f pod.yaml pod/counter created iMac:kubernetes neo$ kubectl logs counter 0: Sun Oct 4 12:32:44 UTC 2020 1: Sun Oct 4 12:32:45 UTC 2020 2: Sun Oct 4 12:32:46 UTC 2020 3: Sun Oct 4 12:32:47 UTC 2020 4: Sun Oct 4 12:32:48 UTC 2020 5: Sun Oct 4 12:32:49 UTC 2020 6: Sun Oct 4 12:32:50 UTC 2020 7: Sun Oct 4 12:32:51 UTC 2020 8: Sun Oct 4 12:32:52 UTC 2020 9: Sun Oct 4 12:32:53 UTC 2020
apiVersion: v1 kind: Pod metadata: name: hostaliases-pod spec: restartPolicy: Never hostAliases: - ip: "127.0.0.1" hostnames: - "foo.local" - "bar.local" - ip: "10.1.2.3" hostnames: - "foo.remote" - "bar.remote" containers: - name: cat-hosts image: busybox command: - cat args: - "/etc/hosts"
apiVersion: v1 kind: Pod metadata: name: envars-fieldref spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "sh", "-c"] args: - while true; do echo -en '\n'; printenv NODE_NAME POD_NAME POD_NAMESPACE; printenv POD_IP POD_SERVICE_ACCOUNT; sleep 10; done; env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: POD_SERVICE_ACCOUNT valueFrom: fieldRef: fieldPath: spec.serviceAccountName restartPolicy: Never
apiVersion: v1 kind: Pod metadata: name: envars-resourcefieldref spec: containers: - name: test-container image: k8s.gcr.io/busybox:1.24 command: [ "sh", "-c"] args: - while true; do echo -en '\n'; printenv CPU_REQUEST CPU_LIMIT; printenv MEM_REQUEST MEM_LIMIT; sleep 10; done; resources: requests: memory: "32Mi" cpu: "125m" limits: memory: "64Mi" cpu: "250m" env: - name: CPU_REQUEST valueFrom: resourceFieldRef: containerName: test-container resource: requests.cpu - name: CPU_LIMIT valueFrom: resourceFieldRef: containerName: test-container resource: limits.cpu - name: MEM_REQUEST valueFrom: resourceFieldRef: containerName: test-container resource: requests.memory - name: MEM_LIMIT valueFrom: resourceFieldRef: containerName: test-container resource: limits.memory restartPolicy: Never
就緒探針
readinessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 10 #10s之後開始第一次探測 periodSeconds: 5 #第一次探測之後每隔5s探測一次
kubelet --allowed-unsafe-sysctls \ 'kernel.msg*,net.core.somaxconn' ...
apiVersion: v1 kind: Pod metadata: name: sysctl-example spec: securityContext: sysctls: - name: kernel.shm_rmid_forced value: "0" - name: net.core.somaxconn value: "1024" - name: kernel.msgmax value: "65536"
allowPrivilegeEscalation 表示是否繼承父進程權限,runAsUser 表示使用 UID 1000 的用戶運行
apiVersion: v1 kind: Pod metadata: name: security-context-demo spec: securityContext: runAsUser: 1000 containers: - name: sec-ctx-demo image: busybox:latest securityContext: runAsUser: 1000 allowPrivilegeEscalation: false
spec: securityContext: runAsUser: 1000 fsGroup: 2000 runAsNonRoot: true
security.alpha.kubernetes.io/sysctls
apiVersion: v1 kind: Pod metadata: name: sysctl-example annotations: security.alpha.kubernetes.io/sysctls: kernel.shm_rmid_forced=1 spec:
unsafe-sysctls
apiVersion: v1 kind: Pod metadata: name: sysctl-example annotations: security.alpha.kubernetes.io/unsafe-sysctls: net.core.somaxconn=65535 #使用unsafe sysctl,設置最大連接數 spec: securityContext: privileged: true #開啟privileged權限
創建 service.yaml 檔案
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 80 - name: https protocol: TCP port: 443 targetPort: 443
iMac:kubernetes neo$ kubectl create -f service.yaml service/my-service created
查看服務
iMac:kubernetes neo$ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 113m my-service ClusterIP 10.106.157.143 <none> 80/TCP,443/TCP 64s
查看 service 後端代理的 pod 的 ip,這裡沒有掛載 pod 所以顯示 none
iMac:kubernetes neo$ kubectl get endpoints my-service NAME ENDPOINTS AGE my-service <none> 2m20s
報漏 80.11.12.10:80 地址
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 9376 externalIPs: - 80.11.12.10
apiVersion: v1 kind: Service metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com
apiVersion: v1 kind: Service metadata: name: spring-cloud-config-server namespace: default labels: app: springboot spec: #type: NodePort ports: web - port: 8888 targetPort: web clusterIP: 10.10.0.1 selector: app: spring-cloud-config-server
apiVersion: v1 kind: Service metadata: name: my-service spec: type: NodePort selector: app: MyApp ports: # By default and for convenience, the `targetPort` is set to the same value as the `port` field. - port: 80 targetPort: 80 # Optional field # By default and for convenience, the Kubernetes control plane will allocate a port from a range (default: 30000-32767) nodePort: 30007
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376 clusterIP: 10.0.171.239 type: LoadBalancer status: loadBalancer: ingress: - ip: 192.0.2.127
apiVersion: v1 kind: Service metadata: name: registry namespace: default labels: app: registry spec: type: NodePort selector: app: registry ports: - name: registry port: 5000 nodePort: 30050 protocol: TCP --- apiVersion: apps/v1 kind: Deployment metadata: name: registry namespace: default labels: app: registry spec: replicas: 1 selector: matchLabels: app: registry template: metadata: labels: app: registry spec: containers: - name: registry image: registry:latest resources: limits: cpu: 100m memory: 100Mi env: - name: REGISTRY_HTTP_ADDR value: :5000 - name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY value: /var/lib/registry ports: - containerPort: 5000 name: registry protocol: TCP
apiVersion: v1 kind: ConfigMap metadata: name: db-config namespace: default data: db.host: 172.16.0.10 db.port: '3306' db.user: neo db.pass: chen
創建配置
neo@MacBook-Pro-Neo ~/tmp/kubernetes % kubectl create -f key-value.yaml configmap/db-config created
將配置項保存到檔案
apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-container image: gcr.io/google_containers/busybox command: [ "/bin/sh", "-c", "cat /usr/local/etc/config/db.host" ] volumeMounts: - name: config-volume mountPath: /usr/local/etc/config volumes: - name: config-volume configMap: name: db-config restartPolicy: Never
定義多組配置項
apiVersion: v1 kind: ConfigMap metadata: name: spring-cloud-config namespace: default data: config: | spring.security.user=config spring.security.user=passw0rd euerka: | spring.security.user=eureka spring.security.user=passw0rd gateway: | spring.security.user=gateway spring.security.user=passw0rd
envFrom 可將 ConfigMap 中的配置項定義為容器環境變數
apiVersion: v1 kind: Pod metadata: name: neo-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] envFrom: - configMapRef: name: special-config restartPolicy: Never
引用單個配置項使用 valueFrom
neo@MacBook-Pro-Neo ~/tmp/kubernetes % cat key-value.yaml apiVersion: v1 kind: ConfigMap metadata: name: db-config namespace: default data: db.host: 172.16.0.10 db.port: '3306' db.user: neo db.pass: chen --- apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-container image: busybox command: [ "/bin/sh", "-c", "env" ] env: - name: DBHOST valueFrom: configMapKeyRef: name: db-config key: db.host - name: DBPORT valueFrom: configMapKeyRef: name: db-config key: db.port restartPolicy: Never neo@MacBook-Pro-Neo ~/tmp/kubernetes % kubectl create -f key-value.yaml configmap/db-config created pod/test-pod created
定義配置
apiVersion: v1 kind: ConfigMap metadata: name: redis-config labels: app: redis data: redis.conf: |- pidfile /var/lib/redis/redis.pid dir /var/lib/redis port 6379 bind 0.0.0.0 appendonly yes protected-mode no requirepass 123456
引用配置
apiVersion: apps/v1 kind: Deployment metadata: name: redis labels: app: redis spec: replicas: 1 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:5.0.8 command: - "sh" - "-c" - "redis-server /usr/local/etc/redis/redis.conf" ports: - containerPort: 6379 resources: limits: cpu: 1000m memory: 1024Mi requests: cpu: 1000m memory: 1024Mi livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 300 timeoutSeconds: 1 periodSeconds: 10 successThreshold: 1 failureThreshold: 3 readinessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 timeoutSeconds: 1 periodSeconds: 10 successThreshold: 1 failureThreshold: 3 volumeMounts: - name: data mountPath: /data - name: config mountPath: /usr/local/etc/redis/redis.conf subPath: redis.conf volumes: - name: data persistentVolumeClaim: claimName: redis - name: config configMap: name: redis-config
apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-container image: gcr.io/google_containers/busybox command: [ "/bin/sh","-c","find /etc/config/" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: special-config items: - key: special.how path: path/to/special-key restartPolicy: Never
apiVersion: v1 kind: PersistentVolume metadata: name: example-pv spec: capacity: storage: 100Gi # volumeMode field requires BlockVolume Alpha feature gate to be enabled. volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: path: /mnt/disks/ssd1 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - example-node
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-volume provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer --- apiVersion: v1 kind: PersistentVolume metadata: name: netkiller-local-pv spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-volume local: path: /tmp/neo nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - minikube --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: netkiller-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: local-volume --- kind: Pod apiVersion: v1 metadata: name: busybox namespace: default spec: containers: - name: busybox image: busybox:latest # image: registry.netkiller.cn:5000/netkiller/welcome:latest imagePullPolicy: IfNotPresent command: - sleep - "3600" volumeMounts: - mountPath: "/srv" name: mypd restartPolicy: Always volumes: - name: mypd persistentVolumeClaim: claimName: netkiller-pvc
部署 POD
iMac:kubernetes neo$ kubectl create -f example/volume/local.yaml storageclass.storage.k8s.io/local-volume created persistentvolume/netkiller-local-pv created persistentvolumeclaim/netkiller-pvc created pod/busybox created
查看POD狀態
iMac:kubernetes neo$ kubectl get pod NAME READY STATUS RESTARTS AGE busybox 1/1 Running 0 2m28s
進入POD查看local卷的掛載情況,同時創建一個測試檔案。
iMac:kubernetes neo$ kubectl exec -it busybox sh kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead. / # mount | grep /srv tmpfs on /srv type tmpfs (rw) / # echo helloworld > /srv/netkiller / # cat /srv/netkiller helloworld
進入宿主主機查看掛載目錄
$ cat /tmp/neo/netkiller helloworld
.spec.completions 標誌Job結束需要成功運行的Pod個數,預設為1
.spec.parallelism 標誌並行運行的Pod的個數,預設為1
.spec.activeDeadlineSeconds 標誌失敗Pod的重試最大時間,超過這個時間不會繼續重試
apiVersion: batch/v1 kind: Job metadata: name: busybox spec: completions: 1 parallelism: 1 template: metadata: name: busybox spec: containers: - name: busybox image: busybox command: ["echo", "hello"] restartPolicy: Never
$ kubectl create -f job.yaml job "busybox" created $ pods=$(kubectl get pods --selector=job-name=busybox --output=jsonpath={.items..metadata.name}) $ kubectl logs $pods
.spec.schedule 指定任務運行周期,格式同Cron
.spec.startingDeadlineSeconds 指定任務開始的截止期限
.spec.concurrencyPolicy 指定任務的並發策略,支持Allow、Forbid和Replace三個選項
apiVersion: batch/v2alpha1 kind: CronJob metadata: name: hello spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: hello image: busybox args: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster restartPolicy: OnFailure
正常情況 Service 只是暴露了連接埠,這個連接埠是可以對外訪問的,但是80連接埠只有一個,很多 Service 都要使用 80連接埠,這時就需要使用虛擬主機技術。
多個 Service 共同使用一個 80 連接埠,通過域名區分業務。這就是 Ingress 存在的意義。
+----------+ Ingress +---------+ Pod +----------+ | internet | ---------> | Service | --------> | Pod Node | +----------+ +---------+ +----------+
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: springboot spec: backend: service: name: springboot port: number: 80
Ingress / ---> /api --> api-service:8080 www.netkiller.cn ---------> | ---> /usr --> usr-service:8080 \ ---> /img --> img-service:8080
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: uri-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: www.netkiller.cn http: paths: - path: /api backend: serviceName: api-service servicePort: 8080 - path: /usr backend: serviceName: usr-service servicePort: 8080 - path: /img backend: serviceName: img-service servicePort: 8080
www.netkiller.cn --| Ingress |-> www.netkiller.cn www:80 | --------------> | img.netkiller.cn --| |-> img.netkiller.cn img:80
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: vhost-ingress spec: rules: - host: www.netkiller.cn http: paths: - backend: serviceName: www servicePort: 80 - host: img.netkiller.cn http: paths: - backend: serviceName: img servicePort: 80
http://www.netkiller.cn/1100 => /article/1100
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: rewrite-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /article/$1 spec: rules: - host: www.netkiller.cn http: paths: # 可以有多個(可以正則) - path: /($/.*) backend: serviceName: article servicePort: 80
# 該註解只在配置了HTTPS之後才會生效進行跳轉 nginx.ingress.kubernetes.io/ssl-redirect: "true" # 強制跳轉到https,不論是否配置了https證書 nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
三種annotation按匹配優先順序順序:
canary-by-header > canary-by-cookie > canary-weight
# Release Version apiVersion: v1 kind: Service metadata: name: hello-service labels: app: hello-service spec: ports: - port: 80 protocol: TCP selector: app: hello-service --- # canary Version apiVersion: v1 kind: Service metadata: name: canary-hello-service labels: app: canary-hello-service spec: ports: - port: 80 protocol: TCP selector: app: canary-hello-service
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: canary annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "30" spec: rules: - host: canary.netkiller.cn http: paths: - backend: serviceName: canary-hello-service
$ for i in $(seq 1 10); do curl http://canary.netkiller.cn; echo '\n'; done
annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "canary"
$ for i in $(seq 1 5); do curl -H 'canary:always' http://canary.netkiller.cn; echo '\n'; done
annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "canary" nginx.ingress.kubernetes.io/canary-by-header-value: "true"
$ for i in $(seq 1 5); do curl -H 'canary:true' http://canary.netkiller.cn; echo '\n'; done