カテゴリ:パソコン全般 > linux

新しめのラズパイの固定IPの方法はちまたに溢れているが、うちの古いラズパイ2(Jessie)を固定IP化する方法は見つかりにくくなっているので、メモ。

設定に使っているファイルは3つ。
/etc/dhcpcd.conf
/etc/network/interfaces
/etc/wpa_supplicant/wpa_supplicant.conf

IPの固定化
/etc/dhcpcd.confに以下を書き足してある。これで、wlanは192.168.1.100に固定されている。
そして、有線etherは、192.168.2.16に固定。なお、有線etherは直結でのデバッグを想定しているので、dnsとかは設定していない。
interface wlan0
static ip_address=192.168.1.100/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
static domain_search=

interface eth0
static ip_address=192.168.2.16
static routers=
static domain_name_servers=
static domain_search=
WiFiの認証
/etc/network/interfacesの方は、編集するなと書いてあるが、以下の内容で動いている。
allow-hotplug wlan0
#iface wlan0 inet dhcp
iface wlan0 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
そして、/etc/wpa_supplicant/wpa_suppilicant.confは以下。
country=GB
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
        scan_ssid=0       # ssidがステルスモードなら 1
        ssid="XXXXXXXXXXXXXXXX"
        psk=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        proto=RSN         # 認証方式が WPA2-PSK の場合は RSN, WPAならWPA
        key_mgmt=WPA-PSK  #
        pairwise=CCMP     # 暗号方式が TKIP なら TKIP, AESならCCMP
        group=CCMP        # 暗号方式が TKIP なら TKIP, AESならCCMP
}

k8sクラスタ内のnginxにcurlを投げて、ちゃんと返事をしてくれるか確認したい。クラスタ内のサービスを外に露出するには、(公式ではなく)googleによると以下の5つがある。今回は二番目のNodePortを試sした。なお、デフォルトのClusterIPはあくまでクラスタ内部での公開。
  • ClusterIP(デフォルト)
  • NodePort
  • LoadBalancer
  • ExternalName
  • Headless
準備
まずnginxを動かす。yamlはこれ。
$ cat my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
          - containerPort: 80
$ kubectl apply -f my-nginx.yaml
deployment.apps/my-nginx created
そしてその時のservice、endpointにnginxが居ないことを確認。つまり、外から(中からも?)アクセスできない。
$ kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    9h
$ kubectl get endpoints
NAME         ENDPOINTS           AGE
kubernetes   192.168.1.25:6443   9h
ちなみに、nginxがexposeしているportが(当然)80番であることを確認する方法。
$ sudo docker inspect nginx | grep -A2 -i port
            "ExposedPorts": {
                "80/tcp": {}
            },
--
            "ExposedPorts": {
                "80/tcp": {}
            },

NodePortでexposeする
ここからが重要。まず、上のdeploymentと紐付けるservice用のyamlはこれ。
$ cat my-nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-service
spec:
  type: NodePort
  ports:
    - protocol: "TCP"
      port: 8080
      targetPort: 80
      nodePort: 30080
  selector:
    app: nginx
podsの80番のportを30080でexposeするぜ、という宣言。8080番の意味は定かではないが、おそらくクラスタ内部では8080番でアクセスする、ということだと思われる。
$ kubectl apply -f my-nginx-svc.yaml
service/my-nginx-service created
applyしてみると、サービスとエンドポイントの状態にnginxが増えてる。
$ kubectl get svc
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes         ClusterIP   10.96.0.1        <none>        443/TCP          10h
my-nginx-service   NodePort    10.103.190.166   <none>        8080:30080/TCP   7m49s
(base) mm@mm-FMVA05011P:~/kubernetes$ kubectl get endpoints
NAME               ENDPOINTS           AGE
kubernetes         192.168.1.25:6443   10h
my-nginx-service   10.128.0.9:80       7m54s
curlで動作確認
ノードのIP:30080(NodePort)にcurlを投げると、ちゃんとお返事をしてくれました。
$ curl http://192.168.1.25:8888
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
(base) mm@mm-FMVA05011P:~/kubernetes$
参考にしたのはここ。みなさまお世話になります。

テストしてみる(失敗)
k8sの状態がready(下)になったので、アプリがちゃんとデプロイできるかテストしてみる。なお、ややこしくなりそうなので、ラズパイはクラスタから一旦切り離した。
$ kubectl get nodes
NAME            STATUS   ROLES    AGE   VERSION
mm-fmva05011p   Ready    master   8h    v1.16.2
試したアプリは定番のnginx。コマンドはこれ↓
kubectl run nginx --image=nginx
しかし、いつまで立ってもpodの状態がpendingのままである、、、困った。
$ kubectl get deployment
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   0/1     1            0           12s
$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6db489d4b7-rqcxf   0/1     Pending   0          15s

