最佳实践10多个项目将使用一个容器/参数化运行

时间:2019-07-19 09:12:48

标签: python docker sh

想法是有一个容器,其中包含所有小型项目,并根据参数运行。

当前情况是什么

我的项目文件夹如下:

├── MAIN_PROJECT_FOLDER
│   ├── PROJECT_SUB_CATEGORY1
│   ├── ├── PROJECT_NAME_FOLDER1
│   │   │   ├── run.sh
│   │   │   ├── main.py
│   │   │   ├── config.py
│   ├── ├── PROJECT_NAME_FOLDER2
│   │   │   ├── run.sh
│   │   │   ├── main.py
│   │   │   ├── config.py
│   ├── PROJECT_SUB_CATEGORY2
│   ├── ├── PROJECT_NAME_FOLDER1
│   │   │   ├── run.sh
│   │   │   ├── main.py
│   │   │   ├── config.py
│   ├── ├── PROJECT_NAME_FOLDER2
│   │   │   ├── run.sh
│   │   │   ├── main.py
│   │   │   ├── config.py

每个 run.sh 文件都具有prod / dev参数,可以像这样执行:

sudo ./run.sh prod = prod
sudo ./run.sh dev  = dev
sudo ./run.sh      = dev

创建另一个 .SH 文件或 Dockerfile 的方法是什么,该文件最后可以像这样执行:

sudo docker run CONTAINER_NAME PROJECT_NAME PROD/DEV
sudo docker run test_contaner test_project1 prod
sudo docker run test_contaner test_project1 dev
sudo docker run test_contaner test_project2 prod
... and so one

基本上,每个项目都是参数,并且prod / dev将以某种方式成为run.sh执行的一部分。

寻找最佳实践来实现这一目标。

3 个答案:

答案 0 :(得分:2)

最佳做法通常是制作一张只做一件事的图像。在您的示例中,这意味着四个单独的Docker映像;每个目录都有自己的Dockerfile。

使用环境变量配置这样的设置也比使用命令行参数更容易。 https://12factor.net/ describe this之类的网站以及其他一些用于构建服务的做法。 (以我的经验,在像Docker Compose或Kubernetes这样的YAML规范中,添加另一个键/值环境对要比从多个不同的部分构建正确的命令行要容易得多。)

这将导致您出现类似的序列

sudo docker build -t me/cat1proj1 CATEGORY_1/PROJECT_1
sudo docker run -e ENVIRONMENT=prod me/cat1proj1

在架构上,Docker容器运行任何单个进程,并且绝对没有阻止您编写描述的包装器脚本的操作。该单个命令被指定为“入口点”和“命令”的组合。如果您同时指定了两者,则命令将作为参数传递给入口点。可以在Dockerfile CMD中指定“命令”部分,但也可以在docker run命令行中覆盖它。

如果您根本不编写任何特殊脚本,则可以运行(假设您已经COPY将项目放置在正确的目录中)

sudo docker run test_image ./test_project1/run.sh prod

(我有几个项目是相同的应用程序,具有不同的脚本,它们以不同的方式启动它们-例如,Web服务器与具有相同代码的异步作业运行器-并仅使用备用启动脚本来启动它们这样)。

有一种模式可以使其他脚本成为ENTRYPOINT,然后将“命令”解释为该脚本的参数。该命令仅作为参数$1$2"$@"传递。这样做的问题是,它打破了一些常规的调试路径。

# "test_project1" "prod" passed as arguments to entrypoint script
sudo docker run test_image test_project1 prod

# But that breaks getting a debug shell
sudo docker run --rm -it test_image bash

# More complex commands get awkward
sudo docker run --rm --entrypoint=/bin/ls test_image -l /app

答案 1 :(得分:1)

我个人会使用Supervisor之类的工具,该工具可以在一个docker容器中运行。

在基于Ubuntu和Debian的发行版上安装supervisor

sudo apt install supervisor

启动supervisor守护程序:

sudo service supervisor start

/etc/supervisord/supervisord.conf中,您将找到放置项目配置的位置:

