Ansible cloudformation 模块使用 shell 的这些环境变量:
$ export AWS_PROFILE=djangoapp
$ export AWS_DEFAULT_REGION=ca-central-1
$ aws configure list
Name Value Type Location
---- ----- ---- --------
profile djangoapp manual --profile
access_key ****************WKWG shared-credentials-file
secret_key ****************/I4Z shared-credentials-file
region ca-central-1 env AWS_DEFAULT_REGION
$ cat ~/.aws/config
[djangoapp]
region = ca-central-1
$ cat ~/.aws/credentials
[djangoapp]
aws_access_key_id = ****************KWG
aws_secret_access_key = ********************/I4Z
$
$ aws ec2 describe-vpcs
{
"Vpcs": [
{
"CidrBlock": "172.31.0.0/16",
"DhcpOptionsId": "dopt-aaaaa8",
"State": "available",
"VpcId": "vpc-ccccccc",
"OwnerId": "444444444",
"InstanceTenancy": "default",
"CidrBlockAssociationSet": [
{
"AssociationId": "vpc-cidr-assoc-fffff",
"CidrBlock": "172.31.0.0/16",
"CidrBlockState": {
"State": "associated"
}
}
],
"IsDefault": true
}
]
}
./site.yml
---
- name: Todobackend deployment playbook
hosts: localhost
connection: local
gather_facts: no
vars_files:
- secrets.yml
environment:
AWS_DEFAULT_REGION: "{{ lookup('env', 'AWS_DEFAULT_VERSION') | default('ca-central-1', true) }}"
tasks:
- include: tasks/create_stack.yml
- include: tasks/deploy_app.yml
./tasks/create_stack.yml
---
- name: task to create/update stack
cloudformation:
stack_name: todobackend
state: present
template: templates/stack.yml
template_format: yaml
template_parameters:
VpcId: "{{ vpc_id }}"
SubnetId: "{{ subnet_id }}"
KeyPair: "{{ ec2_keypair }}"
InstanceCount: "{{ instance_count | default(1) }}"
DbSubnets: "{{ db_subnets | join(',') }}"
DbAvailabilityZone: "{{ db_availability_zone }}"
DbUsername: "{{ db_username }}"
DbPassword: "{{ db_password }}"
tags:
Environment: test
register: cf_stack
- name: Debug output
debug: msg="{{ cf_stack }}"
when: debug is defined
./templates/stack.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Todobackend Stack"
# Stack Parameters
Parameters:
VpcId:
Type: "AWS::EC2::VPC::Id"
Description: "The target VPC Id"
SubnetId:
Type: "AWS::EC2::Subnet::Id"
Description: "The target Subnet Id in availability zone a"
KeyPair:
Type: "String"
Description: "The key pair that is allowed SSH access"
InstanceCount:
Type: "Number"
Description: "The desired number of application instances"
DbSubnets:
Type: "List<AWS::EC2::Subnet::Id>"
Description: "The target DB Subnet Group subnet Ids"
DbAvailabilityZone:
Type: "String"
Description: "The target availability zone for the database instance"
DbUsername:
Type: "String"
Description: "The RDS database username"
DbPassword:
Type: "String"
Description: "The RDS database password"
NoEcho: "true"
# Stack Resources
Resources:
# Configure auto scaing group
AutoScalingGroup:
Type: "AWS::AutoScaling::AutoScalingGroup"
Properties:
VPCZoneIdentifier: [ { "Ref": "SubnetId" } ]
LaunchConfigurationName: { "Ref": "AutoScalingLaunchConfiguration" }
MinSize: 0
MaxSize: 2
DesiredCapacity: { "Ref": "InstanceCount" }
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, " -instance" ] ] }
PropagateAtLaunch: "true"
AutoScalingLaunchConfiguration:
Type: "AWS::AutoScaling::LaunchConfiguration"
Properties:
ImageId: ami-05958d7635caa4d04
InstanceType: t2.micro
keyName: { "Ref": "KeyPair" }
IamInstanceProfile: { "Ref": "EC2InstanceProfile" }
SecurityGroups:
- { "Ref": "EC2InstanceSecurityGroup" }
UserData: {
"Fn::Base64": { "Fn::Join": ["", [
"#!/bin/bash\n",
"echo ECS_CLUSTER=", { "Ref": "EcsCluster"}, " >> /etc/ecs/ecs.config\n"
] ] }
}
EC2InstanceSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "todobackend-sg"
VpcId: { "Ref": "VpcId" }
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: "8080"
ToPort: "8080"
SourceSecurityGroupId: { "Ref": "ElbSecurityGroup" }
- IpProtocol: "tcp"
FromPort: "22"
ToPort: "22"
CidrIp: "0.0.0.0/0"
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-instance-sg" ] ] }
EC2InstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
Roles: [ { "Ref": "EC2InstanceRole" } ]
EC2InstanceRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": [ "ec2.amazonaws.com" ] },
"Action": [ "sts:AssumeRole"]
}
]
}
Path: "/"
ManagePolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceForEc2Role"
# Configure RDS
DbInstance:
Type: "AWS::RDS::DBInstance"
Properties:
DBSubnetGroupName: { "Ref": "DbSubnetGroup" }
MultiAZ: "false"
AvailabilityZone: { "Ref": "DBAvailabilityZone" }
AllocatedStorage: 8
StorageType: "gp2"
DBInstanceClass: "db.t2.micro"
DBName: "todobackend"
Engine: "MySQL"
EngineVersion: "5.6"
MasterUserName: { "Ref": "DbUserName" }
MasterUserPassword: { "Ref": "DbPassword" }
VPCSecurityGroups:
- { "Ref": "DbSecurityGroup" }
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::Stackname" }, "-db" ] ] }
DbSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "Todobackend DB Security Group"
VpcId: { "Ref": "VpcId" }
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: "3306"
ToPort: "3306"
SourceSecurityGroupId: { "Ref": "EC2InstanceSecurityGroup" }
DbSubnetGroup:
Type: "AWS::RDS::DBSubnetGroup"
Properties:
DBSubnetGroupDescription: "Todobackend DB Subnet Group"
SubnetIds: { "Ref": "DbSubnets" }
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-db-subnet-group" ] ] }
# Configure ELB
ElasticLoadBalancer:
Type: "AWS::ElasticLoadBalancing::LoadBalancer"
Properties:
CrossZone: "false"
SecurityGroups: [ { "Ref": "ElbSecurityGroup" } ]
Listeners:
- LoadBalancerPort: "80"
InstancePort: "8000"
Protocol: "http"
HealthCheck:
Target: "HTTP:8000/todos"
HealthyThreshold: "2"
UnhealthyThreshold: "10"
Interval: "30"
Timeout: "5"
Subnets: [ { "Ref": "SubnetId" } ]
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-elb" ] ] }
ElbSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "Todobackend ELB Security Group"
VpcId: { "Ref": "VpcId" }
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: "80"
ToPort: "80"
CidrIp: "0.0.0.0/0"
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-elb-sg" ] ] }
# Configure ECS
EcsCluster:
Type: "AWS::ECS::EcsCluster"
TodobackendTaskDefinition:
Type: "AWS::ECS::TaskDefinition"
Properties:
ContainerDefinitions:
- Name: todobackend
Image: shamdockerhub/todobackend
Memory: 450
Environment:
- Name: DJANGO_SETTINGS_MODULE
Value: todobackend.settings.release
- Name: MYSQL_HOST
Value: { "Fn::GetAtt": ["DbInstance", "Endpoint.Address"] }
- Name: MYSQL_USER
Value: { "Ref": "DbUsername" }
- Name: MYSQL_PASSWORD
Value: { "Ref": "DbPassword" }
MountPoints:
- ContainerPath: /var/www/todobackend
SourceVolume: webroot
Command:
- uwsgi
- "--socket /var/www/todobackend/todobackend.sock"
- "--chmod-socket=666"
- "--module todobackend.wsgi"
- "--master"
- "--die-on-term"
- Name: nginx
Image: shamdockerhub/todobackend-nginx
Memory: 300
PortMappings:
- ContainerPort: "8000"
HostPort: "8000"
MountPoints:
- ContainerPath: /var/www/todobackend
SourceVolume: webroot
Volumes:
- Name: webroot
Host:
SourcePath: /ecs/webroot
TodobackendService:
Type: "AWS::ECS::Service"
Properties:
TaskDefinition: { "Ref": "TodobackendTaskDefinition" }
Cluster: { "Ref": "EcsCluster" }
LoadBalancers:
- ContainerName: "nginx"
ContainerPort: "8000"
LoadBalancerName: { "Ref": "ElasticLoadBalancer" }
Role: { "Ref": "EcsServiceRole" }
DesiredCount: 0
EcsServiceRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": { "ecs.amazonaws.com" }
},
"Action": [ "sts:AssumeRole" ]
}
]
}
Path: "/"
ManagePolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole
TodobackendAdhocTaskDefinition: # Application management task
Type: "AWS::ECS::TaskDefinition"
Properties:
ContainerDefinitions:
- Name: todobackend
Image: shamdockerhub/todobackend
Memory: 245
Environment:
- Name: DJANGO_SETTINGS_MODULE
Value: todobackend.settings.release
- Name: MYSQL_HOST
Value: { "Fn::GetAtt": ["DbInstance", "Endpoint.Address"] }
- Name: MYSQL_USER
Value: { "Ref": "DbUsername" }
- Name: MYSQL_PASSWORD
Value: { "Ref": "DbPassword" }
MountPoints:
- ContainerPath: /var/www/todobackend
SourcePath: webroot
Volumes:
- Name: webroot
Host:
SourcePath: /ecs/webroot
# Stack outputs
Outputs:
ElbDomainName:
Description: "Public DNS name of Elastic Load Balancer"
Value: { "Fn:GetAtt": [ "ElasticLoadBalancer", "DNSName" ] }
EcsCluster:
Description: "Amazon resource name (ARN) of Todobackend Ecs Cluster"
Value: { "Ref": "EcsCluster" }
TodobackendTaskDefinition:
Description: "Amazon resource name (ARN) of Todobackend Task definition"
Value: { "Ref": "TodobackendTaskDefinition"}
TodobackendAdhocTaskDefinition:
Description: "Amazon resource name(ARN) of Todobackend Adhoc Task Definition"
Value: { "Ref": "TodobackendAdhocTaskDefinition" }
TodobackendService:
Description: "Amazon resource name (ARN) of Todobackend service"
Value: { "Ref": "TodobackendService" }
下面是CreateStack
操作错误:
$ ansible-playbook site.yml --ask-vault-pass -e debug=true -vvv
ansible-playbook 2.5.1
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/home/user1/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/dist-packages/ansible
executable location = /usr/bin/ansible-playbook
python version = 2.7.15+ (default, Oct 7 2019, 17:39:04) [GCC 7.4.0]
Using /etc/ansible/ansible.cfg as config file
Vault password:
Parsed /etc/ansible/hosts inventory source with ini plugin
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match
'all'
Read vars_file 'secrets.yml'
statically imported: /home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/tasks/create_stack.yml
Read vars_file 'secrets.yml'
[WARNING]: file /home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/tasks/deploy_app.yml is
empty and had no tasks to include
PLAYBOOK: site.yml *****************************************************************************************************
1 plays in site.yml
Read vars_file 'secrets.yml'
Read vars_file 'secrets.yml'
PLAY [Todobackend deployment playbook] *********************************************************************************
META: ran handlers
Read vars_file 'secrets.yml'
TASK [task to create/update stack] *************************************************************************************
task path: /home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/tasks/create_stack.yml:2
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/cloud/amazon/cloudformation.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: mohet01-ubuntu
<127.0.0.1> EXEC /bin/sh -c 'echo ~ && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019 `" && echo ansible-tmp-1576716480.56-111176828564019="` echo /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019 `" ) && sleep 0'
<127.0.0.1> PUT /home/user1/.ansible/tmp/ansible-local-7506yaa0Y9/tmpl7pqXl TO /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/cloudformation.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/ /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/cloudformation.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'AWS_DEFAULT_REGION=ca-central-1 /usr/bin/python2 /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/cloudformation.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /home/user1/.ansible/tmp/ansible-tmp-1576716480.56-111176828564019/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
File "/tmp/ansible_bfmm8l/ansible_module_cloudformation.py", line 314, in create_stack
cfn.create_stack(**stack_params)
File "/tmp/ansible_bfmm8l/ansible_modlib.zip/ansible/module_utils/cloud.py", line 150, in retry_func
raise e
ClientError: An error occurred (ValidationError) when calling the CreateStack operation: [/Resources/EcsServiceRole/Type/AssumeRolePolicyDocument/Statement/0/Principal/Service/ecs.amazonaws.com] 'null' values are not allowed in templates
fatal: [localhost]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"aws_access_key": null,
"aws_secret_key": null,
"changeset_name": null,
"create_changeset": false,
"disable_rollback": false,
"ec2_url": null,
"notification_arns": null,
"profile": null,
"region": null,
"role_arn": null,
"security_token": null,
"stack_name": "todobackend",
"stack_policy": null,
"state": "present",
"tags": {
"Environment": "test"
},
"template": "templates/stack.yml",
"template_body": null,
"template_format": "yaml",
"template_parameters": {
"DbAvailabilityZone": "ca-central-1a",
"DbPassword": "ccccc",
"DbSubnets": "subnet-22222,subnet-33333",
"DbUsername": "todobackend",
"InstanceCount": "1",
"KeyPair": "admin",
"SubnetId": "subnet-33333",
"VpcId": "vpc-111111"
},
"template_url": null,
"termination_protection": null,
"validate_certs": true
}
},
"msg": "Failed to create stack todobackend: An error occurred (ValidationError) when calling the CreateStack operation: [/Resources/EcsServiceRole/Type/AssumeRolePolicyDocument/Statement/0/Principal/Service/ecs.amazonaws.com] 'null' values are not allowed in templates An error occurred (ValidationError) when calling the CreateStack operation: [/Resources/EcsServiceRole/Type/AssumeRolePolicyDocument/Statement/0/Principal/Service/ecs.amazonaws.com] 'null' values are not allowed in templates - <class 'botocore.exceptions.ClientError'>."
}
to retry, use: --limit @/home/user1/git/ContDelivery_course/DjangoApp/todobackend-deploy/site.retry
PLAY RECAP *************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1
$
为什么module_args
字典有空值?如何解决此错误?
Ansible 2.5.1 正在使用 Python 2.7