1. 容器简介
1.1 什么是Linux容器
容器其实是一种沙盒技术。沙盒像一个集装箱一样把你的应用“装”起来的技术。这样应用与应用之间就因为有了边界而不至于相互干扰;而被装进了集装箱的应用,也可以被方便地搬来搬去。
Linux容器是与系统其他部分隔离开的一系列进程,从另一个镜像运行,并由该镜像提供支持进程所需的全部文件。容器提供的镜像包含了应用的所有依赖项,因而在从开发到测试再到生产的整个过程中,它都具有可移植性和一致性。
1.2 容器的基本概念
隔离性:容器在操作系统层面提供了进程、文件系统、网络等资源的隔离,确保每个容器内部的操作不会影响到其他容器或主机系统。
轻量级:与虚拟机不同,容器不需要包含整个操作系统,而是共享主机操作系统的内核。因此,容器的启动速度快,占用的资源少。一般容器以毫秒级启动,而虚拟机是分钟级启动。
可移植性:容器镜像包含应用程序及其依赖的所有软件和库,使得容器可以在任何支持容器运行的环境中运行(例如,开发者的本地机器、测试环境、生产环境等)。
1.3 容器的核心技术实现
Namespaces
-
提供进程、网络、文件系统等资源的隔离,确保每个容器内的进程只能看到并操作属于该容器的资源。比如:命名空间可以提供一个进程相互隔离的独立网络空间,不同的容器间进程pid可以相同,进程并不冲突影响,但可以共享底层的计算和存储资源。
Cgroups
-
管理和限制容器使用的资源(如 CPU、内存、磁盘 I/O 等),确保容器不会超出分配的资源。比如给容器A分配4颗CPU,8G 内存,那这个容器最多用这么多的资源。如果内存超过8G ,会启动swap,效率降低,也可能会被调度系统给kill掉。
白话文:Namespace的作用是“隔离”,他让应用进程只能“看到”该Namespace内的“世界”;而Cgroups的作用是“限制”,它给这个“世界”围了一圈看不见的“墙”。如此一来,进程就真的被“装”在了一个于是隔绝的“房间”里。这就是所谓的沙盒!
1.4 容器和虚拟机的区别?
传统虚拟技术
虚拟机(virtual machine)就是带环境安装的一种解决方案。
它可以在一种操作系统里面运行另一种操作系统,比如在Windows 系统里面运行Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。
虚拟机的缺点:1 资源占用多 2 冗余步骤多 3 启动慢
容器技术
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
Docker和传统虚拟化方式的不同之处:
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。
为什么docker比VM快?
docker有着比虚拟机更少的抽象层。由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。避免引寻、加载操作系统内核比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载Guest OS,这个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了这个过程,因此新建一个docker容器只需要几秒钟。
2. Docker简介
2.1 Docker是什么
官网:
开发人员通过Docker将他们的想法变为现实
将软件打包为标准化单元,用于开发、装运和部署
容器是一个标准的软件单元,它封装代码及其所有依赖项,以便应用程序从一个计算环境快速可靠地运行到另一个环境。Docker容器镜像是一个轻量级、独立的可执行软件包,包括运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。
容器镜像在运行时会变成容器,在Docker容器的情况下,镜像在Docker引擎上运行时会成为容器。容器化软件可用于基于Linux和Windows的应用程序,无论基础结构如何,都将始终运行相同的软件。容器将软件与其环境隔离开来,并确保它统一工作,尽管存在差异,例如开发和阶段之间的差异。
2013年,Docker推出了集装箱行业标准。容器是一个标准化的软件单元,允许开发人员将应用程序与其环境隔离,解决“它在我的机器上工作”的难题。对于今天数以百万计的开发人员来说,Docker是构建和共享容器化应用程序的事实标准——从桌面到云。我们正在为开发人员和开发团队构建从代码到云的独特连接体验。
2.2 Docker的目标
docker的主要目标是"Build,Ship and Run any App,Angwhere", 构建,运输,处处运行
-
构建:做一个docker镜像
-
运输:docker pull or push
-
运行:启动一个容器
每一个容器,他都有自己的文件系统rootfs.
3. Docker熟识
3.1 Docker部署及配置
$ cd /etc/yum.repos.d $ wget https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo $ sed -i 's#download.docker.com#mirrors.ustc.edu.cn/docker-ce#g' docker-ce.repo $ yum -y install docker-ce $ systemctl enable --now docker.service
# 启动nginx:1.21容器,并且后台运行 $ docker container run -itd --name webserver01 -p 8080:80 nginx:1.21 # 参数解释 -i:让容器的标准输入打开 -t:分配一个伪终端 -d:后台启动 -p: 将宿主机的8080端口映照至容器的80端口 # 执行命令终端部分回显如下: Unable to find image 'nginx:1.21' locally 1.21: Pulling from library/nginx 1fe172e4850f: Pull complete 35c195f487df: Pull complete 213b9b16f495: Pull complete a8172d9e19b9: Pull complete f5eee2cb2150: Pull complete 93e404ba8667: Pull complete Digest: sha256:859ab6768a6f26a79bc42b231664111317d095a4f04e4b6fe79ce37b3d199097 Status: Downloaded newer image for nginx:1.21 28a074f14de734d565b61d49f8dd1fd005a7da696b381ec3514fe89449ae96b3 # 前台运行nginx容器,退出时删除 $ docker container run -it nginx:1.21 docker container run -itd --name webserver01 -p 80:80 registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:1.24.0
测试访问:宿主机IP+8080
配置镜像加速器
# 配置镜像加速器 $ mkdir -p /etc/docker $ tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://kr1xs9ba.mirror.aliyuncs.com"] } EOF $ systemctl daemon-reload $ systemctl restart docker # 查看是否生效 $ docker info |grep -A 3 'Registry Mirrors'
3.2 Docker架构
-
Docker Client(Docker 客户端)
Docker 客户端是用户与 Docker 系统交互的接口。用户通过 CLI 命令(如 docker run
、docker build
)与 Docker 守护进程通信,发出管理容器的各种指令。
-
Dockerd(Docker 守护进程)
Dockerd 是 Docker 的核心守护进程,负责处理来自 Docker 客户端的请求。它管理 Docker 容器、镜像、网络和数据卷,并将请求传递给下一级组件(如 Containerd)。通过 gRPC 与 Containerd 通信。
-
Containerd
Containerd 是一个高级容器运行时,负责管理容器的整个生命周期。它处理容器的创建、执行、挂载存储和网络管理。通过 gRPC 接收 Dockerd 的指令,并进一步传递给 Runc 和 Shim。
-
Runc
Runc 是一个 CLI 工具,用于根据 Open Container Initiative (OCI) 规范创建和运行容器。Runc 实际上负责执行容器的启动、停止等底层操作。它通过 fork
进程来创建容器。
-
Shim
Shim 是一个中介程序,在容器与 Containerd 之间提供隔离。当 Runc 启动容器后,Shim 保持容器运行并将 Runc 从系统进程中分离。这样,如果 Containerd 需要重启或崩溃,容器仍然可以继续运行。
3.3 Docker基本指令
[root@docker-ce ~]# docker version Client: Docker Engine - Community # 这是社区版本的 Docker 引擎。 Version: 26.1.1 # Docker 客户端的版本号。 API version: 1.45 # Docker API 的版本号。Docker引擎通信的接口。 Go version: go1.21.9 # Go 版本 Git commit: 4cf5afa # 编译客户端时使用的 Git 提交哈希值。这是特定构建的唯一标识符。 Built: Tue Apr 30 11:51:00 2024 # 客户端的构建时间。 OS/Arch: linux/amd64 # 客户端运行的操作系统和架构 Context: default # Docker Context 是 Docker CLI 的配置,允许用户在多个 Docker 主机之间轻松切换。 Server: Docker Engine - Community Engine: Version: 26.1.1 API version: 1.45 (minimum version 1.24) Go version: go1.21.9 Git commit: ac2de55 Built: Tue Apr 30 11:49:57 2024 OS/Arch: linux/amd64 Experimental: false containerd: # containerd 是一个容器运行时,负责在宿主机上管理容器的生命周期。 Version: 1.6.31 GitCommit: e377cd56a71523140ca6ae87e30244719194a521 runc: # runc 是一个 CLI 工具,用于根据 OCI 规范运行容器。 Version: 1.1.12 GitCommit: v1.1.12-0-g51d5e94 docker-init: Version: 0.19.0 GitCommit: de40ad0 [root@docker-ce ~]# docker info Client: Docker Engine - Community Version: 26.1.1 Context: default Debug Mode: false Plugins: buildx: Docker Buildx (Docker Inc.) Version: v0.14.0 Path: /usr/libexec/docker/cli-plugins/docker-buildx compose: Docker Compose (Docker Inc.) Version: v2.27.0 Path: /usr/libexec/docker/cli-plugins/docker-compose Server: Containers: 2 Running: 2 # 正在运行的容器数量,这里是 2。 Paused: 0 # 暂停的容器数量,这里是 0。 Stopped: 0 # 停止的容器数量,这里是 0。 Images: 2 # Docker 镜像的数量,这里是 2。 Server Version: 26.1.1 # Docker 服务器的版本号,这里是 26.1.1。 Storage Driver: overlay2 Backing Filesystem: xfs # 底层文件系统,这里是 xfs。 Supports d_type: true Using metacopy: false Native Overlay Diff: true userxattr: false Logging Driver: json-file Cgroup Driver: cgroupfs Cgroup Version: 1 Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog Swarm: inactive Runtimes: io.containerd.runc.v2 runc Default Runtime: runc Init Binary: docker-init containerd version: e377cd56a71523140ca6ae87e30244719194a521 runc version: v1.1.12-0-g51d5e94 init version: de40ad0 Security Options: seccomp Profile: builtin Kernel Version: 3.10.0-1160.el7.x86_64 Operating System: CentOS Linux 7 (Core) OSType: linux Architecture: x86_64 CPUs: 2 Total Memory: 3.84GiB Name: docker-ce ID: f0bf43f3-cec5-41dd-8c3d-673a340c7237 Docker Root Dir: /var/lib/docker Debug Mode: false Experimental: false Insecure Registries: 127.0.0.0/8 Registry Mirrors: https://kr1xs9ba.mirror.aliyuncs.com/ Live Restore Enabled: false
4. Docker镜像管理
4.1 登录官方镜像仓库
# 登录 [root@docker-ce ~]# docker login Log in with your Docker ID or email address 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. You can log in with your password or a Personal Access Token (PAT). Using a limited-scope PAT grants better security and is required for organizations using SSO. Learn more at https://docs.docker.com/go/access-tokens/ Username: 2364736365@qq.com Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded # 退出 [root@docker-ce ~]# docker logout
4.2 搜索官方仓库镜像 docker search <image_name>
[root@docker-ce ~]# docker search nginx | head -3 NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx Official build of Nginx. 16723 [OK] bitnami/nginx Bitnami nginx Docker Image 124 [OK]
4.2 获取镜像 docker pull <image_name>:<tag>
[root@docker-ce ~]# docker pull tomcat:8-jdk8-corretto 8-jdk8-corretto: Pulling from library/tomcat 8de5b65bd171: Pull complete 00e9707b9594: Pull complete a4d7141ce82b: Pull complete 683f96ff9142: Pull complete 021d0aeb9b5a: Pull complete Digest: sha256:5519094808b33f320cb8821e6789ae813d1c71ee3dd03ecc35035fb94be37a3e Status: Downloaded newer image for tomcat:8-jdk8-corretto docker.io/library/tomcat:8-jdk8-corretto # 查看本地所有镜像 [root@docker-ce ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE tomcat 8-jdk8-corretto 6aa794aeaf2e Less than a second ago 379MB nginx 1.21 fa5269854a5e 12 days ago 142MB
4.3 导出镜像 docker image save <image_name>:<tag> >/to/path/<image_name>.tar.gz
$ docker image save tomcat:8-jdk8-corretto >/opt/docker-images-tar/tomcat-8-jdk-corretto.tar.gz
提示:导出时需要指定镜像的名称和版本,否则再次导入时镜像名称为空
4.4 导入镜像: docker image load -i /to/path/<image_name>.tar.gz
[root@docker-ce ~]# docker image load -i /opt/docker-images-tar/tomcat-8-jdk-corretto.tar.gz 2ce46c79ab58: Loading layer [==================================================>] 171.1MB/171.1MB adfbec3e67f4: Loading layer [==================================================>] 184.6MB/184.6MB 62ad8969b17a: Loading layer [==================================================>] 3.072kB/3.072kB 693367907f2b: Loading layer [==================================================>] 31.19MB/31.19MB 21dcce1be3e6: Loading layer [==================================================>] 2.048kB/2.048kB Loaded image: tomcat:8-jdk8-corretto [root@docker-ce ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE tomcat 8-jdk8-corretto 6aa794aeaf2e Less than a second ago 379MB nginx 1.21 fa5269854a5e 12 days ago 142MB
4.5 删除镜像
[root@docker-ce ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE tomcat 8-jdk8-corretto 6aa794aeaf2e Less than a second ago 379MB nginx 1.21 fa5269854a5e 12 days ago 142MB [root@docker-ce ~]# docker image rmi tomcat:8-jdk8-corretto Untagged: tomcat:8-jdk8-corretto Untagged: tomcat@sha256:5519094808b33f320cb8821e6789ae813d1c71ee3dd03ecc35035fb94be37a3e Deleted: sha256:6aa794aeaf2e3b423640cb6c28c62735c517b5cf0ac2067f1a6ebc4b826adf35 Deleted: sha256:377575e0dcdd8ca0c48ad4bb750fdef5671d7422e5dfac0aa1056afb3ac0391b Deleted: sha256:cd150b0a9540259ba404d900d5a4decefb51d5d64f912edf71fc4fbdf1b275b6 Deleted: sha256:e18b666124eab8f055392a4bc8dc0dd3f861963b7c872d535a4ad103192b9def Deleted: sha256:e3469860a4508d511a68fd1d5ed754565b5c922cc3b283d195a56e42b97d7192 Deleted: sha256:2ce46c79ab58cf3d6f9556e9141c0564407a50028040a94bb8a82ab6e33331c2 [root@docker-ce ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx 1.21 fa5269854a5e 12 days ago 142MB
删除镜像(删除镜像需要保证没有基于此镜像运行容器,否则需要先删除容器)
4.6 查看镜像详细信息
[root@docker-ce ~]# docker image inspect nginx:1.21 [ { "Id": "sha256:fa5269854a5e615e51a72b17ad3fd1e01268f278a6684c8ed3c5f0cdce3f230b", "RepoTags": [ "nginx:1.21" ], "RepoDigests": [ "nginx@sha256:859ab6768a6f26a79bc42b231664111317d095a4f04e4b6fe79ce37b3d199097" ], "Parent": "", "Comment": "", "Created": "2022-04-20T10:43:12.055940177Z", "Container": "3c8758320eb6a5293e75ce1ff5afe91584a72b4ee400792f34985a27673ffbc2", "ContainerConfig": { "Hostname": "3c8758320eb6", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ # 容器加载的环境变量 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "NGINX_VERSION=1.21.6", "NJS_VERSION=0.7.2", "PKG_RELEASE=1~bullseye" ], "Cmd": [ # 容器的启动命令 "/bin/sh", "-c", "#(nop) ", "CMD [\"nginx\" \"-g\" \"daemon off;\"]" ], "Image": # 镜像ID "sha256:e158bbfdf1201dbc8876232cef4465c5f69c1fd0986f05ee48291a92debc21a0", "Volumes": null, "WorkingDir": "", "Entrypoint": [ "/docker-entrypoint.sh" ], ...... ...... ]
4.7 镜像的拉取策略(了解:Kubernetes Pod章节细讲)
在 Docker 中,镜像拉取策略(Image Pull Policy)决定了在启动容器时如何拉取镜像。指定镜像拉取策略可以控制 Docker 是否从注册表中拉取最新的镜像,或者使用本地缓存的镜像。
-
IfNotPresent(默认策略):
-
-
仅当本地不存在指定的镜像时才从注册表中拉取镜像。如果本地已有该镜像,则使用本地镜像。
-
[root@docker-ce ~]# docker run --pull=ifnotpresent <image_name>:<image_id>
-
Always:
-
-
每次启动容器时总是从注册表中拉取镜像,无论本地是否已有该镜像。
-
-
-
适用于需要确保总是使用最新版本镜像的情况,确保容器总是基于最新的镜像版本启动。
-
[root@docker-ce ~]# docker run --pull=always <image_name>:<image_id>
-
Never:
-
-
仅使用本地已有的镜像,从不从注册表中拉取镜像。如果本地不存在指定的镜像,则启动容器会失败。
-
-
-
适用于不希望或无法访问镜像注册表的情况,确保使用本地镜像缓存。
-
[root@docker-ce ~]# docker run --pull=never <image_name>:<image_id>
4.8 阿里云镜像仓库使用
官网:阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台
# 退出Docker官方镜像仓库 [root@docker-ce ~]# docker logout Removing login credentials for https://index.docker.io/v1/ # 登录阿里云Docker Registry [root@docker-ce ~]# docker login --username=aliyun6412327368 registry.cn-hangzhou.aliyuncs.com 用于登录的用户名为阿里云账号全名,密码为开通服务时设置的密码。 # 从Registry中拉取镜像 [root@docker-ce ~]# docker pull registry.cn-hangzhou.aliyuncs.com/hujiaming/jiaming:[镜像版本号] # 将镜像推送到Registry [root@docker-ce ~]# docker login --username=aliyun6412327368 registry.cn-hangzhou.aliyuncs.com [root@docker-ce ~]# docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/hujiaming/jiaming:[镜像版本号] [root@docker-ce ~]# docker push registry.cn-hangzhou.aliyuncs.com/hujiaming/jiaming:[镜像版本号]
5. 容器的日常管理
# 查看本地所有容器 [root@docker-ce ~]# docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES # 启动一个容器 [root@docker-ce ~]# docker run -itd --name webserver -p 80:80 nginx:1.21 1257c74b96d9ebebb34e23844838debb1873d5cec7ae5206551dc2463ca8f94a # 查看正在运行的容器 [root@docker-ce ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1257c74b96d9 nginx:1.21 "/docker-entrypoint.…" 12 seconds ago Up 11 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp webserver # 查看所有的容器包括退出 [root@docker-ce ~]# docker ps -a # 查看所有的容器ID包括退出 [root@docker-ce ~]# docker ps -aq [root@docker-ce ~]# curl -I localhost:80 HTTP/1.1 200 OK Server: nginx/1.21.5 Date: Sun, 09 Jun 2024 05:32:51 GMT Content-Type: text/html Content-Length: 615 Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT Connection: keep-alive ETag: "61cb2d26-267" Accept-Ranges: bytes # 停止/杀死/重启容器 [root@docker-ce ~]# docker container stop/kill/restart webserver webserver [root@docker-ce ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1257c74b96d9 nginx:1.21 "/docker-entrypoint.…" About a minute ago Exited (0) 8 seconds ago webserver # 删除指定容器 [root@docker-ce ~]# docker container rm webserver webserver # 停止所有的容器(慎用) [root@docker-ce ~]# docker stop `docker ps -q` # 删除已经退出的容器(慎用) [root@docker-ce ~]# docker rm `docker ps -a |grep Exited | awk '{print $1}'`
-
CONTAINER ID:
-
-
容器的唯一标识符(ID)。
-
-
IMAGE:
-
-
容器所使用的 Docker 镜像。
-
-
COMMAND:
-
-
容器启动时运行的命令。
-
-
CREATED:
-
-
容器创建的时间。容器是在
35 minutes ago
(35 分钟前)创建的。
-
-
STATUS:
-
-
容器的当前状态和运行时间。状态是
Up 25 minutes
,表示容器已经运行了 25 分钟。
-
-
PORTS:
-
-
容器的端口映射信息。
-
-
-
-
0.0.0.0:80->80/tcp
表示宿主机的80
端口映射到容器的80
端口,使用 TCP 协议。
-
-
-
-
-
:::80->80/tcp
表示在所有 IPv6 地址上,宿主机的80
端口映射到容器的80
端口,使用 TCP 协议。
-
-
-
NAMES:
-
-
容器的名称。
-
这个 Docker 容器使用 nginx:1.21
镜像,在 35 分钟前创建,并已经运行了 25 分钟。它将宿主机的 80
端口映射到容器的 80
端口,容器名称是 webserver
。
5.2 查看容器的日志
# 查看容器的日志 [root@docker-ce ~]# docker logs -f <容器名称/容器ID> --tail 1 #或者: [root@docker-ce ~]# cd /var/lib/docker/containers/<容器id>
5.3 查看容器的IP地址
[root@docker-ce ~]# docker inspect <容器名称/容器ID> | grep -i -w "ipaddress" # 或者 [root@docker-ce ~]# docker inspect --format '{{ .NetworkSettings.IPAddress }}' <容器ID/容器名称>
5.4 进入容器的方法
[root@docker-ce ~]# docker container run -itd --name webserver -p 8080:80 nginx:1.21 da5151566915f5acce7cf8cb75e475b1ca69860e7a0b64e33600b9aba2eeda1b [root@docker-ce ~]# docker container exec -it webserver bash root@da5151566915:/# ls /etc/nginx/conf.d/ default.conf root@da5151566915:/# exit exit
5.5 容器与宿主机间传输文件
# 将容器的文件拷贝至宿主机中 [root@docker-ce ~]# docker cp webserver:/usr/share/nginx/html/index.html /tmp/ Successfully copied 2.05kB to /tmp/ [root@docker-ce ~]# echo "2401" > /tmp/index.html # 宿主机的文件拷贝至容器中 [root@docker-ce ~]# docker cp /tmp/index.html webserver:/usr/share/nginx/html/index.html
5.6 容器的状态(了解,Kubernetes细讲Pod的状态
)
Created:
-
容器已经被创建,但尚未启动。此时,容器处于一种待机状态,等待进一步的操作。
Running:
-
容器正在运行并执行其主要进程。处于此状态的容器可以处理请求和数据。
-
docker ps
可以显示所有正在运行的容器。
Paused:
-
容器的所有进程被暂停(挂起)。此时,容器的 CPU 资源被释放,但内存等其他资源仍然占用。
-
你可以通过
docker pause <container_id>
暂停一个容器,并通过docker unpause <container_id>
恢复它。
Restarting:
-
容器正在重新启动。通常,这种情况发生在容器配置了自动重启策略时,其进程由于某种原因终止并触发重启。
Exited:
-
容器的主进程已经停止运行,并且没有自动重启策略,或者重启策略未能触发。容器退出状态码可以通过
docker inspect
命令查看。 -
docker ps -a
可以显示所有包含已退出容器的列表。
Dead:
-
容器遇到严重问题,无法恢复。通常,这是 Docker 容器处于崩溃状态的标志,需要人工干预来处理这个容器。
5.7 容器的重启策略
略,Kubernetes细讲
5.8 容器重命名(慎用)
# 查看正在运行的容器 [root@docker-ce ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 804a660194cc nginx "/docker-entrypoint.…" 43 seconds ago Up 41 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp webserver02 # 修改容器的名称 [root@docker-ce ~]# docker rename webserver02 webserver01 # 查看正在运行的容器名称 [root@docker-ce ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 804a660194cc nginx "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp webserver01
6. Docker数据卷的管理
简介:Docker 存储卷(Volume)是用于将数据从宿主机持久化到容器中的一种机制。存储卷可以在容器之间共享和重用,即使容器被删除,卷中的数据仍然会保留。这样容器就算重启数据依然存在!数据卷是经过特殊设计的目录,同一个数据卷可以只支持多个容器的访问。可以对数据卷里的内容直接进行修改。数据卷的变化不会影像镜像的更新。数据卷会一直存在,即使挂载数据卷的容器已经被删除。
Docker数据卷的三种方式
-
Volumes:
-
-
Docker 管理宿主机文件系统的一部分(/var/lib/docker/volumes)。保存数据的最佳方式。
-
-
Bind mounts:
-
-
将宿主机的任意位置的文件或者目录挂载到容器中。
-
-
tmpfs:
-
-
挂载存储在主机系统的内存中,而不会写入主机的文件系统。如果不希望将数据持久存储在任何位置,可以使用tmpfs,同时避免写入容器可写层提高性能。
-
6.1 挂载时创建卷
[root@docker-ce ~]# mkdir /opt/docker-volumes [root@docker-ce ~]# docker rm -f $(docker ps -a -q) [root@docker-ce ~]# docker container run -itd --name webserver -p 8080:80 -v /opt/docker-volumes/webserver:/usr/share/nginx/html nginx:1.21 5ca2dc75699e71f87f8281c705876eaa9ddc58b1dc39521726f8b85bef4728dd [root@docker-ce ~]# curl http://localhost:8080 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> <hr><center>nginx/1.21.6</center> </body> </html> [root@docker-ce ~]# echo "hello docker volume" >/opt/docker-volumes/webserver/index.html [root@docker-ce ~]# curl http://localhost:8080 hello docker volume
6.2 挂载指定volume
volume是Docker官方推荐的持久化方案,默认情况下,volume的存储空间来自于宿主机文件系统中的某个目录,如/var/lib/docker/volumes/,docker系统外的程序无权限修改其中的数据。一个volume可以同时供多个container使用,如果没有container使用volume,其不会自动删除,用户需运行docker volume prune明确删除。
如果用户显式创建volume,则需要给其指定一个名称;如果是隐式创建volume,Docker会自动为其分配一个在宿主机范围内唯一的名字。
docker volume create 创建 volume 时,宿主机目录路径必须以/或~/开头,否则 Docker 会将其当成volume而不是bind mount。
如果容器中的目录不存在,docker会自动创建目录;如果容器中的目录已有内容,docker会使用宿主机上目录的内容覆盖容器目录的内容。
# 创建卷 [root@docker-ce ~]# docker volume create nginx-test01 # 查看卷 [root@docker-ce ~]# docker volume ls DRIVER VOLUME NAME local nginx-test01 # 查看卷的详细信息 [root@docker-ce ~]# docker volume inspect nginx-test01 [ { "CreatedAt": "2024-06-11T20:58:55+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/nginx-test01/_data", "Name": "nginx-test01", "Options": null, "Scope": "local" } ] # 查看nginx-test01持久卷的存放位置 [root@docker-ce ~]# ll /var/lib/docker/volumes/nginx-test01/_data/ # 挂载测试卷 [root@docker-ce ~]# docker run -itd --name webserver03 -p 81:80 -v nginx-test01:/usr/share/nginx/html nginx:v1.24.0 b97b8e15658b3bca4dc90bdacb9e4e6fdc2933c9797e2b84be0a15d71e10ebb7 # 查看容器的详细信息 [root@docker-ce ~]# docker inspect webserver03 "Mounts": [ { "Type": "volume", "Name": "nginx-test01", "Source": "/var/lib/docker/volumes/nginx-test01/_data", "Destination": "/usr/share/nginx/html", "Driver": "local", "Mode": "z", "RW": true, "Propagation": "" } # 再次查看nginx-test01的数据卷目录 [root@docker-ce ~]# ll /var/lib/docker/volumes/nginx-test01/_data/ 总用量 8 -rw-r--r-- 1 root root 497 4月 11 2023 50x.html -rw-r--r-- 1 root root 615 4月 11 2023 index.html # 访问容器 [root@docker-ce ~]# curl 172.17.0.3 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> # 修改index.html文件内容 [root@docker-ce ~]# echo "hello volume nginx-test01" > /var/lib/docker/volumes/nginx-test01/_data/index.html # 再次测试 [root@docker-ce ~]# curl 172.17.0.3 hello volume nginx-test01
6.2 Bind Mounts持久化
bind mount持久化方式将宿主机中的文件、目录挂载到容器上,相应文件、目录可以被宿主机读写,也可以被容器读写。
bind mount持久化方式可以将数据存储在宿主机器任何地方,但会依赖宿主机的目录结构,因此不能通过docker CLI直接管理,并且非Docker进程和Docker进程都可以修改。
特点:
-
-
Docker容器与宿主机耦合过于紧密,移植性较差。
-
使用场景:
-
-
container共享宿主机配置文件,如docker将宿主机文件/etc/resolv.conf文件bind mount到容器上,两者会使用相同的DNS服务器。
-
将宿主机开发环境中的源代码或实验结果共享给容器。例如:你在宿主机上进行一个项目Maven的测试,每次你在宿主机上对Maven项目进行了更改后,容器就可以直接获取更改后的结果
-
# 创建数据目录 [root@docker-ce ~]# mkdir /app/html -p # 创建容器并且指定挂载Bind Mounts的卷 [root@docker-ce ~]# docker run -itd --name webserver04 -p 82:80 --mount type=bind,src=/app/html/,dst=/usr/share/nginx/html nginx:v1.24.0 # 查看容器详细信息 [root@docker-ce ~]# docker inspect webserver04 "Mounts": [ { "Type": "bind", "Source": "/app/html/", "Target": "/usr/share/nginx/html" } ], # 测试访问容器 [root@docker-ce ~]# curl 172.17.0.3 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> <hr><center>nginx/1.24.0</center> </body> </html> # 创建发布文件 [root@docker-ce ~]# echo "hello nginx world" > /app/html/index.html # 再次测试 [root@docker-ce ~]# curl 172.17.0.3 hello nginx world
6.2.1 bind mount注意事项
-
宿主机目录路径必须以/或~/开头,否则docker会将其当成是volume 而不是bind mount。
-
如果宿主机上的目录不存在,docker会自动创建目录(多级的绝对路径好像不行)。
-
如果容器中的目录不存在,docker会自动创建目录。
-
如果容器中目录已有内容,那么docker会使用宿主机上目录的内容覆盖容器目录的内容。
6.3 tmpfs mount持久化
tmpfs mount只在Linux主机内存中持久化,是临时性的。当容器停止,tmpfs mount会被移除。
特点:
-
-
只能在Linux主机内存中,不会持久化到磁盘。
-
不支持多容器间共享。
-
场景:
当启动需要访问这些敏感数据的container或者service时,docker会在宿主机上创建一个tmpfs,然后将敏感数据读出写到tmpfs中,再将tmpfs mount到container中,这样能保证数据安全。当容器停止运行时,则相应的tmpfs也从系统中删除。
# 创建容器并且指定他的卷为tmpfs类型 [root@docker-ce ~]# docker run -itd \ --name webserver05 \ --mount type=tmpfs,dst=/usr/share/nginx/html \ nginx:v1.24.0 d8a8122c5eb77e4873866ea33406b7e1ec12eb1ed0ec596bcf6761b78afeacce # 查看容器的相关信息 [root@docker-ce ~]# docker inspect webserver05 "Mounts": [ { "Type": "tmpfs", "Source": "", "Destination": "/usr/share/nginx/html", "Mode": "", "RW": true, "Propagation": "" }
6.4 存储卷的生命周期管理
# 查看所有volume [root@docker-ce ~]# docker volume ls # 查看单个volume详细信息 [root@docker-ce ~]# docker inspect nginx-test01/volume的名称 [ { "CreatedAt": "2024-06-11T20:58:55+08:00", # 创建时间 "Driver": "local", # 卷的驱动类型。local 是默认驱动,表示卷存储在本地宿主机文件系统中。 "Labels": null, # 卷的标签,用于标记或组织卷。可以为空或包含键值对。 "Mountpoint": "/var/lib/docker/volumes/nginx-test01/_data", # 卷在宿主机上的挂载点,即卷数据存储的路径。 "Name": "nginx-test01", # 卷的名称,是在 Docker 中标识这个卷的唯一名称。 "Options": null, # 卷的选项,可以用于配置卷的特定行为。可以为空或包含键值对。 "Scope": "local" # 卷的作用范围。local 表示该卷仅在创建它的宿主机上可用。 } ] # 查看volume是否挂载 [root@docker-ce ~]# docker ps -q | xargs docker inspect --format '{{ .Id }}: {{ .Mounts }}' # 删除未使用的volume(慎用) [root@docker-ce ~]# docker volume prune WARNING! This will remove anonymous local volumes not used by at least one container. Are you sure you want to continue? [y/N] y Total reclaimed space: 0B # 删除单个或多个volume [root@docker-ce ~]# docker volume rm <VOLUME_NAME> <VOLUME_NAME>
7. Docker网络
7.1 Docker网络简介
Docker默认使用桥接模式,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的 Container-IP 直接通信。
Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法直接通过 Container-IP 访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过-p参数来启用,访问容器的时候就通过[宿主机IP]:[端口]访问容器。
# 查看网络列表 [root@docker-ce ~]# docker network list NETWORK ID NAME DRIVER SCOPE 6ea2847e7566 bridge bridge local 6dc3b646ecc7 host host local 8017dbee2836 none null local
-
bridge:
-
-
Docker默认的容器网络驱动。此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及iptables nat 表配置与宿主机通信。
-
-
host:
-
-
容器与主机共享同一Network Namespace,共享同一套网络协议栈、路由表及iptables规则等。容器与主机看到的是相同的网络视图。
-
-
Container:
-
-
创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口。
-
-
None:
-
-
容器没有任何网络配置, 完全隔离的网络环境。
-
7.2 host网络模式配置
相当于Vmware中的桥接模式,与宿主机在同一个网络中,但没有独立IP地址。
Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。
一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、iptable规则等都与其他的Network Namespace隔离。 一个Docker容器一般会分配一个独立的Network Namespace。 但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡、配置自己的IP等,而是使用宿主机的IP和端口。
[root@docker-ce ~]# docker run -itd --name webserver01 --net=host nginx:v1.24.0 [root@docker-ce ~]# docker exec -it webserver01 bash root@docker-ce:/# hostname -I 192.168.174.110 172.17.0.1
7.3 container网络模式配置
该模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。 新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
[root@docker-ce ~]# docker run -id --name webserver03 nginx:v1.24.0 6f9927ffd0090d95be1c4d93f278b40dfa8f0319fa4e8105c42a6446c0eba3c6 [root@docker-ce ~]# docker run -itd --name webserver04 --net container:webserver03 registry.cn-hangzhou.aliyuncs.com/hujiaming/tomcat:9.0.89-jdk8 [root@docker-ce ~]# docker exec -it webserver03 bash root@6f9927ffd009:/# curl localhost:8080 <!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/9.0.89</h3></body></html>
7.4 null网络模式配置
none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。 也就是说,这个Docker容器没有网卡、IP、路由等信息。这种网络模式下容器只有lo回环网络,没有其他网卡。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
8. Dockerfile自动构建docker镜像
root@e05ec18da21c:/# ls / bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var boot docker-entrypoint.d etc lib media opt root sbin sys usr
这些挂载在容器根目录上、用来为容器进程提供隔离后执行环境的文件系统,就是所谓的“容器镜像”。它还有一个更为专业的名字,叫作:rootfs(根文件系统)。以上是一个最常见的 rootfs,或者说容器镜像,会包括如下所示的一些目录和文件,比如 /bin,/etc,/proc 等等;而你进入容器之后执行的 /bin/bash,就是 /bin 目录下的可执行文件,与宿主机的 /bin/bash 完全不同。正是由于 rootfs 的存在,容器才有了一个被反复宣传至今的重要特性:一致性。
什么是容器的“一致性”呢?
由于云端与本地服务器环境不同,应用的打包过程,一直是最“痛苦”的一个步骤。
但有了容器之后,更准确地说,有了容器镜像(即 rootfs)之后,这个问题被非常优雅地解决了。由于 rootfs 里打包的不只是应用,而是整个操作系统的文件和目录,也就意味着,应用以及它运行所需要的所有依赖,都被封装在了一起。但实际上,一个一直以来很容易被忽视的事实是,对一个应用来说,操作系统本身才是它运行所需要的最完整的“依赖库”。有了容器镜像“打包操作系统”的能力,这个最基础的依赖环境也终于变成了应用沙盒的一部分。这就赋予了容器所谓的一致性:无论在本地、云端,还是在一台任何地方的机器上,用户只需要解压打包好的容器镜像,那么这个应用运行所需要的完整的执行环境就被重现出来了。这种深入到操作系统级别的运行环境一致性,打通了应用在本地开发和远端执行环境之间难以逾越的鸿沟。
不过,这时你可能已经发现了另一个非常棘手的问题:难道我每开发一个应用,或者升级一下现有的应用,都要重复制作一次 rootfs 吗?
比如,我现在用Centos操作系统的 ISO 做了一个 rootfs,然后又在里面安装了 Java 环境,用来部署我的 Java 应用。那么,我的另一个同事在发布他的 Java 应用时,显然希望能够直接使用我安装过 Java 环境的 rootfs,而不是重复这个流程。一种比较直观的解决办法是,我在制作 rootfs 的时候,每做一步“有意义”的操作,就保存一个 rootfs 出来,这样其他同事就可以按需求去用他需要的 rootfs 了。但是,这个解决办法并不具备推广性。原因在于,一旦你的同事们修改了这个 rootfs,新旧两个 rootfs 之间就没有任何关系了。这样做的结果就是极度的碎片化。那么,既然这些修改都基于一个旧的 rootfs,我们能不能以增量的方式去做这些修改呢?这样做的好处是,所有人都只需要维护相对于 base rootfs 修改的增量内容,而不是每次修改都制造一个“fork”。
Docker 在镜像的设计中,引入了层(layer)的概念。也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量rootfs。
第**一部分:只读层。**
它是这个容器的 rootfs 最下面的五层,对应的正是 ubuntu:latest 镜像的五层。可以看到,它们的挂载方式都是只读的(ro+wh,即 readonly+whiteout,至于什么是 whiteout?)。
第二部分:可读写层。 它是这个容器的 rootfs 最上面的一层,它的挂载方式为:rw,即 read write。在没有写入文件之前,这个目录是空的。而一旦在容器里做了写操作,你修改产生的内容就会以增量的方式出现在这个层中。
这个可读写层的作用,就是专门用来存放你修改 rootfs 后产生的增量,无论是增、删、改,都发生在这里。而当我们使用完了这个被修改过的容器之后,还可以使用 docker commit 和 push 指令,保存这个被修改过的可读写层,并上传到 Docker Hub 上,供其他人使用;而与此同时,原先的只读层里的内容则不会有任何变化。这,就是增量rootfs 的好处。
第三部分:Init 层。
它是一个以“-init”结尾的层,夹在只读层和读写层之间。Init 层是 Docker 项目单独生成的一个内部层,专门用来存放 /etc/hosts、/etc/resolv.conf 等信息。
需要这样一层的原因是,这些文件本来属于只读的 Ubuntu 镜像的一部分,但是用户往往需要在启动容器时写入一些指定的值比如 hostname,所以就需要在可读写层对它们进行修改。
可是,这些修改往往只对当前的容器有效,我们并不希望执行 docker commit 时,把这些信息连同可读写层一起提交掉。所以,Docker 做法是,在修改了这些文件之后,以一个单独的层挂载了出来。而用户执行 docker commit 只会提交可读写层,所以是不包含这些内容的。
[root@docker-ce ~]# docker inspect eeb6ee3f44bd | grep -i workdir "WorkDir": "/var/lib/docker/overlay2/a98f64963021b19fb2703798c7016f35d15d8f4dcf127f732eb0e38d27ca1583/work" [root@docker-ce ~]# ls /var/lib/docker/overlay2/a98f64963021b19fb2703798c7016f35d15d8f4dcf127f732eb0e38d27ca1583/diff
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和操作命令;每一条指令构建一层镜像,因此每一条指令的内容,就是描述该层镜像应当如何构建(也就是你要执行的操作命令)。
8.1 Dockerfile指令集
FROM 这个镜像的妈妈是谁?(指定基础镜像) MAINTAINER 告诉别人,谁负责养它?(指定维护者信息,可以没有)LABLE key=values RUN 你想让它干啥(在命令前面加上RUN即可) ADD/COPY 给它点创业资金(COPY文件,会自动解压) WORKDIR 我是cd,今天刚化了妆(设置当前工作目录) VOLUME 给它一个存放行李的地方(设置卷,挂载主机目录) EXPOSE 它要打开的门是啥(指定对外的端口) CMD 奔跑吧,兄弟!(指定容器启动后的要干的事情) ENTRYPOINT 容器启动命名 ENV 环境变量 USER 切换用户
8.2 单阶段构建镜像
# 下载基础镜像 [root@docker-ce ~]# docker pull registry.cn-hangzhou.aliyuncs.com/hujiaming/node:16.15.0 16.15.0: Pulling from library/node 85bed84afb9a: Already exists 5fdd409f4b2b: Already exists fa3069e6cecf: Already exists 4ee16f45eff9: Already exists 2cee68386155: Already exists 0aa7befa4a1b: Already exists d89fa2ee2a9e: Already exists fea81c9d708c: Already exists 69c8c6687712: Already exists Digest: sha256:a6c217d7c8f001dc6fc081d55c2dd7fb3fefe871d5aa7be9c0c16bd62bea8e0c Status: Downloaded newer image for node:16.15.0 docker.io/library/node:16.15.0 # 创建存放目录 [root@docker-ce ~]# mkdir /opt/docker-images/webserver-vue -p # 编写Dockerfile [root@docker-ce ~]# vim /opt/docker-images/webserver/Dockerfile FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/node:16.15.0 RUN git clone https://gitee.com/mirschao/webserver-vue.git WORKDIR webserver-vue RUN npm install EXPOSE 8080 CMD ["npm", "run", "serve"] # 切换目录 [root@docker-ce ~]# cd /opt/docker-images/webserver # 构建镜像 [root@docker-ce webserver]# docker build -t webserver:v1.0 . # 查看镜像 [root@docker-ce ~]# docker images | grep -i -w webserver webserver v1.0 60f727ab4ebe 5 minutes ago 1.05GB # 运行镜像 [root@docker-ce ~]# docker run -itd --name webserver10 -p 8080:8080 webserver:v1.0 [root@docker-ce ~]# docker ps | grep -w webserver10 97e8ab5a7cfb webserver:v1.0 "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp webserver10
# 拉取基础镜像 [root@docker-ce ~]# docker pull registry.cn-hangzhou.aliyuncs.com/hujiaming/jre:8u211-data # 拉取项目 [root@docker-ce ~]# git clone https://gitee.com/rulaipo/java-test.git # 切换目录 [root@docker-ce ~]# cd java-test/ [root@docker-ce java-test]# ls Dockerfile spring-cloud-eureka-0.0.1-SNAPSHOT.jar # 查看Dockerfile [root@docker-ce java-test]# cat Dockerfile # 基础镜像可以按需修改,可以更改为公司自有镜像 FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/jre:8u211-data # 切换工作目录 WORKDIR /app # jar 包名称改成实际的名称,本示例为 spring-cloud-eureka-0.0.1-SNAPSHOT.jar COPY spring-cloud-eureka-0.0.1-SNAPSHOT.jar /app # 端口 EXPOSE 8761 # 启动 Jar 包 CMD ["java","-jar","spring-cloud-eureka-0.0.1-SNAPSHOT.jar"] # 构建镜像 [root@docker-ce java-test]# docker build -t java-test:v1.0 . [+] Building 0.1s (8/8) FINISHED docker:default => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 446B 0.0s => [internal] load metadata for registry.cn-hangzhou.aliyuncs.com/hujiaming/jre:8u211-data 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [1/3] FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/jre:8u211-data 0.0s => [internal] load build context 0.0s => => transferring context: 62B 0.0s => CACHED [2/3] WORKDIR /app 0.0s => CACHED [3/3] COPY spring-cloud-eureka-0.0.1-SNAPSHOT.jar /app 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:8975f4ec6299fa9f5679551075aa4235144020ce5826bbd2a5ce5dca63b98a7f 0.0s => => naming to docker.io/library/java-test:v1.0 0.0s # 查看镜像 [root@docker-ce java-test]# docker images | grep java java-test v1.0 8975f4ec6299 11 minutes ago 174MB # 启动容器 [root@docker-ce java-test]# docker run -itd --name java-test -p 8761:8761 java-test:v1.0 #查看容器 [root@docker-ce java-test]# docker ps | grep java 8dcb97455b5d java-test:v1.0 "java -jar spring-cl…" 9 minutes ago Up 9 minutes 0.0.0.0:8761->8761/tcp, :::8761->8761/tcp java-test
测试访问
8.3 多阶段构建镜像
# 拉取代码 $ git clone https://github.com/hlions/webserver-vue.git $ cd webserver-vue $ cat Dockerfile FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/node:16.15.0 AS builder COPY ./ /app WORKDIR /app RUN npm install && npm run build FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:v1.24.0 RUN mkdir /app COPY --from=builder /app/dist /app COPY nginx.conf /etc/nginx/nginx.conf $ cat nginx.conf user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root /app; index index.html; try_files $uri $uri/ /index.html; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } } $ docker build -t webserver:v1.1 .
8.4 Dockerfile优化
-
多阶段构建镜像
使用多个FROM指令,将构建过程分为多个阶段;每个阶段生成一个独立的镜像,后续阶段可以使用前面阶段生成的镜像作为基础镜像。多阶段构建可以有效减小最终镜像的体积,分离构建环境和运行环境多阶段构建。它允许在一个Dockerfile中使用多个FROM指令,每个FROM指令都可以使用不同的基础镜像,并且每个FROM指令都开始一个新的构建阶段。多阶段构建的主要目的是将构建过程分为多个独立的阶段,每个阶段生成一个独立的镜像。后续阶段可以使用前面阶段生成的镜像作为基础镜像,从而实现构建环境和运行环境的分离,有效减小最终镜像的体积。
-
把不需要的命令输出丢入/dev/null
对于某些命令的标准输出和错误输出,如果不需要,可以重定向到/dev/null,避免不必要的输出写入镜像层,减小镜像体积。使用command > /dev/null 2>&1的形式进行重定向
FROM ubuntu:latest RUN apt-get update > /dev/null 2>&1 && \ apt-get install -y nginx > /dev/null 2>&1
-
使用普通用户运行
在Dockerfile中使用USER指令切换到非root用户,以最小权限原则运行应用,提高安全性。确保应用程序有足够权限访问所需资源
FROM node:14 RUN useradd zhangsan USER zhangsan
-
选择合适的基础镜像
-
Alpine Linux是一个面向安全的轻型Linux发行版,镜像体积小,相比Ubuntu、CentOS等,Alpine Linux更适合作为基础镜像,务必评估Alpine Linux与应用程序的兼容性。
-
减少镜像层数
-
一次RUN指令形成新的一层,尽量Shell命令都写在一行,减少镜像层。
FROM ubuntu:latest RUN apt-get update && \ apt-get install -y nginx
9. HarBor私有镜像仓库
Harbor 是一个开源的企业级容器镜像仓库,由 VMware 开发并捐赠给 CNCF(Cloud Native Computing Foundation)。它不仅提供了存储和分发 Docker 镜像的基本功能,还增加了很多企业级功能,使得它在安全性、管理性和可扩展性方面表现优异。Docker容器应用的开发和运行离不开可靠的镜像管理。
9.1 安装docker-compose
$ wget https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64 $ mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose $ chmod a+x /usr/local/bin/docker-compose $ docker-compose version Docker Compose version v2.15.1
9.2 安装Harbor
# 上传安装包 [root@docker-ce opt]# ls harbor-offline-installer-v2.7.1.tgz # 解压安装包 [root@docker-ce opt]# tar xf harbor-offline-installer-v2.7.1.tgz # 切换目录 [root@docker-ce opt]# cd harbor # 拉取harbor所需的镜像 [root@docker-ce harbor]# docker load -i harbor.v2.7.1.tar.gz # 修改配置文件 [root@docker-ce harbor]# cp harbor.yml.tmpl harbor.yml [root@docker-ce harbor]# vim harbor.yml ... hostname: harbor.tanke.love http: port: 80 https: port: 443 certificate: /tmp/fullchain.pem private_key: /tmp/privkey.pem harbor_admin_password: 12345 data_volume: /data/harbor location: /var/log/harbor ... # 创建数据存放目录 [root@docker-ce harbor]# mkdir /data/harbor -p # 添加权限 [root@docker-ce harbor]# chmod 777 -R /data/ /var/log/harbor/ # 生成和检查Harbor的配置文件,并确保所有必要的依赖项和环境都已准备就绪。 [root@docker-ce harbor]# ./prepare prepare base dir is set to /opt/harbor Generated configuration file: /config/portal/nginx.conf Generated configuration file: /config/log/logrotate.conf Generated configuration file: /config/log/rsyslog_docker.conf Generated configuration file: /config/nginx/nginx.conf Generated configuration file: /config/core/env Generated configuration file: /config/core/app.conf Generated configuration file: /config/registry/config.yml Generated configuration file: /config/registryctl/env Generated configuration file: /config/registryctl/config.yml Generated configuration file: /config/db/env Generated configuration file: /config/jobservice/env Generated configuration file: /config/jobservice/config.yml Generated and saved secret to file: /data/secret/keys/secretkey Successfully called func: create_root_cert Generated configuration file: /compose_location/docker-compose.yml Clean up the input dir # 查看镜像 [root@docker-ce harbor]# docker-compose images CONTAINER REPOSITORY TAG IMAGE ID SIZE harbor-core goharbor/harbor-core v2.7.1 49d6c8a15d6c 215MB harbor-db goharbor/harbor-db v2.7.1 b3f8d9d6c213 174MB harbor-jobservice goharbor/harbor-jobservice v2.7.1 829d13e6aae7 252MB harbor-log goharbor/harbor-log v2.7.1 eeb93d98a358 133MB harbor-portal goharbor/harbor-portal v2.7.1 fe05b1b0bcfd 135MB nginx goharbor/nginx-photon v2.7.1 e98018335c0d 126MB redis goharbor/redis-photon v2.7.1 229dd1844a26 127MB registry goharbor/registry-photon v2.7.1 9d50b10d3700 78.1MB registryctl goharbor/harbor-registryctl v2.7.1 9b2219d529c8 140MB # 安装和启动 Harbor 的主要安装脚本 [root@docker-ce harbor]# ./install.sh [Step 5]: starting Harbor ... WARN[0000] /opt/harbor/docker-compose.yml: `version` is obsolete [+] Running 10/10 ✔ Network harbor_harbor Created 0.1s ✔ Container harbor-log Started 0.9s ✔ Container registry Started 2.3s ✔ Container registryctl Started 1.9s ✔ Container redis Started 2.0s ✔ Container harbor-portal Started 2.3s ✔ Container harbor-db Started 2.3s ✔ Container harbor-core Started 2.9s ✔ Container nginx Started 4.1s ✔ Container harbor-jobservice Started 4.0s ✔ ----Harbor has been installed and started successfully.---- # 查看启动的容器 [root@docker-ce harbor]# docker-compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS harbor-core goharbor/harbor-core:v2.7.1 "/harbor/entrypoint.…" core 11 minutes ago Up 11 minutes (healthy) harbor-db goharbor/harbor-db:v2.7.1 "/docker-entrypoint.…" postgresql 11 minutes ago Up 11 minutes (healthy) harbor-jobservice goharbor/harbor-jobservice:v2.7.1 "/harbor/entrypoint.…" jobservice 11 minutes ago Up 11 minutes (healthy) harbor-log goharbor/harbor-log:v2.7.1 "/bin/sh -c /usr/loc…" log 11 minutes ago Up 11 minutes (healthy) 127.0.0.1:1514->10514/tcp harbor-portal goharbor/harbor-portal:v2.7.1 "nginx -g 'daemon of…" portal 11 minutes ago Up 11 minutes (healthy) nginx goharbor/nginx-photon:v2.7.1 "nginx -g 'daemon of…" proxy 11 minutes ago Up 11 minutes (healthy) 0.0.0.0:80->8080/tcp, :::80->8080/tcp, 0.0.0.0:443->8443/tcp, :::443->8443/tcp redis goharbor/redis-photon:v2.7.1 "redis-server /etc/r…" redis 11 minutes ago Up 11 minutes (healthy) registry goharbor/registry-photon:v2.7.1 "/home/harbor/entryp…" registry 11 minutes ago Up 11 minutes (healthy) registryctl goharbor/harbor-registryctl:v2.7.1 "/home/harbor/start.…" registryctl 11 minutes ago Up 11 minutes (healthy) # 登录镜像仓库 [root@docker-ce harbor]# docker login https://harbor.tanke.love
9.3 docker-compose介绍
Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过使用 YAML 文件,你可以配置应用需要的所有服务,然后使用一个命令就能创建并启动这些服务。Docker Compose 非常适合用来管理复杂的应用程序,其中包含多个相互依赖的服务。
9.2 编排启动镜像
$ vim /opt/docker-compose/wordpress/docker-compose.yaml services: db: image: mysql:5.7 volumes: - /data/db_data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: somewordpress MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress wordpress: depends_on: - db image: wordpress:latest volumes: - /data/web_data:/var/www/html ports: - "8000:80" restart: always environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress $ cd /opt/docker-compose/wordpress # 启动服务 $ docker-compose up -d [+] Running 34/34 ⠿ wordpress Pulled 181.8s ⠿ 1fe172e4850f Already exists 0.0s ⠿ 012a3732d045 Pull complete 37.3s ⠿ 43092314d50d Pull complete 71.3s ⠿ 4f615e42d863 Pull complete 71.4s ⠿ cd39010a4efc Pull complete 72.2s ⠿ d983c9ce24de Pull complete 72.3s ⠿ ecbdd59ae430 Pull complete 75.8s ⠿ 9d02b88c8618 Pull complete 79.5s ⠿ 50a246031d43 Pull complete 79.6s ⠿ a6c0267e6c34 Pull complete 84.0s ⠿ 787ca6348cef Pull complete 91.5s ⠿ da8ad43595e2 Pull complete 97.4s ⠿ e191f9e80e29 Pull complete 97.4s ⠿ fed8d3fd90f9 Pull complete 106.6s ⠿ 9ffdaa9000ed Pull complete 129.4s ⠿ 5774aeca6412 Pull complete 129.5s ⠿ 6978431bb9e2 Pull complete 129.6s ⠿ fb4d3fb05351 Pull complete 129.7s ⠿ 23d3af42839e Pull complete 133.6s ⠿ a5b33728e4a6 Pull complete 133.7s ⠿ 766e2b674cd0 Pull complete 136.9s ⠿ db Pulled 175.8s ⠿ 4be315f6562f Pull complete 18.4s ⠿ 96e2eb237a1b Pull complete 18.5s ⠿ 8aa3ac85066b Pull complete 18.8s ⠿ ac7e524f6c89 Pull complete 18.9s ⠿ f6a88631064f Pull complete 19.0s ⠿ 15bb3ec3ff50 Pull complete 35.7s ⠿ ae65dc337dcb Pull complete 35.8s ⠿ a4c4c43adf52 Pull complete 35.9s ⠿ c6cab33e8f91 Pull complete 140.1s ⠿ 2e1c4f2c43f6 Pull complete 140.2s ⠿ 2e5ee322af48 Pull complete 140.2s [+] Running 3/3 ⠿ Network root_default Created 0.1s ⠿ Container root-db-1 Started 0.8s ⠿ Container root-wordpress-1 Started 1.1s