[include]
files = /etc/supervisor/conf.d/*.conf

现在,您可以为主管创建配置并将其复制到/etc/supervisor/conf.d/。项目supervisor的示例PROJECT_1配置:

project_1_supervisor.conf

[program:project_1_app]
command=/usr/bin/bash /project_1_path/run.sh prod
directory=/project_1_path/
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/project_1.err.log
stdout_logfile=/var/log/project_1.out.log

重启后,您的supervisor

sudo supervisorctl reread
sudo supervisorctl update

此后,您可以检查您的项目程序是否运行:

$ supervisorctl
project_1_app               RUNNING    pid 590, uptime 0:02:45

答案 2 :(得分:1)

我认为处理此问题的最佳方法是ENV,这是您正在寻找的完整示例。

这是目录结构

enter image description here

这是克隆上述应用程序并执行智能操作的最聪明的dockerfile;)这将需要四个env,默认情况下它将运行项目A。

ENV BASE_PATH="/opt/project"

此ENV用于克隆期间的项目基本路径

  ENV PROJECT_PATH="/main/sub_folder_a/project_a"

此ENV用于项目路径,例如项目B

ENV SCRIPT_NAME="hello.py"

根据您的情况,此ENV将用于运行实际文件,可以是run.shmain.py

ENV SYSTEM_ENV=dev

此环境用于run.sh,它可以devprod

FROM python:3.7.4-alpine3.10

WORKDIR /opt/project

# Required Tools
RUN apk add --no-cache supervisor git tree && \
    mkdir -p /etc/supervisord.d/

# clone remote project or copy your own one
RUN echo "Starting remote clonning...."
RUN git clone https://github.com/Adiii717/python-demo-app.git /opt/project
RUN tree /opt/project

# ENV for start different project, can be overide at run time
ENV BASE_PATH="/opt/project"
ENV PROJECT_PATH="/main/sub_folder_a/project_a"
ENV SCRIPT_NAME="hello.py"
# possible dev or prod
ENV SYSTEM_ENV=dev
RUN chmod +x /opt/project/main/*/*/run.sh
# general config
RUN echo  $'[supervisord] \n\
[unix_http_server] \n\
file = /tmp/supervisor.sock \n\
chmod = 0777 \n\
chown= nobody:nogroup \n\
[supervisord] \n\
logfile = /tmp/supervisord.log \n\
logfile_maxbytes = 50MB \n\
logfile_backups=10 \n\
loglevel = info \n\ 
pidfile = /tmp/supervisord.pid \n\
nodaemon = true \n\
umask = 022 \n\
identifier = supervisor \n\
[supervisorctl] \n\
serverurl = unix:///tmp/supervisor.sock \n\
[rpcinterface:supervisor] \n\
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface \n\
[include] \n\
files = /etc/supervisord.d/*.conf' >> /etc/supervisord.conf 
# script supervisord Config
RUN echo $'[supervisord] \n\
nodaemon=true \n\
[program:run_project ] \n\
command= /run_project.sh  \n\
stdout_logfile=/dev/fd/1 \n\
stdout_logfile_maxbytes=0MB \n\
stderr_logfile_maxbytes = 0 \n\
stderr_logfile=/dev/fd/2 \n\
redirect_stderr=true \n\
autorestart=false \n\
startretries=0 \n\
exitcodes=0 ' >> /etc/supervisord.d/run_project.conf

RUN echo $'#!/bin/ash \n\
    echo -e "\x1B[31m starting project having name ${BASE_PATH}${PROJECT_PATH}/${SCRIPT_NAME}  \x1B[0m" \n\
    fullfilename=${BASE_PATH}${PROJECT_PATH}/${SCRIPT_NAME} \n\ 
    filename=$(basename "$fullfilename") \n\
    extension="${filename##*.}" \n\
if [[ ${extension} == "sh" ]];then \n\
    sh ${BASE_PATH}${PROJECT_PATH}/${SCRIPT_NAME} ${SYSTEM_ENV}  \n\ 
    else \n\
    python ${BASE_PATH}${PROJECT_PATH}/${SCRIPT_NAME} \n\
    fi '      >> /run_project.sh
RUN  chmod +x /run_project.sh


EXPOSE 9080 8000 9088 80

ENTRYPOINT ["supervisord", "--nodaemon", "--configuration", "/etc/supervisord.conf"]

构建docker映像

docker build -t multipy .

运行Docker容器 docker run --rm -it multipy

这将默认运行project a

对于项目b,您的命令将为

docker run --rm -it --env PROJECT_PATH=/main/sub_folder_b/project_b --env SCRIPT_NAME=hello.py multipy

要运行您的run.sh bash文件命令,将是

docker run --rm -it --env SCRIPT_NAME=run.sh multipy

这是一些日志

enter image description here