Docker笔记

本文是对《docker技术入门与实战》这本书所作的笔记

操作Docker容器

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
docker images -aq # 查看所有镜像ID
docker ps -aq # 查看所有容器ID

docker create -it test:0.0.2 # 创建一个容器但是处于停止状态

docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f1db8900b538 test:0.0.1 "/bin/bash" 2 minutes ago Up 24 seconds competent_hertz

docker start f1db8900b538 # 启动容器
docker stop f1db8900b538 # 停止容器
docker rm $(docker ps -aq) # 删除所有容器

新建并启动容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker run --name=ub -it ubuntu:latest /bin/bash
root@b9e36d1fad23:/# exit # 当 exit 后,也会退出容器

docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b9e36d1fad23 ubuntu:latest "/bin/bash" 56 seconds ago Exited (0) 7 seconds ago ub

# 由于 run 以及 create 容器,因此,下一次不需要再次 run,只需要 start 即可启动之前创建好的容器
docker start -i ub # 或者 docker start -i b9e36d1fad23
root@b9e36d1fad23:/# exit

docker killl ub # 杀死运行的容器
docker stop ub # 停止容器
docker rm ub # 删除容器

容器守护态运行,只需要在创建create/run 时指定 -d 参数即可

1
2
3
4
5
6
7
8
9
10
11
docker run --name=ub -it -d ubuntu:latest /bin/bash -c "while true; do echo hello >> log_file; sleep 1; done"
9578f3a1519446846452c5209d017741f3ee2d672ccdfd68884d9b896274b2c9

docker exec -it ub /bin/bash # 进入到容器环境
root@80b21e641d87:/# cat log_file -n
1 hello
2 hello
3 hello
4 hell
...

停止容器

暂停容器

1
2
3
4
5
6
7
8
9
10
docker run --name=ubuntu -it ubuntu /bin/bash
docker start ubuntu

docker pause ubuntu # 暂停容器

docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c94d39af039f ubuntu "/bin/bash" About a minute ago Up 13 seconds (Paused) ubuntu

docker unpause ubuntu # 恢复容器

如果执行一个已经暂停的容器,那么会报错

1
2
docker exec -it ubuntu /bin/bash
Error response from daemon: Container ubuntu is paused, unpause the container before exec

如果删除一个正在运行的容器,那么也会报错

1
2
docker rm ubuntu
Error response from daemon: You cannot remove a running container c94d39af039f28e1c8248d8bbcac3427d4096a70874431d8056e9ec6047af4c4. Stop the container before attempting removal or force remove

启动、停止、重启容器

1
2
3
docker stop ubuntu # 停止容器
docker start ubuntu # 启动
docker restart ubuntu # 重启

进入容器

attach 命令

1
2
3
docker run --name=ubuntu -it ubuntu /bin/bash
docker start ubuntu
docker attach ubuntu

attach是同步执行命令的,这意味着某个窗口内的操作会影响到其他多个窗口的显示

exec 命令

1
2
3
docker run --name=ubuntu -it ubuntu /bin/bash
docker start ubuntu
docker exec -it ubuntu /bin/bash

导入和导出容器

1
2
3
4
5
6
7
8
9
10
11
12
13
docker container export -o=output.tar ubuntu 
# 或者
docker export -o=output.tar ubuntu

ls -lh output.tar

docker import output.tar ubuntu2:0.0.01 # 导入
sha256:d600b372607e36eef6ab67a15548d8fdfdfc9abcdf18783219d3f6c419966200

docker images # 查看所有镜像文件
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu2 0.0.01 d600b372607e 10 seconds ago 72.8MB
ubuntu latest 597ce1600cf4 2 weeks ago 72.8MB

导入容器和导入镜像的区别是:

  • 导入容器快照将丢弃所有的历史信息和元数据
  • 导入镜像则是保存所有完整的记录,体积更大

查看容器

inspect

1
docker inspect ubuntu

top

1
docker top ubuntu

stats 查看统计信息

1
2
docker stats 
docker stats ubuntu

其他容器命令

cp命令,在本地文件系统和容器直接复制文件

