在迎接新冠连续假期之际

mediba Advent Calendar 2020 5日目を担当させていただく t-kusakabeです。がんばります。お願いします。
medibaには10月からお世話になっており、SRE UNIT兼スマートパスというプロダクトを担当させていただいております。

今年も早いもので気づけばアドベントカレンダーの季節です。
カレンダーを見てみると28日を挟んで正月休み。有給を使ってクリスマスが終われば仕事納めだ!という方も少なくないのではないしょうか。

本來是一個期待著聖誕節的活動,但連假也是令人期待不已的啊!不過,這樣一來就會擔心疫情的問題了。今年因為緊急事態宣言和非必要不急的限制,外出的機會相對減少了很多。而且不禁讓人擔心第三波疫情的出現。

そのため自宅でも楽しめる何かが必要ですよね。
自宅で無限に遊べるものと言えばゲームです。
今年もたくさん時間が溶けるタイトルが発表されました。
おそらくこの記事が公開されるときにも、あつ森や桃鉄などを夜通し楽しまれている方もいることでしょう。

提到可以無限遊玩的遊戲中,有一款遊戲絕對不能忘記。
是的,那就是Minecraft。

image.png

大家也许都玩过一次吧。比如挖掘或者PvP战斗,能够有很多种不同的玩法,非常有趣!

というわけで今回は、連休を楽しく過ごすためにMinecraft環境を構築してみましょう!

使用技術

    • ECS(Fargate)

 

    CDK

僕が担当させていただいてるプロダクトではEKSやTerraformを使っていますが、たまには違った技術をということでECSとCDKでやっていこうと思います。
(ほんとはGCPでやりたかったけど、CDKがよかったのでAWSにしました)

你在担心费用吗?别担心,有奖金和红包。

构建环境

首先我们来做准备吧。

请事先安装Node.js。

首先准备一个工作目录。

mkdir advent-calendar2020
cd advent-calendar2020

我也会安装CDK。

npm i -g aws-cdk

为了创建一个雏形,我将尝试进行初始化。
这次我打算用 TypeScript 来做。

cdk init app --language typescript
npm i

当我们执行init操作时,将创建以下内容。这次我们不会做太复杂的事情,只会去处理lib文件夹下的内容。

❯ tree -L 1
.
├── README.md
├── bin
├── cdk.json
├── cdk.out
├── jest.config.js
├── lib
├── node_modules
├── package-lock.json
├── package.json
├── test
└── tsconfig.json

VPC (Virtual Private Cloud) 可以用中文翻译为虚拟私有云。

まずはVPCから作っていきましょう。
CDKにはいろいろと便利なclassが用意されています。
VPC関連は このあたり を見てれば簡単に作れます。

MainStackにどんどん書き連ねてもいいですが、肥大化するのがいやなので最低限いい感じに分割していこうと思います。

lib以下にfileを作ります。

├── advent-calendar2020-stack.ts
└── recipes
    └── VPC
        └── multi_az.ts

VPCを作るためにまず、EC2のパッケージをinstallします。

npm i @aws-cdk/aws-ec2

我们立即开始编写CDK代码吧。

import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';

interface IRecipesVpcMultiAzProps {
  context: cdk.Stack;
}

const RecipesVpcMultiAz = (props: IRecipesVpcMultiAzProps) => {
  // === VPC ===
  const vpc = new ec2.Vpc(props.context, 'vpc', {
    cidr: '10.0.0.0/16',
    natGateways: 0
  })

  return {
    vpc
  };
};

export default RecipesVpcMultiAz;

これだけで、MultiAZなVPCやSubnetを作成してくれます(InternetGatewayやその他諸々)。
今回はめんどくさいのでPublicなSubnetにコンテナを作ろうと思うので、NatGatewayを作らないようにします。
(しっかりやりたい方は、PrivateSubnetに置いたり、ELBを用意したりしてみてください)
VPCのConstructに natGateways というのが用意されているので0にすることでNatGatewayを作らないように出来ます。

此外,我们当然可以将要分割的部分作为一个NestedStack来准备,但是在CDK的NestedStack中存在一个陷阱,即变更点不会被显示为差异。在个人项目中使用时可能不会有问题,但是在业务中使用时,意外的变更可能会导致非常困扰,所以这次我们并没有将其作为NestedStack单独切分出来,而是将其作为一个函数准备,并通过从MainStack中调用来创建一个CloudFormation模板,以便能够检查差异。

ECR的中文本地化版本

事先准备好ECS使用的镜像。
这次我们将借用这个镜像。
虽然可以从DockerHub获取镜像,但既然已经有了,还是将其放在ECR上吧。

以下的命令可用于支持ECR的公共和私有两种情况,但是私有情况下需要认证授权。

aws ecr get-login-password | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com

请给代码打上标签并推送到ECR。
请事先创建好仓库。

docker tag itzg/minecraft-server:latest ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/minecraft-server:latest
docker push ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/minecraft-server:latest

华中科技大学(ECS)

因为建立了VPC,让我们准备ECS吧。
因为我喜欢Fargate,所以我们将使用Fargate。

ECS拥有大约4个主要概念。
– 集群(Cluster)
– 服务(Service)
– 任务定义(TaskDefinition)
– 容器定义(ContainerDefinition)

我们将使用CDK来创建它们。

lib
├── advent-calendar2020-stack.ts
└── recipes
    └── ECS
        └── fargate.ts

安装需要的软件包。

npm i @aws-cdk/aws-ecs
npm i @aws-cdk/aws-ecr

