0lblsuwlfakqc22vw.jpeg

¿Cómo instalas un paquete de Python en tu entorno si no tienes acceso a Internet? Recientemente me encontré con este problema al crear un entorno de AWS Sagemaker Studio para mi equipo en AWS.

Creación de un entorno privado de AWS para Sagemaker

Para este proyecto en particular, configuré Sagemaker en modo VPC Only con la restricción de mantener la arquitectura privada, lo que significa crear una VPC y subredes privadas, pero sin acceso a Internet.

Por lo tanto, todas las comunicaciones de red, incluida la comunicación de aplicaciones con las API de AWS, deben pasar por interfaces de punto final de VPC. Esto permite mantener la conexión segura, ya que los datos enviados y recibidos nunca pasarán por Internet utilizando la red troncal de AWS.

Es particularmente adecuado para limitar la exposición a riesgos de seguridad, más particularmente cuando se procesa información personal o se debe cumplir con algunos estándares de seguridad.

Foto por Nadir sYzYgY en desempaquetar

Accediendo al repositorio de paquetes Pypi desde AWS Sagemaker

En mi equipo, los científicos de datos usan Python como lenguaje principal y, a veces, necesitan paquetes de Python que no se proporcionan en Imágenes de Python prediseñadas de Sagemaker, así que me centraré en este caso de uso. Afortunadamente, la solución también funciona para otros lenguajes y repositorios como npm.

Normalmente, sus usuarios intentarán instalar cualquier paquete que necesiten mediante el comando pip. Pero, como no se permite el acceso a Internet, este comando fallará porque pip no podrá comunicarse con los servidores de Pypi.org.

Abriendo internet

Una opción es abrir el acceso a Internet y permitir conexiones HTTP salientes a las IP de Fastly CDN utilizadas por los servidores de Pypi.org. Pero esto no es viable en nuestro caso ya que no queremos ninguna conexión a Internet en la arquitectura.

Usando un servidor Pypi dedicado

El blog de AWS también proporciona un ejemplo del uso de un paquete de Python llamado Bandersnatch.. Este artículo describe cómo configurar un servidor, que actúa como un host bastión, que reflejará Pypi y será accesible solo desde sus subredes privadas.

Esta no es una opción viable, ya que debe saber de antemano qué paquetes de Python debe proporcionar y, de alguna manera, tendrá que crear subredes públicas y darle acceso espejo del servidor Pypi a Internet.

Usando AWS Cordeartifact

En última instancia, esta es la solución que se me ocurrió y que funciona en mi caso.

Artefacto de código AWS es la solución de gestión de artefactos proporcionada por AWS. Es compatible con otros servicios de AWS como AWS Service Catalog para controlar el acceso a los recursos dentro de una organización.

Para usarlo, deberá crear un «dominio» que sirva como paraguas para administrar el acceso y aplicar políticas en toda su organización. Luego, tendrás que crear un repositorio que sirva tus artefactos para tus diferentes aplicaciones.

Además, un repositorio puede tener repositorios ascendentes. Por lo tanto, si un paquete de Python no está disponible en el repositorio de destino, la demanda se transmitirá al repositorio ascendente para su cumplimiento.

Más precisamente, este flujo de trabajo tiene en cuenta las versiones de los paquetes. La documentación oficial proporciona un flujo de trabajo detallado:

Si my_repo contiene la versión del paquete solicitada, se devuelve al cliente.

Si my_repo no contiene la versión del paquete solicitada, CodeArtifact la busca en my_repoLos repositorios ascendentes. Si se encuentra la versión del paquete, se copia una referencia a él en my_repoy la versión del paquete se devuelve al cliente.

Si ninguno my_repo ni sus repositorios ascendentes contienen la versión del paquete, un HTTP 404 Not Found La respuesta se devuelve al cliente.

¿Guay, verdad? Incluso almacenará en caché la versión del paquete para futuras solicitudes.

Esta es precisamente la estrategia que vamos a utilizar, ya que AWS Codeartifact nos permite definir un repositorio que tenga una conexión externa como Pypi como repositorio ascendente.

Creación de recursos de AWS Codeartifact con Terraform

Como AWS Codeartifact es un servicio de AWS, puede crear fácilmente un punto final de VPC en la VPC de su entorno para conectarse a él.

Nota: estoy usando Terraform v1.6.4 y el proveedor de AWS v5.38.0

locals {
region = "us-east-1"
}

