Titangene (Titan)
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
1
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
Dockerfile 學習筆記 === ###### tags: `Docker`、`Container`、`DevOps` ## Dockerfile 是什麼? `Dockerfile` 是一個文字檔,其中包含了使用者可以在指令列 (command line) 上使用的所有指令,Docker 透過 `docker build` 指令執行 `Dockerfile` 中的所有指令來自動建置新的 image。 ==???== Dockerfile 紀錄了從 Base Image 到客製化 Image 的安裝步驟,讓使用者利用 Dockerfile 的命令來自訂 Base Image 資訊、維護者資訊、Image 操作命令和容器啟動時的執行命令。 ## Dockerfile 能做什麼? - 可將環境進行版本控制 - 自動建置環境,減少重複步驟 - 自訂建立 image 時要做什麼 - 自訂建立 container 時要做什麼 ## Dockerfile 如何使用? `docker build` 指令可從 `Dockerfile` 和 context 來建立 image,建立的 context 是指定位置 `PATH` 或 `URL` 中的一堆檔案,`PATH` 是本地檔案系統上的目錄,`URL` 是 Git repository 的位置。 context 是以遞迴的方式處理。因此,`PATH` 包含當前目錄和子目錄,`URL` 包含 Git repository 及其子模組,會在 repository 的根目錄中尋找 `Dockfile`。下面例子使用當前目錄作為 `context` 的建置指令: ```shell $ docker build . Sending build context to Docker daemon 6.51 MB ... ``` 建置是由 Docker daemon 執行,不是由 CLI 執行。 建置步驟: 1. 將整個 context (recursively,遞迴地,當前目錄和子目錄) 傳遞給 Docker daemon。 2. 從當前目錄 (不包括子目錄) 中找到 `Dockerfile`。 3. 檢查 `Dockerfile` 語法。 4. 依次執行 `Dockerfile` 中的指令,根據指令生成中間過度 image (存儲在本地,為之後的指令或建置作 cache)。 在 Docker daemon 執行 `Dockerfile` 中的指令之前,Docker daemon 會先執行 `Dockerfile` 的初步驗證,檢查 `Dockerfile` 語法是否有誤,如果語法錯誤時會回傳錯誤訊息: ```shell $ docker build -t test/myapp . Sending build context to Docker daemon 2.048 kB Error response from daemon: Unknown instruction: RUNCMD ``` 在大多數情況下,最好將 `Dockerfile` 放在空目錄中,只新增建置 `Dockerfile` 所需的檔案。 > 警告:不要用 root 目錄 `/` 來作為 `PATH`,因為 Docker 會把硬碟內的全部內容傳輸給Docker daemon。 如果要在建置 context 中使用檔案,可通過 `Dockerfile` 引用指令中指定的檔案,例如 `COPY` 指令。 為了加快建置速度,減少傳遞給 docker daemon 的檔案數量,請自行在 context 目錄中新增 `.dockerignore` 此檔案來排除在建置過程中用不到的檔案和目錄。有關如何建立 `.dockerignore` 此檔案的資訊,請參閱 [.dockerignore file]() 此文件。 使用 `docker build` 指令時,預設會使用在 context 根目錄中的 `Dockerfile`,如果在 `docker build` 指令中使用 `-f` 參數,可以指定特定目錄或特定名稱的 `Dockerfile` 來建置 image,例如: ```shell $ docker build -f /path/to/a/Dockerfile . ``` 使用 `-t` 參數可以在建置成功時,指定 repository 和 tag 來保存新的 image: ```shell $ docker build -t shykes/myapp . ``` 如果想在建置後將 image 標記到多個 repository 中,可以加上多個 `-t` 參數: ```shell $ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest . ``` Docker daemon 會逐個執行 `Dockerfile` 中的指令。在必要時,將每條指令的結果提交給新的 image,最後輸出新 image 的 ID。Docker daemon 會自動清理你傳送的 context。 請注意,每條指令都是獨立執行的,並且會建立一個新 image,所以 `RUN cd /tmp` 對下一行指令沒有任何影響。 只要有可能,Docker 將重新使用中間images(緩存),以顯著加速docker build過程。這由控制台輸出中的使用緩存消息指示。(有關詳細信息,請參閱Dockerfile最佳實踐指南構建緩存部分): 只要有可能,Docker 將重新使用中間映像(緩存),以顯著加速Docker構建過程。 這由控制台輸出中的使用緩存消息指示。 (有關更多信息,請參閱Dockerfile最佳實踐指南中的“構建緩存”部分): 參考連結: - [深入Dockerfile(一): 語法指南 by qianlei90](https://github.com/qianlei90/Blog/issues/35) - [Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/) --- ## Dockerfile 指令格式 關鍵字通常會用全大寫,使用 `#` 來註釋,命令格式為 `[INSTRUCTION] [arguments]` 執行順序:`FROM` > `RUN` > `CMD` = `ENTRYPOINT` ### FROM 格式: - `FROM <image> [AS <name>]` - `FROM <image>[:<tag>] [AS <name>]` - `FROM <image>[@<digest>] [AS <name>]` 第一條命令必須為 `FROM` 命令,說明使用哪個 image 作為基底 (Base Image)。如果想在同一個 Dockerfile 中建立多個 image 時,可以使用多個 `FROM` 命令 (每個 Image 一次) ```dockerfile FROM php:7.1-apache ``` 也可以搭配 `ARG` 命令一起使用 ```dockerfile ARG CODE_VERSION=latest FROM base:${CODE_VERSION} CMD /code/run-app FROM extras:${CODE_VERSION} CMD /code/run-extras ``` > 詳情請參考官方的 [Dockerfile reference - FROM | Docker Documentation](https://docs.docker.com/engine/reference/builder/#from) 文件。 ### `MAINTAINER` (deprecated,棄用) 格式:`MAINTAINER [name]` 維護者的資訊 (Dockerfile 的作者名字) ```dockerfile MAINTAINER titangene, Titangene.tw@gmail.com ``` `LABEL` 指令是更加靈活的版本,您應該使用它,因為它可以設定任何所需的 metadata,並且可以輕鬆查看,例如使用 `docker inspect`。 如果想設定與 `MAINTAINER` 欄位對應的標籤就可以使用 `LABEL` 指令: ```dockerfile LABEL maintainer="Titangene.tw@gmail.com" ``` 這可從 `docker inspect` 指令和其他標籤查看。 > 詳情請參考官方的 [Dockerfile reference - MAINTAINER | Docker Documentation](https://docs.docker.com/engine/reference/builder/#maintainer-deprecated) 文件。 ### RUN 格式: - `RUN <command>` - `RUN ["executable", "param1", "param2"]` 建立 Image 時所要執行的命令,執行 Image 中所具有的命令或程式,包括套件安裝、建立目錄或執行程式產生設定檔等,每執行一條 `RUN` 命令,Image 就會新增一層 (將 Image Commit)。`RUN` 命令可分為 Shell (前者) 和 Exec (後者) 方式。 前者是在 shell 終端中執行命令,預設情況是 Linux 上的 `/bin/sh -c` 或 Windows 上的 `cmd /S /C`;後者則使用 `exec` 命令執行,差別在於 exec 執行完後便會登出。如果要指定使用其它終端可以透過第二種方式實作,例如 `RUN ["/bin/bash", "-c", "echo hello"]`。 ```dockerfile RUN apt-get install ruby # 使用 apt-get 安裝套件 ``` 每條 `RUN` 命令將在當前 Image 基底上執行指定命令,並產生新的 Image。當命令較長時可以使用 `\` 來換行。 ```dockerfile RUN /bin/bash -c 'source $HOME/.bashrc; \ echo $HOME' # 等同於下面命令 RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME' ``` > 詳情請參考官方的 [Dockerfile reference - RUN | Docker Documentation](https://docs.docker.com/engine/reference/builder/#run) 文件。 可以使用 && \ 符號將你的 Command 切成小區塊,增加程式可讀性 錯誤的 RUN 用法:RUN 之間是獨立的,不可透過 cd 指令更改目錄位置 ```dockerfile RUN cd /usr/local RUN touch index.html ``` ### CMD 格式: - `CMD ["command", "parameter1", "parameter2"]`,使用 `exec` 執行,官方推薦使用 - `CMD command parameter1 parameter2`,在 `/bin/sh` 中執行,使用在給需要互動的命令 - `CMD ["param1","param2"]`,提供給 `ENTRYPOINT` 的預設參數 ==提供執行Container的預設值參數???== 建立新容器會執行的指令 容器啟動時執行的命令,每個 Dockerfile 只能有一條 `CMD` 命令。如果指定了多條命令,只有最後一條會被執行。 如果使用者啟動容器時候指定了運行的命令 ( `docker run [image]` 後面加上指令或參數),則會覆蓋掉 `CMD` 指定的命令。 > 詳情請參考官方的 [Dockerfile reference - CMD | Docker Documentation](https://docs.docker.com/engine/reference/builder/#cmd) 文件。 ### COPY 格式: - `COPY [--chown=<user>:<group>] <src>... <dest>` - `COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]` (如果路徑有包含空格,就需要用此格式) 將本機上的檔案或目錄,複製到容器的絕對路徑中。本機來源檔案的路徑必須為當下目錄的相對路徑,而容器則必須使用絕對路徑 ```dockerfile COPY hom* /mydir/ # adds all files starting with "hom" COPY hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt" COPY test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/ COPY test /absoluteDir/ # adds "test" to /absoluteDir/ COPY --chown=55:mygroup files* /somedir/ COPY --chown=bin files* /somedir/ COPY --chown=1 files* /somedir/ COPY --chown=10:11 files* /somedir/ ``` > 詳情請參考官方的 [Dockerfile reference - COPY | Docker Documentation](https://docs.docker.com/engine/reference/builder/#copy) 文件。 ### EXPOSE 格式:`EXPOSE <port> [<port>/<protocol>...]` 對外開放的 port,可指定多個,以空格分離 container linsten 的 port mapping: container 的 ### ENV 格式: - `ENV <key> <value>` - `ENV <key>=<value> ...` 設定環境變數 ### ADD 在 Image 裡加入指定路徑的資料夾或檔案 ### ENTRYPOINT 容器啟動後所要執行的命令,格式與 `CMD` 命令相同 固定的命令,將參數傳入 支援 Shell 形式和 Exec 形式,官方建議啟動容器程式或命令的 `ENTRYPOINT` 使用 Exec 形式,其容器內程序是以 PID 1 運行,當容器程序停止時,可確保一併停止容器。若採用 Shell 形式,部分特殊程式有可能會無法正常停止 (SIGTERM),導致容器無法完全退出,殘留程序仍舊佔用著系統資源 ### LABEL 用來標註映像檔的 Metadata,如作者、Email、映像檔描述等等,當透過 `docker inspect [image]` 查看時,在 `ContainerConfig` 下的 Labels 中可查詢到相關資訊 ### VOLUME 格式:`VOLUME ["/path"]` 建立可掛載的目錄 ### WORKDIR 格式:`WORKDIR <path to working directory>` 指定 `RUN`、`CMD`、`ENTRYPOINT`、`ADD`、`COPY` 指令的工作目錄,也會為執行中的 image 執行個體設定工作目錄。 `WORKDIR` 指令可使用多次。如果使用相對路徑,則會沿用前面使用的 `WORKDIR` 指令的路徑繼續指定工作目錄,例如: ```dockerfile WORKDIR /a WORKDIR b WORKDIR c RUN pwd ``` 最後 `pwd` 輸出為 `/a/b/c` `WORKDIR` 指令可以解析在前面使用的 `ENV` 指令 (設置環境變數)。只能使用在 Dockerfile 中設定的環境變數,例如: ```dockerfile ENV DIRPATH /path WORKDIR $DIRPATH/$DIRNAME RUN pwd ``` 最後 `pwd` 輸出為 `/path/$DIRNAME`,因為 Dockerfile 中沒有 `DIRNAME` 的環境變數 > 詳情請參考官方的 [Dockerfile reference - WORKDIR | Docker Documentation](https://docs.docker.com/engine/reference/builder/#workdir) 文件。 ### USER 設定 Image 的使用者 ID 與名稱 ![](https://i.imgur.com/8mwwNEj.png) ## 使用 Dockerfile 建立 image ### 利用 Dockerfile 自製 Node.js 環境的 image 先建立兩個檔案 `Dockerfile` 和 `index.js` ```shell project ├── Dockerfile └── index.js ``` index.js 內容如下: ```javascript var os = require("os"); var hostname = os.hostname(); console.log("hello from " + hostname); ``` Dockerfile 內容如下: ```dockerfile FROM alpine RUN apk update && apk add nodejs COPY . /app WORKDIR /app CMD ["node","index.js"] ``` 接著透過下面指令將 Dockerfile 建立一個名為 `hello:v0.1` 的 image ```shell $ cd project $ docker build -t hello:v0.1 . Sending build context to Docker daemon 3.072kB Step 1/5 : FROM alpine latest: Pulling from library/alpine ff3a5c916c92: Pull complete Digest: sha256:7df6db5aa61ae9480f52f0b3a06a140ab98d427f86d8d5de0bedab9b8df6b1c0 Status: Downloaded newer image for alpine:latest ---> 3fd9065eaf02 Step 2/5 : RUN apk update && apk add nodejs ---> Running in 51e07ef585d3 fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz v3.7.0-56-g2e8e7a0d34 [http://dl-cdn.alpinelinux.org/alpine/v3.7/main] v3.7.0-58-g26701b74f8 [http://dl-cdn.alpinelinux.org/alpine/v3.7/community] OK: 9044 distinct packages available (1/10) Installing ca-certificates (20171114-r0) (2/10) Installing nodejs-npm (8.9.3-r0) (3/10) Installing c-ares (1.13.0-r0) (4/10) Installing libcrypto1.0 (1.0.2n-r0) (5/10) Installing libgcc (6.4.0-r5) (6/10) Installing http-parser (2.7.1-r1) (7/10) Installing libssl1.0 (1.0.2n-r0) (8/10) Installing libstdc++ (6.4.0-r5) (9/10) Installing libuv (1.17.0-r0) (10/10) Installing nodejs (8.9.3-r0) Executing busybox-1.27.2-r7.trigger Executing ca-certificates-20171114-r0.trigger OK: 61 MiB in 21 packages ---> f88a14df1710 Removing intermediate container 51e07ef585d3 Step 3/5 : COPY . /app ---> 28eb9b55974e Step 4/5 : WORKDIR /app ---> a88c7b53351f Removing intermediate container de42f94a18c1 Step 5/5 : CMD node index.js ---> Running in 9b776089e32c ---> 6fd0a40621db Removing intermediate container 9b776089e32c Successfully built 6fd0a40621db Successfully tagged hello:v0.1 ``` 可以用下面指令確認 image 是否已建立,可以看到我們剛剛建立的 image `hello:v0.1` ```shell $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello v0.1 6fd0a40621db 19 minutes ago 50.2MB alpine latest 3fd9065eaf02 2 weeks ago 4.15MB ``` 下面說明 Dockerfile 所做的步驟: - `FROM alpine`:使用 [Alpine Linux](https://hub.docker.com/_/alpine/) [^1] 的 image 作為基底 (Base Image),如果是第一次使用此 image,就會到遠端 (預設是 Docker Hub) pull 該 image - `RUN apk update && apk add nodejs`:更新套件,並安裝 Node.js - `COPY . /app`:將 host 目前目錄中的所有資料夾和檔案 (也就是剛剛建立的 `index.js` ) 複製到 container 的 `/app` 目錄中 - `WORKDIR /app`:設定建置時 container 內的工作目錄,等同於 `$ cd /app` - `CMD ["node","index.js"]`:等同於執行 `$ node index.js`,執行 `index.js` ![](http://training.play-with-docker.com/images/ops-images-dockerfile.svg) 接著利用我們剛剛建立的 image 建立並啟動 container,可以看到 `index.js` 的輸出結果是 container ID ```shell $ docker run hello:v0.1 09122602999f $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 09122602999f hello:v0.1 "node index.js" 15 seconds ago Exited (0) 13 seconds ago quirky_northcutt ``` #### Image Layer image 實際上是分層建立的。Docker 從 Docker Store 或其他地方抓取各種 bits 的 "fetch" 和 "pull" 任務,這些 bits 是用於建立一個或多個容器層 用 `docker historys [image]` 查看剛剛建立的 image,可以看到建置 image 的過程, ```shell $ docker historys hello:v0.1 IMAGE CREATED CREATED BY SIZE COMMENT 6fd0a40621db About a minute ago /bin/sh -c #(nop) CMD ["node" "index.js"] 0B a88c7b53351f About a minute ago /bin/sh -c #(nop) WORKDIR /app 0B 28eb9b55974e About a minute ago /bin/sh -c #(nop) COPY dir:5cf7ea58546cf88... 171B f88a14df1710 About a minute ago /bin/sh -c apk update && apk add nodejs 46.1MB 3fd9065eaf02 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 2 weeks ago /bin/sh -c #(nop) ADD file:093f0723fa46f6c... 4.15MB ``` ```shell echo "console.log(\"this is v0.2\");" >> index.js ``` ```shell $ docker build -t hello:v0.2 . Sending build context to Docker daemon 3.072kB Step 1/5 : FROM alpine ---> 3fd9065eaf02 Step 2/5 : RUN apk update && apk add nodejs ---> Using cache ---> f88a14df1710 Step 3/5 : COPY . /app ---> 319f89e6c437 Step 4/5 : WORKDIR /app ---> 873a59dcbd8a Removing intermediate container d16c79e94d57 Step 5/5 : CMD node index.js ---> Running in 315e6029b38a ---> 8c49f82ab20d Removing intermediate container 315e6029b38a Successfully built 8c49f82ab20d Successfully tagged hello:v0.2 ``` ```shell $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello v0.2 8c49f82ab20d 15 minutes ago 50.2MB hello v0.1 6fd0a40621db 19 minutes ago 50.2MB alpine latest 3fd9065eaf02 2 weeks ago 4.15MB ``` ![](http://training.play-with-docker.com/images/ops-images-cache.svg) ## 相關連結 - [Dockerfile 指令 | Docker —— 從入門到實踐](https://philipzheng.gitbooks.io/docker_practice/content/dockerfile/instructions.html) - [Dockerfile簡單介紹 | Bonze](https://bonze.tw/dockerfile簡單介紹/) [^1]: Alpine Linux 是一套輕量的 Linux OS,Alpine 官方提供的 Docker image 大小大約只有 5 MB

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully