安装Docker 我使用的虚拟机版本为Centos7。
卸载旧版 如果系统中存在旧版docker,先卸载旧版。
yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine \ docker-selinux
配置Docker的yum库 首先安装yum工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
安装成功后,配置Docker的yum源:
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.reposudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
更新yum,建立缓存
安装Docker yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
检查Docker是否安装成功可以使用命令 docker -v,如果出现以下提示:
则说明安装成功。
启动 systemctl start docker systemctl stop docker systemctl restart docker systemctl enable docker docker ps
配置镜像加速 官方镜像是在海外,速度较慢,建议替换为国内镜像。
我这里使用的是轩辕镜像,配置如下:
sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json <<EOF { "registry-mirrors": [ "https://docker.xuanyuan.me" ] } EOF sudo systemctl daemon-reloadsudo systemctl restart docker
快速入门 部署Mysql docker run -d \ --name mysql \ -p 3306:3306 \ -e TZ=Asia/Shanghai \ -e MYSQL_ROOT_PASSWORD=123 \ mysql
执行完命令以后,docker就会帮我们拉取并下载镜像,然后运行mysql容器。
命令解读 为什么要做端口映射?
因为容器环境是对外隔离的,所以其他主机无法直接访问到容器中的服务(比如我现在有一台主机想要访问一台虚拟机中的容器是无法直接访问的,但是虚拟机是可以直接访问容器的),因此要通过端口映射,其他主机可以访问虚拟机的端口从而访问到容器中的端口。
-e KEY=VALUE:配置环境变量
mysql :指定运行的镜像的名字
Docker基础 常见命令 这里有一张图可以帮助理解记忆一些常见命令:
演示 :
docker pull nginx docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 605c77e624dd 16 months ago 141MB mysql latest 3218b38490ce 17 months ago 516MB docker run -d --name nginx -p 80:80 nginx docker ps docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}" docker stop nginx docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}" docker start nginx docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}" docker inspect nginx docker exec -it nginx bash docker exec -it mysql mysql -uroot -p docker rm nginx docker rm -f nginx
命令别名
给命令起别名可以简化复杂命令的书写,配置步骤如下:
vi /root/.bashrc 内容如下: alias rm ='rm -i' alias cp ='cp -i' alias mv ='mv -i' alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"' alias dis='docker images' if [ -f /etc/bashrc ]; then . /etc/bashrc fi
数据卷 容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,我们要读写容器内的文件非常不方便。因此,容器提供程序的运行环境,但是程序运行产生的数据、程序运行依赖的配置都应该与容器解耦 。
什么是数据卷? 数据卷(volume) 是一个虚拟目录,是容器内目录 与宿主机目录 之间映射的桥梁 。
如果我们要让Nginx代理我们的静态资源,最好是放到html目录;如果我们要修改Nginx的配置,最好是找到conf下的nginx.conf文件。
但遗憾的是,容器运行的Nginx所有的文件都在容器内部。所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作。如图:
在上图中:
我们创建了两个数据卷:conf、html Nginx容器内部的conf目录和html目录分别与两个数据卷关联。 而数据卷conf和html分别指向了宿主机的/var/lib/docker/volumes/conf/_data目录和/var/lib/docker/volumes/html/_data目录 这样以来,容器内的conf和html目录就 与宿主机的conf和html目录关联起来,我们称为挂载 。此时,我们操作宿主机的/var/lib/docker/volumes/html/_data就是在操作容器内的/usr/share/nginx/html目录。只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了。
/var/lib/docker/volumes这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为/数据卷名/_data。
数据卷命令
注意: 容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建 。
演示:
docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx docker volume ls DRIVER VOLUME NAME local 29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459flocal htmldocker volume inspect html [ { "CreatedAt" : "2024-05-17T19:57:08+08:00" , "Driver" : "local" , "Labels" : null, "Mountpoint" : "/var/lib/docker/volumes/html/_data" , "Name" : "html" , "Options" : null, "Scope" : "local" } ] ll /var/lib/docker/volumes/html/_data 总用量 8 -rw-r--r--. 1 root root 497 12月 28 2021 50x.html -rw-r--r--. 1 root root 615 12月 28 2021 index.html cd /var/lib/docker/volumes/html/_datavi index.html docker exec -it nginx bash
如果我们在创建容器的时候不进行容器的挂载,那么系统也会为我们的容器创建匿名的数据卷并进行挂载。
查看一个容器数据卷挂载情况的流程如下:
挂载本地目录或文件 可以发现,数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载。挂载语法与数据卷类似:
-v 本地目录:容器内目录 -v 本地文件:容器内文件
注意 :本地目录或文件必须以 / 或 ./开头,如果直接以名字开头,会被识别为数据卷名而非本地目录名。
例如:
-v mysql:/var/lib/mysql -v ./mysql:/var/lib/mysql
演示 :删除并重新创建mysql容器,并完成本地目录挂载:
挂载/root/mysql/data到容器内的/var/lib/mysql目录 挂载/root/mysql/init到容器内的/docker-entrypoint-initdb.d目录(初始化的SQL脚本目录) 挂载/root/mysql/conf到容器内的/etc/mysql/conf.d目录(这个是MySQL配置文件目录) docker rm -f mysql cd ~docker run -d \ --name mysql \ -p 3306:3306 \ -e TZ=Asia/Shanghai \ -e MYSQL_ROOT_PASSWORD=123 \ -v /root/mysql/data:/var/lib/mysql \ -v /root/mysql/conf:/etc/mysql/conf.d \ -v /root/mysql/init:/docker-entrypoint-initdb.d \ mysql ls -l mysql总用量 4 drwxr-xr-x. 2 root root 20 5月 19 15:11 conf drwxr-xr-x. 7 polkitd root 4096 5月 19 15:11 data drwxr-xr-x. 2 root root 23 5月 19 15:11 init ls -l datadocker exec -it mysql mysql -uroot -p123 show variables like "%char%" ; +--------------------------+--------------------------------+ | Variable_name | Value | +--------------------------+--------------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8mb3 | | character_sets_dir | /usr/share/mysql-8.0/charsets/ | +--------------------------+--------------------------------+ show databases; +--------------------+ | Database | +--------------------+ | hmall | | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.00 sec) use hmall; show tables; +-----------------+ | Tables_in_hmall | +-----------------+ | address | | cart | | item | | order | | order_detail | | order_logistics | | pay_order | | user | +-----------------+ +----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+ | id | user_id | province | city | town | mobile | street | contact | is_default | notes | +----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+ | 59 | 1 | 北京 | 北京 | 朝阳区 | 13900112222 | 金燕龙办公楼 | 李佳诚 | 0 | NULL | | 60 | 1 | 北京 | 北京 | 朝阳区 | 13700221122 | 修正大厦 | 李佳红 | 0 | NULL | | 61 | 1 | 上海 | 上海 | 浦东新区 | 13301212233 | 航头镇航头路 | 李佳星 | 1 | NULL | | 63 | 1 | 广东 | 佛山 | 永春 | 13301212233 | 永春武馆 | 李晓龙 | 0 | NULL | +----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+ 4 rows in set (0.00 sec)
镜像 镜像的结构 举个例子,我们要从0部署一个Java应用,大概流程是这样:
准备一个linux服务(CentOS或者Ubuntu均可) 安装并配置JDK 上传Jar包 运行jar包 那因此,我们打包镜像也是分成这么几步:
准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可) 安装并配置JDK 拷贝jar包 配置启动脚本 上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合 。
但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer (层 )。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作 。
Dockerfile 由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以Docker就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给Docker去执行即可。
而这种记录镜像结构的文件就称为Dockerfile
常用命令如下:
指令 说明 示例 FROM 指定基础镜像 FROM centos:6ENV 设置环境变量,可在后面指令使用 ENV key valueCOPY 拷贝本地文件到镜像的指定目录 COPY ./xx.jar /tmp/app.jarRUN 执行Linux的shell命令,一般是安装过程的命令 RUN yum install gccEXPOSE 指定容器运行时监听的端口,是给镜像使用者看的 EXPOSE 8080 ENTRYPOINT 镜像中应用的启动命令,容器运行时调用 ENTRYPOINT java -jar xx.jar
例如,要基于Ubuntu镜像来构建一个Java应用,其Dockerfile内容如下:
FROM ubuntu:16.04 ENV JAVA_DIR=/usr/localENV TZ=Asia/ShanghaiCOPY ./jdk8.tar.gz $JAVA_DIR / COPY ./docker-demo.jar /tmp/app.jar RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN cd $JAVA_DIR \ && tar -xf ./jdk8.tar.gz \ && mv ./jdk1.8.0_144 ./java8 ENV JAVA_HOME=$JAVA_DIR/java8ENV PATH=$PATH:$JAVA_HOME/binEXPOSE 8080 ENTRYPOINT ["java" , "-jar" , "/app.jar" ]
有人提供了基础的系统加JDK环境,我们在此基础上制作java镜像,就可以省去JDK的配置:
FROM openjdk:11.0 -jre-busterENV TZ=Asia/ShanghaiRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone COPY docker-demo.jar /app.jar ENTRYPOINT ["java" , "-jar" , "/app.jar" ]
构建镜像 准备一个demo项目和对应的Dockerfile文件
首先,我们将docker-demo.jar包以及Dockerfile拷贝到虚拟机的/root/demo目录:
然后,执行命令,构建镜像:
cd /root/demodocker build -t docker-demo:1.0 .
命令说明:
docker build : 就是构建一个docker镜像-t docker-demo:1.0 :-t参数是指定镜像的名称(repository和tag). : 最后的点是指构建时Dockerfile所在路径,由于我们进入了demo目录,所以指定的是.代表当前目录,也可以直接指定Dockerfile目录(但是一定要与Dockerfile中定好的一样):docker build -t docker-demo:1.0 /root/demo
结果如下:
网络 容器之间能否互相访问呢?我们来测试一下。
首先,我们查看下nginx容器的详细信息,重点关注其中的网络IP地址:
docker inspect nginx "Networks" : { "bridge" : { "IPAMConfig" : null, "Links" : null, "Aliases" : null, "MacAddress" : "02:42:ac:11:00:02" , "NetworkID" : "b5684b121e5b225da9cc310c82be9cad6d766569d412b5def92285a6846d8a71" , "EndpointID" : "454baaff6edfc188458f40717f69abc47642e0d6876e4aed4d5082a8a52a526c" , "Gateway" : "172.17.0.1" , "IPAddress" : "172.17.0.2" , "IPPrefixLen" : 16, "IPv6Gateway" : "" , "GlobalIPv6Address" : "" , "GlobalIPv6PrefixLen" : 0, "DriverOpts" : null, "DNSNames" : null 可以看到其ip为:172.17.0.2 docker exec -it docker-demo bash ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time =0.463 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time =0.055 ms 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time =0.059 ms 64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time =0.061 ms 64 bytes from 172.17.0.2: icmp_seq=5 ttl=64 time =0.061 ms
发现可以互联,没有问题。
但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败。
所以,我们必须借助于docker的网络功能来解决这个问题。
常用命令如下:
自定义网络演示:
docker network create ersami docker network ls NETWORK ID NAME DRIVER SCOPE b5684b121e5b bridge bridge local 9516b045d2fd ersami bridge local c966eef56748 host host local 244e4a7c4401 none null local docker network connect ersami nginx --alias ng docker network connect ersami docker-demo --alias dd docker exec -it docker-demo bash ping ng PING ng (172.20.0.2) 56(84) bytes of data. 64 bytes from nginx.ersami (172.20.0.2): icmp_seq=1 ttl=64 time =0.093 ms 64 bytes from nginx.ersami (172.20.0.2): icmp_seq=2 ttl=64 time =0.066 ms ping mysql PING nginx (172.20.0.2) 56(84) bytes of data. 64 bytes from nginx.ersami (172.20.0.2): icmp_seq=3 ttl=64 time =0.050 ms 64 bytes from nginx.ersami (172.20.0.2): icmp_seq=4 ttl=64 time =0.062 ms
OK,现在无需记住IP地址也可以实现容器互联了
我们也可以在创建容器的时候指定网络:
--network ersami docker run -d --name docker-demo -p 8080:8080 --network ersami docker-demo
总结 :
在自定义网络中,可以给容器起多个别名,默认的别名是容器名本身 在同一个自定义网络中 的容器,可以通过别名互相访问 (不同网络中的容器是无法互相访问的) DockerCompose 大家可以看到,我们部署一个简单的java项目,其中包含3个容器:
而稍微复杂的项目,其中还会有各种各样的其它中间件,需要部署的东西远不止3个。如果还像之前那样手动的逐一部署,就太麻烦了。
而Docker Compose就可以帮助我们实现多个相互关联的Docker容器的快速部署 。它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器。
基本语法 docker-compose文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务(service)。由于service就是在定义某个应用的运行时参数,因此与docker run参数非常相似。
可以和手动部署对比着看:
docker run 参数 docker compose 指令 说明 –name container_name 容器名称 -p ports 端口映射 -e environment 环境变量 -v volumes 数据卷配置 –network networks 网络
项目部署演示:
version: "3.8" services: mysql: image: mysql container_name: mysql ports: - "3306:3306" environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: 123 volumes: - "./mysql/conf:/etc/mysql/conf.d" - "./mysql/data:/var/lib/mysql" - "./mysql/init:/docker-entrypoint-initdb.d" networks: - hm-net hmall: build: context: . dockerfile: Dockerfile container_name: hmall ports: - "8080:8080" networks: - hm-net depends_on: - mysql nginx: image: nginx container_name: nginx ports: - "18080:18080" - "18081:18081" volumes: - "./nginx/nginx.conf:/etc/nginx/nginx.conf" - "./nginx/html:/usr/share/nginx/html" depends_on: - hmall networks: - hm-net networks: hm-net: name: hmall
基础命令 docker compose [OPTIONS] [COMMAND]
其中,OPTIONS和COMMAND都是可选参数,比较常见的有:
类型 参数或指令 说明 Options -f 指定compose文件的路径和名称 -p 指定project名称。project就是当前compose文件中设置的多个service的集合,是逻辑概念 Commands up 创建并启动所有service容器 down 停止并移除所有容器、网络 ps 列出所有启动的容器 logs 查看指定容器的日志 stop 停止容器 start 启动容器 restart 重启容器 top 查看运行中的进程 exec 在指定的运行中容器中执行命令
演示:
cd /rootdocker rm -f $(docker ps -qa) docker rmi hmall rm -rf mysql/datadocker compose up -d [+] Building 15.5s (8/8) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 358B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/openjdk:11.0-jre-buster 15.4s => [1/3] FROM docker.io/library/openjdk:11.0-jre-buster@sha256:3546a17e6fb4ff4fa681c3 0.0s => [internal] load build context 0.0s => => transferring context: 98B 0.0s => CACHED [2/3] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 0.0s => CACHED [3/3] COPY hm-service.jar /app.jar 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:32eebee16acde22550232f2eb80c69d2ce813ed099640e4cfed2193f71 0.0s => => naming to docker.io/library/root-hmall 0.0s [+] Running 4/4 ✔ Network hmall Created 0.2s ✔ Container mysql Started 0.5s ✔ Container hmall Started 0.9s ✔ Container nginx Started 1.5s docker compose images CONTAINER REPOSITORY TAG IMAGE ID SIZE hmall root-hmall latest 32eebee16acd 362MB mysql mysql latest 3218b38490ce 516MB nginx nginx latest 605c77e624dd 141MB docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS hmall root-hmall "java -jar /app.jar" hmall 54 seconds ago Up 52 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp mysql mysql "docker-entrypoint.s…" mysql 54 seconds ago Up 53 seconds 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp nginx nginx "/docker-entrypoint.…" nginx 54 seconds ago Up 52 seconds 80/tcp, 0.0.0.0:18080-18081->18080-18081/tcp, :::18080-18081->18080-18081/tcp