Nginx官方镜像Dockerfile浅析

目录

Dockerfile获取

dfimage逆向获取

Nginx官网获取

Dockerfile分析

启动命令分析

Docker 容器入口点脚本分析

exec "$@"

exec 命令

"$@" 参数

总结


        在云原生技术快速发展的今天,Docker 作为容器技术的代表,为软件的打包、分发和部署提供了一种轻量级、可移植的解决方案。Dockerfile 作为 Docker 的核心组件之一,其设计和编写直接关系到容器镜像的构建效率和运行时的性能。了解和掌握 Dockerfile 的关键技术点,对于任何希望在云原生领域内进行开发和运维的工程师来说都是至关重要的。

        本文旨在通过分析 Nginx 官方 Docker 镜像的 Dockerfile,探讨 Dockerfile 的获取方法、结构和编写策略,以及容器启动脚本的作用和设计原则。我们将深入讨论在 Dockerfile 中使用 exec "$@" 的好处,以及这种做法在其他镜像设计中的应用价值,为构建更加高效、可靠的容器化应用打下坚实的基础。

Dockerfile获取

dfimage逆向获取

alias dfimage='docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage'/usr/bin/dockerdfimage nginx:1.25.4

dfimage='docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage'/usr/bin/docker nginx:1.25.4

逆向Dockerfile

CMD ["bash"]
LABEL maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>
ENV NGINX_VERSION=1.25.4
ENV NJS_VERSION=0.8.3
ENV PKG_RELEASE=1~bookworm
RUN RUN set -x  \&& groupadd --system --gid 101 nginx  \&& useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx  \&& apt-get update  \&& apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates  \&& NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; export GNUPGHOME="$(mktemp -d)"; found=''; for server in hkp://keyserver.ubuntu.com:80 pgp.mit.edu ; do echo "Fetching GPG key $NGINX_GPGKEY from $server"; gpg1 --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY"  \&& found=yes  \&& break; done; test -z "$found"  \&& echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY"  \&& exit 1; gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" ; rm -rf "$GNUPGHOME"; apt-get remove --purge --auto-remove -y gnupg1  \&& rm -rf /var/lib/apt/lists/*  \&& dpkgArch="$(dpkg --print-architecture)"  \&& nginxPackages=" nginx=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} "  \&& case "$dpkgArch" in amd64|arm64) echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list  \&& apt-get update ;; *) echo "deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list  \&& tempDir="$(mktemp -d)"  \&& chmod 777 "$tempDir"  \&& savedAptMark="$(apt-mark showmanual)"  \&& apt-get update  \&& apt-get build-dep -y $nginxPackages  \&& ( cd "$tempDir"  \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" apt-get source --compile $nginxPackages )  \&& apt-mark showmanual | xargs apt-mark auto > /dev/null  \&& { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; }  \&& ls -lAFh "$tempDir"  \&& ( cd "$tempDir"  \&& dpkg-scanpackages . > Packages )  \&& grep '^Package: ' "$tempDir/Packages"  \&& echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list  \&& apt-get -o Acquire::GzipIndexes=false update ;; esac  \&& apt-get install --no-install-recommends --no-install-suggests -y $nginxPackages gettext-base curl  \&& apt-get remove --purge --auto-remove -y  \&& rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list  \&& if [ -n "$tempDir" ]; then apt-get purge -y --auto-remove  \&& rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; fi  \&& ln -sf /dev/stdout /var/log/nginx/access.log  \&& ln -sf /dev/stderr /var/log/nginx/error.log  \&& mkdir /docker-entrypoint.d # buildkit
COPY docker-entrypoint.sh / # buildkitdocker-entrypoint.shCOPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d # buildkitdocker-entrypoint.d/docker-entrypoint.d/10-listen-on-ipv6-by-default.shCOPY 15-local-resolvers.envsh /docker-entrypoint.d # buildkitdocker-entrypoint.d/docker-entrypoint.d/15-local-resolvers.envshCOPY 20-envsubst-on-templates.sh /docker-entrypoint.d # buildkitdocker-entrypoint.d/docker-entrypoint.d/20-envsubst-on-templates.shCOPY 30-tune-worker-processes.sh /docker-entrypoint.d # buildkitdocker-entrypoint.d/docker-entrypoint.d/30-tune-worker-processes.shENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE map[80/tcp:{}]
STOPSIGNAL SIGQUIT
CMD ["nginx" "-g" "daemon off;"]

