dustland

dustball in dustland

Docker@wsl

Docker@wsl

docker是什么?

docker相对于虚拟机是更新一代的虚拟化技术

Docker和虚拟机的区别? - 知乎 (zhihu.com)

img

基础设施就是笔记本电脑

主操作系统就是windows11

Hypervisor虚拟机管理系统比如windows的Hyper-V,还有VMWare,VritualBox等等

从操作系统就是虚拟机,比如VMWare上的windows server 2003,又如Hyper-V上的WSL.

每个从操作系统都占有独立的内存,CPU等硬件资源.这体现在VMware虚拟机或者WSL运行前可以设置使用CPU数量,内存大小等待

在Docker中,Docker守护进程取代了虚拟机管理类系统,docker是运行在操作系统之上的后台进程,管理docker容器

所有Docker容器都经过Docker守护进程使用CPU,内存等硬件资源.而守护进程是主操作系统的一个后台进程,受主操作系统的调度.

简单的说,docker就是更先进的更轻量级的虚拟技术?

docker四个核心组件

docker的四个核心组件:

客户端与服务器

镜像

仓库

容器

C/S体系

docker可以作为工作站使用,也可以作为客户端使用.

就是计网中的C/S模型

docker镜像

"一个docker镜像可以包含一个完整的ubuntu操作系统环境,里面仅安装了Apache或用户需要的其他应用程序"

可以类比.exe文件,exe在磁盘中躺着的时候,就是一个文件.但是加载进入内存之后就成了进程映像.

docker镜像和.exe文件一个性质,而docker容器和进程映像一个性质

docker镜像是只读的,这好理解,就好比一个已经编译链接生成的.exe文件也是只读的一个道理.因为它就没有被改变的理由.如果要"修改"docker镜像,只能是重新生成或者制作

docker容器

容器是从镜像创建的运行实例.

镜像是一个静态概念,而容器就是一个动态概念

docker中的概念 目标文件中的概念
docker镜像 文件系统中的.exe文件
docker容器 内存中的进程映像

到此不禁要考虑一个问题,如果本次在docker容器中写了一个test.txt文件并保存,

关机之后,下次再启动这个docker容器,这个test.txt还存在吗?如果存在,显然是保存在了磁盘中,保存在那里了?docker镜像中吗?还是主操作系统中?

如果导出这个容器,制作一个新镜像,然后新镜像导入成其他电脑的docker容器,那么这个容器中还有test.txt吗?

实际上docker在镜像上面还有一可写入层,镜像被多个容器共用,每个容器创建后私自的改动写入自己的可写入层

docker仓库

集中存放docker镜像文件的地方

仓库的概念可以类比git,有本地的git仓库,也有远程的公共仓库服务器github,gitee

docker概念 git概念
docker镜像 仓库中的文件
docker本地仓库 git本地仓库
docker远程仓库 github中的仓库
docker注册仓库服务器 github

四组件关系

image-20220722201232759

现阶段用不到Dockerfile,甚至Docker仓库也只是白嫖一下官方的镜像,上传什么的以后再说

wsl上使用docker

ubuntu wsl上的docker安装见

window10下子系统ubuntu安装Docker

换docker下载源

在使用docker之前,先把下载源换成国内源,会方便很多

如果梯子流量管够并且梯子够快,当然不用换下载源.

这里换元不是指wsl的apt命令下载源,也不是指docker中容器的apt命令下载源.

而是指docker从哪里拉取镜像

在wsl中vim /etc/docker/daemon.json

然后添加下载源

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

保存后重启docker服务

1
service docker restart

docker命令

docker基本命令

版本信息
1
docker version
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
root@Executor:/home/docker# docker version
Client: Docker Engine - Community
Version: 20.10.17
API version: 1.41
Go version: go1.17.11
Git commit: 100c701
Built: Mon Jun 6 23:02:57 2022
OS/Arch: linux/amd64
Context: default
Experimental: true

