Cada vez mais, os clientes recorrem a nós com uma solicitação para fornecer acesso ao cluster Kubernetes para a possibilidade de acessar serviços dentro do cluster: para poder se conectar diretamente a algum banco de dados ou serviço, para conectar o aplicativo local a aplicativos dentro do cluster ...
Por exemplo, é necessário se conectar da sua máquina local para o serviço memcached.staging.svc.cluster.local
. Fornecemos essa oportunidade com uma VPN dentro do cluster ao qual o cliente se conecta. Para fazer isso, estamos anunciando as sub-redes de pods, serviços e envia o DNS do cluster ao cliente. Assim, quando o cliente tenta se conectar ao serviço memcached.staging.svc.cluster.local
, a solicitação vai para o DNS do cluster e, em resposta, recebe o endereço desse serviço da rede de serviço do cluster ou do endereço do pod.Configuramos os clusters K8s usando o kubeadm, onde a sub-rede de serviço é por padrão 192.168.0.0/16
e a rede de pod é 10.244.0.0/16
. Geralmente tudo funciona bem, mas há alguns pontos:- A sub-rede é
192.168.*.*
freqüentemente usada em redes de escritórios de clientes e ainda mais frequentemente em redes domésticas de desenvolvedores. E então temos conflitos: os roteadores domésticos trabalham nessa sub-rede e a VPN envia essas sub-redes do cluster para o cliente. - Temos vários clusters (produção, estágio e / ou vários clusters de desenvolvimento). Em todos eles, por padrão, haverá as mesmas sub-redes para pods e serviços, o que cria grandes dificuldades para trabalhar com serviços em vários clusters simultaneamente.
Já há algum tempo, adotamos a prática de usar sub-redes diferentes para serviços e pods na estrutura de um projeto - em geral, para que todos os clusters estejam com redes diferentes. No entanto, há um grande número de clusters em operação que eu não gostaria de rolar do zero, pois eles executam muitos serviços, aplicativos com estado, etc.E então nos perguntamos: como eu mudaria a sub-rede em um cluster existente?Pesquisa de decisões
A prática mais comum é recriar todos os serviços com o tipo ClusterIP. Como alternativa, eles também podem aconselhar isso:O processo a seguir apresenta um problema: depois de tudo configurado, os pods apresentam o IP antigo como servidor de nomes DNS no /etc/resolv.conf.
Como ainda não encontrei a solução, tive que redefinir todo o cluster com o kubeadm reset e iniciá-lo novamente.
Mas isso não serve para todos ... Aqui estão notas introdutórias mais detalhadas para o nosso caso:- Usado por flanela;
- Existem aglomerados nas nuvens e no ferro;
- Eu gostaria de evitar a implantação repetida de todos os serviços no cluster;
- É necessário fazer tudo com um mínimo de problemas;
- A versão do Kubernetes é 1.16.6 (no entanto, outras ações serão semelhantes para outras versões);
- A principal tarefa é
192.168.0.0/16
substituí-lo por um cluster implantado usando o kubeadm por uma sub-rede de serviço 172.24.0.0/16
.
E aconteceu que durante muito tempo foi interessante ver o que e como no Kubernetes ele é armazenado no etcd, o que pode ser feito com ele ... Então pensamos: “ Por que não atualizar os dados no etcd substituindo os antigos endereços IP (sub-rede) para os novos ? ”Procurando ferramentas prontas para trabalhar com dados no etcd, não encontramos nada que resolva completamente a tarefa. (A propósito, se você conhece algum utilitário para trabalhar com dados diretamente no etcd, seremos gratos pelos links.) No entanto, o OpenShift etcdhelper se tornou um bom ponto de partida (graças a seus autores!) .Este utilitário é capaz de se conectar a ETCD usando certificados e ler os dados usando os comandos ls
, get
, dump
.Adicionar etcdhelper
O seguinte pensamento é lógico: "O que impede a adição deste utilitário, adicionando a capacidade de gravar dados no etcd?"Foi traduzido para uma versão modificada do etcdhelper com dois novos changeServiceCIDR
e recursos changePodCIDR
. Você pode ver o código dela aqui .O que os novos recursos fazem? Algoritmo changeServiceCIDR
:- crie um desserializador;
- compile uma expressão regular para substituir o CIDR;
- passamos por todos os serviços com o tipo ClusterIP no cluster:
- decodifique o valor de etcd para o objeto Go;
- usando uma expressão regular, substitua os dois primeiros bytes do endereço;
- atribua ao serviço um endereço IP de uma nova sub-rede;
- crie um serializador, converta o objeto Go em protobuf, escreva novos dados no etcd.
A função changePodCIDR
é essencialmente a mesma changeServiceCIDR
- somente em vez de editar a especificação de serviço, fazemos isso para o nó e alteramos .spec.PodCIDR
para uma nova sub-rede.Prática
Alterar serviceCIDR
O plano para a implementação da tarefa é muito simples, mas envolve tempo de inatividade no momento da recriação de todos os pods no cluster. Depois de descrever as etapas principais, também compartilharemos nossos pensamentos sobre como, em teoria, essa simples pode ser minimizada.Ações preparatórias:- instalar o software necessário e montar o etcdhelper corrigido;
- etcd de backup e
/etc/kubernetes
.
Plano de ação curto para alterar o serviço- Modificando manifestos apiserver e controller-manager
- reemissão de certificados;
- Serviços de clusterIP mudam no etcd;
- reinicie todos os pods em um cluster.
A seguir, é apresentada uma sequência completa de ações em detalhes.1. Instale o etcd-client para despejo de dados:apt install etcd-client
2. Nós coletamos etcdhelper:- Colocamos golang:
GOPATH=/root/golang
mkdir -p $GOPATH/local
curl -sSL https://dl.google.com/go/go1.14.1.linux-amd64.tar.gz | tar -xzvC $GOPATH/local
echo "export GOPATH=\"$GOPATH\"" >> ~/.bashrc
echo 'export GOROOT="$GOPATH/local/go"' >> ~/.bashrc
echo 'export PATH="$PATH:$GOPATH/local/go/bin"' >> ~/.bashrc
- Nós nos salvamos
etcdhelper.go
, carregamos as dependências, coletamos:
wget https://raw.githubusercontent.com/flant/examples/master/2020/04-etcdhelper/etcdhelper.go
go get go.etcd.io/etcd/clientv3 k8s.io/kubectl/pkg/scheme k8s.io/apimachinery/pkg/runtime
go build -o etcdhelper etcdhelper.go
3. Faça o backup do etcd:backup_dir=/root/backup
mkdir ${backup_dir}
cp -rL /etc/kubernetes ${backup_dir}
ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt --key=/etc/kubernetes/pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt --endpoints https://192.168.199.100:2379 snapshot save ${backup_dir}/etcd.snapshot
4. Altere a sub-rede de serviço nos manifestos do plano de controle do Kubernetes. Nos arquivos /etc/kubernetes/manifests/kube-apiserver.yaml
e /etc/kubernetes/manifests/kube-controller-manager.yaml
altere o parâmetro --service-cluster-ip-range
para uma nova sub-rede: em 172.24.0.0/16
vez disso 192.168.0.0/16
.5. Como estamos alterando a sub-rede de serviço para a qual o kubeadm emite certificados para o apiserver (inclusive), eles devem ser reemitidos:- Vejamos quais domínios e endereços IP o certificado atual é emitido:
openssl x509 -noout -ext subjectAltName </etc/kubernetes/pki/apiserver.crt
X509v3 Subject Alternative Name:
DNS:dev-1-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:apiserver, IP Address:192.168.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
- Prepare a configuração mínima para o kubeadm:
cat kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: "172.24.0.0/16"
apiServer:
certSANs:
- "192.168.199.100"
- Vamos excluir o crt e a chave antigos, porque sem isso um novo certificado não será emitido:
rm /etc/kubernetes/pki/apiserver.{key,crt}
- Emita novamente certificados para o servidor API:
kubeadm init phase certs apiserver --config=kubeadm-config.yaml
- , :
openssl x509 -noout -ext subjectAltName </etc/kubernetes/pki/apiserver.crt
X509v3 Subject Alternative Name:
DNS:kube-2-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP Address:172.24.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
- API- :
docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
admin.conf
:
kubeadm alpha certs renew admin.conf
- etcd:
./etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt -cert /etc/kubernetes/pki/etcd/server.crt -key /etc/kubernetes/pki/etcd/server.key -endpoint https://127.0.0.1:2379 change-service-cidr 172.24.0.0/16
! , pod' /etc/resolv.conf
CoreDNS (kube-dns), kube-proxy iptables . . - ConfigMap'
kube-system
:
kubectl -n kube-system edit cm kubelet-config-1.16
— clusterDNS
IP- kube-dns: kubectl -n kube-system get svc kube-dns
.
kubectl -n kube-system edit cm kubeadm-config
— data.ClusterConfiguration.networking.serviceSubnet
. - kube-dns, kubelet :
kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
- Resta reiniciar todos os pods no cluster:
kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(\S+)\s+(\S+).*/kubectl --namespace \1 delete pod \2/e'
Tempo de inatividade minimizado
Reflexões sobre como minimizar o tempo de inatividade:- Após alterar as manifestações do plano de controle, crie um novo serviço kube-dns, por exemplo, com um nome
kube-dns-tmp
e um novo endereço 172.24.0.10
. - Crie
if
no etcdhelper, que não modificará o serviço kube-dns. - Substitua o endereço em todos os kubelets
ClusterDNS
por um novo, enquanto o serviço antigo continuará funcionando simultaneamente com o novo. - Aguarde até que os pods com aplicativos rolem sozinhos por motivos naturais ou em um horário combinado.
- Exclua o serviço
kube-dns-tmp
e altere-o serviceSubnetCIDR
para o serviço kube-dns.
Esse plano minimizará o tempo de inatividade em até ~ um minuto - pelo tempo em que o serviço for removido kube-dns-tmp
e a sub-rede do serviço for substituída kube-dns
.PodNetwork de modificação
Ao mesmo tempo, decidimos ver como modificar o podNetwork usando o etcdhelper resultante. A sequência de ações é a seguinte:- nós corrigimos as configurações
kube-system
; - consertamos o manifesto do kube-controller-manager;
- nós mudamos o podCIDR diretamente no etcd;
- reinicie todos os nós do cluster.
Agora, mais sobre essas ações:1. Modifique o ConfigMap no espaço para nome kube-system
:kubectl -n kube-system edit cm kubeadm-config
- corrigido data.ClusterConfiguration.networking.podSubnet
em uma nova sub-rede 10.55.0.0/16
.kubectl -n kube-system edit cm kube-proxy
- nós corrigimos data.config.conf.clusterCIDR: 10.55.0.0/16
.2. Modifique o manifesto do gerente do controlador:vim /etc/kubernetes/manifests/kube-controller-manager.yaml
- nós corrigimos --cluster-cidr=10.55.0.0/16
.3. Olhe para os valores atuais .spec.podCIDR
, .spec.podCIDRs
, .InternalIP
, .status.addresses
para todos os nós do cluster:kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'
[
{
"name": "kube-2-master",
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
],
"InternalIP": "192.168.199.2"
},
{
"name": "kube-2-master",
"podCIDR": "10.244.0.0/24",
"podCIDRs": [
"10.244.0.0/24"
],
"InternalIP": "10.0.1.239"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.244.1.0/24",
"podCIDRs": [
"10.244.1.0/24"
],
"InternalIP": "192.168.199.222"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.244.1.0/24",
"podCIDRs": [
"10.244.1.0/24"
],
"InternalIP": "10.0.4.73"
}
]
4. Substitua o podCIDR fazendo alterações diretamente no etcd:./etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt -cert /etc/kubernetes/pki/etcd/server.crt -key /etc/kubernetes/pki/etcd/server.key -endpoint https://127.0.0.1:2379 change-pod-cidr 10.55.0.0/16
5. Verifique se o podCIDR realmente mudou:kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'
[
{
"name": "kube-2-master",
"podCIDR": "10.55.0.0/24",
"podCIDRs": [
"10.55.0.0/24"
],
"InternalIP": "192.168.199.2"
},
{
"name": "kube-2-master",
"podCIDR": "10.55.0.0/24",
"podCIDRs": [
"10.55.0.0/24"
],
"InternalIP": "10.0.1.239"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.55.1.0/24",
"podCIDRs": [
"10.55.1.0/24"
],
"InternalIP": "192.168.199.222"
},
{
"name": "kube-2-worker-01f438cf-579f9fd987-5l657",
"podCIDR": "10.55.1.0/24",
"podCIDRs": [
"10.55.1.0/24"
],
"InternalIP": "10.0.4.73"
}
]
6. Por sua vez, reiniciaremos todos os nós do cluster.7. Se pelo menos um nó sair do podCIDR antigo , o kube-controller-manager não poderá iniciar e os pods no cluster não serão planejados.De fato, a alteração do podCIDR pode ser facilitada (por exemplo, assim ). Mas queríamos aprender a trabalhar diretamente com o etcd, porque há casos em que editar objetos do Kubernetes no etcd é a única opção possível. (Por exemplo, você não pode simplesmente alterar o campo de um serviço sem tempo de inatividade spec.clusterIP
.)Total
O artigo considera a possibilidade de trabalhar com dados no etcd diretamente, ou seja, ignorando a API do Kubernetes. Às vezes, essa abordagem permite que você faça "coisas complicadas". As operações descritas no texto foram testadas em clusters K8s reais. No entanto, seu status de prontidão para uso generalizado é PoC (prova de conceito) . Portanto, se você quiser usar uma versão modificada do utilitário etcdhelper em seus clusters, faça-o por sua própria conta e risco.PS
Leia também no nosso blog: