Docker Susun menunggu wadah X sebelum memulai Y


326

Saya menggunakan rabbitmq dan contoh python sederhana dari sini bersama dengan docker-compose. Masalah saya adalah saya harus menunggu rabbitmq untuk sepenuhnya dimulai. Dari apa yang saya cari sejauh ini, saya tidak tahu bagaimana menunggu dengan kontainer x (dalam pekerja kasus saya) sampai y (rabbitmq) dimulai.

Saya menemukan posting blog ini di mana dia memeriksa apakah host lain sedang online. Saya juga menemukan perintah buruh pelabuhan ini :

Tunggu

Penggunaan: buruh pelabuhan tunggu CONTAINER [CONTAINER ...]

Blokir hingga wadah berhenti, lalu cetak kode keluarnya.

Menunggu wadah untuk berhenti mungkin bukan yang saya cari, tetapi jika ya, apakah mungkin menggunakan perintah itu di dalam docker-compose.yml? Solusi saya sejauh ini adalah menunggu beberapa detik dan memeriksa port, tetapi apakah ini cara untuk mencapai ini? Jika saya tidak menunggu saya mendapatkan kesalahan.

docker-compose.yml

worker:
    build: myapp/.
    volumes:
    - myapp/.:/usr/src/app:ro

    links:
    - rabbitmq
rabbitmq:
    image: rabbitmq:3-management

contoh python hello (rabbit.py):

import pika
import time

import socket

pingcounter = 0
isreachable = False
while isreachable is False and pingcounter < 5:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect(('rabbitmq', 5672))
        isreachable = True
    except socket.error as e:
        time.sleep(2)
        pingcounter += 1
    s.close()

if isreachable:
    connection = pika.BlockingConnection(pika.ConnectionParameters(
            host="rabbitmq"))
    channel = connection.channel()

    channel.queue_declare(queue='hello')

    channel.basic_publish(exchange='',
                          routing_key='hello',
                          body='Hello World!')
    print (" [x] Sent 'Hello World!'")
    connection.close()

Dockerfile untuk pekerja:

FROM python:2-onbuild
RUN ["pip", "install", "pika"]

CMD ["python","rabbit.py"]

Pembaruan November 2015 :

Script shell atau menunggu di dalam program Anda mungkin merupakan solusi yang mungkin. Tetapi setelah melihat Masalah ini saya mencari perintah atau fitur docker / docker-compose sendiri.

Mereka menyebutkan solusi untuk melaksanakan pemeriksaan kesehatan, yang mungkin merupakan pilihan terbaik. Koneksi tcp terbuka tidak berarti layanan Anda siap atau mungkin tetap siap. Selain itu saya perlu mengubah entri saya di dockerfile saya.

Jadi saya berharap untuk jawaban dengan komposisi buruh pelabuhan pada perintah papan, yang diharapkan akan terjadi jika mereka menyelesaikan masalah ini.

Pembaruan Maret 2016

Ada proposal untuk menyediakan cara bawaan untuk menentukan apakah sebuah wadah "hidup". Jadi komposisi buruh pelabuhan mungkin dapat menggunakannya dalam waktu dekat.

Perbarui Juni 2016

Tampaknya pemeriksaan kesehatan akan diintegrasikan ke dalam buruh pelabuhan di Versi 1.12.0

Perbarui Januari 2017

Saya menemukan solusi menyusun buruh pelabuhan lihat: Menulis Docker menunggu wadah X sebelum memulai Y


2
Menggunakan cek kesehatan di telah usang dalam docker-compose 2.3 untuk mendorong membuat sistem terdistribusi toleran terhadap kesalahan. Lihat: docs.docker.com/compose/startup-order
Kmaid

Jawaban:


284

Akhirnya menemukan solusi dengan metode menulis buruh pelabuhan. Karena format file komposisi pembuat galat 2.1 Anda dapat menentukan pemeriksaan kesehatan .

Saya melakukannya dalam proyek contoh Anda harus menginstal setidaknya buruh pelabuhan 1.12.0+. Saya juga perlu memperpanjang Dockerfile rabbitmq-manajemen , karena curl tidak diinstal pada gambar resmi.