原因調査:ログの取得
デバッグはなんといてもログの確認から。以下のコマンドでログ(イベント)を確認してみた。
$ kubectl get events
LAST SEEN   TYPE      REASON                    OBJECT                        MESSAGE
9m31s       Normal    NodeHasSufficientMemory   node/mm-fmva05011p            Node mm-fmva05011p status is now: NodeHasSufficientMemory
9m31s       Normal    NodeHasNoDiskPressure     node/mm-fmva05011p            Node mm-fmva05011p status is now: NodeHasNoDiskPressure
9m31s       Normal    NodeHasSufficientPID      node/mm-fmva05011p            Node mm-fmva05011p status is now: NodeHasSufficientPID
9m          Normal    RegisteredNode            node/mm-fmva05011p            Node mm-fmva05011p event: Registered Node mm-fmva05011p in Controller
8m59s       Normal    Starting                  node/mm-fmva05011p            Starting kube-proxy.
5m10s       Normal    Starting                  node/mm-fmva05011p            Starting kube-proxy.
80s         Warning   FailedScheduling          pod/nginx-6db489d4b7-pwtmk    0/1 nodes are available: 1 node(s) had taints that the pod didn't tolerate.
3m51s       Normal    SuccessfulCreate          replicaset/nginx-6db489d4b7   Created pod: nginx-6db489d4b7-pwtmk
3m51s       Normal    ScalingReplicaSet         deployment/nginx              Scaled up replica set nginx-6db489d4b7 to 1
すると下の方にWarningとFailedSchedulingというメッセージを発見。この行に書いてある、taintsをググってみると、マスターノードがスケジューリング(アサインメント)の対象になっていないからだいうことが判明しました。ここにまとまっています(感謝)
あるいは、以下でも同じメッセージが確認可能。
$ kubectl describe pods nginx
Name:           nginx-6db489d4b7-pwtmk
Namespace:      default
Priority:       0
Node:           <none>
Labels:         pod-template-hash=6db489d4b7
                run=nginx
Annotations:    <none>
Status:         Pending
IP:            
IPs:            <none>
Controlled By:  ReplicaSet/nginx-6db489d4b7
Containers:
  nginx:
    Image:        nginx
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-8c7b7 (ro)
Conditions:
  Type           Status
  PodScheduled   False
Volumes:
  default-token-8c7b7:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-8c7b7
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age                  From               Message
  ----     ------            ----                 ----               -------
  Warning  FailedScheduling  83s (x3 over 2m24s)  default-scheduler  0/1 nodes are available: 1 node(s) had taints that the pod didn't tolerate.
原因調査: その2
マスターの状態を確認します。ありますね、TaintsのところにNoScheduleとしっかり書いてある。
$ kubectl describe node mm-fmva05011p
Name:               mm-fmva05011p
Roles:              master
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=mm-fmva05011p
                    kubernetes.io/os=linux
                    node-role.kubernetes.io/master=
Annotations:        flannel.alpha.coreos.com/backend-data: {"VtepMAC":"8a:f6:cf:94:f0:e1"}
                    flannel.alpha.coreos.com/backend-type: vxlan
                    flannel.alpha.coreos.com/kube-subnet-manager: true
                    flannel.alpha.coreos.com/public-ip: 192.168.1.25
                    kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Tue, 26 Nov 2019 23:49:06 +0900
Taints:             node-role.kubernetes.io/master:NoSchedule
Unschedulable:      false
対策
taintについているNoScheduleを取るのは以下のコマンド。末尾の"-"がポイント
$ kubectl taint nodes mm-fmva05011p node-role.kubernetes.io/master:NoSchedule-
ちゃんとTaintsが変わっていることを確認。
$ kubectl describe node mm-fmva05011p| grep -i taint
Taints:             <none>
テストしてみる(成功)
しばらく(と言っても一瞬)で、podの状態が変わっていた。
$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6db489d4b7-thkfr   1/1     Running   0          18s
$ kubectl get deployment
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           23s

検証環境をjenkinsから制御したい。実行環境がarmなので、検証環境もarmということで、ラズパイを使うことにする。手順は、
  1. JDKのインストール(スレーブ ホスト)
  2. SSH接続用のkeyの用意(マスター ホスト)
  3. SSH keyのコピー(スレーブ ホスト)
  4. SSH接続設定(マスター jenkins)
  5. 動作確認(マスター jenkins)
1. JDKのインストール
ラズパイで、今、使えるjdkはこんな感じ。
$ apt-cache search oracle jdk
libunsafe-mock-java - Java library providing backported sun.misc.Unsafe class from JDK 8
java-package - Utility for creating Java Debian packages
oracle-java7-jdk - Java™ Platform, Standard Edition 7 Development Kit
oracle-java8-jdk - Java™ Platform, Standard Edition 8 Development Kit
java8のjdkをインストールしてみる。
$ sudo apt-get install oracle-java8-jdk
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています               
状態情報を読み取っています... 完了
oracle-java8-jdk はすでに最新バージョン (8u65) です。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 1 個。
もう、入ってた。

