1 Docker 介绍
- 什么是Docker
- Docker架构与内部组件
- Docker有什么优点
- 虚拟机与容器区别
- 应用场景
1.1 什么是Docker
Docker是一个开源的应用容器引擎,基于LXC(Linux Container)内核虚拟化技术实现,提供一系列更强的功能,比如镜像、Dockerfile 等。
Docker 理念是将应用及依赖包打包到一个可移植的容器中,可发布到任意Linux发行版Docker引擎上。使用沙箱机制运行程序,程序之间相互隔离。
Docker 使用 Go 语言开发的。
1.2 Docker 架构与内部组件
Docker 采用 C/S 架构,Docker daemon 作为服务端接受客户端请求,并处理这些请求,比如创建,运行容器等。客户端为用户提供一系列指令与 Docker daemon交互。(Docker daemon 守护进程是服务端)
- LXC:Linux Container,Linux 容器技术,共享内核,容器共享宿主机资源,使用 namespace 和 cgroups 对资源限制与隔离。
- Cgroups:Control Groups,Linux 内核提供的一种限制单进程或者多进程资源使用的机制;比如CPU,内存等资源的使用限制。
- NameSpace:命名空间,也称名称空间,Linux内核提供的一种限制单进程或者多进程资源隔离的机制;一个进程可以属于多个命名空间。Linux 内核提供了六种NameSpace:UTS,IPC,PID,Network,Mount 和 User。
namespace | 系统调用参数 | 隔离内容 |
---|---|---|
UTS | CLONE_NEWUTS | 主机和域名 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 |
PID | CLONE_NEWPID | 进程编号 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口等 |
Mount | CLONE_NEWNS | 挂载点(文件系统) |
User | CLONE_NEWUSER | 用户和用户组 |
- UFS:UnionFS,联合文件系统,支持将不同位置的目录挂载到同一个虚拟文件系统中,形成一种分层的模型;成员目录成为虚拟文件系统的分支(branch)
- AUFS:Advanced Multi Layered Unification Filesystem,高级多层统一文件系统,是UFS的一种,每个branch可以指定 readonly(ro,只读),readwrite(读写)和whiteout-able(wo隐藏)权限;一般情况下,AUFS只有最上层branch才有读写权限,其他的branch均为只读权限。
Linux distribution | Recommended storage drivers | Alternative drivers |
---|---|---|
Ubuntu | overlay2 | overlay¹, devicemapper², aufs³, zfs, vfs |
Debian | overlay2 | overlay¹, devicemapper², aufs³, vfs |
CentOS | overlay2 | overlay¹, devicemapper², zfs, vfs |
Fedora | overlay2 | overlay¹, devicemapper², zfs, vfs |
SLES 15 | overlay2 | overlay¹, devicemapper², vfs |
RHEL | overlay2 | overlay¹, devicemapper², vfs |
官方站点:https://docs.docker.com/storage/storagedriver/select-storage-driver/
:::color1 Ubuntu 默认使用存储驱动是 AUFS,CentOS 默认使用存储驱动是 DeviceMapper。
:::
$ docker --help
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Options:
--config string Location of client config files (default "/root/.docker")
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set
with "docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/root/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Management Commands:
app* Docker App (Docker Inc., v0.9.1-beta3)
builder Manage builds
buildx* Build with BuildKit (Docker Inc., v0.6.3-docker)
config Manage Docker configs
container Manage containers
context Manage contexts
image Manage images
manifest Manage Docker image manifests and manifest lists
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
scan* Docker Scan (Docker Inc., v0.21.0)
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
Commands:
attach Attach local standard input, output, and error streams to a running container
build Build an image from a Dockerfile
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes to files or directories on a container's filesystem
events Get real time events from the server
exec Run a command in a running container
export Export a container's filesystem as a tar archive
history Show the history of an image
images List images
import Import the contents from a tarball to create a filesystem image
info Display system-wide information
inspect Return low-level information on Docker objects
kill Kill one or more running containers
load Load an image from a tar archive or STDIN
login Log in to a Docker registry
logout Log out from a Docker registry
logs Fetch the logs of a container
pause Pause all processes within one or more containers
port List port mappings or a specific mapping for the container
ps List containers
pull Pull an image or a repository from a registry
push Push an image or a repository to a registry
rename Rename a container
restart Restart one or more containers
rm Remove one or more containers
rmi Remove one or more images
run Run a command in a new container
save Save one or more images to a tar archive (streamed to STDOUT by default)
search Search the Docker Hub for images
start Start one or more stopped containers
stats Display a live stream of container(s) resource usage statistics
stop Stop one or more running containers
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
version Show the Docker version information
wait Block until one or more containers stop, then print their exit codes
Run 'docker COMMAND --help' for more information on a command.
To get more help with docker, check out our guides at https://docs.docker.com/go/guides/
1.3 Docker 有什么优点
- 持续集成
在项目快速迭代的情况下,轻量级容器对项目快速构建,环境打包,发布等流程就能提高工作效率
- 版本控制
每个镜像就是一个版本,在一个项目多个版本时可以很方便管理
- 可移植性
容器可以移动到任意一台Docker主机上,而不需要过多关注底层系统
- 标准化
应用程序环境及依赖,操作系统等问题,增加了生产环境故障率,容器保证了所有配置,环境依赖始终不变
- 隔离性与安全
容器之间的进程是相互隔离的,一个容器出现问题不会影响其他容器
1.4 虚拟机与容器区别
虚拟化部署:
虚拟化技术允许你在单个物理服务器的 CPU 上运行多台虚拟机(VM)。 虚拟化能使应用程序在不同 VM 之间被彼此隔离,且能提供一定程度的安全性, 因为一个应用程序的信息不能被另一应用程序随意访问。 虚拟化技术能够更好地利用物理服务器的资源,并且因为可轻松地添加或更新应用程序, 而因此可以具有更高的可伸缩性,以及降低硬件成本等等的好处。 每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统(OS)。容器部署:
容器类似于 VM,但是更宽松的隔离特性,使容器之间可以共享操作系统(OS)。 因此,容器比起 VM 被认为是更轻量级的。且与 VM 类似,每个容器都具有自己的文件系统、CPU、内存、进程空间等。 由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。 容器因具有许多优势而变得流行起来。下面列出的是容器的一些好处:- 敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
- 持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性), 提供可靠且频繁的容器镜像构建和部署。
- 关注开发与运维的分离:在构建、发布时创建应用程序容器镜像,而不是在部署时, 从而将应用程序与基础架构分离。
- 可观察性:不仅可以显示 OS 级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。
- 跨开发、测试和生产的环境一致性:在笔记本计算机上也可以和在云中运行一样的应用程序。
- 跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、 Google Kubernetes Engine 和其他任何地方运行。
- 以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
- 松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分, 并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。
- 资源隔离:可预测的应用程序性能。
- 资源利用:高效率和高密度。
:::color1
容器和虚拟机的区别 一、容器容器是一个不依赖于操作系统,运行应用程序的环境。它通过Linux的Namespaces和Cgroups技术对应用程序进程进行隔离和限制的,Namespace的作用是隔离,它让应用进程只能看到该Namespace内的世界;而Cgroups 的作用是限制分配给进程的宿主机资源。但对于宿主机来说,这些被“隔离”了的进程跟其他进程并没有太大区别。容器只是运行在宿主机上的一种特殊的进程,多个容器之间使用的还是同一个宿主机的操作系统内核。 二、虚拟机
虚拟机(VM)是计算机系统的仿真。简而言之,它可以在实际上是一台计算机的硬件上运行看起来很多单独的计算机。 操作系统(OS)及其应用程序从单个主机服务器或主机服务器池共享硬件资源。每个虚拟机都需要自己的底层操作系统,并且硬件是虚拟化的。管理程序或虚拟机监视器是创建和运行虚拟机的软件,固件或硬件。它位于硬件和虚拟机之间,是虚拟化服务器所必需的。 三、容器和虚拟机的区别
- 容器与虚拟机拥有着类似的使命:对应用程序及其关联性进行隔离,从而构建起一套能够随处运行的自容纳单元。此外,容器与虚拟机还摆脱了对物理硬件的需求,允许我们更为高效地使用计算资源,从而提升能源效率与成本效益。
- 虚拟机会将虚拟硬件、内核(即操作系统)以及用户空间打包在新虚拟机当中,虚拟机能够利用“虚拟机管理程序”运行在物理设备之上。虚拟机依赖于hypervisor,其通常被安装在“裸金属”系统硬件之上,这导致hypervisor在某些方面被认为是一种操作系统。一旦 hypervisor安装完成, 就可以从系统可用计算资源当中分配虚拟机实例了,每台虚拟机都能够获得唯一的操作系统和负载(应用程序)。简言之,虚拟机先需要虚拟一个物理环境,然后构建一个完整的操作系统,再搭建一层Runtime,然后供应用程序运行。
- 对于容器环境来说,不需要安装主机操作系统,直接将容器层安装在主机操作系统之上。在安装完容器层之后,就可以从系统可用计算资源当中分配容器实例了,并且企业应用可以被部署在容器当中。但是,每个容器化应用都会共享相同的操作系统(单个主机操作系统)。容器可以看成一个装好了一组特定应用的虚拟机,它直接利用了宿主机的内核,抽象层比虚拟机更少,更加轻量化,启动速度极快。
- 相比于虚拟机,容器拥有更高的资源使用效率,因为它并不需要为每个应用分配单独的操作系统——实例规模更小、创建和迁移速度也更快。这意味相比于虚拟机,单个操作系统能够承载更多的容器。云提供商十分热衷于容器技术,因为在相同的硬件设备当中,可以部署数量更多的容器实例。此外,容器易于迁移,但是只能被迁移到具有兼容操作系统内核的其他服务器当中,这样就会给迁移选择带来限制。
:::
以KVM举例,Docker对比
- 启动时间
Docker秒级,KVM分钟级。
- 轻量级
容器镜像大小通常以M为单位,虚拟机以G为单位。容器资源占用小,要比虚拟机部署更快速。
- 性能
容器共享宿主机内核,系统级虚拟化,占用资源少,没有Hypervisor层开销,容器性能基本接近物理机;
虚拟机需要Hypervisor层支持,虚拟化一些设备,具有完整的GuestOS,虚拟化开销大,因而降低性能,没有容器性能好。
- 安全性
由于共享宿主机内核,只是进程级隔离,因此隔离性和稳定性不如虚拟机,容器具有一定权限访问宿主机内核,存在一定安全隐患。
- 使用要求
KVM基于硬件的完全虚拟化,需要硬件CPU虚拟化技术支持;若无法支持硬件的虚拟化技术,那么就有可能无法安装64位的操作系统,64位的操作系统需要系统级虚拟化的支持。
容器共享宿主机内核,可运行在主流的Linux发行版,不用考虑CPU是否支持虚拟化技术。
1.5 应用场景
- 应用打包与部署自动化
构建标准化的运行环境;
现在大多方案是在物理机和虚拟机上部署运行环境,面临问题是环境杂乱、完整性迁移难度高等问题,容器即开即用。
- 自动化测试和持续集成/部署
自动化构建镜像和良好的REST API
,能够很好的集成到持续集成/部署环境来。
- 部署与弹性扩展
由于容器是应用级的,资源占用小,弹性扩展部署速度要更快。
- 微服务
Docker这种容器的隔离技术,正式应对了微服务理念,将业务模块放到容器中运行,容器的可复用性大大增加了业务模块扩展性。
2 安装与使用
2.1 安装 Docker
- CentOS7
# 安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加IDocker软件包源
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 更新yum包索引
yum makecache fast
# 安装Docker CE
yum install docker-ce
# 启动
systemctl start docker
# 测试
docker run hello-world
docker version
# 卸载
yum remove docker-ce
rm -rf /var/lib/docker
- Ubuntu14.06 / 16.04
# 安装证书
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
# 添加IDocker软件包源
$sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# 更新apt包索引
$sudo apt-get update
# 安装Docker CE
$ sudo apt-get install docker-ce
# 测试
sudo docker run hello-world
sudo docker version
# 卸载Docker CE
$ sudo apt-get purge docker-ce
$ sudo rm -rf /var/lib/docker
CentOS 使用脚本运行安装 Docker
#!/bin/bash
# Shell ENV
DOCKER_VERSION="20.10.7"
CONTAINERD_VERSION="1.4.6"
# step 1: 安装必要的一些系统工具
echo -e "==> 安装必要的系统工具"
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
echo -e "==> 添加软件源信息"
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3
echo -e "==> 修改配置文件"
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# Step 4: 更新并安装Docker-CE
echo -e "==> 安装更新Docker"
sudo yum makecache fast
# containerd.io Docker运行时环境
sudo yum -y install docker-ce-${DOCKER_VERSION} docker-ce-cli-${DOCKER_VERSION} containerd.io-${CONTAINERD_VERSION}
# Step 5: 配置加速器以及docker参数
echo -e "==> 配置加速器以及docker参数"
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com","http://hub-mirror.c.163.com","https://mirror.ccs.tencentyun.com","http://f1361db2.m.daocloud.io"],
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
# Step 6: 加载服务
echo -e "==> 加载服务"
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl enable docker
# 查看Docker服务信息
echo -e "==> 查看Docker服务信息"
docker info
2.2 镜像
2.2.1 什么是镜像?
简单说,Docker镜像是一个不包含Linux内核而又精简的Linux操作系统。镜像从哪里来?
Docker Hub是由Docker公司负责维护的公共注册中心,包含大量的容器镜像,Docker工具默认从这个公共镜像库下载镜像。https://hub.docker.com/explore
默认是国外的源,下载会慢,可以国内的源提供下载速度:
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
2.2.2 工作过程?
当我们启动一个新的容器时,Docker会加载只读镜像,并在其之上添加一个读写层,并将镜像中的目录复制一份到/var/lib/docker/aufs/mnt/
容器ID为目录下,我们可以使用chroot进入此目录。如果运行中的容器修改一个已经存在的文件,那么会将该文件从下面的只读层复制到读写层,只读层的这个文件就会覆盖,但还存在,这就实现了文件系统隔离,当删除容器后.读写层的数据将会删除,只读镜像不变。
2.2.3 镜像文件存储结构?
docker相关文件存放在:/var/lib/docker
目录下
:::color1 /var/lib/docker/aufs/diff/ #每层与其父层之间的文件差异
/var/lib/docker/aufs/layers/ #每层一个文件,记录其父层一直到根层之间的ID,大部分文件的最后一行都已,表示继承来自同一层
/var/lib/docker/aufs/mnt/ #联合挂载点,从只读层复制到最上层可读写层的文件系统数据
:::
在建立镜像时,每次写操作,都被视作一种增量操作,即在原有的数据层上添加一个新层;所以一个镜像会有若干个层组成。每次commit提交就会对产生一个ID,就相当于在上一层有加了一层,可以通过这个ID对镜像回滚。
目录最新的联合文件系统是:Overlay2 的文件系统
$ docker info | grep "Storage Driver:"
Storage Driver: overlay2
2.2.4 镜像管理命令
search 在Docker Hub上搜索镜像
pull 从注册表中提取镜像或存储库
push 将镜像或存储库推送到注册表
images 列出镜像
commit 根据容器的更改创建一个新镜像
build 从Dockerfile构建映像
rmi 删除一个或多个镜像
export 将容器的文件系统导出为tar归档文件
import 从tarball导入内容以创建文件系统镜像
save 将一个或多个镜像保存到TAR存档(默认情况下以流形式传输到STDOUT)
load 从TAR存档或STDIN加载镜像
范例:镜像管理命令
# 搜索镜像
$ docker search --limit 4 nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 17706 [OK]
bitnami/nginx Bitnami nginx Docker Image 143 [OK]
ubuntu/nginx Nginx, a high-performance reverse proxy & we… 67
bitnami/nginx-ingress-controller Bitnami Docker Image for NGINX Ingress Contr… 22 [OK]
# 拉取镜像
$ docker pull hello-world
# 查看镜像
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 14 months ago 13.3kB
$ docker images --no-trunc
# 运行Ubuntu容器
$ docker run -it --name ubuntu-node1 -d ubuntu:22.04 /bin/bash
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f1998d28779 ubuntu:22.04 "/bin/bash" 3 seconds ago Up 2 seconds ubuntu-node1
# 进入到Ubuntu容器后创建文件/home/a.txt && /home/b.txt
$ docker exec -it ubuntu-node1 /bin/bash
root@2f1998d28779:/# echo "ubuntu content a.txt" > /home/a.txt
root@2f1998d28779:/# echo "ubuntu content b.txt" > /home/b.txt
root@2f1998d28779:/# exit
# 将该容器制作成镜像
$ docker commit -a "zhongzhiwei <zhongzhiwei@kubesphere.io>" -m "添加/home/{a,b}.txt" ubuntu-node1 my-ubuntu:v1.0
# 新镜像运行成容器,查看/home/下是否有新的a.txt和b.txt文件
$ docker run -it --rm --name myubuntu-node1 my-ubuntu:v1.0 /bin/bash
root@c2e74ff511a8:/# ls -l /home/
total 8
-rw-r--r-- 1 root root 21 Nov 21 06:29 a.txt
-rw-r--r-- 1 root root 21 Nov 21 06:29 b.txt
root@c2e74ff511a8:/# exit
# 查看镜像的历史信息
$ docker history my-ubuntu:v1.0
IMAGE CREATED CREATED BY SIZE COMMENT
9bd59fb3d16c 11 seconds ago /bin/bash 144B 添加/home/{a,b}.txt
9d28ccdc1fc7 11 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 11 months ago /bin/sh -c #(nop) ADD file:1e60bfbe5a32672bf… 76.3MB
# 删除镜像
$ docker rmi -f my-ubuntu:v1.0
范例:export 和 import 的使用
# 将容器的文件系统导入到 tar 包中
$ docker export ubuntu-node1 -o myubuntu.tar
$ ls -lh myubuntu.tar
-rw------- 1 root root 76M Nov 21 14:37 myubuntu.tar
# 将tar包导入到Docker本地的镜像中
$ docker import myubuntu.tar myubuntu-import:v1.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myubuntu-import v1.0 7b453b11b8e3 4 seconds ago 76.3MB
# 使用新的镜像运行成容器
$ docker run -it --rm --name myubuntu-node myubuntu-import:v1.0 /bin/bash
root@6df0030e8697:/# ls -l /home/
total 8
-rw-r--r-- 1 root root 21 Nov 21 06:29 a.txt
-rw-r--r-- 1 root root 21 Nov 21 06:29 b.txt
范例:save 和 load 的使用
# 只打包不压缩
$ docker save -o ubuntu2204.tar ubuntu:22.04
$ ls -lh ubuntu2204.tar
-rw------- 1 root root 76M Nov 21 14:41 ubuntu2204.tar
# 打包即压缩的使用
$ docker save ubuntu:22.04 | gzip > ubuntu2204.tar.gz
$ ls -lh ubuntu2204.tar.gz
-rw-r--r-- 1 root root 28M Nov 21 14:43 ubuntu2204.tar.gz
# 导入到本地镜像中
$ docker load -i ubuntu2204.tar
$ docker load -i ubuntu2204.tar.gz
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 22.04 9d28ccdc1fc7 11 months ago 76.3MB
2.3 容器
2.3.1 创建容器常用选项
创建容器命令格式:
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
OPTIONS(常用选项)
范例:创建容器的使用
$ docker run ubuntu:22.04 echo "hello ubuntu container"
hello ubuntu container
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d77e011f236e ubuntu:22.04 "echo 'hello ubuntu …" 12 seconds ago Exited (0) 11 seconds ago thirsty_snyder
# CONTAINER ID:容器的ID
# IMAGE:容器使用的镜像
# COMMAND:容器使用的命令
# CREATED:容器创建的时间
# STATUS:容器的状态
# PORTS:容器使用的端口(端口映射)
# NAMES:容器的名字
$ docker run -it --name ubuntu-node1 -d ubuntu:22.04 /bin/bash
$ docker exec -it ubuntu-node1 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.8 cf48bfee995d
# 使用 --add-host 添加主机hosts
$ docker run -itd --name ubuntu-node2 --add-host huaweicloud:110.41.20.249 ubuntu:22.04 /bin/bash
$ docker exec -it ubuntu-node2 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
110.41.20.249 huaweicloud
172.17.0.9 8b7c0e887fd0
# 获取运行容器的PID
$ docker run -it -d --cidfile=/tmp/container.pid ubuntu:22.04
$ cat /tmp/container.pid
2d94239a2066972a83d9c08b7579e616e74dae7aa9fc10ed47ff1ab7770c1328
# 修改容器的DNS配置
$ docker exec -it ubuntu-node1 cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 100.125.136.29
nameserver 100.125.1.250
options timeout:1 single-request-reopen
$ docker run -it -d --name ubuntu-node3 --dns 8.8.8.8 ubuntu:22.04 /bin/bash
$ docker exec -it ubuntu-node3 cat /etc/resolv.conf
nameserver 8.8.8.8
options timeout:1 single-request-reopen
$ docker run -it -d --name ubuntu-node4 --dns 114.114.114.114 --dns 8.8.8.8 ubuntu:22.04 /bin/bash
$ docker exec -it ubuntu-node4 cat /etc/resolv.conf
nameserver 114.114.114.114
nameserver 8.8.8.8
options timeout:1 single-request-reopen
# 设置容器的环境变量
$ docker run -d -it --name ubuntu-env -e TEST="123456" ubuntu:22.04 /bin/bash
$ docker exec -it ubuntu-env /bin/bash
root@d7d288987e2b:/# echo $TEST
123456
root@d7d288987e2b:/# echo ${TEST}
123456
# 暴露容器的端口
$ docker run -it -d --name nginx-node1 -p 10880:80 nginx:1.23.2-alpine
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
71d9c4b9b9e5 nginx:1.23.2-alpine "/docker-entrypoint.…" 50 minutes ago Up 50 minutes 0.0.0.0:10880->80/tcp, :::10880->80/tcp nginx-node1
$ curl localhost:10880
<!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>
$ docker port nginx-node1
80/tcp -> 0.0.0.0:10880
80/tcp -> :::10880
:::color1 扩展:
Docker 会采用形容词 + 人名的随机组合方式来命名容器;比较有趣的是命名列表使用了许多在各个人类工作领域做出贡献的科学家。:::
# 指定容器的主机名
$ docker run -itd --name nginx -h nginx-node nginx:1.23.2-alpine
$ docker exec -it nginx hostname
nginx-node
# 指定容器的IP地址
$ docker network create --subnet="100.0.0.0/16" network100
$ docker run -itd --name ubuntu-subnet100 --ip "100.0.0.100" --network network100 ubuntu:22.04
$ docker inspect -f "{{.NetworkSettings.Networks.network100.IPAddress}}" ubuntu-subnet100
100.0.0.100
# 宿主机进行连通性测试容器(100.0.0.100)
$ ping -c 1 -W 1 100.0.0.100
PING 100.0.0.100 (100.0.0.100) 56(84) bytes of data.
64 bytes from 100.0.0.100: icmp_seq=1 ttl=64 time=0.059 ms
--- 100.0.0.100 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.059/0.059/0.059/0.000 ms
# 使用--link模式连接容器之间的主机名测试
$ docker run -itd --link=nginx-node1 --name nginx-node2 nginx:1.23.2-alpine
$ docker exec -it nginx-node2 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.14 nginx-node1 71d9c4b9b9e5
172.17.0.16 02d665f0a8c2
$ docker exec -it nginx-node2 ping -c 1 -W 1 nginx-node1
PING nginx-node1 (172.17.0.14): 56 data bytes
64 bytes from 172.17.0.14: seq=0 ttl=64 time=0.099 ms
--- nginx-node1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.099/0.099/0.099 ms
# 指定容器的日志驱动
$ docker run -itd --log-driver syslog --name ubuntu-syslog ubuntu:22.04 /bin/bash
$ docker inspect -f "{{.HostConfig.LogConfig.Type}}" ubuntu-syslog
syslog
# --oom-kill-disable:Linux系统内核有一个机制,当机器的物理内存不足时(并且没有SWAP分区),Linux会Kill占用内存比较大的进程
# 端口映射
$ docker run -it -d --name nginx-node1 -p 10880:80 nginx:1.23.2-alpine
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
71d9c4b9b9e5 nginx:1.23.2-alpine "/docker-entrypoint.…" 50 minutes ago Up 50 minutes 0.0.0.0:10880->80/tcp, :::10880->80/tcp nginx-node1
$ curl localhost:10880
<!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>
$ docker port nginx-node1
80/tcp -> 0.0.0.0:10880
80/tcp -> :::10880
# 暴露宿主机的随机端口
$ docker run -itd --name nginx-port -P nginx:1.23.2-alpine
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a0d71b176031 nginx:1.23.2-alpine "/docker-entrypoint.…" 2 seconds ago Up 1 second 0.0.0.0:49153->80/tcp, :::49153->80/tcp nginx-port
# 当Docker服务重启时容器会不断的尝试重启
$ docker run -itd --restart=always --name nginx-restart nginx:1.23.2-alpine
# 当容器重启3次之后,就不再重启了
$ docker run -itd --restart on-failure:3 --name nginx-on-failure nginx:1.23.2-alpine
范例:指定容器的最大描述符
$ docker exec -it ubuntu-node1 /bin/bash
root@cf48bfee995d:/# ulimit
unlimited
root@cf48bfee995d:/# ulimit -n
1048576
$ docker run -itd --name ubuntu-ulimit --ulimit nproc=10240 --ulimit nofile=10240 ubuntu:22.04 /bin/bash
$ docker exec -it ubuntu-ulimit /bin/bash
root@f4176b390fc7:/# ulimit -a
real-time non-blocking time (microseconds, -R) unlimited
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7268
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 10240
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 10240
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
范例:容器资源限制
# 限制容器的内存资源(软性限制)
$ docker run -itd -m 10240000 --name ubuntu-memory ubuntu:22.04
$ CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
e6653a5599a6 ubuntu-memory 0.00% 556KiB / 9.766MiB 5.56% 656B / 0B 0B / 0B 1
在Docker的默认配置中是没有对容器做资源限制的,在容器发生异常的状况下有可能耗光宿主机所有资源导致OOM,OOM一旦发生任何进程都有可能被系统杀掉。所以在启动Docker容器时最好是指定CPU、内存、硬盘大小等硬件配额。Docker通过cgroup来控制这些资源配额,也就是说下面讲到的命令其实都是在配置cgroup。比如调整了CPU的限制后,在/sys/fs/cgroup/cpu/docker/container_id/
可以看到设置的值。cgroup分配的结果取决于当时主机和其他容器的运行状况,如果A容器很空闲,那么即使给它分配了更多份额,也会被其他忙碌的容器抢夺资源,是一种动态分配。设置好配额后可以在容器中用压力工具stress
来测试。
2.3.1.1 Docker 内存限制
:::color1 -m | —memory:设置容器可用内存大小,支持单位有k、m、g
—memory-swap:设置内存+swap的总大小,配合-m一起使用,该选项不指定的话默认是-m的2倍大小
—oom-kill-disable:不允许容器因为OOM被系统杀掉,建议配合-m选项使用,避免宿主内存被耗光
:::
docker run -itd -m 1024m --memoery-swap=1524m --oom-kill-disable docker.io/centos
#容器使用的内存上限为1G,虚拟内存500M,超过该内存限制就会停止容器
2.3.1.2 Docker 硬盘I/O限制,限制后在容器中读写文件都不会超限
:::color1
—device-read-bps:限制读某个设备的bps —device-write-bps:限制写某个设备的bps —device-read-iops:限制读某个设备的iops —device-write-iops:限制写某个设备的iops:::
docker run -it --name iotest --device-write-bps /dev/sda1:30M docker centos
#限制最高写速度为30M/S
2.3.1.3 Docker CPU配额设置
:::color1 -c | —cpu-share:CPU权重设置,Docker会把CPU资源分成1024份,如果对一个容器设置了1024意味它独占所有CPU资源,如果多个容器同时进行了设置,那么每个容器最后会通过各自占有的百分比来分配CPU资源
—cpus:限制CPU核数
—cpuset-cpus:设置CPU亲和,让容器绑定在指定的CPU上
:::
CPU亲和设置,比如物理机有16个核心,创建的容器只能用0、1、2这三个内核
docker run -it --name test --cpu-shares 1024 docker.io/centos /bin/bash
docker run -it --name test --cpu-shares 512 docker.io/centos /bin/bash
docker run -itd --name testcpu --cpuset-cpus 0-2 docker.io/centos
cat /sys/fs/cgroup/cpuset/docker/dockerid/cpuset.cpus
2.3.1.4 使用progrium/stress镜像测试Docker配额限制
1、内存压测,如下代表启动一个线程,每个线程分配500M内存。由于超过了容器最大内存会报错退出:2、CPU测试。分配权重为2:1,容器启动后在宿主机执行top命令可以看到占比为66%、33%
docker run -it -m 200m --memory-swap=300m progium/stress --vm 1 --vm-bytes 400m
docker run --name "test1" -c 1024 centos
docker run --name "test2" -c 512 centos
2.3.2 容器基本操作
# 查看机器正在运行的容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4ff1ef0b3c7 java-demo:v1.0 "java -jar /app.jar" 4 days ago Up 4 days 0.0.0.0:8090->8080/tcp, :::8090->8080/tcp myjava-demo
# 查看机器最新运行的容器
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4ff1ef0b3c7 java-demo:v1.0 "java -jar /app.jar" 4 days ago Up 4 days 0.0.0.0:8090->8080/tcp, :::8090->8080/tcp myjava-demo
# 查看机器所有的容器,包括正在运行和停止运行的容器
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4ff1ef0b3c7 java-demo:v1.0 "java -jar /app.jar" 4 days ago Up 4 days 0.0.0.0:8090->8080/tcp, :::8090->8080/tcp myjava-demo
# 只显示容器的容器ID
$ docker ps -q
a4ff1ef0b3c7
# 进入到容器的控制台
$ docker run -it -d --name ubuntu-node1 ubuntu:22.04 /bin/bash
$ docker attach ubuntu-node1
root@c4486867031a:/# cat /etc/os-release
PRETTY_NAME="Ubuntu Jammy Jellyfish (development branch)"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
# 删除容器
$ docker rm -f ubuntu-node1
# 重命名容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9b2dabaade80 nginx "/docker-entrypoint.…" 9 days ago Up 9 days 0.0.0.0:88->80/tcp, :::88->80/tcp mynginx
$ docker rename mynginx nginx-node1
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9b2dabaade80 nginx "/docker-entrypoint.…" 9 days ago Up 9 days 0.0.0.0:88->80/tcp, :::88->80/tcp nginx-node1
# 获取容器的详细信息显示
$ docker inspect nginx-node1
# 进入到容器的控制台
$ docker run -it -d --name ubuntu-node1 ubuntu:22.04 /bin/bash
$ docker exec -it ubuntu-node1 /bin/bash
root@8cabe1e6ed8b:/# cat /etc/os-release
PRETTY_NAME="Ubuntu Jammy Jellyfish (development branch)"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04 (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
# 查看容器的映射端口信息
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5f0ef6e5cb9d portainer/portainer "/portainer" 6 days ago Up 6 days 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp portainer
$ docker port portainer
8000/tcp -> 0.0.0.0:8000
8000/tcp -> :::8000
9000/tcp -> 0.0.0.0:9000
9000/tcp -> :::9000
9443/tcp -> 0.0.0.0:9443
9443/tcp -> :::9443
# 将容器的文件和机器的文件相互拷贝
$ echo "Host content" > host.txt
$ docker cp host.txt ubuntu-node1:/tmp
$ docker exec -it ubuntu-node1 cat /tmp/host.txt
Host content
$ docker cp ubuntu-node1:/tmp/host.txt /tmp
$ cat /tmp/host.txt
Host content
# 容器自创建以来发生的变动
$ docker diff ubuntu-node1
C /root
A /root/.bash_history
C /tmp
A /tmp/host.txt
# 动态查看容器的资源利用率
$ docker stats ubuntu-node1
# 静态查看容器的资源利用率
$ docker stats --no-stream ubuntu-node1
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
8cabe1e6ed8b ubuntu-node1 0.00% 560KiB / 1.794GiB 0.03% 730B / 0B 0B / 0B 1
# 更新容器的设置
$ docker update --help
Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]
Update configuration of one or more containers
Options:
--blkio-weight uint16 Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)
--cpu-period int Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota
--cpu-rt-period int Limit the CPU real-time period in microseconds
--cpu-rt-runtime int Limit the CPU real-time runtime in microseconds
-c, --cpu-shares int CPU shares (relative weight)
--cpus decimal Number of CPUs
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)
--kernel-memory bytes Kernel memory limit
-m, --memory bytes Memory limit
--memory-reservation bytes Memory soft limit
--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--pids-limit int Tune container pids limit (set -1 for unlimited)
--restart string Restart policy to apply when a container exits
# 查看Docker的所有事件
$ docker events
2022-11-22T09:56:10.862403169+08:00 container create bc27b8488f040cc30c0651e8f3ee1e62b561b84e6198d57d4136512d8ea64785 (image=ubuntu:22.04, name=ubuntu-node2)
2022-11-22T09:56:10.899529233+08:00 network connect 24c9d2ca6c2a35cb112bf3f045d56d546592ab34efbbf525e65c5f568e15872f (container=bc27b8488f040cc30c0651e8f3ee1e62b561b84e6198d57d4136512d8ea64785, name=bridge, type=bridge)
2022-11-22T09:56:11.078061808+08:00 container start bc27b8488f040cc30c0651e8f3ee1e62b561b84e6198d57d4136512d8ea64785 (image=ubuntu:22.04, name=ubuntu-node2)
2.4 数据持久化
2.4.1 数据卷
将宿主机目录挂载到容器目录。数据卷特点:
- 在容器启动初始化时,如果容器使用的宿主机挂载点有数据,这些数据就会拷贝到容器中。
- 数据卷可以在容器直接共享和重用。
- 可以直接对数据卷里的内容进行修改。
- 数据卷的变化不会影响镜像的更新。
- 卷会一直存在,即使挂载数据卷的容器已经删除。示例:
docker run -itd --name web01 -v /container_data/web:/data nginx:1.23.2-alpine
注:/container_data/web
为宿主机目录,/data
是容器中目录,目录不存在会自动创建。
$ echo "hello docker" > /container_data/web/host.txt
$ docker exec -it web01 cat /data/host.txt
hello docker
2.4.2 容器数据卷
将一个运行的容器作为数据卷,让其他容器通过挂载这个容器实现数据共享。示例:
docker run -itd -v /data --name dvdata ubuntu:22.04
docker run -itd --name web02 --volumes-from dvdata ubuntu:22.04
$ docker run -itd -v /data --name dvdata ubuntu:22.04
$ docker exec -it dvdata /bin/bash
root@a286ea5f5bfa:/# echo "hello dvdata content" > /data/dvdata.txt
root@a286ea5f5bfa:/# exit
$ docker run -itd --name web02 --volumes-from dvdata ubuntu:22.04
$ docker inspect -f "{{.Mounts}}" web02
[{volume d1ef7df5fec2b4f8a8ca87784d4f19ab7168d3438d296c2765489a1e3c2d0fa6 /var/lib/docker/volumes/d1ef7df5fec2b4f8a8ca87784d4f19ab7168d3438d296c2765489a1e3c2d0fa6/_data /data local true }]
$ docker exec -it web02 /bin/bash
root@e9c0ad0f6b4e:/# cat /data/dvdata.txt
hello dvdata content
2.5 搭建 LNMP 网站平台
2.5.1 部署 WordPress 页面(1)
# 创建mysql数据库容器
docker run -itd --name lnmp_mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 --character-set-server=utf8
# 创建wp数据库
docker exec -it lnmp_mysql sh -c 'exec mysql -uroot -p "$MYSQL_RO0T_PASSWORD" -e "create database wp"'
# 创建PHP环境容器
docker run -itd --name lnmp_web \
--link lnmp_mysql:db -p 88:80 \
-v /container_data/web:/var/www/html richarvey/nginx-php-fpm
# 以wordpress博客为例测试
wget https://cn.wordpress.org/wordpress-4.7.4-zh_CN.tar.gz
tar zxf wordpress-4.7.4-zh_c.tar.gz
mv wordpress/* /container_data/web/
# 浏览器测试访问
http://IP:88
查看创建容器的状态
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
238787ee5304 richarvey/nginx-php-fpm "docker-php-entrypoi…" 8 seconds ago Up 7 seconds 443/tcp, 9000/tcp, 0.0.0.0:88->80/tcp, :::88->80/tcp lnmp_web
35b63142b53c mysql:5.7 "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 33060/tcp, 0.0.0.0:3308->3306/tcp, :::3308->3306/tcp lnmp_mysql
2.5.2 部署 WordPress 页面(2)
创建自定义网络以及下载镜像
# 创建相应的自定义网络
docker network create --driver bridge wordpress_net
# 下载相应的镜像
docker pull mariadb:10.6.4-focal
docker pull wordpress
部署 MySQL / MariaDB
# 不需要暴露宿主机的3306端口
docker run -it -d --name db -v db_data:/var/lib/mysql \
--restart=always -e MYSQL_ROOT_PASSWORD=somewordpress \
-e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress \
--network wordpress_net \
-e MYSQL_PASSWORD=wordpress mariadb:10.6.4-focal --default-authentication-plugin=mysql_native_password
部署 WordPress 页面
docker run -it -d --name wordpress --restart=always \
-e WORDPRESS_DB_HOST=db -e WORDPRESS_DB_USER=wordpress \
--network wordpress_net \
-e WORDPRESS_DB_PASSWORD=wordpress -e WORDPRESS_DB_NAME=wordpress \
-p 80:80 wordpress
访问 WordPress 页面
http://110.41.20.249/
http://:80 后续的操作就是 WordPress 根据引导进行部署即可
3 网络管理
3.1 网络模式及工作原理
3.1.1 Docker支持五种网络模式
- bridge
默认网络,Docker启动后创建一个docker0网桥,默认创建的容器也是添加到这个网桥中;IP地址段是172.17.0.1/16 或者 172.18.0.1/16
- host
容器不会获得一个独立的network namespace,而是与宿主机共用一个。
- none
获取独立的network namespace,但不为容器进行任何网络配置。
- container
与指定的容器使用同一个network namespace,网卡配置也都是相同的。
- 自定义网络
自定义网桥,默认与bridge网络一样。
网络模式 | 配置 | 说明 |
---|---|---|
host | —network host | 容器和宿主机共享 Network namespace |
container | —network container:NAME_OR_ID | 容器和另外一个容器共享 Network namespace |
none | —network none | 容器有独立的 Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,配置 IP 等 |
bridge | —network | bridge 默认模式 |
3.1.2 bridge 模式
当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。 从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡 veth pair 设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为 eth0(容器的网卡),另一端放在主机中,以 vethxxx 这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。可以通过 brctl show 命令查看。 bridge 模式是 docker 的默认网络模式,不写—network 参数,就是 bridge 模式。使用 docker run -p 时,docker 实际是在 iptables 做了 DNAT 规则,实现端口转发功能。可以使用 iptables -t nat -vnL 查看。 bridge 模式如下图所示:- 同主机间两个容器间是否可以直接通信?比如在 docker1 上能不能直接访问到 docker2 的 nginx 站点?
- 在宿主机上能否直接访问到 docker2 的 nginx 站点?
- 在另一台主机上如何访问 node1 上的这个 nginx 站点呢?DNAT 发布?
3.1.3 container 模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。container 模式如下图所示:
3.1.4 host 模式
如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。 使用 host 模式的容器可以直接使用宿主机的 IP 地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行 NAT,host 最大的优势就是网络性能比较好,但是 docker host 上已经使用的端口就不能再用了,网络的隔离性不好。 Host 模式如下图所示:3.1.5 none 模式
使用 none 模式,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker 容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。 这种网络模式下容器只有 lo 回环网络,没有其他网卡。none 模式可以在容器创建时通过—network none 来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。 应用场景:- 启动一个容器处理数据,比如转换数据格式
- 一些后台的计算和处理任务
docker network inspect bridge #查看bridge网络的详细配置
范例:Docker支持五种网络模式
# 安装brctl查看网桥情况(提供管理网桥的作用)
$ yum install -y bridge-utils
# apt-get install -y bridge-utils
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
58409c073c1c redis:latest "docker-entrypoint.s…" 5 days ago Up 7 hours 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp myredis
5f0ef6e5cb9d portainer/portainer "/portainer" 7 days ago Up 7 hours 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp portainer
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242e6cb1e84 no veth432cc0d
veth9987f14
# 使用 host 网络模式
# 使用宿主机的网络命名空间
$ docker run -it -d --name busybox-host --network host busybox /bin/sh
$ docker exec -it busybox-host /bin/sh
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether fa:16:3e:6b:c2:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.64/24 brd 192.168.0.255 scope global dynamic noprefixroute eth0
valid_lft 61329sec preferred_lft 61329sec
inet6 fe80::f816:3eff:fe6b:c203/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
link/ether 02:42:e6:cb:1e:84 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:e6ff:fecb:1e84/64 scope link
valid_lft forever preferred_lft forever
# 使用bridge网络模式和host网络模式比较多,none网络模式使用比较少
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
6747a16485d4 bridge bridge local
a40831f989fe host host local
f314686d0f0f none null local
先创建一个docker0的网桥
,使用veth pair创建一对虚拟网卡,一端放到新创建的容器中,并重命名ethO,另一端放到宿主机上,以veth+随机7个字符
串命名,并将这个网络设备加入到docker0网桥
中,网桥自动为容器分配一个IP,<font style="color:#DF2A3F;">并设置docker0的IP为容器默认网关</font>
。所以容器默认网络都加入了这个网桥,因此都可以彼此通信。同时在iptables
添加SNAT转换网络段IP,以便容器访问外网。
:::color1 容器访问外网,容器的数据包会先发送到网关,即宿主机Docker0网桥设备,数据包到达宿主机的网络命名空间以后,就是veth虚拟网卡的一端,Linux内核会查看数据包在Linux路由表的路由选择,若数据包是发送到宿主机本地的话,那么则直接转发给本地地址,若数据包是发送到其他网段,那么目的地址一定外网的( 不是宿主机本地地址 ),Linux内核的TCP协议栈,就会将数据包做一个SNAT,也就是将源地址中容器IP地址改为可以上外网的宿主机的网卡IP地址( eth0 ),并发送到外网机器,外网机器收到的数据包源地址是eth0的宿主机IP地址。
:::
3.2 容器网络访问原理
:::color1 Linux IP信息包过滤原理
:::
Docker 主要通过 <font style="color:#DF2A3F;">netfilter/iptables</font>
实现网络通信。
iptables
由netfilter
和iptables
组成,netfilter
组件是Linux内核集成的信息包过滤系统,它维护一个信息包过滤表,这个表用于控制信息包过滤处理的规则集。而iptables
只是一个在用户空间的工具,用于增删改查这个过滤表的规则。发送方对数据进行TCP/IP封装,接收方则对数据进行TCP/IP的解封装。
参考文献:[ https://blog.csdn.net/weixin_45186298/article/details/122910466 ]
四表五链功能性介绍
iptables是(4张)表的集合:filter、nat、mangle、raw
表是(5条)链的集合:PREROUTING、INPUT、OUTPUT、FORWARD、POSTROUTING
- INPUT
:::warning 客户端发起连接到Apache服务器的时候,数据报文到服务器的网卡处,首先判断 Router Table 的路由表信息,判断该用户的请求是不是本机的,如果不是则丢弃;如果是本机就会交给 netfilter 进行下一步处理。netfilter 会寻找匹配相应的规则,例如:如果目标端口是80的话,就允许放行,这样用户的数据报文就可以到 Http 应用进行处理并得到响应数据报文。
即 INPUT (入站)链。iptables 的规则就是写在 链上的。可以理解为所有入站的数据报文都要经过 iptables 的 INPUT 链,才能到达上层的用户空间。这就要看 INPUT 链的规则是如何编写的,需要匹配规则,如果是放行就放行,如果是拒绝就是拒绝。
:::
- OUTPUT
:::warning 当用户在服务器上操作时,执行 curl 命令请求到 WebServer 的时候,数据报文会向到 netfilter 进行处理,netfilter 将数据报文先后转发到服务器的网卡处,通过 Router Table 路由表中进行转发到 WebServer 进行处理。则 netfilter 的 OUTPUT 链就是用来处理出站的规则匹配。
OUTPUT (出站)链的主要功能就是对机器出去的数据报文进行对应的过滤操作。
:::
- FORWORD
:::info 将Linux服务器充当是路由器。并且Linux 服务器开启 net.ipv4.ip_forward = 1 路由转发功能,当 Client 客户端发起请求到目标地址是 WebServer,Client 发送的数据包会到路由器(Linux服务器)eth0网卡,因为开启了路由转发功能,所以会将数据包转发到 eth1网卡。只要涉及到转发,那么就会经过 netfilter 的 FORWORD 链。
FORWORD (转发)链的主要功能就是处理机器的转发数据报文进行规则匹配。
:::
- PREROUTING
:::info PREROUTING 链在还没有路由表判断之前就对数据报文进行对应的修改。
PREROUTING (路由前)链翻译过来就是路由表之前对数据报文处理的一个接口
:::
- POSTROUTING
:::info POSTROUTING 是先经过路由表判断之后,才到POSTROUTING 进行下一步的处理。
POSTROUTING (路由后)链翻译过来就是路由表之后对数据报文处理的一个接口
:::
:::info 链的作用是承载防火墙的规则,链则是存放在表中。
:::
规则链:承载防火墙规则
处理入站数据包:INPUT
处理出站数据包:OUTPUT
处理转发数据包:FORWARD
在进行路由选择前应用规则(处理数据包):修改目标地址 PREROUTING
在进行路由选择后应用规则(处理数据包):修改源地址 POSTROUTING
规则表:承载防火墙链
raw 表:确定是否对该数据包进行状态跟踪
- 一旦我们的数据包经过防火墙以后,正常情况下会对数据包进行跟踪,跟踪会消耗资源。可以通过 raw 表将这些数据包进行跳出,不进行状态跟踪。
mangle 表:为数据包设置标记,例如:TTL MARK
- 可以在数据包打上固定标签的 mangle 表
- 功能可以有端口映射,SNAT,DNAT
- 基础类防火墙最主要的表,会对数据包进行过滤
:::info 主要的是 nat 表和 filter 表,raw 表和mangle 表的使用较少。
:::
防火墙链表结构
不同表下面拥有的链也是不同的。只要知道要做的防火墙规则是怎么样的,只需要匹配对应的功能,再来选择对应的入站和出站。这样就知道 netfilter 的规则是写在哪张表的哪条链上。(红色标记是重点),例如在NAT表中的PREROUTING 链和 POSTROUTING 链是各自实现SNAT和DNAT的功能。
类别 | 选项 | 用途 |
---|---|---|
添加新的规则 | -A | 在链的末尾追加一条规则 |
-I | 在链的开头(或者指定序号)插入一条规则 | |
查看规则列表 | -L | 列出所有的规则条目 |
-n | 以数字形式形式地址,端口等信息 | |
-v | 以更加详细的方式显示规则信息(显示当前过滤的数据包和过滤的字节数) | |
—line-numbers | 查看规则时,显示规则的序号 | |
删除、清空规则 | -D | 删除链内指定序号(或者内容)的一条规则 |
-F | 清空所有的规则 | |
设置默认策略 | -P | 为指定的链设置默认规则 |
:::color1 容器访问外部,外部访问容器
:::
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5f0ef6e5cb9d portainer/portainer "/portainer" 7 days ago Up 10 hours 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp portainer
$ iptables -vnL -t nat
# 容器访问外部
Chain POSTROUTING (policy ACCEPT 767 packets, 50988 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:9443
0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:9000
0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:8000
# 外部访问容器
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
5 204 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9443 to:172.17.0.2:9443
56 2988 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9000 to:172.17.0.2:9000
14 688 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8000 to:172.17.0.2:8000
可以访问 Portainer 的页面
- 第一次登录需创建admin,访问地址:IP地址:9000 [http://IP:9000/#/init/admin](http://IP:9000/#/init/admin)
- 设置admin用户和密码后首次登录
:::warning
用户名,直接用默认admin
(密码记得8位,随便你写)
:::
- 选择local选项卡后本地docker详细信息显示
- 上一步的图形展示,以及对应的docker命令
$ docker system df
# Stack 是docker-compose
3.3 容器桥接宿主机网络及容器配置固定IP地址
3.3.1 桥接宿主机网络
临时生效:
# 网桥名称
br_name=br0
# 添加网桥
brctl addbr $br_name
# 给网桥设置IP(IP地址为eth0宿主机的IP地址)
ip addr add 10.0.0.54/24 dev $br_name
# 删除已存在的eth0网卡配置
ip addr del 10.0.0.54/24 dev eth0
# 激活网桥
ip link set $br_name up
# 添加eth0到网桥
brctl addif $br_name eth0
# 查看主机的IP信息
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UP group default qlen 1000
link/ether 00:0c:29:8d:f3:e6 brd ff:ff:ff:ff:ff:ff
inet6 fe80::20c:29ff:fe8d:f3e6/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:44:84:af:18 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:0c:29:8d:f3:e6 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.54/24 scope global br0
valid_lft forever preferred_lft forever
inet6 fe80::60b6:4bff:fe93:4803/64 scope link
valid_lft forever preferred_lft forever
# 还需要在Docker启动时桥接这个网桥:
# 没有/etc/sysconfig/docker这个文件
# Docker是交给systemd来管理的,它的配置文件在/usr/lib/systemd/system/docker.service
# 需要添加EnvironmentFile=-/etc/default/docker,让后在ExecStart这个配置中,添加引用的参数$DOCKER_OPTS
$ vim /etc/default/docker # (vim /usr/lib/systemd/system/docker.service)
DOCKER_OPTS="-b=br0"
DOCKER_OPTS="--dns 114.114.114.114 --dns 8.8.8.8"
# 选择网桥
# DOCKER_OPTS="-b=br0"
# 指定DNS
# DOCKER_OPTS="–dns 114.114.114.114 --dns 8.8.8.8"
# 重载 & 重启docker服务
systemctl daemon-reload && systemctl restart docker
# 查看加载状态
systemctl status docker.service
# 查看桥接信息
$ brctl show
bridge name bridge id STP enabled interfaces
br0 8000.000c298df3e6 no eth0
docker0 8000.02424484af18 no
# 查看进程信息
$ ps -ef | grep docker
root 2649 1 0 23:03 ? 00:00:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -b=br0
root 2786 1705 0 23:03 pts/1 00:00:00 grep --color=auto docker
永久生效:
# vi /etc/network/interfaces
auto eth0
iface eth0 inet static
auto br0
iface br0 inet static
address 10.0.0.54
netmask 255.255.255.0
gateway 10.0.0.54
dns-nameservers 114.114.114.114
bridge_ports eth0
3.3.2 配置容器固定IP地址
C_ID=$(docker run -itd --net=none ubuntu:22.04)
C_PID=$(docker inspect -f '{{.State.Pid}}' $C_ID)
#创建network namespace目录并将容器的network namespace软连接到此目录,以便ip netns命令读取
mkdir -p /var/run/netns
ln -s /proc/$C_PID/ns/net /var/run/netns/$C_PID
#添加虚拟网卡veth+容器PID,类型是veth pair,名称是vp+容器PID
ip link add veth$C_PID type veth peer name vp$C_PID
#添加虚拟网卡到brO网桥
brctl addif br0 veth$C_PID
#激活虚拟网卡
ip link set veth$C_PID up
#设置容器网络信息
IP='10.0.0.55/24'
GW='10.0.0.2'
#给进程配置一个network namespace
ip link set vp$C_PID netns $C_PID
#在容器进程里面设置网卡信息
ip netns exec $C_PID ip link set dev vp$C_PID name eth0
ip netns exec $C_PID ip link set eth0 up
ip netns exec $C_PID ip addr add $IP dev eth0
ip netns exec $C_PID ip route add default via 10.0.0.55
宿主机连通性测试Docker容器
$ ping -c 1 -W 1 10.0.0.55
PING 10.0.0.55 (10.0.0.55) 56(84) bytes of data.
64 bytes from 10.0.0.55: icmp_seq=1 ttl=64 time=0.108 ms
--- 10.0.0.55 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.108/0.108/0.108/0.000 ms
3.3.3 pipework
如果你觉得使用上面命令比较复杂,也有别人封装好的脚本:
git clone https://github.com/jpetazzo/pipework.git
cp pipework/pipework /usr/local/bin/
docker run -itd --net=none --name test01 ubuntu
pipework br0 test01 192.168.1.88/24@192.168.1.1
3.3.4 容器SSH连接
$ docker pull centos:centos7.9.2009
$ docker run -itd --name centos-node1 centos:centos7.9.2009
$ docker attach centos-node1
/# yum install -y openssh-server sshd
/# passwd root
Changing password for user root.
New password: Admin@h3c
Retype new password: Admin@h3c
passwd: all authentication tokens updated successfully.
$ docker commit centos-node1 centos7_ssh:v1.0
$ docker run -itd --privileged=true --name centos-ssh-docker -p 2222:22 --restart=always centos7_ssh:v1.0 /usr/sbin/init
$ docker exec -it centos-ssh-docker /bin/bash
/# systemctl start sshd
/# echo "Docker Hosts Content" > /docker.txt
$ ssh root@110.41.20.249 -p 2222
root@110.41.20.249's' password:
Last login: Tue Nov 22 16:13:21 2022 from 120.229.46.77
[root@86c0a63c1092 ~]#
4 Dockerfile
1、Dockerfile指令
2、Build镜像命令
3、构建PHP网站环境镜像
4、构建JAVA网站环境镜像
5、构建支持SSH服务的镜像
4.0 Dockerfile
:::warning
Dockerfile 是用来构建 Docker 镜像的文本文件(类似于Shell脚本),是由一条条构建镜像所需的指令和参数构成的脚本。:::
概述
Dockerfile Reference官网: https://docs.docker.com/engine/reference/builder/
构建三步骤
- 编写Dockerfile文件
- docker build 命令构建镜像
- docker run 依镜像运行容器实例
Dockerfile 常用指令
4.0.1 RUN、CMD和ENTRYPOINT指令区别
- RUN 在 building 时运行,可以写多条
- CMD 和 ENTRYPOINT 在运行 Container 时运行,只能写一条,如果写多条,最后一条生效。
- CMD 在 RUN时可以被 COMMAND 覆盖,ENTRYPOINT 不会被 COMMAND 覆盖,但可以指定
**--entrypoint**
覆盖。
$ docker run --help | grep entrypoint
--entrypoint string Overwrite the default ENTRYPOINT of the image
4.0.2 Build 镜像命令
使用Dockerfile文件构建镜像
Usage: docker build [OPTIONS] PATH | URL | -
Build an image from a Dockerfile
Options:
-t,--tag list # 镜像名称
-f,--file string # 指定Dockerfile文件位置
--no-cache # 构建镜像时不使用缓存
示例:
docker build . # 默认找当前目录以Dockerfile为命名的文件
docker build -t shykes/myapp .
docker build -t shykes/myapp -f /path/Dockerfile /path
docker build -t shykes/myapp - < Dockerfile
docker build -t shykes/myapp - < context.tar. gz
docker build -t shykes/myapp http://www.example.com/Dockerfile
docker build -f shykes/myapp http://www.example.com/contex.tar.gz
4.1 构建PHP网站环境镜像
Docker 容器需要用阻塞进程将容器一直运行中。
范例:start.sh 脚本示例
cat > start.sh <<-'EOF'
#!/bin/bash
service httpd start
service mysqld start
mysqladmin -uroot -p${MYSQL_ROOT_PASSWORD}
tail -f
EOF
范例:Dockerfile 示例
FROM centos:6
MAINTAINER <zhongzhiwei zhongzhiwei@kubesphere.io>
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-6.10.repo
RUN yum install -y httpd php php-gd php-mysql mysql mysql-server
ENV MYSQL_ROOT_PASSWORD 123456
RUN echo "<?php phpinfo()?>" > /var/www/html/index.php
# cat start.sh
# #!/bin/bash
# service httpd start
# service mysqld start
# mysqladmin -uroot -p${MYSQL_ROOT_PASSWORD}
# tail -f
ADD start.sh /start.sh
RUN chmod +x /start.sh
# wget https://cn.wordpress.org/wordpress-4.7.4-zh_CN.tar.gz
ADD https://cn.wordpress.org/wordpress-4.7.4-zh_CN.tar.gz /var/www/html/
RUN cd /var/www/html/ && tar -zxvf wordpress-4.7.4-zh_CN.tar.gz
COPY wp-config.php /var/www/html/wordpress
VOLUME [ "/var/lib/mysql" ]
CMD /start.sh
EXPOSE 80 3306
范例:wp-config.php 示例
<?php
/**
* WordPress基础配置文件。
*
* 这个文件被安装程序用于自动生成wp-config.php配置文件,
* 您可以不使用网站,您需要手动复制这个文件,
* 并重命名为“wp-config.php”,然后填入相关信息。
*
* 本文件包含以下配置选项:
*
* * MySQL设置
* * 密钥
* * 数据库表名前缀
* * ABSPATH
*
* @link https://codex.wordpress.org/zh-cn:%E7%BC%96%E8%BE%91_wp-config.php
*
* @package WordPress
*/
// ** MySQL 设置 - 具体信息来自您正在使用的主机 ** //
/** WordPress数据库的名称 */
define('DB_NAME', 'test');
/** MySQL数据库用户名 */
define('DB_USER', 'root');
/** MySQL数据库密码 */
define('DB_PASSWORD', '密码');
/** MySQL主机 */
define('DB_HOST', 'localhost');
/** 创建数据表时默认的文字编码 */
define('DB_CHARSET', 'utf8');
/** 数据库整理类型。如不确定请勿更改 */
define('DB_COLLATE', '');
/**#@+
* 身份认证密钥与盐。
*
* 修改为任意独一无二的字串!
* 或者直接访问{@link https://api.wordpress.org/secret-key/1.1/salt/
* WordPress.org密钥生成服务}
* 任何修改都会导致所有cookies失效,所有用户将必须重新登录。
*
* @since 2.6.0
*/
define('AUTH_KEY', '(Qc8jjvhi}r7w|N+fZ+5x;~:mTun!08=2f+vn#5m@<OH#q !0H_;PteVL/:a@1e_');
define('SECURE_AUTH_KEY', '!{5p&%:CeG8Wi4q)FkjmEtYU8n3v*K/i01R-{~/7t7lKe?j=fs$!!K>lgw`8%|`E');
define('LOGGED_IN_KEY', '779<wSUu<7Y#=:,,9>c@D{)p+i}0t=F/US}Be/=j)?2!l!Cd4_:cL)3&N,-ls?^X');
define('NONCE_KEY', 'FT{nC1?}qnP@)[JuENB3a%11w;vPp<@YxzbbFBfW5JPZN0wBVN|FVB`q!)f[H(/e');
define('AUTH_SALT', '|Y]}.eZX$S~$#su_{hS0c24:dm@[a..jWav@:bSH)Y6PF)Qk@a;CP*4Cv=-Dpv3W');
define('SECURE_AUTH_SALT', '`!R:FNc{{kdkm;zQ1x2?{fIpbc-Am&UysZrYFHF?u~.N:;Rhy-?YZNnuOa(KE+0o');
define('LOGGED_IN_SALT', '%h>+r06#a^6{Wk3/=8oH2`<p4ub0&d+N;tSeBY+iw(i.Ug9>X]A)rPRS/T.Rslg(');
define('NONCE_SALT', '?Dvzn,,<r4;82^rIx5%+9/fNR?i3sG9^]pHR{+eV@pQP!L~CRFcrXz?&s^ZgWJJ2');
/**#@-*/
/**
* WordPress数据表前缀。
*
* 如果您有在同一数据库内安装多个WordPress的需求,请为每个WordPress设置
* 不同的数据表前缀。前缀名只能为数字、字母加下划线。
*/
$table_prefix = 'wp_';
/**
* 开发者专用:WordPress调试模式。
*
* 将这个值改为true,WordPress将显示所有用于开发的提示。
* 强烈建议插件开发者在开发环境中启用WP_DEBUG。
*
* 要获取其他能用于调试的信息,请访问Codex。
*
* @link https://codex.wordpress.org/Debugging_in_WordPress
*/
define('WP_DEBUG', false);
/**
* zh_CN本地化设置:启用ICP备案号显示
*
* 可在设置→常规中修改。
* 如需禁用,请移除或注释掉本行。
*/
define('WP_ZH_CN_ICP_NUM', true);
/* 好了!请不要再继续编辑。请保存本文件。使用愉快! */
/** WordPress目录的绝对路径。 */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
/** 设置WordPress变量和包含文件。 */
require_once(ABSPATH . 'wp-settings.php');
范例:构建 Docker 镜像
docker build -t php:v1.0 -f Dockerfile .
:::color1 Container 容器是最精简版的 Linux 操作系统。
:::
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
php v1.0 14ebabe2d1c4 12 minutes ago 547MB
$ docker run -itd --name wordpress -p 88:80 php:v1.0
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
69dfab550ca6 php:v1.0 "/bin/sh -c /start.sh" 2 seconds ago Up 2 seconds 3306/tcp, 0.0.0.0:88->80/tcp, :::88->80/tcp wordpress
浏览器访问 http://<IP地址>:88
浏览器访问 http://<IP地址>:88/wordpress
$ docker exec -it wordpress /bin/bash
[root@1c8bab6f45bd /]# echo $MYSQL_ROOT_PASSWORD
123456
[root@404042e7bfc8 /]# vi /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
skip-grant-tables
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
安装 Wordpress 就可以实现了!
Wordpress 后台管理系统。
4.2 构建JAVA网站环境镜像
FROM centos:6
MAINTAINER <zhongzhiwei zhongzhiwei@kubesphere.io>
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-6.10.repo
ADD jdk-8u45-linux-x64.tar.gz /usr/local
ENV JAVA_HOME /usr/local/jdk1.8.0_45
# https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.83/bin/apache-tomcat-8.5.83.tar.gz
# ADD https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.83/bin/apache-tomcat-8.5.83.tar.gz /usr/local
ADD apache-tomcat-8.5.83.tar.gz /usr/local
WORKDIR /usr/local/apache-tomcat-8.5.83
ENTRYPOINT ["bin/catalina.sh", "run"]
EXPOSE 8080
范例:构建 Dockerfile 镜像
$ ls -l
total 179592
-rw-r--r-- 1 root root 10621733 Oct 4 05:43 apache-tomcat-8.5.83.tar.gz
-rw-r--r-- 1 root root 599 Nov 24 11:15 Dockerfile
-rw-r--r-- 1 root root 173271626 Nov 24 11:08 jdk-8u45-linux-x64.tar.gz
$ docker build -t java-tomcat:v1.0 -f Dockerfile .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
java-tomcat v1.0 0be873e2837a 49 seconds ago 550MB
$ docker run -itd --name java-tomcat -p 8088:8080 java-tomcat:v1.0
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7fc19d168b7c java-tomcat:v1.0 "bin/catalina.sh run" 22 seconds ago Up 20 seconds 0.0.0.0:8088->8080/tcp, :::8088->8080/tcp java-tomcat
浏览器访问 http://<IP地址>:8088
4.3 构建SSH服务的镜像
FROM centos:6
MAINTAINER <zhongzhiwei zhongzhiwei@kubesphere.io>
ENV ROOT_PASSWORD 123456
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-6.10.repo
RUN yum install -y openssh-server && \
echo $ROOT_PASSWORD | passwd --stdin root
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key && \
ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
CMD [ "/usr/sbin/sshd","-D" ]
EXPOSE 22
范例:构建 Dockerfile 镜像
$ docker build -t centos-ssh-6:v1.0 -f Dockerfile .
$ docker run -it -d --name centos-ssh-node -p 2022:22 centos-ssh-6:v1.0 /bin/bash
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a45ed0501cde centos-ssh-6:v1.0 "/bin/bash" 25 seconds ago Up 24 seconds 0.0.0.0:2022->22/tcp, :::2022->22/tcp centos-ssh-node
5 私有镜像仓库 & 公共镜像仓库
5.1 搭建私有镜像仓库
- Docker Registry
- 官方 Docker Hub地址:https://hub.docker.com/,中国大陆访问太慢了并且准备被阿里云取代的趋势,不太主流。
- DockerHub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私人仓库供团队使用,基于公司内部项目构建镜像
Docker Hub作为Docker默认官方公共镜像:如果想自己搭建私有镜像仓库,官方也提供registry镜像,使得搭建私有仓库非常简单。
Docker Registry 是官方提供的工具,可以用于构建私有镜像仓库。
- 下载镜像 Docker Registry
$ docker pull registry
$ docker images registry
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest b8604a3fe854 12 months ago 26.2MB
- 运行私有库 Registry,相当于本地有个私有Docker Hub
$ mkdir -pv /app/registry
$ docker run -d -p 5000:5000 \
-v /app/registry:/tmp/registry \
--privileged=true \
--name myregistry \
--restart=always registry
$ docker ps -l
# 默认情况下,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便于宿主机联调
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e27d28926331 registry "/entrypoint.sh /etc…" 4 seconds ago Up 3 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp myregistry
- curl验证私服库上有什么镜像
# 模拟发送一个Get请求
# 测试,查看镜像仓库中所有的镜像
$ curl -XGET http://110.41.20.249:5000/v2/_catalog
{"repositories":[]}
# 可以看到,目前私服库没有任何镜像上传过......
5.2 私有镜像仓库管理
配置私有仓库可信任
$ vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com","http://hub-mirror.c.163.com","https://mirror.ccs.tencentyun.com","http://f1361db2.m.daocloud.io"],
"insecure-registries" : ["110.41.20.249:5000"]
}
service docker restart
打标签
docker tag centos:6 110.41.20.249:5000/centos:6
上传镜像
docker push 110.41.20.249:5000/centos:6
下载镜像
2docker pull 110.41.20.249:5000/centos:6
测试,查看镜像仓库中所有的镜像
$ curl -XGET http://110.41.20.249:5000/v2/_catalog
{"repositories":["centos"]}
列出镜像标签
$ curl http://110.41.20.249:5000/v2/centos/tags/list
{"name":"centos","tags":["6"]}
5.3 DockerHub 公共镜像仓库使用
Docker Hub的功能 Docker Hub被用于源代码管理集成,也用于构建和测试工具来加速部署周期,部署周期从天减少到以分钟计算,Docker宣称已经允许用户加速应用的传输。 架构和技术堆栈升级对于Docker Hub的大规模和不可预知的采用是必须的。 作为开发人员迅速采用Docker容器,IT管理员加速将其纳入企业生产环境。除了管理和配置工具,还需要编排和调度软件。
# 1、注册账号
https://hub.docker.com
# 2、登录Docker Hub
docker login
# 或
docker login --username=dragonzw --password=SZzhongaislf
# 3、镜像打标签
docker tag registry dragonzw/registry:v1
# 4、上传
docker push dragonzw/registry:v1
# 搜索测试:
$ docker search dragonzw
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
dragonzw/nginx 用于测试 Nginx 镜像容器 0
dragonzw/java-demo Springboot Java-demo;Open http://<IP Addres… 0
dragonzw/sentinel Sentinel 用于监控微服务的流量,链路管控平台 0
dragonzw/flask-demo 用python 编写的 flask-demo,默认端口:5000 0
dragonzw/registry Docker私有镜像仓库 0
dragonzw/cfssl 上传cfssl-certinfo_linux-amd64、cfssl_linux-… 0
dragonzw/tomcat 基于CentOS的Tomcat 9.0.22 以及JDK 8u11的镜像 0
# 5、下载
docker pull dragonzw/registry:v1
6 图形化管理页面
6.1 DockerUI
DockerUI,后台使用go语言开发的,前端使用CubeUI开发的基于Docker容器的管理工具,通过图形化的界面,来管理Docker容器,Docker Swarm集群,不需要你懂docker的cli命令,你也可以将Docker和Docker swarm的管理操作的如火纯清,而不费吹灰之力。
DockerUI后台使用最快的fasthttp提供web服务,通过Docker的本地接口,实现和Docker 容器的Enginee进行通信,从而进行Docker的管理。
DockerUI是一个基于Docker API提供图形化页面简单的容器管理系统,支持容器管理、镜像管理。
docker run \
-d \
-p 9090:9000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "/var/run/docker.sock" \
--restart=always \
--name docker-ui abh1nav/dockerui:latest
也可以通过Rest API管理:
docker run \
-d -p 9090:9000 \
--name dockerui \
-e "http://<dockerd host ip>:2375" abh1nav/dockerui:latest
# http://<dockerd host ip>:9090
浏览器访问DockerUI :http://<IP地址>:9090
6.2 Shipyard
Shipyard 是一个基于 Web 的 Docker 管理工具,支持多 host,可以把多个 Docker host 上的 containers 统一管理;可以查看 images,甚至 build images;并提供 RESTful API 等等。 Shipyard 要管理和控制 Docker host 的话需要先修改 Docker host 上的默认配置使其支持远程管理。Shipyard也是基于Docker API实现的容器图形管理系统,支持container、images、engine、cluster等功能,可满足我们基本的容器部署需求。
Shipyard是一个集成管理docker容器、镜像、Registries的系统,它可以简化对横跨多个主机的Docker容器集群进行管理. 通过Web用户界面,你可以大致浏览相关信息,比如你的容器在使用多少处理器和内存资源、在运行哪些容器,还可以检查所有集群上的事件日志。 特点:- ①、支持节点动态集群,可扩展节点的规模(swarm、etcd方案)
- ②、支持镜像管理、容器管理、节点管理等功能
- ③、可视化的容器管理和监控管理
- ④、在线容console终端
- Shipyard分为手动部署和自动部署。
镜像名称 | 运行服务 | 描述 |
---|---|---|
rethinkdb | shipyard数据库 | 一个NoSQL数据库,用于存储shipyard系统的数据,比如账号、节点、容器等信息。 |
microbox/eted | 服务注册、发现系统 | K/V存储系统,用于Swarm节点实现服务注册、发现。也支持consul. zookeeper |
shipyard/docker-proxy | docker AP代理 | 连接本地/var/run/docker.sock代理,用于让Swarm Agent连接AP管理。 |
swarm | swarm集群 | 官方管理 Docker 集群工具,使得多个 engine 为一个整体管理,对外提供Swarm manager API,用户就像操作单台Engine一样。 |
shipyard/shipyard | shipyard前端 | 容器Web管理系统,内部连接Swarm Manager管理容器和RethinkDB存储数据。 |
1)RethinkDB
deploy首先启动的就是RethinkDB容器,shipyard采用RethinkDB作为数据库来保存用户等信息
2)Discovery
为了使用Swarm,我们需要一个外部的密钥值存储群容器,shipyard默认是采用了etcd。
3)shipyard_certs
证书管理容器,实现证书验证功能
4)Proxy
默认情况下,Docker引擎只监听Socket,我们可以重新配置引擎使用TLS或者使用一个代理容器,转发请求从TCP到Docker监听的UNIX Socket。
5)Swarm Manager
Swarm管理器
6)Swarm Agent
Swarm代理,运行在每个节点上。
7)Controller
shipyard控制器,Remote API的实现和web的实现。
官网 Reference:https://shipyard-project.com/
手动部署 Reference:https://shipyard-project.com/manual-deployment/
自动部署 Reference:https://shipyard-project.com/automated-deployment/
GitHub Reference:https://github.com/ehazlett/shipyard
6.2.1 手动部署 Shipyard
拉取相应镜像(可选择)
#!/bin/bash
images=(
rethinkdb
microbox/etcd
shipyard/docker-proxy:latest
swarm:latest
shipyard/shipyard:latest
)
for imageName in ${images[@]} ; do
docker pull $imageName
done
- DataStore 部署
$ docker run \
-ti \
-d \
--restart=always \
--name shipyard-rethinkdb \
rethinkdb
# Shipyard使用RethinkDB作为数据存储。首先,我们将启动一个 RethinkDB 容器。
- Discovery 部署
$ docker run \
-ti \
-d \
-p 4001:4001 \
-p 7001:7001 \
--restart=always \
--name shipyard-discovery \
microbox/etcd -name discovery
# 要启用 Swarm 领导者选举,我们必须使用 Swarm 容器中的外部键值存储。对于此示例,我们将使用etcd,但是,您可以使用 Swarm 支持的任何键/值后端。
- Proxy 部署
$ docker run \
-ti \
-d \
-p 2375:2375 \
--hostname=$HOSTNAME \
--restart=always \
--name shipyard-proxy \
-v /var/run/docker.sock:/var/run/docker.sock \
-e PORT=2375 \
shipyard/docker-proxy:latest
# 默认情况下,Docker 引擎仅侦听套接字。我们可以重新配置引擎以使用 TLS,或者您可以使用代理容器。这是一个非常轻量级的容器,它只是将来自TCP的请求转发到Docker侦听的Unix套接字。
# 注意:如果您使用的是手动TCP / TLS配置,则不需要此功能。
- Swarm Manager 部署
$ docker run \
-ti \
-d \
--restart=always \
--name shipyard-swarm-manager \
swarm:latest \
manage --host tcp://0.0.0.0:3375 etcd://<IP-OF-HOST>:4001
# 运行配置为管理的 Swarm 容器。
# IP-OF-HOST=宿主机IP地址,例如110.41.20.249
- Swarm Agent 部署
$ docker run \
-ti \
-d \
--restart=always \
--name shipyard-swarm-agent \
swarm:latest \
join --addr <IP-OF-HOST>:2375 etcd://<IP-OF-HOST>:4001
# 将运行配置为管理的 Swarm 容器。
# IP-OF-HOST=宿主机IP地址,例如110.41.20.249
- Controller 部署
$ docker run \
-ti \
-d \
--restart=always \
--name shipyard-controller \
--link shipyard-rethinkdb:rethinkdb \
--link shipyard-swarm-manager:swarm \
-p 8080:8080 \
shipyard/shipyard:latest \
server \
-d tcp://swarm:3375
# 运行造船厂控制器
# 控制器启动并且控制器初始化数据存储后,您应该能够通过 http://[ip-of-host]:8080 登录。
- 浏览器访问 Shipyard :
http://<IP地址>:8080
。例如:http://110.41.20.249:8080/
默认帐号:admin
默认密码:shipyard
就可以查看到相应的数据(机器的所有容器)
查看机器的镜像列表
可以进入到容器的控制台界面
6.2.2 自动部署 Shipyard
部署后,脚本将输出要连接的 URL 以及凭据信息。 注意:这将在端口 2375 上公开 Docker 引擎。如果此节点可在安全网络外部访问,则建议使用 TLS。( 1 )选项 要自定义部署,您可以指定以下环境变量。 ( 2 )行动 这将控制部署的操作。可用选项包括:
curl -sSL https://shipyard-project.com/deploy | bash -s
- deploy:部署新的船厂实例
- upgrade:升级现有实例(注意:您需要传递与部署时相同的环境变量以保持相同的配置)
- node:将当前 Docker 引擎添加为集群中的新 Swarm 节点
- remove:完全移除造船厂
curl -sSL https://shipyard-project.com/deploy | ACTION=node DISCOVERY=etcd://10.0.1.10:4001 bash -s
7 容器监控
7.1 cAdvisor + InfluxDB + Grafana 介绍
cAdvisor:Google开源的工具,用于监控Docker主机和容器系统资源,通过图形页面实时显示数据,但不存储;它通过宿主机/proc、/sys、/var/lib/docker
等目录下文件获取宿主机和容器运行信息。
InfluxDB:是一个分布式的时间序列数据库,用来存储cAdvisor收集的系统资源数据。
Grafana:可视化展示平台,可做仪表盘,并图表页面操作很方面,数据源支持zabbix、Graphite、InfluxDB、OpenTSDB、Elasticsearch等。
它们之间关系:
cAdvisor容器数据采集 → InfluxDB容器数据存储 → Grafana可视化展示
7.2 cAdvisor + InfluxDB + Grafana 部署
7.2.1 InfluxDB 部署
# 部署 InfluxDB
$ docker run -itd -p 8083:8083 -p 8086:8086 --name influxdb tutum/influxdb
浏览器访问InfluxDB[ http://<IP地址>:8083 ]
后创建数据库 “cadvisor
“
# 创建数据库
CREATE DATABASE "cadvisor"
# 创建数据库用户和设置密码
CREATE USER "cadvisor" WITH PASSWORD 'cadvisor'
7.2.2 cAdvisor 部署
# 部署 cAdvisor
$ docker run -it -d \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:rw \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker:/var/lib/docker:ro \
--link influxdb:influxdb \
-p 8081:8080 \
--name cadvisor \
google/cadvisor:latest \
-storage_driver=influxdb \
-storage_driver_db=cadvisor \
-storage_driver_host=influxdb:8086
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28471f28ab84 google/cadvisor:latest "/usr/bin/cadvisor -…" 10 seconds ago Up 9 seconds 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp cadvisor
浏览器访问cAdvisor[ http://<IP地址>:8081 ]
7.2.3 Grafana 部署
# 部署 Grafana
$ docker run -itd \
-p 3000:3000 \
-e INFLUXDB_HOST=localhost \
-e INFLUXDB_PORT=8086 \
-e INFLUXDB_NAME=cadvisor \
-e INFLUXDB_USER=cadvisor \
-e INFLUXDB_PASS=cadvisor \
--link influxdb:influxsrv \
--name grafana \
grafana/grafana
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cdf819d018bd grafana/grafana "/run.sh" 4 minutes ago Up 4 minutes 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp grafana
- 浏览器访问 Shipyard :
http://<IP地址>:3000
。例如:http://110.41.20.249:3000/
默认帐号:admin
默认密码:admin
- 添加数据源信息→选择”InfluxDB”
相关 InfluxDB 的信息
- 管理员直接导入Grafana 的Docker 模板信息即可