Skip to content

Instantly share code, notes, and snippets.

@taichunmin
Last active May 21, 2017 12:01
Show Gist options
  • Save taichunmin/1c1dc74b47782b0fa91121f6c8bdf6fb to your computer and use it in GitHub Desktop.
Save taichunmin/1c1dc74b47782b0fa91121f6c8bdf6fb to your computer and use it in GitHub Desktop.
COSCUP 2016 Workshop: 用 Docker 架設班級 git-it 練習環境,投影片使用 Marp 產生。

用 Docker 架設

班級 git-it 練習環境

By 和風信使 ( @taichunmin )

等等會有一些程式碼
在投影片上面不容易複製
建議可以在原檔複製

DEMO!


緣起


網路上有很多 git 教學


但是幾乎大多數


都是模擬的系統


偶然之間

得知有 git-it

這套教學軟體


除了要在終端機下

實機操作 git


還要實機操作 GitHub


Very Good


BUT


但是社團跟系上借的

電腦教室


全部都有裝還原卡


一堂課的時間很寶貴


光安裝系統就飽了


練習 git-it 根本來不及


而且電腦教室的網路

所有人一起抓軟體時


就會慢到跟烏龜一樣


於是希望

可以省掉 git-it 安裝


還希望能有即時成績看板

ACM 中毒


使用 docker 架設 git-it 環境


首先 我們需要


能執行 Docker 的主機


以下是我用過的虛擬主機

DigitalOcean

  • Create DropletOne-click AppsDocker 1.x.x on 14.04

AWS EC2

  • Ubuntu Server 14.04 LTS (HVM)→自己安裝 Docker

如果你需要安裝 Docker

如果你用 DigitalOcean 的 Docker 1.x.x on 14.04 就可以跳過啦。


實做時間

  • 請大家完成到可成功測試 Docker
sudo service docker start
sudo docker run hello-world

接下來我們

要來寫 Dockerfile


Dockerfile 是什麼?


Dockerfile 包含創建映像檔所需要的全部指令。

我們可以使用 docker build 指令來創建映像檔。

通過減少映像檔和容器的創建程序來簡化部署。


先來看幾個

Dockerfile 的訣竅


撰寫 Dockerfile 訣竅

  1. 先開一個基礎的容器
# 開啟一個基礎的 ubuntu:14.04 指令
sudo docker run -ti ubuntu:14.04 /bin/bash
  1. 按照安裝教學安裝一次
  2. 將指令改成 non-interactive (不須互動,自動安裝)
  3. 再把指令複製到 Dockerfile

git-it 安裝教學


系統更新

sudo apt-get update
sudo apt-get upgrade
  • non-interactive
apt-get update -qq
apt-get upgrade -y

git-it 需求

  • Git
  • Node.js
  • 純文字編輯器 (Text Editor)
  • 英文語系 (English locales)

git 安裝

sudo apt-get install git
  • non-interactive
apt-get install -y git

Node.js & npm 安裝

sudo apt-get install nodejs npm
# 由於 git-it 預設使用 node 執行,故須 link
sudo ln -s /usr/bin/nodejs /usr/bin/node
  • non-interactive
apt-get install -y nodejs npm
ln -s /usr/bin/nodejs /usr/bin/node

純文字編輯器 (Text Editor)

sudo apt-get install vim nano
  • non-interactive
apt-get install -y vim nano

git-it 安裝

sudo npm install -g git-it
  • non-interactive
npm install -g git-it

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 教學


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@session\s*required\s*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 的版本。

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

version: '2'
services:
  client:
    build: client/
    ports:
      - "22"

YML 的縮行很重要


開多台 client

  • 可自由指定數量
sudo docker-compose up -d
sudo docker-compose scale client=5

VPS 的 RAM 太小?


設定 swap (建議)

如果開的機器 RAM 較小,則建議手動新增 swap 以供 Docker 使用。


該怎麼指派

SSH PORT 給學員?


查詢 port forwarding 列表

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 加上

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
LABEL role="git-it-client"

使用 LABEL 進行過濾

curl http://host/containers/json?filters={"label":["role=git-it-client"]}
docker ps --filter "label=role=git-it-client" --format '"{{.ID}}":{{.Ports}}'

接下來是 client 的回報程式


client 回報是否有人使用

  1. 查詢登入密碼是否已被更改
OWNED=`[ "never" = "$(chage -l root | grep 'Password expires' | sed -r 's/^[^:]+: //')" ] && echo '1' || echo '0'`
  1. client 定期 (cron) 回報給 scoreboard

client 回報學員的名字

git-it 的題目會要求幫 git 設定使用者的名字和 GitHub 的帳號,所以直接抓 git 的 user.nameuser.username 就好了。

NAME=`git config --global user.name || hostname`
GITHUB=`git config --global user.username || echo ''`

顯示學員的解題數

git-it 會把解題數存放在 ~/.config/git-it/completed.json

COMPLETED=`test -r /root/.config/git-it/completed.json && cat /root/.config/git-it/completed.json || echo []`

client 需安裝 curl

sudo apt-get install curl
  • non-interactive
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 身份執行。

* * * * * root /usr/bin/scoreboard-reporter.sh >> /dev/null 2>&1

client Dockerfile 加入 cron

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

#!/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 指令

# 要把舊的刪除,因為只有最後一個 CMD 有效
CMD /usr/bin/client-start.sh

實作時間


scoreboard


主要接收 client 的回報

還會去跟 Docker Remote API

取得每個 client

所對應的 PORT


單純就是一個 HTTP Server


由於不是每個人都會 PHP

所以就不深入談了


我已經事先把 scoreboard

build 到 Docker Hub


可以直接用 Docker Hub

來寫 docker-compose.yml


scoreboard 的 docker-compose.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

version: '2'
services:
  scoreboard:
    # 省略
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

然後我們需要修改 client 的

docker-compose.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

到此結束

歡迎提問

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment