dockerfile
1.Dockerfile是什么
Dockerfile是用来构建docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本
构建三部曲
1.编写Dockerfile文件
2.docker build命令构建镜像
3.docker run运行新编写好的镜像
2.Dockerfile基础知识
1.每条保留字指令都必须大写字母,且后面要跟随至少一个参数
2.指令按上到下,顺序执行
3.# 表示注释
4.每条指令都会创建一个新的镜像层,并对镜像层进行提交
3.Dockerfile执行逻辑
1.docker从基础镜像运行一个容器
2.执行Dockerfile第一条指令对容器做出修改
3.执行类似docker commit的操作,提交一个新的镜像
4.docker再次基于刚才提交的镜像运行一个容器
5.不断重复,直到Dockerfile的命令运行完成
4.Dockerfile指令
命令 | 说明 |
---|---|
FROM | 基础镜像,指定一个已存在的镜像作为模板,第一条必须是FROM |
MAINTAINER(废弃) | 镜像维护者的姓名和邮箱 |
RUN | 容器构建时需要运行的命令(shell和exec格式),RUN是在docker build时运行 |
EXPOSE | 当前容器对外暴露的端口(只是声明作用) |
WORKDIR | 指定在容器创建后,终端默认登录进来的工作目录 |
USER | 指定该镜像以什么样的用户去执行【不指定默认root】 |
ENV | 设置容器的环境变量,Dockerfile中也可以使用 |
ARG | 在docker build的构建参数, --build-arg value='key' 指定 |
ADD | 将宿主机的目录下的文件拷贝到镜像,且自动处理url和解压tar包 |
COPY | 类似add,拷贝文件和目录到镜像 |
VOLUME | 容器数卷,用于数据保存和持久化工作 |
CMD | 指定容器启动后要干的事情,cmd会被docker run之后的参数替换 |
ENTRYPOINT | 用来指定一个容器启动时要运行的命令,类似cmd指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令参数会被当做参数送给ENTRYPOINT指令指定的程序 |
4-1.FROM
如果本地没有镜像
docker images --digests
获取镜像digest
,当镜像内容发生变化时,digest也会随之变化。
FROM <image> # 1.根据镜像最新版本
FROM <image>[:<tag>] # 2.根据版本
FROM <image>[@<digest>] # 3.根据摘要
FROM nginx
FROM nginx:1.18.0-alpine
FROM node:12.18.4-alpine@sha256:757574c5a2102627de54971a0083d4ecd24eb48fdf06b234d063f19f7bbc22fb
4-4-1.FROM案列
Dockerfile
FROM centos:7
执行build命令
docker build -t <image-name>:<tag> .
docker build -t myos:1 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM centos:7
---> eeb6ee3f44bd
Successfully built eeb6ee3f44bd
查看镜像
docker images
REPOSITORY TAG MAGE ID CREATED SIZE
centos 7 eeb6ee3f44bd 24 months ago 203.9 MB
myos 1 eeb6ee3f44bd 24 months ago 203.9 MB
4-2.RUN
RUN命令是在docker build时运行
RUN <shell-cmd> # shell命令格式
RUN ["executable", "param-1", "param-2"] # json数组格式
RUN ["/bin/bash", "-c", "shell-cmd"] # json数组格式(/bin/bash -c)
RUN yum install -y vim
RUN ["yum", "install", "-y", "vim"]
RUN ["/bin/bash", "-c", "yum install -y vim"]
4-2-1.RUN案列
Dockerfile
FROM centos:7
# RUN ["yum", "install" , "-y", "vim"] 这就是json数组类型
RUN yum install -y vim
执行build命令
docker build -t myos:2 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM centos:7
---> eeb6ee3f44bd
Step 2 : RUN yum install -y vim
---> Running in 994799b2b3ce
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
* base: mirrors.163.com
* extras: mirrors.163.com
* updates: mirrors.163.com
Resolving Dependencies
...
...
...
---> f2e7e4989fe4
Removing intermediate container 994799b2b3ce
Successfully built f2e7e4989fe4
查看镜像
docker images
REPOSITORY TAG MAGE ID CREATED SIZE
centos 7 eeb6ee3f44bd 24 months ago 203.9 MB
myos 2 9a87e5acee99 2 minutes ago 441 MB
4-3.EXPOSE暴露端口
这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是
docker run -P
时,会自动随机映射EXPOSE
的端口。
EXPOSE 80 # 默认暴露的是tcp端口
EXPOSE 80/tcp # 暴露tcp的80端口
EXPOSE 80/utp # 暴露udp的80端口
EXPOSE 80 81 # 暴露多个端口
4-4.CMD 容器启动命令
CMD只有在
docker run
时生效(第一次启动容器时)Dockerfile中有多条CMD指令,只有最后一条会生效。
一般推荐使用
exec
格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号"
,而不要使用单引号。方括号内一定要用双引号
shell 格式:CMD <命令>
exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
4-4-1.shell 格式问题
如果使用 shell
格式的话,实际的命令会被包装为 sh -c
的参数的形式进行执行。比如
CMD echo $HOME
在实际执行中,会将其变更为:
CMD ["sh", "-c", "echo $HOME"]
如执行nginx
CMD service nginx start
而刚才说了 CMD service nginx start
会被理解为CMD [ "sh", "-c", "service nginx start"]
因此主进程实际上是 sh
。那么当 service nginx start
命令结束后,sh
也就结束了,sh
作为主进程退出了,自然就会令容器退出。
CMD [ "sh", "-c", "service nginx start"]
正确的做法是直接执行 nginx
可执行文件,并且要求以前台形式运行。比如:
CMD ["nginx", "-g", "daemon off;"]
4-4-2.CMD案列
Dockerfile
FROM centos:7
RUN yum install -y vim
EXPOSE 80
CMD echo "hello docker"
执行build命令
CACHED
表示使用缓存,因为myos:3
时下载了vim,然后myos:4
使用时会使用缓存来加速build
docker build -t myos:4 .
[+] Building 0.1s (6/6) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 108B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:7 0.0s
=> [1/2] FROM docker.io/library/centos:7 0.0s
=> CACHED [2/2] RUN yum install -y vim 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:0e8a0f5c47552374f63b3ea55e91df8874117094bbbb51ee8f71846fb0f1d53e 0.0s
=> => naming to docker.io/library/myos:4 0.0s
运行镜像
docker run myos:4
hello docker
4-4-3.覆盖CMD命令
ls
覆盖了CMD echo "hello docker"
docker run myos:4 ls
anaconda-post.log
bin
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
4-5.ENTRYPOINT
ENTRYPOINT
的目的和CMD
一样,都是在指定容器启动程序及参数。方括号内一定要用双引号
4-5-1.ENTRYPOINT案列
Dockerfile
FROM centos:7
RUN yum install -y vim
EXPOSE 80
ENTRYPOINT ["echo", "hello"]
执行build命令
docker build -t myos:5 .
[+] Building 0.1s (6/6) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 108B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:7 0.0s
=> [1/2] FROM docker.io/library/centos:7 0.0s
=> CACHED [2/2] RUN yum install -y vim 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:c0ec3062d0dfe3db58e4a6e963eb0e2301e6282d4709ea97c814dfac56777ada 0.0s
=> => naming to docker.io/library/myos:5 0.0s
运行镜像
docker run myos:5
hello
4-5-2.替换ENTRYPOINT命令
ENTRYPOINT
在运行时也可以替代,不过比CMD
要略显繁琐,需要通过docker run
的参数--entrypoint
来指定。
追加效果
docker run myos:5 aaa
hello aaa
使用--entrypoint
替换
docker run --entrypoint='ls' myos:5 /
anaconda-post.log
bin
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
4-6.CMD和ENTRYPOINT配合使用
不变的命令使用
ENTRYPOINT
,改变的命令使用CMD
4-6-1.CMD和ENTRYPOINT案列
Dockerfile
FROM centos:7
RUN yum install -y vim
EXPOSE 80
ENTRYPOINT ["echo"]
CMD ["hello"]
build
docker build -t myos:6 .
[+] Building 0.1s (6/6) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 118B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:7 0.0s
=> [1/2] FROM docker.io/library/centos:7 0.0s
=> CACHED [2/2] RUN yum install -y vim 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:4a7ca9ba2a767ea2f3de96ed6594985faf10ed9db4c733519016f1757fb53154 0.0s
=> => naming to docker.io/library/myos:6 0.0s
运行镜像
docker run myos:6
hello
4-6-2.覆盖CMD命令
ENTRYPOINT ["echo"]
和CMD ["hello"]
,docker run myos:6 ccc
只会覆盖CMD的命令。
docker run myos:6 ccc
ccc
4-7.WORKDIR 指定工作目录
使用
WORKDIR
指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR
会帮你建立目录
4-7-1.WORKDIR案列
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /data
build
docker build -t myos:7 .
运行镜像
进入容器时候,在
/data
目录
docker run -it myos:7
[root@fa7e01743128 data]# pwd
/data
4-7-2.执行多次WORKDIR
如果你的
WORKDIR
指令使用的相对路径,那么所切换的路径与之前的WORKDIR
有关
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /data
WORKDIR aaa
WORKDIR bbb
build
docker build -t myos:8 .
运行镜像
docker run -it myos:8
[root@189c4d87d9ae bbb]# pwd
/data/aaa/bbb
4-8.ENV 设置环境变量
ENV
指令用于在容器中设置环境变量。这些环境变量可供容器中的应用程序访问和使用这个值将会在构建阶段中所有后续指令的环境中(如 RUN CMD等)。
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>
4-8-1.ENV案列
Dockerfile
FROM centos:7
RUN yum install -y vim
ENV name test
WORKDIR /$name
build
docker build -t myos:9 .
运行镜像
docker run -it myos:9
# 1.查看当前路径
[root@8d5539c34fa8 test]# pwd
/test
# 2.查看name值
[root@8d5539c34fa8 test]# echo name
name
# 3.查看所有环境变量
[root@8d5539c34fa8 test]# export
...
...
declare -x name="test"
4-9.ARG构建参数
构建参数和
ENV
的效果一样,都是设置环境变量。所不同的是,ARG
所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
ARG <参数名>[=<默认值>]
4-9-1.ARG案列
Dockerfile
FROM centos:7
RUN yum install -y vim
ARG a_name=test
ENV name $a_name
WORKDIR /$name
build
docker build --build-arg a_name='ccc' -t myos:10 .
运行镜像
docker run -it myos:10
[root@2ab82e0537fe ccc]# pwd
/ccc
# 查询不到ARG参数
[root@2ab82e0537fe ccc]# echo $a_name
4-10.VOLUME匿名数据卷
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
4-10-1.VOLUME案列
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /test
VOLUME ["/test"]
build
docker build -t myos:11 .
运行镜像
默认匿名挂载
/var/lib/docker/volumes/
docker run -it myos:11
[root@a60f6ca0e47d test]# pwd
/test
4-10-2.查看数据卷信息
匿名数据卷
docker inspect -f="{{json .Mounts}}" a60f6ca0e47d
[{
"Type":"volume",
"Name":"ecc956f8de8a3cd28b38cce3323acd8174306e32f40238c21947dfee26d3f7db",
"Source":"/var/lib/docker/volumes/ecc956f8de8a3cd28b38cce3323acd8174306e32f40238c21947dfee26d3f7db/_data",
"Destination":"/test",
"Driver":"local",
"Mode":"",
"RW":true,
"Propagation":""
}]
4-10-3.使用-v绑定
把容器内的
/test
挂载到宿主机的/Users/lxd670/aaa
上
docker run -it -v /Users/lxd670/aaa:/test myos:11
docker inspect -f="{{json .Mounts}}" 447cbe523220
[{
"Type":"bind",
"Source":"/Users/lxd670/aaa",
"Destination":"/test",
"Mode":"",
"RW":true,
"Propagation":"rprivate"
}]
4-10-4.删除数据卷
docker volume prune
4-11.ADD更高级的复制文件
4-11-1.注意事项
源路径
源路径可以有多个。如: ADD ["<源路径1>",... "<目标路径>"]
源路径是相对于执行build的相对路径。
源路径是个文件(不以/结尾),容器不存在就创建,存在就覆盖。如: ADD /a/xxx.txt ./
源路径如果是一个目录(以/结尾),则该目录下的所有内容都将被加入到容器,目录不存在就创建。如: ADD /a/b/ ./
如果源文件是个归档文件(压缩文件),则docker会自动帮解压。如: ADD /a/xxx.tar.gz ./
目标路径
目标路径必须是绝对路径,或相对于WORKDIR的相对路径
目标路径如果不存在,则会创建相应的完整路径
目标路径如果不是一个文件,则必须使用/结束
路径中可以使用通配符
4-11-2-.语法
使用该指令的时候还可以加上
--chown=<user>:<group>
选项来改变文件的所属用户及所属组
ADD [--chown=<user>:<group>] <源路径>... <目标路径>
ADD [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
说明
ADD hom* /mydir/ # 通配符添加多个文件
ADD hom?.txt /mydir/ # 通配符添加
ADD test.txt ./mydir/ # 指定相对路径(相对WORKDIR)
ADD test.txt /mydir # 指定绝对路径
ADD <url> /mydir/xxx.html # 使用url
ADD xxx.tar.gz /mydir/ # 使用tar包
改变用户组
ADD --chown=55:mygroup files* /mydir/
ADD --chown=bin files* /mydir/
ADD --chown=1 files* /mydir/
ADD --chown=10:11 files* /mydir/
4-11-3.案列一:重命名
如果结尾没有使用
/
,那么docker会理解为是一个文件名,所以会把a.sql
改为ccc
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /test
VOLUME ["/test"]
# 会把Dockerfile同级的a.sql文件添加到容器根目录中,并且命名为ccc
ADD a.sql /ccc
Build
docker build -t myos:12 .
进入容器
docker run -it myos:12
[root@4a689bec5eb0 /]# ls
anaconda-post.log ccc etc lib media opt root sbin sys tmp var
bin dev home lib64 mnt proc run srv test usr
4-11-4.案列二:绝对路径
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /test
VOLUME ["/test"]
ADD a.sql /ccc/a.sql
build
docker build -t myos:13 .
启的容器
docker run -it myos:13
[root@e1ea9f0a380c ~]# cd /ccc/
[root@e1ea9f0a380c ccc]# ls
a.sql
4-11-5.案列三:tar
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /test
VOLUME ["/test"]
ADD sql.tar.gz /ccc/
build
docker build -t myos:14 .
启的容器
docker run -it myos:14
[root@916b6b6861d6 test]# cd /ccc/
[root@916b6b6861d6 ccc]# ls
sql
[root@916b6b6861d6 ccc]# cd sql/
[root@916b6b6861d6 sql]# ls
a.sql
4-11-6.案列四:url
ADD https://www.baidu.com ./ccc/
会把页面内容保存为__unnamed__
ADD https://www.baidu.com ./ccc/xxx.html
会把页面内容保存为保存为xxx.html
./ccc/
是相对于WORKDIR
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /test
VOLUME ["/test"]
ADD https://www.nkippis.com ./ccc/
build
docker build -t myos:15 .
启的容器
docker run -it myos:15
[root@cb0c702e815b test]# cd ccc
[root@cb0c702e815b ccc]# pwd
/test/ccc
[root@cb0c702e815b ccc]# ls
__unnamed__
4-12.COPY复制文件
4-12-1.注意事项
用
COPY
指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
源路径
源路径可以有多个。如: COPY ["<源路径1>",... "<目标路径>"]
源路径是相对于执行build的相对路径。
源路径是个文件(不以/结尾),容器不存在就创建,存在就覆盖。如: COPY /a/xxx.txt ./
源路径如果是一个目录(以/结尾),则该目录下的所有内容都将被加入到容器,目录不存在就创建。如: COPY /a/b/ ./
目标路径
目标路径必须是绝对路径,或相对于WORKDIR的相对路径
目标路径如果不存在,则会创建相应的完整路径
目标路径如果不是一个文件,则必须使用/结束
路径中可以使用通配符
4-12-2.语法
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
说明
COPY package.json /usr/src/app/
COPY hom* /mydir/
COPY hom?.txt /mydir/
改变用户组
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
4-12-3.COPY案列
Dockerfile
FROM centos:7
RUN yum install -y vim
WORKDIR /test
VOLUME ["/test"]
COPY sql.tar.gz ./
build
docker build -t myos:16 .
启的容器
docker run -it myos:16
[root@7d0458b121b5 test]# ls
sql.tar.gz
5.使用 .dockerignore 文件
.dockerignore
文件会影响ADD
和COPY
命令
-
过滤
__pycache__
文件夹 -
过滤
.pyc
文件 -
过滤
.log
文件
__pycache__
*.pyc
*.log
6.虚悬镜像
仓库名和标签名都是none的镜像,俗称dangling image
查看镜像信息
# 构建失败的话会出现虚悬镜像
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> fd723f14221c 48 seconds ago 441MB
centos 7 eeb6ee3f44bd 11 months ago 204MB
查看构建历史
docker history d80cc75190a8
IMAGE CREATED CREATED BY SIZE COMMENT
d80cc75190a8 9 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
5c3cf05c0817 9 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
9e939e66aec4 9 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
0ba96a7b914a 9 minutes ago /bin/sh -c yum install net-tools -y 182MB
fd723f14221c 11 minutes ago /bin/sh -c yum install vim -y 237MB
f490851a0bb0 12 minutes ago /bin/sh -c #(nop) WORKDIR /ccc 0B
a2ce1fe75d4d 12 minutes ago /bin/sh -c #(nop) ENV TEST_HOME=/ccc 0B
f11c49394506 12 minutes ago /bin/sh -c #(nop) MAINTAINER lxd670<lxd670@… 0B
eeb6ee3f44bd 11 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 11 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 11 months ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
查看虚悬镜像
docker image ls -f dangling=true
删除虚悬镜像
docker image prune
sudo docker rmi -f image_id
7.fastapi案例
7-1.目录结构
.
├── Dockerfile
├── requirements.txt
└── test_app.py
7-2.创建test_app.py
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/v/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
7-3.导出requirements.txt
pip freeze > requirements.txt
生成requirements.txt
annotated-types==0.5.0
anyio==3.7.1
click==8.1.7
exceptiongroup==1.1.3
fastapi==0.103.1
h11==0.14.0
idna==3.4
pydantic==2.3.0
pydantic_core==2.6.3
sniffio==1.3.0
starlette==0.27.0
typing_extensions==4.7.1
uvicorn==0.23.2
7-4.编写Dockerfile
以下是错误的示范,容器启动不了
没有隔开内容:
CMD ["test_app:app", "--host 0.0.0.0", "--port 80"]
单引号问题:
CMD ["test_app:app", "--host", "'0.0.0.0'", "--port", "80"]
FROM python:latest
WORKDIR /test
COPY test_app.py requirements.txt ./
EXPOSE 80
RUN cd /test && pip install -r requirements.txt
ENTRYPOINT ["uvicorn"]
CMD ["test_app:app", "--host", "0.0.0.0", "--port", "80"]
7-5.构建镜像
因为有缓存(cache),所以构建速度快
docker build -t fm:1 .
[+] Building 0.1s (9/9) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 248B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/python:latest 0.0s
=> [1/4] FROM docker.io/library/python:latest 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 69B 0.0s
=> CACHED [2/4] WORKDIR /test 0.0s
=> CACHED [3/4] COPY test_app.py requirements.txt ./ 0.0s
=> CACHED [4/4] RUN cd /test && pip install -r requirements.txt 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:adc299c3776817159faf859da964335b86535a805ada295ca9c461419a317a7c 0.0s
=> => naming to docker.io/library/fm:1 0.0s
7-6.启动容器
docker run -d -p 80:80 fm:1
7-7.查看容器
ONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
98bca29c2dc9 fm:1 "uvicorn test_app:ap…" 4 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp vibrant_archimedes
8.发布
pass
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律