resource "aws_security_group" "vpce_sg" {
name = "AllowTLS"
description = "Allow TLS inbound traffic and all outbound traffic"
vpc_id = aws_vpc.your_vpc.id

tags = {
Name = "allow_tls_for_vpce"
}
}

resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv4" {
security_group_id = aws_security_group.allow_tls.id
cidr_ipv4 = aws_vpc.your_vpc.cidr_block
from_port = 443
ip_protocol = "tcp"
to_port = 443
}

data "aws_iam_policy_document" "codeartifact_vpce_base_policy" {
statement {
sid = "EnableRoles"
effect = "Allow"
actions = [
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ReadFromRepository",
"sts:GetServiceBearerToken"
]
resources = [
"*",
]
principals {
type = "AWS"
identifiers = [
aws_iam_role.your_sagemaker_execution_role.arn
]
}
}
}

resource "aws_vpc_endpoint" "codeartifact_api_vpce" {
vpc_id = aws_vpc.your_vpc.id
service_name = "com.amazonaws.${local.region}.codeartifact.api"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnets.your_private_subnets.ids

security_group_ids = [
aws_security_group.vpce_sg.id,
]

private_dns_enabled = true
policy = data.aws_iam_policy_document.codeartifact_vpce_base_policy.json
tags = { Name = "codeartifact-api-vpc-endpoint" }
}

Luego, tendrá que crear los diferentes recursos necesarios para que Codeartifact maneje sus solicitudes de nuevos paquetes de Python reflejando Pypi: un dominio, un repositorio de Pypi con una conexión externa y un repositorio que defina Pypi como un repositorio ascendente.

resource "aws_codeartifact_domain" "my_domain" {
domain = "my-domain"

encryption_key = ""

tags = { Name = "my-codeartifact-domain" }
}

resource "aws_codeartifact_repository" "public_pypi" {
repository = "pypi-store"
domain = aws_codeartifact_domain.my_domain.domain

external_connections {
external_connection_name = "public:pypi"
}

tags = { Name = "pypi-store-repository" }
}

resource "aws_codeartifact_repository" "my_repository" {
repository = "my_repository"
domain = aws_codeartifact_domain.my_domain.domain

upstream {
repository_name = aws_codeartifact_repository.public_pypi.repository
}

tags = { Name = "my-codeartifact-repository" }
}

data "aws_iam_policy_document" "my_repository_policy_document" {
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = [aws_iam_role.your_sagemaker_execution_role.arn]
}

actions = ["codeartifact:ReadFromRepository"]
resources = [aws_codeartifact_repository.my_repository.arn]
}
}

resource "aws_codeartifact_repository_permissions_policy" "my_repository_policy" {
repository = aws_codeartifact_repository.my_repository.repository
domain = aws_codeartifact_domain.my_domain.domain
policy_document = data.aws_iam_policy_document.my_repository_policy_document.json
}

¡Aquí lo tienes! Ahora puede configurar fácilmente un espejo Pypi para su entorno privado.

Para que las cosas sean utilizables, también deberá indicarle a los comandos pip que dirijan las solicitudes a un índice específico. Afortunadamente, AWS creó una API para hacer el trabajo pesado por usted. Simplemente agregue esto a su código para que funcione:

aws codeartifact login --tool pip --repository $CODE_ARTIFACT_REPOSITOR_ARN --domain $CODE_ARTIFACT_DOMAIN_ID --domain-owner $ACCOUNT_ID --region $REGION

Por último, pero no menos importante, agregue un punto final de VPC para AWS Codeartifact en su VPC.

data "aws_iam_policy_document" "codeartifact_vpce_base_policy" {
statement {
sid = "EnableRoles"
effect = "Allow"
actions = [
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ReadFromRepository",
"sts:GetServiceBearerToken"
]
resources = [
"*",
]
principals {
type = "AWS"
identifiers = [
aws_iam_role.your_sagemaker_execution_role.arn
]
}
}
}

resource "aws_vpc_endpoint" "codeartifact_api_vpce" {
vpc_id = aws_vpc.your_vpc.id
service_name = "com.amazonaws.${local.region}.codeartifact.api"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnets.your_private_subnets.ids

security_group_ids = [
aws_security_group.vpce_sg.id,
]

private_dns_enabled = true
policy = data.aws_iam_policy_document.codeartifact_vpce_base_policy.json
tags = { Name = "codeartifact-api-vpc-endpoint" }
}

Si desea recibir notificaciones de mis próximas publicaciones sobre AWS y más, por favor suscríbete aquí.

¿Sabías que puedes aplaudir varias veces?