2. SSH接続用のkey作成
これはホスト側で実行する。コマンドはこんな感じ。ちなみにpassphraseは、jenkinsってしました(こんなとこに書く話ではないが)
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/mm/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/mm/.ssh/id_rsa.
Your public key has been saved in /home/mm/.ssh/id_rsa.pub.
3. SSH keyのコピー
上で作ったid_rsa.pubをスレーブ側にコピーして、中身を.ssh/authorized_keysに追加。
cat id_rsa.pub >> .ssh/authorized_keys
chmod 600 .ssh/authorized_keys
4. SSH接続設定
「Jenkinsの管理」→「ノードの管理」→「新規ノード作成」でスレーブを作成する。設定はほぼデフォルト。「Host Key Verification Strategy」だけは、authrized_keysを使うように「Manually trusted key verification strategy」に変更した。

5. 動作確認
nodeの一覧画面をみたら、一台のLinux(arm)機が同期中になっていた。空きスワップ領域のカラムにびっくりマークがついている。4[KB]しかないからだが、とりあえず放置。

6. pipeline設定
とりあえずのpipeline設定はこんな感じ。これもレポジトリ管理したいなー。
node('master'){
  stage('git pull M') {
    git url: 'https://github.com/makotosd/docker_tempmonitor.git', branch: 'master'
  }

  stage('deploy') {
    print "deply"
  }
}

node('raspberrypi-nas') {
   
  stage('git pull') {
    git url: 'https://github.com/makotosd/docker_tempmonitor.git', branch: 'master'
  }
stage('build'){
    def img = docker.build("tmc05/temperature_monitor:${env.BUILD_NUMBER}")
    img.push("${env.BUILD_NUMBER}")
    img.push("latest")
  }
}




ラズパイでインコ環境を監視するためのカメラやら温度計が動いている。結果は、クラウドのDBに送ったりLINEから呼び出したりとしている。

もろもろDocker化+CICDしたくなったので、まずはJenkinsの導入から始めることにした。

1. jenkins(server)イメージの作成
DockerHubにjenkinsのインストールされたベースイメージ(jenkins/jenkins:lts)がある。しかし、このコンテナにはdockerは入っていないので、このままではCICDパイプライン中にdocker buildなどのdockerコマンドを使うことが出来ない。そこで、このベースイメージにdockerをインストールしたmy-jenkinsイメージを作る。Dockerfileはこんな感じ↓ネタ元はQiita
$ cat Dockerfile
FROM jenkins/jenkins:lts

# docker daemonの動いているホストのGIDを指定する
# docker run -v /var/run/docker.sock:/var/run/docker.sock で
# ホストのdocker daemonを共有する前提
ENV DOCKER_GROUP_GID 999

USER root

# docker のバイナリをinstall
RUN wget https://download.docker.com/linux/static/stable/x86_64/docker-18.09.9.tgz
RUN tar -xvf docker-18.09.9.tgz
RUN mv docker/* /usr/bin/

# jenkins userでもdockerが使えるようにする
RUN groupadd -o -g ${DOCKER_GROUP_GID} docker
RUN usermod -g docker jenkins

# jenkinsのuidは1000
# jenkinsユーザを利用するのがベストプラクティス
USER jenkins
このイメージを使って、以下のように起動する。
$ docker build -t my-jenkins:latest .
$ mkdir jenkins
$ cd jenkins/
$ mkdir jenkins_home
$ cd jenkins_home/
$ docker run --name=jenkins -d -p 8080:8080 -p 50000:50000 -v $(pwd):/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock my-jenkins
なお、Dockerfileにある999は、以下のコマンドで事前に調べておいた。
$ ls -lshn /var/run/docker.sock
0 srw-rw---- 1 0 999 0 10月 11 08:15 /var/run/docker.sock
2. ブラウザでjenkinsに接続
つぎに、localhost:8080にブラウザで接続し、以下の設定を行った。
  • unlock
画面に出ているファイルに書いてある文字列を入力して、次に進む。
  • pluginの追加
GitHub
  • userの作成
user名はjenkins、pwはjenjenとしてみた。

3. pipeline作成
そしてようやくpipelineの作成である。やりたいパイプラインは、
a. githubから取ってきて、
b. docker buildして、
c. 検証して、
d. デプロイする
というもの。まずは枠を作るということで、以下をパイプラインスクリプトに書きこんで、実行。うまく行きました。
node('master') {
    
  environment {
    registry = 'tmc05/temperature_monitor'
    registryCredential = 'dockerhub'
  }

  print "BUILD_NUMBER: ${env.BUILD_NUMBER}"
  print "BUILD_ID: ${env.BUILD_ID}"
  print "WORKSPACE: ${env.WORKSPACE}"
  print "JENKINS_URL: ${env.JENKINS_URL}"
  print "BUILD_URL: ${env.BUILD_URL}"
  print "JOB_URL: ${env.JOB_URL}"
  print "DOCKER REG: ${env.registry}"

  stage('git pull') {
    git url: 'https://github.com/makotosd/docker_tempmonitor.git', branch: 'master'
  }
  stage('build'){
    docker.image('ubuntu:latest').inside {
      touch 'hogehoge'
    }
    docker.build("${env.registry}:${env.BUILD_NUMBER}")
  }
  stage('permit'){
    input 'Ready to go?'
  }
  stage('deploy') {
    print "deply"
  }
}
本当は検証はリモートの検証機でやりたい。なので、次はjenkinsのスレーブの設定。

↑このページのトップヘ