[k8s理论知识]5.docker基础(四)Dockerfile构建

容器镜像

前面我们已经介绍了容器的rootfs文件系统。在传统的手动制作roofts的过程中,需要执行以下步骤:

  • 下载基础操作系统的用户态文件
  • 配置文件系统中的各种目录和文件
  • 手动安装依赖、配置环境

这个过程不仅繁琐且容易出错,特别是在需要创建多个不同的环境或频繁更新镜像的场景下。Dockerfile提供了一种声明式的方式来构建和定制容器的根文件系统(rootfs)。通过定义一系列步骤(指令),结合 Docker 的镜像分层机制,能够自动化地构建出一个完整的 rootfs,并确保每次构建时的一致性。

以下是一个示例Dockerfile。用docker build -t my_custom_image .来构建一个容器镜像。注意这里有一个.。这个点代表当前目录下所有文件,和Dockerfile中的COPY .是一个意思。

# 使用官方提供的Python开发镜像作为基础镜像
FROM python:3.9-slim# 将工作目录切换为/app
WORKDIR /app# 将当前目录下的所有内容复制到/app下
ADD . /app# 使用pip命令安装这个应用所需要的依赖
RUN pip install --trusted-host pypi.python.org -r requirements.txt# 允许外界访问容器的80端口
EXPOSE 80# 设置环境变量
ENV NAME World# 设置容器进程为:python app.py,即:这个Python应用的启动命令
CMD ["python", "app.py"]

 CMD的意思是 Dockerfile 指定 python app.py 为这个容器的进程。这里,app.py 的实际路径是 /app/app.py。所以,CMD ["python", "app.py"]等价于"docker run <image> python app.py"。

在使用 Dockerfile 时,你可能还会看到一个叫作 ENTRYPOINT 的原语。实际上,它和 CMD 都是 Docker 容器进程启动所必需的参数,完整执行格式是:“ENTRYPOINT CMD”。但是,默认情况下,Docker 会为你提供一个隐含的 ENTRYPOINT,即:/bin/sh -c。所以,在不指定 ENTRYPOINT 时,比如在我们这个例子里,实际上运行在容器里的完整进程是:/bin/sh -c "python app.py",即 CMD 的内容就是 ENTRYPOINT 的参数。

创建一个应用程序:

[root@master flask-app]# cat app.py 
from flask import Flask
import socket
import osapp = Flask(__name__)@app.route('/')
def hello():html = "<h3>Hello {name}!</h3>" \"<b>Hostname:</b> {hostname}<br/>"           return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname())if __name__ == "__main__":app.run(host='0.0.0.0', port=80)

上面的dockerfile有规定需要的依赖,因此还需要写一个requirements.txt文件。

[root@master flask-app]# cat requirements.txt 
Flask

现在完事具备,进行镜像构建。我们构建了一个叫helloworld的镜像。

[root@master flask-app]# ls
app.py  Dockerfile  requirements.txt
[root@master flask-app]# docker build -t helloworld .
[+] Building 5.9s (9/9) FINISHED                                                                                                      docker:default=> [internal] load build definition from Dockerfile                                                                                            0.0s=> => transferring dockerfile: 519B                                                                                                            0.0s=> [internal] load metadata for docker.io/library/python:3.9-slim                                                                              0.0s=> [internal] load .dockerignore                                                                                                               0.0s=> => transferring context: 2B                                                                                                                 0.0s=> [1/4] FROM docker.io/library/python:3.9-slim                                                                                                0.0s=> [internal] load build context                                                                                                               0.0s=> => transferring context: 109B                                                                                                               0.0s=> CACHED [2/4] WORKDIR /app                                                                                                                   0.0s=> [3/4] ADD . /app                                                                                                                            0.0s=> [4/4] RUN pip install --trusted-host pypi.python.org -r requirements.txt                                                                    5.7s=> exporting to image                                                                                                                          0.1s => => exporting layers                                                                                                                         0.1s => => writing image sha256:b93293d8c593c917c8f49f6ad1c25b9cdc7e7609bf629a64c3f311cc5d4593ee                                                    0.0s => => naming to docker.io/library/helloworld                                                                                                   0.0s 

分别查看基础镜像python:3.9-slim的镜像和helloword的镜像。 

