В Amazon Web Services существует множество различных вычислительных сервисов. В AWS Lambda есть серверный маршрут, где вы можете подготовить свою рабочую нагрузку и запускать ее только тогда, когда вам это нужно. Elastic Compute Cloud (EC2) позволяет вам запускать любую рабочую нагрузку на виртуальных машинах, за которую вы платите каждый час.
Но сегодня многие люди создают контейнерные рабочие нагрузки с помощью Docker. Итак, какие у вас есть варианты для запуска ваших контейнеров в AWS? В этом посте мы создадим контейнер Docker. Затем мы собираемся создать инфраструктуру AWS для размещения этого образа и запустить его через AWS Elastic Container Service (ECS). Оттуда мы рассмотрим, как вы можете развернуть новые версии вашего образа прямо с вашего терминала.
Давайте начнем с создания примера контейнера Docker, который мы можем развернуть.
Образец Docker Image
Для нашей цели давайте создадим пример приложения Node Express, которое содержит одну конечную точку. Мы начнем с того, что сначала настроим наш проект. Все в yarn init остается в качестве по умолчанию, за исключением точки входа, для которой мы вводим server.js.
$ mkdir sample-express-app
$ cd sample-express-app
$ yarn init
question name (sample-express-app):
question version (1.0.0):
question description:
question entry point (index.js): server.js
question repository url:
question author: kylegalbraith
question license (MIT):
question private:
Как только проект настроен через yarn, давайте продолжим и установим express в проект.
$ yarn add express
info No lockfile found.
[1/4] ? Resolving packages...
[2/4] ? Fetching packages...
[3/4] ? Linking dependencies...
[4/4] ? Building fresh packages...
success Saved lockfile.
✨ Done in 0.79s.
Теперь мы можем настроить конечную точку, добавив в файл server.js следующее:
const express = require('express');
const PORT = 8080;
const HOST = '0.0.0.0';
const api = express();
api.get('/', (req, res) => {
res.send('Sample Endpoint\n');
});
api.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
Теперь, когда у нас есть наш пример expressAPI, давайте продолжим и настроим наш Dockerfile. Вот как Dockerfile в итоге будет выглядеть:
FROM node:10
WORKDIR /src/api
COPY package.json ./
COPY yarn.lock ./
RUN yarn install
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
Чтобы проверить себя, давайте создадим этот образ и запустим контейнер с ним.
$ docker build -t sample-express-app .
$ docker run -p 8080:8080 -d sample-express-app
5f3eaa088b35d895411c8d60f684aeba5d68d85f3bc07172c672542fe6b95537
$ curl localhost:8080
Sample Endpoint
Мы видим, что когда мы запускаем контейнер через порт 8080, мы можем вызвать нашу конечную точку через curl и получить ответ Sample Endpoint.
Теперь, когда у нас есть образ Docker для сборки и развертывания, давайте настроим реестр контейнеров в AWS, в который мы можем отправлять наши образы.
Публикация Docker Image в Elastic Container Repository (ECR)
Чтобы продолжить изучение темы AWS с его использованием, мы собираемся настроить репозиторий ECR в AWS. Мы будем использовать этот репозиторий для размещения нашего образа Docker. Прежде чем мы сможем это сделать, необходимо установить и настроить интерфейс командной строки AWS .
У вас установлены и настроены интерфейс командной строки AWS и CDK? Отлично, давайте инициализируем наш проект CDK, чтобы начать представлять нашу инфраструктуру в Typescript. Создайте новый каталог с именем infrastructure в корне вашего репозитория. Затем инициализируйте новый проект через CDK в терминале.
$ cdk init --language typescript
Applying project template app for typescript
Executing npm install...
# Useful commands
* `npm run build` compile typescript to js
* `npm run watch` watch for changes and compile
* `npm run test` perform the jest unit tests
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk synth` emits the synthesized CloudFormation template
Теперь у нас есть проект CDK, заглушенный внутри нашей infrastructure папки. Ключевой файл, который мы собираемся добавить в нашу инфраструктуру: lib/infrastructure-stack.ts.
Для начала давайте добавим наш ресурс репозитория ECR. Во-первых, нам нужно добавить модуль ECR в наш проект CDK.
$ npm install @aws-cdk/aws-ecr
Теперь мы можем подготовить наш репозиторий ECR, обновив наш infrastructure-stack.ts следующим образом.
import cdk = require('@aws-cdk/core');
import ecr = require('@aws-cdk/aws-ecr');
export class InfrastructureStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// ECR repository
const repository = new ecr.Repository(this, 'sample-express-app', {
repositoryName: 'sample-express-app'
});
}
}
Чтобы развернуть инфраструктуру CDK, нам нужно запустить deploy команду из нашей командной строки.
$ cdk deploy
InfrastructureStack: deploying...
InfrastructureStack: creating CloudFormation changeset...
0/3 | 15:51:50 | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | InfrastructureStack User Initiated
0/3 | 15:51:53 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata
0/3 | 15:51:53 | CREATE_IN_PROGRESS | AWS::ECR::Repository | sample-express-app (sampleexpressapp99ADE4E3)
0/3 | 15:51:54 | CREATE_IN_PROGRESS | AWS::ECR::Repository | sample-express-app (sampleexpressapp99ADE4E3) Resource creation Initiated
1/3 | 15:51:54 | CREATE_COMPLETE | AWS::ECR::Repository | sample-express-app (sampleexpressapp99ADE4E3)
1/3 | 15:51:55 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata Resource creation Initiated
2/3 | 15:51:55 | CREATE_COMPLETE | AWS::CDK::Metadata | CDKMetadata
3/3 | 15:51:57 | CREATE_COMPLETE | AWS::CloudFormation::Stack | InfrastructureStack
Теперь в нашей учетной записи AWS есть репозиторий ECR для размещения нашего нового образа Docker. После создания репозитория нам необходимо войти в него, прежде чем мы сможем запустить новый образ. Чтобы сделать это, мы запускаем команду, указанную ниже, в кавычках, чтобы docker login команда вызывалась после get-login возврата.
$ `aws ecr get-login --no-include-email`
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded
$ aws ecr describe-repositories
{
"repositories": [
{
"registryId": "<aws-id>",
"repositoryName": "sample-express-app",
"repositoryArn": "arn:aws:ecr:us-west-2:<aws-id>:repository/infra-sampl-1ewaboppskux6",
"createdAt": 1571007114.0,
"repositoryUri": "<aws-id>.dkr.ecr.us-west-2.amazonaws.com/sample-express-app"
}
]
}
Теперь, когда вошли в систему, мы можем пометить и отправить наше изображение в наш новый репозиторий ECR. Возьмите repositoryUri из describe-repositories команды выше. Мы собираемся использовать это в теге и ввести следующую команду
$ docker tag sample-express-app <aws-id>.dkr.ecr.us-west-2.amazonaws.com/sample-express-app
$ docker push <aws-id>.dkr.ecr.us-west-2.amazonaws.com/sample-express-app
0574222c01c4: Pushed
28c9fa7f105e: Pushed
8942b63b65a1: Pushed
b91230b492da: Pushed
6ad739b471d2: Pushed
954f92adc866: Pushed
adca1e83b51a: Pushed
73982c948de0: Pushed
84d0c4b192e8: Pushed
a637c551a0da: Pushed
2c8d31157b81: Pushed
7b76d801397d: Pushed
f32868cde90b: Pushed
0db06dff9d9a: Pushed
Отлично, наш образ Docker находится в нашем репозитории ECR. Теперь мы можем перейти к его развертыванию и запуску через Elastic Container Service (ECS).
Настройка инфраструктуры ECS
Прежде чем мы сможем запустить контейнер Docker в нашей учетной записи AWS, используя наш новый образ, нам нужно создать инфраструктуру, на которой он будет работать. Мы сосредоточимся на запуске нашего контейнера в Elastic Container Service (ECS), предоставляемой AWS.
ECS - это сервис оркестровки контейнеров, предоставляемый AWS. Это устраняет необходимость в управлении инфраструктурой, планировании и масштабировании для контейнерных рабочих нагрузок.
ECS состоит из трех основных терминов, которые важно знать.
- Кластер: это логическая группа базовых экземпляров EC2, на которых работают наши контейнеры. У нас нет доступа к этим экземплярам, и AWS управляет ими от нашего имени.
- Сервис: длительный процесс, такой как веб-сервер или база данных, работает как сервис в нашем кластере. Мы можем определить, сколько контейнеров должно быть запущено для этого сервиса.
- Определение задачи: это определение нашего контейнера, который может запускаться в кластере индивидуально или через службу. Когда определение задачи выполняется в нашем кластере, мы часто называем его задачей, поэтому работающий контейнер === задача в ECS.
Порядок этих определений также имеет значение. Мы можем думать о том, что кластер - это самый низкий уровень сервиса, когда EC2 запускает наши контейнеры. В то время как задача является экземпляром нашего контейнера, запущенного на одном или нескольких из этих экземпляров.
Давайте перейдем к обеспечению нашей инфраструктуры. Мы собираемся обновить наш, infrastructure-stack.ts чтобы иметь ресурсы, которые нам нужны для нашего кластера ECS. Во-первых, нам нужно добавить несколько других модулей.
$ npm install @aws-cdk/aws-ecs
$ npm install @aws-cdk/aws-ec2
$ npm install @aws-cdk/aws-ecs-patterns
Теперь мы можем добавить ресурсы, необходимые для нашего кластера, который будет запускать новый образ Docker. infrastructure-stack.ts теперь наши должны выглядеть так.
import cdk = require('@aws-cdk/core');
import ecr = require('@aws-cdk/aws-ecr');
import ecs = require('@aws-cdk/aws-ecs');
import ec2 = require('@aws-cdk/aws-ec2');
import ecsPatterns = require('@aws-cdk/aws-ecs-patterns');
export class InfrastructureStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// ECR repository
const repository = new ecr.Repository(this, 'sample-express-app', {
repositoryName: 'sample-express-app'
});
// ECS cluster/resources
const cluster = new ecs.Cluster(this, 'app-cluster', {
clusterName: 'app-cluster'
});
cluster.addCapacity('app-scaling-group', {
instanceType: new ec2.InstanceType("t2.micro"),
desiredCapacity: 1
});
const loadBalancedService = new ecsPatterns.ApplicationLoadBalancedEc2Service(this, 'app-service', {
cluster,
memoryLimitMiB: 512,
cpu: 5,
desiredCount: 1,
serviceName: 'sample-express-app',
taskImageOptions: {
image: ecs.ContainerImage.fromEcrRepository(repository),
containerPort: 8080
},
publicLoadBalancer: true
});
}
}
Первым , что мы замечаем, что мы сначала определим наш ECS кластер app-cluster. Далее нам нужно добавить экземпляр в наш кластер app-scaling-group. Это автомасштабирующая группа t2.microтипов экземпляров, на которой могут работать наши контейнеры. Затем мы используем шаблон обслуживания с балансировкой нагрузки, предоставляемый ecsPatterns модулем.
Мы также устанавливаем значение containerPort 8080 в качестве порта, на котором работает наше экспресс-приложение внутри контейнера.
Этот шаблон создает общедоступный балансировщик нагрузки. Этот балансировщик будет переадресовывать вызовы в наш контейнер через порт 8080, работающий внутри нашей службы ECS.
Мы внедряем эти изменения с помощью другой deploy команды. На этот раз мы укажем, --require-approval never чтобы не получать подсказки об изменениях IAM.
$ cdk deploy --require-approval never
InfrastructureStack: deploying...
InfrastructureStack: creating CloudFormation changeset...
0/46 | 16:41:46 | UPDATE_IN_PROGRESS | AWS::CloudFormation::Stack | InfrastructureStack User Initiated
0/46 | 16:42:07 | CREATE_IN_PROGRESS | AWS::EC2::EIP | app-cluster/Vpc/PublicSubnet2/EIP (appclusterVpcPublicSubnet2EIPD0A381A3)
0/46 | 16:42:07 | CREATE_IN_PROGRESS | AWS::ECS::Cluster | app-cluster (appclusterD09F8E40)
0/46 | 16:42:07 | CREATE_IN_PROGRESS | AWS::EC2::InternetGateway | app-cluster/Vpc/IGW (appclusterVpcIGW17A11835)
0/46 | 16:42:07 | CREATE_IN_PROGRESS | AWS::EC2::EIP | app-cluster/Vpc/PublicSubnet1/EIP (appclusterVpcPublicSubnet1EIP791F54CD)
...
....
.....
44/46 | 16:46:04 | CREATE_COMPLETE | AWS::Lambda::Permission c2C88B6D3
45/46 | 16:46:33 | CREATE_COMPLETE | AWS::ECS::Service | app-service/Service (appserviceServiceA5AB3AA1)
45/46 | 16:46:37 | UPDATE_COMPLETE_CLEA | AWS::CloudFormation::Stack | InfrastructureStack
46/46 | 16:46:38 | UPDATE_COMPLETE | AWS::CloudFormation::Stack | InfrastructureStack
Outputs:
InfrastructureStack.appserviceLoadBalancerDNS0A615BF5 = Infra-appse-187228PB273DW-1700265048.us-west-2.elb.amazonaws.com
InfrastructureStack.appserviceServiceURL90EC0456 = http://Infra-appse-187228PB273DW-1700265048.us-west-2.elb.amazonaws.com
Используя модуль AWS CDK для Elastic Container Service, мы создали все ресурсы, необходимые для нашего нового кластера. Как вы можете видеть из вывода, новый VPC со связанными подсетями был создан от нашего имени. Это хорошее преимущество такого инструмента, как CDK, он создал разумные значения по умолчанию для нашего нового кластера без необходимости их указания.
Теперь мы должны увидеть, что мы создали новый кластер ECS с нашим сервисом, в котором выполняется текущее определение задачи. CDK помог нам, выдав URL для нашего сервисного балансировщика нагрузки. Давайте возьмем этот URL и проверим, что наш контейнер работает и принимает трафик.
$ curl Infra-appse-187228PB273DW-1700265048.us-west-2.elb.amazonaws.com
Sample Endpoint
Наше определение задачи ECS в настоящее время настроено так, чтобы указывать на latest тег изображения Docker, которое мы публикуем. Это означает, что мы можем обновить наш образ, а изменения можно внедрить в наш кластер. Давайте обновим ответ, который мы возвращаем для нашего API.
api.get('/', (req, res) => {
res.send('New Response\n');
});
Теперь давайте создадим и запустим новую версию нашего образа Docker.
$ docker build -t sample-express-app .
$ `aws ecr get-login --no-include-email`
$ docker tag sample-express-app <aws-id>.dkr.ecr.us-west-2.amazonaws.com/sample-express-app:latest
$ docker push <aws-id>.dkr.ecr.us-west-2.amazonaws.com/sample-express-app:latest
9a7704a19307: Pushed
03a86aeeb52b: Layer already exists
c14651828ff6: Layer already exists
4ecb552d7aff: Layer already exists
6ad739b471d2: Layer already exists
954f92adc866: Layer already exists
adca1e83b51a: Layer already exists
73982c948de0: Layer already exists
84d0c4b192e8: Layer already exists
a637c551a0da: Layer already exists
2c8d31157b81: Layer already exists
7b76d801397d: Layer already exists
f32868cde90b: Layer already exists
0db06dff9d9a: Layer already exists
latest: digest: sha256:ed82982bfa5fe6333c4b67afaae0f36e3208a588736c9586ff81dbdd7e1bc0f5 size: 3256
Теперь у нас есть новая версия нашего образа, помещенная в репозиторий. Но он не был развернут на сервисе, работающем в нашем кластере. Чтобы получить изменения, нам нужно перезапустить службу, чтобы она вытягивала latest тег нашего изображения.
К счастью, мы можем сделать это с помощью одного вызова AWS CLI из нашей командной строки.
$ aws ecs update-service --force-new-deployment --cluster app-cluster --service sample-express-app
Как только наш новый образ будет запущен в службу (что может занять несколько минут), мы снова сможем подключиться к нашей конечной точке и увидеть наш новый ответ.
$ curl Infra-appse-187228PB273DW-1700265048.us-west-2.elb.amazonaws.com
New Response
Мы прошли полное упражнение по созданию образа Docker, подготовке кластера ECS и запуску нашего образа в кластере. Мы даже продемонстрировали, как мы можем обновить наш образ и развернуть новые версии на работающем сервисе.
0 комментариев
Добавить комментарий