Cet article est partagé par la communauté Huawei Cloud « Implémentation d'une gestion du trafic multi-cluster basée sur Istio » par l'auteur : Vous pouvez vous faire un ami.
un arrière-plan
La gouvernance des services pour les infrastructures hétérogènes telles que le multi-cloud et le cloud hybride est l'un des scénarios sur lesquels Istio se concentre. Afin d'améliorer la disponibilité des services et d'éviter la dépendance vis-à-vis d'un fournisseur, les entreprises choisissent généralement de déployer des applications dans plusieurs clusters dans plusieurs régions, ou même dans plusieurs environnements cloud, tels que les solutions multi-cloud et cloud hybride, qui sont progressivement devenues les meilleures. choix pour le déploiement d'applications d'entreprise. Par conséquent, de plus en plus d'utilisateurs ont de fortes exigences en matière de gouvernance des services inter-clusters. Dans ce contexte, Istio, en tant que standard de facto dans le domaine de ServiceMesh, a lancé une variété de solutions de gestion multi-clusters.
2. Introduction
Actuellement, Istio prend en charge 4 modèles multiclusters.
- Modèle de plan de contrôle unique de réseau plat
- Modèle de plan multi-contrôle de réseau plat
- Modèle de plan de contrôle unique de réseau non plat
- Modèle de plan multi-contrôle de réseau non plat
Le modèle à plan de contrôle unique du multicluster signifie que plusieurs clusters partagent le même plan de contrôle Istio. Le modèle à plan de contrôle multi-cluster signifie que chaque cluster doit utiliser indépendamment un ensemble de plans de contrôle Istio, qu'il s'agisse d'un seul contrôle. plan ou un modèle de plan multi-contrôle. , chaque ensemble de plans de contrôle Istio (istiod) doit être connecté au serveur Kube-api de tous les clusters, et List-Watch obtient tous les clusters Service、Endpoint、Pod 、Node
et contrôle l'accès au service au sein du cluster ou entre les clusters, mais surveille uniquement VirtualService、DestinationRule、Gateway
les objets API Istio du cluster principal. .
Selon que le réseau inter-clusters est plat ou non, Istio subdivise deux modèles de plan de contrôle :
- Réseau plat : les réseaux de conteneurs multiclusters sont connectés via VPN et d'autres technologies, et les pods peuvent accéder directement à travers les clusters.
- Réseau non plat : les réseaux de conteneurs de chaque cluster sont isolés les uns des autres. L'accès entre clusters ne peut pas être transité et doit passer par la passerelle est-ouest.
Lorsque vous choisissez le modèle multicluster Istio dans un environnement de production, vous devez bien entendu prendre une décision en fonction de votre scénario réel. Si le réseau entre clusters est plat, vous pouvez choisir un modèle de réseau plat, si le réseau entre clusters est isolé, vous pouvez choisir un modèle de réseau non plat. Si la taille du cluster est petite, vous pouvez choisir le modèle à plan de contrôle unique. Si la taille du cluster est grande, vous pouvez choisir le modèle à plan de contrôle multiple.
Ce document sélectionne un modèle de plan multi-contrôle de réseau non plat pour les instructions d'installation : Le modèle d'installation est le suivant. Le
modèle de plan multi-contrôle de réseau non plat présente les caractéristiques suivantes.
- Il n'est pas nécessaire que différents clusters se trouvent sous un seul grand réseau, c'est-à-dire que le réseau de conteneurs n'a pas besoin d'être connecté à trois couches et que l'accès aux services entre clusters se fait via
Istio East-West Gateway
le transfert. - Il n'y a aucune limite sur la plage d'adresses de pod et la plage d'adresses de service de chaque cluster Kubernetes. Elles peuvent se chevaucher avec d'autres clusters et les différents clusters n'interfèrent pas les uns avec les autres.
- Le Sidecar de chaque cluster Kubernetes est uniquement connecté au plan de contrôle Istio de ce cluster, ce qui rend la communication plus efficace.
- Istiod surveille uniquement la configuration Istio du cluster principal, il existe donc des problèmes de réplication redondante pour d'autres ressources.
VirtualService、DestinationRule、Gateway
- Accès aux services internes dans le même cluster : connexion directe entre les pods ; accès aux services entre clusters : s'appuyer sur un proxy DNS pour résoudre les noms de domaine de service d'autres clusters. Les réseaux entre clusters étant isolés les uns des autres, ils dépendent du trafic de transit des autres clusters. le cluster distant.
East-west Gateway
Construction de trois environnements ClusterMesh
Créez deux clusters, cluster1 et cluster2, puis installez le plan de contrôle Istio sur chaque cluster et définissez les deux comme clusters principaux. Le cluster cluster1 se trouve sur le réseau network1 et le cluster cluster2 se trouve sur le réseau network2.
3.1 Conditions préalables
Les informations d'environnement pour cette version sont les suivantes : utilisez Kind pour créer un cluster Kubernetes et la version Kind est v0.19.0. La version Kubernetes est 1.27.3 ; la version Istio est 1.20.1.
Avant de créer un cluster k8s, assurez-vous que docker kubectl et kind sont installés sur le nœud Linux.
Télécharger le binaire istioctl
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.20.1 TARGET_ARCH=x86_64 sh -
Ajouter le client istioctl au chemin