Nginx官网获取

docker-nginx/mainline/debian/Dockerfile at 1f227619c1f1baa0bed8bed844ea614437ff14fb · nginxinc/docker-nginx · GitHub

官网Dockerfile

#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#
FROM debian:bookworm-slimLABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"ENV NGINX_VERSION   1.25.4
ENV NJS_VERSION     0.8.3
ENV PKG_RELEASE     1~bookwormRUN set -x \
# create nginx user/group first, to be consistent throughout docker variants&& groupadd --system --gid 101 nginx \&& useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \&& apt-get update \&& apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \&& \NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; \export GNUPGHOME="$(mktemp -d)"; \found=''; \for server in \hkp://keyserver.ubuntu.com:80 \pgp.mit.edu \; do \echo "Fetching GPG key $NGINX_GPGKEY from $server"; \gpg1 --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \done; \test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" ; \rm -rf "$GNUPGHOME"; \apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \&& dpkgArch="$(dpkg --print-architecture)" \&& nginxPackages=" \nginx=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} \" \&& case "$dpkgArch" in \amd64|arm64) \
# arches officialy built by upstreamecho "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list \&& apt-get update \;; \*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packagesecho "deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list \\
# new directory for storing sources and .deb files&& tempDir="$(mktemp -d)" \&& chmod 777 "$tempDir" \
# (777 to ensure APT's "_apt" user can access it too)\
# save list of currently-installed packages so build dependencies can be cleanly removed later&& savedAptMark="$(apt-mark showmanual)" \\
# build .deb files from upstream's source packages (which are verified by apt-get)&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( \cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \apt-get source --compile $nginxPackages \) \
# we don't remove APT lists here because they get re-downloaded and removed later\
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)&& apt-mark showmanual | xargs apt-mark auto > /dev/null \&& { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \\
# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)&& ls -lAFh "$tempDir" \&& ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \&& grep '^Package: ' "$tempDir/Packages" \&& echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
#   Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
#   ...
#   E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)&& apt-get -o Acquire::GzipIndexes=false update \;; \esac \\&& apt-get install --no-install-recommends --no-install-suggests -y \$nginxPackages \gettext-base \curl \&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \\
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)&& if [ -n "$tempDir" ]; then \apt-get purge -y --auto-remove \&& rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \fi \
# forward request and error logs to docker log collector&& ln -sf /dev/stdout /var/log/nginx/access.log \&& ln -sf /dev/stderr /var/log/nginx/error.log \
# create a docker-entrypoint.d directory&& mkdir /docker-entrypoint.dCOPY docker-entrypoint.sh /
COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
COPY 15-local-resolvers.envsh /docker-entrypoint.d
COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
COPY 30-tune-worker-processes.sh /docker-entrypoint.d
ENTRYPOINT ["/docker-entrypoint.sh"]EXPOSE 80STOPSIGNAL SIGQUITCMD ["nginx", "-g", "daemon off;"]

Dockerfile分析

启动命令分析

ENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE map[80/tcp:{}]
STOPSIGNAL SIGQUIT
CMD ["nginx" "-g" "daemon off;"]
  • ENTRYPOINT 设置了容器启动时执行的主命令,即执行 /docker-entrypoint.sh 脚本。

  • CMD 提供了额外的参数,这些参数将传递给 /docker-entrypoint.sh。

这意味着当容器启动时,实际执行的命令会是 /docker-entrypoint.sh nginx -g "daemon off;"。这里,nginx -g "daemon off;" 是作为参数传递给 /docker-entrypoint.sh 脚本的。

Docker 容器入口点脚本分析

docker-entrypoint.sh

#!/bin/sh
# vim:sw=4:ts=4:etset -e  # 如果任何语句的执行结果不是true则立即退出脚本# 定义一个用于输出日志的函数
entrypoint_log() {# 如果环境变量NGINX_ENTRYPOINT_QUIET_LOGS未设置或为空,打印日志信息if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; thenecho "$@"fi
}# 检查传递给脚本的第一个参数是否是nginx或nginx-debug
if [ "$1" = "nginx" ] || [ "$1" = "nginx-debug" ]; then# 查找/docker-entrypoint.d/目录下是否有文件存在if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; thenentrypoint_log "$0: /docker-entrypoint.d/ 目录不为空,尝试进行配置"entrypoint_log "$0: 在 /docker-entrypoint.d/ 目录中查找shell脚本"# 遍历目录下所有文件,并按版本号排序find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; docase "$f" in*.envsh)# 如果文件具有执行权限,使用source命令执行它if [ -x "$f" ]; thenentrypoint_log "$0: 正在加载 $f";. "$f"else# 如果文件没有执行权限,打印警告日志entrypoint_log "$0: 忽略 $f,没有执行权限";fi;;*.sh)# 如果文件具有执行权限,直接执行它if [ -x "$f" ]; thenentrypoint_log "$0: 正在执行 $f";"$f"else# 如果文件没有执行权限,打印警告日志entrypoint_log "$0: 忽略 $f,没有执行权限";fi;;# 忽略其他类型的文件*) entrypoint_log "$0: 忽略 $f";;esacdoneentrypoint_log "$0: 配置完成;准备启动"elseentrypoint_log "$0: 在 /docker-entrypoint.d/ 目录中未找到文件,跳过配置"fi
fi# 使用传递给脚本的参数替换当前进程,通常用于启动主程序
exec "$@"

exec "$@"

        exec "$@"是上面docker-entrypoint.sh脚本的最后一行,这行命令是 Docker 容器入口点脚本的核心部分,它负责执行容器启动时用户指定的命令。

exec 命令

  • exec 用于执行命令并用该命令替换当前的 shell 进程。这意味着 exec 后面的命令将接管当前的进程,并且当 exec 执行的命令结束时,进程也会结束。

  • 使用 exec 执行命令的好处是,它不会启动新的进程,而是直接在当前进程中运行命令。在 Docker 容器的环境中,这意味着执行的命令(如 nginx)将成为容器进程的主进程。这对于 Docker 容器的生命周期管理非常重要。

"$@" 参数

  • "$@" 是一个特殊的 shell 参数,代表传递给脚本的所有命令行参数的列表,且每个参数都作为独立的引用字符串处理。

  • 在 Docker 容器的上下文中,当运行一个容器并传递命令行参数时,这些参数会被传递给容器的入口点脚本。"$@" 就是用来接收这些参数的。

  • 例如,如果运行 docker run [image] nginx -g "daemon off;",那么 nginx -g "daemon off;" 就是传递给入口点脚本的参数,"$@" 就会包含这些参数。

       

总结

        综上所述,exec "$@" 这行命令的作用是执行传递给入口点脚本的命令(如 nginx -g "daemon off;"),并且以这个命令为容器的主进程。这保证了容器直接运行指定服务,如 Nginx,并且当该服务停止时,容器也会停止。

        在设计其他镜像时,尤其是需要处理信号或需要确保容器优雅停止的场景中,使用 exec "$@" 可以提供类似的优势。例如,数据库镜像、Web 服务器镜像或任何长期运行的服务都可以从这种模式中受益。它确保了容器能够接收到停止信号并优雅地终止,同时也避免了资源泄漏的问题。

        总的来说,使用 exec "$@" 是一种常见的最佳实践,可以提高容器的运行效率和可靠性。在设计容器镜像时,尤其是当容器需要优雅地处理停止和重启信号时,应该考虑采用这种模式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2904547.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

