CentOS停更之后替换方案

阿里云CentOS停服替代方案

Rocky Linux

  • Rocky Linux是一个社区化的企业级操作系统,位于Red Hat Enterprise Linux(RHEL)下游。Rocky Linux与CentOS一样,提供了适用于服务器的稳定版本,旨在作为CentOS的完全兼容替代版本。

  • rocky linux自带cockpit UI面板,开启方法:

yum list installed | grep cockpit
systemctl enable cockpit
systemctl start cockpit
ss -antulp | grep cockpit
#浏览器访问IP:9090端口

国产操作系统介绍

  1. 银河麒麟操作系统 (Kylin OS):(有免费的社区版)

  2. 开发者:国防科技大学

  3. 特点:主要面向政府、国防、教育等领域,具有较高的安全性和稳定性。Kylin OS 有多种版本,如桌面版和服务器版。

  4. 中标麒麟 (NeoKylin):(有免费的社区版)

  5. 开发者:银河麒麟与中标普华合作开发
  6. 特点:基于Linux内核,提供稳定和安全的操作环境,广泛应用于政府和企业。

  7. 深度操作系统 (Deepin OS):(有免费的社区版)

  8. 开发者:武汉深之度科技有限公司特点:用户界面美观,使用方便,针对普通用户和开发者,基于Debian。
  9. Deepin OS 以其深度定制的桌面环境(DDE)和丰富的软件生态而闻名。

  10. UOS (统信操作系统):(有免费的社区版)

  11. 开发者:统信软件技术有限公司
  12. 特点:面向企业级市场,整合了深度和中标麒麟的技术,提供统一的操作系统平台。
  13. 欧拉:
  14. 华为开发的基于Linux内核的操作系统,主要用于企业服务器和云计算环境(有免费的)
  15. 鸿蒙操作系统 (HarmonyOS):(不是基于Linux内核,而是基于LiteOS微内核和其他技术)
  16. 开发者:华为
  17. 特点:不仅是一个手机操作系统,还适用于各种智能设备,如物联网设备、智能家居、汽车等。鸿蒙操作系统强调分布式架构和多设备协同
  18. OpenHarmony:这是HarmonyOS的开源版本

环境准备

实验环境规划

  • 操作系统:rockylinux8
  • 网络:NAT模式,可以确保机器在任何网络都能ssh连接
K8S集群角色 IP 主机名
控制节点 172.16.183.100 rm1
工作节点 172.16.183.101 rn1

安装前准备

  • 软件包安装
yum install -y device-mapper-persistent-data lvm2 wget net-tools nfs-utils lrzsz gcc gcc-c++ make cmake libxml2-devel openssl-devel curl curl-devel unzip sudo libaio-devel wget vim ncurses-devel autoconf automake epel-release openssh-server telnet vim
yum update -y
  • 配置网络
#查看网卡名
ip addr
#编辑网卡配置文件
vim /etc/sysconfig/network-scripts/ifcfg-ens160
#修改成如下内容:
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static
IPADDR=172.16.183.100
NETMASK=255.255.255.0
GATEWAY=172.16.183.2
DNS1=172.16.183.2
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens160
DEVICE=ens160
ONBOOT=yes
nmcli connection reload 
nmcli c up ens160

也可以用nmcli来修改(rockylinux上不太好用,改配置文件好用)

nmcli connection modify ens160 ipv4.method manual connection.autoconnect yes #修改IP地址为手动配置,并且设置开机启动
nmcli connection modify ens160 ipv4.addresses 172.16.183.100/24 #修改IP地址和掩码
nmcli connection modify ens160 ipv4.gateway 172.16.183.2 #修改网关
nmcli connection modify ens160 ipv4.dns 172.16.183.2 #修改DNS,多个DNS以逗号分隔
nmcli connection down ens160 
nmcli connection up ens160 
  • 关闭selinux
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
reboot
  • 配置主机名
hostnamectl set-hostname rm1 && bash
  • 配置hosts
tee -a /etc/hosts <<'EOF'
172.16.183.100   rm1  
172.16.183.101   rn1
EOF
  • 配置ssh互信
ssh-keygen
ssh-copy-id rn1
  • 关闭swap,并把swap的lv重新分配到/
swapoff -a
vim /etc/fstab   
#/dev/mapper/centos-swap swap      swap    defaults        0 0

