Docker 实战指南

什么是 Docker?

Docker 是一个开源的平台,用于自动化部署应用程序

Docker 的一些核心概念

容器(Container)

容器是 Docker 的核心组件,它是一个轻量级、独立的可运行环境,包含了应用程序以及它所需的所有库、配置文件和依赖关系

通过容器,可以确保应用程序在不同的环境中一致运行

镜像(Image)

镜像是一个只读的模板,包含了运行应用程序的所有依赖和配置。镜像可以被用来创建容器

可以从 Docker Hub(一个公共的镜像仓库)下载现成的镜像,也可以基于自己的需求构建自定义镜像

守护进程(Docker Daemon)

守护进程在后台运行,负责管理 Docker 容器的生命周期,包括创建、启动、停止和删除容器

Docker Compose

Docker Compose 是一个工具,允许你定义和运行多容器的 Docker 应用程序

通过一个 YAML 文件(docker-compose.yml),你可以描述应用程序的服务、网络和卷,并通过一个命令来启动或停止整个应用程序栈

Dockerfile

Dockerfile 是定义如何构建 Docker 镜像的文件,它包含一系列指令来告诉 Docker 如何安装应用程序及其依赖项

安装 Docker Engine

移除旧版本

yum remove docker \
           docker-client \
           docker-client-latest \
           docker-common \
           docker-latest \
           docker-latest-logrotate \
           docker-logrotate \
           docker-engine

安装 yum-utils

yum install -y yum-utils
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装 Docker Engine、containerd 和 Docker Compose

yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

启动 Docker

systemctl start docker
systemctl enable docker
docker run hello-world

配置加速

mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
}
EOF
systemctl daemon-reload
systemctl restart docker

常用命令

镜像

# 查找镜像
docker search nginx
# 下载镜像
docker pull nginx
# 镜像列表
# docker images
# 移除镜像
# docker rmi <image id>/<repository:tag>
# 移除失败的话先移除container
# docker rm <container id>/<name>

容器

dcoker run <image id>/<repository:tag> # 运行
docker ps # 查看
docker ps -a # 查看,包括停止的容器
docker ps -aq # 查看所有容器的id,包括停止的容器
docker stop <container id>/<name> # 停止
docker start <container id>/<name> # 启动
docker restart <container id>/<name> # 重启
docker stats <container id>/<name> # 状态
docker logs <container id>/<name> # 日志
docker rm <container id>/<name> # 删除,必须先停止
docker rm -f <container id>/<name> # 强制删除
docker rm -f $(docker ps -aq) # 强制全部删除
# 后台启动一个nginx容器,配置端口映射(主机端口:容器端口),并命名这个容器为nginx
docker run -d -p 80:80 --name nginx nginx
# 在容器中启动一个新进程,进行交互式的终端会话
docker exec -it <container id>/<name> /bin/bash

修改页面

# 容器有自己的文件系统
cd /usr/share/nginx/html
vi index.html # 没有vi
echo '<h2>Hello Docker!</h2>' > index.html

保存镜像

# 生成镜像
docker commit -m 'update index.html' nginx hqh15/nginx:v1.0
docker images
# 打包镜像
docker save -o nginx.tar hqh15/nginx:v1.0

物理传输后加载

docker load -i nginx.tar
docker run -d -p 80:80 --name nginx nginx

分享镜像

docker login -u hqh15
docker tag hqh15/nginx:v1.0 hqh15/nginx:latest
docker push hqh15/nginx:v1.0
docker push hqh15/nginx:latest

存储

容器管理过于麻烦

删除容器数据没了

所以有了目录挂载和卷映射(容器删除后数据依旧保存在主机的目录或卷中)

目录挂载(内外皆空,保持一致)

# 后台运行一个容器,将主机上的/app/html目录挂载到容器内的/usr/share/nginx/html目录
docker run -d -p 80:80 -v /app/html:/usr/share/nginx/html --name nginx nginx
echo '<h2>Hello Docker!</h2>' > index.html