磐启/PAN7030/2.4GHz 无线收发SOC芯片/ESSOP10/SOP16

1 概述 PAN7030 是一款集成 8 位 OTP MCU 和 2.4GHz 无线收发电路芯片&#xff0c;适合应用于玩具小车、 遥控器等领域。 PAN7030 内置 8 位 OTP MCU&#xff0c;包括 1.25KW 的程序存储器、80 字节数据存储器、16 位定 时器和 8 位/11 位 PWM 定时器、看门狗、电压比较器等…

【QT入门】 QTabWidget各种常见用法详解

往期回顾&#xff1a; 【QT入门】 Qt代码创建布局之分裂器布局详解-CSDN博客 【QT入门】 Qt代码创建布局之setLayout使用-CSDN博客 【QT入门】 Qt代码创建布局之多重布局变换与布局删除技巧-CSDN博客 【QT入门】 QTabWidget各种常见用法详解 一般来说&#xff0c;学一个新的控…

出行在外,又想用微软远程桌面控制千里之外的电脑?

前言 说到远程控制&#xff0c;想必很多小伙伴都不陌生。毕竟向日葵软件都是各位所熟知的远程控制软件。 出行在外&#xff0c;使用向日葵远程控制家里或者办公室的电脑是很常见的做法。在各种远程维修电脑的场景下&#xff0c;客服都会要求客户先安装好远程控制软件。 但是向…

探索 2024 年 Web 开发最佳前端框架

前端框架通过简化和结构化的网站开发过程改变了 Web 开发人员设计和实现用户界面的方法。随着 Web 应用程序变得越来越复杂&#xff0c;交互和动画功能越来越多&#xff0c;这是开发前端框架的初衷之一。 在网络的早期&#xff0c;网页相当简单。它们主要以静态 HTML 为特色&a…

在scroll-view中使用input,input键盘弹出时,滚动页面,输入框内容会出现错位问题?

解决办法 <view classpages><view><scroll-view scroll-y"{{sysScroll}}" scroll-top"{{scrollTop}}" class"scroll-hei-2 bg-def">...<input bindfocus"onfocus" bindblur"onblur" placeholder&quo…

kprobe/kretprobe的介绍,原理图,运行情况,用户层代码+内核层代码+预编译代码(详细解释+语法介绍),修改.bpf.c入口处的函数