3.2 Installation du cluster Kubernetes
Les scripts d'installation des clusters cluster1 et cluster2 sont les suivants
# créer-cluster.sh # Ce script gère la création de plusieurs clusters en utilisant kind et le # possibilité de créer et de configurer un registre de conteneurs non sécurisé. set -o xtrace set -o élevé set -o nom set -o échec de pipe # shellcheck source=util.sh NUM_CLUSTERS="${NUM_CLUSTERS :-2}" KIND_IMAGE="${KIND_IMAGE:-}" KIND_TAG="${KIND_TAG:-v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72}" OS="$(unname)" fonction créer des clusters() { num_clusters locaux=${1} image_arglocale="" si [[ "${KIND_IMAGE}" ]] ; alors image_arg="--image=${KIND_IMAGE}" elif [[ "${KIND_TAG}" ]]; alors image_arg="--image=kindest/node:${KIND_TAG}" être pour je dans $(seq "${num_clusters}"); faire genre créer un cluster --name "cluster${i}" "${image_arg}" cluster de correction "${i}" écho fait } fonction fixup-cluster() { local i=${1} # numéro de cluster si [ "$OS" != "Darwin" ];alors # Définissez l'adresse IP du conteneur comme point de terminaison de l'API Kube afin que les clusters atteignent les serveurs API Kube d'autres clusters. docker_ip local docker_ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "cluster${i}-control-plane") kubectl config set-cluster "kind-cluster${i}" --server="https://${docker_ip}:6443" être # Simplifier le nom du contexte kubectl config rename-context "kind-cluster${i}" "cluster${i}" } echo "Création de ${NUM_CLUSTERS} clusters" créer des clusters "${NUM_CLUSTERS}" Kubectl config utilisation-context cluster1 echo "Le type CIDR est $(docker network inspect -f '{{$map := index .IPAM.Config 0}}{{index $map "Subnet"}}' genre)" echo "Terminé"
Au cours du processus d'installation du cluster ci-dessus, pour qu'istiod puisse accéder apiserver
à l'adresse de l'autre cluster, kube-apiserver
l'adresse du cluster est définie sur l'adresse du nœud maître. Puisqu'il s'agit d'un cluster déployé par type, les nœuds maîtres des deux clusters sont essentiellement des conteneurs exécutant Docker sur le même hôte.
Confirmez si cluster1 et cluster2 sont prêts

3.3 Utilisez MetalLB pour attribuer une adresse IP externe à la passerelle
Étant donné que kind est utilisé pour déployer plusieurs clusters, la création de la passerelle nord-sud d'istio et de la passerelle est-ouest nécessite la création du service LoadBalencer, qui nécessitent tous deux l'utilisation d'ExternalIP. Ici, metalLB est utilisé pour réaliser la distribution et l'annonce des adresses IP LB.
Voir type pour créer un cluster à l'aide du segment de sous-réseau de nœud : déployer en mode metalLB L2. 172.18.0.0/16
Liste de configuration metalLB dans le cluster1 : metallb-config-1.yaml
### pour le cluster1 ##Configurez IPAddressPool pour l'attribution des adresses lbip. En mode L2, l'adresse IPpool et le nœud de travail peuvent se trouver dans le même sous-réseau. Version api : metallb.io/v1beta1 genre : IPAddressPool métadonnées : nom : première piscine espace de noms : système metallb spécification : adresses : - 172.18.1.230-172.18.1.240 --- ##Configurer L2Advertisement pour l'annonce d'adresse Version api : metallb.io/v1beta1 genre : L2Advertisement métadonnées : nom : premier-adv espace de noms : système metallb spécification : Pools d'adresses IP : - première piscine
Liste de configuration metalLB dans le cluster cluster2 : metallb-config-2.yaml
### pour le cluster2 ##Configurez IPAddressPool pour l'attribution des adresses lbip. En mode L2, l'adresse IPpool et le nœud de travail peuvent se trouver dans le même sous-réseau. Version api : metallb.io/v1beta1 genre : IPAddressPool métadonnées : nom : deuxième piscine espace de noms : système metallb spécification : adresses : - 172.18.1.241-172.18.1.252 --- ##Configurer L2Advertisement pour l'annonce d'adresse Version api : metallb.io/v1beta1 genre : L2Advertisement métadonnées : nom : deuxième-adv espace de noms : système metallb spécification : Pools d'adresses IP : - deuxième piscine
Installer à l'aide d'un script
#!/usr/bin/env bash set -o xtrace set -o élevé set -o nom set -o échec de pipe NUM_CLUSTERS="${NUM_CLUSTERS :-2}" pour je dans $(seq "${NUM_CLUSTERS}"); faire echo "Démarrage du déploiement de metallb dans le cluster${i}" kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml --context "cluster${i}" kubectl crée un secret générique -n liste de membres du système metallb --from-literal=secretkey="$(openssl rand -base64 128)" --context "cluster${i}" ## Augmentez le temps d'attente. Si la charge metallb n'est pas déployée, une erreur sera signalée lors de la création d'IPAddressPool L2Advertisement. dormir 10 kubectl apply -f ./metallb-config-${i}.yaml --context "cluster${i}" écho "----" fait
Confirmer l'état du déploiement de metalLB
Confirmez les informations IPAddressPool :

