Docker unionfs(overlayfs) 实现 rootfs

简介: Docker unionfs(overlayfs) 实现 rootfs
unionfs联合文件系统

Docker 中的文件存储驱动叫做 storage driver。


Docker 最早支持的stotage driver是 AUFS,它实际上由一层一层的文件系统组成,这种层级的文件系统叫UnionFS。


联合文件系统(UnionFS):Union 文件系统,是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite serveral directories into a single virtual filesystem)。


Union文件系统是Docker镜像的基础。镜像可以通过分层来进行集成,基于基础镜像可以制作具体的应用镜像。


特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。


后来出现的docker版本中,除了AUFS,还支持OverlayFS、Btrfs、Device Mapper、VFS、ZFS等storage driver。


rootfs定位

置于整个容器的最底层。

66b7cfc9582b41bcbdf5fd22331875b2.png


bootfs和rootfs

bootfs(boot file system)主要包含 bootloader 和 kernel,bootloader主要是引导加载 kernel,Linux刚启动时会加载bootfs文件系统。


在Docker镜像的最底层是引导文件系统bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已经由 bootfs 转交给内核,此时系统也会卸载 bootfs。


rootfs(root file system),在bootfs之上,包含的就是典型Linux系统中的 /dev 、 /proc 、 /bin 、 /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu、CentOS等,是copy-on-write资管管理技术叫写时复制的产物。

84e54c75e3c34160be53db6e8db0f702.png

关于copy-on-write,请拜读这个大佬的blog,此文需要嚼碎这个技术的实现。


big_old_blog_link


常用实现

AUFS

AUFS 只是 Docker 使用的存储驱动的一种,除了 AUFS 之外,Docker 还支持了不同的存储驱动,包括 aufs、devicemapper、overlay2、zfs 和 vfs 等等,在最新的 Docker 中,overlay2 取代了 aufs 成为了推荐的存储驱动,但是在没有 overlay2 驱动的机器上仍然会使用 aufs 作为 Docker 的默认驱动。


overlayfs

Overlayfs 是一种堆叠文件系统,它依赖并建立在其它的文件系统之上(例如 ext4fs 和 xfs 等等),并不直接参与磁盘空间结构的划分,仅仅将原来底层文件系统中不同的目录进行“合并”,然后向用户呈现。


简单的总结为以下3点:


(1)上下层同名目录合并;


(2)上下层同名文件覆盖;


(3)lower dir文件写时拷贝。


操作步骤
[root@master ~]# mkdir ovs
[root@master ~]# cd ovs/
[root@master ovs]# ll
总用量 0
[root@master ovs]# mkdir lower
[root@master ovs]# echo a > lower/a
[root@master ovs]# echo c > lower/c
[root@master ovs]# mkdir upper
[root@master ovs]# echo a1 > upper/a
[root@master ovs]# echo b > upper/b
[root@master ovs]# mkdir merged
[root@master ovs]# mkdir work
#初始内容如下
[root@master ovs]# tree ./
./
├── lower
│   ├── a
│   └── c
├── merged
├── upper
│   ├── a
│   └── b
└── work
    └── work
[root@master ovs]# mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work ./merged
[root@master ovs]# tree ./
./
├── lower
│   ├── a
│   └── c
├── merged
│   ├── a
│   ├── b
│   └── c
├── upper
│   ├── a
│   └── b
└── work
    └── work

merged 目录已经可以同时看到 lower和upper中的文件了,而由于文件a同时存在于lower和upper中,因此被覆盖了,只显示了一个a。现在可以查看下merged中a文件的值,发现是upper中的值,mount挂载的时候指定lower为lowerdir类型,upper为upperdir类型。

[root@master ovs]# cat merged/a
a1

lowerdir是限制为只读,无法进行任何写的操作。


upperdir支持可读可写,可以进行写的操作。


观察下面步骤,可以推出upper跟lower出现同名文件,在merged目录下同名文件内的值是upper目录文件的内容。

