انطلق في AWS للبيانات في الوقت الفعلي: خطوتك الأولى الملموسة اليوم
AWS بيانات الوقت الفعلي: الأساسيات في مقال واحد — كود حقيقي، مخططات وخطوات ملموسة، مقتطفات من دورة مكونة من 14 درسًا.
أفضل طريقة لتعلم AWS Données Temps Réel هي بالممارسة. يساعدك هذا المقال على البداية مع مقتطفات عملية مستمدة من دورة تتكون من 14 درسًا — ما يكفي للحصول على نتيجة أولى اليوم.
tl;dr
- مقدمة إلى تدفق بيانات AWS
- Amazon Kinesis
- Amazon OpenSearch
- الأمان والتشفير
- Kafka مقابل Kinesis والبدائل
~$ cat ./parcours.md # AWS بيانات الوقت الفعلي — 6 فصول
01
مقدمة في بث بيانات AWS
→ فصل 00 – درس 1 : البيانات في الوقت الفعلي مقابل المعالجة بالدفعات (Batch)→ فصل 00 – درس 2 : منظومة AWS لبث البيانات
02
Amazon Kinesis
→ فصل 01 – درس 1 : Amazon Kinesis Data Streams — الشظايا والمستهلكون→ فصل 01 – درس 2 : Kinesis Firehose — التسليم المُدار والتحويل+ 1 دروس أخرى
03
Amazon OpenSearch
→ فصل 02 – درس 1 : Amazon OpenSearch — الفهرسة والبحث في الوقت الفعلي→ فصل 02 – درس 2 : Amazon Cognito — المصادقة للوحات OpenSearch+ 1 دروس أخرى
04
الأمان والتشفير
→ فصل 03 – درس 1 : AWS KMS — تشفير البيانات في Kinesis و Firehose→ فصل 03 – درس 2 : IAM و VPC وأفضل ممارسات الأمان للبث
05
Kafka مقابل Kinesis والبدائل
→ فصل 04 – درس 1 : Apache Kafka مقابل Amazon Kinesis — مقارنة كاملة→ فصل 04 – درس 2 : ترحيل Kafka إلى Kinesis / MSK وأنماط هجينة
06
البنى المرجعية
→ فصل 05 – درس 1 : بنية مرجعية — أجهزة IoT الطبية القابلة للارتداء→ فصل 05 – درس 2 : بنية مرجعية — كشف الاحتيال في التجارة الإلكترونية في الوقت الفعلي
🏁
المشروع النهائي
→ تغادر بمشروع ملموس وقابل للعرض
الفصل 01 – الدرس 1 : Amazon Kinesis Data Streams — الشظايا والمستهلكون
NOTEالهدف — فهم البنية الداخلية لـ Kinesis Data Streams : كيف تتيح الشظايا التوازي، وكيف ينشر المنتجون البيانات، وكيف يعالجها المستهلكون — مع أو بدون Enhanced Fan-Out.
1. بنية الشظايا — الوحدة الأساسية
NOTEShard — الشظية (Shard) هي وحدة سعة معزولة داخل تدفق Kinesis. يمكن لكل شظية استقبال حتى 1 ميجابايت/ثانية أو 1000 سجل/ثانية كحد أقصى للإدخال، وتوفير حتى 2 ميجابايت/ثانية للإخراج (لكل مستهلك).
# Capacité par shard
# ─────────────────────────────────────
# Écriture (PUT) : 1 MB/s OU 1 000 records/s (la limite la plus basse s'applique)
# Lecture (GET) : 2 MB/s par GetRecords() - partagée entre TOUS les consommateurs
#
# → Pour 5 Mo/s d'ingestion : vous avez besoin de 5 shards
# → Pour 10 000 records/s : vous avez besoin de 10 shards
# Calculer le nombre de shards nécessaires
max_ingestion_rate = 5 # MB/s
max_record_rate = 3000 # records/s
shards_pour_volume = max_ingestion_rate / 1 # = 5 shards
shards_pour_taux = max_record_rate / 1000 # = 3 shards
nb_shards = max(shards_pour_volume, shards_pour_taux)
print(f"Shards nécessaires : {int(nb_shards)}") # → 5 shardsمفتاح التقسيم — كيف تُوزَّع البيانات
NOTEPartition Key — يحتوي كل سجل على مفتاح تقسيم. يطبّق Kinesis دالة تجزئة MD5 على هذا المفتاح لتحديد الشظية التي سيُوجَّه إليها السجل. السجلات التي تحمل نفس مفتاح التقسيم تذهب دائمًا إلى الشظية نفسها (ترتيب مضمون).
# Exemples de Partition Keys et leur impact
# ✅ BON : clé à haute cardinalité → distribution uniforme entre shards
# device_id = "WATCH-001", "WATCH-002", ..., "WATCH-10000"
kinesis.put_record(
StreamName='donnees-sante',
Data=json.dumps(payload),
PartitionKey=payload['device_id'] # Des milliers de valeurs uniques
)
# ⚠️ PROBLÈME : clé à faible cardinalité → "hot shard" (un shard surchargé)
# Si tous les enregistrements utilisent la même clé :
kinesis.put_record(
StreamName='donnees-sante',
Data=json.dumps(payload),
PartitionKey='donnees-sante' # ← Toujours le même shard ! Goulot d'étranglement
)
# ✅ SOLUTION si pas de clé naturelle : clé aléatoire
import uuid
kinesis.put_record(
StreamName='donnees-sante',
Data=json.dumps(payload),
PartitionKey=str(uuid.uuid4()) # Aléatoire = distribution parfaite
# Attention : perte de l'ordre dans le shard !
)2. المنتجون — إرسال البيانات
put_record مقابل put_records
import boto3, json, time
kinesis = boto3.client('kinesis', region_name='ca-central-1')
# Méthode 1 : put_record — un seul enregistrement
# Latence : ~70ms par appel
donnee = {
"device_id": "WATCH-001",
"heart_rate": 72,
"timestamp": int(time.time())
}
response = kinesis.put_record(
StreamName='donnees-sante',
Data=json.dumps(donnee).encode('utf-8'),
PartitionKey=donnee['device_id']
)
print(f"ShardId: {response['ShardId']}")
# ─────────────────────────────────────
# Méthode 2 : put_records — plusieurs enregistrements en batch (RECOMMANDÉ)
# Latence : ~70ms pour jusqu'à 500 enregistrements !
# Limite : max 500 enregistrements ou 5MB par appel
donnees = [
{"device_id": f"WATCH-{i:03d}", "heart_rate": 60 + i, "timestamp": int(time.time())}
for i in range(10)
]
records = [
{
'Data': json.dumps(d).encode('utf-8'),
'PartitionKey': d['device_id']
}
for d in donnees
]
response = kinesis.put_records(
StreamName='donnees-sante',
Records=records
)
print(f"Enregistrements OK: {response['Records'].__len__()} envoyés")
print(f"Échecs: {response['FailedRecordCount']}")
# Gérer les échecs partiels (put_records peut avoir des succès partiels)
failed_records = [
records[i] for i, r in enumerate(response['Records'])
if 'ErrorCode' in r
]
if failed_records:
print(f"Retry nécessaire pour {len(failed_records)} enregistrements")3. المستهلكون — قراءة البيانات
الوضع القياسي (GetRecords)
NOTEGetRecords Standard — يتشارك جميع المستهلكين حد 2 ميجابايت/ثانية لكل شظية. إذا كان لديك 3 مستهلكين على شظية واحدة، يحصل كل منهم على ~667 كيلوبايت/ثانية فقط.
# Consommateur standard avec GetRecords
import boto3, json, time
kinesis = boto3.client('kinesis', region_name='ca-central-1')
STREAM_NAME = 'donnees-sante'
# 1. Lister les shards
response = kinesis.describe_stream(StreamName=STREAM_NAME)
shards = response['StreamDescription']['Shards']
for shard in shards:
shard_id = shard['ShardId']
# 2. Obtenir un itérateur de shard (LATEST = nouvelles données seulement)
iterator_response = kinesis.get_shard_iterator(
StreamName=STREAM_NAME,
ShardId=shard_id,
ShardIteratorType='LATEST' # Ou 'TRIM_HORIZON' pour lire depuis le début
)
shard_iterator = iterator_response['ShardIterator']
# 3. Boucle de lecture
while True:
records_response = kinesis.get_records(
ShardIterator=shard_iterator,
Limit=100 # Max 100 enregistrements par appel (ou 10MB)
)
for record in records_response['Records']:
data = json.loads(record['Data'].decode('utf-8'))
print(f"Device: {data['device_id']}, HR: {data['heart_rate']}")
shard_iterator = records_response['NextShardIterator']
# Limitation : max 5 appels GetRecords/s par shard
if not records_response['Records']:
time.sleep(1) # Attendre si pas de nouvelles donnéesEnhanced Fan-Out — مستهلكون مخصصون
TIPEnhanced Fan-Out — يحصل كل مستهلك مسجَّل على سعة خاصة به تبلغ 2 ميجابايت/ثانية لكل شظية (بدلاً من المشاركة). يستخدم HTTP/2 push بدلاً من الاستطلاع. مثالي للتطبيقات الحرجة التي تتطلب زمن استجابة منخفض (<70ms).
# Enregistrer un consommateur Enhanced Fan-Out
aws kinesis register-stream-consumer \
--stream-arn arn:aws:kinesis:ca-central-1:123456789012:stream/donnees-sante \
--consumer-name alertes-medicales-temps-reel
# Lister les consommateurs enregistrés
aws kinesis list-stream-consumers \
--stream-arn arn:aws:kinesis:ca-central-1:123456789012:stream/donnees-sante# Consommateur avec Enhanced Fan-Out (SubscribeToShard)
import boto3, json
kinesis = boto3.client('kinesis', region_name='ca-central-1')
STREAM_ARN = 'arn:aws:kinesis:ca-central-1:123456789012:stream/donnees-sante'
CONSUMER_ARN = 'arn:aws:kinesis:ca-central-1:123456789012:stream/donnees-sante/consumer/alertes-medicales:1234567890'
SHARD_ID = 'shardId-000000000000'
# SubscribeToShard avec HTTP/2 streaming (push au lieu de polling)
response = kinesis.subscribe_to_shard(
ConsumerARN=CONSUMER_ARN,
ShardId=SHARD_ID,
StartingPosition={'Type': 'LATEST'}
)
# Traiter le flux d'événements en temps réel
event_stream = response['EventStream']
for event in event_stream:
if 'SubscribeToShardEvent' in event:
for record in event['SubscribeToShardEvent']['Records']:
data = json.loads(record['Data'].decode('utf-8'))
print(f"[Enhanced Fan-Out] Device: {data['device_id']}, HR: {data['heart_rate']}")الفصل 02 – الدرس 1 : Amazon OpenSearch — الفهرسة والبحث في الوقت الفعلي
NOTEالهدف — فهم كيفية عمل Amazon OpenSearch Service : كيفية إنشاء الفهارس، تعريف الـ mappings، وإجراء عمليات البحث والتجميعات في الوقت الفعلي على بيانات التدفق.
1. OpenSearch — المفاهيم الأساسية
NOTEOpenSearch — نسخة مفتوحة المصدر مشتقة من Elasticsearch (طوّرتها AWS منذ 2021). محرك بحث وتحليلات موزَّع مبني على Apache Lucene. Amazon OpenSearch Service هو الإصدار المُدار على AWS.
المصطلحات
| Index | ≈ جدول في قاعدة بيانات |
| Document | ≈ صف في جدول (JSON) |
| Field | ≈ عمود في جدول |
| Shard | جزء من فهرس (توزيع) |
| Replica | نسخة من شظية (توافر عالٍ) |
| Mapping | مخطط أنواع الحقول |
متى تستخدم OpenSearch
2. إنشاء نطاق OpenSearch
# Créer un domaine OpenSearch pour données IoT
aws opensearch create-domain \
--domain-name sante-iot-dashboard \
--engine-version 'OpenSearch_2.11' \
--cluster-config '{
"InstanceType": "t3.small.search",
"InstanceCount": 2,
"DedicatedMasterEnabled": false,
"ZoneAwarenessEnabled": true,
"ZoneAwarenessConfig": {"AvailabilityZoneCount": 2}
}' \
--ebs-options '{
"EBSEnabled": true,
"VolumeType": "gp3",
"VolumeSize": 20,
"Iops": 3000
}' \
--encryption-at-rest-options '{"Enabled": true}' \
--node-to-node-encryption-options '{"Enabled": true}' \
--domain-endpoint-options '{"EnforceHTTPS": true, "TLSSecurityPolicy": "Policy-Min-TLS-1-2-2019-07"}' \
--advanced-security-options '{
"Enabled": true,
"InternalUserDatabaseEnabled": true,
"MasterUserOptions": {
"MasterUserName": "admin",
"MasterUserPassword": "VotreMotDePasseSecurise#123"
}
}'
# Vérifier le statut du domaine
aws opensearch describe-domain \
--domain-name sante-iot-dashboard \
--query "DomainStatus.Processing"3. إنشاء فهرس مع Mapping
NOTEMapping — يحدد الـ mapping أنواع بيانات كل حقل. يستطيع OpenSearch اكتشاف الأنواع تلقائيًا (dynamic mapping)، لكن يُفضَّل تعريف الـ mapping صراحةً للحقول الحرجة لتجنب أخطاء النوع.
import boto3
from opensearchpy import OpenSearch, RequestsHttpConnection
from requests_aws4auth import AWS4Auth
# Connexion au domaine OpenSearch
region = 'ca-central-1'
service = 'es'
host = 'https://votre-domaine.ca-central-1.es.amazonaws.com'
credentials = boto3.Session().get_credentials()
awsauth = AWS4Auth(
credentials.access_key,
credentials.secret_key,
region, service,
session_token=credentials.token
)
client = OpenSearch(
hosts=[{'host': host.replace('https://', ''), 'port': 443}],
http_auth=awsauth,
use_ssl=True,
verify_certs=True,
connection_class=RequestsHttpConnection
)
# Créer l'index avec un mapping explicite
mapping = {
"settings": {
"number_of_shards": 2, # Distribuer sur 2 nœuds
"number_of_replicas": 1 # 1 copie pour la HA
},
"mappings": {
"properties": {
"device_id": {"type": "keyword"}, # Valeur exacte (non analysée)
"patient_id": {"type": "keyword"},
"timestamp": {"type": "date", "format": "epoch_second"},
"heart_rate": {"type": "integer"},
"spo2": {"type": "float"},
"temperature": {"type": "float"},
"steps": {"type": "integer"},
"severite": {"type": "keyword"},
"region": {"type": "keyword"},
"processed_at":{"type": "date"},
# Champ texte analysé pour la recherche full-text
"notes": {"type": "text", "analyzer": "french"}
}
}
}
response = client.indices.create(index='donnees-cardiaques', body=mapping)
print(f"Index créé : {response['acknowledged']}")
# Vérifier le mapping
mapping_info = client.indices.get_mapping(index='donnees-cardiaques')
print(f"Mapping : {list(mapping_info['donnees-cardiaques']['mappings']['properties'].keys())}")4. فهرسة المستندات
import time, json, random
# Indexer un document individuel
document = {
"device_id": "WATCH-042",
"patient_id": "PATIENT-42",
"timestamp": int(time.time()),
"heart_rate": 88,
"spo2": 97.5,
"temperature": 37.1,
"steps": 12453,
"severite": "NORMAL",
"region": "ca-central-1"
}
response = client.index(
index='donnees-cardiaques',
body=document,
id=f"{document['device_id']}-{document['timestamp']}", # ID unique
refresh=True # Rend le document immédiatement recherchable
)
print(f"Document indexé : {response['result']}")
# Indexation en bulk (plus efficace pour de grands volumes)
from opensearchpy.helpers import bulk
def generer_donnees(n_montres=100):
"""Générer n enregistrements de montres cardiaques"""
actions = []
for i in range(n_montres):
heart_rate = random.randint(55, 115)
spo2 = random.uniform(93, 100)
doc = {
"_index": "donnees-cardiaques",
"_id": f"WATCH-{i:03d}-{int(time.time())}",
"_source": {
"device_id": f"WATCH-{i:03d}",
"patient_id": f"PATIENT-{i}",
"timestamp": int(time.time()),
"heart_rate": heart_rate,
"spo2": round(spo2, 1),
"temperature": round(random.uniform(36.0, 37.8), 1),
"steps": random.randint(0, 20000),
"severite": "CRITIQUE" if heart_rate > 140 else "ATTENTION" if heart_rate > 110 else "NORMAL",
"region": "ca-central-1"
}
}
actions.append(doc)
return actions
# Indexation bulk
success, errors = bulk(client, generer_donnees(100))
print(f"Indexé : {success} documents, {len(errors)} erreurs")5. عمليات البحث والتجميعات
بحث بسيط (Match Query)
# Trouver tous les enregistrements CRITIQUES
query_critiques = {
"query": {
"term": {"severite": "CRITIQUE"}
},
"sort": [{"timestamp": {"order": "desc"}}],
"size": 20
}
response = client.search(index='donnees-cardiaques', body=query_critiques)
print(f"Enregistrements critiques : {response['hits']['total']['value']}")
for hit in response['hits']['hits']:
print(f" Device: {hit['_source']['device_id']}, HR: {hit['_source']['heart_rate']}")بحث مع فلتر نطاق (Range Query)
# Trouver les anomalies cardiaques des 30 dernières minutes
now = int(time.time())
thirty_min_ago = now - 1800
query_anomalies = {
"query": {
"bool": {
"must": [
{
"range": {
"timestamp": {
"gte": thirty_min_ago,
"lte": now
}
}
}
],
"should": [
{"range": {"heart_rate": {"gt": 120}}}, # Tachycardie
{"range": {"heart_rate": {"lt": 45}}}, # Bradycardie
{"range": {"spo2": {"lt": 90}}} # Hypoxémie
],
"minimum_should_match": 1
}
},
"sort": [{"timestamp": {"order": "desc"}}]
}
response = client.search(index='donnees-cardiaques', body=query_anomalies)
print(f"Anomalies détectées : {response['hits']['total']['value']}")التجميعات — إحصائيات في الوقت الفعلي
# Agrégation : statistiques cardiaques par device (dernières 24h)
query_stats = {
"query": {
"range": {
"timestamp": {"gte": "now-24h/h", "lte": "now"}
}
},
"aggs": {
"par_device": {
"terms": {"field": "device_id", "size": 50},
"aggs": {
"fc_moyenne": {"avg": {"field": "heart_rate"}},
"fc_max": {"max": {"field": "heart_rate"}},
"fc_min": {"min": {"field": "heart_rate"}},
"spo2_moyenne":{"avg": {"field": "spo2"}},
"nb_critiques":{
"filter": {"term": {"severite": "CRITIQUE"}}
}
}
},
"par_severite": {
"terms": {"field": "severite"},
"aggs": {
"nb_patients": {"cardinality": {"field": "patient_id"}}
}
},
"tendance_horaire": {
"date_histogram": {
"field": "timestamp",
"fixed_interval": "1h",
"format": "yyyy-MM-dd HH:mm"
},
"aggs": {
"fc_moyenne": {"avg": {"field": "heart_rate"}}
}
}
},
"size": 0 # Ne retourner que les agrégations, pas les documents
}
response = client.search(index='donnees-cardiaques', body=query_stats)
buckets = response['aggregations']['par_severite']['buckets']
for b in buckets:
print(f"Sévérité {b['key']}: {b['doc_count']} lectures, {b['nb_patients']['value']} patients")الفصل 02 – الدرس 2 : Amazon Cognito — المصادقة لـ OpenSearch Dashboards
NOTEالهدف — إعداد Amazon Cognito لتأمين الوصول إلى OpenSearch Dashboards، إنشاء أدوار مختلفة لأنواع مختلفة من المستخدمين (مسؤولون، علماء بيانات، مشغلو مراقبة)، وتطبيق تحكم دقيق في الوصول إلى الفهارس.
1. لماذا Cognito لـ OpenSearch؟
NOTEالمشكلة — افتراضيًا، تستخدم OpenSearch Dashboards مصادقة HTTP أساسية (اسم مستخدم/كلمة مرور). هذا غير مناسب لمؤسسة تضم عشرات المستخدمين وملفات تعريف مختلفة واحتياجات مصادقة SSO.
بدون Cognito
مع Cognito
2. بنية Cognito + OpenSearch
# Architecture d'authentification # UTILISATEUR # ↓ (1) Accède à OpenSearch Dashboards # AMAZON COGNITO USER POOL # ↓ (2) Vérifie les credentials (ou SSO via IdP) # ↓ (3) Émet un token JWT # AMAZON COGNITO IDENTITY POOL # ↓ (4) Échange le JWT contre des credentials AWS temporaires (STS) # ↓ (5) Assigne un rôle IAM selon le groupe Cognito # AMAZON OPENSEARCH SERVICE # ↓ (6) Vérifie les permissions Fine-Grained Access Control (FGAC) # DONNÉES DE L'INDEX
3. إنشاء User Pool في Cognito
# Étape 1 : Créer le User Pool
aws cognito-idp create-user-pool \
--pool-name opensearch-sante-users \
--policies '{
"PasswordPolicy": {
"MinimumLength": 12,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": true
}
}' \
--auto-verified-attributes email \
--mfa-configuration OPTIONAL \
--sms-configuration '{"SnsCallerArn": "arn:aws:iam::123456789012:role/CognitoSNSRole", "ExternalId": "extId"}' \
--query "UserPool.Id" --output text
# → us-east-1_XXXXXXXXX (notez cet ID)
# Étape 2 : Créer le App Client (sans secret pour Dashboards)
aws cognito-idp create-user-pool-client \
--user-pool-id us-east-1_XXXXXXXXX \
--client-name opensearch-dashboards-client \
--no-generate-secret \
--explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
--supported-identity-providers COGNITO \
--query "UserPoolClient.ClientId" --output text
# → YYYYYYYYYYYYYYYYYY (notez cet ID)إنشاء مجموعات المستخدمين
# Groupe 1 : Administrateurs OpenSearch (accès total)
aws cognito-idp create-group \
--user-pool-id us-east-1_XXXXXXXXX \
--group-name opensearch-admins \
--description "Accès administrateur complet à OpenSearch" \
--role-arn arn:aws:iam::123456789012:role/OpenSearch-AdminRole
# Groupe 2 : Data Scientists (lecture seule sur tous les index)
aws cognito-idp create-group \
--user-pool-id us-east-1_XXXXXXXXX \
--group-name opensearch-scientists \
--description "Lecture seule pour analyse" \
--role-arn arn:aws:iam::123456789012:role/OpenSearch-DataScientistRole
# Groupe 3 : Opérateurs de monitoring (lecture sur l'index monitoring seulement)
aws cognito-idp create-group \
--user-pool-id us-east-1_XXXXXXXXX \
--group-name opensearch-operators \
--description "Monitoring opérationnel uniquement" \
--role-arn arn:aws:iam::123456789012:role/OpenSearch-OperatorRole
# Créer un utilisateur et l'ajouter à un groupe
aws cognito-idp admin-create-user \
--user-pool-id us-east-1_XXXXXXXXX \
--username dr.dupont@hopital.ca \
--user-attributes Name=email,Value=dr.dupont@hopital.ca Name=email_verified,Value=true \
--temporary-password TempPassword123!
aws cognito-idp admin-add-user-to-group \
--user-pool-id us-east-1_XXXXXXXXX \
--username dr.dupont@hopital.ca \
--group-name opensearch-scientists4. إنشاء Identity Pool
# Créer l'Identity Pool (lie le User Pool aux rôles IAM)
aws cognito-identity create-identity-pool \
--identity-pool-name opensearch-sante-identity \
--allow-unauthenticated-identities false \
--cognito-identity-providers '[
{
"ProviderName": "cognito-idp.ca-central-1.amazonaws.com/ca-central-1_XXXXXXXXX",
"ClientId": "YYYYYYYYYYYYYYYYYY",
"ServerSideTokenCheck": true
}
]' \
--query "IdentityPoolId" --output text
# → ca-central-1:ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ
# Associer les rôles IAM à l'Identity Pool
aws cognito-identity set-identity-pool-roles \
--identity-pool-id "ca-central-1:ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ" \
--roles authenticated=arn:aws:iam::123456789012:role/OpenSearch-DataScientistRole \
--role-mappings '{
"cognito-idp.ca-central-1.amazonaws.com/ca-central-1_XXXXXXXXX:YYYYYYYYYYYYYYYYYY": {
"Type": "Rules",
"AmbiguousRoleResolution": "AuthenticatedRole",
"RulesConfiguration": {
"Rules": [
{
"Claim": "cognito:groups",
"MatchType": "Contains",
"Value": "opensearch-admins",
"RoleARN": "arn:aws:iam::123456789012:role/OpenSearch-AdminRole"
},
{
"Claim": "cognito:groups",
"MatchType": "Contains",
"Value": "opensearch-operators",
"RoleARN": "arn:aws:iam::123456789012:role/OpenSearch-OperatorRole"
}
]
}
}
}'5. التحكم الدقيق في الوصول داخل OpenSearch
NOTEFine-Grained Access Control (FGAC) — يتيح OpenSearch التحكم في الوصول على مستوى الفهرس ونوع المستند وحتى الحقول الفردية. يسمح ذلك بإنشاء أدوار مخصصة لا تعرض سوى البيانات اللازمة.
# Configurer les rôles OpenSearch via l'API REST
import requests, json
host = 'https://votre-domaine.ca-central-1.es.amazonaws.com'
auth = ('admin', 'VotreMotDePasseSecurise#123')
# Rôle 1 : Lecture seule sur l'index données-cardiaques
roles_payload = {
"cluster_permissions": ["cluster:monitor/main"],
"index_permissions": [
{
"index_patterns": ["donnees-cardiaques*"],
"allowed_actions": [
"read",
"indices:data/read/search",
"indices:data/read/msearch",
"indices:admin/mappings/get"
]
}
],
"tenant_permissions": [
{
"tenant_patterns": ["global_tenant"],
"allowed_actions": ["kibana_all_read"]
}
]
}
# Créer le rôle "data-scientist-reader"
requests.put(
f"{host}/_plugins/_security/api/roles/data-scientist-reader",
json=roles_payload, auth=auth
)
# Mapper le rôle IAM Cognito vers le rôle OpenSearch
role_mapping = {
"backend_roles": [
"arn:aws:iam::123456789012:role/OpenSearch-DataScientistRole"
],
"description": "Data Scientists ont accès en lecture aux données cardiaques"
}
requests.put(
f"{host}/_plugins/_security/api/rolesmapping/data-scientist-reader",
json=role_mapping, auth=auth
)
print("Rôle et mapping créés avec succès")6. تفعيل Cognito في OpenSearch
# Activer l'authentification Cognito sur le domaine OpenSearch
aws opensearch update-domain-config \
--domain-name sante-iot-dashboard \
--cognito-options '{
"Enabled": true,
"UserPoolId": "ca-central-1_XXXXXXXXX",
"IdentityPoolId": "ca-central-1:ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ",
"RoleArn": "arn:aws:iam::123456789012:role/CognitoAccessForAmazonOpenSearch"
}'
# Attendre que la mise à jour soit terminée
aws opensearch describe-domain \
--domain-name sante-iot-dashboard \
--query "DomainStatus.Processing"
# Attendre que la valeur soit "false"
# Récupérer l'URL des Dashboards
aws opensearch describe-domain \
--domain-name sante-iot-dashboard \
--query "DomainStatus.Endpoints"va-plus-loin
يغطي هذا المقال المقتطفات الأكثر فائدة — الدورة الكاملة AWS Données Temps Réel (6 فصول، 14 درسًا، تمارين مصححة ومشروع نهائي) تأخذك إلى النهاية.
./acceder-au-cours-complet cours gratuit : Maîtriser Claude Codeالأسئلة الشائعة
كم من الوقت يستغرق تعلم AWS Données Temps Réel؟
مع تقدم منظَّم (6 فصول، 14 درسًا قصيرًا وعمليًا)، يمكن الوصول إلى مستوى تشغيلي في بضعة أسابيع بمعدل 30 إلى 60 دقيقة يوميًا. المهم هو تطبيق كل مفهوم فورًا.
هل هناك متطلبات مسبقة؟
تكفي أساسيات الحوسبة. إذا كنت تستطيع استخدام الطرفية وقراءة كود بسيط، فأنت جاهز.
من أين أبدأ عمليًا؟
أعد تنفيذ الأوامر الواردة في هذا المقال، ثم تابع الدورة الكاملة AWS Données Temps Réel: فهي تربط الـ 14 درسًا بالترتيب مع تمارين ومشروع نهائي.
./a-lire-aussi
→ AWS Data Engineering Bootcamp explained simply (with diagrams and real code)→ Python Data Science : les 9 étapes clés pour passer de zéro à opérationnel→ Python NumPy en pratique : le code et les commandes qui comptent vraiment📬 هل تريد تلقي هذا النوع من الأدلة كل أسبوع؟ اشترك مجانًا — كود حقيقي، بدون كلام زائد.