鉴于在集群上构建安全工具的情况越来越频繁,并且现在一些安全工具也提供了Docker形式的部署,再停留在虚拟机+yum的部署方式似乎已经不太合时宜了。所以在再一次碰到一个安全工具需要使用docker安装的时候,我们毅然(被逼)转向了Docker。希望以后能够就在Docker上部署安全工具吧。
一、CentOS-stream-8上的Docker安装
Docker的安装还是比较简单的,尤其是在线上的情况下,国内也有镜像网站直接提供了安装脚本,但是长期被各种安装步骤折磨的我们还是选择了手撸,以便在某一步出现问题的时候能够及时定位。
1.添加Docker镜像库
首先要将Docker的镜像库添加到centos中。鉴于最近终于搬进了崭新的办公区,上网环境得到大幅改善,所以就不去折腾离线安装了(如果需要离线,应该方法和我们之前在没有网络时痛苦折腾的情况相当)。
使用yum-config-manager --add-repo命令,可以将官方repo地址添加到yum.repos.d目录下,成功完成命令后,可以在该目录下发现一个名为docker-ce.repo的文件,cat一下可以观察到该命令导入的docker repo仓库地址。
[root@localhost ~]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
添加仓库自:https://download.docker.com/linux/centos/docker-ce.repo
[root@localhost ~]# cd /etc/yum.repos.d/
[root@localhost yum.repos.d]# ls
CentOS-Stream-AppStream.repo CentOS-Stream-Extras.repo CentOS-Stream-PowerTools.repo docker-ce.repo
CentOS-Stream-BaseOS.repo CentOS-Stream-HighAvailability.repo CentOS-Stream-RealTime.repo
CentOS-Stream-Debuginfo.repo CentOS-Stream-Media.repo CentOS-Stream-ResilientStorage.repo
CentOS-Stream-Extras-common.repo CentOS-Stream-NFV.repo CentOS-Stream-Sources.repo
[root@localhost yum.repos.d]# cat docker-ce.repo
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-stable-debuginfo]
name=Docker CE Stable - Debuginfo $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/debug-$basearch/stable
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-stable-source]
name=Docker CE Stable - Sources
baseurl=https://download.docker.com/linux/centos/$releasever/source/stable
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-test]
name=Docker CE Test - $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/test
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-test-debuginfo]
name=Docker CE Test - Debuginfo $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/debug-$basearch/test
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-test-source]
name=Docker CE Test - Sources
baseurl=https://download.docker.com/linux/centos/$releasever/source/test
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-nightly]
name=Docker CE Nightly - $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/nightly
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-nightly-debuginfo]
name=Docker CE Nightly - Debuginfo $basearch
baseurl=https://download.docker.com/linux/centos/$releasever/debug-$basearch/nightly
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
[docker-ce-nightly-source]
name=Docker CE Nightly - Sources
baseurl=https://download.docker.com/linux/centos/$releasever/source/nightly
enabled=0
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
2.清除centos中的podman
一般来说,Centos中默认安装了podman,centos的自带“docker”,据说连命令格式都一样,没有深究。
[root@localhost ~]# rpm -qa|grep 'podman'
podman-4.2.0-1.module_el8.7.0+1216+b022c01d.x86_64
cockpit-podman-53-1.module_el8.7.0+1216+b022c01d.noarch
podman-catatonit-4.2.0-1.module_el8.7.0+1216+b022c01d.x86_64
[root@localhost ~]# rpm -qa|grep 'buildah'
buildah-1.27.0-2.module_el8.7.0+1216+b022c01d.x86_64
[root@localhost ~]#
还有buildah,一并删掉了之。否则安装docker的时候会报错。
[root@localhost ~]# yum rm podman buildah
依赖关系解决。
====================================================================================================================================
软件包 架构 版本 仓库 大小
====================================================================================================================================
移除:
buildah x86_64 1:1.24.2-2.module_el8.7.0+1106+45480ee0 @AppStream 30 M
podman x86_64 2:4.0.2-1.module_el8.7.0+1106+45480ee0 @AppStream 51 M
移除依赖的软件包:
cockpit-podman noarch 43-1.module_el8.7.0+1106+45480ee0 @AppStream 493 k
清除未被使用的依赖关系:
conmon x86_64 2:2.1.0-1.module_el8.7.0+1106+45480ee0 @AppStream 172 k
container-selinux noarch 2:2.180.0-1.module_el8.7.0+1106+45480ee0 @AppStream 54 k
containers-common x86_64 2:1-23.module_el8.7.0+1106+45480ee0 @AppStream 361 k
criu x86_64 3.15-3.module_el8.6.0+926+8bef8ae7 @AppStream 1.4 M
fuse-overlayfs x86_64 1.8.2-1.module_el8.7.0+1106+45480ee0 @AppStream 145 k
fuse3 x86_64 3.3.0-15.el8 @anaconda 100 k
fuse3-libs x86_64 3.3.0-15.el8 @anaconda 274 k
libnet x86_64 1.1.6-15.el8 @AppStream 170 k
libslirp x86_64 4.4.0-1.module_el8.6.0+926+8bef8ae7 @AppStream 134 k
podman-catatonit x86_64 2:4.0.2-1.module_el8.7.0+1106+45480ee0 @AppStream 764 k
runc x86_64 1:1.0.3-3.module_el8.7.0+1106+45480ee0 @AppStream 11 M
shadow-utils-subid x86_64 2:4.6-16.el8 @anaconda 205 k
slirp4netns x86_64 1.1.8-2.module_el8.7.0+1106+45480ee0 @AppStream 98 k
事务概要
====================================================================================================================================
移除 16 软件包
将会释放空间:96 M
确定吗?[y/N]: y
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
准备中 : 1/1
运行脚本: buildah-1:1.24.2-2.module_el8.7.0+1106+45480ee0.x86_64 1/1
删除 : buildah-1:1.24.2-2.module_el8.7.0+1106+45480ee0.x86_64 1/16
删除 : cockpit-podman-43-1.module_el8.7.0+1106+45480ee0.noarch 2/16
删除 : podman-2:4.0.2-1.module_el8.7.0+1106+45480ee0.x86_64 3/16
运行脚本: podman-2:4.0.2-1.module_el8.7.0+1106+45480ee0.x86_64 3/16
删除 : containers-common-2:1-23.module_el8.7.0+1106+45480ee0.x86_64 4/16
删除 : container-selinux-2:2.180.0-1.module_el8.7.0+1106+45480ee0.noarch 5/16
运行脚本: container-selinux-2:2.180.0-1.module_el8.7.0+1106+45480ee0.noarch 5/16
删除 : podman-catatonit-2:4.0.2-1.module_el8.7.0+1106+45480ee0.x86_64 6/16
删除 : fuse-overlayfs-1.8.2-1.module_el8.7.0+1106+45480ee0.x86_64 7/16
删除 : fuse3-3.3.0-15.el8.x86_64 8/16
删除 : slirp4netns-1.1.8-2.module_el8.7.0+1106+45480ee0.x86_64 9/16
删除 : runc-1:1.0.3-3.module_el8.7.0+1106+45480ee0.x86_64 10/16
删除 : criu-3.15-3.module_el8.6.0+926+8bef8ae7.x86_64 11/16
删除 : libnet-1.1.6-15.el8.x86_64 12/16
运行脚本: libnet-1.1.6-15.el8.x86_64 12/16
删除 : libslirp-4.4.0-1.module_el8.6.0+926+8bef8ae7.x86_64 13/16
删除 : fuse3-libs-3.3.0-15.el8.x86_64 14/16
运行脚本: fuse3-libs-3.3.0-15.el8.x86_64 14/16
删除 : conmon-2:2.1.0-1.module_el8.7.0+1106+45480ee0.x86_64 15/16
删除 : shadow-utils-subid-2:4.6-16.el8.x86_64 16/16
运行脚本: shadow-utils-subid-2:4.6-16.el8.x86_64 16/16
验证 : buildah-1:1.24.2-2.module_el8.7.0+1106+45480ee0.x86_64 1/16
验证 : cockpit-podman-43-1.module_el8.7.0+1106+45480ee0.noarch 2/16
验证 : conmon-2:2.1.0-1.module_el8.7.0+1106+45480ee0.x86_64 3/16
验证 : container-selinux-2:2.180.0-1.module_el8.7.0+1106+45480ee0.noarch 4/16
验证 : containers-common-2:1-23.module_el8.7.0+1106+45480ee0.x86_64 5/16
验证 : criu-3.15-3.module_el8.6.0+926+8bef8ae7.x86_64 6/16
验证 : fuse-overlayfs-1.8.2-1.module_el8.7.0+1106+45480ee0.x86_64 7/16
验证 : fuse3-3.3.0-15.el8.x86_64 8/16
验证 : fuse3-libs-3.3.0-15.el8.x86_64 9/16
验证 : libnet-1.1.6-15.el8.x86_64 10/16
验证 : libslirp-4.4.0-1.module_el8.6.0+926+8bef8ae7.x86_64 11/16
验证 : podman-2:4.0.2-1.module_el8.7.0+1106+45480ee0.x86_64 12/16
验证 : podman-catatonit-2:4.0.2-1.module_el8.7.0+1106+45480ee0.x86_64 13/16
验证 : runc-1:1.0.3-3.module_el8.7.0+1106+45480ee0.x86_64 14/16
验证 : shadow-utils-subid-2:4.6-16.el8.x86_64 15/16
验证 : slirp4netns-1.1.8-2.module_el8.7.0+1106+45480ee0.x86_64 16/16
已移除:
buildah-1:1.24.2-2.module_el8.7.0+1106+45480ee0.x86_64 cockpit-podman-43-1.module_el8.7.0+1106+45480ee0.noarch
conmon-2:2.1.0-1.module_el8.7.0+1106+45480ee0.x86_64 container-selinux-2:2.180.0-1.module_el8.7.0+1106+45480ee0.noarch
containers-common-2:1-23.module_el8.7.0+1106+45480ee0.x86_64 criu-3.15-3.module_el8.6.0+926+8bef8ae7.x86_64
fuse-overlayfs-1.8.2-1.module_el8.7.0+1106+45480ee0.x86_64 fuse3-3.3.0-15.el8.x86_64
fuse3-libs-3.3.0-15.el8.x86_64 libnet-1.1.6-15.el8.x86_64
libslirp-4.4.0-1.module_el8.6.0+926+8bef8ae7.x86_64 podman-2:4.0.2-1.module_el8.7.0+1106+45480ee0.x86_64
podman-catatonit-2:4.0.2-1.module_el8.7.0+1106+45480ee0.x86_64 runc-1:1.0.3-3.module_el8.7.0+1106+45480ee0.x86_64
shadow-utils-subid-2:4.6-16.el8.x86_64 slirp4netns-1.1.8-2.module_el8.7.0+1106+45480ee0.x86_64
完毕!
3.安装Docker组件
[root@localhost ~]# yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
上次元数据过期检查:0:26:26 前,执行于 2022年11月03日 星期四 06时40分31秒。
依赖关系解决。
====================================================================================================================================
软件包 架构 版本 仓库 大小
====================================================================================================================================
安装:
containerd.io x86_64 1.6.9-3.1.el8 docker-ce-stable 33 M
docker-ce x86_64 3:20.10.21-3.el8 docker-ce-stable 21 M
docker-ce-cli x86_64 1:20.10.21-3.el8 docker-ce-stable 30 M
docker-compose-plugin x86_64 2.12.2-3.el8 docker-ce-stable 10 M
安装依赖关系:
container-selinux noarch 2:2.189.0-1.module_el8.7.0+1216+b022c01d appstream 60 k
docker-ce-rootless-extras x86_64 20.10.21-3.el8 docker-ce-stable 4.6 M
fuse-overlayfs x86_64 1.9-1.module_el8.7.0+1216+b022c01d appstream 73 k
fuse3 x86_64 3.3.0-16.el8 baseos 54 k
fuse3-libs x86_64 3.3.0-16.el8 baseos 95 k
libcgroup x86_64 0.41-19.el8 baseos 70 k
libslirp x86_64 4.4.0-1.module_el8.7.0+1216+b022c01d appstream 70 k
slirp4netns x86_64 1.2.0-2.module_el8.7.0+1216+b022c01d appstream 54 k
安装弱的依赖:
docker-scan-plugin x86_64 0.21.0-3.el8 docker-ce-stable 3.8 M
事务概要
====================================================================================================================================
安装 13 软件包
总下载:103 M
安装大小:394 M
下载软件包:
(1/13): libslirp-4.4.0-1.module_el8.7.0+1216+b022c01d.x86_64.rpm 1.0 MB/s | 70 kB 00:00
(2/13): container-selinux-2.189.0-1.module_el8.7.0+1216+b022c01d.noarch.rpm 819 kB/s | 60 kB 00:00
(3/13): fuse-overlayfs-1.9-1.module_el8.7.0+1216+b022c01d.x86_64.rpm 947 kB/s | 73 kB 00:00
(4/13): slirp4netns-1.2.0-2.module_el8.7.0+1216+b022c01d.x86_64.rpm 2.5 MB/s | 54 kB 00:00
(5/13): fuse3-3.3.0-16.el8.x86_64.rpm 2.5 MB/s | 54 kB 00:00
(6/13): fuse3-libs-3.3.0-16.el8.x86_64.rpm 2.2 MB/s | 95 kB 00:00
(7/13): libcgroup-0.41-19.el8.x86_64.rpm 2.0 MB/s | 70 kB 00:00
(8/13): docker-ce-20.10.21-3.el8.x86_64.rpm 248 kB/s | 21 MB 01:26
(9/13): docker-ce-rootless-extras-20.10.21-3.el8.x86_64.rpm 232 kB/s | 4.6 MB 00:20
(10/13): docker-ce-cli-20.10.21-3.el8.x86_64.rpm 245 kB/s | 30 MB 02:03
(11/13): docker-scan-plugin-0.21.0-3.el8.x86_64.rpm 239 kB/s | 3.8 MB 00:16
(12/13): containerd.io-1.6.9-3.1.el8.x86_64.rpm 237 kB/s | 33 MB 02:22
(13/13): docker-compose-plugin-2.12.2-3.el8.x86_64.rpm 258 kB/s | 10 MB 00:40
------------------------------------------------------------------------------------------------------------------------------------
总计 706 kB/s | 103 MB 02:29
Docker CE Stable - x86_64 1.9 kB/s | 1.6 kB 00:00
导入 GPG 公钥 0x621E9F35:
Userid: "Docker Release (CE rpm) <docker@docker.com>"
指纹: 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35
来自: https://download.docker.com/linux/centos/gpg
导入公钥成功
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
准备中 : 1/1
安装 : docker-scan-plugin-0.21.0-3.el8.x86_64 1/13
运行脚本: docker-scan-plugin-0.21.0-3.el8.x86_64 1/13
安装 : fuse3-libs-3.3.0-16.el8.x86_64 2/13
运行脚本: fuse3-libs-3.3.0-16.el8.x86_64 2/13
运行脚本: container-selinux-2:2.189.0-1.module_el8.7.0+1216+b022c01d.noarch 3/13
安装 : container-selinux-2:2.189.0-1.module_el8.7.0+1216+b022c01d.noarch 3/13
运行脚本: container-selinux-2:2.189.0-1.module_el8.7.0+1216+b022c01d.noarch 3/13
安装 : containerd.io-1.6.9-3.1.el8.x86_64 4/13
运行脚本: containerd.io-1.6.9-3.1.el8.x86_64 4/13
安装 : fuse3-3.3.0-16.el8.x86_64 5/13
安装 : fuse-overlayfs-1.9-1.module_el8.7.0+1216+b022c01d.x86_64 6/13
运行脚本: fuse-overlayfs-1.9-1.module_el8.7.0+1216+b022c01d.x86_64 6/13
安装 : docker-ce-cli-1:20.10.21-3.el8.x86_64 7/13
运行脚本: docker-ce-cli-1:20.10.21-3.el8.x86_64 7/13
运行脚本: libcgroup-0.41-19.el8.x86_64 8/13
安装 : libcgroup-0.41-19.el8.x86_64 8/13
运行脚本: libcgroup-0.41-19.el8.x86_64 8/13
安装 : libslirp-4.4.0-1.module_el8.7.0+1216+b022c01d.x86_64 9/13
安装 : slirp4netns-1.2.0-2.module_el8.7.0+1216+b022c01d.x86_64 10/13
安装 : docker-ce-rootless-extras-20.10.21-3.el8.x86_64 11/13
运行脚本: docker-ce-rootless-extras-20.10.21-3.el8.x86_64 11/13
安装 : docker-ce-3:20.10.21-3.el8.x86_64 12/13
运行脚本: docker-ce-3:20.10.21-3.el8.x86_64 12/13
安装 : docker-compose-plugin-2.12.2-3.el8.x86_64 13/13
运行脚本: docker-compose-plugin-2.12.2-3.el8.x86_64 13/13
运行脚本: container-selinux-2:2.189.0-1.module_el8.7.0+1216+b022c01d.noarch 13/13
运行脚本: docker-compose-plugin-2.12.2-3.el8.x86_64 13/13
验证 : container-selinux-2:2.189.0-1.module_el8.7.0+1216+b022c01d.noarch 1/13
验证 : fuse-overlayfs-1.9-1.module_el8.7.0+1216+b022c01d.x86_64 2/13
验证 : libslirp-4.4.0-1.module_el8.7.0+1216+b022c01d.x86_64 3/13
验证 : slirp4netns-1.2.0-2.module_el8.7.0+1216+b022c01d.x86_64 4/13
验证 : fuse3-3.3.0-16.el8.x86_64 5/13
验证 : fuse3-libs-3.3.0-16.el8.x86_64 6/13
验证 : libcgroup-0.41-19.el8.x86_64 7/13
验证 : containerd.io-1.6.9-3.1.el8.x86_64 8/13
验证 : docker-ce-3:20.10.21-3.el8.x86_64 9/13
验证 : docker-ce-cli-1:20.10.21-3.el8.x86_64 10/13
验证 : docker-ce-rootless-extras-20.10.21-3.el8.x86_64 11/13
验证 : docker-compose-plugin-2.12.2-3.el8.x86_64 12/13
验证 : docker-scan-plugin-0.21.0-3.el8.x86_64 13/13
已安装:
container-selinux-2:2.189.0-1.module_el8.7.0+1216+b022c01d.noarch containerd.io-1.6.9-3.1.el8.x86_64
docker-ce-3:20.10.21-3.el8.x86_64 docker-ce-cli-1:20.10.21-3.el8.x86_64
docker-ce-rootless-extras-20.10.21-3.el8.x86_64 docker-compose-plugin-2.12.2-3.el8.x86_64
docker-scan-plugin-0.21.0-3.el8.x86_64 fuse-overlayfs-1.9-1.module_el8.7.0+1216+b022c01d.x86_64
fuse3-3.3.0-16.el8.x86_64 fuse3-libs-3.3.0-16.el8.x86_64
libcgroup-0.41-19.el8.x86_64 libslirp-4.4.0-1.module_el8.7.0+1216+b022c01d.x86_64
slirp4netns-1.2.0-2.module_el8.7.0+1216+b022c01d.x86_64
完毕!
[root@localhost ~]#
其中,
docker-ce:是Docker Engine的Community版本,如果是docker-ee,则是企业版本。免费至上的话,我们还是应该选择ce版本。
docker-ce-cli:docker引擎的命令行界面(Command Line Interface)的意思。
containerd.io:对应守护进程containerd,用于与操作系统API进行交互的接口,实质上是用于将容器与操作系统解耦的封装层。
docker-compose-plugin:不知道是啥,应该和docker-compose相关,docker-compose是一个用于部署的管理多容器应用的工具,通过一个脚本将构筑一个应用的多个服务协同部署。总之说要装,那就装上好了。
4.打开docker服务
[root@localhost ~]# systemctl start docker.service
[root@localhost ~]# systemctl status docker.service
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since Thu 2022-11-03 07:28:55 EDT; 9s ago
Docs: https://docs.docker.com
Main PID: 46305 (dockerd)
Tasks: 10
Memory: 107.2M
CGroup: /system.slice/docker.service
└─46305 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
11月 03 07:28:52 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:52.958188751-04:00" level=warning msg="Your kernel do>
11月 03 07:28:52 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:52.958239929-04:00" level=warning msg="Your kernel do>
11月 03 07:28:52 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:52.958378738-04:00" level=info msg="Loading container>
11月 03 07:28:55 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:55.004506327-04:00" level=info msg="Default bridge (d>
11月 03 07:28:55 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:55.154919529-04:00" level=info msg="Firewalld: interf>
11月 03 07:28:55 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:55.357115017-04:00" level=info msg="Loading container>
11月 03 07:28:55 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:55.493938906-04:00" level=info msg="Docker daemon" co>
11月 03 07:28:55 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:55.494372649-04:00" level=info msg="Daemon has comple>
11月 03 07:28:55 localhost.localdomain systemd[1]: Started Docker Application Container Engine.
11月 03 07:28:55 localhost.localdomain dockerd[46305]: time="2022-11-03T07:28:55.514570711-04:00" level=info msg="API listen on /va>
[root@localhost ~]#
docker服务需要在组件安装完成后手动打开。打开后使用hello-world镜像测试一下,如下说明docker工作非常正常:
[root@localhost ~]# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:e18f0a777aefabe047a671ab3ec3eed05414477c951ab1a6f352a06974245fe7
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
[root@localhost ~]#
5.安装docker-compose
前面提到,如果需要部署多服务应用,则需要安装docker-compose。这个需要专门下载:
[root@localhost ~]# curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 23.5M 100 23.5M 0 0 784k 0 0:00:30 0:00:30 --:--:-- 705k
[root@localhost ~]#
添加执行权限,以及建立快捷方式(软连接)
[root@localhost ~]# chmod +x /usr/local/bin/docker-compose
[root@localhost ~]# ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
测试安装情况。嗯,这还是个绿色软件……
[root@localhost ~]# docker-compose version
Docker Compose version v2.2.2
二、Windows上的Docker安装
在Windows上安装Docker实在是没法说啥。和Linux上面安装个啥东西装着装着就莫名奇妙失败了不同,在Windows上装个啥属于装着装着莫名其妙就成功了,反正能用就不知道为什么能用的样子。所以当习惯在Linux下面折腾后再回到Windows下面安装的时候,整个就给我整不会了。过程记录不完整,很多都是后来补的,不代表真实流程,凑合着看吧。
1.Docker Desktop安装
到docker官方下载Windows版本的安装程序Docker Desktop,会打包安装使用Docker必须的引擎什么的组件。
早期的时候,由于需要使用hyper-v,docker是木有办法和Vmware共机安装的。好在这个问题现在已经解决了。只需要在Vmware安装的时候选择自动安装WHP(如下图),在Docker安装的时候选择WSL2(如下下图)即可。
这样,安装程序会帮助我们把一切都配置好的。当然,如果Vmware仍不可用,那可能需要删掉重装一次。还有就是原先挂起的虚拟机映像,需要放弃挂起状态,“关机”重来一次就可以加载了。
可以看到,Docker和Vmware都安装完毕后,Hyper-V其实是没有打开的。真正打开的是虚拟机平台、始于Linux的Windows子系统2个组件。
2.子系统环境配置
在Windows的Docker中,直接加载Windows的容器,是默认在Windows的子环境中的;但是如果要加载Linux的容器,则需要切换到Linux子系统——这个需要安装。
前面我们说过,Windows下,总是莫名奇妙能够安装成功。当我发现wsl安装成功,但是没有子系统的时候,习惯性的按照Linux的命令习惯,使用wsl的命令行去折腾,忘记了Window下其实有好得多的方法……。
于是我是这么做的:使用wsl的--help查看了一下帮助,发现这么一个示例命令,然后就执行了一下(当然机器得在线上),然后就安装了ubuntu的子系统,就且过去了…… 事后回想了半天当时是咋就装成功了的-_-!!
但这么装会有几个问题。一是wsl没有升级到wsl 2,据说这样子系统的运行性能会慢很多;二是在Docker的配置菜单中,会发现子系统其实并没有集成到docker里面来——但是docker确实能够正常运转……如下图:
正确的安装方式,按照官网的描述,是这样的:
从这个下载链接下载到wsl的更新安装包(官方名称是Linux内核更新包)进行安装,
https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi
当然我没有这么装,因为我已经装好一个子系统了……再装会报错。所以我继续是这么装的……
然后不管正确还是“错误”的安装方法,都需要设置wsl2为默认的:
这样,在docker的配置页面里面就能看到如下的选择项了,都打开就是:
然而官方是需要继续在Windows商店里下载子系统(Linux分发)进行安装的。
然而我没有……我也装成功了……感觉反而更简单些……
三、Docker的基本操作
如果仅仅是学习测试,或者整个快捷的开发环境,Docker最基本的操作大概只和镜像、容器相关。
1.镜像 Image
Docker镜像可以直接从网站上拉取,也可以在已有镜像上继续构造,或者干脆直接从真实系统构造基础镜像。从虚拟机角度看,可以把镜像看作是一个没有装载运行的虚拟机文件,装在运行后就叫做容器了。这里从最简单直接的开始——如何使用已有的镜像启动一个容器(对比用别人的虚拟机存档创建一个虚拟机)。
(1)Pull 镜像
Pull镜像有2种做法:一种在命令行方式下单纯使用命令来拉取;一种是结合网页和交互式界面。
命令行方式:
首先使用docker查询打算安装的镜像
PS D:\> docker search centos
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
centos DEPRECATED; The official build of CentOS. 7395 [OK]
kasmweb/centos-7-desktop CentOS 7 desktop for Kasm Workspaces 25
couchbase/centos7-systemd centos7-systemd images with additional debug… 5 [OK]
dokken/centos-7 CentOS 7 image for kitchen-dokken 4
dokken/centos-stream-8 3
continuumio/centos5_gcc5_base 3
dokken/centos-8 CentOS 8 image for kitchen-dokken 2
dokken/centos-stream-9 2
spack/centos6 CentOS 6 with Spack preinstalled 1
spack/centos7 CentOS 7 with Spack preinstalled 1
datadog/centos-i386 0
ustclug/centos Official CentOS Image with USTC Mirror 0
dokken/centos-6 CentOS 6 image for kitchen-dokken 0
bitnami/centos-extras-base 0
corpusops/centos-bare https://github.com/corpusops/docker-images/ 0
couchbase/centos-72-java-sdk 0
corpusops/centos centos corpusops baseimage 0
couchbase/centos-72-jenkins-core 0
couchbase/centos-70-sdk-build 0
couchbase/centos-69-sdk-build 0
couchbase/centos-69-sdk-nodevtoolset-build 0
bitnami/centos-base-buildpack Centos base compilation image 0 [OK]
fnndsc/centos-python3 Source for a slim Centos-based Python3 image… 0 [OK]
spack/centos-stream 0
dokken/centos-5 EOL DISTRO: For use with kitchen-dokken, Bas… 0
PS D:\>
然后按照名字拉取:
PS D:\> docker pull centos
Using default tag: latest
latest: Pulling from library/centos
a1d0c7532777: Pull complete
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest
需要一点时间,稍等即可。然后可以使用images命令查看是否拉取成功:
PS D:\> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 5d0da3dc9764 14 months ago 231MB
这样就可以了。
当然上面是在windows cmd里面,在centos里的命令式完全一样。
使用网页和交互式界面方式:
在window下,也可以不完全使用命令行这种方式,一定程度上获取信息会更容易一些。
首先实在hub.docker.com网站上查询需要安装的镜像。
比如在搜索框中搜索ubuntu。每个镜像都会有下载数统计,一般来说下载得越多越好用,通常这种都会是官方的。点进去。
右上角这个就是拉取的命令,直接点击复制,在cmd中粘贴运行就可以:
PS D:\> docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
e96e057aae67: Pull complete
Digest: sha256:4b1d0c4a2d2aaf63b37111f34eb9fa89fa1bf53dd6e4ca954d47caebca4005c2
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
在docker desktop里面查看,我们拉取的2个images都可以看到:
如果想安装特定版本的Ubuntu,而不是tag中那个“latest”,在命令行下,是需要明确指定版本号,在网站中,这个命令也可以直接复制过来——只需要点击tags标签,就可以看到所有可用的版本号:
每个版本的右上角,都有对应的命令格式可以复制使用:
2.容器 Container
把Image运行起来,就是Container。同样我们可以使用命令行和窗口模式。
命令行模式:
使用docker run [-it or -d] image_name [cmd]命令可以将镜像运转起来。其中:
使用-it参数会返回交互式命令行,使用-d参数会直接以daemon方式后台运行。
image_name就是使用images指令查看到的REPOSITORY:TAG字段,似乎用IMAGE ID也可以,不过我还是习惯用名字。
cmd是容器载入后默认执行的命令,一般是/bin/bash,如果不行就试试/bin/sh。当然如果是部署了运用,或者想启动服务(/sbin/init),则需要做对应的配置。
PS D:\> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest a8780b506fa4 2 weeks ago 77.8MB
centos latest 5d0da3dc9764 14 months ago 231MB
PS D:\> docker run -it centos:latest /bin/bash
[root@7b68ae2a82ed /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@7b68ae2a82ed /]# pwd
/
[root@7b68ae2a82ed /]#
看到熟悉的Linux的命令,可以证明容器已经加载起来了。
[root@7b68ae2a82ed /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@7b68ae2a82ed /]# pwd
/
[root@7b68ae2a82ed /]# exit
exit
PS D:\>
使用exit,则可以退出当前容器。
使用docker ps命令,或者使用docker container ls命令,可以观察当前已经加载的容器。但是默认情况下,只显示正在运行的容器;exit指令退出后挂起的容器,则需要添加-a选项才能看到。
PS D:\> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
PS D:\> docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
PS D:\> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b68ae2a82ed centos:latest "/bin/bash" 20 minutes ago Exited (0) 4 minutes ago reverent_maxwell
PS D:\> docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b68ae2a82ed centos:latest "/bin/bash" 20 minutes ago Exited (0) 4 minutes ago reverent_maxwell
PS D:\>
再次运行挂起的容器,不能使用run命令,这样会又启动一个新的容器;而是应该使用attach或者exec指令,推荐使用exec指令,这样命令格式和run基本相似。在此之前,需要start挂起的容器。
PS D:\> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b68ae2a82ed centos:latest "/bin/bash" 24 minutes ago Exited (0) 7 minutes ago reverent_maxwell
PS D:\> docker start reverent_maxwell
reverent_maxwell
PS D:\> docker exec -it reverent_maxwell bash
[root@7b68ae2a82ed /]#
事实上,run就是create+start+exec的合体而已。
窗口模式:
窗口模式很简单,在镜像标签内,点击开始就能启动新的容器
在容器标签内,各种操作也可以直接点选,包括进入交互式命令行界面。
三、构建docker镜像
要用容器构建一个生产环境,那就还有好多内容需要说,但这里就是个入门的话题,所以不深究了——只对如我一般的小白们直觉上会问的第一问题继续尝试——怎么自己构建一个镜像?以及能不能像虚拟机那样完全自己从零安装一个系统,而不是拉取网上别人做好的?
1. 基于Dockfile构建docker基础映像
Docker提供了一种标准的Dockerfile方式供我们构造镜像。Dockfile使用FROM确定初始镜像,使用ADD向初始镜像释放tar形式(或其他支持的压缩形式)的文件系统,使用COPY向初始镜像拷贝文件,使用WORKDIR指定默认工作目录,使用RUN指定在构建镜像时、初始镜像加载成功后需要进行的操作,使用CMD指定在镜像加载后默认执行的命令。具体更详细的指令集网络上已经很多介绍了,这里不赘述。
(1)编写Dockerfile,从scratch开始
如果从零构建一个Dockerfile,则需要在FROM中指定基础镜像为scratch。这是一个虚拟的镜像,不用(无法)从docker中拉取。实际上,这个镜像是一个基础到不能再基础的“运行时”,仅仅包含了系统的核心运行环境,我们可以写一个程序在其上运转,但只能调用基本的系统API,甚至连一些典型动态库都没有(为此掉了一大坑,这个后面还要折腾)。在Windows上,这等同于系统起来后只加载了API三大模块kernal32,user32和gdi32。从linux的角度来说,就是仅仅加载了内核,连shell都没有(使用bash,sh都不行,也无法进入命令行交互,我猜测是没有shell的,需要自己加)。
所以,从scatch开始,如果不是ADD一个shell工具集(比如busybox一类),那就只能是自己部署一个应用程序(比如pighello),然后执行它。事实上,执行完成以后,docker也会自动的exit。
最简单的Dockerfile文件如下所示,从scratch开始,拷贝(对于非压缩文件,ADD的作用和COPY差不多)应用到根目录下,然后启动时默认从根目录执行该文件。
FROM scratch
ADD pighello /
CMD ["/pighello"]
最好单独建立一个文件夹,将Dockerfile和pighello应用程序放在里面再进行镜像构建。因为build命令会把Dockerfile所在目录的整个文件系统打包发送给docker daemon,如果该目录中没有要放进Docker镜像中的文件,就会付出相当无谓的时间成本。
pighello我们使用c++编写,功能很简单:
#include <iostream>
using namespace std;
int main()
{
cout<<"test docker and print hello"<<endl;
}
编译并执行,如果gcc或者g++命令无法找到,那是没有安装C/C++编译环境。之前的文章有介绍,这里不赘述。将执行文件放入Dockerfile所在目录中,准备构建镜像。
[root@localhost package]# ./pighello
Hello and test Docker Image
[root@localhost package]# pwd
/root/package
[root@localhost package]# ls
Dockerfile pighello
[root@localhost package]#
准备妥当后,使用docker build指令构建镜像,使用-t指令指示镜像名称和tag,注意后面还有一个“.”,表示对当前目录构建(dockerfile和构建素材都在这个我们专门建立的文件夹中):
[root@localhost package]# docker build -t pig:1 .
Sending build context to Docker daemon 20.99kB
Step 1/3 : FROM scratch
--->
Step 2/3 : COPY pighello /
---> 510e6b146d3f
Step 3/3 : CMD ["/pighello"]
---> Running in 2d8981d781be
Removing intermediate container 2d8981d781be
---> 67d63421a207
Successfully built 67d63421a207
Successfully tagged pig:1
使用images命令查看,可以看到镜像构建成功了。确实很小,只有18.1KB。但是比官方的hello-world还是大了那么一丢丢……
[root@localhost package]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pig 1 67d63421a207 38 seconds ago 18.1kB
ubuntu latest a8780b506fa4 2 weeks ago 77.8MB
兴奋吧?但是别着急运行,因为运行会报错:),出于文章结构关系,我们这里先不表,跳过去。
(2)编写Dockerfile,从centos开始
为了方便理解后面的排坑指南,也为了更好的理解scratch镜像,这里从官方的ubuntu开始再构建一个不很基础的镜像。
Dockerfile如下:
FROM ubuntu
ADD pighello /
CMD ["/pighello"]
目录如下:
[root@localhost ~]# mkdir package2
[root@localhost ~]# cd package2
[root@localhost package2]# cp ../package/pighello .
[root@localhost package2]# ls
Dockerfile pighello
不同在于将scratch换做了Ubuntu,构建镜像:
[root@localhost package2]# docker build -t pig:2 .
Sending build context to Docker daemon 20.99kB
Step 1/3 : FROM ubuntu
---> a8780b506fa4
Step 2/3 : ADD pighello /
---> 7f41574af5ac
Step 3/3 : CMD ["/pighello"]
---> Running in 2f22f0d94c1b
Removing intermediate container 2f22f0d94c1b
---> e3673208057e
Successfully built e3673208057e
Successfully tagged pig:2
[root@localhost package2]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pig 2 e3673208057e 5 seconds ago 77.8MB
pig 1 67d63421a207 16 minutes ago 18.1kB
ubuntu latest a8780b506fa4 2 weeks ago 77.8MB
[root@localhost package2]#
这个镜像明显要比scratch大很多,但是比官方镜像就没啥差异了。运行看看:
[root@localhost package2]# docker run pig:2
Hello and test Docker Image
再交互式进去看看目录是否如我们所想,注意是利用pig:2镜像新启动一个容器。原来那个容器因为设置了CMD是pighello,所以使用start的话,会再次执行pighello退出。
PS:CMD和ENTRYPOINT的作用大致差不多。区别在于,如果使用ENTRYPOINT设置了默认执行的指令的话,这个run -it /bin/bash的方式也无法替换……。相当于写死了,当然也可以使用docker run --entrypoint /bin/bash pig:2的方式强制替换。
[root@localhost package2]# docker run -it pig:2 /bin/bash
root@36161e59fcdb:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt pighello proc root run sbin srv sys tmp usr var
root@36161e59fcdb:/#
可以看到pighello确实是被拷贝到了根目录下的。
2. 使用容器构建镜像
事实上,从一个可用的官方基础映像开始(并非scratch,那个空到没法操作),我们可以如构建虚拟机快照一般,通过装载映像并使用容器,在容器中手工添加组件(文件)的方式,构造我们自己的镜像。
(1)相关docker命令
我们把container的命令帮助放到前面,这里有一个export命令,可以将容器当前的文件系统(这个理解非常重要,我理解容器就是通用的内核,挂载定制的文件系统,运行起来就像一个装了无数软件的windows系统,确实从安全模式启动的那种感觉,如果向完整的用起来,需要大量的手工配置工作)打包成tar文件导出。
Image的命令帮助放在后面,可以看到,对应export的import实际在Image里面。这里镜像和容器看起来倒是没什么区别——因为容器的文件系统被静态地快照了出来,其实就是镜像。把它挂上一个内核原型起来,就是容器了。
至于镜像的save和load指令,完全用于导入导出操作静态的镜像本身。我们可以用它来从镜像库中导出文件系统为tar文件,然后拿到别的主机上去用。当然,只要是导出的文件系统,包括export的那个,我想应该都是可以拷走用的。感兴趣可以试试,我没试。
(2)从官方镜像定制
我们使用docker启动一个官方的ubuntu镜像,为了方便把pighello拷进去,需要使用docker run -v选项把主机目录挂载进去——不然这个极小的ubuntu系统,连网络都不同,相当麻烦。
使用ubuntu官方映像启动容器,并且通过挂载方式,将包含pighello的目录mount到容器中,进一步将pighello手工拷贝到根目录下:
[root@localhost package2]# docker run -it -v /root/package:/trans ubuntu bash
root@7b4e71a938c8:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp trans usr var
root@7b4e71a938c8:/# cd trans
root@7b4e71a938c8:/trans# ls
Dockerfile pighello
root@7b4e71a938c8:/trans# cp pighello /
root@7b4e71a938c8:/trans# cd ..
root@7b4e71a938c8:/# ls
bin dev home lib32 libx32 mnt pighello root sbin sys trans var
boot etc lib lib64 media opt proc run srv tmp usr
root@7b4e71a938c8:/#
退出容器以后再进来看看,可以看到当挂载的目录卸载后,pighello在根下,和之前我们通过Dokcerfile构建的镜像是一样的。
[root@localhost package2]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b4e71a938c8 ubuntu "bash" 2 minutes ago Exited (0) 5 seconds ago jovial_bohr
[root@localhost package2]# docker start jovial_bohr
jovial_bohr
[root@localhost package2]# docker exec -it jovial_bohr bash
root@7b4e71a938c8:/# ls
bin dev home lib32 libx32 mnt pighello root sbin sys trans var
boot etc lib lib64 media opt proc run srv tmp usr
root@7b4e71a938c8:/#
(3)导出容器快照,并导入镜像库
退出我们定制的这个容器。然后使用export,import命令来把它转到快照库中:
[root@localhost package2]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b4e71a938c8 ubuntu "bash" 6 minutes ago Exited (137) 4 seconds ago jovial_bohr
[root@localhost package2]#
[root@localhost package2]#
[root@localhost package2]# docker container export -o pigtar.tar jovial_bohr
[root@localhost package2]# ls
Dockerfile pighello pigtar.tar
[root@localhost package2]#
[root@localhost package2]#
[root@localhost package2]# docker image import -c 'CMD ["/pighello"]' pigtar.tar pig:3
sha256:0b8ed61b4116a46dfd192d6a01d0504a0761d1b7f71f0af35bd6fb2a3939b3de
[root@localhost package2]#
[root@localhost package2]#
[root@localhost package2]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pig 3 0b8ed61b4116 18 minutes ago 77.8MB
ubuntu latest a8780b506fa4 2 weeks ago 77.8MB
centos latest 5d0da3dc9764 14 months ago 231MB
使用export指令,指定容器名和tar包的名称,将容器快照导出压缩;再使用import命令,指定tar文件和导入后的镜像名称:tag,将快照导入为镜像,存放到镜像库中。这里要注意的是,需要使用-c选项,指定Dockerfile配置命令来设置镜像的启动默认执行命令,否则镜像还是会去执行/bin/bash,就达不到我们的目的了。
使用inpect命令查看,可以看到CMD确实配属进去了。执行,确实如我们所设想的那样:
[root@localhost package2]# docker image inspect pig:3
[
{
"Id": "sha256:0b8ed61b4116a46dfd192d6a01d0504a0761d1b7f71f0af35bd6fb2a3939b3de",
"RepoTags": [
"pig:3"
],
"RepoDigests": [],
"Parent": "",
"Comment": "Imported from -",
"Created": "2022-11-18T03:01:32.296228074Z",
"Container": "",
"ContainerConfig": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": null,
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"DockerVersion": "20.10.21",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": [
"/pighello"
],
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 77810623,
"VirtualSize": 77810623,
"GraphDriver": {
"Data": {
"MergedDir": "/var/lib/docker/overlay2/d76d5fb44f5fcd970e3bc37f7eb3284c390f64a0545de337a7e7218eaf5d8d1b/merged",
"UpperDir": "/var/lib/docker/overlay2/d76d5fb44f5fcd970e3bc37f7eb3284c390f64a0545de337a7e7218eaf5d8d1b/diff",
"WorkDir": "/var/lib/docker/overlay2/d76d5fb44f5fcd970e3bc37f7eb3284c390f64a0545de337a7e7218eaf5d8d1b/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:6b261aec0aca8e4afe592db5cd0e0cd08801e07e3ea593a77d4db113c9adb22d"
]
},
"Metadata": {
"LastTagTime": "2022-11-17T22:01:32.300887225-05:00"
}
}
]
[root@localhost package2]#
[root@localhost package2]#
[root@localhost package2]#
[root@localhost package2]# docker run pig:3
Hello and test Docker Image
[root@localhost package2]#
3.docker run:exec /app: no such file or directory 深坑填埋指南
[root@localhost package]# docker run pig:1
exec /pighello: no such file or directory
[root@localhost package]#
现在可以来填上面挖好的深坑了。基于scratch构建的基础映像,如果直接拿来运行,会出现找不到文件或目录的错误——而且看起来就像是pighello找不到一样。当然现在我们知道这是不可能的,pighello应该好好的拷贝到了根目录下(只是我是先掉进坑里,然后采用ubuntu官方镜像来验证的)
(1)根子还在于对scratch的理解
既然pighello好好的,那到底是什么文件找不到呢?docker官方文档上一个不起眼的字眼暗示了这个问题:
也就是说,事实上scratch是绝对不会提供C/C++的运行时的;我们提供给scratch的程序,必须使用静态编译的方式,把一家老少都打包带着,才能在容器中真正地跑起来;之所以前面在官方的centos、ubuntu中都能跑,是因为那些静态库、动态库文件已经打包到文件系统里面了。
然而,如果直接使用-static进行编译,在Centos中会出现“/usr/bin/ld:找不到-lc的”错误:
[root@localhost ~]# gcc pighello.c -o pighello -static
/usr/bin/ld: 找不到 -lc
collect2: 错误:ld 返回 1
[root@localhost ~]#
如果是c++,则会遇到的错误更多……:
[root@localhost ~]# g++ pighello.cpp -o pighello -static
/usr/bin/ld: 找不到 -lstdc++
/usr/bin/ld: 找不到 -lm
/usr/bin/ld: 找不到 -lc
collect2: 错误:ld 返回 1
[root@localhost ~]#
究其原因,据说是新版的Linux系统不在使用诸如libc.a静态库,而是直接使用libc.so动态库的形式,所以静态链接的时候,无法找到libc.a之类的文件——也就是说,其实找不到的不是pighello,是C/C++运行时的静态链接库。
如果是ubuntu,官方倒是给出了显见的方法:
更新一下apt-get,然后装上build-essential就可以。我没试,但是从centos那边试通的情况看,应该没问题。只是Centos下,解决这个问题网上可真是众说纷纭,导致我很长时间都没有get到正确的方法。所谓深坑也……
(2)Centos的glibc/libstdc++安装
要实现静态编译c/c++程序,在Centos上需要追加安装glibc和libstdc++这2个静态库。glibc的英文全称为The GNU C Library。glibc是GNU发布的libc库,即c运行库。
不要问我怎么知道的,我只能哭着说我蒙的,蒙了好久……。只是从网上一个非常片段的线索中知道,要解决这个问题,可以yum安装一个glibc-static的库。然而,使用yum install glibc-static 是无法安装的。且对于c++,更是连这样的线索都没有。
更多的解决办法,诸如不知从哪里拷贝一个glibc.a,放到/usr/lib64下面,或者建立软连接,或者拷不到的话就去git clone下glibc的源代码,glibc/configure --prefix=安装目录什么的,make然后make list一下等等。我都试过,过程异常复杂,可以说能够解决一个两个问题,当然会因为运行时比较复杂的原因,带来更多的问题。这里就不贴了。
这里我只说说我是这么蒙的……现有的系统里静态库没有,动态库还是装了的,也许静态库就是动态库的安装包加个“-static”。嗯,如果是这样,我们只需要知道动态库的名字就好了:
[root@localhost ~]# rpm -qa|grep 'glibc'
glibc-langpack-en-2.28-197.el8.x86_64
glibc-gconv-extra-2.28-197.el8.x86_64
glibc-common-2.28-197.el8.x86_64
glibc-langpack-zh-2.28-197.el8.x86_64
glibc-headers-2.28-197.el8.x86_64
glibc-2.28-197.el8.x86_64
glibc-all-langpacks-2.28-197.el8.x86_64
glibc-devel-2.28-197.el8.x86_64
[root@localhost ~]# rpm -qa|grep 'c++'
libsigc++20-2.10.0-6.el8.x86_64
libstdc++-8.5.0-17.el8.x86_64
libstdc++-devel-8.5.0-17.el8.x86_64
gcc-c++-8.5.0-17.el8.x86_64
[root@localhost ~]#
需要到centos的库里去搜索:
当然我需要在CentOS stream 8中去搜索。
找到的2个库是这样的:
glibc-static:
找到对应的版本点进去,也介绍了安装的命令:
libstdc++:
同理c++版本在这里:
可见这两个包都在Powertools库里,使用dnf指定参数就可以安装。当然我们也可以使用yum:
[root@localhost ~]# yum install --enablerepo powertools glibc-static -y
CentOS Stream 8 - PowerTools 76 kB/s | 5.1 MB 01:08
上次元数据过期检查:0:00:24 前,执行于 2022年11月17日 星期四 23时44分48秒。
依赖关系解决。
====================================================================================================================================
软件包 架构 版本 仓库 大小
====================================================================================================================================
安装:
glibc-static x86_64 2.28-216.el8 powertools 2.1 M
升级:
glibc x86_64 2.28-216.el8 baseos 2.2 M
glibc-all-langpacks x86_64 2.28-216.el8 baseos 26 M
glibc-common x86_64 2.28-216.el8 baseos 1.0 M
glibc-devel x86_64 2.28-216.el8 baseos 82 k
glibc-gconv-extra x86_64 2.28-216.el8 baseos 1.5 M
glibc-headers x86_64 2.28-216.el8 baseos 487 k
glibc-langpack-en x86_64 2.28-216.el8 baseos 825 k
glibc-langpack-zh x86_64 2.28-216.el8 baseos 2.2 M
安装依赖关系:
libxcrypt-static x86_64 4.1.1-6.el8 powertools 56 k
事务概要
====================================================================================================================================
安装 2 软件包
升级 8 软件包
总下载:36 M
下载软件包:
(1/10): glibc-2.28-216.el8.x86_64.rpm 2.0 MB/s | 2.2 MB 00:01
(2/10): glibc-all-langpacks-2.28-216.el8.x86_64.rpm 12 MB/s | 26 MB 00:02
(3/10): glibc-common-2.28-216.el8.x86_64.rpm 6.8 MB/s | 1.0 MB 00:00
(4/10): glibc-devel-2.28-216.el8.x86_64.rpm 614 kB/s | 82 kB 00:00
(5/10): glibc-gconv-extra-2.28-216.el8.x86_64.rpm 8.5 MB/s | 1.5 MB 00:00
(6/10): glibc-headers-2.28-216.el8.x86_64.rpm 3.2 MB/s | 487 kB 00:00
(7/10): libxcrypt-static-4.1.1-6.el8.x86_64.rpm 13 kB/s | 56 kB 00:04
(8/10): glibc-langpack-en-2.28-216.el8.x86_64.rpm 1.5 MB/s | 825 kB 00:00
(9/10): glibc-langpack-zh-2.28-216.el8.x86_64.rpm 1.1 MB/s | 2.2 MB 00:01
[MIRROR] glibc-static-2.28-216.el8.x86_64.rpm: Curl error (18): Transferred a partial file for http://mirrors.njupt.edu.cn/centos/8-stream/PowerTools/x86_64/os/Packages/glibc-static-2.28-216.el8.x86_64.rpm [transfer closed with 1967786 bytes remaining to read]
(10/10): glibc-static-2.28-216.el8.x86_64.rpm 61 kB/s | 2.1 MB 00:34
------------------------------------------------------------------------------------------------------------------------------------
总计 1.0 MB/s | 36 MB 00:36
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
准备中 : 1/1
升级 : glibc-all-langpacks-2.28-216.el8.x86_64 1/18
升级 : glibc-common-2.28-216.el8.x86_64 2/18
升级 : glibc-gconv-extra-2.28-216.el8.x86_64 3/18
运行脚本: glibc-gconv-extra-2.28-216.el8.x86_64 3/18
升级 : glibc-langpack-en-2.28-216.el8.x86_64 4/18
升级 : glibc-langpack-zh-2.28-216.el8.x86_64 5/18
运行脚本: glibc-2.28-216.el8.x86_64 6/18
升级 : glibc-2.28-216.el8.x86_64 6/18
运行脚本: glibc-2.28-216.el8.x86_64 6/18
运行脚本: glibc-headers-2.28-216.el8.x86_64 7/18
升级 : glibc-headers-2.28-216.el8.x86_64 7/18
升级 : glibc-devel-2.28-216.el8.x86_64 8/18
运行脚本: glibc-devel-2.28-216.el8.x86_64 8/18
安装 : libxcrypt-static-4.1.1-6.el8.x86_64 9/18
安装 : glibc-static-2.28-216.el8.x86_64 10/18
运行脚本: glibc-devel-2.28-197.el8.x86_64 11/18
清理 : glibc-devel-2.28-197.el8.x86_64 11/18
清理 : glibc-headers-2.28-197.el8.x86_64 12/18
清理 : glibc-gconv-extra-2.28-197.el8.x86_64 13/18
运行脚本: glibc-gconv-extra-2.28-197.el8.x86_64 13/18
清理 : glibc-all-langpacks-2.28-197.el8.x86_64 14/18
运行脚本: glibc-all-langpacks-2.28-197.el8.x86_64 14/18
清理 : glibc-langpack-en-2.28-197.el8.x86_64 15/18
清理 : glibc-common-2.28-197.el8.x86_64 16/18
清理 : glibc-langpack-zh-2.28-197.el8.x86_64 17/18
清理 : glibc-2.28-197.el8.x86_64 18/18
运行脚本: glibc-all-langpacks-2.28-216.el8.x86_64 18/18
运行脚本: glibc-2.28-197.el8.x86_64 18/18
运行脚本: glibc-common-2.28-216.el8.x86_64 18/18
验证 : glibc-static-2.28-216.el8.x86_64 1/18
验证 : libxcrypt-static-4.1.1-6.el8.x86_64 2/18
验证 : glibc-2.28-216.el8.x86_64 3/18
验证 : glibc-2.28-197.el8.x86_64 4/18
验证 : glibc-all-langpacks-2.28-216.el8.x86_64 5/18
验证 : glibc-all-langpacks-2.28-197.el8.x86_64 6/18
验证 : glibc-common-2.28-216.el8.x86_64 7/18
验证 : glibc-common-2.28-197.el8.x86_64 8/18
验证 : glibc-devel-2.28-216.el8.x86_64 9/18
验证 : glibc-devel-2.28-197.el8.x86_64 10/18
验证 : glibc-gconv-extra-2.28-216.el8.x86_64 11/18
验证 : glibc-gconv-extra-2.28-197.el8.x86_64 12/18
验证 : glibc-headers-2.28-216.el8.x86_64 13/18
验证 : glibc-headers-2.28-197.el8.x86_64 14/18
验证 : glibc-langpack-en-2.28-216.el8.x86_64 15/18
验证 : glibc-langpack-en-2.28-197.el8.x86_64 16/18
验证 : glibc-langpack-zh-2.28-216.el8.x86_64 17/18
验证 : glibc-langpack-zh-2.28-197.el8.x86_64 18/18
已升级:
glibc-2.28-216.el8.x86_64 glibc-all-langpacks-2.28-216.el8.x86_64 glibc-common-2.28-216.el8.x86_64
glibc-devel-2.28-216.el8.x86_64 glibc-gconv-extra-2.28-216.el8.x86_64 glibc-headers-2.28-216.el8.x86_64
glibc-langpack-en-2.28-216.el8.x86_64 glibc-langpack-zh-2.28-216.el8.x86_64
已安装:
glibc-static-2.28-216.el8.x86_64 libxcrypt-static-4.1.1-6.el8.x86_64
完毕!
[root@bogon pro]# dnf --enablerepo=powertools install libstdc++-static
上次元数据过期检查:0:16:26 前,执行于 2022年11月17日 星期四 08时28分45秒。
依赖关系解决。
==========================================================================================================================================================================================
软件包 架构 版本 仓库 大小
==========================================================================================================================================================================================
安装:
libstdc++-static x86_64 8.5.0-17.el8 powertools 600 k
事务概要
==========================================================================================================================================================================================
安装 1 软件包
总下载:600 k
安装大小:5.1 M
确定吗?[y/N]: y
下载软件包:
libstdc++-static-8.5.0-17.el8.x86_64.rpm 2.8 MB/s | 600 kB 00:00
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
总计 1.0 MB/s | 600 kB 00:00
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
准备中 : 1/1
安装 : libstdc++-static-8.5.0-17.el8.x86_64 1/1
运行脚本: libstdc++-static-8.5.0-17.el8.x86_64 1/1
验证 : libstdc++-static-8.5.0-17.el8.x86_64 1/1
已安装:
libstdc++-static-8.5.0-17.el8.x86_64
完毕!
[root@bogon pro]# g++ test.cpp -o a -static
[root@bogon pro]#
[root@localhost ~]# yum install --enablerepo powertools libstdc++-static -y
上次元数据过期检查:0:17:45 前,执行于 2022年11月17日 星期四 23时44分48秒。
依赖关系解决。
====================================================================================================================================
软件包 架构 版本 仓库 大小
====================================================================================================================================
安装:
libstdc++-static x86_64 8.5.0-17.el8 powertools 600 k
事务概要
====================================================================================================================================
安装 1 软件包
总下载:600 k
安装大小:5.1 M
下载软件包:
[MIRROR] libstdc++-static-8.5.0-17.el8.x86_64.rpm: Curl error (18): Transferred a partial file for http://mirrors.njupt.edu.cn/centos/8-stream/PowerTools/x86_64/os/Packages/libstdc%2b%2b-static-8.5.0-17.el8.x86_64.rpm [transfer closed with 387698 bytes remaining to read]
libstdc++-static-8.5.0-17.el8.x86_64.rpm 19 kB/s | 600 kB 00:31
------------------------------------------------------------------------------------------------------------------------------------
总计 19 kB/s | 600 kB 00:31
运行事务检查
事务检查成功。
运行事务测试
事务测试成功。
运行事务
准备中 : 1/1
安装 : libstdc++-static-8.5.0-17.el8.x86_64 1/1
运行脚本: libstdc++-static-8.5.0-17.el8.x86_64 1/1
验证 : libstdc++-static-8.5.0-17.el8.x86_64 1/1
已安装:
libstdc++-static-8.5.0-17.el8.x86_64
完毕!
[root@localhost ~]#
然后再静态编译试试:
[root@localhost ~]# gcc pighello.c -o pighello -static
[root@localhost ~]# g++ pighello.cpp -o pighello -static
[root@localhost ~]#
重新构建镜像并执行:
[root@localhost ~]# mv pighello ./package/
[root@localhost ~]# cd package/
[root@localhost package]# ls
Dockerfile pighello
[root@localhost package]# docker build -t pig:1 .
Sending build context to Docker daemon 3.428MB
Step 1/3 : FROM scratch
--->
Step 2/3 : COPY pighello /
---> a291200e3be6
Step 3/3 : CMD ["/pighello"]
---> Running in 7ac260b798a3
Removing intermediate container 7ac260b798a3
---> ee3adaaf3c7a
Successfully built ee3adaaf3c7a
Successfully tagged pig:1
[root@localhost package]# docker run pig:1
test docker and print hello
[root@localhost package]#
如此就顺利地搞定了。
4.直接打包文件系统构建镜像
其实前面我们提到,container的export方法只不过是把容器快照当时的文件系统给打包导出了,然后从镜像装在容器只是再用image的import导入后重新挂上内核运行时而已。从这个原理角度,我们完全可以自行手工剪裁系统并打包文件,然后构建镜像。
当然,篇幅原因。另外我打算用这个方法干一件蠢事,所以留待下篇再说……