파트 3 - 간단한 EC2 인스턴스 - Awesome AWS CDK
Structure and Setup of a CDK application
Testing the stack
소개
Most tutorials on AWS try to get you to deploy the world famous t2.micro
instance (a small virtual server under the free usage tier on AWS) using the AWS console. You then go on to install something like Wordpress through SSH or through a user script (a script that runs when an instance is created) that you define in the AWS console.
So that's exactly what we're going to deploy. But we're going to use the AWS CDK instead.
This is what you'll learn in this tutorial:
- Bootstrapping a new CDK application
- The structure and setup of a CDK application
- How to provision and setup an ec2 instance
- How to setup terminal access to the instance via SSH key.
- How to update the ec2 instance with a user script and update the deployed cdk stack
- How to write tests for the cdk application
- How to destroy the deployed stack
Let's go!
새 CDK 애플리케이션 부트스트랩
Using your terminal, create a new directory simple-ec2
and cd into it:
mkdir simple-ec2 && cd simple-ec2
We previously setup the AWS CDK cli globally. If you haven't done that see in this series.
Bootstrap a new cdk project template that uses Typescript:
cdk init --language=typescript
This will initialize a new cdk project for you in Typescript.
- Run
npm update
to ensure you're using the latest version of the CDK. - If you have version conflicts between
@aws-cdk/core
and other@aws-cdk
sub-packages, then you'll come across some weird errors.@aws-cdk/core
and every other imported@aws-cdk/PACKAGE
should have the same version.
CDK 애플리케이션의 구조 및 설정
구조
# tree -I 'node_modules'
.
├── bin
│ └── simple-ec2.ts # entry point
├── cdk.json
├── jest.config.js # for tests
├── lib # where the infrastructure code you write will go
│ └── simple-ec2-stack.ts
├── package.json
├── package-lock.json
├── README.md
├── test # test folder
│ └── simple-ec2.test.ts
└── tsconfig.json
-
./bin/simple-ec2.ts
is the entry point file used by the cdk. This is where you define your stack(s). - The IaC that provisions the resources will be inside the
lib
folder and is required by./bin/simple-ec2.ts
duringsynth
anddeploy
actions. I'll explain both commands later. -
./test/simple-ec2.test.ts
contains the template code to test your CDK application
설정
In ./bin/simple-ec2.ts
a new App()
is defined and this represents a single stack.
- We can add a description for our stack in this file.
- This description will be visible in the Cloudformation console:
// ./bin/simple-ec2.ts
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { SimpleEc2Stack } from '../lib/simple-ec2-stack';
const app = new cdk.App();
new SimpleEc2Stack(app, 'SimpleEc2Stack', {
description: 'This is a simple EC2 stack'
});
Let's head over to ./lib/simple-ec2-stack.ts
and see what the CDK boostrapped for us:
// ./lib/simple-ec2-stack.ts
import * as cdk from '@aws-cdk/core';
export class SimpleEc2Stack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// The code that defines your stack goes here
}
}
As you can see, the cdk init
command bootstrapped a nice template for us to get to writing our IaC fast. The base class cdk.Stack
gives us the ability to create a new Cloudformation stack.
We also need to create a .env
file to keep our AWS Account number and the region we will use. In the root of the project create a file called .env
and add the following. Replace the xxXxXxxXXxXxx
with your AWS account number and use the region you want.
AWS_ACCOUNT_NUMBER=xxXxXxxXXxXxx
AWS_ACCOUNT_REGION=us-west-2
인프라 코드 작성
We want to create an ec2 instance so we will need the @aws-cdk/ec2
library. (This is the same process for any other AWS service you need to provision resources). We will also need @aws-cdk/aws-iam
library to give permissions to our instance to do stuff. We also want to be able to read our .env
file so lets also install dotenv
package
npm install @aws-cdk/aws-ec2 @aws-cdk/aws-iam dotenv
Remember, since the CDK is written in Typescript and is typed excellently, while typing you can access intellisense and see the various properties of CDK resources e.g. in the image below I can see what properties an instance of ec2.Instance()
class has.
첫 번째 반복은 다음과 같습니다.
import * as cdk from '@aws-cdk/core'
import * as ec2 from '@aws-cdk/aws-ec2' // import ec2 library
import * as iam from '@aws-cdk/aws-iam' // import iam library for permissions
require('dotenv').config()
const config = {
env: {
account: process.env.AWS_ACCOUNT_NUMBER,
region: process.env.AWS_REGION
}
}
export class SimpleEc2Stack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
// its important to add our env config here otherwise CDK won't know our AWS account number
super(scope, id, { ...props, env: config.env })
// Get the default VPC. This is the network where your instance will be provisioned
// All activated regions in AWS have a default vpc.
// You can create your own of course as well. https://aws.amazon.com/vpc/
const defaultVpc = ec2.Vpc.fromLookup(this, 'VPC', { isDefault: true })
// Lets create a role for the instance
// You can attach permissions to a role and determine what your
// instance can or can not do
const role = new iam.Role(
this,
'simple-instance-1-role', // this is a unique id that will represent this resource in a Cloudformation template
{ assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') }
)
// lets create a security group for our instance
// A security group acts as a virtual firewall for your instance to control inbound and outbound traffic.
const securityGroup = new ec2.SecurityGroup(
this,
'simple-instance-1-sg',
{
vpc: defaultVpc,
allowAllOutbound: true, // will let your instance send outboud traffic
securityGroupName: 'simple-instance-1-sg',
}
)
// lets use the security group to allow inbound traffic on specific ports
securityGroup.addIngressRule(
ec2.Peer.anyIpv4(),
ec2.Port.tcp(22),
'Allows SSH access from Internet'
)
securityGroup.addIngressRule(
ec2.Peer.anyIpv4(),
ec2.Port.tcp(80),
'Allows HTTP access from Internet'
)
securityGroup.addIngressRule(
ec2.Peer.anyIpv4(),
ec2.Port.tcp(443),
'Allows HTTPS access from Internet'
)
// Finally lets provision our ec2 instance
const instance = new ec2.Instance(this, 'simple-instance-1', {
vpc: defaultVpc,
role: role,
securityGroup: securityGroup,
instanceName: 'simple-instance-1',
instanceType: ec2.InstanceType.of( // t2.micro has free tier usage in aws
ec2.InstanceClass.T2,
ec2.InstanceSize.MICRO
),
machineImage: ec2.MachineImage.latestAmazonLinux({
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
}),
keyName: 'simple-instance-1-key', // we will create this in the console before we deploy
})
// cdk lets us output prperties of the resources we create after they are created
// we want the ip address of this new instance so we can ssh into it later
new cdk.CfnOutput(this, 'simple-instance-1-output', {
value: instance.instancePublicIp
})
}
}
AWS 콘솔에서 새 키 페어 생성
Before we try to deploy our newly created instance, we need to go to the AWS console and create a key pair that we will use to access the instance called simple-instance-1-key
- Log into the AWS console.
- Go to
EC2
dashboard. - Go to Key Pairs and click create Key Pair
- Enter key name as
simple-instance-1-key
and click create - Your new key pair will be created and your browser will automatically download a new
.pem
file calledsimple-instance-1-key.pem
- this is the key file you'll use to gain access to your instance via SSH
.aws/
라는 pems
라는 새 디렉토리를 만듭니다.mkdir ~./aws/pems/
mv ~/Downloads/simple-instance-1-key.pem ~/.aws/pems
# important step or your key file won't work
chmod 400 ~/.aws/pems/simple-instance-1-key.pem
배포
Remember we set up our aws profiles and crednetials in ~/.aws/config
and ~/.aws/credentials
back in
I will be deploying to my default
profile which is linked to my personal AWS account with the region us-west-2
.
If you have another profile you want to use then in the commands below use that profile name instead of default
.
In your terminal:
cdk synth --profile default
This command will synthesize your stack.
When CDK apps are executed, they produce (or “synthesize”, in CDK parlance) an AWS CloudFormation template for each stack defined in your application.
- Essentially it will print the cloudformation template for your stack to your console.
- It's a good way to check that there's nothing wrong with your stack before trying to deploy since
cdk synth
will verify the resources you are trying to provision can actually be provisioned. - You should something like this:
❯ cdk synth --profile default
Resources:
simpleinstance1role9EEDA67C:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ec2.amazonaws.com
Version: "2012-10-17"
...
...
...
If the entire stack prints without error then you're okay to go.
Now we can deploy:
cdk deploy --profile default
y
and press ENTER
to continue with the deployment:스택이 생성되는 동안 콘솔에서 Cloudformation의 출력이 표시되기 시작합니다.
스택이 성공적으로 배포되면 다음이 표시되어야 합니다.
스택 끝에 정의한 출력을 확인하십시오. CDK는 새로 생성된 인스턴스의 퍼블릭 IP 주소를 인쇄했습니다. 스택이 성공적으로 배포되면 이 방법을 사용하여 모든 값을 출력할 수 있습니다.
또는 인스턴스 대시보드를 확인하여 ec2 콘솔에서 이 정보를 얻을 수 있습니다.
인스턴스에 액세스
Let's ssh into our newly created instance with our key file and the public ip address.
Note: that the default user for AMAZON LINUX images is ec2-user
ssh -i ~/.aws/pems/simple-instance-1-key.pem [email protected]
안타깝게도 아무 것도 실행하지 않는 인스턴스가 있습니다.
해결하자!
사용자 스크립트 추가
- Let's create a new file under
./lib/
directory calleduser_script.sh
. Paste this code into that file. - This code will deploy Apache, Wordpress, Mysql server on this intance. In this script, the database password will be
pl55w0rd
. - It's very insecure to add passwords to scripts but in this case I'm doing this just for demonstration purposes.
- In production you should first of all never use such a weak password and secondly, not inside such a script.
- Rather, you should deploy the Mysql database on AWS RDS and setup credentials for that database using AWS Secrets Manager.
Here's the setup file:
#! /bin/bash
# become root user
sudo su
# update dependencies
yum -y update
# we'll install 'expect' to input keystrokes/y/n/passwords
yum -y install expect
# Install Apache
yum -y install httpd
# Start Apache
service httpd start
# Install PHP
yum -y install php php-mysql
# php 7 needed for latest wordpress
amazon-linux-extras -y install php7.2
# Restart Apache
service httpd restart
# Install MySQL
wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm
rpm -ivh mysql-community-release-el7-5.noarch.rpm
yum -y update
yum -y install mysql-server
# Start MySQL
service mysqld start
# Create a database named blog
mysqladmin -uroot create blog
# Secure database
# non interactive mysql_secure_installation with a little help from expect.
SECURE_MYSQL=$(expect -c "
set timeout 10
spawn mysql_secure_installation
expect \"Enter current password for root (enter for none):\"
send \"\r\"
expect \"Change the root password?\"
send \"y\r\"
expect \"New password:\"
send \"pl55w0rd\r\"
expect \"Re-enter new password:\"
send \"pl55w0rd\r\"
expect \"Remove anonymous users?\"
send \"y\r\"
expect \"Disallow root login remotely?\"
send \"y\r\"
expect \"Remove test database and access to it?\"
send \"y\r\"
expect \"Reload privilege tables now?\"
send \"y\r\"
expect eof
")
echo "$SECURE_MYSQL"
# Change directory to web root
cd /var/www/html
# Download Wordpress
wget http://wordpress.org/latest.tar.gz
# Extract Wordpress
tar -xzvf latest.tar.gz
# Rename wordpress directory to blog
mv wordpress blog
# Change directory to blog
cd /var/www/html/blog/
# Create a WordPress config file
mv wp-config-sample.php wp-config.php
#set database details with perl find and replace
sed -i "s/database_name_here/blog/g" /var/www/html/blog/wp-config.php
sed -i "s/username_here/root/g" /var/www/html/blog/wp-config.php
sed -i "s/password_here/pl55w0rd/g" /var/www/html/blog/wp-config.php
# create uploads folder and set permissions
mkdir wp-content/uploads
chmod 777 wp-content/uploads
#remove wp file
rm -rf /var/www/html/latest.tar.gz
We will need to add fs
module at the top of our ./lib/simple-ec2-stack.ts
file since instance.addUserData()
needs to access the file system during deployment.
import * as cdk from '@aws-cdk/core'
import * as ec2 from '@aws-cdk/aws-ec2' // import ec2 library
import * as iam from '@aws-cdk/aws-iam' // import iam library for permissions
// lets include fs module
import * as fs from 'fs'
Then we can the function instance.addUserData()
right before our output function:
...
// add user script to instance
// this script runs when the instance is started
instance.addUserData(
fs.readFileSync('lib/user_script.sh', 'utf8')
)
// cdk lets us output prperties of the resources we create after they are created
// we want the ip address of this new instance so we can ssh into it later
new cdk.CfnOutput(this, 'simple-instance-1-output', {
value: instance.instancePublicIp
})
...
배포된 스택 업데이트
Let's re-synthesize to check everything is okay:
cdk --synth --profile default
- You should now see the user script commands in the
synth
output - Let's deploy our new changes.
- Cloudformation will only update resources that are being updated.
- In this case, only ec2 instance is being updated.
- Other things like roles and security groups will remain as they are since there are no changes to them in the updated stack.
cdk --deploy --profile default
참고: 탄력적 IP를 사용하지 않기 때문에 인스턴스의 퍼블릭 IP 주소가 변경되었을 가능성이 높습니다.
출력된 IP 주소를 사용하여 Wordpress, Mysql 및 PHP가 올바르게 설치되었는지 확인합니다.
root
및 pl55w0rd
을 기억하십시오.스택 테스트
Well we know our CDK code works and can provision an ec2 instance to run our Wordpress server and database. Good.
But how can we make sure that changes to the CDK code do not do what we don't want it to do?
This is where tests come in.
테스트 요구 사항
- I do not want any instance other
t2.micro
to be used as my server instance type because I always want to remain under AWS free tier usage for EC2. Let's ensure that. - I want to ensure that my instance uses the SSH key with the name
simple-instance-1-key
To accomplish this, we change the code inside the file ./test/simple-ec2.test.ts
to:
import { expect as expectCDK, haveResourceLike } from '@aws-cdk/assert';
import * as cdk from '@aws-cdk/core';
import * as SimpleEc2 from '../lib/simple-ec2-stack';
test('Check InstanceType and SSH KeyName', () => {
const app = new cdk.App();
const stack = new SimpleEc2.SimpleEc2Stack(app, 'MyTestStack');
expectCDK(stack).to(
haveResourceLike('AWS::EC2::Instance', {
InstanceType: 't2.micro',
KeyName: 'simple-instance-1-key'
})
)
});
As you can see from the test code, the test will check the generated Cloudformation template generated by the CDK.
In our case we want to check that the instance is a t2.micro
and that it uses the SSH key simple-instance-1-key
. These are two crucial properties to us.
테스트 실행:
npm test
문제 없다!
이제 인프라를 배포하기 전에 코드에서 테스트를 실행할 수 있어야 합니다! 환상적입니다!
npm test && cdk deploy --profile default
스택 파괴
If you would like to destroy the infrastructure you just provisioned, it's as simple as:
cdk destroy --profile default
And Cloudformation will remove your entire stack!
메모:
결론
The AWS CDK makes writing IaC, provisioning, deploying, updating and destroying infrastructure very painless. You can write tests to make sure you do not deploy the wrong things.
This was a simple example and may seem quite a lot just to deploy an ec2 instance. However, as we progress through the series, you will realize how its very beneficial to complex infrastructure.
다음
In part 4, using the CDK, we will make our Wordpress server more more production ready. We will:
- create AWS RDS Mysql database instead of running the database on the ec2 instance
- provision this database in an isolated subnet to keep it secure from the public Internet
- use AWS SSM to access our instance instead of an SSH key and gain all the benefits of IAM permissions/roles
- deploy an Application Load Balancer
- Create our EC2 instance with better/more advanced script
- Place the Wordpress instance in an AutoScaling Group
Hi I'm Emmanuel ! 저는 소프트웨어 및 DevOps에 대해 글을 씁니다.
이 기사가 마음에 들었고 더 보고 싶다면 저를 추가하거나 저를 팔로우하세요.
Reference
이 문제에 관하여(파트 3 - 간단한 EC2 인스턴스 - Awesome AWS CDK), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/emmanuelnk/part-3-simple-ec2-instance-awesome-aws-cdk-37ia텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)