首先,先准备好集群。
只要查看ECS这方面的内容,就可以解决。

import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as ecs from '@aws-cdk/aws-ecs';
import * as ecr from '@aws-cdk/aws-ecr';

interface IRecipesEcsFargateProps {
  context: cdk.Stack;
  vpc: ec2.IVpc;
}

const RecipesEcsFargate = (props: IRecipesEcsFargateProps) => {
  // === Cluster ===
  const cluster = new ecs.Cluster(props.context, 'EcsFargateCluster', {
    clusterName: 'advent-calendar2020-ecs-cluster',
    vpc: props.vpc
  });
};

export default RecipesEcsFargate;

只需使用这个,就可以创建ECS集群。

接下来,让我们准备一个任务定义。
在运行Minecraft时,需要一定的CPU和内存,因此我们先进行了适当的设置(如果想要更加强大,请根据需要进行相应调整)。


  // 省略

  // === Cluster ===
  const cluster = new ecs.Cluster(props.context, 'EcsFargateCluster', {
    clusterName: 'advent-calendar2020-ecs-cluster',
    vpc: props.vpc
  });

  // 追記
  // === Task Definition ===
  const taskDefinition = new ecs.FargateTaskDefinition(props.context, 'EcsFargateTaskDefinition', {
    cpu: 512,
    memoryLimitMiB: 4096
  });

  // 省略

下面是ContainerDefinition。
我们将定义在ECS上运行的容器。
我们将参考预先准备好的ECR,并将其集成到ContainerDefinition中。

  // 省略

  // === Task Definition ===
  const taskDefinition = new ecs.FargateTaskDefinition(props.context, 'EcsFargateTaskDefinition', {
    cpu: 512,
    memoryLimitMiB: 4096
  });

  // 追記
  // === ECR ===
  const repository = ecr.Repository.fromRepositoryName(props.context, 'Ecr', 'minecraft');

  // === Container Definition ===
  const containerDefinition = new ecs.ContainerDefinition(props.context, 'MineCraft', {
    image: ecs.ContainerImage.fromEcrRepository(repository),
    taskDefinition: taskDefinition,
    environment: {
      EULA: 'TRUE'
    }
  });

  containerDefinition.addPortMappings({
    containerPort: 25565,
    protocol: ecs.Protocol.TCP
  });

  // 省略

最后是服务。
我也会同时开放Minecraft的端口。

  // 省略

  // === ECR ===
  const repository = ecr.Repository.fromRepositoryName(props.context, 'Ecr', 'minecraft');

  // === Container Definition ===
  const containerDefinition = new ecs.ContainerDefinition(props.context, 'MineCraft', {
    image: ecs.ContainerImage.fromEcrRepository(repository),
    taskDefinition: taskDefinition,
    environment: {
      EULA: 'TRUE'
    }
  });

  containerDefinition.addPortMappings({
    containerPort: 25565,
    protocol: ecs.Protocol.TCP
  });

  // 追記
  // === Security Group ===
  const securityGroup = new ec2.SecurityGroup(props.context, 'SecurityGroup', {
    vpc: props.vpc,
    securityGroupName: 'ecs-sg'
  });

  securityGroup.addIngressRule(
      ec2.Peer.ipv4('0.0.0.0/0'),
      ec2.Port.tcp(25565)
  );

  // === Service ===
  const service = new ecs.FargateService(props.context, 'EcsFargateService', {
    cluster: cluster,
    taskDefinition: taskDefinition,
    assignPublicIp: true,
    securityGroup: securityGroup,
    vpcSubnets: props.vpc.selectSubnets({
      subnetType: ec2.SubnetType.PUBLIC
    })
  });

  // 省略

现在,ECS的准备工作已经完成。

让我们最后尝试从主堆栈中调用。

import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';

import RecipesVpcMultiAz from './recipes/VPC/multi_az';
import RecipesEcsFargate from './recipes/ECS/fargate';

export class AdventCalendar2020Stack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // === VPC ===
    const vpcResource = RecipesVpcMultiAz({
      context: this
    });

    // === ECS ===
    RecipesEcsFargate({
      context: this,
      vpc: vpcResource.vpc
    });
  }
}

执行CDK

CDKを用意出来たのであとは実行するだけです。

cdk deploy

CDKを実行するかどうかを聞かれるので y でdeployしてください。
CloudFormationのStackが作られるので、あとは気長に待つだけです。
deployに失敗した場合はCloudFormationのイベントを眺めてみてください。

我要試著玩一玩。

我們已經在ECS上準備好了Minecraft。因此,現在讓我們實際開始遊戲吧。

由于为容器分配了公共IP,我将尝试直接连接。

image.png

没有问题,一切顺利!
没有特别限制,只要告诉IP地址,随时都可以一起愉快地进行多人Minecraft游戏!

image.png

ECS非常灵活,可以轻松扩展,即使人数增加也可以轻松应对。云端真是方便好用啊。

总结

为了即将到来的连假,我在云上以SRE的方式为大家准备了一些能让我们忘记时间、玩得开心的东西。
实际上,我只是想玩一下ECS和CDK而已,只要能够访问到就满足了。
如果需要将游戏数据和设置等内容提取到外部,可以考虑挂载EFS等方式来解决。

由于我被安排负责12月的第一周,这相对较早,所以我想问问大家是否愿意尝试使用ECS和CDK来进行今年最后一次的输入,然后在假期期间在Minecraft上聚在一起玩得开心。

广告
将在 10 秒后关闭
bannerAds