惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
The Blog of Author Tim Ferriss
G
Google Developers Blog
小众软件
小众软件
J
Java Code Geeks
V
Visual Studio Blog
The Register - Security
The Register - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
V
V2EX
博客园 - 叶小钗
N
Netflix TechBlog - Medium
月光博客
月光博客
aimingoo的专栏
aimingoo的专栏
大猫的无限游戏
大猫的无限游戏
T
The Exploit Database - CXSecurity.com
The Hacker News
The Hacker News
Cisco Talos Blog
Cisco Talos Blog
Hacker News: Ask HN
Hacker News: Ask HN
Hacker News - Newest:
Hacker News - Newest: "LLM"
AWS News Blog
AWS News Blog
Webroot Blog
Webroot Blog
The Last Watchdog
The Last Watchdog
T
Threatpost
I
Intezer
T
Tenable Blog
L
LINUX DO - 热门话题
T
Tailwind CSS Blog
Scott Helme
Scott Helme
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Cybersecurity and Infrastructure Security Agency CISA
Engineering at Meta
Engineering at Meta
S
Schneier on Security
Recent Announcements
Recent Announcements
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
F
Fortinet All Blogs
腾讯CDC
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Troy Hunt's Blog
量子位
H
Hacker News: Front Page
V2EX - 技术
V2EX - 技术
Google Online Security Blog
Google Online Security Blog
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
博客园 - Franky
www.infosecurity-magazine.com
www.infosecurity-magazine.com

博客园 - xiaoxi666

深度学习优化器生态演化图谱 神经网络常见的40多种激活函数(应用场景+数学公式+代码实现+函数图象) 程序员究竟要不要写文章 使用Lean4进行形式化建模(以Java线程池为例) 用好 JUnit 5 的高级特性:提升单测效率和质量 好端端的线程池,怎么就卡死了? DeepSeek又在节前放大招! Dubbo实战:四步实现注册中心平滑迁移 LLM中的Top-K/Top-p/温度都是怎么发挥作用的? RLHF各种训练算法科普 近期最值得关注的AI技术报告与Agent综述! 通俗理解自注意力机制和多头注意力机制 什么是南北向流量和东西向流量? 如何实现LLM的通用function-calling能力? 推荐一枚宝藏Up主,顺便聊聊感想 Embedding 模型部署及效果评测 立即执行函数在前端国际化方案中的应用 推荐一款id生成器: Hashids 推荐一款工具,辅助估算线程池参数
你的Docker部署能更简单吗?GitLab集成与多进程管理又怎么做?
xiaoxi666 · 2024-12-09 · via 博客园 - xiaoxi666

前言

文章开始前,先尝试回答几个问题:

  1. 在日常工作中,程序的打包和部署方式流程是怎样的?

  2. 如果使用Docker容器部署方式,是用原生Docker命令,还是Kaniko这类工具?

  3. 除了业务服务外,如果还需要同时运行其他进程,应该怎么办?举个例子:除了主服务进程(比如Web服务器),还需要同时启动Nginx进程(用于代理转发部分前端流量,解决跨域问题)。

以上几个问题,拆解:

  1. 属于 CI 集成的概念:GitLab和Docker的集成是一种主流方式,其他工具也大同小异。

  2. 与内核态和用户态有关。Kaniko是一种常用的容器镜像构建工具。

  3. 需要用到进程管理工具,在Docker容器中同时管理多个进程。

一个个来说。

GitLab 和 Docker 的集成

两者的集成主要有三种方式,手段大同小异,但核心目的都是把Docker打包镜像的流程放到GitLab的流水线中执行。可参见官 https://docs.gitlab.com/ee/ci/docker/using_docker_build.html:

以 shell executor 为例:

可见,做法很简单:将Docker命令作为流水线脚本执行即可,镜像打包出来后可用来部署。以近期热度较高的大模型服务为例,经典的集成架构如下图所示:

其他CI集成的主流程万变不离其宗,不同之处在于在此基础上做一些优化,比如缓存加速等。

常用的容器镜像构建工具:Kaniko

同样,先看官网 https://github.com/GoogleContainerTools/kaniko 定义:

