AWS Lambda Beraksi di Jawa 11. Pergi ke Serverless untuk Produksi

Artikel ini adalah panduan tentang cara menggunakan AWS Lambda dengan cepat dan tanpa rasa sakit dengan contoh sederhana. Cocok untuk pengembang yang sama sekali tidak bekerja dengan Lambda, dan yang tahu Cloud untuk mengevaluasi visi lain untuk mengembangkan aplikasi Serverless.

gambar


pengantar


Halo semuanya.
Nama saya Alexander Gruzdev, saya Pimpinan Tim Java di DINS. Selama lebih dari dua tahun, saya telah bekerja erat dengan infrastruktur AWS dan memiliki pengalaman dalam menulis aplikasi untuk AWS dan dalam menyebarkan aplikasi-aplikasi ini. Dalam pekerjaan saya, saya harus menggunakan ElasticBeanstalk, ECS, Fargate, EKS, dan, tentu saja, AWS Lambda.

Masing-masing layanan ini baik dengan caranya sendiri, dan dalam kerangka artikel ini saya tidak akan mendesak untuk hanya menggunakan AWS Lambda sebagai pengganti untuk semua opsi lainnya. Saya hanya ingin menunjukkan betapa mudahnya untuk mulai menggunakan Lambda, dan dalam hal apa Anda akan mendapatkan manfaat menggunakannya.

Semua orang, dari pengembang hingga manajer, menginginkan proses yang jelas untuk mengirimkan perubahan kepada pelanggan. Semakin transparan jalur ini dan semakin sedikit upaya yang diperlukan, semakin menguntungkan. Paling sering, pengembang dan penguji tidak ingin tahu di mana aplikasi akan digunakan, perangkat keras mana yang harus dipilih, di wilayah mana Anda ingin meletakkan replika, dll. Dalam diagram berikut, Anda dapat melihat kelas layanan "aaS" (sebagai layanan), di mana AWS Lambda mewakili kategori FaaS.

gambar

Singkatnya
FaaS , . , , CRUD C, R, U, D Create Read. , β€” .


Ide


Untuk menunjukkan apa pengembangan solusi Serverless, saya memutuskan untuk mengambil contoh yang agak dangkal. Saya pikir Anda telah melihat formulir umpan balik Hubungi Kami di banyak situs, di mana Anda dapat meninggalkan surat atau telepon dan mengajukan pertanyaan untuk dijawab kemudian. Layar di bawah ini, bentuknya tidak super, tapi subjek artikel ini bukan desain material.


Dengan mengisi formulir ini, pelanggan potensial akan menunggu panggilan atau surat dari Anda.
Dari sisi teknis yang Anda butuhkan:
  • menyimpan data dari formulir ke database;
  • mengirim surat ke agen yang akan menjawab pertanyaan;
  • mungkin menulis layanan lain di mana agen akan menandai pekerjaan yang dilakukan berdasarkan database yang sama (tidak termasuk dalam ruang lingkup artikel).


Implementasi ide tersedia di GitHub .

Arsitektur


Hal pertama yang terlintas dalam pikiran selama implementasi adalah apa yang kita butuhkan:
  • mesin di belakang, depan, gambar, dll.,
  • mobil dengan pangkalan
  • mesin dengan layanan email.




Bagian belakang dan pangkalan, dengan cara yang baik, perlu direplikasi, tetapi misalkan kita melakukan ini untuk mini-store dan beban pada formulir ini minimal. Oleh karena itu, semua mesin ini dapat digabungkan menjadi satu dan digunakan dalam satu set.
Tetapi karena kita mempertimbangkan Serverless, mari kita membangun arsitektur sepenuhnya pada komponen serverless.
Tidak peduli seberapa menjanjikan Serverless mungkin terdengar, kita semua mengerti bahwa server masih ada di sini, mereka hanya menghilang dari bidang visi Anda. Dan sekarang cloud bekerja sehingga Anda bisa melakukan pekerjaan yang bisa Anda lakukan sendiri. Tapi mari kita coba gunakan hanya komponen tanpa server seperti itu:


Diagram menunjukkan pemisahan yang sangat jelas dari fungsi masing-masing komponen.
  • AWS Lambda adalah komponen utama yang berisi kode / logika,
  • S3 bertanggung jawab untuk menyimpan sumber daya statis dan skrip JS,
  • CloudFront - untuk mekanisme caching dan dukungan multi-regional,
  • SES - layanan email,
  • DynamoDB - basis untuk menyimpan data dari formulir (dari siapa, pertanyaan apa, ke mana harus mengirim jawabannya),
  • Gateway API - API HTTP untuk lambda kami,
  • Route53 - diperlukan jika Anda ingin menambahkan nama domain yang indah.


Tidak semua komponen ini akan digunakan dalam panduan kami berikutnya, hanya agar tidak meregangkan artikel.
Route53 dan CloudFront adalah komponen yang cukup sederhana yang dapat Anda baca secara terpisah.
Spoiler kecil yang akan memberi kita solusi:
  • Kami bergerak menjauh dari mendukung mesin EC2, tidak ada debugging melalui ssh,
  • Konfigurasi mudah: konfigurasikan pembatasan / caching / akses dalam satu klik,
  • Mendukung kebijakan akses: membatasi hak dan memberikan akses,
  • Log out / pemantauan di luar kotak,
  • Bayar hanya untuk sumber daya yang digunakan / permintaan.


Demo


Latihan


Untuk mulai mengembangkan solusi Tanpa Server kami, Anda harus memenuhi persyaratan berikut:


Setelah menginstal semua alat di atas, Anda perlu mengkonfigurasi aws-cli Anda untuk akses jarak jauh ke AWS. Ikuti instruksi . Ini akan membutuhkan membuat pengguna baru dan membongkar Kunci Akses dan Kunci Rahasianya.

Perakitan proyek


1. Buat proyek dari templat


Buka direktori untuk proyek masa depan dan darinya luncurkan SAM CLI. Ikuti instruksi:


Catatan: Tergantung pada versi SAM-CLI, perintahnya mungkin sedikit berbeda, tetapi semuanya tetap intuitif. Pilih saja yang paling mirip dengan yang digunakan di atas. Anda juga dapat memilih alat bangun yang berbeda jika Gradle tidak cocok untuk Anda.

Proyek "Halo, Dunia!" siap. Sekarang Anda dapat mengerjakan nama proyek dan paket, dependensi dan kode sumber.

2. Mari kita berurusan dengan kecanduan


Tambahkan dependensi berikut ke build.gradle:
dependencies {
    // AWS
    implementation group: 'com.amazonaws.serverless', name: 'aws-serverless-java-container-core', version: '1.4'
    implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.0'
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-ses', version: '1.11.670'
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-dynamodb', version: '1.11.670'
    implementation group: 'com.amazonaws', name: 'aws-lambda-java-log4j2', version: '1.1.0'

    // Utils
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.10.0'
    implementation group: 'commons-io', name: 'commons-io', version: '2.6'

    // Test
    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.1.0'
    testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.4'
    testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.4'
    testImplementation group: 'junit', name: 'junit', version: '4.12'
}

Yang utama adalah AWS SDK. Mereka akan memungkinkan Anda untuk bekerja dengan layanan tertentu, seperti SES, DynamoDB, dll.

3. Kami menulis lambda


  • Kami mengubah kelas templat permintaan dan respons untuk RequestHandler menjadi AwsProxyRequest dan ContactUsProxyResponse.
    public class App implements RequestHandler<AwsProxyRequest, ContactUsProxyResponse>
    ...
    public ContactUsProxyResponse handleRequest(AwsProxyRequest request, Context context) 

  • AwsClientFactory AWS SDK-.
    /**
     * Just an util class for an eager initialization of sdk clients.
     */
    public class AwsClientFactory {
    
        private static final Logger LOG = LogManager.getLogger(AwsClientFactory.class);
    
        private final AmazonSimpleEmailService sesClient;
        private final DynamoDB dynamoDB;
    
        /**
         * AWS regions should be env variables if you want to generalize the solution.
         */
        AwsClientFactory() {
            LOG.debug("AWS clients factory initialization.");
            sesClient = AmazonSimpleEmailServiceClient.builder().withRegion(Regions.EU_WEST_1).build();
            AmazonDynamoDB dynamoDBClient = AmazonDynamoDBClientBuilder.standard().withRegion(Regions.EU_WEST_1).build();
            dynamoDB = new DynamoDB(dynamoDBClient);
        }
    
        DynamoDB getDynamoDB() {
            return dynamoDB;
        }
    
        AmazonSimpleEmailService getSesClient() {
            return sesClient;
        }
    
    }
    

  • ObjectMapper .
    
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final AwsClientFactory AWS_CLIENT_FACTORY = new AwsClientFactory();
    

  • .
    
     private SendEmailResult sendEmail(ContactUsRequest contactUsRequest) {
            String emailTemplate = getEmailTemplate();
            String email = fillTemplate(emailTemplate, contactUsRequest);
    
            SendEmailRequest sendEmailRequest =
                    new SendEmailRequest(
                            System.getenv("SENDER_EMAIL"),
                            new Destination(List.of(System.getenv("RECIPIENT_EMAIL"))),
                            new Message()
                                    .withSubject(
                                            new Content()
                                                    .withCharset(UTF_8.name())
                                                    .withData(contactUsRequest.getSubject()))
                                    .withBody(new Body()
                                            .withHtml(new Content()
                                                    .withCharset(UTF_8.name())
                                                    .withData(email))));
            LOG.info("Email template is ready");
            return AWS_CLIENT_FACTORY.getSesClient().sendEmail(sendEmailRequest);
    }
    
    private String fillTemplate(String emailTemplate, ContactUsRequest contactUsRequest) {
            return String.format(
                    emailTemplate,
                    contactUsRequest.getUsername(),
                    contactUsRequest.getEmail(),
                    contactUsRequest.getPhone(),
                    contactUsRequest.getQuestion());
    }
    
    private String getEmailTemplate() {
            try {
                return IOUtils.toString(
                        Objects.requireNonNull(this.getClass().getClassLoader()
                                                   .getResourceAsStream("email_template.html")),
                        UTF_8);
            } catch (IOException e) {
                throw new RuntimeException("Loading an email template failed.", e);
            }
    }
    
    private void addEmailDetailsToDb(ContactUsRequest contactUsRequest, SendEmailResult sendEmailResult) {
            AWS_CLIENT_FACTORY.getDynamoDB().getTable("ContactUsTable")
                              .putItem(new Item()
                                      .withPrimaryKey("Id", sendEmailResult.getMessageId())
                                      .withString("Subject", contactUsRequest.getSubject())
                                      .withString("Username", contactUsRequest.getUsername())
                                      .withString("Phone", contactUsRequest.getPhone())
                                      .withString("Email", contactUsRequest.getEmail())
                                      .withString("Question", contactUsRequest.getQuestion()));
    }
    

  • .
    
    private ContactUsProxyResponse buildResponse(int statusCode, String body) {
            ContactUsProxyResponse awsProxyResponse =
                    new ContactUsProxyResponse();
            awsProxyResponse.setStatusCode(statusCode);
            awsProxyResponse.setBody(getBodyAsString(body));
            awsProxyResponse.addHeader(CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
            awsProxyResponse.addHeader("Access-Control-Allow-Origin", "*");
            return awsProxyResponse;
    }
    
     private String getBodyAsString(String body) {
            try {
                return OBJECT_MAPPER.writeValueAsString(new ContactUsResponseBody(body));
            } catch (JsonProcessingException e) {
                throw new RuntimeException("Writing ContactUsResponseBody as string failed.", e);
            }
    }
    

  • . Β«// WARMING UPΒ».
    
    if (Optional.ofNullable(request.getMultiValueHeaders()).map(headers -> headers.containsKey("X-WARM-UP")).orElse(FALSE)) {
                LOG.info("Lambda was warmed up");
                return buildResponse(201, "Lambda was warmed up. V1");
    }
    

  • handleRequest()
    
    @Override
    public ContactUsProxyResponse handleRequest(AwsProxyRequest request, Context context) {
            LOG.info("Request was received");
            LOG.debug(getAsPrettyString(request));
    
            if (Optional.ofNullable(request.getMultiValueHeaders()).map(headers -> headers.containsKey("X-WARM-UP")).orElse(FALSE)) {
                LOG.info("Lambda was warmed up");
                return buildResponse(201, "Lambda was warmed up. V1");
            }
    
            ContactUsRequest contactUsRequest = getContactUsRequest(request);
    
            SendEmailResult sendEmailResult = sendEmail(contactUsRequest);
            LOG.info("Email was sent");
    
            addEmailDetailsToDb(contactUsRequest, sendEmailResult);
            LOG.info("DB is updated");
    
            return buildResponse(200,
                    String.format("Message %s has been sent successfully.", sendEmailResult.getMessageId()));
    }
    



Logika dasarnya cukup sederhana, jadi saya pikir tidak ada gunanya menggambarkannya secara rinci. Ada beberapa hal yang perlu diperhatikan.

Yang pertama adalah logging. Salah satu argumen dari metode lambda Konteks berisi banyak informasi tambahan, serta logger. Oleh karena itu, Anda tidak dapat membuat logger terpisah, tetapi gunakan konteks lambda yang disediakan. Untuk melakukan ini, cukup menelepon sebelum digunakan:
LambdaLogger logger = context.getLogger();


Poin kedua adalah pemanasan. Karena membuat lingkungan yang dapat dieksekusi untuk lambda malas, memulai JVM, memuat classpath dan mengeksekusi kode membutuhkan waktu. Panggilan pertama mungkin memakan waktu beberapa detik, yang tidak baik jika Anda menulis API sinkron yang berbeda. Untuk kasus-kasus seperti itu, Anda sendiri dapat memberi tahu AWS bahwa Anda perlu membuat beberapa instance lambda dalam keadaan siaga. Tapi ini mengharuskan seseorang untuk memanggil lambda. Jika kami melakukan ini dengan versi dasar kode, maka, pada kenyataannya, kami akan mengirim surat dan menulis beberapa data yang tidak akurat ke database.
Untuk menghindari hal ini, kita dapat menambahkan semacam pemrosesan permintaan untuk membedakan permintaan sebenarnya dari permintaan pemanasan. Misalnya, kita bisa menambahkan header khusus ke permintaan. Dalam kasus kami, tajuk "X-WARM-UP" akan digunakan dengan nilai apa pun - untuk memahami bahwa ini adalah permintaan pemanasan dan kami hanya perlu mengembalikan semacam respons tanpa menjalankan logika bisnis.

Hal terakhir yang ingin saya perhatikan adalah penggunaan variabel statis. Ini tidak membuat instance lambda stateful dalam kasus kami, itu hanya memungkinkan menggunakan kembali objek yang sudah ada dan diinisialisasi. Jika objek yang ingin Anda inisialisasi berubah secara statis selama eksekusi kode, coba pikirkan lagi apakah ini akan mengganggu operasi panggilan berikutnya ke lambda. Juga, jika objek ini menggunakan variabel lingkungan selama inisialisasi, Anda tidak harus membuatnya statis.

4. Kami menulis tes


Kode Lambda dapat dicakup oleh jenis tes yang sama yang Anda gunakan dalam kebanyakan kasus. Tetapi untuk menjaga agar artikel tidak semakin berkembang, saya akan membahas pengujian aplikasi Serverless di artikel berikutnya tentang pengujian AWS Lambda dan pengembangan di tempat. Meskipun unit test sudah tersedia di repositori .

5. Kami menulis templat sumber daya SAM


Setelah kami menulis dan menguji lambda kami secara lokal, kami perlu menulis template SAM untuk menggunakan semua sumber daya ini di AWS.

Sedikit tentang SAM
Serverless Application Model – serverless . , CloudFormation DSL, – Lambda, Gateway, DynamoDB . .
SAM, CloudFormation, Terraform – IaC-. SAM Serverless-, Terraform .
Terraform DINS.

Sebenarnya, mari kita lihat sumber daya apa yang perlu kita deklarasikan di SAM-template .

AWS :: Serverless :: Function
Fitur utama sudah terisi bagi kami jika kami menggunakan SAM init untuk membuat proyek. Tapi di sini adalah bagaimana mereka melihat contoh saya:
CodeUri: contact-us-function
Handler: com.gralll.sam.App::handleRequest
Runtime: java8
MemorySize: 256

Di sini saya pikir semuanya sudah cukup jelas.
CodeUri adalah direktori dengan lambler
Handler kami - jalur penuh ke metode
Runtime - apa yang
ditulis oleh lambda MemorySize - berbicara untuk dirinya sendiri

Berbicara tentang memori : hingga 3GB RAM tersedia di lambda, yang sesuai dengan sumber daya 2 CPU. Artinya, Anda tidak dapat mengatur CPU secara terpisah, hanya menambah / mengurangi jumlah memori.

Blok selanjutnya diperlukan untuk memilih metode penyebaran.
AutoPublishAlias: live
DeploymentPreference:
  Type: Canary10Percent10Minutes

AutoPublishAlias - memungkinkan Anda menambahkan alias ke setiap versi yang digunakan . Ini diperlukan untuk menerapkan penyebaran kenari.
Canary10Percent10Minutes - jenis penyebaran yang akan memungkinkan Anda untuk secara bersamaan memegang dua versi lambda: yang lama dan yang baru, tetapi hanya mengarahkan 10% dari lalu lintas ke yang baru. Jika dalam sepuluh menit tidak ada masalah, sisa traffic juga akan dialihkan ke versi baru.
Anda dapat membaca lebih lanjut tentang menggunakan fitur-fitur canggih di halaman SAM .

Berikutnya adalah variabel lingkungan yang akan digunakan dari kode. Setelah itu, blok lain untuk lambda:
Events:
  ContactUs:
    Type: Api
    Properties:
      Path: /contact
      Method: post

Di dalamnya, kita harus menggambarkan pemicu untuk memanggil lambda. Dalam kasus kami, ini akan menjadi permintaan Gateway API .
Ini adalah bentuk yang agak disederhanakan untuk menggambarkan API, tetapi cukup untuk Gateway API yang baru dibuat untuk mengarahkan semua permintaan POST / kontak ke lambda kami.

Tentu saja, kita perlu menggambarkan aspek keamanan. Di luar kotak, lambda yang dibuat tidak akan memiliki akses ke database atau layanan email. Jadi kita perlu secara eksplisit menentukan apa yang akan diizinkan. Ada beberapa cara untuk memberikan akses di dalam AWS. Kami akan menggunakan Kebijakan Berbasis Sumberdaya:
Policies:
  - AWSLambdaExecute
  - Version: '2012-10-17'
    Statement:
      - Effect: Allow
        Action:
          - ses:SendEmail
          - ses:SendRawEmail
        Resource: 'arn:aws:ses:eu-west-1:548476639829:identity/aleksandrgruzdev11@gmail.com'
      - Effect: Allow
        Action:
          - dynamodb:List*
        Resource: '*'
      - Effect: Allow
        Action:
          - dynamodb:Get*
          - dynamodb:PutItem
          - dynamodb:DescribeTable
        Resource: 'arn:aws:dynamodb:*:*:table/ContactUsTable'

Harap dicatat bahwa untuk tabel database kami tentukan nama tertentu, oleh karena itu, kami perlu membuat tabel dengan nama yang sama.
Mengenai SES: Anda melihat alamat email saya. Dalam kasus Anda, itu harus alamat Anda yang sudah dikonfirmasi. Cara melakukannya, lihat di sini .
Tepat setelah itu, Anda dapat menemukan ARN Identitas sumber daya ini dengan mengklik alamat yang dibuat dan menggantinya dengan email dalam contoh di atas.
Mereka seperti menemukan lambda. Sekarang mari kita beralih ke database.

AWS :: Serverless :: SimpleTable
Untuk tugas kami, kami hanya akan membuat satu tabel ContactUsTable :
ContactUsTable:
  Type: AWS::Serverless::SimpleTable
  Properties:
    PrimaryKey:
      Name: Id
      Type: String
    TableName: ContactUsTable
    ProvisionedThroughput:
      ReadCapacityUnits: 2
      WriteCapacityUnits: 2

Bidang yang diperlukan - hanya Id, dan juga menunjukkan ReadCapacityUnits dan WriteCapacityUnits. Kami tidak akan membahas secara terperinci nilai-nilai apa yang harus dipilih, karena ini juga merupakan topik yang agak luas. Anda bisa membacanya di sini . Untuk aplikasi pengujian, nilai kecil dari urutan 1-2 juga cukup. Parameter

global
dapat diambil di blok ini jika, misalnya, Anda mendeklarasikan beberapa sumber daya tipe Function atau API.
Globals:
  Function:
    Timeout: 15
  Api:
    Cors:
      AllowOrigin: "'*'"
      AllowHeaders: "'Content-Type,X-WARM-UP,X-Amz-Date,Authorization,X-Api-Key'"

Saya menggunakannya untuk mengatur batas waktu fungsi dan beberapa pengaturan Cors untuk memanggil Gateway API nanti dari halaman statis saya dengan formulir ContactUs.

Keluaran
Blok ini memungkinkan Anda untuk secara dinamis mendefinisikan beberapa variabel dalam konteks AWS CloudFormation global.
Outputs:
  ContactUsApi:
    Description: "API Gateway endpoint URL for Prod stage for ContactUs function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/contact/"
  ContactUsFunction:
    Description: "ContactUs Lambda Function ARN"
    Value: !GetAtt ContactUsFunction.Arn
  ContactUsFunctionIamRole:
    Description: "Implicit IAM Role created for ContactUs function"
    Value: !GetAtt ContactUsFunctionRole.Arn

Misalnya, kami mendeklarasikan variabel ContactUsApi, yang akan ditetapkan ke nilai, sebagai alamat publik dari titik akhir API yang kami buat.
Karena kami menggunakan $ {ServerlessRestApi} , AWS akan memasukkan pengidentifikasi unik API Gateway baru kami ke dalam string. Akibatnya, aplikasi apa pun yang memiliki akses ke CloudFormation akan bisa mendapatkan alamat ini - dengan demikian Anda tidak dapat membuat hardcode URL layanan Anda. Nah, kelebihan lainnya adalah sangat mudah untuk melihat daftar output - beberapa informasi meta tentang stack Anda.
Daftar lengkap fungsi dan parameter apa yang dapat Anda gunakan dapat ditemukan di sini .

Parameter
Selain semua hal di atas, Anda dapat menambahkan blok Parameter. Opsi ini akan membantu membuat templat lebih fleksibel. Di blok lain dari templat, Anda dapat membuat referensi ke parameter ini, sehingga tidak membuat hardcode beberapa nilai. Misalnya, dalam templat saya dapat berupa email, SES ARN, jumlah memori, dll.

Itu seluruh templat. Tidak ada yang melarang menambahkan beberapa sumber daya lebih dekat, misalnya, ember S3, sumber daya CloudFormation lainnya, atau sumber daya khusus secara umum .

6. Kami melanjutkan ke penyebaran


Untuk merakit proyek kami, kami tidak akan menggunakan Gradle, tetapi SAM.
Majelis
F:\aws\projects\contact-us-sam-app>sam build
Building resource 'ContactUsFunction'
Running JavaGradleWorkflow:GradleBuild
Running JavaGradleWorkflow:CopyArtifacts

Build Succeeded

Built Artifacts  : .aws-sam\build
Built Template   : .aws-sam\build\template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy –guided


Menjalankan perintah build sam dari root proyek akan secara otomatis mengumpulkan semua file yang diperlukan ke folder .aws-sam : kelas, dependensi, template SAM.
Selanjutnya, Anda perlu membuat ember S3, di mana SAM selanjutnya mengunggah semua artefak yang dikumpulkan.
Ini dapat dilakukan melalui konsol AWS berbasis browser, atau dengan perintah
aws s3 mb s3://bucket-name

Catatan: semua kotak dibuat dalam konteks global, dan itu dibagi-bagi di antara semua akun. Jadi Anda tidak dapat membuat ember jika seseorang telah membuatnya di akun Anda.

Saat ember siap, jalankan perintah:
sam package --output-template-file packaged.yaml --s3-bucket <YOUR_BACKET>

Hasil paket
F:\aws\projects\contact-us-sam-app>sam package --output-template-file packaged.yaml --s3-bucket contact-us-sam-app
Uploading to ea0c122c06a50d9676fbf9000a80a3bf  9212768 / 9212768.0  (100.00%)

Successfully packaged artifacts and wrote output template to file packaged.yaml.
Execute the following command to deploy the packaged template
sam deploy --template-file F:\aws\projects\contact-us-sam-app\packaged.yaml --stack-name <YOUR STACK NAME>


Saya mengarahkan bucket contact-us-sam-app saya, dan SAM mengunggah semua sumber daya ke lokasi yang ditentukan. Selanjutnya, SAM sudah memberi tahu Anda perintah untuk membuat tumpukan dengan sumber daya, dan dengan demikian menyematkan keputusan Anda. Kami menjalankan perintah, setelah sedikit selesai:
sam deploy --template-file packaged.yaml --region eu-west-1 --capabilities CAPABILITY_IAM --stack-name contact-us-sam-app

Seperti yang Anda lihat, saya menambahkan --capabilities CAPABILITY_IAM . Ini akan memungkinkan CloudFormation untuk membuat sumber daya IAM. Jika tidak, Anda akan menerima kesalahan InsufficientCapabilities saat membuat.
Berikut ini adalah log karya perintah ini (gambar dapat diklik). Detail seperti status setiap sumber daya dan nilai-nilai output menjadi hanya tersedia di salah satu versi terbaru dari SAM CLI.


7. Periksa status CloudFormation


Kita bisa menunggu penyebaran untuk menyelesaikan di konsol sampai muncul pesan yang menyatakan bahwa stack telah digunakan (perintah penyebaran log dalam paragraf sebelumnya):
Successfully created/updated stack - contact-us-sam-app in eu-west-1

Namun dalam kasus kami, Anda akan melihat pesan ini hanya setelah sepuluh menit karena mode penyebaran kenari. Karenanya, lebih mudah untuk membuka konsol peramban dan menonton tumpukan di sana.

Setelah beberapa waktu, status akan berubah menjadi CREATE_COMPLETE , yang berarti penyelesaiannya berhasil.
Di tab Acara , Anda dapat melihat status semua sumber daya. Jika tumpukan Anda gagal, ini adalah tempat Anda dapat menemukan pesan kesalahan terperinci.
Misalnya, ini: UPDATE_FAILED - jika Anda salah mengkonfigurasi API Gateway di templat.


Di tab Sumber Daya, Anda dapat menemukan semua sumber daya yang dibuat. Jangan kaget dengan kuantitas mereka. Meskipun kami hanya mendeklarasikan fungsi dan tabel database dalam templat SAM, CloudFormation menciptakan banyak sumber daya lain untuk kami. Jika Anda melihat Tipe mereka, Anda dapat memahami layanan apa yang mereka miliki.
Untuk Api Gateway, itu secara implisit dibuat:
  • ServerlessRestApi
  • ServerlessRestApiDeployment
  • ServerlessRestApiProdStage

Juga untuk lambda beberapa objek tambahan telah dibuat.
Sekarang buka Outputs dan temukan URL API kami. Salin, ini akan segera berguna.

8. Formulir HTML KontakUs


Seperti yang Anda ingat, saya memutuskan untuk membuat formulir ContactUs, dan sekarang kita perlu membuatnya agar tersedia tidak hanya di mesin lokal.

Konfigurasi
Adapun formulir itu sendiri, misalnya, saya memutuskan untuk mengambil formulir HTML yang paling sederhana dan menambahkan panggilan Gateway API melalui ajax.

Selain formulir itu sendiri, saya menambahkan beberapa tombol untuk debugging dan menyederhanakan pengisian:
Tetapkan default - pengganti, seperti yang diharapkan, parameter default yang ditentukan dalam HTML.
$("#url-input").val("https://4ykskscuq0.execute-api.eu-west-1.amazonaws.com/Prod/contact");
$("#name-input").val("Mike");
$("#email-input").val("Mike@somemail.com");
$("#phone-input").val("+79999999999");
$("#description-input").val("How much does it cost?");

Jika Anda bermaksud untuk menggunakan fungsionalitas ini, ubah url-input ke path ke Gateway API Anda, yang Anda salin dari Output .

Hosting
  • Buat ember S3 baru.
  • Kami memuat file HTML ke dalam ember dengan memilih opsi untuk menjadikan file tersebut publik.
  • Kami masuk ke ember tempat kami mengunggah file.
  • Buka Properti, lalu aktifkan hosting situs web Static dan lihat Endpoint baru Anda yang tersedia untuk umum.


9. Pemeriksaan Kesehatan


Permintaan standar
Sekarang Anda dapat mengikuti tautan ke formulir Anda, klik Tetapkan default dan Kirim.
Dengan cara ini Anda akan membuat permintaan yang akan pergi melalui API Gateway ke AWS Lambda.

Jika Anda mengkonfigurasi semuanya dengan benar setelah beberapa saat, Anda akan menerima pesan seperti:
Berhasil: Pesan 0102016f28b06243-ae897a1e-b805-406b-9987-019f21547682-000000 telah berhasil dikirim.

Ini berarti bahwa pesan berhasil dikirim. Periksa kotak surat Anda yang Anda tentukan di templat SAM. Jika Anda belum mengubah templat, maka surat itu akan berada dalam format ini:

Anda juga dapat membuka DynamoDB dan memastikan bahwa entri baru telah muncul.

Fitur awal yang dingin
Saya pikir Anda memperhatikan bahwa pesan tentang pengiriman yang berhasil datang setelah waktu yang cukup lama. Hal ini disebabkan oleh fakta bahwa layanan AWS Lambda, setelah menerima permintaan untuk pemrosesan, mulai meningkatkan contoh aplikasi Java Anda, dan ini termasuk mengangkat wadah dengan sistem operasi dan JRE, memuat classpath, menginisialisasi semua variabel, dan hanya setelah itu adalah awal dari handleRequest ( ) . Ini disebut awal yang dingin.

Coba isi dan kirimkan formulir lagi. Kali ini jawabannya datang hampir secara instan, bukan? Jika lebih dari 20-30 menit telah berlalu antara permintaan pertama dan kedua, hasilnya dapat bervariasi.
Apa alasannya? Dan dengan fakta bahwa AWS Lambda cache sudah menggunakan wadah dengan lambdas untuk digunakan kembali. Ini mengurangi waktu inisialisasi seluruh konteks untuk memulai metode.
Tidak ada korelasi yang jelas untuk berapa lama lambda di-cache tergantung pada apa pun, tetapi beberapa orang secara eksperimental menemukan bahwa ini secara langsung tergantung pada nilai RAM yang dipilih. Artinya, lambda dengan memori 128MB akan tersedia lebih lama daripada dengan 3GB. Mungkin ada parameter lain, misalnya, beban rata-rata di wilayah di mana lambda Anda dieksekusi, tetapi ini tidak akurat.
Karena itu, coba sendiri dan rencanakan waktu caching jika Anda menggunakan permintaan sinkron.

Pemanasan
Sebagai opsi tambahan, Anda dapat menggunakan pemanas lambda. Dalam kode, saya menambahkan cek header X-WAMP-UP . Jika ada, lambda hanya mengembalikan respons tanpa mengeksekusi logika bisnis, tetapi wadah akan siap untuk panggilan berikutnya.
Anda dapat memanggil lambda Anda sendiri, misalnya, dengan timer mahkota, menggunakan CloudWatch. Ini akan membantu jika Anda tidak ingin pelanggan Anda bergabung dengan Cold Start.
Dalam formulir HTML, Anda dapat menemukan tombol mode WarmUp, yang menambahkan header khusus ini ke permintaan. Anda dapat memverifikasi bahwa pengiriman surat atau penulisan ke basis data tidak terjadi, tetapi respons dari lambda datang, dan panggilan selanjutnya untuk permintaan sebenarnya tidak akan memakan banyak waktu.

Meringkas


Selama artikel tersebut, kami melewati semua tahap utama dari desain aplikasi hingga rilis dalam apa yang disebut produksi .
Saya berharap bahwa orang-orang yang telah mendengar tentang Serverless dan AWS Lambda, tetapi yang tidak memiliki pengalaman praktis, akan dapat menggunakan panduan ini dan dapat merasakan bahwa ini memberikan keuntungan yang signifikan dalam kecepatan penyebaran beberapa solusi perangkat lunak dan bukan hanya itu.

Manfaat
Bagi saya sendiri, saya telah mengidentifikasi keuntungan yang paling berharga:
  • Free Tier , , .
  • 1-2 . /.
  • , , .
  • Serverless . , β€” . SAM AWS. AWS , , .
  • Lambda , .
  • Serverless , , . ELK/Promethes/Grafana .
  • , API. API Key, IAM , .
  • Yah, mungkin yang paling mendasar adalah Serverless. Tidak perlu memikirkan instance EC2 mana yang harus Anda bayar, cara mengkonfigurasi akses ke sana, cara mengkonfigurasi peringatan jika terjadi crash aplikasi, mengkonfigurasi penskalaan otomatis, dll.


Kekurangan
Sayangnya, ini tidak berarti bahwa sekarang Anda dapat mengambil solusi ini dan menggunakannya sebagai bagian dari sistem perusahaan alih-alih layanan microser favorit Anda di cuber. Sangat sering, Anda perlu menggali lebih dalam untuk menentukan solusi yang lebih cocok dalam kasus tertentu.
Dan saya juga punya komentar yang harus Anda perhatikan ketika memilih Lambda:
  • Mulai dingin. Terutama signifikan saat menggunakan kueri sinkron, dan bahasa seperti Java atau C #. Masalahnya cukup diselesaikan dengan pemanasan, tetapi lebih baik memikirkan solusi seperti itu di muka dan membandingkan biaya dan kemungkinan manfaatnya
  • . 3GB , 2 , , - Fargate, , .
  • API, , .
  • / 1-2 Serverless, .
  • Jika Anda sudah memiliki infrastruktur untuk mengembangkan layanan microser dan CI / CD di Coober, akan lagi menjadi masalah untuk berdebat (terutama untuk manajer) kebutuhan untuk mendukung proses CI / CD lainnya.
  • Nah, di mana tanpa pengujian. Dan untuk menguji lambda untuk throughput cukup sulit, karena masih berbeda dari pengujian kinerja biasa, dan ada banyak faktor yang perlu dipertimbangkan.


Penggunaan
Secara umum, penggunaan AWS Lambda dan layanan Serverless lainnya sangat cocok dengan prinsip-prinsip async dan pengembangan yang didorong oleh peristiwa. Dan dengan menggunakan pemrosesan tidak sinkron, hasil optimal dapat dicapai baik dari segi kinerja maupun biaya. Berikut adalah daftar solusi di mana solusi Serveress akan memainkan peran penting:


PS


Karena artikelnya ternyata sangat luas, dan saya tidak ingin mengembang lebih jauh, saya kemungkinan besar akan menyiapkan kelanjutan di mana saya akan menunjukkan bagaimana melakukan pengembangan dengan lebih efisien menggunakan semua fitur dari konsol AWS, kerangka kerja SAM, dan bahkan IntelljIDEA. Ya, karena saya menghilangkan bagian pengujian, saya akan mencoba menjelaskan aspek pengembangan ini secara lebih rinci. Juga, jika Anda ingin menambahkan apa ke artikel berikutnya, atau pertanyaan, jangan ragu untuk menulis di komentar atau dalam pesan pribadi.

Ditambahkan: AWS Lambda dalam Aksi. Bagian 2

Beberapa tautan penting dan bermanfaat dari artikel:

Source: https://habr.com/ru/post/undefined/


All Articles