3.4 Relation d'approbation de configuration de l'autorité de certification racine partagée du cluster
Afin de prendre en charge la communication mTLS inter-clusters sécurisée, le modèle à plan de contrôle multiple nécessite que l'Istiod du plan de contrôle de chaque cluster utilise un certificat CA intermédiaire émis par la même autorité de certification pour que Citatel émette des certificats pour prendre en charge l'authentification bidirectionnelle TLS entre clusters. .
La passerelle est-ouest Istio (accès inter-cluster) utilise le routage basé sur SNI lorsqu'elle fonctionne. Elle l'achemine automatiquement vers le cluster correspondant au SNI en fonction du SNI demandé par TLS. Par conséquent, l'accès entre réseaux dans les réseaux non plats. nécessite que tout le trafic soit chiffré en TLS.
Insérez le certificat et la clé dans le cluster. Le script est le suivant (le script doit être déplacé vers le répertoire du package d'installation d'istio) :
#!/usr/bin/env bash set -o xtrace #set -o levé set -o nom set -o échec de pipe NUM_CLUSTERS="${NUM_CLUSTERS :-2}" ##Créez un répertoire dans le répertoire de niveau supérieur du package d'installation d'istio pour stocker les certificats et les clés. mkdir -p certificats certificats pushd ##Générer le certificat racine et la clé make -f ../tools/certs/Makefile.selfsigned.mk root-ca pour je dans $(seq "${NUM_CLUSTERS}"); faire ##Pour chaque cluster, générez un certificat intermédiaire et une clé pour l'autorité de certification Istio make -f ../tools/certs/Makefile.selfsigned.mk "cluster${i}-cacerts" ##Pour chaque cluster, créez l'espace de noms istio-system kubectl crée un espace de noms istio-system --context "cluster${i}" ## Pour chaque cluster, ajoutez une identification réseau en marquant l'espace de noms du système istio avec la balise topology.istio.io/network kubectl --context="cluster${i}" espace de noms d'étiquette istio-system topology.istio.io/network="network${i}" ##Pour chaque cluster, étiquetez le nœud de travail avec une région et une zone de disponibilité pour faciliter la mise en œuvre par istio du basculement régional et de l'équilibrage de charge régional. kubectl --context="cluster${i}" nœud d'étiquette "cluster${i}-control-plane" topology.kubernetes.io/region="region${i}" kubectl --context="cluster${i}" nœud d'étiquette "cluster${i}-control-plane" topology.kubernetes.io/zone="zone${i}" #Dans chaque cluster, créez un cacerts privé, en utilisant tous les fichiers d'entrée ca-cert.pem, ca-key.pem, root-cert.pem et cert-chain.pem. kubectl supprime les cacerts secrets -n istio-system --context "cluster${i}" kubectl crée des cacerts génériques secrets -n istio-system --context "cluster${i}" \ --from-file="cluster${i}/ca-cert.pem" \ --from-file="cluster${i}/ca-key.pem" \ --from-file="cluster${i}/root-cert.pem" \ --from-file="cluster${i}/cert-chain.pem" écho "----" fait
3.5 Installation du maillage de services Istio
Installez la grille istio multi-plans de contrôle pour les clusters cluster1 et cluster2.
Définissez cluster1 comme cluster principal et exécutez la commande suivante dans le répertoire d'installation d'istio
chat <<EOF> cluster1.yaml Version api : install.istio.io/v1alpha1 genre : IstioOperator spécification : valeurs: mondial: ID de maillage : maillage1 multiCluster : ##Activer la configuration multi-cluster clusterName : cluster1 #Spécifiez le nom du cluster k8s network: network1 #Spécifiez l'identifiant du réseau enregistrement: niveau : débogage EOF
Définissez cluster2 comme cluster principal et exécutez la commande suivante dans le répertoire d'installation d'istio
chat <<EOF> cluster2.yaml Version api : install.istio.io/v1alpha1 genre : IstioOperator spécification : valeurs: mondial: ID de maillage : maille2 multiCluster : ##Activer la configuration multi-cluster clusterName : cluster2 #Spécifiez le nom du cluster k8s network: network2 #Spécifiez l'identifiant du réseau enregistrement: niveau : débogage EOF
#!/usr/bin/env bash set -o xtrace set -o élevé set -o nom set -o échec de pipe OS="$(unname)" NUM_CLUSTERS="${NUM_CLUSTERS :-2}" pour je dans $(seq "${NUM_CLUSTERS}"); faire echo "Démarrage du déploiement d'istio dans le cluster${i}" istioctl install --force --context="cluster${i}" -f "cluster${i}.yaml" echo "Générer la passerelle est-ouest dans le cluster${i}" ## Installez des passerelles est-ouest dans chaque cluster. bash samples/multicluster/gen-eastwest-gateway.sh \ --mesh "mesh${i}" --cluster "cluster${i}" --network "network${i}" | \ istioctl --context="cluster${i}" install -y -f - écho fait
Exécutez le script pour installer et déployer istio
Attendez un moment que l'installation soit terminée
3.6 Ouverture des services sur la passerelle est-ouest
Étant donné que les clusters se trouvent sur des réseaux différents, nous devons ouvrir tous les services (*.local) sur les passerelles est-ouest des deux clusters. Bien que cette passerelle soit publique sur Internet, les services qui la sous-tendent ne sont accessibles qu'aux services dotés de certificats mTLS de confiance, comme s'ils se trouvaient sur le même réseau. Exécutez les commandes suivantes pour exposer les services dans les deux clusters :
Version api : networking.istio.io/v1beta1 genre : Passerelle métadonnées : nom : passerelle inter-réseaux spécification : sélecteur: istio : eastwestgateway # Passerelle dédiée au trafic est-ouest les serveurs: - port: numéro : 15443 # Déjà déclaré nom : tls protocole : TLS svp : mode : AUTO_PASSTHROUGH # Le mode de fonctionnement de la passerelle est-ouest est TLS AUTO_PASSTHROUGH hôtes: - "*.local" # Expose tous les services
Appliquez la configuration de passerelle ci-dessus dans chaque cluster séparément :
kubectl -n istio-system --context=cluster${i} apply -f samples/multicluster/expose-services.yaml
3.7 Configurez le secret pour qu'istiod puisse accéder au serveur API du cluster distant
L'istiod de chaque cluster k8s doit surveiller en liste le serveur Kube-API des autres clusters et utiliser les informations d'identification du cluster K8s pour créer un objet secret afin de permettre à Istio d'accéder au serveur API Kubernetes distant.
#!/usr/bin/env bash set -o xtrace set -o élevé set -o nom set -o échec de pipe OS="$(unname)" NUM_CLUSTERS="${NUM_CLUSTERS :-2}" pour je dans $(seq "${NUM_CLUSTERS}"); faire pour j dans $(seq "${NUM_CLUSTERS}"); faire si [ "$i" -ne "$j" ] alors echo "Activer la découverte des points de terminaison entre le cluster${i} et le cluster${j}" si [ "$OS" == "Darwin" ] alors # Définissez l'adresse IP du conteneur comme point de terminaison de l'API Kube afin que les clusters atteignent les serveurs API Kube d'autres clusters. docker_ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "cluster${i}-control-plane") istioctl créer-remote-secret \ --context="cluster${i}" \ --server="https://${docker_ip}:6443" \ --name="cluster${i}" | \ kubectl apply --validate=false --context="cluster${j}" -f - autre istioctl créer-remote-secret \ --context="cluster${i}" \ --name="cluster${i}" | \ kubectl apply --validate=false --context="cluster${j}" -f - être être fait fait
Exécutez le script ci-dessus : le secret distant est créé.
Vérifiez le journal istiod et constatez que le cluster distant est déjà surveillé.
Quatre pratiques de gestion du trafic multicluster Istio
kubectl create --context=cluster1 exemple d'espace de noms kubectl create --context=cluster2 exemple d'espace de noms kubectl label --context=cluster1 exemple d'espace de noms \ istio-injection = activé kubectl label --context=cluster2 exemple d'espace de noms \ istio-injection = activé kubectl apply --context=cluster1 \ -f échantillons/helloworld/helloworld.yaml \ -l service=helloworld -n exemple kubectl apply --context=cluster2 \ -f échantillons/helloworld/helloworld.yaml \ -l service=helloworld -n exemple
Déployer différentes versions de services dans différents clusters
Déployez l'application helloworld-v1 sur le cluster1 :kubectl apply --context=cluster1 \ -f échantillons/helloworld/helloworld.yaml \ -l version=v1 -n exempleDéployez l'application helloworld-v2 sur le cluster2 :
kubectl apply --context=cluster2 \ -f échantillons/helloworld/helloworld.yaml \ -l version=v2 -n exempleDéployer le client de test
kubectl apply --context=cluster1 \ -f échantillons/sleep/sleep.yaml -n échantillon kubectl apply --context=cluster2 \ -f échantillons/sleep/sleep.yaml -n échantillon
Confirmez que l'instance de chargement est déployée avec succès et que le side-car a été injecté

