0%

Docker-1-Docker基础

docker简介

什么是docker

Docker 最初是dotCloud公司创始人Solomon Hykes在法国期间发起的一个公司内部项目,它是基于 dotCloud公司多年云服务技术的一次革新,并于2013年3月Apache 2.0 授权协议开源,主要项目代码在 GitHub上进行维护。Docker 项目后来还加入了Linux基金会,并成立推动开放容器联盟(OCI)。

Docker使用 Google 公司推出的Go语言进行开发实现,基于Linux 内核的cgroupnamespace,以及OverlayFS 类的Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于LXC,从 0.7版本以后开始去除 LXC,转而使用自行开发的libcontainer,从 1.11开始,则进一步演进为使用runCcontainerd

Docker在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

为什么要用docker

docker的优势:

  1. 更高效的利用系统资源:由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

  2. 更快速的启动时间:传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

  3. 一致的运行环境:开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。

  4. 持续交付和部署:对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

    使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。

    而且使用Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

  5. 更轻松的迁移:由于 Docker确保了执行环境的一致性,使得应用的迁移更加容易。Docker可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

  6. 更轻松的维护和扩展:Docker使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

docker与传统虚拟机的比较:

特性 docker 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个
隔离性 安全隔离 完全隔离

docker的核心概念

Docker 包括三个基本概念

  • 镜像(Image
  • 容器(Container
  • 仓库(Repository

理解了这三个概念,就理解了 Docker 的整个生命周期。

镜像

Docker镜像类似于一个虚拟机镜像(xxx.iso),可以将它理解为一个只读的模板!

例如,一个镜像可以包含一个基本的操作系统环境,里面仅安装了Apache应用程序(或用户需要的其他软件)。可以把它称为Apache镜像,镜像是创建Docker容器的基础。

通过版本管理和增量的文件系统,Docker提供了一套十分简单的机制来创建和更新现有的镜像, 用户甚至可以从网上下载一个已经做好的应用镜像, 并直接使用它。

容器

Docker 容器类似于一个轻量级的沙箱, Docker利用容器来运行和隔离应用。

容器是从镜像创建的应用运行实例。它可以启动、停止、删除,而这些容器都是彼此相互隔离、互不可见的。

可以把容器看作一个简易版的 Linux系统环境(包括root 用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子。

注意:

  • 镜像自身是只读的。
  • 容器从镜像启动的时候,会在镜像的最上层创建一个可写层。

仓库

Docker仓库类似于代码仓库,是Docker集中存放镜像文件的场所。

根据所存储的镜像公开分享与否, Docker仓库可以分为公开仓库(Public)和私有仓库(Private)两种形式。

目前,最大的公开仓库是官方提供的Docker Hub,其中存放着数量庞大的镜像供用户下载。国内不少云服务提供商(如腾讯云 、 阿里云等)也提供了仓库的本地源,可以提供稳定的国内访问。

当然,用户如果不希望公开分享自己的镜像文件, Docker 也支持用户在本地网络内创建一个只能自己访问的私有仓库。

当用户创建了自己的镜像之后就可以使用push命令将它上传到指定的公有或者私有仓库。 这样用户下次在另外一台机器上使用该镜像时,只需要将其从仓库上 pull下来就可以了。

Centos安装docker引擎

Docker目前支持CentOS 7及以后的版本, 内核版本必须是3.10

查看操作系统版本:

1
cat /etc/redhat-release

image-20230812174806359

查看内核版本:

1
uname -r

image-20230812174914985

  • 首选要确保之前没有安装过Docker,这里我直接将所有Docker相关的内容全部卸载。

    1
    2
    3
    4
    5
    6
    7
    8
    yum remove docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-engine

    image-20230812175024340

  • 安装时也有两种方式可以选择:

    1. 手动安装:

      安装依赖的软件包

      1
      yum install -y yum-utils device-mapper-persistent-data lvm2

      image-20230812175237148

      添加Docker文档版本的yum源,官方的yum源安装docker比较慢, 我们配置国内比较快的yum源(阿里云)

      1
      yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

      image-20230812175228047

      安装最新版本的docker引擎(社区版)

      1
      yum -y install docker-ce docker-ce-cli containerd.io

      image-20230812175327569

      安装完成!

      image-20230812175349428

    2. 自动安装:

      1
      curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

      image-20230812175616467

      同样可以安装成功!

docker安装完成后是做为Centos中的一个服务,默认是没有开启的,使用以下命令开启docker,并加入开机自启。

1
2
3
systemctl start docker
systemctl enable docker
systemctl status docker

image-20230812175849860

开启服务成功。

为了验证docker是否可以正常使用,可以执行以下命令运行hello-world镜像。

1
docker run hello-world

这个时候,docker会自动的去找hello-world镜像,然后启动镜像。

image-20230812180100914

返回图示内容,说明docker安装成功。

这里我们使用的是root用户,大家在生产环境中可能拿不到root用户,那么要想操作docker必须是root用户才可以,或者使用root权限去运行。(sudo + 命令

可用把普通用户添加到docker组,避免每次都添加sudo,我这里以用户minglog为例,大家需要修改为自己的用户名。

1
sudo usermod -aG docker minglog

镜像基本操作

查看本机所有镜像

1
docker images

image-20230812180429583

这里由于我们前面运行了一个hello-world镜像,所以目录中有一个镜像。

返回的相关信息意义如下所示:

  1. REPOSITORY:来源仓库。
  2. TAG:镜像的标签信息, 表示镜像的版本. 只是标记, 并不能表示镜像内容。
  3. IMAGE ID:镜像id, 唯一表示一个镜像. 如果两个镜像的 ID 相同, 说明它们实际上指向了同一 个镜像, 只是具有不同标签名称而已。
  4. CREATED:镜像的最后更新时间。
  5. SIZE:镜像大小。

删除镜像

在删除镜像时,使用的命令是:

1
docker rmi hello-world:latest

直接删除镜像可能会报错。

image-20230812180928608

错误的原因是因为,我们有一个容器已经使用了这个镜像。

可以使用以下命令查看当前docker容器列表。

1
docker ps -a

image-20230812181024146

可以看到当前这个容器使用的就是hello-world镜像创建出来的,我们需要先删除这个容器,然后才能删除对应的镜像,使用以下命令进行删除。

1
docker rm a2a

image-20230812181141546

注意:rm后面跟的是容器的ID,每个容器创建出来后都会被分配一个随机ID,在使用容器时就需要指定ID,由于ID比较长,在docker中为了方便使用可以不输入完整的ID,只要不引起歧义(例如:如果输入一个a,而a开头的容器有多个,这个时候就有歧义了。)可以只输入前几个字母。

容器删除完毕后,再运行前面的命令,删除镜像。

1
docker rmi hello-world:latest

image-20230812181428822

删除成功。

获取一个新镜像

当我们在本地主机上使用一个不存在的镜像时Docker就会自动在仓库中下载这个镜像。如果我们想预先下载这个镜像,我们可以使用docker pull命令来下载它。

1
docker pull hello-world

image-20230812181556068

image-20230812181518166

下载完成后, 可以使用这个镜像来运行容器。

配置国内镜像源

下载镜像的时候,默认是从官方地址下载,服务器在国外,速度比较慢,可以换成国内镜像。

编辑配置文件:daemon.json

1
vim /etc/docker/daemon.json

填入以下内容:

1
2
3
4
5
6
7
{
"registry-mirrors": [
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://xjy5kx36.mirror.aliyuncs.com"
]
}

然后重启docker

1
systemctl restart docker

查看是否配置成功

1
docker info

image-20230812181920124

搜索镜像

前面我们在下载hello-world镜像时,都是直接输入的镜像的名字,那么为什么可以这样输入呢?或者我们要使用其他镜像的时候应该写什么呢?

镜像是需要我们提前查找的。

  1. 使用命令查找:

    1
    docker search centos

    image-20230812182214745

    此时给我们返回了大量的centos镜像,我们要下载的话就直接使用docker pull + 惊险名称即可,但是使用命令获取的镜像还是十分有限。我们一般在找镜像的时候都是去docker官网

  2. 在官网查找:

    进入官网,输入你需要查找的镜像名称。

    image-20230812182418733

    然后再返回的结果中找一个star数较高的镜像仓库,点击进入。

    image-20230812182450247

    然后点击tags

    image-20230812182544515

    下方就会展示出很多相关的仓库。

    image-20230812182614177

    如果想要搜索指定的版本,还可以在Fillter tags中过滤。

    例如,我在这里搜索一个和我Centos虚拟机版本相同的镜像。

    image-20230812182805514

    然后直接复制对应的镜像下载命令去Centos中运行即可,这个时候就会自动去下载这个镜像。

    image-20230812182855615

    从镜像的大小我们也可以看出,这里的Centos镜像比我们自己安装操作系统的centos.iso文件也小得多。

    下载完成后,在docker镜像列表中也可以看到下载好的镜像。

    image-20230812183019865

镜像的导入与导出

镜像导出

1
docker save -o docker_images.tar 63(镜像ID)

镜像导入

1
docker load -i docker_images.tar mycentos:1.0.0(镜像名称及版本信息)

容器基本操作

容器是Docker的另一个核心概念。 简单来说,容器是镜像的一个运行实例。所不同的是,镜像是静态的只读文件,而容器带有运行时需要的可写文件层,同时,容器中的应用进程处于运行状态

如果认为虚拟机是模拟运行的一整套操作系统(包括内核、 应用运行态环境和其他系统环境)和跑在上面的应用。 那么Docker容器就是独立运行的一个(或一组)应用,以及它们必需的运行环境。

创建容器

前面我们已经下载好了centos7.7.1908docker镜像,那么接下来我们就基于这个镜像创建出一个docker容器。

1
docker create -it centos:7.7.1908  /bin/bash

说明:

  1. 创建一个交互式的容器
  2. -i:允许你对容器内的标准输入 (STDIN) 进行交互
  3. -t:在新容器内指定一个伪终端或终端。

image-20230812183257299

查看容器

容器创建好后可以输入以下命令查看当前容器列表:

1
docker ps -a

输入参数-a表示查看所有容器,如果不加-a则表示之查看启动的容器。

image-20230812183446741

可以看到刚在我们创建出来的容器状态为Created,表示刚刚创建。

启动容器

输入以下命令启动104开头的容器。

1
docker start 104

启动成功后再次查看当前容器情况。

1
docker ps -a

image-20230812183627741

这个时候容器的状态就被修改为了Up 2 seconds,表示容器在2秒前启动。

进入容器

输入以下命令,进入容器。

1
docker exec -it 104 /bin/bash

通过指定-it参数来保持标准输入打开, 并且分配一个伪终端。可以看到会打开一个新的bash终端,在不影响容器内其他应用的前提下,用户可以与容器进行交互。

image-20230812183958223

可以看到此时已经进入到了104容器终端。

新建并启动容器

前面的操作是分步骤进行的,其实也可以直接新建并启动容器,使用的是docker run命令。

例如:启动一个交互式的centos容器

1
docker run -it centos:7.7.1908  /bin/bash

image-20230812184324803

  1. 检查本地是否存在指定的镜像,不存在就从公有仓库下载;
  2. 利用镜像创建一个容器,并启动该容器;
  3. 分配一个文件系统给容器,并在只读的镜像层外面挂载一层可读写层 ;

删除容器

这个前面已经演示过,使用的是docker rm命令。

查看当前所有容器:

1
docker ps -a

image-20230812184522804

删除42df容器。

1
docker rm 42df

image-20230812184558018

删除成功。

如果要删除正在运行的容器,需要先将容器停止(docker stop 容器ID),然后再删除。

也可直接使用-f参数强制删除。

1
docker rm -f 42df

导入与导出容器

导出容器

某些时候,需要将容器从一个系统迁移到另外一个系统,此时可以使用Docker的导入和导出功能,这也是 Docker自身提供的一个重要特性。

为了测试容器是否导出和导入成功, 我们在centos容器中创建一个新的文件a.txt

image-20230812184855621

然后执行exit退出容器。

接下来在在客户机中执行以下命令,导出centos容器。

1
docker export -o ~/test_centos.tar 104
  • -o:后面接的是导出的容器文件路径。

image-20230812185102309

容器导出成功。

导入容器

将上节导出的容器导入之后会成为镜像

使用以下命令进行导入:

1
docker import test_centos.tar mycentos:1.0.0

image-20230812200440587

导入完成后,可以看到对应的结果。

接下来使用该镜像创建容器:

1
docker run -it mycentos:1.0.0 /bin/bash

image-20230812200633147

此时可以看到a.txt仍然存在。

除了使用import参数去导入容器之外,还有一种方式:

1
docker load -i test_centos.tar

也可以导入容器。

查看容器详情

使用以下命令查看容器的运行详情:

1
docker container inspect 104	

此时会返回大量容器的信息:包括容器 Id、创建时间、路径、状态、镜像、配置等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
[
{
"Id": "104ef035ace89419964fc89786d381ed816bcd1fa7df9c20296fb829124059fe",
"Created": "2023-08-12T10:32:43.500143405Z",
"Path": "/bin/bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 9099,
"ExitCode": 0,
"Error": "",
"StartedAt": "2023-08-12T10:35:31.180132945Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:08d05d1d5859ebcfb3312d246e2082e46cb307f0e896c9ac097185f0b0b19e56",
"ResolvConfPath": "/var/lib/docker/containers/104ef035ace89419964fc89786d381ed816bcd1fa7df9c20296fb829124059fe/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/104ef035ace89419964fc89786d381ed816bcd1fa7df9c20296fb829124059fe/hostname",
"HostsPath": "/var/lib/docker/containers/104ef035ace89419964fc89786d381ed816bcd1fa7df9c20296fb829124059fe/hosts",
"LogPath": "/var/lib/docker/containers/104ef035ace89419964fc89786d381ed816bcd1fa7df9c20296fb829124059fe/104ef035ace89419964fc89786d381ed816bcd1fa7df9c20296fb829124059fe-json.log",
"Name": "/trusting_blackwell",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"ConsoleSize": [
37,
104
],
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": [],
"BlkioDeviceWriteBps": [],
"BlkioDeviceReadIOps": [],
"BlkioDeviceWriteIOps": [],
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/ce0432fe01c9d39ae1b43144e55e86f34de7af652c690bb25b3a7492ac6993c7-init/diff:/var/lib/docker/overlay2/a3c704053905bcbfc154ad103027c534c682ba82a0469c4bc952d733c636a126/diff",
"MergedDir": "/var/lib/docker/overlay2/ce0432fe01c9d39ae1b43144e55e86f34de7af652c690bb25b3a7492ac6993c7/merged",
"UpperDir": "/var/lib/docker/overlay2/ce0432fe01c9d39ae1b43144e55e86f34de7af652c690bb25b3a7492ac6993c7/diff",
"WorkDir": "/var/lib/docker/overlay2/ce0432fe01c9d39ae1b43144e55e86f34de7af652c690bb25b3a7492ac6993c7/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "104ef035ace8",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"Image": "centos:7.7.1908",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"org.label-schema.build-date": "20191024",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS"
}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "442bdbb885126073783b14cf732b4ddcb9e2426b188962a69904d13a44eca13f",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/442bdbb88512",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "d2011baaf5aae026c8d8649149f10705f0e07a5debd4814fb08636e9b7093f26",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "15c5d3fb3ee9ac98ee5c1222a3117e6a4d7fd28aeecb19e405b91e1f735ff738",
"EndpointID": "d2011baaf5aae026c8d8649149f10705f0e07a5debd4814fb08636e9b7093f26",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]

可以看到此时容器也有相应的网络配置信息:

IP地址为:172.17.0.2

Gateway网关:172.17.0.1

查看容器内进程

1
docker top 104

image-20230812201544833

可以看到容器中的centos运行的进程十分少,这也是容器技术节省磁盘空间的原因之一,它不创建任何无关的进程。

查看容器资源占用情况

1
docker stats 104

image-20230812201714153

这个时候就开始对104容器进行资源监听。可以看到此时内存占用为412KBCPU占用为0

如果不想监听容器,只是获取其当前的资源占用情况,可以加上--no-stream参数。

1
docker stats --no-stream 104

image-20230812201913707

此时就不会去监听该容器的实时变化情况。

容器与主机之间进行文件交互

  1. 从主机复制文件到容器

    主机文件情况:

    image-20230812202144371

    1
    docker cp helloworld.sh 104:/root/

    image-20230812202159522

    接下来进入104容器root家目录。

    1
    2
    3
    docker exec -it 104 /bin/bash
    cd /root
    ll

    image-20230812202338408

    可以看到有对应的文件上传成功。

    也可以直接使用以下命令,不进入容器shell直接查看/root/下的文件内容。

    1
    docker exec -it 104 ls /root/

    image-20230812202542508

  2. 从容器复制到主机

    1
    docker cp 104:/root/a.txt .

    image-20230812202642911

    可以看到a.txt文件被拷贝到当前目录。

镜像高级操作

创建镜像

前面我们创建的镜像都是直接通过pull命令从仓库拷贝出来的,那么没有办法创建自己的镜像呢?

答案是有的。

创建镜像的方法主要有2种:

  1. 基于已有容器创建。
  2. 基于Dockerfile文件创建

基于已有容器创建

使用的命令是docker commit

1
docker commit -m 'add new file : a.txt' -a 'minglog'  104  new_centos:1.0.0
  • -m:后面跟的是镜像的描述信息。
  • -a:后面跟的是镜像的作者。
  • 104:指的是现有容器。
  • new_centos:1.0.0:新镜像的名称和版本号。

image-20230812203124059

镜像创建成功。

使用新镜像创建一个容器。

1
docker run -it new_centos:1.0.0 /bin/bash

image-20230812203233404

前面创建的文件也存在。

image-20230812203250999

基于Dockerfile 创建

基于Dockerfile创建是最常见的方式。 Dockerfile是一个文本文件, 利用给定的指令描述基于某个父镜像创建新镜像的过程。

下面使用Dockerfile创建一个基于centosjava开发环境:

在创建新镜像前,首先将前面的所有容器全部删除。

image-20230812203606996

然后开始编写Dockerfile文件。

此时可以在自己的windows电脑中写完后再统一上传到虚拟机。

在桌面新建文件夹Centos_Java_Docker,然后将JDK安装包复制进来。

然后新建Dockerfile文件,使用VSCode打开,在VSCode中有相关插件帮助我们更快的编写Dockerfile,填入以下内容。

1
2
3
4
5
6
7
8
9
10
11
FROM centos:7.7.1908


RUN mkdir -p /opt/module
RUN mkdir -p /opt/software
COPY ./software /opt/software

RUN tar -zxvf /opt/software/jdk-*.tar.gz -C /opt/module/

ENV JAVA_HOME=/opt/module/jdk1.8.0_212
ENV PATH=$JAVA_HOME/bin:$PATH

说明:

  1. 每一个指令都会在镜像上创建一个新的层,每一个指令的前缀都必须是大写的。
  2. 第一条FROM,指定使用哪个镜像源
  3. RUN 指令告诉docker 在镜像内执行命令,安装了什么。
  4. COPY 是把文件copy到镜像中,源文件必须是相对路径不能是绝对路径
  5. ENV 在镜像中设置环境变量

接下来将整个文件夹打包,然后上传到虚拟机。

image-20230812205746270

解压文件。

1
unzip Centos_Java_Docker.zip

然后进入Centos_Java_Docker文件夹,执行以下命令使用Dockerfile文件创建镜像。

1
2
cd Centos_Java_Docker
docker build -t centos_java8:1.0.0 .

image-20230812211031818

创建成功。

image-20230812211103868

接下来使用该镜像创建容器。

1
docker run -it centos_java8:1.0.0 /bin/bash

输入以下命令查看java是否安装成功。

image-20230812211215487

安装成功。

为镜像添加ssh服务

编写Dockerfile脚本如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 设置继承镜像
FROM centos_java8:1.0.0
# 提供作者信息
MAINTAINER minglog (luoming@tipdm.com)

# 更换国内阿里云yum源
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
# 阿里云有部分代码为`cloud`这个代码表示只能在阿里云上才可以运行,否则会运行失败,需要将其删除
RUN sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache # 更新yum下载镜像

# 安装sshd
RUN yum install -y openssh-server openssh-clients
RUN sed -i '/^HostKey/'d /etc/ssh/sshd_config
RUN echo 'HostKey /etc/ssh/ssh_host_rsa_key'>>/etc/ssh/sshd_config

# 生成 ssh-key
RUN ssh-keygen -t rsa -b 2048 -f /etc/ssh/ssh_host_rsa_key

# 更改 root 用户登录密码为
RUN echo 'root:123456' | chpasswd

# 开发 22 端口
EXPOSE 22

# 镜像运行时启动sshd
RUN mkdir -p /opt
RUN echo '#!/bin/bash' >> /opt/run.sh
RUN echo '/usr/sbin/sshd -D' >> /opt/run.sh
RUN chmod +x /opt/run.sh
CMD ["/opt/run.sh"]

然后,使用以下命令搭建镜像。

1
docker build -t centos_ssh:1.0.0 .

然后,使用以下命令创建容器并开启ssh

1
docker run -d centos_ssh:1.0.0

image-20230813143003161

SSH需要使用容器的IP地址进行连接,输入以下命令查看容器IP地址。

1
docker container inspect ce3

image-20230813143137445

可以看到容器的IP地址为172.17.0.2,接下来在主机使用SSH连接容器。

1
SSH root@172.17.0.2

image-20230813143241349

输入密码123456后连接成功。

image-20230813143257551

端口映射与容器互联

端口映射

在启动容器的时候,如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。

使用-p参数来指定映射规则:

例如在开启容器时使用以下命令,表示将容器的22端口映射到主机的2222端口

1
docker run -d -p 2222:22 centos_ssh:1.0.0

这个时候就可以直接通过主机的2222端口去连接容器的SSH

在本机Windows电脑就可以直接通过2222端口连接虚拟机中的容器。

image-20230813143701107

image-20230813143944588

连接成功。

通过ssh连接docker容器后环境变量会发生变化,导致前面我们安装过的Java无法使用。

解决方法:更改字段分隔符,使之仅仅遇到换行时分割
/etc/profile中添加以下代码即可。

1
2
3
4
for item in `cat /proc/1/environ |tr '\0' '\n'`
do
export $item
done

image-20230813144815306

容器互联

端口映射并不是唯一把docker连接到另一个容器的方法。docker有一个连接系统允许将多个容器连接在一起,共享连接信息。docker连接会创建一个父子关系,其中父容器可以看到子容器的信息。

容器命名

创建容器的时候,Docker会自动为容器分配一个随机的名字,我们也可以通过--name参数指定一个好记的名字,方便容器间的互联。

1
docker run -d --name hadoop102 centos_ssh:1.0.0

image-20230813145945832

docker的网络

docker还会给我们创建三个网络:bridge/host/none。我们可以通过network ls命令查看当前宿主机中所有的docker网络。

1
docker network ls

image-20230813150112832

其中,网桥bridge模式是在实际项目中常用的。接下来,以交互模式启动两个centos_ssh容器。在没有指定相关网络的情况下,容器都会连接到默认的bridge网络。

在创建一个hadoop103容器。

1
docker run -d --name hadoop103 centos_ssh:1.0.0

查看bridge网络情况。

1
docker network inspect bridge

image-20230813150343679

可以看到hadoop102hadoop103容器的IP地址分别为172.17.0.4172.17.0.5,并且相互之间是可以ping通的。

image-20230813150513316

但是172.17.0.4等这些ip地址不能固定,每次启动容器的时候有可能会变化,如果组建集群的话很不方便。

自定义bridge网络,实现容器间通讯

docker daemon实现了一个内嵌的DNS server,使容器可以直接通过“容器名”通信。使用默认的bridge网络,不能通过DNS server实现通过容器名通信,但是使用自定义bridge网络可以做到通过容器名互相通信。

  1. 创建自定义bridge网络,网络名为minglog

    1
    docker network create --driver bridge minglog

    再次查看当前docker的所有网络

    1
    docker network ls

    image-20230813151258707

  2. 删除容器hadoop102hadoop103

    1
    2
    docker rm -f hadoop102
    docker rm -f hadoop103
  3. 重新创建容器hadoop102hadoop103并加入到minglog网络,通过--network参数指定。

    1
    2
    docker run -d --name hadoop102 --network minglog centos_ssh:1.0.0
    docker run -d --name hadoop103 --network minglog centos_ssh:1.0.0

    然后查看minglog网络状态

    image-20230813153528611

    可以看到在minglog网络中有hadoop102hadoop103两个容器。

    进入hadoop102

    1
    ssh root@172.18.0.2

    然后ping hadoop103IP地址172.18.0.3发现可以ping通。

    image-20230813153732978

虽然现在我们已经能够实现容器之间通讯了,但是此时容器的IP地址还是随机的,还是以172开头的IP地址,我们还是无法直接指定每个容器的IP地址。

要想彻底解决这个问题需要使用到网桥,并且是在宿主机上搭建网桥,然后让所有的容器和宿主机都连接到网桥进行统一IP分配和管理。

image-20230813155618022

网桥搭建实现指定容器IP地址

  1. 安装pipeworkbrctl工具

    在宿主(hadoop102)上安装git(如果已经安装, 跳过该步骤)

    1
    yum install -y git

    下载pipework

    1
    2
    3
    git config --global --unset http.proxy
    git config --global --unset https.proxy
    git clone https://gitclone.com/github.com/jpetazzo/pipework.git

    如果使用上述命令无法克隆仓库内容,那么可以创建脚本pipework,然后将以下网站内容填入脚本中。

    1
    https://gitee.com/mirrors/Pipework/raw/master/pipework

    pipework脚本添加到path

    1
    sudo cp pipework/pipework /usr/bin/

    安装brctl工具

    1
    yum install bridge-utils
  2. 配置网络

    首先删除所有容器

    1
    docker rm -f $(docker ps -aq)

    image-20230813161156160

    接下来查看宿主机网络信息。

    1
    cat /etc/sysconfig/network-scripts/ifcfg-ens33

    image-20230813161442099

    • ens33192.168.128.100
    • 网关:192.168.128.2
    • DNS:192.168.128.2

    在宿主机上执行如下命令:

    1
    2
    3
    4
    5
    6
    sudo brctl addbr br0; \  # 添加网桥br0
    sudo ip link set dev br0 up; \ # 启动网桥br0
    sudo ip addr del 192.168.128.100/24 dev ens33; \ # 将宿主机ip从ens33中删除
    sudo ip addr add 192.168.128.100/24 dev br0; \ # 将宿主机ip加入到网桥中
    sudo brctl addif br0 ens33; \ # 把ens33搭到网桥br0上
    sudo ip route add default via 192.168.128.2 dev br0 # 给br0添加新的路由(将网关信息配置给到br0)

    注意:中间会将宿主机的ipens33中移除,会导致断网,所以要将所有的命令放在一行执行,在每行的末尾加上/

  3. 创建两个容器

    1
    2
    docker run -d --name hadoop102 centos_ssh:1.0.0
    docker run -d --name hadoop103 centos_ssh:1.0.0
  4. 给两个容器添加ip,并搭在br0

    1
    2
    sudo pipework  br0 hadoop102 192.168.128.202/24@192.168.128.2
    sudo pipework br0 hadoop103 192.168.128.203/24@192.168.128.2

    至此搭建完成。

  5. 测试网络是否可以使用。

    • 使用SSH进入hadoop102容器
    1
    ssh root@192.168.128.202

    image-20230813162842949

    进入成功。

    • 测试hadoop102hadoop103通信。

      1
      ping 192.168.128.203

      image-20230813162938013

      通信成功!

    • 测试hadoop102与宿主机通信

      1
      ping 192.168.128.100

      image-20230813163034273

      通信成功。

    • 测试hadoop102windows本机通信

      首先查看本机VM8网卡IP

      1
      ipconfg

      image-20230813163134676

      然后回到hadoop102输入以下命令:

      1
      ping 192.168.128.150

      image-20230813163207951

      通信成功。

现在我们就实现了对容器IP地址进行自定义,并且在本机Windows可以直接通过SSH去访问虚拟机中的容器。

image-20230813163515772

后续我们将为大家演示如何使用容器技术搭建hadoop集群。

注意这仅仅在学习时可能会有效,实际生产中时不允许这样做的,因为所有容器都是放在同一个宿主机的,一旦宿主机宕机,那么所有的节点就会全部崩溃,集群也会无法使用,违背了集群的高可用特性。

-------------本文结束感谢您的阅读-------------