卷映射(以内为基,保持一致)

# 后台运行一个容器,将主机上的/app/html目录挂载到容器内的/usr/share/nginx/html目录,将一个名为ngconf的卷挂载到容器内的/etc/nginx目录
docker run -d -p 81:80 -v /app/html:/usr/share/nginx/html -v ngconf:/etc/nginx --name nginx nginx
docker volume ls
docker volume inspect ngconf
cd /var/lib/docker/volumes/ngconf
ls
cd _data

网络

每一个容器启动后,都会加入一个默认的网络 docker0(172.17.0.1),分配到一个唯一的 ip

# 比如有两个容器,容器1的网络为172.17.0.2,容器2的网络为 172.17.0.3
# 进入容器2访问容器1,通过外部网络访问
curl http://101.43.52.162[:80]
docker container inspect <container id>/<name>
# 通过内部网络访问
curl http://172.17.0.3:80 # 这个80是容器内部端口

ip 由于各种原因可能会变化,而且 docker0 默认不支持主机域名

创建自定义网络,容器名就是稳定域名

docker network --help
docker network ls
docker network create customnet
docker run -d -p 80:80 --name app1 --network customnet nginx
docker run -d -p 81:80 --name app2 --network customnet nginx
docker exec -it app1 bash
curl http://app2:80

Redis 主从同步集群

读写分离

参考:https://hub.docker.com/r/bitnami/redis

最佳实践(以 Mysql 为例)

端口映射,目录挂载/卷映射,环境变量

docker run -d -p 3306:3306 \
-v /app/myconf:/etc/mysql/conf.d \
-v /app/mydata:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:lts

Docker Compose

参考:https://docs.docker.com/reference/compose-file/

docker network create blog

docker run -d -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=wordpress \
-v mysql-data:/var/lib/mysql \
-v /app/myconf:/etc/mysql/conf.d \
--restart always --name mysql \
--network blog \
mysql:lts

docker run -d -p 8080:80 \
-e WORDPRESS_DB_HOST=mysql \
-e WORDPRESS_DB_USER=root \
-e WORDPRESS_DB_PASSWORD=123456 \
-e WORDPRESS_DB_NAME=wordpress \
-v wordpress:/var/www/html \
--restart always --name wordpress \
--network blog \
wordpress:latest

使用 docker compose 管理

docker rm -f $(docker ps -aq)
docker volume rm mysql-data wordpress
docker network rm blog
name: blog
services:
   mysql:
     container_name: mysql
     image: mysql:lts
     ports:
       - "3306:3306"
     environment:
       - MYSQL_ROOT_PASSWORD=123456
       - MYSQL_DATABASE=wordpress
     volumes:
       - mysql-data:/var/lib/mysql
       - /app/myconf:/etc/mysql/conf.d
     restart: always
     networks:
       - blog
   wordpress:
     container_name: wordpress
     image: wordpress:latest
     ports:
       - "8080:80"
     environment:
       - WORDPRESS_DB_HOST=mysql
       - WORDPRESS_DB_USER=root
       - WORDPRESS_DB_PASSWORD=123456
       - WORDPRESS_DB_NAME=wordpress
     volumes:
       - wordpress:/var/www/html
     restart: always
     networks:
       - blog
     depends_on:
       - mysql
volumes:
  mysql-data:
  wordpress:
networks:
  blog:
docker compose --help
docker compose up -d # 启动容器
docker compose down --help
docker compose down # 移除容器(不会移除卷)
docker compose --rmi all -v # 移除容器、镜像、卷

Dockerfile

参考:https://docs.docker.com/reference/dockerfile/

制作自己的镜像

FROM openjdk:17
LABEL author=hqh15
COPY app.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
docker build -f Dockerfile -t app:v1.0 .

镜像分层机制节省空间(镜像是只读层,容器是读写层)