目录
? ? ? ? 根据作者在项目实践中的总结,将开发运维中常用的Docker操作进行总结,涵盖Docker环境搭建、常用命令总结以及Docker镜像制作发布等,并给出较为通用的Dockerfile模板,目的是整合常见用法,提供一篇工具性文章,满足日常开发运维之需求。
????????各大社区里有很多文章对Docker进行了介绍,包括其发展历程、概念、原理,在这里就不赘述。计算机领域的容器container简单来讲就是一个个相对独立运行的“沙箱”(或者准确点说属于独立命名空间下面的进程),彼此不暴露接口,Docker是容器技术的典型代表。与传统的虚拟机不同,Docker容器是轻量级、操作系统层面共享的技术方案,具有启动快、占用资源少的特点;Docker容器拥有自己独立的运行环境,可以独立部署应用服务所需的库以及配置项。
? ? ? ? 在硬件驱动、操作系统版本兼容的情况下,Docker基本能实现“build once, run anywhere”!
1、随着云计算、分布式成为主流,Docker容器在服务部署中必不可少,结合k8s等容器编排技术,服务提供者很容易实现对资源的弹性伸缩(Auto Scaling)并实现服务监控,应用上“云”成为一种时尚。
2、Nvidia Docker 是“炼丹侠”们的福音,它使得Docker容器可以使用宿主机的Nvidia GPU资源加速AI模型训练;将对运行环境有特殊要求的模型训练部署到容器中是一种有效解决方案,能提高宿主机GPU资源的利用率,同时也避免了重复搭建运行环境的烦恼(在实践中,深度学习框架和Cuda版本的兼容以及各种库的安装是费时费力的)。
以Ubuntu环境为例,给出Docker安装脚本:
sudo apt-get remove docker docker-engine docker.io
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce
若需使用Nvidia GPU加速,需要安装nvidia-docker:
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
sudo apt-get install -y nvidia-docker2
sudo pkill -SIGHUP dockerd
# 注:此时需要使用nvidia-docker命令,而不是docker命令
# 启动Docker环境
systemctl start docker
# 查看docker版本
docker version
# 拉取镜像 e.g. docker pull amazing6312/ubuntu16:py36cu100
docker pull [dockerhub账号名/]镜像名称:标签
# 查看本地的镜像
docker image
# 查看本地正在运行的容器
docker ps
# 查看本地所有容器
docker ps -a
# 打开并进入一个特定容器
docker start 容器名称或ID
docker attach 容器名称或ID
# 重启容器
docker restart 容器名称或ID
# 强制停止容器
docker kill 容器名称或ID
# 根据指定的镜像创建一个新容器,并以守护进程模式运行 -d表示后台运行不能终端操作
docker run -d --name 容器名称 镜像名称:标签
# 以下:前者为本地需要映射的文件夹,后者为虚拟文件夹,-it表示交互式操作
docker run --name test_container -it -v /home/xxx/src:/home/dst a7a1861d2150 /bin/bash
# 停止容器
docker stop 容器ID或容器名称
# 删除指定容器
docker rm 容器ID或容器名称
# 删除所有容器
# 1.首先需要停止所有的容器(这一步不用貌似也可以)
docker stop $(docker ps -a -q)
# 2.删除所有的容器(只删除单个时把后面的变量改为容器 id即可)
docker rm $(docker ps -a -q)
# 删除指定id镜像
docker rmi <image id>
# 删除所有镜像(注:若删除的镜像有对应容器在使用,要先删除容器)
docker rmi $(docker images -q)
# 查看容器日志
docker logs 容器ID或容器名称
docker logs -f -t --since --tail 容器ID或容器名称
如:docker logs -f -t --since=”2019-08-01” --tail=10 g7e25e8455v5
-f : 查看实时日志
-t : 查看日志产生的日期
--since : 输出指定日期之后的日志
--tail=10 : 查看最后的10条日志
# 进入容器,在运行中的Docker容器中执行命令
docker [container] exec -it 容器ID或容器名称 shell命令
# 导出容器,将指定容器导出为tar文件
docker export 容器ID或容器名称 > 文件名.tar
# 将docker镜像保存为tar文件
docker save -o <输出文件名> <镜像名称>:<标签>
# 将由docker save命令生成的tar文件加载到Docker中
docker load -i <输入文件名>
# 导入镜像,从tar中导入镜像
docker import 文件名.tar 镜像名称:标签
# 查看容器详情
docker inspect 容器ID或容器名称
# 查看容器资源
docker stats 容器ID或容器名称
#登录docker账号
docker login
# 打包镜像,将正在运行的容器打包为镜像
# -a, --author:指定新镜像的作者信息。
#-m, --message:指定新镜像的描述信息。
#-p, --pause:在创建镜像之前暂停容器的运行。
#-t, --tag:指定新镜像的标签。
docker commit -a "John Doe" -m "My custom image" my_container_ID(or name) my_image:latest
# 打标签,给镜像重命名便于记忆
#-f, --force:强制覆盖已存在的标签。
# --no-tag:不创建新的标签,只返回镜像ID
# 为名为my_image:latest的镜像添加标签my_image:v1.0,并指定远程仓库地址
docker tag my_image:latest my-registry/my_image:v1.0
# 推送镜像到远程仓库
docker push my-registry/my_image:v1.0
# 显示帮助信息
docker -h
# 读取Dockerfile来创建镜像
docker build -t <镜像名称> <Dockerfile所在路径>
# 查看容器内运行的进程
docker top 容器ID或容器名称
? ? ? ? 镜像image是Docker的一个只读模板包含容器运行的配置信息和相关软件,形象说可理解为安装包,但这种说法不严谨;而容器container则是真正运行的示例,一般通过image来创建container,Docker会为一个运行的container实例创建一个副本,在该副本上的更改不影响image和其他container。
? ? ? ? 制作docker镜像通常有以下四种方式,前两种是比较常用的:
1、利用现有容器,进行修改后,使用docker commit命令制作镜像;
2、编写Dockerfile,使用docker build命令制作镜像;
3、通过docker save和docker load命令构建;
4、通过docker export和docker import命令构建
#登录docker账号
docker login
# 打包镜像,将正在运行的容器打包为镜像
# -a, --author:指定新镜像的作者信息。
#-m, --message:指定新镜像的描述信息。
#-p, --pause:在创建镜像之前暂停容器的运行。
#-t, --tag:指定新镜像的标签。
docker commit -a "John Doe" -m "My custom image" my_container_ID(or name) my_image:latest
# 打标签,给镜像重命名便于记忆
#-f, --force:强制覆盖已存在的标签。
# --no-tag:不创建新的标签,只返回镜像ID
# 为名为my_image:latest的镜像添加标签my_image:v1.0,并指定远程仓库地址
docker tag my_image:latest my-registry/my_image:v1.0
# 推送镜像到远程仓库
docker push my-registry/my_image:v1.0
# 基础镜像地址示例,nginx为镜像名,stable-perl为版本标签
FROM hub.docker.com/layers/library/nginx:stable-perl
# 维护者信息
MAINTAINER authorInformation
# 变量,表示jar包名,此时该jar应位于Dockfile相同路径,或者根据实际修改路径名
ARG JAR_FILE='*.jar'
RUN echo ${JAR_FILE}
# 将名为JAR_FILE的jar包存到容器内特定路径下,并重命名为server.jar
ADD ${JAR_FILE} /app/server/server.jar
# JVM参数,根据实际修改
ARG DEAULT_OPTS='-Xms3072m -Xmx3072m -Xmn2048m -Xss1m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=3 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapDump.hprof -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+PrintGCCause -Xloggc:./gc.log'
ENV JAVA_OPTS=$DEAULT_OPTS
# 环境变量根据实际增删改
# 环境变量,服务端口号
ENV SERVER_PORT=80
EXPOSE 80
# 设置容器启动时,默认执行的命令,这里是启动server.jar对应的服务
ENTRYPOINT ["sh","-c","java -jar ${JAVA_OPTS} -Dfile.encoding=UTF-8 -Duser.timezone=GMT+08 -Djava.security.egd=file:/dev/./urandom /app/server/server.jar"]
# docker build -t <镜像名称> -f <Dockerfile所在路径> .
docker build -t my-registry/my_image:v1.0 -f ./Dockerfile .
# 推送镜像到远程仓库
docker push my-registry/my_image:v1.0
# 以 -it 交互式为例进行说明
# -p 端口映射
# -v 路径映射,不做这一步,一些需要保存的文件在容器重启之后就删除了
# -e 环境变量
# --name 容器实例名 镜像地址
docker run -it -p 宿主机端口:容器端口 \
-v 宿主机路径1:容器路径1 \
-v /home/.../postgres/14/lib:/var/lib/postgresql \
-v /home/.../postgres/14/etc/postgres:/etc/postgresql \
-e ENV_XXXX=xxx \
--name my-container my-registry/my_image:v1.0 /bin/bash
# 进入容器内部
docker attach my-container
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7d336a153aad my-registry/my_image:v1.0 "docker-entrypoint.s…" 9 days ago Up 9 days 0.0.0.0:25432->5432/tcp, :::25432->5432/tcp my-container
docker inspect my-container
[
{
"Id": "7d336a153aadcb9bb2e3196a29f9n06d7ed8bbeccu8fae6911429db2exd96a76",
"Created": "2024-01-08T02:28:33.743053899Z",
"Path": "docker-entrypoint.sh",
"Args": [
"/bin/bash"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 101240,
"ExitCode": 0,
"Error": "",
"StartedAt": "2024-01-08T08:10:53.58342763Z",
"FinishedAt": "2024-01-08T08:09:21.405168602Z"
},
"Image": "sha256:b2261d3c6ce0b63be632e7567a92756b780de73d802550b1275baa0997aa34d0",
"ResolvConfPath": "/app/docker/containers/7d336a153aadcb9bb2e3194a29f9d06d7ed8bbeccf8fae6811429db2e7d96a76/resolv.conf",
"HostnamePath": "/app/docker/containers/7d336a153aadcb9bb2e3194a29f9d06d7ed8bbeccf8fae6811429db2e7d96a76/hostname",
"HostsPath": "/app/docker/containers/7d336a153aadcb9bb2e3194a29f9d06d7ed8bbeccf8fae6811429db2e7d96a76/hosts",
"LogPath": "/app/docker/containers/7d336a153aadcb9bb2e3194a29f9d06d7ed8bbeccf8fae6811429db2e7d96a76/7d336a153aadcb9bb2e3194a29f9d06d7ed8bbeccf8fae6811429db2e7d96a76-json.log",
"Name": "/my-container",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "docker-default",
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/home/.../postgres/14/log:/var/log/postgresql",
"/home/.../postgres/14/lib:/var/lib/postgresql",
"/home/.../postgres/14/etc/postgres:/etc/postgresql",
"/home/.../postgres/14/etc/postgres-common:/etc/postgresql-common"
],
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {
"5432/tcp": [
{
"HostIp": "",
"HostPort": "25432"
}
]
},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "private",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": null,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/app/docker/overlay2/b0af88235063e4165f26a0c1ce4f42afaa130072f67c55b66af8dcdfbdd31281-init/diff:/app/docker/overlay2/255f1256262a2cd96b71f4dbec4e3577be1e728b72aea5acb4e233c9e3cff290/diff:/app/docker/overlay2/f04df3e252be53274aa2ae90c247e15d81967649978ce971c8954e77b18524f3/diff:/app/docker/overlay2/33aa1a77a0357f804cee6d79deedeb6da01feb44e74ae389666f94a597b7df12/diff:/app/docker/overlay2/ac2d31f52d8b7ecfaa2fdf92bafce2c163de347cdcdfe15e1bda1397dcda89ef/diff:/app/docker/overlay2/880d8be6cf60ddf24096d1ea64ad59e2def810a2476514f3488bcd4ded3ebca9/diff:/app/docker/overlay2/c3ae26797238833cc1580085d68ead9288d5b817fa7a4e9db860eb7b859fcd3a/diff:/app/docker/overlay2/d98f41864778f055d955232ef7b6b98c3b617d1e0c946a2b0c7e96ce55a95350/diff:/app/docker/overlay2/0d7afc847c62266b0a7e5e3da977d80e37d23e383a66d53ee29d08acaef0b8ae/diff:/app/docker/overlay2/a5724c079c2e6c3b1249422c9e01dd58ed368628f18048007804665057a92a63/diff:/app/docker/overlay2/ee1985012103955efb9eff68eb0ac2e984e25a8c503e4e57652700484346a2c1/diff:/app/docker/overlay2/d58b8e59de6250fef61d38f76fb2cb2f05fd0b47b7dbb4fdeb0134d25f7176fb/diff:/app/docker/overlay2/caf3bfa06daed6fc9d5f5a175f597562e81568142c811fa92f0dfe67bc07d8e6/diff:/app/docker/overlay2/049b0b56c98e8725d283e6942a315b4447c7177450e5b4d9b6e87d936f506670/diff",
"MergedDir": "/app/docker/overlay2/b0af88235063e4165f26a0c1ce4f42afaa130072f67c55b66af8dcdfbdd31281/merged",
"UpperDir": "/app/docker/overlay2/b0af88235063e4165f26a0c1ce4f42afaa130072f67c55b66af8dcdfbdd31281/diff",
"WorkDir": "/app/docker/overlay2/b0af88235063e4165f26a0c1ce4f42afaa130072f67c55b66af8dcdfbdd31281/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "bind",
"Source": "/home/.../log",
"Destination": "/var/log/postgresql",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "/home/.../postgres/14/lib",
"Destination": "/var/lib/postgresql",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "/home/.../postgres/14/etc/postgres",
"Destination": "/etc/postgresql",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "/home/../postgres/14/etc/postgres-common",
"Destination": "/etc/postgresql-common",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "volume",
"Name": "9498012c0f1d1d94e1cc5223e353c1a2dc50da84894292f9c534d551e6abd67a",
"Source": "/app/docker/volumes/9498012c0f1d1d94e1cc5223e353c1a2dc50da84894292f9c534d551e6abd67a/_data",
"Destination": "/var/lib/postgresql/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
"Config": {
"Hostname": "7d336a153aad",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"5432/tcp": {}
},
"Tty": true,
"OpenStdin": true,
"StdinOnce": true,
"Env": [
"POSTGRES_PASSWORD=1234",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/14/bin",
"GOSU_VERSION=1.14",
"LANG=en_US.utf8",
"PG_MAJOR=14",
"PG_VERSION=14.4-1.pgdg110+1",
"PGDATA=/var/lib/postgresql/data"
],
"Cmd": [
"/bin/bash"
],
"Image": ".../postgresql/postgres:14.4",
"Volumes": {
"/var/lib/postgresql/data": {}
},
"WorkingDir": "",
"Entrypoint": [
"docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {},
"StopSignal": "SIGINT"
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "8fddc7536745f974a41f62b4f0b92a1d01cnfecc331fe5af8de9083147aa5776",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"5432/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "25432"
},
{
"HostIp": "::",
"HostPort": "25432"
}
]
},
"SandboxKey": "/var/run/docker/netns/8fddc7536745",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "6bae42855b93c2056965yh81400af385146dc11fcdc0c3891379ff46095e124a",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:47:hc:11:00:03",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "3fae791fb6ac65575eb351rf8d1ee0721ed2409ecfda74d35e468eb9aecc7879",
"EndpointID": "6bae42855b93c2056965yh81400af385146dc11fcdc0c3891379ff46095e124a",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:47:hc:11:00:03",
"DriverOpts": null
}
}
}
}
]
1、同一宿主机上的两个独立容器之间的通信不能使用宿主机IP:映射PORT,而应该使用容器内部的实际IP:PORT(docker inspect查看运行中的容器IP、PORT信息);
?
声明:本文内容是作者个人基于实践的理解,如有错误请指正;本文部分内容或图片参考互联网,如有侵权请联系删除,谢谢!