k8S集群切换IP

news2024/11/18 12:29:24

背景:

在实际交付场景中,有搬迁机房、网络未规划完成需要提前部署、客户使用DHCP分配IP、虚拟机IP更换等场景。目前统一的方案还是建议全部推到重新部署一套业务环境,存在环境清理不干净、重新部署成本较高、周期长、客户对产品能力质疑等问题。因此需要一个统一、智能化的切换IP的技术方案

技术难点:

1、k8s集群通过openssl x509证书作为etcd、master、kubelet、cni之间的鉴权认证方式,x509的证书加密时指定了可访问的IP信息,因此当IP发生切换时,证书将无法使用,各组件之间将无法认证通信。
2、业务本身对IP的依赖,对k8s证书的依赖。

解决思路:

问题1通过更换各组件使用的证书,重新生成证书将原来的IP信息进行替换。
问题2,修改绑定IP、证书的配置文件、环境变量、configmap。

验证步骤:

一、替换配置

替换配置、更新证书、重启服务的操作已集成到自动化脚本,无需人工干预。脚本的功能:

1、备份etcd的数据。备份etcd、kubenetes的配置文件。备份目录:/data/backup_xxxx;
2、替换etcd、apiserver、kube-scheduler、kube-controller、kubelet、registry、kube-system下的configmap、其他配置文件中的证书和IP信息
3、重启对应服务

使用方法:
./update.sh "master|node" ip.txt

"master|node":节点类型,master或者node。master将替换etcd、apiserver、kube-scheduler、kube-controller、kubelet、registry、kube-system下的configmap、其他配置文件中的证书和IP信息。 node将替换kubelet、其他配置文件中的IP信息。

ip.txt:将切换的原始IP和新IP,旧IP和新IP用","分隔,一组IP一行。

update内容脚本如下

set -xe

NC='\033[0m'
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'

log::err() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S.%2N%z')][${RED}ERROR${NC}] %b\n" "$@"
}

log::info() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S.%2N%z')][INFO] %b\n" "$@"
}

log::warning() {
  printf "[$(date +'%Y-%m-%dT%H:%M:%S.%2N%z')][${YELLOW}WARNING${NC}] \033[0m%b\n" "$@"
}

help() {
   printf "
   Useage: ./update_cert.sh \"master|node\" ip.txt
   There is two args.
    --- The firs is node_type,include \033[32m master \033[0m and \033[32m node \033[0m .
    --- The Second ip.txt is ip conf file. You need privoter a old ip and a new ip at least, and separate by ',' ,and new group ip should start a new line.
        example:
            127.0.0.1,192.168.0.1
            127.0.0.2,192.168.0.2
    "
}

check_file() {
  if [[ ! -r ${1} ]]; then
    log::err "can not find ${1}"
    exit 1
  fi
}

backup_file() {
  local file=${1}
  local target_dir=${backup_dir}/${file##*/}
  if [[ ! -e ${target_dir} ]]; then
  	mkdir -p ${target_dir}
    cp -rp "${file}" "${target_dir}/"
    log::info "backup ${file} to ${target_dir}"
  else
    log::warning "does not backup, ${target_dir} already exists"
  fi
}

# get x509v3 subject alternative name from the old certificate
get_subject_alt_name() {
  local cert=${1}.crt
  local alt_name
  local tmp_file

  tmp_file=/tmp/altname.txt

  check_file "${cert}"
  openssl x509 -text -noout -in "${cert}" | grep -A1 'Alternative' | tail -n1 | sed 's/[[:space:]]*Address//g'>${tmp_file}
  update_conf_ip ${ip_conf} ${tmp_file}
  alt_name=$(cat ${tmp_file})
  rm -f ${tmp_file}
  printf "%s\n" "${alt_name}"
}

# get subject from the old certificate
get_subj() {
  local cert=${1}.crt
  local subj

  check_file "${cert}"
  subj=$(openssl x509 -text -noout -in "${cert}" | grep "Subject:" | sed 's/Subject:/\//g;s/\,/\//;s/[[:space:]]//g')
  printf "%s\n" "${subj}"
}

gen_cert() {
  local cert_name=${1}
  local cert_type=${2}
  local subj=${3}
  local cert_days=${4}
  local ca_name=${5}
  local alt_name=${6}
  local ca_cert=${ca_name}.crt
  local ca_key=${ca_name}.key
  local cert=${cert_name}.crt
  local key=${cert_name}.key
  local csr=${cert_name}.csr
  local common_csr_conf='distinguished_name = dn\n[dn]\n[v3_ext]\nkeyUsage = critical, digitalSignature, keyEncipherment\n'

  for file in "${ca_cert}" "${ca_key}" "${cert}" "${key}"; do
    check_file "${file}"
  done

  case "${cert_type}" in
  client)
    csr_conf=$(printf "%bextendedKeyUsage = clientAuth\n" "${common_csr_conf}")
    ;;
  server)
    csr_conf=$(printf "%bextendedKeyUsage = serverAuth\nsubjectAltName = %b\n" "${common_csr_conf}" "${alt_name}")
    ;;
  peer)
    csr_conf=$(printf "%bextendedKeyUsage = serverAuth, clientAuth\nsubjectAltName = %b\n" "${common_csr_conf}" "${alt_name}")
    ;;
  *)
    log::err "unknow, unsupported certs type: ${YELLOW}${cert_type}${NC}, supported type: client, server, peer"
    exit 1
    ;;
  esac

  # gen csr
  openssl req -new -key "${key}" -subj "${subj}" -reqexts v3_ext -config <(printf "%b" "${csr_conf}")  -out "${csr}" >/dev/null 2>&1
  # gen cert
  openssl x509 -in "${csr}" -req -CA "${ca_cert}" -CAkey "${ca_key}" -CAcreateserial -extensions v3_ext  -extfile <(printf "%b" "${csr_conf}")  -days "${cert_days}" -out "${cert}" >/dev/null 2>&1

  rm -f "${csr}"
}

update_conf_ip() {
  ip_file=$1
  conf_file=$2
  check_file "${ip_file}"
  cat ${ip_file}|sed s/[[:space:]]//g|while IFS=, read old_ip new_ip;
  do
    sed -i s/${old_ip}/${new_ip}/g ${conf_file}
  done
}

update_cm_conf() {
	cm_name=$1
	tmp_cm_file=/tmp/${cm_name}.yaml
	kubectl get cm -n kube-system  ${cm_name} -o yaml >${tmp_cm_file}.yaml
	update_conf_ip ${ip_conf} ${tmp_cm_file}.yaml
	kubectl apply -f ${tmp_cm_file}.yaml
	rm -f ${tmp_cm_file}.yaml
	log::info "configmap ${cm_name} is updated"
}

update_kubelet_conf_ip() {
	backup_file ${KUBE_PATH}
	kubelet_conf=${KUBE_PATH}/kubelet.conf
	check_file ${kubelet_conf}
	update_conf_ip ${ip_conf} ${kubelet_conf}
	systemctl restart kubelet
	log::info "restart kubelet, kubelet update success"
}

update_hosts_conf() {
	hosts_file=/etc/hosts
	backup_file ${hosts_file}
	update_conf_ip ${ip_conf} ${hosts_file}
	log::info "${hosts_file} update success"
}

update_registry_conf () {
	registry_conf=/etc/docker/registry/config.yml
	check_file ${registry_conf}
	backup_file ${registry_conf}
	update_conf_ip ${ip_conf} ${registry_conf}
	systemctl restart registry
	log::info "restart registry, registry update success"
}

restart_other_server() {
  #restart other service
  kubectl get pod -n kube-system |egrep "flannel|calico"|awk '{print $1}'|xargs kubectl delete pod -n kube-system
  kubectl get pod -n local-path-storage |egrep "flannel|calico"|awk '{print $1}'|xargs kubectl delete pod -n local-path-storage
}


update_k8s_configmap() {
  for cm in ${cm_list};
  do
    update_cm_conf ${cm}
    if [ ${cm%%-*} != "kubeadm" ]; then
      kubectl get pod -n kube-system |grep ${cm}|awk '{print $1}'|xargs kubectl delete pod -n kube-system
    fi
    log::info "${cm} update success, pod restart"
  done
}

update_etcd_cert() {
  local subj
  local subject_alt_name
  local cert

  #backup etcd config
  backup_file ${ETCD_PATH}
  #backup etcd data
  ETCDCTL_API=3 etcdctl --cacert=/etc/etcd/pki/ca.crt --cert=/etc/etcd/pki/server.crt --key=/etc/etcd/pki/server.key --endpoints=https://127.0.0.1:2379 snapshot save ${backup_dir}/snapshot.db

  #replace ip for etcd config
  for file in $(find ${ETCD_PATH} -type f);
  do
  	update_conf_ip ${ip_conf} $file;
  done

  # generate etcd server,peer certificate
  # /etc/kubernetes/pki/etcd/server
  # /etc/kubernetes/pki/etcd/peer
  for cert in ${ETCD_CERT_SERVER} ${ETCD_CERT_PEER}; do
    subj=$(get_subj "${cert}")
    subject_alt_name=$(get_subject_alt_name "${cert}")
    gen_cert "${cert}" "peer" "${subj}" "${CERT_DAYS}" "${ETCD_CERT_CA}" "${subject_alt_name}"
  done

  # generate etcd healthcheck-client,apiserver-etcd-client certificate
  # /etc/kubernetes/pki/etcd/healthcheck-client
  # /etc/kubernetes/pki/apiserver-etcd-client
  for cert in ${ETCD_CERT_HEALTHCHECK_CLIENT} ${ETCD_CERT_APISERVER_ETCD_CLIENT}; do
    subj=$(get_subj "${cert}")
    gen_cert "${cert}" "client" "${subj}" "${CERT_DAYS}" "${ETCD_CERT_CA}"
  done

  #restart etcd
  systemctl restart etcd
  log::info "restart etcd, etcd update success"
}

update_kubeconf() {
  local cert_name=${1}
  local kubeconf_file=${cert_name}.conf
  local cert=${cert_name}.crt
  local key=${cert_name}.key
  local subj
  local cert_base64

  check_file "${kubeconf_file}"
  # get the key from the old kubeconf
  grep "client-key-data" "${kubeconf_file}" | awk '{print$2}' | base64 -d >"${key}"
  # get the old certificate from the old kubeconf
  grep "client-certificate-data" "${kubeconf_file}" | awk '{print$2}' | base64 -d >"${cert}"
  # get subject from the old certificate
  subj=$(get_subj "${cert_name}")
  gen_cert "${cert_name}" "client" "${subj}" "${CERT_DAYS}" "${CERT_CA}"
  # get certificate base64 code
  cert_base64=$(base64 -w 0 "${cert}")

  # set certificate base64 code to kubeconf
  sed -i 's/client-certificate-data:.*/client-certificate-data: '"${cert_base64}"'/g' "${kubeconf_file}"

  rm -f "${cert}"
  rm -f "${key}"
}
update_master_cert() {
  local subj
  local subject_alt_name
  local conf

  #backup kubenetes config
  backup_file ${KUBE_PATH}

  #replace ip for kubenetes config
  for file in $(find ${KUBE_PATH} -type f);
  do
  	update_conf_ip ${ip_conf} $file;
  done

  # generate apiserver server certificate
  # /etc/kubernetes/pki/apiserver
  subj=$(get_subj "${CERT_APISERVER}")
  subject_alt_name=$(get_subject_alt_name "${CERT_APISERVER}")
  gen_cert "${CERT_APISERVER}" "server" "${subj}" "${CERT_DAYS}" "${CERT_CA}" "${subject_alt_name}"
  log::info "${GREEN}updated ${BLUE}${CERT_APISERVER}.crt${NC}"

  # generate apiserver-kubelet-client certificate
  # /etc/kubernetes/pki/apiserver-kubelet-client
  subj=$(get_subj "${CERT_APISERVER_KUBELET_CLIENT}")
  gen_cert "${CERT_APISERVER_KUBELET_CLIENT}" "client" "${subj}" "${CERT_DAYS}" "${CERT_CA}"
  log::info "${GREEN}updated ${BLUE}${CERT_APISERVER_KUBELET_CLIENT}.crt${NC}"

  # generate kubeconf for controller-manager,scheduler and kubelet
  # /etc/kubernetes/controller-manager,scheduler,admin,kubelet.conf
  for conf in ${CONF_CONTROLLER_MANAGER} ${CONF_SCHEDULER} ${CONF_ADMIN}; do
    update_kubeconf "${conf}"
    log::info "${GREEN}updated ${BLUE}${conf}.conf${NC}"

    # copy admin.conf to ${HOME}/.kube/config
    if [[ ${conf##*/} == "admin" ]]; then
      mkdir -p "${HOME}/.kube"
      local config=${HOME}/.kube/config
      local config_backup
      config_backup=${HOME}/.kube/config.old-$(date +%Y%m%d)
      if [[ -f ${config} ]] && [[ ! -f ${config_backup} ]]; then
        cp -fp "${config}" "${config_backup}"
        log::info "backup ${config} to ${config_backup}"
      fi
      cp -fp "${conf}.conf" "${HOME}/.kube/config"
      log::info "copy the admin.conf to ${HOME}/.kube/config"
    fi
  done

  # generate front-proxy-client certificate
  # /etc/kubernetes/pki/front-proxy-client
  subj=$(get_subj "${FRONT_PROXY_CLIENT}")
  gen_cert "${FRONT_PROXY_CLIENT}" "client" "${subj}" "${CERT_DAYS}" "${FRONT_PROXY_CA}"
  log::info "${GREEN}updated ${BLUE}${FRONT_PROXY_CLIENT}.crt${NC}"

  # restart apiserver, controller-manager, scheduler and kubelet
  for item in "apiserver" "controller-manager" "scheduler"; do
    docker ps | awk '/k8s_kube-'${item}'/{print$1}' | xargs -r -I '{}' docker restart {} >/dev/null 2>&1 || true
    log::info "restarted ${item}"
  done
  systemctl daemon-reload && systemctl restart kubelet && systemctl restart docker
  log::info "restarted kubelet, docker"
}

main() {
  local node_type=$1
  ip_conf=$2

  if [ $#  -ne 2 ]; then
  	help
  	exit 1
  fi

  backup_dir=/data/backup_$(date +%Y%m%d%H%M)
  CERT_DAYS=3650
  KUBE_PATH=/etc/kubernetes
  PKI_PATH=${KUBE_PATH}/ssl
  ETCD_PATH=/etc/etcd

  # configmap lis
  cm_list="coredns kube-proxy kubeadm-config"

  # master certificates path
  # apiserver
  CERT_CA=${PKI_PATH}/ca
  CERT_APISERVER=${PKI_PATH}/apiserver
  CERT_APISERVER_KUBELET_CLIENT=${PKI_PATH}/apiserver-kubelet-client
  CONF_CONTROLLER_MANAGER=${KUBE_PATH}/controller-manager
  CONF_SCHEDULER=${KUBE_PATH}/scheduler
  CONF_ADMIN=${KUBE_PATH}/admin
  CONF_KUBELET=${KUBE_PATH}/kubelet
  # front-proxy
  FRONT_PROXY_CA=${PKI_PATH}/front-proxy-ca
  FRONT_PROXY_CLIENT=${PKI_PATH}/front-proxy-client

  # etcd certificates path

  ETCD_CERT_CA=${ETCD_PATH}/pki/ca
  ETCD_CERT_SERVER=${ETCD_PATH}/pki/server
  ETCD_CERT_PEER=${ETCD_PATH}/pki/peer
  ETCD_CERT_APISERVER_ETCD_CLIENT=${ETCD_PATH}/pki/apiserver-etcd-client

  case ${node_type} in
  master)
    update_etcd_cert
    update_master_cert
    update_hosts_conf
    #wait 10s for start apiserver
    sleep 30
    update_k8s_configmap
    restart_other_server
    update_registry_conf
    log::info "master upgrade finish"
    ;;
  node)
    update_kubelet_conf_ip
    update_hosts_conf
    log::info "node upgrade finish"
    ;;
  *)
    help
    exit 1
    ;;
  esac
}

main "$@"

二、业务修改

本文通过知识中台验证。修改values文件中的IP
1、通过helm 安装的使用下面的方法更新
sed s/ o l d i p / {old_ip}/ oldip/{new_ip}/g values.yaml
helm upgrade -f values-danji.yaml -n mpks mpks-one-mpks .
2、通过天牛安装的,修改对应的全局变量并同步

三、环境实操

验证机器:10.36.249.139
部署版本:知识中台1.4.3单机版
1、部署完成信息确认
k8s
在这里插入图片描述
证书中带有的IP信息
在这里插入图片描述
p0测试全通过
在这里插入图片描述
2、切换IP
./update.sh master ip.txt

在这里插入图片描述
证书更新
在这里插入图片描述

业务更新
在这里插入图片描述
处理不能恢复的异常pod(重启)
在这里插入图片描述
pod恢复正常
在这里插入图片描述
p0验证通过
在这里插入图片描述

补充点

因为测试环境的限制,验证存在不充分的点:
1、未在多台机器K8S集群替换证书
2、单机版KG无ceph、ppoc,切换IP后未对两个模块验证。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/157494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mac - 通过 Script 实现更换桌面壁纸

目录 一.引言 二.OsaScript 实现自动换壁纸 1.切换壁纸脚本 2.定时获取 A.修改 Python 脚本 B.修改 Shell 脚本 C.添加 Crontab 三.Iterm2 更换背景 A.打开 Preferences B.选择 Profiles -> Window C.效果图 一.引言 前面介绍了通过 Python Crontab 实现 mac 上…

【分布式】浅谈CAP、BASE理论(1)

CAP理论 起源 CAP定理&#xff0c;又被称作布鲁尔定理。这个定理起源于加州大学柏克莱分校的计算机科学家埃里克布鲁尔在2000年的分布式计算原理研讨会上提出的一个猜想1。在2002年&#xff0c;麻省理工学院的赛斯吉尔伯特和南希林奇发表了布鲁尔猜想的证明&#xff0c;使之成…

Portraiture2023免费后期修图插件磨皮神器DR5

后期修图中有一个非常关键&#xff0c;而且不可或缺的步骤&#xff0c;那就是磨皮&#xff0c;磨皮的方法有很多种&#xff0c;但最方便快捷就能达到大片级效果的方法&#xff0c;莫过于使用磨皮插件了&#xff0c;Portraiture图像磨皮插件&#xff0c;支持PS CC- CC2023&#…

4.R语言【rehsape2包】介绍、melt( )、cast( )函数、其他使用技巧

b站课程视频链接&#xff1a; https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新&#xff0c;但是要花钱&#xff0c;我花99&#x1f622;&#x1f622;元买了&#xff0c;感觉讲的没问题&#xff0c;就是知识点结构有点乱&#xff0c;有点废话&#xff09;&…

CAD转换PDF怎么转换?新手必备的方法

相信很多从事建筑类的相关工作的小伙伴们对CAD文件都并不陌生&#xff0c;在设计图纸和数据更改大都是使用这类文件的专业工具。这类文件的专业性较强设计出来的图纸也比较规范&#xff0c;但就有一个问题比较麻烦&#xff0c;那便是在不同设备打开需要对应的查阅软件&#xff…

【TypeScript】Ts基本概念

TypeScript基本概念 TypeScript 是什么&#xff1f; 目标&#xff1a;能够说出什么是typescript 内容&#xff1a; TS 官方文档TS 中文参考 - 不再维护 TypeScript 简称&#xff1a;TS&#xff0c;是 JavaScript 的超集&#xff0c;简单来说就是&#xff1a;JS 有的 TS 都有…

振弦采集模块参数配置工具的连接与断开

振弦采集模块参数配置工具的连接与断开 在指令区的【 COM 端口】组合框内操作完成。【端口】 下拉框&#xff1a;列出了本计算机当前已经存在的所有 COM 端口名称&#xff0c;若与模块连接的端口名称未在下拉框中列出&#xff0c;还可通过手工输入端口名的方法自由输入。 【速…

【NI Multisim 14.0原理图文件管理——新建设计文件】

目录 序言 一、原理图文件管理 二、新建设计文件 &#x1f34d;1. 空白文件 &#x1f34d; 2.系统安装模板文件 &#x1f34d;3. 自定义模板文件 序言 NI Multisim最突出的特点之一就是用户界面友好。它可以使电路设计者方便、快捷地使用虚拟元器件和仪器、仪表进行电路设…

C#,图像二值化(21)——局部阈值的韦尔纳算法(Wellner Thresholding)及源代码

1 韦尔纳算法&#xff08;Wellner Throsholding&#xff09;摘要针对计算大量缺陷时速度较慢且图像阈值不平滑的Wellner算法&#xff0c;本文提出了两种改进方案&#xff0c;第一种是一维平滑算法&#xff08;ODSA&#xff09;&#xff0c;第二种是基于第一种算法的&#xff0c…

《MYSQL实战45讲》笔记(11-20)

11&#xff1a;怎么给字符串字段加索引&#xff1f; 业务场景&#xff1a;基于字符串字段做查询。例如邮箱登录等等。 前缀索引的优势&#xff1a;相对于整个字段索引&#xff0c;前缀索引的占用空间更小。 前缀索引带来的问题&#xff1a;区分度过低的时候会额外扫描次数。 …

CATIA和CAD超全对比,来看看哪个好用

从文明伊始&#xff0c;绘图和制图就对人类发展发挥着重要作用&#xff0c;它通过图形表示使概念更容易理解&#xff0c;这导致了“工程艺术”的发展。绘图是一种使用图片和符号来呈现概念、想法或观点的方式&#xff0c;而绘图是任何现实世界对象或事物的图形表示。为了简化数…

1、Visual Studio 2017安装

目录 一、简介 二、安装步骤 三、Visual Studio 2017的使用 一、简介 Visual Studio是微软推出的一款C编译器&#xff0c;将“高级语言”翻译为“机器语言&#xff08;低级语言&#xff09;”的程序&#xff0c;VS是一个非常完整的开发工具集&#xff0c;包括了所有软件生命…

Vue2.0开发之——组件数据共享-父向子传值(38)

一 概述 组件之间的关系父向子传值子向父传值 二 组件之间的关系 2.1 组件之间的关系 在项目开发中&#xff0c;组件之间的最常见的关系分为如下两种 父子关系兄弟关系 2.2 父子组件之间的数据共享 父子组件之间的数据共享又分为: 父 -> 子共享数据子 -> 父共享数据…

070-JAVA项目实训:仿QQ即时通讯软件讲座五(讲解用户注册功能)

【上一讲】069-JAVA项目实训:仿QQ即时通讯软件讲座四(讲解系统登录功能)_CSDN专家-赖老师(软件之家)的博客-CSDN博客 本文主要内容是实现注册QQ用户功能,自动获取本机IP地址,与系统用户判断端口是否唯一,使用的主要技术如下: 1.使用数据库技术完成注册功能; 2.自动…

【Linux】线程概念

目录&#x1f308;前言&#x1f338;1、Linux线程概念&#x1f361;1.1、概念&#x1f362;1.2、线程的优点&#x1f367;1.3、线程的缺点&#x1f368;1.4、线程的异常和用途&#x1f33a;2、Linux下进程 vs 线程&#x1f308;前言 这篇文章给大家带来线程的学习&#xff01;…

渗透测试基础【01】——测试流程(IPC$)

渗透测试基础【01】——测试流程 注意&#xff0c;攻击对方需要对方授权&#xff0c;本文章目的只为教学&#xff0c;不要拿去干违法的事 1 渗透测试流程 授权&#xff08;获取目标用户授权&#xff0c;否则是违法行为&#xff09;信息收集 nslookup whois扫描漏洞 nmapip…

【前端代码高亮】页面代码高亮展示之Highlight高亮组件应用详解,附多个主题效果

【写在前面】作为前端开发的基本上都有接触这个高亮组件&#xff0c;这个也是目前被大家誉为最全面&#xff0c;体验最好的&#xff0c;所以针对数据展示型高亮显示我是力推这个的&#xff0c;但是我个人还是觉得还有不完美的地方&#xff0c;比如说一边输入一边高亮就不行了。…

《UEFI内核导读》如果没有内存,程序代码还能不能执行?

敬请关注&#xff1a;“固件C字营 首先思考一个问题&#xff0c;冯洛伊曼架构的计算机如果没有内存&#xff0c;代码能被否正常执行&#xff1f;过程&#xff08;函数&#xff09;能不能被相互调用&#xff1f;答案是肯定的&#xff0c;其中的一个实现方法如下。这里只介绍汇编…

如何在Instagram上开店?Instagram开店教程请收好

Instagram 是国外非常重要的图像分享平台&#xff0c;具有很强的视觉吸引力。更重要的是&#xff0c;它是最受网红欢迎的社交媒体网站之一&#xff0c;每天有超过5亿用户访问它。巨大的流量和高活跃度&#xff0c;为卖家提供了大量的营销机会。而且最重要的是&#xff0c;现在在…

唤醒手腕 Go 语言开发学习笔记(基本简介、环境安装、基础知识)

1. Go语言简介 Go&#xff08;又称 Golang&#xff09;是 Google 的 Robert Griesemer&#xff0c;Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近&#xff0c;但功能上有&#xff1a;内存安全&#xff0c;GC&#xff08;垃圾回收&#xf…