# 用 Docker 架設班級 git-it 練習環境 ###### COSCUP 2016 ###### By 和風信使 ( [@taichunmin](https://github.com/taichunmin) ) --- #### 投影片 <http://goo.gl/3aHqNd> #### 原檔 <https://goo.gl/EiUL61> ###### 等等會有一些程式碼 ###### 在投影片上面不容易複製 ###### 建議可以在原檔複製 --- # DEMO! --- # 緣起 --- ## 網路上有很多 git 教學 --- ## 但是幾乎大多數 --- ## 都是模擬的系統 --- ## 偶然之間 ## 得知有 git-it ## 這套教學軟體 --- ## 除了要在終端機下 ## 實機操作 git --- ## 還要實機操作 GitHub --- ## Very Good --- ## BUT --- ## 但是社團跟系上借的 ## 電腦教室 --- ## 全部都有裝還原卡 --- ## 一堂課的時間很寶貴 --- ## 光安裝系統就飽了 --- ## 練習 git-it 根本來不及 --- ## 而且電腦教室的網路 ## 所有人一起抓軟體時 --- ## 就會慢到跟烏龜一樣 --- ## 於是希望 ## 可以省掉 git-it 安裝 --- ## 還希望能有即時成績看板 ### ~~ACM 中毒~~ --- # 使用 docker 架設 git-it 環境 --- ## 首先 我們需要 --- ## 能執行 Docker 的主機 --- ## 以下是我用過的虛擬主機 ### DigitalOcean * `Create Droplet`→`One-click Apps`→`Docker 1.x.x on 14.04` ### AWS EC2 * `Ubuntu Server 14.04 LTS (HVM)`→自己安裝 Docker --- ## 如果你需要安裝 Docker * [官方 Linux 安裝腳本](https://get.docker.com/) * [Ubuntu](https://docs.docker.com/engine/installation/linux/ubuntulinux/) * [其他版本](https://docs.docker.com/engine/installation/) > 如果你用 DigitalOcean 的 `Docker 1.x.x on 14.04` 就可以跳過啦。 --- ## 實做時間 * 請大家完成到可成功測試 Docker ```sh sudo service docker start sudo docker run hello-world ``` --- ## 接下來我們 ## 要來寫 Dockerfile --- # Dockerfile 是什麼? --- #### Dockerfile 包含創建映像檔所需要的全部指令。 #### 我們可以使用 `docker build` 指令來創建映像檔。 #### 通過減少映像檔和容器的創建程序來簡化部署。 --- ## 先來看幾個 ## Dockerfile 的訣竅 --- ## 撰寫 Dockerfile 訣竅 1. 先開一個基礎的容器 ```sh # 開啟一個基礎的 ubuntu:14.04 指令 sudo docker run -ti ubuntu:14.04 /bin/bash ``` 2. 按照安裝教學安裝一次 3. 將指令改成 non-interactive (不須互動,自動安裝) 4. 再把指令複製到 Dockerfile --- ## git-it 安裝教學 #### <https://github.com/jlord/git-it/blob/master/original-readme.md> --- ## 系統更新 ```sh sudo apt-get update sudo apt-get upgrade ``` * non-interactive ```sh apt-get update -qq apt-get upgrade -y ``` --- ## git-it 需求 * Git * Node.js * 純文字編輯器 (Text Editor) * 英文語系 (English locales) --- ## git 安裝 ```sh sudo apt-get install git ``` * non-interactive ```sh apt-get install -y git ``` --- ## Node.js & npm 安裝 ```sh sudo apt-get install nodejs npm # 由於 git-it 預設使用 node 執行,故須 link sudo ln -s /usr/bin/nodejs /usr/bin/node ``` * non-interactive ```sh apt-get install -y nodejs npm ln -s /usr/bin/nodejs /usr/bin/node ``` --- ## 純文字編輯器 (Text Editor) ```sh sudo apt-get install vim nano ``` * non-interactive ```sh apt-get install -y vim nano ``` --- ## git-it 安裝 ```sh sudo npm install -g git-it ``` * non-interactive ```sh npm install -g git-it ``` --- ## Dockerfile 小結 ```dockerfile FROM ubuntu:14.04 RUN apt-get update -qq RUN apt-get upgrade -y RUN apt-get install -y git nodejs npm vim nano RUN ln -s /usr/bin/nodejs /usr/bin/node RUN npm install -g git-it ``` --- ## 接下來 --- ## 該怎麼讓學員 ## 進到自己的蘿蔔坑呢 --- ## 我們只有很少的主機 --- ## 一台主機上有很多個 client --- ## 不能讓學員 ## 從 host 進入 client #### ~~r m - r f /~~ --- # SSH ###### Secure Shell --- ## 然後再把 ## 每個 client 的 SSH 埠 --- ## port forwarding 出來 --- # BUT --- ## Docker 容器 ## 通常預設沒有 ssh --- ## 架設 ssh 教學 * Docker 官方範例 <https://goo.gl/edqX2W> --- # Dockerfile 小結 ```dockerfile # ... 略 RUN apt-get install -y openssh-server RUN mkdir /var/run/sshd RUN echo 'root:git-it' | chpasswd RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config RUN sed 's@sessions*requireds*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"] ``` --- # 現在 --- ## 你的 Dockerfile ## 已經足以練習 git-it --- # BUT --- ## 一次面對一個班級的時候 ## 還是有很多問題 --- ## 該怎麼快速開很多 client? --- # ~~docker start~~ # docker-compose --- ## 安裝 docker-compose 請注意 Docker Compose 的版本。 ```bash curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose docker-compose --version ``` > 參考網址 > > 1. <https://docs.docker.com/compose/install/> > 2. <https://github.com/docker/compose/releases> --- # docker-compose.yml ```yml version: '2' services: client: build: client/ ports: - "22" ``` > YML 的縮行很重要 --- ## 開多台 client * 可自由指定數量 ```sh sudo docker-compose up -d sudo docker-compose scale client=5 ``` --- # VPS 的 RAM 太小? --- ## 設定 swap (選擇性) 如果開的機器 RAM 較小,則建議手動新增 swap 以供 Docker 使用。 * <https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04> --- ## 該怎麼指派 ## SSH PORT 給學員? --- ## 查詢 port forwarding 列表 ```sh docker-compose ps ``` ``` Name Ports -------------------------------------------------------------------- gititcoursedocker_client_1 0.0.0.0:32794->22/tcp scoreboard 0.0.0.0:22000->22/tcp, 0.0.0.0:80->80/tcp ``` --- ## client 的密碼 ## 所有人都知道 ## 該怎麼辦? --- ## 強制登入後更改密碼 在 Dockerfile 加上 ```Dockerfile RUN chage -d 0 root ``` --- ## 實做時間 * 使用 SSH 進入 client * 登入後立即要求更改 root 密碼 * 輸入 `git-it` 後進入選題選單 --- ## 接下來是比較進階的主題 --- # scoreboard --- # scoreboard ## 能幫助我們什麼呢? --- ## scoreboard 能幫我們... * 顯示 client 的 SSH PORT * 顯示哪個 client 還沒有人使用 * 顯示學員的名字 * 顯示學員的解題數 * 使用網頁公開資料 --- ## scoreboard 該怎麼用程式 ## 抓取 client 的 SSH PORT? --- #### 先幫 client 的 Dockerfile 加上 LABEL ```Dockerfile # Dockerfile LABEL role="git-it-client" ``` --- ## 使用 LABEL 進行過濾 * 在 Docker Remote API [文件](https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#list-containers) [範例](https://github.com/taichunmin/git-it-course-scoreboard/blob/master/app/Console/Commands/DockerRemotePortsUpdate.php) ```sh curl http://host/containers/json?filters={"label":["role=git-it-client"]} ``` * docker ps [範例](https://github.com/taichunmin/git-it-course-docker/blob/master/port-reporter.sh) ```sh docker ps --filter "label=role=git-it-client" --format '"{{.ID}}":{{.Ports}}' ``` --- ## 接下來是 client 的回報程式 --- ## client 回報是否有人使用 1. 查詢登入密碼是否已被更改 ```sh OWNED=`[ "never" = "$(chage -l root | grep 'Password expires' | sed -r 's/^[^:]+: //')" ] && echo '1' || echo '0'` ``` 2. client 定期 (cron) 回報給 scoreboard --- ## client 回報學員的名字 git-it 的題目會要求幫 git 設定使用者的名字和 GitHub 的帳號,所以直接抓 git 的 `user.name` 和 `user.username` 就好了。 ```sh NAME=`git config --global user.name || hostname` GITHUB=`git config --global user.username || echo ''` ``` --- ## 顯示學員的解題數 git-it 會把解題數存放在 `~/.config/git-it/completed.json` ```sh COMPLETED=`test -r /root/.config/git-it/completed.json && cat /root/.config/git-it/completed.json || echo []` ``` --- ## client 需安裝 curl ```sh sudo apt-get install curl ``` * non-interactive ```sh apt-get install -y curl ``` --- ## 回報資料給 scoreboard ``` #!/bin/bash COMPLETED=`test -r /root/.config/git-it/completed.json && cat /root/.config/git-it/completed.json || echo []` MID=`hostname` NAME=`git config --global user.name || hostname` GITHUB=`git config --global user.username || echo ''` OWNED=`[ "never" = "$(chage -l root | grep 'Password expires' | sed -r 's/^[^:]+: //')" ] && echo '1' || echo '0'` curl --data-urlencode "completed=${COMPLETED}" --data-urlencode "mid=${MID}" --data-urlencode "name=${NAME}" --data-urlencode "github=${GITHUB}" --data-urlencode "owned=${OWNED}" http://scoreboard/completed/update ``` --- ## 為什麼剛剛的回報程式 ## 可以用 scoreboard 當作主機? --- ## 因為 Docker 的 LINK ## 會很貼心的幫我們設定 ## DNS 解析 `/etc/hosts` --- ## 於 client 中 ## 架設 crontab 定期回報 --- ## 撰寫 `/etc/crontab` 每分鐘回報 client 的資料給 scoreboard,以 root 身份執行。 ```cron * * * * * root /usr/bin/scoreboard-reporter.sh >> /dev/null 2>&1 ``` --- ## client Dockerfile 加入 cron * 參考 <https://github.com/aptible/docker-cron-example> ```Dockerfile RUN apt-get -y install rsyslog ADD crontab /etc/crontab ADD client-start.sh /usr/bin/client-start.sh ADD scoreboard-reporter.sh /usr/bin/scoreboard-reporter.sh RUN chmod +x /usr/bin/client-start.sh RUN chmod +x /usr/bin/scoreboard-reporter.sh RUN touch /var/log/cron.log ``` --- ## client 啟動腳本 由於我們需要執行的程式已經超過一個了,為了避免 Dockerfile 的 CMD 指令太過複雜,所以我們要開新 client 要執行的指令都寫在 `client-start.sh` ```sh #!/bin/sh # client-start.sh sleep 5 rsyslogd cron /usr/sbin/sshd # 刪掉 -D 參數 /usr/bin/scoreboard-reporter.sh # 開機時先回報一次 tail -F /var/log/syslog /var/log/cron.log ``` --- ## 修改 Dockerfile CMD 指令 ```Dockerfile # 要把舊的刪除,因為只有最後一個 CMD 有效 CMD /usr/bin/client-start.sh ``` --- ## 實作時間 * 請參考 <https://github.com/taichunmin/git-it-course-docker/tree/master/client> * client 完成到可以回報資料給 scoreboard --- # scoreboard # [scoreboard Dockerfile](https://github.com/taichunmin/git-it-course-docker/tree/master/scoreboard) --- ## 主要接收 client 的回報 ## 還會去跟 Docker Remote API ## 取得每個 client ## 所對應的 PORT --- ## 單純就是一個 HTTP Server --- ## 由於不是每個人都會 PHP ## 所以就不深入談了 --- ## 我已經事先把 scoreboard ## build 到 Docker Hub --- ## 可以直接用 Docker Hub ## 來寫 `docker-compose.yml` --- #### scoreboard 的 `docker-compose.yml` --- ```yml version: '2' services: scoreboard: # build: scoreboard/ image: taichunmin/git-it-course-docker:scoreboard ports: - "80:80" - "22000:22" container_name: scoreboard volumes: - /var/run/docker.sock:/var/run/docker.sock ``` * port 80 是網頁伺服器 * port 22 是 SSH,為了方便就固定對應到 22000 * 設定了 `container_name` 可以避免誤開多台 * 設定 `volumes` 可以讓 scoreboard 存取 Remote API --- ## 須開放 Docker Remote API ## 給 scoreboard --- ## 開放 Docker Remote API 利用 Volumes ```Dockerfile version: '2' services: scoreboard: # 省略 volumes: - /var/run/docker.sock:/var/run/docker.sock ``` --- ## 然後我們需要修改 client 的 ## `docker-compose.yml` --- ```yml version: '2' services: client: build: client/ ports: - "22" links: - scoreboard depends_on: - scoreboard ``` * 讓 docker-compose 自動對應 port 22 出來 * LINK 到 scoreboard,讓 client 可以回報資料 * `depends_on` 讓 docker-compose 確保有 scoreboard --- # 到此結束 # 歡迎提問
{"metaMigratedAt":"2023-06-14T15:44:56.836Z","metaMigratedFrom":"YAML","title":"用 Docker 架設班級 git-it 練習環境","breaks":true,"contributors":"[{\"id\":\"0d9a5e06-1f92-4142-b9df-fed4c8873573\",\"add\":17,\"del\":0}]"}
    2608 views