tag:blogger.com,1999:blog-23959064919612031242024-03-23T20:17:02.951+08:00Danny's tech notebook | 丹尼技術手札A personal technical notebook
一本個人的技術手札 (天道酬勤)TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.comBlogger355125tag:blogger.com,1999:blog-2395906491961203124.post-51822825621614063722023-08-29T13:17:00.008+08:002023-08-29T13:21:26.948+08:00[Golang] 在VSCode中管理多個Go專案 Go 1.18+<p><span style="background-color: white; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px;">在進行多個Go專案開發時,我們經常會遇到一些困擾,尤其是當我們嘗試在VSCode中同時打開一個包含多個Go專案的目錄時。這時候,你可能會遇到以下錯誤訊息:</span></p><p class="part in-view" data-endline="6" data-position="108" data-size="0" data-startline="6" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="108" data-size="2" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">錯誤</span></p><pre class="part in-view" data-endline="12" data-position="111" data-startline="7" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">gopls requires a module at the root of your workspace.
You can work with multiple modules by opening each one as a workspace folder.
Improvements to this workflow will be coming soon (https://github.com/golang/go/issues/32394),
and you can learn more here: https://github.com/golang/go/issues/36899.
</code></pre><p class="part in-view" data-endline="15" data-position="420" data-size="0" data-startline="14" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="420" data-size="51" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">從Go 1.18版本開始,原生支持多模塊工作區,這是通過在父目錄中具有一個go.work文件來實現的。</span><br style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;" /><span data-position="472" data-size="13" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">假設我們有這樣的目錄結構:</span></p><pre class="part in-view" data-endline="24" data-position="486" data-startline="16" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">/parent
├── proj1
│ ├── go.mod
│ ├── hello-world.go
└── proj2<br />├── go.mod
├── slices.go
</code></pre><p class="part in-view" data-endline="26" data-position="588" data-size="0" data-startline="26" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="588" data-size="28" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">我們可以通過執行以下命令來創建並填充go.work文件:</span></p><pre class="part in-view" data-endline="32" data-position="617" data-startline="27" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">cd parent
go work init
go work use proj1<br />go work use proj</code>2</pre><p class="part in-view" data-endline="34" data-position="691" data-size="0" data-startline="34" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="691" data-size="39" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">這將在您的父目錄中添加一個go.work文件,其中包含您標記為使用的目錄列表:</span></p><pre class="part in-view" data-endline="42" data-position="731" data-startline="35" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">go 1.18
use (
./proj1<br /> ./proj2<br />)
</code></pre><p class="part in-view" data-endline="44" data-position="787" data-size="0" data-startline="44" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="787" data-size="49" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">通過這種方式,我們就能夠在VSCode中輕鬆地管理和切換不同的Go專案,而無需擔心模塊相關的問題。</span></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-79216300513523927742023-08-21T16:35:00.004+08:002023-08-21T16:35:57.157+08:00[Golang] 實現跨平台編譯的CGO交叉編譯:解鎖Go語言開發的更多可能<h1 class="part" data-endline="1" data-id="實現跨平台編譯的CGO交叉編譯:解鎖Go語言開發的更多可能" data-startline="1" id="實現跨平台編譯的CGO交叉編譯:解鎖Go語言開發的更多可能" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-left: 0px; margin-right: 0px; margin-top: 0px !important; padding-bottom: 0.3em;"><span data-position="2" data-size="29" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">實現跨平台編譯的CGO交叉編譯:解鎖Go語言開發的更多可能</span></h1><p class="part in-view" data-endline="3" data-position="33" data-size="0" data-startline="3" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="33" data-size="101" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在現代軟體開發中,跨平台支援已經成為一個重要的議題。不同的平台可能需要不同的二進位檔案格式或庫,這導致了開發者需要在多個平台上進行編譯。如果你使用的是Go語言,你有幸可以利用CGO交叉編譯來實現這一點。</span></p><h2 class="part in-view" data-endline="5" data-id="CGO交叉編譯的基本概念" data-startline="5" id="CGO交叉編譯的基本概念" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/lA4aogofQjmGHHelAZwgUA#CGO%E4%BA%A4%E5%8F%89%E7%B7%A8%E8%AD%AF%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="CGO交叉編譯的基本概念"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="139" data-size="12" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">CGO交叉編譯的基本概念</span></h2><p class="part in-view" data-endline="6" data-position="152" data-size="0" data-startline="6" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="152" data-size="49" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在沒有使用CGO的情況下,Go語言的交叉編譯非常簡單,只需使用少量的參數即可完成。以下是一個例子:</span></p><pre class="part in-view" data-endline="9" data-position="202" data-startline="7" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
</code></pre><p class="part in-view" data-endline="10" data-position="257" data-size="0" data-startline="10" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="257" data-size="92" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在這個命令中,我們使用了CGO_ENABLED=0來關閉CGO,然後通過GOOS和GOARCH來指定目標平台為Linux,架構為amd64。這樣我們就可以在不使用CGO的情況下進行編譯。</span></p><p class="part in-view" data-endline="12" data-position="352" data-size="0" data-startline="12" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="352" data-size="60" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">另外,我們還可以使用-ldflags選項來添加一些編譯選項,例如去除調試信息、靜態編譯等。以下是一個帶有這些選項的例子:</span></p><pre class="part in-view" data-endline="16" data-position="414" data-startline="14" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-s -w --extldflags "-static -fpic"' main.go
</code></pre><h2 class="part in-view" data-endline="18" data-id="CGO的挑戰與解決辦法" data-startline="18" id="CGO的挑戰與解決辦法" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/lA4aogofQjmGHHelAZwgUA#CGO%E7%9A%84%E6%8C%91%E6%88%B0%E8%88%87%E8%A7%A3%E6%B1%BA%E8%BE%A6%E6%B3%95" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="CGO的挑戰與解決辦法"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="527" data-size="11" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">CGO的挑戰與解決辦法</span></h2><p class="part in-view" data-endline="19" data-position="539" data-size="0" data-startline="19" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="539" data-size="87" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">然而,當我們需要使用CGO進行交叉編譯時,情況就變得複雜了。CGO(C Go調用)允許Go語言調用C函數,但這對於交叉編譯來說可能是一個障礙,因為不同的平台可能有不同的C庫。</span></p><p class="part in-view" data-endline="21" data-position="628" data-size="0" data-startline="21" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="628" data-size="55" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在這種情況下,我們需要關閉CGO,並使用專門的編譯器和選項。以下是一個示例,展示了如何使用CGO進行交叉編譯:</span></p><pre class="part in-view" data-endline="25" data-position="685" data-startline="23" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CGO_LDFLAGS="-static" go build -a -v
</code></pre><p class="part in-view" data-endline="27" data-position="794" data-size="0" data-startline="27" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="794" data-size="104" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在這個命令中,我們將CGO_ENABLED設置為1,以啟用CGO。然後,我們通過CC環境變數指定GCC編譯器,並使用CGO_LDFLAGS來指定CGO部分的編譯為靜態編譯。這樣我們就可以實現帶有CGO的交叉編譯。</span></p><h2 class="part in-view" data-endline="29" data-id="跨平台編譯工具的應用" data-startline="29" id="跨平台編譯工具的應用" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/lA4aogofQjmGHHelAZwgUA#%E8%B7%A8%E5%B9%B3%E5%8F%B0%E7%B7%A8%E8%AD%AF%E5%B7%A5%E5%85%B7%E7%9A%84%E6%87%89%E7%94%A8" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="跨平台編譯工具的應用"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="905" data-size="10" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">跨平台編譯工具的應用</span></h2><p class="part in-view" data-endline="30" data-position="916" data-size="0" data-startline="30" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="916" data-size="66" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">如果你是在macOS平台上進行開發,你可以通過一個名為musl-cross的工具來簡化交叉編譯的過程。以下是如何使用這個工具的步驟:</span></p><ol class="part in-view" data-endline="32" data-position="984" data-size="0" data-startline="32" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em;"><li class="" data-endline="32" data-position="987" data-size="0" data-startline="32" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;"><span data-position="987" data-size="25" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">通過Homebrew安裝musl-cross工具:</span></li></ol><pre class="part in-view" data-endline="35" data-position="1013" data-startline="33" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">brew install FiloSottile/musl-cross/musl-cross
</code></pre><ol class="part in-view" data-endline="37" data-position="1069" data-size="0" data-startline="37" start="2" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em;"><li class="" data-endline="37" data-position="1072" data-size="0" data-startline="37" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;"><span data-position="1072" data-size="50" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">安裝成功後,你可以使用musl-cross工具來實現交叉編譯。只需在編譯命令中指定編譯器和參數即可:</span></li></ol><pre class="part in-view" data-endline="40" data-position="1123" data-startline="38" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CGO_LDFLAGS="-static" go build -a -v
</code></pre><h2 class="part in-view" data-endline="42" data-id="結語" data-startline="42" id="結語" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/lA4aogofQjmGHHelAZwgUA#%E7%B5%90%E8%AA%9E" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="結語"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="1235" data-size="2" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">結語</span></h2><p class="part in-view" data-endline="43" data-position="1238" data-size="0" data-startline="43" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="1238" data-size="144" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">CGO交叉編譯是一個強大的工具,使得Go語言開發者能夠在不同的平台上進行開發和部署。無論是對於沒有CGO的純Go項目,還是帶有CGO的項目,我們都可以透過適當的參數和工具,輕鬆實現跨平台編譯的目標。這種靈活性為我們的開發帶來了更多可能性,使得我們能夠更方便地在不同的環境中進行測試和部署。</span></p><p class="part in-view" data-endline="45" data-position="1384" data-size="0" data-startline="45" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="1384" data-size="5" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">參考資料:</span></p><p class="part in-view" data-endline="48" data-position="1391" data-size="0" data-startline="47" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="1391" data-size="27" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">[Go語言涉及CGO的交叉編譯(跨平台編譯)解決辦法]</span><br style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;" /><span data-position="1419" data-size="46" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">[FiloSottile/musl-cross - GitHub Repository] (</span><a href="https://github.com/FiloSottile/musl-cross" rel="noopener" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; text-decoration-line: none;" target="_blank">https://github.com/FiloSottile/musl-cross</a><span data-position="1506" data-size="1" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">)</span></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-8088663365430433812023-08-21T15:03:00.000+08:002023-08-21T15:03:03.223+08:00在 Docker 中使用 Koko 工具創建網絡連接的示例<h1 class="part" data-endline="1" data-id="在-Docker-中使用-Koko-工具創建網絡連接的示例" data-startline="1" id="在-Docker-中使用-Koko-工具創建網絡連接的示例" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-left: 0px; margin-right: 0px; margin-top: 0px !important; padding-bottom: 0.3em;"><span data-position="2" data-size="29" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在 Docker 中使用 Koko 工具創建網絡連接的示例</span></h1><p class="part in-view" data-endline="3" data-position="33" data-size="0" data-startline="3" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="33" data-size="121" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在 Docker 環境中,經常需要創建不同容器之間的網絡連接以及配置網絡參數。Koko 是一個工具,可以幫助我們在 Docker 容器中設置網絡連接。本文將介紹如何使用 Koko 工具在 Docker 容器中創建網絡連接,並顯示一些示例命令。</span></p><h2 class="part in-view" data-endline="5" data-id="1創建獨立的網絡連接" data-startline="5" id="1創建獨立的網絡連接" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/q1Lj4RKZRNyWY5b--iJJDw#1%E5%89%B5%E5%BB%BA%E7%8D%A8%E7%AB%8B%E7%9A%84%E7%B6%B2%E7%B5%A1%E9%80%A3%E6%8E%A5" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="1創建獨立的網絡連接"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="159" data-size="11" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">1.創建獨立的網絡連接</span></h2><p class="part in-view" data-endline="7" data-position="172" data-size="0" data-startline="7" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="172" data-size="38" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">首先,我們可以使用以下命令在兩個 Ubuntu 容器之間創建獨立的網絡連接:</span></p><pre class="part in-view" data-endline="12" data-position="212" data-startline="9" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">sudo docker run --network none -dt --name ubuntu1 ubuntu:bionic /bin/bash
sudo docker run --network none -dt --name ubuntu2 ubuntu:bionic /bin/bash
</code></pre><p class="part in-view" data-endline="13" data-position="368" data-size="0" data-startline="13" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="368" data-size="31" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">這將在兩個容器之間創建一個隔離的網絡環境,使它們能夠相互通信。</span></p><h2 class="part in-view" data-endline="15" data-id="2在容器中創建veth對" data-startline="15" id="2在容器中創建veth對" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/q1Lj4RKZRNyWY5b--iJJDw#2%E5%9C%A8%E5%AE%B9%E5%99%A8%E4%B8%AD%E5%89%B5%E5%BB%BAveth%E5%B0%8D" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="2在容器中創建veth對"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="404" data-size="13" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">2.在容器中創建veth對</span></h2><p class="part in-view" data-endline="17" data-position="419" data-size="0" data-startline="17" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="419" data-size="34" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">接下來,我們可以使用以下命令容器在主機和主機之間創建 veth 對:</span></p><pre class="part in-view" data-endline="21" data-position="454" data-startline="18" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">sudo ./koko -c net1 -d k8s_helloworld-python_helloworld-python_default_409cdca7-6c2a-45bf-a885-a5e6794f34c1_22,net1,10.200.0.2/24
sudo ./koko -c net2 -d k8s_helloworld-python-access-pod_helloworld-python-access-pod_default_36bf5089-8791-44dc-aa0c-40d1bd09a8c4_22,net1,10.200.0.3/24
</code></pre><p class="part in-view" data-endline="22" data-position="744" data-size="0" data-startline="22" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="744" data-size="52" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">接下來的命令將在容器和主機之間創建兩個 veth 對 net1 和 net2,並為它們分配 IP 地址。</span></p><h2 class="part in-view" data-endline="24" data-id="3創建橋接網絡" data-startline="24" id="3創建橋接網絡" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/q1Lj4RKZRNyWY5b--iJJDw#3%E5%89%B5%E5%BB%BA%E6%A9%8B%E6%8E%A5%E7%B6%B2%E7%B5%A1" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="3創建橋接網絡"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="801" data-size="8" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">3.創建橋接網絡</span></h2><p class="part in-view" data-endline="25" data-position="810" data-size="0" data-startline="25" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="810" data-size="33" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">我們可以通過以下步驟創建一個橋接網絡放置veth對連接到橋接接口:</span></p><pre class="part in-view" data-endline="31" data-position="844" data-startline="26" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">sudo ip l a br0 type bridge
sudo ip l s br0 up
sudo ip l s net1 master br0
sudo ip l s net2 master br0
</code></pre><p class="part in-view" data-endline="32" data-position="955" data-size="0" data-startline="32" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="955" data-size="44" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">這將創建一個名為 br0 的橋接接口,把 net1 和 net2 連接到這個橋接接口上。</span></p><h2 class="part in-view" data-endline="34" data-id="4-在命名空間中顯示IP地址" data-startline="34" id="4-在命名空間中顯示IP地址" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/q1Lj4RKZRNyWY5b--iJJDw#4-%E5%9C%A8%E5%91%BD%E5%90%8D%E7%A9%BA%E9%96%93%E4%B8%AD%E9%A1%AF%E7%A4%BAIP%E5%9C%B0%E5%9D%80" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="4-在命名空間中顯示IP地址"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="1004" data-size="15" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">4. 在命名空間中顯示IP地址</span></h2><p class="part in-view" data-endline="35" data-position="1020" data-size="0" data-startline="35" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="1020" data-size="23" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">要顯示容器中的IP地址,我們可以使用以下命令:</span></p><pre class="part in-view" data-endline="41" data-position="1044" data-startline="36" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">sudo docker exec -it k8s_micro-apm_micro-apm-qj76q_kube-system_5bd48be1-5530-4811-a546-1d4a688343a2_3 ip addr | grep -P "^\d|inet "
sudo docker exec -it k8s_helloworld-python_helloworld-python_default_409cdca7-6c2a-45bf-a885-a5e6794f34c1_22 ip addr | grep -P "^\d|inet "
sudo docker exec -it k8s_helloworld-python-access-pod_helloworld-python-access-pod_default_36bf5089-8791-44dc-aa0c-40d1bd09a8c4_22 ip addr | grep -P "^\d|inet "
sudo docker exec -it k8s_micro-apm_micro-apm-qj76q_kube-system_5bd48be1-5530-4811-a546-1d4a688343a2_3 ping -c5 10.200.0.2
</code></pre><p class="part in-view" data-endline="42" data-position="1606" data-size="0" data-startline="42" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="1606" data-size="44" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">這些命令將顯示容器中的 IP 地址,並且最後一條命令還會在容器之間執行 Ping 測試。</span></p><h2 class="part in-view" data-endline="44" data-id="5清理網絡連接" data-startline="44" id="5清理網絡連接" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/q1Lj4RKZRNyWY5b--iJJDw#5%E6%B8%85%E7%90%86%E7%B6%B2%E7%B5%A1%E9%80%A3%E6%8E%A5" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="5清理網絡連接"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="1655" data-size="8" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">5.清理網絡連接</span></h2><p class="part in-view" data-endline="45" data-position="1664" data-size="0" data-startline="45" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="1664" data-size="21" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">最後,我們可以使用以下命令來清理網絡連接:</span></p><pre class="part in-view" data-endline="51" data-position="1686" data-startline="46" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f7; border-radius: 3px; border: inherit !important; box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; letter-spacing: 0.35px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px; word-break: break-all;"><code style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background: transparent; border-radius: 3px; border: 0px; box-sizing: border-box; color: inherit !important; display: inline; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">sudo ./koko -D k8s_micro-apm_micro-apm-qj76q_kube-system_5bd48be1-5530-4811-a546-1d4a688343a2_3,net1
sudo ./koko -D k8s_helloworld-python_helloworld-python_default_409cdca7-6c2a-45bf-a885-a5e6794f34c1_22,net1
sudo ./koko -D k8s_helloworld-python-access-pod_helloworld-python-access-pod_default_36bf5089-8791-44dc-aa0c-40d1bd09a8c4_22,net1
sudo ip l d br0
</code></pre><p class="part in-view" data-endline="52" data-position="2049" data-size="0" data-startline="52" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin: 0px 0px 16px;"><span data-position="2049" data-size="20" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">在創建網絡連接和橋接接口之前將清理命令。</span></p><h2 class="part in-view" data-endline="54" data-id="結論" data-startline="54" id="結論" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; border-bottom: 1px solid rgb(238, 238, 238); box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; letter-spacing: 0.35px; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;"><a class="anchor hidden-xs" href="https://hackmd.io/q1Lj4RKZRNyWY5b--iJJDw#%E7%B5%90%E8%AB%96" smoothhashscroll="" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: transparent; box-sizing: border-box; color: #337ab7; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;" title="結論"><span class="octicon octicon-link" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; -webkit-font-smoothing: antialiased; box-sizing: border-box; color: black; display: inline-block; font-family: octicons; font-feature-settings: normal; font-kerning: auto; font-optical-sizing: auto; font-size: 16px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variation-settings: normal; font-weight: normal; line-height: 1; text-rendering: auto; user-select: none; vertical-align: middle; visibility: hidden;"></span></a><span data-position="2074" data-size="2" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">結論</span></h2><p class="part in-view" data-endline="55" data-position="2077" data-size="0" data-startline="55" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: white; box-sizing: border-box; color: #333333; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, "PingFang TC", "Microsoft JhengHei", 微軟正黑, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; letter-spacing: 0.35px; margin-bottom: 0px !important; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><span data-position="2077" data-size="96" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; box-sizing: border-box;">通過這些示例命令,我們可以在 Docker 容器中使用 Koko 工具來創建、管理和清理網絡連接,滿足不同的網絡配置需求。希望本文能夠幫助您更好地理解如何在 Docker 環境中進行網絡設置。</span></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-88791439101237117972022-06-30T15:35:00.001+08:002022-06-30T15:59:34.971+08:00[TensorFlow Lite] TensorFlow Lite 有用的工具<p><b style="font-family: arial; font-size: x-large;">TensorFlow Lite Tool: Toco (轉換器)</b></p><p><span style="font-family: arial; font-size: medium;">toco是用來生成一個可供TensorFlow Lite框架使用tflite文件。</span></p><p><span style="font-size: medium;"><span style="font-family: arial;">bazel 編譯方式<br /></span><span style="font-family: arial;"><span style="color: #2b00fe;">$ bazel build tensorflow/lite/toco:toco</span></span></span></p><p><span style="font-family: arial; font-size: medium;">程式位於tensorflow/lite/toco資料夾下。Toco有三個主要功能,即匯入、匯出和轉換。匯入將輸入轉為Model類別,匯出將模型轉為flite模型或是grpahviz。轉換以輸入標示為基礎對模型操作,並且它會刪除未使用的運算元等。</span></p><p><span></span></p><a name='more'></a><span style="font-family: arial; font-size: medium;"><br /></span><p></p><p><span style="font-family: arial; font-size: large;"><b>凍結模型: freeze_graph</b></span></p><p><span style="font-family: arial; font-size: medium;">freeze_graph是模型固化工具。把模型參數也一同寫進同一個模型檔案中。它的部分核心是convert_variables_to_constrants</span></p><p><span style="font-family: arial; font-size: medium;">summarize_graph 這工具在tensorflow/python/tools</span></p><p><span style="font-family: arial; font-size: medium;">bazel 編譯方式<br /><span style="background-color: white;"><span style="color: #2b00fe;">$ </span></span></span><span style="color: #2b00fe; font-family: arial; font-size: medium;">bazel build tensorflow/python/tools:freeze_graph</span></p><p><span style="font-family: arial; font-size: large;"><b><br /></b></span></p><p><span style="font-family: arial; font-size: large;"><b>影像標記 (label_image)</b></span></p><p><span style="font-family: arial; font-size: medium;">這工具在 tensorflow/lite/examples/label_image類,它是一個月C++寫的影像分類工具。它會讀取一個影像和模型檔案,按照標記的噹案進行分類</span></p><p><span style="font-family: arial; font-size: medium;">首先,下載影像逼較要用到的幾個檔案:</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><div class="separator" style="clear: both;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ ./tensorflow/lite/examples/ios/download_models.sh<br /></span></span></pre></h3></div><p>然後,建置執行檔案。Android裝置的ABI可以透過以下指令過得:</p><div class="separator" style="clear: both;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">adb shell getprop | grep abi</span></span></pre></h3></div><p>假設你的裝置是armv864位元晶片,可以執行下列指令:</p><div class="separator" style="clear: both;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ baxel build --config android_arm64 --config monolithic --cxxopt=-std=c++11 \
//tensorflow/lite/examples/label_image:label_image</span></span></pre></h3></div><p>若是在x86的機器上,可以執行下列指令:</p><div class="separator" style="clear: both;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ baxel build --config monolithic \
//tensorflow/lite/examples/label_image:label_image</span></span></pre></h3></div><p><br /></p><p><span style="font-family: arial; font-size: large;"><b>最小整合 (Minimal)</b></span></p><p><span style="font-family: arial; font-size: medium;">這工具在</span><span style="font-family: arial; font-size: large;">tensorflow/lite/examples/minimal,它示範了怎像讀取模型、建置解譯器,以及執行預測。</span></p><p><span style="font-family: arial; font-size: large;"><br /></span></p><p><span style="font-family: arial; font-size: large;"><b>Graphviz (dot format)</b></span></p><p><span style="font-family: arial; font-size: medium;">TensorFlow提供了很多視覺化工具,例如: 透過toco</span><span style="font-family: arial; font-size: large;">將</span><span style="font-family: arial; font-size: large;">TensorFlow張量流圖轉為Graphviz:</span></p><div class="separator" style="clear: both;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ toco --input_file=tf_files/optimized_graph.lite \
--output_file=tf_files/lite.dot \
--input_format=TFLITE \
--output_format=GRAPHVIZ_DOT \
--input_shape=1,224,224,3 \
--input_array=input \
--output_array=final_result \
--inference_type=FLOAT \
--input_data_type=FLOAT</span></span></pre></h3></div><p><span style="font-family: arial; font-size: large;">產生出來的.dot file 可以透過支援Graphviz視覺化工具(例如: Dot Viewer) 來看模型圖</span></p><p><span style="font-family: arial; font-size: large;"><br /></span></p><p><span style="font-family: arial; font-size: large;"><b>模型評校 (<span style="background-color: white; color: #4d4d4d;">summarize_graph & benchmark_model)</span></b></span></p><p><span style="font-family: arial; font-size: medium;">summarize_graph 這工具在tensorflow/<span style="background-color: white; color: #4d4d4d;">tools/graph_transforms</span>,它示範了怎像讀取模型、建置解譯器,以及執行預測。</span></p><p><span style="font-family: arial; font-size: medium;">bazel 編譯方式<br /><span style="background-color: white;"><span style="color: #2b00fe;">$ bazel build tensorflow/tools/graph_transforms:summarize_graph</span></span></span></p><p><span face="-apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif, SimHei, SimSun" style="color: #4d4d4d; font-size: medium;"><span style="background-color: white;">例如: 執行下面的指令,產生模型概要</span></span></p><div class="separator" style="clear: both;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ summarize_graph --in_graph=tf_files/retrained_graph.pb</span></span></pre></h3></div><p><span style="font-family: arial; font-size: medium;"><br />benchmark_model這工具也是在tensorflow/<span style="background-color: white; color: #4d4d4d;">tools/graph_transforms</span></span></p><p><span style="font-family: arial; font-size: medium;">bazel 編譯方式<br /><span style="background-color: white;"><span style="color: #2b00fe;">$ bazel build tensorflow/tools/graph_transforms:benchmark_model</span></span></span></p><p style="text-align: left;"><span face="-apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif, SimHei, SimSun" style="color: #4d4d4d; font-size: medium;"><span style="background-color: white;">例如: 執行下面的指令,產生模型概要</span></span></p><div class="separator" style="clear: both;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ benchmark_model --graph=tf_files/retrained_graph.pb \
--show_flops \
--input_layer=Placeholder \
--input_layer_type=float \
--input_layer_shape=1,299,299,3 \
--output_layer=final_result</span></span></pre></h3></div><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-12978449413446317872022-06-18T15:59:00.031+08:002022-06-30T12:57:06.331+08:00[Raspberry Pi] Install TensorFlow Lite on Raspberry Pi 4<p><span style="font-size: large;"><b style="font-family: arial;">Build TensorFlow Lite Static Library </b><b><span style="font-family: arial;">on Raspberry Pi 4</span></b></span></p><p><span style="font-family: arial; font-size: medium;">I found a very useful resource about installing TensorFlow Lite 2.* on Raspberry Pi 4 as follows: <a href="https://qengineering.eu/install-tensorflow-2-lite-on-raspberry-pi-4.html">https://qengineering.eu/install-tensorflow-2-lite-on-raspberry-pi-4.html</a></span></p><p><span style="font-family: arial; font-size: large;"><tb_cm style="color: #4a86e8;"></tb_cm></span></p><p><span style="font-size: medium;"><span style="font-family: arial;">It's about to build TensorFlow Lite static library in version </span><span style="font-family: arial;">2.4</span><span style="font-family: arial;"> </span><span style="font-family: arial;">:</span></span></p><p><span style="font-family: arial; font-size: medium;"><tb_cm style="color: #4a86e8;"># the tools needed<span></span></tb_cm></span></p><a name='more'></a><span style="font-family: arial; font-size: medium;"><br /><span style="background-color: #ffffdf;"></span><tb_cd>$ sudo apt-get install cmake curl<br /></tb_cd><span style="background-color: #ffffdf;"></span><tb_cm style="color: #4a86e8;"># download the latest TensorFlow version (2.4.0)<br /></tb_cm><span style="background-color: #ffffdf;"></span><tb_cd>$ wget -O tensorflow.zip https://github.com/tensorflow/tensorflow/archive/v2.4.0.zip<br /></tb_cd><span style="background-color: #ffffdf;"></span><tb_cm style="color: #4a86e8;"># unpack and give the folder a convenient name<br /></tb_cm><span style="background-color: #ffffdf;"></span><tb_cd>$ unzip tensorflow.zip<br /></tb_cd><span style="background-color: #ffffdf;"></span><tb_cd>$ mv tensorflow-2.4.0 tensorflow<br /></tb_cd><span style="background-color: #ffffdf;"></span><tb_cd>$ cd tensorflow<br /></tb_cd><span style="background-color: #ffffdf;"></span><tb_cm style="color: #4a86e8;"># get the dependencies<br /></tb_cm><span style="background-color: #ffffdf;"></span><tb_cd>$ ./tensorflow/lite/tools/make/download_dependencies.sh<br /></tb_cd><span style="background-color: #ffffdf;"></span><tb_cm style="color: #4a86e8;"># run the C++ installation (± 25 min)<br /></tb_cm><span style="background-color: #ffffdf;"></span><tb_cd>$ ./tensorflow/lite/tools/make/build_rpi_lib.sh</tb_cd></span><p></p><p><span style="font-size: medium;"><span style="font-family: arial;">pi@raspberrypi:~/Downloads/tensorflow-2.4.0/tensorflow/lite/tools/make/gen$ tree -L 3<br /></span><span style="font-family: arial;">.<br /></span><span style="font-family: arial;">└── rpi_armv7l<br /></span><span style="font-family: arial;"> ├── bin<br /></span><span style="font-family: arial;"> │ ├── benchmark_model<br /></span><span style="font-family: arial;"> │ ├── benchmark_model_performance_options<br /></span><span style="font-family: arial;"> │ └── minimal<br /></span><span style="font-family: arial;"> ├── lib<br /></span><span style="font-family: arial;"> │ ├── benchmark-lib.a<br /></span><span style="font-family: arial;"> │ └── libtensorflow-lite.a<br /></span><span style="font-family: arial;"> └── obj<br /></span><span style="font-family: arial;"> └── tensorflow</span></span></p><div><span style="font-family: arial; font-size: medium;"><br /></span></div><div><span style="font-family: arial; font-size: medium;"><br /></span></div><div><span style="font-family: arial; font-size: medium;"><b><span>Or, if you want to use cmake to do so, please follow</span><a href="https://www.tensorflow.org/lite/guide/build_cmake#build_tensorflow_lite_c_library"> the steps</a><span>:</span></b></span></div><p><span style="color: #3d85c6; font-family: arial; font-size: medium;">Step 1. Install CMake tool</span></p><p><span style="font-family: arial; font-size: medium;">$ sudo apt-get install cmake</span></p><p><span style="color: #3d85c6; font-family: arial; font-size: medium;">Step 2. Clone TensorFlow repository</span></p><p><span style="font-family: arial; font-size: medium;">$ git clone https://github.com/tensorflow/tensorflow.git tensorflow_src</span></p><p><span style="font-family: arial; font-size: medium;">Note: If you're using the TensorFlow Docker image, the repo is already provided in /tensorflow_src/.</span></p><p><span style="color: #3d85c6; font-family: arial; font-size: medium;">Step 3. Create CMake build directory</span></p><p><span style="font-family: arial; font-size: medium;">$ mkdir tflite_build</span></p><p><span style="font-family: arial; font-size: medium;">$ cd tflite_build</span></p><p><span style="color: #3d85c6; font-family: arial; font-size: medium;">After that, run the following commands.</span></p><p><span style="font-family: arial; font-size: medium;">$ cmake -DTFLITE_ENABLE_XNNPACK=OFF ../tensorflow_src/tensorflow/lite</span></p><p><span style="font-family: arial; font-size: medium;">$ cmake --build . -j4</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><b><span style="font-family: arial; font-size: large;">Build TensorFlow Lite Shared Library on Raspberry Pi 4 using Bazel-on-ARM project</span></b></p><p><span style="font-family: arial; font-size: medium;">Bazel is an open-source build tool from Google, used to build projects such as TensorFlow. Raspberry Pi OS (Raspbian) doesn't have a package for Bazel, and the Bazel project doesn't provide a binary for armhf.</span></p><p><span style="font-family: arial; font-size: medium;">Please install bazel first based on <a href="https://github.com/koenvervloesem/bazel-on-arm"><b>Bazel on ARM</b></a> on Raspberry Pi</span></p><p><span style="font-family: arial; font-size: medium;">$ git clone https://github.com/koenvervloesem/bazel-on-arm<br />$ cd bazel-on-arm<br />$ sudo make requirements<br />$ make bazel<br /><span style="color: #3d85c6;">#or If you want to build a specific Bazel version, run the build script with the version number as an argument:</span><br />$ env EXTRA_BAZEL_ARGS="--host_javabase=@local_jdk//:jdk" bash ./scripts/build_bazel.sh 3.7.2<br /><span style="color: #3d85c6;">#You can install it to /usr/local/bin with:</span><br />sudo make install</span></p><p><span style="font-family: arial;"><span style="font-size: medium;"><i><b><span style="color: #999999;">There are pre-build bazel binary files in Release: https://github.com/koenvervloesem/bazel-on-arm/releases</span></b><br /></i><b style="color: #3d85c6;"><br /></b></span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span><b style="color: #3d85c6;">Build TensorFlow Lite (</b><b><span style="color: red;"> is not successful </span></b><b style="color: #3d85c6;">)</b></span><br /></span><span style="font-family: arial;">$ bazel build -c opt //tensorflow/lite/c:libtensorflowlite_c.so<br /></span></span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: large;"><b>Prebuilt binary for TensorflowLite</b></span></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: arial; font-size: large;"><a href="https://github.com/PINTO0309/TensorflowLite-bin"><b>TensorflowLite-bin</b></a> <b><span style="color: #2b00fe;"><i>(Important)</i></span></b></span></p><p><span style="font-family: arial; font-size: medium;">Prebuilt binary for TensorflowLite's standalone installer. For RaspberryPi. I provide a FlexDelegate, MediaPipe Custom OP, XNNPACK and XNNPACK Multi-Thread PythonAPI enabled binary. </span></p><p><b><span style="color: red; font-family: arial; font-size: medium;">If you want the best performance with RaspberryPi4/3, install Ubuntu 18.04+ aarch64 (64bit) instead of Raspbian armv7l (32bit). The official Tensorflow Lite is performance tuned for aarch64. On aarch64 OS, performance is about 4 times higher than on armv7l OS</span></b></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span face="-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-weight: 600;">Python3.7 - Buster</span></span></span></p></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;">$ wget https://github.com/PINTO0309/TensorflowLite-bin/releases/download/v2.8.0/tflite_runtime-2.8.0-cp37-none-linux_armv7l.whl</span></span></p></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;">$ sudo pip3 install tflite_runtime-2.8.0-cp37-none-linux_armv7l.whl</span></span></p></blockquote><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><b><span style="font-family: arial; font-size: large;">The successful way to build and install </span><span style="font-family: arial; font-size: large;">TensorFlow Lite on Raspberry Pi 4</span><span style="font-family: arial; font-size: x-large;">:</span></b></p><h1 class="devsite-page-title" style="box-sizing: inherit; display: inline; margin-inline-end: 52px; margin-top: 0px; overflow: hidden; text-align: left; text-overflow: ellipsis; vertical-align: middle;"><p><span style="font-size: medium;"><span style="font-family: arial;"><span color="rgba(0, 0, 0, 0)" style="letter-spacing: var(--devsite-h1-letter-spacing);">Use Bazelisk to install Bazel <br /></span><span><a href="https://bazel.build/install/bazelisk" style="font-weight: normal;">https://bazel.build/install/bazelisk</a><br /><br /></span></span><span style="background-color: #f7f7f7; font-family: arial;"><span style="color: #3d85c6;">#First, we need to install bazelisk, and make sure you get the correct CPU arch and 64/32 bit for node.js <br /></span></span><span style="font-family: arial;"><span style="color: #333333;"><span style="background-color: #f7f7f7;">$ sudo npm install -g @bazel/bazelisk<br /></span></span></span></span><b><span style="font-family: arial; font-size: medium;"><span style="color: #3d85c6;">#We can directly build bazel from source: ( </span><span style="color: red;">it works</span></span><span style="color: #3d85c6;"><span style="font-family: arial; font-size: medium;"> )</span><br /></span></b></p><p style="text-align: left;"><span style="font-size: medium;"><span style="font-weight: normal;"><span><span style="font-family: arial;">$ cd tensorflow<br /></span></span></span><span style="font-weight: normal;"><span><span style="font-family: arial;">$ ./configure<br /></span></span></span><span style="font-weight: normal;"><span><span style="font-family: arial;">$ </span></span><span style="font-family: arial;">bazel build -c opt //tensorflow/lite/c:libtensorflowlite_c.so<br />$ sudo cp -rf bazel-bin/<br /></span></span><span style="font-weight: normal;"><span style="font-family: arial;">$ sudo cp -rf bazel-bin/</span><span style="font-family: arial;">tensorflow/lite/c/* /usr/local/lib<br />$ sudo mkdir -p /usr/local/include/tensorflow/lite/c<br />$ <br /></span></span><span style="font-weight: normal;"><span style="font-family: arial;">$ sudo mkdir -p /usr/local/include/tensorflow/lite/c<br /></span></span><span style="font-weight: normal;"><span style="font-family: arial;">$ </span><span style="font-family: arial;">sudo cp -rf </span><span style="font-family: arial;">tensorflow/lite/c/* </span><span style="font-family: arial;">/usr/local/include/tensorflow/lite/c<br /></span></span><span style="font-family: arial;"><span style="font-weight: normal;">$ sudo ldconfig</span></span></span></p><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;"><span style="font-weight: normal;"><br /></span></span></span></p><p></p><p><b style="font-family: arial;"><span style="font-size: large;">Further Reading:<br /></span></b><b style="font-family: arial; font-size: large;">Building Bazel and TensorFlow 2.x on aarch64 ( ARM Board )<br /></b><a href="https://community.arm.com/arm-community-blogs/b/ai-and-ml-blog/posts/building-bazel-and-tensorflow-2-x-on-aarch64" style="font-family: arial; font-size: large;">https://community.arm.com/arm-community-blogs/b/ai-and-ml-blog/posts/building-bazel-and-tensorflow-2-x-on-aarch64</a></p></h1><p><span style="font-size: medium;"><span style="font-family: arial;"><br /></span></span></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-38786738330428986412022-06-08T16:52:00.020+08:002022-06-17T15:52:41.730+08:00[Raspberry Pi] Install TensorFlow 2.2 and OpenCV 4.4.0 on Raspberry Pi 4 and use Neural Compute Stick 2<p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br />So far as we have known that Raspberry Pi 4, which is based on ARMv7 Processor rev 3 (v7l), has much more CPU computing power than previous generations. Neural Compute Stick 2(NCS 2) is a Plug and Play Development Kit for AI Inferencing via USB. Fortunately, I have both of them. Because of that, I was just wondering how to use them at the same time. The first idea coming to my mind is about AI inferencing. If we can have Tensorflow installed on it and do inferencing using NCS 2, that will be great. </span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: arial; font-size: medium;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhONWUvll3GT4L3tDhw1iKPwrYsKfiNR7pog5C5kp6uLmcxk8RDFZ69LvbbQkCmWbNEYgBL5LUtJbwXgZACyBgpBJW0oLUa35GrpbQ4yTP0PuRxQQqQjdUVJ6kxy_38t3_HE2ZJFqm9rbdBDzcw9fSI1vqV5zfiuQ1kjHUqr4Qg7M1Vh59fIlnLKoU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="674" data-original-width="1058" height="408" src="https://blogger.googleusercontent.com/img/a/AVvXsEhONWUvll3GT4L3tDhw1iKPwrYsKfiNR7pog5C5kp6uLmcxk8RDFZ69LvbbQkCmWbNEYgBL5LUtJbwXgZACyBgpBJW0oLUa35GrpbQ4yTP0PuRxQQqQjdUVJ6kxy_38t3_HE2ZJFqm9rbdBDzcw9fSI1vqV5zfiuQ1kjHUqr4Qg7M1Vh59fIlnLKoU=w640-h408" width="640" /></a></span></div><span style="font-family: arial; font-size: medium;"><br /><br /></span><span><a name='more'></a></span><p></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">But, during the period of time trying, I found that it is not easy to have Tensorflow on Raspberry Pi 4. After several hours of experiments, I summarized the following instructions to do so. That is also my memo which helps me to keep the records.</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br /></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: large;"><b>Install Tensorflow on Raspberry Pi 4</b></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">I found this post is very good resource for installing Tensorflow on Raspberry Pi 4. Here you go:<br /><a href="https://qengineering.eu/install-tensorflow-2.2.0-on-raspberry-pi-4.html"><span>https://qengineering.eu/install-tensorflow-2.2.0-on-raspberry-pi-4.html</span></a></span></p><div style="background: rgb(255, 255, 255); border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; text-align: left; vertical-align: baseline;"><p style="border: 0px; box-sizing: inherit; font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; margin-top: 0px; max-height: 600px; overflow-wrap: normal; overflow: auto; text-align: left; vertical-align: baseline; width: auto;"><span style="font-size: medium;"><span class="fs11lh1-5 cf1 ff1" style="background: transparent; border: 0px; color: #4a86e8; font-family: arial; line-height: 22px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"># get a fresh start<br /></span><span style="background-color: transparent; font-family: arial;">$ sudo apt-get update<br /></span><span style="background-color: transparent; font-family: arial;">$ sudo apt-get upgrade</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span class="fs11lh1-5 cf2 ff1" style="background: transparent; border: 0px; color: #3c78d8; line-height: 22px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"># install the dependencies (if not already onboard)<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo apt-get install gfortran<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo apt-get install libhdf5-dev libc-ares-dev libeigen3-dev<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo apt-get install libatlas-base-dev libopenblas-dev libblas-dev<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo apt-get install</span><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">openmpi-bin libopenmpi-dev<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo apt-get install liblapack-dev cython<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo pip3 install keras_applications==1.0.8 --no-deps<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo pip3 install keras_preprocessing==1.1.0 --no-deps<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo pip3 install -U --user six wheel mock<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo -H pip3 install pybind11<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo -H pip3 install h5py</span><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">==2.10.0<br /></span></span><span style="font-family: arial;"><span class="fs11lh1-5 cf2 ff1" style="background: transparent; border: 0px; color: #3c78d8; line-height: 22px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"># upgrade setuptools 40.8.0 -> 52.0.0<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo -H pip3 install --upgrade setuptools<br /></span></span><span style="font-family: arial;"><span class="fs11lh1-5 cf2 ff1" style="background: transparent; border: 0px; color: #3c78d8; line-height: 22px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"># install gdown to download from Google drive<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ </span><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">sudo -H </span><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">pip install gdown<br /></span></span><span style="font-family: arial;"><span class="fs11lh1-5 cf2 ff1" style="background: transparent; border: 0px; color: #3c78d8; line-height: 22px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"># download the wheel<br /></span></span><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ gdown https://drive.google.com/uc?id=11mujzVaFqa7R1_lB7q0kVPW22Ol51MPg<br /></span></span><span style="font-family: arial;"><span class="fs11lh1-5 cf2 ff1" style="background: transparent; border: 0px; color: #3c78d8; line-height: 22px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;"># install TensorFlow<br /></span></span></span></p><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;"><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">$ sudo -H pip3 install tensorflow-2.2.0-cp37-cp37m-linux_armv7l.whl </span><span class="fs12lh1-5 ff1" style="background: transparent; border: 0px; line-height: 24px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">wrapt --upgrade --ignore-installed<br />$ sudo apt install libopencv-dev<br /><span style="color: #3d85c6;">#$ sudo pip3 install opencv-contrib-python</span><br />$ sudo pip3 install numpy==1.19.1<br /><br /></span></span></span><span style="font-size: medium;"><span><span style="color: #4a86e8; font-family: arial;"># install matplotlib and related<br /></span></span><span><span style="font-family: arial;"><code class="hljs language-python" style="border: 0px; box-sizing: inherit; font-size: var(--fs-body1); font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; line-height: inherit; margin: 0px; padding: 0px; vertical-align: baseline;"><span style="font-family: arial;"><span><span style="font-style: inherit; font-variant-caps: inherit; font-variant-ligatures: inherit; font-weight: inherit; white-space: inherit;">$ sudo pip3 install python3-matplotlib</span><br /></span></span></code></span></span><span style="color: #4a86e8; font-family: arial;"># Or if pip3 for matplotlib doesn't work<br /></span><span style="font-family: arial;">$ sudo apt install python3-matplotlib<br /></span><span style="background-color: transparent; font-family: arial;"><span style="color: #4a86e8;"># and complete the installation by rebooting<br /></span></span><span style="background-color: transparent; font-family: arial;">$ sudo reboot <br /></span><span style="background-color: transparent; font-family: arial;">$ cd Downloads/</span></span></p><p></p><p><span style="background-color: transparent;"><span style="font-family: arial; font-size: medium;"><br /></span></span></p><p><span style="font-family: arial; font-size: large;"><b>Install OpenVINO™ toolkit for Raspbian* OS</b></span></p><p><span style="background-color: transparent;"><span style="font-family: arial; font-size: medium;">Once we have installed Tensorflow ready, then we can try to use NCS 2 via OpenVINO. I just follow the offical web site to do so:<br /><a href="https://docs.openvino.ai/2020.4/openvino_docs_install_guides_installing_openvino_raspbian.html#install-package">https://docs.openvino.ai/2020.4/openvino_docs_install_guides_installing_openvino_raspbian.html#install-package</a></span></span></p><p><span style="font-family: arial; font-size: medium;">$ sudo mkdir -p /opt/intel/openvino<br /></span><span style="font-size: medium;"><span style="font-family: arial;">$ wget https://download.01.org/opencv/2019/openvinotoolkit/l_openvino_toolkit_raspbi_p_2019.1.094.tgz<br /></span><span style="font-family: arial;">$ sudo tar -xf l_openvino_toolkit_raspbi_p_2019.1.094.tgz.tgz --strip 1 -C /opt/intel/openvino<br /></span><span style="font-family: arial;">$ sudo apt install cmake<br /></span><span style="font-family: arial;">$ source /opt/intel/openvino/bin/setupvars.sh</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Add USB Rules</span><br /></span><span style="font-family: arial;">$ sudo usermod -a -G users "$(whoami)"<br /></span><span style="font-family: arial;">$ source /opt/intel/openvino/bin/setupvars.sh<br /></span><span style="font-family: arial;">$ sh /opt/intel/openvino/install_dependencies/install_NCS_udev_rules.sh</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Plug in your Intel® Neural Compute Stick 2<br /></span></span><span style="font-family: arial;"><span style="color: #3d85c6;">#Build and Run Object Detection Sample</span><br /></span><span style="font-family: arial;">$ mkdir build && cd build<br /></span><span style="font-family: arial;">$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=armv7-a" /opt/intel/openvino/deployment_tools/inference_engine/samples/cpp/<br /></span><span style="font-family: arial;">$ make -j2 object_detection_sample_ssd</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Download the pre-trained Face Detection model</span><br /></span><span style="font-family: arial;">$ wget --no-check-certificate https://download.01.org/opencv/2020/openvinotoolkit/2020.1/open_model_zoo/models_bin/1/face-detection-adas-0001/FP16/face-detection-adas-0001.bin<br /></span><span style="font-family: arial;">$ wget --no-check-certificate https://download.01.org/opencv/2020/openvinotoolkit/2020.1/open_model_zoo/models_bin/1/face-detection-adas-0001/FP16/face-detection-adas-0001.xml<br /></span><span style="font-family: arial;"><br /><span style="color: #3d85c6;">#Test OpenVINO example using NCS2</span><br />$ sudo apt install eog<br /></span><span style="font-family: arial;">$ ./armv7l/Release/object_detection_sample_ssd -m face-detection-adas-0001.xml -d MYRIAD -i faces.png<br /></span><span style="font-family: arial;">$ eog out_0.bmp</span></span></p><p><span style="background-color: transparent; font-family: arial; font-size: large;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjG5mfWR82YBFeQQlaSIuuL9kPk6lYUKUrQ0cqqxPUb8NXCZhBuYEGEC-wjaGT7kxji9FGHUuGheci0_ib84OmnvuOnBpQN5dMgAhbaW0u8QD0ARcHPiuGQWKIFwYtOk4M5S6AROKNOul-MvaxZEspqJF1eea99RY_Td6iPYqsfVGcdiWu7v6SCxKs" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="570" data-original-width="840" height="434" src="https://blogger.googleusercontent.com/img/a/AVvXsEjG5mfWR82YBFeQQlaSIuuL9kPk6lYUKUrQ0cqqxPUb8NXCZhBuYEGEC-wjaGT7kxji9FGHUuGheci0_ib84OmnvuOnBpQN5dMgAhbaW0u8QD0ARcHPiuGQWKIFwYtOk4M5S6AROKNOul-MvaxZEspqJF1eea99RY_Td6iPYqsfVGcdiWu7v6SCxKs=w640-h434" width="640" /></a></div></div><p style="text-align: left;"><span style="font-size: medium;"><span style="background-color: white; font-family: arial;">$ sudo apt install eog<br /></span><span style="background-color: white; font-family: arial;">$ ./armv7l/Release/object_detection_sample_ssd -m face-detection-adas-0001.xml -d MYRIAD -i faces.png<br /></span><span style="background-color: white; font-family: arial;">$ eog out_0.bmp<br /><br /></span></span></p><p><span style="font-size: medium;"><span style="color: #3d85c6; font-family: arial;">#Intel's Deep Learning Inference Engine (DL IE) is a part of Intel® OpenVINO™ toolkit. </span><span style="color: #3d85c6; font-family: arial;">You can use it as a computational backend for OpenCV deep learning module.<br /></span><span style="font-family: arial;"><span style="color: #3d85c6;"><span style="background-color: white;"># Install OpenvCV 4.4.0<br /></span></span></span><span style="font-family: arial;">$ sudo apt install build-essential cmake git pkg-config libgtk-3-dev \</span><span style="font-family: arial;">libavcodec-dev libavformat-dev libswscale-dev libv4l-dev \<br /></span><span style="font-family: arial;">libxvidcore-dev libx264-dev libjpeg-dev libpng-dev libtiff-dev \</span><span style="font-family: arial;">gfortran openexr libatlas-base-dev python3-dev python3-numpy \<br /></span><span style="font-family: arial;">libtbb2 libtbb-dev libdc1394-22-dev libopenexr-dev \<br /></span><span style="font-family: arial;">libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev</span></span></p><p><span style="font-family: arial; font-size: medium;"><span>$ sudo dpkg --add-architecture armhf && \<br /></span><span>apt-get update && \<br /></span><span>apt-get install -y --no-install-recommends \<br /></span><span>crossbuild-essential-armhf \<br /></span><span>xz-utils \<br /></span><span>libgtk2.0-dev:armhf \<br /></span><span>libpython-dev:armhf \<br /></span><span>libpython3-dev:armhf \<br /></span><span>python-numpy \<br /></span><span>python3-numpy \<br /></span><span>libgstreamer1.0-dev:armhf \<br /></span><span>libgstreamer-plugins-base1.0-dev:armhf<br /></span><span><br /><span style="color: #3d85c6;">#Clone the OpenCV’s and OpenCV contrib repositories:</span></span><span style="white-space: pre;"> </span><br /><span>$ mkdir ~/opencv_build && cd ~/opencv_build<br /></span><span>$ git clone https://github.com/opencv/opencv.git<br /></span><span>$ git clone https://github.com/opencv/opencv_contrib.git</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Once the download is complete, create a temporary build directory, and navigate to it:</span><br /></span><span style="font-family: arial;">$ cd ~/opencv_build/opencv<br /></span><span style="font-family: arial;">$ mkdir -p build && cd build</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Setup environment variables to detect Inference Engine:</span><br /></span><span style="font-family: arial;">$ source /opt/intel/openvino/bin/setupvars.sh<br /></span><span style="font-family: arial;">$ export ngraph_DIR=/opt/intel/openvino/deployment_tools/ngraph/cmake/</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Set up the OpenCV build with CMake:</span><br /></span><span style="font-family: arial;">$ cmake -D CMAKE_BUILD_TYPE=RELEASE \<br /></span><span style="font-family: arial;">-D WITH_IPP=OFF \<br /></span><span style="font-family: arial;">-D BUILD_TESTS=OFF \<br /></span><span style="font-family: arial;">-D BUILD_PERF_TESTS=OFF \<br /></span><span style="font-family: arial;">-D OPENCV_ENABLE_PKG_CONFIG=ON \<br /></span><span style="font-family: arial;">-D CMAKE_INSTALL_PREFIX=/usr/local \<br /></span><span style="font-family: arial;">-D INSTALL_C_EXAMPLES=ON \<br /></span><span style="font-family: arial;">-D CMAKE_INSTALL_PREFIX=$(python3 -c "import sys; print(sys.prefix)") \<br /></span><span style="font-family: arial;">-D INSTALL_PYTHON_EXAMPLES=ON \<br /></span><span style="font-family: arial;">-D OPENCV_GENERATE_PKGCONFIG=ON \<br /></span><span style="font-family: arial;">-D ENABLE_NEON=ON \<br /></span><span style="font-family: arial;">-D CPU_BASELINE="NEON" \<br /></span><span style="font-family: arial;">-D WITH_INF_ENGINE=ON \<br /></span><span style="font-family: arial;">-D OPENCV_EXTRA_MODULES_PATH=~/opencv_build/opencv_contrib/modules \<br /></span><span style="font-family: arial;">-D OPENCV_PYTHON3_INSTALL_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") \<br /></span><span style="font-family: arial;">-D PYTHON_EXECUTABLE=$(which python3) \<br /></span><span style="font-family: arial;">-D BUILD_EXAMPLES=ON ..</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Start the compilation process and install it</span><br /></span><span style="font-family: arial;">$ make -j4 && sudo make install</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Fixed the error of import cv2 on Python3</span><br /></span><span style="font-family: arial;">$ sudo pip3 install numpy==1.19.1<br /></span><span style="font-family: arial;">$ cd lib/python3/<br /></span><span style="font-family: arial;">$ sudo cp cv2.cpython-37m-arm-linux-gnueabihf.so /usr/lib/python3/dist-packages/</span></span></p><p><span style="font-size: medium;"><span style="color: #3d85c6; font-family: arial;">#To verify the installation, type the following commands and you should see the OpenCV version.<br /></span><span style="font-family: arial;"><span style="color: #3d85c6;">#C++ bindings:</span><br /></span><span style="font-family: arial;">$ pkg-config --modversion opencv4</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="color: #3d85c6;">#Python bindings:</span><br /></span><span style="font-family: arial;">$ python3 -c "import cv2; print(cv2.__version__)"</span></span></p><p><span style="font-family: arial; font-size: large;"><br /></span></p><p><span style="font-family: arial; font-size: large;"><b>Test all of them together</b></span></p><p><span style="font-family: arial;"><span style="font-size: medium;">Building a frontend for a Raspberry Pi Machine Learning Server with Intel Movidius NCS</span><br /></span><span style="font-family: arial; font-size: medium;"><a href="https://towardsdatascience.com/building-a-frontend-for-a-raspberry-pi-machine-learning-server-with-intel-movidius-ncs-cb940d75103">https://towardsdatascience.com/building-a-frontend-for-a-raspberry-pi-machine-learning-server-with-intel-movidius-ncs-cb940d75103</a></span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEidRuSldufMoGVffQj3dNoNQ-bF2ay72hiuPv3h8uPza7Rx2f3pESzNt9dwd0p99qbDI_zi6KHov8IWEznzf3Ad_RmehElHMLd2fTP-AfJpp6ysUBRErb5SlX-HQwI1WZkGFqwZQZcoAbDpMPTwXkpgFDOw3vAjGeR5xV7E1HUKVBGQ8Y6J38QChFs" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="666" data-original-width="1760" height="242" src="https://blogger.googleusercontent.com/img/a/AVvXsEidRuSldufMoGVffQj3dNoNQ-bF2ay72hiuPv3h8uPza7Rx2f3pESzNt9dwd0p99qbDI_zi6KHov8IWEznzf3Ad_RmehElHMLd2fTP-AfJpp6ysUBRErb5SlX-HQwI1WZkGFqwZQZcoAbDpMPTwXkpgFDOw3vAjGeR5xV7E1HUKVBGQ8Y6J38QChFs=w640-h242" width="640" /></a></div><br /><br /><p></p><p><b style="font-family: arial; font-size: x-large;">Reference</b></p><p><span style="font-family: arial; font-size: medium;"><a href="https://bbs.huaweicloud.com/forum/thread-82599-1-1.html">https://bbs.huaweicloud.com/forum/thread-82599-1-1.html</a><br /></span><a href="https://linuxize.com/post/how-to-install-opencv-on-ubuntu-20-04/" style="font-family: arial; font-size: large;">https://linuxize.com/post/how-to-install-opencv-on-ubuntu-20-04/</a><br /><a href="https://github.com/opencv/opencv/wiki/Intel's-Deep-Learning-Inference-Engine-backend#linux" style="font-family: arial; font-size: large;">https://github.com/opencv/opencv/wiki/Intel's-Deep-Learning-Inference-Engine-backend#linux</a></p><p style="text-align: left;"><span style="background-color: white; font-family: arial; font-size: large;"><br /></span></p><p style="text-align: left;"><span style="background-color: white; font-family: arial; font-size: large;"><br /></span></p><p style="text-align: left;"><span style="background-color: white; font-family: arial; font-size: large;"><br /></span></p><p style="text-align: left;"><span style="background-color: white; font-family: arial; font-size: large;"><br /></span></p><p style="text-align: left;"><span style="background-color: white; font-family: arial; font-size: large;"><br /></span></p><p style="text-align: left;"><span style="background-color: white; font-family: arial; font-size: large;"><br /></span></p><pre class="lang-py s-code-block" style="border-radius: var(--br-md); border: 0px; box-sizing: inherit; color: var(--highlight-color); font-family: var(--ff-mono); font-size: var(--fs-body1); font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; line-height: var(--lh-md); margin-bottom: calc(var(--s-prose-spacing) + 0.4em); margin-top: 0px; max-height: 600px; overflow-wrap: normal; overflow: auto; padding: var(--su12); vertical-align: baseline; width: auto;"><code class="hljs language-python" style="border: 0px; box-sizing: inherit; font-family: inherit; font-size: var(--fs-body1); font-stretch: inherit; font-style: inherit; font-variant: inherit; font-weight: inherit; line-height: inherit; margin: 0px; padding: 0px; vertical-align: baseline; white-space: inherit;"><br /></code></pre><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-391807043148392272022-05-27T16:50:00.004+08:002022-05-28T09:27:27.840+08:00[Golang] Golang with Cgo 動態連結(dynamic linking) 函式庫範例與筆記 in Linux environment<p><span style="font-family: arial; font-size: medium;">之前曾經動手實作並測試Golang with Cgo 動態連結(dynamic linking) 與 靜態連結(static linking)函式庫範例用在Windows環境,相關內容整理在這篇:</span></p><p><a href="https://danny270degree.blogspot.com/2021/11/golang-golang-with-cgo-dynamic-linking.html" style="background-color: white;"><span style="font-family: arial; font-size: medium;">[Golang] Golang with Cgo 動態連結(dynamic linking) 與 靜態連結(static linking)函式庫範例與筆記 in Windows environment</span></a></p><p><span style="font-family: arial; font-size: medium;">最近看到一篇 "<a href="https://www.bitlogs.tech/2020/09/%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84cgo%E7%A4%BA%E4%BE%8B/">最简单的cgo示例</a>",既簡單且完整的示範了Golang with Cgo 動態連結(dynamic linking) 函式庫範例在Linux環境,個人將其改寫並放到github上: <span></span></span></p><a name='more'></a><p></p><p><span style="font-family: arial; font-size: medium;"><a href="https://github.com/teyenliu/linux-cgo-example">https://github.com/teyenliu/linux-cgo-example</a></span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh7ZOPUJ9lmPhYXgoAns8yBv0ivJ_gcgLwUfscGUHRYLxiJdHtV8_Ve10yhlJLuYD3M-BD5hQuSYeUG5ek-6lDhHAdhwAHpoIqZvFopStx_cpcl02bads60V_-q0Mk6lISQ_zFx4lt3BVwhF7XIYNT6TERERsSVy3oaaVyp1MiBUtBtGdJM_Rkn4Io" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="375" data-original-width="227" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEh7ZOPUJ9lmPhYXgoAns8yBv0ivJ_gcgLwUfscGUHRYLxiJdHtV8_Ve10yhlJLuYD3M-BD5hQuSYeUG5ek-6lDhHAdhwAHpoIqZvFopStx_cpcl02bads60V_-q0Mk6lISQ_zFx4lt3BVwhF7XIYNT6TERERsSVy3oaaVyp1MiBUtBtGdJM_Rkn4Io=w242-h400" width="242" /></a></div><br /><br /><p></p><p></p><div class="separator" style="clear: both; text-align: left;">使用方式:</div><div class="separator" style="clear: both; text-align: left;"><h3 style="background-color: white; margin: 0px; position: relative;"><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;"><span style="color: #f8f8f2;">$ git clone https://github.com/teyenliu/linux-cgo-example
$ cd linux-cgo-example
$ make buildhello
$ cd testing
</span><span style="color: #999999;"><i># 需要修改Makefile內的:
# -I/<your_path>/linux-cgo-example/testing/3rd/include 與
# -L /<your_path>/linux-cgo-example/testing/3rd/lib</i></span><span style="color: #f8f8f2;">
$ make buildtesting
$ ./testing
This is cgo from Go</span></span></span></pre></h3></div><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-1441035662980277262022-05-25T09:12:00.020+08:002022-06-19T16:03:08.190+08:00[Golang] Golang應用程式使用TensorFlow/TensorFlow Lite(TFLite)/Tensorflow Serving 的方法整理<p><span style="font-family: arial; font-size: medium;">Golang應用程式使用TensorFlow 或是 Tensorflow Lite(TFLite) 的前提是必須要有Tensorflow C 函式庫 或是 TensorFlow Lite C 函式庫,一般來說,比較少情況會是用Golang應用程式使用TensorFlow透過TensorFlow C 函式庫,因為未經過最佳化而效能不佳。Golang應用程式使用TensorFlow Lite(TFLite) 透過TensorFlow Lite C 函式庫會是效能較好的其中一種方式。以下將整理相關資訊來說明:</span></p><h3 style="text-align: left;"><span><a name='more'></a></span><p style="text-align: left;"><span style="font-family: arial; font-size: large;"> </span></p></h3><h3 style="text-align: left;"><span style="font-family: arial; font-size: large;">TensorFlow C 函式庫</span></h3><p><a href="https://github.com/tensorflow/build/tree/master/golang_install_guide"><span style="font-family: arial; font-size: medium;">Documentation for installing the Go bindings for TensorFlow.</span></a></p><p><span style="font-family: arial; font-size: medium;">這篇文件算是官方整理出如何安裝 Go bindings for TensorFlow,Go's application 可以透過TensorFlow C API 來進行推論(非訓練). 其中,他使用已經編譯好的TensorFlow C 函式庫,下載後安裝。<b>個人覺得此法是最方便的</b>。</span></p><p><br /></p><p><a href="https://kknews.cc/code/p59ojl2.html"><span style="font-family: arial; font-size: medium;">使用Go語言來理解Tensorflow</span></a></p><p><span style="font-family: arial; font-size: medium;">這篇文章說明了使用Go綁定的用途: </span></p><p><span style="font-family: arial; font-size: medium;">Go綁定可用於導入和定義常量圖,Go API缺少對Variable的支持:該API旨在使用已經訓練過的模型,而不是從頭開始訓練模型。安裝TensorFlow for Go的時候已經明確說明了:</span></p><p><span style="font-family: arial; font-size: medium;">TensorFlow提供了可用於Go程序的API。這些API特別適合於加載用Python創建並需要在Go程序中執行的模型。換句話說,在訓練階段使用Python定義並訓練模型;之後使用Go來加載並使用訓練過的模型!</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"><a href="https://github.com/tensorflow/tensorflow/tree/master/tensorflow/go">TensorFlow in Go</a> (Construct and execute TensorFlow graphs in Go.)</span></p><p><span style="font-family: arial; font-size: medium;">這篇比較不同的地方是<b>從源代碼來構建 TensorFlow C 庫</b> (Build the C library for Tensorflow)</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">bazelisk build -c opt --config monolithic //tensorflow:libtensorflow.so</span></span></pre></h3><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;">但編譯好的 TensorFlow C 庫需要設<span style="background-color: white; color: #121212;">置環境變量,</span></span><span style="background-color: white; color: #121212; font-family: arial;">對應的路徑要換為Linux 上存儲TensorFlow C庫的位置。例如 :</span></span></p><div class="highlight" style="background-color: white; color: #121212; font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif; margin: 1em 0px;"><pre style="background: rgb(246, 246, 246); border-radius: 4px; font-size: 0.9em; margin-bottom: 0px; margin-top: 0px; overflow-wrap: initial; overflow: auto; padding: calc(0.888889em); word-break: initial;"><code class="language-text" style="background-color: inherit; border-radius: 0px; font-family: Menlo, Monaco, Consolas, "Andale Mono", "lucida console", "Courier New", monospace; font-size: inherit; margin: 0px; padding: 0px;">export LIBRARY_PATH=$LIBRARY_PATH:~/go/src/github.com/tensorflow/tensorflow/bazel-bin/tensorflow
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/go/src/github.com/tensorflow/tensorflow/bazel-bin/tensorflow</code></pre></div><p data-pid="v8DQgLbh" style="background-color: white; color: #121212; margin: 1.4em 0px;"><br /></p><p style="text-align: left;"><a href="https://zhuanlan.zhihu.com/p/326116638"><span style="font-family: arial; font-size: medium;">Golang部署TensorFlow機器學習模型</span></a></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">這篇提供了簡單的範例,用Python做模型訓練,用Golang做預測服務。</span></p><p data-pid="Y6t7wplq" style="background-color: white; color: #121212; margin: 1.4em 0px; text-align: left;"><span style="font-family: arial; font-size: medium; vertical-align: inherit;">首先需要注意Python模型保存時需要保存為指定的格式:</span></p><div class="highlight" style="background-color: white; color: #121212; font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif; margin: 1em 0px;"><pre style="background: rgb(246, 246, 246); border-radius: 4px; font-size: 0.9em; margin-bottom: 0px; margin-top: 0px; overflow-wrap: initial; overflow: auto; padding: calc(0.888889em); word-break: initial;"><code class="language-python3" style="background-color: inherit; border-radius: 0px; font-family: Menlo, Monaco, Consolas, "Andale Mono", "lucida console", "Courier New", monospace; font-size: inherit; margin: 0px; padding: 0px;"><span class="kn" style="font-weight: 600;">from</span> <span class="nn" style="color: #646464;">tensorflow.python.saved_model.builder_impl</span> <span class="k" style="font-weight: 600;">import</span> <span class="n">SavedModelBuilder</span>
<span class="k" style="font-weight: 600;">with</span> <span class="n">tf</span><span class="o" style="font-weight: 600;">.</span><span class="n">Session</span><span class="p">()</span> <span class="k" style="font-weight: 600;">as</span> <span class="n">session</span><span class="p">:</span>
<span class="c1" style="color: #999999; font-style: italic;"># 训练模型操作。。。</span>
<span class="c1" style="color: #999999; font-style: italic;"># 保存模型</span>
<span class="n">builder</span> <span class="o" style="font-weight: 600;">=</span> <span class="n">SavedModelBuilder</span><span class="p">(</span><span class="s2" style="color: #f1403c;">"存储路径"</span><span class="p">)</span>
<span class="c1" style="color: #999999; font-style: italic;"># 保存时需要定义tag</span>
<span class="n">builder</span><span class="o" style="font-weight: 600;">.</span><span class="n">add_meta_graph_and_variables</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="p">[</span><span class="s2" style="color: #f1403c;">"tag"</span><span class="p">])</span>
<span class="n">builder</span><span class="o" style="font-weight: 600;">.</span><span class="n">save</span><span class="p">()</span></code></pre></div><p data-pid="jSIeVTG-" style="background-color: white; color: #121212; margin: 1.4em 0px;"><span style="font-family: arial; font-size: medium; vertical-align: inherit;">Golang運行模型,</span><span style="font-family: arial; font-size: large;">具體代碼:</span></p><div class="highlight" style="background-color: white; color: #121212; font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", "Noto Sans CJK SC", "WenQuanYi Micro Hei", sans-serif; margin: 1em 0px;"><pre style="background: rgb(246, 246, 246); border-radius: 4px; font-size: 0.9em; margin-bottom: 0px; margin-top: 0px; overflow-wrap: initial; overflow: auto; padding: calc(0.888889em); word-break: initial;"><code class="language-go" style="background-color: inherit; border-radius: 0px; font-family: Menlo, Monaco, Consolas, "Andale Mono", "lucida console", "Courier New", monospace; font-size: inherit; margin: 0px; padding: 0px;"><span class="kn" style="font-weight: 600;">package</span> <span class="nx">main</span>
<span class="kn" style="font-weight: 600;">import</span> <span class="p">(</span>
<span class="s" style="color: #f1403c;">"fmt"</span>
<span class="nx">tf</span> <span class="s" style="color: #f1403c;">"github.com/tensorflow/tensorflow/tensorflow/go"</span>
<span class="p">)</span>
<span class="kd" style="font-weight: 600;">func</span> <span class="nf" style="color: #f1403c; font-weight: 600;">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">m</span><span class="p">,</span> <span class="nx">err</span> <span class="o" style="font-weight: 600;">:=</span> <span class="nx">tf</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">LoadSavedModel</span><span class="p">(</span><span class="s" style="color: #f1403c;">"modelPath"</span><span class="p">,</span> <span class="p">[]</span><span class="kt" style="color: #175199; font-weight: 600;">string</span><span class="p">{</span><span class="s" style="color: #f1403c;">"modelTag"</span><span class="p">},</span> <span class="kc" style="font-weight: 600;">nil</span><span class="p">)</span> <span class="c1" style="color: #999999; font-style: italic;">// 载入模型
</span><span class="c1" style="color: #999999; font-style: italic;"></span> <span class="k" style="font-weight: 600;">if</span> <span class="nx">err</span> <span class="o" style="font-weight: 600;">!=</span> <span class="kc" style="font-weight: 600;">nil</span> <span class="p">{</span>
<span class="c1" style="color: #999999; font-style: italic;">// 模型加载失败
</span><span class="c1" style="color: #999999; font-style: italic;"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Printf</span><span class="p">(</span><span class="s" style="color: #f1403c;">"err: %v"</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1" style="color: #999999; font-style: italic;">// 打印出所有的Operator
</span><span class="c1" style="color: #999999; font-style: italic;"></span> <span class="k" style="font-weight: 600;">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">op</span> <span class="o" style="font-weight: 600;">:=</span> <span class="k" style="font-weight: 600;">range</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Graph</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Operations</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Printf</span><span class="p">(</span><span class="s" style="color: #f1403c;">"Op name: %v"</span><span class="p">,</span> <span class="nx">op</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Name</span><span class="p">())</span>
<span class="p">}</span>
<span class="c1" style="color: #999999; font-style: italic;">// 构造输入Tensor。根据你的模型入参格式来定义
</span><span class="c1" style="color: #999999; font-style: italic;"></span> <span class="nx">x</span> <span class="o" style="font-weight: 600;">:=</span> <span class="p">[</span><span class="mi" style="color: #0066ff;">1</span><span class="p">][</span><span class="mi" style="color: #0066ff;">8</span><span class="p">]</span><span class="kt" style="color: #175199; font-weight: 600;">int32</span><span class="p">{</span>
<span class="p">{</span><span class="mi" style="color: #0066ff;">0</span><span class="p">,</span><span class="mi" style="color: #0066ff;">1</span><span class="p">,</span><span class="mi" style="color: #0066ff;">2</span><span class="p">,</span><span class="mi" style="color: #0066ff;">3</span><span class="p">,</span><span class="mi" style="color: #0066ff;">4</span><span class="p">,</span><span class="mi" style="color: #0066ff;">5</span><span class="p">,</span><span class="mi" style="color: #0066ff;">6</span><span class="p">,</span><span class="mi" style="color: #0066ff;">7</span><span class="p">},</span>
<span class="p">}</span>
<span class="nx">tensor_x</span><span class="p">,</span> <span class="nx">err</span> <span class="o" style="font-weight: 600;">:=</span> <span class="nx">tf</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">NewTensor</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span>
<span class="k" style="font-weight: 600;">if</span> <span class="nx">err</span> <span class="o" style="font-weight: 600;">!=</span> <span class="kc" style="font-weight: 600;">nil</span> <span class="p">{</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Printf</span><span class="p">(</span><span class="s" style="color: #f1403c;">"err: %s"</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Error</span><span class="p">())</span>
<span class="k" style="font-weight: 600;">return</span>
<span class="p">}</span>
<span class="nx">kb</span><span class="p">,</span> <span class="nx">err</span> <span class="o" style="font-weight: 600;">:=</span> <span class="nx">tf</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">NewTensor</span><span class="p">(</span><span class="nb" style="color: #0066ff;">float32</span><span class="p">(</span><span class="mi" style="color: #0066ff;">1</span><span class="p">))</span>
<span class="k" style="font-weight: 600;">if</span> <span class="nx">err</span> <span class="o" style="font-weight: 600;">!=</span> <span class="kc" style="font-weight: 600;">nil</span> <span class="p">{</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Printf</span><span class="p">(</span><span class="s" style="color: #f1403c;">"err: %s"</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Error</span><span class="p">())</span>
<span class="k" style="font-weight: 600;">return</span>
<span class="p">}</span>
<span class="nx">s</span> <span class="o" style="font-weight: 600;">:=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">Session</span>
<span class="nx">feeds</span> <span class="o" style="font-weight: 600;">:=</span> <span class="kd" style="font-weight: 600;">map</span><span class="p">[</span><span class="nx">tf</span><span class="p">.</span><span class="nx">Output</span><span class="p">]</span><span class="o" style="font-weight: 600;">*</span><span class="nx">tf</span><span class="p">.</span><span class="nx">Tensor</span><span class="p">{</span>
<span class="c1" style="color: #999999; font-style: italic;">// operation name 需要根据你的模型入参来写
</span><span class="c1" style="color: #999999; font-style: italic;"></span> <span class="nx">m</span><span class="p">.</span><span class="nx">Graph</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Operation</span><span class="p">(</span><span class="s" style="color: #f1403c;">"input_x"</span><span class="p">).</span><span class="nf" style="color: #f1403c; font-weight: 600;">Output</span><span class="p">(</span><span class="mi" style="color: #0066ff;">0</span><span class="p">):</span> <span class="nx">tensor_x</span><span class="p">,</span>
<span class="nx">m</span><span class="p">.</span><span class="nx">Graph</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Operation</span><span class="p">(</span><span class="s" style="color: #f1403c;">"keep_prob"</span><span class="p">).</span><span class="nf" style="color: #f1403c; font-weight: 600;">Output</span><span class="p">(</span><span class="mi" style="color: #0066ff;">0</span><span class="p">):</span> <span class="nx">kb</span><span class="p">,</span>
<span class="p">}</span>
<span class="nx">fetches</span> <span class="o" style="font-weight: 600;">:=</span> <span class="p">[]</span><span class="nx">tf</span><span class="p">.</span><span class="nx">Output</span><span class="p">{</span>
<span class="c1" style="color: #999999; font-style: italic;">// 输出层的name 也要根据你的模型写
</span><span class="c1" style="color: #999999; font-style: italic;"></span> <span class="nx">m</span><span class="p">.</span><span class="nx">Graph</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Operation</span><span class="p">(</span><span class="s" style="color: #f1403c;">"score/ArgMax"</span><span class="p">).</span><span class="nf" style="color: #f1403c; font-weight: 600;">Output</span><span class="p">(</span><span class="mi" style="color: #0066ff;">0</span><span class="p">),</span>
<span class="p">}</span>
<span class="nx">result</span><span class="p">,</span> <span class="nx">err</span><span class="o" style="font-weight: 600;">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Run</span><span class="p">(</span><span class="nx">feeds</span><span class="p">,</span> <span class="nx">fetches</span><span class="p">,</span><span class="kc" style="font-weight: 600;">nil</span><span class="p">)</span>
<span class="k" style="font-weight: 600;">if</span> <span class="nx">err</span> <span class="o" style="font-weight: 600;">!=</span> <span class="kc" style="font-weight: 600;">nil</span> <span class="p">{</span>
<span class="c1" style="color: #999999; font-style: italic;">// 模型预测失败
</span><span class="c1" style="color: #999999; font-style: italic;"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Printf</span><span class="p">(</span><span class="s" style="color: #f1403c;">"err: %s "</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Error</span><span class="p">())</span>
<span class="p">}</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf" style="color: #f1403c; font-weight: 600;">Printf</span><span class="p">(</span><span class="s" style="color: #f1403c;">"%#v"</span><span class="p">,</span> <span class="nx">result</span><span class="p">)</span>
<span class="p">}</span>
</code></pre><div><code class="language-go" style="background-color: inherit; border-radius: 0px; font-family: Menlo, Monaco, Consolas, "Andale Mono", "lucida console", "Courier New", monospace; font-size: inherit; margin: 0px; padding: 0px;"><span class="p"><br /></span></code></div></div><p><br /></p><h3 style="text-align: left;"><span style="font-family: arial; font-size: large;">TensorFlow Lite C 函式庫</span></h3><p><a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/c/README.md"><span style="font-family: arial; font-size: medium;">Build the C library for TFLite</span></a></p><p><span style="font-family: arial; font-size: medium;">這篇包含 TensorFlow Lite 的 C API的建置與使用</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">bazelisk build -c opt --config monolithic //tensorflow/lite/c:libtensorflowlite_c.so</span></span></pre></h3><p><span style="font-family: arial; font-size: medium;"><i>P.S: 編譯完成後,shared library: libtensorflowlite_c.so 在bazel-bin/tensorflow/lite/c</i></span><br /><br /></p><p><a href="https://golangexample.com/go-tflite-go-binding-for-tensorflow-lite/"><span style="font-family: arial; font-size: medium;">Go-tflite: Go binding for TensorFlow Lite</span></a></p><p><span style="font-family: arial; font-size: medium;">https://github.com/mattn/go-tflite</span></p><p><span style="font-family: arial; font-size: medium;">這篇文章提供了很不錯的使用TFLite的範例程式。例如: label_image 可以快速驗證編譯好的TensorFlow Lite C 函式庫。</span></p><p><br /></p><h3><span style="font-family: arial; font-size: large;">採用的編譯TensorFlow方式:</span></h3><p><span style="font-size: medium;"><span><span style="font-family: arial;">使用 Bazelisk 安裝 Bazel: </span><span style="font-family: arial;"><a href="https://bazel.build/install/bazelisk">https://bazel.build/install/bazelisk</a>,此方式是</span></span><span style="font-family: arial;">使用 Go 從來源編譯:go install github.com/bazelbuild/bazelisk@latest (需要 Go 1.17 以上版本) 或是用 npm install的方式(<b>建議</b>)。</span></span></p><p><span style="font-family: arial; font-size: medium;">下列式實驗過後成功的做法:</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span><span style="font-size: 15.84px; font-weight: 400;"><span style="color: #f8f8f2;">$ sudo npm install -g @bazel/bazelisk
$ go env GOPATH
$ git clone --branch v2.9.0 https://github.com/tensorflow/tensorflow.git ${GOPATH}/src/github.com/tensorflow/tensorflow
$ sudo apt install libprotobuf-dev protobuf-compiler
$ sudo apt install python3 swig python3-numpy
$ cd ${GOPATH}/src/github.com/tensorflow/tensorflow</span><i><span style="color: #999999;">
# 設定版本</span></i><span style="color: #f8f8f2;">
$ ./configure
You have bazel 4.2.1 installed.
Please specify the location of python. [Default is /usr/bin/python3]:
Found possible Python library paths:
/usr/lib/python3.9/dist-packages
/usr/lib/python3/dist-packages
/usr/local/lib/python3.9/dist-packages
Please input the desired Python library path to use. Default is [/usr/lib/python3.9/dist-packages]
Do you wish to build TensorFlow with ROCm support? [y/N]: N
No ROCm support will be enabled for TensorFlow.
Do you wish to build TensorFlow with CUDA support? [y/N]: N
No CUDA support will be enabled for TensorFlow.
Do you wish to download a fresh release of clang? (Experimental) [y/N]: N
Clang will not be downloaded.
Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is -Wno-sign-compare]:
Would you like to interactively configure ./WORKSPACE for Android builds? [y/N]: N
Not configuring the WORKSPACE for Android builds.
Preconfigured Bazel build configs. You can use any of the below by adding "--config=<>" to your build command. See .bazelrc for more details.
--config=mkl # Build with MKL support.
--config=mkl_aarch64 # Build with oneDNN and Compute Library for the Arm Architecture (ACL).
--config=monolithic # Config for mostly static monolithic build.
--config=numa # Build with NUMA support.
--config=dynamic_kernels # (Experimental) Build kernels into separate shared objects.
--config=v1 # Build with TensorFlow 1 API instead of TF 2 API.
Preconfigured Bazel build configs to DISABLE default on features:
--config=nogcp # Disable GCP support.
--config=nonccl # Disable NVIDIA NCCL support.
Configuration finished
</span></span></span><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;"><pre class="language-javascript" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); color: black; font-weight: 700; hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;"><span style="color: #999999;"># Build TensorFlow C 函式庫</span></span></span></pre>$ bazel build -c opt //tensorflow:libtensorflow.so
<pre class="language-javascript" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); color: black; font-weight: 700; hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;"><span style="color: #999999;"># Build TensorFlow Lite C 函式庫</span></span></span></pre>$ bazel build -c opt //tensorflow/lite/c:libtensorflowlite_c.so
</span></span></pre></h3><p><br /></p><p style="text-align: left;"><a href="https://tflitego.nicolasbortolotti.com/"><span style="font-family: arial; font-size: medium;">TensorFlow Lite for Go</span></a></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">看到還不錯的TensorFlow Lite for Golang的專案. Github: <a href="https://github.com/nbortolotti/tflitego">https://github.com/nbortolotti/tflitego</a></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">tflitego 為在 Golang 中使用 TensorFlow Lite 提供了一個簡單明了的解決方案。其目標是提供一個有凝聚力的 API,與 TensorFlow Lite C API 連接並且容易維護。</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">Examples for tflitego: <a href="https://github.com/nbortolotti/tflitego_examples">https://github.com/nbortolotti/tflitego_examples</a></span></p><p style="text-align: left;"><br /></p><p style="text-align: left;"><br /></p><p style="text-align: left;"><span style="font-family: arial;"><span style="font-size: large;"><b>Tensorflow Serving</b></span></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">對於Tensorflow Serving,目前只能先放上別人的連結,日後再研究:<br /><a href="https://sineyuan.github.io/post/tensorflow-mnist-pratice/">https://sineyuan.github.io/post/tensorflow-mnist-pratice/</a></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br /></span></p><p style="text-align: left;"><br /></p><h3 style="text-align: left;"><span style="font-family: arial; font-size: large;">其他資源:</span></h3><h1 style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; line-height: 1.5em; margin: 5px 0px 0px; outline: 0px; padding: 0px 0px 10px; vertical-align: baseline;"><a href="https://www.getit01.com/p20181229252032004/"><span style="font-family: arial; font-size: medium;">TensorFlow Lite淺度解析</span></a></h1><p><span style="font-family: arial; font-size: medium;"><a href="https://github.com/wamuir/golang-tf">golang-tf (contains Dockerfiles for using Tensorflow in Go)</a></span></p><p><span style="font-family: arial; font-size: medium;">此提供了包含用於在 Go 中使用 Tensorflow 的 Dockerfile。您可以自行構建或從Docker Hub 上的wamuir/golang-tf拉取鏡像</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"></span></p><h1 style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: 微软雅黑, Verdana, Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.5em; margin: 5px 0px 0px; outline: 0px; padding: 0px 0px 10px; vertical-align: baseline;">安裝Golang<br /></h1><h3 dir="auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px;">Download Go binary</h3><h1 style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: 微软雅黑, Verdana, Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.5em; margin: 5px 0px 0px; outline: 0px; padding: 0px 0px 10px; vertical-align: baseline;"><p dir="auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; margin-bottom: 16px; margin-top: 0px;">In current development, Golang version in the environment is v1.14</p><div class="snippet-clipboard-content notranslate position-relative overflow-auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; overflow: auto !important; position: relative !important;"><pre class="notranslate" style="background-color: var(--color-canvas-subtle); border-radius: 6px; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px;"><code style="background: transparent; border-radius: 6px; border: 0px; box-sizing: border-box; display: inline; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">mkdir tmp
cd /tmp
wget https://dl.google.com/go/go1.18.3.linux-amd64.tar.gz
sudo tar -xvf go1.18.3.linux-amd64.tar.gz
sudo mv go /usr/local
</code></pre></div></h1><h3 dir="auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px;"><a aria-hidden="true" class="anchor" href="https://github.com/teyenliu/microsegmentation#optional-environment-setup" id="user-content-optional-environment-setup" style="background-color: transparent; box-sizing: border-box; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1) 0s, background-color 0s ease 0s, box-shadow 0s ease 0s, border-color 0s ease 0s;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z" fill-rule="evenodd"></path></svg></a>Environment setup</h3><h1 style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; color: #333333; font-family: 微软雅黑, Verdana, Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.5em; margin: 5px 0px 0px; outline: 0px; padding: 0px 0px 10px; vertical-align: baseline;"><p dir="auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; margin-bottom: 16px; margin-top: 0px;">We can add the following content in ~/.profile or ~/.bashrc</p><div class="snippet-clipboard-content notranslate position-relative overflow-auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; overflow: auto !important; position: relative !important;"><pre class="notranslate" style="background-color: var(--color-canvas-subtle); border-radius: 6px; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px;"><code style="background: transparent; border-radius: 6px; border: 0px; box-sizing: border-box; display: inline; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">#Golang path
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
</code></pre></div><p dir="auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; margin-bottom: 16px; margin-top: 0px;">Then, renewing the shell sessions</p><div class="snippet-clipboard-content notranslate position-relative overflow-auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; overflow: auto !important; position: relative !important;"><pre class="notranslate" style="background-color: var(--color-canvas-subtle); border-radius: 6px; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px;"><code style="background: transparent; border-radius: 6px; border: 0px; box-sizing: border-box; display: inline; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">source ~/.profile
</code></pre></div><p dir="auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; margin-bottom: 16px; margin-top: 0px;">Golang should have been now installed successfully on the machine and to check if it is run below command</p><div class="snippet-clipboard-content notranslate position-relative overflow-auto" style="background-color: #0d1117; box-sizing: border-box; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; font-weight: 400; overflow: auto !important; position: relative !important;"><pre class="notranslate" style="background-color: var(--color-canvas-subtle); border-radius: 6px; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: 1.45; margin-bottom: 16px; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 16px;"><code style="background: transparent; border-radius: 6px; border: 0px; box-sizing: border-box; display: inline; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; line-height: inherit; margin: 0px; overflow-wrap: normal; overflow: visible; padding: 0px; word-break: normal;">go version</code></pre></div></h1><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-57241340827422808522022-05-17T13:27:00.006+08:002022-05-17T14:18:05.349+08:00[Offensive Security] eBPF、K8S與Offensive Security相關議題之研究<p><span style="font-family: arial; font-size: medium;"> 資安議題已經是越來越火熱的項目,本篇主要目的是進行eBPF、K8S與Offensive Security相關議題之研究,以便於掌握目前之發展狀況。</span></p><span><a name='more'></a></span><p><br /></p><p><span style="font-family: arial;"><b><span style="font-size: large;">Bad BPF: </span></b><br /><a href="https://github.com/pathtofile/bad-bpf"><span style="font-size: medium;">https://github.com/pathtofile/bad-bpf</span></a></span></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: arial; font-size: medium;">A collection of malicious eBPF programs that make use of eBPF's ability to read and write user data in between the usermode program and the kernel.</span></p><p><span style="font-family: arial; font-size: medium;">有一篇文章: <a href="http://www.ctfiot.com/40159.html">EBPF恶意利用及防御 </a> 就在測試 Bad BPF提供的恶意利用的範例,但最後有提到 eBPF的惡意利用雖然有點明顯,其實受限制頗多(因為使用BCC來實作),且存在適配的問題。這些都是有待解決的。</span></p></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><br /></p></blockquote><p style="text-align: left;"><span style="font-family: arial;"><b><span style="font-size: large;">用eBPF製作的Rootkit: </span></b><br /></span></p><p><span style="font-family: arial; font-size: medium;"><a data-cke-saved-href="https://github.com/Gui774ume/ebpfkit" href="https://github.com/Gui774ume/ebpfkit" rel="noreferrer noopener" target="_blank" title="https://github.com/Gui774ume/ebpfkit">https://github.com/Gui774ume/ebpfkit<br /></a>這篇文章: <a data-cke-saved-href="https://tech.meituan.com/2022/03/29/how-to-detect-bad-ebpf-used-in-linux.html" href="https://mp.weixin.qq.com/s?__biz=MzI5MDc4MTM3Mg%3D%3D&mid=2247489146&idx=1&sn=af682a4d461df8ace644e10901b6be8b&scene=21&ref=www.ctfiot.com#wechat_redirect" rel="noreferrer noopener" target="_blank" title="https://tech.meituan.com/2022/03/29/how-to-detect-bad-ebpf-used-in-linux.html"><span>Linux中基於eBPF的惡意利用與檢測機制</span></a> ,值得深入一讀。</span></p><p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: arial; font-size: medium;"><b>攻擊範例: </b></span></p></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: arial; font-size: medium;">透過ebpfkit, 被入侵者的80 port 被轉發到 22 port, 原本的web service正常, 但攻擊者可由80 port 連 sshd</span></p></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: arial; font-size: medium;">攻擊Demo影片:</span><a data-cke-saved-href="https://v.qq.com/txp/iframe/player.html?vid=x3324tskmwm" href="https://v.qq.com/txp/iframe/player.html?vid=x3324tskmwm" rel="noreferrer noopener" target="_blank" title="https://v.qq.com/txp/iframe/player.html?vid=x3324tskmwm"><span style="font-family: arial; font-size: medium;">https://v.qq.com/txp/iframe/player.html?vid=x3324tskmwm</span></a></p></blockquote></blockquote><p><br /></p><p><span style="font-family: arial; font-size: large;"><b>Rootkit介紹: </b></span></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">這篇文章:<a href="https://www.gushiciku.cn/pl/axPG/zh-tw">【Rootkit系列研究】Rootkit檢測技術發展現狀</a> 清楚介紹了當前主流的 Rootkit 防禦技術以及一些非常規 Rootkit 的可實施檢測方案。</span></p></blockquote><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br /></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"> </span></p><p style="text-align: left;"><span style="font-family: arial;"><span style="font-size: large;"><b>使用 eBPF 逃逸容器技术分析与实践:<br /></b></span></span><a href="https://paper.seebug.org/1750/"><span style="font-family: arial; font-size: medium;">https://paper.seebug.org/1750/</span></a></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p style="background-color: white; box-sizing: border-box; color: #3a4145; line-height: 1; margin-bottom: 10px; margin-top: 0px; text-align: left; text-rendering: optimizelegibility;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;"><b>逃逸分析</b></span></p><p style="background-color: white; box-sizing: border-box; color: #3a4145; line-height: 1; margin-bottom: 10px; margin-top: 0px; text-align: left; text-rendering: optimizelegibility;"></p><p></p><table style="background-color: white; border-collapse: collapse; border-spacing: 0px; border: 1px solid rgb(239, 239, 239); color: #3a4145; font-weight: 500; margin: 1.6em 0px; max-width: 100%; width: 800px;"><thead style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><th style="border-bottom-color: rgb(239, 239, 239); border-bottom-style: solid; border-image: initial; border-left-color: rgb(239, 239, 239); border-left-style: solid; border-right-color: rgb(239, 239, 239); border-right-style: solid; border-top-color: initial; border-top-style: initial; border-width: 0px 1px 1px; box-sizing: border-box; color: black; line-height: 20px; padding: 8px; text-align: left; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">特性/功能</span></th><th style="border-bottom-color: rgb(239, 239, 239); border-bottom-style: solid; border-image: initial; border-left-color: rgb(239, 239, 239); border-left-style: solid; border-right-color: rgb(239, 239, 239); border-right-style: solid; border-top-color: initial; border-top-style: initial; border-width: 0px 1px 1px; box-sizing: border-box; color: black; line-height: 20px; padding: 8px; text-align: left; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">要求</span></th></tr></thead><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td style="background-color: #f6f6f6; border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">bpf系統調用</span></td><td style="background-color: #f6f6f6; border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;"><span style="box-sizing: border-box; vertical-align: inherit;">擁有CAP_SYS_ADMIN;</span><span style="box-sizing: border-box; vertical-align: inherit;">內核 5.8 開始擁有 CAP_SYS_ADMIN 或 CAP_BPF</span></span></td></tr><tr style="box-sizing: border-box;"><td style="border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">非特權 bpf - “套接字過濾器”,如</span></td><td style="border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">kernel.unprivileged_bpf_disabled為0或擁有上述權限</span></td></tr><tr style="box-sizing: border-box;"><td style="background-color: #f6f6f6; border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">perf_event_open 系統調用</span></td><td style="background-color: #f6f6f6; border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;"><span style="box-sizing: border-box; vertical-align: inherit;">擁有CAP_SYS_ADMIN;</span><span style="box-sizing: border-box; vertical-align: inherit;">內核 5.8 開始擁有 CAP_SYS_ADMIN 或 CAP_PERFMON</span></span></td></tr><tr style="box-sizing: border-box;"><td style="border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">探針</span></td><td style="border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;"><span style="box-sizing: border-box; vertical-align: inherit;">需要使用tracefs;</span><span style="box-sizing: border-box; vertical-align: inherit;">kernel 4.17後啟用perf_event_open創建</span></span></td></tr><tr style="box-sizing: border-box;"><td style="background-color: #f6f6f6; border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">跟踪點</span></td><td style="background-color: #f6f6f6; border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">需要使用tracefs</span></td></tr><tr style="box-sizing: border-box;"><td style="border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">raw_tracepoint</span></td><td style="border: 1px solid rgb(239, 239, 239); box-sizing: border-box; line-height: 20px; padding: 8px; vertical-align: top;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">內核4.17後通過bpf調用BPF_RAW_TRACEPOINT_OPEN智能</span></td></tr></tbody></table><p></p><p></p></blockquote><p style="background-color: white; box-sizing: border-box; color: #3a4145; line-height: 1; margin-bottom: 10px; margin-top: 0px; text-align: left; text-rendering: optimizelegibility;"></p><p></p><ul style="background-color: white; box-sizing: border-box; color: #3a4145; font-weight: 500; margin: 1.6em 0px;"><ul><li style="box-sizing: border-box; overflow-wrap: break-word;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">bpf_probe_read:安全地從空間內核讀取數據</span></li></ul></ul><ul style="background-color: white; box-sizing: border-box; color: #3a4145; font-weight: 500; margin: 1.6em 0px;"><ul><li style="box-sizing: border-box; overflow-wrap: break-word;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">bpf_probe_write_user:嘗試以一種安全的方式向用戶空間寫數據</span></li></ul></ul><ul style="background-color: white; box-sizing: border-box; color: #3a4145; font-weight: 500; margin: 1.6em 0px;"><ul><li style="box-sizing: border-box; overflow-wrap: break-word;"><span style="font-family: arial; font-size: medium;"><span style="box-sizing: border-box; vertical-align: inherit;">bpf_override_return:用於</span><code style="background: rgb(34, 49, 63); border-radius: 0.3em; box-sizing: border-box; color: #f8f8f2; padding: 1px 3px; white-space: pre-wrap;">error injection</code><span style="box-sizing: border-box; vertical-align: inherit;">修改kprobe監控的函數返回值</span></span></li></ul></ul><ul style="background-color: white; box-sizing: border-box; color: #3a4145; font-weight: 500; margin: 1.6em 0px;"><ul><li style="box-sizing: border-box; overflow-wrap: break-word;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">讀取內核空間裡的敏感信息,或者掛鉤關鍵系統調用的返回點,獲取其他進程空間裡的敏感信息</span></li></ul></ul><ul style="background-color: white; box-sizing: border-box; color: #3a4145; font-weight: 500; margin: 1.6em 0px;"><ul><li style="box-sizing: border-box; overflow-wrap: break-word;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">其他高級權限裡,注入shellcode或者改變的數據進程執行路徑執行自己的命令</span></li></ul></ul><ul style="background-color: white; box-sizing: border-box; color: #3a4145; font-weight: 500; margin: 1.6em 0px;"><ul><li style="box-sizing: border-box; overflow-wrap: break-word;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">其他相似的方法...</span></li></ul></ul><p></p><p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p style="background-color: white; box-sizing: border-box; color: #3a4145; margin: 1.6em 0px; overflow-wrap: break-word; text-align: left;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">展示使用eBPF,需要一些權限和裝載偽文件系統,以下eBPF kprobe、tracepoint的條件:</span></p><p style="background-color: white; box-sizing: border-box; color: #3a4145; margin: 1.6em 0px; overflow-wrap: break-word; text-align: left;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">eBPF 作為附加內核的特定應用,在不會掛起的應用程序中,在不會掛起的內容中,又是可以容納的名稱,並且可以在掛起名稱的範圍內,同時掛載所有內容,並且在掛起時也保持不變。外的進程。</span></p><p style="background-color: white; box-sizing: border-box; color: #3a4145; margin: 1.6em 0px; overflow-wrap: break-word; text-align: left;"><span style="font-family: arial; font-size: medium;"><span style="box-sizing: border-box; vertical-align: inherit;">這些Linux內核為Linux內核提供了某個固定的函數,函數被稱為</span><code style="background: rgb(34, 49, 63); border-radius: 0.3em; box-sizing: border-box; color: #f8f8f2; padding: 1px 3px; white-space: pre-wrap;">BPF-HELPERS</code><span style="box-sizing: border-box; vertical-align: inherit;">,它們的eBPF程序提供了一定的幫助器來查看不同的內核功能,可以使用</span><code style="background: rgb(34, 49, 63); border-radius: 0.3em; box-sizing: border-box; color: #f8f8f2; padding: 1px 3px; white-space: pre-wrap;">man bpf-helpers</code><span style="box-sizing: border-box; vertical-align: inherit;">不同的幫助器類型來調用eBPF程序。不同,關於追查的幫手裡換來以下幾個:</span></span></p><p style="background-color: white; box-sizing: border-box; color: #3a4145; margin: 1.6em 0px; overflow-wrap: break-word; text-align: left;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">這些程序提供了整個讀寫程序上的各種幫助程序用戶態空間的功能,同時提供了加載內核空間或B區的讀取數據能力攻擊者,同時提供了向內核空間進行讀取的能力,有多種提升容器的逃逸權限:</span></p><p style="background-color: white; box-sizing: border-box; color: #3a4145; margin: 1.6em 0px; overflow-wrap: break-word; text-align: left;"><span style="box-sizing: border-box; font-family: arial; font-size: medium; vertical-align: inherit;">注意是eBPF無法改變系統調用時需要的參數,但是可以改變用戶態進入進程進程裡的內存數據。</span></p></blockquote><p style="text-align: left;"></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p style="text-align: left;"><br /></p></blockquote><p><span style="font-family: arial;"><span style="font-size: large;"><b>雲原生之Kubernetes 安全</b></span><br /><a href="https://paper.seebug.org/1803/" style="font-size: large;">https://paper.seebug.org/1803/</a></span></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p><span style="font-family: arial; font-size: medium;">隨著越來越多企業踏上上雲的步伐,在攻防演練中常常碰到雲相關的場景,例如:公有云、私有云、混合雲、虛擬化集群等。以往滲透路徑是「外網突破-> 提權-> 權限維持-> 信息收集-> 橫向移動-> 循環收集信息」,直到獲得重要目標系統。但隨著業務上雲以及虛擬化技術的引入改變了這種格局,也打開了新的入侵路徑,例如:</span></p><p><span style="font-family: arial; font-size: medium;">● 通過虛擬機攻擊雲管理平台,利用管理平台控制所有機器。</span></p><p><span style="font-family: arial; font-size: medium;">● 通過容器進行逃逸,從而控制宿主機以及橫向滲透到K8s Master節點控制所有容器。</span></p><p><span style="font-family: arial; font-size: medium;">● 利用 KVM-QEMU/執行逃逸獲取宿主機,進入物理網絡橫向移動控制雲平台。</span></p><p><span style="font-family: arial; font-size: medium;">目前互聯網上針對雲原生場景下的攻擊手法大都零零散散,僅有少部分廠商發布過相關矩陣技術,但都沒有過多的細節展示,本文基於微軟發布的 Kubernetes 威脅矩陣進行擴展,將深入介紹相關的具體攻擊方法。</span></p></blockquote><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh_eJIEh5gDKKGi37j79yKhVe6A20iAoCtbq7EHK3YDV9n1x3vgax3vRefZXnJ2SZtjBZpkK2jMm_Odh3xT1ll2oWnbrJw7wKAqBUaJbEGvufm4FYqxyhlEMOzaaEsSPKxlACXv9G8vGeJ4RsYb5W7atzkA464NpqFQN5meQ2OAwJNXPBbrOHSMfuI" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="504" data-original-width="1080" src="https://blogger.googleusercontent.com/img/a/AVvXsEh_eJIEh5gDKKGi37j79yKhVe6A20iAoCtbq7EHK3YDV9n1x3vgax3vRefZXnJ2SZtjBZpkK2jMm_Odh3xT1ll2oWnbrJw7wKAqBUaJbEGvufm4FYqxyhlEMOzaaEsSPKxlACXv9G8vGeJ4RsYb5W7atzkA464NpqFQN5meQ2OAwJNXPBbrOHSMfuI=s16000" /></a></div><br /> <p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><br /></p></blockquote><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-55837918080692346052022-05-17T11:15:00.005+08:002022-05-25T08:57:20.629+08:00[Surftrace] 開源項目Surftrace,基於libbpf的ftrace加強版<p style="text-align: left;"><span style="font-family: arial; font-size: medium;"> <span>Surftrace 是由系統運維SIG 推出的一個ftrace 封裝器和開發編譯平台,讓用戶既能基於libbpf 快速構建工程進行開發,也能作為ftrace 的封裝器進行trace 命令編寫。</span></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">Surftrace 在網絡層面的增強,使得用戶只需要有相關的網絡基礎和一定的內核知識儲備,就可以用較低編碼工作量達到精準追踪網絡報文在Linux 內核的完整處理過程。適合用於追踪Linux 內核協議棧代碼、定位深層次網絡問題。<span></span></span></p><a name='more'></a><p></p><p><span style="font-family: arial; font-size: medium;">surftrace的主要目標是為了降低追踪的線索,達到快速獲取核心信息目標。</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: arial; font-size: medium;">一內核跟踪符號,並獲取關鍵內核數據;</span></li><li><span style="font-family: arial; font-size: medium;">除了 C 和 linux 操作系統內核,用戶自行掌握其他知識點(需要獲取數據進行二次處理除外);</span></li><li><span style="font-family: arial; font-size: medium;">作品名稱發行版;</span></li><li><span style="font-family: arial; font-size: medium;">bbcc類似開發模式,達到libbpf最佳資源消耗;</span></li></ul><div><span style="font-family: arial; font-size: medium;">更多資訊請參考:</span></div><div><a href="https://zhuanlan.zhihu.com/p/513202949"><span style="font-family: arial; font-size: medium;">https://zhuanlan.zhihu.com/p/513202949</span></a></div><div><a href="https://github.com/aliyun/surftrace"><span style="font-family: arial; font-size: medium;">https://github.com/aliyun/surftrace</span></a></div><div><br /></div><p></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-75706246703110762352022-05-15T11:15:00.004+08:002022-10-17T10:31:23.888+08:00[Kafka] 初探Kafka using Golang<p><span style="font-family: arial; font-size: large;"><b>前言</b></span></p><p><span style="font-family: arial; font-size: medium;">本篇是個人進行初探Kafka using Golang時,把遇到坑填平後的結果紀錄。</span></p><p><br /></p><p><span style="font-family: arial; font-size: large;"><b>Kafka 基本知識</b></span></p><p><span style="font-size: medium;">網路上已經有很多相關文章,故不贅述。</span></p><p><a href="https://medium.com/%E3%84%9A%E5%8C%97%E7%9A%84%E6%89%80%E8%A6%8B%E6%89%80%E8%81%9E/%E5%88%9D%E5%AD%B8kafka-5668cf06e5bf"><span style="font-family: arial; font-size: medium;">初學Kafka</span></a></p><p><a href="https://ftn8205.medium.com/kafka-%E4%BB%8B%E7%B4%B9-golang%E7%A8%8B%E5%BC%8F%E5%AF%A6%E4%BD%9C-2b108481369e"><span style="font-family: arial; font-size: medium;">Kafka 介紹 + Golang程式實作</span></a></p><p><a href="https://www.lixueduan.com/post/kafka/05-quick-start/"><span style="font-family: arial; font-size: medium;">Kafka(Go)教程(五)---Producer-Consumer API 基本使用</span></a></p><p style="text-align: left;"><a href="https://www.twblogs.net/a/5c2461efbd9eee16b3db5b7e"><span style="font-family: arial; font-size: medium;">Kafka理論之Consumer Group & Coordinator</span></a></p><span><a name='more'></a></span><p><br /></p><p><span style="font-family: arial; font-size: large;"><b>Kafka 環境啟動</b></span></p><p><span style="font-family: arial; font-size: medium;"> 透過Docker-Compose YAML file 如下:<br /></span></p><h3><p><span style="font-weight: normal;"><span style="font-family: arial; font-size: medium;">$ docker-compolse -f dc-kafka.yaml up -d</span></span></p></h3><p><span style="color: #999999; font-family: arial; font-size: medium;"><i>P.S: 這邊需要設定Kafka啟動所需要的IP與Port Number (我習慣用9091) <br />PLAINTEXT_HOST://<<Server IP>>:9091 </i></span><br /></p><p><span style="font-family: arial; font-size: medium;">dc-kafka.yaml</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;">version: '2'
services:
zookeeper:
image: confluentinc/cp-zookeeper:5.3.1
hostname: zookeeper
container_name: zookeeper
ports:
- "2181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
broker:
image: confluentinc/cp-kafka:5.3.1
hostname: broker
container_name: broker
depends_on:
- zookeeper
ports:
- "9091:9091"
environment:
KAFKA_BROKER_ID: 1
KAFKA_BOOTSTRAP.SERVERS: 'broker:29091'
KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:29091,PLAINTEXT_HOST://</span><span style="color: red; font-size: 15.84px;"><<Server IP>></span><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;">:</span><span style="color: #ff00fe; font-size: 15.84px;">9091</span><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;">
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
control-center:
image: confluentinc/cp-enterprise-control-center:5.3.1
hostname: control-center
container_name: control-center
depends_on:
- zookeeper
- broker
ports:
- "9021:9021"
environment:
CONTROL_CENTER_BOOTSTRAP_SERVERS: 'broker:29091'
CONTROL_CENTER_ZOOKEEPER_CONNECT: 'zookeeper:2181'
CONTROL_CENTER_CONNECT_CLUSTER: 'connect:8083'
CONTROL_CENTER_KSQL_URL: "http://ksql-server:8088"
CONTROL_CENTER_KSQL_ADVERTISED_URL: "http://localhost:8088"
CONTROL_CENTER_SCHEMA_REGISTRY_URL: "http://schema-registry:8081"
CONTROL_CENTER_REPLICATION_FACTOR: 1
CONTROL_CENTER_INTERNAL_TOPICS_PARTITIONS: 1
CONTROL_CENTER_MONITORING_INTERCEPTOR_TOPIC_PARTITIONS: 1
CONFLUENT_METRICS_TOPIC_REPLICATION: 1
PORT: 9021</span><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;">
</span></span></pre><div><span style="font-weight: normal;"><br /></span></div><p style="text-align: left;"><span style="font-weight: normal;">開啟Kafka Configuration Web UI : </span><span style="font-weight: 400;"><<Server IP>>:9021 </span></p></h3><p><span style="font-family: arial; font-size: medium;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: arial; font-size: medium;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi2TWNIu8CUEjA9PFGz02KOTMBfZWCvEtSZK8xvYMLA32hnjatYdYtZ0k-ajfuyXkftpgEY5U4Fr9vKZ0y6AzR-EkCOPFa57X6V_S6XhQ8WQxA-pGDfdb6xqNbgS4_lF7vUN1zpklKBdLJ3lJqA9B_9_boMSxxxh342s8HZ4PVX7nnuNHudCsaIvC0" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="409" data-original-width="1150" height="228" src="https://blogger.googleusercontent.com/img/a/AVvXsEi2TWNIu8CUEjA9PFGz02KOTMBfZWCvEtSZK8xvYMLA32hnjatYdYtZ0k-ajfuyXkftpgEY5U4Fr9vKZ0y6AzR-EkCOPFa57X6V_S6XhQ8WQxA-pGDfdb6xqNbgS4_lF7vUN1zpklKBdLJ3lJqA9B_9_boMSxxxh342s8HZ4PVX7nnuNHudCsaIvC0=w640-h228" width="640" /></a></span></div><span style="font-family: arial; font-size: medium;"><br />P.S: 我這邊先創建一個Topic: <b><span style="color: red;">packet-log</span></b> with 12 Partitions,這邊的Topic: <b><span style="color: red;">packet-log</span></b> 也會在下面程式中使用到<br /><br /></span><p></p><p><span style="font-family: arial; font-size: medium;"><b style="font-size: x-large;">Kafka example using Golang</b></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">我是修改: 這篇文章的Source Code: </span><a class="text-body" href="https://segmentfault.com/a/1190000040820548" style="--bs-text-opacity: 1; box-sizing: border-box; text-decoration-line: none;"><span style="font-family: arial; font-size: medium;">Go 操作kafka包sarama</span></a></p><p style="text-align: left;"><span style="color: #666666; font-family: arial; font-size: medium;"><i>sarama 是一個純Go 客戶端庫,用於處理Apache Kafka(0.8 及更高版本)。</i></span></p><p style="text-align: left;"><br /></p><p style="text-align: left;"><span style="font-family: arial; font-size: large;">Kafka Producer</span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">package</span> main</div><br /><div><span style="color: #569cd6;">import</span> (</div><div> <span style="color: #ce9178;">"fmt"</span></div><div> <span style="color: #ce9178;">"log"</span></div><div> <span style="color: #ce9178;">"os"</span></div><div> <span style="color: #ce9178;">"os/signal"</span></div><div> <span style="color: #ce9178;">"sync"</span></div><div> <span style="color: #ce9178;">"time"</span></div><br /><div> <span style="color: #ce9178;">"github.com/Shopify/sarama"</span></div><div>)</div><br /><div><span style="color: #6a9955;">//异步生产者Goroutines</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">AsyncProducer</span>() {</div><div> <span style="color: #9cdcfe;">config</span> := sarama.<span style="color: #dcdcaa;">NewConfig</span>()</div><div> <span style="color: #9cdcfe;">config.Producer.Return.Successes</span> = <span style="color: #569cd6;">true</span></div><div> <span style="color: #9cdcfe;">config.Producer.Return.Errors</span> = <span style="color: #569cd6;">true</span></div><div> <span style="color: #9cdcfe;">producer</span>, <span style="color: #9cdcfe;">err</span> := sarama.<span style="color: #dcdcaa;">NewAsyncProducer</span>([]<span style="color: #4ec9b0;">string</span>{<span style="color: #ce9178;">"</span><span style="color: #ce9178;">localhost</span><span style="color: #ce9178;">:9091"</span>}, config)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #dcdcaa;">panic</span>(err)</div><div> }</div><br /><div> <span style="color: #6a9955;">// Trap SIGINT to trigger a graceful shutdown.</span></div><div> <span style="color: #9cdcfe;">signals</span> := <span style="color: #dcdcaa;">make</span>(<span style="color: #569cd6;">chan</span> os.Signal, <span style="color: #b5cea8;">1</span>)</div><div> signal.<span style="color: #dcdcaa;">Notify</span>(signals, os.Interrupt)</div><br /><div> <span style="color: #569cd6;">var</span> (</div><div> <span style="color: #9cdcfe;">wg</span> sync.WaitGroup</div><div> <span style="color: #9cdcfe;">enqueued</span>, <span style="color: #9cdcfe;">successes</span>, <span style="color: #9cdcfe;">producerErrors</span> <span style="color: #4ec9b0;">int</span></div><div> )</div><br /><div> wg.<span style="color: #dcdcaa;">Add</span>(<span style="color: #b5cea8;">1</span>)</div><div> <span style="color: #c586c0;">go</span> <span style="color: #569cd6;">func</span>() {</div><div> <span style="color: #c586c0;">defer</span> wg.<span style="color: #dcdcaa;">Done</span>()</div><div> <span style="color: #c586c0;">for</span> <span style="color: #c586c0;">range</span> producer.<span style="color: #dcdcaa;">Successes</span>() {</div><div> successes++</div><div> }</div><div> }()</div><br /><div> wg.<span style="color: #dcdcaa;">Add</span>(<span style="color: #b5cea8;">1</span>)</div><div> <span style="color: #c586c0;">go</span> <span style="color: #569cd6;">func</span>() {</div><div> <span style="color: #c586c0;">defer</span> wg.<span style="color: #dcdcaa;">Done</span>()</div><div> <span style="color: #c586c0;">for</span> <span style="color: #9cdcfe;">err</span> := <span style="color: #c586c0;">range</span> producer.<span style="color: #dcdcaa;">Errors</span>() {</div><div> log.<span style="color: #dcdcaa;">Println</span>(err)</div><div> producerErrors++</div><div> }</div><div> }()</div><br /><div>ProducerLoop:</div><div> <span style="color: #c586c0;">for</span> {</div><div> <span style="color: #9cdcfe;">message</span> := &sarama.ProducerMessage{Topic: <span style="color: #ce9178;">"packet-log"</span>,</div><div> Value: sarama.<span style="color: #dcdcaa;">StringEncoder</span>(fmt.<span style="color: #dcdcaa;">Sprintf</span>(<span style="color: #ce9178;">"testing AsyncProducer </span><span style="color: #9cdcfe;">%d</span><span style="color: #ce9178;">"</span>, enqueued))}</div><div> time.<span style="color: #dcdcaa;">Sleep</span>(<span style="color: #b5cea8;">1</span> * time.Second)</div><div> <span style="color: #c586c0;">select</span> {</div><div> <span style="color: #c586c0;">case</span> producer.<span style="color: #dcdcaa;">Input</span>() <- message:</div><div> enqueued++</div><br /><div> <span style="color: #c586c0;">case</span> <-signals:</div><div> producer.<span style="color: #dcdcaa;">AsyncClose</span>() <span style="color: #6a9955;">// Trigger a shutdown of the producer.</span></div><div> <span style="color: #c586c0;">break</span> ProducerLoop</div><div> }</div><div> }</div><div> wg.<span style="color: #dcdcaa;">Wait</span>()</div><br /><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"Successfully produced: </span><span style="color: #9cdcfe;">%d</span><span style="color: #ce9178;">; errors: </span><span style="color: #9cdcfe;">%d</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, successes, producerErrors)</div><div>}</div><br /><div><span style="color: #6a9955;">//异步生产者Select</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">AsyncProducerSelect</span>() {</div><div> <span style="color: #9cdcfe;">producer</span>, <span style="color: #9cdcfe;">err</span> := sarama.<span style="color: #dcdcaa;">NewAsyncProducer</span>([]<span style="color: #4ec9b0;">string</span>{<span style="color: #ce9178;">"localhost:9091"</span>}, <span style="color: #569cd6;">nil</span>)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #dcdcaa;">panic</span>(err)</div><div> }</div><br /><div> <span style="color: #c586c0;">defer</span> <span style="color: #569cd6;">func</span>() {</div><div> <span style="color: #c586c0;">if</span> <span style="color: #9cdcfe;">err</span> := producer.<span style="color: #dcdcaa;">Close</span>(); err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatalln</span>(err)</div><div> }</div><div> }()</div><br /><div> <span style="color: #6a9955;">// Trap SIGINT to trigger a shutdown.</span></div><div> <span style="color: #9cdcfe;">signals</span> := <span style="color: #dcdcaa;">make</span>(<span style="color: #569cd6;">chan</span> os.Signal, <span style="color: #b5cea8;">1</span>)</div><div> signal.<span style="color: #dcdcaa;">Notify</span>(signals, os.Interrupt)</div><br /><div> <span style="color: #569cd6;">var</span> <span style="color: #9cdcfe;">enqueued</span>, <span style="color: #9cdcfe;">producerErrors</span> <span style="color: #4ec9b0;">int</span></div><div>ProducerLoop:</div><div> <span style="color: #c586c0;">for</span> {</div><div> <span style="color: #c586c0;">select</span> {</div><div> <span style="color: #c586c0;">case</span> producer.<span style="color: #dcdcaa;">Input</span>() <- &sarama.ProducerMessage{Topic: <span style="color: #ce9178;">"packet-log"</span>, Key: <span style="color: #569cd6;">nil</span>,</div><div> Value: sarama.<span style="color: #dcdcaa;">StringEncoder</span>(fmt.<span style="color: #dcdcaa;">Sprintf</span>(<span style="color: #ce9178;">"testing AsyncProducerSelect </span><span style="color: #9cdcfe;">%d</span><span style="color: #ce9178;">"</span>, enqueued))}:</div><div> enqueued++</div><div> time.<span style="color: #dcdcaa;">Sleep</span>(<span style="color: #b5cea8;">1</span> * time.Second)</div><div> <span style="color: #c586c0;">case</span> <span style="color: #9cdcfe;">err</span> := <-producer.<span style="color: #dcdcaa;">Errors</span>():</div><div> log.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"Failed to produce message"</span>, err)</div><div> producerErrors++</div><div> <span style="color: #c586c0;">case</span> <-signals:</div><div> <span style="color: #c586c0;">break</span> ProducerLoop</div><div> }</div><div> }</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"Enqueued: </span><span style="color: #9cdcfe;">%d</span><span style="color: #ce9178;">; errors: </span><span style="color: #9cdcfe;">%d</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, enqueued, producerErrors)</div><div>}</div><br /><div><span style="color: #6a9955;">//同步生产模式</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">SaramaProducer</span>() {</div><div> <span style="color: #9cdcfe;">producer</span>, <span style="color: #9cdcfe;">err</span> := sarama.<span style="color: #dcdcaa;">NewSyncProducer</span>([]<span style="color: #4ec9b0;">string</span>{<span style="color: #ce9178;">"</span><span style="color: #ce9178;">localhost</span><span style="color: #ce9178;">:9091"</span>}, <span style="color: #569cd6;">nil</span>)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatalln</span>(err)</div><div> }</div><div> <span style="color: #c586c0;">defer</span> <span style="color: #569cd6;">func</span>() {</div><div> <span style="color: #c586c0;">if</span> <span style="color: #9cdcfe;">err</span> := producer.<span style="color: #dcdcaa;">Close</span>(); err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatalln</span>(err)</div><div> }</div><div> }()</div><br /><div> <span style="color: #9cdcfe;">msg</span> := &sarama.ProducerMessage{Topic: <span style="color: #ce9178;">"packet-log"</span>, Value: sarama.<span style="color: #dcdcaa;">StringEncoder</span>(<span style="color: #ce9178;">"testing SaramaProducer"</span>)}</div><div> <span style="color: #9cdcfe;">partition</span>, <span style="color: #9cdcfe;">offset</span>, <span style="color: #9cdcfe;">err</span> := producer.<span style="color: #dcdcaa;">SendMessage</span>(msg)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"FAILED to send message: </span><span style="color: #9cdcfe;">%s</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, err)</div><div> } <span style="color: #c586c0;">else</span> {</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"> message sent to partition </span><span style="color: #9cdcfe;">%d</span><span style="color: #ce9178;"> at offset </span><span style="color: #9cdcfe;">%d</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, partition, offset)</div><div> }</div><br /><div>}</div><br /><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">main</span>() {</div><div> <span style="color: #6a9955;">//生产者</span></div><div> <span style="color: #6a9955;">//AsyncProducer()</span></div><div> <span style="color: #6a9955;">//SaramaProducer()</span></div><div> <span style="color: #dcdcaa;">AsyncProducerSelect</span>()</div><div>}</div><br /></div><p><br /></p><p><span style="font-family: arial; font-size: large;">Kafka ConsumerGroup</span></p><p><span style="font-family: arial;"><i><span style="color: #666666; font-size: medium;">P.S: 我這邊是使用ConsumerGroup的例子 ( 多個Consumers在同一個Group內,每一訊息保證只會有一個Consumer拿到)</span></i></span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">package</span> main</div><br /><div><span style="color: #569cd6;">import</span> (</div><div> <span style="color: #ce9178;">"context"</span></div><div> <span style="color: #ce9178;">"fmt"</span></div><div> <span style="color: #ce9178;">"log"</span></div><div> <span style="color: #ce9178;">"os"</span></div><div> <span style="color: #ce9178;">"os/signal"</span></div><br /><div> <span style="color: #ce9178;">"github.com/Shopify/sarama"</span></div><div>)</div><br /><div><span style="color: #569cd6;">type</span> <span style="color: #4ec9b0;">consumerGroupHandler</span> <span style="color: #569cd6;">struct</span> {</div><div>}</div><br /><div><span style="color: #569cd6;">func</span> (consumerGroupHandler) <span style="color: #dcdcaa;">Setup</span>(sarama.ConsumerGroupSession) <span style="color: #4ec9b0;">error</span> {</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">nil</span></div><div>}</div><div><span style="color: #569cd6;">func</span> (consumerGroupHandler) <span style="color: #dcdcaa;">Cleanup</span>(sarama.ConsumerGroupSession) <span style="color: #4ec9b0;">error</span> { <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">nil</span> }</div><br /><div><span style="color: #6a9955;">// ConsumeClaim must start a consumer loop of ConsumerGroupClaim's Messages().</span></div><div><span style="color: #569cd6;">func</span> (consumerGroupHandler) <span style="color: #dcdcaa;">ConsumeClaim</span>(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) <span style="color: #4ec9b0;">error</span> {</div><div> <span style="color: #c586c0;">for</span> <span style="color: #9cdcfe;">msg</span> := <span style="color: #c586c0;">range</span> claim.<span style="color: #dcdcaa;">Messages</span>() {</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"Message claimed: value = </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">, timestamp = </span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">, topic = </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, msg.Value, msg.Timestamp, msg.Topic)</div><div> session.<span style="color: #dcdcaa;">MarkMessage</span>(msg, <span style="color: #ce9178;">""</span>)</div><div> }</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">nil</span></div><div>}</div><br /><div><span style="color: #6a9955;">//消费者组</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">SaramaConsumerGroup</span>() {</div><div> <span style="color: #9cdcfe;">config</span> := sarama.<span style="color: #dcdcaa;">NewConfig</span>()</div><div> <span style="color: #9cdcfe;">config.Consumer.Return.Errors</span> = <span style="color: #569cd6;">false</span></div><div> <span style="color: #9cdcfe;">config.Version</span> = sarama.V0_10_2_0 <span style="color: #6a9955;">// specify appropriate version</span></div><div> <span style="color: #9cdcfe;">config.Consumer.Offsets.Initial</span> = sarama.OffsetOldest <span style="color: #6a9955;">// 未找到组消费位移的时候从哪边开始消费</span></div><br /><div> <span style="color: #9cdcfe;">group</span>, <span style="color: #9cdcfe;">err</span> := sarama.<span style="color: #dcdcaa;">NewConsumerGroup</span>([]<span style="color: #4ec9b0;">string</span>{<span style="color: #ce9178;">"140.113.207.9:9091"</span>}, <span style="color: #ce9178;">"my-group"</span>, config)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #dcdcaa;">panic</span>(err)</div><div> }</div><div> <span style="color: #c586c0;">defer</span> <span style="color: #569cd6;">func</span>() { <span style="color: #9cdcfe;">_</span> = group.<span style="color: #dcdcaa;">Close</span>() }()</div><br /><div> <span style="color: #6a9955;">// Track errors</span></div><div> <span style="color: #c586c0;">go</span> <span style="color: #569cd6;">func</span>() {</div><div> <span style="color: #c586c0;">for</span> <span style="color: #9cdcfe;">err</span> := <span style="color: #c586c0;">range</span> group.<span style="color: #dcdcaa;">Errors</span>() {</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"ERROR"</span>, err)</div><div> }</div><div> }()</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"Consumed start"</span>)</div><div> <span style="color: #6a9955;">// Iterate over consumer sessions.</span></div><div> <span style="color: #9cdcfe;">ctx</span> := context.<span style="color: #dcdcaa;">Background</span>()</div><div> <span style="color: #c586c0;">for</span> {</div><div> <span style="color: #9cdcfe;">topics</span> := []<span style="color: #4ec9b0;">string</span>{<span style="color: #ce9178;">"packet-log"</span>}</div><div> <span style="color: #9cdcfe;">handler</span> := consumerGroupHandler{}</div><br /><div> <span style="color: #6a9955;">// `Consume` should be called inside an infinite loop, when a</span></div><div> <span style="color: #6a9955;">// server-side rebalance happens, the consumer session will need to be</span></div><div> <span style="color: #6a9955;">// recreated to get the new claims</span></div><div> <span style="color: #9cdcfe;">err</span> := group.<span style="color: #dcdcaa;">Consume</span>(ctx, topics, handler)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #dcdcaa;">panic</span>(err)</div><div> }</div><div> }</div><div>}</div><br /><div><span style="color: #6a9955;">//消费者</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">SaramaConsumer</span>() {</div><div> <span style="color: #9cdcfe;">consumer</span>, <span style="color: #9cdcfe;">err</span> := sarama.<span style="color: #dcdcaa;">NewConsumer</span>([]<span style="color: #4ec9b0;">string</span>{<span style="color: #ce9178;">"140.113.207.9:9091"</span>}, sarama.<span style="color: #dcdcaa;">NewConfig</span>())</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #dcdcaa;">panic</span>(err)</div><div> }</div><br /><div> <span style="color: #c586c0;">defer</span> <span style="color: #569cd6;">func</span>() {</div><div> <span style="color: #c586c0;">if</span> <span style="color: #9cdcfe;">err</span> := consumer.<span style="color: #dcdcaa;">Close</span>(); err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatalln</span>(err)</div><div> }</div><div> }()</div><br /><div> <span style="color: #9cdcfe;">partitionConsumer</span>, <span style="color: #9cdcfe;">err</span> := consumer.<span style="color: #dcdcaa;">ConsumePartition</span>(<span style="color: #ce9178;">"packet-log"</span>, <span style="color: #b5cea8;">0</span>, sarama.OffsetNewest)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #dcdcaa;">panic</span>(err)</div><div> }</div><br /><div> <span style="color: #c586c0;">defer</span> <span style="color: #569cd6;">func</span>() {</div><div> <span style="color: #c586c0;">if</span> <span style="color: #9cdcfe;">err</span> := partitionConsumer.<span style="color: #dcdcaa;">Close</span>(); err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatalln</span>(err)</div><div> }</div><div> }()</div><br /><div> <span style="color: #6a9955;">// Trap SIGINT to trigger a shutdown.</span></div><div> <span style="color: #9cdcfe;">signals</span> := <span style="color: #dcdcaa;">make</span>(<span style="color: #569cd6;">chan</span> os.Signal, <span style="color: #b5cea8;">1</span>)</div><div> signal.<span style="color: #dcdcaa;">Notify</span>(signals, os.Interrupt)</div><br /><div> <span style="color: #9cdcfe;">consumed</span> := <span style="color: #b5cea8;">0</span></div><div>ConsumerLoop:</div><div> <span style="color: #c586c0;">for</span> {</div><div> <span style="color: #c586c0;">select</span> {</div><div> <span style="color: #c586c0;">case</span> <span style="color: #9cdcfe;">msg</span> := <-partitionConsumer.<span style="color: #dcdcaa;">Messages</span>():</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"Consumed message offset </span><span style="color: #9cdcfe;">%d</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, msg.Offset)</div><div> consumed++</div><div> <span style="color: #c586c0;">case</span> <-signals:</div><div> <span style="color: #c586c0;">break</span> ConsumerLoop</div><div> }</div><div> }</div><br /><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"Consumed: </span><span style="color: #9cdcfe;">%d</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, consumed)</div><div>}</div><br /><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">main</span>() {</div><div> <span style="color: #6a9955;">//消费者</span></div><div> <span style="color: #dcdcaa;">SaramaConsumerGroup</span>()</div><div> <span style="color: #6a9955;">//SaramaConsumer()</span></div><div>}</div><br /></div><p><br /></p><p><span style="font-family: arial; font-size: medium;">測試:</span></p><p><span style="font-family: arial; font-size: medium;">啟動一個Producer&3個Consumers(在同一個Group)</span></p><p><span style="font-family: arial; font-size: medium;">運行結果:</span></p><p><span style="font-family: arial; font-size: medium;">3個Consumers會拿到唯一的訊息,達到ConsumerGroup的效果</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">Consumed start
2022/05/15 02:39:28 Message claimed: value = testing AsyncProducerSelect 3, timestamp = 2022-05-15 02:39:28.104 +0000 UTC, topic = packet-log
2022/05/15 02:39:31 Message claimed: value = testing AsyncProducerSelect 6, timestamp = 2022-05-15 02:39:31.105 +0000 UTC, topic = packet-log
2022/05/15 02:39:32 Message claimed: value = testing AsyncProducerSelect 7, timestamp = 2022-05-15 02:39:32.106 +0000 UTC, topic = packet-log
2022/05/15 02:39:34 Message claimed: value = testing AsyncProducerSelect 9, timestamp = 2022-05-15 02:39:34.108 +0000 UTC, topic = packet-log</span></span></pre></h3><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">Consumed start
2022/05/15 02:39:25 Message claimed: value = testing AsyncProducerSelect 0, timestamp = 2022-05-15 02:39:25.102 +0000 UTC, topic = packet-log
2022/05/15 02:39:30 Message claimed: value = testing AsyncProducerSelect 5, timestamp = 2022-05-15 02:39:30.105 +0000 UTC, topic = packet-log
2022/05/15 02:39:33 Message claimed: value = testing AsyncProducerSelect 8, timestamp = 2022-05-15 02:39:33.107 +0000 UTC, topic = packet-log</span></span></pre></h3><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">Consumed start
2022/05/15 02:39:26 Message claimed: value = testing AsyncProducerSelect 1, timestamp = 2022-05-15 02:39:26.103 +0000 UTC, topic = packet-log
2022/05/15 02:39:27 Message claimed: value = testing AsyncProducerSelect 2, timestamp = 2022-05-15 02:39:27.103 +0000 UTC, topic = packet-log
2022/05/15 02:39:29 Message claimed: value = testing AsyncProducerSelect 4, timestamp = 2022-05-15 02:39:29.104 +0000 UTC, topic = packet-log</span><span style="font-size: 15.84px; font-weight: 400;">
</span></span></pre><div><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;"><br /></span></span></div></h3><p><span style="font-family: arial; font-size: medium;"></span></p><p><span style="font-family: arial; font-size: medium;"><b style="font-size: x-large;">心得</b></span></p><p><span style="font-family: arial; font-size: medium;">Kafka是個很強大的Message Queueing & Streaming的平台,目前的初探僅止於透過小程式去體驗Kafak強大的功能。</span></p><p><span style="font-family: arial; font-size: medium;"><br />目前的疑問: 設定Partitions 的數量需要大於 Group內的Consumers數量,才能使每個Consumer都能收到訊息,有其他的方式達到嗎?</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-12059792886405294712022-05-10T13:10:00.005+08:002022-05-15T11:40:22.311+08:00[RedisGraph] 初探 RedisGraph<p><span style="font-size: medium;"><span style="font-family: arial;">前陣子因為交接別人的應用程式,有幸</span><span style="font-family: arial;">(<strike>被迫</strike>)</span><span style="font-family: arial;">接觸到RedisGraph這個東西,為了能讓自己快速上手這玩意,便做了一些小研究。</span></span></p><p><span style="font-family: arial; font-size: x-large;">甚麼是RedisGraph?</span></p><p><span style="font-family: arial; font-size: medium;">RedisGraph 在 Redis 上實現了一種高效能記憶體圖資料庫。該專案作為 Redis 模組開源提供,支援 openCyper 查詢語言,可完成圖的建立、查詢、條件匹配等操作。為支援高效的圖搜尋操作,RedisGraph 底層實現了一種稱為 Hexastore 的三元組儲存結構。</span></p><span></span><span><a name='more'></a></span><p><span style="font-family: arial; font-size: x-large;">RedisGraph 的核心觀念</span></p><p><span style="font-family: arial; font-size: medium;">不同的圖資料庫,可能採用了不同的結構表示圖。有的使用了鄰接列表,也有的使用了鄰接矩陣。對於 RedisGraph 而言,關鍵在於給出一種支援對圖做快速搜尋的資料結構,其使用了一種稱為“Hexastore”的結構儲存圖中的所有關係。</span></p><p><span style="font-family: arial; font-size: medium;">Hexastore 對圖的表示:</span></p><p><span style="font-family: arial; font-size: medium;">Hexastore 的結構由一系列三元組組成。其中,每個三元組包括如下三部分:</span></p><p><span style="font-family: arial; font-size: medium;">主語(Subject);</span></p><p><span style="font-family: arial; font-size: medium;">謂詞(Predicate);</span></p><p><span style="font-family: arial; font-size: medium;">目標(Object)。</span></p><p><span style="font-family: arial; font-size: medium;">主語表示源節點,謂詞表示關係,目標表示目的節點。對於圖中的每條關係,Hexastore 將儲存源節點、關係邊和目的節點間。以下面這條關係為例:</span></p><p><span style="font-family: arial; font-size: medium;">(NodeA)-[Connect]->(NodeB) </span></p><p><span style="font-family: arial; font-size: medium;">其中, NodeA是源節點,Connect是關係,NodeB是目標節點。</span></p><p><span style="font-family: arial; font-size: medium;">一旦構建了 Hexastore,我們可以輕易地實現圖搜尋。我們在下一個段落介紹圖查詢語言 Cypher</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: x-large;">啟動RedisGraph </span></p><p><span style="font-family: arial; font-size: medium;">我們可以用Docker直接啟動或是使用Docker Compose</span></p><p><span style="font-family: arial; font-size: medium;">Docker</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">docker run -p 6379:6379 -it --rm redislabs/redisgraph<br /></span></span></pre></h3><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;">Docker-Compose.yml (包含 Redisinsight)</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span><span style="font-size: 15.84px; font-weight: 400;">version: "3.3"
services:
demo-service-redis:
image: redislabs/redisgraph
restart: always
ports:
- 6379:6379
networks:
- demo-net
redisinsight:
image: redislabs/redisinsight
depends_on: [demo-service-redis]
ports:
- 8001:8001
networks:
- demo-net
networks:
demo-net:</span></span></span>
</pre><div><br /></div></h3><p><span style="font-family: arial; font-size: medium;">啟動後,attach到啟動的Container上,便可使用redis-cli 方便進行測試:</span></p><p><span style="font-size: medium;"><span style="font-family: arial;">$ docker exec -it 7f783929ee62 bash<br /></span><span style="font-family: arial;">root@7f783929ee62:/data# redis-cli<br /></span><span style="font-family: arial;">127.0.0.1:6379></span></span></p><p><span style="font-family: arial; font-size: medium;">RedisGraph官網有一些簡單例子可以說明:</span></p><p><span style="font-family: arial; font-size: medium;">建立新的圖資料庫與其關聯資料</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">127.0.0.1:6379> GRAPH.QUERY Topo "CREATE (sc:Command {name:'App1'})-[c:Connect]->(dc:Command {name:'App2'}), (sc:Command {name:'App1'})-[c:Connect]->(dc:Command {name:'App3'})"
1) 1) "Labels added: 1"
2) "Nodes created: 4"
3) "Properties set: 4"
4) "Relationships created: 2"
5) "Cached execution: 0"
6) "Query internal execution time: 0.386266 milliseconds"
</span></span></pre><div><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;"><br /></span></span></div></h3><p><span style="font-family: arial; font-size: medium;"><span style="font-size: x-large;">圖查詢語言 Cypher</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;">RedisGraph支援圖查詢語言 Cypher,以上述建立的範例來</span><span style="font-family: arial;">查詢目的端點的Command.name為'App3',如下:</span></span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">127.0.0.1:6379> GRAPH.QUERY Topo "MATCH (sc:Command)-[c:Connect]->(dc:Command) WHERE dc.name = 'App3' RETURN sc.name, dc.name"
1) 1) "sc.name"
2) "dc.name"
2) 1) 1) "App1"
2) "App3"
3) 1) "Cached execution: 0"
2) "Query internal execution time: 0.484505 milliseconds"</span></span></pre></h3><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br /></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"></span></p><h3><p><span style="color: #666666; font-family: arial; font-size: large;"><i>P.S: 使用Redisinsight</i></span></p><p><span style="color: #999999; font-family: arial; font-size: medium;"><i>可以使用Redisinsight來連接資料庫並且檢視資料庫。</i></span></p><p><span style="color: #999999; font-family: arial; font-size: medium;"><i>操作方式</i></span></p><p><span style="color: #999999; font-family: arial; font-size: medium;"><i>Step 1: 開啟redisinsight</i></span></p><p><i><span style="color: #999999; font-family: arial; font-size: medium;"></span></i></p><p><span style="color: #999999; font-family: arial; font-size: medium;"><i>http://<<your server IP>>:8001/</i></span></p><p><span style="color: #999999; font-family: arial; font-size: medium;"><i>Step 2: 連線DB</i></span></p><p><span style="color: #999999; font-family: arial; font-size: medium;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="color: #999999; font-family: arial; font-size: medium;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjNRHAMNZdqkFglV0Kll45YTvsdr-sIuH8Irz6DSZ-XVffEDjE7TcVYEx8kvqSEH8npjLFyCC0XIiRb6H3p49aGDG6CNKjb-uFi9vLMEXjAynPBmjTDgA4RrQRK5ZMEEa7VGDIS-BM69jiN3ViF7CT7MBptKdNmoi56c6HBEluXL38cburc3aIdsyY" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="854" data-original-width="1765" height="310" src="https://blogger.googleusercontent.com/img/a/AVvXsEjNRHAMNZdqkFglV0Kll45YTvsdr-sIuH8Irz6DSZ-XVffEDjE7TcVYEx8kvqSEH8npjLFyCC0XIiRb6H3p49aGDG6CNKjb-uFi9vLMEXjAynPBmjTDgA4RrQRK5ZMEEa7VGDIS-BM69jiN3ViF7CT7MBptKdNmoi56c6HBEluXL38cburc3aIdsyY=w640-h310" width="640" /></a></span></div><span style="color: #999999; font-family: arial; font-size: medium;"><br /></span><p></p><p><span style="color: #999999; font-family: arial; font-size: medium;"><i>填入你的主機位置,資料庫名稱設定為建立好的DB</i></span></p><p><span style="color: #999999;"><span style="font-family: arial; font-size: medium;"></span></span></p><div class="separator" style="clear: both; text-align: center;"><span style="color: #999999;"><span style="font-family: arial; font-size: medium;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEid4y4OgZyAj3LaZBNFaO0Iv7PqXmWWmQzqNZCQhzbU3zIhxRRSXYtAxO65sC3vZYeXZ4JmXUJTRgd2EBj0cuo_Rl4TqpSd1DdFqIOPcoM0whbf98AafirS-1RUF6Nslhn-wFZl7Rlxu8P1axElH7US4JAuQ-35QaMisnS47OaeKdTylNR9QurVSP4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="808" data-original-width="1184" height="436" src="https://blogger.googleusercontent.com/img/a/AVvXsEid4y4OgZyAj3LaZBNFaO0Iv7PqXmWWmQzqNZCQhzbU3zIhxRRSXYtAxO65sC3vZYeXZ4JmXUJTRgd2EBj0cuo_Rl4TqpSd1DdFqIOPcoM0whbf98AafirS-1RUF6Nslhn-wFZl7Rlxu8P1axElH7US4JAuQ-35QaMisnS47OaeKdTylNR9QurVSP4=w640-h436" width="640" /></a></span></span></div><span style="color: #999999;"><span style="font-family: arial; font-size: large;"><div class="separator" style="clear: both; text-align: center;"><br /></div></span></span><p></p><p><span style="font-family: arial; font-size: large;"><i><span style="color: #999999;">Step 3: 讀取資料</span></i></span></p><p><span style="font-family: arial; font-size: large;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi0l_34URf8wZU68b3CYdmR4gYqhfTH3Gb72K70PME2t3aphHkAg_laUVIwfanYjV2Mv-tYSVxWHhZTJvTPRARGXE10Xm_3jY6kCCY_ERsFDg1lbJKcDRNXKeESrucLyFjP900LrUrJkfpe-XXwO4zpO2pnKNKhBZKcurSO9UHn94XBeNJx4qQXXdE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="734" data-original-width="1122" height="418" src="https://blogger.googleusercontent.com/img/a/AVvXsEi0l_34URf8wZU68b3CYdmR4gYqhfTH3Gb72K70PME2t3aphHkAg_laUVIwfanYjV2Mv-tYSVxWHhZTJvTPRARGXE10Xm_3jY6kCCY_ERsFDg1lbJKcDRNXKeESrucLyFjP900LrUrJkfpe-XXwO4zpO2pnKNKhBZKcurSO9UHn94XBeNJx4qQXXdE=w640-h418" width="640" /></a></div><br /><p></p></h3><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br />Reference:</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><a href="https://iter01.com/80981.html">揭祕RedisGraph: Redis內嵌高效能記憶體圖資料庫</a></span></p><p><a href="https://redis.io/docs/stack/graph/"><span style="font-family: arial; font-size: medium;">RedisGraph</span></a></p><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-42564076367714884092022-05-06T21:22:00.005+08:002022-05-09T09:55:44.952+08:00[Elasticsearch] 佈署 Elasticsearch 到 K8S 遇到了 failed to obtain node locks 問題<p style="text-align: left;"><span style="font-family: arial; font-size: medium;">之前佈署 Elasticsearch 到 K8S 並使用 hostPath 的 Volume 遇到了 failed to obtain node locks 問題如下:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj4MV4oK8ZydEhStZ8wJhni1K_6TTUxVK_nbJMpIJqK2cSEkX3HXWZrZ2sPfM6I1Fay2g3QxFXAPCnLcxj-v-iUCgK-uCtfYprrw0OXqrEuP9sh8Az9LM7e5v-53T1f-ehIAsBAUCTFx3ry_cjuYFPCeeoqdZ2jhNTXMC9TS-sYLckBBNJm0cxRWnI" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="561" data-original-width="1046" height="344" src="https://blogger.googleusercontent.com/img/a/AVvXsEj4MV4oK8ZydEhStZ8wJhni1K_6TTUxVK_nbJMpIJqK2cSEkX3HXWZrZ2sPfM6I1Fay2g3QxFXAPCnLcxj-v-iUCgK-uCtfYprrw0OXqrEuP9sh8Az9LM7e5v-53T1f-ehIAsBAUCTFx3ry_cjuYFPCeeoqdZ2jhNTXMC9TS-sYLckBBNJm0cxRWnI=w640-h344" width="640" /></a></div><p style="text-align: left;"></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><span></span></span></p><a name='more'></a><span style="font-family: arial; font-size: medium;">看起來問題像是Elasticsearch 無法在 /usr/share/elasticsearch/data 下建立檔案。 上網查詢到下列紅色字體的方法可以解決權限問題:</span><p></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><i>如果Volume是使用 hostpath,則 initContainer可用chown 改變路徑的Volume:</i></span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;"><span style="color: #f8f8f2;">apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elasticsearch
spec:
serviceName: "elasticsearch"
replicas: 1
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
</span><span style="color: red;"> initContainers:
- name: set-permissions
image: registry.hub.docker.com/library/busybox:latest
command: ['sh', '-c', 'mkdir -p /usr/share/elasticsearch/data && chown 1000:1000 /usr/share/elasticsearch/data' ]
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data</span><span style="color: #f8f8f2;">
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:6.6.1
env:
- name: discovery.type
value: single-node
ports:
- containerPort: 9200
name: client
- containerPort: 9300
name: nodes
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
volumes:
- name: data
hostPath:
path: /indexdata
---
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
labels:
service: elasticsearch
spec:
ports:
- port: 9200
name: client
- port: 9300
name: nodes
type: NodePort
selector:
app: elasticsearch</span></span></span>
</pre><div><br /></div></h3><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">細部追查,看起來在主機上的 /indexdata 原本的權限是 root:root,但是啟動後變成了 danny:danny</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">應該就是紅色字體的 InitContainer啟動後做了下列動作( userid 1000 是 danny )<br /></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><span style="background-color: #272822; color: red;">mkdir -p /usr/share/elasticsearch/data && chown 1000:1000 /usr/share/elasticsearch/data</span> </span></p><p style="text-align: left;"><span style="font-size: medium;">Root Cause: 當 pod 中的容器以用戶身份運行root並且需要對已掛載卷的寫入權限時,這是必要的。</span></p><p><span style="font-family: arial; font-size: large;">延伸閱讀: </span><a href="https://localcoder.org/kubernetes-how-to-set-volumemount-user-group-and-file-permissions" style="font-family: arial; font-size: large;">Kubernetes: how to set VolumeMount user group and file permissions</a></p><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-31913159034204074572022-05-05T13:07:00.006+08:002022-05-06T20:39:11.439+08:00[網路科普] Linux cooked-mode capture (SLL)<p style="text-align: left;"><span style="font-family: arial; font-size: medium;">之前用GoPacket去抓device name 為 "any" device interface (Pseudo-device that captures on all interfaces),"any"可以透過 tcpdump -D 查看到:</span></p><p style="text-align: left;"></p><p style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgqHnig-BVG9czqxhhFGOKejjYOL9Lnc3K_5c3VS-KGv1c8B6n-9in5nuo8exX2aI6zOC_l1v98VkNzja2JJk0J5UuTDZPNuNkbaEJTUwBvtgVdnk4yI0N77Roba26SEogAtS729OZLlui8g2PVzW52QwERLRuR1fFTzd3MhDUMJM7QN5DGbwt-Bqk" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: arial; font-size: medium;"><img alt="" data-original-height="208" data-original-width="541" height="154" src="https://blogger.googleusercontent.com/img/a/AVvXsEgqHnig-BVG9czqxhhFGOKejjYOL9Lnc3K_5c3VS-KGv1c8B6n-9in5nuo8exX2aI6zOC_l1v98VkNzja2JJk0J5UuTDZPNuNkbaEJTUwBvtgVdnk4yI0N77Roba26SEogAtS729OZLlui8g2PVzW52QwERLRuR1fFTzd3MhDUMJM7QN5DGbwt-Bqk=w400-h154" width="400" /></span></a></p><p style="text-align: left;"></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">"any"這個device interface可以抓到經過所有interfaces的封包,部分程式碼如下:<span></span></span></p><a name='more'></a><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #6a9955;">// Open device</span></div><div> <span style="color: #9cdcfe;">handle</span>, <span style="color: #9cdcfe;">err</span> = pcap.<span style="color: #dcdcaa;">OpenLive</span>(<span style="color: #ce9178;">"andy"</span>, snapshot_len, promiscuous, timeout)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] Error : </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, device, err)</div><div> }</div></div><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-size: medium;"><span style="font-family: arial;">但,突然發現抓到的封包,link-type 都是 LINUX_SLL,這就讓我起了滿頭黑人問號...。</span><span style="font-family: arial;">同樣使用tcpdump去抓"any" device interface,也會有相同的結果:</span></span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;">$ sudo tcpdump -i any -w test.pcap tcp
tcpdump: listening on </span><span style="font-size: 15.84px;"><span style="color: #01ffff;">any</span></span><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;">, </span><span style="font-size: 15.84px;"><span style="color: #01ffff;">link-type LINUX_SLL (Linux cooked v1)</span></span><span style="color: #f8f8f2; font-size: 15.84px; font-weight: 400;">, capture size 262144 bytes</span></span></pre></h3><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br /></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><a href="https://zhuanlan.zhihu.com/p/268905035">經過survey</a>,有兩種可能的情形:</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">1. 數據包從“any”設備進行捕獲(即tcpdump -i any,Pseudo-device),因為不是所有接口都具有相同的鏈路層類型。</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">2. 數據包從鏈路層頭部不可用或不能使用的設備上進行捕獲(譬如Linux PPP),因為Linux PPP代碼不能可靠地向libpcap提供PPP報頭。</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">Wiki上對於<a href="https://wiki.wireshark.org/SLL">SLL</a>的解釋也非常清楚:</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><span style="background-color: white; color: #333333;">This is the pseudo-protocol used by libpcap on Linux to capture from the "any" device and to capture on some devices where the native link layer header isn't available or can't be used. <br /></span></span><span style="background-color: white; color: #333333; font-family: arial; font-size: large;">...<br /></span><span style="background-color: white; color: #333333; font-family: arial; font-size: large;">...<br /></span><span style="background-color: white; color: #333333; font-family: arial; font-size: large;">When capturing from the "any" device, or from one of those other devices, in Linux, the libpcap doesn't supply the link-layer header for the real "hardware protocol" like </span><a href="https://wiki.wireshark.org/Ethernet" style="background-color: white; box-sizing: border-box; color: #4183c4; font-family: arial; font-size: large; margin: 0px; outline: 0px; padding: 0px;">Ethernet</a><span style="background-color: white; color: #333333; font-family: arial; font-size: large;">, but instead supplies </span><a href="https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html" style="background-color: white; box-sizing: border-box; color: #4183c4; font-family: arial; font-size: large; margin: 0px; padding: 0px; text-decoration-line: none;">a fake link-layer header for this pseudo-protocol</a><span style="background-color: white; color: #333333; font-family: arial; font-size: large;">.</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;">在這篇文章中有提到如何做轉換: <br /><a href="https://www.cnblogs.com/aios/p/9545378.html">Linux cooked-mode capture 格式转换</a></span></p><p><span style="font-family: arial; font-size: medium;">若需要將linux cooked capture格式的包轉換為Ethernet格式,有那麼幾種方法:</span></p><p><span style="font-family: arial; font-size: medium;">1. 寫代碼讀出每一個包後再改寫到新文件(使用libpcap或者基於pcap頭部結構體偏移);</span></p><p><span style="font-family: arial; font-size: medium;">2. tcpdump 3.0+ 版本下,可以用tcprewrite直接改寫,這應該是最快捷的方法;</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ tcpdump -r linux_sll.pcap從文件 linux_sll.pcap //讀取,鏈接類型 LINUX_SLL(Linux 熟)
$ tcprewrite --dlt=enet --infile=linux_sll.pcap --outfile=enet.pcap
$ tcpdump - r enet.pcap //從文件 enet.pcap 中讀取,鏈接類型 EN10MB(以太網)<br /></span></span></pre></h3><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">也可以用 named pipe 來避免寫入到Disk</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px; font-weight: 400;">$ sudo rm /tmp/linux_sll.pcap
$ sudo rm /tmp/enet.pcap
$ mkfifo /tmp/linux_sll.pcap
$ mkfifo /tmp/enet.pcap
$ sudo tcpdump -s 0 -n -w - -U -i any > /tmp/linux_sll.pcap
$ tcprewrite --dlt=enet --infile=/tmp/linux_sll.pcap --outfile=/tmp/enet.pcap
$ tcpdump -r /tmp/enet.pcap</span></span></pre></h3><p><br /></p><p><br /></p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><br /><p></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-70269923534252594042022-05-03T14:33:00.005+08:002022-05-05T10:40:08.400+08:00[Golang] 用 Golang 使用 Raw Socket v.s. libpcap 兩者優劣比較<p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;">前陣子花了一些時間在找一種方式,可以透過寫程式的方式抓取進出於任何device interfaces on Linux主機上的封包,目前主要是有這兩大作法: </span><span style="font-family: arial;">Raw Socket v.s. libpcap<span></span></span></span></p><a name='more'></a><p></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">Raw Socket:</span></p><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;">可用於多種用途,例如發送和接收原始 IPv4 數據包而不必擔心鏈路層(即,它們插入 IP 層而不是網絡設備驅動程序)。如果需要訪問原始鏈接層,大多數操作系統上的</span><span style="font-family: arial;">Raw Socket</span><span style="font-family: arial;">不支持(Linux 是明顯的例外)。</span></span></p><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;">Golang 使用 Raw Socket開發,原則上不需安裝其他套件,是透過system call方式開啟 Raw Socket,它的domain 可以使用AF_INET (Layer 3) 與 AF_PACKET (Layer 2),但AF_INET對於 outgoing packets 會抓不到,所以需使用</span><span style="font-family: arial;">AF_PACKET回比較完整。</span></span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">可以透過interface上的IP來綁定特定interface,部分範例如下:</span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;">// AF_INET can't capture outgoing packets, must change to use AF_PACKET</span></div><div> <span style="color: #6a9955;">// https://github.com/golang/go/issues/7653</span></div><div> <span style="color: #6a9955;">// http://www.binarytides.com/packet-sniffer-code-in-c-using-linux-sockets-bsd-part-2/</span></div><div> <span style="color: #9cdcfe;">proto</span> := (syscall.ETH_P_ALL<<<span style="color: #b5cea8;">8</span>)&<span style="color: #b5cea8;">0xff00</span> | syscall.ETH_P_ALL>><span style="color: #b5cea8;">8</span> <span style="color: #6a9955;">// change to Big-Endian order</span></div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"[DEBUG] proto: "</span>, proto)</div><div> <span style="color: #9cdcfe;">fd</span>, <span style="color: #9cdcfe;">err</span> := syscall.<span style="color: #dcdcaa;">Socket</span>(syscall.AF_PACKET, syscall.SOCK_RAW, proto)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatal</span>(<span style="color: #ce9178;">"socket: "</span>, err)</div><div> }</div><div> <span style="color: #c586c0;">defer</span> syscall.<span style="color: #dcdcaa;">Close</span>(fd)</div><div> <span style="color: #c586c0;">if</span> t.addr != <span style="color: #ce9178;">""</span> && t.addr != <span style="color: #ce9178;">"0.0.0.0"</span> {</div><div> <span style="color: #9cdcfe;">ifi</span>, <span style="color: #9cdcfe;">err</span> := net.<span style="color: #dcdcaa;">InterfaceByName</span>(t.addr)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatal</span>(t.addr, <span style="color: #ce9178;">" interfacebyname: "</span>, err)</div><div> }</div><div> <span style="color: #9cdcfe;">lla</span> := syscall.SockaddrLinklayer{Protocol: <span style="color: #dcdcaa;">uint16</span>(proto), Ifindex: ifi.Index}</div><div> <span style="color: #c586c0;">if</span> <span style="color: #9cdcfe;">err</span> := syscall.<span style="color: #dcdcaa;">Bind</span>(fd, &lla); err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatal</span>(<span style="color: #ce9178;">"bind: "</span>, err)</div><div> }</div><div> }</div></div><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;">libpcap:</span></p><p><span style="font-family: arial; font-size: medium;">libpcap 在不同的操作系統上使用不同的機制。在 Linux 上,它使用 PF_PACKET Raw或Cooked Socket,這取決於它是否知道接口的 Linux 鏈路層類型(ARPHRD_ 值)以及該鏈路層類型的接口是否產生有用的鏈路層標頭。</span></p><p><span style="font-size: medium;"><span style="font-family: arial;">Golang使用libpcap開發,在需要先安裝 libpcap-dev 套件,在Golang下有一強大的package : gopacket,其使用libpcap函示庫擷取封包,對上提供大量的封包處理的功能,並且還可以類似於tcpdump一樣設定過濾條件,</span><span style="font-family: arial;">部分範例如下:</span></span></p><p><span style="font-family: arial; font-size: medium;"></span></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div> <span style="color: #6a9955;">// Open device</span></div><div> <span style="color: #9cdcfe;">handle</span>, <span style="color: #9cdcfe;">err</span> = pcap.<span style="color: #dcdcaa;">OpenLive</span>(device, snapshot_len, promiscuous, timeout)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] Error : </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, device, err)</div><div> }</div><br /><div> <span style="color: #6a9955;">// Set BPF Filter</span></div><div> <span style="color: #c586c0;">if</span> <span style="color: #9cdcfe;">err</span> := handle.<span style="color: #dcdcaa;">SetBPFFilter</span>(<span style="color: #ce9178;">"not port 22"</span>); err != <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #dcdcaa;">panic</span>(err)</div><div> }</div><br /><div> <span style="color: #c586c0;">defer</span> handle.<span style="color: #dcdcaa;">Close</span>()</div><div> <span style="color: #6a9955;">// Use the handle as a packet source to process all packets</span></div><div> <span style="color: #9cdcfe;">packetSource</span> := gopacket.<span style="color: #dcdcaa;">NewPacketSource</span>(handle, handle.<span style="color: #dcdcaa;">LinkType</span>())</div><div> <span style="color: #9cdcfe;">packets</span> := packetSource.<span style="color: #dcdcaa;">Packets</span>()</div><div> <span style="color: #9cdcfe;">timer</span> := time.<span style="color: #dcdcaa;">NewTimer</span>(time.<span style="color: #dcdcaa;">Duration</span>(timerclick) * time.Second)</div><br /><div> <span style="color: #c586c0;">for</span> {</div><div> <span style="color: #c586c0;">select</span> {</div><div> <span style="color: #c586c0;">case</span> <span style="color: #9cdcfe;">packet</span> := <-packets:</div><div> ...</div></div><p><span style="font-family: arial; font-size: medium;">P.S: Gopacket相關參考文件如下:</span></p><p><span style="font-family: arial; font-size: medium;"><a href="https://www.devdungeon.com/content/packet-capture-injection-and-analysis-gopacket#write-pcap-file">使用 Gopacket 進行數據包捕獲、注入和分析</a></span></p><p><span style="font-family: arial; font-size: medium;"><a href="https://iter01.com/592052.html">網路流量抓包庫 gopacket</a></span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;">為什麼使用libpcap會比較好?</span></p><p style="background-color: white; border: 0px; box-sizing: inherit; clear: both; color: #232629; font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; line-height: inherit; margin-bottom: var(--s-prose-spacing); margin-left: 0px; margin-right: 0px; margin-top: 0px; padding: 0px; vertical-align: baseline;"><span style="box-sizing: inherit; font-family: arial; font-size: medium; vertical-align: inherit;">三個原因:</span></p><p style="background-color: white; border: 0px; box-sizing: inherit; clear: both; color: #232629; font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; line-height: inherit; margin-bottom: var(--s-prose-spacing); margin-left: 0px; margin-right: 0px; margin-top: 0px; padding: 0px; vertical-align: baseline;"><span style="box-sizing: inherit; font-family: arial; font-size: medium; vertical-align: inherit;">1)正確設置更容易。</span></p><p style="background-color: white; border: 0px; box-sizing: inherit; clear: both; color: #232629; font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; line-height: inherit; margin-bottom: var(--s-prose-spacing); margin-left: 0px; margin-right: 0px; margin-top: 0px; padding: 0px; vertical-align: baseline;"><span style="box-sizing: inherit; font-family: arial; font-size: medium; vertical-align: inherit;">2) 它是可移植的,甚至可以移植到 Windows,它使用非常相似但不同的套接字 API。</span></p><p style="background-color: white; border: 0px; box-sizing: inherit; clear: both; color: #232629; font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; line-height: inherit; margin-bottom: var(--s-prose-spacing); margin-left: 0px; margin-right: 0px; margin-top: 0px; padding: 0px; vertical-align: baseline;"><span style="box-sizing: inherit; font-family: arial; font-size: medium; vertical-align: inherit;">3)它快得多。</span></p><p style="background-color: white; border: 0px; box-sizing: inherit; clear: both; color: #232629; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Adjusted", "Segoe UI", "Liberation Sans", sans-serif; font-size: 15px; font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; line-height: inherit; margin-bottom: var(--s-prose-spacing); margin-left: 0px; margin-right: 0px; margin-top: 0px; padding: 0px; vertical-align: baseline;"><br /></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">Reference:</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"></span></p><p style="background-color: white; border: 0px; box-sizing: inherit; color: #232629; flex: 1 1 auto; font-stretch: inherit; font-variant-east-asian: inherit; font-variant-numeric: inherit; font-weight: inherit; line-height: 1.3; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-wrap: break-word; padding: 0px; text-align: left; vertical-align: baseline;"><span style="font-family: arial; font-size: medium;"><a class="question-hyperlink" href="https://stackoverflow.com/questions/7856509/does-libpcap-use-raw-sockets-underneath-them" style="border: 0px; box-sizing: inherit; cursor: pointer; font-family: var(--theme-post-title-font-family); font-size: var(--fs-headline1); font-stretch: inherit; font-style: inherit; font-variant: inherit; line-height: 1.35; margin: 0px; padding: 0px; text-decoration-line: none; user-select: auto; vertical-align: baseline;">Does libpcap use raw sockets underneath them?<br /></a><a class="question-hyperlink" href="https://stackoverflow.com/questions/60736230/why-libpcap-is-better-than-sniffing-with-raw" style="border: 0px; box-sizing: inherit; cursor: pointer; font-family: var(--theme-post-title-font-family); font-size: var(--fs-headline1); font-stretch: inherit; font-style: inherit; font-variant: inherit; line-height: 1.35; margin: 0px; padding: 0px; text-decoration-line: none; user-select: auto; vertical-align: baseline;">Why libpcap is better than sniffing with raw?</a></span></p><div><br /></div>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-78656466005198528202022-05-02T11:38:00.006+08:002022-05-02T13:34:09.722+08:00[Golang] 檢查IP 是否在特定範圍內的簡單範例<p><span style="font-family: arial; font-size: medium;">檢查IP 是否在特定範圍內,的最快方法是什麼?例如,給定範圍192.168.1.1至192.168.10.254,如何檢查給定的輸入IP是否在該範圍內?以下是簡單範例:</span><span></span></p><a name='more'></a><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">package</span> main</div><br /><div><span style="color: #569cd6;">import</span> (</div><div> <span style="color: #ce9178;">"bytes"</span></div><div> <span style="color: #ce9178;">"fmt"</span></div><div> <span style="color: #ce9178;">"net"</span></div><div>)</div><br /><div><span style="color: #569cd6;">var</span> (</div><div> <span style="color: #9cdcfe;">ip1</span> = net.<span style="color: #dcdcaa;">ParseIP</span>(<span style="color: #ce9178;">"192.168.1.1"</span>)</div><div> <span style="color: #9cdcfe;">ip2</span> = net.<span style="color: #dcdcaa;">ParseIP</span>(<span style="color: #ce9178;">"192.168.10.254"</span>)</div><div>)</div><br /><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">check</span>(ip <span style="color: #4ec9b0;">string</span>) <span style="color: #4ec9b0;">bool</span> {</div><div> <span style="color: #9cdcfe;">trial</span> := net.<span style="color: #dcdcaa;">ParseIP</span>(ip)</div><div> <span style="color: #c586c0;">if</span> trial.<span style="color: #dcdcaa;">To4</span>() == <span style="color: #569cd6;">nil</span> {</div><div> fmt.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;"> is not an IPv4 address</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, trial)</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">false</span></div><div> }</div><div> <span style="color: #c586c0;">if</span> bytes.<span style="color: #dcdcaa;">Compare</span>(trial, ip1) >= <span style="color: #b5cea8;">0</span> && bytes.<span style="color: #dcdcaa;">Compare</span>(trial, ip2) <= <span style="color: #b5cea8;">0</span> {</div><div> fmt.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;"> is between </span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;"> and </span><span style="color: #9cdcfe;">%v</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, trial, ip1, ip2)</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">true</span></div><div> }</div><div> fmt.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;"> is NOT between </span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;"> and </span><span style="color: #9cdcfe;">%v</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">"</span>, trial, ip1, ip2)</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">false</span></div><div>}</div><br /><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">main</span>() {</div><div> <span style="color: #dcdcaa;">check</span>(<span style="color: #ce9178;">"1.2.3.4"</span>)</div><div> <span style="color: #dcdcaa;">check</span>(<span style="color: #ce9178;">"216.14.49.185"</span>)</div><div> <span style="color: #dcdcaa;">check</span>(<span style="color: #ce9178;">"192.168.3.3"</span>)</div><div> <span style="color: #dcdcaa;">check</span>(<span style="color: #ce9178;">"1::16"</span>)</div><div>}</div><br /></div><p><span style="font-family: arial; font-size: medium;">Result:</span></p><div style="background-color: #1e1e1e; line-height: 19px;"><span style="color: #569cd6; font-family: Consolas, Courier New, monospace;"><span style="font-size: 14px; white-space: pre;">1.2.3.4 is NOT between 192.168.1.1 and 192.168.10.254
216.14.49.185 is NOT between 192.168.1.1 and 192.168.10.254
192.168.3.3 is between 192.168.1.1 and 192.168.10.254
1::16 is not an IPv4 address</span></span><span style="color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; white-space: pre;">
</span></div><div><br /></div><p><a href="https://stackoverflow.com/questions/19882961/go-golang-check-ip-address-in-range">https://stackoverflow.com/questions/19882961/go-golang-check-ip-address-in-range</a></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-82587380095706002932022-04-20T16:55:00.007+08:002022-04-20T16:56:41.447+08:00[Golang] 一個簡單範例使用gopacket去解析LLDP封包內容<p><span style="font-family: arial; font-size: medium;">因為工作上需要查看主機上所收到任何的LLDP封包,故在網上搜尋了一下資料與範例程式碼,組成下列一個簡單範例。</span></p><p><span style="font-family: arial; font-size: medium;">本程式會找出所有在此本機上的Net Devices ( Interface ),並依序收集封包,一旦發現有LLDP則會列印相關資訊在console上。<span></span></span></p><a name='more'></a><p></p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">package</span> main</div><br /><div><span style="color: #569cd6;">import</span> (</div><div> <span style="color: #ce9178;">"fmt"</span></div><div> <span style="color: #ce9178;">"log"</span></div><div> <span style="color: #ce9178;">"net"</span></div><div> <span style="color: #ce9178;">"strings"</span></div><div> <span style="color: #ce9178;">"time"</span></div><br /><div> <span style="color: #ce9178;">"github.com/google/gopacket"</span></div><div> <span style="color: #ce9178;">"github.com/google/gopacket/layers"</span></div><div> <span style="color: #ce9178;">"github.com/google/gopacket/pcap"</span></div><div>)</div><br /><div><span style="color: #569cd6;">var</span> (</div><div> <span style="color: #9cdcfe;">snapshot_len</span> <span style="color: #4ec9b0;">int32</span> = <span style="color: #b5cea8;">1024</span></div><div> <span style="color: #9cdcfe;">promiscuous</span> <span style="color: #4ec9b0;">bool</span> = <span style="color: #569cd6;">true</span></div><div> <span style="color: #9cdcfe;">err</span> <span style="color: #4ec9b0;">error</span></div><div> <span style="color: #9cdcfe;">timeout</span> time.Duration = <span style="color: #b5cea8;">1</span> * time.Second</div><div> <span style="color: #9cdcfe;">timerclick</span> <span style="color: #4ec9b0;">int</span> = <span style="color: #b5cea8;">30</span></div><div> <span style="color: #9cdcfe;">handle</span> *pcap.Handle</div><div> <span style="color: #9cdcfe;">nottododevices</span> = []<span style="color: #4ec9b0;">string</span>{<span style="color: #ce9178;">"virbr"</span>, <span style="color: #ce9178;">"docker"</span>, <span style="color: #ce9178;">"any"</span>, <span style="color: #ce9178;">"lo"</span>, <span style="color: #ce9178;">"usbmon"</span>}</div><div>)</div><br /><div><span style="color: #6a9955;">// contains checks if a string is present in a slice</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">contains</span>(s []<span style="color: #4ec9b0;">string</span>, str <span style="color: #4ec9b0;">string</span>) <span style="color: #4ec9b0;">bool</span> {</div><div> <span style="color: #c586c0;">for</span> <span style="color: #9cdcfe;">_</span>, <span style="color: #9cdcfe;">v</span> := <span style="color: #c586c0;">range</span> s {</div><div> <span style="color: #c586c0;">if</span> strings.<span style="color: #dcdcaa;">Contains</span>(str, v) {</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">true</span></div><div> }</div><div> }</div><br /><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">false</span></div><div>}</div><br /><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">main</span>() {</div><div> <span style="color: #6a9955;">// Get all devices</span></div><div> <span style="color: #9cdcfe;">devices</span>, <span style="color: #9cdcfe;">err</span> := pcap.<span style="color: #dcdcaa;">FindAllDevs</span>()</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Fatal</span>(err)</div><div> }</div><div> <span style="color: #6a9955;">// Print the device information</span></div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"Devices found:"</span>)</div><div> <span style="color: #c586c0;">for</span> <span style="color: #9cdcfe;">_</span>, <span style="color: #9cdcfe;">device</span> := <span style="color: #c586c0;">range</span> devices {</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">Name: "</span>, device.Name)</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"Description: "</span>, device.Description)</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"Devices addresses: "</span>, device.Description)</div><div> <span style="color: #c586c0;">for</span> <span style="color: #9cdcfe;">_</span>, <span style="color: #9cdcfe;">address</span> := <span style="color: #c586c0;">range</span> device.Addresses {</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"- IP address: "</span>, address.IP)</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"- Subnet mask: "</span>, address.Netmask)</div><div> }</div><div> <span style="color: #6a9955;">// Get LLDP Packets</span></div><div> <span style="color: #c586c0;">if</span> !<span style="color: #dcdcaa;">contains</span>(nottododevices, device.Name) {</div><div> fmt.<span style="color: #dcdcaa;">Println</span>(<span style="color: #ce9178;">"Get LLDP..."</span>)</div><div> <span style="color: #dcdcaa;">ReadLLDPInfo</span>(device.Name, snapshot_len, promiscuous, timeout)</div><div> }</div><div> }</div><div>}</div><br /><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">ReadLLDPInfo</span>(device <span style="color: #4ec9b0;">string</span>, snapshot_len <span style="color: #4ec9b0;">int32</span>, promiscuous <span style="color: #4ec9b0;">bool</span>, timeout time.Duration) {</div><br /><div> <span style="color: #6a9955;">// Open device</span></div><div> <span style="color: #9cdcfe;">handle</span>, <span style="color: #9cdcfe;">err</span> = pcap.<span style="color: #dcdcaa;">OpenLive</span>(device, snapshot_len, promiscuous, timeout)</div><div> <span style="color: #c586c0;">if</span> err != <span style="color: #569cd6;">nil</span> {</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] Error : </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, device, err)</div><div> }</div><div> <span style="color: #c586c0;">defer</span> handle.<span style="color: #dcdcaa;">Close</span>()</div><div> <span style="color: #6a9955;">// Use the handle as a packet source to process all packets</span></div><div> <span style="color: #9cdcfe;">packetSource</span> := gopacket.<span style="color: #dcdcaa;">NewPacketSource</span>(handle, handle.<span style="color: #dcdcaa;">LinkType</span>())</div><div> <span style="color: #9cdcfe;">packets</span> := packetSource.<span style="color: #dcdcaa;">Packets</span>()</div><div> <span style="color: #9cdcfe;">timer</span> := time.<span style="color: #dcdcaa;">NewTimer</span>(time.<span style="color: #dcdcaa;">Duration</span>(timerclick) * time.Second)</div><br /><div> <span style="color: #c586c0;">for</span> {</div><div> <span style="color: #c586c0;">select</span> {</div><div> <span style="color: #c586c0;">case</span> <span style="color: #9cdcfe;">packet</span> := <-packets:</div><div> <span style="color: #9cdcfe;">lldpLayerInfos</span> := packet.<span style="color: #dcdcaa;">Layer</span>(layers.LayerTypeLinkLayerDiscoveryInfo)</div><div> <span style="color: #c586c0;">if</span> lldpLayerInfos == <span style="color: #569cd6;">nil</span> {</div><div> <span style="color: #c586c0;">continue</span></div><div> }</div><br /><div> <span style="color: #9cdcfe;">lldpInfos</span> := lldpLayerInfos.(*layers.LinkLayerDiscoveryInfo)</div><div> <span style="color: #c586c0;">if</span> lldpInfos.PortDescription == device {</div><div> <span style="color: #c586c0;">continue</span></div><div> }</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] ************************************************"</span>, device)</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] PortDescription : </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, device, lldpInfos.PortDescription)</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] SysName : </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, device, lldpInfos.SysName)</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] SysDescription : </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, device, lldpInfos.SysDescription)</div><div> <span style="color: #569cd6;">var</span> <span style="color: #9cdcfe;">mgmtAddress</span> <span style="color: #4ec9b0;">string</span></div><div> <span style="color: #c586c0;">switch</span> lldpInfos.MgmtAddress.Subtype {</div><div> <span style="color: #c586c0;">case</span> layers.IANAAddressFamilyIPV4:</div><div> <span style="color: #9cdcfe;">mgmtAddress</span> = net.<span style="color: #dcdcaa;">IP</span>(lldpInfos.MgmtAddress.Address).<span style="color: #dcdcaa;">String</span>()</div><div> <span style="color: #c586c0;">default</span>:</div><div> <span style="color: #9cdcfe;">mgmtAddress</span> = <span style="color: #dcdcaa;">string</span>(lldpInfos.MgmtAddress.Address)</div><div> }</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] MgmtAddress : </span><span style="color: #9cdcfe;">%s</span><span style="color: #ce9178;">"</span>, device, mgmtAddress)</div><br /><div> <span style="color: #c586c0;">case</span> <-timer.C:</div><div> log.<span style="color: #dcdcaa;">Printf</span>(<span style="color: #ce9178;">"[</span><span style="color: #9cdcfe;">%v</span><span style="color: #ce9178;">] Time's up for </span><span style="color: #9cdcfe;">%d</span><span style="color: #ce9178;">"</span>, device, timerclick)</div><div> timer.<span style="color: #dcdcaa;">Stop</span>()</div><div> <span style="color: #c586c0;">return</span></div><div> }</div><div> }</div><div>}</div></div><p><span style="font-family: arial;"></span></p><p style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;">執行結果:</p><div><span><span><pre class="Playground-output" style="background: rgb(247, 249, 250); border-radius: 0px; box-sizing: border-box; line-height: 1.4; margin-bottom: 0px; margin-top: 0px; overflow-x: auto; padding: 0px;"><span style="font-family: arial;"><span style="color: #202224;">Devices found:
Name: enp4s0
Description:
Devices addresses:
- IP address: 140.96.27.24
- Subnet mask: ffffff00
- IP address: fe80::5ded:b7e0:45dc:b29c
- Subnet mask: ffffffffffffffff0000000000000000
Get LLDP...
2022/04/19 13:42:11 [enp4s0] ************************************************
2022/04/19 13:42:11 </span><b><span style="color: #800180;">[enp4s0] PortDescription : Gi1/0/14</span></b><span style="color: #202224;">
2022/04/19 13:42:11<b> </b></span><b><span style="color: #800180;">[enp4s0] SysName : n2048apm0</span></b><span style="color: #202224;">
2022/04/19 13:42:11 [enp4s0] SysDescription :
2022/04/19 13:42:11 [enp4s0] MgmtAddress :
2022/04/19 13:42:35 [enp4s0] Time's up for 30
Name: any
Description: Pseudo-device that captures on all interfaces
Devices addresses: Pseudo-device that captures on all interfaces
Name: lo
Description:
Devices addresses:
- IP address: 127.0.0.1
- Subnet mask: ff000000
- IP address: ::1
- Subnet mask: ffffffffffffffffffffffffffffffff</span></span><span style="color: #202224; font-size: large;">
</span></pre><div style="font-size: large;"><br /></div></span></span></div><p><span style="font-family: arial; font-size: large;"><b>參考資料:</b></span></p><p><span style="font-family: arial; font-size: large;"></span></p><p style="background-color: #fefefe; box-sizing: border-box; color: #333333; font-family: "Microsoft JhengHei", monospace; font-weight: 500; line-height: 1.1; margin: 20px 0px 10px; text-align: left;"><span style="font-size: medium;"><a href="https://iter01.com/592052.html">網路流量抓包庫 gopacket</a></span></p><p style="background-color: #fefefe; box-sizing: border-box; line-height: 1.1; margin: 20px 0px 10px; text-align: left;"><span style="color: #333333; font-family: Microsoft JhengHei, monospace; font-size: medium;"><a href="https://vimsky.com/zh-tw/examples/detail/golang-ex-github.com.google.gopacket---NewPacketSource-function.html">Golang gopacket.NewPacketSource函數代碼示例</a></span></p><p><a href="https://golangshowcase.com/question/how-to-use-decodelinklayerdiscovery-in-lldp-go"><span style="font-family: arial; font-size: medium;">How to use decodeLinkLayerDiscovery in lldp.go?</span></a></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #66d9ef; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;">for NIC in $(find /sys/class/net -type l -not -lname "*virtual*" -printf "%f\n" | sort); do
echo "NIC: ${NIC}"
echo "NIC MAC: $(ethtool -P ${NIC})"
timeout 300 tcpdump -nn -v -i ${NIC} -s 1500 -c 1 "ether[20:2] == 0x2000"
done</span></span></pre><p><span style="font-family: arial;"></span></p><p style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><br /></p><p><a href="http://www.panticz.de/identify-switch-port"><span style="font-family: arial; font-size: medium;">LLDP: Identify switch port to which the server is connected</span></a></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #66d9ef; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;">lldpLayerInfos := packet.Layer(layers.LayerTypeLinkLayerDiscoveryInfo)
if lldpLayerInfos == nil {
continue
}
lldpInfos := lldpLayerInfos.(*layers.LinkLayerDiscoveryInfo)
log.Printf("[%v] ************************************************", iface.Name)
log.Printf("[%v] PortDescription : %s", iface.Name, lldpInfos.PortDescription)
log.Printf("[%v] SysName : %s", iface.Name, lldpInfos.SysName)
log.Printf("[%v] SysDescription : %s", iface.Name, lldpInfos.SysDescription)
var mgmtAddress string
switch lldpInfos.MgmtAddress.Subtype {
case layers.IANAAddressFamilyIPV4:
mgmtAddress = net.IP(lldpInfos.MgmtAddress.Address).String()
default:
mgmtAddress = string(lldpInfos.MgmtAddress.Address)
}
log.Printf("[%v] MgmtAddress : %s", iface.Name, mgmtAddress)</span></span></pre><p><span style="font-family: arial;"></span></p><p style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><br /></p><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-56949210402404097132022-04-16T16:58:00.011+08:002023-04-30T15:19:46.090+08:00[CI/CD] 自建 DroneCI + Gitea + Docker Registry + Docker Registry Web 一次搞定<p><span style="font-family: arial; font-size: medium;">本篇將提供一個 Docker Compose YAML file 的範例,可產生 DroneCI + Gitea + Docker Registry + Docker Registry Web 的 CI/CD環境</span></p><p><span style="font-size: medium;"><span style="font-family: arial;">Gitea 是蠻多公司的選擇,所以我選用它。</span><span style="font-family: arial;">同時,在這個講求效率的開發時代,必須要搭配一個 CI/CD 的平台。Drone CI 這是一套開源的持續整合框架,可以幫工程師節省非常多上版的時間,從測試、包版、image化到部屬全部一條龍服務,而且非常容易上手,易於維護,因此我們選用作為平台。</span></span></p><p><span style="font-family: arial; font-size: medium;">另外,Docker Registry 這就不用再多說了,而方便查詢Registry的Web可以提供快速與便利的介面來查看在Registry內的Docker Images。</span></p><p><span style="font-family: arial; font-size: medium;">先整理一下 Port Mapping List:<span></span></span></p><a name='more'></a><p></p><p></p><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgwxZ1lkYNENABbMZwmcy3pe7hRta6z0E7DnqvPyMn5FS7ohpJZM804t-rR9xXeufwOUx0X4iuigBQAl7SLZ5naH4C94F77qPbjswCc14TInixGgSQt2BX92hLNUgb9Ofk5gLLrDXXgMgD3KrCVCNXASncVV9OXaAJ0UDV6H_bs5TYczxPliCcpW2o" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="218" data-original-width="397" height="220" src="https://blogger.googleusercontent.com/img/a/AVvXsEgwxZ1lkYNENABbMZwmcy3pe7hRta6z0E7DnqvPyMn5FS7ohpJZM804t-rR9xXeufwOUx0X4iuigBQAl7SLZ5naH4C94F77qPbjswCc14TInixGgSQt2BX92hLNUgb9Ofk5gLLrDXXgMgD3KrCVCNXASncVV9OXaAJ0UDV6H_bs5TYczxPliCcpW2o=w400-h220" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">上述的系統可以透過執行下列範例 docker-compose YAML file 即完成建置。</span></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">( 執行環境: VM with IP: 192.168.56.101 )<br />假設有個USB Disk:<br />$ sudo fdisk -l<br /><br /></span><span style="font-family: arial; font-size: large;">Disk /dev/sda: 465.8 GiB, 500107861504 bytes, 976773167 sectors<br /></span><span style="font-family: arial; font-size: large;">Disk model: Expansion<br /></span><span style="font-family: arial; font-size: large;">Units: sectors of 1 * 512 = 512 bytes<br /></span><span style="font-family: arial; font-size: large;">Sector size (logical/physical): 512 bytes / 512 bytes<br /></span><span style="font-family: arial; font-size: large;">I/O size (minimum/optimal): 512 bytes / 512 bytes<br /></span><span style="font-family: arial; font-size: large;">Disklabel type: dos<br /></span><span style="font-family: arial; font-size: large;">Disk identifier: 0x033ceea7</span></p><p><span style="font-family: arial; font-size: medium;">Device Boot Start End Sectors Size Id Type<br /></span><span style="font-family: arial; font-size: large;">/dev/sda1 2 976773166 976773165 465.8G 7 HPFS/NTFS/exFAT</span><span style="font-family: arial; font-size: medium;"> 位於 /dev/sda1</span><br /><br /><span style="font-family: arial; font-size: medium;">我們把它掛載在 /mnt/USB/registry<br />$ sudo mkdir /mnt/USB</span><br /><span style="font-family: arial; font-size: medium;">$ sudo mount /dev/sda1 /mnt/USB/</span></p><p><span style="font-family: arial; font-size: medium;"><br /><i><span style="color: #999999;">(</span></i></span><i><span style="color: #999999;"><span style="font-family: arial; font-size: large;">更改 </span><span style="font-family: arial; font-size: large;">Customize the storage location)<br /></span><span style="font-family: arial; font-size: large;">volumes:<br />- /mnt/USB/registry:/var/lib/registry</span></span></i></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-align: left; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span><span style="color: #66d9ef; font-size: 15.84px;">version: "3.3"
services:
docker-registry:
image: registry:2
environment:
- REGISTRY_HTTP_ADDR=0.0.0.0:5000
restart: always
ports:
- 0.0.0.0:5000:5000
volumes:
- /mnt/USB/registry:/var/lib/registry
networks:
- drone-net
docker-registry-web:
image: hyper/docker-registry-web:latest
environment:
- REGISTRY_URL=http://192.168.56.101:5000/v2
restart: always
ports:
- 0.0.0.0:8081:8080
networks:
- drone-net
drone-server:
image: drone/drone:1
environment:
- DRONE_GITEA_SERVER=http://192.168.56.101:3000
- DRONE_GITEA_CLIENT_ID=</span><span style="color: red; font-size: 15.84px;"><客戶端ID></span><span style="color: #66d9ef; font-size: 15.84px;">
- DRONE_GITEA_CLIENT_SECRET=</span><span style="color: red; font-size: 15.84px;"><客戶端密鑰></span><span style="color: #66d9ef; font-size: 15.84px;">
- DRONE_RPC_SECRET=1q2w3e4r </span><span style="color: #a64d79; font-size: 15.84px;">#共用密碼(自行設定)</span><span style="font-size: 15.84px;"><span style="color: #66d9ef;">
- DRONE_SERVER_HOST=192.168.56.101:8000
- DRONE_SERVER_PROTO=http
restart: always
ports:
- 0.0.0.0:8000:80
networks:
- drone-net
drone-runner:
image: drone/drone-runner-docker:1
environment:
- DRONE_RPC_PROTO=http
- DRONE_RPC_HOST=drone-server
- DRONE_RPC_SECRET=1q2w3e4r </span><span style="color: #a64d79;">#共用密碼(自行設定)</span><span style="color: #66d9ef;">
- DRONE_RUNNER_CAPACITY=1
- DRONE_RUNNER_NAME=drone-runner-1
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- drone-net
gitea-server:
image: gitea/gitea:1.13.3
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- drone-net
volumes:
- ./gitea:/data
ports:
- "3000:3000"
- "222:22"
networks:
</span></span></span></span><p style="text-align: left;"><span style="color: #66d9ef; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;"> drone-net:</span></span><span style="font-size: medium;"><br /></span></p></pre><p style="text-align: left;"><span style="font-family: arial;"><span style="font-size: medium;">當建立 Docker Registry之後,可將 Registry 設定到 insecure 清單可忽略 TLS 協定,接下來在需要使用 Docke rRegistry的主機內設定為到 insecure 清單。</span></span></p><div style="font-family: arial; font-size: large;">insecure 設定檔路徑「/etc/docker/daemon.json」</div><p><span style="font-family: arial;"><span style="font-size: medium;"></span></span></p><div><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;"><span style="color: #66d9ef;">{
</span><span style="color: #eeeeee;">"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
},</span><span style="color: #66d9ef;">
</span><b style="color: #66d9ef;">"insecure-registries":["192.168.56.101:5000"]</b><span style="color: #66d9ef;">
}</span></span></span></pre><p><span style="font-family: arial; font-size: medium;">重新啟動 docker 服務才可以生效</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #66d9ef; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;">systemctl restart docker</span></span></pre><p><span style="font-family: arial; font-size: medium;">詳細請參考: <a href="https://docs.docker.com/registry/insecure/">https://docs.docker.com/registry/insecure/</a></span></p><p><br /></p></div><p><span style="font-family: arial; font-size: medium;">P.S: 產生 Gitea OAuth</span></p><p><span style="font-family: arial; font-size: medium;">根據文件,第一步要在 Gitea 的設定中找到 Applications 這個頁籤,建立一個新的應用。名稱我們給他 drone,URI 是 http://192.168.56.101:8000</span></p><p style="text-align: left;"><span style="font-family: arial;"><span style="font-size: medium;"></span></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: arial;"><span style="font-size: medium;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhJD600s-W4uKTHQgu5Ba2_pULsPbvbduN0qZXD1UeFD7F40-G8VlW-NSnUwmG-ZNhzc3gz-HB3k5y4aHSyRmzXAV7r9pOkvCVby31L6j8Belh2i9ZGAhYGEBiVVluwoY-Nmz70Fo-6NVJ1p2T0nIlgX_FnR99Ba2KVGkSyGjvC37bgytX1xRlyR2Q" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="412" data-original-width="1145" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEhJD600s-W4uKTHQgu5Ba2_pULsPbvbduN0qZXD1UeFD7F40-G8VlW-NSnUwmG-ZNhzc3gz-HB3k5y4aHSyRmzXAV7r9pOkvCVby31L6j8Belh2i9ZGAhYGEBiVVluwoY-Nmz70Fo-6NVJ1p2T0nIlgX_FnR99Ba2KVGkSyGjvC37bgytX1xRlyR2Q=w640-h230" width="640" /></a></span></span></div><p style="text-align: left;"><span style="font-family: arial;"><span style="font-size: medium;"><span style="font-family: arial;"><span style="font-size: medium;">當按下建立應用程式後,可以得到</span></span>客戶端 ID 與 </span></span><span style="font-family: arial; font-size: medium;">客戶端密鑰,並將這些資訊填入到YAML 內的 drone-server 所需的環境變數內:</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;"><span style="color: #66d9ef;">- DRONE_GITEA_CLIENT_ID=</span><span style="color: red;"><客戶端ID></span><span style="color: #66d9ef;">
- DRONE_GITEA_CLIENT_SECRET=</span><span style="color: red;"><客戶端密鑰></span></span></span></pre><p style="text-align: left;"><br /></p><span><span><div><span style="font-family: arial; font-size: medium;"></span></div></span></span><p></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">參考資料:</span></p><p style="text-align: left;"><a href="https://jiaming0708.github.io/2021/03/22/gitea-drone-docker/"><span style="font-family: arial; font-size: medium;">在本機使用Docker架Gitea和DroneCI</span></a></p><p><a href="https://jiajunhuang.com/articles/2020_01_01-use_gitea_with_drone.md.html"><span style="font-family: arial; font-size: medium;">使用Gitea+Drone打造自己的CI/CD系统</span></a></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-18272868518516738412022-04-09T17:01:00.000+08:002022-04-09T17:01:01.034+08:00[Parca]測試 Polar Signals 開源 Parca (基於eBPF 的profiler)<p><span style="font-family: arial; font-size: medium;">前陣子發現在Github上有個open-source projects: Parca (包含Server & Agent ),扒文了一下,Parca是Solar Signals的一個開源項目。它是持續分析存儲、查詢引擎和一個基於eBPF 的profiler。其目的在通過系統地測量代碼性能將可觀察性空間提升到一個新的水平,使每個人都能夠優化他們的代碼。</span></p><p><span style="font-family: arial; font-size: medium;">Parca 旨在將持續分析技術帶給所有人;其打包了許多開箱即用的功能,包括收集、存儲和提供可用於長期查詢的profiles 的能力— 包括CPU 分析,以確定CPU 執行一段特定代碼所需的時間。相關資源如下:<span></span></span></p><a name='more'></a><p></p><p><span style="font-family: arial; font-size: medium;">Github:<br /><a href="https://github.com/parca-dev/parca-agent">https://github.com/parca-dev/parca-agent<br /></a><a href="https://github.com/parca-dev/parca">https://github.com/parca-dev/parca</a></span></p><p><span style="font-family: arial; font-size: medium;">Document: <br /><a href="https://www.parca.dev/">https://www.parca.dev/</a></span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;">我依照官網文件做個簡單的CPU 分析功能測試如下:</span></p><p><span style="font-family: arial; font-size: medium;">Parca-Agent:</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #66d9ef; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 15.84px;">curl -sL https://github.com/parca-dev/parca-agent/releases/download/v0.7.0/parca-agent_0.7.0_`uname -s`_`uname -m`.tar.gz | tar xvfz - parca-agent
sudo ./parca-agent --node=hsl --systemd-units=docker.service --log-level=debug --kubernetes=false --store-address=localhost:7077 --insecure</span></pre><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">Parca Server ( 改用 port:7077 )</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #66d9ef; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 15.84px;">curl -sL https://github.com/parca-dev/parca/releases/download/v0.10.0/parca_0.10.0_`uname -s`_`uname -m`.tar.gz | tar xvfz - parca
./parca --config-path="parca.yaml" --port=":7077"</span></pre><p><br /></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">開啟網頁後可以看到CPU profile的畫面,相當厲害!</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEifQNPid40K5jmD8tBr_iR277lOgooCyDGRqLTn9kLLyE7BdckmDaK-ltllsaPl8jaBJrrefd_tKMGgHBttdsdmTHfSyGmifk6NQvybd_EgxgVR2S0QT4B5d8R7JVGTLl-1vIrPJpEKIuD0DeJjKYhal1E6DbMiTMA3FAoIT552pX1EHAo30o5XuJ8" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="946" data-original-width="1917" src="https://blogger.googleusercontent.com/img/a/AVvXsEifQNPid40K5jmD8tBr_iR277lOgooCyDGRqLTn9kLLyE7BdckmDaK-ltllsaPl8jaBJrrefd_tKMGgHBttdsdmTHfSyGmifk6NQvybd_EgxgVR2S0QT4B5d8R7JVGTLl-1vIrPJpEKIuD0DeJjKYhal1E6DbMiTMA3FAoIT552pX1EHAo30o5XuJ8=s16000" /></a></div><br /><br /><p></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-16691988872214274742022-03-29T13:09:00.011+08:002022-04-05T16:02:34.859+08:00[libbpf] 為何要使用 libbpf? 什麼是libbpfgo?<p><br /></p><p><span style="font-family: arial; font-size: medium;"><span>在開始說清楚講明白什麼是libbpfgo之前,我們可以先看這一投影片介紹初學者使用Go開發eBPF程式指南: </span><a href="https://speakerdeck.com/lizrice/beginners-guide-to-ebpf-programming-with-go"><span>Beginner's Guide to eBPF programming with Go</span></a>,此作者也提供 GitHub: <a href="https://github.com/lizrice/libbpfgo-beginners">https://github.com/lizrice/libbpfgo-beginners</a>,可體驗一下用libbpfgo開發的小範例程式Basic eBPF examples in Golang using libbpfgo.</span></p><span style="font-family: arial; font-size: medium;"><a href="https://github.com/lizrice/libbpfgo-beginners"></a></span><p></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-size: medium;"><span style="font-family: arial;">看過了簡單的小範例之後,接下來這篇文章主要是解釋什麼是vmlinux.h 以及為什麼在編寫 eBPF 程序時應該開始使用它: </span><a href="https://blog.aquasec.com/vmlinux.h-ebpf-programs"><span style="font-family: arial;">What is vmlinux.h and Why is It Important for Your eBPF Programs?</span></a></span></p><p><span style="font-family: arial; font-size: medium;">重點如下:<span></span></span></p><a name='more'></a><p></p><p><span style="font-family: arial; font-size: medium;">eBPF 程序需要知道它們正在處理什麼數據結構。您可以通過簡單的一行來實現:#include "vmlinux.h"。</span></p><p><span style="font-family: arial; font-size: medium;">vmlinux.h 簡而言之是生成的代碼。它包含正在運行的 Linux 內核在其自己的源代碼中使用的所有類型定義。構建 Linux 時,其中一個輸出工件是一個名為vmlinux的文件。它通常也與主要發行版打包在一起。這是一個ELF二進製文件,其中包含已編譯的可引導內核。</span></p><p><span style="font-family: arial; font-size: medium;">有一個工具,恰當地命名為bpftool,在 Linux 存儲庫中維護。它具有讀取vmlinux 目標文件並生成vmlinux.h 文件的功能。由於它包含已安裝內核使用的所有類型定義,因此它是一個非常大的頭文件。</span></p><p><span style="font-family: arial; font-size: medium;">實際命令為: </span></p><div style="font-family: arial;"><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true">$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h</div></div></div></div></div><p><span style="font-family: arial; font-size: medium;">現在,當import此header file時,你的 bpf 程序可以讀取原始內存並知道哪些字節對應於您要使用的結構的哪些字段。</span></p><p><span style="font-family: arial; font-size: medium;">由於vmlinux.h文件是從你安裝的內核生成的,因此如果你嘗試在運行不同內核版本的另一台機器上運行它而不重新編譯,你的 bpf 程序可能會中斷。這是因為,從版本到版本,內部結構的定義在 linux 源代碼中會發生變化。</span></p><p><span style="font-family: arial; font-size: medium;">但是,使用 libbpf 可以<b>啟用</b>稱為"<b>CO:RE"</b>或是稱為 "編譯一次,到處運行"的東西。libbpf 中定義了一些Macro(例如BPF_CORE_READ),它們將分析你嘗試訪問的vmlinux.h中定義的類型中的哪些字段。如果你要訪問的字段已在運行內核使用的結構定義中移動,宏/幫助程序將為你找到它。因此,是否使用 從自己的內核生成的vmlinux.h文件編譯 bpf 程序然後在另一個內核上運行它並不重要。</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><h3 style="text-align: left;"><span style="font-family: arial; font-size: large;">接下來重點來了,為何要使用 libbpf? </span></h3><p><span style="font-family: arial; font-size: medium;">這篇文章說明得很好 <a href="https://www.containiq.com/post/libbpf"><span>Libbpf: A Beginners Guide</span></a></span></p><p><span style="font-family: arial; font-size: medium;">Libbpf 是一組用於構建 BPF 應用程序的替代工具。對於網絡、安全和分析應用程序,它提供了優於 BCC 的幾個潛在優勢。</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"><b>Libbpf 和 BPF CO-RE</b></span></p><p><span style="font-family: arial; font-size: medium;">Libbpf 通常與 BPF CO-RE 一起使用(編譯一次,到處運行)。BPF CO-RE 旨在解決 BPF 的可移植性問題,允許您創建在不同內核版本上運行的二進製文件。</span></p><p><span style="font-family: arial; font-size: medium;">它包括BPF 類型格式 (BTF)信息。這意味著您需要使用在編譯時設置了CONFIG_DEBUG_INFO_BTF=y的內核構建。如果您使用的是標準的消費者 Linux 版本,則需要進行自定義編譯以啟用此功能;否則,你會遇到錯誤。</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"><b>Libbpf 相對於 BCC 的優勢</b></span></p><p><span style="font-family: arial; font-size: medium;">Libbpf 通過消除各種令人頭疼的問題,使開發人員可以專注於手頭的任務。</span></p><p><span style="font-family: arial; font-size: medium;">它生成簡單的二進製文件,編譯一次就可以在任何地方運行。它消除了許多依賴關係並儘可能接近一對一地執行您的代碼。</span></p><p><span style="font-family: arial; font-size: medium;">需要安裝以運行使用 BCC 編譯的程序的 LLVM、內核和頭文件依賴項可以運行到超過 100 MB。消除包含 LLVM 和 Clang 庫的開銷會導致更小的二進製文件。</span></p><p><span style="font-family: arial; font-size: medium;">例如,一個包含它們的工具使用 BCC 編譯為 645 KB。使用 libbpf 工具重新編譯的工俱生成了一個只有 151 KB 的可移植二進製文件。這是一個重大的尺寸減小。</span></p><p><span style="font-family: arial; font-size: medium;">Libbpf 還創建使用更少內存的二進製文件,例如,與 BCC 的 Python 的 80 MB 相比,內存佔用為 9 MB 。</span></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><p><span style="font-family: arial; font-size: medium;"><b><i>延伸閱讀:</i></b></span></p><p><span style="font-family: arial; font-size: medium;"><a href="https://en.pingcap.com/blog/why-we-switched-from-bcc-to-libbpf-for-linux-bpf-performance-analysis/">Why We Switched from BCC to libbpf for Linux BPF Performance Analysis</a></span></p><p><span style="font-family: arial; font-size: medium;"><a href="https://en.pingcap.com/blog/tips-and-tricks-for-writing-linux-bpf-applications-with-libbpf/">Tips and Tricks for Writing Linux BPF Applications with libbpf</a></span></p><p></p><p style="text-align: left;"></p><p></p><ul style="text-align: left;"><li><a href="https://github.com/libbpf/libbpf-bootstrap"><span style="font-family: arial; font-size: medium;">https://github.com/libbpf/libbpf-bootstrap</span></a></li></ul><p></p><ul style="text-align: left;"><li><span style="font-family: arial; font-size: medium;"><a href="https://github.com/iovisor/bcc/tree/master/libbpf-tools">https://github.com/iovisor/bcc/tree/master/libbpf-tools</a></span></li></ul><div style="text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><p><span style="font-family: arial; font-size: medium;">bpftool 是一個通用工具,用於檢查 BPF 資源,並提供各種額外的 BPF 相關工具,例如 BPF 程序骨架的代碼生成。這些工具大量使用後一種功能來加載 BPF 程序並與之交互。<br />鑑於 bpftool 包還不能在許多發行版中廣泛使用,bpftool 二進製文件被簽入到 bin/ 子目錄中的 BCC 存儲庫中。一旦 bpftool 包更廣泛地可用,就可以改變這有利於使用 bpftool 的預打包版本。</span></p></div></blockquote></div><p style="text-align: left;"><b><span style="font-family: arial; font-size: medium;"><a href="https://pingcap.com/zh/blog/libbpf-tools">Libbpf-tools —— 讓Tracing 工具身輕如燕</a></span></b></p><div style="text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><p><span style="font-family: arial; font-size: medium;">隨著BPF CO-RE 的落地,直接使用內核開發人員提供的libbpf 庫來開發BPF 程序,開發方式和編寫普通C 用戶態程序一樣:一次編譯生成小型的二進製文件。libbpf 作為BPF 程序加載器,接管了重定向、加載、驗證等功能,BPF 程序開發者只需要關注BPF 程序的正確性和性能即可。這種方式將開銷降到了最低,且去除了龐大的依賴關係,使得整體開發流程更加順暢。<br />性能優化大師Brendan Gregg 在用libbpf + BPF CO-RE 轉換一個BCC 工具後給出了性能對比數據:</span> </p></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><p><span style="font-family: arial; font-size: medium;"><span style="color: #800180;"><i>As my colleague Jason pointed out, the memory footprint of opensnoop as CO-RE is much lower than opensnoop.py. 9 Mbytes for CO-RE vs 80 Mbytes for Python.</i></span></span> </p></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><p><span style="font-family: arial;"><span style="font-size: medium;">我們可以看到在運行時相比BCC 版本,libbpf + BPF CO-RE 版本節約了近 </span><b><span style="color: #800180; font-size: large;">9 倍 </span></b><span style="font-size: medium;">的內存開銷,這對於物理內存資源已經緊張的服務器來說會更友好。</span></span></p></div></blockquote></div><p style="text-align: left;"></p><p style="text-align: left;"></p><p style="text-align: left;"></p><p style="text-align: left;"></p><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><br /></span></p><h3 style="text-align: left;"><span style="font-family: arial; font-size: medium;">那</span><span style="font-family: arial; font-size: large;">什麼是libbpfgo?</span></h3><p><span style="font-family: arial; font-size: medium;">這篇文章 <a href="https://blog.aquasec.com/libbpf-ebpf-programs"><span>How to Build eBPF Programs with libbpfgo?</span></a> 提到了重點:</span></p><p><span style="font-family: arial; font-size: medium;"><span>近年來,作者一直在使用一個名為 BCC 的項目來編譯、加載和與他的 bpf 程序交互。(<i><b><span style="color: #999999;">我個人也遇到了BCC 需要 </span></b></i></span><span style="color: #999999;"><b><i>kernel header packages,造成</i></b></span><i><b><span style="color: #999999;">佈署上的不方便性與程式碼為明碼的問題</span></b></i>)。</span></p><p><span style="font-family: arial; font-size: medium;">他最近了解了<span style="color: #800180;"><b>一種更好的方法來構建名為libbpf的 ebpf 項目</b></span>。在開發基於 libbpf 的程序時,有一些很好的資源可供使用,但入門仍然會讓人不知所措。</span></p><p><span style="font-family: arial; font-size: medium;">為了說明 libbpf 的本質以及如何使用它,他將編寫一個簡單的 bpf 程序,它會告訴我們每次進程使用 mmap 系統調用的時間。然後他將用 C 語言編寫一個用戶空間程序,它會加載已編譯的 bpf 程序並監聽它的輸出。</span></p><p><span style="font-family: arial; font-size: medium;"><b><span style="color: #800180;">libbpfgo 是 libbpf 本身的一個wrapper。libbpfgo 的目標是實現 libbpf 的所有公共 API,以便您可以輕鬆地從 Go 中使用它。</span></b>libbpfgo已經開始被使用於 Tracee(Aqua 的開源項目之一)需要的功能。</span></p><p><span style="font-family: arial; font-size: medium;">作者所介紹的範例在這: </span></p><p><span style="font-family: arial; font-size: medium;"><a href="https://github.com/grantseltzer/libbpfgo-example"><span>https://github.com/grantseltzer/libbpfgo-example</span></a></span></p><p><span style="font-family: arial; font-size: medium;">執行方式如下:</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 15.84px;"><b><i><span style="color: #cccccc;">#Install packages</span></i></b><span style="color: #66d9ef;">
~$ sudo apt-get update
~$ sudo apt-get install libbpf-dev make clang llvm libelf-dev
~$ git clone https://github.com/grantseltzer/libbpfgo-example
~$ cd libbpfgo-example
~/libbpfgo-example$ make && sudo ./libbpfgo-prog</span></span></pre><p><span style="font-family: arial; font-size: medium;">P.S: 如果有發生找不到 libbpf.a 的錯誤訊息,則會需要修改Makefile 內的 go_target內的CGO_LDFLAGS.</span></p><p><span style="font-family: arial; font-size: medium;">例如: CGO_LDFLAGS="/usr/lib/x86_64-linux-gnu/libbpf.a"</span></p><p><span style="font-family: arial; font-size: medium;">延伸閱讀:</span></p><p><a href="https://programs.wiki/wiki/stepping-on-the-pit-use-libbpfgo-to-build-your-first-ebpf-project.html"><span style="font-family: arial; font-size: medium;">use libbpfgo to build your first eBPF project</span></a></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-41827728807562803852022-03-29T10:43:00.007+08:002022-03-29T10:45:14.684+08:00[Janus] 如何快速制定與使用Janus API Gateway<p><br /></p><h3 style="background-color: white; color: #4d4d4d; margin: 0px; position: relative;"><b><span style="font-family: arial; font-size: medium;">[前言]</span></b></h3><p><span style="font-family: arial; font-size: medium;">當前端(Frontend) Web App 需要使用到多個API Servers時,最快的方式就是使用API Gateway 作為前端的進入點,透過URI Route的設定與對應後端的API & Server。</span></p><p><span style="font-size: medium;"><span style="font-family: arial;">根據</span><span style="font-family: arial;">Janus's </span><span style="font-family: arial;">Github 上官方的描述, 它是一個輕量級的 API Gateway和 管理平台,可控制訪問 API 的人員、訪問時間以及訪問方式。Janus 還將記錄有關使用者如何與你的 API 交互以及何時出現問題的詳細分析。</span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;">官方文件: <a href="https://hellofresh.gitbooks.io/janus/content/">https://hellofresh.gitbooks.io/janus/content/</a></span></span></p><p><span style="font-family: arial; font-size: medium;">下面段落將說明如何快速制定與使用Janus API Gateway。</span></p><p><span></span></p><a name='more'></a><span style="font-family: arial; font-size: medium;"><br /></span><p></p><h3 style="background-color: white; color: #4d4d4d; margin: 0px; position: relative;"><b><span style="font-family: arial; font-size: medium;">[如何快速制定與使用Janus API Gateway]</span></b></h3><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">首先 複製 Github上的 Janus Source Code</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #66d9ef; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 15.84px;">~$ cd ~/
~$ git clone https://github.com/motiv-labs/janus.git</span></pre><p><span style="font-family: arial;"><span style="font-size: medium;"><br /></span></span></p><p><span style="font-family: arial;"><span style="font-size: medium;">製作 Janus's Docker Image</span></span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="color: #66d9ef; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 15.84px;">~$ cd janus
~/janus$ </span><span style="color: #66d9ef; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;">docker build -t janus:debug .</span></span></pre><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">利用janus/examples/front-proxy 內的設定作為我的範本(Template)</span></p><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span><span style="color: #66d9ef; font-size: 15.84px; font-weight: 400;">~/janus$ cd example/front-proxy
~/janus/examples/front-proxy$ tree -L 3
.
├── apis
│ ├── app.json </span><span style="color: white; font-size: 15.84px; font-weight: 400;"> <== I put my api's URI route definition here</span><span style="color: #66d9ef; font-size: 15.84px; font-weight: 400;">
│ └── example.json
├── docker-compose.yml
├── janus.toml
<strike>└── stubs</strike> </span><span><span style="color: white; font-size: 15.84px; font-weight: 400;"><== Remove this folder because it is just for testing</span><strike style="color: #66d9ef; font-size: 15.84px; font-weight: 400;">
├── service.json</strike><strike style="color: #66d9ef; font-size: 15.84px; font-weight: 400;">
└── status.json</strike><span style="color: #66d9ef; font-size: 15.84px; font-weight: 400;"> </span></span></span></span><span style="color: #f8f8f2; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 17.6px; font-weight: 400;">
</span></pre><div><p style="font-weight: 400;"><span style="font-family: arial; font-size: medium;"><br /></span></p><p style="font-weight: 400;"><span style="font-family: arial; font-size: medium;">在apis/app.json 放入我們需要的URI route 對應的設定,下列為app.json內容:</span></p><p style="font-weight: 400;"><span style="color: #999999; font-family: arial; font-size: medium;"><i>P.S: 可以在apis/內放多個.json檔案,裡面的內容如果需要定義多個以上的URI Routes,可以用逗號分隔多個設定並放入中括號內。</i></span></p></div></h3><h3><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-align: left; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span><span style="font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;"><span style="font-size: 15.84px; font-weight: 400;"><span style="color: white;">[</span><span style="color: #66d9ef;">
{
"name" : "frontend9",
"active" : true,
"proxy" : {
"append_path" : true,
"listen_path" : "/k8s/podsvc/*",
"upstreams" : {
"balancing": "roundrobin",
"targets": [
{"target": "http://localhost:8088"}
]
},
"methods" : ["GET","POST"]
}
}</span><span style="color: white;">,</span><span style="color: #66d9ef;">
{
"name" : "simpleginserver2",
"active" : true,
"proxy" : {
"strip_path" : true,
"listen_path" : "/simple2/*",
"upstreams" : {
"balancing": "roundrobin",
"targets": [
{"target": "http://localhost:8001"}
]
},
"methods" : ["GET"]
}
}
</span><span style="color: white;">]</span></span></span><span style="color: #f8f8f2; font-family: arial; font-size: medium;"><span style="font-weight: 400;">
</span></span></span></pre><p style="text-align: left;"><span style="font-family: arial; font-size: medium;"><span style="font-weight: normal;"><br /></span><span style="font-weight: normal;">說明: 以上述</span></span><span style="font-size: medium;"><span style="font-family: arial; font-weight: 400;">simpleginserver2 設定來說,我使用了 </span></span><span style="font-family: arial; font-size: large; font-weight: 400;">"strip_path" = true,</span><span style="font-family: arial; font-size: large; font-weight: 400;">客戶端對上面配置的API的請求:</span></p><div><pre style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; background: rgb(247, 247, 247); border: none; box-sizing: border-box; break-inside: avoid; color: #333333; direction: ltr; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 16px; font-weight: 400; letter-spacing: 0.2px; margin-bottom: 1.275em; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 0.85em 1em; text-size-adjust: none; white-space: pre-wrap;"><code class="lang-http" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; background: 0px 0px; border: none; box-sizing: border-box; break-inside: avoid; color: inherit; direction: ltr; display: inline; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 0.85em; line-height: inherit; margin: 0px; max-width: initial; overflow: initial; padding: 0px; text-size-adjust: none; white-space: pre;"><span class="hljs-keyword" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; box-sizing: border-box; color: #8959a8; font-size: inherit; text-size-adjust: none;">GET</span> <span class="hljs-string" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; box-sizing: border-box; color: #718c00; font-size: inherit; text-size-adjust: none;">/simple2/noaction</span> HTTP/1.1
<span class="hljs-attribute" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; box-sizing: border-box; color: #c82829; font-size: inherit; text-size-adjust: none;">Host</span>: localhost:8080</code></pre></div></h3><h3><div><p><span style="font-family: arial; font-size: medium; font-weight: 400;">將導致 Janus 向上游服務發送以下請求如下:</span></p></div></h3><h3><pre style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; background: rgb(247, 247, 247); border: none; box-sizing: border-box; break-inside: avoid; color: #333333; direction: ltr; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 16px; font-weight: 400; letter-spacing: 0.2px; margin-bottom: 1.275em; margin-top: 0px; overflow-wrap: normal; overflow: auto; padding: 0.85em 1em; text-size-adjust: none; white-space: pre-wrap;"><code class="lang-http" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; background: 0px 0px; border: none; box-sizing: border-box; break-inside: avoid; color: inherit; direction: ltr; display: inline; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 0.85em; line-height: inherit; margin: 0px; max-width: initial; overflow: initial; padding: 0px; text-size-adjust: none; white-space: pre;"><span class="hljs-keyword" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; box-sizing: border-box; color: #8959a8; font-size: inherit; text-size-adjust: none;">GET</span> <span class="hljs-string" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; box-sizing: border-box; color: #718c00; font-size: inherit; text-size-adjust: none;">/noaction</span> HTTP/1.1
<span class="hljs-attribute" style="-webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; box-sizing: border-box; color: #c82829; font-size: inherit; text-size-adjust: none;">Host</span>: localhost:8001</code></pre></h3><h3><div><p style="font-weight: 400;"><span style="font-family: arial; font-size: medium;"><br /></span></p><p style="font-weight: 400;"><span style="font-family: arial; font-size: medium;">最後,需修改 docker-compose.yml</span></p></div><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><span style="font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 15.84px;"><span style="color: #cccccc;"><i># 下列是我的範例</i></span><span style="color: #66d9ef; font-weight: 400;">
~/janus/examples/front-proxy$ cat docker-compose.yml
version: '3.3'
services:
janus:
image: janus:debug </span><span style="color: white; font-weight: 400;"><== 改為之前所 build 的 docker image</span><span><span style="color: #66d9ef; font-weight: 400;">
ports:
- "8080:8080"
- "8081:8081"
volumes:
- ./janus.toml:/etc/janus/janus.toml
- ./apis:/etc/janus/apis
</span><i><span style="color: #cccccc;"># 執行 Janus API Gateway</span></i></span><span style="color: #66d9ef; font-weight: 400;">
~/janus/examples/front-proxy$ docker-compose up -d</span></span></span><span style="color: #f8f8f2; font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;"><span style="font-size: 17.6px; font-weight: 400;">
</span></span></pre><div><br /></div></h3>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-20203933984899350992022-03-23T10:20:00.003+08:002022-03-23T10:25:36.239+08:00[GraphQL] 使用 Apollo Client + React 來建置 GraphQL 前端應用程式<h3><b><span style="font-size: medium;"><span style="font-family: arial;">[前言]</span></span></b></h3><p><span style="font-size: medium;"><span><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">GraphQL系列文章</span></span></span></span></span></p><ul class="posts" style="background-color: white; border-width: 0px; color: #6a6a6a; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 1.2; list-style: none none; margin: 0px; padding: 0px; text-align: left; text-indent: -15px;"><ul><li style="background: none; border-bottom-color: rgba(52, 52, 52, 0); border-bottom-style: solid; border-top-color: initial; border-top-style: none; border-width: 0px; list-style: outside none none; margin: 0.25em 0px; padding: 0.25em 15px 0.25em 1.3em;"><a href="https://danny270degree.blogspot.com/2022/03/graphql-apollo-client-react-graphql.html" style="color: #3778cd; text-decoration-line: none;"><span style="font-size: medium;">[GraphQL] 使用 Apollo Client + React 來建置 GraphQL 前端應用程式</span></a></li></ul></ul><p style="text-align: left;"><span style="font-size: medium;"><span><span style="font-family: arial;"><span style="color: #0000ee; vertical-align: inherit;"></span></span></span></span></p><ul class="posts" style="background-color: white; border-width: 0px; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 1.2; list-style: none none; margin: 0px; padding: 0px; text-align: left; text-indent: -15px;"><ul><li style="background: none; border-bottom-color: rgba(52, 52, 52, 0); border-bottom-style: solid; border-top-color: rgb(239, 239, 239); border-top-style: solid; border-width: 0px; color: #6a6a6a; list-style: outside none none; margin: 0.25em 0px; padding: 0.25em 15px 0.25em 1.3em;"><a href="https://danny270degree.blogspot.com/2022/03/graphql-golang-gin-frameworkgraphqlapi.html" style="color: #3778cd; text-decoration-line: none;"><span style="font-size: medium;">[GraphQL] 使用Golang Gin Framework來建置GraphQL的API Server</span></a></li></ul></ul><p><span style="font-size: medium;"><span><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">本篇內容將提供快速的指引,使用Golang Gin Framework來建置</span></span></span></span><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">GraphQL的API </span></span></span><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">Server。</span></span></span></span></p><p><span style="font-size: medium;"><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;"><br /></span></span></span></span></p><h3><span style="font-family: arial; font-size: medium;">[Apollo Client Architecture]</span></h3><p><span style="font-family: arial; font-size: medium;">Apollo Client 與 UI 框架無關 (可與 Angular.js、Vue.js、React 甚至原生 iOS 和 Android 應用程序一起使用)。<span></span></span></p><a name='more'></a><p></p><p><span style="font-family: arial; font-size: medium;">前端應用程序將 GraphQL 查詢發送到 Apollo 客戶端,後者處理查詢並從 GraphQL 服務器請求數據,兼容任何構建設置和 GraphQL API。</span></p><p><span style="font-family: arial; font-size: large;">然後 GraphQL 服務器將數據響應發送回 Apollo 客戶端。然後規範化並存儲數據。前端應用程序接收 UI 更新。Apollo Client 基本上為前端應用程序做了一些繁重的工作。它還提供開箱即用的智能緩存。</span></p><p><span style="font-family: arial; font-size: medium;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: arial; font-size: medium;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhcNqQhDhHQlxO5dReHkXBUyhK39fe7zMDPcrQrQ-0Yma5vLvPTm0jiNwWGPdsieZEE3lIZqkNpKJjE87Ak_afuZmIEcMB2_pvLNfmAaorh7VfSlimhMciBvgAHVk2IqmmhrWytKDr0F68mlrCGr5wdkkt5y2ahu1RBoXX6Gdy52AO2HpSi5jXKgGg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="234" data-original-width="729" height="206" src="https://blogger.googleusercontent.com/img/a/AVvXsEhcNqQhDhHQlxO5dReHkXBUyhK39fe7zMDPcrQrQ-0Yma5vLvPTm0jiNwWGPdsieZEE3lIZqkNpKJjE87Ak_afuZmIEcMB2_pvLNfmAaorh7VfSlimhMciBvgAHVk2IqmmhrWytKDr0F68mlrCGr5wdkkt5y2ahu1RBoXX6Gdy52AO2HpSi5jXKgGg=w640-h206" width="640" /></a></span></div><p></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><h3><span style="font-family: arial; font-size: medium;">[使用 Apollo Client + React 來建置 GraphQL 前端應用程式]</span></h3><p><span style="font-family: arial; font-size: medium;">Apollo Client + React for GraphQL 是參考下列文章的方式來建置: </span></p><p></p><ul style="text-align: left;"><li><a href="https://www.apollographql.com/docs/react/get-started/"><span style="font-family: arial; font-size: medium;">https://www.apollographql.com/docs/react/get-started/</span></a></li><li><a href="https://programmingwithmosh.com/backend/graphql/getting-started-with-apollo-graphql-in-your-react-app/"><span style="font-family: arial; font-size: medium;">https://programmingwithmosh.com/backend/graphql/getting-started-with-apollo-graphql-in-your-react-app/</span></a></li></ul><p></p><p><span style="font-family: arial; font-size: medium;"><br /></span></p><h3 style="background-color: white; color: #333333; margin: 10px 0px 0px; padding: 0px;"><ul style="text-align: left;"><li><span style="font-family: arial; font-size: medium; margin: 0px; padding: 0px; vertical-align: inherit;">安裝以下軟件包</span></li></ul></h3><h3><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); color: #f8f8f2; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 17.6px; font-weight: 400; hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><code class="language-javascript" style="background: none; border-radius: 0px; border: none; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 0.9em; hyphens: none; line-height: 1.5; margin: 0px; overflow-wrap: normal; padding: 0px; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal; word-spacing: normal;">npm install @apollo<span class="token operator" style="margin: 0px; padding: 0px;">/</span>client graphql</code></pre><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><em style="margin: 0px; padding: 0px;">apollo/client:</em><span style="margin: 0px; padding: 0px; vertical-align: inherit;"> 包含設置 Apollo 客戶端(版本 3.0)所需的一切</span></p><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><em style="margin: 0px; padding: 0px;">graphql: </em><span style="margin: 0px; padding: 0px; vertical-align: inherit;"> 提供解析 GraphQL 查詢的邏輯。</span></p><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;"><br /></span></p></h3><h3 style="background-color: white; color: #333333; font-family: "Open Sans", HelveticaNeue-Light, "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; margin: 10px 0px 0px; padding: 0px;"><ul style="text-align: left;"><li><span style="font-size: medium;">import 模組</span></li></ul></h3><h3><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">在應用程序的根目錄中,通常是</span><em style="margin: 0px; padding: 0px;">index.js </em><span style="margin: 0px; padding: 0px; vertical-align: inherit;">或</span><em style="margin: 0px; padding: 0px;">App.js, </em><span style="margin: 0px; padding: 0px; vertical-align: inherit;">讓我們準備好導入。</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); color: #f8f8f2; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 17.6px; font-weight: 400; hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><code class="language-javascript" style="background: none; border-radius: 0px; border: none; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 0.9em; hyphens: none; line-height: 1.5; margin: 0px; overflow-wrap: normal; padding: 0px; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal; word-spacing: normal;"><span class="token keyword" style="color: #66d9ef; margin: 0px; padding: 0px;">import</span> <span class="token punctuation" style="margin: 0px; padding: 0px;">{</span>
ApolloClient<span class="token punctuation" style="margin: 0px; padding: 0px;">,</span>
InMemoryCache<span class="token punctuation" style="margin: 0px; padding: 0px;">,</span>
ApolloProvider<span class="token punctuation" style="margin: 0px; padding: 0px;">,</span>
useQuery<span class="token punctuation" style="margin: 0px; padding: 0px;">,</span><span style="margin: 0px; padding: 0px;"></span>
gql<span style="margin: 0px; padding: 0px;"></span>
<span class="token punctuation" style="margin: 0px; padding: 0px;">}</span> <span class="token keyword" style="color: #66d9ef; margin: 0px; padding: 0px;">from</span> <span class="token string" style="color: #a6e22e; margin: 0px; padding: 0px;">"@apollo/client"</span><span class="token punctuation" style="margin: 0px; padding: 0px;">;</span> </code></pre><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;"></span><br /></p></h3><h3 style="background-color: white; color: #333333; font-family: "Open Sans", HelveticaNeue-Light, "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; margin: 10px 0px 0px; padding: 0px;"><ul style="text-align: left;"><li><span style="font-size: medium; margin: 0px; padding: 0px; vertical-align: inherit;">創建 Apollo 客戶端</span></li></ul></h3><h3><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">在應用程序中初始化 Apollo 客戶端。</span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); color: #f8f8f2; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 17.6px; font-weight: 400; hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><code class="language-javascript" style="background: none; border-radius: 0px; border: none; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 0.9em; hyphens: none; line-height: 1.5; margin: 0px; overflow-wrap: normal; padding: 0px; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal; word-spacing: normal;"><span class="token keyword" style="color: #66d9ef; margin: 0px; padding: 0px;">const</span> client <span class="token operator" style="margin: 0px; padding: 0px;">=</span> <span class="token keyword" style="color: #66d9ef; margin: 0px; padding: 0px;">new</span> <span class="token class-name" style="margin: 0px; padding: 0px;">ApolloClient</span><span class="token punctuation" style="margin: 0px; padding: 0px;">(</span><span class="token punctuation" style="margin: 0px; padding: 0px;">{</span>
uri<span class="token punctuation" style="margin: 0px; padding: 0px;">:</span> ”https<span class="token punctuation" style="margin: 0px; padding: 0px;">:</span><span class="token operator" style="margin: 0px; padding: 0px;">/</span><span class="token operator" style="margin: 0px; padding: 0px;">/</span>my<span class="token operator" style="margin: 0px; padding: 0px;">-</span>example<span class="token operator" style="margin: 0px; padding: 0px;">-</span>url<span class="token punctuation" style="margin: 0px; padding: 0px;">.</span>com”<span class="token punctuation" style="margin: 0px; padding: 0px;">,</span> <span class="token comment" spellcheck="true" style="color: slategrey; margin: 0px; padding: 0px;">// Your running GraphQL server URL</span>
cache<span class="token punctuation" style="margin: 0px; padding: 0px;">:</span> <span class="token keyword" style="color: #66d9ef; margin: 0px; padding: 0px;">new</span> <span class="token class-name" style="margin: 0px; padding: 0px;">InMemoryCache</span><span class="token punctuation" style="margin: 0px; padding: 0px;">(</span><span class="token punctuation" style="margin: 0px; padding: 0px;">)</span>
<span class="token punctuation" style="margin: 0px; padding: 0px;">}</span><span class="token punctuation" style="margin: 0px; padding: 0px;">)</span><span class="token punctuation" style="margin: 0px; padding: 0px;">;</span></code></pre><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">在應用程序的根目錄中,如上所示初始化 Apollo 客戶端實例,並為其提供 GraphQL 服務器 URL。</span><span style="margin: 0px; padding: 0px; vertical-align: inherit;">這也可以是本地模式路徑。</span></span></p><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;"><br /></span></span></p></h3><h3 style="background-color: white; color: #333333; font-family: "Open Sans", HelveticaNeue-Light, "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; margin: 10px 0px 0px; padding: 0px;"><ul style="text-align: left;"><li><span style="font-size: medium; margin: 0px; padding: 0px; vertical-align: inherit;">連接 Apollo 客戶端以與 Apollo Provider 反應</span></li></ul></h3><h3><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">下一步是使用 Apollo Provider 組件將 Apollo Client 連接到 React。</span><span style="margin: 0px; padding: 0px; vertical-align: inherit;">它包裝了 React 應用程序並將客戶端放置在上下文中。</span><span style="margin: 0px; padding: 0px; vertical-align: inherit;">這允許從應用程序中的所有組件訪問 Apollo 客戶端。</span><span style="margin: 0px; padding: 0px; vertical-align: inherit;">將提供程序包裝在應用程序的根目錄中。</span></span></p><pre class="language-javascript" style="background: rgb(39, 40, 34); border-radius: 0.3em; border: 1px solid rgb(204, 204, 204); color: #f8f8f2; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 17.6px; font-weight: 400; hyphens: none; line-height: 1.5; margin-bottom: 0.5em; margin-top: 0.5em; overflow-wrap: normal; overflow: auto; padding: 1em; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal;"><code class="language-javascript" style="background: none; border-radius: 0px; border: none; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 0.9em; hyphens: none; line-height: 1.5; margin: 0px; overflow-wrap: normal; padding: 0px; tab-size: 4; text-shadow: rgba(0, 0, 0, 0.3) 0px 1px; word-break: normal; word-spacing: normal;"><span class="token operator" style="margin: 0px; padding: 0px;"><</span>ApolloProvider client <span class="token operator" style="margin: 0px; padding: 0px;">=</span> <span class="token punctuation" style="margin: 0px; padding: 0px;">{</span>client<span class="token punctuation" style="margin: 0px; padding: 0px;">}</span><span class="token operator" style="margin: 0px; padding: 0px;">></span>
<span class="token operator" style="margin: 0px; padding: 0px;"><</span>App <span class="token operator" style="margin: 0px; padding: 0px;">/</span><span class="token operator" style="margin: 0px; padding: 0px;">></span>
<span class="token operator" style="margin: 0px; padding: 0px;"><</span><span class="token operator" style="margin: 0px; padding: 0px;">/</span>ApolloProvider<span class="token operator" style="margin: 0px; padding: 0px;">></span></code></pre></h3><h2 style="background-color: white; border-bottom: none; color: #333333; font-family: "Open Sans", HelveticaNeue-Light, "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; margin: 40px 0px 20px; padding: 0px; position: relative;"><ul style="text-align: left;"><li><span style="font-size: medium; margin: 0px; padding: 0px; vertical-align: inherit;">使用 GraphQL API 並執行查詢</span></li></ul></h2><h3><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">我們已經準備好實際使用 GraphQL API 並使用 GraphQL 查詢請求數據。</span></span><span style="font-size: 17.6px; margin: 0px; padding: 0px; vertical-align: inherit;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">可以將 Apollo Client 集成到任何 React 項目中,並將其與現有的 GraphQL 服務器一起使用。</span></span></p></h3><h3 style="background-color: white; color: #333333; font-family: "Open Sans", HelveticaNeue-Light, "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; margin: 10px 0px 0px; padding: 0px;"><ul style="text-align: left;"><li><span style="font-size: medium; margin: 0px; padding: 0px; vertical-align: inherit;">使用useQuery</span></li></ul></h3><h3><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><span style="background-color: #fcfcfc; font-size: 17.6px; margin: 0px; padding: 0px; vertical-align: inherit;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">useQuery是一個 React Hook,它是</span></span><i style="background-color: #fcfcfc; font-size: 17.6px; margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">用於</span></i><span style="background-color: #fcfcfc; font-size: 17.6px; margin: 0px; padding: 0px; vertical-align: inherit;"><span style="margin: 0px; padding: 0px; vertical-align: inherit;">在 Apollo 應用程序中執行查詢的 API。</span></span></p><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><br /></p><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;">接下來,我們利用UsersList.js來呈現查詢與顯示資料的React元件:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">import</span> { <span style="color: #9cdcfe;">gql</span>, <span style="color: #9cdcfe;">useQuery</span> } <span style="color: #c586c0;">from</span> <span style="color: #ce9178;">'@apollo/client'</span>;</div><br /><div><span style="color: #569cd6;">const</span> <span style="color: #4fc1ff;">GET_POST_FOR_USERS</span> = <span style="color: #dcdcaa;">gql</span><span style="color: #ce9178;">`</span></div><div><span style="color: #ce9178;">query GetUsers {</span></div><div><span style="color: #ce9178;"> users {</span></div><div><span style="color: #ce9178;"> id</span></div><div><span style="color: #ce9178;"> email</span></div><div><span style="color: #ce9178;"> userId</span></div><div><span style="color: #ce9178;"> }</span></div><div><span style="color: #ce9178;">}</span></div><div><span style="color: #ce9178;">`</span>;</div><br /><div><span style="color: #569cd6;">const</span> <span style="color: #dcdcaa;">UsersList</span> = () <span style="color: #569cd6;">=></span> {</div><div> <span style="color: #569cd6;">const</span> { <span style="color: #4fc1ff;">loading</span>, <span style="color: #4fc1ff;">error</span>, <span style="color: #4fc1ff;">data</span> } = <span style="color: #dcdcaa;">useQuery</span>(<span style="color: #4fc1ff;">GET_POST_FOR_USERS</span>);</div><div> <span style="color: #c586c0;">if</span> (<span style="color: #4fc1ff;">loading</span>) <span style="color: #c586c0;">return</span> <span style="color: grey;"><</span><span style="color: #569cd6;">p</span><span style="color: grey;">></span>Loading...<span style="color: grey;"></</span><span style="color: #569cd6;">p</span><span style="color: grey;">></span>;</div><div> <span style="color: #c586c0;">if</span> (<span style="color: #4fc1ff;">error</span>) <span style="color: #c586c0;">return</span> <span style="color: grey;"><</span><span style="color: #569cd6;">p</span><span style="color: grey;">></span>Error :(<span style="color: grey;"></</span><span style="color: #569cd6;">p</span><span style="color: grey;">></span>;</div><br /><div> <span style="color: #c586c0;">return</span> <span style="color: #4fc1ff;">data</span>.<span style="color: #9cdcfe;">users</span>.<span style="color: #dcdcaa;">map</span>(<span style="color: #9cdcfe;">currentUser</span> <span style="color: #569cd6;">=></span> (</div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">li</span> <span style="color: #9cdcfe;">key</span>=<span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">currentUser</span>.<span style="color: #9cdcfe;">id</span><span style="color: #569cd6;">}</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">li</span><span style="color: grey;">></span>id: <span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">currentUser</span>.<span style="color: #9cdcfe;">id</span><span style="color: #569cd6;">}</span><span style="color: grey;"></</span><span style="color: #569cd6;">li</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">li</span><span style="color: grey;">></span>userId: <span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">currentUser</span>.<span style="color: #9cdcfe;">userId</span><span style="color: #569cd6;">}</span> <span style="color: grey;"></</span><span style="color: #569cd6;">li</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">li</span><span style="color: grey;">></span>email: <span style="color: #569cd6;">{</span><span style="color: #9cdcfe;">currentUser</span>.<span style="color: #9cdcfe;">email</span><span style="color: #569cd6;">}</span><span style="color: grey;"></</span><span style="color: #569cd6;">li</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">br</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"></</span><span style="color: #569cd6;">li</span><span style="color: grey;">></span></div><div> ));</div><div>};</div><br /><div><span style="color: #c586c0;">export</span> <span style="color: #c586c0;">default</span> <span style="color: #dcdcaa;">UsersList</span>;</div></div><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;"><br /></p><p style="background-color: white; color: #333333; font-family: "open sans", helvetica, sans-serif; font-size: 17.6px; font-weight: 400; margin: 0px 0px 25px; padding: 0px;">我們在App.js 加入相關的實作:</p><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px; white-space: pre;"><div><span style="color: #c586c0;">import</span> {</div><div> <span style="color: #9cdcfe;">ApolloClient</span>,</div><div> <span style="color: #9cdcfe;">InMemoryCache</span>,</div><div> <span style="color: #9cdcfe;">ApolloProvider</span>,</div><div> <span style="color: #9cdcfe;">useQuery</span>,</div><div> <span style="color: #9cdcfe;">gql</span></div><div>} <span style="color: #c586c0;">from</span> <span style="color: #ce9178;">"@apollo/client"</span>;</div><div><span style="color: #c586c0;">import</span> { <span style="color: #9cdcfe;">GenGraphQLURI</span> } <span style="color: #c586c0;">from</span> <span style="color: #ce9178;">'./components/common/utils'</span>;</div><div><span style="color: #c586c0;">import</span> <span style="color: #9cdcfe;">UsersList</span> <span style="color: #c586c0;">from</span> <span style="color: #ce9178;">'./components/apollo/UsersList'</span>;</div><br /><br /><div><span style="color: #569cd6;">function</span> <span style="color: #dcdcaa;">App</span>() {</div><div> <span style="color: #569cd6;">const</span> <span style="color: #4fc1ff;">client</span> = <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">ApolloClient</span>({</div><div> <span style="color: #9cdcfe;">uri</span><span style="color: #9cdcfe;">:</span> <span style="color: #dcdcaa;">GenGraphQLURI</span>(<span style="color: #ce9178;">"127.0.0.1"</span>, <span style="color: #ce9178;">"8088"</span>, <span style="color: #ce9178;">"/gql"</span>),</div><div> <span style="color: #9cdcfe;">cache</span><span style="color: #9cdcfe;">:</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">InMemoryCache</span>()</div><div> });</div><br /><div> <span style="color: #c586c0;">return</span> (</div><div> <span style="color: grey;"><</span><span style="color: #569cd6;">div</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span><span style="color: #4ec9b0;">ApolloProvider</span> <span style="color: #9cdcfe;">client</span>=<span style="color: #569cd6;">{</span><span style="color: #4fc1ff;">client</span><span style="color: #569cd6;">}</span><span style="color: grey;">></span></div><div> <span style="color: grey;"><</span> <span style="color: #4ec9b0;">UsersList</span> <span style="color: grey;">/></span></div><div> <span style="color: grey;"></</span><span style="color: #4ec9b0;">ApolloProvider</span><span style="color: grey;">></span></div><div> <span style="color: grey;"></</span><span style="color: #569cd6;">div</span><span style="color: grey;">></span></div><div> );</div><div>}</div><br /><div><span style="color: #c586c0;">export</span> <span style="color: #c586c0;">default</span> <span style="color: #dcdcaa;">App</span>;</div></div></h3><div><br /></div><p><span style="font-family: arial; font-size: medium;">在APM API Server上測試 GraphQL query 之結果與在React + Apollo Client 上測試GraphQL query 相吻合。</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgmjvIvvxdYrzfxcGUOTQTFthoo152s_fa4HAOiggj_EyVDJAVDnI_oS9NSRkOYH-4jQR3kHbSkPdvsclCCaHoFYbMq6hXJrt90AiX-kwf2Iet3GgHM7aIiPDmsim3sTPaNX8TQukPiCFptU0SvngcSl5D1H-imp9ni1QhOl3Os3sS0neCPFNM2NcA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="529" data-original-width="1231" height="276" src="https://blogger.googleusercontent.com/img/a/AVvXsEgmjvIvvxdYrzfxcGUOTQTFthoo152s_fa4HAOiggj_EyVDJAVDnI_oS9NSRkOYH-4jQR3kHbSkPdvsclCCaHoFYbMq6hXJrt90AiX-kwf2Iet3GgHM7aIiPDmsim3sTPaNX8TQukPiCFptU0SvngcSl5D1H-imp9ni1QhOl3Os3sS0neCPFNM2NcA=w640-h276" width="640" /></a></div><br /><br /><p></p><p><br /></p><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-31056572654480403262022-03-22T17:30:00.005+08:002022-03-23T10:25:50.776+08:00[GraphQL] 使用Golang Gin Framework來建置GraphQL的API Server<h3 style="text-align: left;"><b><span style="font-size: medium;"> <span style="font-family: arial;">[前言]</span></span></b></h3><div><p><span style="font-size: medium;"><span><span style="font-family: arial;">GraphQL系列文章</span></span></span></p><ul class="posts" style="background-color: white; border-width: 0px; color: #6a6a6a; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 1.2; list-style: none none; margin: 0px; padding: 0px; text-indent: -15px;"><ul><li style="background: none; border-bottom-color: rgba(52, 52, 52, 0); border-bottom-style: solid; border-top-color: initial; border-top-style: none; border-width: 0px; list-style: outside none none; margin: 0.25em 0px; padding: 0.25em 15px 0.25em 1.3em;"><a href="https://danny270degree.blogspot.com/2022/03/graphql-apollo-client-react-graphql.html" style="color: #3778cd; text-decoration-line: none;"><span style="font-size: medium;">[GraphQL] 使用 Apollo Client + React 來建置 GraphQL 前端應用程式</span></a></li></ul></ul><p><span style="font-size: medium;"><span><span style="font-family: arial;"><span style="color: #0000ee; vertical-align: inherit;"></span></span></span></span></p><ul class="posts" style="background-color: white; border-width: 0px; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; line-height: 1.2; list-style: none none; margin: 0px; padding: 0px; text-indent: -15px;"><ul><li style="background: none; border-bottom-color: rgba(52, 52, 52, 0); border-bottom-style: solid; border-top-color: rgb(239, 239, 239); border-top-style: solid; border-width: 0px; color: #6a6a6a; list-style: outside none none; margin: 0.25em 0px; padding: 0.25em 15px 0.25em 1.3em;"><a href="https://danny270degree.blogspot.com/2022/03/graphql-golang-gin-frameworkgraphqlapi.html" style="color: #3778cd; text-decoration-line: none;"><span style="font-size: medium;">[GraphQL] 使用Golang Gin Framework來建置GraphQL的API Server</span></a></li></ul></ul></div><p><span style="font-size: medium;"><span><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">本篇內容將提供快速的指引,使用Golang Gin Framework來建置</span></span></span></span><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">GraphQL的API </span></span></span><span style="font-family: arial;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">Server。</span></span></span></span></p><p><br /></p><h3><span style="font-size: medium;">[使用Golang Gin Framework來建置GraphQL的API Server]</span></h3><p style="text-align: left;">GraphQL API Server主體上來說是參考下列文章的方式來建置:</p><p><a href="https://ithelp.ithome.com.tw/articles/10213118"><span style="vertical-align: inherit;">第 1 天 - API 服務器(Go,GraphQL) - 第 1 部分</span></a></p><p><a href="https://ithelp.ithome.com.tw/articles/10213461"><span style="vertical-align: inherit;">第 2 天 - API 服務器(Go,GraphQL) - 第 2 部分</span></a></p><p>Golang Gin Server網路上已經有很多資源,我們假設已經有的前提下,如何來建置GraphQL的API Server。首先,先安裝所需的模組:</p><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; font-family: arial; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><i><span style="color: #999999;">#在個人自己的<span style="font-family: "Times New Roman";">Gin Server 的 project 內</span></span></i><br /><span style="color: #333333;">$ go get github.com/99designs/gqlgen</span></div><div data-plaintext="true" style="color: #333333;">$ go get github.com/gin-gonic/gin</div></div></div></div></div><p style="font-family: arial; font-size: large;">我們需要準備下列檔案: </p><span><a name='more'></a></span><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; color: #333333; font-family: arial; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true">$ tree -L 1</div><div data-plaintext="true">.</div><div data-plaintext="true">├── gqlgen.yml</div><div data-plaintext="true">├── internal</div><div data-plaintext="true">├── scripts</div><div data-plaintext="true"> ......</div></div></div></div></div><p style="font-family: arial; font-size: large;">gqlgen.yml 的內容如下:</p></div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; color: #333333; font-family: arial; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;"># go-gql-server gqlgen.yml file</span></div><div> <span style="color: #6a9955;"># Refer to https://gqlgen.com/config/</span></div><div> <span style="color: #6a9955;"># for detailed .gqlgen.yml documentation.</span></div><br /><div> <span style="color: #569cd6;">schema</span>:</div><div> - <span style="color: #ce9178;">internal/gql/schemas/schema.graphql</span></div><div> <span style="color: #6a9955;"># Let gqlgen know where to put the generated server</span></div><div> <span style="color: #569cd6;">exec</span>:</div><div> <span style="color: #569cd6;">filename</span>: <span style="color: #ce9178;">internal/gql/generated/generated.go</span></div><div> <span style="color: #569cd6;">package</span>: <span style="color: #ce9178;">gql</span></div><div> <span style="color: #6a9955;"># Let gqlgen know where to put the generated models (if any)</span></div><div> <span style="color: #569cd6;">model</span>:</div><div> <span style="color: #569cd6;">filename</span>: <span style="color: #ce9178;">internal/gql/models/generated/generated.go</span></div><div> <span style="color: #569cd6;">package</span>: <span style="color: #ce9178;">models</span></div><div> <span style="color: #6a9955;"># Let gqlgen know where to put the generated resolvers</span></div><div> <span style="color: #569cd6;">resolver</span>:</div><div> <span style="color: #569cd6;">filename</span>: <span style="color: #ce9178;">internal/gql/resolvers/generated/generated.go</span></div><div> <span style="color: #569cd6;">type</span>: <span style="color: #ce9178;">Resolver</span></div><div> <span style="color: #569cd6;">package</span>: <span style="color: #ce9178;">resolvers</span></div><div> <span style="color: #569cd6;">autobind</span>: []</div></div></div><div style="font-size: large;"><br /></div></div></div></div><p style="font-family: arial; font-size: large;">scripts/gqlgen.sh 的內容如下:</p><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; color: #333333; font-family: arial; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #6a9955;">#!/bin/bash</span></div><div> <span style="color: #dcdcaa;">printf</span> <span style="color: #ce9178;">"\nRegenerating gqlgen files\n"</span></div><div> rm -f internal/gql/generated/generated.go \</div><div> internal/gql/models/generated/generated.go \</div><div> internal/gql/resolvers/generated/generated.go</div><div> <span style="color: #569cd6;">time</span> gqlgen <span style="color: #9cdcfe;">$1</span></div><br /><div> cp internal/gql/generated/generated.go internal/gql</div><div> mv internal/gql/generated.go internal/gql/main.go</div><div> cp internal/gql/models/generated/generated.go internal/gql/models</div><div> mv internal/gql/models/generated.go internal/gql/models/users.go</div><div> cp internal/gql/resolvers/generated/generated.go internal/gql/resolvers</div><div> mv internal/gql/resolvers/generated.go internal/gql/resolvers/main.go</div><br /><div> <span style="color: #dcdcaa;">printf</span> <span style="color: #ce9178;">"\nDone.\n\n"</span></div></div></div><div style="font-size: large;"><br /></div></div></div></div><p style="font-family: arial; font-size: large;">透過執行 scripts/gqlgen.sh, 會generate codes (包含 generated、models、resolvers、schemas),並放在internal資料夾內,其檔案樹狀結構如下:</p><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; color: #333333; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><span style="font-family: arial;">$ tree -L 3</span></div><div data-plaintext="true"><span style="font-family: arial;">.</span></div><div data-plaintext="true"><span style="font-family: arial;">├── gql</span></div><div data-plaintext="true"><span style="font-family: arial;">│ ├── generated</span></div><div data-plaintext="true"><span style="font-family: arial;">│ │ └── generated.go</span></div><div data-plaintext="true"><span style="font-family: arial;">│ ├── main.go</span></div><div data-plaintext="true"><span style="font-family: arial;">│ ├── models</span></div><div data-plaintext="true"><span style="font-family: arial;">│ │ ├── generated</span></div><div data-plaintext="true"><span style="font-family: arial;">│ │ └── users.go</span></div><div data-plaintext="true"><span style="font-family: arial;">│ ├── resolvers</span></div><div data-plaintext="true"><span style="font-family: arial;">│ │ ├── generated</span></div><div data-plaintext="true"><span style="font-family: arial;">│ │ └── main.go</span></div><div data-plaintext="true"><span style="font-family: arial;">│ └── schemas</span></div><div data-plaintext="true"><span style="font-family: arial;">│ └── schema.graphql</span></div><div data-plaintext="true"><span style="font-family: arial;">└── handlers</span></div><div data-plaintext="true"><span style="font-family: arial;"> └── gql.go</span></div><div style="font-family: arial; font-size: large;"><br /></div></div></div></div><p style="font-family: arial; font-size: large;"><i><span style="color: #999999;">備註: </span></i></p><p style="font-family: arial; font-size: large;"><i><span style="color: #999999;">在<a href="https://www.agiliq.com/blog/2020/04/graphql-api-server-using-golang-gin-framework/">GraphQL api server using golang Gin framework</a> 有提到可以用下列方式產生初步的GraphQL骨架,所以不一定要用上述的方式</span></i></p><div class="language-sh highlighter-rouge" style="background-color: white; box-sizing: inherit; font-family: "Noto Sans", sans-serif; font-size: 16px;"><div class="highlight" style="box-sizing: inherit;"><pre class="highlight" style="background: rgb(33, 39, 51); border-left: 8px solid rgb(60, 70, 84); box-sizing: inherit; font-family: monospace, monospace; font-size: 1em; margin-bottom: 1.25rem; margin-top: 0px; overflow: auto; padding: 1.5625rem;"><code style="background-color: transparent; border-radius: 0px; box-sizing: inherit; font-family: "Source Code Pro", monospace; font-size: inherit; padding: 0px; word-break: normal;"><i><span style="color: #999999;"><span class="nv" style="box-sizing: inherit;">$ </span>go run github.com/99designs/gqlgen init</span></i></code></pre></div></div><p style="font-family: arial; font-size: large;">我們需要實作相關檔案:</p><p><span style="font-family: arial; font-size: medium;">internal/handlers/gql.go</span></p></div><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; color: #333333; font-family: arial; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">package</span> handlers</div><br /><div><span style="color: #569cd6;">import</span> (</div><div> <span style="color: #ce9178;">"microsegmentation/internal/gql"</span></div><div> <span style="color: #ce9178;">"microsegmentation/internal/gql/resolvers"</span></div><br /><div> <span style="color: #ce9178;">"github.com/99designs/gqlgen/handler"</span></div><div> <span style="color: #ce9178;">"github.com/gin-gonic/gin"</span></div><div>)</div><br /><div><span style="color: #6a9955;">// GraphqlHandler defines the GQLGen GraphQL server handler</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">GraphqlHandler</span>() gin.HandlerFunc {</div><div> <span style="color: #6a9955;">// NewExecutableSchema and Config are in the generated.go file</span></div><div> <span style="color: #9cdcfe;">c</span> := gql.Config{</div><div> Resolvers: &resolvers.Resolver{},</div><div> }</div><br /><div> <span style="color: #9cdcfe;">h</span> := handler.<span style="color: #dcdcaa;">GraphQL</span>(gql.<span style="color: #dcdcaa;">NewExecutableSchema</span>(c))</div><br /><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">func</span>(c *gin.Context) {</div><div> h.<span style="color: #dcdcaa;">ServeHTTP</span>(c.Writer, c.Request)</div><div> }</div><div>}</div><br /><div><span style="color: #6a9955;">// PlaygroundHandler Defines the Playground handler to expose our playground</span></div><div><span style="color: #569cd6;">func</span> <span style="color: #dcdcaa;">PlaygroundHandler</span>(path <span style="color: #4ec9b0;">string</span>) gin.HandlerFunc {</div><div> <span style="color: #9cdcfe;">h</span> := handler.<span style="color: #dcdcaa;">Playground</span>(<span style="color: #ce9178;">"Go GraphQL Server"</span>, path)</div><div> <span style="color: #c586c0;">return</span> <span style="color: #569cd6;">func</span>(c *gin.Context) {</div><div> h.<span style="color: #dcdcaa;">ServeHTTP</span>(c.Writer, c.Request)</div><div> }</div><div>}</div></div></div></div></div></div><div><p><span style="font-family: arial; font-size: medium;">internal/resolvers/main.go</span></p></div><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; color: #333333; font-family: arial; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div style="line-height: 19px;"><div><span style="color: #569cd6;">package</span> resolvers</div><br /><div><span style="color: #569cd6;">import</span> (</div><div> <span style="color: #ce9178;">"context"</span></div><br /><div> <span style="color: #6a9955;">// remeber to change the import path</span></div><div> gql <span style="color: #ce9178;">"microsegmentation/internal/gql"</span></div><div> models <span style="color: #ce9178;">"microsegmentation/internal/gql/models"</span></div><div>)</div><br /><div><span style="color: #6a9955;">// THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.</span></div><br /><div><span style="color: #569cd6;">type</span> <span style="color: #4ec9b0;">Resolver</span> <span style="color: #569cd6;">struct</span>{}</div><br /><div><span style="color: #569cd6;">func</span> (r *Resolver) <span style="color: #dcdcaa;">Mutation</span>() gql.MutationResolver {</div><div> <span style="color: #c586c0;">return</span> &mutationResolver{r}</div><div>}</div><div><span style="color: #569cd6;">func</span> (r *Resolver) <span style="color: #dcdcaa;">Query</span>() gql.QueryResolver {</div><div> <span style="color: #c586c0;">return</span> &queryResolver{r}</div><div>}</div><br /><div><span style="color: #569cd6;">type</span> <span style="color: #4ec9b0;">mutationResolver</span> <span style="color: #569cd6;">struct</span>{ *Resolver }</div><br /><div><span style="color: #569cd6;">func</span> (r *mutationResolver) <span style="color: #dcdcaa;">CreateUser</span>(ctx context.Context, input models.UserInput) (*models.User, <span style="color: #4ec9b0;">error</span>) {</div><div> <span style="color: #dcdcaa;">panic</span>(<span style="color: #ce9178;">"not implemented"</span>)</div><div>}</div><div><span style="color: #569cd6;">func</span> (r *mutationResolver) <span style="color: #dcdcaa;">UpdateUser</span>(ctx context.Context, input models.UserInput) (*models.User, <span style="color: #4ec9b0;">error</span>) {</div><div> <span style="color: #dcdcaa;">panic</span>(<span style="color: #ce9178;">"not implemented"</span>)</div><div>}</div><div><span style="color: #569cd6;">func</span> (r *mutationResolver) <span style="color: #dcdcaa;">DeleteUser</span>(ctx context.Context, userID <span style="color: #4ec9b0;">string</span>) (<span style="color: #4ec9b0;">bool</span>, <span style="color: #4ec9b0;">error</span>) {</div><div> <span style="color: #dcdcaa;">panic</span>(<span style="color: #ce9178;">"not implemented"</span>)</div><div>}</div><br /><div><span style="color: #569cd6;">type</span> <span style="color: #4ec9b0;">queryResolver</span> <span style="color: #569cd6;">struct</span>{ *Resolver }</div><br /><div><span style="color: #569cd6;">func</span> (r *queryResolver) <span style="color: #dcdcaa;">Users</span>(ctx context.Context, userID *<span style="color: #4ec9b0;">string</span>) ([]*models.User, <span style="color: #4ec9b0;">error</span>) {</div><div> <span style="color: #6a9955;">// this is for test purpose</span></div><div> <span style="color: #569cd6;">var</span> <span style="color: #9cdcfe;">tempID</span> = <span style="color: #ce9178;">"ec17af15-e354-440c-a09f-69715fc8b595"</span></div><div> <span style="color: #569cd6;">var</span> <span style="color: #9cdcfe;">tempEmail</span> = <span style="color: #ce9178;">"your@email.com"</span></div><div> <span style="color: #569cd6;">var</span> <span style="color: #9cdcfe;">tempUserID</span> = <span style="color: #ce9178;">"UserID-1"</span></div><div> <span style="color: #9cdcfe;">records</span> := []*models.User{</div><div> &models.User{</div><div> ID: &tempID,</div><div> Email: &tempEmail,</div><div> UserID: &tempUserID,</div><div> },</div><div> }</div><div> <span style="color: #c586c0;">return</span> records, <span style="color: #569cd6;">nil</span></div><div>}</div></div></div></div></div></div></div><p style="font-family: arial; font-size: large;"><br /></p></div></div><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">完成後,就可以在自己的Gin Server上加入GraphQL相關的API Routes</span></p><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; color: #333333; font-family: arial; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; line-height: 19px; white-space: pre;"><div style="line-height: 19px;"><div> <span style="color: #6a9955;">//GraphQL</span></div><div> <span style="color: #6a9955;">// Playground handler</span></div><div> r.<span style="color: #dcdcaa;">GET</span>(<span style="color: #ce9178;">"/heartbeat"</span>, handlers.<span style="color: #dcdcaa;">Heartbeat</span>())</div><div> r.<span style="color: #dcdcaa;">GET</span>(<span style="color: #ce9178;">"/gql"</span>, handlers.<span style="color: #dcdcaa;">PlaygroundHandler</span>(<span style="color: #ce9178;">"/gql"</span>))</div><div> r.<span style="color: #dcdcaa;">POST</span>(<span style="color: #ce9178;">"/gql"</span>, handlers.<span style="color: #dcdcaa;">GraphqlHandler</span>())</div></div></div></div></div></div></div><div><p><br /></p><p><span style="font-family: arial; font-size: medium;">透過瀏覽器 http://<<your ip address>>/gql 即可看到下列畫面:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhal8RGmidCOsNmCtMnJBBJ3vHu1F6BejUHL3ye5-0TzSjaX9D3ktWPf5qrkHfdUx4gqx9hUWwI2D0SBeeUaL_dbu2RpYnzdPHNxBc5et-XaWAkuMYrvJ1smdiXlssY0epR4kLnakR_v0xrlQW64Cx4NCgG8qLtX2jyViCvuR4KbnnuVkX2NieEgeg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="309" data-original-width="1091" height="182" src="https://blogger.googleusercontent.com/img/a/AVvXsEhal8RGmidCOsNmCtMnJBBJ3vHu1F6BejUHL3ye5-0TzSjaX9D3ktWPf5qrkHfdUx4gqx9hUWwI2D0SBeeUaL_dbu2RpYnzdPHNxBc5et-XaWAkuMYrvJ1smdiXlssY0epR4kLnakR_v0xrlQW64Cx4NCgG8qLtX2jyViCvuR4KbnnuVkX2NieEgeg=w640-h182" width="640" /></a></div><p></p></div></div><div><br /></div><div><h3><span style="font-size: medium;">[相關其他參考]</span></h3><p><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">有許多線上資源可以參考,</span></span>在此不再贅述。請參考下列相關連結:</p><p><a href="https://www.agiliq.com/blog/2020/04/graphql-api-server-using-golang-gin-framework/" style="font-family: arial; font-size: large;">GraphQL api server using golang Gin framework</a></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><p><span style="font-family: arial; font-size: medium;">Schema</span></p></div></blockquote><div style="text-align: left;"><p></p><ul style="text-align: left;"><ul><li><span style="font-family: arial; font-size: medium;">Schema is the way to provide structure of the object or resource. Based on the schema we can query or mutate the data/resource on the GraphqL server.</span></li></ul></ul><p></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><p><span style="font-family: arial; font-size: medium;">Queries</span></p></div></blockquote><div style="text-align: left;"><p></p><ul style="text-align: left;"><ul><li><span style="font-family: arial; font-size: medium;">Queries are used to retrieve the data from the GraphQL server.</span></li></ul></ul><p></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><p><span style="font-family: arial; font-size: medium;">Mutations</span></p></div></blockquote><div style="text-align: left;"><p></p><ul style="text-align: left;"><ul><li><span style="font-family: arial; font-size: medium;">Mutations are used to update/modify the resource on the server.</span></li></ul></ul><p></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><p><span style="font-family: arial; font-size: medium;">Resolvers</span></p></div></blockquote><div><p></p><ul style="text-align: left;"><ul><li><span style="font-family: arial; font-size: medium;">Resolvers are used to create the data structure that matches with the provided resource schema.</span></li></ul></ul><div><span style="font-family: arial; font-size: medium;"><br /></span></div><p></p><p><b><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">在這篇文章: </span></span><a href="https://blog.51cto.com/rongfengliang/3122832"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">使用 GraphQL 滿足現代業務需求</span></span></a> </b></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"></p><ul style="text-align: left;"><li><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">GraphQL 架構</span></span></li></ul><p></p></blockquote><div><p></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhQt5ykinwk6CRxG9-BiJd6cm7xD-5Wu1sn7Hu3qFCvj2-xURAkgs9u974AkUMsto6KMw8ZFX84apjIozT6U9ELV-06m3taX3Bc5nnGNYFyfM9A_u1HJ1BPdnLGeobMzZJMIys3imnK__W4h8G_roknlItCkHqVlyQQUY7nrTaQYIM_rnv_FQT1UYo" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="505" data-original-width="996" height="324" src="https://blogger.googleusercontent.com/img/a/AVvXsEhQt5ykinwk6CRxG9-BiJd6cm7xD-5Wu1sn7Hu3qFCvj2-xURAkgs9u974AkUMsto6KMw8ZFX84apjIozT6U9ELV-06m3taX3Bc5nnGNYFyfM9A_u1HJ1BPdnLGeobMzZJMIys3imnK__W4h8G_roknlItCkHqVlyQQUY7nrTaQYIM_rnv_FQT1UYo=w640-h324" width="640" /></a></div></div></blockquote><div><p><br /></p><p><a href="https://segmentfault.com/a/1190000011263214"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;"><b>一篇文章幫你理清GraphQL的核心概念(原文)</b></span></span></a></p></div><div><ul style="background-color: white; box-sizing: border-box; color: #212529; font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin-bottom: 1.25rem; margin-top: 0px; padding-left: 2rem; text-align: left;"><li><span style="box-sizing: border-box; font-weight: bolder;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">GraphQL 的特徵:</span></span></span></li><ul><li style="box-sizing: border-box;"><span style="box-sizing: border-box; vertical-align: inherit;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">可描述性的:使用GraphQL,你獲取的都是你想要的數據,很少也不會少;</span></span></span></li><li style="box-sizing: border-box;"><span style="box-sizing: border-box; vertical-align: inherit;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">請求的對象,請求的對象的一個簡單的對象:GraphQL自然遵循間的關係,我們可以通過簡單的對象請求,我們獲取到一個我們相關的對象,通過簡單的對象,可以獲取一個和她創建的所有文章,然後可以獲取文章的所有評論;</span></span></span></li><li style="box-sizing: border-box;"><span style="box-sizing: border-box; vertical-align: inherit;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">強類型的:使用GraphQL的類型系統,我們可以描述被服務器查詢的可能的數據,確保從服務器獲取到的數據和我們查詢的一致;</span></span></span></li><li style="box-sizing: border-box;"><span style="box-sizing: border-box; vertical-align: inherit;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">不做限制:現在並沒有綁定到某種語言的某種語言,實際上已經有了某種語言;</span></span></span></li><li style="box-sizing: border-box;"><span style="box-sizing: border-box; vertical-align: inherit;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">後台代碼:QL不存在特定的數據庫,甚至可以使用連接關聯的API。</span></span></span></li><li style="box-sizing: border-box;"><span style="box-sizing: border-box; vertical-align: inherit;"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">好反省的:GraphQL服務器查詢架構的細節。</span></span></span></li></ul></ul><p><br /></p><p><a href="https://f2e.kalan.dev/advanced/24.html#query-%E8%88%87-mutation"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;"><b>GraphQL</b></span></span></a></p><p></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgruP2B5JtwiYSmNKN_0LtIZAryUYOKXFL8sVYsrIguJ0lBfSaFV0Pn18TxV2IcISuWAIsCXf_8EHfrSidgB4RS1U15A4cwWR8VSrMF_HxqvHYW1Uu6XQCgR0E8-l5q4XH2urAtUInAiA3zjD-j2TI12SaZRZXsUJ8J7Q5JcauYj1rjUhcH9TVqLWQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="285" data-original-width="676" height="270" src="https://blogger.googleusercontent.com/img/a/AVvXsEgruP2B5JtwiYSmNKN_0LtIZAryUYOKXFL8sVYsrIguJ0lBfSaFV0Pn18TxV2IcISuWAIsCXf_8EHfrSidgB4RS1U15A4cwWR8VSrMF_HxqvHYW1Uu6XQCgR0E8-l5q4XH2urAtUInAiA3zjD-j2TI12SaZRZXsUJ8J7Q5JcauYj1rjUhcH9TVqLWQ=w640-h270" width="640" /></a></div></div></blockquote><div><br /></div><div><p></p><p><a href="https://blog.51cto.com/rongfengliang/3122832"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">使用 GraphQL 滿足現代業務需求</span></span></a></p><p><a href="https://ishellworld.blogspot.com/2019/04/graphql.html"><span style="vertical-align: inherit;"><span style="vertical-align: inherit;">GraphQL - 漸進式導入的架構</span></span></a></p><p><br /></p><p><br /></p></div>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-87322881240567714152022-03-18T11:14:00.003+08:002022-03-18T21:15:25.341+08:00[Golang] Go 1.16 中關於go get和go install的變動<p> </p><h3><span style="font-family: arial; font-size: medium;">[前言]</span></h3><div><span style="font-family: arial; font-size: medium;">其實Go 1.16已經 Release 很久了,但最近發現關於 go get 的行為跟以前不一樣並且多了 go install 這個 tool,所以本篇記錄一下需要注意的地方。</span></div><div></div><div><span style="font-family: arial; font-size: medium;"><br /></span></div><div><span style="font-family: arial; font-size: medium;"><h3 style="font-family: "Times New Roman";"><span style="font-family: arial; font-size: medium;">[關於go get和go install的變動]</span></h3><p style="text-align: left;"><span style="font-family: arial; font-size: medium;">我目前Golang的版本如下:</span></p><p style="text-align: left;"><span></span></p><a name='more'></a><span style="font-family: arial; font-size: medium;"><br /></span><p></p></span></div><div><span style="font-family: arial;"><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true">$ go version</div><div data-plaintext="true">go version go1.16.2 linux/amd64</div></div></div></div></div><p style="font-family: "Times New Roman"; font-size: large; text-align: left;"><span style="font-family: arial;">在 Go 1.16 中,行為變更是 go build 和 go test 不會自動編輯 go.mod 了,</span><span style="font-family: arial;">go get 將二進位制安裝相關的功能都轉移到了 go install, </span><span style="font-family: arial;">僅作為用於編輯 go.mod 檔案的命令,例如為專案增加外部的模組,</span><span style="font-family: arial;">在後續版本(計劃是 Go 1.17)中刪掉 go get 安裝二進位制的功能。</span></p><p><span style="font-size: large;">需要注意的是 go install <package>@<version> 是從 1.16 開始增加的,</span><span style="font-size: large;">無論你當前是否在一個模組下,此命令都會在 $GOPATH/bin 下安裝指定版本的工具。另外,</span><span style="font-size: large;">go install <package>@<version></span><span style="font-size: large;"> 只能安裝有main package的軟體包(換句話說是有可執行的程式)。</span></p><p><span style="font-size: large;">此外由於 Go 1.16 中 GO111MODULE 預設是開啟的,go install 不會修改 go.mod 之類的檔案(不會造成任何模組衝突的意外)。</span></p><p><span style="font-size: large;">總結以上資訊,Go 1.16 中將進行如下處理:</span></p><p></p><ul style="text-align: left;"><li><span style="font-size: medium;">通過在程式碼中修改 import 語句,來修改 go.mod:</span></li><li><span style="font-size: medium;"> go get 可用於新增新模組;</span></li><li><span style="font-size: medium;"> go mod tidy 刪除掉無用的模組;</span></li></ul><p></p><p><span style="font-family: arial; font-size: medium;"></span></p><div><span style="font-family: arial; font-size: medium;"><br /></span></div><div><span style="font-family: arial; font-size: medium;">Reference:</span></div><div><a href="https://iter01.com/572906.html">https://iter01.com/572906.html</a></div><div><br /></div></span></div><p><br /></p>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0tag:blogger.com,1999:blog-2395906491961203124.post-5366494120159213252022-03-16T15:11:00.005+08:002022-08-29T15:32:20.082+08:00[React] 從無到有建立全新的 React 應用程式<h3><span style="font-family: arial; font-size: medium;">[前言]</span></h3><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;">本篇內容將提供快速的指引,</span><span style="font-family: arial;">包含相關需安裝的套件,</span><span style="font-family: arial;">從無到有建立全新的 React 應用程式(</span><span style="font-family: arial;"> single-page application, SPA</span><span style="font-family: arial;">)</span></span></p><p style="text-align: left;"><span style="font-size: medium;"><span style="font-family: arial;"><br /></span></span></p><div><h3><span style="font-family: arial; font-size: medium;">[安裝]</span></h3><p><span style="font-size: medium;">請先下載 node.js 打包檔於 <a href="https://nodejs.org/en/">https://nodejs.org/en/</a></span></p><p><span style="font-size: medium;">本次範例是使用 </span><a href="https://nodejs.org/dist/v16.17.0/node-v16.17.0-linux-x64.tar.xz"><span style="font-size: medium;">node version v16.17.0 (.tar.gz file) </span></a></p><p><span style="background-color: #fefefe; color: #333333; font-family: arial; font-size: medium; text-indent: 16px;">安裝步驟如下:</span></p><p><span></span></p><a name='more'></a><span style="background-color: #fefefe; color: #333333; font-family: arial; font-size: medium; text-indent: 16px;"><br /></span><p></p><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true" style="font-family: arial;">$ tar xvf node-v16.17.0-linux-x64.tar.xz</div><div data-plaintext="true"><div data-plaintext="true" style="font-family: arial;">$ cd node-v16.17.0-linux-x64</div><div data-plaintext="true" style="font-family: arial;">$ sudo cp -rf bin/* /usr/local/bin/</div><div data-plaintext="true" style="font-family: arial;">$ sudo cp -rf include/* /usr/local/include/</div><div data-plaintext="true" style="font-family: arial;">$ sudo cp -rf lib/* /usr/local/lib/</div><div data-plaintext="true" style="font-family: arial;"><br /></div><div data-plaintext="true" style="font-family: arial;">$ node -v</div><div data-plaintext="true"><span style="font-family: arial;">v14.17.0</span></div><div data-plaintext="true"><span style="font-family: arial;">$ npm</span></div><div data-plaintext="true"><span style="font-family: arial;">6.14.13</span></div></div></div></div></div><p><span style="font-size: medium;">完成後,可以安裝相關套件</span></p><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true" style="font-family: arial;">$ sudo npm install --global yarn</div><div data-plaintext="true"><div data-plaintext="true" style="font-family: arial;">$ yarn --version</div><div data-plaintext="true"><span style="font-family: arial;">1.22.10</span></div></div></div></div></div><h3><span style="font-family: arial; font-size: medium;"><br /></span></h3><h3><span style="font-family: arial; font-size: medium;">[</span>Create React App]</h3><p><span style="font-size: medium;">Create React App 是React官方推薦的適合學習 React 的環境,而且也是使用 React 建立一個全新的 single-page 應用程式的最佳方法。</span></p><p><span style="font-size: medium;">它會為你設定好開發環境,以便你能夠使用最新的 JavaScript 特性,提供良好的開發者體驗,並且為線上環境最佳化你的應用程式。你需要在你的機器上安裝 Node >= 14.0.0 and npm >= 5.6。要建立項目,請執行:</span></p><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><div data-plaintext="true"><span style="font-family: arial;">$ npx create-react-app my-app</span></div><div data-plaintext="true"><span style="font-family: arial;"><br /></span></div><div data-plaintext="true"><span style="font-family: arial;">$ cd my-app</span></div><div data-plaintext="true"><span style="font-family: arial;"><br /></span></div><div data-plaintext="true"><span style="font-family: arial;">$ npm start</span></div><div data-plaintext="true"><span style="font-family: arial;">//or</span></div><div data-plaintext="true"><span style="font-family: arial;">$ yarn start</span></div></div><div data-plaintext="true"><div><span style="font-family: arial;"><br /></span></div></div></div></div></div><p><span style="font-size: medium;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-size: medium;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEigM40zQNAv5EhvQJ6a-CqNcTBuXRhTAyOvN9wXMBZOr52-q2tRGeCxnvWL2Uex2s7wCuAmAH8ddhZVda5yzHX1UjbVk6KdiQG8Tk_pUReColy6jjKVbHbNVhcR70G_tegTHPuzyZRUjWRbAYd5DpxYB2btceHKEQX_xJj3Qqja_TSZ95h4A-Ye-NU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="345" data-original-width="619" height="357" src="https://blogger.googleusercontent.com/img/a/AVvXsEigM40zQNAv5EhvQJ6a-CqNcTBuXRhTAyOvN9wXMBZOr52-q2tRGeCxnvWL2Uex2s7wCuAmAH8ddhZVda5yzHX1UjbVk6KdiQG8Tk_pUReColy6jjKVbHbNVhcR70G_tegTHPuzyZRUjWRbAYd5DpxYB2btceHKEQX_xJj3Qqja_TSZ95h4A-Ye-NU=w640-h357" width="640" /></a></span></div><p></p><p><span style="font-size: medium;">React也已經支援TypeScript,若需要專案使用Typescript,以下列方式可快速建好</span></p><div><div data-codeblock="true" style="background-color: #fbfaf8; border-radius: 4px; border: 1px solid rgba(0, 0, 0, 0.15); box-sizing: border-box; padding: 8px;"><div data-plaintext="true"><div data-plaintext="true"><span style="font-family: arial;">$ create-react-app </span><span style="font-family: arial;">my-app</span><span style="font-family: arial;"> <b>--typescript</b></span></div></div></div></div></div>TeYen (Danny)http://www.blogger.com/profile/12494975466289886246noreply@blogger.com0