• [技术干货] Debian13系统更新后启动卡住问题的处理
    前言PC安装的操作系统是Debian 13,显卡为Nvidia。更新系统内核版本后,启动卡住,显示停在cups.service就不动了,同时显示dkms.service和nvidia-persistenced.service的状态都是 Failed。如图处理步骤开机时在grub启动菜单的Debian启动项上按"e",进入grub编辑模式,找到 linux 开头的行,在该行末尾添加3。3和前面的单词之间至少一个空格。然后按Ctrl + X或F10保存,自动重启。重启后会进入命令行界面,用root用户登录。安装内核对应版本的linux-headerapt install -y linux-header-$(uname -r)清理所有nvidia相关的程序apt purge *nvidia* apt autoremove -y重新安装nvidia驱动apt install -y nvidia-detect apt install -y nvidia-driver firmware-misc-nonfree重启shutdown -r now
  • [技术干货] [prometheus]监控postgres
    前言相关Repo: https://github.com/prometheus-community/postgres_exporter本文使用的postgres_exporter的版本为 0.16.0,postgres的版本为15.7步骤在db中创建用户并授权。create user exporter encrypted password '123456'; ALTER user exporter SET search_path TO pg_catalog, public; GRANT CONNECT ON DATABASE postgres TO exporter; GRANT pg_monitor to exporter; -- 不确定是否需要做 -- GRANT USAGE ON SCHEMA pg_catalog TO exporter; -- GRANT SELECT ON pg_stat_statements TO exporter;(可选,建议)安装 pg_stat_statements。具体操作可查询其它文档。安装完后可以切exporter用户执行SELECT * FROM pg_stat_statements LIMIT 1; 测试 exporter 用户能否正常访问 pg_stat_statements 的视图。启动。编写了一个脚本来启动。更多 postgres_exporter 的启动参数可以参考./postgres_exporter --help#!/bin/bash export DATA_SOURCE_URI="localhost:5432/postgres?sslmode=disable" export DATA_SOURCE_USER="exporter" export DATA_SOURCE_PASS="123456" script_dir=$(cd $(dirname $0) && pwd) app_name="postgres_exporter" is_running() { ps -ef | grep -v grep | grep "${script_dir}/${app_name}" > /dev/null if [ $? -eq 0 ]; then echo "${script_dir}/${app_name} is running" return 0 else echo "${script_dir}/${app_name} is not running" return 1 fi } start_app() { is_running if [ $? -eq 0 ]; then return 0 fi echo "starting ${script_dir}/${app_name}" nohup ${script_dir}/${app_name} \ --collector.postmaster \ --collector.stat_statements > ${script_dir}/app.log 2>&1 & } stop_app() { is_running if [ $? -eq 1 ]; then return 0 fi echo "stopping ${script_dir}/${app_name}" kill $(ps -ef | grep -v grep | grep "${script_dir}/${app_name}" | awk '{print $2}') } restart_app() { stop_app sleep 1 start_app } main() { if [ ! -f "${script_dir}/${app_name}" ]; then echo "${script_dir}/${app_name} not found" fi local action=$1 if [ x"$action" == "x" ]; then local action="start" fi case "$action" in start) start_app ;; stop) stop_app ;; restart) restart_app ;; status) is_running ;; *) echo "Usage: {start|stop|restart|status}" exit 1 ;; esac } main $@编辑prometheus.yml,添加收集postgres_exporter。注意替换路径# ... scrape_configs: - job_name: "postgres" file_sd_configs: - files: ['/etc/prometheus/sd_configs/postgres/*.yaml'] refresh_interval: 10s编辑/etc/prometheus/sd_configs/postgres/pg.yaml- targets: ['192.168.0.201:9187'] labels: instance: 192.168.0.201发送 hup 信号给 prometheus 或重启 prometheusgrafana 导入 dashboard。在grafana官网找的ID为 9628 的dashboard(可选)没事干的话,可以跑个 pgbench,观察基准测试下的 pg metrics。不过再没事干也别拿生产在用的DB跑基准测试。
  • [技术干货] [kubernetes]使用kubeadm和containerd部署kubernetes
    前言因宿主机内核版本限制和垂直伸缩特性的需要,安装的k8s版本为1.25,runtime为containerd,cni为calico。containerd、kubeadm、kubelet也可以用包管理器来安装,因为不想配repo,也不想校验repo版有哪些区别,所以这几个都是用原生二进制方式安装的。环境信息IPHostnameOS VersionKernel VersionComment192.168.0.11node1centos 7.93.10control panel192.168.0.12node2centos 7.93.10worker node192.168.0.13node3centos 7.93.10worker node用到的组件版本信息containerd: 1.7.21k8s: 1.25.16系统初始化系统初始化步骤要求每个节点都要操作,一些主机名等信息需要根据实际修改。修改hostname,k8s要求每个节点的hostname不一样hostnamectl set-hostname node1 hostnamectl set-hostname node2 hostnamectl set-hostname node3(可选)如果没有dns可以让hostname之间直接访问,需要配置/etc/hosts192.168.0.11 node1 192.168.0.12 node2 192.168.0.13 node3(可选)如果要长时间使用,最好配置一下时间同步。关闭swap。默认情况下,k8s检测到swap就会异常退出,导致node上的k8s启动失败。# 临时关闭。永久关闭需要修改 /etc/fstab swapoff -a装载内核模块。如果不装载br_netfilter,下一步配置系统参数会报错。# 添加配置 cat << EOF > /etc/modules-load.d/containerd.conf overlay br_netfilter EOF # 立即装载 modprobe overlay modprobe br_netfilter # 检查装载。如果没有输出结果说明没有装载。 lsmod | grep br_netfilter配置系统参数。编辑/etc/sysctl.conf文件或/etc/sysctl.d/目录下的文件,添加或修改以下配置。编辑完成后执行sysctl -p使配置生效。(如果修改的是/etc/sysctl.d目录下的文件,sysctl -p需要指定文件名才能生效)net.ipv4.ip_forward=1 net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 vm.swappiness = 0(可选)内核版本若高于4.1,可考虑使用ipvs来增强网络通信性能安装依赖,否则安装containerd时,runc组件会有问题# conntrack-tools的版本为1.4.4-4.el7,如果低了可能会导致runc异常 # 如果安装提示缺少依赖,而依赖在centos 7的repo源里没有,可从高版本centos、alma等发行版下载rpm包再安装 yum install -y conntrack-tools安装containerd从 cid:link_0下载二进制包解压压缩包到根目录。压缩包里面的文件路径都已经按照根目录组织好了,所以直接解压到根路径就行了。tar xf cri-containerd-cni-1.7.21-linux-amd64.tar.gz -C /生成containerd的配置文件mkdir /etc/containerd containerd config default > /etc/containerd/config.toml编辑containerd的配置文件/etc/containerd/config.toml,主要修改container的数据目录,并启用systemd的cgroup# 修改数据存储目录 root = "/home/apps/containerd" # 对于使用systemd作为init system的linux发行版,官方建议用systemd作为容器cgroup driver # false改成true SystemdCgroup = true重加载systemd配置,启动containerdsystemctl daemon-reload systemctl start containerd systemctl enable containerd简单验证下containerd是否正常# 查看systemd status systemctl status containerd # 查看image,正常情况下还没有image crictl images # 验证runc是否正常,如果输出有报错,可以参考"系统初始化"的第8步"安装依赖" runc --version安装kubelet和kubeadm本节步骤在所有k8s node都要操作。k8s的二进制文件安装包可以从github下载:cid:link_1在changelog中找到二进制包的下载链接,下载server binary即可,里面包含了master和node的二进制文件。解压下载好的压缩包,并将其中的二进制文件放到环境变量PATH目录下tar xf kubernetes-server-linux-amd64.tar.gz cd kubernetes/server/bin/ find . -type f -perm /u+x -exec cp {} /usr/local/bin/ \;新建或编辑kubelet的service文件 /usr/lib/systemd/system/kubelet.service[Unit] Description=kubelet: The Kubernetes Node Agent Documentation=https://kubernetes.io/docs/ Wants=network-online.target After=network-online.target [Service] ExecStart=/usr/local/bin/kubelet Restart=always StartLimitInterval=0 RestartSec=10 [Install] WantedBy=multi-user.target创建目录mkdir -p /usr/lib/systemd/system/kubelet.service.d/新建或编辑文件/usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf# Note: This dropin only works with kubeadm and kubelet v1.11+ [Service] Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" # This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env # This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use # the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file. EnvironmentFile=-/etc/sysconfig/kubelet ExecStart= ExecStart=/usr/local/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS启动kubeletsystemctl enable --now kubelet创建集群创建集群的步骤在control panel节点操作即可。初始化集群,这里主要指定k8s版本,可根据需求,参考kubeadm init --help提示配置初始化参数,比如pause镜像地址,pod ip范围等。kubeadm init --kubernetes-version v1.25.16如果init报错失败,可以查下containerd和kubelet的日志。失败后可以重置下kubeadm resetsystemctl status containerd journalctl -xeu containerd systemctl status kubelet journalctl -xeu kubelet如果init成功,控制台会输出worker node加入cluster的命令,将这个命令粘贴到worker node执行即可,比如:kubeadm join 192.168.0.11:6443 --token 123456 \ --discovery-token-ca-cert-hash sha256:123456init成功后,输出还会提示创建kubeconfig,根据提示操作即可mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config配置网络插件。k8s需要cni插件才能让pod之间正常通信,这里用的是calico插件# 下载配置文件。下载后可根据需求修改其中的image地址 wget https://docs.projectcalico.org/manifests/calico.yaml # 部署calico kubectl apply -f calico.yaml检查网络插件部署效果。只有全部ready都为1/1才算成功kubectl get nodes kubectl get pod -n kube-system测试集群部署完成后,可以起个pod试下能否正常调度# 创建一个pod。nginx需要提前docker pull kubectl create deployment nginx --image=nginx # 暴露端口 kubectl expose deployment nginx --port=80 --type=NodePort # 查看状态 kubectl get pods,svc
  • [技术干货] [linux]mark-tool
    前言在远程连接到linux进行操作时,经常要切换目录,有些目录切换频次较高,因此写了个shell工具,用于收藏目录、切换目录等。也不需要安装,直接添加脚本即可。配置首先声明脚本是基于bash shell,zsh和fish未经测试。编辑文件~/.bash_custom_functions,添加以下内容mark(){ local markfile="$HOME/.mark" if [ ! -f ${markfile} ]; then echo "${markfile} is not exist, create it." touch ${markfile} fi case $1 in l) cat -n ${markfile} ;; d) # delete if [ "x" == "x$2" ]; then echo "need a sequence number" return 1 fi if [ $2 -lt 0 ] || [ $2 -gt $(wc -l < ${markfile}) ]; then echo "invalid sequence number" return 1 fi sed -i "$2d" ${markfile} ;; a) # add / append local new_mark=$(pwd) local tmp_grep=$(grep ${new_mark} ${markfile}) if [ "x" == "x${tmp_grep}" ]; then echo "mark current dirname: ${new_mark}" echo ${new_mark} >> ${markfile} else echo "Warning! Duplicate mark" fi ;; c) # cd if [ "x" == "x$2" ]; then echo "need a sequence number" return 1 fi if [ $2 -lt 0 ] || [ $2 -gt $(wc -l < ${markfile}) ]; then echo "invalid sequence number" return 1 fi local target_dir=$(sed -n "$2p" ${markfile}) if [ ! -d ${target_dir} ]; then echo "${target_dir} not found" return 1 fi cd ${target_dir} ;; *) echo "Options: l(ist), a(dd), d(elete), c(hange)" esac }在~/.bashrc中追加以下内容以引入上一步的文件if [ -f ~/.bash_custom_functions ]; then . ~/.bash_custom_functions fisource生效,或者重建shell会话source ~/.bashrc
  • [技术干货] nginx参数调优能提升多少性能
    前言nginx安装后一般都会进行参数优化,网上找找也有很多相关文章,但是这些参数优化对Nginx性能会有多大影响?为此我做个简单的实验测试下这些参数能提升多少性能。声明一下,测试流程比较简单,后端服务也很简单,测试时间也很短,所以实验并不严谨,结果仅作参考,需要根据实际情况进行参数调优。文章或有错误和疏漏之处,欢迎各位大佬指出或补充。环境IP操作系统CPU内存部署服务192.168.3.60Debian 11.844 GBwrk192.168.3.61Debian 11.844 GBnginx192.168.3.62Debian 11.844 GB后端服务nginx:版本1.24.0,编译参数:./configure --with-threads --with-file-aio --with-http_ssl_module --with-http_v2_module --with-http_gunzip_module --with-http_gzip_static_module --with-stream --with-compat --with-pcre-jit --prefix=/home/admin/apps/nginx使用wrk进行性能测试,版本为 4.1.0,通过 apt 包管理器安装。因为主要测试nginx反向代理的性能,所以用go写了个响应"hello world"的api,减少后端服务导致的性能影响。测试方法:调整nginx参数后多次运行wrk,取平均值。(方法并不严谨,应该用更专业的工具测试运行几小时,将测试数据采用更科学的方法汇总,但时间精力有限,所以采用这个非常简单无脑的实验方法)实验结果下面的实验过程主要就是调参数,比较繁琐,所以把实验结果先放到前面。综合配置可参考“实验过程 - 13. 综合调优”。再次声明,由于测试流程和后端逻辑都比较简单,服务器和网络情况也没严格控制变量所以结果仅供参考。根据实验结果来看,增大工作进程数能直接提升性能,但不是和CPU核心数一致就能最大化,可能少一点才能达到最佳性能。除了nginx和系统参数调优,网络和后端服务对性能的影响也很大,而且在大部分ToB业务场景下,后端服务和数据库才是性能短板。序号测试方式Nginx参数优化项总请求数平均每秒请求数平均延迟优化效果1wrk -> 后端无413998468884.591.66ms+673%2wrk -> nginx -> 后端无,默认配置5348598911.3012.04ms-3.1wrk -> nginx -> 后端设置工作进程数为2102774517127.495.95ms+92.19%3.2wrk -> nginx -> 后端设置工作进程数为367665111274.058.97ms+26.51%3.3wrk -> nginx -> 后端设置工作进程数为auto(4)5477949125.6611.14ms+2.41%4wrk -> nginx -> 后端设置工作进程数和CPU亲和性为auto5377138958.1011.67ms+0.52%5wrk -> nginx -> 后端在4的基础上设置worker_connections 65535;5327588874.8511.80ms-0.4%6wrk -> nginx -> 后端在5的基础上设置accept_mutex on;4255407088.3915.58ms-20.45%7wrk -> nginx -> 后端在6的基础上设置multi_accept on5915009854.7710.60ms+10.58%8wrk -> nginx -> 后端在7的基础上设置改为upstream5586799308.3012.00ms+4.45%9wrk -> nginx -> 后端在8的基础上设置keepalive63267310541.4910.06ms+18.29%10wrk -> nginx -> 后端在9的基础上设置加一个后端100648516772.086.53ms+88.21%11wrk -> nginx -> 后端在2的基础上设置加一个后端61088210178.2610.21ms+14.21%12wrk -> nginx -> 后端在3.1的基础上设置keepalive104102417348.365.94ms+94.67%13wrk -> nginx -> 后端在2的基础上设置deferred5961979934.6110.90ms+11.48%14wrk -> nginx -> 后端在2的基础上修改内核参数5815359689.9110.95ms+8.73%15wrk -> nginx -> 后端综合调优108715118115.785.94ms+103.28%单独测试nginx的性能,避免后端服务和网络情况的影响。序号Nginx参数优化项总请求数平均每秒请求数平均延迟优化效果1无,默认配置232740038787.612.71ms-2在1的基础上设置工作进程数为auto7418729123633.13791.04us218.74%3在2的基础上设置CPU亲和性7437087123945.45784.02us219.54%4在3的基础上设置工作进程连接数和多请求7638947127300.44764.67us228.19%调整环境,nginx都采用默认配置,只是修改了各组件的位置。因为组件在同一台服务器,资源竞争情况也会影响性能。环境总请求数平均每秒请求数平均延迟wrk、nginx和后端各在不同的服务器5348598911.3012.04mswrk单独服务器,nginx和后端在同一台服务器3866306441.0516.24mswrk、nginx和后端在同一台服务器4021636700.3815.15ms实验过程1. 直连后端测试首先用wrk直接测试后端。因为没有中间商赚差价,所以理论上直连性能会比nginx代理的性能高。# curl 测试后端响应是否正常 curl http://192.168.3.62:8101 # wrk 直接测试后端服务。线程数为4,连接数为100,测试时间为60秒。 wrk -t4 -c100 -d60s http://192.168.3.62:8101wrk测试结果总请求数平均每秒请求数平均延迟413998468884.591.66ms2. 使用nginx默认配置代理nginx刚安装后有一个默认配置,这里只改了location /的配置,修改为反向代理到后端服务location / { #root html; #index index.html index.htm; proxy_pass http://192.168.3.62:8101; }wrk测试结果。相较于后端直连,性能缩水很多总请求数平均每秒请求数平均延迟5348598911.3012.04ms3. 增加工作进程数nginx默认工作进程数为1,通过修改worker_processes可指定,一般小于或等于CPU核心数worker_processes总请求数平均每秒请求数平均延迟对比默认配置1(默认)5348598911.3012.04ms-2102774517127.495.95ms+92.19%367665111274.058.97ms+26.51%auto(4)5477949125.6611.14ms+2.41%4. 设置CPU亲和性通过worker_cpu_affinity绑定工作进程和CPU,避免nginx进程在CPU之间切换导致的伪共享带来的性能问题。nginx配置:worker_processes auto; worker_cpu_affinity auto;wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置5377138958.1011.67ms+0.52%5. 设置worker_connectionsworker_connections用于设置每个Nginx进程可处理并发连接的最大数,默认为1024。worker_processes auto; worker_cpu_affinity auto; events { worker_connections 65535; }wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置5327588874.8511.80ms-0.4%6. 启用互斥锁nginx配置worker_processes auto; worker_cpu_affinity auto; events { worker_connections 65535; accept_mutex on; }wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置4255407088.3915.58ms-20.45%7. 启用多请求支持默认情况下,每个工作进程一次只接受一个新连接。开启后,每个工作进程将接受所有的新连接。nginx配置worker_processes auto; worker_cpu_affinity auto; events { worker_connections 65535; accept_mutex on; multi_accept on; }wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置5915009854.7710.60ms+10.58%8. 使用upstream之前的配置都通过proxy_pass直接反向代理到后端,修改为upstream。nginx配置worker_processes auto; worker_cpu_affinity auto; events { worker_connections 65535; accept_mutex on; multi_accept on; } http { upstream backend { server 192.168.3.62:8101; } server { location / { proxy_pass http://backend; } } }wrk测试结果。性能有所降低,但在多个后端的情况下,还是配置upstream更方便。总请求数平均每秒请求数平均延迟对比默认配置5586799308.3012.00ms+4.45%9. 设置keepalive长连接长连接的存在可以减少建立和关闭TCP连接带来的消耗和延迟。nginx配置worker_processes auto; worker_cpu_affinity auto; events { worker_connections 65535; accept_mutex on; multi_accept on; } http { upstream backend { server 192.168.3.62:8101; keepalive 32; keepalive_requests 2000; } server { location / { proxy_pass http://backend; } } }wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置63267310541.4910.06ms+18.29%10. 增加后端实例数分别在默认配置和上一步的基础上,将后端实例数加1。修改后的nginx配置worker_processes auto; worker_cpu_affinity auto; events { worker_connections 65535; accept_mutex on; multi_accept on; } http { upstream backend { server 192.168.3.62:8101; server 192.168.3.62:8102; keepalive 32; keepalive_requests 2000; } server { location / { proxy_pass http://backend; } } }wrk测试结果配置总请求数平均每秒请求数平均延迟对比默认配置默认配置多后端61088210178.2610.21ms+14.21%默认配置,长连接,工作进程数2104102417348.365.94ms+94.67%修改配置多后端100648516772.086.53ms+88.21%11. 延迟处理新连接设置deferred参数可延迟处理新连接,加上这个配置后,当用户与nginx服务器建立连接时,只有用户有请求数据时才会将TCP连接状态改为ESTABLISHED,否则就直接丢弃这条连接。通过减少服务器和客户端之间发生的三次握手建立连接的数量来帮助提高性能。nginx配置worker_processes 1; events { worker_connections 1024; } http { server { listen 8100 deferred; } }wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置5961979934.6110.90ms+11.48%12. 修改内核参数修改的内核参数如下# 网卡接受数据包的队列最大长度 net.core.netdev_max_backlog = 24800 # 已经收到syn包,但是还没有来得及确认的连接队列 net.ipv4.tcp_max_syn_backlog = 24800 # 端口监听队列的最大长度, 存放的是已经处于ESTABLISHED而没有被应用程序接管的TCP连接 net.core.somaxconn = 65535 # SYN的超时重传次数 net.ipv4.tcp_syn_retries = 2 # 服务端等待客户端响应ACK的超时重传次数 net.ipv4.tcp_synack_retries = 2 # 作为服务端才拥有TCP Fast Open机制 net.ipv4.tcp_fastopen = 2nginx的配置为默认配置。wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置5815359689.9110.95ms+8.73%13. 综合调优开启多请求支持,增加工作进程连接数,配置长连接,增加后端实例,修改内核参数。如果不想一遍遍修改工作进程数,直接设置为auto最省事,虽然不一定会是最优配置,但总比默认强。nginx配置worker_processes auto; events { worker_connections 65535; multi_accept on; } http { upstream backend { server 192.168.3.62:8101; server 192.168.3.62:8102; keepalive 32; keepalive_requests 2000; } server { lister deferred backlog=24800; location / { proxy_pass http://backend; } } }内核参数net.core.netdev_max_backlog = 24800 net.ipv4.tcp_max_syn_backlog = 24800 net.core.somaxconn = 65535 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_fastopen = 2wrk测试结果总请求数平均每秒请求数平均延迟对比默认配置108715118115.785.94ms+103.28%14. 单独测试nginx以上测试场景都有nginx反向代理后端,而网络和后端也会在一定程度上影响nginx性能,所以这里单独测试nginx。nginx配置worker_processes auto; worker_cpu_affinity auto; events { worker_connections 65535; multi_accept on; } http { server { location / { return 200 'hello world'; } } }wrk测试结果。在没有反向代理的情况下,增加工作进程数就能直接提升nginx性能。序号Nginx参数优化项总请求数平均每秒请求数平均延迟优化效果1无,默认配置232740038787.612.71ms-2在1的基础上设置工作进程数为auto7418729123633.13791.04us218.74%3在2的基础上设置CPU亲和性7437087123945.45784.02us219.54%4在3的基础上设置工作进程连接数和多请求7638947127300.44764.67us228.19%
  • [技术干货] [kubernetes]服务健康检查
    前言进程在运行,但是不代表应用是正常的,对此pod提供的探针可用来检测容器内的应用是否正常。k8s对pod的健康状态可以通过三类探针来检查:LivenessProbe、ReadinessProbe和StartupProbe。健康检查探针LivenessProbe用于判断容器是否存活(Running状态),如果LivenessProbe探针检测到容器不健康,则kubelet“杀掉”容器,并根据容器的重启策略做相应的处理。如果一个容器不包含LivenessProbe探针,那么kubelet认为该容器的livenessprobe探针返回的值永远是success。ReadinessProbe用于判断容器服务是否可用(Ready状态),达到Ready状态的Pod才可以接收请求。对于被Service管理的Pod,Service与Pod EndPoint 的关联关系也将基于Pod是否Ready进行设置。如果在运行过程中Ready状态变为False,则系统自动将其从Service的后端EndPoint列表中隔离出去,后续再把恢复到Ready状态的Pod加到后端EndPoint列表。这样能保证客户端在访问service时不会被转发到服务不可用的Pod实例上。StartupProbe某些应用会遇到启动比较慢的情况,这种有且仅有一次的超长延时,使用StartupProbe更加适合。实现方式三种探针均可配置三种实现方式。ExecAction在容器内运行一个命令,如果该命令的返回码为0,则表明容器健康。以下示例中,通过运行cat /tmp/health 判断一个容器运行是否正常。在Pod运行后,将在创建文件后的10秒删除文件。LivenessProbe的初次探测时间(initialDelaySeconds)为15秒,探测结果为Fail,将导致kubelet杀掉该容器并重启。apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-exec spec: containers: - name: liveness image: busybox args: - /bin/sh - -c - echo ok > /tmp/health; sleep 10; rm -rf /tmp/health; sleep 600 livenessProbe: exec: command: - cat - /tmp/health initialDelaySeconds: 15 timeoutSeconds: 1TCPSocketAction通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。示例:apiVersion: v1 kind: Pod metadata: name: pod-with-healthcheck spec: containers: - name: nginx image: nginx ports: - containerPort: 80 livenessProbe: tcpSocket: port: 80 initialDelaySeconds: 30 timeoutSeconds: 1HTTPGetAction通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。以下例子中,kubelet定时发送HTTP请求到 localhost:80/_status/healthz来进行容器应用的健康检查。apiVersion: v1 kind: Pod metadata: name: pod-with-healthcheck spec: containers: - name: nginx image: nginx ports: - containerPort: 80 livenessProbe: httpGet: path: /_status/healthz port: 80 initialDelaySeconds: 30 timeoutSeconds: 1主要参数initialDelaySeconds:健康检查探针的初次探测时间,单位为秒。例如设置为30的话,容器启动30秒后才会进行健康检测。periodSeconds:检测频率,单位为秒,默认值为10。最小值为1秒timeoutSeconds:探针检测的超时时间,默认为1秒。failureThreshold:最小连续探测失败次数,默认为3。如果连续3次探测失败,则将容器视为不健康。successThreshold:最小连续探测成功次数,默认为1。如果1次探测正常,则将容器视为健康。
  • [技术干货] [kubernetes]安装dashboard
    前言kubernetes官方文档中的web UI网页管理工具是kubernetes-dashboard,可提供部署应用、资源对象管理、容器日志查询、系统监控等常用的集群管理功能。为了在页面上显示系统资源的使用情况,需要部署 Metrics Server(参考博客园 - 安装metrics-server)。kubernetes版本:1.26.6创建资源对象官方yaml。github仓库地址:https://github.com/kubernetes/dashboard。这里的版本为 v2.7.0。用到的镜像分别为kubernetesui/dashboard:v2.7.0 和 kubernetesui/metrics-scraper:v1.0.8 。可以从docker hub直接下载,内网离线环境可以先传到内网的镜像仓库,然后镜像改成内网镜像地址。# Copyright 2017 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. apiVersion: v1 kind: Namespace metadata: name: kubernetes-dashboard --- apiVersion: v1 kind: ServiceAccount metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard --- kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: # type: NodePort ports: - port: 443 targetPort: 8443 # nodeport: 30001 selector: k8s-app: kubernetes-dashboard --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-certs namespace: kubernetes-dashboard type: Opaque --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-csrf namespace: kubernetes-dashboard type: Opaque data: csrf: "" --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-key-holder namespace: kubernetes-dashboard type: Opaque --- kind: ConfigMap apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-settings namespace: kubernetes-dashboard --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard rules: # Allow Dashboard to get, update and delete Dashboard exclusive secrets. - apiGroups: [""] resources: ["secrets"] resourceNames: [ "kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf", ] verbs: ["get", "update", "delete"] # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map. - apiGroups: [""] resources: ["configmaps"] resourceNames: ["kubernetes-dashboard-settings"] verbs: ["get", "update"] # Allow Dashboard to get metrics. - apiGroups: [""] resources: ["services"] resourceNames: ["heapster", "dashboard-metrics-scraper"] verbs: ["proxy"] - apiGroups: [""] resources: ["services/proxy"] resourceNames: [ "heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper", ] verbs: ["get"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard rules: # Allow Metrics Scraper to get metrics from the Metrics server - apiGroups: ["metrics.k8s.io"] resources: ["pods", "nodes"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: kubernetes-dashboard template: metadata: labels: k8s-app: kubernetes-dashboard spec: securityContext: seccompProfile: type: RuntimeDefault containers: - name: kubernetes-dashboard image: registry.elifen.cn/kubernetesui/dashboard:v2.7.0 imagePullPolicy: Always ports: - containerPort: 8443 protocol: TCP args: - --auto-generate-certificates - --namespace=kubernetes-dashboard # Uncomment the following line to manually specify Kubernetes API server Host # If not specified, Dashboard will attempt to auto discover the API server and connect # to it. Uncomment only if the default does not work. # - --apiserver-host=http://my-address:port volumeMounts: - name: kubernetes-dashboard-certs mountPath: /certs # Create on-disk volume to store exec logs - mountPath: /tmp name: tmp-volume livenessProbe: httpGet: scheme: HTTPS path: / port: 8443 initialDelaySeconds: 30 timeoutSeconds: 30 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 volumes: - name: kubernetes-dashboard-certs secret: secretName: kubernetes-dashboard-certs - name: tmp-volume emptyDir: {} serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule --- kind: Service apiVersion: v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: ports: - port: 8000 targetPort: 8000 selector: k8s-app: dashboard-metrics-scraper --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: dashboard-metrics-scraper template: metadata: labels: k8s-app: dashboard-metrics-scraper spec: securityContext: seccompProfile: type: RuntimeDefault containers: - name: dashboard-metrics-scraper image: registry.elifen.cn/kubernetesui/metrics-scraper:v1.0.8 ports: - containerPort: 8000 protocol: TCP livenessProbe: httpGet: scheme: HTTP path: / port: 8000 initialDelaySeconds: 30 timeoutSeconds: 30 volumeMounts: - mountPath: /tmp name: tmp-volume securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule volumes: - name: tmp-volume emptyDir: {}发布到集群:kubectl create -f kube-dashboard.yaml使用nodePort访问dashboardk8s官方文档给的示例是用kube-proxy,这里用的nodePort,稍微改动下原yaml中的kubernetes-dashboard服务,加了nodePort的配置。kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: type: NodePort ports: - port: 443 targetPort: 8443 nodeport: 30001 selector: k8s-app: kubernetes-dashboard应用:kubectl apply -f kube-dashboard.yaml若运行正常,浏览器访问 https://<节点IP>:30001,理应显示dashboard的界面,接下来生成用于登录的token。使用token访问创建一个serviceaccountapiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard或者使用命令kubectl create serviceaccount admin-user -n kubernetes-dashboard授予cluster-admin的集群管理员权限apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard使用命令kubectl create clusterrolebinding admin-user --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:admin-user获取tokenkubectl -n kubernetes-dashboard create token admin-user页面填入token登录。 
  • [技术干货] [kubernetes]安装metrics-server
    前言metrics server为Kubernetes自动伸缩提供一个容器资源度量源。metrics-server 从 kubelet 中获取资源指标,并通过 Metrics API 在 Kubernetes API 服务器中公开它们,以供 HPA 和 VPA 使用。之前已经用k8s的二进制文件搭建了一套集群环境,搭建步骤见:二进制部署k8s集群-基于containerd。现需要在这个集群环境内部署Metrics-Server,用于配置应用自动伸缩。集群环境:主机:Debian 11Kubernetes版本:1.26.6步骤获取yaml文件。wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server.yaml编辑yaml文件。之前部署集群用的自签名证书,metrics-server直接请求kubelet接口会证书校验失败,因此deployment中增加- --kubelet-insecure-tls参数。另外镜像原先在registry.k8s.io,国内下载不方便,下面的配置中修改成了国内镜像仓库地址。内网环境中可以先下载,然后再推到内网镜像仓库,镜像也改成内网镜像地址。apiVersion: apps/v1 kind: Deployment metadata: labels: k8s-app: metrics-server name: metrics-server namespace: kube-system spec: # ... template: spec: containers: - args: - --cert-dir=/tmp - --secure-port=4443 - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --kubelet-use-node-status-port - --metric-resolution=15s - --kubelet-insecure-tls image: registry.cn-hangzhou.aliyuncs.com/rainux/metrics-server:v0.6.4发布kubectl apply -f metrics-server.yaml查看是否在运行kubectl get pods -n kube-system | grep metrics获取集群的指标数据kubectl get --raw /apis/metrics.k8s.io/v1beta1 | python3 -m json.tool根据输出可见,集群提供nodes和pods的资源指标。{ "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "metrics.k8s.io/v1beta1", "resources": [ { "name": "nodes", "singularName": "", "namespaced": false, "kind": "NodeMetrics", "verbs": [ "get", "list" ] }, { "name": "pods", "singularName": "", "namespaced": true, "kind": "PodMetrics", "verbs": [ "get", "list" ] } ] }测试kubectl top nodestop命令kubectl top命令用来查看node节点和pod的资源使用情况。# 查看 top 命令的帮助 kubectl top --help # 查看node节点的资源使用情况 kubectl top node # 查看pod的资源使用情况 kubectl top pod # 查看所有命名空间的pod资源使用情况 kubectl top pod -A转载自https://www.cnblogs.com/XY-Heruo/p/17669633.html
  • [技术干货] [kubernetes]二进制部署k8s集群-基于containerd
    前言k8s从1.24版本开始不再直接支持docker,但可以自行调整相关配置,实现1.24版本后的k8s还能调用docker。其实docker自身也是调用containerd,与其k8s通过docker再调用containerd,不如k8s直接调用containerd,以减少性能损耗。除了containerd,比较流行的容器运行时还有podman,但是podman官方安装文档要么用包管理器在线安装,要么用包管理器下载一堆依赖再编译安装,内网离线环境下安装可能会比较麻烦,而containerd的安装包是静态二进制文件,解压后就能直接使用,离线环境下相对方便一点。本文将在内网离线环境下用二进制文件部署一个三节点集群+harbor镜像仓库。集群中部署了三个apiserver,并配置nginx反向代理,提升master的高可用性(如对高可用有进一步要求,可以再加个keepalive)。相关软件信息:名称版本说明containerdcri-containerd-cni-1.7.2-linux-amd64容器运行时harbor2.8.2容器镜像仓库etcd3.4.24键值对数据库kubernetes1.26.6容器编排系统nginx1.25.1负载均衡,反向代理apiserver服务器信息:IP操作系统硬件配置Hostname说明192.168.3.31Debian 11.6 amd644C4Gk8s31nginx+etcd+master+node192.168.3.32Debian 11.6 amd644C4Gk8s32etcd+master+node192.168.3.33Debian 11.6 amd644C4Gk8s33etcd+master+node192.168.3.43Debian 11.6 amd644C4G无harbor,内网域名registry.atlas.cn1. 系统初始化初始化部分需要三台k8s节点主机都执行, 根据实际情况修改参数。修改主机名# 3.31服务器 hostnamectl set-hostname k8s31 # 3.32服务器 hostnamectl set-hostname k8s32 # 3.33服务器 hostnamectl set-hostname k8s33修改/etc/hosts文件,增加以下配置。192.168.3.31 k8s31 192.168.3.32 k8s32 192.168.3.33 k8s33配置时间同步服务# 1. 安装chrony时间同步应用 apt install -y chrony # 2. 添加内网的ntp服务器地址。如果在公网,可配置阿里云的ntp服务器地址:ntp.aliyun.com echo 'server 192.168.3.41 iburst' > /etc/chrony/sources.d/custom-ntp-server.sources # 3. 启动 systemctl start chrony # 如果已经启动过了, 可以热加载配置: chronyc reload sources关闭swap。如果安装系统时创建了swap,则需要关闭。swapoff -a装载内核模块# 添加配置 cat << EOF > /etc/modules-load.d/containerd.conf overlay br_netfilter EOF # 立即装载 modprobe overlay modprobe br_netfilter # 检查装载。如果没有输出结果说明没有装载。 lsmod | grep br_netfilter配置系统参数# 1. 添加配置文件 cat << EOF > /etc/sysctl.d/k8s-sysctl.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 user.max_user_namespaces=28633 vm.swappiness = 0 EOF # 2. 配置生效 sysctl -p /etc/sysctl.d/k8s-sysctl.conf启用ipvs。编写system配置文件,实现开机自动装载到内核。# 1. 安装依赖 apt install -y ipset ipvsadm # 2. 新建文件并添加配置, 文件路径可任意 tee /root/scripts/k8s.sh <<EOF modprobe -- ip_vs modprobe -- ip_vs_rr modprobe -- ip_vs_wrr modprobe -- ip_vs_sh modprobe -- nf_conntrack EOF # 3. 新建system文件, 实现开机执行脚本 cat << EOF > /etc/systemd/system/myscripts.service [Unit] Description=Run a Custom Script at Startup After=default.target [Service] ExecStart=sh /root/scripts/k8s.sh [Install] WantedBy=default.target EOF # 4. 加载并启用system systemctl daemon-reload systemctl enable myscripts2. 部署harbor镜像仓库由于部署集群时候需要先拉取一些镜像,所以harbor在集群外的一个服务器单独部署。官方安装脚本使用了docker,所以harbor节点需要先安装docker,安装步骤可参考 博客园 - linux离线安装docker与compose。harbor的安装步骤可参考 博客园 - centos离线安装harbor,这里大致写一下。harbor的github release GitHub - goharbor/harbor/releases 提供离线安装包,要先下载好,然后解压。创建ssl证书mkdir certs # 创建服务器证书密钥文件harbor.key openssl genrsa -des3 -out harbor.key 2048 # 输入密码,确认密码,自己随便定义,但是要记住,后面会用到。 # 创建服务器证书的申请文件harbor.csr openssl req -new -key harbor.key -out harbor.csr # 输入密钥文件的密码, 然后一路回车 # 备份一份服务器密钥文件 cp harbor.key harbor.key.org # 去除文件口令 openssl rsa -in harbor.key.org -out harbor.key # 输入密钥文件的密码 # 创建一个自当前日期起为期十年的证书 harbor.crt openssl x509 -req -days 3650 -in harbor.csr -signkey harbor.key -out harbor.crt修改配置文件 harbor.yml,仅列出自修改项。数据存储目录和日志目录自定义了。hostname: 192.168.3.43 certificate: /home/atlas/apps/harbor/certs/harbor.crt private_key: /home/atlas/apps/harbor/certs/harbor.key # admin用户登录密码 harbor_admin_password: Harbor2023 # 数据卷目录 data_volume: /home/atlas/apps/harbor/data # 日志目录 location: /home/atlas/apps/harbor/logs/执行安装脚本./install.sh浏览器访问 https://192.168.3.43 ,测试能否正常登录访问harbor。3. 安装containerd从GitHub https://github.com/containerd/containerd/releases 下载二进制包解压压缩包tar xf cri-containerd-cni-1.7.2-linux-amd64.tar.gz -C /生成 containerd 配置文件mkdir /etc/containerd containerd config default > /etc/containerd/config.toml编辑 /etc/containerd/config.toml,修改以下内容# 修改数据存储目录 root = "/home/apps/containerd" # 对于使用systemd作为init system的linux发行版,官方建议用systemd作为容器cgroup driver # false改成true SystemdCgroup = true # 修改pause镜像下载地址,这里用的是内网域名地址 sandbox_image = "registry.atlas.cn/public/pause:3.9" # 私有harbor的连接信息 [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.atlas.cn"] [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.atlas.cn".tls] insecure_skip_verify = true [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.atlas.cn".auth] username = "admin" password = "Harbor2023"重加载systemd配置,启动containerdsystemctl daemon-reload systemctl start containerd4. 生成ca证书后面的k8s和etcd集群都会用到ca证书。如果组织能提供统一的CA认证中心,则直接使用组织颁发的CA证书即可。如果没有统一的CA认证中心,则可以通过颁发自签名的CA证书来完成安全配置。这里自行生成一个ca证书。# 生成私钥文件ca.key openssl genrsa -out ca.key 2048 # 根据私钥文件生成根证书文件ca.crt # /CN为master的主机名或IP地址 # days为证书的有效期 openssl req -x509 -new -nodes -key ca.key -subj "/CN=192.168.3.31" -days 36500 -out ca.crt # 拷贝ca证书到/etc/kubernetes/pki mkdir -p /etc/kubernetes/pki cp ca.crt ca.key /etc/kubernetes/pki/5. 部署etcd集群部署一个三节点etcd集群,集群间使用https协议加密通信。etcd的安装包可以从官网下载,下载后解压,将压缩包中的etcd和etcdctl放到/usr/local/bin目录。编辑文件etcd_ssl.cnf。IP地址为etcd节点。[ req ] req_extensions = v3_req distinguished_name = req_distinguished_name [ req_distinguished_name ] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [ alt_names ] IP.1 = 192.168.3.31 IP.2 = 192.168.3.32 IP.3 = 192.168.3.33创建etcd服务端证书openssl genrsa -out etcd_server.key 2048 openssl req -new -key etcd_server.key -config etcd_ssl.cnf -subj "/CN=etcd-server" -out etcd_server.csr openssl x509 -req -in etcd_server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions v3_req -extfile etcd_ssl.cnf -out etcd_server.crt创建etcd客户端证书openssl genrsa -out etcd_client.key 2048 openssl req -new -key etcd_client.key -config etcd_ssl.cnf -subj "/CN=etcd-client" -out etcd_client.csr openssl x509 -req -in etcd_client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions v3_req -extfile etcd_ssl.cnf -out etcd_client.crt编辑etcd的配置文件。注意,各节点的ETCD_NAME和监听地址不一样,ip和证书文件路径要根据实际来修改。以下示例为192.168.3.31的etcd配置ETCD_NAME=etcd1 ETCD_DATA_DIR=/home/atlas/apps/etcd/data ETCD_CERT_FILE=/home/atlas/apps/etcd/certs/etcd_server.crt ETCD_KEY_FILE=/home/atlas/apps/etcd/certs/etcd_server.key ETCD_TRUSTED_CA_FILE=/home/atlas/apps/kubernetes/certs/ca.crt ETCD_CLIENT_CERT_AUTH=true ETCD_LISTEN_CLIENT_URLS=https://192.168.3.31:2379 ETCD_ADVERTISE_CLIENT_URLS=https://192.168.3.31:2379 ETCD_PEER_CERT_FILE=/home/atlas/apps/etcd/certs/etcd_server.crt ETCD_PEER_KEY_FILE=/home/atlas/apps/etcd/certs/etcd_server.key ETCD_PEER_TRUSTED_CA_FILE=/home/atlas/apps/kubernetes/certs/ca.crt ETCD_LISTEN_PEER_URLS=https://192.168.3.31:2380 ETCD_INITIAL_ADVERTISE_PEER_URLS=https://192.168.3.31:2380 ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster ETCD_INITIAL_CLUSTER="etcd1=https://192.168.3.31:2380,etcd2=https://192.168.3.32:2380,etcd3=https://192.168.3.33:2380" ETCD_INITIAL_CLUSTER_STATE=new编辑/etc/systemd/system/etcd.service,注意根据实际修改配置文件和etcd二进制文件的路径[Unit] Description=etcd key-value store Documentation=https://github.com/etcd-io/etcd After=network.target [Service] EnvironmentFile=/home/atlas/apps/etcd/conf/etcd.conf ExecStart=/usr/local/bin/etcd Restart=always [Install] WantedBy=multi-user.target加载systemd配置,启动etcdsystemctl daemon-reload systemctl start etcd验证集群是否部署成功。注意根据实际修改证书文件路径和etcd节点的IP与端口etcdctl --cacert=/etc/kubernetes/pki/ca.crt --cert=/home/atlas/apps/etcd/certs/etcd_client.crt --key=/home/atlas/apps/etcd/certs/etcd_client.key --endpoints=https://192.168.3.31:2379,https://192.168.3.32:2379,https://192.168.3.33:2379 endpoint health如果集群部署成功,应该有如下类似输出https://192.168.3.33:2379 is healthy: successfully committed proposal: took = 27.841376ms https://192.168.3.32:2379 is healthy: successfully committed proposal: took = 29.489289ms https://192.168.3.31:2379 is healthy: successfully committed proposal: took = 35.703538ms6. 部署k8sk8s的二进制文件安装包可以从github下载:https://github.com/kubernetes/kubernetes/releases在changelog中找到二进制包的下载链接,下载server binary即可,里面包含了master和node的二进制文件。解压后将其中的二进制文件挪到 /usr/local/bin6.1 安装apiserver编辑master_ssl.cnf。DNS.5 ~ DNS.7为三台服务器的主机名,另行设置/etc/hosts。IP.1为Master Service虚拟服务的Cluster IP地址,IP.2 ~ IP.4为apiserver的服务器IP[req] req_extensions = v3_req distinguished_name = req_distinguished_name [req_distinguished_name] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [alt_names] DNS.1 = kubernetes DNS.2 = kubernetes.default DNS.3 = kubernetes.default.svc DNS.4 = kubernetes.default.svc.cluster.local DNS.5 = k8s31 DNS.6 = k8s32 DNS.7 = k8s33 IP.1 = 169.169.0.1 IP.2 = 192.168.3.31 IP.3 = 192.168.3.32 IP.4 = 192.168.3.33生成证书文件openssl genrsa -out apiserver.key 2048 openssl req -new -key apiserver.key -config master_ssl.cnf -subj "/CN=192.168.3.31" -out apiserver.csr # ca.crt和ca.key是 "2. openssl生成证书"中的两个证书文件 openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 36500 -extensions v3_req -extfile master_ssl.cnf -out apiserver.crt使用cfssl创建sa.pub和sa-key.pem。cfssl和cfssljson可以从github下载cat<<EOF > sa-csr.json { "CN":"sa", "key":{ "algo":"rsa", "size":2048 }, "names":[ { "C":"CN", "L":"BeiJing", "ST":"BeiJing", "O":"k8s", "OU":"System" } ] } EOF # cfssl和cfssljson可自行在GitHub搜索下载 cfssl gencert -initca sa-csr.json | cfssljson -bare sa - openssl x509 -in sa.pem -pubkey -noout > sa.pub编辑kube-apiserver的配置文件,注意根据实际情况修改文件路径和etcd地址KUBE_API_ARGS="--secure-port=6443 \ --tls-cert-file=/home/atlas/apps/kubernetes/apiserver/certs/apiserver.crt \ --tls-private-key-file=/home/atlas/apps/kubernetes/apiserver/certs/apiserver.key \ --client-ca-file=/home/atlas/apps/kubernetes/certs/ca.crt \ --service-account-issuer=https://kubernetes.default.svc.cluster.local \ --service-account-key-file=/home/atlas/apps/kubernetes/certs/sa.pub \ --service-account-signing-key-file=/home/atlas/apps/kubernetes/certs/sa-key.pem \ --apiserver-count=3 --endpoint-reconciler-type=master-count \ --etcd-servers=https://192.168.3.31:2379,https://192.168.3.32:2379,https://192.168.3.33:2379 \ --etcd-cafile=/home/atlas/apps/kubernetes/certs/ca.crt \ --etcd-certfile=/home/atlas/apps/etcd/certs/etcd_client.crt \ --etcd-keyfile=/home/atlas/apps/etcd/certs/etcd_client.key \ --service-cluster-ip-range=169.169.0.0/16 \ --service-node-port-range=30000-32767 \ --allow-privileged=true \ --audit-log-maxsize=100 \ --audit-log-maxage=15 \ --audit-log-path=/home/atlas/apps/kubernetes/apiserver/logs/apiserver.log --v=2"编辑service文件。/etc/systemd/system/kube-apiserver.service[Unit] Description=Kubernetes API Server Documentation=https://github.com/kubernetes/kubernetes [Service] EnvironmentFile=/home/atlas/apps/kubernetes/apiserver/conf/apiserver ExecStart=/usr/local/bin/kube-apiserver $KUBE_API_ARGS Restart=always [Install] WantedBy=multi-user.target加载service文件,启动kube-apiserversystemctl daemon-reload systemctl start kube-apiserver生成客户端证书openssl genrsa -out client.key 2048 # /CN的名称用于标识连接apiserver的客户端用户名称 openssl req -new -key client.key -subj "/CN=admin" -out client.csr openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 36500创建客户端连接apiserver所需的kubeconfig配置文件。其中server为nginx监听地址。注意根据实际修改配置apiVersion: v1 kind: Config clusters: - name: default cluster: server: https://192.168.3.31:9443 certificate-authority: /home/atlas/apps/kubernetes/certs/ca.crt users: - name: admin user: client-certificate: /home/atlas/apps/kubernetes/apiserver/certs/client.crt client-key: /home/atlas/apps/kubernetes/apiserver/certs/client.key contexts: - context: cluster: default user: admin name: default current-context: default6.2 安装kube-controller-manager编辑配置文件 /home/atlas/apps/kubernetes/controller-manager/conf/envKUBE_CONTROLLER_MANAGER_ARGS="--kubeconfig=/home/atlas/apps/kubernetes/apiserver/conf/kubeconfig \ --leader-elect=true \ --service-cluster-ip-range=169.169.0.0/16 \ --service-account-private-key-file=/home/atlas/apps/kubernetes/apiserver/certs/apiserver.key \ --root-ca-file=/home/atlas/apps/kubernetes/certs/ca.crt \ --v=0"编辑service文件/etc/systemd/system/kube-controller-manager.service[Unit] Description=Kubernetes Controller Manager Documentation=https://github.com/kubernetes/kubernetes [Service] EnvironmentFile=/home/atlas/apps/kubernetes/controller-manager/conf/env ExecStart=/usr/local/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_ARGS Restart=always [Install] WantedBy=multi-user.target加载配置文件并启动systemctl daemon-reload systemctl start kube-controller-manager6.3 安装kube-scheduler编辑配置文件KUBE_SCHEDULER_ARGS="--kubeconfig=/home/atlas/apps/kubernetes/apiserver/conf/kubeconfig \ --leader-elect=true \ --v=0"编辑service文件 /etc/systemd/system/kube-scheduler.service[Unit] Description=Kubernetes Scheduler Documentation=https://github.com/kubernetes/kubernetes [Service] EnvironmentFile=//home/atlas/apps/kubernetes/scheduler/conf/env ExecStart=/usr/local/bin/kube-scheduler $KUBE_SCHEDULER_ARGS Restart=always [Install] WantedBy=multi-user.target启动systemctl daemon-reload systemctl start kube-scheduler6.4 安装nginx这里用nginx对apiserver进行tcp反向代理,也可以使用haproxy。nginx编译安装可参考 博客园 - linux编译安装nginx,docker安装nginx更加简单,本文略过。以下为示例配置:worker_processes auto; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 65536; } stream{ log_format json2 '$remote_addr [$time_local] ' '$protocol $status $bytes_sent $bytes_received ' '$session_time "$upstream_addr" ' '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"'; access_log logs/stream.log json2; upstream apiservers { server 192.168.3.31:6443; server 192.168.3.32:6443; server 192.168.3.33:6443; } server { listen 9443; proxy_pass apiservers; } }6.5 安装kubelet编辑文件 /home/atlas/apps/kubernetes/kubelet/conf/env。注意修改hostname-override中的IP为Node节点自己的IP。如果修改了containerd的socket地址,则配置中也要按实际修改。KUBELET_ARGS="--kubeconfig=/home/atlas/apps/kubernetes/apiserver/conf/kubeconfig \ --config=/home/atlas/apps/kubernetes/kubelet/conf/kubelet.config \ --hostname-override=192.168.3.31 \ --v=0 \ --container-runtime-endpoint="unix:///run/containerd/containerd.sock"主要参数说明参数说明--kubeconfig设置与 apiserver 连接的配置,可以与 controller-manager 的 kubeconfig 相同。新的Node节点注意拷贝客户端相关证书文件,比如ca.crt, client.key, client.crt--configkubelet 配置文件,设置可以让多个Node共享的配置参数。--hostname-override本Node在集群中的名称,默认值为主机名--network-plugin网络插件类型,推荐使用CNI网络插件编辑文件 /home/atlas/apps/kubernetes/kubelet/conf/kubelet.configkind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 address: 0.0.0.0 port: 10250 cgroupDriver: systemd clusterDNS: ["169.169.0.100"] clusterDomain: cluster.local authentication: anonymous: enabled: true主要参数说明参数说明address服务监听IP地址port服务监听端口号,默认值为10250cgroupDrivercgroupDriver驱动,默认值为cgroupfs,可选 systemdclusterDNS集群DNS服务的IP地址clusterDomain服务DNS域名后缀authentication是否允许匿名访问或者是否使用webhook鉴权编辑service文件 /etc/systemd/system/kubelet.service[Unit] Description=Kubernetes Kubelet Server Documentation=https://github.com/kubernetes/kubernetes After=docker.target [Service] EnvironmentFile=/home/atlas/apps/kubernetes/kubelet/conf/env ExecStart=/usr/local/bin/kubelet $KUBELET_ARGS Restart=always [Install] WantedBy=multi-user.target加载service并启动kubeletsystemctl daemon-reload && systemctl start kubelet6.6 安装kube-proxy编辑文件 /home/atlas/apps/kubernetes/proxy/conf/env。注意修改hostname-override中的IP为Node节点自己的IP。KUBE_PROXY_ARGS="--kubeconfig=/home/atlas/apps/kubernetes/apiserver/conf/kubeconfig \ --hostname-override=192.168.3.31 \ --proxy-mode=ipvs \ --v=0"编辑service文件 /etc/systemd/system/kube-proxy.service[Unit] Description=Kubernetes Kube-Proxy Server Documentation=https://github.com/kubernetes/kubernetes After=network.target [Service] EnvironmentFile=/home/atlas/apps/kubernetes/proxy/conf/env ExecStart=/usr/local/bin/kube-proxy $KUBE_PROXY_ARGS Restart=always [Install] WantedBy=multi-user.target加载service并启动systemctl daemon-reload && systemctl start kube-proxy6.7 安装calico在master节点通过kubectl查询自动注册到 k8s 的 node 信息。由于 Master 开启了 https 认证,所以 kubectl 也需要使用客户端 CA证书连接Master,可以直接使用 apiserver 的 kubeconfig 文件。kubectl --kubeconfig=/home/atlas/apps/kubernetes/apiserver/conf/kubeconfig get nodes若是不想每次敲命令都要指定kubeconfig文件,可以编辑~/.bashrc,增加如下内容后source ~/.bashrcalias kubectl='/usr/local/bin/kubectl --kubeconfig=/home/atlas/apps/kubernetes/apiserver/conf/kubeconfig'如果操作步骤和以上保持一致,命令执行应有类似如下输出。NAME STATUS ROLES AGE VERSION 192.168.3.31 Ready <none> 18m v1.26.6 192.168.3.32 Ready <none> 16m v1.26.6 192.168.3.33 Ready <none> 16m v1.26.6由于安装containerd时,安装包里已经包含了cni插件,所以节点状态都是ready。自测节点之间通信还是有问题,所以换成相对来说熟悉点的calico。下载calico文件。wget https://docs.projectcalico.org/manifests/calico.yaml编辑calico.yaml文件。因为内网离线部署,以提前在公网下载好calico的镜像并推送到内网的harbor镜像仓库,所以配置文件中的镜像修改成了内网的镜像。image: registry.atlas.cn/calico/cni:v3.26.1 image: registry.atlas.cn/calico/node:v3.26.1 image: registry.atlas.cn/calico/kube-controllers:v3.26.1部署calico。PS:此处的kubectl命令以alias kubeconfigkubectl apply -f calico.yaml查看calico的pod是否正常运行。如果正常,状态应该都是running;若不正常,则需要describe pod的信息查看什么问题kubectl get pods -A6.8 集群内安装CoreDNS编辑部署文件 coredns.yaml。其中corefile里面的forward地址是内网的dns服务器地址,若内网没有dns,可修改为/etc/resolv.conf。coredns的镜像地址也是提前推送的harbor的镜像。--- apiVersion: v1 kind: ConfigMap metadata: name: coredns namespace: kube-system labels: addonmanager.kubernetes.io/mode: EnsureExists data: Corefile: | cluster.local { errors health { lameduck 5s } ready kubernetes cluster.local 169.169.0.0/16 { fallthrough in-addr.arpa ip6.arpa } prometheus :9153 forward . 192.168.3.41 cache 30 loop reload loadbalance } . { cache 30 loadbalance forward . 192.168.3.41 } --- apiVersion: apps/v1 kind: Deployment metadata: name: coredns namespace: kube-system labels: k8s-app: kube-dns kubernetes.io/name: "CoreDNS" spec: replicas: 1 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 selector: matchLabels: k8s-app: kube-dns template: metadata: labels: k8s-app: kube-dns spec: priorityClassName: system-cluster-critical tolerations: - key: "CriticalAddonsOnly" operator: "Exists" nodeSelector: kubernetes.io/os: linux affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: k8s-app operator: In values: ["kube-dns"] topologyKey: kubernetes.io/hostname imagePullSecrets: - name: registry-harbor containers: - name: coredns image: registry.atlas.cn/public/coredns:1.11.1 imagePullPolicy: IfNotPresent resources: limits: memory: 170Mi requests: cpu: 100m memory: 70Mi args: [ "-conf", "/etc/coredns/Corefile" ] volumeMounts: - name: config-volume mountPath: /etc/coredns readOnly: true ports: - containerPort: 53 name: dns protocol: UDP - containerPort: 53 name: dns-tcp protocol: TCP - containerPort: 9153 name: metrics protocol: TCP securityContext: allowPrivilegeEscalation: false capabilities: add: - NET_BIND_SERVICE drop: - all readOnlyRootFilesystem: true livenessProbe: httpGet: path: /health port: 8080 scheme: HTTP initialDelaySeconds: 60 timeoutSeconds: 5 successThreshold: 1 failureThreshold: 5 readinessProbe: httpGet: path: /ready port: 8181 scheme: HTTP dnsPolicy: Default volumes: - name: config-volume configMap: name: coredns items: - key: Corefile path: Corefile --- apiVersion: v1 kind: Service metadata: name: kube-dns namespace: kube-system annotations: prometheus.io/port: "9153" prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name: "CoreDNS" spec: selector: k8s-app: kube-dns clusterIP: 169.169.0.100 ports: - name: dns port: 53 protocol: UDP - name: dns-tcp port: 53 protocol: TCP - name: metrics port: 9153 protocol: TCP部署一个nginx用于测试。注意按实际修改镜像地址--- apiVersion: apps/v1 kind: Deployment metadata: name: deploy-nginx spec: replicas: 2 selector: matchLabels: app: nginx env: dev template: metadata: labels: app: nginx env: dev spec: containers: - name: nginx image: registry.atlas.cn/public/nginx:1.25.1 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: svc-nginx spec: ports: - protocol: TCP port: 80 targetPort: 80 selector: app: nginx env: dev发布nginx服务kubectl create -f nginx.yaml运行一个ubuntu的pod。镜像基于原版的ubuntu:22.04修改,提前安装了dnsutils再封装推送到内网harbor,Dockerfile内容如下:FROM ubuntu:22.04 RUN apt update -y && apt install -y dnsutils iputils-ping curl RUN apt clean && rm -rf /var/lib/apt/lists/*使用文件声明pod。注意按实际修改镜像地址apiVersion: v1 kind: Pod metadata: name: ubuntu namespace: default spec: containers: - name: ubuntu image: registry.atlas.cn/public/ubuntu:22.04.1 command: - tail - -f - /dev/null发布podkubectl create -f ubuntu.yaml使用exec选项进入pod内kubectl exec -it ubuntu -- bash在pod内测试能否连通nginx。若一切响应正常,说明集群已基本搭建成功# 测试能否解析出svc-nginx的ip nslookup svc-nginx # 测试能否调通svc-nginx:80 curl http://svc-nginx补充以上步骤只是部署了一个能正常发布服务的基础k8s集群,生产环境中还要考虑存储、网络、安全等问题,相关内容比较多,本文不再赘述,可参考其它文档。问题记录sysctl加载配置时报错sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: No such file or directorysysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: No such file or directory处理:装载内核模块modprobe br_netfilter转载自https://www.cnblogs.com/XY-Heruo/p/17638634.html
  • [技术干货] [nginx]反向代理grpc
    前言nginx从1.13.10版本开始提供对gRPC代理的支持。由于grpc基于http2,因此编译nginx时需要添加参数--with-http_v2_module来启用对http2协议的支持。常用配置应该是nginx 1.25版本开始,声明http2的语法应该单独写,而不是写在listen中。listen 80; http2 on;基本配置http { server { listen 80 http2; location / { grpc_pass grpc://192.168.0.14:84; } } # 示例2, 通过server_name复用端口 server { listen 80 http2; server_name demo2.test.com; location / { grpc_pass grpc://192.168.0.14:85; } } }反向代理后端SSL gRPCserver { listen 80 http2; grpc_ssl_verify off; # 关闭对grpc服务器的ssl证书验证 grpc_ssl_session_reuser on; # 启用与grpc服务器https连接的ssl会话重用功能 location / { grpc_pass grpcs://192.168.0.14:84; # grpc后端地址 } }nginx同时启用https。客户端 -> nginx(https) -> 服务端(SSL)server { listen 443 ssl http2; ssl_certificate ssl/test.pem; ssl_certificate_key ssl/test.key; grpc_ssl_verify off; grpc_ssl_session_reuser on; location / { grpc_pass grpcs://192.168.0.14:84; } }负载均衡配置upstream grpc_backend { server 192.168.0.11:8001; server 192.168.0.12:8001; } server { listen 80 http2; location / { grpc_pass grpc://grpc_backend; } }配置指令名称语法默认值说明grpc_bindaddress [transparent] 或offnil设置从指定的本地IP地址及端口进行反向代理。设置transparent时,将客户端真实IP透传给后端。grpc_buffer_sizesize4k或8k设用于从grpc服务器读取响应数据缓冲区大小。grpc_passaddressnil后端grpc的地址grpc_hide_headerfieldnil指定grpc后端响应数据中,不向客户端传递的http头grpc_pass_headerfieldnil允许部分后端请求头返回给客户端grpc_ignore_headersfieldsnil设置禁止nginx处理从后端获取响应的headergrpc_set_headerfield value 在转发给grpc后端前,修改或添加请求头grpc_connect_timeouttime60snginx与后端建立连接的超时时间grpc_read_timeouttime60s从后端连续接收两个读操作之间的超时时间grpc_send_timeouttime60s从后端连续接收两个写操作之间的超时时间grpc_socket_keepaliveon 或 offoff启用nginx与后端的tcp keepalive机制grpc_intercept_errorson 或 offoff启用拦截后端响应码大于或等于300的结果grpc_next_upstream  当出现指令之中指定的条件时,将未返回响应的请求传递给upstream中的另一个后端grpc_next_upstream_timeouttime0next_upstream过程中的超时时间grpc_next_upstream_triesnumber0next_upstream中下一个后端的尝试次数grpc_ssl_protocols  指定nginx与后端建立ssl连接的ssl协议的版本grpc_ssl_session_reuseon 或 offon启用与后端https连接的ssl会话复用功能grpc_ssl_ciphers  设置建立https连接时用于协商使用的加密算法组合grpc_ssl_server_nameon或offoff在与grpc服务器建立ssl连接时,设置是否启用通过SNI或RFC6066传递主机名grpc_ssl_certificatefilenil指定后端对nginx的ssl证书文件grpc_ssl_certificate_keyfilenil指定后端对nginx的ssl私钥文件grpc_ssl_password_filefilenil指定后端对nginx的ssl密码文件grpc_ssl_verifyon 或 offoff设置是否启用对grpc后端的ssl证书验证机制grpc_ssl_namenameproxy_pass指令指定的主机名指定对后端ssl证书验证的主机名grpc_ssl_crlfilenil证书吊销列表文件grpc_ssl_trusted_certificatefilenil指定一个pem格式的ca证书文件grpc_ssl_verify_depthnumber1设置证书链的验证深度转载自https://www.cnblogs.com/XY-Heruo/p/17575356.html
  • [技术干货] [linux]常见内核TCP参数描述与配置
    前言所有的TCP/IP参数都位于/proc/sys/net目录下(请注意,对/proc/sys/net目录下内容的修改都是临时的,任何修改在系统重启后都会丢失),如果需要固化设置,则需要修改/etc/sysctl.conf(也可以在/etc/sysctl.d目录下新建conf文件)sysctl命令基本使用# 查看指定参数 sysctl net.ipv4.tcp_tw_reuse # 查看所有内核参数 sysctl -a # 临时修改指定内核参数 sysctl -w net.ipv4.tcp_tw_reuse=1 # 重加载 /etc/sysctl.conf文件 sysctl -p # 重加载所有系统配置文件 sysctl --systemTIME_WAIT问题linux系统下,TCP连接断开后,会以TIME_WAIT状态保留一定时间,然后才会释放端口。当并发请求过多的时候,就会产生大量的TIME_WAIT状态的连接。如果没有及时断开,会有大量的端口资源的服务器资源被占用。对此我们有必要调整下linux的TCP内核参数,让系统更快地释放TIME_WAIT连接。统计TCP各种状态的数量netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'编辑配置文件/etc/sysctl.conf,加入以下内容:net.ipv4.tcp_syncookies= 1 net.ipv4.tcp_tw_reuse= 1 net.ipv4.tcp_tw_recycle= 1 net.ipv4.tcp_fin_timeout= 30生效:sysctl -p # 如果编辑的文件在 /etc/sysctl.d/ 目录下, 需要改成使用以下命令 sysctl --system高并发下端口配置优化net.ipv4.tcp_keepalive_time= 1200 net.ipv4.ip_local_port_range= 1024 65535 net.ipv4.tcp_max_syn_backlog= 8192 net.ipv4.tcp_max_tw_buckets= 5000参数说明对于不同的linux发行版,默认值可能不一样。net.core.somaxconn一般情况下默认值是128,不同linux发行版可能会有区别。该参数用于控制处于监听状态的套接字的最大连接队列长度,对于高并发的nginx服务器而言要注意调大该参数值,比如16384,32768。net.core.xmem_default和net.core.xmem_max参数说明默认值net.core.rmem_default系统范围接收数据的内核缓冲区初始大小262144byte,即256KBnet.core.wmem_default系统范围发送数据的内核缓冲区初始大小262144byte,即256KBnet.core.rmem_max系统范围接收数据的内核缓冲区最大大小262144byte,即256KBnet.core.wmem_max系统范围发送数据的内核缓冲区最大大小262144byte,即256KB默认值在不同的linux发行版可能会有所不同。网络环境良好和内存资源充足的情况下,增大上述四个参数的值有助于提高并发能力,减少丢包和延迟。网络环境较差或内存资源不足的情况下,可以考虑减小上述四个参数的值。如果xmem_default的值大于xmem_max的值,将以xmem_max为准,且超出的部分内存将被浪费。net.ipv4.ip_local_port_range一般情况下默认值为32768 60999,表示本地端口范围为32768到60999,不同linux发行版可能会有所不同。常见优化配置:net.ipv4.ip_local_port_range = 1024 65535通过将本地端口号限制在指定的范围内,可以避免与系统或其它应用程序使用的端口号发生冲突。如果服务器上还运行了后端应用程序,注意要错开后端服务的端口号。net.ipv4.tcp_fastopen该参数用于启用或禁用 TCP 的快速打开(TCP Fast Open)功能。TCP 快速打开是一种优化的 TCP 握手过程,旨在减少客户端与服务器之间的往返延迟时间,从而加速连接的建立。传统的 TCP 握手过程需要三次往返(3-way handshake)才能建立连接。而 TCP 快速打开通过在初始 SYN 数据包中携带客户端发送的应用层数据,使服务器可以在接收到 SYN 数据包后直接发送 SYN+ACK 数据包,从而减少了一个往返的延迟。net.ipv4.tcp_fastopen 参数有以下几个取值:0:表示禁用 TCP 快速打开功能。1:表示启用 TCP 快速打开功能。2:表示启用 TCP 快速打开功能,并允许客户端在第一次握手时发送数据包。需要注意的是,启用 TCP 快速打开功能需要支持该功能的客户端和服务器。如果客户端或服务器不支持 TCP 快速打开,即使在内核中启用了该功能,TCP 连接仍然会回退到传统的三次握手过程。对于linux服务器,内核版本应高于3.7。net.ipv4.tcp_fin_timeout一般情况下默认值为60,单位秒,不同linux发行版可能会有所不同。用于控制TCP/IP协议栈中的FIN-WAIT-2状态的超时时间。在TCP协议中,当一段的连接主动关闭后,会进入FIN-WAIT-2状态,等待对方的确认,以确保双方都完成了连接关闭。当FIN-WAIT-2状态持续超过该参数值是,连接会被内核强制关闭,这对于释放系统资源,提高连接处理能力非常重要。较小的参数值可以更快地释放系统资源,但可能导致一些连接在网络不稳定的情况下被错误地关闭。net.ipv4.tcp_keepalive_time一般情况下默认值为7200,单位秒,不同linux发行版可能会有所不同。该参数用于控制TCP/IP协议栈中的 TCP keepalive 检测时间间隔。TCP keepalive是一种机制,用于检测处于空闲状态的连接是否仍然有效。当一段时间内没有数据传输时,TCP Keepalive会发送一些特定的探测报文到对方,以确认连接的状态。这对于检测死连接、清理空闲连接和提高连接可靠性很重要。如果该参数值默认2小时,如果修改为很小的值,将会带来频繁的keepalive检测,这会增加网络流量和系统负载,不必要的连接也可能被中断。同时也会增加系统安全问题,攻击者可以利用Keepalive探测报文进行DoS攻击或网络扫描。net.ipv4.tcp_max_tw_buckets不同linux发行版可能会有所不同,可能是65536或180000。该参数用于控制 TIME_WAIT 状态的 TCP 连接的最大数量。当TIME_WAIT数超过该参数值,新的连接请求可能会被丢弃或拒绝。较小的值会加快清理TIME_WAIT,但可能会有连接异常。一般情况下默认即可,根据实际情况可以考虑减少或增多。net.ipv4.tcp_max_syn_backlog一般情况下默认值为1024,不同linux发行版可能会有所不同。该参数用于控制TCP/IP协议栈中SYN队列的最大长度。在 TCP 握手过程中,当客户端发送 SYN 报文请求建立连接时,服务器端会将这些 SYN 请求放入 SYN 队列中等待处理。net.ipv4.tcp_max_syn_backlog 参数指定了 SYN 队列的最大长度,即能够同时等待处理的 SYN 请求的最大数量。较小的 net.ipv4.tcp_max_syn_backlog 值可能会导致 SYN 队列溢出,从而无法处理所有的连接请求。这可能会导致客户端无法成功建立连接,出现连接超时或连接被拒绝的情况。net.ipv4.tcp_syncookies一般情况下默认为0,表示关闭,不同linux发行版可能会有所不同。置为1表示开启。表示开启SYNCookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击。当系统遭受SYN Flood攻击时,攻击者会发送大量的TCP SYN请求,消耗服务器资源并导致服务不可用。启用SYN Cookie机制后,当服务器接收到一个新的 TCP SYN 请求时,会根据该请求生成一个 SYN Cookie,并将 SYN Cookie 发送回给客户端。客户端在后续的请求中需要携带该 SYN Cookie。服务器在收到后续请求时,会验证 SYN Cookie 的合法性,并根据其中的信息还原出原始的 SYN 请求。通过使用 SYN Cookie 机制,服务器可以在不消耗太多资源的情况下抵御 SYN Flood 攻击,确保系统的稳定性和可用性。启用 SYN Cookie 机制也可能带来一些问题,一些网络设备可能无法正确处理SYN Cookie的连接请求,导致连接无法建立或其它问题。net.ipv4.tcp_synack_retries一般情况下默认为5,不同linux发行版可能会有所不同。该参数用于设置在连接建立过程中,发送 SYN-ACK(同步应答)包后等待客户端 ACK(确认应答)包的最大重试次数。在 TCP 连接的三次握手过程中,服务器收到客户端的 SYN(同步)包后,会回复一个 SYN-ACK 包作为应答。然后服务器等待客户端发送 ACK 包来确认连接的建立。如果服务器在等待期间未收到 ACK 包,它将重试发送 SYN-ACK 包,重试次数由 net.ipv4.tcp_synack_retries 参数确定。网络环境糟糕的情况下可以考虑增加参数值,以允许更多的重试次数,增加连接建立的成功率。减小参数值有助于快速建立连接和减少资源占用。net.ipv4.tcp_syn_retries一般情况下默认为6,不同linux发行版可能会有所不同。该参数用于设置在连接建立过程中,发送 SYN(同步)包后等待对方响应的最大重试次数。当客户端发送 SYN 包后,如果没有收到服务器的 SYN-ACK(同步应答)包,客户端会重试发送 SYN 包,重试次数由 net.ipv4.tcp_syn_retries 参数确定。net.ipv4.tcp_timestamps一般情况下默认为1,表示开启,不同linux发行版可能会有所不同。置为0表示关闭。启用后允许在TCP报文中添加时间戳信息,用于测量报文的往返时间(RTT)和计算报文的时序。net.ipv4.tcp_tw_reuse一般情况下默认为0,表示关闭,不同linux发行版可能会有所不同。置为1表示开启。允许重用TIME_WAIT Socket,也就是可以重用TIME_WAIT占用的端口。启用net.ipv4.tcp_tw_reuse也可能带来一些问题。例如,如果处于TIME_WAIT连接上仍然存在未完全处理的数据包,重用该端口可能导致数据包被传递到错误的连接上,从而导致数据错乱或安全问题。net.ipv4.tcp_tw_recycle一般情况下默认为0,表示关闭,不同linux发行版可能会有所不同。置为1表示开启。启用快速回收TIME_WAIT Socket,内核根据一定规则释放TIME_WAIT的端口资源。具体的回收规则可以根据net.ipv4.tcp_timestamps参数和其它相关参数进行调整。当多个客户端位于同一个NAT网络后面时,启用快速回收可能导致来自不同客户端的连接被错误服用,导致数据错乱或安全问题。net.ipv4.tcp_rmem和net.ipv4.tcp_wmem用于设置tcp接收缓冲区和发送缓冲区的大小,有三个值组成,分别是最小值、默认值和最大值。类似于net.core.xmem_default和net.core.xmem_max。不过net.core是系统全局参数,适用于所有类型的socket,包括tcp和udp。 转载自https://www.cnblogs.com/XY-Heruo/p/17562091.html
  • [技术干货] [nginx]lua读取请求体
    前言nginx默认不读取请求体的数据,但可以通过$request_body内置变量来获取。$request_body存在内存中,如果它的字节大小超过nginx配置的client_body_buffer_size的值,nginx就会把请求体存放到临时文件中。此时数据就不在内存中了,这会导致$request_body为空。同步非阻塞方式获取请求体ngx.req.read_body含义:同步读取客户端请求体,且不会阻塞nginx的事件循环。使用此指令后,就可以通过ngx.req.get_body_data来获取请求体的数据了。但如果使用临时文件来存放请求体,就需要先使用函数ngx.req.get_body_file来获取临时文件名,再读取临时文件中的请求体数据。环境:rewrite_by_lua*、access_by_lua*、content_by_lua*ngx.req.get_body_data含义:执行ngx.req.read_body指令后,可以使用本指令在内存中获取请求体数据,结果会返回一个lua的字符串类型的数据。如果要获取table类型的数据,则需要使用ngx.req.get_post_args。环境:rewrite_by_lua*、access_by_lua*、content_by_lua*、log_by_lua*ngx.req.get_post_args含义:读取包含当前请求在内的所有post请求的查询参数,返回一个table类型的数据环境:rewrite_by_lua*、access_by_lua*、content_by_lua*、log_by_lua*、header_filter_by_lua*、body_filter_by_lua*ngx.req.get_body_file含义:获取存放请求体的临时文件名。如果请求体被存放在内存中,获取的值就是nil。示例获取string类型的请求体location /testlua { client_max_body_size 10k; client_body_buffer_size 1k; content_by_lua_block { local ngx = require "ngx"; ngx.req.read_body() -- 开启读取请求体模式 local data = ngx.req.get_body_data() -- 获取内存中的请求体 if data then ngx.print(string.format("data: %s, type: %s",data,type(data))) return else local file = ngx.req.get_body_file() -- 如果内存中没有, 则到临时文件中读取 if file then ngx.say("body is in file ", file) else ngx.say("no body found") end end } }请求测试curl -i http://192.168.1.111/testlua -d 'test=123&a=qwe&b=zxc' HTTP/1.1 200 OK Server: openresty Date: Sun, 28 May 2023 10:05:51 GMT Content-Type: application/octet-stream Transfer-Encoding: chunked Connection: keep-alive data: test=123&a=qwe&b=zxc, type: string获取table类型的请求体location /testlua { client_max_body_size 10k; client_body_buffer_size 1k; content_by_lua_block { local ngx = require "ngx"; ngx.req.read_body() -- 开启读取请求体模式 local args, err = ngx.req.get_post_args() -- 获取内存中的请求体 if args then for k,v in pairs(args) do if type(v) == "table" then ngx.say(k, ": ", table.concat(v, ", ")) else ngx.say(k, ": ", v) end end else local file = ngx.req.get_body_file() -- 如果内存中没有, 则到临时文件中读取 if file then ngx.say("body is in file ", file) else ngx.say("no body found") end end } }请求测试curl -i http://192.168.1.111/testlua -d 'test=123&a=qwe&b=zxc' HTTP/1.1 200 OK Server: openresty Date: Sun, 28 May 2023 10:37:48 GMT Content-Type: application/octet-stream Transfer-Encoding: chunked Connection: keep-alive a: qwe b: zxc test: 123转载自https://www.cnblogs.com/XY-Heruo/p/17442319.html
  • [问题求助] linux下的idea插件,无法登录
    操作系统能debian13,idea版本为2025.3.3,点击华为账号登录,然后就一直登录中
  • [问题求助] IDE会出linux版吗
    如题,想问问IDE会出linux版吗,如果出,有规划时间吗
  • [介绍/入门] Linux 通用软件包 AppImage 打包详解
    AppImage 是 Linux 系统中一种新型的软件包格式,它与 rpm、deb 这些软件包格式相比最大的不同便是:(1)无需安装,即用即删。(2)只需打包一次,便可到处运行。完美的解决了不同 Linux 发行版(Ubuntu/Debian/Fedora/CentOS)之间软件包不统一的问题。它的工作原理便是将程序运行所需的文件全部打包在一个文件中,待程序运行时再将这些文件提取在 /tmp/.mount_xxxxxxx/ 目录中,然后执行 AppRun 脚本启动程序以进行资源的调用。以下便是一个 AppImage 文件内部包含的目录树结构:AppDir/ ├── AppRun ├── 应用图标.png ├── 程序名.desktop ├── usr/ ├── bin/ ├── lib/ ├── share/它本质上就是一个 squashfs 文件系统 + runtime 执行器。特别注意:(1)要实现跨平台运行,待打包的程序最好是在 CentOS 7 系统上进行编译 ,然后再进行打包。【注:编译 C/C++ 程序所使用的系统库 glibc 在 Linux 系统上几乎肯定存在,而该库有着良好的向后兼容性,因此使用旧版本的 glibc 库编译出来的程序几乎可以完美的运行在新版本的 glibc 系统上。而在 CentOS 7 上的 glibc 版本是 2.17,该版本较旧且兼容性较好,因此在其系统上编译出来的 C 程序通常也可以在大部分的 Linux 发行版系统中使用。】(2)待打包程序依赖的 lib 文件中最好只包含其专属的库文件即可,不要包含类似 glibc 这样的系统库文件。【注:这是因为在 A 系统中的 glibc 文件通常并不可以在 B 系统中使用,因此为了避免 AppImage 程序运行错误,请勿这样去做。再者,glibc 在 Linux 系统中是肯定会存在的,因此也并不需要额外去包含这样的依赖文件。】手动打包 - appimagetool linuxdeploy是由 AppImage 官方制作的打包工具,在使用它进行打包时,必须要先分析待打包程序的动态库依赖情况,然后再完成对 AppDir 目录的装填,最后才能使用 appimagetool 完成对程序的打包。由于分析程序的依赖情况是个很复杂的问题,因此该工具在使用上体验并不太好。接下来,我将演示如何对一个简单的 C 程序完成打包过程:(1)文件准备:hello.c。// 主文件 hello.c#include <stdio.h>int main() { printf("Hello Appimage\n"); return 0;}(2)编译并打包#(1)编译及检验运行gcc -o hello hello.c./hello#(2)制作 AppDir 目录树mkdir -p AppDir/usr/bin/cp ./hello AppDir/usr/bin/wget https://github.com/boolean-world/appimage-resources/blob/master/hello-world-appimage/hello-world-icon.png -O AppDir/hello.png #任意图片文件即可nano AppDir/hello.desktop #文件内容见下方nano AppDir/AppRun #脚本内容见下方#(3)开始制作 AppImage 程序/root/appimagetool-x86_64.AppImage AppDir/附注:hello.desktop 文件如下:[Desktop Entry]Name=helloExec=helloIcon=helloType=ApplicationCategories=Utility;Terminal=trueAppRun 脚本如下:#!/bin/shAPPDIR="$(dirname "$(readlink -f "$0")")"# 添加库目录if [ -d "$APPDIR/lib64" ]; then export LD_LIBRARY_PATH="$APPDIR/lib64:$LD_LIBRARY_PATH"fiif [ -d "$APPDIR/usr/lib" ]; then export LD_LIBRARY_PATH="$APPDIR/usr/lib:$LD_LIBRARY_PATH"fi# 启动主程序 //注意:不同应用主程序路径需要修改exec "$APPDIR/usr/bin/hello" "$@"AppDir 目录树结构如下:AppDir/├── AppRun //启动程序,可以是简单的脚本,也可以是 ELF,只要保证运行该脚本主程序能被启动即可。├── hello.desktop //注意 EXEC 的值,它对应的是/usr/bin/目录中的程序,而 Icon 对应的是当前目录├── hello.png //也支持 svg 格式└── usr └── bin └── hello自动打包 - linuxdeploylinuxdeploy是一个由第三方制作的 AppImage 打包工具,与 appimagetool 不同的是,它可以对待打包程序自动进行依赖分析,并自动将所需的依赖及资源文件按照 AppDir 的目录格式给装填完毕,用户只需将模版化的 desktop 文件和 icon 文件准备好即可,使用起来简直美滋滋。【示例一】:接下来,我将演示如何对一个需要依赖的简单 C 程序完成打包过程:(1)文件准备:mylib.h、mylib.c、main.c、Makefile。// 动态库头文件 mylib.h#ifndef MYLIB_H#define MYLIB_Hint add(int a, int b);void hello();#endif// 动态库源码 mylib.c#include <stdio.h>#include "mylib.h"int add(int a, int b) { return a + b;}void hello() { printf("Hello from my dynamic library!\n");}// 主程序 main.c#include <stdio.h>#include "mylib.h"int main() { hello(); int result = add(3, 5); printf("3 + 5 = %d\n", result); return 0;}# Makefile 文件CC=gccCFLAGS=-fPIC -WallLDFLAGS=-sharedTARGET_LIB=libmylib.soTARGET_MAIN=mainall: $(TARGET_LIB) $(TARGET_MAIN)$(TARGET_LIB): mylib.o $(CC) $(LDFLAGS) -o $(TARGET_LIB) mylib.omylib.o: mylib.c mylib.h $(CC) $(CFLAGS) -c mylib.c$(TARGET_MAIN): main.o $(TARGET_LIB) $(CC) main.o -L. -lmylib -o $(TARGET_MAIN)main.o: main.c mylib.h $(CC) -c main.cclean: rm -f *.o $(TARGET_MAIN) $(TARGET_LIB)(2)编译并打包#(1)编译及检验运行cd myappmakemv libmylib.so /lib64/libmylib.so./main#(2)制作的 main.desktop 文件内容cat main.desktop[Desktop Entry]Name=mainExec=mainIcon=mainType=ApplicationCategories=Utility;Terminal=true#(3)获取一个 Icon 文件wget https://github.com/boolean-world/appimage-resources/blob/master/hello-world-appimage/hello-world-icon.png -O main.png#(4)开始制作 AppImage 程序/root/linuxdeploy-x86_64.AppImage --appdir /root/myapp --output appimage --icon-file main.png --desktop-file main.desktop -e mainls -l main*.AppImage 【示例二】:最后,我再演示如何对一个系统命令 find 完成打包过程:#(1)制作的 find.desktop 文件内容cat find.desktop[Desktop Entry]Name=findExec=findIcon=findType=ApplicationCategories=Utility;Terminal=true#(2)获取一个 Icon 文件wget https://github.com/boolean-world/appimage-resources/blob/master/hello-world-appimage/hello-world-icon.png -O find.png#(3)开始制作 AppImage 程序cd $(dirname $(which find))/root/linuxdeploy-x86_64.AppImage --appdir /root/find --output appimage --icon-file find.png --desktop-file find.desktop -e findls -l find*.AppImage 注意:(1)建议将 icon 和 desktop 文件放置在 find 命令根目录下,这样在打包的时候能够避免很多问题。(2)由于 linuxdeploy 在打包环节调用的是 appimagetool,而 appimagetool 在打包的时候会在 github 上拉取 runtime 文件,因此在使用前建议设置全局代理以确保 github 可访问。(*)全局代理设置export http_proxy=http://192.168.56.1:7890export https_proxy=http://192.168.56.1:7890export no_proxy=192.168.56.1,localhostexport HTTP_PROXY=http://192.168.56.1:7890export HTTPS_PROXY=http://192.168.56.1:7890export NO_PROXY=192.168.56.1,localhost文章来源:https://www.cnblogs.com/kqdssheng/p/19269888
总条数:1202 到第 页
上滑加载中