Docker-Compose环境变量无法被Cron Job识别的问题排查
我在docker-compose中为data-collector服务配置了多组环境变量,供定时运行的Python应用调用。相关配置、验证操作及报错情况如下:
docker-compose.yaml 配置
version: '2.1' services: zookeeper: container_name: zookeeper image: zookeeper:3.3.6 restart: always hostname: zookeeper ports: - "2181:2181" environment: ZOO_MY_ID: 1 kafka: container_name: kafka image: wurstmeister/kafka:1.1.0 hostname: kafka links: - zookeeper ports: - "9092:9092" environment: KAFKA_ADVERTISED_HOST_NAME: kafka KAFKA_CREATE_TOPICS: "topic:1:1" KAFKA_LOG_MESSAGE_TIMESTAMP_TYPE: LogAppendTime KAFKA_MESSAGE_TIMESTAMP_TYPE: LogAppendTime KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 data-collector: container_name: data-collector #image: mystreams:0.1 build: context: /home/junaid/eMumba/CASB/Docker/data_collector/ dockerfile: Dockerfile links: - kafka environment: - KAFKA_HOST=kafka - OFFICE_365_APP_ID=98aff1c5-7a69-46b7-899c-186851054b43 - OFFICE_365_APP_SECRET=zVyS/V694ffWe99QpCvYqE1sqeqLo36uuvTL8gmZV0A= - OFFICE_365_APP_TENANT=2f6cb1a6-ecb8-4578-b680-bf84ded07ff4 - KAFKA_CONTENT_URL_TOPIC=o365_activity_contenturl - KAFKA_STORAGE_DATA_TOPIC=o365_storage - KAFKA_PORT=9092 - POSTGRES_DB_NAME=casb - POSTGRES_USER=postgres - POSTGRES_PASSWORD=pakistan - POSTGRES_HOST=postgres_database depends_on: postgres_database: condition: service_healthy postgres_database: container_name : postgres_database build: context: /home/junaid/eMumba/CASB/Docker/data_collector/ dockerfile: postgres.dockerfile #image: ayeshaemumba/casb-postgres:v3 #volumes: # - ./postgres/data:/var/lib/postgresql/data environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: pakistan POSTGRES_DB: casb expose: - "5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 30s timeout: 30s retries: 3
容器内环境变量验证
# docker exec -it data-collector sh # echo $KAFKA_HOST kafka
报错情况
Cron Job的日志显示
KeyError: 'KAFKA_HOST',说明Cron Job无法读取这些环境变量。
我的问题
- 为什么Cron Job无法获取到设置的环境变量?
- 我知道可以通过构建镜像时传递shell脚本的方式传入环境变量,但有没有办法直接从docker-compose传递?
补充配置文件
Dockerfile
FROM python:3.5-slim # Creating Application Source Code Directory RUN mkdir -p /usr/src/app # Setting Home Directory for containers WORKDIR /usr/src/app # Installing python dependencies COPY requirements.txt /usr/src/app/ RUN pip install --no-cache-dir -r requirements.txt # Copying src code to Container COPY . /usr/src/app # Add storage crontab file in the cron directory ADD crontab-storage /etc/cron.d/storage-cron # Give execution rights on the storage cron job RUN chmod 0644 /etc/cron.d/storage-cron RUN chmod 0644 /usr/src/app/cron_storage_data.sh # Create the log file to be able to run tail RUN touch /var/log/cron.log #Install Cron RUN apt-get update RUN apt-get -y install cron # Run the command on container startup CMD cron && tail -f /var/log/cron.log
crontab-storage
*/1 * * * * sh /usr/src/app/cron_storage_data.sh # Don't remove the empty line at the end of this file. It is required to run the cron job
cron_storage_data.sh
#!/bin/bash cd /usr/src/app /usr/local/bin/python3.5 storage_data_collector.py
1. 为什么Cron Job读不到环境变量?
Cron运行任务时,只会加载系统默认的极简环境变量(比如PATH、HOME),不会继承容器shell会话里的环境变量。
原因很简单:你在docker-compose里设置的环境变量属于容器的主进程(也就是cron && tail -f...这个启动进程),但Cron本身启动的子任务是脱离当前shell环境的,它不会自动读取容器启动时注入的环境变量集合。这就导致你docker exec进去能看到变量,但Cron任务看不到。
2. 怎么从docker-compose直接传递环境变量给Cron?
当然有办法,不需要重新构建镜像,推荐这几种实用方案:
方法一:修改shell脚本,自动加载容器环境变量
容器启动时的所有环境变量都存在/proc/1/environ文件里(容器的PID 1就是启动主进程),只要在你的shell脚本开头加一段代码,就能把这些变量导入到Cron的执行环境中:
#!/bin/bash # 加载容器主进程的所有环境变量 export $(cat /proc/1/environ | tr '\0' '\n') cd /usr/src/app /usr/local/bin/python3.5 storage_data_collector.py
这种方法最灵活,docker-compose里的环境变量修改后,脚本会自动读取最新值,不需要额外同步配置。
方法二:在crontab中直接定义环境变量
你可以在crontab文件的开头直接写出需要的变量值,比如:
KAFKA_HOST=kafka POSTGRES_HOST=postgres_database # 其他需要的变量... */1 * * * * sh /usr/src/app/cron_storage_data.sh # Don't remove the empty line at the end of this file. It is required to run the cron job
不过这种方法的缺点是,如果你在docker-compose里修改了环境变量,必须同步修改crontab文件,维护起来比较麻烦,适合变量值固定的场景。
方法三:容器启动时生成环境变量文件
修改Dockerfile的启动命令,把容器环境变量写入系统环境文件,然后让Cron任务加载这个文件:
- 修改Dockerfile的CMD:
CMD env >> /etc/environment && cron && tail -f /var/log/cron.log
- 修改crontab-storage,让任务执行前加载环境文件:
*/1 * * * * . /etc/environment && sh /usr/src/app/cron_storage_data.sh # Don't remove the empty line at the end of this file. It is required to run the cron job
这种方法也能自动同步docker-compose的环境变量,只是需要修改两处配置。
内容的提问来源于stack exchange,提问作者el323