1
2
docker cp 002.jpg ubuntu:/tmp/          # 本地复制到容器
docker cp ubuntu:/tmp/002.jpg ./003.jpg # 容器复制到本地

diff 命令查看容器内文件系统的变化

1
2
docker diff ubuntu
docker container diff ubuntu

port 查看端口映射

1
2
docker container port ubuntu
docker port ubuntu

端口映射与容器互连

端口映射与容器互连

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker pull nginx:latest
# 将容器的80端口映射到主机的8080端口
docker run -p8080:80 --name web -it nginx:latest

# 之后就可以通过浏览器访问
curl AAA.BBB.CCC.DDD:8080

docker start web
docker stop web
# 暂停容器执行,那么curl将无法访问
docker pause web
docker unpause web

# 查看容器log信息
docker logs web

使用 -p 指定端口映射有以下几种方式:

  • 映射的IP地址:映射的主机端口:容器端口
    比如 docker run -p127.0.0.1:8080:80 --name web -it nginx:latest
    表示在主机上127.0.0.1端口8080监听

  • 映射IP地址::容器端口
    比如 docker run -p127.0.0.1::80 --name web -it nginx:latest
    表示在主机上127.0.0.1端口任意一个监听,具体是哪一个可以通过 docker ps 查看

1
2
3
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
31122a7a1c45 nginx:latest "/docker-entrypoint.…" 11 seconds ago Up 11 seconds 127.0.0.1:49153->80/tcp web

然后 curl 127.0.0.1:49153

  • 映射的主机端口:容器端口
    本地监听所有地址 0.0.0.0:端口
1
2
3
4
# 查看容器下得到端口映射
docker port web
80/tcp -> 0.0.0.0:8080
80/tcp -> :::8080
1
2
# 在容器终止后立即删除创建的容器
docker run -it --name web --rm -p8080:80 web nginx:latest

容器互连

--link 用于容器之间的互连

例子

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
docker pull training/postgres
# 开启数据库服务器
docker run -it --name db training/postgres
docker start db

# --link name:alias
# 其中 name 表示要连接的 数据库容器名字,alias是别名
docker run --name webdb -it -p8080:5000 --link db:db training/webapp python app.py

# 访问 WEB URL
curl AAA.BBB.CCC.DDD:8080

查看环境信息
docker run --name webdb -it -P --rm --link db:db training/webapp env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=fca1c5740e6c
TERM=xterm
DB_PORT=tcp://172.17.0.2:5432
DB_PORT_5432_TCP=tcp://172.17.0.2:5432
DB_PORT_5432_TCP_ADDR=172.17.0.2
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_PROTO=tcp
DB_NAME=/webdb/db
DB_ENV_PG_VERSION=9.3

docker exec -it webdb /bin/bash # 进入容器内部
root@f1f8c4964731:/opt/webapp# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 db e3afbd191344
172.17.0.3 f1f8c4964731

ping db # 检测web和数据库是否连通

匿名卷和命名卷的区别

匿名卷

匿名卷在进行数据卷挂载时不指定宿主主机目录,也就是 -v 只指定了容器内的目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 匿名挂载 /tmp 为容器内的目录
docker run -itd --name u1 -v /tmp ubuntu
3bd9272862ce13dce6acca5d558a1db74309997cb743b29109c60d6739a4aa7d

# 进入容器
docker exec -it u1 /bin/bash
root@3bd9272862ce:/# df -hT
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 40G 18G 21G 46% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
shm tmpfs 64M 0 64M 0% /dev/shm
/dev/vda2 ext4 40G 18G 21G 46% /tmp
tmpfs tmpfs 1.9G 0 1.9G 0% /proc/acpi
tmpfs tmpfs 1.9G 0 1.9G 0% /proc/scsi
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/firmware

