目录
写在前面:
毕设完成初稿的空闲期,学一点技术和理论知识吧~
今天来看一看docker,之前一直出现在github里的readme中,却一直未认真学习过。
正文开始:
docker百度翻译是码头工人,也就是勤勤恳恳的互联网搬砖工具(像我一样简直泰裤辣)。技术名词是容器,在我的印象中其就是一个打包令人发指的环境的工具,没错容器就是一个屏蔽环境差异的工具,也就是说,只要程序打包到docker中,那么无论运行在什么环境下程序的行为都是一致的,程序员再也无法施展表演才华,不会再有“在我的环境上可以运行呀!(好像是我的口头禅hhh)”,真正实现“build once, run everywhere”。
docker的一个好处是快速部署。
docker中有这样几个概念:
- dockerfile
- image
- container
可以简单理解image为可执行程序,container是运行起来的进程,dockerfile是image的源代码,docker就是编译器。因此我们只需要在dockerfile中指定需要哪些程序、依赖什么样的配置,之后把dockerfile交给“编译器”docker进行“编译”,也就是docker build命令,生成的可执行程序就是image,之后就可以运行这个image了,这就是docker run命令,image运行起来后就是docker container。
docker使用方法
Docker Hello World
hetao@ubuntu-server:~$ docker run ubuntu:15.10 /bin/echo "Hello world"
Hello world
第一次运行docker命令就出现了错误
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/json": dial unix /var/run/docker.sock: connect: permission denied
这是因为docker进程使用Unix Socket端口。而默认情况下,Unix Socket属于root用户,需要root权限才能访问。
解决方法:
docker守护进程启动的时候,会默认赋予名字为docker的用户组读写Unix socket的权限,因此只要创建docker用户组,并将当前用户加入到docker用户组中,那么当前用户就有权限访问Unix socket了,进而也就可以执行docker相关命令。
sudo groupadd docker #添加docker用户组
sudo gpasswd -a $USER docker #将登陆用户加入到docker用户组中
newgrp docker #更新用户组
docker ps #测试docker命令是否可以使用sudo正常使用
添加完之后记得注销重新登录才可以使用docker!
Docker列出所有镜像
hetao@ubuntu-server:~$ docker image ls
输出内容包括:
REPOSITORY TAG IMAGE ID CREATED SIZE
上述代码里用到的ubuntu:15.10表示REPOSITORY:TAG
回到前面的代码段
hetao@ubuntu-server:~$ docker run ubuntu:15.10 /bin/echo "Hello world"
Hello world
各个参数解析:
以上命令完整的意思可以解释为:Docker 以 ubuntu15.10 镜像创建一个新容器,然后在容器里执行 bin/echo “Hello world”,然后输出结果。
运行交互式的容器
通过 docker 的两个参数 -i -t,让 docker 运行的容器实现"对话"的能力:
hetao@ubuntu-server:~$ docker run -i -t ubuntu:15.10 /bin/bash
各个参数解析:
注意到root@f6abab97a1a0
说明已经进入一个ubuntu15.10系统的容器,
CTRL+D或exit可退出容器。
启动容器(后台模式)
使用以下命令创建一个以进程方式运行的容器
hetao@ubuntu-server:~$ docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
4483fbb8f303851a6c6189724187fa75d7c792c2d0f1933ed13f3e8b096c78ff
在输出中,我们没有看到期望的 “hello world”,而是一串长字符
4483fbb8f303851a6c6189724187fa75d7c792c2d0f1933ed13f3e8b096c78ff
这个长字符串叫做容器 ID,对每个容器来说都是唯一的,我们可以通过容器 ID 来查看对应的容器发生了什么。
首先,我们需要确认容器有在运行,可以通过docker ps来查看:
hetao@ubuntu-server:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4483fbb8f303 ubuntu:15.10 "/bin/sh -c 'while t…" 29 seconds ago Up 28 seconds bold_feistel
输出详情介绍:
在宿主主机内使用docker logs
命令,查看容器内标准输出:
hetao@ubuntu-server:~$ docker logs 4483fbb8f303
也可以使用
hetao@ubuntu-server:~$ docker logs bold_feistel
停止容器
使用docker stop
命令停止容器。
通过docker ps
查看容器已经停止工作。
Docker容器使用
Docker使用
获取镜像
如果本地没有ubuntu镜像,我们可以使用docker pull
命令载入ubuntu镜像。
启动容器
查看所有容器的命令为docker ps -a
使用docker start
启动一个已经停止的容器:
hetao@ubuntu-server:~$ docker start 4483fbb8f303
4483fbb8f303
后台运行
在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的运行模式。
hetao@ubuntu-server:~$ docker run -itd --name ubuntu-test ubuntu /bin/bash
c1cea3f635ca37bad2dfbd79af5a396b3711ae55f5657014288b8820cb9894d0
进入容器
在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:
attach 命令
可以看到attach进入后exit会导致容器停止。
exec 命令
可以看到exec进入后exit不会导致容器停止。
导出和导入容器
导出容器
如果要导出本地某个容器,可以使用docker export
命令。
hetao@ubuntu-server:~$ docker export c1cea3f635ca > unbuntu-test.tar
导入容器快照
可以使用 docker import 从容器快照文件中再导入为镜像,以下实例将快照文件 unbuntu.tar 导入到镜像 test/ubuntu:v1
hetao@ubuntu-server:~$ cat unbuntu-test.tar | docker import - test/ubuntu:v1
sha256:e62202c2a6343bcc8c3adaf07f2c1242ccd97c6334c839368fd89be75c9e32c2
此外,也可以通过指定 URL 或者某个目录来导入,例如:
docker import http://example.com/exampleimage.tgz example/imagerepo
删除容器
hetao@ubuntu-server:~$ docker rm -f c1cea3f635ca
c1cea3f635ca
下面的命令可以清理掉所有处于终止状态的容器。
docker container prune
运行一个web应用
前面我们运行的容器并没有一些什么特别的用处。
接下来让我们尝试使用 docker 构建一个 web 应用程序。
我们将在docker容器中运行一个 Python Flask 应用来运行一个web应用。
hetao@ubuntu-server:~$ docker pull training/webapp
hetao@ubuntu-server:~$ docker run -d -P training/webapp python app.py
参数说明:
查看web应用容器
使用 docker ps
来查看我们正在运行的容器:
这里面多了一个端口信息。
docker开放了5000端口(默认python flask端口)映射到主机端口32768上。这时我们可以通过浏览器访问web应用。
hetao@ubuntu-server:~$ 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:b8ff:fe0d:dcc9 prefixlen 64 scopeid 0x20<link>
ether 02:42:b8:0d:dc:c9 txqueuelen 0 (以太网)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 29 bytes 3953 (3.9 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eno1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether ac:1f:6b:f8:45:48 txqueuelen 1000 (以太网)
RX packets 455790 bytes 155867904 (155.8 MB)
RX errors 0 dropped 82 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eno2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::c12a:8cbc:d150:5b72 prefixlen 64 scopeid 0x20<link>
ether ac:1f:6b:f8:45:49 txqueuelen 1000 (以太网)
RX packets 1527242321 bytes 1414677853599 (1.4 TB)
RX errors 0 dropped 879953 overruns 0 frame 0
TX packets 1501449340 bytes 1186573294020 (1.1 TB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (本地环回)
RX packets 36731960 bytes 9287047803 (9.2 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 36731960 bytes 9287047803 (9.2 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1492
inet 10.69.47.84 netmask 255.255.255.255 destination 10.68.0.1
inet6 fe80::35d8:b88:c33d:44d2 prefixlen 10 scopeid 0x20<link>
ppp txqueuelen 3 (点对点协议)
RX packets 5476184 bytes 652310617 (652.3 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 9293471 bytes 3731330667 (3.7 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vethe2fd92b: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::b863:5dff:fe8d:6575 prefixlen 64 scopeid 0x20<link>
ether ba:63:5d:8d:65:75 txqueuelen 0 (以太网)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 29 bytes 4228 (4.2 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
我们也可以通过 -p 参数来设置不一样的端口:
hetao@ubuntu-server:~$ docker run -d -p 5000:5000 training/webapp python app.py
f8debcc9cc39d05b1ddca3c7fd06db395a1899b9e4083a3346112513405c2bfd
则容器内部的5000端口映射到本地主机的5000端口上。
网络端口的快捷方式
通过 docker ps 命令可以查看到容器的端口映射,docker 还提供了另一个快捷方式 docker port,使用 docker port 可以查看指定 (ID 或者名字)容器的某个确定端口映射到宿主机的端口号。
hetao@ubuntu-server:~$ docker port dreamy_satoshi
5000/tcp -> 0.0.0.0:5000
查看web应用程序日志
docker logs [ID或者名字] 可以查看容器内部的标准输出。
-f: 让 docker logs 像使用 tail -f 一样来输出容器内部的标准输出。
从上面,我们可以看到应用程序使用的是 5000 端口并且能够查看到应用程序的访问日志。
hetao@ubuntu-server:~$ docker logs -f dreamy_satoshi
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
10.127.177.4 - - [01/Jun/2023 11:47:20] "GET / HTTP/1.1" 200 -
10.127.177.4 - - [01/Jun/2023 11:47:20] "GET /favicon.ico HTTP/1.1" 404 -
查看web应用程序容器的进程
我们还可以使用 docker top 来查看容器内部运行的进程
hetao@ubuntu-server:~$ docker top dreamy_satoshi
UID PID PPID C STIME TTY TIME CMD
root 1574545 1574523 0 19:47 ? 00:00:00 python app.py
检查web应用程序
使用 docker inspect 来查看 Docker 的底层信息。它会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息。
Docker镜像使用
当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载。
列出镜像列表
docker images
列出本地主机的镜像。
同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本,如 ubuntu 仓库源里,有 15.10、14.04 等多个不同的版本,我们使用 REPOSITORY:TAG 来定义不同的镜像。
如果你不指定一个镜像的版本标签,例如你只使用 ubuntu,docker 将默认使用 ubuntu:latest 镜像。
获取一个新的镜像
docker pull
命令下载不存在的镜像。
查找镜像
我们可以从 Docker Hub 网站来搜索镜像,Docker Hub 网址为: docker hub。我们也可以使用 docker search 命令来搜索镜像。比如我们需要一个 httpd 的镜像来作为我们的 web 服务。我们可以通过 docker search 命令搜索 httpd 来寻找适合我们的镜像。
hetao@ubuntu-server:~$ docker search httpd
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
httpd The Apache HTTP Server Project 4439 [OK]
centos/httpd-24-centos7 Platform for running Apache httpd 2.4 or bui… 45
centos/httpd 36 [OK]
clearlinux/httpd httpd HyperText Transfer Protocol (HTTP) ser… 5
hypoport/httpd-cgi httpd-cgi 2 [OK]
solsson/httpd-openidc mod_auth_openidc on official httpd image, ve… 2 [OK]
publici/httpd httpd:latest 1 [OK]
dockerpinata/httpd 1
manageiq/httpd Container with httpd, built on CentOS for Ma… 1 [OK]
dockette/apache Apache / HTTPD 1 [OK]
centos/httpd-24-centos8 1
dockette/httpdump 0
betterweb/httpd 0
avenga/httpd-static 0
19022021/httpd-connection_test This httpd image will test the connectivity … 0
httpdocker/kubia 0
openquantumsafe/httpd Demo of post-quantum cryptography in Apache … 0
jitesoft/httpd Apache httpd on Alpine linux. 0
e2eteam/httpd 0
manasip/httpd 0
httpdss/archerysec ArcherySec repository 0 [OK]
vulhub/httpd 0
patrickha/httpd-err 0
paketobuildpacks/httpd 0
manageiq/httpd_configmap_generator Httpd Configmap Generator 0 [OK]
删除镜像
镜像删除使用 docker rmi 命令
创建镜像
当我们从 docker 镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对镜像进行更改。
更新镜像
更新镜像之前,我们需要使用镜像来创建一个容器。
hetao@ubuntu-server:~$ docker run -t -i ubuntu:15.10 /bin/bash
root@69dd7fd6306b:/# apt-get update
在运行的容器内使用 apt-get update 命令进行更新。
在完成操作之后,输入 exit 命令来退出这个容器。
此时ID为69dd7fd6306b的容器是按我们需求更改的容器。我们可以通过命令docker commit
来提交容器副本。
hetao@ubuntu-server:~$ docker commit -m="has update" -a="hetao" 69dd7fd6306b hetao/ubuntu:v2
sha256:a488e76bbbbe7cce5b1841eecf45afe9837b5b2929679fd6b142b69e3b64967f
参数说明:
构建镜像
我们使用命令docker build
, 从零开始来创建一个新的镜像。为此,我们需要创建一个 Dockerfile 文件,其中包含一组指令来告诉 Docker 如何构建我们的镜像。
参考文献:
知乎:什么是Docker?看这一篇干货文章就够了!
docker:菜鸟教程