目录 kprobe/kretprobe 介绍 原理图 运行情况 代码解释 .bpf.c 源码 语法 / 函数接口 char LICENSE[] SEC("license") "Dual BSD/GPL" SEC do_unlinkat filename结构体 ​编辑 BPF_KPROBE bpf_get_current_pid_tgid(…

Vue 04 Vue 中的 Ajax、slot 插槽

Vue学习 Vue 0401 Vue中的Ajax服务器准备axios使用跨域问题解决Vue-CLI 配置代理1Vue-CLI 配置代理2案例: 用户搜索vue-resource 02 slot插槽默认插槽具名插槽作用域插槽slot总结 Vue 04 B站 Vue全家桶&#xff08;BV1Zy4y1K7SH&#xff09; 学习笔记 Vue 中的 ajax 01 Vue中的…

设计模式之原型模式讲解

原型模式本身就是一种很简单的模式&#xff0c;在Java当中&#xff0c;由于内置了Cloneable 接口&#xff0c;就使得原型模式在Java中的实现变得非常简单。UML图如下&#xff1a; 我们来举一个生成新员工的例子来帮助大家理解。 import java.util.Date; public class Employee…

SnapGene 5 for Mac 分子生物学软件

SnapGene 5 for Mac是一款专为Mac操作系统设计的分子生物学软件&#xff0c;以其强大的功能和用户友好的界面&#xff0c;为科研人员提供了高效、便捷的基因克隆和分子实验设计体验。 软件下载&#xff1a;SnapGene 5 for Mac v5.3.1中文激活版 这款软件支持DNA构建和克隆设计&…

python仿真报告自动化——excite TD齿轮角加速度级计算

python仿真报告自动化——excite TD齿轮角加速度级计算 1 、问题-燃油泵相位优化2、难点-excite TD结果文件的提取3、代码 1 、问题-燃油泵相位优化 用excite TD对齿轮系进行仿真&#xff0c;模拟不同燃油泵相位对齿轮传动振动的影响&#xff0c;用齿轮角加速度级作为评价指标…

Redis命令介绍

一、redis启动&#xff1a; 本地启动&#xff1a;redis-cli 远程启动&#xff1a;redis-cli -h host -p port -a password Redis 连接命令 1 AUTH password 验证密码是否正确 2 ECHO message 打印字符串 3 PING 查看服务是否运行 4 QUIT 关闭当前连接 5 SELECT index 切换…

Postman测试含有变量的接口的方法

1.在路径中用/&#xff1a;设置变量名 2.设置token验证 3.填写参数

Cisco Firepower FMCv修改管理Ip方法

FMCv 是部署在VMWARE虚拟平台上的FMC 部署完成后&#xff0c;如何修改管理IP 1 查看当前版本 show version 可以看到是for VMware 2 修改管理IP步骤 2.1 进入expert模式 expert2.2 进入超级用户 sudo su并输入密码 2.3 查看当前网卡Ip 2.4 修改Ip 命令&#xff1a; /…

前端基础知识html

一.基础标签 1.<h1>-<h6>:定义标题&#xff0c;h最大&#xff0c;h最小 2.<font>&#xff1a;定义文本的字体&#xff0c;尺寸&#xff0c;颜色 3.<b>&#xff1a;定义粗体文本 4.<i>&#xff1a;定义斜体文本 5.<u>&#xff1a;定义文本下…

基于js css的瀑布流demo

要实现照片按照瀑布流展示&#xff0c;写个小demo&#xff0c;记录下。 瀑布流实现思路如下&#xff1a; CSS 弹性布局对 3 列按横向排列&#xff0c;对每一列内部按纵向排列 html代码&#xff1a; <div class"content"></div> css代码&#xff1a; …

2024 年广西职业院校技能大赛高职组《云计算应用》赛项样卷

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜…

uniapp调用腾讯图形验证码,兼容h5、APP(安卓)

因项目要兼容安卓APP&#xff0c;所以使用webview做成了一个组件 新建hybrid文件夹&#xff0c;创建要在webview引入的html文件 <!DOCTYPE html> <html><head><meta charset"utf-8" /><meta name"viewport"content"widt…

腾讯云4核8G服务器价格,12M带宽一年646元,送3个月

2024年腾讯云4核8G服务器租用优惠价格&#xff1a;轻量应用服务器4核8G12M带宽646元15个月&#xff0c;CVM云服务器S5实例优惠价格1437.24元买一年送3个月&#xff0c;腾讯云4核8G服务器活动页面 txybk.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器优惠价格 轻…

C#学习笔记5:简单上位机串口助手的实现

今日尝试使用C#进行 自己编写一款上位机串口助手&#xff1a; 文章提供源码、解释、测试效果、整体工程下载...... 以下为串口助手的界面效果&#xff1a; 目录 1、功能设计与摆放控件&#xff1a; 2.遇到的报错解决与基本提示&#xff1a; C#System.InvalidOperationExcept…

镜视界 | DevSecOps CI/CD 管道中数字供应链安全的集成策略

目录 前言 数字供应链&#xff08;DSC&#xff09;的定义 数字供应链安全的重点内容和风险因素 CI/CD管道的安全目标和可信实体 将数字供应链安全集成到CI/CD管道中 结语 本文字数&#xff1a;7715&#xff0c;阅读时长&#xff1a;19分钟 1.前言 在敏捷开发的模式下&…