Sekarang saya menguji apakah halaman manajemen rabbitmq-container tersedia. Jika ikal selesai dengan exitcode 0 aplikasi wadah (python pika) akan dimulai dan mempublikasikan pesan ke halo antrian. Sekarang berfungsi (output).

docker-compose (versi 2.1):

version: '2.1'

services:
  app:
    build: app/.
    depends_on:
      rabbit:
        condition: service_healthy
    links: 
        - rabbit

  rabbit:
    build: rabbitmq/.
    ports: 
        - "15672:15672"
        - "5672:5672"
    healthcheck:
        test: ["CMD", "curl", "-f", "http://localhost:15672"]
        interval: 30s
        timeout: 10s
        retries: 5

keluaran:

rabbit_1  | =INFO REPORT==== 25-Jan-2017::14:44:21 ===
rabbit_1  | closing AMQP connection <0.718.0> (172.18.0.3:36590 -> 172.18.0.2:5672)
app_1     |  [x] Sent 'Hello World!'
healthcheckcompose_app_1 exited with code 0

Dockerfile (rabbitmq + curl):

FROM rabbitmq:3-management
RUN apt-get update
RUN apt-get install -y curl 
EXPOSE 4369 5671 5672 25672 15671 15672

Versi 3 tidak lagi mendukung bentuk kondisi depend_on . Jadi saya pindah dari depend_on ke restart on-failure. Sekarang wadah aplikasi saya akan memulai ulang 2-3 kali sampai berfungsi, tetapi masih merupakan fitur menulis buruh pelabuhan tanpa menimpa titik masuk.

docker-compose (versi 3):

version: "3"

services:

  rabbitmq: # login guest:guest
    image: rabbitmq:management
    ports:
    - "4369:4369"
    - "5671:5671"
    - "5672:5672"
    - "25672:25672"
    - "15671:15671"
    - "15672:15672"
    healthcheck:
        test: ["CMD", "curl", "-f", "http://localhost:15672"]
        interval: 30s
        timeout: 10s
        retries: 5

  app:
    build: ./app/
    environment:
      - HOSTNAMERABBIT=rabbitmq
    restart: on-failure
    depends_on:
      - rabbitmq
    links: 
        - rabbitmq

6
@svenhornberg pingmenggunakan ICMP sehingga tidak mendukung port TCP. Mungkin ncuntuk menguji port TCP. Mungkin lebih baik menggunakan psql -h localhost -p 5432dan menanyakan sesuatu.
Matt

36
"tergantung pada" telah dihapus dalam versi 3 docs.docker.com/compose/compose-file/#dependson
nha

48
@nha Sepertinya conditionbentuk depends_ondihapus, tetapi depends_onitu sendiri masih ada di v3
akivajgordon

14
Bagaimana pemeriksaan kesehatan masih dapat digunakan untuk mengontrol pesanan startup jika depends_ondengan conditiontelah dihapus?
Franz

43
Sulit dipercaya bahwa ini masih menyakitkan
npr

71

Secara native itu belum memungkinkan. Lihat juga permintaan fitur ini .

Sejauh ini Anda perlu melakukan itu dalam wadah Anda CMDuntuk menunggu sampai semua layanan yang diperlukan ada di sana.

Dalam Dockerfiles CMDAnda bisa merujuk ke naskah awal Anda sendiri yang membungkus memulai layanan kontainer Anda. Sebelum Anda memulainya, Anda menunggu yang tergantung seperti:

Dockerfile

FROM python:2-onbuild
RUN ["pip", "install", "pika"]
ADD start.sh /start.sh
CMD ["/start.sh"]

start.sh

#!/bin/bash
while ! nc -z rabbitmq 5672; do sleep 3; done
python rabbit.py

Mungkin Anda perlu menginstal netcat Dockerfilejuga. Saya tidak tahu apa yang sudah diinstal pada gambar python.

Ada beberapa alat di luar sana yang menyediakan logika menunggu yang mudah digunakan, untuk pemeriksaan port tcp sederhana:

Untuk menunggu lebih kompleks:


