Problem using environment variables with Astro and Google Cloud Run

huangapple go评论70阅读模式
英文:

Problem using environment variables with Astro and Google Cloud Run

问题

我正在尝试将Astro应用程序部署为turborepo中更大设置的一部分。该应用程序使用服务器端渲染,并已将其部署到Google Cloud Run中的Docker容器中。

在本地开发时,我可以将以下内容放入我的环境文件中:

PUBLIC_VALUE="https://example.com/example".

然后,当我需要从客户端从应用程序中进行对该URL的请求时,我可以从某个.js文件中执行以下操作:

const BASE_HOST = import.meta.env.PUBLIC_VALUE
export const BASE_URL = BASE_HOST

然后,在React组件的某个地方,我可能会这样做:

const response = await axios.post(`${BASE_URL}/signin`, { emailAddress, password });

当在本地运行yarn run dev时,所有这些都可以很好地工作。但是,当部署到Google Cloud Run中的Docker容器时,我发现该变量为未定义。

我非常习惯使用Google Cloud Run,并且已成功使我的Astro应用程序运行无任何问题。通常,在Google Cloud中,我可以在Google Cloud控制台中设置环境变量,然后可以使用process.env从我的代码中获取它们。我非常习惯在其他应用程序中执行此操作。但是,当我尝试使用上面的代码获取我的值时,"import.meta.env.PUBLIC_VALUE" 在客户端是未定义的,console.log() 显示值明显为未定义。

为什么它是未定义的?

我在下面提供了有关我的设置的更多信息:

这是astro.config的内容:

import { defineConfig } from 'astro/config';
import tailwind from "@astrojs/tailwind";
import react from "@astrojs/react";
import 'dotenv/config';
import node from "@astrojs/node";

export default defineConfig({
  integrations: [tailwind(), react()],
  output: 'server',
  adapter: node({
    mode: "standalone"
  })
});

这是我的Dockerfile:

FROM node:18-alpine AS base

RUN apk add --no-cache libc6-compat
RUN apk update
RUN yarn global add turbo

FROM base AS builder
WORKDIR /app
COPY . .
RUN turbo prune --scope=admin-frontend --docker

FROM base AS installer
WORKDIR /app

COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/yarn.lock ./yarn.lock
RUN yarn install

COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json

RUN turbo run build --filter=admin-frontend...

FROM base AS runner
WORKDIR /app

RUN addgroup --system --gid 1001 expressjs
RUN adduser --system --uid 1001 expressjs
USER expressjs
COPY --from=installer /app .

ENV HOST=0.0.0.0
ENV PORT=3000
EXPOSE 3000

CMD node ./apps/admin-frontend/dist/server/entry.mjs

正如我之前提到的,使用此设置,该项目在本地Docker中和Cloud Run上构建和运行非常顺畅。

英文:

I am trying to deploy an Astro application as part of a bigger setup in turborepo. The application uses server side rendering, and I am have deployed it to Google Cloud Run in a docker container.

When developing locally I can put this in my env file:

PUBLIC_VALUE="https://example.com/example".

And then when I need to make a request to the url in my application from the client side I can do the following from a .js file somewhere

const BASE_HOST = import.meta.env.PUBLIC_VALUE
export const BASE_URL = BASE_HOST

Then somewhere in a React component i might do this:

