Docker基础
一、Docker简介
1. Docker是什么?
(1)容器概述
容器是在linux上本机运行,并与其他容器共享主机的内核,它运行的是一个独立的进程,不占用其他任何可执行文件的内存,非常轻量
(2)docker概述
- Docker是一个开源的应用容器引擎,基于go语言开发并遵循apache2.0协议开源
- Docker是在Linux容器里运行应用的开源工具,是一种轻量级的“虚拟机”
- Docker的容器技术可以在一台主机上轻松为任何应用创建一个轻量级的、可移植的、自给自足的容器,也可以利用docker在多台主机上轻松的创建并运行容器
官网:https://www.docker.com/
中文官网:http://www.docker-cn.com/
(3)Docker的设计宗旨
Build,Ship and Run Any App Anywhere
通过对应用组件的封装、发布、部署、运行等生命周期的管理,达到应用组件级别的“一次封装,到处运行”的目的。这里的组件,既可以是一个应用,也可以是一套服务,甚至是一个完整的操作系统。
2. Docker作用
Docker是一种容器技术,使用Docker可以:
- 将软件环境安装并配置好,打包成一个镜像Image,然后将该镜像发布出去(Docker仓库)
- 其他使用者可以在仓库中下载获取这个镜像
- 通过Docker运行这个镜像,就可以获取同样的环境(容器)
Docker简化了环境部署和配置,实现“一次构建,处处运行”,避免了因运行环境不一致而导致的异常
可以将Docker简单的认为是一个虚拟机,可以运行各种软件环境的虚拟机,但与传统虚拟机技术有所不同
Docker容器技术与传统虚拟机技术的区别:
- 传统虚拟机技术:模拟一个完整的操作系统,先虚拟出一套硬件,然后在其上安装操作系统,最后在系统上再运行应用程序
缺点:资源占用多,启动慢 - Docker容器技术:不是模拟一个完整的操作系统,没有进行硬件虚拟,而是对进程进行隔离,封装成容器,容器内的应用程序是直接使用宿主机的内核,且容器之间是互相隔离的,互不影响
优点:更轻便、效率高、启动快、秒级
3.docker容器和虚拟机(vm)区别:
容器是在Linux上本机运行,并与其他容器共享主机的内核,它运行的是一个独立的进程,不占其他任何可执行文件的内存,非常轻量。
虚拟机运行的是一个完整的操作系统,通过虚拟机管理程序对主机资源进行虚拟访问,相比之下需要的资源更多。
特性 | Docker容器 | 虚拟机 |
---|---|---|
启动速度 | 秒级 | 分钟级 |
计算能力损耗 | 几乎无 | 损耗50%左右 |
性能 | 接近原生 | 弱于 |
系统支持量(单机) | 上千个(根据业务大小而定,一般在200多个左右) | 几十个 |
操作系统 | 主要支持Linux | 几乎所有 |
隔离性 | 进程级别,资源隔离/限制 | 系统级别,完全隔离 |
部署难度 | 非常简单 | 组件多,部署复杂 |
执行性能 | 和物理系统几乎一致 | vm会占用一些资源 |
镜像体积 | 镜像MB级别 | 镜像GB级别 |
管理效率 | 简单 | 组件相互依赖,管理复杂 |
网络连接 | 比较弱 | 借助neutron可以灵活组件各类网络管理 |
4.容器在内核中支持两种重要的技术
docker本质就是宿主机的一个进程,docker是通过namespace实现资源隔离,通过cgroup实现资源限制,通过写实复制技术(copy-on-write)实现了高效的文件系统(类似虚拟机的磁盘比如分配500g并不是实际占用物理磁盘500g)
namespace的六项隔离:
namespace | 系统调用参数 | 隔离内容 |
---|---|---|
UTS | CLONE_NEWUTS | 主机名与域名 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 |
PID | CLONE_NEWPID | 进程编号 |
NETWORK | CLONE_NEWNET | 网络设备、网络栈、端口等 |
MOUNT | CLONE_NEWNS | 挂载点(文件系统) |
USER | CLONE_NEWUSER | 用户和用户组 |
5. Docker核心概念
(1)Docker主机(Host)
安装了Docker程序的主机,运行Docker守护进程
(2)Docker镜像(Image)
Docker的镜像是创建容器的基础,类似虚拟机的快照,可以理解为一个面向Docker容器引擎的只读模板。
通过镜像启动一个容器,一个镜像是一个可执行的包,其中包括运行应用程序所需要的所有内容包含代码、运行时间、库、环境变量、和配置文件。
Docker镜像也是一个压缩包,只是这个压缩包不只是可执行文件,环境部署脚本,它还包含了完整的操作系统。因为大部分的镜像都是基于操作系统来构建,所以很轻松的就可以构建本地和远端一样的环境,这也是Docker镜像的精髓
(3)Docker容器(Container)
Docker的容器是从镜像创建的运行实例,他可以被启动、停止和删除。所创建的每一个容器都是相互隔离、互不可见,以保证平台的安全性
可以把容器看做是一个简易版的linux环境(包括root用户权限、镜像空间、用户空间和网络空间等)和运行在其中的应用程序。
(4)Docker仓库(Repository)
Docker仓库是用来集中保存镜像的地方,当创建了自己的镜像之后,可以使用push命令将它上传到公有仓库(pubile)或者私有仓库(private)。当下次要在另外一台机器上使用这个镜像时,只需从仓库获取
容器的三要素:Docker镜像(Image)、Docker容器(Container)、Docker仓库(Repository)
使用Docker的步骤:
1. 安装Docker
2. 从Docker仓库中下载软件对应的镜像
3. 运行这个镜像,此时会生成一个Docker容器
4. 对容器的启动/停止就是对软件的启动/停止
注:Docker的镜像、容器、日志等内容全部都默认存储在 /var/lib/docker/目录下
二、Docker安装
1、环境准备
查看服务器信息:
cat /proc/cpuinfo # 查看cpu信息 cat /proc/meminfo # 查看内存信息 uname -r # 查看内核信息,Docker要求CentOS必须是64位,且内核是3.10及以上 sudo reboot # 重启,sudo表示以管理员root身份执行 sudo halt # 关机
关闭安全策略:
(1)关闭防火墙 systemctl stop firewalld (2)关闭SELinux vi /etc/selinux/config 将SELINUX=enforcing改为SELINUX=disabled 重启 reboot 临时关闭SELinux setenforce 0
2、内核升级
首先uname -r 看看现在的内核版本号是否是3.1以下,则需要升级
1、导入public key
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
2、安装ELRepo到CentOS-6.5中
rpm -ivh http://www.elrepo.org/elrepo-release-6-8.el6.elrepo.noarch.rpm
3、安装 kernel-lt(lt=long-term)
yum --enablerepo=elrepo-kernel install kernel-lt -y
或者安装kernel-ml(ml=mainline)
yum --enablerepo=elrepo-kernel install kernel-ml -y
4、修改引导文件,将默认引导改为0
vi /etc/grub.conf
改为default=0
5、重启查看版本
说明:1、如果报curl: (35) SSL connect error错误
2、执行yum update nss
2. 安装Docker
Docker版本:社区版CE、企业版EE
安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2 ------------------------------------------------------- 1.yum-utils:提供了yum-config-manager工具 2.device mapper:是Linux内核中支持逻辑卷管理的通用设备映射机制,它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构 3.device mapper存储驱动程序需要device-mapper-persistent-data和lvm2
设置阿里云镜像源
yum-config-manager --add-repo https:
//mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安装:
yum install -y docker-ce docker-ce-cli containerd.io
3. 启动/停止
docker version # 查看版本 systemctl start docker # 启动 systemctl stop docker # 停止 systemctl status docker # 查看状态 systemctl restart docker # 重启 systemctl enable docker # 设置开机自动启动 # 验证,运行hello-world docker run hello-world # 下载hello-world镜像并运行
4. Docker卸载
查看已安装的包:yum list installed | grep docker 删除软件包:yum -y remove docker-io.x86_64 删除镜像/容器:rm -rf /var/lib/docker
4. 配置Docker镜像加速
使用阿里云提供的镜像加速(镜像仓库),也可以使用网易云等
#阿里云平台
https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
步骤:
-
注册并登陆“阿里云的开发者平台" http://dev.aliyun.com
-
查看专属的加速器地址
-
配置自己的Docker加
#阿里云 vi /etc/docker/daemon.json {"registry-mirrors": ["https://sswv6yx0.mirror.aliyuncs.com"] }
# 网易云 {"registry-mirrors": ["http://hub-mirror.c.163.com"] } 重新加载 systemctl daemon-reload systemctl restart docker
三、Docker操作
输入docker
可以查看Docker的命令用法,输入docker COMMAND --help
查看指定命令的详细用法
1. 镜像操作
操作 | 命令 | 说明 |
---|---|---|
查找 | docker search 关键字 | 可以在Docker Hub网站查看镜像的详细信息,如镜像的tag标签 |
抽取 | docker pull 镜像名:tag | :tag表示软件的版本,如果不指定默认是latest |
查看镜像信息 |
镜像下载后存放在 / var /lib/docker |
|
列表 | docker images |
查看所有本地镜像 |
获取元信息 | docker inspect 镜像id | 获取镜像的元信息,详细信息 |
删除 |
docker rmi -f 镜像id或镜像名:tag docker rmi -f $(dockeer images -qa) 删除全部 #批量删镜像 |
删除指定的本地镜像,-f表示强制删除 |
镜像设置标签/新标签 |
docker tag 860c279d2fec runoob/centos:dev
|
docker tag 镜像ID,这里是 860c279d2fec ,用户名称、 镜像源名(repository name)和新的标签名(tag) |
查看版本信息和详细内容 |
docker version
|
|
显示镜像的摘要信息 显示镜像完整要信息 |
docker images --digests docker images --no-trunc |
2. 容器操作
操作 | 命令 | 说明 |
---|---|---|
运行 |
docker run --name 容器名 -i -t -p 主机端口:容器端口 -d -v 主机目录:容器目录:ro 镜像id或镜像名称:tag 例如:
|
--name 指定容器名,名称自定义,如果不指定会自动命名; -i 以交互模式运行,即以交互模式运行容器; -t 分配一个伪终端,即命令行,通常组合使用-it ; -p 指定端口映射,将主机端口映射到容器内的端口; -d 表示后台运行,即守护式运行容器; -v 指定挂载主机目录到容器目录,默认为rw读写模式,ro表示只读 --net=host 指明网络模式为host,如果不指明默认为bridge --privileged=true 启动拥有特权模式 |
容器自启动 |
#创建容器时设置 docker run -d --restart=always --name 容器名称 镜像名称 #更新已有容器 docker update --restart=always 容器ID # 例如: docker update --restart=always 56f0b18af626
#关闭容器自启动的命令 docker update --restart=no 容器名或id |
# 多个参数值选择 docker update --restart=always 容器ID |
列表 |
docker ps -a -q docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}" |
1、查看正在运行的容器,-a表示显示所有容器,-q表示只显示容器id 2、显示指定列 |
启动 | docker start 容器id或容器名称 | 启动容器 |
停止 |
docker stop 容器id或容器名称 docker kill 容器的ID/名称 |
1、停止正在运行的容器 2、kill和stop区别在于stop会通知容器需要进行关闭,在等待10S左右关闭容器,预留一个服务缓存数据保存的时间,而kill会立刻关闭容器,缓存在内存中的数据有可能会直接丢失 |
删除 |
docker rm [-f] 容器id或容器名称 #批量停止容器 |
删除容器,-f表示强制删除正在运行的容器 |
日志 |
docker logs 容器id或容器名称 docker logs -f --tail 容器id |
1、获取容器的日志 2、参数说明: -t 是加入时间戳 -f 跟随最新的日志打印 --tail 数字 显示最后多少条 |
在容器中执行 |
docker exec -it 容器id或容器名称 /bin/bash docker attach 容器id或容器名称 |
1、进入正在运行的容器中并开启一个交互模式的终端,可以在容器中执行操作 2、如果不指定会自动命名; -i 以交互模式运行,即以交互模式运行容器;-t 分配一个伪终端,即命令行,通常组合使用-it 3、区别:exec:容器中打开的终端,并且可以启动新的进程 attach:直接进入容器启动命令的终端,不启动新的进程 |
拷贝文件 |
docker cp 主机中的文件路径 容器id或容器名称:容器路径; docker cp 容器id或容器名称:容器中的文件路径 主机路径 列如:
|
将文件中的文件拷贝到容器中;将容器中的文件拷贝到主机中 |
获取容器内部元细节信息 |
docker inspect 容器id docker inspect 实例:标签 |
获取容器的元信息 |
查看容器内运行的进程 | docker top 容器id | |
清理掉所有处于终止状态的容器 | docker container prune | |
查看容器日志路径 |
|
|
查看网络 |
docker network ls docker network inspect composetest_default |
可以直接执行 docker run 命令,等同于先执行 docker create 命令,再执行 docker start 命令。 注意∶容器是一个与其中运行的 shell 命令共存亡的终端,命令运行容器运行,命令结束容器退出。 docker 容器默认会把容器内部第一个进程,也就是 pid=1 的程序作为docker容器是否正在运行的依据,如果docker容器中pid = 1 的进程挂了,那么docker容器便会直接退出,也就是说Docker容器中必须有一个前台进程,否则认为容器已经挂掉。 当利用 docker run 来创建容器时,Docker 在后台的标准运行过程是∶ (1)检查本地是否存在指定的镜像。当镜像不存在时,会从公有仓库下载 (2)利用镜像创建并启动一个容器 (3)分配一个文件系统给容器,在只读的镜像层外面挂载一层可读写层 (4)从宿主主机配置的网桥接口中桥接一个虚拟机接口到容器中 (5)分配一个地址池中的 IP 地址给容器; (6)执行用户指定的应用程序,执行完毕后容器被终止运行
3.案例
以CentOS为例:
docker search centos
docker pull centos
docker run --name mycentos -it centos:latest # 根据centos:latest镜像运行容器,并以交互模式进入容器中
# 实际上是在Docker容器中运行一个精简版的CentOS系统
exit # 退出并关闭容器
docker ps -a
docker start mycentos
docker stop mycentos
docker rm mycentos
docker rm -f $(docker ps -aq) # 强制删除所有容器
docker exec -i -t mycentos /bin/bash 进入镜像
注:Docker容器内实际上是运行着一个精简版的Linux系统
以tomcat为例:
# 示例1:基本用法
docker search tomcat
docker pull tomcat
docker run --name mytomcat -p 8888:8080 -d tomcat
# 测试:http://宿主机地址:8888
docker stop mytomcat
docker ps -a
docker start mytomcat
# 示例2:拷贝文件和挂载目录
docker run -p 8080:8080 -d tomcat
docker exec -it 70cba924861c /bin/bash
cd /usr/local/tomcat/webapps/ROOT
exit
echo welcome to tomcat > index.jsp
docker cp index.jsp 70cba924861c:/usr/local/tomcat/webapps/ROOT # 将宿主机中的文件拷贝到容器中指定的目录中
# 部署web项目,将war文件放到容器中
docker cp spring-web.war 70cba924861c:/usr/local/tomcat/webapps
# 问题:如果项目更改了需要重新拷贝war文件,太麻烦,可以直接挂载目录(也称为数据卷Volume)
docker run \
-p 8080:8080 \
-v /my/tomcat/webapps/spring-web.war:/usr/local/tomcat/webapps/spring-web.war \
-v /my/tomcat/data:/usr/local/tomcat/dataVolume:ro \
-d tomcat
# 示例3:启动多个容器,一个镜像可以启动多个容器,互相隔离、独立
docker run -p 8081:8080 -d tomcat
docker run -p 8082:8080 -d tomcat
docker run -p 8083:8080 -d tomcat
4.容器的导出和导入(export和import)
4 .1导出容器
docker export [options] container
方式一: #如果要导出本地某个容器,可以使用 docker export 命令。 命令:docker export 容器名或者容器ID > 导出的路径以及tar包的名字 案例: docker export 1e560fca3906 > centos.tar 方式二: 命令:docker export -o 导出的路径及tar包的名称 容器名称或容器ID #其中,-o表示输出的文件,这里指定了输出的路径,如果没有指定路径,则默认生成到当前文件夹。dockerdemocontainer.tar为目标文件,dockerdemo为源容器名。 docker export -o D:\containers\dockerdemocontainer.tar dockerdemo
4 .2 导入容器
#从tar包导入内容为docker镜像 docker import [options] file|URL|- [REPOSITORY[:TAG]] OPTIONS说明: -c :应用docker 指令创建镜像; -m :提交时的说明文字 #可以使用 docker import 从容器快照文件中再导入为镜像, 命令:docker import tar包路径 自定义镜像名称TAG (默认是latest)
案例1
#以下实例将快照文件 centos.tar 导入到镜像 test/centos:v1
docker import centos.tar test/centos:v1
案例2: docker import dockerdemocontainer.tar dockerdemo:imp #dockerdemocontainer.tar表示要导入的容器,dockerdemo:imp表示导入后的镜像名称,imp表示给导入的镜像打tag。 案例3 #也可以通过指定 URL 或者某个目录来导入,例如: docker import http://example.com/exampleimage.tgz example/imagerepo
案例4cat 文件名 | docker import - 镜像名称:标签
cat nginx.tar | docker import - nginx:test
docker run -itd --name jc 0553d2743bba bash #重新创建容器
注:实例化失败,提示 docker: Error response from daemon: No command specified
解决方法:使用docker ps -a ,查看被导出的容器的COMMAND
docker run -d -p 81:80 -p 3307:3306 test/centos:v1 /run.sh
5、镜像的导出与导入(save和load)
5.1docker镜像的导出
命令: docker save [options] images [images...] docker save > 自定义tar包名 镜像名称:TAG (默认是latest) 案例1: docker save -o dockerdemo.tar dockerdemo 案例2: docker save > dockerdemo.tar dockerdemo 说明:其中-o和>表示输出到文件,dockerdemo.tar为导出的目标文件,dockerdemo为源镜像名。
5.2docker镜像的导入
导入命令load: docker load [options] 案例1: docker load -i dockerdemo.tar 案例2: docker load < dockerdemo.tar 说明:其中-i(i即imput)和<表示从文件输入。上面的两个命令都会成功导入镜像以及相关元数据,包括tag信息。
5.3docker export和docker save的区别
1.镜像导入是一个复制的过程,容器导入是将当前容器变成一个新的镜像 2.docker save保存的是镜像(image),docker export保存的是容器(container); 3.docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像; 4.export命令导出的tar文件略小于save命令导出的。 5.因为export导出的是容器,export导出的文件在import导入时,无法保留镜像所有的历史(即每一层layer信息),不能进行回滚操作。
而save是根据镜像来的,所以导入时可以完整保留下每一层layer信息。如下图所示:dockerdemo:latest是save导出load导入的,dockerdemo:imp是export导出import导入的。 6.docker load不能对导入的镜像重命名,而docker import导入可以为镜像指定新名称。例如,上面导入的时候指定dockerdeom:imp。 1)对于是使用镜像导入导出还是使用容器导入导出该如何选择呢?有下面两点建议: 2)若是只想备份image,使用save和load。 若是在启动容器后,容器内容有变化,需要备份,则使用export和import。
四、上传镜像至仓库
1、将本地镜像发布到阿里云
步骤:
-
登陆“阿里云-开发者平台”,创建命名空间和镜像仓
- https://cr.console.aliyun.com/repository/cn-chengdu/ml_yhw/mycentos_yhw1.0.1/details
-
将镜像推送到阿里云
# 登陆阿里云的docker仓库 docker login --username=tangyang8942@163.com registry.cn-hangzhou.aliyuncs.com # 创建指定镜像的tag,归入某个仓库 docker tag b25b1dad795c registry.cn-hangzhou.aliyuncs.com/itany/centos:v1.0 # 将镜像推送到仓库中 docker push registry.cn-hangzhou.aliyuncs.com/itany/centos:v1.0
-
拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/itany/centos:v1.0
2、默认上传到 docker Hub官方公共仓库
默认上传到 docker Hub官方公共仓库,需要注册使用公共仓库的账号https://hub.docker.com 点击使用 docker login 命令来输入用户名、密码和邮箱来完成注册和登录。 在上传镜像之前,还需要先对本地镜像添加新的标签,然后再使用docker push 命令进行上传 docker tag nginx:latest jc111/nginx:web #添加新的标签时必须在前面加上自己的dockerhub的username docker login #登录公共仓库 Username:账号 password:密码 docker push jc111/nginx:web #上传镜像
查看镜像
五、使用Docker搭建环境
1. 安装MySQL
# 1.拉取镜像
docker pull mysql:5.7
# 2.运行容器
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
docker exec -it mysql /bin/bash
find / -name "*mysql*"
exit
# 3.创建用于挂载的目录
mkdir -p /my/mysql/conf # 挂载配置文件
mkdir -p /my/mysql/data # 挂载数据文件
mkdir -p /my/mysql/logs # 挂载日志文件
# 4.拷贝配置文件并修改
docker cp mysql:/etc/mysql/mysql.conf.d/mysqld.cnf /my/mysql/conf/
vi /my/mysql/conf/mysqld.conf
character-set-server=utf8
# 5.重新运行容器
docker rm -f mysql # 删除原来的容器
docker run \
--name mysql \
-p 3306:3306 \
-v /my/mysql/conf:/etc/mysql/mysql.conf.d/ \
-v /my/mysql/data:/var/lib/mysql \
-v /my/mysql/logs:/logs \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
说明:\:是连接符
# 6.访问
# 本地访问
docker exec -it mysql /bin/bash
mysql -u root -p
# 远程访问
mysql -u root -p -h 宿主机地址
#7.备份
docker exec myql服务容器ID sh -c ' exec mysqldump --all-databases -uroot -p"123456" ' > /yhw/all-databases.sql
2. 安装Redis
# 1.拉取镜像
docker pull redis
# 2.创建用于挂载的目录
mkdir -p /my/redis/conf
mkdir -p /my/redis/data
# 3.拷贝配置文件并修改
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
tar zxf redis-4.0.10.tar.gz
cp redis.conf /my/redis/conf/
vi redis.conf
requirepass itany
appendonly yes
# 4.运行容器
docker run \
--name myredis \
-p 6379:6379 \
-v /my/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-v /my/redis/data:/data \
-d redis redis-server /usr/local/etc/redis/redis.conf
# 5.访问
# 本地访问
docker exec -it myredis /bin/bash
redis-cli
# 远程访问
使用RedisDesktopManager工具连接
3. 安装Nginx
# 1.拉取镜像
docker pull nginx
# 2.运行容器
docker run --name mynginx -p 80:80 -d nginx
# 3.创建用于挂载的目录
mkdir -p /my/nginx # 挂载nginx所有数据
mkdir -p /my/nginx/html # 挂载nginx虚拟主机(网站html数据)
# 4.拷贝配置文件
docker cp mynginx:/etc/nginx/nginx.conf /my/nginx # 拷贝主配置文件
docker cp mynginx:/etc/nginx/conf.d /my/nginx # 拷贝虚拟主机配置文件
echo welcome to nginx > /my/nginx/html/index.html # 自定义索引页
# 5.重启运行容器
docker rm -f mynginx
docker run \
--name mynginx \
--privileged=true \
-p 80:80 -p 443:443 \
-v /my/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /my/nginx/html:/usr/share/nginx/html:ro \
-v /etc/nginx/conf.d:/usr/nginx/conf.d \
-d nginx
# 6.测试
http://宿主机地址
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性