4.1 Vérifier le trafic entre clusters
Utilisez Sleep pod pour appeler à plusieurs reprises le service HelloWorld. Pour confirmer que l'équilibrage de charge fonctionne comme prévu, le service HelloWorld doit être appelé depuis tous les clusters.
Envoyer une requête au service HelloWorld depuis le pod Sleep du cluster1
Envoyer une requête au service HelloWorld depuis le pod Sleep du cluster2
4.3 Vérifier l'accès depuis la passerelle
Accédez au serveur Helloworld via la passerelle
Créez des ressources istio telles que le service virtuel et la passerelle. La liste de configuration est la suivante.
# helloworld-gateway.yaml Version api : networking.istio.io/v1beta1 genre : Passerelle métadonnées : nom : helloworld-gateway spécification : sélecteur: istio : ingressgateway # utiliser le contrôleur par défaut d'istio les serveurs: - port: numéro : 80 nom : http protocole : HTTP hôtes: - "*" --- Version api : networking.istio.io/v1beta1 genre : Service virtuel métadonnées : nom : bonjour le monde spécification : hôtes: - "*" passerelles : - passerelle helloworld http : - correspondre: - taper: exact : /bonjour itinéraire: - destination: hôte : helloworld port: numéro : 5000
Remarque : Cette configuration doit être appliquée aux deux clusters
L'effet d'accès est le suivant :

