一、Docker基础
1.1、容器是什么?
容器 (英语:Container) 是一种基础工具[1][2]。泛指任何可以用于容纳其它物品的工具,可以部分或完全封闭,被用于容纳、储存、运输物品[3]。物体可以被放置在容器中,而容器则可以保护内容物。
1.2、容器技术是什么?
时下火热的计算机技术之容器技术大行其道,极受追捧,多少人被它迷得神魂颠倒。容器,顾名思义将物品纳入其中与外界隔离开来。举个例子:以往我们开发人员开发软件时,因为客户可能处于世界各地,可能有客户使用Linux的、有使用Mac的、有使用Windows等,我们需要开发对应平台版本,甚至于单单Linux还有SUSE、Debian、Redhat等发行版,就算是CentOS还有6、7、8等版本,不同版本之间程序版本差异可能巨大,这给我们开发带来了巨大的成本。现在有了容器技术,我们大可以这样,不再考虑任何平台,只针对容器开发,只要你操作系统支持容器技术,能够运行容器,这就足够了。因为容器技术是将一个应用及其依赖的环境等打包成一个镜像文件,需要时直接拉取镜像文件即可创建一个容器,只要系统支持容器技术,你就能够运行此镜像,就能够运行该镜像包含的进程!无须关心它是什么系统什么平台!你甚至可以在Ubuntu上运行SUSE、CentOS等操作系统而无需使用类似KVM创建虚拟机安装操作系统如此繁缛!这大大节约了资源消耗!
还有就是容器处于沙箱环境与世隔绝,运行于独立的用户空间,和外部互不干扰,典型的世外桃源,鸡犬相闻,老死不相往来!容器里面出现问题,仅仅影响该容器而不会影响该容器以外的地方。比如说某个运行中的容器感染了病毒计算资源被窃取来挖矿,因为有cgroup的存在,运行容器之时指定了该容器占用资源的量级,因此挖矿代码只能够占满该容器的计算资源而无法干涉多余的资源。
但其实容器技术并不是新鲜事物,其最早出现在2000年FreeBSD上,当时只是类似chroot切根,在2001年Linux上出现Linux vserver,为了支持容器技术,Linux kernel引入了名称空间的概念,封装全局硬件资源并在抽象层上进行切分,这种资源包括6种,如下表所示:
NameSpace/名称空间 | Constant/系统函数 | Isolates/隔离内容 | Kernel Version |
---|---|---|---|
Cgroup | CLONE_NEWCGROUP | Cgroup root directory;资源分配 | |
IPC | CLONE_NEWIPC | 信息量、消息队列、共享内存 | 2.6.19 |
Network | CLONE_NEWNET | 网络设备、网络协议栈、端口等 | 2.6.2.9 |
Mount | CLONE_NEWNS | mount point; | 2.6.2.9 |
PID | CLONE_NEWPID | process ID; | 2.6.2.4 |
User | CLONE_NEWUTS | user,group ID | 2.4.19 |
UTS | CLONE_NEWUSER | hostname,NIS domain name | 3.8 |
我们来解释一下:对于上面内容,理解得先有个前提:一切有为法,如梦幻泡影!所有东西都是模拟出来的!
比如:user:
Linux上只有一个root,但是我们运行容器时却依然使用的是root,这是怎么回事?很简单,创建个普通用户,把主根下的分支的属主属组改为该用户,它不就成了该分支的超级管理员!,至于还叫root,你把它当成alias就行了!group同理!让它永远活在自己的王国当中!
有了上面user的解释,其它的都是同理,模拟就对了!
1.3、Docker是什么?
上面提到容器技术最早可追溯到2000年左右,为什么直至最近几年才焕发第二春???这就得提到Docker Cloud这家公司了。
以往Linux上容器是使用LXC这样的工具,但是很麻烦,因此在很长一段时间内容器技术并没有得到重用。直至docker的出现。docker采用了一种非常精妙的设计,如镜像技术、分层管理机制使得容器的易用性得到极大提升。
-
镜像技术:
- 1个用户空间只运行1个进程,并将该进程及其所依赖的环境打包成一个只读的镜像文件
分层存储机制:
- 镜像文件依赖于一种特殊的分层存储机制,这种机制需要特殊的分层叠加文件系统,Ubuntu上是aufs,Redhat上是overlayfs;每一个容器都是建立在一个只读镜像文件上面,它产生的数据与增删查改能操作仅限于额外建立出来的那一层而不会影响到底层的镜像文件,如此层层叠加,上一层的容器销毁并不会影响到底层。何为分层,如我在registry拉去了一个镜像,是三层结构,但是我本地的镜像已经有了它的下面两层,那么下载时只会下载本地没有的第三层,而不是全部下载,这样就大大节约了资源消耗,这需要本地积累。
我们再看看docker这个单词,码头搬运工的意思,很形象!它就像码头搬运工一样,将需要运输的代码装入容器这个集装箱,HTTP协议通信,运输到目的地交付给客户;客户打开集装箱得到里面的代码程序。
docker也是C/S架构,默认C与S都在同一主机,监听Unix socket接口,通过共享内存交流,因此速度非常快
图片来源docker官方文档,版权所有归docker.com所有
如上图所示!Client发起请求如创建并启动容器,docker daemon会先在本地images repo看下有无对应镜像,有则在image基础上新建一层并启动容器;无则向registry请求远程端的image拉取,docker默认是将registry指向自己的官方仓库;远程端有则通过下载回本地保存后再新建一层并启动容器。我们请求的images其实是仓库名,因为在docker仓库里一个仓库只存放一种镜像的不同版本,默认是最新版,区分不同版本是使用tag,比如mysql:TAG这种格式,官方站点是hub.docker.com
二、Docker基础使用
2.1、安装
如CentOS7
sudo yum install -y docker-ce
2.2、查看docker信息
安装后,先看下docker info,我是Ubuntu 20.4版,在装系统时选择docker后面就可以省却安装步骤。
sanxi@container:~$ sudo docker info #只想看版本信息的话直接sudo docker version就行了,info是最详细的!
[sudo] password for sanxi:
Client:
Debug Mode: false
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 19.03.11
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
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: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc version:
init version: fec3683
Security Options:
apparmor
seccomp
Profile: default
Kernel Version: 5.4.0-40-generic
Operating System: Ubuntu Core 16
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 1.941GiB
Name: container
ID: PZB6:ZC2Z:KXYQ:UBMB:ZYW7:4VIP:W3HF:G74Z:2M2X:XBGS:HVKJ:TZGM
Docker Root Dir: /var/snap/docker/common/var-lib-docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
WARNING: No swap limit support
2.3、镜像加速服务:
因docker镜像服务器托管在国外云服务器上,我们访问可能比较慢,可以使用国内镜像加速服务,比如阿里云就有提供,只是需要个人账号绑定,控制台-容器镜像服务-镜像加速器!
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://XXXXXX.mirror.aliyuncs.com"] #因每个账号都是不一样的
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
这里有个小插曲,因为我是安装Ubuntu 20.04时选择了docker服务,默认是采用snap,因此重启会报错
sanxi@container:~$ sudo systemctl restart dockerd
Failed to restart dockerd.service: Unit dockerd.service not found.
故障处理思路:既然是unit文件找不到,那就先查询unit在不在!
sanxi@container:~$ sudo systemctl list-unit-files | grep docker
snap-docker-471.mount enabled enabled
snap.docker.dockerd.service enabled enabled
看!我系统上默认是snap方式的。。。我还傻傻地systemctl了好几遍,我说怎么就不存在呢。。。然后Google了一下才知道从我这个Ubuntu 20.04版本开始,默认的软件管理工具改为snap
sanxi@container:~$ sudo snap restart docker
Restarted
#这下就正常重启了。。。哈哈,感觉自己好逗。
2.4、docker命令格式与规范
docker命令风格以前是混杂的,现在整合为标准格式了,比如你创建容器可以docker create XXX,新标准格式是docker cotainer create,我们来看看新版本的命令格式,docker -h就可以看到,我的博客仅使用新版规范,不用旧版命令。大多数命令直接在后面跟上-h或者--help就可以查看使用说明了
Management Commands:
builder Manage builds
config Manage Docker configs
container Manage containers
context Manage contexts
engine Manage the docker engine
image Manage images
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
2.5、镜像image基础命令
2.5.1、image拉取:
可以直接create,镜像不存在会自动到registry下载,不需要pull都可以,search是搜索
sanxi@container:~$ sudo docker image pull redis:6.0.5-alpine #tag的使用方式在上面有讲述
6.0.5-alpine: Pulling from library/redis
df20fa9351a1: Pull complete #镜像分层机制的体现,这里显示分了6层:1个pull complete代表一层。
9b8c029ceab5: Pull complete
e983a1eb737a: Pull complete
18cbc0ec1809: Pull complete
a6ec7a0dc6d8: Pull complete
e7cefbc91f72: Pull complete
Digest: sha256:b7561e4e994ab52cb2062b9c3e943ab2af3287caf9d9aeb6719acc77013769a5
Status: Downloaded newer image for redis:6.0.5-alpine
docker.io/library/redis:6.0.5-alpine
2.5.2、显示image列表
sanxi@container:~$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis 6.0.5-alpine b546e82a6d0e 5 weeks ago 31.5MB
2.6、容器基础命令
2.6.1、用image启动
sanxi@container:~$ sudo docker container run -it --name redis -d redis:6.0.5-alpine
[sudo] password for sanxi:
a02278d981ba56353e8dcdbd334d4f42510d537d414cfe4f073d5e3a4993a354
#此处i指的是交互模式,t是tty终端,--name是给容器命名,-d是后台运行,-rm是退出容器后直接删除
2.6.2、容器内执行命令
使用docker命令登录redis容器内部并执行命令
sanxi@container:~$ sudo docker container exec -it redis redis-cli -h 172.17.0.2
172.17.0.2:6379> SET sanxi "666"
OK
172.17.0.2:6379> GET sanxi
"666"
172.17.0.2:6379> exit
#啊!多么熟悉的界面!下面是exec的用法,值得注意的是CONTAINER后面一定要跟上COMMAND,不可省略,如上
2.6.3、非登录式执行
如果不想登录容器,只想在运行容器里的一个命令,可以这样
sanxi@container:~$ sudo docker container exec redis netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN
tcp 0 0 :::6379 :::* LISTEN
#exec指定容器后,直接跟上命令就行,前提是容器里的进程支持。
2.6.4、停止容器
sanxi@container:~$ sudo docker container stop redis
redis
sanxi@container:~$ sudo docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
#stop后自动销毁容器,也可以使用kill,区别是stop是赐死,自己上吊,是主动死;kill就是皇帝下令直接满门抄斩,被动死,没得反抗。
2.6.5、查看容器状态
sanxi@container:~$ sudo docker ps --all
[sudo] password for sanxi:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a02278d981ba redis:6.0.5-alpine "docker-entrypoint.s…" 19 hours ago Exited (0) 18 hours ago redis
4c38d2da1f40 redis:6.0.5-alpine "docker-entrypoint.s…" 19 hours ago Exited (0) 19 hours ago crazy_galileo
7c755aa0f098 redis:6.0.5-alpine "docker-entrypoint.s…" 19 hours ago Exited (0) 19 hours ago
2.6.6、彻底删除容器
sanxi@container:~$ sudo docker container rm redis crazy_galileo hopeful_antonelli
redis
crazy_galileo
hopeful_antonelli
sanxi@container:~$ sudo docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2.6.7、查看容器日志:
容器中因为只运行一个程序,因此log是直接发送往控制台而不是保存在某个文件中
sanxi@container:~$ sudo docker container logs redis
1:C 18 Jul 2020 06:47:40.979 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 18 Jul 2020 06:47:40.979 # Redis version=6.0.5, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 18 Jul 2020 06:47:40.980 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 18 Jul 2020 06:47:40.980 * Increased maximum number of open files to 10032 (it was originally set to 1024).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.0.5 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 1
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
1:M 18 Jul 2020 06:47:40.981 # Server initialized
1:M 18 Jul 2020 06:47:40.981 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 18 Jul 2020 06:47:40.981 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 18 Jul 2020 06:47:40.981 * Ready to accept connections
1:M 18 Jul 2020 07:47:41.006 * 1 changes in 3600 seconds. Saving...
1:M 18 Jul 2020 07:47:41.006 * Background saving started by pid 50
50:C 18 Jul 2020 07:47:41.007 * DB saved on disk
50:C 18 Jul 2020 07:47:41.007 * RDB: 0 MB of memory used by copy-on-write
1:M 18 Jul 2020 07:47:41.106 * Background saving terminated with success
2.6.8、查看资源占用
查看当前所有容器占用资源状态信息
sanxi@container:~$ sudo docker container stats
#这个会跟top命令一样占据前台而且没有q退出,只能CTRL+C停止
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
123036b766f1 redis 0.07% 3.344MiB / 1.941GiB 0.17% 1.37kB / 0B 0B / 61.4kB 5
2.6.9、查看进程信息
查看容器内运行的进程及其子进程的相关信息
sanxi@container:~$ sudo docker container top redis
UID PID PPID C STIME TTY TIME CMD
systemd+ 189259 189241 0 06:47 ? 00:00:03 redis-server
当创建容器时忘了赋予-it形式时,可用attach命令
2.7、镜像制作与上传
2.7.1、制作镜像
sanxi@container:~$ sudo docker container commit -a sanxi -p redis sanxi666/redis:v0.1
sha256:9e2b722774031f13fe026e98d75e62f5bdcf369c53904b328dde31750371c496
sanxi@container:~$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
sanxi666/redis v0.1 9e2b72277403 23 seconds ago 31.5MB
redis 6.0.5-alpine b546e82a6d0e 5 weeks ago 31.5MB
#这里-a是指明作者,-p是暂停容器,因为不暂停的话数据会不同步
2.7.2、验证
验证新制作的镜像是否有保存数据
sanxi@container:~$ sudo docker container exec -it redistest redis-cli
[sudo] password for sanxi:
127.0.0.1:6379> GET sanxi
(nil)
为什么没有数据???因为redis是基于缓存机制,没有保存下来到硬盘当然就没有数据了,哈哈哈!
2.7.3、登录docker hub
如何上传制作的镜像到registry?登录docker hub,前提是你先在hub.docker.com注册好账号!
sanxi@container:~$ sudo docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: sanxi666
Password:
WARNING! Your password will be stored unencrypted in /root/snap/docker/471/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
2.7.4、push镜像
sanxi@container:~$ sudo docker image push sanxi666/redis:v0.1
The push refers to repository [docker.io/sanxi666/redis]
e00efe894614: Pushed
54d572db5c56: Mounted from library/redis
48c3f965247a: Mounted from library/redis
2615d778af32: Mounted from library/redis
08563e05d6f1: Mounted from library/redis
655693698129: Mounted from library/redis
50644c29ef5a: Mounted from library/redis
v0.1: digest: sha256:4fe1138f936fd396926ec1165072ddeffe54bb67e788a61aadbe4adc2eb3e317 size: 1777
#值得注意的是,你在打包镜像时给定的目录一定要与docker hub的一致,比如我之前打包和hub的目录都是sanxi666/redis
2.7.5、验证结果
去docker hub网站查看是否成功