Go Networking en Azure: Arquitectura Cloud, VNets, Seguridad y Patrones de Producción
Construye aplicaciones Go resilientes en Azure. Domina redes virtuales, balanceo de carga, endpoints de servicio, identidades administradas, monitoreo, y despliegues multi-región para infraestructura cloud empresarial.
La Red es la Aplicación
Escribes código Go brillante. Funciona perfectamente en tu laptop. Luego despliegas a Azure y todo se rompe.
No porque tu código sea malo. Porque la topología de red es diferente. Las reglas de seguridad bloquean tráfico. Los balanceadores de carga se comportan inesperadamente. El despliegue multi-región introduce latencia que no anticipaste.
Esto no es un problema de Go. Es un problema de red.
A escala, la red no es infraestructura. La red es parte de tu arquitectura de aplicación.
Esta guía te enseña a pensar sobre redes como un arquitecto cloud, no como un administrador de sistemas.
Parte 1: Fundamentos de Redes Azure
Antes de desplegar nada, entiende los bloques de construcción.
Virtual Network (VNet) — Tu Cloud Privada
Azure Subscription
└── Virtual Network (VNet): 10.0.0.0/16
├── Subnet-App: 10.0.1.0/24 (Go services)
├── Subnet-Data: 10.0.2.0/24 (Bases de datos)
├── Subnet-Cache: 10.0.3.0/24 (Redis, Memcached)
└── Subnet-Gateway: 10.0.4.0/24 (VPN, ExpressRoute)
Un VNet es tu espacio de red aislado. El tráfico dentro permanece dentro. El tráfico afuera está bloqueado por defecto.
Crear un VNet en código:
// infrastructure/azure/vnet.go
package azure
import (
"context"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
)
type VNetBuilder struct {
subscriptionID string
resourceGroup string
client *armnetwork.VirtualNetworksClient
}
func NewVNetBuilder(subscriptionID, resourceGroup string) (*VNetBuilder, error) {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, err
}
client, err := armnetwork.NewVirtualNetworksClient(subscriptionID, cred, nil)
if err != nil {
return nil, err
}
return &VNetBuilder{
subscriptionID: subscriptionID,
resourceGroup: resourceGroup,
client: client,
}, nil
}
// CreateVNet crea una red virtual
func (vb *VNetBuilder) CreateVNet(ctx context.Context, name string, addressSpace string) error {
vnetParams := armnetwork.VirtualNetwork{
Location: toPtr("eastus"),
Properties: &armnetwork.VirtualNetworkPropertiesFormat{
AddressSpace: &armnetwork.AddressSpace{
AddressPrefixes: []*string{toPtr(addressSpace)},
},
},
}
poller, err := vb.client.BeginCreateOrUpdate(
ctx,
vb.resourceGroup,
name,
vnetParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
// CreateSubnet crea una subred dentro de VNet
func (vb *VNetBuilder) CreateSubnet(ctx context.Context, vnetName, subnetName, addressPrefix string) error {
subnetsClient, err := armnetwork.NewSubnetsClient(vb.subscriptionID, nil, nil)
if err != nil {
return err
}
subnetParams := armnetwork.Subnet{
Properties: &armnetwork.SubnetPropertiesFormat{
AddressPrefix: toPtr(addressPrefix),
ServiceEndpoints: []*armnetwork.ServiceEndpointPropertiesFormat{
{
Service: toPtr("Microsoft.Sql"),
},
{
Service: toPtr("Microsoft.Storage"),
},
},
},
}
poller, err := subnetsClient.BeginCreateOrUpdate(
ctx,
vb.resourceGroup,
vnetName,
subnetName,
subnetParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
func toPtr[T any](v T) *T {
return &v
}
Network Security Groups (NSGs) — Tu Firewall
Los NSGs son firewalls con estado que controlan tráfico en el nivel de subred o NIC.
// infrastructure/azure/nsg.go
package azure
type NSGBuilder struct {
client *armnetwork.SecurityGroupsClient
subscriptionID string
resourceGroup string
}
func NewNSGBuilder(subscriptionID, resourceGroup string) (*NSGBuilder, error) {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, err
}
client, err := armnetwork.NewSecurityGroupsClient(subscriptionID, cred, nil)
if err != nil {
return nil, err
}
return &NSGBuilder{
client: client,
subscriptionID: subscriptionID,
resourceGroup: resourceGroup,
}, nil
}
// CreateAppSubnetNSG crea reglas NSG para tier de app
func (nb *NSGBuilder) CreateAppSubnetNSG(ctx context.Context, nsgName string) error {
// Permitir HTTPS inbound desde load balancer
nsgParams := armnetwork.SecurityGroup{
Location: toPtr("eastus"),
Properties: &armnetwork.SecurityGroupPropertiesFormat{
SecurityRules: []*armnetwork.SecurityRule{
// Permitir HTTPS desde load balancer
{
Name: toPtr("Allow-HTTPS-LB"),
Properties: &armnetwork.SecurityRulePropertiesFormat{
Protocol: toPtr(armnetwork.SecurityRuleProtocolTCP),
SourceAddressPrefix: toPtr("AzureLoadBalancer"),
SourcePortRange: toPtr("*"),
DestinationAddressPrefix: toPtr("*"),
DestinationPortRange: toPtr("443"),
Access: toPtr(armnetwork.SecurityRuleAccessAllow),
Priority: toPtr(int32(100)),
Direction: toPtr(armnetwork.SecurityRuleDirectionInbound),
},
},
// Permitir HTTP desde load balancer (health checks)
{
Name: toPtr("Allow-HTTP-LB"),
Properties: &armnetwork.SecurityRulePropertiesFormat{
Protocol: toPtr(armnetwork.SecurityRuleProtocolTCP),
SourceAddressPrefix: toPtr("AzureLoadBalancer"),
SourcePortRange: toPtr("*"),
DestinationAddressPrefix: toPtr("*"),
DestinationPortRange: toPtr("8080"),
Access: toPtr(armnetwork.SecurityRuleAccessAllow),
Priority: toPtr(int32(101)),
Direction: toPtr(armnetwork.SecurityRuleDirectionInbound),
},
},
// Negar todo otro inbound
{
Name: toPtr("Deny-All-Inbound"),
Properties: &armnetwork.SecurityRulePropertiesFormat{
Protocol: toPtr(armnetwork.SecurityRuleProtocolAsterisk),
SourceAddressPrefix: toPtr("*"),
SourcePortRange: toPtr("*"),
DestinationAddressPrefix: toPtr("*"),
DestinationPortRange: toPtr("*"),
Access: toPtr(armnetwork.SecurityRuleAccessDeny),
Priority: toPtr(int32(4096)),
Direction: toPtr(armnetwork.SecurityRuleDirectionInbound),
},
},
// Permitir outbound a storage
{
Name: toPtr("Allow-Storage-Outbound"),
Properties: &armnetwork.SecurityRulePropertiesFormat{
Protocol: toPtr(armnetwork.SecurityRuleProtocolTCP),
SourceAddressPrefix: toPtr("*"),
SourcePortRange: toPtr("*"),
DestinationAddressPrefix: toPtr("Storage"),
DestinationPortRange: toPtr("443"),
Access: toPtr(armnetwork.SecurityRuleAccessAllow),
Priority: toPtr(int32(100)),
Direction: toPtr(armnetwork.SecurityRuleDirectionOutbound),
},
},
},
},
}
poller, err := nb.client.BeginCreateOrUpdate(
ctx,
nb.resourceGroup,
nsgName,
nsgParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
Parte 2: Arquitectura de Red Azure — La Imagen Completa
graph TB
subgraph "Internet"
INTERNET["Usuarios<br/>APIs Externas"]
end
subgraph "Azure Front Door / CDN"
AFD["Front Door<br/>Load Balancer Global<br/>DDoS Protection"]
end
subgraph "Azure Application Gateway"
AGW["App Gateway<br/>Layer 7<br/>SSL Termination<br/>WAF"]
end
subgraph "Azure Virtual Network - East Region"
subgraph "Subnet-App (10.0.1.0/24)"
NSG1["NSG: Allow LB Only"]
AKS1["AKS Cluster<br/>3x nodes<br/>Go Pods"]
end
subgraph "Subnet-Data (10.0.2.0/24)"
NSG2["NSG: Allow App Subnet<br/>Deny Internet"]
SQL["Azure SQL DB<br/>Private Endpoint"]
COSMOS["Cosmos DB<br/>Private Endpoint"]
end
subgraph "Subnet-Cache (10.0.3.0/24)"
NSG3["NSG: Allow App Subnet"]
REDIS["Azure Cache<br/>for Redis"]
end
LB["Internal Load Balancer<br/>Pod-to-Pod"]
end
subgraph "Azure Virtual Network - West Region"
subgraph "Subnet-App-W (10.1.1.0/24)"
AKS2["AKS Cluster<br/>3x nodes<br/>Go Pods"]
end
subgraph "Subnet-Data-W (10.1.2.0/24)"
SQL2["Azure SQL DB<br/>Read Replica"]
end
LB2["Internal Load Balancer"]
end
subgraph "Network Connectivity"
VNET_PEER["VNet Peering<br/>East ↔ West"]
GATEWAY["VPN Gateway<br/>On-prem Connection"]
end
subgraph "Supporting Services"
KEYVAULT["Key Vault<br/>Secrets & Certs"]
APPINSIGHTS["Application Insights<br/>Monitoring"]
LOG["Log Analytics<br/>Logs"]
end
subgraph "Security & Monitoring"
DEFENDER["Defender for Cloud"]
SENTINEL["Azure Sentinel<br/>SIEM"]
end
INTERNET -->|HTTPS| AFD
AFD -->|Route| AGW
AGW -->|Route| AKS1
AGW -->|Route| AKS2
AKS1 -->|gRPC| LB
AKS1 -->|Query| SQL
AKS1 -->|Cache| REDIS
AKS1 -->|Secrets| KEYVAULT
AKS2 -->|Peer| AKS1
AKS1 -.->|VNet Peering| VNET_PEER
AKS2 -.->|VNet Peering| VNET_PEER
SQL -->|Replication| SQL2
AKS1 -->|Telemetry| APPINSIGHTS
AKS2 -->|Telemetry| APPINSIGHTS
APPINSIGHTS -->|Store| LOG
LOG -->|Analyze| DEFENDER
DEFENDER -->|Alert| SENTINEL
style AFD fill:#2196f3,stroke:#333,stroke-width:2px
style AGW fill:#2196f3,stroke:#333,stroke-width:2px
style AKS1 fill:#ff9800,stroke:#333,stroke-width:2px
style AKS2 fill:#ff9800,stroke:#333,stroke-width:2px
style KEYVAULT fill:#4caf50,stroke:#333,stroke-width:2px
Parte 3: Estrategias de Balanceo de Carga
Azure ofrece múltiples opciones de balanceo de carga. Elige basado en tu arquitectura.
Azure Front Door (Global Load Balancing)
Para aplicaciones sirviendo usuarios worldwide:
// infrastructure/azure/frontdoor.go
package azure
import (
"context"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/frontdoor/armfrontdoor"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
)
type FrontDoorBuilder struct {
client *armfrontdoor.FrontDoorsClient
subscriptionID string
resourceGroup string
}
func NewFrontDoorBuilder(subscriptionID, resourceGroup string) (*FrontDoorBuilder, error) {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, err
}
client, err := armfrontdoor.NewFrontDoorsClient(subscriptionID, cred, nil)
if err != nil {
return nil, err
}
return &FrontDoorBuilder{
client: client,
subscriptionID: subscriptionID,
resourceGroup: resourceGroup,
}, nil
}
// CreateMultiRegionFrontDoor crea load balancer global
func (fb *FrontDoorBuilder) CreateMultiRegionFrontDoor(ctx context.Context) error {
frontdoorParams := armfrontdoor.FrontDoor{
Location: toPtr("global"),
Properties: &armfrontdoor.FrontDoorProperties{
FrontendEndpoints: []*armfrontdoor.FrontendEndpoint{
{
Name: toPtr("api-endpoint"),
Properties: &armfrontdoor.FrontendEndpointProperties{
HostName: toPtr("api.example.com"),
SessionAffinityState: toPtr(armfrontdoor.SessionAffinityStateDisabled),
},
},
},
BackendPools: []*armfrontdoor.BackendPool{
{
Name: toPtr("east-region"),
Properties: &armfrontdoor.BackendPoolProperties{
Backends: []*armfrontdoor.Backend{
{
Address: toPtr("east-lb.eastus.cloudapp.azure.com"),
HTTPPort: toPtr(int32(80)),
HTTPSPort: toPtr(int32(443)),
Priority: toPtr(int32(1)),
Weight: toPtr(int32(50)),
BackendHostHeader: toPtr("east-lb.eastus.cloudapp.azure.com"),
EnabledState: toPtr(armfrontdoor.BackendEnabledStateEnabled),
},
},
HealthProbeSettings: &armfrontdoor.SubResource{
ID: toPtr("/subscriptions/.../healthProbeSettings/default"),
},
LoadBalancingSettings: &armfrontdoor.SubResource{
ID: toPtr("/subscriptions/.../loadBalancingSettings/default"),
},
},
},
{
Name: toPtr("west-region"),
Properties: &armfrontdoor.BackendPoolProperties{
Backends: []*armfrontdoor.Backend{
{
Address: toPtr("west-lb.westus.cloudapp.azure.com"),
HTTPPort: toPtr(int32(80)),
HTTPSPort: toPtr(int32(443)),
Priority: toPtr(int32(1)),
Weight: toPtr(int32(50)),
BackendHostHeader: toPtr("west-lb.westus.cloudapp.azure.com"),
EnabledState: toPtr(armfrontdoor.BackendEnabledStateEnabled),
},
},
},
},
},
RoutingRules: []*armfrontdoor.RoutingRule{
{
Name: toPtr("api-routing"),
Properties: &armfrontdoor.RoutingRuleProperties{
FrontendEndpoints: []*armfrontdoor.SubResource{
{
ID: toPtr("/subscriptions/.../frontendEndpoints/api-endpoint"),
},
},
AcceptedProtocols: []*armfrontdoor.FrontDoorProtocol{
toPtr(armfrontdoor.FrontDoorProtocolHttps),
},
PatternsToMatch: []*string{
toPtr("/*"),
},
RouteConfiguration: &armfrontdoor.ForwardingConfiguration{
BackendPool: &armfrontdoor.SubResource{
ID: toPtr("/subscriptions/.../backendPools/east-region"),
},
ForwardingProtocol: toPtr(armfrontdoor.FrontDoorForwardingProtocolHttpsOnly),
CustomForwardingPath: toPtr("/"),
CacheConfiguration: &armfrontdoor.CacheConfiguration{
QueryParameterStripDirective: toPtr(armfrontdoor.FrontDoorQueryStripDirectiveStripAll),
},
},
EnabledState: toPtr(armfrontdoor.RoutingRuleEnabledStateEnabled),
},
},
},
EnabledState: toPtr(armfrontdoor.FrontDoorEnabledStateEnabled),
},
}
poller, err := fb.client.BeginCreateOrUpdate(
ctx,
fb.resourceGroup,
"api-frontdoor",
frontdoorParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
Application Gateway (Layer 7 Load Balancing)
Para enrutamiento basado en URLs, hostnames, SSL termination:
// infrastructure/azure/appgateway.go
package azure
type AppGatewayBuilder struct {
client *armnetwork.ApplicationGatewaysClient
subscriptionID string
resourceGroup string
}
// Crear app gateway con path-based routing
func (agb *AppGatewayBuilder) CreateAppGateway(ctx context.Context) error {
appgatewayParams := armnetwork.ApplicationGateway{
Location: toPtr("eastus"),
Properties: &armnetwork.ApplicationGatewayPropertiesFormat{
Sku: &armnetwork.ApplicationGatewaySKU{
Name: toPtr(armnetwork.ApplicationGatewaySKUNameStandardV2),
Tier: toPtr(armnetwork.ApplicationGatewayTierStandardV2),
Capacity: toPtr(int32(2)),
},
// Frontend
FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
{
Name: toPtr("appgw-frontend"),
Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
PublicIPAddress: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../publicIPAddresses/appgw-pip"),
},
},
},
},
// HTTP listeners
HTTPListeners: []*armnetwork.HTTPListener{
{
Name: toPtr("https-listener"),
Properties: &armnetwork.HTTPListenerPropertiesFormat{
FrontendIPConfiguration: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../frontendIPConfigurations/appgw-frontend"),
},
FrontendPort: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../frontendPorts/https-port"),
},
Protocol: toPtr(armnetwork.ApplicationGatewayProtocolHttps),
SSLCertificate: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../sslCertificates/appgw-cert"),
},
HostName: toPtr("api.example.com"),
RequireServerNameIndication: toPtr(true),
},
},
},
// Backend pools (diferentes servicios)
BackendAddressPools: []*armnetwork.BackendAddressPool{
{
Name: toPtr("invoice-service-pool"),
Properties: &armnetwork.BackendAddressPoolPropertiesFormat{
BackendAddresses: []*armnetwork.BackendAddress{
{
IPAddress: toPtr("10.0.1.10"),
},
{
IPAddress: toPtr("10.0.1.11"),
},
},
},
},
{
Name: toPtr("analytics-service-pool"),
Properties: &armnetwork.BackendAddressPoolPropertiesFormat{
BackendAddresses: []*armnetwork.BackendAddress{
{
IPAddress: toPtr("10.0.1.20"),
},
},
},
},
},
// URL path-based routing rules
URLPathMaps: []*armnetwork.URLPathMap{
{
Name: toPtr("api-routing-rules"),
Properties: &armnetwork.URLPathMapPropertiesFormat{
DefaultBackendAddressPool: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../backendAddressPools/default-pool"),
},
DefaultBackendHTTPSettings: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../backendHttpSettingsCollection/default-settings"),
},
PathRules: []*armnetwork.PathRule{
{
Name: toPtr("invoice-rule"),
Properties: &armnetwork.PathRulePropertiesFormat{
Paths: []*string{
toPtr("/api/invoices/*"),
toPtr("/api/v1/invoices/*"),
},
BackendAddressPool: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../backendAddressPools/invoice-service-pool"),
},
BackendHTTPSettings: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../backendHttpSettingsCollection/http-settings"),
},
},
},
{
Name: toPtr("analytics-rule"),
Properties: &armnetwork.PathRulePropertiesFormat{
Paths: []*string{
toPtr("/api/reports/*"),
toPtr("/api/analytics/*"),
},
BackendAddressPool: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../backendAddressPools/analytics-service-pool"),
},
BackendHTTPSettings: &armnetwork.SubResource{
ID: toPtr("/subscriptions/.../backendHttpSettingsCollection/http-settings"),
},
},
},
},
},
},
},
EnableHTTP2: toPtr(true),
},
}
poller, err := agb.client.BeginCreateOrUpdate(
ctx,
agb.resourceGroup,
"api-appgateway",
appgatewayParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
Parte 4: Private Endpoints y Service Endpoints
Mantén el tráfico de datos dentro de tu VNet.
Private Endpoints (Recomendado para Datos)
// infrastructure/azure/private_endpoint.go
package azure
type PrivateEndpointBuilder struct {
client *armnetwork.PrivateEndpointsClient
subscriptionID string
resourceGroup string
}
// Crear private endpoint para SQL Database
func (peb *PrivateEndpointBuilder) CreateSQLPrivateEndpoint(ctx context.Context) error {
peParams := armnetwork.PrivateEndpoint{
Location: toPtr("eastus"),
Properties: &armnetwork.PrivateEndpointProperties{
PrivateLinkServiceConnections: []*armnetwork.PrivateLinkServiceConnection{
{
Name: toPtr("sql-connection"),
Properties: &armnetwork.PrivateLinkServiceConnectionProperties{
PrivateLinkServiceID: toPtr(
"/subscriptions/.../providers/Microsoft.Sql/servers/fiscal-db/databases/invoices",
),
GroupIDs: []*string{
toPtr("sqlServer"),
},
},
},
},
Subnet: &armnetwork.Subnet{
ID: toPtr("/subscriptions/.../subnets/Subnet-Data"),
},
},
}
poller, err := peb.client.BeginCreateOrUpdate(
ctx,
peb.resourceGroup,
"sql-pe",
peParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
// Crear private endpoint para Storage Account
func (peb *PrivateEndpointBuilder) CreateStoragePrivateEndpoint(ctx context.Context) error {
peParams := armnetwork.PrivateEndpoint{
Location: toPtr("eastus"),
Properties: &armnetwork.PrivateEndpointProperties{
PrivateLinkServiceConnections: []*armnetwork.PrivateLinkServiceConnection{
{
Name: toPtr("storage-connection"),
Properties: &armnetwork.PrivateLinkServiceConnectionProperties{
PrivateLinkServiceID: toPtr(
"/subscriptions/.../providers/Microsoft.Storage/storageAccounts/fiscaldata",
),
GroupIDs: []*string{
toPtr("blob"),
},
},
},
},
Subnet: &armnetwork.Subnet{
ID: toPtr("/subscriptions/.../subnets/Subnet-Data"),
},
},
}
poller, err := peb.client.BeginCreateOrUpdate(
ctx,
peb.resourceGroup,
"storage-pe",
peParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
Service Endpoints (Alternativa Más Simple)
Service Endpoint Architecture:
┌─────────────────────────────────────┐
│ App Subnet (10.0.1.0/24) │
│ ├── Pod 1 → ServiceEndpoint │
│ ├── Pod 2 → ServiceEndpoint │
│ └── Pod 3 → ServiceEndpoint │
└──────────────┬──────────────────────┘
│ (Internal, no Internet)
↓
Microsoft.Sql
Microsoft.Storage
Microsoft.KeyVault
Service Endpoint vs Private Endpoint:
| Feature | Service Endpoint | Private Endpoint |
|---|---|---|
| Costo | Gratis | $0.01/hora |
| Tipo de Red | Servicio-a-servicio | DNS privada |
| Seguridad | Con estado | Aislamiento completo |
| Ideal Para | No sensible | PII, credenciales |
Parte 5: Azure Kubernetes Service (AKS) Networking
AKS es tu runtime de aplicación Go en Azure. Entender AKS networking es esencial.
graph TB
subgraph "Azure Subscription"
subgraph "AKS Cluster"
VMSS["Virtual Machine Scale Set<br/>3 nodes"]
subgraph "Node 1"
POD1["Pod 1<br/>Go Service<br/>10.244.1.5"]
POD2["Pod 2<br/>Go Service<br/>10.244.1.6"]
end
subgraph "Node 2"
POD3["Pod 3<br/>Go Service<br/>10.244.2.5"]
end
subgraph "Node 3"
POD4["Pod 4<br/>Go Service<br/>10.244.3.5"]
end
CNI["Azure CNI<br/>Assigns IPs from VNet"]
end
subgraph "VNet: 10.0.0.0/16"
SUBNET["Subnet: 10.0.1.0/24<br/>Pod IPs: 10.244.0.0/16"]
end
end
VMSS -->|Deploy| POD1
VMSS -->|Deploy| POD2
VMSS -->|Deploy| POD3
VMSS -->|Deploy| POD4
CNI -->|Assign IPs| POD1
CNI -->|Assign IPs| POD3
SUBNET -->|Contain| VMSS
style VMSS fill:#ff9800
style CNI fill:#2196f3
style SUBNET fill:#4caf50
Creación de Clúster AKS en Go
// infrastructure/azure/aks.go
package azure
import (
"context"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
)
type AKSBuilder struct {
client *armcontainerservice.ManagedClustersClient
subscriptionID string
resourceGroup string
}
func NewAKSBuilder(subscriptionID, resourceGroup string) (*AKSBuilder, error) {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, err
}
client, err := armcontainerservice.NewManagedClustersClient(subscriptionID, cred, nil)
if err != nil {
return nil, err
}
return &AKSBuilder{
client: client,
subscriptionID: subscriptionID,
resourceGroup: resourceGroup,
}, nil
}
// CreateAKSCluster crea un clúster Kubernetes administrado
func (ab *AKSBuilder) CreateAKSCluster(ctx context.Context) error {
aksParams := armcontainerservice.ManagedCluster{
Location: toPtr("eastus"),
Identity: &armcontainerservice.ManagedClusterIdentity{
Type: toPtr(armcontainerservice.ResourceIdentityTypeSystemAssigned),
},
Properties: &armcontainerservice.ManagedClusterProperties{
DNSPrefix: toPtr("fiscal-aks"),
KubernetesVersion: toPtr("1.27.0"),
// Network profile
NetworkProfile: &armcontainerservice.NetworkProfile{
NetworkPlugin: toPtr(armcontainerservice.NetworkPluginAzure),
NetworkPolicy: toPtr(armcontainerservice.NetworkPolicyAzure),
ServiceCIDR: toPtr("10.240.0.0/16"),
DNSServiceIP: toPtr("10.240.0.10"),
DockerBridgeCIDR: toPtr("172.17.0.1/16"),
// Pod CIDR (if using Azure CNI)
PodCIDR: toPtr("10.244.0.0/16"),
},
// Default node pool
AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{
{
Name: toPtr("systempool"),
Properties: &armcontainerservice.ManagedClusterAgentPoolProfileProperties{
Count: toPtr(int32(3)),
VMSize: toPtr("Standard_D2s_v3"),
OSType: toPtr(armcontainerservice.OSTypeLinux),
Mode: toPtr(armcontainerservice.AgentPoolModeSystem),
VNetSubnetID: toPtr(
"/subscriptions/.../virtualNetworks/fiscal-vnet/subnets/aks-subnet",
),
MaxPods: toPtr(int32(110)),
AvailabilityZones: []*string{
toPtr("1"),
toPtr("2"),
toPtr("3"),
},
},
},
},
// Managed identity for credential handling
ServicePrincipalProfile: &armcontainerservice.ManagedClusterServicePrincipalProfile{
ClientID: toPtr("msi"),
},
// RBAC enabled
EnableRBAC: toPtr(true),
// AAD integration
AADProfile: &armcontainerservice.ManagedClusterAADProfile{
Managed: toPtr(true),
AdminGroupObjectIDs: []*string{
toPtr("admin-group-object-id"),
},
},
},
}
poller, err := ab.client.BeginCreateOrUpdate(
ctx,
ab.resourceGroup,
"fiscal-aks",
aksParams,
nil,
)
if err != nil {
return err
}
_, err = poller.PollUntilDone(ctx, nil)
return err
}
Parte 6: Identidad Administrada y Seguridad Zero-Trust
No uses connection strings. Usa identidades administradas.
// infrastructure/identity.go
package infrastructure
import (
"context"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
)
// GetTokenUsingManagedIdentity recupera token sin secretos
func GetTokenUsingManagedIdentity(ctx context.Context) (string, error) {
// Usa automáticamente Azure Pod Identity o MSI
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return "", err
}
// Token para Azure Storage
token, err := cred.GetToken(ctx, azcore.TokenRequestOptions{
Scopes: []string{"https://storage.azure.com/.default"},
})
if err != nil {
return "", err
}
return token.Token, nil
}
// Go Service Usando Managed Identity
type StorageClient struct {
cred *azidentity.DefaultAzureCredential
}
func NewStorageClient(ctx context.Context) (*StorageClient, error) {
cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, err
}
return &StorageClient{cred: cred}, nil
}
// Subir archivo a Azure Blob Storage (sin connection string necesario)
func (sc *StorageClient) UploadBlob(ctx context.Context, containerName, blobName string, data []byte) error {
token, err := sc.cred.GetToken(ctx, azcore.TokenRequestOptions{
Scopes: []string{"https://storage.azure.com/.default"},
})
if err != nil {
return err
}
// Usar token en request
req, err := http.NewRequestWithContext(ctx, "PUT",
fmt.Sprintf("https://storage.blob.core.windows.net/%s/%s", containerName, blobName),
bytes.NewReader(data),
)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token.Token)
req.Header.Set("x-ms-blob-type", "BlockBlob")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
Parte 7: Arquitectura Multi-Región
Para alta disponibilidad y disaster recovery.
graph TB
subgraph "Global"
AFD["Azure Front Door<br/>Global Router"]
end
subgraph "East Region"
RG_E["Resource Group<br/>East"]
VNet_E["VNet: 10.0.0.0/16"]
AKS_E["AKS Cluster<br/>3 nodes"]
SQL_E["SQL Primary<br/>Fiscal Data"]
REDIS_E["Redis Cache"]
TRAFFIC_E["Traffic Manager<br/>Health Check"]
end
subgraph "West Region"
RG_W["Resource Group<br/>West"]
VNet_W["VNet: 10.1.0.0/16"]
AKS_W["AKS Cluster<br/>3 nodes"]
SQL_W["SQL Read Replica<br/>Read-Only"]
REDIS_W["Redis Cache"]
TRAFFIC_W["Traffic Manager<br/>Health Check"]
end
subgraph "Data Replication"
GEO_REP["Geo-Replication<br/>Async<br/>RPO: 5min"]
end
AFD -->|Weight 50%| TRAFFIC_E
AFD -->|Weight 50%| TRAFFIC_W
TRAFFIC_E -->|Health Check| AKS_E
TRAFFIC_W -->|Health Check| AKS_W
AKS_E -->|Query| SQL_E
AKS_E -->|Cache| REDIS_E
AKS_W -->|Query| SQL_W
AKS_W -->|Cache| REDIS_W
SQL_E -->|Replicate| GEO_REP
GEO_REP -->|Replicate| SQL_W
style AFD fill:#2196f3,stroke:#333,stroke-width:2px
style GEO_REP fill:#ff9800,stroke:#333,stroke-width:2px
Parte 8: Monitoreo y Diagnósticos
Ve qué está pasando en tu red.
// infrastructure/monitoring.go
package infrastructure
import (
"context"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
)
type MonitoringBuilder struct {
client *armmonitor.MetricsClient
subscriptionID string
resourceGroup string
}
// Consultar métricas de latencia de red
func (mb *MonitoringBuilder) GetNetworkLatency(ctx context.Context) error {
// Consultar Application Insights para latencia
query := `
requests
| where cloud_RoleInstance startswith "invoice"
| summarize
P50 = percentile(duration, 50),
P95 = percentile(duration, 95),
P99 = percentile(duration, 99),
AvgCount = count()
by cloud_RoleName
`
// Ejecutar query vía Analytics Client
// Results muestran latencia por servicio
return nil
}
// Monitorear uso de ancho de banda VNet
func (mb *MonitoringBuilder) GetVNetBandwidth(ctx context.Context) error {
metric := "BytesInPerSecond"
metricsClient := armmonitor.NewMetricsClient(mb.subscriptionID, nil)
result, err := metricsClient.List(
ctx,
"/subscriptions/.../resourceGroups/.../providers/Microsoft.Network/virtualNetworks/fiscal-vnet",
&armmonitor.MetricsClientListOptions{
Metric: &metric,
Timespan: toPtr("PT1H"),
},
)
if err != nil {
return err
}
// Procesar métricas de ancho de banda
for _, value := range result.Value {
for _, series := range value.Timeseries {
for _, point := range series.Data {
// Rastrear utilización de ancho de banda
}
}
}
return nil
}
Conclusión: La Arquitectura de Red Determina la Arquitectura de Aplicación
Tu código Go es tan bueno como la red en la que se ejecuta.
Azure te da herramientas poderosas: VNets para aislamiento, Load Balancers para distribución, Private Endpoints para seguridad, AKS para orquestación. Pero entender cómo estos trabajos juntos es lo que separa despliegues de prototipos de infraestructura de producción.
La misma aplicación Go puede ejecutarse a 1,000 RPS o 100,000 RPS. La diferencia no es el código. La diferencia es la arquitectura de red.
La red no es algo que sucede después de que escribas código. La red es parte de tu diseño de aplicación. Cuando diseñas tu red primero, tu aplicación escala con ella.