Bisakah Anda menjelaskan apa yang Anda maksud dengan CMD? Apakah ini berarti program saya harus melakukannya, seperti saya melakukannya dengan pemeriksaan port? Atau maksud Anda CMD spesifik dari eg linux untuk ini?
svenhornberg

terima kasih telah menjelaskan, saya mendukung jawaban Anda. Tetapi saya pikir permintaan fitur yang akan datang, akan menjadi jawaban yang tepat untuk pertanyaan saya jadi saya meninggalkannya sejauh ini tidak terjawab.
svenhornberg

44

Menggunakan restart: unless-stoppedatau restart: alwaysdapat memecahkan masalah ini.

Jika pekerja containerberhenti ketika rabbitMQ tidak siap, itu akan dimulai kembali sampai siap.


3
Saya suka solusi ini untuk kasus ini, tetapi tidak berfungsi untuk wadah yang tidak keluar ketika salah satu subproses yang dijalankan gagal. Misalnya, wadah Tomcat akan terus berjalan bahkan jika servlet Java yang dijalankannya gagal terhubung ke server database. Memang, Docker membuat render servlet seperti Tomcat sebagian besar tidak perlu.
Derek Mahar

@DerekMahar, jika Anda memiliki aplikasi web berbasis Java yang hanya melayani panggilan REST, apa yang Anda gunakan alih-alih Jetty / Tomcat?
JoeG

2
@ JoG, maksudku Tomcat adalah wadah servlet yang dapat menampung banyak aplikasi, bukan tertanam Tomcat. Docker membuat yang pertama sebagian besar tidak perlu, sementara membuat yang terakhir lebih populer untuk layanan microser, misalnya.
Derek Mahar

35

Baru-baru ini mereka telah menambahkan depends_onfitur .

Edit:

Mulai dari menulis versi 2.1+ yang dapat Anda gunakan depends_onbersama dengan healthcheckuntuk mencapai ini:

Dari dokumen :

version: '2.1'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
  redis:
    image: redis
  db:
    image: redis
    healthcheck:
      test: "exit 0"

Sebelum versi 2.1

Anda masih dapat menggunakan depends_on, tetapi itu hanya mempengaruhi urutan layanan dimulai - tidak jika mereka siap sebelum layanan dependen dimulai.

Tampaknya memerlukan setidaknya versi 1.6.0.

Penggunaan akan terlihat seperti ini:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres 

Dari dokumen:

Ketergantungan ekspres antar layanan, yang memiliki dua efek:

  • docker-compose up akan memulai layanan dalam urutan ketergantungan. Pada contoh berikut, db dan redis akan dimulai sebelum web.
  • LAYANAN penyusun docker akan secara otomatis menyertakan dependensi SERVICE. Pada contoh berikut, web pembuat docker juga akan membuat dan memulai db dan redis.

Catatan: Seperti yang saya mengerti, meskipun ini tidak mengatur urutan di mana kontainer dimuat. Itu tidak menjamin bahwa layanan di dalam wadah benar-benar dimuat.

Misalnya, Anda wadah postgres mungkin naik. Tetapi layanan postgres itu sendiri mungkin masih menginisialisasi dalam wadah.


10
dnephin menulis: depend_on hanya memesan. Untuk benar-benar menunda dimulainya wadah lain perlu ada beberapa cara untuk mendeteksi kapan suatu proses telah selesai menginisialisasi sendiri.
svenhornberg

15
"Versi 3 tidak lagi mendukung bentuk kondisi depends_on." docs.docker.com/compose/compose-file/#dependson
akauppi

depends_ontidak menunggu sampai wadahnya dalam readykeadaan (apa pun artinya dalam kasus Anda). Itu hanya menunggu sampai wadah dalam keadaan 'berjalan'.
htyagi

19

Anda juga bisa menambahkannya ke opsi perintah misalnya.

command: bash -c "sleep 5; start.sh"

https://github.com/docker/compose/issues/374#issuecomment-156546513

untuk menunggu di port Anda juga dapat menggunakan sesuatu seperti ini

command: bash -c "while ! curl -s rabbitmq:5672 > /dev/null; do echo waiting for xxx; sleep 3; done; start.sh"

untuk menambah waktu tunggu Anda dapat meretas lebih banyak:

