你們好,我叫謝偉,是一名程序員。node
今天的主題:kubernetes 學習前言,主要和容器相關。mysql
容器技術誕生後,成爲雲計算領域的絕對主角,但容器自己價值並不大,任何互聯網領域都涉及到部署,容器編排才重要。創造docker 的dotCloud 的公司並無獲取到雲計算領域的紅利,雖然以後也推出的自家的 docker swarm 應用於容器編排,相比設計理念更爲先進的 kubernetes,存在更多的問題,事實上 k8s 已經成爲容器編排領域的領頭羊。幾乎全部的互聯網公司,雲計算公司都使用 k8s 用於容器編排。nginx
簡單的說容器編排的意思是:將應用容器化,按照一套規則自動在節點按照用戶的需求部署。這套規則由k8s 規定,開發者應用容器化,按照規則編寫編排腳本便可。git
總體上 k8s設計理念先進,得益於 Google 領域內的多年實踐總結,儘管如此,對開發者而言,掌握 k8s 知識,卻有點複雜。程序員
複雜體如今兩方面:github
集羣部署有規格要求,若是你是初學者,想要親手部署這樣一套集羣服務,環境可能都沒有。基礎的容器組件拉取也多是問題,不過這一狀況正在好轉。golang
開發者需掌握 k8s 諸多的概念,另外得掌握編排腳本語法約束,不過,只要肯花時間均可以掌握。web
簡單的說 k8s 適用於複雜的部署,多節點,多應用,系統越複雜,上 k8s 調度起來更方便。簡單的系統,應用容器化,運行容器便可,若是多應用使用單節點部署方案 docker-compose 便可。redis
容器技術給應用創造一個徹底獨立的環境,能夠跨平臺使用,我認爲是每個開發者須要掌握的技術之一。sql
如何使用容器技術?
容器技術主要包四大組成部分:
何爲鏡像:簡單的說,包含虛擬運行環境的文件包,是一堆文件的合集,服務在該系統之上可以運行起來。docker 鏡像採用了分層架構。
何爲容器:簡單的說,鏡像的運行狀態,用來隔離虛擬環境的基礎設施。主要包含:鏡像、運行環境、指令集
何爲網絡:網絡是應用之間通信的媒介。
何爲數據卷:應用確定會涉及到數據持久化操做,數據卷就是用於宿主機和容器之間共享或者持久化。
容器技術的實現: 得益於如下三點技術
問題一: Namespace 怎麼就能作到隔離?
容器本質上是進程,Linux操做系統提供了 PID, Mount, Network 等 namespace, 使被隔離的進程只能看到當前的環境狀態。
比方說 PID namespace, 啓動容器執行命令,爲何該進程的 PID 是 1 呢?
>> ps -ef | grep 1
root 1 0 0 14:08 ? 00:00:01 /go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything
root 15 0 0 15:20 pts/0 00:00:00 bash
root 33 15 0 15:22 pts/0 00:00:00 ps -ef
root 34 15 0 15:22 pts/0 00:00:00 grep 1
複製代碼
其中 /go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything
是我本身的容器啓動時的命令。
主要緣由是:Linux 建立進程能夠指定 PID
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
複製代碼
相似的其餘也能夠依靠對應的 namespace 技術作到隔離。
問題二:control Groups 怎麼就可以如今計算機資源的使用了?
首先限制的資源包括:CPU, 內存,磁盤,網絡等。若是你常用 docker,必定會常常看到一些目錄: /sys/fs/cgroup
>> mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
複製代碼
以 CPU 爲例,如何實現對資源的限制呢?
首先查看我本機運行的容器:
>> docker ps --format "{{.ID}}: {{.Status}}"
81f4b0f829c9: Up 4 days
226749434f46: Up 4 days
3102306bf580: Up 4 days
複製代碼
>> ls /sys/fs/cgroup/cpu/docker
drwxr-xr-x 2 root root 0 Dec 1 07:31 226749434f46cc4197a24c09e691536b679b372735f1197608bc9085c1d95d9f
drwxr-xr-x 2 root root 0 Nov 26 08:44 3102306bf58037012bd5d2b6f595ae00450125d4152ed53d2db71d35ab297439
drwxr-xr-x 2 root root 0 Nov 26 14:17 81f4b0f829c9558228691c979af188b30cfaec01dd78b97d90e95aff00cbfb99
-rw-r--r-- 1 root root 0 Dec 1 07:30 cgroup.clone_children
-rw-r--r-- 1 root root 0 Dec 1 07:30 cgroup.procs
-r--r--r-- 1 root root 0 Dec 1 07:30 cpuacct.stat
-rw-r--r-- 1 root root 0 Dec 1 07:30 cpuacct.usage
-r--r--r-- 1 root root 0 Dec 1 07:30 cpuacct.usage_all
-r--r--r-- 1 root root 0 Dec 1 07:30 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Dec 1 07:30 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Dec 1 07:30 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Dec 1 07:30 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Dec 1 07:30 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Dec 1 07:30 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Dec 1 07:30 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Dec 1 07:30 cpu.shares
-r--r--r-- 1 root root 0 Dec 1 07:30 cpu.stat
-rw-r--r-- 1 root root 0 Dec 1 07:30 notify_on_release
-rw-r--r-- 1 root root 0 Dec 1 07:30 tasks
>> cd 226749434f46cc4197a24c09e691536b679b372735f1197608bc9085c1d95d9f
-rw-r--r-- 1 root root 0 Dec 1 07:31 cgroup.clone_children
-rw-r--r-- 1 root root 0 Nov 26 14:06 cgroup.procs
-r--r--r-- 1 root root 0 Dec 1 07:31 cpuacct.stat
-rw-r--r-- 1 root root 0 Dec 1 07:31 cpuacct.usage
-r--r--r-- 1 root root 0 Dec 1 07:31 cpuacct.usage_all
-r--r--r-- 1 root root 0 Dec 1 07:31 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Dec 1 07:31 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Dec 1 07:31 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Dec 1 07:31 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Dec 1 07:31 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Dec 1 07:31 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Dec 1 07:31 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Dec 1 07:31 cpu.shares
-r--r--r-- 1 root root 0 Dec 1 07:31 cpu.stat
-rw-r--r-- 1 root root 0 Dec 1 07:31 notify_on_release
-rw-r--r-- 1 root root 0 Dec 1 07:31 tasks
複製代碼
沒錯,docker 實現對資源的限制靠的是寫文件的形式,即對相似這樣的文件進行限制:假設:cpu.cfs_period_us(10000),cpu.cfs_quota_us(2000)代表只能使用到20 % 的CPU 資源。
cpu.cfs_period_us
cpu.cfs_quota_us
...
複製代碼
問題三:聯合文件系統是什麼意思?
將多個位置的不一樣目錄掛載到同一目錄下。
好比:
>> tree
├── A
│ ├── a
│ └── x
└── B
├── b
└── x
複製代碼
掛載到 C 目錄下:
$ tree C
├── a
├── b
└── x
複製代碼
固然實際的實現比我說的要複雜的多。docker 正是依靠這三種技術,使的能夠爲咱們創造一個徹底隔離的沙箱,這樣作的好處是:環境的一致性,方便部署。你會愈來愈少的問這樣一個問題:我本地能夠運行啊?爲何在服務器上就不行。
docker 採用典型的 C/S 架構,你安裝 docker 軟件。默認會在本機啓動一個守護進程 docker daemon , 同時提供一個命令行客戶端 docker cli . 你可使用命令行操做包括:鏡像、容器等各類資源。
根據 docker 的四大組成部分,docker cli 的命令主要圍繞這四個命令展開:
你可能會以爲命令行命令太多,怎麼辦?創造高頻使用環境便可,即:平常工做中頻繁的不斷的使用docker 便可。
Docker Hub 是最大的鏡像託管平臺,做用是:1. 鏡像存儲 2. 鏡像分發。無數的開源軟件的官方鏡像託管在該平臺,用戶能夠徹底免費的從該平臺拉取鏡像,完成本身的任務。
那麼對於開發者如何製做本身的鏡像呢?
答案是:編寫 Dockerfile 文件。
docker 公司對於製做鏡像,約束了一套語法規範,其實命令也就 10幾個。
先不談具體的命令是什麼,假設你本身開發了一套系統,沒有容器以前,你部署應用會執行什麼操做?
這是否是大概的操做流程?
是的,dockerfile 將這一系列動做指令化。
舉個例子:我這邊本身寫了個服務,對外暴露的是 API,我如今想製做鏡像,我會怎麼作呢?
FROM golang:1.13.4
MAINTAINER XieWei(1156143589@qq.com)
EXPOSE 8888 8081
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime;\
echo "Asia/Shanghai" > /etc/timezone;\
dpkg-reconfigure -f noninteractive tzdata
WORKDIR /go/src/github.com/wuxiaoxiaoshen/go-anything
RUN echo $PWD
COPY . /go/src/github.com/wuxiaoxiaoshen/go-anything
RUN apt-get update && apt-get install -q -y vim nginx git openssh-client cron && apt-get clean;\
go mod vendor;\
make remove;\
make prod;\
echo Succeed!
CMD ["bash","-c", "/go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything"]
複製代碼
簡單的說:
dockerfile 文件編寫並不用太複雜,就能夠按照開發者的約束製做鏡像。很是便利。若是記不住命令,仍然建議自身創造高頻使用環境,在平常工做中,有意無心的多使用。
通常我會把製做好的鏡像直接推送至 Docker Hub, 須要使用到再從 Docker Hub 上拉取,固然你須要先註冊個帳號。
仍是我這個應用:go-anything, 實質它是個 web 服務,爲創造稍微複雜的系統,這個系統,我使用到了 MySQL, Redis, Kafka 等技術集成進來。
若是這些 MySQL, Redis, Kafka 等都選擇的是雲服務,go-anything 中指定相應的雲服務地址便可,至關於沒有外部依賴。固然我沒錢買雲服務,這些服務都選擇的是在本地啓動相應的容器,這麼說,這實際上是個多應用的編排的問題。
若是是單節點或者測試環境,那麼我推薦使用 docker-compose 來連接和啓動多服務。我這個服務依賴於上面三個服務,這三個服務啓動以後,才能正確的運行服務。
那麼什麼是 docker-compose, 簡單的說,是用 Python 編寫的命令行工具,用來定義和運行由多個容器組成的應用。它規定了一套語法規範,這些規範的關鍵字都和上文提到的容器相關。
就我這個應用,我怎麼編寫 docker-compose 呢?
version: "3"
services:
redis:
image: redis:latest
ports:
- 6379:6379
expose:
- 6379
container_name: redis_for_go_anything
command: redis-server --appendonly yes --requirepass "adminRedis"
networks:
- go-anything-network
volumes:
- data:/data
mysql:
image: mysql:latest
container_name: mysql_for_go_anything
networks:
- go-anything-network
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: adminMysql
MYSQL_DATABASE: go-anything
MYSQL_USER: root
ports:
- 3306:3306
kafka:
image: index.docker.io/wurstmeister/kafka:latest
container_name: kafka_for_go_anything
ports:
- 9092:9092
environment:
KAFKA_OFFSETS_TOPIC_REPLIATION_FACTOR: 1
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://127.0.0.1:9092"
KAFKA_LISTENERS: "PLAINTEXT://:9092"
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_CREATE_TOPICS: "go-anything:20:1:compact"
KAFKA_LOG_DIRS: /kafka/kafka-logs
depends_on:
- zookeeper
networks:
- go-anything-network
volumes:
- data:/kafka/kafka-logs
zookeeper:
image: index.docker.io/wurstmeister/zookeeper:latest
container_name: zookeeper_for_go_anything
ports:
- 2181:2181
networks:
- go-anything-network
networks:
go-anything-network:
driver: bridge
volumes:
data: {}
複製代碼
看上去稍微複雜點,其實能夠劃分爲四個部分:
version: 版本
services: 服務
volume: 數據卷
network: 網絡
複製代碼
其中最重要的是 services,像 network, volume 這些,不寫均可以使用默認的,但我通常都會寫,顯式化得定義 network 和 volume。
單獨抽出 redis 服務來看,實際上是關於容器的操做:
redis:
image: redis:latest
ports:
- 6379:6379
expose:
- 6379
container_name: redis_for_go_anything
command: redis-server --appendonly yes --requirepass "adminRedis"
networks:
- go-anything-network
volumes:
- data:/data
複製代碼
redis: 服務的名稱,自定義 image: 鏡像地址和版本 ports: 主機和容器端口映射 expose: 暴露容器端口 container_name: 容器自定義名稱 command: 容器啓動時命令 networks: 指定網絡 volume: 數據卷定義
這些好比端口,執行命令等,開發者有時候記不住,怎麼辦? 我也記不住,都是查看 docker hub 上相應的文檔說明,畢竟這是別人製做的鏡像,不看文檔,怎麼知道怎麼作。
編排好上文三個服務:MySQL, Redis, Kafka,怎麼一鍵讓服務運行?
>> docker-compose -f docker-compose.yml up -d
複製代碼
能夠命名爲 docker-compose.yml,也能夠不這樣命名。不指定文件,自動會在當前目錄下尋找 docker-compose.yml。docker-compose 還提供了其餘命令,絕大多數也是用來操做鏡像和容器的。
假設我把個人應用已經推送至 docker hub 上:wuxiaoshen/go-anything:v0.19
我如今應該怎麼啓動個人服務呢?
>> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wuxiaoshen/go-anything v0.19 1788461daca4 30 hours ago 1.43GB
>> docker run --name go-anything-2 --link mysql_for_go_anything --link redis_for_go_anything --link kafka_for_go_anything --net go-anything_go-anything-network -p 8081:8888 -d 1788461daca4
複製代碼
其中:
--name 指定容器名稱
--link 連接服務
-p 端口映射
--net 指定網絡
-d 後臺運行
複製代碼
爲何要 --link 服務呢?由於我項目的配置文件是這樣的:
mysql:
port: "3306"
db: go-anything
user: root
passwd: adminMysql
host: mysql
redis:
port: "6379"
auth: adminRedis
host: redis
kafka:
broker: kafka:9092
topic: go-anything
consumerGroup: go-anything-consumer-group
複製代碼
其中對應的 host 和 docker-compose.yml 對應的services 中命名的服務名稱一致。這樣能夠直接使用服務名稱代替 host,而不用顯式的指定主機地址(127.0.0.1)
這樣咱們就啓動了多應用:
>> docker ps --format "{{.ID}}: {{.Command}}: {{.Ports}}"
c7d820406af2: "bash -c /go/src/git…": 8081/tcp, 0.0.0.0:8081->8888/tcp
d197ec955421: "start-kafka.sh": 0.0.0.0:9092->9092/tcp
50cdda796143: "/bin/sh -c '/usr/sb…": 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp
3cb75161e860: "docker-entrypoint.s…": 0.0.0.0:3306->3306/tcp, 33060/tcp
46d16bd9837e: "docker-entrypoint.s…": 0.0.0.0:6379->6379/tcp
複製代碼
啓動服務中指定了映射端口:將宿主機的 8081 和 容器內的 8888 的端口相互映射,而應用指定的端口是:8888,這樣本機訪問 8081 端口能夠訪問到容器 8888 端口。
# 項目內指定端口
router.Run("8888")
複製代碼
測試下:
>> docker logs -f c7d820406af2
2019/12/01 14:08:46 Env: service
2019/12/01 14:08:46 Web Start...
2019/12/01 14:08:46 Step 0: Configs...
2019/12/01 14:08:46 Step 1: Mysql...
2019/12/01 14:08:46 configs: LoadConfigs: key: service.mysql
2019/12/01 14:08:46 Keys: MySQL: map[string]interface {}{"db":"go-anything", "host":"mysql", "passwd":"adminMysql", "port":"3306", "user":"root"}
2019/12/01 14:08:46 root:adminMysql@tcp(mysql:3306)/go-anything?charset=utf8&parseTime=True&loc=Local
2019/12/01 14:08:47 Step 2: Redis...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.redis
2019/12/01 14:08:47 Keys: Redis: map[string]interface {}{"auth":"adminRedis", "host":"redis", "port":"6379"}
2019/12/01 14:08:47 Step 3: Kafka...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.kafka
2019/12/01 14:08:47 Keys: Kafka: map[string]interface {}{"broker":"kafka:9092", "consumergroup":"go-anything-consumer-group", "topic":"go-anything"}
2019/12/01 14:08:47 Step 4: Email...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.email
// 省略
[DBUG] 2019/12/01 14:08 GET: /v1/api/status/health -> github.com/wuxiaoxiaoshen/go-anything/src/Healthz.healthZHandler() and 2 more
// 省略
[DBUG] 2019/12/01 14:08 Application: running using 1 host(s)
[DBUG] 2019/12/01 14:08 Host: addr is :8888
[DBUG] 2019/12/01 14:08 Host: virtual host is 0.0.0.0:8888
[DBUG] 2019/12/01 14:08 Host: register startup notifier
[DBUG] 2019/12/01 14:08 Host: register server shutdown on interrupt(CTRL+C/CMD+C)
Now listening on: http://0.0.0.0:8888
Application started. Press CTRL+C to shut down.
複製代碼
>> curl http://127.0.0.1:8081/v1/api/status/health | jq .
{
"code": 200,
"data": "pong",
"status": "ok"
}
複製代碼
能夠看到完成了多應用的啓動部署。
能夠看到單節點上部署多應用,其實 docker-compose 是個很好的工具,那假如多節點呢?docker-compose 徹底無用武之地啊。
以後我會仍然使用這個示例在 k8s 集羣上進行啓動部署。
下期:k8s 基本概念。
代碼:go-anything
<全文完>