可见,Kaniko 是一个用于构建容器镜像的工具,它允许我们从Dockerfile构建镜像,且不需要Docker守护进程。Kaniko executor 构建镜像的详细步骤如下:

  1. Kaniko Executor Image:Kaniko执行器镜像是一个特殊的容器镜像,它包含了所有必要的工具和依赖,以便在容器内部构建新的Docker镜像。

  2. Building an Image from a Dockerfile:用户提供一个Dockerfile,Kaniko执行器镜像将根据这个Dockerfile来构建一个新的镜像。Dockerfile包含了一系列的指令,这些指令定义了如何构建一个新Docker镜像,例如FROMRUNCOPYADD等。

  3. Extracting the Filesystem of the Base Image:在Dockerfile中,FROM 指令指定了基础镜像,Kaniko 首先需要提取这个基础镜像的文件系统。这个文件系统被复制到 Kaniko 执行器镜像的临时目录中,以便后续的构建过程可以在用户空间中对其进行操作。

  4. Executing Commands in the Dockerfile:Kaniko逐个执行Dockerfile中的指令。对于每个指令,Kaniko都会在用户空间中模拟该指令的效果。例如,如果指令是RUN apt-get update,Kaniko会在容器内部执行这个命令,就像在普通的Docker构建过程中一样。

  5. Snapshotting the Filesystem in Userspace:在每个指令执行之后,Kaniko 会在用户空间中对文件系统进行快照。这个快照捕获了自上一个快照以来文件系统的所有变化,这些变化包括新创建的文件、修改过的文件和删除的文件。

  6. Appending a Layer of Changed Files:如果在执行某个指令后文件系统发生了变化,Kaniko 会将这些变化作为一个新层添加到基础镜像上。这个过程是逐层进行的,每个指令可能对应一个或多个层,这取决于文件系统的变化。

  7. Updating Image Metadata:除了添加文件系统的变化之外,Kaniko还会更新镜像的元数据,包括标签、环境变量、工作目录等。这些元数据信息也是 Dockerfile 中指令的一部分,Kaniko会确保这些信息被正确地应用到新构建的镜像上。

  8. Pushing the Image to a Registry:一旦所有的指令都被执行完毕,并且所有的变化都被添加到镜像中,Kaniko 会将这个新构建的镜像推送到指定的容器镜像仓库(registry)。这个步骤需要提供仓库的认证信息,以便Kaniko能够成功地将镜像推送到仓库。

总的来说,Kaniko与原生Docker最大的不同之处在于:Kaniko在用户空间中模拟 Dockerfile中的指令,并逐层捕获文件系统的变化,最终构建出一个新的Docker镜像,而不需要Docker守护进程的参与。这种方法提高了安全性,并且可以在多种环境中灵活地构建容器镜像(也即:可以在没有root权限的环境中运行)。另外,在GitLab官网 https://docs.gitlab.com/ee/ci/docker/using_kaniko.html 也有 Kaniko 的使用方法描述:

在 Docker 容器中管理多个进程

现实中,大部分情况下容器中仅会有一个进程,也即1号进程。但总会有例外发生,以上文中的例子来说,某个容器中不仅需要主服务进程提供Web服务,还需要Nginx进程用于代理转发部分前端流量,以解决跨域问题。此时就需要同时启动并管理多个进程。有需求就有实现,目前多进程管理工具有很多了,一种常见的方案是使用Python编写的进程管理工具supervisord,它可以管理多个进程,包括启动、停止、重启等操作。针对上述例子,整体集成方式如下:

  1. 制作基础镜像:基于centos系统镜像,安装supervisord 和nginx。
/bin/sh -c yum install -y epel-release
/bin/sh -c yum install -y supervisor
/bin/sh -c yum install -y nginx-1.10.3
/bin/sh -c yum clean all

# 注意只列出了本文涉及到的几个命令,还有很多其他功能需要安装
# 可参考仓库 https://github.com/CentOS/sig-cloud-instance-images
# 该仓库包括了centos系统的各种镜像的构建命令,囊括了很多实用命令安装脚本



2. 编写 /etc/supervisord.conf,它是supervisord的主配置文件,定义了supervisord 进程管理器的全局设置和行为,确保所有被管理的进程都能按照预期的方式启动和运行。通过这个配置文件,可以集中管理多个服务,使得服务的部署和维护更加方便。

[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700

[supervisord]
logfile=/var/log/supervisor/supervisord.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=info
pidfile=/var/run/supervisord.pid
nodaemon=false
minfds=1024
minprocs=200

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock

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


[unix_http_server]:定义了 supervisord 的 Unix 套接字 HTTP 服务器的配置,用于 supervisorctl 命令行工具或其他客户端与 supervisord 进行通信。

[supervisord]:包含了 supervisord 守护进程的全局配置,如日志文件位置、日志大小限制、日志备份数量、进程文件位置、最小文件描述符和进程数量等。

[rpcinterface:x]:定义了 RPC 接口的配置,允许远程管理 supervisord。这通常用于 supervisorctl 命令行工具。

[supervisorctl]:定义了 supervisorctl 命令行工具的配置,如连接到 supervisord 的 URL。

[include] :允许 supervisord 包含其他配置文件。这使得你可以将不同的程序配置在不同的文件中,然后由主配置文件统一管理。通常,这个节会包含 /etc/supervisor/conf.d/*.ini,这样 supervisord 就会自动加载 /etc/supervisor/conf.d/ 目录下的所有 .ini 文件。

  1. 为各工程使用方便,将 /etc/supervisor/conf.d/nginx.ini 提前打包到镜像中,后续只需配置 nginx.conf 即可。

 
[program:nginx]
command=/etc/nginx/sbin/nginx
user=xiaoxi666
priority=999
numprocs=1
autostart=true
autorestart=true
startsecs=1
startretries=3
stopsignal=KILL
stopwaitsecs=10
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.err
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=5
stopasgroup=true

[program:nginx]:定义了一个名为 nginx 的程序。supervisord 会根据这个名称来识别和控制这个进程。各个字段含义:

command:指定启动进程的命令。

user:指定以哪个用户身份运行 Nginx 进程。通常建议使用非 root 用户来运行服务以提高安全性。

priority:设置程序的启动优先级。较小的数字表示更高的优先级,即 supervisord 会先启动优先级数值较小的程序。

numprocs:指定应该启动多少个进程。这里设置为1,意味着只启动一个 Nginx 进程。

autostart:设置为 true 时,supervisord 会在启动时自动启动这个程序。

autorestart:设置为 true 时,如果 nginx 进程意外退出,supervisord 会自动重启它。

startsecs:指定 Nginx 进程启动后多少秒内被认为是稳定的。如果在这个时间内进程退出,supervisord 会认为启动失败。

startretries:如果 Nginx 进程在 startsecs 时间内退出,supervisord 会尝试重启它。这个值指定了最大重启次数。

stopsignal:指定停止 Nginx 进程时发送的信号。KILL 信号是一个强制终止进程的信号。

stopwaitsecs:指定 supervisord 等待 Nginx 进程停止的时间(以秒为单位)。如果超时,supervisord 会发送 KILL 信号。

stdout_logfile:指定 Nginx 进程的标准输出日志文件的位置。

stderr_logfile:指定 Nginx 进程的标准错误日志文件的位置。

stdout_logfile_maxbytes:设置标准输出日志文件的最大大小。当文件达到这个大小后,会滚动切分输出。

stdout_logfile_backups:设置标准输出日志文件的备份数量。当日志文件滚动切分时,旧的日志文件会被保留的备份数量。

stopasgroup:设置为 true 时,supervisord 会向整个进程组发送停止信号,这可以确保所有子进程都被正确终止。

做完这一步,该镜像就可以提供给“同时需要Web服务和Nginx服务”的工程使用了。

  1. 各工程编写自己的Dockerfile文件,将自定义的服务ini文件(假设名字为supervisor-app.ini)和 nginx.conf 文件复制到指定目录,然后启动supervisord。
# 定义环境变量
ENV SUPERVISOR_CONF_DIR=/etc/supervisor/conf.d
ENV NGINX_CONF_DIR=/etc/nginx/conf.d
ENV APP_DIR=/app
ENV PROJECT_NAME=xiaoxi666_demo_project

# 复制supervisord的配置文件到supervisord的配置目录
COPY supervisor-app.ini ${SUPERVISOR_CONF_DIR}/supervisor-app.ini

# 复制nginx配置文件到指定的nginx配置目录
COPY nginx.conf ${NGINX_CONF_DIR}/nginx.conf

# 复制当前目录下的所有文件到APP_DIR
COPY . ${APP_DIR}

# 创建项目目录并移动target目录下的内容到项目目录
RUN mkdir -p ${APP_DIR}/${PROJECT_NAME} \
    && mv ${APP_DIR}/target/* ${APP_DIR}/${PROJECT_NAME} \
    && chown -R xiaoxi666:xiaoxi666 ${APP_DIR}

# 暴露端口
EXPOSE 8080 其他要暴露的端口

# 启动supervisord
CMD ["/usr/bin/supervisord", "-n"]

其中,supervisor-app.ini文件内容为主进程的启动命令,一个示例:

[program:xiaoxi666-demo-project]
command=java -jar 其他虚拟机参数 xiaoxi666-demo-project.jar
user=root
priority=999
numprocs=1
autostart=true
autorestart=true
startsecs=1
startretries=3
stopsignal=KILL
stopwaitsecs=20
stdout_logfile=%(ENV_LOG_DIR)/supervisor/xiaoxi666-demo-project.log
stderr_logfile=%(ENV_LOG_DIR)/supervisor/xiaoxi666-demo-project.err
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=5
stopasgroup=true

而 nginx.conf 根据实际代理需求配置即可。

整体来看,文件引用层级是这样的:

后记

本文以实际工程中的例子为切入点,围绕“Docker容器镜像构建”、“GitLab集成部署”,以及“容器多进程管理”这几个侧重点做了一些探讨和梳理,同时给出了对应的官方参考资料链接,有兴趣的读者可进一步深入学习。也欢迎大家留言,谈谈你所经历的容器集成部署方式是怎样的。