aws-sam-cliのlocalクライアントからホストのポートにアクセスできない
以下のコマンドを実行した後は、 host.docker.internal で通信が可能になる。
sysctl -w net.ipv4.conf.docker0.route_localnet=1
iptables -t nat -I PREROUTING -i docker0 -d 172.17.0.1 -p all -j DNAT --to 127.0.0.1iptables -t filter -I INPUT -i docker0 -d 127.0.0.1 -p all -j ACCEPTいつも実行するのは面倒なので、docker.service をsystemd editを使って一部の設定を更新するのように更新して、毎回実行するように変更してもいいかもしれない。
[Service]ExecStartPost=/bin/sysctl -w net.ipv4.conf.docker0.route_localnet=1ExecStartPost=/sbin/iptables -t nat -I ...ExecStartPost=/sbin/iptables -t filter -I ...以下は調査した記録。
Linuxで、Lambda関数から localhost:8080 (ポート番号はなんでもいい)にアクセスするコードを sam local start-api で実行するとエラーになる。macOSなら動作するらしい。
ローカル側でネットワーク接続を受け付けるサービスを実行する。pkgsite を使っているが、動いていればなんでもいい。
pkgsite -http=:8080この状態で、 localhost:8080 にアクセスするLambda関数を実装する。
template.yaml
Section titled “template.yaml”AWSTemplateFormatVersion: '2010-09-09'Transform: AWS::Serverless-2016-10-31
Resources: MyLambdaFunction: Type: AWS::Serverless::Function Properties: PackageType: Image Architectures: - x86_64 Events: MyAPI: Type: Api Properties: Path: / Method: GET Metadata: DockerContext: . Dockerfile: Dockerfile
Outputs: ApiURL: Description: URL of the API Value: !GetAtt ApiURL.ArnDockerfile
Section titled “Dockerfile”FROM golang:1.23 AS buildWORKDIR /go/srcCOPY main.go go.mod go.sum ./RUN go build -o /go/bin/bootstrap
FROM public.ecr.aws/lambda/provided:al2023COPY --from=build /go/bin/bootstrap /var/runtime/bootstrapENTRYPOINT ["/var/runtime/bootstrap"]main.go
Section titled “main.go”package main
import ( "context" "io" "net/http"
"github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda")
type Response events.APIGatewayProxyResponse
func HandleRequest(ctx context.Context) (Response, error) { resp, err := http.Get("http://localhost:8080/") if err != nil { return Response{StatusCode: 500, Body: err.Error()}, nil } b, err := io.ReadAll(resp.Body) if err != nil { return Response{StatusCode: 500, Body: err.Error()}, nil } return Response{StatusCode: 200, Body: string(b)}, nil}
func main() { lambda.Start(HandleRequest)}go.mod と go.sum は省略。
関数をテスト
Section titled “関数をテスト”以下のコマンドでサーバを立ち上げる。
sam buildsam local start-api他の端末からアクセスするが、ここではエラーになる。
$ curl http://localhost:3000/
Get "http://localhost:8080/": dial tcp [::1]:8080: connect: connection refusedプロトコルがHTTPSの場合は以下のようなエラーになる。net/http としてはTLSを期待しているところに、サーバから生のHTTPが返却されているためエラーとしているようだが、なぜこうなるのかは分からない。
$ curl http://localhost:3000/
Get "https://localhost:8080/": http: server gave HTTP response to HTTPS clientDockerは host-gateway という名前の特別なホストを管理していて、これを使ってLinuxでも host.docker.internal を再現できる。
docker run -ti --rm --add-host=host.docker.internal:host-gateway debian:bookworm bashこれを実行すると、コンテナ内の /etc/hosts にエントリが追加される。
172.17.0.1 host.docker.internalルーティング的にもコンテナの外に向いている。
root@38acc6ae11d9:/# ip rdefault via 172.17.0.1 dev eth0172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
root@38acc6ae11d9:/# ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever257: eth0@if258: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever—add-hosts オプションは、 docker compose の場合 extra_hosts プロパティで行う。
---services: service1: extra_hosts: - host.docker.internal:host-gatewaysam-cli では —add-host オプションがあるので使う。
sam local start-api --add-host=host.docker.internal:host-gatewayコード側で、アクセスするURLを変更する。
resp, err := http.Get("http://localhost:8080/")resp, err := http.Get("http://host.docker.internal:8080/")- Is it possible to connect to a database running on localhost?
- Add —add-host cli option to expose docker container option with the same name.
それでも動作しない場合
Section titled “それでも動作しない場合”iptables を実行しているなら停止すると解決するかもしれない。Dockerで直接 —add-host して試したときの結果は以下のようになる。
$ docker run -ti --rm --add-host=host.docker.internal:host-gateway debian:bookworm bash
root@ddf3af11eef1:/# apt updateroot@ddf3af11eef1:/# apt install curlroot@ddf3af11eef1:/# curl http://host.docker.internal:8080/curl: (7) Failed to connect to localhost port 8080 after 0 ms: Couldn't connect to server手元だと iptables を止めたら疎通するようになった。
run0 systemctl stop iptables.servicerun0 systemctl restart docker.serviceiptables だけ停止すると、sam-cli で start-api する度に以下のエラーが出力されるので、なんらかの操作をDockerから iptables へ行っているとは思うが詳細は調べていない。
Unable to enable DNAT rule: (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 127.0.0.1 --dport 8949 -j DNAT --to-destination 172.17.0.2:8080 ! -i docker0: iptables: No chain/target/match by that name.