[root@master ovs]# echo a2 lower/a
a2 lower/a
[root@master ovs]# cat lower/a
a
[root@master ovs]# cat merged/a
a1

生成的/merged可以作为container的rootfs


思考??????

1、若是对merged目录下文件进行操作,lower、upper目录下文件内容会不会变化呢?

[root@master merged]# ll
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 2 4月  17 17:10 b
-rw-r--r-- 1 root root 2 4月  17 17:02 c
[root@master merged]# cat *
a1
b
c
[root@master merged]# echo testb > b
[root@master merged]# echo testc > c
[root@master merged]# cat *
a1
testb
testc


lower目录

[root@master lower]# ll
总用量 8
-rw-r--r-- 1 root root 2 4月  17 17:02 a
-rw-r--r-- 1 root root 2 4月  17 17:02 c
[root@master lower]# cat c
c
[root@master lower]#

upper目录

[root@master upper]# ll
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
[root@master upper]# cat b c
testb
testc

出现了一个有意思的地方,upper目录本身就只有a和b俩个文件,突然就多了一个c文件,对比前面mount tree后的目录架构,这是在操作merged文件的时候生成的,而且lower值没有变动,却都生成在了upper下。


原因:因为lower不可进行写操作,所以采用了 CoW 技术,在对 c 进行修改时,复制了一份数据到 overlay 的 upperdir,从 lower copy 到 upper,也叫做 copy_up。


2、删除文件会发生事情呢?


现在lower目录生成一个f文件。

[root@master lower]# echo f > f
[root@master lower]# ll
总用量 12
-rw-r--r-- 1 root root 2 4月  17 17:02 a
-rw-r--r-- 1 root root 2 4月  17 17:02 c
-rw-r--r-- 1 root root 2 4月  17 17:34 f

查看upper目录和merged目录框架,f文件出现在了merged,此时upper没有f文件。

[root@master ovs]# cd upper/
[root@master upper]# ll
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
[root@master upper]# cd ../merged/ && ll
总用量 16
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
-rw-r--r-- 1 root root 2 4月  17 17:34 f
[root@master merged]# cat f
f

删除merged中的f文件,查看upper目录和lower目录,upper中出现了一个大小为0的c类型文件f

[root@master merged]# rm -rf f
[root@master merged]# ll
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
[root@master merged]# ll ../upper/
总用量 12
-rw-r--r-- 1 root root    3 4月  17 17:03 a
-rw-r--r-- 1 root root    6 4月  17 17:25 b
-rw-r--r-- 1 root root    6 4月  17 17:25 c
c--------- 1 root root 0, 0 4月  17 17:53 f
[root@master merged]# ll ../lower/
总用量 12
-rw-r--r-- 1 root root 2 4月  17 17:02 a
-rw-r--r-- 1 root root 2 4月  17 17:02 c
-rw-r--r-- 1 root root 2 4月  17 17:34 f

删除upper目录的f文件,就会发现,merged目录中又出现了f文件,若是删除lower中f文件,merged和upper中就没有f文件。

[root@master merged]# rm -rf ../upper/f
[root@master merged]# ll
总用量 16
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
-rw-r--r-- 1 root root 2 4月  17 17:34 f
[root@master merged]# cat f
f
#删除lower中f文件
[root@master merged]# rm -rf ../lower/f
[root@master merged]# ll
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
[root@master merged]# ll ../upper/
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c

换成upper目录生成一个文件执行上述流程,发现merged删除了后,upper目录也没有该文件。

[root@master upper]# ll
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
[root@master upper]# echo g > g
[root@master upper]# ll
总用量 16
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
-rw-r--r-- 1 root root 2 4月  17 18:00 g
[root@master upper]#
[root@master upper]# cd ../merged/ && ll
总用量 16
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
-rw-r--r-- 1 root root 2 4月  17 18:00 g
[root@master merged]# cat g
g
[root@master merged]# rm -rf g
[root@master merged]# ll
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
[root@master merged]# ll ../upper/
总用量 12
-rw-r--r-- 1 root root 3 4月  17 17:03 a
-rw-r--r-- 1 root root 6 4月  17 17:25 b
-rw-r--r-- 1 root root 6 4月  17 17:25 c
[root@master merged]# ll ../lower/
总用量 8
-rw-r--r-- 1 root root 2 4月  17 17:02 a
-rw-r--r-- 1 root root 2 4月  17 17:02 c

综上所述:


lowerdir类型目录的文件,在merged删除后,会在upperdir类型目录生成一个c类型的删除标记,删除该标记,merged内又会出现这个文件,lowerdir类型目录整个过程不受影响,若想真的彻底删除文件,只能在lowerdir类型目录下删除。


upperdir类型目录,在merged删除文件后,upperdir类型目录就直接彻底删除文件,不会有标记。


总之:只有可读类型目录下文件在merged中删除后会在upperdir类型目录生成可回退标记,可写文件在merged中删除后就是直接彻底删除。


如何在docker实际中实现overlay?

思考???

1、merged既然是rootfs,rootfs是一个操作系统,那镜像分层打包是否跟它有关系呢??


2、怎样去理解镜像分层跟dockerfile内copy、add、echo和run等等可写操作的关系?


3、dockerfile生成的文件内容在哪呢?


剖析

docker三大要素:images repository container


image是静态体现,image经过run后进行runtime变成container,利用镜像分层解释,image为镜像层,为只读、不可进行写操作,当容器启动时,一个新的可写层将被加载到镜像的顶部,这一层通常被称为容器层。


猜想:是不是一个容器(包含lowerdir和upperdir)进行了一次commit,他是不是变成了静态也就是镜像层,是不是也就是等于一个merged目录进行了commit,它只是最底层也就是layer0,merged又是lowerdir目录和upperdir目录合并生成的,最终的结果是不是每次执行完写操作后commit都会生成不同大小且内容不一样的rootfs,生成了layer1....layerN?


证实一下

镜像分层理论:layer1是在layer0的基础上套了一层可写层也就是upperdir类型操作,进行了增删改查。一次类推


拿centos:7举例:


/var/lib/docker docker默认存储目录


image:镜像相关


overlay2:docker 文件所在目录,也可能不叫这个名字,具体和文件系统有关,比如可能是 aufs 等


layerdb:docker image layer 信息 元数据


imagedb:docker image 信息 元数据

[root@master merged]# docker images | grep centos
centos                                                            7          eeb6ee3f44bd   19 months ago   204MB
[root@master merged]# cd /var/lib/docker
[root@master docker]# cd image/
[root@master image]# ll
总用量 0
drwx------. 5 root root 81 4月  17 18:36 overlay2
[root@master image]# cd overlay2/
[root@master overlay2]# ll
总用量 8
drwx------. 4 root root   58 4月  12 11:52 distribution
drwx------. 4 root root   37 4月  12 10:52 imagedb
drwx------. 5 root root   45 4月  12 11:54 layerdb
-rw-------  1 root root 4185 4月  17 18:36 repositories.json
[root@master overlay2]# cd imagedb/
[root@master imagedb]# ll
总用量 0
drwx------. 3 root root 20 4月  12 10:52 content
drwx------. 3 root root 20 4月  12 10:52 metadata
[root@master imagedb]# pwd
/var/lib/docker/image/overlay2/imagedb


查看centos镜像的imagedb content,根据imageid查到所属content,查到diff_ids,type为layers,看到只有一层sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02

[root@master sha256]# ll | grep eeb6ee3f44bd
-rw------- 1 root root 2754 4月  17 18:36 eeb6ee3f44bd0b5103bb561b4c16bcb82328cfe5809ab675bb17ab3a16c517c9
[root@master sha256]# cat eeb6ee3f44bd0b5103bb561b4c16bcb82328cfe5809ab675bb17ab3a16c517c9 | jq . | tail
    }
  ],
  "os": "linux",
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02"
    ]
  }
}

编辑dockerfile,模拟写操作

[root@master test]# cat Dockerfile
 FROM centos:7
 RUN echo "Hello world" > /tmp/newfile
[root@master test]# docker build -t hello-centos .
[+] Building 0.6s (6/6) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                  0.0s
 => => transferring dockerfile: 92B                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                     0.0s
 => => transferring context: 2B                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/centos:7                                                                                           0.0s
 => [1/2] FROM docker.io/library/centos:7                                                                                                             0.0s
 => [2/2] RUN echo "Hello world" > /tmp/newfile                                                                                                       0.6s
 => exporting to image                                                                                                                                0.0s
 => => exporting layers                                                                                                                               0.0s
 => => writing image sha256:19fd5667d4b3483ce76a4a219da2c1a8303922c370d4942a1c8a8cef12883c1c                                                          0.0s
 => => naming to docker.io/library/hello-centos                                                                                                       0.0s

查看生成的镜像,会发现在174下又多了一层diff_ids。关注rootfs等于一直在套娃。

[root@master test]# docker images | grep hello
hello-centos                                                      latest     19fd5667d4b3   About a minute ago   204MB
[root@master test]# cd /var/lib/docker
docker/     dockershim/
[root@master test]# cd /var/lib/docker/image/overlay2/imagedb/content/sha256/
[root@master sha256]# ll | grep 19fd5667d4b3
-rw------- 1 root root 2132 4月  17 18:48 19fd5667d4b3483ce76a4a219da2c1a8303922c370d4942a1c8a8cef12883c1c
[root@master sha256]# cat 19fd5667d4b3483ce76a4a219da2c1a8303922c370d4942a1c8a8cef12883c1c | jq . | tail
  "moby.buildkit.buildinfo.v1": "eyJmcm9udGVuZCI6ImRvY2tlcmZpbGUudjAiLCJhdHRycyI6eyJmaWxlbmFtZSI6IkRvY2tlcmZpbGUifSwic291cmNlcyI6W3sidHlwZSI6ImRvY2tlci1pbWFnZSIsInJlZiI6ImRvY2tlci5pby9saWJyYXJ5L2NlbnRvczo3In0seyJ0eXBlIjoiZG9ja2VyLWltYWdlIiwicmVmIjoiZG9ja2VyLmlvL2xpYnJhcnkvY2VudG9zOjciLCJwaW4iOiJzaGEyNTY6MTc0ZjU2ODU0OTAzMjZmYzBhMWMwZjU1NzBiODY2MzczMjE4OWIzMjcwMDdlNDdmZjEzZDJjYTU5NjczZGIwMiJ9XX0=",
  "os": "linux",
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02",
      "sha256:e59ab628089ad0067e9bf80156b7a5ab67af7de217e9f700835dea340c611247"
    ]
  }
}

上述已经证实了思考1和2。


结论如下:

1、每一次镜像打包(commit),就会生成一个新的镜像,是在基础的rootfs上加入写操作生成一个不同大小且内容不一样的rootfs,未运行起来的时候就是一个images,运行起来就是一个等待commit的容器。


2、dockerfile中每次copy、add等写操作都是一次commit,每次commit后就会在原有layer上加一层可写层,所以docker build构建慢的话,尝试优化下写操作的行数,尽量一行搞定。


接着见证下思考3

既然要看文件内容,那找到存储的地方即可


文件含义如下:


cache-id:为具体/var/lib/docker/overlay2/<cache-id>存储路径


diff:diffID,用于计算 ChainID


size:当前 layer 的大小


docker使用了chainID的方式来保存layer,layer.ChainID只用本地,根据layer.DiffID计算,并用于layerdb的目录名称。


chainID唯一标识了一组(像糖葫芦一样的串的底层)diffID的hash值,包含了这一层和它的父层(底层),


当然这个糖葫芦可以有一颗山楂,也就是chainID(layer0)==diffID(layer0);


对于多颗山楂的糖葫芦,ChainID(layerN) = SHA256hex(ChainID(layerN-1) + " " + DiffID(layerN))

[root@master layerdb]# pwd
/var/lib/docker/image/overlay2/layerdb
[root@master layerdb]# ll
总用量 8
drwxr-xr-x 32 root root 4096 4月  17 15:22 mounts
drwxr-xr-x 40 root root 4096 4月  17 18:48 sha256
drwxr-xr-x  2 root root    6 4月  17 18:48 tmp
[root@master layerdb]# cd sha256/ && ll | grep 174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02
drwx------ 2 root root 71 4月  17 18:36 174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02
[root@master sha256]# cd 174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02
[root@master 174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02]# ll
总用量 616
-rw-r--r-- 1 root root     64 4月  17 18:36 cache-id
-rw-r--r-- 1 root root     71 4月  17 18:36 diff
-rw-r--r-- 1 root root      9 4月  17 18:36 size
-rw-r--r-- 1 root root 615388 4月  17 18:36 tar-split.json.gz
[root@master 174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02]# cat diff
sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02

由于是layer0,所以 chainID 就是 diffID,然后开始计算 layer1 的 chainID,

"rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02",
      "sha256:e59ab628089ad0067e9bf80156b7a5ab67af7de217e9f700835dea340c611247"
    ]
  }
[root@master 174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02]# echo -n "sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02 sha256:e59ab628089ad0067e9bf80156b7a5ab67af7de217e9f700835dea340c611247" | sha256sum| awk '{print $1}'
57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6
[root@master sha256]# cd 57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6
[root@master 57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6]# pwd
/var/lib/docker/image/overlay2/layerdb/sha256/57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6
[root@master 57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6]# ll
总用量 20
-rw-r--r-- 1 root root  25 4月  17 18:48 cache-id
-rw-r--r-- 1 root root  71 4月  17 18:48 diff
-rw-r--r-- 1 root root  71 4月  17 18:48 parent
-rw-r--r-- 1 root root   2 4月  17 18:48 size
-rw-r--r-- 1 root root 317 4月  17 18:48 tar-split.json.gz
[root@master 57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6]# cat size
12[root@master 57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6]# cat diff
sha256:e59ab628089ad0067e9bf80156b7a5ab67af7de217e9f700835dea340c611247[root@master 57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6]#
[root@master 57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6]# cat cache-id
5ah0zbkrwbuegl2doi4zee804

计算得知layer1为57f8217a884e130476cafd7384920774cd3d4d488d65863046ff1f4f5f28d5c6,直接去改文件目录下查看cashe-id获取存储名称,获取名称为5ah0zbkrwbuegl2doi4zee804。查看/var/lib/docker/overlay2/5ah0zbkrwbuegl2doi4zee804


查询到newfile,内容为Hello world。

[root@master 5ah0zbkrwbuegl2doi4zee804]# pwd
/var/lib/docker/overlay2/5ah0zbkrwbuegl2doi4zee804
[root@master 5ah0zbkrwbuegl2doi4zee804]# ll
总用量 8
drwxr-xr-x 4 root root 28 4月  17 18:48 diff
-rw-r--r-- 1 root root 26 4月  17 18:48 link
-rw-r--r-- 1 root root 28 4月  17 18:48 lower
drwx------ 3 root root 18 4月  17 18:48 work
[root@master 5ah0zbkrwbuegl2doi4zee804]# cd diff/
[root@master diff]# ll
总用量 0
drwxr-xr-x 2 root root  6 4月  17 18:48 etc
drwxrwxrwt 2 root root 21 4月  17 18:48 tmp
[root@master diff]# cd tmp/
[root@master tmp]# ll
总用量 4
-rw-r--r-- 1 root root 12 4月  17 18:48 newfile
[root@master tmp]# cat newfile
Hello world
[root@master tmp]# pwd
/var/lib/docker/overlay2/5ah0zbkrwbuegl2doi4zee804/diff/tmp

