linux系统的中主要有两种启用系统环境的情况,一种是 用户登录,比如进入bash或者sh等命令行操作shell界面,一种是 用户不登录,而是程序自动运行。
根据 两种情况,适用的创建永久的环境变量的方式就会有所不同。
静态环境变量 是指 内容不会变动的,是一个固定的值。
这种情况的环境变量 直接在Dockerfile种使用ENV 指定即可。
比如:
ENV JAVA_HOME=/spark/java/jre1.8.0_181 JRE_HOME=/spark/java/jre1.8.0_181 CLASSPATH=$JAVA_HOME/lib/:$JRE_HOME/lib/
ENV PATH $PATH:$JAVA_HOME/bin:/usr/local/python37/bin
ENV PYSPARK_PYTHON python3
ENV PYSPARK_DRIVER_PYTHON python3
有一种使用场景是 我们在这个镜像启动之前,是不知道 环境变量的值的,必须要等镜像启动之后才知道这个环境变量的值是什么,这种情况我们称为 动态环境变量。
比如 ${HOSTNAME} 或者 K U B E R N E T E S S E R V I C E H O S T , {KUBERNETES_SERVICE_HOST}, KUBERNETESSERVICEHOST,{KUBERNETES_SERVICE_PORT},这几个变量都是需要 镜像启动后,在相应的pod种,才会有这几个变量的值。
解决方法是 在Dockerfile的 入口 endpoint.sh 中 再运行 环境变量 注入。
因为endpoint.sh是 每个镜像启动后 都会运行的 脚本,因为镜像已经启动,所以 可以获取到相关的 环境变量。
在Dockerfile的末尾使用代码如下:
ENV MY_HOME /usr/local/zzq
COPY entrypoint.sh ${MY_HOME}/entrypoint.sh
WORKDIR ${MY_HOME}
ENTRYPOINT ["./entrypoint.sh"]
entrypoint.sh代码如下:
echo 'export PYSPARK_SUBMIT_ARGS="--master k8s://https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT} --deploy-mode client pyspark-shell" ' >> /etc/profile.d/spark.sh
echo 'export PATH=/usr/local/python37/bin:$PATH' >> /etc/profile.d/airflow.sh
echo 'ln -sf /usr/bin/python /usr/bin/python3' >> /etc/profile.d/airflow.sh
echo 'export PATH=$PATH:$AIRFLOW_HOME' >> /etc/profile.d/airflow.sh
source /etc/profile
echo "hail env:"
echo $PYSPARK_SUBMIT_ARGS
python3 -V
whereis python
echo $PATH
echo "env:"
env
echo "spark conf:"
cat $SPARK_HOME/conf/spark-defaults.conf
说明,我们这里注入的是/etc/profile所使用的环境变量注入脚本目录/etc/profile.d/ 。
/etc/profile 是负责全局的环境变量的,包括 终端和普通的程序运行,都会加载其中的环境变量。
/etc/profile 有两种用法,一种是把export 语句直接加在 /etc/profile文件中,一种是添加一个sh文本文件到 /etc/profile.d/目录,然后执行source /etc/profile 环境变量就会生效,下一次登陆也生效。
该文件包含专属于用户的bash shell的bash信息,当登陆bash shell时以及每次打开bash时执行。
使用方式
打开文件
vim ~/.bashrc
在文件末尾加上
export PATH=$PATH:/.../...
使设置生效
source ~/.bashrc
为每一个运行bash shell的用户执行此文件。当bash shell被打开时,该文件被读取。
每个用户都可使用该文件输入专用于自己的shell信息,当用户使用bash shell登陆时,该文件仅仅被执行一次,默认情况下,其设置的一些环境变量,执行用户的.bashrc文件。
在Debian中使用.profile代替bash_profile文件。
每次退出bash shell时,执行该文件。
此文件为系统的每个用户设置环境信息,当用户第一次登陆时(无论是shell登录还是程序运行),该文件被执行。以上几种文件都是 用户使用bash shell 命令shell界面的时候才会加载环境变量,但是程序运行的时候 可能就读取不到环境变量了,只有/etc/profile是全局的,即负责shell登录也在程序运行时生效。
并从/etc/profile.d目录的配置文件中搜集shell的设置。
上面我们已经 记录了如下设置程序的环境变量,比如使用ENV 或者 /etc/profile 。
但是jupyter notebook因为它并不是一个普通的程序,它有自己的运行原理和内核,所以启动jupyter notebook之后,它的环境变量时跟 linux系统的环境变量是 不完全一致的。
我们来探索一下 如何 设置 jupyter notebook中的环境变量
设置 永久的静态变量 可以使用 Dockerfile 的ENV的方式
比如:
ENV JAVA_HOME=/spark/java/jre1.8.0_181 JRE_HOME=/spark/java/jre1.8.0_181 CLASSPATH=$JAVA_HOME/lib/:$JRE_HOME/lib/
ENV PATH $PATH:$JAVA_HOME/bin:/usr/local/python37/bin
ENV PYSPARK_PYTHON python3
ENV PYSPARK_DRIVER_PYTHON python3
这样设置的系统环境变量 jupyter noterbook是可以同步到的。
在jupyter noterbook中使用命令
!env
可以输出所有的环境变量
发现ENV方式设置的环境变量 可以被 jupyter noterbook 识别到。
但是 如果我们有一些动态变量,是在启动了jupyter的镜像之后才有值的环境变量,!env就获取不到了,也不能通过Dockfile的ENV方式设置,那么 有么有其他的方法设置动态的环境变量让 jupyter notebook 识别到呢
方案如下
比如在/etc/init.d/poststart.sh文件中增加
export PYSPARK_SUBMIT_ARGS="--master k8s://https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT} --deploy-mode client --conf spark.executor.memory=8g --conf spark.executor.cores=2 --conf spark.executor.instances=2 --conf spark.executor.pyspark.memory=1g --conf spark.driver.memory=2g pyspark-shell"
或者修改 /lib/systemd/system/jupyer-notebook.service 文件
增加环境变量如下:
Environment=MYOWN_VAR=theVar
这种方式不能自动设置,只能手动设置,比如在cell的框中输入:
%env MY_VAR=MY_VALUE
%env PYSPARK_SUBMIT_ARGS="--master k8s://https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT} --deploy-mode client --conf spark.executor.memory=8g --conf spark.executor.cores=2 --conf spark.executor.instances=2 --conf spark.executor.pyspark.memory=1g --conf spark.driver.memory=2g pyspark-shell"
或者
import os
os.environ['PYSPARK_SUBMIT_ARGS'] ="--master k8s://https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT} --deploy-mode client --conf spark.executor.memory=8g --conf spark.executor.cores=2 --conf spark.executor.instances=2 --conf spark.executor.pyspark.memory=1g --conf spark.driver.memory=2g pyspark-shell"
或者把多个环境变量写在文件中,比如把 VARIABLE_NAME=VARIABLE_VALUE 写入到.env文件中。
使用命令
import os
env_vars = !cat ../script/.env
for var in env_vars:
key, value = var.split('=')
os.environ[key] = value
还可以借助python-dotenv
新建一个命名为.env的文件,使用如下命令加载
使用命令
%load_ext dotenv
%dotenv
如果是k8s方式部署的hub或者jupyter book。可以在yaml中使用extraConfig 或者 lifecycleHooks的方式
extraConfig
hub:
nodeSelector:
kops.k8s.io/instancegroup: nodes
extraEnv:
JUPYTER_ENABLE_LAB: 1
HOST: "jp.test.com"
resources:
requests:
cpu: 100m
memory: 2Gi
limits:
cpu: 200m
memory: 4Gi
extraConfig: |
import os
c.JupyterHub.authenticator_class = 'oauthenticator.gitlab.GitLabOAuthenticator'
#c.KubeSpawner.cmd = ['jupyter-labhub']
#c.GitLabOAuthenticator.scope = ['api read_repository write_repository']
c.GitLabOAuthenticator.oauth_callback_url = "http://git.test.com/hub/oauth_callback"
c.GitLabOAuthenticator.client_id = "123"
c.GitLabOAuthenticator.client_secret = "123"
c.GitLabConfig.access_token = "123"
c.GitLabConfig.url = "http://git.test.com"
async def add_auth_env(spawner):
auth_state = await spawner.user.get_auth_state()
if not auth_state:
spawner.log.warning("auth failed %s", spawner.user)
return
spawner.environment['GITLAB_ACCESS_TOKEN'] = auth_state['access_token']
spawner.environment['GITLAB_USER_LOGIN'] = auth_state['gitlab_user']['username']
spawner.environment['GITLAB_USER_ID'] = str(auth_state['gitlab_user']['id'])
spawner.environment['GITLAB_USER_EMAIL'] = auth_state['gitlab_user']['email']
spawner.environment['GITLAB_USER_NAME'] = auth_state['gitlab_user']['name']
spawner.environment['ENV'] = 'prod'
spawner.environment['PYSPARK_SUBMIT_ARGS']='--master k8s://https://'+os.environ.get("KUBERNETES_SERVICE_HOST","10.1.0.1")+':'+os.environ.get("KUBERNETES_SERVICE_PORT",443)+' --deploy-mode client --conf spark.executor.instances=2 pyspark-shell'
或者
lifecycleHooks:
postStart:
exec:
command:
- "sh"
- "-c"
- >
sed -i "1ispark.master k8s://https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}" $SPARK_HOME/conf/spark-defaults.conf ;
echo "spark.driver.pod.name ${HOSTNAME}" >> $SPARK_HOME/conf/spark-defaults.conf ;
echo "spark.driver.host `hostname -i`" >> $SPARK_HOME/conf/spark-defaults.conf ;
if [ -x /etc/init.d/poststart.sh ] ; then
/etc/init.d/poststart.sh prod;
fi;
if [ -x /usr/bin/git ]; then
echo "https://oauth2:${GITLAB_ACCESS_TOKEN}@git.test.com" > ~/.git-credentials;
mkdir -p $HOME/.config/git ;
echo -e 'hail*.log\n.ipynb_checkpoints\n__pycache__/\n*.egg-info/\n.eggs/\n.cache/\n.mypy_cache/\n.DS_Store' > $HOME/.config/git/ignore;
/usr/bin/git config --global credential.helper store;
/usr/bin/git config --global user.email "${GITLAB_USER_EMAIL}";
/usr/bin/git config --global user.name "${GITLAB_USER_LOGIN}";
fi;
还有一种设置方式是 修改内核文件,kernel.json,jupyter notebook的原理就是依赖与kernel启动不同的语言环境,所以 环境变量与 kernel息息相关。
如果我们希望 每次启动 jupyter notebook的环境,环境变量都生效,可以尝试修改kernel.json
首先需要创建一个ipython的kernel,其中设置我们的环境变量。
步骤如下:
1、阅读文档 https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-specs
2、在linux系统shell中使用命令jupyter kernelspec list 查看 已经安装了哪些kernels 和 它们的文件存储在哪里
3、复制其中我们需要的文件kernel.json,比如python3环境的
4、修改文件命名和其中的部分内容,再复制回去,主要修改 display_name和env如下:
{
"display_name": "Python 3 with environment",
"language": "python",
"argv": [
"/usr/bin/python3",
"-m",
"ipykernel_launcher",
"-f",
"{connection_file}"
],
"env": {"LD_LIBRARY_PATH":""}
}