云简历挑战是一个项目,可帮助我们学习使用云和一些基本工具。您可以在此处访问项目的轮廓。
在这篇博客文章中,我将告诉您我完成的步骤,以及我经历了Cloud Resume Challenge的挑战。这个挑战使我更熟悉与Terraform一起使用AWS服务。我了解了服务的工作原理。我还学到了很多我现在想不到的事情。
您可以看到最终结果here并查看GitHub code。
您可以作为AWS Free Tier的一部分完成此挑战。
您只需要购买域名,但是如果您是学生,则有很多免费购买的方法。在本文的下一部分中,我将讨论如何获得免费域名。
现在,让我们看一下我们需要完成的阶段。
挑战步骤
- 在html/css中构建网站。
此步骤非常容易。只需使用HTML和CSS创建简历网站的简历页。
- 带有S3桶的主机网站。
我正在使用Terraform构建,但是您可以使用AWS SAM。
我创建了一个S3存储桶并确定了必要的CORS规则,最后,我确保将对象集体上传到S3桶。
resource "aws_s3_bucket" "cloud-resume-bucket" {
bucket = var.bucket_name
acl = "public-read"
policy = file("website/policy.json")
website {
index_document = "index.html"
error_document = "error.html"
}
}
resource "aws_s3_bucket_cors_configuration" "s3_bucket_cors" {
bucket = aws_s3_bucket.cloud-resume-bucket.id
cors_rule {
allowed_headers = ["*"]
allowed_methods = ["GET", "POST"]
allowed_origins = ["*"]
max_age_seconds = 10
}
}
resource "aws_s3_object" "test" {
for_each = fileset("${path.module}/html", "**/*.*")
acl = "public-read"
bucket = var.bucket_name
key = each.value
source = "${path.module}/html/${each.value}"
content_type = lookup(var.mime_types, split(".", each.value)[length(split(".", each.value)) - 1])
}
- 用于路由http/s流量的云方。
S3网站URL应将HTTPS用于安全性。我用Cloudfront做到了。
resource "aws_cloudfront_distribution" "s3_cf" {
origin {
domain_name = "${aws_s3_bucket.cloud-resume-bucket.bucket_regional_domain_name}"
origin_id = "${local.s3_origin_id}"
}
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
custom_error_response {
error_caching_min_ttl = 0
error_code = 404
response_code = 200
response_page_path = "/error.html"
}
aliases = [var.domain_name]
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "${local.s3_origin_id}"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https" #redirect-to-https
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
# viewer_certificate {
# cloudfront_default_certificate = true
# }
viewer_certificate {
acm_certificate_arn = aws_acm_certificate_validation.acm_val.certificate_arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
}
- Route53用于自定义DNS。
在此步骤中,我将我的域名注册到Route53服务中,如下所示。
(使用GitHub学生包,您可以获取一个免费的域名。)
resource "aws_route53_zone" "main" {
name = var.domain_name
}
resource "aws_route53_record" "domain" {
zone_id = "${aws_route53_zone.main.zone_id}"
name = "${var.domain_name}"
type = "A"
alias {
name = "${aws_cloudfront_distribution.s3_cf.domain_name}"
zone_id = "${aws_cloudfront_distribution.s3_cf.hosted_zone_id}"
evaluate_target_health = false
}
}
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = aws_route53_zone.main.zone_id
}
- 证书经理,用于使用SSL证书启用安全访问。
我设置了ACM服务的SSL证书。
resource "aws_acm_certificate" "cert" {
domain_name = var.domain_name
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
resource "aws_acm_certificate_validation" "acm_val" {
certificate_arn = aws_acm_certificate.cert.arn
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
- DynamoDB用于数据库,存储网站访问者数量。
我创建了一个DynamoDB数据库来检索和更新访问者计数器。
resource "aws_dynamodb_table" "visiters" {
name = var.dynamodb_table
billing_mode = "PROVISIONED"
read_capacity = 1
write_capacity = 1
hash_key = "id"
attribute {
name = "id"
type = "N"
}
}
- lambda函数(Python)读取/写网站访问者数量到dynamodb。
将其设置为使用数据库后。我添加了与Python(Boto3)和必要的IAM策略一起编写的Lambda功能。
data "archive_file" "lambda_zip" {
type = "zip"
source_dir = "${path.module}/src"
output_path = "${path.module}/src.zip"
}
resource "aws_s3_object" "this" {
bucket = aws_s3_bucket.cloud-resume-bucket.id
key = "src.zip"
source = data.archive_file.lambda_zip.output_path
etag = filemd5(data.archive_file.lambda_zip.output_path)
}
//Define lambda function
resource "aws_lambda_function" "apigw_lambda_ddb" {
function_name = "app"
description = "visiter counter"
s3_bucket = aws_s3_bucket.cloud-resume-bucket.id
s3_key = aws_s3_object.this.key
runtime = "python3.8"
handler = "app.lambda_handler"
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
role = aws_iam_role.lambda_exec.arn
environment {
variables = {
DDB_TABLE = var.dynamodb_table
}
}
}
resource "aws_iam_role" "lambda_exec" {
name_prefix = "LambdaDdbPost"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "lambda.amazonaws.com"
}
}
]
})
}
resource "aws_iam_policy" "lambda_exec_role" {
name_prefix = "lambda-tf-pattern-ddb-post"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:UpdateItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/${var.dynamodb_table}"
}
]
}
POLICY
}
resource "aws_iam_role_policy_attachment" "lambda_policy" {
role = aws_iam_role.lambda_exec.name
policy_arn = aws_iam_policy.lambda_exec_role.arn
}
- API网关触发lambda函数。
我设置了触发lambda func的API网关。
# resource "random_string" "random" {
# length = 4
# special = false
# }
resource "aws_apigatewayv2_api" "http_lambda" {
# name = "${var.apigw_name}-${random_string.random.id}"
name = "${var.apigw_name}"
protocol_type = "HTTP"
}
resource "aws_apigatewayv2_stage" "default" {
api_id = aws_apigatewayv2_api.http_lambda.id
name = "$default"
auto_deploy = true
}
resource "aws_apigatewayv2_integration" "apigw_lambda" {
api_id = aws_apigatewayv2_api.http_lambda.id
integration_uri = aws_lambda_function.apigw_lambda_ddb.invoke_arn
integration_type = "AWS_PROXY"
integration_method = "POST"
}
resource "aws_apigatewayv2_route" "get" {
api_id = aws_apigatewayv2_api.http_lambda.id
route_key = "GET /"
target = "integrations/${aws_apigatewayv2_integration.apigw_lambda.id}"
}
# Gives an external source permission to access the Lambda function.
resource "aws_lambda_permission" "api_gw" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.apigw_lambda_ddb.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.http_lambda.execution_arn}/*/*"
}
然后,在使用GitHub操作设置CI/CD流程后,我启动了网站。这篇博客文章只是我工作的摘要。完成此操作时,我遇到了许多困难。对我来说,最困难的一步是连接lambda -apigw -dynamoDB服务,但是经过一些思考和研究,我能够解决这个问题。感谢您的阅读,如果您想详细介绍项目的代码,可以访问我的GitHub帐户。