command: bash -c "for i in {1..100} ; do if ! curl -s rabbitmq:5672 > /dev/null ; then echo waiting on rabbitmq for $i seconds; sleep $i; fi; done; start.sh"

13

restart: on-failure melakukan trik untuk saya .. lihat di bawah

---
version: '2.1'
services:
  consumer:
    image: golang:alpine
    volumes:
      - ./:/go/src/srv-consumer
    working_dir: /go/src/srv-consumer
    environment:
      AMQP_DSN: "amqp://guest:guest@rabbitmq:5672"
    command: go run cmd/main.go
    links:
          - rabbitmq
    restart: on-failure

  rabbitmq:
    image: rabbitmq:3.7-management-alpine
    ports:
      - "15672:15672"
      - "5672:5672"


7

Anda juga dapat menyelesaikan ini dengan menetapkan titik akhir yang menunggu layanan untuk dinyalakan dengan menggunakan netcat (menggunakan skrip docker-wait ). Saya suka pendekatan ini karena Anda masih memiliki commandbagian bersih di Anda docker-compose.ymldan Anda tidak perlu menambahkan kode khusus buruh pelabuhan ke aplikasi Anda:

version: '2'
services:
  db:
    image: postgres
  django:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    entrypoint: ./docker-entrypoint.sh db 5432
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

Maka Anda docker-entrypoint.sh:

#!/bin/sh

postgres_host=$1
postgres_port=$2
shift 2
cmd="$@"

# wait for the postgres docker to be running
while ! nc $postgres_host $postgres_port; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "Postgres is up - executing command"

# run the command
exec $cmd

Ini saat ini didokumentasikan dalam dokumentasi buruh pelabuhan resmi .

PS: Anda harus menginstal netcatdalam contoh buruh pelabuhan Anda jika ini tidak tersedia. Untuk melakukannya, tambahkan ini ke Dockerfile Anda :

RUN apt-get update && apt-get install netcat-openbsd -y 

4

Ada utilitas siap pakai yang disebut " docker-wait " yang dapat digunakan untuk menunggu.


1
Terima kasih, tetapi ini hanya skrip shell jadi itu seperti jawaban h3nrik atau menunggu di dalam python. Ini bukan fitur dari docker-compose sendiri. Semoga Anda melihat di github.com/docker/compose/issues/374 mereka berencana untuk menerapkan pemeriksaan kesehatan yang akan menjadi cara terbaik. Koneksi tcp terbuka tidak berarti layanan Anda siap atau mungkin tetap siap. Selain itu saya perlu mengubah entri saya di dockerfile saya.
svenhornberg

3

Sudah mencoba berbagai cara, tetapi menyukai kesederhanaan ini: https://github.com/ufoscout/docker-compose-wait

Gagasan bahwa Anda dapat menggunakan ENV vars dalam file buruh pelabuhan compose untuk menyerahkan daftar layanan host (dengan port) yang harus "ditunggu" seperti ini: WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017.

Jadi misalkan Anda memiliki file docker-compose.yml berikut (salin / lampau dari repo README ):

version: "3"

services:

  mongo:
    image: mongo:3.4
    hostname: mongo
    ports:
      - "27017:27017"

  postgres:
    image: "postgres:9.4"
    hostname: postgres
    ports:
      - "5432:5432"

  mysql:
    image: "mysql:5.7"
    hostname: mysql
    ports:
      - "3306:3306"

  mySuperApp:
    image: "mySuperApp:latest"
    hostname: mySuperApp
    environment:
      WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017

Selanjutnya, agar layanan menunggu, Anda perlu menambahkan dua baris berikut ke Dockerfiles Anda (ke Dockerfile dari layanan yang harus menunggu layanan lain untuk memulai):

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait
RUN chmod +x /wait

Contoh lengkap contoh Dockerfile tersebut (lagi dari README repo proyek ):

FROM alpine

## Add your application to the docker image
ADD MySuperApp.sh /MySuperApp.sh

## Add the wait script to the image
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait
RUN chmod +x /wait

## Launch the wait tool and then your application
CMD /wait && /MySuperApp.sh

Untuk detail lainnya tentang kemungkinan penggunaan, lihat README