Server: Docker Engine - Community
Engine:
Version: 20.10.17
API version: 1.41 (minimum version 1.12)
Go version: go1.17.11
Git commit: a89b842
Built: Mon Jun 6 23:01:03 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.6
GitCommit: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
runc:
Version: 1.1.2
GitCommit: v1.1.2-0-ga916309
docker-init:
Version: 0.19.0
GitCommit: de40ad0
系统信息
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
root@Executor:/home/docker# docker info
Client:
Context: default
Debug Mode: false
Plugins:
app: Docker App (Docker Inc., v0.9.1-beta3)
buildx: Docker Buildx (Docker Inc., v0.8.2-docker)
scan: Docker Scan (Docker Inc., v0.17.0)

Server:
Containers: 1
Running: 1
Paused: 0
Stopped: 0
Images: 3
Server Version: 20.10.17
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
runc version: v1.1.2-0-ga916309
init version: de40ad0
Security Options:
seccomp
Profile: default
Kernel Version: 5.10.102.1-microsoft-standard-WSL2 ;wsl系统内核版本信息
Operating System: Ubuntu 20.04.4 LTS ;wsl的操作系统信息
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 3.831GiB
Name: Executor
ID: 7LD5:E4VA:BGYO:LUWX:AUSB:2MM2:2RI2:DULK:QK6O:YFNA:REKG:CUFA
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Registry Mirrors:
https://hub-mirror.c.163.com/
https://ustc-edu-cn.mirror.aliyuncs.com/
https://ghcr.io/
https://mirror.baidubce.com/
Live Restore Enabled: false

WARNING: No blkio throttle.read_bps_device support
WARNING: No blkio throttle.write_bps_device support
WARNING: No blkio throttle.read_iops_device support
WARNING: No blkio throttle.write_iops_device support

docker镜像的使用

查找注册仓库中的镜像
1
docker search <镜像>
1
2
3
4
5
6
7
root@Executor:/home/docker# docker search httpd
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
httpd The Apache HTTP Server Project 4090 [OK]
centos/httpd-24-centos7 Platform for running Apache httpd 2.4 or bui… 44
centos/httpd 35 [OK]
clearlinux/httpd httpd HyperText Transfer Protocol (HTTP) ser… 2
...

其中STARS表明该镜像的权威性

OFFICIAL表明该镜像是否为官方镜像

从仓库拉取镜像

要私人定制一个花里胡哨的ubuntu镜像,首先得有一个干净的基础镜像

1
docker pull <仓库名>:<标签>

这里仓库名一般就是操作系统名比如ubuntu,标签就是操作系统版本比如20.04

因为docker仓库一般以操作系统命名,其中的镜像文件一般以对应操作系统版本号命名

1
2
3
4
5
6
root@Executor:~# docker pull ubuntu:20.04
20.04: Pulling from library/ubuntu
d7bfe07ed847: Pull complete
Digest: sha256:fd92c36d3cb9b1d027c4d2a72c6bf0125da82425fc2ca37c414d4f010180dc19
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04
查看本地镜像列表
1
2
3
4
root@Executor:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 20.04 20fffa419e3a 6 weeks ago 72.8MB
ansible/ubuntu14.04-ansible latest 4621d4fe2959 6 years ago 461MB
栏目 意义
RESPOSITORY 镜像仓库源
TAG 镜像标签
IMAGE ID 镜像ID
CREATED 镜像创建时间
SIZE 镜像大小

可以使用仓库源:镜像标签来指定一个唯一的镜像,也可以直接使用镜像ID指定一个唯一的镜像

删除本地镜像
1
docker rmi <镜像>

这里镜像或者是仓库原:镜像标签指定,或者是镜像ID指定

甚至不用输全信息就可以指定唯一一个镜像

比如只输入镜像ID的前两位或者前三位

1
2
3
4
5
6
7
8
9
10
root@Executor:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 20.04 20fffa419e3a 6 weeks ago 72.8MB
ubuntu 18.04 5a214d77f5d7 9 months ago 63.1MB
ansible/ubuntu14.04-ansible latest 4621d4fe2959 6 years ago 461MB
root@Executor:~# docker rmi 5a
Untagged: ubuntu:18.04
Untagged: ubuntu@sha256:0fedbd5bd9fb72089c7bbca476949e10593cebed9b1fb9edf5b79dbbacddd7d6
Deleted: sha256:5a214d77f5d747e6ed81632310baa6190301feeb875cf6bf9da560108fa09972
Deleted: sha256:824bf068fd3dc3ad967022f187d85250eb052f61fe158486b2df4e002f6f984e

