毕设完成初稿的空闲期,学一点技术和理论知识吧~
今天来看一看docker,之前一直出现在github里的readme中,却一直未认真学习过。
docker百度翻译是码头工人,也就是勤勤恳恳的互联网搬砖工具(像我一样简直泰裤辣)。技术名词是容器,在我的印象中其就是一个打包令人发指的环境的工具,没错容器就是一个屏蔽环境差异的工具,也就是说,只要程序打包到docker中,那么无论运行在什么环境下程序的行为都是一致的,程序员再也无法施展表演才华,不会再有“在我的环境上可以运行呀!(好像是我的口头禅hhh)”,真正实现“build once, run everywhere”。
docker的一个好处是快速部署。
docker中有这样几个概念:
可以简单理解image为可执行程序,container是运行起来的进程,dockerfile是image的源代码,docker就是编译器。因此我们只需要在dockerfile中指定需要哪些程序、依赖什么样的配置,之后把dockerfile交给“编译器”docker进行“编译”,也就是docker build命令,生成的可执行程序就是image,之后就可以运行这个image了,这就是docker run命令,image运行起来后就是docker container。
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!
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
查看容器已经停止工作。
获取镜像
如果本地没有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
前面我们运行的容器并没有一些什么特别的用处。
接下来让我们尝试使用 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
使用 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
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
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 -
我们还可以使用 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
使用 docker inspect 来查看 Docker 的底层信息。它会返回一个 JSON 文件记录着 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:菜鸟教程