BLOG
지리적 AWS 리전에 걸쳐 많은 노드를 관리하는 조직에서는 해당 AWS OpsWorks for Chef 자동화 구현 시 레이턴시를 줄이고 노드 간에 로드를 줄이기를 희망할 수 있습니다. 여러 서버 간에 노드를 배포함으로써 조직은 하나 이상의 지역에 상주하는 둘 이상의 Chef Server에서 Cookbook 들과 기타 구성을 일관되게 구축할 수 있는 방법을 찾을 수 있습니다. 이를 위해 고객은 하나 이상의 Chef Automate 인스턴스에 Cookbook을 배포하는 프로세스를 추진하는 몇 가지 추가적인 AWS서비스를 사용할 수 있습니다.
개요
대규모의 Chef사용자들이 이같은 상황을 접할 수 있게 되는 상황은 그들의 OWCA (OpsWorks for Chef Automate)서버의 OpWoorks로 관리되는 노드 수가 그 용량을 초과할 때 입니다. 고객은 가장 최근의 백업을 사용하여 더 큰 Amazon EC2인스턴스로 전환할 수 있지만 이 또한 제한에 걸릴 수도 있습니다. 또한, 세계적으로 분산된 환경에서는 통신 레이턴시가 발생할 수 있습니다.
이는 노드 관리 기능을 여러 OpsWorks for Chef Automate 인스턴스에 대해 관리함으로써 해결할 수 있는 문제입니다. 그러나 이러한 접근 방식은 여러 OWCA 인스턴스 간에 Cookbook을 동기화하는 문제를 제기합니다. 이 작업은 AWS CodeCommit, AWS CodeBuild, AWS CodePipeline및 옵션 AWS Lambda를 사용하여 수행할 수 있습니다. 이 블로그 게시물에서는 고객이 두 지역에 걸쳐 Chef 관리 인프라를 확장할 수 있는 방법을 보여 줍니다.
CodeCommit 과 CodePipeline을 사용하면 쿡북의 개발자가 중앙 저장소에 변화를 줄 수 있습니다. CodePipeline은 이 커밋을 트리거하고 업데이트된 저장소 컨텐츠를 CodeBuild로 전송하여 처리하도록 합니다. 간단한 스크립팅을 사용하여 CodeBuild는 chef supermarket의 의존성을 제거하고 필요한 cookbook 계정(또는 계정)의 각 Chef Automate 인스턴스에 업로드합니다. chef-client cookbook을 사용해서 노드는 사전 구성된 일정에 따라 자동으로 체크인 하게 될 것입니다. 이 블로그 포스트의 파이프 라인의 호출 단계에서는 AWS Lambda 및 AWS Systems Manager를 사용하여 환경의 모든 노드에 대해 chef-client 를 실행할 수 있습니다. 이 단계는 선택 사항이지만 더 많은 변경 사항을 적용하는 시나리오에 유용합니다.
Setup
이 값을 설정하려면 AWS Cloud Formation템플릿을 사용합니다. 템플릿에 추가할 각 리소스를 살펴보겠습니다. 이 작업을 수행하기 전에 Chef Automation인스턴스의 OpsWorks를 하나 이상 생성해야 합니다. 각 인스턴스의 스타터 키트에는 [Starter-Kit]/.chef/ 디렉토리의 개인 키가 포함되어 있습니다. 이 키를 Code Build의 인증에 사용할 수도 있지만, Chef Automate 콘솔에서 별도의 사용자를 생성하고 이를 공용/전용 키 쌍으로 할당하는 것이 좋습니다. 참조를 위해 Chef.io에 있는 가이드라인을 따르시길 바랍니다. 적어도 이 사용자 계정에는 committer 권한이 필요합니다. 이 사용자의 개인 키는 계정의 Amazon S3 버킷에 저장할 수 있으며, CodeBuild에서 cookbook 업로드 프로세스 중에 Chef Automate 서버를 인증하는 데 액세스 합니다. 이 버킷에 대한 액세스를 적절한 버킷 정책으로 엄격히 제어하는 것 또한 중요합니다. 또 다른 고려 사항은 AWS KMS (AWS Key Management Service)에 키를 저장하는 것입니다.
구성 요소들
파라미터
이 템플릿에는 하나의 입력 매개 변수인 KeyBucket 만 필요하며, 이는 이 cookbook 저장소와 동기화할 각 Chef Automate 인스턴스에 대한 개인 키가 들어 있는 Amazon S3 버켓에 해당합니다.
[Json]
{
“Parameters”: {
“KeyBucket”: {
“Type”: “String”,
“Description”: “Name of S3 bucket which contains the OWCA private keys.”,
“AllowedPattern”: “^[a-z0-9][a-z0-9-.]*$”,
“MinLength”: 3,
“MaxLength”: 63,
“ConstraintDescription”: “Please provide a valid S3 bucket name.”
}
}
}
IAM사용 권한
파이프 라인이 제대로 작동하려면 두개의 AWS ID및 액세스 관리(IAM)역할이 필요합니다.
첫번째 IAM역할인 BuildRole은 빌드 작업 중에 CodeBuild에서 사용되며 CodeBuild컨테이너에 대한 기본 사용 권한이 필요합니다. 또한 이 역할은 각각의 Chef Automate 인스턴스에 대한 인증을 위한 개인 키를 포함하고 있는 Amazon S3 버킷(템플릿의 Parameters 섹션 참조)에 액세스 해야 합니다.
두번째 IAM역할인 FunctionRole는 AWS Lambda 환경에서 각 인스턴스에 대해 chef-client를 실행하는 데 사용되며, Lambda 실행 역할에 필요한 Amazon CloudWatch 로그 권한 외에 이 역할에는 AWS Systems Manager를 통해 명령을 전송하는 기능이 필요합니다.
[Json]
{
“Resources”: {
“BuildRole”: {
“Type”: “AWS::IAM::Role”,
“Properties”: {
“AssumeRolePolicyDocument”: {
“Version”: “2012-10-17”,
“Statement”: [ {
“Effect”: “Allow”,
“Principal”: {
“Service”: [ “codebuild.amazonaws.com” ]
},
“Action”: [ “sts:AssumeRole” ]
} ]
},
“Path”: “/”,
“Policies”: [ {
“PolicyName”: “CodeBuildS3WithCWL”,
“PolicyDocument”: {
“Version”: “2012-10-17”,
“Statement”: [
{
“Effect”: “Allow”,
“Action”: [
“s3:Get*”,
“s3:List*”
],
“Resource”: [
{ “Fn::GetAtt”: [ “ArtifactBucket”, “Arn” ] },
{
“Fn::Join”: [ “”, [
{ “Fn::GetAtt”: [ “ArtifactBucket”, “Arn” ] },
“/*”
] ]
},
{
“Fn::Join”: [ “”, [
“arn:aws:s3:::”,
{ “Ref”: “KeyBucket” }
] ]
},
{
“Fn::Join”: [ “”, [
“arn:aws:s3:::”,
{ “Ref”: “KeyBucket” },
“/*”
] ]
}
]
},
{
“Effect”: “Allow”,
“Resource”: [
{
“Fn::Join”: [ “”, [
“arn:aws:logs:”,
{ “Ref”: “AWS::Region” },
“:”,
{ “Ref”: “AWS::AccountId” },
“:log-group:/aws/codebuild/*”
] ]
},
{
“Fn::Join”: [ “”, [
“arn:aws:logs:”,
{ “Ref”: “AWS::Region” },
“:”,
{ “Ref”: “AWS::AccountId” },
“:log-group:/aws/codebuild/*:*”
] ]
}
],
“Action”: [
“logs:CreateLogGroup”,
“logs:CreateLogStream”,
“logs:PutLogEvents”
]
},
{
“Effect”: “Allow”,
“Resource”: [ “arn:aws:s3:::codepipeline-*” ],
“Action”: [
“s3:PutObject”,
“s3:GetObject”,
“s3:GetObjectVersion”
]
},
{
“Effect”: “Allow”,
“Action”: [ “ssm:GetParameters” ],
“Resource”: {
“Fn::Join”: [ “”, [
“arn:aws:ssm:”,
{ “Ref”: “AWS::Region” },
“:”,
{ “Ref”: “AWS::AccountId” },
“:parameter/CodeBuild/*”
] ]
}
}
]
}
} ]
}
},
“FunctionRole”: {
“Type”: “AWS::IAM::Role”,
“Properties”: {
“AssumeRolePolicyDocument”: {
“Version”: “2012-10-17”,
“Statement”: [ {
“Effect”: “Allow”,
“Principal”: {
“Service”: [ “lambda.amazonaws.com” ]
},
“Action”: [ “sts:AssumeRole” ]
} ]
},
“Path”: “/”,
“Policies”: [ {
“PolicyName”: “LambdaBasicExecutionWithSSM”,
“PolicyDocument”: {
“Version”: “2012-10-17”,
“Statement”: [
{
“Effect”: “Allow”,
“Action”: [
“ssm:SendCommand”,
“ssm:GetCommandInvocation”
],
“Resource”: “*”
},
{
“Effect”: “Allow”,
“Action”: [
“logs:CreateLogGroup”,
“logs:CreateLogStream”,
“logs:PutLogEvents”
],
“Resource”: [
{
“Fn::Join”: [ “”, [
“arn:aws:logs:”,
{ “Ref”: “AWS::Region” },
“:”,
{ “Ref”: “AWS::AccountId” },
“:log-group:/aws/lambda/*”
] ]
},
{
“Fn::Join”: [ “”, [
“arn:aws:logs:”,
{ “Ref”: “AWS::Region” },
“:”,
{ “Ref”: “AWS::AccountId” },
“:log-group:/aws/lambda/*:*”
] ]
}
]
},
{
“Effect”: “Allow”,
“Action”: [
“codepipeline:PutJobSuccessResult”,
“codepipeline:PutJobFailureResult”
],
“Resource”: “*”
}
]
}
} ]
}
}
}
}
Amazon S3 Bucket
Codepipeline 에 artifact 를 저장하기 위해 파이프라인 자체가 만들어질 때 ArtifactBucket 자원이 생성되고 참조가 됩니다.
[Json]
{
“Resources”: {
“ArtifactBucket”: {
“Type”: “AWS::S3::Bucket”
}
}
}
CodeCommit repository
생성되는 CodeCommit 저장소는 cookbook을 저장하기 위한 Chef Repo 역할을 합니다. 저장소 구조는 다음 형식을 준수해야 합니다.
[Json]
{
“Resources”: {
“Repo”: {
“Type”: “AWS::CodeCommit::Repository”,
“Properties”: {
“RepositoryDescription”: “Cookbook repository for multiple region OWCA deployment.”,
“RepositoryName”: “owca-multi-region-repo”
}
}
}
}
.chef
.chef/디렉토리 구조는 Chef Automate 스타터 키트의 OpsWorks에 일반적으로 포함되어 있는 것과 약간 다릅니다. 이후의 수정을 통해 나이프 유틸리티는 통신할 Chef Automate 인스턴스와 사용할 인증서 파일을 결정할 때 환경 변수를 사용할 수 있게 만들어줍니다.
아래의 knife.rb 파일은 yaml을 사용하여 config.yml의 구성 데이터를 구문 분석합니다. 올바른 구성 파일은 CHEF_SERVER 환경 변수의 값을 기준으로 결정됩니다.
[YAML]
require ‘yaml’
CHEF_SERVER = ENV[‘CHEF_SERVER’] || “NONE”
current_dir = File.dirname(__FILE__)
base_dir = File.join(File.dirname(File.expand_path(__FILE__)), ‘..’)
env_config = YAML.load_file(“#{current_dir}/#{CHEF_SERVER}/config.yml”)
log_level :info
log_location STDOUT
node_name ‘pivotal’
client_key “#{current_dir}/#{CHEF_SERVER}/private.pem”
syntax_check_cache_path File.join(base_dir, ‘.chef’, ‘syntax_check_cache’)
cookbook_path [File.join(base_dir, ‘cookbooks’)]
chef_server_url env_config[“server”]
ssl_ca_file File.join(base_dir, ‘.chef’, ‘ca_certs’, ‘opsworks-cm-ca-2016-root.pem’)
trusted_certs_dir File.join(base_dir, ‘.chef’, ‘ca_certs’)
커뮤니케이션 할 올바른 Chef Automate 인스턴스를 결정하려면 각 인스턴스의 .chef/ 아래에 인스턴스 이름에 해당하는 고유한 디렉토리가 있어야 합니다. 이 디렉토리 내에서 config.yml 파일은 아래 형식을 따라야 합니다.
server: ‘https://[SERVER_FQDN]/organizations/default’
현재 각각의 Chef Automate 인스턴스 디렉토리에는 서버와 통신하는 데 필요한 개인 키가 포함되어 있지 않습니다. SSL/API키 또는 암호와 같은 인증 정보를 소스 제어 시스템에 커밋 된 파일에 포함하지 않는 것이 좋습니다. 대신 S3에서 필요한 키를 포함하기 위해 Build process에 단계를 추가했습니다.
Berksfile
Berksfile내에서, cookbooks/디렉토리에 포함된 모든 cookbook은 다음과 같은 형식으로 참조되어야 합니다. 이는 것이 해당 위치의 저장소에서 찾을 수 있다는 것을 Berkshelf에게 알려 줄 것입니다.
Chef Supermarket에서 가져온 Cookbook은 일반적으로 포함될 수 있습니다.
buildspec.yml
buildspec.yml파일은 CodeBuild에 종속성을 다운로드하고 각 Chef Automate 인스턴스로 cookbook을 업로드하기 위한 지침을 제공합니다. Berks명령을 사용하려면 빌드 프로세스 중에 ChefDK를 설치해야 합니다. 이 예에서는 설치 패키지를 Chef.io.에서 다운로드합니다. 또는 ChefDK를 패키징 하여 사용자 지정 빌드 환경에 미리 설치할 수 있으므로 빌드 시간을 줄일 수 있습니다. 다음 예와 같이 빌드 프로세스의 주요 단계는 다음과 같습니다.
1. ChefDK를 다운로드하여 설치합니다.
2. S3에서 자동으로 인스턴스 두개를 인증하는 개인 키를 복사합니다.
3. Berks install을 실행하여 종속성을 다운로드하고 설치합니다.
4. 두 Chef Automate 인스턴스에 cookbook을 업로드하세요.
[YAML]
version: 0.2
phases:
install:
commands:
– “wget https://packages.chef.io/files/stable/chefdk/1.5.0/ubuntu/14.04/chefdk_1.5.0-1_amd64.deb”
– “dpkg -i ./chefdk_1.5.0-1_amd64.deb”
build:
commands:
– “aws s3 cp s3://[KEY_BUCKET]/[CHEF_SERVER_1]/private.pem ./.chef/[CHEF_SERVER_1]/private.pem”
– “aws s3 cp s3://[KEY_BUCKET]/[CHEF_SERVER_2/private.pem ./.chef/[CHEF_SERVER_2]/private.pem”
– “CHEF_SERVER=[CHEF_SERVER_1] berks install”
– “CHEF_SERVER=[CHEF_SERVER_2] berks install”
– “CHEF_SERVER=[CHEF_SERVER_1] berks upload –no-ssl-verify”
– “CHEF_SERVER=[CHEF_SERVER_2] berks upload –no-ssl-verify”
post_build:
commands:
– “echo ‘Complete'”
이 빌드 사양은 파이프 라인 구축 단계 중에 CodeBuild에 의해 제공됩니다.
[Json]
{
“Resources”: {
“BuildProject”: {
“Type”: “AWS::CodeBuild::Project”,
“Properties”: {
“Artifacts”: { “Type”: “CODEPIPELINE” },
“Description”: “Installs cookbook dependencies from Berksfile and uploads to one or more OWCA servers.”,
“Environment”: {
“ComputeType”: “BUILD_GENERAL1_SMALL”,
“Image”: “aws/codebuild/ubuntu-base:14.04”,
“Type”: “LINUX_CONTAINER”
},
“ServiceRole”: {
“Fn::GetAtt”: [ “BuildRole”, “Arn” ]
},
“Source”: { “Type”: “CODEPIPELINE” }
}
}
}
}
Lambda 기능
Lambda기능(ChefClientfunction)은 Boto3을 통해 AWS Systems Manager를 사용하여 기능 코드에 제공된 인스턴스 목록에 sudo chef-client를 호출합니다. 아래의 예에서는 목록에서 전달되는 두 개의 인스턴스 ID를 사용합니다. 그러나 Boto3은 태그 또는 기타 관련 속성별로 노드 목록을 생성하는 데 추가로 활용할 수 있습니다. 이것은 독자들을 위한 연습장으로 남겨놓았습니다.
[Json]
{
“Resources”: {
“ChefClientFunction”: {
“Type”: “AWS::Lambda::Function”,
“Properties”: {
“Code”: {
“ZipFile”: {
“Fn::Join”: [ “\n”, [
“from __future__ import print_function”,
“”,
“import boto3”,
“”,
“ssm = boto3.client(‘ssm’)”,
“code_pipeline = boto3.client(‘codepipeline’)”,
“”,
“def lambda_handler(event,context):”,
” job_id = event[‘CodePipeline.job’][‘id’]”,
“”,
” try:”,
” response = ssm.send_command(“,
” InstanceIds=[“,
” ‘[INSTANCE_ID_1]’,”,
” ‘[INSTANCE_ID_2]'”,
” ],”,
” DocumentName=’AWS-RunShellScript’,”,
” Comment=’chef-client’,”,
” Parameters={“,
” ‘commands’: [‘sudo chef-client’]”,
” }”,
” )”,
“”,
” command_id = response[‘Command’][‘CommandId’]”,
” print(‘SSM Command ID: ‘ + command_id)”,
” print(‘Command Status: ‘ + response[‘Command’][‘Status’])”,
“”,
” # Include monitoring of job success/failure as needed.”,
“”,
” code_pipeline.put_job_success_result(jobId=job_id)”,
“”,
” return”,
” except Exception as e:”,
” print(e)”,
“”,
” code_pipeline.put_job_failure_result(jobId=job_id, failureDetails={‘message’: e.message, ‘type’: ‘JobFailed’})”,
“”,
” raise e”
] ]
}
},
“Description”: “Executes chef-client on specified nodes.”,
“Handler”: “index.lambda_handler”,
“Role”: {
“Fn::GetAtt”: [ “FunctionRole”, “Arn” ]
},
“Runtime”: “python2.7”,
“Timeout”: “10”
}
}
}
}
Pipeline
마지막으로, 파이프 라인이 배포될 때 각 구성 요소를 사용하여 두 Chef Automate 서버에 동시에 배포합니다.
[Json]
{
“Resources”: {
“OWCAPipeline”: {
“Type”: “AWS::CodePipeline::Pipeline”,
“Properties”: {
“ArtifactStore”: {
“Location”: { “Ref”: “ArtifactBucket” },
“Type”: “S3”
},
“RoleArn”: {
“Fn::Join”: [ “”, [
“arn:aws:iam::”,
{ “Ref”: “AWS::AccountId” },
“:role/AWS-CodePipeline-Service”
] ]
},
“Stages”: [
{
“Actions”: [
{
“ActionTypeId”: {
“Category”: “Source”,
“Owner”: “AWS”,
“Provider”: “CodeCommit”,
“Version”: “1”
},
“Configuration”: {
“BranchName”: “master”,
“RepositoryName”: {
“Fn::GetAtt”: [ “Repo”, “Name” ]
}
},
“Name”: “ChefRepo”,
“OutputArtifacts”: [
{ “Name”: “Cookbooks” }
]
}
],
“Name”: “Source”
},
{
“Actions”: [
{
“ActionTypeId”: {
“Category”: “Build”,
“Owner”: “AWS”,
“Provider”: “CodeBuild”,
“Version”: “1”
},
“Configuration”: {
“ProjectName”: { “Ref”: “BuildProject” }
},
“InputArtifacts”: [
{ “Name”: “Cookbooks” }
],
“Name”: “Berkshelf”
}
],
“Name”: “Build”
},
{
“Actions”: [
{
“ActionTypeId”: {
“Category”: “Invoke”,
“Owner”: “AWS”,
“Provider”: “Lambda”,
“Version”: “1”
},
“Configuration”: {
“FunctionName”: { “Ref”: “ChefClientFunction” }
},
“Name”: “LambdaChefClient”
}
],
“Name”: “Invoke”
}
]
}
}
}
}
요약
Chef Automate 인스턴스를 위한 여러 OpsWorks에 노드를 배포함으로써 인스턴스당 평균 워크 로드를 줄일 수 있으므로, 인프라의 확장에 따라 훨씬 더 많은 인스턴스를 관리할 수 있습니다. 여러 AWS서비스를 사용하면 서버, 지역 및 AWS계정 전반에서 cookbook의 일관성과 관리 용이성을 보장할 수 있습니다.
원문 URL: https://aws.amazon.com/ko/blogs/mt/distributing-your-aws-opsworks-for-chef-automate-infrastructure/
** 메가존 TechBlog는 AWS BLOG 영문 게재글중에서 한국 사용자들에게 유용한 정보 및 콘텐츠를 우선적으로 번역하여 내부 엔지니어 검수를 받아서, 정기적으로 게재하고 있습니다. 추가로 번역및 게재를 희망하는 글에 대해서 관리자에게 메일 또는 SNS페이지에 댓글을 남겨주시면, 우선적으로 번역해서 전달해드리도록 하겠습니다.