收工~~~

目录
相关文章
|
2月前
|
存储 缓存 Linux
docker的底层原理六: 联合文件系统(UnionFS)
本文介绍了Docker使用的联合文件系统(UnionFS),它通过分层存储和写时复制(CoW)机制,实现了容器的轻量级、高性能存储,支持了镜像继承、数据持久化和隔离性。
101 0
|
4月前
|
C# 开发者 Windows
WPF与PDF文档:解锁创建和编辑PDF文件的新技能——从环境配置到代码实践,手把手教你如何在WPF应用中高效处理PDF,提升文档管理效率
【8月更文挑战第31天】随着数字文档的普及,PDF因跨平台兼容性和高保真度成为重要格式。WPF虽不直接支持PDF处理,但借助第三方库(如iTextSharp)可在WPF应用中实现PDF的创建与编辑。本文通过具体案例和示例代码,详细介绍了如何在WPF中集成PDF库,并展示了从设计用户界面到实现PDF创建与编辑的完整流程。不仅包括创建新文档的基本步骤,还涉及在现有PDF中添加页眉页脚等高级功能。通过这些示例,WPF开发者可以更好地掌握PDF处理技术,提升应用程序的功能性和实用性。
185 0
|
4月前
|
缓存 JavaScript 应用服务中间件
深入理解Docker中的UnionFS联合文件系统及其应用
【8月更文挑战第24天】本文深入探讨了联合文件系统(UnionFS)在Docker中的作用及其实现容器高效运行的机制。UnionFS通过叠加多个文件系统形成统一视图,确保各容器间的文件系统修改相互隔离。在Docker中,镜像由多层构成,通过只读底层与可写顶层的设计极大节省了磁盘空间。文章还分享了最佳实践,包括最小化镜像大小、利用缓存、避免频繁写操作以及使用多阶段构建技术,帮助开发者构建更轻量、高效的Docker容器。
133 0
|
7月前
|
Docker 容器
【Docker】UnionFS 联合文件系统
【1月更文挑战第26天】【Docker】UnionFS 联合文件系统
|
存储 Ubuntu Docker
Docker镜像存储-overlayfs
Docker镜像存储-overlayfs 一、概述   Docker中的镜像采用分层构建设计,每个层可以称之为“layer”,这些layer被存放在了/var/lib/docker//目录下,这里的storage-driver可以有很多种如:AUFS、OverlayFS、VFS、Brtfs等。
2186 0
|
存储 Ubuntu Shell
《自己动手写Docker》书摘之三: Linux UnionFS
UnionFS unionfs是一种为Linux,FreeBSD和NetBSD操作系统设计的把其他文件系统联合到一个联合挂载点的文件系统服务。它使用branch把不同文件系统的文件和目录“透明地”覆盖,形成一个单一一致的文件系统。
9657 0
|
19天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
164 77
|
27天前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
5天前
|
Unix Linux Docker
CentOS停更沉寂,RHEL巨变限制源代:Docker容器化技术的兴起助力操作系统新格局
操作系统是计算机系统的核心软件,管理和控制硬件与软件资源,为用户和应用程序提供高效、安全的运行环境。Linux作为开源、跨平台的操作系统,具有高度可定制性、稳定性和安全性,广泛应用于服务器、云计算、物联网等领域。其发展得益于庞大的社区支持,多种发行版如Ubuntu、Debian、Fedora等满足不同需求。
24 4
|
21天前
|
数据建模 应用服务中间件 nginx
docker替换宿主与容器的映射端口和文件路径
通过正确配置 Docker 的端口和文件路径映射,可以有效地管理容器化应用程序,确保其高效运行和数据持久性。在生产环境中,动态替换映射配置有助于灵活应对各种需求变化。以上方法和步骤提供了一种可靠且易于操作的方案,帮助您轻松管理 Docker 容器的端口和路径映射。
66 3

热门文章

最新文章