Saya mencari jawaban yang serupa. Saya biasanya bekerja dengan hub.docker.com/r/dadarek/wait-for-dependencies karena menggunakan netcat di bawahnya. Yang Anda berikan adalah berbasis Rust. Tidak dapat mengomentari kualitas Anda, tetapi bagi saya tidak ada lapisan tambahan yang merupakan pro pasti.
Filip Malczak

1
Saya sangat merekomendasikan ini dengan alasan keamanan. Anda menjalankan eksekusi sewenang-wenang dari hyperlink. Solusi yang lebih baik adalah melakukan hal yang sama dengan skrip statis yang disalin ke gambar dengan COPY
Paul K

@PaulK tentu saja, dapat dimengerti bahwa menjalankan apa pun dari hyperlink tidak aman, tapi itu hanya demo di atas cara membuat https://github.com/ufoscout/docker-compose-waitperpustakaan berfungsi :) Cara Anda menggunakan perpustakaan itu tidak mengubah jawaban yang dapat Anda manfaatkan dengan lib. Keamanan adalah topik yang kompleks dan jika kita melangkah jauh, kita harus memeriksa apa yang dilakukan perpustakaan di dalamnya, bahkan jika kita SALINAN itu :) Jadi lebih baik untuk lebih spesifik dalam komentar Anda seperti: "Saya sangat merekomendasikan terhadap penggunaan perpustakaan itu dari hyperlink ". Semoga Anda setuju, terima kasih atas petunjuknya!
Evereq

2

mendasarkan pada posting blog ini https://8thlight.com/blog/dariusz-pasciak/2016/10/17/docker-compose-wait-for-dependencies.html

Saya mengkonfigurasi saya docker-compose.ymlseperti yang ditunjukkan di bawah ini:

version: "3.1"

services:
  rabbitmq:
    image: rabbitmq:3.7.2-management-alpine
    restart: always
    environment:
      RABBITMQ_HIPE_COMPILE: 1
      RABBITMQ_MANAGEMENT: 1
      RABBITMQ_VM_MEMORY_HIGH_WATERMARK: 0.2
      RABBITMQ_DEFAULT_USER: "rabbitmq"
      RABBITMQ_DEFAULT_PASS: "rabbitmq"
    ports:
      - "15672:15672"
      - "5672:5672"
    volumes:
      - data:/var/lib/rabbitmq:rw

  start_dependencies:
    image: alpine:latest
    links:
      - rabbitmq
    command: >
      /bin/sh -c "
        echo Waiting for rabbitmq service start...;
        while ! nc -z rabbitmq 5672;
        do
          sleep 1;
        done;
        echo Connected!;
      "

volumes:
  data: {}

Maka saya lakukan untuk menjalankan =>:

docker-compose up start_dependencies

rabbitmqlayanan akan mulai dalam mode daemon, start_dependenciesakan menyelesaikan pekerjaan.


lol, membuat permintaan via "curl", "-f", "http://localhost:15672"mana Anda perlu menginstal managementplugin dan menggunakan pemeriksaan kesehatan yang sudah usang - jawaban terbaiknya. Contoh kerja sederhana dengan cek melalui nc- downvote. ha, ok ...
Igor Komar

jawabannya tidak menggunakan fitur buruh pelabuhan asli, itu tidak relevan jika Anda menggunakan curl, nc atau alat lainnya. sementara! nc sama dengan yang sudah diposting di jawaban lain.
svenhornberg


1
@IgorKomar, terima kasih kawan, kau menyelamatkan hariku! : 3 Saya menggunakan mekanik yang hampir sama untuk memeriksa server mysql siap sebelum aplikasi yang sebenarnya dimulai. ;) Saya memberikan perintah serupa kedocker-compose run --name app-test --rm "app" bash -l -c 'echo Waiting for mysql service start... && while ! nc -z db-server 3306; do sleep 1; done && echo Connected! && /bin/bash /script/ci_tests.sh'
TooroSan

1

Dalam versi 3 file Menulis Docker, Anda dapat menggunakan RESTART .

Sebagai contoh:

docker-compose.yml

