一、说明
- 第 1 部分:Python 包:数据人员入门(第 1 部分,共 2 部分),探讨了 Python 模块、Python 包的基础知识以及如何将模块导入您自己的项目。
- 第 2 部分:Python 包:数据人员入门(第 2 部分,共 2 部分),介绍了依赖项管理和虚拟环境。
- 第 3 部分:构建 Python 项目的最佳实践,涵盖了构建项目的 9 个最佳实践和示例。
- 第 4 部分:从 Python 项目到 Dagster 管道,我们探讨了设置 Dagster 项目以及数据资产的关键概念。
- 第 5 部分:环境变量 在 Python 中,我们将介绍环境变量的重要性以及如何使用它们。
- 第 6 部分:类型提示,或类型提示如何减少错误。
- 第 7 部分:工厂模式,或学习设计模式,它们是软件设计中常见问题的可重用解决方案。
注册我们的时事通讯以获取所有更新!
今天,我们将看看在 Python 中管理环境变量。环境变量提供了一种以非硬编码方式配置应用程序的方法,使您能够在不更改实际代码的情况下修改应用程序行为。
在生产环境中参数化数据管道时,环境变量的使用变得尤为重要。它们允许将数据库凭据或 API 密钥等敏感信息存储在代码库之外。这不仅增强了安全性,而且还使代码更具可移植性和更易于管理。
在本文中,我们将揭开 Python 中环境变量概念的神秘面纱,解释它们是如何工作的,为什么它们很重要,以及如何有效地利用它们来增强你的 Python 编程技能。我们将引导您完成实际示例,介绍技术和最佳实践,使您能够设置 Python 环境、配置重要工具的路径或设置脚本所依赖的环境变量。
目录
- 什么是环境变量?
- 环境变量的范围和生命周期
- 环境变量和配置
- 管理环境变量的最佳实践
二、什么是环境变量?
Python 提供了一个名为 os 的内置模块作为与底层操作系统交互的接口。此模块提供了一个类似字典的对象,允许您与环境变量进行交互。os.environ
os.environ
充当环境变量名称与其值之间的映射。它可以在 Python 程序中访问、修改或创建环境变量。让我们分解一下。
2.1 读取 Python 环境变量
要在 Python 中读取环境变量的值,您需要将其视为字典并按其名称访问该变量。下面是一个示例:os.environ
import os# Accessing an environment variable
print(os.environ['HOME'])
这将打印您的默认“主”目录:在 macOS 系统或 Linux 系统上:/Users/username
/home/username
/Users/elliot
此处是环境变量的名称,该变量通常存储当前用户主目录的路径。如果环境变量存在,其值将打印到控制台。但是,如果环境变量不存在,这将引发 .HOME
KeyError
若要避免此错误,可以选择使用该方法,该方法允许您提供在找不到环境变量时将返回的默认值:get
import os# Accessing an environment variable with a default value
print(os.environ.get('HOME', '/home/default'))
在这种情况下,如果变量不存在,则将返回字符串。HOME
/home/default
您可以使用以下脚本打印和浏览所有环境变量:
import osfor name, value in os.environ.items():print("{0}: {1}".format(name, value))
2.2 修改和添加环境变量
您可以通过为对象中的键赋值来修改现有环境变量的值,也可以添加新环境变量:os.environ
import os# Setting an environment variable
os.environ['MY_VARIABLE'] = 'foo'# Now, the new variable is accessible via os.environ
print(os.environ['MY_VARIABLE']) # Outputs: foo
在这种情况下,将创建环境变量并将其设置为 。但是,只要当前进程正在运行,此变量就可用。MY_VARIABLE
foo
请记住,对对象所做的更改是进行更改的进程的本地更改。如果在 Python 脚本中设置环境变量,则该变量可用于该脚本及其创建的任何子进程,但在更广泛的环境或其他不相关的进程中不可见。os.environ
以这种方式设置的环境变量的范围和生命周期仅限于分配它们的当前进程。由于这可能是一个复杂的问题,请考虑我们上面的例子。
我们的 Python 脚本设置了一个名为 的环境变量。假设我们调用另一个 Python 脚本并创建一个子进程(进程 1)。第二个脚本也将能够访问 。但是,如果您尝试从第一个脚本(进程 2)未启动的其他 Python 脚本或在运行脚本后从命令行访问,您会发现该脚本不可用 - 它不是更广泛环境的一部分。但是,这两个进程都可以访问任何全局变量,如上例所示。MY_VARIABLE
foo
MY_VARIABLE
MY_VARIABLE
MY_VARIABLE
GLOBAL_VARIABLE
三、环境变量的范围和生命周期
要跨不同的会话或进程持久化环境变量,您需要在 Python 之外的操作系统环境中设置它们。执行此操作的方法因操作系统和 shell 而异,但通常涉及编辑 shell 配置文件或使用命令行实用程序。
让我们回顾一下我们谈论操作系统、shell 和命令行实用程序时的意思。
3.1 操作系统、外壳和命令行实用程序
操作系统是在计算机上运行的最基本的软件。它充当用户和计算机硬件之间的中介,使用户能够执行程序、管理文件以及与设备交互。操作系统允许您管理文件、进程、内存和安全性。流行的操作系统包括Microsoft Windows,macOS和Linux发行版(如Ubuntu,Fedora等)。
外壳允许您与计算机的操作系统进行交互。作为数据工程师,您将主要使用命令行 shell,它允许您通过将命令作为文本键入来向操作系统发出命令。
当您打开终端窗口(在 Linux 或 Mac 上)或命令提示符(在 Windows 上)时,您将与 shell 进行交互。您可以键入一个命令,例如运行 Python 脚本,或者(在 Linux 或 Mac 上)或(在 Windows 上)列出当前目录中的文件。这些都是与命令行外壳交互的示例。python my_script.py
ls
dir
外壳配置文件是外壳在启动时读取的特殊文件。作为数据工程师,您可能会使用它们来设置每次打开新终端窗口时应该可用的环境变量。这使得它更容易,因为 shell 每次启动时都会自动执行。
命令行实用程序是设计为从文本界面使用的程序。无需单击按钮,您可以从命令行运行 Python 脚本(例如,使用 pip 安装 Python 包或使用 jupyter 启动 Jupyter 笔记本服务器)或从命令行使用 Git 进行版本控制。其他例子包括Unix命令行实用程序,如grep,awk,sed,用于搜索,过滤和转换文本数据;curl 和 wget 从网络下载文件;和 ssh 以在使用分布式系统或基于云的资源时与远程服务器连接。
3.2 进程级范围
在 Python 脚本中设置或修改的环境变量可以在同一进程中访问。如果脚本启动另一个进程(称为子进程),则该子进程将继承其父进程的环境,包括父进程设置的任何环境变量。但是,如果两个单独的进程都设置了一个同名的环境变量,它们就不会相互干扰;每个进程都有自己独立的环境副本。os.environ
3.3 用户级和系统级范围:
如果要设置特定用户的所有进程或系统上的所有进程都可以访问的环境变量,则需要在 Python 之外的 shell 或操作系统的配置文件中执行此操作。这些变化的范围更广,但不是立竿见影的;它们通常要求您启动新的 shell 会话或重新启动系统。
3.4 更改的持久性
在脚本完成执行后,不会保留在 Python 脚本中使用的环境变量所做的更改。这意味着,如果您运行设置环境变量的脚本,然后运行另一个脚本或返回到 shell,则与第一个脚本相比的环境变量更改将不可见。os.environ
这种非持久性实际上可能是一个优势。它允许您的脚本根据需要修改其环境以实现其自身目的,而不会影响其他进程或留下可能影响未来 shell 会话的持久更改。
如果要设置在多个 shell 会话中持续存在或可供其他应用程序使用的环境变量,则必须在 shell 级别或通过操作系统的接口设置环境变量。执行此操作的确切方法取决于您的操作系统和 shell。但是,这应该小心完成,因为它可能会对系统的行为产生广泛的影响。
四、环境变量和配置
利用环境变量进行配置被认为是软件开发中的最佳实践,原因有两个。
首先,它们允许您避免直接在代码中存储敏感信息,如密码、API 密钥或数据库 URI。这可以防止此类信息无意中暴露,例如,通过包含在版本控制存储库中。
其次,它们还使您的代码更具可移植性。当开发、测试和生产环境需要不同的设置时,可以通过环境变量控制这些设置,而无需修改代码。如果需要更改配置值,可以在环境变量中更新该值,而无需更改应用程序的代码,也无需重新部署应用程序。
4.1 对敏感信息使用环境变量
要将环境变量用于敏感数据(如 API 密钥或数据库 URI),只需确保在运行 Python 代码的环境中设置相关的环境变量。
例如,您可能有一个连接到数据库并使用 API 的 Python 脚本。您可以使用环境变量,而不是在脚本中硬编码数据库 URI 和 API 密钥:
import os# Accessing sensitive data from environment variables
db_uri = os.environ.get('DATABASE_URI')
api_key = os.environ.get('API_KEY')
在这种情况下,需要在运行脚本的环境中设置 and 环境变量。这可以在启动脚本之前在 shell 中完成,也可以在 shell 启动时读取的配置文件中完成。DATABASE_URI
API_KEY
4.2 不要硬编码!
在代码中硬编码敏感信息(即直接将特定值或参数直接嵌入代码中),例如 API 密钥或数据库 URI,这是一个重大的安全风险。如果您的代码是共享或公开的,例如,在 GitHub 上,任何可以看到代码的人都可以看到这些敏感详细信息。这可能允许未经授权访问您的数据库或滥用您的 API 密钥。
即使您不打算共享代码,仍然存在风险。硬编码信息保留在版本控制历史记录中,因此以后访问存储库的任何人都可以在旧提交中找到敏感数据。
硬编码还会降低代码的灵活性。如果要使用其他数据库进行测试或需要更改 API 密钥,则需要更改并重新部署代码。相比之下,如果您使用环境变量来获取此类详细信息,则可以在需要时简单地更改环境变量,而无需接触代码。
五、管理环境变量的最佳做法
使用所谓的“dotenv”文件,您可以将配置存储在应用程序启动时加载的文件中。该库提供了一种将环境变量加载到 Python 中的对象中的方法。以下是使用它的方法:.env
python-dotenv
.env files
os.environ
如果您尚未安装该库,请安装该库。您可以通过 pip:python-dotenv 安装它
pip install python-dotenv
在项目根目录中创建一个文件并向其中添加一些变量。该文件可能看起来像这样:.env
DATABASE_URI=postgres://myuser:mypassword@localhost:5432/mydatabase
API_KEY=abcdef123456
注意:不应将此文件提交到您的版本控制系统。您应该添加到您的文件中以确保 Git 忽略它..env.gitignore
在 Python 脚本中,您可以使用以下命令加载文件:python-dotenv.env
from dotenv import load_dotenv# Load the .env file
load_dotenv()# Now you can access the variables
import os
db_uri = os.getenv('DATABASE_URI')
api_key = os.getenv('API_KEY')
该函数读取文件并将其内容加载到 .load_dotenv()
.env
os.environ
5.1 生产中的环境变量
在开发中,使用一种简单而安全的方式来设置应用程序可以使用的环境变量通常是有益的。根据您的特定生产环境,这可能看起来非常不同。
您可以利用特定服务来管理云环境中的机密:
- 亚马逊云科技 (AWS) 密钥管理器或参数存储
- Azure Key Vault
- 谷歌云秘密管理器
您还可以在容器编排系统中使用特定的机密管理对象:
- Kubernetes Secrets或与HashiCorp Vault等工具的集成
- 码头工人的秘密
我们将看到这在 Amazon Elastic Container Service (Amazon ECS) 和 Kubernetes 中是如何工作的。
5.2 Amazon ECS 任务定义和 AWS Secrets Manager
您可以直接在任务定义中定义环境变量。下面是一个 JSON 格式的示例:
{"containerDefinitions": [{"name": "my-container","image": "my-image","environment": [{"name": "ENV_VARIABLE_NAME","value": "value"}]}]
}
对于敏感数据,您可以将密钥存储在 AWS 密钥管理器中,并在任务定义中引用它们:
{"containerDefinitions": [{"name": "my-container","image": "my-image","secrets": [{"name": "DB_PASSWORD","valueFrom": "arn:aws:secretsmanager:region:aws_account_id:secret:secret_name"}]}]
}
5.3 Kubernetes ConfigMaps
在 Kubernetes 中,您可以使用非敏感数据的 ConfigMaps 和敏感数据的 Secrets 来管理环境变量。
下面是使用 ConfigMap 定义环境变量的示例。首先,在 yaml 文件中创建一个配置映射:
apiVersion: v1
kind: ConfigMap
metadata:name: app-config
data:ENV_VARIABLE_NAME: value
然后,在 Pod 中引用配置映射:
apiVersion: v1
kind: Pod
metadata:name: my-app
spec:containers:- name: my-containerimage: my-imageenvFrom:- configMapRef:name: app-config
或者,您可以将 Kubernetes Secrets 用于敏感数据。首先,使用您的密钥创建一个 bash 文件:
kubectl create secret generic app-secrets --from-literal=DB_PASSWORD=secretpassword
然后,在 Pod 中引用密钥:
apiVersion: v1
kind: Pod
metadata:name: my-app
spec:containers:- name: my-containerimage: my-imageenv:- name: DB_PASSWORDvalueFrom:secretKeyRef:name: app-secretskey: DB_PASSWORD
5.4 何时使用环境变量
环境变量最适合用于因部署环境而异的配置数据以及不应直接存储在代码中的敏感数据。
- 数据库 URL 和其他相关设置
- API 密钥、令牌或机密
- 外部服务的主机名或 URL
- 不应在代码中公开的任何类型的敏感数据
- 可能需要经常更改的微调配置选项
- 大量二进制数据
- 最好存储在数据库或其他存储服务中的数据
5.5 管理机密和敏感信息的其他注意事项
管理机密和敏感信息是环境变量的主要用途之一。通过将此数据存储在环境变量中,可以将其排除在代码之外。这可以防止机密在版本控制系统中公开,并允许您在不修改代码的情况下更改机密。
但是,仅使用环境变量不足以保证机密安全。您还应该:
- 避免记录环境变量,因为不应看到机密的人员通常可以访问日志。
- 请注意可能暴露环境变量的错误消息。
- 确保版本控制系统忽略您的文件。
.env
5.6 环境变量促进一致性
保持开发和生产环境的一致性有助于防止代码在不同环境中行为不同时出现的错误。环境变量可以帮助解决这个问题。
通过将环境变量用于因环境而异的配置,可以确保代码本身在所有环境中保持一致。只有环境变量中的设置会更改。
这意味着只需设置适当的环境变量,即可在开发、测试或生产环境中运行代码。如果您的代码在一个环境中工作,则更有可能在其他环境中工作。
但是,这需要纪律。您应该抵制对配置数据进行硬编码的诱惑,而是始终使用环境变量。此外,团队的所有成员都应了解如何在其开发环境中设置和使用环境变量。这可以通过使用python-dotenv等工具来实现,这些工具简化了管理环境变量的过程。
六、结论
我们已经了解了使用环境变量如何帮助更安全地管理生产应用程序,并参数化数据工程管道。如果您有任何问题或需要进一步澄清,感谢您的阅读!