4.3 Vérifier l'équilibrage de charge régional
Pour un contrôle plus précis du trafic, définissez les pondérations des deux régions sur 80 % et 20 % respectivement, et utilisez DestinationRule pour configurer la répartition des pondérations . region1 -> zone1
region1 -> zone2
# localité-lb-weight.yaml Version api : networking.istio.io/v1beta1 genre : DestinationRule métadonnées : nom : bonjour le monde espace de noms : exemple spécification : hôte : helloworld.sample.svc.cluster.local Politique de trafic : pool de connexions : http : maxRequestsParConnexion : 1 équilibreur de charge : simple : ROUND_ROBIN localitéLbSetting : activé : vrai distribuer: - de : région1/* à: "région1/*": 80 "région2/*": 20 - de : région2/* à: "région2/*": 80 "région1/*": 20 Détection des valeurs aberrantes : consécutive5xxErreurs : 1 intervalle : 1 s baseEjectionTime : 1 m
Remarque : Cette configuration doit être appliquée aux deux clusters
Envoyer une requête au service HelloWorld depuis le cluster1 via la passerelle
Envoyer une requête au service HelloWorld depuis le cluster2 via la passerelle

4.4 Vérifier le basculement régional
Lorsque plusieurs instances de service sont déployées dans plusieurs régions/régions, si l'instance de service dans une certaine région/région n'est pas disponible, le trafic peut être transféré vers des instances de service dans d'autres régions/régions pour réaliser un basculement régional, garantissant ainsi la fiabilité du service.
# localité-lb-failover.yaml Version api : networking.istio.io/v1beta1 genre : DestinationRule métadonnées : nom : bonjour le monde espace de noms : exemple spécification : hôte : helloworld.sample.svc.cluster.local Politique de trafic : pool de connexions : http : maxRequestsPerConnection : 1 # Désactivez HTTP Keep-Alive et forcez chaque requête HTTP à utiliser une nouvelle politique de connexion équilibreur de charge : simple : ROUND_ROBIN localityLbSetting : # Configuration de l'équilibrage de charge régional, après avoir activé la détection des valeurs aberrantes, elle est activée par défaut. activé : vrai basculement : # Stratégie de basculement régionale - de : région1 à : région2 - de : région2 à : région1 Détection des valeurs aberrantes : consécutif5xxErrors : 1 # 1 erreur 5xx consécutive intervalle : 1s # Intervalle de détection 1s baseEjectionTime: 1m #Temps d'éjection de base 1m
Remarque : Cette configuration doit être appliquée aux deux clusters
Envoyer une requête au service HelloWorld depuis le cluster1 via la passerelle
Simulez une panne et définissez manuellement la version Helloworld V1 dans le cluster cluster1 pour qu'elle échoue.
Accédez à nouveau, la détection des pannes prend effet, déclenche le basculement et vérifie que la version dans la réponse est toujours v2, ce qui signifie que nous accédons au service helloworld dans la région 2, réalisant ainsi un basculement régional.
Le principe du basculement est que lorsque toutes les instances de la région actuelle sont indisponibles, il sera transféré vers la région actuelle. Sinon, le trafic sera envoyé vers d'autres instances disponibles dans la région actuelle.
Cinq remarques
Les références sont les suivantes :
-
Communauté open source istio (instructions d'installation pour l'architecture multi-primaire inter-réseau) : https://istio.io/latest/zh/docs/setup/install/multicluster/multi-primary_multi-network/
-
Référence du script de cluster d'installation Kind : https://github.com/cnych/multi-cluster-istio-kind/tree/main/kind-create
-
Référence de gestion des certificats multiclusters : https://istio.io/latest/zh/docs/tasks/security/cert-management/plugin-ca-cert/
Cliquez pour suivre et découvrir les nouvelles technologies de Huawei Cloud dès que possible~
La première mise à jour majeure de JetBrains 2024 (2024.1) est open source. Même Microsoft prévoit de la payer. Pourquoi est-elle encore critiquée pour son open source ? [Récupéré] Le backend de Tencent Cloud s'est écrasé : un grand nombre d'erreurs de service et aucune donnée après la connexion à la console. L'Allemagne doit également être "contrôlable de manière indépendante". Le gouvernement de l'État a migré 30 000 PC de Windows vers Linux deepin-IDE et a finalement réussi démarrage ! Visual Studio Code 1.88 est sorti. Bon gars, Tencent a vraiment transformé Switch en une "machine d'apprentissage pensante". Le bureau à distance RustDesk démarre et reconstruit le client Web. La base de données de terminaux open source de WeChat basée sur SQLite, WCDB, a reçu une mise à niveau majeure.