worker:
    build: myapp/.
    volumes:
    - myapp/.:/usr/src/app:ro
    restart: on-failure
    depends_on:
    - rabbitmq
rabbitmq:
    image: rabbitmq:3-management

Perhatikan bahwa saya menggunakan depend_on alih-alih tautan karena yang terakhir tidak digunakan dalam versi 3.

Meskipun berfungsi, itu mungkin bukan solusi ideal karena Anda me-restart wadah buruh pelabuhan di setiap kegagalan.

Lihat juga RESTART_POLICY . itu memungkinkan Anda menyempurnakan kebijakan mulai ulang.

Saat Anda menggunakan Tulis dalam produksi , sebenarnya adalah praktik terbaik untuk menggunakan kebijakan mulai ulang:

Menentukan kebijakan mulai ulang seperti memulai ulang: selalu untuk menghindari waktu henti



0

Berikut adalah contoh di mana mainwadah menunggu workerketika mulai merespons ping:

version: '3'
services:
  main:
    image: bash
    depends_on:
     - worker
    command: bash -c "sleep 2 && until ping -qc1 worker; do sleep 1; done &>/dev/null"
    networks:
      intra:
        ipv4_address: 172.10.0.254
  worker:
    image: bash
    hostname: test01
    command: bash -c "ip route && sleep 10"
    networks:
      intra:
        ipv4_address: 172.10.0.11
networks:
  intra:
    driver: bridge
    ipam:
      config:
      - subnet: 172.10.0.0/24

Namun, cara yang tepat adalah menggunakan healthcheck(> = 2.1).


0

Tidak disarankan untuk penerapan serius, tetapi di sini pada dasarnya adalah perintah "tunggu x detik".

Dengan docker-composeversi 3.4sebuah start_periodinstruksi telah ditambahkan kehealthcheck . Ini berarti kita dapat melakukan hal berikut:

docker-compose.yml:

version: "3.4"
services:
  # your server docker container
  zmq_server:
    build:
      context: ./server_router_router
      dockerfile: Dockerfile

  # container that has to wait
  zmq_client:
    build:
      context: ./client_dealer/
      dockerfile: Dockerfile
    depends_on:
      - zmq_server
    healthcheck:
      test: "sh status.sh"
      start_period: 5s

status.sh:

#!/bin/sh

exit 0

Apa yang terjadi di sini adalah bahwa healthcheckdipanggil setelah 5 detik. Ini memanggil status.shskrip, yang selalu mengembalikan "Tidak masalah". Kami baru saja membuat zmq_clientwadah menunggu 5 detik sebelum memulai!

Catatan: Penting bagi Anda untuk memilikinya version: "3.4". Jika .4tidak ada, komposisi buruh pelabuhan mengeluh.


1
Sebagai solusi "tunggu 5" yang naif, yang satu ini cukup cerdik. Saya akan menang, tetapi saya tidak akan melakukannya karena ini tidak benar-benar bekerja dengan pengaturan seperti prod dan saya takut bahwa seseorang akan melihat jumlah suara daripada membaca dengan cermat. Tetap saja, saya ingin mengatakan "bung, itu cerdas";)
Filip Malczak

PS. Untuk solusi yang lebih rumit, lihat jawaban
Evereq

Itu tidak apa start_periodtidak. Konfigurasi itu berarti ada masa tenggang di mana pemeriksaan kesehatan yang gagal tidak dihitung sebagai percobaan ulang. Jika berhasil lebih awal, itu dianggap sehat. Setelah periode mulai, kegagalan akan dihitung sebagai coba lagi. Lihat docs.docker.com/engine/reference/builder/#healthcheck
Capi Etheriel

-4

Saya hanya punya 2 file menulis dan mulai yang pertama dan yang kedua nanti. Script saya terlihat seperti itu:

#!/bin/bash
#before i build my docker files
#when done i start my build docker-compose
docker-compose -f docker-compose.build.yaml up
#now i start other docker-compose which needs the image of the first
docker-compose -f docker-compose.prod.yml up

Ini tidak dianggap sebagai praktik yang baik. Anda tidak dapat memberikan solusi yang terdiri dari beberapa conatiners dari satu file penulisan itu.
juergi
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.