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

推荐订阅源

量子位
S
Secure Thoughts
S
Schneier on Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Cyberwarzone
Cyberwarzone
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Privacy International News Feed
L
Lohrmann on Cybersecurity
Schneier on Security
Schneier on Security
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
C
Cybersecurity and Infrastructure Security Agency CISA
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Latest news
Latest news
H
Hacker News: Front Page
月光博客
月光博客
Forbes - Security
Forbes - Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Security @ Cisco Blogs
WordPress大学
WordPress大学
Recent Commits to openclaw:main
Recent Commits to openclaw:main
aimingoo的专栏
aimingoo的专栏
宝玉的分享
宝玉的分享
D
Docker
U
Unit 42
Recorded Future
Recorded Future
Spread Privacy
Spread Privacy
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
云风的 BLOG
云风的 BLOG
Application and Cybersecurity Blog
Application and Cybersecurity Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Heimdal Security Blog
Microsoft Azure Blog
Microsoft Azure Blog
V
Vulnerabilities – Threatpost
Vercel News
Vercel News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
博客园 - 聂微东
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog
G
GRAHAM CLULEY
Apple Machine Learning Research
Apple Machine Learning Research
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Blog — PlanetScale
Blog — PlanetScale
博客园 - 三生石上(FineUI控件)
罗磊的独立博客
Help Net Security
Help Net Security
Google Online Security Blog
Google Online Security Blog
S
SegmentFault 最新的问题

博客园 - 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集成部署”,以及“容器多进程管理”这几个侧重点做了一些探讨和梳理,同时给出了对应的官方参考资料链接,有兴趣的读者可进一步深入学习。也欢迎大家留言,谈谈你所经历的容器集成部署方式是怎样的。