这里使用ubuntu:18.04镜像的ID号删除,只需要指定前2位

正在被引用的镜像不能被删除(该镜像有运行容器时不能删除)

创建镜像
1
docker commit

在一个ubuntu20.04的容器中(不是wsl中,是wsl的docker容器中)安装ssh之后创建镜像,然后使用新镜像创建容器,看看ssh还有没有

1
2
root@Executor:/home/docker# docker commit -m="ssh" -a="dustball" 49e5c3d8f7c6 ssh/ubuntu
sha256:886ba4c00ba4b506eb6070d729ff9282a5220717153f3fb8a1f20e7b9f69a236

-m提交描述信息

-a指定作者

ssh/ubuntu:镜像名称

sha256码表明镜像创建完毕,使用docker images观察本地所有镜像

1
2
3
4
root@Executor:/home/docker# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ssh/ubuntu latest 886ba4c00ba4 27 seconds ago 235MB
ubuntu 20.04 20fffa419e3a 6 weeks ago 72.8MB

新镜像ssh/ubuntu显然比安装ssh之前的镜像ubuntu大好多

用新镜像创建容器并尝试运行ssh,发现是有的

1
2
3
4
5
6
7
8
9
root@Executor:/home/docker# docker run -it ssh/ubuntu bash
root@2e155d040460:/# ssh
usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]
[-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
[-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]
[-i identity_file] [-J [user@]host[:port]] [-L address]
[-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
[-Q query_option] [-R address] [-S ctl_path] [-W host:port]
[-w local_tun[:remote_tun]] destination [command]
1
2
patchelf --set-interpreter /home/kali/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --set-rpath /home/kali/glibc-all-in-one/libs/2.23-0ubuntu3_amd64 target_file

1
2
$ patchelf --set-interpreter /home/kali/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --set-rpath /home/kali/glibc-all-in-one/libs/2.23-0ubuntu3_amd64 test

docker容器的使用

创建/删除容器

创建容器

1
docker create [启动选项] <镜像> [命令行] 

比如创建一个终端上交互启动,默认自动执行/bin/bash命令的ubuntu:20.04容器

1
2
3
4
root@Executor:~# docker create -t -i ubuntu:20.04 /bin/bash
e94cf81e102f33880cc1e21d9e50985eaac9927642d925009c4cadc761bb2d8c
root@Executor:~# docker ps -a | grep e94
e94cf81e102f ubuntu:20.04 "/bin/bash" 20 seconds ago Created quirky_chatelet

已经创建但是从未运行过的容器,其状态为"Created"

这里命令行的作用是,规定容器启动后要自动执行的命令

/bin/bash意思是启动后立刻执行/bin/bash这条命令,如果之前默认的shell是bash则无所谓,如果之前默认的shell是sh,tsh等等其他shell,则改为使用bash

关于"启动选项"和"命令行参数"

一定要分清两个东西,前者规定的是如何启动容器,后者规定的是启动后容器立刻自动干啥

删除容器前提是该容器没有被运行

1
docker rm <容器>

运行状态的容器需要强制删除

1
docker rm -f <容器>

删除所有容器

1
2
3
4
5
6
7
8
9
10
11
12
13
root@Executor:~# docker  rm $(docker ps -a -q)
e94cf81e102f
205041549d08
fa210e84dd67
bce56d6e5727
08142959bdde
dd33995e37ab
3a666c93b635
b458aa62f64c
85c3fa6b67d3
3519c8f7070f
79543801ed50
897a086a85f4

或者使用docker container prune

查看所有容器
1
docker ps -a
1
2
3
4
5
6
7
8
9
10
root@Executor:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
08142959bdde ubuntu:20.04 "bash" 21 minutes ago Exited (0) 18 minutes ago awesome_austin
dd33995e37ab ubuntu:20.04 "bash" 23 minutes ago Exited (0) 23 minutes ago festive_lamarr
3a666c93b635 ubuntu:20.04 "bash" 24 minutes ago Exited (129) 38 seconds ago quirky_kilby
b458aa62f64c ubuntu:20.04 "-t" 24 minutes ago Created angry_mclean
85c3fa6b67d3 ubuntu:20.04 "bash" 24 minutes ago Exited (0) 24 minutes ago quizzical_grothendieck
3519c8f7070f ubuntu:20.04 "bash" 25 minutes ago Exited (0) 25 minutes ago affectionate_kare
79543801ed50 ubuntu:20.04 "bash" 26 minutes ago Exited (0) 23 minutes ago sleepy_driscoll
897a086a85f4 ansible/ubuntu14.04-ansible "/bin/bash" 2 hours ago Exited (255) 2 hours ago friendly_bhaskara
栏目 意义 备注
CONTAINER ID 容器 注意不是镜像id,本地所有容器都有一个自己独特的id,用于区分和索引
IMAGE 镜像 容器是从哪个镜像创建的
COMMAND 命令行 create创建或者run创建并启动时的命令行参数
CREATED 创建时间 使用docker run创建该容器的时间
STATUS 状态 created(已创建)
restarting(重启中)
running(运行中)
removing(迁移中)
paused(暂停)
exited(停止)
dead(死亡)
PORTS 端口 容器的端口信息和使用的连接类型(tcp)
NAMES 名称 Docker自动给每个容器都起一个名字,格式是:形容词__名
awesome_austin
festive_lamarr
...
如果要人为给容器起名字,需要在docker run的命令行上加上--name=<名字>
运行/停止容器

容器用create创建之后不会执行,需要start命令执行

1
2
3
4
docker start [启动选项] <容器>
docker start [启动选项] <容器1> <容器2> ...
docker stop [停止选项] <容器>
docker stop [停止选项] <容器1><容器2> ...

启动选项要么是-a(attach),要么是-i(interactive)

对于stop(或者kill)指令,其停止选项可以是多长时间之后停止

关于"启动选项"和"命令行参数"

一定要分清两个东西,前者规定的是如何启动容器,后者规定的是启动后容器立刻自动干啥

ps -a列出的COMMAND栏目,这是docker create时指定的命令行参数,它告诉容器启动后自动做什么

而docker start的启动选项只有两种,-a或者-i,区别是:

-a只会将容器的标准输出绑定到当前终端,docker start -a <容器>执行后,这时候可以看到容器的输出root@容器id

但是尝试在终端输入什么命令,都不会有结果,但是终端也没有阻塞,可以一直输入.

这是因为-a启动的容器,其标准输入并没有绑定到终端,因此我们在终端的任何输入都没有发往容器.

也就是说我们可以从终端上接收docker容器的输出,但是没法给他输入

这貌似很奇怪,因为没有输入哪来的输出呢?

实际上可以在创建容器的时候指定命令行参数,让容器启动就自动执行一些命令,此时这些命令的输出就可以打印到终端

比如创建一个在后台自动每1秒执行一次打印"helloworld"的容器,然后用-a启动参数启动它,就可以看到它一直向终端输出helloworld

1
2
3
4
5
6
root@Executor:~# docker create ubuntu:20.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
167d2e45de172117166431467875f3e44f81a29fbaa1e786fecf2caf77227d25
root@Executor:~# docker start -a 167
hello world
hello world
...

-i会将容器的标准输入绑定到当前终端

使用docker start默认后台运行容器,该命令执行后终端环境还是在wsl中,相应容器已经在后台运行

如果想要让容器在前台与我们交互,可以使用-i启动选项(前提是,create创建容器的时候有加入-it选项,否则start使用-i选项无效)

即:

使用docker create -it <容器>这样创建的容器,使用docker start <容器>会在后台运行(即使没有长期任务也会一直运行),使用docker start -i <容器>会在前台交互.

使用docker create <容器>这样创建的容器,最多可以docker start -a <容器>绑定输出(前提是create时命令行上有长期的任务),但是docker start -i <容器>无效,如果创建容器时命令行上没有给容器指定一个长期的任务,那么后台运行的容器会立刻停止

创建+运行容器
1
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

其作用相当于

1
2
docker create [OPTIONS] <镜像> [COMMAND][ARG...]
docker start [OPTIONS] <容器>

OPTIONS是docker start 的启动选项啊

COMMAND是docker create的命令行参数

一定要分清两者

1
docker run -t -i <镜像> /bin/bash

这里镜像可以是镜像id,也可以是仓库源:镜像标签

-it一起用,意思是让docker分配一个伪终端,并绑定到容器的标准输入

说人话-t作用是加上root@容器号:这种命令提示,不用-t,只有-i也可以进行命令交互,但是不会有这种提示

后面命令行/bin/bash意思是,启动后自动运行bash(虽然默认也是运行的bash,但是可能有些容器默认运行sh),使用bash这个shell进行交互

1
2
root@Executor:~# docker run -t -i ubuntu:20.04
root@08142959bdde:/#

可以发现主机名已经由Executor编变成了容器的id号,现在的终端已经是docker内部环境了

这里容器id和镜像id不同,本地的每个容器都有一个不同的id

如果要退出容器到wsl,只需要在docker容器的终端中输入exit

1
2
3
4
root@Executor:~# docker run -t -i ubuntu:20.04
root@08142959bdde:/# exit
exit
root@Executor:~#

主机名又变回Executor说明目前已经在wsl中了

Exit命令执行后,该docker容器立刻停止.后来如果还想继续使用则需要在wsl中使用docker start命令

创建容器时Docker干了啥

image-20220722210940799

单从"桥接一个虚拟接口到容器",就知道物理机和docker容器在同一个网段

后台运行

刚才已经知道,直接用docker run -t -i ubuntu:20.04这种命令创建容器时,创建容器后终端环境是容器中环境,主机名会变成docker容器号

而现在只想让docker容器运行,但是终端依然是wsl的环境,不进入docker容器,应该咋整呢?

1
docker run -d <镜像> <命令行参数>
1
2
3
4
5
root@Executor:~# docker run -d -it ubuntu:20.04
fa210e84dd679b8402a78ca404988fe7ec031f0475385631f95fb497d764e86b
root@Executor:~# docker ps -a | grep fa21
fa210e84dd67 ubuntu:20.04 "bash" 12 seconds ago Up 12 seconds intelligent_austin
root@Executor:~#

可以发现,run -d创建容器并运行之后,终端上还是显示root@Executor表明仍然在wsl中,没有进到容器里

相当于docker create -it <容器>然后docker run <容器>

进入/退出容器

对于正在后台运行的容器,如果想要进入容器与之交互,需要使用

1
2
docker attach [选项] <正在运行的容器> [命令行]
docker exec [选项] <正在运行的容器>

两者的区别是,

使用attach进入容器,在容器中用exit退出时,容器会停止运行

使用exec进入容器,在容器中用exit退出时,容器会继续运行

由于在wsl中可以使用docker stop命令停止容器,因此只使用exec命令即可

1
2
3
4
5
6
7
8
9
root@Executor:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
49e5c3d8f7c6 ubuntu:20.04 "bash" About an hour ago Up 9 minutes lucid_ramanujan
root@Executor:~# docker exec -it 49 bash
root@49e5c3d8f7c6:/# exit
exit
root@Executor:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
49e5c3d8f7c6 ubuntu:20.04 "bash" About an hour ago Up 9 minutes lucid_ramanujan

退出容器使用exit,或者ctrl+d

导出/导入容器

将容器导出成tar包

1
docker export <容器> > <位置>

比如将49e5c3d8f7c6这个容器(已安装ssh)导出到当前目录

1
2
3
root@Executor:/home/docker# docker export 49 > ssh-ubuntu.tar
root@Executor:/home/docker# ls
ssh-ubuntu.tar

将tar包导入成镜像(不能直接导入成容器)

1
docker import [OPTIONS] <tar包> <镜像>

将tar包导入为指定名称的镜像

1
2
3
4
5
6
7
8
root@Executor:/home/docker# ls
ssh-ubuntu.tar
root@Executor:/home/docker# docker import ssh-ubuntu.tar ssh/ubuntu
sha256:725bf542d42fc0f7aad48f47ef2b81d1bcf7931bd1283a2b91ccbd3f8246e876
root@Executor:/home/docker# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ssh/ubuntu latest 725bf542d42f 5 seconds ago 234MB
ubuntu 20.04 20fffa419e3a 6 weeks ago 72.8MB

后面使用该镜像只需要docker run -it <镜像> bash

docker网络

参考

Docker 网络模式详解及容器间网络通信 - 知乎 (zhihu.com)

Docker:网络模式详解 - Gringer - 博客园 (cnblogs.com)

四个模式

host

docker容器与wsl共用ip地址,docker服务直接使用wsl的端口地址空间,外界可以通过ip:port方式访问docker容器

none

无网络链接

bridge

桥接模式,安装Docker时会为wsl创建一个叫做docker0的虚拟网桥

这个网桥只对桥接的docker容器还有wsl本身可见,作用是wsl和桥接容器之间的通信

两个接到同一网桥的容器或者容器和网桥之间均可以相互通信

对于容器来说,wsl上的网桥就相当于一个交换机

wsl:

1
2
3
4
5
6
7
8
9
root@Executor:~# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:d9ff:fedd:7c32 prefixlen 64 scopeid 0x20<link>
ether 02:42:d9:dd:7c:32 txqueuelen 0 (Ethernet)
RX packets 23965 bytes 1142407 (1.1 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 39791 bytes 118121373 (118.1 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

容器1:

1
2
3
4
5
6
7
8
root@2c98c979b54a:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.4 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:04 txqueuelen 0 (Ethernet)
RX packets 147 bytes 206651 (206.6 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 120 bytes 8092 (8.0 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

容器2:

1
2
3
4
5
6
7
8
root@7cf02bb435be:/var/www/html# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 6 bytes 516 (516.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

wsl的docker0网卡,两个容器的eth0网卡,其IP地址都是172.17.0.X(实际上从子网掩码看出该网段的IP地址可以是172.17.X.X)

docker的桥接和虚拟机的桥接不同

虚拟机和主机桥接之后,虚拟机,主机,主机连接的路由器,都在同一网段

docker容器和docker主机的桥接,所有docker容器和docker主机的docker0网卡在同一网段,docker主机的另一张网卡eth0和路由器在同一网段

img

docker容器和docker0网桥相连使用了veth pair技术,每个容器的eth0都与docker0上的一个veth*(一个数字)配对,两个虚拟网卡组成数据通道,从一个进去必然从另一出来

说是"桥接",实际上相当于docker主机进行了一个NAT变换

所有容器的数据通过自己的虚拟网卡eth0发往docker主机的虚拟 网卡docker0,然后docker主机用"外网"网卡eth0上网(这里外网是相对于docker0虚拟网桥而言的,当然相对于路由器来说eth0还是"内网")

container

两个容器共享同一网络空间,包括ip地址和端口号

目前用不到,用到时再说

docker网络相关命令

1
docker network ...

查看网络模式

1
docker network list

列出三个默认的网络模式

1
2
3
4
5
root@Executor:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
3ce19195196f bridge bridge local
c1341214f1d0 host host local
d4e52924ce6b none null local

目前只用到docker network list命令,其他需要以后再说

配置apache2+php环境

由于官方的镜像不能满足要求,我需要修改官方镜像创建满足我们要求的镜像

容器内基本配置

首先要从官方的镜像改造一个满足我们要求的镜像

这需要经历一个官方镜像->改造容器->导出新镜像的过程

拉取apache+php镜像

1
docker pull php:7.0-apache

创建临时容器

说他是"临时容器",是因为我们需要用这个基础镜像创建容器进行一些改造,然后生成我们需要的镜像

1
docker run -it php:7.0-apache bash

换docker容器的apt下载源

备份

备份原来的下载源

1
/etc/apt/sources.list /etc/apt/sources.list.bak
更新

更新下载源,由于此时没有vi没有vim,只能用cat等shell命令完成

换元之前先看一下容器的操作系统版本

1
2
root@06ca8ea43c1c:/var/www/html# cat /etc/issue
Debian GNU/Linux 9 \n \l

然后上网搜对应版本的国内源进行更换

适用于Debian GNU/LINUX 9的阿里云源:

1
2
3
4
5
6
7
8
9
10
cat > /etc/apt/sources.list << EOF
deb http://mirrors.aliyun.com/debian/ stretch main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ stretch main non-free contrib
deb http://mirrors.aliyun.com/debian-security stretch/updates main
deb-src http://mirrors.aliyun.com/debian-security stretch/updates main
deb http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib
deb http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib
deb-src http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib
EOF

配置网站

在该容器的网站根目录/var/www/html下面,写一个info.php,打印php信息作为测试

1
<?php phpinfo(); ?>

在系统根目录下写一个/start.sh

1
2
/etc/init.d/apache2 restart   
/usr/bin/tail -f /dev/null

第一条作用是启动apache2服务器,第二条作用是,让执行这条命令的容器一直有前台进程在忙,防止docker容器自动停止

安装ssh等其他可有可无的服务...

导出镜像

1
docker commit <容器> apache/ubuntu:latest

将该容器导出为一个叫"apache/ubuntu:latest"的容器

从新镜像创建新容器

现在使用docker run命令,从新apache/ubuntu:latest镜像上创建并启动新容器

1
docker run -dit -p 10011:80 apache/ubuntu:latest /bin/bash -c "/start.sh"

-d后台运行

-it启动交互终端

-p 10011:80,wsl的10011端口映射到容器的80端口

apache/ubuntu:latest我们自定义的镜像名

/bin/bash -c "/start.sh"命令行参数,启动后立刻自动执行

没有指定网络模式默认为桥接模式

没有指定端口上的传输层服务类型默认为TCP服务

1
2
3
4
5
root@Executor:~# docker run -dit -p 10011:80 apache/ubuntu /bin/bash -c "/start.sh"
edc1b61f6c7a20ce4d85989ffa4c5042dc13769161f4d89952604a8496b869d1
root@Executor:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
edc1b61f6c7a apache/ubuntu "docker-php-entrypoi…" 2 seconds ago Up 1 second 0.0.0.0:10011->80/tcp, :::10011->80/tcp friendly_northcutt

此时新容器就创建好了,可以看到PORTS栏目里面,wsl的10011端口已经映射给了docker的80端口,其上运行了一个tcp服务

访问容器

怎么访问这个容器呢?

访问这个容器就是访问wsl的10011号端口,可以使用ip:port方式进行访问

在wsl上使用ifconfig看一下wsl的ip地址是多少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@Executor:~# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:86ff:feaf:17dc prefixlen 64 scopeid 0x20<link>
ether 02:42:86:af:17:dc txqueuelen 0 (Ethernet)
RX packets 45 bytes 26682 (26.6 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 50 bytes 5382 (5.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.31.174.19 netmask 255.255.240.0 broadcast 172.31.175.255
inet6 fe80::215:5dff:fe99:9ce8 prefixlen 64 scopeid 0x20<link>
ether 00:15:5d:99:9c:e8 txqueuelen 1000 (Ethernet)
RX packets 613 bytes 86373 (86.3 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 304 bytes 90966 (90.9 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

发现wsl在windows主机NAT后面的ip地址是172.31.174.19

虽然wsl和windows主机之间的网络模式是NAT模式,与windows主机同路由器局域网内的其他设备是无法访问wsl的,

但是windows主机是可以访问wsl的,windows主机相对于wsl就是"外网环境",我们只需要在windows主机浏览器上访问172.31.174.19:10011

或者windows主机上curl 172.31.174.19:10011/info.php

image-20220724204301206

可以看见,windows主机是可以访问docker容器的apache2服务的

站在windows主机的角度,怎样看出这是一个docker容器提供的apache2服务,而不是wsl提供的apache2服务呢?

从该phpinfo的回显结果可以隐约看出这是一个docker容器提供的服务

image-20220724204601250

首先这个System名,谁会起一个edc1b61f6c7a作为主机名啊?这个数是一个docker容器id

1
2
3
root@Executor:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
edc1b61f6c7a apache/ubuntu "docker-php-entrypoi…" 12 minutes ago Up 11 minutes 0.0.0.0:10011->80/tcp, :::10011->80/tcp friendly_northcutt

其次,后面的configuration->apache2handler中

image-20220724204736232

这里ip地址和端口号均与wsl不一样,如果是wsl直接提供apache2服务,这里应是172.31.174.19:80

1
2
3
4
5
6
7
8
9
10
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse