kOps로 AWS에 K8S 배포하기
by softPine가시다님과 함께하는 PKOS 1주차 스터디.
Kops 설치 방법(EC2 Instance)
- OS
- Amazon Linux 2
curl -LO <https://github.com/kubernetes/kops/releases/download/$>(curl -s <https://api.github.com/repos/kubernetes/kops/releases/latest> | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
chmod +x kops-linux-amd64
mv kops-linux-amd64 /usr/local/bin/kops
Route53 도메인 설정
- Route53에서 서브도메인을 지원하기에 서브도메인을 이용하여 생성한다.
- 생성 후 나오는 NameServers의 값을 저장한다.
aws route53 create-hosted-zone --name kops.domain.co.kr --caller-reference 1
---
{
"Location": "<https://route53.amazonaws.com/2013-04-01/hostedzone/Z0801636DLEL2UTQFMB6>",
"HostedZone": {
"Id": "/hostedzone/Z0801636DLEL2UTQFMB6",
"Name": "kops.domain.co.kr.",
"CallerReference": "1",
"Config": {
"PrivateZone": false
},
"ResourceRecordSetCount": 2
},
"ChangeInfo": {
"Id": "/change/C00464822MVNRLF9UM031",
"Status": "PENDING",
"SubmittedAt": "2023-03-06T03:01:44.432000+00:00"
},
"DelegationSet": {
"NameServers": [
"ns-451.awsdns-56.com",
"ns-1671.awsdns-16.co.uk",
"ns-1239.awsdns-26.org",
"ns-536.awsdns-03.net"
]
}
}
- 상위 도메인인 domain.co.kr에서 서브도메인으로 라우팅을 할 수 있도록 Record를 서브도메인 명으로 생성한다.
- aws cli로 간단하게 처리를 하기 위해 아래 JSON 파일을 생성한다.
{
"Comment": "CREATE/DELETE/UPSERT a record ",
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "kops.domain.co.kr",
"Type": "NS",
"TTL": 172800,
"ResourceRecords": [
{ "Value": "ns-451.awsdns-56.com"},
{ "Value": "ns-1671.awsdns-16.co.uk"},
{ "Value": "ns-1239.awsdns-26.org"},
{ "Value": "ns-536.awsdns-03.net"}
]
}}]
}
- 위와 같은 json 파일 생성 후 아래 명령어로 서브도메인을 생성한다.
aws route53 change-resource-record-sets --hosted-zone-id Z14NHEMS72ESY4 --change-batch file://Record.json
---
{
"ChangeInfo": {
"Id": "/change/C00498933LQH5QIAAL9R",
"Status": "PENDING",
"SubmittedAt": "2023-03-06T03:18:17.965000+00:00",
"Comment": "CREATE/DELETE/UPSERT a record "
}
}
- JSON 파일에 오류가 없는 한 Pending Status가 고유 ID와 함께 반환되어야 한다.
- 고유 ID를 포함한 아래 명령어로 Route53의 전파 상태를 확인한다. INSYNC는 변경 내용이 Route53 DNS 서버에 전파 되었음을 나타낸다.
aws route53 get-change --id /change/C00498933LQH5QIAAL9R
---
{
"ChangeInfo": {
"Id": "/change/C00498933LQH5QIAAL9R",
"Status": "INSYNC",
"SubmittedAt": "2023-03-06T03:18:17.965000+00:00",
"Comment": "CREATE/DELETE/UPSERT a record "
}
}
- INSYNC 상태가 보인다면 아래 명령어로 정상적으로 NameServer가 질의되는 것을 확인하면 된다.
dig ns kops.domain.co.kr
---
; <<>> DiG 9.16.1-Ubuntu <<>> ns kops.domain.co.kr
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3571
;; flags: qr rd ad; QUERY: 1, ANSWER: 12, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;kops.domain.co.kr. IN NS
;; ANSWER SECTION:
kops.domain.co.kr. 0 IN NS ns-1239.awsdns-26.org.
kops.domain.co.kr. 0 IN NS ns-1671.awsdns-16.co.uk.
kops.domain.co.kr. 0 IN NS ns-451.awsdns-56.com.
kops.domain.co.kr. 0 IN NS ns-536.awsdns-03.net.
ns-1239.awsdns-26.org. 0 IN A 205.251.196.215
ns-1239.awsdns-26.org. 0 IN AAAA 2600:9000:5304:d700::1
ns-1671.awsdns-16.co.uk. 0 IN A 205.251.198.135
ns-1671.awsdns-16.co.uk. 0 IN AAAA 2600:9000:5306:8700::1
ns-451.awsdns-56.com. 0 IN A 205.251.193.195
ns-451.awsdns-56.com. 0 IN AAAA 2600:9000:5301:c300::1
ns-536.awsdns-03.net. 0 IN A 205.251.194.24
ns-536.awsdns-03.net. 0 IN AAAA 2600:9000:5302:1800::1
;; Query time: 0 msec
;; SERVER: 172.29.192.1#53(172.29.192.1)
;; WHEN: Mon Mar 06 12:33:49 KST 2023
;; MSG SIZE rcvd: 452
클러스터 저장용 S3 버킷 생성
- Kops는 클러스터 설치 이후에도 클러스터를 관리할 수 있는데, 이를 위해 클러스터의 상태나 사용하는 키 정보들을 지속적으로 추적해야 하는데 이 정보를 S3에 저장할 수 있다.
- 다수의 클러스터에서 동일한 S3 버킷을 사용할 수 있고, 사용자는 이 S3 버킷을 같은 클러스터를 운영하는 운영자에게 공유할 수 있다.
- 하지만 이 S3 버킷에 접근 가능한 사람은 모든 클러스터에 관리자 권한으로 접근이 가능하게되니, 운영팀 이외에 공유되지 않도록 해야한다.
- 저는 Kops 운영 인스턴스를 AWS에 생성을 해두었기에 Instance Profile로 설정하여 사용하기로 했습니다.
S3 Bucket 생성
S3 Bucket 생성
aws s3api create-bucket --bucket cluster.kops.domain.co.kr \\
--region ap-northeast-2 \\
--object-ownership BucketOwnerEnforced \\
--create-bucket-configuration LocationConstraint=ap-northeast-2 \\
--versioning-configuration Status=Enable
S3 Bucket Public Access Block 설정
aws s3api put-public-access-block --bucket cluster.kops.domain.co.kr \\
--public-access-block-configuration \\
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
S3 Bucket Policy 설정
- KopsS3-Bucket-Policy.json
{
"Id": "Policy1678159299540",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1678159297382",
"Action": [
"s3:*",
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::cluster.kops.domain.co.kr/*",
"Principal": {
"AWS": [
"arn:aws:iam::123456789012:role/domain-SKR-KOPS-role"
]
}
}
]
}
- Bucket Policy 설정
aws s3api put-bucket-policy --bucket cluster.kops.domain.co.kr --policy file://KopsS3-Bucket-Policy.json
EC2 Instance Profile 설정
IAM Instance Profile Trust 파일 생성
- KopsS3-EC2-Trust.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
IAM Instance Profile Permission 파일 생성
- KopsS3-EC2-Permission.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EventBridgeActions",
"Effect": "Allow",
"Action": [
"events:*",
"schemas:*",
"scheduler:*",
"pipes:*"
],
"Resource": "*"
},
{
"Sid": "IAMCreateServiceLinkedRoleForApiDestinations",
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam::*:role/aws-service-role/AmazonEventBridgeApiDestinationsServiceRolePolicy",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": "apidestinations.events.amazonaws.com"
}
}
},
{
"Sid": "IAMCreateServiceLinkedRoleForAmazonEventBridgeSchemas",
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam::*:role/aws-service-role/schemas.amazonaws.com/AWSServiceRoleForSchemas",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": "schemas.amazonaws.com"
}
}
},
{
"Sid": "SecretsManagerAccessForApiDestinations",
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:UpdateSecret",
"secretsmanager:DeleteSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue"
],
"Resource": "arn:aws:secretsmanager:*:*:secret:events!*"
},
{
"Sid": "IAMPassRoleAccessForEventBridge",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/*",
"Condition": {
"StringLike": {
"iam:PassedToService": "events.amazonaws.com"
}
}
},
{
"Sid": "IAMPassRoleAccessForScheduler",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/*",
"Condition": {
"StringLike": {
"iam:PassedToService": "scheduler.amazonaws.com"
}
}
},
{
"Sid": "IAMPassRoleAccessForPipes",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/*",
"Condition": {
"StringLike": {
"iam:PassedToService": "pipes.amazonaws.com"
}
}
},
{
"Action": [
"sqs:*"
],
"Effect": "Allow",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:AcceptVpcPeeringConnection",
"ec2:AcceptVpcEndpointConnections",
"ec2:AllocateAddress",
"ec2:AssignIpv6Addresses",
"ec2:AssignPrivateIpAddresses",
"ec2:AssociateAddress",
"ec2:AssociateDhcpOptions",
"ec2:AssociateRouteTable",
"ec2:AssociateSubnetCidrBlock",
"ec2:AssociateVpcCidrBlock",
"ec2:AttachClassicLinkVpc",
"ec2:AttachInternetGateway",
"ec2:AttachNetworkInterface",
"ec2:AttachVpnGateway",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateCarrierGateway",
"ec2:CreateCustomerGateway",
"ec2:CreateDefaultSubnet",
"ec2:CreateDefaultVpc",
"ec2:CreateDhcpOptions",
"ec2:CreateEgressOnlyInternetGateway",
"ec2:CreateFlowLogs",
"ec2:CreateInternetGateway",
"ec2:CreateLocalGatewayRouteTableVpcAssociation",
"ec2:CreateNatGateway",
"ec2:CreateNetworkAcl",
"ec2:CreateNetworkAclEntry",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:CreateVpcEndpoint",
"ec2:CreateVpcEndpointConnectionNotification",
"ec2:CreateVpcEndpointServiceConfiguration",
"ec2:CreateVpcPeeringConnection",
"ec2:CreateVpnConnection",
"ec2:CreateVpnConnectionRoute",
"ec2:CreateVpnGateway",
"ec2:DeleteCarrierGateway",
"ec2:DeleteCustomerGateway",
"ec2:DeleteDhcpOptions",
"ec2:DeleteEgressOnlyInternetGateway",
"ec2:DeleteFlowLogs",
"ec2:DeleteInternetGateway",
"ec2:DeleteLocalGatewayRouteTableVpcAssociation",
"ec2:DeleteNatGateway",
"ec2:DeleteNetworkAcl",
"ec2:DeleteNetworkAclEntry",
"ec2:DeleteNetworkInterface",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVpc",
"ec2:DeleteVpcEndpoints",
"ec2:DeleteVpcEndpointConnectionNotifications",
"ec2:DeleteVpcEndpointServiceConfigurations",
"ec2:DeleteVpcPeeringConnection",
"ec2:DeleteVpnConnection",
"ec2:DeleteVpnConnectionRoute",
"ec2:DeleteVpnGateway",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeCarrierGateways",
"ec2:DescribeClassicLinkInstances",
"ec2:DescribeCustomerGateways",
"ec2:DescribeDhcpOptions",
"ec2:DescribeEgressOnlyInternetGateways",
"ec2:DescribeFlowLogs",
"ec2:DescribeInstances",
"ec2:DescribeInternetGateways",
"ec2:DescribeIpv6Pools",
"ec2:DescribeLocalGatewayRouteTables",
"ec2:DescribeLocalGatewayRouteTableVpcAssociations",
"ec2:DescribeKeyPairs",
"ec2:DescribeMovingAddresses",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkAcls",
"ec2:DescribeNetworkInterfaceAttribute",
"ec2:DescribeNetworkInterfacePermissions",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribePrefixLists",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupReferences",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeStaleSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:DescribeVpcEndpointConnectionNotifications",
"ec2:DescribeVpcEndpointConnections",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcEndpointServiceConfigurations",
"ec2:DescribeVpcEndpointServicePermissions",
"ec2:DescribeVpcEndpointServices",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeVpcs",
"ec2:DescribeVpnConnections",
"ec2:DescribeVpnGateways",
"ec2:DetachClassicLinkVpc",
"ec2:DetachInternetGateway",
"ec2:DetachNetworkInterface",
"ec2:DetachVpnGateway",
"ec2:DisableVgwRoutePropagation",
"ec2:DisableVpcClassicLink",
"ec2:DisableVpcClassicLinkDnsSupport",
"ec2:DisassociateAddress",
"ec2:DisassociateRouteTable",
"ec2:DisassociateSubnetCidrBlock",
"ec2:DisassociateVpcCidrBlock",
"ec2:EnableVgwRoutePropagation",
"ec2:EnableVpcClassicLink",
"ec2:EnableVpcClassicLinkDnsSupport",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:ModifySecurityGroupRules",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:ModifyVpcEndpoint",
"ec2:ModifyVpcEndpointConnectionNotification",
"ec2:ModifyVpcEndpointServiceConfiguration",
"ec2:ModifyVpcEndpointServicePermissions",
"ec2:ModifyVpcPeeringConnectionOptions",
"ec2:ModifyVpcTenancy",
"ec2:MoveAddressToVpc",
"ec2:RejectVpcEndpointConnections",
"ec2:RejectVpcPeeringConnection",
"ec2:ReleaseAddress",
"ec2:ReplaceNetworkAclAssociation",
"ec2:ReplaceNetworkAclEntry",
"ec2:ReplaceRoute",
"ec2:ReplaceRouteTableAssociation",
"ec2:ResetNetworkInterfaceAttribute",
"ec2:RestoreAddressToClassic",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:UnassignIpv6Addresses",
"ec2:UnassignPrivateIpAddresses",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:*",
"organizations:DescribeAccount",
"organizations:DescribeOrganization",
"organizations:DescribeOrganizationalUnit",
"organizations:DescribePolicy",
"organizations:ListChildren",
"organizations:ListParents",
"organizations:ListPoliciesForTarget",
"organizations:ListRoots",
"organizations:ListPolicies",
"organizations:ListTargetsForPolicy"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:*",
"s3-object-lambda:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"route53:*",
"route53domains:*",
"cloudfront:ListDistributions",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticbeanstalk:DescribeEnvironments",
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:GetBucketWebsite",
"ec2:DescribeVpcs",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeRegions",
"sns:ListTopics",
"sns:ListSubscriptionsByTopic",
"cloudwatch:DescribeAlarms",
"cloudwatch:GetMetricStatistics"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "apigateway:GET",
"Resource": "arn:aws:apigateway:*::/domainnames"
},
{
"Action": "ec2:*",
"Effect": "Allow",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "elasticloadbalancing:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "cloudwatch:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "autoscaling:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": [
"autoscaling.amazonaws.com",
"ec2scheduled.amazonaws.com",
"elasticloadbalancing.amazonaws.com",
"spot.amazonaws.com",
"spotfleet.amazonaws.com",
"transitgateway.amazonaws.com"
]
}
}
}
]
}
IAM Role 및 Instnace Profile 생성
aws iam create-role --role-name domain-SKR-KOPS-role --assume-role-policy-document file://KopsS3-EC2-Trust.json
aws iam put-role-policy --role-name domain-SKR-KOPS-role --policy-name domain-SKR-KOPS-policy --policy-document file://KopsS3-EC2-Permission.json
aws iam create-instance-profile --instance-profile-name domain-SKR-KOPS-instance-profile
aws iam add-role-to-instance-profile --instance-profile-name domain-SKR-KOPS-instance-profile --role-name domain-SKR-KOPS-role
EC2 Instance에 Instance Profile 연결
# Instance ID를 meta-data로 확인
curl http://169.254.169.254/latest/meta-data/instance-id
# InstanceID를 갖고 Role 설정
aws ec2 associate-iam-instance-profile --instance-id i-0f5816da773b2efeb --iam-instance-profile Name=domain-SKR-KOPS-instance-profile
K8S Cluster 생성
kubectl 설치(EC2 Instance)
curl -LO "<https://dl.k8s.io/release/$>(curl -L -s <https://dl.k8s.io/release/stable.txt>)/bin/linux/amd64/kubectl"
curl -LO "<https://dl.k8s.io/$>(curl -L -s <https://dl.k8s.io/release/stable.txt>)/bin/linux/amd64/kubectl.sha256"
echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client --output=yaml
rm -rf ./kubectl*
ssh key 생성
ssh-keygen
---
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:0yGvi5rMwH77upu8l6tk3r66iD/ImnqcnOS72hlxwCo root@SKRI-Kops-01
The key's randomart image is:
+---[RSA 2048]----+
| |
| . |
| o . . |
| . . + . |
|E . . S o |
|...o o |
|.*+oo .. |
|.*O%.+o. . |
|O+O=^#Bo. |
+----[SHA256]-----+
환경 변수 설정
- cluster 이름(이전에 생성한 kops.domain.co.kr 도메인)과 s3명을 입력한다.
echo export KOPS_CLUSTER_NAME=kops.domain.co.kr >> ~/.bashrc
echo export KOPS_STATE_STORE=s3://cluster.kops.domain.co.kr >> ~/.bashrc
source ~/.bashrc
클러스터 생성
kops create cluster \\
--name=${KOPS_CLUSTER_NAME} \\
--cloud=aws \\
--networking amazonvpc \\
--zones=ap-northeast-2a,ap-northeast-2c \\
--network-cidr 172.30.0.0/16 \\
--master-size t3.medium \\
--node-size t3.medium \\
--node-count=2 \\
--kubernetes-version "1.24.10" \\
--ssh-public-key ~/.ssh/id_rsa.pub -y
생성된 클러스터 확인
source <(kubectl completion bash)
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -F __start_kubectl k' >> ~/.bashrc
source ~/.bashrc
k get no
---
NAME STATUS ROLES AGE VERSION
i-025ff97f4a34dd7fa Ready node 19m v1.24.10
i-0c69a38808d05a0d7 Ready control-plane 20m v1.24.10
i-0e37ceecc596e40da Ready node 19m v1.24.10
블로그의 정보
나의 삽질저장소
softPine