背景:

sealos安装的Kubernetes集群,删除后增加节点失败。

Error: failed to add hosts: run command /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.Master --domain sealos.hub on 10.0.0.Node:22, output: , error: Process exited with status 139 from signal SEGV,

环境:

应用 信息
os Ubuntu
sealos v4.2.2
Kubernetes v1.25.7

问题1:文件时md5校验失败

操作步骤:

sealos delete --nodes 10.0.0.X 
sealos add --nodes 10.0.0.X 

执行报错:

sha256 sum not match

Sealos 的 –debug 参数是一个全局参数,用于开启调试模式,以便在出现问题时能更详细地了解系统的运行情况。

sealos add --nodes 10.0.0.X  --debug

Sealos传输文件时比较md5查看是否报错,issues给了一个解决办法:可以关闭校验。

#  export SEALOS_SCP_CHECKSUM=false

修改完,出现了另外一个问题:

问题2:sealctl 增加host失败

Error: failed to add hosts: run command /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.Master --domain sealos.hub on 10.0.0.Node:22, output: , error: Process exited with status 139 from signal SEGV,

 debug模式查看详细

2023-06-28T09:56:43 debug show registry info, IP: 10.0.0.X:22, Domain: sealos.hub, Data: /var/lib/registry2023-06-28T09:56:43 debug start to exec /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.X --domain sealos.hub on 10.0.0.M:222023-06-28T09:56:44 error Applied to cluster error: failed to add hosts: run command /var/lib/sealos/data/default/rootfs/opt/sealctl hosts add --ip 10.0.0.X --domain sealos.hub on 10.0.0.M:22, output: , error: Process exited with status 139 from signal SEGV,2023-06-28T09:56:44 debug save objects into local: /root/.sealos/default/Clusterfile, objects: [apiVersion: apps.sealos.io/v1beta1kind: Cluster

可见,是seactl执行"sealctl hosts add –ip 10.0.0.X –domain sealos.hub" 失败。此文件是从master拷贝过来的:

/var/lib/containers/storage/overlay/5dd64dde7bc046cfd7554458e2950c41c0d86536e4e96532cfa2ee60685404d4/merged/opt to dst /var/lib/sealos/data/default/rootfs/opt2023-06-28T09:56:40 debug remote copy files src /var/lib/containers/storage/overlay/44ce75c1b3e483c61ecfbc07a72f87da8627ce40d9df9451f2d04dfad8dffc65/merged/opt to dst /var/lib/sealos/data/default/rootfs/opt2023-06-28T09:56:42 debug remote copy files src

我们试着手动拷贝过去,执行是正常的。初步判断是传输的文件出错 ,issure沟通,建议试着“scp进程可能有问题,比较md5查看是否有问题。export SEALOS_SCP_CHECKSUM = true”,还是出现 “sha256 sum not match”。无效。

解决

这个v4.2.2 的一个bug,希望下个版本能修复。最终一个临时处理办法:重置集群

# sealos reset 

我尝试重置集群,再加入node是可行的。

小结

本文主要记录排查、解决【sealos-v4.2.2删除节点后再增加节点失败】的过程,原因sealos的版本v4.2.2 问题,,其一,旧版本在拷贝seactl文件到node时会抛出失败:sha256比较文件时,经排查判断是传输过程文件出错了。其二,可以临时通过重置集群来恢复。 希望对你有所帮助。

参考

Metrics Server是一个可扩展的、高效的容器资源度量源,用于Kubernetes内置的自动伸缩管道。

kubectl top 可以查看node、pod 的实时资源使用情况:如CPU、内存。但需要预装 Metrics Server ,不然会出现如下错误提示:

kubectl top pods
error: Metrics API not available

Metrics Server 从 Kubelet 收集资源指标,并通过Metrics API在 Kubernetes apiserver 中公开, 以供Horizo​​ntal Pod Autoscaler和Vertical Pod Autoscaler使用。指标 API 也可以通过 访问kubectl top,从而更容易调试自动缩放管道。

今天主要介绍安装的过程,以及记录安装过程遇到一些问题。

开始安装

这里我们采用官网给的清单 components.yaml安装最新的 Metrics Server 版本:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

查看是否安装成功:

# kubectl get deployment metrics-server -n kube-system
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   0/1     1            0           118s

发现安装失败。

问题1:镜像拉取失败

查看pod详细信息:

# kubectl get pod  -n kube-system
NAME                              READY   STATUS         RESTARTS      AGE
metrics-server-55c774cdbb-jmfz8   0/1     ErrImagePull   0             2m34s

查看pod日志:

# kubectl logs pods/metrics-server-55c774cdbb-jmfz8 -n kube-system
Error from server (BadRequest): container "metrics-server" in pod "metrics-server-55c774cdbb-jmfz8" is waiting to start: trying and failing to pull image

从日志可知是镜像拉取不下来,我们可以修改镜像地址。可以从dockerhub,查找最新版本,如作者安装的版本是:metrics-server:v0.6.1。打开components.yaml , 修改Deployment,spec.containers.coimage 为搜索到的,如“registry.aliyuncs.com/google_containers/metrics-server:v0.6.1",修改完详细如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  strategy:
    rollingUpdate:
      maxUnavailable: 0
  template:
    metadata:
      labels:
        k8s-app: metrics-server
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --metric-resolution=15s
        image: registry.aliyuncs.com/google_containers/metrics-server:v0.6.1

更新部署deployment:

# kubectl delete  deployment metrics-server -n kube-system
deployment.apps "metrics-server" deleted

# kubectl apply -f components.yaml

问题2:证书

更新后,发现另外一个问题:

# server.go:132] unable to fully scrape metrics: unable to fully scrape metrics from node docker-desktop: unable to fetch metrics from node docker-desktop: Get "https://192.168.1.88:10250/stats/summary?only_cpu_and_memory=true": x509: cannot validate certificate for 192.168.1.88 because it doesn't contain any IP SANs

从日志可知是权限验证(证书)出了问题,通过搜索github issues ,issue回复里提供了一个解决方法是在可以关闭证书验证,详细如下:

修改清单components.yam, 增加:

- --kubelet-insecure-tls

修改完 Deplyment详细如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    k8s-app: metrics-server
  name: metrics-server
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
  strategy:
    rollingUpdate:
      maxUnavailable: 0
  template:
    metadata:
      labels:
        k8s-app: metrics-server
    spec:
      containers:
      - args:
        - --cert-dir=/tmp
        - --secure-port=4443
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --kubelet-insecure-tls
        - --metric-resolution=15s

其中:

–kubelet-preferred-address-types: 优先使用 InternalIP 来访问 kubelet,这样可以避免节点名称没有 DNS 解析记录时,通过节点名称调用节点 kubelet API 失败的情况(未配置时默认的情况);

–kubelet-insecure-tls: kubelet 的 10250 端口使用的是 https 协议,连接需要验证 tls 证书。–kubelet-insecure-tls 不验证客户端证书。

然后,检查是否启动成功:

#kubectl get deployment metrics-server -n kube-system
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   1/1     1            1           14m

success。

参考

背景:

在导入外部集群到rancher时,cattle-cluster-agent报错

level=fatal msg="looking up cattle-system/cattle ca/token: no secret exists for service account cattle-system/cattle"

环境:

应用 信息
Kubernetes v1.25.7
Rancher v2.6.6

解决

经查,K8s在版本v1.24启用新测试功能特性-LegacyServiceAccountTokenNoAutoGeneration,默认是开启的。

服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的持有者令牌来验证请求。

服务账号通常由 API 服务器自动创建并通过 ServiceAccount 准入控制器关联到集群中运行的 Pod 上。 持有者令牌会挂载到 Pod 中可预知的位置,允许集群内进程与 API 服务器通信。 服务账号也可以使用 Pod 规约的 serviceAccountName 字段显式地关联到 Pod 上。

我们可以通过k8s 组件上指定的各种特性开关控制 - 【特性门控是描述 Kubernetes 特性的一组键值对。你可以在 Kubernetes 的各个组件中使用 –feature-gates 标志来启用或禁用这些特性。】 关闭该特性:

打开 /etc/kubernetes/manifest/kube-controller-manager.yml的 spec.container.command中增加

- --feature-gates=LegacyServiceAccountTokenNoAutoGeneration=false

修改完,例如:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: kube-controller-manager
    tier: control-plane
  name: kube-controller-manager
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-controller-manager
    - --allocate-node-cidrs=true
    - --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
    - --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
    - --bind-address=0.0.0.0
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --cluster-cidr=1.2.0.0/10
    - --cluster-name=kubernetes
    - --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
    - --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
    - --controllers=*,bootstrapsigner,tokencleaner
    - --feature-gates=EphemeralContainers=true
    - --kubeconfig=/etc/kubernetes/controller-manager.conf
    - --leader-elect=true
    - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
    - --root-ca-file=/etc/kubernetes/pki/ca.crt
    - --service-account-private-key-file=/etc/kubernetes/pki/sa.key
    - --service-cluster-ip-range=10.88.0.0/22
    - --use-service-account-credentials=true
    - --feature-gates=LegacyServiceAccountTokenNoAutoGeneration=false
    ...

重启 kube-controller-manager,ps:如果是多个master,需要每个都修改。

小结

本文记录导入外部集群到rancher时,遇到的用户认证密钥不正确的问题。回顾k8s在1.23~1.25的更新日志,以及查阅issues,原因是1.24启用了新测试特性LegacyServiceAccountTokenNoAutoGeneration,该特性使用经过签名的持有者令牌来验证请求。可以通过k8s组件上指定的各种特性开关控制来关闭此特性来解决报错,最终正常导入到rancher。

参考

背景:

sealos安装的Kubernetes集群,master节点出现大量端口占用。

环境:

应用 信息
os Ubuntu
sealos v4.1.7
Kubernetes v1.23.9

排查问题

按照本地端口对输出进行排序

netstat -anp | grep ESTABLISHED | awk '{print $4}' | sort | uniq -c | sort -n
    28 10.0.0.101:6443
    111 127.0.0.1:2379
  9009 10.0.0.101:5000

查找本地端口对应的应用程序

  lsof -i :5000
  image-cri 2811249 root 4120u  IPv4 291818320      0t0  TCP master1:41710->master1:5000 (ESTABLISHED)

可见,是image-cri-shim占用的端口数。

image-cri-shim 工作原理

image-cri-shim 是一个基于 CRI (Container Runtime Interface) 和 kubelet 的 gRPC (Google Remote Procedure Call) shim。CRI 是 Kubernetes 中用于与容器运行时进行交互的接口,而 kubelet 是负责维护容器运行状态和节点级别的资源管理的 Kubernetes 组件。

常用操作:

  • 启动服务: systemctl start image-cri-shim
  • 停止服务: systemctl stop image-cri-shim
  • 重启服务: systemctl restart image-cri-shim
  • 查看服务状态: systemctl status image-cri-shim
  • 参考日志: journalctl -u image-cri-shim -f

重现问题

  1. 在测试环境安装相同版本的sealos版本4.1.7,这里就不赘述,可以参考(https://sealos.io/zh-Hans/docs/getting-started/kuberentes-life-cycle)

  2. 安装好之后,发现image-cri-shim的版本(4.1.3)不对,

image-cri-shim --version
image-cri-shim version 4.1.3-ed0a75b9
  1. 更新image-cri-shim版本为4.1.7
wget https://github.com/labring/sealos/releases/download/v4.1.7/sealos_4.1.7_linux_amd64.tar.gz && tar xvf sealos_4.1.7_linux_amd64.tar.gz.1 image-cri-shim
sealos exec -r master,node "systemctl stop image-cri-shim"
sealos scp "./image-cri-shim" "/usr/bin/image-cri-shim"
sealos exec -r master,node "systemctl start image-cri-shim"
sealos exec -r master,node "image-cri-shim -v"

输出类似以下内容,表示成功:

image-cri-shim version 4.1.7-ed0a75b9
192.168.1.101:22: image-cri-shim version 4.1.7-ed0a75b9
192.168.1.102:22: image-cri-shim version 4.1.7-ed0a75b9
  1. 启动后,观察日志:
root@master1:~# journalctl -u image-cri-shim -f
-- Logs begin at Wed 2023-04-19 22:42:32 UTC. --
May 25 06:49:38 master1 image-cri-shim[1116090]: 2023-05-25T06:49:38 info actual imageName: pause:3.6
May 25 06:49:38 master1 image-cri-shim[1116090]: 2023-05-25T06:49:38 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 06:54:38 master1 image-cri-shim[1116090]: 2023-05-25T06:54:38 info actual imageName: pause:3.6
May 25 06:54:38 master1 image-cri-shim[1116090]: 2023-05-25T06:54:38 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 06:59:38 master1 image-cri-shim[1116090]: 2023-05-25T06:59:38 info actual imageName: pause:3.6
May 25 06:59:38 master1 image-cri-shim[1116090]: 2023-05-25T06:59:38 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
  1. 监控端口数的变化
netstat -na | grep 5000 | wc -l
96
116
120
...
  1. 可见,每个5分钟,占用的端口数就增加20左右。问题复现了,接着我们来看看怎么处理。

更新版本为4.2.0

从sealos的github的issues有提供一个修复方案:升级image-cri-shim的版本为4.2.0

wget https://github.com/labring/sealos/releases/download/v4.2.0/sealos_4.2.0_linux_amd64.tar.gz && tar xvf sealos_4.2.0_linux_amd64.tar.gz image-cri-shim
sealos exec -r master,node "systemctl stop image-cri-shim"
sealos scp "./image-cri-shim" "/usr/bin/image-cri-shim"
sealos exec -r master,node "systemctl start image-cri-shim"
sealos exec -r master,node "image-cri-shim -v"

输出如下,表示成功:

image-cri-shim version 4.2.0-f696a621
192.168.1.101:22: image-cri-shim version 4.2.0-f696a621
192.168.1.102:22: image-cri-shim version 4.2.0-f696a621

观察是否已修复

  1. 观察inamge-cri-shim日志:
root@master1:~# journalctl -u image-cri-shim -f
-- Logs begin at Wed 2023-04-19 22:42:32 UTC. --
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info Timeout: {15m0s}
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info criRegistryAuth: map[]
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info criOfflineAuth: map[sealos.hub:5000:{Username:admin Password:passw0rd Auth: Email: ServerAddress:http://sealos.hub:5000 IdentityToken: RegistryToken:}]
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info socket info shim: /var/run/image-cri-shim.sock ,image: /run/containerd/containerd.sock, registry: http://sealos.hub:5000
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info changed ownership of socket "/var/run/image-cri-shim.sock" to root/root
May 25 07:44:09 master1 image-cri-shim[1159432]: 2023-05-25T07:44:09 info changed permissions of socket "/var/run/image-cri-shim.sock" to -rw-rw----
May 25 07:44:41 master1 image-cri-shim[1159432]: 2023-05-25T07:44:41 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 07:49:42 master1 image-cri-shim[1159432]: 2023-05-25T07:49:42 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
May 25 07:54:42 master1 image-cri-shim[1159432]: 2023-05-25T07:54:42 info image: k8s.gcr.io/pause:3.6, newImage: sealos.hub:5000/pause:3.6, action: ImageStatus
  1. 监控端口数的变化
root@master1:~# netstat -na | grep 5000 | wc -l
1 
6 
1
  1. 可见,请求是还有,但并没有增加端口数,看来问题修复了。

ps: 我们重现问题的Kubenetes版本是v1.23.9,在实际开发中发现其他版本的v1.25.7也会出现类似问题,验证此版本待后续更新。

小结

本文主要记录排查、复现、处理【sealos安装的Kubenetes集群master节点大量占用端口】的过程,原因系image-cri-shim的版本问题,旧版本会频繁请求master,导致创建大量的连接,占用端口数。通过升级image-cri-shim版本可以解决该问题。

参考

在 Kubernetes 中,Kubelet 是在每个节点上运行的重要组件之一,它负责管理容器的生命周期。而 CRI(Container Runtime Interface)则是 Kubelet 用于与容器运行时进行通信的接口(如下图)。

CRI 采用了 ProtoBuffer 和 gPRC,规定 kubelet 该如何调用容器运行时去管理容器和镜像,Kubernetes 通过CRI可支持多种类型的OCI容器运行时,例如 docker、contained、CRI-O、runC、fraki和Kata Containers 等)。

为了方便用户进行容器运行时的调试工作,社区提供了 crictl 工具,用于与 CRI 接口进行交互,本文简要介绍如何使用 crictl 对 Kubernetes节点进行调试 。

kubelet-layout:

安装

你可以从 cri-tools 发布页面 下载一个压缩的 crictl 归档文件,用于几种不同的架构。 下载与你的 kubernetes 版本相对应的版本。 提取它并将其移动到系统路径上的某个位置,例如 /usr/local/bin/。

  • 查看版本,验证安装
crictl --version

输出例如如下,说明安装成功:

crictl version v1.23.0 

查看或编辑配置

要查看或编辑当前配置,请查看或编辑 /etc/crictl.yaml 的内容。

cat /etc/crictl.yaml
image-endpoint: unix:///var/run/image-cri-shim.sock
runtime-endpoint: unix:///run/containerd/containerd.sock

调试节点

  • 列出运行中的容器:
crictl ps

例如我们列出k8s集群的所有容器,例如输出:

CONTAINER           IMAGE               CREATED             STATE               NAME                      ATTEMPT             POD ID
508e30da66ce7       7a71aca7b60fc       3 days ago          Running             calico-node               0                   e0ec650992997
9daa288a68426       f822f80398b9a       3 days ago          Running             calico-typha              0                   f5c4bd6471941
300d948e75019       f6bc1b780606f       3 days ago          Running             kube-controller-manager   1                   d5d681744a377
1cfdc1a6726ae       0198979b7707e       3 days ago          Running             kube-scheduler            1                   eb6ff07ees98c
3699c312c56f9       9e6a540eeeb62       3 days ago          Running             kube-proxy                0                   e8707140d12941
4159d7ec37b29       5bc0062e9555c       3 days ago          Running             kube-apiserver            0                   22d043569737f
8f56a047e8627      25f8c7f3da61c       3 days ago          Restart             etcd                      0                   458e540c798c8

本例中,etcd容器一直启动,可以使用以下命令获取容器的日志:

crictl logs container-id

如此,通过日志帮助定位问题。

更多命令

  • 列出所有的pods
crictl pods
  • 创建容器
crictl run --runtime=remote \
  docker.io/library/nginx:latest \
  nginx-container

ps:使用远程容器CRI来使用最新的 nginx 镜像启动nginx-container的容器。

  • 删除容器:
crictl rm nginx-container
  • 列出所有镜像:
crictl images
  • 帮助
 crictl -h 
NAME:
   crictl - client for CRI

USAGE:
   crictl [global options] command [command options] [arguments...]

VERSION:
   v1.23.0

COMMANDS:
   attach              Attach to a running container
   create              Create a new container
   exec                Run a command in a running container
   version             Display runtime version information
   images, image, img  List images
   inspect             Display the status of one or more containers
   inspecti            Return the status of one or more images
   imagefsinfo         Return image filesystem info
   inspectp            Display the status of one or more pods
   logs                Fetch the logs of a container
   port-forward        Forward local port to a pod
   ps                  List containers
   pull                Pull an image from a registry
   run                 Run a new container inside a sandbox
   runp                Run a new pod
   rm                  Remove one or more containers
   rmi                 Remove one or more images
   rmp                 Remove one or more pods
   pods                List pods
   start               Start one or more created containers
   info                Display information of the container runtime
   stop                Stop one or more running containers
   stopp               Stop one or more running pods
   update              Update one or more running containers
   config              Get and set crictl client configuration options
   stats               List container(s) resource usage statistics
   completion          Output shell completion code
   help, h             Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --config value, -c value            Location of the client config file. If not specified and the default does not exist, the program's directory is searched as well (default: "/etc/crictl.yaml") [$CRI_CONFIG_FILE]
   --debug, -D                         Enable debug mode (default: false)
   --image-endpoint value, -i value    Endpoint of CRI image manager service (default: uses 'runtime-endpoint' setting) [$IMAGE_SERVICE_ENDPOINT]
   --runtime-endpoint value, -r value  Endpoint of CRI container runtime service (default: uses in order the first successful one of [unix:///var/run/dockershim.sock unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]). Default is now deprecated and the endpoint should be set instead. [$CONTAINER_RUNTIME_ENDPOINT]
   --timeout value, -t value           Timeout of connecting to the server in seconds (e.g. 2s, 20s.). 0 or less is set to default (default: 2s)
   --help, -h                          show help (default: false)
   --version, -v                       print the version (default: false)

以上。

参考

本文主要介绍Docker、Go、PostgreSQL如何修改它们的时区。

首先需要知道一些基础概念:

  1. Unix 时间戳 -是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
  2. UTC –协调世界时,又称世界统一时间、世界标准时间、国际协调时间。
  3. CST–可视为中国、古巴的标准时间或美国、澳大利亚的中部时间。北京时间,也就是东八区时间。

Docker

Docker 作为部署和运行应用程序的环境,默认使用 UTC 作为其容器的时区,但我们可以通过设置环境变量来修改时区。

修改的方法

  1. 在 Dockerfile 中添加以下行:
ENV TZ=Asia/Shanghai
  1. 在 Kubernetes 中的 Pod 配置文件中,添加 env 字段,设置环境变量。Example:
spec:
  containers:
  - name: my-container
    image: my-image
    env:
      - name: TZ
        value: Asia/Shanghai
  1. 验证在容器中使用 env 命令查看环境变量,例如
env

输出会有key为TZ,value为Asia/Shanghai表示成功

TZ=Asia/Shanghai

Go

在 Go 中修改时区需要使用标准库中的 time 包。我们可以通过FixedZone来修改时区。 Example:

package main

import (
	"fmt"
	"time"
)

func main() {
	var utcZone = time.FixedZone("UTC", 0*3600) // UTC
	time.Local = utcZone
	utcNow := time.Now()
	utcDate := time.Date(utcNow.Year(), utcNow.Month(), utcNow.Day(), 0, 0, 0, 0, utcNow.Location())
	fmt.Printf("UTC time: %s\n", utcDate.String())
	fmt.Printf("UTC timestamp: %d\n", utcDate.Unix())

	var cstZone = time.FixedZone("CST", 8*3600) // 东八
	time.Local = cstZone
	cstNow := time.Now()
	cstDate := time.Date(cstNow.Year(), cstNow.Month(), cstNow.Day(), 0, 0, 0, 0, cstNow.Location())
	fmt.Printf("CST time: %s\n", cstDate.String())
	fmt.Printf("CST timestamp: %d\n", cstDate.Unix())
}
//output
//UTC time: 2023-02-25 00:00:00 +0000 UTC
//UTC timestamp: 1677283200
//CST time: 2023-02-25 00:00:00 +0800 CST
//CST timestamp: 1677254400

在上面的代码中,我们使用time.FixedZone分别设置UTC、上海时区,并获取当天零点的时间戳 _date。

PostgreSQL

在PostgreSQL系统内部,所有日期和时间都用全球统一时间UTC格式存储, 时间在发给客户前端前由数据库服务器根据TimeZone 配置参数声明的时区转换成本地时间

在 PostgreSQL 中,我们可以通过修改 postgresql.conf 文件来修改时区。 以下是如何在 Docker运行 PostgreSQL 中修改时区的步骤:

  1. 拷贝dockers中的 postgresql.conf到宿主主机
sudo docker cp [your_docker_contariner_id]:/var/lib/postgresql/data/postgresql.conf /[your_work_space]/
  1. 修改配置
sudo vi /[your_work_space]/postgresql.conf

查找替换timezone为上海时区

timezone = 'Asia/Shanghai'

可以通过sql查找支持的时区:

select * from pg_timezone_names;
  1. 保存并覆盖dockers中配置
sudo docker cp /[your_work_space]/postgresql.conf [your_docker_contariner_id]:/var/lib/postgresql/data/
  1. 重新容器
sudo docker restart [your_docker_contariner_id

现在,你的 PostgreSQL 数据库就使用了正确的时区。

  1. 检查是否设置成功 通过sql获取设置:
select * from pg_db_role_setting;

也可以查看数据库中表字段格式为TimestampTZ,Example 修改前

2023-02-25 00:00:00 +0000 +00

修改后台

2023-02-25 08:00:00 +0000 +08

以上。

参考

本文主要学习记录Kubernetes集群暴露服务的方式: Ingress。

简介

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。

IngressController 为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

上图清晰标识出了Ingress的流量走向,其中:

  • Ingress基于DNS名称(host)或URL路径把请求转发⾄指定的Service资源的规则。它仅是⼀组路由规则的集合。
  • Ingress控制器是真正实现“流量穿透”,可以由具有反向代理(HTTP/HTTPS)功能的服务程序实现 , 然后根据这些规则的匹配机制路由请求流量

Ingress 资源声明

Ingress是Kubernetes API的标准资源类型之⼀ ,一个最小Ingress例子:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

其中:

  • Ingress 需要指定 apiVersion、kind、 metadata和 spec 字段。
  • Ingress 对象的命名必须是合法的 DNS 子域名名称。
  • Ingress annotations 来配置一些选项, ⽤于识别其所属的Ingress控制器的类别。
  • Ingress rules 提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP(S) 流量的规则。

更多参考: https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/

Ingress Controller

Ingress控制器可以由任何具有反向代理(HTTP/HTTPS)功能的服务程序实现,目前支持和维护 AWS、 GCE 和 Nginx Ingress 控制器。

  • Nginx Ingress 作为反向代理和负载均衡器。
  • Apache APISIX Ingress 控制器 是一个基于 Apache APISIX 网关 的 Ingress 控制器。

更多参考:https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress-controllers/

安装 Ingress Nginx

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/cloud/deploy.yaml  -O ingress-nginx-deploy.yaml

kubectl apply -f  ingress-nginx-deploy.yaml

观测是否成功:

kubectl get pods -n ingress-nginx --watch

如果成功的话,查看namespace下所有的资源信息

root@master:/home/master# kubectl get all -n ingress-nginx
NAME                                            READY   STATUS    RESTARTS   AGE
pod/ingress-nginx-controller-123456   1/1     Running   0          1h

NAME                                         TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
service/ingress-nginx-controller             LoadBalancer   10.104.182.98    192.168.1.100   80:31666/TCP,443:31888/TCP   1d
service/ingress-nginx-controller-admission   ClusterIP      10.111.228.99   <none>         443/TCP                      1d

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           1d

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-123456   1         1         1      1d

NAME                                       COMPLETIONS   DURATION   AGE
job.batch/ingress-nginx-admission-create   1/1           1s         1d
job.batch/ingress-nginx-admission-patch    1/1           2s         1d

使用Ingress 发布 Tomcat

部署Tomcat Service

  1. 创建deployment

        kubectl create deployment web --image=tomcat:8.0.50-jre8-alpine
    
  2. 将 Deployment 暴露出来

    kubectl expose deployment web --type=NodePort --port=8080
    
    
  3. 将 Deployment 暴露出来

    root@master:/home/master# kubectl get service web
        NAME   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
        web    NodePort   10.100.183.22   <none>        8080:32562/TCP   30s
    
  4. 验证nodeport 是否正常访问 tomcat ,浏览器访问 http://matster_ip:32562

  5. 创建 ingress

 apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: ingressfoo
      annotations:
        kubernetes.io/ingress.class: "nginx"
      namespace: default
    spec:
      rules:
      - host: ingressfoo.io
        http:
          paths:
          - backend:
              service:
                name: web
                port:
                  number: 8080
            path: /bar
            pathType: Prefix

查看ingress是否创建成功

root@master:/home/master# kubectl get ingress
NAME         CLASS    HOSTS                     ADDRESS        PORTS   AGE
ingressfoo   <none>   ingressfoo.io   192.168.1.100   80      1h

说明Ingress创建成功,修改hosts :

ingressfoo.io 192.168.1.100

验证访问 ingressfoo.io/bar

success

参考

本文主要介绍Kubernetes集群资源监控机制和资源指标 前言 临近双11,对于码农,尤其是后端,尤其是某宝某东的后端,那是多么激动人心(心惊胆战)的一夜,为什么?怕宕机呀~。那么就让我们来构建护城河–监控与自动扩容,来抵挡千军万马–高并发场景。首先让我们学习一些基础:k8s集群资源监控机制和资源指标。 分析 从需求出发,我们自然需要收集一些数据(资源指标),再根据指标做一系列的操作(control),比如说预警警告、统计、自动扩容等。 首先,我们希望可以监控整个Kubernetes集群的健康状况,包括: 整个集群的资源利⽤率 集群中的所有⼯作节点是否运⾏正常、系统资源容量⼤⼩ 每个⼯作节点上运⾏的容器化应⽤的数量 k8s资源控制 我们看看k8s如何资源控制 限制节点 以购物平台为例,微服务广受推崇,比如双11当天,用户进首页是流畅的,但是进活动主页就卡顿,到了零点时,加入购物车的按钮都转圈了。设想你的设计可能是三个微服务(主页服务 、双十一活动服务、订单服务),可想而知,活动服务是压力最大的,我们希望,就算宕机,不要影响其他的服务。所以可以把活动服务限制运行在某节点。 创建一个会被调度到特定节点上的 Pod,你也可以通过设置 nodeName 将某个 Pod 调度到特定的节点 nodeName: foo-node # 调度 Pod 到特定的节点 限制内存 还是上面的例子,我们把活动服务调度到了 foo-node。那么剩下的服务没有去限制,但想想订单服务的压力也不小,这里我们希望限制它的资源上限。 要为容器指定内存请求,请在容器资源清单中包含 resources:requests 字段。 同理,要指定内存限制,请包含 resources:limits。 resources: requests: memory: "1000Gi" limits: memory: "1000Gi" 限制CPU 要为容器指定 CPU 请求,请在容器资源清单中包含 resources: requests 字段。 要指定 CPU 限制,请包含 resources:limits resources: limits: cpu: "100" requests: cpu: "100" Example 环境准备: k8s集群(master * 1,node * 2 ) kubectl 命令行工具( 笔者使用rancher) 创建一个namespace(stress)。 增加一个deployment, 修改内存上限为10M 1.

前两篇(1.volume2.pv&pvc)通过部署redis学习实战了k8s的来Volume、PV和PVC。但是,应⽤程序存在“有状态”和“⽆状态”两种类别,显然redis属于读写磁盘需求的有状态应⽤程序,如⽀持事务功能的RDBMS存储系统,所以,本文学习实战k8s有状态应用的部署。

Stateful基础

StatefulSet是Pod资源控制器的⼀种实现,⽤于部署和扩展有状态应⽤的Pod资源,确保它们的运⾏顺序及每个Pod资源的唯⼀性。适用以下需求的应用:

  • 稳定且唯⼀的⽹络标识符。
  • 稳定且持久的存储。
  • 有序、优雅地部署和扩展。
  • 有序、优雅地删除和终⽌。
  • 有序⽽⾃动地滚动更新。

部署

接着,这里把之前的redis存储修改为stateful的方式,修改后的步骤:

  1. 创建 ConfigMap (参考第一篇)
  2. 修改 Deployment 为StatefulSets

修改部署StatefulSets

mkdir my-redis-statefulsets.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: my-ns
spec:
  replicas: 1
  serviceName: redis
  selector:
    matchLabels:
      name: redis
  template:
    metadata:
      labels:
        name: redis
    spec:
      containers:
        - name: redis
          image: redis
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
#          command: ["redis-server","/etc/redis/redis.conf"]
          command:
            - redis-server
            - /etc/redis/redis.conf
          ports:
            - containerPort: 6379
          volumeMounts:
            - name: my-redis-config
              mountPath: /etc/redis/redis.conf
              subPath: redis.conf
            - name: my-redis-storage
              mountPath: /data
      volumes:
        - name: my-redis-storage
          emptyDir: {}
        - name: my-redis-config
          configMap:
            name: my-redis-config
            items:
              - key: redis.conf
                path: redis.conf
 
---
kind: Service
apiVersion: v1
metadata:
  labels:
    name: redis
  name: redis
  namespace: my-ns
spec:
  type: NodePort
  ports:
  - name: redis
    port: 6379
    targetPort: 6379
    nodePort: 30379
  selector:
    name: redis          

其中:

  1. Headless Service:⽤于为Pod资源标识符⽣成可解析的DNS资源记录
  2. StatefulSet ⽤于管控Pod资源
  3. volumeClaimTemplate则基于静态或动态的PV供给⽅式为Pod资源提供 专有且固定的存储(这里我们直接使用了第二篇创建的pv)

测试

redis-client 连接 NodeId:NodePort

# redis-cli -h YourNodeIp-p 30379 -a 123456
YourNodeIp:30379> info
# Serverredis_version:7.0.4

连接成功!

参考

之前学习实践使用熟悉卷(Volume)来存储利用k8s储存卷来部署redis,本文接着学习实践k8s的存储,主要通过redis存储例子学习实战PV和PVC。

PV & PVC

Kubernetes为例⽤户和开发隐藏底层架构的⽬标,在用户和存储服务之间添加了一个中间层,也就是PersistentVolume和PersistentVolumeClaim。

RedisVolume修改为PV&PVC

接着,这里把之前的redis存储修改为pv-pvc的方式,修改后的步骤:

  1. 创建 ConfigMap (参考第一篇)
  2. 增加声明PV和PVC (新增)
  3. 增加 Deployment (把Volume修改为 步骤2 的PVC)
  4. 暴露 Service (参考第一篇)

步骤2,增加声明PV和PVC

mkdir my-redis-pv-pvc.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-redis-pv
  labels:
     app: my-redis
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: "/mnt/data/my-redis"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: redis-pvc
  namespace: my-ns
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

执行创建

master# kubectl apply -f my-redis-pv-pvc.yaml

查看pv状态

master# kubectl get pv 
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM     STORAGECLASS    REASON   AGE
my-redis-pv     1Gi        RWO            Retain           Bound    my-ns/redis-pvc                                                 50m

查看pvc状态

master# kubectl get pvc -n my-ns
NAME               STATUS   VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS   AGE
redis-pvc          Bound    my-redis-pv    1Gi             RWO                                          54m

pv和pvc已经Bound成功。

步骤3,增加 Deployment (把Volume修改为 步骤2 的PVC)

mkdir my-redis-deployment-pvc.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-redis # Unique name for the deployment
  namespace: myns
  labels:
    app: my-redis       # Labels to be applied to this deployment
spec:
  selector:
    matchLabels:     # This deployment applies to the Pods matching these labels
      app: my-redis
      role: master
      tier: backend
  replicas: 1        # Run a single pod in the deployment
  template:          # Template for the pods that will be created by this deployment
    metadata:
      labels:        # Labels to be applied to the Pods in this deployment
        app: my-redis
        role: master
        tier: backend
    spec:            # Spec for the container which will be run inside the Pod.
      containers:
        - name: my-redis
          image: redis
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
#          command: ["redis-server","/etc/redis/redis.conf"]
          command:
            - redis-server
            - /etc/redis/redis.conf
          ports:
            - containerPort: 6379
          volumeMounts:
            - name: my-redis-config
              mountPath: /etc/redis/redis.conf
              subPath: redis.conf
            - name: my-redis-storage
              mountPath: /data
      volumes:
        - name: my-redis-persistent-storage 
          persistentVolumeClaim:
          claimName: redis-pvc # 这里修改为步骤2声明的pvc
        - name: my-redis-config
          configMap:
            name: my-redis-config
            items:
              - key: redis.conf
                path: redis.conf

执行创建deployment

master# kubectl apply -f my-redis-deployment-pvc.yaml

检查状态

master# kubectl get pod -n my-ns
NAME                                   READY   STATUS    RESTARTS   AGE 
my-redis-6565459689-mbptf              1/1     Running   0          53m

测试

redis-client 连接 NodeId:NodePort

# redis-cli -h YourNodeIp-p 30379 -a 123456
YourNodeIp:30379> info
# Server
redis_version:7.0.4

连接成功。

参考

jefffff

Stay hungry. Stay Foolish COOL

Go backend developer

China Amoy