yihaotu
    • 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
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

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

      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.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      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
    • Note Insights New
    • Engagement control
    • Make a copy
    • 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 Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy 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
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

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

    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.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    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
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 網路模擬與分析(3/08):透過 Python 進行自動化運維 & 在 Mininet 中使用 Iperf (封包測量工具) + gnuplot (繪圖套件) ###### tags: `Mininet`、`Iperf`、`Ansible`、`EVE-NG`、`gnuplot` ## 安裝 python 套件 : pip & 安裝 paramiko 安裝 ppa ``` add-apt-repository ppa:deadsnakes/ppa ``` 更新套件&升級套件 ``` apt-get update && apt-get upgrade -y ``` 檢查 python 版本 ``` root@vm2:/home/user# python3 --version Python 3.5.2 ``` 使用`curl`下載 pip 安裝包 ``` root@vm2:/home/user# curl https://bootstrap.pypa.io/pip/3.5/get-pip.py -o get-pip.py % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1863k 100 1863k 0 0 283k 0 0:00:06 0:00:06 --:--:-- 460k ``` 並使用`sudo python3 get-pip.py --force-reinstall`進行安裝 **若有出現以下問題,代表你下載的套件包與 python 版本不符,需要下載正確的版本** ``` root@vm2:/home/user# sudo python3 get-pip.py --force-reinstall ERROR: This script does not work on Python 3.5 The minimum supported Python version is 3.6. Please use https://bootstrap.pypa.io/pip/3.5/get-pip.py instead. ``` 下載 paramiko ``` pip install paramiko ``` --- ## 使用 EVE-NG 建立拓樸 在VMWare 開啟 EVE 虛擬機,並登入給予的網址`192.168.102.158` ![](https://i.imgur.com/LCB50cX.png) 在瀏覽器輸入該網址並建立一個 lab,即可看到以下畫面。 ![](https://i.imgur.com/pVrREdS.png) 新增一個節點 Router,其配置如下 ![](https://i.imgur.com/utIdJ9e.png) 新增一個網路,其配置如下 ![](https://i.imgur.com/QTD3z88.png) 最後我們在新增2個節點,而該拓樸圖如下 ![](https://i.imgur.com/qFD6mjH.png) --- ### 設定 R1 的 dhcp/ssh 功能 將 Router 設定成 dhcp,並且查看該 IP ``` Router>en Router#conf t Router(config)#hostname R1 R1(config)#int e0/2 R1(config-if)#ip addr dhcp R1(config-if)#no sh R1(config-if)# *Dec 31 23:01:48.082: %DHCP-6-ADDRESS_ASSIGN: Interface Ethernet0/2 assigned DHCP address 192.168.102.159, mask 255.255.255.0, hostname R1 R1(config-if)#do show ip int brief Interface IP-Address OK? Method Status Protocol Ethernet0/0 unassigned YES unset administratively down down Ethernet0/1 unassigned YES unset administratively down down Ethernet0/2 192.168.102.159 YES DHCP up up Ethernet0/3 unassigned YES unset administratively down down R1(config-if)# Translating "pnpntpserver.localdomain"...domain server (192.168.102.2) ``` 開啟一台虛擬機,與 R1 進行 ping 的傳輸 ``` root@vm2:/home/user# ping 192.168.102.159 PING 192.168.102.159 (192.168.102.159) 56(84) bytes of data. 64 bytes from 192.168.102.159: icmp_seq=1 ttl=255 time=0.903 ms 64 bytes from 192.168.102.159: icmp_seq=2 ttl=255 time=2.87 ms ^C --- 192.168.102.159 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1000ms rtt min/avg/max/mdev = 0.903/1.888/2.874/0.986 ms root@vm2:/home/user# ``` 回到 R1 設定 ssh 的帳號、密碼 ``` R1(config-if)#username cisco privilege 15 password ci sco R1(config)#ip domain-name test.com R1(config)#crypto key generate rsa The name for the keys will be: R1.test.com Choose the size of the key modulus in the range of 360 to 4096 for your General Purpose Keys. Choosing a key modulus greater than 512 may take a few minutes. How many bits in the modulus [512]: 1024 % Generating 1024 bit RSA keys, keys will be non-exportable... [OK] (elapsed time was 0 seconds) R1(config)# *Dec 31 23:11:23.368: %SSH-5-ENABLED: SSH 1.99 has been enabled R1(config)#ip ssh version 2 R1(config)#line vty 0 4 R1(config-line)#login local R1(config-line)#transport input ssh R1(config-line)# ``` 設定完後,可以回到虛擬機進行 ssh 測試 ``` root@vm2:/home/user# ssh cisco@192.168.102.159 The authenticity of host '192.168.102.159 (192.168.102.159)' can't be established. RSA key fingerprint is SHA256:RWmcEorbsP+ib7reGDGQNFogzE3rFRt8GMev9E36eMQ. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '192.168.102.159' (RSA) to the list of known hosts. Password: Password: R1# ``` --- ## 透過 ansible 進行多台 host 的部署 ### 在 ubuntu 下載 Ansible 下載 ansible 相關指令 ``` $ sudo apt update $ sudo apt install software-properties-common $ sudo add-apt-repository --yes --update ppa:ansible/ansible $ sudo apt install ansible ``` 下載完後,在 EVE 建立該拓樸,由於上一個實驗有先建立相同的拓樸,我們只需要重啟 R1,並重新設定 ssh 規則 ![](https://i.imgur.com/Ynx2xXd.png) 設定完後,再重新進行 ssh 連線,接著就會發生以下問題。 ``` root@vm2:/home/user# ssh cisco@192.168.102.159 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the RSA key sent by the remote host is SHA256:qeGdNCj60VTspA1GqXjpny0PGmd0vPO+IO6TWQsH9B8. Please contact your system administrator. Add correct host key in /root/.ssh/known_hosts to get rid of this message. Offending RSA key in /root/.ssh/known_hosts:3 remove with: ssh-keygen -f "/root/.ssh/known_hosts" -R 192.168.102.159 RSA host key for 192.168.102.159 has changed and you have requested strict checking. Host key verification failed. ``` > 解法: ssh-keygen -f "/root/.ssh/known_hosts" -R 192.168.102.159 確認連線成功並`exit`後,先創建一個資料夾`cisco`,並且創建兩個檔案名叫 ‘hosts’, ‘config_cisco.yml’ 1. 'hosts' ``` [cisco] 192.168.102.159 ansible_connection=local ``` > **設定要管理的機器,這邊我們使用的是"Router"** 2. 'config_cisco.yml' ``` - hosts: 192.168.102.159 # R1 的 ip gather_facts: yes vars: - cisco_host_ip: 192.168.102.159 ssh_username: cisco ssh_password: cisco enable_open: yes enable_password: cisco roles: - config_cisco ``` > **設定該機器的相關變數** 設定完之後,我們接著要根據 cicso_playbook 的格式進行設定 ![](https://i.imgur.com/ojagQwO.png) 因此我們在當前目錄下再創建資料夾`config_cisco`,進入後再創建`tasks`去建立 ansible-playbook 的樹狀圖 再當前目錄下建立檔案 ‘main.yml’ 3. 'main.yml' ``` - name: cisco_description_ssh_certification set_fact: cisco_verification: host: "{{cisco_host_ip}}" username: "{{ssh_username}}" password: "{{ssh_password}}" authorize: "{{enable_open}}" auth_pass: "{{enable_password}}" - name: config e0/0 ios_config: provider: "{{cisco_verification}}" parents: interface Ethernet 0/0 lines: - ip addr 192.168.1.254 255.255.255.0 - no shut - name: config e0/1 ios_config: provider: "{{cisco_verification}}" parents: interface Ethernet 0/1 lines: - ip addr 192.168.2.254 255.255.255.0 - no shut - name: show ip interface ios_command: provider: "{{cisco_verification}}" commands: show ip interface brief register: show_ip_log - name: show_ip_log debug: var: show_ip_log.stdout_lines with_items: show_ip_log.results ``` > **根據 Router 下的 Hosts 去管理多台主機的配置&設定** 儲存後,回到`cisco`的路徑下,使用`tree`查看該樹狀圖 ``` root@vm2:/home/user/cisco/config_cisco# cd .. root@vm2:/home/user/cisco# tree . ├── config_cisco │   └── tasks │   └── main.yml ├── config_cisco.yml └── hosts 2 directories, 3 files ``` > **若未安裝 `tree`,可使用`apt install tree`** 使用`ansible-playbook config_cisco.yml -i hosts`進行配置 ``` PLAY [192.168.102.159] ********************************************************* TASK [Gathering Facts] ********************************************************* [DEPRECATION WARNING]: Distribution Ubuntu 16.04 on host 192.168.102.159 should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See https://docs.ansible.com/ansible /2.9/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg. ok: [192.168.102.159] TASK [config_cisco : cisco_description_ssh_certification] ********************** ok: [192.168.102.159] TASK [config_cisco : config e0/0] ********************************************** changed: [192.168.102.159] TASK [config_cisco : config e0/1] ********************************************** changed: [192.168.102.159] TASK [config_cisco : show ip interface] **************************************** ok: [192.168.102.159] TASK [config_cisco : show_ip_log] ********************************************** ok: [192.168.102.159] => (item=show_ip_log.results) => { "ansible_loop_var": "item", "item": "show_ip_log.results", "show_ip_log.stdout_lines": [ [ "Interface IP-Address OK? Method Status Protocol", "Ethernet0/0 192.168.1.254 YES manual up up ", "Ethernet0/1 192.168.2.254 YES manual up up ", "Ethernet0/2 192.168.102.159 YES DHCP up up ", "Ethernet0/3 unassigned YES unset administratively down down" ] ] } PLAY RECAP ********************************************************************* 192.168.102.159 : ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ``` 接著可以回到 Router 進行配置檔是否成功? ``` R1(config)#do show ip int brief Interface IP-Address OK? Method Status Protocol Ethernet0/0 192.168.1.254 YES manual up up Ethernet0/1 192.168.2.254 YES manual up up Ethernet0/2 192.168.102.159 YES DHCP up up Ethernet0/3 unassigned YES unset administratively down down ``` --- ## 在 Mininet 環境下使用 Iperf + gnuplot 若未安裝 mininet 可以用`sudo apt-get install mininet` 在家目錄創建資料夾`mininet`,進入後使用`mn`創建拓樸 ``` root@vm2:/home/user/mininet# mn *** Creating network *** Adding controller *** Adding hosts: h1 h2 *** Adding switches: s1 *** Adding links: (h1, s1) (h2, s1) *** Configuring hosts h1 h2 *** Starting controller c0 *** Starting 1 switches s1 ... *** Starting CLI: mininet-wifi> ``` 1. net:查看拓樸資訊 ``` h1 h1-eth0:s1-eth1 h2 h2-eth0:s1-eth2 s1 lo: s1-eth1:h1-eth0 s1-eth2:h2-eth0 c0 ``` 2. xterm s1 h1 h2:在 mininet 開啟 s1, h1, h2 虛擬機 ![s1](https://i.imgur.com/TjAIghA.png) ![h1 h2](https://i.imgur.com/SNh2ZU9.png) > 預設 IP 是 `10.0.0.0/24` 網域 3. pingall:測試該拓樸連線 ``` mininet-wifi> pingall *** Ping: testing ping reachability h1 -> h2 h2 -> h1 *** Results: 0% dropped (2/2 received) ``` 4. h1 ping 10.0.0.2 -c 3:在 Mininet 環境下進行封包測試 ![](https://i.imgur.com/GAeKtaq.png) > `-c(count): + [變數]` --- ### Mininet 進行 HTTP 測試 * 對於 Mininet 的環境下,每一個 Host 都可以視為獨立的主機,因此我們可以做一個簡單的測試。 將 h1 設為 HTTPServer,使用該指令`python -m SimpleHTTPServer 80` ![h1](https://i.imgur.com/htyXHIr.png) 而 h2 可以切換成一般使用者進行 firefox 的測試,去查看 HTTPServer。 ![](https://i.imgur.com/cUrc3Yl.png) h1 的網頁伺服器情形 ![](https://i.imgur.com/j1WMAYv.png) 5. 我們也可以透過封包傳遞的過程,去執行`link h1 s1 down`來模擬網路連線中斷的情形。 ![](https://i.imgur.com/gvEe8jY.png) 使用`link h1 s1 up`可以將 link 重新連線,因此可以看到封包中斷後又重新發送封包到 h2 了。 ![](https://i.imgur.com/CFZeOXT.png) 6. 在 Mininet 執行終端機的內容,可以在 mininet 使用`sh + command`,ex:`sh pwd` ![](https://i.imgur.com/cuV0spX.png) --- ### Iperf 進行網路效能檢測 Iperf 一共有兩種傳輸方式:TCP/UDP,在實驗中,我們將 h1 當作 Client,h2 當作 Server。 1. TCP 封包 h2 [server] 執行`iperf -s -i 1` > `-s`: server > `-i(interval) 1`: 計算吞吐量,週期是一秒一次 h1 [client] 執行`iperf -c 10.0.0.2 -t 15` > `-c`: client > `10.0.0.2`: 指定 server 的 ip,這邊我們使用 h2 的 ip > -t(time/duration): 根據傳輸時間去發送 TCP 封包,預設值是10秒,這邊我們設定15秒。 ![](https://i.imgur.com/xYZsgOE.png) 當 Client 發送後,可以看到 Server 收到了每一秒封包的值,還有0-15秒封包平均值。 ![](https://i.imgur.com/bFgqkGA.png) 2. UDP 封包 h2 [server] 執行`iperf -s -i 1 -u` > `-u`: 指定 UDP 封包 h1 [client] 執行`iperf -c 10.0.0.2 -t 15 -u -b 100M` > `-u`: 指定 UDP 封包 > `-b(badnwidth) 100M`: 封包傳送速率為100M > * TCP 與 UDP 不同在於,TCP 是根據你網路的傳輸速率有多快就會給多快。 ![](https://i.imgur.com/bEJeWhl.png) 我們回到 Server,一樣可以看到每筆封包的數值以及平均。 ![](https://i.imgur.com/glEUP3n.png) 3. 多台 Server 設定 假設你的環境需要多個 Server 測試封包的流量,你可以使用`-p`去區分每個 Server 所屬的埠號。 因此我們可以在 Mininet 在新增一台 h1, h2,使用`xterm h1 h2` h2 [server] 執行`iperf -s -t 1 -u -p 5555` h2 [server] 執行`iperf -s -t 1 -u -p 6666` ![](https://i.imgur.com/xTZHy0T.png) 因此現在2台 Server 有了不同的 port 號後,我們的 Client 就可以對於不同的 Server 去進行連線。 ![](https://i.imgur.com/W4XBjn5.png) --- ### 將數值利用 gnuplot 進行繪圖 上一個實驗我們利用了 Iperf 取得了封包的數值,若你想將數值繪成圖,便可以使用這個套件 gnuplot 將數值繪圖起來。 但是在繪圖之前,我們需要將數據記錄起來,因此我們可以使用`>`將數據導向到所指定的檔案內。 h2 [server] 執行`iperf -s -t 1 -u -p 5555 > udp5555` > udp5555: 所指定的檔案 ‘udp5555’ 而 h1 一樣發送 UDP 封包到 Server`iperf -c 10.0.0.2 -t 10 -b 100M -p 5555` 當 h1 結束後,h2 使用`ctrl + C`結束執行,並且查看 ‘udp5555’ 的檔案內是否有 UDP 的資料? ![](https://i.imgur.com/QXah3Ay.png) 結果可以看到資料進來了,代表 UDP 封包的數據已經記錄起來了 ![](https://i.imgur.com/7oph5pR.png) **接著我們可以使用3個 linux 的文字處理:grep, tr, awk 去取得數據的時間跟吞吐量。** 我們可以從 h2 得知數據後,再近一步的過濾出想要的結果。 因此 “udp5555” 過濾結果的過程會如下方所示 ``` 1. cat udp5555 | grep sec // 過濾出“時間”、“吞吐量”共通有的特徵 2. cat udp5555 | grep sec | head -n 20 // 取前20筆資料 3. cat udp5555 | grep sec | head -n 20 | tr "-" " " // 用空格取代每一筆時間軸的 “-” 4. cat udp5555 | grep sec | head -n 20 | tr "-" " " | awk '{print $4, $8}' // 取出“時間”、“吞吐量”的數值 5. cat udp5555 | grep sec | head -n 20 | tr "-" " " | awk '{print $4, $8}' > result.txt // 將結果映射到 “result.txt” ``` 取出結果後,我們可以在終端機使用`gnuplot`的指令進入到 ‘gnuplot’ 內 ``` root@vm2:/home/user# gnuplot G N U P L O T Version 5.0 patchlevel 3 last modified 2016-02-21 Copyright (C) 1986-1993, 1998, 2004, 2007-2016 Thomas Williams, Colin Kelley and many others gnuplot home: http://www.gnuplot.info faq, bugs, etc: type "help FAQ" immediate help: type "help" (plot window: hit 'h') Terminal type set to 'qt' gnuplot> ``` 接著選擇 result.txt 進行繪圖`plot "result.txt"` 接著依照你的結果去設定 x 軸, y 軸,以我的例子是: ``` set yrange [0:30] set ytics 0,5,30 replot set xrange [0:20] set xtics 0,1,20 replot plot "result.txt" with linespoints set xlabel "tine (sec)" set ylabel "throughput (Mbis/sec)" replot set title "udp throughput" replot ``` > `xrange`,`yrange`: x 軸, y 軸參數的範圍 > `xtics`,`ytics`: x 軸, y 軸每個參數之間的間隔 > `xlabel`,`ylabel`: x 軸, y 軸的 label > `title`: 圖片的 title > `replot`: 更新 繪製完圖片後,將格式轉為 gif 檔`set terminal "gif"` 將圖片儲存成 gif 檔`set output "result.gif"`,並且`replot`。 最後你就可以得到一份完整的結果圖了 ![](https://i.imgur.com/0F6AnkK.png) --- ## Reference 1. iperf intro : https://m1016c.pixnet.net/blog/post/145780230 2. iperf command : https://linuxhint.com/iperf_command_usage/ 3. p4-bmv2 download : https://github.com/p4lang/behavioral-model 4. p4c download : https://github.com/p4lang/p4c

    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
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    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