查看该匿名卷中宿主主机下映射到哪一个目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker inspect u1
# 找到下面的 key
"Mounts": [
{
"Type": "volume",
"Name": "e65dcc2666344331c9b0018b2ad075486d9ff86ed48896fd0ba3c70ac2b9708e",
"Source": "/var/lib/docker/volumes/e65dcc2666344331c9b0018b2ad075486d9ff86ed48896fd0ba3c70ac2b9708e/_data",
"Destination": "/tmp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],

# 发现匿名卷位于本地主机中一个卷,可以通过以下命令查看
docker volume ls
DRIVER VOLUME NAME
local 491fd24e3e5d13984342fd8e84ed76902b486b6e542ab6a2bbe84c9dffe29039
local 62094e39dd958d49be248afa38a7b045480167d0f4ed8007d761b7b6024758f7
local e65dcc2666344331c9b0018b2ad075486d9ff86ed48896fd0ba3c70ac2b9708e

命名卷

命名卷就是在进行数据卷挂载时指定宿主主机目录,也就是 -v 宿主主机目录:容器内目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 命名挂载到具体的宿主主机目录下
docker run -itd --name u2 -v /tmp:/tmp ubuntu
452db5e69fb7bbf89913db7b0ab40fb40bb06f871248031a1d49ee2e68ed86de

root@452db5e69fb7:/# df -hT
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 40G 18G 21G 46% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
shm tmpfs 64M 0 64M 0% /dev/shm
/dev/vda2 ext4 40G 18G 21G 46% /tmp
tmpfs tmpfs 1.9G 0 1.9G 0% /proc/acpi
tmpfs tmpfs 1.9G 0 1.9G 0% /proc/scsi
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/firmware

# 查看该目录
root@452db5e69fb7:/# ls /tmp
1
2
3
4
5
6
7
8
9
10
11
12
docker inspect u2
# 找到Mounts ,发现挂载到宿主主机下的 /tmp目录
"Mounts": [
{
"Type": "bind",
"Source": "/tmp",
"Destination": "/tmp",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],

数据卷

所谓的数据卷就是用于容器之间共享数据的一个特殊目录,同时也可以和宿主主机进行数据的交换。

并且对数据卷的修改不会影响镜像文件,使得应用和数据分隔开。

数据卷存储在本地的 /var/lib/docker/volumes/ 目录,需要root权限读取

操作 volume

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker volume create -d local test # 创建一个数据卷

docker volume inspect test # 查看数据卷
[
{
"CreatedAt": "2021-10-15T20:45:20+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/test/_data",
"Name": "test",
"Options": {},
"Scope": "local"
}
]

使用数据卷,可以在 docker run 使用数据卷,有以下几种方法可以使用

  • 普通数据卷volume,映射到主机 /var/lib/docker/volumes/
  • 绑定数据卷bind,在 run 容器时就映射到主机的某个目录
  • 临时数据卷tmpfs,只存在于内存中,容器退出后自动删除数据卷

使用数据卷有两种方式:

  • -v/--volume ,由(:)分隔的三个字段组成,卷名:容器路径:选项列表。选项列表可以 ro/rw.ro=readonly
    • 将宿主目录绑定到容器中的某个目录位置
  • --mount ,由多个键值对组成,由 , 分隔,每个由一个 <key=<value>> 元组组成。
    • type ,值可以为 bind,volume,tmpfs
    • source ,对于命名卷,是卷名。对于 匿名卷,这个字段被省略。可能被指定为 sourcesrc
    • destination ,文件或目录将被挂载到容器中的路径。可以指定为 destinationdsttarget
    • volume-opt 可以多次指定。
    • 可以挂载宿主主机中的某一个文件
    • 详细可以参考 https://www.cnblogs.com/panpanwelcome/p/12604262.html
    • 例1:普通数据卷 --mount type=volume,source=test,destination=/tmp,ro
    • 例2:绑定数据卷 --mount type=bind,src=/root/tmpdir,dst=/tmp,rw
    • 例2:临时数据卷 --mount type=tmpfs,target=/tmp,不需要指定卷名
1
2
3
4
5
6
7
8
9
10
11
12
13
docker pull training/webapp
# 本地目录必须是绝对路径,容器目录可以是相对路径
# 下面这个是匿名卷 source
docker run -d -P --name web \
--mount type=bind,source=/home/ubuntu/mnt/webapp,destination=/opt/webapp \
training/webapp:latest \
python3 app.py

# 上面的绑定数据卷可以替换为
docker run -d -P --name web2 \
-v /home/ubuntu/mnt/webapp:/opt/webapp \
training/webapp \
python3 app.py

数据卷容器也是一个容器,其作用就是提供数据卷给其他的容器

1
2
3
4
5
6
7
8
9
10
11
12
docker run -it -v /dbdata --name ubuntu2 ubuntu:latest
root@2243882e6053:/# ls /dbdata/ # 查看数据卷下的所有文件

# 下面创建两个容器用于挂载容器ubuntu2中提供的数据卷
# --volumes-from 还可以从多个容器中挂载数据卷
docker run -it --volumes-from ubuntu2 --name db1 ubuntu:latest
docker run -it --volumes-from ubuntu2 --name db2 ubuntu:latest
# 这里从 db1 中挂载数据卷
docker run -it --volumes-from db1 --name db2 ubuntu:latest
# 这两种容器下均有一个 /dbdata 数据卷
# 查看数据卷下的文件
docker exec db1 /bin/bash -c "cat /dbdata/helloworld"

当删除一个容器时并不会删除数据卷,相反如果要删除数据卷,那么必须最删除所有已经挂载的容器后,

手动调用命令 docker rm -v 删除关联到匿名数据卷的容器(注意,需要停止正在运行的容器才可以删除数据卷)。

上面中创建了四个数据卷容纳,分别为 ubuntu2, db1, db2, db3

因此如果要删除,需要依次调用:

1
2
3
4
docker rm -v ubuntu2
docker rm -v db1
docker rm -v db2
docker rm -v db3

更多例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 挂载本地主机 /tmp 目录到容器 /tmp 下
# 之后容器访问 /tmp 相当于访问宿主主机的 /tmp 目录
docker run -it --name ubuntu2 --mount type=bind,source=/tmp,target=/tmp/ ubuntu:latest
# 创建挂载临时数据卷的容器时不需要指定源目录
docker run -it --name ubuntu3 --mount type=tmpfs,target=/tmp/ ubuntu:latest

# 挂载成功后 df -hT 可以查看该虚拟数据卷
root@227dc53d29e0:# df -hT
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 40G 15G 24G 39% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
shm tmpfs 64M 0 64M 0% /dev/shm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
docker volume create test
# 挂载 命名卷 到 容器下的 /tmp
docker run -it --name ub1 -v test:/tmp ubuntu:latest
# 挂载 ub1 中数据卷 到 ub3 下,此时 ub3可以和ub1共享目录
docker run -it --name ub2 --volumes-from ub1 ubuntu:latest

# 先删除关联到数据卷的容器
docker rm -v ub1
docker rm -v ub2
# 如果数据卷正在使用,那么无法删除
docker volume rm test

# 下面这种方法和上面是一样的
docker run -it --name ub1 --mount type=volume,source=test,target=/tmp ubuntu:latest
docker run -it --name ub2 --volumes-from ub1 ubuntu:latest

持续开发与管理

集成通过自动化的构建完成,包括自动编译、自动测试和发布,减少人工的干预。

持续交付强调产品在修改后要及时的进行线上自动化测完和发布,使得尽管发生很小的改变也要发布。
这与传统的一旦有重大更新才发布不一样。

关于使用 jenkins 进行持续开发

1
2
3
4
docker run -it --name ci -p 8080:8080 -p 5555:50000 jenkins
docker start ci
# 查看初始化密码
docker logs ci

使用 gitlab

gitlab提供了社区版本的Gitlab(CE)的docker镜像文件

使用Docker安装Gitlab CE社区版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export GITLAB_HOME=/srv/gitlab
# 获取镜像,然后运行
docker run --detach \
--hostname AAA.BBB.CCC.DDD \
--publish 443:443 --publish 8080:80 --publish 5555:22 \
--name gitlab \
--restart always \
--volume $GITLAB_HOME/config:/etc/gitlab \
--volume $GITLAB_HOME/logs:/var/log/gitlab \
--volume $GITLAB_HOME/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest

# run完成以后由于要初始化,可以通过下面命令查看初始化进程
sudo docker logs -f gitlab
# 查看初始化root密码,用于浏览器登录gitlab
docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
Password: YCLOBVBHgCXtFwjzPomPDF2+5Z/DMN+1ZKlM9oYc1Rc=

具体可以到 https://docs.gitlab.com/ee/install/docker.html 查看帮助文档

搭建私有仓库

1
2
3
4
5
# 拉取注册仓库
docker pull registry
# 将容器的5000端口映射到宿主机的5555端口,且后台运行
# 同时将上传的镜像文件存储到 映射的宿主目录
docker run -d -p 5555:5000 --name=registry-srv -v /home/ubuntu/mnt/registry:/var/lib/registry registry

搭建可视化WEB客户端

1
2
3
4
5
6
docker pull hyper/docker-registry-web
# 启动该容器
docker run -it -p 8080:8080 --name registry-web --link registry-srv \
-e REGISTRY_URL=http://AAA.BBB.CCC.DDD:5555/v2 \
-e REGISTRY_NAME=localhost:5555 \
hyper/docker-registry-web

之后可以通过浏览器访问 http://AAA.BBB.CCC.DDD:8080 查看

上传image仓库

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
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 597ce1600cf4 2 weeks ago 72.8MB
registry latest b2cb11db9d3d 6 weeks ago 26.2MB
hyper/docker-registry-web latest 0db5683824d8 5 years ago 599MB

# 标记image
docker tag ubuntu:latest AAA.BBB.CCC.DDD:5555/joxrays/mycustomubuntu:0.0.1
# 上传
docker push AAA.BBB.CCC.DDD:5555/joxrays/mycustomubuntu:0.0.1
The push refers to repository [AAA.BBB.CCC.DDD:5555/joxrays/mycustomubuntu]
Get https://AAA.BBB.CCC.DDD:5555/v2/: http: server gave HTTP response to HTTPS client

# 错误以上错误说明需要HTTPS的仓库,可以修改配置文件
# 然后重启 docker
vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
],

"insecure-registries" :["AAA.BBB.CCC.DDD:5555"]
}

sudo systemctl restart docker
# 启动仓库中心和web端
docker start registry-src
docker start registry-web

# 然后再次push
docker push AAA.BBB.CCC.DDD:5555/joxrays/mycustomubuntu:0.0.1
1
2
3
4
5
6
7
8
cd ~/mnt/registry/docker/registry/v2/repositories
tree -L 3
.
└── joxrays
└── mycustomubuntu
├── _layers
├── _manifests
└── _uploads

可以看到上传成功
经过上面的步骤,现在可以pull私有仓库中的image

1
docker pull AAA.BBB.CCC.DDD:5555/joxrays/mycustomubuntu:0.0.1

网络功能

docker0

Docker服务启动时会在本地系统中创建一个虚拟网络设备——网桥docker0
用于和容器之间进行数据的通信,可以通过以下命令查看当前docker0网桥以及挂载到的容器接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 可能需要安装 sudo apt install bridge-utils
brctl show docker0
bridge name bridge id STP enabled interfaces
docker0 8000.0242498071b9 no veth3bb7ad1
veth5eeff11
veth6ac9fcf
查看docker0的IP地址信息
ip addr show docker0
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:49:80:71:b9 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:49ff:fe80:71b9/64 scope link
valid_lft forever preferred_lft forever

可以看到上面列举了当前运行的所有容器虚拟接口 vethXXXXXX 以及 docker0 的IP地址为 172.17.0.1
而在容器内部可以查看到其 eth0 接口配置

1
2
3
4
5
6
7
docker start ubuntu
docker exec -it ubuntu /bin/bash
ifconfig
eth0 ....

# 查看路由表信息
route -n

大概就是:在Docker服务启动后,会在宿主主机创建一个虚拟网桥 docker0,其IP地址作为每一个容器的 eth0 网关IP,
这意味着所有发送和接收数据都要经过docker0网桥,从而实现不同容器可以直接进行数据通信。

具体的做法是每一个容器除了内部有一个 eth0 接口之外,在每一个容器外还有一个 vtheXXX 接口,该接口挂载到 docker0 网桥上,因此可以很方便的和容器内部 eth0 通信
一个大致流程如下

1
容器1(eth0) <-> vethXXX <-> docker0 <-> vethYYY <-> 容器2(eth0)

容器访问控制

通过 iptables 管理防火墙设置

映射容器端口到主机端口,也就是所谓的内网穿透,使得外网可以访问容器内网,一般是在容器run时指定一个映射端口,除此之外,主机也应该通过 iptables 开放该映射端口

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
# 查看当前正在运行的容器映射的端口
docker port goweb
5555/tcp -> 0.0.0.0:5555
5555/tcp -> :::5555

docker port reg-srv
443/tcp -> 0.0.0.0:443
443/tcp -> :::443

# 查看nat表
sudo iptables -t nat -nvL
...
Chain POSTROUTING (policy ACCEPT 431 packets, 26664 bytes)
pkts bytes target prot opt in out source destination
3 172 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:443
0 0 MASQUERADE tcp -- * * 172.17.0.5 172.17.0.5 tcp dpt:5555

Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
197 11732 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 to:172.17.0.2:443
7 388 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5555 to:172.17.0.5:5555

...

上面 Chain DOCKER 两条转发规则表示的意思是,将发往宿主主机上的所有IP地址的且对应某个映射端口(比如443、5555)的数据转发到对应的容器内部地址

1
2
202.192.80.1 -> AAA.BBB.CCC.DDD:5555 -> 172.17.0.5:5555
202.192.80.1 -> AAA.BBB.CCC.DDD:443 -> 172.17.0.2:443

配置容器网桥

Docker创建容器时,默认随机从空闲网段中选择一个作为 eth0 的IP地址,同时也会将 docker0 作为该容器的默认网关

自定义网桥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 删除旧网桥
sudo systemctl stop docker
sudo ip link set dev docker0 down
sudo brctl delbr docker0

# 创建一个新网桥 bridge0
sudo brctl addbr bridge0
sudo ip addr add 192.168.1.1/24 dev bridge0
sudo ip link set dev bridge0 up
# 查看
brctl show bridge0

# 配置 DOCKER_OPTS 参数
echo 'DOCKER_OPTS="-b=bridge0"' | sudo tee /etc/default/docker
sudo systemctl restart docker

PS:上面这些方法对于Docker来说没有用了,因该使用最新的方法
具体可以查看官方文档: https://docs.docker.com/network/bridge/

服务代理也就是反向代理,接收来自外部互联网的请求,然后根据控制请求将其转发到内部的服务器。为了提高服务器的吞吐量,一般将服务代理服务器作为负载均衡机器,提供一个统一的接口,分发用户访问请求。从而减轻内部服务器的处理负担。

Dockerfile创建镜像

Dockerfile常用指令

Dockerfile 中的一些常用指令

指令 用法
ARG 定义创建镜像时使用的变量
FROM 指定一个构建基础镜像,每个镜像一条FROM指令
LABEL 指定一些基本的元数据信息
EXPOSE 镜像内监听哪些端口
WORKDIR 指定一个工作目录
VOLUME 指定一个数据卷挂载点
ENV 指定镜像的环境变量
SHELL 指定SHELL类型

下面是基本的运行指令

指令 用法
RUN 用于运行特定的指令
CMD 在容器启动时执行的默认命令
ADD 添加内容到镜像
COPY 复制内容到镜像

多步骤构建,再一个复杂的Docker镜像文件中,一般需要对其进行编译,然后再在运行环境中运行。

Docker构建应用程序时可以分多步进行编译并最终可以运行,一般Dockerfile需要至少两个镜像构建文件

  • 编译环境镜像,用于编译生成二进制文件
  • 运行环境镜像,提供二进制文件运行的最基本环境

下面举一个简单的例子说明如何构建一个go程序以及运行

下面go代码文件和dockefile文件均位于同一个目录,假设为 ./build
Go代码

1
2
3
4
5
6
package main
import "fmt"

func main() {
fmt.Println("hello world")
}

Dockerfile文件

1
2
3
4
5
6
7
8
9
10
11
12
FROM golang:1.9 as builder
RUN mkdir -p /go/src/test
WORKDIR /go/src/test
COPY main.go . # 复制 main.go 到 /go/src/test 中
RUN CGO_ENABLED=0 GOOS=linux go build -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificate
WORKDIR /root
COPY --from=builder /go/src/test/app .

CMD ["./app"]

然后就可以开始构建了

1
2
3
4
5
# 构建一个 name:tag 的镜像
docker build -t joxrays/mytest:0.0.1 ./build
REPOSITORY TAG IMAGE ID CREATED SIZE
joxrays/mytest 0.0.1 e95bf2c68de1 About a minute ago 7.97MB
<none> <none> 5313430166ba 4 minutes ago 751MB

可以看到编译环境镜像 751MB,而用于生产环境的镜像大小才 7.97MB

为镜像添加SSH服务

创建一个带有SSH服务的镜像,有以下两种方法

  • 使用命令 docker commit 命令创建
  • 基于 Dockerfile 构建

docker commmit 构建

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
# 进入容器
docker start ubuntu
docker exec -it ubuntu /bin/bash

# 在容器内执行以下命令
apt update
apt install openssh-server
# 启动SSH服务
mkdir -p /var/run/sshd
/usr/sbin/sshd

netstat -anplt | grep 22

# 然后修改SSH服务的安全登录配置
vim /etc/pam.d/sshd
# 注释字段
# session required pam_loginuid.so

cd /root
mkdir .ssh
vim authorized_keys
# 复制本地主机 .ssh/id_rsa.pub 公钥到容器中的 authorized_keys 文件
cat /home/ubuntu/.ssh/id_rsa.pub

# 重启SSH服务器
pkill sshd
/usr/sbin/sshd [-D]

exit

# 保存镜像文件
docker commit -a "joxrays" -m "test image" ubuntu sshdubuntu:v1
# 查看images
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sshdubuntu v1 e87ed0e7ae06 3 seconds ago 273MB

之后就可以run一个容器,这里我并没有一开始就进入容器,而是run的时候让其进入后台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 后台运行容器,暂时不进入。这里指定运行容器时要运行的命令 /usr/sbin/sshd -D
docker run --name sshdub -it -d -p 5555:22 sshdubuntu:v1 /usr/sbin/sshd -D
# 查看端口映射
docker port sshdub
22/tcp -> 0.0.0.0:5555
22/tcp -> :::5555

# 进入容器
docker exec -it sshdub /bin/bash

# 修改配置 vim /etc/ssh/sshd_config
RSAAuthentication yes #启用 RSA 认证
PubkeyAuthentication yes #启用公钥私钥配对认证方式
AuthorizedKeysFile .ssh/authorized_keys #公钥文件路径(和上面生成的文件同)
PermitRootLogin yes #root能使用ssh登录

exit

# 连接到容器中的ssh
ssh [email protected] -p 5555

基于 Dockerfile 构建

1
2
3
mkdir work-ssh-dir && cd work-ssh-dir
touch Dockerfile run.sh
cat ~/.ssh/id_rsa.pub > authorized_keys

run.sh脚本内容

1
2
#!/bin/bash
/usr/sbin/sshd -D

Dockerfile文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
FROM ubuntu:latest

# 元数据信息
LABEL author="joxrays"
LABEL email="[email protected]"
LABEL version="1.0.0"

RUN apt update && \
apt install -y openssh-server

RUN mkdir /var/run/sshd && mkdir /root/.ssh

ADD authorized_keys /root/.ssh/authorized_keys

ADD run.sh /run.sh

RUN chmod +x /run.sh

# 开放端口给外部容器, 需要注意是容器也应该监听该端口,这样才有效
EXPOSE 22

# 启动容器时运行的命令
CMD [ "/run.sh" ]

之后就可以构建image了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 指定名字和标签
# 到指定目录下构建
cd work-ssh-dir
docker build -t testsshd:v1.0 .

# 查看构建的镜像
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
testsshd v1.0 3df9c9a8e932 3 seconds ago 229MB

# 测试镜像
docker run -it -d -p5555:22 --name sshdub2 testsshd:v1.0
# 查看镜像是否成功运行
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
44577bea3ee8 testsshd:v1.0 "/run.sh" About a minute ago Up About a minute 0.0.0.0:8080->22/tcp, :::8080->22/tcp sshdub2

docker exec -it sshdub2 /bin/bash
# 测试ssh连接是否成功
ssh [email protected] -p 8080

Docker web lamp例子

1
2
3
4
5
6
docker pull linode/lamp
docker run -it -d -p 8080:80 -p5555:3306 --name web2 linode/lamp
docker exec -it web2 /bin/bash
# 进入容器启动apache和 mysql服务器
service apache2 start
service mysql start
1
2
go build -o main main.go
GO_MYSQL_USERNAME=example_user GO_MYSQL_PASSWORD=Admin2015 GO_MYSQL_DBNAME=exampleDB GO_MYSQL_TBNAME=exampleDB ./main

使用Dockerfile镜像

docker 基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker images
docker pull ubuntu:latest
docker image inspect ubuntu:latest # 查看镜像的详细信息
docker tag ubuntu:latest myubuntu:latest # 打标签,类似链接作用
docker rmi myubuntu:latest # 删除标签
docker history ubuntu # 查看镜像分层的创建信息
docker search --filter=stars=3000 nginx -- # 搜索image
docker image rm ubuntu
docker rmi ubuntu

# 删除镜像,通过标签或者ID删除镜像文件,如果删除的是一个标签,那么不会影响原来的镜像文件
docker image rm mysql
docker rmi mysql

docker image prune # 清理镜像

创建镜像

  • 基于已有的容器创建
  • 基于本地模版导入
  • 基于Dockerfile创建

基于已有的容器创建

1
2
3
4
5
6
7
8
9
10
11
12
13
docker pull ubuntu
docker run -it ubuntu /bin/bash

root@ac797674282e:/# touch helloworld
root@ac797674282e:/# exit

docker container commit -m "this is a sample" -a "Josexy" ac797674282e test:0.0.1
sha256:c1086c912ae90a31491560deb4a70ac62d689fa3a73b3477e478a4af4f3c48c3

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 0.0.1 c1086c912ae9 21 seconds ago 72.8MB
ubuntu latest 597ce1600cf4 2 weeks ago 72.8MB

基于模版的导入

https://wiki.openvz.org/Download/template/precreated 下载镜像文件

然后本地导入即可

1
2
3
4
wget http://download.openvz.org/template/precreated/centos-7-x86_64-minimal.tar.gz
docker import ./centos-7-x86_64-minimal.tar.gz test:0.0.1
cat centos-7-x86_64-minimal.tar.gz | docker import - test:0.1
docker images

基于Dockerfile构建镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat Dockerfile

FROM debian:stretch-slim
LABEL version="1.0" maintainer="docker user <[email protected]>"
RUN apt-get update && \
apt-get install -y python3 &&\
apt-get clean

docker image build -t python:3.0.3 . # 注意最后是一个Dockerfile所在目录

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
python 3.0.3 ddb30df35e04 20 seconds ago 112MB
test 0.0.1 c1086c912ae9 15 minutes ago 72.8MB
debian stretch-slim 22816b634936 3 days ago 55.3MB
ubuntu latest 597ce1600cf4 2 weeks ago 72.8MB

docker image inspect python:3.0.3

存出和载入镜像

导出
1
2
3
docker image 
docker image save test:0.0.1 -o test.tar
ls -lh test.tar
导入
1
2
docker image load --input=test.tar
docker images

上传镜像

上传需要登陆到Docker Hub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
docker login
cat /home/ubuntu/.docker/config.json
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "XXXXXXXX"
}
}
}

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 0.0.1 c1086c912ae9 27 minutes ago 72.8MB

docker tag test:0.0.1 joxrays/test2:0.0.2 # 注意打标签需要加入 joxrays 用户名

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 0.0.1 c1086c912ae9 27 minutes ago 72.8MB
joxrays/test2 0.0.2 c1086c912ae9 27 minutes ago 72.8MB

# 会将镜像上传到 docker.io/joxrays/test2 下
docker push joxrays/test2:0.0.2 # 注意这里需要push加入 joxrays 用户名下

之后就可以打开网页 https://hub.docker.com/u/joxrays 查看自己上传的所有镜像了


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!