[root@master flask-app]# docker inspect python:3.9-slim
[                                                                                                                                                    ..."RootFS": {"Type": "layers","Layers": ["sha256:e0781bc8667fb5ebf954df4ae52997f6f5568ec9f07e21e5db7c9d324ed41e1f","sha256:cc2286334a7b859aa0ec5587bed4005b2fae27a61f5e4843dd01dfee6ecf185b","sha256:d42276be00b539521cf3a61a5e7b7559f570ca9086b8292bc9e24086a137b524","sha256:f71fab544a97b53995586065bf69aa01f29f3872a311209f03300db323ff23aa","sha256:ec6949936a528cbd8ad7cac0f8bf5373d741c0e16155ea68dbee7238410dd93c"]},"Metadata": {"LastTagTime": "2024-10-18T14:51:00.297312684+08:00"}}
]
[root@master flask-app]# docker inspect helloworld
[..."RootFS": {"Type": "layers","Layers": ["sha256:e0781bc8667fb5ebf954df4ae52997f6f5568ec9f07e21e5db7c9d324ed41e1f","sha256:cc2286334a7b859aa0ec5587bed4005b2fae27a61f5e4843dd01dfee6ecf185b","sha256:d42276be00b539521cf3a61a5e7b7559f570ca9086b8292bc9e24086a137b524","sha256:f71fab544a97b53995586065bf69aa01f29f3872a311209f03300db323ff23aa","sha256:ec6949936a528cbd8ad7cac0f8bf5373d741c0e16155ea68dbee7238410dd93c","sha256:28ed04d7ed6da2e98aa6a62a782506046585764b56b5493a65ff38a61ddf111e","sha256:7bc81f71f3f328c2a6b7072c516b296c175a2a56ce858aaec31126b658fee12d","sha256:49715b59dd2acbf3053e3a43c9d891d32d83b9e46346c79664f57dbce1487ab9"]},"Metadata": {"LastTagTime": "2024-10-18T14:53:06.746455214+08:00"}}
]

 这是因为dockerfile中每写一条指令就增加一层。这里有三个指令就增加了三层。RUN 指令会创建一层,因为它执行了系统命令并修改了文件系统(比如安装软件包)。COPY 指令会创建一层,因为它将主机文件系统中的内容复制到了容器文件系统。WORKDIR 虽然不直接改变文件系统,但它可能也会生成一层,特别是如果它创建了新的目录。

FROM python:3.9-slim
# 指令1:安装依赖
RUN apt-get update && apt-get install -y some-package
# 指令2:复制应用代码
COPY . /app
# 指令3:设置工作目录
WORKDIR /app

但是,如果你使用了多个 RUN 指令,这将会创建两层。

RUN apt-get update
RUN apt-get install -y requirements.txt
启动容器

用下面命令创建一个基于hellowold的容器。可以看到容器进程的PID。

[root@master flask-app]# docker run -d -p 4000:80 helloworld
[root@master flask-app]# docker ps | grep helloworld
a12905d273a8   helloworld                                          "python app.py"           11 seconds ago   Up 10 seconds   0.0.0.0:4000->80/tcp   sweet_diffie
[root@master flask-app]# curl http://localhost:4000
<h3>Hello World!</h3><b>Hostname:</b> a12905d273a8<br/>
[root@master flask-app]# docker inspect --format "{{.State.Pid}}" a12905d273a8
60170

按照上一节的方式查看容器的挂载点。 

root@master flask-app]# docker inspect a12905d273a8
[..."GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/b59a08ebe30ade52611f94365f3556bae223b37d88d0cb7592679afe69f5d8ce-init/diff:/var/lib/docker/overlay2/uit1drj7ap3e8sfd1qomx1mm0/diff:/var/lib/docker/overlay2/jr5fk1tl1ax31hdzhw8t7uib5/diff:/var/lib/docker/overlay2/ta41v10i47ncwxttxy68wq8j1/diff:/var/lib/docker/overlay2/a0ed33e4dde514234341f448746c0d9f0212dd2d36fa6547e903778ac6ac4a4f/diff:/var/lib/docker/overlay2/190310e299528d08415447d03851c32851d7ff87d5abaec51d3568f000938a43/diff:/var/lib/docker/overlay2/50e5422941afaa40a3aeb189c92e2e1d54edc84395139301db3ddef2cc5d6c7e/diff:/var/lib/docker/overlay2/5a2e03241015ce3d3a229d07192bfc2aee321bbdac1493edea090bc19cf979e8/diff:/var/lib/docker/overlay2/ceb41f31ae5d549df537ca2069762f289e0560205a4ff509ede50393c1c2d457/diff","MergedDir": "/var/lib/docker/overlay2/b59a08ebe30ade52611f94365f3556bae223b37d88d0cb7592679afe69f5d8ce/merged","UpperDir": "/var/lib/docker/overlay2/b59a08ebe30ade52611f94365f3556bae223b37d88d0cb7592679afe69f5d8ce/diff","WorkDir": "/var/lib/docker/overlay2/b59a08ebe30ade52611f94365f3556bae223b37d88d0cb7592679afe69f5d8ce/work"},"Name": "overlay2"},

进入容器,查看他的cgroups设置:

[root@master ~]# docker exec -it a12905d273a8 /bin/bash
root@a12905d273a8:/app# cat /proc/self/cgroup
11:pids:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
10:memory:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
9:perf_event:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
8:cpuset:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
7:net_prio,net_cls:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
6:devices:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
5:blkio:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
4:cpuacct,cpu:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
3:hugetlb:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
2:freezer:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope
1:name=systemd:/system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope

 根据cgroups设置找到容器的cpu控制组,查看procs文件,果然发现该容器的PID。

[root@master]# cd /sys/fs/cgroup/cpu
[root@master cpu]# cd system.slice/docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope/
[root@master docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope]# ls
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks
[root@master docker-a12905d273a8ee6c8994d234620f7cc7d888ee44b812b5b173776d9fe09bd3f3.scope]# cat cgroup.procs 
60170
73678

查看该PID的namespace,可以看到,一个进程的每种 Linux Namespace,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 Namespace 文件上。

[root@master flask-app]# ls -l /proc/60170/ns
总用量 0
lrwxrwxrwx 1 root root 0 10月 18 15:05 ipc -> ipc:[4026533030]
lrwxrwxrwx 1 root root 0 10月 18 15:05 mnt -> mnt:[4026533028]
lrwxrwxrwx 1 root root 0 10月 18 15:03 net -> net:[4026533033]
lrwxrwxrwx 1 root root 0 10月 18 15:05 pid -> pid:[4026533031]
lrwxrwxrwx 1 root root 0 10月 18 15:05 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 10月 18 15:05 uts -> uts:[4026533029]
setns()函数

当执行docker exec命令的时候我们可以进入容器的内部。而这个操作所依赖的是setns() 的 Linux 系统调用。它的示例如下:

#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE);} while (0)int main(int argc, char *argv[]) {int fd;fd = open(argv[1], O_RDONLY);if (setns(fd, 0) == -1) {errExit("setns");}execvp(argv[2], &argv[2]); errExit("execvp");
}

这段代码是一个简单的C程序,其功能是通过Linux系统调用来实现进程加入到指定的Namespace中。它接收两个参数,第一个是要加入的Namespace文件的路径,比如/proc/25686/ns/net;第二个参数是要在这个Namespace里运行的进程,比如/bin/bash。代码通过open()系统调用打开指定的Namespace文件,然后将这个文件的描述符传递给setns()函数来实现将当前进程加入到指定的Namespace中。最后使用execvp()函数来执行指定的进程。

编译这段程序,然后执行。传入的参数为容器的PID 60170 和要执行的命令/bin/bash。

gcc -o set_ns set_ns.c 
./set_ns /proc/60170/ns/net /bin/bash 

执行完这些代码之后,我们就已经进入了这个容器的内部,具体表现为,查看网卡ifconfig,发现只有一个网卡,而宿主机是有四个网卡的,另一点是,当执行exit后,发现真的退出了。此时执行ifconfig,发现可以显示宿主机全部网卡。

[root@master flask-app]# ./set_ns /proc/60170/ns/net /bin/bash 
[root@master flask-app]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)RX packets 9  bytes 632 (632.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 7  bytes 650 (650.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536inet 127.0.0.1  netmask 255.0.0.0inet6 ::1  prefixlen 128  scopeid 0x10<host>loop  txqueuelen 1000  (Local Loopback)RX packets 0  bytes 0 (0.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0[root@master flask-app]# exit
exit
[root@master flask-app]# ifconfig
cali09721b04d08: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1480ether ee:ee:ee:ee:ee:ee  txqueuelen 0  (Ethernet)RX packets 0  bytes 0 (0.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0calibefc89337fa: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1480ether ee:ee:ee:ee:ee:ee  txqueuelen 0  (Ethernet)RX packets 224212076  bytes 41421978547 (38.5 GiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 224212076  bytes 41421978547 (38.5 GiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0calid0c8c564fc3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1480ether ee:ee:ee:ee:ee:ee  txqueuelen 0  (Ethernet)RX packets 1102176  bytes 371634074 (354.4 MiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 731015  bytes 62569826 (59.6 MiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0calif5aeba59a79: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1480ether ee:ee:ee:ee:ee:ee  txqueuelen 0  (Ethernet)RX packets 7  bytes 650 (650.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 9  bytes 632 (632.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255ether 02:42:34:93:20:be  txqueuelen 0  (Ethernet)RX packets 532  bytes 37600 (36.7 KiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 720  bytes 993730 (970.4 KiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500inet 192.168.244.128  netmask 255.255.255.0  broadcast 192.168.244.255inet6 fe80::d245:ea72:a2e1:17d9  prefixlen 64  scopeid 0x20<link>inet6 fe80::f1be:e2dd:e05c:eb75  prefixlen 64  scopeid 0x20<link>inet6 fe80::74a7:3914:ebcb:c52e  prefixlen 64  scopeid 0x20<link>ether 00:0c:29:19:01:f6  txqueuelen 1000  (Ethernet)RX packets 1102176  bytes 371634074 (354.4 MiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 731015  bytes 62569826 (59.6 MiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536inet 127.0.0.1  netmask 255.0.0.0loop  txqueuelen 1000  (Local Loopback)RX packets 224212076  bytes 41421978547 (38.5 GiB)RX errors 0  dropped 0  overruns 0  frame 0TX packets 224212076  bytes 41421978547 (38.5 GiB)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480inet 10.244.219.64  netmask 255.255.255.255tunnel   txqueuelen 1000  (IPIP Tunnel)RX packets 0  bytes 0 (0.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0veth7ebbc12: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500ether 56:0c:a3:6b:57:e5  txqueuelen 0  (Ethernet)RX packets 7  bytes 650 (650.0 B)RX errors 0  dropped 0  overruns 0  frame 0TX packets 9  bytes 632 (632.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

执行setns()可以进入容器内部,可以通过另一个实验来验证。查看当前容器的namespace与我们执行的set_ns的namespace。执行ps -eo pid,ppid可以查看pid以及他的父pid。通过pid之间的关联,判断36219是执行的./set_ns 的进程,而95158是该setns进入容器后的bin/bash进程。查看95158的命名空间和容器60170的命名空间,发现是一样的。这可以从另一个方面说明setns进入了容器的内部。

[root@master ~]# ps -eo pid,ppid,cmd | grep /bin/bash36219  36218 /bin/bash95158  36219 /bin/bash96815  16207 grep --color=auto /bin/bash
[root@master ~]# ls -l /proc/95158/ns/net
lrwxrwxrwx 1 root root 0 10月 18 15:40 /proc/95158/ns/net -> net:[4026533033]
[root@master ~]# ls -l /proc/60170/ns/net
lrwxrwxrwx 1 root root 0 10月 18 15:03 /proc/60170/ns/net -> net:[4026533033]

既然知道他们进入了同一个命名空间,那么可以通过指定命名空间的方式让另一个进程进入这个容器的内部。

docker run -it --net container:a12905d273a8 busybox: v1 ifconfig

此时执行ifconfig,和上面setns执行的结果是一样的。

而如果我指定–net=host,就意味着这个容器不会为进程启用 Network Namespace。这就意味着,这个容器拆除了 Network Namespace 的“隔离墙”,所以,它会和宿主机上的其他普通进程一样,直接共享宿主机的网络栈。这就为容器直接操作和使用宿主机网络提供了一个渠道。

docker exec

docker exec 命令就是通过setns()函数进入容器内部的。

现在我们运行一个exec命令。已知一个容器的pid 1为主进程,在我们的例子中是app.y,而docker exec实际上是把bin/bash进程放入了和容器相同的命名空间里面,所以当我们查看进程的时候,会有一个进程编号为17的进程。

[root@master flask-app]# docker exec -it a12905d273a8 /bin/bash
root@a12905d273a8:/app# apt-get update
...
root@a12905d273a8:/app# apt-get install -y procps
...
root@a12905d273a8:/app# psPID TTY          TIME CMD17 pts/0    00:00:00 bash187 pts/0    00:00:00 ps
root@a12905d273a8:/app# ps -e -o pid,tty,time,cmdPID TT           TIME CMD1 ?        00:00:03 python app.py17 pts/0    00:00:00 /bin/bash188 pts/0    00:00:00 ps -e -o pid,tty,time,cmd

此时在宿主机上查看,可以看到docker exec本身就是一个进程,对应之前的set_ns本身就是一个进程。通过docker top <containerID>查看pid,可以更清晰的看到,这里的bin/bash命令在宿主机上的进程id为43646。而docker exe 启动的bin/bash进程和容器本身都有相同的父进程,为docker的守护进程dockerd。

[root@master ~]# ps aux | grep bin/bash
root      36219  0.0  0.0 115756  2272 pts/1    S    10:33   0:00 /bin/bash
root      43581  1.1  0.4 1518912 16424 pts/1   Sl+  20:59   0:00 docker exec -it a12905d273a8 /bin/bash
root      43646  0.5  0.0   4592  2052 pts/0    Ss+  20:59   0:00 /bin/bash
root      43717  0.0  0.0 112824   988 pts/2    S+   20:59   0:00 grep --color=auto bin/bash
[root@master ~]# ps -eo pid,ppid,cmd | grep /bin/bash36219  36218 /bin/bash43581  36219 docker exec -it a12905d273a8 /bin/bash43646  60148 /bin/bash44721  16207 grep --color=auto /bin/bash
[root@master ~]# docker top a12905d273a8
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                43646               60148               0                   20:59               pts/0               00:00:00            /bin/bash
root                60170               60148               0                   15:03               ?                   00:00:03            python app.py

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

限制游客在wordpress某分类下阅读文章的数量

在WordPress中实现某个分类下的内容限制游客只能阅读前5篇文章&#xff0c;注册用户可以阅读更多文章的功能&#xff0c;可以通过以下步骤来完成&#xff1a; 1. 安装和激活插件 首先&#xff0c;你可以使用一个插件来简化这个过程。一个常用的插件是 “MemberPress” 或 “R…

三菱FX3U-4AD模块怎样读出模拟量数据的?

关于使用FX3U-4AD读出模拟量数据时&#xff0c;所需的最低限度的程序&#xff0c;就此进行说明。读出模拟量数据的步骤如下所示&#xff1a; 1、确认单元号 从左侧的特殊功能单元/模块开始&#xff0c;依次分配单元号0~7。连接在FX3UC-32MT-LT(-2)可编程控制器上时&#xff0c…

LeetCode 3191.使二进制数组全部等于 1 的最少操作次数 I:模拟(说是最小操作次数,其实不重复翻转就是了)

【LetMeFly】3191.使二进制数组全部等于 1 的最少操作次数 I&#xff1a;模拟&#xff08;说是最小操作次数&#xff0c;其实不重复翻转就是了&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/minimum-operations-to-make-binary-array-elements-equal-to…

ubuntu24.0离线安装Ollama和纯cpu版本以及对接Spring AI

文章目录 一.官网下载 0.3.13版本二.将文件包上传至ubuntu服务器三.下载安装脚本四.剔除GPU相关下载ROCM等&#xff0c;纯CPU运行脚本五.ollama常用命令六. 远程测试 七.对接spring AI 一.官网下载 0.3.13版本 ollama离线安装包下载地址 二.将文件包上传至ubuntu服务器 三.下…

【openwrt-21.02】openwrt中NPT(network prefix translation)实现举例

参考链接 [OpenWrt Wiki] NAT examples IPv4 NPT 启用 IPv4 到 IPv4 网络前缀转换 cat << "EOF" > /etc/nftables.d/npt.sh LAN_PFX="192.168.1.0/24" WAN_PFX="192.168.2.0/24" . /lib/functions/network.sh network_flush_cache …

电感电容谐振原理及Matlab仿真

一、电感电容谐振原理概述 电感电容谐振&#xff08;LC谐振&#xff09;是一种电路现象&#xff0c;它发生在电感器&#xff08;L&#xff09;和电容器&#xff08;C&#xff09;通过适当的方式连接时&#xff0c;电路中电流和电压之间形成共振。在这种共振状态下&#xff0c;…

计算机组成原理与系统结构——外部存储器

笔记内容及图片整理自XJTUSE “计算机组成原理与系统结构” 课程ppt&#xff0c;仅供学习交流使用&#xff0c;谢谢。 磁盘 磁盘是一个由非磁性材料构成的圆形盘片&#xff08;称为基片&#xff09;&#xff0c;上面涂抹可磁化材料。传统的基片一直是铝制或铝合金的&#xff0…

基于SpringBoot+Vue+Uniapp汽车保养系统小程序的设计与实现

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而…

linux下使用VSCODE 调试python

文章目录 一、环境准备安装VS Code&#xff1a;安装Python&#xff1a; 二、环境测试创建Python文件&#xff1a;编写测试代码运行 Linux下使用VS Code调试Python 在Linux环境中进行Python开发时&#xff0c;一个高效、直观的调试工具是必不可少的。Visual Studio Code&#xf…

2024.10月18日- Vue2组件开发(3)

Vue组件开发 一、 ref属性 如果在vue里&#xff0c;想要获取DOM对象&#xff0c;并且不想使用JS的原生语法&#xff0c;那么就可以使用ref属性。ref属性的用法&#xff1a; 1&#xff09;在HTML元素的开始标记中&#xff0c;或者在Vue子组件中的开始标记中定义&#xff0c;相…

第 5 章:vuex

1. 理解 vuex vuex 是什么&#xff1a; 概念&#xff1a;专门在 Vue 中实现集中式状态&#xff08;数据&#xff09;管理的一个 Vue 插件&#xff0c;对 vue 应用中多个组件的共享状态进行集中式的管理&#xff08;读/写&#xff09;&#xff0c;也是一种组件间通信的方式&am…

ant design vue TimePicker时间选择器不点击确认也可以设置值

文章目录 前言一、背景二、操作步骤1.复现前的准备工作&#xff08;1&#xff09;vue版本和ant design vue 版本&#xff08;2&#xff09;任意ant design vue TimePicker的demo 2.解决问题&#xff08;1&#xff09;使用change时间&#xff08;无效&#xff09;&#xff08;2&…

Python10.15作业

2、写程序&#xff0c;输入一个字符串&#xff0c; 打印字符串的如下内容 打印这个字符串的第一个字符打印这个字符串的最后一个字符串如果这个字符串的长度是 奇数&#xff0c;则打印中间这个字符 用 len(x) 求字符串的长度 #2 s1input(“请输入字符串”) print(s1[0]) pri…

uniapp上如何绑定全局事件总线(引入自定义全局组件例如弹窗)

1、在main.js中挂载bus // main.js Vue.prototype.$bus new Vue(); uni.$bus Vue.prototype.$bus; // 确保在 uni 上绑定 2、写一个全局弹窗组件 <template><view v-if"visible" class"toast-container"><view class"icon-conta…

DEV C++自动补全文件头的设置操作

第一步&#xff1a;打开DEV C 第二步&#xff1a;打开“工具” 第三步&#xff1a;点击“编辑器属性” 第四步&#xff1a;点击“代码” 第五步&#xff1a;点击“缺省源” 第六步&#xff1a;输入常用的文件头代码&#xff1a; 例如&#xff1a; #include<bits/stdc.h&g…

数据结构(JAVA)包装类泛型

文章目录 包装类基本数据类型和对应的包装类装箱和拆箱面试题 泛型什么是泛型泛型的语法泛型类的使用泛型的使用裸类型(Raw Type) &#xff08;仅需了解&#xff09;擦除机制泛型的上界泛型方法 包装类 基本数据类型和对应的包装类 注意&#xff0c;除了int基本数据类型的包装…

OracleT5-2 Solaris11安装

1、Solaris11安装 在光驱中插入Solaris11的光盘后,在ok提示中boot cdrom {0} ok boot cdrom NOTICE: Entering OpenBoot. NOTICE: Fetching Guest MD from HV. NOTICE: Starting additional cpus. NOTICE: Initializing LDC services. NOTICE: Probing PCI devices. N…

《 C++ 修炼全景指南:十六 》玩转 C++ 特殊类:C++ 六种必备特殊类设计的全面解析

摘要 这篇博客深入探讨了六种 C 特殊类的设计及其技术细节。首先&#xff0c;介绍了如何设计只能在堆上或栈上创建对象的类&#xff0c;通过控制构造函数的访问权限来限定对象的内存分配区域。接着&#xff0c;探讨了如何设计一个不能被拷贝的类&#xff0c;避免资源重复释放的…

玄机平台-应急响应-webshell查杀

首先xshell连接 然后进入/var/www/html目录中&#xff0c;将文件变成压缩包 cd /var/www/html tar -czvf web.tar.gz ./* 开启一个http.server服务&#xff0c;将文件下载到本地 python3 -m http.server 放在D盾中检测 基本可以确认木马文件就是这四个 /var/www/html/shell.p…

Qt通过QProcess调用第三方进程

我们在运行Qt程序时&#xff0c;有时需要调用第三方程序&#xff0c;这时就可以用QProcess类。具体如下&#xff1a; 一. 启用进程之前 我们需要在头文件中定义一个私有变量指针&#xff0c;为了使他能长时间运行&#xff0c;定义为指针。 #include <QProcess> class …