Skip to main content

5 posts tagged with "docker"

View All Tags

· 6 min read
zaxro

安裝 kubeadm 遇到的問題

kubeadm 用於建立和管理叢集的基礎結構,而 kubectl 用於操作和管理叢集中的資源。這兩個工具在 Kubernetes 的部署和管理中扮演著不同的角色,安裝的官網介紹,

  1. 先安裝 docker(這邊是參考官網,也可以用其他種 container)
info

因為原本 centos 是內建 podman,但這邊為了符合 k8s 網站的教學所以使用 docker,我自己在安裝 docker 過程會遇到跟 podman 的衝突,像這樣

Docker CE Stable - x86_64                                                                                                                    133 kB/s | 3.5 kB     00:00
错误:
问题 1: problem with installed package podman-docker-3:4.3.1-2.module_el8.8.0+1254+78119b6e.noarch
- package docker-ce-3:24.0.2-1.el8.x86_64 conflicts with docker provided by podman-docker-3:4.3.1-2.module_el8.8.0+1254+78119b6e.noarch
- package docker-ce-3:24.0.2-1.el8.x86_64 conflicts with docker provided by podman-docker-3.1.0-0.13.module_el8.5.0+733+9bb5dffa.noarch
- package docker-ce-3:24.0.2-1.el8.x86_64 conflicts with docker provided by podman-docker-3.3.0-0.15.module_el8.5.0+870+f792de72.noarch
- package docker-ce-3:24.0.2-1.el8.x86_64 conflicts with docker provided by podman-docker-3.3.0-0.17.module_el8.5.0+874+6db8bee3.noarch
- package docker-ce-3:24.0.2-1.el8.x86_64 conflicts with docker provided by podman-docker-3.3.0-2.module_el8.5.0+877+1c30e0c9.noarch

解法是先移除 podman,並優先使用 docker 的 repo.安裝在 centos 可以看這篇

  1. 移除 podman
yum remove podman
  1. 擴展工具集
yum install -y yum-utils
  1. 添加軟體庫
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  1. 修改權限,把/etc/yum.repos.d/docker-ce.repo加上 priority=10,這樣就不會跟/etc/yum.repos.d/CentOS-Stream-AppStream.repo衝突!
/etc/yum.repos.d/docker-ce.repo
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://download.docker.com/linux/centos/8/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://download.docker.com/linux/centos/gpg
priority=10

5.安裝 docker

yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

6.起服務

systemctl start docker
  1. 安裝 kubelet kubeadm kubectl
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

# Set SELinux in permissive mode (effectively disabling it)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

sudo systemctl enable --now kubelet

基本上這邊會發現 kubelet 服務報錯無法起來,接著是講解法!

  1. 使用kubeadm init依然會報錯,這個指令目的是建立 master node(也就是 control plane),包括 API Server、Controller Manager 和 Scheduler。當您運行 kubeadm init 時,它將生成初始化控制平面所需的配置文件、證書和其他資源。kubeadm init報錯如下
    [ERROR CRI]: container runtime is not running: output: time="2023-05-29T16:13:25+08:00" level=fatal msg="validate service connection: CRI v1 runtime API is not implemented for endpoint \"unix:///var/run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"

以上報錯如何解決

這個問題是因為/etc/containerd/config.toml設定有問題的關係 ,解法有兩種

  • 最簡單解法把/etc/containerd/config.toml 刪掉(可以先備份),這會讓 k8s 使用預設資料
rm /etc/containerd/config.toml
  • 比較複雜解法就要了解 /etc/containerd/config.toml 如何設定,預設是 disabled_plugins
disabled_plugins = ["cri"]

要改成 enabled_plugins,表示啟用 CRI 插件,因為 k8s 預設使用 CRI(Container Runtime Interface)來管理跟運行容器,如果禁用 CRI 插件或没有指定有效的容器配置,Kubernetes 將會報錯!

enabled_plugins = ["cri"]
[plugins."io.containerd.grpc.v1.cri".containerd]
endpoint = "unix:///var/run/containerd/containerd.sock"
  1. allow user to use this cluster
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
  1. Setup Your Pod Network
  • 建立檔案network.yml