lvs
#看到swap占用了3.91G的LVM
  #root rl -wi-ao---- 35.08g                                                    
  #swap rl -wi-a-----  3.91g
lvdisplay
#获取到LV Path /dev/rl/swap
#删除这个lv
lvremove /dev/rl/swap
#获取vg剩余空间
vgs
#VFree = 3.91G
#扩容lv
lvextend -L +3.91G /dev/rl/root
#扩容文件系统(xfs系统)
xfs_growfs /
#检查根分区
df -h

删除交换分区后必须重新配置 grub、BLS 引导条目、dracut 和 initramfs,否则重启会卡住或报 "找不到 swap" 错误:

# 1. 查看当前 GRUB_CMDLINE_LINUX 的完整内容(确认要删的参数)
grep GRUB_CMDLINE_LINUX /etc/default/grub
# 典型输出(VG 名可能含主机名,如 rl_rm1,也可能是 rl):
# GRUB_CMDLINE_LINUX="crashkernel=... resume=/dev/mapper/rl-swap rd.lvm.lv=rl/root rd.lvm.lv=rl/swap rhgb quiet"

# 2. 修改 /etc/default/grub,删除 swap 相关参数(影响未来新装内核的默认参数)
#    删除 resume= 参数(指向 swap 设备,用于休眠恢复)
sed -i 's/ resume=[^ "]*//' /etc/default/grub
#    删除 rd.lvm.lv=.../swap 参数(告诉 dracut 激活 swap LV)
sed -i 's/ rd\.lvm\.lv=[^ "]*swap//' /etc/default/grub

# 3. 验证 /etc/default/grub 修改结果
grep GRUB_CMDLINE_LINUX /etc/default/grub

# 4.【关键】使用 grubby 更新已有的 BLS 引导条目
#    Rocky Linux 使用 BLS(Boot Loader Specification),内核启动参数存储在
#    /boot/loader/entries/*.conf 中。仅修改 /etc/default/grub 不会更新已有的 BLS 条目!
#    必须用 grubby 删除所有已有内核条目中的 swap 相关参数:
grubby --update-kernel=ALL --remove-args="resume rd.lvm.lv=rl/swap"
# 注意:rd.lvm.lv=rl/swap 中的 rl 替换为实际 VG 名(通过 vgs 查看)

# 验证 BLS 条目已更新(确认 resume 和 swap 参数已消失)
grubby --info=ALL | grep -E "resume|swap" || echo "BLS 条目已清理干净"

# 5. 清除 dracut 的 resume 模块配置
#    dracut 有独立的 resume 配置,仅清理引导参数不够!
#    dracut -f 时 resume 模块仍会自动将已删除的 swap 设备写入 initramfs,
#    导致启动时 initramfs 尝试激活不存在的 swap LV 用于休眠恢复,系统卡住。
rm -f /etc/dracut.conf.d/resume.conf
# 显式禁用 resume 模块,防止 dracut 自动探测残留的 swap 引用
echo 'omit_dracutmodules+=" resume "' > /etc/dracut.conf.d/noresume.conf
# 如果存在 systemd-hibernate-resume 服务也一并禁用
systemctl disable systemd-hibernate-resume@*.service 2>/dev/null || true

# 6. 重新生成 initramfs(此步不可跳过,必须在上一步清除 resume 配置之后执行)
dracut -f

# 7. 重新生成 grub 配置(无论 UEFI 还是 Legacy BIOS 统一用此命令)
grub2-mkconfig -o /boot/grub2/grub.cfg

# 8. 重启验证
init 6

如果有扩盘需求,关机后在VMWare中扩盘,进系统扩容LVM:

#lsblk看到nvme0n1盘容量已经扩容了,现在新建分区,扩容到LVM
fdisk /dev/nvme0n1
#新建一个nvme0n1p3分区,type为8e
#w保存
partprobe /dev/nvme0n1
pvcreate /dev/nvme0n1p3
vgextend rl /dev/nvme0n1p3
lvextend -L +40G /dev/rl/root
xfs_growfs /dev/rl/root
  • 修改内核参数
#将br_netfilter模块添加到 Linux 内核中。这是Linux网桥的核心模块,提供网络包转发和过滤。Kubernetes需要网络插件(如 Flannel、Calico)实现容器间通信,而这些网络插件通常会用Linux网桥来进行数据包转发和过滤。
modprobe br_netfilter
echo "modprobe br_netfilter" >> /etc/profile
#开启包过滤
tee /etc/sysctl.d/k8s.conf <<'EOF'
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

sysctl -p /etc/sysctl.d/k8s.conf
  • 关闭防火墙
systemctl stop firewalld && systemctl disable firewalld
  • 配置时间同步
yum -y install chrony
systemctl enable chronyd --now 

tee -a /etc/chrony.conf <<'EOF'  
server ntp1.aliyun.com iburst
server ntp2.aliyun.com iburst
server ntp1.tencent.com iburst
server ntp2.tencent.com iburst
EOF
#Iburst: 如果设置该选项,前四轮询之间的间隔将是2秒,而不是minpoll。 这对于在chronyd启动后快速获得时钟的第一次更新非常有用。即:重启chronyd后,2秒后,直接同步一次时间。
crontab -e
* * * * * /usr/bin/systemctl restart chronyd
systemctl restart crond
  • 配置aliyun docker repo源
#配置国内安装docker和containerd的阿里云在线源
yum install yum-utils -y
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum clean all && yum makecache

组件安装

docker

#docker也要安装,docker跟containerd不冲突,安装docker是为了能基于dockerfile构建镜像
yum install docker-ce -y
systemctl enable docker --now
#配置docker镜像加速器,k8s所有节点均按照以下配置
tee /etc/docker/daemon.json <<'EOF'
{
 "registry-mirrors":["https://docker.m.daocloud.io"]
} 
EOF
#重启docker:
systemctl daemon-reload && systemctl restart docker && systemctl status docker

containerd

yum install containerd.io-1.6.22*  -y
cd /etc/containerd
#rm -rf *
  • 如果有harbor环境,修改config.toml为以下。(将\替换为安装harbor机器的IP)
tee /etc/containerd/config.toml <<'EOF'
disabled_plugins = []
imports = []
oom_score = 0
plugin_dir = ""
required_plugins = []
root = "/var/lib/containerd"
state = "/run/containerd"
temp = ""
version = 2

[cgroup]
  path = ""

[debug]
  address = ""
  format = ""
  gid = 0
  level = ""
  uid = 0

[grpc]
  address = "/run/containerd/containerd.sock"
  gid = 0
  max_recv_message_size = 16777216
  max_send_message_size = 16777216
  tcp_address = ""
  tcp_tls_ca = ""
  tcp_tls_cert = ""
  tcp_tls_key = ""
  uid = 0

[metrics]
  address = ""
  grpc_histogram = false

[plugins]

  [plugins."io.containerd.gc.v1.scheduler"]
    deletion_threshold = 0
    mutation_threshold = 100
    pause_threshold = 0.02
    schedule_delay = "0s"
    startup_delay = "100ms"

  [plugins."io.containerd.grpc.v1.cri"]
    device_ownership_from_security_context = false
    disable_apparmor = false
    disable_cgroup = false
    disable_hugetlb_controller = true
    disable_proc_mount = false
    disable_tcp_service = true
    enable_selinux = false
    enable_tls_streaming = false
    enable_unprivileged_icmp = false
    enable_unprivileged_ports = false
    ignore_image_defined_volumes = false
    max_concurrent_downloads = 3
    max_container_log_line_size = 16384
    netns_mounts_under_state_dir = false
    restrict_oom_score_adj = false
    sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.7"
    selinux_category_range = 1024
    stats_collect_period = 10
    stream_idle_timeout = "4h0m0s"
    stream_server_address = "127.0.0.1"
    stream_server_port = "0"
    systemd_cgroup = false
    tolerate_missing_hugetlb_controller = true
    unset_seccomp_profile = ""

    [plugins."io.containerd.grpc.v1.cri".cni]
      bin_dir = "/opt/cni/bin"
      conf_dir = "/etc/cni/net.d"
      conf_template = ""
      ip_pref = ""
      max_conf_num = 1

    [plugins."io.containerd.grpc.v1.cri".containerd]
      default_runtime_name = "runc"
      disable_snapshot_annotations = true
      discard_unpacked_layers = false
      ignore_rdt_not_enabled_errors = false
      no_pivot = false
      snapshotter = "overlayfs"

      [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
        base_runtime_spec = ""
        cni_conf_dir = ""
        cni_max_conf_num = 0
        container_annotations = []
        pod_annotations = []
        privileged_without_host_devices = false
        runtime_engine = ""
        runtime_path = ""
        runtime_root = ""
        runtime_type = ""

        [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime.options]

      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]

        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
          base_runtime_spec = ""
          cni_conf_dir = ""
          cni_max_conf_num = 0
          container_annotations = []
          pod_annotations = []
          privileged_without_host_devices = false
          runtime_engine = ""
          runtime_path = ""
          runtime_root = ""
          runtime_type = "io.containerd.runc.v2"

          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            BinaryName = ""
            CriuImagePath = ""
            CriuPath = ""
            CriuWorkPath = ""
            IoGid = 0
            IoUid = 0
            NoNewKeyring = false
            NoPivotRoot = false
            Root = ""
            ShimCgroup = ""
            SystemdCgroup = true

      [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]
        base_runtime_spec = ""
        cni_conf_dir = ""
        cni_max_conf_num = 0
        container_annotations = []
        pod_annotations = []
        privileged_without_host_devices = false
        runtime_engine = ""
        runtime_path = ""
        runtime_root = ""
        runtime_type = ""

        [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime.options]

    [plugins."io.containerd.grpc.v1.cri".image_decryption]
      key_model = "node"

    [plugins."io.containerd.grpc.v1.cri".registry]
      config_path = ""

      [plugins."io.containerd.grpc.v1.cri".registry.auths]

      [plugins."io.containerd.grpc.v1.cri".registry.configs]
        [plugins."io.containerd.grpc.v1.cri".registry.configs."172.16.183.100:32002".tls]
            insecure_skip_verify = true
            ca_file = ""
            cert_file = ""
            key_file = ""
        [plugins."io.containerd.grpc.v1.cri".registry.configs."172.16.183.100:32002".auth]
            username = "admin"
            password = "Harbor12345"

      [plugins."io.containerd.grpc.v1.cri".registry.headers]

      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
         [plugins."io.containerd.grpc.v1.cri".registry.mirrors."172.16.183.100:32002"]
            endpoint = ["http://172.16.183.100:32002"]
          [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
             endpoint = ["https://docker.m.daocloud.io"]

    [plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
      tls_cert_file = ""
      tls_key_file = ""

  [plugins."io.containerd.internal.v1.opt"]
    path = "/opt/containerd"

  [plugins."io.containerd.internal.v1.restart"]
    interval = "10s"

  [plugins."io.containerd.internal.v1.tracing"]
    sampling_ratio = 1.0
    service_name = "containerd"

  [plugins."io.containerd.metadata.v1.bolt"]
    content_sharing_policy = "shared"

  [plugins."io.containerd.monitor.v1.cgroups"]
    no_prometheus = false

  [plugins."io.containerd.runtime.v1.linux"]
    no_shim = false
    runtime = "runc"
    runtime_root = ""
    shim = "containerd-shim"
    shim_debug = false

  [plugins."io.containerd.runtime.v2.task"]
    platforms = ["linux/amd64"]
    sched_core = false

  [plugins."io.containerd.service.v1.diff-service"]
    default = ["walking"]

  [plugins."io.containerd.service.v1.tasks-service"]
    rdt_config_file = ""

  [plugins."io.containerd.snapshotter.v1.aufs"]
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.btrfs"]
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.devmapper"]
    async_remove = false
    base_image_size = ""
    discard_blocks = false
    fs_options = ""
    fs_type = ""
    pool_name = ""
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.native"]
    root_path = ""

  [plugins."io.containerd.snapshotter.v1.overlayfs"]
    root_path = ""
    upperdir_label = false

  [plugins."io.containerd.snapshotter.v1.zfs"]
    root_path = ""

  [plugins."io.containerd.tracing.processor.v1.otlp"]
    endpoint = ""
    insecure = false
    protocol = ""

[proxy_plugins]

[stream_processors]

  [stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
    accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
    args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
    env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
    path = "ctd-decoder"
    returns = "application/vnd.oci.image.layer.v1.tar"

  [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
    accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
    args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
    env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
    path = "ctd-decoder"
    returns = "application/vnd.oci.image.layer.v1.tar+gzip"

[timeouts]
  "io.containerd.timeout.bolt.open" = "0s"
  "io.containerd.timeout.shim.cleanup" = "5s"
  "io.containerd.timeout.shim.load" = "5s"
  "io.containerd.timeout.shim.shutdown" = "3s"
  "io.containerd.timeout.task.state" = "2s"

[ttrpc]
  address = ""
  gid = 0
  uid = 0
EOF
systemctl daemon-reload && systemctl start containerd && systemctl enable containerd
#修改/etc/crictl.yaml文件,指定k8s的运行时和拉取镜像都使用containerd
#这个文件是 crictl 工具的配置文件,用于指定与 containerd 交互的相关设置:
#指定unix:///run/containerd/containerd.sock 是为了告诉 crictl 使用 Unix 域套接字的方式来连接 containerd 的 API。containerd 提供了一个 socket 文件 /run/containerd/containerd.sock,crictl 通过连接该 socket 文件,可以与 containerd 进行通信,管理容器和镜像等操作。
#runtime-endpoint:指定 containerd 的运行时接口地址:crictl 可以与 containerd 通信来管理容器生命周期和资源隔离。
#image-endpoint:指定 containerd 的镜像接口地址:以便 crictl 可以与 containerd 通信来管理镜像的拉取和推送。
#timeout:指定 crictl 等待 containerd 响应的最大时间,避免出现无响应的情况。
#debug:开启或关闭 crictl 的调试模式,方便排查问题。

cat > /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF
systemctl restart containerd

k8s-1.30

#配置安装k8s组件需要的阿里云的repo源 
cat > /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm/repodata/repomd.xml.key
EOF
#其中baseurl是指定仓库的URL地址。这个仓库是Kubernetes针对CentOS 7 x86_64架构的yum仓库,yum会从这个指定的URL地址获取Kubernetes的安装包和依赖包,从而进行安装。使用阿里云镜像站可以提高安装速度和稳定性。
yum clean all && yum makecache
yum install -y kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0
systemctl enable kubelet

kubeadm初始化k8s集群

初始化集群

kubeadm config print init-defaults > kubeadm.yaml
tee kubeadm.yaml <<'EOF'
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 172.16.183.100 #控制节点的ip
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///run/containerd/containerd.sock  #指定containerd容器运行时
  imagePullPolicy: IfNotPresent
  name:  rm1 #控制节点主机名
  taints: null
---
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers
kind: ClusterConfiguration
kubernetesVersion: 1.30.0 
networking:
  dnsDomain: cluster.local
  podSubnet: 10.244.0.0/16 
  serviceSubnet: 10.96.0.0/12 
scheduler: {}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF
kubeadm init --config=kubeadm.yaml --ignore-preflight-errors=SystemVerification

配置kubeconfig

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

#1. 设置kubectl的alias为k:
whereis kubectl ## kubectl: /usr/bin/kubectl
echo "alias k=/usr/bin/kubectl">>/etc/profile
source /etc/profile
#2. 设置alias的自动补全:
source <(kubectl completion bash | sed 's/kubectl/k/g')
#3. 解决每次启动k都会失效,要重新刷新环境变量(source /etc/profile)的问题:在~/.bashrc文件中添加以下代码:source /etc/profile
echo "source /etc/profile" >> ~/.bashrc
#4. 给alias k也配置补全
tee -a ~/.bashrc <<'EOF'
alias k='kubectl'
complete -o default -F __start_kubectl k
source <(kubectl completion bash)
EOF
##使命令生效
source ~/.bashrc

扩容工作节点

#master上
kubeadm token create --print-join-command
#把rnode1加入k8s集群,加上参数:--ignore-preflight-errors=SystemVerification
#打工作节点label
kubectl label nodes rn1 node-role.kubernetes.io/work=worker

安装calico

#上传calico镜像包和yaml文件,解压
##在线下载地址为:https://docs.projectcalico.org/manifests/calico.yaml
ctr -n=k8s.io images import calico.tar.gz
#修改calico.yaml中网卡名称
#- name: CLUSTER_TYPE
#  value: "k8s,bgp"
# 上面这一段的下面添加:(指定calico从ens33跨节点通信,不指定的话,会走lo的IP,没办法跨节点通信,也就没办法给pod划分IP)
- name: IP_AUTODETECTION_METHOD
  value: "interface=ens160"

kubectl apply -f calico.yaml
#测试网络访问
kubectl run busybox --image busybox:1.28  --image-pull-policy=IfNotPresent --restart=Never --rm -it busybox -- sh
# ping www.baidu.com
# nslookup kubernetes.default.svc.cluster.local