const response = await axios.post(${BASE_URL}/signin`, { emailAddress, password });

And it all all works very well when running locally with yarn run dev. However whern deoployed to Google Cloud run in a docker container I can see that the variable is undefined.

I am very used to using GoogleCloud Run. And I have sucesfully gotten my Astro application to run without any problems. Usually in Google Cloud I can set environmental variables in the Google Cloud Console, and then I can fetch them from my code with process.env. I am very used to doing thus with other applications. However when I try fetching my value with the above code "import.meta.env.PUBLIC_VALUE" is undefined at clientside. console.log() with the value clearly shows.

Why is it undefined?

I have included some more info on my setup below:

This is the content of astro.config.


import { defineConfig } from 'astro/config';
import tailwind from "@astrojs/tailwind";
import react from "@astrojs/react";
import 'dotenv/config';
import node from "@astrojs/node";

// https://astro.build/config
export default defineConfig({
  integrations: [tailwind(), react()],
  output: 'server',
  adapter: node({
    mode: "standalone"
  })
});

This is my Dockerfile:


FROM node:18-alpine AS base

# The web Dockerfile is copy-pasted into our main docs at /docs/handbook/deploying-with-docker.
# Make sure you update this Dockerfile, the Dockerfile in the web workspace and copy that over to Dockerfile in the docs.

# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
RUN apk update
RUN yarn global add turbo

FROM base AS builder
# Set working directory
WORKDIR /app
COPY . .
RUN turbo prune --scope=admin-frontend --docker

# Add lockfile and package.json's of isolated subworkspace
FROM base AS installer
WORKDIR /app

# First install dependencies (as they change less often)
COPY .gitignore .gitignore
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/yarn.lock ./yarn.lock
RUN yarn install

# Build the project and its dependencies
COPY --from=builder /app/out/full/ .
COPY turbo.json turbo.json

# Uncomment and use build args to enable remote caching
# ARG TURBO_TEAM
# ENV TURBO_TEAM=$TURBO_TEAM

# ARG TURBO_TOKEN
# ENV TURBO_TOKEN=$TURBO_TOKEN

RUN turbo run build --filter=admin-frontend...

FROM base AS runner
WORKDIR /app

# Don't run production as root
RUN addgroup --system --gid 1001 expressjs
RUN adduser --system --uid 1001 expressjs
USER expressjs
COPY --from=installer /app .

ENV HOST=0.0.0.0
ENV PORT=3000
EXPOSE 3000

CMD node ./apps/admin-frontend/dist/server/entry.mjs

As mentioned the project builds and runs smoothly, locally in docker, and on Cloud Run with this setup.

答案1

得分: 1

  1. 使用 @beam-australia/react-env 获取环境变量。您可以参考这个文档

  2. 使用第三方库,例如 react-env,如NextJS - 如何获取运行时环境变量中建议的,还可以参考这个线程

同时确保 .env 文件未包含在 .gitignore 中。

更新

根据文档和这个GitHub 线程。在加载其他文件之前,Astro会分析配置文件。因此,使用 import.meta.env 访问在 .env 文件中设置的环境变量是不可能的。

> Astro在加载其他文件之前会评估配置文件。因此,您不能使用 import.meta.env 访问在 .env 文件中设置的环境变量。
>
> 您可以在配置文件中使用 process.env 来访问其他环境变量,比如由CLI设置的变量。
>
> 您还可以使用 ViteloadEnv 辅助程序手动加载 .env 文件。
>
> astro.config.mjs
> >import { loadEnv } from "vite"; >const { SECRET_PASSWORD } = loadEnv(import.meta.env.MODE, >process.cwd(), ""); >

英文:

You can try the following troubleshoot options:

  1. Use @beam-australia/react-env to fetch ENV variables . You can follow this documentation.

  2. Using a third-party library, such as react-env, as advised in: NextJS - How to get runtime environment variables and also have a look at this thread

Also make sure .env files are not included in .gitignore

Update

As per the documentation and this github thread. Before loading your other files, Astro analyzes configuration files. As a result, accessing environment variables that were set in.env files is not possible using import.meta.env.

> Astro evaluates configuration files before it loads your other files. As such, you can’t use import.meta.env to access environment variables that were set in .env files.
>
>You can use process.env in a configuration file to access other environment variables, like those set by the CLI.
>
>You can also use Vite’s loadEnv helper to manually load .env files.
>
> astro.config.mjs
>
>import { loadEnv } from "vite";
>const { SECRET_PASSWORD } = loadEnv(import.meta.env.MODE, >process.cwd(), "");
>

答案2

得分: 0

在尝试了许多来自Roopa M的建议后,我最终只能接受谷歌云运行(Google Cloud Run)处理环境变量和Astro之间肯定存在一些奇怪的错误。

我的解决方法是从客户端永远不使用环境变量。我们可以通过从process.env服务器端获取它们,然后在客户端将它们解析到特定组件来实现这一点。可以在这里看到一个使用这种方法的注册页面示例:

---
import SignUp from "../components/SignUp";
import { BASE_URL } from '../utils/api';
---

<html lang="en">
	<head>
		<meta charset="utf-8" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<title>Sign Up</title>
	</head>
	<body>
		<div>
			<SignUp BASE_URL={BASE_URL}  client:load />
		</div>
	</body>
</html>

正如可以看到的那样,该变量只是直接从服务器端传递到组件中。

然后在api.js中导出如下:

export const BASE_URL = import.meta.env.PUBLIC_BACKEND_BASE_URL ?? process.env.PUBLIC_BACKEND_BASE_URL

可以一步完成,但将其放在自己的文件中使我更容易处理有关该变量的一些自定义逻辑。

我选择优先使用import.meta.env.PUBLIC_BACKEND_BASE_URL,因为在其他环境中,如本地运行,它实际上是有效的。因此,通过使用nullish coalescing运算符,如果可能的话,我从import.meta.env获取。这样,在Google Cloud Run中,它将只从process.env导入,因为另一个语句将返回undefined。

英文:

After trying lots of suggestions from Roopa M
I ended up just accepting that there must be some weird bug between how Google Cloud Run handles environmental variables and Astro.

My workaround has been to just never use environmental variables client side. We can do this by fetching them serverside from process.env and then parsing them to the specific component client side. An example of a signup page using this can be seen here:


---
import SignUp from &quot;../components/SignUp&quot;;
import { BASE_URL } from &#39;../utils/api&#39;;
---

&lt;html lang=&quot;en&quot;&gt;
	&lt;head&gt;
		&lt;meta charset=&quot;utf-8&quot; /&gt;
		&lt;link rel=&quot;icon&quot; type=&quot;image/svg+xml&quot; href=&quot;/favicon.svg&quot; /&gt;
		&lt;title&gt;Sign Up&lt;/title&gt;
	&lt;/head&gt;
	&lt;body&gt;
		&lt;div&gt;
			&lt;SignUp BASE_URL={BASE_URL}  client:load /&gt;
		&lt;/div&gt;
	&lt;/body&gt;
&lt;/html&gt;

As can be seen the variable is just passed from serverside directly in the component.

Then in api.js its exported like this:

export const BASE_URL = import.meta.env.PUBLIC_BACKEND_BASE_URL ?? process.env.PUBLIC_BACKEND_BASE_URL

It could be done in one step but having it in its own file makes it easier for me to handle some custom logic regarding the variable.

I have chosen to prioritise import.meta.env.PUBLIC_BACKEND_BASE_URL, because in other environments such running locally it actually works. So by using the nullish coalescing operator I fetch from import.meta.env if possible. That way in Google Cloud run it will just import from process.env since the other statement will return undefined.

huangapple
  • 本文由 发表于 2023年6月29日 12:25:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76578045.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定