Docker 学习笔记
Docker
vm: 创建操作系统实例
docker: 创建软件容器
无论是基于翻译和模拟的全虚拟化技术、半虚拟化技术,还是有了CPU硬件加持下的全虚拟化技术,其虚拟化的目标都是一台完整的计算机,拥有底层的物理硬件、操作系统和应用程序执行的完整环境。
为了让虚拟机中的程序实现像在真实物理机器上运行“近似”的效果,背后的HyperVisor做了大量的工作,付出了“沉重”的代价。
虽然 HyperVisor 做了这么多,但你有没有问过虚拟机中的程序,这是它想要的吗?或许 HyperVisor 给的太多,而目标程序却说了一句:你其实可以不用这样辛苦。
确实存在这样的情况,虚拟机中的程序说:我只是想要一个单独的执行执行环境,不需要你费那么大劲去虚拟出一个完整的计算机来。虚拟出一台计算机的成本高还是只虚拟出一个隔离的程序运行环境的成本高?答案很明显是前者。一台物理机可能同时虚拟出10台虚拟机就已经开始感到乏力了,但同时虚拟出100个虚拟的执行环境却还是能够从容应对,这对于资源的充分利用可是有巨大的好处。
近几年大火的容器技术正是在这样的指导思想下诞生的。
不同于虚拟化技术要完整虚拟化一台计算机,容器技术更像是操作系统层面的虚拟化,它只需要虚拟出一个操作系统环境。
install
# 从软件仓库安装 docker (低版本)
# 1. 从软件仓库安装 docker
yum install -y docker
# 2. 启动 docker 服务
systemctl start docker
# 3. 为 docker 设置开启自启
systemctl enable docker
# Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
# 4. 查看 docker 服务详情
docker info
# 从官方仓库安装 docker (最新版)
# 1. 移除已安装的 docker 相关包
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 2. 删除残留文件
rm -rf /var/lib/docker/
# 3. 配置 docker 仓库
yum install -y yum-utils
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 4. 安装 docker
yum install docker-ce docker-ce-cli containerd.io
# 5. 启动 docker
systemctl start docker
uninstall
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
dir
/var/lib/docker/
和 /var/lib/containerd
包含镜像、容器、卷、网络等内容, 卸载后不会自动删除
script
拉取镜像:
docker pull <镜像名称>[:镜像版本]
查看本地镜像列表:
docker images [镜像名称关键字]
删除镜像:
# 通过【镜像名称:镜像版本(默认为 latest)】/【镜像 id】 删除镜像
docker rmi <镜像标识>
# 删除所有镜像 (强制删除已存在实例容器的镜像, 无法删除正在运行中的容器所对应的镜像)
docker rmi -f $(docker images -aq)
docker rmi -f `docker images -aq`
查看容器列表:
# 查看正在运行中的容器
docker ps
# 查看所有容器
docker ps -a
删除容器:
# 通过【容器 id】/【容器 name】 删除容器
docker rm <容器标识>
# 强制删除所有容器
docker rm -f $(docker ps -aq)
新建容器:
docker run [options] <镜像标识> [command]
-d # 以后台模式启动容器, docker 容器内 1 号进程结束时, 会自动关闭该容器
-p localPort:containPort # 为容器设置端口映射
-t # 为容器分配一个模拟终端, 否则将以无模拟终端的模式下运行(没有 tab 补全)
-i # stdin, 以交互模式进入容器, 否则无法与容器进行输入输出交互
--name <name> # 为容器设置 name 属性
# -i (是否需要交互)、-t (是否开启一个终端)、-d (是否在后台运行)
# 容器退出后立即删除该容器
docker run -it --rm --name="test_centos" centos:centos7.9.2009
docker exec -it <容器标识> bash # 容器内开启一个终端并切换到宿主机前台来执行命令, 这里的 bash 就是一个 command
docker attach <容器标识> # 进入容器内的 1 号进程 (结束该终端后容器立即停止)
ctrl + d # 从容器的终端上注销并返回宿主机终端
# 由于不能使用 Ctrl + c 向容器内的终端发送 SIGINT 信号, 因此使用 Ctrl + d 向终端发送 EOF
ctrl + q + p # 将容器的终端切换后后台运行并返回宿主机终端
关闭容器:
# 立即关闭容器, 默认等待 10 秒后再关闭
docker stop -t 0 <容器标识>
# 强制结束容器的运行
docker kill <容器标识>
启动容器:
# 启动容器
docker start <容器标识>
# 启动并直接与容器进行交互
docker start -i <容器标识>
重启容器:
docker restart <容器标识>
docker restart -i <容器标识>
容器内的日志:
# 查看 1 号进程的 STDOUT
docker logs <容器标识>
# 跟踪日志输入
docker logs -f <容器标识>
# 查看最后 5 行的日志信息
docker logs -tail 5 <容器标识>
# 在每行的开头显示时间信息
docker logs -t <容器标识>
# 查看指定时间以后的日志
docker logs --since 2021-10-22T12:45:53.381769000Z <容器标识>
查看容器详细信息:
docker inspect <容器标识>
文件传输:
# 向容器内复制文件
docker cp localFile <容器标识>:targetFile
# 向宿主机复制文件
docker cp <容器标识>:sourceFile targetFile
# docker cp from to
打包镜像:
docker commit -m "msg" <容器标识>[ 新镜像名称][:新镜像 tag]
修改镜像 tag 信息:
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
为容器指定容器卷:
# 指定数据集映射路径
docker run -v 宿主机目录:容器目录的绝对路径 <镜像标识>
# 指定一个容器只读的数据卷
docker run -v $PWD/conf:/root/conf:ro <镜像标识>
# 挂载已创建的数据卷系统
docker run -d --mount type=volume,source=<数据卷名称>,target=<容器内的目标路径> <镜像标识>
# 使用已设置的数据卷规则
docker run -d --volumes-from <存在数据卷的容器标识> <镜像标识>
构建 Dockerfile:
docker build -f <Dockerfile> -t <name>:<tag> .
# 上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。
为容器指定网路模式:
docker run --net=<bridge | host | none | container:xxx> <镜像标识>
管理数据卷系统:
# 创建一个数据卷系统
docker volume create
# 查看所有的数据卷系统
docker volume ls
管理网络环境:
# 查看所有网路环境
docker network ls
# 查看指定网路环境详情
docker network inspect <netword id>
# 创建一个新的网路环境
docker network create -d bridge my-net
# 新建容器并指定到新建的网络环境中
docker run --network my-net <镜像标识>
# 为容器指定网路环境
docker connect
镜像 image
镜像就是一个容器的模板
容器 container
容器就是一个镜像所对应的实例, 一个镜像可以运行出来多个容器, 因此镜像就像是一个模板, 而容器是这个模板的产物。
容器卷 valume
网络模式 net
docker 安装后会自动在宿主机上创建一张 docker0 网卡作为桥接模式的网关。
<默认> --net=bridge (docker0 网卡为容器分配一张网卡, 这张网卡挂载在 docker0 下)
--net=host (容器直接使用宿主机的网卡, 可以看到宿主机上的所有网卡)
--net=none (为容器创建独立的命名空间, 但是不配置网卡信息, 默认配置中只有 lo)
--net=container:<容器id> (多个容器共用一张 docker0 所分配出来的的网卡)
常用软件
mysql
# 指定数据路径映射
docker run -p 3306:3306 --name $some-mysql -v /data/docker/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=$my-secret-pw -d mysql:$tag
# 指定配置文件路径映射
docker run -p 3306:3306 -v /etc/container/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=$my-secret-p -d mysql:$tag
# 导入 sql 文件
docker exec -i $some-mysql sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /some/path/on/your/host/all-databases.sql
# 指定时区
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=$my-secret-pw -e TZ='Asia/Shanghai' -d mysql:$tag
# 配置默认编码
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=$my-secret-pw -d mysql:$tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
docker run -p 3306:3306 --name "main_mysql" -v /data/docker/mysql:/var/lib/mysql -v /etc/container/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD="Qc@mysql" -e TZ='Asia/Shanghai' -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
FastDFS
docker pull ygqygq2/fastdfs-nginx
docker run -dit -p 8888:8080 --name fastdfs_tracker -v /var/fdfs/tracker:/var/fdfs ygqygq2/fastdfs-nginx:latest tracker
docker run -dit --network=container:$trackerID --name fastdfs_storage0 -e TRACKER_SERVER=$trackerIP:22122 -v /var/fdfs/storage0:/var/fdfs ygqygq2/fastdfs-nginx:latest storage
Docker File
docker build -f
-t : .
Dockerfile --> Image --> Container
.java --> .class --> app
FORM: 基础镜像
MAINTAINER: 设置 Dockerfile 的作者名称
WORKDIR: 工作目录, 进入容器的初始目录
COPY: 将上下文路径中的文件复制到镜像中
ADD: 复制并自动解压指定文件 (不保留压缩包)
RUN: 运行 command (在 docker build 时运行, 可以多次使用)
RUN ls -al 等价于 RUN ["ls", "-al"]
# shell 格式 <==> exec 格式
# Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。
RUN yum install -y wget \
&& wget -O apache-tomcat-9.0.53.tar.gz "https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.53/bin/apache-tomcat-9.0.53.tar.gz" \
&& tar -zxf apache-tomcat-9.0.53.tar.gz
CMD: 运行 command (在 docker run 时运行, 多个 CMD 指令中前者会被后者所覆盖, 并且 docker run 中的 command 不为空时将作为最终的 CMD)
ENTRYPOINT: 同 CMD 运行机制一样, 但是不会被 docker run 中的 command 所覆盖, 而是追加在 ENTRYPOINT 之后 (利用 ENTRYPOINT 和 CMD 可以实现默认参数)
ENV: 设置环境变量
ARG: 设置 Dockerfile 中的变量
VOLUME: 定义匿名数据卷
EXPOSE: 向外提示内部使用的端口 (docker run -P 会自动映射 EXPOSE 所指定的端口)
Docker Compose
安装
mkdir -p /usr/local/lib/docker/cli-plugins/
curl -SL https://github.com/docker/compose/releases/download/v2.1.1/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
docker compose version
启动命令: docker compose up -d
# demo docker-compose.yml
version: "3.9" # optional since v1.27.0
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
# deldog docker-compose.yml
version: '3'
volumes:
prometheus_data: {}
grafana_data: {}
services:
dogbin:
image: dogbin/dogbin
restart: always
environment:
- SESSION_KEY=${SESSION_KEY}
- DB_LOCATION=/dogbin.xdb
- HOST=${HOST}
- SIMPLEANALYTICS=${SIMPLEANALYTICS}
- CDN_S3_ENDPOINT=${S3_ENDPOINT}
- CDN_S3_ACCESS_KEY=${S3_ACCESS_KEY}
- CDN_S3_SECRET=${S3_SECRET}
- CDN_S3_SECURE=${S3_SECURE}
- CDN_S3_REGION=${S3_REGION}
- ENABLE_SCREENSHOTTER=true
volumes:
- ./dogbin.xdb:/dogbin.xdb
ports:
- 127.0.0.1:8080:8080
expose:
- 8080
- 9090
links:
- "highlighter:highlighter"
- "screenshotter:screenshotter"
- "iframely:iframely"
labels:
- "com.centurylinklabs.watchtower.enable=true"
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:8080/ || exit 1"]
interval: 5s
timeout: 5s
retries: 3
highlighter:
image: dogbin/highlighter
restart: always
labels:
- "com.centurylinklabs.watchtower.enable=true"
screenshotter:
image: dogbin/screenshotter
restart: always
environment:
- DOGBIN_HOST=http://dogbin:8080
- S3_ENDPOINT=${S3_ENDPOINT}
- S3_ACCESS_KEY=${S3_ACCESS_KEY}
- S3_SECRET=${S3_SECRET}
- S3_SECURE=${S3_SECURE}
- S3_REGION=${S3_REGION}
- S3_HOST=${CDN_S3_HOST}
- S3_BUCKET=${CDN_S3_BUCKET}
labels:
- "com.centurylinklabs.watchtower.enable=true"
iframely:
image: dogbin/iframely
restart: always
environment:
- PORT=80
- HOST=iframely.docker.localhost
- BASE_APP_URL=https://${HOST}
ports:
- 127.0.0.1:8083:80
expose:
- 80
volumes:
- ./iframely.config.js:/iframely/config.local.js
command: server.js
autoheal:
image: willfarrell/autoheal
restart: always
environment:
- AUTOHEAL_CONTAINER_LABEL=all
- AUTOHEAL_INTERVAL=10
- AUTOHEAL_START_PERIOD=30
volumes:
- /var/run/docker.sock:/var/run/docker.sock
prometheus:
image: prom/prometheus
volumes:
- ./deployment/prometheus:/etc/prometheus/
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
grafana:
image: grafana/grafana
depends_on:
- prometheus
volumes:
- grafana_data:/var/lib/grafana
- ./deployment/grafana/provisioning/:/etc/grafana/provisioning/
environment:
- GF_SECURITY_ADMIN_PASSWORD=foobar
- GF_USERS_ALLOW_SIGN_UP=false
ports:
- 127.0.0.1:8082:3000
node-exporter:
image: prom/node-exporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- --collector.filesystem.ignored-mount-points
- "^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)"
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_LABEL_ENABLE=true