network.yml在這!
network.yml
---
kind: Namespace
apiVersion: v1
metadata:
name: kube-flannel
labels:
k8s-app: flannel
pod-security.kubernetes.io/enforce: privileged
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: flannel
name: flannel
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
- apiGroups:
- networking.k8s.io
resources:
- clustercidrs
verbs:
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: flannel
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-flannel
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: flannel
name: flannel
namespace: kube-flannel
---
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
labels:
tier: node
k8s-app: flannel
app: flannel
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds
namespace: kube-flannel
labels:
tier: node
app: flannel
k8s-app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
hostNetwork: true
priorityClassName: system-node-critical
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: flannel
initContainers:
- name: install-cni-plugin
image: docker.io/flannel/flannel-cni-plugin:v1.1.2
#image: docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.2
command:
- cp
args:
- -f
- /flannel
- /opt/cni/bin/flannel
volumeMounts:
- name: cni-plugin
mountPath: /opt/cni/bin
- name: install-cni
image: docker.io/flannel/flannel:v0.22.0
#image: docker.io/rancher/mirrored-flannelcni-flannel:v0.22.0
command:
- cp
args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel
image: docker.io/flannel/flannel:v0.22.0
#image: docker.io/rancher/mirrored-flannelcni-flannel:v0.22.0
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
resources:
requests:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: false
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EVENT_QUEUE_DEPTH
value: "5000"
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
- name: xtables-lock
mountPath: /run/xtables.lock
volumes:
- name: run
hostPath:
path: /run/flannel
- name: cni-plugin
hostPath:
path: /opt/cni/bin
- name: cni
hostPath:
path: /etc/cni/net.d
- name: flannel-cfg
configMap:
name: kube-flannel-cfg
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate
  • 套用設定檔
kubectl apply -f network.yml

會出現以下代表成功

namespace/kube-flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created

檢查確認是否 control plane 是 ready.

kubectl get nodes #確認是否為ready

NAME STATUS ROLES AGE VERSION
73gogo Ready control-plane 3h9m v1.27.2

· 3 min read
zaxro

遇到問題的情境

CICD 步驟裡面,每當開發人員把 code 寫完之後,會進行測試並把 code build 成 image 後推到 image registry ,之後到環境執行 deploy。

在 gitlab 裡面會用 gitlab runner,並在.gitlab-ci.yml 設計促發的情境下,執行.gitlab-ci.yml 內容,要在 gitlab runner 內 build image 通常會用到 docker:dind,它是具有 docker 引擎的 container!一般的 image 內通常都不會去安裝 docker 引擎,這段有點難懂,但你可以想想有 nginx,mysql 等等的 image,基本上只會有自己預設功能,不需要 docker 引擎,對於 docker 而言,裡面要可以用 docker 指令的, docker:dind 是一個選項。

在 build 版執行的 yml 如下。 主要功能就是登入在 gitlab 上面的公司的 image registry,之後在 gitlab runner 執行 build image 並推到 gitlab image registry.

.gitlab-ci.yml
stages:
- build

variables:
IMAGE_NAME: hahaha
CI_IMAGE: $CI_REGISTRY_IMAGE/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA

# Build moni
build-moni:
stage: build
image: docker:dind
tags:
- docker
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build . -t $CI_IMAGE
- docker push $CI_IMAGE
- docker rmi $CI_IMAGE
except:
- schedules


Dockerfile
FROM node:16

COPY /node /node
WORKDIR /node
RUN npm install;npm install gulp -g;gulp build;
EXPOSE 4000
CMD [ "npm", "run", "start" ]

執行過程到這一步- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY遇到報錯, error during connect: Post "http://docker:2375/v1.24/auth": dial tcp: lookup docker on 1.1.1.1:53: no such host

解法:

在你掛到 gitlab runner 中 /etc/gitlab-runner 裡面的 config.toml,請先用 docker inspect 觀察 volume 掛載位置,如果你是用官網 local volume 掛載方法,那就是在 VM 上去 vim /srv/gitlab-runner/config/config.toml,去修改 volumes = ["/cache"] 變成 volumes = ["/var/run/docker.sock:/var/run/docker.sock","/cache"]

並執行docker restart gitlab-runner!

可能原因:

執行這個修改,volumes 改為 ["/var/run/docker.sock:/var/run/docker.sock", "/cache"],讓 Runner Executor 使用主機外部的 Docker Engine。

所以推測,這可能是 gitlab runner 跟 gitlab server 上面驗證的機制,如果沒有這樣加,dind 就找不到 gitlab server 的驗證那關,所以就會失敗

· 2 min read
zaxro

docker create context 是用來建立一個新的 context,以指向一個 remote Docker daemon,用於管理 Docker 主機和 Docker registry 的機制。

可以使用 docker context use 指令來切換 context,切換完成後即可使用 docker 指令操作 remote Docker daemon。如果使用雲端 container,例如 azure web container service,就可以用這個指令去管理雲端 container。包含新建 刪除都可以使用,所以不要太開心一次起一大堆雲端 container 這樣會被 charging 到爆。

info

如果有用到 docker buildx 可能會把 docker context 跟 docker buildx create --name 搞混, 兩個是不同的.

以下是 docker create context 指令的教學:

使用 docker create context 指令可以建立一個新的 context,用來指向一個 remote Docker daemon。以下是指令的詳細說明:

docker context create CONTEXT_NAME --docker "host=REMOTE_DOCKER_HOST" --description "CONTEXT_DESCRIPTION"
  • CONTEXT_NAME: 指定新建立的 context 的名稱。
  • REMOTE_DOCKER_HOST: 指定 remote Docker daemon 的位置,可以是一個 IP 或是一個 domain name。
  • CONTEXT_DESCRIPTION: 可以加入對 context 的描述,方便管理。

例如,以下指令建立一個名為 mycontext 的 context,指向一個 remote Docker daemon,並加入描述:

docker context create mycontext --docker "host=myremotehost:2376" --description "My remote Docker daemon"

建立完成後,可以使用 docker context use 指令來切換 context:

docker context use mycontext

· 7 min read
zaxro

volume of docker

以前都在弄自己的小專案,只要東西跑得起來就好,沒想過會遇過因為硬碟吃滿導致 container 服務掛掉的問題。

docker 掛載 volume 會有三種方式,綁定掛載(Bind Mount),命名卷(Named Volume),匿名卷(Anonymous Volume),前兩種在 container 被刪除時不會被刪,會永久保存,docker 內沒被掛載或使用 Anonymous Volume 則都會被刪除。

tip
  1. docker logs 資料包含哪些?

Docker 會捕捉所有容器的標准輸出(和標准錯誤),並以 JSON 格式將其寫入文件中。 JSON 格式將每行注釋為其源(stdout 或 stderr)和其時間戳。 每個日誌文件僅包含有關一個容器的信息。

  1. docker logs 什麼時候會被刪除? 當 container 被 rm -f 掉的時候 (單純 restart 還會在)!如果要刪除 container 又想保留 docker log,請用docker logs CONTAINER_ID > container.log .

    如果是一般非 docker log,要把打掛載出來

  2. 沒有掛載出來的資料,例如你把 log(非 stdout)寫到某檔案,但該檔案沒被掛載,在本機找得到嗎? 如果沒有掛載出來的資料,例如,程式把特定 log 寫到特定資料夾內(非 standard-out),且你 docker 沒有掛載該資料夾,這樣你在你的 host 主機是不會看到該資料夾的檔案的,他不會在/var/lib/docker 底下,而是在 docker 維護的可寫層中(/var/lib/docker/overlay2),這時候要拿到該檔案,只能透過docker cp指令,把檔案拿出來!

    基本上是建議把 app log 放在 std-out 中,透過 docker 管理 log 的機制去限制 log 大小,像這篇提到的解法,不然可能會遇到的情況像是 db 資料把/var/lib/docker/overlay2 塞爆之類的情況,像這篇

    # Copy the logs out to the host
docker copy CONTAINER_ID:/path/to/your/log_file /host/path/to/store

# Mount a directory for them
docker run -d \
-v /host/path/to/store/logs:/container/path/stored/logs \
your-image

如何減少不必要的硬體佔用

因為 docker 運行過程本身 log 也會佔用硬碟資源,為了不讓這個 log 過度膨脹,可以用以下方式限制

  1. 修改 docker 設定檔
/etc/docker/daemon.json
{
"log-driver":"json-file",
"log-opts": {"max-size":"500m", "max-file":"3"}
}

`systemctl daemon-reload`
`systemctl restart docker
`
  1. 修改 docker-compose.yml
services:
hellow:
build: .
container_name: mattermost
restart: always
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "10"

並使用以下指令重啟,一般 restart 不會吃新 config

docker-compose -f docker-compose.yml up -d --force-recreate mm
danger

下這個指令前務必想好是否重要資訊有用 Bind Mount or Named Volume 掛載出來!

  1. docker 指令
docker run --log-opt max-size=10m --log-opt max-file=5 nginx

官網 log 管理,管理 docker log 方式很多,預設都適用 json-file,設定可已透過修改/etc/docker/daemon.json 並重啟達成。

預設是在同一份檔案,無限大小寫入,透過以上方法,可以限定檔案大小跟可產生檔案數量,當 log 容量到達上限時,Docker 程序將停止寫入該日誌文件,並開始使用新名稱啟動新的日誌文件。默認情況下,新日誌文件將具有與舊日誌文件相同的名稱,但會在末尾添加連續號(例如,access.log.1、access.log.2 等)。

Docker 保留的日誌文件數量由max-file選項決定。默認情況下,Docker 將保留最多 1 個日誌文件(要配置max-size才會生效)。當達到最大日誌文件數量時,Docker 將開始使用新的日誌數據覆蓋最舊的日誌文件。

(ps:資訊以此連結為準,設定可能會改變的)

what should do when you find / or /var are full

info

預設 docker 的東西都放在/var/lib/docker 底下

/var/lib/docker
builder  buildkit  containers  image  network  overlay2  plugins  runtimes  swarm  tmp  trust  volumes

可以在該資料夾底下用du -sh ./*確認哪個地方吃得異常兇, 並用docker system df看資料佔用,如果不用的 image 很多可以清除

docker image prune --all

他會刪除所有沒有被 container 使用的 image,警示如下:

WARNING! This will remove all images without at least one container associated to them.

  1. 如果當初沒有做任何預防,導致/var 真的爆了,可以優先檢查以下幾項

檢查 docker logs 是否佔太兇

其實就是去/var/lib/docker/containers,執行du -sh ./* 看哪個 container 佔最多資源,然後去看裡面那隻放 docker logs 的檔名為 container-id-json.log 的檔案是否長太大,確認後可以砍掉。

檢查 named volume 中存放資訊

其實就是去/var/lib/docker/volumes 底下,看有沒有某個 volume 佔太多資源,然後裡面放的東西可能不重要(例如 nginx log)

檢查 image 跟沒用的 container

image 指令:刪除沒有被活動的 container 使用的 image docker image prune --all

container 指令:刪除停止的 container docker container prune

檢查 /var/lib/docker/overlay2

如果沒有掛載,他會持續把資料寫入這隻,也可能會造成硬碟爆掉

整理

檢查順序

  1. docker system df
  2. 檢查 local volume,docker logs,image,container 佔用情況
  3. 檢查 /var/lib/docker/overlay2

可以的對策有

  1. 如果是 docker logs 那可以限制 docker logs 大小
  2. 如果是 local volume 那就要跟 rd 討論 log 格式了
  3. 如果是/var/lib/docker/overlay2,這個狀況就比較多,ex mysql 對 binary log 的自動清除要開之類的
  4. 沒有在用的 container volume image 用指令清掉就好

· 5 min read
zaxro

基本介紹

danger

如果不支援多平台,就會出現在 arm build,然後在 amd deploy 失敗,上網查不到原因的問題。

Docker Buildx 是 Docker 官方提供的一個命令行工具,用於通過 Dockerfile 构建 Docker 映像。它的主要特點是可以跨平臺構建 Docker 映像,並支持多種 CPU 架構(如 x86、ARM、IBM Power 等),是一個用來建立和管理 multi-arch Docker image 的工具。

info

docker buildx 的單位是 node,跟 docker context 不同,前者是建立影像的主機,可以使用多個節點來加速 Docker 映像的建立過程,後者則是用來管理 Docker 主機和 Docker registry 的機制,定義了一個或多個 Docker 主機和 Docker registry 的設定。

以下是使用 Docker Buildx 構建 Docker 映像的基本步驟:

1. 啟用 multi-arch 支援

要使用 docker buildx 建立 multi-arch Docker image,必須先啟用 multi-arch 支援(Docker Desktop 預配置了 binfmt_misc 對其他平台的支持,可以忽略此指令)。可以使用以下指令啟用 multi-arch 支援:

docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

2. 建立 builder(node)

要使用 docker buildx 建立 multi-arch Docker image,必須先建立一個 builder。使用以下指令建立 builder:

docker buildx create --name mybuilder

3. 設定 builder

建立 builder 後,必須將 builder 設定為目前使用的 builder。使用以下指令設定 builder:

docker buildx use mybuilder

4. 建立 Dockerfile

接下來,建立一個 Dockerfile,並在其中指定要建立的 Docker image。例如,在 Dockerfile 中可以指定要建立的 image 為 ubuntu:latest,並在其中安裝一些軟體套件:

FROM --platform=$TARGETPLATFORM ubuntu:latest

RUN apt-get update && \\
apt-get install -y software-properties-common && \\
add-apt-repository -y ppa:nginx/stable && \\
apt-get update && \\
apt-get install -y nginx

5. 建立 multi-arch Docker image

建立 Dockerfile 後,可以使用以下指令建立 multi-arch Docker image:

docker buildx build --platform linux/arm/v7 -t myimage:latest --load .

在上面的命令中,--platform 參數指定了要構建的 CPU 架構,-t 參數指定了映像的標籤,. 表示當前目錄是 Dockerfile 所在的目錄。

--load 相當於--output=type=docker 沒有指定會出現 warning

WARN[0000] No output specified for docker-container driver. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load

--push 遠端 registry 相當於--output=type=registry

info

https://github.com/docker/buildx/issues/59 2023/3/9 使用--platform 配合--load,不能支援多平台,一次一個平台,否則會報錯 --push 支援一次多平台 image

error: docker exporter does not currently support exporting manifest lists

建立完成後,可以使用以下指令查看建立的 Docker image:

docker images myimage

推送 Docker 映像:

$ docker push myimage:latest

以上就是使用 Docker Buildx 構建 Docker 映像的基本步驟。使用 Docker Buildx 可以讓我們更方便地構建跨平臺的 Docker 映像,提高了開發效率。

info

docker buildx rm docker-test 刪除

docker buildx use docker-test切換

docker buildx create --name my-node 是建立節點嗎?

是的,docker buildx create --name my-node 指令會建立一個名為 my-node 的節點。節點是用來建立 Docker 映像的主機,可以使用多個節點來加速 Docker 映像的建立過程,或者建立不同平台的 Docker 映像。

建立節點時,可以使用 --driver 選項指定節點的驅動程式,例如 docker-containerdocker。驅動程式還可以是其他第三方平台,例如 AWS、Azure 或 Google Cloud。

在建立節點時,可以指定節點的名稱、驅動程式、驅動程式選項、Docker 映像等資訊。例如,以下是建立一個名為 my-node 的節點,使用 Docker 容器作為驅動程式,使用 moby/buildkit:v0.8.1 作為 Docker 映像的示範指令:

docker buildx create --name my-node --driver docker-container --driver-opt image=moby/buildkit:v0.8.1

建立節點後,可以使用 docker buildx ls 指令列出所有節點。建立 Docker 映像時,可以使用 --builder 選項指定要使用的節點。例如:

docker buildx build --builder my-node --platform linux/amd64 -t myimage:latest .

可以使用名為 my-node 的節點建立 Docker 映像,並指定要建立的平台為 linux/amd64。