One place for hosting & domains

      How To Set Up Logical Replication with PostgreSQL 10 on Ubuntu 18.04


      Introduction

      When setting up an application for production, it’s often useful to have multiple copies of your database in place. The process of keeping database copies in sync is called replication. Replication can provide high-availability horizontal scaling for high volumes of simultaneous read operations, along with reduced read latencies. It also allows for peer-to-peer replication between geographically distributed database servers.

      PostgreSQL is an open-source object-relational database system that is highly extensible and compliant with ACID (Atomicity, Consistency, Isolation, Durability) and the SQL standard. Version 10.0 of PostgreSQL introduced support for logical replication, in addition to physical replication. In a logical replication scheme, high-level write operations are streamed from a master database server into one or more replica database servers. In a physical replication scheme, binary write operations are instead streamed from master to replica, producing a byte-for-byte exact copy of the original content. In cases where you would like to target a particular subset of data, such as off-load reporting, patching, or upgrading, logical replication can offer speed and flexibility.

      In this tutorial, you will configure logical replication with PostgreSQL 10 on two Ubuntu 18.04 servers, with one server acting as the master and the other as the replica. By the end of the tutorial you will be able to replicate data from the master server to the replica using logical replication.

      Prerequisites

      To follow this tutorial, you will need:

      Step 1 — Configuring PostgreSQL for Logical Replication

      There are several configuration settings you will need to modify to enable logical replication between your servers. First, you’ll configure Postgres to listen on the private network interface instead of the public one, as exposing data over the public network is a security risk. Then you’ll configure the appropriate settings to allow replication to db-replica.

      On db-master, open /etc/postgresql/10/main/postgresql.conf, the main server configuration file:

      • sudo nano /etc/postgresql/10/main/postgresql.conf

      Find the following line:

      /etc/postgresql/10/main/postgresql.conf

      ...
      #listen_addresses = 'localhost'         # what IP address(es) to listen on;
      ...
      

      Uncomment it by removing the #, and add your db_master_private_ip_address to enable connections on the private network:

      Note: In this step and the steps that follow, make sure to use the private IP addresses of your servers, and not their public IPs. Exposing a database server to the public internet is a considerable security risk.

      /etc/postgresql/10/main/postgresql.conf

      ...
      listen_addresses = 'localhost, db_master_private_ip_address'
      ...
      

      This makes db-master listen for incoming connections on the private network in addition to the loopback interface.

      Next, find the following line:

      /etc/postgresql/10/main/postgresql.conf

      ...
      #wal_level = replica                    # minimal, replica, or logical
      ...
      

      Uncomment it, and change it to set the PostgreSQL Write Ahead Log (WAL) level to logical. This increases the volume of entries in the log, adding the necessary information for extracting discrepancies or changes to particular data sets:

      /etc/postgresql/10/main/postgresql.conf

      ...
      wal_level = logical
      ...
      

      The entries on this log will be consumed by the replica server, allowing for the replication of the high-level write operations from the master.

      Save the file and close it.

      Next, let’s edit /etc/postgresql/10/main/pg_hba.conf, the file that controls allowed hosts, authentication, and access to databases:

      • sudo nano /etc/postgresql/10/main/pg_hba.conf

      After the last line, let’s add a line to allow incoming network connections from db-replica. We’ll use db-replica‘s private IP address, and specify that connections are allowed from all users and databases:

      /etc/postgresql/10/main/pg_hba.conf

      ...
      # TYPE      DATABASE        USER            ADDRESS                               METHOD
      ...
      host         all            all             db_replica_private_ip_address/32      md5
      

      Incoming network connections will now be allowed from db-replica, authenticated by a password hash (md5).

      Save the file and close it.

      Next, let’s set our firewall rules to allow traffic from db-replica to port 5432 on db-master:

      • sudo ufw allow from db_replica_private_ip_address to any port 5432

      Finally, restart the PostgreSQL server for the changes to take effect:

      • sudo systemctl restart postgresql

      With your configuration set to allow logical replication, you can now move on to creating a database, user role, and table.

      Step 2 — Setting Up a Database, User Role, and Table

      To test the functionality of your replication settings, let’s create a database, table, and user role. You will create an example database with a sample table, which you can then use to test logical replication between your servers. You will also create a dedicated user and assign them privileges over both the database and the table.

      First, open the psql prompt as the postgres user with the following command on both db-master and db-replica:

      Create a new database called example on both hosts:

      Note: The final ; in these commands is required. On interactive sessions, PostgreSQL will not execute SQL commands until you terminate them with a semicolon. Meta-commands (those starting with a backslash, like q and c) directly control the psql client itself, and are therefore exempt from this rule. For more on meta-commands and the psql client, please refer to the PostgreSQL documentation.

      Using the connect meta-command, connect to the databases you just created on each host:

      Create a new table called widgets with arbitrary fields on both hosts:

      • CREATE TABLE widgets
      • (
      • id SERIAL,
      • name TEXT,
      • price DECIMAL,
      • CONSTRAINT widgets_pkey PRIMARY KEY (id)
      • );
      • CREATE TABLE widgets
      • (
      • id SERIAL,
      • name TEXT,
      • price DECIMAL,
      • CONSTRAINT widgets_pkey PRIMARY KEY (id)
      • );

      The table on db-replica does not need to be identical to its db-master counterpart. However, it must contain every single column present on the table at db-master. Additional columns must not have NOT NULL or other constraints. If they do, replication will fail.

      On db-master, let's create a new user role with the REPLICATION option and a login password. The REPLICATION attribute must be assigned to any role used for replication. We will call our user sammy, but you can replace this with your own username. Make sure to also replace my_password with your own secure password:

      • CREATE ROLE sammy WITH REPLICATION LOGIN PASSWORD 'my_password';

      Make a note of your password, as you will use it later on db-replica to set up replication.

      Still on db-master, grant full privileges on the example database to the user role you just created:

      • GRANT ALL PRIVILEGES ON DATABASE example TO sammy;

      Next, grant privileges on all of the tables contained in the database to your user:

      • GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO sammy;

      The public schema is a default schema in each database into which tables are automatically placed.

      With these privileges set, you can now move on to making the tables in your example database available for replication.

      Step 3 — Setting Up a Publication

      Publications are the mechanism that PostgreSQL uses to make tables available for replication. The database server will keep track internally of the connection and replication status of any replica servers associated with a given publication. On db-master, you will create a publication, my_publication, that will function as a master copy of the data that will be sent to your subscribers — in our case, db-replica.

      On db-master, create a publication called my_publication:

      • CREATE PUBLICATION my_publication;

      Add the widgets table you created previously to it:

      • ALTER PUBLICATION my_publication ADD TABLE widgets;

      With your publication in place, you can now add a subscriber that will pull data from it.

      Step 4 — Creating a Subscription

      Subscriptions are used by PostgreSQL to connect to existing publications. A publication can have many subscriptions across different replica servers, and replica servers can also have their own publications with subscribers. To access the data from the table you created on db-master, you will need to create a subscription to the publication you created in the previous step, my_publication.

      On db-replica, let's create a subscription called my_subscription. The CREATE SUBSCRIPTION command will name the subscription, while the CONNECTION parameter will define the connection string to the publisher. This string will include the master server's connection details and login credentials, including the username and password you defined earlier, along with the name of the example database. Once again, remember to use db-master's private IP address, and replace my_password with your own password:

      • CREATE SUBSCRIPTION my_subscription CONNECTION 'host=db_master_private_ip_address port=5432 password=my_password user=sammy dbname=example' PUBLICATION my_publication;

      You will see the following output confirming the subscription:

      Output

      NOTICE: created replication slot "my_subscription" on publisher CREATE SUBSCRIPTION

      Upon creating a subscription, PostgreSQL will automatically sync any pre-existing data from the master to the replica. In our case there is no data to sync since the widgets table is empty, but this is a useful feature when adding new subscriptions to an existing database.

      With a subscription in place, let's test the setup by adding some demo data to the widgets table.

      Step 5 — Testing and Troubleshooting

      To test replication between our master and replica, let's add some data to the widgets table and verify that it replicates correctly.

      On db-master, insert the following data on the widgets table:

      • INSERT INTO widgets (name, price) VALUES ('Hammer', 4.50), ('Coffee Mug', 6.20), ('Cupholder', 3.80);

      On db-replica, run the following query to fetch all the entries on this table:

      You should now see:

      Output

      id | name | price ----+------------+------- 1 | Hammer | 4.50 2 | Coffee Mug | 6.20 3 | Cupholder | 3.80 (3 rows)

      Success! The entries have been successfully replicated from db-master to db-replica. From now on, all INSERT, UPDATE, and DELETE queries will be replicated across servers unidirectionally.

      One thing to note about write queries on replica servers is that they are not replicated back to the master server. PostgreSQL currently has limited support for resolving conflicts when the data between servers diverges. If there is a conflict, the replication will stop and PostgreSQL will wait until the issue is manually fixed by the database administrator. For that reason, most applications will direct all write operations to the master server, and distribute reads among available replica servers.

      You can now exit the psql prompt on both servers:

      Now that you have finished testing your setup, you can add and replicate data on your own.

      Troubleshooting

      If replication doesn't seem to be working, a good first step is checking the PostgreSQL log on db-replica for any possible errors:

      • tail /var/log/postgresql/postgresql-10-main.log

      Here are some common problems that can prevent replication from working:

      • Private networking is not enabled on both servers, or the servers are on different networks;
      • db-master is not configured to listen for connections on the correct private network IP;
      • The Write Ahead Log level on db-master is incorrectly configured (it must be set to logical);
      • db-master is not configured to accept incoming connections from the correct db-replica private IP address;
      • A firewall like UFW is blocking incoming PostgreSQL connections on port 5432;
      • There are mismatched table names or fields between db-master and db-replica;
      • The sammy database role is missing the required permissions to access the example database on db-master;
      • The sammy database role is missing the REPLICATION option on db-master;
      • The sammy database role is missing the required permissions to access the widgets table on db-master;
      • The table wasn't added to the publication on db-master.

      After resolving the existing problem(s), replication should take place automatically. If it doesn't, use following command to remove the existing subscription before recreating it:

      • DROP SUBSCRIPTION my_subscription;

      Conclusion

      In this tutorial you've successfully installed PostgreSQL 10 on two Ubuntu 18.04 servers and configured logical replication between them.

      You now have the required knowledge to experiment with horizontal read scaling, high availability, and the geographical distribution of your PostgreSQL database by adding additional replica servers.

      To learn more about logical replication in PostgreSQL 10, you can read the chapter on the topic on the official PostgreSQL documentation, as well as the manual entries on the CREATE PUBLICATION and CREATE SUBSCRIPTION commands.



      Source link

      Como Inspecionar a Rede do Kubernetes


      Introducão

      O Kubernetes é um sistema de orquestração de container que pode gerenciar aplicações containerizadas em um cluster de nodes de servidores. A manutenção da conectividade de rede entre todos os containers em um cluster requer algumas técnicas avançadas de rede. Neste artigo vamos cobrir brevemente algumas ferramentas e técnicas para inspecionar essa configuração de rede.

      Estas ferramentas podem ser úteis se você estiver debugando problemas de conectividade, investigando problemas de taxa de transferência de rede, ou explorando o Kubernetes para aprender como ele funciona.

      Se você quiser aprender mais sobre o Kubernetes em geral, nosso guia An Introduction to Kubernetes cobre o básico. Para uma visão específica de rede do Kubernetes, por favor leia Kubernetes Networking Under the Hood.

      Começando

      Este tutorial irá assumir que você tem um cluster Kubernetes, com o kubectl instalado localmente e configurado para se conectar ao cluster.

      As seções seguintes contém muitos comandos que se destinam a serem executados em um node do Kubernetes. Eles se parecerão com isso:

      • echo 'este é um comando de node'

      Comandos que devem ser executados em sua máquina local terão a seguinte aparência:

      • echo 'este é um comando local'

      Nota: A maioria dos comandos neste tutorial precisará ser executada como usuário root. Se em vez disso você usar um usuário habilitado para o sudo em seus nodes de Kubernetes, por favor adicione sudo para executar comandos quando necessário.

      Encontrando o IP do Cluster de um Pod

      Para encontrar o endereço IP de um pod do Kubermetes, utilize o comando kubectl get pod em sua máquina local, com a opção -o wide. Esta oção irá listar mais informações, incluindo o node onde o pod reside, e o IP do cluster do pod.

      Output

      NAME READY STATUS RESTARTS AGE IP NODE hello-world-5b446dd74b-7c7pk 1/1 Running 0 22m 10.244.18.4 node-one hello-world-5b446dd74b-pxtzt 1/1 Running 0 22m 10.244.3.4 node-two

      A coluna IP irá conter o endereço IP local do cluster para cada pod.

      Se você não vir o pod que está procurando, certifique-se de que você está no namespace certo. Você pode listar todos os pods em todos os namespaces adicionando o flag --all-namespaces.

      Encontrando o IP de um Serviço

      Você pode também encontrar o IP de um serviço utilizando o kubectl. Neste caso iremos listar todos os serviços em todos os namespaces:

      • kubectl get service --all-namespaces

      Output

      Output NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 10.32.0.1 <none> 443/TCP 6d kube-system csi-attacher-doplugin ClusterIP 10.32.159.128 <none> 12345/TCP 6d kube-system csi-provisioner-doplugin ClusterIP 10.32.61.61 <none> 12345/TCP 6d kube-system kube-dns ClusterIP 10.32.0.10 <none> 53/UDP,53/TCP 6d kube-system kubernetes-dashboard ClusterIP 10.32.226.209 <none> 443/TCP 6d

      O IP do serviço pode ser encontrado na coluna CLUSTER-IP.

      Encontrando e Inserindo Namespaces de Rede do Pod

      Cada pod do Kubernetes é atribuído ao seu próprio namespace de rede. Namespaces de rede (ou netns) são primitivas de rede do Linux que fornecem isolação entre dispositivos de rede.

      Isto pode ser útil para executar comandos a partir do netns do pod, para verificar resolução de DNS ou conectividade geral de rede. Para fazer isto, precisamos primeiro olhar para o ID de processo de um dos containers em um pod. Para o Docker, podemos fazer isto com uma série de dois comandos. Primeiro, liste os containers que estão executando em um node:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 173ee46a3926 gcr.io/google-samples/node-hello "/bin/sh -c 'node se…" 9 days ago Up 9 days k8s_hello-world_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0 11ad51cb72df k8s.gcr.io/pause-amd64:3.1 "/pause" 9 days ago Up 9 days k8s_POD_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0 . . .

      Encontre o container ID ou name de qualquer container no pod que você está interessado. Na saída acima estamos mostrando dois containers:

      • O primeiro container é o app hello-world executando no pod hello-world
      • O segundo é um container pause executando no pod hello-world. Este container existe apenas para manter o namespace de rede do pod

      Para obter o ID de processo de um dos containers, tome nota do container ID ou name, e utilize-o no seguinte comando docker:

      • docker inspect --format '{{ .State.Pid }}' container-id-or-name

      Output

      14552

      Um ID de processo (ou PID) será a saída. Agora podemos utilizar o programa nsenter para executar um comando no namespace de rede do processo:

      • nsenter -t your-container-pid -n ip addr

      Certifique-se de utilizar seu próprio PID, e substitua ip addr pelo comando que você gostaria de executar dentro do namespace de rede do pod.

      Nota: Uma vantagem de se utilizar nsenter para executar comandos no namespace do pod – versus a utilização de algo como docker exec – é que você tem acesso a todos os comandos disponíveis no node, em vez do conjunto de comandos gralmente limitados instalados em containers.

      Encontrando a Interface Ethernet Virtual de um Pod

      Cada namespace de rede do pod comunica-se com o netns raiz do node através de um pipe ethernet virtual. No lado do node, este pipe aparece como um dispositivo que geralmente começa com veth e termina em um identificador único, tal como veth77f2275 ou veth01. Dentro do pod este pipe aparece como eth0.

      Pode ser útil correlacionar qual dispositivo veth está emparelhado com um pod em particular. Para fazer isto, vamos listar todos os dispositivos de rede no node, em seguida listar os dispositivos no namespace de rede do pod. Podemos correlacionar os números dos dispositivos entre as duas listas para fazer a conexão.

      Primeiro, execute ip addr no namespace de rede do pod utilizando o nsenter. Consulte a seção anterior Encontrando e Inserindo Namespaces de Rede do Pod para detlahes de como fazer isto:

      • nsenter -t pid-do-seu-container -n ip addr

      Output

      1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link/ether 02:42:0a:f4:03:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.244.3.4/24 brd 10.244.3.255 scope global eth0 valid_lft forever preferred_lft forever

      O comando mostrará uma lista das interfaces do pod. Observe o número if11 depois de eth0@ na saída do exemplo. Isso significa que essa eth0 do pod está ligada à décima primeira interface do node. Agora execute ip addr no namespace padrão do node para listar suas interfaces:

      Output

      1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever . . . 7: veth77f2275@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP group default link/ether 26:05:99:58:0d:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::2405:99ff:fe58:db9/64 scope link valid_lft forever preferred_lft forever 9: vethd36cef3@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP group default link/ether ae:05:21:a2:9a:2b brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::ac05:21ff:fea2:9a2b/64 scope link valid_lft forever preferred_lft forever 11: veth4f7342d@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP group default link/ether e6:4d:7b:6f:56:4c brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet6 fe80::e44d:7bff:fe6f:564c/64 scope link valid_lft forever preferred_lft forever

      A décima primeira interface é a veth4f7342d nessa saída do exemplo. Este é o pipe ethernet virtual para o pod que estamos inevstigando.

      Inspeção do Rastreamento de Conexão do Conntrack

      Antes da versão 1.11, o Kubernetes usava o iptables NAT e o módulo conntrack do kernel para rastrear conexões. Para listar todas as conexões sendo rastreadas atualmente, utilize o comando conntrack:

      Para assitir continuamente por novas conexões, utilize o flag -E:

      Para listar conexões controladas pelo conntrack a um endereço de destino específico, utilize o flag -d:

      • conntrack -L -d 10.32.0.1

      Se os seus nodes estão tendo problemas para fazer conexões confiáveis aos serviços, é possível que sua tabela de rastreamento de conexões esteja cheia e que novas conexões estejam sendo descartadas. Se é esse o caso você pode ver mensagens como as seguintes em seus logs de sistema:

      /var/log/syslog

      
      Jul 12 15:32:11 worker-528 kernel: nf_conntrack: table full, dropping packet.
      
      

      Há uma configuração do sysctl para o número máximo de conexões a serem rastreadas. Você pode listar o valor atual com o seguinte comando:

      • sysctl net.netfilter.nf_conntrack_max

      Output

      net.netfilter.nf_conntrack_max = 131072

      Para definir um novo valor, utilize o flag -w:

      • sysctl -w net.netfilter.nf_conntrack_max=198000

      Para tornar essa configuração permanente, adicione-a ao arquivo sysctl.conf:

      /etc/sysctl.conf

      
      . . .
      net.ipv4.netfilter.ip_conntrack_max = 198000
      

      Inspecionando as Regras do Iptables

      Antes da versão 1.11, o Kubernetes usou o iptables NAT para implementar tradução de IP virtual e o balanceamento de carga para IPs de Serviço.

      Para fazer um dump de todas as regras iptables em um node, utilize o comando iptables-save:

      Como a saída pode ser longa, você pode querer redirecionar para um arquivo (iptables-save > output.txt) ou um paginador (iptables-save | less) para avaliar suas regras mais facilmente.

      Para listar apenas as regras NAT do Serviço do Kubernetes, utilize o comando iptables e o flag -L para especificar o canal correto:

      • iptables -t nat -L KUBE-SERVICES

      Output

      Chain KUBE-SERVICES (2 references) target prot opt source destination KUBE-SVC-TCOU7JCQXEZGVUNU udp -- anywhere 10.32.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- anywhere 10.32.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain KUBE-SVC-XGLOHA7QRQ3V22RZ tcp -- anywhere 10.32.226.209 /* kube-system/kubernetes-dashboard: cluster IP */ tcp dpt:https . . .

      Consultando o DNS do Cluster

      Uma maneira de fazer o debug da resolução de DNS do cluster é fazer o deploy de um container para debug com todas as feramentas que você precisa, em seguida utilize kubectl para executar nslookup nele. Isso é descrito na documentação oficial do Kubernetes.

      Outra maneira de consultar o DNS do cluster é a utilização do dig e nsenter a partir do node. Se o dig não está instalado, pode-se instalar com o apt em distribuições Linux baseadas em Debian.

      Primeiro, encontre o IP do cluster do serviço kube-dns:

      • kubectl get service -n kube-system kube-dns

      Output

      NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.32.0.10 <none> 53/UDP,53/TCP 15d

      O IP do cluster está destacado acima. Em seguida, vamos utilizar nsenter para executar o dig no namespace do container. Veja a seção Encontrando e Inserindo Namespaces de Rede do Pod para mais informações sobre isso.

      • nsenter -t 14346 -n dig kubernetes.default.svc.cluster.local @10.32.0.10

      Este comando dig procura o nome de domínio completo do Serviço de service-name.namespace.svc.cluster.local e especifica o IP do serviço DNS do cluster (@10.32.0.10).

      Olhando para os Detalhes do IPVS

      A partir do Kubernetes 1.11, o kube-proxy pode configurar o IPVS para lidar com a tradução de IPs de serviços virtuais para IPs de pods. Você pode listar a tabela de tradução com ipvsadm:

      Output

      IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 100.64.0.1:443 rr -> 178.128.226.86:443 Masq 1 0 0 TCP 100.64.0.10:53 rr -> 100.96.1.3:53 Masq 1 0 0 -> 100.96.1.4:53 Masq 1 0 0 UDP 100.64.0.10:53 rr -> 100.96.1.3:53 Masq 1 0 0 -> 100.96.1.4:53 Masq 1 0 0

      Para mostrar um único IP de serviço, utilize a opção -t e especifique o IP desejado:

      • ipvsadm -Ln -t 100.64.0.10:53

      Output

      Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 100.64.0.10:53 rr -> 100.96.1.3:53 Masq 1 0 0 -> 100.96.1.4:53 Masq 1 0 0

      Conclusão

      Neste artigo, analisamos alguns comandos e técnicas para explorar e inspecionar os detalhes da rede do cluster do Kubernetes. Para mais informações sobre Kubernetes, dê uma olhada na nossa tag de tutoriais de Kubernetes e na documentação oficial do Kubernetes.



      Source link

      A Rede do Kubernetes nos Bastidores


      Introdução

      O Kubernetes é um poderoso sistema de orquestração de container que pode gerenciar o deployment e a operação de aplicações containerizadas em um cluster de servidores. Além de coordenar as cargas de trabalho do container, o Kubernetes fornece a infraestrutura e as ferramentas necessárias para manter a conectividade de rede entre suas aplicações e serviços.

      A Documentação de Rede do Cluster do Kubernetes afirma que os requisitos básicos de uma rede Kubernetes são:

      • todos os containers podem se comunicar com todos os outros containers sem NAT
      • todos os nodes podem se comunicar com todos os containers (e vice-versa) sem NAT
      • o IP com o qual um container se vê é o mesmo IP que os outros o veem

      Neste artigo, discutiremos como o Kubernetes satisfaz esses requisitos de rede dentro de um cluster: como os dados se movem dentro de um pod, entre pods e entre nodes.

      Também mostraremos como um Serviço do Kubernetes pode fornecer um único endereço IP estático e uma entrada de DNS para uma aplicação, facilitando a comunicação com serviços que podem ser distribuídos entre vários pods de dimensionamento e deslocamento constantes.

      Se você não estiver familiarizado com a terminologia dos pods e nodes do Kubernetes ou com outros itens básicos, nosso artigo An Introduction to Kubernetes cobre a arquitetura geral e os componentes envolvidos.

      Primeiro, vamos dar uma olhada na situação da rede dentro de um único pod.

      A Rede do Pod

      No Kubernetes, um pod é a unidade mais básica de organização: um grupo de containers fortemente acoplados que estão todos intimamente relacionados e executam uma única função ou serviço.

      Em termos de rede, o Kubernetes trata pods de maneira semelhante a uma máquina virtual tradicional ou a um único host físico: cada pod recebe um único endereço IP exclusivo, e todos os containers dentro do pod compartilham esse endereço e se comunicam entre si através da interface de loopback lo usando o nome de host localhost. Isso é conseguido atribuindo todos os containers do pod à mesma pilha de rede.

      Essa situação deve parecer familiar para qualquer pessoa que fez o deploy de vários serviços em um único host antes dos dias da containerização. Todos os serviços precisam usar uma porta exclusiva para ouvir, mas, por outro lado, a comunicação é descomplicada e tem pouca sobrecarga.

      A Rede de Pod para Pod

      A maioria dos clusters do Kubernetes precisará fazer deploy de vários pods por node. A comunicação de pod para pod pode ocorrer entre dois pods no mesmo node ou entre dois nodes diferentes.

      Comunicação Pod a Pod em um Node

      Em um único node, você pode ter vários pods que precisam se comunicar diretamente uns com os outros. Antes de rastrearmos a rota de um pacote entre os pods, vamos analisar a configuração de rede de um node. O diagrama a seguir fornece uma visão geral, que abordaremos em detalhes:

      Cada node tem uma interface de rede – eth0 neste exemplo – anexada à rede de clusters do Kubernetes. Essa interface fica dentro do namespace de rede root do node. Este é o namespace padrão para dispositivos de rede no Linux.

      Assim como os namespaces de processo permitem que os containers isolem as aplicações em execução umas das outras, namespaces de rede isolam dispositivos de rede tais como interfaces e bridges. Cada pod em um node é atribuído ao seu próprio namespace de rede isolado.

      Os namespaces de pod são conectados de volta ao namespace root com um par ethernet virtual, essencialmente um pipe entre os dois namespaces com uma interface em cada extremidade (aqui estamos utilizando veth1 no namespace root e eth0 dentro do pod).

      Finalmente, os pods são conectados entre si e à interface eth0 do node através de uma bridge, br0 (seu node pode usar algo como cbr0 ou docker0). Uma bridge funciona essencialmente como um switch Ethernet físico, usando ARP (protocolo de resolução de endereço) ou roteamento baseado em IP para procurar outras interfaces locais para onde direcionar o tráfego.

      Agora vamos rastrear um pacote do pod1 para o pod2:

      • pod1 cria um pacote com o IP do pod2 como seu destino
      • O pacote trafega pelo par de ethernet virtual para o namespace root da rede
      • O pacote continua até a bridge br0
      • Como o pod de destino está no mesmo node, a bridge envia o pacote para o par de ethernet virtual do pod2
      • O pacote trafega através do par de ethernet virtual, no namespace de rede do pod2 e na interface de rede eth0 do pod.

      Agora que rastreamos um pacote de pod para pod dentro de um node, vamos ver como o tráfego do pod viaja entre nodes.

      Comunicação Pod para Pod entre dois Nodes

      Como cada pod em um cluster tem um IP exclusivo e cada pod pode se comunicar diretamente com todos os outros pods, um pacote que se move entre os pods em dois nodes distintos é muito semelhante ao cenário anterior.

      Vamos rastrear um pacote do pod1 para o pod3, que está em outro node:

      • pod1 cria um pacote com o IP do pod3 como seu destino
      • O pacote trafega pelo par de ethernet virtual para o namespace root da rede
      • O pacote continua até a bridge br0
      • A bridge não encontra nenhuma interface local para onde rotear, assim o pacote é enviado para a rota padrão via eth0
      • Opcional: se o seu cluster exigir uma sobreposição de rede para rotear corretamente os pacotes para os nodes, o pacote poderá ser encapsulado em um pacote VXLAN (ou outra técnica de virtualização de rede) antes de ir para a rede. Alternativamente, a própria rede pode ser configurada com as rotas estáticas adequadas, nesse caso, o pacote trafega para eth0 e sai da rede inalterado.
      • O pacote entra na rede do cluster e é roteado para o node correto.
      • O pacote entra no node de destino na eth0
      • Opcional: se o seu pacote foi encapsulado, ele será desencapsulado neste momento
      • O pacote continua para a bridge br0
      • A bridge encaminha o pacote para o par de ethernet virtual do pod de destino
      • O pacote passa pelo par de ethernet virtual para a interface eth0 do pod

      Agora que estamos familiarizados com a forma como os pacotes são roteados por meio dos endereços IP do pod, vamos dar uma olhada nos serviços do Kubernetes e em como eles se baseiam nessa infraestrutura.

      A Rede de Pod para Serviço

      Seria difícil enviar tráfego para uma aplicação específica usando apenas IPs de pod, pois a natureza dinâmica de um cluster do Kubernetes significa que os pods podem ser movidos, reiniciados, atualizados ou redimensionados para dentro e para fora. Além disso, alguns serviços terão muitas réplicas, por isso precisamos de alguma forma de balancear a carga entre eles.

      O Kubernetes resolve esse problema com os Serviços. Um Serviço é um objeto da API que mapeia um único IP virtual (VIP) para um conjunto de IPs de pod. Além disso, o Kubernetes fornece uma entrada de DNS para o nome de cada serviço e IP virtual, para que os serviços possam ser facilmente acessados por nome.

      O mapeamento de IPs virtuais para IPs de pods dentro do cluster é coordenado pelo processo kube-proxy em cada node. Esse processo configura ou o iptables ou IPVS para traduzir automaticamente os VIPs em IPs de pods antes de enviar o pacote para a rede do cluster. Conexões individuais são rastreadas para que os pacotes possam ser devidamente decodificados quando retornarem. O IPVS e o iptables podem fazer o balanceamento de carga de um único IP virtual de serviço em vários IPs de pods, embora o IPVS tenha muito mais flexibilidade nos algoritmos de balanceamento de carga que ele pode usar.

      Nota: Este processo de rastreamento de tradução e de conexão acontece inteiramente no kernel do Linux. O kube-proxy lê a API do Kubernetes e atualiza o ip no iptables e IPVS, mas ele não está no caminho dos dados para pacotes individuais. Isso é mais eficiente e de melhor desempenho do que as versões anteriores do kube-proxy, que funcionava como um proxy de mando do usuário.

      Vamos seguir a rota que um pacote leva de um pod, pod1 novamente, para um serviço, service1:

      • pod1 cria um pacote com o IP do service1 como seu destino
      • O pacote trafega pelo par de ethernet virtual para o namespace root da rede
      • O pacote continua até a bridge br0
      • A bridge não encontra nenhuma interface local para onde rotear o pacote, assim o pacote é enviado para a rota padrão via eth0
      • Iptables ou IPVS, configurados pelo kube-proxy, acham o IP de destino do pacote e o traduzem de um IP virtual para um dos IPs do pod de serviço, usando quaisquer algoritmos de balanceamento de carga disponíveis ou especificados
      • Opcional: seu pacote pode ser encapsulado neste ponto, como discutido na seção anterior
      • O pacote entra na rede do cluster e é roteado para o node correto.
      • O pacote entra no node de destino na eth0
      • Opcional: se o seu pacote foi encapsulado, ele será desencapsulado neste momento
      • O pacote continua até a bridge br0
      • O pacote é enviado para o par de ethernet virtual via veth1
      • O pacote passa pelo par de ethernet virtual e entra no namespace de rede do pod através de sua interface de rede eth0

      Quando o pacote retorna para o node1, a tradução de VIP para IP do pod será revertida, e o pacote retornará através da bridge e da interface virtual para o pod correto.

      Conclusão

      Neste artigo, analisamos a infraestrutura de rede interna de um cluster do Kubernetes. Discutimos os blocos construtivos que compõem a rede e detalhamos a jornada salto-por-salto de pacotes em diferentes cenários.

      Para mais informações sobre o Kubernetes, dê uma olhada na tag para nossos tutoriais de Kubernetes e a documentação oficial do Kubernetes.



      Source link