Docker容器在AWS Fargate运行时出现本地未触发的JavaScript错误
我构建了基于node:18.4-alpine3.16的Docker容器,本地运行无错误,但部署到AWS Fargate后,容器虽能启动却抛出本地未出现的JavaScript运行时错误。
示例代码:
const fcl = require('@onflow/fcl'); const storefrontAddress = 0x9a0766d93b6608b7; const eventOfferMade = `A.${fcl.sansPrefix(storefrontAddress)}.SaleOfferAvailable`;
这段代码本地运行无报错,但在Fargate上运行时抛出n.replace is not a function错误。排查后发现fcl.sansPrefix方法要求接收字符串类型,而传入的是十六进制数字,因此触发错误。将数字强制转换为字符串后即可在Fargate上正常运行:
const eventOfferMade = `A.${fcl.sansPrefix(String.toString(storefrontAddress))}.SaleOfferAvailable`;
请问:
- 容器在Fargate上是否会自动开启
"use strict"模式? - 是否存在JavaScript引擎差异?
- 是否有内核依赖导致此类问题?
环境信息
- 构建环境:Ubuntu 18.04
- Docker版本:
Client: Docker Engine - Community Cloud integration: v1.0.24 Version: 20.10.18 API version: 1.41 Go version: go1.18.6 Git commit: b40c2f6 Built: Thu Sep 8 23:11:34 2022 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.18 API version: 1.41 (minimum version 1.12) Go version: go1.18.6 Git commit: e42327a Built: Thu Sep 8 23:09:28 2022 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.8 GitCommit: 9cd3357b7fd7218e4aec3eae239db1f68a5a6ec6 runc: Version: 1.1.4 GitCommit: v1.1.4-0-g5fd4c4d docker-init: Version: 0.19.0 GitCommit: de40ad0
- 部署方式:通过Copilot部署到ECS,任务定义:
{ "ipcMode": null, "executionRoleArn": "arn:aws:iam::445530232977:role/geosvc-test-geowebsvr-ExecutionRole-1U946MJKGD7TT", "containerDefinitions": [ { "dnsSearchDomains": [], "environmentFiles": null, "logConfiguration": { "logDriver": "awslogs", "secretOptions": null, "options": { "awslogs-group": "/copilot/geosvc-test-geowebsvr", "awslogs-region": "us-west-2", "awslogs-stream-prefix": "copilot" } }, "entryPoint": [], "portMappings": [ { "hostPort": 8080, "protocol": "tcp", "containerPort": 8080 } ], "command": [], "linuxParameters": null, "cpu": 0, "environment": [ { "name": "COPILOT_APPLICATION_NAME", "value": "geosvc" }, { "name": "COPILOT_ENVIRONMENT_NAME", "value": "test" }, { "name": "COPILOT_LB_DNS", "value": "geosv-Publi-WFRGPHEB1KSL-217029508.us-west-2.elb.amazonaws.com" }, { "name": "COPILOT_SERVICE_DISCOVERY_ENDPOINT", "value": "test.geosvc.local" }, { "name": "COPILOT_SERVICE_NAME", "value": "geowebsvr" } ], "resourceRequirements": null, "ulimits": null, "dnsServers": [], "mountPoints": [], "workingDirectory": null, "secrets": null, "dockerSecurityOptions": [], "memory": null, "memoryReservation": null, "volumesFrom": [], "stopTimeout": null, "image": "445530232977.dkr.ecr.us-west-2.amazonaws.com/geosvc/geowebsvr@sha256:049cddcf8755a19f720a1ee27c7a33300f7b0bcdc7be5800f1bb9f1bcff1df2d", "startTimeout": null, "firelensConfiguration": null, "dependsOn": null, "disableNetworking": null, "interactive": null, "healthCheck": null, "essential": true, "links": [], "hostname": null, "extraHosts": null, "pseudoTerminal": null, "user": null, "readonlyRootFilesystem": null, "dockerLabels": null, "systemControls": [], "privileged": null, "name": "geowebsvr" } ], "placementConstraints": [], "memory": "1024", "taskRoleArn": "arn:aws:iam::445530232977:role/geosvc-test-geowebsvr-TaskRole-9S1YXNWDGRHK", "compatibilities": [ "EC2", "FARGATE" ], "taskDefinitionArn": "arn:aws:ecs:us-west-2:445530232977:task-definition/geosvc-test-geowebsvr:107", "family": "geosvc-test-geowebsvr", "requiresAttributes": [ { "targetId": null, "targetType": null, "value": null, "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" }, { "targetId": null, "targetType": null, "value": null, "name": "ecs.capability.execution-role-awslogs" }, { "targetId": null, "targetType": null, "value": null, "name": "com.amazonaws.ecs.capability.ecr-auth" }, { "targetId": null, "targetType": null, "value": null, "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" }, { "targetId": null, "targetType": null, "value": null, "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17" }, { "targetId": null, "targetType": null, "value": null, "name": "com.amazonaws.ecs.capability.docker-remote-api.1.21" }, { "targetId": null, "targetType": null, "value": null, "name": "com.amazonaws.ecs.capability.task-iam-role" }, { "targetId": null, "targetType": null, "value": null, "name": "ecs.capability.execution-role-ecr-pull" }, { "targetId": null, "targetType": null, "value": null, "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" }, { "targetId": null, "targetType": null, "value": null, "name": "ecs.capability.task-eni" } ], "pidMode": null, "requiresCompatibilities": [ "FARGATE" ], "networkMode": "awsvpc", "runtimePlatform": null, "cpu": "256", "revision": 107, "status": "ACTIVE", "inferenceAccelerators": null, "proxyConfiguration": null, "volumes": [] }
1. 关于"use strict"模式
Fargate不会自动为容器内的Node.js进程开启"use strict"模式。该模式的启用仅取决于代码本身:要么在代码顶部显式声明"use strict",要么ES模块默认启用。你的问题和严格模式无关。
2. 关于JavaScript引擎差异
你使用的是固定版本的Node.js镜像node:18.4-alpine3.16,本地和Fargate上运行的Node.js版本完全一致,对应的V8引擎版本也完全相同,不存在引擎差异导致的行为不同。
3. 关于内核依赖
Alpine Linux使用musl libc而非glibc,但Node.js官方的alpine镜像已经针对musl做了适配,且你的问题属于JavaScript类型处理错误,和系统内核、libc无关。
4. 本地与Fargate行为差异的原因
问题核心是代码中传入fcl.sansPrefix的参数类型不匹配:storefrontAddress被定义为十六进制数字,而fcl.sansPrefix要求接收字符串类型,调用字符串的replace方法时自然报错。
本地运行无报错的原因大概率是你本地测试时,该变量被无意中转换为了字符串(比如从环境变量读取,环境变量默认是字符串类型),而Fargate上是直接使用了代码中定义的数字类型。
优化建议
你当前的修复代码中String.toString(storefrontAddress)写法存在问题,String.toString是获取String构造函数的字符串表示,传入数字参数无意义,推荐使用更规范的类型转换方式:
// 方式1:调用数字的toString方法 const eventOfferMade = `A.${fcl.sansPrefix(storefrontAddress.toString())}.SaleOfferAvailable`; // 方式2:通过String构造函数转换 const eventOfferMade = `A.${fcl.sansPrefix(String(storefrontAddress))}.SaleOfferAvailable`;
内容的提问来源于stack exchange,提问作者HieroB

